Maschinelles Lernen im Energiesektor oder nicht nur jeder kann morgen zuschauen

Eine genaue Vorhersage zukünftiger Ereignisse ist in vielen Bereichen eine vielversprechende und interessante Aufgabe: von der Wettervorhersage bis zur Fintech (Börsenkurse, Wechselkurse). Maschinelles Lernen kann heute den Zeit- und Arbeitsaufwand für Managemententscheidungen erheblich reduzieren.  Etwa ein halbes Jahr lang experimentierte

unser Data Science-Team bei NORBIT mit verschiedenen Modellen des maschinellen Lernens, um Klassifizierungs- und Regressionsprobleme zu lösen und B2B-Geschäftsprozesse zu optimieren. Als jedoch die Aufgabe der Vorhersage der Zeitreihen auftauchte, stellte sich heraus, dass die verfügbaren Materialien zu diesem Thema im Netzwerk nicht ausreichten, um eine schnelle Lösung zu entwickeln.


Das Wesentliche der Aufgabe war es , den stündlichen Verbrauch der gesamten Stadt für die nächsten drei Tage (72 Stunden) mit maximaler Genauigkeit (durchschnittlicher absoluter Fehler in Prozent oder MAPE <3%) für die Bedürfnisse eines großen Stromerzeugungsunternehmens vorherzusagen.

Der maximal zulässige Fehler von 3% ist ein sehr hoher Indikator für die Genauigkeit von Prognosen. Vor dem Einsatz von maschinellem Lernen lag der Fehler im Bereich von 3 bis 23%, was direkt zu finanziellen Verlusten führte, da bei unzureichender Stromerzeugung zusätzliche Kapazitäten auf dem Stromgroßhandelsmarkt (der viel teurer als selbst erzeugt ist) mit Überproduktion gekauft werden mussten - auf dem Markt verkauft billiger als sie in die Stadt verkaufen könnten. Der negative Effekt der Abweichung der geplanten Last von der tatsächlichen Last ist auch eine Änderung der Frequenz des Wechselstroms im Netz.

Viele Faktoren beeinträchtigen die Erzielung einer hohen Prognosegenauigkeit, z. B. bei einem plötzlichen Temperaturanstieg greifen Menschen sofort nach Konsolen von Klimaanlagen, während sie beim Absenken Heizungen aus den Mezzaninen erhalten. In den Ferien oder bei großen Fußballspielen gehören Fernseher usw. Während einige Ereignisse zyklisch und möglicherweise vorhersehbar sind, fehlen andere vollständig. Nehmen wir an, plötzlich hörte das Stahlgeschäft aufgrund eines Unfalls auf zu arbeiten und alle Prognosen verwandelten sich in einen Kürbis (in unserer Zielstadt gab es keine großen Produktionsunternehmen). Es gab ein ziemlich interessantes Beispiel für ein Problem bei der Erstellung von Prognosen im Energiesektor, als einer der Spitzenwerte bei Änderungen der erzeugten Strommenge durch ein auf einem See gestrandetes Schiff verursacht wurde und der Kapitän mit den Eigentümern von Wasserkraftwerken übereinstimmte, um die Wasserabgabe zu verringern.Natürlich konnte das Modell des maschinellen Lernens dieses Ereignis nicht auf der Grundlage historischer Daten vorhersagen.

Die gute Nachricht für uns ist, dass der Kunde solche „Sondertage“ in Bezug auf die Leistungsbeschreibung angegeben hat, die einen durchschnittlichen Prognosefehler von 8% zuließen.

Der Kunde hat lediglich die historischen Daten des stündlichen Stromverbrauchs für 3 Jahre und den Namen der Stadt angegeben.


Die erste Aufgabe zur Vorbereitung der Erstellung eines maschinellen Lernmodells ist die Visualisierung und die Suche nach zusätzlichen Faktoren, die die Prognose beeinflussen können. Der Grad eines solchen Einflusses wird zweckmäßigerweise visuell unter Verwendung einer Wärmekarte der Korrelation von Attributen bewertet. Das dunkle Quadrat bedeutet in diesem Fall die bedingungslose umgekehrte Beziehung der Größen (je größer ein Wert, desto kleiner der andere und umgekehrt), weiß - direkt:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt


# df - pandas DataFrame  
sns.heatmap(df.corr());


Soweit ich weiß, werden in den Modellen, die zur Vorhersage des Stromverbrauchs in der Russischen Föderation verwendet werden, zwei Faktoren verwendet - die Geschichte des Verbrauchs und die Temperaturprognose. 


Im Winter und Herbst ist der Energieverbrauch höher - niedrige Temperaturen und relativ kurze Tageslichtstunden. Der Faktor „Wochentag“ wirkt sich auch auf den Energieverbrauch aus - am Wochenende sinkt er stark. Verbrauchsspitzen treten am Mittwoch oder Donnerstag auf.





Innerhalb eines Tages an Wochentagen treten die Spitzenwerte des Energieverbrauchs nach 11 bis 12 Stunden auf und fallen nach neun Uhr abends allmählich mit einem starken Abfall ab. 


Alles ist wie in den Lehrbüchern:

Tägliche Winter- und Sommerverbrauchspläne für Industrie- und Kulturzentren. Quelle

Modellieren


Prophet


Die erste Idee, wie man die Aufgabe so schnell wie möglich erledigt, ist, Prophet von Facebook zu nehmen. Ich hatte bereits Erfahrung damit und erinnerte mich daran, dass „es einfach und schnell funktioniert“. Der Prophet möchte die Spalten "ds" und "y" im Pandas-Datenrahmen sehen, d. H. Datum im Format JJJJ-MM-TT HH: MM: SS, und das Ziel ist der Verbrauch pro Stunde (Arbeitsbeispiele finden Sie in der Dokumentation ).

df = pd.read_pickle('full.pkl')
pjme = df.loc[:'2018-06-01 23:00'].rename(columns={'hour_value': 'y'}) 
pjme['ds'] = pjme.index
split_date = '2018-03-01 23:00'
pjme_train = pjme.loc[pjme.index <= split_date]
pjme_test = pjme.loc[pjme.index > split_date]


playoffs = pd.DataFrame({
  'holiday': 'playoff',
  'ds': df[(df.rest_day == 1)|(df.is_weekend == 1)].index,
  'lower_window': 0,
  'upper_window': 1,
})


model = Prophet(holidays=playoffs)
model.fit(pjme_train.reset_index())
forecast = model.predict(df=pjme_test.reset_index())



Die Vorhersagen des „Propheten“ sahen objektiv aus, aber der Fehler war 7–17% höher als der zulässige, daher habe ich keine weiteren Experimente mit diesem Rahmen durchgeführt.

Sarimaax


Der zweite Versuch, dieses Problem zu lösen, war die Verwendung von SARIMAX. Die Methode zur Auswahl der Koeffizienten ist recht zeitaufwändig, aber es war möglich, den Prognosefehler im Vergleich zum Propheten auf 6 bis 11% zu reduzieren. 

Leider blieb nur das Ergebnis für die wöchentlichen Vorhersagen übrig, aber es waren diese Vorhersagedaten, die ich als zusätzliche Merkmale in den Boosting-Modellen verwendete.

Für die Prognose müssen Sie die SARIMA-Parameter (p, d, q) (P, D, Q, s) auswählen:

  • p ist die autoregressive Reihenfolge (AR), mit der Sie die vergangenen Werte der Zeitreihe addieren können (kann ausgedrückt werden als "morgen wird es wahrscheinlich schneien, wenn es in den letzten Tagen geschneit hat");
  • d ist die Integrationsreihenfolge der Quelldaten (sie bestimmt die Anzahl der vorherigen Zeitpunkte, die vom aktuellen Wert subtrahiert werden sollen);
  • q ist die Reihenfolge des gleitenden Durchschnitts (MA), mit der wir den Modellfehler in Form einer linearen Kombination der zuvor beobachteten Fehlerwerte ermitteln können;
  • P - p, aber saisonal;
  • D - d, aber saisonal;
  • Q - q, aber saisonal;
  • s ist die Dimension der Saisonalität (Monat, Quartal usw.).

In der saisonalen Erweiterung des wöchentlichen Verlaufs sehen Sie den Trend und die Saisonalität, mit denen saisonale_Zusammensetzung angezeigt werden kann:

import statsmodels.api as sm
sm.tsa.seasonal_decompose(df_week).plot()


Gemäß den Graphen der Autokorrelation und der partiellen Autokorrelation wählte er die anfänglichen Näherungen der Parameter für das SARIMAX-Modell aus: p = 0, P = 1, q = 2, Q = 2.

sm.graphics.tsa.plot_acf(df_week.diff_week[52:].values.squeeze(), lags=60, ax=ax)
sm.graphics.tsa.plot_pacf(df_week.diff_week[52:].values.squeeze(), lags=60, ax=ax)


Und die endgültig ausgewählten Werte sind (0, 2, 2) x (1, 2, 0, 52). Infolgedessen sah das Diagramm der Verbrauchsprognose folgendermaßen aus:


Erhöhen


Zu diesem Zeitpunkt stellte sich heraus, dass ohne die Verwendung zusätzlicher externer Faktoren die erforderliche Genauigkeit nicht erreicht werden konnte. Dies sind vor allem Wetterfaktoren: Temperatur, Druck, Luftfeuchtigkeit, Windstärke und -richtung, Niederschlag. 

Der erste Versuch war der Versuch, ein Wetterarchiv in Yandex zu kaufen. Die Jungs haben eine exzellente API und technischen Support, aber speziell in unserer Stadt gab es ziemlich viele Lücken in den Daten. Infolgedessen habe ich das Archiv im Dienst openweathermap.org für 10 US-Dollar gekauft. Es ist wichtig zu beachten, dass Prognosefehler trotz der Tatsache, dass das Wetter ein sehr nützlicher Faktor ist, das endgültige MAPE-Modell offensichtlich beeinträchtigen. Ich traf Informationen, dass die richtige Lösung für dieses Problem im Training wäre, nicht die tatsächlichen historischen Werte des Wetters zu verwenden, sondern Wettervorhersagen für drei Tage, aber leider hatte ich keine solche Gelegenheit. 

Ich habe auch Zeichen der Tageszeit, des Wochentags, der Feiertage, der Seriennummer des Tages im Jahr sowie eine große Anzahl von Zeitverzögerungen und Durchschnittswerten für frühere Perioden hinzugefügt.

Alle Daten nach der Skalierung (MinMaxScale) und One Hot Encoding (die Werte jedes kategorialen Elements werden zu separaten Spalten mit 0 und 1, sodass das Element mit drei Werten zu drei verschiedenen Spalten wird und die Einheit nur in einer von ihnen enthalten ist) habe ich in einer kleinen verwendet Wettbewerb der drei beliebten Boost-Modelle XGBoost, LightGBM und CatBoost. 

XGBoost


Über XGBoost wurde viel Gutes geschrieben. Auf der SIGKDD-Konferenz 2016 vorgestellt, sorgte er für Furore und nimmt immer noch eine führende Position ein. Interessantes Material für diejenigen, die noch nichts davon gehört haben.

Bereiten wir die Daten für das Modell vor: 

from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.model_selection import train_test_split


def gen_data(
    stop_day = '2018-06-01 23:00', 
    drop_columns = [],
    test_size=0.15,
    is_dummies=True,
    is_target_scale=False):

    df = pd.read_pickle('full.pkl')
    df = df.loc[:stop_day]
    scaler = MinMaxScaler()
    target_scaler = StandardScaler()
    y = df.hour_total
    
    if is_target_scale:        
        y = target_scaler.fit_transform(y.values.reshape(-1, 1)).reshape(1,-1)[0]
        
    X = df.drop(['hour_value', *drop_columns], axis=1)
    num_columns = X.select_dtypes(include=['float64']).columns
    X[num_columns] = scaler.fit_transform(X[num_columns])
    
    if is_dummies:
        X = pd.get_dummies(X)

    train_count_hours = len(X) - 72
    valid_count_hours = len(X) - int(len(X) * 0.2)
    X_test  = X[train_count_hours:]
    y_test  = y[train_count_hours:]
    X = X[:train_count_hours]
    y = y[:train_count_hours]

    #           ,       
    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=test_size, random_state=42)
    train_dates = X_train.index
    val_dates = X_test.index
    return X, y, X_train, X_val, y_train, y_val, X_test, y_test, target_scaler


Ich unterrichte ein Modell mit den besten zuvor ausgewählten Hyperparametern:

X, y, X_train, X_val, y_train, y_val, X_test, y_test, _ = gen_data(
    stop_day = stop_day, 
    drop_columns = drop_columns,
    test_size=0.1,
    is_dump=True,
    is_target_scale=False)


params_last = {
    'base_score': 0.5, 
    'booster': 'gbtree', 
    'colsample_bylevel': 1, 
    'colsample_bynode': 1, 
    'colsample_bytree': 0.4, 
    'gamma': 0,
    'max_depth': 2, 
    'min_child_weight': 5, 
    'reg_alpha': 0, 
    'reg_lambda': 1, 
    'seed': 38,
    'subsample': 0.7, 
    'verbosity': 1,
    'learning_rate':0.01
}


reg = xgb.XGBRegressor(**params_last, n_estimators=2000)
print(X_train.columns)
reg.fit(X_train, y_train,
        eval_set=[(X_val, y_val)],
        early_stopping_rounds=20,
        verbose=False)


y_pred = reg.predict(X_test)


Lightgbm


LightGBM ist ein Framework von Microsoft, dessen Hauptvorteil die Lerngeschwindigkeit bei wirklich großen Datenmengen ist. Im Gegensatz zu XGBoost kann LightGBM auch mit Kategorien arbeiten und benötigt weniger Speicher. Hier dann ist es mehr.

import lightgbm as lgb
from lightgbm import LGBMRegressor
from sklearn.metrics import mean_squared_error


stop_day = '2018-06-01 23:00'
start_day_for_iterate = datetime.strptime(stop_day, '%Y-%m-%d %H:%M')
test_size = 0.2

X, y, X_train, X_val, y_train, y_val, X_test, y_test, _ = gen_data(
    stop_day = stop_day, 
    drop_columns = drop_columns,
    test_size=test_size,
    is_dump=True,
    drop_weekend=False,
    is_target_scale=False)


model = LGBMRegressor(boosting_type='gbdt'
                num_leaves=83
                max_depth=-1
                learning_rate=0.008
                n_estimators=15000
                max_bin=255
                subsample_for_bin=50000
                min_split_gain=0
                min_child_weight=3,
                min_child_samples=10
                subsample=0.3
                subsample_freq=1
                colsample_bytree=0.5
                reg_alpha=0.1
                reg_lambda=0
                seed=38,
                silent=False
                nthread=-1)


history = model.fit(X_train, y_train, 
            eval_metric='rmse',
            eval_set=[(X_val, y_val)],
            early_stopping_rounds=40,
            verbose = 0)


y_pred = model.predict(X_test, num_iteration=model.best_iteration_)


Catboost


CatBoost ist eine erweiterte Bibliothek zur Gradientenverstärkung für Open-Source-Entscheidungsbäume von Yandex . Der vorteilhafte Unterschied des Modells besteht in der Bequemlichkeit der Arbeit mit Datensätzen, die kategoriale Attribute enthalten. Sie können Daten mit Kategorien in das Modell übertragen, ohne sie in Zahlen umzuwandeln, und sie zeigen Muster von Hand nichts muss verdreht werden, und die Qualität der Vorhersage bleibt hoch.

cat_features=np.where(X.dtypes == 'category')[0]
eval_dataset = Pool(X_test, y_test)

model = CatBoostRegressor(learning_rate=0.5,
                          eval_metric='RMSE'
                          leaf_estimation_iterations=3,
                          depth=3)

model.fit(X_train, y_train,
          eval_set=(X_val, y_val),
          cat_features=cat_features,
          plot=True,
          verbose=True)

y_pred = model.predict(X_test)


XGBoost vs. LightGBM vs. Catboost


Um zahlreiche Artikel nicht zu wiederholen, werde ich von hier aus eine Vergleichstabelle geben .


Um den MAPE-Fehler zu berechnen, habe ich den letzten bekannten Monat (28 Tage) genommen und beim Verschieben des Fensters mit einer Auflösung von einer Stunde eine Prognose für die nächsten 72 Stunden erstellt.

Und in meinem spontanen Wettbewerb waren die Preise:

3. Platz: mein Favorit - XGBoost - mit der niedrigsten Prognosequalität;
2. Platz: CatBoost als bequemster und „all out of the box“;
1. Platz: LightGBM als schnellster und genauester.

Für einen genaueren Vergleich der Modelle habe ich R2 (R-Quadrat oder Bestimmungskoeffizient, der zeigt, wie stark sich die bedingte Varianz des Modells von der Varianz der realen Werte unterscheidet) und RMSLE (Root Mean Squared Logarithmic Error oder Root Mean Square Logarithmic Error, dh der Abstand) verwendet zwischen zwei Punkten in der Ebene - realer Wert und vorhergesagt). 

MetrikenLightgbmCatboostXGBoostProphetSarimaax
r20,941370,939840,929090,814350,73778
Msle0,024680,024770,012190,008290,00658

Aber das alles natürlich in Bezug auf meine Aufgabe. Bei anderen Daten in unterschiedlichen Händen kann alles völlig anders sein. 

Nächstes Mal werde ich eine eigene Geschichte über die Vorhersage des Abflusses von Mitarbeitern für die Planung von Einstellungen in unserem Unternehmen erzählen.

Source: https://habr.com/ru/post/undefined/


All Articles