用 python 实现一个简单的神经网络
1. 前言
让我们先看一个经典的双层神经网络模型,想必大家都很熟悉了。
在这个模型中,有一个输入层、一个输出层和一个隐藏层。但是单纯根据这个模型来看图说话,实现一个神经网络,还是比较困难的。
2. 另一种表示
现在看看这个双层神经网络的另外一种表示方法:
同前面的图相比,有几点区别:
- 除了输入(x)、输出(y_pred)和隐藏层(h 和 h_relu)这几个表征层之外,另外还有两个网络层 w1 和 w2。可以将 w1 看做输入表示到隐藏表示之间的映射,将 w2 看做隐藏表示到输出表示之间的映射。
- 输入向量的维度为 D_in, 输出向量维度为 D_out,隐藏层向量维度为 H。可以批量执行,每批样本量为 N。
- 在隐藏层内部,使用了 relu 激活函数
3. 反向传播
反向传播中,最重要的是求梯度。在矩阵乘这种场景下如何求梯度呢?
如
h_relu * w2 = y_pred
那么,有了 y_pred 的梯度(grad_y_pred),如何求 h_relu 和 w2 的梯度呢?
很简单:
grad_h_relu = grad_y_pred * w2.T
grad_w2 = h_relu.T * grad_y_pred
等式左边变量的梯度,依赖于等式右边变量的梯度和左边其他变量的现值。具体如何乘,是否需要做转置,可根据矩阵的维度确定。
除了矩阵乘法梯度计算之外,其他梯度计算根据相应情况确定。
4. 实现
有了上面的表示,我们用 numpy 手动实现一个神经网络就比较简单了
# -*- coding: utf-8 -*-
import numpy as np
N, D_in, H, D_out = 64, 1000, 100, 10
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)
w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)
learning_rate = 1e-6
for t in range(500):
h = x.dot(w1)
h_relu = np.maximum(h, 0)
y_pred = h_relu.dot(w2)
loss = np.square(y_pred - y).sum()
print(t, loss)
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.T.dot(grad_y_pred)
grad_h_relu = grad_y_pred.dot(w2.T)
grad_h = grad_h_relu.copy()
grad_h[h < 0] = 0
grad_w1 = x.T.dot(grad_h)
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2