一.前言

最近在朋友的介绍下,了解了一个神经网络的调参神器——微软开发的NNI (Neural Network Intelligence),在经过简单尝试之后,发现是真的香。倘若你也苦于每次炼丹都要手动设置超级参数,那你可以选择尝试一下NNI,从此告别繁杂重复的调参参数手动设置。话不多说,直接上干货。

二.NNI怎样帮助调参的?

对于人工智能从业者,炼丹可谓是一门看家本领,但是通常炼丹是一项十分痛苦的事情。一般来说,我们使用的模型都具有很多的超级参数,那究竟怎样的超级参数组合才能使得模型效果最佳呢?我们往往需要进行大量的尝试,对不同参数设置不同的值进行组合,对于每次组合进行一次实验,然后再设置另一组参数进行实验,如此反复直到找到符合理想的一组参数。

那么,我们能不能直接一次性设置多组参数,然后让程序自动跑完一组然后继续跑下一组呢?

当然可以,直接写个脚本也能做到。或者尝试使用NNI,通过NNI我们可以设置好超级参数的搜索空间,然后使用其实现的超级参数调优算法去探索一组理想的超参。此外,它还提供了可视化的WEB界面,能够让我们实时观测程序的运行状态和搜索空间的搜索状态等等。

2.1 NNI调参的工作流程

那么,NNI是怎样做到帮我们自动的探索超参的搜索空间呢,其工作流程大概如下:

Input: 搜索空间(search space), 实验代码(trial code), 配置文件(config file)
Output: 一组最优的超级参数(hyperparameter)配置1: For t = 0, 1, 2, ..., maxTrialNum(配置文件设置的最大实验次数),
2:      hyperparameter = chose a set of parameter from search space //从搜索空间选择对应的一组参数
3:      final result = run_trial_and_evaluate(hyperparameter) //利用这组参数进行实验,并返回最终的结果
4:      report final result to NNI //将本组超参的最终结果报告给NNI
5:      If reach the upper limit time: // 达到了设置实验运行时间的上限,
6:          Stop the experiment //停止实验
7: return hyperparameter value with best final result // 返回最佳的一组超级参数

了解了其工作流程以后,接下来,博主将代领大家利用NNI进行实践了。

三.NNI调参实践

3.1 NNI的安装

NNI支持如下主流的操作系统:

Ubuntu >= 16.04
macOS >= 10.14.1
Windows 10 >= 1809

其安装命令如下:

# Linux或MacOS
python3 -m pip install --upgrade nni# Windows
python -m pip install --upgrade nni

安装完成后,若想验证是否安装成功,可以参见官方的提供的示例:

git clone -b v2.6 https://github.com/Microsoft/nni.git

若git clone失败,可以直接把后面的链接贴到浏览器去手动下载。

然后,运行官网的例子:

# Linux或MacOS
nnictl create --config nni/examples/trials/mnist-pytorch/config.yml# Windows
nnictl create --config nni\examples\trials\mnist-pytorch\config_windows.yml

即在--config后面加上trials目录下minst-pytorch目录下的相应配置文件即可。

若运行成功,可以看到命令行输出的Web UI URLs,选择本机IP:8080,即http://127.0.0.1:8080在浏览器打开就可看到Web界面。

3.2 实验代码说明

本次使用的实验代码是一个利用Resnet网络来对一个汽车分类数据集进行分类的Demo。因为只是尝试下NNI,在实验过程中只探索了最常用的三个超级参数:

batch size
epochs
learning rate

由于需要更新模型中的超级参数,因此引入可变参数

import argparsedef get_params():parser = argparse.ArgumentParser()parser.add_argument("--batch_size", type=int, default=32)parser.add_argument("--epochs", type=int, default=50)parser.add_argument("--lr", type=float, default=0.001)parser.add_argument("--use_cuda", action='store_true', default=False)parser.add_argument('--seed', type=int, default=1, metavar='S',help='random seed (default: 1)')args, _ = parser.parse_known_args()return argsif __name__ == "__main__":pass

其实也可以直接简单定义一个以超参变量名为键的字典这里是因为笔者在已有代码上进行修改的缘故才如此)。由于使用NNI,因此只需要将NNI得到的参数名和参数值更新到args中,即:

tuner_params = nni.get_next_parameter()
params = vars(merge_parameter(get_params(), tuner_params))

上述代码中merge_parameter()方法可以将nni中的参数”添加“到args中去,然后通过vars()函数,可以使得我们在实验代码中像用字典一样取对应的超参,例如:

for epoch in range(args['epochs'])

根据2.1节,在使用NNI时,我们还需要向NNI报告当前结果以及报告最终结果,即:

nni.report_intermediate_result(test_acc)nni.report_final_result(test_acc)

完成主程序的代码,仅供参考:

import nni
import torch
import torch.nn as nn
import torch.utils.data as data
import torch.optim as optim
from data_loader import CarDataset
from model.resnet18 import Resnet18
from configuration import get_params
from nni.utils import merge_parameter
import logginglogger = logging.getLogger('resnet_AutoML')def train(model,train_iter,loss_fn,optimizer,device):"""功能:训练模型"""model.train()train_l_sum, train_acc_sum, n, c = 0,0,0,0for x,y in train_iter:x,y = x.float().to(device),y.to(device)y_hat = model(x)l = loss_fn(y_hat,y)# 梯度清零optimizer.zero_grad()# 反向传播l.backward()# 更新参数optimizer.step()train_l_sum += l.item()train_acc_sum += (y_hat.argmax(dim = 1) == y).sum().item()n += y.shape[0]c += 1return train_acc_sum / n, train_l_sum / cdef evaluate_accuracy(model,test_iter,loss_fn,device):"""功能:在测试集上进行测评"""model.eval()test_l_sum,test_acc_sum,n,c = 0.0,0,0,0with torch.no_grad():for x,y in test_iter:x,y = x.float().to(device),y.to(device)y_hat = model(x)test_l_sum += loss_fn(y_hat,y)test_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()n += y.shape[0]c += 1return test_acc_sum / n, test_l_sum / cdef main(args):# 是否启用GPU加速use_cuda = args['use_cuda'] and torch.cuda.is_available()torch.manual_seed(args['seed'])# 定义GPU设备device = torch.device("cuda:0" if use_cuda else "cpu")# 加载数据 train_dataset = CarDataset(data_path="car3/train")test_dataset = CarDataset(data_path="car3/test")train_iter = data.DataLoader(dataset=train_dataset,batch_size=args['batch_size'],shuffle=True,num_workers=4)test_iter = data.DataLoader(dataset=test_dataset,batch_size=args['batch_size'],shuffle=False,num_workers=4)# 加载Resnet模型    model = Resnet18(class_num=3).to(device)# 设定交叉熵作为损失函数loss_fn = nn.CrossEntropyLoss()# 选择Adam优化器optimizer = optim.Adam(params=model.parameters(), lr=args['lr'])for epoch in range(args['epochs']):train_acc, train_loss = train(model, train_iter, loss_fn, optimizer, device)test_acc, test_loss = evaluate_accuracy(model, test_iter, loss_fn, device)print("Epoch {}: train_acc: {:.6} train_loss: {:.4} test_acc: {:.6} test_loss: {:.4}".format(epoch, train_acc, train_loss, test_acc, test_loss))nni.report_intermediate_result(test_acc)logger.debug('test accuracy {:.4f}'.format(test_acc))logger.debug('Pipe send intermediate result done.')# 报告最终结果nni.report_final_result(test_acc)logger.debug('Final result is %g', test_acc)logger.debug('Send final result done.')if __name__ == "__main__":try:tuner_params = nni.get_next_parameter()logger.debug(tuner_params)params = vars(merge_parameter(get_params(), tuner_params))# print(type(params))main(params)except Exception as exception:logger.exception(exception)raise

3.4 搜索空间配置

使用NNI时,我们需要创建一个搜索空间配置文件,可以为YAML文件或JSON文件,在其中我们可以设置我们使用的超级参数的取值,展示采用JSON格式定义的搜索空间:

{"batch_size": {"_type":"choice", "_value": [16, 32, 64]},"epochs":{"_type":"choice","_value":[20, 30]},"lr":{"_type":"choice","_value":[0.0001, 0.001, 0.01]}
}

3.5 实验配置

除了搜索空间外,还可以指定实验的关键信息,例如实验文件、调参算法等,配置可以采用YAML文件,对应的示例如下:

experimentName: CIFAR10
searchSpaceFile: search_space.json # 指定搜索空间文件
trialCommand: python main.py --use_cuda # 实验运行shell命令
experimentWorkingDirectory: nni-experiments # NNI记录实验日志的目录
trialConcurrency: 1
tuner: # 指定调参算法name: TPEclassArgs:optimize_mode: maximize
trainingService: # 本地运行platform: local

3.5 运行结果展示

进入命令行,切换到实验代码所在的目录后输入如下命令:

nnictl create --config exp_config.yaml

便可看到命令行的输出如下:

在浏览器输入localhost:8080便可以看到如下主界面:

点击Trials detail即可看到具体的实验细节,包括有实验参数的超参组别:

以及各个实验报的中间结果(这里为测试集上的准确率):

四.结语

本文只是NNI调参的入门,具体细节请参见如下资源:

  • NNI 微软官方Github地址
  • NNI使用官方教程

项目的完整源码地址:NNI自动调参示例程序 (有条件的支持一下)
以上便是本文的全部内容,要是觉得不错的话,可以点个赞或关注一下博主,后续还会持续带来各种干货,当然要是有问题的话也请批评指正!!!

使用NNI,从此告别手动调参相关推荐

  1. #####好好好####从Google Visor到Microsoft NNI再到Advisor调参服务接口发展史

    从Google Visor到Microsoft NNI再到Advisor调参服务接口发展史 tobe Contributor to the world. 10 人赞了该文章 介绍 从规则编程到机器学习 ...

  2. 谷歌重磅:可以优化自己的优化器!手动调参或将成为历史!?

    文 | 小轶 编 | 夕小瑶 背景 Google Brain团队发布的一篇最新论文在外网引发热议,或将成为Deep Learning发展历程上里程碑式的工作.它所讨论的,是所有AI行业者都要面对的-- ...

  3. 自动机器学习新进展!性能超过人类调参师6个点,AutoGluon 低调开源

    机器之心报道 机器之心,Datawhale编辑 自动机器学习效果能有多好?比如让 MobileNet1.0 backbone 的 YOLO3 超过 ResNet-50 backbone 的 faste ...

  4. 调参到头秃?你需要这份自动超参搜索技术攻略

    点击上方,选择星标或置顶,不定期资源大放送! 阅读大概需要13分钟Follow小博主,每天更新前沿干货 来源:PaperWeekly本文约4845字,建议阅读9分钟本文介绍了自动超参搜索的系统架构及技 ...

  5. Lesson 6.5Lesson 6.6.1Lesson 6.6.2 机器学习调参基础理论与网格搜索多分类评估指标的macro与weighted过程GridSearchCV的进阶使用方法

    Lesson 6.5 机器学习调参基础理论与网格搜索 在上一小节执行完手动调参之后,接下来我们重点讨论关于机器学习调参的理论基础,并且介绍sklearn中调参的核心工具--GridSearchCV. ...

  6. Xgboost调参小结

      XGBoost全称是eXtreme Gradient Boosting,由陈天奇所设计,和传统的梯度提升算法相比,XGBoost进行了许多改进,它能够比其他使用梯度提升的集成算法更加快速.关于xg ...

  7. 加载svr模型_机器学习XGBoost实战,网格搜索自动调参,对比随机森林,线性回归,SVR【完整代码(含注释)+数据集见原文链接】...

    建议:阅读2020.8.7的文章,完全了解GDBT和XGBT的原理. 机器学习- XGBoost,GDBT[过程:决策树,集成学习,随机森林,GDBT,XGBT,LightGBM] 本次实践内容: 数 ...

  8. 【机器学习基础】一文归纳AI调参炼丹之法

    1  超参数优化 调参即超参数优化,是指从超参数空间中选择一组合适的超参数,以权衡好模型的偏差(bias)和方差(variance),从而提高模型效果及性能.常用的调参方法有: 人工手动调参 网格/随 ...

  9. 自动化机器学习(AutoML)之自动贝叶斯调参

    一.Python实现自动贝叶斯调整超参数 [导读]机器学习中,调参是一项繁琐但至关重要的任务,因为它很大程度上影响了算法的性能.手动调参十分耗时,网格和随机搜索不需要人力,但需要很长的运行时间.因此, ...

最新文章

  1. ssl mybatis实现数据库字段的加解密
  2. linux重置网络协议,Linux 内核网络协议栈 ------ tcp_ack 函数处理接收到的ACK包之后 ....
  3. android关机菜单修改,Android4.4关机菜单添加重启系列选项
  4. Go gin使用html模板
  5. boost::format模块一些真实的、简单的测试
  6. 如何将Pcm格式的音频文件转换成Wave格式的文件
  7. ssl提高组周六模拟赛【2018.9.22】
  8. pdfbox java.lang.outofmemoryerror_Apache PDFBox 1.8.11 发布,Java 的 PDF 处理类
  9. swoole 捕捉php错误,swoole怎么处理错误
  10. Ext.Window更换iframe的地址
  11. TensorFlow推出命令式、可定义的运行接口Eager Execution
  12. 常见Linux命令(非文件操作)
  13. Vscode的SSH插件远程连接Linux
  14. 无限级分类处理成树形结构
  15. CentOS 8: No URLs in mirrorlist error
  16. Tapestry学习4
  17. Mac上键入数学符号怎样输入
  18. 【agc004e】Salvage Robots
  19. 先帝爷下南阳御驾三请,联东吴灭曹魏鼎足三分~~~
  20. windows tcpdump

热门文章

  1. Java网络编程之MINA框架(1)
  2. 『杭电1173』采矿
  3. 02 Dask源码剖析-Dask的数据模型-Bag
  4. https nginx 换成其他端口_nginx 80端口下HTTPS请求跳转到指定其他端口
  5. 阿里云设置登录掩码无法登录
  6. 机器学习-贝叶斯公式
  7. android学习笔记之JNI
  8. 扇形统计图、折线统计图数据统计
  9. 外贸开发信退信严重,找EmailCamel解决!
  10. php傻瓜集合包,护卫神·PHP套件集合包