这是一篇学习贴。
1 import torch
2 import torch.nn as nn
3 import torch.nn.functional as F
4 import torch.optim as optim
5 from torchvision import datasets, transforms
6 import matplotlib.pyplot as plt
7 import numpy
8
9 # 一个函数,用来计算模型中有多少参数
10 def get_n_params(model):
11 np=0
12 for p in list(model.parameters()): # 参数
13 np += p.nelement()
14 return np
15
16 # 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置
17 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
18
19
20
21 input_size = 28*28 # MNIST上的图像尺寸是 28x28
22 output_size = 10 # 类别为 0 到 9 的数字,因此为十类
23 # PyTorch里包含了 MNIST, CIFAR10 等常用数据集,调用 torchvision.datasets 即可把这些数据由远程下载到本地
24 # torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)
25 # root 为数据集下载到本地后的根目录,包括 training.pt 和 test.pt 文件
26 # train,如果设置为True,从training.pt创建数据集,否则从test.pt创建。
27 # download,如果设置为True, 从互联网下载数据并放到root文件夹下
28 # transform, 一种函数或变换,输入PIL图片,返回变换之后的数据。
29 # target_transform 一种函数或变换,输入目标,进行变换
30
31 train_loader = torch.utils.data.DataLoader(
32 datasets.MNIST('./data', train=True, download=True,
33 transform=transforms.Compose(
34 [transforms.ToTensor(),
35 transforms.Normalize((0.1307,), (0.3081,))])),
36 batch_size=64, shuffle=True)
37
38 test_loader = torch.utils.data.DataLoader(
39 datasets.MNIST('./data', train=False, transform=transforms.Compose([
40 transforms.ToTensor(),
41 transforms.Normalize((0.1307,), (0.3081,))])),
42 batch_size=1000, shuffle=True)
43
44
45
46
47 ##显示部分数据
48 plt.figure(figsize=(8, 5))
49 for i in range(20):
50 plt.subplot(4, 5, i + 1)
51 image, _ = train_loader.dataset.__getitem__(i)
52 plt.imshow(image.squeeze().numpy(),'gray')
53 plt.axis('off');
54
55
56
57
58 class FC2Layer(nn.Module):
59 def __init__(self, input_size, n_hidden, output_size):
60 # nn.Module子类的函数必须在构造函数中执行父类的构造函数
61 # 下式等价于nn.Module.__init__(self)
62 super(FC2Layer, self).__init__()
63 self.input_size = input_size
64 # 这里直接用 Sequential 就定义了网络,注意要和下面 CNN 的代码区分开
65
66 self.network = nn.Sequential(
67 nn.Linear(input_size, n_hidden),
68 nn.ReLU(),
69 nn.Linear(n_hidden, n_hidden),
70 nn.ReLU(),
71 nn.Linear(n_hidden, output_size),
72 nn.LogSoftmax(dim=1)
73 )
74 def forward(self, x):
75 # view一般出现在model类的forward函数中,用于改变输入或输出的形状
76 # x.view(-1, self.input_size) 的意思是多维的数据展成二维
77 # 代码指定二维数据的列数为 input_size=784,行数 -1 表示我们不想算,电脑会自己计算对应的数字
78 # 在 DataLoader 部分,我们可以看到 batch_size 是64,所以得到 x 的行数是64
79 # 大家可以加一行代码:print(x.cpu().numpy().shape)
80 # 训练过程中,就会看到 (64, 784) 的输出,和我们的预期是一致的
81
82 # forward 函数的作用是,指定网络的运行过程,这个全连接网络可能看不啥意义,
83 # 下面的CNN网络可以看出 forward 的作用。
84 x = x.view(-1, self.input_size)
85 return self.network(x)
86
87 class CNN(nn.Module):
88 def __init__(self, input_size, n_feature, output_size):
89 # 执行父类的构造函数,所有的网络都要这么写
90 super(CNN, self).__init__()
91 # 下面是网络里典型结构的一些定义,一般就是卷积和全连接
92 # 池化、ReLU一类的不用在这里定义
93 self.n_feature = n_feature
94 self.conv1 = nn.Conv2d(in_channels=1, out_channels=n_feature, kernel_size=5)
95 self.conv2 = nn.Conv2d(n_feature, n_feature, kernel_size=5)
96 self.fc1 = nn.Linear(n_feature*4*4, 50)
97 self.fc2 = nn.Linear(50, 10)
98
99 # 下面的 forward 函数,定义了网络的结构,按照一定顺序,把上面构建的一些结构组织起来
100 # 意思就是,conv1, conv2 等等的,可以多次重用
101 def forward(self, x, verbose=False):
102 x = self.conv1(x)
103 x = F.relu(x)
104 x = F.max_pool2d(x, kernel_size=2)
105 x = self.conv2(x)
106 x = F.relu(x)
107 x = F.max_pool2d(x, kernel_size=2)
108 x = x.view(-1, self.n_feature*4*4)
109 x = self.fc1(x)
110 x = F.relu(x)
111 x = self.fc2(x)
112 x = F.log_softmax(x, dim=1)
113 return x
114
115
116
117 # 训练函数
118 def train(model):
119 model.train()
120 # 主里从train_loader里,64个样本一个batch为单位提取样本进行训练
121 for batch_idx, (data, target) in enumerate(train_loader):
122 # 把数据送到GPU中
123 data, target = data.to(device), target.to(device)
124
125 optimizer.zero_grad()
126 output = model(data)
127 loss = F.nll_loss(output, target)
128 loss.backward()
129 optimizer.step()
130 if batch_idx % 100 == 0:
131 print('Train: [{}/{} ({:.0f}%)] Loss: {:.6f}'.format(
132 batch_idx * len(data), len(train_loader.dataset),
133 100. * batch_idx / len(train_loader), loss.item()))
134
135
136 def test(model):
137 model.eval()
138 test_loss = 0
139 correct = 0
140 for data, target in test_loader:
141 # 把数据送到GPU中
142 data, target = data.to(device), target.to(device)
143 # 把数据送入模型,得到预测结果
144 output = model(data)
145 # 计算本次batch的损失,并加到 test_loss 中
146 test_loss += F.nll_loss(output, target, reduction='sum').item()
147 # get the index of the max log-probability,最后一层输出10个数,
148 # 值最大的那个即对应着分类结果,然后把分类结果保存在 pred 里
149 pred = output.data.max(1, keepdim=True)[1]
150 # 将 pred 与 target 相比,得到正确预测结果的数量,并加到 correct 中
151 # 这里需要注意一下 view_as ,意思是把 target 变成维度和 pred 一样的意思
152 correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()
153
154 test_loss /= len(test_loader.dataset)
155 accuracy = 100. * correct / len(test_loader.dataset)
156 print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
157 test_loss, correct, len(test_loader.dataset),
158 accuracy))
159
160
161 #在小型全连接上测试
162 n_hidden = 8 # number of hidden units
163
164 model_fnn = FC2Layer(input_size, n_hidden, output_size)
165 model_fnn.to(device)
166 optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
167 print('Number of parameters: {}'.format(get_n_params(model_fnn)))
168
169 train(model_fnn)
170 test(model_fnn)
171
172
173
174 #在CNN上测试 Training settings
175 n_features = 6 # number of feature maps
176
177 model_cnn = CNN(input_size, n_features, output_size)
178 model_cnn.to(device)
179 optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
180 print('Number of parameters: {}'.format(get_n_params(model_cnn)))
181
182 train(model_cnn)
183 test(model_cnn)
184
185
186
187
188 ## 打乱像素顺序再次在两个网络上训练与测试
189 # 考虑到CNN在卷积与池化上的优良特性,如果我们把图像中的像素打乱顺序
190 # 这样 卷积 和 池化 就难以发挥作用了,为了验证这个想法,我们把图像中的像素打乱顺序再试试。
191 # 这里解释一下 torch.randperm 函数,给定参数n,返回一个从0到n-1的随机整数排列
192 perm = torch.randperm(784)
193 plt.figure(figsize=(8, 4))
194 for i in range(10):
195 image, _ = train_loader.dataset.__getitem__(i)
196 # permute pixels
197 image_perm = image.view(-1, 28*28).clone()
198 image_perm = image_perm[:, perm]
199 image_perm = image_perm.view(-1, 1, 28, 28)
200 plt.subplot(4, 5, i + 1)
201 plt.imshow(image.squeeze().numpy(), 'gray')
202 plt.axis('off')
203 plt.subplot(4, 5, i + 11)
204 plt.imshow(image_perm.squeeze().numpy(), 'gray')
205 plt.axis('off')
206
207
208
209
210
211 # 打乱像素顺序再次在两个网络上训练与测试
212
213 # 对每个 batch 里的数据,打乱像素顺序的函数
214 def perm_pixel(data, perm):
215 # 转化为二维矩阵
216 data_new = data.view(-1, 28*28)
217 # 打乱像素顺序
218 data_new = data_new[:, perm]
219 # 恢复为原来4维的 tensor
220 data_new = data_new.view(-1, 1, 28, 28)
221 return data_new
222
223 # 训练函数
224 def train_perm(model, perm):
225 model.train()
226 for batch_idx, (data, target) in enumerate(train_loader):
227 data, target = data.to(device), target.to(device)
228 # 像素打乱顺序
229 data = perm_pixel(data, perm)
230
231 optimizer.zero_grad()
232 output = model(data)
233 loss = F.nll_loss(output, target)
234 loss.backward()
235 optimizer.step()
236 if batch_idx % 100 == 0:
237 print('Train: [{}/{} ({:.0f}%)] Loss: {:.6f}'.format(
238 batch_idx * len(data), len(train_loader.dataset),
239 100. * batch_idx / len(train_loader), loss.item()))
240
241 # 测试函数
242 def test_perm(model, perm):
243 model.eval()
244 test_loss = 0
245 correct = 0
246 for data, target in test_loader:
247 data, target = data.to(device), target.to(device)
248
249 # 像素打乱顺序
250 data = perm_pixel(data, perm)
251
252 output = model(data)
253 test_loss += F.nll_loss(output, target, reduction='sum').item()
254 pred = output.data.max(1, keepdim=True)[1]
255 correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()
256
257 test_loss /= len(test_loader.dataset)
258 accuracy = 100. * correct / len(test_loader.dataset)
259 print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
260 test_loss, correct, len(test_loader.dataset),
261 accuracy))
262
263
264
265
266 ##在小型全连接上测试
267 perm = torch.randperm(784)
268 n_hidden = 8 # number of hidden units
269
270 model_fnn = FC2Layer(input_size, n_hidden, output_size)
271 model_fnn.to(device)
272 optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
273 print('Number of parameters: {}'.format(get_n_params(model_fnn)))
274
275 train_perm(model_fnn, perm)
276 test_perm(model_fnn, perm)
277
278
279
280 #在卷积神经网络上训练与测试:
281 perm = torch.randperm(784)
282 n_features = 6 # number of feature maps
283
284 model_cnn = CNN(input_size, n_features, output_size)
285 model_cnn.to(device)
286 optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
287 print('Number of parameters: {}'.format(get_n_params(model_cnn)))
288
289 train_perm(model_cnn, perm)
290 test_perm(model_cnn, perm)