Инженерный блог

BigQuery ML против маркетплейсов: Как собрать свою рекомендательную систему для e-commerce в Казахстане за выходные

09.08.2026
Шарафутдинов Р.

Суровая реальность: Выживание вне маркетплейсов

Давайте начистоту: делать классический e-commerce в Казахстане в 2026 году — это боль. С одной стороны вас давит желтый гигант с его рассрочками и бесплатной доставкой, с другой — фиолетовый монстр с миллионами ПВЗ. Если вы всё ещё пытаетесь конкурировать с ними в лоб, просто продавая те же чехлы для айфонов или утюги, у меня для вас плохие новости.

Ваш единственный шанс выжить — это нишевость, сервис и пользовательский опыт (UX). Но какой может быть UX, когда пользователь заходит на ваш сайт, видит простыню товаров из категории «Вам также может понравиться», где к зимним шинам предлагают купить летние кроссовки? Это фиаско, братан.

«Ну, нам нужна нейронка!» — скажет ваш продакт-менеджер. «Давайте наймем команду дата-саентистов, поднимем кластер Kubernetes, прикрутим Apache Spark, Kafka для стриминга и будем обучать графовые нейросети на GPU!» — радостно подхватит техлид, потирая руки в предвкушении освоения бюджета.

Спойлер: вы потратите полгода, $50k, а на выходе получите костыль, который будет падать при каждом деплое. А можно сделать иначе. Можно взять BigQuery ML, пару банок энергетика, выходные — и выкатить в прод рабочую рекомендательную систему, которая уделает 90% кастомных решений.

Что такое BigQuery ML и почему это чит-код?

BigQuery — это не просто колоночная база данных для аналитики, куда вы сливаете сырые логи из GA4. Это вычислительный монстр. А BigQuery ML (BQML) — это фича, которая позволяет обучать и выполнять модели машинного обучения прямо внутри хранилища данных, используя... обычный SQL!

Вам не нужно выгружать данные в Python, гонять их через Pandas, поднимать Jupyter Notebooks, страдать с зависимостями и деплоить Pickle-файлы на сервера. Данные остаются в BigQuery. Вычисления происходят в BigQuery. Вы просто пишете CREATE MODEL — и гугловская инфраструктура делает всю грязную работу за вас.

Задача: Рекомендация товаров на основе матричной факторизации (Collaborative Filtering)

Для e-commerce золотой стандарт стартовых рекомендаций — это коллаборативная фильтрация. Идея проста: если Арман и Серик купили одинаковые кроссовки и футболку, а Арман еще купил кепку, то Серику тоже стоит предложить эту кепку. Модель находит скрытые связи между пользователями и товарами на основе их истории взаимодействий (просмотры, добавления в корзину, покупки).

В BQML для этого используется алгоритм матричной факторизации (Matrix Factorization). Он берет матрицу «Пользователь — Товар — Оценка взаимодействия» и раскладывает ее на векторы (эмбеддинги). Звучит как матан, но для нас это просто пара строк кода.

Шаг 1: Подготовка данных (Фича-инжиниринг на коленке)

Главное правило ML: Garbage in, garbage out. Если скормить модели мусор, она выдаст мусор. Нам нужно собрать историю взаимодействий пользователей с товарами. Допустим, у нас есть сырые события из GA4 (в таблице events_*).

Мы не можем просто взять покупки — их слишком мало (разреженность данных). Нам нужны неявные сигналы (implicit feedback). Присвоим веса действиям:

  • Просмотр товара (view_item) = 1 балл
  • Добавление в корзину (add_to_cart) = 3 балла
  • Покупка (purchase) = 10 баллов
-- Создаем витрину данных для обучения
CREATE OR REPLACE TABLE `ecommerce_ml.training_data` AS
WITH user_item_interactions AS (
  SELECT
    user_pseudo_id AS user_id,
    (SELECT value.string_value FROM UNNEST(items) LIMIT 1) AS item_id,
    event_name
  FROM
    `your-project.analytics_12345.events_*`
  WHERE
    event_name IN ('view_item', 'add_to_cart', 'purchase')
    AND _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)) AND FORMAT_DATE('%Y%m%d', CURRENT_DATE())
)
SELECT
  user_id,
  item_id,
  SUM(
    CASE 
      WHEN event_name = 'view_item' THEN 1
      WHEN event_name = 'add_to_cart' THEN 3
      WHEN event_name = 'purchase' THEN 10
      ELSE 0
    END
  ) AS interaction_score
FROM
  user_item_interactions
WHERE item_id IS NOT NULL
GROUP BY
  user_id, item_id
HAVING interaction_score >= 1;

Вот и весь дата-пайплайн. Никакого Airflow, Spark и долгих выгрузок. Данные уже в BQ.

Шаг 2: Обучение модели (Магия SQL)

Теперь самое сладкое. Учим нейронку (ну ладно, факторизацию матриц). Засекайте время:

-- Обучение модели матричной факторизации
CREATE OR REPLACE MODEL `ecommerce_ml.item_recommender`
OPTIONS(
  model_type='matrix_factorization',
  user_col='user_id',
  item_col='item_id',
  rating_col='interaction_score',
  feedback_type='implicit', -- У нас неявные оценки (не 5 звезд, а клики)
  l2_reg=0.1,               -- Регуляризация, чтобы не переобучиться на ботах
  num_factors=20            -- Размерность скрытых векторов (эмбеддингов)
) AS
SELECT
  user_id,
  item_id,
  interaction_score
FROM
  `ecommerce_ml.training_data`;

Нажимаем Run. Уходим пить кофе. Через 10-15 минут модель готова. Google под капотом распараллелил вычисления на тысячах серверов, оптимизировал градиентный спуск и сохранил веса. Вы только что сэкономили месяц жизни дата-саентиста.

Шаг 3: Оценка качества (Метрики без булшита)

Модель обучилась, но как понять, что она не советует дичь? В BQML есть встроенная функция для эвалюации (оценки). Для неявного фидбека мы смотрим на метрику Mean Average Precision (MAP). Она показывает, насколько релевантными были товары в топе рекомендаций.

-- Оценка модели
SELECT * FROM ML.EVALUATE(MODEL `ecommerce_ml.item_recommender`);

Если MAP выше 0.05 — для старта это уже пушка. Если меньше — надо крутить гиперпараметры (поиграться с l2_reg, добавить больше данных, фильтровать ботов-парсеров).

Шаг 4: Генерация предсказаний (Предикты в прод)

Теперь нам нужно получить топ-5 рекомендованных товаров для каждого пользователя. Делаем это одним запросом:

-- Получаем рекомендации
CREATE OR REPLACE TABLE `ecommerce_ml.user_recommendations` AS
SELECT
  user_id,
  ARRAY_AGG(STRUCT(item_id, predicted_interaction_score) ORDER BY predicted_interaction_score DESC LIMIT 5) AS recommended_items
FROM
  ML.RECOMMEND(MODEL `ecommerce_ml.item_recommender`)
GROUP BY
  user_id;

У нас есть готовая таблица: user_id и массив из 5 лучших item_id для него.

Шаг 5: Архитектура отдачи (Highload для бедных, но умных)

Остался последний рывок. Как отдать эти рекомендации на фронтенд? Дергать BigQuery каждый раз, когда юзер заходит на главную страницу — это самоубийство бюджета (BQ берет деньги за просканированные терабайты) и дикие задержки (latency ~1-2 секунды, что для фронта смерть).

Поэтому мы делаем элегантно:

  1. Cloud Scheduler + Cloud Run (Job): Раз в сутки ночью запускаем скрипт, который выполняет SQL-запросы на переобучение модели и генерацию предиктов.
  2. Экспорт в Redis (или Cloud SQL): Скрипт забирает готовую таблицу user_recommendations из BigQuery и пушит ее в in-memory базу (например, Redis Memorystore) или быстрый Cloud SQL (PostgreSQL).
  3. Бэкенд (Node.js/Go): Фронтенд делает легкий GET-запрос /api/recommendations?user_id=123. Бэкенд мгновенно (за 5 мс) забирает массив айдишников из Redis, джоинит с каталогом товаров (названия, цены, картинки) и отдает на фронт.

Сравнение затрат на разработку ML (Кастом против BQML)

Накопительные расходы ($) на инфраструктуру, зарплаты и поддержку в течение первого года.

Но постойте, а как же «Холодный старт»?

Любой душнила с Хабра спросит: «А что делать с новыми пользователями, у которых нет истории? Ваша матричная факторизация сломается!». Да, сломается. Для новых юзеров (холодный старт) мы не можем дать персонализированные рекомендации.

Поэтому в бэкенде мы пишем простой фолбэк (fallback):

// Псевдокод бэкенда на Node.js
async function getRecommendations(userId) {
  // Пытаемся найти перс. рекомендации в Redis
  let recs = await redis.get(`recs:${userId}`);
  
  if (!recs) {
    // Если юзер новый или инкогнито — отдаем глобальные хиты продаж
    // Эту таблицу мы тоже заранее считаем в BQ и кладем в Redis!
    recs = await redis.get('recs:global_top_sellers');
  }
  
  return expandItemsFromDatabase(recs);
}

Всё. Никакой магии. Инженерия — это искусство находить простые костыли, которые работают в 99% случаев.

Цена вопроса: Сколько стоит этот киберпанк?

Давайте считать деньги. Мы же в Казахстане, тут за каждый тенге спросят.

  • Хранение данных в BQ: Копейки (первые 10 ГБ бесплатно, дальше $0.02 за ГБ).
  • Обучение модели (BQML): Вы платите за объем данных, обработанных при обучении. Если ваша витрина весит 1 ГБ, то обучение будет стоить примерно... бесплатно (в рамках бесплатного терабайта квоты BQ) или пару центов.
  • Экспорт в Redis: Мелкий инстанс Redis (или бесплатный кластер от Upstash) обойдется от $0 до $15 в месяц.

Итого: Меньше $20 в месяц за полноценную рекомендательную систему, которая повысит конверсию на 5-15%.

Доля покупок по источнику рекомендаций (Ожидание)

Как распределяются покупки после внедрения BQML Collaborative Filtering.

Итог: Не усложняйте

Разработка — это не про то, чтобы использовать самые модные и сложные технологии. Это про то, как быстро доставить ценность бизнесу. Пока конкуренты пишут ТЗ на микросервисную архитектуру машинного обучения, вы за одни выходные собираете пайплайн на SQL, который уже в понедельник начинает приносить деньги.

BigQuery ML — это ультимативный швейцарский нож для data-инженеров и фулстеков. Да, он не выиграет Kaggle-соревнования. Да, он не умеет в сложные графовые архитектуры. Но для 95% бизнес-задач e-commerce (рекомендации, LTV, отток) его хватает с головой.

Хватит кормить маркетплейсы. Стройте свой UX. И да пребудет с вами высокий LTV и дешевый CAC!

Рустам Шарафутдинов

Рустам Шарафутдинов

Автор инженерного блога

Эксперт в области архитектуры Google Cloud и Senior Full-Stack разработчик с более чем 15-летним опытом. Специализируется на отказоустойчивых архитектурах, оптимизации высоконагруженных проектов и интеграции AI (Vertex AI).

Экспертность: GCP, Kubernetes, Микросервисы, React, Node.js

Комментарии (0)