paint-brush
スクロールをやめて構築を始めましょう: 独自の AI 映画レコメンダーを作成しましょう@superlinked
新しい歴史

スクロールをやめて構築を始めましょう: 独自の AI 映画レコメンダーを作成しましょう

Superlinked11m2025/03/06
Read on Terminal Reader

長すぎる; 読むには

次に観たい映画を正確に予測する、カスタムの AI 駆動型推奨システムを構築する方法を学びます。このチュートリアルでは、ベクター データベースを使用して映画推奨システムを作成するプロセスについて説明します。最新の AI 推奨エンジンの仕組みを学び、Superlinked を使用して独自のシステムを構築する実践的な経験を積むことができます。
featured image - スクロールをやめて構築を始めましょう: 独自の AI 映画レコメンダーを作成しましょう
Superlinked HackerNoon profile picture
0-item
1-item

「俺はB級映画が見たいって言ったじゃないか!」

無限スクロールの終焉(そして何を見るかという議論…)

次に何を見たらよいのかわからず、Netflix を延々とスクロールし続けることにうんざりしていませんか? 次に観たい映画を正確に予測する独自のカスタム AI 駆動型推奨システムを構築できたらどうでしょうか?


このチュートリアルでは、ベクター データベース (VectorDB)を使用して映画推奨システムを作成するプロセスについて説明します。最新の AI 推奨エンジンの仕組みを学び、 Superlinkedを使用して独自のシステムを構築する実践的な経験を積むことができます。


(コードに直接ジャンプしたいですか?こちらのGitHub のリポジトリをご覧ください。独自のユースケースでレコメンデーション システムを試す準備はできましたか?こちらのデモを入手してください。)

おすすめしてみましょう!

この記事では、このノートブックに沿って進めていきます。また、 Colab を使用してブラウザから直接コードを実行することもできます。


Netflix の推奨アルゴリズムは、膨大な選択肢 (2023 年には約 16,000 本の映画と TV 番組) と、ユーザーに番組を提案しなければならないスピードを考えると、関連性の高いコンテンツを提案するのに非常に優れています。Netflix はどのようにしてそれを行っているのでしょうか。一言で言えば、セマンティック検索です


セマンティック検索は、ユーザーのクエリや映画/テレビ番組の説明の背後にある意味とコンテキスト (属性と消費パターンの両方) を理解するため、従来のキーワードベースのアプローチよりもクエリと推奨事項のパーソナライズが向上します。


しかし、セマンティック検索にはいくつかの課題があります。その中でも最も重要なのは、1) 正確な検索結果の確保、2) 解釈可能性、3) スケーラビリティです。これらは、成功するコンテンツ推奨戦略が対処しなければならない課題です。Superlinked のライブラリを使用すると、これらの困難を克服できます。


この記事では、 Superlinked ライブラリを使用して独自のセマンティック検索を設定し、好みに基づいて関連する映画のリストを生成する方法を説明します。

セマンティック検索 - 課題

セマンティック検索はベクトル検索において多くの価値をもたらしますが、開発者にとってベクトル埋め込みに関する 3 つの大きな課題をもたらします。

  • 品質と関連性: 埋め込みがデータの意味を正確に捉えていることを保証するには、埋め込み手法、トレーニング データ、ハイパーパラメータを慎重に選択する必要があります。埋め込みの品質が低いと、検索結果が不正確になり、関連性のない推奨事項が表示される可能性があります。


  • 解釈可能性: 高次元ベクトル空間は複雑すぎて、簡単に理解することはできません。その中にエンコードされた関係性と類似性を理解するには、データ サイエンティストはそれらを視覚化して分析する方法を開発する必要があります。


  • スケーラビリティ: 特に大規模なデータセットでの高次元の埋め込みの管理と処理は、計算リソースに負担をかけ、レイテンシを増加させる可能性があります。運用環境でのスケーラビリティとリアルタイム パフォーマンスを確保するには、インデックス作成、検索、類似性計算の効率的な方法が不可欠です。


Superlinked ライブラリを使用すると、これらの課題に対処できます。以下では、特定の映画に関する情報から始めて、この情報をマルチモーダル ベクトルとして埋め込み、すべての映画の検索可能なベクトル インデックスを構築し、クエリの重みを使用して結果を微調整し、適切な映画の推奨に到達するコンテンツ レコメンダー (特に映画用) を構築します。それでは始めましょう。

Superlinked で高速かつ信頼性の高い検索エクスペリエンスを実現

以下では、Superlinked ライブラリの次の要素を使用して、Netflix 映画データセットでセマンティック検索を実行します。

  • 新しさスペース - データの鮮度 (最新性と関連性) を理解し、新しい映画を識別します。
  • TextSimilarity スペース - 説明、タイトル、ジャンルなど、映画に関するさまざまなメタデータを解釈します。
  • クエリ時間の重み - クエリを実行するときにデータ内で最も重要なものを選択できるため、データセット全体を再埋め込み、後処理を実行したり、カスタムの再ランク付けモデルを採用したりすることなく最適化できます (つまり、レイテンシが短縮されます)。

Netflix データセットとその活用方法

映画をうまく推薦することが難しいのは、選択肢が非常に多い(2023 年には 9000 タイトル以上)ためであり、ユーザーはオンデマンドですぐに推薦を求めています。データ駆動型のアプローチで、見たいものを探してみましょう。映画のデータセットでは、次のことがわかっています。

  • 説明
  • ジャンル
  • タイトル
  • リリース年


これらの入力を埋め込み、埋め込みの上にベクトル インデックスをまとめることで、意味的に検索できる空間を作成できます。


インデックス付きベクトル空間を取得したら、次の操作を行います。

  • まず、アイデア(心温まるロマンチックコメディ)でフィルタリングした映画を閲覧します
  • 次に、結果を微調整し、特定の入力フィールドでの一致に重点を置きます(つまり、重み付け)。
  • 次に、説明、ジャンル、タイトルをそれぞれ異なる検索語で検索します。
  • そして、近いけれども完全に一致しない映画を見つけたら、その映画を参考にして検索します

インストールとデータセットの準備

最初のステップは、ライブラリをインストールし、必要なクラスをインポートすることです。


(注: 以下、これをgoogle colabで実行している場合は、 alt.renderers.enable(“mimetype”)alt.renderers.enable('colab')に変更します。 githubで実行している場合は、 “mimetype” をそのままにしておきます。)


 %pip install superlinked==5.3.0 from datetime import timedelta, datetime import altair as alt import os import pandas as pd from superlinked.evaluation.charts.recency_plotter import RecencyPlotter from superlinked.framework.common.dag.context import CONTEXT_COMMON, CONTEXT_COMMON_NOW from superlinked.framework.common.dag.period_time import PeriodTime from superlinked.framework.common.schema.schema import schema from superlinked.framework.common.schema.schema_object import String, Timestamp from superlinked.framework.common.schema.id_schema_object import IdField from superlinked.framework.common.parser.dataframe_parser import DataFrameParser from superlinked.framework.dsl.executor.in_memory.in_memory_executor import ( InMemoryExecutor, InMemoryApp, ) from superlinked.framework.dsl.index.index import Index from superlinked.framework.dsl.query.param import Param from superlinked.framework.dsl.query.query import Query from superlinked.framework.dsl.query.result import Result from superlinked.framework.dsl.source.in_memory_source import InMemorySource from superlinked.framework.dsl.space.text_similarity_space import TextSimilaritySpace from superlinked.framework.dsl.space.recency_space import RecencySpace alt.renderers.enable("mimetype") # NOTE: to render altair plots in colab, change 'mimetype' to 'colab' alt.data_transformers.disable_max_rows() pd.set_option("display.max_colwidth", 190)


また、データセットを準備する必要があります。時間定数を定義し、データの URL の場所を設定し、データ ストア ディクショナリを作成し、CSV を pandas DataFrame に読み込み、データフレームとデータをクリーンアップして適切に検索できるようにし、簡単な検証と概要を実行します。(詳細については、 セル 3 と 4 を参照してください。)


データセットが準備されたので、Superlinked ライブラリを使用して取得を最適化できます。

ベクトル検索のインデックスの構築

Superlinked のライブラリには、インデックスの構築と検索の管理に使用するコア ビルディング ブロックのセットが含まれています。これらのビルディング ブロックの詳細については、 ここ をご覧ください。


まず、システムにデータを伝えるためにスキーマを定義する必要があります。

 # accommodate our inputs in a typed schema @schema class MovieSchema: description: String title: String release_timestamp: Timestamp genres: String id: IdField movie = MovieSchema()


次に、スペースを使用して、埋め込み時にデータの各部分をどのように処理するかを指定します。どのスペースが使用されるかは、データ型によって異なります。各スペースは、可能な限り最高品質の検索結果を返すようにデータを埋め込むように最適化されます。


スペース定義では、データ内の意味関係を反映するために入力をどのように埋め込むべきかを説明します。


 # textual fields are embedded using a sentence-transformers model description_space = TextSimilaritySpace( text=movie.description, model="sentence-transformers/paraphrase-MiniLM-L3-v2" ) title_space = TextSimilaritySpace( text=movie.title, model="sentence-transformers/paraphrase-MiniLM-L3-v2" ) genre_space = TextSimilaritySpace( text=movie.genres, model="sentence-transformers/paraphrase-MiniLM-L3-v2" ) # release date are encoded using our recency space # periodtimes aim to reflect notable breaks in our scores recency_space = RecencySpace( timestamp=movie.release_timestamp, period_time_list=[ PeriodTime(timedelta(days=4 * YEAR_IN_DAYS)), PeriodTime(timedelta(days=10 * YEAR_IN_DAYS)), PeriodTime(timedelta(days=40 * YEAR_IN_DAYS)), ], negative_filter=-0.25, ) movie_index = Index(spaces=[description_space, title_space, genre_space, recency_space])


スペースを設定してインデックスを作成したら、ライブラリのソース部分とエグゼキュータ部分を使用してクエリを設定します。 ノートブックのセル 10 ~ 13 を参照してください。


クエリの準備ができたので、クエリの実行と重みの調整による検索の最適化に進みましょう。

新しさを理解し、スーパーリンクでそれを使用する方法

新しさのスペースを使用すると、データセットから古いリリースまたは新しいリリースを優先的に取得することで、クエリの結果を変更できます。期間として 4 年、10 年、40 年を使用して、タイトルが多い年に焦点を当てられるようにします ( セル 5を参照)。


4 年、10 年、40 年でスコアが途切れていることに注目してください。40 年以上前のタイトルにはnegative_filterスコアが付けられます。

期間別の最新スコア

異なるクエリ時間の重みを使用して検索結果を確認し最適化する

ノートブックに結果を表示するための簡単なユーティリティ関数を定義しましょう。


 def present_result( result: Result, cols_to_keep: list[str] = ["description", "title", "genres", "release_year", "id"], ) -> pd.DataFrame: # parse result to dataframe df: pd.DataFrame = result.to_pandas() # transform timestamp back to release year df["release_year"] = [ datetime.fromtimestamp(timestamp).year for timestamp in df["release_timestamp"] ] return df[cols_to_keep]


シンプルおよび高度なクエリ

Superlinked ライブラリを使用すると、さまざまな種類のクエリを実行できます。ここでは 2 つを定義します。両方のクエリ タイプ (シンプルとアドバンス) では、好みに応じて個々のスペース (説明、タイトル、ジャンル、もちろん最新) に重みを付けることができます。これらの違いは、シンプル クエリでは、1 つのクエリ テキストを設定すると、説明、タイトル、ジャンルのスペースで同様の結果が表示されることです。


高度なクエリを使用すると、よりきめ細かな制御が可能になります。必要に応じて、説明、タイトル、ジャンルの各スペースに異なるクエリ テキストを入力できます。クエリ コードは次のとおりです。


 query_text_param = Param("query_text") simple_query = ( Query( movie_index, weights={ description_space: Param("description_weight"), title_space: Param("title_weight"), genre_space: Param("genre_weight"), recency_space: Param("recency_weight"), }, ) .find(movie) .similar(description_space.text, query_text_param) .similar(title_space.text, query_text_param) .similar(genre_space.text, query_text_param) .limit(Param("limit")) ) advanced_query = ( Query( movie_index, weights={ description_space: Param("description_weight"), title_space: Param("title_weight"), genre_space: Param("genre_weight"), recency_space: Param("recency_weight"), }, ) .find(movie) .similar(description_space.text, Param("description_query_text")) .similar(title_space.text, Param("title_query_text")) .similar(genre_space.text, Param("genre_query_text")) .limit(Param("limit")) )


シンプルなクエリ

単純なクエリでは、クエリ テキストを設定し、自分にとっての重要度に応じて異なる重みを適用します。


 result: Result = app.query( simple_query, query_text="Heartfelt romantic comedy", description_weight=1, title_weight=1, genre_weight=1, recency_weight=0, limit=TOP_N, ) present_result(result) 


シンプルなクエリ結果 1

結果には、すでに見たことのあるタイトルがいくつか含まれています。この問題に対処するには、新しさに重み付けして、結果を最近のタイトルに偏らせます。重みは、合計が 1 になるように正規化されます (つまり、すべての重みは常に合計が 1 になるように調整されます)。そのため、重みの設定方法について心配する必要はありません。


 result: Result = app.query( simple_query, query_text="Heartfelt romantic comedy", description_weight=1, title_weight=1, genre_weight=1, recency_weight=3, limit=TOP_N, ) present_result(result) 


シンプルなクエリ結果 1

私の結果(上記)はすべて 2021 年以降のものになります。


シンプルなクエリを使用すると、特定のスペース (説明、タイトル、ジャンル、または最新性) に重みを付けて、結果を返すときにそのスペースをより重視することができます。これを試してみましょう。以下では、ジャンルに重みを付け、タイトルの重みを下げます。クエリ テキストは基本的に、追加のコンテキストがあるジャンルだけです。最新性はそのままにしておきます。これは、結果が最近の映画に偏っているようにするためです。


 result = app.query( simple_query, query_text="Heartfelt romantic comedy", description_weight=1, title_weight=0.1, genre_weight=2, recency_weight=1, limit=TOP_N, ) present_result(result)


このクエリは、リリース年を少し後ろにずらして、よりジャンルを重視した結果を表示します (以下)。


シンプルなクエリ結果 3

高度なクエリ

高度なクエリでは、さらにきめ細かな制御が可能です。新しさの制御はそのままに、説明、タイトル、ジャンルの検索テキストを指定し、それぞれに好みに応じて特定の重みを割り当てることもできます(以下およびセル 19 ~ 21 )。

 result = app.query( advanced_query, description_query_text="Heartfelt lovely romantic comedy for a cold autumn evening.", title_query_text="love", genre_query_text="drama comedy romantic", description_weight=0.2, title_weight=3, genre_weight=1, recency_weight=5, limit=TOP_N, ) present_result(result)


特定の映画を使って検索する

たとえば、前回の映画検索結果で、すでに見た映画が見つかり、似たような映画を見たいと思ったとします。たとえば、1954 年のロマンティック コメディー「ホワイト クリスマス」(id = tm16479) が好きだとします。この映画は、歌手とダンサーが集まってステージ ショーを行い、バーモント州の経営難の宿に客を呼び込むという内容です。advanced_query に、追加のwith_vector句 ( movie_idパラメータ付き) を追加することで、with_movie_query ではこの映画 (または好きな映画) を使用して検索できるようになり、個別のサブ検索クエリ テキストと重み付けを細かく制御できるようになります。


まず、movie_id パラメータを追加します。

 with_movie_query = advanced_query.with_vector(movie, Param("movie_id"))


そして、他のサブ検索クエリを空にするか、最も関連性の高いものに設定し、意味のある重み付けも設定できます。最初のクエリがホワイト クリスマスのステージ パフォーマンス/バンドの側面を反映した結果を返すとします ( セル 24 を参照)。ただし、私はもっと家族向けの映画を見たいと考えています。description_query_text を入力して、結果を希望の方向に偏らせることができます。

 result = app.query( with_movie_query, description_query_text="family", title_query_text="", genre_query_text="", description_weight=1, title_weight=0, genre_weight=0, recency_weight=0, description_query_weight=1, movie_id="tm16479", limit=TOP_N, ) present_result(result) 


高度なクエリ結果 1

しかし、結果を見ると、実はもっと気楽で面白いものを求めていることに気づきました。それに応じてクエリを調整してみましょう。


 Result = app.query( with_movie_query, description_query_text="", title_query_text="", genre_query_text="comedy", description_weight=1, title_weight=0, genre_weight=2, recency_weight=0, description_query_weight=1, movie_id="tm16479", limit=TOP_N, ) present_result(result) 


高度なクエリ結果 2

よし、結果はこちらの方が良い。このうちの 1 つを選ぶ。ポップコーンをつけて!

結論

Superlinked を使用すると、検索品質のテスト、反復、改善が簡単になります。上記では、Superlinked ライブラリを使用して、Netflix のようにベクトル空間でセマンティック検索を実行し、正確で関連性の高い映画の結果を返す方法について説明しました。また、適切な結果が得られるまで重みと検索用語を微調整して、結果を微調整する方法についても説明しました。


さあ、 ノートブックを自分で試してみて、何ができるか見てみましょう。

ぜひお試しください – コードとデモを入手してください!

  • 💾 コードを入手する:こちらのGitHub リポジトリで完全な実装を確認してくださいフォークして調整し、独自のものにしてください。


  • 🚀 実際に動作を確認: 実際のセットアップでこれがどのように機能するかを確認したいですか? 簡単なデモを予約して、 Superlinked が推奨事項をどのように強化できるかを確認してください。今すぐデモを入手してください!


推奨エンジンは、コンテンツを発見する方法を形作っています。映画、音楽、製品など、ベクター検索は未来です。そして今、独自のベクター検索を構築するためのツールが手に入りました。


著者:モル・カプロンツァイ