Apache MXNet - Python API ndarray
Apache MXNet - Python API: NDArray
Section titled “Apache MXNet - Python API: NDArray”本章探讨 mxnet.ndarray (通常导入为 mx.nd),它是 Apache MXNet 中用于数值计算的核心库。NDArray 是在 MXNet 中处理数据的基础。
理解 mxnet.ndarray
Section titled “理解 mxnet.ndarray”MXNet 的 NDArray 提供了一个多功能的 N 维数组对象,类似于 NumPy 数组,但具有专为深度学习和高性能计算量身定制的关键特性:
- 硬件加速:NDArray 操作可以在各种硬件上运行,包括 CPU 和 NVIDIA GPU,从而显著加速大型计算。
- 自动并行化:MXNet 的执行引擎会自动在可用的硬件资源上并行化 NDArray 操作。
- 异步执行:操作通常异步执行,尤其是在 GPU 上,这使得 Python 前端可以在计算在后台运行时继续处理。
注意:MXNet 2.0 引入了 mxnet.numpy API,旨在提高与 NumPy 的兼容性。虽然 mxnet.ndarray (mx.nd) 仍然是基础库,并在旧代码库和内部广泛使用,但新项目可能更喜欢 mxnet.numpy,以获得更熟悉的 NumPy 风格体验。本教程重点介绍原始的 mxnet.ndarray API。
以下是如何从 Python 列表创建 NDArray:
import mxnet as mxfrom mxnet import nd
# 创建一个一维 NDArrayx = nd.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])print(x)输出:
<html> <body> <p> [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] <NDArray 10 @cpu(0)> </p> </body></html>二维 NDArray 示例:
# 创建一个二维 NDArray (3x10 矩阵)y = nd.array([[1,2,3,4,5,6,7,8,9,10], [11,12,13,14,15,16,17,18,19,20], [21,22,23,24,25,26,27,28,29,30]])print(y)输出:
<html> <body> <p> [[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] [11. 12. 13. 14. 15. 16. 17. 18. 19. 20.] [21. 22. 23. 24. 25. 26. 27. 28. 29. 30.]] <NDArray 3x10 @cpu(0)> </p> </body></html>让我们深入探讨 mxnet.ndarray API 的类、函数和参数。
| 类 | 定义 |
|---|---|
| NDArray(handle[, writable]) | 表示多维、同构固定大小项目数组的主要数组对象。它可以驻留在 CPU 或 GPU 内存中。 |
| CachedOp(sym[, flags]) | 缓存操作符的句柄,内部用于优化带有 NDArray 输入的符号图的重复执行。通常不直接由终端用户使用。 |
常用函数和参数
Section titled “常用函数和参数”mxnet.ndarray 模块提供了丰富的数值计算函数集,类似于 NumPy 中的函数。其中许多函数也可以作为 NDArray 对象本身的方法使用(例如,x.sum() 等同于 nd.sum(x))。
| 函数及参数 | 定义 |
|---|---|
| Activation(data, act_type=‘relu’, out=None, name=None) | 对输入按元素应用激活函数。支持的 act_type 包括 ‘relu’、‘sigmoid’、‘tanh’、‘softrelu’、‘softsign’。这常用于神经网络。 |
| BatchNorm(data, gamma, beta, moving_mean, moving_var, eps=0.001, momentum=0.9, fix_gamma=False, use_global_stats=False, output_mean_var=False, axis=1, cudnn_off=False, min_calib_range=None, max_calib_range=None, out=None, name=None) | 执行批标准化 (Batch Normalization)。通过数据的均值和方差对其进行标准化,然后应用学习到的缩放因子 (gamma) 和偏移量 (beta)。这对于稳定和加速深度神经网络的训练至关重要。 |
| BilinearSampler(data, grid, cudnn_off=False, out=None, name=None) | 使用 grid 对输入特征图应用双线性采样 (Bilinear Sampling)。这是空间变换网络 (Spatial Transformer Networks) 中的关键组件,允许对特征图进行可学习的空间变换。grid 指定采样坐标。 |
| BlockGrad(data, out=None, name=None) | 停止梯度计算。在反向传播期间,梯度不会通过此操作符流向其输入。这对于冻结网络部分或进行特定的对抗性训练设置非常有用。 |
| cast(data, dtype, out=None, name=None) | 将输入 NDArray 的所有元素转换为新的数据类型 (dtype),例如 ‘float32’、‘float16’、‘int32’。 |
使用 BilinearSampler 对小型特征图进行上采样(放大)的示例。这里,我们定义一个用于缩放的仿射变换矩阵。
import mxnet as mxfrom mxnet import nd
# 输入数据 (1 通道, 2x2 图像)data = nd.array([[[[1, 2], [3, 4]]]], ctx=mx.cpu())
# 用于 2 倍缩放的仿射矩阵 (grid 生成中的 0.5 表示放大 2 倍)# [s_x, 0, 0 ]# [0, s_y, 0 ] (针对相对于源的 target_shape)# 我们想将 4x4 输出 grid 映射到 2x2 输入affine_matrix = nd.array([[0.5, 0, 0], [0, 0.5, 0]], ctx=mx.cpu())
affine_matrix = affine_matrix.reshape((1, 6)) # 批量大小为 1,展平的 2x3 矩阵
# 为 4x4 输出生成 grid,映射回 'data' 坐标grid = nd.GridGenerator(data=affine_matrix, transform_type='affine', target_shape=(4, 4))
output = nd.BilinearSampler(data, grid)print(output)输出(值将被插值):
<html> <body> <p> [[[[1. 1.25 1.75 2. ] [1.5 1.75 2.25 2.5 ] [2.5 2.75 3.25 3.5 ] [3. 3.25 3.75 4. ]]]] <NDArray 1x1x4x4 @cpu(0)> </p> </body></html>使用带有 warp field 的 BilinearSampler 进行水平移动数据的示例。warp grid 中的正水平位移意味着从左侧采样。
import mxnet as mxfrom mxnet import nd
# 输入数据 (1 通道, 4x4 图像)data = nd.array([[[[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12], [13, 14, 15, 16]]]], ctx=mx.cpu())
# 创建一个 warp grid。warp_matrix 第一个通道中的值 1 表示移动 1 像素。# 我们想将图像内容向左移动 1 像素,因此样本从右侧 1 像素处获取。# Grid 值已归一化:-1 到 1。在 4 像素宽图像中移动 1 像素,在归一化坐标中为 1 * (2/4) = 0.5。# x-flow field 中的正值将采样点向右移动。flow_x = nd.full((1,1,4,4), 0.5, ctx=mx.cpu()) # 将采样点向右移动 1 像素flow_y = nd.zeros((1,1,4,4), ctx=mx.cpu())warp_matrix = nd.concat(flow_x, flow_y, dim=1) # 形状 (1, 2, H, W)
grid = nd.GridGenerator(data=warp_matrix, transform_type='warp', target_shape=(4,4)) # 对于 warp,target_shape 通常被忽略output = nd.BilinearSampler(data, grid)print("内容向左移动,最右侧列由于越界采样而变为零:")输出(内容向左移动,最右侧列由于越界采样而变为零,BilinearSampler 通常将其视为零):
<html> <body> <p>Data shifted (content moves left, zeros appear on right):[[[[ 2. 3. 4. 0. ] [ 6. 7. 8. 0. ] [10. 11. 12. 0. ] [14. 15. 16. 0. ]]]] <NDArray 1x1x4x4 @cpu(0)> </p> </body></html>使用 cast() 函数的示例:
original_array = nd.array([300.0, 10.1, 15.4, -1.0, -2.0])casted_array = nd.cast(original_array, dtype='uint8')print(f"原始: {original_array}")print(f"转换为 uint8: {casted_array}")输出(值被限制/环绕到 uint8 范围 [0, 255]):
<html> <body> <p>Original: [300. 10.1 15.4 -1. -2. ] <NDArray 5 @cpu(0)>Casted to uint8: [255 10 15 255 254] <NDArray 5 @cpu(0)> </p> </body></html>ndarray.contrib:实验性功能
Section titled “ndarray.contrib:实验性功能”mxnet.ndarray.contrib 包包含实验性 API 和功能。提供这些函数是为了让社区试用并提供反馈。它们可能会在未来版本中被提升到主要的 mxnet.ndarray API,或者被更改或移除。在生产环境中使用时请谨慎。
ndarray.contrib 中的精选函数
Section titled “ndarray.contrib 中的精选函数”| 函数及参数 | 定义 |
|---|---|
| rand_zipfian(true_classes, num_sampled, range_max, out=None, name=None) | 从近似的 Zipfian 分布中抽取随机样本。常用于大规模分类任务的候选采样(例如,在自然语言处理中)。 |
| foreach(body, data, init_states, name=None) | 在 NDArray 切片上执行类似 for 循环的计算。body 是一个 Python 函数,定义每次迭代的计算。这是一种表达某些序列操作的方式,这些操作仍然可以由 MXNet 引擎管理。 |
| while_loop(cond, func, loop_vars, max_iterations=None, name=None) | 执行类似 while 循环的计算。cond 是确定循环条件的 Python 函数,func 定义循环内的计算,loop_vars 是迭代的变量。 |
| cond(pred, then_func, else_func, name=None) | 执行 if-then-else 分支。pred 是一个 NDArray 标量条件;如果为真,则执行 then_func,否则执行 else_func。then_func 和 else_func 都是返回 NDArray 列表的 Python 函数。 |
| isinf(data, out=None, name=None) | 执行按元素检查,确定 NDArray 元素是否为无穷大。返回布尔型 NDArray。 |
| getnnz(data, axis=None, out=None, name=None) | 计算稀疏张量(特别是在 CPU 上的 CSR 格式)中非零存储值的数量。如果存储了显式零,也包括在内。 |
| requantize(data, min_range, max_range, out_min_range=None, out_max_range=None, out_type=‘int8’, out=None, name=None) | 使用新的最小/最大阈值,将数据从 int32(及其最小/最大范围)重新量化到 int8 等较低精度类型。用于模型量化以进行推理优化。 |
实现示例 (contrib)
Section titled “实现示例 (contrib)”使用 rand_zipfian 的示例:
from mxnet.contrib import nd as contrib_nd
true_classes = mx.nd.array([2, 7]) # 真类标签的批量num_sampled = 3range_max = 10 # 从 [0, 9] 采样
samples, expected_count_true, expected_count_sample = contrib_nd.rand_zipfian( true_classes, num_sampled, range_max)print(f"样本:\n{samples}")print(f"真类别的期望计数:\n{expected_count_true}")print(f"采样类别的期望计数:\n{expected_count_sample}")输出(实际样本会因运行而异):
<html> <body> <p>Samples:[[0 1 4] [3 0 1]]<NDArray 2x3 @cpu(0)>Expected count for true classes:[0.411263 पैसा 0.23979962]<NDArray 2 @cpu(0)>Expected count for sampled classes:[[0.73500365 0.5108256 0.3413174 ] [0.37796447 0.73500365 0.5108256 ]]<NDArray 2x3 @cpu(0)> </p> </body></html>使用 while_loop 计算达到指定限制的类似斐波那契数列的示例:
from mxnet.contrib import nd as contrib_nd
# 循环条件:当前值 < 20cond = lambda i, prev, curr: curr < 20
# 循环函数:更新 prev, curr;输出 currfunc = lambda i, prev, curr: ([curr], [i + 1, curr, prev + curr])
# 初始循环变量:(迭代计数, 前一个值, 当前值)loop_vars = (mx.nd.array([0]), mx.nd.array([0]), mx.nd.array([1]))
outputs, states = contrib_nd.while_loop(cond, func, loop_vars, max_iterations=10)
print(f"每次迭代的输出(当前值):\n{outputs[0]}")print(f"最终状态 (i, prev, curr):\n{states}")输出:
<html> <body> <p>Outputs from each iteration (current_value):[[ 1.] [ 1.] [ 2.] [ 3.] [ 5.] [ 8.] [13.]]<NDArray 7x1 @cpu(0)>Final states (i, prev, curr):[<NDArray 1 @cpu(0)>, <NDArray 1 @cpu(0)>, <NDArray 1 @cpu(0)>] </p> </body></html>ndarray.image:图像处理工具
Section titled “ndarray.image:图像处理工具”mxnet.ndarray.image 模块提供了专门用于对 NDArray 执行图像处理和增强任务的函数。这些函数常用于计算机视觉模型的数据预处理流水线中。
ndarray.image 中的精选函数
Section titled “ndarray.image 中的精选函数”| 函数及参数 | 定义 |
|---|---|
| adjust_lighting(data, alpha, out=None, name=None) | 使用基于 PCA 的噪声调整图像光照,这是一种来自 AlexNet 的技术。alpha 是 PCA 分量乘数向量。 |
| crop(data, x, y, width, height, out=None, name=None) | 裁剪图像 NDArray。输入形状通常是 (H x W x C) 或 (N x H x W x C)。x, y 是裁剪区域的左上角坐标。 |
| normalize(data, mean=(0,0,0), std=(1,1,1), out=None, name=None) | 通过按通道减去 mean 并除以 std 来标准化形状为 (C x H x W) 或 (N x C x H x W) 的图像张量。 |
| random_crop(data, size, interp=2, out=None, name=None) | 将图像随机裁剪到指定 size(宽度,高度)。如果源图像较小,可能会进行上采样。interp 控制插值方法。 |
| random_lighting(data, alpha_std=0.05, out=None, name=None) | 根据 AlexNet 风格,向图像添加随机的基于 PCA 的光照噪声。alpha_std 控制噪声因子的标准差。 |
| random_resized_crop(data, size, area=(0.08, 1.0), ratio=(0.75, 1.33), interp=2, out=None, name=None) | 裁剪图像的随机区域并将其调整为 size。裁剪区域和宽高比在指定的 area 和 ratio 范围内随机化。ImageNet 训练中常用的增强方法。 |
| resize(data, size, keep_ratio=False, interp=2, out=None, name=None) | 将图像大小调整为目标 size(宽度,高度)。keep_ratio 保留宽高比。interp 指定插值方法。 |
| to_tensor(data, out=None, name=None) | 将形状为 (H x W x C) 或 (N x H x W x C) 且像素值在 [0, 255] 范围内的图像 NDArray 转换为形状为 (C x H x W) 或 (N x C x H x W) 且通过除以 255 将值转换为 [0, 1] 范围内的张量。 |
实现示例 (image)
Section titled “实现示例 (image)”使用 to_tensor 和 normalize 的示例:
import mxnet as mxfrom mxnet import ndimport numpy as np # 用于创建初始 numpy 数组
# 创建一个 uint8 值在 [0, 255] 范围内的虚拟图像(高度=2,宽度=2,通道=3)dummy_image_np = np.array([[[10, 20, 30], [40, 50, 60]], [[70, 80, 90], [100, 110, 120]]], dtype=np.uint8)img_nd = mx.nd.array(dummy_image_np)print(f"原始图像 (HWC):\n{img_nd}")
# 转换为张量 (CHW, float32, 范围 [0,1])img_tensor = nd.image.to_tensor(img_nd)print(f"作为张量的图像 (CHW):\n{img_tensor}")
# 标准化张量mean_vals = nd.array([0.485, 0.456, 0.406]) # 典型的 ImageNet 均值std_vals = nd.array([0.229, 0.224, 0.225]) # 典型的 ImageNet 标准差normalized_tensor = nd.image.normalize(img_tensor, mean=mean_vals, std=std_vals)print(f"标准化后的张量:\n{normalized_tensor}")输出(由于浮点精度,值将为近似值):
<html> <body> <p>Original image (HWC):[[[ 10 20 30] [ 40 50 60]]
[[ 70 80 90] [100 110 120]]]<NDArray 2x2x3 @cpu(0)>Image as tensor (CHW):[[[0.03921569 0.15686275] [0.27450982 0.39215687]]
[[0.07843138 0.19607843] [0.3137255 0.43137255]]
[[0.11764706 0.23529412] [0.3529412 0.47058824]]]<NDArray 3x2x2 @cpu(0)>Normalized tensor:[[[-1.9469869 -1.4330131 ] [-0.91903937 -0.40506548]]
[[-1.6856009 -1.1579224 ] [-0.6302439 -0.10256535]]
[[-1.2815042 -0.76430035] [-0.24709654 0.2701073 ]]]<NDArray 3x2x2 @cpu(0)> </p> </body></html>ndarray.random:随机数生成
Section titled “ndarray.random:随机数生成”mxnet.ndarray.random 模块提供了用于生成带有从各种统计分布中抽取的随机数的 NDArray 的函数。这对于初始化模型参数、创建用于测试的随机数据或实现随机过程至关重要。
ndarray.random 中的精选函数
Section titled “ndarray.random 中的精选函数”| 函数及参数 | 定义 |
|---|---|
| uniform(low=0, high=1, shape=None, dtype=None, ctx=None, out=None) | 从 [low, high) 上的均匀分布中生成样本。 |
| normal(loc=0, scale=1, shape=None, dtype=None, ctx=None, out=None) | 从均值为 loc、标准差为 scale 的正态(高斯)分布中生成样本。 |
| randn(*shape, **kwargs) | 从标准正态分布(均值 0,方差 1)中生成样本。shape 作为单独的参数传递。 |
| exponential(scale=1, shape=None, dtype=None, ctx=None, out=None) | 从速率为 1/scale (lambda) 的指数分布中生成样本。 |
| gamma(alpha, beta, shape=None, dtype=None, ctx=None, out=None) | 从形状为 alpha、尺度为 beta 的伽马分布中生成样本。 |
| multinomial(data, shape=1, get_prob=False, out=None, dtype=‘int32’) | 从多项分布中生成样本。data 包含每个结果的概率。 |
| negative_binomial(k, p, shape=None, dtype=None, ctx=None, out=None) | 从负二项分布中生成样本(在 r 次失败之前获得 k 次成功的次数,成功概率为 p)。k、p 的解释可能有所不同;请查阅 MXNet 文档以获取具体信息。 |
| generalized_negative_binomial(mu, alpha, shape=None, dtype=None, ctx=None, out=None) | 从由均值 mu 和离散度 alpha 参数化的广义负二项分布中生成样本。 |
| shuffle(data, out=None, name=None) | 沿 NDArray 的第一个轴打乱其元素。 |
| randint(low, high, shape=None, dtype=‘int32’, ctx=None, out=None) | 从范围 [low, high) 的离散均匀分布中生成随机整数。 |
| *_like functions (e.g., uniform_like, normal_like) | 这些函数(例如 normal_like(data, loc=0, scale=1)) 生成具有与输入 data NDArray 相同形状和上下文的随机样本,但值从指定的分布中抽取。 |
实现示例 (random)
Section titled “实现示例 (random)”从均匀分布和正态分布中抽取样本:
from mxnet.ndarray import random as random_nd # 为了清晰起见使用别名
# 均匀分布uniform_samples = random_nd.uniform(low=-1, high=1, shape=(2, 3))print(f"均匀样本 [-1, 1]:\n{uniform_samples}")
# 正态分布normal_samples = random_nd.normal(loc=0, scale=0.01, shape=(2,2))print(f"正态样本 (均值 0, 标准差 0.01):\n{normal_samples}")
# randintint_samples = random_nd.randint(low=0, high=10, shape=(5,), ctx=mx.cpu())print(f"随机整数 [0, 9]:\n{int_samples}")输出(实际随机值会因运行而异):
<html> <body> <p>Uniform samples [-1, 1]:[[-0.5206176 0.2503767 -0.95505637] [-0.7613349 0.04832268 0.9208205 ]]<NDArray 2x3 @cpu(0)>Normal samples (mean 0, std 0.01):[[ 0.00133301 -0.00306308] [-0.00209531 0.00401994]]<NDArray 2x2 @cpu(0)>Random integers [0, 9]:[8 3 0 9 2]<NDArray 5 @cpu(0)> </p> </body></html>ndarray.utils:实用函数
Section titled “ndarray.utils:实用函数”mxnet.ndarray.utils 模块(通常直接在 mx.nd 下提供,用于像 zeros 这样的常用函数)提供了用于创建、保存和加载 NDArray 的实用函数。
ndarray.utils 中的精选函数
Section titled “ndarray.utils 中的精选函数”| 函数及参数 | 定义 |
|---|---|
| zeros(shape, ctx=None, dtype=None, stype=‘default’) | 返回一个给定 shape 和 dtype、填充为零的新数组。stype 可以指定稀疏类型,如 ‘csr’ 或 ‘row_sparse’。 |
| empty(shape, ctx=None, dtype=None, stype=‘default’) | 返回一个给定 shape 和 dtype 的新数组,不初始化其条目。内容将是任意内存值。如果您打算立即填充它,它比 zeros 更快。 |
| array(source_array, ctx=None, dtype=None) | 从公开数组接口的对象(例如 Python 列表、NumPy 数组)创建 NDArray。 |
| load(fname) | 从文件加载 NDArray 列表或字典。文件通常使用 mx.nd.save() 保存。 |
| load_frombuffer(buf) | 从二进制字符串缓冲区(例如,从文件或网络读取的缓冲区)加载 NDArray 字典或列表。 |
| save(fname, data) | 将 NDArray 列表或将字符串映射到 NDArray 的字典保存到二进制文件中。data 可以是单个 NDArray、NDArray 列表或 str -> NDArray 字典。 |
实现示例 (utils)
Section titled “实现示例 (utils)”使用 zeros 创建数组以及使用 save/load 的示例:
# zeros 通常通过 mx.nd.zeros 访问x = mx.nd.zeros((2, 3), ctx=mx.cpu(), dtype='float32')print(f"零数组:\n{x}")
# 保存和加载 NDArraya = mx.nd.array([1, 2, 3])b = mx.nd.array([[4, 5], [6, 7]])
file_path = 'my_arrays.params' # 保存的 NDArray 的常见扩展名# 保存为列表mx.nd.save(file_path, [a, b])loaded_list = mx.nd.load(file_path)print(f"加载的列表: {loaded_list}")
# 保存为字典mx.nd.save(file_path, {'first': a, 'second': b})loaded_dict = mx.nd.load(file_path)print(f"加载的字典: {loaded_dict}")输出:
<html> <body> <p>Zeros array:[[0. 0. 0.] [0. 0. 0.]]<NDArray 2x3 @cpu(0)>Loaded list: [<NDArray 3 @cpu(0)>, <NDArray 2x2 @cpu(0)>]Loaded dict: {'first': <NDArray 3 @cpu(0)>, 'second': <NDArray 2x2 @cpu(0)>} </p> </body></html>