Я использую следующий код, чтобы подсчитать, сколько % слов закодировано в неизвестные токены.
paragraph_chinese = '...' # It is a long paragraph from a text file.
from transformers import AutoTokenizer, BertTokenizer
tokenizer_bart = BertTokenizer.from_pretrained("fnlp/bart-base-chinese")
encoded_chinese_bart = tokenizer_bart.encode(paragraph_chinese)
unk_token_id_bart = tokenizer_bart.convert_tokens_to_ids(["[UNK]"])
len_paragraph_chinese = len(paragraph_chinese)
unk_token_cnt_chinese_bart = encoded_chinese_bart.count(unk_token_id_bart[0])
print("BART Unknown Token count in Chinese Paragraph:", unk_token_cnt_chinese_bart, "(" + str(unk_token_cnt_chinese_bart * 100 / len_paragraph_chinese) + "%)")
print(type(tokenizer_bart))
который печатает:
BART Unknown Token count in Chinese Paragraph: 1 (0.015938795027095953%)
<class 'transformers.models.bert.tokenization_bert.BertTokenizer'>
Мой вопрос: я заметил один неизвестный токен. Как я могу узнать, какое слово вызывает этот неизвестный токен?
п.с. Я попробовал print(encoded_chinese_bart)
, но это список идентификаторов токенов.
Используя transformers 4.28.1
Когда вы используете BertTokenizerFast вместо «медленной» версии, вы получаете объект BatchEncoding, который дает вам доступ к нескольким удобным методам, позволяющим сопоставить токен обратно с исходной строкой.
В следующем коде используется метод token_to_chars:
from transformers import BertTokenizerFast
# just an example
paragraph_chinese = '马云 Kočka 祖籍浙江省嵊县 Kočka 现嵊州市'
tokenizer_bart = BertTokenizerFast.from_pretrained("fnlp/bart-base-chinese")
encoded_chinese_bart = tokenizer_bart(paragraph_chinese)
unk_token_id_bart = tokenizer_bart.unk_token_id
len_paragraph_chinese = len(paragraph_chinese)
unk_token_cnt_chinese_bart = encoded_chinese_bart.input_ids.count(unk_token_id_bart)
print(f'BART Unknown Token count in Chinese Paragraph: {unk_token_cnt_chinese_bart} ({unk_token_cnt_chinese_bart * 100 / len_paragraph_chinese}%)')
#find all indices
unk_indices = [i for i, x in enumerate(encoded_chinese_bart.input_ids) if x == unk_token_id_bart]
for unk_i in unk_indices:
start, stop = encoded_chinese_bart.token_to_chars(unk_i)
print(f"At {start}:{stop}: {paragraph_chinese[start:stop]}")
Оригинал:
BART Unknown Token count in Chinese Paragraph: 2 (7.407407407407407%)
At 3:8: Kočka
At 17:22: Kočka
@Raptor Я обновил свой ответ, добавив переменные.
Основываясь на ответе от cronoik, я написал свою собственную реализацию langchain_text_splitter.TokenTextSplitter
для tranformers.BertTokenizerFast
. Он использует исходный текст напрямую, сохраняя неизвестные слова (не заменяя их на [UNK]).
from typing import Any
from langchain_text_splitters import TextSplitter
from transformers import BertTokenizerFast
from transformers.tokenization_utils_base import VERY_LARGE_INTEGER
class CustomTokenTextSplitter(TextSplitter):
"""Splitting text to tokens using model tokenizer."""
def __init__(
self,
tokenizer: BertTokenizerFast,
chunk_size: int,
**kwargs: Any,
) -> None:
"""Create a new TextSplitter."""
super().__init__(**kwargs, chunk_size=chunk_size)
self.tokenizer = tokenizer
def split_text(self, text: str) -> list[str]:
return split_text_on_tokens(
text=text, chunk_size=self._chunk_size, chunk_overlap=self._chunk_overlap, tokenizer=self.tokenizer
)
def split_text_on_tokens(*, text: str, chunk_size: int, chunk_overlap: int, tokenizer: BertTokenizerFast):
"""Split incoming text and return chunks using tokenizer."""
splits = []
tokenization = tokenizer(text, add_special_tokens=False, truncation=False, max_length=VERY_LARGE_INTEGER)
input_ids = tokenization["input_ids"]
assert len(tokenization.encodings) <= 1, "Should be Only one encoding"
encodings = tokenization.encodings[0]
start_idx = 0
cur_idx = min(start_idx + chunk_size, len(input_ids))
while start_idx < len(input_ids):
start, _ = encodings.token_to_chars(start_idx)
_, end = encodings.token_to_chars(cur_idx - 1)
splits.append(text[start:end])
if cur_idx == len(input_ids):
break
start_idx += chunk_size - chunk_overlap
cur_idx = min(start_idx + chunk_size, len(input_ids))
return splits
Было бы здорово, если бы значения не были жестко закодированы.