feat: implement bpe algorithm

This commit is contained in:
Sergey Penkovsky
2025-07-11 12:21:33 +03:00
parent 45eaaabd51
commit 362a7483e6
15 changed files with 714 additions and 1 deletions

0
tests/__init__.py Normal file
View File

13
tests/conftest.py Normal file
View File

@@ -0,0 +1,13 @@
import pytest
from simple_llm.tokenizer.simple_bpe import SimpleBPE
from simple_llm.tokenizer.optimize_bpe import OptimizeBPE
@pytest.fixture(scope="session")
def large_text():
"""Генерирует большой текст для тестирования"""
return " ".join(["мама мыла раму"] * 1000)
@pytest.fixture(params=[SimpleBPE, OptimizeBPE])
def bpe_class(request):
"""Возвращает классы BPE для тестирования"""
return request.param

View File

@@ -0,0 +1,35 @@
import pytest
from simple_llm.tokenizer.simple_bpe import SimpleBPE
from simple_llm.tokenizer.optimize_bpe import OptimizeBPE
def test_large_text_processing(bpe_class, large_text):
"""Тест обработки большого текста"""
bpe = bpe_class(vocab_size=100)
bpe.fit(large_text)
# Проверки
assert 50 < len(bpe.vocab) <= 100
assert all(len(token) <= 4 for token in bpe.vocab) # Проверка на разумную длину токенов
assert "мама" in bpe.vocab or "ма" in bpe.vocab # Проверка на наличие ожидаемых токенов
def test_special_characters(bpe_class):
"""Тест обработки специальных символов"""
text = "!@#$%^&*()_+1234567890"
bpe = bpe_class(vocab_size=30)
bpe.fit(text)
# Проверки
assert len(bpe.vocab) > 10
for char in set(text):
assert any(char in token for token in bpe.vocab) # Каждый символ должен быть в каком-то токене
def test_unicode_characters(bpe_class):
"""Тест обработки unicode-символов"""
text = "日本語 한국어 русский English"
bpe = bpe_class(vocab_size=50)
bpe.fit(text)
# Проверки
assert len(bpe.vocab) > 20
assert any("" in token for token in bpe.vocab)
assert any("" in token for token in bpe.vocab)

54
tests/test_bpe.py Normal file
View File

@@ -0,0 +1,54 @@
import pytest
from simple_llm.tokenizer.simple_bpe import SimpleBPE
from simple_llm.tokenizer.optimize_bpe import OptimizeBPE
class TestBPE:
@pytest.fixture(params=[SimpleBPE, OptimizeBPE])
def bpe_class(self, request):
return request.param
def test_initialization(self, bpe_class):
"""Тест инициализации BPE-токенизатора"""
bpe = bpe_class(vocab_size=100)
assert bpe.vocab_size == 100
assert bpe.vocab == []
assert bpe.token2id == {}
assert bpe.id2token == {}
def test_fit_simple_text(self, bpe_class):
"""Тест обучения на простом тексте"""
text = "мама мыла раму"
bpe = bpe_class(vocab_size=20)
bpe.fit(text)
# Проверки словаря
assert isinstance(bpe.vocab, list)
assert len(bpe.vocab) > 0
assert len(bpe.vocab) <= 20
assert all(isinstance(token, str) for token in bpe.vocab)
# Проверка словарей
assert len(bpe.vocab) == len(bpe.token2id)
assert len(bpe.vocab) == len(bpe.id2token)
# Проверка соответствия токенов и ID
for token in bpe.vocab:
assert bpe.token2id[token] == bpe.vocab.index(token)
assert bpe.id2token[bpe.token2id[token]] == token
@pytest.mark.parametrize("text,expected_size", [
("", 0),
("а", 1),
("ааааа", 2) # Должны быть 'а' и 'аа'
])
def test_edge_cases(self, bpe_class, text, expected_size):
"""Тест граничных случаев"""
bpe = bpe_class(vocab_size=10)
bpe.fit(text)
assert len(bpe.vocab) == expected_size
def test_duplicate_protection(self, bpe_class):
"""Тест защиты от дубликатов токенов"""
bpe = bpe_class(vocab_size=50)
bpe.fit("аааааааааа" * 100) # Много повторений
assert len(bpe.vocab) == len(set(bpe.vocab))