用Gym學習強化學習之Policy Gradient

作者:Cloudyyyyy@HIT

興趣方向:自然語言處理、人工智能


目錄

  • 什麼是強化學習
  • 強化學習的問題要素
  • Gym簡介
  • Policy Gradient實戰
  • 總結
  • 參考

1 什麼是強化學習

強化學習在機器學習的應用分類裏常常和監督學習非監督學習並列。

監督學習非監督學習中,我們可以獲得固定的數據集,將數據集餵給特定的模型,擬合數學公式,學習其特徵規律。

監督學習 VS 非監督學習

在上述問題中,我們需要考慮的問題是如何將已有數據集的知識遷移到新的數據集中。

然而,強化學習並不屬於這類問題。在強化學習裏,我們沒有現成的數據集可以學習,而是需要在環境中“試錯”,根據環境給我們的反饋進行模型參數的調整。

所以,反饋即是強化學習獨特的問題特徵。

2 強化學習的問題要素

在強化學習中,三個重要的要素是:Actor(Agent)、Environment、Reward

這三者的關係可以表示爲:

Agent是我們學習的對象,Agent可以做出自己的行爲action,然後獲取Environment反饋的信息observation(state)。根據observation(state)附帶的激勵reward,Agent可以判斷本次action的好壞,進而調整自己的參數。同時,本次observation(state)將繼續作爲Agent的輸入進行下一次行爲action的判斷。

這種根據環境反饋來進行模型學習的方式就是強化學習。

3 Gym簡介

聽完上面的描述,讀者可能對強化學習還存在較爲模糊的認知,這裏選擇Gym平臺來讓大家直觀感受強化學習的訓練過程。

Gym 環境示意

Gym是Openai開發的一個用來測試強化學習算法的工具。當我們使用Gym的時候,我們只需要重點關注Agent的訓練過程,而Environment、reward這些信息都將由Gym平臺提供,無需我們進行額外的設計和開發。

這裏我們選擇較爲簡單的CartPole-v1 1進行強化學習算法Policy Gradient的實現:

在這個任務中,小車Cart通過左移和右移來保證竿子Pole的平衡,時間越久獎勵reward越高。

在使用前,請根據官方文檔安裝其Gym,本文選擇的工具爲Python 3.6,Pytorch 0.4

4 Policy Gradient實戰

在Policy Gradient中,我們需要訓練一個Agent。這個Agent相當於一個分類器,其輸入是觀測到環境的信息observation(state),輸出爲行爲action的概率分佈。我們可以用簡單的多層感知機去實現這個分類器:

在該任務中,可以查看輸入observation(state)和輸出action的維度:

import gym
env = gym.make('CartPole-v1')
print(env.action_space)
# Discrete(2)
print(env.observation_space)
# Box(4,)

我們可以構建一個簡單的多層感知機充當二分類器:

class PGN(nn.Module):
    def __init__(self):
        super(PGN, self).__init__()
        self.linear1 = nn.Linear(4, 24)
        self.linear2 = nn.Linear(24, 36)
        self.linear3 = nn.Linear(36, 1)

    def forward(self, x):
        x = F.relu(self.linear1(x))
        x = F.relu(self.linear2(x))
        x = torch.sigmoid(self.linear3(x))
        return x

我們的需要訓練一個CartAgent,應當至少具備以下接口:

class CartAgent(object):
    def __init__(self, learning_rate, gamma):
        self.pgn = PGN()
        self.gamma = gamma

        self._init_memory()
        self.optimizer = torch.optim.RMSprop(self.pgn.parameters(), lr=learning_rate)

    def memorize(self, state, action, reward):
        # save to memory for mini-batch gradient descent
        self.state_pool.append(state)
        self.action_pool.append(action)
        self.reward_pool.append(reward)
        self.steps += 1

    def learn(self):
        pass

    def act(self, state):
        return self.pgn(state)

前面提過,強化學習不同於監督學習(能夠直接使用訓練語料中的X和Y擬合該模型)。在Policy Gradient中,我們需要使用特殊的損失函數:

其中 p(a|s) 由Agent模型計算得出,R 即Reward,由環境給出。這個損失函數能夠提高reward值大的action出現的概率。loss的核心實現爲:

def learn(self):
    self._adjust_reward()

    # policy gradient
    self.optimizer.zero_grad()
    for i in range(self.steps):
        # all steps in multi games 
        state = self.state_pool[i]
        action = torch.FloatTensor([self.action_pool[i]])
        reward = self.reward_pool[i]

        probs = self.act(state)
        m = Bernoulli(probs)
        loss = -m.log_prob(action) * reward
        loss.backward()
    self.optimizer.step()
    
    self._init_memory()

想要讓該損失函數得到訓練,我們就必須獲得多組state,action,reward。在實際應用中,這樣的組合需要我們在遊戲過程中隨機抽樣出來。

對於某次遊戲,我們可以抽樣出多組state、action、reward:

具體的實現爲:

# hyper parameter
BATCH_SIZE = 5
LEARNING_RATE = 0.01
GAMMA = 0.99
NUM_EPISODES = 500

env = gym.make('CartPole-v1')
cart_agent = CartAgent(learning_rate=LEARNING_RATE, gamma=GAMMA)

for i_episode in range(NUM_EPISODES):
    next_state = env.reset()
    env.render(mode='rgb_array')

    for t in count():
        state = torch.from_numpy(next_state).float()

        probs = cart_agent.act(state)
        m = Bernoulli(probs)
        action = m.sample()

        action = action.data.numpy().astype(int).item()
        next_state, reward, done, _ = env.step(action)
        env.render(mode='rgb_array')

        # end action's reward equals 0
        if done:
            reward = 0

        cart_agent.memorize(state, action, reward)

        if done:
            logger.info({'Episode {}: durations {}'.format(i_episode, t)})
            break

    # update parameter every batch size
    if i_episode > 0 and i_episode % BATCH_SIZE == 0:
        cart_agent.learn()

上述state、action、reward組合存在一定的問題。在這個遊戲中所有的reward均爲0或1,但是某次遊戲實際進行了多輪,某次action產生的效益可能遠遠大於其他相同reward的action。因此,我們需要在某次遊戲中結合整個馬爾科夫鏈去考慮某次action的潛在reward

一種很容易想到的思路是,如果某次action之後的reward很大,說明這次action更好,我們可以將這種潛在的reward添加進來:

def _adjust_reward(self):
    # backward weight
    running_add = 0
    for i in reversed(range(self.steps)):
        if self.reward_pool[i] == 0:
            running_add = 0
        else:
            running_add = running_add * self.gamma + self.reward_pool[i]
            self.reward_pool[i] = running_add

此外,我們需要reward進行適當的均一化。如果reward均爲正,則在抽樣過程中可能會讓一些本應當降低概率的action獲得更大的概率提升;如果reward沒有進行平均,則遊戲行爲更多的action組傾向於獲得更大的概率提升。

    # normalize reward
    reward_mean = np.mean(self.reward_pool)
    reward_std = np.std(self.reward_pool)
    for i in range(self.steps):
        self.reward_pool[i] = (self.reward_pool[i] - reward_mean) / reward_std

完成代碼後,可以看到實驗結果。在起初的時候,小車只能堅持較短的時間:

進行150次遊戲後,小車堅持的時間已經有了顯著的提高。

完全版的Demo在這裏:github.com/cloudyyyyy/pytorch-rl-demo 4

5 總結

從實踐結果看,實現一個Demo級別的Policy Gradient算法就可以取得較好的效果。如果你想學習強化學習算法,Gym將會是一個測試這些算法的好幫手。

6 參考

  1. Gym: A toolkit for developing and comparing reinforcement learning algorithms
  2. 臺灣大學-李宏毅《Deep Reinforcement Learning》
  3. Reinforcement Learning (DQN) tutorial
  4. Finspire13/pytorch-policy-gradient-example
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章