Q_Learning算法的一个简单的应用。

来源:互联网 发布:西安淘宝店铺培训班 编辑:程序博客网 时间:2024/05/19 02:43

想必很多人对Q_Learning比较熟悉。但是Q_Learning重在Learning。那么Q_Learning以何种方式去Learning呢?我们可以想象一下一个正在森林中摸索前进的迷途小羔羊。周围能见度很低,只能走一步看一步。它无法预知距离自己太远的山路,但是它记忆力比较好,曾经走过的路,遇到过的环境它都记得。那么这些“经验”却是小羔羊能够不断摸索前进的宝贵“知识”,羔羊遇到了与之前碰到的相似的环境或者一样的环境的时候,它会选择它的记忆中能够使得自己顺利通过的走法,因为之前这样走没有什么问题,那么现在还是像这样走,问题也许也不太大,虽然可能还会有更好的走法(这需要羔羊进一步的学习。)Q_Learning算法与之一样,一个陌生的环境中,我们往往只关心我们所处的当前环境下要采取怎样的行动,这个行动使得我们获益最大而不考虑更远的情况。

下面就一篇博客上提到的英文原文(Step-By-Step Tutorial点击打开链接)中提到的一个具体的场景为例,来举个Q_Learning算法的实际应用的例子。英文原文大家可以在网上直接下载,原文中只给出算法原型,以及部分分析过程,以及最后的收敛状态。并没有给出具体的实现程序。学过算法的人应该知道,能不能根据别人的算法编制出能够解决实际问题的程序,在某种程度上能够体现出子集对该算法的理解程度。所以对于比较熟悉Q_Learning算法的读者来说,这篇博客纯属扯淡,但是对于刚刚入门,想要学习并且真正理解Q_Learning算法的人来说可能还有点用。我使用Q_Learning算法基本上实现了Step-By-Step Tutorial中提出的简单场景中提炼出来的基本问题。java代码以及运行结果如下:

package Q_Learning;
public class Q_Learning {
private int[][] R;//当前状态转移到下一状态下的reward
private int[][] Q;//某个状态下,采取某个动作之后可预见的将来的回报,评估当前状态下动作选择的好坏。
public int[][] getR() {
return R;
}
public void setR(int[][] r) {
R = r;
}
public int[][] getQ() {
return Q;
}
public void setQ(int[][] q) {
Q = q;
}
//获取R值
public int GetCertainR(int Rx,int Ry){
return R[Rx][Ry];
}
//获取Q值
public int GetCerTainQ(int Qx,int Qy){
return Q[Qx][Qy];
}
//更新Q值
public void UpdateQq(int Currentstate,int BestAction,int BestQ){
Q[Currentstate][BestAction]=R[Currentstate][BestAction]+(int)(0.8*BestQ);
}
//寻找最优Q值对应的动作,输入的是当前状态,寻找可以到达的下一个记忆状态的最优值
public boolean ChooseTheBestAction(int Currentstate){
//看当前状态下的未来最大回报对应的动作值
int next_avaliable_state[]=new int[R[0].length];//当前状态的下个状态,以及到达下一个状态的动作。这里有点特殊,需要注意下。
int MaxQ[]=new int[R[0].length];//当前状态的下个状态的的最优Q值
int BestNextState=0,BeatQ=0,ActionSequence=0;
int count=0;
int CRstate=Currentstate;
boolean EndLearning=false;
while(!EndLearning){
count=0;
for(int temp=0;temp<MaxQ.length;temp++){
MaxQ[temp]=-1;
next_avaliable_state[temp]=-1;
}
System.out.print("currentstate"+":");
System.out.println(Currentstate+"\t");
for(int the_next_avaliable_state=0;the_next_avaliable_state<R[0].length;the_next_avaliable_state++){
if(R[CRstate][the_next_avaliable_state]!=-1){//两个状态之间能够进行转移,也就是可选的动作。
//找到记忆中下一个状态的最优Q值及其对应的下标
next_avaliable_state[count]=the_next_avaliable_state;//一个可达的状态
System.out.print("nextstate"+":");
System.out.println(the_next_avaliable_state+"\t");
//该状态下的最大Q值
for(int the_qvalue_index=0;the_qvalue_index<Q[0].length;the_qvalue_index++){
if(MaxQ[count]<Q[the_next_avaliable_state][the_qvalue_index]){
MaxQ[count]=Q[the_next_avaliable_state][the_qvalue_index];
}
}
count++;
}
}
//在所有的最优Q值中选出一个最优的Q值及其对应的动作
BeatQ=MaxQ[0];
for(int getthebestq_a=0;getthebestq_a<MaxQ.length;getthebestq_a++){
if(BeatQ<=MaxQ[getthebestq_a]){
BeatQ=MaxQ[getthebestq_a];
BestNextState=next_avaliable_state[getthebestq_a];
}
//把上面的for去掉,注释的这两行也能够运行。只限于这个程序,其他情况下不一定行。原因:看运行结果。
//BeatQ=BeatQ<=MaxQ[getthebestq_a]?MaxQ[getthebestq_a]:BeatQ;
//BestNextState=BestNextState<=next_avaliable_state[getthebestq_a]?next_avaliable_state[getthebestq_a]:BestNextState;
}
System.out.print("BestNextState"+":");
System.out.println(BestNextState+"\t");
/**
* 如果当前的状态下有很多可以供选择的动作,这里就在这些可以选择的动作里面
* 随机的选择一个动作执行,更新当前状态下可转移到的状态的动作的Q值,这里需要注意一下,如果我们训练的次数足够多,那么
* 总会把当前状态可达状态下的所偶遇Q值全部都更新一下。
* **/
ActionSequence=0;
for(int AccessAction=0;AccessAction<next_avaliable_state.length;AccessAction++){
if(next_avaliable_state[AccessAction]!=-1)
ActionSequence+=1;
}
int AccessActionindex=(int)(Math.random()*ActionSequence);//随机选择一个动作
UpdateQq(CRstate,next_avaliable_state[AccessActionindex],MaxQ[AccessActionindex]);
if(next_avaliable_state[AccessActionindex]!=5){//目标是拿到将来最高的回报,在state=5处,回报值最大,反之最小。
CRstate=BestNextState;//把下一个状态作为当前状态
}else{
EndLearning=true;
}
}
return true;
}
}


package Q_Learning;
public class TrainQ_Learning {
public static void main(String[] args){
Q_Learning ql=new Q_Learning();
//初始化reward
int R[][]=new int[6][6];
//初始化Q
int Q[][]=new int[6][6];
//Q的旧值
int CopyQ[][]=new int[6][6];
int Traintimes=0;
for(int setr_x=0;setr_x<6;setr_x++){
for(int setr_y=0;setr_y<6;setr_y++){
R[setr_x][setr_y]=-1;
Q[setr_x][setr_y]=0;
CopyQ[setr_x][setr_y]=0;
}
}
R[0][4]=0;
R[1][3]=0;
R[1][5]=100;
R[2][3]=0;
R[3][1]=0;
R[3][2]=0;
R[3][4]=0;
R[4][0]=0;
R[4][3]=0;
R[4][5]=100;
R[5][1]=0;
R[5][4]=0;
R[5][5]=100;
ql.setR(R);
ql.setQ(Q);
while(Traintimes<500){
//随机选择一个状态
ql.ChooseTheBestAction((int)(Math.random()*6));
Traintimes++;
}
for(int finalx=0;finalx<6;finalx++){
for(int finaly=0;finaly<6;finaly++){
System.out.print(ql.GetCerTainQ(finalx,finaly)+"\t");
}
System.out.println();
}
}
/**
* R=
*  -1  -1  -1  -1  0  -1 
*  -1  -1  -1   0 -1  100 
*  -1  -1  -1   0 -1  -1 
*  -1   0   0  -1  0  -1 
*   0  -1  -1   0 -1  100
*  -1   0  -1  -1  0  100 
* 初始化时,Q随机初始化,随便给值就可以了,这里全部给初值0

* 初始Q=
*   0  0  0  0  0  0
*   0  0  0  0  0  0
*   0  0  0  0  0  0
*   0  0  0  0  0  0
*   0  0  0  0  0  0
*   0  0  0  0  0  0
* **/
}

运行结果:

00 0 0 396 0
0 0 0316 0 496
0 0 0316 0 0
0 396 2520 396 0
316 0 0316 0 496
0 396 00 396 496