【深度学习】使用Tensorflow和Keras对本地数据集进行图像分类以及性能调优

1. 背景

学习深度学习框架Tensorflow的Keras框架进行图像分类,按照网上的教程一步步来,记录要点和总结

2. 要点

1. 准备本地数据集
2. 使用Keras加载本地数据集,分为训练集和测试集,并进行磁盘读取优化
3. 手动创建网络,添加自定义层
4. 针对过拟合的处理
5. 性能调优

总结

本程序的目的是利用tensorflow框架进行图像分类

要点:

    1. 本地准备数据集

    2. 利用tensorflow tf.keras.utils.image_dataset_from_directory将本地文件生成Dataset对象

    3. 利用Dataset.to_numpy()将验证数据集的images和labels转换成numpy数组,以便验证预测的正确性

    4. 使用自定义优化器在训练过程中随训练次数降低学习率,防止过拟合

    5. 使用数据集中的充分打乱和重复数据,防止过拟合

    6. 使用在神经网络中添加数据增强层(随机旋转,随机缩放,随机翻转)防止过拟合

    7. 使用在神经网络中添加Dropout层,(目的是在网络的输出中随机丢弃一定比率的数据,防止过拟合

    8. 在训练完成后,使用plt生成准确率和损失值的趋势图,方便分析和调整

3.1 准备本地数据集

准备图片目录结构如下

-DataSet

        -Daisy

                -XXX.jpg

                -YYY.jpg

                -...

        -Sunflowers

                -XXX.jpg

                -YYY.jpg

                ...

        -...

要点就是文件夹里面放同一类型的图片,文件夹名称就是这些图片的标签

3.2 加载数据集

train_ds = tf.keras.utils.image_dataset_from_directory(
        train_data_dir,
        seed=123, #将样本打乱的随机数种子
        image_size=(image_height, image_width),
        batch_size=batch_size #批次大小,受制于系统性能
    ).shuffle(32) #使用shuffle将图片打乱

    # single_data = list(train_ds.as_numpy_iterator())
    # label = single_data[0]
    
    val_ds = tf.keras.utils.image_dataset_from_directory(
        test_data_dir,
        seed=123,
        image_size=(image_height, image_width),
        batch_size=1
    )

3.3 手动创建网络,添加自定义层

# 定义一个序贯模型
model = tf.keras.Sequential(
    [
        # 归一化层,通常用于加速模型的训练并提高其性能 【0~255】 -> 【0~1】
        normalization_layer,

        # 接下来是三个卷积层,每个卷积层后都接一个ReLU激活函数
        tf.keras.layers.Conv2D(
            16, 3, activation='relu'),
        # 接下来是一个最大池化层,用于降低特征图的尺寸,从而减少计算复杂度
        tf.keras.layers.MaxPool2D(),

        # 第二个卷积层和最大池化层
        tf.keras.layers.Conv2D(
            32, 3, activation='relu'),
        tf.keras.layers.MaxPool2D(),

        # 第三个卷积层和最大池化层
        tf.keras.layers.Conv2D(
            64, 3, activation='relu'),
        tf.keras.layers.MaxPool2D(),

        # Flatten层将卷积层的输出拉平,为全连接层做准备
        tf.keras.layers.Flatten(),
        # 第一个全连接层,128个神经元,激活函数为ReLU
        tf.keras.layers.Dense(128, activation='relu'),
        # 最后一个全连接层,numclasss个神经元,用于分类任务,不使用激活函数
        tf.keras.layers.Dense(numclasss)
    )
)

4 针对过拟合的处理

上面的网络训练小样本数据集会出现明显的过拟合,验证集的准确率只有大约50%,大约当epoch 大于3的时候就开始出现过拟合,我现在没有结果截图了,有兴趣的可以尝试

4.1 过拟合发生的原因

1. 训练数据集太小或缺乏代表性。如果训练集数据量太小,模型可能会在训练过程中过于拟合训练数据,导致在新的、未见过的数据上表现不佳。同时,如果训练数据缺乏代表性,模型可能无法泛化到所有可能的情况,从而产生过拟合。
2. 训练集样本存在的噪音干扰过大。如果训练样本中存在大量噪声,模型可能会过度关注这些噪声,从而忽略真实的输入输出关系,导致过拟合。
3. 模型复杂度过高。如果所使用的模型复杂度超过训练数据的复杂性,模型可能会过度拟合训练数据,产生过拟合现象。
4. 训练过程中的优化方法不当。例如,如果使用了过于复杂的优化算法(如过多的迭代次数),可能会使模型在训练过程中过于追求最小化损失函数,从而产生过拟合。
5. 验证集和测试集与训练集分布不一致。如果验证集和测试集的数据分布与训练集的数据分布相差太大,那么模型在这些数据集上的表现可能不佳,导致过拟合。
6. 数据集中的类别不平衡。如果数据集中某些类别的样本数量远大于其他类别,模型可能会过度关注数量较多的类别,从而忽略数量较少的类别,导致过拟合。
7. 特征选择不当。如果所选特征与预测目标无关或相关性不大,那么模型在这些特征上的学习就可能是无效的,甚至可能导致过拟合。
8. 超参数调整不当。模型的超参数如学习率、批次大小、正则化参数等调整不当,也可能导致过拟合。

4.2 减少过拟合的方法

为了防止过拟合,可以采取以下措施:

1. 增加训练数据集的数量和多样性,使其更具代表性。
对训练数据进行预处理,如去噪、归一化等,以减少噪声对模型的影响。
2. 选择合适的模型复杂度,避免过于复杂的模型。
3. 选择合适的优化算法和参数,避免过度优化。
4. 验证集和测试集应该与训练集保持一致的数据分布。
5. 对于类别不平衡的数据集,可以采用重采样、生成合成样本等方法来平衡类别分布。
6. 选择与预测目标高度相关的特征,并删除无关或相关性不大的特征。
7. 调整超参数以优化模型的性能,例如通过交叉验证等方法来寻找最佳的超参数组合。

4.3 代码调整

1. 逐渐降低的学习率 
    lr_schedule = tf.keras.optimizers.schedules.InverseTimeDecay(
        0.001,
        decay_steps=500,
        decay_rate=1,
        staircase=False)

'''
lr_schedule = tf.keras.optimizers.schedules.InverseTimeDecay(: 这一行代码是创建了一个名为InverseTimeDecay的学习率调度器实例,这个实例被赋值给了变量lr_schedule。这个调度器在Tensorflow的优化器库中定义。
0.001: 这是初始学习率,即在开始训练时使用的学习率。
decay_steps=500: 这是指在训练过程中,学习率会在每500步进行一次衰减。
decay_rate=1: 这是指学习率的衰减比例。一个衰减率值为1的学习率调度器意味着学习率会在每个decay_steps步长后减半。
staircase=False: 这表示当进行学习率衰减时,不是按照阶梯式(staircase)方式进行,而是连续平滑地衰减。
'''

这段代码是用来创建一个学习率调度器(Learning Rate Scheduler)的,该调度器使用了一种叫做"Inverse Time Decay"的策略。这种策略基本上是说,随着时间的推移,学习率会以一种倒数的方式进行衰减。在实践中,这种学习率调度器通常用于深度学习模型训练中,因为随着训练的进行,模型可能会逐渐适应训练数据,因此需要逐渐降低学习率以避免过拟合。通过这种方式,可以在训练初期使用较高的学习率进行模型训练,然后在训练过程中逐渐降低学习率,从而使得模型能够在保持良好泛化性能的同时,更好地优化模型参数。

2. 降低卷积核过高的权重

将上面的网络定义修改如下

# 定义一个序贯模型
model = tf.keras.Sequential(
    [
        # 归一化层,通常用于加速模型的训练并提高其性能
        normalization_layer,
        # 接下来是三个卷积层,每个卷积层后都接一个ReLU激活函数和L2正则化,用于防止过拟合
        tf.keras.layers.Conv2D(
            16, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)),
        # 接下来是一个最大池化层,用于降低特征图的尺寸,从而减少计算复杂度
        tf.keras.layers.MaxPool2D(),
        # 第二个卷积层和最大池化层
        tf.keras.layers.Conv2D(
            32, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)),
        tf.keras.layers.MaxPool2D(),
        # 第三个卷积层和最大池化层
        tf.keras.layers.Conv2D(
            64, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)),
        tf.keras.layers.MaxPool2D(),
        # 接下来是一个Dropout层,用于防止过拟合,随机丢弃一部分神经元
        tf.keras.layers.Dropout(0.2),
        # Flatten层将卷积层的输出拉平,为全连接层做准备
        tf.keras.layers.Flatten(),
        # 第一个全连接层,128个神经元,激活函数为ReLU
        tf.keras.layers.Dense(128, activation='relu'),
        # 最后一个全连接层,numclasss个神经元,用于分类任务,不使用激活函数
        tf.keras.layers.Dense(numclasss)
    )
)

注意,和上面网络的不同点:

kernel_regularizer=tf.keras.regularizers.l2(0.001):这是正则化项,用于控制模型的复杂性以防止过拟合。L2正则化是最常见的正则化方法之一,它通过对卷积核的权重进行惩罚来减少模型的复杂度。这里的参数0.001是 L2 正则化的系数。

3. 数据增强
data_augmentation = tf.keras.Sequential(
        [
            tf.keras.layers.RandomFlip(
                'horizontal', input_shape=(image_height, image_width, 3)),
            tf.keras.layers.RandomRotation(0.1),
            tf.keras.layers.RandomZoom(0.1)
        ]
    )

'''
tf.keras.Sequential( [...] ):这是创建一个顺序模型(Sequential model)的语句,该模型将按照定义的顺序执行一系列层。
tf.keras.layers.RandomFlip( 'horizontal', input_shape=(image_height, image_width, 3) ):这是一个随机水平翻转层。它将输入图像(预期为彩色图像,具有高度image_height、宽度image_width和3个颜色通道)沿水平方向翻转。这种翻转是随机的,也就是说,翻转操作在每次前向传播时都有可能发生。
tf.keras.layers.RandomRotation(0.1):这是一个随机旋转层。它将输入图像(保持与前面相同的形状)旋转一个随机的角度,角度范围为-0.1到+0.1弧度(约合10度)。这意味着每次前向传播时,图像都可能以不同的角度旋转。
tf.keras.layers.RandomZoom(0.1):这是一个随机缩放层。它将输入图像(保持与前面相同的形状)随机缩放,缩放因子在0.9到1.1之间变化。这意味着每次前向传播时,图像都可能以不同的缩放比例进行缩放。
'''

这段代码是使用 TensorFlow 和 Keras 创建的一个数据增强(Data Augmentation)模块。数据增强是一种技术,通过在训练数据周围添加一些小的变化(或扰动)来扩展数据集,从而增加模型的泛化能力。这种方法可以帮助模型更好地理解输入数据的各种变化,并提高其预测的准确性。

然后将数据增强层插入到网络的最前面

# 定义一个序贯模型
model = tf.keras.Sequential(
    # 列表中的第一个元素是数据增强层,通常用于提高模型的泛化能力
    [
        data_augmentation,
        # 列表中的第二个元素是归一化层,通常用于加速模型的训练并提高其性能
        normalization_layer,
        # 接下来是三个卷积层,每个卷积层后都接一个ReLU激活函数和L2正则化,用于防止过拟合
        tf.keras.layers.Conv2D(
            16, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)),
...

最后,看看结果吧

可以看到,当训练轮次达到15轮时,训练还没有发生过拟合(通常表现为”Traning Accuracy“继续提高,但是"Validation Accuracy"停滞不前或开始下降,同时Validation的loss开始上升),而且准确率达到了70%以上

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值