caffe详解之Python接口

▼魔方 西西 提交于 2021-02-03 00:24:55

从零开始,一步一步学习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,而且还有反向传播!这样就可以更新整个网络的权值与偏置,同时得到该batchloss

#训练网络进行一次正向与反向传播,并进行更新权值与偏置;
sover.step(1#表示进行1次训练。

sover.step(n)#表示进行n次训练。

solver.solve()

根据solver文件中设置进行完整model训练。

# 根据solver文件中设置进行完整model训练
solver.solve()

训练设置

训练过程中选择使用GPUCPU,若有多块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源创计划”,欢迎正在阅读的你也加入,一起分享。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!