728x90

이번 포스팅에서는 pytorch의 기본 흐름에 대해 적어보겠다. dataset은 car_evaluation data를 사용한다.

Data

car_evaluation

  • price : 자동차 가격
  • maint : 자동차 유지 비용
  • doors : 자동차 문 개수
  • persons : 수용 인원
  • lug_capacity : 수하물 용량
  • safety : 안정성
  • output : 차 상태 - 이 데이터는 unacc, acc, good, vgood 중 하나의 값을 가진다.

Library

import torch
import torch.nn as nn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

Data Load

dataset = pd.read_csv('./car_evaluation.csv')

dataset.head()

fig_size = plt.rcParams['figure.figsize']
fig_size[0] = 8
fig_size[1] = 6
plt.rcParams['figure.figsize'] = fig_size

dataset.output.value_counts().plot(kind='pie', autopct='%0.05f%%',
                                  colors=['lightblue', 'lightgreen', 'orange', 'pink'],
                                   explode=(0.05, 0.05, 0.05, 0.05))

Preprocessing

먼저 astype() 메서드를 이용하여 범주 특성을 갖는 데이터를 범주형 타입으로 변환한다. 또한, 파이토치를 이용한 모델 학습을 해야 하므로 범주형 타입을 텐서로 변환해야 한다.

categorical_col = ['price', 'maint', 'doors', 'persons', 'lug_capacity', 'safety']

for category in categorical_col:
    dataset[category] = dataset[category].astype('category')
    
price = dataset['price'].cat.codes.values # 범주형 데이터를 텐서로 변환하기 위해 다음과 같은 절차가 필요하다.
maint = dataset['maint'].cat.codes.values
doors = dataset['doors'].cat.codes.values
persons = dataset['persons'].cat.codes.values
lug_capacity = dataset['lug_capacity'].cat.codes.values
safety = dataset['safety'].cat.codes.values

categorical_data = np.stack([price, maint, doors, persons, lug_capacity, safety], 1)

범주형 데이터를 숫자로 변환하기 위해 cat.codes를 사용한다. cat.codes는 어떤 클래스가 어떤 숫자로 매핑되어 있는지 확인이 어려운 단점이 있으므로 주의해야 한다.

이제 torch 모듈을 이용하여 배열을 텐서로 변환한다.

categorical_data = torch.tensor(categorical_data, dtype=torch.int64)
categorical_data

마지막으로 레이블로 사용할 컬럼에 대해서도 텐서로 변환해준다. 이번에는 get_dummies를 이용하여 넘파이 배열로 변환한다.

outputs = pd.get_dummies(dataset.output)
outputs = outputs.values
outputs = torch.tensor(outputs).flatten() # 1차원 텐서로 변환

print(categorical_data.shape)
print(outputs.shape)

워드 임베딩은 유사한 단어끼리 유사하게 인코딩되도록 표현하는 방법이다. 또한, 높은 차원의 임베딩일수록 단어 간의 세부적인 관계를 잘 파악할 수 있다. 따라서 단일 숫자로 변환된 넘파이 배열을 N차원으로 변경하여 사용한다.
배열은 N차원으로 변환하기 위해 먼저 모든 범주형 칼럼에 대한 임베딩 크기를 정의한다. 보통 컬럼의 고유 값 수를 2로 나누는 것을 많이 사용한다.

다음은 (모든 범주형 칼럼의 고유값 수, 차원의 크기) 형태의 배열을 출력한 결과이다.

categorical_column_sizes = [len(dataset[column].cat.categories) for column in categorical_col]
categorical_embedding_sizes = [(col_size, min(50, (col_size+1)//2)) for col_size in categorical_column_sizes]

print(categorical_embedding_sizes)

데이터셋을 훈련과 테스트 용도로 분리한다.

total_records = 1728
test_records = int(total_records * .2) # 전체 데이터 중 20%를 테스트로 사용

categorical_train = categorical_data[:total_records - test_records]
categorical_test = categorical_data[total_records - test_records:total_records]
train_outputs = outputs[:total_records - test_records]
test_outputs = outputs[total_records - test_records:total_records]

Modeling

class Model(nn.Module):
    def __init__(self, embedding_size, output_size, layers, p=0.4):
        super().__init__()
        self.all_embeddings = nn.ModuleList([nn.Embedding(ni, nf) for ni, nf in embedding_size])
        self.embedding_dropout = nn.Dropout(p)
        
        all_layers = []
        num_categorical_cols = sum((nf for ni, nf in embedding_size))
        input_size = num_categorical_cols # 입력층의 크기를 찾기 위해 범주형 칼럼 개수를 input_size에 저장
        
        for i in layers:
            all_layers.append(nn.Linear(input_size, i))
            all_layers.append(nn.ReLU(inplace=True))
            all_layers.append(nn.BatchNorm1d(i))
            all_layers.append(nn.Dropout(p))
            input_size = i
            
        all_layers.append(nn.Linear(layers[-1], output_size))
        self.layers = nn.Sequential(*all_layers) # 신경망의 모든 계층이 순차적으로 실행되도록 모든 계층에 대한 목록을 nn.Sequential 클래스로 전달
        
    def forward(self, x_categorical):
        embeddings = []
        for i, e in enumerate(self.all_embeddings):
            embeddings.append(e(x_categorical[:, i]))
        x = torch.cat(embeddings, 1)
        x = self.embedding_dropout(x)
        x = self.layers(x)
        return x
model = Model(categorical_embedding_sizes, 4, [200, 100, 50], p=0.4)
print(model)

loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
device = torch.device('cpu')
epochs = 500
aggregated_losses = []
train_outputs = train_outputs.to(device=device, dtype=torch.int64)

for i in range(epochs):
    i += 1
    y_pred = model(categorical_train)
    single_loss = loss_function(y_pred, train_outputs)
    aggregated_losses.append(single_loss)
    
    if i%25 == 1:
        print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
        
    optimizer.zero_grad()
    single_loss.backward() # 가중치를 업데이트
    optimizer.step() # 기울기 업데이트
    
print(f'epoch: {i:3} loss: {single_loss.item():10.10f}')

테스트 데이터셋으로 예측을 해보자.

test_outputs = test_outputs.to(device=device, dtype=torch.int64)
with torch.no_grad():
    y_val = model(categorical_test)
    loss = loss_function(y_val, test_outputs)
    
print(f'Loss: {loss:.8f}')

 

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

tensor의 복사방법  (0) 2022.08.06
[Pytorch] register_buffer  (0) 2022.08.01
pytorch - nn.function과 nn의 차이점  (0) 2022.06.19
torch - GPU 사용하기  (0) 2022.06.13
Pytorch nn.ModuleList  (0) 2022.05.30
728x90

detach()와 clone()은 기존 Tensor를 복사하는 방법 중 하나이다.

tensor.clone()은 기존 텐서의 내용을 복사한 텐서를 생성하겠다는 의미이며, detach()는 기존 텐서에서 기울기가 전파되지 않는 텐서이다.

즉, tensor.clone().detach()는 기존 텐서를 복사한 새로운 텐서를 생성하지만 기울기에 영향을 주지는 않겠다는 의미이다.

구분 메모리 계산 그래프 상주 유무
tensor.clone() 새롭게 할당 계산 그래프에 계속 상주
tensor.detach() 공유해서 사용 계산 그래프에 상주하지 않음
tensor.clone().detach() 새롭게 할당 계산 그래프에 상주하지 않음

 

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

Pytorch 기본 예제  (0) 2022.12.24
[Pytorch] register_buffer  (0) 2022.08.01
pytorch - nn.function과 nn의 차이점  (0) 2022.06.19
torch - GPU 사용하기  (0) 2022.06.13
Pytorch nn.ModuleList  (0) 2022.05.30
728x90

transformer를 공부하던 중 positional encoding부분에서 register_buffer라는 부분이 생소하여 포스팅으로 기록한다.

torch.nn.Module.register_buffer는 매개 변수로 간주되지 않는 버퍼를 등록하는 데 사용된다. register_buffer는 다음과 같은 특징이 있다.

  1. optimizer가 update하지 않는다.
  2. state_dict에는 저장된다.
  3. GPU에서 작동한다.

즉, 네트워크를 구성하는데 있어 중간에 업데이트를 하지 않는 일반 layer를 넣고 싶을 때 사용한다.

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

Pytorch 기본 예제  (0) 2022.12.24
tensor의 복사방법  (0) 2022.08.06
pytorch - nn.function과 nn의 차이점  (0) 2022.06.19
torch - GPU 사용하기  (0) 2022.06.13
Pytorch nn.ModuleList  (0) 2022.05.30
728x90

파이토치로 모델링을 할 때 nn을 사용할 때도 있고 nn.functional을 사용할 때도 있다.

이 둘의 차이점은 무엇인지 알아보자.

 

먼저 nn을 사용하는 코드는 다음과 같다.

import torch
import torch.nn as nn

inputs = torch.randn(64, 3, 244, 244)
conv = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
outputs = conv(inputs)
layer = nn.Conv2d(1, 1, 3)

nn.functional을 사용하는 코드는 다음과 같다.

import torch.nn.functional as F

inputs = torch.randn(64, 3, 244, 244)
weight = torch.randn(64, 3, 3, 3)
bias = torch.randn(64)
outputs = F.conv2d(inputs, weight, bias, padding=1)

 

nn.Conv2d에서 input_channel과 output_channel을 사용해서 연산했다면 functional.conv2d는 입력과 가중치 자체를 직접 넣어준다. 이때 직접 넣어준다는 의미는 가중치를 전달해야 할 때마다 가중치 값을 새로 정의해야 함을 의미한다.

다음은 nn과 nn.functional을 비교한 표이다.

구분 nn nn.functional
형태 nn.Conv2d : 클래스
nn.Module 클래스를 상속받아 사용
nn.functional.conv2d : 함수
def function (input)으로 정의된 순수한 함수
호출 방법 하이퍼파라미터를 전달한 후 함수 호출을 통해 데이터 전달 함수를 호출할 때 하이퍼파라미터, 데이터 전달
위치 nn.Sequential 내에 위치 nn.Sequential에 위치할 수 없음
파라미터 파라미터를 새로 정의할 필요 없음 가중치를 수동으로 전달해야 할 때마다 자체 가중치를 정의

 

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

tensor의 복사방법  (0) 2022.08.06
[Pytorch] register_buffer  (0) 2022.08.01
torch - GPU 사용하기  (0) 2022.06.13
Pytorch nn.ModuleList  (0) 2022.05.30
Pytorch nn.Embedding()  (0) 2022.05.30
728x90

일반적으로 하나의 GPU를 사용할 때는 다음과 같은 코드를 사용한다.

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
model = Net()
model.to(device)

 

하지만 다수의 GPU를 사용한다면 다음 코드와 같이 nn.DataParallel을 이용한다.

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = Net()
if torch.cuda.device_count() > 1:
  model = nn.DataParallel(net)
model.to(device)

 nn.DataParallel을 사용할 경우 배치 크기가 알아서 각 GPU로 분배되는 방식으로 작동하기 때문에 GPU 수 만큼 배치 크기도 늘려 주어야 한다.

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

[Pytorch] register_buffer  (0) 2022.08.01
pytorch - nn.function과 nn의 차이점  (0) 2022.06.19
Pytorch nn.ModuleList  (0) 2022.05.30
Pytorch nn.Embedding()  (0) 2022.05.30
Pytorch 기본 문법 - 모델 평가  (0) 2022.05.25
728x90

공식 Documentation

Python List와 마찬가지로 nn.Module을 저장하는 역할을 한다.

예제

list를 nn.ModuleList()로 감싸 주면 된다.

class MyModule(nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])

    def forward(self, x):
        # ModuleList can act as an iterable, or be indexed using ints
        for i, l in enumerate(self.linears):
            x = self.linears[i // 2](x) + l(x)
        return x

 

nn.Sequential()과의 차이점

  • nn.Sequential() : 안에 들어가는 모듈들을 연결해주고, 하나의 뉴럴넷을 정의한다. 즉, 나열된 모듈들의 output shape과 input shape이 일치해야 한다는 것. 
  • nn.ModuleList() : 개별적으로 모듈들이 담겨있는 리스트. 모듈들의 연결관계가 정의되지 않는다. 즉, forward 함수에서 ModulList 내의 모듈들을 이용하여 적절한 연결관계를 정의하는 과정이 필수적이다.

 

참고자료

https://pytorch.org/docs/stable/generated/torch.nn.ModuleList.html

 

ModuleList — PyTorch 1.11.0 documentation

Shortcuts

pytorch.org

https://dongsarchive.tistory.com/67

 

nn.ModuleList vs nn.Sequential

파이토치 코드를 보다보면 자주 등장하는 두 가지 클래스다. 비슷하게 쓰이는것 같으면서도 그 차이점을 구별해라 하면 말하기 어려운데, 구글링을 해 보니 친절한 답변이 있어서 가져왔다. (링

dongsarchive.tistory.com

 

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

pytorch - nn.function과 nn의 차이점  (0) 2022.06.19
torch - GPU 사용하기  (0) 2022.06.13
Pytorch nn.Embedding()  (0) 2022.05.30
Pytorch 기본 문법 - 모델 평가  (0) 2022.05.25
Pytorch 기본 문법 - 모델 훈련  (0) 2022.05.23
728x90

Embedding이란 임의의 길이의 실수 벡터로 밀집되게 표현하는 일련의 방법을 의미한다.

Pytorch는 임베딩 벡터를 사용하는 방법이 크게 두 가지 있다. 바로 임베딩 층을 만들어 훈련 데이터로부터 처음부터 임베딩 벡터를 학습하는 방법과 미리 사전에 훈련된 임베딩 벡터들을 가져와 사용하는 방법이다. 전자에 해당하는 방법이 pytorch에서는 nn.Embedding()을 사용하여 구현한다.

공식 Documentation

This module is often used to store word embeddings and retrieve them using indices. The input to the module is a list of indices, and the output is the corresponding word embeddings.

즉, 이 모듈은 단어 임베딩을 저장하고 인덱스를 사용하여 검색하는 데 자주 사용되고, 모듈에 대한 입력은 인덱스 목록이고 출력은 해당 단어 임베딩이라는 뜻이다.

 

입력 파라미터

torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, 
                   max_norm=None, norm_type=2.0, scale_grad_by_freq=False, 
                   sparse=False, _weight=None, device=None, dtype=None)
  • num_embeddings (int) - 임베딩을 위한 사전의 크기
  • embedding_dim (int) - 임베딩 벡터의 크기
  • padding_idx (int, optional) - 선택적으로 사용하는 인자. 패딩을 위한 토큰의 인덱스를 알려줌.

 

예제

import torch.nn as nn

# 크기 3의 텐서 10개가 포함된 임베딩 모듈
embedding = nn.Embedding(10, 3)

# 각각 4개의 인덱스로 구성된 2개의 표본 배치
input = torch.LongTensor([[1,2,4,5], [4,3,2,9]])
embedding(input)
tensor([[[-0.4509, -0.8820, -2.2471],
         [ 0.4561, -0.7103,  1.2992],
         [-0.4517,  0.1363, -0.4372],
         [-0.6953,  1.3094, -0.1399]],

        [[-0.4517,  0.1363, -0.4372],
         [-0.1696,  0.2681, -1.6542],
         [ 0.4561, -0.7103,  1.2992],
         [-0.4525, -1.2973,  0.6719]]], grad_fn=<EmbeddingBackward0>)

 

출처

https://pytorch.org/docs/stable/generated/torch.nn.Embedding.html

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

torch - GPU 사용하기  (0) 2022.06.13
Pytorch nn.ModuleList  (0) 2022.05.30
Pytorch 기본 문법 - 모델 평가  (0) 2022.05.25
Pytorch 기본 문법 - 모델 훈련  (0) 2022.05.23
Pytorch 기본 문법 - 파라미터 정의  (0) 2022.05.23
728x90

모델에 대한 평가는 함수와 모듈을 이용하는 두 가지 방법이 있다.

1. 함수를 이용하여 모델을 평가하는 방법

import torch
import torchmetrics

preds = torch.randn(10, 5).softmax(dim=-1)
target = torch.randint(5, (10,))

acc = torchmetrics.functional.accuracy(preds, target) # 모델을 평가하기 위해 torchmetrics.functional.accuracy 이용

2. 모듈을 이용하여 모델을 평가하는 방법

import torch
import torchmetrics
metric = torchmetrics.Accuracy()

n_batchs = 10
for i in range(n_batchs):
    preds = torch.randn(10, 5).softmax(dim=-1)
    target = torch.randint(5, (10,))
    
    acc = torchmetrics.functional.accuracy(preds, target)
    print(f'accuracy on batch {i}: {acc}') # 현재 배치에서 모델 정확도
    
acc = metric.compute()
print(f'accuracy no all data: {acc}') # 모든 배치에서 모델 평가

 

혹은 사이킷런에서 제공하는 confusion metrix를 이용하는 방법도 고려해 볼 수 있다.

728x90

'딥러닝 > Pytorch' 카테고리의 다른 글

Pytorch nn.ModuleList  (0) 2022.05.30
Pytorch nn.Embedding()  (0) 2022.05.30
Pytorch 기본 문법 - 모델 훈련  (0) 2022.05.23
Pytorch 기본 문법 - 파라미터 정의  (0) 2022.05.23
Pytorch 기본 문법 - 모델 정의  (0) 2022.05.23
728x90

구체적인 훈련 방법에 대해 알아보자. 가장 먼저 필요한 절차가 optimizer.zero_grad() 메서드를 이용하여 기울기를 초기화하는 것이다. 파이토치는 기울기 값을 계산하기 위해 loss.backward() 메서드를 이용하는데, 이것을 사용하면 새로운 기울기 값이 이전 기울기 값에 누적하여 계산된다. 

이 방법은 순환 신경망 모델을 구현할 때 효과적이지만 누적 계산이 필요하지 않은 모델에 대해서는 불필요하다. 따라서 기울기 값에 대해 누적 계산이 필요하지 않을 때는 입력 값을 모델에 적용하기 전에 optimizer.zero_grad() 메서드를 호출하여 미분 값이 누적되지 않게 초기화해 주어야 한다.

 

딥러닝 학습 절차 파이토치 학습 절차
모델, 손실 함수, 옵티마이저 정의 모델, 손실 함수, 옵티마이저 정의
optimizer.zero_grad( ):
전방향 학습, 기울기 초기화
전방향 학습 (입력 -> 출력 계산) output = model(input) : 출력 계산
손실 함수로 출력과 정답의 차이 계산 loss = loss_fn(output, target) : 오차 계산
역전파 학습 loss.backward( ) : 역전파 학습
기울기 업데이트 optimizer.step( ) : 기울기 업데이트

 

다음은 loss.backward() 메서드를 이용하여 기울기를 자동 계산한다. loss_backward()는 배치가 반복될 때마다 오차가 중첩적으로 쌓이게 되므로 매번 zero_grad()를 사용하여 미분 값을 0으로 초기화한다.

다음은 모델을 훈련시키는 예시 코드이다.

for epoch in range(100):
    yhat = model(x_train)
    loss = criterion(yhat, y_train)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
728x90
728x90

모델을 학습하기 전에 필요한 파라미터들을 정의한다. 사전에 정의할 파라미터는 다음과 같다.

  • 손실 함수 : 학습하는 동안 출력과 실제 값 사이의 오차를 측정한다. 손실 함수로 많이 사용되는 것은 다음과 같다.
    • BCELoss : 이진 분류를 위해 사용
    • CrossEntropyloss : 다중 클래스 분류를 위해 사용
    • MSELoss : 회귀 모델에서 사용
  • 옵티마이저 : 데이터와 손실 함수를 바탕으로 모델의 업데이트 방법을 결정한다. 다음은 옵티마이저의 주요 특성이다.
    • optimizer는 step() 메서드를 통해 전달받은 파라미터를 업데이트한다.
    • 모델의 파라미터별로 다른 기준을 적용시킬 수 있다.
    • torch.optim.Optimizer (params, defaults)는 모든 옵티마이저의 기본이 되는 클래스이다.
    • zero_grad() 메서드는 옵티마이저에 사용된 파라미터들의 기울기를 0으로 만든다.
    • torch.optim.lr_scheduler는 에포크에 따라 학습률을 조절할 수 있다.
  • 학습률 스케줄러 : 미리 지정한 횟수의 에포크를 지날 때마다 학습률을 감소시켜 준다. 학습률 스케줄러를 이용하면 학습 초기에는 빠른 학습을 진행하다가 전역 최소점ㅇ 근처에 다다르면 학습률을 줄여서 최적점을 찾아갈 수 있도록 해준다. 학습률 스케줄러의 종류는 다음과 같다.
    • optim.lr_scheduler.StepLR : 람다 함수를 이용하여 그 함수의 결과를 학습률로 설정한다.
    • optim.lr_scheduler.MultiStepLR : StepLR과 비슷하지만 특정 단계가 아닌 지정된 에포크에만 감마 바율로 감소시킨다.
    • optim.lr_scheduler.ExponentialLR : 에포크마다 이전 학습률에 감마만큼 곱한다.
    • optim.lr_scheduler.CosineAnnealingLR : 학습률을 코사인함수의 형태처럼 변화시킨다. 따라서 학습률이 커지기도 작아지기도 한다.
    • optim.lr_scheduler.ReduceLROnPlateau : 학습이 잘되고 있는지 아닌지에 따라 동적으로 학습률을 변화시킬 수 있다.

 

다음은 모델의 파라미터를 정의하는 예시 코드이다.

from torch.optim import optimizer

criterion = torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer=optimizer, 
                                              lr_lambda=lambda eopch: 0.95 ** epoch)

for eopch in range(1, 100+1): # 에포크 수만큼 데이터를 반복하여 처리
    for x, y in dataloader: # 배치 크기만큼 데이터를 가져와서 학습 진행
        optimizer.zero_grad()

loss_fn(model(x), y).backward()
optimizer.step()
scheduler.step()
728x90

+ Recent posts