0


b站的用纸笔训练神经网络【matlab与python实现】

b站的用纸笔训练神经网络【matlab与python实现】

我的工作

之前在b站上看到小蛮大佬做的一期用纸笔训练神经网络的视频,关于正向传递和反向传播这一块受益匪浅,但是视频中也存在一些公式以及绘图错误的地方,所以尝试复现了一些代码来更清晰的展现整个过程,目前只提供matlab与python版本的代码(PS:该博客只对视频中提到的内容做一些浅显的梳理,便于初学者理解)。

视频链接:https://www.bilibili.com/video/BV1R64y187yt/

基本思路

神经网络正向传递和反向传播的过程可以看成下图所示
正向传递:左边3×4维的矩阵经过一个黑盒后会得到右边3×2维的矩阵

  • 一开始的时候,我们经过黑盒得到的3×2维的矩阵不一定如我们所愿是实际的目标结果Y,我们需要借助已知的目标结果Y对黑盒进行调整
  • 输入3×4维矩阵X,经过黑盒生成3×2维矩阵y(这里是y,不是目标矩阵Y),这样在不断的更新迭代后,黑盒表现的很好了
  • 那么当我们再次输入3×4维矩阵X,输出的预测矩阵y就会和目标矩阵Y相差无几,那么对黑盒进行调整的过程,就是反向传播过程

反向传播:右边3×2维的矩阵对黑盒进行调整的过程
在这里插入图片描述

黑盒是什么

黑盒包括经过的各种神经元,通过一些列矩阵相乘、激活函数等操作,最终由softmax得到输出结果。在这里插入图片描述
这里引用视频中的一张图,红色方框内可以看成黑盒的部分
其中的紫色线段、蓝色线段、黑色线段,分别对应三个权重w1、w2、w3(这三个权重初始时是随机生成的)。比如紫色线段(对应w1),左边是四个输入(x1、x2、x3、x4),右边是三个神经元输出(s1、s2、s3),所以w1的维度就是4×3,以此类推。
所以神经网络的正向传递可以看成是一系列的矩阵相乘的过程
在这里插入图片描述
前面提到,三个权重在一开始是随机生成的,那么反向传递的调整过程,就是对这三个权重进行调整,利用预测的y与实际的Y的差值,即Loss,通过Loss分别对w进行求导(视频里用的是链式法则来解决),得到w的调整量g(w1、w2、w3对应g1、g2、g3),反向传递完后,原来的权重w减去对应的g(当然这个g一般会乘上学习率以及转置)即可得到更新后的w。
总结来说就是反向传播就是更新w的过程。

g1、g2、g3用链式法则求导的公式我会在代码中给出,就用matlab的代码来说一下吧,需要注意 * 和 .* 的区别,*是矩阵乘法,需要前一个数组的列与后一个数组的行相等,而 .*是需要两个矩阵维度完全相等的,是矩阵对应位置相乘
在这里插入图片描述

MATLAB源码

建议用matlab来debug矩阵变化的各个过程,比较方便清晰

clc,clear,close all 
%% 训练样本
X=[1,1,0,0;
    0,0,1,1;
    1,0,0,1];
Y=[1,0; %实际值
    0,1;
    1,0];
[Inx,Iny]=size(X);%输入矩阵的维数
[Outx,Outy]=size(Y);%输出矩阵的维数
Hid_wide=3;%隐藏层节点维度
D=100; %损失初始值
a=0.1;%学习率
times=1;
res=0.001;%容差
Loss=inf; %预测值-实际值

%% 循环训练权重
while D>res %大于容差则一直循环
%     disp(times);
    disp(D);
    %第一层
    if times==1 %第一次随机生成
    W1=rand(Iny,Hid_wide);%第一次循环随机生成权重W1
    end
    S1=X*W1;
    %S1节点经过激活函数sigmod
    Z1=sigmoid(S1);
    
    %第二层
    if times==1
    W2=rand(Hid_wide,Outx);%第一次循环随机生成权重W2
    end
    S2=Z1*W2;
    %S2节点经过激活函数
    Z2=sigmoid(S2);
    
    %输出层
    if times==1
    W3=rand(Outx,Outy);
    end
    Q=Z2*W3;
    %输出层节点经过激活函数
    y=sigmoid(Q); %预测值
    
    %计算损失值
    sum=0;
    for m=1:Outx
        for n=1:Outy
            sum=sum+(y(m,n)-Y(m,n))^2;
        end
    end
    if D>sum
        D=sum;
    end
    
    
    %% 反向传递过程
    %loss对W3求导
    g3=(y-Y)'*Z2;
    
    %loss对W2求导
    g2=(((y-Y)*W3').*(sigmoid(S2).*(1-sigmoid(S2))))'*Z1;
    
    %loss对W1求导
    g1=((((y-Y)*W3').*(sigmoid(S2).*(1-sigmoid(S2))))*W2'.*(sigmoid(S1).*(1-sigmoid(S1))))'*X;
    
    %% 更新权重
    W1=W1-a*g1';
    W2=W2-a*g2';
    W3=W3-a*g3';
    Loss(times)=D; %记录每次的损失值

    times=times+1; 
    
end
plot(Loss);%打印损失降低过程

disp('调整后的W1');
disp(W1);
disp('调整后的W2');
disp(W2);
disp('调整后的W3');
disp(W3);
disp('真实Y值');
disp(Y);
disp('训练的Y值');
disp(y);

%% 激活函数sigmod
function result=sigmoid(A) 
result=inf;
   [A1,A2]=size(A);
   for i=1:A1
        for j=1:A2
            result(i,j)=1/(1+exp(-A(i,j)));
        end
    end
end

Python源码

刚入门python,原谅我大量不熟练的操作,数组计算部分相比matlab确实太累了

"""
作者:猪脚三父
日期:2022年02月01日
"""import numpy as np
import matplotlib.pyplot as plt  # 画图用的包defsigmoid(A):# 激活函数sigmod
    A1 = np.size(A,0)
    A2 = np.size(A,1)
    result =[[0.0for col inrange(A2)]for row inrange(A1)]# 初始化一个A1*A2维度的列表
    result = np.array(result)# 转为数组for i inrange(A1):for j inrange(A2):
            result[i, j]=1/(1+ np.exp(-1* A[i, j]))return result

if __name__ =='__main__':
    X =[[1,1,0,0],[0,0,1,1],[1,0,0,1]]
    Y=[[1,0],[0,1],[1,0]]
    X = np.array(X)# 转成array格式
    Y = np.array(Y)# 转成array格式
    Inx = np.size(X,0)# 输入矩阵的维数
    Iny = np.size(X,1)# 输入矩阵的维数
    Outx = np.size(Y,0)# 输出矩阵的维数
    Outy = np.size(Y,1)# 输出矩阵的维数
    Hid_wide =3# 隐藏层节点维度
    D =100# 损失初始值
    a =0.1# 学习率
    times =1
    res =0.001# 容差
    Loss =[]while D > res:# 大于容差则一直循环# 第一层if times ==1:# 第一次随机生成
            W1 = np.random.random((Iny, Hid_wide))# 第一次循环随机生成权重W1
        S1 = np.dot(X, W1)
        Z1 = sigmoid(S1)# S1节点经过激活函数sigmod# 第二层if times ==1:# 第一次随机生成
            W2 = np.random.random((Hid_wide, Outx))# 第一次循环随机生成权重W2
        S2 = np.dot(Z1, W2)
        Z2 = sigmoid(S2)# S2节点经过激活函数sigmodif times ==1:# 第一次随机生成
            W3 = np.random.random((Outx, Outy))# 第一次循环随机生成权重W3
        Q = np.dot(Z2, W3)# 输出层节点经过激活函数
        y = sigmoid(Q)# 预测值# 计算损失值sum=0for m inrange(Outx):for n inrange(Outy):sum=sum+(y[m, n]- Y[m, n])**2if D >sum:
            D =sum# 反向传递过程# loss对W3求导
        g3 = np.dot(np.transpose(y - Y), Z2)# loss对W2求导
        tmp2 = np.dot(y - Y, np.transpose(W3))
        tmp3 = np.multiply(sigmoid(S2),1- sigmoid(S2))
        tmp1 = np.multiply(tmp2, tmp3)
        g2 = np.dot(np.transpose(tmp1), Z1)# loss对W1求导
        tmp6 = np.multiply(sigmoid(S1),1- sigmoid(S1))
        tmp5 = np.dot(tmp1, np.transpose(W2))
        tmp4 = np.multiply(tmp5, tmp6)
        g1 = np.dot(np.transpose(tmp4), X)# 更新权重
        W1 = W1 - a * np.transpose(g1)
        W2 = W2 - a * np.transpose(g2)
        W3 = W3 - a * np.transpose(g3)
        Loss.append(D)# 记录每次的损失值

        times = times +1# 打印数据 & 画图print('调整后的W1')print(W1)print('调整后的W2')print(W2)print('调整后的W3')print(W3)print('真实Y值')print(Y)print('训练的Y值')print(y)

    t =range(len(Loss))
    plt.figure(dpi=100, figsize=(12,6))# 指定图像分辨率和画板大小
    plt.fill_between(t, Loss, color="skyblue", alpha=0.3)
    plt.plot(t, Loss, color="blue")# 多勾勒一层蓝边
    plt.xlabel('迭代次数')# x轴上的名字
    plt.ylabel('Loss')# y轴上的名字
    plt.rcParams['font.sans-serif']=['SimHei']# 不加不能显示中文
    plt.rcParams['axes.unicode_minus']=False# 不加不能显示中文
    plt.show()# 打印图像

本文转载自: https://blog.csdn.net/qq_43604992/article/details/122765818
版权归原作者 猪脚三父 所有, 如有侵权,请联系我们删除。

“b站的用纸笔训练神经网络【matlab与python实现】”的评论:

还没有评论