본문 바로가기
TYLER SONGBlog
← Blog index
LLM Research5 min read

월말 정리 (Month 1): NumPy ML과 PyTorch MLP를 재현 가능한 포트폴리오로 묶기

이번 달에 만든 머신러닝 코드를 다른 사람도 같은 순서로 실행할 수 있게 정리한다. NumPy 구현과 PyTorch 구현을 각각 저장소로 구성하고, 실행 방법과 결과를 README에 기록한다. 두 저장소를 연결하는 포트폴리오 인덱스 초안도 만든다.

By 송민성

오늘의 목표

Month 1 결과물을 numpy-ml과 torch-basics 저장소로 정리한다. 코드를 처음 보는 사람이 README만 읽고 설치, 실행, 결과 확인까지 할 수 있는 상태를 만든다.

개념과 직관

오늘은 새 이론을 배우지 않는다. 대신 재현 가능성(reproducibility, 같은 코드와 조건으로 비슷한 결과를 다시 얻을 수 있는 성질)을 점검한다.

연구 코드를 요리법에 비유해 보자.

  • 코드: 조리 과정
  • 데이터: 재료
  • 라이브러리 버전: 조리 도구
  • 난수 시드: 재료를 섞는 순서
  • README: 요리법 설명서
  • 실행 결과: 완성된 음식 사진

조리 과정만 있고 재료와 도구 설명이 없다면 다른 사람은 결과를 재현하기 어렵다. 머신러닝 코드도 마찬가지다.

재현 가능한 저장소의 흐름은 다음과 같다.

text
README 읽기
    ↓
필요한 라이브러리 설치
    ↓
정해진 명령으로 코드 실행
    ↓
손실과 정확도 확인
    ↓
기록된 결과와 비교

난수 시드(random seed, 무작위 값을 만드는 시작점을 고정하는 숫자)를 설정하면 가중치 초기값과 데이터 순서를 일정하게 만들 수 있다.

text
같은 코드 + 같은 데이터 + 같은 설정 + 같은 난수 시드
→ 비슷한 실험 결과

단, 실행 환경과 하드웨어가 다르면 결과의 마지막 소수점까지 같지 않을 수 있다. 숫자가 완전히 같은지보다 학습 곡선과 최종 성능이 비슷한지를 확인한다.

코드로 직접 해보기

1. 저장소 구조 만들기

두 구현을 한 저장소에 억지로 합치지 말고 학습 목적에 따라 나눈다.

text
numpy-ml/
├── README.md
├── requirements.txt
├── linear_regression.py
├── logistic_regression.py
└── mlp.py

torch-basics/
├── README.md
├── requirements.txt
├── tensor_basics.py
├── autograd_demo.py
└── mlp.py

터미널에서 폴더를 만든다.

bash
mkdir numpy-ml torch-basics

2. NumPy 구현에 난수 시드와 실행 결과 추가하기

numpy-ml/mlp.py의 시작 부분에 난수 시드를 넣는다.

python
import numpy as np np.random.seed(42) X = np.array([ [0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0], ]) y = np.array([[0.0], [1.0], [1.0], [0.0]]) W1 = np.random.randn(2, 4) * 0.1 b1 = np.zeros((1, 4)) W2 = np.random.randn(4, 1) * 0.1 b2 = np.zeros((1, 1)) def sigmoid(x): return 1 / (1 + np.exp(-x)) for step in range(5000): hidden = np.tanh(X @ W1 + b1) prediction = sigmoid(hidden @ W2 + b2) error = prediction - y loss = np.mean(error ** 2) output_grad = 2 * error / len(X) z2_grad = output_grad * prediction * (1 - prediction) W2_grad = hidden.T @ z2_grad b2_grad = np.sum(z2_grad, axis=0, keepdims=True) hidden_grad = z2_grad @ W2.T z1_grad = hidden_grad * (1 - hidden ** 2) W1_grad = X.T @ z1_grad b1_grad = np.sum(z1_grad, axis=0, keepdims=True) learning_rate = 0.1 W1 -= learning_rate * W1_grad b1 -= learning_rate * b1_grad W2 -= learning_rate * W2_grad b2 -= learning_rate * b2_grad print("최종 손실:", loss) print("예측:") print(np.round(prediction, 3)) # LLM 연결: 행렬곱, 활성화 함수, 손실 계산은 LLM 학습에서도 반복된다.

이 코드는 입력, 은닉층, 출력층으로 이어지는 다층 퍼셉트론(MLP, 여러 층의 선형 계산과 활성화 함수를 연결한 신경망)을 NumPy로 직접 구현한다.

실행 명령은 다음과 같다.

bash
cd numpy-ml python mlp.py

3. PyTorch MLP를 실행 가능한 파일로 정리하기

torch-basics/mlp.py에 데이터, 모델, 학습 반복문을 한 파일로 정리한다.

python
import torch from torch import nn torch.manual_seed(42) X = torch.tensor([ [0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0], ]) y = torch.tensor([[0.0], [1.0], [1.0], [0.0]]) model = nn.Sequential( nn.Linear(2, 4), nn.Tanh(), nn.Linear(4, 1), ) loss_fn = nn.BCEWithLogitsLoss() optimizer = torch.optim.SGD(model.parameters(), lr=0.1) for step in range(5000): logits = model(X) loss = loss_fn(logits, y) optimizer.zero_grad() loss.backward() optimizer.step() with torch.no_grad(): probability = torch.sigmoid(model(X)) print("최종 손실:", loss.item()) print("예측 확률:") print(probability.round(decimals=3)) # LLM 연결: PyTorch의 자동 미분과 최적화 반복문은 LLM 학습 코드의 기초다.

실행한다.

bash
cd torch-basics python mlp.py

NumPy 코드에서는 기울기를 직접 계산했다. PyTorch 코드에서는 자동 미분(automatic differentiation, 계산 과정을 추적해 기울기를 자동 계산하는 기능)이 loss.backward()를 통해 같은 역할을 수행한다.

4. 필요한 라이브러리 기록하기

numpy-ml/requirements.txt:

text
numpy

torch-basics/requirements.txt:

text
torch

설치 명령도 README에 기록한다.

bash
python -m pip install -r requirements.txt

라이브러리 버전을 확인하려면 다음 명령을 실행한다.

bash
python -m pip freeze

실험에 사용한 버전이 중요하다면 출력에서 필요한 라이브러리의 버전을 requirements.txt에 기록한다.

text
numpy==실제로_사용한_버전

확인하지 않은 버전 번호를 임의로 적지 않는다.

5. README 작성하기

각 저장소의 README.md에는 다음 내용을 채운다.

markdown
# 저장소 이름 ## 목적 이 저장소에서 직접 구현하거나 확인한 내용을 설명한다. ## 구현 목록 - 선형 회귀 - 로지스틱 회귀 - 다층 퍼셉트론 ## 실행 환경 - 파이썬 버전: - 주요 라이브러리: - 운영체제: ## 설치 python -m pip install -r requirements.txt ## 실행 python mlp.py ## 실행 결과 최종 손실과 예측값을 붙여 넣는다. ## 배운 점 직접 구현하며 이해한 내용을 세 문장 이내로 기록한다. ## LLM 연결 이 구현이 LLM 학습의 어떤 구성 요소와 연결되는지 한 문장으로 기록한다.

6. 포트폴리오 인덱스 v0 만들기

두 저장소를 연결하는 portfolio-index.md를 만든다.

markdown
# 머신러닝 학습 포트폴리오 ## Month 1 ### NumPy로 구현한 머신러닝 - 저장소: numpy-ml - 핵심 내용: 행렬 연산, 손실 함수, 기울기 계산, 파라미터 갱신 - LLM 연결: 신경망 학습 내부 계산을 직접 확인했다. ### PyTorch 기초와 MLP - 저장소: torch-basics - 핵심 내용: 텐서, 자동 미분, 모델 정의, 학습 반복문 - LLM 연결: 대규모 모델 학습 코드와 같은 기본 실행 흐름을 구현했다.

오늘의 실험

각 구현 파일 마지막에 다음 형식의 주석을 한 줄씩 추가한다.

python
# LLM 연결: 이 코드의 행렬곱은 토큰 표현을 다음 층으로 변환하는 계산과 연결된다.
python
# LLM 연결: 이 손실 계산은 모델의 예측과 정답 차이를 숫자로 만드는 역할을 한다.
python
# LLM 연결: 이 파라미터 갱신 과정은 LLM 학습에서도 같은 기본 원리로 반복된다.

주석을 추가한 뒤 각 저장소를 처음 사용하는 사람처럼 다시 실행한다.

  1. README의 설치 명령만 사용한다.
  2. README의 실행 명령을 복사해 실행한다.
  3. 오류가 나면 오류 메시지와 해결 방법을 README에 기록한다.
  4. 최종 손실과 예측값을 실행 결과에 붙여 넣는다.
  5. 같은 코드를 두 번 실행해 결과가 비슷한지 확인한다.

실험 기록은 다음 형식으로 남긴다.

text
실행 날짜:
파이썬 버전:
난수 시드:
실행 명령:
최종 손실:
예측값:
오류와 해결 방법:
LLM에서 연결되는 부분:

왜 이걸 배우나 (LLM / Post-training 연결)

LLM 연구 업무에서는 모델 성능 숫자만큼 데이터, 설정, 코드 버전, 실행 명령을 함께 남기는 일이 중요하다. 후속 학습(Post-training, 사전 학습된 모델을 목적에 맞게 추가로 학습하는 과정) 실험을 비교하려면 이전 결과를 다시 실행하고 차이의 원인을 추적할 수 있어야 한다.

NumPy 구현은 신경망 내부 계산을 이해했다는 증거가 되고, PyTorch 구현은 실제 연구 코드의 기본 구조를 다룰 수 있다는 증거가 된다.

초보자가 막히는 지점

  • ModuleNotFoundError가 나오면 해당 저장소에서 python -m pip install -r requirements.txt를 실행한다.
  • 파일 이름을 numpy.py나 torch.py로 만들면 실제 라이브러리와 이름이 충돌할 수 있다. 다른 이름을 사용한다.
  • NumPy 코드와 PyTorch 코드의 손실값이 정확히 같지 않아도 된다. 초기화 방식과 손실 함수가 다르면 숫자도 달라진다.
  • README에 실행 명령만 적고 실행 위치를 빠뜨리기 쉽다. 어느 폴더에서 명령을 실행하는지 함께 적는다.
  • 난수 시드를 설정했는데 결과가 다르면 파이썬과 라이브러리 버전, 실행 장치, 데이터 순서를 확인한다.

알고리즘 (병행 1시간)

이번 달 오답 노트의 문제만 다시 푼다. 정답 코드를 보지 않고 문제당 최대 20분 동안 시도한 뒤, 막히면 이전 풀이에서 핵심 아이디어 한 줄만 확인한다.

각 문제에는 다음 세 항목을 기록한다.

text
접근:
입력에서 무엇을 저장하고 어떤 순서로 처리했는가?

시간복잡도:
입력 크기를 n이라고 할 때 연산 횟수가 어떻게 증가하는가?

틀린 이유:
문제 이해, 자료형, 경계값, 반복문 범위, 조건문, 시간 초과 중 무엇이 원인이었는가?

다시 틀린 문제는 정답을 외우지 말고 작은 입력을 손으로 추적한다.

text
입력
→ 변수 값 변화
→ 조건문 선택
→ 최종 출력

재풀이가 끝나면 각 문제를 혼자 해결, 힌트 후 해결, 다시 복습 중 하나로 표시한다.