1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

U-22 プログラミングコンテストに応募した話

1
Posted at

はじめに

愛媛大学自然言語処理研究室の村上彰悟です。

大学の授業でPBL(Project Based Learning)に取り組みました。異なる研究室の学生5人でチームを組み、課題設定から解決方法の検討、実装まで進める授業です。
今回は愛媛県からの要請で、地域課題の解決につながるAIサービスを開発することを目的としており、具体的な課題はチームで検討し、ごみ分別を題材に「ごみサポ」というアプリを開発しました。

チームでは主にフロントエンドとバックエンドに分かれて実装し、私はバックエンドを担当しました。

「ごみサポ」はU-22 プログラミング・コンテスト20261に応募しています。この記事を書いている時点では、審査結果はまだ出ていません。

コードと実験結果はGitHubで公開しています。

作ったもの

「ごみサポ」は、正式な品目名が分からなくても、見た目や用途を自然言語で入力してごみの分別を調べられるアプリです。現在は松山市清水地区に対応しています。

例えば、検索欄には次のような入力ができます。

お弁当の透明なフタ

雨の日に使う壊れた長いやつ

中身を使い切った銀色の缶

分別を判断するための情報が足りない場合は、素材や大きさなどを追加で確認します。分類が確定すると、分別区分、出し方、次回収集日を表示します。

デモ

以下の動画では、「お弁当の透明なフタ」と入力してから、検索結果と次回収集日が表示されるまでを確認できます。

demo.gif

課題

自治体のごみ分別サービスでは、品目名が分かっていれば検索できますが、名前が分からず見た目や用途しか説明できない場合があります。

例えば、

パンの袋を留める四角いもの

のような入力です。

この場合、正式な品目名と入力した文字列がほとんど重ならないため、従来のキーワード検索では目的の品目を見つけにくくなります。

一方、ChatGPTのような汎用LLMは曖昧な表現を解釈できますが、ごみの分類ルールは自治体ごとに異なります。後述する評価では、Web検索を利用したGPT-5.6でも、松山市の分類と一致しない例が確認されました。

あいまい検索 自治体固有情報 誤回答リスク 応答速度
多くのごみ検索サービス 23 不可 可 低 速
汎用的な対話サービス 4 可 やや可 高 遅
提案システム 可 可 低 速

そこで、松山市の品目データから入力と意味的に近い候補を検索し、その結果を根拠としてLLMが分類を判定するRAGを実装しました。

システム構成

フロントエンドはFlutter、バックエンドはFastAPIで実装しています。FlutterからAmazon API Gatewayを経由してAWS Lambda上のFastAPIを呼び出します。LambdaではMangumを使ってFastAPIを動かしています。

あいまい検索では、Amazon Nova Liteを検索クエリの言い換えと分類判定に、Amazon Titan Text Embeddings V2を意味検索に使用しています。

松山市の品目データと清水地区の収集カレンダーはLambdaに同梱しており、検索ログはAmazon DynamoDB、バックエンドの実行ログはAmazon CloudWatch Logsへ保存します。AWSリソースはTerraformで管理しています。

image.png

処理の流れは次の通りです。

ユーザーの入力
    ↓
Nova Liteで検索向けの表現に変換
    ↓
Titan Text Embeddings V2でクエリをベクトル化
    ↓
松山市の品目データから意味的に近い候補を検索
    ↓
Nova Liteが検索候補を根拠に分類
    ├─ 情報が足りない → 追加質問
    └─ 分類を確定
            ↓
清水地区のカレンダーから次回収集日を取得

image.png

検索結果をそのまま回答にするのではなく、取得した地域データをNova Liteの分類判定に与えるRAG構成です。

検索クエリの作成

ユーザーの入力は、最初にNova Liteで検索向けの短い表現へ変換します。

プロンプトでは、品目、素材、大きさ、用途、汚れの状態など、分類に必要な情報を残すようにしています。一方で、元の入力から判断できない情報は追加しないようにしています。

LLMの出力だけに任せず、変換後の検索文にもコード側で補正を入れています。

具体的には、入力にない素材が追加された場合は削除し、ユーザーが明示した素材や識別表示が変換後に欠落した場合は補います。

また、Nova Liteによる言い換えで検索対象が変わることを防ぐため、一部の語についてルールベースの補正を行っています。「Xの箱」「Xのふた」のような入力で検索対象がX本体にずれることを防ぐため、「箱」「ふた」「キャップ」「容器」「電池」などの語をあらかじめ定義し、元の入力には含まれているのに言い換え後に消えていた場合は検索文へ戻します。例えば「カメラのフィルムが入っていた箱」で「箱」が消えると、箱ではなくフィルムやカメラ本体に検索が寄るためです。

検索に不要な「松山市」「清水地区」「ごみ分別」といった定型語も除去します。表記の正規化は限定的で、現状では「ビン」「瓶」を「びん」にそろえる処理などを入れています。

Titan Embeddingによる意味検索

検索対象には、松山市『ごみ分別はやわかり帳(家庭用)』をもとに作成した1,756品目のデータを使っています。分類は次の9区分です。

可燃 / 埋立 / 金・ガ / 紙類 / ペット / プラ / 水銀 / 粗大 / 禁止

各品目のEmbeddingは事前にTitan Text Embeddings V2で計算し、ベクトル索引として保存しています。検索時にEmbeddingを計算するのはユーザーのクエリだけです。

クエリと各品目のベクトルは正規化しているため、内積でコサイン類似度を求められます。現在は類似度の高い上位8件を候補とし、コサイン類似度が0.15未満の候補は除外しています。

この0.15は「分類を確定するためのしきい値」ではありません。意味検索でNova Liteへ渡す候補として残すかどうかの下限です。検索側では再現率を優先し、最終的に分類できるかどうかは後段で判定します。

「雨の日に使う壊れた長いやつ」のような入力では、正式な品目名との文字列一致がほとんどありません。Embedding検索では文字列そのものではなく、ベクトル空間上の近さを使って候補を取得します。

image.png

分類判定と追加質問

意味検索で得た上位候補を、そのまま最終結果にはしません。

例えば「容器」という入力だけでは、プラスチック製、ガラス製、金属製のどれかを判断できません。検索上位にプラスチック容器が多く含まれていても、それだけを理由にプラスチック製と確定すると誤分類につながります。

分類時には、元の入力、追加質問への回答、検索で取得した候補をNova Liteへ渡します。Nova Liteは、検索結果に含まれる分類条件とユーザーから得られた情報を照合します。

素材、大きさ、製品か容器包装か、PET・プラなどの識別表示、電池や電源の有無など、分類を分ける情報が不足している場合は追加質問を返します。

Nova Liteには分類結果とあわせて0〜1の確信度スコアを出力させ、0.75未満の場合は確定扱いにしません。また、モデルが示した根拠の中に、その分類コードを実際に支持する候補があるかもコード側で確認しています。

追加質問は最大2回です。同じ内容の質問を繰り返さないようにし、2回確認しても分類できない場合は、無理に9区分のいずれかを返さず、確定できなかったことを回答します。

次回収集日の算出

分類が確定した後の次回収集日は、LLMには生成させていません。

バックエンドで、分類結果と清水地区の収集カレンダーを照合し、次の収集日を求めます。

当日の収集については時刻も確認します。可燃ごみは7時、それ以外の対象区分は8時を搬出期限として扱い、期限を過ぎていれば次の収集日を返します。

自然言語から分類を判断する部分にはLLMを使い、収集日のようにルールで決められる部分は通常のプログラムで処理しています。

image.png

評価

評価データ

あいまい検索の性能を確認するため、松山市の品目データから評価用データを作成しました。

品目単位で調整用データと評価用データに分割し、同じ品目が両方に入らないようにしています。最終評価には評価用データ30件を使用しました。

各ケースでは正式な品目名をそのまま質問に使わず、Nova Liteで意図的に曖昧な表現へ言い換えています。例えば、

梱包の時に使うプチプチみたいなの

のようなクエリです。

追加質問が必要なケースでは、その質問に答えるための実物の条件も評価データ側に保持しています。生成した曖昧クエリは、元の品目を同定できる内容になっているか確認し、レビューを通過したものだけを採用しました。

GPT-5.6との比較

提案システムと、GPT-5.6へ自然文で質問する方法を比較しました。

システム 検索 判定LLM 精度 平均応答時間
提案システム Titan Embedding検索 Nova Lite 90.0%(27/30) 2.0秒
GPT-5.6 + Web検索 Web検索 GPT-5.6 86.7%(26/30) 11.5秒
GPT-5.6 検索なし なし GPT-5.6 66.7%(20/30) 12.5秒

GPT-5.6には「愛媛県松山市において『{クエリ}』は何ゴミに分類されますか?」という形式で質問しました。自由文の回答は、別モデルで9分類コードへ変換して採点しています。

GPT-5.6側のAPIクレジットの都合から、比較は30件で行いました。
精度、応答時間ともに提案システムの方が優れている結果となりました。

検索方式の比較

検索部分の寄与を確認するため、判定LLM、分類プロンプト、追加質問シミュレータを固定し、検索方法だけを切り替えました。

検索構成 精度 平均応答時間 内訳
Hybrid(表層一致 + Embedding) 86.7%(26/30) 1.2秒 誤分類4
Embedding単体 90.0%(27/30) 1.2秒 誤分類3
表層一致のみ 73.3%(22/30) 1.2秒 未確定6 + 誤分類2

表層一致のみでは、曖昧なクエリと正式品目名の文字列が重ならず、候補を取得できないケースが6件ありました。
Hybridでは、文字列上は近いものの正解ではない品目が上位へ入り、後段の判定を誤らせるケースがありました。
今回の自前Embedding索引ではEmbedding単体が最も高い結果でした。

Bedrock Knowledge Baseを使った検索との比較

意味検索には、現在使用しているTitan Text Embeddings V2による検索のほかに、Amazon Bedrock Knowledge Basesも試しました。

現在の方式では、松山市の各品目をTitan Text Embeddings V2でベクトル化し、1品目ずつ管理しています。検索時には入力を同じモデルでベクトル化し、各品目とのコサイン類似度から候補を取得します。

一方、Bedrock Knowledge Basesは、文書の取り込みやEmbedding化、索引の管理、検索までをAWS側で扱えるRAG向けのマネージドサービスです。検索基盤を自分で実装する必要が少ない点では、こちらの方が手軽です。

それぞれについて、意味検索のみの場合と、表層一致検索を組み合わせたHybridの場合を比較しました。

意味検索の実装 Hybrid(表層一致を併用) 意味検索のみ
Titan Embedding + 自前検索 86.7% 90.0%
Bedrock Knowledge Base 93.3% 83.3%

Titan Embeddingによる自前検索では意味検索のみが最も高く、Bedrock Knowledge BaseではHybridが最も高い結果となりました。

この結果から、表層一致検索を併用すれば常に精度が向上するわけではなく、検索基盤によって適した構成が異なることが分かりました。

現在の公開環境では、検索単位を1品目ごとに管理しやすく、検索処理もこちらで制御できるTitan Embedding + 自前検索を採用しています。

調整用40件での改善確認

評価用データで評価する前に、別の40件を使って検索方法と分類処理を調整しました。

構成 精度
表層一致のみ 72.5%
1品目単位の意味検索を追加 82.5%
品目種別ごとの判定ルールをプロンプトへ追加 90.0%

最後の段階では、例えば「プラスチック製品」と「プラスチック製容器包装」を区別するなど、松山市の分別ルールに沿って候補を判定するためのルールをプロンプトへ追加しました。

この結果は、最終評価とは別の40件を使って調整していた段階のものです。改善の過程を確認するための結果として載せておきます。

誤答分析

評価用データ30件のうち提案システムが誤分類したのは3件でした。

ピアノ

正解は「禁止」ですが、「大きな木製の鍵盤楽器」というクエリから、琴や鍵盤ハーモニカなどが上位候補となり、「粗大」と判定しました。

このケースでは、検索段階で正解の「ピアノ」を候補に含められていませんでした。

ただ、そもそもピアノを「大きな木製の鍵盤楽器」と入力するユーザーがどれくらいいるのか、という疑問もあります(笑)。今回の評価データは、正式な品目名を隠してLLMに曖昧な表現へ言い換えさせているため、このように実際の利用場面ではやや不自然なクエリも含まれています。

そのため、この誤答は検索性能の課題であると同時に、評価データの自然さについても見直す必要がある例だと考えています。

グリルパン

正解は「金・ガ」ですが、「網目のついた調理器具」という表現から、調理台や電磁調理器などが上位候補として取得され、「粗大」と判定しました。

このケースも、検索段階で正解のグリルパンを候補に含められなかった例です。「網目のついた調理器具」のように特徴だけを表した入力では、意味的に近い別の調理器具が上位候補となりました。

一方で、この入力も実際のユーザーがグリルパンを表現する言葉として自然かどうかは検討の余地があります。ピアノの例と同様に、LLMで生成した評価クエリの自然さも今後確認する必要があります。

グラインダーの替刃

正解ラベルは「埋立」ですが、システムは「金・ガ」と判定しました。

データ内では、カッター、カミソリ、草刈機、芝刈機など多くの替刃が「金・ガ」に分類されている一方、グラインダーの替刃は「埋立」に分類されています。GPT-5.6 + Web検索、GPT-5.6検索なしでも同様に「金・ガ」と回答しました。

このケースは前の2件とは異なり、検索候補に正解品目が入らなかったことよりも、類似する品目の多くとは異なる分類になっていることが誤答に影響したと考えられます。

GPT-5.6の誤答

GPT-5.6検索なしでは、白熱電球を「埋立」、コピー用紙を「可燃」、スプリングマットレスを「粗大」とするなど、一般的な知識に基づく回答が松山市の分類と一致しないケースがありました。

Web検索を利用したGPT-5.6では、「血圧を測る機械」という入力を電子血圧計と解釈し、評価対象である水銀式血圧計とは異なる品目として回答する例がありました。この入力だけでは水銀式か電子式かを判断できません。提案システムでは、このように分類を決めるための情報が不足している場合は、推測して分類を確定せず、必要な情報を追加質問で確認するようにしています。

今回の誤答分析から、自治体の情報を参照できるかだけでなく、曖昧な入力から正しい品目を検索できるかが精度に影響することが分かりました。一方で、LLMで生成した評価クエリには実際の利用場面では不自然なものも含まれており、評価データの作り方にも改善の余地があります。

おわりに

「ごみサポ」では、松山市の品目データを意味検索し、取得した情報を根拠にLLMが分別を判定するRAGを実装しました。品目名が分からない場合でも、見た目や用途を自然な言葉で入力して分別方法を調べられるシステムを目指しました。

自分自身、大学進学をきっかけに松山へ引っ越してきた際、地元よりもごみ分別が細かく、捨て方に迷うことが何度もありました。今回のように、品目名が分からなくても自然な言葉で気軽に検索できる仕組みがあれば、同じように松山へ引っ越してきた人や、ごみ分別に慣れていない人の助けになるのではないかと考えています。

開発にあたっては、愛媛県にAWSの利用費を負担していただいたほか、AWSに関する講義や、AWSジャパンの技術者の方による伴走支援も受けました。普段の研究では経験することのない、AWSを使ったサービスの構築や運用まで取り組むことができ、貴重な経験になりました。

余談にはなりますが、U-22 プログラミング・コンテストの応募締切が8月31日で、8月は国際会議の投稿締切やインターンも重なっていたため、かなり慌ただしいスケジュールでの開発になりました(笑)。その中でも、チームで一つのサービスを形にし、実装だけでなく評価や誤答分析まで行えたのは良い経験でした。

  1. U-22 プログラミング・コンテスト2026
    https://u22procon.com/ ↩

  2. 松山市, 「松山市公式LINE」
    https://www.city.matsuyama.ehime.jp/shisei/koho/sns/LINE/LINE.html ↩

  3. Delight System, 「ごみ分別アプリ『さんあ〜る』」
    https://threer.delight-system.co.jp/ ↩

  4. OpenAI, 「ChatGPT」
    https://chatgpt.com/ ↩

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?