目录
预备知识
基础知识
(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计算梯度
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))
线性回归:线性回归是一种建立连续数值之间线性关系的统计模型,通过拟合一个直线(或超平面)来描述自变量和因变量的关系。该模型一般使用最小二乘法来你和数据,即最小化预测值与真实值之间的残差平方和。
简单举例就是假设自变量 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')
逻辑回归:逻辑回归是一种二分类模型,在机器学习中,通常用于对图片、文本或其他非结构化输入数据进行分类。其主要思想是将输入特征与权重相乘,并添加偏置项。这个乘积被送入一个 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')
全连接神经网络(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