ナレッジグラフでスターウォーズファンに映画を推薦する
人間の持つ知識を形式的に表現する、知識表現の研究は古くからなされてきており、例えば一つの形としてWebシステムではよくつかわれるリレーショナルモデルなどがある。近年よく着目されているのがナレッジグラフであり、先端的な研究を超えて、実産業での活用事例(例えばGoogleのナレッジグラフサーチ)も多くみられるようになった。
本記事では、noteのレコメンドシステムも手がけている筆者がWikidataのエンドポイントを利用して、ナレッジグラフを探索し、スターウォーズファンにおすすめできそうな映画をリストアップしてみる。最終的にこんな感じのリストが得られる。スターウォーズファンのみなさまには、興味が惹かれるタイトルがあっただろうか?
ナイト ミュージアム2
インディ・ジョーンズ/クリスタル・スカルの王国
地獄の黙示録
チャーリーズ・エンジェルフルスロットル
ブレードランナー 2049
ジャッジ・ドレッド
ミッション:インポッシブル
エクスペンダブルズ3 ワールドミッション
今そこにある危機
キャプテン・フィリップス
光る眼
カウボーイ&エイリアン
今そこにある危機
バットマン ビギンズ
野性の呼び声
キングスマン
エアフォース・ワン
ジャッジ・ドレッド
もしも昨日が選べたら
リレーショナルモデルとナレッジグラフ
「人々はさまざまな映画館に通う」という知識をどのようにリレーショナルモデルでモデリングできるだろうか?人が通うのだから、「人」は登場人物として考えられ、「映画館」も考えることができる。お気に入りの映画館だけに通う人もいれば、毎回違う映画館に通う人もいるので、人と映画館の関係は1 対 1 ではなくN対Nになるだろう。以下に簡易的な実体関連図を示す。リレーショナルモデルではここからさらに深ぼって、実際には人々と映画館の関係を表す方法を考えたり(中間テーブル)、テーブルが持つべきプロパティを考えたりすることが多い。この過程で不要なプロパティを増やしてしまったり、問い合わせにおいて非効率なテーブル構造を選択してしまったりすることがよくある。
ナレッジグラフ(ここではRDFを指すこととする)では、リレーショナルモデルとは異なり、主語・述語・目的語からなる「トリプル構造」で知識を表現する。例えば、「ジョンは1920年築の映画館に行く」という知識があった時ナレッジグラフでは以下のように表される。ナレッジグラフでは実世界に存在するオブジェクトの間の関係やオブジェクトが持つ性質を直感的に表現することができる。
主語と目的語は、別途抽象と実体の関係(instance_of)を持っており、例えば「ジョン」の抽象は「人間」、「映画館」の抽象は「建物」のようなことを表現できる。オブジェクト指向モデリングにおける、クラスとインスタンスのようなものだと思うと考えやすい。また、RDFにおいては、主語と目的語はURLのように唯一固有のIDをもち、識別可能である。
これで知識が表現できたとして、どう問い合わせたり、知識の一部を表現したりするのかというと、SPARQLというSQLのようなSyntaxの問い合わせ言語がW3Cで定義されており、リレーショナルデータベースユーザにとっては比較的直感的な形式で、ナレッジグラフに問い合わせができる。
最先端の研究は?「生成AI時代のナレッジグラフ」セッション
そんなナレッジグラフなのだが、最近の研究や活用事例はどうなっているのだろうか。筆者は先日JSAI2024に参加してきたのでナレッジグラフのセッションを聴講してきた。近年ではLLMに代表される生成AIのような、新たな知識の表現形式が台頭してきている。ナレッジグラフを研究・活用する方々が、各々のナレッジグラフと生成AIに対する見解と、研究と活用事例を様々な観点から解説していた。
生成AIとナレッジグラフの関わりとしては、2系統あって、生成AIからナレッジグラフにアプローチするパターンと、ナレッジグラフから生成AIにアプローチするパターンがあるらしい。前者で言えば、今までの手法だと自然言語で書かれたテキストからナレッジグラフを構築するのは骨の折れる作業であり困難だったのを、LLMのようにテキスト表現を構造化されたドキュメントにうまく変換できるツールを使うと、継続的にナレッジグラフをメンテナンスできるようになるらしい。後者では、例えばRAGのようなユースケースが考えられ、生成AIに対する問い合わせとナレッジグラフに対するクエリを組み合わせて、回答性能を上げるケースがあるようだ。
スターウォーズが好きな人におすすめの映画は?
あなたが大のスターウォーズファンだとして、次に他にスターウォーズっぽい映画を心の底では探したいと思っているとしよう。ナレッジグラフに問い合わせてスターウォーズ好きなあなたにおすすめの映画を探してみよう。実は既に使えるようになっているナレッジグラフのサービスがあって、Wikidataというサービスで、Wikipediaに存在する文書とその中の大量の項目、項目間の関係をRDFでナレッジグラフ化したサービスになっている。Wikidataのこのチュートリアルがわかりやすい。
SPARQLの文法を確認したくないあなたにはGUIのクエリビルダーがあり、項目を探す、絞り込んで検索を実行するという一連の操作が簡単に実行できるようになっている。プロパティの入力フィールドに項目を入れると自動的に「監督」のようにサジェストされるがこれは先述の通り、(RDFを使っている)Wikidataにおいて「監督」の概念は一意に決まっており(URIも存在する)、「ジョージ・ルーカス」さんであっても、他に同姓同名のジョージルーカスさんがいたとしても、スターウォーズをつくったジョージルーカスさんとは識別できるようになっている。
自然言語でのSPAQRLクエリのモデリング
ナレッジグラフに対するSPAQRLでの問い合わせのよい点(SQL&リレーショナルモデルと比較して)として比較的自然言語に近い形式でクエリを記述できることにある。対象は「映画」であるので、その「映画」が持つべき性質や動作を列挙すれば良い。例えば、スターウォーズは「SF映画」なのでとりあえずSFでないといけないとか、「ジャンル」はスターウォーズと同じじゃないといけないとかである。スターウォーズは言わずもがなSF超大作なので「興行収入」が高い、CGや特撮にお金がかかってそうな映画のほうがスターウォーズっぽい感じがする。
それらを列挙してみると、例えば以下のようになる。
映画は
sfである
興行収入が1億円以上
上演時間
冒険物である
キャラ数が多い
宇宙船がでてくる
なお以降のSPARQLクエリはほぼChatGPT 4o モデルと一緒に作った。
興行収入がN億円以上で30年以内に公開された映画を推薦する
まずはシンプルそうなクエリから始めよう。以下はSPARQLで「興行収入がN億円以上で30年以内に公開された映画を列挙する」クエリである。wdt:~ という表現は見慣れないが、これは先述したような、Wikidataにおける「監督」などの概念のIDを示していて、film変数が持つべき性質(述語、目的語)を記述している。
SELECT ?film ?filmLabel
WHERE {
?film wdt:P31 wd:Q11424; # 映画であること (wdt:P31: instance of, wd:Q11424: film)
wdt:P2142 ?boxOffice; # 興行収入 (wdt:P2142: box office)
wdt:P577 ?releaseDate. # 公開日 (wdt:P577: publication date)
FILTER(?boxOffice > 1000000) # 興行収入が1,000,000ドル以上であること
FILTER(YEAR(?releaseDate) >= YEAR(NOW()) - 30) # 公開日が過去30年以内であること
SERVICE wikibase:label { # ラベルを取得する
bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en".
}
}
GROUP BY ?film ?filmLabel
ORDER BY DESC(?releaseDate)
LIMIT 300 # 結果を300件に制限
このクエリをWikidata Query Serviceに投げると、以下のような結果が得られる。オスカーとルシンダはおもしろそうだが、ロマンスっぽい映画なのでちょっとジャンルが違うような気がする。
もっとスターウォーズテイストな映画がよい
スターウォーズっぽい映画といった時に例えばジャンルが同じだったら大体スターウォーズっぽいような気がする。SPRQLで書くと以下のようになる。
概ねスターウォーズっぽい冒険的な要素を持っていそうなタイトルが得られた。「神様の言うとおり」、「タクシー運転手約束は海を超えて」の二つのタイトルが気になるが調べてみるとどちらもアクション映画らしい。
SELECT ?film ?filmLabel
WHERE {
# スターウォーズエピソード4のジャンルを取得
wd:Q17738 wdt:P136 ?starWarsGenre.
# 映画の情報を取得
?film wdt:P31 wd:Q11424; # 映画であること (wdt:P31: instance of, wd:Q11424: film)
wdt:P2142 ?boxOffice; # 興行収入 (wdt:P2142: box office)
wdt:P577 ?releaseDate; # 公開日 (wdt:P577: publication date)
wdt:P136 ?genre; # ジャンル (wdt:P136: genre)
wdt:P2047 ?duration. # 上映時間 (wdt:P2047: duration)
# ジャンルがスターウォーズエピソード4のジャンルのいずれかに一致する
FILTER(?genre = ?starWarsGenre)
# 興行収入が1億円以上であること
FILTER(?boxOffice > 1000000)
# 公開日が過去30年以内であること
FILTER(YEAR(?releaseDate) >= YEAR(NOW()) - 30)
# ラベルを取得する
SERVICE wikibase:label {
bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en".
}
}
GROUP BY ?film ?filmLabel
LIMIT 300
もっとスターウォーズファンなあなたに
スターウォーズのキャストが出ている映画じゃないとみないという、奇特な方もいるだろう。ナレッジグラフではリレーショナルモデルで問い合わせが難しいような、「ある映画に出演していたキャストが出ている映画、かつそのシリーズは除く」のようなちょっと面倒くさそうなクエリも比較的直感的にかける。ローグワンはスターウォーズシリーズじゃないらしいが、こちらは概念的にはスターウォーズの外伝に分類されるらしい。
SELECT DISTINCT ?film ?filmLabel ?actor ?actorLabel
WHERE {
# スターウォーズエピソード4に出演していたキャストを取得
wd:Q17738 wdt:P161 ?actor.
# 映画の情報を取得
?film wdt:P31 wd:Q11424; # 映画であること (wdt:P31: instance of, wd:Q11424: film)
wdt:P2142 ?boxOffice; # 興行収入 (wdt:P2142: box office)
wdt:P577 ?releaseDate; # 公開日 (wdt:P577: publication date)
wdt:P136 ?genre; # ジャンル (wdt:P136: genre)
wdt:P2047 ?duration; # 上映時間 (wdt:P2047: duration)
wdt:P161 ?actor. # キャスト (wdt:P161: cast member)
# 映画がスターウォーズシリーズに属さないことを確認する
FILTER NOT EXISTS {
?film wdt:P179 wd:Q22092344. # スターウォーズシリーズ (wd:Q22092344)
}
# ジャンルがスターウォーズエピソード4のジャンルのいずれかに一致する
wd:Q17738 wdt:P136 ?starWarsGenre.
FILTER(?genre = ?starWarsGenre)
FILTER(?boxOffice > 1000000)
# 公開日が過去30年以内であること
FILTER(YEAR(?releaseDate) >= YEAR(NOW()) - 30)
# ラベルを取得する
SERVICE wikibase:label {
bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en".
}
}
GROUP BY ?film ?filmLabel ?actor ?actorLabel
LIMIT 500
まとめ
ナレッジグラフ(e.g, RDF)とは知識表現の形式でありSPARQLクエリを書けばちょっと賢い問い合わせを実行できる(Wikidataなど)
ChatGPT(などのLLM)は意外とWikidataのリソースを記憶しており、リソースIDを調べなくても、SPARQLクエリをほぼ自動生成することができた
ナレッジグラフを活用するとちょっと賢いコンテンツ推薦システムができるかもしれない
もっとnoteのエンジニアの記事が読みたい方はこちらからどうぞ!