PyTorch教程-5.2. 多层感知器的实现

多层感知器 (mlp) 的实现并不比简单的线性模型复杂多少。关键的概念差异是我们现在连接多个层。
import torchfrom torch import nnfrom d2l import torch as d2l
from mxnet import np, npxfrom mxnet.gluon import nnfrom d2l import mxnet as d2lnpx.set_np()
import jaxfrom flax import linen as nnfrom jax import numpy as jnpfrom d2l import jax as d2l
no gpu/tpu found, falling back to cpu. (set tf_cpp_min_log_level=0 and rerun for more info.)
import tensorflow as tffrom 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`__ 自动将类属性注册为要跟踪的参数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 `__ 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 `__ 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): a = torch.zeros_like(x) return torch.max(x, a)
def relu(x): return np.maximum(x, 0)
def relu(x): return jnp.maximum(x, 0)
def relu(x): return tf.math.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
@d2l.add_to_class(mlpscratch)def forward(self, x): x = x.reshape((-1, self.num_inputs)) h = relu(np.dot(x, self.w1) + self.b1) return np.dot(h, self.w2) + self.b2
@d2l.add_to_class(mlpscratch)def forward(self, x): x = x.reshape((-1, self.num_inputs)) h = relu(jnp.matmul(x, self.w1) + self.b1) return jnp.matmul(h, self.w2) + self.b2
@d2l.add_to_class(mlpscratch)def forward(self, x): x = tf.reshape(x, (-1, self.num_inputs)) h = relu(tf.matmul(x, self.w1) + self.b1) return tf.matmul(h, self.w2) + self.b2  
5.2.1.3. 训练
幸运的是,mlp 的训练循环与 softmax 回归完全相同。我们定义模型、数据、训练器,最后fit在模型和数据上调用方法。
model = mlpscratch(num_inputs=784, num_outputs=10, num_hiddens=256, lr=0.1)data = d2l.fashionmnist(batch_size=256)trainer = d2l.trainer(max_epochs=10)trainer.fit(model, data)
model = mlpscratch(num_inputs=784, num_outputs=10, num_hiddens=256, lr=0.1)data = d2l.fashionmnist(batch_size=256)trainer = d2l.trainer(max_epochs=10)trainer.fit(model, data)
model = mlpscratch(num_inputs=784, num_outputs=10, num_hiddens=256, lr=0.1)data = d2l.fashionmnist(batch_size=256)trainer = d2l.trainer(max_epochs=10)trainer.fit(model, data)
model = mlpscratch(num_inputs=784, num_outputs=10, num_hiddens=256, lr=0.1)data = d2l.fashionmnist(batch_size=256)trainer = d2l.trainer(max_epochs=10)trainer.fit(model, data)
5.2.2. 简洁的实现
正如您所料,通过依赖高级 api,我们可以更简洁地实现 mlp。
5.2.2.1. 模型
与我们对 softmax 回归实现的简洁实现(第 4.5 节)相比,唯一的区别是我们添加了两个完全连接的层,而我们之前只添加了 一个。第一个是隐藏层,第二个是输出层。
class mlp(d2l.classifier): def __init__(self, num_outputs, num_hiddens, lr): super().__init__() self.save_hyperparameters() self.net = nn.sequential(nn.flatten(), nn.lazylinear(num_hiddens), nn.relu(), nn.lazylinear(num_outputs))
class mlp(d2l.classifier): def __init__(self, num_outputs, num_hiddens, lr): super().__init__() self.save_hyperparameters() self.net = nn.sequential() self.net.add(nn.dense(num_hiddens, activation='relu'), nn.dense(num_outputs)) self.net.initialize()
class mlp(d2l.classifier): num_outputs: int num_hiddens: int lr: float @nn.compact def __call__(self, x): x = x.reshape((x.shape[0], -1)) # flatten x = nn.dense(self.num_hiddens)(x) x = nn.relu(x) x = nn.dense(self.num_outputs)(x) return x
class mlp(d2l.classifier): def __init__(self, num_outputs, num_hiddens, lr): super().__init__() self.save_hyperparameters() self.net = tf.keras.models.sequential([ tf.keras.layers.flatten(), tf.keras.layers.dense(num_hiddens, activation='relu'), tf.keras.layers.dense(num_outputs)])  
以前,我们forward为模型定义了使用模型参数转换输入的方法。这些操作本质上是一个管道:您获取一个输入并应用一个转换(例如,矩阵与权重相乘,然后是偏差加法),然后重复使用当前转换的输出作为下一个转换的输入。但是,您可能已经注意到 forward这里没有定义任何方法。实际上,从类(第 3.2.2 节mlp)继承 方法以简单地调用 (是输入),现在定义为通过类进行的一系列转换。该类抽象了前向过程,使我们能够专注于转换。我们将进一步讨论如何forwardmoduleself.net(x)xsequentialsequentialsequential类在第 6.1.2 节中起作用 。
5.2.2.2. 训练
训练循环与我们实现 softmax 回归时完全相同。这种模块化使我们能够将有关模型架构的问题与正交考虑分开。
model = mlp(num_outputs=10, num_hiddens=256, lr=0.1)trainer.fit(model, data)
model = mlp(num_outputs=10, num_hiddens=256, lr=0.1)trainer.fit(model, data)
model = mlp(num_outputs=10, num_hiddens=256, lr=0.1)trainer.fit(model, data)
model = mlp(num_outputs=10, num_hiddens=256, lr=0.1)trainer.fit(model, data)
5.2.3. 概括
现在我们在设计深度网络方面有了更多的实践,从单层到多层深度网络的步骤不再构成如此重大的挑战。特别是,我们可以重用训练算法和数据加载器。但请注意,从头开始实施 mlp 仍然很麻烦:命名和跟踪模型参数使得扩展模型变得困难。例如,假设想要在第 42 层和第 43 层之间插入另一层。这可能是第 42b 层,除非我们愿意执行顺序重命名。此外,如果我们从头开始实施网络,框架就很难执行有意义的性能优化。
尽管如此,您现在已经达到了 1980 年代后期的最先进水平,当时完全连接的深度网络是神经网络建模的首选方法。我们的下一个概念性步骤将是考虑图像。在我们这样做之前,我们需要回顾一些关于如何有效计算模型的统计基础知识和细节。
5.2.4. 练习
更改隐藏单元的数量num_hiddens并绘制其数量如何影响模型的准确性。这个超参数的最佳值是多少?
尝试添加隐藏层以查看它如何影响结果。
为什么用单个神经元插入隐藏层是个坏主意?会出什么问题?
改变学习率如何改变你的结果?在所有其他参数固定的情况下,哪个学习率能给你最好的结果?这与纪元数有何关系?
让我们联合优化所有超参数,即学习率、时期数、隐藏层数和每层隐藏单元数。
通过对所有这些进行优化可以获得的最佳结果是什么?
为什么处理多个超参数更具挑战性?
描述联合优化多个参数的有效策略。
比较框架的速度和从头开始实施一个具有挑战性的问题。它如何随着网络的复杂性而变化?
测量对齐良好和未对齐矩阵的张量矩阵乘法的速度。例如,测试维度为 1024、1025、1026、1028 和 1032 的矩阵。
这在 gpu 和 cpu 之间有何变化?
确定 cpu 和 gpu 的内存总线宽度。
尝试不同的激活函数。哪一个效果最好?
网络的权重初始化之间是否存在差异?有关系吗?

EZ Form半刚性电缆MIL-DTL-17 QPL有什么特别之处
基于RFID技术防碰撞算法有效解决低标签密度时空传率高的问题
雷军为何对红米Note3偏爱之甚
网络互助平台“水滴互助”完成近5亿元B轮融资,由腾讯领投
百度地图宣布城市车道级导航取得里程碑突破
PyTorch教程-5.2. 多层感知器的实现
2013年下届慕尼黑电子展即将上演
CAXA三点角度标注的详细介绍
小米手机的无线充电刷新了新记录?
北醒车规级激光雷达AD2首次亮相HICOOL峰会|市领导莅临展位听取联合创始人汇报
3D NAND闪存技术成熟:产量增长高于需求,降价是必然趋势
数字经济行业盛会,引领高科技创新发展!
数字电网的关键技术架构解析
提高性能和可靠性:陶瓷印刷电路板的研究
为什么说区块链对食品和饮料包装的未来非常重要
艾默生推出面向油气行业用于陆上钻探作业的节能照明解决方案
移位寄存器串入并出与并入串出
PCIe 4.0固态硬盘适合你么?aigo国民好物P7000抢先体验
人脸识别技术应用基础广泛 市场前景十分光明
CCD相机的高速数据采集系统设计