Support Vector Machine, 30 lignes

Dans cet article, je vais vous montrer comment écrire votre machine vectorielle de support très simple sans scikit-learn ou d'autres bibliothèques avec une implémentation prête à l'emploi en seulement 30 lignes en Python. Si vous vouliez comprendre l'algorithme SMO, mais que cela vous paraissait trop compliqué, alors cet article peut vous être utile.



Il est impossible d'expliquer ce qu'est la machine à vecteurs de support Matrix ... Vous devez le voir vous-même.





Ayant appris que Habré a la capacité d'insérer des éléments multimédias, j'ai créé une petite démo (si ça ne marche pas du coup, vous pouvez encore tenter votre chance avec la version sur github [1] ). Placer sur un plan (espace de deux entités X et Oui ) plusieurs points rouges et bleus (c'est notre jeu de données) et la machine effectuera la classification (chaque point de l'arrière-plan est repeint en fonction de l'endroit où la demande correspondante serait classée). Déplacez les points, changez le noyau (je vous conseille d'essayer les fonctions de base radiale) et la dureté de la bordure (constante C ). Mes excuses pour le terrible code de JS - je n'y ai écrit que quelques fois dans ma vie, pour comprendre l'algorithme, utilisez le code Python plus loin dans l'article.



Teneur









Support Vector Machine est une méthode d'apprentissage automatique (apprentissage supervisé) pour résoudre des problèmes de classification, de régression, de détection d'anomalies, etc. Nous la considérerons à l'aide de l'exemple d'un problème de classification binaire. Notre exemple de formation est un ensemble de vecteurs de caractéristiques xi classé dans l'une des deux classes yi=±1 ... Demande de classification - vecteur x auquel nous devons attribuer la classe +1 ou 1 ...



Dans le cas le plus simple, les classes de l'échantillon d'apprentissage peuvent être divisées en traçant une seule ligne droite comme sur la figure (pour un plus grand nombre d'entités, ce serait un hyperplan). Maintenant, quand une demande vient classer un point x , il est raisonnable de le placer dans la classe de quel côté il sera.



Comment choisir la meilleure ligne droite? Intuitivement, je veux que la ligne droite passe au milieu entre les classes. Pour ce faire, écrivez l'équation de la ligne droite comme xw+b=0 et mettez à l'échelle les paramètres de sorte que les points de l'ensemble de données les plus proches de la ligne droite satisfassent xw+b=±1 (plus ou moins, selon la classe) - ces points sont appelés vecteurs de support .



Dans ce cas, la distance entre les points limites des classes est 2/|w| ... Évidemment, nous voulons maximiser cette valeur afin de séparer au mieux les classes. Ce dernier équivaut à minimiser 12|w|2 , tout le problème d'optimisation est écrit





min12|w|2subject to: yi(xiw+b)10.





Si vous le résolvez, alors la classification sur demande x produit comme ça





class(x)=sign(xw+b).





Il s'agit de la machine vectorielle de support la plus simple.



Et que faire dans le cas où des points de classes différentes se pénètrent mutuellement comme sur la figure?



Nous ne pouvons plus résoudre le problème d'optimisation précédent - aucun paramètre ne remplit ces conditions. Ensuite, il est possible de permettre aux points de violer la limite du montant ξi0 , mais il est également souhaitable que ces contrevenants soient aussi peu nombreux que possible. Ceci peut être réalisé en modifiant la fonction objectif avec un terme supplémentaire (régularisation L1 ):





min(12|w|2+Ciξi)subject to: ξi+yi(xiw+b)10,ξi0,





et la procédure de classement se poursuivra comme avant. Ici l'hyperparamètre C est responsable de la force de la régularisation, c'est-à-dire qu'il détermine à quel point nous avons besoin de points pour respecter la frontière: plus C - Le plus ξi disparaîtra et moins de points violeront la limite. Les vecteurs de support dans ce cas sont les points pour lesquels ξi>0 ...



Mais que se passe-t-il si l'ensemble d'entraînement ressemble au logo de The Who et que les points ne peuvent jamais être séparés par une ligne droite?



Ici, nous serons aidés par une technique ingénieuse - l' astuce du noyau [4] . Cependant, pour l'appliquer, il faut passer au problème de Lagrange dit dual (ou dual ). Une description détaillée de celui-ci peut être trouvée dans Wikipedia [5] ou dans la sixième conférence du cours [3] . Les variables dans lesquelles le nouveau problème est résolu sont appelées multiplicateurs doubles ou de Lagrange... Le problème double est souvent plus simple que le problème d'origine et présente de bonnes propriétés supplémentaires, par exemple, il est concave même si le problème d'origine n'est pas convexe. Bien que sa solution ne coïncide pas toujours avec la solution du problème d'origine (rupture de dualité), il existe un certain nombre de théorèmes qui, sous certaines conditions, garantissent une telle coïncidence (forte dualité). Et ce n'est que notre cas, vous pouvez donc vous attaquer au double problème en toute sécurité





maxλni=1λi12ni=1nj=1yiyj(xixj)λiλj,subject to: 0λiC, for i=1,2,,n,ni=1yiλi=0,





λi - deux variables. Après avoir résolu le problème de maximisation, il est également nécessaire de calculer le paramètre b , qui n'est pas inclus dans le problème double, mais est nécessaire pour le classificateur





b=Ek,ξk0[ykiλiyi(xixk)].





Le classificateur peut (et devrait) être réécrit en termes de variables doubles





class(x)=sign(f(x)),f(x)=iλiyi(xix)+b.





Quel est l'avantage de cet enregistrement? Veuillez noter que tous les vecteurs de l'ensemble de formation sont inclus ici exclusivement sous forme de produits scalaires (xixj) ... Vous pouvez d'abord mapper des points sur une surface dans un espace de plus grande dimension, puis seulement calculer le produit scalaire des images dans le nouvel espace. Pourquoi cela peut être vu sur la figure.



Si le mapping est réussi, les images des points sont séparées par un hyperplan! En fait, tout est encore mieux: il n'y a pas besoin d'afficher, car on ne s'intéresse qu'au produit scalaire, et non aux coordonnées spécifiques des points. Ainsi, toute la procédure peut être émulée en remplaçant le produit scalaire par la fonction K(xi;xj) , qui s'appelle le noyau . Bien sûr, toutes les fonctions ne peuvent pas être un noyau - au moins hypothétiquement, il doit y avoir un mappage φ tel que K(xi;xj)=(φ(xi)φ(xj)) ... Les conditions nécessaires sont déterminées par le théorème de Mercer [6] . L'implémentation Python représentera linéaire ( K(xi;xj)=xTixj ), polynôme ( K(xi;xj)=(xTixj)d ) le noyau et le noyau des fonctions de base radiale ( K(xi;xj)=eγ|xixj|2 ). Comme vous pouvez le voir dans les exemples, les noyaux peuvent introduire leurs hyperparamètres spécifiques dans l'algorithme, ce qui affectera également son fonctionnement.



Vous avez peut-être vu une vidéo où l'action de la gravité est expliquée à l'aide de l'exemple d'un film de caoutchouc étiré sous la forme d'un entonnoir [7] . Cela fonctionne, car le mouvement d'un point sur une surface courbe dans un espace de grande dimension équivaut au mouvement de son image dans un espace de dimension inférieure, si vous lui fournissez une métrique non triviale. En fait, le noyau plie l'espace.





Algorithme SMO



Donc, on est au but, il reste à résoudre le double problème posé dans la section précédente





maxλni=1λi12ni=1nj=1yiyjK(xi;xj)λiλj,subject to: 0λiC, for i=1,2,,n,ni=1yiλi=0,





puis trouvez le paramètre





b=Ek,ξk0[ykiλiyiK(xi;xk)],





et le classificateur prendra la forme suivante





class(x)=sign(f(x)),f(x)=iλiyiK(xi;x)+b.





L'algorithme SMO (Sequential Minimal Optimization, [8] ) pour résoudre le problème dual est le suivant. Dans la boucle, en utilisant une heuristique complexe ( [9] ), une paire de variables doubles est sélectionnée λM et λL , puis la fonction objectif est minimisée sur eux, avec la condition de la constance de la somme $ en ligne $ y_ \ M \ lambda_ \ M + y_ \ L \ lambda_ \ L $ en ligne $ yMλM+yLλL et restrictions 0λMC , 0λLC (réglage de la dureté de la bordure). La condition de somme stocke la somme de tous yiλi inchangé (après tout, nous n'avons pas touché au reste des lambdas). L'algorithme s'arrête lorsqu'il détecte une observance suffisamment bonne des conditions dites KKT (Karush-Kuna-Tucker [10] ).



Je vais faire quelques simplifications.



  • Je rejetterai l'heuristique complexe de la sélection d'index (ceci est fait dans le cours de l'Université de Stanford [11] ) et j'irai sur un index, et je sélectionnerai l'autre au hasard.
  • Je refuserai de vérifier le CCP et effectuerai un nombre prédéterminé d'itérations à l'avance.
  • Dans la procédure d'optimisation elle-même, contrairement au travail classique [9] ou à l'approche de Stanford [11] , j'utiliserai l'équation vectorielle d'une droite. Cela simplifiera grandement les calculs (comparez le volume de [12] et [13] ).


Maintenant pour les détails. Les informations de l'échantillon de formation peuvent être écrites sous la forme d'une matrice





K=(y1y1K(x1;x1)y1y2K(x1;x2)y1yNK(x1;xN)y2y1K(x2;x1)y2y2K(x2;x2)y2yNK(x2;xN)yNy1K(xN;x1)yNy2K(xN;x2)yNyNK(xN;xN)).





Dans ce qui suit, j'utiliserai la notation avec deux indices ( Ki,j ) pour faire référence à un élément de la matrice et avec un index ( Kk ) pour désigner le vecteur colonne de la matrice. Collectez les variables doubles dans un vecteur colonne λ ... Nous sommes intéressés par





maxλni=1λi12λTKλL.





Supposons, à l'itération courante, que l'on veuille maximiser la fonction objectif par des indices L et M ... Nous prendrons des dérivés, il est donc pratique de sélectionner les termes contenant les indices L et M ... C'est facile à faire dans la partie avec le montant λi , mais la forme quadratique nécessitera plusieurs transformations.



Lors du calcul λTKλ la sommation est effectuée sur deux indices, soit i et j ... Mettez en évidence les paires d'indices contenant L ou M ...





Réécrivons le problème en combinant tout ce qui ne contient pas λL ou λM ... Pour faciliter le suivi des indices, faites attention à K dans l'image.

$$ display $$ \ begin {aligné} \ mathscr {L} & = \ lambda_ \ M + \ lambda_ \ L - \ sum_ {j} \ lambda_ \ M \ lambda_j K _ {\ M, j} - \ sum_ { i} \ lambda_ \ L \ lambda_i K _ {\ L, i} + \ text {const} + \\ {\ text {compensation} \ atop \ text {double count}} \ rightarrow \ qquad & + \ frac {1 } {2} \ lambda_ \ M ^ 2 K _ {\ M, \ M} + \ lambda_ \ M \ lambda_ \ LK _ {\ M, \ L} + \ frac {1} {2} \ lambda_ \ L ^ 2 K_ {\ L, \ L} = \\ & = \ lambda_ \ M \ left (1- \ sum_ {j} \ lambda_j K _ {\ M, j} \ right) + \ lambda_ \ L \ left (1 - \ sum_ {i} \ lambda_i K _ {\ L, i} \ right) + \\ & + \ frac {1} {2} \ left (\ lambda_ \ M ^ 2 K _ {\ M, \ M} + 2 \ lambda_ \ M \ lambda_ \ LK _ {\ M, \ L} + \ lambda_ \ L ^ 2 K _ {\ L, \ L} \ right) + \ text {const} = \\ & = \ boldsymbol {k} ^ T_0 \ boldsymbol {v} _0 + \ frac {1} {2} \ boldsymbol {v} ^ {\, T} _0 \, \ boldsymbol {Q} \, \ boldsymbol {v} _0 + \ text {const}, \ end {aligné} $$ display $$ L=λM+λLjλMλjKM,jiλLλiKL,i+const+ +12λ2MKM,M+λMλLKM,L+12λ2LKL,L==λM(1jλjKM,j)+λL(1iλiKL,i)++12(λ2MKM,M+2λMλLKM,L+λ2LKL,L)+const==kT0v0+12vT0Qv0+const,





const désigne des termes indépendants de λL ou λM ... Dans la dernière ligne, j'ai utilisé la notation

$$ display $$ \ begin {align} \ boldsymbol {v} _0 & = (\ lambda_ \ M, \ lambda_ \ L) ^ T, \ tag {4a} \\ \ boldsymbol {k} _0 & = \ left ( 1 - \ boldsymbol {\ lambda} ^ T \ boldsymbol {K} _ {\ M}, 1 - \ boldsymbol {\ lambda} ^ T \ boldsymbol {K} _ {\ L} \ right) ^ T, \ tag { 4b} \\ \ boldsymbol {Q} & = \ begin {pmatrix} K _ {\ M, \ M} & K _ {\ M, \ L} \\ K _ {\ L, \ M} & K _ { \ L, \ L} \\ \ end {pmatrix}, \ tag {4c} \\ \ boldsymbol {u} & = (-y_ \ L, y_ \ M) ^ T. \ tag {4d} \ end {align} $$ display $$ v0=(λM,λL)T,k0=(1λTKM,1λTKL)T,Q=(KM,MKM,LKL,MKL,L),u=(yL,yM)T.





Notez que k0+Qv0 ne dépend pas de λL ni de λM

$$ display $$ \ boldsymbol {k} _0 = \ begin {pmatrix} 1 - \ lambda_ \ M K _ {\ M, \ M} - \ lambda_ \ L K _ {\ M, \ L} - \ sum_ {i \ neq \ M, \ L} \ lambda_i K _ {\ M, i} \\ 1 - \ lambda_ \ MK _ {\ L, \ M} - \ lambda_ \ LK _ {\ L, \ L} - \ sum_ { i \ neq \ M, \ L} \ lambda_i K _ {\ L, i} \\ \ end {pmatrix} = \ begin {pmatrix} 1 - \ sum_ {i \ neq \ M, \ L} \ lambda_i K _ {\ M, i} \\ 1 - \ sum_ {i \ neq \ M, \ L} \ lambda_i K _ {\ L, i} \\ \ end {pmatrix} - \ boldsymbol {Q} \ boldsymbol {v} _0. $$ afficher $$ k0=(1λMKM,MλLKM,LiM,LλiKM,i1λMKL,MλLKL,LiM,LλiKL,i)=(1iM,LλiKM,i1iM,LλiKL,i)Qv0.





Le noyau est symétrique, donc QT=Q et tu peux écrire





L=(k0+Qv0Qv0)Tv0+12vT0Qv0+const=(k0+Qv0)Tv012vT0Qv0+const





Nous voulons effectuer la maximisation pour que $ en ligne $ y_ \ L \ lambda_ \ L + y_ \ M \ lambda_ \ M $ en ligne $ yLλL+yMλM resté constant. Pour cela, les nouvelles valeurs doivent se trouver sur la ligne droite

$$ display $$ (\ lambda_ \ M ^ \ text {nouveau}, \ lambda_ \ L ^ \ text {nouveau}) ^ T = \ boldsymbol {v} (t) = \ boldsymbol {v} _0 + t \ boldsymbol {u}. $$ afficher $$ (λnewM,λnewL)T=v(t)=v0+tu.





Il est facile de s'assurer que pour tout t

$$ afficher $$ y_ \ M \ lambda_ \ M ^ \ text {nouveau} + y_ \ L \ lambda_ \ L ^ \ text {nouveau} = y_ \ M \ lambda_ \ M + y_ \ L \ lambda_ \ L + t (-y_ \ M y_ \ L + y_ \ L y_ \ M) = y_ \ M \ lambda_ \ M + y_ \ L \ lambda_ \ L. $$ afficher $$ yMλnewM+yLλnewL=yMλM+yLλL+t(yMyL+yLyM)=yMλM+yLλL.





Dans ce cas, il faut maximiser





L(t)=(k0+Qv0)Tv(t)12vT(t)Qv(t)+const,





ce qui est facile à faire en prenant le dérivé





dL(t)dt=(k0+Qv0)Tdvdt12(d(vTQv)dv)Tdvdt==kT0u+vT0QTuvTQTu(vT0vT)Qu=kT0utuTQu.





En égalant la dérivée à zéro, nous obtenons





t=kT0uuTQu.





Et encore une chose: peut-être allons-nous monter plus loin que nécessaire et nous retrouver en dehors de la place comme sur la photo. Ensuite, vous devez prendre du recul et revenir à sa frontière

$$ display $$ (\ lambda_ \ M ^ \ text {nouveau}, \ lambda_ \ L ^ \ text {nouveau}) = \ boldsymbol {v} _0 + t _ * ^ {\ text {restr}} \ boldsymbol { u}. $$ afficher $$ (λnewM,λnewL)=v0+trestru.





Ceci termine l'itération et sélectionne de nouveaux index.





Mise en œuvre





Le diagramme schématique de la formation d'une machine à vecteurs de support simplifiée peut être écrit comme







Jetons un œil au code dans un vrai langage de programmation. Si vous n'aimez pas le code des articles, vous pouvez l'étudier sur le github [14] .



Code source de la machine vectorielle de support simplifié
import numpy as np

class SVM:
  def __init__(self, kernel='linear', C=10000.0, max_iter=100000, degree=3, gamma=1):
    self.kernel = {'poly'  : lambda x,y: np.dot(x, y.T)**degree,
         'rbf': lambda x,y: np.exp(-gamma*np.sum((y-x[:,np.newaxis])**2,axis=-1)),
         'linear': lambda x,y: np.dot(x, y.T)}[kernel]
    self.C = C
    self.max_iter = max_iter

  #   t,       
  def restrict_to_square(self, t, v0, u): 
    t = (np.clip(v0 + t*u, 0, self.C) - v0)[1]/u[1]
    return (np.clip(v0 + t*u, 0, self.C) - v0)[0]/u[0]

  def fit(self, X, y):
    self.X = X.copy()
    #   0,1  -1,+1;     sklearn
    self.y = y * 2 - 1 
    self.lambdas = np.zeros_like(self.y, dtype=float)
    #  (3)
    self.K = self.kernel(self.X, self.X) * self.y[:,np.newaxis] * self.y
    
    #  self.max_iter 
    for _ in range(self.max_iter):
      #     
      for idxM in range(len(self.lambdas)):                                    
        # idxL  
        idxL = np.random.randint(0, len(self.lambdas))                         
        #  (4)
        Q = self.K[[[idxM, idxM], [idxL, idxL]], [[idxM, idxL], [idxM, idxL]]] 
        #  (4a)
        v0 = self.lambdas[[idxM, idxL]]                                        
        #  (4b)
        k0 = 1 - np.sum(self.lambdas * self.K[[idxM, idxL]], axis=1)           
        #  (4d)
        u = np.array([-self.y[idxL], self.y[idxM]])                            
        #   (5),    idxM = idxL
        t_max = np.dot(k0, u) / (np.dot(np.dot(Q, u), u) + 1E-15) 
        self.lambdas[[idxM, idxL]] = v0 + u * self.restrict_to_square(t_max, v0, u)
    
    #    
    idx, = np.nonzero(self.lambdas > 1E-15) 
    #  (1)
    self.b = np.mean((1.0-np.sum(self.K[idx]*self.lambdas, axis=1))*self.y[idx]) 
  
  def decision_function(self, X):
    return np.sum(self.kernel(X, self.X) * self.y * self.lambdas, axis=1) + self.b

  def predict(self, X): 
    #   -1,+1  0,1;     sklearn
    return (np.sign(self.decision_function(X)) + 1) // 2

      
      









Lors de la création d'un objet de la classe SVM, vous pouvez spécifier des hyperparamètres. L'entraînement se fait en appelant la fonction fit, les classes doivent être spécifiées comme 0 et 1 (converti en interne en 1 et +1 , conçu pour une plus grande compatibilité avec sklearn), la dimension du vecteur d'entités est autorisée de manière arbitraire. La fonction de prédiction est utilisée pour la classification.





Comparaison avec sklearn.svm.SVC



Non pas que cette comparaison ait beaucoup de sens, car nous parlons d'un algorithme extrêmement simplifié, développé uniquement dans le but d'enseigner aux étudiants, mais quand même. Pour tester (et pour voir comment tout utiliser), vous pouvez faire ce qui suit (ce code est également disponible sur le github [14] ).



Comparaison avec sklearn.svm.SVC sur un jeu de données 2D simple
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
from sklearn.datasets import make_blobs, make_circles
from matplotlib.colors import ListedColormap

def test_plot(X, y, svm_model, axes, title):
  plt.axes(axes)
  xlim = [np.min(X[:, 0]), np.max(X[:, 0])]
  ylim = [np.min(X[:, 1]), np.max(X[:, 1])]
  xx, yy = np.meshgrid(np.linspace(*xlim, num=700), np.linspace(*ylim, num=700))
  rgb=np.array([[210, 0, 0], [0, 0, 150]])/255.0
  
  svm_model.fit(X, y)
  z_model = svm_model.decision_function(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
  
  plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
  plt.contour(xx, yy, z_model, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
  plt.contourf(xx, yy, np.sign(z_model.reshape(xx.shape)), alpha=0.3, levels=2, cmap=ListedColormap(rgb), zorder=1)
  plt.title(title)

X, y = make_circles(100, factor=.1, noise=.1)
fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(12,4))
test_plot(X, y, SVM(kernel='rbf', C=10, max_iter=60, gamma=1), axs[0], 'OUR ALGORITHM')
test_plot(X, y, SVC(kernel='rbf', C=10, gamma=1), axs[1], 'sklearn.svm.SVC')

X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=1.4)
fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(12,4))
test_plot(X, y, SVM(kernel='linear', C=10, max_iter=60), axs[0], 'OUR ALGORITHM')
test_plot(X, y, SVC(kernel='linear', C=10), axs[1], 'sklearn.svm.SVC')

fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(12,4))
test_plot(X, y, SVM(kernel='poly', C=5, max_iter=60, degree=3), axs[0], 'OUR ALGORITHM')
test_plot(X, y, SVC(kernel='poly', C=5, degree=3), axs[1], 'sklearn.svm.SVC')

      
      







Après le lancement, des images seront générées, mais comme l'algorithme est aléatoire, elles seront légèrement différentes à chaque lancement. Voici un exemple du fonctionnement de l'algorithme simplifié (de gauche à droite: noyaux linéaires, polynomiaux et rbf)



Et c'est le résultat de la version industrielle de la machine à vecteurs de support.



Si la dimension 2 semble trop petit, vous pouvez toujours tester sur MNIST



Comparaison avec sklearn.svm.SVC sur 2 classes de MNIST
from sklearn import datasets, svm
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

class_A = 3
class_B = 8

digits = datasets.load_digits()
mask = (digits.target == class_A) | (digits.target == class_B)
data = digits.images.reshape((len(digits.images), -1))[mask]
target = digits.target[mask] // max([class_A, class_B]) # rescale to 0,1
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.5, shuffle=True)

def plot_confusion(clf):
  clf.fit(X_train, y_train)
  y_fit = clf.predict(X_test)

  mat = confusion_matrix(y_test, y_fit)
  sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False, xticklabels=[class_A,class_B], yticklabels=[class_A,class_B])
  plt.xlabel('true label')
  plt.ylabel('predicted label');
  plt.show()

print('sklearn:')
plot_confusion(svm.SVC(C=1.0, kernel='rbf', gamma=0.001))
print('custom svm:')
plot_confusion(SVM(kernel='rbf', C=1.0, max_iter=60, gamma=0.001))

      
      







Pour MNIST, j'ai essayé plusieurs paires aléatoires de classes (l'algorithme simplifié ne prend en charge que la classification binaire), mais je n'ai trouvé aucune différence dans le travail de l'algorithme simplifié et de sklearn. La matrice de confusion suivante donne une idée de la qualité.







Conclusion



Merci à tous ceux qui ont lu jusqu'au bout. Dans cet article, nous avons examiné une implémentation de tutoriel simplifiée d'une machine à vecteurs de support. Bien sûr, il ne peut pas rivaliser avec un design industriel, mais en raison de l'extrême simplicité et du code compact en Python, ainsi que du fait que toutes les idées de base de SMO ont été préservées, cette version de SVM pourrait bien prendre sa place dans le salle de cours. Il convient de noter que l'algorithme est plus simple non seulement qu'un algorithme très délicat [9] , mais même sa version simplifiée de l'Université de Stanford [11] . Après tout, une machine à vecteurs de support en 30 lignes est magnifique.



Liste de références



  1. https://fbeilstein.github.io/simplest_smo_ever/
  2. page sur http://www.machinelearning.ru
  3. "Les débuts de l'apprentissage automatique", KAU
  4. https://en.wikipedia.org/wiki/Kernel_method
  5. https://en.wikipedia.org/wiki/Duality_(optimization)
  6. http://www.machinelearning.ru
  7. https://www.youtube.com/watch?v=MTY1Kje0yLg
  8. https://en.wikipedia.org/wiki/Sequential_minimal_optimization
  9. Platt, John. Fast Training of Support Vector Machines using Sequential Minimal Optimization, in Advances in Kernel Methods – Support Vector Learning, B. Scholkopf, C. Burges, A. Smola, eds., MIT Press (1998).
  10. https://en.wikipedia.org/wiki/Karush%E2%80%93Kuhn%E2%80%93Tucker_conditions
  11. http://cs229.stanford.edu/materials/smo.pdf
  12. https://www.researchgate.net/publication/344460740_Yet_more_simple_SMO_algorithm
  13. http://fourier.eng.hmc.edu/e176/lectures/ch9/node9.html
  14. https://github.com/fbeilstein/simplest_smo_ever



All Articles