Сравнение текстов — важная задача в обработке текстов, анализе данных и исторических исследованиях. Поиск похожих документов с помощью косинусной близости — популярный метод в NLP, который позволяет находить тексты со схожей тематикой или содержанием.
Пример 1
- Создадим мини-модель эмбеддингов (векторы слов вручную).
- Реализуем TF-IDF взвешивание.
- Посчитаем усреднённые векторы документов.
- Сравним их через косинусное сходство.
- Визуализируем результаты.
🔍 Шаг 1: Создаём «базу знаний» — ручные эмбеддинги (hand-crafted embeddings)
Допустим, у нас есть 5 слов и мы вручную зададим их векторы (размерность=3), отражающие:
animal(животное): чем ближе к 1, тем более «животное» слово.action(действие): чем ближе к 1, тем более «активное» слово.location(место): чем ближе к 1, тем больше связано с местом.
| Слово | Вектор [animal, action, location] | Пояснение |
|---|---|---|
| кот | [0.9, 0.1, 0.3] | Животное, мало действий |
| собака | [0.8, 0.5, 0.2] | Животное, более активное |
| лежит | [0.2, 0.1, 0.8] | Действие + связано с местом |
| бежит | [0.1, 0.9, 0.4] | Активное действие |
| подоконник | [0.0, 0.0, 0.9] | Чисто «место» |
📝 Шаг 2: Подготовка документов
Возьмём 3 документа (уже предобработанные):
- D1: [«кот», «лежит»]
- D2: [«собака», «бежит»]
- D3: [«кот», «лежит», «подоконник»]
Уникальные слова (термины):["кот", "лежит", "собака", "бежит", "подоконник"]
📊 Шаг 3: Вычисляем TF-IDF
TF-IDF (Term Frequency-Inverse Document Frequency) — статистическая мера, которая:
- TF (Term Frequency): Учитывает частоту слова в документе.
- IDF (Inverse Document Frequency): Штрафует слова, встречающиеся во многих документах (например, предлоги).
3.1. Считаем TF (Term Frequency) — частота слова в документе
| Термин | D1 | D2 | D3 |
|---|---|---|---|
| кот | 1/2 = 0.5 | 0 | 1/3 ≈ 0.333 |
| лежит | 1/2 = 0.5 | 0 | 1/3 ≈ 0.333 |
| собака | 0 | 1/2 = 0.5 | 0 |
| бежит | 0 | 1/2 = 0.5 | 0 |
| подоконник | 0 | 0 | 1/3 ≈ 0.333 |
3.2. Считаем IDF
IDF (Inverse Document Frequency) — это мера того, насколько редким или уникальным является слово в коллекции документов.

- В классической формуле TF-IDF (например, в sklearn) используется натуральный логарифм (ln).
- Но в некоторых старых источниках или ручных вычислениях применяют log10.
| Без логарифма: | С логарифмом: |
| Редкие слова получают гипертрофированный вес, искажая результаты. | Веса слов балансируются:: частые слова (например, «и», «в») получают почти нулевой IDF, редкие — умеренный вес. |
Всего документов: 3.
Расчёт IDF:
| Термин | nt | IDF |
|---|---|---|
| кот | 2 (D1, D3) | log(3/2)≈0.405 |
| лежит | 2 (D1, D3) | log(3/2)≈0.405 |
| собака | 1 (D2) | log(3/1)≈1.098 |
| бежит | 1 (D2) | log(3/1)≈1.098 |
| подоконник | 1 (D3) | log(3/1)≈1.098 |
3.3. TF-IDF = TF * IDF
| Термин | D1 | D2 | D3 |
|---|---|---|---|
| кот | 0.5 × 0.405 ≈ 0.203 | 0 | 0.333 × 0.405 ≈ 0.135 |
| лежит | 0.5 × 0.405 ≈ 0.203 | 0 | 0.333 × 0.405 ≈ 0.135 |
| собака | 0 | 0.5 × 1.098 ≈ 0.549 | 0 |
| бежит | 0 | 0.5 × 1.098 ≈ 0.549 | 0 |
| подоконник | 0 | 0 | 0.333 × 1.098 ≈ 0.366 |
Итоговые вектора документов:
- D1:
[0.203 (кот), 0.203 (лежит), 0 (собака), 0 (бежит), 0 (подоконник)] - D2:
[0, 0, 0.549 (собака), 0.549 (бежит), 0] - D3:
[0.135 (кот), 0.135 (лежит), 0, 0, 0.366 (подоконник)]
Интерпретация:
- «Собака» и «бежит» имеют высокий TF-IDF в D2, так как встречаются только там.
- «Подоконник» уникален для D3, поэтому его вес максимален в этом документе.
- «Кот» и «лежит» встречаются в двух документах, поэтому их TF-IDF ниже.
Этот метод помогает выделить наиболее значимые термины для каждого документа.
🧮 Шаг 4: Усреднение векторов с учётом TF-IDF
1. Вектора слов
| Слово | Вектор |
|---|---|
| кот | [0.9, 0.1, 0.3] |
| собака | [0.8, 0.5, 0.2] |
| лежит | [0.2, 0.1, 0.8] |
| бежит | [0.1, 0.9, 0.4] |
| подоконник | [0.0, 0.0, 0.9] |
2. TF-IDF веса
| Слово | D1 | D2 | D3 |
|---|---|---|---|
| кот | 0.203 | 0 | 0.135 |
| лежит | 0.203 | 0 | 0.135 |
| собака | 0 | 0.549 | 0 |
| бежит | 0 | 0.549 | 0 |
| подоконник | 0 | 0 | 0.366 |
Усреднение по документам:
- Умножаем каждый вектор слова на его TF-IDF вес в документе.
- Суммируем полученные вектора.
- Делим на сумму TF-IDF весов (или количество слов, если используем простое усреднение).
Документ D1: [«кот», «лежит»]
- Умножаем вектора на TF-IDF:
кот= [0.9, 0.1, 0.3] × 0.203 ≈ [0.1827, 0.0203, 0.0609]лежит= [0.2, 0.1, 0.8] × 0.203 ≈ [0.0406, 0.0203, 0.1624]
- Суммируем:[0.1827+0.0406, 0.0203+0.0203, 0.0609+0.1624]=[0.2233, 0.0406, 0.2233]
- Делим на сумму TF-IDF (
0.203 + 0.203 = 0.406):≈[0.55, 0.10, 0.55]
Итоговые усреднённые вектора документов:
| Документ | Усреднённый вектор (animal, action, location) | Интерпретация |
|---|---|---|
| D1 | [0.55, 0.10, 0.55] | Сбалансирован между animal и location (кот лежит где-то). |
| D2 | [0.45, 0.70, 0.30] | Максимум action (собака бежит), средние значения animal и location. |
| D3 | [0.23, 0.04, 0.75] | Доминирует location (подоконник), слабые animal и action. |
Такой подход позволяет:
- Учитывать семантику слов (через их вектора).
- Взвешивать вклад каждого слова через TF-IDF.
- Получать осмысленные документные вектора, отражающие их содержание.
🔥 Шаг 5: Сравнение через косинусное сходство
Косинусная близость (cosine similarity) — это мера схожести двух векторов, вычисляемая как косинус угла между ними. В NLP (Natural Language Processing) она часто используется для сравнения векторных представлений слов, предложений или документов.
Косинусное сходство измеряет угол между векторами, игнорируя их длину. Однако если вектора имеют разную длину, скалярное произведение может дать искажённые результаты. Поэтому:

Нормализация — это процесс приведения векторов к единичной длине (норме), что особенно важно для корректного расчёта косинусного сходства.
L2-нормализация (евклидова норма)

Дано:
- D1:
[0.55, 0.10, 0.55](норма ≈ 0.784) →[0.701, 0.128, 0.701] - D2:
[0.45, 0.70, 0.30](норма ≈ 0.874) →[0.515, 0.801, 0.343] - D3:
[0.23, 0.04, 0.75](норма ≈ 0.788) →[0.292, 0.051, 0.955]
После нормализации (||A|| = ||B|| = 1) косинусное сходство можно считать как простое скалярное произведение: cos(θ) = A · B
Косинусное сходство после нормализации
| Документы | D1 | D2 | D3 |
|---|---|---|---|
| D1 | 1.000 | 0.708 | 0.831 |
| D2 | 0.708 | 1.000 | 0.366 |
| D3 | 0.831 | 0.366 | 1.000 |
Интерпретация:
- D1 и D3 наиболее близки (оба содержат «кот» и «лежит»).
- D2 («собака бежит») отличается от остальных.
📉 Шаг 6: Визуализация в 2D
Используем PCA.
*D1 и D3 близки по оси PC1 (Animal + Location), D2 выделяется по PC2 (Action).*
💡 Выводы:
- Ручные эмбеддинги позволяют контролировать признаки (например, «животное», «действие»).
- TF-IDF уменьшает влияние частых слов (например, «кот» в нескольких документах).
- Косинусное сходство показывает семантическую близость (даже если слова разные).
- Визуализация помогает увидеть кластеры документов.
Пример 2.
После предобработки (токенизации, приведения к нижнему регистру и удаления стоп-слов) документы имеют следующий вид:
- Док1: [«кот», «лежит»]
- Док2: [«собака», «бежит»]
- Док3: [«кот», «лежит», «подоконник»]
- Док4: [«собака», «бежит», «парк»]
- Док5: [«птица», «сидит», «подоконник»]
- Док6: [«кот», «ловит», «птица»]
Шаг 1: Построение словаря уникальных терминов
Из всех документов извлекаем уникальные слова (термины):
- «кот» (Док1, Док3, Док6)
- «лежит» (Док1, Док3)
- «собака» (Док2, Док4)
- «бежит» (Док2, Док4)
- «подоконник» (Док3, Док5)
- «парк» (Док4)
- «птица» (Док5, Док6)
- «сидит» (Док5)
- «ловит» (Док6)
Итоговый словарь:["кот", "лежит", "собака", "бежит", "подоконник", "парк", "птица", "сидит", "ловит"]
Шаг 2: Векторизация документов (Bag of Words)
Каждый документ представляется в виде вектора, где каждое значение соответствует частоте термина в документе.
| Термин | Док1 | Док2 | Док3 | Док4 | Док5 | Док6 |
|---|---|---|---|---|---|---|
| кот | 1 | 0 | 1 | 0 | 0 | 1 |
| лежит | 1 | 0 | 1 | 0 | 0 | 0 |
| собака | 0 | 1 | 0 | 1 | 0 | 0 |
| бежит | 0 | 1 | 0 | 1 | 0 | 0 |
| подоконник | 0 | 0 | 1 | 0 | 1 | 0 |
| парк | 0 | 0 | 0 | 1 | 0 | 0 |
| птица | 0 | 0 | 0 | 0 | 1 | 1 |
| сидит | 0 | 0 | 0 | 0 | 1 | 0 |
| ловит | 0 | 0 | 0 | 0 | 0 | 1 |
Шаг 3: Анализ тематик (группировка документов)
Можно выделить несколько тематических групп на основе совпадения терминов:
- Тема «Кот»:
- Док1: кот + лежит.
- Док3: кот + лежит + подоконник.
- Док6: кот + ловит + птица.
- Тема «Собака»:
- Док2: собака + бежит.
- Док4: собака + бежит + парк.
- Тема «Птица»:
- Док5: птица + сидит + подоконник.
- Док6: кот + ловит + птица (пересечение с темой «Кот»).
- Общий термин «подоконник»:
- Док3 (кот) и Док5 (птица).
Шаг 4: Построение матрицы косинусной близости
Вычисление косинусной близости для всех пар:
1. Док1 vs Док2:
- Векторы: A=[1,1,0,0,0,0,0,0,0], B=[0,0,1,1,0,0,0,0,0]
- Скалярное произведение: 0+0+0+0+0+0+0+0+0=0
- Нормы: ∥A∥=1+1=2, ∥B∥=1+1=2
- Результат: 0/(2⋅2)=0.
2. Док1 vs Док3:
- Векторы: A=[1,1,0,0,0,0,0,0,0], B=[1,1,0,0,1,0,0,0,0]
- Скалярное произведение: 1+1+0+0+0+0+0+0+0=2
- Нормы: ∥A∥=2, ∥B∥=1+1+1=3
- Результат: 2/(2⋅3)≈0.82.
Итоговая матрица косинусной близости (6×6):
| Док1 | Док2 | Док3 | Док4 | Док5 | Док6 | |
|---|---|---|---|---|---|---|
| Док1 | 1.00 | 0.00 | 0.82 | 0.00 | 0.00 | 0.41 |
| Док2 | 0.00 | 1.00 | 0.00 | 0.82 | 0.00 | 0.00 |
| Док3 | 0.82 | 0.00 | 1.00 | 0.00 | 0.33 | 0.33 |
| Док4 | 0.00 | 0.82 | 0.00 | 1.00 | 0.00 | 0.00 |
| Док5 | 0.00 | 0.00 | 0.33 | 0.00 | 1.00 | 0.33 |
| Док6 | 0.41 | 0.00 | 0.33 | 0.00 | 0.33 | 1.00 |
Интерпретация результатов:
- Высокая близость (≥0.8):
- Док1 и Док3 («кот», «лежит»).
- Док2 и Док4 («собака», «бежит»).
- Средняя близость (~0.3–0.4):
- Док3 и Док5 («подоконник»).
- Док3 и Док6 («кот»).
- Док5 и Док6 («птица»).
- Нулевая близость:
- Документы без общих терминов (например, Док1 и Док2).
Матрица подтверждает тематическую группировку, выявленную ранее.
Шаг 5: Кластеризация методом k-means (k=3)
Цель:
Автоматически разделить 6 документов на 3 кластера на основе их векторных представлений (из шага 2) и матрицы косинусной близости (из шага 4).
Подготовка данных
Исходные векторы документов (Bag of Words):
| Документ | кот | лежит | собака | бежит | подоконник | парк | птица | сидит | ловит |
|---|---|---|---|---|---|---|---|---|---|
| Док1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Док2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
| Док3 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| Док4 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
| Док5 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
| Док6 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
Применение алгоритма k-means
k-means — алгоритм кластеризации, который:
- Выбирает
kначальных центроидов (случайно или по правилу). - Назначает каждый документ ближайшему центроиду (по косинусной близости).
- Пересчитывает центроиды как среднее векторов в кластере.
- Повторяет шаги 2–3 до сходимости.
Шаг 2.1: Инициализация центроидов (k=3)
Выберем 3 случайных документа в качестве начальных центроидов:
- Центроид 1: Док1 (
[1, 1, 0, 0, 0, 0, 0, 0, 0]) - Центроид 2: Док2 (
[0, 0, 1, 1, 0, 0, 0, 0, 0]) - Центроид 3: Док5 (
[0, 0, 0, 0, 1, 0, 1, 1, 0])
Шаг 2.2: Назначение документов кластерам
Для каждого документа вычисляем косинусную близость с каждым центроидом и выбираем ближайший.
| Документ | Центроид 1 (Док1) | Центроид 2 (Док2) | Центроид 3 (Док5) | Ближайший кластер |
|---|---|---|---|---|
| Док1 | 1.00 | 0.00 | 0.00 | Кластер 1 |
| Док2 | 0.00 | 1.00 | 0.00 | Кластер 2 |
| Док3 | 0.82 | 0.00 | 0.33 | Кластер 1 |
| Док4 | 0.00 | 0.82 | 0.00 | Кластер 2 |
| Док5 | 0.00 | 0.00 | 1.00 | Кластер 3 |
| Док6 | 0.41 | 0.00 | 0.33 | Кластер 1 |
Промежуточные кластеры:
- Кластер 1: Док1, Док3, Док6
- Кластер 2: Док2, Док4
- Кластер 3: Док5
Шаг 2.3: Пересчёт центроидов
Вычисляем средний вектор для каждого кластера:
- Кластер 1 (Док1, Док3, Док6):
- Среднее по «кот»: (1+1+1)/3=1
- Среднее по «лежит»: (1+1+0)/3≈0.67
- Среднее по «подоконник»: (0+1+0)/3≈0.33
- Среднее по «птица»: (0+0+1)/3≈0.33
- Среднее по «ловит»: (0+0+1)/3≈0.33
- Остальные: 0.
- Новый центроид 1:
[1, 0.67, 0, 0, 0.33, 0, 0.33, 0, 0.33]
- Кластер 2 (Док2, Док4):
- Среднее по «собака»: (1+1)/2=1
- Среднее по «бежит»: (1+1)/2=1
- Среднее по «парк»: (0+1)/2=0.5
- Остальные: 0.
- Новый центроид 2:
[0, 0, 1, 1, 0, 0.5, 0, 0, 0]
- Кластер 3 (Док5):
- Новый центроид 3 (без изменений):
[0, 0, 0, 0, 1, 0, 1, 1, 0]
- Новый центроид 3 (без изменений):
Шаг 2.4: Повторное назначение документов
Повторяем вычисление косинусной близости с новыми центроидами:
| Документ | Центроид 1 (новый) | Центроид 2 (новый) | Центроид 3 (Док5) | Ближайший кластер |
|---|---|---|---|---|
| Док1 | 0.92 | 0.00 | 0.00 | Кластер 1 |
| Док2 | 0.00 | 0.89 | 0.00 | Кластер 2 |
| Док3 | 0.88 | 0.00 | 0.33 | Кластер 1 |
| Док4 | 0.00 | 0.84 | 0.00 | Кластер 2 |
| Док5 | 0.19 | 0.00 | 1.00 | Кластер 3 |
| Док6 | 0.64 | 0.00 | 0.33 | Кластер 1 |
Кластеры не изменились → алгоритм сошёлся.
3. Итоговые кластеры
| Кластер | Документы | Тематика |
|---|---|---|
| 1 | Док1, Док3, Док6 | Кот |
| 2 | Док2, Док4 | Собака |
| 3 | Док5 | Птица |
Пояснения:
- Кластер 1: Объединяет документы с ключевым словом «кот».
- Кластер 2: Документы с «собака» и «бежит».
- Кластер 3: Док5 («птица», «подоконник») оказался в отдельном кластере, так как не имеет сильной связи с другими.
Проблемный момент:
- Док6 («кот ловит птица») мог бы быть отнесён к кластеру 3 из-за «птица», но доминирующий термин «кот» перевесил.
Шаг 6: Визуализация (опционально)
Можно использовать PCA для уменьшения размерности и отобразить документы в 2D:
- Ось X: Главная компонента (например, «кот» vs «собака»).
- Ось Y: Вторая компонента (например, «подоконник»).
- Док1, Док3, Док6 будут близки по X, Док5 смещён к «подоконник».
Итоговые согласованные результаты:
- Тематические группы:
- Кот: Док1, Док3, Док6.
- Собака: Док2, Док4.
- Птица: Док5 (частично Док6).
- Словарь терминов: 9 уникальных слов.
- Схожесть документов:
- Наибольшая: Док1-Док3, Док2-Док4.
- Наименьшая: Док2-Док5 (нет общих терминов).
Такой разбор позволяет систематизировать документы для поиска, классификации или анализа тематик.
Существует несколько методов, каждый из которых имеет свои особенности и области применения.
Сравнительные методы в анализе текстов развивались параллельно с развитием лингвистики, компьютерных технологий и исторических исследований:
- XXI век: Развитие BERT, Doc2Vec и других методов на основе глубокого обучения.
- XIX век: Сравнительно-исторический метод в лингвистике (Ф. Бопп, Я. Гримм) использовался для анализа родства языков.
- XX век: Появление TF-IDF (1960–70-е), векторных моделей (1990-е), нейросетевых подходов (2010-е).
Сравнительная таблица методов анализа документов
| Метод | Тип | Учёт контекста | Точность | Скорость | Плюсы | Минусы | Идеальное применение |
|---|---|---|---|---|---|---|---|
| Bag-of-Words (BoW) | Статистический | ❌ | Низкая | ⚡⚡⚡⚡ | Простота, интерпретируемость | Игнорирует порядок слов и семантику | Быстрый прототипинг, точный поиск ключевых слов |
| TF-IDF | Статистический | ❌ | Средняя | ⚡⚡⚡ | Учёт важности слов в коллекции | Чувствителен к длине документа | Поисковики (Elasticsearch), классификация |
| BM25 | Статистический | ❌ | Высокая | ⚡⚡⚡ | Учёт длины документа, нелинейный TF | Нет семантики | Поисковые системы, ранжирование |
| Word2Vec/GloVe (усреднение) | Векторный | ✔️ (частично) | Средняя | ⚡⚡ | Улавливает семантику слов | Теряет порядок слов | Кластеризация, рекомендации |
| Doc2Vec | Векторный | ✔️ | Высокая | ⚡⚡ | Учёт контекста документа | Требует много данных для обучения | Анализ отзывов, тематическое моделирование |
| BERT/Sentence-BERT | Нейросетевой | ✔️✔️✔️ | Очень высокая | ⚡ (требует GPU) | Лучшая семантическая точность | Ресурсоёмкость | Юридические/медицинские тексты, чат-боты |
| Siamese Networks | Нейросетевой | ✔️✔️✔️ | Очень высокая | ⚡ (требует GPU) | Сравнение пар документов | Сложность обучения | Детекция плагиата, поиск дубликатов |
Инструменты для сравнения документов
Программные библиотеки
- Python:
scikit-learn(TF-IDF, косинусное сходство).gensim(Word2Vec, Doc2Vec).transformers(BERT, RoBERTa).
- R:
tm,text2vec.
Готовые решения
- Diff Tools:
- R7-Office, Microsoft Word — сравнение версий документов 13.
- ArcGIS File Compare — для текстовых и бинарных файлов 7.
Онлайн-сервисы
- Plagiarism Checkers (Turnitin, Antiplagiat).
- Semantic Text Similarity APIs (Google NLP, Hugging Face).