利用python对caffe网络层自定义及绘制曲线

还没有仔细分析各个接口的方法,只给出了示例

思考:python书写网络层,如何用C++来跑网络呢?

自定义网络层

卷积层是对数据进行卷积操作,如果我不想进行卷积运算,而是简单的像素加减,可以通过python自定义网络层来实现。

 python用来写网络C++用来跑网络

准备工作

  • step1    修改caffe/Makefile.config文件,且取消注销代码:with_python_layer :=1
取消注释
  • step2    重新编译caffe
cd caffe
make clean  
make all -j8
make pycaffe
  • step3     修改网络层的参数type设置成python,python_param{},下述代码给予对比
# 使用python
layer {
  type: 'Python'
  name: 'loss'
  top: 'loss'
  bottom: 'ipx'
  bottom: 'ipy'
  python_param {
    # the module name -- usually the filename -- that needs to be in $PYTHONPATH
    module: 'pyloss'
    # the layer name -- the class name in the module
    # 这个参数的名字需要和接下来自定义层的类名相同
    layer: 'MyLayer'
  }
  # set loss weight so Caffe knows this is a loss layer.
  # since PythonLayer inherits directly from Layer, this isn't automatically
  # known to Caffe
  loss_weight: 1
}

# 不用python
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

使用python自定义层与调用

这里在ubuntu18中使用pycharm

自定义层

import sys
caffe_root = "/home/tyd/caffe/"
sys.path.insert(0, caffe_root+'python')
import caffe
import numpy as np
import yaml  #这个库就是把我们的参数做一个传递   这个库linux下一直在
import cv2

# MyLayer和上边的代码中的python_param{layer:MyLayer}是对应的
class MyLayer(caffe.Layer):      #千万注意,这里看一下下图, 这里是个继承关系

#这里的四个函数不是随便定义的,而是caffe给出来规则
    def setup(self, bottom, top):        #做一下准备工作
        self.num = yaml.load(self.param_str)['num']
        print("Parament num: ", self.num)
    def reshape(self, bottom, top):    #对你的数据做一些维度的变化
        pass  #这里不执行任何操作, pass就是字面意思,跳过

# 因为我们自己写的层, 所以我们要把前向传播和反向传播定义好
# 这里举个例子, 因为是自定义所以自由度比较大, 我们这里先实现一个特征图每个元素都加21, 如下图的param_str:"num:21"
# 在前向传播的时候让特征图上的所有值都加上21
# 这里的top就是prototxt文件中的输出意思, bottom就是输入的意思
# 这里的top[0]就是第0个输出
    def forward(self, bottom, top):
        top[0].reshape(*bottom[0].shape)
        print bottom[0].data.shape
        print bottom[0].data
        top[0].data[...] = bottom[0].data + self.num #输出的数据= 输入数据+自己定义的21
        print top[0].data[...]  # ...就是全部的意思
    def backward(self, top, propagate_down, bottom):
        pass #这里选择不做反向传播

生成.prototxt网络配置文件

如果你的网络层很多,几十层,难道你要每次都手写一个layer{}吗? 或者不断的复制粘贴吗

太麻烦了, 用代码把配置文件生成出来

import sys
caffe_root='/home/tyd/caffe/'
sys.path.insert(0,caffe_root+'python')
from caffe import layers as L
from caffe import params as P
import caffe

def lenet(lmdb,batch_size):
    n = caffe.NetSpec()
    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_filter=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_filter=dict(type='xavier'))
    # pooling层需要说明是均值还是max
    n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
    n.ip1 = L.InnerProduct(n.pool2, num_output=500, weight_filter=dict(type='xavier'))
    n.relu1 = L.ReLU(n.ip1, in_place=True)
    n.ip2 = L.InnerProduct(n.relu1, num_output=10, weight_filter=dict(type='xavier'))
    n.loss = L.SoftmaxWithLoss(n.ip2, n.label)
    return n.to_proto()
    # .to_proto()应该是方法函数
    

with open('lenet_auto_train.prototxt', 'w') as f:
    f.write(str(lenet('examples/mnist/mnist_train_lmdb', 64)))
with open('lenet_auto_test.prototxt', 'w') as f:
    f.write(str(lenet('examples/mnist/mnist_test_lmdb', 100)))

使用自定义层

# 接下来告诉你如何使用我们定义的这个python层
# 加载自定义的网络层
net = caffe.Net("conv.prototxt", caffe.TEST)

# 图片预处理
# 加载一张图片并转换成numpy格式
im = np.array(cv2.imread("timg.jpeg"))
# 图片是[width, height, channels]3个维度
# conv.prototxt配置文件是[batch, channels, width,height]4个维度
# 通过np.newaxis自动在axis=0处增加一个维度
im_input = im(np.newaxis, :, :]
# 这里的im_input.shape=(1, width, height, channels)
print im_input.shape
# 这里还需要修改每个维度的位置,通过transpose()直接对位调整就行了
im_input2 = im_input.transpose((0,3,1,2))
print im_input2.shape


# 接下来需要修改prototxt文件最前几行中的dim值,改成我们修改好的维度值
net.blobs['data'].reshape(*im_input2.shape)

# 把图片的数据放进去
net.blobs['data'].data[...] = im_input2

#执行forward方法
net.forward()

绘制loss函数曲线

把loss函数曲线画出来,或者accuracy rate曲线也画出来

import numpy as np
import matplotlib.pyplot as plt
import sys, os
caffe_root = '/home/tyd/caffe/' 
sys.path.insert(0, caffe_root+'python')
import caffe

# caffe.set_device(0)
caffe.set_mode_cpu()
solver = caffe.SGDSolver('/home/tyd/caffe/examples/mnist/lenet_solver.prototxt')

niter = 1000
test_interval =200
train_loss = np.zeros(niter)
test_acc = np.zeros(int(np.ceil(niter / test_interval)))

for it in range(niter):
    solver.step(1)
    
    train_loss[it] = solver.net.blobs['loss'].data

 

你可能感兴趣的