OpenVINO 토크나이저

OpenVINO 토크나이저

LLM 텍스트 생성 같은 작업은 어쨌든 토크나이제이션을 거쳐야 해요. 토크나이저가 입력 텍스트를 토큰 시퀀스(각각에 대응하는 ID)로 바꿔 줘야 모델이 추론 중에 이해하고 처리할 수 있거든요. 반대로 숫자 시퀀스를 문자열로 되돌리는 변환은 디토크나이제이션이라고 불러요. 이 글에서는 OpenVINO 토크나이저가 어떻게 작동하고 설치·변환·사용하는지 하나씩 살펴볼게요.

출처: OpenVINO Tokenizers — OpenVINO™ documentation

토크나이저와 모델의 관계

토크나이저와 모델의 관계에서 기억해야 할 핵심이 두 가지 있어요.

  • 텍스트 입력을 받는 모든 모델은 토크나이저와 짝을 이루며, 토크나이저 없이는 사용할 수 없어요.
  • 특정 작업에서 모델 정확도를 재현하려면 모델 학습 때 쓴 것과 동일한 토크나이저를 사용하는 게 필수예요.

OpenVINO Tokenizers는 OpenVINO 확장이자 Python 라이브러리로, 토크나이저 변환을 간소화해 프로젝트에 매끄럽게 통합되게 해줘요. 할 수 있는 일을 정리하면 이래요.

  • OpenVINO에 텍스트 처리 연산을 추가해요. 토크나이저와 디토크나이저 모두 OpenVINO 모델이라서 다른 모델처럼 읽기·컴파일·저장 등 모든 작업을 똑같이 할 수 있어요.
  • 제3자 의존성 없이 토크나이제이션·디토크나이제이션을 수행해요.
  • Hugging Face 토크나이저를 OpenVINO 토크나이저·디토크나이저로 변환해 여러 환경에 효율적으로 배포할 수 있어요.
  • OpenVINO 모델 여러 개를 하나로 합칠 수 있어요. 분류기나 RAG 임베더처럼 파이프라인 추론마다 토크나이저와 모델을 한 번씩 쓰는 모델에 권장돼요.
  • 텍스트 생성 모델에 그리디 디코딩 파이프라인을 추가해요.
  • TensorFlow Text MUSE 모델 같은 TensorFlow 모델을 쓸 수 있어요. 단, TensorFlow 통합은 StringSplit, StaticRegexpReplace, StringLower 같은 문자열 텐서 연산을 위해 추가 변환 확장이 필요해요.

참고: OpenVINO Tokenizers는 CPU 디바이스에서만 추론할 수 있어요.

지원되는 토크나이저

Hugging Face 토크나이저 타입 토크나이저 모델 타입 토크나이저 디토크나이저
Fast WordPiece Yes No
BPE Yes Yes
Unigram No No
Legacy SentencePiece .model Yes Yes
Custom tiktoken Yes Yes
RWKV Trie Yes Yes

참고: 변환된 토크나이저와 원본 토크나이저의 출력이 달라져 특정 작업의 모델 정확도가 오르거나 내릴 수 있어요. 이 변화를 완화하려면 프롬프트를 수정하면 돼요. OpenVINO Tokenizers 저장소에서 원본과 변환된 토크나이저/디토크나이저 출력이 일치하는 테스트 비율을 확인할 수 있어요.

Python 설치

  1. 가상 환경을 만들고 활성화해요.
python3 -m venv venv

source venv/bin/activate
  1. OpenVINO Tokenizers를 설치해요.

변환된 OpenVINO 토크나이저 사용, Hugging Face 토크나이저를 OpenVINO 토크나이저로 변환, 최신 변경 사항을 실험할 프리릴리스 버전 설치, 소스에서 빌드하는 방법까지 여러 선택지가 있어요. Conda 배포판으로도 설치할 수 있어요.

pip install openvino-tokenizers
pip install openvino-tokenizers[transformers]
pip install --pre -U openvino openvino-tokenizers --extra-index-url https://storage.openvinotoolkit.org/simple/wheels/nightly
source path/to/installed/openvino/setupvars.sh

      git clone https://github.com/openvinotoolkit/openvino_tokenizers.git

cd openvino_tokenizers

pip install --no-deps .

C++ 설치

C++ 파이프라인에서는 사전 빌드된 바이너리로 변환된 토크나이저를 사용할 수 있어요.

  1. OS에 맞는 OpenVINO 아카이브 배포판을 내려받아 압축을 풀어요.
  2. OpenVINO Tokenizers 사전 빌드 라이브러리를 내려받아요. 호환성을 위해 OpenVINO Tokenizers 버전의 앞 세 자리 숫자가 OpenVINO 버전과 OS와 일치해야 해요.
  3. OpenVINO 설치 디렉토리에 OpenVINO Tokenizers 아카이브를 풀어요.
플랫폼 대상 디렉토리
Linux_x86 <openvino_dir>/runtime/lib/intel64/
Linux_arm64 <openvino_dir>/runtime/lib/aarch64/
Windows <openvino_dir>\runtime\bin\intel64\Release\
MacOS_arm64 <openvino_dir>/runtime/lib/arm64/Release/

이후 코드에 바이너리 확장을 추가할 수 있어요.

core.add_extension("libopenvino_tokenizers.so")
core.add_extension("openvino_tokenizers.dll")
core.add_extension("libopenvino_tokenizers.dylib")

2023.3.0.0 버전을 쓴다면 바이너리 확장 파일 이름은 (lib)user_ov_extension.(dll/dylib/so)예요.

변환된 모델을 읽고 컴파일하는 방법은 모델 준비 가이드에서 배울 수 있어요.

토크나이저 사용법

1. 토크나이저를 OpenVINO IR로 변환하기

CLI 도구나 Python API로 Hugging Face 토크나이저를 IR로 변환할 수 있어요. 이미 변환된 OpenVINO 토크나이저가 있다면 이 단계를 건너뛰면 돼요.

먼저 의존성을 설치해요.

pip install openvino-tokenizers[transformers]

CLI로 변환하려면 convert_tokenizer 명령을 쓰고, 변환된 모델을 컴파일해 사용할 수 있어요.

!convert_tokenizer $model_id --with-detokenizer -o tokenizer
from pathlib import Path
import openvino_tokenizers
from openvino import Core


tokenizer_dir = Path("tokenizer/")
core = Core()
ov_tokenizer = core.read_model(tokenizer_dir / "openvino_tokenizer.xml")
ov_detokenizer = core.read_model(tokenizer_dir / "openvino_detokenizer.xml")

tokenizer, detokenizer = core.compile_model(ov_tokenizer), core.compile_model(ov_detokenizer)

Python API로 변환하면 convert_tokenizer에 Hugging Face 토크나이저를 넘겨 두 OpenVINO 모델을 얻어요.

from transformers import AutoTokenizer
from openvino_tokenizers import convert_tokenizer

hf_tokenizer = AutoTokenizer.from_pretrained(model_id)
ov_tokenizer, ov_detokenizer = convert_tokenizer(hf_tokenizer, with_detokenizer=True)

save_model로 변환된 토크나이저를 나중에 재사용할 수 있게 저장하세요.

from pathlib import Path
from openvino import save_model

tokenizer_dir = Path("tokenizer/")
save_model(ov_tokenizer, tokenizer_dir / "openvino_tokenizer.xml")
save_model(ov_detokenizer, tokenizer_dir / "openvino_detokenizer.xml")

컴파일해서 사용하는 것도 가능해요.

from openvino import compile_model

tokenizer, detokenizer = compile_model(ov_tokenizer), compile_model(ov_detokenizer)

결과는 ov_tokenizer, ov_detokenizer라는 두 OpenVINO 모델이에요.

2. 토크나이즈하고 입력 준비하기

토큰화된 입력에서 필요한 보조 입력(포지션 ID, 빔 인덱스)을 만들어 추론 요청 입력에 담아요. EOS 토큰 ID는 디토크나이저의 rt_info에서 읽어와요.

import numpy as np

text_input = ["Quick brown fox jumped"]

model_input = {name.any_name: output for name, output in tokenizer(text_input).items()}

if "position_ids" in (input.any_name for input in infer_request.model_inputs):
   model_input["position_ids"] = np.arange(model_input["input_ids"].shape[1], dtype=np.int64)[np.newaxis, :]

# no beam search, set idx to 0
model_input["beam_idx"] = np.array([0], dtype=np.int32)
# end of sentence token is where the model signifies the end of text generation
# read EOS token ID from rt_info of tokenizer/detokenizer ov.Model object
eos_token = ov_tokenizer.get_rt_info(EOS_TOKEN_ID_NAME).value

3. 텍스트 생성하기

추론 루프를 돌며 마지막 토큰의 예측을 누적하고, EOS 토큰을 만나면 멈춰요. 다음 추론을 위해 새로 생성한 토큰과 어텐션 마스크·포지션 ID를 갱신하죠.

tokens_result = np.array([[]], dtype=np.int64)

# reset KV cache inside the model before inference
infer_request.reset_state()
max_infer = 10

for _ in range(max_infer):
   infer_request.start_async(model_input)
   infer_request.wait()

   # get a prediction for the last token on the first inference
   output_token = infer_request.get_output_tensor().data[:, -1:]
   tokens_result = np.hstack((tokens_result, output_token))
   if output_token[0, 0] == eos_token:
      break

   # prepare input for new inference
   model_input["input_ids"] = output_token
   model_input["attention_mask"] = np.hstack((model_input["attention_mask"].data, [[1]]))
   model_input["position_ids"] = np.hstack(
      (model_input["position_ids"].data, [[model_input["position_ids"].data.shape[-1]]])
   )

4. 출력 디토크나이즈하기

누적된 토큰 결과를 디토크나이저에 넘겨 문자열로 되돌려요.

text_result = detokenizer(tokens_result)["string_output"]
print(f"Prompt:\n{text_input[0]}")
print(f"Generated:\n{text_result[0]}")

더 알아보기 (Learn more)