# 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是對的選擇。