使用NNI的LSTM情感分析例子分析

构建NNI实际就是在原有的深度学习程序基础上添加一个.yml的配置文件和搜索空间即可(其中定义搜索空间有两种方式:1.非注释型—添加.json文件,2.注释型—直接在源码中添加注释)

1、注释方式

由于这些新添加的代码是注释,仍然可以在没有安装NNI的环境中照常运行代码。而且有些代码需要调整没有整理的比较好,都是定义在代码中间,这种方式可以直接进行修改,个人比较喜欢该方式。本实例使用LSTM情感分析算法的修改

1.1 使用注释更新代码

其中主要用到了一下函数

  1. @nni.variable将在其后续行生效,该行是一个赋值语句,其左值必须由关键字name=指定@nni.variable。
  2. @nni.report_intermediate_result/ @nni.report_final_result将数据发送给该线的评估员/调谐器。
import tensorflow as tf
import numpy as np
from random import randint
import time
import re
import argparse
import nni
import math
import logging
from os import listdir
from os.path import isfile,join
import matplotlib.pyplot as plt
def getTrainBatch(batchSize,maxSeqLength,ids):#从文件总获取训练集
    labels=[]
    arr = np.zeros([batchSize,maxSeqLength])
    for i in range(batchSize):
        if (i%2)==0:
            num = randint(1,11499)
            labels.append([1,0])
        else:
            num = randint(13499,24999)
            labels.append([0,1])
        arr[i] = ids[num-1:num]
    return arr,labels
def getTestBatch(batchSize,maxSeqLength,ids):#从文件中获取数据集
    labels = []
    arr = np.zeros([batchSize, maxSeqLength])
    for i in range(batchSize):
        num = randint(11499,13499)
        if (num <= 12499):
            labels.append([1,0])
        else:
            labels.append([0,1])
        arr[i] = ids[num-1:num]
    return arr, labels
def main():
    maxSeqLength = 250
    '''@nni.variable(nni.choice(15,20,24,30),name = batchSize)'''
    batchSize = 24
    '''@nni.variable(nni.choice(50,100,200,250,300),name = numDimensions)'''
    numDimensions = 250
    '''@nni.variable(nni.choice(32,64,128,256),name = lstmUnits)'''
    lstmUnits = 64
    numClasses = 2
    iterations = 50000
    wordsList = np.load(r'wordsList.npy')
    wordsList = wordsList.tolist()
    wordsList = [word.decode('UTF-8') for word in wordsList]#进行解码,这一步很重要,不然回报word not in vocab错误
    wordVectors = np.load(r'wordVectors.npy')
    ids = np.load(r'idsMatrix.npy')
    tf.reset_default_graph()
    input_data = tf.placeholder(tf.int32,shape=[batchSize,maxSeqLength])#占位符,必不可少
    labels = tf.placeholder(tf.int32,shape=[batchSize,numClasses])#根据其格式来写
    data = tf.Variable(tf.zeros([batchSize, maxSeqLength, numDimensions]), dtype=tf.float32)
    data = tf.nn.embedding_lookup(wordVectors, input_data)#按照索引来找相应的词向量
    data = tf.cast(data, tf.float32)#由于版本的问题,这一步必不可少,将x的数据格式转化成dtype,有的版本可以不写
    #LSTM网络的构建
    lstmCell = tf.contrib.rnn.BasicLSTMCell(lstmUnits)#lstmUnits为cell中隐藏层神经元的个数
    lstmCell = tf.contrib.rnn.DropoutWrapper(cell=lstmCell, output_keep_prob=0.75)
    value, _ = tf.nn.dynamic_rnn(lstmCell, data, dtype=tf.float32)
    weight = tf.Variable(tf.truncated_normal([lstmUnits, numClasses]))
    bias = tf.Variable(tf.constant(0.1, shape=[numClasses]))
    value = tf.transpose(value, [1, 0, 2])#value的输出为[batchsize,length,hidden_size]
    #也可以写成 value, states = tf.nn.dynamic_rnn(lstmCell, data, dtype=tf.float32),vale=states[-1],可以先打印出states的shape再进行判断
    last = tf.gather(value, int(value.get_shape()[0]) - 1)#上述对value做这种变化与注释中states[-1]结果相似
    prediction = (tf.matmul(last, weight) + bias)#可以再加上softmax层
    correctPred = tf.equal(tf.argmax(prediction, 1), tf.argmax(labels, 1))#与标签进行判断
    accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=labels))#计算交叉熵
    '''@nni.variable(nni.choice(0.01,0.001,0.0001),name = lr)'''
    lr=0.001
    optimizer = tf.train.AdamOptimizer().minimize(loss)#随机梯度下降最小化loss
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())#初始化变量
        accuracy_=0
        loss_=0
        for i in range(iterations):
            nextBatch, nextBatchLabels = getTrainBatch(batchSize,maxSeqLength,ids)
            sess.run(optimizer, {input_data: nextBatch, labels: nextBatchLabels})
 
            if (i % 1000 == 0 and i != 0):
                loss_ = sess.run(loss, {input_data: nextBatch, labels: nextBatchLabels})
                accuracy_ = sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels})
 
                print("iteration {}/{}...".format(i + 1, iterations),
                      "loss {}...".format(loss_),
                      "accuracy {}...".format(accuracy_))
                '''@nni.report_intermediate_result(accuracy_)'''
        '''@nni.report_final_result(accuracy_)'''

if __name__ =="__main__":
    main()

在LSTM情感分析中除了一般RNN考虑的损失率,LSTM单元数量以外词向量维度也是一个重要的参数,因此对上面代码中的参数batchSize,numDimensions,lstmUnits,lr进行了搜索定义

其中的nni.choice表示从参数中选择值

除此以外还有以下类型:

  • {"_type":"randint","_value":[upper]}:变量值是[0,upper]范围内的随机整数
  • {"_type":"uniform","_value":[low, high]}:变量值是低和高之间的值
  • {"_type":"quniform","_value":[low, high, q]}:变量值是round(uniform(low, high) / q) * q中的分布
  • {"_type":"loguniform","_value":[low, high]}:变量值是exp(uniform(log(low), log(high)))中的分布
  • {"_type":"qloguniform","_value":[low, high, q]}:变量值是round(loguniform(low, high)) / q) * q中的分布
  • {"_type":"normal","_value":[label, mu, sigma]}:分布为均值μ和标准差sigma
  • {"_type":"qnormal","_value":[label, mu, sigma, q]}:变量值为round(normal(mu, sigma) / q) * q的分布,适用于可能在mu附近取值的离散变量,但基本上是无界的
  • {"_type":"lognormal","_value":[label, mu, sigma]}:变量值为 exp(normal(mu, sigma))的分布,返回值的对数是正态分布的
  • {"_type":"qlognormal","_value":[label, mu, sigma, q]}:变量值为round(exp(normal(mu, sigma)) / q) * q的分布,适用于离散变量

 

1.2 配置文件编写

官方地址:https://github.com/Microsoft/nni/blob/master/docs/ExperimentConfig.md#Configuration

confing.yml文件内容

authorName: zcz #作者名称
experimentName: example_LSTM #项目名称
trialConcurrency: 1 #同时运行的最大试用作业数
maxExecDuration: 1d #实验持续的最长时间 s,m,h,d 
maxTrialNum: 10 #最大实验次数,包括成功和失败的次数
trainingServicePlatform: local #运行实验的平台
useAnnotation: true #是否使用注释法生成搜索空间
tuner: #调谐器选择
  #可以选择 TPE, Random, Anneal, Evolution, BatchTuner
  builtinTunerName: TPE
  classArgs: #指定调谐器算法的参数
    optimize_mode: maximize
  gpuNum: 2
trial:
  command: python3 lstm.py #指定运行试验过程的命令
  codeDir: . #指定您自己的试用文件的目录
  gpuNum: 2 #指定运行试验过程的gpu数。默认值为0

文件中的注释说明已经很清楚了

除此以外NNI的配置文件还有一些其他的功能配置,如上面说的该配置文件使用的注释法生成搜索空间,如果使用非注释法只需要把useAnnotation改为false,添加搜索空间地址searchSpacePath

运行试验平台(trainingServicePlatform)除了local以外还可以使用remote,pai,kubeflow 

local:本地运行

remote:远程服务器运行,并且应该归档machineList字段以便建立与远程机器的SSH连接

pai:向Microsoft的OpenPai提交试用工作

kubeflow:将试验作业提交给kubeflow
tuner:NNI自带了一些SDK,其中包括TPE, Random, Anneal, Evolution, BatchTuner, GridSearch,同时支持自定义的tuner配置

1.3 结果分析

上面已经配置好,直接运行

nnictl create --config config.yml

然后在浏览器中输入ip:8080,可以看到运行情况

1.3.1 主页

上半部分包括三个模块,分别是实验运行情况,搜索空间和配置文件

下半部分包括作业前十的指标及对其性能进行排序

1.3.2 超级参数

超极参数页面,可以发现各个参数改变对最终结果的影响,很明显lstmUnits和batchSize过小影响结果较大

1.3.3 查看试用状态

包括:试用版ID,试用期,开始时间,结束时间,状态,准确性和搜索空间文件

1.3.4 中间结果图

     

通过分析中间结果图发现lstmUnits结果低于128时,值的收敛性及稳定性将大为降低

2 非注释方式

非注释方式是微软给的样例使用的方式,主要包括三部分,1、定义搜索空间文件,2、修改代码,3、配置文件该方法需要修改代码进行更新,这里我使用了一个自己写的简单的RNN进行mnist手写字识别的程序

2.1 定义搜索空间

这里定义搜索空间需要新建一个.json文件,内容如下

{
	"HIDDEN_LAYER":{"_type":"choice","_value":[32,64,128,256,512]},
	"BATCH_SIZE":{"_type":"choice","_value":[32, 64, 128, 256,512]},
	"lr":{"_type":"choice","_value":[0.0001, 0.001, 0.01, 0.1]}
}

2.2 修改代码

代码首先引入nni

然后从tuner获取配置

tuner_params = nni.get_next_parameter()

中间指标数据:nni.report_intermediate_result(指标)

报告配置性能:nni.report_final_result(指标)

修改后代码如下

import tensorflow as tf
import argparse
import nni
import math
import logging
from tensorflow.examples.tutorials.mnist import input_data


def getParam():
	parser = argparse.ArgumentParser()
	parser.add_argument("--HIDDEN_LAYER", type=int, default=128)
	parser.add_argument("--BATCH_SIZE",type=int,default=128)
	parser.add_argument("--INPUTS_NUM",type=int,default=28)
	parser.add_argument("--STEPS_NUM",type=int,default=28)
	parser.add_argument("--CLASS_NUM",type=int,default=10)
	parser.add_argument("--lr",type=float,default=0.001)
	parser.add_argument("--batch_num", type=int, default=2000)
	args, _ = parser.parse_known_args()
	return args

def rnn(x,param):
	weights = {
		'in':tf.Variable(tf.random_normal([param['INPUTS_NUM'],param['HIDDEN_LAYER']])),#28*128
		'out':tf.Variable(tf.random_normal(([param['HIDDEN_LAYER'],param['CLASS_NUM']])))#128*10
	}
	biases = {
		'in':tf.Variable(tf.random_normal([param['HIDDEN_LAYER'],])),#128
		'out':tf.Variable(tf.random_normal(([param['CLASS_NUM'],])))#10
	}

	x=tf.reshape(x,[-1,param['INPUTS_NUM']])
	X_in=tf.matmul(x,weights['in'])+biases['in']
	X_in=tf.reshape(X_in,[-1,param['STEPS_NUM'],param['HIDDEN_LAYER']])

	cell=tf.nn.rnn_cell.BasicLSTMCell(param['HIDDEN_LAYER'],forget_bias=1.0,state_is_tuple=True)

	_init_state = cell.zero_state(param['BATCH_SIZE'],tf.float32)

	output,states = tf.nn.dynamic_rnn(cell,X_in,initial_state=_init_state,time_major=False)
	result = tf.matmul(states[1],weights['out'])+biases['out']
	return result

def main(param):
	print(param['STEPS_NUM'])
	print(param['INPUTS_NUM'])
	mnist=input_data.read_data_sets('/tmp/tensorflow/mnist/input_data',one_hot=True)
	x=tf.placeholder(tf.float32,[None,param['STEPS_NUM'],param['INPUTS_NUM']])
	y=tf.placeholder(tf.float32,[None,param['CLASS_NUM']])
	#定义损失函数
	pred=rnn(x,param)
	cost=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
	train_op = tf.train.AdamOptimizer(param['lr']).minimize(cost)

	#定义模型预测结果及准确率计算方法
	correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
	accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

	with tf.Session() as sess:
		sess.run(tf.global_variables_initializer())
		batch_xs=None
		batch_ys=None
		step=1
		for i in range(param['batch_num']):
			batch_xs, batch_ys = mnist.train.next_batch(param['BATCH_SIZE'])
			batch_xs = batch_xs.reshape([param['BATCH_SIZE'], param['STEPS_NUM'],param['INPUTS_NUM']])
			sess.run(train_op,feed_dict={x:batch_xs,y:batch_ys})
			if i%100==0:
				test_cc=sess.run(accuracy,feed_dict={x:batch_xs,y:batch_ys})
				print(test_cc)
				nni.report_intermediate_result(test_cc)
		test_cc=sess.run(accuracy,feed_dict={x:batch_xs,y:batch_ys})
		nni.report_final_result(test_cc)

if __name__ == '__main__':
	try:
		param=vars(getParam())
		tuner_params = nni.get_next_parameter()
		param.update(tuner_params)
		print("start run")
		main(param)
	except Exception as exception:
		logger.exception(exception)
		raise

 

2.3 配置文件

配置文件相较于上面的注释方式主要修改

useAnnotationfalse 
searchSpacePathsearch_space.json
authorName: zcz
experimentName: example_mnist
trialConcurrency: 1
maxExecDuration: 1h
maxTrialNum: 10
#choice: local, remote, pai
trainingServicePlatform: local
searchSpacePath: search_space.json
#choice: true, false
useAnnotation: false
tuner:
  #choice: TPE, Random, Anneal, Evolution, BatchTuner
  #SMAC (SMAC should be installed through nnictl)
  builtinTunerName: TPE
  classArgs:
    #choice: maximize, minimize
    optimize_mode: maximize
trial:
  command: python3 rnn_mnist.py
  codeDir: .
  gpuNum: 2

3 总结

根据官方解释,NNI是微软提供的一个帮助用户运行自动机器学习(AutoML)实验的工具包。其主要是方便用户进行超参调试

使用的时候发现在定义参数范围时可以尽量选择的范围稍微大一些,上文的程序我主要是依靠论文中建议参数范围进行设置,发现各个参数对其结果影响程度没有体现出来

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值