- MLP应用示例
- 首先看一个动画展示
- 上面动画中黄色的点代表的是我们想识别的点,墨蓝色的点代表是干扰的点。
- 那为什么我们要识别黄色的点?举个实际的例子,有一批用户,有部分用户是价格敏感的,有部分用户是价格不敏感。那么从商家的角度来考虑,就要找出这部分价格敏感的用户,然后给他们做补贴。再比如,有一批邮件,其中有部分是垃圾邮件,其他都是正常的邮件。所以我们要找出其中的垃圾邮件,将他们过滤掉,节省用户查看邮件的时间,降低邮件中毒的风险。按照我们上面的示例,黄色的点就代表价格敏感用户(或垃圾邮件),墨蓝色的点就代表其他用户(或正常的邮件)。
- 黄色的点被红色覆盖就表示我们找到了这个黄色的点。
- 墨蓝色的干扰点也会被红色覆盖,这个就代表算法找错了。所以算法每次迭代的目的就是为了尽可能多的找到黄色的点,尽可能少的找到墨蓝色的点。
- 黄色或者墨蓝色的点每被覆盖一次,就表示算法进行了一次迭代学习。经过一次学习如果找到一批黄色的点,就将这一批点覆盖成红色。所以每一次覆盖黄色点的数量是不固定的,也可能没有。
文章来源:https://www.toymoban.com/news/detail-414743.html
- 从上面的动画可以看到:
- MLP是一个可以做分类的算法:判断一个输入是黄色点,还是墨蓝色点。
- MLP需要先有一个标注的数据:需要先把所有的点标记为黄色的点和墨蓝色的点。基于标注数据训练的模型的过程也叫有监督学习,模型称为有监督学习模型。如果模型无需标注数据训练,则称为无监督学习模型,比如聚类模型。
- MLP优化是一个不断迭代的过程:经过一轮一轮的迭代,MLP预测到越来越多的黄色的点。
- 首先看一个动画展示
- MLP前向传播计算过程展示
- 本文中的MLP网络结构如下图所示:
- 上图中的各个参数说明如下:
- 假设我们有一个样本数据x=(6.35,9.33),对应的目标值是0,即y=0。在黄蓝点的图中位置如下图红框所示:
- input0,input1:代表输入的2个值。对于样本[x=(6.35,9.33),y=0],input0=6.35,input1=9.33
- hide00,hide01,……, hide07:代表隐含层的8个值。这个是我们随意设置的,也可以设为18或者4.
- output0:代表输出层的1个值。对于样本[x=(6.35,9.33),y=0],output0对应的真实值为0
- 每一个有向箭头表示:箭头的左边作为箭头右边的一个输入,输入的量为箭头左边的值乘以连接的权重,即上图冲箭头上方标记的值。对于样本[x=(6.35,9.33),y=0],以下图红色箭头标记的连接为例:
hide00 = input0*0.2 = 6.35*0.2 = 1.27
- 假设我们有一个样本数据x=(6.35,9.33),对应的目标值是0,即y=0。在黄蓝点的图中位置如下图红框所示:
- 一个样本,由输入得到预测输出的完整计算过程如下(前向传播):
- 由输入得到隐含层的值,如下图所示,每一个隐含层的单元都与输入层的两个单元相连接,以样本[x=(6.35,9.33),y=0]为例,
- hide00的输入量:
hide00_in =input0*0.2+input1*0.2=6.35*0.2+9.33*0.2=27+1.87=3.14。
- 由hide00_in的计算公式可以知道,hide00_in只是输入值的一个线性变换,而我们的神经网络模型是可以拟合线性和非线性的,所以这里就需要加一个非线性的变换,叫做“激活函数”。MLP的激活函数为sigmoid,公式为:
sigmoidx=1.01.0+e-x
所以,hide00 = sigmoid(hide00_in)= 1.01.0+e-hide00_in=1.01.0+e-3.14=0.96 - hide01,hide02,……, hide 07,都是一样的计算方法。
- hide00的输入量:
- 由隐含层计算预测输出值:8个隐含层单元都与输出层有连接,以样本[x=(6.35,9.33),y=0]为例,hide00=hide01=hide02=……=hide07=0.96。
- output0的输入量:
output0_in=hide00*0.2+……+hide07*0.2=hide00*0.2*8=3.14*8=25.12
- 和隐含层的计算一样,要加一个激活函数,将线性变换转换为非线性变换,用的激活函数也是sigmoid。所以:
output0= sigmoid(output0_in)= 1.01.0+e-output0_in=1.01.0+e-25.12=1.00 - 由sigmoid 函数的性质可以知道,其最终输出的值是一个0~1之间的连续值。但是我们最终的输出是一个分类,以这里的数据为例,最终输出的是一个二分类:是黄色的点,还是墨蓝色的点。那怎么由一个连续的值转换成一个二分类呢?
- 在了解怎么转换之前,先理解sigmoid函数代表什么含义。在判断一个对象的分类时,很难做到 100% 的把握,所以需要算分类的概率,而sigmoid的值就是这个概率。以本文的例子为例,sigmoid的值就代表所判断的点是黄色点的概率:
- 0.5 是分界线,无法判断属于哪一个分类。
- 0.6以上,基本可以认为属于黄色点分类,虽然把握不大。
- 0.8以上,基本可以认为属于黄色点分类,把握也很大。
- 所以由sigmoid值转换成二分类,就是我们自己设定一个阈值,比如0.6,如果sigmoid值大于这个阈值,就标记为黄色点(label为1),否则标记为墨蓝色的点(label为0)。
- 在了解怎么转换之前,先理解sigmoid函数代表什么含义。在判断一个对象的分类时,很难做到 100% 的把握,所以需要算分类的概率,而sigmoid的值就是这个概率。以本文的例子为例,sigmoid的值就代表所判断的点是黄色点的概率:
- output0的输入量:
- 由输入得到隐含层的值,如下图所示,每一个隐含层的单元都与输入层的两个单元相连接,以样本[x=(6.35,9.33),y=0]为例,
- 至此,有一个输入值,计算得到最终预测输出值,以及怎么由预测输出值得到最终的label的计算过程都已经完成了。
- 本文中的MLP网络结构如下图所示:
- MLP反向传播计算过程展示:
- 反向传播是相对于正向传播而言的。由输入层经过一系列计算得到预测输出的过程我们定义为正向传播,也有的地方叫前向传播。相反的,由输出层经过一系列计算,更新输出层和隐含层,以及隐含层和输入层连接权重的过程,称之为反向传播。所以:反向传播,就是更新权重的过程。
- 根据预测输出值output0和真实label之间的差值调整隐含层到输出层的权重:
- 如上图所示,输出层和隐含层的连接权重的更新公式为:
wi新=wi+y1*1-y1*y-y1*eta*hi- wi新:是更新后的权重。
- wi:是更新前的权重。在上面的例子中就是:0.2。
-
y1:是预测输出值。在上面的例子中是:
output0= sigmoid(output0_in)= 1.01.0+e-output0_in=1.01.0+e-25.12=1.00 - y:是样本的label值。以上面的样本[x=(6.35,9.33),y=0]为例,是0.
- eta:是我们定义的一个值。学名叫“学习速率”,作用是用来控制收敛的速度:学习率设置太小,需要花费过多的时间来收;学习率设置较大,在最小值附近震荡却无法收敛到最小值。
-
hi:隐含层第i个单元的值。在上面的例子中就是:hide01,hide02,……, hide 07。其中:
hide00 = sigmoid(hide00_in)= 1.01.0+e-hide00_in=1.01.0+e-3.14=0.96 - 以hide00 和 output0 之间的连接权重为例,来按照上面的更新公式计算一下(这里我们设定eta为0.8):
w0新=w0+y1*1-y1*y-y1*eta*h00=0.2+1.0*1-1.0*0-1.0*0.8*0.96=0.2 - 其他隐含层单元格到输出层的权重更新以此类推。
- 至于为什么是这样,我们这里先不说,后面将算法原理的时候会详细讲。
- 如上图所示,输出层和隐含层的连接权重的更新公式为:
- 调整输入层到隐含层的权重:
- 如上图所示,隐含层和输入层的连接权重的更新公式为:
- wi:是更新前的权重。在上面的例子中就是:0.2。
-
h[i]:隐含层第i变量的值。在上面的例子中:
hide00 = sigmoid(hide00_in)= 1.01.0+e-hide00_in=1.01.0+e-14=0.96. - weight_h_y[i]:隐含层第i单元格到输出层的权重。在上面的例子中就是:0.2。
- delta_y_h=y1*(1-y1)*(y-y1):y1 和 y值含义如上。
- eta:如上为“学习速率”。
- xi:输入层第i个单元格的值。以上面的样本[x=(6.35,9.33),y=0]为例,x0=6.35,x1=9.33
- 以input0 和 hide00 之间的连接权重为例,来按照上面的更新公式计算一下(这里我们设定eta为0.8):
w0新=w0+h0*1-h0*weight_h_y[0]*delta_y_h*eta*x0=0.2+0.96*1-0.96*0.2*1.0*1-1.0*0-1.0*0.8*6.35=0.2 - 其他输入层单元格和各个隐含层单元格之间的权重更新以此类推。
- 如上图所示,隐含层和输入层的连接权重的更新公式为:
- MLP代码实现
- 工欲善其事必先利其器,要做算法学习,首先要有数据,所以第一步我们首先构造实验用的样本。
- 先定义三个数组,分别用来存放:所有的数据、label为1的数据和label为0的数据。rawdata数组中的一行代表一条样本记录,python中的数组定义如下所示,因为样本的数量现在还不确定,所以就先定义一个动态的数组,在生成数据的时候动态添加。
rawdata = [] #所有样本数据集合
positivedata = [] #label为1的样本集合
negativedata = [] #label为0的样本集合 - 样本数据的构造方式为:我们先随机生成一些二维的数据,然后用三个圆去圈其中的一些点,被圈中的点就设置label为1,没有被圈中的就设置label为0。所以先随机设定三个圆的圆心,每个圆心是一个二维数组。设定圆心的时候,有一个要考虑,就是圆心和圆心之间的距离,需要大于两个圆的半径之和。
r1 = [1.1, 1.1] #第一个圆的圆心
r2 = [4.3, 4.3] #第二个圆的圆心
r3 = [8.1, 8.3] #第三个圆的圆心
半径在筛选的时候直接硬编码了,所以就不在这里设置变量了。 - 接下来就是生成具体的样本数据了。1)这里设定生成1000个样本,所以设置一个1000的循环来生成。2)每一个样本是一个二维数组,每一维的值都用服从均匀分布的随机函数进行生成,用的是numpy库中自带的函数。3)生成一个样本数据之后,就计算这个数据跟三个圆心之间的距离,然后判断这个这个距离是不是小于任何一个圆的半径(即在这个圆内),如果是就将这个样本数据label标记为1,否则标记为0.
#生成样本数据
for i in range(1000): #总的样本数据量为1000
d = [np.random.uniform(0.0, 10.4), np.random.uniform(0.0, 10.4)] #生成一个x和y都在0~10.4的之间的二维数据,
d1 = 0.0 #距离圆心1的距离
d2 = 0.0 #距离圆心2的距离
d3 = 0.0 #距离圆心3的距离
d1 = np.sqrt(np.square(d[0] - r1[0]) + np.square(d[1] - r1[1])) #计算新生成的点距离圆心1的距离
d2 = np.sqrt(np.square(d[0] - r2[0]) + np.square(d[1] - r2[1])) #计算新生成的点距离圆心2的距离
d3 = np.sqrt(np.square(d[0] - r3[0]) + np.square(d[1] - r3[1])) #计算新生成的点距离圆心3的距离
if d1 <= 1.5 or d2 <= 2.0 or d3 <= 2.0: #当生成的点,在三个圆中任意一个圆内,就将label设置为1
d = [d[0], d[1], 1.0]
positivedata.append(list([d[0], d[1]])) #将label为1的数据,单独存一份,后面画图使用
rawdata.append(list(d))
else: #否则就将label设置为0
d = [d[0], d[1], 0.0]
negativedata.append(list([d[0], d[1]])) #将label为0的数据,单独存一份,后面画图使用
rawdata.append(list(d)) - 在开始学习之前,要把模型需要的超参数先设置一下。这里只有一个超参数eta,用于控制权重收敛速度的参数,这里根据经验设置为0.8.
eta = 0.8 #权重的学习速率,一般记为lr,这里只是为了和数学公式一致,所以就用了数学公式里面的字母η的读音 - 接下来就是要定义整个算法的核心:权重。这个也是经过样本训练之后,唯一剩下的,用于预测的东西。因为我们的网络组成是:输入层2个单元格,隐含层8个单元格,输出层1个单元格。所以输入层到隐含层的权重设置为:8*2的数组,用来存储每个输入层单元格到隐含层各个单元格的权重。为了提升拟合函数的普适性(非一定经过原点),所以对输入层增加一个偏置量,所以输入层到隐含层的权重设置为:8*3的数组。对于隐含层到输出层的权重,我就设置为:1*8的数组,用来存储每个隐含层单元格到输出层单元格的权重。对于权重的值均赋值[-0.1, 0.1]之间的随机值。
#初始化权重向量
weight_x_h = [] #输入层到隐含层的权重向量
weight_h_y = [] #隐含层到输出层的权重向量
#这里的网络结构为:
# 输入层:2 个值
# 隐含层:8 个值
# 输出层:1 个值
for i in range(8):
w1 = []
for j in range(2+1): #每个隐含层值都跟输入层的2个值连接,所以这里针对每个隐含层的值,都建立一个二维数组表示输入层的两个值跟这个隐含层值之间的权重
# 这里新增了一个偏置量,所以维度要比输入的2多1维
w1.append(random.uniform(-0.1, 0.1))
weight_x_h.append(w1)
for i in range(8): #隐含层的8个值跟输出层也是全连接,所以为每一个隐含层值设置一个跟输出层的连接权重值
weight_h_y.append(random.uniform(-0.1, 0.1))
#以上权重值都取-0.1~0.1之间 - 得到初始权重之后,就基本可以进行学习训练了。每一个样本训练都包括两个过程:1)前向传播过程。2)反向传播过程。:
- 前向传播过程:由输入值计算出预测输出值。
x_y = rawdata.__getitem__(j)
x = [x_y[0], x_y[1], 0] #因为我们设置了偏置,所以需要对x数据添加一个常数项
y = x_y[2]
h = [] #用于存储隐含层变量的值
for l in range(len(weight_x_h)): #前向计算的第一步:由输入层计算隐含层的值。输入层和隐含层的权重为weight_x_h
sum = 0.0
for lx in range(len(x)):
sum += x[lx]*weight_x_h[l][lx]
sum = 0/(0+np.exp(0-sum)) #对x*w的值做sigmoid 变换
h.append(sum) #将变换后的值存到隐含层向量中
y1 = 0.0
for l in range(len(h)): #前向计算的第二步:由隐含层计算输出层的值。隐含层和输出层的权重为weight_h_y
y1 += h[l]*weight_h_y[l]
if np.isnan(y1):
time.sleep(3)
y1 = 0/(1 + np.exp(0-y1)) #对h*w的值做sigmoid 变换 - 反向传播过程:由预测输出值和真实label之间的差距,更新权重。
delta_y_h = y1*(1-y1)*(y-y1) #后向反馈计算的第一步:由真实值y和预测值y1的差值,乘以输出层sigmoid函数的导数,得到隐含层权重调整基础项
delta_h_x = []
for m in range(len(weight_h_y)): #后向反馈计算的第二步:由隐含层权重调整基础项,乘以隐含层到输出层的权重,得到隐含层到输入层的误差项
#再用误差项,乘以隐含层sigmoid函数的导数,得到输入层权重调整基础项
delta_h_m = h[m]*(1-h[m])*weight_h_y[m]*delta_y_h
delta_h_x.append(delta_h_m)
for ll in range(len(weight_h_y)): #调整隐含层到输出层的权重
weight_h_y[ll] += eta*delta_y_h*h[ll]
for n in range(len(weight_x_h)): #调整输入层到隐含层的权重
for p in range(len(weight_x_h[n])):
weight_x_h[n][p] += eta*delta_h_x[n]*x[p]
- 前向传播过程:由输入值计算出预测输出值。
- 至此我们将MLP算法的整个就完整的实现了,当然这里实现的是最简易版本:1)网络层次只有3层,只有一个隐含层。2)超参数是人工设定的,也没有做超参数学习。3)权重更新使用的是随机梯度下降,也没有做batch尝试。4)所有权重更新使用的是一个值,并没有做差异化处理。等等。这些都会在后面逐一通过实例来进行说明。
- 先定义三个数组,分别用来存放:所有的数据、label为1的数据和label为0的数据。rawdata数组中的一行代表一条样本记录,python中的数组定义如下所示,因为样本的数量现在还不确定,所以就先定义一个动态的数组,在生成数据的时候动态添加。
- 完整的代码如下:
# 最简易版本
rawdata = [] #所有样本数据集合
positivedata = [] #label为1的样本集合
negativedata = [] #label为0的样本集合
#这里label为1的样本,采用在一片数据中划定三个圆,圆内的数据标记为label为1.
r1 = [1.1, 1.1] #第一个圆的圆心
r2 = [4.3, 4.3] #第二个圆的圆心
r3 = [8.1, 8.3] #第三个圆的圆心
#生成样本数据
for i in range(1000): #总的样本数据量为1000
d = [np.random.uniform(0.0, 10.4), np.random.uniform(0.0, 10.4)] #生成一个x和y都在0~10.4的之间的二维数据,
d1 = 0.0 #距离圆心1的距离
d2 = 0.0 #距离圆心2的距离
d3 = 0.0 #距离圆心3的距离
d1 = np.sqrt(np.square(d[0] - r1[0]) + np.square(d[1] - r1[1])) #计算新生成的点距离圆心1的距离
d2 = np.sqrt(np.square(d[0] - r2[0]) + np.square(d[1] - r2[1])) #计算新生成的点距离圆心2的距离
d3 = np.sqrt(np.square(d[0] - r3[0]) + np.square(d[1] - r3[1])) #计算新生成的点距离圆心3的距离
if d1 <= 1.5 or d2 <= 0 or d3 <= 0: #当生成的点,在三个圆中任意一个圆内,就将label设置为1
d = [d[0], d[1], 1.0]
positivedata.append(list([d[0], d[1]])) #将label为1的数据,单独存一份,后面画图使用
rawdata.append(list(d))
else: #否则就将label设置为0
d = [d[0], d[1], 0.0]
negativedata.append(list([d[0], d[1]])) #将label为0的数据,单独存一份,后面画图使用
rawdata.append(list(d))
eta = 0.8 #权重的学习速率,一般记为lr,这里只是为了和数学公式一致,所以就用了数学公式里面的字母η的读音
#初始化权重向量
weight_x_h = [] #输入层到隐含层的权重向量
weight_h_y = [] #隐含层到输出层的权重向量
#这里的网络结构为:
# 输入层:2 个值
# 隐含层:8 个值
# 输出层:1 个值
for i in range(8):
w1 = []
for j in range(2+1): #每个隐含层值都跟输入层的2个值连接,所以这里针对每个隐含层的值,都建立一个二维数组表示输入层的两个值跟这个隐含层值之间的权重
# 这里新增了一个偏置量,所以维度要比输入的2多1维
w1.append(random.uniform(-0.1, 0.1))
weight_x_h.append(w1)
for i in range(8+1): #隐含层的8个值跟输出层也是全连接,所以为每一个隐含层值设置一个跟输出层的连接权重值
# 这里新增了一个偏置量,所以维度要比输入的8多1维
weight_h_y.append(random.uniform(-0.1, 0.1))
#以上权重值都取-0.1~0.1之间
for i in range(1000): #设置最大的迭代次数为1000次
shinydata = []
for j in range(len(rawdata)): #遍历样本中的每一个数据,进行训练
x_y = rawdata.__getitem__(j)
x = [x_y[0], x_y[1], 1.0] #因为我们设置了偏置,所以需要对x数据添加一个常数项
y = x_y[2]
h = [] #用于存储隐含层变量的值
for l in range(len(weight_x_h)): #前向计算的第一步:由输入层计算隐含层的值。输入层和隐含层的权重为weight_x_h
sum = 0.0
for lx in range(len(x)):
sum += x[lx]*weight_x_h[l][lx]
sum = 1.0/(1.0+np.exp(0-sum)) #对x*w的值做sigmoid 变换
h.append(sum) #将变换后的值存到隐含层向量中
y1 = 0.0
for l in range(len(h)): #前向计算的第二步:由隐含层计算输出层的值。隐含层和输出层的权重为weight_h_y
y1 += h[l]*weight_h_y[l]
if np.isnan(y1):
time.sleep(3)
y1 = 1.0/(1 + np.exp(0-y1)) #对h*w的值做sigmoid 变换
# print(y1)
if y1 > 0.5:
shinydata.append([x_y[0], x_y[1]])
delta_y_h = y1*(1-y1)*(y-y1) #后向反馈计算的第一步:由真实值y和预测值y1的差值,乘以输出层sigmoid函数的导数,得到隐含层权重调整基础项
delta_h_x = []
for m in range(len(weight_h_y)): #后向反馈计算的第二步:由隐含层权重调整基础项,乘以隐含层到输出层的权重,得到隐含层到输入层的误差项
#再用误差项,乘以隐含层sigmoid函数的导数,得到输入层权重调整基础项
delta_h_m = h[m]*(1-h[m])*weight_h_y[m]*delta_y_h
delta_h_x.append(delta_h_m)
for ll in range(len(weight_h_y)): #调整隐含层到输出层的权重
weight_h_y[ll] += eta*delta_y_h*h[ll]
for n in range(len(weight_x_h)): #调整输入层到隐含层的权重
for p in range(len(weight_x_h[n])):
weight_x_h[n][p] += eta*delta_h_x[n]*x[p]
- 工欲善其事必先利其器,要做算法学习,首先要有数据,所以第一步我们首先构造实验用的样本。
- MLP算法的数学说明
- 首先是前向传播计算的数学说明。
- 前向计算的公式是由算法的网络结构决定的,即如下的公式是由算法的网络结构决定的(也即每一个):
hi=sigmoidj=0mwj-iinputj
outk=sigmoid(i=0nwi-khi)
其中:- hi:表示第i个隐含层单元格的值。
- sigmoidx=11+e-x:表示sigmoid数学变换。
- inputj:表示第j个输入层单元格的值。
- wj-i:表示第j个输入层单元格和第i个隐含层单元格之间的连接权重。
- outk:表示第k个输出层单元格。在我们这里只有一个输出层单元格,所以k就是1。
- wi-k:表示第i个隐含层单元格和第k个输出层单元格之间的连接权重。
- 那公式里面的计算为啥是先做加和,再做sigmoid变换呢?
- 从上面的过程梳理,我们不难发现,前向计算最终输出的是一个输入x属于某个类别(本文即label为1)的概率,记为p(1|x)。由贝叶斯公司可以知道:
p1x=px1*p(1)p(x)=px1*p(1)px1*p1+px0*p(0)
上下同除以px1*p(1):
p1x=px1*p(1)p(x)=11+px0*p(0)px1*p1
设z=lnpx1*p(1)px0*p0,则:
-z=-lnpx1*p1px0*p0 (1)
e-z=e-lnpx1*p(1)px0*p0=elnpx1*p(1)px0*p0-1=px0*p0px1*p1 (2)
p1x=11+px0*p(0)px1*p1=11+e-z (3)
所以,由一个输入向量x,计算其属于某个类别的概率,可以表示为sigmoid函数。
p1x=11+e-z - 得到了sigmoid函数的理论说明后,就要解决z=wixi+b的理论说明。由上面的假设可知
z=lnpx1*p(1)px0*p0=lnp(x|1)p(x|0)+lnp(1)p(0)=lnpx1-lnpx0+lnp(1)p(0)
在本例中x是一个二维数组,所以假设:
w1=lnpx1x1
w2=-lnpx0x2
b=lnp(1)p(0)
则:
z=lnpx1-lnpx0+lnp1p0=w1*x1+w2*x2+b
所以,基于上面的推导可以得到,z=wixi+b是可以成立的。
- 从上面的过程梳理,我们不难发现,前向计算最终输出的是一个输入x属于某个类别(本文即label为1)的概率,记为p(1|x)。由贝叶斯公司可以知道:
- 大家可能觉得上面的推导是不是一种投机取巧,先知道了答案,然后根据答案来假设需要的条件,再推导一遍。事实上是可以这么理解的,因为激活函数确实是有很多个选择,不是只有sigmoid,只要前后的逻辑能够自洽就可以。但是每种深度学习算法都代表着一种实验出来的结构,是固定的。因为这个结构是实验出来的,所以并没有特别明确的理论基础,所以也可以自己通过实验进行修改和验证。
- 前向计算的公式是由算法的网络结构决定的,即如下的公式是由算法的网络结构决定的(也即每一个):
- 其次是反向传播计算的数学说明。
- 因为我们用的梯度下降的方法对权重进行优化,所以权重的优化公式为:
wi=wi+η*∂∇∂wi=wi+η*∂∂wi12(ylabel-yi)2- 首先,函数的梯度的定义就是它相对于各个变量的偏导数。所以用误差对wi求偏导得到梯度。
- 第二,真实值ylabel和预测值yi之间的误差衡量这里用的平方差,乘以1/2是为了后面计算简洁。
- η是学习率,表示参数收敛的快慢,也就是每一步更新的步长。如果步长过大就会最优值周围来回震荡,如果太小就要花费很长的时间才能到达最优值,所以这个值一般以一个经验来定。当然参数优化也有一些套路,这个在后面会详细解释。
- ∂∂:表示偏导数,分子是原表达式,分母是要求导数的变量。
- ∇=12(ylabel-yi)2:表示误差项。
- 有了基础公式之后,我们就要对公式做拆解,最终写成一个可以做加减乘除运算的式子。
- 首先yi和wi没有直接联系,而是通过两个表达式连接在一起的:
z=w1*x1+w2*x2+b=wixi+b
yi=sigmoidz=11+e-z - 所以建立一个链式导数如下:
wi=wi+η*∂∇∂wi=wi+η*∂∂wi12ylabel-yi2=wi+η*∂∂yi12ylabel-yi2*∂∂zsigmoidz*∂z∂wi - 对于各个部分的导数计算,这里就直接给结果,如果想知道为啥是这个结果的,可以看看常用函数的导数相关的知识:
∂∂yi12ylabel-yi2=12*2*ylabel-yi*-1=-ylabel-yi
∂∂zsigmoidz=∂∂zez1+ez=ez*1*11+ez+ez*-11+ez2*ez=sigmoidz*1-sigmoidz=yi*(1-yi)
∂z∂wi=∂∂wiwixi+b=xi
- 所以,最终得到的可以做加减乘除运算的式子是:
wi=wi+η*-ylabel-yi*yi*1-yi*xi
所以大家可以看到,经过那么多的数学推导,在实际编程的时候,其实不会把偏导数、链式导数等等这些复杂的概念进行处理,最终一定是转化成可以做加减的计算,连乘除都不需要。所以大家如果在学习相关的算法的时候,只要记住:这些复杂的数学公式的推导,最终只是为了得到一个简单的加减公式就好。如果你不想了解这些推导的数学原理,就直接找到最终那个式子把他记下来就可以了。
- 首先yi和wi没有直接联系,而是通过两个表达式连接在一起的:
- 有了基础的公式之后,我们就要对反向传播计算的两个过程进行具体的推导了:
- 对于输出层到隐含层的反向传播,就直接用计算误差12ylabel-yi2对连接权重whi进行求导,所以这个过程很直接:
whi=whi+η*∂∇∂whi=whi+η*-ylabel-yi*yi*1-yi*hi- whi:表示第i个隐含层单元格和输出层单元格的连接权重。
- η:是学习速率。
- ylabel:真实label值。
- yi:预测的输出值。
- hi:第i个隐含层单元格的值。
- 对于隐含层到输出层的反向传播,要用计算误差12ylabel-yi2对输入层到隐含层的连接权重wxi进行求导,所以这里面就要有多个链式传导:
wxi=wxi+η*∂∇∂wxi=wxi+η*∂∇∂yi*∂yi∂z*∂z∂hi*∂hi∂zx*∂zx∂wxi
其中:
∂∇∂yi*∂yi∂z=-ylabel-yi*yi*1-yi=δ
∂z∂hi=whi
∂hi∂zx=hi*1-hi
∂zx∂wxi=xi
所以:
wxi=wxi+η*δ*whi*hi*1-hi*xi- wxi:表示第i个输入层单元格和到隐含层单元格的连接权重。
- η:是学习速率。
- ylabel:真实label值。
- yi:预测的输出值。
- hi:第i个隐含层单元格的值。
- xi:第i个输入层单元格的值。
- whi:表示第i个隐含层单元格和到输出层单元格的连接权重。
- z=whihi+b:表示隐含层到输出层的线性聚合。
- zx=wxixi+b:表示输入层到隐含层的线性聚合。
- 至此我们对于反向传播过程中两个环节的权重更新都已经做完了,整个算法的数学说明也完成了。
- 对于输出层到隐含层的反向传播,就直接用计算误差12ylabel-yi2对连接权重whi进行求导,所以这个过程很直接:
- 因为我们用的梯度下降的方法对权重进行优化,所以权重的优化公式为:
- 首先是前向传播计算的数学说明。
可以关注公众号看更新的信息文章来源地址https://www.toymoban.com/news/detail-414743.html
到了这里,关于MLP算法详解与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!