使用NumPy手写主流机器学习模型,普林斯顿大学的博士后David Bourgin最近开源了一个强大的项目。该项目包含超过3万行代码和30多个模型,可能成为“最强”的机器学习基础工具之一。
NumPy作为Python生态系统中最受欢迎的科学计算库,许多读者已经相当熟悉它。它为Python提供了高效的多维数组计算,并且提供了一系列高级数学函数,可以快速构建模型的整个计算流程。可以说,NumPy是现代深度学习框架的基础。
尽管现在使用NumPy编写模型已不再是主流做法,但这种方式仍然是理解底层架构和深度学习原理的有效途径。David Bourgin将多种机器学习模型用NumPy实现,并开源了这些代码,同时提供了相关论文和测试结果。
项目地址:https://github.com/ddbourgin/numpy-ml
据机器之心估计,该项目大约有30个主要的机器学习模型,此外还有15个用于预处理和计算的小工具,总共包含62个.py文件。平均每个模型的代码行数超过500行,在神经网络模型的layer.py文件中,代码行数接近4000行。
这可能是目前用NumPy手写机器学习模型的“最高境界”。
谁用NumPy实现了大量机器学习模型
浏览项目的代码目录,可以看出作者基本上实现了所有主流模型,这是一项令人惊叹的工作。David Bourgin不仅是一位博士后,而且在2018年获得了加州大学伯克利分校的计算认知科学博士学位,之后在普林斯顿大学从事博士后研究。尽管毕业时间不长,他已经发表了多篇优秀的论文,其中包括在ICML 2019中被接受的Oral论文。
虽然有一些人在autograd仓库中已经有了类似的实现,但David的项目仍然有其独特之处。他明确地进行了所有梯度计算,以突出概念和数学的清晰性。然而,这种方法也有缺点,每次需要对新函数进行微分时,都需要手动写出其公式。
估计David在完成这个项目后,机器学习基础已经非常扎实。他表示下一步将会添加更多的文档和示例,以便其他人更容易使用。
项目总体介绍
该项目的最大特点是作者用NumPy手写了所有的机器学习模型,包括更显式的梯度计算和反向传播过程。可以说,这是一个机器学习框架,只是代码的可读性更强。
David表示他一直在逐步编写或收集不同模型和模块的纯NumPy实现。虽然这些实现可能不如其他框架运行得快,但它们能够使模型的具体过程更加直观。当需要查看模型API背后的实现,而又不想阅读复杂的框架代码时,这些实现可以作为一个快速参考。
项目文件分为多个文件夹,每个文件夹包含不同类型的代码。作者在每个代码集下都提供了参考材料,例如模型的效果示例图、参考论文和参考链接等。
当然,如此庞大的代码可能会存在一些Bug,作者也希望更多人能够参与进来完善这些实现。如果有任何人之前也用纯NumPy实现过有趣的模型,可以直接提交PR请求。
手写NumPy全家福
David在GitHub上提供了模型和模块的实现列表,这些列表基本上反映了代码文件的结构。项目主要包括两大类模型:传统机器学习模型和主流深度学习模型。
在传统模型中,既有复杂的模型如隐马尔可夫模型和提升方法,也有经典的模型如线性回归和最近邻方法。而在深度学习模型中,主要通过不同的模块、层次、损失函数和优化器来构建代码架构,从而快速构建各种神经网络。
除了模型之外,项目还包括一些辅助模块,如预处理组件和有用的工具。
项目示例
由于代码量庞大,这里仅展示一个示例,说明如何用NumPy实现点积注意力机制。
```python class DotProductAttention(LayerBase): def init(self, scale=True, dropoutp=0, init="glorotuniform", optimizer=None): super().init(optimizer) self.init = init self.scale = scale self.dropoutp = dropoutp self.optimizer = self.optimizer self.initparams()
def _fwd(self, Q, K, V):
scale = 1 / np.sqrt(Q.shape[-1]) if self.scale else 1
scores = Q @ K.swapaxes(-2, -1) * scale # attention scores
weights = self.softmax.forward(scores) # attention weights
Y = weights @ V
return Y, weights
def _bwd(self, dy, q, k, v, weights):
d_k = k.shape[-1]
scale = 1 / np.sqrt(d_k) if self.scale else 1
dV = weights.swapaxes(-2, -1) @ dy
dWeights = dy @ v.swapaxes(-2, -1)
dScores = self.softmax.backward(dWeights)
dQ = dScores @ k * scale
dK = dScores.swapaxes(-2, -1) @ q * scale
return dQ, dK, dV
```
在这个示例中,输入向量Q、K、V被传递给_fwd
函数,用于计算每个向量的注意力分数,并通过softmax函数得到权重。_bwd
函数计算V、注意力权重、注意力分数、Q和K的梯度,用于更新网络权重。
这些代码展示了David是如何用NumPy实现这些复杂模型的,同时也体现了该项目的价值和实用性。