Genetic Algorithm Optimization

In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('seaborn-talk')
from sklearn.datasets import load_iris
from sklearn.linear_model import Perceptron
In [2]:
iris = load_iris()
iris.feature_names[:2], iris.target_names[:2]
Out[2]:
(['sepal length (cm)', 'sepal width (cm)'],
 array(['setosa', 'versicolor'], dtype='<U10'))
In [3]:
idx = (iris.target == 0) | (iris.target == 1)
X = iris.data[idx, :2]
y = iris.target[idx]

fig, ax = plt.subplots(figsize=(12, 7))
ax.scatter(X[y == 0, 0], X[y == 0, 1], c='navy', label='Iris Setosa')
ax.scatter(X[y == 1, 0], X[y == 1, 1], c='crimson', label='Iris Versicolor')
ax.set(xlabel='Sepal Length', ylabel='Sepal Width')
ax.legend();
In [4]:
model = Perceptron()
model.fit(X, y)
Out[4]:
Perceptron(alpha=0.0001, class_weight=None, early_stopping=False, eta0=1.0,
           fit_intercept=True, max_iter=1000, n_iter_no_change=5, n_jobs=None,
           penalty=None, random_state=0, shuffle=True, tol=0.001,
           validation_fraction=0.1, verbose=0, warm_start=False)
In [5]:
gx = np.linspace(min(X[:, 0]), max(X[:, 0]), 128)
gy = np.linspace(min(X[:, 1]), max(X[:, 1]), 128)
gx, gy = np.meshgrid(gx, gy)
g_X = np.c_[gx.reshape(-1), gy.reshape(-1)]
g_y = model.predict(g_X)

fig, ax = plt.subplots(figsize=(12, 7))
ax.plot(g_X[g_y == 0, 0], g_X[g_y == 0, 1], '.', c='navy', alpha=.1)
ax.plot(g_X[g_y == 1, 0], g_X[g_y == 1, 1], '.', c='crimson', alpha=.1)
ax.scatter(X[y == 0, 0], X[y == 0, 1], c='navy', label='Iris Setosa')
ax.scatter(X[y == 1, 0], X[y == 1, 1], c='crimson', label='Iris Versicolor')
ax.set(xlabel='Sepal Length', ylabel='Sepal Width')
ax.legend();
In [6]:
model.coef_, model.intercept_
Out[6]:
(array([[ 23.2, -38.7]]), array([-5.]))
In [7]:
def hardlim(x):
    return np.heaviside(x, 0)

g_y = model.coef_ @ g_X.T + model.intercept_
g_y = hardlim(g_y).reshape(-1)

fig, ax = plt.subplots(figsize=(12, 7))
ax.plot(g_X[g_y == 0, 0], g_X[g_y == 0, 1], '.', c='navy', alpha=.1)
ax.plot(g_X[g_y == 1, 0], g_X[g_y == 1, 1], '.', c='crimson', alpha=.1)
ax.scatter(X[y == 0, 0], X[y == 0, 1], c='navy', label='Iris Setosa')
ax.scatter(X[y == 1, 0], X[y == 1, 1], c='crimson', label='Iris Versicolor')
ax.set(xlabel='Sepal Length', ylabel='Sepal Width')
ax.legend();
In [8]:
class GenPerceptron:
    def __init__(self, ch):
        self.coef_ = np.array([[ch[0], ch[1]]]).copy()
        self.intercept_ = np.array([ch[2]]).copy()

    def predict(self, X):
        y_hat = self.coef_ @ X.T + self.intercept_
        return np.heaviside(y_hat, 0).reshape(-1)
In [9]:
def train_gen_perceptron(chs, n, X, y):
    model = GenPerceptron(chs[n, :])
    # we return the unchanged numbers
    # but a different function could change them
    ch = np.c_[model.coef_, model.intercept_]
    y_hat = model.predict(X)
    mse = ((y - y_hat)**2).mean()
    return model, mse, ch
In [10]:
def gen_eval(chs, X, y, fun=None):
    err = []
    if fun is None:
        fun = train_gen_perceptron
    for n in range(chs.shape[0]):
        model, mse, ch = fun(chs, n, X, y)
        chs[n, :] = ch  # update "unchanged" numbers
        err.append(mse)
    return err
In [11]:
def gen_best_k(chs, err, k):
    idx = np.argpartition(err, k)[:k]
    return chs[idx, :].copy()
In [12]:
def gen_reproduce(chs, npop=30, mutation=0.01):
    pop_idx_f = np.random.choice(np.arange(chs.shape[0]), npop)
    pop_idx_m = np.random.choice(np.arange(chs.shape[0]), npop)
    pop_f = chs[pop_idx_f, :].copy()
    pop_m = chs[pop_idx_m, :].copy()

    cross = np.random.choice([False, True], pop_f.shape)
    pop = np.where(cross, pop_f, pop_m)

    mutation = np.random.rand(*pop_f.shape) < mutation
    pop = np.where(mutation, pop*np.random.rand(), pop)

    #print(pop.shape, chs.shape)
    #pop = np.vstack((pop, chs))
    return pop
In [13]:
def train(X, y, npop=30, k=3, n_iter=10, fun=None):
    err, best = None, None
    pop = np.random.normal(0, 7, (npop, 3))
    for i in range(n_iter):
        err = gen_eval(pop, X, y, fun=fun)
        best = gen_best_k(pop, err, k)
        pop = gen_reproduce(best, npop, mutation=0.1)
        print('iter', i, 'err', min(err))
    err_best = gen_eval(best, X, y)
    return best, err_best
In [14]:
best, err = train(X, y, npop=60, n_iter=10)
one_best_idx = np.argsort(err)[0]
one_best = best[one_best_idx, :]
model = GenPerceptron(one_best.reshape(-1))
best, err
iter 0 err 0.02
iter 1 err 0.02
iter 2 err 0.02
iter 3 err 0.01
iter 4 err 0.01
iter 5 err 0.01
iter 6 err 0.01
iter 7 err 0.01
iter 8 err 0.01
iter 9 err 0.01
Out[14]:
(array([[ 3.9196341 , -5.77830173, -2.32107403],
        [ 3.9196341 , -5.77830173, -2.32107403],
        [ 3.9196341 , -5.77830173, -2.32107403]]),
 [0.01, 0.01, 0.01])
In [15]:
g_y = model.predict(g_X)

fig, ax = plt.subplots(figsize=(12, 7))
ax.plot(g_X[g_y == 0, 0], g_X[g_y == 0, 1], '.', c='navy', alpha=.1)
ax.plot(g_X[g_y == 1, 0], g_X[g_y == 1, 1], '.', c='crimson', alpha=.1)
ax.scatter(X[y == 0, 0], X[y == 0, 1], c='navy', label='Iris Setosa')
ax.scatter(X[y == 1, 0], X[y == 1, 1], c='crimson', label='Iris Versicolor')
ax.set(xlabel='Sepal Length', ylabel='Sepal Width')
ax.legend();
In [16]:
m2 = Perceptron()
m2.coef_ = np.array([[23.2, -38.7]])
m2.intercept_ = np.array([-5.])
m2.classes_ = np.array([0, 1])
m2.score(X, y)
Out[16]:
0.99

Neuro Evolution

In [17]:
import warnings
from sklearn.exceptions import ConvergenceWarning

# max_iter=1 makes the problem look harder
# but will give lots of warnings
warnings.filterwarnings('ignore', category=ConvergenceWarning)

def train_sklearn_perceptron(chs, n, X, y):
    model = Perceptron(warm_start=True, max_iter=1)
    model.coef_ = chs[n, :-1]
    model.intercept_ = chs[n, -1]
    model.fit(X, y)
    ch = np.c_[model.coef_, model.intercept_]
    y_hat = model.predict(X)
    mse = ((y - y_hat)**2).mean()
    return model, mse, ch
In [18]:
best, err = train(X, y, npop=10, n_iter=10, fun=train_sklearn_perceptron)
one_best_idx = np.argsort(err)[0]
one_best = best[one_best_idx, :]
model = GenPerceptron(one_best.reshape(-1))
best, err
iter 0 err 0.34
iter 1 err 0.05
iter 2 err 0.02
iter 3 err 0.02
iter 4 err 0.02
iter 5 err 0.09
iter 6 err 0.01
iter 7 err 0.07
iter 8 err 0.01
iter 9 err 0.01
Out[18]:
(array([[ 24.47543674, -38.60673805,  -6.27789424],
        [ 30.07543674, -48.97678791,  -5.27789424],
        [ 29.54124731, -48.37678791,  -4.27789424]]),
 [0.02, 0.01, 0.01])
In [19]:
g_y = model.predict(g_X)

fig, ax = plt.subplots(figsize=(12, 7))
ax.plot(g_X[g_y == 0, 0], g_X[g_y == 0, 1], '.', c='navy', alpha=.1)
ax.plot(g_X[g_y == 1, 0], g_X[g_y == 1, 1], '.', c='crimson', alpha=.1)
ax.scatter(X[y == 0, 0], X[y == 0, 1], c='navy', label='Iris Setosa')
ax.scatter(X[y == 1, 0], X[y == 1, 1], c='crimson', label='Iris Versicolor')
ax.set(xlabel='Sepal Length', ylabel='Sepal Width')
ax.legend();