Semantic Chunking
Семантическое разбиение делит документы в местах смены темы, определяемых по сходству эмбеддингов соседних предложений, чтобы каждый фрагмент содержал одну идею.
Семантическое разбиение делит документы на фрагменты для генерации с дополненной извлечением (RAG) и поиска в местах смены темы. Эти места определяют, сравнивая эмбеддинги соседних предложений, а не разделяя текст через фиксированное число символов или токенов. Каждый фрагмент охватывает одну идею, поэтому его единственный вектор в векторной базе данных хорошо её представляет, а извлечённый фрагмент передаёт большой языковой модели (LLM) законченную мысль, а не конец одного раздела и начало следующего.
Как работает семантическое разбиение#
Распространённый алгоритм, популяризированный уроками Грега Камрадта по разделению текста и реализованный как SemanticChunker в LangChain, состоит из четырёх шагов. Текст делят на предложения и создают эмбеддинг каждого из них, часто объединяя предложения с соседними, чтобы сгладить шум. Косинусное сходство каждой пары последовательных предложений преобразуют в расстояние. Порог отмечает точки разрыва, где расстояние необычно велико: по умолчанию LangChain использует 95-й процентиль всех расстояний в документе, а также предлагает пороги на основе стандартного отклонения, межквартильного диапазона и градиента. Предложения между точками разрыва образуют фрагмент. Семантический разделитель в LlamaIndex использует тот же порог по процентилю.
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
sentences = [
"The XR-12 coolant pump circulates water through the press line.",
"Inspect the XR-12 pump seals for leaks every week.",
"A worn pump bearing grinds loudly before it fails.",
"The staff cafeteria now opens for breakfast at 7:00.",
"The cafeteria menu adds vegetarian lunches in May.",
]
# Косинусное расстояние между каждым предложением и следующим
embeddings = model.encode(sentences, normalize_embeddings=True)
distances = 1 - np.sum(embeddings[:-1] * embeddings[1:], axis=1)
# Начать новый фрагмент, если расстояние превышает 95-й процентиль
threshold = np.percentile(distances, 95)
chunks = [[sentences[0]]]
for sentence, distance in zip(sentences[1:], distances):
if distance > threshold:
chunks.append([])
chunks[-1].append(sentence)
print(np.round(distances, 2))
print(*[" ".join(chunk) for chunk in chunks], sep="\n")Расстояния между соседними предложениями равны 0,56, 0,64, 0,98 и 0,62. Поэтому единственная точка разрыва приходится на скачок до 0,98 — от подшипника насоса к столовой. В результате получается один фрагмент из трёх предложений о насосе XR-12 и один фрагмент из двух предложений о столовой.
Сравнение стратегий разбиения на фрагменты#
- Разбиение на фрагменты фиксированного размера: Делит текст каждые N токенов, обычно с небольшим перекрытием соседних фрагментов. Метод быстрый и предсказуемый, но игнорирует границы тем.
- Рекурсивное разбиение: Сначала делит текст по абзацам, затем по строкам и предложениям, пока каждый фрагмент не уложится в ограничение по размеру. Для обычного текста LangChain рекомендует рекурсивный разделитель по символам.
- Разбиение с учётом структуры: Следует заголовкам, разделам Markdown, элементам HTML или макету страницы PDF.
- Семантическое разбиение: Следует за изменениями смысла, но требует создавать эмбеддинг каждого предложения при индексации и даёт фрагменты с сильно различающимся размером. Поэтому в реализациях задают минимальную и максимальную длину.
- Позднее разбиение на фрагменты и контекстное извлечение: Решают проблему фрагментов, которым не хватает контекста остальной части документа, например когда местоимение ссылается на объект в предыдущем фрагменте. Эти методы сочетают с любым из описанных выше способов определения границ.
Разбиение действий на фрагменты в робототехнике лишь называется так же: в этом случае политика предсказывает последовательность будущих команд двигателя как единое целое.
Семантическое разбиение за пределами текста#
В расшифровках систем преобразования речи в текст нет разделения на абзацы, поэтому точки разрыва на основе эмбеддингов часто используют, чтобы находить смену темы в совещании или подкасте. Видео тоже можно разбивать по содержанию, чтобы каждый клип показывал одно событие для понимания видео или мультимодального RAG, а не произвольный фрагмент длительностью 10 секунд. Ultralytics YOLO26 помогает определять такие границы в сценах с людьми и транспортом: режим отслеживания присваивает каждому обнаруженному объекту постоянный ID между кадрами, поэтому конвейер может начать запись клипа при появлении нового ID и завершить её, когда все отслеживаемые ID покинут сцену.
Улучшает ли семантическое разбиение качество извлечения?#
Границы тематических фрагментов особенно важны, когда несвязанные темы расположены рядом без заголовков, например в расшифровках, на веб-страницах, собранных парсером, и в объединённых заметках. Семантические фрагменты также зависят от модели эмбеддингов и порога, поэтому после изменения любого из них один и тот же документ разбивается иначе, а оба параметра следует настраивать, ориентируясь на показатель полноты на реальных запросах. В исследовании Оправданы ли вычислительные затраты на семантическое разбиение? (2024) сравнили семантическое разбиение на фрагменты и разбиение на фрагменты фиксированного размера в задачах поиска документов, поиска подтверждающих данных и генерации ответов на основе результатов поиска и выяснили, что дополнительные вычисления для семантического разбиения не обеспечили стабильного улучшения по сравнению с фрагментами фиксированного размера.










