새소식

부스트캠프 AI Tech 4기

[NLP_KLUE] 3. 한국어 Tokenizing

  • -

Tokenizing의 목적

  1. 의미를 지닌 단위로 자연어를 분절
  2. Model의 학습 시, 동일한 size로 입력
    ⇒ tokenizer는 특정 사이즈로 token의 개수를 조절하는 함수가 필수로 포함되어야 한다.

 

어절 단위 tokenizing

모든 문장을 띄어쓰기 단위로 분리하는 것

max_seq_length = 10
text = "오늘은 KLUE 대회 첫 날이다."

tokenized_text = text.split(" ")

# padding
tokenized_text += ["padding"] * (max_seq_length - len(tokenized_text))
# truncation
tokenized_text = tokenized_text[:max_seq_length]

print(tokenized_text)
# ['오늘은', 'KLUE', '대회', '첫', '날이다.', 'padding', 'padding', 'padding', 'padding', 'padding']

 

class Tokenizer:
    def __init__(self):
        self.tokenizer_type_list = ["word"]
        self.pad_token = "<pad>"
        self.max_seq_length = 10
        self.padding = False

    def tokenize(self, text, tokenizer_type): 
        assert tokenizer_type in self.tokenizer_type_list, "정의되지 않은 tokenizer_type입니다."
        if tokenizer_type == "word":
            tokenized_text = text.split(" ")
        if self.padding:
            tokenized_text += [self.pad_token] * (self.max_seq_length - len(tokenized_text))
            return tokenized_text[:self.max_seq_length]
        else:
            return tokenized_text[:self.max_seq_length]
            
    def batch_tokenize(self, texts, tokenizer_type):
        for i, text in enumerate(texts):
            texts[i] = self.tokenize(text, tokenizer_type)
        return texts
word_tokenizer = Tokenizer()
word_tokenizer.pad_token = "[PAD]"
word_tokenizer.max_seq_length = 10
word_tokenizer.padding = True

print(word_tokenizer.tokenize("페이커가 숙제 방송을 진행중입니다.", "word"))
# ['페이커가', '숙제', '방송을', '진행중입니다.', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']

 

 

형태소 단위 tokenizing

from konlpy.tag import Mecab

mecab = Mecab()
print(mecab.pos("아버지가방에들어가신다."))

#[('아버지', 'NNG'), ('가', 'JKS'), ('방', 'NNG'), ('에', 'JKB'), ('들어가', 'VV'), ('신다', 'EP+EF'), ('.', 'SF')]

 

class Tokenizer:
    def __init__(self):
        self.tokenizer_type_list = ["word", "morph"]
        self.pad_token = "<pad>"
        self.max_seq_length = 10
        self.padding = False
    
    def tokenize(self, text, tokenizer_type):
        assert tokenizer_type in self.tokenizer_type_list, "정의되지 않은 tokenizer type 입니다."
        if tokenizer_type == "word":
            tokenized_text = text.split(" ")
        elif tokenizer_type == "morph":
            tokenized_text = [lemma[0] for lemma in mecab.pos(text)]
        if self.padding:
            tokenized_text += [self.pad_token] * (self.max_seq_length - len(tokenized_text))
            return tokenized_text[:self.max_seq_length]
        else:
            return tokenized_text[:self.max_seq_length]
    
    def batch_tokenize(self, texts, tokenizer_type):
        for i, text in enumerate(texts):
       	    texts[i] = self.tokenize(text, tokenizer_type)
        return texts
print(my_tokenizer.tokenize("점심으로 계란 볶음밥이랑 라면 먹었음", "morph"))
# ['점심', '으로', '계란', '볶음밥', '이랑', '라면', '먹', '었', '음', '[PAD]']

 

 

음절 단위 tokenizing

음절 단위 tokenizing은 한 자연어를 한 글자씩 분리

text = "우리집 고양이들이 요새 계속 밥달라고 보채고 있다."
tokenized_text = list(text)    # split 함수는 입력 string에 대해서 특정 string을 기반으로 분리해줍니다.
print(tokenized_text)  

# ['우', '리', '집', ' ', '고', '양', '이', '들', '이', ' ', '요', '새', ' ', '계', '속', ' ', '밥', '달', '라', '고', ' ', '보', '채', '고', ' ', '있', '다', '.']

 

class Tokenizer:
    def __init__(self):
        self.tokenizer_type_list = ["word", "morph", "syllable"]
        self.pad_token = "<pad>"
        self.max_seq_length = 10
        self.padding = False

    def tokenize(self, text, tokenizer_type): 
        assert tokenizer_type in self.tokenizer_type_list, "정의되지 않은 tokenizer_type입니다."
        if tokenizer_type == "word":
            tokenized_text = text.split(" ")
        elif tokenizer_type == "morph":
            tokenized_text = [lemma[0] for lemma in mecab.pos(text)]
        elif tokenizer_type == "syllable":
            tokenized_text = list(text)
        if self.padding:
            tokenized_text += [self.pad_token] * (self.max_seq_length - len(tokenized_text))
            return tokenized_text[:self.max_seq_length]
        else:
            return tokenized_text[:self.max_seq_length]
            
    def batch_tokenize(self, texts, tokenizer_type):
        for i, text in enumerate(texts):
            texts[i] = self.tokenize(text, tokenizer_type)
        return texts

 

 

자소 단위 tokenizing

한글은 하나의 문자도 최대 초성, 중성, 종성, 총 3개의 자소로 분리가 가능하다.
자소 분리를 위해 hgtk 라이브러리를 사용

 

import hgtk

text = "자소 분리"
tokenized_text = list(hgtk.text.decompose(text))
print(tokenized_text)

# ['ㅈ', 'ㅏ', 'ᴥ', 'ㅅ', 'ㅗ', 'ᴥ', ' ', 'ㅂ', 'ㅜ', 'ㄴ', 'ᴥ', 'ㄹ', 'ㅣ', 'ᴥ']

 

class Tokenizer:
    def __init__(self):
        self.tokenizer_type_list = ["word", "morph", "syllable", "jaso"]
        self.pad_token = "<pad>"
        self.max_seq_length = 10
        self.padding = False

    def tokenize(self, text, tokenizer_type): 
        assert tokenizer_type in self.tokenizer_type_list, "정의되지 않은 tokenizer_type입니다."
        if tokenizer_type == "word":
            tokenized_text = text.split(" ")
        elif tokenizer_type == "morph":
            tokenized_text = [lemma[0] for lemma in mecab.pos(text)]
        elif tokenizer_type == "syllable":
            tokenized_text = list(text)
        elif tokenizer_type == "jaso":
            tokenized_text = list(hgtk.text.decompose(text))
        if self.padding:
            tokenized_text += [self.pad_token] * (self.max_seq_length - len(tokenized_text))
            return tokenized_text[:self.max_seq_length]
        else:
            return tokenized_text[:self.max_seq_length]
            
    def batch_tokenize(self, texts, tokenizer_type):
        for i, text in enumerate(texts):
            texts[i] = self.tokenize(text, tokenizer_type)
        return texts

 

 

WordPiece tokenizing

from tokenizers import BertWordPieceTokenizer

# Initialize an empty tokenizer
wp_tokenizer = BertWordPieceTokenizer(
    clean_text=True,    # [이순신, ##은, ' ', 조선]에서 ' '(띄어쓰기)를 지우고자 할 때 True / Bert는 clean_text=True
    handle_chinese_chars=True,
    strip_accents=False,    # True: [YepHamza] -> [Yep, Hamza] : 대문자 기준 분리
    lowercase=False,
)

# train
wp_tokenizer.train(
    files="my_data/wiki_20190620_small.txt", # corpus file
    vocab_size=10000,
    min_frequency=2,
    show_progress=True,
    special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"],
    limit_alphabet=1000,
    wordpieces_prefix="##"
)

# Save the files
wp_tokenizer.save_model("wordPieceTokenizer", "my_tokenizer") # 디렉토리, vocab 저장 이름
# ['wordPieceTokenizer/my_tokenizer-vocab.txt']

 

print(wp_tokenizer.get_vocab_size())
# 10000

 

text = "이순신은 조선 중기의 무신이다."
tokenized_text = wp_tokenizer.encode(text)

print(tokenized_text)
# Encoding(num_tokens=10, attributes=[ids, type_ids, tokens, offsets, attention_mask, special_tokens_mask, overflowing])

print(tokenized_text.tokens)
# ['이', '##순', '##신은', '조선', '중', '##기의', '무', '##신이', '##다', '.']

print(tokenized_text.ids)
# [706, 1454, 7607, 2002, 755, 2606, 452, 8496, 1017, 16]

 

class Tokenizer:
    def __init__(self):
        self.tokenizer_type_list = ["word", "morph", "syllable", "jaso", "wordPiece"]
        self.pad_token = "<pad>"
        self.max_seq_length = 10
        self.padding = False

    def tokenize(self, text, tokenizer_type): 
        assert tokenizer_type in self.tokenizer_type_list, "정의되지 않은 tokenizer_type입니다."
        if tokenizer_type == "word":
            tokenized_text = text.split(" ")
        elif tokenizer_type == "morph":
            tokenized_text = [lemma[0] for lemma in mecab.pos(text)]
        elif tokenizer_type == "syllable":
            tokenized_text = list(text)
        elif tokenizer_type == "jaso":
            tokenized_text = list(hgtk.text.decompose(text))
        elif tokenizer_type == "wordPiece":
            tokenized_text = wp_tokenizer.encode(text).tokens
        if self.padding:
            tokenized_text += [self.pad_token] * (self.max_seq_length - len(tokenized_text))
            return tokenized_text[:self.max_seq_length]
        else:
            return tokenized_text[:self.max_seq_length]
            
    def batch_tokenize(self, texts, tokenizer_type):
        for i, text in enumerate(texts):
            texts[i] = self.tokenize(text, tokenizer_type)
        return texts

 


부스트캠프 AI Tech 교육 자료를 참고하였습니다.

728x90
Contents