type
status
date
slug
summary
tags
category
icon
password

从零开始实现的 softmax

以下内容大部分来自动手学深度学习在线课程加上一些笔记.

理论知识


前置知识

线性代数,线性回归模型,概率相关知识

softmax 指代一种分类问题.这样的问题首先考虑怎么样抽象这个问题,首先我们要实现的是一个函数,输入已知的信息,输出这是哪一类物体.这里输出采用独热码,意思就是,有 个类别,我们把已知的信息作为输入,然后有 个输出,这 个输出中只有一个是 ,其他都是 . 代表真, 代表假.这样就可以表示出答案了.而实际上我们能做到的充其量是给出这个物体是这一类的概率,也就是每个输出的值会在 区间内.

模型函数

那么如何实现这个函数呢,你能看出独热码不是一个线性函数,而我们想要这个函数尽量简单,就得用线性函数模拟这个理想的函数.其实就是从线性回归的多输入一输出套很多层,成了多输入多输出.从:
变成了
这种编码方式就是 个线性回归在一起,不一样的地方是,现在每个线性回归输出值得是 这个区间内的.我们取一个最大的,也就是最靠近 的作为预测,形式化来说就是:
指的是预测值, 就是每个线性回归模型的输出.
我们想要想要接近完美的模拟独热码,希望这个分类函数分的很清楚,也就是期望最好是一个类别非常接近 ,其他几乎为 ,形式化来说:
再考虑一个问题,我们的输出值必须是 区间内的,这里就是为什么它叫 softmax 回归的原因,我们用 softmax 函数来把线性回归的输出再变换一次,压到 区间.形式化来说:
这个就是一个 softmax 函数的定义,原本的结果是 ,现在最终的结果我们用 表示.

损失函数

现在再看我们的函数输出,它其实是一个概率分布,我们的目的就是让概率分布与真实概率的分布差异尽量小,那我们的"损失"就是概率分布的差异,衡量两个概率分布的区别,就想到交叉熵:
从目的的导向看,这个函数就很适合作为损失函数,让我们把输出代进去:
梯度下降法要求我们把这个函数关于所有参数的梯度都求出来,这个函数也很容易计算梯度:

总结

理论可行,实践开始,我们以图像的分类问题为例.

这里和线性回归模型相同,是使用该公式:
但此时 y 其实是每一行有多个输出的,因此它是一个矩阵,我们最好把它大写:
 
接下来定义 softmax 函数,注意 python 有广播机制,简单来说就是猜测当你违反数学规律进行运算时你实际想要干什么,特别地,这里就是把 exp_sum 给扩充成和 X_exp 同样的形状.
然后就可以实现模型了.对于为什么对 X 要执行 reshape 操作,我们可以动用爱的力量(
进行 reshape 操作是在开发过程中帮助开发者发现问题的一种手段。如果输入数据的形状与模型参数的形状不匹配,通常会在 reshape 操作或矩阵乘法时抛出错误,从而提醒开发者检查数据的形状是否正确。
假设我们有一个简单的神经网络,输入层有 3 个神经元,隐藏层有 2 个神经元。模型参数 的形状是 (3,2)。输入数据 的形状可能是 (4,3),表示有 4 个样本,每个样本有 3 个特征。此时,可以直接进行矩阵乘法: 形状为(4,3)×(3,2)=(4,2)
但如果输入数据的形状是 (4,2),表示每个样本有 2 个特征,而模型参数 的形状仍然是 (3,2),则无法直接进行矩阵乘法。此时,需要检查输入数据的形状是否正确,并进行相应的调整。
接下来实现交叉熵损失函数
下面的代码中,y_hat 是一个矩阵,代表着 batch_size 个样本的概率分布,而 y 是一个列向量,每个元素是正确分类的标号
最后对于这个模型,我们得有一个评判标准,这个模型的功能如何,具体到这个例子,就是预测的准确率有多少
对于这里的类型转换,让我们看看 deepseek 的说法(
  1. 数据类型不匹配可能导致错误: 在深度学习框架(如 PyTorch 或 TensorFlow)中,不同的操作可能对输入张量的数据类型有不同的要求。如果 y_predict 和 y 的数据类型不一致,可能会导致无法执行某些操作,或者结果不符合预期。
  1. 确保比较操作的正确性: 在进行比较操作(如 ==)时,如果两个张量的数据类型不同,框架可能会尝试进行隐式类型转换。然而,这种隐式转换并不总是可靠,可能会导致意外结果。显式地将 y_predict 转换为与 y 一致的类型,可以避免这种潜在问题。
最后模型整体的精度应该是整个数据集的正确率.为了方便,我们用一个 Accumulator 类来实现两组数据的一起累加.
定义一个梯度下降的函数
开始训练
一个简单的线性回归模型线性同余方程
Loading...