最近自己做了一个个人版的“小爱同学”,能够进行 NLP 处理,并进行对应的操作。之前做过一个版本,是依托 ChatGPT 的 API 做的聊天功能,但这次,我们可以让它干活了!
比如,我提问”what’s the value of jdbc_database_url for the deployment order-service in PROD”,它就会识别我的意图是想要查到生产环境上 order-service 的 jdbc_database_url 是什么,并自动连接 Kubernetes 集群查询 jdbc_database_url 的值。
问题拆解
- 需要识别 NLP 的意图。比如上面的例子里面,我们的意图是要查询 jdbc_database_url 的值。
- 需要找到句子里面的关键词(也叫槽位),比如,我们需要知道 PROD 的意思是生产环境,order-service 是部署名字,以及我们需要查询的字段是什么。
Bert
通过 Bert 模型,以及阿里达摩院的这篇[论文][3],我们可以轻松实现上面的两件事。
相关代码在[这里][2],以及[中文介绍][1]。
简单而言,通过 Bert 的能力,我们可以识别出语句的意图是什么,以及每个槽位是什么,进而我们可以做下一步的操作。举个例子,“what’s the value of jdbc_database_url for the deployment order-service in PROD”这句话,对应的槽位就是O O O O B-env-var-name O O O B-deployment-name O B-environment
,进而得到env-var-name
是jdbc_database_url
,order-service
是deployment-name
和PROD
是environment
。
需要做哪些
训练语料
基于 Bert 的预训练模型,我们可以进行简单的微调,就可以完成很多 NLP 任务。但是,这里“简单的微调”并不是说没什么工作。相反,相当繁杂的人工任务是在准备语料上面,包括对意图识别打标,和槽位打标。
在实例代码中,大概有 4000 多条训练数据,都需要人工一条条处理。“what’s the value of jdbc_database_url for the deployment order-service in PROD”这句话,我们需要打一个意图的标签kubernetes_pod_env_value
,以及对应的槽位就是O O O O B-env-var-name O O O B-deployment-name O B-environment
。
顺便吐槽一句,网上各种流行的开源 LLM 模型,却没有一家开源了自己的语料。所以,核心才是语料,而语料的背后是人工和业务知识。
实现逻辑
这个无需多言,每个意图都需要实现一边。当然现在有 ChatGPT 可以帮忙实现,但需要有 debug 的能力,毕竟 ChatGPT 生成的代码还需要调试和修改才能够用。
我这里需要连接 Kubernetes,ChatGPT 提供的代码是用自己本地的kube config
文件,但没有谁会真的把自己的kube config
文件提交到代码仓库,然后部署上去的,太危险了。所以,真实的实现还是需要额外的考虑。
Bert 是如何做到的
还是这个例子,当输入是
what's the value of jdbc_database_url for the deployment order-service in PROD
输出是
O O O O B-env-var-name O O O B-deployment-name O B-environment
这里只有槽位(slot)的信息,还缺失意图。实际上,输入会先进行 Tokenizer。
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
tokenizer.tokenize('value')
然后再在一头一尾分别加上[CLS]
和[SEP]
,并补齐成 50 个字符(Bert 要求统一输入长度),[CLS]
就是用来做意图识别的。
最后输出大概是这样,101
就是[CLS]
, 102
就是[SEP]
。
[ 101, 2054, 2003, 1996, 2783, 2544, 1997, 5310, 6337, 2326, 2006, 4013, 2094, 102,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]
再将上面的数据喂入模型,输出的结果就是loss
、intent_logits
和slot_logits
。
我们不需要处理loss
,因为这个是训练的时候才会用到的。
对于intent_logits
,如果我有 3 个意图(unknown, play_music, turn_off_light),那么这个输出就是每个意图的可能性,取最高值对应的 index 就行了。
对于slot_logits
,slot 有多个可能性(PAD, UNK, O, B-deployment-name, B-environment),输出的是每个 slot label 的可能性,取最高值对应的 index 就行了。