Softmax回归模型(多分类模型)

一、Softmax模型(多分类)定义:

逻辑回归是线性回归的推广,引入激活函数使得使得模型输出层单个神经元的单一输出值限制在0和1之间,适合于二分类问题。
Softmax模型是逻辑回归模型的推广,通过在输出层加入多个神经元,将单输出问题(二分类)映射到多输出问题(多分类)。
在这里插入图片描述

  • Softmax回归中,输出为一维向量(o1,o2,o3),其中oi表示样本在类别i上的置信度。
  • 一个特征对应多个权重参数,表示该特征在各个分类上的权重。

二、训练过程:

1.参数维度:

在这里插入图片描述
注意这里xi是一个特征。

  • 分类数/输出维度决定偏置值b维度:每个分类的预测单元(神经元)都有一个偏置值b,而分类数又和输出维度相同
  • 特征数×分类数/输出维度决定权重W维度:每个特征都作为所有类别的神经元的输入。

2.对真实值标签编码:

  • 分类问题得到的是分类结果,为了更好的计算损失,需要对真实值标签进行一位有效编码
    在这里插入图片描述

3.对预测值标签编码:

3.1Softmax激活函数:

Softmax激活函数可以将样本在各个分类上预测结果数值组成的一维向量O转换为在0~1上的概率,并且所有分类预测概率之和为1。
在这里插入图片描述
具体来说,对于在每个分类上的置信度(预测概率),Softmax激活函数的计算公式如下:
在这里插入图片描述

3.2对预测结果编码:

多分类问题的Softmax回归输出是一个一维向量通过Softmax激活函数后向量中的各个值分别表示对各个分类的置信度(预测概率)

其中向量中最高置信度oi对应的类别即为预测结果(使用独热编码实现)
在这里插入图片描述在这里插入图片描述
注意这里xi是一个特征。

4.损失函数:

4.1单个样本上的损失函数:

4.1.1损失函数定义:

对真实值和预测值进行合理的编码后,y和yhat都是关于类别概率的一维向量,这样就可以通过比较概率之间的区别来衡量预测损失。

在这里插入图片描述

4.1.2交叉熵损失函数:

在这里插入图片描述
注意这里的yi是一个数,而不是一个向量,这里求的是对一个样本的损失而不是一个batch的损失。

4.1.3交叉熵损失函数化简:

由于真实值向量y中仅有一个1,其余全是0,因此损失函数可以化简如下:
在这里插入图片描述
注意这里的yi是一个数,而不是一个向量,这里求的是对一个样本的损失而不是一个batch的损失。
此时损失函数就是当前样本的真实类别y上的预测概率yhaty的对数再取反。 即对分类问题只关心 模型在正确类别上的预测概率(yhaty) 而不关心模型在错误类别上的预测概率。

4.1整个batch上的损失函数:

训练过程中计算损失是按照整个batch计算的。
在这里插入图片描述
在这里插入图片描述

5.梯度下降算法:

5.1梯度计算:

在这里插入图片描述
在这里插入图片描述
注意这里xi是一个特征。

5.2梯度下降算法公式:

在这里插入图片描述

6.训练过程举例:******

以十分类模型的一次训练过程为例:
1.获取一个batch,里面包含batch_size张图片。
2.将batch_size张图片展成一维(例如24×24的图片展成784),获得输入维度为:batch_size×784×1(图片数×特征维度[784×1])。
3.参数W维度计算为784×10,参数b维度计算为10×1。
4.每张图片分别输入来计算各个分类j上的预测值yj=wij·xij+bj(其中i表示特征,i从1遍历到784),最终十个yj组合成输出维度1×10。
5.整个batch中的输出组合成维度batch_size×10。
6.使用softmax回归将输出映射成概率,维度为batch_size×10,并且每行概率之和为1。
7.使用交叉熵损失函数计算batch中所有图片的概率损失,并取均值。
7.计算各个参数wmn、bn关于损失函数的梯度。
8.反向传播算法修改参数值。
9.输入下一个batch进行训练。

三、底层代码实现:

import torch
from IPython import display
from d2l import torch as d2l


# 1.获取数据集,并返回一个指定batch大小的DataLoader
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

num_inputs = 784# 由于softmax回归输入要求是向量,所以对于24×24的图片,应该拉长成784的向量作为输入
num_outputs = 10# 10分类问题,输出为长度为10的向量,里面记录样本在各个类别上的预测概率
# 2.初始化模型参数值
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)# 均值为0方差为1,形状为784×10(784个特征,每个特征都作为10个类别的输入)
b = torch.zeros(num_outputs, requires_grad=True)# 形状为10×1,因为10个类别的预测都有一个

# 3.激活函数
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition

# 4.交叉熵损失函数
def cross_entropy(y_hat, y):
    return -torch.log(y_hat[range(len(y_hat)), y])

# 5.梯度下降算法
lr = 0.1
def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

# 6.模型架构,输出每行概率之和为1的矩阵
def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)

# 7.计算batch中分类正确的样本数
def accuracy(y_hat, y):
    """计算预测正确的数量。"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())

# 8.计算每个epoch模型精度
class Accumulator:
    """在`n`个变量上累加。"""
    def __init__(self, n):
        self.data = [0.0] * n

    def 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]
def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度。"""
    if isinstance(net, torch.nn.Module):
        net.eval()
    metric = Accumulator(2)
    for X, y in data_iter:
        metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

# 9.一次epoch训练
def train_epoch_ch3(net, train_iter, loss, updater):
    """训练模型一个迭代周期(定义见第3章)。"""
    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):
            updater.zero_grad()
            l.backward()
            updater.step()
            metric.add(
                float(l) * len(y), accuracy(y_hat, y),
                y.size().numel())
        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]

# 10.训练过程
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    """训练模型"""
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
    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


# 11.绘制训练过程图像
class Animator:  
    """在动画中绘制数据。"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes,]
        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, fmts

    def add(self, x, y):
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if 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)
# 训练
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
# 预测
def predict_ch3(net, test_iter, n=6):  
    """预测标签(定义见第3章)。"""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)

四、Pytorch版代码:

import torch
from torch import nn
from d2l import torch as d2l
# 1.获取训练集和测试集的DataLoader迭代器
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# 2.定义网络模型
num_inputs = 784# 由于softmax回归输入要求是向量,所以对于24×24的图片,应该拉长成784的向量作为输入
num_outputs = 10# 10分类问题,输出为长度为10的向量,里面记录样本在各个类别上的预测概率
net = nn.Sequential(
    nn.Flatten(),# 将输入展平
    nn.Linear(num_inputs, num_outputs)# 线性全连接层
)

# 3.初始化参数。这个函数检查m是否是一个线性层(nn.Linear),如果是,则使用正态分布(均值为0,标准差为0.01)初始化该层的权重参数
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights)

# 4.交叉熵损失函数
loss = nn.CrossEntropyLoss()

# 5.梯度下降算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

# 6.训练过程
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姓蔡小朋友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值