논문 링크: https://arxiv.org/pdf/1908.08342
논문 코드: https://github.com/RunzheYang/MORL
블로그: https://blog.csdn.net/weixin_56760882/article/details/145572826
이 문서는 심해 보물 환경(DST)을 예시로 들어 복제 결과를 설명한 것이지만, 본문에서는 FTN(과일 나무 탐사) 환경을 기반으로 복제 과정을 설명합니다.
복제 과정에서 원 논문 코드를 git으로 가져온 후, 노트북(RTX 4060)에서 환경을 구성하고 실행했습니다.
이 프로젝트는 다중 목적 강화학습 알고리즘의 일반화된 버전으로, 핵심은 envelope MOQ-learning이며, 4가지 환경에 적용됩니다(2개의 합성 환경 + 2개의 실제 환경).
합성 환경 두 개:
- Deep Sea Treasure (DST): 심해 보물 찾기
- Fruit Tree Navigation (FTN): 과일 나무 탐사
복잡한 실제 환경 두 개:
- Task-Oriented Dialog Policy Learning (Dialog): 작업 중심 대화 정책 학습
- SuperMario Game (SuperMario): 슈퍼마리오 게임
디렉토리 synthetic 내 파일 목록:
| 파일 경로 | 핵심 역할 | 필요한 이해 내용 |
|---|---|---|
| synthetic/train.py | 합성 환경 학습 진입점 | 명령줄 파라미터 분석(예: --env-name ft로 FTN 환경 지정, --method crl-envelope로 핵심 알고리즘 지정), 학습 루프 로직 |
| synthetic/crl/envelope/meta.py | 핵심 알고리즘 구현 | envelope MOQ-learning의 핵심 로직(다중 목적 보상 가중, 정책 업데이트, 경험 재현) |
| synthetic/crl/envelope/models | 네트워크 구조 | 선형/비선형 네트워크 구성(다양한 합성 환경의 상태/액션 공간 대응) |
| synthetic/envs | 합성 환경 정의 | DST/FTN 환경의 상태, 액션, 다중 목적 보상 함수 정의(환경 규칙 이해는 복제의 전제) |
1. 환경 설정 - 기본 의존성 설치
원 코드가 의존하는 torch 0.4.0(2018년 버전)은 RTX40 시리즈 GPU의 CUDA 아키텍처 지원이 부족합니다(RTX4060은 Ada Lovelace 아키텍처, sm_89, 하지만 torch 0.4.0은 최대 sm_75/Turing 아키텍처까지 지원).
RTX4060 호환을 위해 pytorch 업그레이드:
# 1. 새로운 환경 생성(파이썬 3.6.15 지정)
conda create -n morl-synthetic python=3.6.15 -y
# 2. 환경 활성화
conda activate morl-synthetic
# 3. 파이썬 3.6 호환 핵심 의존성 설치
pip install numpy==1.19.5 scipy==1.5.4 -i https://pypi.tuna.tsinghua.edu.cn/simple
# 4. torch 1.10.2+cu113 설치(파이썬 3.6 유일한 고버전, RTX4060 호환)
pip install torch==1.10.2+cu113 torchvision==0.11.3+cu113 torchaudio==0.10.2+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html -i https://pypi.tuna.tsinghua.edu.cn/simple
PyTorch + CUDA가 RTX4060과 호환되는지 확인:
python -c "import torch;
print('PyTorch 버전:', torch.__version__);
print('CUDA 버전:', torch.version.cuda);
print('CUDA 사용 가능 여부:', torch.cuda.is_available());
print('GPU 이름:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else '인식되지 않음')"
기타 보조 의존성 설치:
# 기타 보조 의존성
pip install termcolor==1.1.0 torchfile==0.1.0 Pillow==8.4.0 six==1.16.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
visdom 설치:
visdom은 외부 네트워크에서 정적 자원(js/css)을 다운로드하려고 시도하여 네트워크 초과 또는 접근 실패가 발생합니다. 최종적으로 오프라인 다운로드 및 컴파일을 선택했습니다.
1. visdom 오프라인 리소스 패키지 다운로드:
cd D:\ProgramData\논문 복제\FirRL
# visdom 저장소 클론
git clone https://github.com/fossasia/visdom.git【내가 직접 zip을 다운로드했습니다】
# 압축 해제된 visdom-master 디렉토리로 이동
cd D:\ProgramData\논문 복제\FirRL\visdom-master
# yarn 미러 소스 변경(국내 다운로드 속도 개선)
yarn config set registry https://registry.npmmirror.com
# electron 미러 소스 설정
yarn config set electron_mirror https://npmmirror.com/mirrors/electron/
# Visdom 프론트엔드 컴파일 의존성 설치(3-8분 소요, 오류 없으면 성공)
yarn install --ignore-optional
# static 폴더 컴파일(1-2분, 성공 시 "Compiled successfully" 출력)
yarn run build
# visdom 설치 디렉토리 찾기(출력 경로 복사)
python -c "import visdom; print(visdom.__path__[0])"
출력: D:\Anaconda\envs\morl-synthetic\lib\site-packages\visdom
# 컴파일된 파일 복사
xcopy /E /I /Y D:\ProgramData\논문 복제\FirRL\visdom-master\py\visdom\static D:\Anaconda\envs\morl-synthetic\lib\site-packages\visdom\static
참고: 【/E: 하위 디렉토리 및 파일 전체 복사;】 【/I: 대상이 존재하지 않을 경우 디렉토리 생성.】 주소를 자신의 주소로 변경하세요.
이 단계는 static을 생성합니다. 원래는 visdom-master에 static이 없었습니다.
2. static.zip을 visdom 설치 디렉토리의 static 폴더로 압축 해제:
경로: D:\Anaconda\envs\morl-synthetic\Lib\site-packages\visdom\static
(만약 static 폴더가 없으면 먼저 생성하세요)
3. visdom 시작 코드의 예외 처리 수정:
D:\Anaconda\envs\morl-synthetic\Lib\site-packages\visdom\server.py의 671-673줄 수정:
# 오류 코드
# logging.error('Error {} while downloading {}'.format(exc.code, key))
# 대체
if hasattr(exc, 'code'):
logging.error('Error {} while downloading {}'.format(exc.code, key))
else:
logging.error('Error while downloading {}: {}'.format(key, exc))
디렉토리 로그 문제 해결:
D:\ProgramData\논문 복제\FirRL\synthetic\utils\monitor.py에서 init_log 함수를 열고 1줄 디렉토리 생성 코드 추가:
def init_log(self, save_path, name):
# 추가된 줄(파일 not found 예외 해결, 논문 원 로직 변경하지 않음)
import os; os.makedirs(save_path, exist_ok=True)
# 논문 원 코드 유지
self.log_file = open("{}{}.log".format(save_path, name), 'w')
4. 자원 다운로드 건너뛰기로 visdom 시작:
D:\\Anaconda\\envs\\morl-synthetic\\lib\\site-packages\\visdom\\server.py에서 download_scripts() 함수를 교체:
def download_scripts():
"""
함수 재작성: 모든 외부 리소스 다운로드 건너뛰기, 로컬 static 폴더 사용
"""
import os
# 로컬 static 폴더 경로 정의(복사한 경로와 일치해야 함)
static_dir = os.path.join(os.path.dirname(__file__), 'static')
# 로컬 static 파일 존재 여부 확인(복사한 파일이 유효한지 확인)
if os.path.exists(os.path.join(static_dir, 'js', 'main.js')):
print("로컬 static 폴더 감지, 외부 리소스 다운로드 건너뜀!")
return
# 로컬에 파일이 없는 경우 경고 메시지 출력(오류 발생하지 않음)
print("로컬 static 폴더 누락, 복사 경로 확인 필요!")
return
morl-synthetic 환경 활성화 상태에서 visdom 시작(새로운 PowerShell 창에서 실행)
python -m visdom.server
# 성공 시: It's Alive! 및 http://localhost:8097 접근 가능
성공 출력:
Checking for scripts.
It's Alive!
INFO:root:Application Started
You can navigate to http://localhost:8097
2. 모델 학습
FTN 도메인(과일 나무 탐사): 나무 형태의 탐사 게임입니다. 에이전트(스마트 엔티티)는 나무 뿌리에서 출발하여 각 단계에서 분기를 선택하고, 6가지 과일을 수집하는 것이 목표(다중 목적)이며, 잎으로 가면 라운드 종료. 로그에 있는 단계 수, 누적 단계 수, 액션, 보상 목록은 에이전트가 이 나무 장면에서 이동한 기록이며, True는 잎에 도달했음을 나타냅니다(이 라운드 종료).
morl-synthetic 환경 활성화 상태 유지
먼저 synthetic 디렉토리로 이동:
cd D:\ProgramData\논문 복제\FirRL\synthetic
코드 실행:
python train.py --env-name dst --method crl-envelope --model linear --gamma 0.99 `
>> --mem-size 4000 --batch-size 256 --lr 1e-3 --epsilon 0.5 --epsilon-decay `
>> --weight-num 32 --episode-num 2000 --optimizer Adam --save crl/envelope/saved/ `
>> --log crl/envelope/logs/ --update-freq 100 --beta 0.96 --name s3_r9 `
>> 2>&1 | Tee-Object -FilePath "crl/envelope/logs/training_s3_r9_$timestamp.log"
학습 과정 및 결과:
3. 평가
평가 코드:
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"; python test/eval_dst.py --env-name dst --method crl-envelope --model linear --gamma 0.99 --save crl/envelope/saved/ --pltpareto --name s3_r9 2>&1 | Tee-Object -FilePath "crl/envelope/logs/eval_s3_r9_$timestamp.log"
결과:
- policy-0.936: 정책 F1 점수(모델이 최적해에 얼마나 가까운지 측정), 정상 범위 0.85~0.98;
- prediction-0.945: 예측 F1 점수(모델이 수익/비용 예측 정확도 측정); 이 두 지표는 임계값(일반적으로 F1>0.85가 합격)을 훨씬 초과하여, DST 환경에서 학습된 s3_r9 모델의 성능이 우수함을 보여줌.
심해 보물(DST) 환경에서의 다중 목적 강화학습(MORL) 파레토 전면 평가 그래프:
| 요소 | 의미 |
|---|---|
| X축 `teasure value` | 보물 가치, 높을수록 얻은 보물이 많음(목표 1). |
| Y축 `time penalty` | 시간 페널티(음수), 더 음수일수록 시간이 더 많이 소요됨/페널티가 큼(목표 2). |
| 검은 점선 + 검은 점 `Pareto` | 이론적 최적 파레토 전면(실제 값), 알고리즘이 접근해야 할 목표. |
| 주황색 점 `Recovered` | 정책 실행으로 얻은 해(에이전트.act로 정책 샘플링). |
| 초록색 점/선 `Predicted` | 모델이 직접 예측한 해(에이전트.predict로 출력된 HQ 값). |
녹색(Predicted)과 주황색(Recovered) 점이 검은 점선(이론적 파레토)에 거의 밀접하게 붙어 있는 것으로:
- 모델 예측 파레토 해(녹색)는 이론적 최적과 거의 일치;
- 정책 실행 해(주황색)도 이론적 전면과 높게 일치, 알고리즘으로 학습된 정책이 최적 균형을 효과적으로 복제함.
4. 고버전 PyTorch 호환 주의사항(오류 방지)
원 코드는 PyTorch 0.4.0 기반, 고버전에서는 미세 조정 필요:
1. Tensor 데이터 유형 호환
synthetic/train.py/roijers_train.py에서 다음 코드 찾기:
# 원 코드(0.4.0)
FloatTensor = torch.cuda.FloatTensor if use_cuda else torch.FloatTensor
LongTensor = torch.cuda.LongTensor if use_cuda else torch.LongTensor
PyTorch 1.10에 맞춤:
# PyTorch 1.10 호환
FloatTensor = torch.cuda.FloatTensor if use_cuda else torch.FloatTensor
LongTensor = torch.cuda.LongTensor if use_cuda else torch.LongTensor
# 고버전 Tensor 할당 경고 수정
torch.set_default_tensor_type(FloatTensor)
2. data.cpu()를 cpu().data로 변경(일부 파일)
act_1 = act_1.data.cpu()는 경고 발생 가능
PyTorch 0.4.0 기반 원 코드는 고버전에서 train.py의 Tensor 작업을 조정해야 합니다:
작업 파일: D:\ProgramData\논문 복제\FirRL\synthetic\train.py
다음 코드 찾기:
if args.method == "crl-naive":
act_1 = act_1.data.cpu()
act_2 = act_2.data.cpu()
elif args.method == "crl-envelope":
act_1 = probe.dot(act_1.data)
act_2 = probe.dot(act_2.data)
elif args.method == "crl-energy":
act_1 = probe.dot(act_1.data)
act_2 = probe.dot(act_2.data)
PyTorch 1.0+에 맞춤:
if args.method == "crl-naive":
act_1 = act_1.cpu().data # cpu()와 data 순서 변경
act_2 = act_2.cpu().data
elif args.method == "crl-envelope":
act_1 = probe.dot(act_1.cpu().data) # CUDA Tensor 오류 방지 위해 cpu() 추가
act_2 = probe.dot(act_2.cpu().data)
elif args.method == "crl-energy":
act_1 = probe.dot(act_1.cpu().data)
act_2 = probe.dot(act_2.cpu().data)
합성 환경에서 논문에서 권장하는 파라미터를 사용하여 학습했으며, 원 코드 수정은 거의 없었고 주로 환경 설정에 시간이 소요되었습니다. --episode-num 2000로 학습 시간은 약 3-4시간(5000회는 실행하지 않음).
참고: 정상 코드의 로그 파일은 터미널 출력을 저장하지 않아, 이러한 데이터를 사용하여 분석 및 그래프 작성해야 하며, 이는 더 직관적입니다.