機器學習的引入
機器學習最近由於AlphaGo和數據挖掘的大熱而變得流行起來,然而我們可以讓它變得更加cool,在接下來的模塊中,我將講述我對遊戲中應用機器學習的理解以及我的現有成果。
應用機器學習的意義
遊戲進行中,尤其是網絡遊戲,會積累大量的數據,數據賦予了計算機行爲意識,而我們如果能“告訴”計算機數據歸納總結的方法,那計算機可能會產生驚人的思維能力,而機器學習就是這樣的一種神奇的工具。
前景
目前的遊戲由於同質化嚴重,所以一個高度智能的遊戲體驗會帶給玩家難以想象的快感,而且筆者以爲,很有可能在某一天,我們能在遊戲中建立真正的人工智能,因爲遊戲世界高度抽象化,數據的提取難度大大降低,當然,這有點跑題了。
應用機器學習的決策邏輯
遊戲的進行,決策和事件無疑是最重要的兩個條件,傳統的遊戲中,我們使用大量的條件判斷來進行決策,不僅在邏輯上很繁瑣,而且有一個重大的問題,它是“死的”,比如玩家經過一個怪物,怪物必定會攻擊玩家,如果要改善這一決策,那需要添加許多條件,比如針對怪物的“攻擊性”,重寫判斷邏輯。
機器學習則不然,我們可以根據以前的記錄進行訓練,每個怪物都是獨特的,即使他們隸屬於同一個類,“個性”將使AI十分豐富。
目標–魚類的遺傳
由於機器學習的數學模型非常複雜(對筆者來說是這樣的,花了很多功夫纔看懂),我們先講一個應用,然後我們根據其需求來應用機器學習。
在之前的遊戲底層邏輯中,筆者曾說過想要實現一款海洋模擬的遊戲,之前的隻言片語可能已經能描述大概的框架了,不知道也沒關係,此處我們有一個問題:捕食行爲
捕食行爲基本上是動物最重要的行爲了,此處有三個需要決策的問題,在進入捕食狀態前的判斷中,我們需要知道:
1、是否需要捕食
2、捕食什麼魚類(前提是在食物鏈上的)
3、哪個地方適合捕食
是否捕食
在傳統遊戲中,設定一個飢餓值,這個值隨着時間遞減,減少到一定程度就去捕食。
而這種狀態是不合理的:
- 首先,我們喫東西並不一定是飢餓,其他的因素都有可能使其變化。
- 其次,這種if(1)else(0)的變化在空間上表現出來的是跳躍性的曲線,不符合我們越飢餓越想喫東西這樣的特性。
- 然後,我們的決策沒有隨機性,很容易預測,很“無聊”。
解決方案
我們設定一個合理的初始函數:
矩陣(向量)表達:
其實就是多元一次方程,H函數得到一個值,然後我們根據值進行決策。
此處提及幾個概念:
θ 我們稱其爲權值,意思是該變量對決策產生影響的重要程度。
x 此處稱爲特性。
根據此函數,我們輸入一系列x,就能得出決策結果。然後你們可能發現了一個問題,
遺傳
所謂遺傳是怎麼一回事呢,此處我們每個魚類種羣共享一個H函數,魚在生產下一代的時候,將完全繼承(clone)或者部分繼承(block copy)該函數,此處我們降維處理,將其直接複製。在這種機制下,擁有好的H函數的魚羣將存活下來,並且繁衍生息,而擁有差的H函數的魚羣就被淘汰了。
針對H函數進行訓練,LogisticRegression
此處不會講解關於機器學習的基本知識,太多而且筆者水平有限,講解不清楚。
下面的模塊講解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
然後我們的沿着這個方向一點點的改善,的到訓練的迭代公式:
好了,公式就是這個,以上是推導過程:
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(重點是機器學習在遊戲中的應用)的長篇博客,歡迎大家指正交流╰( ̄▽ ̄)╯