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 = словарь + вектор частот для каждого документа.
- Порядок слов теряется, отсюда и название «мешок».
- Просто и эффективно для классификации, но векторы большие и разрежённые.
- Модель не знает, что «кот» и «кошка» близки — это разные измерения.