chap3 线性神经网络(2)
softmax 回归
遇到分类问题时,我们可以使用 softmax 方法将模型的输出作为概率,我们将优化参数以最大化观测数据的概率。为了得到预测结果,我们将设置一个阈值,如选择具有最大概率的标签。
回归 vs 分类
- 回归估计一个连续值
- 分类预测一个离散类别
回归:
- 单连续值输出
- 自然区间 R
- 跟真实值的区别作为损失
分类:
- 通常多个输出
- 输出 i 时预测为第 i 类的置信度
分类 -> 回归
统计学家使用 独热编码 的形式来表示类别数据。
-
独热编码(one-hot encoding)。独热编码是一个向量,它的分量和类别一样多。类别对应的分量设置为1,其他所有分量设置为0。
例如,将用一个三维向量标签 表示【猫,鸡,狗】中的一种动物,那么可以有如下对应关系:对应于“猫”、对应于“鸡”、对应于“狗”:
softmax 算子
softmax由三个步骤组成:
(1)对每个项求幂(使用exp
);
(2)对每一行求和(小批量中每个样本是一行),得到每个样本的归一化常数;
(3)将每一行除以其归一化常数,确保结果的和为1。
1 | def softmax(X): |
模型
假设我们读取了一个批量的样本,其中特征维度(输入数量)为 ,批量大小为 。此外,假设我们在输出中有 个类别。那么小批量特征为 ,权重为,偏置为。softmax 回归的矢量计算表达式为:
相对于一次处理一个样本,小批量样本的矢量化加快了的矩阵-向量乘法。
注:
由于中的每一行代表一个数据样本,所以 softmax 运算可以按行(rowwise)执行:对于的每一行,我们先对所有项进行幂运算,然后通过求和对它们进行标准化。
小批量的未归一化预测和输出概率都是形状为 的矩阵。
1 | def net(X): |
损失函数
用来衡量真实值和预测值之间的区别,他的导数指明参数的更新方向。
L2 loss 均方误差
- 离原点较远的数据更新梯度较大,随着靠近原点,梯度慢慢变小。
L1 loss 绝对值
- 所有数据更新的梯度值都是常数,大部分情况下比 L2 更稳定。
- 缺点:绝对值函数在 0 点不可导,预测值和真实值之间相距过近的时候,导数值会在 -1 和 1 之间震荡(不稳定)
Huber‘s Robust Loss
L1 和 L2 取长补短:
;
交叉熵损失
对数似然
softmax函数给出了一个向量,我们可以将其视为给定任意输入的每个类的估计条件概率。例如,=。假设整个数据集具有个样本,其中索引的样本由特征向量和独热标签向量组成。我们可以将估计值与实际值进行比较:
根据最大似然估计,我们最大化,相当于最小化负对数似然(最大化似然估计等同于最小化损失函数):
疑问 :第二个等号为什么成立?????
其中,对于任何标签和模型预测,损失函数为:
:eqlabel:eq_l_cross_entropy
在本节稍后的内容会讲到, :eqref:eq_l_cross_entropy
中的损失函数通常被称为交叉熵损失(cross-entropy loss)。由于是一个长度为的独热编码向量,所以除了一个项以外的所有项都消失了。由于所有都是预测的概率,所以它们的对数永远不会大于。
因此,如果正确地预测实际标签,即,如果实际标签,则损失函数不能进一步最小化。
注意,这往往是不可能的。例如,数据集中可能存在标签噪声(某些样本可能被误标),或输入特征没有足够的信息来完美地对每一个样本分类。
交叉熵
-
交叉熵用来衡量两个概率的区别:
-
将他作为损失:
只关心正确类的置信值是多大
-
其梯度是真实概率和预测概率的区别:
1 | y = torch.tensor([0, 2]) |
softmax 回归的简洁实现
- 导入包和数据集
1 | import torch |
-
初始化模型参数
softmax 回归的输出层是一个全连接层*。
全连接层的每一个节点都与上一层的所有结点相连,用来把前面提取到的特征综合起来。基于这个特性,全连接层的参数也是最多的。
具体来说,对于任何具有个输入和个输出的全连接层,参数开销为,在实践中可能高得令人望而却步。
幸运的是,将个输入转换为个输出的成本可以减少到,其中超参数可以由我们灵活指定,以在实际应用中平衡参数节约和模型有效性1
2
3
4
5
6
7
8
9
10
11# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
# flatten: 将任意维度的 tensor 都转为 2-维 tensor,第0维度保留,其他维度展开成向量
# 输入:784,输出:10
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights); -
在交叉熵损失函数中传递未归一化的预测,并同时计算 softmax 及其对数
1
loss = nn.CrossEntropyLoss()
-
使用学习率为0.1的小批量随机梯度下降作为优化算法
1
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
-
训练
1
2num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
问题记录
-
softmax 和 logistic 回归分析是一样的吗,如果不一样,区别在哪里?
logistic 是二分类的预测,是 softmax 的一个特例。
-
为什么使用交叉熵作为损失函数,而不用相对熵/互信息等其他基于信息量的度量?
我们的目标:需要量化两个概率之间的区别,这些函数理论上都能做到。
不用其他的函数是因为交叉熵是最好计算的,在此之外没有什么区别。
互信息 ,交叉熵
-
极大似然估计和交叉熵损失函数之间的关系???
python 语法点
记录一些不太熟悉的 python 语法知识点,方便回顾。
zip() 函数
描述
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
zip 方法在 Python 2 和 Python 3 中的不同:在 Python 3.x 中为了减少内存,zip() 返回的是一个对象。如需展示列表,需手动 list() 转换。
如果需要了解 Python3 的应用,可以参考 Python3 zip()。
语法
zip 语法:
1 | zip([iterable, ...]) |
参数说明:
- iterable – 一个或多个迭代器;
返回值
返回元组列表。
实例
以下实例展示了 zip 的使用方法:
1 | >>>a = [1,2,3] |
isinstance() 函数
isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
isinstance() 与 type() 区别:
- type() 不会认为子类是一种父类类型,不考虑继承关系。
- isinstance() 会认为子类是一种父类类型,考虑继承关系。
如果要判断两个类型是否相同推荐使用 isinstance()。
语法
以下是 isinstance() 方法的语法:
1 | isinstance(object, classinfo) |
参数
- object – 实例对象。
- classinfo – 可以是直接或间接类名、基本类型或者由它们组成的元组。
返回值
如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。