Skip to content

Commit 92c6279

Browse files
author
Avinash Kumar
committed
Adversarial attacks on load forecasting model
1 parent b19013c commit 92c6279

21 files changed

+22824
-1
lines changed

.gitignore

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Node artifact files
2+
node_modules/
3+
dist/
4+
5+
# Compiled Java class files
6+
*.class
7+
8+
# Compiled Python bytecode
9+
*.py[cod]
10+
11+
# Log files
12+
*.log
13+
14+
# Package files
15+
*.jar
16+
17+
# Maven
18+
target/
19+
20+
# JetBrains IDE
21+
.idea/
22+
23+
# Unit test reports
24+
TEST*.xml
25+
26+
# Generated by MacOS
27+
.DS_Store
28+
29+
# Generated by Windows
30+
Thumbs.db
31+
32+
# Applications
33+
*.app
34+
*.exe
35+
*.war
36+
37+
# Large media files
38+
*.mp4
39+
*.tiff
40+
*.avi
41+
*.flv
42+
*.mov
43+
*.wmv

README.md

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,54 @@
1-
# adversarial-attacks-on-load-forecasting-model
1+
# Adversarial Attacks on Load Forecasting Model
2+
3+
## Dataset Information
4+
### Dataset Used
5+
1. ENTSO-E Dataset (European Network of Transmission System Operators for Electricity) for hourly load data in Switzerland.
6+
2. DarkSky Dataset for hourly temperature and weather Icon information of 8 major cities in Switzerland.
7+
8+
### Dataset Raw Features
9+
There are 21 raw features at each timestamp:
10+
1. **Load**
11+
2. **8 Cities temperature**
12+
3. **8 Cities weather Icon Information** - categorical feature tells which weather icon [categories: icon1, icon2, icon3]
13+
4. **Holiday** - boolean feature tells weather holiday in switzerland on that date
14+
5. **Month** - categorical feature tells data of which month [categories: Jan, Feb, ... , Dec]
15+
6. **Day** - categorical feature tells data of which day [categories: Mon, Tues, Wed, Thrus, Fri, Sat, Sun]
16+
7. **Hour** - categorical feature tells data of which hour [categories: 0, 1, 2, ... , 23]
17+
18+
### Dataset Source
19+
You can use already preprocessed data present in `data` folder with name `actual_dataset.csv` .
20+
21+
**Feature Vector 77 dimensional at each timestamp:** \
22+
actual_load - 1 feature \
23+
8 cities temperature - 8 features \
24+
8 cities weather icon one hot encoding - (8 cities x 3 categories of icon) = 24 features \
25+
holiday - 1 feature \
26+
weekday one hot encoding - 7 features \
27+
hour one hot encoding - 24 features \
28+
month one hot encoding - 12 features
29+
30+
**NOTE:** Please ignore `entsoe` feature column in `actual_dataset.csv`.
31+
32+
33+
## Train Load Forecasting Model
34+
1. change `DATASET_SPLIT_DATE` in `constants.py` according to how you want to split train and test dataset.
35+
2. Simply Run
36+
```shell
37+
python forecasting.py
38+
```
39+
3. It will save trained model weights in `output/load_forecasting_model_weights.h5`. save `output/loss_epoch_curve.png` and `output/actual_predicted_load.png` images.
40+
41+
42+
## Generate Adversarial Datasets
43+
1. Simply Run:
44+
```shell
45+
python adversarial.py
46+
```
47+
2. It will generate adversarial datasets for various temperature variation in `data` folder. For Ex: `adversarial_dataset_temp_1.csv` means generate adversarial temperature dataset with 1 Fahrenheit change in temperature.
48+
49+
50+
## Results
51+
Check `results.ipynb` file to see various plots like:
52+
1. Temperature Profile
53+
2. Load Forecasting Profile
54+
3. Forecasting MAPE with Temperature Variation

adversarial.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import numpy as np
2+
import pandas as pd
3+
import constants as const
4+
from dataset import load_dataset
5+
from forecasting import forecasting_model
6+
from sklearn.preprocessing import StandardScaler
7+
8+
9+
# bound generated adversarial temperature features - attacker capabilities
10+
def check_constraint(x_orig, x_new):
11+
for temp_idx in range(1, 9):
12+
x_new[temp_idx] = np.clip(x_new[temp_idx], x_orig[temp_idx] - const.BOUND * np.abs(x_orig[temp_idx]),
13+
x_orig[temp_idx] + const.BOUND * np.abs(x_orig[temp_idx]))
14+
return x_new
15+
16+
17+
# calculate signed gradient
18+
def calculate_signed_gradient(X, temp_idx, model):
19+
X_plus = X.copy()
20+
X_minus = X.copy()
21+
X_plus[0, const.SEQ_LENGTH-1, temp_idx] += const.DELTA
22+
X_minus[0, const.SEQ_LENGTH-1, temp_idx] -= const.DELTA
23+
gradient = model.predict(X_plus, verbose=0) - model.predict(X_minus, verbose=0)
24+
return np.sign(gradient)
25+
26+
27+
# Black Box Based Gradient Estimation Algorithm to generate hard to detect adversarial dataset
28+
def gradient_estimation(df, model, temp_variation):
29+
X_adversarial = []
30+
data = np.array(df, dtype=float)
31+
alpha = const.ALPHA * temp_variation
32+
33+
# loop over all datapoint
34+
for i in range(len(data)):
35+
epoch = 1
36+
if i < const.SEQ_LENGTH - 1:
37+
X_adversarial.append(data[i])
38+
continue
39+
40+
# gamma which denotes to increase or decrease load, if 0 then increase load, if 1 then decrease load
41+
gamma = np.random.randint(2)
42+
X = data[i - const.SEQ_LENGTH + 1:i + 1].reshape(1, const.SEQ_LENGTH, const.FEATURE_DIM)
43+
44+
# optimize GRAD_NUM_EPOCHS times
45+
while epoch <= const.GRAD_NUM_EPOCHS:
46+
for temp_idx in range(1, 9):
47+
signed_gradient = calculate_signed_gradient(X, temp_idx, model)
48+
if gamma == 0:
49+
X[0][const.SEQ_LENGTH - 1][temp_idx] += alpha * signed_gradient
50+
else:
51+
X[0][const.SEQ_LENGTH - 1][temp_idx] -= alpha * signed_gradient
52+
epoch = epoch + 1
53+
54+
# bound temperature data - attacker capabilities
55+
X[0][const.SEQ_LENGTH - 1] = check_constraint(data[i], X[0][const.SEQ_LENGTH - 1])
56+
# store generated adversarial datapoint
57+
X_adversarial.append(X[0][const.SEQ_LENGTH - 1])
58+
59+
return pd.DataFrame(X_adversarial, index=df.index, columns=df.columns)
60+
61+
62+
def generate_adversarial_datasets():
63+
# load train and test dataset
64+
train_df, test_df = load_dataset(const.DATASET_NAME, const.DATASET_SPLIT_DATE)
65+
66+
# load saved forecasting model
67+
model = forecasting_model(const.SEQ_LENGTH, const.FEATURE_DIM, const.FORECAST_HORIZON)
68+
model.load_weights(const.MODEL_NAME)
69+
print("loaded trained forecasting model ...")
70+
71+
# preprocess test dataset based on train dataset standard scaler
72+
non_categorical_features = ['actual_load', 'bsl_t', 'brn_t', 'zrh_t', 'lug_t', 'lau_t', 'gen_t', 'stg_t', 'luz_t']
73+
features_scaler = StandardScaler()
74+
features_scaler.fit(train_df[non_categorical_features])
75+
test_df[non_categorical_features] = features_scaler.transform(test_df[non_categorical_features])
76+
77+
# generate adversarial datasets for various variation in temperature (in Fahrenheit)
78+
for i in range(1, const.TEMPERATURE_VARIATION + 1):
79+
adversarial_df = gradient_estimation(test_df, model, temp_variation=i)
80+
# rescale features to actual values using train data standard scaler
81+
adversarial_df[non_categorical_features] = features_scaler.inverse_transform(adversarial_df[non_categorical_features])
82+
# save generated adversarial dataset
83+
adversarial_df.to_csv('data/adversarial_dataset_temp_' + str(i) + ".csv")
84+
print(f'Generated adversarial dataset for temperature variation={i}F')
85+
86+
87+
if __name__ == "__main__":
88+
generate_adversarial_datasets()
14 MB
Binary file not shown.

constants.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Dataset Constants
2+
DATASET_NAME = "data/actual_dataset.csv"
3+
DATASET_SPLIT_DATE = "2017-5-7"
4+
5+
# Forecasting Model Hyperparameters
6+
SEQ_LENGTH = 24
7+
FEATURE_DIM = 77
8+
FORECAST_HORIZON = 1
9+
TRAIN_NUM_EPOCHS = 30
10+
BATCH_SIZE = 32
11+
12+
# Gradient Estimation Hyperparameters
13+
MODEL_NAME = 'output/load_forecasting_model_weights.h5'
14+
ALPHA = 0.01
15+
GRAD_NUM_EPOCHS = 10
16+
BETA = 0.9
17+
DELTA = 0.05
18+
BOUND = 0.4
19+
TEMPERATURE_VARIATION = 5

0 commit comments

Comments
 (0)