Outils pour utilisateurs

Outils du site


nsi:tds:ia:neurones

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
nsi:tds:ia:neurones [2022/04/21 14:02] goupillwikinsi:tds:ia:neurones [2022/04/22 11:37] (Version actuelle) – [Module sklearn] goupillwiki
Ligne 9: Ligne 9:
 Prenons un **exemple**. Prenons un **exemple**.
  
-{{ :nsi:tds:ia:neuronne_1.svg |}}+{{ :nsi:tds:ia:neurone_1.svg |}}
  
 On étudie une population d'individus. Ces individus ont tous une paire d'attributs, $x$ et $y$, ce qui permet de placer chaque individu dans un nuage de points. On sait également que certains individus sont du genre //rond bleu// et d'autres du genre //carré rouge//. On étudie une population d'individus. Ces individus ont tous une paire d'attributs, $x$ et $y$, ce qui permet de placer chaque individu dans un nuage de points. On sait également que certains individus sont du genre //rond bleu// et d'autres du genre //carré rouge//.
Ligne 31: Ligne 31:
 </WRAP> </WRAP>
  
-{{ :nsi:tds:ia:neuronne_2.svg |}}+{{ :nsi:tds:ia:neurone_2.svg |}}
  
 Nous souhaitons donc réaliser un automate qui, quand on lui présente un individu inconnu, décide de la catégorie de cet individu. Ce que l'on connaît de l'individu, ce sont ses attributs $x$ et $y$ que nous transmettons à la machine et la machine prend sa décision seulement sur cette information. Nous souhaitons donc réaliser un automate qui, quand on lui présente un individu inconnu, décide de la catégorie de cet individu. Ce que l'on connaît de l'individu, ce sont ses attributs $x$ et $y$ que nous transmettons à la machine et la machine prend sa décision seulement sur cette information.
  
-{{ :nsi:tds:ia:neuronne_3.svg |}}+{{ :nsi:tds:ia:neurone_3.svg |}}
  
 On peut chercher une droite séparant les deux nuages. Cette droit a une certaine équation cartésienne  $a\cdot x + b\cdot y + c = 0$ -- on préfère cette forme qui est plus générale que $y = m\cdot x + p$ -- où $a$, $b$ et $c$ ont une certaine valeur qu'il faudra trouver. On peut chercher une droite séparant les deux nuages. Cette droit a une certaine équation cartésienne  $a\cdot x + b\cdot y + c = 0$ -- on préfère cette forme qui est plus générale que $y = m\cdot x + p$ -- où $a$, $b$ et $c$ ont une certaine valeur qu'il faudra trouver.
Ligne 51: Ligne 51:
 Ce qui donne la machine ci-dessous : Ce qui donne la machine ci-dessous :
  
-{{ :nsi:tds:ia:neuronne_4.svg |}}+{{ :nsi:tds:ia:neurone_4.svg |}}
  
 Il ne nous reste qu'à trouver les bonnes valeurs de $a$, $b$ et $c$. Il ne nous reste qu'à trouver les bonnes valeurs de $a$, $b$ et $c$.
Ligne 59: Ligne 59:
 Dans le fichier {{ :nsi:datasets:iris.csv |}} on vous donne une liste d'individus, des iris, donc on fournit les dimensions des pétales. On pourra prendre $x = $ longueur pétale ; $y =$ largeur pétale. La décision que l'on veut produire est « Est Setosa » qui est vraie pour une iris de l'espèce Setosa et fausse pour les autres. Dans le fichier {{ :nsi:datasets:iris.csv |}} on vous donne une liste d'individus, des iris, donc on fournit les dimensions des pétales. On pourra prendre $x = $ longueur pétale ; $y =$ largeur pétale. La décision que l'on veut produire est « Est Setosa » qui est vraie pour une iris de l'espèce Setosa et fausse pour les autres.
  
-  - En représentant le nuage de point correspondant, déterminer une équation $a\cdot x + b\cdot y + c = 0$ qui sépare les points des Setosa des autres. +  - Exécutez le programme ci-dessous pour afficher le nuage de points. 
-  - Compléter le programme ci-dessous en écrivant une fonction ''est_setosa(longueur:float, largeur:float) -> bool'' qui décide si une iris, dont les pétales ont une longueur et une largeur connue, est de l'espèce Setosa.+  - Déterminez une équation $a\cdot x + b\cdot y + c = 0$ qui sépare les points des Setosa des autres. 
 +  - Complétez le programme en écrivant une fonction ''est_setosa(longueur:float, largeur:float) -> bool'' qui décide si une iris, dont les pétales ont une longueur et une largeur connue, est de l'espèce Setosa
 +  - Exécutez la fonction sur l'ensemble des individus de ''data'' pour vous assurer que le résultat est bien celui attendu.
  
 <code python> <code python>
-code à faire+import csv 
 +import matplotlib.pyplot as plt 
 + 
 +extraction des données du fichier 
 +def formatage(row): 
 +    return { 
 +        'petal_length':float(row['petal_length']), 
 +        'petal_width':float(row['petal_width']), 
 +        'species':row['species'
 +    } 
 + 
 +with open('iris.csv', encoding='utf8') as file: 
 +    reader = csv.DictReader(file, delimiter=','
 +    data = [formatage(row) for row in reader] 
 + 
 +def plot_sub_cloud(species, color): 
 +    """ 
 +    Trace le nuage des point d'une certaine espèce 
 +    color indique la couleur désirée 
 +    """ 
 +    x = [item["petal_length"] for item in data if item["species"] == species] 
 +    y = [item["petal_width"] for item in data if item["species"] == species] 
 +    if len(x) > 0: 
 +        plt.scatter(x, y, c=color, label=species) 
 + 
 +plot_sub_cloud('setosa', 'b'
 +plot_sub_cloud('versicolor', 'g'
 +plot_sub_cloud('virginica', 'r'
 +plt.legend() 
 +plt.show()
 </code> </code>
  
Ligne 82: Ligne 113:
 Dans le cadre de l'apprentissage, notre fonction $f$ va poser problème. Dans le cadre de l'apprentissage, notre fonction $f$ va poser problème.
  
-{{ :nsi:tds:ia:neuronne_5.svg |}}+{{ :nsi:tds:ia:neurone_5.svg |}}
  
 Nous avons dit qu'il s'agissait de trouver une droite séparant les deux nuages. Mais plusieurs droites sont possibles. Ci-dessus, la verte et la rose conviennent. Pourtant on sent que la verte est meilleure. Elle coupe plus au centre de la zone séparant les nuages. Nous avons dit qu'il s'agissait de trouver une droite séparant les deux nuages. Mais plusieurs droites sont possibles. Ci-dessus, la verte et la rose conviennent. Pourtant on sent que la verte est meilleure. Elle coupe plus au centre de la zone séparant les nuages.
Ligne 92: Ligne 123:
   * une variation continue : par exemple 0,6 proche de la droite du côté point rouge...   * une variation continue : par exemple 0,6 proche de la droite du côté point rouge...
  
-{{ :nsi:tds:ia:neuronne_6.svg |}}+{{ :nsi:tds:ia:neurone_6.svg |}}
  
 Sur cette figure on voit une notion de flou autour de la droite. Cette façon de faire est plus réaliste et va permettre de donner une importante au fait qu'un point est éloigné ou proche de la droite. Sur cette figure on voit une notion de flou autour de la droite. Cette façon de faire est plus réaliste et va permettre de donner une importante au fait qu'un point est éloigné ou proche de la droite.
  
-{{ :nsi:tds:ia:neuronne_7.svg |}}+{{ :nsi:tds:ia:neurone_7.svg |}}
  
 <WRAP tip> <WRAP tip>
Ligne 130: Ligne 161:
 La méthode proposée est encore vague : nous devons définir comment on compare $s$ et $s_{attendu}$ et comment cette comparaison modifie les paramètres $a$, $b$ et $c$. La méthode proposée est encore vague : nous devons définir comment on compare $s$ et $s_{attendu}$ et comment cette comparaison modifie les paramètres $a$, $b$ et $c$.
  
-On commence par choisir une formule pour évaluer l'erreur entre $s$ et $s_{attendu}$.+On commence par choisir une formule pour évaluer l'erreur entre $s$ et $s_{attendu}$. On notera $\mathcal{E}$ l'erreur.
  
-On dira $\mathcal{E} = \frac{1}{2}\sum_{individus} (s-s_{attendu})^2$.+$$\mathcal{E} = \frac{1}{2}\sum_{individus} (s-s_{attendu})^2$$
  
 <WRAP tip> <WRAP tip>
Ligne 151: Ligne 182:
 On choisit de faire varier $a$ de la quantité On choisit de faire varier $a$ de la quantité
  
-$$da = -\lambda \cdot \sum_{individus}(s-s_{attendu})\cdot f'(h) \cdot x$$+$$da = -\lambda \cdot \frac{d\mathcal{E}}{da} = -\lambda \cdot \sum_{individus}(s-s_{attendu})\cdot f'(h) \cdot x$$
  
 où $\lambda$ est un paramètre qu'il faut ajuster en bricolant un peu -- part de savoir faire... -- et on peut choisir $\lambda = 0,1$ -- l'expérience montre que ça fonctionne bien avec $\lambda = 0,1$. où $\lambda$ est un paramètre qu'il faut ajuster en bricolant un peu -- part de savoir faire... -- et on peut choisir $\lambda = 0,1$ -- l'expérience montre que ça fonctionne bien avec $\lambda = 0,1$.
Ligne 164: Ligne 195:
 ==== Exercice 2 ==== ==== Exercice 2 ====
  
-Dans le fichier ''main2.py''...+Vous pouvez reprendre le chargement de données du fichier précédentIl va falloir le modifier un peu.
  
-  * Écrivez une fonction ''calc_h(x, y, a, b, c)'' qui calcule $h = a\cdot x + b\cdot y + c$ +  - Modifiez le formatage de sorte que pour chaque fleur de ''iris.csv'' on obtienne un tuple ''(x, y, sa)'' où  
-  Écrivez une fonction ''f(h)'' qui calcule $f(h)$ +    ''x'' représente la longueur du pétale 
-  Écrivez une fonction ''fp(h)'' qui calcule $f'(h)$ (je vous laisse déterminer l'expression de $f'$) +    * ''y'' représente la largeur du pétale 
-  Écrivez une fonction ''g(data, a, b, c)''\\ ''data'' représente l'ensemble des données sous la forme ''%%[(x0, y0, sa0), (x1, y1, sa1), ...]%%''\\ J'ai noté ''sa'' pour $s$ attendu, c'est à dire $s_{attendu}$.\\ Cette fonction fait le calcul les trois valeurs et les renvoie :+    * ''sa'' représente le résultat attendu. 1 pour une //Setosa// et 0 sinon. 
 +  - Écrivez une fonction ''calc_h(x, y, a, b, c)'' qui calcule $h = a\cdot x + b\cdot y + c$ 
 +  Écrivez une fonction ''f(h)'' qui calcule $f(h)$ 
 +  Écrivez une fonction ''fp(h)'' qui calcule $f'(h)$ (je vous laisse déterminer l'expression de $f'$) 
 +  Écrivez une fonction ''g(data, a, b, c)''\\ ''data'' représente l'ensemble des données sous la forme ''%%[(x0, y0, sa0), (x1, y1, sa1), ...]%%''\\ Cette fonction fait le calcul les trois valeurs et les renvoie :
     * $g_a = \sum_{individus} (s-s_{attendu})\cdot f'(h) \cdot x$     * $g_a = \sum_{individus} (s-s_{attendu})\cdot f'(h) \cdot x$
     * $g_b = \sum_{individus} (s-s_{attendu})\cdot f'(h) \cdot y$     * $g_b = \sum_{individus} (s-s_{attendu})\cdot f'(h) \cdot y$
     * $g_c = \sum_{individus} (s-s_{attendu})\cdot f'(h)$     * $g_c = \sum_{individus} (s-s_{attendu})\cdot f'(h)$
-  Écrivez une une fonction ''apprentissage(data)'' qui initialise $(a,b,c)$ aléatoirement puis réalise une centaine de fois le calcul :+  Écrivez une une fonction ''apprentissage(data)'' qui initialise $(a,b,c)$ aléatoirement puis réalise une centaine de fois le calcul :
  
 $$(a,b,c) \leftarrow (a -\lambda\cdot g_a, b - \lambda \cdot g_b, c - \lambda\cdot g_c)$$ $$(a,b,c) \leftarrow (a -\lambda\cdot g_a, b - \lambda \cdot g_b, c - \lambda\cdot g_c)$$
Ligne 179: Ligne 214:
 On pourra utiliser une constante ''LAMBDA = 0.1'' On pourra utiliser une constante ''LAMBDA = 0.1''
  
-Le fichier ''main2.py'' contient le gabarit des fonctions à compléter et il contient une partie test qui se charge de produire ''data'' à partir du fichier ''data1.csv'', puis exécute la fonction ''apprentissage'' pour trouver les bonnes valeurs de $(a,b,c)$.+Exécutez le calcul et vérifiez que le triplet $(a, b, c)$ obtenu est valide. 
 + 
 +==== Base d'apprentissage, base de test ==== 
 + 
 +//On peut dire aussi entraînement pour apprentissage.// 
 + 
 +Comme vous l'avez vu, il nous faut un fichier de données pour entraîner notre neurone. 
 + 
 +On souhaite bien-sûr que notre neurone, une fois entraîné, ne fasse pas (ou très peu) d'erreurs sur les items de cette base. 
 + 
 +Mais ça ne suffit pas : nous voulons que le neurone réponde juste pour des items qui n'étaient pas dans la base (sinon quel intérêt ?) 
 + 
 +Pour vérifier que c'est bien le cas, on constitue une base de tests contenant d'autres individusabsents de la base d'apprentissage. On connaît $s_{attendu}pour ces individus et ainsi on peut vérifier que le neurone fait bien ce qu'il devrait.
  
 ===== Réseau ===== ===== Réseau =====
Ligne 185: Ligne 232:
 La machine étudiée est très limitée. On voit qu'elle en peut rien faire dans un cas plus complexe. La machine étudiée est très limitée. On voit qu'elle en peut rien faire dans un cas plus complexe.
  
-{{ :nsi:tds:ia:neuronne_8.svg |}}+{{ :nsi:tds:ia:neurone_8.svg |}}
  
 D'ailleurs, il n'y a pas de réseau dans notre machine : elle ne contenait qu'**un neurone** ! D'ailleurs, il n'y a pas de réseau dans notre machine : elle ne contenait qu'**un neurone** !
Ligne 193: Ligne 240:
 //Ci-dessous, chaque ellipse représente une machine équivalente à celle étudiée précédemment.// //Ci-dessous, chaque ellipse représente une machine équivalente à celle étudiée précédemment.//
  
-{{ :nsi:tds:ia:neuronne_9.svg |}}+{{ :nsi:tds:ia:neurone_9.svg |}}
  
 Les mathématiques nécessaires ne vont pas être beaucoup plus complexe. Il faudra, en fonction des erreurs constatées en sortie, modifier les réglages de chaque neurone en remontant de la sortie vers les entrées. On appelle cela la **rétro-propagation du gradient**. Les mathématiques nécessaires ne vont pas être beaucoup plus complexe. Il faudra, en fonction des erreurs constatées en sortie, modifier les réglages de chaque neurone en remontant de la sortie vers les entrées. On appelle cela la **rétro-propagation du gradient**.
Ligne 227: Ligne 274:
 Ici ''rn'' est l'objet contenant notre réseau de neurones. Ici ''rn'' est l'objet contenant notre réseau de neurones.
  
-Le fichier ''perceptron.py'' vous donne un exemple d'utilisation pour notre exemple avec les iris. Vous pouvez l'ouvrir et l'exécuter.+Voici un exemple que vous pouvez tester. 
 + 
 +<code python> 
 +import csv 
 +from sklearn.linear_model import Perceptron 
 + 
 +with open('iris.csv', encoding='utf8') as file: 
 +    reader = csv.DictReader(file, delimiter=','
 +    X = [] # paires (petal_length, petal_width) 
 +    Y = [] # étiquettes : 1 pour setosa, 0 sinon 
 +    for row in reader: 
 +        l = float(row ["petal_length"]) 
 +        w = float(row ["petal_width"]) 
 +        X.append((l,w)) 
 +        if row['species'] == 'setosa': 
 +            Y.append(1) 
 +        else: 
 +            Y.append(0) 
 + 
 +# création du réseau 
 +rn = Perceptron(tol=1e-3) 
 + 
 +# apprentissage 
 +rn.fit(X, Y) 
 + 
 +# on peut voir ce que prédit notre réseau pour les fleurs du fichier 
 +# on obtient une liste de 0 et de 1 correspondant aux espèces 
 +vous pouvez voir qu'il ne se trompe pas ! 
 +print(rn.predict(X)) 
 +</code> 
 + 
 +Si on veut étendre la prédiction aux virginica et versicolor, le résultat n'est pas bon. Pour mieux faire, on ajoute la prise en compte des sépales. De plus, on n'est pas obligés d'imposer 1 et 0 en sortie. On peut directement indiquer les étiquettes désirées. 
 + 
 +<code python> 
 +import csv 
 +from sklearn.linear_model import Perceptron 
 + 
 +with open('iris.csv', encoding='utf8') as file: 
 +    reader = csv.DictReader(file, delimiter=','
 +    X = [] # paires (petal_length, petal_width) 
 +    Y = [] # étiquettes : 1 pour setosa, 0 sinon 
 +    for row in reader: 
 +        pl = float(row ["petal_length"]) 
 +        pw = float(row ["petal_width"]) 
 +        sl = float(row["sepal_length"]) 
 +        sw = float(row["sepal_width"]) 
 +        X.append((pl, pw, sl, sw)) 
 +        Y.append(row['species']) 
 + 
 +# création du réseau 
 +rn = Perceptron(tol=1e-3) 
 + 
 +# apprentissage 
 +rn.fit(X, Y) 
 + 
 +# on peut voir ce que prédit notre réseau pour les fleurs du fichier 
 +# on obtient une liste de 0 et de 1 correspondant aux espèces 
 +# vous pouvez voir qu'il ne se trompe pas ! 
 +print(rn.predict(X)) 
 + 
 +# et on peut faire un compte des succès : 
 +Yp = rn.predict(X) 
 +count = 0 
 +for i in range(len(Y)): 
 +    if Y[i] == Yp[i]: 
 +       count += 1 
 +print(count/len(Y)) 
 +</code> 
 + 
 +On obtient environ 95 % de succès ! 
 + 
 +Le travail de formatage est un peu fastidieux. On gagne beaucoup de temps en utilisant Pandas. 
 + 
 +<code python> 
 +import pandas 
 +from sklearn.linear_model import Perceptron 
 + 
 +train = pandas.read_csv("iris.csv"
 +rn = Perceptron(tol=1e-3) 
 +X = train[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']] 
 +Y = train['species'
 +rn.fit(X, Y) 
 +print(rn.predict(X)) 
 +</code> 
 + 
 +<WRAP important> 
 +Dans cet exemple, j'utilise ''predict'' sur les individus de la base d'apprentissage. C'est un peu absurde : le perceptron a appris avec ces données, ce n'est pas une prédiction mais une récitation de ce qu'il a appris ! Pour mieux faire, il faudrait proposer constituer un fichier de test avec des iris absentes de la base d'apprentissage. 
 +</WRAP> 
 + 
 +===== Exemples d'applications ===== 
 + 
 +Vous pouvez essayer d'appliquer cette méthode dans le problème du [[nsi:datasets:titanic|Titanic]].
nsi/tds/ia/neurones.1650542527.txt.gz · Dernière modification : de goupillwiki