토크나이저 확장
SentencePiece
# 기존 학습
import os
options = dict(
# input spec
input="filtered_tokens2.txt", # 파일 경로
input_format="text", # 파일 포맷
# output spec
model_prefix="llama_kor_tokenizer", # 토크나이저 모델명
# algorithm spec
# BPE alg
model_type="bpe", # bpe 알고리즘 사용
vocab_size=10000, # 학습된 토크나이저의 단어 집합 갯수
# normalization
normalization_rule_name="identity", # turn off normalization
remove_extra_whitespaces=False, # 불필요한 공백 제거 여부 설정
input_sentence_size=200000000, # 학습에 사용 될 전체 문장의 최대 개수
max_sentence_length=1073741824, # 학습에 사용 할 개별 문장의 최대 바이트수
seed_sentencepiece_size=1000000,
shuffle_input_sentence=True,
# rare word treatment
character_coverage=1.0, # 모든 문자를 최소 한 번 포함하도록 사전을 만듦
byte_fallback=True, # 사전에 없는 단어는 개별 바이트 단위로 토큰화하도록 설정(BBPE)
# 위 파라미터 연구 예정
# merge rules(토큰을 생성하는 방식을 제어하는 규칙 집합)
split_digits=True, # 숫자를 별도의 토큰으로 분리
split_by_unicode_script=False, # 유니코드 스크립트에 따라 분리하도록 설정(한국어는 False가 더 좋음)
split_by_whitespace=True, # 공백 문자를 기준으로 분리
split_by_number=False, # 숫자 구분 기호를 기준으로 분리/ 이를 False로 지정한 것은 바로, 한국어는 쉼표 같은 조사를 사용하여 천,만 단위를 구분하는데, 이를 True로 하면 12,345 를 1,2,3,,,4,5 이렇게 분리함.
max_sentencepiece_length=16, # 서브워드의 최대길이 지정
add_dummy_prefix=True , #
allow_whitespace_only_pieces=True,
# special tokens(llama 특수 토큰의 id 지정)
unk_id=3, # the UNK token MUST exist
bos_id=1, # the others are optional, set to -1 to turn off
eos_id=2,
# systems # 사용 가능한 모든 cpu 코어를 사용하여 학습 속도 향
num_threads=os.cpu_count(), # use ~all system resources
)
# 토크나이저 학습
import sentencepiece as spm
def main():
spm.SentencePieceTrainer.train(
**options
)
main()
Length of kor text: 18
--------------
NEW TOKENIZER
--------------
Length of encoded IDs: 8
---
Compression ratio: 0.44
---
Encoded token IDs: [359, 4949, 47, 966, 7906, 397, 1842, 49]
---
Decoded text: 안녕하세요, 오늘 날씨가 좋네요.
--------------
llama TOKENIZER
--------------
Length of encoded IDs: 30
---
Compression ratio: 1.67
---
Encoded token IDs: [1, 29871, 31734, 238, 136, 152, 30944, 31578, 31527, 29892, 29871, 31346, 238, 141, 155, 29871, 238, 133, 163, 31781, 30903, 29871, 239, 165, 142, 238, 135, 167, 31527, 29889]
---
Decoded text: <s> 안녕하세요, 오늘 날씨가 좋네요.
기존 Llama2 토크나이저 병합
import os
import re
from transformers import LlamaTokenizer
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
from huggingface_hub import hf_hub_download
from sentencepiece import sentencepiece_model_pb2 as sp_pb2_model
# 기존 토크나이저 로드하기
original_tokenizer_path = hf_hub_download(repo_id="meta-llama/Llama-2-7b-hf",token='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', filename="tokenizer.model", local_dir="original_tokenizer")
original_tokenizer_spm = sp_pb2_model.ModelProto()
original_tokenizer_spm.ParseFromString(open(original_tokenizer_path, "rb").read())
# 확장된 토크나이저 로드하기
new_tokenizer_spm = sp_pb2_model.ModelProto()
new_tokenizer_spm.ParseFromString(open("llama_kor_tokenizer.model", "rb").read())
# 기존 Llama2 토크나이저의 중복 토큰을 제외한 새로운 단어 추가하기
def contains_eng(text):
eng_pattern = re.compile(r"[\u0020-\u007E]+")
return True if eng_pattern.search(text) else False
original_tokenizer_tokenset = set(p.piece for p in original_tokenizer_spm.pieces)
print(f"Number of tokens before merge: {len(original_tokenizer_tokenset)}")
for p in new_tokenizer_spm.pieces:
piece = p.piece
if piece not in original_tokenizer_tokenset and not contains_eng(piece):
new_p = sp_pb2_model.ModelProto().SentencePiece()
new_p.piece = piece
new_p.score = 0
original_tokenizer_spm.pieces.append(new_p)
print(f"Number of tokens after merge: {len(original_tokenizer_spm.pieces)}")
# 병합한 토크나이저 저장
extended_tokenizer_save_path="llama-kor-tokenizer"
os.makedirs(extended_tokenizer_save_path, exist_ok=True)
with open(os.path.join(extended_tokenizer_save_path, "tokenizer.model"), "wb") as f:
f.write(original_tokenizer_spm.SerializeToString())
# 모델의 임베딩 레이어 크기를 조정
model.resize_token_embeddings(len(tokenizer)) # 모델은 따로 로드해야합니다.