Реализация AI-сегментации клиентов

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
Реализация AI-сегментации клиентов
Средняя
~5 рабочих дней
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1240
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1167
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    867
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1084
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    829

Реализация AI-сегментации клиентов

Ручная сегментация клиентов — это 3-5 бизнес-сегментов, созданных интуитивно и пересматриваемых раз в квартал. ML-сегментация обнаруживает 10-20 статистически значимых кластеров, автоматически описывает их через LLM и обновляет ежедневно. Разница в precision маркетинговых кампаний: 15-25% vs 40-60% CTR.

Feature Engineering для сегментации

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])
        }

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

Размер базы Число признаков Время кластеризации Оптимальных кластеров
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%+ новых клиентов.