問題描述
這是一個圖論問題,其實可以使用窮舉法搜索,但是機器學習裏便於練習就把它變成了強化學習算法。
現在你外出旅行到了一個你不熟悉的城鎮,我們抽象爲城鎮中有六個點,這六個點存在着某種連接關係,無論你在哪個位置,都需要走到第六個點上纔算成功。用鄰接矩陣表示如下。
我們暫且放下數據結構中的深度遍歷搜索圖算法。來看一下隨機算法的速度——當然慢於確定性算法。使用C++作爲編程工具,強化學習算法Sarsa和Q-learning實現如下,實際上我們發現,Q-learning算法與Sarsa算法的運行速度相當,但是Sarsa算法傾向於走的路更長一些。
我們選取的獎賞矩陣如下所示
Sarsa算法
類的結構
/*強化學習算法Sarsa*/
class Sarsa
{
public:
Sarsa() = default;
mat& run(const mat&r,const mat&t, int iter=1000);//輸入獎賞矩陣和轉移矩陣
void main();//測試函數
private:
mat qmat;
};
算法核心
mat & machineLearning::Sarsa::run(const mat & r, const mat & t, int iter)
{
//從0號節點到n_row-1號節點
qmat.randu(r.n_rows, r.n_cols);
const double mu{ 0.7 }, gamma{ 0.4 }, epsilon{0.1};
const int target{ static_cast<int>(r.n_rows - 1) };
int now{}, reward{}, next{}, action{}, nextaction{};
for (int i{ 0 }; i < iter; ++i)
{
//選擇一個初始位置
now = std::rand()%r.n_rows;
if (randu() < epsilon)//貪心選擇
{
vector<int> index{};
for (int j{ 0 }; j < t.n_rows; ++j)
{
if (t(now, j) != 0)
index.push_back(j);
}
action = index[std::rand() % index.size()];
}
else//選取最大值的動作
{
action = (t.row(now) != 0).index_max();
}
while (now != target)
{
reward = r(now, action);
next = action;
if (randu() < epsilon)//貪心選擇
{
vector<int> index{};
for (int j{ 0 }; j < t.n_rows; ++j)
{
if (t(next, j) != 0)
index.push_back(j);
}
nextaction = index[std::rand() % index.size()];
}
else//選取最大值的動作
{
nextaction = (t.row(now) != 0).index_max();
}
qmat(now, action) += mu * (reward + gamma * qmat(next, nextaction) - qmat(now, action));
now = next;
action = nextaction;
}
}
return qmat;
}
測試程序
void machineLearning::Sarsa::main()
{
double inf{ std::numeric_limits<double>::max() };
mat t, r;
t.load("t.dat",arma::arma_ascii);
r.load("r.dat", arma::arma_ascii);
run(r, t,3000).print("result:");
}
Q-learning算法/TD(0)算法
TD(0)算法相對於上述的Sarsa算法相比更爲簡單。在變量上只用記憶now和action,不用記憶next和nextaction變量,因爲q-learning只根據當前情況下的情況做出判斷,從不考慮下一個情況。
mat & machineLearning::Qlearning::run(const mat & r, const mat & t, int iter)
{
//從0號節點到n_row-1號節點
qmat.randu(r.n_rows, r.n_cols);
const double mu{ 0.7 }, gamma{ 0.4 }, epsilon{ 0.1 };
const int target{ static_cast<int>(r.n_rows - 1) };
int now{}, reward{}, action{};
for (int i{ 0 }; i < iter; ++i)
{
//選擇一個初始位置
now = std::rand() % r.n_rows;
while (now != target)
{
if (randu() < epsilon)//貪心選擇
{
vector<int> index{};
for (int j{ 0 }; j < t.n_rows; ++j)
{
if (t(now, j) != 0)
index.push_back(j);
}
action = index[std::rand() % index.size()];
}
else//選取最大值的動作
{
action = (t.row(now) != 0).index_max();
}
reward = r(now, action);
qmat(now, action) += mu * (reward + gamma * qmat.row(action).max() - qmat(now, action));
now = action;
}
}
return qmat;
}
運行結果
結果這麼看,假如你從0號節點出發,那麼你會向值較高的節點前進,因爲獎勵多,所以你選擇1號節點,然後是2號,到達5號節點完成任務。其他節點也是類似。
需要注意的是,不能使用0初始化,否則會出現很多錯誤!!!