MxNet系列——how_to——new_op

本文介绍了如何在MXNet中创建自定义操作符(网络层)。提供了两种方法:使用Python的CustomOp,适用于CPU和GPU;使用C++/mshadow(CUDA),可以获得最佳性能。并以softmax操作符为例,详细展示了其实现过程。
部署运行你感兴趣的模型镜像

博客新址: http://blog.xuezhisd.top
邮箱:xuezhisd@126.com


如何创建新的操作符(网络层)

本节内容描述了创建新的MXNet操作符(或网络)的过程。

我们已经尽了最大努力为最常用的案例提供高性能操作符。然而,如果你需要自定义一个网络层,比如新的损失函数,有两个选择:

  • 借助前端语言(比如,Python),使用 CustomOp 来写新的操作符。既可以运行在CPU上,也可以运行在GPU上。根据你的实现情况,性能可能很高,也可能很低。
  • 使用 C++/mshadow (CUDA)。如果你不熟悉MXNet,mashadow或Cuda,这是非常困难的,除非你熟悉MXNet,mashadow和Cuda。但是它能获得最佳性能。

CustomOp

在Python中实现一个操作符和在C++中实现方法相似,但更加简单。下面是一个例子,创建了一个softmax操作符。首先构建 mxnet.operator.CustomOp 的子类,然后覆写一些方法:

import os
# 如果自定义的操作符运行在CPU上,MXNET_CPU_WORKER_NTHREADS 必须大于1
os.environ["MXNET_CPU_WORKER_NTHREADS"] = "2"
import mxnet as mx
import numpy as np

class Softmax(mx.operator.CustomOp):
    def forward(self, is_train, req, in_data, out_data, aux):
        x = in_data[0].asnumpy()
        y = np.exp(x - x.max(axis=1).reshape((x.shape[0], 1)))
        y /= y.sum(axis=1).reshape((x.shape[0], 1))
        self.assign(out_data[0], req[0], mx.nd.array(y))

上面的代码定义了新操作符的前向传播计算函数。前向传播函数的输入包含一个输入(NDArray)的列表和一个输出(NDArray)的列表。为了简便,我们调用输入NDArray的 .asnumpy(),将其转换成基于CPU的NumPy数组。

这样(数据转换)将会非常慢。如果你希望获得最佳性能,那么就保持数据为NDArray格式,并使用mx.nd中的操作来进行计算。

最后,我们使用 CustomOp.assign 将结果数组y赋值给 out_data[0]。它将会根据req的值来决定赋值方式,req取值包括:‘write’, ‘add’, 或 ‘null’。

下面是反向传播计算函数,和上面很相似:

def backward(self, req, out_grad, in_data, out_data, in_grad, aux):
    l = in_data[1].asnumpy().ravel().astype(np.int)
    y = out_data[0].asnumpy()
    y[np.arange(l.shape[0]), l] -= 1.0
    self.assign(in_grad[0], req[0], mx.nd.array(y))

Softmax类定义了新的自定义操作符的计算,但仍然需要通过定义 mx.operator.CustomOpProp 的子类的方式,来定义它的I/O格式。首先,使用名字 softmax 注册新操作符:

@mx.operator.register("softmax")
class SoftmaxProp(mx.operator.CustomOpProp):

其次,使用参数 need_top_grad=False 调用基(父类的)构造函数,因为softmax的是一个损失层,所以并不需要来自预测层的梯度输入:

    def __init__(self):
        super(SoftmaxProp, self).__init__(need_top_grad=False)

然后,声明输入和输出:

    def list_arguments(self):
        return ['data', 'label']

    def list_outputs(self):
        return ['output']

注意:list_arguments 同时声明了输入和参数。我们推荐按照以下顺序排列它们:['input1', 'input2', ... , 'weight1', 'weight2', ...]

接着,定义函数 infer_shape ,以声明输出和权重的形状,并检查输入形状的一致性:

        def infer_shape(self, in_shape):
            data_shape = in_shape[0]
            label_shape = (in_shape[0][0],)
            output_shape = in_shape[0]
            return [data_shape, label_shape], [output_shape], []

输入/输出张量的第一维是数据批的大小。标签是一组整数,每个整数对应一项数据,并且输出和输入的形状相同。函数 Infer_shape() 应该返回三个列表,即使某一项为空,顺序也必须是:输入,输出和辅助状态(此处没有)。

最后,定义函数 create_operator()。在创建softmax的实例时,后端将会调用该函数:

    def create_operator(self, ctx, shapes, dtypes):
        return Softmax()

如果想使用自定义操作符,需要创建 mx.sym.Custom 符号。其中,使用新操作符的注册名字来设置参数 op_type

mlp = mx.symbol.Custom(data=fc3, name='softmax', op_type='softmax')

该例子的完整的代码位于:examples/numpy-ops/custom_softmax.py

C++/MShadow (CUDA)

更多信息,请查阅 Developer Guide - SimpleOpDeveloper Guide - Operators

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

`torch gather` 和 MXNet的`F.gather_nd` 都是用来从张量中选取特定索引元素的功能,但在使用上有一些细微差别。 1. **PyTorch (torch)**: `gather`函数主要用于沿着给定维度`dim`获取指定索引处的元素。它接受两个参数,第一个参数是源张量(`input`),第二个参数是一个长度匹配源张量该维度的整数切片(`index`)。例如,如果你有一个三维张量和一维索引,你可以选择每个索引对应的列。输出的张量将与输入的形状相同,除了指定的维度会变为1。 ```python input = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) index = torch.tensor([0, 1]) output = torch.gather(input, dim=1, index=index) # 输出: tensor([[1, 4], # [5, 8]]) ``` 2. **MXNet (F.gather_nd)**: `F.gather_nd`与`torch`的`gather`类似,但它可以处理更高维度的索引,允许你通过多个索引来取出多维张量的元素。这个函数需要一个数据张量`(data)`、一个形状匹配的数据张量`(indices)`作为索引以及一个轴`(axis)`。这个函数返回的是一个由索引指定元素组成的张量,形状是 `(indices.shape[:-1] + data.shape[axis+1:])`。 ```python import mxnet as mx data = mx.nd.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) indices = mx.nd.array([[0, 1], [1, 0]]) # 二维索引 output = mx.nd.F.gather_nd(data, indices) # 输出: [[1, 3], [6, 5]] ``` **区别总结**: - PyTorch的`gather`更适用于单个或低维度索引的情况。 - MXNet的`F.gather_nd`支持高维度或多维索引,适合提取多位置的元素。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值