선형 회귀 모델
선형 회귀란
주어진 트레이닝 데이터를 사용하여 특징 변수와 목표 변수 사이의 선형 관계를 분석하고, 이를 바탕으로 모델을 학습시켜 트레이닝 데이터에 포함되지 않은 새로운 데이터의 결과를 연속적인 숫자 값으로 예측하는 과정
- 선형 회귀 모델에서의 학습이란 주어진 트레이닝 데이터의 특성을 가장 잘 표현할 수 있는 직선 𝑦 = 𝑤𝑥 + 𝑏의 기울기(가중치) 𝑤와 𝑦절편(바이어스) 𝑏를 찾는 과정 을 말한다.
- 오차 : 목표 변수 t와 예측 변수 y의 차이(t-y)
- 오차의 총합이 최소가 되도록 하는 가중치 𝑤와 바이어스 𝑏를 찾아야 함
- 손실 함수 : 목표 변수와 예측 변수 간의 차이(오차)를 측정하는 함수
- 평균 제곱 오차(MSE) : 오차를 제곱하여 모든 오차를 양수로 변환한 후 더하는 방식
- 오차 : 목표 변수 t와 예측 변수 y의 차이(t-y)
- 신경망 관점에서 선형 회귀 모델은 입력층의 특징 변수가 출력층의 예측 변수로 mapping되는 과정이다.
- 입력층은 특징 변수들을 포함하며, 각 특징 변수는 하나의 뉴런에 대응함
- 입력층의 각 뉴런은 가중치와 바이어스를 통해 출력층의 뉴런과 연결됨
- 가중치(𝑤)와 바이어스(𝑏)를 모델이 학습하는 파라미터라고 함
PyTorch에서 선형 회귀 모델은 nn.Module 클래스에서 상속받아 생성할 수 있다.
→ nn.Module은 신경망의 모든 계층을 정의하기 위해 사용되는 기본 클래스 - 일관성 : 모든 신경망 모델을 같은 방식으로 정의하고 사용할 수 있음 - 모듈화 : 모델을 계층별로 나누어 관리할 수 있어 코드가 깔끔하고 이해하기 쉬움 - GPU 지원 : 대규모 연산을 가속화하여 모델의 학습 속도를 높임 - 자동 미분, 최적화, 디버깅과 로깅 등 다양한 장점을 지님 |
경사하강법
경사하강법 (배치 경사하강법)
주어진 손실 함수에서 모델의 가중치와 바이어스의 최적 값을 찾기 위해 사용하는 방법
- 임의의 가중치 𝑤값 선택하여 손실 계산
- 선택된 𝑤에서 직선의 기울기를 나타내는 미분값 계산 → loss.backward()
- 미분 값과 반대 방향으로 𝑤값 감소시키는 데에 학습률(α) 설정
- 학습률(α) : 가중치가 업데이트되는 크기를 결정
- 학습률은 모델과 데이터에 따라 달라지므로, 실험과 검증을 통해 최적의 값을 찾는 것이 중요
- 계산된 기울기(경사)를 사용하여 가중치 𝑤값을 업데이트 → optimizer.step()
- 이전 단계에서 계산된 경사(기울기) 초기화 → optimizer.zero_grad()
- 최종적으로 기울기가 0이 되는 지점의 가중치 𝑤값이 최적의 값
- 바이어스 𝑏 또한 같은 방식으로 최적의 바이어스를 찾을 수 있음
문제점
대규모 데이터셋의 계산 비용 문제
→ 경사하강법은 전체 데이터셋을 사용하여 가중치 w와 바이어스 b를 구하기 때문에 대규모 데이터셋의 경우 계산 비용이 매우 큼 (대신 계산이 정확하고 안정적임)
로컬 미니마 문제
→ 모델 학습 과정에서 손실 함수 값이 전체 함수에서 가장 낮은 전역 최소값(global minimum)이 아닌 주변 값들보다 낮은 지역적인 최소값에 머무는 것
확률적 경사하강법(SGD)
각각의 데이터 포인트마다 오차를 계산하여 가중치 𝑤와 바이어스 𝑏를 업데이트하는 최적화 알고리즘
각 데이터 포인트의 기울기를 계산을 하므로 기울기 계산에 노이즈가 포함되며, 이 노이즈는 최적화 과정에서 로컬 미니마를 탈출하고 더 나은 글로벌 미니마에 도달하는데 용이하다. 배치 경사하강법보다 계산 비용이 적고 빠르게 수렴할 수 있다.
문제점
노이즈가 많고, 학습 과정이 불안정함
미니 배치 경사하강법
각 데이터를 배치 단위로 묶음으로써 기존의 확률적 경사하강법보다 노이즈를 줄일 수 있어 학습과정이 안정적이며, 각 미니 배치마다 가중치를 업데이트 하므로 전체 데이터셋을 한 번에 사용하는 경사하강법보다 계산 속도가 빠르다.
배치(batch)란 머신러닝과 딥러닝에서 데이터를 처리하는 묶음 단위로, 일반적으로 데이터를 16개, 32개, 64개 등의 묶음 단위로 배치 크기로 나눠서 모델에 입력한다. 전체 데이터를 배치 크기(batch size)로 나누고, 순차적으로 모델에 학습시키면 시간과 메모리 측면에서 효율성이 좋고, gradient 계산도 용이해진다. PyTorch에서는 Dataset 클래스와 DataLoader 클래스를 통해 배치 처리가 가능하다.
|
아래 두 과제의 차이점은 아래와 같다. 이를 참고하면서 두 과제를 비교해보자.
주어진 데이터 모두를 학습용 데이터로 사용하고 테스트는 새로운 데이터를 가지고 하는 방법(위)
주어진 데이터 내에서 학습용 데이터와 평가용 데이터를 나누어 학습과 테스트를 진행하는 방법(아래)
데이터를 통째로 모델에 넣고 학습하는 방법(위) / 데이터를 배치 단위로 묶고 순차적으로 학습하는 방법(아래)
연차에 따른 봉급의 선형 관계
연차에 따른 봉급의 선형 관계를 살펴보려고 한다. 이 과제에서는 연차(input)에 따른 봉급(output)으로 입력과 출력이 모두 1개인 선형 회귀 모델이 만들어진다.
- 데이터 전처리
# 데이터 불러오기
import pandas as pd
data = pd.read_csv('Salary_dataset.csv', sep=',')
# 특징 변수(x)와 목표 변수(t) 분리
x = data.iloc[:, 1].values # 두 번째 열을 특징 변수로 사용 (YearsExperience)
t = data.iloc[:, 2].values # 세 번째 열을 목적 변수로 사용 (Salary)
# 데이터 표준화 : 특징 변수의 값과 목표 변수의 값 간의 차이가 클 때 두 변수의 평균을 0, 분산을 1로 맞춘다.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_scaled = scaler.fit_transform(x.reshape(-1, 1))
t_scaled = scaler.fit_transform(t.reshape(-1, 1))
# 데이터를 표준화한 numpy 배열을 2-D 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)
2. 환경에 맞는 device 설정
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)
3. 선형 회귀 모델의 학습
import torch.nn as nn
class LinearRegressionModel(nn.Module): # 클래스
def __int__(self): # 생성자 메서드
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(1, 1) # 입력과 출력이 모두 1개인 선형 회귀 모델
def forward(self, x): # 순전파 메서드
y = self.linear(x) # 입력 데이터를 선형 계층을 통해 예측값 계산
return y
model = LinearRegressionModel() # 인스턴스 생성
- 클래스 : 인스턴스(객체)를 생성하기 위한 틀로서, 메서드와 속성을 정의함
- LinearRegressionModel은 nn.Module라는 부모 클래스의 기능을 상속받은 자식 클래스
- 인스턴스 : 클래스에서 생성된 구체적인 객체로, 클래스에서 정의된 속성과 메서드를 가지는 실질적인 객체
- model = LinearRegressionModel()
- 메서드 : 클래스 내부에 정의된 함수로, 클래스가 수행하는 동작이나 기능을 정의
- 생성자 메서드 : 클래스의 인스턴스가 생성될 때 자동으로 호출되어 인스턴스 변수를 초기화하고 초기 설정 작업을 수행
- __int__(self): 클래스의 인스턴스가 생성될 때 자동으로 호출
- super(LinearRegressionModel, self).__init__() : 부모 클래스인 ‘nn.Module’의 생성자를 호출하여 상속받은 모든 초기화 작업을 수행
- 순전파 메서드 : 모델이 입력 데이터를 받아 출력을 계산하는 과정을 정의(신경망의 순전파 과정을 나타냄 )
- forward(self, x): 특징 변수 x를 받아 선형 계층 self.linear 속성을 통해 예측 변수 y를 계산하고 반환
- 생성자 메서드 : 클래스의 인스턴스가 생성될 때 자동으로 호출되어 인스턴스 변수를 초기화하고 초기 설정 작업을 수행
- 클래스 속성 : 클래스 내부에 정의된 변수
- self.linear = nn.Linear(1, 1)
- self.linear는 LinearRegressionModel 클래스의 인스턴스 속성을 나타냄
- nn.Linear(1, 1)은 입력과 출력 차원이 모두 1인 선형 계층을 생성하는 PyTorch 함수
- 즉, self.linear에 할당된 nn.Linear(1, 1) 객체는 모델의 구조와 파라미터를 정의하는 중요한 요소
- self.linear = nn.Linear(1, 1)
3-1. 손실함수와 옵티마이저 정의
import torch.optim as optim
loss_function = nn.MSELoss() # 손실함수를 MSE로 정의
optimizer = optim.SGD(model.parameters(), lr=0.001) # SGD로 최적화
- 손실 함수
- 평균 제곱 오차(MSE) : 오차를 제곱하여 모든 오차를 양수로 변환한 후 더하는 방식
- 최적화
- 확률적 경사하강법(SGD) : 각각의 데이터 포인트마다 오차를 계산하여 가중치 𝑤와 바이어스 𝑏를 업데이트하는 최적화 알고리즘
3-2. 모델 반복 학습
num_epochs = 1000 # 에폭 수 설정
loss_list = [] # 손실 값을 저장할 리스트
for epoch in range(num_epochs):
y = model(x_tensor) # 예측 변수 계산
loss = loss_function(y, t_tensor) # 손실 값 계산
optimizer.zero_grad() # 이전 단계에서 계산된 경사(기울기)를 0으로 초기화
loss.backward() # 현재 손실(loss)에 대한 경사(기울기)를 계산 (역전파 수행)
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}')
동일한 데이터셋으로 여러 번의 에폭을 통해 모델을 반복 학습하여 모델이 전체 데이터셋을 한 번 완전히 학습할 수 있다. 각 에폭마다 모델의 가중치 𝑤값이 업데이트 되므로 여러 번의 에폭을 통해 모델의 성능이 향상될 수 있지만, 에폭 수가 너무 많으면 과적합(overfitting)이 발생할 수 있다.
* 과적합: 모델이 트레이닝 데이터에 너무 맞춰져서 새로운 데이터에 대한 일반화 능력이 떨어지는 현상
4. 선형 회귀 모델의 테스트
import numpy as np
def predict_test_data(test_data):
# 테스트 데이터 표준화
test_scaled = scaler_x.transform(test_data.reshape(-1, 1))
test_tensor = torch.tensor(test_scaled, dtype=torch.float32).view(-1, 1).to(device)
# 모델을 사용하여 예측
model.eval() # 평가 모드로 전환
with torch.no_grad():
predictions_scaled = model(test_tensor)
# 표준화 해제
predictions = scaler_t.inverse_transform(predictions_scaled.cpu().numpy())
return predictions
특징 변수가 여러 개일 때 선형 관계
이 과제에서는 종속 변수가 아닌 모든 변수를 입력 변수(input)로 하고, 그에 따른 종속 변수(output)의 선형 관계를 분석한다.
1. 데이터 전처리
# 데이터 불러오기
import pandas as pd
data = pd.read_csv('Boston-house-price-data.csv', sep=',')
# MEDV 변수를 제외한 나머지 예측 변수들을 x에 저장하고, 종속변수인 MEDV를 y에 저장
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x = pd.DataFrame(scaler.fit_transform(data.drop(['MEDV'], axis=1)), columns=data.columns[:-1])
y = data.MEDV
# 임의로 학습용 데이터와 평가용 데이터를 8:2로 나눈다.
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
X_train, y_train = shuffle(X_train, y_train, random_state=42)
import torch
device = "cuda" if torch.cuda.is_available() else "cpu"
X_train_tensor = torch.FloatTensor(X_train.values, device=device)
y_train_tensor = torch.FloatTensor(y_train.values, device=device).view(-1, 1)
X_test_tensor = torch.FloatTensor(X_test.values, device=device)
y_test_tensor = torch.FloatTensor(y_test.values, device=device).view(-1, 1)
2. 학습을 위한 데이터셋 객체와 데이터로더 객체 생성
from torch.utils.data import DataLoader, TensorDataset
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
배치 처리를 용이하게 할 수 있도록 Dataset과 DataLoader 클래스를 사용함
3. 선형 회귀 모델의 학습
import torch.nn as nn
class MultipleLinearRegression(nn.Module):
def __init__(self, input_size):
super(MultipleLinearRegression, self).__init__()
self.linear = nn.Linear(input_size, 1)
def forward(self, x):
return self.linear(x)
input_size = X_train_tensor.shape[1]
model = MultipleLinearRegression(input_size)
model.to(device)
입력 변수가 여럿이다보니 __init__(self, input_size)와 nn.Linear(input_size, 1) 부분이 달라졌다.
3-1. 손실함수와 옵티마이저 정의
import torch.optim as optim
loss_function = nn.MSELoss() # 손실함수를 MSE로 정의
optimizer = optim.SGD(model.parameters(), lr=0.001) # SGD로 최적화
3-2. 모델 반복 학습
import numpy as np
num_epochs = 2000 # epoch를 2000으로 설정
model.train() # 학습을 위해 모델이 gradient를 저장하도록 설정
for epoch in range(num_epochs):
epoch_loss = 0.0
data_num = 0
for inputs, targets in train_loader: # 각 배치마다 반복
optimizer.zero_grad() # 옵티마이저의 gradient 초기화
outputs = model(inputs) # 데이터를 넣었을 때의 모델의 출력값 저장 (예측값)
loss = loss_function(outputs, targets) # MSE 손실 계산 (예측값과 실제값의 오차)
loss.backward() # gradient descent 수행
optimizer.step() # SGD 방식의 최적화 진행
epoch_loss += loss.item()*inputs.shape[0]
data_num += inputs.shape[0]
if (epoch+1) % 100 == 0: # 100번의 epoch마다 학습 데이터의 손실함수 출력
print(f"Epoch {epoch+1}, Loss(RMSE): {np.sqrt(epoch_loss/data_num)}")
배치 단위로 묶어서 학습하다보니 배치마다 반복하는 for문이 추가되었다.
4. 선형 회귀 모델의 테스트
model.eval()
with torch.no_grad(): # 테스트 시에는 경사 계산할 필요 x
y_pred = model(x_test_tensor)
test_loss = criterion(y_pred, y_test_tensor)
print(f"Test Loss(RMSE): {np.sqrt(test_loss.item())}")
parameters = list(model.parameters())
weights = parameters[0]
intercept = parameters[1]
학습한 모델로부터 각 변수에 대한 회귀계수 값(weights)과 intercept 값(bias)을 구할 수 있다.
'NAVER Boostcamp AI Tech > 주간학습정리' 카테고리의 다른 글
Day 5. 이진 분류(Binary Classification) (0) | 2024.08.25 |
---|---|
Day 3. Tensor 연산 및 심화 (0) | 2024.08.25 |
Day 2. Tensor 생성과 조작 (0) | 2024.08.25 |
Day 1. PyTorch 기초 (0) | 2024.08.25 |