«

梯度下降和反向传播: 能改(下)

时间:2023-2-27 23:21     作者:wen     分类: AI


通过上面的学习, 我们已然了解现代神经网络的精髓之一的梯度下降算法, 但是如果仔细观察我们设计的预测函数, 你就会发现, 这是一个非常危险和不完善的模型. 比如在另一片海域里, 豆豆的大小和毒性的关系是豆豆越大毒性越小, 我们发现无论怎样调整w我们都无法得到理想的预测函数, 原因很简单, 我们的预测函数y=wx, 很明显是一个必须经过原点的直线, 换句话说, 这个预测函数直线的自由被限制住了, 只能旋转不能移动. 因为大家很清楚, 一个直线完整的函数应该是y=wx+b, 之前我们为了遵循, "如无必要, 勿曾新知" 的理念, 一直在刻意的避免这个截距参数b. 直到现在, 我们终于避无可避, 是时候增加"新知"了.
size-Toxicity function
截距b的作用大家很清楚, 可以让直线在平面内自由平移, 而斜率w, 它可以让直线自由的旋转. 当我们把直线平移的自由还给它之后, 这两者的结合才能让直线在整个平面内真正自由起来. 我们来看一下加入截距b之后发生的改变, 首先, 我们带入b重新推演一次预测和梯度下降的过程, 当然为了简单我们还是先看单个豆豆样本的情况, y=wx+b是预测函数, 豆豆的大小是x0,毒性是y0,预测是wx0+b, 那么根据方差代价函数得到方差代价是这样的e = (y0-(wx0+b))2 = x02w2+(2x0b-2x0y0)w+(y02+b2-2y0b), 你会发现没有b的时候, 或者b为0的时候, 代价函数是: x02w2+(-2x0y0)w+y02, 那现在既然有了b, 接下来我们就要看这个b取不同的值的时候, 会对代价函数造成怎样的影响, 这里我们需要把代价函数的图像从二维变成三维, 给b留出一个维度, b=0就是我们之前讨论的抛物线, b=0.1, e和w关系还是一个标准的开口向上的抛物线, 因为b的改变只影响这个抛物线的系数, 换句话说, 改变的只是抛物线的具体样子, 而不会让它变成其它形状, 同样的道理, b=0.2, b=0.3 也是, 等等等等, 我们好像已经看出一些眉目了, 这好像是一个曲面, 没错, 我们这里b的取值间隔是0.1, 描绘出来的效果似乎还是不太明显, 当我们把b的取值间隔弄小一点, 知道无限的小下去, 你就会发现, 果真是一个三维空间中的曲面.
e-b代价函数形成的曲图
那我们该如何看待这个曲面呢? 在有些教程和书籍中, 很多时候为了看着明显, 把它画成了一个鼓鼓的碗状, 其实对于线性回归问题中, 这种豆豆数据形成的代价函数, 实际上并没有那么鼓, 而是一个便便的碗, 扁的几乎看不出来是个碗状, 但是当我们把这个曲面的等高线画出来, 就可以看出这确实也是一个"碗". 很明显这个碗状的最低点, 肯定是问题关键的所在, 我们回忆一下, 在没有b出现的时候, 曲线最低点代表w的取值造成的预测误差最小, 那这个曲面最低点意味着什么呢?

首先想想这个最低点是怎么形成的? 没错, 我们每次取不同的w和b, 都会导致误差e不相同, 这个曲面也就是我们带入b后得到的代价函数的图像, 而他的最低点也就是意味着, 这里的w和b的取值会让预测的误差(代价)最小, 而如果我们能得到这个最低点的w和b值, 放回到预测函数中, 那么此时此刻, 恰如彼时彼刻, 预测也就是最好的, 现在我们的目标就很明确了, 如何在这个曲面上取最低点处的w和b值?

在没有b出现的美好时刻, 也就是说在b=0处, 沿着w的方向切上一刀, 我们知道这将形成一个, 关于e和w的开口向上的抛物线, 然后不断的通过梯度下降算法调整w, 最后到达最低点, 但是你会发现, 此刻曲线的最低点, 却并不是曲面的最低点, 换句话说, b=0的取值并不是最好的, 关于b, 套路其实还是一样的, 我们在这一点如果沿着b的方向给曲面来上一刀, 你会发现, 切口还是一个开口向上的抛物线, 如果是这样的话那就很nice了, 我们在这个抛物线上也向最低点挪动即可, 但果真如此吗?

我们之前已经分析出来e和w的关系是一个抛物线, 现在我们不妨在看一下e和b的关系,
e= (y0-(wx0+b))2,
这是方差代价函数, 要研究b, 那我们围绕b重新整理一下这个式子,
e = b2+(2x0w-2y0)b+(x02w2+y02-2x0yow)
, 当w确定的时候, 也就是我们沿着b的方向切下一刀, 不如当前这个点的w之为wcut, 这个时候代价函数是这样的,
e = b2+(2x0wcut-2y0)b+(x02wcut2+y02-2x0yowcut)
, 也就是把w看作一个确定值的时候, e和b的关系又是一个标准的开口向上的一元二次函数, 所以面对现在这个误差代价函数曲面, 我们还可以换个角度取理解它形成方式, 除了可以向一开始那样认为是e关于w的一元二次函数曲线, 在b取不同的值的时候形成的以外, 也可以认为是e关于b的一元二次函数曲线在每次w取不同的值的时候形成的, 现在我们在b上要做的事情和在w上一毛一样. 不断的调整b, 让它向这个曲线的最低点挪动, 而具体的方法也是一样的, 根据斜率进行下降, 我们完整的来看一下这个过程, 假设一开始我们的w=0.1, b=0.1 , 我们横看此处, 看见的是b确定的时候e和w形成的一个曲线, 根据此处的斜率(导数) 调整w, 大小是斜率(导数)学习率alpha, 方向是根据斜率的正负确定, 我们侧看此处, 看见的是w确定的时候e和b形成的一个曲线, 根据此处的斜率(导数)调整b, 大小是斜率(导数)学习率alpha, 方向根据斜率正负确定, 把这两个方向上的调整运动合成一个合成调整运动, 这样我们就完成了一次调整, 到达下一个点后我们继续横看调整w, 侧看调整b, 当我们反复进行这个过程的时候, 也就逐渐向这个曲面的最低点挪动, 所以说这里同时有w和b的代价函数曲面, 和只有w的代价函数曲线相比, 这个下降过程本质是一样的, 换汤不换药罢了, 或者说只是冲w一味药换成了w和b两味药, 但是有一点我们的代价函数已经是一个曲面了, 这个下降过程如果我们再说是"斜率下降"就不太合适了, 毕竟一个曲面上某点的斜率是个什么东西呢? 是关于w的还是b的呢? 要回答这个问题, 需要发散一下思维, 换一个角度来看这个下降的过程, 我们在代价函数的w和b两个方向上分别求得斜率, 或者说导数, 对于这个有两个自变量的代价函数, 我们先偏向w求导数, 在偏向b求导数, 为了区分只有一个自变量的情况, 为了区分只有一个自变量的情况, 我们把在某一个变量上的导数也称之为"偏导数", 如果我们把对w和对b的偏导数看作向量, 把两个向量合在一起, 形成一个新的合向量, 沿着这个和向量进行下降, 是这个曲面在该点下降最快的方式. 这个合向量在数学里称为"梯度", 到此为止, 你就理解了我们为什么说, 梯度比斜率更广泛的一个概念了. 他是把各个方向上的偏导数(斜率)当作向量合起来形成的一个总向量, 代表着这个点下降最快的方向, 当然在二维曲线中, 因为没有其它方向, 梯度和斜率可以认为是一回事儿, 而为了这个下降算法的名称更具有广泛性, 所以我们一般称之为"梯度下降", 而不是"斜率下降"
e-(w,b)代价函数.

# 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
# cost_function_w.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()
# sgd_w_b.py
import dataset
import matplotlib.pyplot as plt
import numpy as np

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()

for _ in range(500):
    for i in range(100):
        x = xs[i]
        y = ys[i]
        # a = x^2
        # b = -2xy
        # c = y^2
        # 斜率k = 2aw+b
        # e = 2x^2w + (2xb-2xy) w的导数
        dw = 2 * (x ** 2) * w + (2 * x * b - 2 * x * y)
        # e = 2b+(2xw-2y) b的导数
        db = 2 * b + 2 * x * w - 2 * y
        alpha = 0.1
        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.01s

标签: 人工智能