mshadow入门指南
mshadow是一个基于表达式模板实现的张量库,在MXNet框架中被广泛使用。这篇文章简单介绍了mshadow的基本用法和特性,文章主要翻译自mshadow/guide/README。
张量数据结构
mshadow中的主要数据结构就是张量(Tensor),下面是一个简化版本的声明定义(来自mshadow/tensor.h文件):
typedef unsigned index_t;
template<int dimension>
struct Shape {
index_t shape_[dimension];
};
template<typename Device, int dimension, typename DType = float>
struct Tensor {
DType *dptr_;
Shape<dimension> shape_;
Stream<Device> stream_;
index_t stride_;
};
// this is how shape object declaration look like
Shape<2> shape2;
// this is how tensor object declaration look like
Tensor<cpu, 2> ts2;
Tensor<gpu, 3, float> ts3;
在上述代码中, Tensor<cpu,2>
是内存上的一个二维张量,而Tensor<gpu,3>
是存储在GPU显存上的一个三维张量。Shape<k>
给出了一个k维张量的维度信息。通过使用模板编程技术,用户可以申请存储在特定设备上的不同尺寸的张量。下面是一个二维张量的定义:
struct Shape<2> {
index_t shape_[2];
};
struct Tensor<cpu, 2, float> {
float *dptr_;
Shape<2> shape_;
index_t stride_;
};
Tensor<cpu, 2>
包含一个名为dptr_
指针,指向张量所在的内存空间地址。Shape<2>
是一个保存张量形状信息的结构体。stride_
给出了在最小维度上分配的内存单元的数量,它与内存对齐有关。在进行内存分配时,stride_
的值会被自动设置。
下面的代码可以帮助我们更好地理解mahsdow中的张量。
float data[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
Tensor<cpu, 2> ts;
ts.dptr_ = data;
ts.shape_ = mshadow::Shape2(3, 2);
ts.stride_ = 3;
// now: ts[0][0] == 0, ts[0][1] == 1 , ts[1][0] == 3, ts[1][1] == 4
for (index_t i = 0; i < ts.size(0); ++i) {
for (index_t j = 0; j < ts.size(1); ++j) {
printf("ts[%u][%u]=%f\n", i, j, ts[i][j]);
}
}
代码中的ts
是一个\(3 \times 2\)的矩阵,其中data[2]
、data[5]
以及data[8]
作为填充单元被忽略掉。如果想访问连续内存,设置stride_=shape_[1]
即可。
内存分配
mshadow的一个重要设计就是将张量视作一个“白盒”。只要我们把dptr_
和shape_
以及stride_
对应起来,它就可以工作:
- 对于
Tensor<cpu, k>
,dptr_
指向由new float[]
申请的内存空间,或者是某些预分配的内存空间 - 对于
Tensor<gpu, k>
,dptr_
必须指向由cudaMallocPitch
申请的GPU显存
mshadow提供了显式内存分配的函数,如下所示:
// create a 5 x 3 tensor on the device, and allocate space
Tensor<gpu, 2> ts2(Shape2(5, 3));
AllocSpace(&ts2);
// allocate 5 x 3 x 2 tensor on the host, initialized by 0
Tensor<cpu, 3> ts3 = NewTensor<cpu>(Shape3(5,3,2), 0.0f);
// free space
FreeSpace(&ts2); FreeSpace(&ts3);
mshadow中的所有的内存分配操作都是显式进行的,不会出现任何隐式的内存分配或内存销毁等操作。这就意味着,Tensor<cpu, k>
更像一个指针(或引用),而不是一个对象。如果我们把一个张量赋值给另一个,那么他们会指向相同的内存空间。另外,这种特性对用户来说是十分友好的,只需要把一个指针交给mshadow,即可零成本地受益于mshadow的高性能计算能力。
mshadow还提供了一个名为TensorContainer
的STL风格容器,它的行为和张量类似,但是会在析构时自动释放内存。
逐元素的操作
mshadow中所有的运算符(+,-,*,/等)都是元素级操作。考虑如下SGD更新代码:
void UpdateSGD(Tensor<cpu, 2> weight, Tensor<cpu, 2> grad, float eta, float lambda) {
weight -= eta * (grad + lambda * weight);
}
在编译期,上述代码会被转化成下面的代码:
void UpdateSGD(Tensor<cpu,2> weight, Tensor<cpu,2> grad, float eta, float lambda) {
for (index_t y = 0; y < weight.size(0); ++y) {
for (index_t x = 0; x < weight.size(1); ++x) {
weight[y][x] -= eta * (grad[y][x] + lambda * weight[y][x]);
}
}
}
可以看到,代码转换过程中没有发生任何内存分配操作。对于Tensor<gpu, k>
,对应的函数会被转化成具有相同含义的CUDA核函数。使用表达式模板,上述的转换过程会发生在编译期。
CPU与GPU上的通用代码
由于mshadow对Tensor <cpu,k>
和Tensor <gpu,k>
提供了相同的接口,因此我们可以轻松地编写运行在CPU和GPU上地代码。比如,下面的代码可以同时被CPU和GPU上的张量所使用。
template<typename xpu>
void UpdateSGD(Tensor<xpu, 2> weight, const Tensor<xpu, 2> &grad,
float eta, float lambda) {
weight -= eta * (grad + lambda * weight);
}
矩阵乘法
mshadow提供了一个矩阵点积的实现,内部封装了MKL和cuBLAS等库。
template<typename xpu>
void Backprop(Tensor<xpu, 2> gradin,
const Tensor<xpu, 2> &gradout,
const Tensor<xpu, 2> &netweight) {
gradin = dot(gradout, netweight.T());
}
用户自定义操作
假设用户要在mshadow中自定义一个逐元素的sigmoid函数,那么我们可以通过下面的代码将sigmoid操作加入到mshadow中。
struct sigmoid {
MSHADOW_XINLINE static float Map(float a) {
return 1.0f / (1.0f + expf(-a));
}
};
template<typename xpu>
void ExampleSigmoid(Tensor<xpu, 2> out, const Tensor<xpu, 2> &in) {
out = F<sigmoid>(in * 2.0f) + 1.0f;
}
转换后的代码就如下所示(CPU版本):
template<typename xpu>
void ExampleSigmoid(Tensor<xpu, 2> out, const Tensor<xpu, 2> &in) {
for (index_t y = 0; y < out.size(0); ++y) {
for(index_t x = 0; x < out.size(1); ++x) {
out[y][x] = sigmoid::Map(in[y][x] * 2.0f) + 1.0f;
}
}
}
同样,我们也可以定义形如out = F<sigmoid>+2.0
以及out = F<sigmoid>(F<sigmoid>(in))
的复合表达式。此外,在GPU上运行的版本将会被转化为CUDA和函数,详见defop.cpp文件。
完整的例子
下面的代码来自于basic.cpp,它展示了如何使用mshadow进行计算。
// header file to use mshadow
#include "mshadow/tensor.h"
// this namespace contains all data structures, functions
using namespace mshadow;
// this namespace contains all operator overloads
using namespace mshadow::expr;
int main(void) {
// intialize tensor engine before using tensor operation, needed for CuBLAS
InitTensorEngine<cpu>();
// assume we have a float space
float data[20];
// create a 2 x 5 x 2 tensor, from existing space
Tensor<cpu, 3> ts(data, Shape3(2,5,2));
// take first subscript of the tensor
Tensor<cpu, 2> mat = ts[0];
// Tensor object is only a handle, assignment means they have same data content
// we can specify content type of a Tensor, if not specified, it is float bydefault
Tensor<cpu, 2, float> mat2 = mat;
// shape of matrix, note size order is the same as numpy
printf("%u X %u matrix\n", mat.size(0), mat.size(1));
// initialize all element to zero
mat = 0.0f;
// assign some values
mat[0][1] = 1.0f; mat[1][0] = 2.0f;
// elementwise operations
mat += (mat + 10.0f) / 10.0f + 2.0f;
// print out matrix, note: mat2 and mat1 are handles(pointers)
for (index_t i = 0; i < mat.size(0); ++i) {
for (index_t j = 0; j < mat.size(1); ++j) {
printf("%.2f ", mat2[i][j]);
}
printf("\n");
}
// shutdown tensor enigne after usage
ShutdownTensorEngine<cpu>();
return 0;
}
mshadow入门指南的更多相关文章
- Web API 入门指南 - 闲话安全
Web API入门指南有些朋友回复问了些安全方面的问题,安全方面可以写的东西实在太多了,这里尽量围绕着Web API的安全性来展开,介绍一些安全的基本概念,常见安全隐患.相关的防御技巧以及Web AP ...
- Vue.js 入门指南之“前传”(含sublime text 3 配置)
题记:关注Vue.js 很久了,但就是没有动手写过一行代码,今天准备入手,却发现自己比菜鸟还菜,于是四方寻找大牛指点,才终于找到了入门的“入门”,就算是“入门指南”的“前传”吧.此文献给跟我一样“白痴 ...
- yii2实战教程之新手入门指南-简单博客管理系统
作者:白狼 出处:http://www.manks.top/document/easy_blog_manage_system.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文 ...
- 【翻译】Fluent NHibernate介绍和入门指南
英文原文地址:https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started 翻译原文地址:http://www.cnblogs ...
- ASP.NET MVC 5 入门指南汇总
经过前一段时间的翻译和编辑,我们陆续发出12篇ASP.NET MVC 5的入门文章.其中大部分翻译自ASP.NET MVC 5 官方教程,由于本系列文章言简意赅,篇幅适中,从一个web网站示例开始讲解 ...
- 一起学微软Power BI系列-官方文档-入门指南(1)Power BI初步介绍
我们在前一篇文章微软新神器-Power BI,一个简单易用,还用得起的BI产品中,我们初步介绍了Power BI的基本知识.由于Power BI是去年开始微软新发布的一个产品,虽然已经可以企业级应用, ...
- 一起学微软Power BI系列-官方文档-入门指南(2)获取源数据
我们在文章: 一起学微软Power BI系列-官方文档-入门指南(1)Power BI初步介绍中,我们介绍了官方入门文档的第一章.今天继续给大家介绍官方文档中,如何获取数据源的相关内容.虽然是英文,但 ...
- 一起学微软Power BI系列-官方文档-入门指南(3)Power BI建模
我们前2篇文章:一起学微软Power BI系列-官方文档-入门指南(1)Power BI初步介绍 和一起学微软Power BI系列-官方文档-入门指南(2)获取源数据 中,我们介绍了官方入门文档与获取 ...
- 一起学微软Power BI系列-官方文档-入门指南(4)Power BI的可视化
在前面的系列文章中,我们介绍了官方有关获取数据,以及建模的原始文档和基本介绍.今天继续给大家介绍官方文档中,有关可视化的内容.实际上获获取数据和建模更注重业务关系的处理,而可视化则关注对数据的解读.这 ...
随机推荐
- python中浅拷贝和深拷贝的区别
浅拷贝 可变类型浅拷贝copy函数就是浅拷贝,只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象可变类型:a = [1, 2, 3] b = [11, 2 ...
- python 魔法方法诠释
什么是Python魔法方法 什么是魔法方法呢?它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法"的特殊方法. 它们经常是两个下划线包围来命名的(比如 ini ...
- 排序算法:冒泡排序(Bubble Sort)
冒泡排序 算法原理 冒泡排序的原理是每次从头开始依次比较相邻的两个元素,如果后面一个元素比前一个要大,说明顺序不对,则将它们交换,本次循环完毕之后再次从头开始扫描,直到某次扫描中没有元素交换,说明每个 ...
- C++指针delete后还要置为null
非常好的一篇说明: 转载:https://blog.csdn.net/qq_36570733/article/details/80043321 众所周知,最开始我们用new来创建一个指针,那么等我们用 ...
- 下载、安装 PL/SQL Developer
操作系统:Windows 10 x64 第一节:下载 Oracle Database XE 11gR2 第二节:安装.验证安装 Oracle Database XE 11gR2 第三节:Oracle ...
- php正则偷电影
1.是将电影网站弄到自己的phpstudy下面,然后进行获取电影的一些数据,然后将其存到数据库,不要获取别人网站的数据,不然会导致网站的崩溃.
- 汕尾6397.7539(薇)xiaojie:汕尾哪里有xiaomei
汕尾哪里有小姐服务大保健[微信:6397.7539倩儿小妹[汕尾叫小姐服务√o服务微信:6397.7539倩儿小妹[汕尾叫小姐服务][十微信:6397.7539倩儿小妹][汕尾叫小姐包夜服务][十微信 ...
- localStorage使用小结
一.什么是localStorage.sessionStorage 在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题 ...
- C语言/C++编程学习:送给考计算机二级的同学:公共基础知识总结!
数据结构与算法 1.算法 算法:是指解题方案的准确而完整的描述. 算法不等于程序,也不等计算机方法,程序的编制不可能优于算法的设计. 算法的基本特征:是一组严谨地定义运算顺序的规则,每一个规则都是有效 ...
- 扫描仪扫描文件处理-Python批量处理
多进程处理扫描出来的图片,参见: https://github.com/barrer/scan-helper bug问题反馈github提Issues