【人工智能原理自学】梯度下降和反向传播:能改

你好,我是小航,一个正在变秃、变强的文艺倾年。
笔记来自B站UP主Ele实验室的《小白也能听懂的人工智能原理》。
本文讲解梯度下降和反向传播:能改,一起卷起来叭!

目录

  • 一、“挪”
  • 二、再“挪”
  • 三、梯度下降

一、“挪”

一步到位计算固然是好,但是非常消耗计算资源,抛物线最低点的寻找过程,其实不必一步到位,大可以采用一点点挪动的方式。
【人工智能原理自学】梯度下降和反向传播:能改_第1张图片
那么问题来了:该怎样挪呢?

机制如你,会想到用斜率判断:
【人工智能原理自学】梯度下降和反向传播:能改_第2张图片
我们知道最低点的斜率为0,当K > 0则,小球则向左挪动,当K < 0,则向右挪动。
【人工智能原理自学】梯度下降和反向传播:能改_第3张图片
【人工智能原理自学】梯度下降和反向传播:能改_第4张图片

【人工智能原理自学】梯度下降和反向传播:能改_第5张图片
那么斜率怎么求呢?机制如你又想到求导

【人工智能原理自学】梯度下降和反向传播:能改_第6张图片
接下来如何调整斜率,我们希望当W距离最低点比较远的时候,我们其实希望它能快一点,而逐渐接近最低点的时候,稳如老狗。

【人工智能原理自学】梯度下降和反向传播:能改_第7张图片


我们对上述想法进行代码实现:

豆豆数据集模拟:dataset.py

import numpy as np

def get_beans(counts):
	xs = np.random.rand(counts)
	xs = np.sort(xs)
	ys = [1.2*x+np.random.rand()/10 for x in xs]
	return xs,ys

使用固定步长下降算法:sgd_step.py

import dataset
import matplotlib.pyplot as plt
import numpy as np

# 豆豆数量m
m = 100
xs, ys = dataset.get_beans(m)

# 配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel("Bean Size")
plt.ylabel("Toxicity")
plt.scatter(xs, ys)

w = 0.1
y_pre = w * xs
plt.plot(xs, y_pre)
plt.show()

step = 0.01
for _ in range(100):
    # 抛物线代价函数
    # e = x0^2 * w^2 + (-2x0y0) * w + y0^2
    # 斜率k = 2aw + b(求导)
    k = 2 * np.sum(xs ** 2) * w + np.sum(-2 * xs* ys)
    k = k / m
    if k > 0 :
        w = w - step
    else :
        w = w + step
    y_pre = w * xs
    # 绘制动态
    plt.clf() ## 清空窗口
    plt.scatter(xs, ys)
    plt.xlim(0, 1)
    plt.ylim(0, 1.2)
    plt.plot(xs, y_pre)
    plt.pause(0.01) # 暂停0.01秒

实验结果:
【人工智能原理自学】梯度下降和反向传播:能改_第8张图片
使用随机梯度下降算法:sgd.py

import dataset
import matplotlib.pyplot as plt
import numpy as np

# 豆豆数量m
m = 100
xs, ys = dataset.get_beans(m)

# 配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel("Bean Size")
plt.ylabel("Toxicity")
plt.scatter(xs, ys)

w = 0.1
y_pre = w * xs
plt.plot(xs, y_pre)
plt.show()

for _ in range(100):
    for i in range(100):
        x = xs[i]
        y = ys[i]
        # 抛物线代价函数
        # e = x0^2 * w^2 + (-2x0y0) * w + y0^2
        # 斜率k = 2aw + b(求导)
        k = 2 * (x**2) * w + (-2 * x * y)
        # alpha为学习率
        alpha = 0.1
        w = w - alpha * k
        # 绘制动态
        plt.clf() ## 清空窗口
        plt.scatter(xs, ys)
        y_pre = w * xs
        plt.xlim(0, 1)
        plt.ylim(0, 1.2)
        plt.plot(xs, y_pre)
        plt.pause(0.01) # 暂停0.01秒

# 重新绘制散点图和预测曲线
# plt.scatter(xs, ys)
# y_pre = w * xs
# plt.plot(xs, y_pre)
# plt.show()

使用批量梯度下降算法:sgd_batch.py

import dataset
import matplotlib.pyplot as plt
import numpy as np

# 豆豆数量m
m = 100
xs, ys = dataset.get_beans(m)

# 配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel("Bean Size")
plt.ylabel("Toxicity")
plt.scatter(xs, ys)

w = 0.1
y_pre = w * xs
plt.plot(xs, y_pre)
plt.show()

alpha = 0.01
for _ in range(100):
    # 抛物线代价函数
    # e = x0^2 * w^2 + (-2x0y0) * w + y0^2
    # 斜率k = 2aw + b(求导)
    k = 2 * np.sum(xs ** 2) * w + np.sum(-2 * xs* ys)
    k = k / m
    w = w - alpha * k
    y_pre = w * xs
    # 绘制动态
    plt.clf() ## 清空窗口
    plt.scatter(xs, ys)
    plt.xlim(0, 1)
    plt.ylim(0, 1.2)
    plt.plot(xs, y_pre)
    plt.pause(0.01) # 暂停0.01秒

二、再“挪”

而事实上,一个直线完整的函数 应该是:y = wx + b(截距)
【人工智能原理自学】梯度下降和反向传播:能改_第9张图片
我们得到的代价函数是这样的:
【人工智能原理自学】梯度下降和反向传播:能改_第10张图片)
现在我们给b留出一个维度:
【人工智能原理自学】梯度下降和反向传播:能改_第11张图片
我们会发现得到的是一个“碗”状的曲面:
【人工智能原理自学】梯度下降和反向传播:能改_第12张图片)

曲面的最低点是:
【人工智能原理自学】梯度下降和反向传播:能改_第13张图片)
现在我们的目标很明确了,如何求出该最低点(Wmin,Bmin)

我们在b=0处沿着W的方向切上一刀,得到一个开口向上的抛物线,很容易得到最低点:
【人工智能原理自学】梯度下降和反向传播:能改_第14张图片)
但是我们发现此刻曲线的最低点并不是曲面的最低点
【人工智能原理自学】梯度下降和反向传播:能改_第15张图片)
那我们接着沿着b的方向切上一刀,得到一个开口向上的抛物线:
【人工智能原理自学】梯度下降和反向传播:能改_第16张图片)

我们把两个方向上的运动合为一个方向,这样我们完成了一次调整:

【人工智能原理自学】梯度下降和反向传播:能改_第17张图片)
【人工智能原理自学】梯度下降和反向传播:能改_第18张图片)
【人工智能原理自学】梯度下降和反向传播:能改_第19张图片)
我们分别对e函数有关w、b求偏导:
【人工智能原理自学】梯度下降和反向传播:能改_第20张图片)
【人工智能原理自学】梯度下降和反向传播:能改_第21张图片)
这样我们的整个过程可以总结为:

【人工智能原理自学】梯度下降和反向传播:能改_第22张图片)


我们对上述过程代码实现:

豆豆数据集模拟:dataset.py

import numpy as np

def get_beans(counts):
	xs = np.random.rand(counts)
	xs = np.sort(xs)
	ys = np.array([(0.7*x+(0.5-np.random.rand())/5+0.5) for x in xs])
	return xs,ys

豆豆毒性分布如下:
【人工智能原理自学】梯度下降和反向传播:能改_第23张图片)
代价函数(W为自变量):cost_function_w.py

import dataset
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 从数据中获取随机豆豆
m = 100
xs, ys = dataset.get_beans(m)

# 配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel('Bean Size')
plt.ylabel('Toxicity')

# 豆豆毒性散点图
plt.scatter(xs, ys)

# 预测函数
w = 0.1
b = 0.1
y_pre = w * xs + b

# 预测函数图像
plt.plot(xs, y_pre)

# 显示图像
plt.show()

# 代价函数
ws = np.arange(-1, 2, 0.1)
bs = np.arange(-2, 2, 0.1)

# 配置3D图像显示插件
fig = plt.figure()
ax = Axes3D(fig)
ax.set_zlim(0, 2)

for b in bs:  # 每次取不同的w
    es = []
    for w in ws:
        y_pre = w * xs + b
        # 得到w和b的关系
        e = (1 / m) * np.sum((ys - y_pre) ** 2)
        es.append(e)
    # plt.plot(ws,es)
    ax.plot(ws, es, b, zdir='y')

# 显示图像
plt.show()

实验结果:
【人工智能原理自学】梯度下降和反向传播:能改_第24张图片)
代价函数(B为自变量):cost_function_b.py

import matplotlib.pyplot as plt
import dataset
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

#从数据中获取随机豆豆
m=100
xs,ys = dataset.get_beans(m)


#配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel('Bean Size')
plt.ylabel('Toxicity')

# 豆豆毒性散点图
plt.scatter(xs, ys) 

#预测函数
w=0.1
b=0.1
y_pre = w*xs+b

#预测函数图像
plt.plot(xs,y_pre) 

#显示图像
plt.show()  




#代价函数
ws = np.arange(-1,2,0.1)
bs = np.arange(-2,2,0.1)


fig = plt.figure()
ax = Axes3D(fig)

ax.set_zlim(0,2)

for w in ws:#每次取不同的w
	es = []	
	for b in bs:
		y_pre = w*xs+b
		#得到w和b的关系
		e = (1/m)*np.sum((ys-y_pre)**2)
		es.append(e)
	#plt.plot(ws,es) 
	figure = ax.plot(bs, es, w, zdir='y')


#显示图像
plt.show()  

实验结果:
【人工智能原理自学】梯度下降和反向传播:能改_第25张图片)
当然我们也可以使用plot_surface函数绘制曲面绘制曲面版的:cost_function_surface.py

import matplotlib.pyplot as plt
import dataset
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

#从数据中获取随机豆豆
m=100
xs,ys = dataset.get_beans(m)




#配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel('Bean Size')
plt.ylabel('Toxicity')

# 豆豆毒性散点图
plt.scatter(xs, ys) 

#预测函数
w=0.1
b=0.1
y_pre = w*xs+b

#预测函数图像
plt.plot(xs,y_pre) 

#显示图像
plt.show()  



#代价函数
ws = np.arange(-1,2,0.1)
bs = np.arange(-2,2,0.1)

#把ws和bs变成一个网格矩阵
#这个网格矩阵的含义可以参考这篇文章:
#https://blog.csdn.net/lllxxq141592654/article/details/81532855
ws,bs = np.meshgrid(ws,bs)
print(ws)#打印出来瞅瞅
print(bs)


es = 0
#因为ws和bs已经变成了网格矩阵了
#一次性带入全部计算,我们需要一个一个的算
for i in range(m):
	y_pre = ws*xs[i]+bs#取出一个样本在网格矩阵上计算,得到一个预测矩阵
	e = (ys[i]-y_pre)**2#标准值减去预测(矩阵)得到方差矩阵
	es += e#把单样本上的方差矩阵不断累加到es上
es = es/m#求平均值,这样es方差矩阵每个点的位置就是对应的ws和bs矩阵每个点位置预测得到的方差



fig = plt.figure()
ax = Axes3D(fig)

ax.set_zlim(0,2)

#plot_surface函数绘制曲面
#cmap='rainbow表示彩虹图(用不同的颜色表示不同值)
ax.plot_surface(ws, bs, es, cmap='rainbow')

#显示图像
plt.show()  

实验结果:
【人工智能原理自学】梯度下降和反向传播:能改_第26张图片)

使用随机梯度下降算法:sgd_w_b.py

import dataset
import matplotlib.pyplot as plt

# 豆豆数量m
m = 100
xs, ys = dataset.get_beans(m)

# 配置图像
plt.title("Size-Toxicity Function", fontsize=12)
plt.xlabel("Bean Size")
plt.ylabel("Toxicity")
plt.scatter(xs, ys)

w = 0.1
b = 0.1
y_pre = w * xs + b
plt.plot(xs, y_pre)
plt.show()

# alpha为学习率
alpha = 0.01
# 训练500次
for _ in range(500):
    for i in range(100):
        x = xs[i]
        y = ys[i]
        # 抛物线代价函数
        # 斜率k(求导)
        dw = 2 * (x ** 2) * w + 2 * x * b - 2 * x * y
        db = 2 * b + 2 * x * w - 2 * y

        w = w - alpha * dw
        b = b - alpha * db
    # 训练一次后刷新
    # 绘制动态
    plt.clf()  ## 清空窗口
    plt.scatter(xs, ys)
    y_pre = w * xs + b
    plt.xlim(0, 1)
    plt.ylim(0, 1.2)
    plt.plot(xs, y_pre)
    plt.pause(0.01)  # 暂停0.01秒

实验结果:
【人工智能原理自学】梯度下降和反向传播:能改_第27张图片)

对于单个样本的曲面实际上是一个U型的特殊碗

三、梯度下降

训练神经网络的时候,基本就是三个步骤:

【人工智能原理自学】梯度下降和反向传播:能改_第28张图片)

  1. 正向计算网络输出;
  2. 计算Loss;
  3. 反向传播,计算Loss的梯度来更新参数(即梯度下降)。

【人工智能原理自学】梯度下降和反向传播:能改_第29张图片)

小的训练集上训练的时候,通常每次对所有样本计算Loss之后通过梯度下降的方式更新参数(批量梯度下降),但是在大的训练集时,这样每次计算所有样本的Loss再计算一次梯度更新参数的方式效率是很低的。

因此梯度下降常常分为:随机梯度下降、mini-batch梯度下降以及batch梯度下降。


随机梯度下降(Stochastic Gradient Descent):

【人工智能原理自学】梯度下降和反向传播:能改_第30张图片
随机梯度下降每次迭代(iteration)计算单个样本的损失并进行梯度下降更新参数,这样在每轮epoch就能进行 m 次参数更新

优点:

  • 参数更新速度大大加快,因为计算完每个样本的Loss都会进行一次参数更新

缺点:

  • 计算量大且无法并行。批量梯度下降能够利用矩阵运算和并行计算来计算Loss,但是SGD每遍历到一个样本就进行梯度计算和参数下降,无法进行有效的并行计算。

  • 容易陷入局部最优导致模型准确率下降。因为单个样本的Loss无法代替全局Loss,这样计算出来的梯度方向也会和全局最优的方向存在偏离。但是由于样本数量多,总体的Loss会保持降低,只不过Loss的变化曲线会存在较大的波动。像下图这样:

    【人工智能原理自学】梯度下降和反向传播:能改_第31张图片


批量梯度下降(Batch Gradient Descent):

批量梯度下降就是每个epoch计算所有样本的Loss,进而计算梯度进行反向传播、参数更新:

【人工智能原理自学】梯度下降和反向传播:能改_第32张图片
m 为训练集样本数,l 为损失函数,ϵ 表示学习率

优点:

  • 每个epoch通过所有样本来计算Loss,这样计算出的Loss更能表示当前分类器在于整个训练集的表现,得到的梯度的方向也更能代表全局极小值点的方向。如果损失函数为凸函数,那么这种方式一定可以找到全局最优解。

缺点:

  • 每次都需要用所有样本来计算Loss,在样本数量非常大的时候即使也只能有限的并行计算,并且在每个epoch计算所有样本Loss后只更新一次参数,即只进行一次梯度下降操作,效率非常低。

小批量梯度下降(min-Batch Gradient Descent):

【人工智能原理自学】梯度下降和反向传播:能改_第33张图片
小批量梯度下降将所有的训练样本划分到 batches 个min-batch中,每个mini-batch包含 batchsize 个训练样本。每个iteration计算一个mini-batch中的样本的Loss,进而进梯度下降和参数更新,这样兼顾了批量梯度下降的准确度和随机梯度下降的更新效率。

  • 当 batch_size=m 时,小批量梯度下降就变成了批量梯度下降;
  • 当 batch_size=1 ,就退化为了SGD。

一般来说 batch_size 取2的整数次方的值。不得不说,“折中调和”真是经久不衰的智慧。

事实上,我们平时用梯度下降的时候说的最多的SGD指的是小批量梯度下降,各种论文里所说的SGD也大都指的mini-batch梯度下降这种方式。tensorflow中也是通过定义batch_size的方式在优化过程中使用小批量梯度下降的方式(当然,也取决于batch_size的设置)


相关代码仓库链接,欢迎Star:传送门

你可能感兴趣的:(人工智能,人工智能,python)