(Texto retirado do livro Practical genetic algorithms, Randy L. Haupt e Sue Ellen Haupt.—2ª ed.)
Otimização de Solução: Seleção Natural
O método do gradiente descendente, entre outros de busca de mínimo de funções, adotam a abordagem básica de desviar de um ponto de partida arbitrário. Eles diferem em decidir em que direção se mover e em que distância se mover. Melhorias sucessivas aumentam a velocidade desses algoritmos, mas não aumentam a capacidade do algoritmo de encontrar um mínimo global em vez de um mínimo local.
Alguns algoritmos surgiram para resolver tal problema. Alguns desses métodos incluem o algoritmo genético (Holland, 1975), simulated annealing (Kirkpatrick et al., 1983), otimização particle swarn (Parsopoulos e Vrahatis, 2002), otimização de colônias de formigas (Dorigo e Maria, 1997) e algoritmos evolutivos. (Schwefel, 1995). Esses métodos geram novos pontos no espaço de pesquisa, aplicando os operadores aos pontos atuais e movendo estatisticamente para lugares mais ideais no espaço de pesquisa.
Eles dependem de uma pesquisa inteligente de um espaço de solução grande, mas finito, usando métodos estatísticos. Os algoritmos não exigem a utilização de derivadas de função de custo e, portanto, podem lidar com variáveis discretas e funções de custo contínuo. Eles representam processos na natureza que são notavelmente bem sucedidos em otimizar fenômenos naturais.
O algoritmo genético (GA) é uma técnica de otimização e busca baseada nos princípios da genética e seleção natural. Um GA permite que uma população composta por muitos indivíduos evolua sob regras de seleção especificadas para um estado que maximiza a “adequação” (isto é, minimiza a função de custo).
O método foi desenvolvido por John Holland (1975) ao longo dos anos 1960 e 1970 e finalmente popularizado por um de seus alunos, David Goldberg, que conseguiu resolver um problema difícil envolvendo o controle da transmissão de gasoduto para sua dissertação (Goldberg, 1989). O trabalho original de Holland foi resumido em seu livro. Ele foi o primeiro a tentar desenvolver uma base teórica para GAs através de seu teorema de esquema. O trabalho de De Jong (1975) mostrou a utilidade do GA para otimização de funções e fez o primeiro esforço conjunto para encontrar parâmetros GA otimizados. Desde então, muitas versões da programação evolucionária foram tentadas com vários graus de sucesso.
Algumas das vantagens de um GA incluem:
• Otimiza com variáveis contínuas ou discretas, • Não requer derivadas,
• Pesquisas simultâneas de uma ampla amostragem da superfície de custo, • Lida com um grande número de variáveis,
• É bem adequado para computação paralela,
• Otimiza variáveis com superfícies de custo extremamente complexas (elas podem saltar de um mínimo local),
• Fornece uma lista de variáveis ótimas, não apenas uma única solução,
• Funciona com dados gerados numericamente, dados experimentais ou funções analíticas.
Essas vantagens são intrigantes e produzem resultados impressionantes quando as abordagens tradicionais de otimização falham. Naturalmente, o GA não é a melhor maneira de resolver todos os problemas. Por exemplo, os métodos tradicionais foram ajustados para encontrar rapidamente a solução de uma função analítica convexa bem comportada de apenas algumas variáveis. Para tais casos, os métodos baseados em cálculos superam o GA, encontrando rapidamente o mínimo, enquanto o GA ainda está analisando os custos da população inicial. Para esses problemas, o otimizador deve usar a experiência do passado e empregar esses métodos rápidos.
No entanto, muitos problemas realistas não se enquadram nessa categoria. Além disso, para problemas que não são excessivamente difíceis, outros métodos podem achar a solução mais rápida que o GA.
A grande população de soluções que dá ao GA seu poder também é sua maldição quando se trata de velocidade em um computador serial - a função de custo de cada uma dessas soluções deve ser avaliada. No entanto, se um computador paralelo estiver disponível, cada processador poderá avaliar uma função separada ao mesmo tempo. Assim, o GA é ideal para tais cálculos paralelos. Algoritmo Genético Binário
O GA começa, como qualquer outro algoritmo de otimização, definindo as variáveis de otimização, a função de custo e o custo. Ele termina como outros algoritmos de otimização também, testando a convergência. No meio, no entanto, esse algoritmo é bem diferente. Um caminho através dos componentes do GA é mostrado como um fluxograma abaixo.
A representação de uma função custo de duas variáveis é uma superfície com picos e vales, muito parecido com um mapa topográfico. Para encontrar um vale, um algoritmo de otimização procura o custo mínimo. Para encontrar um pico, um algoritmo de otimização pesquisa o custo máximo. Essa analogia leva ao exemplo de encontrar o ponto mais alto do Parque Nacional das Montanhas Rochosas. Um gráfico tridimensional de uma porção do parque (nosso espaço de busca) é mostrado na figura abaixo, e um mapa topográfico bruto (128 × 128 pontos). A localização do topo do Long’s Peak (14.255 pés acima do nível do mar) é o objetivo. Três outras características interessantes na área incluem Storm Peak (13,326 pés), Mount Lady Washington (13.281 pés) e Chasm Lake (11.800 pés).
Como há muitos picos na área de interesse, as técnicas de otimização convencionais têm dificuldade em encontrar Long’s Peak a menos que o ponto de partida esteja na vizinhança imediata do pico. Na verdade, todos os métodos que exigem um gradiente da função de custo não funcionam bem com dados discretos.
Seleção das Variáveis e da Função Custo
Uma função custo gera uma saída de um conjunto de variáveis de entrada (um cromossomo). A função custo pode ser uma função matemática, um experimento ou um jogo. O objeto é modificar a saída de alguma maneira desejável, encontrando os valores apropriados para as variáveis de entrada.
Um exemplo é o processo de encher uma banheira com água. O custo é a diferença entre as temperaturas desejada e real da água. As variáveis de entrada são quanto os registros quente e frio são girados. Neste caso, a função de custo é o resultado experimental de colocar a mão na água. O GA começa definindo um cromossomo ou uma matriz de valores de variáveis a serem otimizados. Se o cromossomo possui Nvar variáveis (um problema de otimização Nvar-dimensional)
dado por p1, p2, ... , pNvar, então o cromossomo é escrito como um vetor linha com Nvar elementos.
chromosome = [p1, p2, ... , pNvar]
Por exemplo, procurar a elevação máxima em um mapa topográfico requer uma função de custo com variáveis de entrada de longitude (x) e latitude (y):
chromosome = [x,y]
onde Nvar = 2. Cada cromossomo tem um custo encontrado avaliando a função de custo, f( ), em p1,
p2, ... , pNvar:
custo = f(chromosome) = f(p1, p2, ... , pNvar)
Como estamos tentando encontrar o pico no Parque Nacional das Montanhas Rochosas, a função de custo é escrita como a negativa da elevação para colocá-la na forma de um algoritmo de minimização:
f(x,y) = - elevation(x,y)
A maioria dos problemas de otimização exige restrições ou limites de variáveis. Por exemplo, permitir que o peso do carro vá para zero ou deixe a largura do carro de 10 metros são valores variáveis impraticáveis. Variáveis irrestritas podem ter qualquer valor.
Variáveis podem ser restritas de três formas. Primeiro, limites rígidos na forma de>, <, ≥ e ≤ podem ser impostos às variáveis. Quando uma variável excede um limite, ela é definida como igual a esse limite. Se x tiver limites de 0 < x < 10 e o algoritmo atribuir x = 11, x será reatribuído ao valor de 10.
Segundo, as variáveis podem ser transformadas em novas variáveis que inerentemente incluem as restrições. Se x tem limites de 0 < x < 10, então x = 5 sin y + 5 é uma transformação entre a variável restrita x e a variável irrestrita y. Variando y para qualquer valor é o mesmo que variando x dentro de seus limites. Esse tipo de transformação altera um problema de otimização restrita em um problema de otimização irrestrito de maneira suave.
Finalmente, pode haver um conjunto finito de valores variáveis a partir do qual escolher, e todos os valores estão dentro da região de interesse. Tais problemas vêm na forma de selecionar peças de um suprimento limitado.
Variáveis dependentes apresentam problemas especiais para algoritmos de otimização porque variar uma variável também altera o valor da outra variável. Por exemplo, tamanho e peso do carro são dependentes. Aumentar o tamanho do carro provavelmente aumentará o peso também (a menos que outro fator, como o tipo de material, também seja alterado). Variáveis independentes, como os coeficientes da série de Fourier, não interagem entre si. Se 10 coeficientes não são suficientes para representar uma função, então mais pode ser adicionado sem ter que recalcular os 10 originais. Codificação e Decodificação de Variáveis
Como os valores das variáveis são representados em binário, deve haver uma maneira de converter valores contínuos em binário e vice-versa. A quantização amostra um intervalo contínuo de valores e categoriza as amostras em subfaixas não sobrepostas. Em seguida, um valor discreto exclusivo é atribuído a cada subintervalo. A diferença entre o valor real da função e o nível de quantização é conhecida como o erro de quantização.
A figura abaixo é um exemplo da quantização de uma função de Bessel (J0(x)) usando 4 bits.
A quantização começa por amostrar uma função e colocar as amostras em níveis de quantização iguais (figura abaixo). Qualquer valor que esteja dentro de um dos níveis é igual ao valor médio, alto ou baixo desse nível. Em geral, definir o valor para o valor médio do nível de quantização é melhor, porque o maior erro possível é meio nível. Arredondando o valor para o valor baixo ou alto de o nível permite um erro máximo igual ao nível de quantização. As fórmulas matemáticas para a codificação e decodificação binárias da n-ésima variável, pn, são dadas como segue:
Para a codificação: pnorm= pn−plo phi−plo gene[m]=round
{
pnorm−2−m −∑
p =1 m−1 gene[ p]2−p}
Para a decodificação: pquant=
∑
m=1 Ngene gene [m]2−m +2−(M +1) qn=pquant(phi−plo)+plo onde:• pnorm: variável normalizada (0 < pnorm < 1);
• plo: menor valor da variável;
• phi: maior valor da variável;
• gene[m]: versão binária de pn;
• round{ }: arredonda para o inteiro mais próximo; • pquant: versão quantizada de pnorm;
• qn: versão quantizada de pn;
O GA binário trabalha com bits. A variável x tem um valor representado por uma cadeia de bits que é o gene de comprimento Ngene. Por exemplo, se Ngene = 2 e x tiver limites definidos por 1 < x < 4,
então um gene com 2 bits tem 2Ngene = 4 valores possíveis. Os bits podem representar um número
inteiro decimal, valores quantizados ou valores qualitativos. O valor quantificado do gene ou variável é matematicamente encontrado multiplicando-se o vetor contendo os bits por um vetor contendo os níveis de quantização:
qn=gene×Q
T
onde:
• gene = [b1 b2 … bNgene];
• Ngene: número de bits em um gene;
• bn: bit binário (0 ou 1)
• Q: vetor de quantização = [2-1 2-2 … 2Ngene] • QT: transposta de Q
Aumentar o número de bits diminui o erro de quantização. A representação binária pode corresponder a um valor não-numérico qualitativo, como uma cor ou opinião que foi previamente
definida pela representação binária.
O GA trabalha com as codificações binárias, mas a função custo geralmente requer variáveis contínuas. Sempre que a função custo é avaliada, o cromossomo deve primeiro ser decodificado usando as equações anteriores.
Um exemplo de um cromossomo binário codificado que possui Nvar variáveis, cada uma codificada
com Ngene = 10 bits, é:
Substituindo cada gene desse cromossomo na equação qn=pquant(phi−plo)+plo acarreta em um
array da versão quantizada das variáveis. Esse cromosso tem um total de Nbits = Ngene x Nvar = 10 x
Nvar bits.
Para o exemplo do Parque, o mapa topográfico possui 128 x 128 pontos amostrados em uma área limitada (latitude e longitude), ilustrado na figura abaixo. O objetivo é achar o ponto mais alto (Long’s Peak).
Os possíveis valores de coordenadas são então codificadas em número binário, conforme a tabela abaixo. Se x e y são codificados em dois genes, cada um com Ngene = 7 bits, então existem 27
Por exemplo, um cromossomo que traduz a latitude 40º15’29.7” e longitude 105º36’50” (matricialmente [99,25]) tem a seguinte representação binária:
A População
O GA começa com um grupo de cromossomos conhecido como população. A população tem Npop
cromossomos e é uma matriz Npop x Nbits preenchida com 0s e 1s aleatórios. O valor da função custo
(altitude) de cada cromossomo é então avaliado. A tabela abaixo mostra um exemplo de uma população de 8 cromossomos (indivíduos) e seus respectivos custos.
Para ilustrar a geração de natureza aleatória da população, a figura abaixo mostra as coordenadas de cada um dos cromossomos da tabela acima. Durante a otimização, os valores reais da longitude e da latitude não precisam ser calculados. A decodificação é necessária apenas para interpretar os resultados no final.
Seleção Natural
Sobrevivência do mais apto se traduz em descartar os cromossomos com o maior custo. Primeiro, os custos dos Npop cromossomos associados são ordenados do menor custo para o maior custo.
Então, apenas os melhores são selecionados para continuar, enquanto o restante é excluído. A taxa de seleção, Xrate, é a fração de Npop que sobrevive para a próxima etapa de acasalamento. O número
de cromossomos que são mantidos em cada geração é Nkeep = XrateNpop.
A seleção natural ocorre a cada geração ou iteração do algoritmo. Dos cromossomos Npop em uma
geração, apenas os Nkeep superiores sobrevivem para o acasalamento, e os piores Npop – Nkeep são
descartados para dar lugar à nova prole.
Decidir quantos cromossomos manter é algo arbitrário. Deixar apenas alguns cromossomos sobreviverem até a próxima geração limita os genes disponíveis na prole. Manter muitos cromossomos permite que indivíduos de mau desempenho tenham a chance de contribuir com suas características para a próxima geração.
Frequentemente é utilizada uma taxa de 50% (Xrate = 0,5) no processo de seleção natural. Em nosso
exemplo, Npop = 8, com uma taxa de seleção de 50%, Nkeep = 4. Os resultados da seleção natural são
mostrados na tabela abaixo. Note que os cromossomos da tabela foram primeiramente ordenados por custo. Então, os quatro com o menor custo sobrevivem até a próxima geração e se tornam pais em potencial.
Outra abordagem para a seleção natural é chamada de limiar (thresholding). Nesta abordagem, todos os cromossomos que têm um custo menor do que um certo limiar sobrevivem. O limiar deve permitir que alguns cromossomos continuem para que os pais produzam descendentes. Caso contrário, toda uma nova população deve ser gerada para encontrar alguns cromossomos que passam no teste. No início, apenas alguns cromossomos podem sobreviver. Nas gerações posteriores, no entanto, a maioria dos cromossomos sobreviverá, a menos que o limiar seja alterado. Uma característica atraente dessa técnica é que a população não precisa ser classificada.
Seleção dos Pais
Dois cromossomos são selecionados a partir do conjunto de acasalamento dos cromossomos Nkeep
para produzir dois novos filhos. O pareamento ocorre na população de acasalamento até que nasçam os Npop - Nkeep para substituir os cromossomos descartados. O pareamento dos cromossomos em um
GA pode ser variado, as principais são:
1- Pareamento de cima para baixo. Comece no topo da lista e pareie os cromossomos dois de cada vez até que os cromossomos Nkeep sejam selecionados para o acasalamento. Assim, o algoritmo
combina linhas ímpares com linhas pares. A mãe tem índices na matriz da população dada por ma = 1, 3, 5, ... e o pai tem os números de linha pa = 2, 4, 6, ... Essa abordagem não modela bem a natureza, mas é muito simples de programar. É bom para iniciantes tentarem.
2- Pareamento aleatório. Essa abordagem usa um gerador de números aleatórios uniforme para selecionar cromossomos.
3- Pareamento aleatório ponderado. É atribuído a cada um dos cromossomos uma probabilidade de ser escolhido para acasalar, com probabilidade inversamente proporcional ao seu custo. Um cromossomo de menor custo tem maior probabilidade de acasalamento e vice-versa. Um número aleatório determina qual cromossomo será selecionado. Este tipo de ponderação é frequentemente referido como ponderação de roleta. Existem duas técnicas: ponderação por ordem (rank) e ponderação de custo.
a) Ponderação por ordem. Essa abordagem é independente do problema e encontra a probabilidade da orden n do cromossomo:
Pn=Nkeep−n+1
∑
n=1 Nkeep
n
A tabela abaixo mostra os resultados para os Nkeep = 4 cromossomos do nosso exemplo.
As probabilidades cumulativas listadas na coluna 4 são usadas na seleção do cromossomo. Um número aleatório entre zero e um é gerado. Começando no topo da lista, o primeiro cromossomo com uma probabilidade cumulativa maior que o número aleatório é selecionado para o conjunto de cruzamentos. Por exemplo, se o número aleatório for r = 0,577, então 0,4 < r < 0,7, logo o cromossomo 2 será selecionado. Se um cromossomo for pareado com ele mesmo, existem várias alternativas.
Primeiro, realize o acasalamento assim mesmo, isso significa que haverá três desses cromossomos na próxima geração. Em segundo lugar, escolha aleatoriamente outro cromossomo. A aleatoriedade nesta abordagem é mais indicativa da natureza. Terceiro, escolha outro cromossomo usando a mesma técnica de pesagem.
A ponderação por ordem é apenas um pouco mais difícil de programar do que o pareamento de cima para baixo. Pequenas populações têm uma alta probabilidade de selecionar o mesmo cromossomo. As probabilidades só precisam ser calculadas uma vez, já que elas não mudam a cada geração.
b) Ponderação por custo. A probabilidade de seleção é calculada a partir do custo do cromossomo em vez de sua classificação na população. Um custo normalizado é calculado para cada cromossomo subtraindo-se o custo mais baixo dos cromossomos descartados (cNkeep+1) do custo de todos os cromossomos na lista de acasalamento:
Cn=cn−cN
keep+ 1
Pn é calculado a partir de:
Pn=
|
Cn∑
m Nkeep Cm|
Subtraindo cNkeep+1 garante que todos os custos sejam negativos. A tabela abaixo lista os
custos normalizados, assumindo que cNkeep+1 = -12097.
Esta abordagem tende a pesar mais o cromossomo superior quando há um grande espalhamento no custo entre o cromossomo superior e inferior. Por outro lado, tende a pesar os cromossomos de maneira uniforme quando todos os cromossomos têm aproximadamente o mesmo custo. Os mesmos problemas se aplicam como discutido acima, se um cromossomo for selecionado para acasalar consigo mesmo.
As probabilidades devem ser recalculadas a cada geração.
4- Seleção por torneio. Outra abordagem que imita a competição por acasalamento na natureza é escolher aleatoriamente um pequeno subconjunto de cromossomos (dois ou três) da sub-população de acasalamento e o cromossomo com o menor custo nesse subconjunto se torna pai. O torneio se repete para cada pai necessário.
Pareamento por limiar e seleção por torneio fazem bom par, porque a população nunca precisa ser ordenada. A seleção por torneio funciona melhor para populações maiores porque o ordenamento se torna demorada para grandes populações.
Cada um dos esquemas de seleção resulta em um conjunto diferente de pais. Como tal, a composição da próxima geração é diferente para cada esquema de seleção. Roleta e seleção por torneio são padrão para a maioria dos GAs.
É muito difícil dar conselhos sobre qual esquema de ponderação funciona melhor. A figura abaixo mostra a probabilidade de seleção para cinco métodos de seleção.
A seleção uniforme tem uma probabilidade constante para cada um dos oito pais. Seleção ponderada e seleção por torneio com dois cromossomos têm aproximadamente as mesmas probabilidades para os oito pais. A pressão de seleção é a razão entre a probabilidade de o cromossomo mais adequado ser selecionado como pai e a probabilidade de que o cromossomo médio seja selecionado.
Acasalamento
Acasalamento é a criação de um ou mais descendentes dos pais selecionados no processo de pareamento. A composição genética da população é limitada pelos os atuais membros da população. A forma mais comum de acasalamento envolve dois pais que produzem dois descendentes. Um ponto de cruzamento (cross-over) é selecionado aleatoriamente entre o primeiro e o último bits dos cromossomos dos pais.
Primeiro, o pai passa seu código binário para a esquerda desse ponto de cruzamento para o filho1.
De maneira semelhante, a mãe passa seu código binário para a esquerda do mesmo ponto de cruzamento para o filho2. Em seguida, o código binário à direita do ponto de cruzamento do pai vai
para o filho2 e a mãe passa seu código para o filho1. Consequentemente, os filhos contêm partes dos
Os pais produziram um total de Npop - Nkeep filhos, então a população de cromossomos agora está de
volta a Npop. A tabela abaixo mostra o processo de pareamento e acasalamento para o exemplo do
Parque. O primeiro conjunto de pais é formado pelos cromossomos 3 e 2, e tem um ponto de cruzamento entre os bits 5 e 6. O segundo grupo de pais é formado pelos cromossomos 3 e 4 e tem um ponto de cruzamento entre os bits 10 e 11. Esse processo é conhecido como cross-over simples ou de ponto único.
Mutações
Mutações aleatórias alteram uma certa porcentagem dos bits na lista de cromossomos. A mutação é a segunda maneira pela qual um GA explora uma superfície de custo. Ele pode introduzir características que não estão na população original e impede que o GA converja muito rápido antes de amostrar toda a superfície de custo.
Uma única mutação de ponto altera um 1 para um 0 e vice-versa. Os pontos de mutação são selecionados aleatoriamente a partir do número total de bits Npop x Nbits na matriz da população.
Aumentar o número de mutações aumenta a liberdade do algoritmo de pesquisar fora da região atual do espaço variável. Ele também tende a desviar o algoritmo de uma solução popular. Mutações não ocorrem na iteração final.
Geralmente também não é permitida mutação na melhor solução, ou seja, no indivíduo com menor custo. Eles são designados como soluções de elite destinadas a propagar-se inalteradas. Esse elitismo é muito comum em GAs (Por que jogar fora uma resposta perfeitamente boa?).
Para o problema do Parque Nacional das Montanhas Rochosas, podemos optar por mutar 20% da população (μ = 0,20), exceto pelo melhor cromossomo. Assim, um gerador de números aleatórios gera os índices do indivíduo e dos bits a serem mutados.
Neste caso, o número de mutações é dado por:
#mutações = μ x (Npop – 1) x Nbits
No nosso exemplo, #mutações = 0.2 x 7 x 14 = 19.6 ≈ 20. A partir de 20 números aleatórios dos pares (indíce do indivíduo, índice do bit), um possível resultado seria o mostrado abaixo, onde os bits mutados estão em itálico.
Próxima Geração
Depois que as mutações ocorrem, os custos associados aos cromossomos descendentes e mutados são calculados. Dessa forma, tem-se uma nova população, que na terminologia de GA é chamada de uma nova geração, considerada mais evoluída. O processo descrito é repetido iterativamente, promovendo a evolução da população.
Para nosso exemplo, a população inicial da próxima geração, proveniente da tabela anterior, é mostrada na tabela abaixo após o ordenamento.
Os quatro cromossomos inferiores são descartados e substituídos pelos descendentes dos quatro pais principais. Outros 20 bits aleatórios são selecionados para mutação dos 7 cromossomos inferiores. A população no final da geração 2 é mostrada na tabela abaixo, com a posição no mapa dessa geração ilustrada na figura seguinte.
Convergência
O número de gerações que evoluem depende de uma solução aceitável ser atingida ou de um número definido de iterações ser excedido. Depois de algum tempo, todos os cromossomos e custos associados se tornariam iguais se não fossem as mutações. Neste ponto, o algoritmo deve ser parado.
A maioria dos GAs mantém o controle das estatísticas da população na forma de média populacional e custo mínimo. Para o nosso exemplo, após três gerações, o mínimo global é encontrado em -14199. Este mínimo foi encontrado verificando a função custo 29 vezes:
8
⏟
populaçãoinicial
+ 7
⏟
verificações por geração
× 3
⏟
número de gerações
= 29
Em outras palavras, de todo o universo de soluções, que envolvem 128 x 128 possibilidades, apenas 29 foram verificadas, o que representa 0,18% do universo.
A figura e tabela abaixo ilustram a população final, mostrando 4 indivíduos perto da solução. O Long’s Peak está, na verdade, a 14.255 pés acima do nível do mar, mas o erro de quantização produziu um máximo de 14.199.