39. 복잡한 행동 학습 사례 배고픔 관리 에이전트 만들기, 상태와 행동 공간 설계

서론

강화 학습(Reinforcement Learning, RL)은 에이전트가 환경과 상호작용하며 최적의 행동을 학습하는 기계 학습의 한 분야입니다. 이번 글에서는
배고픔 관리 에이전트를 만들기 위한 사례를 소개합니다. 이 에이전트는 현실 세계에서의 복잡한 행동 문제를 해결하기 위해 강화 학습을
활용하며, 상태와 행동 공간을 효과적으로 설계하여 목표를 달성할 수 있도록 합니다.

배경 지식

배고픔 관리 시스템은 생물학적 요소와 외부 환경의 변화에 기반하여 에이전트가 식사를 관리하는 것을 목표로 합니다. 이러한 시스템은
특히 인간과 같은 복잡한 행동을 모델링하는 데 적합합니다. 행동 학습에서 고려해야 할 주요 요소는
상태(state)행동(action)입니다. 에이전트는 주어진 상태에서 어떤 행동을 선택하고,
그 행동이 선택된 후에 새로운 상태로 전이되는 환경에서 학습하게 됩니다.

상태와 행동 공간 설계

배고픔 관리 에이전트의 상태 공간은 에이전트의 현재 상태를 나타내며, 이는 다음과 같은 요소로 구성될 수 있습니다:

  • 배고픔 수준(hunger level): 0 (완전 포만) ~ 10 (매우 배고픔)
  • 에너지원(energy source): 최근에 섭취한 음식의 종류
  • 활동 수준(activity level): 현재 에이전트의 활동 정도

에이전트는 다음과 같은 행동을 취할 수 있습니다:

  • 음식 섭취 (consume food)
  • 운동 (exercise)
  • 휴식 (rest)

상태와 행동 공간을 정의함으로써 에이전트는 기능적이고 의미 있는 선택을 할 수 있으며, 환경과의 상호작용을 통해
최적의 정책(.policy)을 학습할 수 있습니다.

환경 설정

배고픈 관리 에이전트의 환경은 강화 학습의 OpenAI Gym 라이브러리를 사용할 수 있습니다. 이 환경은 에이전트가 상태를 관찰하고 행동을 취할 수 있는
인터페이스를 제공합니다. 이번에 구축할 환경의 초기 코드를 아래와 같이 설정해 보겠습니다.

import gym
from gym import spaces
import numpy as np

class HungerManagementEnv(gym.Env):
    def __init__(self):
        super(HungerManagementEnv, self).__init__()
        self.action_space = spaces.Discrete(3)  # 3가지 행동: 음식을 먹다, 운동하다, 쉬다
        self.observation_space = spaces.Box(low=0, high=10, shape=(3,), dtype=np.float32)  # 배고픔 수준, 에너지원, 활동 수준
        self.state = None

    def reset(self):
        self.state = np.array([10, 0, 0], dtype=np.float32)  # 초깃값: 매우 배고픔
        return self.state

    def step(self, action):
        hunger_level, energy_source, activity_level = self.state
        if action == 0:  # 음식을 먹다
            hunger_level = max(0, hunger_level - 5)  # 배고픔 감소
            energy_source += 1  # 에너지원 증가
        elif action == 1:  # 운동하다
            hunger_level = min(10, hunger_level + 2)  # 배고픔 증가
            activity_level += 1  # 활동 수준 증가
        elif action == 2:  # 쉬다
            hunger_level = min(10, hunger_level + 1)  # 배고픔 완화

        self.state = np.array([hunger_level, energy_source, activity_level], dtype=np.float32)
        reward = -hunger_level  # 배고픔이 낮을수록 좋은 보상
        done = hunger_level <= 0  # 배고픔 수준이 0이면 종료

        return self.state, reward, done, {}

    def render(self, mode='human'):
        print(f"Hunger Level: {self.state[0]}, Energy Source: {self.state[1]}, Activity Level: {self.state[2]}")    
    

에이전트 설계

에이전트를 설계하기 위해 강화 학습 알고리즘 중 하나인 Q-Learning을 사용할 수 있습니다. Q-Learning 알고리즘은
각 상태에서 가능한 행동에 대한 가치를 학습하여 에이전트가 최적의 정책을 선택하도록 합니다. 에이전트의 기본 구조는 다음과 같습니다.

import random

class QLearningAgent:
    def __init__(self, action_space):
        self.action_space = action_space
        self.q_table = {}
        self.learning_rate = 0.1
        self.discount_factor = 0.95
        self.exploration_rate = 1.0
        self.exploration_decay = 0.99

    def get_action(self, state):
        if random.uniform(0, 1) < self.exploration_rate:
            return self.action_space.sample()  # 무작위 행동 선택
        return np.argmax(self.q_table.get(tuple(state), np.zeros(self.action_space.n)))  # 최적 행동 선택

    def learn(self, state, action, reward, next_state):
        current_q = self.q_table.get(tuple(state), np.zeros(self.action_space.n))[action]
        max_future_q = np.max(self.q_table.get(tuple(next_state), np.zeros(self.action_space.n)))
        new_q = (1 - self.learning_rate) * current_q + self.learning_rate * (reward + self.discount_factor * max_future_q)
        if tuple(state) not in self.q_table:
            self.q_table[tuple(state)] = np.zeros(self.action_space.n)
        self.q_table[tuple(state)][action] = new_q

        self.exploration_rate *= self.exploration_decay  # 탐색률 감소
    

훈련 및 학습

에이전트가 환경과 상호작용하면서 학습하도록 훈련하는 코드를 작성해 보겠습니다.
에피소드를 반복하면서 에이전트가 행동을 하도록 하고, 보상을 제공하며 Q-테이블을 업데이트합니다.

def train_agent(env, agent, episodes):
    for episode in range(episodes):
        state = env.reset()
        done = False
        while not done:
            action = agent.get_action(state)
            next_state, reward, done, _ = env.step(action)
            agent.learn(state, action, reward, next_state)
            state = next_state
        if (episode + 1) % 100 == 0:
            print(f"Episode: {episode + 1}, Q-Table Size: {len(agent.q_table)}")
    

결과 및 성능 평가

에이전트 학습이 완료된 후, 성능을 평가하기 위해 에이전트를 여러 번 실행하여 배고픔 수준이 0에 도달하는
평균 에피소드 수를 측정할 수 있습니다. 아래 코드는 성능 평가를 위한 방법입니다.

def evaluate_agent(env, agent, episodes):
    total_steps = 0
    for episode in range(episodes):
        state = env.reset()
        done = False
        steps = 0
        while not done:
            action = agent.get_action(state)
            next_state, reward, done, _ = env.step(action)
            state = next_state
            steps += 1
        total_steps += steps
    return total_steps / episodes

average_steps = evaluate_agent(env, agent, 100)
print(f"Average Steps to Hunger Level 0: {average_steps}")
    

결론

배고픔 관리 에이전트를 개발하면서 강화 학습의 기본 개념과 환경 설계, 에이전트 설계,
그리고 Q-Learning 알고리즘을 통해 복잡한 행동 문제를 해결하는 방법을 살펴보았습니다.
이 글에서 논의한 내용을 바탕으로 더 복잡한 환경과 에이전트를 설계하고 개선할 수 있는
기회를 만들어 보기를 바랍니다. 강화 학습은 훌륭한 가능성을 가진 분야이며, 다양한 문제를 해결하는 데
활용될 수 있습니다.