木槿花篱

嘻嘻,欢迎~

MNIST 数据集分类# 构建简单的CNN对 mnist 数据集进行分类

这是一篇学习贴。
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}%)]\tLoss: {:.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('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.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}%)]\tLoss: {:.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('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.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)

 

posted @ 2020-07-29 13:41  木槿花篱  阅读(679)  评论(0编辑  收藏  举报