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
一般情况下,我们会先设置一个较大的学习率以尽快到达极小点。但固定学习率往往会带来另一个问题,那就是无法准确收敛,并在极小值附近反复波动。所以,为了兼顾梯度下降法后期的收敛速度和准确度,必须要应用学习率衰减策略。
一般情况下,学习率衰减策略有三种,分别是:
在实际应用中,按步长衰减是最常用的,其基本公式如下:
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 震荡或超出数值范围。