【第十四周】PyTorch深度学习实践1

目录

摘要

本周主要通过B站刘二大人的视频对 PyTorch 进行实践学习,了解了PyTorch 中各个常用的类以及方法,学习了搭建神经网络的四个步骤,为后面手动复现模型提供了实践基础。其中,最重要的是学习到了看文档的能力,对于新学习到的类和方法能够通过查询文档去理解用法和作用。

Abstract

This week, I mainly focused on practical learning with PyTorch, understanding the various commonly used classes and methods within it. I learned the four steps to building a neural network, which provided a practical foundation for manually reproducing models in the future. Most importantly, I developed the ability to read documentation, allowing me to understand the usage and function of newly learned classes and methods by consulting the documentation.

本周学习内容来自于《PyTorch深度学习实践》完结合集 1~8课

参考资料来自于PyTorch 英文文档

1.反向传播

对于简单的线性模型可以通过解析式来求对参数的导数。

一个复杂的神经网络中含有成千上百个参数,分别对这些参数求解析是一个很繁重的任务,因此我们需要一个新的办法------反向传播算法

线性函数叠加之后总是可以化简成 W ⋅ X + b W·X+b W⋅X+b的形式,这样子多层叠加就没有意义了,所以要在每一层的结果处加上一个非线性函数。

前馈过程求解中间值和局部导数,反向传播过程求解对各个参数的偏导。

PyTorch中 Tensor(张量)包含两个成员,一个是保存参数的 Data,一个是保存导数的 Grad。同样这两个成员也是 Tensor。

torch.Tensor 和 torch.tensor的区别

代码如下:

python 复制代码
import torch

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

# 创建一个 Tensor 类型,方便求梯度
w = torch.tensor([1.0])    
w.requires_grad = True      

# 前向传播
def forward(x):
    return x * w

# 计算损失,创建计算图
def loss(x, y):
    y_pred = forward(x)
    return (y_pred - y) ** 2


print("predict before training", 4, forward(4).item())

for epoch in range(100):
    for x, y in zip(x_data, y_data):   # 选择一个样本更新梯度值
        l = loss(x, y)
        l.backward()
        print('\tgrad:', x, y, w.grad.item())
        w.data = w.data - 0.01 * w.grad.data

        # 每次更新完梯度要清除梯度
        w.grad.data.zero_()

    print("progress:", epoch, l.item())

print("predict after training", 4, forward(4).item())

结果如下:

2.线性回归

神经网络模型的搭建遵循上图的四个流程。

2.1.准备数据集

python 复制代码
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

2.2.设计模型

定义 LinearModel 类

python 复制代码
class LinearModel(torch.nn.Module):

这里定义了一个名为 LinearModel 的类,它继承自 torch.nn.Module。torch.nn.Module 是所有神经网络模块的基类,包含了一些有用的属性和方法,用于管理模型参数、前向传播等。

python 复制代码
def __init__(self):
    super(LinearModel, self).__init__()
    self.linear = torch.nn.Linear(1, 1)
  • init 方法是 Python 中类的构造函数,当创建类的实例时会自动调用。
  • super(LinearModel, self).init() 调用了父类 torch.nn.Module 的构造函数,这是必要的步骤,以确保父类能够正确地初始化。
  • self.linear = torch.nn.Linear(1, 1) 创建了一个线性层(全连接层),输入维度为1,输出维度也为1。这意味着该模型将接受一个标量作为输入,并产生一个标量作为输出。

前向传播

python 复制代码
def forward(self, x):
    y_pred = self.linear(x)
    return y_pred
  • forward 方法定义了模型的前向传播逻辑,即如何从输入数据 x 计算出输出 y_pred。
  • self.linear(x) 使用之前定义的线性层对输入 x 进行变换。
  • return y_pred 返回前向传播的结果,即预测值 y_pred。

2.3.定义损失函数和优化器


定义损失和优化器

python 复制代码
criterion = torch.nn.MSELoss(size_average=False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

model.parameters() 返回模型中所有需要优化的参数。

2.4.模型训练

python 复制代码
for epoch in range(100):
    y_pred = model(x_data)
    loss = criterion(y_pred, y_data)
    print(epoch, loss.item())
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())

x_test = torch.tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)

结果如下:

3.逻辑回归



为什么交叉熵可以衡量两个分布的差异呢?

交叉熵 H ( P , Q ) H(P,Q) H(P,Q) 可以理解为使用概率分布 Q Q Q编码来自概率分布P的数据所需的平均比特数。具体来说:如果 P ( x ) P(x) P(x) 和 Q ( x ) Q(x) Q(x) 完全相同,即 P ( x ) = Q ( x ) P(x)=Q(x) P(x)=Q(x),那么 H ( P , Q ) H(P,Q) H(P,Q) 等于 H ( P ) H(P) H(P),也就是真实分布的熵。如果 P ( x ) P(x) P(x) 和 Q ( x ) Q(x) Q(x) 不同,那么 H ( P , Q ) H(P,Q) H(P,Q) 会大于 H ( P ) H(P) H(P),因为使用不准确的概率分布Q来编码数据会导致更多的冗余信息,从而需要更多的比特数。

代码如下:

python 复制代码
import torch
import torch.nn.functional as F

# prepare dataset
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[0], [0], [1]])


# design model using class
class LogisticRegressionModel(torch.nn.Module):
    def __init__(self):
        super(LogisticRegressionModel, self).__init__()
        self.linear = torch.nn.Linear(1, 1)

    def forward(self, x):
        y_pred = F.sigmoid(self.linear(x))
        return y_pred


model = LogisticRegressionModel()

# construct loss and optimizer
# 默认情况下,loss会基于element平均,如果size_average=False的话,loss会被累加。
criterion = torch.nn.BCELoss(size_average=False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# training cycle forward, backward, update
for epoch in range(1000):
    y_pred = model(x_data)
    loss = criterion(y_pred, y_data)
    print(epoch, loss.item())

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())

x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)

结果如下:

4.处理多维特征的输入

上图为预测一个人在一年之后糖尿病病情加重概率的例子,这时每个样本会有八个不同的特征,通过八个不同的特征来预测是否病情会加重。

对于多维的特征输入我们需要把每一个特征x乘以相应的权重。在进行逻辑回归时,把每一个维度的x乘相应的权值的和加上一个偏置量。然后再把每个样本的式子写成矩阵乘法的形式。

python 复制代码
import numpy as np
import torch
import matplotlib.pyplot as plt
 
# prepare dataset
xy = np.loadtxt('diabetes.csv', delimiter=',', dtype=np.float32)
x_data = torch.from_numpy(xy[:, :-1]) # 第一个':'是指读取所有行,第二个':'是指从第一列开始,最后一列不要
y_data = torch.from_numpy(xy[:, [-1]]) # [-1] 最后得到的是个矩阵
 
# design model using class
 
 
class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = torch.nn.Linear(8, 6) # 输入数据x的特征是8维,x有8个特征
        self.linear2 = torch.nn.Linear(6, 4)
        self.linear3 = torch.nn.Linear(4, 1)
        self.sigmoid = torch.nn.Sigmoid() # 将其看作是网络的一层,而不是简单的函数使用
 
    def forward(self, x):
        x = self.sigmoid(self.linear1(x))
        x = self.sigmoid(self.linear2(x))
        x = self.sigmoid(self.linear3(x)) # y hat
        return x
 
 
model = Model()
 
# construct loss and optimizer
# criterion = torch.nn.BCELoss(size_average = True)
criterion = torch.nn.BCELoss(reduction='mean')  
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
 
epoch_list = []
loss_list = []
# training cycle forward, backward, update
for epoch in range(100):
    y_pred = model(x_data)
    loss = criterion(y_pred, y_data)
    print(epoch, loss.item())
    epoch_list.append(epoch)
    loss_list.append(loss.item())
 
    optimizer.zero_grad()
    loss.backward()
 
    optimizer.step()
 
 
plt.plot(epoch_list, loss_list)
plt.ylabel('loss')
plt.xlabel('epoch')
plt.show()

torch.sigmoid、torch.nn.Sigmoid和torch.nn.functional.sigmoid的区别

结果如下:

5.加载数据集

5.1.导入必要的库

python 复制代码
import torch
import numpy as np
from torch.utils.data import Dataset
from torch.utils.data import DataLoader

5.2.准备数据集

python 复制代码
class DiabetesDataset(Dataset):
    def __init__(self, filepath):
        xy = np.loadtxt(filepath, delimiter=',', dtype=np.float32)
        self.len = xy.shape[0]
        self.x_data = torch.from_numpy(xy[:, :-1])
        self.y_data = torch.from_numpy(xy[:, [-1]])

    def __getitem__(self, index):
        return self.x_data[index], self.y_data[index]

    def __len__(self):
        return self.len
  • DiabetesDataset 继承自 Dataset 类,其中 Dataset 是抽象类,不可被实例化。
  • _init _ 方法:
    读取CSV文件并将其转换为NumPy数组。
    self.len 存储数据集的样本数量。
    self.x_data存储特征数据,self.y_data 存储标签数据。
  • _getitem _ 方法:
    返回指定索引处的特征和标签。
  • _len _ 方法:
    返回数据集的长度(样本数量)。

创建数据加载器

python 复制代码
dataset = DiabetesDataset('diabetes.csv')
train_loader = DataLoader(dataset=dataset, batch_size=32, shuffle=True, num_workers=0)

5.3.定义模型

python 复制代码
class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = torch.nn.Linear(8, 6)
        self.linear2 = torch.nn.Linear(6, 4)
        self.linear3 = torch.nn.Linear(4, 1)
        self.sigmoid = torch.nn.Sigmoid()

    def forward(self, x):
        x = self.sigmoid(self.linear1(x))
        x = self.sigmoid(self.linear2(x))
        x = self.sigmoid(self.linear3(x))
        return x
  • Model 继承自 torch.nn.Module 类。

  • _init _ 方法:

    定义了三个全连接层(Linear)和一个Sigmoid激活函数。

  • forward 方法:

    定义了前向传播过程,输入数据依次通过三个全连接层和Sigmoid激活函数。

5.4.构建损失函数和优化器

python 复制代码
criterion = torch.nn.BCELoss(reduction='mean')
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

5.5.训练模型

python 复制代码
if __name__ == '__main__':
    for epoch in range(100):
        for i, data in enumerate(train_loader, 0):
            inputs, labels = data
            y_pred = model(inputs)
            loss = criterion(y_pred, labels)
            print(epoch, i, loss.item())

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

运行结果和上一小节类似,这一小节主要学习的是 dataloader 的具体作用和理解 epoch、batch 和 iteration 之间的关系。

总结

构建一个通用的神经网络模型是一个系统而精细的过程,大致可以分为四个关键步骤。首先,准备数据集是整个流程的基石,这一步骤不仅包括收集所需的数据,还需要对数据进行清洗、预处理以及划分训练集与测试集,确保数据的质量和适用性。接着,模型搭建阶段需要根据任务需求选择合适的网络架构,如卷积神经网络(CNN)用于图像识别,循环神经网络(RNN)适合序列数据处理等,并确定每一层的具体参数。第三步,定义损失函数和选择优化器是调整模型以达到最佳性能的关键,损失函数衡量预测值与真实值之间的差异,而优化器则通过反向传播算法最小化这种差异。最后,训练模型涉及迭代地将数据输入网络中,不断调整权重直至模型在训练集上表现良好,并通过验证集或测试集评估其泛化能力。掌握这一系列流程,就可以自己动手搭建绝大多数神经网络。

相关推荐
热爱跑步的恒川1 小时前
【论文复现】基于图卷积网络的轻量化推荐模型
网络·人工智能·开源·aigc·ai编程
阡之尘埃3 小时前
Python数据分析案例61——信贷风控评分卡模型(A卡)(scorecardpy 全面解析)
人工智能·python·机器学习·数据分析·智能风控·信贷风控
孙同学要努力5 小时前
全连接神经网络案例——手写数字识别
人工智能·深度学习·神经网络
Eric.Lee20215 小时前
yolo v5 开源项目
人工智能·yolo·目标检测·计算机视觉
其实吧36 小时前
基于Matlab的图像融合研究设计
人工智能·计算机视觉·matlab
丕羽6 小时前
【Pytorch】基本语法
人工智能·pytorch·python
ctrey_6 小时前
2024-11-1 学习人工智能的Day20 openCV(2)
人工智能·opencv·学习
SongYuLong的博客6 小时前
Air780E基于LuatOS编程开发
人工智能
Jina AI6 小时前
RAG 系统的分块难题:小型语言模型如何找到最佳断点?
人工智能·语言模型·自然语言处理
-派神-6 小时前
大语言模型(LLM)量化基础知识(一)
人工智能·语言模型·自然语言处理