多层感知器 (MLP) 的实现并不比简单的线性模型复杂多少。关键的概念差异是我们现在连接多个层。
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
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
函数。
由于我们忽略了空间结构,我们将reshape
每个二维图像转换为长度为 的平面向量num_inputs
。最后,我们只用几行代码就实现了我们的模型。由于我们使用框架内置的 autograd,这就是它所需要的全部。
5.2.1.3. 训练
幸运的是,MLP 的训练循环与 softmax 回归完全相同。我们定义模型、数据、训练器,最后fit
在模型和数据上调用方法。