来评估评估你训练的机器人是否够聪明
tiancorn 发布于2018-08-03 02:28 浏览:823 回复:3
6
收藏
最后编辑于2018-08-07

      在UNIT平台上配置技能意图,配置对话模板、标注对话样本完成特定场景的BOT对话模型训练后 如果要把BOT应用到实际业务中还需要系统科学地对当前BOT的对话理解效果进行测试评估,进而对“是否还需要进一步优化、是否可以上线”进行判断和决策。

下面介绍一套BOT对话理解测评方案,帮助大家完成测评的工作,以保障BOT上线效果和用户体验。

1、对话意图词槽理解效果评估
1.1、从实际场景中随机富集评估样本集
评估样本集要求

覆盖BOT所有意图,且包含真实负例,从实际业务场景中去随机富集并标注后 再调整,比如增加随机样本中缺少的某些新功能意图的样本。
评估集中不可包含训练集中的任何样本。

评估指标:

整体Precision = 意图和词槽都识别正确的正例样本数 / 模型识别为正例的总数
整体recall = 意图和词槽都识别正确的正例样本数 / 评估样本中所有正例样本数
意图Precision = 意图都识别正确的正例样本数 / 意图都识别的所有正例样本数
意图Recall = 意图都识别正确的正例样本数 / 评估样本中所有正例样本数

评估集文件格式

天气怎么样?    ASK_WEATHER
周六天气好吗?    ASK_WEATHER      user_time:周六
杭州明天是什么天气?    ASK_WEATHER     user_loc:杭州###user_time:明天
你好       SYS_OTHER

一行为一个样本,顺序依次为:query,意图,词槽。三者之间以’\t’分隔,词槽之间以’###’分隔,编码为utf-8。再次提醒,评估样本建议为真实场景下的样本,请勿凭空构造。

评估集中的每条样本都要标注意图、词槽(没有词槽的只标意图),其中正例样本是指需要识别为场景中意图的样本,例如上面评估查天气的BOT的评估样本中意图为ASK_WEATHER的都是正例样本。而对于当前bot对应场景不应该识别的对话 都可以标为负例样本,同系统预置的负例意图SYS_OTHER 表示。

 
1.2、评估脚本,下载后可以与评估样本放在python的根目录下

# -*- coding:utf-8 -*-
import urllib2
import ssl
import json
def 
 evaluate(f_in,url,bot_id,user_id,f_out="evaluate_result",bernard_level=0,version="2.0",log_id="0",bot_session="",client_session="{\"client_results\":\"\", \"candidate_options\":[]}",source="KEYBOARD",type="TEXT",update="",asr_candidates=[]) :
    try :
        file_input=open(f_in,'r')
    except :
        print '无法打开文件 %s'%f_in
        exit()
    file_out=open(f_out,'w')
    prase_data = {
        "bot_session":bot_session,
        "log_id":log_id,
        "request":{
            "bernard_level":bernard_level,
            "query":"",
            "query_info":{
                "asr_candidates":asr_candidates,
                "source":source,
                "type":type
            },
            "updates":update,
            "user_id":user_id,
            "client_session":client_session
        },
        "bot_id":bot_id,
        "version":version
    }
    requrl =url 
    right_think=0
    right_word=0
    right_samples=0
    right_prediction=0
    number_cacul=0
    for row in file_input :
        if row :
            fail=0
            sentance=row.strip().split('\t')
            sentance.append('')
            prase_data["request"]["query"]=sentance[0]
            try :
                request =urllib2.Request(requrl, json.dumps(prase_data, ensure_ascii = False, encoding='utf-8'))
                request.add_header('Content-Type', 'application/json')
                response = urllib2.urlopen(request,timeout=3)
                content = response.read().rstrip('\r\n')
            except :
                if fail<3 :
                    fail+=1
                else :
                    file_out.write(sentance[0]+'\t'+'SYS_ERROR_URL'+'\n')
            if content:
                bot_resp=json.loads(content)
                try :
                    res=bot_resp['result']['response']['schema']
                    slot_str=''
                    for slot in res["slots"]:
                        slot_str += slot["name"] + ':' +  slot["original_word"] + '###'
                    slot_str=slot_str.encode('UTF8').rstrip('#')
                except :
                    file_out.write(sentance[0]+'\t'+'url返回错误'+sentance[1]+'\t'+sentance[2]+'\t'+'SYS_ERROR_JSON'+content+'\n')
                    continue
                number_cacul+=1
                if not (sentance[1]=='SYS_OTHER' or sentance[1]==''):
                    right_samples+=1
                    s1='正例样本'
                else :
                    s1='负例样本'
                if sentance[1]==res['intent'] :
                    s2=',意图正确'
                    right_think+=1
                else :
                    s2=',意图错误'
                word_l=sentance[2].split('###')
                word_l.sort()
                word_r=slot_str.split('###')
                word_r.sort()
                if len(word_l)==len(word_r):
                    task=0
                    for i in range(len(word_l)) :
                        if  word_l[i]!=word_r[i] :
                            task=1
                            break
                    if not task :
                        s3='词槽正确'
                        if s2==',意图正确' :
                            right_word+=1
                    else :
                        s3='词槽错误'
                if not (res['intent']=='SYS_OTHER' or res['intent']==''):
                    right_prediction+=1
                    s4='预测为正例'
                else :
                    s4='预测为负例'
                file_out.write(sentance[0]+'\t'+s1+s4+s2+s3+'\t'+sentance[1]+'\t'+sentance[2]+'\t'+res['intent'].encode('UTF8')+'\t'+slot_str+'\n')
    file_input.close()
    file_out.close()
    file_out=open(f_out,'r+')
    content=file_out.read()
    file_out.seek(0,0)
    write_lines=''
    if number_cacul == 0 :
        write_lines+='对样本进行预测失败,请检查您的配置\n'
        print '对样本进行预测失败,请检查您的配置' 
        file_out.close()
        exit()
    write_lines+='已成功对%d个样本进行预测\n'%number_cacul
    print '已成功对%d个样本进行预测'%number_cacul  
    if right_samples == 0 :
        write_lines+='error:识别为正例样本数为0,无法计算准确率\n'
        print 'error:识别为正例样本数为0,无法计算准确率'
    else :
        write_lines+='整体准确率:%.2f(%d/%d)\n'%(float(right_word)/float(right_prediction)*100,right_word,right_prediction)
        write_lines+='意图准确率:%.2f(%d/%d)\n'%(float(right_think)/float(right_prediction)*100,right_think,right_prediction)
        print '整体准确率:%.2f(%d/%d)'%(float(right_word)/float(right_prediction)*100,right_word,right_prediction)
        print '意图准确率:%.2f(%d/%d)'%(float(right_think)/float(right_prediction)*100,right_think,right_prediction)
    if right_samples == 0 :
        write_lines+='error:评估集合中正例样本数为0,无法计算召回率\n'
        print 'error:评估集合中正例样本数为0,无法计算召回率'
    else :
        write_lines+='整体召回率:%.2f(%d/%d)\n'%(float(right_word)/float(right_samples)*100,right_word,right_samples)
        write_lines+='意图召回率:%.2f(%d/%d)\n'%(float(right_think)/float(right_samples)*100,right_think,right_samples)
        print '整体召回率:%.2f(%d/%d)'%(float(right_word)/float(right_samples)*100,right_word,right_samples)
        print '意图召回率:%.2f(%d/%d)'%(float(right_think)/float(right_samples)*100,right_think,right_samples)
    file_out.write(write_lines+content)
    file_out.close()

https://pan.baidu.com/s/149obA3W0WpBWOavuJCEYXQ  上面的评估脚本可以直接从这里下载

然后,获取自己的access_token,bot_id和user_id,具体参见https://ai.baidu.com/docs#/UNIT-v2-API/top。

获取access_token前先获取  API Key / Secret Key :

然后在用API Key / Secret Key 按照 http://ai.baidu.com/docs#/Auth/top  的方法获取Access Token。

有了输入文件名、url地址、bot_id 和 user_id四个参数就可以调用函数获得评估结果了。示例程序如下:

import bot_evaluation
my_access_token="xxxxxxxxxx"
my_user_id="xxxxxx"
my_bot_id=8888
my_f_in="xxx.xxx"
url_address='https://aip.baidubce.com/rpc/2.0/unit/bot/chat?access_token'
my_url=url_address+my_access_token
bot_evaluation.evaluate(f_in=my_f_in,url=my_url,bot_id=my_bot_id,user_id=my_user_id)

 运行完成之后,stdout有四个所求数值的的输出,如:

此外,也会把结果和中间数据存储到一个文件中,可自主定义该文件的文件名,输出文件格式如下:

注:若运行过程中url请求出错,无法获得响应,会在输出文件中的该样本处打印 SYS_ERROR_URL,若返回结果为错误信息,则在输出文件中的该样本处打印SYS_ERROR_JSON和错误信息。

参数列表

              必需参数:

                     f_in:输入文件名

                     url:bot 的url请求地址

                     bot_id:在『我的BOT』列表中查看

                     user_id:可写dev_botid

              可选参数:

                     f_out,默认为”access_result”

                     bernard_level,默认为0

                     version,默认为”2.0”

                     log_id,默认为”0”

                     bot_session,默认为空    

                     client_session,默认为”{\"client_results\":\"\", \"candidate_options\":[]}"

                     source,默认为”KEYBOARD”

                     type,默认为”TEXT”

                     update,默认为空

                     asr_candidates,默认为空

       (对话API 参见https://ai.baidu.com/docs#/UNIT-v2-API/top )

2、查看评估结果,判断可上线还是需继续优化
     使用评估结果结合业务的实际要求,综合判断BOT理解效果是否可以达到上线标准。
如判断达不到上线标注可以review测评的详细数据,分析具体是哪些意图、词槽的召回和识别有问题,分析问题的类型分类,并进行有针对的优化,比如有针对性的配置对话模板、标注更多对话样本强化训练。

 

 

收藏
点赞
6
个赞
共3条回复 最后由SB呵呵风在笑回复于2018-08-07 16:46
#4SB呵呵风在笑回复于2018-08-07

前排围观。

 

0
#3爱上主丽叶回复于2018-08-06

好东西,用的时候可以直接拿过来copy了。大赞!

0
#2伊茨米可回复于2018-08-03

硬货十足!

0
TOP
切换版块