BP神经网络(二)

385 阅读3分钟

一、增加参数

  还是以上次的计算过程为例。(本部分在上次的基础上扩展为函数,实现快速调用。后续将BP网络封装成“类”,并举实际例子探索BP神经网络的应用。实际上这也是我当时学习BP神经网络的过程,先自己写一段代码,然后逐渐扩展,最终形成一个比较实用的代码)

  已知一个BP神经网络,学习率为0.9,当前的训练样本为x={1,0,1},预期的分类标号为1,当前网络的权重和阈值如表,描述一次迭代过程。

\omega_{14} \omega_{15} \omega_{24} \omega_{25} \omega_{34} \omega_{35} \omega_{46} \omega_{56} b_4 b_5 b_6
0.2 -0.3 0.4 0.1 -0.5 0.2 -0.3 -0.2 -0.4 0.2 0.1
(为方便理解,权值和阈值使用统一字母表示)

  前面讲述了单次的迭代过程,本次增加参数,实现循环迭代,达到满意效果后停止循环。

#参数设置
def parameter():
    net_max_time=2000  #最大迭代次数
    net_goal=0.00001  #目标误差
    net_lr=0.9       #学习率
    return net_max_time,net_goal,net_lr

  激活函数仍采用sigmoid函数。

sigmoid(x)=\frac{1}{1+e^{-x}}
#sigmoid函数
def sigmoid(num1,num2):
    return 1/(1+np.exp(num2-num1))  #正向传播所得数值与神经元阈值有相减操作

  输入数据仍采用简易的矩阵表示,实际上在用于实例中并不方便,封装为类时会采用稍微复杂的矩阵表示,本程序只用于方便理解。

#输入
x=np.array([[1,0,1]]).T
#目的结果
y=np.array([[1]]).T
#初始权值和阈值
w1=np.array([[0.2,-0.3],
             [0.4,0.1],
             [-0.5,0.2]])
w2=np.array([[-0.3,-0.2]])
b=np.array([[-0.4,0.2,0.1]]).T

  均方误差

E_k=\frac{1}{2}\sum_{j=1}^l(\hat{y}_j^k-y_j^k)^2

  均方误差的作用已经体现在计算梯度的过程中。而本代码中是为了直观地观察到均方误差的递减过程。

#均方误差
def error(v, y):
    E=(v[-1]-y)**2/2 #本代码中,整个网络输出结果只有一个,因此可以直接选取输出v中的最后一个值
    return E

  接下来进入BP的迭代过程。

  1.信号的正向传播

#信号正向传播
def Forward_Calculation(x,w1,w2,b):
    m1,n1=np.shape(w1)
    v=[]
    for i in range(n1):
        v.append(sigmoid(w1[:,i].dot(x),b[i]))  #输入层至隐含层
    v.append(sigmoid(w2.dot(v),b[-1]))  #隐含层至输出层

    return list(v)

  2.反向计算局部梯度

#反向计算局部梯度
def Back_Calculation(y,w1,w2,b,v):
    m2,n2=np.shape(w2)
    gra=[]
    gra.append(v[-1]*(1-v[-1])*(y-v[-1]))  #输出层至隐含层
    for j in range(n2)[::-1]:
        gra.append(v[j]*(1-v[j])*gra[0]*w2[0][j])
    gra.reverse()  #矩阵反转
    return list(gra)

  3.更新权值和阈值

#更新权值和阈值
def Update_Values(x,w1,w2,b,v,gra,net_lr):
    m1,n1=np.shape(w1)
    m2,n2=np.shape(w2)
    m3,n3=np.shape(b)
    #更新权值
    for i in range(m2):
        for j in range(n2):
            w2[i][j]=w2[i][j]+net_lr*v[j]*gra[-1]
    for i in range(m1):
        for j in range(n1):
            w1[i][j]=w1[i][j]+net_lr*x[i]*gra[j]
    #更新阈值
    b[-1]=b[-1]-net_lr*gra[-1]
    for i in range(m3-1):
        b[i][0]=b[i][0]-net_lr*gra[i]
    return w1,w2,b

  主要函数都已写出,开始写训练函数。训练函数中包含整个迭代过程、计算均方误差、判断均方误差是否低于目标误差。

#训练
def train(x,y,w1,w2,b,net_max_time,net_goal,net_lr):
    m,n=np.shape(x)
    error_value_list=[]
    for time in range(net_max_time):    #迭代次数
        error_number=[]
        for i in range(n):  #数据集中的样本
            v=Forward_Calculation(x[:,i],w1,w2,b) #正向传播
            gra=Back_Calculation(y[:,i],w1,w2,b,v)        #反向计算梯度
            w1,w2,b=Update_Values(x[:,i],w1,w2,b,v,gra,net_lr) #更新权值和阈值
        #计算均方误差
        for j in range(n):
            E=error(v, y)
            error_number.append(E)
        error_value=np.sum(error_number)/(n+1)
        if (time+1)%10==0:  #提醒作用
            print("第{}轮训练".format(time+1))
            print(error_value)
            
        error_value_list.append(error_value)
        if error_value<=net_goal:  #当误差低于目标值时退出训练
            break
    draw_error(list(error_value_list)) #绘制误差曲线
    print("共进行了{}次迭代".format(time+1))

  训练函数中调用了“绘制误差曲线”的函数,绘制误差曲线需要根据均方误差列表error_value_list使用matplotlib包绘制。

#绘制误差曲线 
def draw_error(error_value):  
    plt.xlabel("number of times")
    plt.ylabel("error")
    plt.plot(error_value, 'r-')
    plt.show()

绘制的误差曲线如下:

  由上图可看出,训练至100次左右时,误差不再发生明显变化。

  训练完成后,神经网络“学”到的东西,蕴涵在权值与阈值中。

  训练样本为x={1,0,1},输出值为0.98843845,非常接近分类标号1。

后续:BP神经网络(三)