多層感知器 (MLP) 的實現并不比簡單的線性模型復雜多少。關鍵的概念差異是我們現在連接多個層。
import tensorflow as tf
from d2l import tensorflow as d2l
5.2.1. 從零開始實施
讓我們從頭開始實現這樣一個網絡。
5.2.1.1. 初始化模型參數
回想一下,Fashion-MNIST 包含 10 個類,并且每個圖像由一個28×28=784灰度像素值網格。和以前一樣,我們暫時忽略像素之間的空間結構,因此我們可以將其視為具有 784 個輸入特征和 10 個類別的分類數據集。首先,我們將實現一個具有一個隱藏層和 256 個隱藏單元的 MLP。層數和寬度都是可調的(它們被認為是超參數)。通常,我們選擇層寬度可以被 2 的較大次冪整除。由于內存在硬件中分配和尋址的方式,這在計算上是高效的。
同樣,我們將用幾個張量表示我們的參數。請注意, 對于每一層,我們必須跟蹤一個權重矩陣和一個偏置向量。與往常一樣,我們為這些參數的損失梯度分配內存。
在下面的代碼中,我們使用 `nn.Parameter
< https://pytorch.org/docs/stable/generated/torch.nn.parameter.Parameter.html >`__ 自動將類屬性注冊為要跟蹤的參數autograd
(第 2.5 節) .
class MLPScratch(d2l.Classifier):
def __init__(self, num_inputs, num_outputs, num_hiddens, lr, sigma=0.01):
super().__init__()
self.save_hyperparameters()
self.W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens) * sigma)
self.b1 = nn.Parameter(torch.zeros(num_hiddens))
self.W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs) * sigma)
self.b2 = nn.Parameter(torch.zeros(num_outputs))
In the code below, we first define and initialize the parameters and then enable gradient tracking.
class MLPScratch(d2l.Classifier):
def __init__(self, num_inputs, num_outputs, num_hiddens, lr, sigma=0.01):
super().__init__()
self.save_hyperparameters()
self.W1 = np.random.randn(num_inputs, num_hiddens) * sigma
self.b1 = np.zeros(num_hiddens)
self.W2 = np.random.randn(num_hiddens, num_outputs) * sigma
self.b2 = np.zeros(num_outputs)
for param in self.get_scratch_params():
param.attach_grad()
In the code below we use `flax.linen.Module.param
<https://flax.readthedocs.io/en/latest/api_reference/flax.linen.html#flax.linen.Module.param>`__ to define the model parameter.
class MLPScratch(d2l.Classifier):
num_inputs: int
num_outputs: int
num_hiddens: int
lr: float
sigma: float = 0.01
def setup(self):
self.W1 = self.param('W1', nn.initializers.normal(self.sigma),
(self.num_inputs, self.num_hiddens))
self.b1 = self.param('b1', nn.initializers.zeros, self.num_hiddens)
self.W2 = self.param('W2', nn.initializers.normal(self.sigma),
(self.num_hiddens, self.num_outputs))
self.b2 = self.param('b2', nn.initializers.zeros, self.num_outputs)
In the code below we use `tf.Variable
<https://www.tensorflow.org/api_docs/python/tf/Variable>`__ to define the model parameter.
class MLPScratch(d2l.Classifier):
def __init__(self, num_inputs, num_outputs, num_hiddens, lr, sigma=0.01):
super().__init__()
self.save_hyperparameters()
self.W1 = tf.Variable(
tf.random.normal((num_inputs, num_hiddens)) * sigma)
self.b1 = tf.Variable(tf.zeros(num_hiddens))
self.W2 = tf.Variable(
tf.random.normal((num_hiddens, num_outputs)) * sigma)
self.b2 = tf.Variable(tf.zeros(num_outputs))
5.2.1.2. 模型
為了確保我們知道一切是如何工作的,我們將自己實現 ReLU 激活,而不是直接調用內置relu
函數。
def relu(X):
return np.maximum(X, 0)
由于我們忽略了空間結構,我們將reshape
每個二維圖像轉換為長度為 的平面向量num_inputs
。最后,我們只用幾行代碼就實現了我們的模型。由于我們使用框架內置的 autograd,這就是它所需要的全部。
@d2l.add_to_class(MLPScratch)
def forward(self, X):
X = X.reshape((-1, self.num_inputs))
H = relu(torch.matmul(X, self.W1) + self.b1)
return torch.matmul(H, self.W2) + self.b2
5.2.1.3. 訓練
幸運的是,MLP 的訓練循環與 softmax 回歸完全相同。我們定義模型、數據、訓練器,最后fit
在模型和數據上調用方法。
評論
查看更多