【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 + w3
    return y_pred

# 权重随机初始化
w1, w2, w3 = 1, 0, -1

# 拟定一个损失函数 均方差损失函数
def loss(y_pred, y_true):
    # y_pred = w1 * x ** 2 + w2 * x + w3
    return (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) ** 2
        grad_w1 += 2 * (y_pred - y_true) * x ** 2
        grad_w2 += 2 * (y_pred - y_true) * x
        grad_w3 += 2 * (y_pred - y_true)
        if counter == batch_size:
            # 权重更新 新的权重 = 旧的权重 - 学习率 × 批次训练梯度和 / 每一批次的训练大小
            w1 = w1 - lr * grad_w1 / batch_size  # sgd
            w2 = w2 - lr * grad_w2 / batch_size
            w3 = w3 - lr * grad_w3 / batch_size
            counter = 0
            grad_w1 = 0
            grad_w2 = 0
            grad_w3 = 0
    # 每一轮批次训练后的损失值的平均值
    epoch_loss /= len(X)
    print("第%d轮, loss %f" % (epoch, epoch_loss))
    # 当损失函数低于某一特定数时,训练就可以停止
    if epoch_loss < 0.00001:
        break

print(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指传入的数据,返回得到的结果x
        x = 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:utf8

import 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(最后一层)的维度,比如这里是2
        super(TorchModel, self).__init__()
        # 隐含层1公式:wx + b , w:3 * 5
        self.layer1 = nn.Linear(input_size, hidden_size1)
        # 隐含层2公式:wx + b , w:5 * 2
        self.layer2 = nn.Linear(hidden_size1, hidden_size2)

    # 前向计算
    # 定义网络的使用方式
    def forward(self, x):
        # 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果x
        x = 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 = w1
        self.b1 = b1
        self.w2 = w2
        self.b2 = b2

    def forward(self, x):
        hidden = np.dot(x, self.w1.T) + self.b1 # 1*5
        y_pred = np.dot(hidden, self.w2.T) + self.b2 # 1*2
        return 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(最后一层)的维度,比如这里是2
        super(TorchModel, self).__init__()
        # 隐含层1公式:wx + b , w:3 * 5
        self.layer1 = nn.Linear(input_size, hidden_size1)
        self.sig = nn.Sigmoid()
        # 隐含层2公式:wx + b , w:5 * 2
        self.layer2 = nn.Linear(hidden_size1, hidden_size2)
        self.relu = nn.ReLU()

    # 前向计算
    # 定义网络的使用方式
    def forward(self, x):
        # 第一层代入隐含层1公式,参数x指传入的数据,返回得到的结果x
        x = 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.shape
    pred = 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] = 1
    return one_hot_target

print("手动实现交叉熵:", 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.sigmoid
        self.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, 1
        else:
            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 = 100
    x, y = build_dataset(test_sample_num)
    print("本次预测集中共有%d个正样本,%d个负样本" % (sum(y), test_sample_num - sum(y)))
    correct, wrong = 0, 0
    with 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 += 1
    print("正确预测个数:%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 = 5
    model = 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)

④ 证明预测准确性

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DAY168内网对抗-基石框架篇单域架构域内应用控制成员组成用户策略信息收集环境搭建

知识点&#xff1a; 1、基石框架篇-单域架构-权限控制-用户和网络 2、基石框架篇-单域架构-环境搭建-准备和加入 3、基石框架篇-单域架构-信息收集-手工和工具 1、工作组(局域网) 将不同的计算机按照功能分别列入不同的工作组。想要访问某个部门的资源&#xff0c;只要在“…

MATLAB 建筑顶面面积计算(95)

MATLAB 建筑顶面面积计算(95) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 根据给出的建筑顶面点云,计算建筑面积,具体的方法实现和结果如下: 二、算法实现 1.代码 代码如下(示例): % 从 PLY 文件读取点云数据 filename = D:\shuju\屋顶2.ply; % 替换为你的…

Mac M1 安装数据库

1. Docker下载 由于Sqlserver和达梦等数据库&#xff0c;不支持M系列的芯片&#xff0c;所以我们通过docker安装 下载并安装docker: https://www.docker.com/get-started/ 安装完成后&#xff0c;打开docker 2. SQL Server 安装 2.1 安装 打开终端&#xff0c;执行命令 doc…

二十(GIT3)、echarts(折线图、柱状图、饼图)、黑马就业数据平台(主页图表实现、闭包了解、学生信息渲染)

1. echarts 数据可视化&#xff1a;将数据转换为图形&#xff0c;数据特点更加突出 echarts&#xff1a;一个基于 JavaScript 的开源可视化图表库 echarts官网 1.1 echarts核心使用步骤 // 1. 基于准备好的dom&#xff0c;初始化echarts实例 const myChart echarts.init…

软考高级架构-9.4.4-双机热备技术 与 服务器集群技术

一、双机热备 1、特点&#xff1a; 软硬件结合&#xff1a;系统由两台服务器&#xff08;主机和备机&#xff09;、一个共享存储&#xff08;通常为磁盘阵列柜&#xff09;、以及双机热备软件&#xff08;提供心跳检测、故障转移和资源管理功能的核心软件&#xff09;组成。 …

【Java若依框架】RuoYi-Vue的前端和后端配置步骤和启动步骤

&#x1f399;告诉你&#xff1a;Java是世界上最美好的语言 &#x1f48e;比较擅长的领域&#xff1a;前端开发 是的&#xff0c;我需要您的&#xff1a; &#x1f9e1;点赞❤️关注&#x1f499;收藏&#x1f49b; 是我持续下去的动力&#xff01; 目录 一. 作者有话说 …

Kubernetes Nginx-Ingress | 禁用HSTS/禁止重定向到https

目录 前言禁用HSTS禁止重定向到https关闭 HSTS 和设置 ssl-redirect 为 false 的区别 前言 客户请求经过ingress到服务后&#xff0c;默认加上了strict-transport-security&#xff0c;导致客户服务跨域请求失败&#xff0c;具体Response Headers信息如下&#xff1b; 分析 n…

小程序入门学习(八)之页面事件

一、下拉刷新新事件 1. 什么是下拉刷新 下拉刷新是移动端的专有名词&#xff0c;指的是通过手指在屏幕上的下拉滑动操作&#xff0c;从而重新加载页面数据的行为。 2. 启用下拉刷新 启用下拉刷新有两种方式&#xff1a; 全局开启下拉刷新&#xff1a;在 app.json 的 window…

C++(十二)

前言&#xff1a; 本文将进一步讲解C中&#xff0c;条件判断语句以及它是如何运行的以及内部逻辑。 一&#xff0c;if-else,if-else语句。 在if语句中&#xff0c;只能判断两个条件的变量&#xff0c;若想实现判断两个以上条件的变体&#xff0c;就需要使用if-else,if-else语…

[Linux]文件属性和权限

目录 一.Linux文件的属性二.Linux用户权限分类三.文件权限的查询与修改1.修改用户的权限1).一般法2).8进制法 2.修改所属组和所属者3.如何在创建文件时权限预分配 在学习linux的时候&#xff0c;我们用ll命令显示文件的详情信息&#xff0c;难免会发现文件名前面会有一大堆其它…

ElK 8 收集 MySQL 慢查询日志并通过 ElastAlert2 告警至飞书

文章目录 1. 说明2. 启个 mysql3. 设置慢查询4. filebeat 设置5. 触发慢查询6. MySQL 告警至飞书 1. 说明 elk 版本&#xff1a;8.15.0 2. 启个 mysql docker-compose.yml 中 mysql&#xff1a; mysql:# restart: alwaysimage: mysql:8.0.27# ports:# - "3306:3306&q…

springSecurity权限控制

权限控制&#xff1a;不同的用户可以使用不同的功能。 我们不能在前端判断用户权限来控制显示哪些按钮&#xff0c;因为这样&#xff0c;有人会获取该功能对应的接口&#xff0c;就不需要通过前端&#xff0c;直接发送请求实现功能了。所以需要在后端进行权限判断。&#xff0…

力扣打卡9:重排链表

链接&#xff1a;143. 重排链表 - 力扣&#xff08;LeetCode&#xff09; 这是一道操作链表的题。按照要求&#xff0c;我们可以将解题的步骤分成三步。 1.找链表中间结点&#xff08;我使用了快慢指针寻找&#xff09;&#xff0c;并断开。 2.现在有2链表&#xff0c;将后段…

计算机键盘的演变 | 键盘键名称及其功能 | 键盘指法

注&#xff1a;本篇为 “键盘的演变及其功能” 相关几篇文章合辑。 英文部分机翻未校。 The Evolution of Keyboards: From Typewriters to Tech Marvels 键盘的演变&#xff1a;从打字机到技术奇迹 Introduction 介绍 The keyboard has journeyed from a humble mechanical…

【Appium报错】安装uiautomator2失败

目录 1、通过nmp安装uiautomator2&#xff1a;失败 2、通过 Appium 的平台直接安装驱动程序 3、通过pip 来安装 uiautomator2 1、通过nmp安装uiautomator2&#xff1a;失败 我先是通过npm安装的uiautomator2&#xff0c;也显示已经安装成功了&#xff1a; npm install -g …

SSM整合原理实战案例《任务列表案例》

一、前端程序搭建和运行: 1.整合案例介绍和接口分析: (1).案例功能预览: (2).接口分析: 学习计划分页查询 /* 需求说明查询全部数据页数据 请求urischedule/{pageSize}/{currentPage} 请求方式 get 响应的json{"code":200,"flag":true,"data&…

Chrome扩展程序开发示例

项目文件夹内文件如下&#xff1a; manifest.json文件内容&#xff1a; {"manifest_version": 3,"name": "我的法宝","description": "我的有魔法的宝贝","version": "1.0","icons": {"…

石头剪子布

石头剪子布 C语言实现C实现Java实现Python实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 石头剪子布&#xff0c;是一种猜拳游戏。起源于中国&#xff0c;然后传到日本、朝鲜等地&#xff0c;随着亚欧贸易的不断发展它传到了欧洲&…

RabbitMQ核心概念及工作流程 + AMQP

文章目录 一. RabbitMQ核心概念1. Producer, Consumer, Broker2. Connection和Channel3. Virtual host4. Queue5. Exchange 二. RabbitMQ的工作流程三. AMQP四. web界面操作对用户操作对虚拟机操作 一. RabbitMQ核心概念 RabbitMQ是⼀个消息中间件, 也是⼀个⽣产者消费者模型.…

js循环导出多个word表格文档

文章目录 js循环导出多个word表格文档一、文档模板编辑二、安装依赖三、创建导出工具类exportWord.js四、调用五、效果图js循环导出多个word表格文档 结果案例: 一、文档模板编辑 二、安装依赖 // 实现word下载的主要依赖 npm install docxtemplater pizzip --save// 文件操…