歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~

本文由羅暉發表於雲+社區專欄

1. Google的DQN論文

2015年2月,Google在Nature上發表了一篇論文(見附件):Human-level control through deep reinforcement learning。文章描述瞭如何讓電腦自己學會打Atari 2600電子遊戲。

Atari 2600是80年代風靡美國的遊戲機,總共包括49個獨立的遊戲,其中不乏我們熟悉的Breakout(打磚塊),Galaxy Invaders(小蜜蜂)等經典遊戲。Google演算法的輸入只有遊戲屏幕的圖像和遊戲的得分,在沒有人為幹預的情況下,電腦自己學會了遊戲的玩法,而且在29個遊戲中打破了人類玩家的記錄。

Google給出的深度絡架構圖如下:

網路的左邊是輸入,右邊是輸出。 遊戲屏幕的圖像先經過兩個卷積層(論文中寫的是三個),然後經過兩個全連接層, 最後映射到遊戲手柄所有可能的動作。各層之間使用ReLU激活函數。

2. 強化學習(Q-Learning)

根據維基百科的描述,強化學習定義如下:

強化學習是機器學習中的一個領域,強調如何基於環境而行動,以取得最大化的預期利益。其靈感來源於心理學中的行為主義理論,即有機體如何在環境給予的獎勵或懲罰的刺激下,逐步形成對刺激的預期,產生能獲得最大利益的習慣性行為。

在強化學習的世界裡, 演算法稱之為Agent, 它與環境發生交互,Agent從環境中獲取狀態(state),並決定自己要做出的動作(action).環境會根據自身的邏輯給Agent予以獎勵(reward)。獎勵有正向和反向之分。比如在遊戲中,每擊中一個敵人就是正向的獎勵,掉血或者遊戲結束就是反向的獎勵。

2.1. 馬爾可夫決策過程

現在的問題是,你如何公式化一個強化學習問題,然後進行推導呢?最常見的方法是通過馬爾可夫決策過程。

假設你是一個代理,身處某個環境中(例如《打磚塊》遊戲)。這個環境處於某個特定的狀態(例如,牌子的位置、球的位置與方向,每個磚塊存在與否)。人工智慧可以可以在這個環境中做出某些特定的動作(例如,向左或向右移動拍子)。

這些行為有時候會帶來獎勵(分數的上升)。行為改變環境,並帶來新的狀態,代理可以再執行另一個動作。你選擇這些動作的規則叫做策略。通常來說,環境是隨機的,這意味著下一狀態也或多或少是隨機的(例如,當你漏掉了球,發射一個新的時候,它會去往隨機的方向)。

狀態與動作的集合,加上改變狀態的規則,組成了一個馬爾可夫決策過程。這個過程(例如一個遊戲)中的一個情節(episode)形成了狀態、動作與獎勵的有限序列。

其中 si 表示狀態,ai 表示動作,ri+1 代表了執行這個動作後獲得的獎勵。情節以最終的狀態 sn 結束(例如,「Game Over」畫面)。一個馬爾可夫決策過程基於馬爾可夫假設(Markov assumption),即下一狀態 si+1 的概率取決於現在的狀態 si 和動作 ai,而不是之前的狀態與動作。

2.2. 折扣未來獎勵(Discounted Future Reward)

為了長期表現良好,我們不僅需要考慮即時獎勵,還有我們將得到的未來獎勵。我們該如何做呢?

對於給定的馬爾可夫決策過程的一次運行,我們可以容易地計算一個情節的總獎勵:

鑒於此,時間點 t 的總未來回報可以表達為:

但是由於我們的環境是隨機的,我們永遠無法確定如果我們在下一個相同的動作之後能否得到一樣的獎勵。時間愈往前,分歧也愈多。因此,這時候就要利用折扣未來獎勵來代替:

在這裡 γ 是數值在0與1之間的貼現因子——獎勵在距離我們越遠的未來,我們便考慮的越少。我們很容易看到,折扣未來獎勵在時間步驟 t 的數值可以根據在時間步驟 t+1 的相同方式表示:

如果我們將貼現因子定義為 γ=0,那麼我們的策略將會過於短淺,即完全基於即時獎勵。如果我們希望平衡即時與未來獎勵,那麼貼現因子應該近似於 γ=0.9。如果我們的環境是確定的,相同的動作總是導致相同的獎勵,那麼我們可以將貼現因子定義為 γ=1。

一個代理做出的好的策略應該是去選擇一個能夠最大化(折扣後)未來獎勵的動作。

2.3. Q-Learning演算法描述:

演算法中的 α 是指學習率,其控制前一個 Q 值和新提出的 Q 值之間被考慮到的差異程度。尤其是,當 α=1 時,兩個 Qs,a 互相抵消,結果剛好和貝爾曼方程一樣。

我們用來更新 Qs,a 的只是一個近似,而且在早期階段的學習中它完全可能是錯誤的。但是隨著每一次迭代,該近似會越來越準確;而且我們還發現如果我們執行這種更新足夠長時間,那麼 Q 函數就將收斂並能代表真實的 Q 值。

3. 卷積神經網路(CNN)

在圖像處理中,往往把圖像表示為像素的向量,比如一個1000×1000的圖像,可以表示為一個1000000的向量。在上一節中提到的神經網路中,如果隱含層數目與輸入層一樣,即也是1000000時,那麼輸入層到隱含層的參數數據為1000000×1000000=10^12,這樣就太多了,基本沒法訓練。所以圖像處理要想練成神經網路大法,必先減少參數加快速度。

3.1. 局部感知

卷積神經網路有兩種神器可以降低參數數目,第一種神器叫做局部感知野。一般認為人對外界的認知是從局部到全局的,而圖像的空間聯繫也是局部的像素聯繫較為緊密,而距離較遠的像素相關性則較弱。

因而,每個神經元其實沒有必要對全局圖像進行感知,只需要對局部進行感知,然後在更高層將局部的信息綜合起來就得到了全局的信息。網路部分連通的思想,也是受啟發於生物學裡面的視覺系統結構。視覺皮層的神經元就是局部接受信息的(即這些神經元只響應某些特定區域的刺激)。如下圖所示:左圖為全連接,右圖為局部連接。

在上右圖中,假如每個神經元只和10×10個像素值相連,那麼權值數據為1000000×100個參數,減少為原來的萬分之一。而那10×10個像素值對應的10×10個參數,其實就相當於卷積操作。

3.2. 參數共享

但其實這樣的話參數仍然過多,那麼就啟動第二級神器,即權值共享。在上面的局部連接中,每個神經元都對應100個參數,一共1000000個神經元,如果這1000000個神經元的100個參數都是相等的,那麼參數數目就變為100了。

怎麼理解權值共享呢?我們可以這100個參數(也就是卷積操作)看成是提取特徵的方式,該方式與位置無關。這其中隱含的原理則是:圖像的一部分的統計特性與其他部分是一樣的。這也意味著我們在這一部分學習的特徵也能用在另一部分上,所以對於這個圖像上的所有位置,我們都能使用同樣的學習特徵。

更直觀一些,當從一個大尺寸圖像中隨機選取一小塊,比如說 8x8 作為樣本,並且從這個小塊樣本中學習到了一些特徵,這時我們可以把從這個 8x8 樣本中學習到的特徵作為探測器,應用到這個圖像的任意地方中去。特別是,我們可以用從 8x8 樣本中所學習到的特徵跟原本的大尺寸圖像作卷積,從而對這個大尺寸圖像上的任一位置獲得一個不同特徵的激活值。

如下圖所示,展示了一個3×3的卷積核在5×5的圖像上做卷積的過程。每個卷積都是一種特徵提取方式,就像一個篩子,將圖像中符合條件(激活值越大越符合條件)的部分篩選出來。

3.3. 多卷積核

上面所述只有100個參數時,表明只有1個10×10的卷積核,顯然,特徵提取是不充分的,我們可以添加多個卷積核,比如32個卷積核,可以學習32種特徵。在有多個卷積核時,如下圖所示:

上圖右,不同顏色表明不同的卷積核。每個卷積核都會將圖像生成為另一幅圖像。比如兩個卷積核就可以將生成兩幅圖像,這兩幅圖像可以看做是一張圖像的不同的通道。如下圖所示,下圖有個小錯誤,即將w1改為w0,w2改為w1即可。下文中仍以w1和w2稱呼它們。

下圖展示了在四個通道上的卷積操作,有兩個卷積核,生成兩個通道。其中需要注意的是,四個通道上每個通道對應一個卷積核,先將w2忽略,只看w1,那麼在w1的某位置(i,j)處的值,是由四個通道上(i,j)處的卷積結果相加然後再取激活函數值得到的。

所以,在上圖由4個通道卷積得到2個通道的過程中,參數的數目為4×2×2×2個,其中4表示4個通道,第一個2表示生成2個通道,最後的2×2表示卷積核大小。

3.4. Down-pooling

在通過卷積獲得了特徵 (features) 之後,下一步我們希望利用這些特徵去做分類。理論上講,人們可以用所有提取得到的特徵去訓練分類器,例如 softmax 分類器,但這樣做面臨計算量的挑戰。

例如:對於一個 96X96 像素的圖像,假設我們已經學習得到了400個定義在8X8輸入上的特徵,每一個特徵和圖像卷積都會得到一個 (96 ? 8 + 1) × (96 ? 8 + 1) = 7921 維的卷積特徵,由於有 400 個特徵,所以每個樣例 (example) 都會得到一個 7921 × 400 = 3,168,400 維的卷積特徵向量。學習一個擁有超過 3 百萬特徵輸入的分類器十分不便,並且容易出現過擬合 (over-fitting)。

為瞭解決這個問題,首先回憶一下,我們之所以決定使用卷積後的特徵是因為圖像具有一種「靜態性」的屬性,這也就意味著在一個圖像區域有用的特徵極有可能在另一個區域同樣適用。

因此,為了描述大的圖像,一個很自然的想法就是對不同位置的特徵進行聚合統計,例如,人們可以計算圖像一個區域上的某個特定特徵的平均值 (或最大值)。這些概要統計特徵不僅具有低得多的維度 (相比使用所有提取得到的特徵),同時還會改善結果(不容易過擬合)。這種聚合的操作就叫做池化 (pooling),有時也稱為平均池化或者最大池化 (取決於計算池化的方法)。

3.5. 多層卷積

在實際應用中,往往使用多層卷積,然後再使用全連接層進行訓練,多層卷積的目的是一層卷積學到的特徵往往是局部的,層數越高,學到的特徵就越全局化。

4. DQN演算法描述

單純的Q-Learning演算法使用表來保存狀態,一個1000×1000圖像的像素狀態數基本接近與無窮,故有了CNN+Q-Learning 即DQN演算法,演算法描述如下:

5. 使用DQN訓練「接磚塊」遊戲

深度學習的開源類庫比較多,比較著名的有tensorlow、caffe等。此處我們使用Tensorflow來訓練遊戲「接磚塊」。

遊戲截圖如下:

通過點擊滑鼠左鍵、右鍵控制滑塊的左右移動來接住小球,如果球碰到底面,則遊戲結束

主要python代碼如下(遊戲本身的代碼省略,此處主要關注演算法代碼):

#Game的定義類此處Game是什麼不重要只要提供執行Action的方法獲取當前遊戲區域像素的方法即可
class Game(object):
def __init__(self): #Game初始化
# action是MOVE_STAYMOVE_LEFTMOVE_RIGHT
# ai控制棒子左右移動返回遊戲界面像素數和對應的獎勵(像素->獎勵->強化棒子往獎勵高的方向移動)
def step(self, action):
?
# learning_rate
LEARNING_RATE = 0.99
# 跟新梯度
INITIAL_EPSILON = 1.0
FINAL_EPSILON = 0.05
# 測試觀測次數
EXPLORE = 500000
OBSERVE = 500
# 記憶經驗大小
REPLAY_MEMORY = 500000
# 每次訓練取出的記錄數
BATCH = 100
# 輸出層神經元數代表3種操作-MOVE_STAY:[1, 0, 0] MOVE_LEFT:[0, 1, 0] MOVE_RIGHT:[0, 0, 1]
output = 3 # MOVE_STAY:[1, 0, 0] MOVE_LEFT:[0, 1, 0] MOVE_RIGHT:[0, 0, 1]
input_image = tf.placeholder("float", [None, 80, 100, 4]) # 遊戲像素
action = tf.placeholder("float", [None, output]) # 操作
#定義CNN-卷積神經網路
def convolutional_neural_network(input_image):
weights = {w_conv1:tf.Variable(tf.zeros([8, 8, 4, 32])),
w_conv2:tf.Variable(tf.zeros([4, 4, 32, 64])),
w_conv3:tf.Variable(tf.zeros([3, 3, 64, 64])),
w_fc4:tf.Variable(tf.zeros([3456, 784])),
w_out:tf.Variable(tf.zeros([784, output]))}
?
biases = {b_conv1:tf.Variable(tf.zeros([32])),
b_conv2:tf.Variable(tf.zeros([64])),
b_conv3:tf.Variable(tf.zeros([64])),
b_fc4:tf.Variable(tf.zeros([784])),
b_out:tf.Variable(tf.zeros([output]))}
?
conv1 = tf.nn.relu(tf.nn.conv2d(input_image, weights[w_conv1], strides = [1, 4, 4, 1], padding = "VALID") + biases[b_conv1])
conv2 = tf.nn.relu(tf.nn.conv2d(conv1, weights[w_conv2], strides = [1, 2, 2, 1], padding = "VALID") + biases[b_conv2])
conv3 = tf.nn.relu(tf.nn.conv2d(conv2, weights[w_conv3], strides = [1, 1, 1, 1], padding = "VALID") + biases[b_conv3])
conv3_flat = tf.reshape(conv3, [-1, 3456])
fc4 = tf.nn.relu(tf.matmul(conv3_flat, weights[w_fc4]) + biases[b_fc4])
?
output_layer = tf.matmul(fc4, weights[w_out]) + biases[b_out]
return output_layer
?
#訓練神經網路
def train_neural_network(input_image):
predict_action = convolutional_neural_network(input_image)
?
argmax = tf.placeholder("float", [None, output])
gt = tf.placeholder("float", [None])
?
action = tf.reduce_sum(tf.mul(predict_action, argmax), reduction_indices = 1)
cost = tf.reduce_mean(tf.square(action - gt))
optimizer = tf.train.AdamOptimizer(1e-6).minimize(cost)
?
game = Game()
D = deque()
?
_, image = game.step(MOVE_STAY)
image = cv2.cvtColor(cv2.resize(image, (100, 80)), cv2.COLOR_BGR2GRAY)
ret, image = cv2.threshold(image, 1, 255, cv2.THRESH_BINARY)
input_image_data = np.stack((image, image, image, image), axis = 2)
#print ("IMG2:%s" %input_image_data)
?
with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
?
saver = tf.train.Saver()
?
n = 0
epsilon = INITIAL_EPSILON
while True:
#print("InputImageData:", input_image_data)
action_t = predict_action.eval(feed_dict = {input_image : [input_image_data]})[0]
?
argmax_t = np.zeros([output], dtype=np.int)
if(random.random() <= INITIAL_EPSILON):
maxIndex = random.randrange(output)
else:
maxIndex = np.argmax(action_t)
argmax_t[maxIndex] = 1
if epsilon > FINAL_EPSILON:
epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / EXPLORE
?
reward, image = game.step(list(argmax_t))
?
image = cv2.cvtColor(cv2.resize(image, (100, 80)), cv2.COLOR_BGR2GRAY)
ret, image = cv2.threshold(image, 1, 255, cv2.THRESH_BINARY)
image = np.reshape(image, (80, 100, 1))
input_image_data1 = np.append(image, input_image_data[:, :, 0:3], axis = 2)
?
D.append((input_image_data, argmax_t, reward, input_image_data1))
?
if len(D) > REPLAY_MEMORY:
D.popleft()
?
if n > OBSERVE:
minibatch = random.sample(D, BATCH)
input_image_data_batch = [d[0] for d in minibatch]
argmax_batch = [d[1] for d in minibatch]
reward_batch = [d[2] for d in minibatch]
input_image_data1_batch = [d[3] for d in minibatch]
?
gt_batch = []
?
out_batch = predict_action.eval(feed_dict = {input_image : input_image_data1_batch})
?
for i in range(0, len(minibatch)):
gt_batch.append(reward_batch[i] + LEARNING_RATE * np.max(out_batch[i]))
?
print("gt_batch:", gt_batch, "argmax:", argmax_batch)
optimizer.run(feed_dict = {gt : gt_batch, argmax : argmax_batch, input_image : input_image_data_batch})
?
input_image_data = input_image_data1
n = n+1
print(n, "epsilon:", epsilon, " " ,"action:", maxIndex, " " ,"reward:", reward)
?
train_neural_network(input_image)

6. 總結

說到這裡,相信你已經能對強化學習有了一個大致的瞭解。接下來的事情,應該是如何把這項技術應用到我們的工作中,讓它發揮出應有的價值。

問答 什麼時候使用某種強化學習演算法? 相關閱讀 使用 Q-Learning 實現 FlappyBird AI 強化學習總結 一文學習基於蒙特卡羅的強化學習方法 【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識

此文已由作者授權騰訊雲+社區發布,更多原文請點擊

搜索關注公眾號「雲加社區」,第一時間獲取技術乾貨,關注後回復1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區!


推薦閱讀:
相關文章