# NLP 분야 전처리 관련 Framework
1. Spacy
- Website : https://spacy.io/
- Github : https://github.com/explosion/spaCy
2. NLTK
- Website : https://www.nltk.org/
- Github : https://github.com/nltk/nltk
3. TorchText
- Website : https://torchtext.readthedocs.io/en/latest/
- Github : https://github.com/pytorch/text
4. KoNLPy
- Website : https://konlpy.org/en/latest/
- Github : https://github.com/konlpy/konlpy
# Transformer-based Framework
1. Huggingface transformers
- 제공자 : Huggingface.co
- Website : https://huggingface.co/transformers/
- Github : https://github.com/huggingface/transformers
- Backend : PyTorch and Tensorflow
Huggingface의 transformers는 pretrained model 활용을 주로 지원하며, tokenizer 등 전처리 부분도 pretrained model들이 주로 사용하는 Subword tokenizer 기법에 집중되어 있는 특징이 있습니다.
# 시작하기
가장 기본은 프레임워크를 설치하는 것입니다
!pip install transformers
# (간단) 감정 분석 모델 불러와 테스트해보기
# Transformers 라이브러리에서 pipeline 모듈을 불러옵니다.
from transformers import pipeline
# 감정 분석 모델을 로드하고 'classifier' 변수에 할당합니다.
# 'framework' 매개변수를 'tf'로 설정하여 TensorFlow 기반의 모델을 사용합니다.
classifier = pipeline('sentiment-analysis', framework='tf')
# 'classifier'를 사용하여 주어진 텍스트 문장에 대한 감정 분석을 수행합니다.
# 이 문장은 "We are very happy to include pipeline into the transformers repository." 입니다.
# 모델은 이 문장의 감정을 분석하고 그 결과를 반환합니다.
result = classifier('We are very happy to include pipeline into the transformers repository.')
# 결과를 출력합니다.
print(result)
[{'label': 'POSITIVE', 'score': 0.9978194236755371}]
단순히 모델을 불러와 사용하는데, 성능도 괜찮군요!
한국어는 과연 어떨까요? 실험 ㄱㄱ
print(classifier('어제 사고가 나서 병원에 누워있다'))
print(classifier('I am lying in the hospital because of an accident yesterday'))
[{'label': 'POSITIVE', 'score': 0.7579832077026367}]
[{'label': 'NEGATIVE', 'score': 0.9989859461784363}]
'어제 사고가 나서 병원에 누워있다' 라고 말하면 저는 긍정인지, 부정인지 알 수 없다고 봅니다. 평소 일이 많아서 스트레스가 많은 사람이라면 병원에 누워서 쉰다면 긍정이겠지만, 아니라면 별 감정 안생길 거 같아요.
영어의 경우, 눈에 띄게 부정이라고 나오네요? 아마 편향성 문제가 있다고 생각합니다. '사고', '병원' 이라는 키워드가 부정에 큰 영향을 미치는거 같아요. (아닐수도있음😗😗)
중요한건 같은 의미의 문장도 한글로 넣나, 영어로 넣나 결과가 달라질 수 있다는 것이죠! 그래서 한국어 전용 모델을 사람들이 중요하게 생각하는거 같아요~
# Huggingface transformers 설계구조 개요
1. 일반적인 NLP모델을 통해 어떠한 문제를 푸는 과정
1) Task를 정의하고 그에 맞게 dataset을 가공합니다.
2) 적당한 model을 선택하고 이를 생성(?)합니다.
3) model에 데이터로 학습을 시키고, 이를 통해 나온 weight와 설정(config)들을 저장합니다.
5) 저장한 model의 checkpoint는 배포하거나, evaluation을 할 때 사용하고는 합니다.
2. Tranformers 과정
1) Task를 정의
2) dataset을 알맞게 가공하는 Processors
3) 텍스트 데이터를 전처리할 수 있는 Tokenizer
4) 다양한 model을 정의한 Model
5) optimizer와 학습 schedule(warm up 등)을 관리할 수 있는 Optimization
6) 학습 과정을 전반을 관리하는 Trainer
7) weight와 tokenizer, model을 쉽게 불러올 수 있도록 각종 설정을 저장하는 Config
말만 다르지 거의 동일하다고 보면 되요~
# 학습된 모델 불러오기
from transformers import TFBertForPreTraining
model = TFBertForPreTraining.from_pretrained('bert-base-cased')
print(model.__class__)
from transformers import TFAutoModel
model = TFAutoModel.from_pretrained("bert-base-cased")
print(model.__class__)
둘다 Bert 모델을 불러오는 코드죠~
사용법도 거의 동일한데요, 결과적으로 model.__class__를 확인해 보면 약간의 차이가 있음을 알 수 있습니다. 둘 다 동일한 모델 파라미터를 사용하지만 Pretrain, Downstream Task 등 용도에 따라 모델의 Input이나 Output shape가 다를 수 있습니다. AutoModel을 활용한다면 모델의 상세정보를 확인할 필요 없이 Model ID만으로도 손쉽게 모델 구성이 가능하지만, 정확한 용도에 맞게 사용하려면 모델별 상세 안내 페이지를 참고해서 최적의 모델을 선택하는 것이 좋습니다.
아래 링크를 통해 다양한 모델을 찾아볼 수 있어요!
https://huggingface.co/transformers/v4.11.3/pretrained_models.html
Pretrained models
Here is a partial list of some of the available pretrained models together with a short presentation of each model. For the full list, refer to https://huggi...
huggingface.co
# Tokenizer 불러오기
Pretrained model 기반의 NLP 모델을 사용할 때 가장 중요한 두 가지 클래스는 Model과 Tokenizer라고 할 수 있습니다. 그리고 두 가지는 밀접한 관련이 있습니다. 파라미터 구조가 동일한 Model이라 하더라도 Tokenizer가 다르거나 Tokenizer 내의 Dictionary가 달라지면 사실상 완전히 다른 모델이 됩니다. 그리고 Tokenizer는 어떤 언어를 다루느냐 하는 코퍼스 데이터셋에 따라서도 달라집니다. 즉, model과 tokenizer을 서로 다른 ID를 사용하면 망합니다...
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-cased')
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
토크나이저를 불러왔으면 이것저것 실험해봐야죠?
print(tokenizer("This is Test for aiffel"))
{
'input_ids': [101, 1188, 1110, 5960, 1111, 170, 11093, 1883, 102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]
}
tokenizer에 문장을 넣으면 transformer 모델 학습에 필요한 input_ids, token_type_ids, attention_mask가 나옵니다.
text = 'This is Test for LeeTaeHoon 한글도 잘될까?'
print(tokenizer(text))
encode_text = tokenizer.encode(text)
print(encode_text)
decode_text = tokenizer.decode(encode_text)
print(decode_text)
{'input_ids': [101, 1188, 1110, 5960, 1111, 2499, 1942, 5024, 3048, 6931, 100, 100, 136, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
[101, 1188, 1110, 5960, 1111, 2499, 1942, 5024, 3048, 6931, 100, 100, 136, 102]
[CLS] This is Test for LeeTaeHoon [UNK] [UNK]? [SEP]
일부러 한글도 넣어봤습니다.
Encode는 텍스트를 숫자로 변환해주고, Decode는 숫자를 한글로 변환해준다고 생각하시면 되요~
텍스트를 Encode하여 숫자로 변환하고, 숫자를 Decode로 텍스트로 변환했더니!!! 오잉?
처음에는 없던 [CLS], [SEP], [UNK]이 생겼습니다. 트랜스포머 모델에 입력되는 텍스트의 시작과 끝을 알려주기 위해 [CLS] 와 [SEP] 를 사용합니다. [UNK]은 알 수 없는 텍스트를 의미합니다.
text = [
'This is Test for LeeTaeHoon 한글도 잘될까?',
'나 어제 우울해서 파마했어',
'너 T발 C야?'
]
print(tokenizer(text))
{'input_ids': [[101, 1188, 1110, 5960, 1111, 2499, 1942, 5024, 3048, 6931, 100, 100, 136, 102], [101, 100, 100, 100, 100, 102], [101, 100, 100, 100, 136, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]}
위와 같이 Batch 단위로도 토크나이저 사용이 가능합니다.
물론 위 예시는 한글이 많아서 대부분 [UNK]이 뜨겠죠? 그럼 성능이 떨어지게 되지 않을까요?
하지만, 모델에 입력을 넣어줄 때는 모든 문장의 길이가 같아야 합니다. 즉, 패딩을 이용해야 하죠.그래서 옵션을 추가하면 패딩도 가능합니다
text = [
'This is Test for LeeTaeHoon 한글도 잘될까?',
'나 어제 우울해서 파마했어',
'너 T발 C야?'
]
# tokenizer 함수를 사용하여 여러 개의 문장(batch_sentences)을 처리합니다.
# 'padding=True'는 각 문장을 패딩하여 길이를 맞춰주는 옵션입니다. 이는 가장 긴 문장의 길이에 맞게 패딩을 추가해줍니다.
# 'truncation=True'는 문장이 모델의 최대 길이보다 길 경우 자르는 옵션입니다. 텍스트를 모델의 입력 크기에 맞게 자를 수 있습니다.
# 'return_tensors="tf"'는 결과를 TensorFlow 형식의 텐서로 반환하도록 지정합니다.
print(tokenizer(text,padding=True, truncation=True, return_tensors="tf"))
{'input_ids': <tf.Tensor: shape=(3, 14), dtype=int32, numpy=
array([[ 101, 1188, 1110, 5960, 1111, 2499, 1942, 5024, 3048, 6931, 100,
100, 136, 102],
[ 101, 100, 100, 100, 100, 102, 0, 0, 0, 0, 0,
0, 0, 0],
[ 101, 100, 100, 100, 136, 102, 0, 0, 0, 0, 0,
0, 0, 0]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(3, 14), dtype=int32, numpy=
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(3, 14), dtype=int32, numpy=
array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>}