Skip to content

Commit d2b4236

Browse files
committed
update rls performance monitoring
1 parent 94b8f9f commit d2b4236

File tree

4 files changed

+72
-28
lines changed

4 files changed

+72
-28
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The CUSUM repository contains multiple change point detectors for sequential ana
1515
1. [CUSUM Detectors](#1-cusum-detectors)
1616
2. [Getting Started](#2-getting-started)
1717
3. [Documentation](#3-documentation)
18-
4. [Example: ML Model Performance Monitoring](#4-example-ml-model-performance-monitoring)
18+
4. [Examples](#4-examples)
1919
5. [License](#5-license)
2020

2121
## 0. Overview
@@ -53,17 +53,19 @@ uv sync
5353

5454
## 3. Documentation
5555

56-
Documentation is available at [CUSUM Documentation](https://CUSUM.readthedocs.io/en/latest/)
56+
Documentation is available at [CUSUM Docs](https://CUSUM.readthedocs.io/en/latest/)
5757

5858

59-
## 4. Example: ML Model Performance Monitoring
59+
## 4. Examples:
60+
61+
### a) ML Model Performance Monitoring with Observed Labels
6062

6163
Performance Monitoring of an instance-based linear learning model applying the CUSUM algorithm.
6264

6365
At each time step:
6466

6567
* Generate a prediction with recursive least squares (RLS) model;
66-
* Acquire the true observed value (if you are lucky);
68+
* Acquire the true observed value;
6769
* Compute residual;
6870
* Apply the CUSUM detector on the residuals to identify potential change points;
6971
* Update the model parameters with the new data instance.

docs/_static/images/monitoring.gif

-12.7 MB
Loading

img/monitoring.gif

-12.7 MB
Loading

notebooks/notebook_rls_monitoring.ipynb

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@
99
"import sys\n",
1010
"import os\n",
1111
"import numpy as np\n",
12+
"from dotenv import load_dotenv\n",
13+
"load_dotenv()\n",
14+
"sys.path.append(os.getenv(\"PATH_CUSUM\"))\n",
1215
"from source.model.incremental import RecursiveLeastSquares\n",
1316
"from source.detector.cusum import CUSUM_Detector\n",
1417
"import matplotlib.pyplot as plt\n",
15-
"from matplotlib.animation import FuncAnimation"
18+
"from matplotlib.animation import FuncAnimation\n",
19+
"\n",
20+
"# set random seed for reproducibility\n",
21+
"np.random.seed(42)"
1622
]
1723
},
1824
{
@@ -21,7 +27,7 @@
2127
"metadata": {},
2228
"outputs": [],
2329
"source": [
24-
"n = 600\n",
30+
"n = 300\n",
2531
"\n",
2632
"# Create time series components\n",
2733
"t = np.arange(n)\n",
@@ -52,7 +58,7 @@
5258
"metadata": {},
5359
"outputs": [],
5460
"source": [
55-
"values[300:500] += 50 # Introduce a change point\n",
61+
"values[150:] += 50 # Introduce a change point\n",
5662
"\n",
5763
"# plot the time series\n",
5864
"plt.figure(figsize=(15, 4))\n",
@@ -77,23 +83,25 @@
7783
"metadata": {},
7884
"outputs": [],
7985
"source": [
80-
"# Set number of lags\n",
81-
"num_lags = 3\n",
82-
"\n",
8386
"# Initialize model\n",
84-
"model = RecursiveLeastSquares(num_variables=num_lags, forgetting_factor=0.99, initial_delta=50)\n",
85-
"\n",
86-
"warmup_period = 100\n",
87+
"num_lags = 5\n",
88+
"model = RecursiveLeastSquares(num_variables=num_lags, \n",
89+
" forgetting_factor=0.999, \n",
90+
" initial_delta=1.0)\n",
8791
"\n",
8892
"# Detect change points using CUSUM Detector\n",
89-
"cusum_detector = CUSUM_Detector(warmup_period=warmup_period, delta=3, threshold=5)\n",
93+
"warmup_period = 50\n",
94+
"cusum_detector = CUSUM_Detector(warmup_period=warmup_period, \n",
95+
" delta=1.5, \n",
96+
" threshold=4.0)\n",
97+
"\n",
9098
"\n",
9199
"# Store predictions and observed values\n",
92100
"list_predictions = []\n",
93101
"list_observed = []\n",
94102
"list_residuals = []\n",
95103
"\n",
96-
"list_pos_changes = []\n",
104+
"list_acc_changes = []\n",
97105
"list_change_points = []\n",
98106
"list_index_changes = []\n",
99107
"list_output_detected = []\n",
@@ -104,7 +112,7 @@
104112
"fig.tight_layout(pad=5.0)\n",
105113
"\n",
106114
"# Animation function\n",
107-
"def animate(i):\n",
115+
"def monitoring(i):\n",
108116
"\n",
109117
" global output_detected, list_change_points, list_output_detected, list_residuals\n",
110118
" \n",
@@ -125,18 +133,18 @@
125133
"\n",
126134
" # Store prediction and observed values\n",
127135
" list_predictions.append(y_pred)\n",
128-
" list_observed.append(y[0])\n",
136+
" list_observed.append(float(y[0][0]))\n",
129137
"\n",
130138
" # model params update\n",
131139
" model.update(X, y)\n",
132140
"\n",
133141
" # Compute residuals\n",
134142
" residuals = np.abs(y - y_pred)\n",
135-
" list_residuals.append(residuals[0])\n",
143+
" list_residuals.append(residuals[0][0])\n",
136144
"\n",
137145
" # Detect change points\n",
138146
" output_detected = cusum_detector.detection(residuals[0])\n",
139-
" list_pos_changes.append(output_detected[0][0])\n",
147+
" list_acc_changes.append(float(output_detected[0][0]))\n",
140148
" list_change_points.append(output_detected[-1])\n",
141149
" list_output_detected.append(output_detected)\n",
142150
"\n",
@@ -146,35 +154,69 @@
146154
" change_ax.clear()\n",
147155
"\n",
148156
" # Plot predictions vs observed values\n",
149-
" prediction_ax.plot(list_observed, label='Obs', color='blue')\n",
150-
" prediction_ax.plot(list_predictions, label='Pred', color='orange')\n",
157+
" prediction_ax.plot(list_observed, label='Observation', color='orange')\n",
158+
" prediction_ax.plot(list_predictions, label='Prediction', color='blue', marker='o', markersize=4)\n",
159+
" prediction_ax.fill_between(range(len(list_predictions)),\n",
160+
" list_predictions,\n",
161+
" list_observed,\n",
162+
" color='lightgray', \n",
163+
" alpha=0.7)\n",
151164
" for i, is_cp in enumerate(list_change_points):\n",
152165
" if is_cp:\n",
153166
" prediction_ax.axvline(x=i, color='red', linestyle='--', alpha=0.5)\n",
154-
" if i >= warmup_period:\n",
155-
" prediction_ax.axvline(x=warmup_period, color='gray', label='End WarmUp', linestyle='--')\n",
156167
" prediction_ax.plot()\n",
157168
" prediction_ax.legend()\n",
158-
" prediction_ax.set_title(f'Change Points at idx {100} and {500}\\n RLS Predictions vs Observed Values')\n",
169+
" prediction_ax.set_title(f'Predictions vs Observations')\n",
170+
" prediction_ax.set_xlabel('Time Step')\n",
171+
" prediction_ax.set_ylabel('Prediction Value')\n",
172+
" prediction_ax.grid()\n",
159173
" \n",
160174
" # Plot residuals\n",
161-
" residual_ax.plot(list_residuals, label='Residuals', color='red')\n",
175+
" residual_ax.plot(list_residuals, label='Residuals', color='red', marker='o', markersize=4) \n",
176+
" residual_ax.fill_between(range(len(list_residuals)),\n",
177+
" 0,\n",
178+
" list_residuals,\n",
179+
" color='red', \n",
180+
" alpha=0.1) \n",
162181
" residual_ax.legend()\n",
163182
" residual_ax.set_title('Residuals')\n",
183+
" residual_ax.set_xlabel('Time Step')\n",
184+
" residual_ax.set_ylabel('Residual Value')\n",
185+
" residual_ax.grid()\n",
164186
"\n",
165187
" # Plot detected change points\n",
166-
" change_ax.plot(list_pos_changes, label='Performance Cumulative Changes', color='green')\n",
167-
" change_ax.axhline(y=cusum_detector.threshold, color='r', linestyle='--')\n",
188+
" change_ax.plot(list_acc_changes, label='Cumulative Change', color='green', marker='o', markersize=4)\n",
189+
" change_ax.fill_between(range(len(list_acc_changes)),\n",
190+
" 0,\n",
191+
" list_acc_changes,\n",
192+
" color='green', \n",
193+
" alpha=0.5)\n",
194+
" change_ax.axhline(y=cusum_detector.threshold, color='r', linestyle='--', label='CUSUM Threshold')\n",
168195
" change_ax.legend()\n",
169196
" change_ax.set_title('Detected Change Points')\n",
197+
" change_ax.set_xlabel('Time Step')\n",
198+
" change_ax.set_ylabel('Cumulative Change Value')\n",
199+
" change_ax.grid()\n",
170200
"\n",
171201
"# Create the animation\n",
172-
"ani = FuncAnimation(fig, animate, frames=len(values) - num_lags - 1, repeat=False) #\n",
202+
"ani = FuncAnimation(fig, monitoring, frames=len(values) - num_lags - 1, repeat=False) #\n",
173203
"\n",
174204
"# Save the animation\n",
175205
"ani.save('monitoring.gif', writer='pillow', fps=100)\n",
176206
"plt.close(fig) # Prevent the figure from displaying"
177207
]
208+
},
209+
{
210+
"cell_type": "code",
211+
"execution_count": null,
212+
"metadata": {},
213+
"outputs": [],
214+
"source": [
215+
"import IPython.display as display\n",
216+
"\n",
217+
"# Display the saved GIF\n",
218+
"display.Image(filename='monitoring.gif')"
219+
]
178220
}
179221
],
180222
"metadata": {

0 commit comments

Comments
 (0)