正则化在损失函数中引入模型复杂度指标,利用给w加权值,弱化了训练数据的噪声(一般不正则化b)
l o s s = l o s s ( y 与 y_ ) + REGULARIZER ∗ l o s s ( w ) loss = loss(\textbf{y 与 y\_}) + \textbf{REGULARIZER} * loss(w) loss=loss(y 与 y_)+REGULARIZER∗loss(w)
l o s s ( y 与 y_ ) loss(\textbf{y 与 y\_}) loss(y 与 y_):模型中所有参数的损失函数,如交叉熵,均方误差
REGULARIZER \textbf{REGULARIZER} REGULARIZER:用超参数给出参数w在总loss中的比列,即正则化的权重
loss L 1 ( w ) = ∑ i ∣ w i ∣ \operatorname{loss}_{L_{1}}(w)=\sum_{i}\left|w_{i}\right| lossL1(w)=i∑∣wi∣
loss L 2 ( w ) = ∑ i ∣ w i 2 ∣ \operatorname{loss}_{L 2}(w)=\sum_{i}\left|w_{i}^{2}\right| lossL2(w)=i∑∣∣wi2∣∣
正则化的选择:
待优化参数 w w w,损失函数 l o s s loss loss,学习率 l r lr lr,每次迭代一个batch, t t t 表示当前batch迭代的总次数:
其中:一阶动量:与梯度相关的函数,二阶动量:与梯度平方相关的函数
SGD(梯度下降):
class SGD:
def __init__(self, lr=0.01):
self.lr = lr
def update(self, params, grads):
for key in params.keys():
params[key] -= self.lr * grads[key]
SGD的缺点是,如果函数的形状非均向,比如呈延伸状,搜索的路径就会非常低效。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向。
m t = g t V t = 1 η t = l r ⋅ m t / V t = l r ⋅ g t w t + 1 = w t − η t = w t − l r ⋅ m t / V t = w t − l r ⋅ g t \begin{array}{l} m_{\mathrm{t}}=g_{\mathrm{t}} \quad V_{\mathrm{t}}=1 \\ \eta_{\mathrm{t}}=lr \cdot m_{\mathrm{t}} / \sqrt{V_{t}}= lr \cdot g_{t} \\ \begin{array}{l} w_{\mathrm{t}+1}=w_{t}-\eta_{t} =w_{t}-l r \cdot m_{t} / \sqrt{V_{t}}=w_{t}-l r \cdot g_{t} \end{array} \end{array} mt=gtVt=1ηt=lr⋅mt/Vt=lr⋅gtwt+1=wt−ηt=wt−lr⋅mt/Vt=wt−lr⋅gt
w t + 1 = w t − l r ∗ ∂ l o s s ∂ w t \mathrm{w}_{\mathrm{t}+1}=w_{t}-l r * \frac{\partial l o s s}{\partial w_{t}} wt+1=wt−lr∗∂wt∂loss
w1.assign_sub(lr * grads[0])
b1.assign_sub(lr * grads[1])
SGDM(在SGD基础上增加一阶动量)
m t = β ⋅ m t − 1 + ( 1 − β ) ⋅ g t V t = 1 η t = l r ⋅ m t / V t = l r ⋅ m t = l r ⋅ ( β ⋅ m t − 1 + ( 1 − β ) ⋅ g t ) w t + 1 = w t − η t = w t − l r ⋅ ( β ⋅ m t − 1 + ( 1 − β ) ⋅ g t ) \begin{array}{l} m_{\mathrm{t}}=\beta \cdot m_{t-1}+(1-\beta) \cdot g_{t} \quad V_{t}=1 \\ \begin{array}{l} \eta_{\mathrm{t}}= lr \cdot m_{\mathrm{t}} / \sqrt{V_{\mathrm{t}}}=l r \cdot m_{\mathrm{t}} =lr \cdot\left(\beta \cdot m_{\mathrm{t}-1}+(1-\beta) \cdot g_{\mathrm{t}}\right) \\ w_{\mathrm{t}+1}= w_{\mathrm{t}}-\eta_{\mathrm{t}} =w_{\mathrm{t}}-l r \cdot\left(\beta \cdot m_{\mathrm{t}-1}+(1-\beta) \cdot g_{\mathrm{t}}\right) \end{array} \end{array} mt=β⋅mt−1+(1−β)⋅gtVt=1ηt=lr⋅mt/Vt=lr⋅mt=lr⋅(β⋅mt−1+(1−β)⋅gt)wt+1=wt−ηt=wt−lr⋅(β⋅mt−1+(1−β)⋅gt)
β \beta β是接近于1的超参数
m_w, m_b = 0, 0
beta = 0.9
m_w = beat * m_w + (1 - beta) * grads[0]
m_b = beat * m_b + (1 - beta) * grads[1]
w1.assign_sub(lr * m_w)
b1.assign_sub(lr * m_b)
Adagrad:是借鉴 l 2 \mathcal{l}_2 l2 正则化的思想,每次迭代时自适应地调整每个参数的学习率。其在SGD基础上增加二阶动量
m t = g t V t = ∑ τ = 1 t g τ 2 η t = l r ⋅ m t / ( V t ) = l r ⋅ g t / ( ∑ τ = 1 t g τ 2 + ϵ ) w t + 1 = w t − η t = w t − l r ⋅ g t / ( ∑ τ = 1 t g τ 2 + ϵ ) \begin{array}{c} m_{\mathrm{t}}=g_{t} \quad V_{t}=\sum\limits_{\tau=1}^{t} g_{\tau}^{2} \\ \eta_{\mathrm{t}}=lr \cdot m_{t} /(\sqrt{V_{t}}) =lr \cdot g_{t} /(\sqrt{\sum_{\tau=1}^{t} g_{\tau}^{2}+\epsilon}) \\ w_{t+1}=w_t-\eta_t=w_{t}-lr \cdot g_{t} /(\sqrt{\sum_{\tau=1}^{t} g_{\tau}^{2}+\epsilon}) \end{array} mt=gtVt=τ=1∑tgτ2ηt=lr⋅mt/(Vt)=lr⋅gt/(∑τ=1tgτ2+ϵ)wt+1=wt−ηt=wt−lr⋅gt/(∑τ=1tgτ2+ϵ)
其中 ϵ \epsilon ϵ 是为了保持数值稳定性而设置的非常小的常数,一般取值 e − 7 e^{-7} e−7 到 e − 10 e^{-10} e−10 。
v_w, v_b = 0, 0
v_w += tf.square(grads[0])
v_b += tf.square(grads[1])
w.assign_sub(lr * grads[0] / tf.sqrt(v_w))
b.assign_sub(lr * grads[1] / tf.sqrt(v_b))
在 AdaGrad 算法中,如果某个参数的偏导数累积比较大,其学习率相对较小;相反,如果其偏导数累积较小,其学习率相对较大.但整体是随着迭代次数的
增加,学习率逐渐缩小。
AdaGrad 算法的缺点是在经过一定次数的迭代依然没有找到最优点时,由于这时的学习率已经非常小,很难再继续找到最优点。
RMSProp:可以在有些情况下避免 AdaGrad 算法中学习率不断单调下降以至于过早衰减的缺点。其在SGD基础上增加二阶动量
m t = g t V t = β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 η t = l r ⋅ m t / V t = l r ⋅ g t / ( β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 ) w t + 1 = w t − η t = w t − l r ⋅ g t / ( β ⋅ V t − 1 + ( 1 − β ) ⋅ g t 2 ) \begin{array}{l} m_{\mathrm{t}}=g_{t} \quad V_{t}=\beta \cdot V_{t-1}+(1-\beta) \cdot g_{t}^{2} \\ \eta_{t}=lr \cdot m_{\mathrm{t}} / \sqrt{V_{\mathrm{t}}} =l r \cdot g_{t} /(\sqrt{\beta \cdot V_{t-1}+(1-\beta) \cdot g_{t}^{2}}) \\ w_{\mathrm{t}+1}=w_{t}-\eta_{t}=w_{t}-l r \cdot g_{t} /(\sqrt{\beta \cdot V_{t-1}+(1-\beta) \cdot g_{t}^{2}}) \end{array} mt=gtVt=β⋅Vt−1+(1−β)⋅gt2ηt=lr⋅mt/Vt=lr⋅gt/(β⋅Vt−1+(1−β)⋅gt2)wt+1=wt−ηt=wt−lr⋅gt/(β⋅Vt−1+(1−β)⋅gt2)
其中 β \beta β 为衰减率,一般取值为 0.9。
从上式可以看出,RMSProp 算法和 AdaGrad 算法的区别在于 V t V_t Vt 的计算由累积方式变成了指数衰减移动平均.在迭代过程中,每个参数的学习率并不是呈
衰减趋势,既可以变小也可以变大。
v_w, v_b = 0, 0
beta = 0
v_w = beta * v_w + (1 - beta) * tf.square(grads[0])
v_b = beta * v_b + (1 - beta) * tf.square(grads[1])
w.assign_sub(lr * grads[0] / tf.sqrt(v_w))
b.assign_sub(lr * grads[1] / tf.sqrt(v_b))
Adam:同时结合SGDM一阶动量和RMSProp二阶动量
m t = β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t V t = β 2 ⋅ V s t e p − 1 + ( 1 − β 2 ) ⋅ g t 2 m_{\mathrm{t}}=\beta_{1} \cdot m_{t-1}+\left(1-\beta_{1}\right) \cdot g_{t}\\ V_{t}=\beta_{2} \cdot V_{s t e p-1}+\left(1-\beta_{2}\right) \cdot g_{t}^{2} mt=β1⋅mt−1+(1−β1)⋅gtVt=β2⋅Vstep−1+(1−β2)⋅gt2
假设 M 0 = 0 , G 0 = 0 M_0 = 0, G_0 = 0 M0=0,G0=0,那么在迭代初期 M t M_t Mt 和 G t G_t Gt 的值会比真实的均值和方差要小.特别是当 β 1 \beta_1 β1 和 β 2 \beta_2 β2 都接近于 1 时,偏差会很大。因此,需要对偏差进行修正。
修正一阶动量的偏差: m t ^ = m t 1 − β 1 t \hat{m_t}=\dfrac{m_t}{1-\beta_1^t} mt^=1−β1tmt
修正二阶动量的偏差: V t ^ = V t 1 − β 2 t \hat{V_t}=\dfrac{V_t}{1-\beta_2^t} Vt^=1−β2tVt
η t = l r ⋅ m ^ t / V ^ t = l r ⋅ m t 1 − β 1 t / V t 1 − β 2 t w t + 1 = w t − η t = w t − l r ⋅ m t 1 − β 1 t / V t 1 − β 2 t \begin{aligned} \eta_{t}=& l r \cdot \widehat{m}_{\mathrm{t}} / \sqrt{\widehat{V}_{t}} =l r \cdot \frac{m_{\mathrm{t}}}{1-\beta_{1}^{t}} / \sqrt{\frac{V_{t}}{1-\beta_{2}^{t}}} \\ w_{t+1} &=w_{t}-\eta_{t} =w_{t}-l r \cdot \frac{m_{\mathrm{t}}}{1-\beta_{1}^{t}} / \sqrt{\frac{V_{t}}{1-\beta_{2}^{t}}} \end{aligned} ηt=wt+1lr⋅m t/V t=lr⋅1−β1tmt/1−β2tVt=wt−ηt=wt−lr⋅1−β1tmt/1−β2tVt
m_w, m_b = 0, 0
v_w, v_b = 0, 0
beta1, beta2 = 0.9, 0.999
delta_w, delta_b = 0, 0
global_step = 0
m_w = beta1 * m_w + (1 - beta1) * grads[0]
m_b = beta1 * m_b + (1 - beta1) * grads[1]
v_w = beta2 * v_w + (1 - beta2) * tf.square(grads[0])
v_b = beta2 * v_b + (1 - beta2) * tf.square(grads[1])
m_w_correction = m_w / (1 - tf.pow(beta1, int(global_step)))
m_b_correction = m_b / (1 - tf.pow(beta1, int(global_step)))
v_w_correction = v_w / (1 - tf.pow(beta2, int(global_step)))
v_b_correction = v_b / (1 - tf.pow(beta2, int(global_step)))
w.assign_sub(lr * m_w_correction / tf.sqrt(v_w_correction))
b.assign_sub(lr * m_b_correction / tf.sqrt(v_b_correction))
Adam 会设置 3 个超参数。一个是学习率 α \alpha α,一般设为0.001,并且也可以进行衰减。另外两个是一次momentum系数 β 1 \beta_1 β1 和二次momentum系数 β 2 \beta_2 β2。根据论文,标准的设定值是 β 1 \beta_1 β1 为0.9, β 2 \beta_2 β2 为0.999。设置了这些值后,大多数情况下都能顺利运行。
Momentum
v ← α v − η ∂ L ∂ W W ← W + v \begin{array}{c} \boldsymbol{v} \leftarrow \alpha \boldsymbol{v}-\eta \frac{\partial L}{\partial \boldsymbol{W}} \\ \boldsymbol{W} \leftarrow \boldsymbol{W}+\boldsymbol{v} \end{array} v←αv−η∂W∂LW←W+v
class Momentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
params[key] += self.v[key]
每个参数的实际更新差值取决于最近一段时间内梯度的加权平均值.当某个参数在最近一段时间内的梯度方向不一致时,其真实的参数更新幅度变小;相反,当在最近一段时间内的梯度方向都一致时,其真实的参数更新幅度变大,起到加速作用。一般而言,在迭代初期,梯度方向都比较一致,动量法会起到加速作用,可以更快地到达最优点.在迭代后期,梯度方向会不一致,在收敛值附近振荡,动量法会起到减速作用,增加稳定性.从某种角度来说,当前梯度叠加上部分的上次梯度,一定程度上可以近似看作二阶梯度。
用Tensorflow API: tf. keras搭建网络八股
六步法:
- import
- train, test
- model = tf.keras.models .Sequential
- model.compile
- model.fit
- model.summary
model = tf.keras.models.Sequential ([网络结构]) #描述各层网络
网络结构举例:
拉直层: tf.keras.layers.Flatten( )
全连接层: tf.keras.layers.Dense(神经元个数,activation=“激活函数“,kernel_ regularizer=哪种正则化)
activation (字符串给出)可选: relu、softmax、 sigmoid 、tanh
kernel regularizer可选: tf.keras.regularizers.1()、tf.keras.regularizers.l2()
卷积层: tf.keras.layers.Conv2D(filters = 卷积核个数,kernel size =卷积核尺寸,strides=卷积步长,padding =”valid" or "same")
LSTM层: tf.keras.layers.LSTM()
model.compile(optimizer =优化器,loss =损失函数,metrics = [“准确率"] )
Optimizer可选:
‘sgd’ or tf.keras optimizers.SGD (Ir=学习率,momentum=动量参数)
‘adagrad’ or tf.keras. optimizers. Adagrad (r=学习率)
'adadelta or tf.keras. optimizers.Adadelta (r=学习率)
‘adam’ or tf.keras.optimizers.Adam (r=学习率,beta_ 1=0.9, beta_ 2=0.999)
loss可选:
‘mse’ or tf.keras.losses.MeanSquaredError()
‘sparse_categorical_crossentropy’ or tf.keras losses SparseCategoricalCrossentropy(from_logits=False)
Metrics可选:
‘accuracy’ : y _ y\_ y_和y都是数值,如 y_ =[1] y=[1]
'categorical_accuracy : y_ 和y都是独热码(概率分布).如y_ =[0,1,0] y=[0 256,0.695,0.048]
'sparse_categorical_accuracy : y_ 是数值,y是独热码(概率分布),如y_ =[1] y=[0.256,0.695,0.048]
model.fit (训练集的输入特征,训练集的标签,batch_ size= , epochs= , validation_ data=(测试集的输入特征,测试集的标签),validation_ split=从 训练集划分多少比例给测试集,validation_ freq =多少次epoch测试次)
model.summary()
代码示例:
import tensorflow as tf
fashion = tf.keras.datasets.fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation="softmax")
])
model.compile(optimizer="adam",
loss=tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=False),
metrics=["sparse_categorical_accuracy"])
model.fit(x_train, y_train, batch_size=32, epochs=5,
validation_data=(x_test, y_test), validation_freq=1)
model.summary()
权重的初始值
将权重初始值设为 0 的话,将无法正确进行学习。
为了防止“权重均一化”(严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值。
激活值在分布上有所偏向会出现“表现力受限”的问题。
sigmoid激活函数:为了使各层的激活值呈现出具有相同广度的分布,推导了合适的权重尺度。推导出的结论是,如果前一层的节点数为 n n n,则初始值使用标准差为 1 n \dfrac{1}{\sqrt{n}} n1 的分布。
Relu激活函数:当前一层的节点数为 $n $ 时,He 初始值使用标准差为 2 n \sqrt{\dfrac{2}{n}} n2 的高斯分布
当激活函数使用ReLU时,权重初始值使用He初始值,当激活函数为sigmoid 或tanh 等S 型曲线函数时,初始值使用Xavier 初始值。
Batch Normalization
Batch Norm有以下优点:
Batch Norm的思路是调整各层的激活值分布使其拥有适当的广度。为此,要向神经网络中插入对数据分布进行正规化的层。
Batch Norm,顾名思义,以进行学习时的mini-batch 为单位,按mini-batch进行正规化。具体而言,就是进行使数据分布的均值为0、方差为1 的正规化。
μ B ← 1 m ∑ i = 1 m x i σ B 2 ← 1 m ∑ i = 1 m ( x i − μ B ) 2 x ^ i ← x i − μ B σ B 2 + ε \begin{array}{l} \mu_{B} \leftarrow \frac{1}{m} \sum_{i=1}^{m} x_{i} \\ \sigma_{B}^{2} \leftarrow \frac{1}{m} \sum_{i=1}^{m}\left(x_{i}-\mu_{B}\right)^{2} \\ \hat{x}_{i} \leftarrow \frac{x_{i}-\mu_{B}}{\sqrt{\sigma_{B}^{2}+\varepsilon}} \end{array} μB←m1∑i=1mxiσB2←m1∑i=1m(xi−μB)2x^i←σB2+εxi−μB
通过将这个处理插入到激活函数的前面(或者后面)A,可以减小数据分布的偏向。接着,Batch Norm层会对正规化后的数据进行缩放和平移的变换,用数学式可以如下表示。
y i ← γ x ^ i + β y_{i} \leftarrow \gamma \hat{x}_{i}+\beta yi←γx^i+β
Dropout
Dropout 是一种在学习的过程中随机删除神经元的方法。通过使用Dropout,即便是表现力强的网络,也可以抑制过拟合。
image_gen_train = tf.keras.preprocessing.image.lmageDataGenerator(
rescale = #所有数据将乘以该数值
rotation_range = #随机旋转角度数范围
width_shift_range = #随机宽度偏移量
height_shift_range = #随机高度偏移量
水平翻转: horizontal_flip = #是否随机水平翻转
随机缩放: zoom_range = #随机缩放的范围[1-n, 1+n]
)
image_gen_train.fit(x_train) #这里输入得数据必须是4维数据
load weights(路径文件名)
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,callbacks=[cp_callback])