Patrick's 데이터 세상

Transformer Encoder 카테고리 분류 개발 후기 - 1. Tokenizer 본문

Deep Learning/프로젝트 후기

Transformer Encoder 카테고리 분류 개발 후기 - 1. Tokenizer

patrick610 2022. 2. 27. 17:42
반응형
SMALL

 



그동안 회사에서 작업했던 30개 카테고리 class 분류 모델 개발 과정 및 삽질 과정을 기록하려고 한다.
전체 프로세스에 대한 공유 목적이자 다시 공부하려는 목적이기도 하다.

https://github.com/hipster4020/category_classification

 

GitHub - hipster4020/category_classification: 뉴스 카테고리 분류

뉴스 카테고리 분류. Contribute to hipster4020/category_classification development by creating an account on GitHub.

github.com


 

👉🏻 작업 환경

GPU type NVIDIA GeForce RTX 3090
CPU count 48
GPU count 4
CUDA Version 11.4
Python Version 3.6.9
OS Docker tensorflow - Ubuntu

 

 

👉🏻 Issue

해당 분류 모델의 목적은 스크랩된 뉴스 본문 기사를 30개 클래스 카테고리로 분류하는 것이었다.
클래스는 각 기사당 최대 3개까지 예측하기 위해 30개 클래스에 대해 모두 각각의 0~1까지 범위의 확률을 낼 수 있도록 각각의 class에 sigmoid를 적용하여 예측값이 0.8 이상인 값 중 3개를 추출하고 1개도 없으면 etc로 분류하였다.
따라서, 각 기사 당 predict label은 1~3개가 된다.

모델은 Transformer의 self-attn과 feedforward network로 구성된 EncoderLayer모델을 활용하였다.
Tokenizer는 뉴스 기사 데이터를 Huggingface Tokenizer의 BPE 모델로 별도로 학습한 모델을 사용한다.

 

👉🏻 Tokenizer

https://github.com/hipster4020/category_classification/blob/master/src/processing/tokenizer_train.ipynb

 

GitHub - hipster4020/category_classification: 뉴스 카테고리 분류

뉴스 카테고리 분류. Contribute to hipster4020/category_classification development by creating an account on GitHub.

github.com

Tokenizer 작업은 jupyter notebook으로 진행하였다.

 

• load data

import json
import re
from pprint import pprint
def load_jsonl(input_path) -> list:
    data = [] 
    with open(input_path, "r", encoding="utf-8") as f:
    	for line in f:
        	data.append(json.loads(line.rstrip("\n|\r")))
    return data
data = load_jsonl('../../data/category.json')

json 타입으로 구성된 뉴스 본문 데이터를 불러와 list 타입으로 담는 작업

 

vocab_size = 24000

user_defined_symbols = ["<pad>", "<unk>", "<cls>", "<sep>", "<mask>", "<bos>", "<eos>", "<tsep>", "<name>", "<url>"]
user_defined_symbols += ["<unk0>", "<unk1>", "<unk2>", "<unk3>", "<unk4>", "<unk5>", "<unk6>", "<unk7>", "<unk8>", "<unk9>"]
unused_token_num = 100
unused_list = [f"<unused{i}>" for i in range(unused_token_num)]
user_defined_symbols += unused_list

pprint(user_defined_symbols)

vocabulary size를 24000으로 설정하고, 사용자 정의 토큰에 <pad>, <unk>, <cls>, <sep>, <mask>, <bos>, <eos>, <tsep>, <name>, <url>
<unk0>, <unk1>, <unk2>, <unk3>, <unk4>, <unk5>, <unk6>, <unk7>, <unk8>, <unk9>를 지정.
추가로 <unused> 토큰을 0~99까지 100개 사용자 정의 토큰에 추가하였다.

 

• train

from tokenizers import Tokenizer
from tokenizers.models import BPE

tokenizer = Tokenizer(BPE(unk_token="<unk>"))

 

pretrained 된 tokenizer 말고 직접 train 해서 쓸 것이기 때문에 import 된 Huggingface의 Tokenizer, models를 활용하여 BPE 학습을 진행한다.

from tokenizers import normalizers
from tokenizers.normalizers import NFKC, BertNormalizer

n1 = NFKC()
n2 = BertNormalizer(
        clean_text=False,
        handle_chinese_chars=False,
        strip_accents=False,
        lowercase=False,
     )
tokenizer.normalizer = normalizers.Sequence([n1, n2])

 

NFKC : NFKC 유니코드로 정규화 진행
BertNormalizer : Bert 모델 Normalizer 사용

from tokenizers import pre_tokenizers
from tokenizers.pre_tokenizers import Metaspace

tokenizer.pre_tokenizer = pre_tokenizers.Sequence([Metaspace(replacement="_", add_prefix_space=True
                                                            )
                                                    ]
                                                  )

pre_tokenizers.Sequence : 공백을 분할하고 특수 문자 "_"로 대체하는 Metaspace로 사전 토큰화

def gen():
	for row in data:
    	yield processing(row['content'])
from tokenizers.trainers import BpeTrainer

trainer = BpeTrainer(vocab_size=vocab_size,
             		special_tokens=user_defined_symbols,
                     )
tokenizer.train_from_iterator(gen(), trainer)
tokenizer.model.save("temp")


trainers.BpeTrainer : BpeTrainer로 config 값 세팅(vocab_size : 24000 , user_defined_symbols : 위에 정의한 사용자 정의 토큰)
tokenizer.train_from_iterator : list 뉴스데이터와 trainer로 train 진행
tokenizer.model.save("temp") : 모델 저장

output = tokenizer.encode("본 고안은 이러한 특성을 이용해 사용한다.")
print(output.ids)

tokenizer.decode(output.ids)
from tokenizers import decoders

tokenizer.decoder = decoders.BPEDecoder(suffix='_')
tokenizer.decode(output.ids)

 

trained tokenizer에 test encoding 후 decoding 진행하면 정상적으로 결과 출력된다.

 

• convert transformers tokenizer and save

from transformers import GPT2TokenizerFast

fast_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
fast_tokenizer.pad_token = "<pad>"
fast_tokenizer.unk_token = "<unk>"
fast_tokenizer.cls_token = "<cls>"
fast_tokenizer.sep_token = "<sep>"
fast_tokenizer.mask_token = "<mask>"
fast_tokenizer.bos_token = "<bos>"
fast_tokenizer.eos_token = "<eos>"
special_tokens_dict = {'additional_special_tokens': user_defined_symbols}
fast_tokenizer.add_special_tokens(special_tokens_dict)

새로 정의한 GPT2TokenizerFast에 padding, unknown, cls, sep, mask, bos, eos 토큰을 추가하고, 스페셜 토큰 사전사용자 정의 토큰을 추가한다.

fast_tokenizer.decode(fast_tokenizer.encode("본 고안은 이러한 특성을 이용해 사용한다."))
fast_tokenizer.save_pretrained("tokenizer")

테스트 문장 출력 확인 후 tokenizer save.

 

👉🏻 정리

huggingface BPE 학습한 모델을 GPT2TokenizerFast로 load하여 Transformer Encoder 모델에 사용할 준비를 마쳤다.
2부에서 Transformer 모델, Finetuning에 대해 다루려고 한다


참고
huggingface Tokenizers

 

Components — tokenizers  documentation

When building a Tokenizer, you can attach various types of components to this Tokenizer in order to customize its behavior. This page lists most provided components. PostProcessor After the whole pipeline, we sometimes want to insert some special tokens be

huggingface.co



반응형
LIST
Comments