TGViewer
OK ML OK ML @okmlai · 978 subscribers
Post #34 351
🤖 Ошибка в проде или как одна строка сломала всё (ML edition)

Недавно в ML-сервисе (он ранжировал товары в каталоге) в продакшн попала вот такая правка:

# хотели заменить устаревший scaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)

И всё — выдача внезапно «поехала».
В чём подвох?
В проде модель ждала данные, нормализованные так же, как при обучении.
Но fit_transform обучил новый StandardScaler прямо на боевых данных.
В результате каждый запрос нормализовался «по-своему», и модель выдавала рандом.
Одна строка превратила рекомендательную систему в рандомизатор. Почему? StandardScaler.fit_transform() вычисляет среднее (mean) и стандартное отклонение (std) по предоставленным данным и сразу применяет их ((x - mean) / std).
На обучении он посчитал mean_train и std_train на трейне и преобразовал данные. Модель научилась работать с признаками, имеющими такое распределение (~N(0,1)). На проде в каждом запросе (или батче) он заново вычислял mean_prod и std_prod на лету для одного-единственного запроса и применял их. Для одного объекта mean = x1, std = 0. Попытка поделить на ноль (или получить огромные числа, если std выставлялся в какое-то минимальное значение для избежания деления на ноль). Для небольшого батча нормализация происходила по странным, нестабильным параметрам. Модель получала на вход числа, которые по масштабу и распределению были совершенно не похожи на те, на которых она училась. Ее предсказания становились бессмысленными.

Именно поэтому ML-прод = код + данные + артефакты препроцессинга.

Как нашли:
Метрики CTR резко упали (тревогу поднимают аналитики)
В логах модели значения признаков стали «плясать».
git bisect вывел на единственную строку.

Фикс:
# должно быть строго transform с сохранённым scaler из обучения
scaler = load("scaler.joblib")
X_scaled = scaler.transform(X_request)

Выводы:
Никогда не вызывай fit/fit_transform на продовых данных.
Все препроцессоры должны сохраняться вместе с моделью (joblib/pickle/onnx).
Смоук-тесты для ML — это не только «API 200 ОК», но и проверка распределений фичей.

Весь препроцессинг (кодирование, скалирование, импутация) должен быть инкапсулирован в единый объект, который обучается только один раз и затем переиспользуется. Лучший способ — использовать sklearn.pipeline.Pipeline.

P.S. Ошибка не ограничивается StandardScaler. Любой объект с состоянием, обученным на трейне, должен быть сохранён. Для прод-ML best practices тык.
  • ❤ 13
  • ✍ 3
  • ⚡ 3
  • 😁 2
  • 👍 1
More from @okmlai
  1. Sep 23, 2026Зачем писать стилер, если у пользователя уже есть ИИ-ассистент с доступом ко всему? Патрик…
  2. Sep 21, 2026ИИ получил доступ к своим весам. ЧБД? 16 сентября Irregular опубликовала исследование agen…
  3. Sep 14, 202610 сентября Anthropic опубликовала Detecting and countering misuse of AI — отчёт о злоупот…
  4. Sep 11, 2026Репозиторий недели. Mantis разделить нельзя объединять 📬 У Google есть Mantis — набор нав…
  5. Sep 9, 2026Пароль сменили. Сессии закрыли. А чужие инструкции остались в памяти ИИ. Именно такой сцен…
  6. Sep 4, 2026AI-агенты начали действовать вне инструкций. Вот что нужно знать разработчикам ⤵️ Недавно…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →