用原生 JS 实现深度学习库完成 Chrome Dino 游戏通关

释放双眼,带上耳机,听听看~!
学习如何使用原生JS实现深度学习库完成Chrome Dino游戏的通关,包含正向传播、逆向传播、梯度下降、损失函数、拟合等过程

纸上得来终觉浅,绝知此事要躬行

上一章文章关于机器学习的基础概念搞清楚了,最快的学习方式就是实践,所以这里用原生 JS 不使用任何 XNN 的库完成了一个简易版的深度学习库,完成 Chrome Dino 游戏的通关。

直接上 Demo

用原生 JS 实现深度学习库完成 Chrome Dino 游戏通关

直接上源码: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 运行游戏,里面重要是几个游戏状态和对应的逻辑

github.com/Cowboy-Jr/a…

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

后续的流程就是反复地进行游戏、推理、收集特征数据、训练模型。

后续

具体学习框架的细节,下一篇文章会展开讲解是如何设计的。敬请期待。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

LightGBM算法原理、优点、使用方法及示例代码实现

2023-12-14 11:55:14

AI教程

打倒Chat-GPT(LLMs):Transformer是如何彻底改变我们与机器对话的方式的 🤖

2023-12-14 12:06:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索