12行实现一个简易神经网络
准备工作
现实现一个具有三层的最简单的神经网络,**函数采用sigmod函数。整体结构如下所示:
- x是输入的样本值。设定为5*3的矩阵,即代表5个样本值,每个样本有3个特征值。
- y是标签,为5*1的矩阵代表每一个样本的标签。且取值范围为0或1。即完成一个简单的分类问题。
- w0、w1为中间的权重参数。L0、L1、L2表示每一层的计算结果。
- L2即是最终与标签比对的结果
首先是数据的初始化:
x = np.array(
[[0, 0, 1],
[0, 1, 1],
[1, 0, 1],
[1, 1, 1],
[0, 0, 1]]
)
y = np.array([
[0],
[1],
[1],
[0],
[0],
])
np.random.seed(1)
# w0 w1取值范围为 -1 到 1
w0 = 2 * np.random.random([3, 4]) - 1
w1 = 2 * np.random.random([4, 1]) - 1
因为神经网络可以大概分为两部分:前向传播与反向传播,且主要难点在反向传播。故要对反向传播进行公式的推导。
反向传播的推导
sigmod函数的定义
首先定义好sigmod函数:
def sigmod(x, deriv = False):
if(deriv == True):
return x * (1 - x)
return 1 / (1 + np.exp(-x))
该函数的deriv参数是为了方便进行反向传播的运算。我们知道sigmod函数具有一个很有意思的特征:
故当进行反向传播时需要返回sigmod函数的导数,所以定义deriv参数方便返回值的调用。
损失函数
使用均方函数定义误差,如下:
分别对、求导:
其实就是简单的复合函数求导,难度较低。
在求得梯度之后就可以进行梯度下降算法更新、参数,进行迭代。
代码编写
设置迭代10万次,每一万次输出一下loss值
for j in range(100000):
l0 = x
l1 = sigmod(np.dot(l0, w0))
l2 = sigmod(np.dot(l1, w1))
l2_error = (y - l2) # 均方误差的导数值
l2_delta = l2_error * sigmod(l2, True) # 导数相乘。对应公式
l1_error = l2_delta.dot(w1.T)
l1_delta = l1_error * sigmod(l1, True)
w1 += l1.T.dot(l2_delta) # 每次更新梯度值
w0 += l0.T.dot(l1_delta)
if j % 10000 == 0:
print('Error:' + str(np.mean(np.abs(l2_error))))
总结
整体其实还是蛮抽象的,关键在于梯度的计算。
代码里面的每一个变量都能在公式里找到位置,因为最终更新公式太长了所以把一些变量分开写了。