多相机行人跟踪[04]-用于行人重识别的余弦相似度网络(中)
source link: https://yerfor.github.io/2019/12/13/dl-07/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
在上一篇深度学习随笔中, 我们成功地在TensorRT
框架下跑通了行人特征提取网络,但模型性能不佳,这一篇文章的主要工作是,优化网络结构和训练过程,尽可能地提升了模型的性能。
本文的主要内容在于复现<cosine metric learning>论文实现的行人特征提取网络,这部分的工作包括:
- 采用了轻量级的
Resnet-block
- 自定义了针对余弦相似度优化的
cosine-softmax
作为分类器(classifier
)的激活函数 - 为获取理想的
feature
层向量,对feature
层和分类器的权重做出约束 - 使用生成器(
Generator
)来支持大批量数据集
1.网络结构
由于时间有限,来不及在电脑上画好看的图了,先手写一个placeholder。
1.1 轻量级的Resnet-block
Cosine-metric-learning
采用的Resnet-block
相比起Resnet
论文里的结构有所改进。
先说共同点,都有Identity和Convolutional两种Block,并且输入和输出的关系也是一样的,即Identity的输入输出Shape一致,而Convolutional的输出比输入在尺寸上少了一半,通道数增加了一倍。
再说不同点,cosine-metric-learning将Block内(1,1)的卷积层删去,转而采用两个(3,3)的卷积层,如下图所示:
1.2 自定义的cosine-softmax激活函数
1.2.1 传统Softmax函数在cosine-metric中面临的问题
假设一个二分类问题,输出的feature是一个shape=(2,)的向量(用下图所示的二维平面表示)。将该向量送入softmax中,会得到一个概率分布的向量,shape也是(2,),该向量中的每一位标量代表输入的图像是某一类的概率。我们把概率分布的第一个标量取出来,作为下图的画的等高线考察的值。
1.2.2 针对余弦区分度优化的cosine-softmax函数
传统的softmax
可以用下式描述:
cosine-softmax
的定义式如下:
二者的对比:
参数k的作用:
cosine-softmax
的Tensorflow2.0
实现:
class TrainableCosineSoftmax(tf.keras.layers.Layer): |
实际使用方法:
feature_layer = Dense(classes, name='feature', activation='relu', kernel_regularizer=tf.keras.regularizers.l2(),kernel_constraint=tf.keras.constraints.UnitNorm(), use_bias=False) |
1.2.3 两种激活函数的效果对比:
1.3 对feature层的参数约束
1.3.1 feature层的参数约束
- 对参数进行
l2
正则化,防止过拟合 - 保证权重的参数二阶范数是1
- 不使用
bias
具体实现:
feature_layer = Dense(classes, name='feature', activation='relu', kernel_regularizer=tf.keras.regularizers.l2(),kernel_constraint=tf.keras.constraints.UnitNorm(), use_bias=False) |
1.4 对feature层输出进行归一化
feature的输出,即为下面cosine-softmax
函数里的r
,必须要保证归一化,即单位方向向量。
- 具体实现——使用
tensorflow1.0
的函数,自定义了一个层:
l2_normalization = tf.keras.layers.Lambda(lambda x: tf.nn.l2_normalize(x, dim=1), name='l2_normalization') |
1.5 一些已经解决的小问题
引入cosine-softmax
函数后,网络的loss
在训练时总会在某一个新的epoch
骤然变成nan
:
经过排查,发现可能是因为我们的cosine-softmax
过于严格,导致其输出时,正确的那一类对应的probability是0,由于我们用的损失函数是交叉熵,其计算公式为:
上式中,$y_{true}$是一个one-hot格式的向量,只在所表示的那类的位置取1,其他均为0;$y_{pred}$即为网络输出的预测向量,输出的是一个图像相对于所有分类的一个概率分布.
言归正传,对上面的式子,整个$\Sigma$里面实际上只有正确的那一类对应的项不等于0,所以当网络输出的预测向量对正确那类的预测概率是0时,交叉熵的值就是$ln(0)\rightarrow -\infin$。导致loss变成nan
。
- 我的解决方法是在交叉熵计算信息量的公式里面加上一个很小的偏置,以确保$ln()$里面的数不会取到0,于是新的交叉熵公式变成:
修改loss
函数后,nan
的现象确实消失了。
1.6 一些论文采用的新特性的失败尝试
1.6.1 Dropout/AlphaDropout
Dropout层不能转换成TRT
1.6.2 ‘elu’/‘selu’
会导致梯度消失等问题。
1.6.3 BatchNormalization
不能转换成TRT
1.6.4 triplet loss/magnet loss
还没搞明白这两个Loss,现在用的还是交叉熵,看看效果怎么样。
2.训练过程
2.1 数据准备
2.1.1 MARS数据集的使用
2.1.2 保证各class采样的数量级一致
2.1.3 使用生成器函数来调用大批量数据集
在使用大批量数据集的时候,一次性将所有数据都读取到内存中是不现实的,针对这种情况,tensorflow提供了model.fit_generator
的API,允许我们自己编写“生成器”提供batch
数据,进行训练。
生成器是Python内置的函数类型,与一般函数采用return
一次返回全部内容不同,它的函数体由一个while循环组成,在while循环内有一个yield
,每次调用生成器函数,都会运行一次循环,然后通过yield
返回本次循环生成的值。于是结合之前的数据集处理函数,编写了一个Generator。
具体实现如下:
# 应对大批量数据时,需要使用生成器产生数据 |
2.1.4 数据的预处理
2.1.4.1 归一化
对之前训练的网络做了测试,以下是测试样本:
测试了两种归一化方法,一般来说,最通用的归一化方法应该是:
但是因为我们的数据集实在太大了(450,000张照片),加载这么大量的数据需要巨大的内存(所以我们训练用的是生成器的方式获得batch而不是一开始全部读取到内存里),scikit-learn
现有的函数无法处理这么大的数据,所以我试了两种方法来近似:
该模型训练了80个epoch
样本名 | 余弦相似度(=1-余弦距离) |
---|---|
yellow1 | / |
yellow2 | 0.7345311 |
yellow3 | 0.43169522 |
red | 0.8405349 |
liushishi | 0.55780655 |
huge | 0.6379674 |
该模型训练了15个epoch
样本名 | 余弦距离(=1-余弦距离) |
---|---|
yellow1 | / |
yellow2 | 0.78016734 |
yellow3 | 0.608993 |
red | 0.7567972 |
liushishi | 0.42149162 |
huge | 0.47507387 |
2.1.4.2 数据类型:
- 在上述归一化的过程中,要先cv2.resize成(128,64)后再除255,因为opencv采用uint8的数据类型,会把0.xxx近似成0
- tensorrt不支持float64,而该类型是numpy的默认类型,所以输入前要转成float32
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK