---
# System prepended metadata

title: 深度學習基石與實務 作業二

---

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





