要求:
本地读取并加载数据,可选择本地图片进行识别
如何加速代码训练?
测试集accuracy到达91%
拔高:
如何解决过拟合问题?
测试集accuracy到达93%
参考文章:https://mtyjkh.blog.csdn.net/article/details/117186183
作者:K同学啊
本文为360天深度学习训练营中的学习记录博客
import matplotlib.pyplot as plt
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers,models
import os,PIL,pathlib
data_dir = 'D:\AIoT学习\深度学习训练营\第三周\weather_photos'
data_dir = pathlib.Path(data_dir)
该文章数据为训练内部数据集
Tips:
path.glob(pattern):在此路径表示的目录中全局查找给定的相对pattern,生成所有匹配的文件(任何类型)
在转成列表后可以查看具体文件名
image_count = len(list(data_dir.glob('*/*.jpg')))
print('图片总数为:',image_count)
图片总数为: 1125
roses = list(data_dir.glob('sunrise/*.jpg'))
PIL.Image.open(str(roses[0])) #根据path直接打开图片
使用image_dataset_from_directory方法将磁盘中的数据加载到tf.data.Dataset中
train_ds = keras.preprocessing.image_dataset_from_directory(
directory = data_dir,
validation_split = 0.2,
subset = 'training',
seed = 92,
batch_size = 32,
)
Found 1125 files belonging to 4 classes.
Using 900 files for training.
#设置验证集
val_ds = keras.preprocessing.image_dataset_from_directory(
directory = data_dir,
validation_split = 0.2,
subset = 'validation',
seed = 92,#随机种子与训练集相同
batch_size = 32)
Found 1125 files belonging to 4 classes.
Using 225 files for validation.
#查看数据集集标签,标签按字母顺序对应目录名
class_name = train_ds.class_names
print(class_name)
[‘cloudy’, ‘rain’, ‘shine’, ‘sunrise’]
通过tf.data.Dataset对象的take方法
take()
功能:用于返回一个新的Dataset对象,新的Dataset对象包含的数据是原Dataset对象的子集。
参数:
count:整型,用于指定前count条数据用于创建新的Dataset对象,如果count为-1或大于原Dataset对象的size,则用原Dataset对象的全部数据创建新的对象。
import tensorflow as tf
plt.figure(figsize=(20,10))
for images,labels in train_ds.take(1): #由于前面batch_size选的默认32,因此一批take中由32张图数据,这里我们只选取第一批作为展示
for i in range(20):
ax = plt.subplot(5,10,i+1)
plt.imshow(images[i].numpy().astype('uint8')) #这里调用的astype时tensorflow中与numpy相关的方法而不是numpy本身
plt.title(class_name[labels[i]])
plt.axis('off')
for image_batch,labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break
(32, 256, 256, 3)
(32,)
Datase对象的shuffle(buffer_size缓冲区大小)方法,随机打乱,=1时无打乱效果,数据集本身比较随机可以设置小一些
tf.data.AUTOTUNE
Dataset的cache(filename)方法,可以将数据缓存到内存中,加速运行
Dataset 的 prefetch()方法:
AUTOTUNE = tf.data.AUTOTUNE #-1完全打散
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
卷积神经网络(CNN)的输入是张量 (Tensor) 形式的 (image_height, image_width, color_channels),包含了图像高度、宽度及颜色信息。不需要输入batch size。color_channels 为 (R,G,B) 分别对应 RGB 的三个颜色通道(color channel)。我们需要在声明第一层时将形状赋值给参数input_shape
卷积运算一个重要的特点就是,通过卷积运算,可以使原信号特征增强,并且降低噪音。
model = models.Sequential([
layers.experimental.preprocessing.Rescaling(1./255,input_shape=(256,256,3)), #数据标准化
layers.Conv2D(16,(3,3),activation='relu',input_shape=(256,256,3)), #参数数量 3*3*3*16 + 16
layers.MaxPool2D((2,2)), #先通过最大值池化过滤掉不重要的信息,关注纹理
layers.Conv2D(32,(3,3),activation='relu'),
layers.AveragePooling2D((2,2)), #在深层则平均池化,保留低频的背景信号
layers.Conv2D(64,(3,3),activation='relu'),
layers.Flatten(), #维度等于60*60*64
layers.Dense(128,activation='relu'),
layers.Dropout(0.2), #230400*128+128
layers.Dense(len(class_name),activation='softmax')
])
model.summary()
Rescaling (None, 256, 256, 3) 0
Conv2D (None, 254, 254, 16) 448
MaxPooling (None, 127, 127, 16) 0
2D
Conv2D (None, 125, 125, 32) 4640
AveragePool2D (None, 62, 62, 32) 0
Conv2D (None, 60, 60, 64) 18496
Flatten (None, 230400) 0
Dense (None, 128) 29491328
Dropout None, 128) 0
Dense (None, 4) 516
=================================================================
Total params: 29,515,428
Trainable params: 29,515,428
Non-trainable params: 0
# 设置优化其
opt = keras.optimizers.Adam(learning_rate=0.001)
model.compile(optimizer=opt,loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
epochs = 10
histroy = model.fit(train_ds,validation_data=val_ds,epochs=epochs)
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.plot(histroy.history['accuracy'],label = 'accuracy')
plt.plot(histroy.history['val_accuracy'],label='val_accuracy')
plt.legend(loc='lower right')
plt.xlabel('encho num')
plt.title('Train and val accuracy')
plt.subplot(122)
plt.plot(histroy.history['loss'],label = 'loss')
plt.plot(histroy.history['val_loss'],label = 'val_loss')
plt.legend(loc='lower right')
plt.xlabel('encho num')
plt.title('Train and val loss')
model2 = models.Sequential([
layers.experimental.preprocessing.Rescaling(1./255,input_shape=(256,256,3)), #数据标准化
layers.Conv2D(16,(3,3),activation='relu',input_shape=(256,256,3)), ###此处16可以理解为卷积窗种类,可训练参数数量为3*3*16*3+16
#卷积层输出单个特征图大小256-3+1=254*254,共16个map
layers.MaxPool2D((2,2)), 息,关注纹理
layers.Conv2D(32,(3,3),activation='relu'),
layers.AveragePooling2D((2,2)), #在深层则平均池化,保留低频的背景信号
layers.Conv2D(64,(3,3),activation='relu'),
layers.Flatten(), #60*60*64
layers.Dense(128,activation='relu'), #Flatten输出维度*128+128
layers.Dropout(0.3),
layers.Dense(len(class_name),activation='softmax')
])
model2.summary()
opt = keras.optimizers.Adam(learning_rate=0.001)
model2.compile(optimizer=opt,loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
epochs = 32
histroy = model2.fit(train_ds,validation_data=val_ds,epochs=epochs)
plt.figure(figsize=(12,4))
plt.subplot(121)
plt.plot(histroy.history['accuracy'],label = 'accuracy')
plt.plot(histroy.history['val_accuracy'],label='val_accuracy')
plt.legend(loc='lower right')
plt.xlabel('encho num')
plt.title('Train and val accuracy')
plt.subplot(122)
plt.plot(histroy.history['loss'],label = 'loss')
plt.plot(histroy.history['val_loss'],label = 'val_loss')
plt.legend(loc='lower right')
plt.xlabel('encho num')
plt.title('Train and val loss')
model3 = models.Sequential([
layers.experimental.preprocessing.Rescaling(1./255,input_shape=(256,256,3)),
layers.Conv2D(16,(3,3),activation='relu',input_shape=(256,256,3)),
layers.AveragePooling2D((2,2)),
layers.Conv2D(32,(3,3),activation='relu'),
layers.AveragePooling2D((2,2)),
layers.Conv2D(64,(3,3),activation='relu'),
layers.Flatten(),
layers.Dense(128,activation='relu'), *128+128
layers.Dropout(0.3),
layers.Dense(len(class_name),activation='softmax')
])
model3.summary()
准确率与损失函数图像为
相对前一个方案测试集损失函数有些许降低,但稳定性下降,过拟合仍存在,考虑是否与图片结构有关,可能边缘也包含了不少的信息,将卷积层padding方案改为padding
layers.Conv2D(16,(3,3),padding='same',activation='relu',input_shape=(256,256,3)), #将前两层padding方案改为padding,保留更多边缘信息
加入L2正则化
对第一个卷积层进行修改
layers.Conv2D(16,(3,3),padding='same',activation='relu',input_shape=(256,256,3),kernel_regularizer=keras.regularizers.l2(0.1)),
测试集和训练集准确率仍有明显差异,由此怀疑是否时模型本身的性能问题,选择增加卷积层
model6 = models.Sequential([
layers.experimental.preprocessing.Rescaling(1./255,input_shape=(256,256,3)), #数据标准化
layers.Conv2D(16,(3,3),padding='same',activation='relu',input_shape=(256,256,3),kernel_regularizer=keras.regularizers.l2(0.1)),
layers.AveragePooling2D((2,2)),
layers.Conv2D(32,(3,3),padding='same',activation='relu'),
layers.AveragePooling2D((2,2)),
layers.Conv2D(64,(3,3),activation='relu'),
layers.AveragePooling2D((2,2)),
layers.Conv2D(128,(3,3),activation='relu'), #添加新卷积层
layers.Flatten(),
layers.Dense(128,activation='relu'),
layers.Dropout(0.3),
layers.Dense(len(class_name),activation='softmax')
])