Reference

前言

最近選了一堂AI課程,有一個作業是我們寫出一個Nerual Network,並且使用Titanic Dataset來訓練,並且透過增加 hidden layer 跟 neurons 的方式實現overfitting,並透過 dropout 或其他方法來消除 overfitting 的影響。

在此紀錄ㄧ下作業撰寫的過程。

環境設置與作業要求

環境設置:

  • Python 3.10.9
  • Pytorch 2.0.1

作業要求

  1. Write a custom dataset class for the titanic data (see the data folder on GitHub). Use only the features: “Pclass”, “Age”, “SibSp”, “Parch”, „Fare“, „Sex“, „Embarked“. Preprocess the features accordingly in that class (scaling, one-hot-encoding, etc) and split the data into train and validation data (80% and 20%). The constructor of that class should look like this:
    1
    2
    titanic_train = TitanicDataSet('titanic.csv', train=True)
    titanic_val = TitanicDataSet('titanic.csv', train=False)
  2. Build a neural network with one hidden layer of size 3 that predicts the survival of the passengers. Use a BCE loss (Hint: you need a sigmoid activation in the output layer). Use a data loader to train in batches of size 16 and shuffle the data.
  3. Evaluate the performance of the model on the validation data using accuracy as metric.
  4. Create the following plot that was introduced in the lecture.
  5. Increase the complexity of the network by adding more layers and neurons and see if you can overfit on the training data.
  6. Try to remove overfitting by introducing a dropout layer.

簡單來說

簡單來說,我們會從以下四個步驟中滿足上述要求:

  1. 資料前處理

    • Task 1: 建制 class 並且把 Titanic 的資料導入
    • Task 1: 只選取特定欄位作為訓練特徵
    • Task 1: 資料前處理 (scaling, one-hot-encoding, etc),把性別或是Embaded的這種object型態,非數字的欄位轉換成數字
    • Task 1: 資料切分成 train data 跟 validation data (80% and 20%)
    • Task 1: 建立一個 class 並且把資料導入
  2. 建置 Neural Network

    • Task 2: 建立一個 three layer 的 network (1 input layer + 1 hidden layer + 1 output layer)。
    • Task 2: 第一層 hidden layer 的 neurons size 為 3
    • Task 2: 使用 BCE loss 作為 loss Function
    • Task 2: 使用 sigmoid activation 作為 output layer 的 activation function
  3. 模型訓練

    • Task 3: 開始訓練模型,並且記錄每次的 accuracy
  4. 產出結果

    • Task 4: 產出結果,並且畫出圖表
  5. 製造 Overfitting

    • Task 5: 增加 hidden layer 跟 neurons 的方式實現overfitting
  6. 使用 Dropout

    • Task 6: 透過 dropout 或其他方法來消除 overfitting 的影響

Step1. 資料前處理

那我們就先來開始做資料前處理

  • Task 1: 建制 class 並且把 Titanic 的資料導入
  • Task 1: 只選取特定欄位作為訓練特徵
  • Task 1: 資料前處理 (scaling, one-hot-encoding, etc),把性別或是Embaded的這種object型態,非數字的欄位轉換成數字
  • Task 1: 資料切分成 train data 跟 validation data (80% and 20%)
  • Task 1: 建立一個 class 並且把資料導入

1.1 資料前處理

  1. 我們先匯入目前所需要的所有套件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import os
    # data process
    import numpy as np
    import pandas as pd
    from skimage import io, transform
    from torch.utils.data import Dataset, DataLoader
    from torchvision import transforms, utils

    # plot
    import matplotlib.pyplot as plt

    # neural network
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.optim as optim

    # preprocessing
    from sklearn.preprocessing import MinMaxScaler,OneHotEncoder
    from sklearn.model_selection import train_test_split

    import warnings
    warnings.filterwarnings("ignore")

    plt.ion() # interactive mode
  2. 在開始之前,我想要先把所有需要會使用的參數都放在最上面比較好更改:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # share variables
    D_in, D_out = 10, 1
    num_epochs = 250
    log_interval = 100

    # batch_size:每次訓練的資料量
    batch_size = 30

    # learning rate:因為我會建立兩種不同的 network,因此我們分別設定兩種不同的 learning rate
    learning_rate = 0.001
    multi_learning_rate = 0.001

    # hidden layers
    multi_num_layers = 6

    # hidden neurons:因為我會建立兩種不同的 network,因此我們分別設定兩種不同的 hidden neurons
    neurons = 3
    multi_neurons = 1024
  3. 接著,我們根據要求建制 class 並且把 Titanic 的資料導入,並回傳features

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    class TitanicDataset(Dataset):

    # 初始化函數,用於載入和預處理數據
    def __init__(self, root_dir, train=True, transform=None):
    # train 參數用於指示是訓練數據還是測試數據
    self.train = train
    # transform 參數用於定義一個轉換函數,如果需要對數據進行轉換的話

    # 創建 MinMaxScaler 和 OneHotEncoder 來進行數據預處理
    minmax_scaler = MinMaxScaler()
    onehot_enc = OneHotEncoder()

    # 讀取 CSV 文件中的鐵達尼號數據
    titanic = pd.read_csv(root_dir)
    # 從數據中選取特定的列
    titanic = titanic[["Pclass", "Age", "SibSp", "Parch", "Fare", "Sex", "Embarked", "Survived"]]

    # 將 "Age" 列中的缺失值用平均值填充,並刪除包含缺失值的行
    titanic["Age"] = titanic["Age"].fillna(titanic["Age"].mean())
    titanic = titanic.dropna()
    titanic = titanic.reset_index(drop=True)

    # 將數據分為類別特徵、數值特徵和標籤
    categorical_features = titanic[titanic.select_dtypes(include=['object']).columns.tolist()]
    numerical_features = titanic[titanic.select_dtypes(exclude=['object']).columns].drop('Survived', axis=1)
    label_features = titanic['Survived']

    # 對數值特徵進行歸一化(MinMax 歸一化)
    numerical_features_arr = minmax_scaler.fit_transform(numerical_features)

    # 對類別特徵進行獨熱編碼
    categorical_features_arr = onehot_enc.fit_transform(categorical_features).toarray()

    # 將歸一化的數值特徵和獨熱編碼後的類別特徵合併成一個數據集
    combined_features = pd.DataFrame(data=numerical_features_arr, columns=numerical_features.columns)
    combined_features = pd.concat([combined_features, pd.DataFrame(data=categorical_features_arr)], axis=1)
    combined_features = pd.concat([combined_features, label_features], axis=1).reset_index(drop=True)

    # 將數據集分為訓練集和測試集
    train_data, test_data = train_test_split(combined_features, test_size=0.2, random_state=42)

    # 根據訓練或測試模式選擇要使用的數據
    if train:
    self.data = train_data
    else:
    self.data = test_data

    # 返回數據集的長度
    def __len__(self):
    return len(self.data)

    # 用於訓練神經網絡的函數,返回特徵和標籤
    def __getitem__(self, idx):
    # 獲取在 self.data DataFrame 中的第 idx 行的數據
    sample = self.data.iloc[idx]
    # 將一個數據結構轉換為 PyTorch 張量 並指定這個张量的數據類型為浮點數(float)
    features = torch.FloatTensor(sample[:-1])
    label = torch.FloatTensor([sample['Survived']])
    if self.transform:
    features = self.transform(features)
    return features, label

    # 返回整個數據集的 DataFrame
    def getData(self):
    return self.data

  4. 寫好 function 後,就可以開始使用了,我們可以使用以下指令來測試一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    titanic_train = TitanicDataset('./data/titanic.csv', train=True)
    titanic_val = TitanicDataset('./data/titanic.csv', train=False)
    print('train_dataset len:', len(titanic_train))
    print('val_dataset len:', len(titanic_val))
    print('total_dataset len:', len(titanic_train) + len(titanic_val))
    # 最後會印出如下:
    '''
    train_dataset len: 711
    val_dataset len: 178
    total_dataset len: 889
    '''
  5. 可以透過以下程式,列印出以下結果:

    1
    titanic_val.getData()

Step2. 建制 Neural Network

  1. 接下來我們建制以下 Neural Network,主要做以下事情:
    • __init__: 建立一個 three layer 的 network (1 input layer + 1 hidden layer + 1 output layer)。
      • D_in: input layer 的 neurons size
      • H: hidden layer 的 neurons size
      • D_out: output layer 的 neurons size
    • forward: 進行 forward pass 的地方,主要做第一層的 linear transformation,並且使用 relu 作為 activation function,第二層的 linear transformation,並且使用 sigmoid 作為 activation function,最後回傳預測的結果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class TwoLayerNet(nn.Module):
    def __init__(self, D_in, H, D_out):
    """
    In the constructor we instantiate two nn.Linear modules and assign them as member variables.
    """
    super(TwoLayerNet, self).__init__()
    # the weight and bias of linear1 will be initialized
    # you can access them by self.linear1.weight and self.linear1.bias
    self.linear1 = nn.Linear(D_in, H) # this will create weight, bias for linear1
    self.linear2 = nn.Linear(H, D_out) # this will create weight, bias for linear2
    self.sigmoid = nn.Sigmoid() # Sigmoid activation for binary classification

    def forward(self, x):
    """
    In the forward function we accept a Tensor of input data and we must return a Tensor of output data.
    We can use Modules defined in the constructor as well as arbitrary operators on Tensors.
    """
    h_relu = F.relu(self.linear1(x))
    y_pred = self.sigmoid(self.linear2(h_relu))
    return y_pred
  2. 在訓練模型之前,我們要先把模型建立起來。下面程式碼的意思就是,我們設定 batch_size = 16,每次以 16 個單位進行一次訓練,然後把所有 889 筆資料以 16單位全部訓練完作為一次epoch,input layer 的 neurons size = 10,hidden layer 的 neurons size = 3,output layer 的 neurons size = 1,learning rate = 0.001,總共訓練 500 次。
    • 我們建立了 network 把網路神經建立起來
    • 使用 Adam 作為 optimizer,進行 Gradient Descent 的更新
    • 使用 Binary Cross-Entropy Loss 作為 loss function
    1
    2
    3
    4
    5
    6
    7
    8
    N, D_in, H, D_out = 16, 10, 3, 1
    lr = 0.001
    n_epochs = 50
    log_interval = 100 # Print the training status every log_interval epoch

    network = TwoLayerNet(D_in, H, D_out) # H=3 for one hidden layer with 3 neurons
    optimizer = optim.Adam(network.parameters(), lr)
    criterion = nn.BCELoss() # Define the loss function as Binary Cross-Entropy Loss

Step3. 模型訓練

  1. 可以先建立好所需的 list 清單來記住每次的 loss 跟 accuracy
    1
    2
    3
    4
    train_losses = [] # Save the loss value of each training loop (epoch) of the neural network model during the training process
    train_counter = [] # Save the number of images for training so far
    test_losses = [] # Save the loss value of each test loop (epoch) of the neural network model during the training process
    test_counter = [i*len(titanic_train) for i in range(n_epochs+1)] # how many data for training so far
  2. 建制 train function,主要目的是把 model train 好,使用 train dataset 來進行模型訓練。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    def train(epoch): # 目前跑了第幾個 epoch
    network.train() # 把上一步驟建立好的 network 拿進來使用
    correct = 0 # 紀錄目前正確的次數
    cur_count = 0 # 紀錄目前已經訓練了多少筆資料

    for batch_idx, (data, target) in enumerate(train_dataloader):
    optimizer.zero_grad() # 先把目前的 gradient 清空,因為每次訓練完一個 batch 就會更新一次 gradient

    # forward propagation
    output = network(data) # 把資料餵入 network 進行 forward propagation
    loss = criterion(output, target) # 計算 loss

    # Accuracy
    pred = (output >= 0.5).float() # 因為答案不是0就是1,因此我們需要設定 threshold,大於等於 0.5 就是 1,小於 0.5 就是 0
    correct += (pred == target).sum().item() # 紀錄目前正確的次數,如果與 target 一樣就 +1
    cur_count += len(data) # 紀錄目前已經訓練了多少筆資料

    # backword propagation
    loss.backward() # 計算 loss 的 gradient
    optimizer.step() # 更新 gradient


    if batch_idx % log_interval == 0: # 每 log_interval 印出一次訓練狀態
    print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}\t Accuracy: {}/{} ({:.0f}%)'.format(
    epoch,
    cur_count,
    len(train_dataloader.dataset),
    100. * cur_count / len(train_dataloader.dataset),
    loss.item(),
    correct, len(train_dataloader.dataset),
    100. * correct / len(train_dataloader.dataset))
    )
    train_losses.append(loss.item())
    train_counter.append((batch_idx*16) + ((epoch-1)*len(train_dataloader.dataset)))

    # 回傳目前的 accuracy
    return correct / len(train_dataloader.dataset)
  3. 建制 test function,主要目的是把 train 好的 model 透過 validation dataset 進行測試,看這個模型訓練在檢測未知資料時,準確率如何。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def test():
    network.eval() # 把上一步驟建立好的 network 拿進來使用,告知目前要進行 evaluation 的狀態
    test_loss = 0 # 紀錄目前的 loss
    correct = 0 # 紀錄目前正確的次數
    with torch.no_grad(): # 因為不需要計算 gradient,因此可以使用 torch.no_grad() 來加速
    for data, target in test_dataloader: # 透過 test_dataloader 來取得資料
    # forward propagation
    output = network(data) # 把資料餵入 train 好的 network 進行 forward propagation
    test_loss += criterion(output, target).item() # 計算 loss
    # Accuracy
    pred = (output >= 0.5).float() # 0.5 is the threshold
    correct += (pred == target).sum().item() # 紀錄目前正確的次數,如果與 target 一樣就 +1
    test_loss /= len(test_dataloader.dataset) # 計算平均的 loss
    test_losses.append(test_loss) # 把目前的 loss 加入 list 中
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
    test_loss,
    correct,
    len(test_dataloader.dataset),
    100. * correct / len(test_dataloader.dataset))
    )
    return correct / len(test_dataloader.dataset) # 回傳目前的 accuracy
  4. 最後我們就可以根據 epoch 數量來進行模型的訓練,並做完每次 epoch 時,就透過 test() 來檢驗一下訓練狀況。
    1
    2
    3
    4
    5
    6
    test()
    train_accuracy_list = []
    test_accuracy_list = []
    for epoch in range(1, n_epochs + 1): # 進行 n_epochs 次的訓練
    train_accuracy_list.append(train(epoch)) # 訓練完後,把目前的 accuracy 加入 list 中
    test_accuracy_list.append(test()) # 訓練完後,把目前的 accuracy 加入 list 中
  5. 出來應該會長這樣:

Step4. 產出結果

最後可以透過以下指令來產出結果:

1
2
3
4
5
6
7
import matplotlib.pyplot as plt
plt.plot(train_accuracy_list, color='blue')
plt.plot(test_accuracy_list, color='red')
# plt.ylim(0.5, 1)
plt.legend(['Train Accuracy', 'Test Accuracy', 'Mutli Train Accuracy', 'Mutli Test Accuracy'], loc='lower right')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')

Step5. 製造 Overfitting

製造 Overfitting 主要可以有幾種方式:

  1. epoch 調大,就會有一點 overfitting 的現象
  2. 或是把 hidden layer 數量提高或是把 neurons size 調大,也會有一點 overfitting 的現象

因為題目要求把 hidden layer 數量提高獲釋把 neurons size 調大,因此我們就來試試看吧!
最簡單的方式就是把,hidden layer調高一點,neurons size 調大一點,並且 epoch 調大一點,就可以看到 overfitting 的現象了。

  1. 建立一個 MultiLayerNet,並且把 hidden layer 跟 neurons size 調高一點
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class MultiLayerNet(nn.Module):
    def __init__(self, D_in, H, D_out, num_layers):
    super(MultiLayerNet, self).__init__()
    neurons = 128
    self.input = nn.Linear(D_in, H)
    self.linear1 = nn.Linear(H, 128)
    self.linear2 = nn.Linear(128, 64)
    self.linear3 = nn.Linear(64, 32)
    self.linear4 = nn.Linear(32, 16)
    self.output = nn.Linear(16, D_out)
    self.sigmoid = nn.Sigmoid() # Sigmoid activation for binary classification


    def forward(self, x):
    y_relu = F.relu(self.input(x))
    y_relu = F.relu(self.linear1(y_relu))
    y_relu = F.relu(self.linear2(y_relu))
    y_relu = F.relu(self.linear3(y_relu))
    y_relu = F.relu(self.linear4(y_relu))
    y_pred = self.sigmoid(self.output(y_relu))
    return y_pred

踩雷筆記:如果你純粹增加 layer 並不會有太多學習的效果,你會總是看到一水平的條線…,然後準確率就沒再上升了!後來同學發現,neurons要從多慢慢遞減,才會有學習的效果,因此我們可以把 neurons 設定成 128, 64, 32, 16!
「 同學說:這就像沙漏一樣」,這樣會慢慢一步步的過濾掉不重要的資訊,最後留下重要的資訊!」

  1. 針對 multi network 建立新的 multi_train() 跟 test_multi() function

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    def train_multi(epoch):
    multi_network.train() # 把上一步驟建立好的 network 拿進來使用
    correct = 0
    cur_count = 0

    for batch_idx, (data, target) in enumerate(train_dataloader):
    multi_optimizer.zero_grad()

    # forward propagation
    output = multi_network(data) # 你會發現這裡使用 multi_network 來進行 forward propagation
    loss = multi_criterion(output, target) # 你會發現這裡使用 multi_criterion 來計算 loss

    # Accuracy
    pred = (output >= 0.5).float() # survival_rate is the threshold
    correct += (pred == target).sum().item()
    cur_count += len(data)

    # backword propagation
    loss.backward()
    multi_optimizer.step()


    if batch_idx % log_interval == 0:
    print('Muti Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}\t Accuracy: {}/{} ({:.0f}%)'.format(
    epoch,
    cur_count,
    len(train_dataloader.dataset),
    100. * cur_count / len(train_dataloader.dataset),
    loss.item(),
    correct, len(train_dataloader.dataset),
    100. * correct / len(train_dataloader.dataset))
    )
    train_losses.append(loss.item())
    train_counter.append((batch_idx*16) + ((epoch-1)*len(train_dataloader.dataset)))

    return correct / len(train_dataloader.dataset)

    def test_multi():
    multi_network.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
    for data, target in test_dataloader:
    # forward propagation
    output = multi_network(data)
    test_loss += multi_criterion(output, target).item()
    # Accuracy
    pred = (output >= 0.5).float() # 0.5 is the threshold
    correct += (pred == target).sum().item()
    test_loss /= len(test_dataloader.dataset)
    test_losses.append(test_loss)
    print('\nMulti Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
    test_loss,
    correct,
    len(test_dataloader.dataset),
    100. * correct / len(test_dataloader.dataset))
    )
    return correct / len(test_dataloader.dataset)
  2. 重新 train model

    1
    2
    3
    4
    5
    6
    7
    8
    test_multi()

    multi_train_accuracy_list = []
    multi_test_accuracy_list = []

    for epoch in range(1, n_epochs + 1):
    multi_train_accuracy_list.append(train_multi(epoch))
    multi_test_accuracy_list.append(test_multi())
  3. 重新畫圖: 你可以嘗試把 epoch 條到 500 次,就會看到 overfitting 的現象了!

    1
    2
    3
    4
    5
    6
    7
    import matplotlib.pyplot as plt
    plt.plot(multi_train_accuracy_list, color='orange')
    plt.plot(multi_test_accuracy_list, color='green')
    plt.ylim(0.5, 0.9)
    plt.legend(['Train Accuracy', 'Test Accuracy', 'Mutli Train Accuracy', 'Mutli Test Accuracy'], loc='upper right')
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')

進階版

如果希望可以更加動態的調整neurons跟hidden layer的數量,可以使用以下方式:

  • neurons: 一開始設定的 neurons 數量,如果設定 1024,就會從 1024 開始遞減至 16,每次遞減就除以 2,直到 neurons 數量小於 16 為止
  • num_layers: hidden layer 數量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
neurons = 1024 
num_layers = 5
class MultiLayerNet(nn.Module):
def __init__(self, D_in, D_out, neurons, num_layers):
super(MultiLayerNet, self).__init__()
neurons = neurons
self.input = nn.Linear(D_in, neurons)
self.linears = nn.ModuleList() # 需要注意的是,如果要用 for 回圈建立多個 layer,就必須使用 nn.ModuleList() 來建立
for i in range(num_layers):
self.linears.append(nn.Linear(neurons, max(neurons // 2, 16)))
neurons = max(neurons // 2, 16)
self.output = nn.Linear(neurons, D_out)
self.sigmoid = nn.Sigmoid() # Sigmoid activation for binary classification

def forward(self, x):
y = F.relu(self.input(x))
for layer in self.linears:
y = F.relu(layer(y))
y_pred = self.sigmoid(self.output(y))
return y_pred

Step6. 使用 Dropout

這邊我們可以使用 Dropout 來避免 overfitting 的現象,主要是在 forward propagation 的時候,隨機把一些 neurons 給關掉,這樣就可以避免 overfitting 的現象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch.nn.functional as F

class MultiLayerNet(nn.Module):
def __init__(self, D_in, H, D_out, num_layers, dropout_prob):
super(MultiLayerNet, self).__init__()
self.input = nn.Linear(D_in, H)
self.linear1 = nn.Linear(H, 128)
self.linear2 = nn.Linear(128, 64)
self.linear3 = nn.Linear(64, 32)
self.linear4 = nn.Linear(32, 16)
self.output = nn.Linear(16, D_out)
self.sigmoid = nn.Sigmoid()
self.dropout = nn.Dropout(p=dropout_prob) # 添加 Dropout 層

def forward(self, x):
y_relu = F.relu(self.input(x))
y_relu = F.relu(self.linear1(y_relu))
y_relu = F.relu(self.linear2(y_relu))
y_relu = F.relu(self.linear3(y_relu))
y_relu = F.relu(self.linear4(y_relu))
y_relu = self.dropout(y_relu) # 在需要的位置應用 Dropout
y_pred = self.sigmoid(self.output(y_relu))
return y_pred

這時候你會發現,overfitting 的現象就沒那麼嚴重了!以下是 epoch 數量設定為 200 時的結果。

Without Dropout

With Dropout

進階版

進階版的差異就是,dropout layer 的數量跟 hidden layer 的數量是一樣的,並且 dropout layer 的數量是隨著 hidden layer 的數量遞減的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MultiLayerNet(nn.Module):
def __init__(self, D_in, D_out, neurons, num_layers, dropout_prob=0.8):
super(MultiLayerNet, self).__init__()
neurons = neurons
self.input = nn.Linear(D_in, neurons)
self.linears = nn.ModuleList()
self.dropouts = nn.ModuleList() # ======> dropout layer
for i in range(num_layers):
self.linears.append(nn.Linear(neurons, max(neurons // 2, 16)))
self.dropouts.append(nn.Dropout(p=dropout_prob)) # ======> dropout layer
neurons = max(neurons // 2, 16)
self.output = nn.Linear(neurons, D_out) # output layer
self.sigmoid = nn.Sigmoid() # Sigmoid activation for binary classification

def forward(self, x):
y = F.relu(self.input(x))
for layer, dropout in zip(self.linears, self.dropouts):
y = F.relu(layer(y))
y = dropout(y) # ======> dropout layer
y_pred = self.sigmoid(self.output(y))
return y_pred