Les métaheuristiques sont des procédures ou heuristiques de haut niveau conçues pour guider la recherche de solutions dans des problèmes d’optimisation avec des espaces de solutions vastes, visant à trouver de bonnes solutions plus efficacement que les méthodes traditionnelles.
Les métaheuristiques équilibrent l’exploitation et l’exploration pour éviter les optima locaux, incorporant souvent l’aléatoire, la mémoire ou des mécanismes adaptatifs.
Définition
Un algorithme génétique (AG) est une technique d’optimisation évolutionnaire qui utilise une population de solutions candidates, les faisant évoluer par sélection, croisement et mutation pour s’améliorer de manière itérative vers une solution “optimale”.
Il n’y a probablement pas de concept plus original, plus complexe et plus audacieux dans l’histoire des idées que l’explication mécaniste de Darwin sur l’adaptation.
La sélection naturelle est une différence non aléatoire dans la production reproductive parmi des entités réplicantes, souvent due indirectement à des différences de survie dans un environnement particulier, conduisant à une augmentation de la proportion de caractéristiques héréditaires bénéfiques au sein d’une population d’une génération à l’autre.
Composantes
AG de base
Initialiser la population
Calculer l’aptitude (fitness)
Sélectionner les individus (chromosomes)
Croisement
Mutation
Si non terminé, retourner à l’étape 2
Choix
Comment coder une solution ou un état candidat ?
Comment sélectionner les solutions candidates ?
Comment définir l’opérateur de croisement ?
Comment définir l’opérateur de mutation ?
Comment calculer l’aptitude (fitness) ?
Problème
Problème du sac à dos 0/1 : Étant donné des objets avec des poids et des valeurs définis, l’objectif est de maximiser la valeur totale en sélectionnant des objets pour un sac à dos sans dépasser une capacité fixe. Chaque objet doit être entièrement inclus (1) ou exclu (0).
où \(W\) représente le poids maximal fixe, et \(x_i\) est une variable binaire indiquant si l’objet \(i\) est inclus (1) ou exclu (0).
Applications
Finance et investissement : Dans l’optimisation de portefeuille, où chaque actif a un risque (analogue au poids) et un rendement attendu (valeur), le cadre du sac à dos aide à sélectionner un ensemble d’actifs qui maximise le rendement sans dépasser un seuil de risque.
Allocation des ressources : Couramment utilisé dans la gestion de projets, où les ressources (budget, personnel, temps) doivent être allouées à des projets ou des tâches pour maximiser la valeur totale, en tenant compte de la disponibilité limitée.
Chaîne d’approvisionnement et logistique : Utilisé pour maximiser la valeur des biens transportés dans les limites de poids ou de volume des véhicules. Il peut également être appliqué au stockage en entrepôt, où l’espace est limité, et les articles de grande valeur sont prioritaires.
Placement publicitaire et marketing : Utilisé dans la publicité numérique pour sélectionner la combinaison la plus rentable d’annonces à afficher dans un espace limité (par exemple, espace publicitaire sur un site Web ou une application), maximisant ainsi les revenus dans le cadre des contraintes de taille ou d’affichage.
Les algorithmes gloutons (greedy) prennent la décision de ce qu’il faut faire ensuite en sélectionnant la meilleure option locale parmi toutes les options disponibles sans tenir compte de la structure globale.
def greedy_knapsack_weight(weights, values, capacity): num_items =len(weights)# Créer une liste d'éléments avec leurs valeurs et indices originaux items =list(zip(weights, values, range(num_items)))# Trier les éléments par poids en ordre croissant items.sort() total_weight, total_value =0, 0 solution = np.zeros(num_items, dtype=int)# Sélectionner les éléments selon l'ordre triéfor w, v, idx in items:if total_weight + w <= capacity: solution[idx] =1 total_weight += w total_value += velse:break# Ignorer les éléments qui dépassent la capacitéreturn solution, total_value, total_weight
def greedy_knapsack_value(weights, values, capacity): num_items =len(weights)# Créer une liste d'éléments avec leurs valeurs et indices originaux items =list(zip(values, weights, range(num_items)))# Trier les éléments par valeur en ordre décroissant items.sort(reverse=True) total_weight =0 total_value =0 solution = np.zeros(num_items, dtype=int)# Sélectionner les éléments selon l'ordre triéfor v, w, idx in items:if total_weight + w <= capacity: solution[idx] =1 total_weight += w total_value += vreturn solution, total_value, total_weight
def initialize_population(pop_size, num_items):""" Initialiser la population avec des chaînes binaires aléatoires. Args: pop_size (int): Nombre d'individus dans la population. num_items (int): Nombre d'objets dans le problème du sac à dos. Returns: np.ndarray: Population initialisée. """return np.random.randint(2, size=(pop_size, num_items))
Population
La méthode proposée pour initialiser la population présente un problème.
La sélection par roulette est une méthode stochastique où la probabilité de sélectionner un individu est proportionnelle à son aptitude par rapport au reste de la population.
roulette_selection
def roulette_selection(population, fitness):# Ajuster l'aptitude pour qu'elle ne soit pas négative min_fitness = np.min(fitness) adjusted_fitness = fitness - min_fitness +1e-6# petite valeur epsilon pour éviter la division par zéro total_fitness = np.sum(adjusted_fitness) probabilities = adjusted_fitness / total_fitness pop_size = population.shape[0] selected_indices = np.random.choice(pop_size, size=pop_size, p=probabilities)return population[selected_indices]
L’élitisme dans les algorithmes génétiques est une stratégie où un sous-ensemble des individus les plus aptes de la génération actuelle est directement transféré à la génération suivante.
Cette approche garantit que les meilleures solutions sont préservées tout au long du processus évolutif, accélérant la convergence et maintenant des solutions de haute qualité au sein de la population.
Élitisme
def elitism(population, fitness, elite_size): elite_indices = np.argsort(fitness)[-elite_size:] # Obtenir les indices des meilleurs individus elites = population[elite_indices]return elites
Algorithme Génétique (Version 1)
def genetic_algorithm(weights, values, capacity, pop_size=100, num_generations=100, crossover_rate=0.8, mutation_rate=0.05, elite_percent=0.02): num_items =len(weights) elite_size =max(1, int(pop_size * elite_percent)) population = initialize_population(pop_size, num_items)for generation inrange(num_generations): fitness = evaluate_fitness(population, weights, values, capacity)# Élitisme elites = elitism(population, fitness, elite_size)# Sélection parents = roulette_selection(population, fitness)# Croisement offspring = single_point_crossover(parents, crossover_rate)# Mutation offspring = mutation(offspring, mutation_rate)# Création d'une nouvelle population population = np.vstack((elites, offspring))# Assurer la taille de la populationif population.shape[0] > pop_size: population = population[:pop_size]elif population.shape[0] < pop_size:# Ajouter des individus aléatoires pour combler la population num_new_individuals = pop_size - population.shape[0] new_individuals = initialize_population(num_new_individuals, num_items) population = np.vstack((population, new_individuals))# Après toutes les générations, retourner la meilleure solution fitness = evaluate_fitness(population, weights, values, capacity) best_index = np.argmax(fitness) best_solution = population[best_index] best_value = np.dot(best_solution, values) best_weight = np.dot(best_solution, weights)return best_solution, best_value, best_weight
Encodage par permutation : Utilisé typiquement pour des problèmes comme le voyageur de commerce ou le problème des N-Reines.
Codage par valeurs : Ce codage utilise des valeurs entières, réelles ou des caractères. Exemple : apprendre les paramètres d’un polynôme pour un problème de régression.
Sélection par tournoi
La sélection par tournoi consiste à sélectionner aléatoirement un sous-ensemble d’individus (un tournoi) dans la population, puis à choisir le meilleur individu de ce sous-ensemble comme parent. Ce processus est répété jusqu’à ce que le nombre requis de parents soit sélectionné.
Cette méthode équilibre exploration et exploitation, permettant de contrôler la pression de sélection en modifiant la taille du tournoi. Des tournois plus grands augmentent la pression de sélection, favorisant davantage les individus les plus aptes.
Le croisement uniforme dans les algorithmes génétiques est une technique de recombinaison où chaque gène de la descendance est choisi indépendamment de l’un des deux génomes parentaux avec une probabilité égale. Cette approche permet une combinaison plus variée des traits parentaux par rapport aux méthodes de croisement traditionnelles, favorisant une plus grande diversité génétique dans la population résultante.
Le croisement à un point est avantageux lorsque les blocs de construction (séquences de gènes contiguës) sont significatifs et bénéfiques à préserver. Cependant, dans le problème du sac à dos 0/1, la position des objets dans le chromosome est généralement arbitraire, et préserver des sections contiguës peut ne pas correspondre à de meilleures solutions.
Le croisement uniforme offre une meilleure exploration en mélangeant les gènes de manière indépendante, ce qui s’aligne bien avec la nature du problème du sac à dos où l’inclusion de chaque objet est une décision indépendante. Cela réduit le biais de position et augmente la probabilité de découvrir des combinaisons optimales d’objets.
Croisement
Préservation de l’ordre. Pour chaque descendant, conserver la séquence des éléments d’un parent tout en remplissant les positions restantes avec des éléments de l’autre parent, en préservant leur ordre tel qu’ils apparaissent chez le second parent.
Croisement d’ordre 1 (OX)
Croisement PMX
Comparaison
Caractéristique
PMX
OX
Préservation de la Position
Élevée
Faible
Préservation de l’Ordre
Partielle
Élevée
Exploration vs. Exploitation
Axée sur l’exploitation
Axée sur l’exploration
Complexité de l’Implémentation
Plus élevée
Plus faible
Adéquation de l’Application
Problèmes avec des dépendances positionnelles
Problèmes avec des dépendances d’ordre
Mutation
Dans un flip de bit, une position spécifique du chromosome est inversée (0 devient 1 et vice versa). C’est la méthode la plus courante pour des représentations binaires.
Un taux de mutation typique est de \(1/n\), où \(n\) représente la longueur du chromosome. Ce paramètre équilibre l’exploration et l’exploitation.
Mutation
Remplacement ou réinitialisation aléatoire: Pour des chromosomes à valeurs entières ou réelles, cette mutation implique le remplacement d’une position par une valeur aléatoire tirée d’une distribution uniforme \(U(a, b)\). Similaire au flip de bit, cette mutation est décidée de manière probabiliste pour chaque position.
Mutation
Permutation (Mutation par échange): Dans la mutation de permutation, deux gènes d’un chromosome sont sélectionnés aléatoirement, et leurs positions sont échangées. Ce type de mutation est souvent utilisé pour des problèmes d’optimisation combinatoire, comme le Voyageur de Commerce (TSP).
Remarques
Lors de la conception des opérateurs, il est crucial de s’assurer qu’ils peuvent explorer l’ensemble de l’espace d’états de manière exhaustive.
Les opérateurs de mutation doivent rester impartiaux pour maintenir l’intégrité du processus d’exploration.
Exemple Complet (1)
Un Jupyter Notebook contenant tout le code de la présentation a été créé.
Il inclut des tests sur 25 instances de problèmes.
Dans ce contexte, l’algorithme génétique a constamment surpassé les algorithmes gloutons, égalant les meilleurs résultats gloutons dans 8 cas et les dépassant dans 17 cas, avec des améliorations allant jusqu’à 6 %.
Exemple Complet (2)
Fredj Kharroubi a mené une étude empirique sur le problème du sac à dos, comparant la performance de plusieurs algorithmes : génération-et-test, recherche gloutonne, recuit simulé, et un algorithme génétique.
Une taille plus grande améliore la diversité génétique, mais augmente le coût computationnel.
Une taille plus petite accélère l’exécution, mais risque une convergence prématurée.
Discussions sur les hyperparamètres
Taux de mutation et croisement
Des taux élevés favorisent l’exploration, mais risquent de perturber les solutions viables.
Des taux faibles favorisent l’exploitation, mais risquent de se bloquer dans des optima locaux.
Discussions sur les hyperparamètres
Sélection et taille du tournoi
La sélection par tournoi est robuste face aux distributions inégales des aptitudes.
Une taille de tournoi moyenne (3-5) équilibre bien exploration et exploitation.
Paramètres
Les algorithmes génétiques, comme de nombreux algorithmes d’apprentissage automatique et de recherche, nécessitent un ajustement des hyperparamètres pour optimiser leur performance.
Les hyperparamètres clés dans les algorithmes génétiques incluent la taille de la population, le taux de mutation, le taux de croisement, la méthode de sélection et le nombre de générations.
Discussion
Comme d’autres approches métaheuristiques, les algorithmes génétiques peuvent être piégés dans des optima locaux. Une solution courante, semblable à la technique de redémarrage aléatoire utilisée dans la montée de colline, consiste à réinitialiser périodiquement l’algorithme pour explorer différentes régions de l’espace de solutions.
Doubler la taille de la population à chaque redémarrage augmente la probabilité d’explorer des régions diversifiées de l’espace d’états.
Il est assez peu naturel de modéliser des applications en termes d’opérateurs génétiques comme la mutation et le croisement sur des chaînes de bits. La pseudobiologie ajoute un autre niveau de complexité entre vous et votre problème. Deuxièmement, les algorithmes génétiques prennent beaucoup de temps sur des problèmes non triviaux. \(\ldots\) L’analogie avec l’évolution – où des progrès significatifs nécessitent des millions d’années – peut être tout à fait appropriée. \(\ldots\)Je n’ai jamais rencontré de problème pour lequel les algorithmes génétiques me semblaient être la bonne façon de l’aborder. De plus, je n’ai jamais vu de résultats computationnels rapportés utilisant des algorithmes génétiques qui m’ont favorablement impressionné. Restez fidèle au recuit simulé pour vos besoins de recherche heuristique vaudou.
Glouton : ratio valeur/poids
def greedy_knapsack_ratio(weights, values, capacity): num_items =len(weights)# Calculate value-to-weight ratio for each item ratio = values / (weights +1e-6)# Create a list of items with their ratios and original indices items =list(zip(ratio, values, weights, range(num_items)))# Sort items by ratio in decreasing order items.sort(reverse=True) total_weight =0 total_value =0 solution = np.zeros(num_items, dtype=int)# Select items based on the sorted orderfor r, v, w, idx in items:if total_weight + w <= capacity: solution[idx] =1 total_weight += w total_value += vreturn solution, total_value, total_weight
DEAP est un cadre de calcul évolutif conçu pour le prototypage rapide et le test d’idées. Il vise à rendre les algorithmes explicites et les structures de données transparentes. Le cadre s’intègre parfaitement aux mécanismes de parallélisation, y compris le multiprocessing.
Il a été développé à l’Université Laval depuis 2012.
La programmation génétique est une méthodologie basée sur les algorithmes évolutionnaires qui fait évoluer des programmes informatiques pour résoudre des problèmes en imitant les processus de sélection naturelle.
Elle découvre automatiquement des solutions optimales ou quasi-optimales en modifiant itérativement une population de programmes candidats, guidée par une fonction d’adéquation.
Programmation Génétique
Programmation Génétique
Prologue
Conclusion
Plutôt que d’explorer une seule solution à la fois, les algorithmes génétiques explorent plusieurs solutions en parallèle.
Comparaison entre SA et GA
Aspect
Recuit Simulé (SA)
Algorithmes Génétiques (GA)
Représentation de la Solution
Solution unique améliorée de manière itérative
Population de solutions évoluée au fil des générations
Mécanisme d’Exploration
Déplacements aléatoires vers des solutions voisines ; acceptation basée sur la température
Croisement et mutation génèrent de nouvelles solutions à partir de celles existantes
Mécanisme d’Exploitation
Réduction progressive de la température concentre la recherche autour de la meilleure solution actuelle
Sélection et élitisme favorisent les individus les plus aptes dans la population
Paramètres de Contrôle
Température, calendrier de refroidissement
Taille de la population, taux de croisement, taux de mutation, méthode de sélection
Stratégie de Recherche
Explore en acceptant des solutions moins bonnes à des températures plus élevées
Explore en combinant et mutant des solutions existantes
Équilibre entre Exploration et Exploitation
Contrôlé par le calendrier de température
Contrôlé par les taux des opérateurs génétiques et la pression de sélection
Échapper aux Optima Locaux
Possible grâce à l’acceptation probabiliste de solutions moins bonnes
Possible grâce à la diversité dans la population et aux variations génétiques
Convergence
Dépend du calendrier de refroidissement ; peut être lente pour les grands problèmes
Peut converger prématurément sans diversité suffisante
Résumé
Aperçu des Métaheuristiques
Algorithmes Génétiques (GAs)
Applications des GAs
Composants des GAs :
Encodage : Représentation des solutions candidates (par exemple, chaînes binaires pour le problème du sac à dos).
Population : Un ensemble de solutions candidates initialisées aléatoirement ou par une heuristique.
Sélection : Les méthodes comme la roulette et la sélection par tournoi choisissent les individus les plus aptes pour la reproduction.
Croisement : Combine des parties de deux parents pour créer une descendance (par exemple, croisement à un point).
Mutation : Altère aléatoirement les gènes dans un chromosome pour maintenir la diversité génétique.
Fonction d’Aptitude : Évalue la proximité d’une solution candidate par rapport à l’optimum.
Exemple du Problème du Sac à Dos :
A démontré comment appliquer les GAs au problème du sac à dos 0/1.
A fourni des extraits de code Python mettant en œuvre les composants GA pour le problème.
A montré comment générer une population initiale, effectuer un croisement et une mutation, et sélectionner la génération suivante.
A comparé les solutions GA avec les solutions optimales obtenues à l’aide des OR-Tools de Google.
Exploration d’arbres pour des problèmes de décision complexes.
Réferences
Bye, Robin T., Magnus Gribbestad, Ramesh Chandra, et Ottar L. Osen. 2021. « A Comparison of GA Crossover and Mutation Methods for the Traveling Salesman Problem ». In Innovations in Computational Intelligence and Computer Vision, édité par Manoj Kumar Sharma, Vijaypal Singh Dhaka, Thinagaran Perumal, Nilanjan Dey, et João Manuel R. S. Tavares, 529‑42. Singapore: Springer Singapore.
Cattolico, Mike, et Vincent A Cicirello. 2006. « Non-wrapping order crossover: an order preserving crossover operator that respects absolute position ». Proceedings of the 8th annual conference on Genetic and evolutionary computation, 1125‑32. https://doi.org/10.1145/1143997.1144177.
Dennett, Daniel C. 1995. « Darwin’s dangerous idea ». The Sciences 35 (3): 34‑40.
Eddaly, M., B. Jarboui, et P. Siarry. 2023. Metaheuristics for Machine Learning: New Advances and Tools. Computational Intelligence Methods et Applications. Springer Nature Singapore. https://books.google.ca/books?id=yXMtzwEACAAJ.
Gil-Rios, Miguel-Angel, Ivan Cruz-Aceves, Fernando Cervantes-Sanchez, Igor Guryev, et Juan-Manuel López-Hernández. 2021. « Automatic enhancement of coronary arteries using convolutional gray-level templates and path-based metaheuristics ». In Recent Trends in Computational Intelligence Enabled Research, édité par Siddhartha Bhattacharyya, Paramartha Dutta, Debabrata Samanta, Anirban Mukherjee, et Indrajit Pan, 129‑53. Academic Press.
Gregory, T. Ryan. 2009. « Understanding Natural Selection: Essential Concepts and Common Misconceptions ». Evolution: Education and Outreach 2 (2): 156‑75. https://doi.org/10.1007/s12052-009-0128-1.
Holland, John H. 1973. « Genetic Algorithms and the Optimal Allocation of Trials ». SIAM Journal on Computing 2 (2): 88‑105. https://doi.org/10.1137/0202009.
Mayr, Ernst. 1982. The growth of biological thought: Diversity, evolution, and inheritance. Harvard University Press.
Oliva, D., E. H. Houssein, et S. Hinojosa. 2021. Metaheuristics in Machine Learning: Theory and Applications. Studies in Computational Intelligence. Springer International Publishing. https://books.google.ca/books?id=Zlw4EAAAQBAJ.
Russell, Stuart, et Peter Norvig. 2020. Artificial Intelligence: A Modern Approach. 4ᵉ éd. Pearson. http://aima.cs.berkeley.edu/.
Santos Amorim, Elisa Portes dos, Carolina Ribeiro Xavier, Ricardo Silva Campos, et Rodrigo Weber dos Santos. 2012. « Comparison between Genetic Algorithms and Differential Evolution for Solving the History Matching Problem ». In Computational Science and Its Applications - ICCSA 2012 - 12th International Conference, Salvador de Bahia, Brazil, June 18-21, 2012, Proceedings, Part I, édité par Beniamino Murgante, Osvaldo Gervasi, Sanjay Misra, Nadia Nedjah, Ana Maria A. C. Rocha, David Taniar, et Bernady O. Apduhan, 7333:635‑48. Lecture Notes in Computer Science. Springer. https://doi.org/10.1007/978-3-642-31125-3\_48.