本文以Qwen系列大模型为例,介绍如何在人工智能平台PAI上构建从训练数据生成、模型微调训练到服务部署和调用的NL2BI全链路解决方案。
背景信息
NL2SQL(自然语言生成SQL)技术旨在将自然语言转换为数据库查询语句,使得即使不熟悉SQL的用户也能轻松地从数据库中提取数据进行分析,从而提高决策效率并深入挖掘数据背后的商业洞察(NL2BI)。
通过将NL2SQL与当前先进的大型语言模型相结合,可以显著增强语义理解能力,并支持更复杂的SQL语法生成,实现更精准的SQL查询和更流畅的数据提取分析体验。
尽管如此,NL2BI的应用场景仍面临诸多挑战,例如LLM推理服务的高成本和对业务语义理解能力的不足。为了解决这些问题,通常需要在您自己的业务场景下,对LLM的NL2BI能力进行微调训练。本方案的数据生产过程采用Qwen2-72B-Instruct大语言模型,模型微调采用CodeQwen1.5-7B-Chat模型。
使用流程
基于LLM的NL2BI全链路解决方案的使用流程如下:
您可以参照数据格式要求和数据准备策略,并针对特定的业务场景准备相应的训练数据集。由于NL2SQL人工标注需要较大的工作量,您可以使用PAI-DLC及Qwen2-72B大模型进行批量数据生成,用于后续的模型训练。
在快速开始中,基于CodeQwen1.5-7B-Chat模型进行训练。
通过快速开始将训练好的模型部署为EAS在线服务。
前提条件
在开始执行操作前,请确认您已完成以下准备工作:
已开通PAI(DLC、EAS)后付费,并创建默认工作空间,详情请参见开通PAI并创建默认工作空间。
已创建OSS存储空间(Bucket),用于存储训练数据和训练获得的模型文件。关于如何创建存储空间,详情请参见控制台创建存储空间。
如果您使用RDS数据库,则需要配置包含公网访问的专有网络,进行数据库连接用于生成SQL可执行性的验证。如何创建专有网络,请参见创建和管理专有网络。如何配置公网访问,请参见NAT网关。
使用限制
仅支持在华北6(乌兰察布)地域,使用以下灵骏智算资源,基于PAI-DLC和Qwen2-72B大模型进行批量数据生成:
ml.gu7xf.8xlarge-gu108
ml.gu8xf.8xlarge-gu108
准备数据集
支持使用以下两种方式准备训练数据,本方案以方式二为例,为您介绍如何准备训练数据:
方式二:基于PAI-DLC及Qwen2-72B大模型进行批量训练数据生成。只需要少量示例数据和指定规则,就可进行批量数据生成,也可作为人工标注数据的补充,提升模型能力。
本方案提供了如下数据增广示例的数据库文件、示例问题文件和证据文件,用于快速跑通数据增广流程。
数据准备策略
为了提升训练的有效性和稳定性,您可以参考以下策略准备相关数据:
数据库DDL优化
尽量减少表中存在歧义的字段,并对所有字段添加详细的文本说明。
测试模型在处理最多3个表格、包含20列左右的场景时,准确率较优。
示例数据如下:
正确示例
CREATE TABLE `orders` ( `Row ID` int(11) DEFAULT NULL COMMENT '行的唯一标识符', `Order ID` varchar(14) DEFAULT NULL COMMENT '订单的唯一标识符', `Order Date` date DEFAULT NULL COMMENT '下单日期', `Ship Date` date DEFAULT NULL COMMENT '发货日期', `Ship Mode` varchar(15) DEFAULT NULL COMMENT '发货方式', `Customer ID` varchar(8) DEFAULT NULL COMMENT '客户的唯一标识符', `Customer Name` varchar(25) DEFAULT NULL COMMENT '客户的名字', `Segment` varchar(15) DEFAULT NULL COMMENT '客户所属的市场细分', `Country` varchar(15) DEFAULT NULL COMMENT '订单发货国家的名称', `City` varchar(17) DEFAULT NULL COMMENT '订单发货城市的名称', `State` varchar(20) DEFAULT NULL COMMENT '订单发货州的名称', `Postal Code` int(11) DEFAULT NULL COMMENT '发货地址的邮政编码', `Region` varchar(10) DEFAULT NULL COMMENT '订单发货的地区', `Product ID` varchar(15) DEFAULT NULL COMMENT '产品的唯一标识符', `Category` varchar(15) DEFAULT NULL COMMENT '产品的高级别类别', `Sub-Category` varchar(11) DEFAULT NULL COMMENT '产品的子类别', `Product Name` varchar(128) DEFAULT NULL COMMENT '产品的名称', `Sales` double DEFAULT NULL COMMENT '订单折扣后的总销售额', `Quantity` int(11) DEFAULT NULL COMMENT '订购的商品数量', `Discount` double DEFAULT NULL COMMENT '订单的折扣', `Profit` double DEFAULT NULL COMMENT '订单产生的利润' ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
错误示例
CREATE TABLE `orders` ( `Row ID` int(11) DEFAULT NULL, `Order ID` varchar(14) DEFAULT NULL, `Order Date` date DEFAULT NULL, `Ship Date` date DEFAULT NULL, `Ship Mode` varchar(15) DEFAULT NULL, `Customer ID` varchar(8) DEFAULT NULL, `Customer Name` varchar(25) DEFAULT NULL, `Segment` varchar(15) DEFAULT NULL, `Country` varchar(15) DEFAULT NULL, `City` varchar(17) DEFAULT NULL, `State` varchar(20) DEFAULT NULL, `Postal Code` int(11) DEFAULT NULL, `Region` varchar(10) DEFAULT NULL, `Product ID` varchar(15) DEFAULT NULL, `Category` varchar(15) DEFAULT NULL, `Sub-Category` varchar(11) DEFAULT NULL, `Product Name` varchar(128) DEFAULT NULL, `Sales` double DEFAULT NULL, `Quantity` int(11) DEFAULT NULL, `Discount` double DEFAULT NULL, `Profit` double DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
训练数据集问题尽量涵盖所有实际使用字段,推荐数据集至少包含2000条数据,当模型微调效果不佳时,您可以考虑增加标注数据量。
问题描述需要尽可能丰富问法和场景。
数据格式要求
训练数据接受JSON格式输入,每条数据包括如下字段:
"id":序号。
"conversations":对话内容。包括"human"和"gpt"字段,分别表示用户的问题和模型输出。
"system":工具列表。包含该数据库场景的DDL。
具体示例如下所示:
[
{
"id": 0,
"conversations": [
{
"from": "human",
"value": "返回“PVLDB”的主页。\n"
},
{
"from": "gpt",
"value": "```sql\nSELECT homepage FROM journal WHERE name = \"PVLDB\"\n```\n"
}
],
"system": "你是一个sql助手,根据用户的问题生成sql。给定以下数据库信息:\n```sql\nCREATE TABLE IF NOT EXISTS author(\naid BIGINT COMMENT '作者ID',\nhomepage STRING COMMENT '作者主页',\nname STRING COMMENT '作者名字',\noid BIGINT COMMENT '组织ID'\n);\nCREATE TABLE IF NOT EXISTS conference(\ncid BIGINT COMMENT '会议ID',\nhomepage STRING COMMENT '会议主页',\nname STRING COMMENT '会议名称'\n);\nCREATE TABLE IF NOT EXISTS domain(\ndid BIGINT COMMENT '领域ID',\nname STRING COMMENT '领域名称'\n);\nCREATE TABLE IF NOT EXISTS domain_author(\naid BIGINT COMMENT '作者ID',\ndid BIGINT COMMENT '领域ID'\n);\nCREATE TABLE IF NOT EXISTS domain_conference(\ncid BIGINT COMMENT '会议ID',\ndid BIGINT COMMENT '领域ID'\n);\nCREATE TABLE IF NOT EXISTS journal(\nhomepage STRING COMMENT '期刊主页',\njid BIGINT COMMENT '期刊ID',\nname STRING COMMENT '期刊名称'\n);\nCREATE TABLE IF NOT EXISTS domain_journal(\ndid BIGINT COMMENT '领域ID',\njid BIGINT COMMENT '期刊ID'\n);\nCREATE TABLE IF NOT EXISTS keyword(\nkeyword STRING COMMENT '关键词',\nkid BIGINT COMMENT '关键词ID'\n);\nCREATE TABLE IF NOT EXISTS domain_keyword(\ndid BIGINT COMMENT '领域ID',\nkid BIGINT COMMENT '关键词ID'\n);\nCREATE TABLE IF NOT EXISTS publication(\nabstract STRING COMMENT '摘要',\ncitation_num BIGINT COMMENT '引用数',\njid BIGINT COMMENT '期刊ID',\npid BIGINT COMMENT '论文ID',\nreference_num BIGINT COMMENT '参考论文数',\ntitle STRING COMMENT '论文标题',\nyear BIGINT COMMENT '年份'\n);\nCREATE TABLE IF NOT EXISTS domain_publication(\ndid BIGINT COMMENT '领域ID',\npid BIGINT COMMENT '论文ID'\n);\nCREATE TABLE IF NOT EXISTS organization(\ncontinent STRING COMMENT '作者所在大陆',\nhomepage STRING COMMENT '组织主页',\noid BIGINT COMMENT '组织ID'\n);\nCREATE TABLE IF NOT EXISTS publication_keyword(\npid BIGINT COMMENT '论文ID',\nkid BIGINT COMMENT '关键词ID'\n);\nCREATE TABLE IF NOT EXISTS writes(\naid BIGINT COMMENT '作者ID',\npid BIGINT COMMENT '论文ID'\n);\nCREATE TABLE IF NOT EXISTS cite(\ncited BIGINT COMMENT '被引用论文ID',\nciting BIGINT COMMENT '引用论文ID'\n);\n```\n"
}
]
批量训练数据生成
具体操作步骤如下:
创建云数据库RDS实例和数据库(本方案以RDS MySQL为例,您也可以使用自己的数据库产品),用于生产SQL的可执行性验证。具体操作,请参见RDS-MySQL使用流程。
配置MySQL数据库,您需要尽量完善当前场景中的表字段说明。您可以在MySQL命令行中,执行如下命令快速导入数据库文件(example_superstore.sql)。如何连接MySQL数据库,请参见通过命令行、客户端连接RDS MySQL实例。
create database superstore; use superstore; source example_superstore.sql;
准备示例问题文件和证据文件。
示例问题文件(few_shot_example.json):包含query和columns两个字段。其中:
query:代表与当前数据库相关的示例问题。
columns:代表与该问题相关的列名。
准备3-5个示例问题,列名个数从易到难在1-3个之间,示例问题文件示例内容如下所示:
[ { "query": "总销售额是多少", "columns": [ "Sales" ] }, { "query": "不同城市的销售额是多少", "columns": [ "City", "Sales" ] }, { "query": "2016年每个月总销售额是多少", "columns": [ "Order Date", "Sales" ] }, { "query": "2016年每个月总销售额是多少,按发货时间算, 并按地区统计", "columns": [ "Profit", "Ship Date", "Region" ] } ]
证据文件(evidence_example.json):包含需要在生成数据prompt中进行描述的列名,其中包含如下三个字段:
table:列名所在的表名。
column:代表列名。
type:代表列名的类型。对于不同的type类型,在数据增广prompt中会产生额外描述。type类型如下表所示:
type
说明
data_column
代表时间类型,指定后会产生该示例描述:这一列代表日期,日期的范围是XXX到XXX。
enum_column
代表需要进行枚举的类型,指定后会产生该示例描述:这一列的值是枚举值,可以使用的枚举值为[XXX, XXX, XXX]。
skip_column
表示与业务无关的列名,指定后不会生成与该列相关的问题。
证据文件示例内容如下所示:
[ { "table": "orders", "type": "date_column", "column": "Order Date" }, { "table": "orders", "type": "date_column", "column": "Ship Date" }, { "table": "orders", "type": "enum_column", "column": "Ship Mode" }, { "table": "orders", "type": "enum_column", "column": "Segment" }, { "table": "orders", "type": "enum_column", "column": "Region" }, { "table": "orders", "type": "enum_column", "column": "Country" }, { "table": "orders", "type": "enum_column", "column": "Sub-Category" }, { "table": "orders", "type": "enum_column", "column": "Category" }, { "table": "orders", "type": "skip_column", "column": "Row ID" } ]
将示例问题文件和证据文件上传到已创建的OSS Bucket存储空间,详情请参见步骤三:上传文件。文件上传的示例路径如下:
示例问题文件:oss://example-bucket/simplebi/few_shot_example.json
证据文件:oss://example-bucket/simplebi/evidence_example.json
使用PAI-DLC批量生产数据。
创建分布式训练(DLC)任务,并配置以下关键参数,其他参数配置说明,请参见创建训练任务。
参数
描述
基本信息
任务名称
自定义配置任务名称。
环境信息
节点镜像
单击镜像地址,并在文本框中输入镜像
dsw-registry-vpc.<region>.cr.aliyuncs.com/pai-training-algorithm/llm_deepspeed_llamafactory:v0.0.3
。其中<region>需要根据实际地域进行调整,例如华北6(乌兰察布)配置为cn-wulanchabu。挂载配置
单击添加,将示例问题文件和证据文件所在目录挂载到训练容器中。挂载类型选择对象存储(OSS),并配置以下参数:
OSS:选择示例问题文件和证据文件所在的OSS路径。例如oss://example-bucket/。
挂载路径:配置为/mnt/data。
启动命令
在代码编辑框中配置如下启动命令:
python -m simplebi.data_augmentation.data_generate \ --generate_number 10000 \ --processes_number 40 \ --fewshot_query_filepath /mnt/data/simplebi/few_shot_example.json \ --evidence_filepath /mnt/data/simplebi/evidence_example.json \ --output_filepath /mnt/data/simplebi/qwen2_72b_1000.json \ --host rm-******o.mysql.rds.aliyuncs.com \ --port 3306 \ --user Simple**** \ --database supers**** \ --passwd SimpleB**** \ --dtype bfloat16 \ --max_model_len 8192 \ --tensor_parallel_size 8 \ --query_temperature 0.8 \ --sql_temperature 0.2 \ --max_tokens 1024
参数配置说明,请参见启动参数说明。
资源信息
资源类型
选择灵骏智算。
资源来源
选择竞价资源。
任务资源
单击选择资源规格:ml.gu7xf.8xlarge-gu108或ml.gu8xf.8xlarge-gu108,并配置最高出价。
专有网络配置
专有网络配置(ID)
选择已配置公网访问的VPC网络。如何配置公网访问,请参见NAT网关。
交换机
安全组
选择安全组。
训练模型
快速开始汇集了优秀的国内外AI开源社区预训练模型。您可以在快速开始>Model Gallery中,实现从训练到部署再至推理的完整流程,无需编写代码,极大简化了模型的开发过程。
本方案以CodeQwen1.5-7b-NL2SQL模型为例,为您介绍如何使用已准备好的训练数据,在快速开始中进行模型训练。具体操作步骤如下:
进入Model Gallery页面。
登录PAI控制台。
在顶部左上角根据实际情况选择地域。
在左侧导航栏选择工作空间列表,单击指定工作空间名称,进入对应工作空间内。
在左侧导航栏选择
。
在Model Gallery页面右侧的模型列表中,搜索并单击CodeQwen1.5-7b-NL2SQL模型卡片,进入模型详情页面。
在模型详情页面,单击右上角的微调训练,并在微调训练配置面板中,配置以下关键参数。
参数
描述
训练方式
支持以下两种训练方式,具体说明如下:
QLoRA:QLoRA训练属于高效训练的一种,会在固定并量化模型本身参数的基础上,仅对自注意力权重矩阵进行低秩分解,并更新低秩矩阵参数。该训练方法训练时间短,但效果可能会略差于LoRA。
LoRA:LoRA训练属于高效训练的一种,会在固定模型本身参数的基础上,仅对自注意力权重矩阵进行低秩分解,并更新低秩矩阵参数。
数据集配置
训练数据集
参照以下操作步骤,选择已准备好的训练数据集。
在下拉列表中选择OSS文件或目录。
单击按钮,选择已创建的OSS目录。
在选择OSS文件对话框中,单击上传文件,拖拽上传已准备好的训练数据集文件(示例文件:qwen2_72b_10000_llamafactory.json),并单击确定。
说明如果出现“数据解析失败,请检查文件内容格式”字样,请忽略,不影响您执行后续操作。
训练输出配置
模型名称
注册到AI资产管理>模型中的模型名称。按照控制台界面提示自定义配置。
模型输出路径
选择OSS目录,用来存放训练输出的配置文件。
计算资源配置
资源组类型
选择公共资源组(按量付费)。
任务资源
系统已默认配置了资源规格,您也可以根据需要进行修改:
当训练方式选择QLoRA时:最低使用V100/P100/T4(16 GB显存)及以上卡型。
当训练方式选择LoRA时:最低使用V100(32 GB显存)/A10及以上卡型。
超参数配置
训练算法支持的超参信息,请参见表1.全量超参数说明。针对不同的训练方式,关键超参数推荐配置,请参见表2.超参数推荐配置。
参数配置完成后,单击训练,然后在计费提醒对话框中,单击确定。
系统自动跳转到训练任务详情页面,训练任务启动成功,您可以在该页面查看训练任务状态和任务日志。
部署及调用模型服务
部署模型服务
模型训练成功后,即可部署模型服务。PAI预置了模型的部署配置信息,您仅需提供推理服务的名称以及部署配置使用的资源信息,即可将训练获得的模型部署到模型在线服务(EAS)平台。具体操作步骤如下:
在任务详情页面,单击右上角的部署。
在部署配置面板中,系统已默认配置了模型服务信息和资源部署信息,您也可以根据需要进行修改,参数配置完成后,单击部署。
在计费提醒对话框中,单击确定。
系统将自动跳转到部署任务页面,当状态为运行中时,表示服务部署成功。
调用模型服务
当模型服务部署成功后,您可以按照以下操作步骤调用模型服务:
查询服务访问地址和Token。
在部署任务页面,单击服务名称,进入服务详情页面。
在基本信息区域,单击查看调用信息。
在调用信息对话框的公网地址调用页签,查看访问地址和Token。
本方案是API方式的模型服务部署,您可以在终端中执行如下Python代码,使用OpenAPI的SDK调用模型服务。
from openai import OpenAI client = OpenAI( base_url="<eas_service_path>/v1", api_key="<eas_service_token>", ) chat_completion = client.chat.completions.create( model="/model_dir", messages=[ {"role": "system", "content": "你是一个sql助手,根据用户的问题生成sql,参考如下ddl:\nCREATE TABLE `orders` (\n `Row ID` int DEFAULT NULL COMMENT '行的唯一标识符',\n `Order ID` varchar(14) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '订单的唯一标识符',\n `Order Date` date DEFAULT NULL COMMENT '下单日期',\n `Ship Date` date DEFAULT NULL COMMENT '发货日期',\n `Ship Mode` varchar(15) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '发货方式',\n `Customer ID` varchar(8) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户的唯一标识符',\n `Customer Name` varchar(25) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户的名字',\n `Segment` varchar(15) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户所属的市场细分',\n `Country` varchar(15) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户所在国家的名称',\n `City` varchar(17) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户所在城市的名称',\n `State` varchar(20) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户所在州的名称',\n `Postal Code` int DEFAULT NULL COMMENT '客户地址的邮政编码',\n `Region` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '客户所在的地区',\n `Product ID` varchar(15) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '产品的唯一标识符',\n `Category` varchar(15) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '产品的高级别类别',\n `Sub-Category` varchar(11) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '产品的子类别',\n `Product Name` varchar(128) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '产品的名称',\n `Sales` double DEFAULT NULL COMMENT '订单折扣后的总销售额',\n `Quantity` int DEFAULT NULL COMMENT '订购的商品数量',\n `Discount` double DEFAULT NULL COMMENT '订单的折扣',\n `Profit` double DEFAULT NULL COMMENT '订单产生的利润'\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\nCREATE TABLE `people` (\n `Person` varchar(20) DEFAULT NULL,\n `Region` varchar(10) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\nCREATE TABLE `returns` (\n `Returned` varchar(3) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '订单是否被退单',\n `Order ID` varchar(15) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL COMMENT '订单的唯一标识符'\n) ENGINE=InnoDB DEFAULT CHARSET=latin1,回答sql查询问题"}, {"role": "user", "content": "在2016年,每个月各类产品的订单数量是多少?"} ], ) print(chat_completion.choices)
其中:
<eas_service_path>:替换为上述步骤已查询的服务访问地址。
<eas_service_token>:替换为上述步骤已查询的服务Token。
调用成功后,返回结果示例如下,您的结果以实际为准:
[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content="在2016年,我们可以通过以下SQL查询获取每个月各类产品的订单数量:\n\n```sql\nSELECT \n DATE_FORMAT(`Order Date`, '%Y-%m') AS YearMonth,\n `Category`,\n COUNT(*) AS OrderCount\nFROM \n orders\nWHERE \n YEAR(`Order Date`) = 2016\nGROUP BY \n YearMonth,\n `Category`;\n```\n\n这个查询首先通过DATE_FORMAT函数将订单日期格式化为年月(YYYY-MM),然后按照年月和类别进行分组,最后计算每个月各类产品的订单数量。", role='assistant', function_call=None, tool_calls=None), stop_reason=None)]
服务调用成功后,您可在终端中执行如下Python代码,基于RDS-MySQL验证模型生成SQL。
import re import pymysql def parse(text): pattern = r"```sql\n(.*?)\n```" matches = re.findall(pattern, text, re.DOTALL) if matches: sql_content = matches[0].strip() if not sql_content.endswith(";"): sql_content += ";" return sql_content else: if not text.endswith(";"): text += ";" return text def excute_sql(input_text: str) -> tuple: sql = parse(input_text) cursor = db.cursor() try: cursor.execute(sql) col = cursor.description sql_data = cursor.fetchall() return sql_data except Exception as e: return "error" db = pymysql.connect( host="数据库连接", user="数据库用户名", passwd="数据库密码", port=3306, db="数据库名称", ) print( excute_sql( "填写模型输出文本" ) )
其中关键配置说明如下:
关键配置
描述
host
配置为RDS数据库的公网地址。
user
配置为RDS数据库的连接用户名。
passwd
配置为RDS数据库的连接密码。
port
配置为RDS数据库的连接端口。
db
配置为RDS数据库的连接数据库名称。
excute_sql
将文本内容配置为步骤2调用服务返回结果中的content字段内容。