強化學習與深度強化學習理解

強化學習

主要參考西瓜書和一些網上視頻加上個人理解,歡迎互動。

強化學習的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)
        '''

 完整代碼鏈接:

https://github.com/yenchenlin/DeepLearningFlappyBird

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章