[PYTHON] Il était difficile de comprendre les astuces du noyau dans les machines vectorielles de support (Partie 2: Régression du noyau, régression de crête, etc.)

introduction

J'essaie de comprendre le modèle d'inférence utilisé dans l'apprentissage automatique. Cette fois, j'ai résumé une technique appelée astuce du noyau. La dernière fois, j'ai résumé un aperçu et des exemples de mise en œuvre.

J'ai essayé de bien comprendre la machine vectorielle de support (Partie 1: j'ai essayé le noyau polynomial / RBF en utilisant MakeMoons comme exemple) https://qiita.com/Fumio-eisan/items/f2553266a509d6f2d3a3

Cette fois, la méthode appelée kernel trick a été très difficile à comprendre pour moi, et j'ai eu du mal. Je sentais que j'avais besoin de plus de connaissances mathématiques que de comprendre la régression logistique et les réseaux neuronaux. .. Je voudrais le résumer de manière facile à comprendre, en tenant compte autant que possible des points difficiles à comprendre.

Le contour est ci-dessous.

Je veux créer une fonction non linéaire, alors que dois-je faire?

La machine à vecteurs de support est un classificateur qui peut classer les frontières non linéaires (= séparation superplan). S'il y a une limite que vous souhaitez diviser comme le montre la figure ci-dessous à gauche, formulez une fonction le long de cette limite pour la distinguer. Maintenant, il existe trois techniques (pour autant que je sache) pour créer des fonctions non linéaires. L'une est une méthode appelée expansion de Taylor, qui est une méthode de ** approximation ** de fonctions triangulaires telles que $ sin et cos $ avec des fonctions d'ordre supérieur. Vient ensuite une technique appelée la classe de Fourier. Cette méthode est souvent utilisée dans les thèmes liés au mouvement des vagues, et est exprimée par la somme de $ sin et cos $, qui ont également une périodicité, pour une onde périodique. Et la dernière est ce que l'on appelle la méthode du noyau. Il s'agit d'une méthode d'expression avec des fonctions telles que exp et polynomial, qui sont des fonctions exponentielles.

image.png

Ensuite, une régression non linéaire est effectuée par cette méthode appelée méthode du noyau. À ce stade, envisagez de créer une fonction qui correspond aux données de test, comme illustré dans la figure ci-dessous. À ce stade, une courbe qui ne ressemble pas à la fonction que vous recherchez peut être dessinée. C'est ce qu'on appelle le surapprentissage. En guise de contre-mesure, nous ajouterons un terme de régularisation pour éviter le surapprentissage.

image.png

Créez la fonction optimale de cette manière. Dès la prochaine fois, j'aimerais le comprendre en le mettant en œuvre en mâchant un peu plus.

Comprendre la régression du noyau

Passons maintenant à la régression du noyau. Comme je l'ai mentionné brièvement plus tôt, je le comprends comme un moyen de tracer des lignes non linéaires. Une expression (pensée) similaire est la classe de Fourier. L'idée est d'exprimer le mouvement des ondes en ajoutant des ondes sin et cos. Au travail, j'ai utilisé cette technique pour analyser les pulsations circulant dans les conduites de gaz. Par conséquent, je pensais que la régression du noyau avait une logique similaire.

image.png

Désormais, la régression du noyau est représentée par la somme des fonctions du noyau $ N $. Ce nombre $ N $ est le nombre d'échantillons de données de test.

f({\bf x}) = \sum_{i=1}^{N} \alpha_i k({\bf x}^{(i)}, {\bf x})

L'endroit représenté par ce $ k ({\ bf x} ^ {(i)}, {\ bf x}) $ est appelé une fonction du noyau. Il existe plusieurs types de cette fonction du noyau. Les noyaux typiques sont le noyau gaussien et le noyau polymorphe. Il est exprimé par la formule suivante.

Noyau gaussien:
k({\bf x}, {\bf x}') = exp(-\beta \|{\bf x} - {\bf x}'\|^2)\\

Noyau polygonal:
k({\bf x}, {\bf x}') =  ({\bf x} {\bf x}'+c)^p

Jouez avec le noyau gaussien

Cette fois, je voudrais l'exprimer en utilisant le noyau gaussien. Dans le noyau gaussien, vous pouvez considérer la valeur de $ β $ comme la hauteur de la valeur absolue et $ x '$ comme la position du nombre d'échantillons. La fonction lorsque $ β $ et'x '' sont modifiés est indiquée ci-dessous.

kernel.ipynb


import numpy as np

def kernel(xi, xj, beta=1):
    return np.exp(- beta * np.sum((xi - xj)**2))

X = np.arange(-1.5, 1.5, 0.05)
Y = np.zeros(len(X))
for i in range(len(X)):
    Y[i] = kernel(X[i], 0, 10)
    
X1 = np.arange(-1.5, 1.5, 0.05)
Y1 = np.zeros(len(X1))
for i in range(len(X1)):
    Y1[i] = kernel(X[i], 0, 1)
    
X2 = np.arange(-1.5, 1.5, 0.05)
Y2 = np.zeros(len(X2))
for i in range(len(X2)):
    Y2[i] = kernel(X[i], -1, 1)


plt.figure(figsize=(7, 4))
plt.plot(X2, Y2,label='beta=1,x=-1')
plt.plot(X1, Y1,label='beta=1')
plt.plot(X, Y,label='beta=10')
plt.legend()
plt.show()

015.png

Vous pouvez voir que vous pouvez dessiner une fonction plus nette en augmentant $ β $. Vous pouvez également voir qu'en manipulant la valeur de $ x '$, la position de l'apex se déplace.

Maintenant, parlons de la composition de cette fonction du noyau. Afin de représenter la fonction non linéaire réelle, il est nécessaire d'additionner ce noyau gaussien. Dans le programme ci-dessous, les noyaux gaussiens générés sont additionnés et tracés.

kernel.ipynb


def kernel(xi, xj, beta=1):
    return np.exp(- beta * np.sum((xi - xj)**2))

X = np.arange(-1.5, 1.5, 0.01)
Y = np.zeros(len(X))

centers = [0,-1,1]
weights = [1,-1,2]
for i in range(len(X)):
    for weight, center in zip(weights, centers):
        Y[i] += weight * kernel(X[i], center, 10)

plt.figure(figsize=(7, 4))
plt.plot(X, Y)
plt.show()

016.png

J'ai mis un nombre approprié et je l'ai calculé, mais j'ai pu exprimer une forme non linéaire désordonnée de manière appropriée.

Créer une fonction de noyau optimisée en fonction des données de test

Maintenant que nous savons que nous avons une fonction non linéaire, nous aimerions comprendre la procédure de création d'une fonction non linéaire arbitraire. Dans le cas de la régression linéaire, le coefficient est optimisé en minimisant la fonction d'erreur. L'idée est la même pour la régression du noyau. Optimisé pour $ N $ de $ alpha_ {0 à N} $ dans des exemples de données de test $ N $ (par exemple $ (x ^ {(1)}, y ^ {(1)}) $) Vous pouvez créer une fonction qui peut exprimer la non-linéarité en exécutant.

\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)

Et

{\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)

Exprimons la fonction d'erreur comme $ R (α) $ et soit le carré de la différence entre les valeurs des données de test $ y $ et $ f (x) $.

\begin{align}
R({\bf \alpha}) &= \sum_{i=1}^{N}\left\{y^{(i)}- f({\bf x}^{(i)})\right\}^2
=\sum_{i=1}^{N}\left\{y^{(i)}- \sum_{i=1}^{N} \alpha_i k({\bf x}^{(i)}, {\bf x})\right\}^2 \\
&=(\begin{array} yy_1 - \alpha_1k(\bf x^{(1)}-\bf x)&y_2 - \alpha_2k(\bf x^{(2)}-\bf x)  &\cdots&y_n - \alpha_Nk(\bf x^{(N)}-\bf x) \end{array})
\begin{pmatrix}
y_1 - \alpha_1k(\bf x^{(1)}-\bf x)\\
y_2 - \alpha_2k(\bf x^{(2)}-\bf x) \\
\vdots \\
y_n - \alpha_Nk(\bf x^{(N)}-\bf x)\\ 
\end{pmatrix}\\

&=({\bf y}- K {\bf \alpha})^{\mathrm{T}}({\bf y}- K {\bf \alpha})
\end{align}

Vous pouvez représenter la fonction d'erreur comme suit. Aussi, concernant $ \ alpha $,


\bf y = \bf K\bf α \\
∴\bf α = \bf K^{-1} \bf y

Peut être obtenu en utilisant la matrice inverse de la fonction noyau.

Maintenant, je voudrais reproduire la courbe dessinée par la fonction $ cos $ comme un ensemble de données approprié.

kernel.ipynb


import pandas as pd

def wave_dataset(size, xlim=[0, 2], scale=None):
    x = np.random.uniform(xlim[0], xlim[1], size)
    y = np.cos(x)
    if scale is not None:
        noize = np.random.normal(0, scale, size)
        y = y + noize
    df = pd.DataFrame()
    df['x'] = x
    df['y'] = y
    return df

data = wave_dataset(20, xlim=[-3.14, 3.14], scale=0.05)

plt.figure(figsize=(7, 4))
plt.scatter(data['x'].values, data['y'].values)
plt.ylim((-1.5, 1.5))
plt.show()

017.png

Vous pouvez générer des valeurs aléatoires avec la méthode np.random.uniform (). J'ai ajouté cette valeur à la valeur obtenue par la fonction $ cos $ pour générer une valeur légèrement différente.

Ensuite, nous entrerons des valeurs dans la fonction du noyau. Puis calculez $ \ alpha $ à partir de la matrice inverse.

kernel.ipynb


from itertools import combinations_with_replacement

X = data['x'].values
Y = data['y'].values

#Hyper paramètres
beta = 1

#Le nombre de données
N = X.shape[0]

#Calcul de la matrice de Gram
K = np.zeros((N, N))
for i, j in combinations_with_replacement(range(N), 2):
    K[i][j] = kernel(X[i], X[j])
    K[j][i] = K[i][j]

#Calculer le poids
alpha = np.linalg.inv(K).dot(Y)

Vous êtes maintenant prêt à partir. Enfin, générons et écrivons une fonction en effectuant une régression du noyau à l'aide d'un ensemble de données.

kernel.ipynb


#Régression du noyau
def kernel_predict(X, x, alpha, beta):
    Y = 0
    for i in range(len(X)):
        Y += alpha[i] * kernel(X[i], x, beta)
    return Y

#Prédire les résultats par régression
X_axis = np.arange(-3.14, 3.14, 0.01)
Y_predict = np.zeros(len(X_axis))
for i in range(len(X_axis)):
    Y_predict[i] = kernel_predict(X, X_axis[i], alpha, beta)

#Résultat du tirage au sort
plt.figure(figsize=(7, 4))

##Données d'observation
plt.scatter(data['x'].values, data['y'].values)
##Vraie fonction
plt.plot(X_axis, np.cos(X_axis), c='red')
##Fonction prédite
plt.plot(X_axis, Y_predict)

plt.ylim((-1.5, 1.5))
plt.show()

019.png

Le bleu est la fonction générée par la régression du noyau. Bien sûr, c'est proche du point des données de test, mais c'est différent de la fonction $ cos $. C'est un problème avec le surentraînement des modèles. En fait, la valeur de $ α $ est la suivante.

image.png

Vous pouvez voir qu'il contient une très grande valeur de l'ordre de 10 $ ^ {9-13} $. Avec cela, la valeur maximale qui peut être obtenue prendra également une grande valeur.

En guise de contre-mesure, il sera résolu en exécutant une procédure pour insérer un terme de régularisation.

Comprendre la régression des crêtes

À propos, le terme de régularisation consiste à optimiser l'erreur quadratique en ajoutant un terme appelé terme de régularisation.

\begin{align}
R({\bf \alpha})
&=({\bf y}- K {\bf \alpha})^{\mathrm{T}}({\bf y}- K {\bf \alpha}) + \lambda {\bf \alpha}^{\mathrm{T}} K {\bf \alpha}
\end{align}

Minimiser la fonction de perte en ajoutant $ \ lambda {\ bf \ alpha} ^ {\ mathrm {T}} K {\ bf \ alpha} $ s'appelle ** régression de crête **. Ce terme est appelé terme de régression de régression de crête. Fondamentalement, nous voulons minimiser la valeur de cette fonction de perte, nous devons donc également réduire $ α $. Si vous demandez $ \ alpha $ sous cette condition, cela peut être exprimé comme suit.

{\bf \alpha} = (K+\lambda I_N)^{-1}{\bf y}

En utilisant cette idée, je voudrais recalculer $ \ alpha $ et le tracer à nouveau.

kernel.ipynb



#Coefficient du terme de régularisation
lam = 0.5
alpha_r = np.linalg.inv(K + lam * np.eye(K.shape[0])).dot(Y)

#Prédire les résultats par régression
Y_predict_r = np.zeros(len(X_axis))
for i in range(len(X_axis)):
    Y_predict_r[i] = kernel_predict(X, X_axis[i], alpha_r, beta)

#Résultat du tirage au sort
plt.figure(figsize=(6.472, 4))

##Données d'observation
plt.scatter(data['x'].values, data['y'].values)
##Vraie fonction
plt.plot(X_axis, np.cos(X_axis), c='red')
##Fonction prédite
plt.plot(X_axis, Y_predict_r)

plt.ylim((-1.5, 1.5))
plt.show()

020.png

C'est fait. Je pense que je pourrais dire que j'ai pu le reproduire (bien que la zone autour de $ x = 3 $ soit un tournant en raison du manque de données de test).

en conclusion

Cela a peut-être été une précipitation, mais j'ai résumé la procédure de création d'une fonction non linéaire dans une machine à vecteurs de support. C'était difficile car je devais souvent le comprendre mathématiquement. Cependant, d'un autre côté, je sens que j'ai pris conscience de l'attrait de cet algorithme en comprenant l'idée.

Voici un article auquel j'ai beaucoup fait référence.

Méthode linéaire et méthode du noyau (analyse de régression) https://qiita.com/wsuzume/items/09a59036c8944fd563ff

Le programme complet est ici. https://github.com/Fumio-eisan/SVM_20200417

Recommended Posts

Il était difficile de comprendre les astuces du noyau dans les machines vectorielles de support (Partie 2: Régression du noyau, régression de crête, etc.)
Le livre de PyTorch était difficile à comprendre, alors je l'ai complété
J'ai essayé de comprendre attentivement la machine vectorielle de support (Partie 1: J'ai essayé le noyau polynomial / RBF en utilisant MakeMoons comme exemple).