Bag-of-words: текст как мешок слов

Bag-of-words — самый простой способ превратить текст в вектор чисел: считаем, сколько раз встретилось каждое слово.

Bag-of-words (мешок слов) — представление документа как вектора частот слов без учёта их порядка.

Идея «мешка»

Представьте, что вы высыпали все слова документа в мешок и перемешали. Порядок потерян — остались только сами слова и сколько раз каждое встретилось. Звучит грубо, но для многих задач (спам, тематика, тональность) этого удивительно достаточно: набор слов «выигрыш, бесплатно, кредит» сам по себе выдаёт спам.

Шаг 1. Словарь

Сначала собираем все уникальные слова корпуса — это словарь. Каждому слову даём номер (индекс в векторе).

docs = [
    "кот сидит на окне",
    "кот любит молоко",
    "собака любит кота",
]
vocab = sorted({w for doc in docs for w in doc.split()})
print("Словарь:", vocab)
print("Размер словаря:", len(vocab))

Вывод:

Словарь: ['кот', 'кота', 'любит', 'молоко', 'на', 'окне', 'сидит', 'собака']
Размер словаря: 8

Заметьте: «кот» и «кота» попали как разные слова — вот зачем нужна лемматизация из прошлого урока. Для простоты пока оставим как есть.

Шаг 2. Вектор частот

Теперь каждый документ превращаем в вектор длиной со словарь: на месте слова стоит, сколько раз оно встретилось в документе.

from collections import Counter

docs = [
    "кот сидит на окне",
    "кот любит молоко",
    "собака любит кота",
]
vocab = sorted({w for doc in docs for w in doc.split()})

def to_vector(doc):
    counts = Counter(doc.split())
    return [counts.get(word, 0) for word in vocab]

print("Словарь:", vocab)
for doc in docs:
    print(to_vector(doc), "<-", doc)

Вывод:

Словарь: ['кот', 'кота', 'любит', 'молоко', 'на', 'окне', 'сидит', 'собака']
[1, 0, 0, 0, 1, 1, 1, 0] <- кот сидит на окне
[1, 0, 1, 1, 0, 0, 0, 0] <- кот любит молоко
[0, 1, 1, 0, 0, 0, 0, 1] <- собака любит кота

Готово: три строки текста стали тремя векторами одинаковой длины. С этого момента это обычные числа — можно подавать в модель ML, считать похожесть, обучать классификатор.

Что мы выиграли и что потеряли

  • Плюс: просто, быстро, понятно. Достаточно для многих задач классификации.
  • Минус 1 — порядок: «собака укусила человека» и «человек укусил собаку» дают одинаковый вектор. Смысл частично теряется.
  • Минус 2 — размерность: словарь реального корпуса — десятки тысяч слов, векторы огромные и почти полностью из нулей (разрежённые).
  • Минус 3 — нет смысла слов: «кот» и «кошка» для модели абсолютно разные измерения, никакой близости между ними нет.

Эти минусы и есть мотивация для следующих уроков: n-граммы частично вернут порядок, TF-IDF исправит перекос к частым словам, а эмбеддинги (раздел 4) дадут словам близость по смыслу.

Итог

  • Bag-of-words = словарь + вектор частот для каждого документа.
  • Порядок слов теряется, отсюда и название «мешок».
  • Просто и эффективно для классификации, но векторы большие и разрежённые.
  • Модель не знает, что «кот» и «кошка» близки — это разные измерения.
Проверьте себя
1. Что представляет собой документ в модели bag-of-words?
AУпорядоченную последовательность символов
BВектор частот слов без учёта их порядка
CДерево синтаксического разбора
DОдно число — длину текста
2. Почему «собака укусила человека» и «человек укусил собаку» дают одинаковый bag-of-words вектор?
AПотому что слова приведены к нижнему регистру
BПотому что bag-of-words игнорирует порядок слов
CПотому что у предложений разная длина
DПотому что удалены стоп-слова
3. Какой недостаток bag-of-words исправляют эмбеддинги слов?
AСлишком быстрая работа
BМодель не знает, что «кот» и «кошка» близки по смыслу
CНевозможность привести текст к нижнему регистру
DОтсутствие словаря
Поддержать проект