본문 바로가기

프로그래머/Pytorch

[Pytorch] RNN으로 문장 학습시키기

아래의 문장의 RNN 모델을 통하여 모델링 한다.

sentence = ("if you want to build a ship, don't drum up people together to collect wood and don't assign them tasks and work, but rather teach them to long for the endless immensity of the sea.")

a. 위의 문장을 이용하여 모델을 학습 시킬 때 10문자씩 잘라서 학습시킨다.

b. 학습 완료 후 학습 시 사용했던 입력 데이터를 이용하여 결과물을 출력한다.

전체코드

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

sentence = ("if you want to build a ship, don't drum up people together to "
            "collect wood and don't assign them tasks and work, but rather "
            "teach them to long for the endless immensity of the sea.")

char_set = list(set(sentence)) # 중복을 제거한 문자 집합 생성
char_dic = {c: i for i, c in enumerate(char_set)} # 각 문자에 정수 인코딩

print(char_dic) # 공백도 여기서는 하나의 원소

dic_size = len(char_dic)
print('문자 집합의 크기 : {}'.format(dic_size))

# 하이퍼파라미터 설정
hidden_size = dic_size
sequence_length = 10  # 임의 숫자 지정
learning_rate = 0.1

# 데이터 구성
x_data = []
y_data = []

for i in range(0, len(sentence) - sequence_length):
    x_str = sentence[i:i + sequence_length]
    y_str = sentence[i + 1: i + sequence_length + 1]
    print(i, x_str, '->', y_str)

    x_data.append([char_dic[c] for c in x_str])  # x str to index
    y_data.append([char_dic[c] for c in y_str])  # y str to index

print(x_data[0]) # if you wan에 해당됨.
print(y_data[0]) # f you want에 해당됨.

x_one_hot = [np.eye(dic_size)[x] for x in x_data] # x 데이터는 원-핫 인코딩
X = torch.FloatTensor(x_one_hot)
Y = torch.LongTensor(y_data)

print('훈련 데이터의 크기 : {}'.format(X.shape))
print('레이블의 크기 : {}'.format(Y.shape))

class Net(torch.nn.Module):
    def __init__(self, input_size, hidden_size, layers): # 현재 hidden_size는 dic_size와 같음.
        super(Net, self).__init__()
        self.rnn = torch.nn.RNN(input_size, hidden_size, num_layers=layers, batch_first=True)
        self.fc = torch.nn.Linear(hidden_size, hidden_size, bias=True)

    def forward(self, x):
        x, _status = self.rnn(x)
        x = self.fc(x)
        return x

net = Net(dic_size, hidden_size, 2) # 이번에는 층을 두 개 쌓습니다.

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), learning_rate)

outputs = net(X)
print(outputs.shape) # 3차원 텐서

print(outputs.view(-1, dic_size).shape) # 2차원 텐서로 변환.

print(Y.shape)
print(Y.view(-1).shape)

for i in range(100):
    optimizer.zero_grad()
    outputs = net(X) # (170, 10, 25) 크기를 가진 텐서를 매 에포크마다 모델의 입력으로 사용
    loss = criterion(outputs.view(-1, dic_size), Y.view(-1))
    loss.backward()
    optimizer.step()

    # results의 텐서 크기는 (170, 10)
    results = outputs.argmax(dim=2)
    predict_str = ""
    for j, result in enumerate(results):
        if j == 0: # 처음에는 예측 결과를 전부 가져오지만
            predict_str += ''.join([char_set[t] for t in result])
        else: # 그 다음에는 마지막 글자만 반복 추가
            predict_str += char_set[result[-1]]

    print(predict_str)
분석

set
집합 자료형 - set의 특징은

  • 중복을 허용하지 않는다.

  • 순서가 없다(Unordered).

  • 리스트나 튜플은 순서가 있기(ordered) 때문에 인덱싱을 통해 자료형의 값을 얻을 수 있지만 set 자료형은 순서가 없기(unordered) 때문에 인덱싱으로 값을 얻을 수 없다.

  • 이는 마치 딕셔너리와 비슷하다 - 딕셔너리 역시 순서가 없는 자료형이라 인덱싱을 지원하지 않는다.

  • 만약 set 자료형에 저장된 값을 인덱싱으로 접근하려면 다음과 같이 리스트나 튜플로 변환한후 해야 한다.

  • 중복을 허용하지 않는 set의 특징은 자료형의 중복을 제거하기 위한 필터 역할로 종종 사용하기도 한다.

# " "을 통해 줄바꿈을 하는 모습
# 공백도 문자다
sentence = ("if you want to build a ship, don't drum up people together to "
            "collect wood and don't assign them tasks and work, but rather "
            "teach them to long for the endless immensity of the sea.")

char_set = list(set(sentence)) # 중복을 제거한 문자 집합 생성
print(char_set)

['e', 'b', 'w', ',', 'i', 't', 'y', 'p', 'c', 'l', 'h', 's', '.', 'o', 'g', ' ', 'u', "'", 'f', 'r', 'm', 'a', 'd', 'n', 'k']

각 문자에 정수 인코딩

# 딕셔너리 자료형
char_dic = {c: i for i, c in enumerate(char_set)} # 각 문자에 정수 인코딩
print(char_dic)

{'e': 0, 'b': 1, 'w': 2, ',': 3, 'i': 4, 't': 5, 'y': 6, 'p': 7, 'c': 8, 'l': 9, 'h': 10, 's': 11, '.': 12, 'o': 13, 'g': 14, ' ': 15, 'u': 16, "'": 17, 'f': 18, 'r': 19, 'm': 20, 'a': 21, 'd': 22, 'n': 23, 'k': 24}

하이퍼파라미터 설정

# 포함된 문자의 개수를 hidden size로 설정
hidden_size = dic_size
# 10문자씩 잘라서 학습시킨다
sequence_length = 10  # 임의 숫자 지정
learning_rate = 0.1

전체 문장에 대해 해당 문자를 index로 변환하여 쌓는다

for i in range(0, len(sentence) - sequence_length):
    # x_str은 0 ~ i+seq_len-1, y_str은 0 ~ i+seq_len+1
    x_str = sentence[i:i + sequence_length]
    y_str = sentence[i + 1: i + sequence_length + 1]
    print(i, x_str, '->', y_str)

    x_data.append([char_dic[c] for c in x_str])  # x str to index
    y_data.append([char_dic[c] for c in y_str])  # y str to index

print(x_data[0]) # if you wan에 해당됨.
print(y_data[0]) # f you want에 해당됨.

0 if you wan -> f you want
1 f you want -> you want
...

문자를 index로 변환한다

  • if you wan -> f you want

    [4, 18, 15, 6, 13, 16, 15, 2, 21, 23]
    [18, 15, 6, 13, 16, 15, 2, 21, 23, 5]

원-핫 인코딩

# dic_size : 25
# np.eye : 2D array with ones on the diagonal and zeros elsewhere
# np.eye(dic_size) : 25x25
# x_data : 170x10
x_one_hot = [np.eye(dic_size)[x] for x in x_data] # x 데이터는 원-핫 인코딩
X = torch.FloatTensor(x_one_hot)
Y = torch.LongTensor(y_data)

print('훈련 데이터의 크기 : {}'.format(X.shape))
print('레이블의 크기 : {}'.format(Y.shape))

훈련 데이터의 크기 : torch.Size([170, 10, 25])
레이블의 크기 : torch.Size([170, 10])

Network 설계

class Net(torch.nn.Module):
    # 현재 hidden_size는 dic_size와 같음.
    def __init__(self, input_size, hidden_size, layers): 
        super(Net, self).__init__()
        self.rnn = torch.nn.RNN(input_size, hidden_size, num_layers=layers, batch_first=True)
        # 형태가 유지된다
        self.fc = torch.nn.Linear(hidden_size, hidden_size, bias=True)

    def forward(self, x):
        print(x.shape)
        x, _status = self.rnn(x)
        print(x.shape)
        x = self.fc(x)
        print(x.shape)
        return x

torch.Size([170, 10, 25])
torch.Size([170, 10, 25])
torch.Size([170, 10, 25])

기타 설정

# 이번에는 층을 두 개 쌓습니다.
net = Net(dic_size, hidden_size, 2) 

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), learning_rate)

outputs = net(X)
print(outputs.shape) # 3차원 텐서

print(outputs.view(-1, dic_size).shape) # 2차원 텐서로 변환.

print(Y.shape)
print(Y.view(-1).shape)

torch.Size([170, 10, 25])
torch.Size([1700, 25])
torch.Size([170, 10])
torch.Size([1700])

training

for i in range(100):
    optimizer.zero_grad()
    # (170, 10, 25) 크기를 가진 텐서를 매 에포크마다 모델의 입력으로 사용
    outputs = net(X) 
    #torch.Size([1700, 25]), torch.Size([1700]) 
    loss = criterion(outputs.view(-1, dic_size), Y.view(-1))
    loss.backward()
    optimizer.step()

    # results의 텐서 크기는 (170, 10)
    results = outputs.argmax(dim=2)
    predict_str = ""
    for j, result in enumerate(results):
        if j == 0: # 처음에는 예측 결과를 전부 가져오지만
            predict_str += ''.join([char_set[t] for t in result])
        else: # 그 다음에는 마지막 글자만 반복 추가
            predict_str += char_set[result[-1]]

    print(predict_str)

gbgggbggg'bgbggbbgggggggbgbggggbbbgggkggbbgbggbgbgggggggggggggbgggbbgggggggg'ggggbbbgggggggggbggggggggggbggggbgbgbbggggggbgggggbggggggggggggbggggbgggggggggbggggggggbgbgggbgbgggggg
eoooooooooooooooooooooooeoeoooooeoooooeooooooooooooooooeoooooooooooooooooooooooooooooooooooooeooooooooooooooooooooooooooeoooooooeooeooooooooo ooo ooeooooooooooooooooooooooooeooooo
t kt , t tt k t n , t n k, k kt n ,l t t t kt m t mt t kt t t kt , t
tttt ttttttttttttttttttttttttettttettttt ttttttttttttttttttetttt ttetttttttttttt tttetttt tttttttettttetttttelttt ltttetetttettttttt ttettet ttettttetttttttttt ttt tttt ttttettttt
eaeaeaeaeheeeeueaeaeaeheaeheeehdeeeeeeeaeaeeueaeaeheheheueaeaehaeeueaeeudeaea eaeeeeeeeheeeaeaueaeaehmeeheeremehaeueaeeheaeaeaeheaeaehaeaeeaeaeeaeheeueaeaehueeeueaeaeheheaeaeaeaeh
ioioidom oooi i iii eii ioodi oooii ioiihmeiphieii em ie ihiphme ioii oiieom horii i io m ieai iho i moi hioie ie om ieaphidh m ieaieap ioi oom ieiohi oi i i oio i oip iih
noi on on o ono on oni o no n n on on n on on on o o n on on on noi o n on ononp on on on onn n n o n n
nn on on on nnnonon'l on onm nonoonotonnon n non on onot on' on on nono p n n nonon os on no on'onn nonoon'onp non on on onn n n on o n onon n n n'o
n ' o d e on s tn t d o os od e n d od o n d d o t d o d s s so d d
t ar erd totl t dot d t ton er l tnl ers td dsd er tosle terk dos' ted ton' dr t tth r tod tod tor dr ter er tods do r erslees tod eer ad rol tolets trn eer er
tearl ars toalh.t erotot k r.nl errsl ruleerste.lorem er toalhtehr ros' eod e.ml rr tp tther aotot tos rom edul tom er toasherh r aoslhad tor eer ar eol tpl rs ,rlarlteer .r
teeolteod toedh tlerotoem, toal eoam aptehrpteetheerher toethtehrd roal aos aoal ta tp tther aotet tot aor t tut tother toadh rher aoathel torlther ad eotlapt as tatau ther er
tteot aul toetu tt tothtpt toa toam aptehr teathepthem toathtteu toa' aod tom tosipm her aotht tod toa t tpt thther toamh ther aoateud tor ther ad eo ttpt as e au them hr
ttoom au to tu im todhtpt ton' toum tpteeo atotpthem toath p ms ton' tod ton' amsitm, hem aot t tpd ton , tpt ththem toamh them ao ton' tod thectad eo tpt as a eu toem hdu
ton au' to tp it tod pp ton' eoom apt oo i to pther to tot ec too' aod ton' ecsign' her eo mt tpd ton tp, tpt er to ch them eo ton' tor toec nd ens tpm ad ia ed toemthdu
p ton rn' to cntpt aod pp ton' aonm apiior ienso ther to chteec tond rod ton' aosigns her ao it tnd ton tpi tpt er to ch toer ao ton' tor toec nd ens tnm ad io en toer odn
p,ton trn' to cntpr aod p, ton' tonm aptpersiensh ether to chdeer wond rnd ton' tosipnsiher aosmg tnd ton , tni apther to ch toer ao ton' tor toer nd ensitnm astio on toer odn
g,ton trn' to cnt d andh p, ton' tonm aptpersie sh ethed to chdeec wood rnd ton' tssigndthec aoskt tnd don , tnt tpther to ch ther ao ton' tor thed nd eositnm astt nn themthdn
g,tonm an' to cnt d ansh p, ton't toug aptperste shdethe, to chdeec tond rnd ton't tssignsthec aoskt tnd torkt tnt tpther to ch them ao ton' tor the,end ensktnm astt eon themehdn
g,tonmtan' to cut d assh p, ton't toum aptper teachdethem to chdeer ton' rnd ton't tssign them aoskt tnd torkt tnt tpthem to ch them ao tong tor themend ensitpt askt een themehdp
g,tonmland to cum d assh p, don't aoum aptpersteachdethem to co eere tond rnd don't assign them aoskt tnd tork, tnt tpther to ch them ao tong dor themeod essktnm asktyeeo themtudp
g,tooiland to cum d assh p, don't doum aotperp e thdethem to co lea tood rnd don't dnsign them aoskt tnd dor , tnt tnther to ch them ao long dor themeod eositnm astty eo themtump
g,tonm and to cui d assh p, don't doum autoerp e thaether to co le tood rnd don't dnsign them aoskd tnd dork, dnt tnther to ch them ao tong tor themeod easiipm astty er themtump
...