強化學習
主要參考西瓜書和一些網上視頻加上個人理解,歡迎互動。
強化學習的model如下圖所示,機器在當前狀態下做出動作a,然後環境反饋給機器下一個狀態和一個獎勵。
假定狀態空間X,每一個狀態x∈X,動作空間A,每一個動作a∈A,獎賞函數爲R,P爲狀態轉移函數,那麼強化學習對應了四元組E=<X,A,P,R>。此處以西瓜澆水爲例:
機器通過在環境中不斷嘗試來學得一個最優的“策略”π。策略有兩種表示方法:一種是將策略表示爲函數π:X->A,確定性策略常用這種表示;另一種是概率表示π:X,A->R,隨機性策略常用這種表示,π(x, a)表示爲狀態x下選擇動作a的概率,且。[確定性策略是指在狀態x下,動作是唯一確定的]。另外上圖摘自西瓜書,個人覺得在採取隨機策略下,x=缺水下a=澆水和a=不澆水概率之和應該爲1,此處不是很理解。
強化學習與監督學習的異同
若將“狀態”對應監督學習中的“示例”,“動作”對應爲“標記”,則可看出強化學習的“策略”相當於監督學習中的“分類器”,相當於神經網絡中的修改連接權值W。先提前瞭解一下,如下圖所示:
K-搖臂賭博機
提出了探索和利用兩個概念。“僅探索”指將所有的嘗試機會平均分配給每個搖臂,最後計算每個搖臂平均吐幣數作爲獎賞近似。“僅利用”指按下目前爲止平均獎賞最大的搖臂。“僅探索”能很好估計每個搖臂的獎賞,卻會失去很多選擇最優搖臂機會,“僅利用”沒有很好的估計搖臂期望獎賞,可能經常選不到最優搖臂。
ε-貪心
ε-貪心法很簡單但很實用,是對探索和利用的折中,平時編程經常對隨機探索和啓發式挖掘做均衡,都差不多。每次嘗試時都以ε的概率進行探索,即以均勻概率隨機選擇一個搖臂;以1-ε概率利用,即選擇當前平均獎賞最高的搖臂。此處需要了解一下平均累計獎賞更新:
此處,Q表示獎賞函數,k表示第k個搖臂,n表示第幾次,v表示第n次搖k臂獲得的獎賞。
實際中ε一般設爲指數衰減函數,後期進行過多的探索會產生很多不需要的計算量,就像搖臂機搖10000的期望獎賞已經接近實際值了。
Softmax算法
也是用來完成對探索和利用的折中,基於Boltzmann分佈分配。
P(k)表示每個搖臂被選到的概率,t趨於0爲“僅利用”,t趨於無窮爲“僅探索”。Boltzmann分佈用處很廣,感興趣可以多google,此處我沒試過,不知道效果怎麼樣。
有模型學習
假定任務對應的馬爾科夫決策過程四元組E=<X,A,P,R>均已知。此時,對任意狀態x和x’和動作a,在x狀態下執行動作a轉移到x’狀態的概率是已知的,該轉移帶來的獎賞也是已知的。
在模型已知時,對任意策略π能估計出該策略帶來的期望累計獎賞。令函數表示從狀態x出發,使用策略π所帶來的累計獎賞;函數表示從狀態x出發,執行動作a後再使用策略π帶來的累計獎賞。
“策略”是通過在環境中不斷嘗試而學得,根據策略,在狀態x下就能得知要執行的動作a=π(x),當然如上所說,經常返回概率,表示執行不同動作的可能性。
由累計獎賞定義,有狀態值函數:
狀態動作值函數如下:
由於MDP(有模型學習)具有馬爾科夫性,系統下一時刻狀態僅由當前時刻狀態決定,不依賴以往狀態,所以值函數有簡單的遞歸形式:
這個推導數公式的理解關鍵在於第二步,這是一個簡單的全概率展開,此處採用的是上面所說的隨機性策略,並且假定執行的動作爲a。如此之後x狀態轉變爲x’,表示在執行完動作a後返回狀態x’的概率。此處強烈建議參考西瓜澆水圖理解【本文第二個圖片】。
類似的,對於γ折扣累計獎賞有:
如果觀察細心,會發現和的後半部分即狀態動作值函數。
'
所有的推導關鍵是爲了推出遞歸式,學過算法導論應該理解,遞歸可以用迭代解,也可以用動態規劃解,可以簡化計算,目的也就實現了。
策略改進
理想的策略應該能最大化累計獎賞,即
所以以上的迭代也應該替代爲最優迭代,即
上式均爲最優Bellman等式,其唯一解是最優值函數。
從上面推到和算法分析過程可知,在模型已知時強化學習任務能歸結爲基於動態規劃的尋優問題。
免模型學習
若學習算法不依賴於環境建模,則稱爲“免模型學習”。
蒙特卡羅強化學習
簡單理解:多次“採樣”,然後求取平均累計獎賞來作爲期望累計獎賞的近似。
簡單實現:在模型未知的情形下,我們從起始狀態出發,使用某種策略進行採樣,執行該策略T步並獲得軌跡
<x0,a0,r1, x1,a1,r2,…, xT-1,aT-1,rT,xT>
然後,對軌跡上出現的每一對狀態-動作,記錄其後的獎賞之和,作爲該狀態-動作對的一次累計獎賞採樣值。對多條軌跡進行採樣後,將每個狀態-動作對的累計獎賞採樣值進行平均,即得到狀態-動作值函數的估計。
同樣採用增量式計算,每採樣出一條軌跡,就根據該軌跡涉及的所有“狀態-動作”對來對值函數進行更新,即
時序差分學習
蒙特卡羅強化學習算法的本質,是通過多次嘗試後求平均來作爲期望累計獎賞的近似,但它在求平均時是“批處理式”進行的,即在一個完整的採樣軌跡完成後再對所有的狀態-動作對進行更新。實際上這個更新過程能增量式進行。對於狀態-動作對(x,a),不妨假定基於t個採樣已估計出值函數,則在得到第t+1個採樣時,有
更一般的,將替換成一個較小的正整數α,跟新步長越大,則越靠後的累積獎賞越重要。
無窮多個狀態的解決
不妨直接對連續狀態空間進行學習。假定狀態空間爲n維實數空間X=,此時難以用表格值函數來記錄,先考慮簡單情形,即值函數能表達成線性函數,即
其中x爲狀態向量,爲參數向量,此時的值函數難以精確記錄每個狀態的值,因此採用值函數近似,最常用的肯定就是最小二乘法了。即
爲使誤差最小,採用梯度下降法,關於梯度下降法可以參考BP神經網絡。
對於複雜的情形,線性函數估計不能達到理想的結果,這也就有了深度強化學習DQN。
模仿學習
此處直接以DQN爲例,簡單理解:先提供一些專家數據,可以理解爲帶標籤的數據,進行監督學習,學得一個初始策略,然後在輸入其它情形對反饋進行改進,獲得更好的決策。
逆強化學習
與模仿學習類似,從人類專家提供的範例數據中反推出獎賞函數有助於解決問題。
深度強化學習
深度強化學習DQN簡單可以用如下圖來進行表示,即
DQN實現代碼:
def trainNetwork(s, readout, h_fc1, sess):
# s作爲輸入,readout作爲輸出
# define the cost function
a = tf.placeholder("float", [None, ACTIONS])
y = tf.placeholder("float", [None])
readout_action = tf.reduce_sum(tf.multiply(readout, a), reduction_indices=1) # a=[0,1] or [1,0]
cost = tf.reduce_mean(tf.square(y - readout_action))
train_step = tf.train.AdamOptimizer(1e-6).minimize(cost) # Aim
# open up a game state to communicate with emulator
game_state = game.GameState()
# store the previous observations in replay memory
D = deque()
# printing
a_file = open("logs_" + GAME + "/readout.txt", 'w')
h_file = open("logs_" + GAME + "/hidden.txt", 'w')
# get the first state by doing nothing and preprocess the image to 80x80x4
do_nothing = np.zeros(ACTIONS)
do_nothing[0] = 1
x_t, r_0, terminal = game_state.frame_step(do_nothing)
x_t = cv2.cvtColor(cv2.resize(x_t, (80, 80)), cv2.COLOR_BGR2GRAY)
ret, x_t = cv2.threshold(x_t,1,255,cv2.THRESH_BINARY) # 黑白二值化
s_t = np.stack((x_t, x_t, x_t, x_t), axis=2)
# saving and loading networks
saver = tf.train.Saver()
sess.run(tf.initialize_all_variables())
checkpoint = tf.train.get_checkpoint_state("saved_networks")
if checkpoint and checkpoint.model_checkpoint_path:
saver.restore(sess, checkpoint.model_checkpoint_path)
print("Successfully loaded:", checkpoint.model_checkpoint_path)
else:
print("Could not find old network weights")
# start training
epsilon = INITIAL_EPSILON
t = 0
while "flappy bird" != "angry bird":
# choose an action epsilon greedily
readout_t = readout.eval(feed_dict={s : [s_t]})[0] # 此處值函數通過不斷積累經驗估計出來的
a_t = np.zeros([ACTIONS]) # 動作
action_index = 0
if t % FRAME_PER_ACTION == 0:
if random.random() <= epsilon:
print("----------Random Action----------")
action_index = random.randrange(ACTIONS)
a_t[random.randrange(ACTIONS)] = 1
else:
action_index = np.argmax(readout_t)
a_t[action_index] = 1
else:
a_t[0] = 1 # do nothing
# scale down epsilon
if epsilon > FINAL_EPSILON and t > OBSERVE:
epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / EXPLORE
# run the selected action and observe next state and reward
x_t1_colored, r_t, terminal = game_state.frame_step(a_t) # 返回狀態和reward
x_t1 = cv2.cvtColor(cv2.resize(x_t1_colored, (80, 80)), cv2.COLOR_BGR2GRAY)
ret, x_t1 = cv2.threshold(x_t1, 1, 255, cv2.THRESH_BINARY)
x_t1 = np.reshape(x_t1, (80, 80, 1))
#s_t1 = np.append(x_t1, s_t[:,:,1:], axis = 2)
s_t1 = np.append(x_t1, s_t[:, :, :3], axis=2)
# store the transition in D
D.append((s_t, a_t, r_t, s_t1, terminal))
if len(D) > REPLAY_MEMORY:
D.popleft()
# only train if done observing
if t > OBSERVE:
# sample a minibatch to train on
minibatch = random.sample(D, BATCH)
# get the batch variables
s_j_batch = [d[0] for d in minibatch]
a_batch = [d[1] for d in minibatch]
r_batch = [d[2] for d in minibatch]
s_j1_batch = [d[3] for d in minibatch]
y_batch = []
readout_j1_batch = readout.eval(feed_dict = {s : s_j1_batch})
for i in range(0, len(minibatch)):
terminal = minibatch[i][4]
# if terminal, only equals reward
if terminal:
y_batch.append(r_batch[i]) # 結束
else:
y_batch.append(r_batch[i] + GAMMA * np.max(readout_j1_batch[i])) # 累計reward
# perform gradient step
train_step.run(feed_dict = {
y : y_batch, # reward
a : a_batch, # 動作
s : s_j_batch} # 圖片狀態
)
# update the old values
s_t = s_t1 # 下一狀態
t += 1
# save progress every 10000 iterations
if t % 10000 == 0:
saver.save(sess, 'saved_networks/' + GAME + '-dqn', global_step = t)
# print info
state = ""
if t <= OBSERVE:
state = "observe"
elif t > OBSERVE and t <= OBSERVE + EXPLORE:
state = "explore"
else:
state = "train"
print("TIMESTEP", t, "/ STATE", state, \
"/ EPSILON", epsilon, "/ ACTION", action_index, "/ REWARD", r_t, \
"/ Q_MAX %e" % np.max(readout_t))
# write info to files
'''
if t % 10000 <= 100:
a_file.write(",".join([str(x) for x in readout_t]) + '\n')
h_file.write(",".join([str(x) for x in h_fc1.eval(feed_dict={s:[s_t]})[0]]) + '\n')
cv2.imwrite("logs_tetris/frame" + str(t) + ".png", x_t1)
'''
完整代碼鏈接: