[DL] 파이토치 기본 모델

RG-Im·2023년 4월 30일
1

딥러닝

목록 보기
2/4

1. Convolutional NN (CNN)

참고: 전에 작성했던 CNN에 대한 설명 (Tensorflow)

import torch
import torch.nn as nn
import torch.nn.functional as F 

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
nout=[nin+2pks]+1n_{out} = [\frac{n_{in} + 2p - k}{s}] + 1 \\

ninn_{in}: number of input features
noutn_{out}: number of output features
kk: convolution kernel size
pp: convolution padding size
ss: convolution stride size

class CNN(nn.Module):
    def __init__(self, in_channels=1, num_classes=10): # gray scale
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels=8,
                               kernel_size=3, # ────┐
                               stride=1, # default ─┤─> same output size
                               padding=1) # default ┘ 위 식에 따라서 출력 사이즈가 결정된다
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2) # -> half, 마찬가지로 위 식에 따라서 절반으로 줄어든다.
        # 왜 Conv 층을 안 쓰고 Pooling을 쓰냐? 패딩 안하면 사이즈 줄어드는거 똑같은데
        # -> 풀링 층은 학습을 안하기 때문에 속도면에서 이득
        # -> 학습 파라미터를 줄여줘서 모델의 일반화 성능을 높일 수 있다.
        # -> 또한 주변 특성을 합치는 과정으로 위치에 의한 편향을 줄일 수 있다.
        self.conv2 = nn.Conv2d(in_channels=8, out_channels=16,
                               kernel_size=3,
                               stride=1,
                               padding=1) 
        self.fc1 = nn.Linear(16*7*7, num_classes)

    def forward(self, x):
        x = F.relu(self.conv1(x))     # [batch_size, in_channels, 28, 28]
        x = self.pool(x)              # [batch_size, 8, 14, 14]
        x = F.relu(self.conv2(x))     # [batch_size, 16, 14, 14]
        x = self.pool(x)              # [batch_size, 16, 7, 7]
        x = x.reshape(x.shape[0], -1) # [batch_size, 16*7*7]
        x = self.fc1(x)               # [batch_size, num_classes]
        return x
    
model = CNN()
x = torch.randn(64, 1, 28, 28)
print(x.shape)
print(model(x).shape)

2. Recurrent NN (RNN)

class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, num_classes):
        super().__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.rnn = nn.RNN(input_size, # 타임 스탭(시퀀스)마다 특성의 수
                          hidden_size, # 은닉층 특성 수 
                          num_layers, # 순환 층 수 (stacked)
                          batch_first=True) # [batch_size, sequence, feature]
		# GRU를 사용하는 경우
        self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
        
        # LSTM을 사용하는 경우
		self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        # 양방향일 경우
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, bidirectional=True)

        # 모든 hidden state를 사용하는 경우
        self.fc = nn.Linear(hidden_size * sequence_length, num_classes)
        # 가장 마지막 hidden state만 사용하는 경우
        # self.fc = nn.Linear(hidden_size, num_classes)
        # 양방향일 경우 
        # self.fc = nn.Linear(hidden_size*2, num_classes)

    def forward(self, x):
        # 초기 은닉층 설정
        h0 = torch.zeros(self.num_layers, # D * num_layers (양방향일 경우 D=2)
                         x.size(0), # batch_size
                         self.hidden_size).to(device)
		# LSTM을 사용하는 경우 (양방향일 경우 self.num_layers*2)
        # c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(device)

        # Forward propagation
        out, hidden_state = self.rnn(x, h0)
        
        # GRU을 사용하는 경우
        # out, hidden_state = self.gru(x, h0)

        # LSTM을 사용하는 경우
        # out, (hidden_state, cell_state) = self.lstm(x, (h0, c0))

		# 모든 hidden state를 사용하는 경우
        out = out.reshape(out.shape[0], -1)
        out = self.fc(out)
        
        # 가장 마지막 hidden state만 사용하는 경우
        # out = self.fc(out[:, -1, :]) # [batch_size, last hidden state, features]
        return out
        
model = RNN(input_size=100, 
            hidden_size=256, 
            num_layers=2, 
            num_classes=10).to(device)
            
x = torch.randn(64, 128, 100).to(device) # [batch_size, sequence length, features] 
print(x.shape)
print(model(x).shape)

RNN 대신 GRU를 사용하는 경우는 RNN과 마찬가지로 nn.GRU도 입력과 hidden state를 입력받아 nn.RNNnn.GRU로 바꿔주면 된다.
LSTM는 입력과 hidden state, cell state를 입력받고 출력한다. 때문에 순전파 과정을 작성할 때 lstm에 두 번째 인자로 초기 hidden state와 cell state를 Tuple 형태로 넘겨줘야 한다. 반환되는 값 역시 모델 출력과 튜플 형태의 hidden state와 cell state이다.

양방향 LSTM을 사용할 경우에는 bidirectional=True를 인자로 넘겨준다. 분류를 위한 완전 연결 층도 입력 특성 수를 두 배 늘려준다. hidden state와 cell state도 첫 번째 인자를 두 배로 늘려줘야 한다.

참고: 전에 작성했던 자연어 처리 기초 (정규식, 임베딩, RNN, LSTM, seq2seq (Tensorflow))

profile
공부 저장용

0개의 댓글