dip
求人レコメンドモデルの改善に挑戦した2週間 —— 仮説検証から実運用の問題対応まで

求人レコメンドモデルの改善に挑戦した2週間 —— 仮説検証から実運用の問題対応まで

# 実践# インターンシップ
インターンシップ
2026/08/17公開2026/08/20更新

はじめに

こんにちは!名古屋工業大学学部3年の山本賢人(あかつき)というものです。2026年8月3日(月)から2週間、ディップ株式会社CTO室 AI Embeddedユニットのインターンに参加し、求人レコメンドに使われているモデルの改善をメインに業務を行いました。

こちらは、その際に取り組んだこと・学んだことをまとめた参加記事になります。

インターンの概要・取り組むうえでの目標

今回実務で取り組んだ内容は3つあります。メインはSentence BERTを使った求人レコメンドモデルの精度改善です。加えて、比較実験を回しやすくするパイプラインアーキテクチャの改善と、Vector SearchのANN Indexに関する問題の調査・修復にも取り組みました。

そこでの目標は大規模なモデル改善に対して自分で仮説を立てて行動しつつ、与えられたTODO以外も先回りして追加の成果を出すことでした。

取り組んだこと

1. レコメンドモデルの改善

やりたいこと

バイトルには、求人を見たユーザーに対して次に応募しそうな求人をレコメンドするモデルがあります。このモデルの精度を上げることで、アクセスしたユーザーの応募率などを改善したいという動機があり、今回はこのレコメンドモデルの改善をメインのタスクとして取り組みました。

今回扱うモデルは数百万パラメータの埋め込みモデルであり、求人のテキストなどをベクトル化し、次に閲覧・キープ・応募しそうな求人をより近いベクトルにして検索させることが技術的な目標です。

学習では、行動ログから求人のペアを作り、求人テキストをベクトル化します。ユーザーが実際に選んだ求人ペアを正例として近づけ、逆に選ばなかったであろう求人同士のペアを負例として遠ざける対照学習を行いました。

評価には、学習に使わなかったログを使用して、10個レコメンドした時に実際に閲覧/応募した求人がレコメンド内にあった確率(以降Recall@10)をメインとして扱いました。

仮説を立てて検証する

Notionに既存のモデルから変更して改善しそうな要素を仮説ごとに表の要素を作りつつ、実装はGitHub issue, branchを仮説ごとに切って、Git Worktreeを使用し同時並行でモデルの実装を変更して仮説検証を行いました。ここでは、実際に立てた仮説とその結果の例を複数挙げます。

まず、入力特徴量を変える仮説を試しました。傾向として正答率が上がりそうな要素を特徴量として追加することを考えていて、そこで目をつけたのが、よく目にする要素である職場の雰囲気などの選択式の自然言語の特徴です。正例と負例の一致率を比較し、差が大きかった職場属性4項目を学習テキストへ追加しました。追加して学習を回した結果、評価指標が改善したため採用しました。

次に、改めて正例データの作り方を見直しました。今まではtimestampを必須にしていたため、timestampが記録されない求人のほとんどが学習ペアから除外されていることがわかりました。学習データの作成自体はtimestampに依存せずに作れるため、正例を再生成し、既存モデルと比較しました。その結果、評価指標が改善しました。

さらに、Hard Negativeとして実際にレコメンドされたが候補として選ばれなかったものを負例に採用することで、より難しい問題に対処できる解釈能力を得るのではという仮説のうえで負例を作ってみました。しかし、そもそもこの条件に合う負例を作れるデータの件数が極端に少ないため、有意な改善にならなかったため不採用としました。

結果としては、Recall@10を約42%向上させることができました。

2. 正常にレコメンドができない問題の原因究明と改善

バイトルのレコメンドの一部は前章のモデルで求人をベクトル化し、それをベクトル検索する仕組みで動いています。この仕組みで、検索にヒットするのにその求人実体のDataObjectは存在しないというエラーが発生する問題がありました。

モデルの学習を待っている間のタスクとして、今回はこのバグチケットに対処することにしました。

実験環境での調査

まず、どのような時にこのベクトル検索がヒットするのに実体のデータが存在しない瞬間が存在するのかを、プロダクション環境のログだけでなく、実際の求人数と同等のデータを実験環境で用意して調査してみました。

前提として、ベクトル検索にはVector Search 2.0を使用しています。この時のデータの構造は今回求人実体と呼んでいる求人のデータ本体が入っているDataObjectがあり、それに紐づく形でベクトルが保存されるANN Indexというものが存在する仕組みで動いています(以降実体の

DataObject, ANN Index)。紐づいているとはいえ独立した二つの要素なので、片方が存在しない可能性はありそうです。

プロダクション環境では求人の更新をDELETE → 更新してCREATEする形でバッチ処理を行っており、この瞬間に不整合が起こらないかを確認しました。結果としては、DELETEの処理が走る際に、数十秒ほど実体のDataObjectが存在しないがANN Indexが存在する瞬間を観測しました。

デプロイ環境の実ログを見ると、同一求人で、時間を置いて複数回該当エラーが発生しているためANN Indexだけが永続的に残っている可能性も高そうです。

対処方法

調査の結果を踏まえて、

  • 永続的にANN Indexのみが取り残される可能性があること
  • 一時的にANN Indexのみになる瞬間が存在すること

の二つに対して対策を打つことにしました。

前者に対しては、

レコメンド時に今回のエラーを検知した場合、その場でレコメンド全体を止めるのではなく、対象IDをPub/Subへ送る方式にしました。日次のDataObject同期後に求人の更新がされないことが保証されるので、このタイミングで一度実体を確認して実体が戻っていれば終了、ANNだけが残っていれば同じIDを一度作成してから削除します。一時的な不整合と、残り続けるIndexを後段で切り分けて安全に削除する設計を取ります。

後者に対しては、いったんGoogleにそもそもこの現象が存在するのかを問い合わせることにしました。

結果としては、

1. BatchDeleteDataObjectsが成功した場合、DataObject本体と、そのDataObjectに対応するすべてのCollection Index entryが同時に削除されることは保証されていますか。

同時削除は保証されておりません。Vector Search 2.0 のアーキテクチャでは、データ本体(Spanner データベースへの保存)に対する操作は「強整合性(Strong Consistency)」を持ちますが、検索インデックスの更新は「結果整合性(Eventual Consistency)」となります。そのため、BatchDeleteDataObjects によるデータ本体の削除は即座に完了しますが、インデックスからの削除処理は非同期で実行されます。

の通り自分の仮説通りの現象が公式から認められました。仕様通りらしいです、こちらで対処しましょう......

2. Collection Indexへの削除反映が一時的に遅延するだけでなく、内部処理の失敗などによって、Index entryが削除されないまま残り続ける可能性はありますか。

通常運用において、削除されないまま残り続けることはございません。
データ本体が削除される際、インデックス更新用のイベントはデータベース内のトランザクションで保護されたキューに確実に書き込まれます。バックエンドサービスがこのキューを読み取ってインデックスへ非同期に削除リクエストを送信するため、一時的なエラーが発生した場合でも自動的にリトライされ、最終的には必ず削除が反映されます(通常は検証で確認されたように数秒〜数十秒程度で正常化します)。

の通り、削除されずに永続的に残り続けることはないそうです、なんでだ......

そのほか、モデル改善用の学習パイプラインの比較検証がしやすいようなリファクタリングも同時に行っていました。

目標を踏まえてよかった点・改善したい点

よかった点

大規模モデルの改善に対しても、自分で考えて仮説を回して、最終的に結果を出すことができたのがよかったかなと思っています。また、実験管理と報告用の媒体の情報が完全に独立してしまった点はありつつも、やることを整理して常に何をすればよいかが明確なまま進められたのはよかったと思います。

合わせて、モデルの待ち時間にモデルの学習パイプラインのリファクタリングを行ったりバグチケットを貰ったりして並列で作業をすることもこれだけの成果を出すのに寄与したことかなと思っています。

改善したい点

自律してタスクを進めることとトレードオフにはなりますが、自分の中で中途半端な理解のまま作業をしすぎていた点があったため、報告のタイミングを手戻りが大きくなりそうなタイミングで自分の認識だけで進める際に、認識合わせの時間を作るなどして手戻りを防ぐべきだったなと感じています。

また、情報の整理として全体的に自分がどう考えたかがベースになっていて、それをそのまま報告に使うことで相手に応じた情報の粒度・方向性の調整があまりできていなかったように感じています。そこで、自分の作業に対してある程度構造化した理解を持ったうえで報告や質問を送れるようにしたいなと思いました。

自分の知見としてどこが方針の認識合わせをするべきタイミングかを考えないまま本実装を始めるのをやめよう......

まとめ

2週間の期間とは思えないほどの濃い経験が得られたと思います!

かなり結果に対しては手応えがありつつも、勤務するうえでの改善点が見つかったことが今後の自分のエンジニアリングに対して、本当によい刺激になりました。

エンジニアリングでの知見は今回のインターンに限らず他のインターンでも学べると思いますが、実務でどう考えて実装や報告をするべきかという観点も短い期間で学べるインターンですので、機会があればぜひ参加してみてください。

執筆者

インターンシップ

インターンシップ

インターンシップ参加者による記事一覧です。