神经网络超参数调整(基于ray包)

本文详细介绍了如何在Python中使用ray库进行模型调参,包括通过贝叶斯优化和网格搜索策略,如GridSearchCV和Bayesian Optimization。从安装ray包的注意事项,数据预处理到模型构建,作者分享了关键步骤和代码示例,确保了可复现性和结果解释。
摘要由CSDN通过智能技术生成

前言

  • ray包的安装有很多问题,特别是版本问题,否则会报错
  • ray包搜索算法有很多,包括但不仅限于贝叶斯优化,AX等
  • 多读ray的官方文档,这个包网上找不到特别多资源,官方网站

包的安装

  • 千万不要直接pip install ray,很多依赖包版本更不上,会一直报错,这个问题我花了很长时间解决。
  • 首先安装稍微低一点版本的ray
pip install ray==1.1.0
  • 然后将aiohttp包的版本改一下,参考
pip install aiohttp==3.7.3

示例

导入数据处理包

#加载包
import numpy as np
import pandas as pd
from plotnine import*
from scipy import stats
import seaborn as sns
import statsmodels.api as sm
import matplotlib as mpl
import matplotlib.pyplot as plt
#中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# notebook嵌入图片
%matplotlib inline
# 提高分辨率
%config InlineBackend.figure_format='retina'

from sklearn import preprocessing 
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer,mean_squared_error
from sklearn.preprocessing import MinMaxScaler

# 忽略警告
import warnings
warnings.filterwarnings('ignore')

导入神经网络包

import torch
import torch.nn as nn
from torch import optim
import torchvision.transforms as trans
from torch.utils.data import Dataset
from torch.utils.data import TensorDataset
from torchvision import datasets
from torchvision import transforms
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
from ray import tune
import ray.cloudpickle as pickle
from ray.tune.schedulers import ASHAScheduler
from ray.tune.stopper import EarlyStopping

可复现性

  • 这个地方特别重要,如果不设定随机数种子,在调参的时候每次的初始参数都不一样,这样会报错!
np.random.seed(0)
torch.manual_seed(0)
torch.cuda.manual_seed_all(0)

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

数据准备

数据导入

name = ['序号','新生儿体重','孕妇怀孕期','新生儿胎次状况','孕妇怀孕时年龄','孕妇怀孕前身高','孕妇怀孕前体重','孕妇吸烟状况']
data = pd.read_csv("D:/学习资料/数学/数学建模培训与比赛/孕妇吸烟与胎儿健康/data0901.txt", sep = '\t',names = name,encoding="gbk",index_col = '序号')
data.head()

缺失值剔除

# 剔除缺失值
data = data[(data['孕妇吸烟状况'] < 9) &
            (data['孕妇怀孕前体重'] < 999) & 
            (data['孕妇怀孕前身高'] < 99) & 
            (data['孕妇怀孕时年龄'] < 99) & 
            (data['孕妇怀孕期'] < 999)]

数据归一化

# 归一化
scaler_to = preprocessing.MinMaxScaler()
scaler_to = scaler_to.fit(data)
data_to = scaler_to.transform(data)
data_to = pd.DataFrame(data_to)
data_to.columns = data.columns

划分数据集

# 划分训练集与测试集
X_train,X_valid,y_train,y_valid=train_test_split(data_to.iloc[:,1:],data_to.iloc[:,0],test_size=0.1,random_state=42)
X_train.shape,X_valid.shape

数据张量转换

# 转换为张量
x_train_to = torch.tensor(X_train.values).to(torch.float)
y_train_to = torch.tensor(y_train.values).to(torch.float)
x_train_to.shape

构建神经网络

  • 因为数据比较简单,这里构建单层全连接神经网络
class linreg(nn.Module):
    def __init__(self,input_shape,linear,output_shape):
        super(linreg,self).__init__()
        self.l1 = torch.nn.Linear(input_shape,linear)
        self.l2 = torch.nn.Linear(linear,output_shape)
    def forward(self,x):
        x = F.relu(self.l1(x))
        return self.l2(x)

训练模型函数

  • 可以看到这里需要调整的就是linear,即单层神经元个数。还有迭代次数epochs
  • 值得注意的是,所有需要调整的参数都以字典的形式代替,如config['linear']
def train_model(config):
	# 需要调整的参数:神经元个数
    model = linreg(x_train_to.shape[1],int(config['linear']),1)
    # 选择MSE作为误差衡量标准
    criterion = torch.nn.MSELoss()
    # 优化器选择adam,学习率为0.001,衰减为0.001
    optimizer = optim.Adam(model.parameters(),
                           lr=0.001,
                           betas=(0.9, 0.999),
                           eps=1e-08,
                           weight_decay=0.001)
    # 需要调整的参数:迭代次数
    epochs = int(config['times'])
    # 收集误差信息
    loss_list = []
    for epoch in range(epochs):
        epoch += 1
        optimizer.zero_grad()
        outputs = model(x_train_to)
        loss = criterion(outputs,y_train_to)
        loss.backward()
        optimizer.step()
        loss_list.append(loss.detach().numpy())
    # 因为ray包只接受一个优化指标
    # 所以这里对误差取平均值,这样一组参数只有一个评价分数
    # 注意这里的my_loss,该变量为评价变量
    tune.report(my_loss=np.mean(loss_list))

ray网格搜索调参

定义搜索空间

  • 搜索空间由ray包提供,包含随机取样、均匀取样等等方法,详细可以参考
config = {"linear": tune.grid_search(list(np.arange(2,6,1))),
         "times":tune.grid_search(list(np.arange(50,250,10)))}
result = tune.run(train_model,
				  # CPU使用核心
                  resources_per_trial={"cpu": 8},
                  # 搜索空间
                  config=config)
print("======================== Result =========================")
print(result.results_df)

输出最优参

  • 使用get_best_config()方法输出评价变量最小的参数对
result.get_best_config(metric="my_loss", mode="min")

ray贝叶斯优化调参

  • ray可以调用贝叶斯优化,等搜索算法,详情可以参考
  • 贝叶斯优化参数必须是连续的,这个地方定义搜索空间有两种方式,一种是使用ray自带搜索空间定义方法,在使用贝叶斯优化时,ray会将搜索空间自动转换为连续。一种是使用贝叶斯优化包自带的搜索空间方法。这里推荐后面一种
  • 我们知道当模型收敛时,增加迭代次数loss曲线也不会在有大幅度变化,这里为了节省训练时间,使用早停策略,当评价目标my_loss在3次迭代中变化标准差小于0.001,则停止训练。
  • 更多调度机制早停机制,可以参考官方文档
from ray.tune.suggest.bayesopt import BayesOptSearch
# 定义搜索空间
space = {'linear': (2, 6),
         'times': (50, 200)}
# 定义评价目标,最大/最小化目标
bayesopt = BayesOptSearch(space,metric="my_loss",mode="min")
result = tune.run(train_model,
                  resources_per_trial={"cpu": 8},
                  # 定义评价目标,最大/最小化目标,容忍次数
                  stop = EarlyStopping(metric = "my_loss",
                                       mode = "min",
                                       patience = 3),
                  search_alg=bayesopt,
                  # 迭代次数(贝叶斯优化次数)
                  num_samples=30)
print("======================== Result =========================")
print(result.results_df)

输出最优参

  • 使用get_best_config()方法输出评价变量最小的参数对
result.get_best_config(metric="my_loss", mode="min")

输出

{'linear': 3.49816047538945, 'times': 192.60714596148742}
  • 输出loss值:
result.get_best_trial(metric="my_loss", mode="min").last_result["my_loss"]

输出

0.023971058

使用最优参构建模型

def train_model():
    model = linreg(x_train_to.shape[1],3,1)
    criterion = torch.nn.MSELoss()
    optimizer = optim.Adam(model.parameters(),
                           lr=0.001,
                           betas=(0.9, 0.999),
                           eps=1e-08,
                           weight_decay=0.001)
    epochs = 192
    loss_list = []
    for epoch in range(epochs):
        epoch += 1
        optimizer.zero_grad()
        outputs = model(x_train_to)
        loss = criterion(outputs,y_train_to)
        loss.backward()
        optimizer.step()
        loss_list.append(loss.detach().numpy())
    plt.figure(dpi = 600)
    plt.plot(loss_list)
    plt.xlabel("epoch")
    plt.ylabel("loss")
    print("Loss: {}".format(loss_list[-1]))
train_model()

输出

Loss: 0.023285632953047752

请添加图片描述

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羽星_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值