PyTorch Basic

PyTorch Packages

  • torch: 메인 네임스페이스
  • torch.autograd: 자동 미분을 위한 함수
  • torch.nn: 데이터 구조나 레이어 등 정의
  • torch.optim: 경사 하강법 등 파라미터 최적화 알고리즘 구현
  • torch.utils.data: 미니 배치용 유틸리티 함수
  • torch.onnx: ONNX의 포맷으로 모델을 저장할 때 사용

Tensor

  • 2D Tensor: (batch size, dim)
  • 3D Tensor: (batch size, width, height)
  • 3D Tensor(NLP): (batch size, length, dim)
  • torch.FloatTensor(): 텐서 생성
  • 행렬 곱셈(.matmul()), 원소 별 곱셈(.mul(), *)
  • dim=0: 첫번째 차원(행) 제거 = 같은 열끼리 연산, dim=1: 두번째 차원(열) 제거 = 같은 행끼리 연산
  • view(): reshape, squeeze(): 1인 차원 제거, unsequeeze(): 특정 위치에 1인 차원 추가
  • cat(), stack(), ones_like(), zeros_like()

Linear Regression

선형 회귀 구현

  1. 기본 셋팅 및 변수 선언: seed 설정, x_train 및 y_train 선언
  2. 가중치와 편향의 초기화: W = torch.zeros(1, requires_grad=True)
  3. 가설 세우기: hypothesis = x_train * W + b
  4. 비용 함수 선언: cost = torch.mean((hypothesis - y_train) ** 2)
  5. 경사 하강법 구현:
1
2
3
4
5
optimizer = optim.SGD([W, b], lr=0.01)
optimizer.zero_grad() # gradient를 0으로 초기화
# 파이토치가 미분을 통해 얻은 기울기를 이전에 계산된 기울기 값을 누적시키기 때문에 미분값을 초기화
cost.backword() # 비용 함수를 미분하여 gradient 계산
optimizer.step() # W와 b를 업데이트

Autograd

  • requires_grad=True, backward()

다중 선형 회귀

  • x의 계수를 행렬로 변환해 W와 내적
  • 5x3 크기의 x_train과 3x1 크기의 W를 내적해 5x1 크기의 y_train 계산

nn.Module

1
2
3
4
5
6
from torch import nn
import torch.nn.functional as F

model = nn.Linear(input_dim, output_dim)
cost = F.mse_loss(prediction, y_train)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

Class

1
2
3
4
5
6
7
class LinearRegressionModel(nn.Module):
    def __init__(self): #
        super().__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x):
        return self.linear(x)

Custom Dataset

1
2
3
4
5
6
7
8
9
class CustomDataset(torch.utils.data.Dataset):
    def __init__(self):
    # 데이터셋의 전처리를 해주는 부분

    def __len__(self):
    # 데이터셋의 길이. 즉, 총 샘플의 수를 적어주는 부분

    def __getitem__(self, idx):
    # 데이터셋에서 특정 1개의 샘플을 가져오는 함수

Logistic Regression

  • hypothesis = 1 / (1 + torch.exp(-(x_train.matmul(W) + b)))
  • cost = F.binary_cross_entropy(hypothesis, y_train)
1
2
3
4
5
6
7
8
class BinaryClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(2, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        return self.sigmoid(self.linear(x))

Softmax Regression

  • F.softmax(z, dim=1)
  • F.softmax() + torch.log() = F.log_softmax()
  • F.log_softmax() + F.nll_loss() = F.cross_entropy()
1
2
3
# One-Hot Encoding
y_one_hot = torch.zeros_like(hypothesis)
y_one_hot.scatter_(1, y_train.unsqueeze(1), 1)

Artificial Neural Network

1
2
3
4
5
6
7
model = nn.Sequential(
    nn.Linear(64, 32),
    nn.ReLU(),
    nn.Linear(32, 16),
    nn.ReLU(),
    nn.Linear(16, 10)
)

Convolutional Neural Network

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class CNN(torch.nn.Module):

    def __init__(self):
        super(CNN, self).__init__()

        # 첫번째층
        # ImgIn shape=(?, 28, 28, 1)
        #    Conv     -> (?, 28, 28, 32)
        #    Pool     -> (?, 14, 14, 32)
        self.layer1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2))

        # 두번째층
        # ImgIn shape=(?, 14, 14, 32)
        #    Conv      ->(?, 14, 14, 64)
        #    Pool      ->(?, 7, 7, 64)
        self.layer2 = torch.nn.Sequential(
            torch.nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2))

        # 전결합층 7x7x64 inputs -> 10 outputs
        self.fc = torch.nn.Linear(7 * 7 * 64, 10, bias=True)

        # 전결합층 한정으로 가중치 초기화
        torch.nn.init.xavier_uniform_(self.fc.weight)

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.view(out.size(0), -1)   # 전결합층을 위해서 Flatten
        out = self.fc(out)
        return out

NLP

1
2
3
4
import torch.nn as nn
embedding_layer = nn.Embedding(num_embeddings=len(vocab), 
                               embedding_dim=3,
                               padding_idx=1)

torchtext Error

cannot import name ’load_state_dict_from_url'

  • apply this change in _download_hooks.py
1
2
3
4
try:
    from torch.hub import load_state_dict_from_url
except ImportError:
    from torch.utils.model_zoo import load_url as load_state_dict_from_url

Make Vocabulary

1
2
3
4
from torchtext.vocab import build_vocab_from_iterator

vocab = build_vocab_from_iterator(sequences)
vocab.get_stoi() # 각 단어의 정수 인덱스가 저장된 딕셔너리

Recurrent Neural Network

1
nn.RNN(input_dim, hidden_size, num_layers, batch_fisrt=True)
1
nn.LSTM(input_dim, hidden_size, num_layers, batch_fisrt=True)

Char RNN

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Net(nn.Module):
    def __init__(self, vocab_size, input_size, hidden_size, batch_first=True):
        super(Net, self).__init__()
        self.embedding_layer = nn.Embedding(num_embeddings=vocab_size, # 워드 임베딩
                                            embedding_dim=input_size)
        self.rnn_layer = nn.RNN(input_size, hidden_size, # 입력 차원, 은닉 상태의 크기 정의
                                batch_first=batch_first)
        self.linear = nn.Linear(hidden_size, vocab_size) # 출력은 원-핫 벡터의 크기를 가져야함. 또는 단어 집합의 크기만큼 가져야함.

    def forward(self, x):
        # 1. 임베딩 층
        # 크기변화: (배치 크기, 시퀀스 길이) => (배치 크기, 시퀀스 길이, 임베딩 차원)
        output = self.embedding_layer(x)
        # 2. RNN 층
        # 크기변화: (배치 크기, 시퀀스 길이, 임베딩 차원)
        # => output (배치 크기, 시퀀스 길이, 은닉층 크기), hidden (1, 배치 크기, 은닉층 크기)
        output, hidden = self.rnn_layer(output)
        # 3. 최종 출력층
        # 크기변화: (배치 크기, 시퀀스 길이, 은닉층 크기) => (배치 크기, 시퀀스 길이, 단어장 크기)
        output = self.linear(output)
        # 4. view를 통해서 배치 차원 제거
        # 크기변화: (배치 크기, 시퀀스 길이, 단어장 크기) => (배치 크기*시퀀스 길이, 단어장 크기)
        return output.view(-1, output.size(2))

# 모델 생성
model = Net(vocab_size, input_size, hidden_size, batch_first=True)
# 손실함수 정의
loss_function = nn.CrossEntropyLoss() # 소프트맥스 함수 포함이며 실제값은 원-핫 인코딩 안 해도 됨.
# 옵티마이저 정의
optimizer = optim.Adam(params=model.parameters())

Classification with GRU

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class GRU(nn.Module):
    def __init__(self, n_layers, hidden_dim, n_vocab, embed_dim, n_classes, dropout_p=0.2):
        super(GRU, self).__init__()
        self.n_layers = n_layers
        self.hidden_dim = hidden_dim

        self.embed = nn.Embedding(n_vocab, embed_dim)
        self.dropout = nn.Dropout(dropout_p)
        self.gru = nn.GRU(embed_dim, self.hidden_dim,
                          num_layers=self.n_layers,
                          batch_first=True)
        self.out = nn.Linear(self.hidden_dim, n_classes)

    def forward(self, x):
        x = self.embed(x)
        h_0 = self._init_state(batch_size=x.size(0)) # 첫번째 히든 스테이트를 0벡터로 초기화
        x, _ = self.gru(x, h_0) # GRU의 리턴값은 (배치 크기, 시퀀스 길이, 은닉 상태의 크기)
        h_t = x[:,-1,:] # (배치 크기, 은닉 상태의 크기)의 텐서로 크기가 변경됨. 즉, 마지막 time-step의 은닉 상태만 가져온다.
        self.dropout(h_t)
        logit = self.out(h_t) # (배치 크기, 은닉 상태의 크기) -> (배치 크기, 출력층의 크기)
        return logit

    def _init_state(self, batch_size=1):
        weight = next(self.parameters()).data
        return weight.new(self.n_layers, batch_size, self.hidden_dim).zero_()