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