Nächste-Nachbarn-Klassifikation mit sklearn

Einführung

sklearn text with  animals

Die zugrundeliegenden Konzepte des K-Nächsten-Nachbarn-Klassifikators (KNN) finden Sie im Kapitel [Nächste-Nachbarn-Klassifikation] (naechste_nachbarn_klassifikation.php) unseres Tutorials über Maschinelles Lernen. In diesem Kapitel zeigten wir auch einfache Funktionen, die in Python geschrieben wurden, um die grundlegenden Prinzipien zu demonstrieren.

Anstatt diese Funktionen zu nutzen, - obwohl sie beeindruckende Ergebnisse zeigten, - empfehlen wir, die Funktionalitäten des sklearn-Moduls zu verwenden.

kNN-Klassifikatoren von sklearn

neighbors ist ein Paket des sklearn module, welches Funktionalitäten für Nächste-Nachbarn-Klassifikatoren zur Verfügung stellt.

Die Klassen in sklearn.neighbors können sowohl numpy arrays als auch scipy.sparse-Matrizen als Eingabe verarbeiten. Für dichte Matrizen werden eine große Anzahl möglicher Entfernungsmetriken unterstützt. Für dünnbesetzte Matrizen werden beliebige Minkowski-Metriken für Suchvorgänge unterstützt.

scikit-learn implementiert zwei verschiedene nächste-Nachbarn-Klassifikatoren:

K-Nächster-Nachbar
basiert auf den k-nächsten Nachbarn der Stichprobe, die zu klassifizieren sind. Der Wert `k` ist ein vom Benutzer spezifizierter Wert. Dies ist der am häufigsten engewendete Klassifikator der beiden.
Radius-Nächster-Nachbar
basiert auf der Anzahl der Nachbarn einer Stichprobe innerhalb eines festen Umkreises `r`. `r` ist eine von den Anwendenden zu bestimmende `flot`-Zahl. Dieser Klassifikationtyp wird seltener angewendet.

KNeighborsClassifier

Wir erstellen einen künstlichen Datensatz mit drei Klassen, um den K-Nächster Nachbarnklassifizierer KNeighborsClassifier von sklearn.neighbors testen.

In [2]:
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np

centers = [[2, 3], [5, 5], [7, 9]]
n_classes = len(centers)
data, labels = make_blobs(n_samples=150, 
                          centers=np.array(centers),
                          random_state=1)

Let us visualize what we have created:

In [3]:
import matplotlib.pyplot as plt

colours = ('green', 'red', 'blue')
n_classes = 3

fig, ax = plt.subplots()
for n_class in range(0, n_classes):
    ax.scatter(data[labels==n_class, 0], data[labels==n_class, 1], 
               c=colours[n_class], s=10, label=str(n_class))



ax.legend(loc='upper right');
No description has been provided for this image

Zuerst müssen wir die Daten jetzt in separate Trainings- und Testsets aufteilen, ein Prozess, den wir in unserem Kapitel Aufteilungen der Datensätze unseres Kurses "Maschinelles Lernen" ausführlich beschrieben haben.

In [4]:
from sklearn.model_selection import train_test_split
res = train_test_split(data, labels, 
                       train_size=0.8,
                       test_size=0.2,
                       random_state=1)

train_data, test_data, train_labels, test_labels = res 

Wir sind nun bereit die Klassifikation mit kNeighborsClassifier durchzuführen:

In [5]:
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
knn.fit(train_data, train_labels) 

predicted = knn.predict(test_data)
print("Predictions from the classifier:")
print(predicted)
print("Target values:")
print(test_labels)
Predictions from the classifier:
[2 2 2 0 0 1 1 2 2 1 0 1 0 0 2 0 0 0 1 0 0 1 2 2 0 0 0 1 2 1]
Target values:
[2 2 2 0 0 1 1 2 2 1 0 1 0 0 2 0 0 0 1 0 0 1 1 2 0 0 0 1 2 1]

Der Parameter metric ist standardmäßig Minkowski. Wir haben die Minkowski-Distanz in unserem Kapitel Nächste-Nachbarn-Klassifikation unseres Kurses "Maschinelles Lernen" ausführlich erklärt und in Python-Code implementiert. Der Parameter p entspricht dem p in der Minkowski-Formel: Wenn p auf 1 gesetzt wird, entspricht dies der Verwendung der Manhattan-Distanz, und die euklidische Distanz wird verwendet, wenn p den Wert 2 zugewiesen bekommt.

Der Parameter algorithm bestimmt, welcher Algorithmus verwendet wird, nämlich:

  • ball_tree verwendet BallTree
  • kd_tree verwendet KDTree
  • brute verwendet eine brute-force-Suche. Wir setzen den Parameter auf auto, der versucht, basierend auf den Werten, die der fit-Methode übergeben werden, den geeignetsten Algorithmus zu wählen.

Der Parameter leaf_size wird von BallTree oder KDTree benötigt. Es kann die Geschwindigkeit des Aufbaus und der Abfrage sowie den benötigten Speicherplatz für den Baum beeinflussen. Der optimale Wert hängt von der Art des Problems ab.

In [6]:
# Create and fit a nearest-neighbor classifier
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
knn.fit(data, labels) 
KNeighborsClassifier(algorithm='auto', 
                     leaf_size=30, 
                     metric='minkowski',
                     metric_params=None, 
                     n_jobs=1, 
                     n_neighbors=5, 
                     p=2,
                     weights='uniform')

predicted = knn.predict(data)
print("Ergebnisse der Klassifikation:")
print(predicted)
print("Sollwerte:")
print(labels)
Ergebnisse der Klassifikation:
[2 2 2 1 1 1 0 1 2 0 0 1 1 1 2 1 0 0 2 1 2 1 2 0 1 2 2 2 1 2 0 1 0 0 2 0 2
 1 2 2 2 2 0 0 2 1 0 0 1 2 0 0 0 1 1 2 1 1 1 0 0 1 0 1 2 2 0 2 2 1 0 1 0 2
 1 2 1 2 2 1 2 2 1 1 1 0 0 2 2 2 0 0 0 0 0 2 2 0 2 0 0 0 0 1 2 2 0 1 0 2 1
 2 2 1 1 2 0 2 1 1 1 0 0 0 1 0 0 1 0 1 2 0 0 2 1 0 0 0 2 2 2 1 0 0 1 1 1 1
 2 2]
Sollwerte:
[2 2 2 1 1 1 0 1 2 0 0 1 1 1 2 1 0 0 2 1 2 1 2 0 1 2 2 2 1 2 0 1 0 0 2 0 2
 1 2 2 2 2 0 0 2 1 0 0 1 2 0 0 0 1 1 2 1 1 1 1 0 1 0 1 1 2 0 2 2 1 0 1 0 2
 1 2 1 1 2 1 2 2 1 1 1 0 0 2 2 2 0 0 0 0 0 2 2 0 2 0 0 0 0 1 2 2 0 1 0 2 1
 2 2 1 1 2 0 2 1 1 1 0 0 0 1 0 0 1 0 1 2 0 0 2 1 0 0 0 2 2 2 1 0 0 1 2 1 1
 2 2]

Um das Ergebnis zu evaluieren benötigen wir die Funktion accuracy_score aus dem Modul sklearn.metrics. Um zu sehen, wie sie arbeitet, benutzen wir folgendes kleines Beispiel:

In [7]:
from sklearn.metrics import accuracy_score
y_pred = [0, 2, 1, 3, 2]
y_true = [0, 1, 2, 3, 2]
print(accuracy_score(y_true, y_pred))
print(accuracy_score(y_true, y_pred, normalize=False))
0.6
3

Der erste Aufruf von accuracy_score liefert 0.6 zurück, was bedeutet, dass 60% der Stichproben korrekt bestimmt wurden. Der Parameter normalize wurde per Default auf True gesetzt. Beim zweiten Aufruf setzen wir normalize auf False. Dies bedeutet, dass wir die Anzahl der Elemente erhalten, die korrekt vorhergesagt worden waren.

Nun sind wir bereit, unsere Ergebnisse aus der Klassifizierung des vorigen Beispiels zu bewerten:

In [8]:
print(accuracy_score(predicted, labels))
0.9733333333333334
In [9]:
data[:5], labels[:5]
Out[9]:
(array([[5.76314662, 9.87583893],
        [7.01652757, 9.17718772],
        [8.55880554, 9.1094027 ],
        [4.86355526, 4.88094581],
        [6.12141771, 5.40890054]]),
 array([2, 2, 2, 1, 1]))

Im folgenden Beispiel benutzen wir den Iris-Datensatz:

In [10]:
from sklearn import datasets
from sklearn.model_selection import train_test_split

iris = datasets.load_iris()
data, labels = iris.data, iris.target

res = train_test_split(data, labels, 
                       train_size=0.8,
                       test_size=0.2,
                       random_state=42)
train_data, test_data, train_labels, test_labels = res 
In [11]:
# Create and fit a nearest-neighbor classifier
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
knn.fit(train_data, train_labels) 
KNeighborsClassifier(algorithm='auto', 
                     leaf_size=30, 
                     metric='minkowski',
                     metric_params=None, 
                     n_jobs=1, 
                     n_neighbors=5, 
                     p=2,         # p=2 is equivalent to euclidian distance
                     weights='uniform')

print("Ergebnisse der Klassifikation:")
test_data_predicted = knn.predict(test_data)
print(test_data_predicted)
print("Sollwerte:")
print(test_labels)
Ergebnisse der Klassifikation:
[1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
Sollwerte:
[1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
In [12]:
print(accuracy_score(test_data_predicted, test_labels))
1.0
In [13]:
print("Ergebnisse der Klassifikation:")
learn_data_predicted = knn.predict(train_data)
print(learn_data_predicted)
print("Sollwerte:")
print(train_labels)
print(accuracy_score(learn_data_predicted, train_labels))
Ergebnisse der Klassifikation:
[0 0 1 0 0 2 1 0 0 0 2 1 1 0 0 1 2 2 1 2 1 2 1 0 2 1 0 0 0 1 2 0 0 0 1 0 1
 2 0 1 2 0 2 2 1 1 2 1 0 1 2 0 0 1 1 0 2 0 0 2 1 2 2 2 2 1 0 0 2 2 0 0 0 2
 2 0 2 2 0 1 1 2 1 2 0 2 1 2 1 1 1 0 1 1 0 1 2 2 0 1 2 2 0 2 0 1 2 2 1 2 1
 1 2 2 0 1 1 0 1 2]
Sollwerte:
[0 0 1 0 0 2 1 0 0 0 2 1 1 0 0 1 2 2 1 2 1 2 1 0 2 1 0 0 0 1 2 0 0 0 1 0 1
 2 0 1 2 0 2 2 1 1 2 1 0 1 2 0 0 1 1 0 2 0 0 1 1 2 1 2 2 1 0 0 2 2 0 0 0 1
 2 0 2 2 0 1 1 2 1 2 0 2 1 2 1 1 1 0 1 1 0 1 2 2 0 1 2 2 0 2 0 1 2 2 1 2 1
 1 2 2 0 1 2 0 1 2]
0.9666666666666667

RadiusNeighborsClassifier

Der K-Nächste-Nachbar-Klassifikator arbeitet, indem er einen Kreis um die unbekannte Stichprobe (d. h. das zu klassifizierende Element) erweitert, bis er genau k benachbarte Elemente umfasst. Im Gegensatz dazu verwendet der Radius Neighbors Classifier einen festen Radius, um seinen Suchraum zu definieren. Er identifiziert alle Elemente innerhalb des Trainingsdatensatzes, die innerhalb dieses vordefinierten Radius um das zu klassifizierende Element liegen. Folglich führt die Methode mit dem festen Radius dazu, dass dichte Regionen der Merkmalsverteilung informativere Beiträge liefern, während spärliche Regionen weniger Informationen beisteuern.

Im Zusammenhang mit dem KNN-Klassifikator (k-nearest neighbor) erweitert der Algorithmus mit zunehmender Anzahl der Nachbarn (k) seinen Suchradius, um mehr benachbarte Punkte in den Klassifikationsprozess einzubeziehen. Wird k zu hoch angesetzt oder ist die Umgebung zu spärlich besetzt, kann es passieren, dass der Klassifikator Punkte einer anderen Klasse einbezieht, die weit von der zu klassifizierenden Probe entfernt sind.

Diese Situation kann eintreten, wenn der Datensatz Regionen enthält, in denen die Klassen nicht gut voneinander getrennt sind, was zu überlappenden Regionen zwischen den Klassen führt. Wenn sich der Suchradius vergrößert, kann der Algorithmus beginnen, Punkte aus weit entfernten Klassen einzubeziehen, was zu Fehlklassifizierungen oder Leistungseinbußen führen kann.

In [14]:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

# Generate the dataset
centers = [[-2, 2], [2, 2]]
X, y = make_blobs(n_samples=100, centers=centers, cluster_std=0.5, random_state=42)

X[:3], y[:3]
Out[14]:
(array([[ 2.23661881,  1.96358554],
        [-2.41960876,  1.84539381],
        [ 1.04061439,  1.98674306]]),
 array([1, 0, 1]))

Wir erstellen nun einen isolierten Cluster von 3 Elementen außerhalb des Hauptclusters der Klasse 0. Wir fügen diese Elemente mit concatenate zum Hauptcluster hinzu:

In [15]:
centers = [[0.2, 2.2]]
X1, y1 = make_blobs(n_samples=3, centers=centers, cluster_std=0.1, random_state=42)

X = np.concatenate((X, X1), axis=0)
y = np.concatenate((y, y1))

Wir wollen nun die erzeugten Cluster visualisieren:

In [16]:
# Plot the data
plt.figure(figsize=(10, 6))
for class_value in range(2):
    # select indices of points with the current class label
    row_ix = np.where(y == class_value)
    # plot points for the current class
    plt.scatter(X[row_ix, 0], X[row_ix, 1], label=str(class_value))
plt.title('Generated Classification Dataset with Blobs')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
No description has been provided for this image

Mit dem folgenden Code teilen wir den Datensatz sowohl für die Merkmale als auch für die Bezeichnungen in Trainings- und Testsätze auf, wobei 80 % der Daten für das Training und 20 % (test_size=0.2) für das Testen verwendet werden.

In [17]:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Wir werden nun unsere Trainings- und Testdaten visualisieren. Grüne und blaue Punkte repräsentieren die Trainingsdaten, während hellgrüne und hellblaue Punkte die Testdaten darstellen. Wir sehen, dass innerhalb unserer grünlichen Klasse (Klasse 1) drei Punkte in unmittelbarer Nähe zur bläulichen Klasse liegen. In solchen Fällen schneidet der Radius Neighbors Classifier tendenziell besser ab, es sei denn, wir setzen den Wert von k auf eine Zahl kleiner als 4.

In [18]:
import matplotlib.pyplot as plt


fig, ax = plt.subplots()
# plotting learn data
colours = ('green', 'blue')
for n_class in range(2):
    ax.scatter(X_train[y_train==n_class][:, 0], 
               X_train[y_train==n_class][:, 1], 
               c=colours[n_class], s=40, label=str(n_class))
    
    
# plotting test data
colours = ('lightgreen', 'lightblue')
for n_class in range(2):
    ax.scatter(X_test[y_test==n_class][:, 0], 
               X_test[y_test==n_class][:, 1], 
               c=colours[n_class], s=40, label=str(n_class))

ax.plot();
No description has been provided for this image

Wir werden unsere Datensätze mit Hilfe des "RadiusNeighborsClassifier" klassifizieren:

In [19]:
from sklearn.neighbors import RadiusNeighborsClassifier

# Instantiate the RadiusNeighborsClassifier
rnc = RadiusNeighborsClassifier(radius=0.5)

# Fit the model to the training data
rnc.fit(X_train, y_train)

# Predict the labels for the testing data
y_pred = rnc.predict(X_test)

# Evaluate the accuracy of the model
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)
Accuracy: 1.0

Und nun zum Vergleich mit dem KNeighborsClassifier:

In [20]:
# Instantiate the KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=15)

# Fit the model to the training data
knn.fit(X_train, y_train)

# Predict the labels for the testing data
y_pred = knn.predict(X_test)

# Evaluate the accuracy of the model
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)
Accuracy: 0.9523809523809523

Wenn man die Anzahl der Nachbarn auf 4 festlegt, kann der k-nearest neighbors-Klassifikator auch in diesem Szenario effektiv arbeiten.

In [21]:
knn = KNeighborsClassifier(n_neighbors=4)

# Fit the model to the training data
knn.fit(X_train, y_train)

# Predict the labels for the testing data
y_pred = knn.predict(X_test)

# Evaluate the accuracy of the model
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)
Accuracy: 1.0

Ein weiteres einfaches Beispiel mit RadusNeighborsClassifier

In [22]:
from sklearn.neighbors import RadiusNeighborsClassifier

X = [[0, 1], [0.5, 1], [3, 1], [3, 2], [1.3, 0.8], [2.5, 2.5]]
y = [0, 0, 1, 1, 0, 1]

neigh = RadiusNeighborsClassifier(radius=1.0)
neigh.fit(X, y)

print(neigh.predict([[1.5, 1.2]]))

print(neigh.predict([[3.1, 2.1]]))
[0]
[1]

Wenn wir versuchen, eine Vorhersage auf Daten wie [30, 20] zu machen, kann der Algorithmus keine Nachbarn für den Radius 1,0 finden. Es wird also eine Ausnahme mit dem folgenden Text ausgelöst:

ValueError: No neighbors found for test samples array([0]), you can try using larger radius, giving a label for outliers, or considering removing them from your dataset.

Es gibt einen Parameter zur Einstellung des Labels für Ausreißer, d.h. outlier_label.

Es gibt drei Möglichkeiten, es zu verwenden:

  1. Manuelle Beschriftung: str- oder int-Beschriftung (sollte der gleiche Typ sein, den wir in unseren Daten verwenden) oder Liste manueller Beschriftungen, wenn mehrere Ausgaben verwendet werden.
  2. Es kann auf den Wert 'most_frequent' gesetzt werden. Dadurch wird das am häufigsten vorkommende Label des Datensatzes Ausreißern zugewiesen.
  3. Wenn es auf None (Standardwert) gesetzt ist, wird eine ValueError ausgelöst, wenn ein Ausreißer erkannt wird.

Lassen Sie uns das noch einmal mit 'most_frequent' machen.

In [23]:
neigh = RadiusNeighborsClassifier(radius=1.0,
                                  outlier_label='most_frequent')
neigh.fit(X, y)

print(neigh.predict([[1.5, 1.2]]))

# the following is the previously mentioned outlier:
print(neigh.predict([[30, 20]]))
[0]
[0]

Alternativ setzen wir die Ausreißerklasse auf 2. Wir fügen ein Ausreißerelement zu unserem Lernsatz hinzu:

In [ ]:
from sklearn.neighbors import RadiusNeighborsClassifier

X = [[0, 1], [0.5, 1], [3, 1], [3, 2], [1.3, 0.8], [2.5, 2.5], [2.4, 2.6], [10000, -2321]]
y = [0, 0, 1, 1, 0, 1, 1, 2]

neigh = RadiusNeighborsClassifier(radius=1.0,
                                  outlier_label=2)
neigh.fit(X, y)

print(neigh.predict([[1.5, 1.2]]))
print(neigh.predict([[30, 20]]))

Wir wollen nun wieder auf einem umfangreicheren Datenset arbeiten, welches wir zuerst mittels make_blobs generieren:

In [29]:
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np

centers = [[2, 3], [9, 2], [7, 9]]
n_classes = len(centers)
data, labels = make_blobs(n_samples=255, 
                          centers=np.array(centers),
                          cluster_std = 1.3,
                          random_state=1)
In [30]:
import matplotlib.pyplot as plt

colours = ('green', 'red', 'blue')
n_classes = 3

fig, ax = plt.subplots()
for n_class in range(0, n_classes):
    ax.scatter(data[labels==n_class, 0], data[labels==n_class, 1], 
               c=colours[n_class], s=10, label=str(n_class))
No description has been provided for this image
In [31]:
res = train_test_split(data, labels, 
                       train_size=0.8,
                       test_size=0.2,
                       random_state=1)
train_data, test_data, train_labels, test_labels = res 

Fügen wir eine Zeile am Ende der Trainingsdaten hinzu, die Ausreißerdaten enthält, d.h. die keiner Klasse angehören:

In [32]:
outlier = [4242.2, 4242.2]
train_data = np.vstack([train_data, outlier])
train_data[-3:]
Out[32]:
array([[   8.42869523,    7.82787516],
       [   8.01064497,    8.84559748],
       [4242.2       , 4242.2       ]])
In [ ]:
Nun müssen wir noch ein Label als `outlier`-Label festlegen.
In [33]:
outlier_label = len(np.unique(labels))
train_labels = np.append(train_labels, outlier_label)
train_labels[-10:]
Out[33]:
array([0, 0, 0, 1, 0, 0, 0, 2, 2, 3])

Mit dem Befehl unique können wir überprüfen, welche Klassen verfügbar sind:

In [34]:
np.unique(train_labels)
Out[34]:
array([0, 1, 2, 3])

Im folgenden Code initialisieren wir einen Radius-Neighbors-Classifier mit einem festgelegten Radius, trainieren ihn mit einem Trainingsdatensatz und verwenden dann den trainierten Classifier, um Labels für einen separaten Testdatensatz vorherzusagen.

In [35]:
rnn = RadiusNeighborsClassifier(radius=1)
rnn.fit(train_data, train_labels)
predicted = rnn.predict(test_data)
In [36]:
print(accuracy_score(predicted, test_labels))
1.0

Nun vekleinern wir den Radius:

In [37]:
rnn = RadiusNeighborsClassifier(radius=0.9,
                                outlier_label=outlier_label)
rnn.fit(train_data, train_labels)
predicted = rnn.predict(test_data)
print(accuracy_score(predicted, test_labels))
0.9803921568627451

Wir erzeugen weitere Ausreißer, um sie zu testen:

In [38]:
centers = [[100, 300]]
data_outliers, labels_outliers = make_blobs(n_samples=10, 
                                  centers=np.array(centers),
                                  random_state=1)
In [39]:
predicted = rnn.predict(data_outliers)
predicted
Out[39]:
array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])

Wir vergleichen dies nun mit einem k-nearest-neighbor classifier:

In [41]:
k = 15
knn = KNeighborsClassifier(algorithm='auto', 
                     leaf_size=30, 
                     metric='minkowski',
                     metric_params=None, 
                     n_jobs=1, 
                     n_neighbors=k, # default is 5
                     p=2,         # p=2 is equivalent to euclidian distance
                     weights='uniform')

knn.fit(data, labels) 
predicted = knn.predict(test_data)
print(accuracy_score(predicted, test_labels))
1.0
In [42]:
from sklearn.metrics import confusion_matrix 
# Evaluate Model
cm = confusion_matrix(predicted, test_labels)
print(cm) 
[[24  0  0]
 [ 0 18  0]
 [ 0  0  9]]
In [ ]:
predicted = knn.predict(data_outliers)
predicted

Wir können sehen, dass alle Ausreißer fälschlicherweise als Klasse 2 klassifiziert wurden, weil dies die nächstgelegene vorhandene Klasse zu den Ausreißern ist. Im Folgenden erstellen wir drei Cluster von Ausreißern:

In [43]:
centers = [[100, 300], [10, -10], [-200, -200]]
data_outliers2, labels_outliers2 = make_blobs(n_samples=30, 
                                              centers=np.array(centers),
                                              random_state=1)

predicted = knn.predict(data_outliers2)
predicted
Out[43]:
array([2, 2, 2, 1, 0, 2, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 2, 0, 2, 0, 1, 1,
       2, 2, 1, 2, 0, 1, 0, 0])

Die Ausreißer werden den vorhandenen Clustern zugeordnet, obwohl sie weit entfernt von ihnen liegen. Andererseits wird der Radius-Neighbors-Classifier sie als Ausreißer erkennen:

In [44]:
rnn = RadiusNeighborsClassifier(radius=0.9,
                                outlier_label=outlier_label)
rnn.fit(train_data, train_labels)
predicted = rnn.predict(data_outliers2)
predicted
Out[44]:
array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3])

Bestimmung des optimalen k-Werts

Der optimale Wert für k ist normalerweise die Quadratwurzel von n, wobei n die Gesamtanzahl der Proben unseres Datensatzes ist. Wir können auch einen Wert für k bestimmen, indem wir die Genauigkeitswerte für verschiedene k-Werte plotten:

In [45]:
import matplotlib.pyplot as plt

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np


n_classes = 6
data, labels = make_blobs(n_samples=1000, 
                          centers=n_classes,
                          cluster_std = 1.3,
                          random_state=1)
In [46]:
import matplotlib.pyplot as plt

colours = ('green', 'red', 'blue', 'magenta', 'yellow', 'pink')

fig, ax = plt.subplots()
for n_class in range(0, n_classes):
    ax.scatter(data[labels==n_class, 0], data[labels==n_class, 1], 
               c=colours[n_class], s=10, label=str(n_class))
No description has been provided for this image
In [49]:
res = train_test_split(data, labels, 
                       train_size=0.7,
                       test_size=0.3,
                       random_state=1)
train_data, test_data, train_labels, test_labels = res 

print(len(train_data), len(test_data), len(train_labels))

X, Y = [], []
for k in range(1, 25):
    classifier = KNeighborsClassifier(n_neighbors=k, 
                                      p=2,    # Euclidian
                                      metric="minkowski")
    classifier.fit(train_data, train_labels)
    predictions = classifier.predict(test_data)
    score = accuracy_score(test_labels, predictions)
    X.append(k)
    Y.append(score)
    


fig, ax = plt.subplots()
ax.set_xlabel('k')
ax.set_ylabel('accuracy')
ax.plot(X, Y, "g-.o")
700 300 700
Out[49]:
[<matplotlib.lines.Line2D at 0x7fb5c1b81ad0>]
No description has been provided for this image

Aufgaben

Aufgabe 1

Klassifizieren Sie die Daten in "strange_flowers.txt" mit einem k-Nächste-Nachbar-Klassifikator.

Aufgabe 2

Klassifizieren Sie die Daten in "fruits_data.txt" mit einem k nearest neighbor classifier.

Aufgabe 3

Benutzen Sie sklearn, um die Städtenamen aus dem vorherigen Kapitel zu korrigieren:

Die falsch geschriebenen Städtenamen sind: "Freiburg", "Frieburg", "Freiborg", "Hamborg", "Sahrluis"

Die richtigen Städtenamen sind in data/city_names.txt gespeichert.

Die Levenshtein-Distanz kann importiert werden mit

Levenshtein importieren".

Es muss installiert werden:

pip install python-Levenshtein

Aufgabe 4

Machen Sie das Gleiche nun für die falsch geschriebenen Wörter "holpful", "kundnoss", "holpposs", "thoes", "innerstand", "blagrufoo" und "liberdi"

Verwenden Sie die Datei british-english.txt für eine Liste aller richtig geschriebenen englischen Wörter!

Lösungen

Lösung zu Aufgabe 1

In [51]:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler # necessary to reduce biases of large numbers
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix 
from sklearn.metrics import f1_score 
from sklearn.metrics import accuracy_score 

dataset = pd.read_csv("data/strange_flowers.txt", 
                      header=None, 
                      names=["red", "green", "blue", "size", "label"],
                      sep=" ")
dataset
Out[51]:
red green blue size label
0 247.0 122.0 19.0 3.93 1.0
1 245.0 108.0 5.0 4.08 1.0
2 239.0 97.0 3.0 3.73 1.0
3 241.0 92.0 18.0 4.08 1.0
4 243.0 124.0 0.0 3.79 1.0
... ... ... ... ... ...
790 197.0 251.0 102.0 3.10 4.0
791 197.0 252.0 100.0 3.11 4.0
792 197.0 243.0 103.0 2.89 4.0
793 197.0 242.0 93.0 2.53 4.0
794 197.0 250.0 98.0 2.53 4.0

795 rows × 5 columns

Anstatt Pandas zu verwenden, um die Daten von "strange_flowers.txt" einzulesen, könnten wir auch "loadtxt" von numpy verwenden:

In [52]:
import numpy as np

raw_data = np.loadtxt("data/strange_flowers.txt")
data = raw_data[:,:-1]
labels = raw_data[:,-1]

Wir fahren nun mit dem Pandas DataFrame Objekt dataset fort, das wir mit read_csv eingelesen haben:

In [54]:
data = dataset.drop('label', axis=1)
labels = dataset.label
X_train, X_test, y_train, y_test = train_test_split(data, 
                                                    labels, 
                                                    random_state=0, 
                                                    test_size=0.2) 

X_train[:5]
Out[54]:
red green blue size
64 247.0 111.0 7.0 4.04
632 197.0 251.0 100.0 2.70
52 255.0 119.0 14.0 4.07
527 221.0 83.0 9.0 4.32
662 197.0 251.0 100.0 2.67

Der folgende Code ist notwendig, um die Merkmale sowohl des Trainings- als auch des Testdatensatzes zu standardisieren und sicherzustellen, dass sie angemessen skaliert sind:

In [55]:
scaler = StandardScaler() 
X_train = scaler.fit_transform(X_train) #  transform
X_test = scaler.transform(X_test) #  transform

X_train[:5]
Out[55]:
array([[ 1.05293065, -0.38133608, -0.4982393 ,  0.96420608],
       [-1.12909572,  1.95646096,  1.90393369, -1.7697161 ],
       [ 1.40205487, -0.24774767, -0.31743058,  1.02541329],
       [-0.08172306, -0.84889548, -0.44657967,  1.5354734 ],
       [-1.12909572,  1.95646096,  1.90393369, -1.83092331]])

Wir setzen k auf die Quadratwurzel der Größe des Lernsatzes:

In [56]:
k = int(len(X_train) ** 0.5)
k
Out[56]:
25
In [58]:
classifier = KNeighborsClassifier(n_neighbors=k, 
                                  metric="minkowski",
                                  p=2,    # Euclidian
                                 ) 

classifier.fit(X_train, y_train)
y_pred = classifier.predict(X_test)
y_pred
Out[58]:
array([3., 1., 3., 4., 3., 3., 1., 4., 3., 3., 4., 1., 3., 1., 2., 2., 2.,
       3., 1., 4., 2., 3., 4., 2., 3., 3., 4., 4., 1., 2., 1., 1., 2., 3.,
       1., 3., 3., 2., 2., 1., 3., 3., 4., 1., 4., 2., 3., 2., 3., 2., 2.,
       3., 1., 3., 4., 1., 2., 4., 2., 3., 3., 4., 3., 4., 3., 1., 1., 2.,
       1., 3., 3., 2., 4., 2., 2., 3., 2., 4., 2., 4., 1., 3., 4., 2., 4.,
       3., 2., 2., 2., 3., 2., 2., 3., 3., 1., 4., 2., 1., 2., 2., 1., 2.,
       4., 3., 3., 3., 2., 1., 2., 4., 2., 3., 3., 2., 2., 4., 3., 1., 1.,
       2., 1., 4., 3., 4., 2., 2., 3., 2., 4., 1., 4., 2., 4., 4., 4., 4.,
       4., 2., 4., 4., 4., 2., 3., 2., 1., 1., 2., 3., 1., 1., 3., 1., 2.,
       4., 2., 4., 2., 3., 2.])

Wir evaluieren das Modell:

In [60]:
cm = confusion_matrix(y_test, y_pred)
print(cm) 
[[29  3  0  0]
 [ 1 46  0  0]
 [ 0  0 44  0]
 [ 0  0  0 36]]
In [61]:
print(accuracy_score(y_test, y_pred))
0.9748427672955975

Lösung zu Aufgabe 2

In [64]:
import numpy as np
import pandas as pd
from sklearn.neighbors import NearestNeighbors
from collections import Counter

# Read in the CSV file
df = pd.read_csv('data/fruits_data.csv')

# Extract features (X) and labels (y)
X = df[['Sweetness', 'Acidity', 'Weight']].values
y = df['Fruit'].values

X_train, X_test, y_train, y_test = train_test_split(X, 
                                                    y, 
                                                    random_state=0, 
                                                    test_size=0.2) 
In [65]:
scaler = StandardScaler() 
X_train = scaler.fit_transform(X_train) #  transform
X_test = scaler.transform(X_test) #  transform
In [66]:
# Define the model
classifier = KNeighborsClassifier(n_neighbors=k, 
                                  metric="minkowski",
                                  p=2,    # Euclidian
                                 ) 
In [67]:
classifier.fit(X_train, y_train)
y_pred = classifier.predict(X_test)
y_pred
Out[67]:
array(['Apple', 'Mango', 'Lemon', 'Apple', 'Mango', 'Lemon', 'Mango',
       'Mango', 'Apple', 'Mango', 'Lemon', 'Lemon', 'Mango', 'Lemon',
       'Lemon', 'Apple', 'Lemon', 'Lemon', 'Apple', 'Lemon', 'Apple',
       'Lemon', 'Mango', 'Lemon', 'Apple', 'Apple'], dtype=object)
In [68]:
# Evaluate Model
cm = confusion_matrix(y_test, y_pred)
print(cm) 
[[ 7  1  0]
 [ 0 10  0]
 [ 1  0  7]]
In [69]:
print(accuracy_score(y_test, y_pred))
0.9230769230769231

Lösung zu Aufgabe 3

In [70]:
import Levenshtein

# Load the file containing correct city names
with open('data/city_names.txt', 'r') as file:
    correct_city_names = file.readlines()
correct_city_names = [name.strip() for name in correct_city_names]

# Misspelled city names
misspelled_city_names = ["Freiburg", "Frieburg", "Freiborg", "Hamborg", "Sahrluis"]

# Find the closest match for each misspelled city name
for misspelled_city in misspelled_city_names:
    min_distance = float('inf')
    closest_match = None
    
    # Calculate Levenshtein distance to all correct city names
    for correct_city in correct_city_names:
        distance = Levenshtein.distance(misspelled_city, correct_city)
        if distance < min_distance:
            min_distance = distance
            closest_match = correct_city
    
    print(f"Closest match for '{misspelled_city}': {closest_match}")
Closest match for 'Freiburg': Freiberg
Closest match for 'Frieburg': Lüneburg
Closest match for 'Freiborg': Freiberg
Closest match for 'Hamborg': Hamburg
Closest match for 'Sahrluis': Saarlouis

Lösung zu Aufgabe 4

In [71]:
import Levenshtein

# Load the file containing correct city names
with open('british-english.txt', 'r') as file:
    correct_words = file.readlines()
correct_words = [name.strip() for name in correct_words]

# misspelled ords 
misspelled_words = ["holpful", "kundnoss", "holpposs", 
                    "thoes", "innerstand", "blagrufoo", 
                    "liberdi"]

# Find the closest match for each misspelled city name
for misspelled_word in misspelled_words:
    min_distance = float('inf')
    closest_match = None
    
    # Calculate Levenshtein distance to all correct city names
    for correct_word in correct_words:
        distance = Levenshtein.distance(misspelled_word, correct_word)
        if distance < min_distance:
            min_distance = distance
            closest_match = correct_word
    
    print(f"Closest match for '{misspelled_word}': {closest_match}")
Closest match for 'holpful': helpful
Closest match for 'kundnoss': kindness
Closest match for 'holpposs': helpless
Closest match for 'thoes': hoes
Closest match for 'innerstand': understand
Closest match for 'blagrufoo': barefoot
Closest match for 'liberdi': liberal

Wir sind mit dem Ergebnis von 'hoes' für 'thoes' unzufrieden. Daher verbessern wir das Programm, um auch die zweitnächste Übereinstimmung anzuzeigen.

In [72]:
import Levenshtein

# Load the file containing correct words
with open('british-english.txt', 'r') as file:
    correct_words = file.readlines()
correct_words = [word.strip() for word in correct_words]

# Misspelled words
misspelled_words = ["holpful", "kundnoss", "holpposs", 
                    "thoes", "innerstand", "blagrufoo", 
                    "liberdi"]

# Find the closest and second closest match for each misspelled word
for misspelled_word in misspelled_words:
    distances = []
    
    # Calculate Levenshtein distance to all correct words
    for correct_word in correct_words:
        distance = Levenshtein.distance(misspelled_word, correct_word)
        distances.append((distance, correct_word))
    
    # Sort distances by the first element (distance)
    distances.sort(key=lambda x: x[0])
    
    # Get the closest and second closest matches
    closest_match = distances[0][1]
    second_closest_match = distances[1][1]
    
    print(f"Misspelled word: {misspelled_word}")
    print(f"Closest match: {closest_match}")
    print(f"Second closest match: {second_closest_match}")
    print()
Misspelled word: holpful
Closest match: helpful
Second closest match: doleful

Misspelled word: kundnoss
Closest match: kindness
Second closest match: fondness

Misspelled word: holpposs
Closest match: helpless
Second closest match: hippo's

Misspelled word: thoes
Closest match: hoes
Second closest match: shoes

Misspelled word: innerstand
Closest match: understand
Second closest match: interstate

Misspelled word: blagrufoo
Closest match: barefoot
Second closest match: Baguio

Misspelled word: liberdi
Closest match: liberal
Second closest match: liberty