tl;dr
- gym>=0.10.6이면 상관x
- multiprocessing으로 학습을 진행시키는 경우 주의
impala랑 popart-impala를 구현하다가 소스 코드엔 문제가 없는것 같은데,
도저히 Pong평균스코어가 18점을 못넘기길래 gym을 업데이트 해봤더니
잘되길래 gym 버그인줄 알았는데 결국 내 코드가 문제였다(gym도 쪼끔 잘못했다.)
atari 옵션 설명
gym/envs/__init__.py
우선 gym에서 사용되는 atari게임들의 옵션을 두 부분으로 나눠서
설명할 수 있는데,
- Deterministic: 4프레임 마다 건너뜀(frameskip), 4 프레임동안 같은 action 반복
- NoFrameskip: frameskip 없음
-
없음: {2,3,4}중에 uniform하게 선택해서 건너뜀
- v0: 25% 확률로 주어진 행동 무시하고 이전 행동 반복
- v4: 무시안함
그래서 PongDeterministic-v4라고 하면 4프레임 마다 건너뛰고, 행동을 무시하지 않는 환경을 얻을 수 있다.
문제
우선 사용하던 gym이 0.10.5였고 최신 버전이 현재 0.14.0인데,
같은 PongDeterministic-v4라도 결과가 다르길래
어느 버전부터 이렇게 잘못되나 확인해 봤다.
알고리즘은 popart-impala, worker수는 16
시간별로 전체 worker의 값을 평균냈다.

0.10.6부턴 제대로 작동하길래
각이 나온거 같아서 gym github의 소스코드를 빼봤더니
gym github 0.10.5/0.10.6 비교
atari관련 된건
gym/envs/atari/atari_env.py
밖에 안보였고 해당부분은 0.14.0의 코드에선 이렇게 되어있다.
utils.EzPickle.__init__(
self,
game,
mode,
difficulty,
obs_type,
frameskip,
repeat_action_probability)
0.10.5 이전에선 frameskip, repeat_action_probability가 없고 obs_type까지만 있다.
저 두 argument를 넘기느냐 안넘기느냐 차이인것 같아,
두줄을 지운거랑 안지운거랑 돌려봤다.
- 위: 한 에피소드당 스텝 수 평균
- 아래: 에피소드 Reward평균
아무래도 이게 확실히 원인인것 같다.
진짜 원인
impala를 설계할때 multi-thread가 아니라 multi-processing으로 설계했는데
fork된 뒤로 독립적인 프로세스란 사실을 까먹은게 문제였다.
class Agent(multiprocessing.Process):
def __init__(self):
env = gym.make("PongNoFrameskip-v4")
print("a: ",env.frameskip)
def run(self):
print("b: ",env.frameskip)
agent = Agent()
agent.start()
이렇게 하면,
a: 4
b: 4
가 나올 것 같지만, 0.10.5 이전버전에선
a: 4
b: (2,5) <-이건 atari기본 설정
이렇게 나와버린다.
multi-process가 시작되면서 fork했던 env가 아니라, 새로 오브젝트를 생성하는 문제인것 같다.
0.10.5까지는 init할때 frameskip, repeat_action_probability를 넘겨주지 않았기 때문에, 그게 없는 채로 오브젝트가 생성된 것이고,
0.10.6부터는 넘겨주기 때문에 잘 작동하는 것 같다.
근데 관련해서 생각하다 보니까 init에서 env생성하면 다른 process가 생성될때 같이 fork된단 얘긴데,
그러면 메모리 낭비인것 같아서 run함수 안에서 그냥 생성하는 코드로 바꿨더니 초기 실행이 더 빨라졌다.
예상치 못한 수확도 건져서 이득봤다.
reference
- https://github.com/openai/gym/issues/1496
- https://github.com/openai/gym/compare/c8f8149..5cbc105
- https://github.com/openai/gym/issues/349
- https://github.com/openai/gym/blob/master/gym/envs/__init__.py