BPE (Byte Pair Encoding)

BPE (Byte -Pair Encoding)을 이용하여 최근에는 tokenizer 처럼 사용하는 것 같음. BERT 논문이나 , GPT-2논문 볼 때도 BPE를 사용하는 등 알아봐야할 듯 싶어서 찾아보았다.

이것을 사용하면 rare word에 잘 대처할 수 있고, OOV 등에 대한 문제점을 극복할 수 있다고 함. GPT-2논문에서도 UNK 같은 것을 대처할 수 있는 등의 장점을 말한다.
사실 character embedding 논문을 생각해보면, unit의 단위가 character 단위가 되기 때문에 rare word에 잘 대처할 수 있다.
하지만 character 단위는 너무 의미를 담고 있지 않고, 자주 빈번하는 쌍? 즉 bi-gram(or k-gram)와 같이 자주 만나는 character끼리 붙여서 하나의 unit으로 만들어 줌으로써 character과 word level 중간 정도의 단계로 최소한의 의미를 가지는 unit을 가져가는 느낌이다.
(논문을 읽어보지는 않았지만 논문에 좀 더 자세한 설명이 있지 않을까 싶음. 더욱 자세히 알려면 읽어 보시는게.. )
(korea bpe로 검색되는 https://github.com/hkhpub/nmt_data_process 깃허브가 있다. 여기를 참고하면, 실제 적용 단에서 진행 방식을 알 지 않을까 싶음. 영어의 character을 한국어의 음절 단위로 시작하는 거 같음.)

블로그만 봐서는 정확하게 진행 방식이 이해가 안되었다.(느낌만 이해갔음)
그래서 예시를 들어서 한 번 해봤는데 맞는 거겠지?

논문 및 참고자료 2에 나온 예시를 한 번 해보자.
현재 vocab은 다음과 같은 상태라고 하자.
vocab = {'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
         }

즉 실제 적용할 땐, 데이터 세트 전체를 가지고 하면 될듯. 마치 word embedding할 때, 많은 빈도수의 word만 가지고 사용하는데 그 개념이라고 보면 될 듯.
즉 만약 I am a student. I want to become a smart student. 라는 것이 있다면
I: 2
a m: 1
a: 2
s t u d e n t: 2
w a n t: 1
t o: 1
b e c o m e: 1
s m a r t: 1
이런 것인 듯. 즉 띄어쓰기로 일단 단어를 구분하고 단어의 character을 한 개씩 띄어쓰기로 구분을 해두면 된다.

어쨌든, 이렇게 하고 단어 뒤에 </w> 기호를 붙이고 character을 점점 merge를 하면 된다.
다시 예제로 돌아가서 시작해보자. 병합의 방법은 bigram으로 가장 많은 빈도로 발생한 것 부터 merge를 하면 된다. 참고자료 2 블로그에서 10번에 해당하는 것을 코드로 설명했지만, 이것을 직접 해보면 다음과 같이 될 것이다.

Step 1
5번 l o w </w>         → lo ow w</w>
2번 l o w e r</w>     → lo ow we er r</w>
6번 n e w e s t </w> → ne ew we es st t</w>
3번 w i d e s t </w>  → wi id de es st t</w>

9번 es st
8번 we
7번 lo ow
6번 ne ew t</w>
5번 w</w>
3번 wi id de t</w>
2번 er r</w>

융합할 것: es

Step 2
5 l o w </w>    lo ow w</w>
2 l o w e r </w>  lo ow we er r</w>
6 n e w [es] t</w> ne ew wes est t</w>
3 w i d [es] t</w> wi id des est t</w>

9 est
7 lo ow
6 ne ew wes t</w>
5 w</w>
3 wi id des t</w>
2 we er r</w>

융합할 것: est

Step 3 (귀찮아서 step 3부터는 띄어쓰기는 안했음)
5 low</w>    lo ow w</w>
2 lower</w>  lo ow we er r</w>
6 new[est]</w> ne ew west est</w>
3 wid[est]</w> wi id dest est</w>

9 est</w>
7 lo ow
6 ne ew west
5 w</w>
3 wi id dest
2 we er r</w>

융합할 것: est</w>

Step 4
5 low</w>    lo ow w</w>
2 lower</w>  lo ow we er r</w>
6 new[est</w>] ne ew west</w>
3 wid[est</w>] wi id dest</w>

7 lo ow
6 ne ew west</w>
5 w</w>
3 wi id dest</w>
2 we er r</w>

융합할 것: lo

Step 5
[lo]w</w>    low w</w>
[lo]wer</w>  low we er r</w>
6 new[est</w>] ne ew west</w>
3 wid[est</w>] wi id dest</w>

7 low
6 ne ew west</w>
5 w</w>
3 wi id dest</w>
2 we er r</w>

융합할 것: low

Step 6

[low]</w>    low</w>
[low]er</w>  lowe er r</w>
6 new[est</w>] ne ew west</w>
3 wid[est</w>] wi id dest</w>

6 ne ew west</w>
5 low</w>
3 wi id dest</w>


2 lowe er r</w>

융합할 것: ne

Step 7
5 [low]</w>    low</w>
2 [low]er</w>  lowe er r</w>
[ne]w[est</w>] new west</w>
3 wid[est</w>] wi id dest</w>

6 new west</w>
5 low</w>
3 wi id dest</w>
2 lowe er r</w>

융합할 것: new

Step 8
5 [low]</w>    low</w>
2 [low]er</w>  lowe er r</w>
[new][est</w>] newest</w>
3 wid[est</w>] wi id dest</w>

6 newest</w>
5 low</w>
3 wi id dest</w>
2 lowe er r</w>

융합할 것 newest</w>

Step 9
5 [low]</w>    low</w>
2 [low]er</w>  lowe er r</w>
[newest</w>]
3 wid[est</w>] wi id dest</w>

5 low</w>
3 wi id dest</w>
2 lowe er r</w>

융합할 것: low</w>

Step 10
[low</w>]   
2 [low]er</w>  lowe er r</w>
6 [newest</w>]
3 wid[est</w>] wi id dest</w>

3 wi id dest</w>
2 lowe er r</w>

융합할 것: wi

Final merge 결과
5 [low</w>]   
2 [low] e r </w>  
6 [newest</w>]
[wi] d [est</w>]

즉 이렇게 merge 가 되서 하나의 단위가 되는 것이다. 즉 띄어쓰기로 unit을 구별하면 다음과 같은 결론이 나온다.
{'low</w>': 5,
 'low': 2,
 'e': 2,
 'r': 2,
 '</w>': 2,
 'newest</w>': 6,
 'wi': 3,
 'd': 3,
 'est</w>': 3}

Reference

댓글