Deep learning系列(十三)Transfer learning 和 caffe Fine-tuning

1. Transfer Learning

在实践中因为数据库大小的限制,我们通常不会从头开始(参数随机初始化)训练卷积神经网络。相反,通常会在一个大的数据库上(比如ImageNet,包含1000类总数为120万的图像分类数据库)进行CNN的训练,得到训练好的网络(下面简称ConvNet),然后ConvNet以下面两种方式用到我们的项目中:

  • 将ConvNet作为特征提取器。去掉ConvNet最后一层全连接层,将ConvNet剩下的部分当作特征提取器用到我们的分类任务中。对于AlexNet来说,这将生成一个4096维的特征向量,用这得到的特征向量,训练一个线性分类器(线性SVM或者softmax分类器),用在我们的分类任务中。
  • 用新数据库的数据微调ConvNet。对于在ImageNet预训练的网络ConvNet,我们可以在新的数据库上使用反向传播算法微调它的权重参数。在微调过程中,既可以调整所有层的参数,也可以将前几层的参数固定,微调后面几层。这样做的原因在于预训练的网络前几层通常是通用的特征(比如边缘提取器),而网络的后几层则是与数据库和分类任务相关的,因而可以在给定新的数据库和分类任务时仅调整后面几层。

2. 怎样fine-tune

在fine-tune时,究竟该选择哪种方式的Transfer Learning?需要考虑的因素有许多,其中最重要的两条是新数据库的规模和它与预训练数据库的相似程度,根据这两条因素的不同配置,存在四种场景:

  • 新数据库小,和预训练数据库相似。因为数据库比较小,fine-tune的话可能会产生过拟合,比较好的做法是用预训练的网络作为特征提取器,然后训练线性分类器用在新的任务上。
  • 新数据库比较大,和预训练数据库相似。这种情况下,不用担心过拟合,可以放心地微调整个网络。
  • 新数据库小,和预训练数据库不相似。这时,既不能微调,用预训练网络去掉最后一层作为特征提取器也不合适,可行的方案是用预训练网络的前面几层的激活值作为特征,然后训练线性分类器。
  • 数据库大,和预训练数据库不相似。这时可以从头开始训练,也可以在预训练的基础上进行微调。

3. 用caffe进行fine-tune

和从头开始训练CNN一样,用caffe进行微调也可以分解为四步:

  • 将数据转化为caffe能处理的格式;
  • 定义net;
  • 定义solver;
  • 在预训练的权重基础上进行训练。

3.1. 数据格式转化

最常用的格式是LMDB,假设我们下载的数据库是图片的格式,训练库放在txt文件中,每行包含图片的地址和类别标签[path/to/image.jpeg] [label]:

/home/dumengnan/caffe-master/data/flickr_style/images/12123529133_c1fb58f6dc.jpg 16
/home/dumengnan/caffe-master/data/flickr_style/images/11603781264_0a34b7cc0a.jpg 12
...

将其转化为LMDB使用的函数是convert_imageset,调用.sh文件内容如下:

EXAMPLE=data/flickr_style
DATA=data/flickr_style
TOOLS=build/tools
TRAIN_DATA_ROOT=/

# Set RESIZE=true to resize the images to 256x256. Leave as false if images have
# already been resized using another tool.
RESIZE=true
if $RESIZE; then
  RESIZE_HEIGHT=256
  RESIZE_WIDTH=256
else
  RESIZE_HEIGHT=0
  RESIZE_WIDTH=0
fi

echo "Creating train lmdb..."
# GLOG_logtostderr含义是在/tmp目录下输出日志
# shuffle含义是对数据库图片进行排序
GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    $TRAIN_DATA_ROOT \
    $DATA/train.txt \
    $EXAMPLE/flickr_train_lmdb

echo "Creating test lmdb..."

GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    $TRAIN_DATA_ROOT \
    $DATA/test.txt \
    $EXAMPLE/flickr_test_lmdb

echo "Done."

转化结束后,在flickr_train_lmdb和flickr_test_lmdb文件夹中保存了lmdb文件。

3.2. 定义net

微调参数时,保存net的.prototxt文件可以直接复制预训练时使用的.prototxt文件,然后进行以下修改:

  • 修改data层;
  • 修改output层,包括层的名字和输出类别数;
  • 减小batch的大小(和GPU的大小成比例);
  • 降低前几层的学习率(可以将学习率置为0,所谓冻结这一层的参数),升高最后一层的学习率。

修改层的名字的原因是使用不同的名字,预训练网络中该层的参数会重新初始化,如下图所示:


Deep learning系列(十三)Transfer learning 和 caffe Fine-tuning_第1张图片

3.3. 定义solver

保存solver的.prototxt文件同样不需要重新去写,拷贝预训练时使用的.prototxt文件,进行以下修改:

  • 将net从预训练使用的net换为现在使用的net;
  • 降低学习率(除以100),这样做的原因是相比随机初始化的权重参数,预训练的参数已相对较好了,不需要更新太快;
  • 可以修改最大迭代次数和snapshot。

3.4. 训练

相比使用参数随机初始化的训练,在预训练的基础上进行训练的话调用命令时增加一个-weight参数,并给出预训练参数的存储位置,如下所示:

./build/tools/caffe train -solver models/finetune_flickr_style/solver.prototxt -weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel

4. fine-tune的结果

新的数据库是flickr数据库,分类任务是进行20类的分类(预训练的分类任务是1000类分类),使用图片的数量为700张左右,机器的显卡是GTX970,经过6个小时的训练(迭代次数为80000次),分类准确率收敛到34%左右。

参考资料:
1. http://caffe.berkeleyvision.org/gathered/examples/finetune_flickr_style.html
2. http://cs231n.github.io/transfer-learning/

你可能感兴趣的:(deep-learning)