遗传算法与深度学习实战(18)——使用网格搜索自动超参数优化

遗传算法与深度学习实战(18)——使用网格搜索自动超参数优化

    • 0. 前言
    • 1. 网格搜索
    • 2. 使用网格搜索自动超参数优化
    • 小结
    • 系列链接

0. 前言

我们已经学习了如何使用随机搜索获得较好的超参数优化 (Hyperparameter Optimization, HPO) 结果,但它耗时过长,为了寻找快速且准确的自动 HPO,需要使用更高级的技术。一种简单有效的技术是网格搜索,特别适用于参数空间较小且相对离散的情况。在本节中,我们将介绍网格搜索的基本原理,并实现网格搜索自动超参数优化。

1. 网格搜索

网格搜索 (Grid Search) 的工作原理是将搜索区域按照网格模式划分,并系统地遍历网格中的每个单元。网格搜索在二维空间中易于进行可视化,但该技术对于任何维数的问题都是有效的。
下图展示了随机搜索和网格搜索在超参数空间中的比较,图中展示了一种可能的网格遍历模式,在每个单元格中评估学习率和中间层变量。网格搜索是一种有效的方法,可以以有条不紊且高效的方式评估一系列可能的组合。

网格搜索

2. 使用网格搜索自动超参数优化

在本节中,我们将修改随机搜索自动超参数优化,使用更复杂的网格搜索技术。虽然这种技术更强大和高效,但它仍受限于网格的大小,使用较大的网格单元通常会将结果限制在局部最小值或最大值,而较小的网格单元虽然可以找到全局最小值或最大值,但搜索空间也会增加。
接下来,基于随机搜索代码,实现网格搜索。代码的主要区别在于超参数对象需要跟踪一个参数网格。

(1) 首先,导入所需库,并定义相关超参数:

import numpy as np
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 matplotlib import pyplot as plt
from matplotlib import cm
from IPython.display import clear_output
import time
import math
import types
from itertools import cycle
from sklearn.model_selection import ParameterGrid

def 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 x

(2) 实现 HyperparametersGrid 类和 __init__() 函数,将输入参数的名称提取到 self.hparms 中,然后测试第一个输入是否指向一个生成器,如果为真,就使用 self.create_grid 生成一个参数网格;否则,该实例只是一个子超参数容器:

class HyperparametersGrid(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)     
        self.hparms = [d for d in self.__dict__] 
        self.grid = {}   
        self.gidx = 0
        if isinstance(self.__dict__[self.hparms[0]], types.GeneratorType):        
            self.grid = self.create_grid()
            self.grid_size = len(self.grid)      
            
    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 vals

接下来,在 self.create_grid() 函数中构建参数网格。函数首先创建一个空的网格字典 grid,然后循环遍历超参数列表,调用超参数生成器,使用 next 返回一个值和总值数。然后再次通过生成器循环,提取每个唯一值并将其附加到行列表 row 中。之后,将行附加到网格中,最后通过将网格注入 ParameterGrid 类来完成。ParameterGridscikit-learn 中的一个辅助类,它以输入字典和值列表作为输入,并构造一个网格,其中每个单元格表示各种超参数组合。虽然我们在本示例中,使用两个超参数在二维网格上运行,但 ParameterGrid 可以处理任意维数的问题:

    def create_grid(self):
        grid = {}
        for d in self.hparms:
            v,len = next(self.__dict__[d])
            row = []
            for i in range(len):
                v,_ = next(self.__dict__[d])
                row.append(v)
            grid[d] = row
        grid = ParameterGrid(grid)    
        return grid

拥有包含所有超参数组合的内部参数网格后,更新 next 函数。reset 函数用于将索引复位为参数网格。每次调用 next 都会增加索引,并从参数网格 (self.grid) 中提取下一个值,使用 ** 操作将网格值作为输入解包到 HyperparametersGrid 的新实例中:

    def reset(self):
        self.gidx = 0

    def next(self):
        self.gidx += 1
        if self.gidx > self.grid_size-1:
            self.gidx = 0
        return HyperparametersGrid(**self.grid[self.gidx])

(3) 使用网格超参数类还需要用于控制超参数创建的生成器。为了简单起见,我们定义了两个函数:一个用于浮点数,另一个用于整数。在每个函数内部,我们从最小值 min 到最大值 max 以步长间隔 step 创建一个名为 grid 的值数组,遍历值列表,得到一个新的值和总列表长度。有了总列表长度,就可以通过迭代生成器来创建参数网格:

def grid(min, max, step):  
    grid = cycle(np.arange(min, max, step))
    len = (max-min) / step
    for i in grid:
        yield i, int(len)

def grid_int(min, max, step): 
    grid = cycle(range(min, max, step))
    len = (max-min) / step
    for i in grid:
        yield i, int(len)

(4) 接下来,使用网格超参数类 HyperparametersGrid 和生成器函数来创建父 hp 对象。使用网格生成器函数。在初始化类之后,将创建一个内部参数网格,可以查询关于网格的信息,如获取组合或值的总数。然后,还可以调用父 hp 对象上的 next 来生成一对子对象。可以通过将每个超参数的值数相乘来计算网格组合的数量。在示例中,middle_layer9 个值,learning_rate10 个值,epochs1 个值,batch_size1 个值,总共有 90 个值,即 10×9×1×1=90。当处理多个变量和较小的步长时,网格大小可能会迅速增大:

hp = HyperparametersGrid(
    middle_layer = grid_int(8, 64, 6),
    learning_rate = grid(3.5e-02,3.5e-01, 3e-02),
    batch_size = grid_int(16, 20, 4),    
    epochs = grid_int(200,225,25)  
)

print(hp.grid_size)
print(hp.grid.param_grid)
print(hp.next())
print(hp.next())

(5) 使用 GPU 进行训练。runshp.grid_size 定义,并创建一个名为 grid_size 的新变量,它由 runs 的数量定义,第二个变量用于定义在适应度评估图上绘制的网格单元格的大小:

cuda = 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

loss_fn = nn.MSELoss()  
if cuda:
    loss_fn.cuda()

def train_function(hp):
    hp = hp.next()

    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 tensor
    tensor_y = torch.Tensor(y)
    
    dataset = TensorDataset(tensor_x,tensor_y) # create your datset
    dataloader = DataLoader(dataset, batch_size= hp.batch_size, shuffle=True) # create your dataloader

    model = 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 variables
            x_batch = Variable(torch.Tensor(X).type(Tensor))
            y_batch = Variable(torch.Tensor(y).type(Tensor))                   
            # forward pass
            y_pred = model(x_batch)        
            # compute and print loss
            loss = loss_fn(y_pred, y_batch)  
            ll = loss.data
            history.append(ll.item())                   
            # reset gradients
            optimizer.zero_grad()        
            # backwards pass
            loss.backward()        
            # step the optimizer - update the weights
            optimizer.step()  
    end = time.time() - start
    return end, history, model, hp

best = float("inf")
span, history, model, hp_out = train_function(hp)
print(hp_out)
plt.plot(history)
print(min(history).item())

(6) 最后,输出评估图,根据计算的变量设置 grid_size。使用六边图将适应度值自动映射为颜色,然后,根据组合的数量设置 grid_size。在示例中,我们假设参数的网格是正方形的,但这可能并不总是准确的:

for i in range(runs):  
    span, history, model, hp_out = train_function(hp)
    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()]) 
    if fitness < best:
        best = fitness
        best_hp = hp_out
    clear_output()    
    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18,6))    
    fig.suptitle(f"Best Fitness {best} \n{best_hp}")
    fig.text(0,0,f"Run {i+1}/{runs} 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)      
    hexbins = ax3.hexbin(rh[:, 1], rh[:, 2], C=rh[:, 0], 
                            bins=25, gridsize=grid_size, cmap=cm.get_cmap('gray'))
    ax3.set_xlabel("middle_layer")
    ax3.set_ylabel("learning_rate")    
        
    plt.show()
    time.sleep(1)

下图显示了输出结果,显然比随机搜索要快得多,但不够准确。最终适应度(约 12,000 )是随机搜索中适应度的三分之一(约 57,000)。因此,网格搜索的结果不够准确,但更快更高效。我们可以将搜索范围缩小到较小的范围,并减小步长以提高准确性。

在这里插入图片描述

网格搜索是一种优秀的技术,在需要系统地查看各种超参数组合时,它非常有用。然而,需要特别的是,在输出图中,最佳适应度(暗色区域)与最差适应度(浅色区域)之间相差只有两个单元。然而,我们可以看到在这个浅色区域周围有很多具有良好适应度的区域,这表明我们很可能错过了全局最小值和/或最大值。解决此问题的方法是缩小网格范围,只覆盖两到三个单元的区域,以更好地确定最佳超参数。

小结

网格搜索的优势在于其能够完全覆盖预定义的参数空间,确保找到最优解,然而随着参数空间的增大,网格搜索的计算成本会显著增加,因为它需要评估每个可能的参数组合。在本节中,我们介绍了网格搜索的基本原理,并学习了如何通过网格搜索自动超参数优化。

系列链接

遗传算法与深度学习实战(1)——进化深度学习
遗传算法与深度学习实战(2)——生命模拟及其应用
遗传算法与深度学习实战(3)——生命模拟与进化论
遗传算法与深度学习实战(4)——遗传算法(Genetic Algorithm)详解与实现
遗传算法与深度学习实战(5)——遗传算法中常用遗传算子
遗传算法与深度学习实战(6)——遗传算法框架DEAP
遗传算法与深度学习实战(7)——DEAP框架初体验
遗传算法与深度学习实战(8)——使用遗传算法解决N皇后问题
遗传算法与深度学习实战(9)——使用遗传算法解决旅行商问题
遗传算法与深度学习实战(10)——使用遗传算法重建图像
遗传算法与深度学习实战(11)——遗传编程详解与实现
遗传算法与深度学习实战(12)——粒子群优化详解与实现
遗传算法与深度学习实战(13)——协同进化详解与实现
遗传算法与深度学习实战(14)——进化策略详解与实现
遗传算法与深度学习实战(15)——差分进化详解与实现
遗传算法与深度学习实战(16)——神经网络超参数优化
遗传算法与深度学习实战(17)——使用随机搜索自动超参数优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/890730.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

『Mysql进阶』Mysql explain详解(五)

目录 Explain 介绍 Explain分析示例 explain中的列 1. id 列 2. select_type 列 3. table 列 4. partitions 列 5. type 列 6. possible_keys 列 7. key 列 8. key_len 列 9. ref 列 10. rows 列 11. filtered 列 12. Extra 列 Explain 介绍 EXPLAIN 语句提供有…

【C++指南】C++中的浅拷贝与深拷贝:深入剖析

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 目录 引言 &#x1f343;浅拷贝 基本概念 代码示例分析 &#x1f343;深拷贝 基本概念 代码示例分析…

数据中心物理安全的历史和演变

在当今的数字时代&#xff0c;数据中心托管已成为我们互联世界的支柱。这些设施在存储、管理和处理我们日常生活所需的大量信息方面发挥着至关重要的作用。从社交媒体平台和电子商务网站到流媒体服务和云计算&#xff0c;数据中心为我们依赖的数字服务提供支持。 随着企业越来…

2024.10.10计算机外部设备及调试培训

授课老师&#xff1a;杨戬 1.计算机组成 cpu&#xff0c;主板&#xff0c;内存&#xff0c;硬盘&#xff0c;电源&#xff0c;显示器&#xff0c;键盘和鼠标&#xff0c;光驱和显卡&#xff0c;其他外部设备。 2.虚拟机专业版转换 由于我们在2024.10.8的培训中已经安装了wi…

Spring Boot知识管理系统:安全与合规性

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

学习threejs,THREE.LineDashedMaterial 虚线材质,基于gosper高斯帕曲线生成雪花动画

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.LineDashedMaterial虚…

【ROS2实操四】参数服务

概念 在机器人系统中不同的功能模块可能会使用到一些相同的数据&#xff0c;比如&#xff1a; 导航实现时&#xff0c;会进行路径规划&#xff0c;路径规划主要包含&#xff0c; 全局路径规划和本地路径规划&#xff0c;所谓全局路径规划就是设计一个从出发点到目标点的大致路径…

iOS--NSURLSession Alamofire流程源码解析(万字详解版)

一、NSURLSession NSURLSession的主要功能是发起网络请求获取网络数据&#xff0c;是Apple的网络请求原生库之一。Alamofire就是对NSURLSession的封装&#xff0c;如果对NSURLSession不熟悉的话&#xff0c;那么Alamofire源码看起来会比较费劲的。因此我们先简单学习下NSURLSe…

Springboot整合抖音小程序获取access-token图片检测V3

抽取配置文件 appId以及secret需要自行在抖音开放平台获取 dy:appId: ttb0xxxxxsecret: 12a19a426xxxxxxxxxxxxx获取access-token 参照文档我们调用此接口需要先获取access-token 获取access-token官方文档地址 注意事项 client_token 的有效时间为 2 个小时&#xff0c;重复获…

力扣- 背包问题

关于背包问题,推荐卡哥的视频,结合代码随想录食用,效果绝佳!!! 传送门: 带你学透0-1背包问题&#xff01;| 关于背包问题&#xff0c;你不清楚的地方&#xff0c;这里都讲了&#xff01;| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili 带你学透01背包问题&#xff…

HyperWorks汽车B-柱网格变形

在这一节&#xff0c;将练习如何使用变形域&#xff0c;实现汽车 B-柱有限元模型的网格变形。 图 7-13 网格变形前后的 B 柱模型 Step01&#xff1a;读取并查看模型。 打开模型文件 Exercise_7c.hm。 Step02&#xff1a;创建变形域。 (1) 通过路径 HyperMorph > Morph…

C++笔记之原子操作

C++笔记之原子操作 code review! 文章目录 C++笔记之原子操作1.初始化2.赋值3.取值4.赋给另一个原子类型5.`exchange`6.`compare_exchange_weak` 和 `compare_exchange_strong`使用场景7.注意事项在 C++ 中,原子类型提供了对共享变量的无锁操作,确保多线程环境下的安全。以下…

【Linux】为什么创建目录文件,硬链接数是2;创建普通文件时,硬链接数是1?(超详细图文解答)

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…

图示详解OpenEuler下Samba多用户身份验证配置、测试

前言 前文《图例详解OpenEuler下Samba安装、配置和测试》已对Samba服务的工作原理、安装、配置和测试&#xff0c;做了系统的介绍&#xff0c;并对匿名用户的访问samba服务器做了配置&#xff0c;相必读者已对samba服务的流程有了初步、系统的了解&#xff0c;本文在以上基础上…

DevExpress WinForms中文教程:Data Grid - 如何完成数据输入验证?

本教程介绍DevExpress WinForm的Data Grid控件是如何利用网格组件完成数据输入验证的。 P.S&#xff1a;DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序…

vim 操作

vim编辑器的有三种工作模式&#xff1a;命令模式、插入模式和底行命令模式 打开进入命令模式&#xff1a; 由命令模式到输入模式&#xff1a;i:在光标前插&#xff1b;a:在光标后插&#xff1b;o:在下一行插 由输入模式进入命令模式&#xff1a;esc 由命令模式进入底行命令…

LabVIEW技术难度最大的程序

在LabVIEW开发中&#xff0c;技术难度最大的程序通常涉及复杂的系统架构、高精度的控制要求、大量数据处理&#xff0c;以及跨平台或多硬件设备的集成。以下是几类具有高技术难度的LabVIEW程序&#xff1a; 1. 高精度实时控制系统 LabVIEW中涉及高精度实时控制的系统程序&…

探索极致性能:R9-9950X与I9-14900K的深度较量

处理器是电脑及服务器的心脏&#xff0c;处理器的性能直接影响着电脑或服务器的运行效率、多任务处理能力以及整体用户体验。一款优秀的处理器&#xff0c;能够确保系统流畅运行&#xff0c;无论是处理复杂的数据分析、高强度的图形渲染&#xff0c;还是享受沉浸式的游戏体验&a…

【spring ai】java 实现RAG检索增强,超快速入门

rag 需求产生的背景介绍&#xff1a; 在使用大模型时&#xff0c;一个常见的问题是模型会产生幻觉&#xff08;即生成的内容与事实不符&#xff09;&#xff0c;同时由于缺乏企业内部数据的支持&#xff0c;导致其回答往往不够精准和具体&#xff0c;偏向于泛泛而谈。这些问题…

Selenium实现滑动滑块验证码验证!

背景&#xff1a;在部分的登录中有滑动验证码的验证&#xff0c;由于滑动验证码的缺块是随机的就导致实现起来比较困难&#xff01; 01、实现方案 模板匹配 通过openCV分析两个图片的相似度&#xff0c;获取两个相似度很高图片的坐标&#xff0c;从而计算两个图片的距离。 轮…