遗传算法与深度学习实战(21)——使用差分搜索自动超参数优化
遗传算法与深度学习实战(21)——使用差分搜索自动超参数优化
- 0. 前言
- 1. 差分搜索
- 2. 使用差分搜索自动超参数优化
- 小结
- 系列链接
0. 前言
我们已经学习了如何使用差分进化方法解决不连续解问题,鉴于差分进化使用演化解决方案的独特方法,因此是自动超参数优化 (Hyperparameter Optimization
, HPO
) 的一个很好选择,差分进化还可能克服粒子群优化 (Particle Swarm Optimization, PSO) 和进化策略 (Evolutionary Strategies, ES) 在自动超参数优化中的缺陷。在本节中,我们将利用差分进化进行自动 HPO
。
1. 差分搜索
差分进化 (Differential Evolution, DE) 采用了一个简单的迭代算法,从种群中随机抽样三个个体,然后,计算两个个体之间的差异,并将其加权值添加到第三个个体上,产生了第四个点,即下一个要搜索的区域。
下图描述了在二维空间中进行 DE
算法的一次评估过程。图中,从种群中随机抽取了三个点 (A
、B
、C
)。计算了从 A
到 B
(A - B
) 的差异向量,然后传递给缩放函数 F
。在大多数情况下,我们简单将其乘以加权值 1.0
。然后,我们将加权后的差异向量添加到第三个点上,创建一个新的目标搜索点。
2. 使用差分搜索自动超参数优化
差分进化 (Differential Evolution
, DE
) 使用的搜索机制具有足够的灵活,可以摆脱粒子群优化 (Particle Swarm Optimization, PSO) 和进化策略 (Evolutionary Strategies, ES) 算法中集群问题。接下来,使用 DE
解决四个超参数的最优化问题,以观察 DE
的实际效果。
(1) 首先,导入所需库,并定义相关超参数和网络模型:
import numpy as np
from deap import algorithms
from deap import base
from deap import benchmarks
from deap import creator
from deap import tools
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
from sklearn.decomposition import PCA
from matplotlib import pyplot as plt
from matplotlib import cm
from IPython.display import clear_output
import random
import math
import array
import timedef function(x):return (2*x + 3*x**2 + 4*x**3 + 5*x**4 + 6*x**5 + 10) data_min = -5
data_max = 5
data_step = .5
Xi = np.reshape(np.arange(data_min, data_max, data_step), (-1, 1))
yi = function(Xi)
inputs = Xi.shape[1]
yi = yi.reshape(-1, 1)
plt.plot(Xi, yi, 'o', color='black')
plt.plot(Xi,yi, color="red")class Net(nn.Module):def __init__(self, inputs, middle):super().__init__()self.fc1 = nn.Linear(inputs,middle) self.fc2 = nn.Linear(middle,middle) self.out = nn.Linear(middle,1)def forward(self, x):x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.out(x)return xclass HyperparametersEC(object):def __init__(self, **kwargs):self.__dict__.update(kwargs) self.hparms = [d for d in self.__dict__] def __str__(self):out = ""for d in self.hparms:ds = self.__dict__[d]out += f"{d} = {ds} "return out def values(self):vals = []for d in self.hparms:vals.append(self.__dict__[d])return valsdef size(self):return len(self.hparms)def next(self, individual):dict = {}#initialize generatorsfor i, d in enumerate(self.hparms):next(self.__dict__[d])for i, d in enumerate(self.hparms): dict[d] = self.__dict__[d].send(individual[i])return HyperparametersEC(**dict) def clamp(num, min_value, max_value):return max(min(num, max_value), min_value)def linespace(min,max):rnge = max - minwhile True:i = yieldi = (clamp(i, -1.0, 1.0) + 1.0) / 2.0yield i * rnge + mindef linespace_int(min,max):rnge = max - minwhile True:i = yieldi = (clamp(i, -1.0, 1.0) + 1.0) / 2.0yield int(i * rnge) + mindef static(val):while True:yield valcuda = True if torch.cuda.is_available() else False
print("Using CUDA" if cuda else "Not using CUDA")
Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor
(2) 使用主成分分析 (Principal Component Analysis
, PCA
) 算法:
pop = np.array([[-.5, .75, -.1, .8], [-.5, -.3, -.5, .8],[-.5, 1, -.5, -.8],[ 1, -.3, -.5, .8]])
pca = PCA(n_components=2)
reduced = pca.fit_transform(pop)t = reduced.transpose()plt.scatter(t[0], t[1])
plt.show()IND_SIZE = hp.size()
NGEN = 10
MIN_VALUE = -1
MAX_VALUE = 1
MIN_STRATEGY = 0.5
MAX_STRATEGY = 5CXPB = .6
MUTPB = .3def generateES(icls, scls, size, imin, imax, smin, smax): ind = icls(random.uniform(imin, imax) for _ in range(size)) ind.strategy = scls(random.uniform(smin, smax) for _ in range(size)) return inddef checkStrategy(minstrategy):def decorator(func):def wrappper(*args, **kargs):children = func(*args, **kargs)for child in children:for i, s in enumerate(child.strategy):if s < minstrategy:child.strategy[i] = minstrategyreturn childrenreturn wrappper
return decorator
(3) 接下来,设置 creator
和 toolbox
,在个体注册中使用 NDIM
值设置大小,设置随机选择运算符输出三个元素,其中 k=3
:
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", array.array, typecode="d", fitness=creator.FitnessMin, strategy=None)
creator.create("Strategy", array.array, typecode="d")toolbox = base.Toolbox()
toolbox.register("individual", generateES, creator.Individual, creator.Strategy,IND_SIZE, MIN_VALUE, MAX_VALUE, MIN_STRATEGY, MAX_STRATEGY)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("mate", tools.cxESBlend, alpha=0.25)
toolbox.register("mutate", tools.mutESLogNormal, c=1.0, indpb=0.06)
toolbox.register("select", tools.selTournament, tournsize=3)toolbox.decorate("mate", checkStrategy(MIN_STRATEGY))
toolbox.decorate("mutate", checkStrategy(MIN_STRATEGY))
(4) 在进化算法部分,我们将 DE
中的个体对象定义为 agent
。计算并应用加权后的差异向量到向量 y
中,该计算保证仅在与当前向量分量匹配的随机抽样索引中发生一次,而交叉提供了改变其他分量值以创建新y的机会:
loss_fn = nn.MSELoss()
if cuda:loss_fn.cuda()def train_function(hp): X = np.reshape(np.arange(data_min, data_max, data_step), (-1, 1))y = function(X)inputs = X.shape[1]tensor_x = torch.Tensor(X) # transform to torch tensortensor_y = torch.Tensor(y)dataset = TensorDataset(tensor_x,tensor_y) # create your datsetdataloader = DataLoader(dataset, batch_size= hp.batch_size, shuffle=True) # create your dataloadermodel = Net(inputs, hp.middle_layer) optimizer = optim.Adam(model.parameters(), lr=hp.learning_rate)if cuda:model.cuda() history=[] start = time.time()for i in range(hp.epochs): for X, y in iter(dataloader):# wrap the data in variablesx_batch = Variable(torch.Tensor(X).type(Tensor))y_batch = Variable(torch.Tensor(y).type(Tensor)) # forward passy_pred = model(x_batch) # compute and print lossloss = loss_fn(y_pred, y_batch) ll = loss.datahistory.append(ll.item()) # reset gradientsoptimizer.zero_grad() # backwards passloss.backward() # step the optimizer - update the weightsoptimizer.step() end = time.time() - startreturn end, history, model, hphp_in = hp.next(ind)
span, history, model, hp_out = train_function(hp_in)
plt.plot(history)
print(min(history))run_history = []def evaluate(individual):hp_in = hp.next(individual)span, history, model, hp_out = train_function(hp_in)y_ = model(torch.Tensor(Xi).type(Tensor)) fitness = loss_fn(y_, torch.Tensor(yi).type(Tensor)).data.item() run_history.append([fitness,*hp_out.values()]) return fitness, # fitness eval
toolbox.register("evaluate", evaluate)MU, LAMBDA = 10, 25
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
best_hp = Nonestart = time.time()
for g in range(NGEN):pop, logbook = algorithms.eaMuCommaLambda(pop, toolbox, mu=MU, lambda_=LAMBDA, cxpb=CXPB, mutpb=MUTPB, ngen=1, stats=stats, halloffame=hof, verbose=False)best = hof[0]span, history, model, hp_out = train_function(hp.next(best))y_ = model(torch.Tensor(Xi).type(Tensor)) fitness = loss_fn(y_, torch.Tensor(yi).type(Tensor)).data.item() run_history.append([fitness,*hp_out.values()]) best_hp = hp_outclear_output() fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18,6)) fig.suptitle(f"Best Fitness {best.fitness} \n{best_hp}")fig.text(0,0,f"Generation {g+1}/{NGEN} Current Fitness {fitness} \n{hp_out}")ax1.plot(history)ax1.set_xlabel("iteration") ax1.set_ylabel("loss")ax2.plot(Xi, yi, 'o', color='black') ax2.plot(Xi,y_.detach().cpu().numpy(), 'r') ax2.set_xlabel("X") ax2.set_ylabel("Y")rh = np.array(run_history) M = rh[:,1:IND_SIZE+1] reduced = pca.fit_transform(M)t = reduced.transpose() hexbins = ax3.hexbin(t[0], t[1], C=rh[:, 0], bins=50, gridsize=50, cmap=cm.get_cmap('gray'))ax3.set_xlabel("PCA X")ax3.set_ylabel("PCA Y") plt.show()time.sleep(1)
下图显示了使用 DE
解决目标问题运行 10
代后的最终输出。可以看到,在评估图中点的分布并未聚集,通过这种方法生成的最佳适应度约为 81
。
可以看到,将 DE
应用于 HPO
提供了一个避免 ES
和 PSO
中局部最小值/最大值问题的优秀机制。下图比较了使用 PSO
、ES
和 DE
算法得到评估图。可以看到,PSO
产生了一个广泛的粒子群,大致集中在它期望的最佳解上,ES
产生了一个更紧密的集群结果,但分布更倾向于狭窄带状区域,而 DE
算法非常适合探索边界,并避免陷入局部最小值/最大值。
小结
差分进化能够更加系统和高效地搜索超参数,并避免局部最小值/最大值集群。本节中,通过将差分进化 (Differential Evolution
, DE
) 与深度学习 (Deep learning
, DL
) 集成应用于超参数优化 (Hyperparameter Optimization
, HPO
) 。通过本节学习,可以看到 DE
方法能够用于有效改进 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)——使用进化策略自动超参数优化