Баннер мобильный (3) Пройти тест

Что такое эмбеддинги и почему без них не обходится ни одна языковая модель

Объясняем, как нейросети превращают слова в числа, а числа — в смысл

Разбор

22 июня 2026

Поделиться

Скопировано
Что такое эмбеддинги и почему без них не обходится ни одна языковая модель

Содержание

    Нейросеть умеет работать только с числами и ничего другого на вход не принимает. Поэтому что бы вы в нее ни загрузили, сначала это превращается в наборы чисел. А дальше сеть прогоняет эти числа через себя и превращает их в нужный результат — например, в ответ на ваш вопрос или в подпись к фото.

    При этом большинство данных конвертируются в числа довольно легко. Картинка — это сетка пикселей, и каждый из них задан числами: яркостью и долями красного, зеленого и синего. Видео — это последовательность кадров: по сути, много картинок, которые сменяют друг друга, и из-за скорости смены нам кажется, что изображение движется. Звук — это колебания, которые записываются как последовательность чисел-амплитуд. Но с текстом сложнее.

    Возьмем, к примеру, слово «жираф». В нем нет ни яркости, ни громкости — ничего, что можно измерить и записать готовым числом. Числа тексту приходится придумывать искусственно. Причем так, чтобы они несли смысл: тогда по ним понятно, что «жираф» куда ближе к «слону», чем к «клавиатуре».

    Чтобы решить эту задачу, исследователи придумали прием под названием эмбеддинги — о нем мы и поговорим в статье. Вы узнаете, что это такое, зачем они нужны и как нейросеть превращает текст в числа. Статья рассчитана на новичков, поэтому все будем упрощать и объяснять на простых примерах.

    Что такое эмбеддинги и как они хранят смысл слова

    Слово «embedding» переводится с английского как «вложение» — и в этом весь смысл приема. Объект как будто «вкладывают» в особое числовое пространство, где у каждого слова появляются собственные координаты. По ним и видно, какие слова близки по смыслу, а какие далеки друг от друга.

    Сам подход появился задолго до нейросетей — еще в 1950-х годах. Тогда лингвисты заметили интересную закономерность: о значении слова многое говорит его окружение. Например, даже если слово «бариста» вам незнакомо, но регулярно попадается рядом с «кофе», «латте», «эспрессо» и «за стойкой», вы без всякого словаря поймете, что речь о человеке, который готовит кофе.

    Что такое эмбеддинги
    Источник: автор статьи 

    Первыми эту идею подхватили поисковые системы: чтобы находить и сравнивать документы, им нужно было как-то выразить тексты числами. Сделали они это самым прямым способом — брали слово и подсчитывали, рядом с какими другими словами оно встречается во всех текстах: сколько раз поблизости попалось «кофе», сколько — «латте», сколько — «эспрессо», и так по всему словарю. Получался очень длинный список чисел — вектор слова: 

    бариста = [ …, бетон=0, кофе=53, асфальт=0, латте=31, трактор=0, эспрессо=12, носок=0, … ]

    Однако у такого подхода была проблема: в языке десятки тысяч слов, поэтому вектор для каждого слова растягивался на десятки тысяч позиций. Причем почти все эти позиции были нулевыми — ведь в реальности слово «соседствует» лишь с небольшой частью словаря. Такие раздутые разреженные векторы было сложно хранить и медленно сравнивать. Нужен был способ сжать их до компактного вида, оставив главное. Так появился латентно-семантический анализ (LSA) — прием, который уплотнял огромные разреженные векторы в короткие. Это уже было близко к идее эмбеддингов.

    С приходом нейросетей эмбеддинги стали обучаемыми. Если в LSA сначала строят таблицу, а затем отдельно сжимают ее математическими методами, то нейросеть учится иначе: она много раз пытается угадать пропущенное слово по окружению. Например: «я налил ___ в чашку» — и модель должна выбрать «чай» или «кофе». Когда она ошибается, веса слегка корректируются — и вместе с ними меняются числа в эмбеддингах. Так получаются компактные векторы, в которых слова из похожих контекстов оказываются рядом.

    В 2013 году Google представила метод Word2vec, который учится строить векторные представления слов по их контекстам. Его ценность в том, что такие представления можно было получать быстро и на больших корпусах — то есть не только на небольших учебных примерах, а на реальных данных. А раз эмбеддинги стало возможно обучать в масштабе, их начали широко применять в прикладных задачах: искать похожие слова и тексты, классифицировать документы, строить рекомендации, группировать данные. Именно после Word2vec эмбеддинги стали по‑настоящему практичным инструментом.

    Как слова превращаются в числа

    В реальных нейросетях эмбеддинги устроены куда сложнее учебных примеров: это векторы из сотен чисел, которые модель сама подбирает при обучении на огромных объемах текста. Из‑за множества параметров и шагов обучения такие числа не задают вручную, а смысл отдельных координат обычно не разбирают поштучно. Но базовая идея простая: каждому слову, фразе или кусочку текста соответствует набор чисел, и модель настраивает его так, чтобы близкие по смыслу слова оказывались рядом в этом числовом пространстве.

    Чтобы понять принцип работы эмбеддингов, представим, что у каждого слова есть всего две координаты. Первая — условная шкала «одушевленное ↔ неодушевленное»: 0 — точно неодушевленный предмет, 1 — живое существо. А значения вроде 0.9 мы используем не как «процент живости», а как удобную оценку на шкале: чем ближе к 1, тем вероятнее слово относится к миру живого.

    Вторая координата описывает размер по шкале от 0 до 1: 0 — маленькое, 1 — большое, а все промежуточные значения — что‑то среднее по масштабу.

    С такими вводными каждое слово можно записать парой чисел (X, Y) и отметить точкой на плоскости. В таблице показаны такие координаты для нескольких слов: у «котенка» вектор будет (0.9, 0.1), а у «горы» — (0.0, 1.0).

    Слово
    Живое (X)
    Крупное (Y)
    Котенок
    0.9
    0.1
    Слон
    0.9
    1.0
    Мышь
    0.9
    0.05
    Камень
    0.0
    0.3
    Гора
    0.0
    1.0

    Если вы внимательно посмотрите на числа в таблице, то заметите:

    • «Котенок» и «мышь» стоят почти вплотную — оба одушевленные и маленькие;
    • «Слон» рядом с ними по степени одушевленности, но далеко по размеру;
    • «Камень» и «гора» — в зоне неодушевленного и отличаются масштабом.

    Интересно, что мы не объясняли компьютеру, что «мышь» похожа на «котенка»: это следует из самих чисел — их векторы оказались близки. Так и работают эмбеддинги: в реальных моделях вместо двух координат используются сотни измерений, которые сеть подбирает автоматически.

    Чтобы увидеть это наглядно, построим нашу карту слов с помощью Python. Для этого откройте Google Colab, создайте блокнот и вставьте код ниже:

    import matplotlib.pyplot as plt
    
    words = {
        "Котенок": (0.9, 0.1),
        "Слон":    (0.9, 1.0),
        "Мышь":    (0.9, 0.05),
        "Камень":  (0.0, 0.3),
        "Гора":    (0.0, 1.0),
    }
    
    plt.figure(figsize=(7, 7))
    for word, (x, y) in words.items():
        plt.scatter(x, y, s=120)
        plt.annotate(word, (x, y), textcoords="offset points", xytext=(8, 6), fontsize=13)
    
    plt.title("Карта слов: смысл как координаты")
    plt.xlabel("Неодушевленное  →  Одушевленное")
    plt.ylabel("Маленькое  →  Крупное")
    plt.xlim(-0.15, 1.1)
    plt.ylim(-0.15, 1.1)
    plt.grid(True, linestyle="--", alpha=0.4)
    plt.show()
    

    Если вы все сделали правильно, Colab построит график с точками — по одной на каждое слово из таблицы. По осям отложены две координаты, а расстояние между точками показывает, какие слова оказались ближе друг к другу.

    Карта слов
    Источник: автор статьи

    Вы познакомились с общим принципом: слово можно представить вектором чисел, и тогда близость векторов начинает отражать близость смыслов. Но еще раз повторим: в реальных нейросетях такие координаты никто не задает вручную — модель подбирает их сама, и измерений там не два, а сотни. Поэтому в следующем разделе мы разберем, как это происходит при обучении и почему после него слова действительно начинают группироваться по смыслу.

    Как нейросеть сама подбирает числа в эмбеддингах

    Чтобы понять, откуда берутся эмбеддинги, проведем небольшой эксперимент. Возьмем несколько фраз, запустим обучение упрощенной модели и посмотрим на результаты: сначала ближайшие слова будут выглядеть почти случайно, а затем начнут собираться в группы. После мы спроецируем получившиеся векторы на плоскость и посмотрим, как они выглядят на графике.

    Никакой специальной подготовки для эксперимента не нужно: откройте Google Colab, создайте новый блокнот и повторяйте шаги из этого раздела.

    Шаг 1. Для начала посмотри фразы, с которыми мы будем работать:

    texts = [
        "кот пьет молоко",
        "котенок пьет молоко",
        "кошка любит молоко",
        "пес ест мясо",
        "собака ест мясо",
        "щенок любит кость",
        "кофе это напиток",
        "чай это напиток",
        "кофе с молоком",
        "чай с лимоном",
        "король это мужчина",
        "королева это женщина",
        "принц это мужчина",
        "принцесса это женщина",
    ]
    texts = [    "кот пьет молоко",    "котенок пьет молоко",    "кошка любит молоко",    "пес ест мясо",    "собака ест мясо",    "щенок любит кость",    "кофе это напиток",    "чай это напиток",    "кофе с молоком",    "чай с лимоном",    "король это мужчина",    "королева это женщина",    "принц это мужчина",    "принцесса это женщина",]

    Обратите внимание: в этих фразах мы используем только слова, разделенные пробелами — без тире, запятых и точек. Это важно, потому что токенизация здесь самая простая: строка разбивается на токены командой split() по пробелам. Если поставить запятую или точку, знак препинания «прилипнет» к слову, и модель будет считать молоко и молоко, двумя разными словами.

    Скопируйте этот список в отдельную ячейку Google Colab и запустите ее — она создаст переменную texts, которая нам понадобится в следующих шагах.

    Работа с эмбеддингами в Colab
    Источник: автор статьи 

    Шаг 2. Теперь обучим простую модель на нашем наборе фраз. Код ниже возьмет список texts, разобьет фразы на слова, составит пары «слово → соседние слова» и несколько сотен раз обновит веса модели, чтобы она лучше угадывала контекст. Скопируйте этот блок в следующую ячейку и запустите ее.

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import random
    
    torch.manual_seed(0)
    random.seed(0)
    
    tokens = [t.split() for t in texts]
    vocab = sorted(set(w for sent in tokens for w in sent))
    w2i = {w: i for i, w in enumerate(vocab)}
    i2w = {i: w for w, i in w2i.items()}
    
    window = 2
    pairs = []
    for sent in tokens:
        ids = [w2i[w] for w in sent]
        for pos, center in enumerate(ids):
            for j in range(max(0, pos - window), min(len(ids), pos + window + 1)):
                if j == pos:
                    continue
                pairs.append((center, ids[j]))
    
    random.shuffle(pairs)
    
    V = len(vocab)
    D = 16
    
    class SkipGram(nn.Module):
        def __init__(self, V, D):
            super().__init__()
            self.in_embed = nn.Embedding(V, D)
            self.out_embed = nn.Embedding(V, D)
    
        def forward(self, center_ids):
            x = self.in_embed(center_ids)
            logits = x @ self.out_embed.weight.T
            return logits
    
    model = SkipGram(V, D)
    opt = torch.optim.Adam(model.parameters(), lr=0.05)
    
    def neighbors(word, topn=6):
        if word not in w2i:
            return []
        W = model.in_embed.weight.detach()
        v = W[w2i[word]]
        sims = F.cosine_similarity(v[None, :], W, dim=1)
        best = torch.topk(sims, k=min(topn + 1, V)).indices.tolist()
        best_words = [i2w[i] for i in best if i2w[i] != word]
        return best_words[:topn]
    
    probe_words = ["кот", "собака", "кофе", "чай", "король", "королева"]
    
    print("=== До обучения (почти случайно) ===")
    for w in probe_words:
        print(f"{w:9s} -> {neighbors(w)}")
    
    batch_size = 64
    steps = 900
    
    for step in range(steps):
        batch = pairs[(step * batch_size):((step + 1) * batch_size)]
        if not batch:
            batch = random.sample(pairs, k=min(batch_size, len(pairs)))
    
        c = torch.tensor([p[0] for p in batch])
        y = torch.tensor([p[1] for p in batch])
    
        logits = model(c)
        loss = F.cross_entropy(logits, y)
    
        opt.zero_grad()
        loss.backward()
        opt.step()
    
    print("\n=== После обучения (появляется смысл) ===")
    for w in probe_words:
        print(f"{w:9s} -> {neighbors(w)}")
    

    После выполнения кода мы получили такой вывод:

    === До обучения (почти случайно) ===
    кот       -> ['кошка', 'принц', 'это', 'с', 'пьет', 'лимоном']
    собака    -> ['щенок', 'молоком', 'лимоном', 'чай', 'мясо', 'пьет']
    кофе      -> ['напиток', 'ест', 'принц', 'женщина', 'молоко', 'кость']
    чай       -> ['кость', 'щенок', 'молоком', 'собака', 'это', 'мужчина']
    король    -> ['с', 'принцесса', 'мясо', 'пьет', 'молоком', 'напиток']
    королева  -> ['женщина', 'молоко', 'пес', 'щенок', 'мясо', 'это']
    
    === После обучения (появляется смысл) ===
    кот       -> ['котенок', 'кошка', 'любит', 'молоком', 'пьет', 'собака']
    собака    -> ['пес', 'кошка', 'мясо', 'кот', 'ест', 'лимоном']
    кофе      -> ['чай', 'принц', 'напиток', 'женщина', 'молоком', 'король']
    чай       -> ['кофе', 'принцесса', 'король', 'королева', 'принц', 'женщина']
    король    -> ['принц', 'принцесса', 'напиток', 'чай', 'мужчина', 'женщина']
    королева  -> ['принцесса', 'мужчина', 'ест', 'чай', 'напиток', 'король']
    

    После обучения в списке «ближайших слов» могут попадаться неожиданные соседи — это нормально. До обучения эмбеддинги случайные, а после обучения модель сближает слова, которые встречаются в похожих контекстах, поэтому она группирует их «по ситуации», а не по строгим словарным определениям.

    На результат также влияет то, что мы используем небольшой набор фраз: на таком мини‑корпусе модель легко подхватывает случайные закономерности и иногда «ошибается» в соседях. Главное, что в целом эффект сохраняется — после обучения списки становятся заметно более осмысленными, чем до него.

    Шаг 3. Теперь визуализируем получившиеся эмбеддинги. На предыдущем шаге у каждого слова появился многомерный вектор, и сравнивать такие векторы напрямую неудобно. Поэтому код ниже проецирует их в две координаты и строит график: каждое слово — одна точка. На нем будет видно, что слова из похожих контекстов оказываются рядом и образуют группы.

    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.manifold import TSNE
    
    W = model.in_embed.weight.detach().cpu().numpy()
    
    coords = TSNE(
        n_components=2,
        perplexity=5,
        init="random",
        learning_rate="auto",
        random_state=0
    ).fit_transform(W)
    
    plt.figure(figsize=(12, 8))
    for i, w in i2w.items():
        x, y = coords[i]
        plt.scatter(x, y, s=60)
        plt.text(x + 0.02, y + 0.02, w, fontsize=12)
    
    plt.title("Эмбеддинги после обучения")
    plt.grid(True, linestyle="--", alpha=0.35)
    plt.show()
    

    В результате на графике видно, что слова начинают собираться в группы: 

    Эмбеддинги после обучения
    Источник: автор статьи 

    Если вы повторили все шаги, то увидели: эмбеддинги не задаются модели заранее — они формируются в процессе обучения. Сначала векторы случайные, поэтому все выглядит как шум. Однако чем дольше модель тренируется угадывать слова по контексту, тем заметнее она сближает слова из похожих ситуаций — и на графике начинают проявляться осмысленные группы.

    Полезные статьи по теме

    Разбор

    Поделиться

    Скопировано
    0 комментариев
    Комментарии