感谢李宏毅老师的机器学习,部分内容参考了李宏毅老师的讲义
今天我们来说一说正向传播和反向传播,这里感谢李宏毅老师讲义,让我受益匪浅,但是我这里并不是简单将李老师课程重复,而是加入自己认识,更重要的是我们通过 code 实现这些做法,帮助你更好理解反向传播。
正向传播和反向传播
这里有传播,那么我们说传播什么以及传播方式。这里传播的是导数,也就是找到我们参数对最终的输出的影响程度,对输出影响也就是说明参数对损失函数影响程度。根据求导方式一种正向传播和反向传播。那么我们为什么要做这件事呢?当然即使不了解反向传播也是完全可以做深度学习项目,因为现在流行框架 tensorflow 和 mxnet 已经将这些工作都封装好了,我们不了解这些一样可以设计好的模型。就是我们不知道1+1为什么等于 2 一样也是可以计算 1 + 1 = 2。定义好损失函数我们就是要做到损失函数梯度为零,在线性回归和logistic 回归,我们模型比较简单可能是一个线性方程或者是一个简单复合函数。对于这些模型函数求导很简单。
链式求导
我们需要复习一下求导,
第一种情况
第一种情况是复合函数求导,在复合函数求导方式如上,我这里就不赘述了。
第二种情况
第二种情况是x 通过两个途径来影响最终 z
在神经网络中,最终的损失函数是多个样本损失函数的集合,我们看一看 z 经过一系列运算得到 y 然后 y 用于计算损失函数,今天要看 z 的变化对最后的损失函数有多大影响。
而在在神经网中这件事就变得复杂多了,我们看一看损失函数是每一个样本经过神经网络的损失函数集合这个应该不难理解。
<img src="images/bg_01.png" width="20%"/>
这个求导并不难,每一层线性函数 w 对于 z 求导就是该层神经元的输入。
<img src="images/bg_05.png" width="50%"/>
接下来就是经过激活函数,进入下一层。根据你选择激活函数就很好求导 \frac{\partial l}{\partial z} = \frac{\partial a}{\partial z} \frac{\partial l}{\partial a} = \sigma^{\prime}\frac{\partial l}{\partial a}$$
我们来读解一下下面代码,我们是不是有点数据,如果我们换一个角度来看,假设 和 是已知那么,从右向左看这个不及时,值得注意区别这里 是线性的。
有时候我们这件事换个角度
bg_07.png bg_10.png我们换一个角度来,从输出反向神经网络来看,可以将 和 看成输入,然后这里激活函数是线性放大器。所以这件事我们反过来考虑就简单。
bg_11.png bg_12.png其实这里感觉是有点动态规划意思,我们参数求导这件事有点我们分两步做,先做正向传播计算,然后通过反向传播求出,
最后希望大家关注我们微信公众号
wechat.jpeg
在假期,我们应该合理安排作息时间,玩时间和学习时间适当调节,不然就是没有办法在新学期取得好成绩。这里数据有两个维度[2,9] 分别代表每天玩的时间和学习时间。y 表示在新学期取得成绩。
study.jpgX = np.array(([2,9],[1,5],[3,6]),dtype=float)
y = np.array(([92],[86],[89]),dtype=float)
数据均值化
# 用每列最大值除以每一个值
X = X/np.amax(X,axis=0)
#
y = y/100
print(X)
print(y)
[[0.66666667 1. ]
[0.33333333 0.55555556]
[1. 0.66666667]]
[[0.92]
[0.86]
[0.89]]
nn_diagram_1.png
class NeuralNetwork(object):
def __init__(self):
# 定义参数
self.input_size = 2
self.output_size = 1
self.hidden_size = 3
# weights
self.W1 = np.random.randn(self.input_size,self.hidden_size) #(3,2)
self.W2 = np.random.randn(self.hidden_size,self.output_size) #(3,1)
def feed_forward(self,X):
# 数据在神经网络前向传播
# 隐藏层
self.z = np.dot(X,self.W1)
# 激活函数才用 sigmoid
self.z2 = self.sigmoid(self.z)
# 隐藏层输出(z2) 输出层(3x1) 进行点积,进行 sigmoid 激活函数处理
self.z3 = np.dot(self.z2,self.W2)
output = self.sigmoid(self.z3)
return output
# sigmoid 函数 dreiv 表示是否求导,False 表示前向,True 表示反向求导
def sigmoid(self,s,deriv=False):
if(deriv == True):
return s * (1 - s)
return 1/(1 + np.exp(-s))
def backward(self,X,y,output):
#在神经网中反向传播
# 计算损失函数值
self.output_error = y - output
self.output_delta = self.output_error * self.sigmoid(output,deriv=True)
# 然后 z2_error 来表示隐藏层中的 weights 对 output error 影响程度
self.z2_error = self.output_delta.dot(self.W2.T)
# applying derivative of sigmoid to z2 error
self.z2_delta = self.z2_error * self.sigmoid(self.z2,deriv=True)
# 调节 z2_delta 来调节 (输入层 -> 隐藏层) 的 weights
self.W1 += X.T.dot(self.z2_delta)
# 调节(隐藏层 -> 输出层) weights
self.W2 += self.z2.T.dot(self.output_delta)
def train(self,X,y):
#训练
output = self.feed_forward(X)
self.backward(X,y,output)
算函数损失值,
# 定义神经网络
NN = NeuralNetwork()
for i in range(1000):
NN.train(X,y)
print("Input: " + str(X))
print("Actual Output" + str(y))
print("prediction output: " + str(NN.feed_forward(X)))
print("Loss: " + str(np.mean(np.square(y - NN.feed_forward(X)))) )
Input: [[0.66666667 1. ]
[0.33333333 0.55555556]
[1. 0.66666667]]
Actual Output[[0.92]
[0.86]
[0.89]]
prediction output: [[0.89374538]
[0.88121398]
[0.89661323]]
Loss: 0.00039435753694759285
最后希望大家关注我们微信公众号
wechat.jpeg
网友评论