seq2seq를 공부중에 질문드릴 것이 있습니다.

seq2seq를 공부하고 있습니다.
기존의 자료들은 모두가 과거의 pytorch의 Field와 BucketIterator를 쓰고 있어서 다른 방법으로 하고 싶어서 만들어 보고 있습니다.
대다수가 multi30K 같은 pytorch에서 제공하는 데이터셋을 바탕으로 하고 있어서 custum Dataset으로 하고 싶어서 작성하고 있는데 몇가지 문제점이 있어 도움을 구합니다.

어디서부터 보여드려야 제게 도움이 될까 모르겠어서 tokenizer와 Dataset, DataLoader를 보여드려야 할 것 같습니다.

import re
from torchtext.vocab import build_vocab_from_iterator

def yield_token(spacy, language_dataset):
    for sentence in language_dataset:
        new_string = re.sub(r"[^a-zA-Z0-9 ]","",sentence)

        yield [tok.text for tok in spacy.tokenizer(new_string)]

en_vocab = build_vocab_from_iterator(yield_token(spacy_en, dataset['Go.']), specials=['<unk>', '<sos>', '<eos>', '<pad>'])
ger_vocab = build_vocab_from_iterator(yield_token(spacy_ger, dataset['Geh.']), specials=['<unk>', '<sos>', '<eos>', '<pad>'])
from torch.utils.data import Dataset, DataLoader
import re
from torchtext.vocab import build_vocab_from_iterator
from torch.nn.utils.rnn import pad_sequence


class Translation_Dataset(Dataset):

    def __init__(self, data, en_corpus, ger_corpus, spacy_en, spacy_ger):
        super(Translation_Dataset, self).__init__()
        
        self.dataset = data
        self.en_corpus = en_corpus
        self.ger_corpus = ger_corpus
        self.spacy_en = spacy_en
        self.spacy_ger = spacy_ger

        print(self.en_corpus['Hi'])
        
    def __len__(self):
        return len(self.dataset)

    def __getitem__(self, item):
        return self.tokenizer_eng(self.dataset['Go.'][item]), self.tokenizer_ger(self.dataset['Geh.'][item])

    def tokenizer_ger(self, text: str):
        new_string = re.sub(r"[^a-zA-Z0-9 ]","",text)
        return [self.ger_corpus['<sos>']] + [self.ger_corpus[word] for word in [tok.text for tok in self.spacy_ger.tokenizer(new_string)]],\
                [self.ger_corpus[word] for word in [tok.text for tok in self.spacy_ger.tokenizer(new_string)]] + [self.ger_corpus['<eos>']]
        # return new_string

    def tokenizer_eng(self, text: str):
        new_string = re.sub(r"[^a-zA-Z0-9 ]","",text)
        return [self.en_corpus[word] for word in [tok.text for tok in self.spacy_en.tokenizer(new_string)]] + [self.en_corpus['<eos>']]
        # return new_string
def collate_fn(batch_size):
    input_en_list, input_ger_list, ouput_ger_list = [], [], []

    for input_en, (input_ger, output_ger) in batch_size:
        input_en = torch.tensor(input_en)
        input_en_list.append(input_en)

        input_ger = torch.tensor(input_ger)
        input_ger_list.append(input_ger)

        output_ger = torch.tensor(output_ger)
        ouput_ger_list.append(output_ger)
    
    input_en_tensors = pad_sequence(input_en_list, batch_first=True, padding_value=en_vocab['<pad>'])
    input_ger_tensors = pad_sequence(input_ger_list, batch_first=True, padding_value=ger_vocab['<pad>'])
    output_ger_tensors = pad_sequence(ouput_ger_list, batch_first=True, padding_value=ger_vocab['<pad>'])
    
    return input_en_tensors, input_ger_tensors, output_ger_tensors
from sklearn.model_selection import train_test_split

train_set, val_set = train_test_split(dataset, test_size=0.4, random_state=5555, shuffle=True)
val_set, test_set = train_test_split(val_set, test_size=0.5, random_state=5555, shuffle=True)

train_dataset = Translation_Dataset(train_set, en_vocab, ger_vocab, spacy_en, spacy_ger)
val_dataset = Translation_Dataset(val_set, en_vocab, ger_vocab, spacy_en, spacy_ger)
test_dataset = Translation_Dataset(test_set, en_vocab, ger_vocab, spacy_en, spacy_ger)

train_dataLoader = DataLoader(train_dataset, batch_size=2, shuffle=True, collate_fn=collate_fn)
val_dataLoader = DataLoader(val_dataset, batch_size=2, shuffle=True, collate_fn=collate_fn)
test_dataLoader = DataLoader(test_dataset, batch_size=2, shuffle=True, collate_fn=collate_fn)
sample_set = Translation_Dataset(dataset, en_vocab, ger_vocab, spacy_en, spacy_ger)
sampleLoader = DataLoader(sample_set,  batch_size=2, shuffle=True, collate_fn=collate_fn)
 class Encoder(nn.Module):

    def __init__(self, input_size, embedding_size, hidden_size, num_layers, p):

        super(Encoder, self).__init__()

        self.hidden_size = hidden_size
        self.num_layers = num_layers

        self.embedding = nn.Embedding(input_size, embedding_size)
        self.rnn = nn.LSTM(embedding_size, hidden_size, num_layers, dropout=p)

    def forward(self, x):
        print("in Encoder through parameter x's size:\t", x.size())
        embedding = self.embedding(x)
        outputs, (hidden, cell) = self.rnn(embedding)

        print("in Encoder output's size:\t", outputs.shape)
        print("in Encoder hidden's size:\t", hidden.shape)
        print("in Encoder cell's size:\t", cell.shape, end='\n\n')
        
        return hidden, cell
class Decoder(nn.Module):
    def __init__(self, input_size, embedding_size, hidden_size, output_size, num_layers, p):
        super(Decoder, self).__init__()

        self.hidden_size = hidden_size
        self.num_layers = num_layers
        
        self.dropout = nn.Dropout(p)
        self.embedding = nn.Embedding(input_size, embedding_size)
        self.rnn = nn.LSTM(embedding_size, hidden_size, num_layers)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x, hidden, cell):

        print('size of x: ', x.size())
        embedding = self.embedding(x)
        print('size of embedding: ', embedding.size())
        print('size of hidden:\t', hidden.size())
        print('size of cell:\t', cell.size())

        outputs, (hidden, cell) = self.rnn(embedding, (hidden, cell))
        raise ValueError('sorry')
        return x

여기서 부터 제 문제점이 나옵니다.

encoder_model = Encoder(input_size=len(en_vocab), embedding_size=256, hidden_size=256, num_layers=5, p=0.1)
decoder_model = Decoder(input_size=len(ger_vocab), embedding_size=256, hidden_size=256, output_size=len(ger_vocab) ,num_layers=5, p=0.1)

for en_input, ger_input, ger_output in sampleLoader:
    en_input = en_input.long().to(device)
    ger_input = ger_input.long().to(device)
    ger_output = ger_output.long().to(device)

    hidden, cell = Encoder(en_input)
    output = Decoder(ger_input, hidden, cell)

제가 고민하는 부분은 Encoder에서 반환된 hidden, cell이 이제 Decoder으로 들어가서 LSTM층을 통과할 때 문제가 생깁니다.

Decoder는 Encoder에서 나온 hidden, cell을 받아서 LSTM층에 넣어서 연산을 해야 하는데 tensor 가 항상 맞지 않습니다. 초보자인 제 생각은 함수 collate_fn에서 문제가 있는 것이 아닌가 생각하고 있습니다.
어떻게 하면 tensor 문제를 해결할 수 있는지 모르겠습니다.

좋아요 2

안녕하세요
혹시 화면에 출력된 메시지, 오류 메시지를 올려 주시면 더 쉽게 파악이 가능할 것 같은데요 가능하신가요?
코드를 자세히 보니까 좀 의심되는 부분이 있는데요.
output = Decoder(ger_input, hidden, cell)
위 부분에서 ger_input 에서 한 토큰씩만 입력으로 for 문으로 돌려야하는 것 같은데요 확인이 필요할 것 같습니다.

좋아요 2

지적해 주신 부분이 맞습니다.
ger_input에서 한 토큰씩 입력으로 for 문으로 돌려야 합니다.
원래는 그렇게 하는데 제 버릇이 tensor에 잘 맞게 들어가는지 눈으로 확인하는 것이라
한번에 기존의 코드를 배껴오지 않고 저렇게 작성하였습니다.

in Encoder through parameter x's size:	 torch.Size([2, 6])
in Encoder output's size:	 torch.Size([2, 6, 256])
in Encoder hidden's size:	 torch.Size([5, 6, 256])
in Encoder cell's size:	 torch.Size([5, 6, 256])

size of x:  torch.Size([2, 10])
size of embedding:  torch.Size([2, 10, 256])
size of hidden:	 torch.Size([5, 6, 256])
size of cell:	 torch.Size([5, 6, 256])
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-19-32982a9ce820> in <module>()
      8 
      9     hidden, cell = encoder_model(en_input)
---> 10     output = decoder_model(ger_input, hidden, cell)
     11 

5 frames
/usr/local/lib/python3.7/dist-packages/torch/nn/modules/rnn.py in check_hidden_size(self, hx, expected_hidden_size, msg)
    224                           msg: str = 'Expected hidden size {}, got {}') -> None:
    225         if hx.size() != expected_hidden_size:
--> 226             raise RuntimeError(msg.format(expected_hidden_size, list(hx.size())))
    227 
    228     def check_forward_args(self, input: Tensor, hidden: Tensor, batch_sizes: Optional[Tensor]):

RuntimeError: Expected hidden[0] size (5, 10, 256), got [5, 6, 256]

에러코드는 이렇게 나옵니다.

오류 메시지를 보니 batch_first 설정 문제 인것으로 보입니다.
입력 x를 만드실때는 [batch_size, seq_len]으로 하신것 같은데요
LSTM설정은 batch_first가 아니라서 문제가 발생것으로 보입니다.
LSTM의 "batch_first = True"를 설정하시는게 먼저인 것 같습니다.
아래와 같이 출력되야 합니다
in Encoder hidden’s size: torch.Size([5, 2, 256])
in Encoder cell’s size: torch.Size([5, 2, 256])

좋아요 1

감사합니다.
많은 공부가 되었습니다.
pytorch의 lstm 페이지에서 보니
batch_first가 False일 때 (seq, batch, feature) 이었던 것이
batch_first가 True일 때 (batch, seq, feature)으로 바뀐다고 하는데,

여기서 왜 batch_first 가 True이어야 하는지는 잘 모르겠습니다.
설명해 주실 수 있나요?

LSTM의 출력에서 hidden,cell 은 입력 Sequence 가 모두 들어간 이후에 각 레이어의 마지막 상태입니다.
마지막 상태만 전달하기 때문에 입력 Sequence의 길이에는 영향을 받지 않습니다
그러므로 [Layer 숫자 x 입력 Sequence 갯수 x feature] 으로 출력됩니다.
여기서 입력 Sequence 갯수는 batch_size 입니다.

그리고 nn.LSTM은 batch_first가 False인 경우에 입력을 [len_seq, batch_size, feature]로 가정합니다 .
그런데 지금 만드신 입력이 [batch_size, seq_len, feature] 이기 때문에
오해를 하게되서 출력이 [num_layer x seq_len x feature] 으로 나오는 오류가 생긴 상태입니다.

좋아요 2

감사합니다.
조언을 듣고 곰곰히 생각을 하고 보니 이제야 보입니다.

그렇지만 좀 다른 방법으로 하였는데 함수 collate_fn에서 pad_sequence의 batch_first를 없앴습니다.
그러니까 해결되었습니다.

좋아요 1

그리고 하나 더 질문드립니다.
pytorch의 Dataset 에 관한 질문입니다. 위의 Translation_Dataset입니다.

제가 데이터셋을 다운받아서 pandas의 dataframe으로 열어서 그냥 위의 Translation_Dataset으로 넣으면 잘 돌아갑니다.

그러나 sklearn의 train_test_split을 하여 Translation_Dataset으로 넣고, 이것을 DataLoader으로 넣어 for loop으로 돌리면 다음과 같은 문제가 항상 납니다.

KeyError                                  Traceback (most recent call last)
/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   3360             try:
-> 3361                 return self._engine.get_loc(casted_key)
   3362             except KeyError as err:

11 frames
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()

pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()

KeyError: 125675

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   3361                 return self._engine.get_loc(casted_key)
   3362             except KeyError as err:
-> 3363                 raise KeyError(key) from err
   3364 
   3365         if is_scalar(key) and isna(key) and not self.hasnans:

KeyError: 125675

이 이유를 잘 모르겠습니다.