
경사하강법의 작동 방식을 이해하기 위해 간단한 예시를 살펴본다.
트레이닝 데이터가 , , , 로 주어졌을 때, 가중치 에 따라 손실 값이 어떻게 변하는지 확인한다. (편의상 으로 가정)
| 손실 | |
|---|---|
| -0.5 | 7.5 |
| 0 | 1.875 |
| 0.5 | 0 (최소) |
| 1 | 1.875 |
| 1.5 | 7.5 |
그래프로 그리면 에서 최솟값을 가지는 U자 형태의 포물선이 된다.

특정 지점 에서의 경사는 손실 함수를 에 대해 편미분한 값이다.

체인 룰을 적용하면:
, 을 대입하면:
PyTorch에서는 loss.backward()로 자동 미분을 수행하여 기울기를 계산한다.
계산된 기울기를 사용하여 가중치를 업데이트하는 수식:
일 때:
기울기가 음수이므로, 는 양의 방향으로 이동한다. 이 과정을 반복하면 가 점점 최솟값(0.5)에 가까워진다.

바이어스 도 같은 방식으로 최적값을 찾을 수 있다:
PyTorch에서는 optimizer.step()으로 가중치를 업데이트하고, 업데이트 전에는 반드시 optimizer.zero_grad()로 이전 기울기를 초기화해야 한다.

1) 대규모 데이터셋의 계산 비용
2) 로컬 미니마(local minima) 문제

전체 데이터 합산()이 아니라 개별 데이터 포인트 로 계산하는 것이 핵심이다.
앞선 예시에서 첫 번째 데이터 포인트 만 사용하면 ():
두 번째 데이터 포인트 을 사용:
이런 식으로 데이터 하나하나를 순회하며 가중치를 업데이트해 나간다.
import torch.optim as optim
# 손실 함수 및 옵티마이저 정의
loss_function = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
optim.SGD: 확률적 경사하강법 옵티마이저model.parameters(): 모델의 학습 가능한 파라미터(, )를 옵티마이저에 전달lr: 학습률학습 루프에서의 핵심 3줄:
optimizer.zero_grad() # 이전 기울기 초기화
loss.backward() # 역전파로 기울기 계산
optimizer.step() # 기울기 기반 가중치 업데이트
num_epochs = 1000 # 에폭 수 설정
loss_list = [] # 손실 값 기록용 리스트
for epoch in range(num_epochs):
y = model(x_tensor) # 순전파: 예측값 계산
loss = loss_function(y, t_tensor) # 손실 계산
optimizer.zero_grad() # 기울기 초기화
loss.backward() # 역전파
optimizer.step() # 가중치 업데이트
loss_list.append(loss.item()) # 손실 값 기록
if (epoch+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item()}')
for name, param in model.named_parameters():
print(f'{name}: {param.data}')
loss.item(): 텐서에서 스칼라 값을 추출model.named_parameters(): 모델의 파라미터 이름과 값을 함께 확인 (디버깅용)학습을 돌려보면 손실 값이 수천만 단위로 매우 큰 경우가 있다.

일반적으로 손실 값이 클 때는 학습률을 낮추거나, 이상치를 확인하거나, 에폭 수를 늘려볼 수 있다. 하지만 이번 실습에서는 위 세 가지를 적용해도 손실 값이 줄어들지 않았다. 원인은 특징 변수(YearsExperience)와 목표 변수(Salary)의 스케일 차이가 너무 크기 때문이다.

표준화 후 학습하면 손실 값이 약 0.043으로 크게 감소한다.

from sklearn.preprocessing import StandardScaler
# 특징 변수 표준화
scaler_x = StandardScaler()
x_scaled = scaler_x.fit_transform(x.reshape(-1, 1))
# 목표 변수 표준화
scaler_t = StandardScaler()
t_scaled = scaler_t.fit_transform(t.reshape(-1, 1))
StandardScaler(): 평균 0, 분산 1로 변환하는 스케일러 객체.fit_transform(): 데이터의 평균/표준편차를 계산(fit)하고 변환(transform)을 동시에 수행.reshape(-1, 1): 1차원 배열을 2차원으로 변환 (StandardScaler는 2차원 입력을 기대)표준화된 데이터를 Tensor로 변환:
x_tensor = torch.tensor(x_scaled, dtype=torch.float32).view(-1, 1)
t_tensor = torch.tensor(t_scaled, dtype=torch.float32).view(-1, 1)
데이터 로딩 → 표준화 → 모델 정의 → 학습 → 시각화까지의 전체 흐름이다.
import torch
import torch.nn as nn
import torch.optim as optim
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
# 1. 데이터 로딩
data = pd.read_csv("Salary_dataset.csv", sep=",", header=0)
x = data.iloc[:, 1].values # YearsExperience
t = data.iloc[:, 2].values # Salary
# 2. 데이터 표준화
scaler_x = StandardScaler()
x_scaled = scaler_x.fit_transform(x.reshape(-1, 1))
scaler_t = StandardScaler()
t_scaled = scaler_t.fit_transform(t.reshape(-1, 1))
# 3. Tensor 변환
x_tensor = torch.tensor(x_scaled, dtype=torch.float32).view(-1, 1)
t_tensor = torch.tensor(t_scaled, dtype=torch.float32).view(-1, 1)
# 4. 모델 정의
class LinearRegressionModel(nn.Module):
def __init__(self):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(1, 1)
def forward(self, x):
y = self.linear(x)
return y
model = LinearRegressionModel()
# 5. GPU 지원
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
x_tensor = x_tensor.to(device)
t_tensor = t_tensor.to(device)
# 6. 손실 함수 및 옵티마이저 정의
loss_function = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 7. 학습
num_epochs = 1000
loss_list = []
for epoch in range(num_epochs):
y = model(x_tensor)
loss = loss_function(y, t_tensor)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_list.append(loss.item())
if (epoch+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item()}')
for name, param in model.named_parameters():
print(f'{name}: {param.data}')
# 8. 손실 값 시각화
plt.figure()
plt.plot(loss_list, label='Train Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.title('Loss Trend')
plt.show()