这章节我们来解决的问题是:如何使用神经网络实现逻辑电路中的“异或门”模型?

如下图:




多层感知机解决异或门 多层感知机 异或_神经网络


根据第2章我们知道,单层感知器是能够解决“与门”、“或门”、“非门”这些简单的线性问题,但是不能解决“异或门”这类非线性问题。

1.1 解决异或门问题的思路

如果在单层感知器上增加一层,能够很好的解决异或问题。

例如网络结构如下:


多层感知机解决异或门 多层感知机 异或_深度学习_02


我们把每一个感知器节点代表了一个分类器,那么几个节点学习到的分割线如下:


多层感知机解决异或门 多层感知机 异或_Powered by 金山文档_03


以上3个节点的组合就可以解决异或门问题。

1.2 sigmoid激活函数 -- 线性转为非线性

多层感知器中,我们期望分割面是非线性的,故选择为sigmoid为激活函数。

sigmoid表达式和曲线:


多层感知机解决异或门 多层感知机 异或_深度学习_04


sigmoid函数也叫 Logistic 函数,取值范围为(0,1),它可以将一个实数映射到(0,1)的区间。

求导如下:


多层感知机解决异或门 多层感知机 异或_多层感知机解决异或门_05


这个公式在计算梯度的时候用到。

那么采样非线性激活函数,神经网络就有可能学习到平滑的曲线来分割平面,从线性到非线性。


多层感知机解决异或门 多层感知机 异或_Powered by 金山文档_06


1.3 新的问题

按照单层感知器的权重更新,节点3的权重更新可以轻松求得;但是节点1和节点2权重的更新如何求解呢?


多层感知机解决异或门 多层感知机 异或_深度学习_07


按照公式如下:


多层感知机解决异或门 多层感知机 异或_神经网络_08


要求得w111必须要知道节点1的期望值,但是节点1,2的期望值不存在,不能直接求其梯度。

解决方案:误差反向传播算法(Backpropagation,BP算法)

1.4 误差反向传播算法 -- BP算法

神经网络的训练关键在于如何调整权值和阈值,采用的算法:

(1)单层感知机的算法:梯度下降算法;

(2)多层感知器的算法:误差反向传播算法(error Back Propagation),简称BP算法。

反向传播过程如下:


多层感知机解决异或门 多层感知机 异或_深度学习_09


在多层神经网络中,从最后一层开始,逐层的反向进行误差信息传递,并更新相应权重。使用BP算法的多层前馈(向前传播方式没有回路或者环路)网络又称之为BP神经网络;

BP神经网络学习过程:

以下图来源:http://galaxy.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html

(1)以一个3层全连接网络来为例子

下面通过一个3层全连接网络来讲解,包含2个输入、2个隐藏层(第1层3个节点、第2层2个节点)、一个输出(第3层1个节点),节点计算如右图:


多层感知机解决异或门 多层感知机 异或_多层感知机解决异或门_10


(2)前向传播

输入层到隐藏层的计算,即计算第一层输出y1,y2,y3


多层感知机解决异或门 多层感知机 异或_多层感知机解决异或门_11


隐藏层到隐藏层的计算,即计算第2层输出y4,y5。


多层感知机解决异或门 多层感知机 异或_深度学习_12


隐藏层到输出层,即计算输出y


多层感知机解决异或门 多层感知机 异或_人工智能_13


(3)反向传播

反向传播,根据网络的连接路径,从输出层反向把误差项前向传递,并更新权重和偏置参数的过程。

误差项计算:将网络输出信号y与训练数据集的输出值z(期望值)进行比较,得到的差异被称为输出层神经元的误差信号d(不是均方误差,是计算梯度的某一项)。


多层感知机解决异或门 多层感知机 异或_神经网络_14


内部神经元的误差项的计算,有2种情况:

<1>误差权重:即前向传播的权重;


多层感知机解决异或门 多层感知机 异或_Powered by 金山文档_15


<2>如果传播的误差来自多个关联神经元,它们将被叠加。


多层感知机解决异或门 多层感知机 异或_人工智能_16


多层感知机解决异或门 多层感知机 异或_深度学习_17


(4)权重更新

权重更新,根据网络的连接路径,从输出层反向把误差反向传递并更新权重和偏置的过程。

权重更新公式:


多层感知机解决异或门 多层感知机 异或_神经网络_18


多层感知机解决异或门 多层感知机 异或_多层感知机解决异或门_19


多层感知机解决异或门 多层感知机 异或_多层感知机解决异或门_20


多层感知机解决异或门 多层感知机 异或_Powered by 金山文档_21


(5)总结

来源:http://neuralnetworksanddeeplearning.com/chap2.html


多层感知机解决异或门 多层感知机 异或_Powered by 金山文档_22


1.5 多层感知器 -- 异或门例子

异或门数据集定义:

import numpy as np

def get_xy_data():
    """ XOR的训练集 -- 真值表 """
    x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
    y = np.array([0, 1, 1, 0])
    return x,y

多层感知器模型定义:

import numpy as np

""" 定义感知器模型结构 """
class XORGateModel():
    def __init__(self):
        """ 初始化权重、偏置 """
        self.w = np.random.normal(size=6).reshape((3, 2))   # 权重
        self.b = np.random.normal(size=3)                   # 偏置
        self.lr = 0.1                                       # 学习率(超参数)

    def sigmoid(self, x):
        return 1/(1 + np.exp(-x))
    
    def dsigmoid(self, x):
        y = self.sigmoid(x)
        return y * (1 - y)

    def forward(self, x):
        """ 前向计算 """
        y1 = self.sigmoid((x[0]*self.w[0][0]) + (x[1]*self.w[0][1]) + self.b[0])
        y2 = self.sigmoid((x[0]*self.w[1][0]) + (x[1]*self.w[1][1]) + self.b[1])
        y3 = self.sigmoid((y1*self.w[2][0]) + (y2*self.w[2][1]) + self.b[2])
        return y3
    
    def update_weight(self, x, n, d):
        e = (x[0] * self.w[n][0]) + (x[1] * self.w[n][1]) + self.b[n]
        self.w[n][0] = self.w[n][0] + (self.lr * d * self.dsigmoid(e) * x[0])
        self.w[n][1] = self.w[n][1] + (self.lr * d * self.dsigmoid(e) * x[1])
        self.b[n]    = self.b[n]    + (self.lr * d * self.dsigmoid(e))

    def train(self, X, Y):
        """
        X,Y -- 训练集
        """
        for i in range(100000):   # 迭代
            j = 0
            C = 0
            for xi in X:
                yi = self.forward(xi)               # 前向计算
                Ci = np.power((Y[j] - yi), 2) / 2   # 计算误差
                C += Ci
                # 更新权重跟偏置
                if Ci > 0:
                    d = Y[j] - yi
                    d1 = self.w[2][0] * d
                    d2 = self.w[2][1] * d
                    self.update_weight(xi, 0, d1)   # 节点1
                    self.update_weight(xi, 1, d2)   # 节点2
                    self.update_weight(xi, 2, d)    # 节点3
                j += 1
            print("epoch{} 误差:{}, 权重:{}".format(i, C, self.w))
            # 什么时候退出?
            if C <= 0.05:
                print("=== 与门的4数据都正确了, 退出迭代")
                break

训练并保存参数:

# 训练
    import data_manager
    X, Y = data_manager.get_xy_data()  # 读取数据集
    model = XORGateModel()            # 实例化类对象
    model.train(X, Y)               # 执行训练
    # 验证训练结果是否正确
    for xi in X:
        print("验证  输入:{}   模型的前向计算结果:{}".format(xi, model.forward(xi)))
    # 保存模型参数
    np.savez("./params", model.w, model.b)

读取参数并预测:

import numpy as np

if __name__ == "__main__":
    # 加载参数
    r = np.load("./params.npz")
    print("权重", r["arr_0"])
    print("偏置", r["arr_1"])
    # 设置参数到网络结构
    import model
    and_model = model.AndGateModel()
    and_model.w = r["arr_0"]
    and_model.b = r["arr_1"]
    # 预测
    X = [[0,0], [0, 1], [1, 0], [1, 1]]
    for xi in X:
        print("预测  输入:{}  结果:{}".format(xi, and_model.forward(xi)))


有帮助,一键三连哦~~~~~~~~~~~~~~~~~~~~~~~~