生活随笔
收集整理的這篇文章主要介紹了
                                
行人属性识别二:添加新网络训练和自定义数据集训练
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.                        
 
                                
                            
                            
                            序言
 
上一篇記錄了訓(xùn)練過(guò)程,但是項(xiàng)目中提供的模型網(wǎng)絡(luò)都是偏大的,如果想要在邊緣設(shè)備上部署,還是比較吃力的,所以本文記錄如何加入新的網(wǎng)絡(luò)模型進(jìn)行訓(xùn)練,以repvgg為例,加入mobilenet、shufflenet等網(wǎng)絡(luò)同配置。
 
以及抽取pa100k數(shù)據(jù)集部分屬性作為自己數(shù)據(jù)集進(jìn)行訓(xùn)練,對(duì)于自主標(biāo)記的數(shù)據(jù)集,按照相同格式準(zhǔn)備即可。
 
一、添加新網(wǎng)絡(luò)repvgg
 
本項(xiàng)目中用到網(wǎng)絡(luò)結(jié)構(gòu)其實(shí)和分類網(wǎng)絡(luò)沒(méi)有太大區(qū)別,查看網(wǎng)絡(luò)的構(gòu)建,同樣是卷積+全連接結(jié)構(gòu),所以構(gòu)建起來(lái)就簡(jiǎn)單多了,只需要替換backbone即可。
 
 在models/backbone中添加repvgg.py文件,文件內(nèi)容如下,提供的backbone只有兩個(gè):RepVGG_A0、RepVGG_A0_m:
 
import torch
.nn 
as nn
import numpy 
as np
import torch
import torch
.nn
.functional 
as F
from models
.registry 
import BACKBONE__all__ 
= ['RepVGG_A0','RepVGG_A0_m']def conv_bn(in_channels
, out_channels
, kernel_size
, stride
, padding
, groups
=1):result 
= nn
.Sequential
()result
.add_module
('conv', nn
.Conv2d
(in_channels
=in_channels
, out_channels
=out_channels
,kernel_size
=kernel_size
, stride
=stride
, padding
=padding
, groups
=groups
, bias
=False))result
.add_module
('bn', nn
.BatchNorm2d
(num_features
=out_channels
))return result
class RepVGGBlock(nn
.Module
):def __init__(self
, in_channels
, out_channels
, kernel_size
,stride
=1, padding
=0, dilation
=1, groups
=1, padding_mode
='zeros', deploy
=False):super(RepVGGBlock
, self
).__init__
()self
.deploy 
= deploy           self
.groups 
= groupsself
.in_channels 
= in_channels
assert kernel_size 
== 3assert padding 
== 1padding_11 
= padding 
- kernel_size 
// 2self
.nonlinearity 
= nn
.ReLU
()     if deploy
:self
.rbr_reparam 
= nn
.Conv2d
(in_channels
=in_channels
, out_channels
=out_channels
, kernel_size
=kernel_size
, stride
=stride
,padding
=padding
, dilation
=dilation
, groups
=groups
, bias
=True, padding_mode
=padding_mode
)else:self
.rbr_identity 
= nn
.BatchNorm2d
(num_features
=in_channels
) if out_channels 
== in_channels 
and stride 
== 1 else Noneself
.rbr_dense 
= conv_bn
(in_channels
=in_channels
, out_channels
=out_channels
, kernel_size
=kernel_size
, stride
=stride
, padding
=padding
, groups
=groups
)self
.rbr_1x1 
= conv_bn
(in_channels
=in_channels
, out_channels
=out_channels
, kernel_size
=1, stride
=stride
, padding
=padding_11
, groups
=groups
)def forward(self
, inputs
):if hasattr(self
, 'rbr_reparam'):return self
.nonlinearity
(self
.rbr_reparam
(inputs
))if self
.rbr_identity 
is None:id_out 
= 0else:id_out 
= self
.rbr_identity
(inputs
)return self
.nonlinearity
(self
.rbr_dense
(inputs
) + self
.rbr_1x1
(inputs
) + id_out
)
def get_equivalent_kernel_bias(self
):kernel3x3
, bias3x3 
= self
._fuse_bn_tensor
(self
.rbr_dense
)         kernel1x1
, bias1x1 
= self
._fuse_bn_tensor
(self
.rbr_1x1
)           kernelid
, biasid 
= self
._fuse_bn_tensor
(self
.rbr_identity
)        return kernel3x3 
+ self
._pad_1x1_to_3x3_tensor
(kernel1x1
) + kernelid
, bias3x3 
+ bias1x1 
+ biasid
def _pad_1x1_to_3x3_tensor(self
, kernel1x1
):if kernel1x1 
is None:return 0else:return torch
.nn
.functional
.pad
(kernel1x1
, [1,1,1,1])def _fuse_bn_tensor(self
, branch
):if branch 
is None:return 0, 0if isinstance(branch
, nn
.Sequential
):kernel 
= branch
.conv
.weightrunning_mean 
= branch
.bn
.running_meanrunning_var 
= branch
.bn
.running_vargamma 
= branch
.bn
.weightbeta 
= branch
.bn
.biaseps 
= branch
.bn
.eps
else:assert isinstance(branch
, nn
.BatchNorm2d
)if not hasattr(self
, 'id_tensor'):input_dim 
= self
.in_channels 
// self
.groupskernel_value 
= np
.zeros
((self
.in_channels
, input_dim
, 3, 3), dtype
=np
.float32
)for i 
in range(self
.in_channels
):kernel_value
[i
, i 
% input_dim
, 1, 1] = 1self
.id_tensor 
= torch
.from_numpy
(kernel_value
).to
(branch
.weight
.device
)kernel 
= self
.id_tensorrunning_mean 
= branch
.running_meanrunning_var 
= branch
.running_vargamma 
= branch
.weightbeta 
= branch
.biaseps 
= branch
.epsstd 
= (running_var 
+ eps
).sqrt
()t 
= (gamma 
/ std
).reshape
(-1, 1, 1, 1)return kernel 
* t
, beta 
- running_mean 
* gamma 
/ std
def repvgg_convert(self
):kernel
, bias 
= self
.get_equivalent_kernel_bias
()return kernel
.detach
().cpu
().numpy
(), bias
.detach
().cpu
().numpy
(),class RepVGG(nn
.Module
):def __init__(self
, num_blocks
, num_classes
=1000, width_multiplier
=None, override_groups_map
=None, deploy
=False):super(RepVGG
, self
).__init__
()assert len(width_multiplier
) == 4self
.deploy 
= deployself
.override_groups_map 
= override_groups_map 
or dict()assert 0 not in self
.override_groups_mapself
.in_planes 
= min(64, int(64 * width_multiplier
[0]))self
.out_planes 
= int(512 * width_multiplier
[3])self
.stage0 
= RepVGGBlock
(in_channels
=3, out_channels
=self
.in_planes
, kernel_size
=3, stride
=2, padding
=1, deploy
=self
.deploy
)self
.cur_layer_idx 
= 1self
.stage1 
= self
._make_stage
(int(64 * width_multiplier
[0]), num_blocks
[0], stride
=2)self
.stage2 
= self
._make_stage
(int(128 * width_multiplier
[1]), num_blocks
[1], stride
=2)self
.stage3 
= self
._make_stage
(int(256 * width_multiplier
[2]), num_blocks
[2], stride
=2)self
.stage4 
= self
._make_stage
(int(512 * width_multiplier
[3]), num_blocks
[3], stride
=2)def _make_stage(self
, planes
, num_blocks
, stride
):strides 
= [stride
] + [1]*(num_blocks
-1)blocks 
= []for stride 
in strides
:cur_groups 
= self
.override_groups_map
.get
(self
.cur_layer_idx
, 1)blocks
.append
(RepVGGBlock
(in_channels
=self
.in_planes
, out_channels
=planes
, kernel_size
=3,stride
=stride
, padding
=1, groups
=cur_groups
, deploy
=self
.deploy
))self
.in_planes 
= planesself
.cur_layer_idx 
+= 1return nn
.Sequential
(*blocks
)def forward(self
, x
):out 
= self
.stage0
(x
)out 
= self
.stage1
(out
)out 
= self
.stage2
(out
)out 
= self
.stage3
(out
)out 
= self
.stage4
(out
)return outoptional_groupwise_layers 
= [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26]
g2_map 
= {l
: 2 for l 
in optional_groupwise_layers
}
g4_map 
= {l
: 4 for l 
in optional_groupwise_layers
}def create_RepVGG_A0(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[2, 4, 14, 1], num_classes
=num_classes
,width_multiplier
=[0.75, 0.75, 0.75, 2.5], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_A0_m(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[2, 4, 14, 1], num_classes
=num_classes
,width_multiplier
=[0.75, 0.75, 0.75, 1], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_A0_s(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[2, 4, 14, 1], num_classes
=num_classes
,width_multiplier
=[0.75, 0.75, 0.5, 1], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_A1(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[2, 4, 14, 1], num_classes
=num_classes
,width_multiplier
=[1, 1, 1, 2.5], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_A2(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[2, 4, 14, 1], num_classes
=num_classes
,width_multiplier
=[1.5, 1.5, 1.5, 2.75], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_B0(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=num_classes
,width_multiplier
=[1, 1, 1, 2.5], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_B1(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=num_classes
,width_multiplier
=[2, 2, 2, 4], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_B1g2(deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=1000,width_multiplier
=[2, 2, 2, 4], override_groups_map
=g2_map
, deploy
=deploy
)def create_RepVGG_B1g4(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=num_classes
,width_multiplier
=[2, 2, 2, 4], override_groups_map
=g4_map
, deploy
=deploy
)def create_RepVGG_B2(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=num_classes
,width_multiplier
=[2.5, 2.5, 2.5, 5], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_B2g2(deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=1000,width_multiplier
=[2.5, 2.5, 2.5, 5], override_groups_map
=g2_map
, deploy
=deploy
)def create_RepVGG_B2g4(deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=1000,width_multiplier
=[2.5, 2.5, 2.5, 5], override_groups_map
=g4_map
, deploy
=deploy
)def create_RepVGG_B3(num_classes
=1000,deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=num_classes
,width_multiplier
=[3, 3, 3, 5], override_groups_map
=None, deploy
=deploy
)def create_RepVGG_B3g2(deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=1000,width_multiplier
=[3, 3, 3, 5], override_groups_map
=g2_map
, deploy
=deploy
)def create_RepVGG_B3g4(deploy
=False):return RepVGG
(num_blocks
=[4, 6, 16, 1], num_classes
=1000,width_multiplier
=[3, 3, 3, 5], override_groups_map
=g4_map
, deploy
=deploy
)func_dict 
= {
'RepVGG-A0': create_RepVGG_A0
,
'RepVGG-A0_s': create_RepVGG_A0_s
,
'RepVGG-A0_m': create_RepVGG_A0_m
,
'RepVGG-A1': create_RepVGG_A1
,
'RepVGG-A2': create_RepVGG_A2
,
'RepVGG-B0': create_RepVGG_B0
,
'RepVGG-B1': create_RepVGG_B1
,
'RepVGG-B1g2': create_RepVGG_B1g2
,
'RepVGG-B1g4': create_RepVGG_B1g4
,
'RepVGG-B2': create_RepVGG_B2
,
'RepVGG-B2g2': create_RepVGG_B2g2
,
'RepVGG-B2g4': create_RepVGG_B2g4
,
'RepVGG-B3': create_RepVGG_B3
,
'RepVGG-B3g2': create_RepVGG_B3g2
,
'RepVGG-B3g4': create_RepVGG_B3g4
,
}
def get_RepVGG_func_by_name(name
):return func_dict
[name
]
def repvgg_model_convert(model
:torch
.nn
.Module
, build_func
, save_path
=None,num_classes
=1000):converted_weights 
= {}for name
, module 
in model
.named_modules
():if hasattr(module
, 'repvgg_convert'):kernel
, bias 
= module
.repvgg_convert
()converted_weights
[name 
+ '.rbr_reparam.weight'] = kernelconverted_weights
[name 
+ '.rbr_reparam.bias'] = bias
elif isinstance(module
, torch
.nn
.Linear
):converted_weights
[name 
+ '.weight'] = module
.weight
.detach
().cpu
().numpy
()converted_weights
[name 
+ '.bias'] = module
.bias
.detach
().cpu
().numpy
()else:print(name
, type(module
))del modeldeploy_model 
= build_func
(num_classes
=num_classes
,deploy
=True)for name
, param 
in deploy_model
.named_parameters
():print('deploy param: ', name
, param
.size
(), np
.mean
(converted_weights
[name
]))param
.data 
= torch
.from_numpy
(converted_weights
[name
]).float()if save_path 
is not None:torch
.save
(deploy_model
.state_dict
(), save_path
,_use_new_zipfile_serialization
=False)return deploy_model
@BACKBONE.register("repvgg_a0")
def RepVGG_A0(model_path 
= "/home/cai/project/Rethinking_of_PAR/model/RepVGG-A0-train.pth"):model 
= create_RepVGG_A0
()if model_path 
is not None:pretrained_params 
= torch
.load
(model_path
,map_location
=torch
.device
("cpu"))model
.load_state_dict
(pretrained_params
, strict
=False)return model
@BACKBONE.register("repvgg_a0_m")
def RepVGG_A0_m(model_path 
= "/home/cai/project/Rethinking_of_PAR/model/RepVGG-A0-train.pth"):model 
= create_RepVGG_A0_m
()if model_path 
is not None:pretrained_params 
= torch
.load
(model_path
,map_location
=torch
.device
("cpu"))model
.load_state_dict
(pretrained_params
, strict
=False)return model
 
需要注意的是這兩個(gè)部分,在初始化網(wǎng)絡(luò)前要加載好預(yù)訓(xùn)練模型,其他網(wǎng)絡(luò)也是類似:
 
 然后在models/model_factory.py中添加,這兩個(gè)網(wǎng)絡(luò)最后層輸出的對(duì)應(yīng)通道數(shù):
 
 在train.py中導(dǎo)入
 
 最后修改配置文件,添加repvgg_a0網(wǎng)絡(luò),就可以運(yùn)行了:
 
 
二、自定義數(shù)據(jù)集
 
pa100k數(shù)據(jù)集中包含了26種屬性,有一些屬性對(duì)我來(lái)說(shuō)沒(méi)有用,不想訓(xùn)練這么多,所以抽取其中18中屬性進(jìn)行訓(xùn)練,那么該如何準(zhǔn)備呢?
 
首先,先通過(guò)以下腳本將pa100k的mat標(biāo)簽文件轉(zhuǎn)換為txt形式:
 
import pandas 
as pd
import scipy
from scipy 
import io
def mat2txt(data
, key
):subdata 
= data
[key
]dfdata 
= pd
.DataFrame
(subdata
)dfdata
.to_csv
("/home/cai/data/PA100K/%s.txt" % key
, index
=False)if __name__ 
== "__main__":data 
= scipy
.io
.loadmat
("/home/cai/data/PA100K/annotation.mat")key_list 
= ["attributes", "test_images_name", "test_label","train_images_name", "train_label","val_images_name", "val_label"]for key 
in key_list
:mat2txt
(data
, key
)
 
得到如下幾個(gè)文件:
 
 在通過(guò)如下文件對(duì)屬性進(jìn)行剔除,并聲稱新的txt文件:
 
import ostxts 
= [["train_images_name", "train_label"],["test_images_name", "test_label"],["val_images_name", "val_label"]]
txt_path 
= "/home/cai/data/PA100K"for txt_list 
in txts
:file1 
= open(os
.path
.join
(txt_path
,txt_list
[0]+".txt"),"r")file2 
= open(os
.path
.join
(txt_path
,txt_list
[1]+".txt"),"r")save_file 
= open(txt_list
[0].split
("_")[0]+".txt","w")label1_list 
= []label2_list 
= []for line1 
in file1
.readlines
():label1_list
.append
(line1
)for line2 
in file2
.readlines
():label2_list
.append
(line2
)for i
,label 
in enumerate(label1_list
):if i 
==0 :continuelabel1 
= label1_list
[i
].split
('\n')[0].split
('\'')label2 
= label2_list
[i
].split
(',')for i
,ind 
in enumerate([12,15,16,17,18,19,20,25]):                   label2
.pop
(ind
-i
)label2 
= ",".join
(label2
)save_file
.write
("/home/cai/data/PA100K/PA100k/data/"+label1
[1]+"\t"+label2
+"\n")file1
.close
()file2
.close
()save_file
.close
()
 
得到三個(gè)文件train.txt、test.txt、val.txt,內(nèi)容如下,如果是自己標(biāo)注的數(shù)據(jù)集,想辦法轉(zhuǎn)成這個(gè)格式即可:
 
 
然后根據(jù)dataset/pedes_attr/preprocess/format_pa100k.py編寫format_mydata.py文件,在生成pkl之前需要對(duì)數(shù)據(jù)集屬性進(jìn)行重排,重排規(guī)則看dataset/pedes_attr/annotation.md,其實(shí)不排也可以,不排的話將generate_data_description函數(shù)中l(wèi)abel_txt,reorder=Flase即可,相應(yīng)的修改位置已經(jīng)在代碼中標(biāo)明:
 
import os
import numpy 
as np
import random
import pickle
from easydict 
import EasyDict
from scipy
.io 
import loadmatnp
.random
.seed
(0)
random
.seed
(0)classes_name 
= ['Female','AgeOver60','Age18-60','AgeLess18','Front','Side','Back','Hat','Glasses','HandBag','ShoulderBag','Backpack','ShortSleeve','LongSleeve','LongCoat','Trousers','Shorts','Skirt&Dress']                  group_order 
= [7, 8, 12, 13, 14, 15, 16, 17, 9, 10, 11, 1, 2, 3, 0, 4, 5, 6]                         
def make_dir(path
):if os
.path
.exists
(path
):passelse:os
.mkdir
(path
)def generate_data_description(save_dir
, label_txt
,reorder
):"""create a dataset description file, which consists of images, labels"""image_name 
= []image_label 
= []file = open(label_txt
,"r")for line 
in file.readlines
():name 
= line
.split
("\t")[0]label 
= line
.split
('\t')[1].split
('\n')[0].split
(',')label 
= list(map(int, label
))image_name
.append
(name
)image_label
.append
(label
)dataset 
= EasyDict
()dataset
.description 
= 'pa100k'dataset
.reorder 
= 'group_order'dataset
.root 
= os
.path
.join
(save_dir
, 'data')dataset
.image_name 
= image_namedataset
.label 
= np
.array
(image_label
)dataset
.attr_name 
= classes_namedataset
.label_idx 
= EasyDict
()dataset
.label_idx
.eval = list(range(len(classes_name
)))if reorder
:dataset
.label_idx
.eval = group_orderdataset
.partition 
= EasyDict
()dataset
.partition
.train 
= np
.arange
(0, 80000)      dataset
.partition
.val 
= np
.arange
(80000, 90000)    dataset
.partition
.test 
= np
.arange
(90000, 100000)  dataset
.partition
.trainval 
= np
.arange
(0, 90000)   dataset
.weight_train 
= np
.mean
(dataset
.label
[dataset
.partition
.train
], axis
=0).astype
(np
.float32
)dataset
.weight_trainval 
= np
.mean
(dataset
.label
[dataset
.partition
.trainval
], axis
=0).astype
(np
.float32
)with open(os
.path
.join
(save_dir
, 'dataset_all.pkl'), 'wb+') as f
:pickle
.dump
(dataset
, f
)if __name__ 
== "__main__":save_dir 
= '/home/cai/data/PA100K/MyData/'                                             label_txt 
= "/home/cai/project/Rethinking_of_PAR/data/MyData/label.txt"                generate_data_description
(save_dir
, label_txt
,reorder
=True)
 
修改完成后需要到該文件下添加自己數(shù)據(jù)集名字,我的數(shù)據(jù)集取名為MyData:
 
 在configs中新建mydata.yaml,內(nèi)容根據(jù)pa100k的來(lái)修改,示例如下,加入了新的網(wǎng)絡(luò)和數(shù)據(jù)集:
 
 最后運(yùn)行代碼訓(xùn)練:
 
python train
.py 
--cfg configs
/pedes_baseline
/mydata
.yaml
 
                            總結(jié)
                            
                                以上是生活随笔為你收集整理的行人属性识别二:添加新网络训练和自定义数据集训练的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                            
                                如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。