![]() |
Feature Engineering |
Alle Machine Learning Algorithmen arbeiten auf Vektor-basierten Daten $x \in \mathbb{R}^n$.
Was, wenn unsere Ausgangsdaten keine Vektoren sind?
Beispiel: Wir wollen einen Spam Classifier bauen.
From: BILL GATES FOUNDATION <al8622787@gmail.com>
Subject: HELLO
Reply me urgently i have a very important Donation to share with you.
Have A Blessed Day
Regards
Mr. Bill Gates Foundation
Text Vectorization
Ansatz: Buchstaben → Zahlen
"Hallo Welt" → $[8,1,12,12,15,0,23,5,12,20]$
Nachteile
Ansatz: Wörter zählen
Für jedes Wort im Text wollen wir wissen, wie oft es vorkommt.
"John likes to watch movies. Mary likes movies too."
→ {"John":1,"likes":2,"to":1,"watch":1,
"movies":2,"Mary":1,"too":1}
Um diesen "bag of words" in Vektorform zu bringen, müssen wir jedem Wort einen Vektor Index zuordnen.
| "John" | 0 |
| "likes" | 1 |
| "to" | 2 |
| "watch" | 3 |
| "movies" | 4 |
| "Mary" | 5 |
| "too" | 6 |
"John likes to watch movies" → $[1, 1, 1, 1, 1, 0, 0]$
"Mary likes movies too" → $[0, 1, 0, 0, 1, 1, 1]$
Um Speicher zu sparen, können wir uns auf die häufigsten n Worte beschränken
Oder auf Wörter, die mindestens (oder höchstens) in x% der Dokumente vorkommen
Vorteile "bag of words" Modell:
Nachteile
"Mary likes John" = "John likes Mary"
"movie" ≠ "movies"
Um Wortverwandtschaften besser zu finden kann ein Stemmer verwendet werden:
"running" → "run"
Z.B.: NLTK
Wie kodieren wir Strings, die keine sinnvollen Wörter sind?
From: BILL GATES FOUNDATION <al8622787@gmail.com>
Ansatz: Zähle nicht Wörter, sondern N-Gramme
3 Gramme: "example@th-bingen.de"
→ "exa", "xam", "amp", "mpl", ...
Zum Beispiel: Dictionary für die 10000 häufigsten 3-Gramme
Es gibt 17576 3-Gramme der 26 Kleinbuchstaben und 456976 4-Gramme.
Oft macht es Sinn Großbuchstaben auf Kleinbuchstaben zu mappen.
Zu großes N bei den N-Grammen ist oft schädlich
Wort N-Gramme sind auch möglich (so bleibt mehr Kontext erhalten)
2 Gramme: "Reply me urgently i have a very important"
→ "Reply me", "me urgently", "urgently i", "i have", ...
Bei Strings als Input Daten kommt es oft sehr auf das Problem an.
Beispiel: "Berlinstraße 109, 55411 Bingen"
| Stadt | Bingen | One-hot oder Hashing Trick |
| PLZ | 55411 | Buckets oder Hashing Trick |
| Straße | Berlinstraße | Hashing Trick |
| Hausnummer | 109 | Auf 0-1 skalieren |
In sklearn: CountVectorizer
Eine verbesserte Zählweise ist term frequency-inverse document frequency (TF-IDF):
\[ \operatorname{tf}(t, x) = \frac{\text{Wie oft kommt } t \text{ in } x \text{ vor}}{\text{Anzahl Worte in } x} \]
\[ \operatorname{idf}(t) = \log(\frac{m}{\text{Anzahl Dokumente, die } t \text{ enthalten}}) \]
\[ \operatorname{tfidf}(t,x) = \operatorname{tf}(t, x) \cdot \operatorname{idf}(t) \]
In sklearn: TfidfVectorizer
Ein Nachteil ist, dass wir immer ein Dictionary mitschleppen müssen.
Wie entscheiden wir, wenn ein neues Wort ein anderes im Dictionary ersetzen soll?
Hashing Trick
Wir brauchen eine Hash Funktion
$\operatorname{hash}(t) \to \{0, 1, 2, \ldots, n-1\}$.
Vorteile
Nachteile
In sklearn: HashingVectorizer
Umgang mit Bilddaten
Beispiel: Handschrifterkennung:
Ein Graustufen Bild entspricht einem 2D Array mit Größe $M \times N$.
Jeder Eintrag enspricht einem Pixel und gibt die Intensität wieder.
Ein Farbbild hat noch eine Dimension mehr für den Farbkanal ($M \times N \times C$).
$C$ ist entweder 3 (RGB) oder 4 (RGBA).
import cv2
img = cv2.imread('image.png')
oder
import cv2
img = cv2.imread('image.png', cv2.IMREAD_GRAYSCALE)
Unsere Input Vektoren müssen meistens die gleiche, konstante Größe haben.
Skaliere alle Bilder auf die gleiche Größe
resized = cv2.resize(img, (200, 200))
reshaped = resized.reshape([200 * 200 * 3])
One-hot Encoding von kategorische Daten:
Eine Dimension für jede Kategorie
Beispiel: Wochentag
$ x \in \{\text{Montag}, \text{Dienstag}, \text{Mittwoch},\\ \text{Donnerstag}, \text{Freitag}, \text{Samstag}, \text{Sonntag}\}$
\[ \phi(\text{"Mittwoch"}) = \left[ {\begin{array}{c} 0 \\ 0 \\ 1 \\ 0 \\ 0 \\ 0 \\ 0 \end{array}} \right] \]
In sklearn: OneHotEncoder
Manchmal funktioniert ein Modell besser, wenn wir reelle Werte diskretisieren.
| $x = 3.2 \Rightarrow \tilde x = \begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \end{bmatrix}$ | $x = -1.2 \Rightarrow \tilde x = \begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \end{bmatrix}$ |
| $x = 3.5 \Rightarrow \tilde x = \begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \end{bmatrix}$ | $x = 17.14 \Rightarrow \tilde x = \begin{bmatrix} 0 \\ 0 \\ 0 \\ 1 \end{bmatrix}$ |
Müssen entscheiden:
In sklearn: sklearn.preprocessing.KBinsDiscretizer
Können wir hier mit logistischer Regression einen sinnvollen Classifier bauen?
Eindimensionale Variante:
Ansatz: Quadratische Features
\[ \phi(x) = \left[ {\begin{array}{c} x \\ x^2 \end{array}} \right] \]
Analog für mehr Dimensionen:
\[ x = \left[ {\begin{array}{c} x^{(1)} \\ x^{(2)} \\ \vdots \\ x^{(n)} \end{array}} \right] \quad \phi(x) = \left[ {\begin{array}{c} 1 \\ x^{(1)} \\ x^{(2)} \\ \vdots \\ x^{(n)} \\ x^{(1)} x^{(1)} \\ x^{(1)} x^{(2)} \\ \vdots \\ x^{(1)} x^{(n)} \\ \vdots \\ x^{(n)} x^{(n)} \end{array}} \right] \]
Neue Dimension: $x^{(1)} x^{(1)} + x^{(2)} x^{(2)}$
https://commons.wikimedia.org/wiki/File:Kernel_trick_idea.svg
Achtung: Bei $n$ Eingangs Features und Grad $d$ bekommen wir $O(n^d)$ polynomiale Features
In sklearn: PolynomialFeatures
Viele Algorithmen gehen davon aus / funktionieren besser, wenn die Feature Mittelwert 0 und Standard Abweichung 1 haben.
\[ \phi(x) = \frac{x - \mu}{\sigma} \]
$\mu$ ist der Mittelwert der Trainingsdaten
$\sigma$ ist die Standardabweichung der Trainingsdaten
Achtung: Feature, die one-hot encoded oder sehr dünn besetzt sind, sollten nicht noch skaliert werden
Bag of words Modelle können dann nicht mehr mit sparsen Vektoren gespeichert werden.
Erst skalieren, dann polynomielle Features bilden.
In sklearn: StandardScaler
Ignorieren von Outliern: RobustScaler
Wenn Datenpunkte fehlen können wir diese manchmal trotzdem verwenden. Wir müssen nur entscheiden, wie wir den Datenpunkt auffüllen.
Bei skalierten Daten sind 0 und Mittelwert das gleiche.
In sklearn: sklearn.impute.SimpleImputer
Oft können wir Domänenwissen beim Preprocessing einfließen lassen.
Beispiel: Wir haben als Input ein Audiosignal
$\Rightarrow$ Wir verwenden den Logarithmus der Amplitude als Feature.
Beispiel: Wir haben als Input wieder ein Audiosignal
$\Rightarrow$ Wir wenden die Fouriertransformation auf die Daten an, bevor wir sie weiter verwenden.
Beispiel: Wir wollen Gesten in einem Bild erkennen
$\Rightarrow$ Wir nehmen win zusätzliches Bild nur vom Hintergrund auf und verwenden es, um den Hintergrund herauszurechnen.
Als Daumenregel wollen wir: