第3篇 Fast AI深度学习课程——卷积神经网络结构概论与多标签图像分类

第二节课中,讲述了提高图像分类网络准确率的若干手段,如数据修饰、学习速率重置、参数微调等。本节课将介绍卷积神经网络的基本架构,多标签图像分类,并演示如何将结果提交到Kaggle上。主要内容如下:

  • 如何悄没声儿地把自己挂在Kaggle竞赛的leader board上。
  • 如何对单一图像进行分类。
  • 卷积网络基本结构。
  • 多标签卫星遥感图像分类。

向Kaggle提交结果

1. 生成测试集的各类别的概率
log_preds, y = learn.TTA(is_test=True)
probs = np.exp(log_preds)

其中is_test=True使得网络在测试集而非验证集上进行输出;出于精度考虑,Fast.AI提供的是概率的对数,因此,最终的结果需要通过exp()函数获得。

2. 生成Kaggle所要求格式的数据
# 生成pandas数据对象
ds = pd.DataFrame(probs)
# 按每个类别名,给每列添加表头
ds.columns = data.classes
# 增加id列并设为文件id
ds.insert(0, 'id', [o[5:-4] for o in data.test_ds.fnames])
# 写入gzip压缩文件
ds.to_csv('subm.gz', compression='gzip', index=False)

由于每个文件的文件名形如test/0042d6bf3e5f3700865886db32689436.jpg,因此要去除开头的5个字母和结尾的4个字母,才能得到文件id:0042d6bf3e5f3700865886db32689436

3. 提交至Kaggle

若使用kaggle-cli,则使用如下格式的语句

$ kg submit <submission-file> -u <username> -p <password> -c <competition> -m "<message>"

若使用Kaggle api,则使用如下格式的语句

kaggle competitions submit -c dog-breed-identification -f submission.csv -m "Message"

若使用浏览器,则直接在Late Submission页面中提交即可。

若使用社交账号注册Kaggle,可能大家会对使用kaggle-cli时需要填写的用户名和账号感到迷茫,下面的链接是这种情况的解决方法

这个是使用kaggle API的方法,还有这个

利用训练所得的模型对单一文件进行分类

假设文件名变量为img_file,则使用如下语句进行分类:

# 获取对训练数据集和验证数据集所做的变换
trn_tfms, val_tfms = tfms_from_model(arch, sz)
# 以对验证数据集的变换处理图片
im = val_tfms(Image.open(img_file)
# 将处理后的图片数据包装成图像序列形式,即增加一个维度表明待处理的是图片的序列
preds = learn.predict_array(im[None])
# 获取分类结果
np.argmax(preds)

卷积网络基本结构

课程中利用Excel给出了一个卷积网络的通用结构在MNIST手写数字识别上的简化示例。(这种可视化方法真是厉害了)

1. 卷积核与非线性激活函数

卷积核就是图像处理中的对应的概念。通过卷积操作,可以提取如边缘、形状等图像信息。卷积层后会连接一个非线性激活层,课程中以ReLURectified Linear Unit)为例。ReLU会使得卷积输出的负数部分变为0。所用示例为MNIST手写体数字7,首先经过归一化后,其像素值限定在了0~1区间。卷积核则使用垂直方向和水平方向的边缘检测算子为示意,经过ReLU后,将过滤同一边缘在卷积后图像会出现明暗两条线的现象。经过上述操作,所得效果如下:

图 1. 经卷积与ReLU之后的结果
2. 池化(Pooling)层

经过卷积后的图像,尺寸并未比原图小多少,反而因为多种卷积核的存在,一幅图像会生成多种卷积特征。如果不做处理,这就意味着紧邻的隐含层的连接参数会非常多,一方面会降低计算效率,另一方面会加剧过拟合现象。针对这一问题,网络引入了池化层。其基本理念是:对某一小块区域,按某种策略选择一个数值值描述这一块区域的特征。常用的策略有取平均、选最大值等。课程中将图像划分成2×2的区域,然后采用每一区域的最大值描述其特征。经过上述操作,所得结果如下:

图 2. 池化后的结果
3. 全连接层

最靠近输出层的即是全连接层,也就是常规的神经网络层:对上一层产生的激活值进行线性加权。最后需要根据应用场景,采用sigmoidsoftmax等非线性函数,产生所需要的输出。

多标签卫星遥感图像分类

首先下载Planet数据集,解压后放置在data目录下,其结构为

models  tmp   train-jpg  test-jpg  train_v2.csv

其中train_v2.csv文件的结构如下,每一个文件的标签里都可能包含多类。

图 2. CSV文件结构

选取网络结构为resnet34,生成验证数据集val_idxs = get_cv_idxs(n),利用ImageClassifierData生成所需数据,注意此时数据修饰方式为transforms_top_down。这种方式会对图像随机的进行以下八种变换中的一种:旋转0、90、180、270度,每种角度下还会进行翻转。

由Fast.AI获取的数据data对象,包含val_dstrain_dstest_ds等字段,其中ds表示data set,即数据集合。而data对象还提供另一种访问形式data loader,对应的字段为val_dltrain_dltest_dl,通过这些字段,可以用迭代器的形式产生变换后的块数据,如x, y = next(iter(data.val_dl)),则y的大小为torch.Size([64, 17]),其中64就是数据块的大小,17为类别数,每一行的y则是0-1类别标识:

图 3. 通过data loader获取的y的形式


获取数据后,考虑到每次训练都需要对数据进行放缩,为节省这部分时间,可事先将数据放缩至合适尺寸并存储。

data = data.resize(int(sz*1.3), 'tmp')

接下来就是按照上一节所叙述的通用步骤进行网络训练:查找合适的学习速率;初步训练网络;解锁所有层,并用差异化的学习速率训练网络;锁定卷积层,并放大图像尺寸训练网络;解锁所有层训练网络。最终可得f2=0.930的结果。
其中所示用的评判指标是在构建分类器时以参数metrics传入ConvLearner.pretrained()中的。

一些备注

  • 先对pretrained=True进行训练,再对pretrained=False进行训练的理念:载入的参数已经包含了部分可用的图像信息,而后面附加的全连接层的参数则是随机初始化的。如果直接对所有参数进行训练,则有可能会污染已经训练好的参数。因此,我们期望附加层先获取一些有用信息,因此采用这种策略。
  • 多标签分类中使用sigmoid做非线性激活函数的理念:之所以不选softmax,是由于softmax输出的向量的各值求和为1,与实际的多标签0-1表示不符。
  • F β 指标: F β = ( β 2 + 1 ) P R β 2 P + R ,其中 P 为查准率,即被判定为某一类的数据样本中,确实是该类别的比例; R 为查全率,即某一类的数据样本中,被判别为该类的比例。

一些有用的链接

猜你喜欢

转载自blog.csdn.net/suredied/article/details/80836168