1. 在使用resnet50的全链接层之前的特征作为分类特征的时候,最好在gap后进行一下batchnorm,单单是使用这样一个bn层,就能将性能提升很多个点。
2. 同样的使用resnet50的全链接层前面的特征作为分类特征,直接就将这个2048维的向量送入分类层就完事了,不要再增加embedding 层了,加了之后性能很不好,原因可能是这个embeding 层相当于是又增加了一个随机出来的线性层,而我们加载的resnet50基本上都是在imagenet上训练好的模型,参数都是具有一定的特征提取能力了, 随机出现的embedding层 大概率还会拖累网络。
3 一些比对实验的验证结果:
(1)在market1501上,做了两组比对实验,分别是将resnet50的全卷积之前的网络,gap后形成2048维度的向量,有embedding 和没有embedding的两组比对实验。有embedding的是将2048维送过一个线性层转为1024维度的向量,然后再送入BN层、normalize层、和分类层,没有embedding的是,直接将2048维度的向量送入BN 层、normalize层、和分类层。实验结果显示,没有经过embedding的,性能更好。每个实验做了两次,结果如下:
1. 没有embedding的
Mean AP: 83.5%
CMC Scores:
top-1 93.1%
top-5 97.7%
top-10 98.7%
Mean AP: 83.2%
CMC Scores:
top-1 92.9%
top-5 97.7%
top-10 98.5%
2. 有embedding层的实验结果
Mean AP: 77.9%
CMC Scores:
top-1 90.2%
top-5 97.2%
top-10 98.3%
Mean AP: 77.6%
CMC Scores:
top-1 90.9%
top-5 97.1%
top-10 98.2%
分析可知,效果很明显,加了embedding,会使得性能大幅度下降
(2)在将数据送入网络之前,需要对数据进行预处理,其中的一步就是将数据tensor化后,在进行normalize(),一般的在进行这个normalize()的时候,都是使用了imagenet中的数据:mean = [0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225],但是不同的数据集中,这个统计数据是不同的,我就设置了一组比对实验,这个数据分别是使用了imagenet提供的一般化数据,以及我所使用的market1501数据集的统计了train中所有数据的统计值,mean=[0.415, 0.389, 0.384], std=[0.196, 0.190, 0.188]。两组实验分别做了两次,结果如下:
使用imagenet 的统计数据的结果:
Mean AP: 83.5%
CMC Scores:
top-1 93.1%
top-5 97.7%
top-10 98.7%
Mean AP: 83.2%
CMC Scores:
top-1 92.9%
top-5 97.7%
top-10 98.5%
使用了market1501 train中所有图片的统计结果的训练结果为:
Mean AP: 83.4%
CMC Scores:
top-1 93.1%
top-5 97.5%
top-10 98.5%
Mean AP: 83.8%
CMC Scores:
top-1 93.3%
top-5 97.7%
top-10 98.6%
分析可知,在预处理阶段,使用的normalize 的数值不同,几乎不会对训练数据产生影响。
(3)在reid的分类问题中,训练出来的最后的线性分类器,在初始化分类器的数据时,可能是像大部分的网络那样,使用 init.normal_(self.classifier.weight, std=0.001) 这样的初始化数据;也可以是在初始化的时候,将各个类别的聚类中心,赋值给这个线性分类层,这样的数据,在最初阶段就更贴阶真实的中心,会使得训练过程一直向着一个更优的方向进行。所以我做了对比实验,实验对象是分类器的初始化数据,分别是标准分布初始化,以及使用了数据的统计聚类中心作为初始化的数据。这个中心的计算方式是这样的,我的网络使用resnet50作为backbone,利用这个预训练的参数,将model设置为eval()模式,提取train中的所有数据的特征,然后算出各个类的平均特征,作为聚类中心,并且将这个中心进行了normalize()化,也就是单位化了,模型的训练结果如下所示:
1 随机初始化的网络结果:
Mean AP: 83.5%
CMC Scores:
top-1 93.1%
top-5 97.7%
top-10 98.7%
Mean AP: 83.2%
CMC Scores:
top-1 92.9%
top-5 97.7%
top-10 98.5%
2 使用类中心作为初始数值:
Mean AP: 85.6%
CMC Scores:
top-1 94.0%
top-5 98.0%
top-10 98.7%
Mean AP: 85.7%
CMC Scores:
top-1 94.4%
top-5 98.0%
top-10 98.9%
结果很明显,分类器使用更好的初始化结果,会很大幅度地提升网络的性能。
(4) 在分类层进行训练的时候,对于分类层的参数,只会按照梯度进行更新,不会考虑将分类中心进行单位化,我设置了两组实验,分别是:(1)正常的,训练过程不对分类层进行单位化;(2)在训练过程,对分类层中的参数进行单位化,也就是在每次forward()之前,先将后面的分类层的类中心进行单位化,单位化的代码如下所示:
self.classifier.weight.data.copy_(F.normalize( self.model.classifier.weight.data ,dim=1))
两组实验的结果如下所示:
1.没有增加单位化分类层的类中心的操作
Mean AP: 83.5%
CMC Scores:
top-1 93.1%
top-5 97.7%
top-10 98.7%
Mean AP: 83.2%
CMC Scores:
top-1 92.9%
top-5 97.7%
top-10 98.5%
2.加了单位化的操作
Mean AP: 76.8%
CMC Scores:
top-1 89.7%
top-5 95.8%
top-10 97.5%
Mean AP: 77.8%
CMC Scores:
top-1 90.1%
top-5 95.3%
top-10 96.7%
结果是惨不忍睹,看样加了这个操作,不单是没有提升性能,反而是降低了不少。可是为什么会这样呢?难道是我的代码有问题?这个问题得慢慢思考一下,有可能是代码有问题。
(5)在上面的那组实验产生了性能退化的效果以后,我就在想为什么会加了单位化后会降低这么多性能。因为单位化后,每个类中心都有了统一的标准,为什么就不行了呢?我在不佳单位化的实验中,观察了这个分类层类中心的二范数在,也就是模长在训练过程中是如何变化的,由于参数是标准分不随机赋值的,刚开始,每个类的类中心的二范数是在很小的数值上,最开始阶段大概在0.045左右(2048维度的向量),然后一点一点增加,逐渐增加到1.5左右,并且会稳定在这个数值上面。我就在想,是不是最开始就把这些类中心,由一个很小的二范数数值,增大到很大的二范数数值,影响过大了。我于是又做了一组实验,就是在初始化类中心的时候,不是使用随机的初始化数据,而是先使用训练数据计算出粗略的类中心,再将这个中心单位化,就像第三组(3)的那样,给一个比较大的、比较好的初始值。比对的结果分别是:(1)随机初始化的类中心;(2)粗略的类中心的初始化,结果如下:
1 随机初始化的结果
Mean AP: 76.8%
CMC Scores:
top-1 89.7%
top-5 95.8%
top-10 97.5%
Mean AP: 77.8%
CMC Scores:
top-1 90.1%
top-5 95.3%
top-10 96.7%
2 使用类中心初始化
Mean AP: 79.7%
CMC Scores:
top-1 91.4%
top-5 96.4%
top-10 97.5%
Mean AP: 79.7%
CMC Scores:
top-1 91.1%
top-5 96.0%
top-10 97.7%
比对试验结果可以发现,使用类中心初始化,是可以一定程度上提高性能,但是和不加单位化的进行比较,比如第三组(3),性能还是有了下降。
(6)在将线性分类层送入crossentropy()之前,我们需要对这个线性分类层输出的prob进行一个temperature 系数的调整,temperature系数有什么作用,可以看这个博客里面的第八条解释,线性分类层输出的是prob,我们传递给crossentropy()的是prob/self.temp,下面的代码是描述了该过程的样例代码:
prob = self.classifier(bn_x)
return prob/self.temp
这个系数设置的不同,会影响到最终的模型性能,我设置了多组实验,temp的值,分别设置为 0.05、0.1、0.2、0.5、1.0 各个参数对应的训练结果如下所示
1 temp = 0.05的结果
Mean AP: 81.1%
CMC Scores:
top-1 92.7%
top-5 97.3%
top-10 98.6%
2 temp = 0.1 的结果
Mean AP: 82.8%
CMC Scores:
top-1 92.5%
top-5 97.6%
top-10 98.8%
3 temp = 0.2 的结果
Mean AP: 69.8%
CMC Scores:
top-1 84.5%
top-5 92.8%
top-10 94.8%
4 temp = 0.5的结果
Mean AP: 48.1%
CMC Scores:
top-1 67.7%
top-5 84.1%
top-10 89.0%
5 temp = 1.0的结果
Mean AP: 31.7%
CMC Scores:
top-1 50.8%
top-5 72.9%
top-10 80.5%
比对结果可以发现,不同的temp数值,对结果的影响是比较大的,相对来说,当temp的数值保持在0.1左右的时候,性能比较好。
(7)如上面的第七组实验那样所述,temp系数会影响到训练的结果,那么可不可以将这个数值设为随着训练在逐步变化的数值呢?所以我做了两组实验,分别是:(1)让temp随着训练一点点变大;(2)让temp随着训练一点点变小。变大和变小的程序如下所述:
def train(self, epoch=None, data_loader_source=None,
optimizer=None, print_freq=10, train_iters=400):
self.encoder.train()
if (self.epoch_count == epoch):
self.epoch_count += 1
self.encoder.module.temp = 0.1 / (1 + math.log(self.epoch_count))#让temp随着epoch的增加,逐渐减小,最开始的temp设置为0.1
self.encoder.module.temp = 0.1 × (1 + math.log(self.epoch_count))#让temp随着epoch的增加,逐渐增大,最开始的temp设置为0.1
训练出来的网络性能如下面所示:
1. temp随着训练的过程逐渐增大
Mean AP: 75.4%
CMC Scores:
top-1 88.1%
top-5 94.7%
top-10 96.3%
2. temp随着训练过程逐渐减小
Mean AP: 62.4%
CMC Scores:
top-1 81.1%
top-5 92.9%
top-10 96.1%
3.作为baseline,当temp设置为固定数值0.1不变的时候,结果为
Mean AP: 82.8%
CMC Scores:
top-1 92.5%
top-5 97.6%
top-10 98.8%
到底是尖锐一些好,还是平滑一些好;还是先尖锐,后平滑;还是先平滑,后尖锐。这个东西是有待探索的。因为这个尖锐程度会影响到损失函数的数值的。
(8)将market1501的训练集上图片行人前景,和duke上的训练集中的背景结合,生成market行人为人物,duke为背景的训练集,然后再进行测试。融合后的图片如下图所示:
做了比对试验,分别是使用原来的market图片进行训练和使用融合后的图片进行训练,试验结果如下:
1 在融合后的图片(market为前景,duke为背景)上进行训练,使用正常的图片进行测试:
Mean AP: 66.4%
CMC Scores:
top-1 84.6%
top-5 92.9%
top-10 95.2%
2 在正常的market图片上进行训练,使用正常的图片进行测试:
Mean AP: 78.8%
CMC Scores:
top-1 90.4%
top-5 95.9%
top-10 97.2%
3 在融合后的图片上训练,然后直接迁移到duke身上进行测试:
Mean AP: 9.5%
CMC Scores:
top-1 19.4%
top-5 31.5%
top-10 37.8%
4 在正常的market图片上训练,然后直接迁移到duke上进行测试:
Mean AP: 15.9%
CMC Scores:
top-1 29.1%
top-5 45.5%
top-10 53.1%
5 在正常的market上训练,在融合的market为前景的图片上进行测试:
Mean AP: 45.7%
CMC Scores:
top-1 66.1%
top-5 81.4%
top-10 86.0%
6 在融合的market上训练,在融合的market上测试:
Mean AP: 66.3%
CMC Scores:
top-1 83.8%
top-5 92.3%
top-10 94.4%
4 作对比实验时候的注意事项
1在做多个实验的比对时候,一定要使用parser在命令行中对实验参数进行设置,不要改了一点在服务器上运行一次,不一定哪里就出了问题。并且要做控制变量,不要依次实验改了很多设置。
5 在做跨域问题的时候,在采用聚类方法的时候,现有的大部分模型都是将源域和目标域数据应用到同一个模型的训练中,我就进行了一个实验,就是采用了一个完全独立的模型,这个模型在训练的过程中只是采用聚类后的目标域数据作为feedforward的数据,但是经过实验发现,这样训练得到的模型的性能不如将源域和目标域同时应用到同一个模型中的性能好。
5 在UDA re-ID中, 不要将target作为source的一个增加数据集,就是在计算Cross entropy的时候,不要将src和tgt的数据结合到一块了,这样会导致性能下降。就是下面的代码部分
if self.classifier_mixed:
if epoch<self.target_ok:
loss_src_ce = self.criterion_ce(src_prec[:,:self.src_classes], src_labels,weight_src)
else:
loss_src_ce = self.criterion_ce(src_prec[:, :self.src_classes+num_cluster], src_labels,weight_src)
不要这么用