1인 개발자로서 연구 수준의 AI 프로젝트 구축하기: 나의 스택, 도구 및 워크플로우
요약
1인 개발자가 연구 수준의 AI 프로젝트를 성공적으로 구축하기 위한 스택, 도구 및 워크플로우를 소개합니다. 재현 가능성과 자동화를 핵심 원칙으로 삼아, 혼돈을 방지하고 신뢰할 수 있는 실험 시스템을 만드는 방법을 다룹니다.
핵심 포인트
- 모든 실험 결과는 반드시 재현 가능해야 함
- 수동 기록 대신 자동화된 시스템 구축 권장
- 노트북보다는 모듈화된 코드 중심의 개발 지향
- 체계적인 프로젝트 디렉토리 구조를 통한 관리
저는 예전에 “연구 수준(research-grade)”이라는 말이 거대한 연구소, GPU 클러스터, 그리고 파이프라인의 작은 부분을 각각 담당하는 팀원들을 의미한다고 생각했습니다.
그러다 저는 혼자서 AI 프로젝트를 수행하게 되었습니다. 연구소도 없고, 전담 MLOps 엔지니어도 없으며, 새벽 2시에 무언가 고장 나도 탓할 사람이 아무도 없는 상황 말이죠. 오직 저와 노트북, 클라우드 계정, 그리고 테스트하고 싶은 아이디어 목록만이 늘어갈 뿐이었습니다.
저를 놀라게 한 것은 작업이 힘들다는 점이 아니었습니다. 집중된 스택(stack)과 절제된 워크플로우(workflow)만 있다면, 1인 개발자도 진지한 연구 표준을 견딜 수 있는 시스템을 충분히 구축할 수 있다는 사실이었습니다.
제가 최종적으로 구축한 설정은 다음과 같습니다. “엉망진창인 실험”과 “실제로 신뢰하고 기반을 쌓을 수 있는 무언가” 사이의 차이를 만들어낸 도구, 구조, 그리고 습관들입니다.
문제점: 진보로 위장한 혼돈
초기에 제 프로젝트는 다음과 같은 모습이었습니다:
notebooks에서 final_final_v3.ipynb와 같은 이름으로 훈련된 모델들.
./checkpoints, ./exp, 그리고 /tmp에 흩어져 있는 체크포인트(checkpoints).
어떤 하이퍼파라미터(hyperparameters)가 어떤 결과를 만들어냈는지에 대한 명확한 기록이 없었습니다.
스크립트 간에 복사하여 붙여넣기 된 데이터 전처리(Data preprocessing) 과정은 매번 조금씩 달랐습니다.
저는 진전을 보이고 있었지만, 이를 안정적으로 재현(reproduce)할 수는 없었습니다. 지난주에 했던 실험을 다시 실행하려면 고고학자처럼 흔적을 찾아 뒤져야 했습니다.
그때 저는 이것을 단순한 스크립트 모음이 아니라, 하나의 실제 시스템으로 다루기로 결심했습니다.
1인 AI 빌더를 위한 가이드 원칙
도구에 뛰어들기 전에, 저의 선택을 형성한 원칙들은 다음과 같습니다:
모든 것은 재현 가능해야 한다.
다음 달에 다시 실행했을 때 비슷한 결과를 얻을 수 없다면, 그것은 의미가 없습니다.
의지력보다 자동화.
지표(metrics)를 기록하거나 설정을 수동으로 저장하는 것을 일일이 기억할 수는 없습니다. 시스템이 이를 수행해야 합니다.
기본적으로는 단순하게, 필요할 때는 확장 가능하게 만듭니다.
첫날부터 엔터프라이즈급 MLOps가 필요한 것은 아니다. 오늘 당장 작동하고, 규모가 커져도 망가지지 않는 무언가가 필요할 뿐이다.
코드가 우선이고, 노트북은 그다음입니다 (Code first, notebooks second).
노트북은 탐색을 위한 것이며, 핵심 로직은 모듈 (modules)에 존재해야 합니다.
이를 염두에 두고, 제가 실제로 사용하는 스택은 다음과 같습니다.
프로젝트 구조: 지루하지만 강력함
저는 화려하지는 않지만, 저를 제정신으로 유지해 주는 구조를 선택했습니다.
project/
configs/
baseline.yaml
ablation_v1.yaml
data/
dataset_v2.yaml
src/
data/
init.py
dataset.py
preprocess.py
models/
init.py
baseline.py
training/
train.py
loops.py
inference/
predict.py
utils/
config.py
logging.py
experiments/
logs/
artifacts/
notebooks/
01_eda.ipynb
02_baseline.ipynb
tests/
test_data.py
test_models.py
requirements.txt
pyproject.toml
핵심 포인트:
- configs/는 실험을 위한 YAML 파일들을 보관합니다.
- src/는 순수 파이썬 (Python) 코드로, 임포트 (import) 및 테스트 (test)가 가능합니다.
- notebooks/는 오직 탐색과 시각화 (visualization)만을 위해 사용합니다.
- experiments/는 로그 (logs), 체크포인트 (checkpoints), 그리고 메트릭 (metrics)을 저장합니다.
이러한 분리만으로도 저는 훨씬 더 효율적으로 일할 수 있게 되었습니다.
설정 관리 (Configuration Management): 더 이상의 매직 넘버 (Magic Numbers)는 없다
스크립트에 하이퍼파라미터 (hyperparameters)를 하드코딩하는 것을 그만두었습니다. 대신, YAML 설정을 사용합니다:
configs/model/baseline.yaml
model:
name: "baseline_transformer"
hidden_dim: 256
num_layers: 4
dropout: 0.1
training:
batch_size: 32
lr: 3e-4
epochs: 20
seed: 42
data:
dataset: "my_custom_dataset"
path: "data/processed"
max_length: 128
그 다음, 작은 헬퍼 (helper) 함수로 이를 불러옵니다:
src/utils/config.py
import yaml
from pathlib import Path
from dataclasses import dataclass, field
from typing import Any
@dataclass
class Config:
model: dict = field(default_factory=dict)
training: dict = field(default_factory=dict)
data: dict = field(default_factory=dict)
def load_config(path: str) -> Config:
with open(path, "r") as f:
raw = yaml.safe_load(f)
return Config(**raw)
이제 모든 실험은 제가 버전 관리 (versioning)하고, 차이점 비교 (diff)를 하며, 공유할 수 있는 설정 파일과 연결됩니다.
실험 추적 (Experiment Tracking): 기록되지 않았다면, 일어나지 않은 일이다
스프레드시트를 써보기도 했고, 스크린샷이 가득 담긴 폴더를 만들어보기도 했습니다. 하지만 그 어떤 것도 확장성 (scale)을 갖지 못했습니다.
이제 저는 실험 추적 (experiment tracking)을 위해 Weights & Biases (W&B)를 사용합니다. 개인 사용자에게는 무료이며 통합이 매우 쉽습니다.
src/training/train.py (단순화 버전)
import wandb
from src.utils.config import load_config
from src.data.dataset import get_dataloaders
from src.models.baseline import BaselineTransformer
def train(config_path: str):
config = load_config(config_path)
wandb.init(project="solo-ai-research", config=config)
train_loader, val_loader = get_dataloaders(config.data)
model = BaselineTransformer(**config.model)
...
avg_loss = total_loss / len(train_loader)
# 검증 (Validation)
model.eval()
val_loss = 0
...
wandb.finish()
이 시스템을 갖춤으로써 저는 다음과 같은 작업을 할 수 있습니다:
- 실행 (runs) 결과들을 나란히 비교하기.
- 하이퍼파라미터 (hyperparameters)의 변화가 성능에 어떤 영향을 미치는지 확인하기.
- 실험 전체를 검색하기: "
hidden_dim=256이고epochs=20인 모든 실행 결과를 보여줘."
1인 개발자에게 이것은 금광과 같습니다. 결과를 수동으로 기록하는 팀 전체의 역할을 대신해 줍니다.
데이터 및 모델을 위한 버전 관리 (Version Control)
Git은 코드에는 훌륭하지만, 데이터와 체크포인트 (checkpoints)를 위해서는 그 이상의 무언가가 필요했습니다.
저의 접근 방식:
- 코드: 평소와 같이 Git + GitHub 사용.
- 데이터:
- 원천 데이터 (Raw data)는 명확한 명명 규칙 (naming scheme)과 함께 클라우드 스토리지 (S3, GCS 또는 유사 서비스)에 저장.
- 가공된 데이터 (Processed data)는 DVC (Data Version Control) 또는 간단한 해시 스냅샷 (hashed snapshots)을 사용하여 버전 관리.
- 모델:
- 버전 태그와 함께 저장된 체크포인트:
models/baseline/v1.3.0/checkpoint_epoch_15.pt - JSON 매니페스트 (manifest)에 저장된 메타데이터 (metadata):
- 버전 태그와 함께 저장된 체크포인트:
{
"model_name": "baseline_transformer",
"version": "1.3.0",
...
이 설정을 통해 저는 당황하지 않고 다음과 같은 질문에 답할 수 있습니다: "어떤 데이터를 사용하여, 어떤 설정 (config)으로 학습된 어떤 모델이 이 결과를 만들어냈는가?"
로컬 개발 (Local Development) vs. 헤비 트레이닝 (Heavy Training)
저는 일상적인 작업의 대부분을 노트북에서 수행합니다:
데이터 탐색 (Data exploration).
소규모 실험 (Small-scale experiments).
파이프라인 디버깅 (Debugging pipelines).
헤비 트레이닝 (Heavy training)을 위해서는 클라우드 GPU 인스턴스를 실행합니다. 핵심은 전환을 매끄럽게 만드는 것입니다:
로컬과 클라우드에서 동일한 Docker 이미지를 사용합니다.
설정 기반 실행 (Config-driven runs): 저는 노트북에서 작업하든 원격 머신에서 작업하든 다음과 같이 훈련을 시작합니다:
python src/training/train.py configs/model/baseline.yaml
또한 tmux나 screen을 매우 철저하게 사용합니다. 노트북이 절전 모드로 들어가거나 SSH 연결이 끊겨서 훈련 작업이 중단되더라도, 처음부터 다시 시작하지 않고 다시 연결(reattach)하여 진행 상황을 확인할 수 있어야 하기 때문입니다.
테스트 (Testing): AI 코드라도 테스트가 필요한가?
예, 그렇습니다. 예전에는 ML 코드를 위한 테스트를 건너뛰곤 했습니다. "그냥 실험일 뿐이잖아"라고 스스로에게 말하곤 했죠. 그 생각 때문에 한두 번 이상 큰 코를 다쳤습니다.
이제 저는 최소한 다음을 갖추고 있습니다:
데이터 로딩 및 전처리 (Data loading and preprocessing)를 위한 유닛 테스트 (Unit tests):
# tests/test_data.py
def test_preprocessor_output_shape():
df = make_sample_dataframe()
processed = preprocess(df)
assert processed.shape[1] == EXPECTED_FEATURE_DIM
모델에 대한 건전성 검사 (Sanity checks):
# tests/test_models.py
def test_model_forward_shape():
model = BaselineTransformer(hidden_dim=64, num_layers=2)
x = torch.randn(4, 32, 128) # batch, seq_len, input_dim
out = model(x)
assert out.shape == (4, 32, NUM_CLASSES)
이러한 테스트가 제 연구가 옳다는 것을 보장해주지는 않지만, 어처구니없는 버그 때문에 며칠을 허비하는 일은 막아줍니다.
문서화 및 노트 (Documentation and Notes): 저만의 개인 실험 노트
저는 다음과 같은 내용이 담긴 docs/ 폴더를 유지합니다:
환경 설정 방법과 훈련 실행 방법을 설명하는 README.md.
상위 수준의 노트를 기록하는 EXPERIMENTS.md:
- 무엇을 시도했는가.
- 무엇이 작동했는가.
- 무엇이 실패했으며 그 이유는 무엇인가.
기록 예시:
2026-07-08 — hidden_dim에 대한 어블레이션 (Ablation)
hidden_dim을 128에서 256으로 변경함.- 검증 손실 (Val loss)이 0.38에서 0.33으로 개선됨.
- 훈련 시간이 약 40% 증가함.
- 다음 단계: 배치 크기 (Batch size)를 안정적으로 유지하기 위해 그래디언트 누적 (Gradient accumulation)을 사용하여 512를 시도해 볼 것.
이 노트는 휴식 후 프로젝트로 돌아왔을 때 저의 기억 장치가 되어줍니다.
첫날부터 다르게 했을 일들
지금 알고 있는 지식을 가지고 이 프로젝트를 다시 시작할 수 있다면:
실험 추적 (experiment tracking)을 더 일찍 설정하겠습니다.
무엇을 시도했는지 명확하게 파악하지 못해 몇 주를 허비했습니다.
처음부터 "코드 우선, 노트북은 나중에 (code first, notebooks later)" 원칙을 강제하겠습니다.
나중에 노트북에서 로직을 분리해내는 작업은 매우 고통스러웠습니다.
초기에 최소한의 "프로덕션 수준 (production-like)" 인터페이스를 정의하겠습니다.
설령 배포를 하지 않더라도, 추론 (inference) 과정을 마치 배포할 것처럼 설계하겠습니다: 깔끔한 함수, 명확한 입출력, 숨겨진 전역 상태 (global state) 배제.
환경 설정을 자동화하겠습니다.
docker-compose up 한 번이나 모든 것을 설정해주는 make dev 명령 하나만 있었어도 시간을 엄청나게 아꼈을 것입니다.
핵심 요약 (Key Takeaways)
올바른 워크플로우(workflow)만 있다면 1인 개발자도 충분히 연구 수준의 AI 시스템을 구축할 수 있습니다.
노트북은 샌드박스 (sandbox)로 취급하고, 핵심 로직은 잘 구조화된 Python 모듈에 유지하세요.
설정 기반 (config-driven) 실험과 실험 추적 도구 (W&B와 같은)를 사용하여 체계적으로 관리하세요.
코드뿐만 아니라 데이터, 모델, 그리고 설정 (configs)도 버전 관리하세요.
데이터와 모델 코드에 대한 테스트를 작성하세요. 이는 비용이 많이 드는 실수를 방지해 줍니다.
실험 내용을 실험 노트 (lab notebook)처럼 기록하세요. 미래의 당신이 고마워할 것입니다.
당신은 1인 AI 프로젝트를 어떻게 조직하시나요?
혼자서 혹은 소규모 팀으로 AI 또는 머신러닝 (machine learning) 프로젝트를 진행하고 있다면, 당신의 스택 (stack)은 어떤 모습인가요? 작업 방식을 완전히 바꿔놓은 도구나 습관이 있나요?
무엇이 잘 작동하고 있는지, 그리고 여전히 무엇이 혼란스러운지 듣고 싶습니다. 당신의 설정(또는 가장 큰 고충)을 댓글로 남겨주세요. 함께 더 나은 패턴을 찾아낼 수 있을지도 모릅니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기