在这一部分内容中,我们的主要目标是使用神经网络从零开始构建一个与、或、异或等逻辑运算门,具体而言这一章节是使用神经网络构建异或逻辑门。
逻辑运算门是构建电子单元模块的重要组成部门。顾名思义,逻辑运算门为我们提供逻辑运算操作。就拿异或门操作而言,如上图所示,只有当输入的信号不同的时候,逻辑门才会输出高电平;当输入的信号相同的时候,才会输出低电平。因此,后续将从零介绍如何使用神经网络实现这样一个异或门逻辑运算操作。
1 导入模块
首先,我们需要导入一些必要的模块从而去实现神经网络的构建。在这里,我们暂时并不需要使用那些复杂的框架(比如PyTorch),仅仅使用NumPy库就可以。为了后续可视化模型训练的过程,我们还需要导入matplotlib库。
import numpy as np
import matplotlib.pyplot as plt
2 输入数据
下面,我们研究一下神经网络所需要的输入数据。首先,输入数据分为两个部分,输入的训练数据以及所期望的输出数据(又称之为真值)。
x1,x2 = np.array([0,0,1,1]), np.array([0,1,0,1])
x = np.array([x1,x2])
y = np.array([0,1,1,0])
上述代码中的x1和x2分别表示异或门的两个数据,y表示每对x1和x2所期望的输出数据。输入数据共有四列数据,表示参加模型训练的总共有四组,即batch size为4。为了方便管理输入数据,我们将x1和x2整合成x数据,这个x的尺寸为(2,4),怎样正确的理解x数据的尺寸呢?从线性代数的角度而言,我们需要将x看成是列向量,即共有4组1*2的列向量,每个列向量共有2个特征。
下面,让我们定义一些在构建只含有一个神经元的神经网络工程中所需要用到的超参数吧。其中nums_input表示输入数据特征的个数,在此处由于有x1和x2两个特征,因此为2,nums_hidden表示隐藏层神经元的个数,在这里设置为2,num_output表示输出节点的个数,在此处只有y一个输出,故为1。lr表示学习率,epochs表示训练模型的迭代次数,losses表示需要存储每一次的损失函数的值,这是为了后续绘制损失函数曲线用的。
nums_input, nums_hidden, nums_output = 2,2,1
lr = 0.01
epochs = 10000
losses = []
bs = x.shape[1]
3 前向传播
前项传播是神经网络对输入数据进行学习的过程,具体而言就是将输入数据依次进行下面的公式操作:
上式中的z表示经过w和b计算之后的值,而a表示经过记过激活函数计算的值。w和b的值分别是什么呢?在这里我们需要随机生成w和b的值进行神经网络的第一次训练:
w1 = np.random.rand(nums_hidden, nums_input)
b1 = np.random.rand(nums_hidden,1)
w2 = np.random.rand(nums_output, nums_hidden)
b2 = np.random.rand(nums_output,1)
不难得知,W的行表示输出神经元的个数,列表示输入神经网络的个数;b的行表示输出神经元的个数,而列一直为1。下面对神经网络进行前向传播计算:
# 进行前项传播计算
def sigmoid(x):
return 1.0 / (1 + np.exp(-x))
# 前项传播函数
def forward(x,w1,b1,w2,b2):
# 必须将b1变成列向量
z1 = np.dot(w1,x) + b1
a1 = sigmoid(z1)
z2 = np.dot(w2,a1) + b2
a2 = sigmoid(z2)
return z1,a1,z2,a2
4 反向传播
反向传播的参数计算公式如下:
因此,我们可以按照上述的公式进行编程,对神经网络的参数进行更新(参考这篇博客进行总结)。
def backward(x,y,z1,a1,z2,a2,w1,w2):
bs = x.shape[1]
dz2 = a2 - y
dw2 = np.dot(dz2, a1.T) / bs
db2 = np.sum(dz2, axis=1,keepdims=True) / bs
dz1 = np.dot(w2.T,dz2)*a1*(1-a1)
dw1 = np.dot(dz1, x.T) / bs
db1 = np.sum(dz1, axis=1,keepdims=True) / bs
# 对dw1和dw2的数据的尺寸进行设置
dw1 = dw1.reshape(w1.shape)
db1 = db1.reshape(b1.shape)
dw2 = dw2.reshape(w2.shape)
db2 = db2.reshape(b2.shape)
return dw1,db1,dw2,db2
5 训练模型
因此,就可以开始对神经网络进行训练了。在模型每次训练的迭代过程中,依次做了下面几件事情:
(1)进行了前向传播算法,并输出每一层的z和a值。
(2)进行损失函数的计算。
(3)进行反向传播,需要用到每一层的w,b以及z和a的值。
(4)进行参数的更新。
import matplotlib.pyplot as plt
# 对超参数进行设置
lr = 0.01
epochs = 10000
losses = []
bs = x.shape[1]
# 进行训练
for epoch in range(epochs):
# 进行前项传播
z1,a1,z2,a2 = forward(x,w1,b1,w2,b2)
loss = -np.sum(y * np.log(a2) + (1-y)*np.log(1-a2)) / bs
losses.append(loss)
# 进行反向传播
dw1,db1,dw2,db2 = backward(x,y,z1,a1,z2,a2,w1,w2)
w1 -= lr * dw1
b1 -= lr * db1
w2 -= lr * dw2
b2 -= lr * db2
# 绘制损失函数曲线
plt.plot(losses)
plt.xlabel("epoch")
plt.ylabel("loss value")
下面就是模型可视化的效果:
6 测试模型
# 进行推理计算
def inference(x,w1,b1,w2,b2):
_,_,_,a2 = forward(x,w1,b1,w2,b2)
a2 = np.squeeze(a2)
if a2 > 0.5:
return 1
return 0