티스토리 뷰

Study/AI

[RL] Windows 10에서 OpenAI Gym & Baselines 설치하기

생각많은 소심남 2019. 4. 30. 07:08

 아마 강화학습용 시뮬레이터와 알고리즘을 검증하는데 가장 많이 사용하는 것이 OpenAI GymOpenAI Baselines 일 것이다. Gym은 원래의 뜻인 체육관이란 뜻에도 담겨있는 것처럼, 강화학습 알고리즘을 테스트해볼 수 있는 다양한 simulation environment가 포함되어 있다. 물론 강화학습을 하는 사람이라면 한번쯤 들어봤을 듯한 Atari나 robot Simulator인 MuJoCo도 들어있다. Baselines는 이와 반대로 Gym 환경 base에서 테스트해볼 수 있는 다양한 알고리즘들이 포함되어 있는 패키지이다. 제일 기초적인 DQN부터 시작해서 조금 발전된 형태인 DDPG, TRPO, PPO같은 알고리즘들도 구현되어 있다. 그래서 본인이 만약 알고리즘에 치중해서 개발하고 있고, 성능을 검증하고 싶으면, OpenAI Gym에 연결해서 기존 알고리즘의 성능과 비교해볼 수 있고, 또는 본인이 만든 환경이 강화학습에 맞는 환경인지 알고싶으면 OpenAI Baselines에 연결해서 성능을 얻어볼 수 있을 것이다. 

 그런데 아마 많은 사람들이 가장 맞부딪칠 문제 중 하나가 바로 OS일 것이다. 보통 이런 인공지능 개발에 많이 사용되는 framework가 Linux/MacOS 기반으로 되어 있고, Windows는 공식적으로 지원하지 않아, 처음 이를 접하는 사람은 고민을 하게 될 것이다. 과연 Linux를 설치할 것인가, 아니면 이를 개발하기 위해서 맥북을 살 것인가? 다행인 것은 요새 필요에 따라서 Windows 용으로 많이 포팅을 해놨다는 것이다. 물론 공식적인 루트가 아니라서 지원을 받기는 어렵지만 그래도 어느정도 동작하고, 이에 대한 글을 좀 써보고자 한다.

 우선 기본적으로 설치할 툴이 몇가지 있다. 바로 Anaconda라고 하는 Python/R 개발을 위한 가상 환경( 또는 이런 복잡한 것이 싫으면 Miniconda 라는 Minimal Version을 설치해도 좋다.)이 필요하고, 앞으로 작업하는 모든 것들이 이 환경 상에서 설치되고 동작할 것이다. 그리고 추가로 필요한 것이 Visual C++ Compiler이다. Data Science에서 필요한 패키지 대부분이 C++로 구현되어 있어, 패키지 빌드를 위해서는 해당 컴파일러 설치가 필요하다. 예전에는 Visual C++ Compiler가 유료여서 MinGW같은 대안을 찾기도 했었는데, 요새는 Visual Studio Community version(무료)을 설치하면, 알아서 설치해준다.

 우선 설치가 되었으면 Anaconda Command Prompt를 열고 python 3.6.1용 환경을 만들어준다.

conda create -n py3.6 python=3.6.1

(참고1. py3.6은 명시적으로 지정한 이름으로 원하는 이름으로 변경해서 사용해도 된다)
(참고2. python3.6으로 진행할 경우, 추후 tensorflow 설치시 protobuf관련 오류가 나온다. 이때문에 python은 3.6.1 이후 버전을 사용할 것을 권한다.)

그럼 아마 몇가지 패키지를 설치할 것이고, 해당 환경으로 작업하기 위해서는 activate를 해줘야 한다.

conda activate py3.6

 OpenAI에서 배포하는 소프트웨어 패키지는 git으로 관리되기 때문에 이를 받기 위한 git을 설치해준다.

conda install git

 그리고 혹시 video frame을 녹화하고 싶은 사람이 있다면, 추가적으로 ffmpeg 코덱도 같이 설치해줘야 한다.

conda install -c conda-forge ffmpeg

Gym을 설치하기에 앞서 아마 많은 사람들이 제일 많이 부딪친 부분 중 하나가 Windows에서 Atari Simulator를 사용할 수 있느냐 여부인데, 어떤 사람이 Windows에서도 돌릴 수 있게끔 Atari wrapper를 만들었다. 해당 패키지를 pip를 통해 설치한다.

pip install git+https://github.com/Kojoley/atari-py.git

 참고) 혹시 설치하다가 오류가 발생하는 경우 다음 을 참고하기 바란다.

이제 Gym을 설치해본다.

git clone https://github.com/openai/gym.git
cd gym
pip install -e .

 여기까지 무사히 넘어갔으면, 정상적으로 설치된 것이다. 이제 잘 설치되었는지 확인해보려면, example에 있는 random agent를 돌려보면 된다.

python examples/agents/random_agent.py

 이렇게 하면 random_agent.py내에 설정되어 있는 CartPole-v0가 실행되는 모습을 확인할 수 있다. 물론 제목에 나와 있는 것처럼 random action을 하기 때문에 CartPole이 서 있지 않고 뒤죽박죽으로 움직이는 것을 알 수 있다.

CartPole-v0

참고로 앞에서 ffmpeg 코덱을 정상적으로 설치했다면 현재 실행 위치의 하위 tmp폴더내 동작에 대한 video가 record된 것을 볼 수 있다.

CartPole-demo

 추가로 atari 게임이 잘 수행되는지를 확인하려면, 가장 기본적인 Pong game을 돌려보면 된다. Andrej karpathy가 만든 Policy Gradient 검증용 코드를 돌려보면 매우 재미있는 결과가 나온다. (참고로 아래 코드는 기존 Python 2.7로 구현되어 있는 코드를 Python3로 조금 수정했다.)

import numpy as np
import pickle
import gym

# hyperparameters
H = 200 # number of hidden layer neurons
batch_size = 10 # every how many episodes to do a param update?
learning_rate = 1e-4
gamma = 0.99 # discount factor for reward
decay_rate = 0.99 # decay factor for RMSProp leaky sum of grad^2
resume = False # resume from previous checkpoint?
render = True

# model initialization
D = 80 * 80 # input dimensionality: 80x80 grid
if resume:
  model = pickle.load(open('save.p', 'rb'))
else:
  model = {}
  model['W1'] = np.random.randn(H,D) / np.sqrt(D) # "Xavier" initialization
  model['W2'] = np.random.randn(H) / np.sqrt(H)
  
grad_buffer = { k : np.zeros_like(v) for k,v in model.items() } # update buffers that add up gradients over a batch
rmsprop_cache = { k : np.zeros_like(v) for k,v in model.items() } # rmsprop memory

def sigmoid(x): 
  return 1.0 / (1.0 + np.exp(-x)) # sigmoid "squashing" function to interval [0,1]

def prepro(I):
  """ prepro 210x160x3 uint8 frame into 6400 (80x80) 1D float vector """
  I = I[35:195] # crop
  I = I[::2,::2,0] # downsample by factor of 2
  I[I == 144] = 0 # erase background (background type 1)
  I[I == 109] = 0 # erase background (background type 2)
  I[I != 0] = 1 # everything else (paddles, ball) just set to 1
  return I.astype(np.float).ravel()

def discount_rewards(r):
  """ take 1D float array of rewards and compute discounted reward """
  discounted_r = np.zeros_like(r)
  running_add = 0
  for t in reversed(range(0, r.size)):
    if r[t] != 0: running_add = 0 # reset the sum, since this was a game boundary (pong specific!)
    running_add = running_add * gamma + r[t]
    discounted_r[t] = running_add
  return discounted_r

def policy_forward(x):
  h = np.dot(model['W1'], x)
  h[h<0] = 0 # ReLU nonlinearity
  logp = np.dot(model['W2'], h)
  p = sigmoid(logp)
  return p, h # return probability of taking action 2, and hidden state

def policy_backward(eph, epdlogp):
  """ backward pass. (eph is array of intermediate hidden states) """
  dW2 = np.dot(eph.T, epdlogp).ravel()
  dh = np.outer(epdlogp, model['W2'])
  dh[eph <= 0] = 0 # backpro prelu
  dW1 = np.dot(dh.T, epx)
  return {'W1':dW1, 'W2':dW2}

env = gym.make("Pong-v0")
observation = env.reset()
prev_x = None # used in computing the difference frame
xs,hs,dlogps,drs = [],[],[],[]
running_reward = None
reward_sum = 0
episode_number = 0
while True:
  if render: env.render()

  # preprocess the observation, set input to network to be difference image
  cur_x = prepro(observation)
  x = cur_x - prev_x if prev_x is not None else np.zeros(D)
  prev_x = cur_x

  # forward the policy network and sample an action from the returned probability
  aprob, h = policy_forward(x)
  action = 2 if np.random.uniform() < aprob else 3 # roll the dice!

  # record various intermediates (needed later for backprop)
  xs.append(x) # observation
  hs.append(h) # hidden state
  y = 1 if action == 2 else 0 # a "fake label"
  dlogps.append(y - aprob) # grad that encourages the action that was taken to be taken (see http://cs231n.github.io/neural-networks-2/#losses if confused)

  # step the environment and get new measurements
  observation, reward, done, info = env.step(action)
  reward_sum += reward

  drs.append(reward) # record reward (has to be done after we call step() to get reward for previous action)

  if done: # an episode finished
    episode_number += 1

    # stack together all inputs, hidden states, action gradients, and rewards for this episode
    epx = np.vstack(xs)
    eph = np.vstack(hs)
    epdlogp = np.vstack(dlogps)
    epr = np.vstack(drs)
    xs,hs,dlogps,drs = [],[],[],[] # reset array memory

    # compute the discounted reward backwards through time
    discounted_epr = discount_rewards(epr)
    # standardize the rewards to be unit normal (helps control the gradient estimator variance)
    discounted_epr -= np.mean(discounted_epr)
    discounted_epr /= np.std(discounted_epr)

    epdlogp *= discounted_epr # modulate the gradient with advantage (PG magic happens right here.)
    grad = policy_backward(eph, epdlogp)
    for k in model: grad_buffer[k] += grad[k] # accumulate grad over batch

    # perform rmsprop parameter update every batch_size episodes
    if episode_number % batch_size == 0:
      for k,v in model.items():
        g = grad_buffer[k] # gradient
        rmsprop_cache[k] = decay_rate * rmsprop_cache[k] + (1 - decay_rate) * g**2
        model[k] += learning_rate * g / (np.sqrt(rmsprop_cache[k]) + 1e-5)
        grad_buffer[k] = np.zeros_like(v) # reset batch gradient buffer

    # boring book-keeping
    running_reward = reward_sum if running_reward is None else running_reward * 0.99 + reward_sum * 0.01
    print('resetting env. episode reward total was %f. running mean: %f' % (reward_sum, running_reward))
    if episode_number % 100 == 0: pickle.dump(model, open('save.p', 'wb'))
    reward_sum = 0
    observation = env.reset() # reset env
    prev_x = None

  if reward != 0: # Pong has either +1 or -1 reward exactly when game ends.
    print ('ep %d: game finished, reward: %f' % (episode_number, reward) + ('' if reward == -1 else ' !!!!!!!!'))

Pong-v0

이제 OpenAI baselines를 설치해야 하는데, 일단 여기서 단순히 OpenAI baselines를 설치하는 것보다 조금 customize된 버전의 stable baselines를 설치하는게 좋을거 같아 이를 시도해보려고 한다.

 우선 Baselines에 포함되어 있는 알고리즘 중 몇몇 알고리즘은 MultiAgent를 학습시키는 것도 있는데, 이경우MultiProcessing에 필요한 툴인 MPI (Linux에서는 OpenMPI로 언급되는데, Windows에서는 MS에서 만들어서 배포한다)를 설치해야 한다. 그 후 Anaconda Environment에서 zlib 패키지를 설치한다.

conda install zlib

여기서부터는 이제 pip로 패키지 설치를 해도 된다. 다음 명령어를 통해 stable baselines를 설치해보면 된다.

pip install stable-baselines
pip install tensorflow

 여기까지 하면 기본적으로 실험할 수 있는 환경이 갖춰졌다. 한번 다음 코드를 실행해보자.

import gym

from stable_baselines.common.policies import MlpPolicy
from stable_baselines.common.vec_env import DummyVecEnv
from stable_baselines import PPO2

env = gym.make('CartPole-v1')
env = DummyVecEnv([lambda: env])  # The algorithms require a vectorized environment to run

model = PPO2(MlpPolicy, env, verbose=1)
model.learn(total_timesteps=10000)

obs = env.reset()
for i in range(1000):
    action, _states = model.predict(obs)
    obs, rewards, dones, info = env.step(action)
    env.render()
env.close()

잠깐 코드 설명을 하자면, 강화학습에서는 기본적으로 특정 Policy을 근사하기 위한 과정이 필요하다. 위의 코드는 기본적으로 간단한 64개 노드와 2개의 Layer로 구성된 Multi Layer Perceptron(MLP)를 이용해서 Policy를 근사한다. 이때 agent를 학습시키는 알고리즘으로는 Proximal Policy Optimization (PPO)를 사용하는데, 추가로 몇가지 수정된 버전인 PPO2를 사용했다. test하는 환경은 이전과 동일한 CartPole 환경이다.

댓글