Séance 3 — L’IA qui cherche

Formuler un problème, explorer un espace d’états

Dr. El Hadji Bassirou TOURÉ

Département de Mathématiques et Informatique, Faculté des Sciences et Techniques, Université Cheikh Anta Diop de Dakar

Objectifs de la séance

Ce que la séance construit

Résoudre un problème en cherchant : formuler le problème comme un espace d’états, l’explorer avec une recette commune, puis comparer cinq algorithmes sur le trajet Dakar \(\to\) Touba ; concevoir une heuristique ; et, quand seul l’état final compte, la recherche locale.

  • Formuler un problème de recherche : états, actions, transitions, coûts, but.
  • Dérouler à la main BFS, DFS, UCS, la recherche gloutonne et A*, avec la frontière à chaque étape.
  • Juger un algorithme : complétude, optimalité, temps, espace.
  • Vérifier qu’une heuristique est admissible et consistante, et dire ce que cela garantit.
  • Concevoir une heuristique par relaxation (taquin).
  • Résoudre les 8 reines par hill climbing et par recuit simulé.

Résoudre, c’est chercher

Le trajet de la séance

Fatou et sa famille partent de Dakar pour le Magal de Touba. Aucune route ne relie directement les deux villes : il faut choisir une suite de routes. Un GPS ne « voit » pas le meilleur itinéraire : il dispose d’une carte et l’explore méthodiquement, avant de partir.

L’idée

Quand le monde est observable, déterministe et connu, un agent fondé sur des buts peut chercher une solution en simulant les actions sur un modèle du monde, puis exécuter la suite d’actions trouvée. Le même schéma sert aux itinéraires, aux jeux, à la planification et à la démonstration de théorèmes.

Lecture. Toute la séance compare des stratégies d’exploration sur ce même réseau ; elles diffèrent par le chemin trouvé, et par ce qu’elles coûtent en temps et en mémoire.

Les données de la séance

Réseau simplifié : coordonnées GPS, distances routières arrondies.

route km
Dakar – Rufisque 26
Rufisque – Thiès 45
Dakar – Mbour 83
Mbour – Thiès 64
Thiès – Tivaouane 24
Tivaouane – Kébémer 70
Kébémer – Louga 40
Louga – Saint-Louis 73
Thiès – Diourbel 80
Diourbel – Touba 48
Touba – Louga 100
Mbour – Diourbel 120

10 villes, 12 routes à double sens.

Le problème de recherche, formellement

Les composants d’un problème de recherche

\[ \mathcal P=\big(\mathcal S,\ s_0,\ \mathrm{Actions},\ \mathrm{Résultat},\ c,\ \mathrm{EstBut}\big) \]

\(\mathcal S\) : les états ; \(s_0\) : l’état initial ; \(\mathrm{Actions}(s)\) : les actions applicables en \(s\) ; \(\mathrm{Résultat}(s,a)\) : le modèle de transition ; \(c(s,a,s')>0\) : le coût d’action ; \(\mathrm{EstBut}(s)\) : vrai si \(s\) est un état but.

Sur le réseau

\(\mathcal S\) : les 10 villes ; \(s_0=\) Dakar ; \(\mathrm{Actions}(\text{Thiès})\) : les 4 routes qui partent de Thiès ; \(\mathrm{Résultat}(\text{Thiès},\text{vers Diourbel})=\) Diourbel ; \(c\) : la longueur de la route en km ; \(\mathrm{EstBut}(s)\) : \(s=\) Touba.

Lecture. États, actions et transitions forment l’espace d’états : un graphe dont les sommets sont les villes. Formuler, c’est abstraire : l’état « à Thiès » oublie le carburant, la météo, le péage.

Solution et solution optimale

Définitions

Une solution est une suite d’actions de \(s_0\) à un but ; son coût, le coût de chemin, est la somme des coûts d’action. Une solution optimale a le plus petit coût, noté \(C^*\).

Coûts positifs

Tous les algorithmes de la séance supposent \(c>0\) : avec un coût négatif, un cycle pourrait faire baisser le coût indéfiniment.

chemin sans cycle routes km
Dakar, Rufisque, Thiès, Diourbel, Touba 4 199
Dakar, Mbour, Diourbel, Touba 3 251
Dakar, Mbour, Thiès, Diourbel, Touba 4 275
Dakar, Rufisque, Thiès, Mbour, Diourbel, Touba 5 303
Dakar, …, Tivaouane, Kébémer, Louga, Touba 6 305
Dakar, Mbour, Thiès, …, Louga, Touba 6 381
Dakar, Mbour, Diourbel, Thiès, …, Touba 7 517

Lecture. \(C^*=199\) km, en 4 routes ; le chemin par Mbour n’a que 3 routes mais fait 251 km. Le moins de routes n’est pas le moins de km : cette tension sépare les algorithmes qui suivent.

Espace d’états et arbre de recherche

Les deux premiers niveaux de l’arbre de recherche issu de Dakar ; en orange, les états déjà rencontrés.

Lecture. L’arbre déroule des chemins : Dakar y revient par un aller-retour (un cycle), Thiès par deux chemins (un chemin redondant). Avec les cycles, l’arbre est infini, alors que l’espace d’états a 10 sommets.

Le réseau en Python

ROUTES = [("Dakar", "Rufisque", 26), ("Rufisque", "Thies", 45),
          ("Dakar", "Mbour", 83), ("Mbour", "Thies", 64),
          ("Thies", "Tivaouane", 24), ("Tivaouane", "Kebemer", 70),
          ("Kebemer", "Louga", 40), ("Louga", "Saint-Louis", 73),
          ("Thies", "Diourbel", 80), ("Diourbel", "Touba", 48),
          ("Touba", "Louga", 100), ("Mbour", "Diourbel", 120)]
G = {}                                   # G[ville][voisine] = km
for a, b, km in ROUTES:                  # une route se parcourt dans les deux sens
    G.setdefault(a, {})[b] = km
    G.setdefault(b, {})[a] = km
def voisins(e):                          # successeurs dans l'ordre alphabetique
    return sorted(G[e].items())
print(len(G), sum(len(v) for v in G.values()) // 2)
print(voisins("Thies"))

Résultat \(\rightarrow\)

10 12
[('Diourbel', 80), ('Mbour', 64), ('Rufisque', 45), ('Tivaouane', 24)]

Lecture. Un dictionnaire de dictionnaires : 10 villes, 12 routes (chacune rangée une fois par sens). Les successeurs sortent dans l’ordre alphabétique : c’est la convention de tous les algorithmes de la séance.

Exercice : formuler le problème de Cheikh

Énoncé

Cheikh, coursier à vélo, va de la Poste du Plateau (P) à Ouakam (O) ; temps en minutes.

  1. L’état initial et le test du but.
  2. Les actions en M et le modèle de transition.
  3. Le coût d’action.
  4. Les chemins sans cycle de P à O et leurs coûts.
  5. Le chemin optimal ; le plus court en étapes.

Correction

  1. \(s_0=\) P ; \(\mathrm{EstBut}(s)\) : \(s=\) O.
  2. En M : vers C, F ou P ; \(\mathrm{Résultat}(\text{M},\text{vers C})=\) C.
  3. Le temps de la route : \(c(\text{M},\text{vers C})=3\).
  4. 7 chemins : PMCFO 11, PLCFO 14, PLO 15, PMFO 15, PMCLO 16, PLCMFO 24, PMFCLO 26.
  5. Optimal : PMCFO, 11 min, 4 étapes ; le moins d’étapes : PLO (2), 15 min.

La frontière et la recette générique

La frontière

La frontière est l’ensemble des nœuds générés mais pas encore développés. Développer un nœud, c’est générer ses successeurs ; on n’ajoute à la frontière que ceux dont l’état n’est pas déjà atteint. Comme un éclaireur dans le brouillard : il voit la lisière de ce qu’il a découvert, et choisit où avancer.

Recette générique

  1. Frontière \(\leftarrow\) {racine \(s_0\)} ; atteint \(\leftarrow\{s_0\}\).
  2. Tant que la frontière n’est pas vide : en retirer un nœud \(n\) selon la stratégie.
  3. Si \(n\) est un but : rendre \(n\) (le chemin se lit par les parents).
  4. Sinon, développer \(n\) : chaque successeur non atteint (ou atteint par un chemin plus cher) entre dans la frontière.

Lecture. Tous les algorithmes de la séance suivent cette recette ; seule la règle de retrait change : file, pile, ou file de priorité.

Le nœud et la reconstruction du chemin

class Noeud:
    def __init__(self, etat, parent=None, g=0):
        self.etat, self.parent, self.g = etat, parent, g
    def __lt__(self, autre):             # a priorite egale : ordre alphabetique
        return self.etat < autre.etat
def chemin(n):                           # remonter les parents jusqu'a la racine
    etats = []
    while n is not None:
        etats.append(n.etat); n = n.parent
    return etats[::-1]
a = Noeud("Dakar"); b = Noeud("Rufisque", a, 26); c = Noeud("Thies", b, b.g + 45)
print(chemin(c), c.g)

Résultat \(\rightarrow\)

['Dakar', 'Rufisque', 'Thies'] 71

Lecture. Un nœud porte un état, son parent et le coût \(g\) depuis la racine : Thiès, parent Rufisque, \(g=26+45=71\). Conventions : à priorité égale, ordre alphabétique ; but testé à la sortie de la frontière ; les « nœuds développés » comptés ensuite sont les nœuds sortis de la frontière, le but compris.

Les paramètres \(b\), \(d\), \(m\) et l’explosion combinatoire

Paramètres

\(b\) : le facteur de branchement, nombre maximal de successeurs ; \(d\) : nombre d’actions de la solution la moins profonde ; \(m\) : longueur maximale d’un chemin.

Sur le réseau

\(b=4\) (Thiès) ; \(d=3\) (par Mbour) ; \(m=8\) routes sans cycle depuis Dakar, \(\infty\) avec les cycles.

Nœuds au niveau \(d\) d’un arbre de facteur \(b\) (échelle log).

Lecture. Chaque niveau multiplie par \(b\) : avec \(b=10\), un million de nœuds au niveau 6, mille milliards au niveau 12. C’est le défi central de la recherche.

Les quatre critères

Complétude, optimalité

Un algorithme est complet s’il trouve une solution quand il en existe une ; il est optimal s’il trouve une solution de coût \(C^*\).

Temps, espace

Le temps compte les nœuds générés ou développés ; l’espace compte les nœuds gardés en mémoire en même temps (frontière et atteints). Les deux s’écrivent avec \(b\), \(d\), \(m\) et, pour les coûts, \(C^*\) et le plus petit coût d’action \(\varepsilon\).

Temps et espace ne vont pas ensemble

Un algorithme peut visiter beaucoup de nœuds en n’en gardant que peu : c’est toute la différence entre BFS et DFS.

BFS : la file FIFO

L’idée

La recherche en largeur {(breadth-first search, BFS)} range la frontière dans une file FIFO : le premier nœud entré est le premier sorti, comme au guichet de la gare routière. Elle sort donc tous les nœuds à 1 route de la racine, puis tous ceux à 2 routes, et ainsi de suite.

Ce qu’elle garantit

Le premier but sorti est à la plus petite profondeur : BFS minimise le nombre d’actions. Elle n’est optimale en coût que si toutes les actions coûtent autant. Elle est complète si \(b\) est fini.

Lecture. Ici, BFS cherchera le chemin le moins long en routes, sans regarder les km. Le pas à pas qui suit montre le code, la file et la carte à chaque tour de boucle.

BFS, pas à pas

frontière : Dakar

frontière : Mbour Rufisque

frontière : Rufisque Diourbel Thiès

frontière : Diourbel Thiès

frontière : Thiès Touba

frontière : Touba Tivaouane

frontière : Tivaouane

frontière : Tivaouane

Sortie \(\rightarrow\) 

['Dakar', 'Mbour', 'Diourbel', 'Touba'] 251

Lecture. Initialisation (lignes 2–3) : la file contient la racine Dakar ; Dakar est marqué atteint.

Lecture. Tour 1 : Dakar sort (le plus ancien) ; Mbour et Rufisque entrent en bout de file, parent Dakar.

Lecture. Tour 2 : Mbour sort (le plus ancien) ; Diourbel et Thiès entrent en bout de file, parent Mbour.

Lecture. Tour 3 : Rufisque sort ; ses voisines sont déjà atteintes, la file ne reçoit rien.

Lecture. Tour 4 : Diourbel sort (le plus ancien) ; Touba entre en bout de file, parent Diourbel.

Lecture. Tour 5 : Thiès sort (le plus ancien) ; Tivaouane entre en bout de file, parent Thiès.

Lecture. Tour 6 : Touba sort de la file (ligne 5) ; c’est le but, la fonction rend ce nœud (ligne 7).

Lecture. Ligne 14 : le chemin se lit par les parents ; \(g=251\) km, en 3 routes.

La complexité de BFS

Rappel : la somme géométrique

\[ 1+b+\dots+b^d=\frac{b^{d+1}-1}{b-1}\leq2\,b^d (b\geq2) \]

Le dernier niveau domine : \(b=10\), \(d=5\) : \(111\,111\) nœuds, dont \(100\,000\) au niveau 5.

Temps et espace \(O(b^d)\) si le but est testé à la génération, \(O(b^{d+1})\) s’il l’est à la sortie (convention de la séance) : tout nœud généré reste en mémoire.

\(d\) nœuds temps mémoire
4 \(11\,111\) \(11{,}1\) ms \(11{,}1\) Mo
6 \(1{,}11\times10^6\) \(1{,}11\) s \(1{,}11\) Go
8 \(1{,}11\times10^8\) \(1{,}85\) min \(111\) Go
10 \(1{,}11\times10^{10}\) \(3{,}09\) h \(11{,}1\) To
12 \(1{,}11\times10^{12}\) \(12{,}9\) jours \(1{,}11\) Po
14 \(1{,}11\times10^{14}\) \(3{,}52\) ans \(111\) Po

\(b=10\), un million de nœuds par seconde, 1 ko par nœud.

Lecture. À \(d=8\), deux minutes de calcul mais 111 Go de mémoire : la mémoire sature bien avant la patience.

Exercice : BFS sur la carte de Cheikh

Énoncé

BFS de P à O ; successeurs alphabétiques, but testé à la sortie.

  1. Étapes 1 et 2 : le nœud qui sort, la file.
  2. Les étapes suivantes, jusqu’au but.
  3. Le chemin, son coût, comparés à l’optimum.

Correction

  1. Étape 1 : P sort ; file : L, M.
    Étape 2 : L sort ; file : M, C, O.
  2. Étape 3 : M sort ; file : C, O, F.
    Étape 4 : C sort ; file : O, F.
    Étape 5 : O sort : c’est le but.
  3. P–L–O, 15 min en 2 étapes : le moins d’étapes, pas le moins de minutes (optimum : 11 min).

DFS : la pile LIFO

L’idée

La recherche en profondeur {(depth-first search, DFS)} range la frontière dans une pile LIFO : le dernier nœud empilé sort le premier, comme l’assiette du dessus. Elle descend le plus loin possible, et ne revient en arrière qu’au bout d’une impasse.

Convention

On écarte seulement les états déjà présents sur le chemin courant (recherche arborescente). Les successeurs sont empilés en ordre inverse : le premier dans l’ordre alphabétique se retrouve au sommet et sort d’abord.

Lecture. DFS ne regarde ni les coûts ni la profondeur : son résultat dépend entièrement de l’ordre des successeurs.

DFS, pas à pas

pile (sommet à droite) : Dakar

pile (sommet à droite) : Rufisque Mbour

pile (sommet à droite) : Rufisque Thiès Diourbel

pile (sommet à droite) : Rufisque Thiès Touba Thiès

pile (sommet à droite) : Rufisque Thiès Touba Tivaouane Rufisque

pile (sommet à droite) : Rufisque Thiès Touba Tivaouane

pile (sommet à droite) : Rufisque Thiès Touba Kébémer

pile (sommet à droite) : Rufisque Thiès Touba Louga

pile (sommet à droite) : Rufisque Thiès Touba Touba Saint-Louis

pile (sommet à droite) : Rufisque Thiès Touba Touba

pile (sommet à droite) : Rufisque Thiès Touba

pile (sommet à droite) : Rufisque Thiès Touba

Sortie \(\rightarrow\) 

['Dakar', 'Mbour', 'Diourbel', 'Thies', 'Tivaouane', 'Kebemer', 'Louga', 'Touba'] 517

Lecture. Initialisation (ligne 8) : la pile contient la racine Dakar.

Lecture. Tour 1 : Dakar sort (le plus récent) ; Rufisque et Mbour sont empilés, le premier dans l’ordre alphabétique au sommet.

Lecture. Tour 2 : Mbour sort (le plus récent) ; Thiès et Diourbel sont empilés, le premier dans l’ordre alphabétique au sommet.

Lecture. Tour 3 : Diourbel sort (le plus récent) ; Touba et Thiès sont empilés, le premier dans l’ordre alphabétique au sommet.

Lecture. Tour 4 : Thiès sort (le plus récent) ; Tivaouane et Rufisque sont empilés, le premier dans l’ordre alphabétique au sommet.

Lecture. Tour 5 : Rufisque sort ; toutes ses voisines sont sur le chemin courant : impasse, rien n’est empilé.

Lecture. Tour 6 : Tivaouane sort (le plus récent) ; Kébémer est empilé.

Lecture. Tour 7 : Kébémer sort (le plus récent) ; Louga est empilé.

Lecture. Tour 8 : Louga sort (le plus récent) ; Touba et Saint-Louis sont empilés, le premier dans l’ordre alphabétique au sommet.

Lecture. Tour 9 : Saint-Louis sort ; toutes ses voisines sont sur le chemin courant : impasse, rien n’est empilé.

Lecture. Tour 10 : Touba sort du sommet de la pile ; c’est le but (ligne 12).

Lecture. Ligne 18 : 517 km, en 7 routes : DFS rend le premier chemin trouvé, pas le meilleur.

La mémoire de DFS et l’approfondissement itératif

Mémoire \(O(bm)\), en recherche arborescente seulement

La pile contient le chemin courant (au plus \(m\) nœuds) et, pour chacun, ses frères en attente (au plus \(b-1\)) : \(b=10\), \(m=16\) donnent 161 nœuds, contre \(10^{16}\) pour BFS. Avec un ensemble global des états visités, la mémoire redevient celle de l’espace d’états. DFS n’est pas optimale (517 km ici, 305 km avec l’ordre inverse), ni complète sur un espace infini.

{Approfondissement itératif {(iterative deepening, IDS)}}

Un DFS limité à la profondeur \(\ell\), relancé pour \(\ell=0,1,2,\dots\) : complet, optimal en nombre d’actions, temps \(O(b^d)\), mémoire \(O(bd)\). Sur Dakar \(\to\) Touba : 1, 3, 6 puis 5 nœuds, soit 15 en tout, et le chemin de BFS (251 km).

Lecture. Refaire les niveaux coûte peu : avec \(b=10\), \(d=5\), IDS développe \(123\,456\) nœuds contre \(111\,111\) pour BFS, 11 % de plus.

UCS : le coût le plus bas d’abord

L’idée

La recherche à coût uniforme {(uniform-cost search, UCS)} range la frontière dans une file de priorité ordonnée par \(g(n)\), le coût depuis la racine : elle développe toujours le nœud le moins cher. Une nouvelle qui partirait de Dakar par la route atteindrait les villes dans cet ordre.

Deux différences avec BFS

Un état déjà atteint est réinséré si un chemin strictement moins cher y mène (mise à jour) ; l’ancienne entrée du tas est ignorée à sa sortie (la vérification n.g > meilleur). Le but est testé à la sortie, jamais à la génération.

Lecture. La fonction meilleur_dabord du pas à pas servira aussi à la recherche gloutonne et à A* : seule la priorité \(f\) change.

UCS, pas à pas

tas : Dakar \(g=0\)

tas : Rufisque \(g=26\) Mbour \(g=83\)

tas : Thiès \(g=71\) Mbour \(g=83\)

tas : Mbour \(g=83\) Tivaouane \(g=95\) Diourbel \(g=151\)

tas : Tivaouane \(g=95\) Diourbel \(g=151\)

tas : Diourbel \(g=151\) Kébémer \(g=165\)

tas : Kébémer \(g=165\) Touba \(g=199\)

tas : Touba \(g=199\) Louga \(g=205\)

tas : Louga \(g=205\)

tas : Louga \(g=205\)

Sortie \(\rightarrow\) 

['Dakar', 'Rufisque', 'Thies', 'Diourbel', 'Touba'] 199

Lecture. Initialisation (lignes 2–3) : le tas contient Dakar, de priorité 0.

Lecture. Tour 1 : Dakar sort (plus petit \(g=0\)) ; Rufisque (\(g=26\)), Mbour (\(g=83\)) entrent dans le tas.

Lecture. Tour 2 : Rufisque sort (plus petit \(g=26\)) ; Thiès (\(g=71\)) entre dans le tas.

Lecture. Tour 3 : Thiès sort (plus petit \(g=71\)) ; Tivaouane (\(g=95\)), Diourbel (\(g=151\)) entrent dans le tas.

Lecture. Tour 4 : Mbour sort (plus petit \(g=83\)) ; aucun voisin n’améliore son coût, le tas ne reçoit rien.

Lecture. Tour 5 : Tivaouane sort (plus petit \(g=95\)) ; Kébémer (\(g=165\)) entre dans le tas.

Lecture. Tour 6 : Diourbel sort (plus petit \(g=151\)) ; Touba (\(g=199\)) entre dans le tas.

Lecture. Tour 7 : Kébémer sort (plus petit \(g=165\)) ; Louga (\(g=205\)) entre dans le tas.

Lecture. Tour 8 : Touba sort (plus petit \(g=199\)) ; c’est le but, la fonction rend ce nœud (ligne 9).

Lecture. Dernière ligne : 199 km, 8 nœuds développés.

L’optimalité d’UCS

Théorème (coûts d’action \(\geq0\))

Quand un nœud \(n\) sort de la frontière, \(g(n)\) est le coût minimal pour atteindre son état. Le premier but qui sort coûte donc \(C^*\).

Preuve

On suppose un chemin \(P\) vers le même état, de coût \(<g(n)\). Soit \(n'\) le premier nœud de \(P\) non encore développé : son prédécesseur l’a été, donc \(n'\) est dans la frontière, avec un coût \(\leq\) coût\((P)<g(n)\) (coûts \(\geq0\)). Alors \(n'\) aurait dû sortir avant \(n\) : contradiction.

Complexité et lien avec Dijkstra

Complète si les coûts sont \(\geq\varepsilon>0\) ; temps et espace \(O(b^{1+\lfloor C^*/\varepsilon\rfloor})\) : ici \(C^*=199\), \(\varepsilon=24\), \(b=4\), borne \(4^9=262\,144\), pour 8 nœuds développés. UCS est l’algorithme de Dijkstra (1959), arrêté au but, sur un graphe généré à la demande.

Le retour du Magal : le moment du test de but

n = meilleur_dabord("Touba", "Dakar", lambda g, v: g)    # le retour du Magal
print(chemin(n), n.g)

Résultat \(\rightarrow\)

['Touba', 'Diourbel', 'Thies', 'Rufisque', 'Dakar'] 199

Tester le but à la génération rend 251 km

Au retour, Dakar entre dans le tas par Mbour (\(g=168+83=251\)) avant que Rufisque (\(g=173\)) ne sorte et propose \(173+26=199<251\) : mise à jour. S’arrêter dès que Dakar est généré rendrait le chemin par Mbour, 251 km. Le premier chemin généré vers le but n’est pas le moins cher.

Lecture. Le test à la sortie rend 199 km : Touba, Diourbel, Thiès, Rufisque, Dakar. Pour BFS, tester à la génération est sans risque (même profondeur) ; pour UCS et A*, non.

Exercice : UCS sur la carte de Cheikh

Énoncé

UCS de P à O, priorité \(g\) ; but testé à la sortie.

  1. Étapes 1 et 2 : le nœud qui sort, son \(g\), le tas.
  2. Les étapes suivantes, avec les mises à jour.
  3. Le chemin et son coût.

Correction

  1. Étape 1 : P sort (\(g=0\)) ; tas : M(3), L(7).
    Étape 2 : M sort (\(g=3\)) ; tas : C(6), L(7), F(13).
  2. Étape 3 : C sort (\(g=6\)) ; tas : L(7), F(9) ; mise à jour : F 13\(\to\) 9.
    Étape 4 : L sort (\(g=7\)) ; tas : F(9), O(15).
    Étape 5 : F sort (\(g=9\)) ; tas : O(11) ; mise à jour : O 15\(\to\) 11.
    Étape 6 : O sort (\(g=11\)) : c’est le but.
  3. P–M–C–F–O, 11 min : optimal (6 nœuds sortis).

Quoi, pourquoi, quand ça échoue : UCS

Quoi

La recette générique avec une file de priorité sur \(g\), la mise à jour des meilleurs chemins et le test de but à la sortie.

Pourquoi elle est optimale

Les nœuds sortent par \(g\) croissant et, les coûts étant positifs, aucun chemin moins cher ne peut se cacher derrière la frontière.

Quand ça échoue

Elle ignore où est le but : 8 nœuds pour Dakar \(\to\) Touba, dont Mbour, Tivaouane et Kébémer, hors du chemin. Les coûts négatifs cassent la preuve ; les coûts nuls peuvent la faire tourner sans fin.

L’heuristique et la distance à vol d’oiseau

Heuristique

Une heuristique \(h(n)\) estime le coût du meilleur chemin de l’état de \(n\) à un but, à partir de cet état seul, sans chercher ; \(h=0\) au but. On note \(h^*(n)\) le vrai coût restant. UCS ignore où est Touba ; une estimation de la distance au but peut guider la recherche.

Formule de haversine (\(R=6,371\) km, angles en radians)

\[ d=2R\,\arcsin\sqrt{\sin^2\!\Big(\frac{\varphi_2-\varphi_1}{2}\Big)+\cos\varphi_1\cos\varphi_2\,\sin^2\!\Big(\frac{\lambda_2-\lambda_1}{2}\Big)} \]

\(\varphi\) : latitude, \(\lambda\) : longitude ; la longueur de l’arc de grand cercle, plus court chemin sur la sphère. Dakar \(\to\) Touba : \(x=1{,}88\times10^{-6}+0{,}93499\times1{,}861\times10^{-4}=1{,}759\times10^{-4}\), \(d=2\times6\,371\times0{,}013263=169{,}0\) km.

Les valeurs de \(h\)

GPS = {"Dakar": (14.6928, -17.4467), "Rufisque": (14.7167, -17.2667),
       "Thies": (14.7910, -16.9256), "Mbour": (14.4198, -16.9640),
       "Tivaouane": (14.9500, -16.8167), "Diourbel": (14.6550, -16.2314),
       "Touba": (14.8500, -15.8833), "Louga": (15.6181, -16.2244),
       "Kebemer": (15.3667, -16.4500), "Saint-Louis": (16.0326, -16.4818)}
def haversine(a, b, R=6371):             # distance a vol d'oiseau (km)
    (la1, lo1), (la2, lo2) = GPS[a], GPS[b]
    p1, p2 = math.radians(la1), math.radians(la2)
    dp, dl = p2 - p1, math.radians(lo2 - lo1)
    x = math.sin(dp / 2)**2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2)**2
    return 2 * R * math.asin(math.sqrt(x))
H = {v: round(haversine(v, "Touba")) for v in GPS}
print(round(haversine("Dakar", "Touba"), 2), H["Mbour"], H["Rufisque"])

Résultat \(\rightarrow\)

169.0 126 149
ville Dakar Rufisque Thiès Mbour Tivaouane Diourbel Touba Louga Kébémer Saint-Louis
\(h\) (km) 169 149 112 126 101 43 0 93 84 146
\(h^*\) (km) 199 173 128 168 152 48 0 100 140 173

Lecture. 169,0 km à vol d’oiseau contre 199 km par la route ; Mbour (126) paraît plus proche de Touba que Rufisque (149) : ce détail décide de la recherche gloutonne.

L’admissibilité

Quoi : \(h\leq h^*\) partout

\(h\) est admissible si elle ne surestime jamais le coût restant. Le vol d’oiseau l’est : toute route est une courbe sur la sphère, et le grand cercle est la plus courte.

\(h\) en orange, \(h^*\) en bleu ; neuf villes (Touba exclue).

Lecture. Marge minimale : 5 km, à Diourbel ; l’arrondi au km (au plus \(0{,}5\) km) ne rend aucune valeur inadmissible.

La recherche gloutonne, pas à pas

tas : Dakar \(h=169\)

tas : Mbour \(h=126\) Rufisque \(h=149\)

tas : Diourbel \(h=43\) Thiès \(h=112\) Rufisque \(h=149\)

tas : Touba \(h=0\) Thiès \(h=112\) Rufisque \(h=149\)

tas : Thiès \(h=112\) Rufisque \(h=149\)

tas : Thiès \(h=112\) Rufisque \(h=149\)

Sortie \(\rightarrow\) 

['Dakar', 'Mbour', 'Diourbel', 'Touba'] 251

Lecture. Initialisation (lignes 2–3) : le tas contient Dakar, de priorité 169.

Lecture. Tour 1 : Dakar sort (plus petit \(h=169\)) ; Mbour (\(h=126\)), Rufisque (\(h=149\)) entrent dans le tas.

Lecture. Tour 2 : Mbour sort (plus petit \(h=126\)) ; Diourbel (\(h=43\)), Thiès (\(h=112\)) entrent dans le tas.

Lecture. Tour 3 : Diourbel sort (plus petit \(h=43\)) ; Touba (\(h=0\)) entre dans le tas.

Lecture. Tour 4 : Touba sort (plus petit \(h=0\)) ; c’est le but, la fonction rend ce nœud (ligne 9).

Lecture. Dernière ligne : 251 km, 4 nœuds développés.

Le piège de Mbour

{Recherche gloutonne {(greedy best-first search)} : \(f=h\)}

On développe toujours le nœud qui paraît le plus proche du but. Rapide quand \(h\) guide bien (4 nœuds ici, contre 8 pour UCS). Non optimale ; complète en recherche en graphe sur un espace fini, mais pas en recherche arborescente, où elle peut osciller.

Ce qui la trompe

\(h\) dit ce qui reste, pas ce qui a été dépensé : Mbour est à 126 km de Touba à vol d’oiseau, mais y aller coûte 83 km, puis 120 km jusqu’à Diourbel. La route par Rufisque paraît moins prometteuse, alors qu’elle est 52 km plus courte.

Lecture. La recherche gloutonne oublie \(g\) ; UCS oubliait \(h\). A* additionne les deux.

A* : ce qui est fait, plus ce qui reste {data-court=“Informée”}

L’idée

A* est la recette avec la priorité \(f(n)=g(n)+h(n)\) : le coût déjà payé jusqu’à \(n\), plus l’estimation de ce qui reste ; \(f(n)\) estime le coût de la meilleure solution qui passe par \(n\). Un chauffeur de taxi-brousse additionne ce que marque le compteur et la distance qui reste à vol d’oiseau.

Cas particuliers

\(h=0\) : A* est UCS. Sans \(g\) : c’est la recherche gloutonne. Publié par Hart, Nilsson et Raphael en 1968.

Lecture. Dans le code, seul le dernier argument de meilleur_dabord change : lambda g, v: g + H[v].

A*, pas à pas {data-court=“Informée”}

tas : Dakar \(f=169\)

tas : Rufisque \(f=175\) Mbour \(f=209\)

tas : Thiès \(f=183\) Mbour \(f=209\)

tas : Diourbel \(f=194\) Tivaouane \(f=196\) Mbour \(f=209\)

tas : Tivaouane \(f=196\) Touba \(f=199\) Mbour \(f=209\)

tas : Touba \(f=199\) Mbour \(f=209\) Kébémer \(f=249\)

tas : Mbour \(f=209\) Kébémer \(f=249\)

tas : Mbour \(f=209\) Kébémer \(f=249\)

Sortie \(\rightarrow\) 

['Dakar', 'Rufisque', 'Thies', 'Diourbel', 'Touba'] 199

Lecture. Initialisation (lignes 2–3) : le tas contient Dakar, de priorité 169.

Lecture. Tour 1 : Dakar sort (plus petit \(f=0+169=169\)) ; Rufisque (\(f=175\)), Mbour (\(f=209\)) entrent dans le tas.

Lecture. Tour 2 : Rufisque sort (plus petit \(f=26+149=175\)) ; Thiès (\(f=183\)) entre dans le tas.

Lecture. Tour 3 : Thiès sort (plus petit \(f=71+112=183\)) ; Diourbel (\(f=194\)), Tivaouane (\(f=196\)) entrent dans le tas.

Lecture. Tour 4 : Diourbel sort (plus petit \(f=151+43=194\)) ; Touba (\(f=199\)) entre dans le tas.

Lecture. Tour 5 : Tivaouane sort (plus petit \(f=95+101=196\)) ; Kébémer (\(f=249\)) entre dans le tas.

Lecture. Tour 6 : Touba sort (plus petit \(f=199+0=199\)) ; c’est le but, la fonction rend ce nœud (ligne 9).

Lecture. Dernière ligne : 199 km, 6 nœuds développés.

L’optimalité d’A*

Recherche arborescente : \(h\) admissible \(\Rightarrow\) A* optimal

On suppose qu’un but \(G_2\) sorte avec \(g(G_2)>C^*\). Un nœud \(n\) du chemin optimal est alors dans la frontière, et \(f(n)=g(n)+h(n)\leq g(n)+h^*(n)=C^*<g(G_2)=f(G_2)\), car \(h(G_2)=0\) : \(n\) aurait dû sortir avant \(G_2\). Contradiction.

Recherche en graphe sans réouverture : \(h\) consistante \(\Rightarrow\) A* optimal

\(h\) est consistante (ou cohérente, monotone) si \(h(n)\leq c(n,a,n')+h(n')\) pour toute action. Alors \(f\) ne décroît pas le long d’un chemin : \(f(n')=g(n)+c+h(n')\geq g(n)+h(n)=f(n)\) ; un état sort la première fois avec son coût minimal, et l’écarter ensuite est sans risque.

Lecture. Sur le chemin optimal, \(f\) vaut 169, 175, 183, 194, 199 : il croît jusqu’à \(f=C^*\). Consistante entraîne admissible.

La consistance, vérifiée sur les 12 routes

route \(c\) \(|\Delta h|\) marge route \(c\) \(|\Delta h|\) marge
Dakar – Rufisque 26 20 6 Tivaouane – Kébémer 70 17 53
Rufisque – Thiès 45 37 8 Kébémer – Louga 40 9 31
Dakar – Mbour 83 43 40 Louga – Saint-Louis 73 53 20
Mbour – Thiès 64 14 50 Thiès – Diourbel 80 69 11
Thiès – Tivaouane 24 11 13 Diourbel – Touba 48 43 5
Mbour – Diourbel 120 83 37 Touba – Louga 100 93 7

Lecture. Pour une route à double sens, les deux sens donnent \(|h(a)-h(b)|\leq c(a,b)\). Les 12 marges sont positives ; la plus faible, 5 km, est celle de Diourbel – Touba. Le vol d’oiseau vérifie l’inégalité triangulaire ; l’arrondi n’a cassé aucune marge.

Ce que A* évite, et pourquoi

ville \(g^*\) \(h\) \(f\) A*
Dakar 0 169 169 oui
Rufisque 26 149 175 oui
Thiès 71 112 183 oui
Diourbel 151 43 194 oui
Tivaouane 95 101 196 oui
Touba 199 0 199 but
Mbour 83 126 209 non
Kébémer 165 84 249 non

Efficacité optimale (\(h\) consistante)

A* développe tout nœud de \(f<C^*\) et aucun de \(f>C^*\). Parmi les algorithmes qui garantissent une solution optimale, développent les chemins depuis la racine et n’utilisent que la même heuristique consistante, aucun ne développe moins de nœuds qu’A*, aux égalités près.

Lecture. \(g^*\) : vrai coût depuis Dakar. Mbour et Kébémer, développés par UCS (\(g<199\)), sont évités par A* (\(f>199\)) : 6 nœuds au lieu de 8.

Exercice : A* sur la carte de Cheikh

Énoncé : A* de P à O, \(h\) en orange

  1. Vérifier \(h\leq h^*\).
  2. Étapes 1 et 2 : le nœud qui sort, \(f=g+h\), le tas.
  3. Les étapes suivantes, jusqu’au but.
  4. Chemin et coût ; comparer à UCS et à la recherche gloutonne.

Correction

  1. \(h^*\) : P 11, M 8, C 5, L 7, F 2, O 0 : \(h\leq h^*\) partout.
  2. Étape 1 : P sort (\(f=0+9=9\)) ; tas : M(10), L(13).
    Étape 2 : M sort (\(f=3+7=10\)) ; tas : C(10), L(13), F(15).
  3. Étape 3 : C sort (\(f=6+4=10\)) ; tas : F(11), L(13).
    Étape 4 : F sort (\(f=9+2=11\)) ; tas : O(11), L(13).
    Étape 5 : O sort (\(f=11+0=11\)) : c’est le but.
  4. P–M–C–F–O, 11 min, 5 nœuds (UCS : 6) ; recherche gloutonne : P, L, O, 15 min.

Quoi, pourquoi, quand ça échoue : A*

Quoi

La recette générique avec la priorité \(f=g+h\), le test de but à la sortie et la mise à jour des meilleurs chemins.

Pourquoi

Quand \(h\) est admissible, \(f(n)\) minore le coût de toute solution qui passe par \(n\) : un nœud de la solution optimale sort avant tout but trop cher. \(h\) écarte les nœuds de \(f>C^*\) que UCS développe.

Quand ça échoue

La mémoire : A* garde tous les nœuds générés, exponentiel dans le pire cas. Une heuristique qui surestime perd la garantie (section suivante). Avec \(h=0\), A* n’est que UCS.

La relaxation

Définition

Une relaxation d’un problème s’obtient en retirant des contraintes sur les actions. Le coût optimal du problème relâché est une heuristique admissible du problème d’origine : toute solution d’origine reste permise, donc le relâché coûte au plus autant.

Deux relaxations déjà rencontrées ou à venir

Réseau routier : on retire « suivre les routes » ; on vole en ligne droite : la distance à vol d’oiseau.

Taquin {(8-puzzle)} : une tuile passe de \(A\) à \(B\) si \(A\) et \(B\) sont voisines et si \(B\) est vide. Retirer « \(B\) vide » donne \(h_2\), la somme des distances de Manhattan ; retirer les deux conditions donne \(h_1\), le nombre de tuiles mal placées.

Lecture. Le problème relâché doit se résoudre sans recherche : sinon, calculer \(h\) coûterait autant que chercher.

Le taquin : \(h_1\) et \(h_2\)

Un état à 12 coups du but : tuiles mal placées en orange, distance de Manhattan de chaque tuile en bleu.

Lecture. \(h_1=7\) (seule la tuile 5 est en place), \(h_2=10\), vrai coût 12 : les deux sont admissibles. A* sort 61 nœuds avec \(h_1\), 17 avec \(h_2\) (égalités de \(f\) départagées par ordre lexicographique de l’état) ; IDS en sort \(4\,860\).

Dominance et maximum

Dominance : \(h_2(n)\geq h_1(n)\) pour tout \(n\), toutes deux admissibles

Une tuile mal placée est à au moins une case de sa place : \(h_2\geq h_1\), et \(h_2\) domine \(h_1\). A* avec \(h_2\) ne développe aucun nœud de plus qu’avec \(h_1\) (\(h\) consistantes, aux nœuds de \(f=C^*\) près) : elle guide plus précisément sans compromettre l’optimalité.

Combiner des heuristiques admissibles

\[ h(n)=\max\big(h_1(n),\ h_2(n),\ \dots,\ h_k(n)\big) \]

Chaque \(h_i\) minore \(h^*\), donc leur maximum aussi : il est admissible et domine chacune. Sur l’état de la figure, \(\max(7,10)=10\).

Lecture. Une bonne heuristique est admissible, informative (proche de \(h^*\)) et rapide à calculer. \(h^*\) elle-même serait parfaite, mais la calculer revient à résoudre le problème.

Mesures sur un lot de taquins

\(d\) IDS A*-\(h_1\) A*-\(h_2\)
4 46,6 5,1 5,0
6 169,8 8,3 7,1
8 555,1 14,1 9,6
10 1 704,1 31,2 15,4
12 – 70,7 24,3
16 – 425,6 90,5
20 – 2 403,3 258,9

Nœuds développés en moyenne ; 50 états tirés au hasard (graine 221) à distance \(d\) du but (tous pour \(d\leq6\)) ; IDS jusqu’à \(d=10\).

Échelle logarithmique.

Lecture. À \(d=20\), \(h_2\) développe 9 fois moins que \(h_1\). Facteur de branchement effectif \(b^*\) (\(N=1+b^*+\dots+(b^*)^d\), \(N\) nœuds sortis de la frontière) à \(d=20\) : \(1{,}38\) pour \(h_1\), \(1{,}21\) pour \(h_2\).

Une heuristique qui surestime

On pose \(h'(\text{Rufisque})=230\) au lieu de 149

\(230>h^*(\text{Rufisque})=173\) : \(h'\) n’est plus admissible. Après Dakar : \(f(\text{Rufisque})=26+230=256\), \(f(\text{Mbour})=83+126=209\). Mbour sort, puis Diourbel (\(203+43=246\)), puis Touba (\(f=g=251<256\)) : A* rend 251 km, en 4 nœuds, sans avoir développé Rufisque.

L’admissibilité est le verrou

Surestimer \(h(\text{Rufisque})\) fait croire que passer par Rufisque est trop cher, et A* l’écarte : le chemin optimal (199 km) n’est jamais examiné. Plus \(h\) surestime, plus A* ressemble à la recherche gloutonne.

Exercice : deux heuristiques sur un taquin

L’état \(5,1,3,/,4,\square,2,/,7,8,6\), à 12 coups du but \(1,2,3,/,4,5,6,/,7,8,\square\)

  1. Calculer \(h_1\).
  2. Calculer \(h_2\), tuile par tuile.
  3. Vérifier \(h_1\leq h_2\leq h^*\).
  4. Que vaut \(\max(h_1,h_2)\), et quelle heuristique A* doit-il utiliser ?

Correction

  1. Tuiles mal placées : 5, 1, 2 et 6 : \(h_1=4\).
  2. 5 : 2 cases ; 1 : 1 ; 2 : 2 ; 6 : 1 ; les autres : 0 ; \(h_2=6\).
  3. \(4\leq6\leq12\).
  4. \(\max(4,6)=6=h_2\) : \(h_2\) domine \(h_1\).

Lecture. Les deux sont loin de 12 : les tuiles se gênent, et c’est la contrainte « case vide » que les relaxations ont retirée.

La recherche sans chemin

L’idée

Pour placer 8 reines sans conflit, ou bâtir un emploi du temps à l’UCAD, seul l’état final compte. La recherche locale {(local search)} garde un seul état complet et le remplace par l’un de ses voisins, selon un coût \(h\) à minimiser : pas d’arbre, une mémoire constante.

Emploi du temps

20 cours à placer dans 100 créneaux-salles : \(100^{20}=10^{40}\) affectations, hors de portée de BFS ou de A*. Déplacer un cours donne \(20\times99=1\,980\) voisins : l’état courant s’améliore pas à pas.

Le paysage du coût

Le paysage du coût, en une dimension pour le dessin.

Lecture. Une descente qui ne voit que ses voisins s’arrête au premier creux (minimum local) ou sur un plateau, même si un meilleur état existe plus loin.

Le hill climbing, formellement

Hill climbing, coût \(h\) à minimiser

  1. \(e\leftarrow\) un état complet tiré au hasard.
  2. \(v\leftarrow\) le voisin de plus petit \(h\) (à égalité, le premier dans l’ordre colonne, ligne).
  3. Si \(h(v)\geq h(e)\) : rendre \(e\) (minimum local ou plateau). Sinon \(e\leftarrow v\), et retour en 2.

Les 8 reines

État : la ligne de la reine de chaque colonne, \(8^8=16\,777\,216\) états ; voisins : une reine déplacée dans sa colonne, \(8\times7=56\) ; \(h\) : nombre de paires de reines qui s’attaquent ; \(h=0\) : solution.

Lecture. Le nom vient de la version qui maximise un score ; minimiser \(h\) revient à maximiser \(-h\).

Le hill climbing sur un échiquier

Chaque case porte le \(h\) obtenu en y déplaçant la reine de sa colonne ; en orange, le meilleur voisin.

Lecture. \(h\) : 6, puis 4, puis 2 ; le meilleur voisin vaut encore 2 : arrêt sur un plateau, sans solution.

Le hill climbing en Python

def conflits(e):
    return sum(1 for i in range(8) for j in range(i + 1, 8)
               if e[i] == e[j] or abs(e[i] - e[j]) == j - i)
def voisins_r(e):
    return [e[:c] + (l,) + e[c + 1:] for c in range(8)
            for l in range(8) if l != e[c]]
def hill_climbing(e):
    h, pas = conflits(e), 0
    while h > 0:
        v = min(voisins_r(e), key=conflits)
        if conflits(v) >= h:
            break
        e, h, pas = v, conflits(v), pas + 1
    return e, h, pas
print(hill_climbing((3, 6, 1, 7, 4, 3, 5, 7)))

Résultat \(\rightarrow\)

((2, 6, 1, 7, 0, 3, 5, 7), 2, 2)

Lecture. Le départ de la figure : arrêt à \(h=2\) après 2 pas, sur l’état (2, 6, 1, 7, 0, 3, 5, 7).

Taux de succès mesurés

rng = random.Random(221)
departs = [tuple(rng.randrange(8) for _ in range(8)) for _ in range(1000)]
res = [hill_climbing(e) for e in departs]
ok = [p for _, h, p in res if h == 0]
print(departs[0], len(ok), round(sum(ok) / len(ok), 2))

Résultat \(\rightarrow\)

(3, 6, 1, 7, 4, 3, 5, 7) 166 4.2
variante (mêmes 1 000 départs) succès pas moyens
hill climbing \(16{,}6\) % \(4{,}20\)
hill climbing, au plus 100 pas latéraux consécutifs \(95{,}3\) % \(18{,}47\)
hill climbing avec redémarrages aléatoires 100 % \(6{,}58\) lancers

Lecture. 166 succès sur 1 000. Un pas latéral accepte un voisin de même coût pour traverser un plateau. Avec redémarrages, le nombre de lancers suit une loi géométrique d’espérance \(1/0{,}166=6{,}02\) ; mesuré : \(6{,}58\).

Le recuit simulé, formellement

Un voisin \(v\) tiré au hasard, \(\Delta E=h(v)-h(e)\)

\[ P(\text{accepter }v)=\begin{cases}1 & \text{si }\Delta E\leq0\\ e^{-\Delta E/T} & \text{si }\Delta E>0\end{cases}\qquad T_k=T_0\,\alpha^k \]

\(T\) : la température, qui décroît ; \(T_0=2\), \(\alpha=0{,}995\) : \(T\) passe sous \(0{,}01\) au pas \(1\,058\). \(\Delta E=1\) : \(0{,}607\) à \(T=2\), \(0{,}368\) à \(T=1\), \(0{,}018\) à \(T=0{,}25\).

{Recuit simulé {(simulated annealing)}}

Chaud, on accepte souvent une hausse et l’on explore ; froid, on n’accepte plus que des baisses. Le nom vient de la métallurgie : un métal refroidi lentement atteint un état de basse énergie (Kirkpatrick, Gelatt et Vecchi, 1983).

Un recuit sur les 8 reines

À gauche, la probabilité d’accepter une hausse ; à droite, \(h\) (noir) et \(T\) (orange) au fil des pas.

Lecture. Même départ que le hill climbing (\(h=6\)) : 42 hausses acceptées, dont une jusqu’à \(h=12\), et \(h=0\) au pas 442, là où le hill climbing restait à \(h=2\).

Le recuit en Python

def recuit(e, rng, T0=2.0, alpha=0.995, pas_max=3000):
    h, T = conflits(e), T0
    for k in range(pas_max):
        if h == 0:
            return e, 0, k
        c = rng.randrange(8)
        l = rng.randrange(7)
        l = l + (l >= e[c])                      # une autre ligne que l'actuelle
        v = e[:c] + (l,) + e[c + 1:]
        dE = conflits(v) - h
        if dE <= 0 or rng.random() < math.exp(-dE / T):
            e, h = v, h + dE                     # accepte, meme une hausse
        T = T * alpha                            # refroidissement geometrique
    return e, h, pas_max
rng = random.Random(221)
res = [recuit(e, rng) for e in departs]
ok = [k for _, h, k in res if h == 0]
print(len(ok), round(sum(ok) / len(ok), 1))

Résultat \(\rightarrow\)

942 791.0

Lecture. 942 succès sur 1 000 (\(94{,}2\) %), en \(791{,}0\) pas en moyenne.

Quoi, pourquoi, quand ça échoue : la recherche locale

Quoi

Un état courant complet, un voisinage, un coût \(h\) ; hill climbing : toujours le meilleur voisin ; recuit : un voisin au hasard, accepté selon \(e^{-\Delta E/T}\).

Pourquoi elle passe à l’échelle, et le lien avec le ML

Sur \(16\,777\,216\) états, le hill climbing n’en évalue que 56 par pas. La descente de gradient, \(\symbfit{\theta}\leftarrow\symbfit{\theta}-\eta\nabla J(\symbfit{\theta})\), est une recherche locale dans l’espace continu des paramètres d’un modèle : elle suit la pente de la perte \(J\) (Séance 6, puis cours de Machine Learning).

Quand ça échoue

Minima locaux et plateaux : \(83{,}4\) % d’échecs pour le hill climbing. Le recuit dépend de \(T_0\), \(\alpha\) et du nombre de pas ; la recherche locale ne dit jamais qu’aucune solution n’existe.

Exercice : recherche locale à la main

Énoncé

  1. Sur 4 reines, une par colonne, que vaut \(h\) pour \((0,1,2,3)\), toutes sur une diagonale ?
  2. Combien de voisins a un état à 4 reines ?
  3. Le hill climbing réussit avec \(p=0{,}166\) : combien de lancers en moyenne avec des redémarrages ?
  4. Recuit : probabilité d’accepter \(\Delta E=2\) à \(T=2\), puis à \(T=0{,}5\) ?

Correction

  1. Les 6 paires sont sur la même diagonale : \(h=\binom42=6\).
  2. \(4\times3=12\).
  3. \(1/0{,}166=6{,}02\) en espérance.
  4. \(e^{-2/2}=e^{-1}=0{,}368\) ; \(e^{-2/0{,}5}=e^{-4}=0{,}018\).

Les algorithmes côte à côte

algorithme frontière, priorité complet optimal (coût) temps ; espace km ; nœuds
BFS file FIFO oui (\(b\) fini) si coûts égaux \(O(b^{d+1})\) ; \(O(b^{d+1})\) 251 ; 6
DFS (arbre) pile LIFO si espace fini non \(O(b^m)\) ; \(O(bm)\) 517 ; 10
IDS pile, limite \(0,1,2,\dots\) oui (\(b\) fini) si coûts égaux \(O(b^d)\) ; \(O(bd)\) 251 ; 15
UCS file de priorité, \(g\) oui (coûts \(\geq\varepsilon>0\)) oui \(O(b^{1+\lfloor C^*/\varepsilon\rfloor})\) ; idem 199 ; 8
recherche gloutonne file de priorité, \(h\) en graphe fini non \(O(b^m)\) ; \(O(b^m)\) 251 ; 4
A* file de priorité, \(g+h\) oui (coûts \(\geq\varepsilon>0\)) \(h\) admissible\(^{(*)}\) exponentiel au pire ; tous les nœuds 199 ; 6
hill climbing un état non non selon le paysage ; \(O(1)\) \(16{,}6\) % (reines)
recuit simulé un état non non fixé par \(T_0\), \(\alpha\) ; \(O(1)\) \(94{,}2\) % (reines)

Lecture. Dernière colonne : Dakar \(\to\) Touba (km ; nœuds développés), ou succès sur les 8 reines. BFS : test à la sortie (\(O(b^d)\) à la génération). \(^{(*)}\) consistante en recherche en graphe sans réouverture. Seuls UCS et A* rendent les 199 km.

Le choix d’un algorithme

  1. Seul l’état final compte : recherche locale (hill climbing avec redémarrages, ou recuit), en mesurant le taux de succès.
  2. Les actions coûtent autant : BFS si la mémoire le permet, IDS sinon.
  3. Les coûts diffèrent, sans information sur le but : UCS.
  4. Une heuristique admissible existe (consistante en recherche en graphe) : A*.
  5. La mémoire de A* sature : IDA*, un DFS limité par un seuil sur \(f\) : sur Dakar \(\to\) Touba, seuils 169, 175, 183, 194, 196, 199, soit 20 nœuds développés au lieu de 6, mais seulement le chemin courant en mémoire.

Limites de A*

Une mémoire exponentielle ; une heuristique à concevoir ; des espaces trop grands (le Go : \(b\approx250\), \(d\approx150\)), où l’on combine recherche, échantillonnage et heuristiques apprises (Monte-Carlo Tree Search, AlphaGo). Lien avec le ML : l’heuristique peut s’apprendre à partir de données (AlphaGo estime la valeur d’une position par un réseau de neurones) ; quand transitions et coûts sont inconnus, l’agent apprend en interagissant : c’est l’apprentissage par renforcement (Séance 6).

À retenir

À retenir

  • Problème : \((\mathcal S,s_0,\mathrm{Actions},\mathrm{Résultat},c,\mathrm{EstBut})\) ; \(C^*=199\) km pour Dakar \(\to\) Touba.
  • Recette : une frontière, des états atteints, un parent par nœud ; seule la règle de retrait change.
  • BFS : le moins d’actions (251 km en 3 routes) ; DFS : mémoire \(O(bm)\), résultat selon l’ordre (517 km) ; IDS : les deux qualités.
  • UCS : priorité \(g\), test du but à la sortie ; optimale si les coûts sont positifs (199 km, 8 nœuds).
  • Heuristique : admissible si \(h\leq h^*\) ; consistante si \(h(n)\leq c(n,a,n')+h(n')\). Recherche gloutonne : \(f=h\), piégée par Mbour (251 km).
  • A* : \(f=g+h\), optimal (199 km, 6 nœuds) ; relaxation, dominance et maximum pour concevoir \(h\).
  • Recherche locale : un état, des voisins ; hill climbing \(16{,}6\) %, recuit \(94{,}2\) % sur les 8 reines.

Prochaine séance

Séance 5 — L’IA qui doute

Toute cette séance supposait un monde connu avec certitude : des états observés, des actions à l’effet sûr, des coûts connus d’avance. Un médecin qui reçoit Fatou, fiévreuse en pleine saison des pluies, ne voit pas la maladie, seulement des symptômes. La Séance 5 donne à l’agent un langage pour l’incertitude : les probabilités, la probabilité conditionnelle, le théorème de Bayes, puis les réseaux bayésiens.

La Séance 4 est l’évaluation de mi-parcours, sur les Séances 1 à 3. À préparer : refaire à la main BFS, UCS et A* sur la carte de Cheikh, puis vers Saint-Louis sur le réseau de la séance.

Références

  • Russell, Norvig — Artificial Intelligence: A Modern Approach, 4e éd., Pearson, 2020.
  • Poole, Mackworth — Artificial Intelligence: Foundations of Computational Agents, 3e éd., Cambridge University Press, 2023.
  • Pearl — Heuristics: Intelligent Search Strategies for Computer Problem Solving, Addison-Wesley, 1984.
  • Hart, Nilsson, Raphael — A Formal Basis for the Heuristic Determination of Minimum Cost Paths, IEEE, 1968.
  • Dijkstra — A Note on Two Problems in Connexion with Graphs, Numerische Mathematik, 1959.
  • Kirkpatrick, Gelatt, Vecchi — Optimization by Simulated Annealing, Science, 1983.