OpenAI에서 Reinforcement Learning을 쉽게 연구할 수 있는 환경을 제공하고 있는데 그중에 하나를 OpenAI Gym 이라고 합니다. 여러가지 게임환경과 환경에 대한 API를 제공하여 Reinforcement Learning을 위해 매번 게임을 코딩할 필요 없고 제공되는 환경에서 RL의 알고리즘만 확인을 하면 되기에 편합니다.
그런데 R에서 바로 gym을 사용하는건 불가능하고 Python에서 웹서버를 구동하여 REST API로 연결하는 방식으로만 가능합니다. 그래서 Python이 설치 되어 있는 것을 가정합니다.
$ pip install gym
$ git clone https://github.com/openai/gym-http-api
$ cd gym-http-api
$ pip install -r requirements.txt
install.packages('gym')
gym_http_server.py
를 실행합니다.$ python gym_http_server.py
library(gym)
remote_base <- "http://127.0.0.1:5000"
client <- create_GymClient(remote_base)
print(client)
## <GymClient: http://127.0.0.1:5000>
env_id <- "CartPole-v0"
instance_id <- env_create(client, env_id)
for (iEpisode in 1:10) {
ob <- env_reset(client, instance_id)
for (i in 1:100) {
action <- env_action_space_sample(client, instance_id)
results <- env_step(client, instance_id, action, render = TRUE)
if (results[["done"]])
break
}
}
env_close(client, instance_id)
그럼 아래와 같은 게임이 실행되면서 막대기의 중심을 잃으면서 게임이 끝나는 걸 볼 수 있습니다.
중심을 잃는 순간 게임 종료
gym
라이브러리 설명env_id <- "CartPole-v0"
instance_id <- env_create(client, env_id)
for (iEpisode in 1:10) {
ob <- env_reset(client, instance_id)
for (i in 1:100) {
...
}
}
env_reset(client, instance_id)
는 해당 게임을 초기화 시켜주고 초기 State(Observation)값을 제공합니다.action <- env_action_space_sample(client, instance_id)
env_action_space_sample(client, instance_id)
는 해당 게임에서 가능한 Action들중 랜덤으로 선택해 돌려줍니다. CartPole-v0은 2개의 action (0 - Move Left or 1 - Move Right)을 가지고 있습니다.env_action_space_info
함수로 Action에 대한 정보 화인이 가능합니다.
n
액션의 갯수name
액션의 타입을 말해줍니다. 예제에서는 Discrete이고 좌표나 여러 타입이 있습니다.env_action_space_info(client, instance_id)
## $n
## [1] 2
##
## $name
## [1] "Discrete"
env_step
함수 설명results <- env_step(client, instance_id, action, render = TRUE)
results
## $observation
## $observation[[1]]
## [1] -0.03906595
##
## $observation[[2]]
## [1] -0.2278388
##
## $observation[[3]]
## [1] -0.02720497
##
## $observation[[4]]
## [1] 0.2347456
##
##
## $reward
## [1] 1
##
## $done
## [1] FALSE
##
## $info
## named list()
env_step(client, instance_id, action, render=TRUE)
는 action을 실행하고 list를 돌려줍니다. list의 요소는 다음과 같습니다.
TRUE/FALSE
Baseline으로 사용하기 위해 먼저 위에서 봤던 랜덤 액션의 경우 기대값이 어떻게 되는지 대략적으로 다시 알아보겠습니다. 랜덤으로 게임을 돌릴 경우 거의 시작과 동시에 막대기가 기울면서 게임이 종료 되게 됩니다. 10번을 반복할 경우 아래와 같은 값이 나옵니다.
Random Action
env_id <- "CartPole-v0"
instance_id <- env_create(client, env_id)
total_reward = c()
for (iEpisode in 1:10) {
ob <- env_reset(client, instance_id)
one_episode_reward = 0
for (i in 1:100) {
action <- env_action_space_sample(client, instance_id)
results <- env_step(client, instance_id, action, render = FALSE)
one_episode_reward = one_episode_reward + results$reward
if (results$done) {
total_reward = append(total_reward, one_episode_reward)
}
}
}
env_close(client, instance_id)
mean(total_reward) # 평균 값
## [1] 16.59453
max(total_reward) # Max
## [1] 26
min(total_reward) # Min
## [1] 9
Hill Climbing의 경우 조금씩 값을 변화하면서 좋은 값이 나올때마다 세이브하는 방식입니다. 우슨 학습이 된 결과를 보면 아래와 같이 밸런스를 잘 유지하게 됩니다. 움직이지 않는 것처럼 보이지만 잘보면 왼쪽 오른쪽으로 빠르게 움직이고 있습니다. DQN과 같은 Q-Learning이나 PG로 이렇게 학습하려면 한참 학습을 해야 하기에 이런 간단한 경우는 오히려 Hill Climbing으로 간단하게 학습 시킬 수 있습니다.
library(gym)
setUp = function(url, name) {
client = create_GymClient(url)
instance_id = env_create(client, name)
return(list(client=client, instance_id = instance_id))
}
toMatrix = function(z, ncol) {
return(matrix(unlist(z), ncol=ncol))
}
chooseAction = function(observation, theta) {
p = toMatrix(observation, 4) %*% theta
if (p < 0) {
return(0)
} else {
return(1)
}
}
runEpisode = function(client, instance_id, theta, timesteps=10000, render=F, test=F) {
observation = env_reset(client, instance_id)
reward_sum = 0
if (test) {
timesteps = 10000000
}
for(i in 1:timesteps) {
action = chooseAction(observation, theta)
result = env_step(client, instance_id, action, render)
reward_sum = reward_sum + result$reward
observation = result$observation
if (result$done) {
break
}
}
return(reward_sum)
}
trainTheta = function(client, instance_id, scaling_factor = 0.1, target_reward=200, episodes=100, log=F) {
best_theta = NULL
best_reward = 0
theta = matrix(rnorm(4), 4, 1)
prepare_result = function(reward, theta, success) {
result = list()
result$theta = theta
result$reward = reward
result$success = success
return(result)
}
for(i in 1:episodes) {
newTheta = theta + matrix(rnorm(4), 4, 1) * scaling_factor
assertthat::are_equal(dim(theta), dim(newTheta))
reward = runEpisode(client, instance_id, newTheta)
if(log) {
print(paste0("REWARD: ", reward, " EPISODE: ", i, " BEST REWARD: ", best_reward))
}
if(reward > best_reward) {
best_reward = reward
theta = newTheta
if (best_reward > target_reward) {
return(prepare_result(best_reward, theta, T))
}
}
}
prepare_result(best_reward, theta, F)
}
remote_base <- "http://127.0.0.1:5000"
env_id <- "CartPole-v0"
env = setUp(remote_base, env_id)
client = env$client
instance_id = env$instance_id
result = trainTheta(client, instance_id, scaling_factor = 0.3, target_reward = 1500, episodes=1000, log=T)
if (result$success) {
runEpisode(client, instance_id, result$theta, render=T)
}
env_close(client, instance_id)