学习率:是控制模型学习效率(步长)的权重。
学习率的大小区分
区别 | 学习率大 | 学习率小 |
---|---|---|
学习速度 | 快 | 慢 |
应用场合 | 训练初期 | 数次Epoch过后 |
缺点 | 1.容易损失过大 2.容易发生振荡 | 1.容易过拟合 2.收敛速度慢 |
学习率的设置
在训练过程中,一般根据训练轮数设置动态变化的学习率:
1.刚开始训练时:学习率以 0.01 ~ 0.001 为宜。
2.一定轮数过后:逐渐减缓。
3.接近训练结束:学习速率的衰减应该在100倍以上。
loss-Epoch图像
理想情况下曲线应该是滑梯式下降,如图中的[绿线]:
- 曲线 初始时 上扬 [红线]: Solution:初始 学习率过大 导致 振荡,应减小学习率,并 从头 开始训练 。
- 曲线 初始时 强势下降 没多久 归于水平 [紫线]: Solution:后期 学习率过大 导致 无法拟合,应减小学习率,并 重新训练后面几轮 。
- 曲线 全程缓慢 [黄线]: Solution:初始 学习率过小 导致 收敛慢,应 增大学习率,并从头开始训练。
批量,即Batch,是深度学习中的一个重要概念。
批量通常指两个不同的概念——如果对应的是模型训练方法,那么批量指的是将所有数据处理完以后一次性更新权重或者参数的估计;如果对应的是模型训练中的数据,那么批量通常指的是一次输入供模型计算用的数据量。
基于批量概念的模型训练通常按照如下步骤进行:
批量算法相对应的是递增算法,其步骤如下:
对比:
这里的主要区别是批量算法一次处理所有的数据;而在递增算法中,每处理一个或者数个观测值就要更新一次参数。
在后向传播算法中,“处理”对应的具体操作就是计算损失函数的梯度变化曲线。如果是批量算法,则计算平均或者总的损失函数的梯度变化曲线;而如果是递增算法,则计算损失函数仅在对应于该观测值或者数个观测值时的梯度变化曲线。
“更新”则是从已有的参数值中减去梯度变化率和学习速率的乘积。
为什么要用batch?
batch可以大致理解为计算损失函数时需要用到的样本个数,以上两种不同的概念,仅是在样本个数差异上的不同体现。
1.遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度。这称为Batch gradient descent,批梯度下降。这种方法缺点很明显,计算量大,吃内存,不适用于在线学习。
2.每输入一个数据就计算一次损失函数,进而求梯度并更新参数,称为随机梯度下降,stochastic gradient descent,这种方法速度较快,但由于迭代一次仅依靠单一样本,很容易在相邻的两次迭代计算中的产生梯度差距非常大,甚至方向相反,因此容易不收敛。
3.另一种就是将上述两种方法的折中,称为mini-batch gradient decent,将数据集分成一定数量的批,梯度不容易跑偏容易收敛,同时减少了一次处理的数据数量,因而计算量也小了很多,速度较快。
批量梯度下降法(BGD, Batch Gradient Descent)
随机梯度下降法(SGD, Stochastic Gradient Descent)
小批量梯度下降法(Mini-batch Gradient Descent)
优点 | 缺点 |
---|---|
1.相对于海量数据的大内存,Mini-batch只需要小内存 | 1.批次越小,梯度的估值不准确 |
2.速度更快 | 2.批次越小,越不容易收敛 |
3. 选取局部的梯度方向,较之全局的学习率简单 | 3.所有参数都是用同样的learning rate |
当数据集较大时,并不是适合一次将所有样本批量输入进行训练,而应该使用一个合适的数量来训练,这个合适的数量又该如何选择?
batch_size如何选择?
在合理范围内增大batch_size:
1.能提高内存使用率。
2.减少Epoch迭代次数,提高训练速度。
3.一定范围内batch_size越大,确定的梯度下降方向越准确。
盲目增大batch_size:
1.内存不足。
2.Epoch迭代次数过少,要想达到相同的精度,时间花费增加,调参整体进度下降。
3.batch_size大到一定程度,梯度方向基本不改变。
总结:在硬件配置允许下,从小(也不能过小)增大batch_size,直至一个比较合适的数量,有人指出,将batch_size设置成2的次方倍能加快速度(未经考证)。
Epoch:使用训练集的全部数据对模型进行一次完整训练,也称为“一代训练”
Batch:使用数据集中的一小部分样本对模型权重进行一次方向传播的参数更新,这一小部分样本也称为“一批数据”;
Iteration:使用一个Batch数据对模型进行一次参数更新的过程,称之为“一次训练”;
梯度下降法是最基本的一类优化器,目前主要分为三种梯度下降法:标准梯度下降法(GD, Gradient Descent),随机梯度下降法(SGD, Stochastic Gradient Descent)及批量梯度下降法(BGD, Batch Gradient Descent)。
假设要学习训练的模型参数为W,代价函数为J(W),则代价函数关于模型参数的偏导数即相关梯度为ΔJ(W),学习率为ηt,则使用梯度下降法更新参数为:
W t + 1 = W t − η t Δ J ( W t ) Wt+1=Wt−ηtΔJ(Wt) Wt+1=Wt−ηtΔJ(Wt)
其中,Wt表示t时刻的模型参数。
从表达式来看,模型参数的更新调整,与代价函数关于模型参数的梯度有关,即沿着梯度的方向不断减小模型参数,从而最小化代价函数。
基本策略可以理解为”在当前视野寻找最快路径下山“,因此每走一步,参考当前位置最陡的方向(即梯度)进而迈出下一步。可以形象的表示为:
动量优化方法引入物理学中的动量思想,加速梯度下降,一般有标准动量优化方法Momentum
、 NAG(Nesterov accelerated gradient)
动量优化方法;当我们将一个小球从山上滚下来,没有阻力时,它的动量会越来越大,但是如果遇到了阻力,速度就会变小,动量优化法就是借鉴此思想,使得梯度方向在不变的维度上,参数更新变快,梯度有所改变时,更新参数变慢,这样就能够加快收敛并且减少动荡。
自适应学习率优化算法针对于机器学习模型的学习率,传统的优化算法要么将学习率设置为常数要么根据训练次数调节学习率。极大忽视了学习率其他变化的可能性。然而,学习率对模型的性能有着显著的影响,因此需要采取一些策略来想办法更新学习率,从而提高训练速度。
目前的自适应学习率优化算法主要有:AdaGrad算法,RMSProp算法,Adam算法以及AdaDelta算法。
思想:
算法描述:
缺点:
均方误差(MSE) 是最常用的回归损失函数,计算方法是求预测值与真实值之间距离的平方和,公式如下图所示:
公式:
M S E = ∑ i = 1 n ( y i − y i p ) 2 n MSE = \frac{\sum_{i=1}^n (y_i-y_i^p)^2}{n} MSE=n∑i=1n(yi−yip)2
L 2 损 失 函 数 = M S E = ∑ i = 1 n ( y i − y i p ) 2 L_2损失函数=MSE = \sum_{i=1}^n (y_i-y_i^p)^2 L2损失函数=MSE=i=1∑n(yi−yip)2
代码实现
#MSE function
def mse(true, pred):
"""
true: array of true values
pred: array of predicted values
returns: mean square error loss
"""
return np.sum((true - pred) ** 2)
fig, ax1 = plt.subplots(1,1, figsize = (7,5))
# array of same target value 10000 times
target = np.repeat(100, 10000)
pred = np.arange(-10000,10000, 2)
loss_mse = [mse(target[i], pred[i]) for i in range(len(pred))]
# plot
ax1.plot(pred, loss_mse)
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Loss')
ax1.set_title("MSE Loss vs. Predictions")
# show
fig.tight_layout()
plt.show()
下图是MSE函数的图像,其中目标值是100,预测值的范围从-10000到10000,Y轴代表的MSE取值范围是从0到正无穷,并且在预测值为100处达到最小。
优点 | 缺点 |
---|---|
各点都连续光滑,方便求导,具有较为稳定的解 | 鲁棒性低,当函数的输入值距离中心值较远的时候,使用梯度下降法求解的时候梯度很大,可能导致梯度爆炸 |
备注
均方根误差,它是观测值与真值偏差的平方和观测次数n比值的平方根,在实际测量中,观测次数n总是有限的,真值只能用最可信赖(最佳)值来代替.均方误差对一组测量中的特大或特小误差反映非常敏感,所以,均方根误差能够很好地反映出测量的精密度。均方根误差,当对某一量进行甚多次的测量时,取这一测量列真误差的均方根差(真误差平方的算术平均值再开方),称为标准偏差,以σ表示。σ反映了测量数据偏离真实值的程度,σ越小,表示测量精度越高,因此可用σ作为评定这一测量过程精度的标准。
公式:
M S E = ∑ i = 1 n ( y i − y i p ) 2 MSE = \sqrt{\sum_{i=1}^n (y_i-y_i^p)^2} MSE=i=1∑n(yi−yip)2
M S E = ∑ i = 1 n ( y i − y i p ) 2 n MSE = \sqrt\frac{\sum_{i=1}^n (y_i-y_i^p)^2}{n} MSE=n∑i=1n(yi−yip)2
平均绝对误差(MAE)是另一种用于回归模型的损失函数。MAE是目标值和预测值之差的绝对值之和,其只衡量了预测值误差的平均模长,而不考虑方向,取值范围也是从0到正无穷(如果考虑方向,则是残差/误差的总和——平均偏差(MBE))。
公式:
R M S E = ∑ i = 1 n ∣ y i − y i p ∣ n RMSE = \frac{ \sum_{i=1}^n |y_i-y_i^p|}{n} RMSE=n∑i=1n∣yi−yip∣
R M S E = ∑ i = 1 n ∣ y i − y i p ∣ RMSE = \sum_{i=1}^n |y_i-y_i^p| RMSE=i=1∑n∣yi−yip∣
代码实现
#MAE function
def mae(true, pred):
"""
true: array of true values
pred: array of predicted values
returns: mean absolute error loss
"""
return np.sum(np.abs(true - pred))
fig, ax1 = plt.subplots(1,1, figsize = (7,5))
# array of same target value 10000 times
target = np.repeat(100, 10000)
pred = np.arange(-10000,10000, 2)
loss_mae = [mae(target[i], pred[i]) for i in range(len(pred))]
# plot
ax1.plot(pred, loss_mae)
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Loss')
ax1.set_title("MAE Loss vs. Predictions")
# show
fig.tight_layout()
plt.show()
优点 | 缺点 |
---|---|
无论对于什么样的输入值,都有着稳定的梯度,不会导致梯度爆炸问题,具有较为稳健性的解 | 在中心点是折点,不能求导,不方便求解 |
类别 | L1损失函数 | L2损失函数 |
---|---|---|
鲁棒性 | 强 | 中 |
稳定性 | 不稳定解 | 稳定解 |
鞍点 | 可能多个解 | 总是一个解 |
解释:
鲁棒性:与均方误差相比,平均绝对误差方法的鲁棒性更好,因此,它在许多场合都有应用。平均绝对误差方法之所以是鲁棒的,是因为它能处理数据中的异常值,这或许在那些异常值可能被安全地和有效地忽略的研究中很有用,如果需要考虑任一或全部的异常值,那么平均绝对误差方法是更好的选择。
从直观上说,因为L2范数将误差平方化(如果误差大于1,则误差会放大很多),模型的误差会比L1范数来得大(e.equal(e^2)),因此模型会对这个样本(异常值
)更加敏感,这就需要调整模型来最小化误差,如果这个样本是一个异常值,模型就需要调整以适应单个的异常值,这会牺牲许多其它正常的样本,因为这些正常样本的误差比这单个的异常值的误差小。
稳定性:平均绝对误差方法的不稳定性意味着,对于数据集的一个小的水平方向的波动,回归线也许会跳跃很大。在一些数据结构(data configurations)上,该方法有许多连续解;但是,对数据集的一个微小移动,就会跳过某个数据结构在一定区域内的许多连续解。(The method has continuous solutions for some data configurations; however, by moving a datum a small amount, one could “jump past” a configuration which has multiple solutions that span a region. )在跳过这个区域内的解后,平均绝对误差偏差线可能会比之前的线有更大的倾斜。相反地,均方误差方法的解是稳定的,因为对于一个数据点的任何微小波动,回归线总是只会发生轻微移动;也就说,回归参数是数据集的连续函数。
总而言之,处理异常点时,L1损失函数更稳定,但它的导数不连续,因此求解效率较低。L2损失函数对异常点更敏感,但通过令其导数为0,可以得到更稳定的封闭解。
Huber损失对数据中的异常点没有平方误差损失那么敏感,它在0也可微分。本质上,Huber损失是绝对误差,只是在误差很小时,就变为平方误差,误差降到多小时变为二次误差由超参数δ(delta)来控制。当Huber损失在[0-δ,0+δ]之间时,等价为MSE,而在[-∞,δ]和[δ,+∞]时为MAE。
公式:
L δ ( y , f ( x ) ) = { 1 2 ( y − f ( x ) ) 2 , f o r ∣ y − f ( x ) ∣ ≤ δ δ ∣ y − f ( x ) ∣ − 1 2 δ 2 , o t h e r w i s e . L_\delta(y,f(x))= \begin{cases} \frac{1}{2}(y-f(x))^2,for|y-f(x)|\leq\delta \\ \delta|y-f(x)|-\frac{1}{2}\delta^2,otherwise. \end{cases} Lδ(y,f(x))={21(y−f(x))2,for∣y−f(x)∣≤δδ∣y−f(x)∣−21δ2,otherwise.
代码实现
#Hubei损失函数
def sm_mae(true, pred, delta):
"""
true: array of true values
pred: array of predicted values
returns: smoothed mean absolute error loss
"""
loss = np.where(np.abs(true - pred) < delta, 0.5 * ((true - pred) ** 2),
delta * np.abs(true - pred) - 0.5 * (delta ** 2))
return np.sum(loss)
fig, ax1 = plt.subplots(1,1, figsize = (7,5))
target = np.repeat(0, 1000)
pred = np.arange(-10,10, 0.02)
delta = [0.1, 1, 10]
losses_huber = [[sm_mae(target[i], pred[i], q) for i in range(len(pred))] for q in delta]
# plot
for i in range(len(delta)):
ax1.plot(pred, losses_huber[i], label = delta[i])
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Loss')
ax1.set_title("Huber Loss/ Smooth MAE Loss vs. Predicted values (Color: Deltas)")
ax1.legend()
ax1.set_ylim(bottom=-1, top = 15)
# show
fig.tight_layout()
plt.show()
备注:
这里超参数delta的选择非常重要,因为这决定了你对与异常点的定义。当残差大于delta,应当采用L1(对较大的异常值不那么敏感)来最小化,而残差小于超参数,则用L2来最小化。
Huber损失的评价:
使用MAE训练神经网络最大的一个问题就是不变的大梯度,这可能导致在使用梯度下降快要结束时,错过了最小点。而对于MSE,梯度会随着损失的减小而减小,使结果更加精确。
优点 | 缺点 |
---|---|
会由于梯度的减小而落在最小值附近,比起 M S E MSE MSE,它对异常点更加鲁棒 | 需要不断调整超参数 δ \delta δ(选择合适的 δ \delta δ) |
总结:
从上面可以看出,Huber函数实际上就是一个分段函数,在 [ ≤ δ ] [\leq\delta] [≤δ]之间实际上就是L2损失,这样解决了L1的不光滑问题,在区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题。
Log-Cosh损失(对数双曲余弦),是一种比L2更为平滑
的损失函数,利用双曲余弦来计算预测误差。
公式:
L ( y , y p ) = ∑ i = 1 n log ( c o s h ( y i p − y i ) ) L(y,y^p)=\sum_{i=1}^n\log(cosh(y_i^p-y_i)) L(y,yp)=i=1∑nlog(cosh(yip−yi))
代码实现
#logcosh损失
def logcosh(true, pred):
loss = np.log(np.cosh(pred - true))
return np.sum(loss)
fig, ax1 = plt.subplots(1,1, figsize = (7,5))
target = np.repeat(0, 1000)
pred = np.arange(-10,10, 0.02)
loss_logcosh = [logcosh(target[i], pred[i]) for i in range(len(pred))]
# plot
ax1.plot(pred, loss_logcosh)
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Loss')
ax1.set_title("Log-Cosh Loss vs. Predictions")
# show
fig.tight_layout()
plt.show()
Log-cosh损失的评价
优点 | 缺点 |
---|---|
对于较小的 x x x, log ( c o s h ( y i p − y i ) ) \log(cosh(y_i^p-y_i)) log(cosh(yip−yi))近似等于 x 2 2 \frac{x^2}{2} 2x2,对于较大的 x x x,近似等于 a b s ( x ) − log ( 2 ) abs(x)-\log(2) abs(x)−log(2)。这意味着Log-cosh基本类似于均方误差,但不易受到异常点的影响,它具有Huber损失所有的优点,但不同于Huber损失的是,Log-cosh二阶处处可微。 | 误差很大的时,一阶梯度和Hessian会变成定值 |
在大多数现实世界预测问题中,我们通常希望了解预测中的不确定性,即清楚预测的范围区间而非仅是估计的点,对许多商业问题的决策很有帮助。
分位数损失函数在我们关注区间预测时,能派上很大用场。大致是,使用最小二乘回归进行区间预测,基于的假设是残差 ( y − y ^ ) (y-\hat y) (y−y^)是独立变量,且方差保持不变。
一旦违背了这条假设,那么线性回归模型就不成立,但是我们也不能因此就认为使用非线性函数或基于树的模型更好,而放弃将线性回归模型作为基线方法。这时,分位数损失和分位数回归就派上用场了,因为即便对于具有变化方差或非正态分布的残差,基于分位数损失的回归也能给出合理的预测区间。
公式:
L γ ( y , y p ) = ∑ i : y i < y i p ( 1 − γ ) ∣ y i − y i p ∣ + ∑ i : y i ≥ y i p γ ∣ y i − y i p ∣ L_\gamma(y,y^p)=\sum_{i:y_i
代码实现
y = np.arange(1,25, 0.25)
# linear relationship with contant variance of residual
x1 = y.copy() + np.random.randn(96)
# non contant variance with residuals
x2 = y.copy()
y2 = x2 + np.concatenate((np.random.randn(20)*0.5,
np.random.randn(20)*1,
np.random.randn(20)*4,
np.random.randn(20)*6,
np.random.randn(16)*8), axis=0)
fig, (ax1,ax2) = plt.subplots(1,2 , figsize = (12,5.5) )
#具有恒定的残差方差
ax1.plot(x1, y, 'o')
ax1.set_xlabel('X1')
ax1.set_ylabel('Y')
ax1.set_title('Contant variance of residuals')
#异方差
ax2.plot(x2, y2, 'o')
ax2.set_xlabel('X2')
ax2.set_ylabel('Y')
ax2.set_title('Non contant variance of residuals')
# show
fig.tight_layout()
plt.show()
#用最小二乘进行评估
lr = LinearRegression()
lr.fit(x1.reshape(-1,1),y.reshape(-1,1))
lr2 = LinearRegression()
lr2.fit(x2.reshape(-1,1),y.reshape(-1,1))
fig, (ax1,ax2) = plt.subplots(1,2 , figsize = (12,5.5) )
ax1.plot(x1, y, 'o')
ax1.set_xlabel('X1')
ax1.set_ylabel('Y')
ax1.set_title('Contant variance of residuals')
ax1.plot(x1, lr.predict(x1.reshape(-1,1)))
ax2.plot(x2, y2, 'o')
ax2.set_xlabel('X2')
ax2.set_ylabel('Y')
ax2.set_title('Non contant variance of residuals')
ax2.plot(x2, lr2.predict(x2.reshape(-1,1)))
fig.tight_layout()
plt.show()
上图是两种不同的数据分布,其中左图是残差的方差为常数的情况,而右图则是残差的方差变化的情况。我们利用正常的最小二乘对上述两种情况进行了估计,其中橙色线为建模的结果,但是我们却无法得到取值的区间范围,这时候就需要分位数损失函数来提供。
data = pd.DataFrame(data={'X': x2, 'Y':y2})
mod = smf.quantreg('Y ~ X', data)
res = mod.fit(q=.5)
quantiles = np.array((0.05, 0.95))
def fit_model(q):
res = mod.fit(q=q)
return [q, res.params['Intercept'], res.params['X']] + \
res.conf_int().loc['X'].tolist()
models = [fit_model(x) for x in quantiles]
models = pd.DataFrame(models, columns=['q', 'a', 'b', 'lb', 'ub'])
ols = smf.ols('Y ~ X', data).fit()
ols_ci = ols.conf_int().loc['X'].tolist()
ols = dict(a=ols.params['Intercept'],
b=ols.params['X'],
lb=ols_ci[0],
ub=ols_ci[1])
xn = np.arange(data.X.min(), data.X.max(), 2)
get_y = lambda a, b: a + b * xn
fig, ax = plt.subplots(figsize=(8, 6))
for i in range(models.shape[0]):
yn = get_y(models.a[i], models.b[i])
ax.plot(xn, yn, linestyle='dotted', color='grey')
yn = get_y(ols['a'], ols['b'])
ax.plot(xn, yn, color='red', label='OLS')
ax.scatter(data.X, data.Y, alpha=.2)
legend = ax.legend()
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Quantile regression with 0.05 and 0.95 quantiles')
fig.tight_layout()
plt.show()
上图中上下两条虚线基于0.05和0.95的分位数损失得到的取值区间,从图中可以清晰地看到建模后预测值得取值范围。分位数回归的目标在于估计给定预测值的条件分位数。实际上分位数回归就是平均绝对误差的一种拓展(当分位数为第50个百分位时其值就是平均绝对误差)。
#Quantile损失
def quan(true, pred, theta):
loss = np.where(true >= pred, theta*(np.abs(true-pred)), (1-theta)*(np.abs(true-pred)))
return np.sum(loss)
fig, ax1 = plt.subplots(1,1, figsize = (7,5))
target = np.repeat(0, 1000)
pred = np.arange(-10,10, 0.02)
quantiles = [0.25, 0.5, 0.75]
losses_quan = [[quan(target[i], pred[i], q) for i in range(len(pred))] for q in quantiles]
# plot
for i in range(len(quantiles)):
ax1.plot(pred, losses_quan[i], label = quantiles[i])
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Quantile Loss')
ax1.set_title("Loss with Predicted values (Color: Quantiles)")
ax1.legend()
# show
fig.tight_layout()
plt.show()
通过函数曲线,来对比各个回归损失函数。
代码实现
fig, ax1 = plt.subplots(1,1, figsize = (10,6.5))
target = np.repeat(0, 1000)
pred = np.arange(-10,10, 0.02)
# calculating loss function for all predictions.
loss_mse = [mse(target[i], pred[i]) for i in range(len(pred))]
loss_mae = [mae(target[i], pred[i]) for i in range(len(pred))]
loss_sm_mae1 = [sm_mae(target[i], pred[i], 5) for i in range(len(pred))]
loss_sm_mae2 = [sm_mae(target[i], pred[i], 10) for i in range(len(pred))]
loss_logcosh = [logcosh(target[i], pred[i]) for i in range(len(pred))]
loss_quan1 = [quan(target[i], pred[i], 0.25) for i in range(len(pred))]
losses = [loss_mse, loss_mae, loss_sm_mae1, loss_sm_mae2, loss_logcosh, loss_quan1]
names = ['MSE', 'MAE','Huber (5)', 'Huber (10)', 'Log-cosh', 'Quantile (0.25)']
cmap = ['#d53e4f',
'#fc8d59',
'#fee08b',
'#e6f598',
'#99d594',
'#3288bd']
for lo in range(len(losses)):
ax1.plot(pred, losses[lo], label = names[lo], color= cmap[lo])
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Loss')
ax1.set_title("Loss with Predicted values")
ax1.legend()
ax1.set_ylim(bottom=0, top=40)
# fig.savefig('/Users/princegrover/Documents/msan/Machine-Learning/images/all_regression.png' )
交叉熵(Cross Entropy)是Shannon信息论中一个重要概念,主要用于度量两个概率分布间的差异性信息。
交叉熵可在神经网络(机器学习)中作为损失函数,p表示真实标记的分布,q则为训练后的模型的预测标记分布,交叉熵损失函数可以衡量p与q的相似性。交叉熵作为损失函数还有一个好处是使用sigmoid函数在梯度下降时能避免均方误差损失函数学习速率降低的问题,因为学习速率可以被输出的误差所控制。
在特征工程中,可以用来衡量两个随机变量之间的相似度。
在语言模型中(NLP)中,由于真实的分布p是未知的,在语言模型中,模型是通过训练集得到的,交叉熵就是衡量这个模型在测试集上的正确率。
给定两个概率分布p和q,通过q来表示p的交叉熵为:
H ( p , q ) = − ∑ x p ( x ) log q ( x ) H(p,q) = -\sum_xp(x)\log q(x) H(p,q)=−x∑p(x)logq(x)
注意,交叉熵刻画的是两个概率分布之间的距离,或可以说它刻画的是通过概率分布q来表达概率分布p的困难程度,p代表正确答案,q代表的是预测值,交叉熵越小,两个概率的分布约接近。
假设原始的神经网络的输出为 y 1 , y 2 , y 3 . . . y n y_1,y_2,y_3...y_n y1,y2,y3...yn,那么经过Softmax回归处理之后的输出为:
S o f t m a x ( y i ) = e y i ∑ j = 1 n e y j Softmax(y_i)=\frac{e^{y_i}}{\sum_{j=1}^ne^{y_j}} Softmax(yi)=∑j=1neyjeyi
经常会用Softmax回归把神经网络的输出变成一个概率分布,进而通过交叉熵来计算预测的概率分布和真实答案的概率分布之间的距离。
公式:
l o s s = − ∑ i = 1 n y ^ i log y i + ( 1 − y ^ i ) log ( 1 − y ^ i ) loss = -\sum_{i=1}^n{\hat y_i}{\log y_i}+(1-{\hat y_i}){\log {(1-{\hat y_i})} } loss=−i=1∑ny^ilogyi+(1−y^i)log(1−y^i)
代码实现:
def bin_ce(true, pred):
"""
true: array of true values
pred: array of predicted values
returns: binary cross entropy loss
"""
loss = np.where(true == 1, np.log(pred), np.log(1 - pred))
return -np.sum(loss)
fig, ax1 = plt.subplots(1,1)
# array of same target value 10000 times
target = np.repeat(1, 10000) # considering prediction to be 1
pred = np.arange(0,1, 0.0001) # all predictions b/w 0 and 1 for 10k values
# calculating loss function for all predictions.
loss_bin_ce = [bin_ce(target[i], pred[i]) for i in range(len(pred))]
# plot for binary cross entropy
ax1.plot(pred, loss_bin_ce)
ax1.set_xlabel('Predictions')
ax1.set_ylabel('Binary Cross Entropy Loss/ Log Loss')
ax1.set_title("Loss with Predicted values")
fig.tight_layout()
plt.show()
图像
结论
Binary cross entropy是针对概率之间的损失函数, 只有在 y i y_i yi和 y ^ i \hat y_i y^i是相等时,loss才会为0,否则loss就为一个正数,而且,概率相差越大,loss就越大。
卷积:在泛函分析中,卷积、旋积或摺积(英语:Convolution)是通过两个函数 f f f和 g g g生成第三个函数的一种数学算子,表征函数 f f f与 g g g经过翻转和平移的重叠部分的面积。
数学定义:
设: f ( x ) f(x) f(x), g ( x ) g(x) g(x)是 R 1 R1 R1上的两个可积函数,作积分:
∫ − ∞ ∞ f ( τ ) g ( x − τ ) d τ {\displaystyle \int}_{-\infty}^{\infty}f(\tau)g(x-\tau)d\tau ∫−∞∞f(τ)g(x−τ)dτ
直观过程:
卷积核(kernel)重复这个过程知道遍历了整张图片,将一个二维矩阵转换为另一个二维矩阵。输出特征实质上是在输入数据相同位置上的加权和(权值是卷积核本身的值)
运算过程:
上图,从5×5的输入层矩阵X,与kernel卷积核卷积运算,得到一个3×3的输出层矩阵Y。
其中以输出层第(0,0)个元素“12.0”为例,运算过程为:
Y ( 0 , 0 ) = X 0 , 0 × Y 0 , 0 + X 0 , 1 × Y 0 , 1 + X 0 , 2 × Y 0 , 2 + X 1 , 0 × Y 1 , 0 + X 1 , 1 × Y 1 , 1 + X 1 , 2 × Y 1 , 2 + X 2 , 0 × Y 2 , 0 + X 2 , 1 × Y 2 , 1 + X 2 , 2 × Y 2 , 2 = 3 × 1 + 2 × 2 + 1 × 1 + 2 × 2 = 12 \begin{aligned} Y_{(0,0)} & = X_{0,0}×Y_{0,0}+X_{0,1}×Y_{0,1}+X_{0,2}×Y_{0,2}\\ & +X_{1,0}×Y_{1,0}+X_{1,1}×Y_{1,1}+X_{1,2}×Y_{1,2} \\ & +X_{2,0}×Y_{2,0}+X_{2,1}×Y_{2,1}+X_{2,2}×Y_{2,2} \\ & = 3×1+2×2+1×1+2×2 =12\\ \end{aligned} Y(0,0)=X0,0×Y0,0+X0,1×Y0,1+X0,2×Y0,2+X1,0×Y1,0+X1,1×Y1,1+X1,2×Y1,2+X2,0×Y2,0+X2,1×Y2,1+X2,2×Y2,2=3×1+2×2+1×1+2×2=12
卷积的应用:
用Kernel与图像输入进行卷积,对于图像上的每一个像素点,Kernel与之一一对应,然后把Kernel上的点和图像上对应的点相乘,再把各点的乘积相加,进而得到该点的卷积值。
卷积是一种积分运算,用来求两个曲线重叠区域面积。可以看作加权求和,可以用来消除噪声、特征增强、图像滤波。
在卷积过程中,有这么一个的问题:卷积后所得的结果矩阵,尺寸会缩小。
如上图所示,从5×5的输入层矩阵X,与kernel卷积核卷积运算,得到一个3×3的输出层矩阵Y,输出尺寸较之输入减少。
而在实际应用中,我们并不想丢失任何有价值的数据,这时,Padding操作,能给我们一些帮助。
Padding:用额外的「假」像素(通常值为 0,因此经常使用的术语「零填充」)填充边缘。这样,在滑动时的卷积核可以允许原始边缘像素位于其中心,同时延伸到边缘之外的假像素,从而产生与输入相同大小的输出。
当然,当我们需要,将输入层尺寸,缩小为我们希望的尺寸时,我们也可以应用设置参数Striding(步长),来实现。
Stride:是改变卷积核的移动步长跳过一些像素。Stride 是 1 表示卷积核滑过每一个相距是 1 的像素,是最基本的单步滑动,作为标准卷积模式。Stride 是 2 表示卷积核的移动步长是 2,跳过相邻像素,图像缩小为原来的 1/2。Stride 是 3 表示卷积核的移动步长是 3,跳过 2 个相邻像素,图像缩小为原来的 1/3。
在应用到一张RGB格式的图像输入,卷积操作,通常会每个通道独立进行,再将结果重叠。
滤波器的每个卷积核在各自的输入通道上「滑动」,产生各自的计算结果。一些内核可能比其他内核具有更大的权重,以便比某些内核更强调某些输入通道(例如,滤波器的红色通道卷积核可能比其他通道的卷积核有更大的权重,因此,对红色通道特征的反应要强于其他通道)。
然后将每个通道处理的结果汇在一起形成一个通道。滤波器的卷积核各自产生一个对应通道的输出,最后整个滤波器产生一个总的输出通道。
也可以通过对每个输出滤波器增加偏置项来实现耦合。
在TensorFlow中,对于输出层尺寸大小的设置,可以通过两个参数Valid和Same来实现。
Padding = ‘VALID’ 时,没有填充,因此输出的size总比原图的size小。
如图,3×3的kernel作用在7×7的图像上,当步长stride为1时:
由于没有填充,最终得到的就是虚线中的灰色区域即5×5大小。
Padding = ‘SAME’ 时,用0填充边界.能覆盖原图所有像素,不会舍弃边上的元素;当步长stride为1时,输出和原图size一致。
如图,3×3的kernel作用在7×7的图像上,当步长stride为1时:
由于有0填充边界,最终得到的就是虚线中的灰色区域即7×7大小。
5×5的kernel作用在7×7的图像上,步长stride为1时:
计算公式
什么是池化?
答:也称下采样层,会压缩输入的特征图,一方面减少了特征,参数减少,简化计算 复杂度;另一方面在某种意义上(旋转、平移、伸缩等)保持不变性。
有几种池化操作?
1.平均池化(区域内求平均值)2.最大池化(区域内取最大值)
具体的作用?
1.特征不变形:池化操作是模型更加关注是否存在某些特征而不是特征具体的位置。
2.特征降维:池化相当于在空间范围内做了维度约减,从而使模型可以抽取更加广范围的特征。同时减小了下一层的输入大小,进而减少计算量和参数个数。
3.在一定程度上防止过拟合,更方便优化。
4.扩大感受野。
注意项:
下采样和池化应该是包含关系,池化属于下采样,而下采样不局限于池化,如果卷积 stride=2,此时也可以把这种卷积叫做下采样。
什么是上采样?
答:池化(下采样层)更像是获取缩略图,反之,上采样更像是放大图像。
有哪些常用方法?
1.双线性插值
2.转置卷积(反卷积)
3.反池化(unpooling)
什么是空洞卷积?
什么是反卷积?
答:反卷积是一种特殊的正向卷积,先按照一定的比例通过补0来扩大输入图像的尺寸,接着旋转卷积核,再进行正向卷积。
一.为什么会出现梯度消失或梯度爆炸?
梯度消失和梯度爆炸本质上是一样的,都是因为网络层数太深而引发的梯度反向传播中的连乘效应。
二.解决方法有哪些?
一.什么是Batch Normalization?
对于每个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到均值为0方差为1的比较标准的正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。
附上一个讲解得较为清晰的博客链接
二.如何使用BN?
三.为什么用Group Normalization?什么是Group Normalization?
BN存在的问题:BN全名是Batch Normalization,是以batch的维度做归一化,过小的batch size会导致其性能下降,一般来说每GPU上batch设为32最合适;但是对于一些其他深度学习任务batch size往往只有1-2,比如目标检测,图像分割,视频分类上,输入的图像数据很大,较大的batch size性能下降,误差增大。
Group Normalization(GN)是一种新的深度学习归一化方式,可以替代BN。
四.什么是LN、IN?
深度网络中的数据维度一般是[N, C, H, W]或者[N, H, W,C]格式,N是batch size,H/W是feature的高/宽,C是feature的channel,压缩H/W至一个维度,其三维的表示如上图,假设单个方格的长度是1,那么其表示的是[6, 6,*, * ]
注:事实上,GN的极端情况就是LN和IN,分别对应G等于C和G等于1,作者在论文中给出G设为32较好。
补充知识点:
什么是LRN?
LRN全称为Local Response Normalization,即局部响应归一化层,LRN函数类似DROPOUT和数据增强作为relu激励之后防止数据过拟合而提出的一种处理方法。这个函数很少使用,基本上被类似DROPOUT这样的方法取代,见最早的出处AlexNet论文对它的定义, 《ImageNet Classification with Deep ConvolutionalNeural Networks》
什么是滑动平均?
滑动平均(exponential moving average),或者叫做指数加权平均(exponentially weighted moving average),可以用来估计变量的局部均值,使得变量的更新与一段时间内的历史取值有关。
为什么使用滑动平均?
References:理解滑动平均
1.什么是"感受野"?
2.全连接层的定义是什么?
3.为什么输入数据需要归一化(Normalized Data)?
归一化后有什么好处呢?原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。
对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。
4.什么是GAP?
GAP,全称Global Average Pooling,概念出自于 Network In Network,主要是用来解决全连接的问题,其主要是是将最后一层的特征图进行整张图的一个均值池化,形成一个特征点,将这些特征点组成最后的特征向量进行softmax中进行计算。
参考:VGG详解
为什么用Inception Module?什么是Inception Module?
GoogLeNet是2014年的分类比赛冠军,网络达到了22层,参数量为5M,核心就是Inception模块,这次的版本通常称其为Inception V1。
问题:
Inception Module的架构图如下所示:
Inception Module基本组成结构有四个成分。1X1卷积,3X3卷积,5X5卷积,3X3最大池化。最后对四个成分运算结果进行通道上组合,这就是Inception Module的核心思想。通过多个卷积核提取图像不同尺度的信息,最后进行融合,可以得到图像更好的表征。
在Inception结构中,大量采用了1x1的矩阵,主要是两点作用:1)对数据进行降维;2)引入更多的非线性,提高泛化能力,因为卷积后要经过ReLU激活函数。
GoogLeNet 有9个线性堆叠的Inception模块。它有 22 层(包括池化层的话是 27 层)。该模型在最后一个 inception 模块处使用全局平均池化。
为了阻止该网络中间部分梯度的「消失」过程,作者引入了两个辅助分类器。它们对其中两个 Inception 模块的输出执行 softmax 操作,然后在同样的标签上计算辅助损失。总损失即辅助损失和真实损失的加权和。
References:一文概览Inception家族的「奋斗史」
1*1卷积核的作用有哪些?
Inception V2和Inception V3如何改进?
Inception V2解决的问题:
解决方案:
Inception V3解决的问题:
解决方案:
Inception Net v3 整合了前面 Inception v2 中提到的所有升级,还使用了:
References:一文概览Inception家族的「奋斗史」
什么是残差?
更准确地,假设我们想要找一个 ,使得 ()=,给定一个 的估计值 0,残差(residual)就是 −(0),同时,误差就是 −0。
即使 不知道,我们仍然可以计算残差,只是不能计算误差。
为什么需要残差网络(residual结构)?
什么是残差网络(Residual Networks,ResNets)?
References:残差网络(Residual Network)
为什么有DenseNet?
DenseNet提出了一个更激进的密集连接机制:即互相连接所有的层,具体来说就是每个层都会接受其前面所有层作为其额外的输入。DenseNet中,每个层都会与前面所有层在channel维度上连接(concat)在一起(这里各个层的特征图大小是相同的,后面会有说明),并作为下一层的输入。对于一个L层的网络,DenseNet共包含L(L+1)/2个连接,相比ResNet,这是一种密集连接。而且DenseNet是直接concat来自不同层的特征图,这可以实现特征重用,提升效率,这一特点是DenseNet与ResNet最主要的区别。
DenseNet网络结构是怎样的?
CNN网络一般要经过Pooling或者stride>1的Conv来降低特征图的大小,而DenseNet的密集连接方式需要特征图大小保持一致。为了解决这个问题,DenseNet网络中使用DenseBlock+Transition的结构,其中DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。而Transition模块是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。下图给出了DenseNet的网路结构,它共包含4个DenseBlock,各个DenseBlock之间通过Transition连接在一起。
优势有哪些?
DenseNet的优势主要体现在以下几个方面:
References:DenseNet:比ResNet更优的CNN模型
什么是SeNet?
Squeeze-and-Excitation Networks(简称 SENet),采用了一种全新的「特征重标定」策略,显式地建模特征通道之间的相互依赖关系,具体来说,就是通过学习的方式来自动获取到每个特征通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征。
网络结构是怎样的?
左边为 C’×H’×W’ 的特征图,经过一系列卷积,pooling 操作 Ftr 之后,得到 C×H×W 大小的特征图。接下来进行一个 Sequeeze and Excitation block。
Sequeeze:对 C×H×W 进行 global average pooling,得到 1×1×C 大小的特征图,这个特征图可以理解为具有全局感受野。
Excitation :这里的Excitation用的是 全连接层->Relu激活->全连接层,第一个全连接层将特征维度降低到原来的1/r,第二个全连接层将维度升回原来的大小。这里使用两个而不是一个全连接层的原因是:
(1) 增加更多的非线性,可以更好地拟合通道间复杂的相关性
(2) 尽可能减少参数量和计算量
特征重标定:使用 Excitation 得到的结果作为权重,乘到输入特征上。
参考:轻量级模块SENet与SKNet详解
参考:后ResNet时代:SENet与SKNet