LabHub
Get started
배우기 러닝패스 코스

MiniMind — Train a Small Language Model Yourself, End to End

Change only the style with MiniMind's LoRA and confirm the base weights are unchanged

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

MiniMind model_lora.pyapply_lora·save_lora·load_lora 로 SFT 기준 모델에 rank 8 LoRA 를 얹는다. 기반 가중치를 얼리고 LoRA 만 학습해 답을 '…다냥.' 말투로 바꾸고, 기반 가중치가 한 비트도 바뀌지 않았는지와 합친 모델이 같은 출력을 내는지 확인한다.

왜 중요한가

LoRA 의 약속은 두 가지다 — 적은 파라미터만 학습한다, 기반 모델은 그대로 둔다. 앞의 것은 메모리와 저장을 줄이고, 뒤의 것은 한 기반 위에 LoRA 여럿을 갈아 끼울 수 있게 한다. 그런데 얼리기를 빼먹으면 두 약속이 모두 조용히 깨진다. 학습은 잘 되고 말투도 바뀌지만 기반 가중치까지 움직여 다른 LoRA 가 모두 틀어진다. 그래서 이 실습은 결과(말투)만 보지 않고, 학습 뒤의 기반 가중치를 원본과 한 텐서씩 견준다. MiniMind 의 구현은 알파 배율이 없고 차원이 같은 Linear 에만 붙는다는 점도 코드로 확인한다.

단계

  1. /opt/mm/ref/sft.pth 를 불러 apply_lora(model, rank=8) 을 하고, LoRA 가 붙은 모듈 이름을 /root/mm/lora/targets.jsonrank·targets 로 적으세요.
  2. 얹기 전과 직후의 로짓 최대 차이를 /root/mm/lora/zero.jsonmax_abs_diff 로 적으세요.
  3. 이름에 lora 가 없는 파라미터를 모두 얼리고, 학습하는 파라미터 수·전체 수·비율을 /root/mm/lora/params.json 에 적으세요.
  4. lora_nyang.jsonl 로 LoRA 만 150걸음 학습해 save_lora/root/mm/lora/lora.pth 를 저장하세요.
  5. 학습이 끝난 모델에서 LoRA 를 뺀 가중치를 /root/mm/lora/base_after.pth 로 저장하세요. 원본 SFT 가중치와 같아야 합니다.
  6. 떼어 둔 질문(lora_nyang_val.jsonl)에서 '냥.' 으로 끝나는 답의 비율을 LoRA 없이·있이 재어 /root/mm/lora/effect.json 에 적으세요.
  7. W + B·A 로 합친 가중치를 /root/mm/lora/merged.pth 로 저장하세요.
  8. /root/mm/lora/report.md## 어디에 붙나 ## 무엇이 바뀌나 ## 합치기 세 절을 쓰고, 학습한 파라미터 수와 LoRA 를 얹은 뒤의 '냥' 비율을 넣으세요.

참고

LoRA 는 어디에 붙나

mmkit.load_model("/opt/mm/ref/sft.pth") 로 불러 apply_lora(model, rank=8) 을 한 뒤, lora 속성이 생긴 모듈의 이름을 순서대로 /root/mm/lora/targets.json{"rank": 8, "targets": [...]} 로 적으세요.

model.named_modules() 를 돌며 hasattr(m, 'lora') 인 것을 모으면 됩니다. apply_lora 는 in_features == out_features 인 Linear 에만 붙습니다 — 우리 모델에서 어느 투영이 그 조건을 만족하는지 먼저 짐작해 보세요.

얹은 직후에는 아무것도 바뀌지 않는다

/opt/mm/ref/val.npy 앞 64토큰에 대한 로짓을 apply_lora 전과 직후에 구해 최대 절대 차이를 /root/mm/lora/zero.jsonmax_abs_diff 로 적으세요.

MiniMind 의 LoRA 는 A 를 정규분포로, B 를 0 으로 초기화합니다. B·A 가 0 이라 곁가지의 출력이 0 이고, 학습은 '기반 모델에서 얼마나 벗어날까' 를 0 부터 배웁니다.

LoRA 만 학습하도록 얼린다

학습 스크립트 /root/mm/lora/train_lora.py 를 쓰기 시작하세요 — LoRA 를 얹은 모델에서 이름에 lora 가 없는 파라미터는 requires_grad=False, 있는 것은 True 로 두고, 학습하는 파라미터 수(trainable)·전체 수(total)·비율(ratio)을 /root/mm/lora/params.json 에 적으세요.

MiniMind 의 train_lora.py 가 쓰는 규칙 그대로입니다. 곳마다 A(8×128) + B(128×8) 이고 붙은 곳의 수를 곱하면 됩니다. 전체에는 LoRA 파라미터도 들어갑니다.

말투를 LoRA 로 배운다

/opt/mm/data/lora_nyang.jsonlmmkit.sft_encode 로 바꿔, LoRA 파라미터만 AdamW(lr 5e-3)에 넘겨 150걸음(배치 16·시드 42) 학습하고 save_lora(model, "/root/mm/lora/lora.pth")/root/mm/lora/lora.pth 를 저장하세요.

save_lora 는 LoRA 가 붙은 모듈마다 …lora.A.weight·…lora.B.weight 만 fp16 으로 저장합니다. 파일 크기를 기반 모델(4MB 남짓)과 견줘 보세요. 채점기는 열쇠가 LoRA 뿐인지, B 가 0 에서 움직였는지 봅니다.

기반 가중치는 그대로인가

학습이 끝난 모델의 state_dict 에서 이름에 .lora. 가 든 것을 뺀 나머지를 /root/mm/lora/base_after.pth 로 저장하세요. 채점기가 원본 /opt/mm/ref/sft.pth 와 텐서마다 견줍니다.

얼리기가 제대로 됐다면 한 비트도 다르지 않습니다. requires_grad 를 끄지 않고 옵티마이저에 모든 파라미터를 넘겼다면 기반 가중치도 기울기를 받아 움직여 여기서 걸립니다. PyTorch 옵티마이저는 기울기가 없는(None) 파라미터를 건너뛰므로, 얼리기만 제대로 하면 옵티마이저에 무엇을 넘겼든 기반은 그대로입니다.

말투가 얼마나 바뀌었나

lora_nyang_val.jsonl 의 질문 60개에 욕심쟁이로 답하게 해 '냥.' 으로 끝나는 답의 비율을 기반 모델(base_rate)과 LoRA 를 얹은 모델(lora_rate)에서 재어 /root/mm/lora/effect.json 에 적으세요.

LoRA 를 얹은 모델은 기반 모델을 불러 apply_lora(rank=8)load_lora(model, 경로) 로 만듭니다. 떼어 둔 질문은 LoRA 학습 때 본 적 없는 조합이라, 말투가 '질문 모양' 이 아니라 '답하는 방식' 으로 배워졌는지를 봅니다.

합쳐서 곁가지를 없앤다

lora.pth 의 A·B 로 해당 모듈의 가중치에 B @ A 를 더해(알파 배율 없이) 합친 state_dict 를 /root/mm/lora/merged.pth 로 저장하세요. 채점기는 이 가중치를 LoRA 없는 모델에 넣어, LoRA 를 얹은 모델과 같은 로짓을 내는지 봅니다.

A 의 모양은 (rank, 입력), B 는 (출력, rank) 라 B @ A 가 (출력, 입력) — 원래 weight 와 같은 모양입니다. MiniMind 의 merge_lora 도 같은 일을 하고 fp16 으로 저장합니다. 여기서는 비교를 위해 fp32 로 저장합니다.

LoRA 의 약속을 확인한 기록

/root/mm/lora/report.md## 어디에 붙나 ## 무엇이 바뀌나 ## 합치기 세 절을 쓰고, 3단계의 trainable 과 6단계의 lora_rate 를 숫자로 넣으세요.

LoRA 파일이 기반 모델의 몇 분의 일인지, 알파 배율이 없는 구현이라 rank 를 바꿀 때 무엇을 다시 봐야 하는지도 한 줄 적어 보세요.