400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划
作者 | 无名之辈FTER
责编 | 夕颜
出品 | 程序人生(ID:coder_life)
本文翻译自Rasa官方文档,并融合了自己的理解和项目实战,同时对文档中涉及到的技术点进行了一定程度的扩展,目的是为了更好的理解Rasa工作机制。
与本文配套的项目GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,欢迎star和issues,我们共同讨论、学习!
对话管理
1.1 多轮对话
多轮对话是相对于单轮对话而言的,单轮对话侧重于一问一答,即直接根据用户的问题给出精准的答案。问答更接近一个信息检索的过程,虽然也可能涉及简单的上下文处理,但通常是通过指代消解和 query 补全来完成的,而多轮对话侧重于需要维护一个用户目标状态的表示和一个决策过程来完成任务,具体来说就是用户带着明确的目的而来,希望得到满足特定限制条件的信息或服务,例如:订餐,订票,寻找音乐、电影或某种商品等。因为用户的需求可以比较复杂,可能需要分多轮进行陈述,用户也可能在对话过程中不断修改或完善自己的需求。此外,当用户的陈述的需求不够具体或明确的时候,机器也可以通过询问、澄清或确认来帮助用户找到满意的结果。
因此,任务驱动的多轮对话不是一个简单的自然语言理解加信息检索的过程,而是一个决策过程,需要机器在对话过程中不断根据当前的状态决策下一步应该采取的最优动作(如:提供结果,询问特定限制条件,澄清或确认需求,等等)从而最有效的辅助用户完成信息或服务获取的任务。
注:任务驱动的多轮对话系统通常为封闭域(domain)(闲聊系统为开放域),而特定限制条件对应于槽(Slot),也就是说,用户在满足特定限制条件时就是一次槽值填充的过程,如果用户能够在一次会话中,满足全部的限制条件,那么就不必进行多轮对话,即可直接使用户得到满意的信息或服务。
1.2 对话管理
对话管理,即Dialog Management(DM),它控制着人机对话的过程,是人机对话系统的重要组成部分。DM会根据NLU模块输出的语义表示执行对话状态的更新和追踪,并根据一定策略选择相应的候选动作。简单来说,就是DM会根据对话历史信息,决定此刻对用户的反应,比如在任务驱动的多轮对话系统中,用户带着明确的目的如订餐、订票等,用户需求比较复杂,有很多限制条件,可能需要分多轮进行陈述,一方面,用户在对话过程中可以不断修改或完善自己的需求,另一方面,当用户的陈述的需求不够具体或明确的时候,机器也可以通过询问、澄清或确认来帮助用户找到满意的结果。如下图所示,DM 的输入就是用户输入的语义表达(或者说是用户行为,是 NLU 的输出)和当前对话状态,输出就是下一步的系统行为和更新的对话状态。这是一个循环往复不断流转直至完成任务的过程。
从本质上来说,**任务驱动的对话管理实际就是一个决策过程,系统在对话过程中不断根据当前状态决定下一步应该采取的最优动作(如:提供结果,询问特定限制条件,澄清或确认需求等),从而最有效的辅助用户完成信息或服务获取的任务。**对话管理的任务大致有:
对话状态维护(dialog state tracking, DST)
对话状态是指记录了哪些槽位已经被填充、下一步该做什么、填充什么槽位,还是进行何种操作。用数学形式表达为,t+1 时刻的对话状态S(t+1),依赖于之前时刻 t 的状态St,和之前时刻 t 的系统行为At,以及当前时刻 t+1 对应的用户行为O(t+1)。可以写成S(t+1)←St+At+O(t+1)。
生成系统决策(dialog policy)
根据 DST 中的对话状态(DS),产生系统行为(dialog act),决定下一步做什么 dialog act 可以表示观测到的用户输入(用户输入 -> DA,就是 NLU 的过程),以及系统的反馈行为(DA -> 系统反馈,就是 NLG 的过程)。
作为接口与后端/任务模型进行交互
Rasa Core
Rasa Core是Rasa框架提供的对话管理模块,它类似于聊天机器人的大脑,主要的任务是维护更新对话状态和动作选择,然后对用户的输入作出响应。所谓对话状态是一种机器能够处理的对聊天数据的表征,对话状态中包含所有可能会影响下一步决策的信息,如自然语言理解模块的输出、用户的特征等;所谓动作选择,是指基于当前的对话状态,选择接下来合适的动作,例如向用户追问需补充的信息、执行用户要求的动作等。举一个具体的例子,用户说“帮我妈妈预定一束花”,此时对话状态包括自然语言理解模块的输出、用户的位置、历史行为等特征。在这个状态下,系统接下来的动作可能是:
向用户询问可接受的价格,如“请问预期价位是多少?”;
向用户确认可接受的价格,如“像上次一样买价值200的花可以吗?”
直接为用户预订
2.1 Stories
Rasa的故事是一种训练数据的形式,用来训练Rasa的对话管理模型。故事是用户和人工智能助手之间的对话的表示,转换为特定的格式,其中用户输入表示为相应的意图(和必要的实体),而助手的响应表示为相应的操作名称。Rasa核心对话系统的一个训练示例称为一个故事。这是一个故事数据格式的指南。两段对话样本示例:
<!-- ##表示story的描述,没有实际作用 -->
## greet + location/price + cuisine + num people
* greet- utter_greet
* inform{"location": "rome", "price": "cheap"}- action_on_it- action_ask_cuisine
* inform{"cuisine": "spanish"}- action_ask_numpeople
* inform{"people": "six"}- action_ack_dosearch<!-- Form Action-->
## happy path
* request_weather- weather_form- form{"name": "weather_form"}- form{"name": null}
Story格式大致包含三个部分:
1. 用户输入(User Messages)
使用*开头的语句表示用户的输入消息,我们无需使用包含某个具体内容的输入,而是使用NLU管道输出的intent和entities来表示可能的输入。需要注意的是,如果用户的输入可能包含entities,建议将其包括在内,将有助于policies预测下一步action。这部分大致包含三种形式,示例如下:
(1)* greet 表示用户输入没有entity情况;
(2)* inform{"people": "six"} 表示用户输入包含entity情况,响应这类intent为普通action;
(3)* request_weather 表示用户输入Message对应的intent为form action情况;
2. 动作(Actions)
使用-开头的语句表示要执行动作(Action),可分为utterance actions和custom actions,其中,前者在domain.yaml中定义以utter_为前缀,比如名为greet的意图,它的回复应为utter_greet;后者为自定义动作,具体逻辑由我们自己实现,虽然在定义action名称的时候没有限制,但是还是建议以action_为前缀,比如名为inform的意图fetch_profile的意图,它的response可为action_fetch_profile。
3. 事件(Events)
Events也是使用-开头,主要包含槽值设置(SlotSet)和激活/注销表单(Form),它是是Story的一部分,并且必须显示的写出来。Slot Events和Form Events的作用如下:
(1)Slot Events
Slot Events的作用当我们在自定义Action中设置了某个槽值,那么我们就需要在Story中Action执行之后显著的将这个SlotSet事件标注出来,格式为- slot{"slot_name": "value"}。比如,我们在action_fetch_profile中设置了Slot名为account_type的值,代码如下:
from rasa_sdk.actions import Action
from rasa_sdk.events import SlotSet
import requestsclass FetchProfileAction(Action):def name(self):return "fetch_profile"def run(self, dispatcher, tracker, domain):url = "http://myprofileurl.com"data = requests.get(url).jsonreturn [SlotSet("account_type", data["account_type"])]
那么,就需要在Story中执行action_fetch_profile之后,添加- slot{"account_type" : "premium"}。虽然,这么做看起来有点多余,但是Rasa规定这么做必须的,目的是提高训练时准确度。
## fetch_profile
* fetch_profile- action_fetch_profile- slot{"account_type" : "premium"}- utter_welcome_premium
当然,如果您的自定义Action中将槽值重置为None,则对应的事件为-slot{"slot_name": null}。
(2)Form Events
在Story中主要存在三种形式的表单事件(Form Events),它们可表述为:
Form Action事件
Form Action即表单动作事件,是自定义Action的一种,用于一个表单操作。示例如下:
- restaurant_form
Form activation事件
form activation即激活表单事件,当form action事件执行后,会立马执行该事件。示例如下:
- form{"name": "restaurant_form"}
Form deactivation事件
form deactivation即注销表单事件,作用与form activation相反。示例如下:
- form{"name": null}
总之,我们在构建Story时,可以说是多种多样的,因为设计的故事情节是多种多样的,这就意味着上述三种内容的组合也是非常灵活的。另外,在设计Story时Rasa还提供了Checkpoints 和OR statements两种功能,来提升构建Story的灵活度,但是需要注意的是,东西虽好,但是不要太贪了,过多的使用不仅增加了复杂度,同时也会拖慢训练的速度。其中,Checkpoints用于模块化和简化训练数据,示例如下:
## first story
* greet- action_ask_user_question
> check_asked_question## user affirms question
> check_asked_question
* affirm- action_handle_affirmation
> check_handled_affirmation## user denies question
> check_asked_question
* deny- action_handle_denial
> check_handled_denial## user leaves
> check_handled_denial
> check_handled_affirmation
* goodbye- utter_goodbye
在上面的例子中,可以使用> check_asked_question表示first story,这样在其他story中,如果有相同的first story部分,可以直接用> check_asked_question代替。而OR Statements主要用于实现某一个action可同时响应多个意图的情况,比如下面的例子:
## story
* affirm OR thankyou- action_handle_affirmation
2.2 Domain
Domain,译为**“领域”**,它描述了对话机器人应知道的所有信息,类似于“人的大脑”,存储了意图intents、实体entities、插槽slots以及动作actions等信息,其中,intents、entities在NLU训练样本中定义,slots对应于entities类型,只是表现形式不同。domain.yml文件组成结构如下:
具体介绍如下:
1. intents
intents:- affirm- deny- greet- request_weather- request_number- inform- inform_business- stop- chitchat
intents,即意图,是指我们输入一段文本,希望Bot能够明白这段文本是什么意思。在Rasa框架中,意图的定义是在NLU样本中实现的,并且在每个意图下面我们需要枚举尽可多的样本用于训练,以达到Bot能够准确识别出我们输入的一句话到底想要干什么。
2. session_config
session_config:carry_over_slots_to_new_session: truesession_expiration_time: 60
session_config,即会话配置,这部分的作用为配置一次会话(conversation session)是否有超时限制。上例演示的是,每次会话的超时时间为60s,如果用户开始一段会话后,在60s内没有输入任何信息,那么这次会话将被结束,然后Bot又会开启一次新的会话,并将上一次会话的Slot值拷贝过来。当然,我们希望舍弃上一次会话Slot的值,可以将carry_over_slots_to_new_session设置为false。另外,当session_expiration_time被设置为0时,Bot永远不会结束当前会话并一直等待用户输入(注:执行action_session_start仍然可以开始一次新的会话,在设置为0的情况下)。
3. slots
slots:date_time:type: unfeaturizedauto_fill: falseaddress:type: unfeaturizedauto_fill: false
Slots,即插槽,它就像对话机器人的内存,它通过键值对的形式可用来收集存储用户输入的信息(实体)或者查询数据库的数据等。关于Slots的设计与使用,详情请见本文2.6小节。
4. entities
entities:- date_time- address
entities,即实体,类似于输入文本中的关键字,需要在NLU样本中进行标注,然后Bot进行实体识别,并将其填充到Slot槽中,便于后续进行相关的业务操作。
5. actions
actions:- utter_answer_affirm # utter_开头的均为utter actions- utter_answer_deny- utter_answer_greet- utter_answer_goodbye- utter_answer_thanks- utter_answer_whoareyou- utter_answer_whattodo- utter_ask_date_time- utter_ask_address- utter_ask_number- utter_ask_business- utter_ask_type- action_default_fallback # default actions
当Rasa NLU识别到用户输入Message的意图后,Rasa Core对话管理模块就会对其作出回应,而完成这个回应的模块就是action。Rasa Core支持三种action,即default actions、utter actions以及 custom actions。关于如何实现Actions和处理业务逻辑,我们在一篇文章中详谈,这里仅作简单了解。
6. forms
forms:- weather_form
forms,即表单,该部分列举了在NLU样本中定义了哪些Form Actions。关于Form Actions的相关知识,请移步至本文的2.7小节。
7. responses
responses:utter_answer_greet:- text: "您好!请问我可以帮到您吗?"- text: "您好!很高兴为您服务。请说出您要查询的功能?"utter_ask_date_time:- text: "请问您要查询哪一天的天气?"utter_ask_address:- text: "请问您要查下哪里的天气?"utter_default:- text: "没听懂,请换种说法吧~"
responses部分就是描述UtterActions具体的回复内容,并且每个UtterAction下可以定义多条信息,当用户发起一个意图,比如 “你好!”,就触发utter_answer_greet操作,Rasa Core会从该action的模板中自动选择其中的一条信息作为结果反馈给用户。
2.3 Responses
Responses的作用就是自动响应用户输入的信息,因此我们需要管理这些响应(Responses)。Rasa框架提供了三种方式来管理Responses,它们是:
在domain.yaml文件中存储Responses;
在训练数据中存储Responses;
自定义一个NLG服务来生成Responses。
由于第一种我们在本文2.2(7)小节有过介绍,而创建NLG服务是这样的:
nlg:url: http://localhost:5055/nlg # url of the nlg endpoint# you can also specify additional parameters, if you need them:# headers:# my-custom-header: value# token: "my_authentication_token" # will be passed as a get parameter# basic_auth:# username: user# password: pass
# example of redis external tracker store config
tracker_store:type: redisurl: localhostport: 6379db: 0password: passwordrecord_exp: 30000
# example of mongoDB external tracker store config
#tracker_store:#type: mongod#url: mongodb://localhost:27017#db: rasa#user: username#password: password
2.4 Actions
当Rasa NLU识别到用户输入Message的意图后,Rasa Core对话管理模块就会对其作出回应,而完成这个回应的模块就是action。Rasa Core支持三种action,即default actions、utter actions以及 custom actions。关于如何实现Actions和处理业务逻辑,我们在一篇文章中详谈,这里仅作简单了解。
1. default actions
DefaultAction是Rasa Core默认的一组actions,我们无需定义它们,直接可以story和domain中使用。包括以下三种action:
action_listen:监听action,Rasa Core在会话过程中通常会自动调用该action;
action_restart:重置状态,比初始化Slots(插槽)的值等;
action_default_fallback:当Rasa Core得到的置信度低于设置的阈值时,默认执行该action;
2. utter actions
UtterAction是以utter_为开头,仅仅用于向用户发送一条消息作为反馈的一类actions。定义一个UtterAction很简单,只需要在domain.yml文件中的actions:字段定义以utter_为开头的action即可,而具体回复内容将被定义在templates:部分,这个我们下面有专门讲解。定义utter actions示例如下:
actions:- utter_answer_greet- utter_answer_goodbye- utter_answer_thanks- utter_introduce_self- utter_introduce_selfcando- utter_introduce_selffrom
3. custom actions
CustomAction,即自定义action,允许开发者执行任何操作并反馈给用户,比如简单的返回一串字符串,或者控制家电、检查银行账户余额等等。它与DefaultAction不同,自定义action需要我们在domain.yml文件中的actions部分先进行定义,然后在指定的webserver中实现它,其中,这个webserver的url地址在endpoint.yml文件中指定,并且这个webserver可以通过任何语言实现,当然这里首先推荐python来做,毕竟Rasa Core为我们封装好了一个rasa-core-sdk专门用来处理自定义action。关于action web的搭建和action的具体实现,我们在后面详细讲解,这里我们看下在在Rasa Core项目中需要做什么。假如我们在天气资讯的人机对话系统需提供查询天气和空气质量两个业务,那么我们就需要在domain.yml文件中定义查询天气和空气质量的action,即:
actions:... - action_search_weather
另外,FormAction也是自定义actions,但是需要在domainl.yaml文件的forms字段声明。
forms:- weather_form
2.5 Policies
Policies是Rasa Core中的策略模块,对应类rasa_core.policies.Policy,它的作用就是使用合适的策略(Policy)来预测一次对话后要执行的行为(Actions)。预测的原理是衡量命中的哪些Policies哪个置信度高,由置信度高的Policy选择合适的Action执行。假如出现不同的Policy拥有相同的置信度,那么就由它们的优先级决定,即选择优先级高的Policy。Rasa对提供的Policies进行了优先级排序,具体如下表:
它们的描述与作用如下:
Memoization Policy
MemoizationPolicy只记住(memorizes)训练数据中的对话。如果训练数据中存在这样的对话,那么它将以置信度为1.0预测下一个动作,否则将预测为None,此时置信度为0.0。下面演示了如何在策略配置文件config.yml文件中,配置MemoizationPlicy策略,其中,max_history(超参数)决定了模型查看多少个对话历史以决定下一个执行的action。
policies:- name: "MemoizationPolicy"max_history: 5
注:max_history值越大训练得到的模型就越大并且训练时间会变长,关于该值到底该设置多少,我们可以举这么个例子,比如有这么一个Intent:out_of_scope来描述用户输入的消息off-topic(离题),当用户连续三次触发out_of_scope意图,这时候我们就需要主动告知用户需要向其提供帮助,如果要Rasa Core能够学习这种模型,max_history应该至少为3。story.md中表现如下:
* out_of_scope- utter_default
* out_of_scope- utter_default
* out_of_scope- utter_help_message
Keras Policy
KerasPolicy策略是Keras框架中实现的神经网络来预测选择执行下一个action,它默认的框架使用LSTM(Long Short-Term Memory,长短期记忆网络)算法,但是我们也可以重写KerasPolicy.model_architecture函数来实现自己的框架(architecture)。KerasPolicy的模型很简单,只是单一的LSTM+Dense+softmax,这就需要我们不断地完善自己的story来把各种情况下的story进行补充。下面演示了如何在策略配置文件config.yml文件中,配置KerasPolicy策略,其中,epochs表示训练的次数,max_history同上。
policies:- name: KerasPolicyepochs: 100max_history: 5
Embedding Policy
基于机器学习的对话管理能够学习复杂的行为以完成任务,但是将其功能扩展到新领域并不简单,尤其是不同策略处理不合作用户行为的能力,以及在学习新任务(如预订酒店)时,如何将完成一项任务(如餐厅预订)重新应用于该任务时的情况。EmbeddingPolicy,即循环嵌入式对话策略(Recurrent Embedding Dialogue Policy,REDP),它通过将actions和对话状态嵌入到相同的向量空间(vector space)能够获得较好的效果,REDP包含一个基于改进的Neural Turing Machine的记忆组件和注意机制,在该任务上显著优于基线LSTM分类器。
EmbeddingPolicy效果上优于KerasPolicy,但是它有个问题是耗时,因为它没有使用GPU、没有充分利用CPU资源。KerasPolicy和EmbeddingPolicy比较示意图如下:
配置EmbeddingPolicy参数:
policies:- name: EmbeddingPolicyepochs: 100featurizer:- name: FullDialogueTrackerFeaturizerstate_featurizer:- name: LabelTokenizerSingleStateFeaturizer
注:新版的Rasa将EmbeddingPolicy重命名为TEDPolicy,但是我在config.yml配置文件中将其替换后,提示无法找到TEDPolicy异常,具体原因不明,暂还未涉及源码分析。
Form Policy
FormPolicy是MemoizationPolicy的扩展,用于处理(form)表单的填充事项。当一个FormAction被调用时,FormPolicy将持续预测表单动作,直到表单中的所有槽都被填满,然后再执行对应的FormAction。如果在Bot系统中使用了FormActions,就需要在config.yml配置文件中进行配置。
policies:- name: FormPolicy
Mapping Policy
MappingPolicy可用于直接将意图映射到要执行的action,从而实现被映射的action总会被执行,其中,这种映射是通过triggers属性实现的。举个栗子(domain.yml文件中):
intents:- greet: {triggers: utter_goodbye}
其中,greet是意图;utter_goodbye是action。一个意图最多只能映射到一个action,我们的机器人一旦收到映射意图的消息,它将执行对应的action。然后,继续监听下一条message。需要注意的是,对于上述映射,我们还需要要在story.md文件中添加如下样本,否则,任何机器学习策略都可能被预测的action_greet在dialouge历史中突然出现而混淆。
Fallback Policy
如果意图识别的置信度低于nlu_threshold,或者没有任何对话策略预测的action置信度高于core_threshold,FallbackPolicy将执行fallback action。通俗来说,就是我们的对话机器人意图识别和action预测的置信度没有满足对应的阈值,该策略将使机器人执行指定的默认action。configs.yml配置如下:
policies:- name: "FallbackPolicy"# 意图理解置信度阈值nlu_threshold: 0.3# action预测置信度阈值core_threshold: 0.3# fallback actionfallback_action_name: 'action_default_fallback'
其中,action_default_fallback是Rasa Core中的一个默认操作,它将向用户发送utter_default模板消息,因此我们需要确保在domain.yml文件中指定此模板。当然,我们也可以在fallback_action_name字段自定义默认回复的action,比如my_fallback_cation,就可以这么改:
policies:- name: "FallbackPolicy"nlu_threshold: 0.4core_threshold: 0.3fallback_action_name: "my_fallback_action"
2.6 Slots
Slots,槽值,相当于机器人的内存(memory),它们以键值对的形式存在,用于存储用户输入时消息时比较重要的信息,而这些信息将为Action的执行提供关键数据。Slots的定义位于domain.yaml文件中,它们通常与Entities相对应,即Entities有哪些,Slots就有哪些,并且Slots存储的值就是NLU模型提取的Entities的值。
2.6.1 Slots Type
1. Text类型
示例:
# domain.yaml
slots:cuisine:type: text
2. Boolean类型
示例:
slots:is_authenticated:type: bool
3. categorical类型
示例:
slots:risk_level:type: categoricalvalues:- low- medium- high
4. Float类型
示例:
slots:temperature:type: floatmin_value: -100.0max_value: 100.0
5. List类型
示例:
slots:shopping_items:type: list
6. Unfeaturized 类型
示例:
slots:internal_user_id:type: unfeaturized
2.6.2 Slots Set
Slots值填充有多种方式,它们的操作方式如下:
1. Slots Initial
# domain.yaml
slots:name:type: textinitial_value: "human"
在domain.yaml文件中声明slots时,可以通过initial_value字段为当前slot提供一个初始值,也就是说,当会话开始时,被设定初始值的slot已经被填充好。当然,这个操作不是必须的。
2. Slots Set from NLU
# stories.md
# story_01
* greet{"name": "Ali"}- slot{"name": "Ali"}- utter_greet
假如在stories.md文件添加一个包含-slot{}的story,这就意味着当NLU模型提取到一个名为name的实体且这个实体有在domain.yaml中定义,那么NLU模型提取到的实体值会被自动填充到name槽中。实际上,对于Rasa来说,就算你不添加-slot{}字段,这个实体值也会被提取并自动填充到name槽中。当然,如果你希望禁止这种自动填充行为,改为添加-slot{}字段填充,可以在domain.yaml定义slot时,设置auto_fill的值为False,即:
# domain.yaml
slots:name:type: textauto_fill: False
3. Slots Set By Clicking Buttons
# domain.yaml
utter_ask_color:
- text: "what color would you like?"buttons:- title: "blue" payload: '/choose{"color": "blue"}' # 格式 '/intent{"entity":"value",...}' - title: "red"payload: '/choose{"color": "red"}'
在点击Button时填充Slots的值,是指当我们的Bot(Rasa Core)在回复用户时,可以在回复的消息中附加Button信息,这种Button类似于快捷键,用户获取到之后,可以直接将其发送给Rasa Core,它会直接进行解析以识别intent和提取entity,并将entity的值填充到slot中。比如你让用户通过点击一个按钮来选择一种颜色,那么可以在domain.yaml中utter_ask_color的回复中添加buttons:/choose{"color": "blue"}和/choose{"color": "red"}。注:通常每个button由title和payload字段组成。
4. Slots Set by Actions
from rasa_sdk.actions import Action
from rasa_sdk.events import SlotSet
import requestsclass FetchProfileAction(Action):def name(self):return "fetch_profile"def run(self, dispatcher, tracker, domain):url = "http://myprofileurl.com"data = requests.get(url).jsonreturn [SlotSet("account_type", data["account_type"])]
该示例演示了如何在Custom Action中通过返回事件来填充Slots的值,即调用SlotSet事件函数并将该事件return。需要注意的是,为了达到这个目的,我们在编写Story时必须包含该Slot,即使用-slot{}实现,只有这样Rasa Core就会从提供的信息中进行学习,并决定执行正确的action。Story.md示例如下:
# story_01
* greet- action_fetch_profile- slot{"account_type" : "premium"}- utter_welcome_premium# story_02
* greet- action_fetch_profile- slot{"account_type" : "basic"}- utter_welcome_basic
其中,account_type在domain.yaml中定义如下:
slots:account_type:type: categoricalvalues:- premium- basic
2.6.3 Slots Get
目前主要有两种获取Slots值方式:
1. Get Slot in responses
responses:utter_greet:- text: "Hey, {name}. How are you?"
在domain.yaml的responses部分,可以通过{slotname}的形式获取槽值。
2. Get Slot in Custom Action
from rasa_sdk.actions import Action
from rasa_sdk.events import SlotSet
import requestsclass FetchProfileAction(Action):def name(self):return "fetch_profile"def run(self, dispatcher, tracker, domain):# 获取slot account_type的值account_type = tracker.get_slot('account_type')return []
Tracker,可理解为跟踪器,作用是以会话会话的形式维护助手和用户之间的对话状态。通过Tracker,能够轻松获取整个对话信息,其中就包括Slot的值。
2.7 Form
在Rasa Core中,当我们执行一个action需要同时填充多个slot时,可以使用FormAction来实现,因为FormAction会遍历监管的所有slot,当发现相关的slot未被填充时,就会向用户主动发起询问,直到所有slot被填充完毕,才会执行接下来的业务逻辑。使用步骤如下:
(1)构造story
在story中,不仅需要考虑用户按照我们的设计准确的提供有效信息,而且还要考虑用户在中间过程改变要执行的意图情况或称输入无效信息,因为对于FormAction来说,如果无法获得预期的信息就会报错,这里我们分别称这两种情况为happy path、unhappy path。示例如下:
## happy path
* request_weather- weather_form- form{"name": "weather_form"} 激活form- form{"name": null} 使form无效
## unhappy path
* request_weather- weather_form- form{"name": "weather_form"}
* stop- utter_ask_continue
* deny- action_deactivate_form- form{"name": null}
注:* request_restaurant为意图;- restaurant_form为form action;- form{"name": "restaurant_form"}为激活form;- form{"name": null}为注销form;- action_deactivate_form为默认的action,它的作用是用户可能在表单操作过程中改变主意,决定不继续最初的请求,我们使用这个default action来禁止(取消)表单,同时重置要请求的所有slots。
构建stroy最好使用官方提供的Interactive Learning,防止漏掉信息,详细见本文2.8小节。
(2)添加form字段到Domain
在doamin文件下新增forms:部分,并将所有用到的form名称添加到该字段下:
intents:- request_weatherforms:- weather_form
(3)配置FormPolicy
在工程的配置文件configs.yml中,新增FormPolicy策略:
policies:- name: EmbeddingPolicyepochs: 100max_history: 5- name: FallbackPolicyfallback_action_name: 'action_default_fallback'- name: MemoizationPolicymax_history: 5- name: FormPolicy
(4)Form Action实现
class WeatherForm(FormAction):def name(self) -> Text:"""Unique identifier of the form"""return "weather_form"@staticmethoddef required_slots(tracker: Tracker) -> List[Text]:"""A list of required slots that the form has to fill"""return ["date_time", "address"]def submit(self,dispatcher: CollectingDispatcher,tracker: Tracker,domain: Dict[Text, Any],) -> List[Dict]:"""Define what the form has to doafter all required slots are filled"""address = tracker.get_slot('address')date_time = tracker.get_slot('date_time')return []
当form action第一被调用时,form就会被激活并进入FormPolicy策略模式。每次执行form action,required_slots会被调用,当发现某个还未被填充时,会主动去调用形式为uter_ask_{slotname}的模板(注:定义在domain.yml的templates字段中);当所有slot被填充完毕,submit方法就会被调用,此时本次form操作完毕被取消激活。
2.8 Interactive Learning
虽然我们可以容易的人工构建story样本数据,但是往往会出现一些考虑不全,甚至出错等问题,基于此,Rasa Core框架为我们提供了一种交互式学习(Interactive Learning)来获得所需的样本数据。在互动学习模式中,当你与机器人交谈时,你会向它提供反馈,这是一种强大的方法来探索您的机器人可以做什么,也是修复它所犯错误的最简单的方法。基于机器学习的对话的一个优点是,当你的机器人还不知道如何做某事时,你可以直接教它。
(1)开启Action Server
python -m rasa run actions --port 5055 --actions actions --debug
(2)开启Interactive Learning
python -m rasa interactive -m models/20200313-101055.tar.gz --endpoints configs/endpoints.yml --config configs/config.yml
# 或者(没有已训练模型情况)
# rasa会先训练好模型,再开启交互式学习会话
python -m rasa interactive --data /data --domain configs/domain.yml --endpoints configs/endpoints.yml --config configs/config.yml
分别执行(1)、(2)命令后,我们可以预设一个交互场景根据终端的提示操作即可。如果一个交互场景所有流程执行完毕,按Ctrl+C结束并选择Start Fresh进入下一个场景即可。当然Rasa还提供了可视化界面,以帮助你了解每个Story样本构建的过程,网址:http://localhost:5005/visualization.html。
执行流程大致如下:
Bot loaded. Visualisation at http://localhost:5006/visualization.html .
Type a message and press enter (press 'Ctr-c' to exit).
? Your input -> 查询身份证439912199008071234
? Is the intent 'request_idcard' correct for '查询身份证[439912199008071234](id_number)' and are all entities labeled correctly? Yes
------
Chat History# Bot You
───────────────────────────────────────────────────────────────────1 action_listen
───────────────────────────────────────────────────────────────────2 查询身份证[439912199008071234](id_number)intent: request_idcard 1.00Current slots:address: None, business: None, date-time: None, id_number: None, requested_slot: None------
? The bot wants to run 'number_form', correct? Yes
Chat History# Bot You
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────1 action_listen
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────2 查询身份证[439912199008071234](id_number)intent: request_idcard 1.00
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────3 number_form 1.00您要查询的身份证号码439912199008071234所属人为张三,湖南长沙人,现在就职于地球村物业。form{"name": "number_form"}slot{"id_number": "439912199008071234"}form{"name": null}slot{"requested_slot": null}Current slots:address: None, business: None, date-time: None, id_number: 439912199008071234, requested_slot: None------
? The bot wants to run 'action_listen', correct? Yes
生成的一个Story示例如下:
## interactive_story_10
# unhappy path:chitchat stop but continue path
* greet- utter_answer_greet
* request_number{"type": "身份证号码"}- number_form- form{"name": "number_form"}- slot{"type": "身份证号码"}- slot{"number": null}- slot{"business": null}- slot{"requested_slot": "number"}
* chitchat- utter_chitchat- number_form- slot{"requested_slot": "number"}
* stop- utter_ask_continue
* affirm- number_form- slot{"requested_slot": "number"}
* form: request_number{"number": "440123199087233467"}- form: number_form- slot{"number": "440123199087233467"}- slot{"type": "身份证号码"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_noworries
改进ChitChatAssistant项目
3.1 config.yml
# zh_jieba_mitie_embeddings_config.ymllanguage: "zh"pipeline:
- name: "MitieNLP"model: "data/total_word_feature_extractor_zh.dat"
- name: "JiebaTokenizer"dictionary_path: "data/dict"
- name: "MitieEntityExtractor"
- name: "EntitySynonymMapper"
- name: "RegexFeaturizer"
- name: "MitieFeaturizer"
- name: "EmbeddingIntentClassifier"policies:- name: FallbackPolicynlu_threshold: 0.5ambiguity_threshold: 0.1core_threshold: 0.5fallback_action_name: 'action_default_fallback'- name: MemoizationPolicymax_history: 5- name: FormPolicy- name: MappingPolicy- name: EmbeddingPolicyepochs: 500
考虑到目前项目的样本较少,这里使用MITIE+EmbeddingPolicy组合,虽然训练时慢了点,但是能够保证实体提取的准确性,同时又能够提高意图识别的命中率。
3.2 weather_stories.md
## happy path
* request_weather- weather_form- form{"name": "weather_form"}- form{"name": null}## happy path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}- form{"name": null}
* thanks- utter_noworries## unhappy path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* chitchat- utter_chitchat- weather_form- form{"name": null}
* thanks- utter_noworries## very unhappy path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* chitchat- utter_chitchat- weather_form
* chitchat- utter_chitchat- weather_form
* chitchat- utter_chitchat- weather_form- form{"name": null}
* thanks- utter_noworries## stop but continue path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* stop- utter_ask_continue
* affirm- weather_form- form{"name": null}
* thanks- utter_noworries## stop and really stop path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* stop- utter_ask_continue
* deny- action_deactivate_form- form{"name": null}## chitchat stop but continue path
* request_weather- weather_form- form{"name": "weather_form"}
* chitchat- utter_chitchat- weather_form
* stop- utter_ask_continue
* affirm- weather_form- form{"name": null}
* thanks- utter_noworries## stop but continue and chitchat path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* stop- utter_ask_continue
* affirm- weather_form
* chitchat- utter_chitchat- weather_form- form{"name": null}
* thanks- utter_noworries## chitchat stop but continue and chitchat path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* chitchat- utter_chitchat- weather_form
* stop- utter_ask_continue
* affirm- weather_form
* chitchat- utter_chitchat- weather_form- form{"name": null}
* thanks- utter_noworries## chitchat, stop and really stop path
* greet- utter_answer_greet
* request_weather- weather_form- form{"name": "weather_form"}
* chitchat- utter_chitchat- weather_form
* stop- utter_ask_continue
* deny- action_deactivate_form- form{"name": null}## interactive_story_1
## 天气 + 时间 + 地点 + 地点
* request_weather- weather_form- form{"name": "weather_form"}- slot{"requested_slot": "date_time"}
* form: inform{"date_time": "明天"}- form: weather_form- slot{"date_time": "明天"}- slot{"requested_slot": "address"}
* form: inform{"address": "广州"}- form: weather_form- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "广州"}- slot{"date_time": "后天"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_1
## 天气 + 时间 + 地点 + 时间
* request_weather- weather_form- form{"name": "weather_form"}- slot{"requested_slot": "date_time"}
* form: inform{"date_time": "明天"}- form: weather_form- slot{"date_time": "明天"}- slot{"requested_slot": "address"}
* form: inform{"address": "广州"}- form: weather_form- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* inform{"address": "上海"} OR request_weather{"address": "深圳"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "广州"}- slot{"address": "上海"}- form{"name": null}- slot{"requested_slot": null}
* affirm- utter_answer_affirm## interactive_story_2
## 天气/时间/地点 + 地点
* request_weather{"date_time": "明天", "address": "上海"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "上海"}- form{"name": null}- slot{"requested_slot": null}
* inform{"address": "广州"} OR request_weather{"address": "广州"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "上海"}- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_3
## 天气/时间/地点 + 时间
* request_weather{"address": "深圳", "date_time": "后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "深圳"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "大后天"} OR request_weather{"date_time": "大后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "深圳"}- slot{"date_time": "大后天"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_2
## 天气/时间/地点 + 地点 + 时间
* request_weather{"date_time": "明天", "address": "上海"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "上海"}- form{"name": null}- slot{"requested_slot": null}
* inform{"address": "北京"} OR request_weather{"address": "北京"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "上海"}- slot{"address": "北京"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "北京"}- slot{"date_time": "后天"}- form{"name": null}- slot{"requested_slot": null}
* affirm- utter_answer_affirm## interactive_story_3
## 天气/时间/地点 + 地点 + 地点
* request_weather{"date_time": "后天", "address": "北京"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "北京"}- form{"name": null}- slot{"requested_slot": null}
* inform{"address": "深圳"} OR request_weather{"address": "深圳"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "北京"}- slot{"address": "深圳"}- form{"name": null}- slot{"requested_slot": null}
* inform{"address": "南京"} OR request_weather{"address": "南京"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "深圳"}- slot{"address": "南京"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_4
## 天气/时间/地点 + 时间 + 地点
* request_weather{"date_time": "明天", "address": "长沙"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "长沙"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "长沙"}- slot{"date_time": "后天"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "大后天"} OR request_weather{"date_time": "大后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "长沙"}- slot{"date_time": "大后天"}- form{"name": null}- slot{"requested_slot": null}
* affirm- utter_answer_affirm## interactive_story_5
## 天气/时间/地点 + 时间 + 时间
* request_weather{"date_time": "后天", "address": "深圳"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "深圳"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "深圳"}- slot{"date_time": "明天"}- form{"name": null}- slot{"requested_slot": null}
* inform{"address": "广州"} OR request_weather{"address": "广州"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "深圳"}- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_4
## 天气/时间 + 地点 + 时间
* request_weather{"date_time": "明天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"requested_slot": "address"}
* form: inform{"address": "广州"}- form: weather_form- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "后天"} OR request_weather{"date_time": "后天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "明天"}- slot{"address": "广州"}- slot{"date_time": "后天"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_5
## 天气/地点 + 时间 + 时间
* request_weather{"address": "广州"}- weather_form- form{"name": "weather_form"}- slot{"address": "广州"}- slot{"requested_slot": "date_time"}
* form: inform{"date_time": "后天"}- form: weather_form- slot{"date_time": "后天"}- form{"name": null}- slot{"requested_slot": null}
* inform{"date_time": "明天"} OR request_weather{"date_time": "明天"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "后天"}- slot{"address": "广州"}- slot{"date_time": "明天"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks## interactive_story_1
## 天气/时间/地点 + chit + chit(restart)+询问天气
* request_weather{"date_time": "今天", "address": "广州"}- weather_form- form{"name": "weather_form"}- slot{"date_time": "今天"}- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* chitchat- utter_chitchat
* chitchat- utter_chitchat- action_restart
* request_weather- weather_form- form{"name": "weather_form"}- slot{"requested_slot": "date_time"}
* form: inform{"date_time": "今天"}- form: weather_form- slot{"date_time": "今天"}- slot{"requested_slot": "address"}
* form: inform{"address": "广州"}- form: weather_form- slot{"address": "广州"}- form{"name": null}- slot{"requested_slot": null}
* thanks- utter_answer_thanks
在构建Story样本时,主要是使用Interactive Learning工具实现,以确保枚举尽可能多的unhappy story,同时又能够防止在构建样本时出现信息遗漏的情况。此外,本版本中除了查询天气这个案例,还新增了其他案例,并列举了如何使用同义词、自定义字典以及正则表达式的使用方法,详细见最新版项目。
GitHub地址:ChitChatAssistant https://github.com/jiangdongguo/ChitChatAssistant,欢迎star和issues,我们共同讨论、学习!
原文链接:
https://blog.csdn.net/AndrExpert/article/details/105434136
欢迎所有开发者扫描下方二维码填写《开发者与AI大调研》,只需2分钟,便可收获价值299元的「AI开发者万人大会」在线直播门票!
推荐阅读
前百度主任架构师创业,两年融资千万美元,他说AI新药研发将迎来黄金十年
北京四环堵车引发的智能交通大构想
400 多行代码!超详细中文聊天机器人开发指南 | 原力计划
三大运营商将上线 5G 消息;苹果谷歌联手,追踪 30 亿用户;jQuery 3.5.0 发布 | 极客头条
比特币当赎金,WannaRen 勒索病毒二度来袭!
你公司的虚拟机还闲着?基于 Jenkins 和 Kubernetes 的持续集成测试实践了解一下!
从 Web 1.0到Web 3.0:详析这些年互联网的发展及未来方向
你点的每个“在看”,我都认真当成了AI
相关文章:
Linux配置SSH无密码登陆
可以使用“公钥私钥"认证的方式来进行ssh登录。 所谓 "公钥私钥"认证方式,就是首先在客户机上创建一对公钥和私钥,公钥文件:~/.ssh/id_rsa.pub; 私钥文件:~/.ssh/id_rsa 然后把公钥文件放到目标服务器…
Linux进程浏览器htop安装与使用
htop 是一个 Linux 下的交互式的进程浏览器,可以用来替换Linux下的top命令。当前具有按树状方式来查看进程,支持颜色主题,可以定制等特性。其实htop是top的加强版,增加了很多功能。 官网 http://hisham.hm/htop/ 下载地址http:/…
什么?神经网络还能求解高级数学方程?
来源 | 数据派 THU封图 | CSDN 付费下载于视觉中国 Facebook AI建立了第一个可以使用符号推理解决高级数学方程的AI系统。通过开发一种将复杂数学表达式表示为一种语言的新方法,然后将解决方案视为序列到序列的神经网络的翻译问题,我们构建了一个在解决积…

***和******
网络是一把双刃剑,它在人类社会的发展中起着越来越重要作用,但同时,网络自身的安全问题也像挥之不去的阴影时刻笼罩在人们心头。据不完全统计,全世界平均每 20秒钟就发生一起******事件,互联网上大约有20万个***网站可…
Linux监控工具dstat
dstat是一个用来替换 vmstat,iostat netstat,nfsstat和ifstat这些命令的工具, 是一个全能系统信息统计工具. 与sysstat相比, dstat拥有一个彩色的界面, 在手动观察性能状况时, 数据比较显眼容易观察; 而且dstat支持即时刷新, 譬如输入dstat 3, 即每三秒收集一次, 但最新的数据都…

9月16号晚上,Asuka有一场关于Windows 7组策略的Webcast,欢迎兄弟们来捧场
之所以选题在组策略之一块,是因为Windows 7和2008 R2对于组策略有了很大的功能上的增强,但是很多IT人员都无法意识或者去重视这一块内容,所以我将从下面这3个角度去介绍这些更新。如果您正好有时间,那不妨来技术交流一番:)直播进入…

腾讯天衍实验室联合微众银行研发医疗联邦学习 AI利器让脑卒中预测准确率达80%
近几年,医疗行业正在经历一场数字化转型,这场基于大数据和AI技术的变革几乎改变了整个行业的方方面面,将“信息就是力量”这句箴言体现的淋漓尽致,人们对人工智能寄以厚望,希望它能真正深入临床一线,帮助医…

JavaSript模块化 AMD CMD 详解.....
模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割…
在Eclipse中使用Maven构建Spring项目
最新版的Spring需要使用Maven构建,本文讲述怎么在Eclipse构建Maven项目,以配置Spring项目为例。 maven简单介绍 maven是构建工具,也是构建管理工具。ant只是构建工具,因为不支持生成站点功能,只有预处理,编…

Go 语言官网全新改版
2019独角兽企业重金招聘Python工程师标准>>> 前两天发现 Go 语言官网改版了,布局由原来的左中右变成了上中下结构,主色调没有变,整体依然保持简洁的风格。在首页添加了一个叫 Playground 的模块,它可以编译、运行你输入…
就在今晚 | 港科大李世玮教授问诊未来,开辟大湾区新航路
阳春三月,万象更新,2020年注定是不平凡的一年!有激荡就会遇见变革,有挑战就会迎来机遇。今天总会过去,未来将会怎样?香港科大商学院内地办事处重磅推出全新升级的《袁老师访谈录》全新系列【问诊未来院长系…

NLP(Natural Language Processing)
https://github.com/kjw0612/awesome-rnn#natural-language-processing 通常有: (1)Object Recognition (2)Visual Tracking (3)Image Generation (4)Video Analysis NLP: (1)Language Modeling (2)Speech Recognition…
Linux环境编程
1.__sync_fetch_and_add和__sync_bool_compare_and_swap gcc从4.1.2提供了__sync_*系列的built-in函数,用于提供加减和逻辑运算的原子操作。 其声明如下: type __sync_fetch_and_add (type *ptr, type value, ...) type __sync_fetch_and_sub (type *p…
AI新基建如何构建?浪潮给出了一个答案
作者 | Just出品 | AI科技大本营(ID:rgznai100)伴随生产力升级,社会基础设施也正在发生变化。而智慧时代的新型基础设施,要能够对外提供各种算力服务、数据服务和AI服务。浪潮认为,其核心是计算力的生产中心。因此&…

协作是企业管理的重点和难点
这个问题让我想起了一道数学题,11?。在生活中这个题目的答案会千差万别,更别说一个企业。在我眼中,企业中最难管的是关系,更准确的说是协作。 经理过好几个信息系统建设的项目,小到一个简单的邮件系统&…

使用CSS3美化复选框checkbox
我们知道HTML默认的复选框样式十分简陋,而以图片代替复选框的美化方式会给页面表单的处理带来麻烦,那么本文将结合实例带您一起了解一下使用CSS3将复选框checkbox进行样式美化,并且带上超酷的滑动效果。 查看演示 下载源码HTML 通常我们使用以…

Thift安装
thrift官网http://thrift.apache.org/ #wget http://mirror.bit.edu.cn/apache/thrift/0.9.2/thrift-0.9.2.tar.gz #tar -zvxf thrift-0.9.2.tar.gz # ./configure --prefix/usr/local/thrift #make && makeinstall 增加到环境变量 #export PATH$PATH:/usr/…
“手把手撕LeetCode题目,扒各种算法套路的裤子”
出品 | AI科技大本营(ID:rgznai100)刷LeetCode刷到懵还是一头雾水?莫慌,这里有一个标星27000的算法详解教程。从项目命名来看,作者labuladong就有着要干翻算法的精气神。当然,这个教程不只是为了机械刷题。…

c语言标准库低通的qsort函数不适宜所有排序任务的原因
c语言标准库低通的qsort函数不适宜所有排序任务的原因: 第一:它只能用于内存中的数组排序,不能对链表中的数据排序; 第二:因为它是参数化的函数,所以能对各种数据进行操作,也造成它的运行速度比…

第三周学习进度条
星期日 星期一 星期二 星期三 星期四 星期五 所花时间 下午: 3:00-5:00 上午: 8:00-10:00 下午: 3:00-5:00 下午: 2:30-4:30 下午: 4:30-5:30 下午࿱…
jca分析java dump日志
可以使用jca分析java dump的日志 jca:https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid2245aa39-fa5c-4475-b891-14c205f7333c 运维报说是某机房突然全部都线程阻塞了 其中锁住了tcp的socket,在研究无果的情况下…

shell脚本10
对于使用/bin/bash作为登录shell的系统用户,检查他们在/opt目录中拥有的子目录或文件数量,如果超过100个,则列出具体数值及对应的用户帐号.具体实现: #!/bin/bash DIR"/etc" LMT100 validusersgrep "/bin/bash" /etc/passwd|cut -d ":" -f 1 for…
百度重磅发布云手机:低配置也可玩大型游戏 21
又一个科技巨头发力云游戏。4月15日,百度举行"云手机"线上直播会,发布基于自主研发的ARM服务器的百度"云手机"产品,让用户摆脱硬件的制约,中低端设备也能流畅运行大型游戏和应用。百度"云手机"可以…

创业思维 - Qunar的故事
在这里特别想介绍下Qunar,因为他和我们的最主要的系统-交易系统相关。大家都知道淘宝的交易平台可以说是国内甚至全球最复杂的交易系统。但是我们的交易系统由于承担业务太多,发展太久,历史包裹太重,在业务架构上可以说有很大的问…
“机器学习还是很难用!”
作者 | Caleb Kaiser译者 | 香槟超新星,责编 | 郭芮出品 | CSDN(ID:CSDNnews)我是一名Cortex贡献者,Cortex是一个用于在生产中部署模型的开源平台。首先声明,以下内容是基于我对一些机器学习团队的观察总结…

silverlight 无法发布 如何灵活配置IP
灵活配置IP可以有一个工具的 我为了比赛花了 两天工具做了一个配置Silverlight IP的小工具 可以参考 http://download.csdn.net/source/2714688
使用GoAccess分析Nginx日志
下载 GoAccess 的源代码、编译和安装: http://www.goaccess.io/download # wget http://tar.goaccess.io/goaccess-0.9.6.tar.gz# tar -xzvf goaccess-0.9.6.tar.gz# cd goaccess-0.9.6/# ./configure --prefix/usr/local/goaccess --enable-geoip --enable-utf8#…
STM32中GPIO的8种工作模式
一、推挽输出:可以输出高、低电平,连接数字器件;推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源决定。形象点解释:推挽,就是有推有拉&…
深挖谷歌 DeepMind 和它背后的技术
作者 | James Murphy译者 | 天道酬勤 责编 | Carol出品 | AI科技大本营(ID:rgznai100)人工智能(AI)的子集已经成倍增长,并完成了只有人类才能完成的各种任务。像机器学习这样的技术可以执行管理任务、人脸识别、下棋,甚至翻译语言…
Java编译原理
代码Test.java public class Test {public static void main(String[] args) {int a 0xae;int b 0x10;int c a b;int d c 1;String s;s "hello";}} # javac Test.java # javap -c Test 参考:http://codemacro.com/2015/03/31/intro-java-bytecod…