[nlp] subword 분절
단어에서 subword 란 말 그대로 더 작은 단위의 의미들이다. 예를 들어 "집중" 이라는 한글 단어는 모은다는 뜻이 있는 "집" 그리고 가운데를 뜻하는 "중" 으로 이루어져 있다. 영어의 경우도 Concentrate 는 con/centr/ate 의 세 가지 뜻으로 구성된 단어들이 대부분이다.
따라서 이런 작은 의미 단위로 분절할 수 있다면 학습이 더 유리해지나, 이를 위해서는 subword 를 나눠둔 사전이 필요하다. 오늘은 이 subword 사전을 만들어내기 위한 여러가지 방법들을 알아보고, 간단한 실습을 해보도록 하겠다.
1. Byte Pair Encoding 알고리즘
압축 알고리즘? 이라고 불리는 이 알고리즘은 subword 사전을 생성해내는 알고리즘이다. 학습 코퍼스를 활용해 모델을 학습시킨 후 학습/테스트 코퍼스에 알고리즘을 적용하는 방법이란다. 이 방법을 쓰면 희소성을 효과적으로 낮출 수 있고, Oov도 없앨 수 있다 (예를 들어 아랍 문자는 한국어 학습을 한 모델의 경우 당연히 인식하지 못한다..)
2. BPE Training
그럼 모델 작동 원리에 대해 간단히 알아보겠다. 우선 모델이 작동하는 순서는 다음과 같다
- Training
1. 단어 사전 생성
2. 단어 분절 후 pair 별 빈도 카운트
3. 최빈 pair 를 골라 merge
4. pair 빈도 카운트 업데이트
5. 반복
ex) Apple 의 pair 는
(A,p)
(p,p)
(p,l)
(l,e)
.
.
.
등등
이 방식으로 best pair 를 업데이트해줌
이후에 merge 를 실행하고 나면 최종적으로는 단어들이 어떻게 분절되는지 알 수 있게 됨
3. 유의사항
- 이 알고리즘은 예제에서 알아본 것 처럼 단어 세트의 빈도를 가지고 subword 사전을 만들어주는 거기 때문에 학습 set 과 test set 에서 쓰이는 단어들의 도메인이 어느정도 일치해야 한다는 한계를 가진다.
-입력 데이터가 Oov 에 발생할 경우 UNK 토큰으로 치환해 모델에 입력하자
-그러나 이전 단어들을 기반으로 다음 단어를 예측하는 task 에서는 치명적으로 작동할 수 있다
- 대신 모르는 단어지만 subword 분절을 통해 의미를 유추해 볼 수 있다 (약간.. 신조어같은거.. 버카충? 버스카드충전의 줄임말이라고 함... 알고 계셨나요?ㅎ 저의 경우, ~충 으로 끝나면 보통 비하하는 뜻의 어떤 사람을 뜻하던데.. 이렇게 유추했는데 이 알고리즘을 쓰면 특이하게 쪼개지는 걸 보고 전자로 잘 예측할 수 있었을지도?)
- 그러나! 한국어의 경우에는 띄어쓰기가 제멋대로인 경우가 많기 때문에 바로 subword segmentation 을 적용하는건 위험하다. 따라서 형태소 분석기를 통한 tokenization 을 진행한 후, subword segmentation을 적용하자.
4. 모듈
파이썬에서 현재 subword 를 구현가능한 모델 요렇게 두가지가 있는데 두번째거가 구글에서 만든 최신형(?) 모듈이란다
https://github.com/rsennrich/subword-nmt
GitHub - rsennrich/subword-nmt: Unsupervised Word Segmentation for Neural Machine Translation and Text Generation
Unsupervised Word Segmentation for Neural Machine Translation and Text Generation - GitHub - rsennrich/subword-nmt: Unsupervised Word Segmentation for Neural Machine Translation and Text Generation
github.com
https://github.com/google/sentencepiece
GitHub - google/sentencepiece: Unsupervised text tokenizer for Neural Network-based text generation.
Unsupervised text tokenizer for Neural Network-based text generation. - GitHub - google/sentencepiece: Unsupervised text tokenizer for Neural Network-based text generation.
github.com
5. 실습
이 코드를 py 파일로 저장해서 파일에 적용시키면 된다
import sys
STR = '▁'
if __name__ == "__main__":
ref_fn = sys.argv[1]
f = open(ref_fn, 'r')
for ref in f:
ref_tokens = ref.strip().split(' ')
input_line = sys.stdin.readline().strip()
if input_line != "":
tokens = input_line.split(' ')
idx = 0
buf = []
# We assume that stdin has more tokens than reference input.
for ref_token in ref_tokens:
tmp_buf = []
while idx < len(tokens):
if tokens[idx].strip() == '':
idx += 1
continue
tmp_buf += [tokens[idx]]
idx += 1
if ''.join(tmp_buf) == ref_token:
break
if len(tmp_buf) > 0:
buf += [STR + tmp_buf[0].strip()] + tmp_buf[1:]
sys.stdout.write(' '.join(buf) + '\n')
else:
sys.stdout.write('\n')
f.close()