PyTorch是一个开源的机器学习库,广泛用于计算机视觉和自然语言处理等应用。它提供了强大的GPU加速的张量计算能力,以及构建深度学习模型的动态计算图。
定义:
1. 张量(Tensor)
在PyTorch中,张量是多维数组的表示,类似于NumPy中的数组,但可以在GPU上进行计算。张量是PyTorch中最基本的数据结构,用于表示数据和模型参数。
2. 动态计算图(Dynamic Computational Graph)
PyTorch使用动态计算图(也称为自动微分系统)来构建和训练神经网络。在这种机制下,计算图在运行时构建,允许更灵活的模型设计和调试。每次执行计算时,都会构建一个新的图,这使得修改模型结构变得容易。
3. 自动微分(Autograd)
PyTorch的自动微分引擎能够自动计算梯度,这对于训练神经网络至关重要。当使用张量进行运算时,PyTorch会自动记录操作,使得反向传播变得简单。
4. 模块(Module)
torch.nn.Module
是所有神经网络模块的基类。通过继承这个类,你可以定义自己的网络层、损失函数和模型。模块可以包含其他模块,支持模型的层次化设计。
5. 参数(Parameter)
在神经网络中,参数是模型的权重和偏置,它们在训练过程中被优化。在PyTorch中,Parameter
是Tensor
的一个子类,用于表示可以学习的参数。
6. 优化器(Optimizer)
优化器用于在训练过程中更新模型的参数。PyTorch提供了多种优化器,如SGD、Adam等,它们根据损失函数的梯度来调整参数。
7. 损失函数(Loss Function)
损失函数用于评估模型的预测与真实值之间的差异。常见的损失函数包括均方误差(MSE)、交叉熵损失(CrossEntropyLoss)等。
8. 数据加载器(DataLoader)
DataLoader
是PyTorch中用于加载数据集的类。它提供了一种简便的方式来迭代数据集,支持批处理、打乱数据和多线程加载。
9. 训练循环(Training Loop)
训练循环是执行模型训练的代码块,通常包括前向传播、计算损失、反向传播和参数更新。
算法步骤
1. 数据准备
使用PyTorch的datasets.MNIST
类加载MNIST数据集,包括训练集和测试集。数据集被转换为PyTorch张量,并使用DataLoader
进行批量加载。
# 导入必要的库
import torch.cuda
from torchvision import datasets
from torchvision.transforms import ToTensor# 加载训练数据集
traning_data = datasets.MNIST(root='data', train=True, download=True, transform=ToTensor())
# 加载测试数据集
test_data = datasets.MNIST(root='data', train=False, download=True, transform=ToTensor())# 打印训练数据集的大小
print(len(traning_data))# 导入DataLoader工具,用于创建数据加载器
from torch.utils.data import DataLoader# 创建训练数据加载器
train_dataloader = DataLoader(traning_data, batch_size=64)
# 创建测试数据加载器
test_dataloader = DataLoader(test_data, batch_size=64)# 选择设备,优先使用GPU,如果没有GPU则使用CPU
device = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
# 打印选择的设备
print(f"{device}")
2. 模型构建
定义一个包含四个隐藏层和一个输出层的全连接神经网络。每个隐藏层后都使用Sigmoid激活函数。
# 导入PyTorch的神经网络模块
import torch.nn as nn# 定义神经网络模型
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()# 定义一个展平层,将图像数据展平为一维向量self.flatten = nn.Flatten()# 定义四个隐藏层和一个输出层self.hidden1 = nn.Linear(28 * 28, 512)self.hidden2 = nn.Linear(512, 256)self.hidden3 = nn.Linear(256, 128)self.hidden4 = nn.Linear(128, 64)self.out = nn.Linear(64, 10)def forward(self, x):# 展平输入图像x = self.flatten(x)# 通过四个隐藏层进行前向传播x = self.hidden1(x)x = torch.sigmoid(x)x = self.hidden2(x)x = torch.sigmoid(x)x = self.hidden3(x)x = torch.sigmoid(x)x = self.hidden4(x)x = torch.sigmoid(x)# 通过输出层得到最终的预测结果x = self.out(x)return x# 实例化模型并将其转移到选择的设备上
model = NeuralNetwork().to(device)
3.定义训练函数
# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):model.train() # 设置模型为训练模式batch_size_num = 1for x, y in dataloader:x, y = x.to(device), y.to(device) # 将数据转移到选择的设备上pred = model(x) # 进行前向传播loss = loss_fn(pred, y) # 计算损失optimizer.zero_grad() # 清空梯度loss.backward() # 反向传播optimizer.step() # 更新参数loss_value = loss.item()if batch_size_num % 400 == 0:print(f"{loss_value} {batch_size_num}")batch_size_num += 1
4.定义测试函数
# 定义测试函数
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval() # 设置模型为评估模式test_loss, correct = 0, 0with torch.no_grad(): # 关闭梯度计算for x, y in dataloader:x, y = x.to(device), y.to(device)pred = model(x)test_loss += loss_fn(pred, y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= num_batchescorrect /= sizeprint(f"Test result: \n Accuracy: {(100 * correct)}%, Avg loss: {test_loss}")
5. 损失函数和优化器
使用交叉熵损失函数和Adam优化器进行模型训练。在训练过程中,每400个批次输出一次损失值,以监控训练进度。
# 定义损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
6. 测试过程
在训练完成后,使用测试数据集评估模型的性能。计算测试集上的准确率和平均损失。
# 进行一次训练和测试
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model, loss_fn)
7. 多轮训练
进行25轮训练,每轮结束后在测试集上评估模型性能。
# 进行多个epoch的训练
epochs = 25
for t in range(epochs):print(f"epoch {t + 1}\n")train(train_dataloader, model, loss_fn, optimizer)
print("done")# 在所有训练epoch完成后,再次测试模型的性能
test(test_dataloader, model, loss_fn)
实验结果
算法分析
-
模型结构:本实验使用的是一个简单的全连接神经网络,没有使用卷积层或池化层,这限制了模型对图像特征的提取能力。
-
激活函数:Sigmoid激活函数可能导致梯度消失问题,影响训练效果。可以考虑使用ReLU等其他激活函数。
-
优化器:Adam优化器在大多数情况下表现良好,但根据具体问题,可能需要调整学习率和其他超参数。
-
训练策略:实验中没有使用验证集来调整模型参数,这可能导致过拟合。可以考虑引入验证集来监控模型的泛化能力。
完整代码
import torch.cuda
from torchvision import datasets
from torchvision.transforms import ToTensor
traning_data=datasets.MNIST(root='data',train=True,download=True,transform=ToTensor())
test_data=datasets.MNIST(root='data',train=False,download=True,transform=ToTensor())
print(len(traning_data))# from matplotlib import pyplot as plt
# figure=plt.figure()
# for i in range(9):
# img,label=traning_data[i+59000]
# figure.add_subplot(3,3,i+1)
# plt.title(label)
# plt.axis("off")
# plt.imshow(img.squeeze(),cmap="gray")
# a=img.squeeze()
# plt.show()
from torch.utils.data import DataLoader
train_dataloader=DataLoader(traning_data,batch_size=64)
test_dataloader=DataLoader(test_data,batch_size=64)device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f"{device}")
import torch.nn as nn
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten=nn.Flatten()#展开,创建一个展开对象flatten,输入层self.hidden1=nn.Linear(28*28,512)self.hidden2=nn.Linear(512,256)self.hidden3=nn.Linear(256,128)self.hidden4=nn.Linear(128,64)self.out=nn.Linear(64,10)def forward(self,x):x=self.flatten(x)x=self.hidden1(x)x=torch.sigmoid(x)x=self.hidden2(x)x = torch.sigmoid(x)x = self.hidden3(x)x = torch.sigmoid(x)x = self.hidden4(x)x = torch.sigmoid(x)x=self.out(x)return x
model=NeuralNetwork().to(device)
# print(model)
def train(dataloader,model,loss_fn,optimzier):model.train()batch_size_num=1for x,y in dataloader:x,y=x.to(device),y.to(device)pred=model.forward(x)loss=loss_fn(pred,y)optimzier.zero_grad()loss.backward()optimzier.step()loss_value=loss.item()if batch_size_num%400==0:print(f"{loss_value} {batch_size_num}")batch_size_num+=1def test(dataloader,model,loss_fn):size=len(dataloader.dataset)num_batches=len(dataloader)model.eval()test_loss,correct=0,0with torch.no_grad():for x,y in dataloader:x,y=x.to(device),y.to(device)pred=model.forward(x)test_loss+=loss_fn(pred,y).item()correct+=(pred.argmax(1)==y).type(torch.float).sum().item()a=(pred.argmax(1)==y)b=(pred.argmax(1)==y).type(torch.float)test_loss/=num_batchescorrect/=sizeprint(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")loss_fn=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=0.001)
train(train_dataloader,model,loss_fn,optimizer)
test(test_dataloader,model,loss_fn)epochs=25
for t in range(epochs):print(f"epoch{t+1}\n")train(train_dataloader,model,loss_fn,optimizer)
print("done")
test(test_dataloader,model,loss_fn)
结论
通过本实验,我们成功地使用PyTorch构建了一个简单的神经网络,并在MNIST数据集上进行了训练和测试。实验结果表明,模型能够逐渐学习并提高准确率。然而,为了进一步提高模型性能,可以考虑使用更复杂的模型结构、调整激活函数和优化器,以及引入验证集等策略。