Beherrschen der Modellinterpretation: Gewinnen Sie tiefere Einblicke in Ihren Machine Learning-Modelle

Gamuchirai Zororo Ndawana | 21 Februar, 2024

Einführung

Im Bereich des maschinellen Lernens denken wir häufig in Form von Kompromissen. Während wir eine Leistungskennzahl optimieren, gehen wir oft Kompromisse bei einer anderen Leistungskennzahl ein. Mit der zunehmenden Entwicklung immer größerer und komplizierterer Modelle werden das Verständnis, die Erklärung und die Fehlersuche zu gewaltigen Aufgaben. Die Feinheiten unter der Oberfläche des Modells, das Entschlüsseln der Gründe, warum unsere Modelle die Entscheidungen treffen, die sie treffen, sind von entscheidender Bedeutung. Wie können wir ohne diese Klarheit zuversichtlich sein, dass wir dieses Modell zu den gewünschten Ergebnissen führen können?  Wir können nicht riskieren, dass das Modell auf unbeabsichtigte Weise funktioniert und unsere Bemühungen umsonst sind! Der vorliegende Artikel geht auf diese komplexen Zusammenhänge ein und soll folgende Themen beleuchten:

  1. Identifizierung entscheidender Merkmale: Welche Merkmale waren für Ihr Modell wichtig?

  2. Entschlüsselung der Auswirkungen einzelner Merkmale: Verstehen, wie jedes Merkmal die Leistung Ihres Modells beeinflusst

  3. Erfassen des kollektiven Merkmalseinflusses: Untersuchung der breiteren Auswirkungen von Merkmalen auf die Vorhersagen des Modells

Unsere Reise führt Sie durch die Feinheiten des maschinellen Lernens und ermöglicht es Ihnen, das Verhalten Ihres Modells besser zu verstehen und zu verändern. Machen Sie sich dieses Wissen zu eigen, und nutzen Sie das wahre Potenzial Ihrer Bemühungen um maschinelles Lernen.


Warum ist das wichtig?

Fehlersuche:

Abb. 1: Die Kunst des Debuggens

„Irren ist menschlich, verzeihen ist göttlich“ ~ Alexander Pope

Auf den ersten Blick mag das Debugging banal erscheinen, aber seine Bedeutung geht über die Grenzen von Bereichen hinaus. Stellen Sie sich schwer fassbare Fehler vor, die sich heimlich vor Laufzeitfehlern verstecken und unbemerkt an Ihnen vorbeiziehen. Mit dem Wissen, das auf diesen Seiten vermittelt wird, sind Sie jedoch in der Lage, die komplizierten Muster zu entschlüsseln, die Ihre Modelle weben. Sie werden erkennen, ob diese Muster mit Ihrem Verständnis der realen Welt übereinstimmen. Im Labyrinth der realen Projekte werden Sie geschickt Ihre eigenen Fehler aufspüren und überwinden, denn sie sind das Sprungbrett zur Meisterschaft.

Besseres Feature Engineering:

Technische Merkmale

Abb. 2: Technische Merkmale

„Kontinuierliche Verbesserung ist besser als verspätete Perfektion“ ~ Mark Twain

Nun wollen wir uns in das bezaubernde Reich des Feature Engineering begeben. Stellen Sie sich einen Kreis vor. Ziehen Sie eine rote Linie entlang des Durchmessers; ein einfacher Akt, der jedoch viel Bedeutung hat. Überlegen Sie nun: Wie oft könnte Ihr Durchmesser den Kreisumfang anmutig umrunden? Die Antwort liegt auf der Hand: etwa 3,145, eine Konstante, die allen als Pi bekannt ist.

Dieses Gedankenexperiment dient uns als Leitfaden, der die Auswirkungen der Präsentation auf unsere Lernfähigkeit verdeutlicht. So wie die Krümmung unseres Kreises seine Passgenauigkeit beeinflusst, so verbessern auch Merkmalstransformationen die Harmonie unseres Datensatzes. Indem wir neue Säulen formen, gehen wir über das Gewöhnliche hinaus und heben unsere Vorhersagegenauigkeit in außergewöhnliche Höhen. Hier wird die Intuition zu unserem Kompass, der uns durch unerforschte Gewässer führt. Aber was passiert, wenn die Intuition versagt? Keine Angst, diese Odyssee enthüllt Strategien, um selbst die verwirrendsten Terrains zu meistern. Aber das ist noch nicht alles, denn wir werden die Wirksamkeit dieser neuen technischen Merkmale, die wir eingeführt haben, auch empirisch messen


Ausrichtung der künftigen Datenerhebung:

Abb. 3: Verbesserte Datenerfassung


„Wenn du die Zukunft wissen willst, schau in die Vergangenheit“ ~ Albert Einstein

Während wir die Datenlandschaft durchqueren, klingt eine uralte Weisheit nach: Der Schlüssel zur Zukunft liegt in den Annalen der Vergangenheit. Sind unsere technischen Möglichkeiten ausgereizt? Keine Angst, denn in der Welt der Daten ist die Vergangenheit unser Leuchtturm. Neue Datentypen winken und versprechen, unseren Modellen Leben einzuhauchen. Einblicke, unsere treuen Begleiter, dienen als Proxys, die uns zu den Edelsteinen inmitten der Datenmengen führen. Mit jeder Erkenntnis, die wir gewinnen, wird unser Weg zu neuen Funktionen klarer, und wir malen die Zukunft in Farben endloser Möglichkeiten.

Verbesserte Entscheidungsfindung:

Abb. 4: Verbesserte Entscheidungsfindung

„Ein wenig Lernen ist eine gefährliche Sache“ ~ Alexander Pope

In der Symphonie des maschinellen Lernens sitzen Sie am Steuer. Das Modell tanzt nach Ihrer Pfeife, ein Beweis für die Entscheidungen, die seinen Vorhersagen Leben einhauchen. Maschinelles Lernen geht über bloße Prognosen hinaus; es dringt in die Tiefen der Erkenntnis vor, wo die wahren Schätze vergraben liegen. Ihr Verständnis, Ihre Beherrschung, bildet das Fundament, auf dem Ihre Ambitionen stehen. Jede Einsicht, ein wertvoller Brocken, beeinflusst Ihre Bemühungen.

Lassen Sie sich also von diesen Worten leiten, wenn wir die Geheimnisse des maschinellen Lernens lüften.


Theoretische Grundlage

In diesem Artikel wollen wir ein Gradient-Boost-Baummodell, das in der CatBoost-Python-Bibliothek verfügbar ist, zur Durchführung von Preisregressionsanalysen einsetzen. Allerdings stellt sich gleich zu Beginn eine bemerkenswerte Herausforderung, die eine genauere Untersuchung des Modells und die Identifizierung einflussreicher Merkmale erforderlich macht. Bevor wir uns mit der Anwendung von Blackbox-Erklärungstechniken für unser Modell befassen, ist es unerlässlich, die unserem Blackbox-Modell innewohnenden Einschränkungen und die Gründe für den Einsatz von Blackbox-Erklärern in diesem Zusammenhang zu verstehen.

Gradient Boosted Trees zeigen eine lobenswerte Leistung bei Klassifizierungsaufgaben; dennoch zeigen sie deutliche Einschränkungen, wenn sie auf spezifische Zeitreihenregressionsprobleme angewendet werden. Diese Bäume, die zur Familie der Modelle des maschinellen Lernens gehören, kategorisieren die Eingaben auf der Grundlage des Zielwerts in Gruppen. Anschließend berechnet der Algorithmus den durchschnittlichen Zielwert innerhalb jeder Gruppe und verwendet diese Gruppendurchschnitte für die Vorhersage. Diese während des Trainings ermittelten Gruppendurchschnitte bleiben bestehen, solange keine weiteres Training durchgeführt wird. Ein kritischer Nachteil ergibt sich aus dieser starren Natur, da Gradient Boosted Trees in der Regel Schwierigkeiten haben, Trends effektiv zu extrapolieren. Wenn das Modell mit Eingabewerten konfrontiert wird, die außerhalb seines Trainingsbereichs liegen, neigt es zu sich wiederholenden Vorhersagen, die sich auf Durchschnittswerte bekannter Gruppen stützen, die den zugrunde liegenden Trend außerhalb des beobachteten Trainingsbereichs möglicherweise nicht genau erfassen.

Darüber hinaus setzt das Modell voraus, dass ähnliche Merkmalswerte ähnliche Zielwerte ergeben, eine Annahme, die nicht mit unserer kollektiven Erfahrung im Handel mit Finanzinstrumenten übereinstimmt. Auf den Finanzmärkten können sich die Kursmuster ähneln, obwohl sie an unterschiedlichen Punkten enden. Diese Divergenz stellt die Annahme des Modells in Frage, dass der generative Prozess Daten erzeugt, die in homogene Gruppen fallen. Folglich führt die Verletzung dieser Annahmen zu Verzerrungen in unserem Modell.

Um diese Beobachtungen zu untermauern, führen wir eine Demonstration für die Leser durch, die dieses Phänomen vielleicht nicht selbst beobachtet haben. Unser Ziel ist es, ein umfassendes Verständnis für alle Leser zu gewährleisten.

Wir beginnen mit dem Laden der notwendigen Abhängigkeiten

#pip install pandas if you haven't installed it allready
#We'll use pandas to store and retrieve data
import pandas as pd       

#pip install pandas-ta if you haven't installed it allready
#We'll use pandas_ta to calculate technical indicators
import pandas_ta as ta    

#pip install numpy if you haven't installed it allready
#We'll use numpy to perform optmized vector calculations 
import numpy as np

import matplotlib.pyplot as plt

#pip install numpy if you haven't installed it allready
#We'll use MetaTrader 5 connect to and control our MetaTrader 5 Terminal 
import MetaTrader5 as MT5

#Standard python library
import time
Dann geben wir unsere Anmeldedaten ein
login = enter_your_login
password =  enter_your_password
server = enter_your_broker_server
Jetzt initialisieren wir unser MetaTrader 5 Terminal und melden uns an
if(MT5.initialize(login=login, password= password, server=server)):
    print("Logged in succesfully")
else:
    print("Failed to initialize the terminal and login")
Erfolgreich eingeloggt
Fordern wir eine Million Zeilen von M1-Daten zum Volatility 75 Index an, oder welches Symbol Sie auch immer bevorzugen.

data = pd.DataFrame(MT5.copy_rates_from_pos("Volatility 75 Index",MT5.TIMEFRAME_M1,0,100000))
data

Marktdaten

Abb. 5: Unsere Marktdaten vom MetaTrader 5 Terminal

Berechnen wir technische Indikatoren, die uns bei der Preisprognose helfen können

#20 period exponential moving average
data["ema_20"] = data.ta.ema(length=20)
#40 period exponential moving average
data["ema_40"] = data.ta.ema(length=40)
#100 period exponential moving average
data["ema_100"] = data.ta.ema(length=100)
#20 period relative strength indicator
data.ta.rsi(length=20,append=True)
#20 period bollinger bands with 3 standard deviations
data.ta.bbands(length=20,sd=3,append=True)
#14 period average true range
data.ta.atr(length=14,append=True)
#Awesome oscilator with default settings
data.ta.ao(append=True)
#Moving average convergence divergence (MACD)
data.ta.macd(append=True)
#Chaikins commidity index
data.ta.cci(append=True)
#Know sure thing oscilator
data.ta.kst(append=True)
#True strength index
data.ta.tsi(append=True)
#Rate of change
data.ta.roc(append=True)
#Slope between 2 points
data.ta.slope(append=True)
#Directional movement
data.ta.dm(append=True)

Einrichten des Ziels

data["target"] = data["close"].shift(-30)

Richten wir unser Blackbox-Modell ein

from catboost import CatBoostRegressor

Vorbereitung unserer Trainings- und Testsplits

train_start = 100
train_end   = 10000

test_start = train_end + 100
test_end = test_start + 30000

predictors = [
    "open",
    "high",
    "low",
    "close",
    "KSTs_9",
    "KST_10_15_20_30_10_10_10_15",
    "CCI_14_0.015",
    "AO_5_34",
    "ATRr_14",
    "BBM_20_2.0",
    "BBP_20_2.0",
    "BBB_20_2.0",
    "BBU_20_2.0",
    "BBL_20_2.0",
    "RSI_20",
    "ema_20",
    "ema_40",
    "ema_100",
    "SLOPE_1",
    "ROC_10",
    "TSIs_13_25_13",
    "TSI_13_25_13",
    "MACD_12_26_9",
    "MACDh_12_26_9",
    "MACDs_12_26_9",
    "DMP_14",
    "DMN_14"
]
target = "target"
Entscheidungsbäume reagieren empfindlich auf Skalierung, daher werden wir die Eingabewerte normalisieren und die ersten Messwerte jedes Merkmals in diesem Array namens „first_values“ speichern.
first_values = {}
#Iterating over the columns in the dataset
for col in data.columns:
    #Which of those columns are part of the model inputs?
    if col in predictors:
        #What was the first value in that column?
        first_values[col] = data[col][train_start]
        data[col] = data[col]/first_values[col]

Durchführung von The Train-Testsplits

train_x = data.loc[train_start:train_end,predictors]
train_y = data.loc[train_start:train_end,target]

test_x = data.loc[test_start:test_end,predictors]
test_y = data.loc[test_start:test_end,target]
Anpassen unseres Blackbox-Modells

cat_full = CatBoostRegressor()
cat_full.fit(train_x,train_y)
Gewinnung von Vorhersagen aus unserem Blackbox-Modell
cat_full_predictions = pd.DataFrame(index=test_x.index)
cat_full_predictions["predictions"] = cat_full.predict(test_x)
cat_full_predictions.plot(label=True)
test_y.plot()

Blackbox-Prognose

Abb. 6: Unsere Blackbox-Prognose


Bei der ersten Betrachtung fällt auf, dass das Vorhersagemodell untypische flache Perioden aufweist. Angesichts unseres umfassenden Überblicks über die typische Implementierung von gradientenverstärkten Algorithmen sollten solche Muster nicht unerwartet sein. Unser Interesse liegt darin, herauszufinden, welche Merkmale positiv zur Leistung unseres Modells beitragen und welche als potenzielle Störquellen dienen können. Dies erfordert eine gründliche Prüfung der Möglichkeiten des Feature Engineering.

Um diese Nuancen zu entschlüsseln, schlagen wir vor, eine Reihe von Blackbox-Erklärern einzusetzen. Bevor man diese Erklärer jedoch blindlings anwendet, muss man unbedingt die zugrunde liegenden Mechanismen des Blackbox-Erklärer-Algorithmus verstehen. Dazu gehört eine kritische Bewertung der Annahmen, unter denen sie arbeitet, sodass wir feststellen können, ob diese Annahmen eingehalten oder möglicherweise verletzt werden.

Wenn wir die Anwendung von Blackbox-Erklärungstechniken mit diesem grundlegenden Verständnis angehen, können wir jede Erklärung mit Bedacht interpretieren. Indem wir die Erklärungstechniken hinterfragen und ihre Relevanz abwägen, können wir den abgeleiteten Erkenntnissen ein angemessenes Maß an Vertrauen zuschreiben. Dieser methodische Ansatz verbessert unsere Fähigkeit, aussagekräftige Informationen zu extrahieren und fundierte Entscheidungen auf der Grundlage der Ergebnisse des Blackbox-Erklärungsprozesses zu treffen.


Blackbox-Erklärungsalgorithmen

Bedeutung der „Drop Column“:

Die Technik der „Drop Column Importance“ ist eine Methode zur Bewertung der Genauigkeit eines Modells, bei der zunächst alle Prädiktoren berücksichtigt und anschließend iterativ eine einzelne Spalte entfernt wird, um die daraus resultierenden Auswirkungen auf die Genauigkeit zu beobachten. Dieser Ansatz liefert wertvolle Einblicke in den spezifischen Beitrag der einzelnen Säulen zur Gesamtleistung des Modells.

Drop Column Importance zeichnet sich durch seine Praktikabilität und Sinnhaftigkeit aus. Die dieser Technik zugrunde liegende Annahme entspricht realen Szenarien, in denen häufig Dateninkonsistenzen auftreten. Unabhängig davon, ob es sich um Unterbrechungen der Internetverbindung, Stromausfälle, ungünstige Wetterbedingungen oder andere unvorhergesehene Ereignisse handelt, bildet die Methodik ein robustes Modell ab, das die Ereignisse in der tatsächlichen Betriebsumgebung widerspiegelt. Diese Realitätsnähe erhöht die Anwendbarkeit und Effektivität der Technik bei der Bewältigung von Szenarien, die in praktischen, alltäglichen Situationen vorkommen können.

Stärken:

Schwachstellen:

Möglichkeiten:

Gefahr:


Bedeutung der Permutation:

Die Permutationsbedeutung bietet eine alternative Methode zur Bewertung der Merkmalsbedeutung, indem die Werte einer bestimmten Spalte nach dem Zufallsprinzip vertauscht werden und die daraus resultierenden Auswirkungen auf die Fehlermetriken des Modells bewertet werden. Der Kerngedanke beruht auf der Annahme, dass, wenn eine Säule eine wesentliche Rolle bei der Beeinflussung des Modells spielt, eine Störung ihrer ursprünglichen Werte zu einem Anstieg der Fehlermetriken und einem Rückgang der Gesamtgenauigkeit führen sollte. Ist die Auswirkung der Säule dagegen minimal, sind die beobachtbaren Veränderungen begrenzt, und in bestimmten Fällen kann es sogar zu einer Verbesserung der Leistung kommen.

Diese Technik bietet eine nuancierte Perspektive auf die Bedeutung der Merkmale und ermöglicht eine dynamische Bewertung des Beitrags jedes Merkmals zur Vorhersagekapazität des Modells. Der Permutationsprozess führt ein Maß an Zufälligkeit ein, das dazu beiträgt, die Empfindlichkeit des Modells gegenüber einzelnen Merkmalen aufzudecken, was ein umfassenderes Verständnis ihrer Bedeutung im Rahmen der Vorhersage ermöglicht.

Stärken:

Schwachstellen:

Möglichkeiten:

Gefahr:

Partial Dependency Plots (PDP) und Individual Conditional Expectation (ICE) Plots:

Um das Verhalten des Modells besser zu verstehen, verwenden wir Partial Dependency Plots (PDP) und Individual Conditional Expectation (ICE) Plots. Partielle Abhängigkeitsdiagramme veranschaulichen, wie sich das vorhergesagte Ergebnis entwickelt, wenn ein einzelner Prädiktor variiert und alle anderen Prädiktoren konstant bleiben. In der Zwischenzeit bieten ICE Plots eine detaillierte Perspektive auf die Bedeutung von Merkmalen, indem sie individuelle Kurven für jede Instanz darstellen und so eine umfassende Analyse der Auswirkungen jedes Merkmals ermöglichen.

Zusammen bieten PDPs und ICE Plots eine robuste visuelle Darstellung, die über aggregierte Trends hinausgeht. PDPs bieten einen Überblick über die Beziehung zwischen einem bestimmten Prädiktor und den Vorhersagen des Modells, während ICE Plots die Nuancen durch die Darstellung einer Reihe von Kurven, die jeweils die individuelle Auswirkung dieses Prädiktors über verschiedene Instanzen hinweg darstellen, näher beleuchten. Dieser kombinierte Ansatz verbessert unsere Fähigkeit, komplizierte Muster und Variationen in der Reaktion des Modells auf verschiedene Prädiktorenwerte zu erkennen, und fördert ein gründlicheres Verständnis der Bedeutung von Merkmalen innerhalb des gesamten Vorhersagerahmens.

Stärken:

Schwachstellen:

Möglichkeiten:

Gefahr:


Shapley Additive Erklärungen (SHAP-Werte):


Lloyd Shapely

Abb. 7: In liebevoller Erinnerung an Lloyd Shapley, der am 12. März 2016 in Tucson, Arizona, verstorben ist. Seine Ideen leben ewig weiter.

Benannt nach ihrem Pionier, Lloyd Shapley, werden SHAP-Werte verwendet, um die Ergebnisse eines Modells zu erklären, indem der Beitrag jedes Merkmals zur Vorhersage zugeordnet wird.

SHAP-Werte können berechnet werden, indem der Output des Modells für alle möglichen Kombinationen von Merkmalen ausgewertet wird. Vereinfacht ausgedrückt, geht es darum, die Werte eines bestimmten Merkmals zu verändern, während die anderen konstant bleiben, und die Auswirkungen auf die Vorhersagen des Modells zu beobachten.

Wie bei einem Spieler in einem Spiel ist der SHAP-Wert für ein Merkmal, das die Leistung des Modells nicht verändert, wenn sein Wert gemischt wird (z. B. wenn ein Spieler keinen Einfluss auf die Leistung des Teams hat, wenn er nicht beteiligt ist), tendenziell niedriger. Wenn sich die Änderung eines Merkmals dagegen signifikant auf die Ergebnisse des Modells auswirkt, ist der SHAP-Wert höher, was auf einen größeren Beitrag hinweist.

Die SHAP-Werte liefern ein quantitatives Maß für den Beitrag der einzelnen Merkmale. Durch den Vergleich der SHAP-Werte für verschiedene Merkmale können Sie feststellen, welche Merkmale einen größeren Einfluss auf die Vorhersagen des Modells haben.

Wenn also ein Merkmal wiederholt weggelassen wird (seine Werte werden gemischt oder variiert) und sich die Leistung des Modells nicht wesentlich ändert, deutet dies darauf hin, dass der Beitrag dieses Merkmals zu den Vorhersagen des Modells relativ gering sein könnte.

Stärken:

Schwachstellen:

Möglichkeiten:

Gefahr:


Mutual Information:

Die Mutual Information (Transinformation oder gegenseitige Information, MI) ist ein Maß für die Menge an Informationen, die das Vorhandensein oder Fehlen einer Variable über eine andere Variable liefert. Sie quantifiziert den Grad der Abhängigkeit oder Assoziation zwischen zwei Variablen.

Stärken:

Schwachstellen:

Möglichkeiten:

Gefahr:


Zum Glück gibt es spezialisierte Python-Bibliotheken, die diesen Prozess vereinfachen und es uns ersparen, den Großteil des Codes von Grund auf neu zu erstellen.

Es ist wichtig zu erwähnen, dass dieser Artikel nicht auf die Feinheiten des Trainings und der Anpassung von Modellen eingeht. Stattdessen wird sie sich auf die Phase nach dem Training konzentrieren und untersuchen, wie die Leistung eines Modells bewertet und interpretiert werden kann. Das mitgelieferte Code-Beispiel erstellt ein Basismodell, das lediglich der Veranschaulichung dient und die Schritte nach dem Entwurf und dem Training Ihres Modells verdeutlicht. 

Fangen wir an

Drop Column Importance

Wir werden den Algorithmus der rekursiven Merkmalseliminierung aus der sklearn-Bibliothek als Implementierung von Drop Column Importance verwenden

from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVR
Der Algorithmus der rekursiven Merkmalseliminierung erwartet von uns ein überwachtes Lernmodell, das er anpassen und bewerten kann.
Das Modell sollte entweder durch seine Koeffizienten oder durch eine dedizierte Funktion Informationen über die Bedeutung des Merkmals liefern.
Das Modell muss nicht dasselbe sein wie das Modell, das wir in unserem Problem verwenden.
Das Modell muss nicht aus der Sklearn-Bibliothek stammen, aber es sollte zumindest einen Sklearn-Wrapper haben.

Wir werden ein lineares Modell verwenden und seine Genauigkeit bewerten, während wir zufällig Merkmale ausschließen.

lm = LinearRegression()
rfe = RFE(lm,step=1)

Das Argument step gibt an, wie viele Merkmale bei jeder Iteration weggelassen werden sollen.

Dann passen wir den Algorithmus der rekursiven Merkmalseliminierung an:

rfe = rfe.fit(train_x,train_y)
Schauen wir uns an, welche Merkmale unser RFE-Algorithmus informativ fand:
rfe.support_
array([ True, True, True, True, False, False, False, False, False, True, False, True, True, True, False, True, True, True, False, False, False, False, False, False, True, False, True])
Wir können auch eine Rangliste der einzelnen Merkmale erhalten:

rfe.ranking_
array([ 1, 1, 1, 1, 2, 5, 14, 11, 13, 1, 7, 1, 1, 1, 8, 1, 1, 1, 12, 15, 3, 4, 9, 10, 1, 6, 1])

Die sklearn-Implementierung stellt uns eine Maske zur Verfügung, die wir auf die Spalten in unserem train_x-Datenframe anwenden können, um die Namen der Spalten zu sehen, die RFE für wichtig hält.

train_x.columns[rfe.support_]
Index(['open', 'high', 'low', 'close', 'BBM_20_2.0', 'BBB_20_2.0', 'BBU_20_2.0', 'BBL_20_2.0', 'ema_20', 'ema_40', 'ema_100', 'MACDs_12_26_9', 'DMN_14'], dtype='object')
Aus allen von uns verwendeten Indikatoren geht hervor, dass die folgenden Indikatoren wertvolle Informationen enthalten:
    1) Open         2) High         3) Low         4) Close         6) KSTs_9         7) KST_10_15_20_30_10_10_10_15         7) All 3 exponentielle gleitende Durchschnitte         8) 4 Komponenten der Bollinger Bänder: 'BBM_20_2.0' 'BBB_20_2.0' 'BBU_20_2.0' 'BBL_20_2.0'         9) MACD: 'MACDs_12_26_9'         10) Directional Movement Minus. Die anderen Indikatoren können Rauschen verursacht haben, aber denken Sie daran, dass es sich nur um Schätzungen handelt, die als Orientierungshilfe dienen!
Wir können nicht davon ausgehen, dass dies der Wahrheit entspricht, aber es ist dennoch eine vernünftige Behauptung, die wir aufstellen können.


Als Nächstes kommen wir zur Bewertung der Permutationsbedeutung

Wir werden eine Python-Bibliothek namens Explain Like I'm 5 (ELI5) verwenden, um den Algorithmus zu implementieren

#pip install eli5 if you don't allready have it installed
import eli5
from eli5.sklearn import PermutationImportance
from sklearn.ensemble import GradientBoostingRegressor
gbr = GradientBoostingRegressor().fit(train_x,train_y)

permutation = PermutationImportance(gbr).fit(test_x,test_y)

Nun bewerten wir die Gewichtung, die der Algorithmus jedem unserer Merkmale zugewiesen hat:

eli5.show_weights(permutation,feature_names = test_x.columns.to_list())


Permutationsbedeutungsgewichte

Abb. 8: Permutation Importgewichte


Die Interpretation der Ergebnisse der Permutationsbedeutung ist einfach. Die Merkmale sind in absteigender Reihenfolge ihrer Wichtigkeit geordnet, wobei die wichtigsten Merkmale ganz oben und die weniger wichtigen ganz unten stehen. Der Gewichtungswert, z. B. „0,0986 +- 0,0256“, gibt die Veränderung der Fehlermetrik des Modells an. Für die Leser, die eine knappe Information suchen, sei noch einmal gesagt, dass bei der Permutationsbedeutung die Werte eines Merkmals zufällig gemischt werden. Wenn das Merkmal einen signifikanten Einfluss auf das Modell hat, sollte diese Randomisierung zu einem Anstieg der Fehlermetriken des Modells führen. Aufgrund des stochastischen Charakters des Algorithmus wird dieser Anstieg als Spanne und nicht als konkreter Wert angegeben.

Umgekehrt gibt es bei Merkmalen, die nicht wichtig sind, entweder keine Veränderung oder in einigen Fällen eine Verringerung der Fehlermetriken des Modells, wenn die Werte zufällig gemischt werden. Diese duale Natur der Ergebnisse — entweder eine Zunahme oder eine Abnahme der Fehlermetriken — liefert einen klaren Hinweis auf die relative Bedeutung jedes Merkmals und trägt zu einer präzisen und verständlichen Interpretation der Ergebnisse der Permutationsbedeutung bei. Daraus können wir schließen, dass die Wichtigkeit von Drop Columns und die Wichtigkeit von Permutationen in Bezug auf die Wichtigkeit von Merkmalen in unserem Modell eine ähnliche Tendenz aufweisen.

Partial Dependence Plots und Individual Conditional Expectation (ICE) Plots:

#Import partial dependence display from sklearn
from sklearn.inspection import PartialDependenceDisplay

for feature_name in predictors:
    PartialDependenceDisplay.from_estimator(cat_full,test_x,[feature_name])
    plt.grid()
    plt.show()

PDP Eröffnungspreise

MACDS PDP

Abb. 9: Partielles Abhängigkeitsdiagramm der Eröffnungspreise und der MACD-Signallinie



Die Darstellung der partiellen Abhängigkeit ist ein wertvolles Hilfsmittel, um zu verstehen, wie eine Änderung einer bestimmten Variable die Vorhersagen unseres Modells beeinflusst. Aus der Analyse der partiellen Abhängigkeit der MACD-Signallinie können wir schließen, dass ein Anstieg der MACD-Signallinie im Allgemeinen mit einem Anstieg des von unserem Modell prognostizierten Schlusskurses einhergeht.

Es ist jedoch wichtig zu beachten, dass die Interpretation von partiellen Abhängigkeitsdiagrammen komplizierter sein kann, wie das folgende MACD-Höhen-Diagramm zeigt.

Partial Depedence Plot der MACD-Höhe

Abb. 10: Partial Depedence Plot der MACD-Höhe

Die Komplexität bei der Interpretation solcher Diagramme kann sich aus nicht-linearen Beziehungen oder Wechselwirkungen mit anderen Variablen ergeben, die eine differenziertere Analyse erfordern. Im Falle der MACD-Höhenkurve sind möglicherweise zusätzliche Überlegungen und Untersuchungen erforderlich, um die genaue Art der Beziehung zwischen der MACD-Höhe und dem vom Modell prognostizierten Schlusskurs aufzudecken.

Diese Erkenntnis unterstreicht die Notwendigkeit einer umfassenden Untersuchung der partiellen Abhängigkeitsdiagramme unter Berücksichtigung der einzigartigen Merkmale jeder einzelnen Variablen und, wenn es Probleme bei der Interpretation gibt, die Suche nach weiteren Erkenntnissen durch zusätzliche Analysen oder Beratung durch Experten.

Individuelle bedingte Erwartungsdiagramme (Individual Conditional Expectation, ICE) werden erstellt, um einen detaillierten Überblick darüber zu geben, wie einzelne Instanzen auf Änderungen einer bestimmten Prädiktorvariablen reagieren. Im Folgenden wird Schritt für Schritt erklärt, wie ICE-Plots erstellt und verwendet werden:

1. Individuelle Vorhersagevariationen:

2. Variieren der Prädiktorvariable:

3. Individuelle Kurven erstellen:

4. Plotten:

5. Auslegung:

6. Vergleichende Analyse:

Verwendung:

In der Tat ist die PDP-Parzelle einfach der Durchschnitt der ICE-Parzellen. Die PDP-Linie ist unten in orange dargestellt.

for feature in predictors:
    PartialDependenceDisplay.from_estimator(cat_full,test_x,[feature],kind='both')

ICE Hoch

ICE EMA

Abb. 11: ICE-Plot des Höchstkurses und des 40-Perioden-EMA

Anhand der beobachteten Diagramme ist ersichtlich, dass ein Anstieg des Höchstpreises oder des exponentiellen gleitenden Durchschnitts keine unmittelbare Auswirkung auf die Vorhersagen unseres Modells hat. Stattdessen scheint es, dass diese Variablen einen signifikanten Anstieg erfahren müssten, bevor eine nennenswerte Änderung der Vorhersagen des Modells zu erwarten ist. Diese Beobachtung deutet darauf hin, dass das Modell nicht sehr empfindlich auf inkrementelle Änderungen dieser besonderen Merkmale reagiert.

Zu den wichtigsten Erkenntnissen aus den Diagrammen gehören:

  1. Schwellenwert-Empfindlichkeit: Das Modell scheint eine Schwellenwertsensitivität aufzuweisen, d. h., es benötigt eine wesentliche oder einen Schwellenwert für die Veränderung des Höchstpreises oder des exponentiellen gleitenden Durchschnitts, bevor es seine Vorhersagen deutlich anpasst.

  2. Schrittweise Reaktion: Inkrementelle oder moderate Änderungen der genannten Merkmale scheinen keine unmittelbaren Anpassungen der Modellvorhersagen zu bewirken. Dieses Merkmal deutet auf eine allmähliche oder gedämpfte Reaktion auf solche Veränderungen hin.

  3. Niedrige Empfindlichkeit: Die Schlussfolgerung, dass das Modell nicht sehr empfindlich auf Änderungen dieser Merkmale reagiert, bedeutet, dass geringe Schwankungen des hohen Preises oder des exponentiellen gleitenden Durchschnitts wahrscheinlich keine erkennbaren Auswirkungen auf das Ergebnis des Modells haben.

Das Verständnis der Empfindlichkeit des Modells gegenüber verschiedenen Merkmalen ist entscheidend für eine effektive Modellinterpretation und Entscheidungsfindung. Diese Erkenntnisse können als Grundlage für weitere Analysen und die Verfeinerung des Modells dienen oder die Beteiligten über die Art der Beziehungen zwischen bestimmten Prädiktoren und den Vorhersagen des Modells informieren.

Wir können auch 2-dimensionale Partial Depedence Plots untersuchen, um die gemeinsame Wirkung von 2 Merkmalen zu sehen:

#Setting up the plot
fig , ax = plt.subplots(figsize=(10,5))
column_names = [('ROC_10','ATRr_14')]

#Plotting 2D PDP
disp_4 = PartialDependenceDisplay.from_estimator(cat_full, test_x[0:1000],column_names, ax=ax)
plt.show()

2D Partial Depedence Diagramm

Abb. 12: 2D Partial Depedence Diagramm des Rate of Change (ROC) und Average True Range (ATR) Indikators.

Die Analyse der 2D-Darstellung der partiellen Abhängigkeit zeigt, dass im Allgemeinen ein Anstieg der Indikatoren sowohl von ROC (Rate of Change) als auch des ATR (Average True Range) mit einem Anstieg der von unserem Modell erwarteten Vorhersage einhergeht. Diese positive Korrelation entspricht dem erwarteten Verhalten des Modells als Reaktion auf Veränderungen dieser Indikatoren.

Die Grafik gibt jedoch auch Aufschluss über die Wechselwirkung zwischen diesen beiden Indikatoren. Vor allem das Fehlen klar definierter Grenzen in der Grafik deutet darauf hin, dass die Beziehung nicht durchgängig stabil ist. Stattdessen gibt es Bereiche mit unterschiedlichen Stärken und Schwächen, was darauf hindeutet, dass der Einfluss der ROC- und ATR-Indikatoren auf die Vorhersagen des Modells in den verschiedenen Regionen des Diagramms variieren kann.

Das Vorhandensein von zufälligen Enklaven schwacher Erwartungen innerhalb der Grenzen starker Erwartungen unterstreicht den dynamischen Charakter der Interaktion zusätzlich. Es ist wichtig zu beachten, dass das beobachtete Verhalten durch den Stichprobenansatz beeinflusst werden kann, insbesondere wenn aus Gründen der Recheneffizienz eine Teilmenge des Datenrahmens verwendet wurde.

Hier einige Überlegungen und mögliche Auswirkungen:

  1. Einschränkungen bei der Probenahme: Die beobachteten Unstimmigkeiten könnten teilweise darauf zurückzuführen sein, dass aus Gründen der Recheneffizienz eine kleinere Stichprobe verwendet wurde. Ein breiterer Stichprobenansatz könnte einen umfassenderen Blick auf die Interaktion ermöglichen.

  2. Nicht-Linearität oder Wechselwirkungen: Das Fehlen klar definierter Grenzen und das Vorhandensein gemischter Punkte lassen auf mögliche Nichtlinearitäten oder Wechselwirkungen zwischen den ROC- und ATR-Indikatoren schließen. Weitere Untersuchungen, möglicherweise mit größeren Stichproben, könnten differenziertere Muster zutage fördern.

  3. Robustheit des Modells: Das Verständnis der Variabilität der Interaktion hilft bei der Bewertung der Robustheit des Modells und seiner Empfindlichkeit gegenüber verschiedenen Kombinationen von Eingangsmerkmalen.

Zusammenfassend lässt sich sagen, dass die Interpretation der partiellen 2D-Abhängigkeitsdarstellung wertvolle Einblicke in die Reaktion des Modells auf Veränderungen der ROC- und ATR-Indikatoren bietet. In Anbetracht des möglichen Einflusses der Stichprobengröße auf die beobachteten Muster könnten weitere Untersuchungen und, falls machbar, eine umfangreichere Stichprobenstrategie die Genauigkeit dieser Ergebnisse verbessern.


Shapely Additive Erklärungen (SHAP) Werte

#pip install shap if you don't have it installed
#Import SHAP
import shap

#Initialise the shap package
shap.initjs()

Ein paar Dinge sind zu beachten:

Unser Datensatz hat starke Korrelationsterme, und das ist ein großes Problem bei der Berechnung von SHAP-Werten, da es die Annahme verletzt, dass die Merkmale unabhängig sind:

Für diese Übung werden wir eine vierte Lösung verwenden, bei der spezielle SHAP-Algorithmen zum Einsatz kommen, die für korrelierte Daten ausgelegt sind. Baumbasierte SHAP-Algorithmen können gut mit Korrelationen umgehen.

#Initialise shap value calculator
tree_explainer = shap.TreeExplainer(cat_full)

#Store SHAP values
shap_values = tree_explainer.shap_values(test_x)

#Plot SHAP values
shap.summary_plot(shap_values,test_x)

SHAP-Werte

Abb. 13: SHAP-Wert-Plots

So interpretieren wir die zusammenfassende SHAP-Darstellung:

  1. Merkmal Wichtigkeit: Die oben auf der y-Achse aufgeführten Merkmale haben einen stärkeren Einfluss auf den Anstieg des Schlusskurses, während die Merkmale am unteren Ende der y-Achse einen stärkeren Einfluss auf den Rückgang des Schlusskurses haben. 

  2. Richtung der Auswirkungen: Jedes Merkmal hat farbkodierte Punkte entlang der x-Achse. Blaue Punkte symbolisieren niedrigere Merkmalswerte und rote Punkte höhere Merkmalswerte. Die horizontale Positionierung dieser Punkte gibt Aufschluss darüber, wie sich der Wert des Merkmals auf den Schlusskurs auswirkt. Unsere SHAP-Wert-Berechnungen zeigen also, dass unser High_Low_Sread nicht sehr aussagekräftig für den Schlusskurs ist.

  3. Größe der Position: Die horizontale Position jedes Punktes entlang der x-Achse stellt die Größe der Auswirkung dar, daher haben extrem hohe und niedrige Werte relativ stärkere Auswirkungen auf den Schlusskurs als die Merkmale Volumen, Offen und High_Low_Spread in diesem Beispiel.


Mutual Information

from sklearn.feature_selection import mutual_info_regression
mi_scores = mutual_info_regression(train_x, train_y)
mi_scores = pd.Series(mi_scores, name="MI Scores", index=train_x.columns)
mi_scores = mi_scores.sort_values(ascending=False)
mi_scores

ema_100                                       1.965130

ema_40                                         1.960548

ema_20                                         1.933651

BBM_20_2.0                                   1.902066

BBL_20_2.0                                   1.895867

BBU_20_2.0                                  1.881435

high                                             1.795941

low                                              1.786879

close                                            1.783567

open                                            1.777118

TSIs_13_25_13                              0.232247

ATRr_14                                        0.215980

MACDs_12_26_9                            0.214559

KST_10_15_20_30_10_10_10_15    0.208868

KSTs_9                                         0.205177

MACD_12_26_9                            0.174518

TSI_13_25_13                              0.168086

AO_5_34                                     0.128653

BBB_20_2.0                                 0.104481

RSI_20                                         0.095368

MACDh_12_26_9                          0.076360

DMP_14                                      0.060191

DMN_14                                     0.048856

ROC_10                                     0.042115

BBP_20_2.0                               0.028558

CCI_14_0.015                            0.022320

SLOPE_1                                   0.004144

def plot_mi_scores(scores):
    scores = scores.sort_values(ascending=True)
    width = np.arange(len(scores))
    ticks = list(scores.index)
    plt.barh(width, scores)
    plt.yticks(width, ticks)
    plt.title("Mutual Information Scores")
plt.figure(dpi=100, figsize=(8, 5))
plt.grid()
plot_mi_scores(mi_scores)


Darstellung der gegenseitigen Informationen (Mutual Information Plot)

Abb. 14: Werte der gegenseitigen Information

Wir beobachten einen konsistenten Konsens über alle unsere Blackbox-Erklärungstechniken, was darauf hindeutet, dass die in diesen Erklärungen immer wieder hervorgehobenen Merkmale wahrscheinlich wesentliche Informationen enthalten, die wir effektiv nutzen können. Der Exponential Moving Average, die Bollinger Bänder und die Kurse von Open, High, Low und Close rangieren durchweg an der Spitze. Wenn wir also die zentrale Tendenz messen, können wir logischerweise zu dem Schluss kommen, dass diese Merkmale sehr informativ sind und wir daher alles andere weglassen können. 

Lassen Sie uns unsere Ergebnisse validieren.

Wir werden 2 Modelle verwenden, eine einfache lineare Regression und unsere leistungsstarke Blackbox. Mit den Erkenntnissen, die wir aus unseren Blackbox-Erklärungsmethoden gewonnen haben, wählen wir die Merkmale sorgfältig aus. 

Zunächst importieren wir die benötigten Abhängigkeiten.

from sklearn.linear_model import LinearRegression
from catboost import CatBoostRegressor
from sklearn.metrics import mean_squared_error

    Dann werden wir zuerst das einfachere Modell anpassen

    #First we fit the simpler model
    lm = LinearRegression()
    
    lm.fit(train_x.loc[:,["open","high","low","close"]],train_y)

    Wir werden dann beurteilen, wie gut das einfachere Modell zu den Trainingsdaten passt.

    lm_predictions = pd.DataFrame(lm.predict(train_x.loc[:,["open","high","low","close"]]), index = train_y.index)
    lm_fit = lm.predict(train_x.loc[:,["open","high","low","close"]])
    residuals = pd.DataFrame(train_y - lm_fit)

    Wir werden nun unser leistungsfähigeres Blackbox-Modell einsetzen, um die Residuen unseres einfacheren Modells zu lernen, und wir werden unserem Blackbox-Modell die Merkmale geben, die wir für am informativsten halten

    #Now we bring in our more powerfull black-box model
    cat = CatBoostRegressor()
    cat.fit(
        train_x.loc[:,["BBM_20_2.0","BBL_20_2.0","BBU_20_2.0","ema_40","ema_20","ema_100"]],
        residuals)

    Wir werden einen Vergleich der Fehlerniveaus auf den Testdaten durchführen, indem wir die Leistung unseres anfänglichen Blackbox-Modells, das alle verfügbaren Merkmale verwendet, mit der Genauigkeit eines verfeinerten Modells vergleichen. Das verfeinerte Modell enthält die Unterstützung einer einfacheren linearen Regression und verwendet strategisch eine Teilmenge sorgfältig ausgewählter Merkmale.

    lm_test_predictions = pd.DataFrame(lm.predict(test_x.loc[:,["open","high","low","close"]]),index=test_y.index)
    cat_full_test_predictions = cat_full.predict(test_x[predictors])
    
    cat_residuals_predictions = pd.DataFrame(cat.predict(test_x.loc[:,["BBM_20_2.0","BBL_20_2.0","BBU_20_2.0","ema_40","ema_20","ema_100"]]),index=test_y.index)

    Wir werden das Fehlerniveau unserer ursprünglichen Blackbox bewerten:

    full_error = mean_squared_error(test_y,cat_full_test_predictions)

    und wir vergleichen sie mit dem Fehlerniveau unseres neuen Blackbox-Hybrids:

    hybrid_predictions = lm_test_predictions.iloc[:,0] + cat_residuals_predictions.iloc[:,0]
    hybrid_error = mean_squared_error(test_y, hybrid_predictions)
    delta_error = full_error - hybrid_error
    (delta_error / full_error) * 100

    94.428

    Unser neuer Blackbox-Hybrid hat die Fehlerkennzahlen um 94 % verbessert.

    hybrid_predictions.plot()

    Hybride Blackbox-Prognose

    Abb. 15: Neue Blackbox-Prognose

    Von größter Bedeutung ist die bemerkenswerte Verbesserung der Realitätsnähe unserer neuen Blackbox-Prognosen, die nicht mehr die anfangs beobachteten unnatürlichen flachen Perioden aufweisen. Es ist wichtig, dass der Leser erkennt, dass wir jetzt eine verbesserte Genauigkeit mit einem gestrafften Satz von Merkmalen erreichen. Dies unterstreicht den Grundsatz, dass weniger mehr ist, wenn es effektiv umgesetzt wird.


    Bringen wir alles unter einen Hut

    Jetzt können wir alle Komponenten in eine umfassende Handelsstrategie integrieren.

    MARKET_SYMBOL = "Volatility 75 Index"
    DEVIATION = 100
    VOLUME = 0
    symbol_info = MT5.symbol_info(MARKET_SYMBOL)
    VOLUME = symbol_info.volume_min * 1
    def preprocess(df):
        #20 period exponential moving average
        df["ema_20"] = df.ta.ema(length=20)
        #40 period exponential moving average
        df["ema_40"] = df.ta.ema(length=40)
        #100 period exponential moving average
        df["ema_100"] = df.ta.ema(length=100)
        #20 period bollinger bands with 3 standard deviations
        df.ta.bbands(length=20,sd=2,append=True)
        df = df.loc[100:,:]
    def fetch_prices():
        current_prices = pd.DataFrame()
        current_prices = pd.DataFrame(MT5.copy_rates_from_pos(MARKET_SYMBOL,MT5.TIMEFRAME_M1,0,200))
        preprocess(current_prices)
        return(current_prices)
    def normalise_prices(raw_data):
        for col in raw_data.columns:
            if col in first_values:
                raw_data[col] = raw_data[col] / first_values[col]
    model_forecast = 0
    def hybrid_forecast(model_1,model_2):
        market_data = fetch_prices()
        normalise_prices(market_data)
        forecast_1 = model_1.predict(market_data.loc[199:200,["open","high","low","close"]])
        forecast_2 = model_2.predict(market_data.loc[199:200,["BBM_20_2.0","BBL_20_2.0","BBU_20_2.0","ema_40","ema_20","ema_100"]])
        out = forecast_1 + forecast_2
        return(out)
    INITIAL_BALANCE = MT5.account_info().balance
    CURRENT_BALANCE = 0
    if __name__ == "__main__":
        while True:
            
            #Account standing
            info = MT5.account_info()
            CURRENT_BALANCE = info.balance
            profit = CURRENT_BALANCE - INITIAL_BALANCE
            
            model_forecast = hybrid_forecast(lm,cat)
            print("Current forecast: ",model_forecast)
            
            #We have no open positions
            if(MT5.positions_total() == 0):
                print("No open positions")
                
                #Buy
                if(model_forecast > MT5.symbol_info(MARKET_SYMBOL).ask):
                    print("Following model forecast buy")
                    MT5.Buy(MARKET_SYMBOL,VOLUME)
                    last_trade = 1
                            
                #Sell    
                elif(model_forecast < MT5.symbol_info(MARKET_SYMBOL).ask):
                    print("Following model forecast sell")
                    MT5.Sell(MARKET_SYMBOL,VOLUME)
                    last_trade = 0
                    
            elif(MT5.positions_total() > 0):
                print("Checking model forecast")
                
                if((model_forecast > MT5.symbol_info(MARKET_SYMBOL).ask) & (last_trade == 0)):
                    print("Model is forecasting a move that hurts our exposure. Closing positions")
                    MT5.Close()
                
                elif((model_forecast < MT5.symbol_info(MARKET_SYMBOL).ask) & (last_trade == 1)):
                    print("Model is forecasting a move that hurts our exposure. Closing positions")
                    MT5.Close()
                    
            print("Total Profit/Loss: ",profit)
            time.sleep(60)

    Current forecast: [239100.04622681] No open positions Following model forecast buy Total Profit/Loss: 0.0


    Schlussfolgerung

    Je weiter wir fortschreiten, um größere und komplexere Modelle zu trainieren, desto schwieriger wird es, sie zu verstehen, zu erklären und zu debuggen. Wenn wir nicht genau verstehen, was unsere maschinellen Lernmodelle unter der „Motorhaube“ tun und warum sie zu den Entscheidungen kommen, die sie treffen, können wir nicht sicher sein, dass das Modell so funktioniert, wie wir es beabsichtigen. Das Unvermögen, Modelle zu verstehen und Fehler zu beheben, rechtfertigt nicht die zusätzliche Komplexität, die der Einsatz dieser Techniken mit sich bringt.  Der Wert, den wir konsequent aus einem Werkzeug ziehen können, ist auf das Maß beschränkt, in dem wir das Werkzeug verstehen. Zu diesem Zweck wurde dieser Artikel verfasst, um Ihnen mehr Vertrauen in Ihre Modelle zu geben und tiefere Einblicke in die sich drehenden Rädchen in der berüchtigten Blackbox der maschinellen Lernmodelle zu ermöglichen.