Upgrade to Pro — share decks privately, control downloads, hide ads and more …

スマートに「関連する記事」を表示する仕組みを作る:OpenAI Embedding API + Supabase + pgvectorを利用した類似度検索の実装

スマートに「関連する記事」を表示する仕組みを作る:OpenAI Embedding API + Supabase + pgvectorを利用した類似度検索の実装

2024-05-02 DS集会@VRChat

contradiction29

May 02, 2024
Tweet

More Decks by contradiction29

Other Decks in Programming

Transcript

  1. この世界はWebサイトであふれている • 例 ◦ 旦那デスノート (https://danna-shine.com/) ◦ ガールズちゃんねる (https://girlschannel.net/) ◦

    発⾔⼩町 (https://komachi.yomiuri.co.jp/) • どんなWebサイトであろうと、Webサイト管理者がやりたいことは本質的に 不変である。それは...
  2. Webサイト運営者はユーザーエンゲージメントを⾼めたい • 「ユーザーエンゲージメント」 ◦ ユーザーがどの程度「エンゲージ」しているかを表す ◦ 「エンゲージ」が具体的に何を指すかはサイトによって違う • 何で計測する? ◦

    サイトによって計測⽅法は様々 ▪ 1ユーザー当たりのページビュー数 ▪ ユーザーのサイト内滞在時間の平均値 ▪ 旦那デスノートの場合:「〇ねばイイね!」の数 • ほとんどのサイト運営者はユーザーエンゲージメントの上昇を⽬指している ◦ 広告収益を稼いだり、コンバージョン数を上げて利益を得るため ▪ コンバージョン数:「Webサイトで獲得する最終的な成果」のこと ▪ 商品購⼊数、有料プランの導⼊数、アフィリエイトの獲得数など ◦ (ほかに⽬的がある場合もある)
  3. いかにしてユーザーエンゲージメントを⾼めるか? • ⾃分がWebサイトを持っているとする。いかにしてユーザーエンゲージメン トを⾼めるか? ◦ ⽅法はいろいろありそう • ほかのサイトをヒントに考えてみる ◦ ピクシブ百科事典

    ◦ ニコニコ⼤百科 ◦ Wikipedia • 若かったあの頃、将来への不安を抱えながら読み漁ったサイトたちを思い出 してほしい • あなたを夢中にさせた機能は何?
  4. ⾃⼰紹介 • ⾃⼰紹介に興味がない⽅のために、 左半分は柴⽝の画像にしています • 関⼼領域 ◦ データエンジニアリング、データマネジメ ント(本業) ◦

    TypeScript, React, Remix, Supabaseなど • VRChatプレイ時間数 : 67.4時間 ◦ まだUser、伸び盛りがある ◦ はやくKnownになりたい https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Shiba_inu_taiki.jpg
  5. 実装⽅法の検討 タグを利⽤する⽅法 内容に基づく⽅法 実装内容 - 同じタグを持つ記事を関連す る記事として表⽰する - 記事⾃体をベクトル化して、ドッ ト積から類似度を算出する

    ⻑所 - 実装が簡単 - 内容に基づいて関連記事を出せる - タグがなくても関連記事を出せる 短所 - タグがない記事が多い - 同じタグが付いているから関 連しているとも⾔えない - 関連している記事でも同じタ グが付いていない場合が多い - 実装が⾯倒 - ちょっと⾦がかかる リニューアル前までは採⽤ • 短所が無視できない⼤きさ サイトリニューアルに伴い こちらを採⽤
  6. 全体的な流れの説明 1. 記事をEmbeddingしてベクトルを算出 ◦ embedding = 「似ているインプットを似ているベクトルとして返すブラックボック ス」 ◦ 似ている記事は似ているベクトルになる

    ◦ (これ以上は踏み込めない) 2. ベクトルをデータベースに格納 3. ドット積を算出し、類似した記事を出⼒ ◦ ドット積:ベクトル同⼠の積の出し⽅の⼀つ ◦ ドット積の値でソートすれば類似度順にソートしたことと等価になる 4. サーバーサイドで類似記事をフェッチし、クライアントサイドに渡す 5. クライアントサイドで表⽰する
  7. 技術選定 • embeddingの⼿段としてはOpenAI Embedding APIを採⽤ ◦ text-embedding-3-smallモデルを利⽤ ◦ 費⽤対効果に優れていたため ◦

    取り込み可能なトークン数が8192と⼤きい • ベクトルを格納するデータベースとしてはSupabaseを利⽤ ◦ ベクターDBとしての格納〜サーバーサイドでの類似記事のフェッチまで⼀貫してできるため ◦ 認証機能もこれで実装しており、既存の技術選定の拡張でできる • 補⾜:Supabaseについて ◦ Open-source alternative for firebase ◦ PostgreSQLだけでなく、認証‧認可の機能やedge functionsの機能もある ◦ いわゆるBackend as a Service
  8. 記事データをembeddingしてsupabaseに格納する • 処理の流れ ◦ 投稿フォーム側からpostId、投稿内容を受け取る ▪ postId : 投稿に対して⼀意に振られるID ▪

    投稿内容:HTML ◦ OpenAI Embedding APIに投稿内容を投⼊し、ベ クトルに変換 ◦ 変換したベクトルをPostgreSQL (on Supabase) に格納 • かなり簡単 ◦ 50⾏程度 https://github.com/sora32127/healthy-person-emulator-dotorg/blob/main/app/m odules/embedding.server.ts
  9. supabase内部で類似度検索関数を作成 • argument ◦ post_id : 類似している記事を探したい対象 ◦ match_threshold :

    類似度の閾値 ◦ match_count : 受け取りたい数 • return ◦ post_id : 類似記事のid ◦ post_title : 類似記事のタイトル ◦ similarity : 類似度
  10. 効果測定1:関連記事経由平均ページビュー数は1.85倍に増加 04/10 機能実装 計測ぶっ壊れゾーン • 「関連する記事」経由でのユーザー当たり平均 ページビュー数(PV数)で効果を計測する ◦ データ定義: ▪

    リファラー = https://healthy-person-emulator.or g/archives/* ▪ event_name = page_view ▪ PV数をuser_pseudo_idごとに集計 ▪ 集計後、⽇時で平均値を算出 • 平均PV数は1.85倍に上昇 ◦ 03/02 ~ 04/09平均 : 8.26 ◦ 04/21 ~ 04/29平均 : 15.32 • 注意: ◦ 計測ぶっ壊れゾーンは除外している ▪ 03/01, 04/11 ~ 04/20 ◦ 「次のページ」「前のページ」での遷移と 区別はできていない ◦ ⼤規模サイトリニューアルと同時に実装し ているため、因果関係は明確ではない
  11. 効果測定2 : 平均エンゲージメント時間は1.47倍に増加 • 「平均エンゲージメント時間」の定義 ◦ ユーザーがサイトを実際に閲覧していた時 間のこと ◦ タブを開いていない状態やバックグランド

    にある状態はエンゲージメント時間として カウントされない • 平均エンゲージメント時間は1.47倍に上昇 ◦ 03/02 ~ 04/09平均 : 191.4秒 ◦ 04/21 ~ 04/29平均 : 282.1秒 • 注意: ◦ 計測ぶっ壊れゾーンは除外している ◦ 関連記事経由平均PV数よりほかの要因が絡 んでいる可能性が⾼いので、参考程度
  12. 補⾜:コストについて • 前提 ◦ 1記事当たりの平均トークン数は642.8 ◦ サイト全体では5,808,792トークン ◦ text-embedding-3-smallのコストは $0.02/1M

    tokens ▪ https://openai.com/pricing • サイト全体のembeddingにかかった合計コスト ◦ 5.8 * $0.02 = $0.116 = 17.4円 ◦ $1 = 150円換算 ◦ 実際は記事の編集時にもembeddingを⾏うのでもう少し⾼くなる ◦ それでも安い