原文:
towardsdatascience.com/grey-wolf-optimizer-how-it-can-be-used-with-computer-vision-234d051a52ae
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ad57c915837b47f321a246dfe8226936.png
由 DALL·E 3 根据提示“在海滩上夜晚绘制一群未来主义灰色狼”创建的图像
这是我的自然启发系列文章的最后一部分。之前,我曾谈到过受遗传学、群体、蜜蜂和蚂蚁启发的算法。今天,我将谈论狼。
当一篇期刊论文的引用次数达到 5 位数时,你就知道正在进行一些严肃的业务。灰狼优化器[1](GWO)就是这样一个例子。
概述和动机
类似于粒子群优化(PSO)、人工蜂群(ABC)和蚁群优化(ACO),灰狼优化(GWO)也是一种元启发式算法。尽管它对解决方案没有数学上的保证,但在实践中表现良好,并且不需要对底层问题有任何分析知识。这使得我们可以从“黑盒”中进行查询,并简单地利用观察到的结果来优化我们的解决方案。
正如我在蚁群优化文章中提到的,所有这些最终都回到了探索-利用权衡的基本概念。那么,为什么会有这么多不同的元启发式算法呢?
首先,这是因为研究人员必须发表论文。他们工作的一部分是从事从不同角度探索事物,并分享他们的发现如何带来比现有方法更多的好处。(或者有些人会说,发表论文是为了证明他们的薪水并寻求晋升。但让我们不要走得太远。)
其次,这要归因于“没有免费午餐”定理[2],这是 GWO 的作者们自己提到的。虽然那个定理是专门说优化算法没有免费午餐,但我认为对于数据科学来说,同样也是适用的。没有单一的终极解决方案可以适用于所有情况,我们通常不得不尝试不同的方法来找出什么有效。
因此,让我们继续添加另一个元启发式算法到我们的工具箱中。因为拥有另一个可能有一天派上用场的工具永远不会有害。
用例
首先,让我们考虑一个简单的图像分类问题。一个巧妙的方法是使用预训练的深度神经网络作为特征提取器,将每个图像转换为特征向量。
正如我在这里所解释的,特征是图像中特征的存在或不存在。与其说一张图片胜过千言万语,不如说一张图片胜过千个特征。真的。(或者可能是 512 个特征,或者 2048 个特征)。
然后,我们可以应用任何经典的机器学习技术,例如集成学习(例如 bagging 和 boosting)、KNN 或 SVM,来进行分类。当然,我们也可以使用全连接层。训练时间将显著减少,节省的时间远超过仅仅冻结权重,因为我们避免了特征提取所需的重复计算。此外,特征向量可以与其他信息(例如来自其他特征工程技术和/或图像的元信息,如果适用)连接。
sklearn 提供了一系列易于使用的 API,仅集成部分就足以让你眼花缭乱。然而,这不仅仅是选择使用哪种算法的问题;你还需要选择合适的超参数,而且没有一种适合所有情况的组合可以使用。
这就是 GWO 可以发挥作用的地方。我们可以使用这种元启发式优化技术来选择超参数,而不是使用网格搜索或随机搜索。
在深入探讨 GWO(灰狼优化)的实现细节之前,这里先给出特征提取的代码。
classDataManager:def__init__(self,root):self.C,self.H,self.W=3,32,32mean=[0.485,0.456,0.406]std=[0.229,0.224,0.225]self.transform=transforms.Compose([transforms.Resize(512),transforms.Pad((0,0,512,512),fill=0,padding_mode='constant'),transforms.CenterCrop((512,512)),transforms.ToTensor(),transforms.Normalize(mean,std)])self.trainset=torchvision.datasets.Food101(root='food101',split='train',download=True,transform=self.transform)self.testset=torchvision.datasets.Food101(root='food101',split='test',download=True,transform=self.transform)self.classes=list(self.testset.class_to_idx.keys())self.get_loader()defget_loader(self):self.batch_size=8self.trainloader=torch.utils.data.DataLoader(self.trainset,batch_size=self.batch_size,shuffle=True,num_workers=0)self.testloader=torch.utils.data.DataLoader(self.testset,batch_size=self.batch_size,shuffle=False,num_workers=0)classFeatureExtractor:def__init__(self,dataMananger,feature_layer):self.dataMananger=dataMananger self.trainloader=self.dataMananger.trainloader self.testloader=self.dataMananger.testloader self.classes=self.dataMananger.classes self.artifacts_dir="./artifact/"ifnotos.path.exists(self.artifacts_dir):os.makedirs(self.artifacts_dir)self.device=torch.device(torch.cuda.current_device()iftorch.cuda.is_available()else'cpu')self.model=vgg19(weights=VGG19_Weights.DEFAULT)self.model.to(self.device)self.feature_layer=feature_layer self.features=[]self.hook=self.model._modules.get(feature_layer).register_forward_hook(self.hook_fn)defhook_fn(self,_module,_in,_out):self.features.append(_out.cpu().data.numpy())defextract(self,loader=None):self.model.eval()features=[]labels=[]loader=loaderorself.trainloaderwithtorch.no_grad():fordataintqdm(loader):images,y_true=data images,y_true=images.to(self.device),y_true.to(self.device)output=self.model(images)features.extend(output.cpu().numpy())labels.extend(y_true.numpy())returnfeatures,labels这样,你只需三行代码就能从 Food101 等数据集中提取出特征。该数据集可在paperswithcode [3]上找到,截至 2024 年 23 月,声明“本网站上的所有内容均公开许可,采用 CC-BY-SA”。CC BY-SA 和其他类型的许可定义可以在这里找到。
(*请注意,Food101 包含 101,000 张图片,下载可能需要大约一个小时。可以通过torchvision或tensorflow方便地下载。如果你想要复制下面的结果,只需下载测试集即可。如果你选择这样做,请记住注释掉所有调用 trainset 的行。)
dataset=DataManager('food101')extractor=FeatureExtractor(dataset,feature_layer='avgpool')features,labels=extractor.extract(dataset.testloader)注意,你可以使用model._modules来确定要提取的相关层的名称。此外,你可以修改DataManager来处理你自己的本地数据集。为了加快复制的速度,你可以在FeatureExtractor.extract()方法() 中添加if np.random.rand() < 0.90: continue来随机选择 10% 的数据。或者,你也可以只切片未打乱的数据集的第一部分,正如本演示所示。
公式(数学与代码)
正如我展示了如何在不了解蜜蜂的摇摆舞或蚂蚁如何对信息素做出反应的情况下理解和利用 ABC 和 ACO 一样,我们将学习 GWO 而不考虑狼在现实中如何狩猎。
足够的是认识到 alpha、beta 和 delta 狼是领导者,而 omega 狼是追随者,追随者的行为由其领导者指导。
在 GWO 中,我们有一个“狼”的群体,表示潜在的解,每个解由其各自的适应度(这是特定于你试图解决的优化问题)定义。适应度最高的解是 alpha(α),而第二和第三好的解分别是 beta(β)和 delta(δ)。
每个解是解空间中的一个点,具有n-维,如前文所述。我将与 GWO 的创始人 [1] 保持一致,并将每个候选解表示为X(因为它是向量而不是标量,所以加粗)。它是一个时间(严格来说,是迭代)的函数,并且根据创始人,个体候选者不使用任何下标或上标表示。
α、β 和 δ 的位置分别表示为X{α}、X*{β} 和X*{δ}。每个狼(即候选解)在每次迭代中都会通过以下方式更新:
<…/Images/1c23ef90637b78aee65dbd5f824d904c.png>
来自 [2] 的方程描述了 GWO 的更新过程。图片由作者提供。
其中D{_α*}* 可以被视为与 alpha 相关的一种距离,尽管它通过一个因子 _C*₁ 调整以鼓励探索。那只狼的结果位置将是X₁、X₂ 和 *X_₃ 的算术平均值,其中 alpha、beta 和 delta 被赋予了同等的重要性。(由于这是一个元启发式算法,当然,如果你更喜欢,你可以使用加权平均)。
注意,∘ 是哈达玛积,即两个向量之间的逐元素乘法。A和C是系数向量,其中r₁ 和r₂ 是在 [0,1] 范围内的随机向量,a的分量在迭代过程中从 2 线性减少到 0。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/fc7b531f56e9feda5cf5198976120d84.png
系数向量 A 和 C 的控制方程。图片由作者提供。
这就是您需要的所有数学知识。真的。这并不意味着拥有超过 10,000 次引用的学术论文必须有高度复杂的数学。只要它能工作,越简单越好。
在 Python 代码中,我们可以将GreyWolfOptimizer类编写如下。
classGreyWolfOptimizer:def__init__(self,dim,n_wolves,n_iterations):self.dim=dim self.n_wolves=n_wolves self.n_iterations=n_iterations self.alpha_wolf=Noneself.beta_wolf=Noneself.delta_wolf=Nonedefoptimize(self,features,labels,upper_bound,lower_bound):#wolves = np.random.rand(self.n_wolves, self.dim)wolves=lower_bound+(upper_bound-lower_bound)*np.random.rand(self.n_wolves,self.dim)fitness=np.zeros(self.n_wolves)self.X_train,self.X_val,self.y_train,self.y_val=train_test_split(features,labels.ravel(),test_size=0.2)foriterationintqdm(range(self.n_iterations)):foriinrange(self.n_wolves):fitness[i]=self.fitness_function(wolves[i])print("fitness: ",fitness)sorted_indices=np.argsort(fitness)delta_id,beta_id,alpha_id=sorted_indices[-3:]self.alpha_wolf=wolves[alpha_id]self.beta_wolf=wolves[beta_id]self.delta_wolf=wolves[delta_id]foriinrange(self.n_wolves):limit=1-iteration/self.n_iterations A1=2*np.random.uniform(low=-limit,high=limit)A2=2*np.random.uniform(low=-limit,high=limit)A3=2*np.random.uniform(low=-limit,high=limit)# simplified to be linearly decreasing with iterationC1,C2,C3=2*np.random.rand(),2*np.random.rand(),2*np.random.rand()D_alpha=abs(C1*self.alpha_wolf-wolves[i])D_beta=abs(C2*self.beta_wolf-wolves[i])D_delta=abs(C3*self.delta_wolf-wolves[i])X1=self.alpha_wolf-A1*D_alpha X2=self.beta_wolf-A2*D_beta X3=self.delta_wolf-A3*D_delta wolves[i]=np.clip((X1+X2+X3)/3,lower_bound,upper_bound)returnself.alpha_wolfdeffitness_function(self,hyperparams):n_estimators=int(hyperparams[0]*50+60)# 10 to 110max_depth=int(hyperparams[1]*8+12)# 4 to 20min_samples_split=int(np.exp(hyperparams[2]*2+3))# 2 to 148max_features=int(hyperparams[3]*50+60)# 10 to 110clf=RandomForestClassifier(n_estimators=n_estimators,max_depth=max_depth,min_samples_split=min_samples_split,max_features=max_features)clf.fit(self.X_train,self.y_train)y_pred=clf.predict(self.X_val)accuracy=accuracy_score(self.y_val,y_pred)returnaccuracy# to maximize通过这种方式,我们可以确定用于构建机器学习模型以从train_features(由FeatureExtractor类获得)预测train_labels(由FeatureExtractor类获得)的超参数。
lower_bound,upper_bound=np.array([-1,-1,-1,-1]),np.array([1,1,1,1])dim=len(upper_bound)n_wolves=10n_iterations=5optimizer=GreyWolfOptimizer(dim,n_wolves,n_iterations)optimal_hyperparams=optimizer.optimize(features,labels,upper_bound,lower_bound)结果
注意,本文的目的不是展示一些创纪录的模型性能。这样的演示对于绝大多数读者来说是不可复制的,因为这需要长时间的训练。我不相信这样的“信我的话”的东西;您应该始终尝试在自己的计算机上复制结果。
相反,这里的重点是教您如何利用 GWO(灰狼优化算法)来选择分类模型的超参数。这可以应用于从某些预训练网络中提取的特征向量,或者简单地应用于表格数据。按照上述步骤,您可以在几分钟内轻松地重现结果。
特征提取每张图像大约需要 1 秒钟(每张图像为 512x512 像素)。对于 Food101 测试集的 10%,2500 张图像加起来需要几分钟(去做其他事情,稍后再回来)。幸运的是,这个特征提取过程只发生一次,之后,每张图像都可以用其特征向量更简洁地表示。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/bb4a7e4e273b96930f9b6b8b25cacdfb.png
作者提供的特征提取时间截图。批处理大小为 8。
为了建立基线,我们使用默认的超参数;模型以及评估指标在几秒钟内获得。在约 1000 个未见过的样本上,这种方法给出了 57%的准确率和 56-57%的 F1 分数。
clf=RandomForestClassifier()clf.fit(optimizer.X_train,optimizer.y_train)y_pred=clf.predict(optimizer.X_val)print(classification_report(optimizer.y_val,y_pred))https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/85e45cfcb462391cb46464b99a54b2fd.png
作者提供的成果截图,使用默认的 RandomForestClassifier
现在,我们执行 GWO 并使用以下建议的超参数。经过短暂的 5 分钟搜索和 5 次迭代后,这提高了 2 个百分点的准确率和 F1 分数。
n_estimators=int(optimal_hyperparams[0]*50+60)max_depth=int(optimal_hyperparams[1]*8+12)min_samples_split=int(np.exp(optimal_hyperparams[2]*2+3))max_features=int(optimal_hyperparams[0]*50+60)clf=RandomForestClassifier()clf.fit(optimizer.X_train,optimizer.y_train)y_pred=clf.predict(optimizer.X_val)print(classification_report(optimizer.y_val,y_pred))https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/04272dd9d53220989d5e42bfccca28c4.png
作者提供的成果截图,使用 GWO 随后是 RandomForestClassifier。
注意到搜索空间被限制在[-1, 1]之间,我使用像np.exp(optimal_hyperparams[j])这样的东西。这确保了搜索空间的不同维度可以相互比较。此外,它还允许我们进行对数搜索,这对于学习率等事物尤为重要。
请记住,GWO 只是众多优化技术中的一种,并且没有单一的最佳方法,正如“无免费午餐”定理所支持的。此外,超参数调优是 GWO 的一个用例,但绝对不是唯一的。
结论
在本文中,我解释了与灰狼优化器相关的数学知识,以及实现它的代码。我将这些应用于随机森林分类器超参数调优的简单用例。此外,我还提供了从预训练的深度神经网络中高效提取特征的代码,通过这些代码,你可以应用各种其他机器学习技术,并作为数据科学家展示增值。
虽然这里没有展示,但 GWO 的创始作者[2]写道,该算法在大多数多模态函数测试中优于 PSO 和 GSA(重力搜索算法)。
未来
尽管存在其他受自然界启发的算法,例如受萤火虫和杜鹃鸟启发的算法,但我将不会继续介绍这些算法。因此,本文是该系列的最后一篇。
展望未来,我计划撰写与计算机视觉相关的内容,这是真正让我开始作为数据科学家职业生涯的领域,也是我在职业生涯初期所专注的领域。我还打算撰写如何使用 AWS 展示我们的工作,并在求职面试中脱颖而出。
参考文献
[1] S. Mirjalili, S. M. Mirjalili, 和 A. Lewis, 灰狼优化器 (2014), Advances in engineering software, 69:46–61.
[2] D. H. Wolpert 和 W. G. Macready, 无免费午餐优化定理 (1997), IEEE transactions on evolutionary computation, 1(1): 67–82.
[3] L. Bossard, M. Guillaumin, 和 L. Van Gool, Food-101 (2014), Papers With Code. 从paperswithcode.com/dataset/food-101获取。