Programmation pour l’IA
scikit-learn pour la séparation linéaire;On va reprendre le dataset de la semaine dernière : iris.csv. On rappelle la visualisation suivante :
features=["sepal.length", "sepal.width"],
data ne doit contenir que les
données relatives à ces features, classes doit contenir les classes des
exemples dans \(\{-1, 1\}\) : classnames = ["setosa", "autre"]
où les éléments de classes Virginica et
Versicolor sont envoyés sur la classe "autre".df = pd.read_csv("iris.csv")
features = ["sepal.length", "sepal.width"]
classnames = ["setosa", "autre"]
data = df[features].to_numpy()
d = {"Setosa" : 1, "Virginica" : -1, "Versicolor" : -1 }
classes = df["variety"].replace(d).to_numpy(dtype=int)
data_train, data_test, classes_train, classes_test = train_test_split(data, classes, test_size=0.2, random_state=52)
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(data_train, classes_train)
accuracy = metrics.accuracy_score(classes_test, clf.predict(data_test))
print(accuracy)svm de
scikit-learn, apprenez un séparateur à vaste marge. On
pourra s’inspirer du code suivant :from sklearn import svm
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(X, y)On ajoute cela au code précédent.
from sklearn import svm
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(data_train, classes_train)# On trace les points du jeu de données
plt.scatter(data[:, 0], data[:, 1], c=classes)
# plot the decision function
ax = plt.gca()
DecisionBoundaryDisplay.from_estimator(
clf,
data,
plot_method="contour",
colors="k",
levels=[-1, 0, 1],
alpha=0.5,
linestyles=["--", "-", "--"],
ax=ax,
)
# plot support vectors
ax.scatter(
clf.support_vectors_[:, 0],
clf.support_vectors_[:, 1],
s=100,
linewidth=1,
facecolors="none",
edgecolors="k",
)
plt.show()Le code en entier. On remarquera dans ce cas que des points se
trouvent dans la bande. Ce sont des points de test qui n’ont donc pas
pris en compte dans le calcul du séparateur. On remarquera la
modification du nuage de point (scatter) pour distinguer
les données d’entraînement des données test.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.inspection import DecisionBoundaryDisplay
from sklearn.model_selection import train_test_split
from sklearn import metrics
df = pd.read_csv("iris.csv")
features = ["sepal.length", "sepal.width"]
data = df[features].to_numpy()
d = {"Setosa" : 1, "Virginica" : -1, "Versicolor" : -1 }
classes = df["variety"].replace(d).to_numpy(dtype=int)
data_train, data_test, classes_train, classes_test = train_test_split(data, classes, test_size=0.2, random_state=52)
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(data_train, classes_train)
accuracy = metrics.accuracy_score(classes_test, clf.predict(data_test))
print(f"Précision du modèle : {accuracy}")
plt.scatter(data_train[:, 0], data_train[:, 1], c=classes_train)
plt.scatter(data_test[:, 0], data_test[:, 1], c=classes_test, marker="*")
# plot the decision function
ax = plt.gca()
DecisionBoundaryDisplay.from_estimator(
clf,
data,
plot_method="contour",
colors="k",
levels=[-1, 0, 1],
alpha=0.5,
linestyles=["--", "-", "--"],
ax=ax,
)
# plot support vectors
ax.scatter(
clf.support_vectors_[:, 0],
clf.support_vectors_[:, 1],
s=100,
linewidth=1,
facecolors="none",
edgecolors="k",
)
plt.show()Il suffit de changer le début du code où les données sont chargées.
df = pd.read_csv("iris.csv")
# on change les features
features = ["petal.length", "petal.width"]
# on ne garde que les variétés Versicolor et Virginica
df = df[df["variety"] != "Setosa"]
# on extrait les données
data = df[features].to_numpy()
# on distingue les classes
d = {"Virginica" : 1, "Versicolor" : -1 }
classes = df["variety"].replace(d).to_numpy(dtype=int)
# le reste est la même chose que dans la question précédente
data_train, data_test, classes_train, classes_test = train_test_split(data, classes, test_size=0.2, random_state=52)
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(data_train, classes_train)
accuracy = metrics.accuracy_score(classes_test, clf.predict(data_test))
print(accuracy)
plt.scatter(data_train[:, 0], data_train[:, 1], c=classes_train)
plt.scatter(data_test[:, 0], data_test[:, 1], c=classes_test, marker="*")
# plot the decision function
ax = plt.gca()
DecisionBoundaryDisplay.from_estimator(
clf,
data,
plot_method="contour",
colors="k",
levels=[-1, 0, 1],
alpha=0.5,
linestyles=["--", "-", "--"],
ax=ax,
)
# plot support vectors
ax.scatter(
clf.support_vectors_[:, 0],
clf.support_vectors_[:, 1],
s=100,
linewidth=1,
facecolors="none",
edgecolors="k",
)
plt.show()Avec le code suivant :
df=pd.read_csv("mystery.csv")
f=["x1","x2"]
data = df[f].to_numpy()
classes = df["class"].to_numpy()
plt.scatter(data[:,0], data[:,1], c=classes)
plt.plot()On visualise :
Non, on voit bien qu’une droite ne parviendra pas à séparer l’ensemble des données.
Avec le code suivant :
df=pd.read_csv("mystery.csv")
f=["x1","x2"]
data = df[f].to_numpy()
classes = df["class"].to_numpy()
plt.scatter(data[:,0]**2, data[:,1]**2, c=classes)
plt.plot()On visualise :
qui semble désormais linéairement séparable.
On entraîne donc un classifieur sur \((x_1^2, x_2^2)\).
df=pd.read_csv("mystery.csv")
f=["x1","x2"]
data0 = df[f].to_numpy()
data = data**2
classes = df["class"].to_numpy()
# le reste est la même chose que dans l'exercice précédent
data_train, data_test, classes_train, classes_test = train_test_split(data, classes, test_size=0.2, random_state=52)
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(data_train, classes_train)
accuracy = metrics.accuracy_score(classes_test, clf.predict(data_test))
print(accuracy)
plt.scatter(data_train[:, 0], data_train[:, 1], c=classes_train)
plt.scatter(data_test[:, 0], data_test[:, 1], c=classes_test, marker="*")
# plot the decision function
ax = plt.gca()
DecisionBoundaryDisplay.from_estimator(
clf,
data,
plot_method="contour",
colors="k",
levels=[-1, 0, 1],
alpha=0.5,
linestyles=["--", "-", "--"],
ax=ax,
)
# plot support vectors
ax.scatter(
clf.support_vectors_[:, 0],
clf.support_vectors_[:, 1],
s=100,
linewidth=1,
facecolors="none",
edgecolors="k",
)
plt.show()On obtient :
Même exercice avec xor.csv mais c’est à vous de trouver la transformation qui vous permettra de séparer linéairement les données.
On visualise :
C’est une fonction xor dans le sens suivant : les points
violets sont ceux tels que \(s(x_1x_2) = 0 =
s(x_1) \oplus s(x_2)\) et les points jaunes sont tels que \(s(x_1x_2) = 1 = s(x_1) \oplus s(x_2)\) où
\(s(z) = 0\) si \(z>0\) et \(1\) sinon. On choisit donc la
transformation \((x_1,x_2) \mapsto (0,
x_1x_2)\). On va donc avoir les points sur une ligne séparés
entre les points positifs et les points négatifs.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.inspection import DecisionBoundaryDisplay
from sklearn.model_selection import train_test_split
from sklearn import metrics
df = pd.read_csv("xor.csv")
features = ["x1", "x2"]
data0 = df[features].to_numpy()
classes = df["class"]
plt.scatter(data0[:,0], data0[:, 1], c=classes)
plt.savefig("xor.svg")
plt.clf()
data = np.stack((np.zeros(len(data0)), data0[:,0]*data0[:,1]), axis=1)
data_train, data_test, classes_train, classes_test = train_test_split(data, classes, test_size=0.3, random_state=52)
clf = svm.SVC(kernel="linear", C=1000)
clf.fit(data_train, classes_train)
accuracy = metrics.accuracy_score(classes_test, clf.predict(data_test))
print(accuracy)
plt.scatter(data_train[:, 0], data_train[:, 1], c=classes_train)
plt.scatter(data_test[:, 0], data_test[:, 1], c=classes_test, marker="*")
# plot the decision function
ax = plt.gca()
DecisionBoundaryDisplay.from_estimator(
clf,
data,
plot_method="contour",
colors="k",
levels=[-1, 0, 1],
alpha=0.5,
linestyles=["--", "-", "--"],
ax=ax,
)
# plot support vectors
ax.scatter(
clf.support_vectors_[:, 0],
clf.support_vectors_[:, 1],
s=100,
linewidth=1,
facecolors="none",
edgecolors="k",
)
plt.savefig("xorsep.svg")Implémentez la régression linéaire par descente de gradient vue en cours.
En s’inspirant de la méthode vue dans le TP, on peut calculer le
gradient en \(W\) ainsi (où \(X,Y\) constitue le jeu de donnée) pour une
erreur quadratique (avec numpy, on peut écrire le produit matriciel avec
@ et la transposition avec .T):
def grad(X,Y,W):
return (W@X-Y)@XVoici un exemple pour le jeu de données iris.csv:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
def error(X,Y,W):
return np.sum((X@W-Y)**2)
def grad(X,Y,W):
return (X@W-Y)@X
df = pd.read_csv("iris.csv")
features = ["sepal.length", "sepal.width"]
data = df[features].to_numpy()
m = np.max(np.sqrt(np.sum(data*data, axis=1)))
data = data / m
data = np.stack((data[:,0], data[:,1], np.ones(len(data))), axis=1)
d = {"Setosa" : 1, "Virginica" : -1, "Versicolor" : -1 }
classes = df["variety"].replace(d).to_numpy(dtype=int)
delta = 1e-8
W = np.array([0,0,0], dtype=float)
plt.scatter(data[:,0],data[:,1],c=classes)
e = error(data, classes ,W)
g = grad(data,classes,W)
while np.dot(g,g) > 1e-3:
print(f"Gradient: {g}")
W -= delta*g
print(f"New value {W}")
e=error(data,classes,W)
g = grad(data,classes,W)
print(f"Error {e}")
a=-W[0]/W[1]
x=np.array(data[:,0])
plt.plot(x, a*x-W[2]/W[1])
plt.show()On voit ici qu’on a choisi un très petit pas de gradient (\(10^{-8}\)) ce qui peut paraître ridicule. Cependant, l’erreur ici est une somme qui contient 150 termes (un par élément du jeu de données). Le gradient a donc une valeur très importante. En fait, il faudrait normaliser l’erreur (et de ce fait le gradient) par un facteur \(m\) qui est le nombre de point dans le jeu de données. Voir ci-dessous (on peut alors choisir un pas de gradient plus petit) :
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
def error(X,Y,W):
return (1/X.shape[0])*np.sum((X@W-Y)**2)
def grad(X,Y,W):
return (2/X.shape[0])*(X@W-Y)@X
df = pd.read_csv("iris.csv")
features = ["sepal.length", "sepal.width"]
data = df[features].to_numpy()
m = np.max(np.sqrt(np.sum(data*data, axis=1)))
data = data / m
data = np.stack((data[:,0], data[:,1], np.ones(len(data))), axis=1)
d = {"Setosa" : 1, "Virginica" : -1, "Versicolor" : -1 }
classes = df["variety"].replace(d).to_numpy(dtype=int)
delta = 0.4
W = np.array([0,0,0], dtype=float)
plt.scatter(data[:,0],data[:,1],c=classes)
e = error(data, classes ,W)
g = grad(data,classes,W)
while np.dot(g,g) > 1e-3:
print(f"Gradient: {g}")
W -= delta*g
print(f"New value {W}")
e=error(data,classes,W)
g = grad(data,classes,W)
print(f"Error {e}")
a=-W[0]/W[1]
x=np.array(data[:,0])
plt.plot(x, a*x-W[2]/W[1])
plt.show()