modifications des tp, exercices, ajouts d'indications en JS, d'exemples en gloutons, 3 semaines de boulot enfin voilà

This commit is contained in:
2026-01-15 18:32:27 +01:00
parent 2048ebb9b7
commit ec1ccf9460
18 changed files with 3821 additions and 172 deletions

View File

@@ -4,7 +4,7 @@ D'ordre général, on parle de distance ***euclidienne*** que l'on peut noter co
Distance AB de deux points A (x<sub>a</sub>, y<sub>a</sub>) et B (x<sub>b</sub>,y<sub>b</sub>) =
$$
AB = \sqrt{(xa - xb)^2 + (ya - yb)^2}
AB = \sqrt{(x_a - x_b)^2 + (y_a - y_b)^2}
$$
@@ -18,7 +18,7 @@ il s'agit également d'une autre mesure de distance populaire, qui mesure la val
$$
Distance (AB) = |xa - xb| + |ya - yb|
Distance(A, B) = |x_a - x_b| + |y_a - y_b|
$$

View File

@@ -1,9 +1,9 @@
### Exercices
1. **Écrire une fonction `calculer_distance`** qui prend en entrée les caractéristiques de deux Pokémon et retourne la distance euclidienne entre eux.
1. **Écrire une fonction `calculer_distance`** qui prend en entrée les caractéristiques de deux Pokémon et retourne la distance euclidienne entre eux.
2. **Tester la fonction** avec des exemples simples pour vérifier son bon fonctionnement
Conseil : n'oubliez pas d'importer la bibliothèque `pandas`qui permet de travailler avec des fichiers csv (le cours se trouve [ici](../données_en_table))
Conseil : n'oubliez pas d'importer la bibliothèque `pandas` qui permet de travailler avec des fichiers csv (le cours se trouve [ici](../données_en_table))
Le fichier [pokemons.csv](pokemons.csv) qui va avec.
@@ -13,7 +13,7 @@ Ne pas oublier d'installer la bibliothèque pandas
pip install --upgrade --proxy=172.16.0.253:3128 pandas
```
Pour charger les données :
```python
import pandas as pd
@@ -21,6 +21,11 @@ import pandas as pd
pokemons = pd.read_csv('chemin/vers/pokemons.csv')
```
---
<details>
<summary><strong>Voir la correction</strong></summary>
```python
import numpy as np
@@ -35,6 +40,8 @@ pokemon2 = [85, 80] # Exemple de stats pour le Pokémon 2
distance = calculer_distance(pokemon1, pokemon2)
print(f"La distance entre les deux Pokémon est : {distance}")
```
**Résultat attendu :** `La distance entre les deux Pokémon est : 31.400636936215164`
</details>

View File

@@ -48,7 +48,7 @@ import matplotlib.pyplot as plt
# Supposons que ces listes contiennent les statistiques d'attaque et de défense de quelques Pokémon
attaques = [55, 75, 150, 45]
defenses = [45, 60, 50, 65]
noms = ['Pikachu', 'Bulbasaur', 'Charizard', 'Squirtle']
noms = ['Pikachu', 'Bulbizarre', 'Salameche', 'Carapuce']
plt.figure(figsize=(10, 5)) # Définit la taille de la figure
plt.scatter(attaques, defenses, color='red') # Crée un nuage de points avec les statistiques d'attaque et de défense
@@ -70,6 +70,130 @@ Ce code génère un graphique en nuage de points où chaque point représente un
--------
### Implémentation de l'algorithme KNN
Maintenant que nous savons visualiser nos données, passons à l'implémentation de l'algorithme.
#### Étape 1 : Calculer la distance entre deux points
```python
import math
def calculer_distance(point1, point2):
"""
Calcule la distance euclidienne entre deux points.
point1 et point2 sont des listes de coordonnées [x, y]
"""
somme = 0
for i in range(len(point1)):
somme += (point1[i] - point2[i]) ** 2
return math.sqrt(somme)
# Test
print(calculer_distance([0, 0], [3, 4])) # Affiche 5.0
```
#### Étape 2 : Trouver les k plus proches voisins
```python
def trouver_voisins(echantillons, mystere, k):
"""
Trouve les k plus proches voisins de mystere dans echantillons.
Paramètres:
echantillons : liste de dictionnaires {'coords': [x, y], 'classe': 'Type'}
mystere : liste [x, y] du point à classifier
k : nombre de voisins à retourner
Retourne:
Liste des k plus proches voisins
"""
# Calculer la distance pour chaque échantillon
distances = []
for echantillon in echantillons:
dist = calculer_distance(echantillon['coords'], mystere)
distances.append({'echantillon': echantillon, 'distance': dist})
# Trier par distance croissante
distances_triees = sorted(distances, key=lambda x: x['distance'])
# Retourner les k premiers
return [d['echantillon'] for d in distances_triees[:k]]
```
#### Étape 3 : Déterminer la classe majoritaire
```python
def classe_majoritaire(voisins):
"""
Détermine la classe la plus représentée parmi les voisins.
Paramètres:
voisins : liste de dictionnaires avec une clé 'classe'
Retourne:
La classe majoritaire (str)
"""
compteur = {}
for voisin in voisins:
classe = voisin['classe']
if classe in compteur:
compteur[classe] += 1
else:
compteur[classe] = 1
# Trouver la classe avec le plus grand nombre
classe_max = None
max_count = 0
for classe, count in compteur.items():
if count > max_count:
max_count = count
classe_max = classe
return classe_max
```
#### Étape 4 : L'algorithme KNN complet
```python
def knn(echantillons, mystere, k):
"""
Algorithme des k plus proches voisins.
Paramètres:
echantillons : liste de dictionnaires {'coords': [x, y], 'classe': 'Type'}
mystere : liste [x, y] du point à classifier
k : nombre de voisins à considérer
Retourne:
La classe prédite pour mystere
"""
voisins = trouver_voisins(echantillons, mystere, k)
return classe_majoritaire(voisins)
```
#### Exemple complet
```python
# Échantillon de Pokémon
pokemons = [
{'coords': [49, 49], 'classe': 'Eau'}, # Ecayon
{'coords': [50, 95], 'classe': 'Psy'}, # Deoxys
{'coords': [80, 45], 'classe': 'Psy'}, # Eoko
{'coords': [90, 75], 'classe': 'Psy'}, # Groret
{'coords': [90, 75], 'classe': 'Eau'}, # Tarpaud
]
# Pokémon mystère à classifier
pokemon_mystere = [65, 40]
# Prédiction avec k=3
prediction = knn(pokemons, pokemon_mystere, k=3)
print(f"Le Pokémon mystère est probablement de type : {prediction}")
```
--------
Auteur : Florian Mathieu
Licence CC BY NC

View File

@@ -23,7 +23,7 @@ Parmi les modèles et méthodes de classification, on peut citer :
- K-NN Voisins
- Arbres de décision
- Réseaux de neuronnes
- Réseaux de neurones
----------
@@ -33,7 +33,7 @@ L'algorithme des *k plus proches voisins* permet de résoudre certains problème
Il s'agit d'une méthode de raisonnement à partir de cas connus : ce modèle va aider à prendre des décisions en recherchant un ou des cas similaires déjà résolus.
Attention, ici, il n'y a pas détape d apprentissage: uniquement une construction d un ***modèle*** à partir dun échantillon d'apprentissage.
Attention, KNN est un algorithme dit ***paresseux*** (*lazy learning*) : contrairement à d'autres algorithmes de classification, il n'y a pas de phase d'entraînement où un modèle est construit. L'échantillon d'apprentissage est simplement mémorisé, et tout le travail de calcul se fait au moment de la prédiction.
### Qu'est ce qu'un modèle ?
@@ -58,7 +58,7 @@ Le Professeur Chen, inventeur du Pokédex, utilise cet algorithme afin que son a
Pour simplifier, imaginons que les Pokemons ne possèdent que deux caractéristiques : leurs points de vie et leur valeur d'attaque. On peut prendre deux types pour commencer.
| Nom | Écayon | Deoxys | Éoko | Groret | Taraud |
| Nom | Écayon | Deoxys | Éoko | Groret | Tarpaud |
| ------------- | ------ | ------ | ---- | ------ | ------ |
| Points de vie | 49 | 50 | 80 | 90 | 90 |
| Attaque | 49 | 95 | 45 | 75 | 75 |
@@ -78,7 +78,7 @@ Pour simplifier, imaginons que les Pokemons ne possèdent que deux caractéristi
![echantillon_2](assets/echantillon_pokemon_2.png)
Il devrait donc se trouver dans zone. On peut alors trouver ses cinq ou six plus proches voisins.
Il devrait donc se trouver dans cette zone. On peut alors trouver ses cinq ou six plus proches voisins.
- Parmi ces voisins se trouvent deux Pokémons de type Eau, et trois de types Psy.
- Le Pokémon mystère sera donc probablement de type Psy !
@@ -95,6 +95,8 @@ D'après vous, que faut-il pour pouvoir prédire la classe d'un Pokemon donné ?
- Un pokemon *mystère* dont on souhaite prédire la classification
- la valeur de *k*
> **Comment choisir k ?** On choisit généralement k impair (pour éviter les égalités lors du vote majoritaire) et une valeur raisonnable comme √n (racine carrée du nombre d'échantillons) ou entre 5 et 7 pour de petits échantillons.
Une fois ces données modélisées, on peut formuler notre algorithme:
- Trouver dans l'échantillon, les *k* plus proches voisins du pokemon *mystère*

View File

@@ -1,7 +1,5 @@
## Choixpeau Magique et KNN
##
<img src="assets/choixpeau.jpeg" alt="ChoixpeauMagique" style="zoom: 25%;" />
@@ -87,7 +85,7 @@ def charger_table(nom_fichier):
"courage": int(eleve[1]),
"loyauté": int(eleve[2]),
"sagesse":int(eleve[3]),
"malicez":int(eleve[4]),
"malice":int(eleve[4]),
"maison" : eleve[5]})
return table

View File

@@ -1,38 +1,128 @@
# Imports regroupés en haut du fichier (PEP 8)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Chargement des données
pokemons = pd.read_csv('pokemons.csv')
# Afficher les premières lignes pour vérification
print("=== Aperçu des données ===")
print(pokemons.head())
import matplotlib.pyplot as plt
# Filtrons les données pour n'avoir que les Pokémon de type Eau
eau_pokemons = pokemons.dropna(subset=['Pokemons de type Eau'])
# Création du graphe
plt.scatter(eau_pokemons['points de vie'], eau_pokemons['Pokemons de type Eau'])
plt.title('Points de vie vs Attribut Eau des Pokémon')
plt.xlabel('Points de vie')
plt.ylabel('Attribut Eau')
plt.show()
# Restructuration des données pour avoir une colonne "type" explicite
def restructurer_donnees(df):
"""
Transforme le DataFrame pour avoir des colonnes : nom, points_de_vie, attaque, type
"""
donnees = []
for _, row in df.iterrows():
if pd.notna(row['Pokemons de type Eau']):
donnees.append({
'nom': row['nom'],
'points_de_vie': row['points de vie'],
'attaque': row['Pokemons de type Eau'],
'type': 'Eau'
})
elif pd.notna(row['Pokemons de type Psy']):
donnees.append({
'nom': row['nom'],
'points_de_vie': row['points de vie'],
'attaque': row['Pokemons de type Psy'],
'type': 'Psy'
})
return pd.DataFrame(donnees)
import numpy as np
pokemons_clean = restructurer_donnees(pokemons)
print("\n=== Données restructurées ===")
print(pokemons_clean.head(10))
# Ajoutons un Pokémon "mystère"
pokemon_mystere = [70, 65]
# Visualisation des données
def afficher_graphique(df, pokemon_mystere=None):
"""
Affiche un nuage de points des Pokémon par type
"""
plt.figure(figsize=(10, 6))
# Pokémon de type Eau
eau = df[df['type'] == 'Eau']
plt.scatter(eau['points_de_vie'], eau['attaque'], color='blue', label='Type Eau', s=100)
# Pokémon de type Psy
psy = df[df['type'] == 'Psy']
plt.scatter(psy['points_de_vie'], psy['attaque'], color='purple', label='Type Psy', s=100)
# Pokémon mystère si fourni
if pokemon_mystere:
plt.scatter(pokemon_mystere[0], pokemon_mystere[1], color='red', marker='X', s=200, label='Mystère')
plt.title('Classification des Pokémon par type')
plt.xlabel('Points de vie')
plt.ylabel('Attaque')
plt.legend()
plt.grid(True)
plt.show()
# Fonction de calcul de distance euclidienne
def calculer_distance(pokemon1, pokemon2):
"""
Calcule la distance euclidienne entre deux Pokémon
pokemon1 et pokemon2 sont des listes [points_de_vie, attaque]
"""
return np.sqrt(np.sum(np.square(np.array(pokemon1) - np.array(pokemon2))))
# Calculons la distance de chaque Pokémon de type Eau au Pokémon "mystère"
distances = eau_pokemons.apply(lambda row: calculer_distance([row['points de vie'], row['Pokemons de type Eau']], pokemon_mystere), axis=1)
# Ajoutons ces distances au DataFrame
eau_pokemons['Distance au mystère'] = distances
# Algorithme KNN complet
def knn(df, pokemon_mystere, k=5):
"""
Algorithme des k plus proches voisins
# Affichons les Pokémon triés par leur distance au mystère
print(eau_pokemons.sort_values(by='Distance au mystère').head())
Paramètres:
df : DataFrame contenant les Pokémon avec colonnes points_de_vie, attaque, type
pokemon_mystere : liste [points_de_vie, attaque] du Pokémon à classifier
k : nombre de voisins à considérer
Retourne:
Le type prédit pour le Pokémon mystère
"""
# Calcul des distances pour chaque Pokémon
df_copy = df.copy()
df_copy['distance'] = df_copy.apply(
lambda row: calculer_distance([row['points_de_vie'], row['attaque']], pokemon_mystere),
axis=1
)
# Tri par distance croissante et sélection des k premiers
k_voisins = df_copy.sort_values(by='distance').head(k)
print(f"\n=== Les {k} plus proches voisins ===")
print(k_voisins[['nom', 'type', 'distance']])
# Vote majoritaire
type_majoritaire = k_voisins['type'].value_counts().idxmax()
votes = k_voisins['type'].value_counts()
print(f"\n=== Votes ===")
print(votes)
return type_majoritaire
# Exemple d'utilisation
if __name__ == "__main__":
# Définir un Pokémon mystère (points_de_vie, attaque)
pokemon_mystere = [65, 40]
# Afficher le graphique avec le Pokémon mystère
afficher_graphique(pokemons_clean, pokemon_mystere)
# Appliquer l'algorithme KNN avec k=5
type_predit = knn(pokemons_clean, pokemon_mystere, k=5)
print(f"\n=== Résultat ===")
print(f"Le Pokémon mystère ({pokemon_mystere[0]} PV, {pokemon_mystere[1]} attaque) est probablement de type : {type_predit}")

View File

@@ -0,0 +1,38 @@
import pandas as pd
# Chargement des données
pokemons = pd.read_csv('pokemons.csv')
# Afficher les premières lignes pour vérification
print(pokemons.head())
import matplotlib.pyplot as plt
# Filtrons les données pour n'avoir que les Pokémon de type Eau
eau_pokemons = pokemons.dropna(subset=['Pokemons de type Eau'])
# Création du graphe
plt.scatter(eau_pokemons['points de vie'], eau_pokemons['Pokemons de type Eau'])
plt.title('Points de vie vs Attribut Eau des Pokémon')
plt.xlabel('Points de vie')
plt.ylabel('Attribut Eau')
plt.show()
import numpy as np
# Ajoutons un Pokémon "mystère"
pokemon_mystere = [70, 65]
def calculer_distance(pokemon1, pokemon2):
return np.sqrt(np.sum(np.square(np.array(pokemon1) - np.array(pokemon2))))
# Calculons la distance de chaque Pokémon de type Eau au Pokémon "mystère"
distances = eau_pokemons.apply(lambda row: calculer_distance([row['points de vie'], row['Pokemons de type Eau']], pokemon_mystere), axis=1)
# Ajoutons ces distances au DataFrame
eau_pokemons['Distance au mystère'] = distances
# Affichons les Pokémon triés par leur distance au mystère
print(eau_pokemons.sort_values(by='Distance au mystère').head())