2 분 소요

본 포스트는 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)

태그: ,

카테고리:

업데이트:

댓글남기기