多少行代码可以实现神经网络,并且了解反向传播算法。
import numpy as np
X = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
y = np.array([[0, 0, 1, 1]]).T
syn = 2 * np.random.random((3, 1)) - 1
for i in range(10000):
l = 1 / (1 + np.exp(-np.dot(X, syn)))
l_delta = (y - l) * (l * (1 - l))
syn += np.dot(X.T, l_delta)
复制代码
如上,通过给定的输入的输出进行训练,最后根据给定输入预测输出。
x1 | x2 | x3 | y |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
0 | 1 | 1 | 0 |
训练数据如上。
下面对代码进行解释:
X:表示输出数据
y: 表述输出数据
syn: 3 * 1维的初始化随机权重
网络结构如下所示:
其过程大概为:
输入数据x
乘以对应的权重w
,得到的值z
经过非线性函数转换(sigmoid)得到预测值l
。
计算l
的目标值y
的误差。训练的目标为将误差最小化,因此可以通过调节输入x
和权重w
。因为x
为输入数据,不可以调节。因此目标为:通过不断的调节权重w
,使其得到的预测值与目标的值的误差最小(或者小于某个阈值),完成训练。可以使用权重w
对新的输入进行预测。
其迭代过程为最后3行代码,稍作解释:
第6行:输入x
乘以权重w
,经过sigmoid
函数变化得到l
。 第7行:将误差最小化的过程就是不断调节w
的过程。根据链式法则求loss
关于w
的导数,找到损失函数最快降低的方法。如上图片所示:loss
关于w
的导数 = loss
关于l
的导数 * l
关于z
的导数 * z
关于w
的导数, 也即是最后两行的内容。
下面我将扩展一下代码, 以便更好的理解:
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def sigmoid_derivative(x):
return x * (1 - x)
X = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
y = np.array([[0, 0, 1, 1]]).T
syn = 2 * np.random.random((3, 1)) - 1
for i in range(10000):
l = sigmoid(np.dot(X, syn))
loss = (y - l) ** 2
if i % 1000 == 0:
print("Loss: ", np.sum(loss))
loss_derivative = 2 * (y - l)
l_delta = loss_derivative * sigmoid_derivative(l)
syn += np.dot(X.T, l_delta)
print("syn:\n" ,syn)
print("l:\n", l)
复制代码
下面是打印结果:
Loss: 0.8505452956411994
Loss: 0.0013461667714825178
Loss: 0.0006585827213748538
Loss: 0.0004348364571153864
Loss: 0.00032425670465056856
Loss: 0.0002583897080090336
Loss: 0.00021470251268552865
Loss: 0.00018361751185635365
Loss: 0.000160374621766103
Loss: 0.00014234163846899023
syn:
[[10.38061079]
[-0.20679655]
[-4.98439294]]
l:
[[0.00679775]
[0.00553486]
[0.99548654]
[0.9944554 ]]
复制代码
可以看到随着迭代次数的增加,loss变的越来越小,说明计算得到的值越接近真实值。 最后打印的权重syn和预测值l
也证明了这一点。
后面打算加入隐藏层,提高神经网络的泛化能力。
参考资料:
A Neural Network in 11 lines of Python (Part 1)
深度学习之反向传播算法 上/下 Part 3 ver 0.9 beta