# <center>深度學習基石與實務 作業二</center> ## <center>611121212 李奕承</center> ## 這份作業的過程讓我學習到 - 如何把圖片讀進來作為訓練資料 - 如何使用 卷基層、池化層、Flatten()、Sequential() ### 0.需要引入的庫 ```python import matplotlib.pyplot as plt import torch import numpy from torchvision.io import read_image import torch.nn as nn from torch.utils.data import Dataset from torch.utils.data import DataLoader import pandas as pd import time from os import listdir ``` ### 1.如何讀取圖片? 與第一份作業不同, kaggle 上提供的csv只有圖片檔名和label,所以要先讀到檔名,再用檔名把所有圖片讀進來 ```py # 設定 train_truth.csv 的路徑 data_path = "./train_truth.csv" # 用pandas把 train_truth.csv 讀進來 train_data = pd.read_csv(data_path) # 建立Dataset class MyData(Dataset): def __init__(self, data): # 把 train_truth.csv 中的 category 行做為label self.label = data['category'] # 把 train_truth.csv 中的 filename 行獨立提取出來 self.filename = data['filename'] # 建立一個陣列用來存讀到的圖片 self.image = [] # 利用 filename 中的檔名把圖片讀進來 for item in self.filename: # read_image 是 torchvision.io 中提供的函數,可以用來讀圖片 self.image.append(read_image("./music_train/{}".format(item))[:3, :, :]) print("read {}".format(item)) # 紀錄所有訓練資料的筆數 self.n = data.shape[0] def __getitem__(self, idx): # 返回對應 idx 的圖片及 label return self.image[idx].float(), self.label[idx] def __len__(self): # 返回訓練資料的筆數 return self.n # 實例化訓練資料集(把 train_truth.csv 丟進 Dataset 用來初始化 train = MyData(train_data) ``` ### 2.如何用Sequential建立神經網路? ```python # 實例化一個 nn.Sequential,把想要的層照順序填入就好 net = nn.Sequential( # nn.Conv2d 是pytorch 的卷積層函數 nn.Conv2d(3, 1, kernel_size=10, stride=1, padding=0), # 接一層Tanh() nn.Tanh(), # nn.MaxPool2d 是 pytorch 的最大值池化層 nn.MaxPool2d(kernel_size=2, stride=2), # 第二層卷積 nn.Conv2d(1, 1, kernel_size=10, stride=1, padding=0), # 第二層Tanh() nn.Tanh(), # 第二次池化層 nn.MaxPool2d(kernel_size=2, stride=2), # 第三層卷積 nn.Conv2d(1, 1, kernel_size=10, stride=1, padding=0), # 第三層Tanh() nn.Tanh(), # 第三次池化層 nn.MaxPool2d(kernel_size=2, stride=2), # 進入全連接層前要先把前面的 output 拍平 nn.Flatten(), # 第一層全連接層 nn.Linear(2337, 1000), nn.Tanh(), # 第二層全連接層 nn.Linear(1000, 500), nn.Tanh(), # 第三層全連接層。最後的結果應有88種音階 nn.Linear(500, 88), nn.Tanh(), ) class Network(nn.Module): def __init__(self): super(Network, self).__init__() # 把上面建立好的網路拿來用 self.layer = net def forward(self, input_array): # 把資料丟進網路,回傳辨識結果 y_pred = self.layer(input_array) return y_pred ``` ### 3.其他準備 ```python # 取得訓練時間用來作為檔名 train_time_temp = time.localtime() train_time = time.strftime("%Y/%m/%d %H:%M:%S", train_time_temp) # 為畫圖做一些初始化 plt.figure(figsize=(18, 9)) lo = plt.subplot(2, 2, 3) lo.set_title("loss") lo.set_xlabel('epoch times') lo.set_ylabel('loss') # 設定訓練參數 BATCH_SIZE = 100 LEARN_RATE = 0.01 MOMENTUM = 0.9 EPOCHS = 1000 # 實例化訓練資料集(把 train_truth.csv 丟進 Dataset 用來初始化 train = MyData(train_data) # 實例化一個 DataLoader train_loader = DataLoader(dataset=train, batch_size=BATCH_SIZE, shuffle=True) # 實例化一個 神經網路模型 MyNetwork = Network() # loss_function 使用 CrossEntropyLoss loss_fn = nn.CrossEntropyLoss() # 實例化一個優化器 optimizer = torch.optim.SGD(MyNetwork.parameters(), lr=LEARN_RATE, momentum=MOMENTUM) ``` ### 4.開始訓練 ```python # 開始訓練 # 用來計數的 i i = 0 # 用來放 loss 結果的變數 loss = 0 # 紀錄過程中的 loss 的陣列 loss_list = [] # 紀錄 i 值的陣列 i_list = [] for epoch in range(EPOCHS): # 每做完一個 epoch i 就加一 i = i + 1 # 把圖片及標籤按照 BATCH_SIZE 拿出指定的數量來訓練 for images, label in train_loader: # 把圖片丟進網路,得到預測資料 t_p = MyNetwork(images) # 把預測資料和標籤丟進 loss_function loss = loss_fn(t_p, label) # 把優化器歸零 optimizer.zero_grad() # 逆傳遞運算 loss.backward() # 更新神經元的參數 optimizer.step() # 把做完一個 epoch 的 loss 紀錄下來 loss_list.append(loss.detach()) # 計錄本次 epoch 的編號,用來畫圖 i_list.append(i) # 印出本次 epoch 的編號及loss結果 print(i, loss.detach()) # 用紀錄的資料畫圖 lo.plot(i_list, loss_list, 'y') ``` ### 5.作答測試資料 使用 python 的 os 庫來取得在測試資料夾中的圖片檔名 ```python from os import listdir # 設定測試圖片所在的路徑 test_path = "./music_test" # 取得該路徑下所有的檔名 test_image_name = listdir(test_path) # 用來放預測結果的陣列 output = [] # 把檔名從陣列中一個一個拿出來 for item in test_image_name: # 用拿出來的檔名把圖片讀出來 t_p = MyNetwork(read_image("./music_test/{}".format(item))[:3, :, :].float()) # 要印出值需要先detach() a = t_p.detach() # 印出88種可能性的機率分布 print(a) # 印出88種可能性的機率中最大的那項的索引號 print(torch.argmax(a, dim=1).numpy()) # 把機率最大的索引號紀錄到陣列中 output.append(torch.argmax(a, dim=1).numpy()) ``` ### 6.結束的處理 把測試結果存成要提交的格式,並顯示loss的變化 ```python # 用來放預測結果的陣列 output = [] # 把檔名從陣列中一個一個拿出來 for item in test_image_name: # 用拿出來的檔名把圖片讀出來 t_p = MyNetwork(read_image("./music_test/{}".format(item))[:3, :, :].float()) # 要印出值需要先detach() a = t_p.detach() # 印出88種可能性的機率分布 print(a) # 印出88種可能性的機率中最大的那項的索引號 print(torch.argmax(a, dim=1).numpy()) # 把機率最大的索引號紀錄到陣列中 output.append(torch.argmax(a, dim=1).numpy()) # 產生測試資料結果的csv檔 test_output = pd.DataFrame(output, test_image_name) test_output.columns.name = 'filename' test_output.columns = ['category'] test_output.to_csv('./test/{}.csv' .format(time.strftime("%Y-%m-%d %H_%M_%S", train_time_temp)), index_label="filename" ) # 顯示loss完的變化圖 plt.show() ```