关于人工智能:mlagents项目实践一

4次阅读

共计 9693 个字符,预计需要花费 25 分钟才能阅读完成。

本文首发于:行者 AI

强化学习 (reinforcement learning) 是机器学习和人工智能里的一类问题,钻研如何通过一系列的程序决策来达成一个特定指标。它是一类算法,是让计算机实现从一开始什么都不懂,脑袋里没有一点想法,,通过一直地尝试,从谬误中学习,最初找到法则,学会了达到目标的办法。这就是一个残缺的强化学习过程。这里咱们能够援用下方图做一个更直观形象的解释。

Agent 为智能体,也就是咱们的算法,在游戏当中以玩家的模式呈现。智能体通过一系列策略,输入一个行为 (Action) 从而作用到环境 (Environment),而环境则返回作用后的状态值也就是图中的察看(Observation) 和处分值(Reward)。当环境返回处分值给智能体之后,更新本身所在的状态,而智能体获取到新的 Observation。

1. ml-agents

1.1 介绍

目前游戏大部分 Unity 游戏数量宏大,引擎欠缺,训练环境好搭建。因为 Unity 能够跨平台,能够在 Windows、Linux 平台下训练后再转成 WebGL 公布到网页上。而 mlagents 是 Unity 的一款开源插件,能让开发者在 Unity 的环境下进行训练,甚至不必去编写 python 端的代码,不必深刻了解 PPO,SAC 等算法。只有开发者配置好参数,就能够很轻松的应用强化学习的算法来训练本人的模型。

如对算法有趣味,请点此处能够学习算法 PPO,SAC。

<u> 更多理解点击返回 </u>

1.2 Anaconda、tensorflow 及 tensorboard 装置

本文介绍的 ml-agents 须要通过 Python 与 Tensorflow 通信,训练时从 ml-agents 的 Unity 端拿到 Observation、Action、Reward、Done 等信息传入 Tensorflow 进行训练,而后将模型的决策传入 Unity。因而在装置 ml-agents 前,须要依据如下链接进行 tensorflow 的装置。

Tensorboard 不便数据可视化,不便分析模型是否达到预期。

装置具体点击返回

1.3 ml-agents 装置步骤

(1) 返回 github 下载 ml-agents (本实例采纳 release6 版本)

github 能够下载

(2) 将压缩包解压,把 com.unity.ml-agentscom.unity.ml-agents.extensions 放入 Unity 的 Packages 目录下(如果没有请创立一个),将manifest.json 中退出此两个目录。

(3) 装置实现后,到工程中就导入后,建设个新脚本,输出以下援用以验证装置胜利

using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Policies;

public class MyAgent : Agent

{

}

2. ml-agents 训练实例

2.1 概要及工程

Environment 通常利用马尔可夫过程来形容,agent 通过采取某种 policy 来产生 Action,和 Environment 交互,产生一个 Reward。之后 agent 依据 Reward 来调整优化以后的 policy。

本例理论工程参考消消乐规定,凑齐三个同样的色彩即可得分,本实例去除了四个连色及多连的额定处分(以不便设计环境)

工程实例下载处 点击返回

Unity 工程导出局部请参考官网 点击返回。

上面将从四个角度来分享我的项目我的项目实际的办法,接口抽离、选算法、设计环境、参数调整。

2.2 游戏框架 AI 接口抽离

将工程的 Observation、Action 须要的接口从游戏中抽离进去。用于传入游戏以后的状态和执行游戏的动作。

static List<ML_Unit> states = new List<ML_Unit>();

public class ML_Unit
{public int color = (int)CodeColor.ColorType.MaxNum;
    public int widthIndex = -1;
    public int heightIndex = -1;
}
// 从以后画面中,拿到所有方块的信息,蕴含所在位置 x(长度), 地位 y(高度), 色彩(坐标轴零点在左上)public static List<ML_Unit> GetStates()
{states.Clear();
    var xx = GameMgr.Instance.GetGameStates();
    for(int i = 0; i < num_widthMax;i++)
    {for(int j = 0; j < num_heightMax; j++)
        {ML_Unit tempUnit = new ML_Unit();
            try
            {tempUnit.color = (int)xx[i, j].getColorComponent.getColor;
            }
            catch
            {Debug.LogError($"GetStates i:{i} j:{j}");
            }
            tempUnit.widthIndex = xx[i, j].X;
            tempUnit.heightIndex = xx[i, j].Y;
            states.Add(tempUnit);
        }
    }
    return states;
}

public enum MoveDir
{
    up,
    right,
    down,
    left,
}

public static bool CheckMoveValid(int widthIndex, int heigtIndex, int dir)
{
    var valid = true;
    if (widthIndex == 0 && dir == (int)MoveDir.left)
    {valid = false;}
    if (widthIndex == num_widthMax - 1 && dir == (int)MoveDir.right)
    {valid = false;}

    if (heigtIndex == 0 && dir == (int)MoveDir.up)
    {valid = false;}

    if (heigtIndex == num_heightMax - 1 && dir == (int)MoveDir.down)
    {valid = false;}
    return valid;
}

// 执行动作的接口,依据地位信息和挪动方向,调用游戏逻辑挪动方块。widthIndex 0-13,heigtIndex 0-6,dir 0-3 0 上 1 右 2 下 3 左
public static void SetAction(int widthIndex,int heigtIndex,int dir,bool immediately)
{if (CheckMoveValid(widthIndex, heigtIndex, dir))
    {GameMgr.Instance.ExcuteAction(widthIndex, heigtIndex, dir, immediately);
    }
}

2.3 游戏 AI 算法抉择

走入强化学习我的项目的第一个课题,面对泛滥算法,抉择一个适合的算法能事倍功半。如果对算法的个性还不太熟悉,能够间接应用 ml-agents 自带的 PPO 和 SAC。

本例笔者最开始应用的 PPO 算法,尝试了比拟多的调整,均匀 9 步能力走对一步,成果比拟蹩脚。

起初仔细分析游戏的环境,因为此工程的三消类的游戏,每次的环境都齐全不一样,每一步的后果对下一步产生的影响并没有多大关系,对马尔科夫链的需要不强。因为 PPO 是 OnPolicy 的 policy-based 的算法,每次更新的策略更新十分小心,导致后果很难收敛(笔者尝试了 XX 布,仍然没有收敛)。

相比 DQN 是 OffPolicy 的 value-base 算法,能够收集大量环境的参数建设 Qtable,逐渐找到对应的环境的最大值。

简略地说,PPO 是在线学习,每次本人跑几百步后,回过头来学习这几百步哪里做得对,哪里做的不对,而后更新学习后,再跑几百步,如此重复。这样学习效率慢不说,还很难找到全局最优的解。

而 DQN 是离线学习,能够跑上亿步,而后回去把这些跑过的中央都拿进去学习,而后很容易找到全局最优的点。

(本例应用 PPO 做演示,后续分享在 ml-agents 外接算法,应用内部工具 stable_baselines3,采纳 DQN 的算法来训练)

2.4 游戏 AI 设计环境

当咱们确定了算法框架之后,如何设计 Observation、Action 及 Reward,便成了决定训练成果的决定性因素。在这个游戏中,环境的这里的环境次要有两个变量,一个是方块的地位,另一个是方块的色彩。

–Observation:

针对如果上图,咱们的本例长 14、宽 7、色彩有 6 种。

ml-agents 应用的 swish 作为激活函数,能够应用不太大的浮点数(-10f ~10f),然而为了让 agents 取得环境更污浊,训练成果更现实,咱们还是须要对环境进行编码。

本例笔者应用 Onehot 的形式进行环境编码,左上角定位坐标零点。如此下来,左上角的青色方块的环境编码就能够示意为 长[0,0,0,0,0,0,0,0,0,0,0,0,0,1],

高[0,0,0,0,0,0,1],色彩按固定枚举来解决(黄, 绿, 紫, 粉, 蓝, 红)色彩[0,0,0,0,1,0]。

环境总共蕴含(14+7+6)14 * 7 = 2646

代码示例:

public class MyAgent : Agent
{static List<ML_Unit> states = new List<ML_Unit>();
    public class ML_Unit
    {public int color = (int)CodeColor.ColorType.MaxNum;
        public int widthIndex = -1;
        public int heightIndex = -1;
    }

    public static List<ML_Unit> GetStates()
    {states.Clear();
        var xx = GameMgr.Instance.GetGameStates();
        for(int i = 0; i < num_widthMax;i++)
        {for(int j = 0; j < num_heightMax; j++)
            {ML_Unit tempUnit = new ML_Unit();
                try
                {tempUnit.color = (int)xx[i, j].getColorComponent.getColor;
                }
                catch
                {Debug.LogError($"GetStates i:{i} j:{j}");
                }
                tempUnit.widthIndex = xx[i, j].X;
                tempUnit.heightIndex = xx[i, j].Y;
                states.Add(tempUnit);
            }
        }
        return states;
    }

    List<ML_Unit> curStates = new List<ML_Unit>();
    public override void CollectObservations(VectorSensor sensor)
    {
        // 须要判断是否方块挪动完结,以及方块结算完结
        var receiveReward = GameMgr.Instance.CanGetState();
        var codeMoveOver = GameMgr.Instance.IsCodeMoveOver();
        if (!codeMoveOver || !receiveReward)
        {return;}

        // 取得环境的状态信息
        curStates = MlagentsMgr.GetStates();
        for (int i = 0; i < curStates.Count; i++)
        {sensor.AddOneHotObservation(curStates[i].widthIndex, MlagentsMgr.num_widthMax);
            sensor.AddOneHotObservation(curStates[i].heightIndex, MlagentsMgr.num_heightMax);
            sensor.AddOneHotObservation(curStates[i].color, (int)CodeColor.ColorType.MaxNum);
        }
    }
}

–Action:

每个方块能够上下左右挪动,咱们须要记录的最小信息蕴含,14* 7 个方块,以及每个方块能够挪动 4 个方向,本例方向枚举(上,右,下,左)。

左上为零点,左上角的青色方块占据了 Action 的前四个动作,别离是(左上角的青色方块向上挪动,左上角的青色方块向右挪动,左上角的青色方块向下挪动,

左上角的青色方块向左挪动)。

那么动作总共蕴含 14 7 4 = 392

仔细的读者可能会发现 左上角的青色方块 并不能往上和往左挪动,这时咱们须要设置 Actionmask,来屏蔽掉这些在规定上禁止的动作。

代码示例:

public class MyAgent : Agent
{
    public enum MoveDir
    {
        up,
        right,
        down,
        left,
    }


    public void DecomposeAction(int actionId,out int width,out int height,out int dir)
    {width = actionId / (num_heightMax * num_dirMax);
        height = actionId % (num_heightMax * num_dirMax) / num_dirMax;
        dir = actionId % (num_heightMax * num_dirMax) % num_dirMax;
    }

    // 执行动作,并取得该动作的处分
    public override void OnActionReceived(float[] vectorAction)
    {
        // 须要判断是否方块挪动完结,以及方块结算完结
        var receiveReward = GameMgr.Instance.CanGetState();
        var codeMoveOver = GameMgr.Instance.IsCodeMoveOver();
        if (!codeMoveOver || !receiveReward)
        {Debug.LogError($"OnActionReceived CanGetState = {GameMgr.Instance.CanGetState()}");
            return;
        }

        if (invalidNums.Contains((int)vectorAction[0]))
        {
            // 方块结算的调用,这里能够取得处分(这里是惩办,因为这是在屏蔽动作内,训练的时候会调用所有的动作,在非训练的时候则不会进此逻辑)GameMgr.Instance.OnGirdChangeOver?.Invoke(true, -5, false, false);
        }
        DecomposeAction((int)vectorAction[0], out int widthIndex, out int heightIndex, out int dirIndex);
        // 这里回去执行动作,挪动对应的方块,朝对应的方向。执行结束后会取得处分,并依据状况重置场景
        MlagentsMgr.SetAction(widthIndex, heightIndex, dirIndex, false);
    }

    //MlagentsMgr.SetAction 调用后,执行完动作,会进入这个函数
    public void RewardShape(int score)
    {
        // 计算取得的处分
        var reward = (float)score * rewardScaler;
        AddReward(reward);
        // 将数据退出 tensorboard 进行统计分析
        Mlstatistics.AddCumulativeReward(StatisticsType.action, reward);
        // 每一步蕴含惩办的动作,能够晋升摸索的效率
        var punish = -1f / MaxStep * punishScaler;
        AddReward(punish);
        // 将数据退出 tensorboard 进行统计分析
        Mlstatistics.AddCumulativeReward(StatisticsType.punishment, punish);
    }

    // 设置屏蔽动作 actionmask
    public override void CollectDiscreteActionMasks(DiscreteActionMasker actionMasker)
    {
        // Mask the necessary actions if selected by the user.
        checkinfo.Clear();
        invalidNums.Clear();
        int invalidNumber = -1;
        for (int i = 0; i < MlagentsMgr.num_widthMax;i++)
        {for (int j = 0; j < MlagentsMgr.num_heightMax; j++)
            {if (i == 0)
                {invalidNumber = i * (num_widthMax + num_heightMax) + j * num_heightMax + (int)MoveDir.left;
                    actionMasker.SetMask(0, new[] {invalidNumber});
                }
                if (i == num_widthMax - 1)
                {invalidNumber = i * (num_widthMax + num_heightMax) + j * num_heightMax + (int)MoveDir.right;
                    actionMasker.SetMask(0, new[] {invalidNumber});
                }

                if (j == 0)
                {invalidNumber = i * (num_widthMax + num_heightMax) + j * num_heightMax + (int)MoveDir.up;
                    actionMasker.SetMask(0, new[] {invalidNumber});
                }

                if (j == num_heightMax - 1)
                {invalidNumber = i * (num_widthMax + num_heightMax) + j * num_heightMax + (int)MoveDir.down;
                    actionMasker.SetMask(0, new[] {invalidNumber});
                }
            }
        }
    }
}

原工程打消过程中应用大量协程,有很高的提早,咱们须要再训练时把提早的工夫挤出来。

为了不影响游戏的主逻辑,个别状况下把协程外面的 yield return new WaitForSeconds(fillTime)中的 fillTime 改成 0.001f, 这样能够在不大量批改游戏逻辑的状况下,在模型抉择 Action 后能最快失去 Reward。

public class MyAgent : Agent
{private void FixedUpdate()
    {var codeMoveOver = GameMgr.Instance.IsCodeMoveOver();
        var receiveReward = GameMgr.Instance.CanGetState();
        if (!codeMoveOver || !receiveReward /*||!MlagentsMgr.b_isTrain*/)
        {return;}
        // 因为有协程须要等待时间,须要期待产生 Reward 后才去申请决策。所以不能应用 ml-agents 自带的 DecisionRequester
        RequestDecision();}
}

2.5 参数调整

在设计好模型后,咱们先初步跑一版本,看看后果跟咱们设计的预期有多大的差别。

首先配置 yaml 文件,用于初始化网络的参数:

behaviors:
SanXiaoAgent:
trainer_type: ppo
hyperparameters:
batch_size: 128
buffer_size: 2048
learning_rate: 0.0005
beta: 0.005
epsilon: 0.2
lambd: 0.9
num_epoch: 3
learning_rate_schedule: linear
network_settings:
normalize: false
hidden_units: 512
num_layers: 2
vis_encode_type: simple
memory: null
reward_signals:
extrinsic:
gamma: 0.99
strength: 1.0
init_path: null
keep_checkpoints: 25
checkpoint_interval: 100000
max_steps: 1000000
time_horizon: 128
summary_freq: 1000
threaded: true
self_play: null
behavioral_cloning: null
framework: tensorflow

训练代码请参照官网提供的接口,本例应用 release6 版本,命令如下

mlagents-learn config/ppo/sanxiao.yaml --env=G:\mylab\ml-agent-buildprojects\sanxiao\windows\display\121001display\fangkuaixiaoxiaole --run-id=121001xxl --train --width 800 --height 600 --num-envs 2 --force --initialize-from=121001

训练实现后,关上 Anaconda,在 ml-agents 工程主目录上输出 tensorboard –logdir=results –port=6006,复制 http://PS20190711FUOV:6006/ 到浏览器上关上,即可看到训练后果。

(mlagents) PS G:\mylab\ml-agents-release_6> tensorboard --logdir=results --port=6006
TensorBoard 1.14.0 at http://PS20190711FUOV:6006/ (Press CTRL+C to quit)

训练效果图如下:

move count 为消掉一次方块,须要走的均匀步数,大略须要 9 布能力走正确一步。在应用 Actionmask 状况下,能够在 6 步左右打消一次方块。

–Reward:

依据下面表格的 Reward,查看处分处分设计的均值。笔者喜爱管制在 0.5 到 2 之间。过大过小能够调整 rewardScaler。

//MlagentsMgr.SetAction 调用后,执行完动作,会进入这个函数
public void RewardShape(int score)
{
    // 计算取得的处分
    var reward = (float)score * rewardScaler;
    AddReward(reward);
    // 将数据退出 tensorboard 进行统计分析
    Mlstatistics.AddCumulativeReward(StatisticsType.action, reward);
    // 每一步蕴含惩办的动作,能够晋升摸索的效率
    var punish = -1f / MaxStep * punishScaler;
    AddReward(punish);
    // 将数据退出 tensorboard 进行统计分析
    Mlstatistics.AddCumulativeReward(StatisticsType.punishment, punish);
}

3. 总结及杂谈

目前 ml-agents 官网做法应用模拟学习,应用专家数据在训练网络。

笔者在此例中尝试 PPO,有肯定的成果。但 PPO 目前针对三消训练起来有肯定难度的,比拟难收敛,很难找到全局最优。

设置环境和 Reward 须要谨严的测试,否则对后果会产生极大的误差,且难以排查。

强化学习目前算法迭代比拟快,如果以上有谬误的中央,欢送斧正,大家一起提高。

因篇幅无限,不能把整个我的项目的代码全放进去,如有趣味钻研的同学,能够在下方留言,我能够残缺我的项目通过邮箱的形式发给大家。

后续将分享在 ml-agents 外接算法,应用内部工具 stable_baselines3,采纳 DQN 的算法来训练。

正文完
 0