微商网站模板,近一周的热点新闻,医疗网站建设公司哪家好,wordpress付费查看插件在前面#xff0c;我们介绍了线性回归模型的原理及实现。线性回归适合于预测连续值#xff0c;而对于分类问题的离散值则束手无策。因此引出了本文所要介绍的softmax回归模型#xff0c;该模型是针对多分类问题所提出的。下面我们将从softmax回归模型的原理开始介绍#xf…在前面我们介绍了线性回归模型的原理及实现。线性回归适合于预测连续值而对于分类问题的离散值则束手无策。因此引出了本文所要介绍的softmax回归模型该模型是针对多分类问题所提出的。下面我们将从softmax回归模型的原理开始介绍最后我们同样会基于PyTorch来实现基本的softmax模型。
1、分类问题
假设现在我们需要对图像进行分类每次输入的数据是一个2x2的灰度图像。如果用一个标量来表示每个像素值则每个图像可以对应x1,x2,x3,x4x_1,x_2,x_3,x_4x1,x2,x3,x4四个特征因此可以用一个特征向量(x1,x2,x3,x4)(x_1,x_2,x_3,x_4)(x1,x2,x3,x4)来表示图像。
另外假设每个图像属于类别“猫”“鸡”和“狗”其中一个那么我们可以使用独热编码one-hot encoding来表示分类数据。例如标签yyy是一个三维向量其中[1,0,0]对应“猫”类别、[0,1,0]对应“鸡”类别、[0,0,1]对应“狗”类别
y∈{(1,0,0),(0,1,0),(0,0,1)}.(1)y\in{(1,0,0),(0,1,0),(0,0,1)}.\tag{1}y∈{(1,0,0),(0,1,0),(0,0,1)}.(1)
2、模型网络架构
在分类问题中我们需要估计一张图像对于所有类别的条件概率每一个类别对应则一个输出则该模型是一个具有n个输入和m个输出的回归模型n是图像的特征向量长度m是类别数量。
在上面的例子中我们有4个输入特征和3个可能的输出类别因此我们需要12个标量来表示权重w3个标量来表示偏置b计算每个类别未规范化的条件概率
o1x1w11x2w12x3w13x4w14b1o2x1w21x2w22x3w23x4w24b2o3x1w31x2w32x3w33x4w34b3.(2)o_1x_1w_{11}x_2w_{12}x_3w_{13}x_4w_{14}b_1 \ o_2x_1w_{21}x_2w_{22}x_3w_{23}x_4w_{24}b_2 \ o_3x_1w_{31}x_2w_{32}x_3w_{33}x_4w_{34}b_3 .\tag{2}o1x1w11x2w12x3w13x4w14b1o2x1w21x2w22x3w23x4w24b2o3x1w31x2w32x3w33x4w34b3.(2)
2中的o1,o2,o3o_1,o_2,o_3o1,o2,o3就是图像对于所有类别的条件概率只不过此时还没有对概率进行规范化还不能符合我们的要求所有类别的条件概率之和为1。
我们用矩阵形式来表示 x,W,b,ox,W,b,ox,W,b,o
x[x1x2x3x4] W[w11w12w13w14w21w22w23w24w31w32w33w34] b[b1b2b3] o[o1o2o3].(3)x\begin{bmatrix} x_1 x_2 x_3 x_4 \end{bmatrix}\\ \W\begin{bmatrix} w_{11} w_{12} w_{13} w_{14}\ w_{21} w_{22} w_{23} w_{24}\ w_{31} w_{32} w_{33} w_{34}\ \end{bmatrix} ,,,,,,b\begin{bmatrix} b_1 b_2 b_3 \end{bmatrix}\\ \o\begin{bmatrix} o_1 o_2 o_3 \end{bmatrix} .\tag{3}x[x1x2x3x4] W⎣⎡w11w21w31w12w22w32w13w23w33w14w24w34⎦⎤b[b1b2b3] o[o1o2o3].(3)
则2可以表示为
oxWTb.(4)oxW^Tb.\tag{4}oxWTb.(4)
我们还可以用神经网络图图1来表示softmax回归模型。与线性回归一样softmax回归也是单层的神经网络。由于每个输出o1,o2,o3o_1,o_2,o_3o1,o2,o3都依赖于所有的输入x1,x2,x3,x4x_1,x_2,x_3,x_4x1,x2,x3,x4因此softmax回归的输出层还是一个全连接层。 图1softmax回归的神经网络图
3、softmax运算
在上面我们由权重与输⼊特征进⾏矩阵-向量乘法再加上偏置b得到的输出o1,o2,o3o_1,o_2,o_3o1,o2,o3。为了获取最终的预测结果我们使用argmaxjoj\arg\underset{j}{\max}o_jargjmaxoj来选择最大的输出ojo_joj作为预测概率。然而直接将线性层的输出视为概率时存在⼀些问题一方面我们没有限制这些输出值的总和为1。另⼀方面根据输入的不同输出值甚至可能为负值。
为了解决上述问题社会科学家邓肯·卢斯于1959年在选择模型choice model的理论基础上发明了softmax函数。 softmax函数将未规范化的预测值变换为非负并且总和为1的概率值同时保证模型可导。我们⾸先对每个未规范化的预测求幂这样可以确保输出非负。为了确保最终输出的总和为1我们再对每个求幂后的结果除以它们的总和。如下式
y^softmax(o) 其中 yjexp(oj)∑k1qexp(ok).(5)\hat{y}softmax(o),,,,,,其中,,\hat{y_j}\frac{exp(o_j)}{\sum_{k1}{q}exp(o_k)}.\tag{5}ysoftmax(o)其中yj∑k1qexp(ok)exp(oj).(5)
这⾥对于所有的jjj总有0≤yj^≤10 ≤ \hat{y_j} ≤ 10≤yj≤1。因此yj\hat{y_j} yj^可以视为⼀个正确的概率分布。softmax运算并不会改变未规范化的预测ooo之间大小的顺序只会将每个类别的预测值转换概率值。因此在预测过程中我们可以用下式来选择输入图像最有可能的类别
argmaxjyjargmaxjoj.(6)\arg\underset{j}{\max}\hat{y_j}\arg\underset{j}{\max}o_j.\tag{6}argjmaxyjargjmaxoj.(6)
尽管softmax是⼀个⾮线性函数但softmax回归的输出仍然由输⼊特征的仿射变换决定。因此softmax回归是⼀个线性模型linear model。
4、小批量样本的矢量化
为了提⾼计算效率并且充分利用GPU我们通常会针对小批量数据执行矢量计算。假设我们读取了⼀个批量的样本XXX其中特征维度输⼊数量为d批量大小为n。此外假设我们在输出中有q个类别。那么小批量样本的特征为X∈Rn×dX\in\R{n×d}X∈Rn×d权重为W∈Rd×qW\in\R{d×q}W∈Rd×q偏置为b∈R1×qb\in\R^{1×q}b∈R1×q。softmax回归的小批量样本的⽮量计算表达式为
OXWTbYsoftmax(O).(7)OXWTb\ \hat{Y}softmax(O).\tag{7}OXWTbY^softmax(O).(7)
5、损失函数
接下来我们需要一个损失函数来评估预测的效果。由于在softmax回归中我们只关心正确类别的预测概率而不需要像线性回归那么精确地预测数值因此我们使用交叉熵损失函数来评估模型的预测效果
l(y,y^)−∑j1qyjlog yj.(8)l(y,\hat{y})-\sum_{j1}{q}{y_j}log,{\hat{y_j}}.\tag{8}l(y,y)−j1∑qyjlogyj.(8)
又因为正确标签向量yyy中只有一个标量为1其余全为0因此8可化简为
l(y,y^)−log yj.(9)l(y,\hat{y})-log,\hat{y_j}.\tag{9}l(y,y)−logyj^.(9)
为了使8更好做偏导计算我们将5代入8中
l(y,y^)−∑j1qyjlog exp(oj)∑k1qexp(ok)∑j1qyjlog ∑k1qexp(ok)−∑j1qyjojlog∑k1qexp(ok)−∑j1qyjoj.(10)l(y,\hat{y})-\sum_{j1}{q}y_jlog,\frac{exp(o_j)}{\sum_{k1}{q}exp(o_k)}\ \qquad\qquad\quad\sum_{j1}{q}y_jlog,\sum_{k1}qexp(o_k)-\sum_{j1}^qy_jo_j\ \qquad\quadlog\sum_{k1}qexp(o_k)-\sum_{j1}qy_jo_j.\tag{10}l(y,y^)−j1∑qyjlog∑k1qexp(ok)exp(oj)j1∑qyjlogk1∑qexp(ok)−j1∑qyjojlogk1∑qexp(ok)−j1∑qyjoj.(10)
6、参数更新
我们对交叉熵损失函数10求导获取l(y,y)l(y,\hat{y})l(y,y)关于ojo_joj的梯度
∂oj l(y,y)exp(oj)∑k1qexp(ok)−yjsoftmax(o)j−yj.(11)\partial_{o_j},l(y,\hat{y})\frac{exp(o_j)}{\sum_{k1}{q}exp(o_k)}-y_jsoftmax(o)_j-y_j.\tag{11}∂ojl(y,y^)∑k1qexp(ok)exp(oj)−yjsoftmax(o)j−yj.(11)
然后采用梯度下降法softmax回归的训练过程为采用正态分布来初始化权重WWW然后通过下式进行迭代更新
Wt1←Wt−α[1N∑n1N(softmax(o)j−yj)].(12)W_{t1}←W_t-\alpha[\frac{1}{N}\sum_{n1}^{N}(softmax(o)_j-y_j)].\tag{12}Wt1←Wt−α[N1n1∑N(softmax(o)j−yj)].(12)
7、实现softmax回归模型
7.1、读取数据集
在本节中我们用softmax回归来实现图像识别。在此之前我们先下载Fashion-MNIST数据集。
pythonimport torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2ld2l.use_svg_display()通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中。
python# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans transforms.ToTensor()
mnist_train torchvision.datasets.FashionMNIST(root/data, trainTrue, transformtrans, downloadTrue)
mnist_test torchvision.datasets.FashionMNIST(root/data, trainFalse, transformtrans, downloadTrue)Fashion-MNIST由10个类别的图像组成 每个类别由训练数据集train dataset中的6000张图像 和测试数据集test dataset中的1000张图像组成。 因此训练集和测试集分别包含60000和10000张图像。 测试数据集不会用于训练只用于评估模型性能。
pythonlen(mnist_train), len(mnist_test)每个输入图像的高度和宽度均为28像素。 数据集由灰度图像组成其通道数为1。
pythonmnist_train[0][0].shapeFashion-MNIST中包含的10个类别分别为t-shirtT恤、trouser裤子、pullover套衫、dress连衣裙、coat外套、sandal凉鞋、shirt衬衫、sneaker运动鞋、bag包和ankle boot短靴。 以下函数用于在数字标签索引及其文本名称之间进行转换。
pythondef get_fashion_mnist_labels(labels): #save返回Fashion-MNIST数据集的文本标签text_labels [t-shirt, trouser, pullover, dress, coat,sandal, shirt, sneaker, bag, ankle boot]return [text_labels[int(i)] for i in labels]创建一个函数来可视化这些样本。
pythondef show_images(imgs, num_rows, num_cols, titlesNone, scale1.5): #save绘制图像列表figsize (num_cols * scale, num_rows * scale)_, axes d2l.plt.subplots(num_rows, num_cols, figsizefigsize)axes axes.flatten()for i, (ax, img) in enumerate(zip(axes, imgs)):if torch.is_tensor(img):# 图片张量ax.imshow(img.numpy())else:# PIL图片ax.imshow(img)ax.axes.get_xaxis().set_visible(False)ax.axes.get_yaxis().set_visible(False)if titles:ax.set_title(titles[i])return axes我们将一个小批量的数据集可视化出来看看。
pythonX, y next(iter(data.DataLoader(mnist_train, batch_size15)))
show_images(X.reshape(15, 28, 28), 3, 5, titlesget_fashion_mnist_labels(y));为了使我们在读取训练集和测试集时更容易我们使用内置的数据迭代器而不是从零开始创建。 回顾一下在每次迭代中数据加载器每次都会读取一小批量数据大小为batch_size。 通过内置数据迭代器我们可以随机打乱了所有样本从而无偏见地读取小批量并通过多线程来读取数据。
pythonbatch_size 256def get_dataloader_workers(): #save使用4个进程来读取数据return 4train_iter data.DataLoader(mnist_train, batch_size, shuffleTrue,num_workersget_dataloader_workers())读取一个小批量数据集所需的时间
pythontimer d2l.Timer()
for X, y in train_iter:continue
f{timer.stop():.2f} sec为了方便使用我们将上述的代码整合为一个函数。
python# 整合上述所有组件
def load_data_fashion_mnist(batch_size, resizeNone): #save下载Fashion-MNIST数据集然后将其加载到内存中trans [transforms.ToTensor()]if resize:trans.insert(0, transforms.Resize(resize))trans transforms.Compose(trans)mnist_train torchvision.datasets.FashionMNIST(root/data, trainTrue, transformtrans, downloadTrue)mnist_test torchvision.datasets.FashionMNIST(root/data, trainFalse, transformtrans, downloadTrue)return (data.DataLoader(mnist_train, batch_size, shuffleTrue,num_workersget_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffleFalse,num_workersget_dataloader_workers()))随后我们使用load_data_fashion_mnist来读取数据集并通过resize参数调整图像的尺寸。
pythontrain_iter, test_iter load_data_fashion_mnist(32, resize28)
for X, y in train_iter:print(X.shape, X.dtype, y.shape, y.dtype)break
for X,y in test_iter:print(X.shape, X.dtype, y.shape, y.dtype)break7.2、从零实现softmax回归
本节我们将使用刚刚在6.1节中引入的Fashion-MNIST数据集 并设置数据迭代器的批量大小为256。
pythonimport torch
from IPython import display
from d2l import torch as d2lbatch_size 256
train_iter, test_iter d2l.load_data_fashion_mnist(batch_size)7.2.1、初始化参数
和之前线性回归的例子一样这里的每个样本都将用固定长度的向量表示。 原始数据集中的每个样本都是28×28的图像。 在本节中我们将展平每个图像把它们看作长度为784的向量。又因为我们的数据集有10个类别所以网络输出维度为10。 因此权重将构成一个784×10的矩阵 偏置将构成一个1×10的行向量。 与线性回归一样我们将使用正态分布初始化我们的权重W偏置初始化为0。
pythonnum_inputs 784
num_outputs 10W torch.normal(0, 0.01, size(num_inputs, num_outputs), requires_gradTrue)
b torch.zeros(num_outputs, requires_gradTrue)7.2.2、定义softmax激活函数
回想一下实现softmax由三个步骤组成
对每个项求幂使用exp对每一行求和小批量中每个样本是一行将每项除以所属行的和确保结果的和为1。
softmax的公式定义 实现如下
python
def softmax(X):X_exp torch.exp(X)partition X_exp.sum(1, keepdimTrue)return X_exp / partition # 这里应用了广播机制我们来验证一下softmax激活函数的效果
pythonX torch.normal(0, 1, (2, 5))
X_prob softmax(X)
X, X_prob, X_prob.sum(1)7.2.3、定义模型
定义softmax操作后我们可以实现softmax回归模型。 下面的代码定义了输入如何通过网络映射到输出。 注意将数据传递到模型之前我们使用reshape函数将每张原始图像展平为向量。
pythondef net(X):return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) b)7.2.4、定义损失函数
在softmax回归中我们只关心正确类别预测的概率因此我们需要将正确类别的预测概率提取出来参与损失函数计算。
为了加快计算速度我们使用y作为y_hat中概率的索引来提取正确类别的预测概率而不会考虑使用for循环这种低效的方式。
pythony torch.tensor([0, 2]) # 真实的标签
y_hat torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]]) # 预测的概率
y_hat[[0, 1], y]因此我们可以很便捷地实现交叉熵损失函数
pythondef cross_entropy(y_hat, y):return - torch.log(y_hat[range(len(y_hat)), y])cross_entropy(y_hat, y)7.2.5、评估分类精度
在实际预测时我们必须输出最终的预测类别。为了计算分类的精度首先如果y_hat是矩阵那么假定第二个维度存储每个类的预测分数。 我们使用argmax获得每行中最大元素的索引来获得预测类别。 然后我们将预测类别与真实y元素进行比较。 由于等式运算符“”对数据类型很敏感 因此我们将y_hat的数据类型转换为与y的数据类型一致。 结果是一个包含0错和1对的张量。 最后我们求和会得到正确预测的数量。
pythondef accuracy(y_hat, y): #save计算预测正确的数量if len(y_hat.shape) 1 and y_hat.shape[1] 1:y_hat y_hat.argmax(axis1)cmp y_hat.type(y.dtype) yreturn float(cmp.type(y.dtype).sum())评估一下分类的精度
pythonaccuracy(y_hat, y) / len(y)同样对于任意数据迭代器data_iter可访问的数据集 我们可以评估在任意模型net的精度。
pythondef evaluate_accuracy(net, data_iter): #save计算在指定数据集上模型的精度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]这里定义一个实用程序类Accumulator用于对多个变量进行累加。 在上面的evaluate_accuracy函数中 我们在Accumulator实例中创建了2个变量 分别用于存储正确预测的数量和预测的总数量。 当我们遍历数据集时两者都将随着时间的推移而累加。
pythonclass Accumulator: #save在n个变量上累加def __init__(self, n):self.data [0.0] * ndef add(self, *args):self.data [a float(b) for a, b in zip(self.data, args)]def reset(self):self.data [0.0] * len(self.data)def __getitem__(self, idx):return self.data[idx]7.2.6、训练模型
首先我们定义一个函数来训练一个迭代周期。 请注意updater是更新模型参数的常用函数它接受批量大小作为参数。 它可以是d2l.sgd函数也可以是框架的内置优化函数。
pythondef train_epoch_ch3(net, train_iter, loss, updater): #save训练模型一个迭代周期# 将模型设置为训练模式if isinstance(net, torch.nn.Module):net.train()# 训练损失总和、训练准确度总和、样本数metric Accumulator(3)for X, y in train_iter:# 计算梯度并更新参数y_hat net(X)l loss(y_hat, y)if isinstance(updater, torch.optim.Optimizer):# 使用PyTorch内置的优化器和损失函数updater.zero_grad()l.mean().backward()updater.step()else:# 使用定制的优化器和损失函数l.sum().backward()updater(X.shape[0])metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())# 返回训练损失和训练精度return metric[0] / metric[2], metric[1] / metric[2]然后我们再定义一个Animator函数来可视化训练的过程
pythonclass Animator: #save在动画中绘制数据def __init__(self, xlabelNone, ylabelNone, legendNone, xlimNone,ylimNone, xscalelinear, yscalelinear,fmts(-, m--, g-., r:), nrows1, ncols1,figsize(3.5, 2.5)):# 增量地绘制多条线if legend is None:legend []d2l.use_svg_display()self.fig, self.axes d2l.plt.subplots(nrows, ncols, figsizefigsize)if nrows * ncols 1:self.axes [self.axes, ]# 使用lambda函数捕获参数self.config_axes lambda: d2l.set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)self.X, self.Y, self.fmts None, None, fmtsdef add(self, x, y):# 向图表中添加多个数据点if not hasattr(y, __len__):y [y]n len(y)if not hasattr(x, __len__):x [x] * nif not self.X:self.X [[] for _ in range(n)]if not self.Y:self.Y [[] for _ in range(n)]for i, (a, b) in enumerate(zip(x, y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x, y, fmt in zip(self.X, self.Y, self.fmts):self.axes[0].plot(x, y, fmt)self.config_axes()display.display(self.fig)display.clear_output(waitTrue)接下来我们实现一个训练函数 它会在train_iter访问到的训练数据集上训练一个模型net。 该训练函数将会运行多个迭代周期由num_epochs指定。 在每个迭代周期结束时利用test_iter访问到的测试数据集对模型进行评估。 我们将利用Animator类来可视化训练进度。
pythondef train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #save训练模型animator Animator(xlabelepoch, xlim[1, num_epochs], ylim[0.3, 0.9],legend[train loss, train acc, test acc])for epoch in range(num_epochs):train_metrics train_epoch_ch3(net, train_iter, loss, updater)test_acc evaluate_accuracy(net, test_iter)animator.add(epoch 1, train_metrics (test_acc,))train_loss, train_acc train_metricsassert train_loss 0.5, train_lossassert train_acc 1 and train_acc 0.7, train_accassert test_acc 1 and test_acc 0.7, test_acc我们使用之前深度学习线性回归中所定义的小批量随机梯度下降来优化参数。其中设置学习率lr为0.1。
pythonlr 0.1 # 学习率def updater(batch_size):return d2l.sgd([W, b], lr, batch_size)现在我们训练模型10个迭代周期。 请注意迭代周期num_epochs和学习率lr都是可调节的超参数。 通过更改它们的值我们可以提高模型的分类精度。
pythonnum_epochs 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)7.2.7、预测
现在训练已经完成我们的模型已经准备好对图像进行分类预测。 给定一系列图像我们将比较它们的实际标签文本输出的第一行和模型预测文本输出的第二行。
pythondef predict_ch3(net, test_iter, n6): #save预测标签for X, y in test_iter:breaktrues d2l.get_fashion_mnist_labels(y)preds d2l.get_fashion_mnist_labels(net(X).argmax(axis1))titles [true \n pred for true, pred in zip(trues, preds)]d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titlestitles[0:n])predict_ch3(net, test_iter, 5)8、总结
在本文中我们讨论了softmax回归模型的基本原理和实现方法。softmax回归适合于离散的分类线性回归适合于预测连续的数值。