回归可以用于预测多少的问题。 比如预测房屋被售出价格,或者棒球队可能获得的胜场数,又或者患者住院的天数。
事实上,我们也对分类问题感兴趣:不是问“多少”,而是问“哪一个”:
- 某个电子邮件是否属于垃圾邮件文件夹?
- 某个用户可能注册或不注册订阅服务?
- 某个图像描绘的是驴、狗、猫、还是鸡?
- 某人接下来最有可能看哪部电影?
通常,机器学习实践者用分类这个词来描述两个有微妙差别的问题: 1. 我们只对样本的“硬性”类别感兴趣,即属于哪个类别; 2. 我们希望得到“软性”类别,即得到属于每个类别的概率。 这两者的界限往往很模糊。其中的一个原因是:即使我们只关心硬类别,我们仍然使用软类别的模型。
所以我们引入了softmax回归。
0. 准备
导包:
| import torch from IPython import display from d2l import torch as d2l
|
引入Fashion-MINIST
数据集:
1 2
| batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
|
关于d2l
包中内置的load_data_fashion_mnist
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def load_data_fashion_mnist(batch_size, resize=None): """Download the Fashion-MNIST dataset and then load it into memory.""" trans = [transforms.ToTensor()] if resize: trans.insert(0, transforms.Resize(resize)) trans = transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True) mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True) return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()), data.DataLoader(mnist_test, batch_size, shuffle=False, num_workers=get_dataloader_workers()))
|
1. 初始化模型参数
这里的每个样本都将用固定长度的向量表示。 原始数据集中的每个样本都是28×28的图像。 在本节中,我们将展平每个图像,把它们看作长度为784的向量。 我们暂时只把每个像素位置看作一个特征。
回想一下,在softmax
回归中,我们的输出与类别一样多。 因为我们的数据集有10个类别,所以网络输出维度为10。 因此,权重将构成一个784×10的矩阵, 偏置将构成一个1×10的行向量。 与线性回归一样,我们将使用正态分布初始化我们的权重W
,偏置初始化为0。
1 2 3 4 5
| num_inputs = 784 num_outputs = 10
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) b = torch.zeros(num_outputs, requires_grad=True)
|
2. 定义softmax操作
softmax表达式:
softmax(X)ij=∑kexp(Xik)exp(Xij)
实现softmax由三个步骤组成:
- 对每个项求幂(使用exp)
- 对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数
- 将每一行除以其规范化常数,确保结果的和为1
1 2 3 4
| def softmax(X): X_exp = torch.exp(X) partition = X_exp.sum(1, keepdim=True) return X_exp / partition
|
3. 定义模型
softmax回归的矢量计算表达式为:
O=XW+bY^=softmax(O)
定义softmax操作后,我们可以实现softmax回归模型。 下面的代码定义了输入如何通过网络映射到输出。 注意,将数据传递到模型之前,我们使用reshape
函数将每张原始图像展平为向量。
1 2
| def net(X): return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
|
4. 定义损失函数
我们引入交叉熵损失函数
softmax函数给出了一个向量y^, 我们可以将其视为“对给定任意输入x的每个类的条件概率”。 例如y^1=P(y= 猫 ∣x)。 假设整个数据集X,Y具有n个样本, 其中索引ii的样本由特征向量x(i)和独热标签向量y(i)组成。 我们可以将估计值与实际值进行比较:
P(Y∣X)=i=1∏nP(y(i)∣x(i))
根据最大似然估计,我们最大化P(Y∣X),相当于最小化负对数似然:
−logP(Y∣X)=i=1∑n−logP(y(i)∣x(i))=i=1∑nl(y(i),y^(i))
其中,对于任何标签y和模型预测y^,损失函数为:
l(y,y^)=−j=1∑qyjlogy^j
1 2
| def cross_entropy(y_hat, y): return - torch.log(y_hat[range(len(y_hat)), y])
|
5. 分类精度
当预测与标签分类y
一致时,即是正确的。 分类精度即正确预测数量与总预测数量之比。 虽然直接优化精度可能很困难(因为精度的计算不可导), 但精度通常是我们最关心的性能衡量标准,我们在训练分类器时几乎总会关注它。
为了计算精度,我们执行以下操作。 首先,如果y_hat
是矩阵,那么假定第二个维度存储每个类的预测分数。 我们使用argmax
获得每行中最大元素的索引来获得预测类别。 然后我们将预测类别与真实y
元素进行比较。 由于等式运算符“==
”对数据类型很敏感, 因此我们将y_hat
的数据类型转换为与y
的数据类型一致。 结果是一个包含0(错)和1(对)的张量。 最后,我们求和会得到正确预测的数量。
1 2 3 4 5 6 7 8
| def accuracy(y_hat, y): """计算预测正确的数量""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = y_hat.argmax(axis=1) cmp = y_hat.type(y.dtype) == y return float(cmp.type(y.dtype).sum())
accuracy(y_hat, y) / len(y)
|
同样,对于任意数据迭代器data_iter
可访问的数据集, 我们可以评估在任意模型net
的精度。
1 2 3 4 5 6 7 8 9
| def evaluate_accuracy(net, data_iter): """计算在指定数据集上模型的精度""" 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]
|
参考
参考教程:DIVE INTO DEEP LEARNING