실습 준비: 파이썬 · 넘파이 · scikit-learn · PyTorch 최소한
넘파이 배열 · 브로드캐스팅 · scikit-learn fit/predict · PyTorch 자동 미분을 실행하며 익히는 실습 준비
먼저 알아야 할 것
다루는 내용
과정 내내 쓰는 세 도구의 최소한을 실제 실행 출력과 함께 익힙니다. 넘파이 배열의 모양 · 원소별 곱과 행렬 곱 · 축과 브로드캐스팅 · 벡터화(반복문 대비 속도 측정), scikit-learn 추정기의 fit · predict · transform 흐름과 데이터 누수 방지, PyTorch 텐서 · autograd · 네 단계 학습 루프를 다룹니다.
해설
실습 준비: 파이썬 · 넘파이 · scikit-learn · PyTorch 최소한
이 과정의 실습과 프로젝트는 넘파이(NumPy) · scikit-learn · PyTorch 세 도구로 합니다. 이 수업은 세 도구에서 꼭 필요한 것만 — 배열과 모양(shape), 브로드캐스팅, 벡터화,
fit·predict·transform, 텐서와 자동 미분 — 을 실제로 실행한 코드와 출력으로 익힙니다.
| 항목 | 내용 |
|---|---|
| 참고 자료 | NumPy 'absolute beginners' 안내 · numpy.org/doc/stable/user/absolute_beginners.html, scikit-learn 'Getting Started', PyTorch 튜토리얼 |
| 트랙 · 수준 | 입문 · 입문 |
| 이 수업의 범위 | 설치, 배열 만들기와 모양, 원소별 연산 · 행렬 곱, 축(axis), 브로드캐스팅, 벡터화, scikit-learn의 추정기 API(fit · predict · transform · score), 학습 · 테스트 나누기, PyTorch 텐서 · autograd · 최소 학습 루프 |
| 먼저 알면 좋은 것 | 파이썬 기초 문법(변수 · 리스트 · for 문 · 함수) |
| 실행 환경 | Python 3.14, numpy 2.5.1, scikit-learn 1.9.0, torch 2.14.0+cpu, CPU (이 글의 출력은 모두 이 환경에서 실제로 실행한 결과) |
들어가기 전에
- AI란 무엇인가 — 모델 · 파라미터 · 손실 · 학습 · 추론이라는 낱말을 알고 오면 코드가 무엇을 하는지 바로 보입니다.
- 구글 머신러닝 단기 집중 과정 — 선형 회귀와 경사 하강법을 한 번 본 뒤라면 6절의 학습 루프가 쉽게 읽힙니다.
한눈에 보기
- 넘파이 배열의 모양(shape)을 읽고, 원소별 곱
*과 행렬 곱@을 구분합니다. - 브로드캐스팅 규칙으로 "열마다 평균 빼기" 같은 연산을 반복문 없이 씁니다.
- 반복문 대신 벡터화하면 왜 수백~수천 배 빨라지는지 직접 잽니다.
- scikit-learn의 모든 모델이 공유하는
fit→predict/transform흐름을 익힙니다. - PyTorch의 자동 미분(autograd)으로 기울기를 구하고, 다섯 줄짜리 학습 루프를 이해합니다.
0. 설치
가장 단순한 방법은 파이썬을 설치한 뒤 터미널에서 다음을 실행하는 것입니다.
python -m pip install numpy scikit-learn torchGPU가 없어도 이 과정의 모든 실습은 CPU로 돌아갑니다. PyTorch의 GPU 판 설치 명령은 운영체제와 CUDA 버전에 따라 다르므로 PyTorch 공식 설치 페이지의 안내를 따르세요. 브라우저만으로 하고 싶다면 Google Colab 같은 노트북 환경에도 세 도구가 대개 미리 깔려 있습니다.
1. 넘파이 배열과 모양
직관(해설용 비유): 넘파이 배열은 "같은 종류의 숫자를 빈틈없이 줄 세운 표"입니다. 엑셀 표처럼 행과 열이 있고, 표의 크기를 모양(shape)이라고 합니다.
import numpy as np
x = np.array([1.0, 2.0, 3.0])
A = np.array([[1, 2, 3],
[4, 5, 6]])
print(x.shape, A.shape, A.dtype)
print(A.T.shape)실행 출력:
(3,) (2, 3) int64
(3, 2)| 표현 | 뜻 |
|---|---|
(3,) | 원소 3개인 1차원 배열(벡터) |
(2, 3) | 2행 3열 2차원 배열(행렬) |
dtype | 원소 자료형 — 정수는 int64, 소수는 float64 |
A.T | 전치(transpose) — 행과 열을 바꿈, 모양 (3, 2) |
왜 모양부터 보나: 실습 오류의 대부분은 "모양이 안 맞는다"입니다. 코드를 짤 때 배열마다 모양을 주석으로 적어 두는 습관이 가장 효과적인 디버깅입니다. 머신러닝에서는 보통 데이터 행렬 의 모양을 (예제 수, 특성 수)로 둡니다. scikit-learn 문서도 입력 를 (n_samples, n_features) 모양, 즉 행이 예제 · 열이 특성인 표로 받는다고 설명합니다.
2. 원소별 연산과 행렬 곱
print(x * 2) # 원소마다 2배
print(x * x) # 원소끼리 곱
print(A @ x) # 행렬 곱 (2×3)·(3,) → (2,)실행 출력:
[2. 4. 6.]
[1. 4. 9.]
[14. 32.]숫자로 해 보기(해설용): A @ x의 첫 원소는 의 첫 행 과 의 내적 , 둘째는 입니다. 출력과 같습니다. 행렬 곱은 선형대수 1에서 자세히 봅니다.
| 연산자 | 뜻 | 모양 규칙 |
|---|---|---|
* | 원소별 곱(element-wise) | 모양이 같거나 브로드캐스팅 가능해야 함 |
@ | 행렬 곱 | 앞의 열 수 = 뒤의 행 수 |
3. 축(axis)과 브로드캐스팅
직관: axis=0은 "행 방향으로 접기(열마다 계산)", axis=1은 "열 방향으로 접기(행마다 계산)"입니다. 브로드캐스팅(broadcasting)은 모양이 다른 배열끼리 연산할 때 작은 쪽을 자동으로 늘려 맞추는 규칙입니다.
규칙(넘파이 문서): 두 배열의 모양을 끝 차원부터 비교해, 각 차원이 같거나 한쪽이 1이면 호환됩니다. 그렇지 않으면 ValueError: operands could not be broadcast together가 납니다.
X = np.array([[1.0, 10.0],
[2.0, 20.0],
[3.0, 30.0]]) # 모양 (3, 2)
mu = X.mean(axis=0) # 열 평균, 모양 (2,)
sd = X.std(axis=0) # 열 표준편차, 모양 (2,)
Z = (X - mu) / sd # (3,2) - (2,) → 브로드캐스팅
print(mu, sd)
print(np.round(Z, 3))실행 출력:
[ 2. 20.] [0.81649658 8.16496581]
[[-1.225 -1.225]
[ 0. 0. ]
[ 1.225 1.225]]숫자로 해 보기(해설용): 첫 열 의 평균은 2, 편차 의 제곱 평균은 , 표준편차는 입니다. 따라서 표준화 값은 입니다. 넘파이 std는 기본으로 으로 나누는 표준편차(ddof=0)를 씁니다. 로 나누려면 ddof=1을 줍니다.
| 모양 A | 모양 B | 결과 |
|---|---|---|
(3, 2) | (2,) | (3, 2) — B를 행마다 복사한 것처럼 |
(3, 1) | (4,) | (3, 4) — 양쪽 다 늘어남 |
(3, 2) | (3,) | 오류 — 끝 차원 2와 3이 다르고 둘 다 1이 아님 |
세 번째 경우에 "행마다 다른 값을 빼고" 싶다면 B[:, None]으로 모양을 (3, 1)로 바꿔 줍니다.
4. 벡터화: 반복문을 배열 연산으로
직관: 파이썬 for 문은 원소 하나마다 해석기를 거칩니다. 넘파이 배열 연산은 같은 일을 미리 컴파일된 코드로 한꺼번에 합니다. 그래서 벡터화(vectorization)가 머신러닝 코드의 기본 습관입니다.
import time
rng = np.random.default_rng(0)
a = rng.standard_normal(1_000_000); b = rng.standard_normal(1_000_000)
t0 = time.perf_counter(); s = 0.0
for i in range(len(a)): s += a[i] * b[i]
t1 = time.perf_counter(); s2 = a @ b; t2 = time.perf_counter()
print(f"반복문 {t1-t0:.3f}s, 벡터화 {t2-t1:.5f}s, 같은 값? {np.isclose(s, s2)}")실행 출력(이 환경에서 한 번 잰 값 — 컴퓨터마다 다름):
반복문 1.834s, 벡터화 0.00075s, 같은 값? True백만 개 원소의 내적에서 두 방식의 결과는 같고, 시간은 이 측정에서 2,000배 넘게 차이 났습니다. 신경망 한 층의 계산도 결국 "행렬 곱 + 원소별 함수"라서, 벡터화해 두면 GPU로 옮길 때도 그대로 빨라집니다.
5. scikit-learn: 모든 모델이 같은 사용법
직관: scikit-learn의 모델은 모두 추정기(estimator)라는 같은 틀을 따릅니다. 배우는 것은 fit, 쓰는 것은 predict 또는 transform입니다. 모델을 바꿔도 코드는 거의 그대로입니다.
| 메서드 | 하는 일 | 예 |
|---|---|---|
fit(X, y) | 데이터로 파라미터를 학습 | 회귀 계수, 평균 · 표준편차 |
predict(X) | 새 입력의 예측 | 범주, 숫자 |
predict_proba(X) | 범주별 확률 (분류기 일부) | [[0.97, 0.03, 0.0]] |
transform(X) | 학습한 규칙으로 데이터 변환 | 표준화, PCA |
score(X, y) | 기본 평가 지표 | 분류기는 정확도 |
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
Xi, yi = load_iris(return_X_y=True)
print(Xi.shape, yi.shape)
Xtr, Xte, ytr, yte = train_test_split(Xi, yi, test_size=0.25,
random_state=0, stratify=yi)
sc = StandardScaler().fit(Xtr) # 학습 데이터로만 평균 · 표준편차 학습
clf = LogisticRegression(max_iter=1000).fit(sc.transform(Xtr), ytr)
print("테스트 정확도:", round(clf.score(sc.transform(Xte), yte), 3))
print("첫 3개 예측:", clf.predict(sc.transform(Xte[:3])), "정답:", yte[:3])
print("확률:", np.round(clf.predict_proba(sc.transform(Xte[:1])), 3))실행 출력:
(150, 4) (150,)
테스트 정확도: 0.974
첫 3개 예측: [0 0 0] 정답: [0 0 0]
확률: [[0.971 0.029 0. ]]붓꽃(iris) 데이터는 꽃 150송이의 특성 4개(꽃받침 · 꽃잎의 길이와 너비)와 품종 3개 라벨로 이뤄진, scikit-learn에 들어 있는 작은 데이터입니다. 테스트 38송이 중 37송이를 맞혀 정확도 0.974가 나왔습니다.
왜 fit을 학습 데이터에만 하나: 표준화의 평균 · 표준편차를 전체 데이터로 구하면 테스트 데이터의 정보가 학습에 섞입니다. 이것이 데이터 누수(data leakage)입니다. 규칙은 "변환기도 학습 데이터로만 fit하고, 테스트에는 transform만" 입니다. 이 실수를 구조적으로 막는 Pipeline은 특성 공학과 데이터 전처리에서 다룹니다. stratify=yi는 학습 · 테스트에 품종 비율을 똑같이 맞추라는 뜻이고, random_state는 결과를 다시 똑같이 얻기 위한 난수 시드입니다.
6. PyTorch: 텐서와 자동 미분
직관: PyTorch의 텐서(tensor)는 넘파이 배열과 거의 같지만 두 가지가 더 있습니다. ① GPU로 옮길 수 있고, ② 계산 과정을 기억해 기울기(미분값)를 자동으로 구합니다. 신경망 학습에 필요한 것은 바로 이 기울기입니다.
import torch
torch.manual_seed(0)
t = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
print(t.shape, t.dtype)
print(t @ t)
w = torch.tensor(2.0, requires_grad=True)
y = w**2 + 3*w
y.backward()
print("y =", y.item(), " dy/dw =", w.grad.item())실행 출력:
torch.Size([2, 2]) torch.float32
tensor([[ 7., 10.],
[15., 22.]])
y = 10.0 dy/dw = 7.0숫자로 해 보기(해설용): 를 미분하면 이고, 에서 입니다. PyTorch가 낸 7.0과 같습니다. requires_grad=True는 "이 값에 대한 기울기를 추적하라", backward()는 "끝에서부터 거꾸로 미분하라"는 뜻입니다. 거꾸로 미분하는 원리(역전파)는 역전파를 계산 그래프로 손계산하기에서 봅니다.
다섯 줄 학습 루프
데이터 을 선형 모델로 배우는 가장 작은 예입니다.
xs = torch.tensor([[0.0],[1.0],[2.0],[3.0]]); ys = 2*xs + 1
model = torch.nn.Linear(1, 1) # y = w x + b
opt = torch.optim.SGD(model.parameters(), lr=0.1)
for step in range(200):
opt.zero_grad() # ① 지난 기울기 지우기
loss = ((model(xs) - ys)**2).mean() # ② 예측 → 손실(MSE)
loss.backward() # ③ 기울기 계산
opt.step() # ④ 파라미터 갱신
print("학습된 w, b:", round(model.weight.item(), 3), round(model.bias.item(), 3),
" 손실:", f"{loss.item():.2e}")실행 출력:
학습된 w, b: 2.0 1.0 손실: 1.48e-12flowchart LR
A[데이터 xs, ys] --> B[모델 예측 model xs]
B --> C[손실 계산 MSE]
C --> D[loss.backward 기울기]
D --> E[opt.step 파라미터 갱신]
E --> F[opt.zero_grad 기울기 초기화]
F --> B
E --> G[학습 끝: w≈2, b≈1]왜 zero_grad가 필요한가: PyTorch는 기울기를 덮어쓰지 않고 더해 쌓습니다. 여러 묶음의 기울기를 모으고 싶을 때 편하게 하려는 설계입니다. 그래서 매 걸음 처음에 지우지 않으면 지난 기울기가 섞여 학습이 엉뚱하게 흘러갑니다. 이 네 단계(지우기 → 손실 → backward → step)는 앞으로 나올 모든 PyTorch 코드에서 똑같이 반복됩니다.
세 도구는 언제 쓰나
| 도구 | 잘하는 일 | 이 과정에서 |
|---|---|---|
| 넘파이 | 배열 계산, 손계산 검산 | 수학 트랙 검산, 넘파이로 신경망 만들기 |
| scikit-learn | 표 형태 데이터의 고전 머신러닝, 전처리 · 평가 | 머신러닝 트랙 전체, 손글씨 숫자 분류기 |
| PyTorch | 신경망 정의 · 자동 미분 · GPU 학습 | 딥러닝 트랙, PyTorch로 CNN 학습하기 |
연습 문제
1. 모양 (5, 3)인 X와 (3,)인 w가 있습니다. X @ w와 X * w의 모양은?
풀이: X @ w는 (5×3)·(3,) → (5,)입니다. X * w는 브로드캐스팅으로 w를 행마다 곱해 (5, 3)입니다.
2. 모양 (4, 3)인 X에서 행마다(예제마다) 평균을 빼려고 X - X.mean(axis=1)을 쓰면 왜 오류가 나고, 어떻게 고치나요?
풀이: X.mean(axis=1)의 모양은 (4,)라서 (4, 3)과 끝 차원 3 · 4가 맞지 않습니다. X - X.mean(axis=1, keepdims=True) 또는 X - X.mean(axis=1)[:, None]로 모양을 (4, 1)로 만들면 됩니다.
3. y = 3*w**2에서 w = 1일 때 y.backward() 뒤 w.grad는?
풀이: 입니다.
4. 아래 코드의 문제점은?
sc = StandardScaler().fit(X) # X는 전체 데이터
Xtr, Xte = train_test_split(sc.transform(X), test_size=0.2)풀이: 전체 데이터로 평균 · 표준편차를 구해 테스트 정보가 새어 들어갑니다(데이터 누수). 먼저 나누고, 학습 데이터로만 fit한 뒤 두 묶음에 transform해야 합니다.
스스로 점검
- 넘파이에서
*와@는 어떻게 다른가요?- 답:
*는 같은 자리 원소끼리 곱하는 원소별 곱이고,@는 행과 열의 내적으로 이뤄진 행렬 곱입니다.
- 답:
- scikit-learn에서
fit과transform을 나누어 둔 이유는?- 답: 학습 데이터에서 배운 규칙(평균, 주성분 등)을 테스트 · 실서비스 데이터에 똑같이 적용하기 위해서입니다. 새 데이터로 다시
fit하면 규칙이 바뀌고 누수가 생깁니다.
- 답: 학습 데이터에서 배운 규칙(평균, 주성분 등)을 테스트 · 실서비스 데이터에 똑같이 적용하기 위해서입니다. 새 데이터로 다시
- PyTorch 학습 루프에서
loss.backward()와opt.step()은 각각 무엇을 하나요?- 답:
backward()는 손실에 대한 각 파라미터의 기울기를 계산해.grad에 쌓고,step()은 그 기울기로 파라미터를 실제로 갱신합니다.
- 답:
용어 정리
| 용어 | 쉬운 뜻 |
|---|---|
| 배열(array) · 텐서(tensor) | 같은 자료형 숫자를 여러 차원으로 늘어놓은 묶음 |
| 모양(shape) | 배열의 각 차원 크기 |
| 축(axis) | 계산을 따라 접을 차원 |
| 브로드캐스팅(broadcasting) | 모양이 다른 배열을 자동으로 늘려 맞추는 규칙 |
| 벡터화(vectorization) | 반복문 대신 배열 연산 한 번으로 쓰기 |
| 추정기(estimator) | scikit-learn에서 fit으로 학습하는 모든 객체 |
| 데이터 누수(data leakage) | 평가 데이터 정보가 학습에 섞이는 실수 |
| 난수 시드(random seed) | 같은 난수를 다시 얻기 위한 시작값 |
| 자동 미분(autograd) | 계산 과정을 기록해 기울기를 자동으로 구하는 기능 |
| 옵티마이저(optimizer) | 기울기로 파라미터를 고치는 규칙(SGD, Adam 등) |
더 깊이 보려면
NumPy의 'absolute beginners' 안내는 배열 만들기 · 인덱싱 · 모양 바꾸기 · 브로드캐스팅 · 기본 통계를 한 페이지에 모아 두었고, 'Broadcasting' 문서는 규칙을 그림과 함께 설명합니다. scikit-learn 'Getting Started'는 추정기 · 변환기 · 파이프라인 · 교차 검증을 짧게 소개합니다. PyTorch 공식 튜토리얼의 'Learn the Basics' 연재는 텐서 · 데이터 로딩 · 모델 만들기 · 자동 미분 · 최적화를 순서대로 다룹니다.
다음에 볼 것
- 선형대수 1: 벡터 · 행렬 · 내적 · 행렬 곱 · 노름 — 오늘 쓴
@가 수학적으로 무엇인지 손으로 계산합니다. - 미분 최소한 —
backward()가 구한 기울기의 뜻을 배웁니다. - AX란 무엇인가 — 직장인 경로: 입문 다음은 AX(AI 전환) 트랙입니다
라이선스
이 글은 원문 번역이 아니라 KAITABASE가 직접 쓴 해설이며, 코드는 직접 작성해 위 환경에서 실행했습니다. 숫자 예는 설명용입니다. NumPy · scikit-learn 문서는 BSD 계열 라이선스, PyTorch 튜토리얼은 BSD-3 라이선스로 공개되어 있습니다.
출처
- 참고: numpy.org/doc/stable/user/absolute_beginn…
- 참고: numpy.org/doc/stable/user/basics.broadcas… (브로드캐스팅 규칙)
- 참고: scikit-learn.org/stable/getting_started.h… (fit ·
(n_samples, n_features)) - 참고: scikit-learn.org/stable/modules/generated… (
numpy.std(x, ddof=0)과 같은 표준편차) - 참고: docs.pytorch.org/tutorials/beginner/basic… (PyTorch Learn the Basics)
AI 원리부터 이해하기 · 입문 모듈
입문 모듈 학습
이 글은 입문 모듈의 학습 자료입니다. 전체 6개 자료 중 4번째입니다.
출처·원문
원문 보기요약만