遗传算法与深度学习实战(23)——利用遗传算法优化深度学习模型
- 0. 前言
- 1. 神经进化
- 2. 使用遗传算法作为深度学习优化器
- 小结
- 系列链接
0. 前言
神经进化涵盖了所有用于改进深度学习的进化算法。更具体地说,神经进化用来定义应用于深度学习的特定优化模式。我们已经学习了如何将进化算法应用于超参数优化,并使用 Numpy 实现多层感知器 (multi-layer perceptron, MLP) 模型,接下来,我们使用遗传算法进行模型优化。
1. 神经进化
神经进化包括超参数优化、参数优化(权重/参数搜索)和网络优化技术。在本节中,我们将深入探讨如何应用进化方法来直接优化网络参数,从而消除通过网络进行的损失反向传播。
神经进化通常用于改进单个深度学习网络模型,也存在其他将进化应用于深度学习的方法,可以扩大搜索范围到多个模型。
2. 使用遗传算法作为深度学习优化器
在本节中,我们将多层感知器 (multi-layer perceptron
, MLP
) 模型中使用的深度学习 (Deep learning
, DL
) 优化方法从反向传播替换为神经进化优化。因此,我们完全依赖于遗传算法,而不使用任何形式的反向传播优化器(如梯度下降或 Adam
)。
接下来,我们使用多层感知器 (multi-layer perceptron, MLP) 中一节中的 MLP
网络作为基本网络模型,然后使用 DEAP
实现遗传算法将训练优化过程包装起来。
(1) 首先,导入所需库,并加载数据集:
import numpy as np
import sklearn
import sklearn.datasets
import sklearn.linear_model
import matplotlib.pyplot as plt
from IPython.display import clear_output
from deap import algorithms
from deap import base
from deap import benchmarks
from deap import creator
from deap import tools
import random
number_samples = 100 #@param {type:"slider", min:100, max:1000, step:25}
difficulty = 1 #@param {type:"slider", min:1, max:5, step:1}
problem = "circles" #@param ["classification", "blobs", "gaussian quantiles", "moons", "circles"]
number_features = 2
number_classes = 2
middle_layer = 5 #@param {type:"slider", min:5, max:25, step:1}
def load_data(problem):
if problem == "classification":
clusters = 1 if difficulty < 3 else 2
informs = 1 if difficulty < 4 else 2
data = sklearn.datasets.make_classification(
n_samples = number_samples,
n_features=number_features,
n_redundant=0,
class_sep=1/difficulty,
n_informative=informs,
n_clusters_per_class=clusters)
if problem == "blobs":
data = sklearn.datasets.make_blobs(
n_samples = number_samples,
n_features=number_features,
centers=number_classes,
cluster_std = difficulty)
if problem == "gaussian quantiles":
data = sklearn.datasets.make_gaussian_quantiles(mean=None,
cov=difficulty,
n_samples=number_samples,
n_features=number_features,
n_classes=number_classes,
shuffle=True,
random_state=None)
if problem == "moons":
data = sklearn.datasets.make_moons(
n_samples = number_samples)
if problem == "circles":
data = sklearn.datasets.make_circles(
n_samples = number_samples)
return data
data = load_data(problem)
X, Y = data
plt.figure("Input Data")
plt.scatter(X[:, 0], X[:, 1], c=Y, s=40, cmap=plt.cm.Spectral)
(2) 作为基线,比较 sklearn
的简单逻辑回归(分类)模型:
def show_predictions(model, X, Y, name=""):
""" display the labeled data X and a surface of prediction of model """
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01), np.arange(y_min, y_max, 0.01))
X_temp = np.c_[xx.flatten(), yy.flatten()]
Z = model.predict(X_temp)
plt.figure("Predictions " + name)
plt.contourf(xx, yy, Z.reshape(xx.shape), cmap=plt.cm.Spectral)
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(X[:, 0], X[:, 1],c=Y, s=40, cmap=plt.cm.Spectral)
clf = sklearn.linear_model.LogisticRegressionCV()
clf.fit(X, Y)
show_predictions(clf, X, Y, "Logistic regression")
LR_predictions = clf.predict(X)
print("Logistic Regression accuracy : ", np.sum(LR_predictions == Y) / Y.shape[0])
(3) 实现 MLP
网络模型,用 Neural_Network
类中的 set_parameters
函数替换 train
和 back_prop
函数:
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
## Neural Network
class Neural_Network:
def __init__(self, n_in, n_hidden, n_out):
# Network dimensions
self.n_x = n_in
self.n_h = n_hidden
self.n_y = n_out
# Parameters initialization
self.W1 = np.random.randn(self.n_h, self.n_x) * 0.01
self.b1 = np.zeros((self.n_h, 1))
self.W2 = np.random.randn(self.n_y, self.n_h) * 0.01
self.b2 = np.zeros((self.n_y, 1))
self.parameters = [self.W1, self.b1, self.W2, self.b2]
def forward(self, X):
""" Forward computation """
self.Z1 = self.W1.dot(X.T) + self.b1
self.A1 = np.tanh(self.Z1)
self.Z2 = self.W2.dot(self.A1) + self.b2
self.A2 = sigmoid(self.Z2)
def set_parameters(self, individual):
"""Sets model parameters """
idx = 0
for p in self.parameters:
size = p.size
sh = p.shape
t = individual[idx:idx+size]
t = np.array(t)
t = np.reshape(t, sh)
p -= p
p += t
idx += size
def predict(self, X):
""" Compute predictions with just a forward pass """
self.forward(X)
return np.round(self.A2).astype(np.int)
循环遍历模型中的参数列表,得到参数列表大小和形状,然后从个体中提取相同数量的基因。然后,构造一个新的张量并重新调整其形状以匹配原始的参数/权重张量。将原始张量与自身相减以将其归零并保持引用,然后添加新的张量。实际上,我们将个体的基因序列部分交换到张量中,然后将其作为模型内的新权重进行替换。
(4) 由于 train
和 back_prop
函数已经被完全移除,因此网络无法执行任何形式的常规反向传播训练。set_parameters
函数设置模型的权重/参数,我们使用遗传算法 (Genetic Algorithms, GA) 来搜索这些值。接下来,实例化 MLP
网络,将所有参数设置为 1.0
,输出结果如下所示:
nn = Neural_Network(2, middle_layer, 1)
number_of_genes = sum([p.size for p in nn.parameters])
print(number_of_genes)
individual = np.ones(number_of_genes)
nn.set_parameters(individual)
print(nn.parameters)
show_predictions(nn, X, Y, "Neural Network")
nn_predictions = nn.predict(X)
print("Neural Network accuracy : ", np.sum(nn_predictions == Y) / Y.shape[0])
(5) 上图显示了在所有权重/参数设置为 1.0
的情况下模型的预测输出。接下来,实现 GA
算法优化网络参数:
creator.create("FitnessMax", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)
def uniform(low, up, size=None):
try:
return [random.uniform(a, b) for a, b in zip(low, up)]
except TypeError:
return [random.uniform(a, b) for a, b in zip([low] * size, [up] * size)]
toolbox = base.Toolbox()
toolbox.register("attr_float", uniform, -1, 1, number_of_genes)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.attr_float)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("select", tools.selTournament, tournsize=5)
toolbox.register("mate", tools.cxBlend, alpha=.5)
toolbox.register("mutate", tools.mutGaussian, mu=0.0, sigma=.1, indpb=.25)
(6) 实现评估函数 evaluate()
,函数返回准确率的倒数。这样我们就可以通过最小化适应度,从而最大化进化过程中个体的准确率:
def evaluate(individual):
nn.set_parameters(individual)
nn_predictions = nn.predict(X)
return 1/np.sum(nn_predictions == Y) / Y.shape[0],
toolbox.register("evaluate", evaluate)
(7) 最后,演化种群以优化模型,使用 eaSimple()
函数训练种群。然后,比较最后一代种群的一个样本个体和当前最佳个体。通过使用提前停止,在模型性能达到提前停止条件(如果准确率达到某个值)时,停止模型优化过程。通过检查提前停止条件,代码可以在找到可接受的解决方案时立即停止:
MU = 1000 #@param {type:"slider", min:5, max:1000, step:5}
NGEN = 100 #@param {type:"slider", min:100, max:1000, step:10}
RGEN = 10 #@param {type:"slider", min:1, max:100, step:1}
CXPB = .6
MUTPB = .3
random.seed(64)
pop = toolbox.population(n=MU)
hof = tools.HallOfFame(1)
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", np.mean)
stats.register("std", np.std)
stats.register("min", np.min)
stats.register("max", np.max)
best = None
history = []
for g in range(NGEN):
pop, logbook = algorithms.eaSimple(pop, toolbox,
cxpb=CXPB, mutpb=MUTPB, ngen=RGEN, stats=stats, halloffame=hof, verbose=False)
best = hof[0]
clear_output()
print(f"Gen ({(g+1)*RGEN})")
show_predictions(nn, X, Y, "Neural Network")
nn_predictions = nn.predict(X)
print("Current Neural Network accuracy : ", np.sum(nn_predictions == Y) / Y.shape[0])
plt.show()
nn.set_parameters(best)
show_predictions(nn, X, Y, "Best Neural Network")
plt.show()
nn_predictions = nn.predict(X)
fitness = np.sum(nn_predictions == Y) / Y.shape[0]
print("Best Neural Network accuracy : ", fitness)
if fitness > .99999: #stop condition
break
在下图中可以看到,种群演化已经演化为能够以 100%
准确率解决圆圈问题。而使用同样的 MLP 网络进行反向传播训练,在该问题上仅有 50%
的准确率。
我们也可以使用 GA
来探索其他问题数据集,并比较该方法与简单的反向传播和梯度下降优化。可以通过完成以下问题进一步了解神经进化优化的工作原理:
- 增加或减少样本数量,然后重新运行代码
- 改变交叉和突变率,然后重新运行代码
- 增加或减少中间层的大小,然后重新运行代码
小结
神经进化用来定义应用于深度学习的特定优化模式。在本节中,我们通过遗传算法优化简单 DL
网络的权重/参数,替换在误差反向传播训练过程中的所用优化器。
系列链接
遗传算法与深度学习实战(1)——进化深度学习
遗传算法与深度学习实战(2)——生命模拟及其应用
遗传算法与深度学习实战(3)——生命模拟与进化论
遗传算法与深度学习实战(4)——遗传算法(Genetic Algorithm)详解与实现
遗传算法与深度学习实战(5)——遗传算法中常用遗传算子
遗传算法与深度学习实战(6)——遗传算法框架DEAP
遗传算法与深度学习实战(7)——DEAP框架初体验
遗传算法与深度学习实战(8)——使用遗传算法解决N皇后问题
遗传算法与深度学习实战(9)——使用遗传算法解决旅行商问题
遗传算法与深度学习实战(10)——使用遗传算法重建图像
遗传算法与深度学习实战(11)——遗传编程详解与实现
遗传算法与深度学习实战(12)——粒子群优化详解与实现
遗传算法与深度学习实战(13)——协同进化详解与实现
遗传算法与深度学习实战(14)——进化策略详解与实现
遗传算法与深度学习实战(15)——差分进化详解与实现
遗传算法与深度学习实战(16)——神经网络超参数优化
遗传算法与深度学习实战(17)——使用随机搜索自动超参数优化
遗传算法与深度学习实战(18)——使用网格搜索自动超参数优化
遗传算法与深度学习实战(19)——使用粒子群优化自动超参数优化
遗传算法与深度学习实战(20)——使用进化策略自动超参数优化
遗传算法与深度学习实战(21)——使用差分搜索自动超参数优化
遗传算法与深度学习实战(22)——使用Numpy构建神经网络