羊了,但是依旧生龙活虎。补补之前落下的SGD算法,这个在深度学习中应用广泛。
在梯度之前,非常重要一个概念:方向导数,这里 u u u是 n n n维向量,代表一个方向,通过极限的方式定义函数 f f f在方向 u u u上的导数,或者说增长率:
当 u u u是标准(正交)单位向量 i i i时(坐标轴方向),方向导数退化为偏导数: ∇ u f ( x ) = f i ′ ( x ) = ∂ f ( x ) ∂ x i \nabla_uf(x)=f_i'(x)=\frac{\partial f(x)}{\partial x_i} ∇uf(x)=fi′(x)=∂xi∂f(x)
任意一个方向导数可以用单位向量的线性组合表示:
证明:
这是一个构造性证明,而且由于 f i ′ ( x ) f_i'(x) fi′(x)之间线性无关, u i u_i ui应是唯一的。中间用到的高维链式求导法则,其实一定程度上隐含了这种分解性,换而言之,利用偏导的分解性推导出更为一般的方向导数的分解性质。
梯度就是函数对所有单位向量求偏导构成的向量(方向),代表函数 f f f在定义空间 R n R^n Rn中的“增长率”。利用方向导数的定义,以及前面的定理,得 ∇ u f ( x ) = ∇ f ( x ) ⋅ u = ∣ ∣ ∇ f ( x ) ∣ ∣ ∣ u ∣ ∣ c o s α \nabla_uf(x)=\nabla f(x)\cdot u=||\nabla f(x)|||u||cos\alpha ∇uf(x)=∇f(x)⋅u=∣∣∇f(x)∣∣∣u∣∣cosα
α \alpha α是 ∇ u f ( x ) \nabla_uf(x) ∇uf(x)和 u u u的夹角。
不妨设 u u u是单位向量,化简得到方向导数和梯度之间的关系:
∇ u f ( x ) = ∣ ∣ ∇ f ( x ) ∣ ∣ c o s α \nabla_uf(x)=||\nabla f(x)||cos\alpha ∇uf(x)=∣∣∇f(x)∣∣cosα
当 u = ∇ f ( x ) u=\nabla f(x) u=∇f(x)时, α = 0 \alpha=0 α=0,得到了最大的方向导数,这意味着,梯度是函数变化最剧烈的方向,是定义在空间 R n R^n Rn中的“增长率”。
算法思想
如果多元函数 J ( θ ) J(\theta) J(θ)在点 θ \theta θ的邻域内可微,则 θ \theta θ向梯度反方向变化, J ( θ ) J(\theta) J(θ)下降得最快。可以通过梯度下降找到函数的局部最小点。
这里, θ \theta θ通常随机初始化, α \alpha α是学习率。通常的收敛准则为梯度大小小于预定义阈值(这通常意味着已经收敛到一个较平缓的的区域——极值点处)或者达到设置的最大迭代轮数。
如何计算梯度?
其实,和算偏导一样,只不过逐一对每个变量效率太低了,因而归纳一类变量的特征,进行求导,也就是向量求导、矩阵求导。
eg.线性回归函数:
学习率
每次迭代朝梯度反方向的移动步长。
学习率越大,收敛越快,但过大会导致无法收敛,可能达到目标只需要一小步,迈大步怎么也无法到达目标;
学习率过小,收敛速度过大,影响模型效率;
因此调一个合适的学习率参数很必要。
GD在训练集过于庞大时,会产生巨大的计算成本!
如较为简单的线性回归,梯度计算量和训练集大小 m m m是线性关系。巨额的计算只为移动一小步,这似乎是很不划算的,因此有了随机梯度下降。SGD中参数仅依据一个训练样本进行更新,而不是整个数据集!更新 m m m次!而随机,是指更新选择的样本顺序在每轮迭代中是随机生成。
SGD在神经网络中担任优化器的角色,The Optimizer,实际上几乎所有深度学习的优化算法属于SGD一族,遵循以下运作模式:
每轮迭代使用的训练样本称为一个批处理,batch;完整使用一遍训练集称为一个epoch,通常epoch大小代表一个sample会进入网络的次数。
SGD的两个重要参数,它们之间的相互作用是微妙的。但实际上多数情况下,我们不需要调参。Adam是一种具有自适应学习率的SGD算法,无需任何参数调优,可以“自调优”。
以上是在Kaggle平台的深度学习入门课程的第三讲部分内容(Stochastic Gradient Descent),并为深入进行讨论,下面是基于Keras的简单神经网络实现以及训练:
构建网络:
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(512, activation='relu', input_shape=[11]),
layers.Dense(512, activation='relu'),
layers.Dense(512, activation='relu'),
layers.Dense(1),
])
设置Optimizer和loss function:
model.compile(
optimizer='adam',
loss='mae',
)
训练:
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=10,
)