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矩阵变化的各个过程,比较方便清晰

  1. clc,clear,close all
  2. %% 训练样本
  3. X=[1,1,0,0;
  4. 0,0,1,1;
  5. 1,0,0,1];
  6. Y=[1,0; %实际值
  7. 0,1;
  8. 1,0];
  9. [Inx,Iny]=size(X);%输入矩阵的维数
  10. [Outx,Outy]=size(Y);%输出矩阵的维数
  11. Hid_wide=3;%隐藏层节点维度
  12. D=100; %损失初始值
  13. a=0.1;%学习率
  14. times=1;
  15. res=0.001;%容差
  16. Loss=inf; %预测值-实际值
  17. %% 循环训练权重
  18. while D>res %大于容差则一直循环
  19. % disp(times);
  20. disp(D);
  21. %第一层
  22. if times==1 %第一次随机生成
  23. W1=rand(Iny,Hid_wide);%第一次循环随机生成权重W1
  24. end
  25. S1=X*W1;
  26. %S1节点经过激活函数sigmod
  27. Z1=sigmoid(S1);
  28. %第二层
  29. if times==1
  30. W2=rand(Hid_wide,Outx);%第一次循环随机生成权重W2
  31. end
  32. S2=Z1*W2;
  33. %S2节点经过激活函数
  34. Z2=sigmoid(S2);
  35. %输出层
  36. if times==1
  37. W3=rand(Outx,Outy);
  38. end
  39. Q=Z2*W3;
  40. %输出层节点经过激活函数
  41. y=sigmoid(Q); %预测值
  42. %计算损失值
  43. sum=0;
  44. for m=1:Outx
  45. for n=1:Outy
  46. sum=sum+(y(m,n)-Y(m,n))^2;
  47. end
  48. end
  49. if D>sum
  50. D=sum;
  51. end
  52. %% 反向传递过程
  53. %lossW3求导
  54. g3=(y-Y)'*Z2;
  55. %loss对W2求导
  56. g2=(((y-Y)*W3').*(sigmoid(S2).*(1-sigmoid(S2))))'*Z1;
  57. %loss对W1求导
  58. g1=((((y-Y)*W3').*(sigmoid(S2).*(1-sigmoid(S2))))*W2'.*(sigmoid(S1).*(1-sigmoid(S1))))'*X;
  59. %% 更新权重
  60. W1=W1-a*g1';
  61. W2=W2-a*g2';
  62. W3=W3-a*g3';
  63. Loss(times)=D; %记录每次的损失值
  64. times=times+1;
  65. end
  66. plot(Loss);%打印损失降低过程
  67. disp('调整后的W1');
  68. disp(W1);
  69. disp('调整后的W2');
  70. disp(W2);
  71. disp('调整后的W3');
  72. disp(W3);
  73. disp('真实Y');
  74. disp(Y);
  75. disp('训练的Y');
  76. disp(y);
  77. %% 激活函数sigmod
  78. function result=sigmoid(A)
  79. result=inf;
  80. [A1,A2]=size(A);
  81. for i=1:A1
  82. for j=1:A2
  83. result(i,j)=1/(1+exp(-A(i,j)));
  84. end
  85. end
  86. end

Python源码

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

  1. """
  2. 作者:猪脚三父
  3. 日期:2022年02月01日
  4. """import numpy as np
  5. import matplotlib.pyplot as plt # 画图用的包defsigmoid(A):# 激活函数sigmod
  6. A1 = np.size(A,0)
  7. A2 = np.size(A,1)
  8. result =[[0.0for col inrange(A2)]for row inrange(A1)]# 初始化一个A1*A2维度的列表
  9. result = np.array(result)# 转为数组for i inrange(A1):for j inrange(A2):
  10. result[i, j]=1/(1+ np.exp(-1* A[i, j]))return result
  11. if __name__ =='__main__':
  12. X =[[1,1,0,0],[0,0,1,1],[1,0,0,1]]
  13. Y=[[1,0],[0,1],[1,0]]
  14. X = np.array(X)# 转成array格式
  15. Y = np.array(Y)# 转成array格式
  16. Inx = np.size(X,0)# 输入矩阵的维数
  17. Iny = np.size(X,1)# 输入矩阵的维数
  18. Outx = np.size(Y,0)# 输出矩阵的维数
  19. Outy = np.size(Y,1)# 输出矩阵的维数
  20. Hid_wide =3# 隐藏层节点维度
  21. D =100# 损失初始值
  22. a =0.1# 学习率
  23. times =1
  24. res =0.001# 容差
  25. Loss =[]while D > res:# 大于容差则一直循环# 第一层if times ==1:# 第一次随机生成
  26. W1 = np.random.random((Iny, Hid_wide))# 第一次循环随机生成权重W1
  27. S1 = np.dot(X, W1)
  28. Z1 = sigmoid(S1)# S1节点经过激活函数sigmod# 第二层if times ==1:# 第一次随机生成
  29. W2 = np.random.random((Hid_wide, Outx))# 第一次循环随机生成权重W2
  30. S2 = np.dot(Z1, W2)
  31. Z2 = sigmoid(S2)# S2节点经过激活函数sigmodif times ==1:# 第一次随机生成
  32. W3 = np.random.random((Outx, Outy))# 第一次循环随机生成权重W3
  33. Q = np.dot(Z2, W3)# 输出层节点经过激活函数
  34. y = sigmoid(Q)# 预测值# 计算损失值sum=0for m inrange(Outx):for n inrange(Outy):sum=sum+(y[m, n]- Y[m, n])**2if D >sum:
  35. D =sum# 反向传递过程# loss对W3求导
  36. g3 = np.dot(np.transpose(y - Y), Z2)# lossW2求导
  37. tmp2 = np.dot(y - Y, np.transpose(W3))
  38. tmp3 = np.multiply(sigmoid(S2),1- sigmoid(S2))
  39. tmp1 = np.multiply(tmp2, tmp3)
  40. g2 = np.dot(np.transpose(tmp1), Z1)# lossW1求导
  41. tmp6 = np.multiply(sigmoid(S1),1- sigmoid(S1))
  42. tmp5 = np.dot(tmp1, np.transpose(W2))
  43. tmp4 = np.multiply(tmp5, tmp6)
  44. g1 = np.dot(np.transpose(tmp4), X)# 更新权重
  45. W1 = W1 - a * np.transpose(g1)
  46. W2 = W2 - a * np.transpose(g2)
  47. W3 = W3 - a * np.transpose(g3)
  48. Loss.append(D)# 记录每次的损失值
  49. times = times +1# 打印数据 & 画图print('调整后的W1')print(W1)print('调整后的W2')print(W2)print('调整后的W3')print(W3)print('真实Y值')print(Y)print('训练的Y值')print(y)
  50. t =range(len(Loss))
  51. plt.figure(dpi=100, figsize=(12,6))# 指定图像分辨率和画板大小
  52. plt.fill_between(t, Loss, color="skyblue", alpha=0.3)
  53. plt.plot(t, Loss, color="blue")# 多勾勒一层蓝边
  54. plt.xlabel('迭代次数')# x轴上的名字
  55. plt.ylabel('Loss')# y轴上的名字
  56. plt.rcParams['font.sans-serif']=['SimHei']# 不加不能显示中文
  57. plt.rcParams['axes.unicode_minus']=False# 不加不能显示中文
  58. plt.show()# 打印图像

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

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

还没有评论