AI-United » Allgemein » Feature-Engineering – Verstehen - Kontinuierliche Numerische Daten

Feature-Engineering – Verstehen – Kontinuierliche Numerische Daten

Strategien für die Arbeit mit kontinuierlichen numerischen Daten

Kontinuierliche Numerische Daten

Einführung

Die Aussage „Daten regieren die Welt“ ist eine treffende Paraphrase der berühmten Wahrheit „Geld regiert die Welt“, passend an unsere heutige digitale fortschrittliche Zeit. Die Daten, die sich von der Größe und dem Umfang unterscheiden, versorgen jedes intelligente System und dienen als Grundlage für einen oder mehrere auf maschinellem Lernen, tiefem Lernen oder statistischen Methoden basierenden Algorithmen. Eine der wichtigsten Aufgaben von Rohdaten ist es die entwicklung solcher Merkmale, die das Verstehen und die Anwendung von KI Algorithmen ermöglichen.

Wie wird die Pipeline des maschinellen Lernens erzeugt?

Eine End-zu-End-Pipeline ist ein Bestandteil jedes intelligenten Systems. Angefangen mit der Aufnahme von Daten, wendet sie verschiedene Datenverarbeitungstechniken an, deren Aufgabe ist, die Verarbeitung und Entwicklung von bedeutsamen Merkmalen und Attributen. Am häufigsten werden statistische Modelle oder Modelle des maschinellen Lernens für das Modellieren auf diesen Merkmalen gewählt, die dann in weiteren Vorgängen angewandt werden, wenn dies notwendig ist. Unten sehen Sie die Darstellung der Standard-Pipeline aus dem CRISP-DM-Industriestandard-Prozessmodell.

Standard-Pipeline des maschinellen Lernens

Wenn wir die Rohdaten aufnehmen und dann auf diesen Daten verschiedene Modelle erstellen, bekommen wir oft keine gewünschten Ergebnisse und Leistungen, daraus. Der Grund dafür liegt an der begrenzten Intelligenz der Algorithmen, die wichtige Merkmale aus den Rohdaten nicht gewinnen kann (einige automatisierte Techniken für die Merkmalsentwicklung können Deep Learning Modelle ermöglichen).

In der Abbildung oben ist die Standart-Pepiline des maschinellen Lernens zu sehen, die den Datenaufbereitungsprozess veranschaulicht. In diesem Artikel werden verschiedene Methoden beschrieben, wie man bedeutungsvolle Attribute oder Merkmale aus den Rohdaten nach der erforderlichen Vorverarbeitung bekommt.

Motivation

Als bedeutender Bestandteil des Aufstellens jedes intelligenten Systems wird Feature Engineering betrachtet. Für die Unterstützung des automatisierten maschinellen Lernens werden viele neuere Methoden wie Deep Learning und Meta-Heuristiken verwendet, trotzdem bleibt dieses Problem domänenspezifisch, da die Leistung jedes Systems, von den Merkmalen abhängig ist. Vor dem Modellieren verbringen oft Datenwissenschaftler 70% ihrer Zeit in der Datenvorbereitungsphase, insofern ist Feature Engineering für die sowohl Kunst als auch Wissenschaft. Ein paar Zitaten von einigen bekannten Personen in der Welt der Informatik beweisen, wie wichtig das Feature Engineering in unserer heutigen digitalen Zeit ist.

„Merkmale sind kompliziert, nehmen viel Zeit in Anspruch und erfordern spezielles Wissen. ‚Angewendetes maschinelles Lernen‘ ist eigentlich Feature Engineering.“

Prof. Andrew Ng.

Dieses Zitat zeigt, warum die Datenwissenschaftler fast 70% ihrer Zeit im Engineering von Merkmalen verbringen: Dieser Prozess ist schwierig und zeitaufwändig und sieht sowohl Domänenwissen als auch mathematische Berechnungen voraus.

„Feature Engineering wird als Prozess der Umwandlung von Rohdaten in Merkmale betrachtet. Es bildet das Grundproblem der Vorhersagemodelle besser ab und führt zu einer verbesserten Modellgenauigkeit bei ungesehenen Daten.“

Dr. Jason Brownlee

Diese Aussage zeigt, dass das Feature-Engineering ein sehr komplizierter Prozess ist, dessen Aufgabe die Umwandlung von Daten in Merkmale ist, die dann als Eingabedaten für Modelle des maschinellen Lernens dienen. So helfen diese gut qualitativen Merkmale, die Gesamtmodellleistung zu verbessern. Dabei ist es auch zu beachten, dass Merkmale vom Grundproblem sehr abhängig sind. Trotz der Konformität der Aufgaben des maschinellen Lernens in verschiedenen Szenarien unterscheiden sich die erhaltenen Merkmale stark voneinander. Z. B. wenn die E-Mails in Spam und Nicht-Spam oder handgeschriebene Ziffern zugeordnet werden.

Prof. Pedro Domingos von der Universität in Washington schreibt in seinem Artikel „Ein paar nützliche Dinge über maschinelles Lernen“ folgendes:

„Manchmal sind einige Projekte im Bereich des maschinellen Lernens erfolgreich, und manchmal haben die Mißerfolg. Wo liegt der Unterschied? Als einer der wichtigsten Faktoren sind dann die verwendeten Merkmale zu nennen.“

Prof. Pedro Domingos

Das letzte Zitat gehört Xavier Conort, dem weltberühmten Kaggler aus Singapur. Damit motiviert er uns zum Feature-Engineering und erklärt, dass die meisten Probleme im Bereich maschinellen Lernens in der Praxis häufig auf Kaggle zu finden sind, zu dem jeder Zugriff hat.

„Die Algorithmen, die wir verwendeten, sind für Kagglers Standard. … Der größte Teil aller unserer Bemühungen wird in das Feature-Engineering investiert. … Um das Risiko einer Modellüberanpassung zu vermeiden, werden alle Merkmale sorgfältig bearbeitet und einige davon verworfen.“

Xavier Conort

Merkmale verstehen

Gewöhnlich wird ein Merkmal als eine spezifische Darstellung der Rohdaten betrachtet. Sie bilden ein individuelles, messbares Attribut, das typisch durch eine Spalte in einem Datensatz dargestellt wird. Wenn wir einen generischen zweidimensionalen Datensatz betrachten, so können wir feststellen, dass jede Beobachtung durch eine Zeile und jedes Merkmal in einer Spalte dargestellt wird, die für eine Beobachtung am wertvollsten ist.

In der Abbildung oben ist zu sehen, dass jede Zeile gewöhnlich einen Merkmalsvektor zeigt. So wird eine zweidimensionale Merkmalsmatrix aus dem gesamten Merkmalssatz über alle Beobachtungen gebildet, auch als Merkmalssatz (Featureset) bekannt. Da dabei zweidimensionale Daten dargestellt werden, ist es mit Dataframe oder Kalkulationstabellen zu vergleichen. Diese numerischen Matrizen oder Tensoren werden für die Arbeit der Algorithmen im maschinellen Lernen verwendet. Aus diesem Grund ist die Aufgabe der meisten Feature-Engineering-Techniken die Umwandlung von Rohdaten in einige numerische Darstellungen, die von diesen Algorithmen leicht verstanden werden können.

Nach der Datenmenge unterscheidet man zwei Haupttypen von Merkmalen. Inhärente Rohmerkmale werden direkt aus dem Datensatz ohne zusätzliche Datenmanipulation oder Engineering erhalten. Abgeleitete Merkmale werden normalerweise vom Feature-Engineering erhalten, dabei ist es wichtig, dass Merkmale aus vorhandenen Datenattributen erhalten werden. Als ein einfaches Beispiel kann man das Erstellen eines neuen Merkmals „Alter“ aus einem Mitarbeiterdatensatz nennen. Es enthält „Geburtsdatum“ und setzt voraus, dass das Geburtsdatum vom aktuellen Datum subtrahiert wird.

Man unterscheidet verschiedene Arten und Formate von Daten, einschließlich strukturierter und unstrukturierter Daten. Unsere Aufgabe ist es hier, verschiedene Feature-Engineering-Strategien für den Umgang mit strukturierten kontinuierlichen numerischen Daten zu beschrieben.

Feature-Engineering für numerische Daten

Numerische Daten sind typische Daten in Form von Skalarwerten, die Beobachtungen, Aufzeichnungen oder Messungen schildern. Unter numerischen Daten versteht man kontinuierliche Daten und nicht diskrete Daten, die als kategoriale Daten repräsentiert werden. Numerische Daten werden auch als ein Vektor von Werten bezeichnet, wobei jeder Wert im Vektor ein bestimmtes Merkmal aufweisen kann. Ganzzahlen und Float Daten sind die am häufigsten verwendeten und weit verbreiteten numerischen Datentypen für kontinuierliche numerische Daten. Vor dem Eingeben von numerischen Daten direkt in die Modelle maschinellen Lernens, müssen zuerst Merkmale erstellen werden, die für das Szenario, das Problem und die Domäne sehr wichtig sind. Deshalb wird Feature-Engineering gebraucht. Wenn wir uns jetzt für Python entscheiden, können wir einige Strategien für das Feature-Engineering für numerische Daten betrachten. Zuerst werden folgende notwendige Abhängigkeiten aufgeladen (gewöhnlich in einem Jupyter-Notebook).

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as spstats

%matplotlib inline
Rohe Maßeinheiten

Als Grundlage für numerische Rohdaten wird der Kontext und das Datenformat genommen, um dann sie direkt in Modelle maschinellen Lernens einzugeben. Die Hauptaufgabe der Merkmale ist die Darstellung von Werten und Zählwerten. Als Beispiel nehmen wir den Pokémon-Datensatz, der auch auf Kaggle leicht zu finden ist.

poke_df = pd.read_csv('datasets/Pokemon.csv', encoding='utf-8') poke_df.head()

Pokémon ist eine große Medien-Franchise. Sie ist als ein Symbol für Taschenmonster namens Pokémon bekannt und beschäftigt sich mit diesen fiktiven Charakteren. Sie werden als fiktive Tiere mit Supermächten betrachten! Dieser Datensatz besteht aus diesen Charakteren mit verschiedenen Statistiken für jeden Charakter. Jeder Charakter hat seine eigene Statistik. Die Summe von Charakteren bildet den Datensatz.

Werte

Im  Dataframe-Snapshot in der Abbildung oben ist deutlich zu sehen, dass numerische Rohwerte von verschiedenen Attributen  dargestellt werden und direkt verwendet werden können. Im folgenden Ausschnitt sind einige dieser Merkmale genauer betrachtet.

poke_df[['HP', 'Attack', 'Defense']].head()

Im obigen Dataframe sind Attribute geschildert, die als Merkmale für die Trefferpunkte, Angriffs- und Verteidigungswerte jedes Pokémons dienen. Tatsächlich verwenden diese Felder auch einige zugrundeliegende statistische Messwerte.

poke_df[['HP', 'Attack', 'Defense']].describe()

Gute Ideen zu statistischen Maßen in solchen Merkmalen wie Häufigkeiten, Durchschnitt, Standardabweichung und Quartile sind hier zu finden.

Berechnungen

Die Häufigkeiten oder Vorkommen bestimmter Attribute bilden Merkmale, die als noch eine Form von Rohmessungen weiter betrachtet wird. Als Beispiel nehmen wir Daten aus dem Millionen-Song-Datensatz. Er enthält die Anzahl oder Häufigkeit von Liedern, die von verschiedenen Benutzern gehört wurden.

popsong_df = pd.read_csv('datasets/song_views.csv', 
                          encoding='utf-8')
popsong_df.head(10)

Folgender Snapshot stellt das listen_count-Feld deutlich dar, dass solche numerische Merkmale direkt verwendet, die sich auf Frequenz bzw. Anzahl basieren.

Binarisierung

Um ein Modell für die Lösung eines Problems zu erstellen, braucht man nicht unbedingt Rohhäufigkeiten oder –häufigkeit. Beim Erstellen eines Empfehlungssystems für Liedempfehlungen sind die Interessen von Personen von großer Bedeutung. Die Häufigkeit des Abhörens von Liedern ist unwichtig, da unser Forschungsfeld das Repertoire der Lieder beinhaltet. In diesem Fall wird ein binäres Merkmal gegenüber dem Merkmal bevorzugt, das als seine Grundlage die Berechnung hat. Das Feld listen_count wird  so aussehen.

watched = np.array(popsong_df['listen_count']) 
watched[watched >= 1] = 1
popsong_df['watched'] = watched

Man kann hier auch die scikit-learn’s Binarizer-Klasse aus dem Modul preprocessing verwenden, um dieselbe Aufgabe anstelle von numpy -Arrays auszuführen.

from sklearn.preprocessing import Binarizer

bn = Binarizer(threshold=0.9)
pd_watched = bn.transform([popsong_df['listen_count']])[0]
popsong_df['pd_watched'] = pd_watched
popsong_df.head(11)

Aus dem obigen Snapshot können wir deutlich sehen, dass beide Methoden das gleiche Ergebnis erzielt haben. So erhalten wir ein binäres Merkmal, das anzeigt, ob das Lied von jedem Benutzer gehört wurde oder nicht, was dann in einem relevanten Modell weiter verwendet wird.

Abrunden

Für kontinuierliche numerische Attribute wie Proportionen oder Prozentwerten benötigt man keine Rohwerte mit hoher Genauigkeit. Diese Präzisionsprozentsätze werden in numerische Ganzzahlen abgerundet, die dann direkt als Rohwerte oder sogar als kategoriale Merkmale verwendet werden. Für die Darstellung der Speichereinheiten und deren prozentualen Beliebtheit nehmen wir einen Dummy-Datensatz und veranschaulichen dieses Konzept.

items_popularity = pd.read_csv('datasets/item_popularity.csv',  
                               encoding='utf-8')
items_popularity['popularity_scale_10'] = np.array(
                   np.round((items_popularity['pop_percent'] * 10)),  
                   dtype='int')
items_popularity['popularity_scale_100'] = np.array(
                  np.round((items_popularity['pop_percent'] * 100)),    
                  dtype='int')
items_popularity

Die obigen Ausgangsdaten zeigen, dass wir zwei Rundungsformeln ausprobiert haben. Die Aufgabe dieser Merkmale ist es, die Popularitäten der Einheiten jetzt sowohl auf einer Skala von 1 bis 10 als auch auf einer Skala von 1 bis 100 zu schildern. Abhängig vom Szenario und Problem werden diese beiden Werte als numerische oder kategoriale Merkmale betrachtet.

Interaktionen

Die Ausgabeantworten (diskrete Klassen oder kontinuierliche Werte) werden von überwachten Modellen des maschinellen Lernens modelliert und treten als Funktion der Variablen von Eingabemerkmalen auf. Eine einfache lineare Regressionsgleichung wird so dargestellt:

wo die Eingabemerkmale durch Variablen abgebildet werden;

mit Gewichten oder Koeffizienten, die mit bezeichnet sind;

das Ziel ist die Vorhersage der Antwort y.

In diesem einfachen linearen Modell ist die Beziehung zwischen Ausgangs- und Eingangsdaten gezeigt, die sich ausschließlich auf den individuellen, separaten Eingabemerkmalen basieren.

Da die Merkmalsvariablen oft als ein Teil des Eingabe-Merkmal-Satzes betrachtet werden, macht es in einigen Szenarien Sinn, die Interaktionen dazwischen zu prüfen und zu erfassen. Die Erweiterung der obigen linearen Regressionsformulierung mit Interaktionsmerkmalen kann so dargestellt werden:

wo die Merkmale dargestellt werden als;

bezeichnend die Interaktionsfunktionen.

Einige Interaktionsmerkmale für unser Pokémon-Datasatz werden so aussehen.

atk_def = poke_df[['Attack', 'Defense']]
atk_def.head()

Der Ausgabe-Dataframe zeigt, dass es zwei numerische (kontinuierliche) Merkmale gibt, Attack und Defence. Wenn scikit-learn angewendet wird, können Merkmale bis zum 2. Grad ausgebaut werden.

from sklearn.preprocessing import PolynomialFeatures

pf = PolynomialFeatures(degree=2, interaction_only=False,  
                        include_bias=False)
res = pf.fit_transform(atk_def)
res

Output
------

array([[    49.,     49.,   2401.,   2401.,   2401.],
       [    62.,     63.,   3844.,   3906.,   3969.],
       [    82.,     83.,   6724.,   6806.,   6889.],
       ..., 
       [   110.,     60.,  12100.,   6600.,   3600.],
       [   160.,     60.,  25600.,   9600.,   3600.],
       [   110.,    120.,  12100.,  13200.,  14400.]])

In der angeführten Merkmalsmatrix werden insgesamt fünf Merkmale dargestellt, zu denen auch neue Interaktionsmerkmale gehören. Der Maß jedes Merkmals in der Matrix ist wie folgt zu sehen.

pd.DataFrame(pf.powers_, columns=['Attack_degree',  
                                  'Defense_degree'])

Anhang diesen Ausgabedaten sehen wir, was jedes Merkmal eigentlich aus den hier dargestellten Daten repräsentiert wird. Wenn wir das wissen, können wir jedes Merkmal benennen wie folgt. Dies erleichtert uns das Verständnis und gibt die Möglichkeit, einem Merkmal einen besseren, leichteren und einfacheren Namen zu vergeben.

intr_features = pd.DataFrame(res, columns=['Attack', 'Defense',  
                                           'Attack^2', 
                                           'Attack x Defense',  
                                           'Defense^2'])
intr_features.head(5)

Im folgenden Dataframe werden die eigentlichen Merkmale zusammen mit ihren Interaktionsmerkmalen dargestellt.

Binning

Bei der Arbeit mit rohen, kontinuierlichen numerischen Merkmalen haben wir einige Probleme. Das erste Problem besteht in der häufigen Verteilungsverzerrung der Werte in diesen Merkmalen, da einige Werte ziemlich oft vorkommen, während andere ziemlich selten sind. Das andere Problem liegt an unterschiedlichen Wertebereichen, wenn beispielsweise die Anzahl der Ansichten bestimmter Musikvideos ungewöhnlich groß ist (Despacito ist sehr anschaulich!) und der anderen aber sehr klein. Wenn diese Merkmale direkt verwendet werden, können viele Probleme entstehen, die das Modell beeinträchtigen werden. Um das zu vermeiden, gibt es einige Strategien, darunter Binning und Transformationen.

Binning, auch als Quantisierung bekannt, dient zur Umwandlung kontinuierlicher numerischer Merkmale in diskrete (Kategorien). Diese diskreten Werte oder Zahlen werden als Kategorien oder Bins betrachtet, deren Zweck das Zusammenfassen und Gruppieren der rohen, kontinuierlichen numerischen Werte ist. Da jedes Bin einen bestimmten Intensitätsgrad darstellt, fällt eine bestimmte Reihe von kontinuierlichen numerischen Werten in ihn hinein. Zu den spezifischen Strategien von Daten-Binning gehören Fixed-Width Binning und adaptives Binning. Als nächstes entnehmen wir mal dem 2016 FreeCodeCamp Developer\Coder survey einige Daten, um über verschiedene Attribute zu forschen, die sich auf Programmierer und Softwareentwickler beziehen.

fcc_survey_df = pd.read_csv('datasets/fcc_2016_coder_survey_subset.csv', 
encoding='utf-8')

fcc_survey_df[['ID.x', 'EmploymentField', 'Age', 'Income']].head()

Die ID.x Variable ist im Grunde eine einzigartige Kennzeichnung für jeden Entwickler, der an der Umfrage teilgenommen hat. Die anderen Felder sind dabei ziemlich selbsterklärend.

Fixed-Width Binning

Wie der Name schon sagt, haben wir beim Fixed-Width Binning mit bestimmten fixierten Breiten für jedes der Bins zu tun. Sie werden gewöhnlich vom Benutzer vorgegeben, der die Daten analysiert. Aufgrund einiger Domänenkenntnisse, Regeln oder Begrenzungen wird jedem Bin einen bestimmten Wertebereich zugewiesen. Wenn wir Rohwerte binnen wollen, sollen wir das auf Rundungen basierendes Binning verwenden, in dem wir die zuvor erwähnte Rundungsoperation benutzen.

Betrachten wir nun das Merkmal Alter aus dem Coder-Survey-Dataset mit seiner Verteilung.

fig, ax = plt.subplots()
fcc_survey_df['Age'].hist(color='#A9C5D3', edgecolor='black',  
                          grid=False)
ax.set_title('Developer Age Histogram', fontsize=12)
ax.set_xlabel('Age', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)

Das Histogramm schildert das Alter des Entwicklers und wie erwartet weist die Verzerrung nach rechts auf (Entwickler mit geringerem Alter). Diese Rohwerte für das Alter werden in bestimmte Bins eingeteilt, die sich auf dem folgenden Schema basieren:

Age Range: Bin
---------------
 0 -  9  : 0
10 - 19  : 1
20 - 29  : 2
30 - 39  : 3
40 - 49  : 4
50 - 59  : 5
60 - 69  : 6
  ... and so on

Wir können nun leicht anwenden, da wir es früher im Abschnitt Rundung gelernt haben. Wir runden die Rohwerte für das Alter ab, indem wir den Zähler nach der Teilung durch 10 nehmen. Nachdem der Zähler durch 10 geteilt wurde, so werden diese Rohwerte für das Alter als abgerundet betrachtet, wie es im früheren Abschnitt Aufrundung beschrieben wurde.

fcc_survey_df['Age_bin_round'] = np.array(np.floor(
                              np.array(fcc_survey_df['Age']) / 10.))

fcc_survey_df[['ID.x', 'Age', 'Age_bin_round']].iloc[1071:1076]

Dank der Aufrundung werden die entsprechenden Bins für jedes Alter ordnungsgemäß zugeordnet. Was ist aber zu tun, wenn mehr Flexibilität notwendig ist? Oder wenn die auf unseren eigenen Regeln \ Logik basierenden Bingewichte bestimmen und festlegen müssen? Binning auf Basis von benutzerdefinierten Bereichen wird uns helfen, diese Ziele zu erreichen. Das folgenden Schema hilft uns, einige benutzerdefinierte Bereiche für das Binning des Alters der Entwickler zu definieren.

Age Range : Bin
---------------
 0 -  15  : 1
16 -  30  : 2
31 -  45  : 3
46 -  60  : 4
61 -  75  : 5
75 - 100  : 6

Wenn für das Binning benutzerdefinierte Werte als Basis genommen werden, so werden die Bins für jeden Entwickler-Altersangabe gekennzeichnet und beide Angaben, das Bin und das entsprechende Label, gespeichert.

bin_ranges = [0, 15, 30, 45, 60, 75, 100]
bin_names = [1, 2, 3, 4, 5, 6]
fcc_survey_df['Age_bin_custom_range'] = pd.cut(
                                           np.array(
                                              fcc_survey_df['Age']), 
                                              bins=bin_ranges)
fcc_survey_df['Age_bin_custom_label'] = pd.cut(
                                           np.array(
                                              fcc_survey_df['Age']), 
                                              bins=bin_ranges,            
                                              labels=bin_names)
# view the binned features 
fcc_survey_df[['ID.x', 'Age', 'Age_bin_round', 
               'Age_bin_custom_range',   
               'Age_bin_custom_label']].iloc[10a71:1076]
Adaptives Binning

Die Verwendung von Fixed-Width Binning hat einen großen Nachteil: Da die Binbereiche manuell festgelegt werden, gehen die irreguläre, nichteinheitliche Bins zu Ende, bei denen als Grundlage die Anzahl von Datenpunkten oder Werten genommen wird, die dann entsprechend in jedes Bin fallen. Einige der Bins können dicht, die anderen dünn besiedelt werden. Es können auch leere Bins entstehen! Adaptives Binning ist eine sicherere Strategie in solchen Szenarien, bei denen sich die Daten für sich selbst sprechen! Für das Festlegen von Binbereichen wird ausschließlich die Datenverteilung verwendet.

Quantile-basiertes Binning ist eine gute Strategie für die Verwendung des adaptiven Binnings. Quantile sind bestimmte Werte oder Grenzwerte, deren Aufgabe ist: die Unterteilung der kontinuierlichen hoch geschätzten Verteilung eines bestimmten numerischen Feldes in diskrete aneinandergrenzende Bins oder Intervalle. Dank den q-Quantilen wird ein numerisches Attribut in q gleiche Partitionen abgetrennt. Am beliebtesten sind folgende Quantile: das 2-Quantil, das als Median bekannt ist und die Datenverteilung in zwei gleiche Bins unterteilt, 4-Quantile, die als Quartile bekannt sind und die Daten in 4 gleiche Bins unterteilen, und 10-Quantiles, die auch als Dezile bekannt sind und 10 gleich breite Bins erzeugen. Betrachten wir nun die Datenverteilung für das Income.

fig, ax = plt.subplots()
fcc_survey_df['Income'].hist(bins=30, color='#A9C5D3', 
                             edgecolor='black', grid=False)
ax.set_title('Developer Income Histogram', fontsize=12)
ax.set_xlabel('Developer Income', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)

In der obigen Verteilung wird eine rechte Verzerrung im Einkommen dargestellt, wobei weniger Entwickler mehr Geld verdienen und umgekehrt. Wenn wir ein 4-Quantile- oder ein Quartil-basierendes adaptives Binning-Schema nehmen, können wir die Quartile leicht wie folgt erhalten.

quantile_list = [0, .25, .5, .75, 1.]
quantiles = fcc_survey_df['Income'].quantile(quantile_list)
quantiles

Output
------
0.00      6000.0
0.25     20000.0
0.50     37000.0
0.75     60000.0
1.00    200000.0
Name: Income, dtype: float64

Visualisieren wir nun diese Quantile im ursprünglichen Verteilungshistogramm.

fig, ax = plt.subplots()
fcc_survey_df['Income'].hist(bins=30, color='#A9C5D3', 
                             edgecolor='black', grid=False)

for quantile in quantiles:
    qvl = plt.axvline(quantile, color='r')
ax.legend([qvl], ['Quantiles'], fontsize=10)

ax.set_title('Developer Income Histogram with Quantiles', 
             fontsize=12)
ax.set_xlabel('Developer Income', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)

Durch rote Linien in der angeführten Verteilung werden die Quartilwerte und das potenzielle Bin angedeutet. Versuchen wir jetzt das auf Quartilen basierendes Binning-System aufzubauen.

quantile_labels = ['0-25Q', '25-50Q', '50-75Q', '75-100Q']
fcc_survey_df['Income_quantile_range'] = pd.qcut(
                                            fcc_survey_df['Income'], 
                                            q=quantile_list)
fcc_survey_df['Income_quantile_label'] = pd.qcut(
                                            fcc_survey_df['Income'], 
                                            q=quantile_list,       
                                            labels=quantile_labels)

fcc_survey_df[['ID.x', 'Age', 'Income', 'Income_quantile_range', 
               'Income_quantile_label']].iloc[4:9]

Dies sollte uns eine gute Idee geben, wie das Quantile-basierete adaptive Binning arbeitet. Das Ergebnis des Binnings führt zu diskret geschätzten kategorialen Merkmalen, und man benötigt möglicherweise einen zusätzlichen Schritt des Feature-Engineerings für die kategorialen Daten, bevor man sie in einem beliebigen Modell verwendet. Im nächsten Teil werden Feature-Strategien für kategoriale Daten kurz betrachtet.

Statistische Transformationen

Oben wurden die negativen Auswirkungen von verzerrten Datenverteilungen betrachtet. In diesem Teil beschäftigen wir uns mit einer anderen Strategie des Feature-Engineerings, indem wir statistische oder mathematische Transformationen verwenden. Es werden die Log-Transformation sowie die Box-Cox-Transformation betrachtet, die zur Power Transform-Funktionsfamilie gehören und gewöhnlich zum Erstellen von monotonen Datentransformationen verwendet werden. Ihre wichtigste Bedeutung besteht darin, dass sie dabei helfen, die Varianz zu stabilisieren, sich eng an der Normalverteilung zu halten und die auf ihrer Verteilung basierende Daten vom Mittelwert unabhängig zu machen.

Log Transformation

Die Log Transformation ist eine der Leistungstransformationsfunktionen und wird mathematisch so dargestellt:

also log von x an der b Basis, ist gleich y. Dies kann dann in

dies zeigt, auf welche Potenz die Basis b angehoben werden muss, um x zu erhalten. Der gewöhnliche Logarithmus verwendet b = e, wo e = 2,71828 allgemein als eulersche Zahl bekannt ist. Man kann auch die Basis = 10 verwenden, die im Allgemeinen im Dezimalsystem gebräuchlich ist.

Log Transformationen sind sinnvoll, wenn man sie auf verzerrte Verteilungen angewendet, da sie dazu neigen, die Werte zu erweitern, die in den Bereich niedrigerer Größen fallen, und die Werte zu komprimieren oder zu reduzieren, die in den Bereich höherer Größen fallen. Log Transformationen neigt im Gegenteil  dazu, die verzerrte Verteilung so normal wie möglich darzustellen. Verwenden wir nun die Log Transformation für das Merkmal Income, das zuvor bereits als Beispiel betrachtet wurde.

fcc_survey_df['Income_log'] = np.log((1+ fcc_survey_df['Income']))
fcc_survey_df[['ID.x', 'Age', 'Income', 'Income_log']].iloc[4:9]

Das Income_log -Feld stellt das transformierte Merkmal nach der Log Transformation dar. Betrachten wir nun die Datenverteilung in diesem transformierten Feld.

income_log_mean = np.round(np.mean(fcc_survey_df['Income_log']), 2)

fig, ax = plt.subplots()
fcc_survey_df['Income_log'].hist(bins=30, color='#A9C5D3', 
                                 edgecolor='black', grid=False)
plt.axvline(income_log_mean, color='r')
ax.set_title('Developer Income Histogram after Log Transform', 
             fontsize=12)
ax.set_xlabel('Developer Income (log scale)', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.text(11.5, 450, r'$\mu$='+str(income_log_mean), fontsize=10)

Anhand des obigen Histogramms ist es deutlich zu sehen, dass die Verteilung normaler oder ganz normal im Vergleich mit der verzerrten Verteilung der Originaldaten ist.

Box-Cox-Transformation

Die Box-Cox-Transformation ist eine weitere beliebte Methode, die zu der Familie der Power-Transform-Funktionen gehört. Diese Funktion setzt voraus, dass die zu transformierenden numerischen Werte positiv sein müssen (ähnlich wie bei der Log Transformation erwartet). Wenn sie negativ sind, hilft das Verschieben mit einem konstanten Wert. Mathematisch wird die Box-Cox-Transformationsfunktion wie folgt dargestellt.

Wenn die resultierende transformierte Ausgabe y eine Funktion der Eingabe x und des Transformationsparameters λ ist und λ = 0 ist, die resultierende Transformation die natürliche Log Transformation ist, die zuvor schon betrachtet wurde. Der optimale Wert von λ wird normalerweise unter Verwendung einer maximalen Wahrscheinlichkeit oder Schätzung der Wahrscheinlichkeit bestimmt. Versuchen wir jetzt die Box-Cox-Transformation auf unseren Entwickler-Dataset anzuwenden. Zuerst wurde der optimale Lambda-Wert aus der Datenverteilung erhalten, indem die Nicht-Null-Werte wie folgt gelöscht werden.

income = np.array(fcc_survey_df['Income'])
income_clean = income[~np.isnan(income)]
l, opt_lambda = spstats.boxcox(income_clean)
print('Optimal lambda value:', opt_lambda)

Output
------
Optimal lambda value: 0.117991239456

Nachdem nun der optimale λ-Wert erhalten wurde, wird die Box-Cox-Transformation für zwei Werte von λ, so dass λ = 0 und λ = λ (optimal), verwendet und dann entsprechend Income transformiert.

fcc_survey_df['Income_boxcox_lambda_0'] = spstats.boxcox(
                                        (1+fcc_survey_df['Income']), 
                                          lmbda=0)
fcc_survey_df['Income_boxcox_lambda_opt'] = spstats.boxcox(
                                            fcc_survey_df['Income'], 
                                              lmbda=opt_lambda)

fcc_survey_df[['ID.x', 'Age', 'Income', 'Income_log', 
               'Income_boxcox_lambda_0',       
               'Income_boxcox_lambda_opt']].iloc[4:9]

Wie erwartet haben Income_log und Income_boxcox_lamba_0 die gleichen Werte. Betrachten wir nun die Verteilung des transformierten Merkmals Income nach der Transformation mit dem Optimum λ.

income_boxcox_mean = np.round(
                      np.mean(
                       fcc_survey_df['Income_boxcox_lambda_opt']),2)

fig, ax = plt.subplots()
fcc_survey_df['Income_boxcox_lambda_opt'].hist(bins=30, 
                     color='#A9C5D3', edgecolor='black', grid=False)
plt.axvline(income_boxcox_mean, color='r')
ax.set_title('Developer Income Histogram after Box–Cox Transform', 
             fontsize=12)
ax.set_xlabel('Developer Income (Box–Cox transform)', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.text(24, 450, r'$\mu$='+str(income_boxcox_mean), fontsize=10)

Die Verteilung sieht ausgewogener aus, ähnlich zu dem, was wir nach der Log-Transformation erhalten haben.

Fazit

Da Feature Engineering ein sehr wichtiger Aspekt des maschinellen Lernens und der Datenwissenschaft ist, entsprechend sollte es niemals ignoriert werden. Man unterscheidet automatisierte Methoden für das Feature-Engineering im Deep Learning und automatisierte Frameworks für maschinelles Lernen wie AutoML (es erfordert gute Merkmale, um gut zu funktionieren). Einige dieser automatisierten Methoden erfordern oft spezifische technische Merkmale, deren Basis die Datentypen, Domäne und das zu lösende Problem beinhalten.

In diesem Artikel wurden ausschließlich beliebte Strategien für das Feature-Engineering für kontinuierliche numerische Daten betrachtet.

Sollten Sie Fragen dazu haben, so können Sie sich an das Team von AI-United.de per Mail oder Q&A wenden.

Quellen: 

https://github.com/dipanjanS/practical-machine-learning-with-python/tree/master/notebooks/Ch04_Feature_Engineering_and_Selection

https://github.com/dipanjanS/practical-machine-learning-with-python

AI-United-Redaktion

Kommentar hinzufügen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.