本次经验的分享主要以一个二分类的垃圾分类的例子展开的。
首先值得一提的就是图像分类不像图像检测和实例分割,不需要花费很长时间在数据集的处理上【图像分类不需要对图片进行打标签的处理】
一、数据集的预处理
- 图像数据的处理(图像数据归一化的操作)
- 图像标签数据的处理(标签值实行one-hot编码)
- 数据集的划分(训练集和验证集比例)
- 图像增强(作用)
下面我们将从上面几点对数据集的预处理进行阐述。
(1)图像数据的处理中最重要的部分就是对图像数据进行归一化的操作。那么归一化后有什么好处呢?
原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。
对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。
总来的来说就是归一化的好处:1、能够让训练数据分布在同一个区域。2、加快网络训练的速度。
(2)图像标签数据的处理,这一部分需要实现标签值one-hot编码。那么这边为啥需要进行one-hot编码呢?
我目前的理解就是结合损失函数【例如交叉熵损失】进行联合使用,下面我会进行详细的阐述。
(3)数据集的划分,这一部分就是当我们的数据集混合在一起,并没有明确地区分数据集和验证集的时候,我们需要进行的一步。我们需要按照比例进行划分。【82开,73开等等】具体的比例按照你的实验进行处理。
(4)数据增强的作用
数据增强这一环节不仅仅是为了增加数据集的规模,它的另一个作用就是当训练集的准确率很高的时候,验证集的准确率上不去的时候,可以有效地提高验证集的准确率。【这是目前可以采用的一个策略之一】
二、模型的构造
- 自建模型
- 使用网上比较好的预训练模型
下面我将从这两个方面对模型的构造(tensorflow)进行阐述。
当时一个神经网络的作业(建一个网络去模拟一个函数),我的舍友是使用pytorch进行做的,我发现pytorch在建造一个自定义的layer层还是很方便的,而且loss function那方面自定义还简单一些。
不过我今天就是主要从tensorflow的方向进行阐述。
(1)自建模型
tensorflow之曲线拟合,里面有自己自建的add_layer
https://www.cnblogs.com/maskerk/p/9951850.html
这边的话,其实我想讲并不是以上什么自定义层add_layer。我想讲的是我垃圾二分类自定义的一个网络。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D,MaxPooling2D,Flatten,Dense,Dropout
# 构建模型
model = Sequential()
model.add(Conv2D(filters=64,
kernel_size=(16,16),
padding = 'same',
input_shape=(225,225,3),
activation = 'relu'))
model.add(MaxPooling2D(pool_size=(3,3)))
model.add(Conv2D(filters = 32,
kernel_size=(8,8),
padding = 'same',
activation = 'relu'))
model.add(MaxPooling2D(pool_size=(3,3)))
model.add(Flatten())
model.add(Dense(128,activation = 'relu'))
model.add(Dropout(0.5))
model.add(Dense(2,activation = 'softmax'))
print(model.summary())
这是该自建网络结构。这边其实你需要了解的点还是很多的。【例如你如何对这些网络中间层进行命名;这个Output Shape是啥;后面参数的个数如何进行计算等等】
这边我主要讲解参数的个数如何进行计算的问题。主要分为卷积层和全连接层。
分享我看到的讲解比较好的blog
详解keras的model.summary()输出参数Param计算过程
https://blog.csdn.net/ybdesire/article/details/85217688
(2)使用网上比较好的预训练模型
其实在卷积网络这一块前人已经做的很好了。
● LeNet:卷积网络的第一个成功应用是由Yann LeCun于1990年代开发的。其中最著名的是LeNet架构,用于读取邮政编码,数字等。
● AlexNet::卷积网络在计算机视觉中的第一个应用是AlexNet, 由亚历克斯:克里维斯基,伊利亚萨茨基弗和吉奥夫欣顿发展。AlexNet在2012年被提交给lmageNet ILSVRC挑战,明显优于第二名。该网络与LeNet具有非常相似的体系结构,但是使用更多层数,更大和更具特色的卷积层。
● ZFNet:ILSVRC 2013获奖者是Matthew Zeiler和Rob Fergus的卷积网络。它被称为ZFNet (Zeiler & FergusNet的缩写)。通过调整架构超参数,特别是通过扩展中间卷积层的大小,使第-层的步幅和过滤器尺寸更小,对AlexNet的改进。
● GoogleNet.:ILSVRC 2014获奖者是Szegedy等人的卷积网络。来自Google. 其主要贡献是开发-一个初始模块,大大减少了网络中的参数数量(4M, 与AlexNet的60M相比) 。GoogLeNet还有几个后续版本,最近的是Inception-v4.
● VGGNet: 2011年ILSVRC的亚军是来自Karen Simonyan和Andrew Zisserman的网络,被称为VGGNet. 它的主要贡献在于表明网络的深度是良好性能的关键组成部分。他们最终的网络包含16个CONV/FC层,并且具有非常均匀的架构,从始至终只能执行3x3卷积和2x2池化。VGGNet的缺点是使用更多的内存和参数更多。
● ResNet.:Kaiming He等人开发的残差网络是ILSVRC 2015的获胜者。它大量使用特殊的跳过连接和批量归一化。该架构在网络末端也没有使用全连接层。ResNets目前是迄今为止最先进的卷积神经网络模型。
在垃圾二分类中,我使用到的网络模型是Inception_ResNet_v2。
from tensorflow.keras.applications.inception_resnet_v2 import InceptionResNetV2
base_net = InceptionResNetV2(include_top = False,input_shape=(225,225,3))
base_net.trainable = False
model = Sequential()
model.add(base_net)
model.add(Flatten())
model.add(Dense(128,activation='relu',kernel_initializer='he_normal'))
model.add(Dropout(0.5))
model.add(Dense(2,activation='softmax'))
这边的话,其实还有激活函数、权重的初始值可以进行阐述,并且需要讲解明白。【这边看情况,之后学习时间不紧,我会把这边的写好,讲明白的。】
我就简单阐述一下,我这边的激活函数以及权重的初始值。
激活函数的话,不要使用sigmoid函数,原因嘛?容易出现梯度爆炸或梯度消失的现象。
kernel_initializer=‘he_normal’,这个权重矩阵赋初值,这边he_normal只是和relu激活函数比较匹配。
三、模型的编译
- 模型的损失函数
- 模型的优化函数
- 模型的评判标准
下面我将从以上三点对模型的编译进行阐述。
(1)模型的损失函数
模型的损失函数选择非常多,现有就非常多了,例如L1,L2范式、均方误差(mse)、交叉熵。当然,如果你有更好的想法,你可以自定义你的损失函数。
现有损失函数讲解的比较好的:
目标函数或称损失函数,是网络中的性能函数。
https://blog.csdn.net/weixin_30919429/article/details/99088970
自定义损失函数的一个小例子:
tensorflow2.0 - 自定义loss function(损失函数)
https://blog.csdn.net/qq_32623363/article/details/104154418
(2)模型的优化函数
常见的优化函数:
Adam是实际学习中最常用的算法,其是momentum和RMSProp算法的结合。它对RMSProp优化器的更新,利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。
# 开始编译模型
print("compiling model...")
# 设置损失函数、优化函数和评判度量
INIT_LearnRate = 1e-4
EPOCHS = 3
opt = Adam(lr = INIT_LearnRate, decay = INIT_LearnRate / EPOCHS)
model.compile(loss = "binary_crossentropy", optimizer = opt, metrics = ["accuracy"])
print("compiling finish!!!")
机器学习的常见优化方法
https://www.cnblogs.com/GeekDanny/p/9655597.html
(3)模型的评价标准
metrics = ["accuracy"]
Keras.metrics中的accuracy总结
https://zhuanlan.zhihu.com/p/95293440
四、模型的训练、模型的保存、模型的评价
- 模型的训练
- 模型的保存
- 模型的评价
下面我们将这几个方面进行阐述。
(1)模型的训练
History = model.fit()
(2)模型的保存
import os
from tensorflow.keras.callbacks import ModelCheckpoint
def save_model(model, checkpoint_save_path, model_dir):
if os.path.exists(checkpoint_save_path):
print("loading model...")
model.load_weights(checkpoint_save_path)
print("success!!")
checkpoint_period = ModelCheckpoint(
model_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
monitor='val_accuracy',
mode='max',
save_weights_only=False,
save_best_only=True,
period=2
)
return checkpoint_period
(3)模型的评价
当看到训练集和验证集loss的曲线以及accuracy的曲线的时候,我们应该如何进行处理和调参呢?【在这边的话,我目前的水平还不是很够的。如果你有你的想法的话,可以评论告诉我!】
我说说我自己一些想法吧,我也咨询了我的一些同学。
这边的话,不得不提到两个词——过拟合和欠拟合。这两个词确实在训练神经网络中是我们不得不考虑的问题。这两个词在定义上很好理解,但是我们如何从数据上着手。
垃圾二分类(我自建的一个模型),当时accuracy的图像显示训练集的准确率能够达到85%-89%,验证集的准确率能够达到92%,这是在2500张训练集情况下。但是在22000张训练集的情况下,验证集的准确率始终在88%-89%之间就上不去了。其实我一直在思考如何进行继续提高:
这边需要提到我一个同学从数据上对过拟合的理解。他认为当训练集达到95%以上(起码是90%以上),并且验证集的准确率迟迟不上去,甚至还有点下降的趋势,说明这个模型训练过拟合了。
五、训练调参的经验
这边的话,我可先贴一个目前比较好的一个论文,感觉很多人都有看。
Bag of Tricks for Image Classification with Convolutional Neural Networks
之后我会新开一章进行补充。