目录
0 环境准备
1 Caffe编译(生成)
2 Caffe再编译
实验原理
实验过程
一、添加ChangeLossLayer损失层
二、添加一个新的卷积层
3 使用
训练
预测
4
由于BVLC Caffe不支持Cuda 8.0,以下实验均在Microsoft Caffe基础上完成。本节将完整列出编译安装深度学习框架Caffe必需的组件。
需要严格按照说明书安装,否则将导致安装失败。
Visual Studio 2013是之后用于编译Caffe工程的IDE。
Caffe编译需要的是python2.7,所以一般做法是安装Anaconda2。注意安装过程中不要多余勾选它没勾的框。安好后手动添加环境变量%CONDA_HOME%(实际python2.7安装根目录)、%CONDA_HOME%\Scripts和%CONDA_HOME%\Library\bin。实际python2.7安装根目录是什么意思呢?就是实际的python2.7解释器——python.exe所在的目录喽。
注意,我编译时,是先用Anaconda3创建了python2.7虚拟环境“python27”,然后添加的第一个环境变量就要相应地变为%CONDA_HOME%\envs\python27,即%CONDA_HOME% := %CONDA_HOME%\envs\python27。创建好虚拟环境后记得要安装numpy。
或者有些人的虚拟环境默认是创建在其他地方的比如C盘的.conda文件夹等等,总之这一步的目的就是为了声明python2.7的位置,和配置PyCharm的环境变量的过程可以说是非常相似的。所以具体的环境变量根据你的python2.7的实际位置来定就好。
步骤略。
步骤略。
Caffe,全称Convolution Architecture For Feature Extraction,是一个清晰且快速的深度学习框架。下面介绍一下如何在Windows 10下配置Caffe框架。
这一步可以省略。这样的话在生成libcaffe这一步中,VS2013会自动帮我们下载NugetPachages,是不是很贴心呢?但是,为了避免某些校园网网络存在不稳定等的复杂情况而导致失败,我们先下载好了,这样就不用等着VS2013缓慢地下载啦。
\windows\CommonSettings.props.example
为%CAFFE_MASTER%\windows\CommonSettings.props
用记事本打开CommonSetting.props,修改如下:将默认的
默认
为 false
,修改为
修改
路径为当前python 2.7的安装路径
%CONDA_HOME%\;
默认
为 false,
修改为
修改
再提醒一下上面的%CONDA_HOME%和%MATLAB_HOME%取决于你的python2.7和Matlab的实际安装路径。
,
调试器模式设为Release X64,
所有项目的属性中“将警告是为错误”设为“否”。结合第2步,这一步就能确保编译过程中使用我们之前下载好的NugetPackages。
如果在生成matcaffe时出现问题..\..\matlab\+caffe\private\caffe_.cpp(16): fatal error C1083: 无法打开包括文件: “gpu/mxGPUArray.h”: No such file or directory,则把%MATLAB_HOME%\toolbox\distcomp\gpu\extern\include下的gpu文件夹复制到%MATLAB_HOME%\extern\include下,然后继续从matcaffe开始生成。
如果在生成pycaffe时出现问题..\..\python\caffe\_caffe.cpp(10): fatal error C1083: 无法打开包括文件: “numpy/arrayobject.h”: No such file or directory,说明未安装numpy,给当前python2.7装上numpy即可。然后继续从pycaffe开始生成。
至此Windows 10下的caffe就安装成功了,是不是非常的简单呢?这里也提供一下我编译好的caffe作为参考(4uvj)。但是,这个版本并没有把matcaffe编译进去,而且如果以后想要做进一步更改,编译Caffe是必须步骤,所以建议完整地按照说明亲自安装。另外,%CAFFE_HOME%\pycaffe中的caffe文件夹是用于import caffe的caffe包。在
import caffe
前,需要将%CAFFE_HOME%\pycaffe加入python工程路径或将%CAFFE_HOME%\pycaffe下的caffe文件夹拷贝到相应python工程所在虚拟环境下的包引用目录中。caffe只支持python2.7的虚拟环境。
针对caffe添加新的层,网上有许多教程。但是,大部分教程关注的是Caffe for Linux,缺乏Caffe for Windows下的完整、快速和通用的实现方法。有一些贴出了Caffe for Windows下的实验流程,但是都集中于新层的hpp、cpp和cu代码的编写方面。虽然新层的hpp、cpp和cu代码的编写也很重要,但有些时候,往往我们得到了已编写好的新层代码,却不知如何将其再编译进Caffe for Windows中。因此,本节将以向Caffe for Windows中添加新损失函数(层)“ChangeLossLayer"为例,详细描述相应的完整实验步骤。
假设我想添加一个叫ChangeLossLayer的损失层,损失函数是L=l0+l1+l2,其中l1、l2和l3都具有Contrastive Loss的形式,且具有同一个阈值参数margin。除此之外,这个损失层还有额外3个参数alpha、beta和gamma用于调整l1、l2和l3的大小。类似于SmoothL1Loss具有的参数sigma可以在prototxt中定义,我们所添加的层具有的4个参数margin、alpha、beta和gamma也应该能够在prototxt中定义。所以这些参数都是需要后面在一个叫caffe.proto的文件里注册的。
怎么看出来一个新损失层需要哪些参数?只需要看它的cpp文件即可。以SmoothL1Loss为例,打开它的cpp文件,可以看到第5行表明需要用到的自定义参数就是sigma。同理,只需要看其他损失层的cpp相同位置,即可知道后面我们需要注册哪些参数。
template
void SmoothL1LossLayer::LayerSetUp(
const vector*>& bottom, const vector*>& top) {
SmoothL1LossParameter loss_param = this->layer_param_.smooth_l1_loss_param();
sigma2_ = loss_param.sigma() * loss_param.sigma(); //
has_weights_ = (bottom.size() >= 3);
if (has_weights_) {
CHECK_EQ(bottom.size(), 4) << "If weights are used, must specify both "
"inside and outside weights";
}
}
要添加ChangeLossLayer光有源代码是不够的,还需要在caffe.proto中定义新的结构化数据。关于caffe.proto的原理可以参考这里。caffe.proto的位置见下表。修改caffe.proto时务必注意格式和大小写。
caffe.proto中都是描述性语言,Caffe for Windows需要的是由caffe.proto编译而来的C代码(caffe.pb.h和caffe.pb.cc)。所以改完caffe.proto后会用到一个proto文件的编译器protoc。这个是需要额外下载的。
最后会用到原生工程自带的ProtoCompile.cmd。ProtoCompile.cmd在再编译Caffe工程的过程中由Caffe工程自动调用。ProtoCompile.cmd的位置见下表。ProtoCompile.cmd负责调用protoc编译caffe.proto并自动替换老caffe.pb.h和caffe.pb.cc。
目前全部的材料总结如下,下面将把新损失层集成到Caffe for Windows中。
1、用记事本打开caffe.proto,做如下修改,具体的名字根据实际添加的层决定,但是修改时务必注意格式和大小写。
optional ChangeLossParameter change_loss_param = 151;
message ChangeLossParameter {
optional float margin = 1 [default = 1.0];
optional float alpha = 2 [default = 3.0];
optional float beta = 3 [default = 5.0];
optional float gamma = 4 [default = 50.0];
}
2、下载proto文件的编译器protoc。打开该网址选择protoc-2.6.1-win32.zip。假设将下载后的压缩包解压到了%PROTOC_HOME%,则将%PROTOC_HOME%添加到系统环境变量。之所以链接中的版本是2.6.1,是因为caffe不支持别的版本。
3、用记事本打开ProtoCompile.cmd,做一点小修改:删掉第二行的set PROTO_DIR=%~2%;将第12行"%PROTO_DIR%protoc"改为"protoc"(删掉了引号中的%PROTO_DIR%)。最后保存。这样做是因为我们已经添加了%PROTOC_HOME%至系统环境变量。
4、将新层的hpp文件拖到%CAFFE_MASTER%\include\caffe\layers下,将新层的cpp和cu文件拖到%CAFFE_MASTER%\src\caffe\layers下。
5、用VS2013打开%CAFFE_MASTER%\windows\Caffe.sln,
展开libcaffe\cu,右键layers->添加现有项->选择刚才拖动后的cu文件;展开libcaffe\src,右键layers->添加现有项->选择刚才拖动后的cpp文件;展开libcaffe\include,右键layers->添加现有项->选择刚才拖动后的hpp文件。
6、检查调试器模式是否设为Release X64,
所有项目的属性中“将警告是为错误”是否设为了“否”,是否启用了已下载好的NugetPackages,最大并行项目生成数是否设为了3(或更小)。matcaffe和pycaffe是否根据第一节已设置正确。
7、先右键libcaffe选“重新生成”,再右键caffe选“重新生成”,最后依次重新生成剩余的14个项目。
这样,Windows 10下的Caffe框架就重新编译成功了,我们就可以像使用其他层一样随意使用新添加的层了,是不是非常的简单方便呢?相比Caffe for Linux,不得不说Caffe for Windows的安装和修改稍微复杂了一些,但是Microsoft Caffe已经尽可能地适配了Windows系统,而且安装成功后,后者的工作效率丝毫不比前者差,甚至能获得更完善的GPU支持。考虑到Windows 10的工作链比Linux完善得多,Caffe for Windows仍具有足够吸引人的性价比。
以回归任务为例,样本是N×N×3的图片,首先按照7/1/2或8/2分成训练、测试预测集,这样就有了3个内容是图片的文件夹。当然可以直接读取纯图片,但是读取慢。第二种方法是分别生成训练和测试的lmdb格式数据集,这种格式是Caffe数据层支持的格式,但是需要预先将图片和标签存入相应的数据库,例如
import os
import lmdb
from PIL import Image
import numpy as np
import sys
import caffe
file_input = open('./test.txt', 'r') # file info
img_list = []
label_list = []
num = 1
for line in file_input:
content = line.strip()
content = content.split(' ')
img_list.append(content[0]) # Name of the sample
label = []
for i in range(37): # Number of my tags of a label
label.append(float(content[i+1]))
label_list.append(label)
del content
print(num)
num = num + 1
file_input.close()
# Saving the samples to the database
in_db = lmdb.open('./img/', map_size=int(1e10))
with in_db.begin(write=True) as in_txn:
for in_idx, in_ in enumerate(img_list):
_in_ = in_.split('_')[0]
in__ = _in_ + '/' + in_ + '.bmp'
im_file = './' + in__
im = Image.open(im_file)
im = np.array(im)
im = im[:, :, ::-1]
im = im.transpose((2, 0, 1))
im_dat = caffe.io.array_to_datum(im)
in_txn.put('{:0>10d}'.format(in_idx), im_dat.SerializeToString())
print('image carried through: {} [{}/{}]'.format(in__, in_idx+1, len(img_list)))
del im_file, im, im_dat
in_db.close()
# Saving the corresponding labels to the database
in_db = lmdb.open('./label/', map_size=int(1e10))
with in_db.begin(write=True) as in_txn:
for in_idx, in_ in enumerate(img_list):
target_label = np.zeros((37, 1, 1))
for i in range(37):
target_label[i, 0, 0] = label_list[in_idx][i]
label_data = caffe.io.array_to_datum(target_label)
in_txn.put('{:0>10d}'.format(in_idx), label_data.SerializeToString())
print('labels carried through: {} [{}/{}]'.format(in_, in_idx+1, len(img_list)))
del target_label, label_data
in_db.close()
print('Preparation accomplished.')
这样就制作好了用于训练的数据集,其中每张图片对应一个37维的标签。网络模型就能直接从数据库里读取图片了。
使用caffe一般需要以下几个文件:
train_val.prototxt:设定训练的具体网络结构,即定义具体的数据层、卷积层、全连接层、损失函数等等。网站Netscope用于可视化train_val.prototxt中的网络。
solver.prototxt:设定超参数,如:
# The train/test net protocol buffer definition
net: "./train_val.prototxt"
test_iter:1313
test_interval: 10000
base_lr: 0.001
lr_policy: "step"
gamma:0.1
stepsize: 55000
display: 100
max_iter: 200000
momentum: 0.9
weight_decay: 0.00001
snapshot: 5000
snapshot_prefix: "./snapshot"
solver_mode: GPU
test_iter和test_interval一定要设,否则在训练时每迭代一次就进行一次测试,导致训练极慢。
deploy.prototxt:设定用模型进行预测时的网络结构,基本与 train_val.prototxt相同。但它应该是不含loss layer的。
设置好solver.prototxt和train_val.prototxt后,使用如下命令启动训练(已添加环境变量)
caffe.exe train --solver=./solver.prototxt --gpu=all
pause
一般来说,一个epoch所含的iteration=样本数量÷batch_size。
直接执行测试脚本(已添加环境变量),但不便于获取结果
caffe test --weights=./snapshot_iter_200000.caffemodel --model=./deploy.prototxt --iterations=99402 --gpu=all
pause
也可以利用caffe的python接口进行预测,能够根据需要看到中间结果或保存结果
# -*- coding: utf-8 -*-
import caffe
net = caffe.Net('./deploy.prototxt', 1, weights='./snapshot_iter_200000.caffemodel')
with open('./test.txt') as image_list:
with open('./pd.txt', 'w') as r:
while 1:
list_name = image_list.readline()
if not list_name:
break
img_name = list_name.split(' ')[0]
img_y = list_name.split(' ')[1] # 真实值
out = net.forward()
score = (out['fc3'])[0, 0] # 获取预测值;fc3是我的最后一个全连接层名
r.write(img_name + " " + img_y + " " + repr(score) + '\n')