
BPE |
는 Byte Pair Encoding의 약자로, Sentencepiece와 같은 Tokenizing 방법의 base이다. |
| 문장 혹은 단어 안에 있는 글자들을 적절한 단위로 나누는 subword tokenizer의 하나로, token들의 빈도를 기반으로 높은 빈도의 토큰들을 merge해가며 최종 token들을 만들어내는 방법. https://wikidocs.net/22592 참고해서 보면 좋을 듯 |
|
| SentencePiece ? |
| SentencePiece는 간단하게 말하면 기존에 존재하던 unigram, BPE와 같은 tokenizer들을 모든 언어에 대해 적용이 가능하도록 약간의 추가적인 기능들을 더해서 구현한 것이다. 특별히 새로운 알고리즘을 제시했다기보다는, 기존의 tokenizer들을 좀 더 사용하기 편하고 성능이 좋게 개선했다고 생각하면 되겠다. SentencePiece는 다음과 같은 특성들을 가진다. 1. Pre-tokenization을 필요로 하지 않는다. 기존의 tokenizer들은 영어를 기준으로 학습되었다면 영어가 띄어쓰기를 기반으로 해서 단어들이 대부분 분리되기 때문에, 띄어쓰기가 없는 일본어나 중국어와 같은 언어들에는 적용될 수 없다는 한계점을 가지고 있었다. 하지만 SentencePiece는 띄어쓰기를 다른 알파벳 혹은 글자처럼 하나의 character로 취급한다. 그렇기 때문에 모든 종류의 언어에 대해 적용이 가능하고, pre-tokenization이 필요하지 않다는 장점을 가진다. 2. Character-coverage 설정이 가능하다. 영어의 경우에는 알파벳의 개수가 30개 내외로 적기 때문에 괜찮지만, 중국어나 일본어와 같은 문자들의 경우에는 굉장히 많은 종류의 한자를 포함하고 있고, 이에 따라 엄청나게 많은 개수의 character들을 다 다루어야 해서 tokenizer를 학습하는 데에 시간이 오래 걸린다는 한계점을 가진다. 하지만 SentencePiece는 모든 character를 다 고려하는 것이 아닌, 빈도에 따라 상위 99%의 character들만 고려하는 등의 옵션을 주는 것이 가능하다. 3. Subword generalzation이 가능하다. 우리가 학습한 tokenizer가 항상 완벽하다면 좋겠지만, 이것이 overfitting되어 있을 가능성을 배제할 수는 없다. 그렇기 때문에 SentencePiece에서는 우리가 학습한 tokenizer에서 나올 수 있는 token들만을 사용하는 것이 아니라, 가끔은 다른 token들도 샘플링하면서 사용하여 overfitting하는 것이 가능하다. 4. 속도가 빠르다. 직접 구현해보면 알겠지만, 알고리즘이 굉장히 최적화되어 있어서 같은 BPE와 unigram을 실행하더라도 굉장히 빠른 속도를 보여준다. 이러한 장점들 때문에 SentencePiece는 NLP에서 널리 쓰이는 tokenizer로 자리매김하였다. |
import sentencepiece as spm
spm.SentencePieceTrainer.Train('--input=$train_data_path --model_prefix=$output_file_name --vocab_size=$vocab_size --model_type=$model_type --control_symbols=$control_symbols')
https://process-mining.tistory.com/191
input: 학습해야 하는 training dataset의 경로
model_prefix: SentencePiece의 학습 결과 도출되는 결과 파일 (model 파일과 vocab 파일)의 이름
vocab_size: 결과로 만들어낼 token의 최종 개수
model_type: unigram, bpe, char, word 중 어떤 tokenizer를 활용해서 SentencePiece를 학습할 것인지 선택
control_symbols: 하나의 token으로 취급할 substring이 무엇인지 선택. 여기에서 선택된 character는 최종 substring으로 남고, merge되거나 나눠지지 않는다.
함께 보면 좋을 것 같다. https://wikidocs.net/86657
이전의 신경망기계번역(NMT: Neural machine translation)은 주로 고정된 개수의 vocabulary 안에서 작업했다. 하지만 번역(translation)이란 대부분 vocabulary 개수의 제한이 없는 open-vocabulary의 특성상, OOV(out of vocabulary) word가 많이 발생할 수밖에 없다. 과거에는 사전에 존재하지 않은 단어(out-of-vocabulary word)의 번역을 위해 backing off model을 활용하였으나 이것 또한 한계가 있으므로 이 논문에서는 희귀하거나 모르는 단어를 back-off model 대신 subword unit의 연결로 encoding하여 OOV 문제를 해결해 보고자 한다.
* backing off : (you go back to a n-1 gram level to calculate the probabilities when you encounter a word with prob=0)
prob=0인 단어를 만날 때 확률을 계산하기 위해 n-1그램 수준으로 돌아가는 것을 의미.
- 가장 많이 사용되는 방식은 "stupid back off"라고 하며, 1레벨 뒤로 돌아갈 때마다 확률을 0.4로 곱한다. 따라서 sunny가 3-gram 모델에 존재한다면 확률은 0.4 * P("sunny"|"is a very")가 된다.
필요한 경우 0.4^n을 곱하여 유니그램 모델로 돌아갈 수 있다. 여기서 n은 물러난 횟수.
최근의 Neural machine translation model은 인상적인 결과를 보여주지만 여전히 희귀 단어에 대한 번역은 해결되지 않은 문제이다. 신경망 모델의 사전은 주로 30000개에서 50000개 가량으로 한정이 되어 있지만 번역은 대부분의 경우 open-vocabulary한 특징을 지니고 있고 특히 교착어나 합성어와 같은 생산적인 단어 형성 과정(없던 단어를 만들어냄)을 가진 언어들 때문에 번역은 단어 수준 이하로 내려가는 모델을 필요로 한다.
단어 수준의 신경망기계번역 모델에서 OOV 단어들의 번역은 back-off to a dictinary look-up을 통해 작업했으며, 게다가 단어 수준 모형에서는 희귀 단어(rare word)들을 번역하거나 만들어 낼 수 없었다. Unknown words를 target text에 그대로 갖다 쓰거나 형태학적인 변화(Morphological changes)나 transliteration(음역: 다른언어로 대체하여 씀)이 때때로 필요하기도 했다.
해당 논문의 주된 목표는 이러한 rare words에 대해 back-off model 이용 없이 open-vocabulary 번역을 할 수 있는 신경망번역 모델을 만들어내는 것으로, 이것은 번역 과정을 단순화하는 것뿐만 아니라 subword model은 rare words에 있어서 large-vocabulary 모델과 back-off dictionary보다 좋은 정확도를 보였으며, 학습하지 않았던 새로운 단어들도 생산적으로 만들어냈다. back-off model 대신 본 논문에서 제시할 subword unit을 사용할 경우 OOV 문제를 더 확실히 해결해 open-vocabulary problem에서 성능 향상을 이끌어낼 수 있다.
- open vocabulary 기계 번역은 희귀 단어(rare word)를 subword units를 통해 encoding함으로써 가능해졌다.
- word segmentation 방법으로 압축 알고리즘인 byte pair encoding(BPE)를 채택했다. BPE는 가변 길이 시퀀스의 고정 크기 어휘를 통해 open vocabulary를 표현할 수 있으므로, neural network model에서의 word segmentation에 적합한 방법이다.
본 논문에서는 subword units 수준으로 동작하는 NMT모델을 연구했고, 이 모델의 주된 목표는 희귀 단어에 대한 back-off 모델 없이 네트워크 그 자체에서 open-vocabulary 번역을 수행하게 하는 것이다.
원문:
We follow the neural machine translation architecture by Bahdanau et al. (2015), which we will briefly summarize here. However, we note that our approach is not specific to this architecture. The neural machine translation system is implemented as an encoder-decoder network with recurrent neural networks. The encoder is a bidirectional neural network with gated recurrent units (Cho et al., 2014) that reads an input sequence x = (x1, ..., xm) and calculates a forward sequence of hidden states ( −→h 1, ..., −→h m), and a backward sequence ( ←−h 1, ..., ←−h m). The hidden states −→h j and ←−h j are concatenated to obtain the annotation vector hj . The decoder is a recurrent neural network that predicts a target sequence y = (y1, ..., yn). Each word yi is predicted based on a recurrent hidden state si , the previously predicted word yi−1, and a context vector ci . ci is computed as a weighted sum of the annotations hj . The weight of each annotation hj is computed through an alignment model αij , which models the probability that yi is aligned to xj . The alignment model is a singlelayer feedforward neural network that is learned jointly with the rest of the network through backpropagation. A detailed description can be found in (Bahdanau et al., 2015). Training is performed on a parallel corpus with stochastic gradient descent. For translation, a beam search with small beam size is employed.
신경망기계번역 system은 RNN으로 이루어진 encoder-decoder network로써 작동됩니다.
Encoder는 gated recurrent units를 포함한 bidirectional neural network입니다. 이는 input sequence를 forward sequence of hiddne states와 backward sequence를 계산합니다. 그 후 각각의 hidden states를 이어붙여(concat) annotation vector를 얻습니다.
Decoder는 target sequence를 예측하는 RNN입니다. target sequence의 각각의 원소는 recurrent hidden state, 이전에 예측된 단어, annotation vector들의 가중합으로 계산되는 context vector에 의해 예측됩니다. 이 때 annotation들의 가중치는 alignment model에 의해 계산됩니다.
논문에서는 Bahdanau와 연구진이 2015년에 만든 신경망 기계 번역 구조를 따릅니다. 간단하게 설명을 하지만 정확한 구조는 아니라는 점은 주의하시길 바랍니다.
신경망 기계 번역은 RNN을 이용한 encoder-decoder 구조로 구현됐습니다.
Encoder는 gated recurrent units를 가진 양방향 신경망입니다. 인코더는 입력 시퀀스 x=(x1,⋯,xm)𝑥=(𝑥1,⋯,𝑥𝑚)을 읽고 순방향 hidden states (→h1,⋯,−→hm)(ℎ1→,⋯,ℎ𝑚→)와 역방향 hidden states (←h1,⋯,←−hm)(ℎ1←,⋯,ℎ𝑚←)를 계산합니다. 그리고 hidden states →hjℎ𝑗→와 ←hjℎ𝑗←를 합쳐서(concatenate) annotation 벡터 hjℎ𝑗를 만듭니다.
Decoder는 target 시퀀스(y=(y1,⋯,yn)𝑦=(𝑦1,⋯,𝑦𝑛))를 예측하는 recurrent neural network입니다. 각 단어 yi𝑦𝑖는 recurrent hidden state si𝑠𝑖, 이 전 예측 단어 yi−1𝑦𝑖−1 그리고 context vector ci𝑐𝑖를 기반으로 예측됩니다.
ci𝑐𝑖는 hjℎ𝑗의 가중합으로 계산되고, 그때의 가중치는 alignment model αij𝛼𝑖𝑗에 의해 계산됩니다. alignment model은 네트워크의 나머지 부분과 back-propagation을 통해 함께 학습되는 single-layer feedforward 신경망입니다.
출처: https://real-myeong.tistory.com/71
[NLP] Neural Machine Translation of Rare Words with Subword Units(BPE) 리뷰
https://arxiv.org/pdf/1508.07909.pdf Abstract 이전의 Neural machine translation(NMT)는 고정된 단어장에서 작동했지만, 번역은 open-vocabulary problem입니다. 이는 OOV(Out of Vocabulary) 문제에 취약할 수밖에 없습니다. 이
real-myeong.tistory.com
해당 논문의 주된 motivation은
처음 보는 새로운 단어라 하더라도 형태소, 음소와 같이 기존에 이미 알고 있는 subword unit들에 의해
번역되었을 때 번역 결과가 명백해진다는 점이다.
본 논문에서는 rare word를 적절한 subword 단위로 나누어야
신경망번역 네트워크가 명확한 번역을 할 수 있도록 학습하며, 이전에 본 적 없는 단어들을 번역하고 만들어내는 지식을 일반화할 수 있도록 해 준다고 가정한다.
현재의 language model에서 translatable하지 않더라도, 다른 language의 translation의 subword를 사용하면 translate이 가능하다.
- 이름 등의 고유 명사는 음절 별로 대응시킨다.
- Barack Obama (English; German)
- Барак Обама (Russian)
- バラク・オバマ (ba-ra-ku o-ba-ma) (Japanese)
- 동의어, 외래어 등 같은 origin을 갖는 단어들은 일정한 규칙을 갖고 변형되므로, character-level translation 사용한다.
- claustrophobia (English)
- Klaustrophobie (German)
- Клаустрофобия (Klaustrofobiâ) (Russian)
- 복합어는 각각의 sub-word를 번역한 후 결합한다.
- solar system (English)
- Sonnensystem (Sonne + System) (German)
- Naprendszer (Nap + Rendszer) (Hungarian)
위와 같은 규칙으로 german training data에서 가장 빈도 낮은 100개의 word를 분석하면 english data를 통해 56개의 복합어, 21개의 고유명사, 6개의 외래어 등을 찾아낼 수 있었다.
통계적기계번역(SMT)에서도 unknown word 번역은 주된 연구대상이었다.
대부분의 unknown word들은 name같은 고유명사들이었으며 OOV는 고유명사 (사람 이름, 지역명), 외래어 등에 대해서 자주 발생한다. 이를 해결하기 위해 character level로 word를 분리한 뒤, 각 character들이 일정한 기준을 충족할 경우 하나의 token으로 묶어 표현하는 방식을 채택했다. 이를 통해 text size는 줄어들게 된다. 이 때 단어를 subword로 구분하는 기존의 Segmentation algorithm을 사용하되, 좀 더 과감한 기준을 적용하고자 했다. vocabulary size와 text size는 서로 상충하는 관계이므로 vocabulary size가 감소한다면 시간/공간 복잡도는 낮아지겠지만 unknown word의 개수가 증가하게 된다.
BPE는 서브워드 분리(subword segmentation) 알고리즘으로, 기존에 있던 단어를 분리한다는 의미이다. BPE을 요약하면, 글자(character) 단위에서 점차적으로 단어 집합(vocabulary)을 만들어 내는 Bottom up 방식의 접근을 사용한다.
서브워드 분리(Subword segmenation) 작업은 하나의 단어는 더 작은 단위의 의미있는 여러 서브워드들(Ex) birthplace = birth + place)의 조합으로 구성된 경우가 많기 때문에, 하나의 단어를 여러 서브워드로 분리해서 단어를 인코딩 및 임베딩하겠다는 의도를 가진 전처리 작업입니다. 이를 통해 OOV나 희귀 단어, 신조어와 같은 문제를 완화시킬 수 있습니다. 실제로 언어의 특성으로 인해 영어나 한국어는 서브워드 분리를 시도했을 때 어느 정도 의미있는 단위로 나누는 것이 가능합니다. (https://wikidocs.net/22592)
우선 훈련 데이터에 있는 단어들을 모든 글자(chracters) 또는 유니코드(unicode) 단위로 단어 집합(vocabulary)를 만들고, 가장 많이 등장하는 유니그램을 하나의 유니그램으로 통합한다.
BPE는 다음과 같은 과정을 따른다.
- word를 character의 sequence로 변환 후 end symbol · 추가
- 모든 character의 pair를 센 후 가장 빈도가 높은 pair of character (‘A’, ‘B’)를 새로운 symbol ‘AB’ (character n-gram)로 치환
- 2번 단계를 원하는 횟수만큼(vocabulary size만큼 token이 생성될 때까지) 반복
BPE의 반복 횟수는 vocabulary size라는 hyperparameter에 따라 결정된다.
import re, collections
from IPython.display import display, Markdown, Latex
num_merges = 10
dictionary = {'l o w </w>' : 5,
'l o w e r </w>' : 2,
'n e w e s t </w>':6,
'w i d e s t </w>':3
}
def get_stats(dictionary):
# 유니그램의 pair들의 빈도수를 카운트
pairs = collections.defaultdict(int)
for word, freq in dictionary.items():
symbols = word.split()
for i in range(len(symbols)-1):
pairs[symbols[i],symbols[i+1]] += freq
print('현재 pair들의 빈도수 :', dict(pairs))
return pairs
def merge_dictionary(pair, v_in):
v_out = {}
bigram = re.escape(' '.join(pair))
p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')
for word in v_in:
w_out = p.sub(''.join(pair), word)
v_out[w_out] = v_in[word]
return v_out
bpe_codes = {}
bpe_codes_reverse = {}
for i in range(num_merges):
display(Markdown("### Iteration {}".format(i + 1)))
pairs = get_stats(dictionary)
best = max(pairs, key=pairs.get)
dictionary = merge_dictionary(best, dictionary)
bpe_codes[best] = i
bpe_codes_reverse[best[0] + best[1]] = best
print("new merge: {}".format(best))
print("dictionary: {}".format(dictionary))
Iteration 1
현재 pair들의 빈도수 : {('l', 'o'): 7, ('o', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 8, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('e', 's'): 9, ('s', 't'): 9, ('t', '</w>'): 9, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'e'): 3}
new merge: ('e', 's')
dictionary: {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3}
Iteration 2
현재 pair들의 빈도수 : {('l', 'o'): 7, ('o', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('w', 'es'): 6, ('es', 't'): 9, ('t', '</w>'): 9, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'es'): 3}
new merge: ('es', 't')
dictionary: {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}
Iteration 3
현재 pair들의 빈도수 : {('l', 'o'): 7, ('o', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('w', 'est'): 6, ('est', '</w>'): 9, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est'): 3}
new merge: ('est', '</w>')
dictionary: {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}
Iteration 4
현재 pair들의 빈도수 : {('l', 'o'): 7, ('o', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('w', 'est</w>'): 6, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('l', 'o')
dictionary: {'lo w </w>': 5, 'lo w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}
Iteration 5
현재 pair들의 빈도수 : {('lo', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('w', 'est</w>'): 6, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('lo', 'w')
dictionary: {'low </w>': 5, 'low e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}
Iteration 6
현재 pair들의 빈도수 : {('low', '</w>'): 5, ('low', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('w', 'est</w>'): 6, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('n', 'e')
dictionary: {'low </w>': 5, 'low e r </w>': 2, 'ne w est</w>': 6, 'w i d est</w>': 3}
Iteration 7
현재 pair들의 빈도수 : {('low', '</w>'): 5, ('low', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('ne', 'w'): 6, ('w', 'est</w>'): 6, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('ne', 'w')
dictionary: {'low </w>': 5, 'low e r </w>': 2, 'new est</w>': 6, 'w i d est</w>': 3}
Iteration 8
현재 pair들의 빈도수 : {('low', '</w>'): 5, ('low', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('new', 'est</w>'): 6, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('new', 'est</w>')
dictionary: {'low </w>': 5, 'low e r </w>': 2, 'newest</w>': 6, 'w i d est</w>': 3}
Iteration 9
현재 pair들의 빈도수 : {('low', '</w>'): 5, ('low', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('low', '</w>')
dictionary: {'low</w>': 5, 'low e r </w>': 2, 'newest</w>': 6, 'w i d est</w>': 3}
Iteration 10
현재 pair들의 빈도수 : {('low', 'e'): 2, ('e', 'r'): 2, ('r', '</w>'): 2, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'est</w>'): 3}
new merge: ('w', 'i')
dictionary: {'low</w>': 5, 'low e r </w>': 2, 'newest</w>': 6, 'wi d est</w>': 3}
본 논문에서는 BPE를 두 가지 방법으로 적용하고 평가했다.
- source, target vocabulary를 따로 만들도록 독립적으로 학습
- source, target vocabulary 2개를 합쳐서 한 번에 학습(joint BPE)
전자는 text와 vocabulary 크기가 compact하다는 장점과, 만들어진 subword unit이 훈련 데이터 내에 존재한다는 장점이 있다.
반면에 후자는 source, target의 segmentation 사이의 연관성을 증가 시켜준다.

- WDict : back-off dictionary를 이용한 단어 수준 모델
- WUnk : back-off dictionary 없이 OOV를 UNK로 전부 치환한 단어 수준 모델
(아래는 subword model) - C2-50K : char-bigram 사용 (shortlist of 50000 unsegmented words)
- BPE-60K : 독립 BPE사용(source, target 데이터에 대해 각각 BPE적용)
- BPE-J90K : joint BPE사용 모델
단어 수준의 모델끼리 비교해보면 back-off dictionary 사용 모델이 rare/unseen word에 대한 unigram F1 score이 향상되는 것을 볼 수 있으며, source와 target 각각 따로 BPE를 수행하는 BPE보다 동시에 수행하는 BPE joint가 더 좋은 성능을 보였다.
또한 전체적으로 볼 때 subword모델이 웬만해서 back-off dictionary모델에 비해 성능이 좋다는 것도 확인할 수 있었다.
논문의 주된 주장은 rare and unknown 단어들의 번역은 단어수준 모델에서는 성능이 좋지 못하고 subword 모델에서는 번역 성능을 높인다는 것이다.

Figure2에서 모든 system에 대해 Unigram F1 값이 저빈도 단어들에 대해 감소하는 것을 확인할 수 있다.
논문의 주된 기여는 희귀 단어들을 subword units의 sequence로 표현함으로써 신경망 기계번역 시스템이 open-vocabulary 번역을 할 수 있다는 것을 보여주었다는 점이다. 이 방법은 back-off translation 모델을 사용하는 것보다 간단하며 효율적이다.
분석을 통해 baseline NMT system이 out-of-vocabulary 단어뿐만 아니라 rare in-vocabuary 단어에서도 좋지 않은 성능을 보이는 것을 밝혔고, subword 모델의 vocabulary 크기를 줄이는 것이 실질적으로 성능 향상에 도움이 되다는 것도 확인되었다. 향후 번역 작업에 필요한 최적의 어휘 크기를 자동으로 학습하는 것이 연구과제가 될 것이며, 이것은 언어 쌍과 training data의 양에 따라 달라질 것으로 예상된다. 상대적인 효과는 어휘 크기와 같은 언어별 요인에 따라 달라지겠지만, subword segmentation이 대부분의 언어 쌍에 적합하다고 여겨지며 이는 대규모 NMT(신경 기계 번역) 어휘나 back-off model의 의존도도 줄여줄 것이다.