본문 바로가기
AI SCHOOL/TIL

[DAY 75] PyTorch를 활용한 자동차 연비 회귀 예측

2023. 4. 12.

PyTorch를 활용하여 자동차 연비 회귀 예측을 했다.

어제 같은 데이터셋으로 Tensorflow를 활용한 것과 비교하며 동작 과정을 이해해 봤다.

데이터 준비

train = pd.read_csv('train.csv.zip', index_col="ID")
test = pd.read_csv('test.csv.zip', index_col="ID")
train.shape, test.shape
 
# 실행 결과
((4209, 377), (4209, 376))

pandas를 사용하여 train set, test set을 로드.

categorical_feature = train.select_dtypes(include="object").columns
train[categorical_feature] = train[categorical_feature].astype("category")
test[categorical_feature] = test[categorical_feature].astype("category")

categorical 변수들을 category 타입으로 변경한다.

from sklearn.preprocessing import OrdinalEncoder

oe = OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1)
train[categorical_feature] = oe.fit_transform(train[categorical_feature])
test[categorical_feature] = oe.transform(test[categorical_feature])

OrdinalEncoder를 통해 인코딩하여 전부 숫자형 데이터로 변환해 준다. test set을 fit 하지 않게 유의해야 한다.

X, y = train.drop(columns="y"), train["y"]
X.shape, y.shape

# 실행 결과
((4209, 376), (4209,))

타깃을 담은 y와 타깃을 드롭한 X를 준비한다.

from sklearn.model_selection import train_test_split

X_train, X_valid, y_train, y_valid = train_test_split(
    X, y, test_size=0.05, random_state=42)
X_train.shape, X_valid.shape, y_train.shape, y_valid.shape

# 실행 결과
((3998, 376), (211, 376), (3998,), (211,))

Hold-out Validation을 위해 train, valid 세트로 분할한다. valid set은 원래 데이터의 5%로 지정했다.

Tensor 형태로 가공

GPU는 부동소수점형 데이터를 더욱 효율적으로 처리할 수 있도록 설계되어 있다.

import torch
import torch.nn as nn
import torch.optim as optim

먼저 필요한 라이브러리를 임포트 하고

X_train_t = torch.FloatTensor(X_train.values)
y_train_t = torch.FloatTensor(y_train.values.reshape(-1, 1))
X_valid_t = torch.FloatTensor(X_valid.values)
y_valid_t = torch.FloatTensor(y_valid.values.reshape(-1, 1))
X_test_t = torch.FloatTensor(X_test.values)

X_train_t.shape, y_train_t.shape, X_valid_t.shape, y_valid_t.shape, X_test_t.shape

# 실행 결과
(torch.Size([3998, 376]),
 torch.Size([3998, 1]),
 torch.Size([211, 376]),
 torch.Size([211, 1]),
 torch.Size([4209, 376]))

pytorch에서 사용 가능한 부동소수점형 데이터인 텐서 형태로 데이터를 가공한다.
텐서로 변환하지 않으면 pytorch 라이브러리를 활용한 모델을 학습할 수 없다.

딥러닝 레이어 만들기

class MultivariateLinearRegression(nn.Module):
    def __init__(self, input_size, output_size):
        super(MultivariateLinearRegression, self).__init__()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(input_size, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, output_size)
        )
    
    def forward(self, x):
        x = self.linear_relu_stack(x)
        return x

nn.Module 클래스는 파이토치 모델을 정의할 때 사용하는 기본 클래스이며, MultivariateLinearRegression 클래스는 nn.Module 클래스를 상속받는다.
super 함수를 통해 부모 클래스인 nn.Module의 초기화 메소드를 호출하는 방식이다.

또한 각 층의 입력 개수는 이전 층의 출력 개수와 같아야 한다.

# 모델 인스턴스 생성
model = MultivariateLinearRegression(input_size=X_train_t.shape[1], output_size=1)

모델 생성 시 input_size는 트레인 데이터의 변수 개수를 지정한다. 여기서는 376이다. output_size는 회귀 문제이므로 1로 지정한다.

criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

손실 함수 MSE, 옵티마이저 Adam을 설정한다. learning rate는 0.001이다.

학습

반복문을 통해 학습을 구현했다.

num_epochs = 500
loss_list = []
for epoch in range(num_epochs):
    y_pred = model(X_train_t)
    loss = criterion(y_train_t, y_pred)

    # 역전파
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    loss_list.append(loss.item())
    if epoch % 50 == 0:
        print(f'epoch : {epoch}, loss : {loss.item()}')

# 실행 결과
epoch : 0, loss : 94.9512710571289
epoch : 50, loss : 77.02014923095703
epoch : 100, loss : 70.36820983886719
epoch : 150, loss : 67.18152618408203
epoch : 200, loss : 65.17955017089844
epoch : 250, loss : 63.6912956237793
epoch : 300, loss : 62.43519592285156
epoch : 350, loss : 61.28118896484375
epoch : 400, loss : 60.13337326049805
epoch : 450, loss : 58.975807189941406

epoch를 500으로 지정하고 50번째마다 epoch와 loss를 출력하도록 작성했다. loss를 시각화하기 위해 loss_list를 만들었다. 아래에서 확인해 보자.

pd.Series(loss_list).plot()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

epoch에 따른 loss를 시각화한다.

epoch

epoch에 따라 loss가 줄어드는 것을 명확히 확인할 수 있다.

검증

with torch.no_grad():
    y_valid_pred = model(X_valid_t)
    print(f'val_loss : {criterion(y_valid_t, y_valid_pred).item()}')
    
# 실행 결과
val_loss : 59.27927780151367

valid 데이터에 대한 예측 결과를 y_valid_pred에 담았다. valid loss를 출력해 봤다. 오버피팅이 발생한 것 같진 않다.

from sklearn.metrics import r2_score
valid_score = r2_score(y_valid_t, y_valid_pred)
valid_score

# 실행 결과
0.5815702147196941

r2 score를 확인한 결과 약 0.58157을 확인했다.

예측 및 제출

with torch.no_grad():
    y_predict = model(X_test_t)

torch.no_grad()는 Autograd 엔진이 트래킹 하는 것을 멈추어 메모리 사용량을 줄이고 연산 속도를 높인다. 예측값을 구할 때 미분이 필요하지 않으므로 torch.no_grad()를 사용한다.

y_predict

# 실행 결과
tensor([[ 93.7523],
        [108.6962],
        [ 93.8014],
        ...,
        [ 96.9853],
        [112.9933],
        [ 93.3684]])

예측값이 도출되었다. 그러나 제출할 파일을 만들기 위해서 아래와 같이 변형해 준다.

y_predict.squeeze()

# 실행 결과
tensor([ 93.7523, 108.6962,  93.8014,  ...,  96.9853, 112.9933,  93.3684])

squeeze 함수를 사용하여 이제 submission 데이터에 대입한다.

file_name = f"submit_pytorch_{valid_score:.5f}.csv"
file_name

# 실행 결과
submit_pytorch_0.58157.csv

위에서 구한 r2 score를 파일명에 포함하여 kaggle에 제출했다.

result

제출 결과 public score 0.50785, private score 0.50838을 얻었다.
높은 점수는 아니지만 PyTorch를 사용하여 모델 생성, 학습, 예측, 결과 제출의 흐름을 경험했다.

텐서플로 버전2는 scikit-learn과 API가 유사한데 PyTorch는 Numpy와 유사하며, 모델 컴파일 과정이 없는 등의 차이점이 있었다. 코드가 다르더라도 전체적 구성이 같기 때문에 몇 번 더 활용해 보면 훨씬 익숙해질 것 같다.


이후 합성곱 신경망(Convolutional Neural Network, CNN)에 대한 강의가 이어졌다.
1. CIFAR10 데이터셋을 사용하여 10개 종류 사물, 동물 컬러 이미지 다루기 튜토리얼 : 공식문서 링크
2. 꽃 사진 데이터셋을 사용하여 5개 종류의 꽃 이미지 다루기 튜토리얼 : 공식문서 링크

텐서플로 공식 문서의 위 튜토리얼을 통해 CNN을 경험해 봤다.
그리고 시뮬레이션을 통해 CNN을 이해하는 데 도움을 주는 CNN explainer를 알게 되었다.

 

반응형

댓글