ChatBI は、自然言語 SQL (NL2SQL) 技術を使用して、自然言語でデータクエリを実行し、企業のレポート生成を支援します。このトピックでは、「Alixiang」 レストラン管理システムを例に、ChatBI の主な機能を順を追って説明し、お客様がサービスを迅速かつ効率的に利用開始できるよう支援します。
PolarDB for AI 機能の有効化
AI ノードを追加し、AI ノードに接続するためのデータベースアカウントを設定します。詳細については、「PolarDB for AI 機能の有効化」をご参照ください。
説明クラスターの購入時にすでに AI ノードを追加している場合は、AI ノード用のデータベースアカウントを直接設定できます。詳細については、「標準アカウントの作成」をご参照ください。
このアカウントには、対象のデータテーブルに対する読み取りおよび書き込み権限が必要です。これにより、ChatBI 変換プロセスですべてのデータベース操作を実行できます。
[Cluster Endpoint]を使用して、PolarDB クラスターに接続します。詳細については、「PolarDB for AIへのログイン」をご参照ください。
説明コマンドラインからクラスターに接続する場合は、
-cオプションを追加してください。DMS はデフォルトで[プライマリアドレス]を使用してクラスターに接続します。手動で[Cluster Endpoint]に変更する必要があります。変更後、元の SQL ウィンドウを閉じて新しいウィンドウを開き、SQL 文を実行します。
データ準備
「Alixiang」は架空のレストラン企業です。この請求管理システムには、次の 3 つのテーブルが含まれています。クリックしてダウンロードできます。
テーブルスキーマに基づいて、テーブルと列にコメントを入力できます。これにより、大規模言語モデル (LLM) はデータをより正確に認識、理解できるようになり、データ処理とデータ分析におけるモデルの精度と効率性が向上します。
CREATE TABLE restaurant_info (
id INT COMMENT '店舗 ID',
position VARCHAR(128) COMMENT '店舗の所在地',
PRIMARY KEY (id)
) COMMENT='店舗テーブル';
CREATE TABLE menu_info (
id INT COMMENT 'メニュー項目 ID',
name VARCHAR(64) COMMENT 'メニュー項目名',
type INT COMMENT 'メニュー項目の種類',
unit_price INT COMMENT '単価',
PRIMARY KEY (id)
) COMMENT='メニューテーブル';
CREATE TABLE bill_info (
id INT COMMENT '請求書 ID',
items VARCHAR(512) COMMENT '注文品',
actual_amount INT COMMENT '実支払額',
restaurant_id INT COMMENT '店舗 ID',
waiter VARCHAR(16) COMMENT 'ウェイター',
diner_count INT COMMENT '食事客数',
pay_time DATE COMMENT '注文時間',
PRIMARY KEY (id)
) COMMENT='請求書テーブル';ChatBI の使用
次に、PolarDB for AI のNL2SQL モデルを使用して、ユーザーの質問に対応する SQL ステートメントを生成します。
テーブルスキーマインデックスの作成
次の SQL ステートメントを使用して、schema_index という名前のテーブルスキーマインデックスを作成し、テーブルスキーマ情報を大規模言語モデル (LLM) に提供できます。
/*polar4ai*/CREATE TABLE schema_index(id integer, table_name varchar, table_comment text_ik_max_word, table_ddl text_ik_max_word, column_names text_ik_max_word, column_comments text_ik_max_word, sample_values text_ik_max_word, vecs vector_768,ext text_ik_max_word, PRIMARY KEY (id));このテーブルはデータベースで直接表示されません。次の SQL ステートメントを実行して情報を表示できます。
/*polar4ai*/SHOW TABLES;次に、次の SQL ステートメントを使用して、データテーブルスキーマをインデックステーブル schema_index にインポートできます。
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_text2vec, SELECT '') WITH (mode='async', resource='schema') INTO schema_index;ステートメントを実行すると、PolarDB for AI は現在のデータベース内のすべてのテーブルをベクトル化し、デフォルトで列値をサンプリングします。
ステートメントを実行すると、システムはバックグラウンドタスクの task_id (例:bce632ea-97e9-11ee-bdd2-492f4dfe0918) を返します。次の SQL を使用して現在のタスクのステータスを照会できます。返された taskStatus が finish になると、インデックス構築は完了です。
/*polar4ai*/SHOW TASK `bce632ea-97e9-11ee-bdd2-492f4dfe0918`;NL2SQL モデルを使用した質問への回答
次の SQL ステートメントを実行して、LLM ベースの NL2SQL をオンラインで使用します。次の例では、ユーザーのクエリは「今週の総収益はいくらですか?」で、使用するテーブルスキーマインデックスは schema_index です。
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, SELECT 'What is the total revenue for this week') WITH (basic_index_name='schema_index');データベースは、LLM からの応答を受信するまでしばらく待つ必要があります。期待される結果は次のとおりです。
返される結果は SELECT SUM(actural_amount) AS total_income FROM bill_info WHERE pay_time >= '2024-10-14' AND pay_time <= '2024-10-20'; です。
上記の例に基づいて、いくつかの典型的な質問をすることもできます。これらの質問は、GROUP BY、複数テーブルの JOIN、ORDER BY、数式など、さまざまなシナリオをカバーしています。
番号 | ユーザーの質問 | NL2SQL の戻り値 |
1 | 店舗を収益順に並べ替える |
|
2 | 上海で最も収益の高い店舗はどれですか? |
|
3 | 上海の一人当たりの平均消費額はいくらですか? |
|
4 | 今月最も注文されたメニュー項目のトップ 10 は何ですか? |
|
5 | 今月の収益の前月比成長率は何パーセントですか? |
|
6 | 上海で最も人流が多い店舗はどれですか? |
|
ご覧のとおり、LLM ベースの NL2SQL モデルはユーザーの質問に効果的に回答できますが、一部の応答は期待どおりではありません。たとえば、2 番目の質問では、ユーザーは店舗名を返してほしいと考えています。質問を「上海で最も収益の高い店舗はどれですか? 店舗名を返してください」と言い換えると、モデルは次の SQL ステートメントを返します:SELECT r.name FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position = 'Shanghai' GROUP BY r.name ORDER BY SUM(b.actural_amount) DESC LIMIT 1;。モデルをファインチューニングすることで精度を向上させることもできます。以降のセクションでは、これらの問題について説明します。
モデルのファインチューニング
質問テンプレートの設定
一般的な質問テンプレートを使用して、特定の知識を導入することでモデルをガイドできます。これにより、モデルはその知識に基づいて SQL ステートメントを生成できるようになります。
次の SQL を実行して、質問テンプレートテーブル
polar4ai_nl2sql_patternを作成します。CREATE TABLE `polar4ai_nl2sql_pattern` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'プライマリキー', `pattern_question` text COMMENT 'テンプレート質問', `pattern_description` text COMMENT 'テンプレートの説明', `pattern_sql` text COMMENT 'テンプレート SQL', `pattern_params` text COMMENT 'テンプレートパラメーター', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;テーブル名は
polar4ai_nl2sql_patternで始まる必要があり、テーブルスキーマには上記のCREATE TABLEステートメントの 5 つの列を含める必要があります。次に、質問テンプレート用のインデックステーブル
pattern_indexを作成します。/*polar4ai*/CREATE TABLE pattern_index(id integer, pattern_question text_ik_max_word, pattern_description text_ik_max_word, pattern_sql text_ik_max_word, pattern_params text_ik_max_word, pattern_tables text_ik_max_word, vecs vector_768, PRIMARY KEY (id));2 番目の質問に対してテンプレートを設定します。これは、ファインチューニングで店舗の住所を返すために使用されます。
次の SQL ステートメントを実行して、新しいパターンを追加します。
INSERT INTO polar4ai_nl2sql_pattern (id, pattern_question, pattern_description, pattern_sql, pattern_params) VALUES ( 1, "Which outlet in #{position} has the highest revenue?", "Which outlet in [location] has the highest revenue?", "SELECT r.position FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position LIKE '%#{position}%' GROUP BY r.position ORDER BY SUM(b.actural_amount) DESC LIMIT 1;", '[{"table_name":"bill_info","param_info":[{"param_name":"#{position}","value":["Shanghai"]}], "explanation": "消費地"}]' );このパターンでは、スロットを使用して複数の場所を照合します。
pattern_sql列に正しい SQL ステートメントを入力し、スロットを#{}でマークします。pattern_params列はテーブル情報の後処理に使用されますが、ここでは無視できます。次に、質問テンプレート情報をインデックステーブルにインポートします。
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_text2vec, SELECT '') WITH (mode='async', resource='pattern') INTO pattern_index;schema_indexのインデックス構築プロセスと同様に、タスク ID も返されます。/*polar4ai*/SHOW TASK 'xxx-xxx-xxx'を実行して、現在のタスクのステータスを表示できます。説明polar4ai_nl2sql_patternテーブル内のデータが更新された場合は、pattern_indexを再作成し、データを再度インポートする必要があります。次の SQL ステートメントを使用して、古いインデックスを削除できます。/*polar4ai*/DROP TABLE pattern_index;問題の原因となった SQL ステートメントを再実行し、
pattern_indexヒントを追加します。/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, SELECT 'Which outlet in Shanghai has the highest revenue?') WITH (basic_index_name='schema_index',pattern_index_name='pattern_index');実行結果は 1 行のデータを返します。result 列の値は
SELECT r.position FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position LIKE '%上海%' GROUP BY r.position ORDER BY SUM(b.actural_amount) DESC LIMIT 1;です。
設定テーブルの構築
質問を前処理したり、最終的に生成された SQL を後処理したりする場合は、設定テーブルを使用できます。
語彙意味ヒント
6 番目の質問では、大規模言語モデル (LLM) が「人流」という用語を正確に理解できないため、polar4ai_nl2sql_llm_config テーブルを設定して前処理を実行できます。
CREATE TABLE `polar4ai_nl2sql_llm_config` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'プライマリキー',
`is_functional` int(11) NOT NULL DEFAULT '1' COMMENT '有効かどうか',
`text_condition` text COMMENT 'テキスト条件',
`query_function` text COMMENT 'クエリ処理',
`formula_function` text COMMENT '数式情報',
`sql_condition` text COMMENT 'SQL 条件',
`sql_function` text COMMENT 'SQL 処理',
PRIMARY KEY (`id`)
);関連する設定項目を挿入して、「人流」または「顧客フロー」を「食事客数」としてカウントするように LLM を設定します。
INSERT INTO polar4ai_nl2sql_llm_config (id, is_functional, text_condition, query_function, formula_function, sql_condition, sql_function) VALUES (
1,
1,
"customer traffic||customer flow",
"",
"人流または顧客フローは、食事客数の合計として計算されます",
"",
""
);この場合、is_functional の値が 1 のときは、その設定項目が有効であることを示します。text_condition フィールドの値は「customer traffic||customer flow」で、「customer traffic」または「customer flow」を含む質問と一致します。formula_function フィールドは、テキストまたは数式を使用して、専門用語を大規模言語モデル (LLM) に説明します。
この場合、インデックステーブルの構築やベクトル化を行わずに、直接 SQL 生成を実行できます。結果は次のとおりです。
NL2SQL 予測コマンドを実行します:/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, SELECT '上海で最も人流が多い店舗はどれですか') WITH (basic_index_name='schema_index',pattern_index_name='pattern_index');。生成された SQL は次のとおりです:SELECT r.position, SUM(b.diner_count) AS total_customer_flow FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id WHERE r.position LIKE '%上海%' GROUP BY r.id ORDER BY total_customer_flow DESC LIMIT 1;。モデルは「人流」を食事客数 (diner_count) として正しく解釈し、統計を行いました。
あいまい検索のヒント
質問 3 では、= 演算子を使用して地名を取得すると、名前が完全一致でない場合に失敗します。したがって、地名の照合にはあいまい検索を使用する必要があります。次の設定項目を追加できます。
INSERT INTO polar4ai_nl2sql_llm_config (id, is_functional, text_condition, query_function, formula_function, sql_condition, sql_function) VALUES (
2,
1,
"",
"",
"店舗の場所 'position' の照合には、あいまい検索が必要です",
"",
""
);text_condition が空の場合、設定項目はグローバルに適用されます (注意して使用してください)。
結果は次のとおりです。ご覧のとおり、場所の照合はあいまい検索を正常に使用しています。
生成された SQL は SELECT AVG(bi.actural_amount / bi.diner_count) AS avg_per_person FROM bill_info bi JOIN restaurant_info ri ON bi.restaurant_id = ri.id WHERE ri.position LIKE '%上海%'; です。場所の照合条件は、あいまい検索のために LIKE '%上海%' を使用します。
同様に、質問 5 についても、前月比と前年比の計算式を polar4ai_nl2sql_llm_config 設定テーブルに追加することで、生成される SQL の精度を向上させることができます。ご自身でお試しください。
チャート出力
NL2SQL で SQL ステートメントを生成した後、クエリ結果を取得し、縦棒グラフ、折れ線グラフ、円グラフなどのチャートで視覚的に表示できます。PolarDB の NL2Chart ソリューションは、質問に基づいて SQL ステートメントを実行し、対応するレポートを返すことができます。縦棒グラフ、円グラフ、折れ線グラフをサポートしています。
NL2SQL でのステートメントが次のようであると仮定します。
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, SELECT 'Merchant type statistics') WITH (basic_index_name='schema_index',pattern_index_name='pattern_index');対応する SQL ステートメントが生成された後、それが実行され、意味のある空でない結果が返されることを確認します。
SELECT merchtype AS merchant_type, COUNT(*) AS product_count FROM hkrt_merchant_info GROUP BY merchtype;NL2Chart の使用:
構文
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart, <SQL_statement>) WITH (usr_query = <usr_query>, result_type = <result_type>);パラメーター
パラメーター名
説明
サンプル値
usr_query
ユーザーが入力した質問。チャート生成の要件を明確にするために使用されます。
「2023年の各四半期の売上統計」
result_type
返される結果のタイプを指定します。現在、
'IMAGE'のみがサポートされています。'IMAGE'SQL ステートメント
NL2SQL モジュールによって生成された SQL クエリステートメント。データの取得に使用されます。
SELECT quarter, sales FROM sales_data WHERE year = 2023例:生成された SQL ステートメントのクエリ結果をチャートに変換する
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart, SELECT merchtype AS merchant_type, COUNT(*) AS number_of_merchants FROM hkrt_merchant_info GROUP BY merchtype) WITH (usr_query = 'Merchant type statistics', result_type='IMAGE');結果は次のとおりです。
説明返されるリンクは画像 URL で、90 分間有効です。
http://db4ai-xxx-xx-xxxx-xxx-xxxx.aliyuncs.com/pc-bpze47ma2c515087l6/OSSAccessKeyId=xxxxxxx&Expires=1716130199&Signature=KvPFzfMebIEmqxPIXURurwwbsXM%3D
(オプション) チャートタイプの選択と指定
モデルは、ユーザーの質問とデータに対する理解に基づいて、適切なチャートを選択します。ユーザーの質問を使用してモデルがチャートを生成するようにガイドすることを推奨します。
次の表は、質問タイプとチャートタイプのマッピングを示しています。
質問タイプ
チャートタイプ
ユーザーの質問の例
説明
数量統計
縦棒グラフ
「都市別の売上統計を提供してください」
数量、総額、頻度など、異なるカテゴリ間の数値比較を示します。
トレンド変化
折れ線グラフ
「過去1年間のユーザー成長トレンドを表示してください」
時間や順序付けられたカテゴリにわたるデータのトレンドを示し、連続性を強調します。
比率分布
円グラフ
「各製品ラインの売上比率を表示してください」
全体に対する部分の比率関係を示すのに適しています。データはカテゴリカルであり、明確な合計がある必要があります。
usr_queryパラメーターを変更することで、特定のチャートタイプを指定します。usr_queryパラメーターの末尾に補助コマンドを追加します。-- 出力 SQL を nl2chart に入力して折れ線グラフを描画します /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart, SELECT merchtype AS merchant_type, COUNT(*) AS number_of_merchants FROM hkrt_merchant_info GROUP BY merchtype ) WITH (usr_query = 'Merchant type statistics, draw a line chart', result_type='IMAGE');
-- 出力 SQL を nl2chart に入力して円グラフを描画します /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart, SELECT merchtype AS merchant_type, COUNT(*) AS number_of_merchants FROM hkrt_merchant_info GROUP BY merchtype ) WITH (usr_query = 'Merchant type statistics, draw a pie chart', result_type='IMAGE');
詳細については、「NL2Chart:自然言語からスマートチャートを生成する」をご参照ください。
モデルの再トレーニングとファインチューニング
モデルがビジネス要件を満たさない場合は、モデルを再トレーニングし、内部パラメーターをファインチューニングすることで、より良い結果を得られます。
前提条件
この機能は、polar.mysql.x8.2xlarge.gpu 仕様 (16 コア、125 GB、GU100 × 1) の AI ノードを持つクラスターでのみ利用できます。
一度にトレーニングできるモデルは 1 つのみです。
一度にデプロイできるモデルは 1 つのみです。
手順
モデルのトレーニング
/*polar4ai*/CREATE MODEL udf_qwen_14b_chat WITH (model_class='qwen-14b-chat', model_parameter=(basic_index_name='schema_index', pattern_index_name='pattern_index',training_type='efficient_sft')) as (SELECT '')パラメーター
パラメーター名 | 説明 | デフォルト | 有効値/範囲 |
model_class | モデルのタイプです。現在は {'qwen-14b-chat', 'qwen-turbo'} をサポートしています。 | なし | {'qwen-14b-chat', 'qwen-turbo'} |
model_parameter | 必須パラメーターとオプションパラメーターを含むモデルのパラメーター設定です。 | なし | なし |
basic_index_name | トレーニングデータ内のデータベース情報の取得元となるインデックステーブル名です。データベースのインデックステーブルである必要があります。 | なし | なし |
pattern_index_name | トレーニングデータ内の質問テンプレート情報の取得元となるインデックステーブル名です。質問テンプレートのインデックステーブルである必要があります。 | なし | なし |
training_type | トレーニングタイプです。有効値は {'efficient_sft', 'sft'} です。'efficient_sft' は効率的なトレーニングを示し、通常は LoRA 手法を使用します。'sft' は全パラメータトレーニングを示します。 | なし | {'efficient_sft', 'sft'} |
n_epochs | エポック数です。トレーニング中にモデルがデータセットから学習する回数を示します。推奨範囲は 1~3 で、必要に応じて調整できます。 | 3 | [1, 200] |
learning_rate | 学習率です。データ更新ごとのパラメーターの重みの増分を表します。学習率が大きいほどパラメーターの変化が大きくなり、モデルへの影響も大きくなります。 | '3e-4' | なし |
batch_size | バッチサイズです。モデルのパラメーター更新におけるデータのステップサイズを表します。推奨バッチサイズは 16 または 32 です。 | 16 | {8, 16, 32} |
lr_scheduler_type | 学習率ポリシーです。トレーニング中に重みを更新する際に使用する学習率を動的に変更します。 | 'linear' | {'linear', 'cosine', 'cosine_with_restarts', 'polynomial', 'constant', 'constant_with_warmup', 'inverse_sqrt', 'reduce_lr_on_plateau'} |
eval_steps | モデル検証の間隔となるステップサイズです。トレーニング精度と損失を定期的に評価するために使用します。 | 50 | [1, 2147483647] |
sequence_length | トレーニングデータのシーケンス長です。1 サンプルあたりの最大長を示します。この長さを超えるデータは自動的に切り捨てられます。 | 2048 | [500, 2048] |
lr_warmup_ratio | ウォームアップに使用する総トレーニングステップに対する割合です。 | 0.05 | (0, 1) |
weight_decay | L2 正則化です。過学習の抑制に役立ちます。 | 0.01 | (0, 0.2) |
gradient_checkpointing | GPU メモリを節約するために、勾配チェックポインティングを有効または無効にします。 | 'True' | {'True', 'False'} |
use_flash_attn | Flash Attention を使用するかどうかを指定します。 | 'True' | {'True', 'False'} |
lora_rank | LoRA トレーニングにおけるランクサイズです。トレーニングデータがモデルに与える影響の度合いを決定します。 | 8 | {2, 4, 8, 16, 32, 64} |
lora_alpha | LoRA トレーニングにおけるスケーリング係数です。初期トレーニング重みを調整するために使用します。 | 32 | {8, 16, 32, 64} |
lora_dropout | トレーニング中にランダムにドロップするニューロンの割合です。過学習を防ぎ、モデルの汎化能力を向上させます。 | 0.1 | (0, 0.2) |
lora_target_modules | ファインチューニングと最適化の対象とするモデルの特定モジュールを指定します。 | 'ALL' | {'ALL', 'AUTO'} |
モデルの表示
/*polar4ai*/SHOW model udf_qwen_14b_chatモデルの削除
/*polar4ai*/DROP model udf_qwen_14b_chatすべてのモデルの表示
/*polar4ai*/SHOW modelsモデルのデプロイ
トレーニング済みモデルは、デプロイ後にのみ NL2SQL で使用できます。
/*polar4ai*/deploy model udf_qwen_14b_chatデプロイメントの表示
/*polar4ai*/SHOW deployment udf_qwen_14b_chatデプロイメントの削除
/*polar4ai*/DROP deployment udf_qwen_14b_chatすべてのデプロイメントの表示
/*polar4ai*/SHOW deploymentsデプロイ済みモデルによる自然言語から SQL への変換
/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, SELECT 'What is the content for id=1?') WITH (basic_index_name='schema_index', llm_model='udf_qwen_14b_chat')パラメーター
パラメーター | 説明 |
basic_index_name | 必須です。現在の質問に関連するデータベース情報のインデックステーブルを指定する必要があります。 |
llm_model | オプションです。空のままにすると、ファインチューニングされていないモデルが自然言語から SQL への変換に使用されます。値を指定する場合は、「サービング」状態のデプロイメント名であることを確認してください。デプロイが完了していないモデルは、ここでは使用できません。 |