項目描述:
在該項目中,你將使用強化學習算法,實現(xiàn)一個自動走迷宮機器人。
如上圖所示,智能機器人顯示在右上角。在我們的迷宮中,有陷阱(紅色×××)及終點(藍色的目標點)兩種情景。機器人要盡量避開陷阱、盡快到達目的地。
小車可執(zhí)行的動作包括:向上走 u
、向右走 r
、向下走 d
、向左走l
。
執(zhí)行不同的動作后,根據(jù)不同的情況會獲得不同的獎勵,具體而言,有以下幾種情況。
- 撞到墻壁:-10
- 走到終點:50
- 走到陷阱:-30
- 其余情況:-0.1
我們需要通過修改 robot.py
中的代碼,來實現(xiàn)一個 q learning 機器人,實現(xiàn)上述的目標。
section 1 算法理解
1.1 強化學習總覽
強化學習作為機器學習算法的一種,其模式也是讓智能體在“訓練”中學到“經驗”,以實現(xiàn)給定的任務。但不同于監(jiān)督學習與非監(jiān)督學習,在強化學習的框架中,我們更側重通過智能體與環(huán)境的交互來學習。通常在監(jiān)督學習和非監(jiān)督學習任務中,智能體往往需要通過給定的訓練集,輔之以既定的訓練目標(如最小化損失函數(shù)),通過給定的學習算法來實現(xiàn)這一目標。然而在強化學習中,智能體則是通過其與環(huán)境交互得到的獎勵進行學習。這個環(huán)境可以是虛擬的(如虛擬的迷宮),也可以是真實的(自動駕駛汽車在真實道路上收集數(shù)據(jù))。
在強化學習中有五個核心組成部分,它們分別是:環(huán)境(environment)、智能體(agent)、狀態(tài)(state)、動作(action)和獎勵(reward)。在某一時間節(jié)點t:
智能體在從環(huán)境中感知其所處的狀態(tài)
智能體根據(jù)某些準則選擇動作
環(huán)境根據(jù)智能體選擇的動作,向智能體反饋獎勵
通過合理的學習算法,智能體將在這樣的問題設置下,成功學到一個在狀態(tài) 選擇動作
的策略
。
1.2 計算q值
在我們的項目中,我們要實現(xiàn)基于 q-learning 的強化學習算法。q-learning 是一個值迭代(value iteration)算法。與策略迭代(policy iteration)算法不同,值迭代算法會計算每個”狀態(tài)“或是”狀態(tài)-動作“的值(value)或是效用(utility),然后在執(zhí)行動作的時候,會設法最大化這個值。因此,對每個狀態(tài)值的準確估計,是我們值迭代算法的核心。通常我們會考慮最大化動作的長期獎勵,即不僅考慮當前動作帶來的獎勵,還會考慮動作長遠的獎勵。
在 q-learning 算法中,我們把這個長期獎勵記為 q 值,我們會考慮每個 ”狀態(tài)-動作“ 的 q 值,具體而言,它的計算公式為:
也就是對于當前的“狀態(tài)-動作” ,我們考慮執(zhí)行動作
后環(huán)境給我們的獎勵
,以及執(zhí)行動作
到達
后,執(zhí)行任意動作能夠獲得的最大的q值
,
為折扣因子。
不過一般地,我們使用更為保守地更新 q 表的方法,即引入松弛變量 alpha,按如下的公式進行更新,使得 q 表的迭代變化更為平緩。
根據(jù)已知條件求。
已知:如上圖,機器人位于 s1,行動為 u
,行動獲得的獎勵與題目的默認設置相同。在 s2 中執(zhí)行各動作的 q 值為:u
: -24,r
: -13,d
: -0.29、l
: +40,γ取0.9。
1.3 如何選擇動作
在強化學習中,「探索-利用」問題是非常重要的問題。具體來說,根據(jù)上面的定義,我們會盡可能地讓機器人在每次選擇最優(yōu)的決策,來最大化長期獎勵。但是這樣做有如下的弊端:
- 在初步的學習中,我們的 q 值會不準確,如果在這個時候都按照 q 值來選擇,那么會造成錯誤。
- 學習一段時間后,機器人的路線會相對固定,則機器人無法對環(huán)境進行有效的探索。
因此我們需要一種辦法,來解決如上的問題,增加機器人的探索。由此我們考慮使用 epsilon-greedy 算法,即在小車選擇動作的時候,以一部分的概率隨機選擇動作,以一部分的概率按照最優(yōu)的 q 值選擇動作。同時,這個選擇隨機動作的概率應當隨著訓練的過程逐步減小。
在如下的代碼塊中,實現(xiàn) epsilon-greedy 算法的邏輯,并運行測試代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import random import operator actions = [ 'u' , 'r' , 'd' , 'l' ] qline = { 'u' : 1.2 , 'r' : - 2.1 , 'd' : - 24.5 , 'l' : 27 } epsilon = 0.3 # 以0.3的概率進行隨機選擇 def choose_action(epsilon): action = none if random.uniform( 0 , 1.0 ) < = epsilon: # 以某一概率 action = random.choice(actions) # 實現(xiàn)對動作的隨機選擇 else : action = max (qline.items(), key = operator.itemgetter( 1 ))[ 0 ] # 否則選擇具有最大 q 值的動作 return action |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
range ( 100 ): res + = choose_action(epsilon) print (res) res = '' for i in range ( 100 ): res + = choose_action(epsilon) print (res) ldllrrllllrlldlldllllllllllddulldlllllldllllludlldllllluudllllllulllllllllllullullllllllldlulllllrlr |
section 2 代碼實現(xiàn)
2.1 maze 類理解
我們首先引入了迷宮類 maze
,這是一個非常強大的函數(shù),它能夠根據(jù)你的要求隨機創(chuàng)建一個迷宮,或者根據(jù)指定的文件,讀入一個迷宮地圖信息。
-
使用
maze("file_name")
根據(jù)指定文件創(chuàng)建迷宮,或者使用maze(maze_size=(height, width))
來隨機生成一個迷宮。 -
使用
trap number
參數(shù),在創(chuàng)建迷宮的時候,設定迷宮中陷阱的數(shù)量。 -
直接鍵入迷宮變量的名字按回車,展示迷宮圖像(如
g=maze("xx.txt")
,那么直接輸入g
即可。 - 建議生成的迷宮尺寸,長在 6~12 之間,寬在 10~12 之間。
在如下的代碼塊中,創(chuàng)建你的迷宮并展示。
1
2
3
4
5
6
7
8
|
from maze import maze % matplotlib inline % confer inlinebackend.figure_format = 'retina' ## to-do: 創(chuàng)建迷宮并展示 g = maze(maze_size = ( 6 , 8 ), trap_number = 1 ) g maze of size ( 12 , 12 ) |
你可能已經注意到,在迷宮中我們已經默認放置了一個機器人。實際上,我們?yōu)槊詫m配置了相應的 api,來幫助機器人的移動與感知。其中你隨后會使用的兩個 api 為 maze.sense_robot()
及 maze.move_robot()
。
-
maze.sense_robot()
為一個無參數(shù)的函數(shù),輸出機器人在迷宮中目前的位置。 -
maze.move_robot(direction)
對輸入的移動方向,移動機器人,并返回對應動作的獎勵值。
隨機移動機器人,并記錄下獲得的獎勵,展示出機器人最后的位置。
1
2
3
4
5
6
7
8
9
10
11
|
rewards = [] ## 循環(huán)、隨機移動機器人10次,記錄下獎勵 for i in range ( 10 ): res = g.move_robot(random. choice(actions)) rewards.append(res) ## 輸出機器人最后的位置 print (g.sense_robot()) ## 打印迷宮,觀察機器人位置 g ( 0 , 9 ) |
2.2 robot 類實現(xiàn)
robot
類是我們需要重點實現(xiàn)的部分。在這個類中,我們需要實現(xiàn)諸多功能,以使得我們成功實現(xiàn)一個強化學習智能體。總體來說,之前我們是人為地在環(huán)境中移動了機器人,但是現(xiàn)在通過實現(xiàn) robot
這個類,機器人將會自己移動。通過實現(xiàn)學習函數(shù),robot
類將會學習到如何選擇最優(yōu)的動作,并且更新強化學習中對應的參數(shù)。
首先 robot
有多個輸入,其中 alpha=0.5, gamma=0.9, epsilon0=0.5
表征強化學習相關的各個參數(shù)的默認值,這些在之前你已經了解到,maze
應為機器人所在迷宮對象。
隨后觀察 robot.update
函數(shù),它指明了在每次執(zhí)行動作時,robot
需要執(zhí)行的程序。按照這些程序,各個函數(shù)的功能也就明了了。
運行如下代碼檢查效果(記得將 maze
變量修改為你創(chuàng)建迷宮的變量名)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
import random import operator class robot( object ): def __init__( self , maze, alpha = 0.5 , gamma = 0.9 , epsilon0 = 0.5 ): self . maze = maze self .valid_actions = self .maze.valid_actions self .state = none self .action = none # set parameters of the learning robot self .alpha = alpha self .gamma = gamma self .epsilon0 = epsilon0 self . epsilon = epsilon0 self .t = 0 self .qtable = {} self . reset() def . reset( self ): """ reset the robot """ self .state = self .sense_state() self .create_qtable_line( self .state) def . set status( self , learning = false, testing = false): """ determine whether the robot is learning its q table, or executing the testing procedure. """ self . learning = learning self .testing = testing def . update_parameter( self ): """ some of the paramters of the q learning robot can be altered, update these parameters when necessary. """ if self .testing: # todo 1. no random choice when testing self . epsilon = 0 else : # todo 2. update parameters when learning self . epsilon * = 0.95 return self . epsilon def . sense_state( self ): """ get the current state of the robot. in this """ # todo 3. return robot's current state return self .maze.sense_robot() def . create_qtable_line( self , state): """ create the qtable with the current state """ # todo 4. create qtable with current state # our qtable should be a two level dict, # qtable[state] ={'u':xx, 'd':xx, ...} # if qtable[state] already exits, then do # not change it. self .qtable.setdefault(state, {a: 0.0 for a in self .valid_actions}) def . choose_action( self ): """ return an action according to given rules """ def . is_random_exploration(): # todo 5. return whether do random choice # hint: generate a random number, and compare # it with epsilon return random.uniform( 0 , 1.0 ) < = self . epsilon if self . learning: if is_random_exploration(): # todo 6. return random choose aciton return random. choice( self .valid_actions) else : # todo 7. return action with highest q value return max ( self .qtable[ self .state].items(), key = operator.itemgetter( 1 ))[ 0 ] elif self .testing: # todo 7. choose action with highest q value return max ( self .qtable[ self .state].items(), key = operator.itemgetter( 1 ))[ 0 ] else : # todo 6. return random choose aciton return random. choice( self .valid_actions) def . update_qtable( self , r, action, next_state): """ update the qtable according to the given rule. """ if self . learning: # todo 8. when learning, update the q table according # to the given rules self .qtable[ self .state][action] = ( 1 - self .alpha) * self .qtable[ self .state][action] + self .alpha * ( r + self .gamma * max ( self .qtable[next_state].values())) def . update( self ): """ describle the procedure what to do when update the robot. called every time in every epoch in training or testing. return current action and reward. """ self .state = self .sense_state() # get the current state self .create_qtable_line( self .state) # for the state, create q table line action = self .choose_action() # choose action for this state reward = self .maze.move_robot(action) # move robot for given action next_state = self .sense_state() # get next state self .create_qtable_line(next_state) # create q table line for next state if self . learning and not self .testing: self .update_qtable(reward, action, next_state) # update q table self .update_parameter() # update parameters return action, reward # from robot import robot # g=maze(maze_size=(6,12), trap_number=2) g = maze( "test_world\maze_01.txt" ) robot = robot(g) # 記得將 maze 變量修改為你創(chuàng)建迷宮的變量名 robot.set_status(learning = true,testing = false) print (robot.update()) g ( 'd' , - 0.1 ) maze of size ( 12 , 12 ) |
2.3 用 runner 類訓練 robot
在完成了上述內容之后,我們就可以開始對我們 robot
進行訓練并調參了。我們準備了又一個非常棒的類 runner
,來實現(xiàn)整個訓練過程及可視化。使用如下的代碼,你可以成功對機器人進行訓練。并且你會在當前文件夾中生成一個名為 filename
的視頻,記錄了整個訓練的過程。通過觀察該視頻,你能夠發(fā)現(xiàn)訓練過程中的問題,并且優(yōu)化你的代碼及參數(shù)。
嘗試利用下列代碼訓練機器人,并進行調參。可選的參數(shù)包括:
-
訓練參數(shù)
-
訓練次數(shù)
epoch
-
訓練次數(shù)
-
機器人參數(shù):
-
epsilon0
(epsilon 初值) -
epsilon
衰減(可以是線性、指數(shù)衰減,可以調整衰減的速度),你需要在 robot.py 中調整 -
alpha
-
gamma
-
-
迷宮參數(shù):
- 迷宮大小
- 迷宮中陷阱的數(shù)量
- 可選的參數(shù):
- epoch = 20
- epsilon0 = 0.5
- alpha = 0.5
- gamma = 0.9
- maze_size = (6,8)
- trap_number = 2
1
2
3
4
5
6
7
8
9
10
|
from runner import runner g = maze(maze_size = maze_size,trap_number = trap_number) r = robot(g,alpha = alpha, epsilon0 = epsilon0, gamma = gamma) r.set_status(learning = true) runner = runner(r, g) runner.run_training(epoch, display_direction = true) #runner.generate_movie(filename = "final1.mp4") # 你可以注釋該行代碼,加快運行速度,不過你就無法觀察到視頻了。 g |
使用 runner.plot_results()
函數(shù),能夠打印機器人在訓練過程中的一些參數(shù)信息。
- success times 代表機器人在訓練過程中成功的累計次數(shù),這應當是一個累積遞增的圖像。
- accumulated rewards 代表機器人在每次訓練 epoch 中,獲得的累積獎勵的值,這應當是一個逐步遞增的圖像。
- running times per epoch 代表在每次訓練 epoch 中,小車訓練的次數(shù)(到達終點就會停止該 epoch 轉入下次訓練),這應當是一個逐步遞減的圖像。
使用 runner.plot_results()
輸出訓練結果
1
|
runner.plot_results() |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.51cto.com/14159827/2403054