暂存源码,待附详细解析。
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms # torchvision是独立于pytorch的关于图像操作的一些方便工具库。
# vision.datasets : 几个常用视觉数据集,可以下载和加载
# vision.models : 流行的模型,例如 AlexNet, VGG, ResNet 和 Densenet 以及训练好的参数。
# vision.transforms : 常用的图像操作,例如:数据类型转换,图像到tensor ,numpy 数组到tensor , tensor 到 图像等。
# vision.utils : 用于把形似 (3 x H x W) 的张量保存到硬盘中,给一个mini-batch的图像可以产生一个图像格网
print(torch.__version__) # pytorch 的版本
# 定义一些超参数
BATCH_SIZE=512 # batch_size即每批训练的样本数量
EPOCHS=20 # 循环次数
DEVICE=torch.device("cuda" if torch.cuda.is_available() else "cpu") # 让torch判断是否使用GPU,即device定义为CUDA或CPU
# 下载 MNIST的数据集
# 训练集
train_loader = torch.utils.data.DataLoader( # vision.utils : 用于把形似 (3 x H x W) 的张量保存到硬盘中,给一个mini-batch的图像可以产生一个图像格网。
datasets.MNIST('data', train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(), # 图像转化为Tensor
transforms.Normalize((0.1307,), (0.3081,)) # 标准化(参数不明)
])),
batch_size=BATCH_SIZE, shuffle=True) # shuffle() 方法将序列的所有元素随机排序
# 测试集
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('data', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=BATCH_SIZE, shuffle=True) # shuffle() 方法将序列的所有元素随机排序
# 下面我们定义一个网络,网络包含两个卷积层,conv1和conv2,
# 然后紧接着两个线性层作为输出,
# 最后输出10个维度,这10个维度我们作为0-9的标识来确定识别出的是那个数字
# 这里建议大家将每一层的输入和输出维度都作为注释标注出来,这样后面阅读代码的会方便很多
class ConvNet(nn.Module):
def __init__(self):
super().__init__()
# 128x28
self.conv1=nn.Conv2d(1,10,5) # 10, 24x24
self.conv2=nn.Conv2d(10, 20,3) #128, 10x10
self.fc1=nn.Linear(20*10*10, 500)
self.fc2=nn.Linear(500, 10)
def forward(self, x):
in_size=x.size(0)
# 卷积层 -> relu -> 最大池化
out = self.conv1(x) # 24
out = F.relu(out)
out = F.max_pool2d(out, 2, 2) # 12
#卷积层 -> relu -> 多行变一行 -> 线性 -> relu -> 线性 -> sigmoid
out = self.conv2(out) # 10
out = F.relu(out)
out = out.view(in_size, -1) # view()函数作用是将一个多行的Tensor,拼接成一行。
out = self.fc1(out)
out = F.relu(out)
out = self.fc2(out)
# softmax
out = F.log_softmax(out, dim=1)
# 返回值 out
return out
# 我们实例化一个网络,实例化后使用“.to”方法将网络移动到GPU
model = ConvNet().to(DEVICE)
# 优化器我们也直接选择简单暴力的Adam
optimizer = optim.Adam(model.parameters())
# -----------------------训练-------------------------------
# 定义 训练函数 ,我们将训练的所有操作都封装到train函数中
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device) # CPU转GPU
optimizer.zero_grad() # 优化器清零
output = model(data) # 由model,计算输出值
loss = F.nll_loss(output, target) # 计算损失函数loss
loss.backward() # loss反向传播
optimizer.step() # 优化器优化
if(batch_idx+1)%30 == 0: # 输出结果
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
# -------------------------------------------------------------
# ---------------------测试-----------------------------------
# 测试的操作也一样封装成一个函数
def test(model, device, test_loader):
model.eval() # 转换模式
test_loss = 0 # 损失函数初始化为0
correct = 0 # correct是啥玩意?????
with torch.no_grad(): # 表示不反向求导(应该是)
for data, target in test_loader: # 遍历所有的data和target
data, target = data.to(device), target.to(device) # CPU -> GPU
output = model(data) # output为预测值,由model计算出
test_loss += F.nll_loss(output, target, reduction='sum').item() ### 将一批的损失相加
pred = output.max(1, keepdim=True)[1] ### 找到概率最大的下标
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
# ---------------------------------------------------------------
# 下面开始训练,这里就体现出封装起来的好处了,只要写两行就可以了
for epoch in range(1, EPOCHS + 1):
train(model, DEVICE, train_loader, optimizer, epoch)
test(model, DEVICE, test_loader)