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.
Dieses Mal werde ich als Zusammenfassung der linearen Regressionsausgabe die lineare Approximation unter Verwendung des Gaußschen Kernels sowie der L1-Regularisierung (Lasso) und der L2-Regularisierung (Ridge) zusammenfassen, die das Übertraining unterdrücken.
Die folgenden Seiten wurden auf diese Zeit verwiesen. Vielen Dank.
Als ich früher über [Verallgemeinerung der linearen Regression] schrieb (https://qiita.com/hiro88hyo/items/11ef220db50a87545027), schrieb ich, dass "die Basisfunktion alles sein kann". Selbst in der tatsächlichen Regression ist es nicht immer möglich, mit einer geraden Linie zu approximieren, und es ist auch notwendig, eine Knöchelnäherung wie eine polymorphe Funktion oder eine Dreiecksfunktion vorzunehmen.
Betrachten Sie beispielsweise $ y = -xsin (x) + \ epsilon $ mit Rauschen, das zu $ y = -xsin (x) $ hinzugefügt wird.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots()
t = np.linspace(0, 2*np.pi, 100)
y1 = - t * np.sin(t)
n=40
np.random.seed(seed=2020)
x = 2*np.pi * np.random.rand(n)
y2 = - x * np.sin(x) + 0.4 * np.random.normal(loc=0.0, scale=1.0, size=n)
ax.scatter(x, y2, color='blue', label="sample(with noise)")
ax.plot(t, y1, color='green', label="-x * sin(x)")
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=11)
plt.show()
Die blauen Punkte sind Beispiele mit Rauschen, und die durchgezogenen grünen Linien zeigen die erwarteten Funktionen. Das Rauschen wurde mit Zufallszahlen addiert, aber der Startwert der Zufallszahlen wurde festgelegt, so dass jedes Mal das gleiche Ergebnis erzielt wurde.
Diesmal ist die Zielfunktion bekannt, sie muss jedoch aus dem Zustand geschätzt werden, in dem nicht bekannt ist, ob die Stichprobe ein Polynom oder eine andere Funktion ist.
Der Gaußsche Kernel ist wie folgt definiert:
k(\boldsymbol{x}, \boldsymbol{x'})=\exp(-\beta||\boldsymbol{x}-\boldsymbol{x'}||^2)
Diese Funktion hat die folgende Form und kehrt durch Verschieben oder Ändern der Größe der Mitte dieser Funktion zur Zieldatenzeichenfolge zurück.
Nun die gewünschte Funktion
f({\bf x})=\sum_{i=1}^{N} \alpha k({\bf x}^{(i)}, {\bf x}')
Und. Der Beispielwert sei $ \ hat {y} $
\hat{y} =
\left(\begin{matrix}
k({\bf x}^{(1)}, {\bf x}) & k({\bf x}^{(2)}, {\bf x}) & \cdots & k({\bf x}^{(N)}, {\bf x})
\end{matrix}\right)
\left(\begin{matrix}
\alpha_0 \\
\alpha_1 \\
\vdots \\
\alpha_N
\end{matrix}\right)
Nächster,
{\bf y} =
\left(\begin{matrix}
y^{(1)} \\
y^{(2)} \\
\vdots \\
y^{(N)}
\end{matrix}\right)
,\;
K=\left(\begin{matrix}
k({\bf x}^{(1)}, {\bf x}^{(1)}) & k({\bf x}^{(2)}, {\bf x}^{(1)}) & \cdots & k({\bf x}^{(N)}, {\bf x}^{(1)}) \\
k({\bf x}^{(1)}, {\bf x}^{(2)}) & k({\bf x}^{(2)}, {\bf x}^{(2)}) & & \vdots \\
\vdots & & \ddots & \vdots \\
k({\bf x}^{(1)}, {\bf x}^{(N)}) & \cdots & \cdots & k({\bf x}^{(N)}, {\bf x}^{(N)})
\end{matrix}\right)
,\;
{\bf \alpha}
=
\left(\begin{matrix}
\alpha^{(1)} \\
\alpha^{(2)} \\
\vdots \\
\alpha^{(N)}
\end{matrix}\right)
Es wird so entwickelt (es tut mir leid für diese seltsame Kopie). Hier heißt $ K $ ** Gramm Matrix **. Der quadratische Fehler $ L $ zwischen $ f ({\ bf x}) $ und $ \ hat {y} $ ist
L=({\bf y}- K {\bf \alpha})^{\mathrm{T}}({\bf y}- K {\bf \alpha})
Und lösen Sie dies für $$ alpha
Ich habe die KernelRegression-Klasse mit der obigen Formel implementiert.
class KernelRegression: #Keine Regularisierung
def __init__(self, beta=1):
self.alpha = np.array([])
self.beta = beta
def _kernel(self, xi, xj):
return np.exp(- self.beta * np.sum((xi - xj)**2))
def gram_matrix(self, X):
N = X.shape[0]
K = np.zeros((N, N))
for i in range(N):
for j in range(N):
K[i][j] = self._kernel(X[i], X[j])
K[j][i] = K[i][j]
return K
def fit(self, X, Y):
K = self.gram_matrix(X)
self.alpha = np.linalg.inv(K)@Y
def predict(self, X, x):
Y = 0
for i in range(len(X)):
Y += self.alpha[i] * self._kernel(X[i], x)
return Y
Machen wir eine Annäherung an die erste Stichprobe.
model = KernelRegression(beta=20)
model.fit(x, y2)
xp = np.linspace(0, 2*np.pi, 100)
yp = np.zeros(len(xp))
for i in range(len(xp)):
yp[i] = model.predict(x, xp[i])
fig, ax = plt.subplots()
plt.ylim(-3,6)
ax.plot(xp, yp, color='purple', label="estimate curve")
ax.scatter(x, y2, color='blue', label="sample(with noise)")
ax.plot(t, y1, color='green', label="-x*sin(x)")
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=11)
plt.show()
Es scheint, dass es über die gegebene Probe geht, aber es ist chaotisch. Es ist völlig anders als die grüne Kurve.
Wie oben gezeigt, wird die Tatsache, dass das Lernergebnis den Lehrerdaten entspricht, aber weit von der Realität entfernt ist, als ** Überanpassung ** bezeichnet. Ist es einem Mathe-Test ähnlich, den Sie lösen können, wenn Sie das gleiche Problem wie ein Lehrbuch haben, aber Sie können es nicht lösen, sobald Sie es ein wenig arrangiert haben?
Übertrainierte Modelle werden auch als schlecht ** Generalisierungsleistung ** bezeichnet. Die Vorhersagegenauigkeit ist gering. Um dies zu verhindern, brauchen wir das Konzept der ** Regularisierung **.
Im obigen Beispiel ist der Parameter $ \ bf {\ alpha} $ extrem groß geworden, was zu einer heftigen Kurve führt, durch die die Punkte einer bestimmten Probe gezwungen werden. Danach scheint dies häufig der Fall zu sein, wenn die Anzahl der Daten extrem gering ist oder viele Variablen vorhanden sind.
Um dies zu verhindern, kann bei der Berechnung von $ \ alpha $ der Bereich von $ \ alpha $ begrenzt werden, indem dem quadratischen Fehler der Kernel-Regression ein ** Regularisierungsterm ** hinzugefügt wird. Die L1-Regularisierung (Lasso) und die L2-Regularisierung (Ridge) sind im maschinellen Lernen bekannt. Das Mischen der beiden wird als Elastic Net-Regularisierung bezeichnet.
Beginnen Sie mit L2 anstelle von L1.
Vorhin
L=({\bf y}- K {\bf \alpha})^{\mathrm{T}}({\bf y}- K {\bf \alpha})+\lambda||{\bf \alpha}||_2^2
Optimieren. Je größer $ \ lambda $ ist, desto eingeschränkter ist $ \ alpha $, was zu einer fügsameren (?) Kurve führt. Wenn dies teilweise durch $ \ alpha $ differenziert und auf 0 gesetzt und für $ \ alpha $ gelöst wird,
{\bf \alpha} = (K+\lambda I_N)^{-1}{\bf y}
Es wird sein. Dies ist leicht zu ändern, da Sie lediglich die Einheitsmatrix zur Grammmatrix hinzufügen und die inverse Matrix berechnen müssen.
Ich habe den Teil der KernelRegression-Klasse geändert, der nach $ \ alpha $ fragt. Der Rest ist fast der gleiche.
class KernelRegression: #L2-Regularisierung
def __init__(self, beta=1, lam=0.5):
self.alpha = np.array([])
self.beta = beta
self.lam = lam
def _kernel(self, xi, xj):
return np.exp(- self.beta * np.sum((xi - xj)**2))
def gram_matrix(self, X):
N = X.shape[0]
K = np.zeros((N, N))
for i in range(N):
for j in range(N):
K[i][j] = self._kernel(X[i], X[j])
K[j][i] = K[i][j]
return K
def fit(self, X, Y):
K = self.gram_matrix(X)
self.alpha = np.linalg.inv(K + self.lam * np.eye(X.shape[0]))@Y
def predict(self, X, x):
Y = 0
for i in range(len(X)):
Y += self.alpha[i] * self._kernel(X[i], x)
return Y
Versuchen Sie, mit derselben Stichprobe zu approximieren, als ob sie nicht reguliert wäre. Die Werte von $ \ beta $ und $ \ lambda $ wurden entsprechend festgelegt.
model = KernelRegression(beta=0.3, lam=0.1)
model.fit(x, y2)
xp = np.linspace(0, 2*np.pi, 100)
yp = np.zeros(len(xp))
for i in range(len(xp)):
yp[i] = model.predict(x, xp[i])
fig, ax = plt.subplots()
plt.ylim(-3,6)
ax.plot(xp, yp, color='purple', label="estimate curve")
ax.scatter(x, y2, color='blue', label="sample(with noise)")
ax.plot(t, y1, color='green', label="-x*sin(x)")
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=11)
plt.show()
Diesmal war es eine perfekte Übereinstimmung. Tatsächlich ändert sich die Form außerhalb des angezeigten Bereichs, aber Sie können sehen, dass eine ziemlich enge Approximationskurve in einem bestimmten Bereich gezeichnet werden kann.
$ \ Beta $ und $ \ lambda $ heißen ** Hyperparameter **, und Sie müssen sie tatsächlich optimieren, indem Sie sie nach und nach ändern, um den Wert der Verlustfunktion zu minimieren.
In der L2-Regularisierung als Regularisierungsbegriff$\lambda ||{\bf \alpha}||^2_{2}
Das Finden einer Lösung ist etwas kompliziert, da der Regularisierungsterm bei der L1-Regularisierung nicht unterschieden werden kann. Es scheint, dass Scikit-Learn durch eine Methode namens Coordinate Descent implementiert wird, aber ich verstehe es noch nicht, daher möchte ich Scikit-Learn in diesem Artikel gehorsam verwenden.
Die L1-Regularisierung kann einen signifikanten Teil des Parameters $ \ alpha $ auf 0 reduzieren (eine spärliche Lösung erhalten) und hilft, unerwünschte Parameter zu entfernen.
Ich werde es gehorsam mit scicit-learn umsetzen.
from sklearn.metrics.pairwise import rbf_kernel
from sklearn.linear_model import Lasso
kx = rbf_kernel(x.reshape(-1,1), x.reshape(-1,1))
KX = rbf_kernel(xp.reshape(-1,1), x.reshape(-1,1))
clf = Lasso(alpha=0.01, max_iter=10000)
clf.fit(kx, y2)
yp = clf.predict(KX)
fig, ax = plt.subplots()
plt.ylim(-3,6)
ax.plot(xp, yp, color='purple', label="estimate curve")
ax.scatter(x, y2, color='blue', label="sample(with noise)")
ax.plot(t, y1, color='green', label="-x*sin(x)")
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=11)
plt.show()
print("coef: ", clf.coef_)
coef: [-0. 0. 0. -0. -0.79703436 -0.
-0. -0. 0. -0. -0. 3.35731837
0. -0. -0.77644607 0. -0. 0.
-0. -0.19590705 0. -0. 0. -0.
0. 0. -0. -0. -0. 0.
-0. 0. -0. -0. 0. 0.58052561
0.3688375 0. -0. 1.75380012]
Dies ist auch der ursprünglichen Funktion sehr ähnlich. Sie können auch sehen, dass ein wesentlicher Teil des Parameters (coef) 0 ist.
Elasticnet Sowohl die L1-Regularisierung als auch die L2-Regularisierung fügen der Verlustfunktion einen Regularisierungsterm hinzu, aber jeder kann unabhängig betrachtet werden, und Sie können entscheiden, welche und wie viel übernommen werden soll, was als ** ElasticNet ** bezeichnet wird. Ich werde. Tatsächlich definierte die Scikit-Learn-Implementierung die ElasticNet-Parameter als Lasso und Ridge.
Bei der linearen Regression kann jede Funktion als Basisfunktion ausgewählt werden. Da der Gaußsche Kernel (nicht beschränkt auf) einen hohen Freiheitsgrad aufweist und dazu neigt, überlernt zu werden, ist es möglich, ein Regressionsmodell mit höherer Generalisierungsleistung zu erstellen, indem Einschränkungen durch L1-Regularisierung oder L2-Regularisierung hinzugefügt werden.
Ich habe die Regressionsserie mehrmals zusammengefasst, aber um Ihnen zu helfen, den Inhalt der Regression im [Scikit-Learn-Spickzettel] zu verstehen (https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html) Ist es nicht?
Tsukkomi ist willkommen, weil ich es angemessen zusammengefasst habe.
Recommended Posts