社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

带你一文透彻学习【PyTorch深度学习实践】分篇——线性模型 & 梯度下降

CSDN学习 • 3 年前 • 530 次点击  


本篇文章来源于专栏《Python从入门到人工智能》,更多内容可复制链接到此查看:https://blog.csdn.net/qq_44731019/category_11717408.html

对于一个算法(模型)。在深度学习中,简要的处理方式是:

准备数据集(Datasets)—>> Model(选择模型) —>> Training (模型训练) —>> 推理(进行推理预测)。

至于优化等,可以理解为后续的补充。

监督学习:数据集需要 交付给算法模型 进行训练,利用所训练的模型,在获得 新的数据时 可以得到相应的输出。

线性模型的基本模型如下,其中的ω和  是模型中的参数,训练模型的过程即为确定模型中参数的过程:

   
在本模型中设置成      对于不同的  有不同的线性模型及图像与之对应。

在模型训练中 会先随机取得一个值,继而 计算其和标准量之间的 偏移量,从而判断 当前模型 是否符合预期。

记实际值为  ,模型对应的预测值为  ,则其中的偏移量为  ,以此来代表 模型估计值 对原值的误差。

通常,该公式定义为Training Loss (Error)

  

本例中,原题目中的几种  所对应的Loss如下 几个图所示:

其中的每行 为   不同时 的 单个样本的损失,最后一行为 平均损失。

对于单个样本,loss 可用于 指代样本误差。对于所有样本,可同理用Mean Square Error (MSE)来指代 整体样本的平均平方误差(均方差cost)

MSE:均方误差(Mean Square Error)。,即 每个样本对应的损失值 (平方) 求和,再除以样本总个数。

  

  :

  :

  :


由cost的计算公式可知,当平均损失为0时,模型最佳,但由于 仅当数据无噪声 且 模型完美贴合数据 的情况下才会出现这种情况,因此 模型训练的目的 应当是 误差(损失)尽可能小,而非找到 误差为0的情况。

不同  得到的 MSE:

1.1.1 基础练习  

根据上面的分析,code如下:注释我已经写的比较清楚啦。

# 昵 称:XieXu# 时 间: 2023/2/12/0012 21:10
# 导入必要的工具包import numpy as npimport matplotlib.pyplot as plt
# 自定义 简单数据集。x与y 一一对应 (训练集)x_data = [1.0, 2.0, 3.0]y_data = [2.0, 4.0, 6.0]

# 模型,(前馈)def forward(x): return x * w

# 损失函数def loss(x, y): y_pred = forward(x) # 即 y_hat。相当于 预测的值 return (y_pred - y) * (y_pred - y) # 平方(均方误差)。简单形式的均方误差。这个计算的 为单个样本的误差
# 【穷举法】# 如下两个列表 保存 权重 及其对应的损失值w_list = []mse_list = []
# [0.0,4.1),间隔为0.1。即 0.0, 0.1, 0.2, 0.3 ... 4.0 起始值(含),停止值(不含),步长for w in np.arange(0.0, 4.1, 0.1): # 外层循环 控制权重 2023.2.13 08:20 print("w=", w) l_sum = 0 # zip 将x_data 和 y_data 打包为一个tuple(元组),方便同时遍历。 for x_val, y_val in zip(x_data, y_data): # 内层循环控制进行 权重:调用forward函数 对应的预测,以及 调用上面定义的loss函数 进行损失值计算 y_pred_val = forward(x_val) # 计算 每个样本的 预测值 loss_val = loss(x_val, y_val) # 计算 每个样本的 损失值 l_sum += loss_val # 将 所有样本的 损失求和(这里没做均值) print("\t", x_val, y_val, y_pred_val, loss_val) # 打印出 每一个样本:x,x对应的y,预测值y_hat,损失值 print("MSE=", l_sum / 3) # 这里 除以样本总数,进行均值,即 均方误差 w_list.append(w) # 将每次 用完的 权重添加到列表中,用以 下面画图 的横坐标~ mse_list.append(l_sum / 3) # 除以样本总数。每个权重 对应的 所有样本的平均误差(均方误差) MSE,也叫均值吧。下面绘图的纵坐标
# 绘制图形plt.plot(w_list, mse_list) # 横坐标、纵坐标 的取值plt.ylabel("Loss") # 纵坐标y的标签plt.xlabel("w") # 横坐标w的标签plt.show()

可以得到结果:红色为我做的标注~

从下面 控制台打印的日志中,我们 可以 很容易看出来上图 的由来:

开始时,随着W增加,平均损失MSE逐渐减小,


到W为2.0时,MSE达到最小 即0,

后面   继续增加,MSE又变大了:


1.1.2 练习  

同样地,基于上面的例子,练习:

实现线性模型  并输出loss的3D图像。

不同之处在于,定义的模型,与上面相比,加了个 偏置项B。

# 昵 称:XieXu# 时 间: 2023/2/13/0012 12:26
# 练习题# 作业题目:实现线性模型(y=wx+b)并画出loss的3D图像。import numpy as npimport matplotlib.pyplot as pltfrom mpl_toolkits.mplot3d import Axes3D
# 以下两行代码 解决坐标轴不能显示中文问题 否则会报类似错误:FigureCanvasAgg.draw(self) (图上的 中文无法正常显示)from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 输入数据,设函数为y=3x+2x_data = [1.0, 2.0, 3.0]y_data = [5.0, 8.0, 11.0]
# 定义模型def forward(x): return x * w + b
# 定义损失函数def loss(x, y): y_pred = forward(x) return (y_pred - y) * (y_pred - y)

mse_list = [] # 保存mse,均方误差W = np.arange(0.0, 4.1, 0.1)B = np.arange(0.0, 4.1, 0.1)[w, b] = np.meshgrid(W, B) # 通过 两个坐标轴上的点 在平面上画网格
l_sum = 0for x_val, y_val in zip(x_data, y_data): y_pred_val = forward(x_val) print(y_pred_val) loss_val = loss(x_val, y_val) l_sum += loss_val
fig = plt.figure()ax = Axes3D(fig)ax.plot_surface(w, b, l_sum / 3)# 以下三行 设置坐标的文字说明ax.set_xlabel("权重W")ax.set_ylabel("偏置项 B")ax.set_zlabel("损失值")plt.show() # 画图

可以得到如下图形:这就是 (   ) 损失值的3D图形。

其中,控制台输出的日志中,含有警告:


定位到源代码中:

ax = Axes3D(fig)

翻译一下上面的警告:大意即 由于版本不同,可暂时忽略。

1.1.3 其它:在深度学习训练中,横轴往往是Epoch

在深度学习训练的可视化图形中,一般横轴是Epoch,即训练轮数:往往在训练集上表现是,随着训练轮数增多,损失越来越低;而在测试集(老师读 开发集)上的效果是,损失在刚开始会下降,而后到某个点,又会逐渐上升。而我们的目标就是,想找到损失最低的那个点;从而进一步 对超参数进行处理等其它操作。

深度学习中可能需要考虑更多问题,比如 可视化问题 / 训练的时间问题(可能需要几天,甚至连续几周都是有可能的) / 断点问题(比如训练7天会结束,但是第6天程序崩溃了,这期间产生的数据 结果 怎么办)

老师提到了 Pytorch可视化工具——Visdom。目前我没有用到…稍微大型的项目可能会用到吧。可以在百度搜索Visdom并查看它的相关信息,以及其GitHub官网。

💧1.2 Gradient Descent(梯度下降)

1.2.1 梯度下降法 的由来:问题背景

在上面 线性模型的方法中,所使用的思想是基于穷举,即提前已经设定好 参数的准确值 在某个区间内并以某个步长进行穷举,如(np.arange(0.0,4.1,0.1))。

但是,这种思想 在多维的情况下,即多个参数的时候,会引起维度诅咒的现象,在一个N维曲面中找一个最低点,容易 使得原问题变得不可解。那么 既然有这样的问题,就需要对算法 进行改进。

那么,使用 分治法 如何?

即:大化小,小化无,先对整体 进行分割采样,在相对最低点进行进一步采样,直到其步长与误差符合条件。

但是,分治法有两个缺点:

  • 容易只找到局部最优解,而不易找到一个全局最优解。

  • 如果需要分得更加细致,则计算量仍然巨大。

同时,由于以上问题的存在,引起了参数优化的问题,即求解使loss最小时的参数的值。

简言之,即求得  ,使得loss值最小。

  

如何优化,求得符合条件的  呢?

1.2.2 何为梯度?

梯度,即 导数变化最大的值,其方向导数变化最大的方向

这里,可以使用高等数学中关于 一个点处 导数的定义:

  

(这里仅简单理解,并非严谨数学推导)对于  ,若增函数,梯度为上升方向,若减函数,梯度为下降方向。由数学知识 知道,需要取梯度下降的方向 即梯度的反方向作为变化方向,才能尽可能地求得极小值(最小值)。

下面的图将便于理解:

1.2.3 梯度下降

在深度学习中,所说的凸函数,是与高等数学定义中的 凸函数,完全反过来的,知道这一点就好。如下图所示,在深度学习中,将其看做一个凸函数。

当前,  的取值点 与 全局最小值点:

那么,取值点 需要 向下更新,所取的梯度即为  ,更新的公式如下,其中   学习率,即 所下降的步长,不宜取太大。

  

  按梯度下降方向进行更新:

局限性

  • [1] 梯度下降算法 容易进入 局部最优解(非凸函数),但是

    实际问题中的局部最优点较少

    ,或 已经

    基本可以当成全局最优点。

  • [2] 梯度下降算法容易陷入鞍点(鞍点处,梯度值为0)(总之不是取得 损失最小值的点,简单理解为 局部最优:在优化问题中,鞍点 是一种特殊的局部最优解,是一个难以优化的点,因为优化算法 可能 很难从鞍点 附近找到 全局最优解)。(陷入到鞍点,导致无法继续 进行迭代!)

1.2.4 梯度下降的公式 如何得来的?(理解)

通过线性模型,我们知道 均方误差的公式即:其中  

  

进一步地,可以对  求偏导,一步一步来:

  

  

  是常数,对求  的导数没有影响,可以提到前面来:

  

根据数学知识,由于是对  求导,那么 可以 把(  )看做一个整体,【复合函数求导】:对含有  的 (外部)整体 求导,乘以内部 对  求导,故而得:

  

而(  )对  求导的结果 显然为  ,调整一下顺序,就得到对  的最终求导结果:

  

因此,梯度下降的更新公式为:

  

下图即上述公式的推导过程:

梯度的求解公式,应用到code中的示例:

1.2.5 随机梯度下降(Stochastic gradient descent,SGD);Mini-batch!

平时用的比较多的,是随机梯度下降(SGD)

SGD采用单个训练样本的损失来近似平均损失,故 SGD 用单个训练数据即可对模型参数进行一次更新,大大加快了训练速度。

随机梯度下降 每次 只需要计算 一个样本关于  的导数


随机梯度下降SGD 与 标准梯度下降的区别:

  • 标准梯度下降 在权值更新前 汇总所有样例得到的标准梯度,随机梯度下降 则是通过考察每次训练实例来更新。

  • 标准梯度下降的是使用准确的梯度,理直气壮地走,随机梯度下降使用的是近似的梯度,小心翼翼地走。

  • 标准梯度下降的步长 比 随机梯度下降 的大。

  • 当有多个局部极小值时,随机梯度 反而更可能 避免 进入局部极小值中。

同时,为了降低随机梯度的方差,使迭代算法更加稳定,在真实操作中,会同时处理若干训练数据,该方法叫做小批量随机梯度下降法(Mini_Batch Gradient Densent)。,这才是真正地运用了 随机梯度下降(SGD),目前,在实际应用中,我们所说的梯度下降,(batch)都是指的 Mini-batch SGD,小批量 随机梯度下降。 这在神经网络中,尤其明显!!!使用及其广泛!!!(既 保证 性能,又保证时间复杂度不是特别高)

小结

普通梯度下降算法利用数据整体,不容易避免鞍点,算法性能欠佳但算法效率高随机梯度下降需要利用每个的单个数据,虽然算法性能良好,但计算过程 环环相扣 无法将样本抽离开 并行运算,因此算法效率低,时间复杂度高

综上所述,可采取一种折中的方法,即批量梯度下降方法。

将若干个样本分为一组,记录一组的梯度 用以代替随机梯度下降中的单个样本。

该方法最为常用,也是默认接口。一般,mini-batch可以在2的幂次中挑选最优取值。例如16、32、64、128、256等。

1.2.6 梯度下降练习:方法一(推荐 完全 自己 多手写几遍)

给定一个数据集,x_data、y_data。寻找y=wx模型的w最优解。

code练习如下,注释中,我已经介绍的比较详细啦!我想这可以帮助绝大多数朋友理解。

# 昵 称:XieXu# 时 间: 2023/2/13/0013 13:26
# 梯度下降算法
# 注:在 深度学习算法 中,并没有过多的局部最优点。# 即 一般 通过梯度下降 就可以求得 最优点
import numpy as npimport matplotlib.pyplot as plt
# 训练数据,x与y一一对应x_data = [1.0, 2.0, 3.0]y_data = [2.0, 4.0, 6.0]
epoch_list = [] # # 2023.2.13 13:58 保存训练轮数cost_list = [] # 2023.2.13 13:58 每一轮对应的损失值
w = 1.0 # 初始化,我们指定一个W取值

# 前馈计算(定义模型)def forward(x): return x * w

# 求MSE (定义cost函数,计算均方误差)def cost(xs, ys): cost = 0 for x, y in zip(xs, ys): y_pred = forward(x) cost += (y_pred - y) ** 2 return cost / len(xs) # 计算 均方误差(损失)

# 求梯度(求W的偏导数)def gradient(xs, ys): grad = 0 for x, y in zip(xs, ys): temp = forward(x) grad += 2 * x * (temp - y) # 由导数公式得 return grad / len(xs)

print("Predict(before training)", 4, forward(4)) # 这是根据 我们 给定的那个W,计算y=wx得到的值,forward(4) 即4
# 开始训练for epoch in range(1, 101, 1): cost_val = cost(x_data, y_data) # 计算均方误差(损失) grad_val = gradient(x_data, y_data) # 求梯度(W的偏导数) w -= 0.01 * grad_val # 梯度下降,更新梯度(W) print("Epoch: ", epoch, "w = ", w, "loss = ", cost_val) # w就会用在下一轮的训练中
epoch_list.append(epoch) # 保存轮数1~100,做绘图的横坐标 cost_list.append(cost(x_data, y_data)) # 每轮对应的 损失值
print("Predict(after training)", 4, forward(4)) # 根据100轮 之后的权重W,计算x取4时,预测的y值。实际应该是无限接近2,即2
# 绘图plt.plot(epoch_list, cost_list) # 横坐标 和 纵坐标 分别是 轮数 和 对应该轮的 损失值plt.title("train loss")plt.xlabel('Epoch')plt.ylabel("Cost")plt.show()

得到结果:可以看到,在20轮左右,损失值就已经很接近0了。


控制台的结果:为了便于展示,这里我也特意截取到了20轮的训练结果。

1.2.7 方法二(与法一类似,不过这里 纵轴 是 权值的收敛变化

第二种方式:与第一种类似,只不过这里纵坐标取的是 权值,另外,直接把梯度下降 放到Epoch训练里面了。代码如下:

# 方法二import numpy as npimport matplotlib.pyplot as plt
x_data = [1.0, 2.0, 3.0]y_data = [2.0, 4.0, 6.0]
scope_list = [] # 轮数w_list = [] # 权值W
w = 60 # 我们给W一个初始值
# 学习率k = 0.01 # 定义学习率
# 开始训练for i in range(1,201,1): # 计算cost(loss的和) loss_sum = 0
# 求梯度 for x_val, y_val in zip(x_data, y_data): loss_sum += 2 * x_val * (w * x_val - y_val) cost = loss_sum / 3
# 计算本轮w w = w - k * cost print("Epoch:",i,"W:",w) scope_list.append(i) w_list.append(w)
# 按说纵坐标不应该取权值的,既然取了,那么可以从图形中,看出来大概 50左右,权值就收敛了plt.plot(scope_list, w_list) # 横坐标 取值 依然是 轮数,纵坐标取值是按照W来取的值plt.xlabel("scope")plt.ylabel("W")plt.show()


结果如下所示:

我们也可以看一下,Console控制台输出的内容:可以看到,50多轮依然还在收敛,100轮左右就已经收敛的比较好了~ 最终权值应为2

1.2.8 随机梯度下降练习(一),可视化

这里,即普通的随机梯度下降,每轮训练中,每次计算的 关于  的 偏导数,是仅仅只计算 一个样本的,而非 所有样本的关于  的偏导数 求和 再取均值。

 即 注:这里,随机梯度主要是指,每次拿一个训练数据来训练,然后更新梯度参数。

# 昵 称:XieXu# 时 间: 2023/2/13/0013 15:11# 随机梯度下降
# 方法一# 注:这里,随机梯度主要是指,每次拿一个训练数据来训练,然后更新梯度参数。# 本算法中 梯度总共更新100(epoch)x3 = 300次。梯度下降算法中 梯度总共更新100(epoch)次。import matplotlib.pyplot as plt
x_data = [1.0, 2.0, 3.0]y_data = [2.0, 4.0, 6.0]
w = 1.0

def forward(x): return x * w

# calculate loss function(损失函数--均方误差)def loss(x, y): y_pred = forward(x) return (y_pred - y) ** 2 # 仅 计算一个样本 的损失函数

# define the gradient function sgd(定义随机梯度下降 函数)def gradient(x, y): return 2 * x * (x * w - y) # 注:这里,仅计算 “一个样本” 关于W 的 偏导数

epoch_list = [] # 训练轮数,100轮 2023.2.13 19:24loss_list = []print('predict (before training)', 4, forward(4))for epoch in range(1, 101, 1): for x, y in zip(x_data, y_data): grad = gradient(x, y) # 这里,每轮训练中,三个 样本中 都分别计算了 梯度,for...zip循环。应该是没有用到“随机” w = w - 0.01 * grad # update weight by every grad of sample of training set。根据梯度,更新W。。。 print("\tgrad:", x, y, grad) # 打印每轮训练中,每个样本的x、y、根据其计算的梯度。2023.2.13 19:36 l = loss(x, y) # 根据梯度得到的 更新后的W,进一步 计算损失函数(均方误差) 2023.2.13 19:36 print("progress:", epoch, "w=", w, "loss=", l) # 打印该轮中,通过每一个样本 得到的 W 以及 损失函数值!!!2023.2.13 19:37 epoch_list.append(epoch) loss_list.append(l)
print('predict (after training)', 4, forward(4))plt.plot(epoch_list, loss_list)plt.title("train loss")plt.ylabel('loss')plt.xlabel('epoch')plt.show()


以此类推:

得到的损失函数值,关于 训练轮数的图像如下:

1.2.9 随机梯度下降练习(二),可视化

上面的随机梯度下降算法中,貌似仅仅 是只计算了 每一个样本的梯度,好像没有体现“随机”。

这里再换个类似的算法,基本一样,但用到了random随机。

该方法与目录1.2.6类似。

# 方法二import randomimport numpy as npimport matplotlib.pyplot as plt
x_data = [1.0, 2.0, 3.0]y_data = [2.0, 4.0, 6.0]
scope_list = [] # 训练轮数w_list = [] # 权值
w = 60
# 学习率k = 0.01
for i in range(1, 201, 1): # 计算cost(即随机一个loss当cost用) rand = random.randint(0, 2) # 取值为[0,2],即 随机生成0~2内的 某个 整数,包含0和2。(三个样本,随机取一个) cost = 2 * x_data[rand] * (w * x_data[rand] - y_data[rand]) # 从而计算得到 关于W的偏导数
# 计算本轮w w = w - k * cost # 更新W print("Epoch=", i, "W=", w) # 本轮更新后的W 2023.2.13 20:30 scope_list.append(i) w_list.append(w)
plt.plot(scope_list, w_list) # 横坐标为轮数,纵坐标为权值。plt.xlabel("scope")plt.ylabel("W")plt.show()

如下图所示,可以看出,75轮左右,权值就已经逐渐收敛了~

以上内容来自《Python从入门到人工智能》,该专栏有CSDN优质博主精心打造,主打Python和人工智能,若您觉得文章写的不错,可以扫码购买该付费专栏哦,作者会不定时更新博文,且您有相关技术疑问也可直接咨询博主。

扫码直接购买Python从入门到人工智能》




好啦,今天的内容分享就到这,感觉不错的同学记得分享点赞哦!





    



    
点分享
点收藏
点点赞
点在看

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/153483