全部代码:
import matplotlib.pyplot
import torch
from torch import nn # nn是完成神经网络相关的一些工作
from torch.nn import functional as F # functional是常用的一些函数
from torch import optim # 优化的工具包
import torchvision
from matplotlib import pyplot as plt
from utils import plot_images, plot_curve, one_hot
# step1 : load dataset
# 指定了每次梯度更新时用于训练模型的数据样本数量
batch_size = 512 # 一次处理的图片数量 我们的处理的图片是28×28像素
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('mnist_data', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(), # 把numpy格式转换为tensor
torchvision.transforms.Normalize( # 图像像素分布在0-1,所以要-0.1307,除以标准差0.3801,使得数据能够在0附近均匀分布
(0.1307,), (0.3081,))
])),
# 1.torchvision.transforms.Normalize 是 PyTorch 中的一个非常有用的图像预处理转换(transform),
# 它主要用于将图像数据标准化到特定的均值(mean)和标准差(std)上。这个转换通常用于训练深度学习模型之前,
# 特别是卷积神经网络(CNN)模型,因为标准化有助于模型更快地收敛并提高模型的性能。
# 2.这里,(0.1307,) 和 (0.3081,) 分别指定了用于标准化的均值和标准差。注意,虽然这两个元组只包含一个元素,
# 但它们实际上是为每个通道(channel)指定的。在这个特定的例子中,由于这些值是针对MNIST数据集的,而MNIST数据集是灰度图像,所以只有一个通道。
# 3.虽然这里直接给出了均值(0.1307)和标准差(0.3081),但在实际应用中,这些值通常是通过计算整个训练数据集的像素值的统计量来获得的。
# 对于MNIST这样的灰度数据集,计算整个数据集的像素均值和标准差,然后用于所有图像的标准化。
batch_size=batch_size, shuffle=True) # batch_size一次行处理多少张图片,shuffle意味着加载时要做一个随机的打散
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('mnist_data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=False)
x, y = next(iter(train_loader))
# x代表当前批次(batch)中的输入数据,即图像数据。对于MNIST数据集来说,x的形状通常是[batch_size, 1, 28, 28](如果数据没有被转换为灰度图并归一化,
# 则可能是[batch_size, 3, 28, 28],但MNIST是灰度图,所以通道数为1)。这里的batch_size是你在创建DataLoader时指定的每个批次中的样本数。
# y代表当前批次中每个输入数据对应的标签(label),即每个图像对应的数字(0-9之间的整数)。y的形状通常是[batch_size],表示每个样本的类别标签。
print(x.shape, y.shape, x.min(), x.max())
plot_images(x, y, 'image sample')
matplotlib.pyplot.show()
# step2 : bulid a network
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
#wx+b
self.fc1 = nn.Linear(28*28,256) #,28*28是x的维度,256一般根据经验随机决定,大维变成小维
self.fc2 = nn.Linear(256,64) #第二层的输入与上一层的输出相同
self.fc3 = nn.Linear(64,10) #10分类,此处不是根据经验
#计算过程
def forward(self,x):
# x: [b,1,28,28]
# h1 =relu(xw1+b1)
x = F.relu(self.fc1(x))
# h2 = relu(h1w2+b2)
x = F.relu(self.fc2(x))
# h3 = h2w3+b3,最后一层看情况添加激活函数
x = self.fc3(x) # 激活函数加不加取决于你的任务
return x
# step3 : 训练。 训练的逻辑是:每一次求导,然后再去更新
# net.parameters()返回[w1,b1,w2,b2,w3,b3],这就是我们要优化的; lr是学习步长 ;momentum帮助更好的优化
net = Net()
# 使用SGD优化器,学习率为0.01,动量为0.9
optimizer = optim.SGD(net.parameters(),lr=0.01,momentum=0.9)
# 把loss保存起来
train_loss = []
# epoch 是整个数据集的训练轮数。在这个例子中,数据集将被遍历3次。
# batch_idx 是当前批次的索引。
for epoch in range(3):
for batch_idx, (x,y) in enumerate(train_loader):
# x: [b,1,28,28] y : [512]
# 将图像数据从[b,1,28,28]打平成[b,feature],size(0)是batch,因为网络期望的输入是一个一维的特征向量。
x = x.view(x.size(0),28*28)
# [b,10]
# one_hot是一个自定义函数,用于将类别标签转换为one-hot编码
out = net(x)
# [b,10],真实的y
y_onehot= one_hot(y)
# loss=mse(out,y_onehot),求其均方差
loss = F.mse_loss(out,y_onehot)
#清零梯度
optimizer.zero_grad()
#计算梯度
loss.backward()
# 更新梯度:w‘ = w-lr*grad
optimizer.step()
#进行梯度下降的可视化,把数据记录下来
train_loss.append(loss.item())
# 每隔10个批次,打印当前轮次、批次索引和损失值,以便于监控训练过程。
if batch_idx % 10 == 0:
print(epoch,batch_idx,loss.item())
# 将训练损失绘制成曲线图
plot_curve(train_loss)
#we can get optimal [w1,b1,w2,b2,w3,b3]
# step4 : 测试test
total_correct = 0
# 打印loss
for x, y in test_loader:
x = x.view(x.size(0), 28 * 28)
out = net(x) # 得到网络的输出
# out: [b, 10] => pred: [b]
pred = out.argmax(dim=1)
correct = pred.eq(y).sum().float().item() # item()取数值 当前batch正确的个数
total_correct += correct
total_num = len(test_loader.dataset) # 总的测试的数量
acc = total_correct / total_num # 准确率
print('test acc:', acc)
x, y = next(iter(test_loader))
out = net(x.view(x.size(0), 28 * 28))
pred = out.argmax(dim=1)
plot_images(x, pred, 'test')
# 后期可进行的工作:
# def net()中增加网络层数
# def forward()中最后一层可以用softmax()
# loss:F.mse_loss()改成交叉熵函数
utils工具包:
# 四个步骤:load data; bulid model; train; test
import torch
from matplotlib import pyplot as plt
def plot_curve(data): # 绘制loss下降的曲线图
fig = plt.figure()
plt.plot(range(len(data)), data, color='blue')
plt.legend(['value'], loc='upper right')
plt.xlabel('step')
plt.ylabel('value')
plt.show()
def plot_images(img, label, name): # 画图片(因为这里涉及到一个图片的识别),这个地方可以方便地看到图片的识别结果
fig = plt.figure()
for i in range(6):
plt.subplot(2, 3, i + 1)
plt.tight_layout()
plt.imshow(img[i][0] * 0.3081 + 0.1307, cmap='gray', interpolation='none')
plt.title("{}:{}".format(name, label[i].item()))
plt.xticks([])
plt.yticks([])
plt.show
def one_hot(label, depth=10): # 需要通过scatter()完成one_hot编码
out = torch.zeros(label.size(0), depth)
idx = torch.LongTensor(label).view(-1, 1)
out.scatter_(dim=1, index=idx, value=1)
return out