Арифметика слов и похожесть на векторах
Самое впечатляющее свойство эмбеддингов: над смыслом слов можно делать арифметику, и она работает.
Векторная арифметика слов — свойство эмбеддингов, при котором сложение и вычитание векторов слов отражает смысловые отношения:
король − мужчина + женщина ≈ королева.
Почему это вообще возможно
Когда эмбеддинги обучены, разные смысловые «оси» (пол, число, время, столица-страна) оказываются закодированы как устойчивые направления в пространстве. Вектор от «мужчина» к «женщина» — это направление «смены пола». Прибавив его к «король», мы сдвигаемся в ту же сторону и попадаем рядом с «королева». Модель не понимает пол как человек — она лишь выучила, что такие пары слов систематически смещены в одном направлении.
Считаем близость и арифметику руками
Зададим маленькие игрушечные эмбеддинги «руками» так, чтобы они кодировали два признака: «королевственность» и «женскость». Затем посчитаем косинусную близость и проверим знаменитое уравнение.
import math
# игрушечные эмбеддинги: [королевственность, женскость, обычный-человек]
emb = {
"король": [0.9, 0.1, 0.2],
"королева": [0.9, 0.9, 0.2],
"мужчина": [0.1, 0.1, 0.9],
"женщина": [0.1, 0.9, 0.9],
}
def cosine(a, b):
dot = sum(x * y for x, y in zip(a, b))
na = math.sqrt(sum(x * x for x in a))
nb = math.sqrt(sum(y * y for y in b))
return dot / (na * nb)
def add(a, b): return [x + y for x, y in zip(a, b)]
def sub(a, b): return [x - y for x, y in zip(a, b)]
# король - мужчина + женщина = ?
result = add(sub(emb["король"], emb["мужчина"]), emb["женщина"])
print("Результат вектора:", [round(x, 2) for x in result])
# к какому слову он ближе всего?
for word, vec in emb.items():
print("cos(результат, %-8s) = %.3f" % (word, cosine(result, vec)))
Вывод:
Результат вектора: [0.9, 0.9, 0.2] cos(результат, король ) = 0.787 cos(результат, королева) = 1.000 cos(результат, мужчина ) = 0.307 cos(результат, женщина ) = 0.657
Вычисление король − мужчина + женщина дало вектор [0.9, 0.9, 0.2] — ровно эмбеддинг «королева», косинус 1.000. Мы убрали «мужскую часть» и добавили «женскую», оставив «королевственность». Арифметика над смыслом сработала. На настоящих 300-мерных эмбеддингах это происходит само, без ручной разметки осей.
Поиск похожих слов
Та же косинусная близость даёт поиск синонимов и ассоциаций: для слова находим ближайшие по косинусу векторы. Так находят «похожие на кот» (кошка, котёнок, питомец) или ближайшие к запросу документы в семантическом поиске.
import math
emb = {
"кот": [0.9, 0.8, 0.1],
"кошка": [0.85, 0.82, 0.12],
"котёнок": [0.8, 0.75, 0.2],
"молоток": [0.1, 0.05, 0.9],
}
def cosine(a, b):
dot = sum(x * y for x, y in zip(a, b))
na = math.sqrt(sum(x * x for x in a))
nb = math.sqrt(sum(y * y for y in b))
return dot / (na * nb)
query = "кот"
sims = [(w, cosine(emb[query], v)) for w, v in emb.items() if w != query]
sims.sort(key=lambda x: x[1], reverse=True)
for w, s in sims:
print("%-8s %.3f" % (w, s))
Вывод:
кошка 0.999 котёнок 0.995 молоток 0.201
Ближайшие к «кот» — «кошка» и «котёнок», а «молоток» далеко. То самое, чего классика дать не могла: осмысленная близость слов.
Где это используют на практике
- Семантический поиск: искать по смыслу, а не по совпадению слов.
- Рекомендации и кластеризация: группировать близкие понятия.
- Расширение запросов: добавить синонимы к поисковому запросу.
- Вход для моделей: подавать эмбеддинги вместо one-hot.
Итог
- Смысловые отношения кодируются как направления в пространстве эмбеддингов.
- Поэтому работает арифметика: король − мужчина + женщина ≈ королева.
- Похожие слова находят по косинусной близости их векторов.
- Это основа семантического поиска и рекомендаций.