之前写过一篇《这个中秋,我开发了一个识别狗狗的app》。图片识别可以算作是深度学习领域烂大街的主题,几乎每本书和教程都会拿来作为入门示例。移动端的图片识别的教程也很多,大多数都脱胎于Google的教程《TensorFlow for Poets》和《TensorFlow for Poets 2: Android》。有了现成的教程,我对实现狗狗的图像识别信心满满,认为重点在于信息的展示及狗狗信息的收集。
然而多年的开发经验告诉我,真正自己做起来,一定会碰到问题,特别是像tensorflow lite这种频繁迭代的产品。果然,我就一头栽进了tensorflow lite 的巨坑里。下面我就说道说道这个坑。
参考上面提到的教程,将基本功能实现出来还算顺利。然而,等代码build出来,安装到手机上一测试,拿一张狗狗的图片识别,结果的top 1概率都是0.0 ~ 0.02之间,使用训练的图片测试也是如此。为了更好的进行测试,我写了一个测试用例:predictSingleImage(),直接测试sd卡上的图片。排查问题过程如下:
- 确认传递给ImageClassifier的bitmap是正常的,这个可以通过将bitmap保存到文件确认。经过人眼观察,送到ImageClassifier的bitmap中狗狗的图像还是很明显的。
- 确认模型问题。我开始采用的是Google Inception V3模型,换成教程中使用的mobilenet模型,问题依旧。将我训练出来的mobilenet模型放到TensorFlow for Poets 2: Android的示例代码中,工作正常,虽然准确率不高,但至少top 1的概率大于0.4。可以确认模型没有问题。
- 确认代码,基本上复制的TensorFlow for Poets 2: Android示例代码,不可能出现问题。
到了这一步,就非常郁闷了,于是上网搜索,看有没有和我碰到同样问题的。果然,有个哥们在stackoverflow上提了一个问题:Accuracy score of inceptionV3 tflite model is not good as of retrained_graph.pb file,链接在此:https://stackoverflow.com/questions/49952165/accuracy-score-of-inceptionv3-tflite-model-is-not-good-as-of-retrained-graph-pb
这个帖子将问题步骤描述得很详细,最后贴出了测试结果:
然而,这个问题没有人回答。这一下子又陷入了困顿,有一阵子甚至开始怀疑人生:识别静态照片和camer流中一帧图像难道有本质区别吗?
一次搜索中,我无意中看到一个帖子:TfLite Image classification score is not consistent it keeps increasing for same image untill it reaches to some saturation(actual score)。链接地址:https://groups.google.com/a/tensorflow.org/forum/#!topic/tflite/fREwHb4rAPM
外国人提问题都比较严谨,这个帖子也贴了代码,大意就是说对于同一张图片,Tflite的图片分类的结果不一样,每识别一次,top 1概率就会提升,直到到达一个固定值。
看到这里,我才恍然大悟。回想我的代码和教程代码不同之处就在于:我只调用了一次识别过程,而教程是针对图像帧进行识别,会反复调用识别过程,可能之前的概率很低,但很快被后面的结果给覆盖了。
开始,我猜测是代码中tensorflow lite没有初始化好就调用其识别过程。但我在测试代码中加入延时,没有效果。加入循环,对一个图片反复识别几次,后面的识别就正常了。值得一提的是,不需要反复识别同一图片,换几个图片识别,后面再换其他的图片识别一样没问题。为此我添加了predictImages()方法,测试一系列图片,可以看到开始的top 1 score很低,但识别了几张之后,后面的top 1概率就正常了。
这时,我算是明白,我真的跌进tensorflow lite的巨坑里面了。Tensorflow Lite出现这样一个问题也真是匪夷所思,同样的输入和同样的处理,输出结果却不同,真的颠覆了我对编程的理解。
当年爱因斯坦面对量子力学,提出了“上帝是在执骰子吗?”的疑问。
深度学习也是如此?