数据科学

借助 NVIDIA NIM 加速 Vanna 上的文本到 SQL 推理,从而提升分析效率

从自然语言输入生成查询缓慢且效率低下,是决策制定的瓶颈。这迫使分析师和业务用户严重依赖数据团队,从而延迟洞察并限制敏捷性。

Text-to-SQL 正在改变我们与数据交互的方式,使用户能够以自然语言查询结构化数据库。在为专业行业部署特定领域的模型时,这尤其有用。然而,大规模使用这些模型进行分析工作负载推理会带来延迟和性能挑战。

在本教程中,我们将展示如何使用 NVIDIA NIM 优化 Vanna 的文本转 SQL 解决方案。Vanna 的开源文本转 SQL 解决方案因其灵活性、安全性和适应性而在组织中备受关注。

您会学习:

  • 将预处理数据插入数据库并连接 Vanna。
  • 对 Vanna 进行业务术语或定义培训。
  • 利用 NVIDIA NeMo Retriever 嵌入模型检索上下文以生成 SQL。
  • 使用适用于 LLM 的 NVIDIA NIM 端点加速推理。

在演示中,我们使用了 Kaggle 中的两个开源 Steam 游戏数据集,这是一个丰富的游戏元数据集,非常适合分析查询。

预备知识

程序

1. 下载并预处理 Steam 数据集

为此,请克隆位于/ NVIDIA/ GenerativeAIExamples 的存储库,并在社区部分打开 Vanna_with_NVIDIA notebook。

关键预处理步骤包括:

  • 过滤所需列
  • 将具有范围的列解压缩为单独的列
  • 为每款游戏添加游戏长度

预处理步骤结束时,我们会得到三个 CSV:

  • tableau_games.csv
  • tableau_categories.csv
  • tableau_tags.csv

2. 使用 NVIDIA NIM 和 NeMo Retriever 初始化 Vanna

Vanna 实例需要连接到向量数据库、嵌入器和 LLM 端点。我们使用 Milvus 向量数据库,因为它具有 GPU 加速功能、NVIDIA 检索器嵌入模型 (llama-3.2-nv-embedqa-1b-v2) 和 Llama 3.1 70B NIM 微服务

借助 NIM 微服务,您的推理模型可在 NVIDIA 加速基础设施上运行,从而为生产部署提供更快的响应时间和成本效益。NVIDIA NIM 包含在 NVIDIA AI Enterprise 软件许可证中,可在任何地方运行,让您拥有自定义设置的所有权,并完全控制您的知识产权 (IP) 和 AI 应用。

有关 NIM 端点的设置说明,请参阅 NVIDIA NIM 文档

A diagram showing the NVIDIA-accelerated Vanna text-to-SQL pipeline, including input question, embedding generation, vectorDB retrieval, and SQL generation using GPU-accelerated components.
图 1。NVIDIA 加速的 Vanna 工作流

在以下文本中,我们将介绍如何使用 NVIDIA 加速组件初始化 Vanna 实例。

  • 将 Vanna 类定义如下。
from pymilvus import MilvusClient, model
from vanna.milvus import Milvus_VectorStore
from vanna.openai import OpenAI_Chat
from openai import OpenAI

# Set the nvidia api key 
nvidia_api_key = '...'

# Define Vanna Class
class VannaMilvus(Milvus_VectorStore, OpenAI_Chat):
    def __init__(self, llm_client, config=None):
        Milvus_VectorStore.__init__(self, config=config)
        OpenAI_Chat.__init__(self, client=llm_client, config=config)
  • 接下来,使用 NVIDIA API 密钥创建基于 OpenAI Wrapper 的 NIM 客户端。
def get_openai_client():
    client = OpenAI(
        base_url = "https://integrate.api.nvidia.com/v1",
        api_key = nvidia_api_key
    )
    return client

llm_client = get_openai_client()
  • 通过 Milvus DB 为 NeMo Retriever 嵌入模型使用包装器。您可以使用相同的 nvidia_api_key
class EmbeddingWrapper:
    def __init__(self, embedder):
        self.embedder = embedder

    def encode_documents(self, texts):
        result = self.embedder.embed_documents(texts)
        return [np.array(r) for r in result]

    def encode_queries(self, texts):
        embeddings = []
        for text in texts:
            embeddings.append(self.embedder.embed_query(text))
        return embeddings

vanna_embedder = EmbeddingWrapper(nvidia_embedder)

  • 接下来,使用 NVIDIA API 密钥创建基于 OpenAI Wrapper 的 NIM 客户端。
def get_openai_client():
    client = OpenAI(
        base_url = "https://integrate.api.nvidia.com/v1",
        api_key = nvidia_api_key
    )
    return client

llm_client = get_openai_client()
  • 使用本地 Milvus DB 实例。
milvus_uri = "./milvus_nvidia.db"
milvus_client_nvidia = MilvusClient(uri=milvus_uri)
  • 使用之前定义的组件指定 Vanna 实例的配置。
# Specify the LLM used for SQL generation
model_name = "nvidia/llama-3.1-70b-instruct"

# Define the configuration for Vanna instance
config_nvidia = {
        "model": model_name,
        "milvus_client": milvus_client_nvidia,
        "embedding_function": vanna_embedder,
        "n_results": 2,  # The number of results to return from Milvus semantic search.
    }

vn_nvidia = VannaMilvus(llm_client, config=config_nvidia)

3. 创建并填充 SQLite 数据库

  • 在此演示中使用 SQLite 提供了一个轻量级、无服务器的数据库,用于测试文本转 SQL。您可以使用 Vanna 支持的任何数据库。将预处理步骤中的三个 CSV 插入数据库。
import sqlite3
import pandas as pd

# Specify the path to the SQLite database
sqlite_path = 'steam_data.db'

# Connect to the SQLite database
sql_connect = sqlite3.connect(sqlite_path)
c = sql_connect.cursor()

# Create tables
init_sqls = """
CREATE TABLE IF NOT EXISTS games (
    app_id INTEGER PRIMARY KEY,
    name TEXT,
    release_date TEXT,
    price REAL,
    short_description TEXT,
    positive INTEGER,
    negative INTEGER,
    min_owners INTEGER,
    max_owners INTEGER,
    hltb_single REAL
);

CREATE TABLE IF NOT EXISTS categories (
    app_id INTEGER,
    categories TEXT,
    FOREIGN KEY (app_id) REFERENCES games(app_id)
);

CREATE TABLE IF NOT EXISTS tags (
    app_id INTEGER,
    tags TEXT,
    tag_frequencies TEXT,
    FOREIGN KEY (app_id) REFERENCES games(app_id)
);
"""

for sql in init_sqls.split(";"):
    c.execute(sql)

# Read the CSV files
games_df = pd.read_csv('processed_dataset/games.csv')
categories_df = pd.read_csv('processed_dataset/categories.csv')
tags_df = pd.read_csv('processed_dataset/tags.csv')

# Insert data into tables
games_df.to_sql('games', sql_connect, if_exists='append', index=False)
categories_df.to_sql('categories', sql_connect, if_exists='append', index=False)
tags_df.to_sql('tags', sql_connect, if_exists='append', index=False)

sql_connect.commit()
  • 现在,将 SQL DB 连接到 Vanna。
# Connect to the SQLite database
vn_nvidia.connect_to_sqlite(sqlite_path)

4. 训练数据

Vanna 在处理查询数据和业务特定术语的上下文时表现最佳。

# Remove existing training data
existing_training_data = vn_nvidia.get_training_data()
if len(existing_training_data) > 0:
    for _, training_data in existing_training_data.iterrows():
        vn_nvidia.remove_training_data(training_data["id"])

# Get the DDL of the SQLite database
df_ddl = vn_nvidia.run_sql("SELECT type, sql FROM sqlite_master WHERE sql is not null")

# Train the model on the DDL data
for ddl in df_ddl["sql"].to_list():
    vn_nvidia.train(ddl=ddl)
  • 接下来,添加有关数据库模式 games、tg_ 15 和 tg_ 16 中三个表的文档。
# Add documentation about your business terminology or definitions.
vn_nvidia.train(
    documentation="""
    This dataset is used to answer questions about the game trends.
    """
)

# Add documentation about the tables
vn_nvidia.train(
    documentation="""
    The games table contains information about the games.
    The app_id is the unique identifier for the game.This is a primary key.
    The name is the name of the game.
    The release_date is the date the game was released.
    The price is the price of the game. Price in USD, 0.0 if its free.
    The short_description is a brief description of the game.
    The positive is the number of positive reviews or votes.
    The negative is the number of negative reviews or votes.
    The min_owners is the minimum number of owners. Used together with max_owners to get an estimate of the player base.
    The max_owners is the maximum number of owners. Used together with min_owners to get an estimate of the player base.
    The hltb_single is the average playtime of the game. This is an estimate.
    """
)

vn_nvidia.train(
    documentation="""
    The categories table contains information about the categories of the games.
    The app_id is the unique identifier for the game.
    The categories is the categories of the game.
    The app_id is a foreign key to the games table.
    """
)

vn_nvidia.train(
    documentation="""
    The tags table contains information about the tags of the games.
    The app_id is the unique identifier for the game.
    The tags is the tags of the game. These are user defined.
    The tag_frequencies is the frequencies of the tags.
    The app_id is a foreign key to the games table.
    """
)
  • 然后,使用简单的命令查看训练数据。
training_data = vn_nvidia.get_training_data()
training_data

5. 生成 SQL

您已准备好开始使用由 NIM 提供支持的 Vanna 查询数据库。

  • 从简单查询开始。生成的 SQL 也可以在执行之前打印出来。
sql = vn_nvidia.generate_sql("Which 5 games have the most positive reviews and how many?")
vn_nvidia.run_sql(sql)
  • Run a harder query now.
sql = vn_nvidia.generate_sql("Which indie game has the biggest player base?")
vn_nvidia.run_sql(sql)

总结

在本教程中,您学习了如何使用 NVIDIA NIM 加速 Vanna 上的文本转 SQL 推理。凭借清晰的数据库架构、真实数据集和优化的推理端点,该系统能够为用户生成的查询提供响应速度更快的分析。

进一步探索:

  • 以此 Notebook 为起点,在您自己的数据集上试用 Vanna
  • 部署 NVIDIA NIM 端点进行生产级推理。

尝试添加不同的训练信息,以更好地生成 SQL。

 

标签