NL2SQL實踐系列(1):深入解析Prompt工程在text2sql中的應用技巧

汀、人工智能發表於2024-04-18

NL2SQL實踐系列(1):深入解析Prompt工程在text2sql中的應用技巧

NL2SQL基礎系列(1):業界頂尖排行榜、權威測評資料集及LLM大模型(Spider vs BIRD)全面對比優劣分析[Text2SQL、Text2DSL]

NL2SQL基礎系列(2):主流大模型與微調方法精選集,Text2SQL經典演算法技術回顧七年發展脈絡梳理

NL2SQL進階系列(1):DB-GPT-Hub、SQLcoder、Text2SQL開源應用實踐詳解

NL2SQL進階系列(2):DAIL-SQL、DB-GPT開源應用實踐詳解[Text2SQL]

NL2SQL進階系列(3):Data-Copilot、Chat2DB、Vanna Text2SQL最佳化框架開源應用實踐詳解[Text2SQL]

☆☆NL2SQL進階系列(4):ConvAI、DIN-SQL、C3-浙大、DAIL-SQL-阿里等16個業界開源應用實踐詳解[Text2SQL]

☆☆NL2SQL進階系列(5):論文解讀業界前沿方案(DIN-SQL、C3-SQL、DAIL-SQL、SQL-PaLM)、新一代資料集BIRD-SQL解讀

NL2SQL實踐系列(1):深入解析Prompt工程在text2sql中的應用技巧

NL2SQL實踐系列(2):2024最新模型實戰效果(Chat2DB-GLM、書生·浦語2、InternLM2-SQL等)以及工業級案例教學

NL2SQL任務的目標是將使用者對某個資料庫的自然語言問題轉化為相應的SQL查詢。隨著LLM的發展,使用LLM進行NL2SQL已成為一種新的正規化。在這一過程中,如何利用提示工程來發掘LLM的NL2SQL能力顯得尤為重要。

prompt的組成包四個元素:

  • Instruction(指令,必須)
  • Context(上下文資訊,可選)
  • Input Data(需要處理的資料,可選)
  • Output Indicator(要輸出的型別或格式,可選)

一個面向複雜任務的prompt的一般都包含Instruction,Context,Input Data,Output Indicator。
所以面向大語言模型的開發應用過程就是如下公式:
LMM(Instruction + Context + Input Data + Output Indicator) = Output
prompt engineering 就是寫好這四塊東西Instruction,Context,Input Data,Output Indicator,讓模型的輸出Output越準越好

1.text2sql prompt

> prompt = """
>         現在你是一個資料分析師,SQL大神,請根據使用者提供的表的資訊,以及使用者的需求,寫出效率最高的SQL,
>         表資訊如下:
>             表名:students;
>             欄位:id,name,age,location
>         使用者需求:統計一下姓名年齡大於23,姓名包含andy且在beijing,的的學生個數。
>         並且要求輸出的SQL以#開頭,以#結尾,樣例如下:
>                 #SELECT * FROM table#
>                 #SELECT COUNT(*) FROM table#
>         注意不需要分析過程,直接給出SQL語句
>        """
> inputttext ="""<human>:
>      {}
> <aibot>:
> """.format(prompt)

輸出結果: #SELECT COUNT(*) FROM students WHERE age > 23 AND name LIKE '%andy%' AND location = 'beijing'#

2.大模型text2sql 微調教程

LLM大模型:https://huggingface.co/baichuan-inc/Baichuan-13B-Chat

訓練資料:https://huggingface.co/datasets/Clinton/Text-to-sql-v1

Baichuan-13B 是由百川智慧繼 Baichuan-7B 之後開發的包含 130 億引數的開源可商用的大規模語言模型,在權威的中文和英文 benchmark 上均取得同尺寸最好的效果。本次釋出包含有預訓練 (Baichuan-13B-Base) 和對齊 (Baichuan-13B-Chat) 兩個版本。Baichuan-13B 有如下幾個特點:

  • 更大尺寸、更多資料:Baichuan-13B 在 Baichuan-7B 的基礎上進一步擴大引數量到 130 億,並且在高質量的語料上訓練了 1.4 萬億 tokens,超過 LLaMA-13B 40%,是當前開源 13B 尺寸下訓練資料量最多的模型。支援中英雙語,使用 ALiBi 位置編碼,上下文視窗長度為 4096。
  • 同時開源預訓練和對齊模型:預訓練模型是適用開發者的“基座”,而廣大普通使用者對有對話功能的對齊模型具有更強的需求。因此本次開源同時釋出了對齊模型(Baichuan-13B-Chat),具有很強的對話能力,開箱即用,幾行程式碼即可簡單的部署。
  • 更高效的推理:為了支援更廣大使用者的使用,本次同時開源了 int8 和 int4 的量化版本,相對非量化版本在幾乎沒有效果損失的情況下大大降低了部署的機器資源門檻,可以部署在如 Nvidia 3090 這樣的消費級顯示卡上。
  • 開源免費可商用:Baichuan-13B 不僅對學術研究完全開放,開發者也僅需郵件申請並獲得官方商用許可後,即可以免費商用。
資料格式如下:
"""Below are sql tables schemas paired with instruction that describes a task. Using valid SQLite, write a response that appropriately completes the request for the provided tables. ### Instruction: provide the number of patients whose diagnoses icd9 code is 60000? ### Input: CREATE TABLE procedures (\n    subject_id text,\n    hadm_id text,\n    icd9_code text,\n    short_title text,\n    long_title text\n)\n\nCREATE TABLE prescriptions (\n    subject_id text,\n    hadm_id text,\n    icustay_id text,\n    drug_type text,\n    drug text,\n    formulary_drug_cd text,\n    route text,\n    drug_dose text\n)\n\nCREATE TABLE demographic (\n    subject_id text,\n    hadm_id text,\n    name text,\n    marital_status text,\n    age text,\n    dob text,\n    gender text,\n    language text,\n    religion text,\n    admission_type text,\n    days_stay text,\n    insurance text,\n    ethnicity text,\n    expire_flag text,\n    admission_location text,\n    discharge_location text,\n    diagnosis text,\n    dod text,\n    dob_year text,\n    dod_year text,\n    admittime text,\n    dischtime text,\n    admityear text\n)\n\nCREATE TABLE lab (\n    subject_id text,\n    hadm_id text,\n    itemid text,\n    charttime text,\n    flag text,\n    value_unit text,\n    label text,\n    fluid text\n)\n\nCREATE TABLE diagnoses (\n    subject_id text,\n    hadm_id text,\n    icd9_code text,\n    short_title text,\n    long_title text\n) ### Response:SELECT COUNT(DISTINCT demographic.subject_id) FROM demographic INNER JOIN diagnoses ON demographic.hadm_id = diagnoses.hadm_id WHERE diagnoses.icd9_code = "60000" """

訓練程式碼:text2sqlBaichuan13B.py

2.1 姜子牙系列模型

  • Ziya-LLaMA-13B-v1.1
  • Ziya-LLaMA-13B-v1
  • Ziya-LLaMA-7B-Reward
  • Ziya-LLaMA-13B-Pretrain-v1

姜子牙通用大模型V1是基於LLaMa的130億引數的大規模預訓練模型,具備翻譯,程式設計,文字分類,資訊抽取,摘要,文案生成,常識問答和數學計算等能力。目前姜子牙通用大模型已完成大規模預訓練、多工有監督微調和人類反饋學習三階段的訓練過程。

https://huggingface.co/IDEA-CCNL/Ziya-LLaMA-13B-v1

https://github.com/IDEA-CCNL/Ziya-Coding

https://www.modelscope.cn/models/Fengshenbang/Ziya-LLaMA-13B-v1/summary

繼續預訓練 Continual pretraining
原始資料包含英文和中文,其中英文資料來自openwebtext、Books、Wikipedia和Code,中文資料來自清洗後的悟道資料集、自建的中文資料集。在對原始資料進行去重、模型打分、資料分桶、規則過濾、敏感主題過濾和資料評估後,最終得到125B tokens的有效資料。
為了解決LLaMA原生分詞對中文編解碼效率低下的問題,在LLaMA詞表的基礎上增加了7k+個常見中文字,透過和LLaMA原生的詞表去重,最終得到一個39410大小的詞表,並透過複用Transformers裡LlamaTokenizer來實現了這一效果。
在增量訓練過程中,使用了160張40GB的A100,採用2.6M tokens的訓練集樣本數量和FP 16的混合精度,吞吐量達到118 TFLOP per GPU per second。因此能夠在8天的時間裡在原生的LLaMA-13B模型基礎上,增量訓練110B tokens的資料。

  • 多工有監督微調 Supervised finetuning
    在多工有監督微調階段,採用了課程學習(curiculum learning)和增量訓練(continual learning)的策略,用大模型輔助劃分已有的資料難度,然後透過“Easy To Hard”的方式,分多個階段進行SFT訓練。SFT訓練資料包含多個高質量的資料集,均經過人工篩選和校驗:
  1. Self-Instruct構造的資料(約2M):BELLE、Alpaca、Alpaca-GPT4等多個資料集
  2. 內部收集Code資料(300K):包含leetcode、多種Code任務形式
  3. 內部收集推理/邏輯相關資料(500K):推理、申論、數學應用題、數值計算等
  4. 中英平行語料(2M):中英互譯語料、COT型別翻譯語料、古文翻譯語料等
  5. 多輪對話語料(500K):Self-Instruct生成、任務型多輪對話、Role-Playing型多輪對話等

Ziya2-13B-Chat採用"<human>:"和"<bot>:"作為使用者和模型的角色識別Prompt,使用"\n"分隔不同角色對話內容。 在推理時,需要將"<human>:"和"<bot>:"作為字首分別拼接至使用者問題和模型回覆的前面,並使用"\n"串連各對話內容。

Ziya2-13B-Chat adopts "<human>:" and "<bot>:" as the role recognition prompts for users and models, and uses "\n" to separate the contents of different roles. When doing inference, "<human>:" and "<bot>:" need to be concatenated as prefixes in front of the user's question and the model's reply respectively, and "\n" is used to join the contents of each role.

以下為具體使用方法:

Following are the details of how to use it:

from modelscope import AutoTokenizer, AutoModelForCausalLM, snapshot_download
import torch

device = torch.device("cuda")

messages = [{"role": "user", "content": "手機如果貼膜貼了一張防指紋的鋼化膜,那螢幕指紋解鎖還有效嗎?"}]
user_prefix = "<human>:"
assistant_prefix = "<bot>:"
separator = "\n"

prompt = []
for item in messages:
    prefix = user_prefix if item["role"] == "user" else assistant_prefix
    prompt.append(f"{prefix}{item['content']}")
prompt.append(assistant_prefix)
prompt = separator.join(prompt)

model_dir = snapshot_download('Fengshenbang/Ziya2-13B-Chat', revision='master')
model = AutoModelForCausalLM.from_pretrained(model_dir,torch_dtype=torch.bfloat16).to(device)
tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False)
input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to(device)
generate_ids = model.generate(
            input_ids,
            max_new_tokens=512, 
            do_sample = True, 
            top_p = 0.9, 
            temperature = 0.85, 
            repetition_penalty=1.05, 
            eos_token_id=tokenizer.encode("</s>"), 
            )
output = tokenizer.batch_decode(generate_ids)[0]
print(output)

模型部署

import gradio as gr
import os
import gc
import torch


from transformers import AutoTokenizer
#指定環境的GPU,我的環境是2張A100(40GB)顯示卡,於是我設定了兩張卡,也可以一張80GB的A100
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"
#這個utils檔案直接下載官方給的檔案即可
from utils import SteamGenerationMixin


class MindBot(object):
    def __init__(self):
    	#這個model_path為你本地的模型路徑
        model_path = './ziya_v1.1'
        self.model = SteamGenerationMixin.from_pretrained(model_path, device_map='auto').half()
        self.model.eval()
        
        self.tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False)
    
    def build_prompt(self, instruction, history, human='<human>', bot='<bot>'):
        pmt = ''
        if len(history) > 0:
            for line in history:
                pmt += f'{human}: {line[0].strip()}\n{bot}: {line[1]}\n'
        pmt += f'{human}: {instruction.strip()}\n{bot}: \n'
        return pmt
    
    def interaction(
        self,
        instruction,
        history,
        max_new_tokens,
        temperature,
        top_p,
        max_memory=1024
    ):
               
        prompt = self.build_prompt(instruction, history)
        input_ids = self.tokenizer(prompt, return_tensors="pt").input_ids
        if input_ids.shape[1] > max_memory:
            input_ids = input_ids[:, -max_memory:]
            
        prompt_len = input_ids.shape[1]
        # stream generation method
        try:
            tmp = history.copy()
            output = ''
            with torch.no_grad():
                for generation_output in self.model.stream_generate(
                    input_ids.cuda(),
                    max_new_tokens=max_new_tokens, 
                    do_sample=True,
                    top_p=top_p, 
                    temperature=temperature, 
                    repetition_penalty=1., 
                    eos_token_id=2, 
                    bos_token_id=1, 
                    pad_token_id=0
                ):
                    s = generation_output[0][prompt_len:]
                    output = self.tokenizer.decode(s, skip_special_tokens=True)
                    # output = output.replace('\n', '<br>')
                    output = output.replace('\n', '\n\n')
                    tmp.append((instruction, output))
                    yield  '', tmp
                    tmp.pop()
                    # gc.collect()
                    # torch.cuda.empty_cache()
                history.append((instruction, output))
                print('input -----> \n', prompt)
                print('output -------> \n', output)
                print('history: ======> \n', history)
        except torch.cuda.OutOfMemoryError:
            gc.collect()
            torch.cuda.empty_cache()
            self.model.empty_cache()
            history.append((instruction, "【視訊記憶體不足,請清理歷史資訊後再重試】"))
        return "", history
        
    def chat(self):
        
        with gr.Blocks(title='IDEA MindBot', css=".bgcolor {color: white !important; background: #FFA500 !important;}") as demo:
            with gr.Row():
                gr.Column(scale=0.25)
                with gr.Column(scale=0.5):
                    gr.Markdown("<center><h1>IDEA Ziya</h1></center>")
                    gr.Markdown("<center>姜子牙通用大模型V1.1是基於LLaMa的130億引數的大規模預訓練模型,具備翻譯,程式設計,文字分類,資訊抽取,摘要,文案生成,常識問答和數學計算等能力。目前姜子牙通用大模型已完成大規模預訓練、多工有監督微調和人類反饋學習三階段的訓練過程。</center>")
                gr.Column(scale=0.25)
            with gr.Row():
                gr.Column(scale=0.25)
                with gr.Column(scale=0.5):
                    chatbot = gr.Chatbot(label='Ziya').style(height=500)
                    msg = gr.Textbox(label="Input")
                # gr.Column(scale=0.25)
                with gr.Column(scale=0.25):
                    max_new_tokens = gr.Slider(0, 2048, value=1024, step=1.0, label="Max_new_tokens", interactive=True)
                    top_p = gr.Slider(0, 1, value=0.85, step=0.01, label="Top P", interactive=True)
                    temperature = gr.Slider(0, 1, value=0.8, step=0.01, label="Temperature", interactive=True)
            with gr.Row():
                gr.Column(scale=0.25)
                with gr.Column(scale=0.25):
                    clear = gr.Button("Clear")
                with gr.Column(scale=0.25):
                    submit = gr.Button("Submit")
                gr.Column(scale=0.25)
                
            msg.submit(self.interaction, [msg, chatbot,max_new_tokens,top_p,temperature], [msg, chatbot])
            clear.click(lambda: None, None, chatbot, queue=False)
            submit.click(self.interaction, [msg, chatbot,max_new_tokens,top_p,temperature], [msg, chatbot])
        return demo.queue(concurrency_count=10).launch(share=False,server_name="127.0.0.1", server_port=7886)
        

if __name__ == '__main__':
    mind_bot = MindBot()
    mind_bot.chat()
  • 人類反饋學習 Reinforcement learning from Human Feedback

基於SFT階段的模型,Ziya2-13B-Chat針對多種問答、寫作以及模型安全性的任務上進行了人類偏好的對齊。自行採集了數萬條高質量人類偏好資料,使用Ziya2-13B-Base訓練了人類偏好反饋模型,在各任務的偏好資料上達到了72%以上的準確率。

  • 效果評估 Performance
    Ziya2-13B-Base在Llama2-13B的基礎上進行了約650B自建高質量中英文資料集的繼續訓練,在中文、英文、數學、程式碼等下游理解任務上相對於Llama2-13B取得了明顯的提升,相對Ziya-LLaMA-13B也有明顯的提升。

3.Prompt升級

參考文章:https://zhuanlan.zhihu.com/p/635799364?utm_id=0

  • 第一版

儘管模型的輸出SQL語句本身都是正確的,卻存在著一個明顯的問題:它會產生多餘的輸出。具體來說,模型似乎過度地“幻想”了SQL查詢的結果,將一些並不屬於實際查詢結果的資料也一併輸出,這導致了資訊冗餘和不必要的複雜性。

  • 第二版

經過版本升級後,引入了角色扮演的功能,告知模型它現在是一名資料分析師,且精通SQL。然而,儘管模型的輸出SQL語句本身是正確的,但結果呈現的方式卻不夠結構化,這導致它並不適合後續的操作和分析。期望模型僅輸出一段單獨的、結構清晰的SQL語句,而不是包含多餘或複雜化的輸出。作為資料分析師,更注重結果的準確性和實用性,因此希望模型能夠在這方面進行改進。

  • 第三版

經過進一步的版本升級,增強了模型的輸出引導功能,希望它輸出的SQL語句能夠以“#”開頭,並以“#”結尾。然而,發現儘管模型的SQL語句本身是正確的,但其輸出結果卻存在錯誤:結尾部分缺少了一個“#”。這導致了輸出格式的不一致和潛在的識別問題。期待模型在輸出SQL時能夠嚴格遵守規定的格式,確保每個SQL語句都以“#”完整包圍,以滿足後續處理和分析的需求。

  • 最終版

經過又一次的版本升級,不僅在輸出引導方面進行了增強,還提供了具體的示例,以幫助模型更好地理解的期望。這次,欣喜地發現,模型的輸出SQL語句完全符合的需求。透過明確的輸出引導和示例,模型能夠準確地生成結構清晰、格式規範的SQL語句,為後續的資料處理和分析提供了極大的便利。這一改進不僅提升了模型的效能,也進一步提高了的工作效率和準確性。

至此,已深入掌握面向大模型開發的核心技術,學會如何有效利用大模型的強大能力,以應對各類複雜任務。整個過程可細化為以下關鍵步驟:

  • 首先,需精心構建高質量的prompt。其中,指令(Instruction)的設定至關重要,需精確、明確地傳達的任務需求;上下文資訊(Context)作為可選內容,有助於模型更全面地理解問題背景;輸入資料(Input Data)是模型處理的具體物件,應根據任務特點靈活選擇;輸出指引(Output Indicator)則用於規定輸出結果的型別、格式及精度,確保模型輸出符合專業標準。

  • 隨後,需要不斷迭代與最佳化prompt。這是一個精細的除錯過程,透過對比分析模型的實際輸出與預期結果,可以發現prompt中的不足之處,進而針對性地調整其表述和細節。透過多次迭代,可以逐步完善prompt,使模型輸出更加精確、全面。

  • 最後,驗證prompt的穩定性和準確性是不可或缺的環節。透過大量的測試與驗證,可以評估模型在不同情境下的表現,確保其輸出的穩定性和可靠性。此外,還需要關注模型的泛化能力,確保其能夠應對各種未知或複雜情況。

更多優質內容請關注公號:汀丶人工智慧;會提供一些相關的資源和優質文章,免費獲取閱讀。

相關文章