Schritt für Schritt zur Theorie, Implementierung in Python und Analyse mit scikit-learn über den Algorithmus, der zuvor in "Klassifikation des maschinellen Lernens" verwendet wurde. Ich werde mit lernen. Ich schreibe es zum persönlichen Lernen, daher möchte ich, dass Sie alle Fehler übersehen.
Letztes Mal wurde die Klassifizierung für zwei Klassen auf die Klassifizierung für mehrere Klassen erweitert. Dieses Mal werde ich es tatsächlich in Python implementieren.
Ich habe auf die folgenden Seiten verwiesen. Vielen Dank.
Ich möchte Zuvor implementierte logistische Regression auf mehrere Klassen ausweiten. Die Methode ist
Ich werde es versuchen.
Ayame-Daten werden zur Klassifizierung verwendet. Es wird in 3 Klassen (setosa, versicolor, virginica) mit 4 Merkmalsgrößen (sepal_length, sepal_width, petal_length, petal_width) eingeteilt.
Im Folgenden werden wir der Übersichtlichkeit halber die Klassifizierung mit sepal_length und sepal_width implementieren.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import load_iris
sns.set()
iris = sns.load_dataset("iris")
ax = sns.scatterplot(x=iris.sepal_length, y=iris.sepal_width,
hue=iris.species, style=iris.species)
One-vs-Rest One-vs-Rest erstellt einen Zwei-Klassen-Klassifikator für jede zu lernende Label-Klasse und verwendet schließlich den plausibelsten Wert. Da die logistische Regression einen Wahrscheinlichkeitswert ausgibt, wird die Klassifizierung des Klassifikators mit der höchsten Wahrscheinlichkeit übernommen.
Wir werden die LogisticRegression-Klasse verwenden, eine leicht modifizierte Version des logistischen Regressionscodes, den wir zuletzt verwendet haben. Ich habe eine "Predict_Proba" -Methode erstellt, weil sie bestimmt, welcher Wert mit einer Wahrscheinlichkeit verwendet werden soll.
from scipy import optimize
class LogisticRegression:
def __init__(self):
self.w = None
def sigmoid(self, a):
return 1.0 / (1 + np.exp(-a))
def predict_proba(self, x):
x = np.hstack([1, x])
return self.sigmoid(self.w.T @ x)
def predict(self, x):
return 1 if self.predict_proba(x)>=0.5 else -1
def cross_entropy_loss(self, w, *args):
def safe_log(x, minval=0.0000000001):
return np.log(x.clip(min=minval))
t, x = args
loss = 0
for i in range(len(t)):
ti = 1 if t[i] > 0 else 0
h = self.sigmoid(w.T @ x[i])
loss += -ti*safe_log(h) - (1-ti)*safe_log(1-h)
return loss/len(t)
def grad_cross_entropy_loss(self, w, *args):
t, x = args
grad = np.zeros_like(w)
for i in range(len(t)):
ti = 1 if t[i] > 0 else 0
h = self.sigmoid(w.T @ x[i])
grad += (h - ti) * x[i]
return grad/len(t)
def fit(self, x, y):
w0 = np.ones(len(x[0])+1)
x = np.hstack([np.ones((len(x),1)), x])
self.w = optimize.fmin_cg(self.cross_entropy_loss, w0, fprime=self.grad_cross_entropy_loss, args=(y, x))
@property
def w_(self):
return self.w
Implementieren Sie die One-vs-Rest-Klasse. Ich habe auch die Methode "Genauigkeit_Wert" implementiert, um zu berechnen, wie korrekt die Antwort ist, da ich sie später für den Algorithmusvergleich verwenden werde.
from sklearn.metrics import accuracy_score
class OneVsRest:
def __init__(self, classifier, labels):
self.classifier = classifier
self.labels = labels
self.classifiers = [classifier() for _ in range(len(self.labels))]
def fit(self, x, y):
y = np.array(y)
for i in range(len(self.labels)):
y_ = np.where(y==self.labels[i], 1, 0)
self.classifiers[i].fit(x, y_)
def predict(self, x):
probas = [self.classifiers[i].predict_proba(x) for i in range(len(self.labels))]
return np.argmax(probas)
def accuracy_score(self, x, y):
pred = [self.labels[self.predict(i)] for i in x]
acc = accuracy_score(y, pred)
return acc
Klassifizieren Sie tatsächlich anhand der vorherigen Daten.
model = OneVsRest(LogisticRegression, np.unique(iris.species))
x = iris[['sepal_length', 'sepal_width']].values
y = iris.species
model.fit(x, y)
print("accuracy_score: {}".format(model.accuracy_score(x,y)))
accuracy_score: 0.8066666666666666
Die richtige Rücklaufquote von 81% ist nicht sehr gut. Lassen Sie uns visualisieren, wie es klassifiziert wurde. Verwenden Sie zur Visualisierung die Konturmethode von matplotlib. Farben, nach denen der Wert auf dem Gitterpunkt klassifiziert wird.
from matplotlib.colors import ListedColormap
x_min = iris.sepal_length.min()
x_max = iris.sepal_length.max()
y_min = iris.sepal_width.min()
y_max = iris.sepal_width.max()
x = np.linspace(x_min, x_max, 100)
y = np.linspace(y_min, y_max, 100)
data = []
for i in range(len(y)):
data.append([model.predict([x[j], y[i]]) for j in range(len(x))])
xx, yy = np.meshgrid(x, y)
cmap = ListedColormap(('blue', 'orange', 'green'))
plt.contourf(xx, yy, data, alpha=0.25, cmap=cmap)
ax = sns.scatterplot(x=iris.sepal_length, y=iris.sepal_width,
hue=iris.species, style=iris.species)
plt.show()
Wie Sie sehen können, ist setosa richtig klassifiziert, aber die verbleibenden zwei Klassen sind gemischt, so dass die richtige Antwortrate anscheinend etwas niedrig ist. Vorerst wird es so sein.
Implementieren Sie die LogisticRegressionMulti-Klasse für die Softmax-Klassifizierung in der logistischen Regression.
Der Kreuzentropiefehler wurde als Fehlerfunktion für die Bewertung verwendet, und die Parameter wurden unter Verwendung der Methode des steilsten Gradientenabfalls berechnet. Ich habe es ganz richtig gemacht, tut mir leid
from sklearn.metrics import accuracy_score
class LogisticRegressionMulti:
def __init__(self, labels, n_iter=1000, eta=0.01):
self.w = None
self.labels = labels
self.n_iter = n_iter
self.eta = eta
self.loss = np.array([])
def softmax(self, a):
if a.ndim==1:
return np.exp(a)/np.sum(np.exp(a))
else:
return np.exp(a)/np.sum(np.exp(a), axis=1)[:, np.newaxis]
def cross_entropy_loss(self, w, *args):
x, y = args
def safe_log(x, minval=0.0000000001):
return np.log(x.clip(min=minval))
p = self.softmax(x @ w)
loss = -np.sum(y*safe_log(p))
return loss/len(x)
def grad_cross_entropy_loss(self, w, *args):
x, y = args
p = self.softmax(x @ w)
grad = -(x.T @ (y-p))
return grad/len(x)
def fit(self, x, y):
self.w = np.ones((len(x[0])+1, len(self.labels)))
x = np.hstack([np.ones((len(x),1)), x])
for i in range(self.n_iter):
self.loss = np.append(self.loss, self.cross_entropy_loss(self.w, x, y))
grad = self.grad_cross_entropy_loss(self.w, x, y)
self.w -= self.eta * grad
def predict(self, x):
x = np.hstack([1, x])
return np.argmax(self.softmax(x @ self.w))
def accuracy_score(self, x, y):
pred = [self.predict(i) for i in x]
y_ = np.argmax(y, axis=1)
acc = accuracy_score(y_, pred)
return acc
@property
def loss_(self):
return self.loss
Die Eingabe in "LogisticRegressionMulti" verwendet ein One-Hot-Encoded-Label. Mit Pandas 'get_dummies` ist das ganz einfach. (Ich dachte, nachdem ich es gemacht habe, aber ich hätte get_dummies in der Klasse verwenden sollen)
model = LogisticRegressionMulti(np.unique(iris.species), n_iter=10000, eta=0.1)
x = iris[['sepal_length', 'sepal_width']].values
y = pd.get_dummies(iris['species']).values
model.fit(x, y)
print("accuracy_score: {}".format(model.accuracy_score(x, y)))
accuracy_score: 0.8266666666666667
Die richtige Antwortrate liegt bei 83%. Wenn man sich die Fehlerhistorie ansieht, scheint sie konvergiert zu sein, also scheint es so.
Lassen Sie uns auch die Klassifizierung auf die gleiche Weise wie zuvor ausmalen.
Vergleichen Sie schließlich unter Verwendung aller Funktionen den diesmal erstellten Klassifikator mit der LogisticRegression-Klasse von scikit-learn.
Methode | accuracy_score |
---|---|
OneVsRest | 0.98 |
LogisticRegressionMulti | 0.98 |
sklearn LogisticRegression | 0.973 |
Selbst mit dieser Implementierung scheint es, dass Sie eine gute Punktzahl erzielen können, wenn es um die Klassifizierung von Ayame geht.
Mehrklassenklassifizierung mithilfe logistischer Regression implementiert. Ich bin der Meinung, dass andere Klassifikatoren auf ähnliche Weise verwendet werden können. Insbesondere in neuronalen Netzen ist Softmax mit mehreren Klassen eine gängige Methode, daher hielt ich es für nützlich, den theoretischen Teil später zu verstehen.
Recommended Posts