0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PyTorch教程-4.4. 从头开始实现 Softmax 回归

jf_pJlTbmA9 来源:PyTorch 作者:PyTorch 2023-06-05 15:43 次阅读

因为 softmax 回归是如此基础,我们相信您应该知道如何自己实现它。在这里,我们限制自己定义模型的 softmax 特定方面,并重用线性回归部分的其他组件,包括训练循环。

import torch
from d2l import torch as d2l

from mxnet import autograd, gluon, np, npx
from d2l import mxnet as d2l

npx.set_np()

from functools import partial
import jax
from flax import linen as nn
from jax import numpy as jnp
from d2l import jax as d2l

No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)

import tensorflow as tf
from d2l import tensorflow as d2l

4.4.1. Softmax

让我们从最重要的部分开始:从标量到概率的映射。作为复习,请回忆一下在张量中沿特定维度的求和运算符,如第 2.3.6 节和 第 2.3.7 节中所讨论的。给定一个矩阵,X我们可以对所有元素(默认情况下)或仅对同一轴上的元素求和。该axis变量让我们计算行和列的总和:

X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdims=True), X.sum(1, keepdims=True)

(tensor([[5., 7., 9.]]),
 tensor([[ 6.],
     [15.]]))

X = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdims=True), X.sum(1, keepdims=True)

(array([[5., 7., 9.]]),
 array([[ 6.],
    [15.]]))

X = jnp.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdims=True), X.sum(1, keepdims=True)

(Array([[5., 7., 9.]], dtype=float32),
 Array([[ 6.],
    [15.]], dtype=float32))

X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
tf.reduce_sum(X, 0, keepdims=True), tf.reduce_sum(X, 1, keepdims=True)

(,
 )

计算 softmax 需要三个步骤:(i)每一项取幂;(ii) 对每一行求和以计算每个示例的归一化常数;(iii) 将每一行除以其归一化常数,确保结果之和为 1。

(4.4.1)softmax(X)ij=exp⁡(Xij)∑kexp⁡(Xik).

分母的(对数)称为(对数)配分函数。它是在统计物理学中引入的 ,用于对热力学系综中的所有可能状态求和。实现很简单:

def softmax(X):
  X_exp = torch.exp(X)
  partition = X_exp.sum(1, keepdims=True)
  return X_exp / partition # The broadcasting mechanism is applied here

def softmax(X):
  X_exp = np.exp(X)
  partition = X_exp.sum(1, keepdims=True)
  return X_exp / partition # The broadcasting mechanism is applied here

def softmax(X):
  X_exp = jnp.exp(X)
  partition = X_exp.sum(1, keepdims=True)
  return X_exp / partition # The broadcasting mechanism is applied here

def softmax(X):
  X_exp = tf.exp(X)
  partition = tf.reduce_sum(X_exp, 1, keepdims=True)
  return X_exp / partition # The broadcasting mechanism is applied here

对于任何输入X,我们将每个元素变成一个非负数。每行总和为 1,这是概率所要求的。注意:上面的代码对于非常大或非常小的参数并不稳健。虽然这足以说明正在发生的事情,但您不应 将此代码逐字用于任何严肃的目的。深度学习框架内置了这样的保护,我们将在未来使用内置的 softmax。

X = torch.rand((2, 5))
X_prob = softmax(X)
X_prob, X_prob.sum(1)

(tensor([[0.1560, 0.2128, 0.2260, 0.2372, 0.1680],
     [0.1504, 0.2473, 0.1132, 0.2779, 0.2112]]),
 tensor([1.0000, 1.0000]))

X = np.random.rand(2, 5)
X_prob = softmax(X)
X_prob, X_prob.sum(1)

(array([[0.17777154, 0.1857739 , 0.20995119, 0.23887765, 0.18762572],
    [0.24042214, 0.1757977 , 0.23786479, 0.15572716, 0.19018826]]),
 array([1., 1.]))

X = jax.random.uniform(jax.random.PRNGKey(d2l.get_seed()), (2, 5))
X_prob = softmax(X)
X_prob, X_prob.sum(1)

(Array([[0.17380024, 0.13607854, 0.29826194, 0.18967763, 0.20218161],
    [0.24212085, 0.19360834, 0.21299706, 0.17635451, 0.17491929]],   dtype=float32),
 Array([1., 1.], dtype=float32))

X = tf.random.uniform((2, 5))
X_prob = softmax(X)
X_prob, tf.reduce_sum(X_prob, 1)

(,
 )

4.4.2. 该模型

我们现在拥有了实现 softmax 回归模型所需的一切。与我们的线性回归示例一样,每个实例都将由一个固定长度的向量表示。由于这里的原始数据包括28×28像素图像,我们将每个图像展平,将它们视为长度为 784 的向量。在后面的章节中,我们将介绍卷积神经网络,它以更令人满意的方式利用空间结构。

在 softmax 回归中,我们网络的输出数量应该等于类的数量。由于我们的数据集有 10 个类,我们的网络的输出维度为 10。因此,我们的权重构成784×10矩阵加一个1×10 偏差的维行向量。与线性回归一样,我们W使用高斯噪声初始化权重。偏差被初始化为零。

class SoftmaxRegressionScratch(d2l.Classifier):
  def __init__(self, num_inputs, num_outputs, lr, sigma=0.01):
    super().__init__()
    self.save_hyperparameters()
    self.W = torch.normal(0, sigma, size=(num_inputs, num_outputs),
               requires_grad=True)
    self.b = torch.zeros(num_outputs, requires_grad=True)

  def parameters(self):
    return [self.W, self.b]

class SoftmaxRegressionScratch(d2l.Classifier):
  def __init__(self, num_inputs, num_outputs, lr, sigma=0.01):
    super().__init__()
    self.save_hyperparameters()
    self.W = np.random.normal(0, sigma, (num_inputs, num_outputs))
    self.b = np.zeros(num_outputs)
    self.W.attach_grad()
    self.b.attach_grad()

  def collect_params(self):
    return [self.W, self.b]

class SoftmaxRegressionScratch(d2l.Classifier):
  num_inputs: int
  num_outputs: int
  lr: float
  sigma: float = 0.01

  def setup(self):
    self.W = self.param('W', nn.initializers.normal(self.sigma),
              (self.num_inputs, self.num_outputs))
    self.b = self.param('b', nn.initializers.zeros, self.num_outputs)

class SoftmaxRegressionScratch(d2l.Classifier):
  def __init__(self, num_inputs, num_outputs, lr, sigma=0.01):
    super().__init__()
    self.save_hyperparameters()
    self.W = tf.random.normal((num_inputs, num_outputs), 0, sigma)
    self.b = tf.zeros(num_outputs)
    self.W = tf.Variable(self.W)
    self.b = tf.Variable(self.b)

下面的代码定义了网络如何将每个输入映射到输出。请注意,我们将每个28×28reshape在将数据传递给我们的模型之前,将批处理中的像素图像转换为向量。

@d2l.add_to_class(SoftmaxRegressionScratch)
def forward(self, X):
  X = X.reshape((-1, self.W.shape[0]))
  return softmax(torch.matmul(X, self.W) + self.b)

@d2l.add_to_class(SoftmaxRegressionScratch)
def forward(self, X):
  X = X.reshape((-1, self.W.shape[0]))
  return softmax(np.dot(X, self.W) + self.b)

@d2l.add_to_class(SoftmaxRegressionScratch)
def forward(self, X):
  X = X.reshape((-1, self.W.shape[0]))
  return softmax(jnp.matmul(X, self.W) + self.b)

@d2l.add_to_class(SoftmaxRegressionScratch)
def forward(self, X):
  X = tf.reshape(X, (-1, self.W.shape[0]))
  return softmax(tf.matmul(X, self.W) + self.b)

4.4.3. 交叉熵损失

接下来我们需要实现交叉熵损失函数( 4.1.2节介绍 )。这可能是所有深度学习中最常见的损失函数。目前,深度学习的应用很容易抛出分类问题,远远超过那些更好地视为回归问题的问题。

回想一下,交叉熵采用分配给真实标签的预测概率的负对数似然。为了提高效率,我们避免使用 Python for 循环并改用索引。特别是,one-hot 编码y允许我们选择匹配项y^.

为了在行动中看到这一点,我们创建了样本数据,y_hat其中包含 3 个类及其对应标签的 2 个预测概率示例 y。正确的标签是0和2分别(即一等和三等)。使用y中概率的指标y_hat,我们可以有效地挑选出术语。

y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]

tensor([0.1000, 0.5000])

现在我们可以通过对所选概率的对数进行平均来实现交叉熵损失函数。

def cross_entropy(y_hat, y):
  return -torch.log(y_hat[list(range(len(y_hat))), y]).mean()

cross_entropy(y_hat, y)

tensor(1.4979)

@d2l.add_to_class(SoftmaxRegressionScratch)
def loss(self, y_hat, y):
  return cross_entropy(y_hat, y)

y = np.array([0, 2])
y_hat = np.array([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]

array([0.1, 0.5])

Now we can implement the cross-entropy loss function by averaging over the logarithms of the selected probabilities.

def cross_entropy(y_hat, y):
  return -np.log(y_hat[list(range(len(y_hat))), y]).mean()

cross_entropy(y_hat, y)

array(1.4978662)

@d2l.add_to_class(SoftmaxRegressionScratch)
def loss(self, y_hat, y):
  return cross_entropy(y_hat, y)

y = jnp.array([0, 2])
y_hat = jnp.array([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]

Array([0.1, 0.5], dtype=float32)

Now we can implement the cross-entropy loss function by averaging over the logarithms of the selected probabilities.

Note that to make use of jax.jit to speed up JAX implementations, and to make sure loss is a pure function, the cross_entropy function is re-defined inside the loss to avoid usage of any global variables or functions which may render the loss function impure. We refer interested readers to the JAX documentation on jax.jit and pure functions.

def cross_entropy(y_hat, y):
  return -jnp.log(y_hat[list(range(len(y_hat))), y]).mean()

cross_entropy(y_hat, y)

Array(1.4978662, dtype=float32)

@d2l.add_to_class(SoftmaxRegressionScratch)
@partial(jax.jit, static_argnums=(0))
def loss(self, params, X, y, state):
  def cross_entropy(y_hat, y):
    return -jnp.log(y_hat[list(range(len(y_hat))), y]).mean()
  y_hat = state.apply_fn({'params': params}, *X)
  # The returned empty dictionary is a placeholder for auxiliary data,
  # which will be used later (e.g., for batch norm)
  return cross_entropy(y_hat, y), {}

y_hat = tf.constant([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = tf.constant([0, 2])
tf.boolean_mask(y_hat, tf.one_hot(y, depth=y_hat.shape[-1]))


Now we can implement the cross-entropy loss function by averaging over the logarithms of the selected probabilities.

def cross_entropy(y_hat, y):
  return -tf.reduce_mean(tf.math.log(tf.boolean_mask(
    y_hat, tf.one_hot(y, depth=y_hat.shape[-1]))))

cross_entropy(y_hat, y)


@d2l.add_to_class(SoftmaxRegressionScratch)
def loss(self, y_hat, y):
  return cross_entropy(y_hat, y)

4.4.4. 训练

我们重用第 3.4 节fit中定义的方法来训练具有 10 个 epoch 的模型。请注意,时期数 ( )、小批量大小 ( ) 和学习率 ( ) 都是可调整的超参数。这意味着虽然这些值不是在我们的主要训练循环中学习到的,但它们仍然会影响我们模型的性能、bot vis-a-vis 训练和泛化性能。在实践中,您将希望根据数据的验证拆分来选择这些值,然后最终在测试拆分上评估您的最终模型。如第 3.6.3 节所述max_epochsbatch_sizelr,我们将 Fashion-MNIST 的测试数据视为验证集,从而报告此拆分的验证损失和验证准确性。

data = d2l.FashionMNIST(batch_size=256)
model = SoftmaxRegressionScratch(num_inputs=784, num_outputs=10, lr=0.1)
trainer = d2l.Trainer(max_epochs=10)
trainer.fit(model, data)

pYYBAGR5VgqAExsFAAGKX8leS1k713.svg

data = d2l.FashionMNIST(batch_size=256)
model = SoftmaxRegressionScratch(num_inputs=784, num_outputs=10, lr=0.1)
trainer = d2l.Trainer(max_epochs=10)
trainer.fit(model, data)

poYBAGR5Vg2ABqK6AAGLB0Eidv0636.svg

data = d2l.FashionMNIST(batch_size=256)
model = SoftmaxRegressionScratch(num_inputs=784, num_outputs=10, lr=0.1)
trainer = d2l.Trainer(max_epochs=10)
trainer.fit(model, data)

pYYBAGR5Vg-AR9EdAAGLFkwOa9I235.svg

data = d2l.FashionMNIST(batch_size=256)
model = SoftmaxRegressionScratch(num_inputs=784, num_outputs=10, lr=0.1)
trainer = d2l.Trainer(max_epochs=10)
trainer.fit(model, data)

poYBAGR5VhKAbq3pAAGK_cjKgSM338.svg

4.4.5. 预言

现在训练已经完成,我们的模型已准备好对一些图像进行分类。

X, y = next(iter(data.val_dataloader()))
preds = model(X).argmax(axis=1)
preds.shape

torch.Size([256])

X, y = next(iter(data.val_dataloader()))
preds = model(X).argmax(axis=1)
preds.shape

(256,)

X, y = next(iter(data.val_dataloader()))
preds = model.apply({'params': trainer.state.params}, X).argmax(axis=1)
preds.shape

(256,)

X, y = next(iter(data.val_dataloader()))
preds = tf.argmax(model(X), axis=1)
preds.shape

TensorShape([256])

我们更感兴趣的是我们标记错误的图像。我们通过将它们的实际标签(文本输出的第一行)与模型的预测(文本输出的第二行)进行比较来可视化它们。

wrong = preds.type(y.dtype) != y
X, y, preds = X[wrong], y[wrong], preds[wrong]
labels = [a+'n'+b for a, b in zip(
  data.text_labels(y), data.text_labels(preds))]
data.visualize([X, y], labels=labels)

pYYBAGR5VhSAVqY4AAIFNGvzY-Q502.svg

wrong = preds.astype(y.dtype) != y
X, y, preds = X[wrong], y[wrong], preds[wrong]
labels = [a+'n'+b for a, b in zip(
  data.text_labels(y), data.text_labels(preds))]
data.visualize([X, y], labels=labels)

pYYBAGR5VheAX5-hAAHuRRk8q20818.svg

wrong = preds.astype(y.dtype) != y
X, y, preds = X[wrong], y[wrong], preds[wrong]
labels = [a+'n'+b for a, b in zip(
  data.text_labels(y), data.text_labels(preds))]
data.visualize([X, y], labels=labels)

poYBAGR5VhmABcT-AAIHlsNP9aI422.svg

wrong = tf.cast(preds, y.dtype) != y
X, y, preds = X[wrong], y[wrong], preds[wrong]
labels = [a+'n'+b for a, b in zip(
  data.text_labels(y), data.text_labels(preds))]
data.visualize([X, y], labels=labels)

pYYBAGR5VhuAKLrKAAIBilBL_jo766.svg

4.4.6. 概括

到目前为止,我们开始获得解决线性回归和分类问题的一些经验。有了它,我们已经达到了可以说是 1960-1970 年代统计建模的最先进水平。在下一节中,我们将向您展示如何利用深度学习框架更有效地实施该模型。

4.4.7. 练习

在本节中,我们根据 softmax 运算的数学定义直接实现了 softmax 函数。如第 4.1 节所述,这会导致数值不稳定。

测试如果softmax输入的值为100?

测试如果softmax所有输入中最大的小于−100?

通过查看相对于参数中最大条目的值来实施修复。

实现一个cross_entropy遵循交叉熵损失函数定义的函数∑iyilog⁡y^i.

在上面的代码示例中尝试一下。

为什么你认为它运行得更慢?

你应该使用它吗?在哪些情况下有意义?

你需要注意什么?提示:考虑对数的定义域。

返回最有可能的标签总是一个好主意吗?例如,你会为了医学诊断而这样做吗?你会如何尝试解决这个问题?

假设我们要使用 softmax 回归来根据某些特征预测下一个单词。大词汇量可能会带来哪些问题?

试验上面代码的超参数。尤其:

绘制验证损失如何随着学习率的变化而变化。

当您更改小批量大小时,验证和训练损失是否会发生变化?在看到效果之前,您需要多大或多小?

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • pytorch
    +关注

    关注

    2

    文章

    808

    浏览量

    13221
  • Softmax
    +关注

    关注

    0

    文章

    9

    浏览量

    2507
收藏 人收藏

    评论

    相关推荐

    如何使用和声从头开始设置HTTP服务?

    有没有关于如何使用和声从头开始设置HTTP服务的最新教程/设置?(不是从演示项目开始
    发表于 04-08 09:39

    pytorch训练出来的模型参数保存为嵌入式C语言能够调用形式的方法

    个线性回归的简单深度学习例子,输入节点为1个,隐藏层10个节点,输出一个节点。relu用的是普通的,所以不需要参数。f = open('./w1.txt',mode = 'w+') #是创建一个txt文件。参数 'w+'的意思是如果存在该文件,则删除内容,从头开始书写,如
    发表于 12-15 06:57

    能否连接JTAG调试器并从头开始对MCU进行编程,因为MCU上没有旧代码?

    我正在设计一个带有 mimxrt1062 芯片的 PCB。我要把 JTAG 引脚拿出来。我能否连接 JTAG 调试器并从头开始对 MCU 进行编程,因为 MCU 上没有旧代码?有什么需要注意的吗?
    发表于 05-12 06:20

    从头开始构建无人机

    电子发烧友网站提供《从头开始构建无人机.zip》资料免费下载
    发表于 11-22 10:35 1次下载
    <b class='flag-5'>从头开始</b>构建无人机

    如何从头开始制作六足机器人

    电子发烧友网站提供《如何从头开始制作六足机器人.zip》资料免费下载
    发表于 11-23 09:53 2次下载
    如何<b class='flag-5'>从头开始</b>制作六足机器人

    PyTorch教程3.1之线性回归

    电子发烧友网站提供《PyTorch教程3.1之线性回归.pdf》资料免费下载
    发表于 06-05 11:30 0次下载
    <b class='flag-5'>PyTorch</b>教程3.1之线性<b class='flag-5'>回归</b>

    PyTorch教程3.4之从头开始执行线性回归

    电子发烧友网站提供《PyTorch教程3.4之从头开始执行线性回归.pdf》资料免费下载
    发表于 06-05 11:25 0次下载
    <b class='flag-5'>PyTorch</b>教程3.4之<b class='flag-5'>从头开始</b>执行线性<b class='flag-5'>回归</b>

    PyTorch教程3.5之线性回归的简洁实现

    电子发烧友网站提供《PyTorch教程3.5之线性回归的简洁实现.pdf》资料免费下载
    发表于 06-05 11:28 0次下载
    <b class='flag-5'>PyTorch</b>教程3.5之线性<b class='flag-5'>回归</b>的简洁<b class='flag-5'>实现</b>

    PyTorch教程4.1之Softmax回归

    电子发烧友网站提供《PyTorch教程4.1之Softmax回归.pdf》资料免费下载
    发表于 06-05 15:46 0次下载
    <b class='flag-5'>PyTorch</b>教程4.1之<b class='flag-5'>Softmax</b><b class='flag-5'>回归</b>

    PyTorch教程4.4从头开始实现Softmax回归

    电子发烧友网站提供《PyTorch教程4.4从头开始实现Softmax回归.pdf》资料免费下
    发表于 06-05 15:37 0次下载
    <b class='flag-5'>PyTorch</b>教程<b class='flag-5'>4.4</b>之<b class='flag-5'>从头开始</b><b class='flag-5'>实现</b><b class='flag-5'>Softmax</b><b class='flag-5'>回归</b>

    PyTorch教程-3.4. 从头开始执行线性回归

    在 SageMaker Studio Lab 中打开笔记本 我们现在准备好通过线性回归的全功能实现来工作。在本节中,我们将从头开始实现整个方法,包括(i)模型;(ii) 损失函数;(
    的头像 发表于 06-05 15:38 497次阅读
    <b class='flag-5'>PyTorch</b>教程-3.4. <b class='flag-5'>从头开始</b>执行线性<b class='flag-5'>回归</b>

    PyTorch教程-4.1. Softmax 回归

    在 SageMaker Studio Lab 中打开笔记本 在3.1 节中,我们介绍了线性回归,在3.4 节中从头开始实现 ,并在3.5 节中再次使用深度学习框架的高级 API来完成繁重的工作
    的头像 发表于 06-05 15:38 628次阅读
    <b class='flag-5'>PyTorch</b>教程-4.1. <b class='flag-5'>Softmax</b> <b class='flag-5'>回归</b>

    为什么从头开始为汽车应用设计IC很重要

    级和3级自动驾驶技术的好处,这些技术提供了部分到有条件的转向,加速和制动自动化。反过来,这些技术又推动了对车内半导体内容的需求。本文将探讨为什么从性能和安全角度出发,从头开始设计汽车IC比重新利用最初设计用于其他用途的芯片更好。
    的头像 发表于 06-12 14:25 648次阅读

    在Spartan 6 FPGA上从头开始实现全加器

    电子发烧友网站提供《在Spartan 6 FPGA上从头开始实现全加器.zip》资料免费下载
    发表于 06-15 10:13 0次下载
    在Spartan 6 FPGA上<b class='flag-5'>从头开始</b><b class='flag-5'>实现</b>全加器

    如何在PyTorch实现LeNet-5网络

    等人提出,主要用于手写数字识别任务(如MNIST数据集)。下面,我将详细阐述如何在PyTorch从头开始实现LeNet-5网络,包括网络架构设计、参数初始化、前向传播、损失函数选择、优化器配置以及训练流程等方面。
    的头像 发表于 07-11 10:58 791次阅读