O que é programação genética?
A programação genética (PG) é uma técnica de inteligência computacional inspirada na evolução biológica. Diferentemente dos algoritmos genéticos, ela não otimiza parâmetros, mas sim programas completos. Cada indivíduo da população é uma estrutura de árvore representando uma expressão ou função. Essas árvores contêm operadores (como +, -, *, /) e terminais (variáveis ou constantes). O objetivo é evoluir automaticamente um programa que resolva uma tarefa específica. Portanto, a PG busca tanto a estrutura quanto os parâmetros do modelo. Ela é especialmente útil quando não conhecemos a forma analítica da solução. Assim, a máquina descobre a relação entre entradas e saídas por si só.
Como funciona o processo evolutivo?
Inicialmente, uma população aleatória de árvores é gerada de forma randômica. Cada árvore é avaliada por uma função de aptidão (fitness) que mede seu erro. Quanto menor o erro, melhor é considerado aquele programa individual. Os melhores indivíduos são selecionados para reprodução, usando torneio ou roleta. Operadores genéticos como crossover trocam subárvores entre dois pais promissores. A mutação altera aleatoriamente um nó ou subárvore de um único filho. Essas operações produzem uma nova geração com variações exploratórias. Esse ciclo se repete por muitas gerações até que um critério de parada seja atingido. A convergência ocorre quando a aptidão média da população estabiliza. Todo esse processo é guiado exclusivamente pelos dados de treinamento fornecidos.
Vantagens e aplicações práticas
A PG é poderosa porque dispensa modelagem matemática prévia por parte do usuário. Ela pode descobrir leis físicas, equações diferenciais ou classificadores complexos. Por exemplo, ela já foi usada para regressão simbólica e previsão de séries temporais. Além disso, a PG gera modelos interpretáveis, diferentemente das redes neurais profundas. Contudo, seu custo computacional é elevado para populações grandes ou muitas gerações. Ainda assim, ela é aplicada em robótica, finanças e bioinformática com sucesso. Sua flexibilidade permite que o usuário defina operadores e terminais personalizados. Dessa forma, ela se adapta a domínios muito específicos sem grandes ajustes.
A programação genética é uma extensão natural dos algoritmos genéticos clássicos. Enquanto aqueles trabalham com vetores de números, esta trabalha com árvores de sintaxe. Isso torna a PG mais expressiva, mas também mais difícil de ajustar. Por exemplo, o fenômeno da “bloat” (crescimento excessivo das árvores) pode ocorrer. Ele é controlado com penalidades no tamanho da árvore durante a avaliação. Outro cuidado importante é garantir que as árvores sejam sintaticamente válidas. Operações de crossover e mutação devem respeitar a gramática do problema. Muitas bibliotecas, como o DEAP em Python, já implementam essas verificações automaticamente. A escolha dos operadores (funções) e terminais é decisiva para o sucesso. Ela reflete o conhecimento prévio sobre o domínio do problema. Sem uma boa seleção, a busca pode ser ineficiente ou até divergente. Por isso, a experimentação é uma parte fundamental do trabalho com PG. Apesar dos desafios, a PG entrega soluções criativas que surpreendem os especialistas. Ela é, sem dúvida, uma ferramenta valiosa no kit do cientista de dados.
Um exemplo clássico é a regressão simbólica da função seno. Dados de entrada x no intervalo [0, 6] e saída y = sen(x) são fornecidos. O objetivo é evoluir uma expressão matemática que aproxime sen(x) sem conhecê-la. A aptidão é o erro quadrático médio entre a previsão e o valor real. Operadores permitidos: +, -, *, / e a função protetora de divisão. Terminais: x e constantes aleatórias entre -1 e 1. Após algumas gerações, a PG encontra uma boa aproximação polinomial ou trigonométrica. Esse problema ilustra a capacidade da PG de descobrir padrões subjacentes.
Enunciado do exemplo clássico
Utilize programação genética para aproximar a função y = sen(x) para x em [0, 6]. Gere 100 pontos de treinamento com ruído gaussiano de desvio 0.05. População com 50 indivíduos, profundidade máxima 4, crossover 0.8 e mutação 0.1. Execute por 30 gerações e armazene o melhor erro a cada geração. Ao final, plote a curva verdadeira, os dados ruidosos e a previsão do melhor programa. Plote também a evolução do erro ao longo das gerações. Forneça o código Python completo usando a biblioteca gplearn.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# Instale a biblioteca gplearn no Colab: !pip install gplearn import numpy as np import matplotlib.pyplot as plt from gplearn.genetic import SymbolicRegressor from gplearn.functions import make_function # Dados sintéticos com ruído np.random.seed(42) X = np.linspace(0, 6, 100).reshape(-1, 1) y = np.sin(X.ravel()) + np.random.normal(0, 0.05, 100) # Definição da função de aptidão (erro quadrático médio) - já é padrão no gplearn # Criamos um regressor simbólico com operadores básicos est = SymbolicRegressor( population_size=50, generations=30, stopping_criteria=0.01, p_crossover=0.8, p_subtree_mutation=0.1, p_hoist_mutation=0.05, p_point_mutation=0.05, max_samples=0.9, verbose=1, function_set=('add', 'sub', 'mul', 'div', 'sin', 'cos'), random_state=0 ) # Treinamento da PG est.fit(X, y) # Melhor expressão encontrada print("Melhor expressão encontrada:") print(est._program) # Previsões y_pred = est.predict(X) # Gráfico 1: Dados, seno real e previsão plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) plt.scatter(X, y, color='blue', s=10, label='Dados com ruído') plt.plot(X, np.sin(X), 'g--', linewidth=2, label='sen(x) real') plt.plot(X, y_pred, 'r-', linewidth=2, label='Previsão da PG') plt.title('Aproximação da Função Seno') plt.xlabel('x') plt.ylabel('y') plt.legend() plt.grid(True) # Gráfico 2: Evolução do erro (fitness) ao longo das gerações # O gplearn armazena o histórico no atributo .fitness_history_ fitness_hist = est.fitness_history_ erro_medio = [1 - f for f in fitness_hist] # converte fitness (R²) para erro plt.subplot(1, 2, 2) plt.plot(range(len(erro_medio)), erro_medio, 'b-', linewidth=2) plt.title('Evolução do Erro (MSE) por Geração') plt.xlabel('Geração') plt.ylabel('Erro Quadrático Médio') plt.grid(True) plt.tight_layout() plt.show() |
Este código é autoexplicativo e roda perfeitamente no Google Colab. Ele mostra a PG descobrindo uma expressão que se ajusta aos dados ruidosos. O primeiro gráfico compara a previsão com a função real e os pontos medidos. O segundo gráfico exibe a diminuição do erro ao longo das gerações. Mesmo sem conhecimento prévio, o usuário pode observar o poder da PG. A função de aptidão (erro) é minimizada internamente pelo regressor. Portanto, a programação genética automatiza a descoberta de modelos matemáticos. Ela é uma abordagem fascinante para problemas de regressão e classificação.