忆往昔岁月
“你还记得第一份实习,是做什么吗?”
我的第一份算法实习工作,是处理数据。准确地说,是每天手敲30句+SQL,从对话数据库中分析用户高频问题、接通率等信息。
手写SQL是重复性很强的工作,一不小心容易出错,最初的两个月每天为此忙得焦头烂额。
刚刚入门NLP的我天真地想着,“要是模型可以自动生成SQL该多好呀”!
硕士期间,有幸在实验室从事了半年多Text2SQL的科研工作。2021年,我很高兴地看到Text2SQL技术,即通过算法将人的自然语言转换为数据库查询语言SQL,在部分场景中落地应用已经成为了现实。
谈谈自动写SQL的背景
Text2SQL起源于上世纪90年代,是自然语言处理语义解析领域的子任务,核心目标是打破人与结构化数据之间的壁垒,让普通用户可以通过自然语言描述完成复杂数据库的查询工作。
例如我有一张“歌手”相关的表格,歌迷和狗仔队们想知道:
Q:“周杰伦和林俊杰最近的演唱会是什么时候?”
Text2SQL模型自动将问题转换为SQL语言:
A:“SELECT 近期演唱会 FROM singer WHERE 姓名 = 周杰伦 OR 姓名 = 林俊杰”
再返回查询结果:“北京-01.08”和“上海-04.28”
简单的说,以前想从数据库里拿数据或者分析内容,用户要纯手工实现。有了Text2SQL,相当于我雇了一个不花钱不会累的员工(ai模型),只要发布命令(Text),它就会帮我写好SQL。
聊聊技术实现
明白了背景,我们来聊聊技术实现问题。和分类、匹配等很多NLP任务类似,Text2SQL也经历了从早期规则匹配,到现在借助深度学习技术的发展历程,浓缩了一代又一代科学家们的心血。
具体实现方式有很多,为了节省时间,我帮大家抽出了一些代表性的方法。
模版与规则匹配
早期的SQL生成方法主要通过基于统计的模版与规则匹配方式。
SQL查询是一种有很强范式的编程语言,典型特征是可以拆分为“SELECT”和“WHERE”两个片段。再细分一下,几乎90%的SQL语句都可以抽象成如下的模版片段:
AGG表示聚合函数,COLUMN表示需要查询的目标列,WOP表示多个条件之间的关联规则“与/或”,三元组 [COLUMN, OP, VALUE] 构成了查询条件,分别代表条件列、条件操作符、从问题中抽取出的文本片段。*表示目标列和查询条件不止一个!
有了SQL模版,可以先通过统计方法从语料中挖掘出一些高频实体,如人名、地名等,再根据规则填充到模版相应片段即可。来看一个具体 :
Q:价格低于20W的汽车有哪些品牌?
先将问题分词,方便后续处理:“价格 / 低于 / 20W / 的 / 汽车 / 有 / 哪些 / 品牌 ?”
通过预定义规则,很快可以识别 “低于”代表“<”操作符;“低于20W”代表“< 20W”;通过简单数据预处理,还原出条件片段“< 20000”。
通过词性分析和语料统计,识别出“价格”,“20W”,“汽车”是有实际意义的实体。最终通过类似的模版:“$[低于/高于]$的$有哪些?”还原出真实SQL:
A:SELECT 品牌 FROM 汽车 WHERE 价格 < 20000
这里的条件三元组是 [“价格”, “<”, “20000”],目标列为“品牌”,聚合函数为空。
当然通过规则+模版的方式难以穷尽所有自然语言表达,只能处理这种简单的例子。深度学习技术的出现,让盐究员们看到了Text2SQL大放光彩的曙光。
端到端多任务架构
为了充分利用神经网络的特征抽取能力和SQL的语法特点,共享编码器+多任务解码的Seq2Seq架构是一种有效的解决方法。
共享编码器一般是用词向量或预训练语言模型,对query、table和column进行联合编码,捕捉其中隐含的链接关系。其中后两项(表名和列名)统称为数据库模式。例如使用BERT等语言模型时,习惯将输入拼接为 “[CLS] query [SEP] table [SEP] column1 [SEP] column2 [SEP] .... [SEP]”这样的长序列,经过多层Transformer编码,不同table和column对于问题query会有不同权重的相关性。
解码器根据SQL语法特点,设计多个不同的子网络。例如,针对上一小节的SQL模版,可以设计6个子任务分别预测查询目标列、目标列的聚合函数等SQL片段,最终拼接出完整SQL。
多任务架构的优点在于针对不同SQL片段,可以设计有效的损失函数;同时在训练过程中子任务的准确率可以实时监控,便于落地。对此方法感兴趣的读者,可以阅读国防科大的论文M-SQL[1],它在天池NL2SQL[2]中文公开赛上获得了第一名。
端到端抽象语法树解码
这种方法的编码器与前一节类似,但是在解码时将SQL视作一棵抽象语法树,树中各个结点是SQL的关键字(SELECT、WHERE、AND...)或者表名和列名的候选值。生成SQL的过程相当于从树的根结点对语法树做一次深度优先搜索。
以节点“SELECT”为例,“SELECT”节点往下可能包含3个叶子节点:“Column”、“AGG”、“Distinct”,分别代表“选取某一列”、“增加聚合操作”、“对列去重”。从“SELECT”节点向下搜索相当于是3分类任务,根据真实路径和搜索路径依次计算各个节点的交叉熵并求和,作为总损失。
抽象语法树的思想避免了设计各种各样的子网络,对于涉及跨表查询、嵌套查询的复杂数据集有很好的效果。在领域内权威比赛Spider上取得优异成绩的顶会模型IRNet[3]、Rat-SQL[4],都充分借鉴了语法树思想。
其实语法树不仅仅可以生成SQL,还可以生成各种各样好玩的目标序列,例如Python/Java等编程语言,音乐音符等等。CMU的这篇论文[5]从文本生成了Python。
Text2SQL资源大礼包
笔者在Text2SQL领域踩坑半年有余,为避免重复性劳动,我将收集到的资料打包成了一个开源项目,主要包含Text2SQL语义解析数据集、解决方案、参考论文等。欢迎小伙伴们前来
项目地址:https://github.com/yechens/NL2SQL
项目还在持续更新中。对于该领域的部分顶会论文,我加入了自己的解读(BRIDGE:万万没想到,BERT学会写SQL了)。
落地商用的真相
虽然近年该方向在ACL/EMNLP等顶会上出了很多paper,几个知名比赛各家研究院刷的不亦乐乎,不过在我看来Text2SQL实际商用仅处于初级阶段。
究其原因,是准确率还不够高,无法完全满足用户预期。自然语言有很大的熵值,即便同一句话都可以有多种不同理解。同时实际场景中,用户查询内容往往涉及多张表格,查询条件可能非常复杂。语义歧义性、跨表查询、嵌套查询等难点给模型生成准确无误的SQL带来很大挑战。
所以当前阶段,大家看到了Text2SQL应用的曙光,但是还未开始大规模推广;更多时候是结合人工干预与纠错,作为对话系统的一个子模块。
回到标题上来,2021年了,你还在手写SQL吗?相信看完本文读者们心里已经有了答案。对于WikiSQL、TableQA等简单的单表数据集,模型已经完全可以cover。但是在现实复杂场景中,还有很长的研究道路要走。
Text2SQL依然是一个很有研究意义的课题,是自然语言处理跨出文本走向多模态的典型代表。相信未来还会有Text2Music、Text2Moive等新技术诞生,更好地服务于人类社会。