从零开始,一步一步学习caffe的使用,期间贯穿深度学习和调参的相关知识!
生成net文件
from caffe import layers as L, params as P
def lenet(lmdb, batch_size):
# 以Lenet-5网络搭建为例
n = caffe.NetSpec() # 继承自NetSpec
# 创建数据层。数据层类型选用LMDB文件,向上传递两类数据(ntop=2):图片数据和对应的标签,并归一化到[0,1]
n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb,
transform_param=dict(scale=1./255), ntop=2)
#创建卷积层
n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier'))
#创建池化层
n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier'))
n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
#创建全连接层
n.fc1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
#创建激活函数层
n.relu1 = L.ReLU(n.fc1, in_place=True)
n.score = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
#创建loss函数
n.loss = L.SoftmaxWithLoss(n.score, n.label)
return n.to_proto()
with open('mnist/lenet_auto_train.prototxt', 'w') as f:
f.write(str(lenet('mnist/mnist_train_lmdb', 64)))
with open('mnist/lenet_auto_test.prototxt', 'w') as f:
f.write(str(lenet('mnist/mnist_test_lmdb', 100)))
生成solver文件
from caffe.proto import caffe_pb2
def gen_solver(solver_file, net_file, test_net_file=None):
s = caffe_pb2.SolverParameter() #继承自SolverParameter
s.train_net = net_file
if not test_net_file:
s.test_net.append(net_file)
else:
s.test_net.append(test_net_file)
s.test_interval = 500 # 每训练500次,执行一次测试
s.test_iter.append(100) # 测试迭代次数,假设测试数据有8000个,那batch size=80
s.max_iter = 20000 # 最大迭代次数
s.base_lr = 0.001 # 基础学习率
s.momentum = 0.9 # momentum系数
s.weight_decay = 5e-4 # 正则化权值衰减因子,防止过拟合
s.lr_policy = 'step' # 学习率衰减方法
s.stepsize=1000 # 只对step方法有效, base_lr*gamma^floor(iter/stepsize)
s.gamma = 0.1
s.display = 500 # 输出日志间隔迭代次数
s.snapshot = 10000 # 在指定迭代次数时保存模型
s.snapshot_prefix = 'shapshot'
s.type = 'SGD' # 迭代算法类型, ADADELTA, ADAM, ADAGRAD, RMSPROP, NESTEROV
s.solver_mode = caffe_pb2.SolverParameter.GPU #采用GPU训练 或单独设置
with open(solver_file, 'w') as f:
f.write(str(s))
solver = caffe.SGDSolver(solver_file)
return solver
训练 && 测试
solver.net.forward()
solver.net.forward()
是将batch_size
个图片送到网络中去,只有前向传播(Forward Propagation
,FP),作用于训练集
#训练数据作为输入,进行一次前向传播:
solver.net.forward()
#假如有300个数据,我们的batch_size的大小为100,那么:
solver.net.forward() #数据为1-100;
solver.net.forward() #数据为101-200
solver.net.forward() #数据为201-300
solver.net.forward() #数据为1-100
#另外,我们可以设置forward开始的地方,如下面所示:
solver.net.forward(start ='conv1') #表示从conv1开始,这样的话,data层这不用传用新的数据了。
solver.test_nets[i].forward()
solver.test_nets[i].forward()
也是将batch_size
个图片送到网络中去,只有前向传播(Forward Propagation
,FP),作用于测试集。其中i
表示第几个测试网络,从0
开始。例如,我们就一个测试网络的话,我们就写为:solver.test_nets[0].forward()
# 测试数据作为输入,进行一次前向传播:
solver.test_nets[0].forward() #第一个测试网络
solver.test_nets[1].forward() #第二个测试网络
#假如有300个数据,我们的batch_size的大小为100,那么:
solver.test_nets[0].forward() #数据为1-100;
solver.test_nets[0].forward() #数据为101-200
solver.test_nets[0].forward() #数据为201-300
solver.test_nets[0].forward() #数据为1-100
solver.net.backward()
solver.net.backward()
是将batch_size
个图片送到网络中去,只有反向传播(Back Propagation
,BP),作用于训练集
#训练数据作为输入,进行一次反向传播:
solver.net.backward()
solver.step(n)
solver.step(1)
也是将batch_size
个图片送到网络中去,不过solver.step(1)
不仅有正向传播FP
,而且还有反向传播!这样就可以更新整个网络的权值与偏置,同时得到该batch
的loss
值。
#训练网络进行一次正向与反向传播,并进行更新权值与偏置;
sover.step(1)#表示进行1次训练。
sover.step(n)#表示进行n次训练。
solver.solve()
根据solver
文件中设置进行完整model
训练。
# 根据solver文件中设置进行完整model训练
solver.solve()
训练设置
训练过程中选择使用GPU
或CPU
,若有多块GPU
可以指定使用哪一块。
# 使用GPU
caffe.set_device(gpu_id) # 若不设置,默认为0
caffe.set_mode_gpu()
# 使用CPU
caffe.set_mode_cpu()
数据与参数的保存及调用
Blob
#solver.net.blobs为一个字典的数据类型,里面的key值为各个layer的名字,value为caffe的blob块;
solver.net.blobs
#输出:
rderedDict([('data', <caffe._caffe.Blob at 0x7f7bde968398>),
('label', <caffe._caffe.Blob at 0x7f7bde968488>),
('conv1', <caffe._caffe.Blob at 0x7f7bde968578>),
('pool1', <caffe._caffe.Blob at 0x7f7bde968e60>),
('conv2', <caffe._caffe.Blob at 0x7f7bde9686e0>),
('pool2', <caffe._caffe.Blob at 0x7f7bde968cf8>),
('ip1', <caffe._caffe.Blob at 0x7f7bde968c80>),
('ip2', <caffe._caffe.Blob at 0x7f7bde968c08>),
('loss', <caffe._caffe.Blob at 0x7f7bde968b90>)])
#我们可以访问Blob块里的内容了,通过看Blob块的源码你会发现里面有data, diff,count等内容的。
#我们以conv1层为例子,我们访问conv1的输出的数据,可以通过下面的语句:
solver.net.blobs['conv1'].data
solver.net.blobs['conv1'].diff
#如果想看它们的数据结构,可以通过下面的语句得到:
solver.net.blobs['conv1'].data.shape
solver.net.blobs['conv1'].diff.shape
#另外,还可以通过reshape()transpose()等操作对它们变形,应该是对数组的操作之类的吧。
Params
#solver.net.params为一个字典的数据类型,key值为layer的名字,value为caffe的blob块的容器;
solver.net.params()
#输出为:
solver.net.params
orderedDict([ ('conv1', <caffe._caffe.BlobVec at 0x7f7bffd68578>),
('conv2', <caffe._caffe.BlobVec at 0x7f7bde9ff6e0>),
('ip1', <caffe._caffe.BlobVec at 0x7f7bde968f80>),
('ip2', <caffe._caffe.BlobVec at 0x7f7bde968408>)])
#下面,我们可以访问params块里的内容了。我们以conv1层为例子,具体如下:
#sover.net.params['conv1'][0]里面放是与连接权值相关的数据、梯度;可以通过下面方式访问:
solver.net.params['conv1'][0].data #数据值
solver.net.params['conv1'][0].diff #梯度值
#solver.net.params['conv1'][1]里面放的是与偏置相关的的数据、梯度;可以通过下面方式访问:
solver.net.params['conv1'][1].data #数据值
solver.net.params['conv1'][1].diff #梯度值
#同样,我们可以还可以通过它们进行 shape()、reshape()、transpose()等操作
参考
caffe中python接口的使用(https://www.cnblogs.com/yinheyi/p/6062488.html)
-长按关注-
本文分享自微信公众号 - AI异构(gh_ed66a0ffe20a)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/3647270/blog/4642051