실무에서는 PyTorch·TensorFlow로 직접 짠 모델을 학습하거나 회사 표준 라이브러리를 묶은 자체 환경이 필요할 때가 많다. SageMaker 학습은 "AWS가 제공하는 컨테이너를 얼마나 그대로 쓰느냐"로 구분된다.
방식
내가 가져오는 것
컨테이너
적합한 상황
내장 알고리즘
(없음, 데이터만)
AWS 관리
표준 문제, 코드 작성 불요
스크립트 모드
학습 스크립트 1개
AWS 프레임워크 컨테이너
PyTorch/TF 코드를 빠르게
프레임워크 컨테이너 확장
스크립트 + 추가 패키지
AWS 컨테이너 + requirements
프레임워크는 그대로, 의존성만 추가
BYOC (자체 컨테이너)
Docker 이미지 전체
내가 만든 이미지
특수 런타임·시스템 의존성
핵심 직관: 위로 갈수록 편하고, 아래로 갈수록 자유롭다.
시험은 "최소한의 노력으로 X를 하라"는 식으로 묻는다 → 요구가 프레임워크 코드 수준이면 스크립트 모드, 시스템 패키지·특수 런타임 수준이면 BYOC.
어느 방식을 골라도 학습 작업의 수명주기와 경로 규약은 동일하다. 이 규약이 시험의 실제 출제 지점이다.
이 계층 구조는 "관심사 분리(separation of concerns)"의 전형이다. 학습 로직(내 코드)과 실행 환경(컨테이너)을 분리하면, 환경이 안정적일 때는 코드만 갈아끼우고(스크립트 모드) 환경 자체가 특수할 때만 환경을 직접 만든다(BYOC). 불필요하게 낮은 계층으로 내려가면 Docker 빌드·유지보수 부담을 떠안게 되므로, "필요한 만큼만 내려간다"가 원칙이다.
fit()을 호출하면 방식과 무관하게 아래 순서가 돌아간다. 어디서 무엇이 깨지는지 이 그림 위에서 짚어야 한다.
estimator.fit({'training': 's3://.../train/'}) │ ├─ ① 학습 인스턴스 프로비저닝 (ml.g5.xlarge 등, 여기서부터 과금) ├─ ② 컨테이너 이미지 pull (AWS 프레임워크 이미지 or 내 ECR 이미지) ├─ ③ S3 → /opt/ml/input/data/<채널명>/ (File 모드는 전체 복사 후 시작) ├─ ④ source_dir 내려받기 + requirements.txt 설치 ├─ ⑤ entry_point 실행 (BYOC는 컨테이너를 `train` 인자로 실행) ├─ ⑥ /opt/ml/model/ → model.tar.gz → S3 output_path 업로드 └─ ⑦ 인스턴스 종료 (과금 종료)
스크립트 모드는 PyTorch·TensorFlow·Scikit-learn 등으로 작성한 학습 스크립트를 AWS가 관리하는 프레임워크 컨테이너 안에서 실행한다. Docker를 전혀 다루지 않고 .py 파일 하나와 추정기(Estimator)만 있으면 된다.
from sagemaker.pytorch import PyTorchestimator = PyTorch( entry_point='train.py', # 내 학습 스크립트 source_dir='src', # 의존 모듈이 있는 폴더 role=role, framework_version='2.0', # AWS가 제공하는 PyTorch 버전 py_version='py310', instance_type='ml.g5.xlarge', instance_count=1, hyperparameters={'epochs': 10, 'lr': 0.001})estimator.fit({'training': 's3://my-bucket/train/'})
학습 스크립트는 SageMaker가 약속한 환경 변수와 경로 규약을 따른다. 이 표가 스크립트 모드·BYOC 양쪽에 공통으로 적용되는 계약이다.
환경 변수
컨테이너 경로
의미
SM_CHANNEL_<채널명>
/opt/ml/input/data/<채널명>/
fit()에 준 채널별 입력 데이터. 채널명 대문자로 변수명이 만들어진다
SM_MODEL_DIR
/opt/ml/model
여기 저장한 것만model.tar.gz로 묶여 S3에 올라간다
SM_OUTPUT_DATA_DIR
/opt/ml/output/data
모델 외 부산물(평가 리포트·플롯 등)
(하이퍼파라미터)
/opt/ml/input/config/hyperparameters.json
hyperparameters=로 넘긴 값. 스크립트에는 CLI 인자로도 들어온다
SM_NUM_GPUS
—
인스턴스가 가진 GPU 개수
SM_HOSTS / SM_CURRENT_HOST
—
분산 학습 시 전체 호스트 목록 / 내 호스트 이름
# train.py 내부import argparse, osparser = argparse.ArgumentParser()parser.add_argument('--epochs', type=int, default=10)parser.add_argument('--lr', type=float, default=0.001)# SageMaker가 주입하는 표준 경로parser.add_argument('--train', default=os.environ['SM_CHANNEL_TRAINING'])parser.add_argument('--model-dir', default=os.environ['SM_MODEL_DIR'])args = parser.parse_args()# ... 학습 ...# 모델을 SM_MODEL_DIR에 저장하면 SageMaker가 S3로 업로드torch.save(model.state_dict(), os.path.join(args.model_dir, 'model.pth'))
⚠️ 함정: 모델을 아무 곳에나 저장하면 SageMaker가 S3로 올려주지 않는다. 반드시 SM_MODEL_DIR(컨테이너 안 /opt/ml/model)에 저장해야 학습 종료 시 자동으로 S3 출력 경로로 압축·업로드된다. 마찬가지로 학습 데이터는 fit()에 준 채널 이름과 SM_CHANNEL_<채널명> 환경 변수가 짝을 이룬다.
from sagemaker.inputs import TrainingInputtrain_input = TrainingInput( s3_data='s3://my-bucket/train/', input_mode='FastFile' # 전체 복사 없이 바로 학습 시작)estimator.fit({'training': train_input})
이 방식은 "프레임워크(PyTorch)는 AWS 것 그대로, 그 위에 라이브러리 몇 개만" 추가하는 가장 흔한 중간 지점이다.
"무엇이 필요한가"로 방법이 갈린다 — Docker를 꺼내야 하는 경계를 이 표로 외운다.
필요한 것
해결 방법
Docker 필요?
파이썬 패키지(pip 설치 가능)
source_dir/requirements.txt
✕
사내 공용 파이썬 모듈(.py)
source_dir에 함께 두거나 dependencies=[...]
✕
실행 시 환경 변수
Estimator(environment={...})
✕
시스템 패키지(apt: ffmpeg, libsndfile 등)
BYOC
✓
특정 CUDA·드라이버 조합
BYOC
✓
AWS가 제공하지 않는 프레임워크·언어
BYOC
✓
⚠️ 함정: requirements.txt에 ffmpeg 같은 apt 패키지 이름을 적는 보기는 오답이다. pip는 파이썬 패키지 인덱스만 본다 → 설치되지 않는다. "pip로 되는가"가 스크립트 모드와 BYOC를 가르는 실질적 기준이다.
requirements.txt 자동 설치는 "불변 인프라(immutable infrastructure)"와 "가변 설정"의 절충이다. 기반 이미지는 AWS가 검증한 채로 두고(불변), 그 위 파이썬 의존성만 선언적으로 추가한다. 다만 매 학습마다 설치가 일어나므로 시작 지연이 생긴다. 의존성이 무겁거나 자주 바뀌지 않으면 차라리 BYOC로 굽는 편이 빠를 수 있다.
BYOC(Bring Your Own Container)는 Docker 이미지를 직접 만들어 ECR에 올리고, 그 이미지로 학습한다. 특수 런타임(예: 특정 CUDA·드라이버 조합), 시스템 패키지(apt), AWS가 지원하지 않는 프레임워크나 언어가 필요할 때 쓴다.
SageMaker가 컨테이너에 요구하는 규약은 단순하다. 학습 시 컨테이너를 train 인자로 실행할 수 있어야 하고, SageMaker가 약속한 경로를 따른다.
FROM ubuntu:22.04RUN apt-get update && apt-get install -y python3-pip libsndfile1 # 시스템 패키지RUN pip3 install torch custom-internal-libCOPY train.py /opt/program/train.py# SageMaker는 'train' 명령으로 컨테이너를 실행한다ENTRYPOINT ["python3", "/opt/program/train.py"]
컨테이너 내부의 SageMaker 약속 경로:
/opt/ml/input/data/<채널명>/ ← 입력 데이터 (S3에서 복사됨)
/opt/ml/input/config/ ← 하이퍼파라미터(hyperparameters.json) 등
/opt/ml/model/ ← 모델 저장 위치 (S3로 업로드됨)
/opt/ml/output/ ← 실패 시 출력
한 음성 AI 팀이 오디오 전처리에 시스템 라이브러리(libsndfile, ffmpeg)와 특정 버전 CUDA가 필요했다. 처음엔 스크립트 모드에 requirements.txt로 해결하려 했지만 시스템 패키지는 pip로 설치되지 않아 실패했다. BYOC로 ffmpeg·libsndfile을 apt로 구운 이미지를 ECR에 올리니 깔끔하게 돌았다. 교훈: 파이썬 패키지면 스크립트 모드+requirements, 시스템 패키지·런타임이면 BYOC.
개념: 절충안 — 컨테이너는 내 것, 인터페이스는 SageMaker 것
BYOC 이미지에 sagemaker-training 툴킷을 설치하면, 내가 만든 이미지 안에서도 스크립트 모드처럼 entry_point를 넘겨 실행할 수 있다. 시스템 의존성은 이미지로 굽고, 학습 코드는 계속 .py로 갈아끼우는 구성이다. "BYOC = 전부 직접"이 아니라 환경만 내려가고 코드 인터페이스는 위에 남긴다는 감각이 실무 표준에 가깝다.
비용을 줄이는 표준 수단은 관리형 스팟 학습이다. 대신 중단될 수 있으므로 체크포인트가 짝을 이룬다.
estimator = PyTorch( entry_point='train.py', source_dir='src', role=role, framework_version='2.0', py_version='py310', instance_type='ml.g5.2xlarge', instance_count=1, output_path='s3://my-bucket/output/', use_spot_instances=True, # 관리형 스팟: 온디맨드 대비 큰 폭 절감 max_run=3600, # 학습 자체의 최대 시간(초) max_wait=7200, # 스팟 대기 포함 총 대기(초). max_run 이상이어야 함 checkpoint_s3_uri='s3://my-bucket/ckpt/', # 중단 시 여기서 재개)
스팟을 켜고 checkpoint_s3_uri를 안 걸면, 중단될 때마다 처음부터 다시 학습한다. 절감액보다 재학습 비용이 커지는 역전이 실제로 일어난다. 스팟 + 체크포인트는 한 세트다.
내 코드가 있는가? ├─ 없다(표준 문제) → 내장 알고리즘 └─ 있다 ├─ 표준 PyTorch/TF/SKLearn 환경 → 스크립트 모드 ← "최소 노력" 키워드 ├─ + pip로 되는 패키지만 추가 → requirements.txt 확장 └─ + apt·CUDA·미지원 런타임 → BYOC (ECR 이미지)
"최소 노력", "코드 수정 최소화" → 가능한 한 위쪽을 고른다.
"시스템 의존성", "특정 드라이버", "우리가 쓰는 언어를 SageMaker가 지원하지 않음" → BYOC.
어떤 경로를 택하든 /opt/ml/ 규약은 그대로다.
다음 글에서는 모델이 한 대 장비에 담기지 않거나 학습이 너무 느릴 때 쓰는 분산 학습을 본다.