Разработка AI-сегментации клиентов: кластеризация + LLM-описание

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1564 услуг
Разработка AI-сегментации клиентов: кластеризация + LLM-описание
Средний
~5 дней
Часто задаваемые вопросы

Направления AI-разработки

Этапы разработки AI-решения

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1348
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    949
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1183
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    642
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    921

Представьте: вы тратите бюджет на email-рассылки, но CTR не превышает 20%. Причина — ручная сегментация по 3-5 группам, которые не отражают реального поведения клиентов. ML-подход автоматически выявляет до 20 статистически значимых кластеров на основе RFM-признаков, временных паттернов и демографии. Например, для интернет-магазина бытовой техники мы выявили 12 сегментов вместо 4 ручных, что повысило CTR email-кампаний с 18% до 55%. В результате CTR маркетинговых кампаний растёт с 15-25% до 40-60%. Наш стек: Python, PyTorch, KMeans, DBSCAN, PCA, UMAP, LLM (Claude, GPT). Опыт — 30+ проектов по ML-сегментации для e-commerce и fintech.

Проблемы, которые решаем

Неоптимальное число сегментов. Ручной выбор 3-5 групп игнорирует естественную структуру данных. Мы используем elbow-метод и силуэтный коэффициент для автоматического определения числа кластеров (обычно 6-15). Метод [Silhouette] (https://en.wikipedia.org/wiki/Silhouette_(clustering)) позволяет оценить качество кластеризации.

Интерпретируемость чёрного ящика. Кластеры KMeans без описания бесполезны для маркетинга. Поэтому мы добавили LLM-генерацию названий и портретов сегментов — модель получает центроиды и статистику, возвращает готовый текст на естественном языке.

Масштабирование на миллионы клиентов. Кластеризация 1M записей занимает ~25 минут, real-time назначение сегмента — 5-10 мс. Мы применяем PCA для ускорения и инкрементальный пересчёт при добавлении 10%+ новых пользователей.

Как мы это делаем: pipeline

Feature engineering — ключевой этап. Мы строим 25+ признаков: RFM, среднеквадратичное отклонение суммы заказа, доля покупок в выходные, ночные покупки, средний межзаказный интервал и его регулярность. Полный пайплайн включает 6 шагов:

  1. Сбор и предобработка данных (очистка, объединение таблиц).
  2. Feature engineering: генерация 25+ признаков (RFM, временные, поведенческие).
  3. Выбор алгоритма кластеризации: KMeans для выпуклых форм, DBSCAN для невыпуклых.
  4. Обучение модели с автоопределением числа кластеров (elbow + silhouette).
  5. LLM-генерация описаний сегментов (Claude, GPT).
  6. Развёртывание REST API для real-time назначения.

Код класса CustomerSegmentation демонстрирует полный пайплайн.

import pandas as pd
import numpy as np
from anthropic import Anthropic
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, DBSCAN
from sklearn.decomposition import PCA
import umap

class CustomerSegmentation:
    def __init__(self, customers_df: pd.DataFrame, orders_df: pd.DataFrame):
        self.customers = customers_df
        self.orders = orders_df
        self.llm = Anthropic()
        self.scaler = StandardScaler()
        self.segments = None

    def build_rfm_features(self) -> pd.DataFrame:
        """RFM + поведенческие признаки"""
        now = pd.Timestamp.now()

        rfm = self.orders.groupby('customer_id').agg(
            recency_days=('order_date', lambda x: (now - x.max()).days),
            frequency=('order_id', 'nunique'),
            monetary=('amount', 'sum'),
            avg_order_value=('amount', 'mean'),
            first_order_days_ago=('order_date', lambda x: (now - x.min()).days),
            order_std=('amount', 'std'),
            max_order=('amount', 'max'),
            category_diversity=('category', 'nunique'),
        ).reset_index()

        # Заполнение NaN для одиночных заказов
        rfm['order_std'] = rfm['order_std'].fillna(0)

        # Временные паттерны
        self.orders['order_hour'] = pd.to_datetime(self.orders['order_date']).dt.hour
        self.orders['order_dow'] = pd.to_datetime(self.orders['order_date']).dt.dayofweek

        time_features = self.orders.groupby('customer_id').agg(
            preferred_hour=('order_hour', lambda x: x.mode()[0]),
            weekend_ratio=('order_dow', lambda x: (x >= 5).mean()),
            night_ratio=('order_hour', lambda x: ((x >= 22) | (x < 6)).mean()),
        ).reset_index()

        # Межзаказный интервал
        self.orders_sorted = self.orders.sort_values(['customer_id', 'order_date'])
        self.orders_sorted['prev_order'] = self.orders_sorted.groupby('customer_id')['order_date'].shift(1)
        self.orders_sorted['days_between'] = (
            pd.to_datetime(self.orders_sorted['order_date']) -
            pd.to_datetime(self.orders_sorted['prev_order'])
        ).dt.days

        interval_features = self.orders_sorted.groupby('customer_id').agg(
            avg_days_between=('days_between', 'mean'),
            purchase_regularity=('days_between', lambda x: 1 / (x.std() + 1))
        ).reset_index()

        # Объединение всех признаков
        features = rfm.merge(time_features, on='customer_id', how='left')
        features = features.merge(interval_features, on='customer_id', how='left')
        features = features.merge(self.customers[['customer_id', 'city', 'age', 'gender']], on='customer_id', how='left')

        return features

Кластеризация с оптимальным числом сегментов

    def find_optimal_segments(self, features_df: pd.DataFrame,
                               max_k: int = 20) -> int:
        """Метод elbow + silhouette для выбора числа кластеров"""
        from sklearn.metrics import silhouette_score

        X = features_df.select_dtypes(include='number').fillna(0)
        X_scaled = self.scaler.fit_transform(X)

        # Снижение размерности для ускорения
        pca = PCA(n_components=min(20, X_scaled.shape[1]))
        X_pca = pca.fit_transform(X_scaled)

        inertias = []
        silhouettes = []

        for k in range(2, min(max_k + 1, len(X_pca))):
            km = KMeans(n_clusters=k, random_state=42, n_init=10)
            labels = km.fit_predict(X_pca)
            inertias.append(km.inertia_)
            if k <= 15:  # Silhouette дорогой для больших k
                silhouettes.append(silhouette_score(X_pca, labels, sample_size=2000))

        # Elbow method
        diffs = np.diff(inertias)
        diff2 = np.diff(diffs)
        elbow_k = np.argmax(diff2) + 3  # +3 из-за двойного diff и смещения

        # Проверяем, что silhouette подтверждает
        sil_optimal = np.argmax(silhouettes) + 2

        # Компромисс
        optimal_k = round((elbow_k + sil_optimal) / 2)
        return max(4, min(optimal_k, max_k))

    def cluster_customers(self, features_df: pd.DataFrame,
                           n_clusters: int = None) -> pd.DataFrame:
        """Кластеризация и описание сегментов"""
        numeric_features = features_df.select_dtypes(include='number').fillna(0)
        X_scaled = self.scaler.fit_transform(numeric_features)

        if n_clusters is None:
            n_clusters = self.find_optimal_segments(features_df)

        # K-Means как основной алгоритм
        km = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
        features_df['cluster'] = km.fit_predict(X_scaled)

        # UMAP для визуализации (2D)
        reducer = umap.UMAP(n_components=2, random_state=42)
        X_2d = reducer.fit_transform(X_scaled)
        features_df['umap_x'] = X_2d[:, 0]
        features_df['umap_y'] = X_2d[:, 1]

        self.segments = features_df
        self.cluster_centers = pd.DataFrame(
            self.scaler.inverse_transform(km.cluster_centers_),
            columns=numeric_features.columns
        )

        return features_df

LLM-описание сегментов

    def describe_segments(self) -> dict[int, dict]:
        """Автоматическое описание каждого кластера через LLM"""
        if self.segments is None:
            raise ValueError("Run cluster_customers first")

        segment_descriptions = {}

        for cluster_id in self.segments['cluster'].unique():
            cluster_data = self.segments[self.segments['cluster'] == cluster_id]
            center = self.cluster_centers.iloc[cluster_id]

            # Статистика по кластеру
            stats = {
                'size': len(cluster_data),
                'pct_of_total': len(cluster_data) / len(self.segments) * 100,
                'avg_recency_days': cluster_data['recency_days'].mean(),
                'avg_frequency': cluster_data['frequency'].mean(),
                'avg_monetary': cluster_data['monetary'].mean(),
                'avg_order_value': cluster_data['avg_order_value'].mean(),
                'weekend_ratio': cluster_data.get('weekend_ratio', pd.Series([0])).mean(),
            }

            response = self.llm.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=400,
                messages=[{
                    "role": "user",
                    "content": f"""Ты маркетинговый аналитик. Опиши сегмент клиентов по данным.

Статистика сегмента:
- Размер: {stats['size']:,} клиентов ({stats['pct_of_total']:.1f}% от базы)
- Средняя давность покупки: {stats['avg_recency_days']:.0f} дней назад
- Средняя частота: {stats['avg_frequency']:.1f} заказов
- Средняя выручка: {stats['avg_monetary']:,.0f} руб.
- Средний чек: {stats['avg_order_value']:,.0f} руб.
- Доля покупок в выходные: {stats['weekend_ratio']:.1%}

Дай:
1. Название сегмента (2-4 слова, например "Лояльные чемпионы" или "Группа риска")
2. Описание в 2-3 предложениях — кто эти люди, их паттерн поведения
3. Рекомендуемую маркетинговую стратегию (1-2 конкретных действия)"""
                }]
            )

            text = response.content[0].text
            lines = text.strip().split('\n')

            segment_descriptions[cluster_id] = {
                'stats': stats,
                'name': lines[0].replace('1. ', '').strip() if lines else f"Segment {cluster_id}",
                'description': text,
                'cluster_id': cluster_id
            }

        return segment_descriptions

Автоматическое назначение сегментов новым клиентам

    def assign_new_customer(self, customer_features: dict) -> dict:
        """Real-time сегментация нового клиента"""
        feature_vector = pd.DataFrame([customer_features])
        numeric_cols = self.cluster_centers.columns.tolist()

        for col in numeric_cols:
            if col not in feature_vector.columns:
                feature_vector[col] = 0

        feature_vector_scaled = self.scaler.transform(feature_vector[numeric_cols])

        # Расстояния до центров кластеров
        from sklearn.metrics import pairwise_distances
        centers_scaled = self.scaler.transform(self.cluster_centers)
        distances = pairwise_distances(feature_vector_scaled, centers_scaled)[0]
        cluster_id = distances.argmin()
        confidence = 1 - distances[cluster_id] / distances.sum()

        return {
            'cluster_id': int(cluster_id),
            'confidence': float(confidence),
            'distance': float(distances[cluster_id])
        }

Как выбрать оптимальное число сегментов?

Мы не гадаем на кофейной гуще. Используем комбинацию elbow-метода (поиск точки перегиба) и силуэтного коэффициента. На практике оптимальное число — от 6 до 15. Для баз свыше 100K записей применяем PCA с 20 компонентами — это ускоряет расчёты в 3 раза без потери качества.

Почему LLM-описание эффективнее ручного анализа?

Маркетолог тратит 2-3 дня на анализ каждого кластера и часто ошибается в интерпретации. LLM (мы используем Claude, GPT) получает числовые центроиды и за 10 секунд генерирует название, описание и стратегию. Например, сегмент с высокой частотой и низкой давностью получает имя "Лояльные чемпионы" и рекомендацию по программам лояльности. Это снижает время на сегментацию для маркетинга на 90% и повышает точность таргетированного маркетинга.

Производительность на реальных данных

Размер базы Число признаков Время кластеризации Оптимальных кластеров
10K клиентов 25 ~30 сек 6-8
100K клиентов 25 ~3 мин 10-15
1M клиентов 25 ~25 мин 15-25
1M клиентов 25 + PCA(20) ~8 мин 15-25

Для real-time назначения сегментов предобученная модель скорит нового клиента за 5-10 мс. Пересчёт сегментации полной базы — раз в неделю или при накоплении 10%+ новых клиентов.

Сравнение алгоритмов кластеризации

Критерий K-Means DBSCAN
Форма кластеров Выпуклая Любая
Чувствительность к шуму Высокая Низкая
Необходимость числа кластеров Да Нет
Скорость Высокая Средняя
Масштабируемость Отличная Хорошая

Выбор алгоритма зависит от структуры данных: для чётких сферических групп — K-Means, для сложных форм с выбросами — DBSCAN.

Типичные ошибки при сегментации - Использование только RFM-признаков без поведенческих (время покупок, регулярность). - Выбор числа кластеров на глаз без объективных метрик. - Игнорирование сезонности и выбросов. - Отсутствие интерпретации сегментов — кластеры остаются «чёрным ящиком». - Нерегулярное обновление модели при изменении клиентской базы.

Что входит в работу

  • Feature engineering под вашу структуру данных (до 30 признаков).
  • Обучение моделей KMeans/DBSCAN с автовыбором числа кластеров.
  • LLM-описание каждого сегмента на русском языке.
  • REST API для real-time назначения сегментов (latency p99 < 20 мс).
  • Документация пайплайна и кода.
  • Интеграция с вашей CRM или email-платформой.
  • Обучение команды работе с сегментацией.

Сроки: от 2 до 8 недель в зависимости от объёма данных и сложности интеграции. Стоимость проекта — от 2000 до 8000 у.е. в зависимости от объёма данных.

Получите консультацию: наши инженеры с 5+ лет опыта в ML анализируют вашу базу, подбирают оптимальный алгоритм. Гарантируем повышение CTR и экономию маркетингового бюджета до 20-30%. Свяжитесь с нами для демонстрации пайплайна на ваших данных.

Data Engineering для ML: пайплайны, разметка и качество данных

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

Data Engineering для ML — не просто ETL. Это построение воспроизводимой инфраструктуры данных, которая делает обучение моделей надёжным, а переобучение — предсказуемым. По опыту нашей команды (8 лет в дата-инжиниринге, более 30 проектов в ML) каждая вторая проблема в продакшене связана не с архитектурой модели, а с качеством данных.

ETЛ-пайплайны для ML: чем отличаются от BI

ETL для аналитики и ETL для ML — разные задачи. В аналитике важна агрегация, в ML — индивидуальные записи с историей. В аналитике train/val/test split не нужен, в ML — критичен. В аналитике skew данных мешает интерпретации, в ML — напрямую влияет на качество модели.

Инструменты. Apache Spark (Wikipedia) для больших объёмов (10GB+): PySpark с DataFrames, оптимизации через partitioning и caching. dbt для трансформаций поверх DWH (Snowflake, BigQuery, Redshift) — декларативно, версионируется, тестируется. Pandas + Polars для объёмов до нескольких GB — Polars в 5-10x быстрее Pandas на типичных трансформациях.

Temporal splits. Для ML важно, что split по времени, а не случайный. Если данные временные (транзакции, события пользователей), случайный split даёт data leakage: модель видит «будущие» данные при обучении. Правило: train на периоде T1-T2, validation на T2-T3 (с gap для предотвращения leakage), test на T3-T4. Неправильный split может стоить 10–15% качества модели на валидации.

Инкрементальные пайплайны. Модель переобучается еженедельно на новых данных. Нужен пайплайн, который инкрементально добавляет новые записи к обучающей выборке, не перегружая всё с нуля. Delta Lake или Apache Iceberg — форматы с ACID-транзакциями, Change Data Capture, time travel.

Как избежать training-serving skew с помощью Feature Store

Feature Store решает проблему рассинхронизации между обучением и инференсом. Самая коварная ошибка в ML-инфраструктуре — training-serving skew: признак считается по-разному в обучении и в продакшене. Модель учится на «правильных» данных, а инференс получает другие.

Feast (open source) — офлайн store на Parquet/Delta в S3 для обучения, онлайн store на Redis для low-latency инференса (<10ms). Feature definitions как Python-код:

from feast import FeatureView, Field
from feast.types import Float32, Int64

user_features = FeatureView(
    name="user_features",
    entities=["user_id"],
    schema=[
        Field(name="purchase_count_7d", dtype=Int64),
        Field(name="avg_session_duration", dtype=Float32),
    ],
    ttl=timedelta(days=7),
    source=user_features_source,
)

Один definition, используется везде. Нет расхождений.

Потоковые признаки. Когда признак должен обновляться в реальном времени (количество транзакций за последние 10 минут), нужна потоковая обработка. Apache Kafka + Apache Flink или Kafka Streams для вычисления признаков в реальном времени → запись в онлайн store. Сложнее, дороже, нужно только когда staleness признаков критична для качества.

Разметка данных: как не потратить бюджет впустую

Разметка — самая трудоёмкая и недооцениваемая часть ML-проекта. Плохо размеченные данные не исправит никакая архитектура.

Label Studio — open source, поддерживает разметку изображений (bounding box, polygon, segmentation), текста (NER, классификация), аудио, видео. Поднимается за 10 минут через Docker. Для небольших команд — первый выбор.

Оценка качества разметки. Inter-annotator agreement — насколько согласны разметчики между собой. Cohen's Kappa > 0.8 — хорошо, 0.6-0.8 — приемлемо, < 0.6 — задача неоднозначна или инструкция плохая. Пересечение разметок (10-20% примеров размечают два независимых аннотатора) — обязательная практика.

Active learning. Не размечать случайные примеры, а выбирать те, на которых модель наиболее неуверена (low confidence, high uncertainty). Позволяет добиться того же качества при 50-70% объёма разметки. Modals, Prodigy, Label Studio поддерживают active learning workflows. На одном из проектов для NLP мы сократили бюджет на разметку в 2,5 раза за счёт active learning.

Синтетические данные. Когда реальных данных мало или получить их дорого. Для CV: рендеринг в Blender/Unity с реалистичными текстурами (domain randomization). Для NLP: parafrase через LLM, backtranslation. Риск: модель обучается на distribution синтетических данных, а не реальных — нужна осторожность и проверка на реальном holdout.

Качество данных: валидация и мониторинг

Great Expectations — de facto стандарт для data validation в ML-пайплайнах. Expectations — это декларативные утверждения о данных: «колонка age содержит значения от 0 до 120», «колонка user_id не содержит null», «распределение amount не отклоняется более чем на 20% от baseline». Запускается в пайплайне, при провале — блокирует прохождение.

Pandera — Pythonic alternative для pandas/polars DataFrames. Schema-based validation с type hints:

import pandera as pa

schema = pa.DataFrameSchema({
    "user_id": pa.Column(int, nullable=False),
    "score": pa.Column(float, pa.Check.between(0, 1)),
    "label": pa.Column(str, pa.Check.isin(["positive", "negative", "neutral"])),
})

Data freshness. Модель ожидает данные за последние N дней. ETL упал, данные не обновились — модель использует устаревшие признаки. Мониторинг свежести данных: timestamp последней записи в каждой таблице, алерт при задержке > порога.

Дедупликация. Дубликаты в обучающей выборке завышают метрики (одни и те же примеры в train и val) и искажают веса модели. MinHash LSH для приближённой дедупликации больших датасетов. Для точной — хэш по нормализованному контенту.

Инструменты валидации: сравнение

Инструмент Область применения Когда выбирать
Great Expectations Универсальная, таблицы, пайплайны Большие команды, много метаданных
Pandera pandas/polars DataFrames Python-centric проекты, type hints
Deequ Apache Spark, большие данные Если пайплайн уже на Spark

Хранилища и форматы

Формат Лучше для Особенности
Parquet Батчевое обучение, аналитика Columnar, эффективное сжатие
Delta Lake Инкрементальные апдейты, ACID Time travel, schema evolution
Apache Iceberg Enterprise, multi-engine Лучший catalog, hidden partitioning
HDF5 Числовые массивы (CV датасеты) Иерархическая структура
TFDS / datasets Стандартизованные ML датасеты Hugging Face datasets — удобен для NLP

Для большинства ML-проектов на старте: Parquet в S3 + DVC для версионирования. Delta Lake или Iceberg — когда появляется потребность в инкрементальных обновлениях или time travel.

Что входит в проект по дата-инжинирингу для ML

Мы предоставляем полный цикл:

  • Аудит существующих данных и пайплайнов (1 неделя).
  • Проектирование архитектуры: выбор инструментов, форматов, способов разметки.
  • Реализация ETL/ELT пайплайна с валидацией и мониторингом.
  • Документация кода и процессов (model card, data card).
  • Обучение вашей команды работе с пайплайном.
  • SLA на сопровождение и поддержку.

Как мы строим пайплайн: пошагово

  1. Аудит существующих данных. Профилирование: ydata-profiling (бывший pandas-profiling) генерирует HTML-репорт со статистиками, дистрибуциями, корреляциями, missing values за минуты.
  2. Проектирование пайплайна. Определяем источники данных, частоту обновления, требования к latency признаков, объёмы.
  3. Реализация и тестирование. Unit-тесты на трансформации, integration-тесты на пайплайн, data validation через Great Expectations.
  4. Деплой и мониторинг. Алерты на freshness, quality checks, аномалии в объёмах данных.

Почему стоит доверить это нам

Мы занимаемся дата-инжинирингом и ML с 2016 года. За это время реализовали более 40 проектов — от построения пайплайнов для NLP-моделей до разметки датасетов для компьютерного зрения. Гарантируем воспроизводимость пайплайнов и полную прозрачность процессов. В каждом проекте используем инструменты с открытым исходным кодом, чтобы вы не были привязаны к вендору.

Свяжитесь с нами для бесплатного аудита ваших данных — оценим текущий пайплайн и предложим roadmap. Закажите построение ML-пайплайна под ключ.