149k张图片,最后真正拿来训练的只有约2万张。这不是模型压缩,是数据清洗的结果。有人从零训练了一个能在手机或iPad上离线运行的食品识别模型,不用联网、不调云端API,而整个项目里最难的部分不是模型,是数据。

目标很直接:拍一张照片,模型告诉你每样东西是什么、有几个、哪些看起来已经变质或腐烂、哪些根本不是食物。难点在于这一切必须在没有网络连接的设备上完成,模型犯迷糊的时候没有云端可以兜底。

打开网易新闻 查看精彩图片

从149k到49k,先做减法

手头大约有149k张来自不同来源的图片。先剔除掉一个没有边界框标注的大数据集后,剩下11个数据集、49,158张图片可用。

问题在于,每个数据集都有自己的命名习惯、自己对"好照片"的标准,而且大量重复图片反复出现。所以在训练任何东西之前,第一件正经工作是决定扔掉什么。

作者把整个过程按在Google Colab里实际运行的顺序写了下来,包括踩过的坑和修法。有些错误是作者自己犯的,作者把它们保留在流程里,因为大部分收获正来自这些地方。

数据在每个阶段都在缩水

最让人意外的是数据集在每一个环节都会变小。这意味着最终训练用的根本不是49k张,而是约2万张,每一次缩水都有具体原因。

这个任务需要GPU。完整跑一轮意味着在几轮清洗之后、在一个大数据集上跑很多个epoch,用CPU要花几天,而Colab的T4几个小时就能完成。所以每次会话的第一件事就是检查分到了什么硬件。

作者拿到的是Tesla T4,15.6 GB显存。如果显示没有可用GPU,就得付费购买或者找一个提供免费GPU的平台。

三个阶段,先跑通再放大

整个运行由几个变量控制,作者习惯把它们集中放在顶部,省得后面到处找。工作被规划成三个阶段:

  1. 先跑一个基线,看看现成模型知道些什么;
  2. 再做一次5000张的小规模训练,验证整条流水线能跑通;
  3. 试点看起来合理之后,才进行完整训练。

试点这一步省了很多痛苦,因为在小规模运行上发现的bug,如果只在大规模运行上才暴露,会多花好几个小时。

数据搬运环节出了两个岔子。第一个是不完整解压:一次断连之后,得到一个半解压的文件夹,看起来完全正常,实际缺文件。作者是靠清点图片数量、和压缩包总数对比才发现的。现在的做法是删掉文件夹、重新解压、确认数量是49,158再继续。

第二个是多出一层嵌套:压缩包解压到了 data/data/datasets,硬编码路径直接失效。解决办法是用glob去找datasets文件夹,不管它落在哪里。

类别命名,混乱程度超出预期

接下来读取每一个data.yaml,打印出每个数据集的类别。这一步看起来无聊,但它最能暴露数据到底有多乱。

同样的东西被叫成很多种名字。番茄在一个数据集里叫 tomato,另一个叫 vine-tomato,第三个叫 beef-tomato;苹果有 granny-smith、pink-lady、royal-gala 几种叫法;彩椒按颜色区分,有时写成 bell-pepper,有时写成 bell_pepper。此外还有一长串过于具体的标签,把品牌名和规格都塞进了包装产品的类别里。

如果放着不管,每一个变体都会变成独立类别,总数会远远超过真正关心的不同物品数量。

更大的问题是编号。在一个数据集里鸡蛋是类别6,在另一个里是类别5。如果两个数据集混在一起训练而不修正,等于告诉模型同一个物体是两种不同的东西,而两种不同的东西是同一种。

还有一个坑:用glob找标签文件时,README文件也被一起捞了进来。