
NumPy Ninjutsu:掌握用于高性能机器学习的数组操作。
作者 | Ideogram 提供图片
机器学习工作流程通常涉及大量数值计算,这些计算以数学和代数运算的形式处理存储为大型向量、矩阵,甚至张量(三维或更多维度的矩阵对应物)的数据。就计算成本而言,这可能转化为处理、训练和应用推理过程(如使用这些大型数据结构进行预测)的显着的时间和内存消耗。因此,优化这些低级操作的效率至关重要。
这就是NumPy库可以提供帮助的地方:NumPy数组是为快速、内存高效的数值计算而设计的数据结构,利用向量化和广播等敏捷的计算过程,从而实现高性能的机器学习建模过程,就像忍者一样快速而安静。
在本文中,我们将揭示一些NumPy数组操作的代表性示例,这些示例对于优化机器学习工作流程的性能特别有用。
向量化操作
NumPy允许将算术运算或数学函数应用于整个数组,将运算(或函数)逐元素应用,而无需循环。例如,给定一个包含1000个元素的数组arr
,arr*2
会将数组中的每个元素乘以二。
在函数使用方面,向量化非常方便,例如,用于在手动定义的神经网络中执行激活函数。ReLU(Rectified Linear Unit)函数是一个非常常见的激活函数,已被证明在训练神经网络模型方面非常有效。它通过将负值映射到0并保持正值不变来消除信息中的线性。这就是使用NumPy数组向量化实现ReLU激活函数的方法:
1 2 3 4 |
import numpy as np input_array = np.array([-2.0, 0.0, 1.5]) output = np.maximum(0, input_array) print(output) |
输出: [0. 0. 1.5]
为了便于理解,我们刚刚定义了一个包含三个元素的数组——信息流经三个神经元。真正的魔力发生在当元素数量不是三个而是成千上万甚至数百万个时。那时效率优化才能真正发挥作用。
用于批量计算的广播
NumPy数组操作的另一个吸引人的特性是广播:它涉及调整用于它们之间的数学运算的多个数组中的至少一个的尺寸。标准化数据矩阵中的值是一个很好的例子,也是各种机器学习建模技术中一个非常常见的过程,这些技术通常需要缩放数据以获得更有效的结果。
1 2 3 4 5 |
batch = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]) normalized = (batch - batch.mean(axis=0)) / batch.std(axis=0) |
输出
1 2 3 |
array([[-1.22474487, -1.22474487], [ 0. , 0. ], [ 1.22474487, 1.22474487]]) |
在上面的例子中,batch.mean(axis=0)
和batch.std(axis=0)
都是包含两个元素的1D数组:分别是列的平均值和标准差。因此,对于2D矩阵中的每个元素,标准化包括减去平均值并除以该元素所属列的标准差。
矩阵乘法
矩阵乘法是许多机器学习模型(包括经典模型和基于神经网络的模型)应用的线性变换的核心,即用神经元之间连续全连接层的连接权重乘以流动信息。即使在像Transformer这样的非常大的模型(作为语言模型的基础)中,也会发生这种操作。
下面是用NumPy实现的方式,模拟了两个各包含两个神经元的全连接层:
1 2 3 4 5 6 |
weights = np.array([[0.2, 0.8], [0.5, 0.1]]) inputs = np.array([1.0, 2.0]) bias = np.array([0.1, -0.1]) output = np.dot(weights, inputs) + bias print(np.round(output, 2)) |
输出: [1.9 0.6]
高级掩码行选择
当您需要根据某些外部条件选择数据集中的特定实例时,这非常有用,例如,通过布尔掩码建模,这是一个由“真/假”元素组成的1D数组,用于决定要过滤的2D数据集中的哪些行。以下示例使用掩码来选择数据集矩阵中的第二行和第三行。
1 2 3 4 |
data = np.array([[1, 2], [3, 4], [5, 6]]) labels = np.array([0, 1, 1]) filtered = data[labels == 1] print(filtered) |
输出
1 2 |
[[3 4] [5 6]] |
ArgMax用于概率类预测
几个分类模型使用一种称为softmax的函数来计算一个实例属于某个类别的归一化概率,该类别在几个互斥的类别或分类中。在逐字生成文本响应的语言模型中,通过顺序应用下一个词预测问题,这种softmax原理变得极其复杂,需要计算词汇表中每个词(通常是人类语言)是下一个要生成的词的概率。多亏了np.argmax
,找到具有最高概率的词(或通常来说,类别)变得非常容易。
本例说明了该函数在两个实例中的应用,这两个实例的属于三个可能类别的概率存储在logits
矩阵中:
1 2 3 |
logits = np.array([[0.2, 0.8, 0.0], [0.5, 0.3, 0.2]]) predictions = np.argmax(logits, axis=1) print(predictions) |
输出: [1 0]
输出是用于对每个实例进行分类的选定类别(类别默认从0到2索引)。
使用Einsum进行自定义张量运算
Einsum — 爱因斯坦求和的缩写 — 是NumPy中的一个有趣函数。它乍一看可能显得微不足道,但此函数使用特定的符号来以可解释的方式表达数组上的代数运算,如点积、外积,甚至Transformer注意力机制。它还可以方便地在深度学习架构中构建自定义层。
为了能对该函数有一个初步的了解,让我们来看这个例子,它使用等效的“einsum”表达式来表示矩阵乘法:'ij,jk->ik'
。
1 2 3 4 |
A = np.array([[1, 2], [3, 4]]) B = np.array([[5, 6], [7, 8]]) result = np.einsum('ij,jk->ik', A, B) print(result) |
输出
1 2 |
[[19 22] [43 50]] |
如果这个函数背后的机制不像真正的忍术,那还有什么能像呢?
有关此函数工作原理的更多信息,请查看NumPy文档页面。
结论
本文揭示了Python的NumPy库提供的六种有趣的忍术技巧策略,用于高效地执行数组操作,这些操作对于扩展进行数据、模型权重等密集计算的自定义机器学习工作流非常有用。
暂无评论。