「大众 10 万到 20 万之间的车型有几种?」——机器能理解这样的自然语言,并从表格中检索出答案吗?这是非常值得探索的问题,也是追一科技举办中文 NL2SQL 挑战赛的重要目的。
今日,首届中文 NL2SQL 挑战赛正式开始提交模型结果。在这之前,追一科技 AI Lab 就曾介绍过 NL2SQL 的概念与发展进程,而本文则重点关注挑战赛到底该怎么解,希望选手们的提交结果能更上一层楼。
挑战赛地址:https://tianchi.aliyun.com/competition/entrance/231716/introduction
什么是 Nature Language To SQL
顾名思义,Nature Language To SQL 就是将自然语言转化为 SQL 查询语句,从而检索需要的信息。本质上它是将自然语言转化为机器语言,从而执行某些特殊的任务。该任务的重点即能解析自然语言的语义,并将这些语义编码为计算机能理解并执行的语句。
其实之前也有很多类似的研究,例如将 GUI 界面转化为形式化代码、学会编辑源代码等等。虽然它们有很多差异,但都尝试利用深度网络抽取图像或文本的语义信息,并根据这些语义信息生成对应代码。
所以不论是自然语言转 Python 还是 C++代码,它们与 NL2SQL 的思想都是相同的,下面我们可以通过简单的案例看看到底 NL2SQL 能干什么。如下我们有一张表,我们可以从中推断出很多信息,例如我们可以算出「不同型号的宝马总共卖了多少辆?」。
一张表确实容易看出来,但若是数千张表组成的数据库呢,这就需要 SQL 语言来帮忙了。我们可以将如上询问手动写成相应的 SQL 表达式:「SELECT SUM(销量) FROM TABLE WHERE 品牌=="宝马";」,从而得到答案「8」。但这种方式要求我们会 SQL 语句,且一种查询就要写一条表达式。
而 NL2SQL 做的,就是希望能自动化这一过程,希望结合用户想要查询的表格,将用户的问句转化为相应的 SQL 语句。
首届中文 NL2SQL 挑战赛
追一科技希望通过 NL2SQL 赛题,拉近用户与结构化数据间的距离,这样机器不仅能理解我们的问题,同时还能从数据库中自行找到答案。
其实赛题本身很好理解,即输入中文问题,并期待模型能根据该问题输出对应的 SQL 查询语句。整个赛题使用金融以及通用领域的表格作为数据源,并提供标注的自然语言与 SQL 语句对,选手可以利用数据训练出能自动生成 SQL 语言的模型。
训练数据
追一表示本次比赛数据集包含有约 4500 张表格,且基于这些表格提出了 50000 条自然语言问句,以及对应的 SQL 语句。数据都是通过爬取并解析公开数据源中的表格而获得的,然后通过人工标注就能构建提问与对应 SQL 语句。
具体而言,追一科技搭建了一个标注平台,标注人员可以在平台上看到一张表格。在理解表格内容后,标注人员需要基于表格提出自然语言问句,并通过点击 UI 的方式构建 SQL 语句。标注好的数据会经过人工校验,追一会尽量避免错误标注、歧义等情况。
本次赛题将提供 4 万条有标签数据作为训练集,1 万条无标签数据作为测试集。其中,5 千条无标注数据作为初赛测试集,选手是可以直接下载并测试的。而另外 5 千条作为复赛测试集,这些数据是不可下载,选手需要提交打包好的代码镜像来运行得出预测结果。
此外,为了使初赛和复赛更具区分度,复赛阶段的测试集将包含更多在训练集中没有出现过的表格数据,以验证方案的泛化能力。
数据集主要由 3 个文件组成,例如训练集中包括 train.json、train.tables.json 及 train.db。其中 train.json 储存了所有提问与对应的 SQL 表达式,train.tables.json 则储存了所有表格。另外 train.db 为 SQLite 格式的数据库文件,它是为执行模型生成的 SQL 语句而准备的。
在 train.json 文件中,每一行为一条数据样本,如下给出了数据示例与字段说明。整体而言,模型需要根据"table_id"和"question"预测出完整的 SQL 表达式 "sql"。
因为一条 SQL 表达式由很多子语句组成,所以"sql"中由一系列选项组成,包括选择的列、列操作、选择的条件约束等等。具体而言,SQL 的表达字典如下所示。"agg"选择的是 agg_sql_dict、"cond_conn_op"选择的是 conn_sql_dict、"conds"中条件类型选择的是 op_sql_dict。
注意,其中"cond_conn_op"表示的是条件之间的关系,即如果表达式包含多个条件,那么它们间的关系到底是 和、或还是 无。后面的"conds"则通过多个三元组具体描述了条件语句,例如 [3,0,100] 就表示选择第四列大于 100 的条目,即 col_4>100。
那么表格又是什么样的呢?在 train.tables.json 文件中,每一行为一张表格数据,下面简要展示了数据样例及字段说明。
表格数据比较好理解,确定每一个特征列的名字、数据类型和数据值就行了。
下面我们可以通过训练集中的具体样本与表格,了解模型到底需要预测些什么。整体而言,首先模型需要根据 table_id 检索对应的表格,然后再根据 question 再对应表格中检索信息,并生成对应的 SQL 表达式。这里不需要根据问题搜索表,也不会产生跨多张表的情况,相当于简化了问题。
现在重要的是我们需要知道模型怎样才能从问题构建 SQL 表达式。如下所示,在我们看到问题「二零一九年第四周大黄蜂和密室逃生这两部影片的票房总占比是多少呀」时,我们可以找到它们的票房占比分别为 15.6% 和 14.2%,然后加起来就能得出周票房总占比为 29.8%。
那么模型需要做一些什么呢?它应该通过各种子任务确定 SQL 表达式的各个模块,然后构建完整的语句,并通过它检索到正确答案。
具体而言,模型应该通过问题中的「票房总占比是多少」确定需要选择第三个特征列「票房占比(%)」,即"sel": [2];以及对应的聚合函数「SUM」,即"agg": [5]。通过问题中的「大黄蜂和密室逃生」从影片名称中确定"大黄蜂"和"密室逃生"两个条件,同时这两个条件间的关系应该为「AND」,即确定"conds"和"cond_conn_op"分别是什么。
当这些都能正确预测时,模型就能正确合成 SQL 表达式:
SELECT SUM(col_3) FROM Table_4d29d0513aaa11e9b911f40f24344a08 WHERE (col_1 == '大黄蜂' and col_1 == '密室逃生')
如果完全正确时,表达式当然没问题,但是只要有一个子句预测错误,整条语句都会有问题。为此,在实际竞赛中,追一科技的基线模型会提供多种度量标准,我们不仅需要知道每个子句的正确情况,还需要知道整体表达式的正确情况。
基线模型
这样的问题确实非常具有挑战性,因此追一科技提供了基线解决方案供选手参考。只要选手报名了挑战赛,那么就能下载数据集,并放到基线模型上试试效果。我们可以只修改核心的模型部分,就能得出更优秀的结果。
基线模型地址:https://github.com/ZhuiyiTechnology/nl2sql_baseline
基线模型使用 Python 2.7 和 torch 1.0.1 构建,选手需要在天池赛题页面下载数据集与预训练词嵌入,并按一定结构组织才能最终跑起来。如下所示是我们需要组织的数据集结构,后面会把如下 data_nl2sql 目录的所有内容复制到基线模型下的 data 目录。
这里数据集都介绍过了,但还有额外的字符级词向量文件 char_embedding。
追一科技表示,他们提供的字符级预训练词嵌入,会在 10G 大小的百度百科和维基百科语料上采用 Skip-Gram 训练。以字为单位可以避免专有名词带来的 OOV 问题以及分词错误所带来的错误累积。
理论上,我们也可以采用 BERT 来作为模型的输入表示。BERT 的细粒度分词对数据集很友好,而且目前在 WikiSQL 数据集上的 SOTA 模型 X-SQL 以及 SQLova 都是采用这样的策略。追一科技表示,他们将 SOTA 模型迁移到这次挑战赛的数据集后,效果也会有较大的提升,所以选手也可以试试 BERT。
最后,基线模型的 GitHub 项目详细介绍了训练及评估过程,选手只要跟着它一步步走就行了。此外,如果本地环境配置有问题或者缺少 GPU 算力,也可以试试在 Colab 上跑基线模型:
Colab 基线模型:https://drive.google.com/open?id=10RtAom_D4zOp_w5OsYLTtLd2TEuC7C1X
模型训练所需要的算力还是挺大的,在 K80 上训练一个 Epoch 大概需要 7 到 8 分钟,基线模型默认训练 100 个 Epoch。追一表示试验多次后,验证集上的平均 Logic Form Accuracy 为 27.5%,即 SQL 每一个子句全都预测正确的概率为 27.5%。
模型结构
基线模型是在 17 年 SQLNet 的基础上改进的,该网络也是 WikiSQL 数据集的基线模型。该网络的核心思想即将不同的子句分离开,因为不同子句的顺序有的并没有太大关系,它们间的独立性可能非常强,所以分离开也许是个不错的想法。
SQLNet 定义了一个模板,它把子句间的依赖性都构建在模板上,因此不同子句填充到该模板就能完成 SQL 语句的构建。现在模型需要单独预测出各个子句的值,即用 6 个神经网络预测选择的列、聚合函数、条件类型和条件值等等。
下图 (a) 展示了 SQL 表达式的模板,其分为 SELECT 和 WHERE 两部分,WHERE 还可能不止一个条件句。下图 (b) 展示了各个子任务间的依赖性关系,SELECT 和 WHERE 之间是相互独立的,但它们内部之间是有相关性的。例如 WHERE 下的每一个条件句,模型要根据特征列确定特征列的值,也需要根据特征列确定它与值之间的条件类型,最终它们三者组合起来就能确定条目(行)。
图注:选自 SQLNet 原论文,arXiv: 1711.04436。
这个模板算是比较通用的,而且任何 SQL 语言都可以通过扩展模板囊括进去。在挑战赛的基线模型中,它与 SQLNet 的区别并不大,只不过挑战赛会增加 Select-Number 和 Where-Relationship 两个子任务,这两个字任务能更好地适应中文 NL2SQL 数据集。
其实模型比较难解决的还是 WHERE 子句,我们发现该子句中确定特征列,及确定特征列的值是最困难的,它们的准确率相对其它子任务都要低一些。所以如何解决这些问题,并构建更强大的整体性能就是选手们该思考的。
代码结构
模型主体都在 sqlnet 目录下,而我们需要理解并修改的代码基本上都在它的子目录 model 下,其它数据加载或处理等函数直接复用就行了。如下简要展示了 model 目录的代码结构,其中 sqlnet 文件负责搭建整体网络框架,modules 目录下的代码则是完成子任务的各个神经网络。
追一表示,总体而言核心代码可以分为三大部分,选手可以从这三部分出发修改基线模型:
输入表达:因为问句及表格数据中存在很多的专有名词,为了避免词表覆盖度不足及错误分词而导致的 OOV 问题,自然语言问句和表格列名、表格内容都采用了以字为单位的字向量作为基本单位。可以比较容易地切换为 BERT、MT-DNN 及其它更优秀的特征表达。
编码层:采用了双向 LSTM 来对自然语言问句及列名进行编码,从而得到相应的向量表征。这一部分的结构在多个子任务中是共享的,但参数不共享。
各个子任务模块:因为 baseline 是基于 SQLNet 改动而来,所以模型仍有多个子任务。按照子任务的不同,基线模型在代码层面也很显式地进行了模块化。如果选手对不同子任务之间的关系有更好的想法,甚至认为 SQL 有更好的生成方式,都可以直接在这些子任务模块进行改写。
评分标准
对于模型的评估,其主要有两个指标:Logic Form Accuracy 和 Execution Accuracy,最后选手排名的依据则是这两项指标的平均值。其中,Logic Form Accuracy 代表的是选手预测出的 SQL 与真实的 SQL 逻辑一致的准确率,即选择的目标列是否一致、聚合函数是否一致、条件列是否一致、条件值是否一致等等。Execution Accuracy 代表的是选手预测出的 SQL 执行结果与真实 SQL 执行结果是否一致。
这两个指标的计算都非常简单,如果预测的 SQL 表达式或者检索结果与标注一致,那么正确数加 1,最后正确数除上总预测数 就是最终的正确率。
但在实际训练中,基线模型还会输出一系列子任务的效果,如下是训练 80 个 Epoch 后的输出结果,我们可以看到除了 Logic Form 和 Execution 准确率外,还有 Sel-Num 等 8 个子任务的准确率。
如上所示「Sel-」表示 SELECT 部分的子任务,「W-」表示 WHERE 部分的子任务。例如 Sel-Col 表示选择列的正确率 、Sel-Agg 为选择聚合函数的正确率、W-Op 为选择条件类型的正确率、W-Val 为选择条件值的正确率等等。
差异与改进
前面基线模型展示了一般的解决方案,目前也有很多优秀的 NL2SQL 模型可以借鉴,它们在 WikiSQL 数据集上达到了 SOTA 结果。但这里我们需要理解竞赛数据集与 WikiSQL 之间的差别是什么,这样才能有目的地改进。
数据集的三大差异
追一科技表示本次竞赛数据集与 WikiSQL 主要有三点差异/难点,它们也是本次比赛中选手需要去解决的核心问题。
表格内容对选手是可见的,并且问题具有泛化性
以此表为例,如果在 WikiSQL 中,自然语言问句应该是「腾讯控股的股票今天涨了多少」,相应的 SQL 语句是「SELECT 涨跌幅 FROM TABLE WHERE 股票名称=="腾讯控股"」,所以模型可以简单地从原句中抽取一个子字符串来作为条件值。但在挑战赛的数据集中,同样的问句可能会变成「鹅厂今天涨了多少」,这就要求模型不仅可以预测出需要针对「股票名称」这一列做条件限制,同时还要能泛化到表格的内容来生成正确的 SQL。
省略显式的列名信息,更口语化的表达
在 WikiSQL 中,自然语言问句由于标注方式的原因,更像是基于模板的问句,所以通常会带有比较明显的列名信息。比如「腾讯控股的股票今天涨了多少」就会带有「股票」这个列名信息。在挑战赛的数据集中,问题会变得更加口语化、贴近真实场景,这样就要求模型首先要把腾讯和股票名称这一列联系起来,才能正确地生成 SQL。
数字的单位问题
针对如下这样的表格,数据集中的问题可能是,我们的问题为:「北京这周成交了多少千平方米?」。这样在千平方米和万平方米间就存在非归一化单位的问题。
提升方案
前面展示了几种数据集的不同之处,理解它们对改进方案非常重要。前面已经介绍过 X-SQL 和 SQLova 等 WikiSQL 当前最佳模型都在采用 BERT、MT-DNN 作为输入表征,它们能取得非常好的效果。此外,模型的处理流程和依赖关系都可以改进,因此可以总结为如下三个方向:
BERT、XLNet 等更好的输入特征
更合理的模型结构:目前的基线模型是 pipeline 的方式,预测完 condition column 之后再去预测 condition op 和 condition value。但在真实数据中,我们需要一种联合预测的方式来解决问题。
如何合理地将表格内数据也结合进模型,以前的方案都忽略了表格内数据。
这里展示几篇上面3个方向的论文,更多顶尖模型可以在 WikiSQL 基准中找到:
[1]X-SQL: reinforce context into schema representation, 2019
[3]Semantic Parsing with Syntax- and Table-Aware SQL Generation, 2018
[1]A Comprehensive Exploration on WikiSQL with Table-Aware Word Contextualization, 2019
WikiSQL 基准:https://github.com/salesforce/WikiSQL