语义分割-地表建筑物识别的一种解决方案
一,前言
该篇博客是用于记录阿里天池上的一个比赛——语义分割-地表建筑物识别的整个学习,编程的过程,由于是该比赛是我做的第一个机器学习的项目,因此记录的比较详细,细致到每一个函数的用法,和学习时参考的博客。该篇博客的目的主要是供自己作为笔记使用,其次是给对语义分割感兴趣,或者正在参加相关比赛的同学提供一个参考。博客较长,难免有错误,欢迎留言指正,同时欢迎相互交流学习!(另外在此尤其感谢Bubbliiiing的分享,这位大佬分享了大量的课程,代码,博客,非常详细)
二,参考博客
1,为什么卷积核为四维
2,MobileNet模型的复现详解
3,基于Mobile模型的segnet讲解
4,阿里天池比赛地表建筑物识别(点击获取比赛详情)
5, 深度可分离卷积Depthwise Separable Convolution
6, 相关课程
7,Moblie_Unet——主要参考
三,函数
- MonlieNet及SegNet部分函数
1,Conv2D 卷积
keras.layers.Conv2D(filters, kernel_size,
strides=(1, 1),
padding='valid',
data_format=None,
dilation_rate=(1, 1),
activation=None,
use_bias=True,
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None)
- 参数(这里主要会用到下面四个参数)
filter:整数,卷积输出滤波器的数量。
kernel_size:2个整数或2个整数构成的元组/列表,指定2-dim卷积窗口的高度和宽度。可以是单个整数,以指定具有相同值的所有空间维度。
strides:2个整数或2个整数构成的元组/列表,指定沿着高度和宽度卷积的步长,如果是单个整数则指定所有的空间维度具有相同的值。
padding:有“valid”或“same”
这里对padding作进一步解释点击查看, - 在此项目中padding都是用valid即不填充,而是在Conv2D之前使用ZeroPadding2d来手动填充0
2, ZeroPadding2d 2D 输入的零填充层
官方解释
一个博客的解释
keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)
2D 输入的零填充层(例如图像)。
该图层可以在图像张量的顶部、底部、左侧和右侧添加零表示的行和列。
参数
padding: 整数,或 2 个整数的元组,或 2 个整数的 2 个元组。
如果为整数:将对宽度和高度运用相同的对称填充。
如果为 2 个整数的元组:
如果为整数:: 解释为高度和高度的 2 个不同的对称裁剪值: (symmetric_height_pad, symmetric_width_pad)。
如果为 2 个整数的 2 个元组: 解释为 ((top_pad, bottom_pad), (left_pad, right_pad))。
data_format: 字符串, channels_last (默认) 或 channels_first 之一, 表示输入中维度的顺序。channels_last 对应输入尺寸为 (batch, height, width, channels), channels_first 对应输入尺寸为 (batch, channels, height, width)。 它默认为从 Keras 配置文件 ~/.keras/keras.json 中 找到的 image_data_format 值。 如果你从未设置它,将使用 “channels_last”。
输入尺寸
如果 data_format 为 “channels_last”, 输入 4D 张量,尺寸为 (batch, rows, cols, channels)。
如果 data_format 为 “channels_first”, 输入 4D 张量,尺寸为 (batch, channels, rows, cols)。
输出尺寸
如果 data_format 为 “channels_last”, 输出 4D 张量,尺寸为 (batch, padded_rows, padded_cols, channels)。
如果 data_format 为 “channels_first”, 输出 4D 张量,尺寸为 (batch, channels, padded_rows, padded_cols)。
3,BatchNormalization批量标准化层
keras.layers.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer='zeros', gamma_initializer='ones', moving_mean_initializer='zeros', moving_variance_initializer='ones', beta_regularizer=None, gamma_regularizer=None, beta_constraint=None, gamma_constraint=None)
在每一个批次的数据中标准化前一层的激活项, 即,应用一个维持激活项平均值接近 0,标准差接近 1 的转换。
参数
axis: 整数,需要标准化的轴 (通常是特征轴)。 例如,在 data_format=“channels_first” 的 Conv2D 层之后, 在 BatchNormalization 中设置 axis=1。
momentum: 移动均值和移动方差的动量。
epsilon: 增加到方差的小的浮点数,以避免除以零。
center: 如果为 True,把 beta 的偏移量加到标准化的张量上。 如果为 False, beta 被忽略。
scale: 如果为 True,乘以 gamma。 如果为 False,gamma 不使用。 当下一层为线性层(或者例如 nn.relu), 这可以被禁用,因为缩放将由下一层完成。
beta_initializer: beta 权重的初始化方法。
gamma_initializer: gamma 权重的初始化方法。
moving_mean_initializer: 移动均值的初始化方法。
moving_variance_initializer: 移动方差的初始化方法。
beta_regularizer: 可选的 beta 权重的正则化方法。
gamma_regularizer: 可选的 gamma 权重的正则化方法。
beta_constraint: 可选的 beta 权重的约束方法。
gamma_constraint: 可选的 gamma 权重的约束方法。
输入尺寸
可以是任意的。如果将这一层作为模型的第一层, 则需要指定 input_shape 参数 (整数元组,不包含样本数量的维度)。
输出尺寸
与输入相同。
4,Activation 激活函数
见博客
以上四个函数通常是一个卷积层的基本结构,即一个卷积层通常是先ZeroPadding2d零填充,再Conv2D 卷积,再BatchNormalization批量标准化层,最后Activation 激活函数
5,UpSampling2D 2D 输入的上采样层
官方文档
沿着数据的行和列分别重复 size[0] 和 size[1] 次。
参数
size: 整数,或 2 个整数的元组。 行和列的上采样因子。
data_format: 字符串, channels_last (默认) 或 channels_first 之一, 表示输入中维度的顺序。channels_last 对应输入尺寸为 (batch, height, width, channels), channels_first 对应输入尺寸为 (batch, channels, height, width)。 它默认为从 Keras 配置文件 ~/.keras/keras.json 中 找到的 image_data_format 值。 如果你从未设置它,将使用 “channels_last”。
interpolation: 字符串,nearest 或 bilinear 之一。 注意 CNTK 暂不支持 bilinear upscaling, 以及对于 Theano,只可以使用 size=(2, 2)。
输入尺寸
如果 data_format 为 “channels_last”, 输入 4D 张量,尺寸为 (batch, rows, cols, channels)。
如果 data_format 为 “channels_first”, 输入 4D 张量,尺寸为 (batch, channels, rows, cols)。
输出尺寸
如果 data_format 为 “channels_last”, 输出 4D 张量,尺寸为 (batch, upsampled_rows, upsampled_cols, channels)。
如果 data_format 为 “channels_first”, 输出 4D 张量,尺寸为 (batch, channels, upsampled_rows, upsampled_cols)。
6, DepthwiseConv2D深度可分离 2D 卷积
- 这个函数与Pointwise Convolution(逐点卷积)搭配食用构成MobileNet的核心,可参考这篇博客,讲的非常详细点击前往
keras.layers.DepthwiseConv2D(kernel_size, strides=(1, 1), padding='valid', depth_multiplier=1, data_format=None, activation=None, use_bias=True, depthwise_initializer='glorot_uniform', bias_initializer='zeros', depthwise_regularizer=None, bias_regularizer=None, activity_regularizer=None, depthwise_constraint=None, bias_constraint=None)
官方文档
深度可分离 2D 卷积。
深度可分离卷积包括仅执行深度空间卷积中的第一步(其分别作用于每个输入通道)。 depth_multiplier 参数控制深度步骤中每个输入通道生成多少个输出通道。
Arguments
kernel_size: 一个整数,或者 2 个整数表示的元组或列表, 指明 2D 卷积窗口的高度和宽度。 可以是一个整数,为所有空间维度指定相同的值。
strides: 一个整数,或者 2 个整数表示的元组或列表, 指明卷积沿高度和宽度方向的步长。 可以是一个整数,为所有空间维度指定相同的值。 指定任何 stride 值 != 1 与指定 dilation_rate 值 != 1 两者不兼容。
padding: “valid” 或 “same” (大小写敏感)。
depth_multiplier: 每个输入通道的深度方向卷积输出通道的数量。 深度方向卷积输出通道的总数将等于 filterss_in * depth_multiplier。
data_format: 字符串, channels_last (默认) 或 channels_first 之一,表示输入中维度的顺序。 channels_last 对应输入尺寸为 (batch, height, width, channels), channels_first 对应输入尺寸为 (batch, channels, height, width)。 它默认为从 Keras 配置文件 ~/.keras/keras.json 中 找到的 image_data_format 值。 如果你从未设置它,将使用「channels_last」。
activation: 要使用的激活函数 (详见 activations)。 如果你不指定,则不使用激活函数 (即线性激活: a(x) = x)。
use_bias: 布尔值,该层是否使用偏置向量。
depthwise_initializer: 运用到深度方向的核矩阵的初始化器 详见 initializers)。
bias_initializer: 偏置向量的初始化器 (详见 initializers)。
depthwise_regularizer: 运用到深度方向的核矩阵的正则化函数 (详见 regularizer)。
bias_regularizer: 运用到偏置向量的正则化函数 (详见 regularizer)。
activity_regularizer: 运用到层输出(它的激活值)的正则化函数 (详见 regularizer)。
depthwise_constraint: 运用到深度方向的核矩阵的约束函数 (详见 constraints)。
bias_constraint: 运用到偏置向量的约束函数 (详见 constraints)。
输入尺寸
如果 data_format=‘channels_first’, 输入 4D 张量,尺寸为 (batch, channels, rows, cols)。
如果 data_format=‘channels_last’, 输入 4D 张量,尺寸为 (batch, rows, cols, channels)。
输出尺寸
如果 data_format=‘channels_first’, 输出 4D 张量,尺寸为 (batch, filters, new_rows, new_cols)。
如果 data_format=‘channels_last’, 输出 4D 张量,尺寸为 (batch, new_rows, new_cols, filters)。
由于填充的原因, rows 和 cols 值可能已更改。
7,reshape
keras.backend.reshape(x, shape)
将张量重塑为指定的尺寸。
参数
x: 张量或变量。
shape: 目标尺寸元组。
返回
一个张量。
- train部分函数
8,np.random.shuffle打乱数据
9, img.resize图像变换大小
博客解释
这个函数img.resize((width, height),Image.ANTIALIAS)
第一个参数为目标图像的大小
第二个参数:
Image.NEAREST :低质量
Image.BILINEAR:双线性
Image.BICUBIC :三次样条插值
Image.ANTIALIAS:高质量
10,np.array(img)/255 将标签图片转换为数组
博客解释
此处除以255是将数据归一化处理,因为RGB最大的数值为255,处以255可以将RGB值缩小到0-1的范围
11,.append(img)在末尾增加新的对象
12, one_hot_label = np.eye(NCLASSES)[np.array(label, np.int32)]
one-hot编码,将像素分成两类
博客解释
此处NCLASSES是一个全局变量2,(背景+建筑物=2)表示对应的类别号为2,这一步的作用是将图片的数据转化成用0和1表示的两列的one-hot向量(下面是一个直观的效果展示)
a=np.eye(2)[[1,0,0,0,1,0,0,1]]
print(a)
[[0. 1.]
[1. 0.]
[1. 0.]
[1. 0.]
[0. 1.]
[1. 0.]
[1. 0.]
[0. 1.]]
13, yield (np.array(X_train), np.array(Y_train))
菜鸟教程
简单地讲,yield 的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。(其功能相当于一个用来保存X-train,Y-train的数组,但优点在于它不会占用太多的内存,内存的占用始终为一个常数,并且使用起来非常的简洁,具体的解释参考菜鸟教程)
14,np.random.shuffle(lines)将图片打乱,更有利于训练
- 训练参数的设置
15,深度学习三个概念Epoch, Batch, Iteration
- 训练参数的设置
16,ModelCheckpoint用于设置权值保存的细节
checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
monitor='val_loss', save_weights_only=True, save_best_only=False, period=2)
#将训练好的参数保存到logs文件中
log_dir为权值保存的文件夹路径
理解loss和val_loss
monitor='val_loss’用于指定保存损失还是准确率,因为损失的感受不是很直观,而准确率为0到1,且越高越好,因此这里用val_loss,保存准确率
save_best_only=False指定每次保存是否都是比上次好,这里为False,不管是否比上次好都保存
save_weights_only=True是否只保存模型的权重还是将整个模型的结构也保存下来,一般在保持权重,模型结构没什么用,且占用空间
period=2:CheckPoint之间的间隔的epoch数,即训练多少轮保存一次权重,此处训练2轮保存一次权重
17, ReduceLROnPlateau设置学习率下降的方式
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1)
monitor='val_loss’指定监测的指标,这里指定的是val_loss,即测试集的损失率,当损失率不再下降时,改变学习率。
factor=0.5 指定学习率下降的比例,此处表示变为原来的一半
patience=3即忍受多少代损失率不下降后调整学习率
verbose=1即是否在终端打印出学习率改变的信息,此处为显示
18,EarlyStopping早停
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)
原理
将数据分为训练集和验证集
每个epoch结束后(或每N个epoch后): 在验证集上获取测试结果,随着epoch的增加,如果在验证集上发现测试误差上升,则停止训练;
将停止之后的权重作为网络的最终参数。
这种做法很符合直观感受,因为精度都不再提高了,在继续训练也是无益的,只会提高训练的时间。那么该做法的一个重点便是怎样才认为验证集精度不再提高了呢?并不是说验证集精度一降下来便认为不再提高了,因为可能经过这个Epoch后,精度降低了,但是随后的Epoch又让精度又上去了,所以不能根据一两次的连续降低就判断不再提高。一般的做法是,在训练的过程中,记录到目前为止最好的验证集精度,当连续10次Epoch(或者更多次)没达到最佳精度时,则可以认为精度不再提高了。
EarlyStopping的参数:
monitor: 监控的数据接口,有’acc’,’val_acc’,’loss’,’val_loss’等等。正常情况下如果有验证集,就用’val_acc’或者’val_loss’。此处笔者使用的是val_loss即测试集损失率。
min_delta:增大或减小的阈值,只有大于这个部分才算作improvement。这个值的大小取决于monitor,也反映了你的容忍程度。此处笔者的为0,表示一旦减少就算超过阀值。
patience:能够容忍多少个epoch内都没有improvement。这个设置其实是在抖动和真正的准确率下降之间做tradeoff。如果patience设的大,那么最终得到的准确率要略低于模型可以达到的最高准确率。如果patience设的小,那么模型很可能在前期抖动,还在全图搜索的阶段就停止了,准确率一般很差。patience的大小和learning rate直接相关。在learning rate设定的情况下,前期先训练几次观察抖动的epoch number,比其稍大些设置patience。在learning rate变化的情况下,建议要略小于最大的抖动epoch number。此处笔者设置的为10,即检测到有10次损失率下降就停止训练。
mode: 就’auto’, ‘min’, ‘,max’三个可能。如果知道是要上升还是下降,建议设置一下。
min_delta和patience都和“避免模型停止在抖动过程中”有关系,所以调节的时候需要互相协调。通常情况下,min_delta降低,那么patience可以适当减少;min_delta增加,那么patience需要适当延长;反之亦然。
verbose=1即是否在终端打印出相关的信息,此处为显示。
19,model.compile用于配置训练模型
model.compile(loss = 'categorical_crossentropy',
optimizer = Adam(lr=lr),
metrics = ['accuracy'])
loss定义损失函数,此处为交叉熵损失函数
optimizer:选择优化器
optimizer
Adam此处lr=lr是指定习效率为lr
metrics:评价函数,与损失函数类似,只不过评价函数的结果不会用于训练过程中,可以传递已有的评价函数名称,或者传递一个自定义的theano/tensorflow函数来使用
20,model.fit_generator 使用 Python 生成器(或 Sequence 实例)逐批生成的数据,按批次训练模型(这一步是前面的准备好后开始训练了)
model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size),
steps_per_epoch=max(1, num_train//batch_size),
validation_data=generate_arrays_from_file(lines[num_train:], batch_size),
validation_steps=max(1, num_val//batch_size),
epochs=50,
initial_epoch=0,
callbacks=[checkpoint, reduce_lr,early_stopping])
生成器与模型并行运行,以提高效率。 例如,这可以让你在 CPU 上对图像进行实时数据增强,以在 GPU 上训练模型。
generate_arrays_from_file(lines[:num_train], batch_size)定义的一个生成器函数,所谓的生成器函数就是用来读取原图和标签图的一个函数,,lines[:num_train],这里lines是指用于存放train.txt的文件,即用于保存原图与标签图的对应关系的文件,数据类似于‘131.jpg;131.png’;[:num_train]是用来界定训练集和测试集数据的,设置的是90%为训练集,num_train,为90%图片数量的界线;batch_size用于设定每一批训练图片的数量,此处设置为4,可以根据电脑的性能调高或降低
steps_per_epoch=max(1, num_train//batch_size) 在声明一个 epoch 完成并开始下一个 epoch 之前从 generator 产生的总步数(批次样本)。 它通常应该等于你的数据集的样本数量除以批量大小。 对于 Sequence,它是可选的:如果未指定,将使用len(generator) 作为步数。总结来讲就是训练的批次数。
validation_data: 它可以是以下之一:
验证数据的生成器或 Sequence 实例一个 (inputs, targets) 元组一个 (inputs, targets, sample_weights) 元组。在每个 epoch 结束时评估损失和任何模型指标。该模型不会对此数据进行训练。在这里validation_data=generate_arrays_from_file(lines[num_train:], batch_size)。用来指定验证集的位置。(validation:验证)
validation_steps: 仅当 validation_data 是一个生成器时才可用。 在停止前 generator 生成的总步数(样本批数)。 对于 Sequence,它是可选的ÿ