query-understanding-ja-4b

日本語の商品検索クエリを、検索システムで扱いやすい構造化 JSON に変換する Query Understanding モデルです。Qwen/Qwen3.5-4B を LoRA でファインチューニングし、アダプターをベースモデルへマージした完全なチェックポイントとして公開しています。

このモデルは、公開データセットである Amazon Shopping Queries Dataset(ESCI)の日本語クエリを使って、小規模なデータと LoRA で小型言語モデルを検索クエリ解釈に特化できるかを検証した PoC(概念実証)です。特定の EC サイトのクエリログや商品カタログに合わせて調整したものではなく、そのまま本番の検索システムに組み込むことを想定していません。実際のサービスに使う場合は、対象サイトのデータでの再学習と評価が必要です。

transformers で trust_remote_code=True を付けて読み込むと parse メソッドが使えるようになり、クエリ文字列を渡すだけで構造化した結果(Python の dict)が返ります。カスタムコードを使わずに通常の Qwen3.5 モデルとして読み込むことも、vLLM でそのまま配信することもできます。

This model converts a Japanese e-commerce search query into a structured JSON object (product type, full-text search terms, inclusion filters, exclusion filters, and unresolved conditions). It is Qwen/Qwen3.5-4B fine-tuned with LoRA on 24,146 queries derived from the Japanese subset of the Amazon Shopping Queries Dataset (ESCI); the adapter is merged into the checkpoint. Load with trust_remote_code=True and call model.parse(query). This is a proof of concept built on the public ESCI dataset, not a production-ready component; retrain and evaluate on your own query logs before using it in a real search system.

何をするモデルか

検索ボックスに入力される短い文字列には、商品種別、希望条件、除外条件、価格などの条件が混ざっています。これをそのまま全文検索へ渡すと、除外したいはずのブランド名まで通常の検索語として扱われるなど、意図が検索条件に反映されません。このモデルは、クエリを次の 5 項目に分解します。

入力:

ロジクール以外の静音ワイヤレスマウス 黒

出力:

{
  "product_type": "マウス",
  "search_terms": ["静音", "ワイヤレス", "マウス"],
  "include": {
    "color": ["黒"],
    "feature": ["静音", "ワイヤレス"]
  },
  "exclude": {
    "brand": ["ロジクール"]
  },
  "unresolved": []
}

検索システム側では、search_terms を全文検索へ、include をフィルターやブーストへ、exclude を除外や降格へ渡します。unresolved には、価格や送料のように商品属性のスキーマでは扱えない条件が原文のまま入るので、後段で処理の可否を判断できます。モデルは検索エンジン固有のクエリ構文を生成せず、用途に依存しない JSON を返すだけなので、Elasticsearch などのクエリへの変換はアプリケーション側の決定的なルールで行えます。

出力スキーマ

出力は常に次の 5 つのキーを持つ JSON オブジェクト 1 つです。

キー 内容
product_type 文字列または null 探している商品の種別。クエリ中の語をそのまま使い、修飾語は付けない。作品名、人名、ブランド名だけ、型番だけのクエリなど商品種別を表す語がない場合は null
search_terms 文字列の配列 全文検索に渡す語句。クエリを意味のまとまりで分割し、出現順に並べる。product_type がある場合は brand と color の値を含めない。除外対象の語と unresolved の条件も含めない
include 属性名から文字列配列への辞書 クエリに明示された絞り込み条件。該当のない属性は省略
exclude 属性名から文字列配列への辞書 「以外」「なし」「じゃない」「を除く」「不要」などで明示された除外条件。否定表現そのものは値に含めない
unresolved 文字列の配列 価格、送料、評価や人気、状態(中古、新品)、時期(最新、2023年版)など、属性で表せない条件の原文

include と exclude で使える属性は次の 9 種類です。

属性 内容 値の例
brand ブランド名、メーカー名 ナイキ, パナソニック, anker
color 黒, ホワイト, ネイビー, 透明
size 寸法、サイズ表記、容量、規格、対応人数 27cm, lサイズ, 1リットル, 3人用
material 素材、材質 ステンレス, 本革, 綿100%
target 対象者、対象動物 レディース, キッズ, 子供, 犬用
compatible_with 対応機種、対応製品、適合対象 iphone8, ps4, スイッチ
feature 機能、仕様、形状、タイプ、用途などの修飾語 静音, ワイヤレス, 防水, 手帳型
quantity 個数、枚数、入数、セット数、巻数 120粒, 2個セット, 全巻
model_number 型番、品番、製品コード ytx-14bs, sm-g973

主な規則は次のとおりです。

  • クエリに明示された情報だけを抽出し、世界知識で補完しない(「iphone8 ケース」の brand に apple を入れない)
  • include と exclude の値は必ずクエリ中に出現する語句を使う。表記は NFKC 正規化(全角英数字を半角に)だけを行い、言い換え、翻訳、正式名称への変換、誤字の修正はしない
  • 同じ語を include の複数の属性に入れない。brand と color 以外の属性の値は search_terms にも含める
  • 複数語からなる 1 つの名称(usb type c など)は分割しない

値はクエリ中の表記のままなので、ブランドの別名や色の表記揺れの統一は検索システム側の辞書で行う想定です。

使い方

transformers(parse メソッド)

import torch
from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(
    "mahiyama/query-understanding-ja-4b",
    trust_remote_code=True,
    dtype=torch.bfloat16,
    device_map="cuda",
)

result = model.parse("ロジクール以外の静音ワイヤレスマウス 黒")
print(result)
# {'product_type': 'マウス', 'search_terms': ['静音', 'ワイヤレス', 'マウス'],
#  'include': {'color': ['黒'], 'feature': ['静音', 'ワイヤレス']},
#  'exclude': {'brand': ['ロジクール']}, 'unresolved': []}

# 複数のクエリはリストで渡すとバッチ推論になり、同じ順序のリストが返る
results = model.parse([
    "ナイキ スニーカー メンズ 27cm 白以外",
    "子供が使える軽いテニスラケットを探しています 5000円以下",
])

parse メソッドの仕様は次のとおりです。

  • 引数は文字列 1 つ、または文字列のリスト。文字列なら dict を、リストなら dict のリストを返す
  • 生成は貪欲デコード(do_sample=False)で、既定の max_new_tokens は 256。通常の出力は 30 から 40 トークン程度
  • 返り値は正規化済み(NFKC、重複の除去、空の属性の省略、キー順の固定)。生成テキストが JSON として解釈できない場合、そのクエリの結果は None
  • return_raw=True を指定すると、各要素が (dict または None, 生成テキスト) のタプルになる
  • batch_size(既定 16)でバッチの大きさを変えられる。generate に渡す追加の引数はキーワード引数としてそのまま渡せる
  • understand は parse の別名
  • tokenizer はリポジトリから自動で読み込まれる。model.tokenizer に代入すると差し替えられる

学習時と同じ system プロンプトとチャットテンプレート(思考過程の生成を無効化した形式)を内部で組み立てるので、利用側でプロンプトを用意する必要はありません。

カスタムコードを使わない場合

このチェックポイントはベースモデル Qwen/Qwen3.5-4B と同じレイアウトなので、trust_remote_code なしで通常の Qwen3.5 モデルとして読み込めます。その場合は、次の system プロンプトを付け、思考過程を無効化(enable_thinking=False)してチャットテンプレートを適用してください。

商品検索クエリを解析し、次のキーを持つJSONのみを出力してください: product_type, search_terms, include, exclude, unresolved (属性キー: brand, color, size, material, target, compatible_with, feature, quantity, model_number)
import json
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

repo = "mahiyama/query-understanding-ja-4b"
tok = AutoTokenizer.from_pretrained(repo)
model = AutoModelForCausalLM.from_pretrained(repo, dtype=torch.bfloat16, device_map="cuda")

SYSTEM = ("商品検索クエリを解析し、次のキーを持つJSONのみを出力してください: "
          "product_type, search_terms, include, exclude, unresolved "
          "(属性キー: brand, color, size, material, target, compatible_with, feature, quantity, model_number)")
messages = [{"role": "system", "content": SYSTEM},
            {"role": "user", "content": "ナイキ スニーカー メンズ 27cm 白以外"}]
inputs = tok.apply_chat_template(messages, add_generation_prompt=True, enable_thinking=False,
                                 return_tensors="pt", return_dict=True).to(model.device)
out = model.generate(**inputs, max_new_tokens=256, do_sample=False)
text = tok.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
print(json.loads(text))

vLLM

マージ済みの完全なチェックポイントなので、vLLM では通常の Qwen3.5 モデルとして配信できます。

vllm serve mahiyama/query-understanding-ja-4b --max-model-len 4096

OpenAI 互換 API では、上記の system プロンプトを付け、temperature を 0 にし、chat_template_kwargs で enable_thinking を false にしてください。

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
resp = client.chat.completions.create(
    model="mahiyama/query-understanding-ja-4b",
    messages=[{"role": "system", "content": SYSTEM},
              {"role": "user", "content": "iphone15 pro ケース 手帳型 マグネットなし"}],
    temperature=0.0,
    max_tokens=256,
    extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
print(resp.choices[0].message.content)

LoRA アダプター

マージ前の LoRA アダプター(PEFT 形式、rank 64、alpha 128)を lora-adapter フォルダに同梱しています。ベースモデルに後から適用する場合は次のようにします。

import torch
from transformers import AutoModelForCausalLM
from peft import PeftModel

base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-4B", dtype=torch.bfloat16, device_map="cuda")
model = PeftModel.from_pretrained(base, "mahiyama/query-understanding-ja-4b", subfolder="lora-adapter")

動作環境

  • Qwen3.5 に対応した transformers(5.x 系)が必要です。学習と評価は transformers 5.16.1、公開前の動作確認は transformers 5.13.1 と PyTorch 2.11 で行いました
  • bfloat16 で言語モデル部分を GPU に載せると約 8 GB のメモリを使います。GPU メモリが足りない場合は device_map="auto" で CPU と分割して読み込めます(速度は落ちます)
  • Qwen3.5 は線形アテンション層(Gated DeltaNet)を含みます。flash-linear-attention と causal-conv1d がインストールされていれば高速なカーネルが使われ、なければ PyTorch 実装で動作します

学習

学習データ

学習データは 24,146 件で、すべて Amazon Shopping Queries Dataset(ESCI) の日本語サブセットに由来します。ESCI の test 分割は学習、合成、選別のいずれにも使わず、評価専用にしています。

  • 実クエリ 12,043 件: ESCI 日本語 train 分割のユニークなクエリ(正規化した文字列が test と一致するものは除外)
  • 合成クエリ 12,103 件: 日本語の実クエリは平均約 10 文字で 6 割以上が 1〜2 語のため、不足する検索意図を補うために合成した。train 側にだけ現れる商品のタイトル、ブランド、色を種にして、ブランド指定、色指定、ブランドや色や機能の否定、自然文、価格や送料などの未解決条件、対応機種、型番、3 条件以上の複合、表記揺れ(全角、かな、連結、大小文字、軽い誤字)、数量の 12 パターンを Qwen/Qwen3.5-27B に生成させた

正解となる構造化 JSON は Qwen/Qwen3.5-27B が作成しました。27B モデルには 5 項目と 9 属性の詳細な定義を含むガイドラインと 18 件の例を与え、思考過程なしの貪欲デコードで JSON を生成させています。生成した JSON は次の検証を通過したものだけを採用しました。

  1. JSON として解釈でき、必須キー、型、属性名がスキーマに合うこと
  2. include と exclude の値がクエリ中に存在すること
  3. ブランドと色の値を search_terms から外す、除外対象の語を search_terms から外すなど、決定的な規則を後処理で適用すること

学習例は system、user、assistant の 3 メッセージで、system は上記の短い固定プロンプト、user はクエリ、assistant は JSON だけです。詳細なガイドラインはラベル生成時にだけ使い、4B モデルにはその判断規則をファインチューニングで学習させたので、推論時に長い指示や例を送る必要はありません。

学習設定

項目 内容
ベースモデル Qwen/Qwen3.5-4B(テキスト部分の Qwen3_5ForCausalLM としてロード)
学習方式 LoRA による教師ありファインチューニング
LoRA rank / alpha / dropout 64 / 128 / 0.05
LoRA 適用先 通常アテンションの q_proj, k_proj, v_proj, o_proj、線形アテンションの in_proj_qkv, in_proj_z, in_proj_b, in_proj_a, out_proj、FFN の gate_proj, up_proj, down_proj(全 32 層)
学習可能パラメータ 129,859,584(全体の約 3%)
学習データ / 開発データ 24,146 件 / 1,336 件
最大系列長 768 トークン(学習例は平均 116 トークン、最大 224 トークン)
学習率 / スケジュール 1e-4 / cosine、warmup 3%
epoch / 実効バッチ 2 / 32(16 × gradient accumulation 2)
数値形式 / optimizer bfloat16 / fused AdamW
損失 assistant の JSON 部分と終了トークンのみ
思考過程 学習時、推論時とも無効(enable_thinking=False)
学習時間 約 17.6 分(NVIDIA RTX PRO 6000 Blackwell 96 GB 1 枚)

評価

指標

  • Exact Match: product_type、search_terms、include、exclude、unresolved の 5 項目すべてが正解と一致したクエリの割合。比較は NFKC 正規化、大文字小文字の同一視、空白の除去を行った上での完全一致で、search_terms の順序は問いません。JSON として解釈できない出力は不正解です
  • Attribute F1: include と exclude に含まれる(属性名, 値)の組を全クエリで集計した micro F1。検索の絞り込みと除外に直接使う条件の抽出精度を表します
  • JSON 有効率: 出力が JSON として解釈でき、スキーマに合致した割合
  • grounding 率: 出力した include と exclude の値のうち、クエリ中に実在する語句の割合(条件の捏造がないこと)

評価セット

  • 実クエリ 600 件: ESCI 日本語 test 分割のクエリから作成。Qwen3.5-27B にクエリごとに 3 回ラベルを生成させ、3 回とも一致した判断の安定した例 300 件と、一致しなかった判断の難しい例 300 件を選び、その全件を AI エージェントがガイドラインに照らして 1 件ずつ確認して 91 件を修正した。難しいクエリを意図的に半数含むため、この値は ESCI 全体の平均精度ではなく、モデル間の比較値として扱う
  • 合成クエリ 711 件: 学習に使っていない評価側の商品情報から、否定表現、複数条件、対応機種、価格条件などの検索意図を狙って合成したクエリ。正解ラベルは Qwen3.5-27B が生成したものなので、同じ 27B モデルの評価には有利に働く

結果

実クエリ 600 件:

モデル 推論方法 Exact Match Attribute F1 JSON 有効率 grounding 率
Qwen3.5-4B 未学習(短い指示のみ) 4.7 0.0 99.8 0.0
Qwen3.5-4B 未学習(ガイドラインと 18 例の few-shot) 39.8 63.0 100.0 99.3
Qwen3.5-4B LoRA(このモデル) 短い指示のみ 69.5 81.7 100.0 100.0
Qwen3.5-27B 未学習(ガイドラインと 18 例の few-shot) 70.7 84.4 100.0 100.0

合成クエリ 711 件:

モデル 推論方法 Exact Match Attribute F1 JSON 有効率 grounding 率
Qwen3.5-4B 未学習(短い指示のみ) 0.0 0.6 99.4 100.0
Qwen3.5-4B 未学習(ガイドラインと 18 例の few-shot) 20.8 73.9 100.0 99.5
Qwen3.5-4B LoRA(このモデル) 短い指示のみ 68.4 88.8 100.0 99.8
Qwen3.5-27B 未学習(ガイドラインと 18 例の few-shot) 67.7 95.9 100.0 99.9

数値はすべて百分率です。このモデルの Exact Match の 95% 信頼区間(クエリ単位のブートストラップ)は、実クエリ 600 件で 65.5 から 72.8、合成クエリ 711 件で 64.8 から 71.7 です。実クエリでは、約 70 トークンの短い指示だけで、ガイドラインと 18 例を毎回与えた 27B モデルと同程度の精度になっています。

このモデルの項目別の値:

項目 実クエリ 600 件 合成クエリ 711 件
product_type 正解率 87.0 89.6
search_terms F1(クエリ平均) 91.0 91.1
include F1 81.1 88.1
exclude F1 88.2 93.3
unresolved F1 87.5 93.7
除外条件を含むクエリで exclude が完全一致した割合 82.0(50 件中) 95.7(161 件中)

属性別の F1(括弧内は正解に含まれる組の数):

属性 実クエリ 600 件 合成クエリ 711 件
brand 83.9 (136) 90.1 (324)
color 90.9 (15) 96.9 (210)
size 90.7 (45) 91.3 (103)
material 71.0 (16) 92.8 (35)
target 89.6 (48) 84.0 (60)
compatible_with 76.1 (49) 85.2 (92)
feature 77.8 (213) 81.2 (324)
quantity 80.0 (2) 95.4 (72)
model_number 85.7 (15) 88.9 (81)

参考として、同じデータと設定で学習したベースモデルのサイズ違いの結果を示します。

ベースモデル 実クエリ 600 件 EM / Attr F1 合成クエリ 711 件 EM / Attr F1
Qwen3.5-2B LoRA 64.2 / 76.3 63.3 / 86.1
Qwen3.5-4B LoRA(このモデル) 69.5 / 81.7 68.4 / 88.8
Qwen3.5-9B LoRA 71.3 / 82.9 70.3 / 89.9

推論速度

vLLM 0.29.0 の OpenAI 互換サーバー(NVIDIA RTX PRO 6000 Blackwell 96 GB 1 枚、bfloat16、同時実行数 1、貪欲デコード)に 200 件のクエリを 1 件ずつ送って測定した応答時間です。TTFT はリクエスト送信から最初のトークンを受け取るまで、E2E は生成完了までの時間です。

項目
TTFT 平均 / p95 26.2 ms / 27.2 ms
E2E 平均 / p50 / p95 252.4 ms / 241.7 ms / 338.3 ms
入力トークン平均(チャットテンプレート適用後) 75.8
出力トークン平均 32.0
JSON として解釈できた件数 200 / 200

検索リクエストごとに毎回オンラインで推論するには、数百ミリ秒の応答時間は無視できません。実運用では、頻出クエリの解釈結果をオフラインで事前生成してキャッシュし、キャッシュにないクエリだけをオンラインで推論する構成を推奨します。多数のクエリをまとめてバッチ推論する場合のスループットは、単独リクエストの応答時間から想像するよりずっと高くなります。

制限事項

  • 本モデルは公開データセット ESCI を使った PoC であり、本番運用を想定した品質保証や継続的な評価は行っていません。実際のサービスに使う場合は、対象サイトのクエリログでの再学習と評価、出力スキーマとガイドラインの見直し、検索側の正規化辞書との接続が必要です
  • 値はクエリ中の表記をそのまま使うため、ブランドの別名や色の表記揺れの統一(ブラック、黒、black など)は検索システム側で行う必要があります
  • 商品種別と属性の境界(対応機種かブランドか、機能か検索語か)、複合語の分割、「〜なし」の解釈には、ラベル作成時のガイドラインに由来する揺れがあり、Exact Match の上限はラベル自体の一致率に近い水準です
  • 空白を入れずに語を連結したクエリでは語の切れ目が曖昧になり、空白区切りのクエリより精度が下がる傾向があります
  • 学習データは Amazon の日本語検索クエリに基づくため、他の言語や、商品検索以外のドメインでは再学習が必要です
  • 設計上、クエリに書かれていない情報は補完しません。ブランドから商品種別を推測したり、型番からメーカーを特定したりはしません
  • 正解ラベルは大規模言語モデルが生成したもので、人手のアノテーションではありません。評価値も同じ性質のラベルに基づく比較値として扱ってください

ライセンスと出典

@article{reddy2022shopping,
  title   = {Shopping Queries Dataset: A Large-Scale {ESCI} Benchmark for Improving Product Search},
  author  = {Chandan K. Reddy and Lluís Màrquez and Fran Valero and Nikhil Rao and Hugo Zaragoza and Sambaran Bandyopadhyay and Arnab Biswas and Anlu Xing and Karthik Subbian},
  journal = {arXiv preprint arXiv:2206.06588},
  year    = {2022}
}

このモデルを引用する場合:

@misc{query-understanding-ja-4b,
  title  = {query-understanding-ja-4b: Japanese product search query understanding with Qwen3.5-4B and LoRA},
  author = {mahiyama},
  year   = {2026},
  url    = {https://huggingface.co/mahiyama/query-understanding-ja-4b}
}
Downloads last month
5
Safetensors
Model size
5B params
Tensor type
BF16
·
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for mahiyama/query-understanding-ja-4b

Finetuned
Qwen/Qwen3.5-4B
Finetuned
(631)
this model
Adapters
1 model

Paper for mahiyama/query-understanding-ja-4b