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

前言

  • 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

请添加图片描述

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年6月15日 上午11:23
下一篇 2022年6月15日 上午11:25

相关推荐