Skip to content
Snippets Groups Projects
Commit 8e5d9eff authored by SUR Frederic's avatar SUR Frederic
Browse files

2024-2025

parent 496905c2
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Introduction à l'apprentissage automatique - TP4 exercice 1 # Introduction à l'apprentissage automatique - TP4 exercice 1
### SVM sur données synthétiques ### SVM sur données synthétiques
<br> <br>
Dans la [documentation scikit-learn sur les SVM](http://scikit-learn.org/stable/modules/svm.html), lisez l'introduction. Dans la [documentation scikit-learn sur les SVM](http://scikit-learn.org/stable/modules/svm.html), lisez l'introduction.
On utilisera la [classe SVC](http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html). On utilisera la [classe SVC](http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html).
Notez la valeur par défaut de l'hyperparamètre $C$, et les fonctions noyau disponibles nativement (ainsi que leurs paramètres). Notez la valeur par défaut de l'hyperparamètre $C$, et les fonctions noyau disponibles nativement (ainsi que leurs paramètres).
<br> <br>
Dans les questions suivantes, nous séparerons les bases de données Dans les questions suivantes, nous séparerons les bases de données
entre bases d'apprentissage (80% de la base initiale) et base de test (20%) en utilisant `model_selection.train_test_split`, et nous comparerons les performances des différents classifieurs en calculant le score de classification sur la base de test. entre bases d'apprentissage (80% de la base initiale) et base de test (20%) en utilisant `model_selection.train_test_split`, et nous comparerons les performances des différents classifieurs en calculant le score de classification sur la base de test.
<br> <br>
On commence par charger les bibliothèques utiles et définir une fonction de visualisation que nous utiliserons par la suite: On commence par charger les bibliothèques utiles et définir une fonction de visualisation que nous utiliserons par la suite.
__Remarque__: si la visualisation par la fonction `plot_classif_result_SVM` prend trop de temps, augmentez la valeur du pas `h` dans la définition de cette fonction (faites `h=0.05`).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from sklearn import datasets, model_selection, preprocessing, model_selection, svm from sklearn import datasets, model_selection, preprocessing, model_selection, svm
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap from matplotlib.colors import ListedColormap
from matplotlib.colors import Normalize from matplotlib.colors import Normalize
%matplotlib inline %matplotlib inline
# fonction de visualisation # fonction de visualisation
def plot_classif_result_SVM(X,y,clf,title): def plot_classif_result_SVM(X,y,clf,title):
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA']) cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00']) cmap_bold = ListedColormap(['#FF0000', '#00FF00'])
cmap2 = ListedColormap(['#FF8888', '#FFAAAA', '#AAFFAA', '#88FF88']) cmap2 = ListedColormap(['#FF8888', '#FFAAAA', '#AAFFAA', '#88FF88'])
h=0.01 # step size in the mesh h=0.01 # step size in the mesh
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h)) np.arange(y_min, y_max, h))
Zdf = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) Zdf = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z=Zdf>0 Z=Zdf>0
Zdfbin = (np.abs(Zdf)<=1) # 0 if inside margin, 1 if outside Zdfbin = (np.abs(Zdf)<=1) # 0 if inside margin, 1 if outside
Color=np.zeros(Z.shape) # colors for each region Color=np.zeros(Z.shape) # colors for each region
for i in range(len(Z)): for i in range(len(Z)):
if (Z[i]): if (Z[i]):
if Zdfbin[i]: Color[i]=2 if Zdfbin[i]: Color[i]=2
else: Color[i]=3 else: Color[i]=3
else: else:
if Zdfbin[i]: Color[i]=1 if Zdfbin[i]: Color[i]=1
else: Color[i]=0 else: Color[i]=0
# Put the result into a color plot # Put the result into a color plot
Z = Z.reshape(xx.shape) Z = Z.reshape(xx.shape)
Color = Color.reshape(xx.shape) Color = Color.reshape(xx.shape)
plt.figure(figsize=[10,8]) plt.figure(figsize=[10,8])
plt.pcolormesh(xx, yy, Color, cmap=cmap2, shading='auto') plt.pcolormesh(xx, yy, Color, cmap=cmap2, shading='auto')
# Plot also the training points # Plot also the training points
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold, edgecolor='k', s=20) plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold, edgecolor='k', s=20)
# Plot the support vectors (stars): # Plot the support vectors (stars):
plt.scatter(X[clf.support_, 0], X[clf.support_, 1], c=y[clf.support_], cmap=cmap_bold,edgecolor='k',s=80, marker='*') plt.scatter(X[clf.support_, 0], X[clf.support_, 1], c=y[clf.support_], cmap=cmap_bold,edgecolor='k',s=80, marker='*')
plt.xlim(xx.min(), xx.max()) plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max()) plt.ylim(yy.min(), yy.max())
plt.title(title); plt.title(title);
plt.axis("equal") plt.axis("equal")
plt.show() plt.show()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## 1. Jeu de données "blobs" ## 1. Jeu de données "blobs"
Commençons par un jeu de données constitué de 1000 points du plan dans deux classes linéairement séparables, obtenu avec `make_blobs` (comme dans un des TP précédents). Commençons par un jeu de données constitué de 1000 points du plan dans deux classes linéairement séparables, obtenu avec `make_blobs` (comme dans un des TP précédents).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# génération dataset # génération dataset
# on précise random_state pour travailler tous sur le même jeu de données # on précise random_state pour travailler tous sur le même jeu de données
# avec cluster_std=1.5 dans la ligne suivante, les classes sont linéairement séparables # avec cluster_std=1.5 dans la ligne suivante, les classes sont linéairement séparables
X_dataset, y_dataset = datasets.make_blobs(n_features=2, centers=2, cluster_std=1.5, n_samples=1000, random_state=1) X_dataset, y_dataset = datasets.make_blobs(n_features=2, centers=2, cluster_std=1.5, n_samples=1000, random_state=1)
X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2) X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2)
# affichage dataset train (gros points) + test (petits points) # affichage dataset train (gros points) + test (petits points)
plt.figure(figsize=[10,8]) plt.figure(figsize=[10,8])
plt.scatter(X_train[:, 0], X_train[:, 1], marker='o', c=y_train, s=25, edgecolor='k') plt.scatter(X_train[:, 0], X_train[:, 1], marker='o', c=y_train, s=25, edgecolor='k')
plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_test, s=10) plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_test, s=10)
plt.title('dataset') plt.title('dataset')
plt.axis('equal') plt.axis('equal')
plt.grid() plt.grid()
plt.show() plt.show()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Ensuite, nous entraînons une SVM à noyau linéaire, et nous visualisons les vecteurs supports à l'aide de la fonction `plot_classif_result_SVM`. Ensuite, nous entraînons une SVM à noyau linéaire, et nous visualisons les vecteurs supports à l'aide de la fonction `plot_classif_result_SVM`.
On affiche également le score de classification sur la base test, ainsi que le nombre de vecteurs supports pour chaque classe. On affiche également le score de classification sur la base test, ainsi que le nombre de vecteurs supports pour chaque classe.
__Question 1__. Les résultats dans le cas où les classes sont linéairement séparables vous semblent-ils cohérents avec le cours? __Question 1__. Les résultats dans le cas où les classes sont linéairement séparables vous semblent-ils cohérents avec le cours?
Que peut-on dire lorsque les deux classes ne sont plus linéairement séparables? (augmentez la valeur de `cluster_std`) Que peut-on dire lorsque les deux classes ne sont plus linéairement séparables? (augmentez la valeur de `cluster_std`)
En particulier, comparez la marge, les vecteurs supports, leur nombre, le score sur la base de test, et le temps de calcul. En particulier, comparez la marge, les vecteurs supports, leur nombre, le score sur la base de test, et le temps de calcul.
(pour obtenir deux classes non linéairement séparables, augmentez la valeur de `cluster_std` dans `make_blobs` dans la cellule précédente, et relancez la cellule suivante) (pour obtenir deux classes non linéairement séparables, augmentez la valeur de `cluster_std` dans `make_blobs` dans la cellule précédente, et relancez la cellule suivante)
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
SVM=svm.SVC(kernel='linear') SVM=svm.SVC(kernel='linear')
print('Apprentissage...\n') print('Apprentissage...\n')
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
print('\nAffichage...\n') print('\nAffichage...\n')
plot_classif_result_SVM(X_train,y_train,SVM,"SVM") plot_classif_result_SVM(X_train,y_train,SVM,"SVM")
%time print("score test SVM %.3f\n" % SVM.score(X_test, y_test) ) %time print("score test SVM %.3f\n" % SVM.score(X_test, y_test) )
print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1])) print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1]))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<font color=red> <font color=red>
Réponse: Réponse:
</font> </font>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## 2. Jeu de données "moons": ## 2. Jeu de données "moons":
Reprenons le jeu de données synthétique `make_moons`: Reprenons le jeu de données synthétique `make_moons`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# génération dataset # génération dataset
X_dataset, y_dataset = datasets.make_moons(noise=0.25, n_samples=1000, random_state=1) X_dataset, y_dataset = datasets.make_moons(noise=0.25, n_samples=1000, random_state=1)
X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2, random_state=1) X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2, random_state=1)
# affichage dataset train+test # affichage dataset train+test
plt.figure(figsize=[10,8]) plt.figure(figsize=[10,8])
plt.scatter(X_train[:, 0], X_train[:, 1], marker='o', c=y_train, s=25, edgecolor='k') plt.scatter(X_train[:, 0], X_train[:, 1], marker='o', c=y_train, s=25, edgecolor='k')
plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_test, s=10) plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_test, s=10)
plt.title('dataset') plt.title('dataset')
plt.axis('equal') plt.axis('equal')
plt.grid() plt.grid()
plt.show() plt.show()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Noyau linéaire ### Noyau linéaire
La celulle suivante permet l'apprentissage par SVM linéaire, avec différentes valeurs de l'hyperparamètre $C$. La celulle suivante permet l'apprentissage par SVM linéaire, avec différentes valeurs de l'hyperparamètre $C$.
__Question 2__. Vérifiez que diminuer la valeur de l'hyperparamètre $C$ augmente le nombre de vecteurs supports (et change la surface de séparation). Est-ce cohérent avec la discussion du cours? __Question 2__. Vérifiez que diminuer la valeur de l'hyperparamètre $C$ augmente le nombre de vecteurs supports (et change la surface de séparation). Est-ce cohérent avec la discussion du cours?
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("\nC=100") print("\nC=100")
SVM=svm.SVC(kernel='linear',C=100) SVM=svm.SVC(kernel='linear',C=100)
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=100") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=100")
print("score test SVM %.3f" % SVM.score(X_test, y_test) ) print("score test SVM %.3f" % SVM.score(X_test, y_test) )
print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1])) print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1]))
print("\nC=1") print("\nC=1")
SVM=svm.SVC(kernel='linear') # par défaut, C=1 SVM=svm.SVC(kernel='linear') # par défaut, C=1
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=1") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=1")
print("score test SVM %.3f" % SVM.score(X_test, y_test) ) print("score test SVM %.3f" % SVM.score(X_test, y_test) )
print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1])) print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1]))
print("\nC=1e-2") print("\nC=1e-2")
SVM=svm.SVC(kernel='linear',C=0.01) SVM=svm.SVC(kernel='linear',C=0.01)
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=0.01") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=0.01")
print("score test SVM %.3f" % SVM.score(X_test, y_test) ) print("score test SVM %.3f" % SVM.score(X_test, y_test) )
print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1])) print("nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1" % (SVM.n_support_[0],SVM.n_support_[1]))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<font color=red> <font color=red>
Réponse: Réponse:
</font> </font>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Noyau RBF ### Noyau RBF
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
__Question 4__. Pour le noyau RBF et la valeur par défaut de $\gamma$, la cellule suivante présente différentes classifications selon les valeurs de $C$. Retrouvez les situations identifiées dans le cas linéaire. __Question 4__. Pour le noyau RBF et la valeur par défaut de $\gamma$, la cellule suivante présente différentes classifications selon les valeurs de $C$. Retrouvez les situations identifiées dans le cas linéaire.
__Rappel__ : d'après __[la documentation](http://scikit-learn.org/stable/auto_examples/svm/plot_rbf_parameters.html)__ : _"The C parameter trades off correct classification of training examples against maximization of the decision function’s margin. For larger values of C, a smaller margin will be accepted if the decision function is better at classifying all training points correctly. A lower C will encourage a larger margin, therefore a simpler decision function, at the cost of training accuracy. In other words C behaves as a regularization parameter in the SVM."_ __Rappel__ : d'après __[la documentation](http://scikit-learn.org/stable/auto_examples/svm/plot_rbf_parameters.html)__ : _"The C parameter trades off correct classification of training examples against maximization of the decision function’s margin. For larger values of C, a smaller margin will be accepted if the decision function is better at classifying all training points correctly. A lower C will encourage a larger margin, therefore a simpler decision function, at the cost of training accuracy. In other words C behaves as a regularization parameter in the SVM."_
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print("C=1e-2") print("C=1e-2")
SVM=svm.SVC(kernel='rbf',C=0.01) SVM=svm.SVC(kernel='rbf',C=0.01)
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=0.01") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=0.01")
print("C=0.01, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("C=0.01, score test SVM %.3f" % SVM.score(X_test, y_test) )
print("\nC=1") print("\nC=1")
SVM=svm.SVC(kernel='rbf',C=1) # valeur par défaut SVM=svm.SVC(kernel='rbf',C=1) # valeur par défaut
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=1") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=1")
print("C=1, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("C=1, score test SVM %.3f" % SVM.score(X_test, y_test) )
print("\nC=100") print("\nC=100")
SVM=svm.SVC(kernel='rbf',C=100) SVM=svm.SVC(kernel='rbf',C=100)
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=100") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=100")
print("C=100, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("C=100, score test SVM %.3f" % SVM.score(X_test, y_test) )
print("\nC=1e6") print("\nC=1e6")
SVM=svm.SVC(kernel='rbf',C=100000) SVM=svm.SVC(kernel='rbf',C=100000)
%time SVM.fit(X_train,y_train) %time SVM.fit(X_train,y_train)
plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=100000") plot_classif_result_SVM(X_train,y_train,SVM,"SVM, C=100000")
print("C=10000, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("C=10000, score test SVM %.3f" % SVM.score(X_test, y_test) )
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<font color=red> <font color=red>
Réponse: Réponse:
</font> </font>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
La cellule suivante permet de trouver la valeur optimale de l'hyperparamètre $C$ par validation croisée à 5 plis (5-fold cross validation) sur la base d'apprentissage. La valeur de $\gamma$ reste celle fixée par défaut. La cellule suivante permet de trouver la valeur optimale de l'hyperparamètre $C$ par validation croisée à 5 plis (5-fold cross validation) sur la base d'apprentissage. La valeur de $\gamma$ reste celle fixée par défaut.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# estimation de C par 5-fold cross validation # estimation de C par 5-fold cross validation
accuracy=[] accuracy=[]
arrayC=10**(np.arange(-1,5.5,.5)) arrayC=10**(np.arange(-1,5.5,.5))
for C in arrayC: for C in arrayC:
SVM=svm.SVC(kernel='rbf',C=C) SVM=svm.SVC(kernel='rbf',C=C)
scores = model_selection.cross_val_score(SVM, X_train, y_train, cv=5) scores = model_selection.cross_val_score(SVM, X_train, y_train, cv=5)
accuracy.append(scores.mean()) accuracy.append(scores.mean())
print('C=%.4f score de validation croisée = %.4f +/- %.4f'%(C,scores.mean(),scores.std())) print('C=%.4f score de validation croisée = %.4f +/- %.4f'%(C,scores.mean(),scores.std()))
plt.figure() plt.figure()
plt.semilogx(arrayC,accuracy,'-*') plt.semilogx(arrayC,accuracy,'-*')
plt.title('score de validation croisée contre C') plt.title('score de validation croisée contre C')
plt.grid() plt.grid()
plt.show(); plt.show();
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Pour trouver une valeur optimale aux hyperparamètres $\gamma$ et $C$ par validation croisée, on dispose de la fonction [GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html). Pour trouver une valeur optimale aux hyperparamètres $\gamma$ et $C$ par validation croisée, on dispose de la fonction [GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html).
Cette fonction va calculer le score de validation croisée pour différentes valeurs des paramètres: ici, $C$ et $\gamma$ peuvent prendre des valeurs entre $10^{-3}$ et $10^3$. Par défaut, il s'agit de la validation croisée à 5 plis. Cette fonction va calculer le score de validation croisée pour différentes valeurs des paramètres: ici, $C$ et $\gamma$ peuvent prendre des valeurs entre $10^{-3}$ et $10^3$. Par défaut, il s'agit de la validation croisée à 5 plis.
__Remarque__: d'une exécution à l'autre, la sélection des plis change. Cela explique que les meilleurs paramètres trouvés peuvent changer, car ici les scores de validation croisée sont proches les uns des autres. __Remarque__: d'une exécution à l'autre, la sélection des plis change. Cela explique que les meilleurs paramètres trouvés peuvent changer, car ici les scores de validation croisée sont proches les uns des autres.
Visualisez les résultats de la cellule suivante (l'exécution peut prendre une minute): Visualisez les résultats de la cellule suivante (l'exécution peut prendre une minute):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
gamma_range=10**(np.arange(-3.,3.5,.5)) gamma_range=10**(np.arange(-3.,3.5,.5))
C_range=10**(np.arange(-3.,3.5,.5)) C_range=10**(np.arange(-3.,3.5,.5))
parameters = { 'gamma': gamma_range, 'C': C_range } parameters = { 'gamma': gamma_range, 'C': C_range }
SVM = svm.SVC(kernel='rbf') SVM = svm.SVC(kernel='rbf')
gridsearch=model_selection.GridSearchCV(SVM, parameters,cv=5) gridsearch=model_selection.GridSearchCV(SVM, parameters,cv=5)
gridsearch.fit(X_train,y_train) gridsearch.fit(X_train,y_train)
print("Meilleur estimateur trouvé:") print("Meilleur estimateur trouvé:")
print(gridsearch.best_estimator_) print(gridsearch.best_estimator_)
print("avec un score:") print("avec un score:")
print(gridsearch.best_score_) print(gridsearch.best_score_)
print("Meilleurs paramètres:") print("Meilleurs paramètres:")
print(gridsearch.best_params_) print(gridsearch.best_params_)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
__Question 5__. Quel est le score de classification sur la base de test du meilleur classifieur SVM identifié? Visualisez la surface de séparation entre les classes. __Question 5__. Quel est le score de classification sur la base de test du meilleur classifieur SVM identifié? Visualisez la surface de séparation entre les classes.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# votre code ici # votre code ici
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
__Question 6__. (révision): Comparez au résultat de la classification de la base de test par les algorithmes de classification aux 1,5, 10 plus proches voisins, au classifieur de la régression logistique, et au classifieur naïf gaussien. __Question 6__. (révision): Comparez au résultat de la classification de la base de test par les algorithmes de classification aux 1,5, 10 plus proches voisins, au classifieur de la régression logistique, et au classifieur naïf gaussien.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from sklearn import neighbors, naive_bayes, linear_model, neural_network from sklearn import neighbors, naive_bayes, linear_model, neural_network
# votre code ici # votre code ici
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<font color=red> <font color=red>
réponse: réponse:
</font> </font>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Complément facultatif: discussion du rôle du paramètre $\gamma$ du noyau RBF ### Complément facultatif: discussion du rôle du paramètre $\gamma$ du noyau RBF
_(à faire en dehors du TD)_ _(à faire en dehors du TD)_
Observez le résultat de la classification avec un noyau Observez le résultat de la classification avec un noyau
gaussien (RBF), et l'évolution selon différentes valeurs du gaussien (RBF), et l'évolution selon différentes valeurs du
paramètre `gamma` (on gardera $C=1$, valeur par défaut de `SVC`). paramètre `gamma` (on gardera $C=1$, valeur par défaut de `SVC`).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
SVM=svm.SVC(kernel='rbf',gamma=0.01) SVM=svm.SVC(kernel='rbf',gamma=0.01)
SVM.fit(X_train,y_train) SVM.fit(X_train,y_train)
%time plot_classif_result_SVM(X_train,y_train,SVM,"SVM, gamma=0.01") %time plot_classif_result_SVM(X_train,y_train,SVM,"SVM, gamma=0.01")
print("gamma=0.01, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("gamma=0.01, score test SVM %.3f" % SVM.score(X_test, y_test) )
SVM=svm.SVC(kernel='rbf',gamma=1) SVM=svm.SVC(kernel='rbf',gamma=1)
SVM.fit(X_train,y_train) SVM.fit(X_train,y_train)
%time plot_classif_result_SVM(X_train,y_train,SVM,"SVM, gamma=1") %time plot_classif_result_SVM(X_train,y_train,SVM,"SVM, gamma=1")
print("gamma=1, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("gamma=1, score test SVM %.3f" % SVM.score(X_test, y_test) )
SVM=svm.SVC(kernel='rbf',gamma=100) SVM=svm.SVC(kernel='rbf',gamma=100)
SVM.fit(X_train,y_train) SVM.fit(X_train,y_train)
%time plot_classif_result_SVM(X_train,y_train,SVM,"SVM, gamma=100") %time plot_classif_result_SVM(X_train,y_train,SVM,"SVM, gamma=100")
print("gamma=100, score test SVM %.3f" % SVM.score(X_test, y_test) ) print("gamma=100, score test SVM %.3f" % SVM.score(X_test, y_test) )
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Avec un noyau gaussien, $k(x,y)=\exp(-\gamma||x-y||^2)$ est différent de 0 seulement lorsque $x$ et $y$ sont proches relativement à $\gamma$ (disons $||x-y||<1/\sqrt{\gamma}$). Avec un noyau gaussien, $k(x,y)=\exp(-\gamma||x-y||^2)$ est différent de 0 seulement lorsque $x$ et $y$ sont proches relativement à $\gamma$ (disons $||x-y||<1/\sqrt{\gamma}$).
Le coefficient $\sqrt{\gamma}$ peut être vu comme l'inverse de la distance d'influence des vecteurs supports. En effet, le classifieur s'écrit: Le coefficient $\sqrt{\gamma}$ peut être vu comme l'inverse de la distance d'influence des vecteurs supports. En effet, le classifieur s'écrit:
$$f(x) = \sum_i \lambda_i y_i k(x_i,x) + b$$ $$f(x) = \sum_i \lambda_i y_i k(x_i,x) + b$$
__Lorsque $\gamma$ est grand__, $k(x_i,x)$ a une contribution significative seulement pour $x$ proche d'un des vecteurs supports $x_i$. Ainsi, le signe de $f$ sera différent de celui de $b$ seulement pour $x$ proche d'un vecteur support $x_i$ associé à la classe $y_i$ de signe contraire à celui de $b$. On a intérêt à ce que toutes les observations soient des vecteurs supports de manière à minimiser le nombre de mauvaises classifications, donc la somme des variables d'écart. La surface de séparation est donc la superposition de disques autours des points d'une des classes, ce que l'on observe bien ici. __Lorsque $\gamma$ est grand__, $k(x_i,x)$ a une contribution significative seulement pour $x$ proche d'un des vecteurs supports $x_i$. Ainsi, le signe de $f$ sera différent de celui de $b$ seulement pour $x$ proche d'un vecteur support $x_i$ associé à la classe $y_i$ de signe contraire à celui de $b$. On a intérêt à ce que toutes les observations soient des vecteurs supports de manière à minimiser le nombre de mauvaises classifications, donc la somme des variables d'écart. La surface de séparation est donc la superposition de disques autours des points d'une des classes, ce que l'on observe bien ici.
__Lorsque $\gamma$ est petit__, tous les $k(x_i,x)$ dans l'expression de $f$ ont une contribution. __Lorsque $\gamma$ est petit__, tous les $k(x_i,x)$ dans l'expression de $f$ ont une contribution.
Le modèle est alors trop "moyenné" et on a une surface entre classes très régulière (presque une droite ici). Le modèle est alors trop "moyenné" et on a une surface entre classes très régulière (presque une droite ici).
_Justification au passage_ (en complément): si $\gamma$ est petit, on identifie l'exponentielle et son développement limité à l'ordre 1: $\exp(-\gamma||x-y||^2)=1-\gamma||x-y||^2$. Ensuite: _Justification au passage_ (en complément): si $\gamma$ est petit, on identifie l'exponentielle et son développement limité à l'ordre 1: $\exp(-\gamma||x-y||^2)=1-\gamma||x-y||^2$. Ensuite:
$$f(x) = \sum_i \lambda_i y_i k(x_i,x) + b = \sum_i \lambda_i y_i (1-\gamma||x_i-x||^2) + b = \sum_i \lambda_i y_i (1-\gamma||x_i||^2-\gamma||x||^2-2\gamma x_i \cdot x) + b $$ $$f(x) = \sum_i \lambda_i y_i k(x_i,x) + b = \sum_i \lambda_i y_i (1-\gamma||x_i-x||^2) + b = \sum_i \lambda_i y_i (1-\gamma||x_i||^2-\gamma||x||^2-2\gamma x_i \cdot x) + b $$
Donc: Donc:
$$f(x) = \sum_i \lambda_i y_i (1-\gamma ||x_i||^2) + b - \gamma ||x||^2 \sum_i \lambda_i y_i - 2\gamma \left(\sum_i \lambda_i y_i x_i\right) \cdot x$$ $$f(x) = \sum_i \lambda_i y_i (1-\gamma ||x_i||^2) + b - \gamma ||x||^2 \sum_i \lambda_i y_i - 2\gamma \left(\sum_i \lambda_i y_i x_i\right) \cdot x$$
Comme $\sum_i \lambda_i y_i = 0$ (contrainte primale), $f(x)$ vérifie bien une relation du type $f(x) = B+ w\cdot x$, où $B=b-\sum_i \lambda_i y_i \gamma ||x_i||^2$ est une constante ne dépendant pas de $x$, et $w=- 2\gamma \left(\sum_i \lambda_i y_i x_i\right)$. Comme $\sum_i \lambda_i y_i = 0$ (contrainte primale), $f(x)$ vérifie bien une relation du type $f(x) = B+ w\cdot x$, où $B=b-\sum_i \lambda_i y_i \gamma ||x_i||^2$ est une constante ne dépendant pas de $x$, et $w=- 2\gamma \left(\sum_i \lambda_i y_i x_i\right)$.
Cela justifie que la frontière de séparation est linéaire dans le graphique obtenu pour $\gamma=0.01$! Cela justifie que la frontière de séparation est linéaire dans le graphique obtenu pour $\gamma=0.01$!
<br> <br>
Il y a sous-apprentissage avec $\gamma$ petit, et sur-apprentissage avec $\gamma$ grand. Il y a sous-apprentissage avec $\gamma$ petit, et sur-apprentissage avec $\gamma$ grand.
Lorsqu'on ne spécifie pas $\gamma$ dans `svm.SVC`, une valeur est calculée à partir des observations. Voyez dans la documentation comment la valeur par défaut de $\gamma$ est adaptée en fonction des observations. Lorsqu'on ne spécifie pas $\gamma$ dans `svm.SVC`, une valeur est calculée à partir des observations. Voyez dans la documentation comment la valeur par défaut de $\gamma$ est adaptée en fonction des observations.
</font> </font>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Dans la [documentation](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html): Dans la [documentation](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html):
`if gamma='scale' (default) is passed then it uses 1 / (n_features * X.var()) as value of gamma` `if gamma='scale' (default) is passed then it uses 1 / (n_features * X.var()) as value of gamma`
Le noyau RBF sans préciser gamma assure donc une normalisation. Attention, on normalise ici par rapport à la variance globale des caractéristiques. La normalisation par `StandardScaler` assure une normalisation caractéristique par caractéristique. Le noyau RBF sans préciser gamma assure donc une normalisation. Attention, on normalise ici par rapport à la variance globale des caractéristiques. La normalisation par `StandardScaler` assure une normalisation caractéristique par caractéristique.
__Attention__ , si vous utilisez une version de `sklearn` inférieure à 0.22, passez à `svm.SVC` l'argument `gamma='scale'` pour observer le comportement de la version actuelle (la valeur par défaut était à l'époque `auto`). __Attention__ , si vous utilisez une version de `sklearn` inférieure à 0.22, passez à `svm.SVC` l'argument `gamma='scale'` pour observer le comportement de la version actuelle (la valeur par défaut était à l'époque `auto`).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
``` ```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment