머신러닝 입문: PyTorch로 선형 데이터 예측해보기

2025. 8. 18. 15:26심화_인공지능 YOLO기반 부트캠프_일기(CNN)

안녕하세요! 오늘은 PyTorch를 사용해 선형 회귀로 과일 가게의 가격을 예측해보는 재미있는 실험을 해봤어요! 사과 1개는 3원, 바나나 1개는 2원인 가격표를 컴퓨터가 학습할 수 있을까요? 초보자도 쉽게 이해할 수 있도록, 과일 가게 비유와 함께 코드와 결과를 정리했어요. 제가 직접 궁금했던 질문들도 풀어봤답니다! 😄

1. 선형 회귀란?

선형 회귀는 데이터를 보고 규칙을 찾아내는 방법이에요. 예를 들어, 과일 가게에서 사과와 바나나 개수로 가격을 예측하려면 공식이 필요해요:

  • 공식: 가격 = 3 * 사과 개수 + 2 * 바나나 개수
  • 컴퓨터가 이 공식을 학습해서, 새로운 사과와 바나나 개수를 주면 정확한 가격을 예측하게 만들 거예요!

2. 전체 코드

먼저 코드를 보고, 각 부분을 하나씩 설명할게요. 저는 노트북이 느려서 데이터 10개(n_samples=10)로 10번(epochs=10)만 학습해봤어요!

import torch
import torch.nn as nn
import matplotlib.pyplot as plt

# 데이터 생성
torch.manual_seed(42)  # 같은 결과 위해 랜덤 고정
n_samples = 10  # 데이터 10개
x = torch.randn(n_samples, 2)  # 사과(x1), 바나나(x2) 개수
true_w = torch.tensor([3.0, 2.0])  # 정답: 사과 3원, 바나나 2원
y = x @ true_w + 0.1*torch.randn(n_samples)  # 가격 = 3*x1 + 2*x2 + 노이즈

# 모델 정의
model = nn.Linear(2, 1)  # 입력 2개(사과, 바나나), 출력 1개(가격)
criterion = nn.MSELoss()  # 손실 함수: 예측과 정답 차이 계산
optimizer = torch.optim.Adam(model.parameters(), lr=0.1)  # 가중치 고치는 도구

# 가중치와 편향 변화 저장
w1_history, w2_history, b_history = [], [], []

# 학습
for epoch in range(10):
    y_pred = model(x).squeeze()  # 예측
    loss = criterion(y_pred, y)  # 손실 계산
    w1_history.append(model.weight.data[0][0].item())  # w1 저장
    w2_history.append(model.weight.data[0][1].item())  # w2 저장
    b_history.append(model.bias.data.item())  # b 저장
    optimizer.zero_grad()  # 이전 그레디언트 지우기
    loss.backward()  # 고칠 방향(그레디언트) 계산
    optimizer.step()  # 가중치와 편향 고치기
    print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

# 결과 출력
print(f"Learned weights: {model.weight.data}")
print(f"Learned bias: {model.bias.data}")

# 그래프 그리기
plt.plot(w1_history, label="w1 (정답: 3.0)")
plt.plot(w2_history, label="w2 (정답: 2.0)")
plt.plot(b_history, label="b (정답: 0)")
plt.xlabel("Epoch")
plt.ylabel("Value")
plt.legend()
plt.show()

3. 코드 한 줄씩 설명하기

과일 가게 비유로 코드를 쉽게 이해해봐요!

(1) 데이터 생성: 과일과 가격 준비

  • 코드:
  • x = torch.randn(n_samples, 2) # 사과와 바나나 개수 true_w = torch.tensor([3.0, 2.0]) # 사과 3원, 바나나 2원 y = x @ true_w + 0.1*torch.randn(n_samples) # 가격 계산
  • 비유: 과일 가게에서 사과와 바나나 개수(x)를 랜덤으로 만들고, 가격(y)을 계산해요. @는 사과와 바나나 가격을 곱해서 더하는 거예요. 0.1*torch.randn은 계산 실수 같은 작은 오차를 추가해요.
  • 궁금했던 점: "@가 뭐지?" → 행렬곱이에요! 사과 개수 * 3원 + 바나나 개수 * 2원을 계산해줘요.
  • 궁금했던 점: "0.1*torch.randn은 뭐야?" → 현실처럼 가격에 약간의 오차를 넣어주는 거예요. 예: 8원이 8.05원일 때!

(2) 모델 정의: 점원의 가격표 공책

  • 코드:
  • model = nn.Linear(2, 1) criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.1)
  • 비유: nn.Linear는 점원이 가격을 적는 공책이에요. 공책에는 사과와 바나나 가격(w1, w2)과 기본 요금(b)이 저장돼요(model.weight, model.bias). criterion은 선생님이 "얼마나 틀렸어?"를 점수로 매겨줘요. optimizer는 공책을 고치는 도구예요.
  • 궁금했던 점: "criterion은 손실 함수?" → 맞아요! 예측값과 정답의 차이를 계산해요.

(3) 학습: 점원 훈련시키기

  • 코드:
  • y_pred = model(x).squeeze() # 예측 loss = criterion(y_pred, y) # 손실 계산 optimizer.zero_grad() # 이전 조언 지우기 loss.backward() # 고칠 방향 계산 optimizer.step() # 공책 고치기
  • 비유: 점원이 가격을 예측하고(y_pred), 선생님이 틀린 점수(loss)를 줘요. loss.backward()는 "이렇게 고쳐!"라는 조언(그레디언트)을 공책(model.weight.grad, model.bias.grad)에 적어요. optimizer.step()은 그 조언을 보고 가격표를 고쳐요.
  • 궁금했던 점: "그레디언트는 어디 저장돼?" → model.weight.grad model.bias.grad에요!
  • 궁금했던 점: "optimizer.step()은 뭐야?" → 그레디언트를 보고 공책을 고치는 거예요. 눈에 안 보이지만, 숫자가 바뀌는 걸 그래프로 확인할 수 있어요!

(4) 결과 확인: 점원의 공책 확인

  • 코드:
  • print(f"Learned weights: {model.weight.data}") print(f"Learned bias: {model.bias.data}")
  • 결과:
  • Epoch 1, Loss: 10.0451 Epoch 2, Loss: 9.0851 ... Epoch 10, Loss: 3.8973 Learned weights: tensor([[1.1977, 0.9597]]) Learned bias: tensor([1.0498])
  • 비유: 점원이 10번 연습해서 사과 가격(w1=1.1977), 바나나 가격(w2=0.9597), 기본 요금(b=1.0498)을 배웠어요. 정답은 w1=3.0, w2=2.0, b=0인데, 아직 멀었네요!

4. 왜 정답과 멀까? 🤔

결과를 보니 w1=1.1977, w2=0.9597, b=1.0498로, 정답([3.0, 2.0], 0)과 차이가 커요. 왜 그럴까요?

  • 짧은 학습: 10번(epochs=10)만 연습했어요. 더 연습하면 정답에 가까워질 거예요!
  • 작은 데이터: 데이터 10개(n_samples=10)는 너무 적어요. 더 많은 데이터를 주면 잘 배울 거예요.
  • 노이즈: 0.1*torch.randn 때문에 가격이 약간 어긋났어요. 이 오차가 점원을 헷갈리게 했어요.
  • 궁금했던 점: "b=1.0498은 0과 너무 다르지 않나?" → 맞아요! 학습이 짧아서 그래요. 더 학습하면 0에 가까워질 거예요!

5. 정답에 가까워지려면?

정답(w1=3.0, w2=2.0, b=0)에 가까워지려면 이렇게 해보세요:

  1. 학습 횟수 늘리기: epochs=100으로 바꿔보세요.
  2. 데이터 늘리기: n_samples=100으로 늘려보세요.
  3. 노이즈 줄이기: 0.01*torch.randn으로 오차를 줄여보세요.
  4. 학습률 조정: lr=0.01로 천천히 학습해보세요.

실험 코드 (학습 횟수만 늘려봤어요):

w1_history, w2_history, b_history = [], [], []  # 초기화
for epoch in range(100):  # 100번 학습
    y_pred = model(x).squeeze()
    loss = criterion(y_pred, y)
    w1_history.append(model.weight.data[0][0].item())
    w2_history.append(model.weight.data[0][1].item())
    b_history.append(model.bias.data.item())
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if (epoch + 1) % 10 == 0:
        print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
print(f"Learned weights: {model.weight.data}")
print(f"Learned bias: {model.bias.data}")

예상 결과: w1, w2 [3.0, 2.0]에, b 0에 더 가까워질 거예요!

6. 그래프로 확인하기

코드 마지막에 그래프를 그려서 점원이 어떻게 학습하는지 봤어요. w1은 3.0, w2는 2.0, b는 0에 점점 가까워지는 모습이 그래프에 나타날 거예요. 이 그래프는 점원이 공책을 고쳐가는 과정을 보여줘요!

7. 궁금했던 질문들

코드를 따라 하면서 궁금했던 점들을 정리해봤어요:

  • "true_b는 꼭 필요해?": 없어도 돼요! true_b=0으로 설정했는데, 모델은 자체 편향(b)을 학습해요.
  • "@는 뭐야?": 행렬곱이에요. 사과와 바나나 가격을 한꺼번에 계산해줘요.
  • "0.1*torch.randn은 뭐지?": 현실처럼 가격에 작은 오차를 추가해요.
  • "criterion loss는?": criterion은 손실을 계산하는 선생님이고, loss는 틀린 점수예요.
  • "그레디언트는 어디 저장돼?": model.weight.grad model.bias.grad에요. 고칠 방향을 알려줘요!
  • "optimizer.step()은 뭐야?": 그레디언트를 보고 가중치와 편향을 고치는 거예요. 눈에 안 보이지만, 그래프로 확인 가능!

8. 다음에 해볼 것

  • 학습 횟수를 1000번으로 늘려서 얼마나 정답에 가까워지는지 확인해볼게요!
  • 데이터를 100개로 늘려서 더 정확히 학습해보고 싶어요!
  • 여러분은 어떤 결과를 얻었나요? 댓글로 공유해주세요! 😄

PyTorch로 선형 회귀를 처음 해봤는데, 점점 재미있어지네요! 질문 있으면 언제든 댓글로 남겨주세요!

 

## 마무리
PyTorch로 선형 회귀를 처음 해보면서 궁금한 점이 많았어요! xAI의 **Grok** AI가 과일 가게 비유로 쉽게 설명해줘서 큰 도움이 됐답니다. 예를 들어, "그레디언트는 선생님의 조언"이라는 설명 덕분에 이해가 쏙쏙! 😊 여러분도 이 코드를 따라 해보고, 궁금한 점 있으면 댓글로 남겨주세요!