tynys.
реплей 17.07.2026 · 12:00

Проблема, которую видно в данных

Сравнение глобального модельного поля CAMS с физическими сенсорами по совпадающим часам

×1.97
CAMS систематически занижает PM2.5 в Караганде (в Алматы — наоборот, ×0.52)
515 → 77
пик смога у сенсора vs максимум, который вообще видел CAMS, мкг/м³
0.05
корреляция CAMS с реальностью внутри месяца — краткосрочную динамику он не отслеживает вовсе

Данные: три источника, сшитые почасово

Ключ сшивки: город + локальная дата + локальный час; таймзоны выровнены и верифицированы по физике суточных циклов (пик озона CAMS и температуры BME280 — в 14:00 локального)

ИсточникОбъёмЧто берём
Sensor.Community (SDS011/PMS/SEN5x + BME280)709 770 минутных измерений с 9 сенсоров → 22 739 часовPM2.5/PM10 (ground truth), влажность, температура
CAMS (Open-Meteo Air Quality API)23 города × 199 дней × 24 часаPM2.5, PM10, CO, NO₂, SO₂, O₃ — глобальный прогноз
Бюллетени НМУ Казгидромета (ежедневные PPTX)22 города, 4 377 суток (пополняется ежедневно)доли ПДК газов, НМУ-степени, официальный PM2.5, ветер (распарсен из текста прогнозов, 97.6% дней)

Модель: локальная поправка, а не замена CAMS

Учим то, чего у CAMS по построению быть не может — локальную дельту

delta = log(PM2.5_сенсор + 1) − log(PM2.5_CAMS + 1)

HistGradientBoostingRegressor на признаках: сезон, час, влажность, температура, ветер и доли ПДК газов из бюллетеней, НМУ-степень, поля CAMS, город. Два квантиля дельты: q50 — уровень для карты и графиков, q90 — верхняя граница риска. Алерт = q90 > 35 мкг/м³: для родителя астматика пропущенный опасный час хуже лишнего предупреждения.

Валидация 1 — переносимость на незнакомую точку (LOSO)

Leave-one-sensor-out: модель предсказывает локацию, которую никогда не видела; среднее по 5 фолдам

МодельMAE, мкг/м³corrF1 детекции часов >35
CAMS as-is (что видит человек в погодном приложении)14.750.410.13
CAMS + константа города14.220.410.20
Наша модель: ML-поправка + квантильный алерт11.220.590.51

−24% ошибки к CAMS; recall опасных часов 0.78 при покрытии q90 0.81. Данные обновляются ежедневно, метрики пересчитываются — прогон 20.07.2026 на 22 739 часах

Валидация 2 — честный прогноз по времени

Rolling-origin бэктест: train — только прошлые месяцы, test — следующий месяц (5 фолдов, март–июль). Признаки, недоступные в момент выпуска прогноза, заменены лагами

ГоризонтMAE нашаMAE CAMScorr нашаcorr CAMSAlert F1 нашаAlert F1 CAMS
+1 час3.511.00.760.050.560.02
+3 часа5.311.00.590.050.350.02
+6 часов6.011.00.470.050.280.02

Деградация по горизонту — честная: на +6ч модель всё ещё на 45% точнее CAMS по MAE. Абляция: добавление почасового прогноза погоды Open-Meteo (ветер, T, RH, boundary layer height) поверх CAMS-полей меняет метрики <2% — метеорология уже ассимилирована в CAMS (проверено, 06_fetch_weather + вариант lags+weather в бэктесте)

Алерты по узлам (LOSO, q90)

Precision ниже на чистых локациях Алматы — принцип предосторожности; порог калибруется

ЛокацияMAE q50precisionrecallF1
72099 · Алматы6.10.100.930.18
81020 · Караганда11.00.620.560.58
81039 · Караганда11.20.400.850.54
85666 · Алматы8.00.410.800.54
86036 · Караганда17.20.620.770.69

Что проверили и не взяли в модель

Отрицательные результаты — тоже результаты; обе гипотезы остались в продукте как слой объяснения, но не как входы прогноза

ГипотезаЧто добавлялиИтог (LOSO / бэктест)
Прогноз погоды поверх CAMSветер, T, RH, осадки, давление, высота пограничного слоя (Open-Meteo, почасово)изменение <2%: метеорология уже ассимилирована в CAMS
Близость к промзоне и перенос шлейфа ветром42 крупных источника (ТЭЦ, металлургия, цемент): экспозиция Σ E/d² и наветренный перенос E·e^(−d/30км)·cos²θ/(u+1)MAE 10.71 → 11.15, F1 0.51 → 0.50: доли ПДК газов из бюллетеней уже несут измеренный след выбросов, а статическая экспозиция в LOSO работает как отпечаток локации

Атрибуция источников осталась на экранах «Мой двор» и «Карта»: при текущем ветре видно, какие предприятия стоят с наветренной стороны и какова их доля в переносе — это объясняет картину, но в q50/q90 не входит.

Честные ограничения