자료를 공개한 저자 오렐리앙 제롱과 강의자료를 지원한 한빛아카데미에게 진심어린 감사를 전합니다.
에이전트
환경
관측
행동
보상
import gym
env = gym.make("CartPole-v1")
obs = env.reset() # 초기화된 환경 관측치 할당
CartPole-v1의 경우 reset()
메서드는 아래 모양의 초기화된 환경 관측값을 반환함.
array([-0.01258566, -0.00156614, 0.04207708, -0.00180545])
gym
이 제공하는 전체 시뮬레이션 리스트는 아래와 같이 확인:
gym.envs.registry.all()
render()
메서드 실행render()
메서드에서 반환된 이미지를 넘파이 배열로 받으려면 mode="rgb_array"
설정
img = env.render(mode="rgb_array")
img
에는 아래 모양의 컬러 사진이 저장됨.
(800, 1200, 3) # img.shape
가능한 행동은 다음과 같이 확인
env.action_space
CartPole-v1의 경우 다음과 0과 1 두 종류의 행동이 가능:
Discrete(2)
예제: 초기 상태에서 막대가 오른쪽으로 쓰러지고 있기에 오른쪽으로 가속하는 행동 한 번 실행
action = 1 # 오른쪽으로 (살짝) 가속
obs, reward, done, info = env.step(action)
행동 결과는 아래와 같음:
# obs: 한 번 오른쪽으로 가속한 후 관측값
array([-0.01261699, 0.19292789, 0.04204097, -0.28092127])
# reward: CartPole의 경우 보상은 항상 1
1.0
# done: 게임 종료 여부, 즉, 막대가 쓰러졌는지 여부
False
# info: 에이전트 관련 기타 정보. CartPole의 경우 없음.
{}
정책: 막대가 왼쪽으로 쓰러지면 왼쪽으로 가속, 오른쪽으로 쓰러지면 오른쪽으로 가속
def basic_policy(obs):
angle = obs[2]
return 0 if angle < 0 else 1
위 정책을 이용하여 에피소드 500번 시뮬레이션 실행.
여기서 누적되는 보상은 게임이 종료될 때까지의 행동 실행횟수를 가리킴.
totals = []
for episode in range(500):
episode_rewards = 0
obs = env.reset()
for step in range(200):
action = basic_policy(obs)
obs, reward, done, info = env.step(action)
episode_rewards += reward
if done:
break
totals.append(episode_rewards)
평균적으로 41.7회, 최대 68회 행동 실행함.
# np.mean(totals), np.std(totals), np.min(totals), np.max(totals)
(41.718, 8.858356280936096, 24.0, 68.0)
n_inputs = 4 # 관측값 모양(env.observation_space.shape[0])
model = keras.models.Sequential([
keras.layers.Dense(5, activation="elu", input_shape=[n_inputs]),
keras.layers.Dense(1, activation="sigmoid"),
])
loss
)를 기준으로 경사하강법(optimizer
) 적용model.compile(loss="mse",
optimizer="adam")
model.fit()
model.compile(loss="sparse_categorical_crossentropy",
optimizer="nadam",
metrics=["accuracy"])
model.fit()
loss
를 예를 들어 추정된 확률과 타깃 확률 사이의 크로스 엔트로피로 정할 수 있음.예제
따라서 첫째 행동의 대가는 다음과 같이 계산됨:
$$ 10 + \gamma \cdot 0 + \gamma^2 \cdot (-50) = -22 $$
대가 정규화: 대가들의 평균과 표준편차를 계산한 후 표준점수로 변환하기
$$ Z = \frac{X - \mu(X)}{\sigma(X)} $$
y_target
): 0 또는 1loss
)이 최소, 즉, 정책 모델이 가능한 좋은 확률로 행동을 추천하는 방향으로 유도함.
grads
): 실행된 스텝에서 각 파라미터에 대한 손실함수의 그레이디언트def play_one_step(env, obs, model, loss_fn):
with tf.GradientTape() as tape:
left_proba = model(obs[np.newaxis])
action = (tf.random.uniform([1, 1]) > left_proba)
y_target = tf.constant([[1.]]) - tf.cast(action, tf.float32)
loss = tf.reduce_mean(loss_fn(y_target, left_proba))
grads = tape.gradient(loss, model.trainable_variables)
obs, reward, done, info = env.step(int(action[0, 0].numpy()))
return obs, reward, done, grads
def play_multiple_episodes(env, n_episodes, n_max_steps, model, loss_fn):
all_rewards = []
all_grads = []
for episode in range(n_episodes):
current_rewards = []
current_grads = []
obs = env.reset()
for step in range(n_max_steps):
obs, reward, done, grads = play_one_step(env, obs, model, loss_fn)
current_rewards.append(reward)
current_grads.append(grads)
if done:
break
all_rewards.append(current_rewards)
all_grads.append(current_grads)
return all_rewards, all_grads
discount_rewards()
함수discount_and_normalize_rewards()
함수def discount_rewards(rewards, discount_rate):
discounted = np.array(rewards)
for step in range(len(rewards) - 2, -1, -1):
discounted[step] += discounted[step + 1] * discount_rate
return discounted
def discount_and_normalize_rewards(all_rewards, discount_rate):
all_discounted_rewards = [discount_rewards(rewards, discount_rate)
for rewards in all_rewards]
flat_rewards = np.concatenate(all_discounted_rewards)
reward_mean = flat_rewards.mean()
reward_std = flat_rewards.std()
return [(discounted_rewards - reward_mean) / reward_std
for discounted_rewards in all_discounted_rewards]
discount_rewards()
함수discount_and_normalize_rewards()
함수n_iterations = 150 # PG 적용을 150번 실행
n_episodes_per_update = 10 # 10번 에피소드마다 PG 적용
n_max_steps = 200 # 매 에피소드마다 최대 200번 행동
discount_rate = 0.95 # 할인계수
optimizer = keras.optimizers.Adam(lr=0.01)
loss_fn = keras.losses.binary_crossentropy # 손실함수
model = keras.models.Sequential([
keras.layers.Dense(5, activation="elu", input_shape=[4]),
keras.layers.Dense(1, activation="sigmoid"),
])
env = gym.make("CartPole-v1")
# PG 적용: 150회
for iteration in range(n_iterations):
# 에피소드 10번 실행
all_rewards, all_grads = play_multiple_episodes(
env, n_episodes_per_update, n_max_steps, model, loss_fn)
all_final_rewards = discount_and_normalize_rewards(all_rewards,
discount_rate)
all_mean_grads = []
# 스텝별 그레이디언트에 스텝별 행동이익을 곱한 후 파라미터별 그레이디언트 평균값 계산
for var_index in range(len(model.trainable_variables)):
mean_grads = tf.reduce_mean(
[final_reward * all_grads[episode_index][step][var_index]
for episode_index, final_rewards in enumerate(all_final_rewards)
for step, final_reward in enumerate(final_rewards)], axis=0)
all_mean_grads.append(mean_grads)
# 계산된 파라미터별 그레이디언트를 기존의 파라미터에 더하는 방식으로 경사하강법 적용
optimizer.apply_gradients(zip(all_mean_grads, model.trainable_variables))