歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~html
本文由 羅暉發表於 雲+社區專欄
2015年2月,Google在Nature上發表了一篇論文(見附件):Human-level control through deep reinforcement learning。文章描述瞭如何讓電腦本身學會打Atari 2600電子遊戲。python
Atari 2600是80年代風靡美國的遊戲機,總共包括49個獨立的遊戲,其中不乏咱們熟悉的Breakout(打磚塊),Galaxy Invaders(小蜜蜂)等經典遊戲。Google算法的輸入只有遊戲屏幕的圖像和遊戲的得分,在沒有人爲干預的狀況下,電腦本身學會了遊戲的玩法,並且在29個遊戲中打破了人類玩家的記錄。算法
Google給出的深度絡架構圖以下:網絡
網絡的左邊是輸入,右邊是輸出。 遊戲屏幕的圖像先通過兩個卷積層(論文中寫的是三個),而後通過兩個全鏈接層, 最後映射到遊戲手柄全部可能的動做。各層之間使用ReLU激活函數。架構
根據維基百科的描述,強化學習定義以下:app
強化學習是機器學習中的一個領域,強調如何基於環境而行動,以取得最大化的預期利益。其靈感來源於心理學中的行爲主義理論,即有機體如何在環境給予的獎勵或懲罰的刺激下,逐步造成對刺激的預期,產生能得到最大利益的習慣性行爲。dom
在強化學習的世界裏, 算法稱之爲Agent, 它與環境發生交互,Agent從環境中獲取狀態(state),並決定本身要作出的動做(action).環境會根據自身的邏輯給Agent予以獎勵(reward)。獎勵有正向和反向之分。好比在遊戲中,每擊中一個敵人就是正向的獎勵,掉血或者遊戲結束就是反向的獎勵。機器學習
如今的問題是,你如何公式化一個強化學習問題,而後進行推導呢?最多見的方法是經過馬爾可夫決策過程。ide
假設你是一個代理,身處某個環境中(例如《打磚塊》遊戲)。這個環境處於某個特定的狀態(例如,牌子的位置、球的位置與方向,每一個磚塊存在與否)。人工智能能夠能夠在這個環境中作出某些特定的動做(例如,向左或向右移動拍子)。函數
這些行爲有時候會帶來獎勵(分數的上升)。行爲改變環境,並帶來新的狀態,代理能夠再執行另外一個動做。你選擇這些動做的規則叫作策略。一般來講,環境是隨機的,這意味着下一狀態也或多或少是隨機的(例如,當你漏掉了球,發射一個新的時候,它會去往隨機的方向)。
狀態與動做的集合,加上改變狀態的規則,組成了一個馬爾可夫決策過程。這個過程(例如一個遊戲)中的一個情節(episode)造成了狀態、動做與獎勵的有限序列。
其中 si 表示狀態,ai 表示動做,ri+1 表明了執行這個動做後得到的獎勵。情節以最終的狀態 sn 結束(例如,「Game Over」畫面)。一個馬爾可夫決策過程基於馬爾可夫假設(Markov assumption),即下一狀態 si+1 的機率取決於如今的狀態 si 和動做 ai,而不是以前的狀態與動做。
爲了長期表現良好,咱們不只須要考慮即時獎勵,還有咱們將獲得的將來獎勵。咱們該如何作呢?
對於給定的馬爾可夫決策過程的一次運行,咱們能夠容易地計算一個情節的總獎勵:
鑑於此,時間點 t 的總將來回報能夠表達爲:
可是因爲咱們的環境是隨機的,咱們永遠沒法肯定若是咱們在下一個相同的動做以後可否獲得同樣的獎勵。時間愈往前,分歧也愈多。所以,這時候就要利用折扣將來獎勵來代替:
在這裏 γ 是數值在0與1之間的貼現因子——獎勵在距離咱們越遠的將來,咱們便考慮的越少。咱們很容易看到,折扣將來獎勵在時間步驟 t 的數值能夠根據在時間步驟 t+1 的相同方式表示:
若是咱們將貼現因子定義爲 γ=0,那麼咱們的策略將會過於短淺,即徹底基於即時獎勵。若是咱們但願平衡即時與將來獎勵,那麼貼現因子應該近似於 γ=0.9。若是咱們的環境是肯定的,相同的動做老是致使相同的獎勵,那麼咱們能夠將貼現因子定義爲 γ=1。
一個代理作出的好的策略應該是去選擇一個可以最大化(折扣後)將來獎勵的動做。
算法中的 α 是指學習率,其控制前一個 Q 值和新提出的 Q 值之間被考慮到的差別程度。尤爲是,當 α=1 時,兩個 Qs,a 互相抵消,結果恰好和貝爾曼方程同樣。
咱們用來更新 Qs,a 的只是一個近似,並且在早期階段的學習中它徹底多是錯誤的。可是隨着每一次迭代,該近似會愈來愈準確;並且咱們還發現若是咱們執行這種更新足夠長時間,那麼 Q 函數就將收斂並能表明真實的 Q 值。
在圖像處理中,每每把圖像表示爲像素的向量,好比一個1000×1000的圖像,能夠表示爲一個1000000的向量。在上一節中提到的神經網絡中,若是隱含層數目與輸入層同樣,即也是1000000時,那麼輸入層到隱含層的參數數據爲1000000×1000000=10^12,這樣就太多了,基本無法訓練。因此圖像處理要想練成神經網絡大法,必先減小參數加快速度。
卷積神經網絡有兩種神器能夠下降參數數目,第一種神器叫作局部感知野。通常認爲人對外界的認知是從局部到全局的,而圖像的空間聯繫也是局部的像素聯繫較爲緊密,而距離較遠的像素相關性則較弱。
於是,每一個神經元其實沒有必要對全局圖像進行感知,只須要對局部進行感知,而後在更高層將局部的信息綜合起來就獲得了全局的信息。網絡部分連通的思想,也是受啓發於生物學裏面的視覺系統結構。視覺皮層的神經元就是局部接受信息的(即這些神經元只響應某些特定區域的刺激)。以下圖所示:左圖爲全鏈接,右圖爲局部鏈接。
在上右圖中,假如每一個神經元只和10×10個像素值相連,那麼權值數據爲1000000×100個參數,減小爲原來的萬分之一。而那10×10個像素值對應的10×10個參數,其實就至關於卷積操做。
但其實這樣的話參數仍然過多,那麼就啓動第二級神器,即權值共享。在上面的局部鏈接中,每一個神經元都對應100個參數,一共1000000個神經元,若是這1000000個神經元的100個參數都是相等的,那麼參數數目就變爲100了。
怎麼理解權值共享呢?咱們能夠這100個參數(也就是卷積操做)當作是提取特徵的方式,該方式與位置無關。這其中隱含的原理則是:圖像的一部分的統計特性與其餘部分是同樣的。這也意味着咱們在這一部分學習的特徵也能用在另外一部分上,因此對於這個圖像上的全部位置,咱們都能使用一樣的學習特徵。
更直觀一些,當從一個大尺寸圖像中隨機選取一小塊,好比說 8x8 做爲樣本,而且從這個小塊樣本中學習到了一些特徵,這時咱們能夠把從這個 8x8 樣本中學習到的特徵做爲探測器,應用到這個圖像的任意地方中去。特別是,咱們能夠用從 8x8 樣本中所學習到的特徵跟本來的大尺寸圖像做卷積,從而對這個大尺寸圖像上的任一位置得到一個不一樣特徵的激活值。
以下圖所示,展現了一個3×3的卷積核在5×5的圖像上作卷積的過程。每一個卷積都是一種特徵提取方式,就像一個篩子,將圖像中符合條件(激活值越大越符合條件)的部分篩選出來。
上面所述只有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表示卷積核大小。
在經過卷積得到了特徵 (features) 以後,下一步咱們但願利用這些特徵去作分類。理論上講,人們能夠用全部提取獲得的特徵去訓練分類器,例如 softmax 分類器,但這樣作面臨計算量的挑戰。
例如:對於一個 96X96 像素的圖像,假設咱們已經學習獲得了400個定義在8X8輸入上的特徵,每個特徵和圖像卷積都會獲得一個 (96 − 8 + 1) × (96 − 8 + 1) = 7921 維的卷積特徵,因爲有 400 個特徵,因此每一個樣例 (example) 都會獲得一個 7921 × 400 = 3,168,400 維的卷積特徵向量。學習一個擁有超過 3 百萬特徵輸入的分類器十分不便,而且容易出現過擬合 (over-fitting)。
爲了解決這個問題,首先回憶一下,咱們之因此決定使用卷積後的特徵是由於圖像具備一種「靜態性」的屬性,這也就意味着在一個圖像區域有用的特徵極有可能在另外一個區域一樣適用。
所以,爲了描述大的圖像,一個很天然的想法就是對不一樣位置的特徵進行聚合統計,例如,人們能夠計算圖像一個區域上的某個特定特徵的平均值 (或最大值)。這些概要統計特徵不只具備低得多的維度 (相比使用全部提取獲得的特徵),同時還會改善結果(不容易過擬合)。這種聚合的操做就叫作池化 (pooling),有時也稱爲平均池化或者最大池化 (取決於計算池化的方法)。
在實際應用中,每每使用多層卷積,而後再使用全鏈接層進行訓練,多層卷積的目的是一層卷積學到的特徵每每是局部的,層數越高,學到的特徵就越全局化。
單純的Q-Learning算法使用表來保存狀態,一個1000×1000圖像的像素狀態數基本接近與無窮,故有了CNN+Q-Learning 即DQN算法,算法描述以下:
深度學習的開源類庫比較多,比較著名的有tensorlow、caffe等。此處咱們使用Tensorflow來訓練遊戲「接磚塊」。
遊戲截圖以下:
經過點擊鼠標左鍵、右鍵控制滑塊的左右移動來接住小球,若是球碰到底面,則遊戲結束
主要python代碼以下(遊戲自己的代碼省略,此處主要關注算法代碼):
#Game的定義類,此處Game是什麼不重要,只要提供執行Action的方法,獲取當前遊戲區域像素的方法便可 class Game(object): def __init__(self): #Game初始化 # action是MOVE_STAY、MOVE_LEFT、MOVE_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)
說到這裏,相信你已經能對強化學習有了一個大體的瞭解。接下來的事情,應該是如何把這項技術應用到咱們的工做中,讓它發揮出應有的價值。
問答
何時使用某種強化學習算法?
相關閱讀
使用 Q-Learning 實現 FlappyBird AI
強化學習總結
一文學習基於蒙特卡羅的強化學習方法
【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識
此文已由做者受權騰訊雲+社區發佈,更多原文請點擊
搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!