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.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();