【NLP 5、深度学习的根本原理】

打印 上一主题 下一主题

主题 1020|帖子 1020|积分 3060

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目录
  一、梯度下降算法
  1.引例 —— 找极小值问题
  目标:
  方法:
  2.梯度
  例:
  3.求解目标
  为什么丧失函数越小越好
  4.梯度下降法
  代码实现
  5.细节问题
  6.梯度爆炸和梯度消失
  梯度爆炸
  梯度消失
  7.过拟合和欠拟合
  欠拟合(Underfitting)
  定义:
  原因:
  表现:
  过拟合(Overfitting)
  定义:
  原因:
  表现:
  解决方法
  解决欠拟合:
  解决过拟合:
  二、网络布局组件 —— 线性层(全连接层)
  定义网络布局的方法
  ① init初始化:
  ② forward函数,前向计算
  举例:
  nn.Linear函数:
  DNN深层神经网络组件
  三、网络布局组件 —— 激活函数(非线性函数)
  1.Sigmoid函数
  函数特点:
  公式:
  2.tanh函数 —— 双曲正切函数
  公式:
  3.Relu函数
  公式:
  4.Gelu函数
  公式:
  5.Softmax
  作用:
  公式:
  6.激活函数在线性层上的利用
  7.Softmax函数 和 Sigmoid函数的区别
  区别:
  四、网络布局组件 —— 丧失函数
  1.均方差 MSE mean square error
  调用:nn.MSE
  2.交叉熵 Cross Entropy
  公式:
  例:
  手动实现交叉熵的计算
  3.交叉熵与均方差丧失函数的实用范围
  五、基于pytorch框架编写练习模型
  1.设计模型
  2.前向传递(Forward Pass)
  3.预备练习数据 —— 随机天生
  4. 将练习数据整理为张量
  5.⭐模型练习
  ① 设置参数
  ② 建立模型
  ③ 选择优化器
  ④ 读取练习集
  ⑤ 练习过程
  6.模型评估函数evaluate
  7.模型练习好后怎样利用
  ① 模型练习好后生存模型
  ② 练习好的模型编写预测函数
  ③ 用模型做预测
  ④ 证明预测准确性
  
  全力以赴,终有绽放的那天
                                  —— 24.12.5
  一、梯度下降算法

1.引例 —— 找极小值问题

导数的正负衡量了函数在某一点的增减性
目标:

找到合适的x值,使得f(x)最小
方法:

1.任取一点x,计算在这一点的导数值f(x)
2.根据导数的正负,决定x应当调大还是调小
3.迭代进行1、2步,直到x不再厘革(或厘革极小)

原函数为 y = x^2        
导函数为 y = 2*x
在 x = -1 这个点,导数值为 -2,该点导数为负数,阐明在这一点,如果x增大,y会减小,以是f(x)最小值的点应当在-1的右侧(大于-1)

原函数为 y = x^2        
导函数为 y = 2*x
在 x = 1 这个点,导数值为 2,该点导数为正数,阐明在这一点,如果x增大,y会增大,以是f(x)最小值的点应当在1的左侧(小于1)

通过梯度寻找最小值会碰到局部最小值的问题

2.梯度

梯度:可以理解为多元函数的导数,意义与导数根本一致
例:



3.求解目标

找到一个模型权重,使得丧失函数最小(相对足够小),即预测值与真实值差异足够小
为什么丧失函数越小越好

① 丧失函数越小,模型越好
② 我们学习的目标是丧失函数最小化
③ 模型权重影响丧失函数值
④ 通过梯度下降法来找到最优权重

4.梯度下降法

梯度下降法也是基于求导,通过对每一点的求导计算其继续是增长还是下降,以此来判断该点是否是近似最优解(也就是增长/淘汰达到近似稳固)
根据梯度,更新权重
学习率控制步长(权重更新的幅度),学习率应该根据差异模型进行调整
梯度(导数值)也可以控制移动的步长巨细
全局最优解不愿定成立,很难寻找到最优解,但肯定要寻找相对最优解,只要模型最终可用就行
梯度下降法的公式既受到导数的控制又受到学习率的控制


代码实现

Yp = [func(i) for i in X]        
列表推导式(List Comprehension),创建一个新的列表 Yp。它的根本逻辑是遍历可迭代对象 X 中的每一个元素 i,将其作为参数传递给函数 func,然后把 func(i) 的返回结果依次收集起来,最终组成一个新的列表 Yp。
  1. import matplotlib.pyplot as pyplot
  2. import math
  3. import sys
  4. # 生成数据
  5. # X = [0.01 * x for x in range(100)]
  6. # Y = [2*x**2 + 3*x + 4 for x in X]
  7. # print(X)
  8. # print(Y)
  9. # pyplot.scatter(X, Y, color='red')
  10. # pyplot.show()
  11. # input()
  12. # 看作这是一百个样本
  13. X = [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18,
  14.      0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35000000000000003,
  15.      0.36, 0.37, 0.38, 0.39, 0.4, 0.41000000000000003, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47000000000000003, 0.48, 0.49,
  16.      0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.5700000000000001, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66,
  17.      0.67, 0.68, 0.6900000000000001, 0.7000000000000001, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8,
  18.      0.81, 0.8200000000000001, 0.8300000000000001, 0.84, 0.85,
  19.      0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.9400000000000001, 0.9500000000000001, 0.96, 0.97, 0.98, 0.99]
  20. Y = [4.0, 4.0302, 4.0608, 4.0918, 4.1232, 4.155, 4.1872, 4.2198, 4.2528, 4.2862, 4.32, 4.3542, 4.3888, 4.4238, 4.4592,
  21.      4.495, 4.5312, 4.5678, 4.6048, 4.6422, 4.68, 4.7181999999999995, 4.7568, 4.7958, 4.8352, 4.875, 4.9152000000000005,
  22.      4.9558, 4.9968, 5.0382, 5.08, 5.122199999999999, 5.1648, 5.2078, 5.2512, 5.295, 5.3392, 5.3838, 5.4288, 5.4742,
  23.      5.5200000000000005, 5.5662, 5.6128, 5.6598, 5.7072, 5.755, 5.8032, 5.851800000000001, 5.9008, 5.9502, 6.0, 6.0502,
  24.      6.1008, 6.1518, 6.203200000000001, 6.255000000000001, 6.3072, 6.3598, 6.4128, 6.4662, 6.52, 6.5742, 6.6288, 6.6838,
  25.      6.7392, 6.795, 6.8512, 6.9078, 6.9648, 7.022200000000001, 7.08, 7.138199999999999, 7.1968, 7.2558, 7.3152, 7.375,
  26.      7.4352, 7.4958, 7.5568, 7.6182, 7.680000000000001, 7.7422, 7.8048, 7.867800000000001, 7.9312, 7.994999999999999,
  27.      8.0592, 8.1238, 8.1888, 8.2542, 8.32, 8.3862, 8.4528, 8.5198, 8.587200000000001, 8.655000000000001, 8.7232, 8.7918,
  28.      8.8608, 8.9302]
  29. # 随机选定一个模型结构
  30. def func(x):
  31.     y_pred = w1 * x ** 2 + w2 * x + w3
  32.     return y_pred
  33. # 权重随机初始化
  34. w1, w2, w3 = 1, 0, -1
  35. # 拟定一个损失函数 均方差损失函数
  36. def loss(y_pred, y_true):
  37.     # y_pred = w1 * x ** 2 + w2 * x + w3
  38.     return (y_pred - y_true) ** 2
  39. # 学习率设置
  40. lr = 0.1
  41. # batch size 一次处理的样本数量
  42. batch_size = 20
  43. # 训练过程
  44. for epoch in range(1000):
  45.     epoch_loss = 0  # 损失值
  46.     grad_w1 = 0 # w1的梯度
  47.     grad_w2 = 0 # w2的梯度
  48.     grad_w3 = 0 # w3的梯度
  49.     counter = 0 # 计数
  50.     # 在 Python 中, zip()是一个内置函数,它用于将多个可迭代对象(如列表、元组等)中对应的元素打包成一个个元组,
  51.     # 然后返回一个由这些元组组成的可迭代对象(通常是一个 zip 对象)。
  52.     for x, y_true in zip(X, Y):
  53.         y_pred = func(x)
  54.         epoch_loss += loss(y_pred, y_true)
  55.         counter += 1
  56.         # 梯度计算,对损失函数求导,链式法则
  57.         # y_pred = w1 * x ** 2 + w2 * x + w3
  58.         # loss(y_pred, y_true) = (y_pred - y_true) ** 2
  59.         grad_w1 += 2 * (y_pred - y_true) * x ** 2
  60.         grad_w2 += 2 * (y_pred - y_true) * x
  61.         grad_w3 += 2 * (y_pred - y_true)
  62.         if counter == batch_size:
  63.             # 权重更新 新的权重 = 旧的权重 - 学习率 × 批次训练梯度和 / 每一批次的训练大小
  64.             w1 = w1 - lr * grad_w1 / batch_size  # sgd
  65.             w2 = w2 - lr * grad_w2 / batch_size
  66.             w3 = w3 - lr * grad_w3 / batch_size
  67.             counter = 0
  68.             grad_w1 = 0
  69.             grad_w2 = 0
  70.             grad_w3 = 0
  71.     # 每一轮批次训练后的损失值的平均值
  72.     epoch_loss /= len(X)
  73.     print("第%d轮, loss %f" % (epoch, epoch_loss))
  74.     # 当损失函数低于某一特定数时,训练就可以停止
  75.     if epoch_loss < 0.00001:
  76.         break
  77. print(f"训练后权重:w1:{w1} w2:{w2} w3:{w3}")
  78. # 使用训练后模型输出预测值
  79. Yp = [func(i) for i in X]
  80. # 预测值与真实值比对数据分布,可视化表示
  81. pyplot.scatter(X, Y, color="red")
  82. pyplot.scatter(X, Yp)
  83. pyplot.show()
复制代码
求出的练习后参数权重值与真实参数值接近



5.细节问题

原函数是什么不会影响优化流程,模型的选择与其原函数无关,函数本质上只是一个假设,我们只是根据数据来寻找它们之间的规律。原函数是什么是会影响最终的准确率。模型函数决定了表达本事的极限
原函数是什么只是会影响到最终的上限,而我们只能近似的寻找一个当前函数下相对精确的接近分布值,大概结果不对,但是预测也有意义,可以显示大致的趋势
学习率的巨细会影响练习速率(练习到达最终结果的轮数)
模型权重的随机初始化要在肯定的范围内,在差异的框架内范围差异
差异框架练习的权重是差异的,不宜过大
batch_size指用多少样本,来更新一次权重,batch_size过大会迭代速率变慢,产生资源问题
batch_size在大模型中设置的尺度是将显存占满而不爆掉,在小模型时batch_size可调治
梯度控制方向,学习率控制步长
一段样本数据中的梯度累加,以防此中有错误数据带来的影响
简单使命而言,batch_size小的时间反而练习会更快
学习率 × 梯度可以控制步长权重
lr 学习率由大致范围来控制学习率,模型越大学习率越小
项目中,权重参数的多少设置取决于必要挖掘规律的难易,
差异使命的模型函数一样平常不要求肯定,可以随机选择,只是预测结果的准确率差异
batch_size根据具体问题进行选择
loss丧失函数有常用的计算公式
梯度值过大,会使权重参数过大,进而会使原函数过大
学习率简直定在巨细模型中会有肯定范围,大模型的学习率相对更小
梯度的计算与丧失函数有关,由于梯度其实就是丧失函数对三个模型权重求偏导
权重就是模型函数中除了x和y的部分

6.梯度爆炸和梯度消失

梯度爆炸

在神经网络练习过程中,梯度爆炸是指计算得到的梯度(用于更新模型参数的梯度值)变得非常大,使得模型参数在更新时出现极大的厘革,从而导致练习过程不稳固乃至无法收敛。
梯度消失

与梯度爆炸相反,梯度消失是指在神经网络的反向传播过程中,梯度(用于更新模型参数的梯度值)变得非常小,险些接近于零,导致模型参数更新缓慢乃至克制更新。

7.过拟合和欠拟合

欠拟合(Underfitting)

定义:

欠拟合是指模型没有很好地捕捉到数据中的模式和关系,在练习数据上的表现就很差。例如,在一个回归问题中,模型的拟合曲线可能是一条简单的直线,但数据实际呈现出更复杂的曲线关系,导致模型无法准确地预测数据的真实值。
原因:

① 模型复杂度不敷:如利用线性模型来拟合一个本质上黑白线性的数据。
② 特征数目不敷:没有足够的信息来构建一个有效的模型。
表现:

在练习集上的丧失(如均方误差等)较高,模型预测值和真实值之间的差距较大。
模型在练习集和测试集上的性能都比较差,而且随着新数据的增加,性能也不会有明显的改善。

过拟合(Overfitting)

定义:

过拟合是指模型过度学习了练习数据中的细节和噪声,导致在练习数据上表现很好,但在新的、未见过的数据(测试数据)上表现不佳。模型变得过于复杂,它记住了练习数据的具体特征,而不是学习到数据背后的一样平通例律。
原因:

① 模型复杂度太高:例如在一个分类问题中,利用了一个非常复杂的神经网络,其参数数目远远超过了数据所能支持的公道范围,导致模型可以拟合练习数据中的每一个细节,包罗噪声。
② 练习数据不敷:模型在有限的数据中过度挖掘信息。如果数据量较小,模型很轻易记住全部的数据模式,当碰到新的数据时,由于没有学习到通用的规律,无法很好地进行预测。
表现:

在练习集上丧失很低,模型险些可以完美地预测练习数据中的样本。
在测试集上丧失很高,模型的泛化本事差。

解决方法

解决欠拟合:

① 增加模型的复杂度:如利用更高阶的多项式模型、增加神经网络的层数或神经元数目等。
② 增加更多的特征:通过特征工程获取更多与目标变量相关的有效信息。
解决过拟合:

① 低落模型复杂度:如对神经网络进行剪枝(淘汰神经元或连接)、采用正则化方法(如L1和L2正则化)来限定模型参数的巨细。
② 增加练习数据量:通过数据扩充(如在图像数据中进行旋转、翻转等利用)来提供更多的样本。 - 利用交叉验证等技术,更好地评估模型的泛化本事,调整模型超参数以找到合适的模型复杂度。

二、网络布局组件 —— 线性层(全连接层)

线性层,又称全连接层
计算公式y = w * x + b(b无关紧要,w固定是一个矩阵,形状通过输入数据决定)
w b 是到场练习的参数
w 的维度决定了隐含层输出的维度,一样平常称为隐单位个数(hidden size),如果公式中存在b,则b的维度与输出维度雷同
定义网络布局的方法

① init初始化:

声明网络模型中有哪几层
② forward函数,前向计算

定义网络的利用方式,声明模型计算过程
举例:

输入:x(维度1×3)        维度必要一致
隐含层1:w(维度3×5) ——> x × w:1 × 5
隐含层2:w(维度5×2) ——> x × w × w:1 × 2
一个线性层的拟合本事会比较差,线性层可以堆叠多层,但是输入输出的维度必要保持相一致 
  1.     # 前向计算
  2.     # 定义网络的使用方式
  3.     def forward(self, x):
  4.         # 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果x
  5.         x = self.layer1(x)   #shape: (batch_size, input_size) -> (batch_size, hidden_size1)
  6.         x = self.sig(x)
  7.         # 将隐含层1返回的结果赋值给x,将x再传入隐含层2,根据公式得到y的预测值
  8.         y_pred = self.layer2(x) #shape: (batch_size, hidden_size1) -> (batch_size, hidden_size2)
  9.         y_pred = self.relu(y_pred)
  10.         return y_pred
复制代码
nn.Linear函数:

nn.Linear:pytorch中的一个线性层函数 
in_features:输入的维度
out_features:输出的维度
bias:模型公式中需不必要参数 b(默认是true)


DNN深层神经网络组件

Pytorch网络的定义分为两层
第一层声明必要用到哪些层
第二层forward函数定义网络的利用方式
np.dot()函数,矩阵内积运算 
  1. #coding:utf8
  2. import torch
  3. import torch.nn as nn
  4. import numpy as np
  5. """
  6. numpy手动实现模拟一个线性层
  7. """
  8. #搭建一个2个线性层的神经网络模型
  9. #每层都是线性层
  10. class TorchModel(nn.Module):    # 继承Pytorch中的基类nn.Module
  11.     # 初始化环节
  12.     def __init__(self, input_size, hidden_size1, hidden_size2):
  13.         # input_size: 输入的特征维度,比如这里是3
  14.         # hidden_size1: 隐含层1(中间层)的维度,比如这里是5
  15.         # hidden_size2: 隐含层2(最后一层)的维度,比如这里是2
  16.         super(TorchModel, self).__init__()
  17.         # 隐含层1公式:wx + b , w:3 * 5
  18.         self.layer1 = nn.Linear(input_size, hidden_size1)
  19.         # 隐含层2公式:wx + b , w:5 * 2
  20.         self.layer2 = nn.Linear(hidden_size1, hidden_size2)
  21.     # 前向计算
  22.     # 定义网络的使用方式
  23.     def forward(self, x):
  24.         # 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果x
  25.         x = self.layer1(x)   #shape: (batch_size, input_size) -> (batch_size, hidden_size1)
  26.         # 将隐含层1返回的结果赋值给x,将x再传入隐含层2,根据公式得到y的预测值
  27.         y_pred = self.layer2(x) #shape: (batch_size, hidden_size1) -> (batch_size, hidden_size2)
  28.         return y_pred
  29. # 自定义模型
  30. class DiyModel:
  31.     def __init__(self, w1, b1, w2, b2):
  32.         self.w1 = w1
  33.         self.b1 = b1
  34.         self.w2 = w2
  35.         self.b2 = b2
  36.     def forward(self, x):
  37.         hidden = np.dot(x, self.w1.T) + self.b1 # 1*5
  38.         y_pred = np.dot(hidden, self.w2.T) + self.b2 # 1*2
  39.         return y_pred
  40. #随便准备一个网络输入
  41. x = np.array([[3.1, 1.3, 1.2],
  42.               [2.1, 1.3, 13]])
  43. #建立torch模型
  44. torch_model = TorchModel(3, 5, 2)
  45. print("建立的torch模型:")
  46. print(torch_model.state_dict())
  47. print("-----------")
  48. #打印模型权重,权重为随机初始化
  49. torch_model_w1 = torch_model.state_dict()["layer1.weight"].numpy()
  50. torch_model_b1 = torch_model.state_dict()["layer1.bias"].numpy()
  51. torch_model_w2 = torch_model.state_dict()["layer2.weight"].numpy()
  52. torch_model_b2 = torch_model.state_dict()["layer2.bias"].numpy()
  53. print(torch_model_w1, "torch w1 权重")
  54. print(torch_model_b1, "torch b1 权重")
  55. print("-----------")
  56. print(torch_model_w2, "torch w2 权重")
  57. print(torch_model_b2, "torch b2 权重")
  58. print("-----------")
  59. #使用torch模型做预测
  60. torch_x = torch.FloatTensor(x)  # 将矩阵转化为张量
  61. y_pred = torch_model.forward(torch_x)
  62. print("torch模型预测结果:", y_pred)
  63. # #把torch模型权重拿过来自己实现计算过程
  64. diy_model = DiyModel(torch_model_w1, torch_model_b1, torch_model_w2, torch_model_b2)
  65. # 用自己定义的模型来预测作对比
  66. y_pred_diy = diy_model.forward(np.array(x))
  67. print("diy模型预测结果:", y_pred_diy)
复制代码


三、网络布局组件 —— 激活函数(非线性函数)

很多情况下,神经网络只有线性层表达本事会不敷,拟合本事弱
为模型添加非线性因素(与线性函数结合在一起),使模型具有拟合非线性函数的本事
无激活函数时 y = w1(w2(w3 * x + b3) + b2) + b1 仍然是线性函数
1.Sigmoid函数

函数特点:

任意的输入经过sigmoid函数后,其取值都会变为0、1之间

公式:


sigmoid是一个非线性函数,将其和线性函数的线性层组合在一起,可使得线性函数的表达本事增强;可以拟合更复杂的规律;使得输出变为0、1之间(归一化)
* sigmoid函数的导函数等于其 自身 * (1 - 自身)

2.tanh函数 —— 双曲正切函数


公式:




3.Relu函数

分段函数,小于0的部分取值为0,大于0的部分取值为其自身
线性函数:求导后导数为0

公式:



4.Gelu函数


公式:



5.Softmax


作用:

向量归一化
公式:

e在数学和很多编程语言中,exp函数是指数函数,它通常表现以天然常数e为底的指数运算。即 y = exp(x) 等价于 y = e^x。



6.激活函数在线性层上的利用

  1. #搭建一个2个线性层的神经网络模型
  2. #每层都是线性层
  3. class TorchModel(nn.Module):    # 继承Pytorch中的基类nn.Module
  4.     # 初始化环节
  5.     def __init__(self, input_size, hidden_size1, hidden_size2):
  6.         # input_size: 输入的特征维度,比如这里是3
  7.         # hidden_size1: 隐含层1(中间层)的维度,比如这里是5
  8.         # hidden_size2: 隐含层2(最后一层)的维度,比如这里是2
  9.         super(TorchModel, self).__init__()
  10.         # 隐含层1公式:wx + b , w:3 * 5
  11.         self.layer1 = nn.Linear(input_size, hidden_size1)
  12.         self.sig = nn.Sigmoid()
  13.         # 隐含层2公式:wx + b , w:5 * 2
  14.         self.layer2 = nn.Linear(hidden_size1, hidden_size2)
  15.         self.relu = nn.ReLU()
  16.     # 前向计算
  17.     # 定义网络的使用方式
  18.     def forward(self, x):
  19.         # 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果x
  20.         x = self.layer1(x)   #shape: (batch_size, input_size) -> (batch_size, hidden_size1)
  21.         # 将第一层线性层经过模型的值返回,让其继续过激活函数simoid激活函数
  22.         x = self.sig(x)
  23.         # 将隐含层1返回的结果赋值给x,将x再传入隐含层2,根据公式得到y的预测值
  24.         y_pred = self.layer2(x) #shape: (batch_size, hidden_size1) -> (batch_size, hidden_size2)
  25.         # 将第二层线性层经过模型的值返回,让其继续过激活函数Relu激活函数
  26.         y_pred = self.relu(y_pred)
  27.         return y_pred
复制代码

7.Softmax函数 和 Sigmoid函数的区别

Sigmoid函数包管输入的每个值都在0,1之间;紧张运用于二分类问题
Softmax函数包管输入的全部值之和为1;紧张运用于多分类问题
Softmax归一化函数,调用方式①:nn.Softmax()、调用方式②:torch.softmax
sigmoid归一化函数,调用方式①:nn.Sigmoid()、调用方式②:torch.sigmoid
区别:

torch.sigmoid() 是一个函数,nn.Softmax是一个类,二者在模型定义时都不必要参数
torch.Softmax() 是一个类,必要两个参数:输入张量和维度,实例化时,必须传递 维度dim 参数来指定 softmax 利用的维度
        input:输入张量
        dim:指定在哪个维度上应用 softmax 函数。这是一个必须的参数,没有默认值
        dtype(可选):指定输出的数据类型。如果没有指定,输出将具有与输入雷同的数据类型
        out(可选): 指定输出张量。如果没有指定,将创建一个新的张量来存储输出
nn.Softmax() 是一个 torch.nn 模块中的类,而不是一个函数,必须有一个参数,实例化Softmax时,传入维度dim就行

四、网络布局组件 —— 丧失函数

1.均方差 MSE mean square error

调用:nn.MSE

两个数相减,再求平方
对均方差再做开根号,可以得到根方差
Xn:y_pred
Yn:y_true

一样平常可以求平均(mean),偶然也会求和(sum)


2.交叉熵 Cross Entropy

常用于分类使命,只实用于多选一的情况,由于全部类别已给出,以是精确的类别肯定在已知类别中,我们只需保留精确的类别上的概率分布即可
交叉熵公式作为丧失函数利用的前提是模型输出的概率值必须在(0,1)之间,各概率相加和为1(Softmax函数)
分类使命中,网络输出常常是全部类别上的概率分布
做一个分类使命,在每一种可能的概率分布是多少,看在哪一种可能上概率分布最大,则它应该属于哪一类
例:[0.1, 0.4, 0.5],则该样本应该属于第三类0.5
公式:

交叉熵只有精确的那类和这类对应的概率分布,错误类别上预测概率失效,事实上只有精确类别上预测的概率有效

公式中的负号是将log在(0,1)区间上的概率值求出,让概率值取正
p(x)logq(x)代表准确率,准确率在负区间越接近于0,则代表准确率越高,取负后代表丧失值越低,算出的交叉熵H越接近于0,即丧失值越小,越接近真实值 
Softmax函数进行归一化可以包管交叉熵的利用条件,即样本X不大于1,以是交叉熵与Softmax函数常搭配利用
Torch框架中,Softmax函数是交叉熵的一部分,利用交叉熵之前,默认利用一次Softmax函数进行归一化
例:

丧失函数loss:
假设一个三分类使命,某样本的精确标签是第一类,则p = [1, 0, 0],模型预测值假设为[0.5, 0.4, 0.1],则交叉熵计算如下:

手动实现交叉熵的计算

交叉熵在神经网络中的利用 :nn.CrossEntropyLoss()
  1. import torch
  2. import torch.nn as nn
  3. import numpy as np
  4. '''
  5. 手动实现交叉熵的计算
  6. '''
  7. #使用torch计算交叉熵
  8. ce_loss = nn.CrossEntropyLoss()
  9. #假设有3个样本(Batch_size = 3),每个预测都在做3分类
  10. pred = torch.FloatTensor([[1.3, 0.1, 0.3],
  11.                           [0.9, 1.2, 0.9],
  12.                           [0.5, 1.4, 0.2]]) #n * class_num
  13. #正确的类别分别为1,2,0
  14. target = torch.LongTensor([1,2,0])
  15. '''
  16. 等效类别划分语句:one-hot:
  17. target = torch.FloatTensor([[0,1,0],
  18.                            [0,0,1],
  19.                            [1,0,0]])
  20. '''
  21. # 使用交叉熵计算损失函数
  22. loss = ce_loss(pred, target)
  23. print("torch输出交叉熵:", loss)
  24. #手动实现交叉熵
  25. def cross_entropy(pred, target):
  26.     batch_size, class_num = pred.shape
  27.     pred = softmax(pred)
  28.     target = to_one_hot(target, pred.shape)
  29.     entropy = - np.sum(target * np.log(pred), axis=1)
  30.     return sum(entropy) / batch_size
  31. #实现softmax函数 进行归一化
  32. def softmax(matrix):
  33.     # exp函数是e的指数函数,exp(x) = e ^ x,x为任意实数或复数
  34.     return np.exp(matrix) / np.sum(np.exp(matrix), axis=1, keepdims=True)
  35. #验证softmax函数
  36. # print(torch.softmax(pred, dim=1))
  37. # print(softmax(pred.numpy()))
  38. #将输入转化为onehot矩阵型
  39. def to_one_hot(target, shape):
  40.     one_hot_target = np.zeros(shape)
  41.     for i, t in enumerate(target):
  42.         one_hot_target[i][t] = 1
  43.     return one_hot_target
  44. print("手动实现交叉熵:", cross_entropy(pred.numpy(), target.numpy()))
复制代码


3.交叉熵与均方差丧失函数的实用范围

交叉熵处理多选一使命
均方差得当回归使命——即预测值为一个概率值的问题
loss丧失函数是我们优化的目标,我们要让loss函数,也就是丧失值尽可能减小,使预测值与真实值之间差距尽可能小
而差异的丧失函数只是恰恰符合使模型的丧失函数尽可能小的条件

五、基于pytorch框架编写练习模型

实现一个自行构造的找规律(机器学习)使命
规律:x是一个5维向量,如果第1个数 > 第5个数,则为正样本,反之为负样本
1.设计模型

① 用一个线性层(输入维度5,输出维度1)作为第一层模型函数
② 定义一个激活函数sigmoid
③ 设计一个loss函数均方差丧失函数mse_loss
  1. class TorchModel(nn.Module):
  2.     def __init__(self, input_size):
  3.         super(TorchModel, self).__init__()
  4.         self.linear = nn.Linear(input_size, 1)  # 线性层
  5.         self.activation = nn.Sigmoid()    # 激活函数 或写为torch.sigmoid
  6.         self.loss = nn.functional.mse_loss  # loss函数采用均方差损失
复制代码

2.前向传递(Forward Pass)

参数列表中加一个可选项y,如果不传参数y,y默认为None,则让样本x先过线性层,再过激活层,得到预测值y_pred,如果传入参数y,则通过均方差丧失函数计算丧失值loss
  1.     # 当输入真实标签,返回loss值;无真实标签,返回预测值
  2.     def forward(self, x, y=None):
  3.         x = self.linear(x)  # (batch_size, input_size) -> (batch_size, 1)
  4.         y_pred = self.activation(x)  # (batch_size, 1) -> (batch_size, 1)
  5.         if y is not None:
  6.             return self.loss(y_pred, y)  # 预测值和真实值计算损失
  7.         else:
  8.             return y_pred  # 输出预测结果
复制代码

3.预备练习数据 —— 随机天生

天生一个样本,样本的天生方法,代表了我们要学习的规律
随机天生一个5维向量,如果第一个值大于第五个值,认为是正样本,反之为负样本
  1. class TorchModel(nn.Module):
  2.     # 生成一个样本, 样本的生成方法,代表了我们要学习的规律
  3.     # 随机生成一个5维向量,如果第一个值大于第五个值,认为是正样本,反之为负样本
  4.     def build_sample():
  5.         x = np.random.random(5)
  6.         if x[0] > x[4]:
  7.             return x, 1
  8.         else:
  9.             return x, 0
复制代码

4. 将练习数据整理为张量

随机天生一批样本,正负样本匀称天生
注: 如果是标量,要转化为张量,则必要在标量外加一个“[]”,如果是向量,转化为张量则不必要加"[]"
  1. # 随机生成一批样本
  2. # 正负样本均匀生成
  3.     def build_dataset(total_sample_num):
  4.         X = []
  5.         Y = []
  6.         for i in range(total_sample_num):
  7.             x, y = build_sample()
  8.             X.append(x)
  9.             Y.append([y])
  10.             X_array = np.array(X)
  11.             Y_array = np.array(Y)
  12.         return torch.FloatTensor(X_array), torch.FloatTensor(Y_array)
复制代码

5.⭐模型练习

① 设置参数

设置模型练习中必要用到的参数(练习轮次、每轮次练习样本数、学习率、样本总数、输入向量维度)
② 建立模型

调用定义的模型
③ 选择优化器

通过计算的梯度选择优化器公式,对模型参数权重进行优化
④ 读取练习集

从构造的练习数据集上读取练习数据,对模型进行练习
⑤ 练习过程

Ⅰ、model.train():设置练习模式
Ⅱ、对练习集样本开始循环练习(循环取出练习数据)
Ⅲ、根据模型函数和丧失函数的定义计算模型丧失
Ⅳ、计算梯度
Ⅴ、通过梯度用优化器更新权重
Ⅵ、计算完一轮练习数据后梯度进行归零,下一轮重新计算
  1. def main():
  2.     # 配置参数
  3.     epoch_num = 20  # 训练轮数
  4.     batch_size = 20  # 每次训练样本个数
  5.     train_sample = 5000  # 每轮训练总共训练的样本总数
  6.     input_size = 5  # 输入向量维度
  7.     learning_rate = 0.001  # 学习率
  8.     # 建立模型
  9.     model = TorchModel(input_size)
  10.     # 选择优化器 model.parameters()    指定要优化哪个模型的参数
  11.     optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
  12.     log = []
  13.     # 创建训练集,正常任务是读取训练集
  14.     train_x, train_y = build_dataset(train_sample)
  15.     # 训练过程
  16.     for epoch in range(epoch_num):
  17.         model.train()
  18.         watch_loss = []
  19.         for batch_index in range(train_sample // batch_size):   
  20.             x = train_x[batch_index * batch_size : (batch_index + 1) * batch_size]
  21.             y = train_y[batch_index * batch_size : (batch_index + 1) * batch_size]
  22.             loss = model(x, y)  # 计算loss  model.forward(x,y)
  23.             loss.backward()  # 计算梯度
  24.             optim.step()  # 更新权重
  25.             optim.zero_grad()  # 梯度归零
  26.             watch_loss.append(loss.item())
  27.         print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
  28.         acc = evaluate(model)  # 测试本轮模型结果
  29.         log.append([acc, float(np.mean(watch_loss))])
  30.     # 保存模型
  31.     torch.save(model.state_dict(), "model.bin")
  32.     # 画图
  33.     print(log)
  34.     plt.plot(range(len(log)), [l[0] for l in log], label="acc")  # 画acc曲线
  35.     plt.plot(range(len(log)), [l[1] for l in log], label="loss")  # 画loss曲线
  36.     plt.legend()
  37.     plt.show()
  38.     return
复制代码

6.模型评估函数evaluate

evaluate相称于测试部分,用来测试每轮模型预测的精确率
model.eval(),声明模型框架在这个函数中不做练习
指定模型测试数目
天生测试样本(读取测试样本)
声明测试函数中计算梯度:with torch.no_grad()
模型中,不传入参数已知样本y,天生预测样本
模型预测值与样本真实值进行比较,判断预测的精确与否
计算样本预测精确率
  1. # 测试代码
  2. # 用来测试每轮模型的准确率
  3. def evaluate(model):
  4.     model.eval()
  5.     test_sample_num = 100
  6.     x, y = build_dataset(test_sample_num)
  7.     print("本次预测集中共有%d个正样本,%d个负样本" % (sum(y), test_sample_num - sum(y)))
  8.     correct, wrong = 0, 0
  9.     with torch.no_grad():
  10.         y_pred = model(x)  # 模型预测 model.forward(x)
  11.         for y_p, y_t in zip(y_pred, y):  # 与真实标签进行对比
  12.             if float(y_p) < 0.5 and int(y_t) == 0:
  13.                 correct += 1  # 负样本判断正确
  14.             elif float(y_p) >= 0.5 and int(y_t) == 1:
  15.                 correct += 1  # 正样本判断正确
  16.             else:
  17.                 wrong += 1
  18.     print("正确预测个数:%d, 正确率:%f" % (correct, correct / (correct + wrong)))
  19.     return correct / (correct + wrong)
复制代码

7.模型练习好后怎样利用

① 模型练习好后生存模型

  1.     # 保存模型
  2.     torch.save(model.state_dict(), "model.bin")
复制代码
② 练习好的模型编写预测函数

Ⅰ、模型初始化:model = TorchModel(input_size)
Ⅱ、按照路径读取模型:torch.load(model_path)
Ⅲ、加载练习好的权重:model.load_state_dict(torch.load(model_path))  
  1. # 使用训练好的模型做预测
  2. def predict(model_path, input_vec):
  3.     input_size = 5
  4.     model = TorchModel(input_size)
  5.     model.load_state_dict(torch.load(model_path))  # 加载训练好的权重
  6.     print(model.state_dict())
  7.     model.eval()  # 测试模式
  8.     with torch.no_grad():  # 不计算梯度
  9.         result = model.forward(torch.FloatTensor(input_vec))  # 模型预测
  10.     for vec, res in zip(input_vec, result):
  11.         print("输入:%s, 预测类别:%d, 概率值:%f" % (vec, round(float(res)), res))  # 打印结果
复制代码
③ 用模型做预测

  1. if __name__ == "__main__":
  2.     test_vec = [[0.97889086,0.15229675,0.31082123,0.03504317,0.88920843],
  3.                 [0.74963533,0.5524256,0.95758807,0.95520434,0.84890681],
  4.                 [0.00797868,0.67482528,0.13625847,0.34675372,0.19871392],
  5.                 [0.09349776,0.59416669,0.92579291,0.41567412,0.1358894]]
  6.     predict("model.bin", test_vec)
复制代码
④ 证明预测准确性





免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

没腿的鸟

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表