前言
??強化學習是機器學習領域除有監督學習、無監督學習外的另一個研究分支,它主要利用智能體與環境進行互動,從而學習到能獲得良好結果的策略,與有監督學習不同,強化學習的動作并沒有明確的標注資訊,只有來自環境的反饋的獎勵資訊,它通常具有一定的滯后性,用于反映動作的“好與壞”,一個完整的強化 學習程序是從一開始什么都不懂,通過不斷嘗試,從錯誤或懲罰中學習,最 后找到規律,學會達到目的的方法,
應用領域:
? 游戲理論與多主體互動,
? 機器人,
? 電腦網路,
? 車載導航,
? 工業物流,
1 原理
??在強化學習問題中,具有感知和決策能力的物件叫作智能體(Agent),它可以是一段算 法代碼,也可以是具有機械結構的機器人軟硬體系統,智能體通過與外界的環境進行互動從而完成某個任務,這里的環境(Environment)是指能受到智能體的動作而產生影響,并給出相應反饋的外界環境的總和,對于智能體來說,它通過感知環境的狀態(State)而產生決策動作(Action);對于環境來說,它從某個初始初始狀態𝑠1開始,通過接受智能體的動作來動態地改變自身狀態,并給出相應的獎勵(Reward)信號,
??從概率角度描述強化學習程序,它包含了如下 5 個基本物件:
? 狀態𝑠 :反映了環境的狀態特征,在時間戳𝑡上的狀態記為
𝑠
𝑡
𝑠_𝑡
st?,它可以是原始的視覺圖 像、語音波形等信號,也可以是高層抽象過后的特征,如小車的速度、位置等資料,所有的(有限)狀態構成了狀態空間
S
S
S
? 動作𝑎 :是智能體采取的行為,在時間戳𝑡上的狀態記為
𝑎
𝑡
𝑎_𝑡
at?,可以是向左、向右等離散動 作,也可以是力度、位置等連續動作,所有的(有限)動作構成了動作空間
A
A
A
? 策略𝜋(𝑎|𝑠) :代表了智能體的決策模型,接受輸入為狀態𝑠,并給出決策后執行動作的概率分布𝑝(𝑎|𝑠),滿足 ∑𝜋(𝑎|𝑠) = 1, 𝑎∈𝐴,這種具有一定隨機性的動作概率輸出稱為隨機性策略(Stochastic Policy),特別地,當策略模型總是輸出某個動作的概率為 1,其它為 0 時,這種策略模型稱為確定性策略(Deterministic Policy),即 𝑎 = 𝜋(𝑠)
? 獎勵𝑟(𝑠, 𝑎) :表達環境在狀態𝑠時接受動作𝑎后給出的反饋信號,一般是一個標量值,它 在一定程度上反映了動作的好與壞,在時間戳𝑡上的獲得的激勵記為𝑟𝑡(部分資料上記為𝑟𝑡+1,這是因為激勵往往具有一定滯后性)
? 狀態轉移概率𝑝(𝑠′|𝑠, 𝑎) :表達了環境模型狀態的變化規律,即當前狀態𝑠的環境在接受動作𝑎后,狀態改變為𝑠′的概率分布,滿足 ∑ 𝑝(𝑠′|𝑠, 𝑎) = 1,𝑠′∈𝑆
互動程序可由下圖表示:

由互動程序我們得到整個強化學習系統的輸入是:
? State 為Observation,
? Actions 在每個狀態下,有什么行動,
? Reward 進入每個狀態時,能帶來正面或負面的回報,
輸出是:
? Policy 在每個狀態下,會選擇哪個行動,
??增強學習的任務就是找到一個最優的策略Policy,從而使Reward最多,智能體從環境的初始狀態
𝑠
1
𝑠_1
s1?開始,通過策略模型𝜋(𝑎|𝑠)采樣某個具體的動作
𝑎
1
𝑎_1
a1?執行,環境受到動作
𝑎
1
𝑎_1
a1?的影響,狀態根據內部狀態轉移模型𝑝(𝑠′|𝑠, 𝑎)發生改變,變為新的狀態
s
2
s_2
s2?,同時給出智能體的反饋信號:獎勵
𝑟
1
𝑟_1
r1?,由獎勵函式𝑟(
𝑠
1
𝑠_1
s1?,
𝑎
1
𝑎_1
a1?)產生,如此回圈互動,直至達到游戲終止狀態
𝑎
T
𝑎_T
aT?,這個程序產生了一系列的有序資料:𝜏 =
𝑠
1
,
𝑎
1
,
𝑟
1
,
𝑠
2
,
𝑎
2
,
𝑟
2
,
?
,
𝑠
𝑇
𝑠_1, 𝑎_1, 𝑟_1, 𝑠_2, 𝑎_2, 𝑟_2, ? , 𝑠_𝑇
s1?,a1?,r1?,s2?,a2?,r2?,?,sT?
??這個序列代表了智能體與環境的一次交換程序,叫做軌跡(Trajectory),記為𝜏,一次互動程序叫作一個回合(Episode),𝑇代表了回合的時間戳數(或步數),有些環境有明確的終止狀態(Terminal State),比如太空侵略者中的小飛機被擊中后則游戲結束;而部分環境沒有明確的終止標志,如部分游戲只要保持健康狀態,則可以無限玩下去,此時𝑇代表∞,增強學習的演算法就是需要根據這些樣本來 改進策略,從而使得到的樣本中的獎勵更好,
??強化學習有多種演算法,目前比較常用的演算法是,通過行為的價值來選取 特定行為的方法,如Q-learning、SARSA,使用神經網路學習的DQN(Deep Q Network),以及DQN的后續演算法,還有直接輸出行為的Policy Gradients 等,
2 Q-Learning
2.1 原理
??Q-Learning演算法是強化學習中重要且最基礎的演算法,大多數現代的強化 學習演算法,大都是Q-Learning的一些改進,Q-Learning的核心是Q-Table,Q- Table的行和列分別表示State和Action的值,Q-Table的值Q(s,a)衡量當前 States采取行動a的主要依據,
2.2 主要流程
? 初始化Q表(初始化為0或隨機初始化)
Repeat:
? 生成一個在0與1之間的亂數,如果該數大于預先給定的一 個閾值ε,則選擇隨機動作;否則選擇動點依據最高可能性的獎勵基于當前狀態s和Q表,
? 依據上步執行動作,
? 采取行動后觀察獎勵值r和新狀態
s
t
+
1
s_{t+1}
st+1?,
? 基于獎勵值r,利用式下式更新Q表,
其中α為學習率,γ為折扣率,
? 把
s
t
+
1
s_{t+1}
st+1?賦給
s
t
s_{t}
st?
流程圖:

2.3 Q函式
?&emps;Q-Learning演算法的核心是Q(s,a)函式,其中s表示狀態,a表示行動, Q(s,a)的值為在狀態s執行a行為后的最大期望獎勵值,Q(s,a)函式可以看作一 個表格,每一行表示一個狀態,每一列代表一個行動,
??得到Q函式后,就可以在每個狀態做出合適的決策了,如當處于
s
1
s_1
s1?時, 只需考慮Q(
s
1
s_1
s1?, :)這些值,并挑選其中最大的Q函式值,并執行相應的動作,
2.4 貪婪策略
??在狀態s1時,我們一般是執行根據max(Q( s 1 s_1 s1?, : ))中對應的動作a,如果每次都按照這種策略選擇行動就有可能局限于現有經驗中,不 利于發現更有價值或更新的情況,所以,除根據經驗選擇行動外,一般還會給主體(Agent)一定的機會或概率,以探索的方式選擇行動, 這種平衡“經驗”和“探索”的方法又稱為ε貪婪(ε-greedy)策略,根據預 先設定好的ε值(該值一般較小,如取0.1),主體有ε的概率隨機行動,有1- ε的概率根據經驗選擇行動,
2.5 PyTorch實作
??本次用于訓練的小游戲是機器人尋找目標星星,如果小機器人接觸到五角、星,它就能贏得100分的獎勵,如果它接觸到小樹將得到-100的懲罰,根據獎勵進行不斷優化最佳路徑,

首先要創建游戲并進行互動主要包含了 5 個步驟:
? 創建游戲,并回傳游戲物件env,
? 復位游戲狀態,一般游戲環境都具有初始狀態,通過呼叫 env.reset()即可復位游戲狀 態,同時回傳游戲的初始狀態 observation,
? 顯示游戲畫面,通過呼叫 env.render()即可顯示每個時間戳的游戲畫面,一般用做測驗,在訓練時渲染畫面會引入一定的計算代價,因此訓練時可不顯示畫面,
? 與游戲環境互動,通過 env.step(action)即可執行 action 動作,并回傳新的狀態observation、當前獎勵 reward、游戲是否結束標志 done,通過回圈此步驟即可持續與環境互動,直至游戲回合結束,
? 銷毀游戲,呼叫 env.close()即可,
class Env(tk.Tk):
def __init__(self):
super(Env, self).__init__()
self.action_space = ['u', 'd', 'l', 'r']
self.n_actions = len(self.action_space)
self.title('Q Learning')
self.geometry('{0}x{1}'.format(HEIGHT * UNIT, HEIGHT * UNIT))
self.shapes = self.load_images()
self.canvas = self._build_canvas()
self.texts = []
def _build_canvas(self):
canvas = tk.Canvas(self, bg='white',
height=HEIGHT * UNIT,
width=WIDTH * UNIT)
# create grids
for c in range(0, WIDTH * UNIT, UNIT): # 0~400 by 100
x0, y0, x1, y1 = c, 0, c, HEIGHT * UNIT
canvas.create_line(x0, y0, x1, y1)
for r in range(0, HEIGHT * UNIT, UNIT): # 0~400 by 100
x0, y0, x1, y1 = 0, r, HEIGHT * UNIT, r
canvas.create_line(x0, y0, x1, y1)
# 把圖示加載到環境中
self.rectangle = canvas.create_image(50, 50, image=self.shapes[0])
self.tree1 = canvas.create_image(250, 150, image=self.shapes[1])
self.tree2 = canvas.create_image(150, 250, image=self.shapes[1])
self.star = canvas.create_image(250, 250, image=self.shapes[2])
# 對環境進行包裝
canvas.pack()
return canvas
def load_images(self):
rectangle = PhotoImage(
Image.open("img/bob.png").resize((65, 65)))
tree = PhotoImage(
Image.open("img/tree.png").resize((65, 65)))
star = PhotoImage(
Image.open("img/star.jpg").resize((65, 65)))
return rectangle, tree, star
def text_value(self, row, col, contents, action, font='Helvetica', size=10,
style='normal', anchor="nw"):
if action == 0:
origin_x, origin_y = 7, 42
elif action == 1:
origin_x, origin_y = 85, 42
elif action == 2:
origin_x, origin_y = 42, 5
else:
origin_x, origin_y = 42, 77
x, y = origin_y + (UNIT * col), origin_x + (UNIT * row)
font = (font, str(size), style)
text = self.canvas.create_text(x, y, fill="black", text=contents,
font=font, anchor=anchor)
return self.texts.append(text)
def print_value_all(self, q_table):
for i in self.texts:
self.canvas.delete(i)
self.texts.clear()
for i in range(HEIGHT):
for j in range(WIDTH):
for action in range(0, 4):
state = [i, j]
if str(state) in q_table.keys():
temp = q_table[str(state)][action]
self.text_value(j, i, round(temp, 2), action)
def coords_to_state(self, coords):
x = int((coords[0] - 50) / 100)
y = int((coords[1] - 50) / 100)
return [x, y]
def state_to_coords(self, state):
x = int(state[0] * 100 + 50)
y = int(state[1] * 100 + 50)
return [x, y]
def reset(self):
self.update()
time.sleep(0.5)
x, y = self.canvas.coords(self.rectangle)
self.canvas.move(self.rectangle, UNIT / 2 - x, UNIT / 2 - y)
self.render()
# return observation
return self.coords_to_state(self.canvas.coords(self.rectangle))
def step(self, action):
state = self.canvas.coords(self.rectangle)
base_action = np.array([0, 0])
self.render()
if action == 0: # up
if state[1] > UNIT:
base_action[1] -= UNIT
elif action == 1: # down
if state[1] < (HEIGHT - 1) * UNIT:
base_action[1] += UNIT
elif action == 2: # left
if state[0] > UNIT:
base_action[0] -= UNIT
elif action == 3: # right
if state[0] < (WIDTH - 1) * UNIT:
base_action[0] += UNIT
# 移動
self.canvas.move(self.rectangle, base_action[0], base_action[1])
self.canvas.tag_raise(self.rectangle)
next_state = self.canvas.coords(self.rectangle)
# 判斷得分條件
if next_state == self.canvas.coords(self.star):
reward = 100
done = True
elif next_state in [self.canvas.coords(self.tree1),
self.canvas.coords(self.tree2)]:
reward = -100
done = True
else:
reward = 0
done = False
next_state = self.coords_to_state(next_state)
return next_state, reward, done
# 渲染環境
def render(self):
time.sleep(0.03)
self.update()
Q函式:
class QLearningAgent:
def __init__(self, actions):
# 四種動作分別用序串列示:[0, 1, 2, 3]
self.actions = actions
self.learning_rate = 0.01
self.discount_factor = 0.9
#epsilon貪婪策略取值
self.epsilon = 0.1
self.q_table = defaultdict(lambda: [0.0, 0.0, 0.0, 0.0])
# 采樣 <s, a, r, s'>
def learn(self, state, action, reward, next_state):
current_q = self.q_table[state][action]
# 更新Q表
new_q = reward + self.discount_factor * max(self.q_table[next_state])
self.q_table[state][action] += self.learning_rate * (new_q - current_q)
# 從Q-table中選取動作
def get_action(self, state):
if np.random.rand() < self.epsilon:
# 貪婪策略隨機探索動作
action = np.random.choice(self.actions)
else:
# 從q表中選擇
state_action = self.q_table[state]
action = self.arg_max(state_action)
return action
@staticmethod
def arg_max(state_action):
max_index_list = []
max_value = state_action[0]
for index, value in enumerate(state_action):
if value > max_value:
max_index_list.clear()
max_value = value
max_index_list.append(index)
elif value == max_value:
max_index_list.append(index)
return random.choice(max_index_list)
訓練:
env = Env()
agent = QLearningAgent(actions=list(range(env.n_actions)))
#共進行200次游戲
for episode in range(200):
state = env.reset()
while True:
env.render()
# agent產生動作
action = agent.get_action(str(state))
next_state, reward, done = env.step(action)
# 更新Q表
agent.learn(str(state), action, reward, str(next_state))
state = next_state
env.print_value_all(agent.q_table)
# 當到達終點就終止游戲開始新一輪訓練
if done:
break
3 SARSA 演算法
??SARSA 演算法通過: 𝑄 π ( 𝑠 𝑡 , 𝑎 𝑡 ) 𝑄^π(𝑠_𝑡, 𝑎_𝑡) Qπ(st?,at?) ← 𝑄 π ( 𝑠 𝑡 , 𝑎 𝑡 ) 𝑄^π(𝑠_𝑡, 𝑎_𝑡) Qπ(st?,at?) + 𝛼(𝑟 ( 𝑠 𝑡 , 𝑎 𝑡 ) (𝑠_𝑡, 𝑎_𝑡) (st?,at?) + 𝛾 𝑄 π ( 𝑠 𝑡 + 1 , 𝑎 𝑡 + 1 ) 𝑄^π(𝑠_{𝑡+1}, 𝑎_{𝑡+1}) Qπ(st+1?,at+1?) ? 𝑄 π ( 𝑠 𝑡 , 𝑎 𝑡 ) 𝑄^π(𝑠_𝑡, 𝑎_𝑡) Qπ(st?,at?)方式估計 Q 函式,在軌跡的每一步,只需要 𝑠 𝑡 、 𝑎 𝑡 、 𝑟 𝑡 、 𝑠 𝑡 + 1 、 𝑎 𝑡 + 1 𝑠_𝑡、𝑎_𝑡、𝑟_𝑡、𝑠_{𝑡+1}、𝑎_{𝑡+1} st?、at?、rt?、st+1?、at+1?資料即可更新一次 Q 網路,所以叫做 SARSA 演算法(State Action Reward State Action),SARSA 演算法與Q-Leanring演算法的不同之處就是在于 Q 表的更新方式的不同,
3.1 主要流程
? 獲取初始狀態s,
? 執行上一步選擇的行動a,獲得獎勵r和新狀態next_s,
? 在新狀態next_s,根據當前的Q表,選定要執行的下一行動next_a,
? 用r、next_a、next_s,根據SARSA邏輯更新Q表,
? 把next_s賦給s,把next_a賦給a,
3.2 PyTorch實作
??SARSA演算法與Q-Learning演算法的差異是在Q表的更新方式,所以主要修改學習函式,
? 修改學習函式:
# 采樣 <s, a, r,a',s'>
def learn(self, state, action, reward,next_action,next_state):
current_q = self.q_table[state][action]
# 更新Q表
new_q = reward + self.discount_factor * (self.q_table[next_state][next_action])
self.q_table[state][action] += self.learning_rate * (new_q - current_q)
? 修改訓練代碼:
env = Env()
agent = QLearningAgent(actions=list(range(env.n_actions)))
#共進行200次游戲
for episode in range(200):
state = env.reset()
action = agent.get_action(str(state))
while True:
env.render()
#獲取新的狀態、獎勵分數
next_state, reward, done = env.step(action)
#產生新的動作
next_action = agent.get_action(str(state))
# 更新Q表,sarsa根據新的狀態及動作獲取Q表的值
#而不是基于新狀態對所有動作的最大值
agent.learn(str(state), action, reward, next_action,str(next_state))
state = next_state
action=next_action
env.print_value_all(agent.q_table)
# 當到達終點就終止游戲開始新一輪訓練
if done:
break
github地址: https://github.com/aishangcengloua/MLData/tree/master/PyTorch/ReinforcementLearning
參考文獻:
? :Python深度學習基于PyTorch
? :TensorFlow深度學習
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/423856.html
標籤:AI
上一篇:NiN網路詳解
