赞
踩
机器学习最近由于AlphaGo和数据挖掘的大热而变得流行起来,然而我们可以让它变得更加cool,在接下来的模块中,我将讲述我对游戏中应用机器学习的理解以及我的现有成果。
游戏进行中,尤其是网络游戏,会积累大量的数据,数据赋予了计算机行为意识,而我们如果能“告诉”计算机数据归纳总结的方法,那计算机可能会产生惊人的思维能力,而机器学习就是这样的一种神奇的工具。
目前的游戏由于同质化严重,所以一个高度智能的游戏体验会带给玩家难以想象的快感,而且笔者以为,很有可能在某一天,我们能在游戏中建立真正的人工智能,因为游戏世界高度抽象化,数据的提取难度大大降低,当然,这有点跑题了。
游戏的进行,决策和事件无疑是最重要的两个条件,传统的游戏中,我们使用大量的条件判断来进行决策,不仅在逻辑上很繁琐,而且有一个重大的问题,它是“死的”,比如玩家经过一个怪物,怪物必定会攻击玩家,如果要改善这一决策,那需要添加许多条件,比如针对怪物的“攻击性”,重写判断逻辑。
机器学习则不然,我们可以根据以前的记录进行训练,每个怪物都是独特的,即使他们隶属于同一个类,“个性”将使AI十分丰富。
由于机器学习的数学模型非常复杂(对笔者来说是这样的,花了很多功夫才看懂),我们先讲一个应用,然后我们根据其需求来应用机器学习。
在之前的游戏底层逻辑中,笔者曾说过想要实现一款海洋模拟的游戏,之前的只言片语可能已经能描述大概的框架了,不知道也没关系,此处我们有一个问题:捕食行为
捕食行为基本上是动物最重要的行为了,此处有三个需要决策的问题,在进入捕食状态前的判断中,我们需要知道:
1、是否需要捕食
2、捕食什么鱼类(前提是在食物链上的)
3、哪个地方适合捕食
在传统游戏中,设定一个饥饿值,这个值随着时间递减,减少到一定程度就去捕食。
而这种状态是不合理的:
我们设定一个合理的初始函数:
此处提及几个概念:
θ 我们称其为权值,意思是该变量对决策产生影响的重要程度。
x 此处称为特性。
根据此函数,我们输入一系列x,就能得出决策结果。然后你们可能发现了一个问题,
所谓遗传是怎么一回事呢,此处我们每个鱼类种群共享一个H函数,鱼在生产下一代的时候,将完全继承(clone)或者部分继承(block copy)该函数,此处我们降维处理,将其直接复制。在这种机制下,拥有好的H函数的鱼群将存活下来,并且繁衍生息,而拥有差的H函数的鱼群就被淘汰了。
此处不会讲解关于机器学习的基本知识,太多而且笔者水平有限,讲解不清楚。
下面的模块讲解LogisticRegression(以下简称LR)的基本理解,如果不感兴趣可以跳过直接看公式。
我们已经知道了H函数,此处称为数学模型。
H(x)=x1∗θ1+x2∗θ2+...+xn∗θn
现在我们有很多的已知的数据:
x1 x2 x3 H(x),或者叫做y 1 2 3 4 2 3 4 5 诸如此类的,此处的数据最好是数值型,如果是标称型(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
然后我们的沿着这个方向一点点的改善,的到训练的迭代公式:
好了,公式就是这个,以上是推导过程:
首先引入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(重点是机器学习在游戏中的应用)的长篇博客,欢迎大家指正交流╰( ̄▽ ̄)╯
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。