はじめに
Student Cup 2025 にチームで参加しました。
最終成績は score 約0.615 で 3位 でした。
この記事は「解法解説」というよりは、チームでどのような仮説を立て、何を試し、何がうまくいかなかったのかを記録する「作業ログ」として生成AIでまとめ、残します。
(議論はしたものの実装に至らなかった案や、失敗した試行錯誤も含めて記述します)
短期間で方針が二転三転しましたが、その泥臭い過程も含めて共有できればと思います。
取り組みの全体像
期間中に試したアプローチをざっくり分類すると以下の通りです。
- 直当て(Direct Prompting): 50候補をそのままLLMに投げる
- 候補絞り込み(Filtering): Embedding等で候補を減らしてからLLMに投げる
- Embeddingの工夫: ベクトル演算での正解探索
- 中間表現(Feature Extraction): 特徴抽出を挟む
- データ拡張(Augmentation): Perplexity等での外部知識補強
- 運用・アンサンブル: APIパラメータ調整と提出ファイルのパッチワーク
1. 直当てフェーズ:ベースラインの作成
50候補を全部渡して2つ選ばせる
まずはシンプルに、LLM(ChatGPT / Gemini / Claude)に全候補を投げました。
Prompt概要:
「fiction は2つの base_story の合成である。候補リスト(50作品)の中から元となった2つを選べ」
感触:
- ChatGPT5.2: 0.45 前後
- Gemini3 / Claude4.5: 0.50 ~ 0.56 前後
50候補すべてをコンテキストに含めると、LLMの推論能力が分散してしまう印象でした。「雰囲気は似ているが正解ではない作品」に引っ張られるケースが多発しました。
プロンプトエンジニアリングによる改善
- ルールの厳格化(出力フォーマットの固定など)
- 選定根拠の言語化(CoT: Chain of Thought の誘導)
- Step-by-Step での推論
これらを行いました。劇的なスコア向上には至らなかったものの、プロンプトで0.01~0.05程度の変化はあったのでプロンプトの重要性に気が付きました。特に、試行過程を言語化させることで不審な挙動が減ったように感じます。
2. 候補絞り込みフェーズ:Recall重視の戦略
ここが今回一番効いたポイントでした。
- 課題: 50候補を一気に渡すとLLMが混乱する(Context Window内での迷子)
- 対策: Embedding等の軽量な手法でまず Top 10 ~ 30 に絞り、その絞った候補だけをLLMに渡して精密検査させる
結果:
候補を50個で渡すよりも、10~30個程度まで絞った方が最終的な正答率が高い傾向が見られました。
「候補数」自体が重要なハイパーパラメータとなり、Recall(正解を含んでいる確率)とPrecision(LLMが選びきれる数)のトレードオフを探る形になりました。
絞り込みの精度が上がれば候補を減らすことができ、最終的なスコア向上も見込めるため、ここら辺の工夫も勉強したいです。
3. Embeddingフェーズ:ベクトルの夢と現実
「LLMを使わずにEmbeddingだけで解けるのではないか?」という仮説のもと、いくつかの実験を行いました。
合成文生成 → Embedding
- 仮説: 2つの候補から合成文を生成し、ターゲットの fiction との類似度を見れば良いのでは?
- 結果: 精度出ず。 生成される合成文が「それっぽい」だけで、実際の fiction の混ざり方(文体やプロットの融合)を再現できず、ベクトルが近づきませんでした。
ベクトル和(id_a + id_b)による探索
-
手法:
vec(story_A) + vec(story_B)を計算し、vec(fiction)との類似度が高いペアを探索。 - 結果: ローカル検証(一部のデータ)では Top1 Accuracy 0.51 程度が出て期待しましたが、本番データでは通用せず。単純なベクトルの足し算では表現しきれない非線形な要素が多かったと思われます。
上位ペアの Rerank
Embeddingで算出した上位20ペアを LLM に渡し、「どっちが正解っぽい?」と選ばせる Reranking も試しました。補助的な手法としては機能しましたが、決定打にはなりませんでした。
結論: Embedding はあくまで「候補を減らす(足切り)」ために使い、最終決定はLLMに任せるのが正解でした。
4. 中間表現フェーズ:特徴量抽出
fiction と base_story の直接比較ではなく、一度「要素」を抽出してから比較するアプローチです。
- 手法: fiction から「登場人物」「舞台設定」「主要な出来事」を構造化データとして抽出。それをクエリとして base_story を検索・評価する。
- 知見: モデルによって抽出の癖が異なりました。
- gpt-oss系: 制約条件やシステム的なルールの抽出が得意
- llm-jp系: 物語の出来事やプロットの列挙が得意
この「モデルの癖」の違いを利用してアンサンブルする余地がありそうでしたが、今回は時間切れで詰めきれませんでした。しかし、qwen系も試してみたかったです。
5. データ拡張:外部知識の注入
地味ながら絶妙な効果があったのが、base_stories の情報補強です。
- 課題: 提供された base_stories のあらすじが薄く、推論材料が足りない(特にシリーズ物や複雑な設定の作品)。
- 対策: Perplexity を使用し、作品の「世界観」「時代背景」「人間関係」「用語」などの追加情報をWebから収集。説明文(メタデータ)をリッチにしました。
推論モデルを変えずとも、参照するデータの質を上げることで上限スコアが伸びることを再確認しました。しかし、これはアンサンブルするも、決定打には繋がらず。
6. 運用・最終構成
終盤は、オペレーションの勝負になりました。
Web UI から API への移行
Web UI(ChatGPT / Gemini Advancedなど)は手軽ですが、以下の問題がありました。
- 同じプロンプトでも回答がブレる
- 再現性がない
そのため、終盤は APIでのバッチ処理に移行し、結果を安定させました。
差し替え運用(部分的アンサンブル)
単一のモデル・手法ですべて正解させるのは困難です。
- 手法Aで自信がある回答
- 手法B(別モデル)で自信がある回答
これらをマージし、CSVレベルで「強い部分だけを継ぎ接ぎする」泥臭い運用を行いました。
まとめ・所感
今回のコンペを通じて得た結論は以下の通りです。
「検索(候補絞り込み)+ LLM(推論)」の分業が最強
- いきなりLLMに全投げせず、タスクを分割するアプローチが有効でした。
モデルパワーは正義
- 終盤に投入した上位モデル(Claude 4.6 Opus)の推論能力が高く、プロンプトの工夫以上に「モデルの地力」がスコアに直結しました。
データセット自体の質を疑う
- 与えられたデータをそのまま使うだけでなく、外部知識で補強するデータ中心のアプローチが効きました。
技術的に尖ったワンアイデアで突破するというよりは、「手堅いパイプライン構築」と「最新モデルの適切な利用」、そして「泥臭いデータ改善」の総力戦だったと感じています。
付録:使用モデル・ツールまとめ
今回のコンペで使用したリソースの一覧です。
1. 商用API系(推論)
メインの推論および最終的なアンサンブルに使用しました。
- OpenAI: ChatGPT 5.2
- Google: Gemini 3 Pro / Gemini 3 Flash
- Anthropic: Claude 4.5 Sonnet / Claude 4.5 Opus / Claude 4.6 Opus (※終盤に投入)
2. ローカルLLM(検証用)
特定タスクの癖の把握や、推論の多様性を確保するために活用しました。
- gpt-oss-20b
- llm-jp-13b
- llm-jp-3.1-8x13b-instruct4
3. Embedding
候補の絞り込み(Retrieval)フェーズで使用しました。
- qwen3-embedding-8b
4. 補助ツール
-
Perplexity:
base_storiesの背景知識・設定の補強に使用。 - Google Cloud: 各種APIの実行基盤。
- 各種Web UI: プロンプトの挙動確認や、API結果との比較検証。