Рекламный отдел запускает 50 баннеров — какой из них сработает? A/B-тесты требуют недель и тысяч показов. Мы решаем это до запуска: AI-система анализирует визуальные признаки креатива и предсказывает CTR с точностью до ±15% на основе исторических данных. За 5 лет работы мы внедрили аналогичные решения для 30+ проектов в e-commerce и fintech, гарантируя рост CTR на 10–30% после оптимизации. Один из клиентов — маркетплейс одежды — тратил значительный бюджет на тестирование креативов; после внедрения AI-системы они сократили затраты на 40% и повысили средний CTR на 22%. Окупаемость системы составляет 2–3 месяца.
Как AI предсказывает CTR?
Система извлекает два типа признаков: семантические через CLIP и низкоуровневые визуальные (контраст, насыщенность, текстовое покрытие, наличие лиц). Обучаем LightGBM на логарифме CTR для нормализации распределения. LightGBM в 3 раза быстрее нейросетевых аналогов и даёт сравнимую точность.
import torch
import torch.nn.functional as F
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
import numpy as np
import cv2
class CreativeFeatureExtractor:
"""
Мультимодальные признаки: CLIP semantic + низкоуровневые visual.
"""
def __init__(self):
self.clip = CLIPModel.from_pretrained(
'openai/clip-vit-large-patch14'
).eval().cuda()
self.processor = CLIPProcessor.from_pretrained(
'openai/clip-vit-large-patch14'
)
@torch.no_grad()
def extract_clip_features(
self, image: Image.Image
) -> np.ndarray:
inputs = self.processor(images=image, return_tensors='pt').to('cuda')
emb = self.clip.get_image_features(**inputs)
return F.normalize(emb, dim=-1).cpu().numpy().squeeze()
def extract_visual_features(self, image: Image.Image) -> dict:
"""
Низкоуровневые признаки, коррелирующие с CTR:
- face_area_ratio: наличие лица (face = +18% CTR по Nielsen)
- contrast: высокий контраст → заметность
- color_harmony: гармоничная палитра
- text_coverage: сколько % занимает текст
- brightness_variance: визуальная сложность
"""
img_array = np.array(image)
h, w = img_array.shape[:2]
features = {}
# Контраст (Michelson)
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
features['contrast_michelson'] = float(
(gray.max() - gray.min()) / (gray.max() + gray.min() + 1e-8)
)
# Яркостная дисперсия
features['brightness_variance'] = float(gray.std() / 128.0)
# Доминирующие цвета (k=5 через k-means)
pixels = img_array.reshape(-1, 3).astype(np.float32)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
_, labels, centers = cv2.kmeans(
pixels, 5, None, criteria, 5, cv2.KMEANS_PP_CENTERS
)
counts = np.bincount(labels.flatten(), minlength=5)
dominant_color = centers[counts.argmax()]
features['dominant_hue'] = float(
cv2.cvtColor(
dominant_color.reshape(1,1,3).astype(np.uint8),
cv2.COLOR_RGB2HSV
)[0,0,0] / 180.0
)
features['dominant_saturation'] = float(dominant_color.max() - dominant_color.min()) / 255.0
# Детекция лиц
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
total_face_area = sum(fw * fh for _, _, fw, fh in faces) if len(faces) > 0 else 0
features['face_area_ratio'] = total_face_area / (h * w)
features['has_face'] = int(len(faces) > 0)
features['face_count'] = len(faces)
# Доля текстовых регионов (через морфологию)
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)
features['text_coverage_estimate'] = float(
(gradient > 0).mean()
)
return features
import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
class CreativeCTRPredictor:
"""
Обучаем на исторических данных: (visual_features, clip_embedding) → CTR.
Целевая переменная: log(CTR) для нормализации распределения.
"""
def __init__(self):
self.feature_extractor = CreativeFeatureExtractor()
self.model = lgb.LGBMRegressor(
n_estimators=500,
learning_rate=0.05,
max_depth=6,
min_child_samples=20,
subsample=0.8,
colsample_bytree=0.8,
reg_lambda=0.1
)
self.scaler = StandardScaler()
def build_feature_vector(self, image: Image.Image) -> np.ndarray:
visual = self.feature_extractor.extract_visual_features(image)
clip_emb = self.feature_extractor.extract_clip_features(image)
visual_vec = np.array(list(visual.values()))
# CLIP 768-dim + визуальные признаки ~10 dim
return np.concatenate([clip_emb, visual_vec])
def predict_ctr(
self, image: Image.Image
) -> dict:
features = self.build_feature_vector(image)
features_scaled = self.scaler.transform(features.reshape(1, -1))
log_ctr = self.model.predict(features_scaled)[0]
ctr_predicted = np.exp(log_ctr)
# SHAP объяснимость — топ-3 фактора
import shap
explainer = shap.TreeExplainer(self.model)
shap_values = explainer.shap_values(features_scaled)
return {
'predicted_ctr': round(float(ctr_predicted), 4),
'ctr_percentile': None, # заполняется из распределения на train
'top_factors': self._top_shap_factors(shap_values[0], features)
}
def _top_shap_factors(
self, shap_vals: np.ndarray, feature_vals: np.ndarray, top_k: int = 3
) -> list:
top_indices = np.argsort(np.abs(shap_vals))[::-1][:top_k]
feature_names = (
[f'clip_{i}' for i in range(768)] +
['contrast', 'brightness_var', 'dominant_hue',
'dominant_sat', 'face_ratio', 'has_face',
'face_count', 'text_coverage']
)
return [
{
'feature': feature_names[i] if i < len(feature_names) else f'feat_{i}',
'shap_value': float(shap_vals[i]),
'direction': 'positive' if shap_vals[i] > 0 else 'negative'
}
for i in top_indices
]
Почему LightGBM, а не DNN?
Градиентный бустинг на табличных данных даёт лучшее качество, чем нейросети, при малом объёме данных (тысячи креативов). LightGBM в 3 раза быстрее CatBoost на 500+ признаках, а SHAP-интерпретация работает «из коробки». Для продакшена мы используем vLLM для инференса CLIP и ONNX Runtime для ускорения модели.
Что такое SHAP и как он помогает дизайнерам?
SHAP (SHapley Additive exPlanations) присваивает каждому признаку вклад в предсказание — аналогично распределению выигрыша в кооперативной игре. Дизайнер видит, что «наличие лица» увеличило прогнозируемый CTR на 0.8%, а «низкий контраст» снизил на 0.4%. Это позволяет сознательно улучшать баннер, а не гадать. Мы интегрируем SHAP-графики в дашборд Streamlit, где можно загрузить креатив и сразу получить топ-3 факторов.
Как мы интегрируем систему в ваш creative workflow?
Мы разворачиваем REST API на Triton Inference Server или SageMaker. API принимает изображение и возвращает predicted_ctr и top_factors. Через плагины для Figma и Photoshop дизайнеры получают предикт прямо в интерфейсе. Встроенная MLOps-инфраструктура (MLflow, DVC) версионирует данные и модели, поддерживает A/B-тесты новых версий. Типичная интеграция занимает 1–2 недели.
Ключевые инсайты из данных
По накопленной статистике рекламных платформ (Google, Meta):
| Визуальный признак | Влияние на CTR | Примечание |
|---|---|---|
| Наличие лица (frontal) | +15–22% | Особенно для fashion, beauty |
| Высокая насыщенность цвета (>0.6) | +8–14% | Не работает для B2B |
| Текст < 20% площади | +10–17% | Meta ограничение 20% |
| Контраст > 0.7 | +6–11% | Заметность в ленте |
| Лицо смотрит на CTA | +12% | Eye tracking исследования |
| Warm colors (hue 0-60°) | +5–9% | Для food, lifestyle |
Процесс работы
- Аналитика: аудит исторических креативов, сбор метрик, оценка выборки. Мы проверяем, достаточно ли данных и есть ли баланс по категориям.
- Проектирование: выбор архитектуры (CLIP vs EfficientNet, LightGBM vs XGBoost), описание feature pipeline. Определяем метрики качества: MAE, MAPE, корреляция Спирмена.
- Реализация: обучение модели, валидация на отложенной выборке (time-based split). Интегрируем SHAP для объяснимости.
- Тестирование: A/B-эксперимент на 20 креативах — сравниваем предсказание с фактическим CTR после недели показов.
- Деплой: развёртывание API, подключение к дизайн-пайплайну через плагины. Настраиваем мониторинг дрейфа данных.
Что входит в работу
- Документация: model card, эксплуатационная инструкция, описание API.
- Исходный код с DVC-версионированием данных.
- Доступ к дашборду Streamlit для тестирования новых креативов.
- Обучение команды работе с системой (2 часа).
- Поддержка 3 месяца после деплоя.
Сроки
| Задача | Срок |
|---|---|
| Модель на исторических данных клиента (500+ креативов) | 3–5 недель |
| Полная система с API и интеграцией в creative workflow | 6–10 недель |
| Генеративная оптимизация (AI-коррекция баннера) | 10–16 недель |
Мы гарантируем прозрачность прогнозов: каждый предсказанный CTR сопровождается топ-3 факторами от SHAP. Получите консультацию по вашему проекту — оценим данные за 2 дня. Свяжитесь с нами для оценки ваших данных — мы проанализируем 100 креативов за 2 дня. Закажите пилот на 20 креативах — результат через неделю.







