栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

寮傛垨杩愮畻鐨凚P绠楁硶瑙e喅_python寮傛垨杩愮畻?

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

寮傛垨杩愮畻鐨凚P绠楁硶瑙e喅_python寮傛垨杩愮畻?

用python实现BP网络 判别异或 小记

2022.3.28

依据该BP神经网络与Python实现 - -Finley- - 博客园 (cnblogs.com) 博客实现的,但是在一些方面有改动

后来用pytorch又实现了一遍,见添加链接描述

与原博客中不同的:

    我的代码中是直接设置最大迭代次数来终止训练的。(博客中说好的办法是用损失函数作为终止的依据)我使用python的numpy进行矩阵运算,故而没有了博客代码中麻烦的循环。

在原博客中学到的:

    实际应用中我们通常在输入层额外增加一个偏置神经元, 提供一个可控的输入修正;或者为每个隐含层神经元设置一个偏置参数.λ是一个称为学习率的参数,一般在(0,0.1)区间上取值.
遇见问题

    如何用numpy创建矩阵。我的方法是先创建出numpy的array,再用np.mat转换过去(应该还有更好的办法,请赐教)

    **np.multiply() 和 * 和np.dot()有什么区别。**如下:
    对于mat类型来说,*和np.dot()和np.matmul() 一样,都是矩阵乘法(左边的列数要和右边的行数相同),而np.multiply()是对应位置相乘(两矩阵形状要相同)。
    对于array类型来说,np.dot()是矩阵乘法,而*和 np.multiply()是对应位置想乘。

    计算sigmoid的时候是使用对应位置相乘,故而在对sigmoid求导的时候也是对应位置相乘

    偏导相乘的顺序问题,见机器学习中的矩阵向量求导(四) 矩阵向量求导链式法则 - 刘建平Pinard - 博客园 (cnblogs.com) 。
    z = f ( y ) , y = x w ,   ∂ z ∂ x = ∂ z ∂ y w T , ∂ z ∂ w = x T ∂ z ∂ y , ( 对 左 边 求 导 , 它 的 导 数 放 在 右 边 ; 对 右 边 求 导 , 它 的 导 数 放 在 左 边 ) z=f(y),y=xw, frac{partial z}{partial x}=frac{partial z}{partial y}w^T, \ frac{partial z}{partial w}=x^Tfrac{partial z}{partial y}, \(对左边求导,它的导数放在右边;对右边求导,它的导数放在左边) z=f(y),y=xw, ∂x∂z​=∂y∂z​wT,∂w∂z​=xT∂y∂z​,(对左边求导,它的导数放在右边;对右边求导,它的导数放在左边)

    损失函数,以及它的导数
    l o s s = 0.5 ∗ ∑ i = 1 n ( t a r g e t o i − o u t o i ) 2 ∂   t a r g e t o i ∂ o u t o i = 2 ∗ 1 2 ∗ ( t a r g e t o i − o u t o i ) = t a r g e t o i − o u t o i loss=0.5*sum_{i=1}^n (target_{oi}-out_{oi})^2 \frac{partial, target_{oi} }{partial out_{oi}}=2*frac{1}{2}*(target_{oi}-out_{oi})=target_{oi}-out_{oi} loss=0.5∗i=1∑n​(targetoi​−outoi​)2∂outoi​∂targetoi​​=2∗21​∗(targetoi​−outoi​)=targetoi​−outoi​

该结构
    训练集和测试集使用同一个,输入2维数据(但是代码编写时输入3维,因为通常在输入层额外增加一个偏置神经元, 提供一个可控的输入修正),训练目标是能输出正确的异或结果,如输入[1,0],要能输出0;输入[1,1],要能输出1。
代码步骤

    设计隐层层和输出层的权值 w 1 , w 2 w_1,w_2 w1​,w2​ 为(0,1)之间的随机值

    进入前向传播环节,输入数据x0,得到输出y2,以及损失价值loss

    进入后向传播环节,算出偏差loss与y2,y1的导数, 如此易得到loss与w2,w1的导数接着更新w2,w1

    (这时求偏导的时候,偏导相乘要注意顺序)

代码
import numpy as np
from icecream import ic
from matplotlib import  pyplot as plt
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))


def sigmoid_derivative(x):
    #是对应位置相乘
    return np.multiply(x,(1-x))


class BP():
    def __init__(self):
        self.x0=[]    #输入层
        self.x1=[]    #隐含层
        self.x2=[]    #输出层
        self.y1=[]    #y1=x0*w1
        self.y2=[]    #y2=x1w1
        self.w1=[]    #隐层层的权值
        self.w2=[]    #输出层的权值
        self.loss=[]     #损失函数
        self.label=[]   #真实值
        self.y2_gradient=[]     #e对y2的导数
        self.y1_gradient=[]     #e对y2的倒数
        self.loss_delta=[]      #损失函数对误差值求导

    def setup(self,input_n,hidden_n,ouput_n):
        """
        # 对bp初始化,设置各层维度,以及初始随机权值
        """
        input_n+=1  #通常在输入层额外增加一个偏置神经元, 提供一个可控的输入修正
        self.x0=np.mat(np.ones([1,input_n]))  #1*3
        self.x1=np.mat(np.ones([1,hidden_n]))   #1*5
        self.x2=np.mat(np.ones([1,ouput_n]))    #1*1

        self.w1=np.mat(np.random.rand(input_n,hidden_n))    #3*5
        self.w2=np.mat(np.random.rand(hidden_n,ouput_n))    #5*1

        self.y2_gradient=np.mat(np.ones([1,ouput_n]))
        self.y1_gradient=np.mat(np.ones([1,hidden_n]))

    def forward(self,inputx,label):
        """
        前向传播
        """
        self.x0[0, 0:2] = inputx    #输入层是1*3矩阵,但实际输入是1*2
        self.label=np.mat(label)

        self.x1=sigmoid(self.x0*self.w1)    #省略了y1=x0*w1
        self.x2=sigmoid(self.x1*self.w2)

        #计算损失值
        e=self.label-self.x2
        self.loss = (0.5*sum(e**2) /e.size).getA()[0]
        self.loss_delta=e

    def backward(self,learn):
        """
        后向传播
        """

        #注意乘法顺序,求梯度
        self.y2_gradient=np.multiply(sigmoid_derivative(self.x2),self.loss_delta)
        self.y1_gradient=np.multiply(self.y2_gradient*self.w2.T,sigmoid_derivative(self.x1))

        # 更新权值
        self.w2+=self.x1.T*self.y2_gradient*learn
        self.w1+=self.x0.T*self.y1_gradient*learn

def make_pic():
	#绘制图像
    train_epoch = 500  #迭代总次数
    show_epoch = 100

    def optimizer(learn):
        bp.setup(input_n=2,hidden_n=5,ouput_n=1)
        epoch = []
        loss = []
        for k in range(train_epoch):
            for i in range(len(cases)):
                bp.forward(inputx=cases[i], label=labels[i])
                bp.backward(learn)

            #每隔show_epoch个跌打数的时候,看看损失值
            if k % show_epoch == 0:
                l=0
                for i in range(len(cases)):
                    bp.forward(inputx=cases[i], label=labels[i])
                    l+=bp.loss
                epoch.append(k)
                loss.append(l/4)

        return epoch, loss

    epoch1, loss1 = optimizer(learn=0.5)
    epoch2, loss2 = optimizer(learn=0.1)
    epoch3, loss3 = optimizer(learn=0.05)

    plt.figure()
    plt.subplot(1, 2, 1)
    plt.plot(range(0, len(epoch1) * show_epoch, show_epoch), loss1, label='learn=0.5')
    plt.plot(range(0, len(epoch2) * show_epoch, show_epoch), loss2, label='learn=0.1')
    plt.plot(range(0, len(epoch3) * show_epoch, show_epoch), loss3, label='learn=0.05')

    # 显示标签,如果不加这句,即使在plot中加了label='一些数字'的参数,最终还是不会显示标签
    plt.legend(loc="upper right")
    plt.show()

if __name__=='__main__':
    #数据集
    cases = [
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1],
    ]
    labels = [[0], [1], [1], [0]]


    bp=BP()
    bp.setup(2,5,1)     #初始化

    #迭代训练
    for _ in range(100):
        for i in range(len(cases)):
            bp.forward(inputx=cases[i],label=labels[i])
            bp.backward(learn=0.1)


    #画图,看不同学习率,迭代次数不同时的误差率
    make_pic()


代码图像

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/786599.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号