基础知识
1.神经元
2. 神经网络
3. 卷积操作
3.1 单通道卷积
3.2 多通道卷积
3.3 多通道输出
3.4 Batch
[N, C, H, W]
卷积核信息不变,卷积操作会多一定的倍数(和样本数有关)。
4. 池化层
池化是使用某一位置的相邻输出的总体统计特征代替网络在该位置的输出,其好处是当输入数据做出少量平移时,经过池化函数后的大多数输出还能保持不变。比如:当识别一张图像是否是人脸时,我们需要知道人脸左边有一只眼睛,右边也有一只眼睛,而不需要知道眼睛的精确位置,这时候通过池化某一片区域的像素点来得到总体统计特征会显得很有用。由于池化之后特征图会变得更小,如果后面连接的是全连接层,能有效的减小神经元的个数,节省存储空间并提高计算效率。
池化的作用
池化层是特征选择和信息过滤的过程,过程中会损失一部分信息,但是会同时会减少参数和计算量,在模型效果和计算性能之间寻找平衡,随着运算速度的不断提高,慢慢可能会有一些设计上的变化,现在有些网络已经开始少用或者不用池化层。
Avg Pooling 平均池化
对邻域内特征点求平均
- 优缺点:能很好的保留背景,但容易使得图片变模糊
- 正向传播:邻域内取平均
- 反向传播:特征值根据领域大小被平均,然后传给每个索引位置
Max Pooling 最大池化
对邻域内特征点取最大
- 优缺点:能很好的保留一些关键的纹理特征,现在更多的再使用Max Pooling而很少用Avg Pooling
- 正向传播:取邻域内最大,并记住最大值的索引位置,以方便反向传播
- 反向传播:将特征值填充到正向传播中,值最大的索引位置,其他位置补0
公式
5. Padding
角落边缘的像素,只被一个过滤器输出所使用,因为它位于这个3×3的区域的一角。但如果是在中间的像素点,就会有许多3×3的区域与之重叠。 所以那些在角落或者边缘区域的像素点在输出中采用较少,意味着你丢掉了图像边缘位置的许多信息。 那么出现的一个解决办法就是填充操作,在原图像外围以0进行填充,在不影响特征提取的同时,增加了对边缘信息的特征提取。
另外一个好处是,我们在做卷积操作时,每经过一次卷积我们的输入图像大小就会变小,最后经过多次卷积可能我们的图像会变得特别小,我们不希望图像变小的话就可以通过填充操作。
6. 激活函数
参考论文:https://arxiv.org/pdf/1811.03378.pdf
Sigmoid
Tanh
Sigmoid和Tanh激活函数有共同的缺点:即在z很大或很小时,梯度几乎为零,因此使用梯度下降优化算法更新网络很慢。
ReLU
Relu目前是选用比较多的激活函数,但是也存在一些缺点,在z小于0时,斜率即导数为0。 为了解决这个问题,后来也提出来了Leaky Relu激活函数,不过目前使用的不是特别多。
7. Dropout
论文:https://jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf
当一个复杂的前馈神经网络被训练在小的数据集时,容易造成过拟合。为了防止过拟合,可以通过随机丢弃部分特征节点的方式来减少这个问题发生。
代码实战
① 问题定义
十二生肖分类的本质是图像分类任务,我们采用CNN网络结构进行相关实践。
② 数据准备
2.1 解压缩数据集
我们将网上获取的数据集以压缩包的方式上传到aistudio数据集中,并加载到我们的项目内。
在使用之前我们进行数据集压缩包的一个解压。
In [1]
!unzip -q -o data/data68755/signs.zip
2.2 数据标注
我们先看一下解压缩后的数据集长成什么样子。
.
├── test
│ ├── dog
│ ├── dragon
│ ├── goat
│ ├── horse
│ ├── monkey
│ ├── ox
│ ├── pig
│ ├── rabbit
│ ├── ratt
│ ├── rooster
│ ├── snake
│ └── tiger
├── train
│ ├── dog
│ ├── dragon
│ ├── goat
│ ├── horse
│ ├── monkey
│ ├── ox
│ ├── pig
│ ├── rabbit
│ ├── ratt
│ ├── rooster
│ ├── snake
│ └── tiger
└── valid
├── dog
├── dragon
├── goat
├── horse
├── monkey
├── ox
├── pig
├── rabbit
├── ratt
├── rooster
├── snake
└── tiger
数据集分为train、valid、test三个文件夹,每个文件夹内包含12个分类文件夹,每个分类文件夹内是具体的样本图片。
我们对这些样本进行一个标注处理,最终生成train.txt/valid.txt/test.txt三个数据标注文件。
In [ ]
import io
import os
from PIL import Image
from config import get
# 数据集根目录
DATA_ROOT = 'signs'
# 标签List
LABEL_MAP = get('LABEL_MAP')
# 标注生成函数
def generate_annotation(mode):
# 建立标注文件
with open('{}/{}.txt'.format(DATA_ROOT, mode), 'w') as f:
# 对应每个用途的数据文件夹,train/valid/test
train_dir = '{}/{}'.format(DATA_ROOT, mode)
# 遍历文件夹,获取里面的分类文件夹
for path in os.listdir(train_dir):
# 标签对应的数字索引,实际标注的时候直接使用数字索引
label_index = LABEL_MAP.index(path)
# 图像样本所在的路径
image_path = '{}/{}'.format(train_dir, path)
# 遍历所有图像
for image in os.listdir(image_path):
# 图像完整路径和名称
image_file = '{}/{}'.format(image_path, image)
try:
# 验证图片格式是否ok
with open(image_file, 'rb') as f_img:
image = Image.open(io.BytesIO(f_img.read()))
image.load()
if image.mode == 'RGB':
f.write('{}\t{}\n'.format(image_file, label_index))
except:
continue
generate_annotation('train') # 生成训练集标注文件
generate_annotation('valid') # 生成验证集标注文件
generate_annotation('test') # 生成测试集标注文件
2.3 数据集定义
接下来我们使用标注好的文件进行数据集类的定义,方便后续模型训练使用。
2.3.1 导入相关库
In [2]
import paddle
import numpy as np
from config import get
paddle.__version__
2.3.2 导入数据集的定义实现
我们数据集的代码实现是在dataset.py中。
In [3]
from dataset import ZodiacDataset
2.3.3 实例化数据集类
根据所使用的数据集需求实例化数据集类,并查看总样本量。
In [4]
train_dataset = ZodiacDataset(mode='train')
valid_dataset = ZodiacDataset(mode='valid')
print('训练数据集:{}张;验证数据集:{}张'.format(len(train_dataset), len(valid_dataset)))
③ 模型选择和开发
3.1 网络构建
本次我们使用ResNet50网络来完成我们的案例实践。
1)ResNet系列网络
2)ResNet50结构
3)残差区块
4)ResNet其他版本
In [5]
# 请补齐模型实例化代码
# network = ?
模型可视化
In [6]
model = paddle.Model(network)
model.summary((-1, ) + tuple(get('image_shape')))
④ 模型训练和优化
In [6]
EPOCHS = get('epochs')
BATCH_SIZE = get('batch_size')
# 请补齐模型训练过程代码
模型存储
将我们训练得到的模型进行保存,以便后续评估和测试使用。
In [10]
model.save(get('model_save_dir'))
⑤ 模型评估和测试
5.1 批量预测测试
5.1.1 测试数据集
In [7]
predict_dataset = ZodiacDataset(mode='test')
print('测试数据集样本量:{}'.format(len(predict_dataset)))
5.1.2 执行预测
In [12]
from paddle.static import InputSpec
# 请补充网络结构
# 模型封装
model_2 = paddle.Model(network, inputs=[InputSpec(shape=[-1] + get('image_shape'), dtype='float32', name='image')])
# 请补充模型文件加载代码
# 模型配置
model_2.prepare()
# 执行预测
result = model_2.predict(predict_dataset)
In [14]
# 样本映射
LABEL_MAP = get('LABEL_MAP')
# 随机取样本展示
indexs = [2, 38, 56, 92, 100, 303]
for idx in indexs:
predict_label = np.argmax(result[0][idx])
real_label = predict_dataset[idx][1]
print('样本ID:{}, 真实标签:{}, 预测值:{}'.format(idx, LABEL_MAP[real_label], LABEL_MAP[predict_label]))
⑥ 模型部署
In [15]
model_2.save('infer/zodiac', training=False)