---
# System prepended metadata

title: 410921216賴柏諺_深度學習作業三

---

# 410921216賴柏諺_深度學習作業三 
## 零. 程式碼:
:::spoiler 程式碼
```python
import torch # PyTorch
import torch.nn as nn # 神經網路模組
import torch.optim as optim # 優化器
from torch.utils.data import DataLoader # 資料加載器
import pandas as pd # Pandas
from sklearn.model_selection import train_test_split # 拆分資料集
from tqdm.auto import tqdm #進度條
# 檢查GPU是否可用
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 加載訓練資料
train_input_data = pd.read_csv('./train_in.csv')
train_output_data = pd.read_csv('./train_out.csv')
# 拆分輸入和輸出
features = train_input_data.iloc[:, 1:]
labels = train_output_data['Label']
# 拆分訓練集和驗證集
train_input, val_input, train_output, val_output = train_test_split(features, labels, test_size=0.15, random_state=66)
# 將輸入資料轉換為Tensor
features_train = torch.tensor(train_input.values, dtype=torch.float32)
labels_train = torch.tensor(train_output.values, dtype=torch.long)
features_test = torch.tensor(val_input.values, dtype=torch.float32)
labels_test = torch.tensor(val_output.values, dtype=torch.long)
# 轉換為8*2的Tensor
features_train = features_train.view(-1, 8, 2)
features_test = features_test.view(-1, 8, 2)
# 處理訓練集和驗證集加載器
train_data = torch.utils.data.TensorDataset(features_train, labels_train) 
test_data = torch.utils.data.TensorDataset(features_test, labels_test)
train_loader = DataLoader(train_data, batch_size=32, shuffle=True)
test_loader = DataLoader(test_data, batch_size=32, shuffle=False)
# 定義RNN模型
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, num_classes):
        super().__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, num_classes)
    def forward(self, x):
        out, _ = self.lstm(x, None)
        out = self.fc(out[:, -1, :])
        return out
# 定義模型參數和優化器
input_size = 2
hidden_size = 128
num_layers = 2
output_size = 10
learning_rate = 0.001
model = RNN(input_size, hidden_size, num_layers, output_size).to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate) # 使用Adam優化器
criterion = nn.CrossEntropyLoss() # 使用交叉熵損失函數
# 訓練模型
num_epochs = 10
for epoch in tqdm(range(num_epochs)):
    total_loss = 0.0
    model.train() # 設置為訓練模式
    for features, labels in train_loader:
        # 將資料移至GPU
        features = features.to(device)
        labels = labels.to(device)
        optimizer.zero_grad() # 梯度歸零
        output = model(features) # 輸出
        loss = criterion(output, labels) # 計算loss
        loss.backward() # 反向傳播
        optimizer.step() # 更新參數
        total_loss += loss.item() # 累加loss
    print(f'Epoch {epoch+1}/{num_epochs}, Loss: {total_loss/len(train_loader)}') # 輸出loss 
    # 驗證模型
    model.eval() # 設置為評估模式
    with torch.no_grad():
        correct = 0
        total = 0
        for features, labels in test_loader:
            features = features.to(device)
            labels = labels.to(device)
            output = model(features)
            _, predicted_labels = torch.max(output, 1)
            total += labels.size(0)
            correct += (predicted_labels == labels).sum().item()
        print(f'Accuracy: {correct/total:.4f}')
# 載入test_in資料
test_input_data = pd.read_csv('./test_in.csv')
features = test_input_data.iloc[:, 1:]
features = torch.tensor(features.values, dtype=torch.float32)
features = features.view(-1, 8, 2)
features = features.to(device)
# 預測並保存結果
model.eval() # 設置為評估模式
test_output = model(features)
_, predicted_labels = torch.max(test_output, 1)
test_output_data = pd.DataFrame({'Serial No.': test_input_data['Serial No.'], 'Label': predicted_labels.cpu().numpy()})
test_output_data.to_csv('test_out.csv', index=False)

```
:::
## 一. 遇到的問題:
訓練集準確率異常

## 二. 怎麼解決問題:
一開始再切的時候想法是訓練完全部的訓練集，再用測試集做測試，但不知道為什麼每次測出來都是100%，所以就將訓練與測試合併，在同個EPOCH裡做兩件事(當然有記得切換訓練/評估模式)，這樣有讓準確度出現正常的數值，不再每次都100%

## 三. 模型設計思路(個人創意):
1. 模型架構為RNN層接全連接層
2. 這次的RNN模型我做了3種嘗試，依序分別是RNN, GRU, LSTM三種(程式碼附的是LSTM)。
    一開始用RNN，模型參數如下:
    ```
    input_size = 2  
    hidden_size = 128  
    output_size = 10  
    learning_rate = 0.001
    ```
    隱藏層1層，跑出來結果95%。為了提升準確度，使用gru，用同樣的參數但結果測得78%。
    最後選擇用LSTM，參照[Pytorch LSTM](https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html)，並而外多一層隱藏層，其他參數維持，
    ```
    num_layers = 2
    ```
    跑出來的結果還不錯(98%)。
3. 損失函數的部分是使用交叉熵損失函數，用來處理分類的問題
4. 訓練步數大概1步就可以讓測試集的準確率達到95%，大概5步就會在97~99%浮動，最後定在訓練10步
## 四. 學到了什麼:
有了前兩次作業的經驗，這次作業在數據加載的方面處理起來較為得心應手，主要的重點是這次的RNN模型設計。
上課聽老師講解，看到講義一堆模型架構圖看起來很複雜，但程式實作起來比之前的作業都還短(單就模型的部分)。
在決定要使用哪一種函式庫(RNN, GRU, LSTM)時，先看到GRU也是三個單字就用它了，不知道是我的使用上有錯誤還是參數設的不夠好，    kaggle測只有0.78，於是最後使用LSTM，查了一下資料，得知LSTM是為了解決RNN梯度消失的問題所設計的"長短期記憶"模型，更得知在2009年，有人用LSTM構建的人工神經網路模型贏得ICDAR手寫辨識比賽冠軍，看來最後選用LSTM是對的選擇。