1. import numpy as np
    2. import torch
    3. # 调入pytorch内置的mnist数据
    4. from torchvision.datasets import mnist
    5. # 导入预处理模块
    6. import torchvision.transforms as transforms
    7. from torch.utils.data import DataLoader
    8. # 导入nn以及优化器
    9. import torch.nn.functional as F
    10. import torch.optim as optim
    11. from torch import nn
    12. import matplotlib.pyplot as plt
    13. # 当前程序为残差的手写数字识别
    14. # 定义一些超参数
    15. train_batch_size = 32
    16. test_batch_size = 64
    17. learning_rate = 0.01
    18. num_epoches = 10
    19. lr = 0.01
    20. momentum = 0.5
    21. # 下载数据并且进行数据预处理
    22. # 定义预处理函数,这些函数依次放在Compose函数中
    23. # 其中transforms.Compose用来把他们组合在一起,Normalize([0.5], [0.5])用来进行归一化
    24. # 因图像是灰色的,只有一个通道,所以只有一个数字,如果是三个通道,就需要三个数字
    25. transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.5], [0.5])])
    26. # 下载数据,并对数据进行预处理
    27. train_dataset = mnist.MNIST('./data', train=True, transform=transform, download=False)
    28. test_dataset = mnist.MNIST('./data', train=False, transform=transform)
    29. # dataloader是一个可迭代对象,可以使用迭代器一样使用。
    30. train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
    31. test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)
    32. # 可视化数据源
    33. examples = enumerate(test_loader)
    34. batch_idx, (example_data, example_targets) = next(examples)
    35. fig = plt.figure()
    36. # 用来展示图片
    37. # for i in range(6):
    38. # plt.subplot(2, 3, i + 1)
    39. # plt.tight_layout()
    40. # plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
    41. # plt.title("Ground Truth: {}".format(example_targets[i]))
    42. # plt.xticks([])
    43. # plt.yticks([])
    44. # plt.show()
    45. class ResidualBlock(nn.Module):
    46. def __init__(self, channels):
    47. super(ResidualBlock, self).__init__()
    48. self.channels = channels
    49. self.conv1 = torch.nn.Conv2d(channels, channels, kernel_size=3, padding=1)
    50. self.conv2 = torch.nn.Conv2d(channels, channels, kernel_size=3, padding=1)
    51. def forward(self, x):
    52. y = F.relu(self.conv1(x))
    53. y = self.conv2(y)
    54. return F.relu(x + y)
    55. class Net(nn.Module):
    56. def __init__(self):
    57. super(Net, self).__init__()
    58. self.conv1 = nn.Conv2d(1, 10, 5)
    59. self.mp = nn.MaxPool2d(2)
    60. self.rblock1 = ResidualBlock(10)
    61. self.conv2 = nn.Conv2d(10, 20, 3)
    62. self.rblock2 = ResidualBlock(20)
    63. self.flatten1 = nn.Flatten()
    64. self.linear1 = nn.Linear(500, 10)
    65. def forward(self, x):
    66. x = F.relu(self.mp(self.conv1(x)))
    67. x = self.rblock1(x)
    68. x = F.relu(self.mp(self.conv2(x)))
    69. x = self.rblock2(x)
    70. x = self.flatten1(x)
    71. x = self.linear1(x)
    72. x = F.log_softmax(x, dim=1)
    73. return x
    74. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    75. # 实例化网络
    76. model = Net()
    77. model.to(device)
    78. # 定义损失函数和优化器
    79. criterion = nn.CrossEntropyLoss();
    80. optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)
    81. # 训练模型
    82. losses = []
    83. acces = []
    84. eval_losses = []
    85. eval_acces = []
    86. for epoch in range(num_epoches):
    87. train_loss = 0
    88. train_acc = 0
    89. model.train()
    90. # 动态修改学习率
    91. if epoch % 5 == 0:
    92. optimizer.param_groups[0]['lr'] *= 0.1
    93. for batch_idx, (img, label) in enumerate(train_loader):
    94. # print(img.shape)
    95. # torch.Size([64, 1, 28, 28])
    96. img = img.to(device)
    97. label = label.to(device)
    98. # 这里把图像压成了一维的
    99. # img = img.view(img.size(0), -1)
    100. # 前向传播
    101. out = model(img)
    102. loss = criterion(out, label)
    103. # 反向传播
    104. optimizer.zero_grad()
    105. loss.backward()
    106. optimizer.step()
    107. # 记录误差
    108. train_loss += loss.item()
    109. # 计算分类的准确率
    110. _, pred = out.max(1)
    111. num_correct = (pred == label).sum().item()
    112. acc = num_correct / img.shape[0]
    113. train_acc += acc
    114. losses.append(train_loss / len(train_loader))
    115. acces.append(train_acc / len(train_loader))
    116. # 在测试集上检验效果
    117. eval_loss = 0
    118. eval_acc = 0
    119. # 将模型改为预测模式
    120. model.eval()
    121. for img, label in test_loader:
    122. img = img.to(device)
    123. label = label.to(device)
    124. out = model(img)
    125. loss = criterion(out, label)
    126. # 记录误差
    127. eval_loss += loss.item()
    128. # 记录准确率
    129. _, pred = out.max(1)
    130. num_correct = (pred == label).sum().item()
    131. acc = num_correct / img.shape[0]
    132. eval_acc += acc
    133. eval_losses.append(eval_loss / len(test_loader))
    134. eval_acces.append(eval_acc / len(test_loader))
    135. print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}'
    136. .format(epoch, train_loss / len(train_loader), train_acc / len(train_loader),
    137. eval_loss / len(test_loader), eval_acc / len(test_loader)))
    138. plt.title('train loss')
    139. plt.plot(np.arange(len(losses)), losses)
    140. plt.legend(['Train Loss'], loc='upper right')
    141. plt.show()