[태그:] reinforcementLearning

  • CarpPole Deep Q Network 이해

    강아지 책에 나온 예제를 실행했다. 책 실행 환경과 내 그것이 달라 실행할 수 없었다. 나는 docker로 tensorflow를 사용한다. 그것도 cpu가 avx 등 을 지원하지 않아 직접 컴파일했다. openAi gym은 GUI 환경에서 실행되어 docker로 구동하기 힘들다. 데스크탑을 거실에 설치하여 다른 노트북에서 ssh로 접속하여 사용한다. 이런 모든 문제를 jypyter notebook로 해결했다. 전 글에 설명한대로 docker를 아래 명령으로 구동한다.

    docker run -it -v /run/user/1000:/run/user/1000 -v /dev:/dev -v /tmp/.X11-unix:/tmp/.X11-unix:ro --privileged --ipc=host --shm-size=256m --net=host -e DISPLAY=$DISPLAY -e XDG_RUNTIME_DIR=/run/user/1000 --runtime=nvidia -e LC_ALL=C.UTF-8 -v /home/now0930/tensorflow/:/home/mnt tensorflow/tensorflow:1.12.0-rc2-gpu-py3-keras /bin/bash

    jupyter notebook을 실행한다. uid 1000으로 실행하는 방법을 모르겠다.

    docker exec -w /home/mnt/gym vibrant_banach /usr/bin/xvfb-run -s "-screen 0 1400x600x24" jupyter notebook --allow-root

    책 코드를 실행하기 위해 아래와 같이 수정했다.

    import sys
    import gym
    import pylab
    import random
    import numpy as np
    from collections import deque
    from keras.layers import Dense
    from keras.optimizers import Adam
    from keras.models import Sequential
    from gym import wrappers
    
    EPISODES = 300
    
    
    # 카트폴 예제에서의 DQN 에이전트
    class DQNAgent:
        def __init__(self, state_size, action_size):
            self.render = False
            self.load_model = False
    
            # 상태와 행동의 크기 정의
            self.state_size = state_size
            self.action_size = action_size
            print("self.state_size는", self.state_size)
            print("self.action_size는", self.action_size)
            # DQN 하이퍼파라미터
            self.discount_factor = 0.99
            self.learning_rate = 0.001
            self.epsilon = 1.0
            self.epsilon_decay = 0.999
            self.epsilon_min = 0.01
            self.batch_size = 64
            #self.batch_size = 1
            self.train_start = 1000
    
            # 리플레이 메모리, 최대 크기 2000
            self.memory = deque(maxlen=2000)
    
            # 모델과 타깃 모델 생성
            self.model = self.build_model()
            self.target_model = self.build_model()
    
            # 타깃 모델 초기화
            self.update_target_model()
    
            if self.load_model:
                self.model.load_weights("./save_model/cartpole_dqn_trained.h5")
    
        # 상태가 입력, 큐함수가 출력인 인공신경망 생성
        def build_model(self):
            model = Sequential()
            model.add(Dense(24, input_dim=self.state_size, activation='relu',
                            kernel_initializer='he_uniform'))
            model.add(Dense(24, activation='relu',
                            kernel_initializer='he_uniform'))
            model.add(Dense(self.action_size, activation='linear',
                            kernel_initializer='he_uniform'))
            model.summary()
            model.compile(loss='mse', optimizer=Adam(lr=self.learning_rate))
            return model
    
        # 타깃 모델을 모델의 가중치로 업데이트
        def update_target_model(self):
            self.target_model.set_weights(self.model.get_weights())
    
        # 입실론 탐욕 정책으로 행동 선택
        def get_action(self, state):
            if np.random.rand() <= self.epsilon:
                return random.randrange(self.action_size)
            else:
                q_value = self.model.predict(state)
                #print("q_value는", q_value)
                #print("argamx(q_value)는", np.argmax(q_value[0]))
                #print("State는", state)
                return np.argmax(q_value[0])
    
        # 샘플 <s, a, r, s'>을 리플레이 메모리에 저장
        def append_sample(self, state, action, reward, next_state, done):
            self.memory.append((state, action, reward, next_state, done))
    
        # 리플레이 메모리에서 무작위로 추출한 배치로 모델 학습
        def train_model(self):
            if self.epsilon > self.epsilon_min:
                self.epsilon *= self.epsilon_decay
    
            # 메모리에서 배치 크기만큼 무작위로 샘플 추출
            mini_batch = random.sample(self.memory, self.batch_size)
            states = np.zeros((self.batch_size, self.state_size))
            next_states = np.zeros((self.batch_size, self.state_size))
            actions, rewards, dones = [], [], []
    
            for i in range(self.batch_size):
                states[i] = mini_batch[i][0]
                actions.append(mini_batch[i][1])
                rewards.append(mini_batch[i][2])
                next_states[i] = mini_batch[i][3]
                dones.append(mini_batch[i][4])
    
            # 현재 상태에 대한 모델의 큐함수
            # 다음 상태에 대한 타깃 모델의 큐함수
            target = self.model.predict(states)
            target_val = self.target_model.predict(next_states)
    
            # 벨만 최적 방정식을 이용한 업데이트 타깃
            for i in range(self.batch_size):
                #print("target은", target[i])
                if dones[i]:
                    target[i][actions[i]] = rewards[i]
                else:
                    target[i][actions[i]] = rewards[i] + self.discount_factor * (
                        np.amax(target_val[i]))
    
            self.model.fit(states, target, batch_size=self.batch_size,
                           epochs=10, verbose=0)
    
    
    if __name__ == "__main__":
        # CartPole-v1 환경, 최대 타임스텝 수가 500
        env = gym.make('CartPole-v1')
        env = wrappers.Monitor(env, "./gym-results-Cart", force=True, video_callable=lambda episode_id: episode_id%20==0)
    
        state_size = env.observation_space.shape[0]
        action_size = env.action_space.n
    
        # DQN 에이전트 생성
        agent = DQNAgent(state_size, action_size)
    
        scores, episodes = [], []
    
        for e in range(EPISODES):
            done = False
            score = 0
            # env 초기화
            #env = wrappers.Monitor(env, "./gym-results-Cart", force=True)
            state = env.reset()
            state = np.reshape(state, [1, state_size])
            #print("state reshape는",state)
    
            while not done:
                
                if agent.render:
                    env.render()
    
                # 현재 상태로 행동을 선택
                action = agent.get_action(state)
                # 선택한 행동으로 환경에서 한 타임스텝 진행
                next_state, reward, done, info = env.step(action)
                next_state = np.reshape(next_state, [1, state_size])
                # 에피소드가 중간에 끝나면 -100 보상
                reward = reward if not done or score == 499 else -100
    
                # 리플레이 메모리에 샘플 <s, a, r, s'> 저장
                agent.append_sample(state, action, reward, next_state, done)
                # 매 타임스텝마다 학습
                if len(agent.memory) >= agent.train_start:
                    agent.train_model()
    
                score += reward
                state = next_state
    
                if done:
                    # 각 에피소드마다 타깃 모델을 모델의 가중치로 업데이트
                    agent.update_target_model()
    
                    score = score if score == 500 else score + 100
                    # 에피소드마다 학습 결과 출력
                    scores.append(score)
                    episodes.append(e)
                    #pylab.plot(episodes, scores, 'b')
                    #pylab.savefig("./save_graph/cartpole_dqn.png")
                    print("episode:", e, "  score:", score, "  memory length:",
                          len(agent.memory), "  epsilon:", agent.epsilon)
    
                    # 이전 10개 에피소드의 점수 평균이 490보다 크면 학습 중단
                    if np.mean(scores[-min(10, len(scores)):]) > 490:
                        agent.model.save_weights("./save_model/cartpole_dqn.h5")
                        sys.exit()
    

    다음 내용을 수정했다.

    • 10 행: from gym import wrappers: wrappers 사용.
    • 120 행: env = wrappers.Monitor(env, “./gym-results-Cart”, force=True, video_callable=lambda episode_id: episode_id%20==0): 20번마다 동영상으로 저장. 여기를 설정하지 않으면 64번째 에피소드 동영상을 저장하고, 1,000번째 에피소드로 넘어간다. 학습 과정을 알 수 없다.

    cartPole-v1은 10초 동안 막대기가 넘어지지 않으면 끝나는 모델이다. 10초 전 막대기가 12도 넘게 기울어지면 끝난다. Q-learning은 아래 식으로 정의된다. \alpha 는 learning rate, \gamma 는 discount factor.

    Q(S_{t},A_{t})\leftarrow Q(S_{t},A_{t})+\alpha \left [R_{t+1}+\gamma\max_{a}Q(S_{t+1},a)-Q(S_{t},A_{t}) \right ]

    위 식에서 Q(S_{t},A_{t})R_{t+1}+\gamma \max_{a}Q(S_{t+1},a) 로 오차를 줄여 나간다. 오차는 MSE = ( 정답 – 예측 ) ^{2} = (R_{t+1} + \gamma Q(S_{t+1},A_{t+1}) - Q(S_{t},A_{t}) 로 정의한다. keras.fit에서 state에 따른 예측Q(S_{t},A_{t}) 과 정답인 target[i]로 학습한다. keras.fit을 부르기 전에 miniBatch 크기 모든 target[size]를 업데이트한다. print로 taget[i]값을 확인할 수 있다.

    episode: 73   score: 64.0   memory length: 2000   epsilon: 0.2699131774597243
    update전 target[20] [41.376812 37.715374]
    update후 target[20] [41.376812 40.977367]
    #action에 따른 정확한 Q 값을 수정.
    ##여기에서 무작위로 다시 뽑음.
    update전 target[20] [46.597427 39.114082]
    update후 target[20] [46.597427 42.882465]
    ...
    episode: 132   score: 419.0   memory length: 2000   epsilon: 0.009998671593271896
    update전 target[20] [113.11228 112.72044]
    update후 target[20] [113.11228 113.5387 ]
    update전 target[20] [109.97069 109.24642]
    update후 target[20] [109.97069 109.48601]
    update전 target[20] [112.96742 112.18164]
    update후 target[20] [112.92011 112.18164]
    # 132번 episode가 73번 episode보다 정답에 더 근접하여 업데이트 량이 줄었다.

    이렇게 10개 평균 score가 490 이상이면 학습을 중단한다. 적당한 Q 값을 찾는 문제라 value based Reinforce Learning이라 한다. 모델을 정의할 때 Q 값을 제한하지 않으려 activation을 linear로 설정했다.

    300번 학습 결과,스코어 vs 횟수.

    정책을 직접 근사하는 방법은 policy based Reinforce Learning인데, 마지막에 softmax 로 확율을 나오도록 해야 한다.

    Sutton & Barto 가 쓴 책을 보면 잘 이해가지 않는데, 직접 돌려 확인하면 이해했다 착각한다.

  • Monte Carlo Method 코드 분석

    여기에 있는 코드 참조..
    책은 “파이썬과 케라스로 배우는 강화학습” 내용.

    Monte Carlo Method의 코드가 설명되어 있지 않아, python 디버그로 그 결과를 잠깐 살폈다.

    메인 코드.

    # 메인 함수
    if __name__ == "__main__":
        env = Env()
        agent = MCAgent(actions=list(range(env.n_actions)))
    
        for episode in range(1000):
            state = env.reset()
            action = agent.get_action(state)
    
            while True:
                env.render()
    
                # 다음 상태로 이동
                # 보상은 숫자이고, 완료 여부는 boolean
                next_state, reward, done = env.step(action)
                agent.save_sample(next_state, reward, done)
                ##테스트용.
                print(agent.samples)
                print("\n")
    
                # 다음 행동 받아옴
                action = agent.get_action(next_state)
    
                # 에피소드가 완료됐을 때, 큐 함수 업데이트
                if done:
                    print("episode : ", episode)
                    agent.update()
                    agent.samples.clear()
                    break
    

    6번행: episode를 1,000번 반복.
    10번행: 무한번 반복이나, 25번 결과를 보고 break 결정. done은 15번 행에서 업데이트.

    (Pdb) l
    109                 # 보상은 숫자이고, 완료 여부는 boolean
    110                 next_state, reward, done = env.step(action)
    111                 agent.save_sample(next_state, reward, done)
    112                 ##테스트용.
    113                 print(agent.samples)
    114  ->              print("\n")
    115     
    116                 # 다음 행동 받아옴
    117                 action = agent.get_action(next_state)
    118
    119 # 에피소드가 완료됐을 때, 큐 함수 업데이트
    (Pdb) l
    120 if done:
    121 print("episode : ", episode)
    122 agent.update()
    123 agent.samples.clear()
    124 break
    [EOF]
    (Pdb) p agent.samples
    [[[0, 0], 0, False], [[0, 1], 0, False], [[1, 1], 0, False], [[1, 2], -100, True]]
    (Pdb)
    
    

    에피소드가 끝나면, 자신이 방문한 모든 셀을 list로 기억.
    아래 좌표 중, 행과 열이 바뀜.

    22번행: 현재 스테이트에서 다음 스테이트를 예측.

    (Pdb) l
     27             for reward in reversed(self.samples):
     28                 state = str(reward[0])
     29                 if state not in visit_state:
     30                     visit_state.append(state)
     31                     G_t = self.discount_factor * (reward[1] + G_t)
     32  ->                  value = self.value_table[state]
     33                     ##테스트..
     34                     print("Value is ",value)
     35                     self.value_table[state] = (value +
     36                                                self.learning_rate * (G_t - value))
     37     
    (Pdb) 
     38                     print("Value state is", state)
     39                     #print("type of value_table is", type(self.value_table))
     40                     print("Value table is", self.value_table)
     41                     #print("Value table is",self.value_table[state])
     42     
     43                     print("\n")
     44     
     45         # 큐 함수에 따라서 행동을 반환
     46         # 입실론 탐욕 정책에 따라서 행동을 반환
     47         def get_action(self, state):
     48             if np.random.rand() < self.epsilon:
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(34)update()
    -> print("Value is ",value)
    (Pdb) n
    Value is  0.0
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(35)update()
    -> self.value_table[state] = (value +
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(36)update()
    -> self.learning_rate * (G_t - value))
    (Pdb) 
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(38)update()
    -> print("Value state is", state)
    (Pdb) 
    Value state is [1, 2]
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(40)update()
    -> print("Value table is", self.value_table)
    (Pdb) 
    Value table is defaultdict(<class 'float'>, {'[0, 0]': 0.0, '[0, 1]': 0.0, '[1, 0]': 0.0, '[1, 2]': -0.9, '[2, 1]': 0.0, '[1, 1]': 0.0, '[1, 3]': 0.0, '[0, 2]': 0.0, '[2, 2]': 0.0})
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(43)update()
    -> print("\n")
    (Pdb) p self.value_table
    defaultdict(<class 'float'>, {'[0, 0]': 0.0, '[0, 1]': 0.0, '[1, 0]': 0.0, '[1, 2]': -0.9, '[2, 1]': 0.0, '[1, 1]': 0.0, '[1, 3]': 0.0, '[0, 2]': 0.0, '[2, 2]': 0.0})
    (Pdb) 
    
    

    self.value_table을 각 state 관리. 방문한 state를 list로 추가하고, 각 value를 update. 나중에 현재 state를 보고 action을 산출 할 때 필요.

    update 하면 teminal state의 reward를 감가율로 계속 감소시켜 시작 스테이트까지 value_table[state]로 업데이트..

    아래 코드에서

    if state not in visit_state가 있기 때문에, 처음 방문한 state만 관심을 갖음.

     19        # 메모리에 샘플을 추가
     20         def save_sample(self, state, reward, done):
     21             self.samples.append([state, reward, done])
     22     
     23         # 모든 에피소드에서 에이전트가 방문한 상태의 큐 함수를 업데이트
     24  ->      def update(self):
     25             G_t = 0
     26             visit_state = []
     27             for reward in reversed(self.samples):
     28                 state = str(reward[0])
     29                 if state not in visit_state:
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(25)update()
    -> G_t = 0
    (Pdb) p self.samples
    [[[1, 0], 0, False], [[1, 0], 0, False], [[2, 0], 0, False], [[3, 0], 0, False], [[3, 1], 0, False], [[3, 2], 0, False], [[2, 2], 100, True]]
    (Pdb) 
    ...
    (Pdb) l
     24         def update(self):
     25             G_t = 0
     26             visit_state = []
     27             for reward in reversed(self.samples):
     28                 state = str(reward[0])
     29  ->              if state not in visit_state:
     30                     visit_state.append(state)
     31                     G_t = self.discount_factor * (reward[1] + G_t)
     32                     value = self.value_table[state]
     33                     ##테스트..
     34                     print("Value is ",value)
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(30)update()
    -> visit_state.append(state)
    (Pdb) 
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(31)update()
    -> G_t = self.discount_factor * (reward[1] + G_t)
    (Pdb) 
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(32)update()
    -> value = self.value_table[state]
    (Pdb) p self.value_table
    defaultdict(<class 'float'>, {'[0, 0]': 10.523899590468769, '[0, 1]': 2.3971909843905967, '[1, 0]': 45.81574921045315, '[1, 2]': -2.67309, '[2, 1]': -19.99607765407679, '[1, 1]': 6.356969207767222, '[1, 3]': 0.0, '[0, 2]': -0.41447951099999986, '[2, 2]': 89.22430756838313, '[2, 0]': 50.92319972091863, '[3, 0]': 60.0913655106793, '[3, 1]': 66.88710703917133, '[0, 3]': -0.7290000000000001, '[0, 4]': -0.6561000000000001, '[1, 4]': 0.0, '[4, 0]': 8.302503491918047, '[4, 1]': 6.159613912118276, '[4, 2]': 6.304592658442578, '[3, 2]': 80.25132103821517, '[3, 3]': 6.970546731858152, '[2, 3]': 0.0, '[3, 4]': 0.6561000000000001, '[4, 3]': 0.0, '[2, 4]': 0.0, '[4, 4]': 0.0})
    
    

    500회 시행 후, 과거 이력을 보면 아래와 같다.

    (Pdb) p agent.samples
    [[[0, 1], 0, False], [[0, 0], 0, False], [[0, 1], 0, False], [[0, 0], 0, False], [[1, 0], 0, False], [[1, 1], 0, False], [[1, 0], 0, False], [[2, 0], 0, False], [[3, 0], 0, False], [[3, 1], 0, False], [[3, 2], 0, False], [[2, 2], 100, True]]
    (Pdb) l
    116                 # 다음 행동 받아옴
    117                 action = agent.get_action(next_state)
    118     
    119                 # 에피소드가 완료됐을 때, 큐 함수 업데이트
    120                 if done:
    121 B->                  print("episode : ", episode)
    122                     agent.update()
    123                     agent.samples.clear()
    124                     break
    [EOF]
    (Pdb) n
    episode :  500
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(122)<module>()
    -> agent.update()
    (Pdb) l
    117                 action = agent.get_action(next_state)
    118     
    119                 # 에피소드가 완료됐을 때, 큐 함수 업데이트
    120                 if done:
    121 B                   print("episode : ", episode)
    122  ->                  agent.update()
    123                     agent.samples.clear()
    124                     break
    [EOF]
    (Pdb) s
    --Call--
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(24)update()
    -> def update(self):
    (Pdb) l
     19         # 메모리에 샘플을 추가
     20         def save_sample(self, state, reward, done):
     21             self.samples.append([state, reward, done])
     22     
     23         # 모든 에피소드에서 에이전트가 방문한 상태의 큐 함수를 업데이트
     24  ->      def update(self):
     25             G_t = 0
     26             visit_state = []
     27             for reward in reversed(self.samples):
     28                 state = str(reward[0])
     29                 if state not in visit_state:
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(25)update()
    -> G_t = 0
    (Pdb) 
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(26)update()
    -> visit_state = []
    (Pdb) 
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(27)update()
    -> for reward in reversed(self.samples):
    (Pdb) p visit_state
    []
    (Pdb) l
     22     
     23         # 모든 에피소드에서 에이전트가 방문한 상태의 큐 함수를 업데이트
     24         def update(self):
     25             G_t = 0
     26             visit_state = []
     27  ->          for reward in reversed(self.samples):
     28                 state = str(reward[0])
     29                 if state not in visit_state:
     30                     visit_state.append(state)
     31                     G_t = self.discount_factor * (reward[1] + G_t)
     32                     value = self.value_table[state]
    (Pdb) p self.samples
    [[[0, 1], 0, False], [[0, 0], 0, False], [[0, 1], 0, False], [[0, 0], 0, False], [[1, 0], 0, False], [[1, 1], 0, False], [[1, 0], 0, False], [[2, 0], 0, False], [[3, 0], 0, False], [[3, 1], 0, False], [[3, 2], 0, False], [[2, 2], 100, True]]
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(28)update()
    -> state = str(reward[0])
    (Pdb) p state
    [0, 0]
    (Pdb) n
    > /home/now0930/tensorflow/reinforceLearing/reinforcement-learning-kr-master/1-grid-world/3-monte-carlo/mc_agentTest.py(29)update()
    -> if state not in visit_state:
    (Pdb) p state
    '[2, 2]'
    (Pdb) p self.value_table
    defaultdict(<class 'float'>, {'[0, 0]': 12.843038729477103, '[0, 1]': 3.6010347623049843, '[1, 0]': 45.739579013968516, '[0, 2]': 0.38742048900000015, '[1, 1]': 4.629255057991681, '[2, 0]': 50.83890751506551, '[2, 2]': 89.2397438477723, '[3, 1]': 66.85889829029591, '[2, 1]': -19.99607765407679, '[3, 0]': 60.06357418599044, '[4, 0]': 8.024660160565901, '[3, 2]': 80.26033687696258, '[4, 1]': 5.96080478456966, '[3, 3]': 6.970546731858152, '[4, 2]': 6.029123110357315, '[3, 4]': 1.3056390000000002, '[2, 4]': 0.47829690000000014, '[4, 4]': 0.5904900000000002, '[2, 3]': 0.81, '[4, 3]': 0.5314410000000002, '[1, 3]': 0.0, '[1, 4]': 0.0, '[1, 2]': 0.0, '[0, 3]': 0.0})