您的位置:首页 > 大数据 > 人工智能

游戏AI--决策(1)

2016-05-26 12:00 323 查看

机器学习的引入

机器学习最近由于AlphaGo数据挖掘的大热而变得流行起来,然而我们可以让它变得更加cool,在接下来的模块中,我将讲述我对游戏中应用机器学习的理解以及我的现有成果。

应用机器学习的意义

游戏进行中,尤其是网络游戏,会积累大量的数据,数据赋予了计算机行为意识,而我们如果能“告诉”计算机数据归纳总结的方法,那计算机可能会产生惊人的思维能力,而机器学习就是这样的一种神奇的工具。

前景

目前的游戏由于同质化严重,所以一个高度智能的游戏体验会带给玩家难以想象的快感,而且笔者以为,很有可能在某一天,我们能在游戏中建立真正的人工智能,因为游戏世界高度抽象化,数据的提取难度大大降低,当然,这有点跑题了。

应用机器学习的决策逻辑

游戏的进行,决策和事件无疑是最重要的两个条件,传统的游戏中,我们使用大量的条件判断来进行决策,不仅在逻辑上很繁琐,而且有一个重大的问题,它是“死的”,比如玩家经过一个怪物,怪物必定会攻击玩家,如果要改善这一决策,那需要添加许多条件,比如针对怪物的“攻击性”,重写判断逻辑。

机器学习则不然,我们可以根据以前的记录进行训练,每个怪物都是独特的,即使他们隶属于同一个类,“个性”将使AI十分丰富。

目标–鱼类的遗传

由于机器学习的数学模型非常复杂(对笔者来说是这样的,花了很多功夫才看懂),我们先讲一个应用,然后我们根据其需求来应用机器学习。

在之前的游戏底层逻辑中,笔者曾说过想要实现一款海洋模拟的游戏,之前的只言片语可能已经能描述大概的框架了,不知道也没关系,此处我们有一个问题:捕食行为

捕食行为基本上是动物最重要的行为了,此处有三个需要决策的问题,在进入捕食状态前的判断中,我们需要知道:

1、是否需要捕食

2、捕食什么鱼类(前提是在食物链上的)

3、哪个地方适合捕食

是否捕食

在传统游戏中,设定一个饥饿值,这个值随着时间递减,减少到一定程度就去捕食。

而这种状态是不合理的:

首先,我们吃东西并不一定是饥饿,其他的因素都有可能使其变化。

其次,这种if(1)else(0)的变化在空间上表现出来的是跳跃性的曲线,不符合我们越饥饿越想吃东西这样的特性。

然后,我们的决策没有随机性,很容易预测,很“无聊”。

解决方案

我们设定一个合理的初始函数:

H(x)=x1∗θ1+x2∗θ2+...+xn∗θn

矩阵(向量)表达:

H(x)=XTθ

其实就是多元一次方程,H函数得到一个值,然后我们根据值进行决策。

此处提及几个概念:

θ我们称其为权值,意思是该变量对决策产生影响的重要程度。

x此处称为特性。

根据此函数,我们输入一系列x,就能得出决策结果。然后你们可能发现了一个问题,θ怎么解决,我们先根据经验定义一个值,然后进行训练,最终得出一个权值。

遗传

所谓遗传是怎么一回事呢,此处我们每个鱼类种群共享一个H函数,鱼在生产下一代的时候,将完全继承(clone)或者部分继承(block copy)该函数,此处我们降维处理,将其直接复制。在这种机制下,拥有好的H函数的鱼群将存活下来,并且繁衍生息,而拥有差的H函数的鱼群就被淘汰了。

针对H函数进行训练,LogisticRegression

此处不会讲解关于机器学习的基本知识,太多而且笔者水平有限,讲解不清楚。

下面的模块讲解LogisticRegression(以下简称LR)的基本理解,如果不感兴趣可以跳过直接看公式。

我们已经知道了H函数,此处称为数学模型。

H(x)=x1∗θ1+x2∗θ2+...+xn∗θn

现在我们有很多的已知的数据:

x1x2x3H(x),或者叫做y
1234
2345
诸如此类的,此处的数据最好是数值型,如果是标称型(ex.颜色:红,黄,蓝),有比较复杂的转换过程,推荐使用其他的学习算法诸如树回归一类的。

误差函数:H函数根据我们现在已知的权值会得出一个结果,但是它和上表中的H(x)是不同的,我们把现在的权值代入每一组数据求结果,然后用真实结果(表中的H(x),用y表示)做差,这就是误差函数。

J(θ)=12∑i=0n(H(xi)−yi)2

梯度下降

此处我们要克服误差,所以要沿着误差函数变化最快的方向改善我们的θ,该方向叫做梯度方向,二维数学表达式为:

∇f(x,y)=(∂f(x,y)∂x,∂f(x,y)∂y)

多维时相同,也就是对误差函数求偏导。

结果如下:

∑i=0n(yi−H(xi))∗xi

随机梯度下降

为了减少计算复杂度,此处我们使用随机梯度下降,将误差函数的比较范围由整个数据集合变为其中一次(此处直接为新添加的一次):

(yi−H(xi))∗xi

然后我们的沿着这个方向一点点的改善,的到训练的迭代公式:

好了,公式就是这个,以上是推导过程:

θ:=θ−α∇f(xT)

LR代码部分:

首先引入c++矩阵库:Eigen,这个库一般般,用起来不是很顺手,由于字符对齐问题,用VS编译的同学,该库的矩阵类型请自行使用引用&。

#pragma once

#include<Eigen/Dense>
#include"cocos2d.h"
#include<vector>
using namespace Eigen;

template<class ElementType, int ElementNum>
class LogisticRegression//the number of sample elements is best less than 16
{
public:
LogisticRegression(std::string owner)
{
assert(ElementNum < 16 &&
"TOO LONG!!!!");
this->getDataFromFile();
_owner = owner;
}
~LogisticRegression()  {}

virtual bool initWithData(Matrix<ElementType, ElementNum, 1 >& weightMatrix,double pace)
{
_pace = pace;
try
{
_weightMatrix = weightMatrix;
}
catch (ElementType)
{
return false;
}
return true;
}
public:

virtual bool getDataFromFile()
{
for (int i = 0; i < _weightMatrix.rows(); i++)
{
_weightMatrix(i, 0) = (ElementType)cocos2d::UserDefault::getInstance()->getDoubleForKey(
cocos2d::StringUtils::format("Fish_%s_weight_%d,", _owner, i).c_str()
);
}

}

void training(Matrix<ElementType, ElementNum + 1, 1>& data)//a group of data
{
Matrix<ElementType, ElementNum, 1> xi;
for (int i = 0; i < ElementNum; i++)
{
xi(i, 0) = data(i, 0);
}

_weightMatrix = _weightMatrix + _pace*(data(ElementNum, 0) - H_func(xi))*xi;
}

void training(Matrix<ElementType, ElementNum + 1, Eigen::Dynamic>& datas, bool overwide)//more than one
{
for (int i = 0; i < datas.cols(); i++)
{
Matrix<ElementType, ElementNum + 1, 1> data = data.col(i);
training(data);
}
}

ElementType resultBeforeSigmoid(Matrix<ElementType, ElementNum, 1>& data)
{
return data.dot(_weightMatrix);
}

bool result(Matrix<ElementType, ElementNum, 1>& data)//we use boolean to describe the result
{
return sigmoid(resultBeforeSigmoid(data));
}

virtual bool record()
{
for (int i = 0; i < _weightMatrix.rows(); i++)
{
cocos2d::UserDefault::getInstance()->setDoubleForKey(
cocos2d::StringUtils::format("Fish_%s_weight_%d,", _owner, i).c_str(),
(double)_weightMatrix(i, 0)
);
}
}

protected:

bool sigmoid(ElementType num)
{
ElementType  result = 1 / (1 + exp(-1 * num));
return result > 0.5;
}

double H_func(Matrix<ElementType, ElementNum, 1>& data)
{
return data.dot(_weightMatrix);
}

private:
Matrix<ElementType, ElementNum, 1> _weightMatrix;
double _pace;//the speed of convergence
std::string _owner;
};


此处并没有对大规模训练做优化,读取数据也需要童鞋们自己动手,一个基本的类,我们讲完了所有的决策再来谈谈怎么在实例中应用这些模块。

准备写一个有关游戏底层算法,物理算法,以及AI(重点是机器学习在游戏中的应用)的长篇博客,欢迎大家指正交流╰( ̄▽ ̄)╯
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  机器学习 游戏