S
Sberloga
@sberloga2.5K подп.
1.3Kпросмотров
52.3%от подписчиков
6 февраля 2026 г.
Score: 1.5K
История одной витрины, или как пропавшие NULL сломали модель 👻 Привет, народ! Расскажу историю о том, как пропавшие NULL сломали продакшн-модель. Иногда важнее не то, что в данных появилось, а то, что исчезло. Ситуация: Обучаем модель. DS просит витрину с фичами по звонкам. Есть две группы: А (короткие номера) и B (поддержка). Витрина (feature_store_v1) строится по событиям. Если у клиента не было звонков в этих группах, его нет в витрине. Вот как выглядела наша витрина (feature_store_v1): user_id | calls_group_a_count | calls_group_b_count --------|--------------------|-------------------- 101 | 5 | 10 102 | 0 | 3 103 | 12 | 0 Как происходит обучение? DS делает LEFT JOIN с витриной. Клиентов без активности получает NULL. Модель привыкает, что доля NULL стабильна. --- А потом что-то меняется... 🤯 Бизнес запускает новую услугу и появляется третья категория звонков (Группа C). Мы обновляем витрину до feature_store_v2. Новая витрина (feature_store_v2): user_id | calls_group_a_count | calls_group_b_count | calls_group_c_count -------------|--------------------|--------------------|-------------------- 101 | 5 | 10 | 0 102 | 0 | 3 | 0 103 | 12 | 0 | 0 104 (Новый!) | 0 | 0 | 25 Что случилось? Появился user_id 104, активный только в новой группе. При LEFT JOIN он теперь нашел пару в витрине. Количество NULL в данных резко упало. Модель, обученная на мире с 30% пропусков, увидела мир с 15% > DS кричит: "Откатывайте, все сломалось!" > DE отвечают: "Данные же стали лучше!" --- Как не проебаться? 🛠️ 1. Мониторьте пропуски. Резкое падение % NULL — главный индикатор того, что сломалось базовое предположение о данных. Это ваш самый ранний аларм. 2. Если вы с широкими витринами — fillna(0) нормальное решение для обучения (главное на инференсе его не забыть 😁 ) сам я не сторонник fillna(0) т.к. почти всегда 0 и NULL - это разные вещи, имеющие разную природу. Но в данном случае нужно было предусмотреть, что витрина может измениться, данных может стать больше, процент заполняемости измениться, и для простых фичей в виде счетчиков 0 - это дефолтное значение которое можно применять Можно было бы еще формировать витрину сразу для всех пользователей, а не только для тех, у кого есть события. Но если можно проще и меньшими затратами - почему бы и нет? 3. Самое правильное решение — вертикальная витрина. Корень всех бед — в "широком" формате. Правильное решение — вертикальная витрина. Было (широко): user_id | calls_group_a_count | calls_group_b_count --------|--------------------|-------------------- 101 | 5 | 10 Стало (вертикально): user_id | call_group | count --------|------------|------- 101 | a | 5 101 | b | 10 102 | b | 3 103 | a | 12 Почему это решает проблему? Гибкость: Новая категория = новые строки, а не колонки. Схема не меняется, ничего не ломается. Эффективность: Хранит только ненулевые значения и отлично партиционируется. * Честный PIVOT: "Широкую" таблицу для модели создает явный PIVOT. Появление новой группы заставит DS обновить скрипт и осознанно переобучить модель. Вывод: fillna(0) — это не зло, а инструмент. Но он работает предсказуемо, только если пайплайн стабилен. А самый надежный способ сделать пайплайн стабильным — правильная архитектура данных.
1.3K
просмотров
3532
символов
Да
эмодзи
Нет
медиа

Другие посты @sberloga

Все посты канала →
История одной витрины, или как пропавшие NULL сломали модель — @sberloga | PostSniper