
바이트 페어 인코딩(Byte Pair Encoding, BPE)
💡 BPE(Byte Pair Encoding)란?
"Byte Pair Encoding(BPE)"은 '바이트 쌍 인코딩'으로 직역되며, 이는 단어나 문자열에서 연속된 바이트 쌍을 찾아내어 하나의 새로운 토큰으로 결합하는 방식을 의미합니다. 이 알고리즘은 주어진 데이터에서 자주 등장하는 바이트 쌍을 기반으로 새로운 토큰을 만들어내어 데이터를 효율적으로 표현하고 토큰화하는 데 사용됩니다.
📢 여기서 OOV란?
기계는 문제를 풀 때 모르는 단어가 나오면 주어진 문제를 풀기 어려워지는데, 이러한 상황을 OOV(Out-Of-Vocabulary)라고 합니다. OOV는 학습되지 않은 단어가 들어온 상황을 나타내며, 이를 완화하기 위해 Subword Tokenization 방법을 사용합니다. Subword Tokenization 기법 중 BPE나 WordPiece가 해당 기법에 포함됩니다.
BPE 특징과 동작 방식
BPE는 글자 단위에서 점차적으로 단어 집합(Vocabulary)을 만들어 내는 Bottom up 방식을 사용한다. 우선 BPE 방법을 사용하려면 주어진 코퍼스를 글자 단위로 분리한 후 반복적으로 가장 빈도가 높은 바이트 쌍(byte pair)을 찾아내어 하나의 새로운 토큰으로 결합하는 과정을 수행해야합니다.
구체적인 단계는 아래와 같습니다.
✔ 주어진 단어의 빈도수
어떤 데이터로부터 각 단어의 빈도수를 체크하였다고 가정해보겠습니다.
word_freq_1 = {
"w1": 10,
"w2": 5,
"w3": 12,
"w4": 4,
}
print(word_freq_1)
# 출력값
{'low': 10, 'lower': 5, 'newest': 12, 'widest': 4}
1️⃣ 코퍼스 분리
주어진 코퍼스를 글자 단위로 분리하여 초기 토큰 집합을 생성한다.
w1 = list(w1)
w2 = list(w2)
w3 = list(w3)
w4 = list(w4)
print(w1, w2, w3, w4)
# 출력결과
['l', 'o', 'w'] ['l', 'o', 'w', 'e', 'r'] ['n', 'e', 'w', 'e', 's', 't'] ['w', 'i', 'd', 'e', 's', 't']
first_voca = list(set(w1 + w2 + w3 + w4))
print(first_voca)
#출력
['d', 'r', 'e', 'w', 'l', 'n', 'i', 's', 't', 'o']
초기 토큰 집합을 생성하면 초기 단어 집합(vocabulary)도 만들어주어야합니다.
위와 같은 방식을 이용하여 중복된 알파벳을 제거 후 변수에 담아 초기 단어 집합을 만들어주었습니다.
2️⃣ 연속한 pair를 만들고 각각의 빈도를 탐색
BPE의 특징은 알고리즘 동작을 몇번 반복할지를 사용자(개발자)가 정한다는 점이다. 우선 여기서는 5번만 반복한다는 전제하제 예제를 만들어보겠습니다. 여기서 알고리즘 동작이란 가장 빈도수가 높은 쌍을 하나의 단어로 통합하는 과정을 말합니다.
STEP 1(1번 반복 시)
w1_pair = ["lo", "ow"]
w2_pair = ["lo", "ow", "we", "er"]
w3_pair = ["ne", "ew", "we", "es", "st"]
w4_pair = ["wi", "id", "de", "es", "st"]
first_pair = list(set(w1_pair + w2_pair + w3_pair + w4_pair))
print(first_pair)
# 출력
['id', 'er', 'st', 'lo', 'ew', 'ne', 'wi', 'de', 'es', 'we', 'ow']
low, lower, newest, widest를 각 연속된 글자 pair들을 merge하면 위와 같은 pair들이 만들어진다.
word_freq_2 = {
"lo": word_freq_1["w1"] + word_freq_1["w2"],
"ow": word_freq_1["w1"] + word_freq_1["w2"],
"we": word_freq_1["w2"] + word_freq_1["w3"],
"er": word_freq_1["w2"],
"ne": word_freq_1["w3"],
"ew": word_freq_1["w3"],
"es": word_freq_1["w3"] + word_freq_1["w4"],
"st": word_freq_1["w3"] + word_freq_1["w4"],
"wi": word_freq_1["w4"],
"id": word_freq_1["w4"],
"de": word_freq_1["w4"]
}
print(word_freq_2)
# 출력
{'lo': 15, 'ow': 15, 'we': 17, 'er': 5, 'ne': 12, 'ew': 12, 'es': 16, 'st': 16, 'wi': 4, 'id': 4, 'de': 4
각 pair의 빈도수를 확인해보면 we가 17로 제일 높은 부분을 확인할 수 있는데 BPE는 빈도수가 제일 높은 순서대로 merge되기 때문에 첫번째 반복때는 we가 먼저 merge되는 모습을 확인할 수 있다.
w1 = ['l', 'o', 'w']
w2 = ['l', 'o', 'we', 'r']
w3 = ['n', 'e', 'we', 's', 't']
w4 = ['w', 'i', 'd', 'e', 's', 't']
첫번째 반복 시 위와 같이 merge 된 모습을 확인할 수 있다. 그리고 위 과정을 반복한다 한 STEP만 더 해보자.
STEP 2(2번 반복 시)
w1_pair = ["lo", "ow"]
w2_pair = ['lo', 'owe', 'wer']
w3_pair = ['ne', 'ewe', 'wes', 'st']
w4_pair = ["wi", "id", "de", "es", "st"]
first_pair = list(set(w1_pair + w2_pair + w3_pair + w4_pair))
print(first_pair)
# 출력
['ewe', 'id', 'owe', 'st', 'lo', 'ne', 'wes', 'wi', 'de', 'es', 'wer', 'ow']
다시 연속된 pair로 merge 시 위와 같은 결과가 나옵니다.
word_freq_2 = {
"lo": word_freq_1["w1"] + word_freq_1["w2"],
"ow": word_freq_1["w1"] + word_freq_1["w2"],
"owe": word_freq_1["w2"],
"wer": word_freq_1["w2"],
"ne": word_freq_1["w3"],
"ewe": word_freq_1["w3"],
"wes": word_freq_1["w3"],
"st": word_freq_1["w3"] + word_freq_1["w4"],
"wi": word_freq_1["w4"],
"id": word_freq_1["w4"],
"de": word_freq_1["w4"],
"es": word_freq_1["w4"]
}
print(word_freq_2)
# 출력
{'lo': 15, 'ow': 15, 'owe': 5, 'wer': 5, 'ne': 12, 'ewe': 12, 'wes': 12, 'st': 4, 'wi': 4, 'id': 4, 'de': 4, 'es': 4}
그 다음 빈도수가 많은 pair는 "ow" 이므로 2번째 step 결과는 아래와 같이 나타난다.
w1 = ['l', 'ow']
w2 = ['l', 'ow', 'we', 'r']
w3 = ['n', 'e', 'we', 's', 't']
w4 = ['w', 'i', 'd', 'e', 's', 't']
3번 반복
w1 = ['l', 'ow']
w2 = ['l', 'ow', 'we', 'r']
w3 = ['n', 'e', 'we', 'st']
w4 = ['w', 'i', 'd', 'e', 'st']
4번 반복
w1 = ['low']
w2 = ['low', 'we', 'r']
w3 = ['n', 'e', 'we', 'st']
w4 = ['w', 'i', 'd', 'e', 'st']
5번 반복
w1 = ['low']
w2 = ['low', 'we', 'r']
w3 = ['ne', 'we', 'st']
w4 = ['w', 'i', 'd', 'e', 'st']
5번 반복 시 최종 vocabulary는 아래처럼 나오는 모습을 확인할 수 있다.
output
['t', 'i', 'l', 'low', 'e', 'w', 'd', 'ne', 'o', 'st', 'r', 'we', 's', 'n']