目录

预备知识

基础知识

(1)PyTorch基础知识

(2)线性回归模型

(3)逻辑回归模型

(4)全连接神经网络


预备知识

requires_grad :用于指示是否需要计算相应张量的梯度。如果 requires_grad=True,则表示需要对该张量进行梯度计算。

epoch:表示将所有训练样本都过一遍的完整训练过程。

Tensor:Tensor定义为最多可以包含四个维度的多维数组,但可以通过堆叠来创建更高维度的张量。可以使用 torch.Tensor() 、torch.eye()、 torch.randn() 、 torch.zeros() 或 torch.ones() 等构造函数来创建张量对象。

模型参数:模型内部的配置变量,模型可以根据数据可以自动学习出的变量,自行根据数据进行驱动调整。如:权重,偏差等。

超参数:模型外部的配置,不需要数据来驱动,而是在训练前或者训练中人为进行设定和调整,一般需要为它根据已有或现有的经验指定“正确”的值。如:迭代次数epoch,批量大小batch-size,学习率,深层神经网络隐藏层数等。

基础知识

(1)PyTorch基础知识

用法1:PyTorch计算梯度

x = torch.tensor(1., requires_grad=True)
y = x**2
y.backward()
print(x.grad)    # x.grad = 2

通过torch.tensor()方法创建一个值为1.0的张量,并将requires_grad参数设置为True。根据公式 y = x**2 构建计算图,调用 y.backward() 方法,该方法会自动计算 y 对于 x 的梯度,并将梯度值保存在 x.grad 属性中,通过打印 x.grad 来查看计算得到的梯度值。

用法2:线性回归模型训练

x = torch.randn(10, 3)
y = torch.randn(10, 2)

linear = nn.Linear(3, 2)
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(linear.parameters(), lr=0.01)

pred = linear(x)
loss = criterion(pred, y)

loss.backward()
optimizer.step()

pred = linear(x)
loss = criterion(pred, y)

通过torch.rand()方法创建随机张量x和y,x是输入特征,y是目标值,使用nn.Linear(3,2)创建全连接层,输入、输出维度分别为3和2。定义损失函数 criterion 为均方误差(MSE)损失。优化器 optimizer,使用随机梯度下降(SGD)算法来更新模型参数,将 linear.parameters() 传递给优化器,以更新 linear 中的权重和偏置。进行前向传播,计算   模型的预测值 pred和损失 loss,将预测值 pred 和目标值 y 传递给损失函数 criterion。进行反向传播,通过调用 loss.backward() 来计算梯度。调用 optimizer.step() 来执行一步梯度下降,更新模型的参数。进行一步梯度下降后,再次计算预测值 pred 和损失 loss。

用法3:NumPy 数组和 PyTorch 张量转换

x = np.array([[1, 2], [3, 4]])
y = torch.from_numpy(x)
z = y.numpy()

创建了一个 2x2 的 NumPy 数组 x。使用 torch.from_numpy()函数将 NumPy 数组转换为 PyTorch 张量 y。现在,y 是一个 PyTorch 张量,与原始的 NumPy 数组 x 共享相同的数据存储,因此对 y 的修改会反映在 x 上。也可以使用 y.numpy() 方法将 PyTorch 张量转换回 NumPy 数组 。

用法4:构建数据集和加载数据(以CIFAR10数据集为例)

train_dataset = torchvision.datasets.CIFAR10(root=r'path',
                                             train=True, 
                                             transform=transforms.ToTensor(),
                                             download=True)

image, label = train_dataset[0]
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=64, 
                                           shuffle=True)

data_iter = iter(train_loader)
images, labels = data_iter.next()
for images, labels in train_loader:
    # Training code should be written here.
    pass

使用torchvision.datasets.CIFAR10()  函数下载并构建 CIFAR-10 数据集。参数root 为数据集保存的根目录、train=True表示加载训练集、transform=transform.ToTensor()将图像数据转换为张量格式、download=True表示如果数据集不存在,则下载它。

接下来使用train_dataset[0]获取数据集中的第一个数据对,其中包含图像和标签。image是一个张量,表示图像数据,label是一个整数,表示图像标签。

创建数据加载器train_loader,它会提供简单的队列和线程来从文件中加载数据。参数dataset 指定加载的batch_size指定每个小批量的样本数量,shuffle=True表示在每个 epoch 中随机打乱数据集顺序。

使用 iter(train_loader)创建一个数data_iter。通过调用data_iter.next(),获取一个小批量的图像和标签数据。images包含了一个小批量的图像数据,labels为对应图像的标签。最后通过循环来迭代加载每个小批量的图像和标签,并在训练代码中使用它们进行模型训练。

用法5:创建自定义数据集类和加载数据

class CustomDataset(torch.utils.data.Dataset):
    def __init__(self):

        pass
    def __getitem__(self, index):

        pass
    def __len__(self):
        return 0 

custom_dataset = CustomDataset()
train_loader = torch.utils.data.DataLoader(dataset=custom_dataset,
                                           batch_size=64, 
                                           shuffle=True)

该方法为类的继承,传入父类后需要重写子类的__init__、__getitem__等方法。在__init__方法中初始化文件路径或文件名列表等相关信息,可以将文件路径列表作为参数传入或直接指定文件路径。在__getitem__ 方法中实现数据读取和预处理的逻辑,可以使用 numpy、PIL 等库来读取文件,并进行必要的数据预处理操作,例如使用 transform进行图像处理。在__len__ 方法中返回数据集的大小,确保它与你的数据集包含的样本数量相匹配。

重写上述内容后,使用 torch.utils.data.DataLoader创建数据加载器。在创建时,需要传入dataset参数,指定使用的数据集对象、batch_size参数,指定每个小批量的样本数量、shuffle=True参数,打乱样本顺序。

用法6:加载预训练模型(以ResNet18模型为例)

resnet = torchvision.models.resnet18(pretrained=True)
for param in resnet.parameters():
    param.requires_grad = False

resnet.fc = nn.Linear(resnet.fc.in_features, 100)  # 100 is an example.
images = torch.randn(64, 3, 224, 224)
outputs = resnet(images)

通过 torchvision.models.resnet18(pretrained=True) 加载预训练ResNet-18 模型。pretrained=True表示使用在 ImageNet 数据集上预训练好的权重参数。接下来,对模型的所有参数进行遍历,并将 parm.requres_grad设置为 False,冻结所有层的参数,只训练顶部的新添加层。

然后,通过 resnet.fc=nn.Linear(resnet.fc.in_features,100)替换模型的全连接层。resnet.fc是原始 ResNet-18 模型中的最后一层全连接层,它的输入特征数量由resnet.fc.in_features得到。

最后,通过将输入图像传递给模型,可以获取模型的输出结果。在这个例子中,生成了一个大小为 (64, 3, 224, 224) 的随机输入图像,然后通过outputs=resnet(images)进行前向传播,得到模型的输出,打印出outputs的大小,应该是 (64, 100)。

用法7:模型的保存和加载(以ResNet为例)

torch.save(resnet, 'model.ckpt')
model = torch.load('model.ckpt')

torch.save(resnet.state_dict(), 'params.ckpt')
resnet.load_state_dict(torch.load('params.ckpt'))

使用torch.save()可以保存整个模型。若只想保存模型参数,可以用state_dict()方法获取模型的参数字典,然后再保存。加载保存的模型或参数,可以使用torch.load() 函数,并传入文件名,得到相应的模型或参数。

如果保存和加载模型参数时使用的是不同的设备,例如 CPU 和 GPU,会导致加载失败。因此,在保存模型时应该保证模型和参数都在同一个设备上,并在加载时指定相同的设备。例如:

# 保存模型和参数都在 GPU 上。
torch.save(resnet.state_dict(), 'params.ckpt')

# 加载时指定使用 GPU。
device = torch.device('cuda')
resnet.load_state_dict(torch.load('params.ckpt', map_location=device))

(2)线性回归模型

线性回归:线性回归是一种建立连续数值之间线性关系的统计模型,通过拟合一个直线(或超平面)来描述自变量和因变量的关系。该模型一般使用最小二乘法来你和数据,即最小化预测值与真实值之间的残差平方和。

简单举例就是假设自变量 x 和因变量 y 之间存在一个线性关系,可以用一条直线来拟合这个关系。这条直线的数学表达式为:y = wx + b ,其中,w 是直线的斜率,b 是直线的截距。模型的目标是找到最佳的参数 w 和 b,使得预测值与真实值之间的误差最小化。

以下是一个简单的线性回归模型实现方法:

首先定义一些超参数,如输入、输出维度、迭代次数、学习率、训练数据集x_train和目标值y_train。然后使用 PyTorch 提供的nn.Linear类创建一个线性回归模型model,定义损失函数criterion和优化器optimizer。

在训练循环中,将训练数据和目标值转换为 PyTorch 张量,并进行前向传播、计算损失、反向传播和优化器更新。每隔 5 个迭代周期打印一次损失值。训练结束后,使用训练好的模型对训练数据进行预测,并绘制原始数据和拟合直线的图像。最后使用torch.save()函数保存模型参数。

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

# Hyper-parameters
input_size = 1
output_size = 1
num_epochs = 60
learning_rate = 0.001

x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168], 
                    [9.779], [6.182], [7.59], [2.167], [7.042], 
                    [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)

y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573], 
                    [3.366], [2.596], [2.53], [1.221], [2.827], 
                    [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)

# Linear regression model
model = nn.Linear(input_size, output_size)

# Loss and optimizer
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  

# Train the model
for epoch in range(num_epochs):
    # Convert numpy arrays to torch tensors
    inputs = torch.from_numpy(x_train)
    targets = torch.from_numpy(y_train)

    # Forward pass
    outputs = model(inputs)
    loss = criterion(outputs, targets)
    
    # Backward and optimize
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    if (epoch+1) % 5 == 0:
        print ('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, loss.item()))

# Plot the graph
predicted = model(torch.from_numpy(x_train)).detach().numpy()
plt.plot(x_train, y_train, 'ro', label='Original data')
plt.plot(x_train, predicted, label='Fitted line')
plt.legend()
plt.show()

# Save the model checkpoint
torch.save(model.state_dict(), 'model.ckpt')

(3)逻辑回归模型

逻辑回归:逻辑回归是一种二分类模型,在机器学习中,通常用于对图片、文本或其他非结构化输入数据进行分类。其主要思想是将输入特征与权重相乘,并添加偏置项。这个乘积被送入一个 sigmoid 函数,以便将输出值压缩到0和1之间。当输出值大于等于0.5时,被分类为正类(1),否则被分类为负类(0)。

下面是一个简单的逻辑回归模型在MNIST数据集上的训练和测试过程:

首先定义超参数,输入大小、类别数、迭代次数、批量大小和学习率。随后加载数据集并进行预处理(将图像转换为Tensor张量),使用 DataLoader 类创建训练数据加载器(train_loader)和测试数据加载器(test_loader),这些加载器会自动处理数据的批量和随机化。接着创建一个线性回归模型,使用交叉熵损失函数(nn.CrossEntropyLoss())作为损失函数,随机梯度下降优化器(torch.optim.SGD())来更新模型的参数。然后进行模型训练,循环遍历训练数据加载器中的每个批次,将图像展平并进行前向传播计算,计算损失并进行反向传播优化模型的参数。最后对训练后的模型进行测试,在测试阶段,不需要计算梯度,因此使用 torch.no_grad() 上下文管理器来关闭梯度计算。通过遍历测试数据加载器,计算模型在测试集上的准确率。

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms

# Hyper-parameters 
input_size = 28 * 28    # 784
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001

# MNIST dataset (images and labels)
train_dataset = torchvision.datasets.MNIST(root='../../data', 
                                           train=True, 
                                           transform=transforms.ToTensor(),
                                           download=True)

test_dataset = torchvision.datasets.MNIST(root='../../data', 
                                          train=False, 
                                          transform=transforms.ToTensor())

# Data loader (input pipeline)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 
                                          batch_size=batch_size, 
                                          shuffle=False)

# Logistic regression model
model = nn.Linear(input_size, num_classes)

# Loss and optimizer
# nn.CrossEntropyLoss() computes softmax internally
criterion = nn.CrossEntropyLoss()  
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  

# Train the model
total_step = len(train_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):
        # Reshape images to (batch_size, input_size)
        images = images.reshape(-1, input_size)
        
        # Forward pass
        outputs = model(images)
        loss = criterion(outputs, labels)
        
        # Backward and optimize
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if (i+1) % 100 == 0:
            print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}' 
                   .format(epoch+1, num_epochs, i+1, total_step, loss.item()))

# Test the model
# In test phase, we don't need to compute gradients (for memory efficiency)
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.reshape(-1, input_size)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum()

    print('Accuracy of the model on the 10000 test images: {} %'.format(100 * correct / total))

# Save the model checkpoint
torch.save(model.state_dict(), 'model.ckpt')

(4)全连接神经网络

全连接神经网络(Fully Connected Neural Network),也称为多层感知机(Multilayer Perceptron,MLP),是一种最基本的前馈神经网络模型。它由多个全连接层组成,每个全连接层都包括一些神经元,可以将输入数据进行线性变换和非线性变换,得到输出结果。其中,第一层通常被称为输入层,最后一层通常被称为输出层,中间的层次被称为隐藏层。其训练过程通常采用反向传播算法(Backpropagation),通过计算损失函数对网络中各层之间的权重进行调整,以达到学习输入与输出之间的映射关系的目的。

下面是一个简单的全连接神经网络模型,用于对MNIST手写数字数据集进行分类:

首先定义超参数,输入大小、隐藏层大小、类别数、迭代次数、批量大小和学习率。接着,加载并预处理了MNIST数据集。构建模型时使用一个线性层(nn.Linear)作为输入层到隐藏层的连接,一个ReLU激活函数(nn.ReLU)作为隐藏层的非线性变换,再连接一个线性层作为隐藏层到输出层的连接。forward函数定义了前向传播的过程。接着进行模型训练,使用交叉熵损失函数(nn.CrossEntropyLoss)计算损失,Adam优化器(torch.optim.Adam)更新模型的参数。通过循环遍历训练数据集,进行前向传播、反向传播和参数优化。每经过100个批次,打印一次当前的损失值。在测试阶段,关闭梯度计算以节省内存,对测试数据集进行前向传播,计算准确率。

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms

# Device configuration
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001

# MNIST dataset 
train_dataset = torchvision.datasets.MNIST(root='../../data', 
                                           train=True, 
                                           transform=transforms.ToTensor(),  
                                           download=True)

test_dataset = torchvision.datasets.MNIST(root='../../data', 
                                          train=False, 
                                          transform=transforms.ToTensor())

# Data loader
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 
                                          batch_size=batch_size, 
                                          shuffle=False)

# Fully connected neural network with one hidden layer
class NeuralNet(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(NeuralNet, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size) 
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)  
    
    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

model = NeuralNet(input_size, hidden_size, num_classes).to(device)

# Loss and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)  

# Train the model
total_step = len(train_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):  
        # Move tensors to the configured device
        images = images.reshape(-1, 28*28).to(device)
        labels = labels.to(device)
        
        # Forward pass
        outputs = model(images)
        loss = criterion(outputs, labels)
        
        # Backward and optimize
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if (i+1) % 100 == 0:
            print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}' 
                   .format(epoch+1, num_epochs, i+1, total_step, loss.item()))

# Test the model
# In test phase, we don't need to compute gradients (for memory efficiency)
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.reshape(-1, 28*28).to(device)
        labels = labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))

# Save the model checkpoint
torch.save(model.state_dict(), 'model.ckpt')

总结:以上属于PyTorch的基础理论知识、简单的网络模型及相关代码讲解,主要用于神经网络前期的学习。

参考文献:Pytorch中文文档

                 Pytorch教程源码——GitHub