300x250

PyTorch를 간단히 다루어본 적이 있는데, 앞으로의 연구에 익숙하게 활용하기 위해 PyTorch 내용을 정리해보려 한다.

대부분의 내용은 유튜브의 '모두를 위한 딥러닝 시즌2'를 참고하였다.

기본적인 내용과 파이썬 문법은 어느 정도 알고 있다고 가정하고, PyTorch 실습 내용 위주로 정리해두었다.

 

https://www.youtube.com/watch?v=37jxyHXzxU4&list=PLQ28Nx3M4JrhkqBVIXg-i5_CVVoS1UzAv&index=28 

 

 

 

 

목차

     

     

     

    1. RNN 개념

     

    Recurrent Neural Network는 Sequential Data를 주로 다루기 위한 인공신경망이다.

    Sequential Data는 data의 값뿐만 아니라 순서도 중요한 데이터를 말한다.

    예를 들어, 시간에 따라, time step t-1에서의 데이터와 t에서의 데이터가 연관성이 깊은 경우를 들 수 있다. 그리고 word, sentence 등 자연어 또한 단어가 어떤 순서로 나열되어있는가에 따라 의미가 완전히 달라질 수 있다.

     

    RNN

     

    t = 0 일 때(가장 왼쪽), 입력값 x_0가 셀 A에서 처리된다. 여기서 두 갈래로 나뉘는데, 하나는 h_0으로 출력되고, 다른 하나는 다음(t = 1) 셀로 전달된다.

    이렇게 출력되지 않고 다음 셀로 전달되는 값을 'Hidden State'라 한다. 즉, RNN 밖에서 보았을 때에는 hidden state를 알 수 없다.

    따라서 h_1, h_2, ... 등의 출력 값들은 그 전 입력값의 처리 결과들의 영향을 받게 된다. 이러한 원리로 모델이 데이터의 순서를 이해하는 것이다.

     

    또한, RNN은 모든 셀이 parameter를 공유한다. 따라서 sequence가 아주 길더라도 하나의 셀 A로 처리가 가능하다.

     

    셀 A에서는 다음 연산을 수행한다.

     

    \( h_t = f(h_{t-1}, x_t) \)

     

    여기서 함수 \(f\)는 activation function이 될 것이고, \(h_{t-1}\)과 \(x_t\) term에 weight가 붙을 것이다.

    이 연산이 어떤 함수로 어떻게 이루어지는가에 따라 다양한 모델이 존재하는데, 그중 대표적인 것이 바로 LSTM(Long-Short Term Memory)과 GRU(Gated Recurrent Unit)이다.

     

    이 개념을 확장하여 다양한 RNN 모델을 살펴보자.

     

    RNNs

     

    각각의 형태에 따라 다음과 같이 예를 들어볼 수 있다.

    • one to one : 일반적인 Neural Network이다. (이런 형태를 RNN이라고 하지는 않는다.)
    • one to many : 하나의 입력을 받아 여러 출력을 내는 경우를 말한다.
      • 이미지 하나를 입력받아 여러 단어를 출력하는 image captioning task를 예로 들 수 있다.
    • many to one : 여러 입력을 받아 하나의 출력을 내는 경우를 말한다.
      • 문장을 입력받아 감정 label을 출력하는 감성 분석 task를 예로 들 수 있다.
    • many to many : 여러 입력을 받아 여러 출력을 내는 경우이다. 여기서 서로 입출력되는 위치가 다른가 같은가에 따라 나뉜다.
      • 위치가 다른 경우 : 한 문장을 입력받은 후에 어떤 다른 문장을 출력해주는 경우로, 번역을 예로 들 수 있다.
      • 위치가 같은 경우 : video에 새로운 object가 생길 때 그것을 알려주는 task 등을 예로 들 수 있다.

     

     

     

     

     

     

    2. RNN in PyTorch

     

    이제 RNN을 PyTorch에서 어떻게 구현할 수 있는지 알아보자.

    PyTorch에서 RNN을 구현하는 함수는 'torch.nn.RNN'으로, 다음과 같이 매우 간단하게 구현할 수 있다.

     

    rnn = torch.nn.RNN(input_size, hidden_size)
    outputs, _status = rnn(input_data)

     

    윗 줄을 셀 A를 선언하는 과정으로, 아랫줄을 입력을 주고, 출력을 받는 과정으로 생각할 수 있다. _status는 hidden state를 나타낸다.

    여기서, input_data에 들어갈 Input Tensor를 특정한 형식에 맞게 맞춰줄 필요가 있다.

     

     

     

     

    1) Input Size (one-hot encoding)

     

    먼저, 단어가 입력된다고 한다면 각 글자를 벡터의 형태로 변환하여 one-hot encoding 해주는 과정이 필요하다.

    one-hot encoding이란, 단어에 포함된 글자 수를 벡터의 차원으로 하고, 표현할 단어의 인덱스에 1의 값을 부여한 후 다른 인덱스는 모두 0으로 만든 벡터를 생성하는 과정을 말한다. 다음과 같이 두 가지 step으로 나타낼 수 있다.

     

    1. 정수 인코딩 : 각 글자에 고유한 정수를 부여한다.
    2. 표현하고 싶은 글자의 고유한 정수를 인덱스로 간주하여 해당 위치에는 1을, 다른 위치에는 0을 부여한다.

     

    말로 표현하다보니 좀 복잡해 보이는데, 예시를 보면 금방 이해할 수 있을 것이다.

    예를 들어, 'hello'라는 단어를 글자 단위로 one-hot encoding 하면 다음과 같은 one-hot vector를 얻는다.

     

    one-hot encoding example

     

    이렇게 구한 input_size는 위의 RNN 과정에 들어갈 input_data의 세 번째 차원의 값이 된다.

     

     

     

     

    2) Hidden State Size

     

    hidden state의 size는 결국 output shape의 세 번째 차원이 된다. 즉 원하는 output size와 같다.

     

    hidden_size

     

    앞서 셀 A에서 연산된 결과를 두 가지로 나누어 하나는 output으로 출력되고, 다른 하나는 hidden state로 다음 time step에 그대로 전해진다고 하였다. 따라서 두 개의 값은 같고, size도 당연히 같다.

     

     

     

     

    3) Sequence Length

     

    sequence가 총 몇 개 존재하는가, 즉 셀이 총 몇개인가를 나타낸다.

    예를 들어, hello를 입력으로 준다면 sequence length는 5가 될 것이다.

     

    Sequence Length

     

    여기서, PyTorch에서는 모델이 sequence length를 알아서 파악한다. 따라서 모델에 따로 sequence length 값을 전달해줄 필요 없이, input data를 잘 전처리 해주면 된다.

    당연히 각 셀에서의 입력값 개수와 결과값 개수는 같을 것이므로, 위와 같이 input data와 output data의 두 번째 차원은 같은 값을 가질 것이다. 

     

     

     

     

    4) Batch Size

     

    CNN에서와 마찬가지로, RNN에서도 여러 데이터를 묶어 하나의 batch로 만들어 학습을 진행한다.

     

    Batch example

     

    위 예시에서는 h, e, l, o 네 개의 글자로 만들 수 있는 많은 데이터 중 세 개를 하나의 batch로 묶어 학습을 진행한다.

    이러한 batch size 또한 모델에서 자동으로 파악하며, output data에서와 input data에서의 첫 번째 차원에 위치하고, 같은 값을 갖는다.

     

     

     

     

    5) Simple RNN Model

     

    위 예시에서와 같은 방법으로 간단한 RNN 모델을 만들어보자.

     

    import torch
    import numpy as np
    
    input_size = 4
    hidden_size = 2
    
    # one-hot encoding
    h = [1, 0, 0, 0]
    e = [0, 1, 0, 0]
    l = [0, 0, 1, 0]
    o = [0, 0, 0, 1]
    
    input_data_np = np.array([[h, e, l, l, o],
    						  [e, o, l, l, l],
                              [l, l, e, e, l]], dtype=np.float32)
    
    # transform as torch tensor
    input_data = torch.Tensor(input_data_np)
    
    # RNN
    rnn = torch.nn.RNN(input_size, hidden_size)
    outputs, _status = rnn(input_data)

     

     

     

     

     

    3. Hihello Example, Charseq Example

     

    'hihello' problem이란, 단순히 'h', 'i', 'h', 'e', 'l', 'l', 'o'의 character 순서를 RNN 모델이 학습하고 예측하도록 하는 문제이다.

    실습 코드를 살펴보자.

     

    char_set = ['h', 'i', 'e', 'l', 'o']
    
    # hyperparameters
    input_size = len(char_set)
    hidden_size = len(char_set)
    learning_rate = 0.1
    
    # data setting
    x_data = [[0, 1, 0, 2, 3, 3]] # h, i, h, e, l, l
    
    x_one_hot = [[[1, 0, 0, 0, 0], # h
    			  [0, 1, 0, 0, 0], # i
                  [1, 0, 0, 0, 0], # h
                  [0, 0, 1, 0, 0], # e
                  [0, 0, 0, 1, 0], # l
                  [0, 0, 0, 1, 0]]] # l
    
    y_data = [[1, 0, 2, 3, 3, 4]] # i, h, e, l, l, o
    
    # transform as torch tensor variable
    X = torch.FloatTensor(x_one_hot)
    Y = torch.LongTensor(y_data)

     

    그리고 Charseq problem은 hihello problem을 조금 더 일반화한 것으로, hihello 문제에서는 'hihello'라는 string에만 적용이 가능한 모델이었으나, Charseq에서 만드는 모델은 어떤 문자열이 샘플에 들어가든 해당 샘플을 학습하는 모델이다.

     

    sample = " if you want you"
    
    # make dictionary
    char_set = list(set(sample)) # set() : 중복되는 char는 제외하고 list로 만듦
    char_dic = {c: i for i, c in enumerate(char_set)} # index와 character를 받아 dictionary 생성
    
    # hyperparameters
    dic_size = len(char_dic)
    hidden_size = len(char_dic)
    learning_rate = 0.1
    
    # data setting
    sample_idx = [char_dic[c] for c in sample] # character to index
    x_data = [sample_idx[:-1]]
    x_one_hot = [np.eye(dic_size)[x] for x in x_data] # eye(): identity matrix
    y_data = [sample_idx[1:]]
    
    # transform as torch tensor variable
    X = torch.FloatTensor(x_one_hot)
    Y = torch.LongTensor(y_data)
    
    # RNN
    rnn = torch.nn.RNN(input_size, hidden_size, batch_first=True) # batch_first : batch dimension이 맨 앞 차원으로
    
    # loss & optimizer setting
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = optim.Adam(rnn.parameters(), learning_rate)
    
    # training
    for i in range(100):
    	optimizer.zero_grad() # gradient 0으로 안만들어주면 기존 gradient 연산 결과가 축적됨
        outputs, _status = rnn(X)
        loss = criterion(outputs.view(-1, input_size), Y.view(-1))
        loss.backward()
        optimizer.step()
        
        # prediction
        result = outputs.data.numpy().argmax(axis=2)
        result_str = ''.join([char_set[c] for c in np.squeeze(result)]
        print(i, "loss: ", loss.item(), "prediction: ", result,
        	  "true Y: ", y_data, "prediction str: ", result_str)

     

     

    728x90
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기