ELMO(1-1) data.py
본 포스트는 long8v님께서 수행한 ELMO 논문 스터디 내용 중, data source(사전 만들기)에 대한 코드를 리뷰한다.전체 소스는 이곳을 통해 확인 할 수 있다.
1. Library Import
from collections import defaultdict, Counter
import numpy as np
import torch
import torch.nn as nn
from .utils import *
2. Vocab
Vocab은 단어사전을 구성하는 class이다.
2.1 init
def __init__(self, min_freq=0):
self.min_freq = min_freq
- 생성자는 최소 단어 수를 지정하는 역할을 한다.
2.2 call
def __call__(
self, sentence_list, init_token="<SOS>", eos_token="<EOS>", is_target=False
):
- 클래스의 실행부분에는 문장, 시작 토큰, 종료 토큰이 지정되어 있다.
self.stoi_dict = defaultdict(lambda: 1)
self.stoi_dict["<UNK>"] = 1
self.stoi_dict["<PAD>"] = 0
if init_token:
self.stoi_dict[init_token] = len(self.stoi_dict) # 2
if eos_token:
self.stoi_dict[eos_token] = len(self.stoi_dict) # 3 if we have init token
self.special_tokens = list(self.stoi_dict)[:]
self.special_tokens_idx = list(self.stoi_dict.values())[:]
- defaultdict는 collections 모듈에 있는 클래스로, 기본값을 가진 딕셔너리를 생성하는 데 사용된다.
- defaultdict의 인자로는 기본값을 반환하는 함수를 전달해야하며, 위 코드에서는 람다함수를 통해 딕셔너리에서 찾을 수 없는 키값이 입력될 시 1을 반환하게 된다.
- 단어 사전에 없는 값은
<UNK>
토큰에 매핑되며, 이에 대한 인덱스는 1이다. - stoi는 string to int를 의미하며, 해당 dictionary는 key 값으로 단어인 string, value 값으로 index인 Int를 가진다.
if type(sentence_list[0]) is list:
all_tokens = [token for sentence in sentence_list for token in sentence]
else:
all_tokens = sentence_list
all_tokens = [token for token in all_tokens if token not in self.stoi_dict]
- all_tokens는 인자로 받은 sentence_list의 인자들이 list로 구성되어 있을 경우 각 list의 모든 토큰들을 가지는 list로 구성된다.
- 이후 all_tokens는 해당 list의 token들 중 이미 stoi에 있는 단어들을 제외한 인자들로 재구성된다. (중복 허용)
self.token_counter = Counter(all_tokens).most_common()
token_counter = [
word for word, count in self.token_counter if count > self.min_freq
]
_index = len(self.stoi_dict) # get number of special dict
for num, word in enumerate(token_counter):
self.stoi_dict[word] = num + _index # start with _index
- 구성된 list는 Counter class를 통해 빈도가 계산되고 token_counter에 저장된다.
- 이후 token_counter는 앞서 생성자에서 설정한 최소 빈도수를 넘는 것들만 인자로 갖도록 재구성 된다.
_index
는 앞서 구성한 special dict의 길이이며 향후stoi_dict
은 해당 인덱스 이후 값을 갖게 된다.
self.itos_dict = {v: k for k, v in self.stoi_dict.items()}
- 마지막으로 생성하는 itos는 stoi의 반대 개념이다.
2.3 build from dict
def build_from_dict(self, dict_obj):
self.stoi_dict = defaultdict(lambda: 1)
self.stoi_dict.update(dict_obj)
self.itos_dict = {v: k for k, v in self.stoi_dict.items()}
- 이미 구성된 dictionary를 통해 단어 사전을 생성하는 함수이다.
3. Field
class Field:
def __init__(
self,
tokenize=lambda e: e.split(),
init_token="<SOS>",
eos_token="<EOS>",
preprocessing=None,
lower=False,
reverse=False,
max_len=999,
min_freq=0,
):
self.tokenize = tokenize
self.init_token = init_token
self.eos_token = eos_token
self.lower = lower
self.reverse = reverse
self.preprocessing = preprocessing
self.vocab = None
self.pad = lambda data, pad_num: nn.ConstantPad2d((0, pad_num), 0)(data)
self.max_len = max_len
self.min_freq = min_freq
def build_vocab(self, data):
self.vocab = Vocab(self.min_freq)
self.vocab(self.preprocess(data))
def build_vocab_from_dict(self, dict_obj):
self.vocab = Vocab()
self.vocab.build_from_dict(dict_obj)
def preprocess(self, data):
if type(data) == str:
pass
else:
return [self.preprocess(d) for d in data]
if self.lower:
data = data.lower()
if self.preprocessing:
try:
data = self.preprocessing(data)
except:
print(data)
tokenized_data = self.tokenize(data)
if self.reverse:
tokenized_data = tokenized_data[::-1]
if self.init_token:
tokenized_data = [self.init_token] + tokenized_data
if self.eos_token:
tokenized_data = tokenized_data + [self.eos_token]
return tokenized_data[: self.max_len]
def process(self, data):
return self.vocab.stoi(data)
def pad_process(self, data, max_len):
d_list = []
for d in data:
process_d = torch.tensor(self.process(d))
pad_d = self.pad(process_d, max_len - len(process_d)).unsqueeze(0)
d_list.append(pad_d)
return torch.cat((d_list), 0)
댓글남기기