[深度學習]多層感知機(MLP)

nannandbk發表於2024-04-13

多層感知機(MLP)

1. 單層感知機

1.1 感知機

image

image

線性迴歸輸出的是一個實數,感知機輸出的是一個離散的類。

image

1.2 訓練感知機

①如果分類正確的話y<w,x>為正數,負號後變為一個正數,和\(0\)\(max\)之後得\(0\),則梯度不進行更新

②如果分類錯了,y<w,x>為負數,image
的判斷條件成立,就進行梯度更新。

image

圖示:

image

image

image

image

1.3 收斂半徑

image

1.4 XOR問題

image

1.5 總結

  • 感知機是一個二分類模型,是最早的AI模型之一
  • 它的求解方法等價於使用批次大小為1的梯度下降
  • 它不能擬合XOR函式,導致的第一次AI寒冬

2.多層感知機

2.1 學習XOR函式

我們發現單層感知機不能擬合XOR函式,那麼多層行不行呢?

image

image

2.2 什麼是多層感知機

多層感知機(MLP,Multilayer Perceptron)也叫人工神經網路(ANN,Artificial Neural Network),除了輸入輸出層,它中間可以有多個隱層,最簡單的MLP只含一個隱層,即三層的結構,如下圖:

image

隱藏層的大小是超引數

  • 輸入$x \in \mathbb{R}^n $

  • 隱藏層\(W_1\in \mathbb{R}^{m\times n}\),\(b_1\in\mathbb{R}^m\)

  • 輸出層\(w_2\in\mathbb{R}^m,b_2 \in\mathbb{R}\)

    \(h = \sigma(W_1x + b_1)\)

    \(o = w_2^{T}h + b_2\)

\(\sigma\)是按元素的啟用函式

為什麼需要非線性啟用函式呢?

①不使用啟用函式,每一層輸出都是上層輸入的線性函式,無論神經網路有多少層,輸出都是輸入的線性組合。

②使用啟用函式,能夠給神經元引入非線性因素,使得神經網路可以任意逼近任何非線性函式,這樣神經網路就可以利用到更多的非線性模型中。

image

2.3 常見啟用函式

啟用函式需要具備以下幾點性質:

  1. 連續並可導(允許少數點上不可導)的非線性函式。可導的啟用函式可以直接利用數值最佳化的方法來學習網路引數。
  2. 啟用函式及其導函式要儘可能的簡單,有利於提高網路計算效率。
  3. 啟用函式的導函式的值域要在一個合適的區間內,不能太大也不能太小,否則會影響訓練的效率和穩定性。

Sigmoid(Logistic) 函式

with autograd.record():
    y = x.sigmoid()
xyplot(x, y, 'sigmoid')

image

依據鏈式法則,sigmoid函式的導數為

\[sigmoid'(x) = sigmoid(x)(1-sigmoid(x)) \]

下面繪製了sigmoid函式的導數。當輸入為0時,sigmoid函式的導數達到最大值0.25;當輸入越偏離0時,sigmoid函式的導數越接近0。

y.backward()
xyplot(x, x.grad, 'grad of sigmoid')

image

Tanh 函式

with autograd.record():
    y = x.tanh()
xyplot(x, y, 'tanh')

image

依據鏈式法則,tanh函式的導數為:

\[tanh'(x) = 1 - tanh^2(x) \]

下面繪製了tanh函式的導數。當輸入為0時,tanh函式的導數達到最大值1;當輸入越偏離0時,tanh函式的導數越接近0。

y.backward()
xyplot(x, x.grad, 'grad of tanh')

image

ReLU

x = nd.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record():
    y = x.relu()
xyplot(x, y, 'relu')

image

顯然,當輸入為負數時,ReLU函式的導數為0;當輸入為正數時,ReLU函式的導數為1。儘管輸入為0時ReLU函式不可導,但是我們可以取此處的導數為0。下面繪製ReLU函式的導數。

y.backward()
xyplot(x, x.grad, 'grad of relu')

image

2.4 多分類問題

image

image

多隱藏層

image

2.5 總結

  • 多層感知機使用隱藏層和啟用函式來得到非線性模型
  • 常用啟用函式是Sigmoid,Tanh,ReLU
  • 使用Softmax來處理多分類問題
  • 超引數為隱藏層數和各個隱藏層大小

3.多層感知機實現程式碼

自定義實現

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# 實現一個具有單隱藏層的多層感知機,它包含256個隱藏單元
num_inputs, num_outputs, num_hiddens = 784, 10, 256 # 輸入、輸出是資料決定的,256是調參自己決定的
W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True))
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True))
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1,b1,W2,b2]

# 實現 ReLu 啟用函式
def relu(X):
    a = torch.zeros_like(X) # 資料型別、形狀都一樣,但是值全為 0
    return torch.max(X,a)

# 實現模型a
def net(X):
    #print("X.shape:",X.shape)
    X = X.reshape((-1, num_inputs)) # -1為自適應的批次大小
    #print("X.shape:",X.shape)
    H = relu(X @ W1 + b1)
    #print("H.shape:",H.shape)
    #print("W2.shape:",W2.shape)
    return (H @ W2 + b2)


# 損失
loss = nn.CrossEntropyLoss() # 交叉熵損失

# 多層感知機的訓練過程與softmax迴歸的訓練過程完全一樣
num_epochs ,lr = 30, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)

image

注意,這裡我們把上節\(Softmax\)的程式碼加入到\(d2l\)裡面啦。這樣就可以直接呼叫啦。

具體方法:

pip show d2l

然後我們在相應的路徑下面找到d2l的資料夾,開啟後找到torch檔案。開啟後在最下面新增相應的函式定義即可。

train_epoch_ch3函式:

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """訓練模型一個迭代週期(定義見第3章)"""
    # 將模型設定為訓練模式
    if isinstance(net, torch.nn.Module): # isinstance()用來判斷一個物件是否是一個已知的型別
        net.train()
    # 訓練損失總和、訓練準確度總和、樣本數
    metric = Accumulator(3)
    for X, y in train_iter:
        # 計算梯度並更新引數
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch內建的最佳化器和損失函式
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定製的最佳化器和損失函式
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回訓練損失和訓練精度
    return metric[0] / metric[2], metric[1] / metric[2]

evaluate_accuracy函式:

def evaluate_accuracy(net, data_iter):  #@save
    """計算在指定資料集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 將模型設定為評估模式
    metric = Accumulator(2)  # 正確預測數、預測總數
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

train_ch3函式:

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    #該訓練函式將會執行多個迭代週期(由num_epochs指定)。 在每個迭代週期結束時,利用test_iter訪問到的測試資料集對模型進行評估。 
    #我們將利用Animator類來視覺化訓練進度。
    """訓練模型(定義見第3章)"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

框架實現

import torch
from torch import nn
from d2l import torch as d2l

# 隱藏層包含256個隱藏單元,並使用了ReLU啟用函式
net = nn.Sequential(nn.Flatten(),nn.Linear(784,256),nn.ReLU(),nn.Linear(256,10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight,std=0,)
        
net.apply(init_weights)

# 訓練過程
batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss()
trainer = torch.optim.SGD(net.parameters(), lr=lr)

train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image

參考:https://github.com/d2l-ai/d2l-zh-pytorch-slides

相關文章