Skip to content

第六届sky hackson 复盘

比赛经历

比赛前对比赛一头雾水, 很多内容不懂不知、抱着完赛就是胜利的想法加入到比赛中来。

比赛题目是从疫情现状出发考虑构建一个大白AI, 使用AI进行语音交流、识别用户是否戴好口罩, 并根据身份信息决定是否放行。

比赛一开始只是跑了基本的 jupyter notebook, 随后陷入迷茫, 面临着TTS的优化(主要是去噪和生成效果两方面)止步不前, 对于目标检测则存在数据集预处理、模型选择等多个问题。

但是非常感谢比赛中的指导老师 KenYiPeng, 感谢两位老师对于问题的耐心解答, 大大的帮助了我和我的小伙伴们的完赛。

比赛过程中认识了无辑队伍的队长恺, 和打鲁班不加班队的Crownd, 在两人共同帮助下, 优化了一个版本TTS的语音, 后续在无辑帮助下, 对TTS进行了多个版本的优化, 达到了最终的效果, 拿到了5分的成绩。

而目标检测部分也是和进行合作, 对数据集进行清洗、再标注、模型再训练等过程, 最后的mAP也取得了不错的成绩。

可以说技术上的内容, 很大一部分都来自于的帮助与合作。

技术之外, 比赛气氛极好, 群内有问必答, 老师和其他参赛选手都会对问题进行热心解答。

总分上的第一是机缘巧合, 是受到了许多其他队伍的无私帮助, 是组内成员的配合。

比赛感受

比赛整体侧重于神经网络算法的流程、实际的应用。范围从一开始的数据收集、清理、标注, 到中期的算法选择、训练, 后期的模型部署、使用。在指导老师的帮助下, 只要认真参与, 完成比赛根本不成问题。

对于 NemoTAO 这两个工具也充满了惊喜。

Nemo 首先是提供了相当多的预训练模型, 在比赛时我们浏览了NGC的所有模型, 而只需要在程序中简单的修改一下模型的名称, 我们就可以完成对训练模型的更换, 所以在比赛过程中对于可以选择的模型都进行了简单的替换尝试, 可以说在最低的学习成本下搭配了范围内最优的结果。

其次 Nemo 除了 ASR 和 TTS 模型之外, Nemo 还提供了相当多功能范围的预训练模型, 比如文本分类、意图识别、对话跟踪等等等等, 这些都是从 Nemo 的 GitHub 上看到的, 虽然由于比赛时间紧张并没有进行使用, 但是之后一定会找机会尝试一下。

而 Tao 首先是让我知道了 nvidia-docker2 这个工具, 居然是可以在 docker 内部使用 nvidia 的显卡的, 我个人就一直很喜欢 docker 这种工具, 他可以最大程度的保护电脑, 解决各种程序之间的冲突。

Tao 的一个特点就是和 Nemo 一样支持了很多的任务, 只需要在运行的时候简单的调整几个参数, 就可以完成指定任务的训练, 可以说将学习成本降低了非常非常多, 让我们在比赛过程中不需要分心于算法的核心思想, 而是针对结果尽可能的调整训练的过程。

同时 Tao 还可以使用工具直接使用一行命令生成推理后的二进制文件, 比起TensorRT的使用来说简化了非常多, 学习成本也降低了非常多。

为了追求最后的结果精度, 也为了提高自己的能力, 在比赛中选择了更新更强的模型, 但是由于 Tao 内还没有相关镜像, 所以放弃了使用 Tao, , 选择改用 TensorRT。

经验总结

下面是对比赛过程中出现的问题进行的一部分总结, 整体上偏向于想到哪写到哪 模型部署方面个人理解较多, 所以重点请看推荐阅读部分, 环境部分偏向基础的Linux。

数据收集与处理

数据收集要考虑数来源、数据量大小、数据质量, 三个方面

这里按照ASR、TTS、目标检测三个方向进行总结, 插入 Nemo 和 Tao 的简单介绍。

ASR部分

对于ASR与TTS, 比赛中均被指定使用Nemo作为训练框架, 它是Nvidia设计的主要用于NLP方向的一个框架。

其中ASR部分, 由于可以在NGC上搜索预训练模型使用, 所以数据量大小并不需要很大, 在周围人之间进行一些小范围的数据收集即可达到比赛使用要求。(即:数据量小、来源为自行收集, 足以)

ASR作为语音识别, 应该有较好的鲁棒性, 所以在收集到的数据之中, 只要保证人耳可以清晰听出指定内容即可作为有效的训练数据, 无论预训练模型如何识别。

在比赛过程中, 自行收集了大概50条数据, 其中30多条来自不同的人, 剩余十多条来自队内成员录制。语音内容均为:“你好大白请让我进入小区”。并且和无辑队交换了拥有的数据集, 最终使用训练的大概有七十条左右。

TTS部分

TTS部分虽然也是使用Nemo作为训练框架, 但是问题在于他并没有提供中文的预训练模型, 也就是需要我们进行从头训练。

根据比赛要求, 其录制内容为:“您好, xxx, 欢迎回家, 二维码有效请您通过。”, 中间xxx部分表示一个人名。

由于语句内容单一, 除人名部分以外均为固定内容, 所以考虑数据量依然较小(五十条以内), 加上轮次训练足够。所以数据来源依然可以为人工录制。

同时考虑TTS为文本转声音, 即让机器提取每个字对应的声音特征频谱, 以便于将文字化作对应的频谱, 所以数据集内噪声应该尽可能消除、不必要静音片段也应该尽量短、说话内容应该尽量的标准, 尽可能的减少模型学习时候的干扰。----与ASR部分相反。

确定如上规则后, 寻求播音专业朋友帮忙录音50条, 其中包含‘上午好’、‘下午好’、’中午好‘, 和五十个不同的人名, 发音标准, 码率标准, 无底噪, 人工去除头尾静音片段后尝试用于训练。

但是由于人名和问好并没有和后面的‘欢迎回家, 二维码有效请通过’进行分离, 导致最终合成的频谱在人名部分产生了极大混乱, 结果无效。

这里要提一个同样是人工录制的对比:打鲁班不加班队人工录制了一句话‘你好, 小鲁班, 欢迎回家, 二维码有效请通过’, 大概200多条, 进行了足量训练。生成之后MOS效果极佳, pesq也还不错。(感谢打鲁班不加班队无私的回复, 因为我去求教的时候比赛并没有结束)

同时, 除人工录制以外的其他数据来源包括:开源数据集(AIShell2、AIShell3等)、AI开放平台合成TTS(再次感谢无辑队长的帮助)。

开源数据集的问题在于:不够具有针对性, 训练面向的是整个中文语言环境, 而且存在音色众多、声音质量低下, 数据量过大等问题。

这三个问题带来的后果分别是:合成声音过于奇怪、合成之后噪声清晰、训练所需时间大大增加(无论是单轮时间, 还是需要的总轮次, 都大大增加)。

所以Hackthon比赛中TTS部分应该放弃开源数据集的使用, 接下来分析AI开放平台TTS合成。

AI开放平台合成会有两个显而易见的问题:

  • 码率不够, 平台提供的采样率最高可达16k, 远远不足比赛要求的41.4k
  • 内容单一, 虽然模型发声依然带有一定的随机性, 但是相比于人工录制而言, 随机性大大降低, 如果只有一句话, 可用数据集极少, 仅有三五句。

问题一是用ffmpeg脚本批量转化完成, 指定输出码率和声道。下方是比赛中使用的脚本。

import os
from tqdm import tqdm

input_dir = "asr_data_1"
output_dir = "asr_data"

video_list = os.listdir(input_dir)
os.makedirs(output_dir, exist_ok=True)

for video_name in tqdm(video_list):
    os.system("ffmpeg -y -i %s/%s  -ac 1 -ar 44100  %s/%s" % (input_dir, video_name, output_dir, video_name))

问题二是通过分词分句配合多音色采集完成的。

其中分词分句指的是将‘你好, 小叮当, 欢迎回家, 二维码有效请通过’拆解为‘你好’、‘小叮当’, ‘欢迎回家’, ‘二维码有效请通过’, 这样连带本身的句子共有五句话。

同时腾讯拥有多个音色, 女性大概29个可用音色, 最终可得29*5=145个句子用以训练。

至此TTS数据集完成。

目标检测数据集

关于目标检测, 本次比赛要求是否戴口罩, 在比赛之前我们想过人工拍照获得数据集, 但是考虑人数和实际情况, 数据量太小, 难以达到训练要求, 所以采用网络收集。

主要使用的数据收集网站包括:Baidu、Google、PaperWithCodeRoboflow(这里还要感谢无辑队长, 是他分享给我这个网站)。

先后获取大概共计8000张图片的数据和标注信息。主要分类为戴口罩和不戴口罩。

随后使用labelImg对数据集进行重新标注, 对第三类(戴口罩不标准)进行标注。标注完成后由于第三类标签过少, 所以存在严重的数据标签不均衡的问题, 于是在Roboflow上收集到了数据集Mask22,大概一千余张原本数据。

同时使用网站带有的增强功能, 对两个数据集均进行相当的效果增强。

另外, 比赛提供的原本数据集应该是来自于kaggle的数据集Face Mask Detection

收集的两个数据集均包含该数据集图片。 目标检测部分数据集结束。

模型训练与验证

模型训练不拆分模块, 不深入原理, 简单讲一些经验or教训。

另外感谢比赛流程, 环境顺利的情况下, 是可以直接一路Run到结尾的。

Batch_Size

batch_size指的是每次训练要为给模型多少内容, 例如batch_size = 4表示全部数据按4个一组喂给模型, batch_size越大, 要求的显存越大, 但是并非batch_size越大, 学习效果越好。

跟李沐学AI中有提到batch_zie小的情况下, 可能模型泛化性能越好, 因为噪声相对更高。

Epoch

Epoch讲的是全部数据走多少轮, 在本次比赛中, ASR模型300epoch, TTS为6000epoch, CV为300epoch, 其中TTS为从头训练+提高pesq分数, 所以略高。Epoch越高, 所需时间越长。

训练常见问题

  • CUDA out of memory 显存溢出

    显存较低的时候会出现的问题, 降低batch_size属于常见解决方案, 显存过低的时候可能batch_size = 1也无法解决, 建议使用云服务或者更换显卡。

  • loss不停震荡, 幅度很大

    正常现象, 看到某次loss很低可能是在局部最优, 加大力度继续训练。

  • loss已经是0啦, 担心过拟合

    过拟合不是很容易的事情, 几十个Epoch并不会让他过拟合, 可以关注准确率有没有继续提高, 如果还在提高就继续训练!

  • 其他问题排错思路

    • 定位关键节点

      绝大部分报错信息属于堆栈信息, 是告诉你程序在调用执行哪一句的时候产生的问题, 找到最关键的那么一两句话, 可以提高排错速度, 例如 xxxx is not file or dir基本是说文件不存在, Cuda out of memory表明显存溢出。

    • 多用搜索引擎

      你遇到的绝大部分问题, 别人也遇到了, 所以先搜索一般结果会更快。

    • 加入自己的思考

      一个报错可能是有多个原因的, 同样是文件读取, 可能是因为文件格式、文件编码、文件路径、文件内容多个方面的读取错误, 在搜索的同时, 分析他们的解决方案是为了什么, 是否适合你当前的情况, 多次尝试即可。

    • 该问就问

      自己不能解决的问题, 就大胆的去问, 讲清楚自己的问题, 自己的努力, 带上明确的截图和问题的描述, 你不能解决的问题对别人而言可能只是一句话的事情, 但是一定一定一定把问题描述清楚

验证常见问题

  • TTS合成噪声太大

    检查数据集, 采样率是否标准, 底噪是否过大, 干扰到正常训练, 发音是否标准、清晰。数据量是否足够(只有一条是不行的哦)。

    提高训练轮次。

  • 目标检测时loss不提高

    降低 batch_size 的情况下, 一定要增加 epoch 来保证训练的效果。

  • mAP指数较低

    提高数据集质量, 检查是否有错误标注或者标注不均衡的问题, 不均衡的情况下, 数量少的标签学习起来更慢, 拉低整体mAP。

  • 训练时中断

    TTS目前我不知道恢复训练的方法, 只能从头训练, 但是如果训练轮次已经差不多了, 可以使用restore_from方法加载 ckpt 文件, 保存成为 nemo 文件。

    CV训练时官方使用的 Nviia-Tao 提供了继续训练的方法, 继续训练即可。使用其他模型, 如yolov或者yolo-x都可以加载权重文件继续训练。

关于Nemo

Nemo 是 Nvidia 提供的一个关于 NLP 的神经网络功能包, 可以很方便的加载模型、调用算法、进行训练、和部署使用。

可以将 Nemo 视为对 Pytorch 的进一步封装, 更多内容可以看 Nemo 的 GitHub:NeMo

目前最新版本是1.8.0, 比赛使用1.4.0, 可以在 GitHub 中选择1.4.0的 TAG来查看。

同时搭配 NGC 网站选择各种预训练模型。

比赛中我对ASR和TTS的模型、TTS的声码器均尝试进行了替换, 最后选择了效果最好的一个。

关于Tao

这个东西其实我并没有用很多, 但是在比赛过程中遇到了很多问题, 所以这里来重点解释几个常见问题(所有问题答案均来自自己理解, 可能存在谬误):

  • Tao

    tao 本身应该是使用 nvidia-docker 来进行一系列的算法模型训练的, 在不同的镜像内部存在着不同的算法模型, 所以可以看见命令单元格内 tao ssd 表明使用 tao 运行 ssd 算法。同理, 可以使用tao yolo_v3来使用 tao 运行 yolov3 算法。

    可以说, tao 极大简化了 nvidia-docker 的使用, 简化了开发人员的操作难度, 提高了整体的效率。

    • 题外话: docker 与 nvidia-docker

      docker最常被提及的说法是:一个容器, 可以按照虚拟机镜像的思路去理解, 大家使用 docker image 来管理不同的虚拟机镜像, 在镜像的内部自成一套的环境, 包括但不限于操作系统版本、 python 版本、 nvidia 驱动版本、 cmake 版本等等。

      关于 nvidia-docker 我知道的并不多, 但是应该是更好的支持了 nvidia 的驱动, 支持在 docker 内使用显卡。 另外使用的时候可能需要使用指令登陆 ngc。

  • 路径映射

    在上一步的基础上, 我们已经知道, tao 就是一个套着皮的 docker 控制器, 那么路径映射其实就的 docker 的 -v 参数, 使用 -v 参数指定的两个文件夹, 可以视为同一个文件夹。对 A 文件夹进行的修改会同步修改到对 B 文件夹, 反之依然。

    这是本次比赛在目标检测部分最大的拦路虎, 很多报错都是由于路径映射配置出错。

  • NGC

    前面已经说过, 使用 nvidia-docker 需要登陆, 可使用以下命令登陆 NGC:

    sudo docker login -u '$oauthtoken' --password-stdin nvcr.io <<<"Key"
    

    其中 key 需要登陆 NGC网站。

    但是使用指令登陆之后可能依然会被提示未登录的问题。可能原因有三个:

    • 登陆后的配置文件和运行 tao 时候默认读取的配置文件位置不一

      可以采用强制移动方式解决

    • 配置文件及所在文件夹权限要求较高, 无法完成指令

      使用 sudo 运行指令, 或者使用 chmod 修改文件夹权限

    • 依然报错权限问题

      sudo chmod 666 /var/run/docker.sock
      

      这个我并不理解为什么, 所以这里主要放出解决方案, 有问题欢迎讨论。

上述内容基本可以解决对 tao 对理解问题和基本的使用问题。

模型部署

模型部署部分基本上是使用其他人提供的仓库、代码等, 纯纯一个快乐调包人。

所以以下均为一家之谈, 可能用处不大。

推荐阅读

官方 TensorRT 教程视频, 看完之后可以对TRT有一个基础上的概念:TensorRT 教程 | 基于 8.2.3 版本

感谢无辑队长提供的视频链接, 干货满满:详解TensorRT的C++/Python高性能部署, 实战应用到项目

一个高星的 GitHub 项目, 内含各种模型的TRT实现, 经过验证都是基本可以直接使用的, 欢迎大家Star:tensorrtx

官方TRT仓库, 带有官方 plugin: TensorRT

网络可视化工具:Netron

权重文件->ONNX->engine

再次提醒, 以下部分是个人理解, 一定存在偏颇, 请更关注官方文档内容。

所有算法模型最后都会生成一个模型文件, 后缀不一, 但是保存的内容可能是比较类似的, 那就是执行该算法的流程, 和每个流程上的权重, 比如卷积之后使用哪个激活函数, 又或者每个卷积操作不同的权重, 或是对数据切分, 整理等等。

其中每个步骤, 都可以成为一个算子, 不同的文件支持的算子多少, 算子内容都是不一样的。

ONNX 作为最常见的导出模型, 支持的算子可能并不算很多, 但是通过各种组合, 能实现基本所有的操作。生成成为 Onnx 文件后, 该模型文件就可以在任意平台上使用, 实现了通用性的可能。

而 TensorRT 则不同。 TensorRT 支持导入模型文件, 并输出 engine 文件( TRT 输出的其实是二进制文件, 常见以 .engine .bin .plan 等结尾)。同时该文件只支持在一台, 或者说硬件和驱动相同的一类设备上运行, 如果使用不同的硬件, 则会不可知的错误。模型变成了独一无二的专属情况。

举例来说, 读取 Onnx 很简单, 可以在任意平台上, 而读取 engine 文件, 则生成 engine 文件的硬件环境和驱动环境应该与使用 engine 文件的硬件关键和驱动环境完全一致。

为什么?

根据我的理解, 首先 Onnx 支持的算子少, 为了实现最终的效果, Onnx 生成过程中会出现大量的复杂的算子过程, 其中有些可能是不必要的, 或者可替代的。这部分可以通过修改 Onnx 的文件, 或者修改模型导出时参数来解决。

而另外一个角度来讲, 获得通用性必然会有一定牺牲, Onnx支持的算子一直不多, 而TRT支持的算子更多, 而且随着版本更迭, 在不断增加, 那么某些低级算子就可以被替换成为更高级的算法块。

另外TRT会根据当前硬件情况和驱动器, 组合模型中的算子操作, 优化计算逻辑和过程, 不同的硬件和驱动下, 可以使用的工具也不一而同, 所以在不同机器上无法使用 engine 文件也是情有可原的。

比赛情况

比赛过程中, 并没有使用 TAO 生成 bin 文件, 所以这里简单说一下需要注意的情况, 由 Tao 训练的模型, 是带有 key 的, 所以中生成 bin 文件的时候应该注意 key 是否对的上。

如果一开始 key 输错了, 要注意清理缓存。其他我就不知道了。

环境问题

本次比赛出现了大量环境问题, 除了双系统以外, Linux操作不熟也是很大问题。在这里对常见命令和简单问题进行一次总结。

常见命令:

  • sudo 提权, 将普通用户权限提高至 root 用户
  • cd xxx 进入指定的文件夹
  • ls 展示当前文件夹下所有文件
  • mkdir 创建文件夹
  • chmod 修改文件读写权限
  • mv a b 将a文件移动到b
  • rm 删除文件(在linux中谨慎使用, 更多使用mv替代。)
  • top 查看进程、磁盘、cpu的使用情况
  • nvidia-smi 查看 gpu 使用情况
  • kill 杀死进程, 搭配 top 命令获取到的 pid 使用。

以上所有命令均有额外参数, 自行搜索获取更多指令

常见问题

  • ffmpeg下载失败

    需要更换 ubuntu 的下载镜像, 具体可以自行搜索国内阿里源、网易源。

    修改配置文件后执行sudo apt-get update, 更新一下源文件即可。

    注意updateupgrade 的区别, 前者是更新你的仓库文件列表, 告诉系统可以下载哪些文件, 可以去哪里下载这些文件;后者表示升级一下 apt-get 软件。

  • from ruamel.yaml import YAML 报错

    使用 import yaml 替换

  • 报错:TypeError:_run_hydra() missing 1 required positional argument:args

    运行pip install hydra_core==1.1.1

  • jupyter长时间没反应, 卡住了好像。

    首先检查你运行的 cell 前面的中括号内是 * 还是一个数字, 如果是 * 表示程序正在运行, 安静等待即可。如果是数字, 表示运行结束。

    运行结束和预计反馈不一致, 使用 top 查看一下是否存在异常进程, 检查 log 日志是否存在报错, 使用 nvidia-smi 查看显存占用等进行错误排查。

  • jupyter运行缓存 要注意, juypter运行时会保存之前的结果的, 按照你的点击顺序, cell 左侧的数字, 表明当前 cell 是第几个运行的, 所以应该养成定时重启 kernel 的习惯, 避免环境紊乱导致结果不符合预期。

总结

sky hackthon 的比赛侧重于算法部署的一整个的流程, 最后的结果是一个可以被完善的AI应用, 配合指导老师的耐心教导和活跃的气氛组, 整体比赛氛围非常轻松愉快, 认真备赛、克服困难完成比赛是一定可以的!

但是更多依然偏向于直接使用, 没有深入, 目标检测部分直接使用了现成的YoloV5的TRT实现,虽然效果不错, 但是并非自己所学。希望后续的比赛和实践上可以自己进行plugin编写, 进行并行加速、模型转换等更深入的内容。

总体而言, 本次比赛学到了相当多的内容, 虽然对于AI的认识了解更进一步, 但是依然有很多地方存在不足, 值得思考, 可以进步。