Skip to content

Commit a56c517

Browse files
authored
Pifpaf accuracy debug and model complexity deployment (#349)
I.Pifpaf accuracy debugging 1.Use same loss weight configuration with original repo 2.Use same optimizer with original repo(SGD with nestrov) 3.Use same pretrain backbone(converted from original repo) 4.Use weight-decay in training II.Model deployment complexity measurement added 1.measure_flops.py allows to measure GFlops of converted model in pb format
1 parent 073eef4 commit a56c517

File tree

14 files changed

+251
-185
lines changed

14 files changed

+251
-185
lines changed

export_pb.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,25 @@
2525
type=str,
2626
default="MSCOCO",
2727
help="dataset name,to determine which dataset to use, available options: coco ")
28-
parser.add_argument("--dataset_path",
29-
type=str,
30-
default="data",
31-
help="dataset path,to determine the path to load the dataset")
32-
parser.add_argument('--train_type',
33-
type=str,
34-
default="Single_train",
35-
help='train type, available options: Single_train, Parallel_train')
36-
parser.add_argument('--kf_optimizer',
37-
type=str,
38-
default='Sma',
39-
help='kung fu parallel optimizor,available options: Sync_sgd, Async_sgd, Sma')
4028
parser.add_argument("--output_dir",
4129
type=str,
4230
default="save_dir",
4331
help="which dir to output the exported pb model")
44-
32+
parser.add_argument("--export_batch_size",
33+
type=int,
34+
default=None,
35+
help="the expected input image batch_size of the converted model, set to None to support dynamic shape"
36+
)
37+
parser.add_argument("--export_h",
38+
type=int,
39+
default=None,
40+
help="the expected input image height of the converted model, set to None to support dynamic shape"
41+
)
42+
parser.add_argument("--export_w",
43+
type=int,
44+
default=None,
45+
help="the expected input image width of the converted model, set to None to support dynamic shape")
46+
4547

4648
args=parser.parse_args()
4749
Config.set_model_name(args.model_name)
@@ -50,6 +52,9 @@
5052
config=Config.get_config()
5153
export_model=Model.get_model(config)
5254

55+
export_batch_size=args.export_batch_size
56+
export_h,export_w=args.export_h,args.export_w
57+
print(f"export_batch_size:{export_batch_size} export_h:{export_h} export_w:{export_w}")
5358
input_path=f"{config.model.model_dir}/newest_model.npz"
5459
output_dir=f"{args.output_dir}/{config.model.model_name}"
5560
output_path=f"{output_dir}/frozen_{config.model.model_name}.pb"
@@ -64,9 +69,9 @@
6469
export_model.load_weights(input_path)
6570
export_model.eval()
6671
if(export_model.data_format=="channels_last"):
67-
input_signature=tf.TensorSpec(shape=(None,None,None,3))
72+
input_signature=tf.TensorSpec(shape=(export_batch_size,export_h,export_w,3))
6873
else:
69-
input_signature=tf.TensorSpec(shape=(None,3,None,None))
74+
input_signature=tf.TensorSpec(shape=(export_batch_size,3,export_h,export_w))
7075
concrete_function=export_model.infer.get_concrete_function(x=input_signature)
7176
frozen_graph=convert_variables_to_constants_v2(concrete_function)
7277
frozen_graph_def=frozen_graph.graph.as_graph_def()

hyperpose/Config/config_pifpaf.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,17 @@
2828
train.n_step = 1000000
2929
# initial learning rate
3030
train.lr_init = 1e-4
31-
# evey number of step to decay lr
32-
train.lr_decay_every_step = 136120
3331
# decay lr factor
3432
train.lr_decay_factor = 0.2
35-
train.lr_decay_steps=[420000,630000]
36-
train.weight_decay_factor = 0.0
33+
train.lr_decay_steps=[777920,848640]
34+
train.lr_decay_duration=35360
35+
train.weight_decay_factor = 1e-5
3736
train.train_type=TRAIN.Single_train
3837
train.vis_dir=f"./save_dir/{model.model_name}/train_vis_dir"
3938

4039
#eval configuration
4140
eval =edict()
42-
eval.batch_size=8
41+
eval.batch_size=4
4342
eval.vis_dir= f"./save_dir/{model.model_name}/eval_vis_dir"
4443

4544
#test configuration

hyperpose/Dataset/mscoco_dataset/dataset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def official_eval(self,pd_anns,eval_dir=f"./eval_dir"):
152152
json.dump(gt_json,gt_json_file)
153153
gt_json_file.close()
154154

155-
pd_json_path=f"{eval_dir}/pd_ann.json"
155+
pd_json_path=f"{eval_dir}/person_keypoints_val2017_hyperpose_results.json"
156156
pd_json_file=open(pd_json_path,"w")
157157
json.dump(pd_anns,pd_json_file)
158158
pd_json_file.close()
@@ -186,7 +186,7 @@ def official_eval(self,pd_anns,eval_dir=f"./eval_dir"):
186186

187187
def official_test(self,pd_anns,test_dir="./test_dir"):
188188
server_url="https://competitions.codalab.org/competitions/12061"
189-
pd_json_path=f"{test_dir}/pd_ann.json"
189+
pd_json_path=f"{test_dir}/person_keypoints_test-dev2017_hyperpose_results.json"
190190
pd_json_file=open(pd_json_path,mode="w")
191191
json.dump(pd_anns,pd_json_file)
192192
pd_json_file.close()

hyperpose/Model/__init__.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -256,14 +256,14 @@ def get_test(config):
256256
print(f"testing {model_type.name} model...")
257257
return test
258258

259-
def get_preprocess(model_type):
260-
'''get preprocess function based model_type
259+
def get_preprocessor(model_type):
260+
'''get a preprocessor class based on the specified model_type
261261
262-
get the preprocess function of the specified kind of model to help user construct thier own train
263-
and evaluate pipeline rather than using the integrated train or evaluate pipeline directly when in need.
262+
get the preprocessor class of the specified kind of model to help user directly construct their own
263+
train pipeline(rather than using the integrated train pipeline) when in need.
264264
265-
the preprocess function is able to convert the image and annotation to the model output format for training
266-
or evaluation.
265+
the preprocessor class is able to construct a preprocessor object that could convert the image and annotation to
266+
the model output format for training.
267267
268268
Parameters
269269
----------
@@ -272,23 +272,26 @@ def get_preprocess(model_type):
272272
273273
Returns
274274
-------
275-
function
276-
a preprocess function of the specified kind of model
275+
class
276+
a preprocessor class of the specified kind of model
277277
'''
278278

279279
if model_type == MODEL.Openpose or model_type == MODEL.LightweightOpenpose or model_type==MODEL.MobilenetThinOpenpose:
280-
from .openpose.utils import preprocess
280+
from .openpose import PreProcessor
281281
elif model_type == MODEL.PoseProposal:
282-
from .pose_proposal.utils import preprocess
283-
return preprocess
282+
from .pose_proposal import PreProcessor
283+
elif model_type == MODEL.Pifpaf:
284+
from .pifpaf import PreProcessor
285+
return PreProcessor
284286

285-
def get_postprocess(model_type):
286-
'''get postprocess function based model_type
287+
def get_postprocessor(model_type):
288+
'''get a postprocessor class based on the specified model_type
287289
288-
get the postprocess function of the specified kind of model to help user construct thier own
289-
evaluate pipeline rather than using the integrated train or evaluate pipeline directly when in need
290+
get the postprocessor class of the specified kind of model to help user directly construct their own
291+
evaluate pipeline(rather than using the integrated evaluate pipeline) or infer pipeline(to check the model utility)
292+
when in need.
290293
291-
the postprocess function is able to parse the model output feature map and output parsed human objects of Human class,
294+
the postprocessor is able to parse the model output feature map and output parsed human objects of Human class,
292295
which contains all dectected keypoints.
293296
294297
Parameters
@@ -299,13 +302,15 @@ def get_postprocess(model_type):
299302
Returns
300303
-------
301304
function
302-
a postprocess function of the specified kind of model
305+
a postprocessor class of the specified kind of model
303306
'''
304307
if model_type == MODEL.Openpose or model_type == MODEL.LightweightOpenpose or model_type==MODEL.MobilenetThinOpenpose:
305-
from .openpose.utils import postprocess
308+
from .openpose import PostProcessor
306309
elif model_type == MODEL.PoseProposal:
307-
from .pose_proposal.utils import postprocess
308-
return postprocess
310+
from .pose_proposal import PostProcessor
311+
elif model_type == MODEL.Pifpaf:
312+
from .pifpaf import PostProcessor
313+
return PostProcessor
309314

310315
def get_visualize(model_type):
311316
'''get visualize function based model_type
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .model import *
22
from .train import *
3-
from .eval import *
3+
from .eval import *
4+
from .processor import PreProcessor,PostProcessor

hyperpose/Model/pifpaf/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .model import *
22
from .train import *
3-
from .eval import *
3+
from .eval import *
4+
from .processor import PreProcessor,PostProcessor

hyperpose/Model/pifpaf/model.py

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
class Pifpaf(Model):
1212
def __init__(self,parts,limbs,colors=CocoColor,n_pos=17,n_limbs=19,hin=368,win=368,scale_size=32,backbone=None,pretraining=False,quad_size=2,quad_num=1,
13-
lambda_pif_conf=30.0,lambda_pif_vec=2.0,lambda_pif_scale=2.0,lambda_paf_conf=50.0,lambda_paf_src_vec=3.0,lambda_paf_dst_vec=3.0,
14-
lambda_paf_src_scale=2.0,lambda_paf_dst_scale=2.0,data_format="channels_first"):
13+
lambda_pif_conf=1.0,lambda_pif_vec=1.0,lambda_pif_scale=1.0,lambda_paf_conf=1.0,lambda_paf_src_vec=1.0,lambda_paf_dst_vec=1.0,
14+
lambda_paf_src_scale=1.0,lambda_paf_dst_scale=1.0,data_format="channels_first"):
1515
super().__init__()
1616
self.parts=parts
1717
self.limbs=limbs
@@ -65,6 +65,13 @@ def infer(self,x):
6565
paf_conf,paf_src_vec,paf_dst_vec,_,_,paf_src_scale,paf_dst_scale=paf_maps
6666
return pif_conf,pif_vec,pif_scale,paf_conf,paf_src_vec,paf_dst_vec,paf_src_scale,paf_dst_scale
6767

68+
def soft_clamp(self,x,max_value=5.0):
69+
above_mask=tf.where(x>=max_value,1.0,0.0)
70+
x_below=x*(1-above_mask)
71+
x_soft_above=tf.where(x>=max_value,x,max_value)
72+
x_above=(max_value+tf.math.log(1+x_soft_above-max_value))*above_mask
73+
return x_below+x_above
74+
6875
def Bce_loss(self,pd_conf,gt_conf,focal_gamma=1.0):
6976
#shape conf:[batch,field,h,w]
7077
batch_size=pd_conf.shape[0]
@@ -75,15 +82,18 @@ def Bce_loss(self,pd_conf,gt_conf,focal_gamma=1.0):
7582
gt_conf=gt_conf[valid_mask]
7683
#calculate loss
7784
bce_loss=tf.nn.sigmoid_cross_entropy_with_logits(logits=pd_conf,labels=gt_conf)
78-
bce_loss=tf.clip_by_value(bce_loss,0.02,5.0)
85+
bce_loss=self.soft_clamp(bce_loss)
7986
if(focal_gamma!=0.0):
80-
focal=(1-tf.exp(-bce_loss))**focal_gamma
81-
focal=tf.stop_gradient(focal)
82-
bce_loss=focal*bce_loss
87+
p=tf.nn.sigmoid(pd_conf)
88+
pt=p*gt_conf+(1-p)*(1-gt_conf)
89+
focal=1.0-pt
90+
if(focal_gamma!=1.0):
91+
focal=(focal+1e-4)**focal_gamma
92+
bce_loss=focal*bce_loss*0.5
8393
bce_loss=tf.reduce_sum(bce_loss)/batch_size
8494
return bce_loss
8595

86-
def Laplace_loss(self,pd_vec,pd_logb,gt_vec):
96+
def Laplace_loss(self,pd_vec,pd_logb,gt_vec,gt_bmin):
8797
#shape vec: [batch,field,2,h,w]
8898
#shape logb: [batch,field,h,w]
8999
batch_size=pd_vec.shape[0]
@@ -98,40 +108,46 @@ def Laplace_loss(self,pd_vec,pd_logb,gt_vec):
98108
gt_vec_x=gt_vec[:,:,0:1,:,:][valid_mask]
99109
gt_vec_y=gt_vec[:,:,1:2,:,:][valid_mask]
100110
gt_vec=tf.stack([gt_vec_x,gt_vec_y])
111+
#select gt_bmin
112+
gt_bmin=gt_bmin[:,:,np.newaxis,:,:][valid_mask]
101113
#calculate loss
102-
norm=tf.norm(pd_vec-gt_vec,axis=0)
103-
norm=tf.clip_by_value(norm,0.0,5.0)
104-
pd_logb=tf.clip_by_value(pd_logb,-3.0,np.inf)
105-
laplace_loss=pd_logb+(norm+0.1)*tf.exp(-pd_logb)
114+
norm=tf.norm(tf.stack([pd_vec_x-gt_vec_x,pd_vec_y-gt_vec_y,gt_bmin]),axis=0)
115+
pd_logb=3.0*tf.tanh(pd_logb/3.0)
116+
scaled_norm=norm*tf.exp(-pd_logb)
117+
scaled_norm=self.soft_clamp(scaled_norm)
118+
laplace_loss=pd_logb+scaled_norm
106119
laplace_loss=tf.reduce_sum(laplace_loss)/batch_size
107120
return laplace_loss
108121

109122
def Scale_loss(self,pd_scale,gt_scale,b=1.0):
110123
batch_size=pd_scale.shape[0]
111124
valid_mask=tf.logical_not(tf.math.is_nan(gt_scale))
112125
pd_scale=pd_scale[valid_mask]
126+
pd_scale=tf.nn.softplus(pd_scale)
113127
gt_scale=gt_scale[valid_mask]
114128
scale_loss=tf.abs(pd_scale-gt_scale)
115-
scale_loss=tf.clip_by_value(scale_loss,0.0,5.0)/b
129+
denominator=10.0*(0.1+gt_scale)
130+
scale_loss=scale_loss/denominator
131+
scale_loss=self.soft_clamp(scale_loss)
116132
scale_loss=tf.reduce_sum(scale_loss)/batch_size
117133
return scale_loss
118134

119135
def cal_loss(self,pd_pif_maps,pd_paf_maps,gt_pif_maps,gt_paf_maps):
120136
#calculate pif losses
121137
pd_pif_conf,pd_pif_vec,pd_pif_logb,pd_pif_scale=pd_pif_maps
122-
gt_pif_conf,gt_pif_vec,gt_pif_scale=gt_pif_maps
138+
gt_pif_conf,gt_pif_vec,gt_pif_bmin,gt_pif_scale=gt_pif_maps
123139
loss_pif_conf=self.Bce_loss(pd_pif_conf,gt_pif_conf)
124-
loss_pif_vec=self.Laplace_loss(pd_pif_vec,pd_pif_logb,gt_pif_vec)
140+
loss_pif_vec=self.Laplace_loss(pd_pif_vec,pd_pif_logb,gt_pif_vec,gt_pif_bmin)
125141
loss_pif_scale=self.Scale_loss(pd_pif_scale,gt_pif_scale)
126142
loss_pif_maps=[loss_pif_conf,loss_pif_vec,loss_pif_scale]
127143
#calculate paf losses
128144
pd_paf_conf,pd_paf_src_vec,pd_paf_dst_vec,pd_paf_src_logb,pd_paf_dst_logb,pd_paf_src_scale,pd_paf_dst_scale=pd_paf_maps
129-
gt_paf_conf,gt_paf_src_vec,gt_paf_dst_vec,gt_paf_src_scale,gt_paf_dst_scale=gt_paf_maps
145+
gt_paf_conf,gt_paf_src_vec,gt_paf_dst_vec,gt_paf_src_bmin,gt_paf_dst_bmin,gt_paf_src_scale,gt_paf_dst_scale=gt_paf_maps
130146
loss_paf_conf=self.Bce_loss(pd_paf_conf,gt_paf_conf)
131147
loss_paf_src_scale=self.Scale_loss(pd_paf_src_scale,gt_paf_src_scale)
132148
loss_paf_dst_scale=self.Scale_loss(pd_paf_dst_scale,gt_paf_dst_scale)
133-
loss_paf_src_vec=self.Laplace_loss(pd_paf_src_vec,pd_paf_src_logb,gt_paf_src_vec)
134-
loss_paf_dst_vec=self.Laplace_loss(pd_paf_dst_vec,pd_paf_dst_logb,gt_paf_dst_vec)
149+
loss_paf_src_vec=self.Laplace_loss(pd_paf_src_vec,pd_paf_src_logb,gt_paf_src_vec,gt_paf_src_bmin)
150+
loss_paf_dst_vec=self.Laplace_loss(pd_paf_dst_vec,pd_paf_dst_logb,gt_paf_dst_vec,gt_paf_dst_bmin)
135151
loss_paf_maps=[loss_paf_conf,loss_paf_src_vec,loss_paf_dst_vec,loss_paf_src_scale,loss_paf_dst_scale]
136152
#calculate total loss
137153
total_loss=(loss_pif_conf*self.lambda_pif_conf+loss_pif_vec*self.lambda_pif_vec+loss_pif_scale*self.lambda_pif_scale+
@@ -168,7 +184,7 @@ def forward(self,x,is_train=False):
168184
if(is_train==False):
169185
infer_pif_conf=tf.nn.sigmoid(pif_conf)
170186
infer_pif_vec=(pif_vec[:,:]+self.mesh_grid)*self.stride
171-
infer_pif_scale=pif_scale*self.stride
187+
infer_pif_scale=tf.math.softplus(pif_scale)*self.stride
172188
return infer_pif_conf,infer_pif_vec,pif_logb,infer_pif_scale
173189
return pif_conf,pif_vec,pif_logb,pif_scale
174190

@@ -204,7 +220,7 @@ def forward(self,x,is_train=False):
204220
infer_paf_conf=tf.nn.sigmoid(paf_conf)
205221
infer_paf_src_vec=(paf_src_vec[:,:]+self.mesh_grid)*self.stride
206222
infer_paf_dst_vec=(paf_dst_vec[:,:]+self.mesh_grid)*self.stride
207-
infer_paf_src_scale=paf_src_scale*self.stride
208-
infer_paf_dst_scale=paf_dst_scale*self.stride
223+
infer_paf_src_scale=tf.math.softplus(paf_src_scale)*self.stride
224+
infer_paf_dst_scale=tf.math.softplus(paf_dst_scale)*self.stride
209225
return infer_paf_conf,infer_paf_src_vec,infer_paf_dst_vec,paf_src_logb,paf_dst_logb,infer_paf_src_scale,infer_paf_dst_scale
210226
return paf_conf,paf_src_vec,paf_dst_vec,paf_src_logb,paf_dst_logb,paf_src_scale,paf_dst_scale

hyperpose/Model/pifpaf/processor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ def __init__(self,parts,limbs,hin,win,hout,wout,colors=None,data_format="channel
2424

2525
def process(self,annos,mask_valid):
2626
mask_out=cv2.resize(mask_valid,(self.wout,self.hout))
27-
pif_conf,pif_vec,pif_scale = get_pifmap(annos, mask_out, self.hin, self.win, self.hout, self.wout, self.parts, self.limbs, data_format=self.data_format)
28-
paf_conf,paf_src_vec,paf_dst_vec,paf_src_scale,paf_dst_scale = get_pafmap(annos, mask_out, self.hin, self.win, self.hout, self.wout, self.parts, self.limbs, data_format=self.data_format)
29-
pif_maps=[pif_conf,pif_vec,pif_scale]
30-
paf_maps=[paf_conf,paf_src_vec,paf_dst_vec,paf_src_scale,paf_dst_scale]
27+
pif_conf,pif_vec,pif_bmin,pif_scale = get_pifmap(annos, mask_out, self.hin, self.win, self.hout, self.wout, self.parts, self.limbs, data_format=self.data_format)
28+
paf_conf,paf_src_vec,paf_dst_vec,paf_src_bmin,paf_dst_bmin,paf_src_scale,paf_dst_scale = get_pafmap(annos, mask_out, self.hin, self.win, self.hout, self.wout, self.parts, self.limbs, data_format=self.data_format)
29+
pif_maps=[pif_conf,pif_vec,pif_bmin,pif_scale]
30+
paf_maps=[paf_conf,paf_src_vec,paf_dst_vec,paf_src_bmin,paf_dst_bmin,paf_src_scale,paf_dst_scale]
3131
return pif_maps,paf_maps
3232

3333
class PostProcessor:

0 commit comments

Comments
 (0)