如何设置自己网站的关键词,网址建立快捷方式,那里做一元云购网站,宽屏网站模板html1 CASIA-B数据集
本例使用的是预处理后的CASIA-B数据集#xff0c; 数据集下载网址如下。 http#xff1a;//www.cbsr.ia.ac.cn/china/Gait%20Databases%20cH.asp 该数据集是一个大规模的、多视角的步态库。其中包括124个人#xff0c;每个人有11个视角(0#xff0c;18 数据集下载网址如下。 http//www.cbsr.ia.ac.cn/china/Gait%20Databases%20cH.asp 该数据集是一个大规模的、多视角的步态库。其中包括124个人每个人有11个视角(01836...180)在3种行走条件(普通、穿大衣、携带包裹)下采集。
1.1 CASIA-B数据集的两种形式
CASIA-B数据集有视频和轮廓两种形式。
1.1.1 案例讲解
本例直接使用轮廓数据集进行训练 如图2-13(a)所示。 在本例中 对CASIA-B的轮廓数据集做二次处理 将图片中人物的顶端和底部背景去掉方便模型的训练预处理后的数据集如图2-13(b)所示。 1.2 数据集的目录结构 2 代码实战 GitSet模型进行步态与身份识别(CASIA-B数据集)
代码总览https://blog.csdn.net/qq_39237205/article/details/124199534
2.1 代码实战定义函数加载文件夹的文件名称---GaitSet_DataLoader.py第一部分
import numpy as np # 引入基础库
import os
import torch.utils.data as tordata
from PIL import Image
from tqdm import tqdm
import random# 1.1定义函数加载文件夹的文件名称# load_data函数 分为3个步骤#
def load_data(dataset_path,imgresize,label_train_num,label_shuffle): # 完成了整体数据集的封装# 主要分为三个步骤# ①以人物作为标签将完整的数据集分为两部分分别用于训练和测试。# ②分别根据训练集和测试集中的人物标签遍历文件夹获得对应的图片文件名称。# ③用torch.utils.data接口将图片文件名称转化为数据集 使其能够将图片载入并返回。label_str sorted(os.listdir(dataset_path)) # 以人物为标签# 将不完整的样本忽略只载入完整样本removelist [005,026,037,079,109,088,068,048] # 对数据集中样本不完整的人物标签进行过滤留下可用样本。代码中不完整的人物标签可以通过调用load_dir函数来查找。for removename in removelist:if removename in label_str:label_str.remove(removename)print(label_str,label_str)
# -start--------根据乱序标志来处理样本标签顺序并将其分为训练集和测试集----label_index np.arange(len(label_str)) # 序列数组if label_shuffle:np.random.seed(0)# 打乱数组顺序label_shuffle_index np.random.permutation( len(label_str) )train_list label_shuffle_index[0:label_train_num]test_list label_shuffle_index[label_train_num:]else:train_list label_index[0:label_train_num]test_list label_index[label_train_num:]
# -end--------根据乱序标志来处理样本标签顺序并将其分为训练集和测试集----print(train_list,test_list)# 加载人物列表中的图片文件名称data_seq_dir,data_label,meta_data load_dir(dataset_path,train_list,label_str) # 代码调用load_dir函数将标签列表所对应的图片文件名称载入。①test_data_seq_dir, test_data_label, test_meta_data load_dir(dataset_path, test_list, label_str) # 代码调用load_dir函数将标签列表所对应的图片文件名称载入。②# 将图片文件名称转化为数据集train_source DataSet(data_seq_dir, data_label, meta_data, imgresize,True) # 调用自定义类DataSet 返回PyTorch支持的数据集对象且只对训练集进行缓存处理测试集不做缓存处理。①# test数据不缓存test_source DataSet(test_data_seq_dir, test_data_label, test_meta_data, imgresize, False) # 调用自定义类DataSet 返回PyTorch支持的数据集对象且只对训练集进行缓存处理测试集不做缓存处理。②return train_source,test_source
2.2 代码实战实现load_dir函数加载图片文件名称---GaitSet_DataLoader.py第二部分
# 1.2 实现load_dir函数加载图片文件名称,
def load_dir(dataset_path,label_index,label_str):# 在load_dir函数中 通过文件夹的逐级遍历 将标签列表中每个人物的图片文件名称载入。# 该函数返回3个列表对象图片文件名称、图片文件名称对应的标签索引、图片文件名称对应的元数据(人物、行走条件、拍摄角度)data_seq_dir,data_label,meta_data [],[],[]for i_label in label_index: # 获取样本个体label_path os.path.join(dataset_path, label_str[i_label]) # 拼接目录for _seq_type in sorted(os.listdir(label_path)): # 获取样本类型普通条件、穿大衣、携带物品seq_type_path os.path.join(label_path, _seq_type) # 拼接目录for _view in sorted(os.listdir(seq_type_path)): # 获取拍摄角度_seq_dir os.path.join(seq_type_path, _view) # 拼接图片目录if len(os.listdir(_seq_dir)) 0: # 有图片data_seq_dir.append(_seq_dir) # 图片目录data_label.append(i_label) # 图片目录对应的标签meta_data.append((label_str[i_label], _seq_type, _view))else:print(No files:, _seq_dir) # 输出数据集中样本不完整的标签。# 当发现某个标签文件夹中没有图片时会将该标签输出。在使用时可以先用load_dir函数将整个数据集遍历一遍 并根据输出样本不完整的标签回填到第18行代码。return data_seq_dir, data_label, meta_data # 返回结果
2.3 实现定义数据类DataSet---GaitSet_DataLoader.py第三部分
# 1.3 实现定义数据类DataSet
# PyTorch提供了一个torch.utils.data接口可以用来对数据集进行封装。
# 在实现时只需要继承torch.utils.data.Dataset类并重载其__getitem__方法。
# 在使用时框架会向getitem方法传入索引index。在__getitem__方法内部根据指定index加载数据。
class DataSet(tordata.DataLoader):def __init__(self,data_seq_dir,data_label,meta_data,imgresize,cacheTrue): # 初始化self.data_seq_dir data_seq_dir # 存储图片文件名称self.data [None] * len(self.data_seq_dir) # 存放图片self.cache cache # 缓存标志self.meta_data meta_data # 数据的元信息self.data_label np.asarray(data_label) # 存放标签self.imgresize int(imgresize) # 载入的图片大小self.cut_padding int(float(imgresize)/64*10) # 指定图片裁剪的大小def load_all_data(self): # 加载所有数据for i in tqdm(range(len(self.data_seq_dir))):self.__getitem__(i)def __loader__(self,path): # 读取图片并裁剪frame_imgs self.img2xarray(path)/255.0# 将图片横轴方向的前10列与后10列去掉frame_imgs frame_imgs[:,:,self.cut_padding:-self.cut_padding]return frame_imgsdef __getitem__(self, index): # 加载指定索引数据if self.data[index] is None: # 第一次加载data self.__loader__(self.data_seq_dir[index])else:data self.data[index]if self.cache : # 保存到缓存里self.data[index] datareturn data,self.meta_data[index],self.data_label[index]def img2xarray(self,file_path): # 读取指定路径的数据frame_list [] # 存放图片数据imgs sorted(list(os.listdir(file_path)))for _img in imgs : # 读取图片放到数组里_img_path os.path.join(file_path, _img)if os.path.isfile(_img_path):img np.asarray(Image.open(_img_path).resize((self.imgresize, self.imgresize)))if len(img.shape) 3: # 加载预处理后的图片frame_list.append(img[..., 0])else:frame_list.append(img)return np.asarray(frame_list, dtypenp.float) # [帧数高宽]def __len__(self): # 计算数据集长度return len(self.data_seq_dir)
2.4 代码实战测试数据集---train.py第一部分
# 1.4 测试数据集
# 在完成数据集的制作之后对其进行测试。
# 将样本文件夹perdata放到当前目录下并编写代码生成数据集对象。
# 从数据集对象中取出一条数据并显示该数据的详细内容。
from GaitSet_DataLoader import load_data # 加载项目模块# 输出当前CPU-GPU
print(torch V,torch.__version__,cuda V,torch.version.cuda)
pathstr ./data/perdata/perdata
label_train_num 10 # 训练集的个数。剩下是测试集
batch_size (3, 6)
frame_num 8
hidden_dim 64
# label_train_num 70 # 训练数据集的个数剩下的是测试数据库dataconf { # 方便导入参数dataset_path:pathstr,imgresize:64,label_train_num:label_train_num,label_shuffle:True,
}
print(加载训练数据...)
train_source,test_cource load_data(**dataconf) # 一次全载入经过load_data()分别生成训练和测试数据集对象。
print(训练数据集长度,len(train_source)) # label_num * type10* view11
# 显示数据集里面的标签
train_label_set set(train_source.data_label)
print(数据集里面的标签,train_label_set)dataimg,matedata,lebelimg train_source.__getitem__(4) # 从数据集中获取一条数据并显示其详细信息。print(图片样本数据形状, dataimg.shape, 数据的元信息, matedata, 数据标签索引,lebelimg)plt.imshow(dataimg[0]) # 显示图片
plt.axis(off) # 不显示坐标轴
plt.show()def imshow(img):print(图片形状,np.shape(img))npimg img.numpy()plt.axis(off)plt.imshow(np.transpose(npimg, (1, 2, 0)))plt.show()imshow(torchvision.utils.make_grid(torch.from_numpy(dataimg[-10:]).unsqueeze(1),nrow10)) # 显示十张图片
2.5 代码实战实现自定义采集器---GaitSet_DataLoader.py第四部分
# 1.5 实现自定义采集器
# 步态识别模型需要通过三元损失进行训练。三元损失可以辅助模型特征提取的取向使相同标签的特征距离更近不同标签的特征距离更远。
# 由于三元损失需要输入的批次数据中要包含不同标签(这样才可以使用矩阵方式进行正/负样本的采样)需要额外对数据集进行处理。
# 这里使用自定义采样器完成含有不同标签数据的采样功能。# torch.utils.data.sampler类需要配合torch.utils.data.Data Loader模块一起使用。# torch.utils.data.DataLoader是PyTorch中的数据集处理接口。# 根据torch.utils.data.sampler类的采样索引在数据源中取出指定的数据并放到collate_fn中进行二次处理最终返回所需要的批次数据。# 实现自定义采样器TripletSampler类来从数据集中选取不同标签的索引并将其返回。# 再将两个collate_fn函数collate_fn_for_train、collate_fn_for_test分别用于对训练数据和测试数据的二次处理。class TripletSample(tordata.sampler.Sampler): # 继承torch.utils.data.sampler类实现自定义采样器。# TripletSampler类的实现在该类的初始化函数中支持两个参数传入数集与批次参数。其中批次参数包含两个维度的批次大小分别是标签个数与样本个数。def __init__(self,dataset,batch_size):self.dataset dataset # 获得数据集self.batch_size batch_size # 获得批次参数形状为标签个数样本个数self.label_set list(set(dataset.data_label)) # 标签集合def __iter__(self): # 实现采样器的取值过程:从数据集中随机抽取指定个数的标签并在每个标签中抽取指定个数的样本最终以生成器的形式返回。while(True):sample_indices []# 随机抽取指定个数的标签label_list random.sample(self.label_set,self.batch_size[0])# 在每个标签中抽取指定个数的样本for _label in label_list: # 按照标签个数循环data_index np.where(self.dataset.data_label _label)[0]index np.random.choice(data_index,self.batch_size[1],replaceFalse)sample_indices index.tolist()yield np.asarray(sample_indices) # 以生成器的形式返回def __len__(self):return len(self.dataset) # 计算长度# 用于训练数据的采样器处理函数
def collate_fn_train(batch,frame_num):# collate_fn_train函数会对采样器传入的批次数据进行重组并对每条数据按照指定帧数frame_num进行抽取。# 同时也要保证每条数据的帖数都大于等于帧数frame_num。如果帧数小于frame_num则为其添加重复帧。batch_data, batch_label,batch_meta [],[],[]batch_size len(batch) #获得数据条数for i in range(batch_size) : # 依次对每条数据进行处理batch_label.append(batch[i][2]) # 添加数据的标签batch_meta.append(batch[i][1]) # 添加数据的元信息data batch[i][0] # 获取该数据的样本信息if data.shape[0] frame_num: # 如果帧数较少则随机加入几个# 复制帧用于帧数很少的情况multy (frame_num - data.shape[0])//data.shape[0] 1# 额外随机加入的帧的个数choicenum (frame_num - data.shape[0])%data.shape[0]choice_index np.random(data.shape[0],choicenum,replace False)choice_index list(range(0,data.shape[9])) * multy choice_index.tolist()else: # 随机抽取指定个数的帧choice_index np.random.choice(data.shape[0],frame_num,replace False)batch_data.append(data[choice_index]) # 增加指定个数的帧数据# 重新组合合成用于训练的样本数据batch [np.asarray(batch_data),batch_meta,batch_label]return batchdef collate_fn_for_test(batch,frame_num): # 用于测试数据的采样器处理函数# collate_fn_for_test函数会对采样器传入的批次数据进行重组并按照批次数据中最大帧数进行补0对齐。# 同时也要保证母条数据的帧数都大于等于帧数frame_num。如果帧数小于frame_num则为其添加重复帧。batch_size len(batch) # 获得数据的条数batch_frames np.zeros(batch_size,np.int)batch_data,batch_label,batch_meta [],[],[]for i in range(batch_size): # 依次对每条数据进行处理batch_label.append(batch[i][2]) # 添加数据的标签batch_meta.append(batch[i][1]) # 添加数据的元信息data batch[i][0] # 获取该数据的帧样本信息if data.shape[0] frame_num: # 如果帧数较少随机加入几个print(batch_meta, data.shape[0])multy (frame_num - data.shape[0]) // data.shape[0] 1choicenum (frame_num - data.shape[0]) % data.shape[0]choice_index np.random.choice(data.shape[0], choicenum, replaceFalse)choice_index list(range(0, data.shape[0])) * multy choice_index.tolist()data np.asarray(data[choice_index])batch_frames[i] data.shape[0] # 保证所有的都大于等于frame_numbatch_data.append(data)max_frame np.max(batch_frames) # 获得最大的帧数# 对其他帧进行补0填充batch_data np.asarray([np.pad(batch_data[i], ((0, max_frame - batch_data[i].shape[0]), (0, 0), (0, 0)),constant, constant_values0)for i in range(batch_size)])# 重新组合成用于训练的样本数据batch [batch_data, batch_meta, batch_label]return batch
2.6 测试采样器---train.py第二部分
# 1.6 测试采样器
from GaitSet_DataLoader import TripletSample,collate_fn_trainbatch_size (4,8) # 定义批次4个标签每个标签8个数据
frame_num 32 # 定义帧数
num_workers torch.cuda.device_count() # 设置采样器的线程数# 在设置数据加载器额外启动进程的数量时最好要与GPU数量匹配即一个进程服务于一个GPU。如果额外启动进程的数量远远大于GPU数量则性能瓶颈主要会卡在GPU运行的地方起不到提升效率的作用。
print(当前GPU数量,num_workers)
if num_workers 1 : # 如果只有一块GPU或者没有GPU则使用主线程处理num_workers 0
print(数据加载器额外启动进程的数量,num_workers)# 实例化采样器得到对象triplet_sampler。
triplet_sampler TripletSample(train_source,batch_size)
# 初始化采样器的处理函数用偏函数的方法对采样器的处理函数进行初始化。
collate_train partial(collate_fn_train,frame_numframe_num)# 定义数据加载器每次迭代按照采样器的索引在train_source中取出数据
# 将对象triplet_sampler和采样器的处理函数collate_train传入tordata.DataLoader得到一个可用于训练的数据加载器对象train_loader。
# 同时对数据加载器额外启动进程的数量进行了设置如果额外启动进程的数量num_workers是0则在加载数据时不额外启动其他进程。
train_loader tordata.DataLoader(datasettrain_source,batch_samplertriplet_sampler, collate_fncollate_train,num_workersnum_workers)# 从数据加载器中取出一条数据
batch_data,batch_meta,batch_label next(iter(train_loader))
print(该批次数据的总长度,len(batch_data)) # 输出该数据的详细信息
print(每条数据的形状为,batch_data.shape)
print(batch_label) # 输出该数据的标签
2.7 代码实战定义基础卷积类---GaitSet.py第一部分
import torch
import torch.nn as nn
import torch.autograd as autograd
import torch.nn.functional as F# 搭建GaitSet模型: 分为两部分基础卷积(BasicConv2d) 类和GaitSetNet类。# 1.7 定义基础卷积类对原始卷积函数进行封装。在卷积结束后用Mish激活函数和批量正则化处理对特征进行二次处理。
class BasicConv2d(nn.Module):def __init__(self,in_channels,out_channels,kernel_size,**kwargs):super(BasicConv2d,self).__init__()self.conv nn.Conv2d(in_channels,out_channels,kernel_size,biasFalse,**kwargs) # 卷积操作self.BatchNorm nn.BatchNorm2d(out_channels) # BN操作def forward(self,x): # 自定义前向传播方法x self.conv(x)x x * ( torch.tanh(F.softplus(x))) # 实现Mish激活函数PyTorch没有现成的Mish激活函数手动实现Mish激活函数并对其进行调用。return self.BatchNorm(x) # 返回卷积结果
2.8 代码实战定义GaitSetNet类---GaitSet.py第二部分
# 1.8 定义GaitSetNet类
# ①实现3个MGP。
# ②对MGP的结果进行HPM处理。每层MGP的结构是由两个卷积层加一次下采样组成的。在主分支下采样之后与辅助分支所提取的帧级特征加和传入下一个MGP中。
class GaitSetNet(nn.Module):def __init__(self, hidden_dim, frame_num):super(GaitSetNet, self).__init__()self.hidden_dim hidden_dim # 输出的特征维度# 定义MGP部分cnls [1, 32, 64, 128] # 定义卷积层通道数量self.set_layer1 BasicConv2d(cnls[0], cnls[1], 5, padding2)self.set_layer2 BasicConv2d(cnls[1], cnls[1], 3, padding1)self.set_layer1_down BasicConv2d(cnls[1], cnls[1], 2, stride2) # 下采样操作通过步长为2的2x2卷积实现。self.set_layer3 BasicConv2d(cnls[1], cnls[2], 3, padding1)self.set_layer4 BasicConv2d(cnls[2], cnls[2], 3, padding1)self.set_layer2_down BasicConv2d(cnls[2], cnls[2], 2, stride2)# 下采样操作通过步长为2的2x2卷积实现。self.gl_layer2_down BasicConv2d(cnls[2], cnls[2], 2, stride2)# 下采样操作通过步长为2的2x2卷积实现。self.set_layer5 BasicConv2d(cnls[2], cnls[3], 3, padding1)self.set_layer6 BasicConv2d(cnls[3], cnls[3], 3, padding1)self.gl_layer1 BasicConv2d(cnls[1], cnls[2], 3, padding1)self.gl_layer2 BasicConv2d(cnls[2], cnls[2], 3, padding1)self.gl_layer3 BasicConv2d(cnls[2], cnls[3], 3, padding1)self.gl_layer4 BasicConv2d(cnls[3], cnls[3], 3, padding1)self.bin_num [1, 2, 4, 8, 16] # 定义MGP部分self.fc_bin nn.ParameterList([nn.Parameter(nn.init.xavier_uniform_(torch.zeros(sum(self.bin_num) * 2, 128, hidden_dim)))])def frame_max(self, x, n): # 用最大特征方法提取帧级特征# 调用torch.max函数实现从形状[批次个数帧数通道数高度宽度]的特征中沿着帧维度提取最大值得到形状[批次个数通道数高度宽度]的特征提取帧级特征的过程。return torch.max(x.view(n, -1, x.shape[1], x.shape[2], x.shape[3]), 1)[0] # 取max后的值def forward(self, xinput): # 定义前向处理方法n xinput.size()[0] # 形状为[批次个数,帧数高宽]x xinput.reshape(-1, 1, xinput.shape[-2], xinput.shape[-1])del xinput # 删除不用的变量# MGP 第一层x self.set_layer1(x)x self.set_layer2(x)x self.set_layer1_down(x)gl self.gl_layer1(self.frame_max(x, n)) # 将每一层的帧取最大值# MGP 第二层gl self.gl_layer2(gl)gl self.gl_layer2_down(gl)x self.set_layer3(x)x self.set_layer4(x)x self.set_layer2_down(x)# MGP 第三层gl self.gl_layer3(gl self.frame_max(x, n))gl self.gl_layer4(gl)x self.set_layer5(x)x self.set_layer6(x)x self.frame_max(x, n)gl gl x# srart-------HPM处理按照定义的特征尺度self.bin_num将输入特征分成不同尺度并对每个尺度的特征进行均值和最大化计算从而组合成新的特征放到列表feature中。feature list() # 用于存放HPM特征n, c, h, w gl.size()for num_bin in self.bin_num:z x.view(n, c, num_bin, -1)z z.mean(3) z.max(3)[0]feature.append(z)z gl.view(n, c, num_bin, -1)z z.mean(3) z.max(3)[0]feature.append(z)# end-------HPM处理按照定义的特征尺度self.bin_num将输入特征分成不同尺度并对每个尺度的特征进行均值和最大化计算从而组合成新的特征放到列表feature中。# 对HPM特征中的特征维度进行转化# srart-------将每个特征维度由128转化为指定的输出的特征维度hidden_dim。因为输入数据是三维的无法直接使用全连接API所以使用矩阵相乘的方式实现三维数据按照最后一个维度进行全连接的效果。feature torch.cat(feature, 2).permute(2, 0, 1).contiguous() # 62 n cfeature feature.matmul(self.fc_bin[0])feature feature.permute(1, 0, 2).contiguous()# end-------将每个特征维度由128转化为指定的输出的特征维度hidden_dim。因为输入数据是三维的无法直接使用全连接API所以使用矩阵相乘的方式实现三维数据按照最后一个维度进行全连接的效果。return feature # 返回结果
2.8.1 提取帧级特征的过程 2.9 自定义三元损失类---GaitSet.py第三部分
# 1.9 实现 自定义三元损失类
# 定义三元损失TripletLoss类 实现三元损失的计算。具体步骤
# ①对输入样本中的标签进行每两个一组自由组合生成标签矩阵从标签矩阵中得到正/负样本对的掩码
# ②对输入样本中的特征进行每两个一组自由组合生成特征矩阵计算出特征矩阵的欧氏距离。
# ③按照正/负样本对的掩码对带有距离的特征矩阵进行提取得到正/负两种标签的距离。
# ④将正/负两种标签的距离相减再减去间隔值得到三元损失。
class TripletLoss(nn.Module): # 定义三元损失类def __init__(self,batch_size,hard_or_full,margin): # 初始化super(TripletLoss, self).__init__()self.batch_size batch_sizeself.margin margin # 正/负样本的三元损失间隔self.hard_or_full hard_or_full # 三元损失方式def forward(self,feature,label): # 定义前向传播方法# 接收的参数feature为模型根据输入样本所计算出来的特征。该参数的形状为[n.m.d]nHPM处理时的尺度个数62。m样本个数32。d维度256。# 在计算过程中将三元损失看作n份用矩阵的方式对每份m个样本、d维度特征做三元损失计算最后将这n份平均。n,m,d feature.size() # 形状为[n,m,d]# 生成标签矩阵并从中找出正/负样本对的编码输出形状[n,m,m]并且展开hp_mask (label.unsqueeze(1) label.unsqueeze(2)).view(-1)hn_mask (label.unsqueeze(1) ! label.unsqueeze(2)).view(-1)dist self.batch_dist(feature) # 计算出特征矩阵的距离mean_dist dist.mean(1).mean(1) # 计算所有的平均距离dist dist.view(-1)# start-----计算三元损失的hard模式hard_hp_dist torch.max(torch.masked_select(dist, hp_mask).view(n, m, -1), 2)[0]hard_hn_dist torch.min(torch.masked_select(dist, hn_mask).view(n, m, -1), 2)[0]hard_loss_metric F.relu(self.margin hard_hp_dist - hard_hn_dist).view(n, -1) # 要让间隔最小化到0为止# 对三元损失取均值得到最终的hard模式loss[n]hard_loss_metric_mean torch.mean(hard_loss_metric,1)# end-----计算三元损失的hard模式# start-----计算三元损失的full模式# 计算三元损失的full模型full_hp_dist torch.masked_select(dist, hp_mask).view(n, m, -1, 1) # 按照编码得到所有正向样本距离[n,m,正样本个数,1] [62, 32, 8, 1]full_hn_dist torch.masked_select(dist, hn_mask).view(n, m, 1, -1) # 照编码得到所有负向样本距离[n,m,1,负样本个数] [62, 32, 1, 24]full_loss_metric F.relu(self.margin full_hp_dist - full_hn_dist).view(n, -1) # 让正/负间隔最小化到0为止 [62,32*8*24]# 计算[n]中每个三元损失的和full_loss_metric_sum full_loss_metric.sum(1) # 计算[62]中每个loss的和# 计算[n]中每个三元损失的个数(去掉矩阵对角线以及符合条件的三元损失)full_loss_num (full_loss_metric ! 0).sum(1).float() # 计算[62]中每个loss的个数# 计算均值full_loss_metric_mean full_loss_metric_sum / full_loss_num # 计算平均值full_loss_metric_mean[full_loss_num 0] 0 # 将无效值设为0# end-----计算三元损失的full模式return full_loss_metric_mean, hard_loss_metric_mean, mean_dist, full_loss_num # ,lossdef batch_dist(self, x): # 计算特征矩阵的距离x2 torch.sum(x ** 2, 2) # 平方和 [62, 32]# dist [62, 32, 32]dist x2.unsqueeze(2) x2.unsqueeze(2).transpose(1, 2) - 2 * torch.matmul(x, x.transpose(1, 2)) # 计算特征矩阵的距离dist torch.sqrt(F.relu(dist)) # 对结果进行开平方return distdef ts2var(x):return autograd.Variable(x).cuda()def np2var(x):return ts2var(torch.from_numpy(x))
2.10 代码实战训练模型并保存权重文件---train.py第三部分
# 1.10 训练模型并保存权重文件:实例化模型类并遍历数据加载器进行训练。
from GaitSet import GaitSetNet, TripletLoss, np2varhidden_dim 256 # 定义样本的输出维度
encoder GaitSetNet(hidden_dim, frame_num).float()
encoder nn.DataParallel(encoder) # 使用多卡并行训练
encoder.cuda() # 将模型转储到GPU
encoder.train() # 设置模型为训练模型optimizer Ranger(encoder.parameters(), lr0.004) # 定义Ranger优化器TripletLossmode full # 设置三元损失的模式
triplet_loss TripletLoss(int(np.prod(batch_size)), TripletLossmode, margin0.2) # 实例化三元损失
triplet_loss nn.DataParallel(triplet_loss) # 使用多卡并行训练
triplet_loss.cuda() # 将模型转储到GPUckp checkpoint # 设置模型名称
os.makedirs(ckp, exist_okTrue)
save_name _.join(map(str, [hidden_dim, int(np.prod(batch_size)),frame_num, full]))ckpfiles sorted(os.listdir(ckp)) # 载入预训练模型
if len(ckpfiles) 1:modecpk os.path.join(ckp, ckpfiles[-2])optcpk os.path.join(ckp, ckpfiles[-1])encoder.module.load_state_dict(torch.load(modecpk)) # 加载模型文件optimizer.load_state_dict(torch.load(optcpk))print(load cpk !!! , modecpk)
# 定义训练参数
hard_loss_metric []
full_loss_metric []
full_loss_num []
dist_list []
mean_dist 0.01
restore_iter 0
total_iter 1000 # 迭代次数
lastloss 65535 # 初始的损失值
trainloss []_time1 datetime.now() # 计算迭代时间
for batch_data, batch_meta, batch_label in train_loader:restore_iter 1optimizer.zero_grad() # 梯度清零batch_data np2var(batch_data).float() # torch.cuda.DoubleTensor变为torch.cuda.FloatTensorfeature encoder(batch_data) # 将标签转为张量# 将标签转化为张量target_label np2var(np.array(batch_label)).long() # len32triplet_feature feature.permute(1, 0, 2).contiguous() # 对特征结果进行变形形状变为[62, 32, 256]triplet_label target_label.unsqueeze(0).repeat(triplet_feature.size(0), 1) # 复制12份标签[62, 32]# 计算三元损失(full_loss_metric_, hard_loss_metric_, mean_dist_, full_loss_num_) triplet_loss(triplet_feature, triplet_label)if triplet_loss.module.hard_or_full full: #提取损失值loss full_loss_metric_.mean()else:loss hard_loss_metric_.mean()trainloss.append(loss.data.cpu().numpy()) # 保存损失值hard_loss_metric.append(hard_loss_metric_.mean().data.cpu().numpy())full_loss_metric.append(full_loss_metric_.mean().data.cpu().numpy())full_loss_num.append(full_loss_num_.mean().data.cpu().numpy())dist_list.append(mean_dist_.mean().data.cpu().numpy())if loss 1e-9: # 若损失值过小则不参加反向传播loss.backward()optimizer.step()else:print(损失值过小, loss)if restore_iter % 1000 0:print(restore_iter 1000 time:, datetime.now() - _time1)_time1 datetime.now()if restore_iter % 100 0: # 输出训练结果print(iter {}:.format(restore_iter), end)print(, hard_loss_metric{0:.8f}.format(np.mean(hard_loss_metric)), end)print(, full_loss_metric{0:.8f}.format(np.mean(full_loss_metric)), end)print(, full_loss_num{0:.8f}.format(np.mean(full_loss_num)), end)print(, mean_dist{0:.8f}.format(np.mean(dist_list)), end)print(, lr%f % optimizer.param_groups[0][lr], end)print(, hard or full%r % TripletLossmode)if lastloss np.mean(trainloss): # 保存模型print(lastloss:, lastloss, loss:, np.mean(trainloss), need save!)lastloss np.mean(trainloss)modecpk os.path.join(ckp,{}-{:05}-encoder.pt.format(save_name, restore_iter))optcpk os.path.join(ckp,{}-{:05}-optimizer.pt.format(save_name, restore_iter))torch.save(encoder.module.state_dict(), modecpk) # 一定要用encoder对象的module中的参数进行保存。否则模型数的名字中会含有“module”字符串使其不能被非并行的模型载入。torch.save(optimizer.state_dict(), optcpk)else:print(lastloss:, lastloss, loss:, np.mean(trainloss), dont save)print(__________________)sys.stdout.flush()hard_loss_metric.clear()full_loss_metric.clear()full_loss_num.clear()dist_list.clear()trainloss.clear()if restore_iter total_iter: # 如果满足迭代次数则训练结束break
2.11 代码实战测试模型---GaitSet_test.py全
import os
import numpy as np
from datetime import datetime
from functools import partial
from tqdm import tqdm
import torch.nn as nn
import torch.nn.functional as F
import torch
import torch.utils.data as tordata
from GaitSet_DataLoader import load_data,collate_fn_for_test
from GaitSet import GaitSetNet,np2var
# 为了测试模型识别步态的效果不依赖于拍摄角度和行走条件可以多角度识别人物步分别取3组行走条件(普通、穿大衣、携带包裹)的样本输入模型查看该模型所计算出的生征与其他行走条件的匹配程度。
# 1.11 测试模型
print(torch v:,torch.__version__,cuda v:,torch.version.cuda)pathstr ./data/perdata/perdata
label_train_num 70 # 训练数据集的个数剩下是测试数据集
batch_size (8,16)
frame_num 30
hidden_dim 256# 设置处理流程
num_workers torch.cuda.device_count()
print(cuda.device_count,num_workers)
if num_workers 1: # 仅有一块GPU或没有GPU则使用CPUnum_workers 0
print(num_workers,num_workers)dataconf { # 初始化数据集参数dataset_path:pathstr,imgresize:64,label_train_num:label_train_num, # 训练数据集的个数剩下的是测试数据集label_shuffle:True,
}
train_source,test_source load_data(**dataconf)sampler_batch_size 4 # 定义采样批次
# 初始化采样数据的二次处理函数
collate_train partial(collate_fn_for_test,frame_numframe_num)
# 定义数据加载器每次迭代按照采样器的索引在test_source中取出数据
test_loader tordata.DataLoader(datasettest_source,batch_sizesampler_batch_size,samplertordata.sampler.SequentialSampler(test_source),collate_fncollate_train,num_workersnum_workers)# 实例化模型
encoder GaitSetNet(hidden_dim,frame_num).float()
encoder nn.DataParallel(encoder)
encoder.cuda()
encoder.eval()ckp ./checkpoint # 设置模型文件路径
save_name _.join(map(str,[hidden_dim,int(np.prod( batch_size )),frame_num,full]))
ckpfiles sorted(os.listdir(ckp)) # 加载模型
print(ckpfiles::::,ckpfiles)
if len(ckpfiles) 1:# modecpk ckp /ckpfiles[-1]modecpk os.path.join(ckp,ckpfiles[-1])encoder.module.load_state_dict(torch.load(modecpk), False) # 加载模型文件print(load cpk !!! , modecpk)
else:print(No cpk!!!)def cuda_dist(x,y): # 计算距离x torch.from_numpy(x).cuda()y torch.from_numpy(y).cuda()dist torch.sum(x ** 2, 1).unsqueeze(1) torch.sum(y ** 2, 1).unsqueeze(1).transpose(0, 1) - 2 * torch.matmul(x, y.transpose(0, 1))dist torch.sqrt(F.relu(dist))return distdef de_diag(acc,each_angleFalse): # 计算多角度准确率,计算与其他拍摄角度相关的准确率result np.sum(acc - np.diag(np.diag(acc)), 1) / 10.0if not each_angle:result np.mean(result)return resultdef evaluation(data): # 评估模型函数feature, meta, label dataview, seq_type [], []for i in meta:view.append(i[2])seq_type.append(i[1])label np.array(label)view_list list(set(view))view_list.sort()view_num len(view_list)probe_seq [[nm-05, nm-06], [bg-01, bg-02], [cl-01, cl-02]] # 定义采集数据的行走条件gallery_seq [[nm-01, nm-02, nm-03, nm-04]] # 定义比较数据的行走条件num_rank 5 # 取前5个距离最近的数据acc np.zeros([len(probe_seq), view_num, view_num, num_rank])for (p, probe_s) in enumerate(probe_seq): # 依次将采集的数据与比较数据相比for gallery_s in gallery_seq:# Start---获取指定条件的样本特征后按照采集数据特征与比较数据特之间的距离大小匹配对应的标签并计算其准确率。# 步骤如下:# ①计算采集数据特征与比较数据特征之间的距离。# ②对距离进行排序返回最小的前5个排序索引。# ③按照索引从比较数据中取出前5个标签并与采集数据中的标签做比较。# ④将比较结果的正确数量累加起来使每个样本对应5个记录分别代表前5个果中的识别正确个数。如[True,True,True,False,False]# 累加后结果为[1,2,3,3,3]表明离采集数据最近的前3个样本特征中识别出来3个正确结果前5个样本特征中识别出来3个正确结果。# ⑤将累加结果与0比较并判断每个排名中大于0的个数。# ⑥将排名1-5的识别正确个数分别除以采集样本个数再乘以100便得到每个排名的准确率for (v1, probe_view) in enumerate(view_list):for (v2, gallery_view) in enumerate(view_list): # 遍历所有视角gseq_mask np.isin(seq_type, gallery_s) np.isin(view, [gallery_view])gallery_x feature[gseq_mask, :] # 取出样本特征gallery_y label[gseq_mask] # 取出标签pseq_mask np.isin(seq_type, probe_s) np.isin(view, [probe_view])probe_x feature[pseq_mask, :] # 取出样本特征probe_y label[pseq_mask] # 取出标签if len(probe_x) 0 and len(gallery_x) 0:dist cuda_dist(probe_x, gallery_x) # 计算特征之间的距离idx dist.sort(1)[1].cpu().numpy() # 对距离按照由小到大排序返回排序后的索引【0】是排序后的值# 分别计算前五个结果的精确率步骤③~⑥rank_data np.round(np.sum(np.cumsum(np.reshape(probe_y,[-1,1]) gallery_y[idx[:,0:num_rank]],1)0,0)*100/dist.shape[0],2)# End---获取指定条件的样本特征后按照采集数据特征与比较数据特之间的距离大小匹配对应的标签并计算其准确率。acc[p, v1, v2, 0:len(rank_data)] rank_datareturn accprint(test_loader, len(test_loader))
time datetime.now()
print(开始评估模型...)
feature_list list()
view_list list()
seq_type_list list()
label_list list()
batch_meta_list []# 在遍历数据集前加入了withtorch.nograd()语句。该语句可以使模型在运行时不额外创建梯度相关的内存。
# 在显存不足的情况下使用withtorch.nogradO语句非常重要它可以节省系统资源。
# 虽然在实例化模型时使用了模型的eval方法来设置模型的使用方式但这仅注意是修改模型中具有状态分支的处理流程(如dropout或BN等)并不会省去创建显存存放梯度的开销。
with torch.no_grad():for i, x in tqdm(enumerate(test_loader)): # 遍历数据集batch_data, batch_meta, batch_label xbatch_data np2var(batch_data).float() # [2, 212, 64, 44]feature encoder(batch_data) # 将数据载入模型 [4, 62, 64]feature_list.append(feature.view(feature.shape[0], -1).data.cpu().numpy()) # 保存特征结果共sampler_batch_size 个特征batch_meta_list batch_metalabel_list batch_label # 保存样本标签# 将样本特征、标签以及对应的元信息组合起来
test (np.concatenate(feature_list, 0), batch_meta_list, label_list)
acc evaluation(test) # 对组合数据进行评估
print(评估完成. 耗时:, datetime.now() - time)for i in range(1): # 计算第一个的精确率print(Rank-%d 准确率 % (i 1))print(携带包裹: %.3f,\t普通: %.3f,\t穿大衣: %.3f % (np.mean(acc[0, :, :, i]),np.mean(acc[1, :, :, i]),np.mean(acc[2, :, :, i])))for i in range(1): # 计算第一个的精确率除去自身的行走条件print(Rank-%d 准确率(除去自身的行走条件) % (i 1))print(携带包裹: %.3f,\t普通: %.3f,\t穿大衣: %.3f % (de_diag(acc[0, :, :, i]),de_diag(acc[1, :, :, i]),de_diag(acc[2, :, :, i])))np.set_printoptions(precision2, floatmodefixed) # 设置输出精度
for i in range(1): # 显示多拍摄角度的详细结果print(Rank-%d 的每个角度准确率 (除去自身的行走条件) % (i 1))print(携带包裹:, de_diag(acc[0, :, :, i], True))print(普通:, de_diag(acc[1, :, :, i], True))print(穿大衣:, de_diag(acc[2, :, :, i], True))