Daten der Punkte und Slides Einführung der ersten Veranstaltung.
from gpanel import * import csv #um Text-Dateien im CSV-Format zu lesen import random # CSV-File öffnen csvfile = open('C:/temp/data.csv', 'r') # CSV-File einlesen. reader = csv.reader(csvfile, delimiter=',',quoting=csv.QUOTE_NONNUMERIC) # CSV-File in Liste umwandeln datalist = list(reader) # reader schliessen reader.close() # Liste anschauen: # print(datalist) # GPanel aufsetzen makeGPanel(-20,20,-20,20) drawGrid(-15,15,-15,15) # Punkte zeichnen # Alle Punkte in einer Schlaufen zeichnen, x-Koordinate: 1 Komponenten, y-Koordinate: 2. Komponente, Farbe: 3-Komponente # Mögliche hilfreiche Funktion move, if, setColor, fillCircle, etc.
[[0.1231,1.3],[1.31,-0.32],...]
wird eine Klasse zugewiesen, d.h. entweder 0 oder 1.[[0.033131,1],[0.123131,0],[0.123124141,1],[1.2123141,0]]
def computeDistances(xn,yn): # Funktion berechnet die Distanz von (xn,yn) zu den Punkten in datalist return(distanceClassList) def assignClass(distClassList): # die k nächsten nachbar raussuchen # mehrheit herausfinden # mehrheismeinung zurückgeben return(klasse)
Empfehlung: Mindestens zwei Funktionen definieren. Eine zur Berechnung der Distanz-Klassen-Liste, sowie eine zur Zuweisung der Klasse (0 oder 1) resp. Farbe auf Grund der Liste .
Testen des Algorithmus mit
Wer $k$-nearest-neighbour implementiert hat, kann sich überlegen, wie die untenstehende Daten klassifiziert werden sollen: Die Daten finden sich in einer neuen ZIP-Datein
import os for filename in os.listdir("C:/temp/"): print(filename)
Möchte man nur GIF-Dateien lesen, kann dies mit List Comprehensions und Filtern gelöst werden
import os for filename in [filename for filename in os.listdir("C:/temp") if filename.endswith("gif")]: print(filename)
filename.split('_')[2]
kann der String “filename” aufgeteilt ("gesplitted") werden, die 2 steht dabei für das dritte Element nach dem Split in der Liste und entspricht der Ziffer.outcsv = open("C:/temp/outfile.csv", 'a'); # CSV-writer konfigurieren. writer = csv.writer(outcsv, delimiter=',', lineterminator='\n') for item in datalist: #Jeden Eintrag der Datalist als Zeile ausgeben writer.writerow([item[0], item[1], item[2]]) # Wrtier schliessen outcsv.close()
Die Anzahl Nachbarn $k$ ist ein sogenannter “Tunig-Parameter”: Ein Parameter, der die Aussage des Klassifizierers massgeblich beeinflusst. Angehängte Graphik illustriert diesen Zusammenhang.
Beim kNN-Algorithmus geht es darum, den Abstand zu den Nachbarn zu berechnen. Bis jetzt war unser Problem $2$-dimensional. Neu ist es $256$-dimensional, da jeder Pixel einer Dimension entspricht. Daher:
Musterlösung um eine Pixelliste aus allen Dateien eines Verzeichnisses zu erstellen:
import gpanel #um bilder einzulesen import os #um Verzeichnisse zu listen import csv #um CSV-Dateien zu lesen. # Pfad zu den Bilddateien digitsdirectory = 'C:/temp/digits/train/' def getPixeListFromFilePath(filepath): img = gpanel.getImage(filepath) w = img.getWidth() h = img.getHeight() pixellist = [] for y in range(h): for x in range(w): # color is ein Objekt mit verschiedenen Attributen, u.a. red, green, blue. # bei grau sind rot=gruen=blau, d.h., eine Farbe auslesen reicht. # siehe auch https://docs.oracle.com/javase/7/docs/api/java/awt/Color.html color = img.getPixelColor(x, y) # umlegen auf das Intervall [-1,1] zwecks Normalisierung value = color.red / 255 * 2 - 1 # an liste anhaengen pixellist.append(value) return pixellist # Lese Ziffer aus Dateiname aus. def getDigitFromFileName(filename): return int(filename.split('_', 3)[2]) # leere Liste fuer alle Trainingsdaten der Form [-0.93,0.331,....,0.99,3] trainingset = [] # durch alle files im Ziffernverzeichnis loopen for filename in [filename for filename in os.listdir(digitsdirectory) if filename.endswith("gif")]: # Ziffer auslesen currdigit = getDigitFromFileName(filename) # Pixelliste von Datei auslesen currpixellist = getPixeListFromFilePath(digitsdirectory + filename) # Der Pixelliste die Ziffer anhaengen currpixellist.append(currdigit) # Gesamte Liste dem trainingsset anhaengen. trainingset.append(currpixellist) # Das Trainingsset kann jetzt verwendet werden # print(trainingsset)
Der Code unten soll als Grundlage für den eigentlichen kNN Klassifizierer gelten. Es muss einizg noch die Funktion assignClass
geschrieben werden. Das Einlesen der Trainings- und Testdaten ist bereits programmiert, ebenso die Umwanldung eines Bildes in eine Pixelliste.
import csv #um CSV-Dateien zu lesen. # Trainingsdaten trainingset = list(csv.reader(open(digitsdirectory + 'digitsdata.csv', 'r'), delimiter=',', quoting=csv.QUOTE_NONNUMERIC)) # Testdaten testset = list(csv.reader(open(digitsdirectory + 'digitsdatatest.csv', 'r'), delimiter=',', quoting=csv.QUOTE_NONNUMERIC)) def assignClass(point, k): # Funktion die den Abstand von point zu den k naechsten # Nachbarn im trainingset berechnet mit Pythagoras in # 16x16=256 Dimensionen # über alle Trainingsdaten iterieren for i in range(len(trainingsset)): dist = 0; # über alle 256 Pixel iterieren (für alle Trainingsdaten) for j in range(255): dist = dist + (point[j] - trainingsset[i][j])^2 #etc. # gibt die Mehrheitsklasse wieder. Tip: Liste mit Häufigkeiten zurückgeben. return(assignedclass) ## Funktion Testen auf ersten Testbild print(assignClass(testset[0][0:255], 30)) # Auf allen Testdaten for i in range(len(testset)): print(assignClass(testset[i][0:255], 30)) print(testset[i][256])
Sortieren ist kostspielig: Einfache Sortieralgorithmen (BubbleSort) haben eine Komplexität von $\mathcal O(n^2)$, d.h., es werden ca. $n^2$ Operationen benötigt um $n$ Elemente zu sortieren.
getPixelArrayFromFilePath(filepath)
$k$
und Random Forests für verschieden n_estimators
durchführen. Beides sind sogenannte “Tunig-Parameter”, welche die Klassifikationsgüte beinflussen. Frage: Wie könnte ein optimales $k$ bestimmt werden?
Um mit bereits implementierten Methoden von scikit zu arbeiten, müssen zusätzlich die Module numpy, scipy und sklearn installiert werden. Dies geschieht am besten, auf der Kommandozeile / Konsole mit pip
. Vorgänging muss bereits Python (2.7) installiert sein:
C:\Python27\Scripts
cd C:\Python27\Scripts
pip install …
und die Modulnamenpip install numpy pip install scipy pip install sklearn pip install imageio
Ein wichtiges Konstrukt sind sogenannte numpy-Arrays: Diese sind grundsätzlich der Python (geschachtelten) Liste sehr ähnlich, sind aber im Umang viel praktischer. Mehr Details gibt's z.B. hier und hier. Das wichtigste dabei ist wohl, wie auf Elemente zugegriffen werden kann und wie Teilmengen von diesen gebildet werden können:
data = array([[2,3],[3,4]]) #zugriff via Zeile/Spalte: data[0,1] data[1,1] # fuer trainingsdaten trainingdata[0,256] # die Zahl des ersten Bildes trainingdata[4,2] # den Wert des dritten Pixel des 5 Bildes trainingsdata[0,:] # alle Werte inkl. Zahl des ersten Bildes.
Nachher kann relativ einfach mit Python kNN implementiert werden:
#!/usr/bin/python # -*- coding: utf-8 -*- from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import confusion_matrix from numpy import genfromtxt,asarray from imageio import imread # Trainingdaten einlesen trainingdata = genfromtxt('C:/temp/digits/digitsdata.csv', delimiter=',') # Testdaten einlesen testdata = genfromtxt('C:/temp/digits/digitsdatatest.csv', delimiter=',') # Trainingsfeatures und -klassen trennen. # Kolonne 0:255: Features, Kolonne 256 Klasse # Fuer Ekrlaerung der Syntax siehe: https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html train_digits_features = trainingdata[:, :-1] train_digits_class = trainingdata[:, -1] # Testfeatures und -klassen trennen. test_digits_features = testdata[:, :-1] test_digits_class = testdata[:, -1] # kNN initilisieren mit 5 Nachbarn knn = KNeighborsClassifier(n_neighbors=5) # kNN traininieren knn.fit(train_digits_features, train_digits_class) # Vorhersage predicted_class = knn.predict(test_digits_features) # Wahre Test digits ausgeben print(test_digits_class) # Predicted Test digits ausgeben print(predicted_class) ## Vergleichen und Klassifikationsguete berechnen. ## Differenz muss = 0 sein wenn richtig klassifziert. Boolsche Variablen koennen summiert werden. print sum(predicted_class - test_digits_class == 0) / float(len(predicted_class)) ## Konfusionsmatrix berechnen mat_knn = confusion_matrix(test_digits_class, predicted_class) print(mat_knn) # Aquivalent der Funktion getPixelListFromFilePath def getPixelArrayFromFilePath(filepath): img = imread(filepath) return(asarray(((img.flatten()/float(255)*2-1).reshape(1,-1)))) getPixelArrayFromFilePath("C:/temp/digits/test/testimg_181710187299_0_.gif") print(knn.predict(getPixelArrayFromFilePath("C:/temp/digits/test/testimg_181710187299_0_.gif")))
#!/usr/bin/python # -*- coding: utf-8 -*- from sklearn.neighbors import KNeighborsClassifier from numpy import genfromtxt, asarray, sum from sklearn.metrics import confusion_matrix from sklearn.ensemble import RandomForestClassifier ####################################################################### # # Daten einlesen. Für Random Forests (rf) und k-Nearest-Neighbors (knn) # ####################################################################### # Trainingdaten einlesen trainingdata = genfromtxt('C:/temp/digits/digitsdata.csv', delimiter = ',') # Testdaten einlesen testdata = genfromtxt('C:/temp/digits/digitsdatatest.csv', delimiter = ',') # Trainingsfeatures und -klassen trennen. # Kolonne 0:255: Features, Kolonne 256 Klasse # Fuer Ekrlaerung der Syntax siehe: # https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html train_digits_features = trainingdata[: , : -1] train_digits_class = trainingdata[: , -1] # Testfeatures und - klassen trennen. test_digits_features = testdata[: , : -1] test_digits_class = testdata[: , -1] ####################################################################### # # kNN trainieren, vorhersagen und evaluieren. # ####################################################################### #kNN initilisieren mit 10 Nachbarn knn = KNeighborsClassifier(n_neighbors = 10) # kNN traininieren knn.fit(train_digits_features, train_digits_class) # Vorhersage predicted_class_knn = knn.predict(test_digits_features) # Konfusionsmatrix mat_knn = confusion_matrix(test_digits_class, predicted_class_knn) print(mat_knn)# Güte guete_knn = sum(mat_knn.diagonal()) / float(sum(mat_knn)) print "Guete kNN:", guete_knn ####################################################################### # # Random Forests trainieren, vorhersagen und evaluieren. # ####################################################################### rf = RandomForestClassifier(n_estimators = 200, random_state = 17) rf.fit(train_digits_features, train_digits_class) predicted_class_rf = rf.predict(test_digits_features) mat_rf = confusion_matrix(test_digits_class, predicted_class_rf) print(mat_rf) # Güte guete_rf = sum(mat_rf.diagonal()) / float(sum(mat_rf)) print "Guete rf:", guete_rf
<!–
Um mit bereits implementierten Methoden von scikit zu arbeiten, müssen zusätlich die Module numpy, scipy und sklearn installiert werden. Dies geschieht am besten, auf der Kommandozeile / Konsole mit pip
. Vorgänging muss bereits Python (2.7) installiert sein:
C:\Python27\Scripts
cd C:\Python27\Scripts
pip install …
und die Modulnamenpip install numpy pip install scipy pip install sklearn pip install imageio
Ein wichtiges Konstrukt sind sogenannte numpy-Arrays: Diese sind grundsätzlich der Python (geschachtelten) Liste sehr ähnlich, sind aber im Umang viel praktischer. Mehr Details gibt's z.B. hier und hier. Das wichtigste dabei ist wohl, wie auf Elemente zugegriffen werden kann und wie Teilmengen von diesen gebildet werden können:
data = array([[2,3],[3,4]]) #zugriff via Zeile/Spalte: data[0,1] data[1,1] # fuer trainingsdaten trainingdata[0,256] # die Zahl des ersten Bildes trainingdata[4,2] # den Wert des dritten Pixel des 5 Bildes trainingsdata[0,:] # alle Werte inkl. Zahl des ersten Bildes.
Nachher kann relativ einfach mit Python kNN implementiert werden:
#!/usr/bin/python # -*- coding: utf-8 -*- from sklearn.neighbors import KNeighborsClassifier from numpy import genfromtxt,asarray from imageio import imread # Trainingdaten einlesen trainingdata = genfromtxt('C:/temp/digits/digitsdata.csv', delimiter=',') # Testdaten einlesen testdata = genfromtxt('C:/temp/digits/digitsdatatest.csv', delimiter=',') # Trainingsfeatures und -klassen trennen. # Kolonne 0:255: Features, Kolonne 256 Klasse # Fuer Ekrlaerung der Syntax siehe: https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html train_digits_features = trainingdata[:, :-1] train_digits_class = trainingdata[:, -1] # Testfeatures und -klassen trennen. test_digits_features = testdata[:, :-1] test_digits_class = testdata[:, -1] # kNN initilisieren mit 5 Nachbarn knn = KNeighborsClassifier(n_neighbors=5) # kNN traininieren knn.fit(train_digits_features, train_digits_class) # Vorhersage predicted_class = knn.predict(test_digits_features) # Wahre Test digits ausgeben print(test_digits_class) # Predicted Test digits ausgeben print(predicted_class) ## Vergleichen und Klassifikationsguete berechnen. ## Differenz muss = 0 sein wenn richtig klassifziert. Boolsche Variablen koennen summiert werden. print sum(predicted_class - test_digits_class == 0) / float(len(predicted_class)) # Aquivalent der Funktion getPixelListFromFilePath def getPixelArrayFromFilePath(filepath): img = imread(filepath) return(asarray(((img.flatten()/float(255)*2-1).reshape(1,-1)))) getPixelArrayFromFilePath("C:/temp/digits/test/testimg_181710187299_0_.gif") print(knn.predict(getPixelArrayFromFilePath("C:/temp/digits/test/testimg_181710187299_0_.gif")))
Die Slides der Stunde finden sich hier.
Eine Implementierung in Python, die knn_scikit.py
aufgreift findet sich untenstehend
#!/usr/bin/python # -*- coding: utf-8 -*- from sklearn.neighbors import KNeighborsClassifier from numpy import genfromtxt, asarray, sum from sklearn.metrics import confusion_matrix from sklearn.ensemble import RandomForestClassifier ####################################################################### # # Daten einlesen. Für Random Forests (rf) und k-Nearest-Neighbors (knn) # ####################################################################### # Trainingdaten einlesen trainingdata = genfromtxt('C:/temp/digits/digitsdata.csv', delimiter = ',') # Testdaten einlesen testdata = genfromtxt('C:/temp/digits/digitsdatatest.csv', delimiter = ',') # Trainingsfeatures und -klassen trennen. # Kolonne 0:255: Features, Kolonne 256 Klasse # Fuer Ekrlaerung der Syntax siehe: # https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html train_digits_features = trainingdata[: , : -1] train_digits_class = trainingdata[: , -1] # Testfeatures und - klassen trennen. test_digits_features = testdata[: , : -1] test_digits_class = testdata[: , -1] ####################################################################### # # kNN trainieren, vorhersagen und evaluieren. # ####################################################################### #kNN initilisieren mit 10 Nachbarn knn = KNeighborsClassifier(n_neighbors = 10) # kNN traininieren knn.fit(train_digits_features, train_digits_class) # Vorhersage predicted_class_knn = knn.predict(test_digits_features) # Konfusionsmatrix mat_knn = confusion_matrix(test_digits_class, predicted_class_knn) print(mat_knn)# Güte guete_knn = sum(mat_knn.diagonal()) / float(sum(mat_knn)) print "Guete kNN:", guete_knn ####################################################################### # # Random Forests trainieren, vorhersagen und evaluieren. # ####################################################################### rf = RandomForestClassifier(n_estimators = 200, random_state = 17) rf.fit(train_digits_features, train_digits_class) predicted_class_rf = rf.predict(test_digits_features) mat_rf = confusion_matrix(test_digits_class, predicted_class_rf) print(mat_rf) # Güte guete_rf = sum(mat_rf.diagonal()) / float(sum(mat_rf)) print "Guete rf:", guete_rf
Prüfung und ANN