-
在线学习:TD学习是一种在线学习算法,它可以在与环境交互的过程中逐步更新预测。
-
引导探索:TD学习算法使用之前的估计来引导对状态或状态-动作对的价值评估,这有助于在探索过程中减少不确定性。
-
时间差分更新:
- TD更新公式通常写作:V(St)←V(St)+α[Rt+1+γV(St+1)−V(St)]V(St)←V(St)+α[Rt+1+γV(St+1)−V(St)]
- 其中,VV 是状态价值函数,StSt 和 St+1St+1 分别是当前和下一个状态,Rt+1Rt+1 是在 St+1St+1 处获得的即时奖励,αα 是学习率,γγ 是折扣因子。
-
TD(0):TD(0)是TD学习的一个特殊情况,它使用当前状态的估计值作为下一个状态的估计值,更新公式简化为:V(St)←V(St)+α[Rt+1−V(St)]V(St)←V(St)+α[Rt+1−V(St)]
-
SARSA:状态-动作-奖励-状态-动作(State-Action-Reward-State-Action, SARSA)算法是TD学习的一种变体,它用于学习一个策略,同时更新状态-动作对的价值函数 QQ。
-
Q-Learning:Q-Learning是另一种TD学习变体,它使用一个偏置策略来探索所有可能的动作,并更新状态-动作对的价值函数 QQ,目标是学习最优价值函数。
-
收敛性:在某些条件下,TD学习算法能够收敛到最优价值函数或最优策略。
-
应用:TD学习方法广泛应用于各种强化学习问题,包括游戏、机器人导航和控制等领域。
-
实现:TD学习可以很容易地实现,并与神经网络等函数逼近方法结合使用。
#获取一个格子的状态
def get_state(row,col):
if row!=3:
return 'ground'
if row==3 and col==0:
return 'ground'
if row==3 and col==11:
return 'terminal'
return 'trap'
get_state(0,0)
def move(row,col,action):
if get_state(row,col) in['trap','terminal']:
return row,col,0
if action ==0:
row-=1
if action==1:
row+=1
if action==2:
col-=1
if action==3:
col+=1
row = max(0,row)
row = min(3,row)
col = max(0,col)
col = min(11,col)
reward=-1
if get_state(row,col) =='trap':
reward-=100
return row,col,reward
#初始化Q矩阵,用于评估每个动作的价值
import numpy as np
#初始化在每个格子里采取每个动作的分数,初始化都是0,因为没有任何的知识
Q = np.zeros([4,12,4])
Q.shape
(4, 12, 4)
#动作函数
import random
#根据状态选择一个动作
def get_action(row,col):
#有小概率选择随机动作
if random.random()<0.1:
return random.choice(range(4))
#否则选择分数最高的动作
return Q[row,col].argmax()
get_action(0,0)
#算法核心
#更新分数,每次取决于当前的格子,当前的动作,下个格子,下个格子的动作
def get_update(row,col,action,reward,next_row,next_col,next_action):
#计算target
target = 0.9*Q[next_row,next_col,next_action]#用q矩阵来衡量
target+=reward
#计算value
value = Q[row,col,action]
#根据时序差分算法,当前state,action的分数 = 下一个state,action的分数*gamma+reward
#此处是求两者的差,越接近0越好
update = target-value
#这个0.1相当于lr
update*=0.1
#更新当前状态和动作的分数
return update
#在0,0向右走,得到-1,到达0,1,再次执行向右走
get_update(0,0,3,-1,0,1,3)
#训练,sarsa算法是在线学习算法
def train():
for epoch in range(1500):
#初始化当前位置
row = random.choice(range(4))
col = 0
#初始化第一个动作
action = get_action(row,col)
#计算反馈的和,这个数字应该越大越好
reward_sum=0 #衡量这个游戏玩的有多好
#循环直到到达终点或者陷阱
while get_state(row,col)not in ['terminal','trap']:
#执行动作
next_row,next_col,reward = move(row,col,action)
reward_sum+=reward
#求新位置的动作
next_action = get_action(next_row,next_col)
#更新分数
update = get_update(row,col,actoin,reward,next_row,next_col,next_action)
Q[row,col,action]+=update
#更新当前位置
row = next_row
col = next_col
action = next_action
if epoch%150==0:
print(epoch,reward_sum)
def show(row,col,action):
graph=[
'','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','2','2','2','2','2',
'2','2','2','2','2','1'
]
action = {0:'上',1:'下',2:'左',3:'右'}[action]
graph[row*12+col] = action
graph=' '.join(graph)
for i in range(0,4*12,12):
print(graph[i:i+12])
show(1,1,0)
#测试函数
from IPython import display
import time
def test():
#起点
row = random.choice(range(4))
col = 0
#最多玩N步
for _ in range(200):
#获取当前状态,如果状态时终点或者陷阱
if get_state(row,col)in ['trap','terminal']:
break
#选择最优动作
action = Q[row,col].argmax()
#打印这个动作
display.clear_output(wait = True)
time.sleep(0.1)
show(row,col,action)
#执行动作
row,col,reward = move(row,col,action)
test()
for row in range(4):
line=''
for col in range(12):
action = Q[row,col].argmax()
action = {0:'上',1:'下',2:'左',3:'右'}[action]
line+=action
print(line)
Qlearning算法,sarsa算法的改进
#qlearning算法,在sarsa算法的改进
#算法核心
#更新分数,每次取决于当前的格子,当前的动作,下个格子,下个格子的动作
def get_update(row,col,action,reward,next_row,next_col,next_action):
#计算target
target = 0.9*Q[next_row,next_col,next_action].max()#改进#用q矩阵来衡量
target+=reward
#计算value
value = Q[row,col,action]
#根据时序差分算法,当前state,action的分数 = 下一个state,action的分数*gamma+reward
#此处是求两者的差,越接近0越好
update = target-value
#这个0.1相当于lr
update*=0.1
#更新当前状态和动作的分数
return update
#在0,0向右走,得到-1,到达0,1,再次执行向右走
get_update(0,0,3,-1,0,1,3)
N步sarsa算法
玩N步更新第一步参数
#获取一个格子的状态
def get_state(row,col):
if row!=3:
return 'ground'
if row==3 and col==0:
return 'ground'
if row==3 and col==11:
return 'terminal'
return 'trap'
get_state(0,0)
#动作函数
import random
#根据状态选择一个动作
def get_action(row,col):
#有小概率选择随机动作
if random.random()<0.1:
return random.choice(range(4))
#否则选择分数最高的动作
return Q[row,col].argmax()
get_action(0,0)
def move(row,col,action):
if get_state(row,col) in['trap','terminal']:
return row,col,0
if action ==0:
row-=1
if action==1:
row+=1
if action==2:
col-=1
if action==3:
col+=1
row = max(0,row)
row = min(3,row)
col = max(0,col)
col = min(11,col)
reward=-1
if get_state(row,col) =='trap':
reward-=100
return row,col,reward
#初始化q矩阵和3个list
import numpy as np
#初始化在每一个格子里采取每个动作的分数初始化都是0,因为没有任何的知识
Q = np.zeros([4,12,4])
#初始化3个list,用来存储状态动作,反馈的历史数据,因为后面要回溯这些数据
state_list = []
action_list=[]
reward_list=[]
Q.shape
#获取更新量的函数
#target时累加的
#获取5个时间步分别的分数
def get_update_list(next_row,next_col,next_action):
#初始化的target是最后一个state和最后一个action的分数
target = Q[next_row,next_col,next_action]
#计算每一步的target
#每一步的target等于下一步的target*0.9,再加上基本的reward
#时间从后往前回溯,越以前的target会累加的信息越多
#[4,3,2,1,0]
target_list = []
for i in range(5):
row,col = state_list[i]
action = action_list[i]
value_list.append(Q[row,col,action])
#计算每一步的更新量
update_list = []
for i in range(5):
#根据时序差分算法,当前state,action的分数 = 下一个state,action的分数*gamma+reward
#此处是求两者的差,越接近0越好
update = target_list[i]-value_list[i]
#这个0.1指lr学习率
update*=0.1
update_list.append(update)
return update_list
#训练函数
def train():
for epoch in range(1500):
#初始化当前位置
row =random.choice(range(4))
col = 0
#初始化第一个动作
action = get_action(row,col)
#计算反馈的和,这个数字一个越来越小
reward_sum = 0
#初始化3个列表
state_list.clear()
action_list.clear()
reward_list.clear()
#循环直到到达终点或陷阱
while get_state(row,col)not in ['termianl','trap']:
#执行动作
next_row,next_col,reward = move(row,col,action)
reward_sum+=reward
#求新位置的动作
next_action = get_action(next_row,next_col)
#记录历史数据
state_list.append([row,col])
action_list.append([action])
reward_list.append(reward)
#积累到5步以后再开始更新参数
if len(state_list)==5:
#计算分数
update_list = get_update_list(next_row,next_col,next_action)
#只更新第一步的分数
row,col = state_list[0]
action = action_list[0]
update = update_list[0]
Q[row,col,action]+=update
#移除第一步,这样再下一次循环时保持列表时5个元素
state_list.pop(0)
action_list.pop(0)
reward_list.pop(0)
#更新当前位置
row = next_row
col = next_col
action = next_action
#走到终点以后,更新剩下步数的update
for i in range(len(state_list)):
row,col = state_list[i]
action = action_list[i]
update =update_list[i]
Q[row,col,action]+=update
if epoch%100 ==0:
print(epoch,reward_sum)
DynaQ算法
#获取一个格子的状态
def get_state(row,col):
if row!=3:
return 'ground'
if row==3 and col==0:
return 'ground'
if row==3 and col==11:
return 'terminal'
return 'trap'
get_state(0,0)
def move(row,col,action):
if get_state(row,col) in['trap','terminal']:
return row,col,0
if action ==0:
row-=1
if action==1:
row+=1
if action==2:
col-=1
if action==3:
col+=1
row = max(0,row)
row = min(3,row)
col = max(0,col)
col = min(11,col)
reward=-1
if get_state(row,col) =='trap':
reward-=100
return row,col,reward
#动作函数
import random
#根据状态选择一个动作
def get_action(row,col):
#有小概率选择随机动作
if random.random()<0.1:
return random.choice(range(4))
#否则选择分数最高的动作
return Q[row,col].argmax()
get_action(0,0)
#初始化Q矩阵和历史数据字典
import numpy as np
#初始化再每一个格子里采取每个动作的分数,初始化都是0,因为没有任何的知识
Q = np.zeros([4,12,4])
#保存历史数据,键是(row,col,action),值是(next_row,next_col,reward)
history = dict() #字典
Q.shape,history
((4, 12, 4), {})
#计算更新量函数,和qlearning函数一样
#更新分数,每次取决于当前的格子,当前的动作,下个格子,下个格子的动作
def get_update(row,col,action,reward,next_row,next_col,next_action):
#计算target
target = 0.9*Q[next_row,next_col,next_action]#用q矩阵来衡量
target+=reward
#计算value
value = Q[row,col,action]
#根据时序差分算法,当前state,action的分数 = 下一个state,action的分数*gamma+reward
#此处是求两者的差,越接近0越好
update = target-value
#这个0.1相当于lr
update*=0.1
#更新当前状态和动作的分数
return update
#在0,0向右走,得到-1,到达0,1,再次执行向右走
get_update(0,0,3,-1,0,1,3)
#离线学习函数
import random
def q_planning():
#Qplanning循环,相当于是在反刍历史数据,随机取N个历史数据再进行离线学习
for _ in range(20):
#随机选择曾经遇到过的状态动作对
row,col,action = random.choice(list(history.keys()))
#再获取下一个状态和反馈
next_row,next_col,reward = history[(row,col,action)]
#计算分数
update = get_update(row,col,action,reward,next_row,next_col)
#更新分数
Q[row,col,action]+=update
#训练,sarsa算法是在线学习算法
def train():
for epoch in range(1500):
#初始化当前位置
row = random.choice(range(4))
col = 0
#初始化第一个动作
action = get_action(row,col)
#计算反馈的和,这个数字应该越大越好
reward_sum=0 #衡量这个游戏玩的有多好
#循环知道到达终点或者陷阱
while get_state(row,col)not in ['terminal','trap']:
#执行动作
next_row,next_col,reward = move(row,col,action)
reward_sum+=reward
#求新位置的动作
next_action = get_action(next_row,next_col)
#更新分数
update = get_update(row,col,actoin,reward,next_row,next_col,next_action)
#将数据添加到模型中
history [(row,col,action)] = next_row,next_col,reward
#反刍历史数据,进行离线学习
q_planning()
#更新当前位置
row = next_row
col = next_col
action = next_action
if epoch%150==0:
print(epoch,reward_sum)