本文参考了互联网上的一些资料,基于个人理解。
一、搭建神经网络
先在根目录下创建一个data文件夹,用来放下载的数据集
1.需要用的包
import torch
import torch.nn as nn
from torch import optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
2.准备训练用的数据
# 定义transform为这两个函数的集合
transform = transforms.Compose([
transforms.ToTensor(), # 图像转换为张量
transforms.Normalize((0.1307,), (0.3081,)) # 标准化图像数据
])
'''下载训练集数据集(包含训练图片和标签)'''
# datasets.MNIST来加载MNIST数据集作为训练数据集。
# root='data':指定数据集存储的根目录,可以根据需要进行更改。
# train=True:表示加载训练数据集,反之则是测试数据集
# download=True:如果数据集在指定路径中不存在,将自动从官方源下载并保存。
# transform=ToTensor():指定数据转换操作,将图像数据转换为PyTorch中的Tensor张量格式。
train_dataset = datasets.MNIST(
root='data',
train=True,
download=True,
transform=transform, # 张量
) # 对于pythorch库能够识别的数据一般是tensor张量
test_dataset = datasets.MNIST(
root='data',
train=False,
download=True,
transform=transform
)
张量可以理解为不管几维都可以的、不管什么类型都可以的数据类型,为了避免类型冲突,统一转成张量处理。
3.对数据做一些处理
# 创建加载器,指定数据集,指定batch的数量为64,shuffle指定是否打乱数据
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
4.创建神经网络
# 继承构造神经网络的基类
class SimpleNN(nn.Module):
# 构造方法
def __init__(self):
# 找到SimpleNN的父类,用子类实体创建父类实体,实现用子类实体调用父类方法和参数
super(SimpleNN, self).__init__()
# 全连接层
self.fc1 = nn.Linear(28 * 28, 128)
self.fc2 = nn.Linear(128, 64)
self.fc3 = nn.Linear(64, 10)
# 前向传播,计算函数
def forward(self, x):
# 把x展成m*n维,给定了n=28*28, -1代表自行计算n的数值
x = x.view(-1, 28 * 28)
# 激活函数
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x
self.fc1
是一个nn.Linear
类的实例,它在类SimpleNN
的构造函数中被定义为一个全连接(线性)层。这个层的定义形式是nn.Linear(in_features, out_features)
,其中in_features
是输入特征的数量,out_features
是输出特征的数量。- 当你调用
self.fc1(x)
时,实际上是在执行这个全连接层的前向传播操作。x
是这个操作的输入张量,它通过全连接层的权重矩阵进行线性变换,然后加上偏置。如果x
的形状是[N, in_features]
(N 是批量大小),那么输出的形状将会是[N, out_features]
。
torch.relu
是一个函数,用于应用激活函数 ReLU(Rectified Linear Unit,修正线性单元)。ReLU 函数的公式是f(x) = max(0, x)
,它的作用是将所有的负值置为0,正值保持不变。这是深度学习中常用的激活函数,因为它能够在保持非线性的同时,减轻梯度消失的问题。- 在这行代码中,
torch.relu
被用来对self.fc1(x)
的输出应用 ReLU 激活函数。这样做的目的是增加网络的非线性能力,因为线性模型的表达能力是有限的,而通过加入非线性激活函数,网络能够学习到更复杂的模式。
5.损失函数和优化器
model = SimpleNN()
# 交叉熵损失,定义损失函数
criterion = nn.CrossEntropyLoss()
# 优化器,包括随机梯度下降、adam、rmsprop等
optimizer = optim.Adam(model.parameters(), lr=0.001)
最原始的优化器是梯度下降算法,一些算法在此基础上作出了改进。
6.模型训练
def train(model, train_loader, criterion, optimizer, epochs):
for epoch in range(epochs):
# 每一轮开始时重置损失值
running_loss = 0.0
# enumerate 返回可迭代数据的元组,元组内容为id、data
for i, (images, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(images)
# 计算损失值
loss = criterion(outputs, labels)
# 计算梯度,反向回传,存在grad属性里
loss.backward()
optimizer.step()
# 累加当前的batch的损失值到变量中
running_loss += loss.item()
if (i + 1) % 100 == 0:
print(
f'Epoch [{epoch + 1}/{epochs}], Step [{i + 1}/{len(train_loader)}], Loss:{running_loss / 100:.4f}')
running_loss = 0.0
# 训练模型
train(model, train_loader, criterion, optimizer, epochs=5)
三兄弟,顺序不能颠倒
optimizer.zero_grad() 清除上一个batch累计的梯度
loss.backward() 反向回传求w存到参数grad中
optimizer.step() 执行梯度下降算法
7.测试模型
def test(model, test_loader):
# 切换到评估模式
model.eval()
# 记录预测正确的样本数和总样本数
correct = 0
total = 0
# 禁止自动求梯度
with torch.no_grad():
for images, labels in test_loader:
outputs = model(images)
# predicted用来记录最大输出数据的索引,返回最大值和索引,dim=1按行输出,=0按列输出
_, predicted = torch.max(outputs.data, 1)
# 总样本
total += labels.size(0)
# 累计正确样本数
correct += (predicted == labels).sum().item()
print(f'Accuracy on test set: {100 * correct / total:.2f}%')
# 调用
test(model, test_loader)
二、预测一个看看
1.需要用的包
另开了一个文件
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
from model import model
2.再把测试集调出来
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform, download=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
# 没有这句也一样好像
model = model
3.测试+图形化
其实跟上一个test没啥区别,只是多了个图形化
def predict_images(model, data_loader, num_images=5):
model.eval() # 设置为评估模式
# 把测试加载器转成迭代类型,用next拿数据
images, labels = next(iter(data_loader))
images, labels = images[:num_images], labels[:num_images] # 取前num_images张图片进行预测
with torch.no_grad(): # 不计算梯度
outputs = model(images)
_, predicted = torch.max(outputs, 1)
# 显示图片和预测结果
plt.figure(figsize=(10, num_images * 2))
for idx in range(num_images):
plt.subplot(1, num_images, idx + 1)
plt.imshow(images[idx].numpy().squeeze(), cmap='gray') # 显示图片
plt.title(f'Predicted: {predicted[idx].item()}') # 显示预测的类别
plt.axis('off')
plt.show()
# 使用测试集的一个batch进行预测展示
predict_images(model, test_loader, num_images=5)