Python으로 오목 게임 AI를 처음부터 만들기 그 두 49: 다중 팔 밴딧 문제 에이전트를 구현하기 위한 기반 클래스 구현
요약
본 기사는 다중 팔 밴딧 문제(Multi-arm Bandit Problem)를 해결하는 다양한 AI 알고리즘의 기반이 되는 Python 클래스 `MAB_Agent` 구현 방법을 설명합니다. 객체 지향의 '클래스 상속'을 활용하여 공통적인 시뮬레이션 처리와 결과 저장/시각화 기능을 분리하고, 핵심 전략 부분만 추상 메서드로 남겨 개별 알고리즘 개발의 효율성을 극대화하는 것이 목표입니다.
핵심 포인트
- MAB_Agent 기반 클래스를 통해 공통 로직을 구현하여 재사용성을 높였습니다.
- 클래스 상속과 추상 메서드를 사용하여 새로운 알고리즘 추가가 용이합니다.
- 시뮬레이션 결과를 파일에 저장하고 불러와 그래프를 그리면 시간을 절약할 수 있습니다.
- 강화학습의 에이전트 개념을 도입하여 문제 해결 구조를 체계화했습니다.
본 기사의 프로그램은 Python의 버전 3.13에서 실행하고 있습니다. 또한, numpy의 버전은 2.3.5입니다.
| 링크 | 설명 |
|---|---|
| marubatsu.py | Marubatsu, Marubatsu_GUI 클래스의 정의 |
| ... | |
| AI 목록과 지금까지 작성한 데이터 파일에 대해서는 아래 기사를 참조해 주세요. |
지난 기사에서는 다중 팔 밴딧 문제(Multi-arm Bandit Problem)를 '탐색만 하는 것 (원시 몬테카를로법의 근사)'이라는 방법으로 풀어 그 결과를 그래프로 시각화했습니다.
다중 팔 밴딧 문제를 푸는 알고리즘에는 다양한 종류가 있지만, 그 차이는 '다음 레버를 당길 슬롯머신을 어떤 전략(방책)으로 선택할지'라는 한 가지 지점에 있습니다. 그 외의 '시뮬레이션을 실행하는 처리'나 '결과를 그래프로 그리는 처리'는 어떤 알고리즘을 사용하더라도 완전히 공통적입니다.
향후 기사에서 다양한 알고리즘을 차례로 시도해 나갈 때, 매번 똑같은 프로그램을 작성하는 것은 비효율적입니다.
그래서 이번 기사에서는 객체 지향의 강력한 기능인 **'클래스 상속(継承)'**를 사용하여 다음과 같은 접근 방식을 취하기로 했습니다.
공통적인 처리를 모은 '기반 클래스(土台)' 만들기: 알고리즘 종류와 관계없이 공통하는 '시뮬레이션 실행 처리'를 구현합니다.
-
에피소드의 결과를 기록하고, 언제든지 그래프로 그릴 수 있도록 시각화할 수 있는 메서드를 공통 기능으로 준비합니다.
-
변화하는 부분인 '슬롯머신을 선택하는 처리'는 내용물을 비운 추상 메서드로 정의합니다.
개별 알고리즘은 기반 클래스를 '상속'하여 만들기: 기반(기반 클래스)을 통째로 물려받기 때문에, '슬롯머신을 어떻게 선택할지'라는 메서드의 내용만 정의하면 새로운 알고리즘을 쉽게 구현할 수 있습니다.
이러한 접근 방식을 취함으로써, **'새로운 알고리즘의 전략 부분만 집중적으로 구현할 수 있고, 그래프 그리기는 한 줄 호출하는 것만으로 재사용할 수 있다'**는 큰 장점을 얻게 됩니다.
강화학습(Reinforcement Learning)의 세계에서는 문제를 푸는 (행동하는) 프로그램을 에이전트(agent)라고 부릅니다. 따라서 이번 기사에서는 다중 팔 밴딧 문제(Multi-arm Bandit Problem)를 푸는 다양한 알고리즘의 토대가 되는 기반 클래스를 MAB_Agent라는 이름으로 정의합니다.
아래는 MAB_Agent 클래스에 구현할 공통 기능의 방침입니다.
- 설정 기록: 다중 팔 밴딧 문제의 설정(슬롯머신별 당첨 확률)을
__init__메서드(이니셜라이저)를 통해 받아 속성으로 대입하여 기록합니다. - 1 에피소드 처리: 슬롯머신의 레버를 선택하는 일련의 처리 (1 에피소드)를 실행하는 메서드를 구현합니다.
- 전략의 추상화: 1 에피소드 처리 과정에서 호출되는, '어떤 슬롯머신을 선택할지'를 결정하는
select_arm메서드를 내용물이 비어있는 추상 메서드로 정의합니다. - 다수 에피소드 실행: 에피소드를 여러 번 반복 실행하는 메서드를 구현합니다.
- 결과 저장 및 읽기: 실행에 시간이 걸리는 시뮬레이션 결과를 파일에 저장하는 메서드와, 그것을 파일에서 읽어오는 메서드를 정의합니다.
- 시각화 자동화: 지난 기사에서 소개한 다양한 분석용 그래프(누적 보상의 추이 등)를 각각 그리는 메서드를 구현합니다.
상기 방침 중에서 '결과 저장 및 읽기' 기능은 지난 기사에서는 구현하지 않았던 것입니다. 이러한 기능을 구현하는 이유는, 지난 기사에서 설명했듯이, 수천 번의 시뮬레이션(에피소드)을 실행하려면 상당한 시간이 걸리기 때문입니다. 그래프를 그릴 때마다 긴 시간을 기다리는 것은 힘들기 때문에, 한 번 계산한 데이터를 파일에 저장해 두고, 그래프를 그릴 때는 순식간에 읽어오는 구조를 구현합니다.
토대가 되는 MAB_Agent 클래스가 완성되면, 개별 알고리즘(탐색에 전념한 원시 몬테카를로법의 근사나, 향후 기사에서 자세히 설명할 ε-greedy 법 등)은 이 기반 클래스를 '상속'한 자식 클래스로 구현합니다. 자식 클래스 측에서 구현해야 할 것은 다음 단 두 가지뿐입니다.
- 알고리즘 이름 설정: 그래프의 제목 등에 표시할 수 있도록, 클래스 속성
NAME
알고리즘의 이름(문자열)을 할당하는 3-
전략 구현: 기본 클래스에서는 비어있던 select_arm
메소드를 오버라이드하여, '이 알고리즘 고유의 레버 선택 방식'을 구현합니다.
예를 들어, 지난 기사에서 검증했던 '항상 무작위로 슬롯머신을 계속 선택하는 알고리즘(원시 몬테카를로법 근사)'은 이 설계를 사용하면 놀라울 정도로 짧고 간결하게 정의할 수 있습니다. 이때 항상 무작위(Random)로 선택한다는 점에 착안하여 클래스 이름은 Random_MAB_Agent
으로 했습니다.
class Random_MAB_Agent(MAB_Agent):
NAME = "완전 무작위 (원시 몬테카를로법 근사)"
def select_arm(self):
...
기본 클래스로 정의하는 MAB_Agent
클래스의 속성 목록을 정리합니다.
대부분의 속성은 지난 기사에서 구현한 프로그램 변수를 그대로 클래스 속성으로 만든 것입니다. 따라서 해당 속성 설명은 생략하고, 이번 기사에서 새롭게 추가된 부분이나 이름이 변경된 속성을 중심으로 해설하겠습니다.
그래프 제목에 표시할 알고리즘 이름을 기록하는 title
속성을 추가했습니다. '클래스 속성의 NAME을 그대로 쓰면 되지 않을까?'라고 생각할 수 있지만, 향후 기사에서 구현할 'ε-greedy법' 등의 알고리즘에서는 $\epsilon$과 같은 파라미터(설정값)에 따라 그래프를 나누어 비교하고 싶은 경우가 생깁니다. 따라서 알고리즘 이름뿐만 아니라 파라미터 정보까지 담을 수 있는 문자열로 title
을 준비했습니다.
| 속성명 | 의미 |
|---|---|
mab | 다중 팔 밴디트 문제의 환경(슬롯머신의 당첨 확률)을 나타내는 MAB 클래스의 인스턴스 |
title | 그래프 제목에 표시할, 알고리즘의 이름과 파라미터를 포함한 문자열 |
1회성 시뮬레이션(1 에피소드) 데이터를 보관하는 속성으로, 기본적으로 지난 기사의 프로그램 변수와 동일합니다. 다만, 여러 번의 에피소드(시뮬레이션 반복)를 수행할 경우에는 **'가장 마지막에 실행한 (최신) 1회분의 에피소드 데이터'**가 덮어쓰여 남아있는 사양으로 합니다.
| 속성명 | 의미 |
|---|---|
stepnum | 1 에피소드의 총 스텝 수(레버를 당긴 총 횟수) |
step | 현재 처리 중인 스텝 수(스텝 번호: 0 ~ stepnum - 1) |
total_coin | 해당 스텝까지 얻은 '획득 코인의 총합'의 최신값 |
mean_coin | 해당 스텝까지 얻은 코인의 '1회당 평균 매수'의 최신값 |
total_coins | 각 스텝별 total_coin의 추이를 기록하는 list |
mean_coins | 각 스텝별 mean_coin의 추이를 기록하는 list |
slot | 각 슬롯머신의 상세 데이터를 관리하는 딕셔너리 dict. dict 키에 대한 상세 내용은 지난 기사를 참조 |
수천 번에 걸친 에피소드 반복 시뮬레이션 결과를 NumPy의 다차원 배열(ndarray)을 사용하여 효율적으로 기록 및 집계하기 위한 속성입니다.
처음 두 개의 속성은 지난 기사에서 정의한 프로그램 변수와 동일하므로, 자세한 내용은 해당 기사를 참고해 주십시오.
또한, 지난 기사에서는 슬롯머신 데이터의 집계 결과를 mean이나 std 같은 임시 변수에 할당했지만, 클래스 속성으로 장기간 보관하는 특성을 고려하여 한눈에 의미가 전달되도록 조금 더 정제된 이름으로 변경했습니다.
| 속성명 | 의미 |
|---|---|
mean_coins_by_step | [episode][step] 형태를 갖는 2차원 ndarray. 특정 에피소드의 특정 스텝에서의 '스텝당 누적 보상'을 기록함 |
slotratio_by_step | [arm][episode][step] 형태를 갖는 3차원 ndarray. 특정 환경(에피소드와 스텝)에서의 각 슬롯머신의 '성공 확률 추정치(표본 평균)'를 기록함 |
mean_mean_coins_by_step | [step] 형태를 갖는 1차원 ndarray. 모든 에피소드를 거친, 각 스텝에서의『'스텝당 누적 보상'의 평균값』 추이를 기록함 |
std_mean_coins_by_step | [step] 형태를 갖는 1차원 ndarray. 모든 에피소드를 거친, 각 스텝에서의『'스텝당 누적 보상'의 표준편차(변동폭)』 추이를 기록함 |
mean_estimate_coins_by_step | [arm][step] 형태를 갖는 2차원 ndarray. 모든 에피소드를 거친, 각 스텝에서의『'각 슬롯머신의 성공 확률 추정치'의 평균』 추이를 기록함 |
std_estimate_coins_by_step | [arm][step] 형태를 갖는 2차원 ndarray. 모든 에피소드를 거친, 각 스텝에서의『'각 슬롯머신의 성공 확률 추정치'의 표준편차』 추이를 기록함 |
다음으로, MAB_Agent 클래스에 구현할 메서드 목록을 설명합니다.
메서드 이름 링크는 해당 메서드를 정의 및 해설하는 항목으로 연결되는 링크입니다.
| 메서드명 | 의미 |
|---|---|
__init__(self, p) | 초기화자(Initializer). p에는 각 슬롯머신의 성공 확률을 나타내는 list를 할당함 |
save(self, fname) | 시뮬레이션으로 수집한 방대한 에피소드의 데이터를 fname으로 지정된 파일에 저장함 |
load(fname) | 파일에 저장된 데이터를 읽어와 에이전트를 복원함 (정적 메서드, Static Method) |
여기서 등장하는 load 메서드는 인스턴스 메서드가 아니라 '정적 메서드(Static Method)' 로 정의합니다. 따라서 첫 번째 매개변수에 self를 기술할 필요가 없습니다.
load 메서드를 정적 메서드로 정의하는 이유는, '앞으로 파일에서 데이터를 읽어와 새로운 에이전트를 만들고 싶지만, 읽기 전에 굳이 빈 인스턴스를 생성해 두는 것은 부자연스럽기' 때문입니다. 클래스로부터 직접 MAB_Agent.load(파일명)으로 호출할 수 있도록 설계한 것입니다.
지난 글에서 진행했던 시뮬레이션(에피소드)을 실행하는 처리를 클래스의 메서드로 정리했습니다. 또한, 계산 결과의 요약(summary)을 텍스트로 확인할 수 있도록 print_summary 메서드를 새로 추가했습니다.
| 메서드명 | 의미 |
|---|---|
play_episode(self, stepnum) | 총 스텝 수가 stepnum인 에피소드(1회성 시뮬레이션)의 처리를 수행함 |
select_arm(self) | 다음에 당길 슬롯머신을 선택하는 추상 메서드(Abstract Method). 하위 클래스 측에서 각각의 전략을 오버라이딩하여 구현합니다. |
play_episodes(self, stepnum, episodenum) | 총 스텝 수가 stepnum인 에피소드를 episodenum번 반복 실행하여 통계 데이터를 수집함 |
print_summary(self) | 집계된 데이터의 요약(summary)을 텍스트(문자열)로 표시함 |
지난 글에서 구현했던 그래프를 그리는 처리를 모두 메서드화합니다.
더 나아가 이번에는, 향후 기사에서 다양한 강화학습(Reinforcement Learning) 알고리즘을 구현할 때, 각각의 알고리즘 특성을 비교할 수 있는 compare_... 메서드를 새로 추가했습니다. 해당 메서드들은 여러 개의 다른 에이전트들을 list로 묶어 첫 번째 매개변수로 할당하여 처리하기 때문에, 클래스에 종속되는 정적 메서드(Static Method) 로 구현합니다.
| 메서드명 | 그리는 그래프 |
|---|---|
draw_cumulative_reward(self) | 1 에피소드의 '누적 보상 (cumulative reward) (획득 코인의 총합)'의 추이 |
draw_cumulative_reward_per_step(self) | 1 에피소드의 '단계당 누적 보상'의 추이 |
draw_selectnum(self) | 1 에피소드의 '각 슬롯머신의 선택 횟수'의 추이 (애호심 시각화) |
draw_estimated_odds_of_arm(self) | 1 에피소드의 '각 슬롯머신의 당첨 확률 (odds)의 추정치'의 추이 |
draw_cumulative_rewards(self, episodenum) | 지정된 여러 번의 에피소드에 걸친 각각의 '단계당 누적 보상의 추이'를 겹쳐서 그리기 |
draw_mean_cumulative_reward_by_step(self) | 여러 번의 에피소드를 통한 '단계당 누적 보상의 평균'의 추이 |
compare_mean_cumulative_reward_by_step(agent_list) | 여러 개의 다른 알고리즘(agent_list)의 '단계당 누적 보상의 평균'의 추이를 같은 그래프에 그려 실력 성장 속도를 비교하는 4 (정적 메서드) |
draw_std_cumulative_reward_by_step(self) | 여러 번의 에피소드를 통한 '단계당 누적 보상의 표준편차 (변동폭)'의 추이 |
compare_std_cumulative_reward_by_step(agent_list) | 여러 개의 다른 알고리즘의 '단계당 누적 보상의 표준편차'의 추이를 같은 그래프에 그려 결과의 안정성 (변동폭의 적음)을 비교하는 (정적 메서드) |
draw_histogram_of_cumulative_reward_by_step(self, steplist, stepmul) | 여러 번의 에피소드에서의 '특정 단계에서의 누적 보상'의 히스토그램 (분포 변화 추적) |
draw_ci_of_cumulative_reward_by_step(self) | 여러 번의 에피소드에서의 '단계당 누적 보상의 평균값 ± 3σ (신뢰구간: confidence interval)'의 추이 |
draw_ci_of_estimated_odds_of_arm(self) | 여러 번의 에피소드에서의 '각 슬롯머신의 당첨 확률 추정치의 평균 ± 3σ (신뢰구간)'의 추이 |
draw_std_estimated_odds_of_arm(self) | 여러 번의 에피소드에서의 '각 슬롯머신의 당첨 확률 추정치의 표준편차'의 추이 |
전체 설계도(방침)가 정해졌으므로, 여기부터는 기반 클래스 MAB_Agent의 메서드를 하나씩 순서대로 정의해 나갑니다. 이때, 기반 클래스의 전체 정의에 대해서는 마지막에 모아서 게재합니다.
먼저 모든 토대가 되는, 클래스 선언과 초기 설정을 수행하는 __init__ 메서드(이니셜라이저)를 정의합니다.
1 ~ 8 행: 기반 클래스에서 사용할 모듈을 임포트합니다. -
10 행: MAB_Agent(metaclass=ABCMeta)를 기재함으로써, 이 클래스를 '직접 인스턴스화할 수 없는 추상 클래스'로 정의합니다. -
12 행: mab 속성에, 다중 팔 밴딧 문제의 환경 (슬롯머신의 당첨 진정한 확률)을 나타내는 MAB 클래스의 인스턴스를 생성하여 대입합니다. -
13 행: 그래프의 제목으로 이용할 문자열을 title 속성에, 하위 클래스 측에서 선언되는 알고리즘 이름 (NAME 속성)을 title 속성에 대입합니다. 향후 기사에서 파라미터를 가진 알고리즘을 만들 경우에는, 하위 클래스 측에서 __init__ 메서드를 덮어쓰기(오버라이드)하여, 파라미터를 포함한 문자열을 title 속성에 대입합니다.
1 from abc import ABCMeta, abstractmethod
2 from ai import MAB
3 import pickle
...
행 번호가 없는 프로그램
수천 개 이상의 스텝의 시뮬레이션(에피소드)을 수백, 수천 번 반복하여 얻은 통계 데이터는 계산을 완료하는 데 몇 분 이상의 시간이 걸리지만, 이 데이터들은 VSCode 창을 닫아버리면 모두 사라져 버립니다. VSCode를 다시 실행할 때마다 재계산을 하고 오랜 시간 기다리는 것은 힘들기 때문에, 얻은 데이터를 파일에 저장하여 순식간에 복원할 수 있도록 합니다.
파일에 저장하고 불러오는 처리는 이전 기사에서 구현했던 Mbtree 클래스의 파일 저장 및 로드 처리와 완전히 동일한 방식으로 구현할 수 있습니다.
에피소드의 처리 결과는 모두 에이전트 자신의 속성(attribute)에 대입하여 기록되기 때문에, save 메서드에서는 자신(self)을 통째로 파일에 저장합니다. Python에서 파일 저장을 할 때 자주 사용되는 pickle 모듈을 사용하여 데이터를 저장하므로, 확장자는 표준적인 ".pkl"를 사용하기로 합니다. 따라서 가변 인자(placeholder)인 fname에는 확장자를 포함하지 않은 파일명을 전달하게 됩니다.
또한, 이번 다차원 배열 데이터(ndarray)는 용량이 상당히 커지기 때문에, 이전 기사에서 배운 "** gzip 모듈을 이용한 압축**"도 함께 결합하여 파일 크기를 작게 만듭니다.
load 메서드에서는 파일로부터 복원된 인스턴스를 return 문으로 반환값으로 돌려줍니다. 이 처리에 필요한 정보는 파일명(fname)뿐이므로, 이전 기사에서 설명했듯이 인스턴스를 생성하지 않아도 호출할 수 있는 **정적 메서드(@staticmethod)**로 정의합니다.
아래는 save와 load 메서드의 정의입니다.
1 def save(self, fname):
2 with gzip.open(f"{fname}.pkl", "wb") as f:
3 pickle.dump(self, f)
...
행 번호가 없는 프로그램
def save(self, fname):
with gzip.open(f"{fname}.pkl", "wb") as f:
pickle.dump(self, f)
...
1 에피소드를 실행하는 play_episode 메서드는 이전 기사에서 정의된 play_episode 함수와 거의 같지만, 다음 점들이 다릅니다.
- 환경 설정(슬롯머신의 당첨 확률)은
mab속성에 대입되어 있으므로, 가변 인자(mab)를 삭제했습니다. - 슬롯머신 선택 처리를
select_arm메서드를 호출하여 수행하도록 수정했습니다 (17행). - 데이터를 인스턴스의 속성에 기록하도록 수정하고, 반환값은 없게 했습니다.
아래는 play_episode와 select_arm의 정의입니다. select_arm 메서드의 구체적인 처리는 MAB_Agent 클래스를 상속받은 자식 클래스에서 오버라이드하여 정의하므로, @abstractmethod를 기재하여 추상 메서드로 정의합니다.
1 def play_episode(self, stepnum):
2 self.stepnum = stepnum
3 self.total_coin = 0
...
행 번호가 없는 프로그램
def play_episode(self, stepnum):
self.stepnum = stepnum
self.total_coin = 0
...
여러 번의 에피소드를 반복 실행하는 play_episodes 메서드의 처리는 이전 기사에서 1,000회의 에피소드 처리를 진행한 프로그램과 거의 같지만, 클래스의 공통 기능으로 재사용할 수 있도록 다음의 고안을 추가했습니다.
- 유연한 가변 인자 설정: 실험하고 싶은 "총 스텝 수"와 "에피소드 횟수"를 가변 인자인
stepnum과episodenum으로 자유롭게 지정할 수 있게 했습니다 (1~3행). - 속성 데이터 이용:
play_episode가 값을 반환하지 않는 사양(데이터를 속성에 기록)이 되었기 때문에, 실행 후에 에이전트 자신의self.mean_coins
데이터를 추출하여 다차원 배열로 저장하는 부분(8, 9행) -
집계 처리 속도 향상: 이전 기사의 프로그램에서는 그래프를 그릴 때마다 NumPy 함수를 사용하여 평균이나 표준편차를 계산했지만, 방대한 데이터를 그래프를 그릴 때마다 반복적으로 재계산하는 것은 비효율적입니다. 따라서 모든 에피소드 루프가 끝난 직후(13 ~ 16행)에 일괄로 계산하고 속성으로 미리 보관하도록 수정했습니다 -
진행률 표시줄 (Progress Bar): 많은 에피소드를 처리할 경우 몇 분간의 대기 시간이 발생하므로, 확실하게 처리가 진행되고 있음을 체감할 수 있도록 tqdm 모듈을 활용하여 실시간으로 진행 상황(프로그레스 바)이 표시되도록 했습니다(7행).
1 def play_episodes(self, stepnum, episodenum):
2 self.stepnum = stepnum
3 self.episodenum = episodenum
...
행 번호가 없는 프로그램
def play_episodes(self, stepnum, episodenum):
self.stepnum = stepnum
self.episodenum = episodenum
...
그래프를 그리는 메서드를 구현하기 전에, 현재의 집계 결과를 텍스트로 표시하여 확인할 수 있도록 print_summary 메서드를 구현합니다.
이 메서드에서는 시뮬레이션이 어느 정도까지 실행되었는지에 따라 출력하는 내용을 유연하게 변화시킵니다. 이를 판별하기 위해 Python의 내장 함수인 hasattr을 사용하여 지정된 속성이 인스턴스 내에 존재하는지 여부를 체크합니다.
play_episode가 실행되면, 1 에피소드의 길이를 나타내는stepnum속성이 생성됩니다 -play_episodes가 실행되면, 에피소드 반복 횟수를 나타내는episodenum속성이 생성됩니다.
이 두 속성의 유무를 hasattr로 체크함으로써, 아직 계산되지 않은 데이터를 표시하려 할 때 발생하는 오류를 방지할 수 있습니다.
def print_summary(self):
print(f"{self.title}의 결과 요약")
if hasattr(self, "stepnum"):
...
여기부터는 수집한 시뮬레이션 데이터를 바탕으로 알고리즘의 특성을 한눈에 분석할 수 있는 그래프를 순차적으로 구현해 나갑니다.
아래는 '단계별 누적 보상(획득한 코인의 총 개수) 추이'를 그리는 draw_cumulative_reward 메서드의 정의입니다. 프로그램 내용은 다음 개선 사항을 제외하고는 이전 기사와 거의 동일합니다.
- 제목의 알고리즘 표기 개선: 어떤 알고리즘의 그래프인지 한눈에 알 수 있도록, 제목 상단 줄에 알고리즘 이름(
self.title)이 표시되도록 수정했습니다. 향후 모든 그래프 그리기 메서드도 이 형태로 통일할 예정입니다 -
문구 간소화: 그래프 제목을 더 단순하게 만들기 위해, 끝에 붙은 '의 추이'라는 단어를 삭제했습니다.
1 def draw_cumulative_reward(self):
2 plt.title(f"{self.title}\n누적 보상")
3 maxp = max(self.mab.p)
...
행 번호가 없는 프로그램
def draw_cumulative_reward(self):
plt.title(f"{self.title}\n누적 보상")
maxp = max(self.mab.p)
...
아래는 알고리즘의 진정한 실력을 가늠하기 위한 가장 중요한 그래프인 '단계당 누적 보상'을 그리는 draw_cumulative_reward_per_step 메서드의 정의입니다. 프로그램의 기본적인 내용은 앞서와 마찬가지로 그래프 제목 표시를 개선한 점을 제외하고 이전 기사와 거의 동일합니다.
이 그래프는 학습이 진행됨에 따라 '성공 평균' 라인을 넘어, 신의 시점인 '상한선'으로 우상향하게 가까워지는, 영리한 강화학습 알고리즘의 성장 정도를 평가하는 데 가장 중요한 지표가 됩니다.
아래는 각 슬롯 머신이 지금까지 몇 번 선택되었는지에 대한 '선택 횟수'를 그리는 draw_selectnum 메서드의 정의입니다. 프로그램 내용은 지난 기사와 거의 같습니다.
이 그래프를 그려봄으로써, 학습이 진행된 AI가 우수한 슬롯 머신을 어떻게 **'편애(행동의 편향)'**했는지, 혹은 정보를 전혀 활용하지 못하고 모두에게 평등하게 대우하고 있는지를 시각적으로 추적할 수 있습니다.
def draw_selectnum(self):
plt.title(f"{self.title}\n슬롯 머신의 선택 횟수")
for arm in range(self.mab.num):
...
아래는 각 슬롯 머신의 당첨 확률(표본 평균)이 스텝별로 어떻게 변화하는지에 대한 '당첨 추정 확률의 추이'를 그리는 draw_estimated_odds_of_arm 메서드의 정의입니다. 프로그램 내용은 지난 기사와 거의 같습니다.
강화학습 (RL)의 '활용(Exploitation)'에서는 이 데이터(표본 평균)를 슬롯 머신의 실력이라고 믿고 행동을 선택합니다. 따라서, 이 추정치가 '진짜 확률(참 확률)'을 나타내는 검은색 점선(`linestyle=
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기