以下仅是个人在使用过程中的经验总结,请谨慎参考。
常用算法总结
图像分类
- 常用算法(可作为其他任务的骨干网络):
- 服务端:VGG、ResNet、ResNeXt、DenseNet
- 移动端:MobileNet、ShuffleNet等
- 适用场景:识别区分场景类型
目标检测
- 常用算法:Yolo系列
- 适用场景:检测识别场景中的目标类型及位置
目标跟踪
- 单目标:SiamFC、SiamRPN、SiamRPN++
- 多目标:ByteTrack、deep sort
- 适用场景:在上下文连续多帧中持续定位目标位置变化,如跟踪人的位置变化
人脸识别
- 人脸检测:MTCNN、RetinaFace
- 特征提取:ArcFace
- 特征匹配:Nmslib、Annoy
- 适用场景:直播、点播场景下人脸坐标定位和人物身份识别
度量学习
- 常用算法:神经网络模型 + Contrastive Learning
- 适用场景:将输入转换为高维特征,根据特征相似度完成具体任务,如音乐识别等
音频识别
- 常用算法:音频频谱 + 神经网络模型
- 适用场景:如识别音频中是否出现音乐,以及出现的是哪一首音乐
传统图像算法
- 常用算法:边缘检测、直线检测、透视变化、坐标映射
- 适用场景:如识别足球场地边缘,根据球员实时位置构建赛场态势图
常用技术总结
网络设计
- 卷积网络
- 池化
- 全连接网络
- 残差网络
- 分组卷积
- 深度可分离卷积
- Inception网络
- BatchNorm/LayerNorm
- 循环神经网络
- Transformer网络
特征融合
- FPN特征金字塔
- PAN特征融合
- UNet细粒度特征融合
- Attention全局注意力
- 多分支融合叠加
激活函数
- Sigmoid
- ReLU
- Tanh
- Leakey-ReLU
- PReLU
- Swish
优化算法
- 随机梯度下降
- 批量梯度下降
- 小批量梯度下降
- 动量梯度下降
- 自适应梯度下降
损失函数
- 单分类损失Logistic Loss
- 多分类损失CE
- 多标签分类损失BCE
- 类别不均衡损失Focal Loss
- 回归损失MSE、L1 Smoothing
- 多任务损失
- Contrastive Loss
- Label Smoothing
数据增强
- 平移/旋转/翻转/缩放
- 添加高斯噪声
- 亮度/对比度/饱和度变换
- 数据融合MixUp
- 随机掩码Cutout
- 频谱掩码(音频数据增强)
模型压缩
- 模型剪枝
- 模型重参数化
- 模型蒸馏
- 模型量化
训练策略
- 迁移学习
- 权重衰减
- 学习率衰减
- WarmUp预热
- DropOut/DropBlock
- 并行训练
- Early Stopping
使用经验总结
数据预处理:在数据预处理阶段,通常包含以下流程
- 数据加载:这部分可能涉及到大规模训练数据的高性能加载,消除数据读取造成的性能瓶颈
- 数据增强:根据具体任务选择合适的数据增强策略
- 数据归一化:将数据的数值做归一化处理,加速模型的收敛速度
网络模型设计:根据具体的业务场景,选择/设计合适的网络架构,通常遵循以下原则:
- 优先选择开源的预训练模型参数,使用自有数据进行微调训练
- 使用开源预训练模型的基础部分,修改模型的上层适配自有的业务场景
- 根据业务场景自行设计模型架构(通常只在没有开源参考模型的条件下使用,模型效果很难得到保证)
- 通常情况下对于图像、视频数据,追求模型效果一般使用ResNet34、ResNet50等模型架构,追求处理性能一般使用ResNet18、MobileNet、ShuffleNet模型架构
损失函数设计:根据具体业务场景,选择/设计合适的损失函数,通常遵循以下原则:
- 分类场景一般使用交叉熵损失Cross Entropy
- 类别极度不均衡的分类场景(不均衡比例超过1000以上),分类损失尝试Focal Loss
- 在分类损失场景下使用Label Smoothing软化模型的学习能力
- 回归损失根据使用场景可选MSE、MAE、L1 Smoothing等
- 表征学习(也叫对比学习或度量学习)优先使用Circle Loss、ArcFace Loss、Triplet Loss等损失函数,模型训练时根据具体业务场景适当增大训练批量batch size,提升表征学习的特征区分度
训练策略:迭代训练更新模型参数,逐步提升模型效果
- 优先使用小批量梯度下降 + Momentum动量 + LR decay学习率衰减 + Weight Decay权重衰减训练策略
- 如果使用多GPU并行训练,训练的批量大小batch size和学习率LR要同比例改变
- 先使用极少的训练数据验证模型训练效果,排除模型在设计上的问题和工程化问题,然后再迁移到大数据量,避免造成无效的资源和时间浪费
- 模型训练过程中保存每轮迭代预测异常的数据,通过bad case分析,逐步提升数据质量或调整模型策略
- 保存模型每轮迭代的准确率、召回率、loss变化曲线,监控是否发生过拟合、欠拟合等问题
模型验证:使用实际场景数据验证模型的预测性能,包括效果和速度
- 优先检查网络架构和模型参数的匹配情况,防止参数不匹配带来的潜在错误(问题较难定位)
- 使用训练数据验证模型的预测效果,排除训练和验证阶段数据处理不一致带来的差异
- 保证验证数据和训练数据具有相同或相似的数据分布
- 将模型转换为推理模式,固化BatchNorm、Dropout等具有随机性的操作
- 避免不必要的资源消耗,如使用torch.no_grad避免不必要的显存占用
- 关注模型在推理阶段的显卡内存占用,保证分配的内存资源大于波动的峰值
常见问题和思考------模型训练阶段
模型训练效果差
- 常见问题:神经网络模型在训练过程中,没有学习到有效的信息,模型收敛慢或者不收敛
- 原因分析:
- 训练数据中存在脏数据(首要排除的因素)
- 没有使用预训练模型和数据归一化
- 训练集过大,模型容量小,出现欠拟合
- 训练集过小,模型容量大,出现过拟合
- 学习率设置过大,导致参数更新过快,结果出现震荡
- 学习率设置过小,导致参数更新过慢,学习进展缓慢
- 数值稳定性问题导致数值溢出,出现梯度爆炸
- 网络设计问题导致梯度消失
- 使用了不合理的批量大小
- 使用了不合理的训练迭代次数及停止策略
- 使用了不合理的学习率衰减策略
- 使用了不合理的参数初始化策略
- 使用了不合理的数据增强,比如检测人体时对图像做了上下翻转
- 使用了不合理的损失函数,比如分类问题使用回归损失函数
- 使用了不合理的网络架构,根据一维、二维、多维、时序数据选择合适的架构
- 使用了较强的正则化,限制模型的学习能力,出现欠拟合
- 使用了较弱的正则化,过渡学习训练集,出现过拟合
模型训练速度慢
- 常见问题:神经网络模型训练慢,主要体现在收敛慢、资源使用率低等方面
- 原因分析:
- 没有使用GPU资源进行模型训练
- 模型训练时使用过小的学习率,导致参数更新慢
- 模型训练时使用了过大的batch size,导致参数更新频次少
- 模型训练时使用了过小的batch size,没有充分利用计算资源
- 没有使用预训练参数,重头训练效率低
- 没有使用Batch Norm等做数据归一化
- 训练过程中存在过多的内存、磁盘访问
- 模型过于简单,不具备学习复杂任务的能力
- 使用了过强的正则化,限制了模型的学习能力
- 没有使用单机多卡、多机多卡并行训练
- 模型复杂度高,参数量大,以及使用过大的图像分辨率
- 训练数据量大,数据加载成为性能瓶颈
常见问题和思考------模型预测阶段
模型预测指标
- 分类模型:准确率、召回率、PR曲线、ROC曲线(类别不均衡)
- 检测模型:mAP、准确率、召回率
- 特征提取模型:top 1、top K、r_precision
模型预测效果差
- 常见问题:神经网络模型训练效果较好,但是在预测阶段模型表现较差
- 原因分析:
- 模型训练和测试的数据处理pipeline不一致,比如训练时做了Normalize,测试时没做Normalize
- 模型在测试时没有切换到推理模式,如pytorch中的eval()转换
- 输入的数据维度不正确,比如训练时使用[N, C, H, W],测试时也要使用同样的数据维度顺序,有些模型即使输入的数据尺寸和训练时不一样也不会报错
- 模型参数加载不完全,以pytorch框架为例,加载模型时设置完全匹配的参数为False,在加载过程中即使参数和模型不匹配也不会报错,但是会使用默认的随机参数
模型预测资源占用高
- 常见问题:神经网络模型在预测阶段GPU使用率低,CPU使用率高,或者出现显卡内存溢出
- 原因分析:
- 数据预处理在CPU上进行,没有充分利用GPU算力
- 在预测阶段模型没有设置成推理模式,计算产生无用的中间结果占用资源
- 深度学习框架如Pytorch自动搜索最优算子导致显存占用短暂飙升
模型处理未知类别
- 常见问题:如何让分类模型对未见过的数据类别说"不知道,不认识",提升鲁棒性
- 解决方案:
- 给分类模型添加一个其他类别,此种方法不适用于真实开放环境
- 使用BCE(Binary Cross Entropy)多标签二分类损失函数,以猫狗分类为例,分别输出是猫狗的概率,如果输出既不是猫也不是狗,则表示未知
- 使用表征学习(度量学习)方法通过特征匹配进行分类识别,将输入的数据与已知类别进行相似度匹配
模型效果提升
- bad case分析:数据决定了模型的上限,提升模型性能首先应当从数据层面入手,避免脏数据带来的负面影响
- 数据增强:通过给训练数据增加异常扰动提升模型效果的鲁棒性
- 图像数据增强:随机裁剪、旋转、翻转、缩放、颜色/亮度/对比度调整、多图像融合等
- 音频数据增强:频谱随机连续掩码、多频谱融合等
- 选择合适的网络架构:针对具体的使用场景选择合适的网络架构,如图像使用2D卷积、音频使用1D卷积等
- 选择合适的损失函数:针对具体任务使用合适的损失函数,常见的是分类损失、回归损失及混合损失
- 选择合适的评价指标:如分类使用准确率、召回率,类别不均衡使用ROC,目标检测使用mAP等
- 使用开源预训练模型:预训练模型通常具备较好的参数基础,在此基础上进行训练有助于提升性能
- 多模型融合:使用不同的弱模型训练多个效果稍弱的模型,融合多个模型结果提升最终性能
- 知识蒸馏:使用知识蒸馏将大模型学习到的知识迁移到小模型,在提升效果的同时还可以提升速度
常见问题与思考------模型推理加速
模型推理加速
- 数据处理层面
- 将数据预处理、后处理等操作在GPU上进行
- 避免在CPU和GPU之间频繁进行数据拷贝
- 避免保存大量的中间结果,如磁盘写入
- 网络模型层面
- 模型剪枝:根据具体业务场景,裁剪掉与业务不相关的计算模块
- 算子融合:将多个算子融合成一个,减少内存访问次数,如将Conv + BN融合成Conv
- 半精度推理:使用float16进行模型推理
- 分组卷积:使用分组卷积降低网络模型的参数量和计算量,但是会增加内存访问次数
- 模型蒸馏:将大模型学习到的知识迁移到小模型,在提升效果的同时还可以提升速度
- 部署工具层面
- 服务端:ONNX、TensorRT
- 移动端:NCNN、MNN
影响模型速度的主要因素
- 数据处理层面
- 没有使用GPU资源进行推理
- 输入数据尺寸大,如高分辨率图像
- 模型架构层面
- 网络模型参数量大
- 网络模型计算量大
- 网络模型并行度低,如存在多分支结构等
- 内存访问次数多,如大量使用分组卷积
- 工程实现层面
- 没有使用批量推理,没有充分利用GPU的并行计算能力
- 不合理的数据复用导致频繁拷贝
常见问题与思考------工程化问题
工程化常见问题
- 算法集群扩容
- kafka topic的partition数量要大于算法消费者数量
- 直播场景混流和AI算法结果对齐
- 算法解码处理直播流内容,获取每帧的dts、pts时间戳
- 混流侧根据算法返回结果以及dts、pts时间戳对齐到原流,将算法结果压制到直播流中
- 算法侧和混流侧使用pts解码时间戳对齐,不用dts时间戳,否则会造成画面闪烁
- Kafka、Redis数据保存周期
- 根据业务场景和处理性能设定保存周期,保存周期过长造成额外资源占用,保存周期过短造成数据丢失
- Restful API高稳定性、高并发
- 算法模型对外提供API接口需要关注高并发、高稳定性,通常使用gunicorn进行部署
- 算法模型高并发部署需要成倍的资源,重点关注显存、内存和CPU资源占用情况
- 容器化部署
- 关注容器CUDA版本与主机显卡驱动,以及深度学习框架之间的匹配问题