🧩 PyTorch란?
PyTorch는 Meta에서 개발한 오픈소스 머신러닝 및 딥러닝 프레임워크다.
PyTorch는 딥러닝을 쉽게 구현하도록 만들어진 오픈소스 라이브러리다.
- PyTorch: AI 연구자 친화적. 명명백백한 딥러닝의 왕
- TersorFlow: 개발자 친화적. PyTorch가 없는 시대에 태어난 범부
신경망 학습은 다음 과정으로 요약할 수 있다.
- 데이터를 모델에 넣는다
- 예측값을 만든다
- 정답과 비교해서 얼마나 틀렸는지 계산한다 → 손실(loss)
- "어느 방향으로 바꾸면 덜 틀릴까?"를 미분으로 계산한다 → gradient
- 모델의 숫자들(가중치)를 조금씩 수정한다
- 이 과정을 아주 많이 반복한다
PyTorch는 특히 3~5번을 편하게 처리해준다.
PyTorch vs Scikit-learn
Scikit-learn은 전통적인 머신러닝(선형회귀 등)을 다루며, 정형 데이터 분석 및 간단한 전처리에 특화되어 있다. 주로 CPU 연산을 기반으로 하며, 소~중규모 데이터 분석에 적합하다.
model.fit(X_train, y_train)
pred = model.predict(X_test)
반면 PyTorch는 딥러닝 및 인공신경망(CNN, RNN, Transformer 등)을 다루며, 이미지/자연어 등 대규모 비정형 데이터 처리에 특화되어 있다. GPU 및 TPU 가속을 지원하며, 대규모 데이터와 복잡한 신경망 고속 학습에 적합하다.
for epoch in range(epochs):
pred = model(X_train)
loss = loss_fn(pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
PyTorch는 기본적으로 학습 과정을 직접 작성해야 하기 때문에 더 복잡해 보이지만, 대신 모델 구조/손실 함수/학습 방식/GPU 사용/이미지 등 복잡한 데이터 처리/특수한 연구·실험 구조 등을 자유롭게 설정할 수 있다.
💡 scikit-learn은 간편한 머신러닝 도구, PyTorch는 신경망을 세밀하게 만들고 학습시키는 도구다.
PyTorch vs NumPy
갑자기 수치 통계를 다루는 NumPy가 왜 나오나 싶은데, PyTorch는 Numpy array 대신 Tensor라는 자료구조를 사용한다.
- 벡터 = 1차원 Tensor
- 행렬 = 2차원 Tensor
- 행렬 N개 = 3차원 Tensor
- 3차원 텐서 N개 = 4차원 Tensor
import torch
print(torch.tensor([1, 2, 3])) # tensor([1, 2, 3])
print(torch.tensor([[1, 2, 3], [4, 5, 6]]))
# tensor([[1, 2, 3],
# [4, 5, 6]])
문법은 Numpy array와 거의 같아 비슷하게 쓸 수 있으며, Numpy array과의 변환도 가능하다.
import torch
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
print(a + b) # tensor([5., 7., 9.])
print(a * b) # tensor([ 4., 10., 18.])
print(a.mean()) # tensor(2.)
import numpy as np
arr1 = np.array([1, 2, 3])
tensor = torch.tensor(arr1)
arr2 = tensor.numpy()
print(arr1) # [1 2 3]
print(arr2) # [1 2 3]
PyTorch로 신경망, 딥러닝을 다룰 때는 Numpy를 쓰지 않고 Tensor만으로도 된다고 한다. 다만 실무에서는 보통 Pandas/Numpy로 데이터 전처리를 하고, 모델에 넣기 직전에 Tensor로 변환한다고 한다.
X_tensor = torch.tensor(X.values, dtype=torch.float32)
y_tensor = torch.tensor(y.values, dtype=torch.float32)
자동 미분 (Autograd)
GD(Gradient Descent)는 손실 함수(Loss Function)의 값을 최소화하여 모델의 성능을 최적화하기 위해 가중치를 조절하는 경사 하강법 알고리즘을 의미한다. 학습(GD)을 할 때 미분을 할 수 있다면 학습 속도가 빨라진다고 한다. Tensor는 내부적으로 미분 계산을 빠르게 하기 위해, 우리가 직접 미분식을 구현하지 않아도 자동으로 미분을 한다고 한다.
x = torch.tensor(2.0, requires_grad=True) # 미분 추적 기능 ON
y = x**2 + 3*x
y.backward() # 기울기 계산
print(x.grad) # tensor(7.)
중간 평가(Validation)나 최종 평가(Test)를 할 때는 미분 추적 기능을 OFF시켜 메모리나 계산속도 저하를 줄이는 것이 좋다.
with torch.no_grad(): # 미분 추적 기능 OFF
y = x ** 2 + 5*x + 10
# y.backward() # 에러 발생
신경망의 구성요소
import torch # Tensor, 기본 연산
import torch.nn as nn # 신경망 층(Layer), 손실함수
import torch.optim as optim # 최적화 알고리즘
nn.Linear()
신경망의 기본 수식은 선형함수인 𝓎 = 𝓌𝓍 + 𝒷이다.
여기서 𝓌는 Weight(기울기), 𝒷는 Bias(절편)를 의미한다. PyTorch에서는 nn.Linear()를 사용해서 표현한다. 신경망도 본질적으로는 수 많은 𝓌와 𝒷를 가진 함수로, 학습의 목표는 손실이 작아지도록 𝓌와 𝒷를 수정하는 것이다.
- gradient > 0 → 값을 줄임
- gradient < 0 → 값을 늘림
- 어느 정도 움직일지는
lr(learning rate)이 결정
gradient 계산은 backward()에서 하고, 실제 가중치 수정은 optimizer가 한다.
import torch
import torch.nn as nn
import torch.optim as optim
# y = 2x + 1에 가까운 데이터
X = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
y = torch.tensor([[3.0], [5.0], [7.0], [9.0]])
print(X.shape) # torch.Size([4, 1]) : 데이터 4개, Feature 1개
# 모델: y = wx + b
model = nn.Linear(1, 1) # 입력 특성 1개, 출력값 1개
# 손실함수와 optimizer 설정
loss_fn = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01) # GD optimizer
# 학습하기
for epoch in range(1000):
# 1. 예측
pred = model(X)
# 2. 손실 계산
loss = loss_fn(pred, y)
# 3. 이전 gradient 초기화
optimizer.zero_grad()
# 4. gradient 계산
loss.backward()
# 5. 가중치 업데이트
optimizer.step()
if (epoch + 1) % 100 == 0:
print(f"Epoch {epoch + 1}, Loss: {loss.item():.4f}")
# 학습된 가중치와 편향 확인
print("weight:", model.weight.item())
print("bias:", model.bias.item())
# 새 데이터 예측
new_x = torch.tensor([[5.0]])
prediction = model(new_x)
print("x=5일 때 예측:", prediction.item())
'''
Epoch 100, Loss: 0.0111
Epoch 200, Loss: 0.0061
Epoch 300, Loss: 0.0033
Epoch 400, Loss: 0.0018
Epoch 500, Loss: 0.0010
Epoch 600, Loss: 0.0006
Epoch 700, Loss: 0.0003
Epoch 800, Loss: 0.0002
Epoch 900, Loss: 0.0001
Epoch 1000, Loss: 0.0001
weight: 1.9941203594207764
bias: 1.0172868967056274
x=5일 때 예측: 10.987889289855957
'''
Matplotlib 또는 Seaborn을 사용해 시각화할 수 있다.
import matplotlib.pyplot as plt
with torch.no_grad():
x_line = torch.linspace(X.min(), X.max(), 100).reshape(-1, 1)
y_line = model(x_line)
plt.plot(x_line, y_line)
plt.grid(True)
plt.show()

nn.Module: 신경망 모델을 직접 정의하기
nn.Sequential()을 사용해 여러 층을 쌓는 함수 중첩을 구현할 수 있다.
model = nn.Sequential(
nn.Linear(10, 64),
nn.ReLU(),
nn.Linear(64, 1)
)
여기서 ReLU(Rectified Linear Unit)는 입력값이 0 이하면 0을 출력하고, 0보다 크면 입력값 그대로를 출력하는 활성화 함수이다.

선형층만 여러개 쌓아도 결국 하나의 선형 함수와 같다. 그래서 복잡한 곡선, 이미지 패턴, 비선형 관계를 학습하기 어렵다. 여기서 ReLU 같은 활성화 함수를 중간에 넣으면 비선형 관계를 표현할 수 있다.
비선형 방정식의 중첩으로 모든 비선형을 나타낼 수 있음이 증명되었고, 수학적 증명은 나보다 똑똑한 사람들이 이미 잘 정리했으므로 여기서는 생략하도록 하겠다.
비선형 방정식 학습 예시
아래와 같은 문식이 함수를 만들어 보자.

- 이미지 위에 점을 직접 찍기
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
img = Image.open("munsik.png")
plt.figure(figsize=(10, 7))
plt.imshow(img)
# Enter를 누르면 점 선택 종료
clicked_points = plt.ginput(n=-1, timeout=0)
plt.close()
points = np.array(clicked_points, dtype=np.float32)
print("선택한 점 개수:", len(points))
print(points[:5])
np.save("stroke_points.npy", points)
- 찍은 점을 확인하기
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
img = Image.open("munsik.png")
points = np.load("stroke_points.npy")
plt.figure(figsize=(10, 7))
plt.imshow(img)
plt.scatter(
points[:, 0],
points[:, 1],
s=15,
c="red",
label="clicked points"
)
plt.plot(
points[:, 0],
points[:, 1],
color="yellow",
linewidth=1,
label="point order"
)
plt.legend()
plt.show()

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 문식이 좌표
points = np.load("stroke_points.npy").astype(np.float32)
data = torch.tensor(points, dtype=torch.float32)
# 입력: 클릭 순서
X = torch.linspace(-1, 1, len(data)).reshape(-1, 1)
# 출력: 각 순서에서의 [x, y]
y = data
# 모델 정의 (비선형 MLP)
model = nn.Sequential(
nn.Linear(1, 64),
nn.ReLU(),
nn.Linear(64, 128),
nn.ReLU(),
nn.Linear(128, 128),
nn.ReLU(),
nn.Linear(128, 2)
)
# 손실함수 & 옵티마이저
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.005)
# 학습 루프
for epoch in range(10000):
xy_pred = model(X)
loss = criterion(xy_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch + 1) % 1000 == 0:
print(f"Epoch {epoch + 1} | Loss: {loss.item():.6f}")
# 예측 곡선 생성
with torch.no_grad():
t_line = torch.linspace(-1, 1, 2000).reshape(-1, 1)
xy_line = model(t_line)
x_dot = data[:, 0]
y_dot = data[:, 1]
x_line = xy_line[:, 0]
y_line = xy_line[:, 1]
# 시각화
plt.figure(figsize=(7, 5))
plt.scatter(x_dot.numpy(), y_dot.numpy(), color="red", s=15)
plt.plot(x_line.numpy(), y_line.numpy(), color="blue", linewidth=2)
plt.ylim(plt.ylim()[::-1])
plt.axis("equal")
plt.xlabel("x")
plt.ylabel("munsik(x)")
plt.title("Munsik Function")
plt.grid(True)
plt.show()
'''
Epoch 1000 | Loss: 323.198853
Epoch 2000 | Loss: 139.709564
Epoch 3000 | Loss: 75.876106
Epoch 4000 | Loss: 55.751930
Epoch 5000 | Loss: 41.662064
Epoch 6000 | Loss: 24.956499
Epoch 7000 | Loss: 19.054253
Epoch 8000 | Loss: 17.159523
Epoch 9000 | Loss: 14.173531
Epoch 10000 | Loss: 12.325826
'''


분류 문제에서의 함수 중첩
Iris 데이터셋처럼 꽃 종류를 맞히는 문제를 생각해 보자.
- 입력 특성: 꽃받침/꽃잎 길이 등 4개
- 클래스: 3개
model = nn.Sequential(
nn.Linear(4, 16),
nn.ReLU(),
nn.Linear(16, 3)
)
model = nn.Linear(4, 3)
loss_fn = nn.CrossEntropyLoss() # 내부에 Softmax 관련 계산 포함
logits = model(X)
pred_class = logits.argmax(dim=1)
Dataset과 DataLoader
데이터가 많아지면 모든 데이터를 한번에 모델에 넣기 어렵다. 그래서 데이터를 작은 묶음인 Batch(배치)로 나눠서 학습한다.
from torch.utils.data import TensorDataset, DataLoader
dataset = TensorDataset(X_tensor, y_tensor)
loader = DataLoader(
dataset,
batch_size=32,
shuffle=True
)
for epoch in range(10):
for X_batch, y_batch in loader:
pred = model(X_batch)
loss = loss_fn(pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
- batch: 한 번에 모델에 넣는 데이터 묶음
- iteration / step: 배치 하나로 한 번 학습한 것
- epoch: 전체 훈련 데이터를 한 번 모두 사용한 것
DataLoader는 (입력데이터, Label) 형태로 된 Tuple들의 리스트를 입력 형태로 기대한다.
import torch
from torch.utils.data import DataLoader
# X = 이미지 경로 문자열, Y = 정답 label 문자열
images = ['cat.jpg', 'dog.jpg', 'fish.jpg']
labels = ['고양이', '강아지', '물고기']
# Dataset 만들기
train_dataset = list(zip(images, labels))
train_loader = DataLoader(train_dataset, batch_size=3, shuffle=True)
for X_batch, y_batch in train_loader:
print(f'모델에 입력 데이터 개수 : {len(X_batch)}')
print(f'모델에 입력 데이터 내용 : {X_batch}')
print(f'모델 맞춰야하는 Label 결과 : {y_batch}')
break
# 모델에 입력 데이터 개수 : 3
# 모델에 입력 데이터 내용 : ('fish.jpg', 'cat.jpg', 'dog.jpg')
# 모델 맞춰야하는 Label 결과 : ('물고기', '고양이', '강아지')
학습 모드와 평가 모드
# 학습 모드
model.train()
# 평가 모드
model.eval()
with torch.no_grad():
pred = model(X_test)
GPU 사용
모델과 Tensor는 반드시 같은 device에 있어야 한다. 모델은 GPU인데 데이터는 CPU에 있으면 오류가 난다.
device = "cuda" if torch.cuda.is_available() else "cpu"
print(device) # cuda / cpu
# 모델과 데이터를 같은 장치로 보내야 함
model = model.to(device)
X = X.to(device)
y = y.to(device)
for X_batch, y_batch in loader:
X_batch = X_batch.to(device)
y_batch = y_batch.to(device)
pred = model(X_batch)
PyTorch 표준 형태
for epoch in range(epochs):
model.train()
for X_batch, y_batch in train_loader:
# 1. GPU 이동 (필요한 경우)
X_batch = X_batch.to(device)
y_batch = y_batch.to(device)
# 2. 예측
output = model(X_batch)
# 3. 손실 계산
loss = loss_fn(output, y_batch)
# 4. gradient 초기화
optimizer.zero_grad()
# 5. 역전파
loss.backward()
# 6. 파라미터 업데이트
optimizer.step()
# 검증
model.eval()
with torch.no_grad():
# validation data로 성능 평가
pass
💡 예측한다 → 틀린 정도를 계산한다 → 미분한다 → 가중치를 수정한다
'CS > Python' 카테고리의 다른 글
| 파이썬으로 토큰화+임베딩 찍먹하기 (0) | 2026.08.12 |
|---|---|
| 파이썬으로 MLP 찍먹하기 (0) | 2026.08.11 |
| 파이썬으로 모델 검증/해석 찍먹하기 (0) | 2026.08.07 |
| 파이썬으로 머신러닝 찍먹하기 (0) | 2026.08.07 |
| 파이썬으로 데이터 전처리 찍먹하기 (0) | 2026.08.07 |