<h1>
1. 實驗步驟與觀察
</h1>
<h2>
code explain
</h2>
首先題目給的資料有兩份,分別是 train.csv 和 test.csv,目標是的到一個model,藉由輸入x1, x2預測最終的y值。
由於input的維度是1,所以主要使用nn.Linear()來架構model,其次因為因為目標是預測y值,所以model的output只有一個值。
我選擇使用pytorch來完成作業,由於之前就已安裝過,不再多做贅述。
<h3>
import
</h3>
```python
import os
from pathlib import Path
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset
from torch.utils.data import DataLoader, random_split
from tqdm.notebook import tqdm as tqdm
from sklearn.preprocessing import StandardScaler
from pathlib import Path
from datetime import datetime
from matplotlib import pyplot as plt
from sklearn.model_selection import KFold
```
<h3>
setting device to cuda if available
</h3>
```python
if torch.cuda.is_available():
device = torch.device('cuda')
torch.backends.cudnn.benchmark = True
else:
device = torch.device('cpu')
print(device)
```
<h3>
setting dir
</h3>
```python
data_dir = str(Path(fr"./data/train.csv"))
testdata_dir = str(Path(fr"./data/test.csv"))
save_dir = str(Path(fr"./runs/exp"))
```
<h3>
setting hyperparameter
</h3>
```python
# number of subprocesses to use for data loading
num_workers = 0
# how many samples per batch to load
batch_size = 32
epoch_num = 500
dtype = torch.float32
# Learning Rate
LR = 1e-3
EPS = 1e-7
```
<h3>
define CustomDataset class
</h3>
資料處理部分,首先要將csv檔讀進來,並轉成pytorch框架能接受的datatype,這裡選擇numpy,雖然目前沒有加入transform的打算,但還是有準備,這裡設計getitem時會直接將型別轉為tensor,至於dtype則是作為一個參數。
```python
class CustomDataset(Dataset):
def __init__(self, data_dir, dtype=torch.float, transform=None):
data = pd.read_csv(data_dir)
self.inputs = data[['x1', 'x2']].to_numpy()
self.outputs = data['y'].to_numpy()
self.dtype = dtype
self.transform = transform
def __len__(self):
return len(self.inputs)
def __getitem__(self, idx):
input_data = torch.tensor(self.inputs[idx], dtype=dtype)
output_data = torch.tensor(self.outputs[idx], dtype=dtype)
if self.transform:
input_data = self.transform(input_data)
return input_data, output_data
```
<h3>
Data Preprocessing
</h3>
1. 首先利用上一個 block 定義的 class 來讀取資料,建立 dataset 實例
2. 計算 dataset 大小,並切成 train set 跟 validation set
3. 藉由DataLoader class 將兩份資料依照定義的 batch size 做成 dataloader 實例,其中我有將 train set 打亂
```python
dataset = CustomDataset(data_dir)
train_size = int(0.8 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, valid_dataset = random_split(dataset, [train_size, val_size])
dataloaders = {x: DataLoader(y, batch_size=batch_size, shuffle=z)
for x, y, z in zip(['train', 'valid'], [train_dataset, valid_dataset], [True, False])}
```
<h3>
define model structure
</h3>
activation function 使用現代model常用的 ReLU,並且加上Batch Normalization,希望以此來減緩梯度消失的問題。
```python
class DeepModelWithBN(nn.Module):
def __init__(
self,
layer_sizes):
super().__init__()
self.layers = nn.ModuleList()
for i in range(len(layer_sizes) - 1):
layer = nn.Linear(layer_sizes[i], layer_sizes[i + 1]).to(dtype)
self.layers.append(layer)
if i != len(layer_sizes) - 2:
bn = nn.BatchNorm1d(layer_sizes[i + 1]).to(dtype)
self.layers.append(bn)
self.activation = nn.ReLU()
def forward(self, x):
for i, layer in enumerate(self.layers[:-1]):
x = layer(x)
if isinstance(layer, nn.Linear):
x = self.activation(x)
x = self.layers[-1](x)
return x
```
<h3>
define model, optimizer, and criterion
</h3>
定義完 model 架構後,簡單的依照2的指數,創建了一個model的實例。此時選用 adam 作為 optimizer,提高 model 訓練時參數更新時的穩定性,收斂的速度也較快。criterion 則是依照題目定義的分數檢測方式,期望能最小化 MSE Loss。
```python
model_name = 'DeepModelWithBN'
model_args = [[2, 100, 1]]
model = DeepModelWithBN(*model_args)
optimizer = torch.optim.Adam(model.parameters(), lr=LR, eps=EPS)
criterion = nn.MSELoss()
```
<h3>
training
</h3>
為了避免在訓練期間因為其他因素造成沒有存到檔的情形,會在每一個 epoch 時儲存,另外還會儲存目前最佳的 model。
會依照 epoch 數量是否為零判斷是否要沿用目前的儲存路徑,或是避免覆蓋上一個 model 的結果。
```python
def train(ckpt,
save_dir,
model,
dataloaders,
optimizer,
num_epochs=300,
dtype=torch.float32,
device='cpu'):
save_dir = Path(save_dir)
save_dir = increment_path(
Path(save_dir), exist_ok=(False if ckpt['epoch'] == 0 else True), mkdir=True)
model = model.to(dtype=dtype).to(device)
# initial
model.load_state_dict(ckpt['model_state_dict'])
optimizer.load_state_dict(ckpt['optimizer_state_dict'])
criterion = ckpt['criterion']
for epoch in range(1, num_epochs + 1):
# keep track of training and validation loss
train_loss = 0.0
valid_loss = 0.0
print(f"running epoch: {ckpt['epoch'] + 1}")
# Training loop
model.train()
for inputs, outputs in dataloaders['train']:
optimizer.zero_grad()
inputs = inputs.to(dtype).to(device)
outputs = outputs.to(dtype).to(device)
predictions = model(inputs)
loss = criterion(predictions.squeeze(1), outputs)
loss.backward()
optimizer.step()
# update training loss
train_loss += loss.item() * inputs.size(0)
# Validation loop
model.eval()
with torch.no_grad():
for inputs, outputs in dataloaders['valid']:
inputs = inputs.to(dtype).to(device)
outputs = outputs.to(dtype).to(device)
predictions = model(inputs)
loss = criterion(predictions.squeeze(1), outputs)
valid_loss += loss.item() * inputs.size(0)
train_loss = train_loss / len(dataloaders['train'].dataset)
valid_loss = valid_loss / len(dataloaders['valid'].dataset)
ckpt['history']['train_loss'].append(train_loss)
ckpt['history']['valid_loss'].append(valid_loss)
print(
f'Train loss -> {train_loss:.6f} \
Validation loss -> {valid_loss:.6f}')
# update and save check point information
ckpt['epoch'] += 1
ckpt['model_state_dict'] = model.state_dict()
ckpt['optimizer_state_dict'] = optimizer.state_dict()
ckpt['date'] = datetime.now().isoformat()
# save model if validation loss has decreased
if valid_loss <= ckpt['history']['valid_loss_min']:
print(
f"Validation loss decreased ({ckpt['history']['valid_loss_min']:.6f} \
--> {valid_loss:.6f}). Saving model ...")
ckpt['history']['valid_loss_min'] = valid_loss
torch.save(ckpt, str(Path(save_dir) / Path('valid_best.pth')))
torch.save(ckpt, str(Path(save_dir) / Path('last.pth')))
return ckpt
```
<h3>
初始化 ckpt
</h3>
```python
ckpt = {
'epoch': 0,
'model_name': model_name,
'model_args': model_args,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'criterion': criterion,
'history': {
'train_loss': [],
'valid_loss': [],
'valid_loss_min': np.Inf,
'best_acc': 0.0,
},
'date': datetime.now().isoformat(),
}
```
<h3>
預測 test data 的 y
</h3>
```python
model.to(device)
model = model.to(dtype=dtype)
data = pd.read_csv(testdata_dir)
x = data[['x1', 'x2']].to_numpy()
x = torch.tensor(x, dtype=dtype)
model.eval()
with torch.no_grad():
y = model(x.to(device))
y = y.squeeze().cpu().numpy()
ids = np.arange(1, 2001)
df = pd.DataFrame({'id': ids, 'y': y})
df.to_csv('output_last.csv', index=False)
```
<h3>
可視化結果
</h3>
利用matplotlib 來繪圖,為了更好的觀察 model 是否過擬合,以及 loss 收斂的數值,為了可以看得更清楚,這裡直接將 y 座標限制在 0.1 內。
```python
# loss
plt.plot(ckpt['history']['train_loss'],label='train')
plt.plot(ckpt['history']['valid_loss'],label='valid')
plt.legend()
plt.ylim(0,0.1)
plt.title('loss')
plt.xlabel('epoch')
plt.ylabel('loss')
```
為了更了解資料的特性,我將訓練集以matplotlib畫成3d可視圖。經過觀察,我將資料想成一個三維空間中的曲面。我將 x1, x2 想成三維空間的 x, z,y 則對應空間中的 y。
```python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpl_toolkits.mplot3d import Axes3D
data = pd.read_csv(data_dir)
# 創建一個3D圖形
fig = plt.figure()
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 繪製散點圖
ax.scatter(data['x1'].to_numpy(),
data['x2'].to_numpy(),
data['y'].to_numpy(),
c=data['y'].to_numpy(),
cmap='viridis',
marker='o')
# 設置標籤
ax.set_xlabel('X1')
ax.set_ylabel('X2')
ax.set_zlabel('Y')
# 顯示圖形
plt.show()
```

為了可以用另一種角度來查看 model 是否過擬合,完不完美,我利用 numpy 在 x1, x2 為 -1 ~ 1 的範圍生成10000個資料點,並丟入 model 預測。
```python
import numpy as np
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpl_toolkits.mplot3d import Axes3D
# 設定網格點的數量
grid_size = 100
# 使用linspace在-1和1之間創建均勻間隔的值
x_values = np.linspace(-1, 1, grid_size)
y_values = np.linspace(-1, 1, grid_size)
# 使用meshgrid創建網格座標
x_grid, y_grid = np.meshgrid(x_values, y_values)
# 將網格座標陣列整形為2D點陣列
x = np.column_stack((x_grid.flatten(), y_grid.flatten()))
x_t = torch.tensor(x, dtype=dtype)
model = model.to(device)
model.eval()
with torch.no_grad():
y = model(x_t.to(device))
y = y.squeeze().cpu().numpy()
data = pd.DataFrame({'x1': x[:, 0], 'x2': x[:, 1], 'y': y})
# 創建一個3D圖形
fig = plt.figure()
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 繪製散點圖
ax.scatter(data['x1'].to_numpy(), data['x2'].to_numpy(), data['y'].to_numpy(), c=data['y'].to_numpy(), cmap='viridis', marker='o')
# 設置標籤
ax.set_xlabel('X1')
ax.set_ylabel('X2')
ax.set_zlabel('Y')
# 顯示圖形
plt.show()
```


<h3>
Bug
</h3>
在實驗前期,我有發現 loss 的結果有些問題,後來發現兩個 bug 。
第一個是 model 的 output shape 為 (B, 1) 而 output shape 則是 (B, ),一個為二維向量,一個是一維向量,雖然還是可以算出loss值,但結果有些不同。
第二個是在計算loss的總和,並印出來時,將 len(dataloader.dataset) 寫成了 len(dataloader) 一個是回傳有多少個batch,一個是回傳有多少筆資料。
```python
k = iter(dataloaders['train'])
x, y = next(k)
predictions = model(x)
print(predictions.shape)
print(y.shape)
print(y.reshape(batch_size, 1).shape)
print(predictions.squeeze(1).shape)
print(criterion(predictions.squeeze(1), y))
print(criterion(predictions, y))
print(criterion(predictions, y.reshape(batch_size, 1)))
```
執行結果
torch.Size([64, 1])
torch.Size([64])
torch.Size([64, 1])
torch.Size([64])
tensor(1.2346, dtype=torch.float64, grad_fn=<MseLossBackward0>)
tensor(1.2805, dtype=torch.float64, grad_fn=<MseLossBackward0>)
tensor(1.2346, dtype=torch.float64, grad_fn=<MseLossBackward0>)
<h2>
Exp 1
</h2>
到此處成功 train 了幾個 model,可以看出 valid loss 的數值較train震盪的更嚴重。我在想是否有可能是batch過小,model 參數更新時受極端值影響較大。為了避免過度跳動,我嘗試加大batch size,我比較32, 64, 128, 256,發現當 batch size 較大時,跳動的問題有得到緩解,而且訓練時間也有效的減短了,但缺點是 loss 有點降不下去。
| **32** | **64** |
| -------- | -------- |
|  |  |
| **128** | **256** |
|  |  |
<h2>
Exp 2
</h2>
進一步對資料可視化後,發現雜訊不少,所以嘗試加深加廣網路。深度增加後,loss 值下降了不少,而且 model 的收斂更快了,loss 跳動的問題也更不明顯了。此時也發現不管再怎麼加深、加寬網路,loss 值大致上只能降至 0.025,此時我也開始將結果繳交至 kaggle 上。
| **model_args = [[2, 100, 100, 1]]** | **model_args = [[2, 100, 100, 100, 100, 1]]** |
| -------- | -------- |
|  |  |
| **model_args = [[2, 64, 128, 128, 64, 1]]** | **model_args = [[2, 64, 128, 256, 256, 128, 64, 1]]** |
|  |  |
<h2>
Exp 3
</h2>
繳交後發現結果不是很理想,再自己的資料集上,原本可以降到0.03以下的,上傳後全部都是高於0.05。再此問題中,我做的嘗試有:
1. 換一個dataloader,我怕某次資料集分割剛好訓練效果不好
2. 嘗試將 activation fuction 改為 LeakyReLU、ELU、tanh
3. 使用不同的 optimizer 像是 SGD、Adadelta、AdaGrad、RMSprop、AdamW
經過多次嘗試的結果,只有使用tanh比較成功,雖然從訓練的loss變化來看,沒什麼變化,但上傳後的結果竟然比較好,這時我的最佳結果來到0.045。其他的更動都沒有明顯差異,有些 optimizer 反而會使 model 更難收斂,所以後面主要還是使用 Adam。
<h2>
Exp 4
</h2>
簡單觀察不同的 result 後,我發現每個 model 就算最終 loss 值不相上下,其中 y 值都沒有甚麼規律,所以我想到將多個 model 的 result 取平均,是否會更好。
結果是有些資料有變好,但有些資料可能會變更差,後來就放棄直接從不同的 output 找規律了,且就算有進步,也是少個 0.001。
```python
files_list = ["runs/exp61/output_best.csv",
"runs/exp60/output_best.csv",
]
# 初始化一個空的DataFrame用於存儲結果
merged_data = pd.DataFrame()
for index, file in enumerate(files_list):
# 讀取CSV檔案
data = pd.read_csv(file)
# 使用檔案名稱或索引作為新列名
new_column_name = f'y_{index}'
# 將y值重命名為新列名
data = data.rename(columns={'y': new_column_name})
# 合併檔案
if merged_data.empty:
merged_data = data
else:
merged_data = merged_data.merge(data, on='id', how='outer')
# 計算y值的平均
merged_data['y'] = merged_data.iloc[:, 1:].mean(axis=1)
# 將結果輸出到CSV檔案
merged_data.to_csv('merged_data.csv', index=False)
```
<h2>
Exp 5
</h2>
後面決定改 model 架構,當時想到的改法有幾個:
1. 是否加入 BatchNorm1d,我想說為了防止梯度消失,所以還是有留。
2. 使用不同的 weight initial 方式。
3. 加入 Dropout,但是我又看了一下我們 model 的收斂,我認為這只會讓 model 的能力下降而以,所以沒採用。
4. activation function,我只知道 tanh 效果會好一些,但我堆這些函數是否適合用在此處感到疑惑,所以我決定將一個layer的 output 套用不同的 activation function後再concate 在一起。
5. 為了能進一步的加大加深我的 model,我想使用類似ResNet、DenseNet的技術,在不同層之間加入 shortcut,我最後決定將網路設計的像 DenseNet 一樣。
_Block 會將 input 先送進一個全連階層,其 output 經過 batchnorm 之後複製 N 份進入 N 個 activation function 中,最後將這些不同的 features 再 concate 在一起。

DeepMultiAFwithBNmodel 則是以 _Block 為基本模塊,將每一層 layer 的 input 跟 output concate 在一起,就會達到如容下面圖示的效果。

```python
class _Block(nn.Module):
def __init__(
self,
num_input_features,
num_output_features,
activation_list,
drop_rate: float
) -> None:
super().__init__()
self.add_module("linear", nn.Linear(num_input_features, num_output_features))
self.add_module("norm", nn.BatchNorm1d(num_output_features))
self.activation_list = activation_list
self.drop_rate = float(drop_rate)
def forward(self, input):
if isinstance(input, torch.Tensor):
prev_features = [input]
else:
prev_features = input
x = torch.cat(prev_features, 1)
x = self.linear(x)
x = self.norm(x)
features = []
for acivate in self.activation_list:
features.append(acivate(x))
return torch.cat(features, 1)
class DeepMultiAFwithBNmodel(nn.Module):
def __init__(
self,
num_init_features: int,
num_classes,
block_config,
activation_list,
drop_rate: float = 0
):
super().__init__()
self.features = nn.ModuleDict()
num_features = num_init_features
for i, num_layers in enumerate(block_config):
block = _Block(num_features, num_layers, activation_list, drop_rate)
self.features.add_module(f"block{i}", block)
num_features = num_features + num_layers * len(activation_list)
self.classifier = nn.Linear(num_features, num_classes)
# 参数初始化
for m in self.modules():
if isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight) # 使用Xavier初始化
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.BatchNorm1d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def forward(self, init_features):
features = [init_features]
for name, layer in self.features.items():
new_features = layer(features)
features.append(new_features)
x = torch.cat(features, 1)
x = self.classifier(x)
return x
```
經過簡單的嘗試幾個參數,train 出來的結果如下,收斂的速度稍微變慢,且 loss 的浮動有點大,有點不穩定,但最終的收斂數字約莫是0.22,有進步。
參數部分可以發現上面那個的浮動較大,下面的較小,我實驗得到的結果是,activation function 不怎麼影響結果,而是神經網路太寬時就會浮動特別大。
model_args = [2, 1, [16, 64, 128, 512, 1024, 512, 128, 64, 16], [nn.Tanh(), nn.ReLU(), nn.LeakyReLU()]]

model_args = [2, 1, [32, 32, 64, 64, 64, 128, 64, 64, 64, 32, 32], [nn.Tanh(), nn.ReLU(), nn.ELU()]]

<h2>
Exp 6
</h2>
我嘗試著將 LR 調整為 1e-4 ,但結果是 model 收斂太慢,所以我嘗試加入 lr scheduler,這裡測試兩種方式:
1. StepLR,定義是每過step_size個epoch之後,LR = LR * LR_GAMMA。
2. ReduceLROnPlateau,不是依照epoch num,而是看 loss 有沒有持續往下降,或是 acc 有沒有往上升,會有一個 patience 值,如果連續 patience 次還是沒有打破目前最好的,就會更新一次,還可以設定 cooldown 值。
```python
LR_STEP = 100
LR_GAMMA = 0.5
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=LR_STEP, gamma=LR_GAMMA
lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer,
mode='min',
factor=0.5,
patience=30,
verbose=True,
threshold=0.0001,
threshold_mode='rel',
cooldown=50,
min_lr=0,
eps=EPS)
```
經過幾次測試後,效果都不錯,但是 ReduceLROnPlateau 對參數較不敏感,而 StepLR 要針對不同的 model 參數給出 LR_STEP 和 LR_GAMMA,所以後面選用前者。
上傳到 kaggle 後,最好結果為 0.035。


<h2>
Exp 7
</h2>
後來我有問其他同學討論了下結果,他們只有單純使用 Linear,然後深度超過十層,所以我又寫了一個 model。
```python
class DeepModel(nn.Module):
def __init__(
self,
num_init_features: int,
num_classes,
block_config,
# activation_list,
drop_rate: float = 0
):
super().__init__()
self.features = nn.Sequential()
num_features = num_init_features
for i, num_layers in enumerate(block_config):
layer = nn.Linear(num_features, num_layers)
self.features.add_module(f"linear{i}", layer)
if i % 2:
self.features.add_module(f"ELU{i}", nn.ELU())
else:
self.features.add_module(f"Tanh{i}", nn.Tanh())
num_features = num_layers
self.classifier = nn.Linear(num_features, num_classes)
for m in self.modules():
if isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight)
nn.init.constant_(m.bias, 0)
def forward(self, x):
features = self.features(x)
out = self.classifier(features)
return out
```
經過幾次測試,調整後,大概可以收斂至 train loss 跟 valid loss 可以收斂至 0.022,有些甚至能收斂至0.02,上傳後結果都小於0.03。
model_args = [2, 1, [8, 16, 32, 64, 128, 64, 32, 16, 8]]

model_args = [2, 1, [8, 16, 16, 32, 32, 64, 64, 128, 128, 64, 64, 32, 32, 16, 16, 8]]

<h2>
Exp 8
</h2>
目前為止都是只有使用到部分資料,有一部分作為 valid,我認為trainset 更大,可能會有更好的效果,但是此時我們會無法評估是否過擬和,雖然經過簡單的測試可以發現運行1000個epoch也不會使 model 過擬和,但這裡還是使用 K-Fold 來觀察,經由觀察來決定執行的 epoch 數。
經過多次嘗試,最後達到 0.01804 的成績。
```python
def plot_k_res(list):
# loss
for i in list:
plt.plot(i['history']['train_loss'],label='train')
plt.plot(i['history']['valid_loss'],label='valid')
plt.legend()
plt.ylim(0,0.1)
plt.title('loss')
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
```
```python
k_folds = 5
kfold = KFold(n_splits=k_folds, shuffle=True)
train_losses_k = []
valid_losses_k = []
ckpt_list = []
for fold, (train_indices, val_indices) in enumerate(kfold.split(dataset)):
print(f"Fold {fold + 1}")
train_subset = torch.utils.data.Subset(dataset, train_indices)
val_subset = torch.utils.data.Subset(dataset, val_indices)
dataloaders = {x: DataLoader(y, batch_size=batch_size, shuffle=z)
for x, y, z in zip(['train', 'valid'], [train_subset, val_subset], [True, False])}
model_name = 'DeepModel'
model_args = [2, 1, [8, 16, 32, 64, 128, 64, 32, 16, 8]]
model = DeepModel(*model_args)
optimizer = torch.optim.Adam(model.parameters(), lr=LR, eps=EPS)
lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer,
mode='min',
factor=0.5,
patience=30,
verbose=True,
threshold=0.0001,
threshold_mode='rel',
cooldown=50,
min_lr=0,
eps=EPS)
criterion = nn.MSELoss()
ckpt = {
'epoch': 0,
'model_name': model_name,
'model_args': model_args,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'lr_scheduler_state_dict': lr_scheduler.state_dict(),
'criterion': criterion,
'history': {
'train_loss': [],
'valid_loss': [],
'valid_loss_min': np.Inf,
'train_loss_min': np.Inf,
'best_acc': 0.0,
},
'readme': "",
'date': datetime.now().isoformat(),
}
ckpt = train(ckpt, save_dir, model, dataloaders, optimizer, lr_scheduler=lr_scheduler, num_epochs=500, device=device)
train_losses_k.append(ckpt['history']['train_loss_min'])
valid_losses_k.append(ckpt['history']['valid_loss_min'])
ckpt_list.append(ckpt)
print(f"Average Train Loss: {np.mean(train_losses_k):.4f}, Average Val Loss: {np.mean(valid_losses_k):.4f}")
plot_k_res(ckpt_list)
```
<h1>
2. Discussions on the results
</h1>
大致上可以分為三種 model 架構,DeepModel、DeepModelWithBN、DeepMultiAFwithBNmodel。
1. DeepModel:
最晚嘗試的一個,我完全沒有料到 model 這麼單純竟然結果是最好的。內部只有Linear layer 跟 activation function,這裡設計使用 RLU、tanh 兩種穿插使用,最後得到最好的結果,至少我的實驗結果是如此,而且訓練十收斂的 loss 也跟 test result 的成績差不多。
2. DeepModelWithBN:
第一個嘗試的,相對第一個加了 batchnorm,只有用一種 activation function,雖然在 train set、 valid set 中的收斂效果不錯,但不知為何test的result上船成績不理想,差異很大。
3. DeepMultiAFwithBNmodel:
我自己大改的 model 架構,嘗試將 CNN model DenseNet 中的部分技術在全連階層中重現,整體效果不錯,但不是最好的。
經過比較,activation function 使用tanh的效果不錯。
然後 model 參數上,使用十層以上,但寬度最寬只有 128 的會比寬度到 1024 的效果更好。
最後是將多個 model 的結果去取平均,由於是預測,所以我想多個model 之間可能一個預測太高,一個預測太低,所以多個去平均會有較好的結果,且概念向是將多個分離的model output,在接一層全連階層,整體來說是可行的,但是只能說效果不是很顯著,其進步量也有限,且很吃運氣。
<h1>
3. Conclution
</h1>
之前大部分都是去網路上下載別人的 model、別人的資料處理 ,這次所有的東西都是自己打的,所以對一些細節有更深的理解。再來是 K-Fold,之前有聽說過,但藉由這次作業才理解到底在幹麼,並實作出來。最後是操參數的調整,以前我都直接用 Adam 作為 optimizer,趁這次機會多學了好幾個 optimizer 了解其差異、優缺點,還有以前想說 Adam 本身好像就會去調整LR了,那使用 lr scheduler 還有用嗎?由於兩者調整的理論不同,所以 lr scheduler 還是有用。在 activation function 方面,也多認識了好幾個,我之前都無腦用ReLU。