|
1 | | -import gym |
2 | | -import time |
3 | | -import threading |
4 | | -import numpy as np |
| 1 | +# 게임 구현과 DQN 모델을 이용해 게임을 실행하고 학습을 진행합니다. |
5 | 2 | import tensorflow as tf |
6 | | -from scipy.misc import imresize |
| 3 | +import numpy as np |
| 4 | +import random |
| 5 | +import time |
7 | 6 |
|
8 | | -from brain import Brain |
| 7 | +from game import Game |
| 8 | +from model import DQN |
9 | 9 |
|
10 | 10 |
|
11 | | -# 이미지 사이즈를 줄이고, 흑백 사진으로 만듭니다. |
12 | | -def preprocess(screen, width, height): |
13 | | - # 완전한 그레이스케일이 아닌 적당한 수준의 흑백으로 만듭니다. |
14 | | - gray = screen.astype('float32').mean(2) |
15 | | - # 이미지를 리사이즈하고 수치를 보정합니다. 0~1.0 사이의 값으로 만듭니다. |
16 | | - processed = imresize(gray, (width, height)).astype('float32') * (1. / 255) |
| 11 | +tf.app.flags.DEFINE_boolean("train", False, "학습모드. 게임을 화면에 보여주지 않습니다.") |
| 12 | +FLAGS = tf.app.flags.FLAGS |
17 | 13 |
|
18 | | - return processed |
| 14 | +# 최대 학습 횟수 |
| 15 | +MAX_EPISODE = 10000 |
| 16 | +# 1000번의 학습마다 한 번씩 타겟 네트웍을 업데이트합니다. |
| 17 | +TARGET_UPDATE_INTERVAL = 1000 |
| 18 | +# 4 프레임마다 한 번씩 학습합니다. |
| 19 | +TRAIN_INTERVAL = 4 |
| 20 | +# 학습 데이터를 어느정도 쌓은 후, 일정 시간 이후에 학습을 시작하도록 합니다. |
| 21 | +OBSERVE = 100 |
19 | 22 |
|
| 23 | +# action: 0: 좌, 1: 유지, 2: 우 |
| 24 | +NUN_ACTION = 3 |
| 25 | +SCREEN_WIDTH = 6 |
| 26 | +SCREEN_HEIGHT = 10 |
20 | 27 |
|
21 | | -def trainer(brain): |
22 | | - # 컴퓨터의 성능에 따라 학습 빈도를 적절히 제어 할 필요가 있습니다. |
23 | | - # 원래는 게임 몇 프레임마다 학습을 한 번씩 하게 해야 하지만,, |
24 | | - # 일반 컴퓨터/CPU로 학습을 시키는 경우 학습 속도가 느려 학습과정을 보기 어려워 |
25 | | - # 적당한 미니배치 사이즈로 계속 학습을 시키도록 했습니다. |
26 | | - # 컴퓨터가 빠르거나 여러개의 GPU를 사용할 수 있다면 |
27 | | - # 게임 안에 학습 과정을 넣거나, 게임을 여러개를 한 번에 돌리는 방법을 써봐도 좋겠습니다. |
28 | | - steps = 1 |
29 | | - while True: |
30 | | - if len(brain.memory) > 5000: |
31 | | - if steps == 1: |
32 | | - print('Trainer: 학습 시작!') |
33 | 28 |
|
34 | | - brain.train() |
35 | | - steps += 1 |
36 | | - else: |
37 | | - time.sleep(1) |
| 29 | +def train(): |
| 30 | + print('뇌세포 깨우는 중..') |
| 31 | + sess = tf.Session() |
38 | 32 |
|
39 | | - if steps % 100 == 0: |
40 | | - print('Trainer: 타겟 네트웍 업데이트. Step: {}'.format(steps)) |
41 | | - brain.update_target_network() |
| 33 | + game = Game(SCREEN_WIDTH, SCREEN_HEIGHT, show_game=False) |
| 34 | + brain = DQN(sess, SCREEN_WIDTH, SCREEN_HEIGHT, NUN_ACTION) |
42 | 35 |
|
| 36 | + rewards = tf.placeholder(tf.float32, [None]) |
| 37 | + tf.summary.scalar('avg.reward/ep.', tf.reduce_mean(rewards)) |
43 | 38 |
|
44 | | -MAX_EPISODE = 9999999 |
45 | | -SCREEN_WIDTH = 84 |
46 | | -SCREEN_HEIGHT = 84 |
47 | | -ENV_NAME = 'Breakout-v0' |
48 | | -# ENV_NAME = 'Freeway-v0' |
49 | | -# ENV_NAME = 'Pong-v0' |
| 39 | + saver = tf.train.Saver() |
| 40 | + sess.run(tf.global_variables_initializer()) |
50 | 41 |
|
51 | | -env = gym.make(ENV_NAME) |
| 42 | + writer = tf.summary.FileWriter('logs', sess.graph) |
| 43 | + summary_merged = tf.summary.merge_all() |
52 | 44 |
|
53 | | -sess = tf.Session() |
| 45 | + # 타겟 네트웍을 초기화합니다. |
| 46 | + brain.update_target_network() |
54 | 47 |
|
55 | | -print('뇌세포 깨우는 중..') |
56 | | -brain = Brain(session=sess, |
57 | | - width=SCREEN_WIDTH, height=SCREEN_HEIGHT, |
58 | | - n_action=env.action_space.n) |
| 48 | + # 다음에 취할 액션을 DQN 을 이용해 결정할 시기를 결정합니다. |
| 49 | + epsilon = 1.0 |
| 50 | + # 프레임 횟수 |
| 51 | + time_step = 0 |
| 52 | + total_reward_list = [] |
59 | 53 |
|
60 | | -sess.run(tf.global_variables_initializer()) |
| 54 | + # 게임을 시작합니다. |
| 55 | + for episode in range(MAX_EPISODE): |
| 56 | + terminal = False |
| 57 | + total_reward = 0 |
61 | 58 |
|
62 | | -# 타겟 네트웍을 초기화합니다. |
63 | | -brain.update_target_network() |
| 59 | + # 게임을 초기화하고 현재 상태를 가져옵니다. |
| 60 | + # 상태는 screen_width x screen_height 크기의 화면 구성입니다. |
| 61 | + state = game.reset() |
| 62 | + brain.init_state(state) |
64 | 63 |
|
65 | | -# 학습을 시키는 trainer 함수를 쓰레드로 돌립니다. 게임과 학습을 동시에 진행합니다. |
66 | | -# 컴퓨터가 빠르거나 반대로 너무 느리면 쓰레드 대신 게임 반복문 안에서 순차적으로 학습시키는 것이 좋을 수 있습니다. |
67 | | -train_thread = threading.Thread(target=trainer, args=[brain]) |
68 | | -train_thread.start() |
| 64 | + while not terminal: |
| 65 | + # 입실론이 랜덤값보다 작은 경우에는 랜덤한 액션을 선택하고 |
| 66 | + # 그 이상일 경우에는 DQN을 이용해 액션을 선택합니다. |
| 67 | + # 초반엔 학습이 적게 되어 있기 때문입니다. |
| 68 | + # 초반에는 거의 대부분 랜덤값을 사용하다가 점점 줄어들어 |
| 69 | + # 나중에는 거의 사용하지 않게됩니다. |
| 70 | + if np.random.rand() < epsilon: |
| 71 | + action = random.randrange(NUN_ACTION) |
| 72 | + else: |
| 73 | + action = brain.get_action() |
69 | 74 |
|
70 | | -# 게임을 시작합니다. |
71 | | -for episode in range(MAX_EPISODE): |
72 | | - terminal = False |
73 | | - total_reward = 0. |
74 | | - # 다음에 취할 액션을 DQN 을 이용해 결정할 시기를 결정합니다. |
75 | | - # 초반에는 액션을 랜덤값을 이용합니다. 아직 학습이 되지 않았기 때문입니다. |
76 | | - # refer: https://github.com/hunkim/ReinforcementZeroToAll/ |
77 | | - epsilon = 1. / ((episode / 100) + 1) |
78 | | - |
79 | | - # 게임 상태를 초기화합니다. |
80 | | - state = env.reset() |
81 | | - state = preprocess(state, SCREEN_WIDTH, SCREEN_HEIGHT) |
82 | | - brain.init_state(state) |
83 | | - |
84 | | - while not terminal: |
85 | | - # 학습을 한 번도 하지 않았을 때와 입실론이 랜덤값보다 작은 경우에는 랜덤한 액션을 선택합니다. |
86 | | - # 위의 수식에 의하면 랜덤값을 사용하는 빈도가 점점 줄어들다가 |
87 | | - # 1000번 정도의 에피소드가 지나면 랜덤값을 거의 사용하지 않게됩니다. |
88 | | - if episode < 10 or np.random.rand() < epsilon: |
89 | | - action = env.action_space.sample() |
90 | | - else: |
| 75 | + # 일정 시간이 지난 뒤 부터 입실론 값을 줄입니다. |
| 76 | + # 초반에는 학습이 전혀 안되어 있기 때문입니다. |
| 77 | + if episode > OBSERVE: |
| 78 | + epsilon -= 1 / 1000 |
| 79 | + |
| 80 | + # 결정한 액션을 이용해 게임을 진행하고, 보상과 게임의 종료 여부를 받아옵니다. |
| 81 | + state, reward, terminal = game.step(action) |
| 82 | + total_reward += reward |
| 83 | + |
| 84 | + # 현재 상태를 Brain에 기억시킵니다. |
| 85 | + # 기억한 상태를 이용해 학습하고, 다음 상태에서 취할 행동을 결정합니다. |
| 86 | + brain.remember(state, action, reward, terminal) |
| 87 | + |
| 88 | + if time_step > OBSERVE and time_step % TRAIN_INTERVAL == 0: |
| 89 | + # DQN 으로 학습을 진행합니다. |
| 90 | + brain.train() |
| 91 | + |
| 92 | + if time_step % TARGET_UPDATE_INTERVAL == 0: |
| 93 | + # 타겟 네트웍을 업데이트 해 줍니다. |
| 94 | + brain.update_target_network() |
| 95 | + |
| 96 | + time_step += 1 |
| 97 | + |
| 98 | + print('게임횟수: %d 점수: %d' % (episode + 1, total_reward)) |
| 99 | + |
| 100 | + total_reward_list.append(total_reward) |
| 101 | + |
| 102 | + if episode % 10 == 0: |
| 103 | + summary = sess.run(summary_merged, feed_dict={rewards: total_reward_list}) |
| 104 | + writer.add_summary(summary, time_step) |
| 105 | + total_reward_list = [] |
| 106 | + |
| 107 | + if episode % 100 == 0: |
| 108 | + saver.save(sess, 'model/dqn.ckpt', global_step=time_step) |
| 109 | + |
| 110 | + |
| 111 | +def replay(): |
| 112 | + print('뇌세포 깨우는 중..') |
| 113 | + sess = tf.Session() |
| 114 | + |
| 115 | + game = Game(SCREEN_WIDTH, SCREEN_HEIGHT, show_game=True) |
| 116 | + brain = DQN(sess, SCREEN_WIDTH, SCREEN_HEIGHT, NUN_ACTION) |
| 117 | + |
| 118 | + saver = tf.train.Saver() |
| 119 | + ckpt = tf.train.get_checkpoint_state('model') |
| 120 | + saver.restore(sess, ckpt.model_checkpoint_path) |
| 121 | + |
| 122 | + # 게임을 시작합니다. |
| 123 | + for episode in range(MAX_EPISODE): |
| 124 | + terminal = False |
| 125 | + total_reward = 0 |
| 126 | + |
| 127 | + state = game.reset() |
| 128 | + brain.init_state(state) |
| 129 | + |
| 130 | + while not terminal: |
91 | 131 | action = brain.get_action() |
92 | 132 |
|
93 | | - state, reward, terminal, info = env.step(action) |
94 | | - total_reward += reward |
| 133 | + # 결정한 액션을 이용해 게임을 진행하고, 보상과 게임의 종료 여부를 받아옵니다. |
| 134 | + state, reward, terminal = game.step(action) |
| 135 | + total_reward += reward |
| 136 | + |
| 137 | + brain.remember(state, action, reward, terminal) |
| 138 | + |
| 139 | + # 게임 진행을 인간이 인지할 수 있는 속도로^^; 보여줍니다. |
| 140 | + time.sleep(0.3) |
95 | 141 |
|
96 | | - if terminal: |
97 | | - reward = -1 |
| 142 | + print('게임횟수: %d 점수: %d' % (episode + 1, total_reward)) |
98 | 143 |
|
99 | | - # 현재 상태를 Brain에 기억시킵니다. |
100 | | - # 기억한 상태를 이용해 학습하고, 다음 상태에서 취할 행동을 결정합니다. |
101 | | - state = preprocess(state, SCREEN_WIDTH, SCREEN_HEIGHT) |
102 | | - brain.remember(state, action, reward, terminal) |
103 | 144 |
|
104 | | - # 화면을 그립니다. |
105 | | - env.render() |
| 145 | +def main(_): |
| 146 | + if FLAGS.train: |
| 147 | + train() |
| 148 | + else: |
| 149 | + replay() |
106 | 150 |
|
107 | | - print('게임횟수: {} 점수: {}'.format(episode + 1, total_reward)) |
108 | 151 |
|
109 | | - # 에피소드 10회가 지나면 매 회 3번의 샘플링 학습을 시킵니다. |
110 | | - # 학습을 쓰레드로 동시에 시키지 않고 에피소드가 끝난 후 시키려면 이 코드를 사용하세요. |
111 | | - # if episode > 9: |
112 | | - # for count in range(3): |
113 | | - # brain.train() |
114 | | - # |
115 | | - # # 타겟 네트웍을 업데이트 해 줍니다. |
116 | | - # brain.update_target_network() |
| 152 | +if __name__ == '__main__': |
| 153 | + tf.app.run() |
0 commit comments