曹金超,黃 滔,陳 剛,3,吳曉凡,陳 珂,3+
1.浙江大學 計算機科學與技術學院,杭州310027
2.浙江邦盛科技有限公司,杭州310012
3.浙江大學 浙江省大數(shù)據(jù)智能計算重點實驗室,杭州310027
4.網(wǎng)易(杭州)網(wǎng)絡有限公司,杭州310051
隨著信息技術的快速變革,如何通過自然語言直接與傳統(tǒng)關系數(shù)據(jù)庫交互,也就是如何將人的自然問句轉化成數(shù)據(jù)庫可以執(zhí)行的SQL(structured query language)語句已經(jīng)成為人工智能領域的研究熱點之一。為方便管理,可以利用關系型數(shù)據(jù)庫來存儲數(shù)據(jù),然而通過關系型數(shù)據(jù)庫來查詢數(shù)據(jù)需要預先學習數(shù)據(jù)庫系統(tǒng)以及數(shù)據(jù)庫查詢語句SQL上的專業(yè)知識,這對一個普通用戶來說顯然很難實現(xiàn)。為了解決這個問題,許多研究工作都采用了相似的解決思路,通過實現(xiàn)一個自然語言查詢接口(natural language interface,NLI)來完成用戶通過日常使用的自然語言與數(shù)據(jù)庫進行交互從而獲取想要查詢的數(shù)據(jù)[1]。
如今,自然語言查詢接口也產(chǎn)生了很多商業(yè)上的應用,例如智能語音助手、智能搜索引擎和對話系統(tǒng)等。同時,該問題亦是新型供電軌道交通系統(tǒng)混合時態(tài)大數(shù)據(jù)個性化運維的最棘手的難點之一。
NLI 最重要的一個部分就是將自然語言查詢描述轉化為標準的SQL 查詢語句(NL2SQL)。針對這個問題,目前有兩個不同的解決思路:(1)流水線方法,將自然語言查詢轉化為一種中間表達,再將其映射為SQL 語句;(2)深度學習方法,使用端到端的神經(jīng)網(wǎng)絡來完成轉化[1]。
流水線方法將自然語言轉化為中間表達的過程依賴于自然語言查詢的規(guī)則化描述,因此無法處理一些復雜多變的自然語言描述。而近年來,隨著深度學習技術的不斷發(fā)展,使用神經(jīng)網(wǎng)絡模型來處理NL2SQL 的工作也越發(fā)普遍。其優(yōu)勢較前者在于不受自然語言描述多樣性的限制,也能從復雜的表達中提取出關注的語義信息。但現(xiàn)有NL2SQL 的深度學習方法存在著一定的局限性,需指定當前自然語言查詢對應數(shù)據(jù)庫中的哪一張表,也就忽略了自然語言查詢到對應查詢表的映射,同時也無法支持如圖1中所示例的多表查詢。
從這個局限性出發(fā),本文針對自然語言生成多表SQL查詢的問題提出了一種解決方法。本文主要的工作和貢獻如下:
(1)相比于現(xiàn)有工作基于模板的深度學習模型,本文的深度學習模型能處理更復雜的自然語言查詢,因此能生成更為復雜的SQL 語句,包括GROUP BY、ORDER BY等關鍵詞;
(2)利用深度學習模型生成的部分SQL 子句提及的表和數(shù)據(jù)庫模式圖,采用了一種全局最優(yōu)的算法來生成FROM子句中多表的JOIN路徑;
(3)通過在一個開放的復雜自然語言生成SQL的數(shù)據(jù)集Spider上與其他多種模型的對比,本文方法具有更高的準確率。
盡管自然語言查詢接口已經(jīng)發(fā)展了很長一段時間,但要準確地理解自然語言查詢中的語義依舊是一個具有挑戰(zhàn)性的問題。早期的NL2SQL 需要針對不同的數(shù)據(jù)庫來人工制定相應的語法和語義規(guī)則,但此方法缺乏遷移性和擴展性[2]。更傾向于構建與數(shù)據(jù)庫結構和內容無關,能夠自適應的NL2SQL接口。
其中一種就是流水線的方法,這種方法通常利用一些中間表達來把自然語言查詢轉化為SQL。Unger等人[3]在2012年提出了將自然語言查詢轉化為一種名為SPARQL的查詢模板,通過WordNet來填充這個查詢模板中的槽。Jagadish 等人[4]在2014 年提出了NaLIR 系統(tǒng),該系統(tǒng)利用一種“解析樹”的中間表達,將其反饋給用戶后讓用戶選擇與自己理想查詢最匹配的“解析樹”,最終通過譯碼器將用戶選擇的“解析樹”轉化為SQL。整個過程需要用戶與系統(tǒng)交互才能確定最終的結果,自動化程度不高。上述基于流水線的方法[3-4]需要提前清晰地定義它們語義的覆蓋范圍,以及對自然語言查詢描述也存在一些規(guī)則限制,因此不能靈活地處理用戶不同方式的提問,具有一定的局限性。
而隨著深度學習技術的不斷發(fā)展,通過應用神經(jīng)網(wǎng)絡模型就能較好地解決自然語言描述復雜多變的問題。早期的研究把NL2SQL 處理成一個序列生成的問題,利用Seq2Seq 模型[5]來完成這個任務。Li等人使用Seq2Seq+Attention模型[6],通過注意力增強的方法來提升SQL 生成的準確率。Wang 等人使用Seq2Seq+Copying 模型[7],通過復制機制定位到輸入自然語言描述的其中一部分,將其直接作為輸出SQL 語句的對應的部分,復制的部分主要包括自然語言描述中數(shù)據(jù)表的列名和一些字符串或數(shù)值。然而,一般的Seq2Seq模型沒有考慮到SQL語句的格式和語法上還存在一定的規(guī)則,因此生成的SQL 可能會產(chǎn)生語法上的錯誤。因此,Xu 等人[8]在2017 年提出了一個SQLNet模型,利用預先定義好SQL查詢的模板,如圖2 所示,然后對其各個部分進行預測和填充。這種方式相對于Seq2Seq 模型來說能大幅度減少輸出SQL查詢的語法錯誤,從而提高準確率。
Fig.2 SQL sketch in SQLNet圖2 SQLNet中的SQL模板
然而針對深度學習模型,需要大量的自然語言查詢與SQL 查詢的配對標注來作為訓練數(shù)據(jù),而標注數(shù)據(jù)的獲取通常代價昂貴。針對這個問題,一些最新的自然語言處理研究工作提出了使用大規(guī)模語料來產(chǎn)生預訓練模型的方法,預訓練模型在許多自然語言處理(NLP)相關的下游任務中取得了不錯的效果,例如BERT[9](bidirectional encoder representation from transformers)、MT-DNN(multi-task deep neural network)[10]。He等人[11]提出了X-SQL模型,該模型利用MT-DNN預訓練模型對自然語言查詢和數(shù)據(jù)庫模式進行特征提取來提升效果。
現(xiàn)有的深度學習模型[5,8,11]都局限于在數(shù)據(jù)庫的單表中查詢,生成結果不包含多表的JOIN路徑,適用的范圍較窄,無法進行多表SQL 查詢生成。本文提出的模型架構能夠解決這個問題,從而實現(xiàn)包含JOIN路徑的多表SQL查詢的生成。
自然語言查詢轉化為標準SQL 查詢語句,其形式化定義為:
輸入:
由n個詞語組成的自然語言祈使句或疑問句:
數(shù)據(jù)庫模式集合:
其中,k表示該數(shù)據(jù)庫中表的個數(shù)。
其中,Ti表示當前表的表名,m表示表i列名的個數(shù),Aj表示表i的第j個列名。
數(shù)據(jù)庫模式圖(schema graph):
其中,V表示節(jié)點,對應于數(shù)據(jù)庫中的表;E表示邊,對應表之間的主外鍵關聯(lián)關系;w表示邊的權重值,默認為1。
輸出:
與自然語言查詢Q對應的SQL 查詢語句R,格式如圖3所示。
Fig.3 Generated SQL sketch in proposed model圖3 本文生成SQL的模板
其中$VAL來自于自然語言查詢Q中,本文模型暫不進行預測。$TAB和$COL分別來自數(shù)據(jù)庫模式S的表名和列名。$AGG 的取值集合為{‘NONE’,‘MAX’,‘MIN’,‘COUNT’,‘SUM’,‘AVG’},$OP 的取值集合為{‘=’,‘>’,‘<’,‘>=’,‘<=’,‘!=’},$ORD 的取值集合為{‘DESC’,‘ASC’}。(…)*代表是零個或者多個,若為零個,則括號前對應的SQL 關鍵詞也需省略,[…]*代表至少有一個。
為解決自然語言查詢生成多表SQL查詢語句的問題,本文將分為兩個階段,分別為SQL子句生成和JOIN路徑生成。通過充分利用SQL查詢語句特定的語法,將SQL 生成結果定義為圖3 的格式模板,將序列生成轉化為多個分類問題,只需要對帶“$”的部分進行填充即可獲得標準的SQL查詢語句。本文的整體模型架構如圖4所示。
輸入的編碼包括兩部分,自然語言查詢和數(shù)據(jù)庫模式。本文通過使用處理文本序列常用的雙向長短時記憶(bidirectional long short term memory,Bi-LSTM)網(wǎng)絡模型[12]來對自然語言查詢和數(shù)據(jù)庫模式中的列名進行編碼,產(chǎn)生對應的特征向量。
Fig.4 Overall architecture of proposed model圖4 本文模型的整體架構
4.1.1 帶表名信息的列名特征向量
SQL 子句的構成包含許多數(shù)據(jù)表中的列名信息,因此通過模型來獲取數(shù)據(jù)庫模式信息也是很有必要的。針對相關工作中提到的單表查詢,在SQL生成前已經(jīng)給定當前自然語言查詢對應的表,因此不必再將表的信息嵌入到列名的特征向量中。但多表查詢沒有指定對應的表,查詢范圍為對應的整個數(shù)據(jù)庫。為了解決這個問題,在做列名信息的特征表達時,同時利用表名和列名的配對來獲取列名的特征向量。
具體說來,就是給定一個數(shù)據(jù)庫模式S,首先列出表名和列名的配對作為Bi-LSTM 編碼模型的輸入。接下來,通過Bi-LSTM模型的編碼,選擇它最后的隱藏狀態(tài)來代表當前列名的特征向量Ecol。通過這樣編碼的方式,獲取的列名特征向量Ecol既包含了全局的表名信息,也包含了局部的列名信息,充分地利用了數(shù)據(jù)庫模式的信息,更有利于后續(xù)的模型理解自然語言查詢中的語義信息。
4.1.2 基于列名的注意力機制
在獲取自然語言查詢Q的特征表達時,如果只使用Q經(jīng)過Bi-LSTM 模型編碼的最后隱藏狀態(tài)EQ作為特征向量,那么在預測各個子句所包含的列名信息時,就不能突出不同列名在自然語言查詢Q中的重要程度。因此,為凸顯不同列名在自然語言查詢Q中信息側重的不同,本文采用了一種基于列名的注意力機制[13],意在提醒之后的模型,在當前的列名Ecol下,應該更加注意自然語言查詢Q中的哪些部分,從而獲得當前列名對應Q的特征向量EQ|col。
首先,計算針對自然語言查詢Q中每個詞的權重值ω,從而獲得一個長度為句長n的向量:
其中,vi表示向量v的第i維,表示自然語言查詢Q的第i個詞的編碼隱藏狀態(tài),取值范圍從1到n,n是自然語言查詢的長度,W是可訓練的參數(shù)矩陣,其維度為d×d,d表示編碼模型隱藏狀態(tài)輸出的維度。
計算了權重值ω之后,就能將自然語言查詢Q中通過Bi-LSTM模型編碼的每個詞的隱藏狀態(tài)進行加權乘積后再求和,來獲得當前列名對應Q的特征向量:
其中,HQ表示自然語言查詢Q中所有詞的編碼隱藏狀態(tài)矩陣,其矩陣維度為d×n。
本文將SQL 子句的生成分為4 個子任務,包括SELECT、WHERE、GROUP BY 和ORDER BY 子句的預測,每個子任務都是SQL語句的一個部分,各子句中預測填充部分之間相互依賴關系如圖4所示。
4.2.1 模型細節(jié)
將模板中各子句中的填充內容處理成多個多分類問題,采用softmax 函數(shù)為每個類別計算概率來進行分類。為后續(xù)公式表達簡潔清晰,先對矩陣M定義概率分布Pc,計算公式為:
其中,V是可訓練參數(shù)。
在接下來各子句預測模塊的公式中,Ecol和EQ|col分別代表了所有列名編碼和其做注意力機制后對應自然語言查詢Q的矩陣集合,其矩陣維度均為m×d,m表示數(shù)據(jù)庫列名的數(shù)量,d為編碼隱藏狀態(tài)輸出維度。還有其中所有的W都表示為可訓練的模型參數(shù),且各子句預測不共享參數(shù)。
(1)SELECT子句
首先預測子句中列名COL 的數(shù)量,再預測使用哪些列名,取值范圍為對應數(shù)據(jù)庫中所有表名列名配對的集合,公式如下:
在選定使用的列名之后,接下來需要對列名預測其AGG 的數(shù)量和具體是集合{‘NONE’,‘MAX’,‘MIN’,‘COUNT’,‘SUM’,‘AVG’}中的哪些聚合操作,假設當前列名的編號為i,那么公式如下:
(2)WHERE子句
同樣,首先預測子句中列名COL的數(shù)量,再預測使用哪些列名,公式如下:
在選定列名之后,需要對列名預測其OP 操作,取值集合為{‘=’,‘>’,‘<’,‘>=’,‘<=’,‘!=’},假設當前列名的編號為i,公式如下:
(3)GROUP BY子句
該子句的預測模型相對來說更簡單一些,只需預測出子句中列名COL 的數(shù)量和對應哪些列名即可,公式如下:
(4)ORDER BY子句
與SELECT子句預測模型結構類似,首先預測子句中列名COL的數(shù)量,再預測對應使用的列名,公式如下:
在確定使用的列名之后,就需要對列名的升降序操作ORD進行預測,取值集合為{‘DESC’,‘ASC’},假設當前列名的編號為i,公式如下:
4.2.2 訓練和推理
在訓練的過程中,其目標函數(shù)是所有子任務損失函數(shù)的總和,通過最小化目標函數(shù)值的方式來進行參數(shù)更新。針對各子句中對列名COL 的預測,一個數(shù)據(jù)庫中往往有很多列名,而只有少數(shù)幾個列名是選中的正例,其他未出現(xiàn)在SQL 語句中的列名即為負例。為解決樣本不均衡這個問題,針對各子句列名預測部分Pcol,本文使用帶權值的對數(shù)似然損失函數(shù)來表示其損失(loss),公式如下:
其中,α是用來平衡正負樣例的超參數(shù),在本文的實驗中根據(jù)經(jīng)驗取值為3,N表示樣本的數(shù)量,x表示訓練數(shù)據(jù),y表示真值。
其他非列名預測子任務的損失采用傳統(tǒng)標準的交叉熵損失函數(shù)。在模型的訓練過程中,由于已知子句中各部分預測的真實結果,因此各子任務之間互不影響,可并行訓練。
但在預測推理階段,根據(jù)圖4 所示,以SELECT子句為例,在預測列名COL 對應的操作AGG 之前,需要先預測COL的數(shù)量和取值。因此在預測階段模型是不可并行的,并且在預測推理的過程中,一旦某個部分預測出錯,那這個錯誤一直傳遞下去,對后續(xù)的預測造成影響。
在4.2節(jié)的深度學習模型中,已經(jīng)完成了SQL語句中除FROM 子句部分的預測。接下來,需要根據(jù)之前各子句中生成結果中列名所包含的表,通過選擇表與表之間的關系和產(chǎn)生JOIN路徑的條件來生成SQL查詢語句中的FROM子句。
生成FROM 子句中的JOIN 路徑,首先需要獲得已生成的SQL其他子句中列名所包含的表集合St和對應數(shù)據(jù)庫的數(shù)據(jù)庫模式圖G,數(shù)據(jù)庫模式圖描述了數(shù)據(jù)庫中各個表之間的主外鍵關聯(lián)關系,圖5所示為一個數(shù)據(jù)庫的數(shù)據(jù)庫模式圖[1]。
Fig.5 Example of database schema graph圖5 一個數(shù)據(jù)庫模式圖的例子
目前已有的做法[14]是將St中所有的表,選定其中一個節(jié)點為基準點,通過在G上使用廣度優(yōu)先遍歷(breadth first search,BFS)算法來獲取到其他節(jié)點的JOIN 路徑。這個算法依賴于基準點的選擇,是一種局部最優(yōu)化的算法,基準點不同會導致最終生成JOIN路徑結果的不同。
本文將通過表集合St={tab1,tab2,…,tabm}和數(shù)據(jù)庫模式圖G=(V,E,w)來獲取最優(yōu)的JOIN路徑的問題建模成斯坦納樹(Steiner tree)問題[15],目標是從圖G中尋找一棵包括集合St中所有節(jié)點且開銷最小的樹,同時是JOIN路徑的最優(yōu)解。其中St表示表名集合,m為表的數(shù)量。本文采用如下的算法[15]來獲取最優(yōu)的JOIN路徑:
算法1斯坦納樹生成算法
輸入:表集合St={tab1,tab2,…,tabm}和數(shù)據(jù)庫模式圖G=(V,E,w)。
輸出:JOIN路徑,即斯坦納樹Th。
步驟1由G和St構建一個完全圖G1=(V1,E1,w1),其中V1=St;
步驟2由G1獲得其最小生成樹T1(若有多棵最小生成樹,隨機選擇一棵);
步驟3將T1中的邊替換為G中對應的最短路徑(若有多條最短路徑,隨機選擇一條),得到G的子圖Gs;
步驟4由Gs獲得其最小生成樹Ts(若有多棵最小生成樹,隨機選擇一棵);
步驟5刪除Ts中不必要的節(jié)點,得到斯坦納樹Th,其中Th的葉子節(jié)點全部來自St。
通過算法1,得到JOIN 路徑之后,再根據(jù)其路徑上的表之間的主外鍵關聯(lián)關系產(chǎn)生JOIN 條件,最終生成FROM 子句。相比于BFS 算法,此算法求解的路徑是全局最優(yōu)解,因此理論上生成FROM 子句的準確率會更高。
本文的實驗環(huán)境為一臺小型服務器,其操作系統(tǒng)為Ubuntu 16.04.6 LTS,處理器型號為Intel?Xeon?Silver 4110 CPU@2.10 GHz,內存為192 GB,GPU 型號為NVIDIA Quadro P5000,16 GB。本文實驗使用的編程語言為Python,所有的模型訓練都在深度學習框架PyTorch下進行。
本文使用的數(shù)據(jù)集是2018年發(fā)表的Spider數(shù)據(jù)集[16],是一個包含復雜SQL查詢和跨領域數(shù)據(jù)庫的大規(guī)模人工標注英文文本生成SQL數(shù)據(jù)集。該數(shù)據(jù)集中包含206 個數(shù)據(jù)庫,在去除一些嵌套的SQL 查詢后,獲得的訓練集3 917條配對數(shù)據(jù),測試集數(shù)據(jù)993條配對數(shù)據(jù)。
本文對實驗結果采用的評價方式是文獻[8]的匹配準確率(query-match accuracy),通過將生成的SQL查詢和真值轉化成一種標準化的表達,再來比較兩者之間是否匹配,這種衡量標準能減少因子句中多列名出現(xiàn)順序不同而導致不匹配的錯誤。
根據(jù)圖3 中SQL 語句模板,還有針對單獨一個SQL 子句生成結果的評價指標——子句匹配分值(keyword match score),包括了精確率p、召回率r和F1值。以FROM子句為例,F(xiàn)ROM$TAB JOIN($TAB)*,其中的TAB 對應的表就是一個需要預測的值單元,若預測結果為表集合pred={pt1,pt2,…,ptm},子句中表的真值為集合gold={gt1,gt2,…,gtn},那么精確率p、召回率r和F1值定義如下:
在實驗過程中,本文采用了固定的預訓練好的詞向量GloVe[17]來對自然語言查詢和數(shù)據(jù)庫模式進行特征表達,使用Bi-LSTM模型進行文本特征提取,隱藏層維度設置為100維,使用Adam優(yōu)化器[18],學習率設置為1E-3,訓練迭代次數(shù)300 次,批量大?。╞atch size)為64,每次迭代都隨機打亂訓練數(shù)據(jù)的順序。
表1 中給出了不同模型在測試集上對SQL 中4個子句預測的效果,可以觀察到本文模型相較于Seq2Seq 模型以及其改進方法在各個子句上都有明顯提升,且Seq2Seq 模型預測WHERE 子句的效果極差。而針對SQLNet模型來說,其定義SQL語句的模板如圖2 所示,只能對SELECT 和WHERE 子句進行預測。本文對WHERE子句的預測模型和SQLNet中的相同,因此在實驗效果上能保持一致。在SELECT子句預測時,本文模型能預測多個列名,以及一個列名對應的多個聚合操作,SQLNet 模型只支持預測一個列名和其對應的聚合操作,而實驗數(shù)據(jù)中有的SELECT 子句中不止一個列名,因此本文方法在SELECT子句上的預測效果提升較大。
Table 1 F1 of matching 4 clauses in SQL queries on test set表1 測試集上SQL查詢中4個子句匹配的F1 值 %
為更準確地測試算法1與BFS算法在JOIN路徑生成結果的對比,不受SQL 子句生成實驗效果的影響,實驗將訓練集和測試集中除去FROM 子句的SQL 查詢中的表名構建出真實的表集合S,以及通過數(shù)據(jù)庫主外鍵關聯(lián)關系構建數(shù)據(jù)庫模式圖G,兩者同時構成JOIN路徑生成算法的輸入。
通過表2實驗結果分析,可以看出算法1在JOIN路徑的生成上相比于BFS算法在精確率p和召回率r上都有所提升。因此,利用算法1在5.1節(jié)實驗結果的基礎上生成FROM子句以構成完整的SQL語句更為可靠。
Table 2 Experiment result comparison of FROM clause generation表2 FROM子句生成的實驗結果對比 %
為生成結構如圖3所示的SQL查詢語句,實驗過程分為兩個階段,第一階段使用5.1節(jié)中的SQL子句生成結果,第二階段需要將SQL 子句中除FROM 子句的表名構成JOIN路徑生成的表名集合輸入St,然后將算法的輸出轉化為FROM 子句,最終生成SQL查詢的結果。本文的實驗中第二階段的JOIN路徑生成均采用算法1。
從表3 中的實驗結果可以得出,Seq2Seq 模型在表1 中WHERE 子句結果不理想的情況下,在完整SQL 語句的查詢匹配準確率還能達到19%~23%之間,說明數(shù)據(jù)中一部分的SQL 查詢語句中不包含WHERE 子句。SQLNet 模型由于其局限性,不能用于生成多表查詢SQL,因此無法統(tǒng)計其實驗效果。結果表明使用本文兩個階段的模型和算法在多表SQL查詢語句生成上能有效地提升準確率。
Table 3 Query-match accuracy comparison of full pipeline表3 完整流程的查詢匹配準確率對比
本文針對自然語言生成多表SQL 查詢問題,提出了SQL 子句生成和JOIN 路徑生成兩個階段的改進模型和方法。在SQL子句生成階段使用深度學習模型對SQL 語句模板進行填充,將序列生成處理為多個多分類問題,在JOIN 路徑生成階段將其建模為斯坦納樹問題求解。實驗結果表明,本文方法的實驗效果相比于之前的模型和方法上都有明顯的提升,但查詢匹配的準確率仍然不高。由于本文實驗數(shù)據(jù)集Spider的特性,WHERE子句中的VAL不一定出現(xiàn)在自然語言中,對其預測帶來困難,因此本文對VAL暫未進行預測。接下來的工作可以考慮完成對VAL的預測來生成完整的SQL 語句。除此之外,由于缺乏中文自然語言生成SQL 查詢語句相關的數(shù)據(jù)集,因此無法驗證本文模型在中文語料上的實驗效果。