对cuda了解不多,所以使用python创建新的操作层是个不错的选择,当然这个性能不如cuda编写的代码。

在MXNET源码的example/numpy-ops/下有官方提供的使用python编写新操作符的实例。分别跑ndarray_softmax.py、numpy_softmax.py和custom_softmax.py 发现ndarray_softmax.py中训练速度将近其他两种方法的3倍,分析发现ndarray_softmax.py中调用cuda核,而其他两种方法都是numpy在cpu上的运行。

这里总结一下我对ndarray_softmax.py的理解。

分析一下ndarray_softmax.py源码,重写了父类的一些基本方法,其中最重要的是前向和后向操作:

 def forward(self, in_data, out_data):
x = in_data[0]
y = out_data[0]
if self.fwd_kernel is None:
self.fwd_kernel = mx.rtc('softmax', [('x', x)], [('y', y)], """
int i = threadIdx.x + blockIdx.x*blockDim.x;
float max_x = x[i*x_dims[1]];
for (int j = 1; j < x_dims[1]; ++j) {
if (max_x < x[i*x_dims[1]+j]) {
max_x = x[i*x_dims[1]+j];
}
}
float sum = 0.0f;
for (int j = 0; j < x_dims[1]; ++j) {
sum += expf(x[i*x_dims[1]+j]-max_x);
}
for (int j = 0; j < x_dims[1]; ++j) {
y[i*x_dims[1]+j] = expf(x[i*x_dims[1]+j]-max_x)/sum;
}
""")
self.fwd_kernel.push([x], [y], (1, 1, 1), (x.shape[0], 1, 1))

def backward(self, out_grad, in_data, out_data, in_grad):
l = in_data[1]
y = out_data[0]
dx = in_grad[0]
if self.bwd_kernel is None:
self.bwd_kernel = mx.rtc('softmax_grad', [('y', y), ('l', l)], [('dx', dx)], """
int i = blockIdx.x;
int j = threadIdx.x;
int k = static_cast<int>(l[i]);
if (j == k) {
dx[i*dx_dims[1]+j] = y[i*dx_dims[1]+j] - 1.0f;
} else {
dx[i*dx_dims[1]+j] = y[i*dx_dims[1]+j];
}
""")
self.bwd_kernel.push([y,l], [dx], (y.shape[0],1,1), (y.shape[1], 1, 1))

使用mx.rtc(...)定义的就是cuda 编译相关内容了,查看/python/mxnet/rtc.py中Rtc类的定义,其参数部分描述如下:

 """MXRtc object in mxnet.
This class allow you to write CUDA kernels in Python
and call them with NDArray.

Parameters
----------
name : str
Name of the kernel.
inputs : tuple of (str, mxnet.ndarray)
List of input names and ndarray.
outputs : tuple of (str, mxnet.ndarray)
List of output names and ndarray.
kernel : str
The actual kernel code.
Note that this is only the body of the kernel, i.e.
after { and before }. Rtc will decorate the kernel.
For example, if ``name = "mykernel"`` and
inputs = [('x', mx.nd.zeros((10,)))]
outputs = [('y', mx.nd.zeros((10,)))]
kernel = "y[threadIdx.x] = x[threadIdx.x];",
then the compiled kernel will be:
extern "C" __global__ mykernel(float *x, float *y) {
const int x_ndim = 1;
const int x_dims = { 10 };
const int y_ndim = 1;
const int y_dims = { 10 };

y[threadIdx.x] = x[threadIdx.x];
}
"""

以ndarray_softmax.py为例, softmax层输入数据shape=(100,10),输出数据shape=(100,10),那么forward方法里的x_dim=(100,10), 第三个参数即cuda编译的要执行的语句。 在forward方法中看到最后一句push方法,gridDim={'x':1,'y':1,'z':1}, blockDim={'x':100,'y':1,'z':1} (cuda存储参见cudaMemcpy与kernel),于是每一个线程操作一个sample的10个elements,threadIdx.x表示线程在block块中的索引,那么threadIdx.x+blockIdx.x*blockDim.x就是对应线程总的索引,blockDim对应的是block中threads的个数,然后后面softmax前向计算就容易理解了。

再看backward方法,这个kernel将gradDim划分成(100,1,1), blockDim划分成(10,1,1),即每一个element对应着一个线程,然后在每一个线程中计算该element对应的梯度。

example:

实现一个reorganize层,也就是yolo中特征重组层,具体功能YOLO v2 reorg 当然,最清楚的方式是看darknet中源码如何实现。

这个例子只是想继承mx.operator.NDArrayOp实现新的操作层,该操作层中没有权重参数,对于有权重的层要在forward和backward中操作对应的值。

  # -*- coding: utf-8 -*-
import mxnet as mx
import numpy as np
import logging

class NDArrayReorg(mx.operator.NDArrayOp):
def __init__(self, stride=2):
super(NDArrayReorg, self).__init__(True)
self.stride = stride
self.fwd_kernel = None
self.bwd_kernel = None def list_arguments(self):
return ['data'] def list_outputs(self):
return ['output'] def infer_shape(self, in_shape):
data_shape = in_shape[0]
output_shape = [in_shape[0][0], in_shape[0][1]*4
, in_shape[0][2]/self.stride, in_shape[0][3]/self.stride] return [data_shape], [output_shape] def forward(self, in_data, out_data):
x = in_data[0]
y = out_data[0]
if self.fwd_kernel is None:
self.fwd_kernel = mx.rtc('reorg',[('x',x)],[('y',y)],"""
int i = threadIdx.x + blockIdx.x*blockDim.x ;
int yw=y_dims[3];
int yh = y_dims[2];
int N = yw*yh;
int xw=x_dims[3];
int xh = x_dims[2];
int len_block = x_dims[2]*x_dims[3];
for(int j =0; j<xh; j+=2)
for(int k=0; k<xw; k+=2)
{ int t=j/2;
y[i*len_block+t*yw+k/2] = x[i*len_block+j*xw+k];
y[i*len_block+t*yw+k/2+N] = x[i*len_block + j*xw+k+1];
y[i*len_block+t*yw+k/2+2*N] = x[i*len_block +(j+1)*xw+k];
y[i*len_block+t*yw+k/2+3*N] = x[i*len_block +(j+1)*xw+k+1];
}
""")
self.fwd_kernel.push([x],[y],(x.shape[0]*x.shape[1],1,1),(1,1,1)) def backward(self, out_grad, in_data, out_data, in_grad):
y = out_grad[0]
dx = in_grad[0]
if self.bwd_kernel is None:
self.bwd_kernel = mx.rtc('reorg_grad',[('y',y)],[('dx', dx)],"""
int i = threadIdx.x + blockIdx.x * blockDim.x;
int yh = y_dims[2];
int yw = y_dims[3];
int N = yw*yh;
int old_block = dx_dims[2]*dx_dims[3];
for(int k=0;k<4;++k)
for(int j=0; j<yw; ++j)
for(int t=0; t<yh; ++t){
dx[i*old_block+2*j*yw+t*2+k]=y[i*old_block+k*N+j*yw+t];
}
""")
self.bwd_kernel.push([y],[dx],(y.shape[0]*y.shape[1]/4,1,1),(1,1,1)) mnist = mx.test_utils.get_mnist()
batch_size = 100
train_iter = mx.io.NDArrayIter(mnist['train_data'], mnist['train_label'], batch_size, shuffle=True)
val_iter = mx.io.NDArrayIter(mnist['test_data'], mnist['test_label'], batch_size)


data = mx.sym.var('data')
conv1 = mx.sym.Convolution(data=data, kernel=(5,5), num_filter=20)
tanh1 = mx.sym.Activation(data=conv1, act_type="tanh")
# pool1 = mx.sym.Pooling(data=tanh1, pool_type="max", kernel=(2,2), stride=(2,2))

reorg = NDArrayReorg(stride=2)
reg = reorg(data=tanh1, name='reorg')
conv2 = mx.sym.Convolution(data=reg, kernel=(5,5), num_filter=20)
tanh2 = mx.sym.Activation(data=conv2, act_type="tanh") # 80x8x8

conv2 = mx.sym.Convolution(data=tanh2, kernel=(5,5), num_filter=50)
tanh2 = mx.sym.Activation(data=conv2, act_type="tanh")
# pool2 = mx.sym.Pooling(data=tanh2, pool_type="max", kernel=(2,2), stride=(2,2))

flatten = mx.sym.flatten(data=tanh2)
fc1 = mx.sym.FullyConnected(data=flatten,num_hidden=500)
tanh3 = mx.sym.Activation(data=fc1, act_type="tanh")

fc2 = mx.sym.FullyConnected(data=tanh3, num_hidden=10)

mynet = mx.sym.SoftmaxOutput(data=fc2, name='softmax')

print(mynet.infer_shape(data=(100,1,28,28)))
mynet_model = mx.mod.Module(symbol=mynet, context=mx.gpu())

mynet_model.fit(train_iter,
eval_data=val_iter,
optimizer='sgd',
optimizer_params = {'learning_rate':0.1},
eval_metric='acc',
batch_end_callback=mx.callback.Speedometer(100,100),
num_epoch=10) test_iter = mx.io.NDArrayIter(mnist['test_data'], None, batch_size)
prob = mynet_model.predict(test_iter)
test_iter = mx.io.NDArrayIter(mnist['test_data'], mnist['test_label'], batch_size)
# predict accuracy for lenet
acc = mx.metric.Accuracy()
mynet_model.score(test_iter, acc)
print(acc) # 网络是随便构建的,参数也是随便选的,所以出来的值并没有什么参考价值,只是为了验证调用mx.rtc创建cuda的kernel

因此,对于定制的层,可是使用类似的方法定义,该方法显然比使用numpy要快的多。

使用python创建mxnet操作符(网络层)的更多相关文章

  1. Python 创建本地服务器环境生成二维码

    一. 需求 公司要做一个H5手机端适配页面,因技术问题所以H5是外包的,每次前端给我们源码,我们把源码传到服务器让其他人访问看是否存在bug,这个不是很麻烦吗?有人说,可以让前端在他们的服务器上先托管 ...

  2. Python创建Cocos2d-x 2.2方法

    把创建项目做成一个批处理,当创建项目时可以省时省力很多. 操作步骤 1.在 E:\cocos2d-x-2.2.1\tools\project-creator 目录下创建 create_project. ...

  3. Python创建list和按照索引访问list

    Python创建list Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素.比如,列出班里所有同学的名字,就可以用一个list表示:>> ...

  4. [翻译] 使用 Python 创建你自己的 Shell:Part II

    目录 使用 Python 创建你自己的 Shell:Part II 原文链接与说明 步骤 4:内置命令 最后的想法 使用 Python 创建你自己的 Shell:Part II 原文链接与说明 htt ...

  5. [翻译] 使用 Python 创建你自己的 Shell:Part I

    目录 使用 Python 创建你自己的 Shell:Part I 原文链接与说明 步骤 0:项目结构 步骤 1:Shell 循环 步骤 2:命令切分 步骤 3:执行 运行 使用 Python 创建你自 ...

  6. 使用Python创建一个简易的Web Server

    Python 2.x中自带了SimpleHTTPServer模块,到Python3.x中,该模块被合并到了http.server模块中.使用该模块,可以快速创建一个简易的Web服务器. 我们在C:\U ...

  7. python 创建实例--待完善

    今天好好琢磨一下 python 创建实例的先后顺序 一. 就定义一个普通类 Util (默认)继承自 object,覆写 new ,init 方法 class Util(object): def __ ...

  8. 1.面向过程编程 2.面向对象编程 3.类和对象 4.python 创建类和对象 如何使用对象 5.属性的查找顺序 6.初始化函数 7.绑定方法 与非绑定方法

    1.面向过程编程 面向过程:一种编程思想在编写代码时 要时刻想着过程这个两个字过程指的是什么? 解决问题的步骤 流程,即第一步干什么 第二步干什么,其目的是将一个复杂的问题,拆分为若干的小的问题,按照 ...

  9. Python 创建和使用类

    python创建和使用类的方法如下 # class Dog(): # def __init__(self,name,age): # self.name=name # self.age=age # # ...

随机推荐

  1. Linux服务器---网络配置

    禁止ping 有些时候为了保护主机,会禁止其他机器对主机进行ping操作.Ping命令用的是ICMP协议,只要禁用ICMP协议,那么ping方法就无法检测这台主机.关于ICMP协议的配置文件是“/pr ...

  2. CentOS安装mysql并配置远程访问

    最近上班挺无聊,每天就是不停的重启重启重启,然后抓log.于是有事儿没事儿的看卡闲书,搞搞其他事情. 但是,公司笔记本装太多乱其八糟的东西也还是不太好. 于是,想到了我那个当VPN server的VP ...

  3. 学写网页 #04# w3school

    索引: HTML 输入类型 XHTML HTML5 HTML5 样式指南和代码约定 WHO 成立于 1948 年. 对缩写进行标记能够为浏览器.翻译系统以及搜索引擎提供有用的信息. code 元素不保 ...

  4. YAML配置文件

    最近,研究jeeweb这个框架,发现新版本中的配置文件都是用的.yml为后缀的文件,打开一看,和以前的xml和properties语法有很大区别,因此仔细研究一下. 简介: YAML是(YAML Ai ...

  5. 了解微信小程序

    了解微信小程序 版权声明:未经博主授权,内容严禁转载分享! 微信小程序官方网址:https://mp.weixin.qq.com/cgi-bin/wx 某大神知乎专栏地址:七月在夏天 https:// ...

  6. mysql-cluster 7.3.5安装部署

    集群环境 管理节点 10.0.0.19 数据节点 10.0.0.12 10.0.0.17 sql节点 10.0.0.18 10.0.0.22 添加mysql用户 groupadd mysql user ...

  7. Thinkphp5 引入第三方类库的方法

    原文链接:http://www.zhaisui.com/article/42.html

  8. 20145331魏澍琛《网络对抗》Exp2 后门原理与实践

    20145331魏澍琛<网络对抗>Exp2 后门原理与实践 基础问题回答 (1)例举你能想到的一个后门进入到你系统中的可能方式? 上网时候弹出一个广告说你中奖了,或者你可以贷款10万元之类 ...

  9. 20145335郝昊《网络攻防》Exp4 Adobe阅读器漏洞攻击

    20145335郝昊<网络攻防>Exp4 Adobe阅读器漏洞攻击 实验内容 初步掌握平台matesploit的使用 有了初步完成渗透操作的思路 本次攻击对象为:windows xp sp ...

  10. 20145204《网络对抗》逆向及bof基础实践

    20145204<网络对抗>逆向及bof基础实践 实践目的说明 实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何 ...