--- tags: PyTorch title: PyTorch - CNN 卷積神經網絡 - MNIST手寫數字辨識 --- # PyTorch - CNN 卷積神經網絡 - MNIST手寫數字辨識 ## 1. import 需要的 packages ```python= import torch import torch.nn as nn import torch.nn.functional as F from torch.autograd import Variable from torchsummary import summary from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt from keras.datasets import mnist ``` ## 2. 確認一下使用 cuda 或是 cpu ```python=+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(device) ``` ## 3. 資料預處理 (Data Preprcessing) ```python=+ # 使用 keras 直接載入 MNIST dataset (X_train, Y_train), (X_test, Y_test) = mnist.load_data() # Normalization X_train = X_train.astype('float32') / 255 X_test = X_test.astype('float32') / 255 # 利用 sklearn,將每一類都以 8:2 的比例分成訓練資料和測試資料 features_train, features_test, targets_train, targets_test = train_test_split(X_train, Y_train, test_size = 0.2, random_state = 42) # 將切好的 data 轉成 tensor 形式 # Training Datasets featuresTrain = torch.from_numpy(features_train) targetsTrain = torch.from_numpy(targets_train).type(torch.LongTensor) # data type is long # Testing Datasets featuresTest = torch.from_numpy(features_test) targetsTest = torch.from_numpy(targets_test).type(torch.LongTensor) # data type is long # 使用 torch.utils.data.TensorDataset 將 train 和 test datasets 存成 tensor 形式 # Pytorch train and test TensorDataset train = torch.utils.data.TensorDataset(featuresTrain, targetsTrain) test = torch.utils.data.TensorDataset(featuresTest, targetsTest) ``` ## 4.超參數設定 ```python=+ LR = 0.01 # Learning Rate batch_size = 100 # Batch size n_iters = 10000 # Iterations each epoch num_epochs = n_iters / (len(features_train) / batch_size) num_epochs = int(num_epochs) # Epochs ``` ## 5. 將 Training data 和 Testing data 傳至 dataloader ```python=+ # torch.utils.data.TensorDatasetDataLoader(dataset, batch_size=1, shuffle=False,...) 為數據加載器 # 組合數據集和採樣器,並在數據集上提供單進程或多進程迭代器 # Pytorch DataLoader train_loader = torch.utils.data.DataLoader(train, batch_size = batch_size, shuffle = True) test_loader = torch.utils.data.DataLoader(test, batch_size = batch_size, shuffle = True) ``` ## 6. 建立 CNN 模型 **<font color='red'>建立 CNN model ,需要注意的是 `input shape` 在經過每一層後的變化,在設計的時候要稍微計算一下,最後在接上 fully connected 層時要符合其 `input shape` 。</font>** ```python=+ # Create CNN Model class CNN_Model(nn.Module): def __init__(self): super(CNN_Model, self).__init__() # Convolution 1 , input_shape=(1,28,28), output_shape=(16,24,24) self.cnn1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=0) # activation self.relu1 = nn.ReLU() # Max pool 1, output_shape=(16,12,12) self.maxpool1 = nn.MaxPool2d(kernel_size=2) # Convolution 2, output_shape=(32,8,8) self.cnn2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, stride=1, padding=0) # activation self.relu2 = nn.ReLU() # Max pool 2, output_shape=(32,4,4) self.maxpool2 = nn.MaxPool2d(kernel_size=2) # Fully connected 1, input_shape=(32*4*4) self.fc1 = nn.Linear(32 * 4 * 4, 10) def forward(self, x): # Convolution 1 out = self.cnn1(x) out = self.relu1(out) # Max pool 1 out = self.maxpool1(out) # Convolution 2 out = self.cnn2(out) out = self.relu2(out) # Max pool 2 out = self.maxpool2(out) out = out.view(out.size(0), -1) # Linear function (readout) out = self.fc1(out) return out model = CNN_Model().to(device) # Create the CNN Model optimizer = torch.optim.Adam(model.parameters(), lr = LR) # 選擇你想用的 optimizer(Adam) summary(model, (1, 28, 28)) # 利用 torchsummary 的 summary package 印出模型資訊,input size: (1 * 28 * 28) loss_func = nn.CrossEntropyLoss() # 選擇想用的 loss function(CrossEntropy) input_shape = (-1, 1, 28, 28) ``` ``` ---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Conv2d-1 [-1, 16, 24, 24] 416 ReLU-2 [-1, 16, 24, 24] 0 MaxPool2d-3 [-1, 16, 12, 12] 0 Conv2d-4 [-1, 32, 8, 8] 12,832 ReLU-5 [-1, 32, 8, 8] 0 MaxPool2d-6 [-1, 32, 4, 4] 0 Linear-7 [-1, 10] 5,130 ================================================================ Total params: 18,378 Trainable params: 18,378 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.00 Forward/backward pass size (MB): 0.19 Params size (MB): 0.07 Estimated Total Size (MB): 0.27 ---------------------------------------------------------------- ``` ## 7. 訓練模型 ```python=+ # 訓練 function def fit_model(model, loss_func, optimizer, input_shape, num_epochs, train_loader, test_loader): # Traning the Model # 儲存訓練資訊的 List training_loss, training_accuracy = [], [] validation_loss, validation_accuracy = [], [] for epoch in range(num_epochs): # --------------------------- # Training Stage # --------------------------- correct_train, total_train = 0, 0 for i, (images, labels) in enumerate(train_loader): train, labels = images.view(input_shape).to(device), labels.to(device) # 取出 training data 以及 labels(轉 device 的型態) optimizer.zero_grad() # 清空梯度 outputs = model(train) # 將訓練資料輸入至模型進行訓練 (Forward propagation) train_loss = loss_func(outputs, labels) # 計算 loss train_loss.backward() # 將 loss 反向傳播 optimizer.step() # 更新權重 # 計算訓練資料的準確度 (correct_train / total_train) predicted = torch.max(outputs.data, 1)[1] # 取出預測的 maximum total_train += len(labels) # 全部的 label 數 (Total number of labels) correct_train += (predicted == labels).float().sum() # 全部猜中的個數 (Total correct predictions) # 將 accuracy 和 loss 存入 list train_accuracy = 100 * correct_train / float(total_train) # training accuracy (To cpu()) training_accuracy.append(train_accuracy.cpu()) training_loss.append(train_loss.data.cpu()) # training loss (To cpu()) # -------------------------- # Testing Stage # -------------------------- correct_test, total_test = 0, 0 for images, labels in test_loader: test, labels = images.view(input_shape).to(device), labels.to(device) # 取出 testing data 以及 labels(轉 device 的型態) outputs = model(test) # 將測試資料輸入至模型進行測試 (Forward propagation) val_loss = loss_func(outputs, labels) # 計算 loss # 計算測試資料的準確度 (correct_test / total_test) predicted = torch.max(outputs.data, 1)[1] # 取出預測的 maximum total_test += len(labels) # 全部的 label 數 (Total number of labels) correct_test += (predicted == labels).float().sum() # 全部猜中的個數 (Total correct predictions) # 將 accuracy 和 loss 存入 list val_accuracy = 100 * correct_test / float(total_test) # testing accuracy (To cpu()) validation_accuracy.append(val_accuracy.cpu()) validation_loss.append(val_loss.data.cpu()) # testing loss (To cpu()) # 顯現當前 Epoch 訓練情況 print('Train Epoch: {}/{} Traing_Loss: {} Traing_acc: {:.6f}% Val_Loss: {} Val_accuracy: {:.6f}%'.format(epoch+1, num_epochs, train_loss.data, train_accuracy, val_loss.data, val_accuracy)) return training_loss, training_accuracy, validation_loss, validation_accuracy # 訓練模型 training_loss, training_accuracy, validation_loss, validation_accuracy = fit_model(model, loss_func, optimizer, input_shape, num_epochs, train_loader, test_loader) ``` ## 8. 將每一個 epoch 的 Loss 以及 Training / Testing accuracy 紀錄下來並繪製成圖 ```python=+ # Loss plt.plot(range(num_epochs), training_loss, 'b-', label='Training_loss') plt.plot(range(num_epochs), validation_loss, 'g-', label='validation_loss') plt.title('Training & Validation loss') plt.xlabel('Number of epochs') plt.ylabel('Loss') plt.legend() plt.savefig('loss.png') plt.show() # Accuracy plt.plot(range(num_epochs), training_accuracy, 'b-', label='Training_accuracy') plt.plot(range(num_epochs), validation_accuracy, 'g-', label='Validation_accuracy') plt.title('Training & Validation accuracy') plt.xlabel('Number of epochs') plt.ylabel('Accuracy') plt.legend() plt.savefig('acc.png') plt.show() ``` ![](https://imgur.com/PTHWXiw.png) ![](https://imgur.com/ptk2ECs.png)