Softmax函数从零开始实现——2020.2.22
Posted somedayli
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Softmax函数从零开始实现——2020.2.22相关的知识,希望对你有一定的参考价值。
?????先导?本节实现所需的包或模块。
import torch
import torchvision
import numpy as np
import sys
sys.path.append("..") # 为了导?上层?录的d2lzh_pytorch
import d2lzh_pytorch as d2l
1.获取数据集
????使?Fashion-MNIST数据集,并设置批量??为256。
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
2.初始化模型参数
????跟线性回归中的例??样,我们将使?向量表示每个样本。已知每个样本输?是?和宽均为28像素的图像。模型的输?向量的?度是(28 imes 28 =784):该向量的每个元素对应图像中每个像素。由于图像有10个类别,单层神经?络输出层的输出个数为10,因此softmax回归的权重和偏差参数分别为(784 imes 10)和(1 imes 10)的矩阵。
num_inputs = 784
num_outputs = 10
W = torch.tensor(np.random.normal(0, 0.01, (num_inputs,
num_outputs)), dtype=torch.float)
b = torch.zeros(num_outputs, dtype=torch.float)
????需要模型参数梯度,进行跟踪。
W.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
3. 实现softmax运算
????对多维Tensor
按维度操作:给定?个 Tensor
矩阵 X
。我们可以只对其中同?列( dim=0
)或同??( dim=1
)的元素求和,并在结果中保留?和列这两个维度( keepdim=True
)。
X = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(X.sum(dim=0, keepdim=True))
print(X.sum(dim=1, keepdim=True))
输出结果:
tensor([[5, 7, 9]])
tensor([[ 6], [15]])
定义softmax运算:
????矩阵 X
的?数是样本数,列数是输出个数。为了表达样本预测各个输出的概率,softmax运算会先通过 exp 函数对每个元素做指数运算,再对 exp
矩阵同?元素求和,最后令矩阵每?各元素与该?元素之和相除。这样?来,最终得到的矩阵每?元素和为1且?负。因此,该矩阵每?都是合法的概率分布。softmax运算的输出矩阵中的任意??元素代表了?个样本在各个输出类别上的预测概率。
def softmax(X):
X_exp = X.exp()
partition = X_exp.sum(dim=1, keepdim=True)
return X_exp / partition # 这?应?了?播机制
????可以看到,对于随机输?,我们将每个元素变成了?负数,且每??和为1。
X = torch.rand((2, 5))
X_prob = softmax(X)
print(X_prob, X_prob.sum(dim=1))
输出结果:
tensor([[0.2206, 0.1520, 0.1446, 0.2690, 0.2138], [0.1540, 0.2290, 0.1387, 0.2019, 0.2765]]) tensor([1., 1.])
4. 定义模型
????定义上节描述的softmax回归模型了。这?通过 view
函数将每张原始图像改成?度为 num_inputs
的向量。
def net(X):
return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)
5. 定义损失函数
????为了得到标签的预测概率,我们可以使? gather
函数。在下?的例?中,变量 y_hat
是2个样本在3个类别的预测概率,变量 y
是这2个样本的标签类别。通过使? gather
函数,我们得到了2个样本的标签的预测概率。与softmax回归数学表述中标签类别离散值从1开始逐?递增不同,在代码中,标签类别的离散值是从0开始逐?递增的。
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = torch.LongTensor([0, 2])
y_hat.gather(1, y.view(-1, 1))
输出结果:
tensor([[0.1000], [0.5000]])
????实现softmax回归中的交叉熵损失函数:
def cross_entropy(y_hat, y):
return - torch.log(y_hat.gather(1, y.view(-1, 1)))
6. 计算分类准确率
????给定?个类别的预测概率分布 y_hat
,我们把预测概率最?的类别作为输出类别。如果它与真实类别 y
?致,说明这次预测是正确的。分类准确率即正确预测数量与总预测数量之?。
为了演示准确率的计算,下?定义准确率 accuracy
函数。其中 y_hat.argmax(dim=1) 返回矩阵 y_hat 每 ? 中 最 ? 元 素 的 索 引 , 且 返 回 结 果 与 变 量 y 形 状 相 同 。 相 等 条 件 判 断式 (y_hat.argmax(dim=1) == y)
是?个类型为 ByteTensor
的 Tensor
,我们? float()
将其转换为值为0(相等为假)或1(相等为真)的浮点型 Tensor 。
def accuracy(y_hat, y):
return (y_hat.argmax(dim=1) == y).float().mean().item()
????继续使?在演示 gather
函数时定义的变量 y_hat
和 y
,并将它们分别作为预测概率分布和标签。可以看到,第?个样本预测类别为2(该?最?元素0.6在本?的索引为2),与真实标签0不?致;第?个样本预测类别为2(该?最?元素0.5在本?的索引为2),与真实标签2?致。因此,这两个样本上的分类准确率为0.5。
print(accuracy(y_hat, y))
输出结果:
0.5
????评价模型 net 在数据集 data_iter 上的准确率。
# 本函数已保存在d2lzh_pytorch包中?便以后使?。该函数将被逐步改进:它的完整实现将在“图像增?”?节中描述
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n
????因为随机初始化了模型 net ,所以这个随机模型的准确率应该接近于类别个数10的倒数即0.1。
print(evaluate_accuracy(test_iter, net)
输出结果:
0.0568
7. 训练模型
num_epochs, lr = 5, 0.1
# 本函数已保存在d2lzh包中方便以后使用
def train_ch3(net, train_iter, test_iter, loss, num_epochs,
batch_size, params=None, lr=None, optimizer=None):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y).sum()
# 梯度清零
if optimizer is not None:
optimizer.zero_grad()
elif params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
l.backward()
if optimizer is None:
d2l.sgd(params, lr, batch_size)
else:
optimizer.step() # “softmax回归的简洁实现”?节将?到
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f' % (epoch + 1, train_l_sum / n, train_acc_sum / n,test_acc))
以上是关于Softmax函数从零开始实现——2020.2.22的主要内容,如果未能解决你的问题,请参考以下文章