일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 통계
- 데이터 분석
- 데이터 전처리
- 파이썬
- pytorch
- 시각화
- matplotlib
- ADsP
- 데이터분석준전문가
- scikit learn
- pandas
- tableau
- r
- SQL
- ML
- 코딩테스트
- 이코테
- 이것이 코딩테스트다
- Python
- 자격증
- Deep Learning Specialization
- 데이터분석
- sklearn
- 회귀분석
- 딥러닝
- 머신러닝
- IRIS
- SQLD
- Google ML Bootcamp
- 태블로
- Today
- Total
함께하는 데이터 분석
[Find-A][Pytorch] 임베딩 함수를 이용한 LSTM, GRU 모델 구현 본문
RNN 모델 구현
import torch
import torch.nn as nn
import string
import random
import re
import time, math
num_epochs = 2000
print_every = 100
plot_every = 10
chunk_len = 200
hidden_size = 100
batch_size = 1
num_layers = 1
embedding_size = 70
lr = 0.002
필요한 하이퍼 파라미터를 지정
# import 했던 string에서 출력가능한 문자들을 다 불러옴
all_characters = string.printable
# 출력가능한 문자들의 개수를 저장
n_characters = len(all_characters)
print(all_characters)
print('num_chars = ', n_characters)
>>> 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
num_chars = 100
현재 예시는 문자에 대한 순환 신경망이므로 사용 가능한 모든 문자를 저장해놓고 전체 가지 수도 지정
file = open('경로/tiny-shakespeare.txt').read()
file_len = len(file)
print('file_len =', file_len)
>>> file_len = 1115394
이제 학습에 필요한 셰익스피어의 작품이 담긴 데이터셋(tinyshakespeare)을 사용
https://github.com/jcjohnson/torch-rnn/blob/master/data/tiny-shakespeare.txt
데이터는 여기 깃허브에 존재
# 이 함수는 텍스트 파일의 일부분을 랜덤하게 불러오는 코드
def random_chunk():
# (시작지점 < 텍스트파일 전체길이 - 불러오는 텍스트의 길이)가 되도록 시작점과 끝점을 정함
start_index = random.randint(0, file_len - chunk_len)
end_index = start_index + chunk_len + 1
return file[start_index:end_index]
print(random_chunk())
>>> le husband. Come, Camillo,
And take her by the hand, whose worth and honesty
Is richly noted and here justified
By us, a pair of kings. Let's from this place.
What! look upon my brother: both your pard
모든 파일을 한 번에 학습할 수 없기 때문에 일정한 크기로 잘라야 함
랜덤 한 위치에서 시작해 일정 크기만큼의 문자열을 읽어오는 random_chunk라는 함수를 만들어 줌
# 문자열을 받았을때 이를 인덱스의 배열로 바꿔주는 함수
def char_tensor(string):
tensor = torch.zeros(len(string)).long()
for c in range(len(string)):
tensor[c] = all_characters.index(string[c])
return tensor
print(char_tensor('ABCdef'))
>>> tensor([36, 37, 38, 13, 14, 15])
이렇게 일정 크기로 문자열을 읽어온 다음 이를 앞에서 저장해둔 출력 가능한 문자열 리스트를 통해 인덱스로 바꿔주는 char_tensor 함수를 만듦
코드에 예시로 든 것처럼 문자열 ABCdef를 인덱스화하면 결과는 tensor([36, 37, 38, 13, 14, 15])로 나옴
# 랜덤한 텍스트 chunk를 불러와서 이를 입력과 목표값을 바꿔주는 함수
# 예를 들어 pytorch라는 문자열이 들어오면 입력은 pytorc / 목표값은 ytorch 가 됨
def random_training_set():
chunk = random_chunk()
inp = char_tensor(chunk[:-1])
target = char_tensor(chunk[1:])
return inp, target
ramdom_training_set 함수는 랜덤 한 문자열을 불러와 입력값과 목푯값으로 나눠 리턴함
그다음부터는 임베딩과 RNN을 통해 학습
이 과정을 그림으로 그려보면 위와 같음(각 수치는 편의를 위해 임의로 지정한 것)
이 그림에는 디코딩 부분이 들어 있는데 이는 임베딩과 순환 신경망을 통과해 나온 결과를 임베딩하기 전 데이터 형태로 돌려놓는 역할
만약 알파벳 26개를 길이 50짜리 벡터에 임베딩 했다면 다시 알파벳을 나타내는 길이 26짜리 벡터로 돌려놔야 해당 출력을 보고 해석할 수 있기 때문
이 예시에서는 이를 단순하게 한 층의 신경망으로 구현
class RNN(nn.Module):
def __init__(self, input_size, embedding_size, hidden_size, output_size, num_layers=1):
super(RNN, self).__init__()
self.input_size = input_size
self.embedding_size = embedding_size
self.hidden_size = hidden_size
self.output_size = output_size
self.num_layers = num_layers
self.encoder = nn.Embedding(self.input_size, self.embedding_size)
self.rnn = nn.RNN(self.embedding_size,self.hidden_size,self.num_layers)
self.decoder = nn.Linear(self.hidden_size, self.output_size)
def forward(self, input, hidden):
out = self.encoder(input.view(1,-1))
out,hidden = self.rnn(out,hidden)
out = self.decoder(out.view(batch_size,-1))
return out,hidden
def init_hidden(self):
hidden = torch.zeros(self.num_layers, batch_size, self.hidden_size)
return hidden
model = RNN(n_characters, embedding_size, hidden_size, n_characters, num_layers)
위의 클래스를 인스턴스화하는 코드
model = RNN(input_size=n_characters,
embedding_size=embedding_size,
hidden_size=hidden_size,
output_size=n_characters,
num_layers=2)
해당 인스턴스를 테스트 해보는 코드를 작성해보겠음
inp = char_tensor("A")
print(inp)
hidden = model.init_hidden()
print(hidden.size())
out,hidden = model(inp,hidden)
print(out.size())
>>> tensor([36])
torch.Size([2, 1, 100])
torch.Size([1, 100])
A라는 문자열을 입력으로 주고 char_tensor 함수를 사용해 이를 텐서로 바꿔줌
그다음 은닉 상태도 초기화해주고 이렇게 만든 입력과 은닉 상태를 RNN 인스턴스에 인수로 전달하면 forward 함수에 정의된 대로 연산이 진행됨
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
loss_func = nn.CrossEntropyLoss()
def test():
start_str = "b"
inp = char_tensor(start_str)
hidden = model.init_hidden()
x = inp
print(start_str,end="")
for i in range(200):
output,hidden = model(x,hidden)
# 여기서 max값을 사용하지 않고 multinomial을 사용하는 이유는 만약 max 값만 쓰는 경우에
# 생성되는 텍스트가 다 the the the the the 이런식으로 나오기 때문
# multinomial 함수를 통해 높은 값을 가지는 문자들중에 램덤하게 다음 글자를 뽑아내는 방식으로 자연스러운 텍스트를 생성
output_dist = output.data.view(-1).div(0.8).exp()
top_i = torch.multinomial(output_dist, 1)[0]
predicted_char = all_characters[top_i]
print(predicted_char,end="")
x = char_tensor(predicted_char)
for i in range(num_epochs):
# 랜덤한 텍스트 덩어리를 샘플링하고 이를 인덱스 텐서로 변환
inp,label = random_training_set()
hidden = model.init_hidden()
loss = torch.tensor([0]).type(torch.FloatTensor)
optimizer.zero_grad()
for j in range(chunk_len-1):
x = inp[j]
y_ = label[j].unsqueeze(0).type(torch.LongTensor)
y,hidden = model(x,hidden)
loss += loss_func(y,y_)
loss.backward()
optimizer.step()
if i % 100 == 0:
print("\n",loss/chunk_len,"\n")
test()
print("\n","="*100)
모델 부분을 보면 입력값이 들어왔을 때 먼저 이를 임베딩 해주고 RNN 노드에 임베딩 값과 은닉 상태 값을 전달해 줌
이때 임베딩 차원이 문자열 크기와 다르기 때문에 RNN 노드의 결괏값을 디코더를 통해 맞춰줘야 다시 문자로 바꿀 수 있음
초기에는 위와 같은 수준으로 나옴
한참 학습을 진행하고 나면 위와 같은 수준까지 결과가 나오게 됨
GRU 모델 구현
self.rnn = nn.RNN(self.embedding_size,self.hidden_size,self.num_layers)
GRU는 기본 RNN 모델을 클래스 정의 내부에서 위의 라인을
self.rnn = nn.GRU(self.embedding_size,self.hidden_size,self.num_layers)
이렇게 바꾸면 끝
이것이 가능한 이유는 GRU가 RNN과 같이 은닉 상태만 가지고 있기 때문
LSTM
class RNN(nn.Module):
def __init__(self, input_size, embedding_size, hidden_size, output_size, num_layers=1):
super(RNN, self).__init__()
self.input_size = input_size
self.embedding_size = embedding_size
self.hidden_size = hidden_size
self.output_size = output_size
self.num_layers = num_layers
self.encoder = nn.Embedding(self.input_size, self.embedding_size)
self.rnn = nn.LSTM(self.embedding_size,self.hidden_size,self.num_layers)
self.decoder = nn.Linear(self.hidden_size, self.output_size)
def forward(self, input, hidden, cell):
out = self.encoder(input.view(1,-1))
out,(hidden,cell) = self.rnn(out,(hidden,cell))
out = self.decoder(out.view(batch_size,-1))
return out,hidden,cell
def init_hidden(self):
hidden = torch.zeros(self.num_layers,batch_size,self.hidden_size)
cell = torch.zeros(self.num_layers,batch_size,self.hidden_size)
return hidden,cell
model = RNN(n_characters, embedding_size, hidden_size, n_characters, num_layers)
구현적 차이는 셀 상태의 유무 정도로 볼 수 있음
https://www.hanbit.co.kr/store/books/look.php?p_code=B7818450418
'학회 세션 > 파인드 알파' 카테고리의 다른 글
[Find-A][Pytorch] 학습률 (1) | 2022.09.25 |
---|---|
[Find-A][Pytorch] 초기화 (0) | 2022.09.25 |
[Find-A] LSTM, GRU, 임베딩 (1) | 2022.09.21 |
[Find-A] 인공 신경망 (0) | 2022.09.12 |
[Find-A][Scikit Learn] 앙상블 학습 (0) | 2022.08.28 |