【深度学习】基于Pytorch的softmax回归问题辨析和应用(二)
【深度学习】基于Pytorch的softmax回归问题辨析和应用(二)
文章目录
1 softmax回归的实现
1.1 初始化模型参数
1.2 Softmax的实现
1.3 优化器
1.4 训练
2 多分类问题torch.nn.Softmax的使用
1 softmax回归的实现
我们可以发现(通过深度学习框架的高级API能够使实现)
线性(回归变得更加容易)。同样地,通过深度学习框架的高级API也能更方便地实现分类模型。让我们继续使用Fashion-MNIST数据集,并保持批量大小为256.
import torch
from torch import nn
from d2l import torch as d2l
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
1.1 初始化模型参数
在 Sequential
中添加一个带有10个输出的全连接层。同样,在这里,Sequential
并不是必要的,但我们可能会形成这种习惯。因为在实现深度模型时,Sequential
将无处不在。我们仍然以均值0和标准差0.01随机初始化权重。
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
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);
1.2 Softmax的实现
回想一下,softmax函数 $\hat y_j = \frac{\exp(o_j)}{\sum_k \exp(o_k)}$,其中$\hat y_j$是预测的概率分布。$o_j$是未归一化的预测$\mathbf{o}$的第$j$个元素。如果$o_k$中的一些数值非常大,那么 $\exp(o_k)$ 可能大于数据类型容许的最大数字(即 上溢(overflow))。这将使分母或分子变为inf
(无穷大),我们最后遇到的是0、inf
或 nan
(不是数字)的 $\hat y_j$。在这些情况下,我们不能得到一个明确定义的交叉熵的返回值。
解决这个问题的一个技巧是,在继续softmax计算之前,先从所有$o_k$中减去$\max(o_k)$。你可以证明每个 $o_k$ 按常数进行的移动不会改变softmax的返回值。在减法和归一化步骤之后,可能有些 $o_j$ 具有较大的负值。由于精度受限, $\exp(o_j)$ 将有接近零的值,即 下溢(underflow)。这些值可能会四舍五入为零,使 $\hat y_j$ 为零,并且使得 $\log(\hat y_j)$ 的值为 -inf
。反向传播几步后,我们可能会发现自己面对一屏幕可怕的nan
结果。
尽管我们要计算指数函数,但我们最终在计算交叉熵损失时会取它们的对数。
通过将softmax和交叉熵结合在一起,可以避免反向传播过程中可能会困扰我们的数值稳定性问题。如下面的等式所示,我们避免计算$\exp(o_j)$,而可以直接使用$o_j$。因为$\log(\exp(\cdot))$被抵消了。
$$
\begin{aligned}
\log{(\hat y_j)} & = \log\left( \frac{\exp(o_j)}{\sum_k \exp(o_k)}\right) \
& = \log{(\exp(o_j))}-\log{\left( \sum_k \exp(o_k) \right)} \
& = o_j -\log{\left( \sum_k \exp(o_k) \right)}.
\end{aligned}
$$
我们也希望保留传统的softmax函数,以备我们需要评估通过模型输出的概率。
但是,我们没有将softmax概率传递到损失函数中,
loss = nn.CrossEntropyLoss()
1.3 优化器
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
1.4 训练
接下来我们实现一个训练函数,它会在train_iter 访问到的训练数据集上训练一个模型net。该训练函数将会运行多个迭代周期(由num_epochs指定)。在每个迭代周期结束时,利用 test_iter 访问到的测试数据集对模型进行评估。我们将利用 Animator 类来可视化训练进度。
def train_epoch_ch3(net, train_iter, loss, updater): #@save
"""训练模型一个迭代周期(定义见第3章)。"""
# 训练损失总和、训练准确度总和、样本数
metric = Accumulator(3)
if isinstance(updater, gluon.Trainer):
updater = updater.step
for X, y in train_iter:
# 计算梯度并更新参数
with autograd.record():
y_hat = net(X)
l = loss(y_hat, y)
l.backward()
updater(X.shape[0])
metric.add(float(l.sum()), accuracy(y_hat, y), y.size)
# 返回训练损失和训练准确率
return metric[0] / metric[2], metric[1] / metric[2]
使用SGD优化器更新模型参数:
lr = 0.1
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #@save
"""训练模型(定义见第3章)。"""
animator = Animator(xlabel='epoch', 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_metrics
assert train_loss < 0.5, train_loss
assert train_acc <= 1 and train_acc > 0.7, train_acc
assert test_acc <= 1 and test_acc > 0.7, test_acc
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
2 多分类问题torch.nn.Softmax的使用
为什么谈论这个问题呢?是因为我在工作的过程中遇到了语义分割预测输出特征图个数为16,也就是所谓的16分类问题。
因为每个通道的像素的值的大小代表了像素属于该通道的类的大小,为了在一张图上用不同的颜色显示出来,我不得不学习了torch.nn.Softmax的使用。
首先看一个简答的例子,倘若输出为(3, 4, 4),也就是3张4x4的特征图。
import torch
img = torch.rand((3,4,4))
print(img)
输出为:
tensor([[[0.0413, 0.8728, 0.8926, 0.0693],
[0.4072, 0.0302, 0.9248, 0.6676],
[0.4699, 0.9197, 0.3333, 0.4809],
[0.3877, 0.7673, 0.6132, 0.5203]],
[[0.4940, 0.7996, 0.5513, 0.8016],
[0.1157, 0.8323, 0.9944, 0.2127],
[0.3055, 0.4343, 0.8123, 0.3184],
[0.8246, 0.6731, 0.3229, 0.1730]],
[[0.0661, 0.1905, 0.4490, 0.7484],
[0.4013, 0.1468, 0.2145, 0.8838],
[0.0083, 0.5029, 0.0141, 0.8998],
[0.8673, 0.2308, 0.8808, 0.0532]]])
我们可以看到共三张特征图,每张特征图上对应的值越大,说明属于该特征图对应类的概率越大。
import torch.nn as nn
sogtmax = nn.Softmax(dim=0)
img = sogtmax(img)
print(img)
tensor([[[0.2780, 0.4107, 0.4251, 0.1979],
[0.3648, 0.2297, 0.3901, 0.3477],
[0.4035, 0.4396, 0.2993, 0.2967],
[0.2402, 0.4008, 0.3273, 0.4285]],
[[0.4371, 0.3817, 0.3022, 0.4117],
[0.2726, 0.5122, 0.4182, 0.2206],
[0.3423, 0.2706, 0.4832, 0.2522],
[0.3718, 0.3648, 0.2449, 0.3028]],
[[0.2849, 0.2076, 0.2728, 0.3904],
[0.3627, 0.2581, 0.1917, 0.4317],
[0.2543, 0.2898, 0.2175, 0.4511],
[0.3880, 0.2344, 0.4278, 0.2686]]])
可以看到,上面的代码对每张特征图对应位置的像素值进行Softmax函数处理, 图中标红位置加和=1,同理,标蓝位置加和=1。
我们看到Softmax函数会对原特征图每个像素的值在对应维度(这里dim=0,也就是第一维)上进行计算,将其处理到0~1之间,并且大小固定不变。
print(torch.max(img,0))
输出为:
torch.return_types.max(
values=tensor([[0.4371, 0.4107, 0.4251, 0.4117],
[0.3648, 0.5122, 0.4182, 0.4317],
[0.4035, 0.4396, 0.4832, 0.4511],
[0.3880, 0.4008, 0.4278, 0.4285]]),
indices=tensor([[1, 0, 0, 1],
[0, 1, 1, 2],
[0, 0, 1, 2],
[2, 0, 2, 0]]))
可以看到这里3x4x4变成了1x4x4,而且对应位置上的值为像素对应每个通道上的最大值,并且indices是对应的分类。
清楚理解了上面的流程,那么我们就容易处理了。
看具体案例,这里输出output的大小为:16x416x416.
output = torch.tensor(output)
sm = nn.Softmax(dim=0)
output = sm(output)
mask = torch.max(output,0).indices.numpy()
# 因为要转化为RGB彩色图,所以增加一维
rgb_img = np.zeros((output.shape[1], output.shape[2], 3))
for i in range(len(mask)):
for j in range(len(mask[0])):
if mask[i][j] == 0:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 255
rgb_img[i][j][2] = 255
if mask[i][j] == 1:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 180
rgb_img[i][j][2] = 0
if mask[i][j] == 2:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 180
rgb_img[i][j][2] = 180
if mask[i][j] == 3:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 180
rgb_img[i][j][2] = 255
if mask[i][j] == 4:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 255
rgb_img[i][j][2] = 180
if mask[i][j] == 5:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 255
rgb_img[i][j][2] = 0
if mask[i][j] == 6:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 180
if mask[i][j] == 7:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 255
if mask[i][j] == 8:
rgb_img[i][j][0] = 255
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 0
if mask[i][j] == 9:
rgb_img[i][j][0] = 180
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 0
if mask[i][j] == 10:
rgb_img[i][j][0] = 180
rgb_img[i][j][1] = 255
rgb_img[i][j][2] = 255
if mask[i][j] == 11:
rgb_img[i][j][0] = 180
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 180
if mask[i][j] == 12:
rgb_img[i][j][0] = 180
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 255
if mask[i][j] == 13:
rgb_img[i][j][0] = 180
rgb_img[i][j][1] = 255
rgb_img[i][j][2] = 180
if mask[i][j] == 14:
rgb_img[i][j][0] = 0
rgb_img[i][j][1] = 180
rgb_img[i][j][2] = 255
if mask[i][j] == 15:
rgb_img[i][j][0] = 0
rgb_img[i][j][1] = 0
rgb_img[i][j][2] = 0
cv2.imwrite('output.jpg', rgb_img)
- 分享
- 举报
-
浏览量:4971次2021-08-05 09:21:07
-
浏览量:6097次2021-08-03 11:36:18
-
浏览量:6147次2021-08-03 11:36:37
-
浏览量:5115次2021-07-26 11:25:51
-
浏览量:667次2023-09-18 15:02:26
-
浏览量:4794次2021-08-09 16:10:30
-
浏览量:6602次2021-05-31 17:02:05
-
浏览量:5660次2021-06-15 11:49:53
-
浏览量:5623次2021-05-17 16:52:58
-
浏览量:5843次2021-07-26 17:43:04
-
浏览量:5523次2021-08-02 09:34:03
-
浏览量:5298次2021-08-13 15:39:02
-
浏览量:5021次2021-06-21 11:50:25
-
浏览量:5289次2021-08-09 16:10:57
-
浏览量:6611次2021-08-09 16:09:53
-
浏览量:5014次2021-07-26 11:28:05
-
浏览量:4997次2021-07-02 14:29:53
-
浏览量:4296次2021-06-30 11:34:00
-
浏览量:13302次2021-07-08 09:43:47
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
这把我C
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明