Author: Zongwei Zhou | 周纵苇

Weibo: @MrGiovanni

Email: zongweiz@asu.edu

Acknowledgement: Md Rahman Siddiquee (mrahmans@asu.edu)

Caffe的参考文档非常少,自己改代码需要查阅网上好多好多对的错的讨论. 这篇文章主要讲怎么自己编写python layer,从而逼格很高地在caffe中实现自己的想法. 所谓的python layer,其实就是一个自己编写的层,用python来实现. 因为近来深度学习方向要发顶会和顶刊,光是用用Caffe,Tensorflow在数据集里头跑个网络已经基本不可能啦,需要具备修改底层代码的能力.

1. 准备工作

1.1 系统配置

Ubuntu 16.04 LTS

Python 2.7.14

1.2 编译Caffe

按照一般的caffe编译流程(可参考官网,也可参考Install caffe in Ubuntu)就好,唯一的区别就是在Makefile.config中,把这一行修改一下:

# WITH_PYTHON_LAYER := 1

改成

WITH_PYTHON_LAYER := 1

说明我们是要使用python_layer这个功能的。然后编译成功后,在Terminator中输入:

$ caffe

$ python

>>> import caffe

像这样,没有给你报错,说明caffe和python_layer都编译成功啦.

1.3 添加Python路径

写自己的python layer势必需要.py文件,为了让caffe运行的时候可以找到你的py文件,接下来需要把py文件的路径加到python的系统路径中,步骤是:

打开Terminator

输入vi ~/.bashrc

输入i,进入编辑模式

在打开的文件的末尾添加

export PYTHONPATH=/path/to/my_python_layer:$PYTHONPATH

键入esc,:wq,回车,即可保存退出

如果这部分没有看明白,需要上网补一下如何在Linux环境中用vim语句修改文档的知识. 实质上就是修改一个在~/路径下的叫.bashrc的文档.

2. 修改代码

首先我们定义一个要实现的目标:训练过程中,在Softmax层和Loss层之间,加入一个Python Layer,使得这个Layer的输入等于输出. 换句话说,这个Layer没有起到一点作用,正向传播的时候y=x,反向传播的时候导数y'=1. 因此训练的结果应该和没加很相似.

2.1 train_val.prototxt

这个文档是Caffe训练的时候,定义数据和网络结构用的,所以如果用添加新的层,需要在这里定义. 第一步是在网络结构的定义中找到添加Python Layer的位置,根据问题的定义,Python Layer应该在softmax和loss层之间,不过网上的prototxt大多会把这两个层合并在一起定义,成为了

layer {

name: "loss"

type: "SoftmaxWithLoss"

bottom: "fc8_2"

bottom: "label"

top: "loss"

}

我们需要把这个层拆开,变成softmax层和loss层,根据Caffe提供的官方文档,我们知道SoftmaxWithLoss是softmax层和MultinomialLogisticLoss的合并.

The softmax loss layer computes the multinomial logistic loss of the softmax of its inputs. [7]

那拆开后的代码就是

layer {

name: "output_2"

type: "Softmax"

bottom: "fc8_2"

top: "output_2"

}

layer {

name: "loss"

type: "MultinomialLogisticLoss"

bottom: "output_2"

bottom: "label"

top: "loss"

}

拆完了以后就只需要把你定义的Python Layer加到它们中间就好了,注意这个层的输出和输出,输入是bottom,输出是top,这两个值需要和上一层的softmax输出和下一层的loss输入对接好,就像这样(请仔细看注释和代码):

layer { # softmax层

name: "output_2"

type: "Softmax"

bottom: "fc8_2" # 是上一层Fully Connected Layer的输出

top: "output_2" # 是Softmax的输出,Python Layer的输入

}

layer {

type: "Python"

name: "output"

bottom: "output_2" # 要和Softmax输出保持一致

top: "output" # Python Layer的输出

python_param {

module: "my_layer" # 调用的Python代码的文件名

# 也就是1.3中添加的Python路径中有一个python文件叫my_layer.py

# Caffe通过这个文件名和Python系统路径,找到你写的python代码文件

layer: "MyLayer" # my_layer.py中定义的一个类,在下文中会讲到

# MyLayer是类的名字

param_str: '{ "x1": 1, "x2": 2 }' # 额外传递给my_layer.py的值

# 如果没有要传递的值,可以不定义. 相当于给python的全局变量

# 当Python Layer比较复杂的时候会需要用到.

}

}

layer {

name: "loss"

type: "MultinomialLogisticLoss"

bottom: "output" # 要和Python Layer输出保持一致

bottom: "label" # loss层的另一个输入

# 因为要计算output和label间的距离

top: "loss" # loss层的输出,即loss值

}

加完以后的参数传递如图

2.2 my_layer.py

重头戏其实就是这一部分,以上说的都是相对固定的修改,不存在什么算法层面的改动,但是python里面不一样,可以实现很多调整和试验性的试验. 最最基本的就是加入一个上面定义的那个"可有可无"的Python Layer.

在Python的文件中,需要定义类,类的里面包括几个部分:

setup( ): 用于检查输入的参数是否存在异常,初始化的功能.

reshape( ): 也是初始化,设定一下参数的size

forward( ): 前向传播

backward( ): 反向传播

结构如下:

import caffe

class MyLayer(caffe.Layer):

def setup(self, bottom, top):

pass

def reshape(self, bottom, top):

pass

def forward(self, bottom, top):

pass

def backward(self, top, propagate_down, bottom):

pass

根据需要慢慢地填充这几个函数,关于这方面的知识,我很推荐阅读"Guide to Convolutional Neural Networks: A Practical Application to Traffic-Sign Detection and Classification." 中的这个章节 [6].

setup()的定义:

def setup(self, bottom, top):

# 功能1: 检查输入输出是否有异常

if len(bottom) != 1:

raise Exception("异常:输入应该就一个值!")

if len(top) != 1:

raise Exception("异常:输出应该就一个值!")

# 功能2: 初始化一些变量,后续可以使用

self.delta = np.zeros_like(bottom[0].data, dtype=np.float32)

# 功能3: 接受train_val.prototxt中设置的变量值

params = eval(self.param_str)

self.x1 = int(params["x1"])

self.x2 = int(params["x2"])

reshape()的定义:

def reshape(self, bottom, top):

# 功能1: 修改变量的size

top[0].reshape(*bottom[0].data.shape)

# 看了很多材料,我感觉这个函数就是比较鸡肋的那种.

# 这个函数就像格式一样,反正写上就好了...

# 不知道还有其他什么功能了,欢迎补充!

forward()的定义:

这个函数可以变的花样就多了,如果是要定义不同的loss function,可以参考[1],稍微高级一点的可以参考[2],这里就实现一个y=x的简单功能.

def forward(self, bottom, top):

# 目标:y = x

# bottom相当于输入x

# top相当于输出y

top[0].data[...] = bottom[0].data[:]

# 哈哈哈哈,是不是感觉被骗了,一行代码就完事儿了:-)

了解bottom中数据的存储结构是比较重要的,因为参考文档不多,我只能通过print out everything来了解bottom里面究竟存着些什么. 回想在2.1的prototxt中,我们有定义输入Python Layer的都有什么(bottom). bottom可以有多个定义,如果像例子中的只有一个bottom: "output_2",那么bottom[0].data中就存着output_2的值,当二分类问题时也就是两列,一列是Softmax后属于label 0的概率,一列是Softmax后属于label 1的概率. 当bottom定义了多个输入的时候,即

layer {

type: "Python"

name: "output"

bottom: "output_2"

bottom: "label"

top: "output"

python_param {

...

}

}

那么按照顺序,bottom[0].data中依旧存着output_2,bottom[1].data中存着label值,以此类推,可以定义到bottom[n],想用的时候调用bottom[n].data就可以了. top[n].data和bottom的情况类似,也是取决于prototxt中的定义.

想象一下,既然你可以掌控了output_2和label和其他你需要的任何值(通过bottom或者param_str定义),是不是可以在这个forward()函数里面大展身手了?

是的.

但是同时,也要负责计算这个前馈所带来的梯度,可以自己定义变量存起来,网上修改loss函数的例子就是拿self.diff来存梯度的,不过在这个例子中,因为梯度是1,所以我没有管它.

backward()的定义:

def backward(self, top, propagate_down, bottom):

# 由于是反向传播,top和bottom的意义就和前向传播反一反

# top:从loss层传回来的值

# bottom:反向层的输出

for i in range(len(propagate_down)):

if not propagate_down[i]:

continue

bottom[i].diff[...] = top[i].diff[:]

# 其实还要乘以这个层的导数,但是由于y=x的导数是1.

# 所以无所谓了,直接把loss值一动不动的传递下来就好.

对于top和bottom在forward()和backward()函数中不同的意义,不要懵...

top[i].diff是从loss层返回来的梯度,以二分类为例,它的结构是两列,一列是label 0的梯度,一列是label 1的梯度. 因此在backward()正常情况是需要把top[i].diff乘以self.diff的,也就是在forward()中算好的Python Layer自身的梯度. 然后赋值给bottom[i].diff,反向传播到上一层.

关于propagate_down这个东西,我认为是定义是否在这个层做反向传播(权值更新)的,也就是在迁移学习中,如果要固定不更新某一层的参数,就是用propagate_down来控制的. 不用管它,反正用默认的代码就好了.

总的来说,要实现y=x这么一个层,需要写的python代码就是:

import caffe

class MyLayer(caffe.Layer):

def setup(self, bottom, top):

pass

def reshape(self, bottom, top):

top[0].reshape(*bottom[0].data.shape)

def forward(self, bottom, top):

top[0].data[...] = bottom[0].data[:]

def backward(self, self, top, propagate_down, bottom):

for i in range(len(propagate_down)):

if not propagate_down[i]:

continue

bottom[i].diff[...] = top[i].diff[:]

3. 结语

我认为要在Caffe中写好一个Python Layer,最重要的是抓住两点

1)处理好prototxt到python文件的参数传递

2)不能忘了在forward()中计算反向传播梯度

接下来就是一些代码理解和学术创新的事情了,懂得如何写Python Layer,在运动Caffe的过程中就多开了一扇窗,从此不再只是调整solver.prototxt,还有在train_val.prototxt中组合卷积层/池化层/全连接/Residual Unit/Dense Unit这些低级的修改.

更多的细节可以参考最前面的几个参考链接还有自己的理解实践. 在实践过程中,超级建议print所有你不了解的数据结构,例如forward()中的bottom,top; backward()中的bottom,top,即便Caffe用GPU加速,它也会给你打印出来你想要看的数据,一步一步的摸索数据的传递和存储,这也是我花最多时间去弄明白的地方.

祝好!

多线程 python layer_在Caffe中加Python Layer的方法相关推荐

  1. python之禅 中文_《Python之禅》中对于Python编程过程中的一些建议

    <Python之禅>中对于Python编程过程中的一些建议 来源:中文源码网    浏览: 次    日期:2018年9月2日 [下载文档:  <Python之禅>中对于Pyt ...

  2. spring配置中加载properties文件方法

    首先,遇到一个问题,spring配置中加载properties文件配置如下: <context:property-placeholder ignore-unresolvable="tr ...

  3. caffe中的softmax layer

    在caffe中的lenet实现最后一层是softmax layer,输出分类的结果,下面就简单介绍一下softmax回归. 1,首先,在caffe中,softmax layer输出的是原始的输入在每一 ...

  4. 在caffe中调用python层的问题和解决方案

    之前很早以前在caffe中用过python写的loss layer,很久不用后在新的服务器上使用就各种"No module named WeightedEuclideanLossLayer2 ...

  5. 如何在python里加音乐_python中加背景音乐如何操作

    在python中加背景音乐的方法: 1.导入pygame资源包: 2.修改音乐的file路径: 3.使用init()方法进行初始化: 4.使用load()方法添加音乐文件: 5.使用play()方法播 ...

  6. eclipse配置python开发环境_Eclipse中配置python开发环境详解

    Eclipse中配置python开发环境详解 1.下载python安装包.python-2.6.6.msi.并安装. 默认python会安装在C:\Python26下,查看环境变量,如果没有在path ...

  7. cmd查看python版本-在cmd中查看python的安装路径方法

    我相信一定有很多的人跟我一样,经常忘记Python安装的路径,每当用到的时候,最笨的办法就是在全局电脑里,直接查找Python,这样是肯定能查到的,但是如果你的电脑文件超级多,这将是一个工厂量很大的事 ...

  8. 12个python超强学习网站!加python书籍推荐!( 入门python自学推荐!建议收藏!)

    python学习网站 一.python学习网站 1 CSDN 2 Python123 3 python中文学习大本营 4 python开发者社区 5 github 6 python学习网 7 pyth ...

  9. python怎么更新数据库_在Python的Django框架中更新数据库数据的方法

    先使用一些关键参数创建对象实例,如下: >>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... ...

最新文章

  1. Java进阶之自动拆箱与自动装箱
  2. pandas使用dt.year(month/day/hour/minute/second)函数抽取dataframe日期数据列对应的年月日时分秒信息
  3. java 做登录跳转404_springboot 访问路径错误跳转到404(实现方法一)
  4. ecshop affiche.php,affiche.php
  5. Linux压缩解压缩文章总结
  6. (一)Java工程化--Maven基础
  7. 链表之删除链表a/b处的节点
  8. sublime:查看二进制文件
  9. 10.11.5 brew mysql_mac os10.11下安装MySQLdb
  10. mysql delete node_Node.js MySQL DELETE
  11. 《深入理解Linux内核》条目式笔记 _2
  12. C++自学17:goto
  13. 我,对安全研究专家下手的黑客!
  14. 机器视觉——IC芯片字符检测打光实例
  15. IntelliJ IDEA中激活JRebel插件
  16. Win11没有nvidia控制面板怎么解决
  17. JS—C11库仑计说明书,js-c11库仑计说明书
  18. 入职东北国企做程序员一个月,感受如何?
  19. 关于物联网,互联网我们用到的通信
  20. 服务器系统可以重装系统,服务器上可以重装操作系统吗

热门文章

  1. MySQL 5.6.11 GA 发布
  2. android开发超级群(500人)
  3. 基于OpenCV的findContours查找图像连通域,并进行排序
  4. 几种在Linux下查询外网IP的办法
  5. OpenCV图像处理使用笔记(二)——图像矩阵的掩膜操作
  6. linux efi不要boot目录,LINUX下EFIBOOTMGR的使用,删除UEFI主板多余启动项和添加启动项-Go语言中文社区...
  7. php 回调通知 连连支付_php怎么写连连支付退款
  8. shell在二级python_在Shell脚本中检查Python版本的方法
  9. laravel5.6 php,Laravel5.6中的队列简单使用
  10. 关于Element中的clientWidth,scrollWidth,offsetWidth等属性详解