AI | 第6章 深度学习 TensorFlow2 使用 keras 构建神经网络
前言
参考资料:
《B站:黑马程序员3天带你玩转Python深度学习TensorFlow框架》
《B站:卷起来!2022年B站最全最清晰的Tensorflow教程!全86讲!》
《Keras 中文文档》
《TensorFlow中文官网》
《简单粗暴 TensorFlow 2》
《TensorFlow—Keras常用API》
《机器学习实战:基于Scikit-Learn、Keras和TenserFlow 原书第2版》
1. 基本数据处理
1.1 keras 数据集
- 概述:
tf.keras.datasets
是提供 tf.keras.datasets 空间的公开 API,说直白点就是,相关机器学习的数据集,可以直接使用该 API 获取并使用数据
- 有以下几个数据集:
- boston_housing:波斯顿房屋价格回归数据集;
- cifar10:CIFAR10小图像分类数据集;
- cifar100:CIFAR100小图像分类数据集;
- fashion_mnist:Fashion-MNIST 数据集.;
- imdb:IMDB 分类数据集;
- mnist:MNIST手写数字数据集;
- reuters:路透社主题分类数据集;
- 使用步骤:(以 mnist 为例)
- 先定义数据:
mnist = tf.keras.datasets.mnist
; - 加载数据得到训练集与测试集:
(train_data, train_label), (test_data, test_label) = mnist.load_data()
- 先定义数据:
1.2 文件处理
1.3 图片处理
1.3.1 图像基本知识
- 组成图片最基本单位是 像素;
- 图片三要素:组成一张图片特征值是所有的像素值,有三个维度:图片长度、图片宽度、图片通道数;
- 图片的通道数:[0-255] -> [黑-白]
- 描述一个像素点,如果是灰度图,那么只需要一个数值来描述它,就是单通道;
- 灰度图:单通道,[长,宽,1];
- 如果一个像素点,有 RGB 三种颜色来描述它,及时三通道;
- 彩色图片:三通道,[长,宽,3],像素点为 长 X 宽 X 3;
- 描述一个像素点,如果是灰度图,那么只需要一个数值来描述它,就是单通道;
- 图片的通道数:[0-255] -> [黑-白]
- 张量形状:
- 在 TensorFlow 中用一个张量(Tensor 对象)表示一张图片;
- 单个图片:shape = [height, width, channel] 3D 张量;
- 多个图片:shape = [batch,height,width, channel],batch 表示一个批次的张量数量 4D 张量;
- 在 TensorFlow 中用一个张量(Tensor 对象)表示一张图片;
1.3.2 图片数据预处理
- 概述:
- 由于有时候图片数据很大,我们需要使用图片生成器帮助我们读取图片数据;
- 读取完后,我们需要对图片进行归一化处理;
- 由于在实际中的图片可以有残缺等,因此我们训练模型使要把这些因素(平移、缩放、翻转等)考虑进去;
- 对训练集图片做一定的变换是为了解决过拟合问题(训练精度高,测试精度低);
- 有多种操作图片数据的方法,这里使用
tensorflow.keras.preprocessing.image
模块;
- API:
from tensorflow.keras.preprocessing.image import ImageDataGenerator(rescale, rotation_range, height_shift_range, width_shift_range, shear_range, zoom_rang, channel_shift_range, horizontal_flip, fill_mode)
:构造图片生成器,返回一个生成器 datagen;- rescale:归一化;
- rotation_range:旋转角度;
- width_shift_range:横平移变换;
- height_shift_range:平纵移变换;
- shear_range:剪切范围;
- zoom_rang:缩放;
- horizontal_flip:翻转;
- fill_mode=“nearest”:填充设置;
- channel_shift_range:颜色通道;
datagen.flow_from_directory(in_path, batch_size=1, shuffle=False, save_to_dir, save_prefix='', target_size=(224, 224))
:保存变换后的图片数据,是一个二维数组格式 gen_data;- in_path:图片所在目录;
- batch_size:只用1个文件夹;
- shuffle:是否洗牌;
- save_to_dir:保存路径;
- save_prefix:图片前缀;
- target_size:目标大小224*224;
gen_data.next()
:处理下一张图片;
1.3.3 训练集与测试集的图片构造器示例
- 训练集的图片构造器:
# 数据变换处理器 - 从数据角度解决过拟合问题
train_datagen = ImageDataGenerator(
rescale=1. / 255, # 归一化
rotation_range=40, # 旋转角度
width_shift_range=0.2, # 横平移变换
height_shift_range=0.2, # 平纵移变换
shear_range=0.2, # 剪切范围
zoom_range=0.2, # 缩放
horizontal_flip=True, # 翻转
fill_mode="nearest",
)
- 测试集的图片构造器:
# 测试集不能数据变换
valid_datagen = ImageDataGenerator(rescale=1. / 255)
1.3.4 训练集与测试集的数据生成器示例
- 训练集的数据生成器:
train_generator = train_datagen.flow_from_directory(
train_dir, # 文件夹路径
target_size=(64, 64), # 指定resize成的大小,需要与模型的输入大小一致
batch_size=20, # 每次迭代20个数据
seed=7, # 随机种子
# 如果one-hot就是categorical,二分类用binary就可以
class_mode='binary',
) # 数据格式
- 测试集的数据生成器:
validation_generator = valid_datagen.flow_from_directory(
validation_dir,
target_size=(width, height),
batch_size=valid_batch_size,
seed=7,
class_mode='binary',
)
1.4 二进制数据处理
1.5 TFRecords
1.6 tf.data 模块
tf.data.Dataset.from_tensor_slices(input_data)
:将 ndarray 格式转成 tensor 格式。返回 tensors;tensors.repeat(x)
:复制 x 次数据。返回 tensors;tensors.batch(x)
:每 x 个张量数据构成 1 个 x 维张量数据,数据量变成 total / x。返回 tensors;tensors.shuffle(buffer_size=10)
:。指定一个缓冲区 buffer_size,打乱顺序。返回 tensors;- ``:;
- ``:;
- ``:;
- ``:;
- ``:;
- ``:;
- ``:;
2. 构建神经网络
2.1 概述
- 在 TensorFlow2 中,推荐使用 Keras( tf.keras )构建模型;
- Keras 有两个重要的概念: 模型(Model) 和 层(Layer) ;
- 模型:将各种层进行组织和连接,并封装成一个整体,描述了如何将输入数据通过各种层以及运算而得到输出;
- 层:将各种计算流程和变量进行了封装(如基本的全连接层,CNN 的卷积层、池化层等)
2.2 模型构建的基本思路
-
机器学习的步骤是:获取数据、数据处理、特征工程(转换器)、训练模型(估计器)、评估模型、应用;
-
在深度学习里,同样遵循机器学习基本步骤,只不过我们一般把获取数据、数据处理与特征工程放在一起。将训练模型分为下面 模型构建、模型训练 两个步骤;
-
流程:
- 数据加载 与 预处理:使用
tf.keras.datasets
获得数据集并预处理; - 模型构建:即:前向传播。使用
tf.keras.Model
和tf.keras.layers
构建模型; - 模型训练:即:反向传播。使用
tf.keras.losses
计算损失函数,并使用tf.keras.optimizer
优化模型;(常用 Adam 优化器) - 模型评估:使用
tf.keras.metrics
计算评估指标;
- 数据加载 与 预处理:使用
-
模型构建 的思路:(为了得出损失)
- 得分值、预测值:首先随机一个 权重参数W 和 偏置参数b,矩阵求 f = Wx + b 得到得分值;
- 损失值:用得分值计算损失,计算每个 max(0,错误类别得分 - 正确类别得分 +1),叠加得到损失值 loss;
- 损失值:由高次项权重参数W 可能会引起损失,loss 加上 λ*w^2 得到最终损失值(考虑了权重参数的影响)
- 由分数到概率:先 sigmoid 函数,放大差异;归一化得到概率(分数)P;有概率得损失值 L = -logP(越接近0越好)
-
模型训练 的流程:(目的是由损失更新 W 和 b)
- 从 DataLoader 中随机取一批训练数据;
- 将这批数据送入模型,计算出模型的预测值;
- 将模型预测值与真实值进行比较,计算预测值的损失函数(pre_loss);
- 计算损失函数关于模型变量的导数;
- 将求出的导数值传入优化器,使用优化器的 apply_gradients 方法更新模型参数以最小化损失函数;
2.2.1 数据加载 与 预处理
详情请见本篇第一大点《1. 基本数据处理》;
2.2.2 模型构建(前向传播)
- 一般步骤:
- 指定模型类型;
- 定义层;
1. 指定模型类型
tf.keras.model
- 两种类型的模型:Sequential()
:序列模型。是一种“从头一路走到尾”的模型。参数为 keras 支持的层及参数。返回 model 模型;Model()
:通用函数式模型。通用模型可以设计非常复杂、任意拓扑结构的神经网络,例如有向无环网络、共享层网络等。相比于序列模型只能依次线性逐层添加,通用模型能够比较灵活地构造网络结构,设定各层级的关系。返回 model 模型;
2. 定义层
tf.keras.layers
- keras 常用层及参数:- 常用层:
Dense()
:全连接层。参数如下;- activation:激活函数的选择,一般是
relu
。二分类用sigmoid
。多分类用softmax
; - kernel_initializer:权重的初始化方法。随机为 ‘random_normal’;
- bias_initializer:偏置参数的初始化方法。tf.keras.regularizers.l2(0.03) 表示使用l2 正则化,解决过拟合问题 ;
- kernel_regularizer, bias_regularizer:是否加入正则化;
- inputs:输入,可以自己指定,也可以让网络自动选择;
- unite:神经元个数,输出的维度大小;
- use_bias:是否使用偏置项,一般使用;
- activation:激活函数的选择,一般是
Dropout(rate)
:对上一层的神经元随机选取一定比例的失活,不更新,但是权重仍然保留,防止过拟合;- rate:失活的比例,0-1 的浮点数,一般为 0.5,表示每次忽略一半神经元;
Flatten()
:用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡;
- 卷积层:
Conv1D()
:一维卷积层。主要应用于以时间序列数据或文本数据。参数同二维;Conv2D()
:二维卷积层。通常应用于图像数据。参数如下;- filters:卷积核的数目(即输出的维度);
- kernel_size:卷积核的宽度和长度;
- activation:激活函数。一般用 ‘relu’;
- padding:补0策略,为“valid”, “same” 。“valid”代表只进行有效的卷积,即对边界数据不处理。“same”代表保留边界处的卷积结果,通常会导致输出shape与输入shape相同;
- use_bias:布尔值,是否使用偏置项;
Conv3D()
:三维卷积层。三维卷积对三维的输入进行滑动窗卷积。参数同二维;
- 池化层:
MaxPooling1D()
:时域信号进行最大值池化;MaxPooling2D()
:空域信号进行最大值池化号;- pool_size:长度为 2 的整数 tuple,表示在横向和纵向的下采样样子。一般为 (2,2) 表示 1/2;
- padding:和卷积层的 padding 一样,一般为 0。
MaxPooling3D()
:时空域信号进行最大值池化;
- 示例:一个二分类的模型(猫狗分类):
model = keras.models.Sequential([
# 如果训练慢,可以把数据设置的更小一些
keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu', padding='same', input_shape=[width, height, channel]), # 卷积:生成64个特征图,卷积核 3*3,激活函数(概念化),填充,输入 64*64*3
keras.layers.MaxPooling2D(pool_size=2), # 池化 - 最大池化:变成原来 1/2
keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu', padding='same'), # 卷积:生成32个特征图,卷积核 3*3,激活函数(概念化),
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=64, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=64, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=128, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=128, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
# 为全连接层准备
keras.layers.Flatten(), # 拉长数据,相当于将 立体三维特征图 变成 一维向量
keras.layers.Dense(256, activation='relu'), # 得到 512个隐形特征,容易出现过拟合
keras.layers.Dropout(0.5), # (解决过拟合)感受野,每次忽略一半神经元 0.5 rate to drop
# 猫狗分类二分类sigmoid就够了
keras.layers.Dense(1, activation='sigmoid'), # 相当于逻辑回归,映射到 0-1。多分类用activation='softmax'
])
2.2.3 模型训练(反向传播)
-
思路步骤:
- 配置训练器:
- 确定优化器;
- 确定损失函数;
- 确定评估指标;
- 训练模型;
1. 配置训练器
-
tf.keras.optimizers
- 使用优化器:- 常见优化器:TensorFlow2.3 支持的优化器
SGD(lr=0.01, momentum=0.0, decay=0.0, nesterov=False)
:随机梯度下降优化器;- lr:float >= 0. 学习率。一般为 0.01,过大可能导致损失过大;
- momentum:float >= 0. 参数,用于加速 SGD 在相关方向上前进,并抑制震荡;
- decay:float >= 0. 每次参数更新后学习率衰减值;
- nesterov:boolean. 是否使用 Nesterov 动量;
Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)
:Adam 优化器。常用于图像;- lr:float >= 0. 学习率;
- beta_1:float, 0 < beta < 1. 通常接近于 1;
- beta_2:float, 0 < beta < 1. 通常接近于 1;
- epsilon:float >= 0. 模糊因子. 若为 None, 默认为 K.epsilon();
- decay:float >= 0. 每次参数更新后学习率衰减值;
- amsgrad:boolean. 是否应用此算法的 AMSGrad 变种,来自论文 “On the Convergence of Adam and Beyond”;
- 常见优化器:TensorFlow2.3 支持的优化器
-
使用损失函数
- 常见损失函数:TensorFlow2.3 支持的损失函数
- mean_squared_error:均方误差 MSE;
- binary_crossentropy:二进制交叉熵。适用于二分类问题;
- categorical_crossentropy:分类交叉熵。适用于多分类问题,并且 labels 是 one-hot 编码;
- sparse_categorical_crossentropy:类交叉熵。适用于多分类问题,并且 labels 是 数字编码;
- 常见损失函数:TensorFlow2.3 支持的损失函数
-
使用指标
- 常见指标:TensorFlow2.3 支持的指标
- accuracy:真实标签和模型预测均为标量,如真实标签为[0,1,1,0,2,0],模型输出的预测为[0,2,1,1,2,0],此时accuracy=4/6;
- categorical_accuracy:真实标签为 one-hot 标签,模型预测为向量形式。如真实标签为[[0, 0, 1],
[0, 1, 0], [0, 1, 0], [1, 0, 0]],模型预测为[[0.1, 0.6, 0.3], [0.2, 0.7,
0.1], [0.3, 0.6, 0.1], [0.9, 0, 0.1]],此时keras会自动将向量形式的标签转换为标量形式,例如将上面那个真实标签转换为[2, 1, 1, 0],将模型预测转换为[1, 1, 1, 0],然后再第一种accuracy计算方法。; - sparse_categorical_accuracy:适用场景是真实标签为标量形式,模型预测为向量形式。如真实标签为[2, 1, 1, 0],模型预测为[[0.1, 0.6, 0.3], [0.2, 0.7, 0.1], [0.3, 0.6, 0.1], [0.9, 0, 0.1]]。此时keras会自动将模型预测转换为标量形式,即将模型预测转换为[1, 1, 1,0],然后再第一种accuracy计算方法;
- binary_accuracy:适用于二分类情况。真实标签为标量,如[2, 1, 1,
0],模型预测为标量概率,如[0.6,0.7,0.6,0.9]。该方法有一个threshold参数,该参数默认为0.5。预测概率大于threshold的设为1,小于等于threshold的设为0。所以模型预测会由[0.6,0.7,0.6,0.9]转换为[1,1,1,1],然后再第一种accuracy计算方法; - top_k_categorical_accuracy:categorical_accuracy(第二种)将模型预测转为标量标签的原理是选取预测向量中的最大值所在索引位置作为预测标签,比如将[[0.1,0.6, 0.3], [0.2, 0.7, 0.1], [0.3, 0.6, 0.1], [0.9, 0, 0.1]]转换为[1, 1, 1, 0];
- 其他指标:
- Accuracy:准确率,用于分类任务;
- Mean: 平均值;
- TruePositives:真正例的数量,用于二分类任务,(真正例:实际类别和预测类别都为正,简写 TP );
- TrueNegatives:真负例的数量,用于二分类任务,(真反例:实际类别和预测类别都为负,简写 TN )
- FalsePositives:假正例的数量,用于二分类任务,(假正例:预测为正,实际为负,简写 FP );
- FalseNegatives:假负例的数量,用于二分类任务,(假反例:预测为负,实际为正,简写 FN );
- Precision:精确率,用于二分类任务,Precision = TP/(TP+FP));
- Recall:召回率,用于二分类任务,Recall = TP/(TP+FN);
- AUC:用于二分类任务的一个指标,可以理解为正样本的预测值大于负样本的概率;
- MSE:均方误差,用于回归任务,可作为损失函数;
- MAE:均绝对误差,用于回归任务,可以作为损失函数;
- RMSE:均方根误差,用于回归任务,可作为损失函数,由MSE开方即可得到;
- 常见指标:TensorFlow2.3 支持的指标
-
API:
model.compile(optimizer, loss, metrics)
- optimizer:优化器;
- loss:损失函数;
- metrics:评估指标;
2. 训练模型
- API:
model.fit(x, y, batch_size=32, epochs=10, verbose=1, callbacks=None,validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0)
:与 fit_generator 类似,区别在于:fit 把所有数据读进内存;fit_generator 相当于生成器,动态生产所需 batch 数据。返回一个 History 的对象,其 History.history 属性记录了损失函数和其他指标的数值随 epoch 变化的情况,如果有验证集的话,也包含了验证集的这些指标变化情况;- x:输入数据。如果模型只有一个输入,那么x的类型是numpy;
- array,如果模型有多个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpy array;
- y:标签,numpy array;
- batch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降,使目标函数优化一步;
- epochs:整数,训练终止时的epoch值,训练将在达到该epoch值时停止,当没有设置initial_epoch时,它就是训练的总轮数,否则训练的总轮数为epochs - inital_epoch;
- verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch输出一行记录;
- callbacks:list,其中的元素是keras.callbacks.Callback的对象。这个list中的回调函数将会在训练过程中的适当时机被调用,参考回调函数;
- validation_split:0~1之间的浮点数,用来指定训练集的一定比例数据作为验证集。验证集将不参与训练,并在每个epoch结束后测试的模型的指标,如损失函数、精确度等。注意,validation_split的划分在shuffle之前,因此如果你的数据本身是有序的,需要先手工打乱再指定validation_split,否则可能会出现验证集样本不均匀;
- validation_data:形式为(X,y)的tuple,是指定的验证集。此参数将覆盖validation_spilt;
- shuffle:布尔值或字符串,一般为布尔值,表示是否在训练过程中随机打乱输入样本的顺序。若为字符串“batch”,则是用来处理HDF5数据的特殊情况,它将在batch内部将数据打乱;
- class_weight:字典,将不同的类别映射为不同的权值,该参数用来在训练过程中调整损失函数(只能用于训练);
- sample_weight:权值的numpy array,用于在训练时调整损失函数(仅用于训练)。可以传递一个1D的与样本等长的向量用于对样本进行1对1的加权,或者在面对时序数据时,传递一个的形式为(samples,sequence_length)的矩阵来为每个时间步上的样本赋不同的权。这种情况下请确定在编译模型时添加了sample_weight_mode=’temporal’;
- initial_epoch: 从该参数指定的epoch开始训练,在继续之前的训练时有用
model.fit_generator(train_data, steps_per_epoch=100, epochs=20, validation_data=, validation_steps=50, verbose=2)
:;- train_data:训练数据生成器;
- steps_per_epoch:每次迭代生成100个数据。其中 100*20 > 训练数据总数;
- epochs:迭代20次;
- validation_data:目标数据生成器;
- validation_steps:每次迭代生成50个数据。其中 100*20 > 训练数据总数;
- verbose:日志显示。0为不输出日志,1为显示进度条,2为每个epoch输出一行记录;
2.2.4 模型评估
- 这里的评估一般指训练后的评估,前面讲的是训练时的评估;
1. 一般评估
- API:
model.predict(x_test)
:传入训练集,返回预测值,一般用来与真实值对比;model.evaluate(x_test, y_test)
:返回测试集评估结果,类型为数组,可以通过数组下标访问;model.summary()
:打印展示模型结构;
2. 画图展示
- 获取训练时的评估值:
history = model.fit()
; - 假设练时的评估值有:acc,loss,val_acc,val_loss,则可以利用 matplotlib.pyplot 进行画图:
# 4.2 画图展示
acc = history.history['acc'] # 训练数据准确率
val_acc = history.history['val_acc'] # 验证数据准确率
loss = history.history['loss'] # 训练集 loss
val_loss = history.history['val_loss'] # 验证集 loss
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training accuracy') # 训练集精确率数据,点图例
plt.plot(epochs, val_acc, 'b', label='Validation accuracy') # 测试集精确率数据,线图例
plt.title('Training and validation accuracy') # 精确率图表标题
plt.figure()
plt.show() # 展示图表
plt.plot(epochs, loss, 'bo', label='Training Loss') # 训练集损失值数据,点图例
plt.plot(epochs, val_loss, 'b', label='Validation Loss') # 测试集损失值数据,线图例
plt.title('Training and validation loss') # 损失值图表标题
plt.legend() # 添加图例
plt.show() # 展示图表
2.3 自定义模型
- 通过继承
tf.keras.Model
类实现:- 重写 init() (构造函数,初始化)和 call(input) (模型调用)两个方法;
class MyModel(tf.keras.Model):
def __init__(self):
super().__init__() # Python 2 下使用 super(MyModel, self).__init__()
# 此处添加初始化代码(包含 call 方法中会用到的层),例如
# layer1 = tf.keras.layers.BuiltInLayer(...)
# layer2 = MyCustomLayer(...)
def call(self, input):
# 此处添加模型调用的代码(处理输入并返回输出),例如
# x = layer1(input)
# output = layer2(x)
return output
# 还可以添加自定义的方法
3. Keras 架构
3.1 Keras 常见网络层
3.2 Keras 网络配置
3.3 Keras 预处理功能
4. 参数调节经验总结
4.1 解决过拟合问题
- 深度学习往往伴随着过拟合问题,我们需要降低拟合程度使模型尽量简单;
- 当acc > val_acc,说明受高此项影响;
- 可调参数:
- 数据变换处理器,对训练集图像进行变换处理。train_datagen = ImageDataGenerator;
- 增加 Dropout 层,每次训练忽略一半神经元。keras.layers.Dropout(0.5);
4.2 从二分类到多分类
- 二分类与多分类往往有以下些参数不同:
属性参数 | 中文含义 | 二分类 | 多分类 |
---|---|---|---|
train_datagen.flow_from_directory(class_mode=‘binary’, …) | 图像生成器类型 | binary | categorical |
keras.layers.Dense(activation=‘sigmoid’, …) | 全连接层 | sigmoid | softmax |
model.compile(loss=‘binary_crossentropy’, …) | 损失函数 | binary_crossentropy | categorical_crossentropy(labels 是 one-hot 编码) 或 sparse_categorical_crossentropy(labels 是数字编码) |
5. 代码示例
5.1 二分类图像识别问题:猫狗分类
if __name__ == "__main__":
# 1. 数据准备
# 1.1 数据所在文件夹
base_dir = '../resources/p02_deep_learning_tensorFlow/dog_and_cat/'
train_dir = os.path.join(base_dir, 'train/')
validation_dir = os.path.join(base_dir, 'validation/')
# # 训练集
# train_cats_dir = os.path.join(train_dir, 'cats')
# train_dogs_dir = os.path.join(train_dir, 'dogs')
#
# # 验证集
# validation_cats_dir = os.path.join(validation_dir, 'cats')
# validation_dogs_dir = os.path.join(validation_dir, 'dogs')
# 2 数据预处理
# 数据变换处理器 - 从数据角度解决过拟合问题
train_datagen = ImageDataGenerator(
rescale=1. / 255, # 归一化
rotation_range=40, # 旋转角度
width_shift_range=0.2, # 横平移变换
height_shift_range=0.2, # 平纵移变换
shear_range=0.2, # 剪切范围
zoom_range=0.2, # 缩放
horizontal_flip=True, # 翻转
fill_mode="nearest",
)
# 测试集不能数据变换
valid_datagen = ImageDataGenerator(rescale=1. / 255)
# 图像处理数据, GPU 可以 224*224*3
width = 64
height = 64
channel = 3
batch_size = 20 # 训练集每次迭代20个数据
valid_batch_size = 20 # 测试集每次迭代20个数据
# 2.1 从文件夹读取数据,并且每次读 20 个
train_generator = train_datagen.flow_from_directory(
train_dir, # 文件夹路径
target_size=(width, height), # 指定resize成的大小,需要与模型的输入大小一致
batch_size=batch_size, # 每次迭代20个数据
seed=7, # 随机种子
# 如果one-hot就是categorical,二分类用binary就可以
class_mode='binary',
) # 数据格式
# 查看数据:train_generator.next()
validation_generator = valid_datagen.flow_from_directory(
validation_dir,
target_size=(width, height),
batch_size=valid_batch_size,
seed=7,
class_mode='binary',
)
# 3. 特征工程
# 估计器:基于 Keras 构建网络模型
# 3.1 前向传播:按顺序构造网络模型
model = keras.models.Sequential([
# 如果训练慢,可以把数据设置的更小一些
keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu', padding='same', input_shape=[width, height, channel]), # 卷积:生成64个特征图,卷积核 3*3,激活函数(概念化),填充,输入 64*64*3
keras.layers.MaxPooling2D(pool_size=2), # 池化 - 最大池化:变成原来 1/2
keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu', padding='same'), # 卷积:生成32个特征图,卷积核 3*3,激活函数(概念化),
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=64, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=64, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=128, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
keras.layers.Conv2D(filters=128, kernel_size=3, activation='relu', padding='same'), # 卷积
keras.layers.MaxPooling2D(pool_size=2), # 池化
# 为全连接层准备
keras.layers.Flatten(), # 拉长数据,相当于将 立体三维特征图 变成 一维向量
keras.layers.Dense(256, activation='relu'), # 得到 512个隐形特征,容易出现过拟合
keras.layers.Dropout(0.5), # (解决过拟合)感受野,每次忽略一半神经元 0.5 rate to drop
# 猫狗分类二分类sigmoid就够了
keras.layers.Dense(1, activation='sigmoid'), # 相当于逻辑回归,映射到 0-1。多分类用activation='softmax'
])
# model.summary() # 展示逻辑结构
# 3.2 反向传播:配置训练器
model.compile(loss='binary_crossentropy',
optimizer=Adam(lr=1e-4),
metrics=['acc']) # 由于是二分类,使用准确率作为评估标准
# 3.3 训练模型
# fit 把所有数据读进内存;fit_generator 相当于生成器,动态生产所需 batch 数据
history = model.fit(
train_generator, # 训练数据生成器
steps_per_epoch=100, # 每次迭代生成100个数据,至少需要跑 100次才能遍历完2000个数据。2000 images = batch_size * steps
epochs=60, # 迭代20次(每个 batch 是 20 个数据)
validation_data=validation_generator, # 目标数据生成器
validation_steps=50, # 1000 images = batch_size * steps
verbose=2)
# 4. 模型评估
# 4.1 评估器评估
# 4.2 画图展示
acc = history.history['acc'] # 训练数据准确率
val_acc = history.history['val_acc'] # 验证数据准确率
loss = history.history['loss'] # 训练集 loss
val_loss = history.history['val_loss'] # 验证集 loss
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training accuracy') # 训练集精确率数据,点图例
plt.plot(epochs, val_acc, 'b', label='Validation accuracy') # 测试集精确率数据,线图例
plt.title('Training and validation accuracy') # 精确率图表标题
plt.figure()
plt.show() # 展示图表
plt.plot(epochs, loss, 'bo', label='Training Loss') # 训练集损失值数据,点图例
plt.plot(epochs, val_loss, 'b', label='Validation Loss') # 测试集损失值数据,线图例
plt.title('Training and validation loss') # 损失值图表标题
plt.legend() # 添加图例
plt.show() # 展示图表