# <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()
```