제6부 생성형 AI 플랫폼
6.2 NeMo I — 프레임워크 구조·데이터
6.2.1 이 장에서 배우는 것 / 선수 지식
6.1에서 생성형 AI 라이프사이클을 세우고, 학습/커스터마이즈가 그 ② 단계임을 봤다. 이 장부터 6.5까지는 그 학습 단계의 도구 — NeMo — 를 판다. NeMo는 엔비디아의 생성형 AI 모델을 GPU에서 대규모로 학습·커스터마이즈·정렬하는 프레임워크다(LLM·멀티모달·음성). 이 장은 NeMo의 구조와, 대규모 학습의 절반인 데이터 경로(토크나이즈 → 바이너리 인덱싱 → 블렌딩 → 시퀀스 패킹 → 로딩)를 익힌다. 병렬화는 6.3, 커스터마이즈(SFT/LoRA)는 6.4, 정렬은 6.5.
핵심 통찰: 대규모 LLM 학습에서 데이터 경로는 모델만큼 중요하다. 수 TB 코퍼스를 매 스텝 토크나이즈하거나 패딩으로 낭비하면 GPU가 기아 상태가 된다(3.8의 "GPU 기아"). NeMo는 데이터를 미리 바이너리로 인덱싱해 무작위 접근·메모리 맵으로 읽고, 여러 데이터셋을 가중 블렌딩하며, 가변 길이 문서를 시퀀스 패킹으로 빈틈없이 채운다. 이 경로를 이해하는 것이 이 장의 목표.
배우는 것:
- NeMo의 구조 — 컬렉션(LLM·멀티모달·음성), 모델·데이터·트레이너의 분리, config 기반(YAML/Hydra) 워크플로, Megatron-Core 병렬 토대.
- 데이터 경로 — 원시 텍스트 → 토크나이즈 → 바이너리 인덱싱(mmap·무작위 접근) → 샤딩·셔플 → 로딩.
- 데이터 블렌딩 — 여러 코퍼스를 가중치로 섞어 도메인 혼합.
- 시퀀스 패킹 — 가변 길이 문서를 고정 길이에 빈틈없이 채워 패딩 낭비 제거.
- 토크나이저(BPE/SentencePiece)와 어휘·특수 토큰.
- 데이터 전처리·config·학습 실행의 흐름(개념 실습).
선수 지식: 6.1(라이프사이클·적응 스펙트럼), 4.14(TP/PP/DP 병렬·NCCL — NeMo가 누림), 3.8(데이터 로딩·GPU 기아), 1.14(혼합정밀·BF16), 1.9(HBM). 병렬화 세부는 6.3. 환경은 NGC NeMo 컨테이너 권장.
정직: NeMo는 구조가 크게 재편된 적이 있다(예: 초기 PyTorch Lightning 기반 → Megatron-Core 중심·NeMo 2.0 재구성, 실행 도구 변화). 컬렉션·API·config 형식·CLI는 버전마다 다르다 — 이 장은 변하지 않는 개념(데이터 바이너리화·블렌딩·패킹·config 기반·Megatron 병렬 토대)을 가르치고, 구체 명령·클래스는 "그 버전 문서로 확인"("시점 의존"). 모든 수치는 환경 의존 개략값.
6.2.2 NeMo는 무엇인가 — 구조
NeMo는 PyTorch 위에, 대규모 분산 학습(Megatron-Core)과 생성형 AI 모델·레시피를 갖춘 프레임워크다.
NeMo가 다루는 관심사를 분리한다:
| 구성요소 | 역할 |
|---|---|
| 모델(model) | 아키텍처·forward·손실(LLM·멀티모달·음성 컬렉션) |
| 데이터(data) | 데이터셋·로더·전처리(이 장의 초점) |
| 트레이너(trainer) | 학습 루프·체크포인트·로깅·분산 |
| 병렬화 | Megatron-Core로 TP/PP/DP/SP(6.3) |
| config | 위 전부를 YAML/Hydra로 선언 — 코드 수정 없이 실험 |
config 기반 워크플로가 NeMo의 색깔이다. 모델 크기·병렬도·데이터·옵티마이저·정밀도를 YAML로 선언하고 오버라이드한다:
# 개념 예시(키·구조는 버전 의존)
model:
hidden_size: 4096
num_layers: 32
tensor_model_parallel_size: 8 # TP(6.3)
pipeline_model_parallel_size: 2 # PP
precision: bf16 # 혼합정밀(1.14)
data:
data_prefix: [0.7, "/data/web", 0.3, "/data/code"] # 블렌딩 가중치(6.2.5)
seq_length: 4096
tokenizer: { type: sentencepiece, model: tokenizer.model }
trainer:
devices: 8
num_nodes: 4 # 32 GPU
max_steps: 100000
핵심: NeMo는 "모델을 코드로 새로 작성하기"보다 검증된 레시피·config를 조정해 대규모 학습을 운용하는 데 최적화됐다. 병렬화(6.3)·정밀도(1.14)·데이터 경로가 config로 노출되어, 코드를 안 건드리고 32·256·1024 GPU로 스케일한다.
6.2.3 대규모 학습의 데이터 문제
수 TB 텍스트로 LLM을 학습할 때 순진한 데이터 처리의 문제:
| 순진한 처리 | 결과(함정) |
|---|---|
| 매 스텝 원시 텍스트를 토크나이즈 | CPU 병목, GPU 기아(3.8) |
| 텍스트 파일을 순차 읽기 | 무작위 셔플·재개 어려움 |
| 가변 길이 문서를 고정 배치로 | 패딩으로 연산·메모리 낭비 |
| 수 TB를 메모리에 적재하지 못함 | 스트리밍·맵 필요 |
NeMo(Megatron-Core 계열)의 해법은 데이터를 미리 바이너리로 인덱싱하는 것이다:
왜 바이너리 인덱싱인가:
- 재토크나이즈 제거: 토크나이즈는 한 번만(오프라인). 학습은 토큰 ID를 바로 읽음 → CPU 병목 제거.
- 무작위 접근·mmap: 수 TB를 메모리에 적재하지 않고 메모리 맵으로 필요한 부분만.
.idx가 문서 경계 오프셋을 가져 임의 인덱스로 즉시 접근(셔플·재개 쉬움). - 결정적 재현·재개: 인덱스 기반이라 셔플 시드·스텝으로 정확히 재개(체크포인트).
6.2.4 워크스루 (1) — 데이터 전처리
원시 텍스트를 학습용 바이너리로 만드는 흐름(스크립트·인자는 버전 의존):
# 입력: jsonl (한 줄에 {"text": "..."} 형태)
# 출력: <prefix>.bin (토큰), <prefix>.idx (문서 경계 인덱스)
python preprocess_data_for_megatron.py \
--input corpus.jsonl \
--json-keys text \
--tokenizer-library sentencepiece \
--tokenizer-model tokenizer.model \
--output-prefix /data/web \
--append-eod \
--workers 32
# --append-eod: 문서 끝 토큰(EOD) 부착 · --workers: 병렬 토크나이즈
# (참고: 줄 끝 `\` 다음에 인라인 주석을 두면 연속이 깨지므로 주석은 별도 줄에)
# → /data/web.bin, /data/web.idx 생성. 학습은 이 prefix를 가리킴.
산출물:
| 파일 | 내용 |
|---|---|
/data/web.bin | 토큰 ID들이 연속 저장(이진). 모든 문서의 토큰을 이어 붙임. |
/data/web.idx | 각 문서의 시작 오프셋·길이 → 무작위 접근/셔플의 기반. |
학습 config의 data_prefix가 이 'web'을 참조한다. mmap으로 읽는다.
함정: 토크나이저는 학습·전처리·추론에서 동일해야 한다(어휘·특수 토큰). 전처리에 쓴 토크나이저와 다른 걸로 추론하면 토큰 ID가 어긋나 모델이 망가진다.
--append-eod로 문서 경계를 표시해 모델이 문서 경계를 학습/존중하게 한다.
6.2.5 데이터 블렌딩 — 도메인 혼합
LLM은 보통 여러 코퍼스(웹·코드·책·위키·논문)를 섞어 학습한다. 각 데이터셋의 비율(가중치) 이 모델 특성을 좌우한다.
# 블렌딩: 가중치와 prefix를 번갈아(합이 1이 되게 정규화)
data:
data_prefix:
- 0.6
- /data/web
- 0.25
- /data/code
- 0.15
- /data/books
# → 매 배치를 web 60% · code 25% · books 15% 비율로 샘플링
원리·주의:
- 샘플링 비율 = 모델이 각 도메인을 보는 양. 코드를 늘리면 코딩 능력↑(다른 도메인은 상대적↓). 블렌딩이 곧 데이터 커리큘럼.
- 작은 고품질 데이터셋을 과샘플링하면 여러 epoch 반복 → 과적합·암기 위험. 비율은 품질·양·목표로 정하고 검증.
- 중복 제거(dedup)·품질 필터가 선행돼야(6.6 Curator) — 블렌딩은 정제된 데이터 위에서.
견해: 블렌딩 비율은 모델 목표의 직접 표현이다. 코딩 어시스턴트면 코드 비중↑, 다국어면 언어 비율 설계. 다만 작은 데이터셋 과샘플링은 암기를 부르니, 비율·epoch를 검증셋 성능으로 조정한다.
6.2.6 시퀀스 패킹 — 패딩 낭비 제거
문서 길이는 제각각인데 학습은 고정 길이 시퀀스(예 4096)로 배치한다. 순진하게 하면 짧은 문서가 패딩으로 채워져 낭비된다.
주의: 문서 경계에서 어텐션이 섞이지 않게 마스킹한다(문서 간 attention 차단).
- 패킹: 여러 문서를 한 고정 길이 시퀀스에 이어 붙여 패딩을 없앤다 → 유효 토큰/스텝↑(학습 효율↑).
- 경계 마스킹: 한 시퀀스에 여러 문서가 들어가도 문서 간 어텐션을 차단(각 문서가 다른 문서를 못 보게) — 안 그러면 무관한 문서가 섞여 학습 오염. (구현·플래그 버전 의존.)
- EOD 토큰: 문서 끝 표시(6.2.4)로 경계 인지.
핵심: 패킹은 특히 SFT(짧은 예시 많음, 6.4) 에서 큰 차이를 낸다 — 짧은 instruction 예시들이 패딩으로 낭비되지 않게. 사전학습도 긴 문서를 이어 붙여 효율을 높인다. 단 경계 마스킹을 빠뜨리면 문서가 섞여 품질이 망가진다.
6.2.7 중간 점검 (체크포인트)
- [ ] NeMo가 PyTorch+Megatron-Core 위의 생성형 AI 학습 프레임워크이며, 모델·데이터·트레이너·병렬화를 config(YAML/Hydra)로 선언함을 안다.
- [ ] 대규모 학습 데이터의 문제(재토크나이즈·무작위 접근·패딩·메모리)와, 바이너리 인덱싱(.bin/.idx·mmap) 이 그것을 어떻게 푸는지 설명한다.
- [ ] 데이터 블렌딩 가중치가 도메인 혼합·모델 특성을 정함을 안다(과샘플링 위험 포함).
- [ ] 시퀀스 패킹이 패딩 낭비를 없애 유효 처리량을 높임과, 경계 마스킹의 필요를 안다.
- [ ] 토크나이저 일관성(전처리·학습·추론 동일)의 중요성을 안다.
- [ ] 원시 텍스트 → 전처리 → config → 학습의 흐름을 따라간다.
증상→의심: GPU 활용률 낮고 CPU 병목 → 매 스텝 토크나이즈(바이너리 인덱싱 안 함) → 전처리로(3.8). 학습이 느린데 토큰 처리량 낮음 → 패딩 낭비 → 시퀀스 패킹. 모델이 무관 문서 섞어 이상 → 패킹 경계 마스킹 누락. 추론 출력이 깨짐 → 토크나이저 불일치(전처리≠추론). 특정 도메인만 잘함/과적합 → 블렌딩 비율·작은셋 과샘플링. 수 TB 데이터 OOM → mmap 미사용·전부 로드.
6.2.8 트레이드오프·대안
학습 프레임워크 선택.
| 선택 | 누구에게 | 비고 |
|---|---|---|
| NeMo(+Megatron-Core) | 대규모 분산 LLM 학습·엔비디아 스택·config 레시피 | 강력한 병렬·검증 레시피, 학습 곡선·버전 변화 |
| Megatron-LM 직접 | 연구·최대 제어 | 저수준, NeMo가 이를 감쌈 |
| HF Transformers + Trainer/Accelerate | 중소 규모·간편·생태계 | 매우 쉬움, 초대규모 병렬은 상대적 약 |
| PyTorch Lightning·자체 | 커스텀 | 유연, 대규모 병렬은 직접 |
| LoRA만(PEFT) | 커스터마이즈만(6.4) | 풀 학습 프레임워크 불필요할 때 |
견해: 초대규모 사전학습·대규모 분산이면 NeMo/Megatron-Core(검증된 TP/PP/DP·데이터 경로)가 강하다. 중소 규모 파인튜닝·간편함이면 HF 생태계가 충분하고 쉽다(6.1 도구 중립). 대부분의 팀은 사전학습이 아니라 커스터마이즈(SFT/LoRA, 6.4) 가 필요한데, 그것은 NeMo로도 HF로도 가능 — 규모·기존 스택·운영으로 고른다.
비엔비디아 정직: NeMo의 데이터 경로(바이너리 인덱싱·블렌딩·패킹)는 Megatron 계열의 표준 기법이고 HF·다른 프레임워크에도 유사 개념이 있다(packed dataset·streaming·dataset mixing). NeMo의 강점은 대규모 병렬(Megatron-Core)·검증 레시피·엔비디아 스택 통합·음성/멀티모달 컬렉션이다. 중소 규모는 HF가 더 간편할 수 있다 — 규모·요구로 판단.
6.2.9 흔한 함정
- 매 스텝 토크나이즈. CPU 병목·GPU 기아. 오프라인 바이너리 인덱싱(.bin/.idx)으로(3.8).
- 토크나이저 불일치. 전처리·학습·추론의 토크나이저(어휘·특수토큰)가 다르면 토큰 ID가 어긋나 모델 붕괴. 동일하게.
- 패킹 경계 마스킹 누락. 한 시퀀스에 섞인 문서들이 서로 어텐션해 오염. 문서 간 어텐션 차단 확인.
- 작은 데이터셋 과샘플링. 블렌딩 비율로 작은 고품질셋을 과하게 반복 → 암기·과적합. 비율·epoch를 검증으로.
- 품질 필터·dedup 생략. 정제 안 된 데이터로 블렌딩하면 중복·저품질이 학습됨. Curator(6.6) 선행.
- 수 TB를 전부 로드. OOM. mmap·스트리밍으로 필요한 부분만.
- config 없이 코드 하드코딩. 병렬도·데이터·정밀도를 코드에 박으면 스케일·실험이 어렵다. config로 노출.
- 버전 가정한 복붙. NeMo 구조·CLI·클래스가 재편됨(1.0→2.0 등) — 그 버전 문서·예제로.
6.2.10 연습문제
연습 1 (바이너리 인덱싱). 수 TB 텍스트로 LLM을 사전학습할 때 "매 스텝 텍스트를 토크나이즈"하는 방식의 문제 셋과, 바이너리 인덱싱(.bin/.idx + mmap)이 각각을 어떻게 해결하는지 설명하라.
정답
문제: ① CPU 토크나이즈 병목 → GPU가 데이터를 기다려 기아(3.8). ② 무작위 셔플·재개 어려움 → 순차 텍스트 파일은 임의 인덱스 접근·정확한 재개가 힘듦. ③ 메모리 → 수 TB를 적재하지 못함. 해결: ① 토크나이즈를 오프라인 1회로 끝내고 .bin(토큰 ID)을 바로 읽음 → CPU 병목 제거. ② .idx가 문서 경계 오프셋을 가져 임의 인덱스 즉시 접근 → 셔플·결정적 재개(시드·스텝 기반). ③ mmap으로 .bin을 메모리에 모두 적재하지 않고 필요한 부분만 페이지 단위로 → 수 TB도 처리. 결과: GPU가 기아 없이고, 재현·재개 가능하며, 메모리 안에서 실행된다.
연습 2 (블렌딩). 코딩 어시스턴트용 모델을 web·code·books로 학습한다. (a) 블렌딩 비율이 모델에 미치는 영향, (b) 작은 고품질 code 데이터셋을 0.5로 과샘플링하면 무슨 위험이, (c) 비율을 어떻게 정·검증하나?
정답
(a) 블렌딩 비율 = 모델이 각 도메인을 보는 양 → code 비중을 높이면 코딩 능력↑(대신 web/books 노출↓로 그쪽 능력 상대 저하). 비율이 곧 데이터 커리큘럼·모델 특성. (b) code가 작은데 0.5로 과샘플링하면 같은 데이터를 여러 epoch 반복 → 암기·과적합(학습셋 코드를 외우고 일반화↓), 다양성 부족. (c) 비율은 목표(코딩 우선)·각 셋의 양·품질로 1차 설정 후, 검증셋(코딩·일반 둘 다) 성능으로 조정 — code 비중을 높여 코딩이 좋아지되 일반 능력·과적합이 허용선인지. dedup·품질 필터(6.6) 선행. 작은 셋은 과샘플링 배수를 제한.
연습 3 (시퀀스 패킹). SFT 데이터가 평균 300토큰인 instruction 예시 수십만 개다. seq_length=4096으로 학습한다. (a) 패킹 없이 하면 무엇이 낭비되나, (b) 패킹의 효과와 (c) 반드시 해야 할 처리는?
정답
(a) 패킹 없이 예시 하나를 4096 시퀀스에 넣으면 300토큰 + 3796 패딩 → 약 93% 패딩 → 연산·메모리·시간이 패딩에 낭비, 유효 학습 처리량 급락. (b) 패킹: 여러 예시(예 ~13개×300)를 한 4096 시퀀스에 이어 붙여 패딩 거의 0 → 유효 토큰/스텝이 ~13배 → 같은 GPU·시간에 훨씬 많은 예시 학습. SFT처럼 짧은 예시가 많을수록 효과 큼. (c) 문서(예시) 간 어텐션 마스킹 — 한 시퀀스에 섞인 예시들이 서로 어텐션하면 무관한 예시가 문맥에 섞여 학습 오염 → 각 예시가 자기 경계 안만 보도록 마스킹, EOD/경계 토큰으로 구분. 이것을 빠뜨리면 패킹이 품질을 저하시킨다.
연습 4 (토크나이저 일관성). 파인튜닝한 모델을 배포했더니 출력이 깨진 토큰·비정상적인 문자다. 학습은 정상이었다. 토크나이저 관점에서 원인과 점검·해결을 제시하라.
정답
원인 후보: 전처리/학습에 쓴 토크나이저와 추론(서빙)의 토크나이저가 다름 — 어휘·머지 규칙·특수 토큰이 어긋나면 같은 텍스트가 다른 토큰 ID로 인코딩/디코딩되어 모델이 학습한 것과 다른 입력을 받고 출력 디코딩도 깨짐. 점검: 학습 config의 tokenizer(model·vocab)와 배포 토크나이저가 동일 파일·버전인지, 특수 토큰(BOS/EOS/EOD/pad) 정의가 같은지, 추가한 토큰이 양쪽에 반영됐는지. 해결: 전처리·학습·추론에서 정확히 같은 토크나이저를 쓰도록 고정(모델과 함께 토크나이저를 패키징). 토큰 ID round-trip 테스트(encode→decode가 원문 복원)로 검증. 토크나이저 일관성은 데이터 경로의 기본 불변식.
연습 5 (프레임워크 선택). 다음에 NeMo와 HF Transformers 중 무엇이 더 적합한지: (a) 1024 GPU로 새 다국어 모델 계속 사전학습, (b) 단일 8-GPU 노드에서 7B 모델 LoRA 파인튜닝, (c) 음성 인식 + LLM 멀티모달 파이프라인.
정답
(a) NeMo(+Megatron-Core) — 1024 GPU 대규모 분산은 검증된 TP/PP/DP·데이터 경로(바이너리 인덱싱·블렌딩)가 핵심이고 NeMo/Megatron이 그에 특화. 계속 사전학습은 대규모 데이터·병렬이 필수. (b) HF Transformers + PEFT — 단일 노드 LoRA는 간편함·생태계가 충분하고 NeMo의 대규모 병렬이 과함. 빠르게 시작. (단 기존 스택이 NeMo면 NeMo LoRA도 가능, 6.4.) (c) NeMo — 음성(ASR/TTS) 컬렉션 + LLM + 멀티모달을 한 프레임워크에서 다룸이 강점. HF도 가능하나 NeMo가 음성·멀티모달 통합에 유리. 핵심: 초대규모·음성/멀티모달=NeMo, 중소 파인튜닝·간편=HF. 규모·도메인·기존 스택으로 판단(도구 중립, 6.1).
6.2.11 요약 · 다음 장
- NeMo는 PyTorch+Megatron-Core 위의 생성형 AI 학습 프레임워크 — 모델·데이터·트레이너·병렬화를 config(YAML/Hydra)로 선언해 코드 수정 없이 대규모로 스케일한다(컬렉션: LLM·멀티모달·음성).
- 대규모 학습의 데이터 경로가 절반이다: 원시 텍스트를 오프라인 토크나이즈 → 바이너리 인덱싱(.bin/.idx·mmap) 해 재토크나이즈·무작위 접근·메모리 문제를 푼다(GPU 안 기아 상태, 3.8).
- 블렌딩(가중치로 도메인 혼합 = 데이터 커리큘럼, 작은셋 과샘플링 주의)과 시퀀스 패킹(패딩 낭비 제거, 경계 마스킹 필수)이 효율의 핵심.
- 토크나이저 일관성(전처리·학습·추론 동일)은 기본 불변식 — 어기면 모델이 깨진다.
- 정직: NeMo 구조·CLI·config는 버전 재편 잦음(확인 필요), 데이터 기법은 Megatron 표준·도구 중립, 중소 규모는 HF가 간편할 수 있음.
다음 장: 6.3 NeMo II — 병렬화(TP·PP·DP·시퀀스). 이 장에서 데이터를 준비했으니, 이제 그 데이터로 거대 모델을 여러 GPU에 나눠 학습하는 병렬화를 판다 — 4.14·5.9에서 본 텐서·파이프라인·데이터 병렬에 더해 시퀀스 병렬, 메모리·통신 트레이드오프, 그리고 config로 병렬도를 정하는 법을 학습 관점에서 깊게 본다.