强化学习及Q-Learning算法的简单理解

首先明确学习的概念,学习是系统为了适应环境而做出的长久的变化,以便在未来更有效率的处理类似的问题

强化学习就是通过算法和训练,让程序的产生相应的变化,未来更好地处理类似问题。

强化学习主要分为两部分:

  1. 学习系统本身(即学习程序)
  2. 环境(即学习程序要解决的问题所处的环境)

强化学习的流程:

  1. 系统判定当前状态,根据策略采取行动
  2. 系统获得环境的反馈
  3. 系统根据反馈,更新策略
  4. 重复上述步骤,直到问题解决

强化学习的特点:

  1. 强化学习是一种无监督学习
  2. 在状态S下,不同的行动A会有不同的反馈R
  3. 反馈R的获得,通常是有延时的
  4. 当前行动A,会对系统后续的行为产生影响

Q-Learning就是其中一种简单的系统实现,通过Q-Table的不断更新,来产生长久的变化。

在强化学习中,通常用Q(S, A)来表示状态S下,采取动作A所带来的价值,所以这个算法就用Q-Learning作了它的名字。

Q-Learning核心思想:

关键点:状态、策略、行动、反馈

  • 通过Q-Table来记录每个状态S可以采用的行动A,Q-Table是一个二维矩阵,每一行代表一个状态,每一列代表一个行动,Q(S,A)的值表示在状态S下,采取行动A的权重(或者说成是能取得的收益)。
  • 特定的策略,在状态S下,通过Q-Table和策略,决定行动A;通常使用的策略是,在状态S下,找到Q(S, A)值最大的那一个A作为当前行动,为避免初始状态下的局部最优情况,通常还会引入随机性。
  • 反馈的获得,可以通过「反馈表」或者是「反馈算法」来获得环境对行动A的反馈R(S, A)。R(S, A)表示在状态S下,采取行动A,获得的反馈。
  • 策略更新,通过贝尔曼方程进行计算,更新Q-Table

Q-Table更新公式:

Q(s,a)Q(s,a)+α[r+γmaxaQ(s,a)Q(s,a)]

参数说明:

α是学习速率,在[0,1]区间取值,表示反馈对当前策略的影响程度。0表示完全不考虑反馈,1表示只看反馈,不看历史经验。

r表示R(S, A),通过「反馈表」或者「反馈算法」获得的反馈值

maxaQ(s,a) 解释:s是状态S时采取行动A之后到达的状态;maxaQ(s,a) 表示在s′状态时,能获得的最大Q值,即历史经验,或者说是记忆中的利益

γ表示折扣因子,代表历史经验中未来利益的重要性,如果为0,表示只看眼前利益r

 

实现步骤:

  1. 初始化Q-Table
  2. 定义Reward获取方法
  3. 制定策略,即在状态S下,如何通过Q-Table选取行动A
  4. 初始化状态S,可以固定,也可以随机
  5. 根据当前状态S,采取行动A,并获得反馈R
  6. 使用更新公式,对Q-Table进行更新
  7. 重复步骤5,6,直到命中退出条件(比如成功、失败、走出了迷宫、到达了目的地等等)
  8. 重复4-7流程N次,N代表训练的回合数

 

Demo:

"-----T"

比如上面的一个字符串,机器人从任意一点进入字符串,可以向左或者向右走一步,最终的目标是走到字母T的位置。

我们的code进行20次训练,得到更新后Q-Table,这时机器人再进入字符串,就能以最效率的方式,走到字母T

 

import numpy as np
#by Jarek.Tan ALPHA
= 0.1 GAMMA = 0.9 EPSILON = 0.9 EPOCH = 20 N_STATE = 6 ACTIONS = ["left", "right"] np.random.seed(2) Q_Table = np.zeros((N_STATE, len(ACTIONS))) def get_reward(S, A): if S == 5: R = 1 elif S == 4 and A == 'right': # S!=5 R = 1 else: R = 0 return R def take_action(S): if S == 0: return 1, 'right', np.max(Q_Table[1, :]) maxQ = np.max(Q_Table[S, :]) if maxQ == 0 or np.random.uniform() > EPSILON: if maxQ == 0: print("maxQ is 0") else: print("Random 0.1 hit") action = np.random.choice(ACTIONS) else: idx = np.argmax(Q_Table[S, :]) print("IDX:", idx) action = ACTIONS[idx] if action == 'left': SN = S - 1 else: SN = S + 1 maxQ = np.max(Q_Table[SN, :]) return SN, action, maxQ def get_value(S, A): if A == 'left': return Q_Table[S, 0] else: return Q_Table[S, 1] def set_value(S, A, V): if A == 'left': Q_Table[S, 0] = V else: Q_Table[S, 1] = V def update_q(S, A, MQ): value = (1 - ALPHA) * get_value(S, A) + ALPHA * (get_reward(S, A) + GAMMA * MQ) set_value(S, A, value) for loop in range(EPOCH): print("Loop:", loop) S = 0 step = 0 steps = [] while S != 5: SN, action, maxQ = take_action(S) update_q(S, action, maxQ) S = SN step += 1 steps.append(action) print("State:", S, "TotalSteps:", step, "\nDetail:", steps) print("Q_Table:", Q_Table)

 

posted @ 2020-04-17 01:20 JarekTan 阅读(570) 评论(0) 推荐(0) 编辑
摘要: 今天主要学习ffmpeg decoding部分的函数定义,这里涉及到AVCodecFilter,AVCodecFilterContext,色度图(chroma)等概念。在decoding部分,会有色度坐标和色度枚举值之间的转换,这里就涉及到色度坐标的概念。简单来说,颜色主要分为亮度和色度两部分;颜色... 阅读全文
posted @ 2014-09-26 19:02 JarekTan 阅读(192) 评论(0) 推荐(0) 编辑
摘要: 图形理解: ----Shape--- / \ Rectangle Circle / \ / \BlueR... 阅读全文
posted @ 2014-03-19 19:41 JarekTan 阅读(194) 评论(0) 推荐(0) 编辑
摘要: 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型对象创建新的对象。原型模式的结构比较简单,在使用C++实现该模式时重点要注意deep copy和shallow copy的问题。prototype模式在实际使用的过程中,可以通过增加一个原型管理器来实现动态添加原型的机制。prototype和AbstractFactory的区别主要在于AbstractFactory模式更侧重于一系列或者一些相关对象的创建,而prototype模式则侧重于通过拷贝的方式创建多个相似的对象。prototype模式可以避免AbstractFactory模式那种工厂和产品平行的结构。Builder模式则是更侧重于 阅读全文
posted @ 2014-03-13 22:09 JarekTan 阅读(153) 评论(0) 推荐(0) 编辑
摘要: 意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。引用设计模式精解中的一段话:生活中有着很多的Builder的例子,个人觉得大学生活就是一个Builder模式的最好体验:要完成大学教育,一般将大学教育过程分成4个学期进行,因此没有学习可以看作是构建完整大学教育的一个部分构建过程,每个人经过这4年的(4个阶段)构建过程得到的最后的结果不一样,因为可能在四个阶段的构建中引入了很多的参数(每个人的机会和际遇不完全相同)。Builder模式要解决的也正是这样的问题:当我们要创建的对象很复杂的时候(通常是由很多其他的对象组合而成),我们要要复杂对象的创建过程和这个对象的表示 阅读全文
posted @ 2014-03-12 19:50 JarekTan 阅读(260) 评论(0) 推荐(0) 编辑
摘要: 从StackoverFlow上面找到的一个比较满意的答案:I will type an example :class A{public:virtual ~A(){}};class B: public A{public:~B(){}};int main(void){A * a = new B;delete a;return 0;}Now in Above Example , destructors will be called recursively bottom to up . My Question is how Compiler do this MAGIC .Good Answer:Th. 阅读全文
posted @ 2014-03-12 14:53 JarekTan 阅读(168) 评论(0) 推荐(0) 编辑
摘要: 一. 虚析构函数我们知道,为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候,必须要通过虚函数找到真正的析构函数。如:classBase{public:Base(){}virtual~Base(){}};classDerived:publicBase{public:Derived(){};~Derived(){};}voidfoo(){Base*pb;pb=newDerived;deletepb;}这是正确的用法,会发生动态绑定,它会先调用Derived的析构函数,然后是Base的析构函数如果析构函数不加virtual,d 阅读全文
posted @ 2014-03-12 09:09 JarekTan 阅读(374) 评论(0) 推荐(0) 编辑
摘要: 构造函数能不能是虚函数:答案:不能解释一下:一、1. 从存储空间角度 虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。2. 从使用角度 虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。二、虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创... 阅读全文
posted @ 2014-03-11 20:52 JarekTan 阅读(448) 评论(0) 推荐(0) 编辑
摘要: 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。抽象工厂模式重点在于“一系列”,即多个对象之间存在某种联系或者是组成一个整体的各个部分。通常在抽象工厂的实现过程中,采用工厂方法模式对每个单独的对象进行抽象。抽象工厂模式将一系列对象的构造行为抽象出来,这样可以很容易的在多个系列之间进行切换。例如一个游戏有三个Boss,每个boss都有特定的攻击方式和防守动作。这个游戏分为简单,普通,困难模式,那么就可以使用抽象工厂模式对这个系统建模,只要用户选择了特定的难度之后,就采用选定的难度的工厂去创建一系列的Boss出来。而单个Boss的攻击和防守行为则可以工厂方法模式来建模,实 阅读全文
posted @ 2014-03-11 20:46 JarekTan 阅读(121) 评论(0) 推荐(0) 编辑
摘要: 从名字上就能看出这个模式的意图,主要是对方法进行抽象。比如游戏中击杀对手的方式很多,可以通过魔法攻击,可以使用武器等等。有一个英雄具有很多的攻击手段,或者不同的英雄具有不同的攻击手段,在发出攻击之前,我们并不知道具体要使用的攻击手段是什么,所以就有了工厂方法模式,将行为的绑定进行延后。PS:工厂方法模式和抽象工厂模式,就像生产键盘的不同生产线和不同的电脑整机生产工厂的区别。一个是对生产键盘这个动作进行抽象;另一个是对生产键盘、鼠标、显示器等多个动作进行一系列的抽象,形成一个完整的整体。http://blog.csdn.net/yzxz/article/details/4601152 这篇文章讲 阅读全文
posted @ 2014-03-10 20:39 JarekTan 阅读(125) 评论(0) 推荐(0) 编辑
点击右上角即可分享
微信分享提示