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) 是?个类型为 ByteTensorTensor ,我们? float() 将其转换为值为0(相等为假)或1(相等为真)的浮点型 Tensor 。

def accuracy(y_hat, y):
 return (y_hat.argmax(dim=1) == y).float().mean().item()

????继续使?在演示 gather 函数时定义的变量 y_haty ,并将它们分别作为预测概率分布和标签。可以看到,第?个样本预测类别为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的主要内容,如果未能解决你的问题,请参考以下文章

多层感知机的从零开始实现( 从D2L 包中抽取函数)

多层感知机的从零开始实现( 从D2L 包中抽取函数)

Softmax 回归的从零开始实现 pytorch

翻译: 3.6. 从零开始实现 Softmax 回归 pytorch

从零实现深度学习框架——Softmax回归简介

我在 Pytorch 中从零开始构建 Softmax 的问题是啥