Softmax 函数
Softmax 函数函数是机器学习和深度学习中相当常用到的函数,它的公式如下:
s
o
f
t
m
a
x
(
S
)
=
e
s
k
∑
j
e
s
j
softmax(S)=\frac { e ^ { s _ { k } } } { \sum _ { j } e ^ { s _ { j } } }
softmax(S)=∑jesjesk
其中 𝑠𝑘 表示的是输入到 Softmax 函数的数据。Softmax 函数具体的作用是将输入标准化到和为 1 的输出,经过 Softmax 函数的的数据可以被认为是概率。
为了更好理解,假设最后的输出
y
y
y 为
(
3.2
,
5.1
,
−
1.7
)
(3.2, 5.1, -1.7)
(3.2,5.1,−1.7),对应的是每一个类别的概率,但是显然这个数字不能直接使用,通常来说概率都是和为 1 的,所以才需要经过 Softmax 进行标准化。
代码实现:
def softmax(input):
assert len(input.shape) == 2 # 输入 softmax 函数的数据必须是一个二维矩阵
exp_value = np.exp(input) # 首先计算指数
output = exp_value/np.sum(exp_value, axis=1)[:, np.newaxis] # 然后按行标准化
return output
test_data = np.array([[3.2, 5.1, -1.7]])
softmax(test_data)
交叉熵损失函数
既然经过 Softmax 函数之后的是概率,那么最根本的想法当然是使正确类别的概率最大。交叉熵损失函数出现的目的就是使正确类别概率最大。
交叉熵是用来衡量两个概率之间的差别。深度学习中使用 softmax 输出的概率拟合真实概率,并使得这两个两个概率之间的交叉熵最小。
假设交叉熵损失函数为 L,那么单个样本的损失定义为:
L
i
=
−
Y
i
log
P
i
L _ { i } = - Y_i \log P_i
Li=−YilogPi
指的是在样本是
x
i
x_i
xi 的情况下,使用概率分布
P
i
P_i
Pi 拟合真实概率分布
Y
i
Y_i
Yi 的误差。为了更容易理解,代入 Softmax 函数的公式作为概率,交叉熵损失函数可以写成:
L
i
=
−
∑
y
i
log
(
e
x
i
∑
j
e
x
j
)
L _ { i } = - \sum y_i \log \left( \frac { e ^ { x_{ i } } } { \sum _ { j } e ^ { x _ { j } } } \right)
Li=−∑yilog(∑jexjexi)
多个样本时,只需要将各个样本的损失相加即可:
L
=
1
N
∑
L
i
L = \frac{1}{N}\sum L _ { i }
L=N1∑Li
其中
Y
=
(
y
1
,
…
,
y
i
,
…
)
Y=(y_1,\dots,y_i,\dots)
Y=(y1,…,yi,…) 是真实概率,因为是独热编码,所以
y
i
y_i
yi 要么为 0 要么 为 1;
X
=
(
x
1
,
…
,
x
i
,
…
)
X=(x_1,\dots,x_i,\dots)
X=(x1,…,xi,…) 是神经网络的输出。
代码实现:
# 最后的输出是 N*C
N, C = 100, 3
test_data = np.random.randn(N, C) # 生成随机数据
# 生成随机标签,不包括 C,并独热编码
test_labels = encoder.fit_transform(np.random.randint(0, C, (N, 1))).toarray()
prob = softmax(test_data) # 每一行对应一个样本,按行计算概率
# 根据公式 7,8 实现
# loss = np.sum(-np.log(prob)) # 根据公式计算 loss
loss = np.sum(-np.log(prob) * test_labels) / N
学习率衰减策略
一般情况下,我们会先设置一个较大的学习率以尽快到达极小点。但固定学习率往往会带来另一个问题,那就是无法准确收敛,并在极小值附近反复波动。所以,为了兼顾梯度下降法后期的收敛速度和准确度,必须要应用学习率衰减策略。
一般情况下,学习率衰减策略有三种,分别是:
- Step decay(按步长衰减)。
- Exponential decay(指数衰减): α = α 0 e − k t \alpha = \alpha_0 e^{-k t} α=α0e−kt, t t t 是迭代次数, α 0 \alpha_0 α0 和 k k k 是提前设定的值。
- 1/t decay (1/t 衰减): α = α 0 / ( 1 + k t ) \alpha = \alpha_0 / (1 + k t ) α=α0/(1+kt), t t t 是迭代次数, α 0 \alpha_0 α0 和 k k k 是提前设定的值。
在实际应用中,按步长衰减是最常用的,其基本公式如下:
l
r
=
b
a
s
e
_
l
r
∗
α
k
lr = base\_lr*\alpha^k
lr=base_lr∗αk
其中,
α
\alpha
α 为设定的衰减因子,
k
k
k 为是迭代次数/提前设定的步长,通俗点讲其实是每迭代一个设定的步长数目,则学习率变为上一个步长的
α
\alpha
α。步长需要根据实际应用调整,但是衰减因子常为 0.1,即学习率是上一个步长的
1
10
\frac{1}{10}
101。
实现代码:
class lr_scheduler(object):
def __init__(self, base_lr, step_size, deacy_factor=0.1):
self.base_lr = base_lr # 最初的学习率
self.deacy_factor = deacy_factor # 学习率衰减因子
self.step_count = 0 # 当前的迭代次数
self.lr = base_lr # 当前学习率
self.step_size = step_size # 步长
def step(self, step_count=1): # 默认 1 次
self.step_count += step_count
def get_lr(self):
# 根据公式 12 实现
self.lr = self.base_lr * \
(self.deacy_factor**(self.step_count//self.step_size)) # 实现上面的公式
return self.lr
应用学习率衰减策略时需要注意最大迭代次数足够,否则可能收敛不完全。学习率非常小的时候,会导致收敛速度很慢;学习率很大时,会出现Loss 震荡或超出数值范围。