일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- CTM
- NLP
- 조축회
- SBERT
- geocoding
- 붕괴 스타레일
- BERTopic
- Tableu
- 데이터리안
- 포아송분포
- Optimizer
- 개체명 인식
- 블루아카이브 토픽모델링
- 트위치
- LDA
- 클래스 분류
- 데벨챌
- 블루 아카이브
- 데이터넥스트레벨챌린지
- 자연어 모델
- 토픽 모델링
- 원신
- KeyBert
- 문맥을 반영한 토픽모델링
- Roberta
- 옵티마이저
- 구글 스토어 리뷰
- 코사인 유사도
- 피파온라인 API
- 다항분포
- Today
- Total
분석하고싶은코코
서브워드 토크나이저(Subword Tokenizer) 본문
서브워드 토크나이저(Subword Tokenizer)
서브워드 토크나이저 기계에게 모든 단어를 학습시킬 수 없기 때문에 생겨난 개념입니다. 바로 앞 글에서 한글에 대한 토크나이저 예시에서 토크나이저를 사용하지 않고 직접 단어를 형태소를 분리하여 단어 사전을 만들었습니다. 그 안에 '법치주의'라는 단어가 없었기 때문에 기계는 그냥 그 단어를 생략하고 정수 인코딩을 진행했습니다. 이를 OOV(Out of Vocabulary) 또는 UNK(Unkown-Token)이라 합니다.
서브워드 분리는 하나의 단어가 분리 시킬 수 있는 더 작은 단어가 있기 때문에 그렇게 분리시켜서 문자 임베딩 작업을 진행하겠다는 것입니다. 예를들어 '소녀시대'라는 단어가 있다고 해봅시다. '소녀'+'시대'가 합쳐진 단어로 '소녀시대'라는 단어를 사용합니다. 이를 그 자체로 문자 임베딩을 하는 것이 아니라 '소녀'와 '시대'로 분리시켜서 문자 임베딩을 진행하면 '소녀시대는 이쁘다' 라는 문장을 기계에게 학습시킬때 서브워드로 분리시키면 '소녀는 이쁘다'라는 문장도 기계가 이해할 수 있게 되는 것입니다. 이 개념이 서브워드 토크나이저의 개념입니다.
BPE(Byte-Pair-Encoding)
BPE 알고리즘은 자연어처리를 위해 등장한 알고리즘은 아닙니다만 자연어처리의 서브워분리 알고리즘에 응용되었습니다. 우선 그렇다면 BPE 알고리즘은 어떤 형태로 진행되는지 간단하게 알아보겠습니다.
aaabdaaabac
# 가장 많이 등장하고 있는 2개의 연속된 문자 조합을 치환
# 치환과정은 동일한 문자, 개수가 있다면 앞선 데이터를 먼저 처리해줍니다.
aa->X
XabdXabac
ab->Y
XYdXYbac
#치환된 문자 동일하게 등장하는게 있을 경우 가장 나중에 다시 치환
XY->Z
ZdZbac
자연어처리에서의 BPE
자연어처리에서 BPE는 최소 단위에서 위로 올라가는 Bottom-up 구조로 진행이 됩니다. 한글 역시 BPE 적용이 가능한데 단위를 자모 단위로 할지 음절단위로 할지 선택하여 진행이 가능합니다.
영어(ENG)
low 5개, lower 2개, newest 6개, widest 3개의 단어가 있는 사전에 BPE알고리즘을 적용하면 다음과 같습니다. 과정은 5번의 과정만 진행을 합니다. 우선 단어를 최소 단위로 모두 분리후 하나씩 조합이 많은 순서대로 결합해가는 과정입니다.
# dictionary
l o w : 5
l o w e r : 2
n e w e s t : 6
w i d e s t : 3
-> vocabulary : {l, o, w, e, r, n, s, t, i, d}
# dictionary update_1 (es)
l o w : 5,
l o w e r : 2,
n e w es t : 6,
w i d es t : 3
-> vocabulary : {l, o, w, e, r, n, s, t, i, d, es}
# dictionary update_2 (es)
l o w : 5,
l o w e r : 2,
n e w est : 6,
w i d est : 3
-> vocabulary : {l, o, w, e, r, n, s, t, i, d, es, est}
# dictionary update_3 (lo)
lo w : 5,
lo w e r : 2,
n e w est : 6,
w i d est : 3
-> vocabulary : {l, o, w, e, r, n, s, t, i, d, es, est, lo}
# dictionary update_4 (low)
low : 5,
low e r : 2,
n e w est : 6,
w i d est : 3
-> vocabulary : {l, o, w, e, r, n, s, t, i, d, es, est, lo, low}
# dictionary update_5 (ne)
low : 5,
low e r : 2,
ne w est : 6,
w i d est : 3
-> vocabulary : {l, o, w, e, r, n, s, t, i, d, es, est, lo, low, ne}
이렇게 학습된 기계는 이제 low의 최상급은 'lowest'라는 단어가 사전 학습에 없었음에도 OOV에 대처가 가능해집니다. 이유는 BPE과정에서 'low', 'est'단어를 학습했기 때문에 'lowest'라는 단어를 서브워드를 조합해서 만들 수 있기 때문입니다.
2) 한글(KOR)
이번에는 한글에 대한 BPE알고리즘을 자모 단위로 3번 적용해보겠습니다.
# 사전
ㄷㅐㅎㅏㄴㅁㅣㄴㄱㅜㄱ : 5
ㄷㅐㅈㅜㅇㄱㅛ_ㅌㅗㅇ : 2
ㅅㅓㅇㅜㄹ : 6
ㅇㅜㄹㅅㅏㄴ : 2
단어목록 : { ㄷ, ㅐ, ㅎ, ㅏ, ㄴ, ㅁ, ㅣ, ㄴ, ㄱ, ㅜ, ㅈ, ㅇ, ㅛ, ㅌ, ㅗ, ㅅ, ㅓ, ㄹ, ㅏ }
# 사전 업데이트_1 (우)
ㄷㅐㅎㅏㄴㅁㅣㄴㄱㅜㄱ : 5
ㄷㅐㅈㅜㅇㄱㅛ_ㅌㅗㅇ : 2
ㅅㅓ우ㄹ : 6
우ㄹㅅㅏㄴ : 2
단어목록 : { ㄷ, ㅐ, ㅎ, ㅏ, ㄴ, ㅁ, ㅣ, ㄴ, ㄱ, ㅜ, ㅈ, ㅇ, ㅛ, ㅌ, ㅗ, ㅅ, ㅓ, ㄹ, ㅏ, 우}
# 사전 업데이트_1 (우)
ㄷㅐㅎㅏㄴㅁㅣㄴㄱㅜㄱ : 5
ㄷㅐㅈㅜㅇㄱㅛㅌㅗㅇ : 2
ㅅㅓ우ㄹ : 6
우ㄹㅅㅏㄴ : 2
단어목록 : { ㄷ, ㅐ, ㅎ, ㅏ, ㄴ, ㅁ, ㅣ, ㄴ, ㄱ, ㅜ, ㅈ, ㅇ, ㅛ, ㅌ, ㅗ, ㅅ, ㅓ, ㄹ, ㅏ, 우}
# 사전 업데이트_2 (울)
ㄷㅐㅎㅏㄴㅁㅣㄴㄱㅜㄱ : 5
ㄷㅐㅈㅜㅇㄱㅛㅌㅗㅇ : 2
ㅅㅓ울 : 6
울ㅅㅏㄴ : 2
단어목록 : { ㄷ, ㅐ, ㅎ, ㅏ, ㄴ, ㅁ, ㅣ, ㄴ, ㄱ, ㅜ, ㅈ, ㅇ, ㅛ, ㅌ, ㅗ, ㅅ, ㅓ, ㄹ, ㅏ, 우, 울}
# 사전 업데이트_3 (대)
대ㅎㅏㄴㅁㅣㄴㄱㅜㄱ : 5
대ㅈㅜㅇㄱㅛㅌㅗㅇ : 2
ㅅㅓ울 : 6
울ㅅㅏㄴ : 2
단어목록 : { ㄷ, ㅐ, ㅎ, ㅏ, ㄴ, ㅁ, ㅣ, ㄴ, ㄱ, ㅜ, ㅈ, ㅇ, ㅛ, ㅌ, ㅗ, ㅅ, ㅓ, ㄹ, ㅏ, 우, 울, 대}
우, 울, 대 라는 3가지 단어를 추가적으로 학습을 통해서 '우울' 울대'와 같이 등장할 수 있는 단어를 기계가 학습할 수 있게 됩니다.
추가적으로 음절과 자모 단위의 한글 BPE와 관련 논문이 있어 링크합니다.
이러한 방법을 통해서 서브워드를 만들어 기계가 자연어를 처리하는데 있어서 최대한 OOV를 만들지 않기 위해서 사용하게 됩니다. 베이직한 BPE는 조합의 빈도수를 기준으로 학습하는 과정이었지만 이 기준을 다르게 적용한 토크나이저들이 존재합니다. 유명한 BERT모델의 토크나이저인 'WordPiece Tokenizer'를 활용했는데 이 토크나이저는 코퍼스의 우도(Likelihood)를 가장 높이는 쌍을 병합합니다. 또 다른 토크나이저로는 Unigram Language Model Tokenizer가 있는데 이 토크나이저는 손실(loss)를 계산하는 방식을 사용합니다. 손실이란 코퍼스의 우도(Likelihood)가 감소하는 정도를 말합니다. 악영향을 주는 토큰의 10~20%를 제거하는 방식으로 지정한 수치에 도달할때까지 반복하여 진행합니다.
이러한 알고리즘을 내부 단어 분리를 위한 유용한 패키지로 구글의 센텐스피스(Sentencepiece)를 사용하여 구현해보겠습니다.
Sentencepiece
논문 : https://arxiv.org/pdf/1808.06226.pdf
센텐스피스 깃허브 : https://github.com/google/sentencepiec
앞서 BPE과정에서 한글은 영어와 다르게 자모와 음절로 분리하는 방법들이 존재했습니다 .이처럼 한글과 영어에 맞는 데이터처리, 알고리즘이 필요한데 이런 부분을 구애받지고 않고 사용이 가능한것에 초점을 맞춘것이 Sentencepiece입니다.
Python에서 Sentencepiece를 불러와 훈련시키는 예시입니다. 초기화시 다음과 같은 설정을 해줄 수 있습니다.
spm.SentencePieceTrainer.Train('--input=[훈련데이터] --model_prefix=[모델이름] --vocab_size=[단어 사이즈] --model_type=[사용모델] --max_sentence_length=[문장 최대길이]')
- input : 학습시킬 파일
- model_prefix : 만들어질 모델 이름
- vocab_size : 단어 집합의 크기
- model_type : 사용할 모델 (unigram(default), bpe, char, word)
- max_sentence_length: 문장의 최대 길이
- pad_id, pad_piece: pad token id, 값
- unk_id, unk_piece: unknown token id, 값
- bos_id, bos_piece: begin of sentence token id, 값
- eos_id, eos_piece: end of sequence token id, 값
- user_defined_symbols: 사용자 정의 토큰
이를 활용해 네이버 영화 리뷰 데이터에 대한 토크나이징을 진행해봅시다!
import sentencepiece as spm
import pandas as pd
import urllib.request
import csv
# 리뷰 데이터 다운로드
urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings.txt", filename="ratings.txt")
# 불러오기
naver_df = pd.read_table('ratings.txt')
# 문장 최대 길이 확인 : 142
max(map(len, naver_df['document']))
# 결측값 행 제거
naver_df = naver_df.dropna(how = 'any')
# 학습을 위해 리뷰 텍스트만 있는 txt파일 생성
with open('naver_review.txt', 'w', encoding='utf8') as f:
f.write('\n'.join(naver_df['document']))
학습
spm.SentencePieceTrainer.Train('--input=naver_review.txt --model_prefix=naver --vocab_size=5000 --model_type=bpe --max_sentence_length=200')
# 학습 모델 불러오기
sp = spm.SentencePieceProcessor()
vocab_file = "naver.model"
sp.load(vocab_file)
idx = 10
print('단어집합의 크기 : ', sp.GetPieceSize())
print('정수 -> 매핑 단어, 10번 인덱스 단어 : ', sp.IdToPiece(idx))
print('단어 -> 정수 매핑 : ', sp.PieceToId('영화') )
print('정수 시퀀스 to 문장 : ', sp.DecodeIds([54, 200, 821, 85]) )
print('서브워드 시퀀스 to 문장 : ', sp.DecodePieces(['▁진짜', '▁최고의', '▁영화입니다', '▁ᄏᄏ']))
print('문장 to 서브워드 시퀀스 : ', sp.encode('진짜 최고의 영화입니다 ㅋㅋ', out_type=str))
print('문장 to 정수 시퀀스 : ', sp.encode('진짜 최고의 영화입니다 ㅋㅋ', out_type=int))
# 결과
단어집합의 크기 : 5000
정수 -> 매핑 단어, 10번 인덱스 단어 : ▁재
단어 -> 정수 매핑 : 4
정수 시퀀스 to 문장 : 어요 영화는 그저 인
서브워드 시퀀스 to 문장 : 진짜 최고의 영화입니다 ᄏᄏ
문장 to 서브워드 시퀀스 : ['▁진짜', '▁최고의', '▁영화입니다', '▁ᄏᄏ']
문장 to 정수 시퀀스 : [50, 166, 790, 100]
토크나이징이 잘 된 모습을 확인할 수 있습니다. 단어 앞에 '_'가 붙어 있는데 이 의미는 토크나이징에서 원 문장으로 복원할때 해당 부분은 띄어쓰기로 대체 됩니다. 그래서 서브워드 시퀀스 to 문장 부분을 보면 _자리가 띄어쓰기로 대체되어 디코딩 된 모습을 확인할 수 있습니다. 이제 훈련된 토크나이저에 임의의 문장을 작성해서 토크나이징을 진행해봅시다!
임의 문장 테스트
lines = [
"오펜하이머 저는 꿀잼이었어요!",
"돈주고 보기 개아까움 킬링타임도 안됨",
]
for line in lines:
print(line)
print(sp.encode_as_pieces(line))
print(sp.encode_as_ids(line))
print()
# 결과
오펜하이머 저는 꿀잼이었어요!
['▁오', '펜', '하', '이', '머', '▁저는', '▁꿀잼', '이', '었어요', '!']
[75, 4255, 3268, 3260, 3531, 1833, 1320, 3260, 544, 3276]
돈주고 보기 개아까움 킬링타임도 안됨
['▁돈주고', '▁보기', '▁개', '아까움', '▁킬링타임', '도', '▁안됨']
[1753, 408, 76, 2784, 903, 3269, 2864]
WordPiece Tokenizer
Bert 모델에 사용된 'BertWordPieceTokenizer'를 사용해서도 토크나이징을 진행해보겠습니다. 데이터는 똑같이 네이버 영화 리뷰를 훈련데이터로 사용하고 단어 집합의 크기를 3만, 훈련 데이터를 초기 분리 수를 6000, 적어도 5번 이상 등장한 쌍에 대해서만 진행하도록 훈련을 진행했습니다.
- files : 단어 집합을 얻기 위해 학습할 데이터
- vocab_size : 단어 집합의 크기
- limit_alphabet : 병합 전의 초기 토큰의 허용 개수
- min_frequency : 최소 해당 횟수만큼 등장한 쌍(pair)의 경우에만 병합
from tokenizers import BertWordPieceTokenizer
tokenizer = BertWordPieceTokenizer(lowercase=False, strip_accents=False)
data_file = 'naver_review.txt'
vocab_size = 30000
limit_alphabet = 6000
min_frequency = 5
tokenizer.train(files=data_file,
vocab_size=vocab_size,
limit_alphabet=limit_alphabet,
min_frequency=min_frequency)
encoded = tokenizer.encode('아 자연어처리 할 수록 어렵다!! ㅠㅠ')
print('토큰화 결과 :',encoded.tokens)
print('정수 인코딩 :',encoded.ids)
print('디코딩 :',tokenizer.decode(encoded.ids))
# 결과
토큰화 결과 : ['아', '자연', '##어', '##처리', '할', '수록', '어렵다', '!', '!', 'ㅠㅠ']
정수 인코딩 : [2111, 6839, 3293, 17319, 3051, 11307, 10515, 5, 5, 5987]
디코딩 : 아 자연어처리 할 수록 어렵다!! ㅠㅠ
앞선 SentencePiece와 다른점은 '_' 대신 '#'이 사용되었는데 자세히보시면 원 문장에서 '자연어처리'로 붙어 있는 상태에서 처음 단어는 #이 없이 '자연'으로 처리되었고 그 뒤로 토큰화 된 단어의 경우 ##을 붙여서 앞에 단어가 있음을 표시는 방식으로 토크나이징이 이뤄졌음을 확인할 수 있습니다.
'머신러닝&딥러닝 > NLP' 카테고리의 다른 글
NLP - Bert이해하기(2)_트랜스포머(transformer) (1) | 2023.09.29 |
---|---|
NLP - Bert이해하기(1)_어텐션 매커니즘(Attention) (0) | 2023.09.29 |
NLP - 토픽 모델링(LSA, LDA) (0) | 2023.09.26 |
개체명 인식(NER)(2)_Bi_LSTM을 통한 개체명 인식 (0) | 2023.09.20 |
개체명 인식(NER)_(1)품사태깅 (0) | 2023.09.20 |