trosna-weighted-regression
Исходный код: https://github.com/89605502155/trosna-weighted-regression
Взвешенная по времени линейная регрессия с защитой от «засыпания» при резких изменениях. Чистый Python (numpy + scikit-learn), эстиматор совместим со sklearn.
pip install trosna-weighted-regression
Проблема
Базовая модель (Ферубко, Казаков, Вестник РГРТУ, 2026, № 95, с. 73–84): прямая строится по 5 последним
точкам, вес точки w_i ∝ |t − t_i|^(−n) (метрическое ядро) или exp(−(t − t_i)² / 2h²) (экспоненциальное),
t — момент следующего мониторинга. После долгой полки или спада старые точки окна тянут наклон назад:
| Сценарий (прогноз на шаг вперёд, n = 1) | Истинные значения | Без защиты | С защитой |
|---|---|---|---|
| Ступенька 1000 → 1500 | 1500 1500 1500 1500 1500 | 1000 1541 1676 1676 1608 | 1000 1500 1500 1500 1500 |
| Спад, затем рост +100/шаг | 910 1010 1110 1210 1310 | 800 909 1048 1186 1310 | 800 910 1110 1210 1310 |
| Всплеск на одну точку | 1500 1000 1000 1000 1000 | 1000 1541 1135 1000 932 | 1000 1500 1000 1000 1000 |
| Провал на одну точку | 500 1000 1000 1000 1000 | 1000 459 865 1000 1068 | 1000 1000 1000 1000 1000 |
Первую точку скачка не предсказывает никто — дело в том, что происходит после неё.
Защита
- Детектор разладки. Новая точка сравнивается с прогнозом на неё. Ошибка нормируется на своё стандартное
отклонение: шум точки σ, умноженный на
√(1 + Σc_i²), гдеŷ = Σc_i·y_i— прогноз (по двум свежим точкам он втрое шумнее одной точки, по пяти — заметно тише). σ — медиана модулей последнихscale_windowнормированных остатков, но не нижеmin_scale(на ровной полке оценка была бы нулевой). Ошибка большеthreshold— разладка. - Пробуждение. При разладке точки до неё выбрасываются, окно строится заново с точки разладки. Пока у нового режима меньше 5 остатков, шум берётся из прежнего режима — детектор не слепнет и ловит второй скачок сразу за первым. Повторные разладки в это время учитываются в оценке шума, поэтому переход в более шумный режим не вызывает бесконечного каскада перестроек.
- Излом тренда. Пока в новом окне одна точка, прогноз — её уровень. Следующая точка, легшая на прямую от последней точки прежнего режима через точку перестройки, — продолжение разворота (долгий спад → рост), и модель сразу идёт по новому наклону. Точка, не легшая ни на уровень, ни на излом, — новая разладка: так ложное срабатывание, за которым следует настоящий скачок, не удваивает скачок в прогнозе.
- Асимметрия. Рост принимается сразу (
confirm_up=0): недодать память опаснее, чем выдать лишнюю. Спад — только послеconfirm_down=1подтверждающей точки, до этого модель держит прежний уровень. - Откат всплесков. Если после немедленной перестройки следующая точка вернулась к старому режиму, старое
окно и оценка шума восстанавливаются, а всплеск отбрасывается (
spikes_). Неподтверждённый спад тоже отбрасывается.
guard=False — базовая модель из статьи без защиты, для сравнения.
Дополнительные механизмы
По умолчанию выключены или не меняют прогноз: базовые настройки дают модель из статьи плюс защиту выше.
- Адаптивное окно (ICI) —
adaptive_window=True,window— наибольшая длина окна. На каждом прогнозе строятся прямые по последним 3, 5, 8, 12, … точкам режима, и берётся самое длинное окно, интервал прогноза которого (±ici_gamma·σ·√Σc²) ещё пересекается с интервалами всех более коротких. На ровном участке окно растёт и прогноз тише, на изломе короткие окна расходятся с длинными, и окно сразу укорачивается. Правило пересечения доверительных интервалов — Гольденшлюгер и Немировский, Катковник (LPA-ICI). - Накопительный детектор CUSUM —
cusum_h(порог,None— выключен) иcusum_k(допуск). Те же нормированные ошибки прогноза копятся по Пейджу:S⁺ = max(0, S⁺ + z − k), отдельно вниз. Ловит устойчивые сдвиги меньшеthreshold, на которых длинное адаптивное окно засыпало бы. При срабатывании окно строится заново с точки, с которой начала копиться сумма. - Интервал прогноза —
predict_interval(X)возвращает(lower, upper)с покрытиемcoverage. Полуширина — квантиль нормированных модулей последнихinterval_windowошибок прогноза на шаг вперёд, умноженный на шум прогноза в точке. Уровень квантиля подстраивается онлайн (адаптивный конформный вывод, Гиббс и Кандес): после промаха интервал расширяется, после попадания понемногу сужается. Шаг подстройки —aci_rate, текущий уровень промахов —interval_alpha_. Верхняя граница — объём ОП, который стоит зарезервировать.
Рекомендуемый набор: WakefulWLS(window=30, adaptive_window=True, cusum_h=5.0).
Проверка на шуме
Монте-Карло: полка, спад или рост длиной 200 точек, затем скачок; шум σ = 5, по 10 запусков на конфигурацию, ядра метрическое и экспоненциальное, окна 5 и 10. «Шагов» — сколько шагов после скачка прогноз ещё выходит за 4σ от истины.
| Скачок | Шагов: без защиты / с защитой | Средняя ошибка, МБ: без / с |
|---|---|---|
| Вверх на 30σ (150 МБ) | 5–8,4 / 1 | 8,2–8,5 / 3,9–5,1 |
| Вверх на 120σ (600 МБ) | 5–10 / 1 | 19,7–25,9 / 3,9–5,1 |
| Вниз на 30σ и 120σ | 5–10 / 2 | примерно равны |
| Пограничный, 6σ (окно 10) | 1 / 1,7–1,8 | 3,8–4,4 / 4,0–4,8 |
Спад занимает 2 шага, потому что так задано (confirm_down=1, 0 — реагировать сразу). На пограничных скачках
защита чуть уступает: после перестройки прогноз строится по 2–3 свежим точкам и шумнее. Ложные срабатывания на
стационарном гауссовом шуме — около 1 на 1000 точек, на шуме с тяжёлыми хвостами (t₃) — около 11; почти все они
откатываются как всплески на следующей же точке.
Дополнительные механизмы
Тот же стенд, метрическое ядро. «База» — window=5, «защита» — window=5 с защитой, «ICI» — плюс
window=30, adaptive_window=True, «ICI + CUSUM» — плюс cusum_h=5. Ячейки — шагов до точного прогноза /
средняя ошибка, МБ.
| Сценарий | База | Защита | ICI | ICI + CUSUM |
|---|---|---|---|---|
| Стационарный шум, ошибка, МБ | 4,6 | 4,6 | 2,2 | 2,2 |
| Скачок вверх на 30σ | 5 / 8,2 | 1 / 5,1 | 1 / 3,4 | 1 / 3,4 |
| Скачок вверх на 6σ | 1,2 / 5,2 | 1,5 / 5,3 | 1 / 3,6 | 1 / 3,5 |
| Скачок вниз на 6σ | 1,1 / 5,2 | 1,5 / 5,5 | 1,6 / 4,1 | 1,6 / 4,0 |
| Новый наклон 1σ/шаг после полки | 0 / 4,9 | 0 / 4,9 | 0–1 / 5,2 | 0 / 3,9 |
| Новый наклон 3σ/шаг после полки | 0,6–0,8 / 5,1–5,2 | 0,6–0,9 / 5,2–5,3 | 2,8–5,2 / 5,5–6,5 | 1,8–2,2 / 4,2 |
| Новый наклон 10σ/шаг после полки | 3 / 6,6 | 2 / 6,2 | 2 / 4,6 | 2 / 4,6 |
Адаптивное окно вдвое снижает ошибку на спокойных участках и после скачков. Слабое место — излом наклона порядка 3σ за шаг: без CUSUM длинное окно выходит на новый наклон на 2–4 шага позже короткого. CUSUM сокращает отставание до 1–1,5 шага, а средняя ошибка с ним ниже, чем у окна из 5 точек. С окном из 5 точек CUSUM почти ничего не даёт: короткое окно и так догоняет за несколько шагов. Экспоненциальное ядро (h = 3) само почти не смотрит дальше 6–8 точек, поэтому выигрыш от ICI у него скромнее: около 20 % на шуме.
Интервал на 95 % (6 запусков по 1500 точек):
| Ряд | Покрытие, без подстройки / с ней | Покрытие 10 шагов после скачка |
|---|---|---|
| Гауссов шум | 0,951 / 0,949 | — |
| Шум t₃ с тяжёлыми хвостами | 0,950 / 0,950 | — |
| Скачки каждые 150 точек, шум вырос втрое | 0,937 / 0,949 | 0,87 / 0,91 |
С адаптивным окном интервал на 25–30 % уже при том же покрытии.
Использование
import numpy as np
from trosna_weighted_regression import WakefulWLS
t = np.arange(len(mem), dtype=float)[:, None] # одна колонка времени
model = WakefulWLS(window=5, kernel="metric", power=1, min_scale=10).fit(t, mem) # mem в МБ
model.predict([[t[-1, 0] + 1]]) # прогноз на следующий мониторинг
model.change_points_, model.spikes_ # где модель «проснулась» и какие всплески отбросила
# Поток: дообучение по мере поступления точек, эквивалентно fit на всём ряде
model.partial_fit([[t_new]], [mem_new])
# Адаптивное окно + CUSUM; верхняя граница 95 % интервала — сколько памяти зарезервировать
model = WakefulWLS(window=30, adaptive_window=True, cusum_h=5.0, min_scale=10).fit(t, mem)
lower, upper = model.predict_interval([[t[-1, 0] + 1]])
Работает в GridSearchCV (с TimeSeriesSplit), Pipeline, clone. X — ровно одна колонка времени,
поэтому стандартные проверки sklearn, подающие несколько признаков, помечены в тестах как ожидаемые падения;
остальные проходят.
Разработка
git clone https://github.com/89605502155/trosna-weighted-regression.git
cd trosna-weighted-regression
uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
Лицензия
MIT, см. LICENSE.
Metadata
Release files for trosna-weighted-regression 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| trosna_weighted_regression-0.1.0.tar.gz | 17.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| trosna_weighted_regression-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.7 kB
Release files / trosna_weighted_regression-0.1.0.tar.gz
| Download URL | trosna_weighted_regression-0.1.0.tar.gz |
|---|---|
| Size | 17.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79c0ae7e63a415ca39c019572e9184c15f32b445358d329370f653e9f0caa257
|
|
BLAKE2b-256 checksum How to use checksums |
83304bc3f21661cdab6bfe4fd2a665de887970620e1a611e3675bbbff30f03a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / trosna_weighted_regression-0.1.0-py3-none-any.whl
| Download URL | trosna_weighted_regression-0.1.0-py3-none-any.whl |
|---|---|
| Size | 18.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
62594c959e2e369aec815a90fc3fcac341d15e449d738e029aeeae44315494ee
|
|
BLAKE2b-256 checksum How to use checksums |
f1ab62496ab82ac7078c83f24dd4b494666c58fa9b3302380d7ca29c89d66ee3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|