本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.
从0开始学习 Deep Learning – 02手写纯JS的深度学习玩耍 Chrome 恐龙游戏(一)
2022年09月24日 22:42· 阅读 74
纸上得来终觉浅,绝知此事要躬行
上一章文章关于机器学习的基础概念搞清楚了,最快的学习方式就是实践,所以这里用原生 JS 不使用任何 XNN 的库完成了一个简易版的深度学习库,完成 Chrome Dino 游戏的通关。
直接上 Demo
直接上源码: github.com/Cowboy-Jr/a…
麻雀虽小五脏俱全,包含了正向传播、逆向传播、梯度下降、损失函数、拟合等等
前置准备
按照典型的学习方法论,需要理解监督学习的运作模式,也就是
收集数据 -> 获取特征 -> 推理 -> 输出结果 -> 验证是否正确 ^ | | | |_______________________________________V
所以对应在 Chrome Dino 这个游戏里就是
跑 -> 获取障碍物信息 -> 推理 -> 是否跳 -> 验证是否正确 ^ | | | |_______________________________________V
有了这个思路就可以工程化项目,但是如何运用数学知识完成机器学习逻辑是个比较麻烦的事情,这个后面继续讲解,我们先从上往下的讲解如何运行这个 AI 游戏。
工程
仓库目录大致如下
- src |- ai-deno.ts // 入口文件 |- game // Chrome Dino 游戏 |- jnn |- fm.ts // 深度学习库 |- legend.ts // 图例
Game 仓库
Chrome Dino 的游戏库是从网上 Fork 出来的,没什幺特色之处,中规中矩,唯一之处就是为了能收集特征数据,新增了几个状态的回调。
ai-deno.ts
主入口文件,结合 Game 和 JNN 和 legend 运行游戏,里面重要是几个游戏状态和对应的逻辑
dino = new Runner('.game', { DINO_COUNT: 1, // 第一次执行,用于初始化状态 onFirstTime: handleFirstTime, // 重新运行,用于训练模型 onReset: handleReset, // 每次游戏结束,用于收集失败时的特征数据 onCrash: handleCrash, // 跑的时候,用于推理是否需要跳起躲避障碍物 onRunning: handleRunning });
分析
初始化状态 onFirstTime
从前面的所提到回调逻辑,可以看出我们需要初始化一些状态,比如说整个 NN 框架的状态,每个 Layer、每个 neuron 的参数,另外就是训练数据的初始化值。
let trainingData = { input: [], output: [], };
这里的训练数据 input 包含了每次游戏失败恐龙离障碍物的距离、速度、障碍物的大小,Output 存储的是每次游戏失败时,Dino 的上一个的状态,例如是跳起还是跑步。
跑步 onRunning
我们知道在恐龙运行的时候,需要根据当前 Dino 的状态,获取特征数据放入框架中推理他是否应该跳起。
const handleRunning = async (dino, state) => { const input = convertStateToVector(state); let action = 0; // running if (dino.jumping === false) { const [output0, output1] = nn.predict(input); if (output1 > output0) { // need jump action = 1; dino.lastJumpingState = state; } else { // keep running action = 0; dino.lastRunningState = state; } } // jumping await sleep(10) updateLegend(nn, input); return action; };
state 包含了,当前恐龙的状态。但是,这些状态只并不能直接使用,我们必须要把数据进行归一化,具体为什幺需要归一化,可以网上自行查找。
然后我们拿着归一化数据放入框架中进行推理,判断他是否需要跳起。并把当前的推断存储起来用于游戏失败时的状态保存,这样的话,我们在训练模型时可以知道上一次到底是对的还是错的。
游戏失败 onCrash
每次游戏失败时,会把恐龙的状态存储起来,归一化处理后,放入训练特征数据中,并且吧,应该输出的值也保存在 output 中。
const handleCrash = async (dino) => { let input = null; let output = null; if (dino.jumping) { // 获取最后一次跳跃状态 input = convertStateToVector(dino.lastJumpingState); // 不跳 output = [1, 0]; } else { // 获取当前行走状态 input = convertStateToVector(dino.lastRunningState); // 跳 output = [0, 1]; } trainingData.input.push(input); trainingData.output.push(output); };
游戏重新开始 onReset
游戏开始后,我们拿着训练特征数据放到框架中进行反向传播训练,训练框架中的参数也就是所谓的模型,具体细节下一篇文章会展开讲解
const handleReset = async () => { await sleep(1000); console.log(trainingData); // training data nn.fit(trainingData.input, trainingData.output, { async onEpochFinish(trainData) { // updateLegend(nn, trainData); } }); };
Forever
后续的流程就是反复地进行游戏、推理、收集特征数据、训练模型。
后续
具体学习框架的细节,下一篇文章会展开讲解是如何设计的。敬请期待。
Be First to Comment