生活随笔
收集整理的這篇文章主要介紹了
行人属性识别二:添加新网络训练和自定义数据集训练
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
序言
上一篇記錄了訓練過程,但是項目中提供的模型網絡都是偏大的,如果想要在邊緣設備上部署,還是比較吃力的,所以本文記錄如何加入新的網絡模型進行訓練,以repvgg為例,加入mobilenet、shufflenet等網絡同配置。
以及抽取pa100k數據集部分屬性作為自己數據集進行訓練,對于自主標記的數據集,按照相同格式準備即可。
一、添加新網絡repvgg
本項目中用到網絡結構其實和分類網絡沒有太大區別,查看網絡的構建,同樣是卷積+全連接結構,所以構建起來就簡單多了,只需要替換backbone即可。
在models/backbone中添加repvgg.py文件,文件內容如下,提供的backbone只有兩個: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
需要注意的是這兩個部分,在初始化網絡前要加載好預訓練模型,其他網絡也是類似:
然后在models/model_factory.py中添加,這兩個網絡最后層輸出的對應通道數:
在train.py中導入
最后修改配置文件,添加repvgg_a0網絡,就可以運行了:
二、自定義數據集
pa100k數據集中包含了26種屬性,有一些屬性對我來說沒有用,不想訓練這么多,所以抽取其中18中屬性進行訓練,那么該如何準備呢?
首先,先通過以下腳本將pa100k的mat標簽文件轉換為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
)
得到如下幾個文件:
在通過如下文件對屬性進行剔除,并聲稱新的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
()
得到三個文件train.txt、test.txt、val.txt,內容如下,如果是自己標注的數據集,想辦法轉成這個格式即可:
然后根據dataset/pedes_attr/preprocess/format_pa100k.py編寫format_mydata.py文件,在生成pkl之前需要對數據集屬性進行重排,重排規則看dataset/pedes_attr/annotation.md,其實不排也可以,不排的話將generate_data_description函數中label_txt,reorder=Flase即可,相應的修改位置已經在代碼中標明:
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)
修改完成后需要到該文件下添加自己數據集名字,我的數據集取名為MyData:
在configs中新建mydata.yaml,內容根據pa100k的來修改,示例如下,加入了新的網絡和數據集:
最后運行代碼訓練:
python train
.py
--cfg configs
/pedes_baseline
/mydata
.yaml
總結
以上是生活随笔為你收集整理的行人属性识别二:添加新网络训练和自定义数据集训练的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。