多層感知機(MLP)
1. 單層感知機
1.1 感知機
線性迴歸輸出的是一個實數,感知機輸出的是一個離散的類。
1.2 訓練感知機
①如果分類正確的話y<w,x>為正數,負號後變為一個正數,和\(0\)取\(max\)之後得\(0\),則梯度不進行更新
②如果分類錯了,y<w,x>為負數,
的判斷條件成立,就進行梯度更新。
圖示:
1.3 收斂半徑
1.4 XOR問題
1.5 總結
- 感知機是一個二分類模型,是最早的AI模型之一
- 它的求解方法等價於使用批次大小為1的梯度下降
- 它不能擬合XOR函式,導致的第一次AI寒冬
2.多層感知機
2.1 學習XOR函式
我們發現單層感知機不能擬合XOR函式,那麼多層行不行呢?
2.2 什麼是多層感知機
多層感知機(MLP,Multilayer Perceptron)也叫人工神經網路(ANN,Artificial Neural Network),除了輸入輸出層,它中間可以有多個隱層,最簡單的MLP只含一個隱層,即三層的結構,如下圖:
隱藏層的大小是超引數
-
輸入$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\)是按元素的啟用函式
為什麼需要非線性啟用函式呢?
①不使用啟用函式,每一層輸出都是上層輸入的線性函式,無論神經網路有多少層,輸出都是輸入的線性組合。
②使用啟用函式,能夠給神經元引入非線性因素,使得神經網路可以任意逼近任何非線性函式,這樣神經網路就可以利用到更多的非線性模型中。
2.3 常見啟用函式
啟用函式需要具備以下幾點性質:
- 連續並可導(允許少數點上不可導)的非線性函式。可導的啟用函式可以直接利用數值最佳化的方法來學習網路引數。
- 啟用函式及其導函式要儘可能的簡單,有利於提高網路計算效率。
- 啟用函式的導函式的值域要在一個合適的區間內,不能太大也不能太小,否則會影響訓練的效率和穩定性。
Sigmoid(Logistic) 函式
with autograd.record():
y = x.sigmoid()
xyplot(x, y, 'sigmoid')
依據鏈式法則,sigmoid函式的導數為
下面繪製了sigmoid函式的導數。當輸入為0時,sigmoid函式的導數達到最大值0.25;當輸入越偏離0時,sigmoid函式的導數越接近0。
y.backward()
xyplot(x, x.grad, 'grad of sigmoid')
Tanh 函式
with autograd.record():
y = x.tanh()
xyplot(x, y, 'tanh')
依據鏈式法則,tanh函式的導數為:
下面繪製了tanh函式的導數。當輸入為0時,tanh函式的導數達到最大值1;當輸入越偏離0時,tanh函式的導數越接近0。
y.backward()
xyplot(x, x.grad, 'grad of tanh')
ReLU
x = nd.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record():
y = x.relu()
xyplot(x, y, 'relu')
顯然,當輸入為負數時,ReLU函式的導數為0;當輸入為正數時,ReLU函式的導數為1。儘管輸入為0時ReLU函式不可導,但是我們可以取此處的導數為0。下面繪製ReLU函式的導數。
y.backward()
xyplot(x, x.grad, 'grad of relu')
2.4 多分類問題
多隱藏層
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)
注意,這裡我們把上節\(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)
參考:https://github.com/d2l-ai/d2l-zh-pytorch-slides