众所周知 Ruby 的机器学习一直是相对弱势的区域,那么它的表现究竟如何呢?本文通过尝试实现一个相对复杂的机器学习算法(DQN)来踩一遍在 Ruby 环境下使用机器学习遇到的坑。由于各式各样的坑,最终 Ruby 版本只完成了模型的 80% 且部分核心依旧借助了 Python。而且因为效率问题,最后也无法真正训练出效果,不过即便如此还是分享出来让大家了解一下吧。
Python 版源码:https://github.com/happybai/atari-agents
Ruby 实验源码:https://github.com/happybai/atari-agents-rb
Atari 游戏环境 Ruby 接口:https://github.com/happybai/ale_ruby_interface
(下图为 Python 版本算法用 CPU 训练1个小时的结果)
[图片上传失败...(image-c50ce0-1559709363531)]
一、Atari(雅达利) 训练环境准备
坑一:缺乏相应环境或相应环境的 Ruby 实现。
很多机器学习的用例都会提供一个学习环境来方便大家来做研究比如,atari 的环境 Arcade-Learning-Environment,增强学习算法的环境 gym, 星际争霸2的学习环境 pysc2。这些环境基本都是 C++ 加上 Python 的接口,或者只有 Python 的版本。好在很多真正有效率的机器学习的库其实都是 C++ 为核心加上 Python 做为接口。本例子中也是以 Arcade-Learning-Environment 为核心,制作了 Ruby 的调用接口 ale_ruby_interface,来实现了训练的环境。是的,这样就可以用 Ruby 玩 Atari 游戏了!
二、相关 Ruby 机器学习库的准备
坑二:主流 Ruby 机器学习库活跃低,更新缓慢,文档不全,不研究一下连装都装不上。
这个例子里用到了两个机器学习的库 NMatrix 和 Tensorflow 的 Ruby 实现 tensorflow.rb
其中 NMatrix 的安装就遇到了问题 https://github.com/SciRuby/nmatrix/issues/591 且不支持高版本的 gcc 编译。
tensorflow.rb 的原理也仅仅是 Ruby 调用 Tensorflow 的 C++ 文件。需要 Bazel 来编译,通过 swig 来调用,安装起来也相对复杂。
三、构建 Tensorflow 算法图
Tensorflow 的基本思想是构建一个算法图,然后加载到 Session 里运行。那么图的构建是 Tensorflow 中非常重要的一步,tensorflow.rb 自然也提供了这部分功能。但是(坑三:tensorflow.rb 提供 api 太少,且文档和错误提示不足)仅仅实现了最核心的部分,所有操作仅通过一个方法 AddOperation 来进行的。具体使用方法,只能去看 Tensorflow 的文档和源码来猜。最关键的是 — 没有错误提示!。参数错了只会报内存溢出。最终我还是放弃了在 Ruby 里进行图的构建,不过好在 tensorflow.rb 里提供了加载已经构建完毕图的方法(实际上 tensorflow.rb 自己的例子也是加载现成的图文件)。所以最终我还是选择从 Python 版本把算法图导出成文件(protobuf 格式),再通过 Ruby 导入,来完成运算图的构建。
四、数据预处理
除了核心图的构建,很多数据的预处理也是构成算法的重要的组成部分,比如本例子中的 History、Memory 类、图片的转换,各种矩阵的 reshape,将训练的结果录成视频。这方面 Ruby 差距就不是很明显了,相反比之 Python 还更顺手一些,只是(坑四:NMatrix 的效率应该不是很好) NMatrix 处理大矩阵效率好像有些差。最终运行的时候效率还不到 Python 版本的 1%,不过关于效率的问题,也有部分原因是有些库我没找到对应的 Ruby 版本就自己随便实现了下,忽略这部分的差距,初步估计应该至少能达到 python 的10%。
五、开始训练
除了上面的效率问题,我刚开始训练模型的时候,跑了10分钟后突然就内存溢出了(坑五:由于 Ruby 会经常和 C++ 的库进行交互,不成熟的库动不动就内存溢出,且原因很难查找)。最后原因也许是 tensorflow.rb 的一个 bug。总之修改了 tensorflow.rb 中的一行源码,重新编译安装后。就解决了这个问题。但这种错误真心及其难调试。最终这个模型终于可以稳定训练了,不过由于效率问题以及其他因素,所以并不会有效果。即便如此,也算是跑了一个相对完整的算法了。
六、使用训练后的成熟模型
虽然上面的模型最终没有训练成功,不过假如我们只是使用训练后的程序模型来玩一下,那 Ruby 应该还是可以的。也就是说,如果不考虑做科学研究,仅仅在应用层面上,也许 Ruby 并没有太多的短板。
七、总结
客观的说,Ruby 的科学计算还是远远落后于 Python,如果我们真想要涉足这个领域,学习一下 Python 的代价可能还不如看两遍线性代数来得大。不过如果在未来,很多算法会变得更加成熟,仅考虑在应用层面,相信 Ruby 也未尝不是一种优秀的选择。
DQN 算法相关:
https://ai.intel.com/demystifying-deep-reinforcement-learning/