✅博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。
✅成品或者定制,扫描文章底部微信二维码。
(1) 基于生成对抗网络的混合密度估计优化算法设计
黑盒优化问题是指目标函数的数学形式未知或过于复杂无法解析表达的优化问题,优化算法只能通过查询目标函数获得输入输出对应关系,而无法利用梯度等解析信息。这类问题在工程设计、材料配方、超参数调优等领域普遍存在。传统的黑盒优化方法如贝叶斯优化和进化算法在特定类型的问题上表现良好,但其性能高度依赖于对问题结构的先验假设。当先验假设与实际问题不匹配时,算法性能会显著下降。密度估计方法通过学习目标函数的分布特征,能够自适应地适应不同类型的问题,是构建通用黑盒优化算法的有前途的技术路线。
生成对抗网络是一种强大的密度估计方法,由生成器和判别器两个神经网络对抗训练而成。生成器学习从简单的先验分布到目标分布的映射,能够产生服从目标分布的样本。判别器学习区分真实样本和生成样本,为生成器提供训练信号。在优化应用中,将历史评估点中的优良点作为真实样本训练生成对抗网络,训练后的生成器能够产生可能具有良好目标函数值的新候选点。这种方法的优势在于不需要显式地假设目标函数的形式,而是通过数据驱动的方式学习优良解的分布特征。
针对优化任务的特点对生成对抗网络进行改进。标准生成对抗网络的训练目标是最小化生成分布与真实分布之间的Jensen-Shannon散度,但这一目标与优化任务的目标并不完全一致。在优化中,我们更关心生成器能否产生目标函数值更优的点,而不仅仅是模仿已知优良点的分布。为此,在生成器的损失函数中加入目标函数值的期望项,引导生成器向产生更优解的方向学习。具体实现采用代理模型预测候选点的目标函数值,代理模型使用高斯过程或神经网络构建,避免了直接查询真实目标函数带来的高昂成本。
混合密度估计策略解决了探索与开发的平衡问题。纯粹依赖生成对抗网络产生的样本可能过于集中在已知优良区域,忽视了对未探索区域的搜索,容易陷入局部最优。混合策略维护两个样本生成源:一个是生成对抗网络产生的开发样本,集中在预测优良的区域;另一个是均匀分布或基于不确定性的探索样本,覆盖搜索空间中的未知区域。两类样本按一定比例混合构成每次迭代的候选集,比例随迭代动态调整,初期偏向探索以发现有希望的区域,后期偏向开发以精化已发现的优良解。
算法的工作流程如下:初始化阶段随机采样若干点并评估其目标函数值,建立初始数据集。迭代优化阶段首先从数据集中筛选优良点作为正样本训练生成对抗网络,然后利用训练好的生成器产生候选点,同时产生探索性的随机点,两者混合后通过代理模型预测目标函数值,选择预测值最优的若干点进行真实评估并加入数据集。重复迭代直到达到评估预算或收敛条件。实验在多个标准测试函数和实际应用问题上验证了算法的有效性,相比传统贝叶斯优化和进化算法,密度估计方法在大多数问题上取得了更好或相当的性能。
(2) 基于反卷积密度网络的精确概率建模方法
生成对抗网络虽然能够产生高质量的样本,但属于隐式密度估计方法,无法直接计算给定点的概率密度值。在优化应用中,概率密度信息对于指导搜索方向和衡量区域重要性具有重要价值。显式密度估计方法能够提供精确的概率密度计算,但传统的参数化方法如高斯混合模型在高维空间中面临参数数量爆炸和表达能力受限的问题。反卷积密度网络是一种新型的显式密度估计方法,通过神经网络参数化的可逆变换将复杂分布映射为简单分布,同时保留精确计算密度的能力。
反卷积密度网络的核心是设计可逆且雅可比行列式易于计算的神经网络层。常用的可逆层类型包括仿射耦合层、自回归层和残差流等。仿射耦合层将输入分为两部分,一部分保持不变,另一部分通过依赖于第一部分的仿射变换进行转换。这种设计保证了可逆性(只需反向应用仿射变换),且雅可比行列式为仿射变换的缩放因子之积,计算简单。通过堆叠多个耦合层并交替分割维度,网络能够学习复杂的非线性映射。训练目标是最大化训练数据的对数似然,即最大化数据在学习到的密度函数下的概率。
针对黑盒优化中数据量有限的特点,对反卷积密度网络进行适应性改进。标准方法假设有大量训练数据可用,但在优化过程中每次评估真实目标函数代价高昂,可用数据往往只有数十到数百个点。小数据量容易导致网络过拟合,学到的密度分布过于集中在训练点附近而泛化能力差。引入正则化技术缓解过拟合,包括权重衰减、早停和dropout等。此外,采用贝叶斯神经网络的思想,在网络权重上施加先验分布,通过变分推断学习权重的后验分布,这样预测时可以通过权重采样得到密度估计的不确定性度量。
关注机制用于解决最小建模精度问题。当目标函数在搜索空间的不同区域具有不同的复杂度时,统一的建模精度可能在简单区域过度拟合而在复杂区域欠拟合。引入空间变化的注意力机制,使网络能够对不同区域分配不同的建模资源。注意力权重通过一个辅助网络计算,输入为空间位置,输出为该位置的重要性权重。在计算密度时,注意力权重调制网络各层的贡献,重要性高的区域获得更精细的建模。注意力网络与密度网络联合训练,通过数据似然的反向传播自动学习合适的注意力分配。
历史信息的构造方法影响探索开发的平衡。简单地使用所有历史评估点训练密度网络会导致模型拟合整个历史分布,而非聚焦于当前最优区域。采用滑动窗口策略,仅使用最近若干次评估的数据训练网络,窗口大小随迭代动态调整。初期窗口较大以保持探索性,后期窗口较小以增强开发性。此外,对历史数据按目标函数值进行加权,优良点获得更高权重使密度估计偏向优良区域。权重函数采用softmax形式,温度参数控制权重分布的集中程度,温度随迭代降低实现从探索到开发的自然过渡。
(3) 基于密度变换的黑盒测试问题构造与算法评估方法
黑盒优化算法的性能评估需要在标准测试问题集上进行比较实验。现有的测试问题主要有两类:一类是基于数学函数构造的合成问题,如Sphere、Rastrigin、Ackley等,这些问题具有已知的全局最优和解析表达式,便于验证算法的正确性;另一类是来自实际应用的真实问题,如超参数优化、分子设计等,更接近实际应用场景但数量有限且评估成本高。合成问题的局限在于其复杂度往往低于真实问题,算法在合成问题上的良好表现不一定能迁移到真实应用中。需要一种能够系统性地增加测试问题复杂度的方法,填补合成问题和真实问题之间的鸿沟。
基于密度变换的测试问题构造方法通过对输入空间进行非线性变换来增加问题难度。核心思想是在原测试问题的基础上,对输入变量施加一个可逆的非线性映射,使得原本简单的搜索空间变得复杂崎岖。密度变换神经网络恰好提供了这样的可逆非线性映射,其可逆性保证变换后的问题仍然具有与原问题相同的最优值和最优解(变换回原空间)。通过调节网络的层数和复杂度,可以控制变换的非线性程度,从而生成不同难度级别的测试问题。
变换的具体构造考虑了多种难度因素。首先是多模态性的增强,通过在变换中引入周期性成分,使得原本单模态的问题变成多模态问题,增加局部最优陷阱的数量。其次是变量耦合的引入,原测试问题中相互独立的变量通过变换后产生依赖关系,降低了可分解性,增加了优化难度。再次是尺度不一致性的放大,变换使不同变量对目标函数的敏感程度差异增大,需要算法具有自适应的步长调节能力。最后是边界效应的复杂化,变换可能使原本位于搜索空间中心的最优解移动到边界附近,考验算法的边界处理策略。
import numpy as np import torch import torch.nn as nn import torch.optim as optim from torch.distributions import Normal class Generator(nn.Module): def __init__(self, latent_dim, output_dim, hidden_dim=128): super(Generator, self).__init__() self.net = nn.Sequential( nn.Linear(latent_dim, hidden_dim), nn.LeakyReLU(0.2), nn.BatchNorm1d(hidden_dim), nn.Linear(hidden_dim, hidden_dim), nn.LeakyReLU(0.2), nn.BatchNorm1d(hidden_dim), nn.Linear(hidden_dim, output_dim), nn.Tanh() ) def forward(self, z): return self.net(z) class Discriminator(nn.Module): def __init__(self, input_dim, hidden_dim=128): super(Discriminator, self).__init__() self.net = nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.LeakyReLU(0.2), nn.Dropout(0.3), nn.Linear(hidden_dim, hidden_dim), nn.LeakyReLU(0.2), nn.Dropout(0.3), nn.Linear(hidden_dim, 1), nn.Sigmoid() ) def forward(self, x): return self.net(x) class SurrogateModel(nn.Module): def __init__(self, input_dim, hidden_dim=64): super(SurrogateModel, self).__init__() self.net = nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 1) ) def forward(self, x): return self.net(x) class GANBasedOptimizer: def __init__(self, obj_func, dim, lb, ub, latent_dim=32): self.obj_func = obj_func self.dim = dim self.lb = np.array(lb) self.ub = np.array(ub) self.latent_dim = latent_dim self.generator = Generator(latent_dim, dim) self.discriminator = Discriminator(dim) self.surrogate = SurrogateModel(dim) self.g_optimizer = optim.Adam(self.generator.parameters(), lr=0.0002, betas=(0.5, 0.999)) self.d_optimizer = optim.Adam(self.discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999)) self.s_optimizer = optim.Adam(self.surrogate.parameters(), lr=0.001) self.data_x = [] self.data_y = [] def normalize(self, x): return 2 * (x - self.lb) / (self.ub - self.lb) - 1 def denormalize(self, x): return (x + 1) / 2 * (self.ub - self.lb) + self.lb def evaluate(self, x): y = self.obj_func(x) self.data_x.append(x.copy()) self.data_y.append(y) return y def get_elite_samples(self, top_ratio=0.3): n = len(self.data_y) if n == 0: return None n_elite = max(1, int(n * top_ratio)) indices = np.argsort(self.data_y)[:n_elite] elite_x = np.array([self.data_x[i] for i in indices]) elite_y = np.array([self.data_y[i] for i in indices]) return elite_x, elite_y def train_gan(self, epochs=100, batch_size=32): elite_data = self.get_elite_samples() if elite_data is None or len(elite_data[0]) < batch_size: return elite_x, _ = elite_data elite_x_norm = self.normalize(elite_x) elite_tensor = torch.FloatTensor(elite_x_norm) for epoch in range(epochs): idx = np.random.choice(len(elite_tensor), min(batch_size, len(elite_tensor)), replace=False) real_samples = elite_tensor[idx] self.d_optimizer.zero_grad() real_labels = torch.ones(len(real_samples), 1) fake_labels = torch.zeros(len(real_samples), 1) real_output = self.discriminator(real_samples) d_real_loss = nn.BCELoss()(real_output, real_labels) z = torch.randn(len(real_samples), self.latent_dim) fake_samples = self.generator(z) fake_output = self.discriminator(fake_samples.detach()) d_fake_loss = nn.BCELoss()(fake_output, fake_labels) d_loss = d_real_loss + d_fake_loss d_loss.backward() self.d_optimizer.step() self.g_optimizer.zero_grad() z = torch.randn(len(real_samples), self.latent_dim) fake_samples = self.generator(z) fake_output = self.discriminator(fake_samples) g_loss = nn.BCELoss()(fake_output, real_labels) g_loss.backward() self.g_optimizer.step() def train_surrogate(self, epochs=100): if len(self.data_x) < 10: return X = torch.FloatTensor(self.normalize(np.array(self.data_x))) y = torch.FloatTensor(np.array(self.data_y)).reshape(-1, 1) y_norm = (y - y.mean()) / (y.std() + 1e-8) for epoch in range(epochs): self.s_optimizer.zero_grad() pred = self.surrogate(X) loss = nn.MSELoss()(pred, y_norm) loss.backward() self.s_optimizer.step() def generate_candidates(self, n_samples=100): self.generator.eval() with torch.no_grad(): z = torch.randn(n_samples, self.latent_dim) generated = self.generator(z).numpy() generated = np.clip(generated, -1, 1) return self.denormalize(generated) def select_candidates(self, candidates, n_select=5): self.surrogate.eval() with torch.no_grad(): X = torch.FloatTensor(self.normalize(candidates)) scores = self.surrogate(X).numpy().flatten() indices = np.argsort(scores)[:n_select] return candidates[indices] def optimize(self, n_init=20, n_iterations=50, n_candidates=100, n_select=5, exploration_ratio=0.3): for _ in range(n_init): x = np.random.uniform(self.lb, self.ub) self.evaluate(x) convergence = [min(self.data_y)] for iteration in range(n_iterations): self.train_surrogate(epochs=50) self.train_gan(epochs=50) gan_candidates = self.generate_candidates(int(n_candidates * (1 - exploration_ratio))) n_explore = int(n_candidates * exploration_ratio) explore_candidates = np.random.uniform(self.lb, self.ub, (n_explore, self.dim)) all_candidates = np.vstack([gan_candidates, explore_candidates]) selected = self.select_candidates(all_candidates, n_select) for x in selected: self.evaluate(x) convergence.append(min(self.data_y)) best_idx = np.argmin(self.data_y) return self.data_x[best_idx], self.data_y[best_idx], convergence class NormalizingFlow(nn.Module): def __init__(self, dim, n_layers=4, hidden_dim=64): super(NormalizingFlow, self).__init__() self.dim = dim self.layers = nn.ModuleList() for _ in range(n_layers): self.layers.append(AffineCouplingLayer(dim, hidden_dim)) def forward(self, x): log_det_sum = 0 for layer in self.layers: x, log_det = layer(x) log_det_sum += log_det return x, log_det_sum def inverse(self, z): for layer in reversed(self.layers): z = layer.inverse(z) return z def log_prob(self, x): z, log_det = self.forward(x) base_log_prob = Normal(0, 1).log_prob(z).sum(dim=-1) return base_log_prob + log_det class AffineCouplingLayer(nn.Module): def __init__(self, dim, hidden_dim): super(AffineCouplingLayer, self).__init__() self.dim = dim self.split = dim // 2 self.scale_net = nn.Sequential( nn.Linear(self.split, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, dim - self.split), nn.Tanh() ) self.translate_net = nn.Sequential( nn.Linear(self.split, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, dim - self.split) ) def forward(self, x): x1, x2 = x[:, :self.split], x[:, self.split:] s = self.scale_net(x1) t = self.translate_net(x1) y2 = x2 * torch.exp(s) + t log_det = s.sum(dim=-1) return torch.cat([x1, y2], dim=-1), log_det def inverse(self, y): y1, y2 = y[:, :self.split], y[:, self.split:] s = self.scale_net(y1) t = self.translate_net(y1) x2 = (y2 - t) * torch.exp(-s) return torch.cat([y1, x2], dim=-1) def sphere_function(x): return np.sum(x ** 2) def rastrigin_function(x): A = 10 return A * len(x) + np.sum(x ** 2 - A * np.cos(2 * np.pi * x)) if __name__ == "__main__": dim = 10 lb = [-5] * dim ub = [5] * dim optimizer = GANBasedOptimizer(sphere_function, dim, lb, ub) best_x, best_y, conv = optimizer.optimize(n_init=30, n_iterations=100) print(f"Best solution found: {best_y:.6e}") print(f"Total evaluations: {len(optimizer.data_y)}")成品代码50-200,定制300起,可以直接沟通
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇