动量、学习率、Early Stopping、Dropout

本文介绍了深度学习中的几种关键优化技术,包括动量、学习率调整、EarlyStopping和Dropout等,详细解析了它们的工作原理及如何应用于实际项目中。

动量和学习率

1、动量(momentum)

梯度更新:

动量:

其中Z表示的是前一次梯度的方向,根据矢量三角形法则,选取下次的运动方向。

在这里插入图片描述
不加动量的情况下,方向变化比较尖锐,没有考虑历史变化的情况,且容易局部最优。
在这里插入图片描述
加动量后,变化更加的缓和,依靠惯性,可能跳出局部最优解。

optimizer = SGD(learning_rate=0.02, momentum=0.9)
optimizer = RMSprop(learning_rate=0.02, momentum=0.9)
optimizer = Adam(learning_rate=0.02, beta_1=0.9, beta_2=0.999)

2、学习率(learning_rate)

运用梯度下降算法进行优化时,权重的更新规则中,在梯度项前会乘以一个系数,这个系数就叫学习速率。
在这里插入图片描述
上图左边的是学习率太小,梯度更新速度太慢;右边是学习率太大,更新速度太快,最后可能找不到最优解。所以需要学习率能够动态的变化,可以根据自己的需要添加变化条件。

当validation accuracy满足 no-improvement-in-n规则时,本来我们是要early stopping的,但是我们可以不stop,而是让learning rate减半,之后让程序继续跑。下一次validation accuracy又满足no-improvement-in-n规则时,我们同样再将learning rate减半(此时变为原始learni rate的四分之一)…继续这个过程,直到learning rate变为原来的1/1024再终止程序。(1/1024还是1/512还是其他可以根据实际确定)。【PS:也可以选择每一次将learning rate除以10,而不是除以2.】

optimizer = SGD(learning_rate=0.02)

for epoch in range(100):
    #get loss
    
    # change learning_rate
    optimizer.learning_rate() = 0.2 * (100-epoch)/100
    
    #update weights
#Assuming optimizer uses lr = 0.05 for all groups
#lr = 0.05       if epoch < 30
#lr = 0.005      if 30 <= epoch < 60
#lr = 0.0005     if 60 <= epoch < 90

3、Early Stopping

所谓early stopping,即在每一个epoch结束时(一个epoch即对所有训练数据的一轮遍历)计算 validation data的accuracy,当accuracy不再提高时,就停止训练。这是很自然的做法,因为accuracy不再提高了,训练下去也没用。另外,这样做还能防止overfitting(过拟合)。

那么,怎么样才算是validation accuracy不再提高呢?并不是说validation accuracy一降下来,它就是“不再提高”,因为可能经过这个epoch后,accuracy降低了,但是随后的epoch又让accuracy升上去了,所以不能根据一两次的连续降低就判断“不再提高”。正确的做法是,在训练的过程中,记录最佳的validation accuracy,当连续10次epoch(或者更多次)没达到最佳accuracy时,你可以认为“不再提高”,此时使用early stopping。这个策略就叫“ no-improvement-in-n”,n即epoch的次数,可以根据实际情况取10、20、30….

4、Dropout

当一个复杂的前馈神经网络被训练在小的数据集时,容易造成过拟合。为了防止过拟合,可以通过阻止特征检测器的共同作用来提高神经网络的性能。

Dropout说的简单一点就是:我们在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征,如图1所示。
图1:使用Dropout的神经网络模型
Dropout具体工作流程
假设我们要训练这样一个神经网络,如图2所示。
图2:标准的神经网络
输入是x输出是y,正常的流程是:我们首先把x通过网络前向传播,然后把误差反向传播以决定如何更新参数让网络进行学习。使用Dropout之后,过程变成如下:

(1)首先随机(临时)删掉网络中一半的隐藏神经元,输入输出神经元保持不变(图3中虚线为部分临时被删除的神经元)
图3:部分临时被删除的神经元
(2) 然后把输入x通过修改后的网络前向传播,然后把得到的损失结果通过修改的网络反向传播。一小批训练样本执行完这个过程后,在没有被删除的神经元上按照随机梯度下降法更新对应的参数(w,b)。

(3)然后继续重复这一过程:

. 恢复被删掉的神经元(此时被删除的神经元保持原样,而没有被删除的神经元已经有所更新)
. 从隐藏层神经元中随机选择一个一半大小的子集临时删除掉(备份被删除神经元的参数)。
. 对一小批训练样本,先前向传播然后反向传播损失并根据随机梯度下降法更新参数(w,b) (没有被删除的那一部分参数得到更新,删除的神经元参数保持被删除前的结果)。
不断重复这一过程。

可以在网络层中直接调用dropout。

network = Sequential([layers.Dense(256, activation='relu'),
                     layers.Dropout(0.5), # 0.5 rate to drop
                     layers.Dense(128, activation='relu'),
                     layers.Dropout(0.5), # 0.5 rate to drop
                     layers.Dense(64, activation='relu'),
                     layers.Dense(32, activation='relu'),
                     layers.Dense(10)])

添加了dropout后training和test部分不一样,需要人为的区分。

for step, (x,y) in enumerate(db):

    with tf.GradientTape() as tape:
        # [b, 28, 28] => [b, 784]
        x = tf.reshape(x, (-1, 28*28))
        # [b, 784] => [b, 10]
        out = network(x, training=True)
    
    
    #test
    out = network(x, training=False)  

完整代码:

import  os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import  tensorflow as tf
from    tensorflow.keras import datasets, layers, optimizers, Sequential, metrics


def preprocess(x, y):

    x = tf.cast(x, dtype=tf.float32) / 255.
    y = tf.cast(y, dtype=tf.int32)

    return x,y


batchsz = 128
(x, y), (x_val, y_val) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())



db = tf.data.Dataset.from_tensor_slices((x,y))
db = db.map(preprocess).shuffle(60000).batch(batchsz).repeat(10)

ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz) 




network = Sequential([layers.Dense(256, activation='relu'),
                     layers.Dropout(0.5), # 0.5 rate to drop
                     layers.Dense(128, activation='relu'),
                     layers.Dropout(0.5), # 0.5 rate to drop
                     layers.Dense(64, activation='relu'),
                     layers.Dense(32, activation='relu'),
                     layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()

optimizer = optimizers.Adam(lr=0.01)



for step, (x,y) in enumerate(db):

    with tf.GradientTape() as tape:
        # [b, 28, 28] => [b, 784]
        x = tf.reshape(x, (-1, 28*28))
        # [b, 784] => [b, 10]
        out = network(x, training=True)
        # [b] => [b, 10]
        y_onehot = tf.one_hot(y, depth=10) 
        # [b]
        loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y_onehot, out, from_logits=True))


        loss_regularization = []
        for p in network.trainable_variables:
            loss_regularization.append(tf.nn.l2_loss(p))
        loss_regularization = tf.reduce_sum(tf.stack(loss_regularization))

        loss = loss + 0.0001 * loss_regularization
 

    grads = tape.gradient(loss, network.trainable_variables)
    optimizer.apply_gradients(zip(grads, network.trainable_variables))


    if step % 100 == 0:

        print(step, 'loss:', float(loss), 'loss_regularization:', float(loss_regularization)) 


    # evaluate
    if step % 500 == 0:
        total, total_correct = 0., 0

        for step, (x, y) in enumerate(ds_val): 
            # [b, 28, 28] => [b, 784]
            x = tf.reshape(x, (-1, 28*28))
            # [b, 784] => [b, 10] 
            out = network(x, training=True)  
            # [b, 10] => [b] 
            pred = tf.argmax(out, axis=1) 
            pred = tf.cast(pred, dtype=tf.int32)
            # bool type 
            correct = tf.equal(pred, y)
            # bool tensor => int tensor => numpy
            total_correct += tf.reduce_sum(tf.cast(correct, dtype=tf.int32)).numpy()
            total += x.shape[0]

        print(step, 'Evaluate Acc with drop:', total_correct/total)

        total, total_correct = 0., 0

        for step, (x, y) in enumerate(ds_val): 
            # [b, 28, 28] => [b, 784]
            x = tf.reshape(x, (-1, 28*28))
            # [b, 784] => [b, 10] 
            out = network(x, training=False)  
            # [b, 10] => [b] 
            pred = tf.argmax(out, axis=1) 
            pred = tf.cast(pred, dtype=tf.int32)
            # bool type 
            correct = tf.equal(pred, y)
            # bool tensor => int tensor => numpy
            total_correct += tf.reduce_sum(tf.cast(correct, dtype=tf.int32)).numpy()
            total += x.shape[0]

        print(step, 'Evaluate Acc without drop:', total_correct/total)

参考:
https://blog.youkuaiyun.com/chezhai/article/details
深度学习中Dropout原理解析

<think>好的,我现在需要帮助用户优化他们的卷积神经网络(CNN)模型,以避免过早停止并提高性能。用户提到他们使用TensorFlow/Keras构建了一个包含多个卷积模块、空洞卷积和全局平均池化的模型,但在第12个epoch触发了early stopping。首先,我需要分析可能导致过早停止的原因,并给出相应的解决方案。 首先,过早停止通常是由于验证集的性能在几个epoch内没有改善,这可能意味着模型过拟合或者训练不够有效。需要从模型架构、超参数、正则化方法等方面考虑优化。 在模型架构方面,用户已经使用了空洞卷积和全局平均池化。可能需要检查卷积层的深度和宽度是否足够,或者是否存在梯度消失的问题。添加残差连接(Residual Connections)可能有助于缓解梯度消失,使深层网络更容易训练。例如,在卷积模块中加入跳跃连接,这样可以让信息更顺畅地传递。 另外,批量归一化(Batch Normalization)也是一个重要的点。在卷积层后添加BN层可以加速训练,减少内部协变量偏移,可能提高模型的收敛速度和稳定性。用户可能需要检查他们的模型是否已经包含了BN层,如果没有,可以考虑添加。 关于正则化方法,用户可能已经使用了一些方法,但可能需要进一步调整。例如,在卷积层后加入Dropout层,但需要注意Dropout的位置和比率。过高的Dropout率可能导致模型欠拟合,而过低则可能无法有效防止过拟合。此外,L2正则化(权重衰减)也是一个选择,可以在卷积层或全连接层中添加。 超参数调整方面,学习率是最关键的参数之一。用户可能使用了固定的学习率,导致在训练后期无法收敛到更优点。建议使用学习率调度器,比如指数衰减或余弦退火,动态调整学习率。另外,优化器的选择也很重要,比如从Adam切换到Nadam或者使用带有动量的SGD,可能会带来更好的效果。 数据增强也是防止过拟合的有效方法。用户可能已经应用了旋转、平移等增强手段,但可以尝试更复杂的增强,如MixUp或CutMix,这些方法能生成更丰富的训练样本,提升模型泛化能力。 早停的耐心值(patience)设置可能需要调整。如果用户的patience设置过低(比如默认的5),可能在验证集波动时过早停止。适当增加patience值,比如到10或15,给模型更多时间改善。 另外,检查输入数据的预处理是否正确,比如归一化是否恰当,数据是否平衡,是否存在标签错误等,这些都可能影响训练效果。如果数据量不足,可以考虑迁移学习,使用预训练模型作为特征提取器,并在顶部进行微调。 最后,梯度裁剪(Gradient Clipping)可以防止梯度爆炸,尤其是在深层网络中。用户可以在优化器中设置梯度裁剪的阈值,稳定训练过程。 总结下来,需要从架构优化、正则化、超参数调整、数据增强等多个方面入手,逐步排查和调整。可能需要用户尝试不同的组合,通过实验找到最适合他们模型和数据的配置。</think>针对CNN模型过早停止和性能优化问题,建议从以下六个方面进行改进: ### 一、模型架构优化 1. 增加残差连接:在空洞卷积模块后添加跳跃连接 ```python def residual_block(x, filters): shortcut = x x = Conv2D(filters, 3, dilation_rate=2, padding='same')(x) x = BatchNormalization()(x) x = ReLU()(x) x = Conv2D(filters, 3, padding='same')(x) x = Add()([shortcut, x]) return x ``` 2. 优化特征维度过渡:使用1×1卷积调整通道数 $$ C_{out} = Conv_{1×1}(C_{in}) $$ ### 二、正则化策略组合 1. 混合正则化方案: ```python x = Conv2D(64, 3, padding='same', kernel_regularizer=l2(0.001))(x) x = BatchNormalization()(x) x = SpatialDropout2D(0.3)(x) ``` 2. 添加高斯噪声层: ```python x = GaussianNoise(0.1)(input_layer) ``` ### 三、动态学习率配置 使用余弦退火调度器: ```python lr_schedule = tf.keras.optimizers.schedules.CosineDecayRestarts( initial_learning_rate=1e-3, first_decay_steps=20, t_mul=2.0, m_mul=0.5) optimizer = AdamW(learning_rate=lr_schedule, weight_decay=1e-4) ``` ### 四、梯度控制策略 1. 自适应梯度裁剪: ```python optimizer = Adam(clipnorm=1.0, clipvalue=0.5) ``` 2. 梯度中心化: $$ g_{centered} = g - \mu(g) $$ ### 五、数据增强升级 混合增强策略: ```python datagen = ImageDataGenerator( rotation_range=30, zoom_range=0.2, width_shift_range=0.2, height_shift_range=0.2, preprocessing_function=cutmix_augment) ``` ### 六、早停策略优化 自适应早停机制: ```python early_stop = EarlyStopping( monitor='val_loss', patience=15, min_delta=0.001, restore_best_weights=True) ``` **实施建议**: 1. 先调整学习率策略和正则化组合 2. 逐步添加残差连接和梯度控制 3. 最后优化数据增强策略 4. 使用TensorBoard监控训练过程: ```python tensorboard = tf.keras.callbacks.TensorBoard( log_dir='logs', histogram_freq=1, update_freq='epoch') ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值