L’objectif de ce TP est d’implémenter l’algorithme du perceptron pour résoudre un problème de classifi- cation binaire. Le corpus utilisé est constitué de votes des membres du congrès américains sur des sujets sensibles ainsi que l’appartenance politique du membre (républicain ou démocrate).
Quelques exemples de données du corpus :
republican,n,y,n,y,y,y,n,n,n,y,?,y,y,y,n,y
republican,n,y,n,y,y,y,n,n,n,n,n,y,y,y,n,?
democrat,?,y,y,?,y,y,n,n,n,n,y,n,y,y,n,n
democrat,n,y,y,n,?,y,n,n,n,n,y,n,y,n,n,y
democrat,y,y,y,n,y,y,n,n,n,n,y,?,y,y,y,y
https://archive.ics.uci.edu/ml/datasets/Congressional+Voting+Records
Au total, 435 exemples constituent ce corpus. 100 exemples seront utilisés en tant que corpus de
validation et le reste sera utilisé pour estimer les paramètres du modèle (corpus d’apprentissage). Il est
important de mélanger les données avant de créer les corpus. Pensez à utiliser une ”graine“ (seed) pour
le générateur de nombres pseudo-aléatoires afin de pouvoir reproduire les résultats.
Travail à effectuer
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
df = pd.read_csv('house-votes-84.data')
np.random.seed(987654321) # also work with panda
df = df.sample(frac=1).replace(['y', 'n', '?', 'republican', 'democrat'], [1, -1, 0, 1, -1])
df.head()
class Perceptron(object):
MAX_ITER = 200
def __init__(self, lr = 0.7):
self.learning_rate = lr
def _classify(self, x):
"""
Give a class label to the x sample
"""
return np.dot(x, self.weights)
def fit(self, data_set):
"""
Dress the learning sample
class -1 -> invert the feature
"""
self.data_set = data_set.copy() # used to calculate error through the training
# invert class -1
dataSet = data_set.copy()
dataTarget = dataSet[:, 0].copy()
for i in range(len(dataTarget)):
if (dataTarget[i] == -1):
dataSet[i] *= -1
dataSet[i][0] = -1
# init the weights
self.weights =np.random.uniform(0.0, 10.0, size=len(data_set[0]))
# self.weights = np.empty(len(data_set[0]))
# self.weights.fill(0.001)
# self.weights = np.append( np.array(1) , np.zeros(len(data_set[0])-1) )
return self._train(dataSet)
def _train(self, train_data):
"""
Perceptron algo
"""
self.index_iter = 0
i = 0
best_score = 0
best_weights = []
self.all_score = []
test_data = self.data_set.copy()
test_data[:,0] = 1
test_target = self.data_set[:, 0]
while self.index_iter < self.MAX_ITER:
if (len(train_data) == i): #linéairement séparable YaY
score = per.score(test_data, test_target) # calculate the current Score
self.all_score.append(score)
return True
if( self._classify(train_data[i]) < 0):
self.weights += train_data[i] * self.learning_rate
self.index_iter += 1
i = 0
# score
score = per.score(test_data, test_target) # calculate the current Score
self.all_score.append(score)
if(best_score < score): # store the best score and his related weights
best_score = score
best_weights = self.weights.copy()
else:
i+=1
self.weights = best_weights # if not lineaire get the best weights found
return False
def score(self, test_data, test_target):
"""
Take a corpus and calculate the score (percentage of success)
"""
res = [(self._classify( test_data[i] ) * test_target[i]) >= 0
for i in range(len(test_target)) ]
return sum(res) / len(test_data)
def plot_error_fit(self):
"""
Plot the errors through the learning
"""
plt.ylim([0,1])
plt.title("Courbe d’evolution du taux d’erreur\n en fonction de l’avancement de l’apprentissage")
plt.xlabel('iteration')
plt.ylabel('% error')
plt.plot(1-np.array(self.all_score))
separator = 335
train = df.values[:separator]
per = Perceptron()
print("Linéairement séparable?", per.fit(train))
print(per.weights)
per.plot_error_fit()
test_data = df.values[separator:].copy()
test_data[:,0] = 1
test_target = df.values[separator:][:, 0]
print("Score du corpus de test:", per.score(test_data, test_target))
Sur ce corpus d’apprentissage (335) il n'est pas possible d’atteindre une erreur nulle, en 200 iterations.
Il faut mettre un corpus de taille plus petite (seprator = 160
) pour que le perceptron ai une chance de trouver une separation lineraire en 200 iteration (updates de weights)
Pour faire évoluer le learning rate pendant l’apprentissage, on peut le faire démarrer a 1 au debut puis le faire baisser en fonction du score et du nombre d'itération