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

目录

一、梯度下降算法

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

import matplotlib.pyplot as pyplot
import math
import sys# 生成数据
# X = [0.01 * x for x in range(100)]
# Y = [2*x**2 + 3*x + 4 for x in X]
# print(X)
# print(Y)
# pyplot.scatter(X, Y, color='red')
# pyplot.show()
# input()# 看作这是一百个样本
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,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,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,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,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,0.81, 0.8200000000000001, 0.8300000000000001, 0.84, 0.85,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]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,4.495, 4.5312, 4.5678, 4.6048, 4.6422, 4.68, 4.7181999999999995, 4.7568, 4.7958, 4.8352, 4.875, 4.9152000000000005,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,5.5200000000000005, 5.5662, 5.6128, 5.6598, 5.7072, 5.755, 5.8032, 5.851800000000001, 5.9008, 5.9502, 6.0, 6.0502,6.1008, 6.1518, 6.203200000000001, 6.255000000000001, 6.3072, 6.3598, 6.4128, 6.4662, 6.52, 6.5742, 6.6288, 6.6838,6.7392, 6.795, 6.8512, 6.9078, 6.9648, 7.022200000000001, 7.08, 7.138199999999999, 7.1968, 7.2558, 7.3152, 7.375,7.4352, 7.4958, 7.5568, 7.6182, 7.680000000000001, 7.7422, 7.8048, 7.867800000000001, 7.9312, 7.994999999999999,8.0592, 8.1238, 8.1888, 8.2542, 8.32, 8.3862, 8.4528, 8.5198, 8.587200000000001, 8.655000000000001, 8.7232, 8.7918,8.8608, 8.9302]# 随机选定一个模型结构
def func(x):y_pred = w1 * x ** 2 + w2 * x + w3return y_pred# 权重随机初始化
w1, w2, w3 = 1, 0, -1# 拟定一个损失函数 均方差损失函数
def loss(y_pred, y_true):# y_pred = w1 * x ** 2 + w2 * x + w3return (y_pred - y_true) ** 2# 学习率设置
lr = 0.1# batch size 一次处理的样本数量
batch_size = 20# 训练过程
for epoch in range(1000):epoch_loss = 0  # 损失值grad_w1 = 0 # w1的梯度grad_w2 = 0 # w2的梯度grad_w3 = 0 # w3的梯度counter = 0 # 计数# 在 Python 中, zip()是一个内置函数,它用于将多个可迭代对象(如列表、元组等)中对应的元素打包成一个个元组,# 然后返回一个由这些元组组成的可迭代对象(通常是一个 zip 对象)。for x, y_true in zip(X, Y):y_pred = func(x)epoch_loss += loss(y_pred, y_true)counter += 1# 梯度计算,对损失函数求导,链式法则# y_pred = w1 * x ** 2 + w2 * x + w3# loss(y_pred, y_true) = (y_pred - y_true) ** 2grad_w1 += 2 * (y_pred - y_true) * x ** 2grad_w2 += 2 * (y_pred - y_true) * xgrad_w3 += 2 * (y_pred - y_true)if counter == batch_size:# 权重更新 新的权重 = 旧的权重 - 学习率 × 批次训练梯度和 / 每一批次的训练大小w1 = w1 - lr * grad_w1 / batch_size  # sgdw2 = w2 - lr * grad_w2 / batch_sizew3 = w3 - lr * grad_w3 / batch_sizecounter = 0grad_w1 = 0grad_w2 = 0grad_w3 = 0# 每一轮批次训练后的损失值的平均值epoch_loss /= len(X)print("第%d轮, loss %f" % (epoch, epoch_loss))# 当损失函数低于某一特定数时,训练就可以停止if epoch_loss < 0.00001:breakprint(f"训练后权重:w1:{w1} w2:{w2} w3:{w3}")# 使用训练后模型输出预测值
Yp = [func(i) for i in X]# 预测值与真实值比对数据分布,可视化表示
pyplot.scatter(X, Y, color="red")
pyplot.scatter(X, Yp)
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

一个线性层的拟合能力会比较差,线性层可以堆叠多层,但是输入输出的维度需要保持相一致 

    # 前向计算# 定义网络的使用方式def forward(self, x):# 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果xx = self.layer1(x)   #shape: (batch_size, input_size) -> (batch_size, hidden_size1)x = self.sig(x)# 将隐含层1返回的结果赋值给x,将x再传入隐含层2,根据公式得到y的预测值y_pred = self.layer2(x) #shape: (batch_size, hidden_size1) -> (batch_size, hidden_size2)y_pred = self.relu(y_pred)return y_pred

nn.Linear函数:

nn.Linear:pytorch中的一个线性层函数 

in_features:输入的维度

out_features:输出的维度

bias:模型公式中需不需要参数 b(默认是true)


DNN深层神经网络组件

Pytorch网络的定义分为两层

第一层声明需要用到哪些层

第二层forward函数定义网络的使用方式

np.dot()函数,矩阵内积运算 

#coding:utf8import torch
import torch.nn as nn
import numpy as np"""
numpy手动实现模拟一个线性层
"""#搭建一个2个线性层的神经网络模型
#每层都是线性层
class TorchModel(nn.Module):    # 继承Pytorch中的基类nn.Module# 初始化环节def __init__(self, input_size, hidden_size1, hidden_size2):# input_size: 输入的特征维度,比如这里是3# hidden_size1: 隐含层1(中间层)的维度,比如这里是5# hidden_size2: 隐含层2(最后一层)的维度,比如这里是2super(TorchModel, self).__init__()# 隐含层1公式:wx + b , w:3 * 5self.layer1 = nn.Linear(input_size, hidden_size1)# 隐含层2公式:wx + b , w:5 * 2self.layer2 = nn.Linear(hidden_size1, hidden_size2)# 前向计算# 定义网络的使用方式def forward(self, x):# 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果xx = self.layer1(x)   #shape: (batch_size, input_size) -> (batch_size, hidden_size1)# 将隐含层1返回的结果赋值给x,将x再传入隐含层2,根据公式得到y的预测值y_pred = self.layer2(x) #shape: (batch_size, hidden_size1) -> (batch_size, hidden_size2)return y_pred# 自定义模型
class DiyModel:def __init__(self, w1, b1, w2, b2):self.w1 = w1self.b1 = b1self.w2 = w2self.b2 = b2def forward(self, x):hidden = np.dot(x, self.w1.T) + self.b1 # 1*5y_pred = np.dot(hidden, self.w2.T) + self.b2 # 1*2return y_pred#随便准备一个网络输入
x = np.array([[3.1, 1.3, 1.2],[2.1, 1.3, 13]])
#建立torch模型
torch_model = TorchModel(3, 5, 2)print("建立的torch模型:")
print(torch_model.state_dict())print("-----------")
#打印模型权重,权重为随机初始化
torch_model_w1 = torch_model.state_dict()["layer1.weight"].numpy()
torch_model_b1 = torch_model.state_dict()["layer1.bias"].numpy()
torch_model_w2 = torch_model.state_dict()["layer2.weight"].numpy()
torch_model_b2 = torch_model.state_dict()["layer2.bias"].numpy()
print(torch_model_w1, "torch w1 权重")
print(torch_model_b1, "torch b1 权重")
print("-----------")
print(torch_model_w2, "torch w2 权重")
print(torch_model_b2, "torch b2 权重")
print("-----------")#使用torch模型做预测
torch_x = torch.FloatTensor(x)  # 将矩阵转化为张量
y_pred = torch_model.forward(torch_x)
print("torch模型预测结果:", y_pred)# #把torch模型权重拿过来自己实现计算过程
diy_model = DiyModel(torch_model_w1, torch_model_b1, torch_model_w2, torch_model_b2)
# 用自己定义的模型来预测作对比
y_pred_diy = diy_model.forward(np.array(x))
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.激活函数在线性层上的使用

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

import torch
import torch.nn as nn
import numpy as np'''
手动实现交叉熵的计算
'''#使用torch计算交叉熵
ce_loss = nn.CrossEntropyLoss()#假设有3个样本(Batch_size = 3),每个预测都在做3分类
pred = torch.FloatTensor([[1.3, 0.1, 0.3],[0.9, 1.2, 0.9],[0.5, 1.4, 0.2]]) #n * class_num#正确的类别分别为1,2,0
target = torch.LongTensor([1,2,0])'''
等效类别划分语句:one-hot:
target = torch.FloatTensor([[0,1,0],[0,0,1],[1,0,0]])
'''# 使用交叉熵计算损失函数
loss = ce_loss(pred, target)
print("torch输出交叉熵:", loss)#手动实现交叉熵
def cross_entropy(pred, target):batch_size, class_num = pred.shapepred = softmax(pred)target = to_one_hot(target, pred.shape)entropy = - np.sum(target * np.log(pred), axis=1)return sum(entropy) / batch_size#实现softmax函数 进行归一化
def softmax(matrix):# exp函数是e的指数函数,exp(x) = e ^ x,x为任意实数或复数return np.exp(matrix) / np.sum(np.exp(matrix), axis=1, keepdims=True)#验证softmax函数
# print(torch.softmax(pred, dim=1))
# print(softmax(pred.numpy()))#将输入转化为onehot矩阵型
def to_one_hot(target, shape):one_hot_target = np.zeros(shape)for i, t in enumerate(target):one_hot_target[i][t] = 1return one_hot_targetprint("手动实现交叉熵:", cross_entropy(pred.numpy(), target.numpy()))


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

交叉熵处理多选一任务

均方差适合回归任务——即预测值为一个概率值的问题

loss损失函数是我们优化的目标,我们要让loss函数,也就是损失值尽可能减小,使预测值与真实值之间差距尽可能小

而不同的损失函数只是恰好符合使模型的损失函数尽可能小的条件


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

实现一个自行构造的找规律(机器学习)任务

规律:x是一个5维向量,如果第1个数 > 第5个数,则为正样本,反之为负样本

1.设计模型

① 用一个线性层(输入维度5,输出维度1)作为第一层模型函数

② 定义一个激活函数sigmoid

③ 设计一个loss函数均方差损失函数mse_loss

class TorchModel(nn.Module):def __init__(self, input_size):super(TorchModel, self).__init__()self.linear = nn.Linear(input_size, 1)  # 线性层self.activation = nn.Sigmoid()    # 激活函数 或写为torch.sigmoidself.loss = nn.functional.mse_loss  # loss函数采用均方差损失

2.前向传递(Forward Pass)

参数列表中加一个可选项y,如果不传参数y,y默认为None,则让样本x先过线性层,再过激活层,得到预测值y_pred,如果传入参数y,则通过均方差损失函数计算损失值loss

    # 当输入真实标签,返回loss值;无真实标签,返回预测值def forward(self, x, y=None):x = self.linear(x)  # (batch_size, input_size) -> (batch_size, 1)y_pred = self.activation(x)  # (batch_size, 1) -> (batch_size, 1)if y is not None:return self.loss(y_pred, y)  # 预测值和真实值计算损失else:return y_pred  # 输出预测结果

3.准备训练数据 —— 随机生成

生成一个样本,样本的生成方法,代表了我们要学习的规律

随机生成一个5维向量,如果第一个值大于第五个值,认为是正样本,反之为负样本

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

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

随机生成一批样本,正负样本均匀生成

注: 如果是标量,要转化为张量,则需要在标量外加一个“[]”,如果是向量,转化为张量则不需要加"[]"

# 随机生成一批样本
# 正负样本均匀生成def build_dataset(total_sample_num):X = []Y = []for i in range(total_sample_num):x, y = build_sample()X.append(x)Y.append([y])X_array = np.array(X)Y_array = np.array(Y)return torch.FloatTensor(X_array), torch.FloatTensor(Y_array)

5.⭐模型训练

① 配置参数

配置模型训练中需要用到的参数(训练轮次、每轮次训练样本数、学习率、样本总数、输入向量维度)

② 建立模型

调用定义的模型

③ 选择优化器

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

④ 读取训练集

从构造的训练数据集上读取训练数据,对模型进行训练

⑤ 训练过程

Ⅰ、model.train():设置训练模式

Ⅱ、对训练集样本开始循环训练(循环取出训练数据)

Ⅲ、根据模型函数和损失函数的定义计算模型损失

Ⅳ、计算梯度

Ⅴ、通过梯度用优化器更新权重

Ⅵ、计算完一轮训练数据后梯度进行归零,下一轮重新计算

def main():# 配置参数epoch_num = 20  # 训练轮数batch_size = 20  # 每次训练样本个数train_sample = 5000  # 每轮训练总共训练的样本总数input_size = 5  # 输入向量维度learning_rate = 0.001  # 学习率# 建立模型model = TorchModel(input_size)# 选择优化器 model.parameters()    指定要优化哪个模型的参数optim = torch.optim.Adam(model.parameters(), lr=learning_rate)log = []# 创建训练集,正常任务是读取训练集train_x, train_y = build_dataset(train_sample)# 训练过程for epoch in range(epoch_num):model.train()watch_loss = []for batch_index in range(train_sample // batch_size):    x = train_x[batch_index * batch_size : (batch_index + 1) * batch_size]y = train_y[batch_index * batch_size : (batch_index + 1) * batch_size]loss = model(x, y)  # 计算loss  model.forward(x,y)loss.backward()  # 计算梯度optim.step()  # 更新权重optim.zero_grad()  # 梯度归零watch_loss.append(loss.item())print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))acc = evaluate(model)  # 测试本轮模型结果log.append([acc, float(np.mean(watch_loss))])# 保存模型torch.save(model.state_dict(), "model.bin")# 画图print(log)plt.plot(range(len(log)), [l[0] for l in log], label="acc")  # 画acc曲线plt.plot(range(len(log)), [l[1] for l in log], label="loss")  # 画loss曲线plt.legend()plt.show()return

6.模型评估函数evaluate

evaluate相当于测试部分,用来测试每轮模型预测的精确率

model.eval(),声明模型框架在这个函数中不做训练

指定模型测试数量

生成测试样本(读取测试样本)

声明测试函数中计算梯度with torch.no_grad()

模型中,不传入参数已知样本y,生成预测样本

模型预测值与样本真实值进行比较判断预测的正确与否

计算样本预测正确率

# 测试代码
# 用来测试每轮模型的准确率
def evaluate(model):model.eval()test_sample_num = 100x, y = build_dataset(test_sample_num)print("本次预测集中共有%d个正样本,%d个负样本" % (sum(y), test_sample_num - sum(y)))correct, wrong = 0, 0with torch.no_grad():y_pred = model(x)  # 模型预测 model.forward(x)for y_p, y_t in zip(y_pred, y):  # 与真实标签进行对比if float(y_p) < 0.5 and int(y_t) == 0:correct += 1  # 负样本判断正确elif float(y_p) >= 0.5 and int(y_t) == 1:correct += 1  # 正样本判断正确else:wrong += 1print("正确预测个数:%d, 正确率:%f" % (correct, correct / (correct + wrong)))return correct / (correct + wrong)

7.模型训练好后如何使用

① 模型训练好后保存模型

    # 保存模型torch.save(model.state_dict(), "model.bin")

② 训练好的模型编写预测函数

Ⅰ、模型初始化:model = TorchModel(input_size)

Ⅱ、按照路径读取模型:torch.load(model_path)

Ⅲ、加载训练好的权重:model.load_state_dict(torch.load(model_path))  

# 使用训练好的模型做预测
def predict(model_path, input_vec):input_size = 5model = TorchModel(input_size)model.load_state_dict(torch.load(model_path))  # 加载训练好的权重print(model.state_dict())model.eval()  # 测试模式with torch.no_grad():  # 不计算梯度result = model.forward(torch.FloatTensor(input_vec))  # 模型预测for vec, res in zip(input_vec, result):print("输入:%s, 预测类别:%d, 概率值:%f" % (vec, round(float(res)), res))  # 打印结果

③ 用模型做预测

if __name__ == "__main__":test_vec = [[0.97889086,0.15229675,0.31082123,0.03504317,0.88920843],[0.74963533,0.5524256,0.95758807,0.95520434,0.84890681],[0.00797868,0.67482528,0.13625847,0.34675372,0.19871392],[0.09349776,0.59416669,0.92579291,0.41567412,0.1358894]]predict("model.bin", test_vec)

④ 证明预测准确性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/34535.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

云计算对定制软件开发的影响

在当代数字世界中&#xff0c;云计算是改变许多行业&#xff08;包括定制软件开发&#xff09;的最伟大的革命性趋势之一。由于这些公司努力寻求更好、更多不同的方式来履行职责&#xff0c;因此云计算与传统的内部部署基础设施相比具有许多不可否认的优势。这种范式转变对定制…

智能交通(8)——腾讯开悟智能交通信号灯调度赛道

本文档用于记录参加腾讯开悟智能信号灯调度赛道的模型优化过程。官方提供了dqn和target_dqn算法&#xff0c;模型的优化在官方提供的代码基础上进行。最终排名是在榜单16&#xff0c;没能进入最后的决赛。 一.赛题介绍 赛题简介&#xff1a;在本地赛题中&#xff0c;参赛团队…

RK3568平台开发系列讲解(IIO篇)IIO缓冲区

🚀返回专栏总目录 文章目录 一、IIO缓冲区的sysfs接口二、IIO缓冲区设置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢IIO缓冲区提供连续的数据捕获,一次可以同时读取多个数据通道。可通过dev/iio:device字符设备节点从用户空间访问缓冲区。在触发器处理程序中,用…

mybatis用pagehelper 然后用CountJSqlParser45,发现自己手写的mapper查询效率很慢

如题 效率慢疑惑 效率慢 分页查询,发现效率很慢,然后发现是比较复杂的sql,CountJSqlParser45它不会帮忙优化掉,就是select多少字段它count的时候也还是这么多字段 框架里的用法是这样的 所以去看了CountJSqlParser45里面的代码,发现如果有group之类的,它就不帮忙把count优化…

k8s,dameonset

Docker容器里应用的日志&#xff0c;默认会保存在宿主机的/var/lib/docker/containers/{{.容器ID}}/{{.容器ID}}-json.log文件里 nodeAffinity toleration DaemonSet可以继续版本回退&#xff0c; kubectl rollout status命令看到这个“滚动更新”的过程&#xff0c;可以查看…

macmini安装ubuntu网卡驱动BCM4360

安装成功效果如下 成功连接wifi 成功分配到IP 执行命令如下 1. sudo apt update 2. sudo apt install broadcom-sta-dkms 3. 重启电脑

OSCP - Proving Grounds - Fanatastic

主要知识点 CVE-2021-43798漏洞利用 具体步骤 执行nmap 扫描&#xff0c;22/3000/9090端口开放&#xff0c;应该是ssh,grafana 和Prometheus Nmap scan report for 192.168.52.181 Host is up (0.00081s latency). Not shown: 65532 closed tcp ports (reset) PORT STA…

24/12/5 算法笔记<强化学习> doubleDQN,duelingDQN

我们前面了解了DQN网络的一些知识&#xff0c;然而DQN还有一些改进的方法&#xff0c;比如doubleDQN和duelingDQN,我们先来将一下doubleDQN和DQN. 先来对比一下 单一网络 vs. 双重网络 DQN:是一个深度神经网络来估计每个动作的Q值 DDQN:使用两个独立的深度神经网络&#xf…

时长输入框、用时输入框

时长输入框、用时输入框 行内组件&#xff0c;用于设定 【时 分 秒】 时长&#xff0c;转存结果为 【毫秒】 <template><div class"time-inputs"><div classtime-input-div v-ifinput><input classtime-input type"number" v-mod…

java反射的基础应用

一、堆内存中的情况 &#xff08;一&#xff09;对象创建与变化 源对象&#xff08;UserBo&#xff09;和目标对象&#xff08;User&#xff09; 在执行UserBo sourceObj new UserBo(username, password);和User targetObj new User();时&#xff0c;分别在堆内存中创建了U…

Unity3D常见的设计模式(五)

系列文章目录 unity知识点 文章目录 系列文章目录&#x1f449;前言&#x1f449;一、桥接模式&#xff08;Bridge&#xff09;&#x1f449;二、适配器模式&#xff08;Adapter&#xff09;&#x1f449;三、 外观模式&#xff08;Facade&#xff09;&#x1f449;四、原型模…

车机端同步outlook日历

最近在开发一个车机上的日历助手&#xff0c;其中一个需求就是要实现手机端日历和车机端日历数据的同步。然而这种需求似乎没办法实现&#xff0c;毕竟手机日历是手机厂商自己带的系统应用&#xff0c;根本不能和车机端实现数据同步的。 那么只能去其他公共的平台寻求一些机会&…

群控系统服务端开发模式-应用开发-邮件工厂结构封装

首先在系统根目录下extend文件夹下创建邮件工厂文件夹并更名叫Mail。 一、邮件发送父类 在Mail目录下创建邮件发送父类并更名为MailSenderInterface.php&#xff0c;代码如下 <?php /*** 邮件发送父类* User: 龙哥三年风水* Date: 2024/12/5* Time: 14:22*/ namespace Ma…

React性能优化

三个可以优化的地方 避免过度多次渲染 组件会在以下情况下重新渲染 注意&#xff1a;例如组件组合的形式&#xff0c;<Test><Counter></Counter></Test>,即使Test发生了重新渲染&#xff0c;Counter也不会重新渲染。另外使用React这样的库或框架时&a…

操作系统学习

问题&#xff1a; 因为想用傲梅来给系统盘扩容&#xff0c;导致无法进入操作系统&#xff0c;报错如下&#xff1a; 无法加载应用程序或操作系统&#xff0c;原因是所需文件丢失或包含错误. 文件:Windowslsystem32lwinload.efi错误代码: 0xc000007b 你需要使用恢复工具。如果…

Web应用程序文件包含-Server2233-解析

B-6 Web应用程序文件包含 任务环境说明:服务器场景名称:Server2233

计算机的错误计算(一百七十五)

摘要 就计算机的错误计算&#xff08;一百七十三&#xff09;与&#xff08;一百七十四&#xff09;中案例&#xff0c;讨论其另外一种形式&#xff1a; 的错误计算问题。 例1. 已知 计算 直接贴图吧&#xff1a; 这样&#xff0c;MATLAB的输出中有4位错误数字&#xff0…

F-K变换法

f-k变换实质上是一种二维傅里叶变换。 在时间域上,对一道地震信号做傅里叶变换,可以得到在时间上不同频度(称为频率)的 波动组分的振幅和相位信息。同样地,在空间上,我们也可以对多道地震信号做类似傅里叶变 换的数值变换,得到在空间上不同频度(称为波数)的波动组分的振幅…

webstorm关闭了弹窗 Confirm Exit 恢复回来

webstorm关闭了 Confirm Exit WebStorm 关闭了“Confirm Exit”功能&#xff0c;意味着当用户尝试关闭IDE时&#xff0c;不会显示确认对话框&#xff0c;直接退出程序。 如果你想要恢复这个功能&#xff0c;可以按照以下步骤操作&#xff1a; 打开WebStorm。 进入设置界面&a…

集成学习之-stacking

一、引言 对于单模型来说&#xff0c;模型的抗干扰能力低&#xff0c;且难以拟合复杂的数据。 所以可以集成多个模型的优缺点&#xff0c;提高泛化能力。 集成学习一般有三种&#xff1a;boosting是利用多个弱学习器串行&#xff0c;逐个纠错&#xff0c;构造强学习器。 baggin…