赞
踩
下图是stanford大学cs231中讲神经网络那一课的PPT中的一页,我这篇文章就是基于他给出的这个框架的复现,并对每行代码的逐一解释。
python code:
- import numpy as np
- import pdb
-
- samples,pixel,hidden,classes=30,4,5,3
- #samples,pixel,neurals of hidden,classes=30,4,5,3
samples:总图片数
pixel:每张图的像素个数
hidden:隐含层中神经元的个数
classes:共分多少类?
输入数据矩阵x的维度=像素列向量的长度 x 样本数/图片数
总共参与训练的图片为30张,每张4个像素。因此,x=4 x 30,也就是说,每张图中的4个像素都被拉伸成一个包含4个元素的列向量,共30张图,有30个这样的列向量。
python code:
- x=np.random.randn(pixel,samples)
- print("x:\n")
- for col in range(samples):
- print(x[:,col])
相应的,30张图都有给出在各个分类下所对应的得分,用于和每次训练的中间结果比较,分3类(猫,狗,船)。这些得分是一个列向量,元素的个数等于种类的个数3,列向量中的内容因和W*x的结果一致,所不同的是这个分数是预先给好的(我在code里面把他叫做y_given)。总共30张图,每张图都有一列得分,共计30列。
y_given=种类个数(保存的是分数) x 样本数
python code:
- y_given=np.random.randn(classes,samples)
- print("y_given:\n")
- for col in range(samples):
- print(y[:,col])
权重矩阵W1的输入是输入数据x,输出是尚未经过非线性计算的一个列向量,其长度应该等于隐藏层h的长度,假设我在这里设计的隐藏层共有10个神经元。x是一个4x30的矩阵,每一列是一张图,W1乘以x中的一列,应该得到维度等于h的一个列向量。假设乘法顺序是W1*col1(x),那么的列数(也就是W1中每行元素的个数)应等于像素个数4。
行数应该等于多少呢?
根据矩阵的运算反推,W1的行数应该等于隐含层神经元的个数10。
W1=神经元的个数 x 像素个数
又因为,x有30列,因此应该有30个这样的运算,得到30个列向量。把这些计算都拼到一起,可通过矩阵的乘法一次性得到计算结果。
先是把输入矩阵x看成是一个个列向量的组合,其中每一列都按照上面的方式运算。
最后得到一个新矩阵Temp1=10x30,完成所有的运算。其中每一列都是W1乘以一张图的结果,共计30列。
如此一来就通过矩阵W1与输入矩阵x的乘法,完成了所有数据的第一次计算,即,Temp1=W1x。
python code:
- w1=np.random.randn(hidden,pixel)
- w1.shape
虽然上面的计算结果还应该再加上一个非线性激活函数才能得到矩阵h=sigmoid(),但这是后面写前向传递函数时的事。这里我们应该继续设计两层神经网络中的第二层权重函数W2。
权重矩阵W2的输入是layer1的结果,是sigmoid(Temp1=W1*x),是一个长度等于神经元个数的列向量,是隐含层的feature。假设乘法顺序是W2*col1(h),那么W2的列数(也就是W2中每行元素的个数)应等于神经元的个数10。
同理,基于W2的输出是这张图在所有分类下的打分。W2的行数应该等于种类的个数3。
Tips:这里我们先考虑Tmep1中的某一个向量,毕竟W1*x是多个列向量的组合。
也就是说,layer1的结果中每一列中的每个元素都是一个神经元,即一个feature。有多少个feature,列向量的长度就是多少。 总共有多少个样本就有多少列这样的神经元。对于W2而言,他的输入是一列列的隐含层feature,因此,他的列数应该等于隐含层feature的数目。这样W2的每一行与一列隐含层相乘的时候正好得到某一类的分数。总共有5类,因此应该有5行。
W2=种类数 x 隐含层feature的数目
python code:
- w2=np.random.randn(classes,hidden)
- w2.shape
我自己的网络是完全参考PPT中的这段代码写的。
基于code中的第一行我们可以先画出计算图(虽然理论上应该是先画图然后才会动手写code),h等于W1*x后接一个sigmoid函数:
这里我们暂时令:
得到第一次运算在计算图中的结果S:
以及第二次运算在计算图中的计算结果H:
然后是code中的第二行 :
令:
code中的最后一行,计算损失函数,损失函数L等于(估计值-实际值)的平方的和。
分两步走,令:
先求差
然后再求差的平方和
python code:
- for t in range(2000):
- #h=sigmoid(w1*x)
- h=1/(1+np.exp(-w1@x))#w1*x followed by sigmoid activation function
- #w2*sigmoid(w1*x)
- y_pred=w2@h
- #choose a good W with Loss function:sum of e*e
- loss=np.square(y_pred-y_given).sum()
- print(t,loss)
逐步计算偏导得到计算图中每处的local gradient。
运用链式法则,根据图中的local gradient写出L关于两个W的偏导:
其中对于W2而言:
其中对于W1而言:
让W沿着梯度相反的方向去调整,每次只调整一点点,用系数η去控制,这个东西叫做学习率。因此,code中有一个很小的变量1e-4。因为如果一次调整的太多容易矫枉过正。
以下是完整的code:
- for t in range(2000):
- #h=sigmoid(w1*x)
- h=1/(1+np.exp(-w1@x))#w1*x followed by sigmoid activation function
- #w2*sigmoid(w1*x)
- y_pred=w2@h
- #choose a good W with Loss function:sum of e*e
- loss=np.square(y_pred-y_given).sum()
- print(t,loss)
-
- grad_y_pred=2*(y_pred-y_given)
- grad_w2=grad_y_pred@(h.T)
- grad_h=(w2.T)@grad_y_pred
- grad_w1=(grad_h*h*(1-h))@x.T
-
- #pdb.set_trace()#在这里设置断点
-
- #leanring rate
- eta=1e-4
- w1-=eta*grad_w1 #w1+(-grad)
- w2-=eta*grad_w2 #w2+(-grad)
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
就本例而言,运行的结果并不理想,误差从最一开始的249,迭代2000次后依然是83.
可见,随着神经元数的增加,损失函数的初始值可能不同,但到了最后都不会收敛到一个很小的值。
初始误差较大,但依然不收敛。
大概迭代500次以后,Loss的误差就很小了。
可见,当像素的个数增加到40个的时候就能达到很好的效果了,继续增加并没有带来更快的收敛速度。
就两层神经网络而言,要想让损失函数的结果快速的趋近于0,似乎应该优先选择增加神经元的个数和每张图的像素个数。而增加图片的个数,也就是样本数,只会增加计算的负担。
(全文完)
--- 作者,松下J27
参考文献(鸣谢):
1,Stanford University CS231n: Deep Learning for Computer Vision
(配图与本文无关)
版权声明:所有的笔记,可能来自很多不同的网站和说明,在此没法一一列出,如有侵权,请告知,立即删除。欢迎大家转载,但是,如果有人引用或者COPY我的文章,必须在你的文章中注明你所使用的图片或者文字来自于我的文章,否则,侵权必究。 ----松下J27
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。