文章目录
- 前言
- 一、恒等函数和softmax函数
- 恒等函数
- softmax 函数
- python实现softmax函数
- 二、实现softmax函数时的注意事项
- 函数优化
- python实现
- 三、softmax函数的特征
- 计算神经网络的输出
- 输出层的softmax函数可以省略
- “学习”和“推理”阶段
- 四、输出层的神经元数量
前言
神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出 层的激活函数。一般而言,回归问题用恒等函数,分类问题用softmax函数。
机器学习的问题大致可以分为分类问题和回归问题
- 分类问题是数据属于哪一个类别的问题。比如,区分图像中的人是男性还是女性的问题就是分类问题
- 回归问题是根据某个输入预测一个(连续的)数值的问题。比如,根据一个人的图像预测这个人的体重的问题就是回归问题(类似“57.4kg”这样的预测)
一、恒等函数和softmax函数
恒等函数
恒等函数会将输入按原样输出,将恒等函数的处理过程用神经网络图来表示,则如下图所示。
softmax 函数
分类问题中使用的 softmax 函数可以用下面的式子表示
y
k
=
exp
(
a
k
)
∑
i
=
1
n
exp
(
a
i
)
y_k=\frac{\exp \left(a_k\right)}{\sum_{i=1}^n \exp \left(a_i\right)}
yk=∑i=1nexp(ai)exp(ak)
exp
(
x
)
\exp (x)
exp(x) 是表示
e
x
\mathrm{e}^x
ex 的指数函数 (
e
\mathrm{e}
e 是纳皮尔常数
2.7182
⋯
)
\left.2.7182 \cdots\right)
2.7182⋯)
上式表示假设输出层共有 n n n 个神经元, 计算第 k k k 个神经元的输出 y k y_k yk 。
- 分子是输人信号 a k a_k ak 的指数函数
- 分母是所有输人信号的指数函数的和。
下图表示softmax函数。函数的输出通过箭头与所有的输入信号相连,这是因为, 输出层的各个神经元都受到所有输入信号的影响(从上式可以看出)
python实现softmax函数
import numpy as np
# 定义输入向量a
a = np.array([0.3, 2.9, 4.0])
# 计算指数函数
exp_a = np.exp(a)
print("指数函数结果:", exp_a) # [ 1.34985881 18.17414537 54.59815003]
# 计算指数函数的和
sum_exp_a = np.sum(exp_a)
print("指数函数的和:", sum_exp_a) # 74.1221542102
# 计算softmax
y = exp_a / sum_exp_a
print("softmax:", y) # [ 0.01821127 0.24519181 0.73659691]
代码按照softmax函数实现,下面把它定义成如下的Python函数
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
二、实现softmax函数时的注意事项
函数优化
上面的 softmax 函数在运算上存在溢出问题,因为计算机表示的数值范围是有限的。
softmax 函数的实现中要进行指数函数的运算,但是此时指数函数的值很容易变得非常大。比如,
e
10
e^{10}
e10 的值会超过
20000
,
e
100
20000, e^{100}
20000,e100 会变成一个后面有 40 多个 0 的超大值,
e
1000
e^{1000}
e1000 的结果会返回一个表示无穷大的inf。如果在这些超大值之间进行除法运算, 结果会出现 不确定的情况。
softmax函数的实现可以像下式这样改进
y
k
=
exp
(
a
k
)
∑
i
=
1
n
exp
(
a
i
)
=
C
exp
(
a
k
)
C
∑
i
=
1
n
exp
(
a
i
)
=
exp
(
a
k
+
log
C
)
∑
i
=
1
n
exp
(
a
i
+
log
C
)
=
exp
(
a
k
+
C
′
)
∑
i
=
1
n
exp
(
a
i
+
C
′
)
\begin{aligned} y_k=\frac{\exp \left(a_k\right)}{\sum_{i=1}^n \exp \left(a_i\right)} & =\frac{\mathrm{C} \exp \left(a_k\right)}{\mathrm{C} \sum_{i=1}^n \exp \left(a_i\right)} \\ & =\frac{\exp \left(a_k+\log \mathrm{C}\right)}{\sum_{i=1}^n \exp \left(a_i+\log \mathrm{C}\right)} \\ & =\frac{\exp \left(a_k+\mathrm{C}^{\prime}\right)}{\sum_{i=1}^n \exp \left(a_i+\mathrm{C}^{\prime}\right)} \end{aligned}
yk=∑i=1nexp(ai)exp(ak)=C∑i=1nexp(ai)Cexp(ak)=∑i=1nexp(ai+logC)exp(ak+logC)=∑i=1nexp(ai+C′)exp(ak+C′)
-
首先在分子和分母上都乘上 C \mathrm{C} C 这个任意的常数 (因为同时对分母和分子乘以相同的常数, 所以计算结果不变)
-
然后把这个 C \mathrm{C} C 移动到指数函数 (exp) 中, 记为 log C \log \mathrm{C} logC
- 这个步骤利用了指数函数的性质: exp ( x + y ) = exp ( x ) ⋅ exp ( y ) \exp(x+y) = \exp(x) \cdot \exp(y) exp(x+y)=exp(x)⋅exp(y)
- 分子部分: exp ( a k ) ⋅ C = exp ( a k ) ⋅ exp ( log C ) = exp ( a k + log C ) \exp(a_k) \cdot \mathrm{C} = \exp(a_k) \cdot \exp(\log \mathrm{C}) = \exp(a_k + \log \mathrm{C}) exp(ak)⋅C=exp(ak)⋅exp(logC)=exp(ak+logC)
- 分母部分: ∑ i = 1 n exp ( a i ) ⋅ C = ∑ i = 1 n exp ( a i ) ⋅ exp ( log C ) = ∑ i = 1 n exp ( a i + log C ) \sum_{i=1}^n \exp(a_i) \cdot \mathrm{C} = \sum_{i=1}^n \exp(a_i) \cdot \exp(\log \mathrm{C}) = \sum_{i=1}^n \exp(a_i + \log \mathrm{C}) ∑i=1nexp(ai)⋅C=∑i=1nexp(ai)⋅exp(logC)=∑i=1nexp(ai+logC)
-
最后把 log C \log \mathrm{C} logC 替换为另一个符号 C ′ \mathrm{C}^{\prime} C′
上式说明, 在进行 softmax 的指数函数的运算时, 加上 (或者减去)某个常数并不会改变运算的结果。这里的 C ′ \mathrm{C}^{\prime} C′ 可以使用任何值, 但是为了防止溢出, 一般会使用输人信号中的最大值。
python实现
下面来看一个具体的例子
import numpy as np
# 定义输入向量a
a = np.array([1010, 1000, 990])
# 直接计算softmax函数会出现数值溢出的问题
# np.exp(a) / np.sum(np.exp(a)) # 结果为 [nan, nan, nan]
# 解决方法:将输入向量减去最大值,然后再计算softmax函数
c = np.max(a) # 最大值为 1010
a_shifted = a - c
softmax_output = np.exp(a_shifted) / np.sum(np.exp(a_shifted))
print(softmax_output) # 结果为 [ 9.99954600e-01, 4.53978686e-05, 2.06106005e-09]
通过减去输入信号中的最大值(上例中的c),原本为nan(not a number,不确定)的地方,现在被正确计算了。
所以可以像下面这样实现softmax函数
def softmax(a):
c = np.max(a)
exp_a = np.exp(a - c) # 溢出对策
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
三、softmax函数的特征
计算神经网络的输出
使用softmax()函数,可以按如下方式计算神经网络的输出。
# 定义输入向量a
a = np.array([0.3, 2.9, 4.0])
# 计算softmax函数
y = softmax(a)
print(y) # [ 0.01821127 0.24519181 0.73659691]
# 验证概率分布的性质,softmax函数的输出应该是一个概率分布,所有元素的和应该为1
print(np.sum(y)) # 1.0
softmax函数的输出是0.0到1.0之间的实数,并且 softmax 函数的输出值的总和是1。输出总和为1是softmax函数的一个重要性质。正因为有了这个性质,我们才可以把softmax函数的输出解释为“概率”。
比如,上面的例子可以解释成y[0]的概率是0.018(1.8%), y[1]的概率 是0.245(24.5%), y[2]的概率是0.737(73.7%)。从概率的结果来看,可以 说“因为第2个元素的概率最高,所以答案是第2个类别”。而且,还可以回答“有 74%的概率是第2个类别,有25%的概率是第1个类别,有1%的概 率是第0个类别”。也就是说,通过使用softmax函数,我们可以用概率的(统计的)方法处理问题。
输出层的softmax函数可以省略
需要注意的是,即便使用了softmax函数,各个元素之间的大小关系也不会改变。这是因为指数函数(y=exp(x))是单调递增函数。实际上, 上例中a的各元素的大小关系和y的各元素的大小关系并没有改变。
比如,a 的最大值是第2个元素,y的最大值也仍是第2个元素。 一般而言,神经网络只把输出值最大的神经元所对应的类别作为识别结果。 并且,即便使用softmax函数,输出值最大的神经元的位置也不会变。
因此, 神经网络在进行分类时,输出层的softmax函数可以省略。在实际的问题中, 由于指数函数的运算需要一定的计算机运算量,因此输出层的softmax函数一般会被省略。
“学习”和“推理”阶段
求解机器学习问题的步骤可以分为“学习”和“推理”两个阶段
- 首先,在学习阶段进行模型的学习
- 在推理阶段,用学到的模型对未知的数据进行推理(分类)
如前所述,推理阶段一般会省略输出层的softmax函数。在输出层使用softmax函数是因为它和神经网络的学习有关系。
四、输出层的神经元数量
输出层的神经元数量需要根据待解决的问题来决定。对于分类问题,输出层的神经元数量一般设定为类别的数量。
比如,对于某个输入图像,预测是图中的数字0到9中的哪一个的问题(10类别分类问题),可以像下图这样, 将输出层的神经元设定为10个。输出层的神经元从上往下依次对应数字 0, 1, …, 9。图中输出层的神经元的值用不同的灰度表示。这个例子中神经元 y 2 y_2 y2 颜色最深,输出的值最大。这表明这个神经网络预测的是 y 2 y_2 y2 对应的类别,也就是“2”。