您可能已经注意到,在回归的情况下,从头开始的实现和使用框架功能的简洁实现非常相似。分类也是如此。由于本书中的许多模型都处理分类,因此值得添加专门支持此设置的功能。本节为分类模型提供了一个基类,以简化以后的代码。
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
4.3.1. 类Classifier
_
我们在下面定义Classifier
类。在中,validation_step
我们报告了验证批次的损失值和分类准确度。我们为每个批次绘制一个更新num_val_batches
。这有利于在整个验证数据上生成平均损失和准确性。如果最后一批包含的示例较少,则这些平均数并不完全正确,但我们忽略了这一微小差异以保持代码简单。
We define the Classifier
class below. In the validation_step
we report both the loss value and the classification accuracy on a validation batch. We draw an update for every num_val_batches
batches. This has the benefit of generating the averaged loss and accuracy on the whole validation data. These average numbers are not exactly correct if the last batch contains fewer examples, but we ignore this minor difference to keep the code simple.
We define the Classifier
class below. In the validation_step
we report both the loss value and the classification accuracy on a validation batch. We draw an update for every num_val_batches
batches. This has the benefit of generating the averaged loss and accuracy on the whole validation data. These average numbers are not exactly correct if the last batch contains fewer examples, but we ignore this minor difference to keep the code simple.
We also redefine the training_step
method for JAX since all models that will subclass Classifier
later will have a loss that returns auxiliary data. This auxiliary data can be used for models with batch normalization (to be explained in Section 8.5), while in all other cases we will make the loss also return a placeholder (empty dictionary) to represent the auxiliary data.
class Classifier(d2l.Module): #@save
"""The base class of classification models."""
def training_step(self, params, batch, state):
# Here value is a tuple since models with BatchNorm layers require
# the loss to return auxiliary data
value, grads = jax.value_and_grad(
self.loss, has_aux=True)(params, batch[:-1], batch[-1], state)
l, _ = value
self.plot("loss", l, train=True)
return value, grads
def validation_step(self, params, batch, state):
# Discard the second returned value. It is used for training models
# with BatchNorm layers since loss also returns auxiliary data
l, _ = self.loss(params, batch[:-1], batch[-1], state)
self.plot('loss', l, train=False)
self.plot('acc', self.accuracy(params, batch[:-1], batch[-1], state),
train=False)
We define the Classifier
class below. In the validation_step
we report both the loss value and the classification accuracy on a validation batch. We draw an update for every num_val_batches
batches. This has the benefit of generating the averaged loss and accuracy on the whole validation data. These average numbers are not exactly correct if the last batch contains fewer examples, but we ignore this minor difference to keep the code simple.
默认情况下,我们使用随机梯度下降优化器,在小批量上运行,就像我们在线性回归的上下文中所做的那样。
4.3.2. 准确性
给定预测概率分布y_hat
,每当我们必须输出硬预测时,我们通常会选择预测概率最高的类别。事实上,许多应用程序需要我们做出选择。例如,Gmail 必须将电子邮件分类为“主要”、“社交”、“更新”、“论坛”或“垃圾邮件”。它可能会在内部估计概率,但最终它必须在类别中选择一个。
当预测与标签 class 一致时y
,它们是正确的。分类准确度是所有正确预测的分数。尽管直接优化精度可能很困难(不可微分),但它通常是我们最关心的性能指标。它通常是基准测试中的相关数量。因此,我们几乎总是在训练分类器时报告它。
准确度计算如下。首先,如果y_hat
是一个矩阵,我们假设第二个维度存储每个类别的预测分数。我们使用argmax
每行中最大条目的索引来获取预测类。然后我们将预测的类别与真实的元素进行比较y
。由于相等运算符==
对数据类型敏感,因此我们转换 的y_hat
数据类型以匹配 的数据类型y
。结果是一个包含条目 0(假)和 1(真)的张量。求和得出正确预测的数量。
@d2l.add_to_class(Classifier) #@save
def accuracy(self, Y_hat, Y, averaged=True):
"""Compute the number of correct predictions."""
Y_hat = Y_hat.reshape((-1, Y_hat.shape[-1]))
preds = Y_hat.argmax(axis=1).type(Y.dtype)
compare = (preds == Y.reshape(-1)).type(torch.float32)
return compare.mean() if averaged else compare
@d2l.add_to_class(Classifier) #@save
def accuracy(self, Y_hat, Y, averaged=True):
"""Compute the number of correct predictions."""
Y_hat = Y_hat.reshape((-1, Y_hat.shape[-1]))
preds = Y_hat.argmax(axis=1).astype(Y.dtype)
compare = (preds == Y.reshape(-1)).astype(np.float32)
return compare.mean() if averaged else compare
@d2l.add_to_class(d2l.Module) #@save
def get_scratch_params(self):
params = []
for attr in dir(self):
a = getattr(self, attr)
if isinstance(a, np.ndarray):
params.append(a)
if isinstance(a, d2l.Module):
params.extend(a.get_scratch_params())
return params
@d2l.add_to_class(d2l.Module) #@save
def parameters(self):
params = self.collect_params()
return params if isinstance(params, gluon.parameter.ParameterDict) and