3.4 Kaggle自然场景图片分类
自然场景识别数据集来自kaggle竞赛平台,最初是由Intel举办的一个图片分类竞赛数据集,它的训练集有14034张图片,测试集有3000张图片,未标注数据集有7301张图片,格式都为jpg,图片分辨率统一为150×150。
数据集共有6个类别:
- buildings(建筑)
- forest(森林)
- glacier(冰川)
- mountain(高山)
- sea(海洋)
- street(街道)
每个类别的训练集图片数量大概有2000多张。
链接:https://pan.baidu.com/s/1u02PyMyTKl2mD5imG4eirQ
提取码:k13k
项目文件树状图
实际项目文件结构
主要流程
data_generator.py
数据读取脚本
图片生成器tf.keras.preprocessing.image.ImageDataGenerator
用到的参数:
rescale
:输入一个整数,通常为1/255,由于图像像素都是0~255的整数,rescale可以 让所有像素统一乘上一个数值,如果是1/255,像素会被转化为0~1之间的数。
从目录读取图片tf.keras.preprocessing.image.ImageDataGenerator.flow_from_directory
flowchatchat_from_directory
:从文件中读写
用到的参数:
directory
:图片存放路径。target_size
:图片宽高缩放到指定的大小,默认(256, 256)。batch_size
:每次读取的图片数,默认32。class_mode
:类别格式,默认’categorical’。
如果是’sparse
’:类别[‘paper’, ‘rock’, ‘scissors’] ——> [0, 1, 2]
如果是’categorical
’:类别[‘paper’, ‘rock’, ‘scissors’] ——> [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
如果是’input
’:类别[‘paper’, ‘rock’, ‘scissors’]保持不变
如果是None
:不返回标签。subset
:子集
from tensorflow.keras.preprocessing.image import ImageDataGenerator
def train_val_generator(data_dir, target_size, batch_size, class_mode = None, subset = 'training'):
train_val_datagen = ImageDataGenerator(rescale = 1./255., validation_split = 0.2)
return train_val_datagen.flow_from_directory(
directory = data_dir,
target_size = target_size,
batch_size = batch_size,
class_mode = class_mode,
subset = subset)
def test_generator(data_dir, target_size, batch_size, class_mode=None):
test_datagen = ImageDataGenerator(rescale = 1./255.)
return test_datagen.flow_from_directory(
directory = data_dir,
target_size = target_size,
batch_size = batch_size,
class_mode = class_mode)
def pred_generator(data_dir, target_size, batch_size, class_mode = None):
pred_datagen = ImageDataGenerator(rescale = 1./255.)
return pred_datagen.flow_from_directory(
directory = data_dir,
target_size = target_size,
batch_size = batch_size,
class_mode = class_mode)
image_plot.py
图片可视化脚本
画图函数plot_images
参数介绍:
images
:包含多张图片数据的序列。labels
:包含图片对应标签的序列(序列中的元素需要是0,1,2,…,9这样的正整数)。
import numpy as np
import matplotlib.pyplot as plt
class_names = ['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']
def plot_images(images, labels):
fig, axes = plt.subplots(3, 5, figsize=(12,6))
axes = axes.flatten()
for img, label, ax in zip(images, labels, axes):
ax.imshow(img)
ax.set_title(class_names[np.argmax(label)])
ax.axis('off')
plt.tight_layout()//自动调整子图参数,使之填充整个图像区域
plt.show()
conv_net.py
模型网络文件
卷积操作tf.keras.layers.Conv2D
用到的参数:
input_shape
:输入数据的维度大小,如3通道64X64的输入图片,要设置为(64,64,3),参数input_shape
只需要在第一个网络层进行设置filters
:输入整数,卷积核个数(等于卷积后输出的通道数)。kernel_size
:卷积核的大小,通常为一个正整数或者一个列表。
例子:kernel_size = 3
,kernel_size(3,3)
,都表示卷积核宽高方向的大小为3.
设置为整数就表示卷积核的height = width = 指定整数。strides
:卷积核的滑动步长,通常为一个正整数或者一个列表,列表可以包含2个或者4个元素,默认为(1, 1)。
例子:strides=2
,strides=(2,2)
,都表示卷积核宽高方向的大小为3padding
:补0设置,可选项为"VALID
","SAME
"。- 如果
padding="VALID"
,表示不做补0设置 - 如果
padding="SAME"
,程序会自动补0,使得输出feature map的宽高=ceil(输入图片宽高/strides),
ceil表示向上取整,当strides=1
时,输入输出的宽高是一致的
- 如果
kernel_initializer
:权重初始化,默认是’glorot_uniform
’(即Xavier均匀初始化)。
可选项:- ‘
RandomNormal
’:正态分布采样,均值为0,标准差0.05 - ‘
glorot_normal
’:正态分布采样,均值为0,标准差stddev = sqrt(2 / (fan_in + fan_out)) - ‘
glorot_uniform
’:均匀分布采样,范围[-limit, limit],标准差limit = sqrt(6 / (fan_in + fan_out)) - ‘
lecun_normal
’:正态分布采样,均值为0,标准差stddev = sqrt(1 / fan_in) - ‘
lecun_uniform
’:均匀分布采样,范围[-limit, limit],标准差limit = sqrt(3 / fan_in) - ‘
he_normal
’:正态分布采样,均值为0,标准差stddev = sqrt(2 / fan_in) - ‘
he_uniform
’:均匀分布采样,范围[-limit, limit],标准差limit = sqrt(6 / fan_in)
fan_in是输入的神经元个数,fan_out是输出的神经元个数。
- ‘
activation
:激活函数。
可选项:- ‘
sigmoid
’:sigmoid
激活函数 - ‘
tanh
’:tanh
激活函数 - ‘
relu
’:relu
激活函数 - '
elu
'或tf.keras.activations.elu(alpha=1.0)
:elu
激活函数 - ‘
selu
’:selu
激活函数 - ‘
swish
’:swish
激活函数(tf2.2版本以上才有) - ‘
softmax
’:softmax
函数 input_shape
:如果是第一层卷积,需要设置输入图片的大小(height, width, channel),如input_shape=(128, 128, 3)。name
:输入字符串,给该层设置一个名称。
- ‘
池化操作tf.keras.layers.MaxPool2D
用到的参数:
pool_size
:池化的大小,设置为整数就表示池化的height = width = 指定整数。strides
:池化的滑动步长,通常等于pool_size。name
:输入字符串,给该层设置一个名称。
全连接操作tf.keras.layers.Dense
用到的参数:
units
:输入整数,全连接层神经元个数。activation
:激活函数,分类网络的输出层一般用’softmax’激活函数。name
:输入字符串,给该层设置一个名称。
展平操作tf.keras.layers.Flatten
举例说明:
[[1,2,3],
[4,5,6], ——> [1,2,3,4,5,6,7,8,9]
[7,8,9]]
from tensorflow.keras import Model
from tensorflow.keras.layers import Flatten, Conv2D, MaxPool2D, Dense
# 定义一个子类来搭建模型
class ConvModel(Model):
def __init__(self):
# 父类初始化
super(ConvModel, self).__init__()
# 卷积层conv_1_1
self.conv_1_1 = Conv2D(input_shape=(
64, 64, 3), filters=32, kernel_size=3, activation='relu', name='conv_1_1')
# 卷积层conv_1_2
self.conv_1_2 = Conv2D(filters=32, kernel_size=3,
activation='relu', name='conv_1_2')
# 池化层max_pool_1
self.max_pool_1 = MaxPool2D(pool_size=2, name='max_pool_1')
# 卷积层conv_2_1
self.conv_2_1 = Conv2D(filters=64, kernel_size=3,
activation='relu', name='conv_2_1')
# 卷积层conv_2_2
self.conv_2_2 = Conv2D(filters=64, kernel_size=3,
activation='relu', name='conv_2_2')
# 池化层max_pool_2
self.max_pool_2 = MaxPool2D(pool_size=2, name='max_pool_2')
# 展平层flatten
self.flatten = Flatten(name='flatten')
# 全连接层
self.dense = Dense(units=6, activation="softmax", name='logit')
def call(self, x):
x = self.conv_1_1(x)
x = self.conv_1_2(x)
x = self.max_pool_1(x)
x = self.conv_2_1(x)
x = self.conv_2_2(x)
x = self.max_pool_2(x)
x = self.flatten(x)
x = self.dense(x)
return x
train.py
import os
import time
import tensorflow as tf
import pandas as pd
import matplotlib.pyplot as plt
from tensorflow.keras.models import save_model
from nets.conv_net import ConvModel
from utils.data_generator import train_val_generator
from utils.image_plot import plot_images
train_gen = train_val_generator(
data_dir='../dataset/natural-scenes/seg_train',
target_size=(64, 64),
batch_size=32,
class_mode='categorical',
subset='training')
val_gen = train_val_generator(
data_dir='../dataset/natural-scenes/seg_train',
target_size=(64, 64),
batch_size=32,
class_mode='categorical',
subset='validation')
# ImageDataGenerator的返回结果是个迭代器,调用一次才会吐一次结果,可以使用.next()函数分批读取图片。
# 取15张训练集图片进行查看
train_batch, train_label_batch = train_gen.next()
plot_images(train_batch, train_label_batch)
# 取15张测试集图片进行查看
val_batch, val_label_batch = val_gen.next()
plot_images(val_batch, val_label_batch)
# 类实例化
model = ConvModel()
'''
模型设置tf.keras.Sequential.compile
用到的参数:
- loss:损失函数,对于分类任务,如果标签没做onehot编码,一般使用"sparse_categorical_crossentropy",否则使用"categorical_crossentropy"。
- optimizer:优化器,这里选用"sgd",更多优化器请查看https://tensorflow.google.cn/api_docs/python/tf/keras/optimizers。
- metrics:评价指标,这里选用"accuracy",更多优化器请查看https://tensorflow.google.cn/api_docs/python/tf/keras/metrics。
'''
# 设置损失函数loss、优化器optimizer、评价标准metrics
model.compile(
loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.SGD(
learning_rate=0.001),
metrics=['accuracy'])
'''
模型训练tf.keras.Sequential.fit
用到的参数:
- x:输入的训练集,可以用ImageDataGenerator读取的数据。
- steps_per_epoch:输入整数,每一轮跑多少步数,这个数可以通过 图片总量/batch_size 得到,如2520/32=78.75。
- epochs:输入整数,数据集跑多少轮模型训练,一轮表示整个数据集训练一次。
- validation_data:输入的验证集,也可以用ImageDataGenerator读取的数据。
- validation_steps:输入整数,验证集跑多少步来计算模型的评价指标,一步会读取batch_size张图片,所以一共验证validation_steps * batch_size张图片。
- shuffle:每轮训练是否打乱数据顺序,默认True。
返回:
History对象,History.history属性会记录每一轮训练集和验证集的损失函数值和评价指标。
'''
history = model.fit(x=train_gen, steps_per_epoch=351,
epochs=100, validation_data=val_gen,
validation_steps=88, shuffle=True)
# 画图查看history数据的变化趋势
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.xlabel('epoch')
plt.show()
'''
模型保存tf.keras.models.save_model
用到的参数:
- model:要保存的模型,也就是搭建的keras.Sequential。
- filepath:模型保存路径。
'''
# 模型保存
# 创建保存路径
model_name = "model-" + time.strftime('%Y-%m-%d-%H-%M-%S')
model_path = os.path.join('models', model_name)
if not os.path.exists(model_path):
os.makedirs(model_path)
save_model(model=model, filepath=model_path)
test.py
from tensorflow.keras.models import load_model
from utils.data_generator import test_generator, pred_generator
from utils.image_plot import plot_images
test_gen = test_generator(
data_dir='../dataset/natural-scenes/seg_test',
target_size=(64, 64),
batch_size=32,
class_mode='categorical')
pred_gen = pred_generator(
data_dir='../dataset/natural-scenes/seg_pred',
target_size=(64, 64),
batch_size=32,
class_mode=None)
'''
模型载入tf.keras.models.load_model
用到的参数:
- filepath:载入模型存放的路径。
模型验证tf.keras.Sequential.evaluate
用到的参数:
- x:输入的验证集,可以用ImageDataGenerator读取的数据。
模型预测tf.keras.Sequential.predict
用到的参数:
- x:需要做预测的数据集,可以用ImageDataGenerator读取的数据。
'''
model_path = '../models/model-2020-07-22-16-51-19'
loaded_model = load_model(filepath=model_path)
loss, accuracy = loaded_model.evaluate(x=test_gen)
pred_batch = pred_gen.next()
pred_result = loaded_model.predict(x=pred_batch)
plot_images(pred_batch, pred_result)