X運用 · 公開アルゴリズム · 第2回

Phoenixとは?フォロー外の投稿が届く仕組み

フォローしていない人の投稿が、なぜあなたのおすすめに現れるのか。キーワードでもトレンドでもなく、「あなたの直近1,024回の行動」が検索キーになっていました。

Xの公開アルゴリズム(xai-org/x-algorithm)を読み解くシリーズ、第2回です。

前回のおさらい: おすすめタイムラインは「候補集め → ランキング → フィルタ」の3段階で作られます。第1回のThunderは、フォロー中の投稿を新着順で集めるだけの「新着回収係」でした。

今回はその逆側、フォローしていない人の投稿を拾ってくる仕組みの主役、Phoenix retrieval(フェニックス・リトリーバル)です。今回もコードで確認できた事実だけを書きます。

Phoenixの役割: フォロー外から最大1,000件

あなたがおすすめを読み込むと、Thunderと並行してPhoenix retrievalも動き、フォロー外の候補を最大1,000件(デフォルト値)取ってきます。

最大取得件数1,000件の設定
最大取得件数の設定 (home-mixer/params/param.rs L4-9)
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/params/param.rs#L4-L9

ここで大事なのは、Phoenixが「何を手がかりに」探すかです。検索窓のような「キーワードの文字一致」ではありません。トレンドでもフォロー関係でもありません。

使うのは、あなたの行動履歴そのものです。投稿の中身ももちろん見られますが、それは「同じ単語を含むか」ではなく「意味・内容が近いか」として扱われます(その仕組みが後述のセマンティックIDです)。

入力は「あなたの直近1,024アクション」

Phoenixに渡されるのは、あなたが最近取った行動の並び、最大1,024件です。

履歴の最大長1,024件の設定
行動履歴の最大長の設定 (home-mixer/params/param.rs L964-969)
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/params/param.rs#L964-L969

ここでの「行動」は投稿のことではなく、あなたが起こしたアクション1つ1つです。いいね、リプライ、リポスト、引用、ブックマーク、共有(DMでの共有・リンクのコピーはそれぞれ別の行動として記録)、動画視聴、写真の拡大など。1行動につき「行動の種類+対象の投稿」が1エントリとして記録されます。

つまりモデルから見た「あなた」の中心は、フォローリストでもフォロワー数でもなく、「直近、何にどう反応してきたか」の記録です。正確には、これに加えて国・言語・年齢・性別・インストールしているアプリといった粗いプロフィール情報も、1つのまとまりとして一緒に入力されます(モデルの本番設定ファイルで確認)。一方で、ユーザーIDそのものに紐づく学習済みの値は持ちません。

細かい話をすると、取得の上限が1,024件で、モデル内部では「履歴1,023件+プロフィール情報のまとまり1個」という構成です。

行動履歴を取得するコード
行動履歴を取得する処理 (home-mixer/query_hydrators/retrieval_sequence_query_hydrator.rs L45-59)
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/query_hydrators/retrieval_sequence_query_hydrator.rs#L45-L59

なお、この履歴の取得時には「DENSE_WITH_SHORT_DWELL」という集計タイプが指定されています(公開デフォルト値)。名前から「短い滞在まで含めた密な行動列」と読み取れますが、整形の詳細は集計サービス側(非公開)にあります。

仕組み: 2つの塔で「あなた」と「投稿」をすれ違い検索

Phoenix retrievalの本体は「Two-Tower(2つの塔)」と呼ばれる型のAIモデルです。名前は難しそうですが、やっていることは3行で言えます。

1. すべての投稿は、内容ごとに巨大な「盤」の上に並べられている(事前に)
2. あなたも、行動履歴をもとに同じ盤のどこかに置かれる
3. おすすめ候補 = あなたが置かれた場所の近くにある投稿

Two-Tower(二つの塔)の図解: 盤にコマを置く2本の手
Two-Towerの図解: 「塔」の正体は、盤にコマを置く2本の手

図の通り、チェスに例えるのが一番分かりやすいです。すべての投稿は盤の上にコマとして置かれていて、あなた自身もコマの1つとして盤に置かれます。そして「塔」とは盤の上のコマのことではなく、コマを盤に置いている2本の手のことです。

塔② 投稿を置く係 世の中の投稿を1つずつ読んで、内容が近いものどうしが近くになるように盤へ置いていく手。置き場所を決める目印が「セマンティックID」(投稿の内容から計算した指紋のようなコード)+投稿者の情報です。この配置作業は事前にまとめて済ませてあります。
塔① あなたを置く係 あなたがタイムラインを読み込んだ瞬間、あなたの行動履歴(と粗いプロフィール情報)を見て、あなたのコマを「好みに一番近い場所」へ置く手。ここで重要なのは、公式文書に「ユーザー固有のID埋め込みは持たない」と明記されていること。アカウント固有の「格」のような学習値はなく、置き場所は行動の中身が主に決めます。

そして、あなたのコマの周り(図の点線の円の中)から投稿コマを上位1,000件拾う ― これが「フォロー外の候補集め」の正体です。

※図では2次元の盤に描いていますが、実物の盤は1,024次元(似ているかどうかを測る方向が1,024本ある空間。モデル設定で確認)です。この話は次回に。

この2本の手は、過去に「実際にいいねが起きた読者と投稿のペア」を大量に見て訓練されています。反応が起きたペアほど盤上で近くに置かれるよう鍛えられているので、「あなたのコマの近所」=「あなたが反応しそうな投稿が集まる場所」になっているわけです。

もう1つ大事な点があります。置き場所を決める材料は「内容」だけではありません。投稿コマには「誰が書いたか」の情報も入っており、あなたの行動履歴にも「誰の投稿に反応してきたか」が刻まれています。学習の結果、あなたがよく反応してきた投稿者の投稿は、内容が多少ズレていてもあなたの近くに置かれやすくなります。盤上の距離には「内容の近さ」と「エンゲージメントを通じた投稿者との結びつき」の両方が折り込まれている、ということです。これは「よく反応する相手を優遇する」という明示的なルールがあるのではなく、学習を通じてそうなりうる、という効果です。

要するにPhoenixは、「あなたがこれまで反応してきたものと、内容的に近い場所にある投稿」を、フォロー関係を一切飛び越えて直接拾ってくる検索エンジンです。「キーワードの文字一致」もフォロー関係も使わない理由が、これで見えたと思います。

もう1つ、Phoenixは新着順でも拾いません。全候補に共通する「投稿から48時間以内」の足切りはありますが、その範囲内であれば古さは問われず、内容が近ければ候補になります。第1回で扱った「何時間も前の投稿がおすすめに出てくる」現象は、フォロー先が多く混んだタイムラインではThunder経由では起きにくく、むしろこちらの経路で起きやすい構造です。

補足: Phoenixは「フォロー外専用」ではない

ここまで「フォロー外の投稿を拾う仕組み」と説明してきましたが、より正確に言うと、Phoenixは「フォロー関係を見ていない」が正しい表現です。

  • 入力は行動履歴と投稿の情報だけで、フォローグラフは参照しません
  • そのため、フォロー中の人の投稿がPhoenix経由で拾われることも普通にあります(Thunderの候補と重複した場合は1本にまとめられます)
  • 逆に言うと、フォローが保証するのは「Thunderの新着回収の対象になる」ことだけ。Phoenixの世界で効くのは、フォローという形式上の関係ではなく、反応を通じた結びつきです

前のセクションの「投稿者との結びつき」と合わせると、「フォローしているのにおすすめで見かけない人」と「フォローしていないのによく出てくる人」が生まれる理由がここにあります。反応していない相手は、フォローしていても盤上では遠い。反応している相手は、フォローしていなくても近い。

「内容の指紋」が持つ重要な性質

セマンティックIDについて、公式文書に重要な一文があります。

「同じトピックの投稿はIDの前置部分を共有するため、見たことのない投稿にも汎化する」

かみ砕くと: このコードは投稿の内容から計算される「住所」のようなもので、内容が近い投稿どうしは住所の先頭部分が一致します。そしてモデルは「この住所の投稿は、こういう人たちに反応される」という住所ごとの傾向を学習しています。

つまり、投稿されたばかりで実績ゼロの投稿でも、内容の型が「過去に反応を集めてきた型」と同じ住所なら、その型に反応してきた人たちの検索結果に浮上できる。個々の投稿の実績ではなく、内容の型ごとの実績が効く設計です。

投稿する側にとっては大きな意味があります。フォロワーの外に届くかどうかは、「その内容の型に反応する読者層が実在するか」「あなたの投稿がその型として認識されるか」で決まる、ということだからです。

このセマンティックIDは、掘れば掘るほど面白い仕組みです(住所の計算方法、読んでいるAIモデルの正体、自分の投稿の住所は見られるのか…)。ボリュームが大きいので、次回まるごと1回かけて解説します。

投稿の「形式」も見られている

内容だけでなく、投稿の形式(画像・動画・長文・引用・リンク)も候補集めに関係します。経路は3つあります。

1. 行動の記録は形式ごとに細かい

行動履歴に刻まれる「行動の種類」は、いいねやリプライだけではありません。形式ごとに専用の行動が定義されています。

  • 画像付き → 写真をタップして拡大した
  • 動画付き → 再生した、どこまで見たか
  • 長文 → 「さらに表示」を開いた
  • 引用 → 引用元の投稿をクリックした
  • リンク付き → リンク先に何秒滞在したか(3秒〜60秒まで刻みで記録)

つまり、いいねを1つも押していなくても、「拡大して読んだ」「展開して読んだ」も行動として記録される設計です。ただし、Phoenixの検索用に整形された履歴に、どの行動がどこまで残るかは非公開部分にあります。「1行動=そのまま1票」の対応までは断定できません。

2. 閲覧者の「形式の癖」もマッチングされる

履歴には「何に反応したか」だけでなく「どの形式にどう反応したか」の癖も刻まれます。動画を最後まで見がちな人には動画が、長文を展開して読む人には長文が寄っていく方向に働きます。

3. 画像の中身もAIが読んでいる

投稿の「住所」を計算するAIは、テキストだけでなく画像も一緒に読み込みます(画像と言語を同時に理解するモデルが使われています)。画像がメインで文字が少ない投稿でも、絵の中身で分類される仕組みです。詳細は次回のセマンティックID回で解説します。

具体例: 「タイムラインにやたら漫画が出てくる」人のループ

漫画を拡大して読む → その行動が履歴に刻まれる → 漫画投稿は内容が似ていて盤上で同じ地区に固まっている → あなたのコマがその地区の近くに置かれる → 漫画の候補が増える → また読む → さらに近づく…

いいねを押していなくても、「読む」だけでこのループは回ります。

ここで1つ整理しておきます。「内容の住所(セマンティックID)」と「形式」は、実質的には別の情報として扱われています。住所が表すのは主に中身(トピックや意味)。形式は、行動の種類(どの種目で反応したか)と投稿のメタ情報(動画の長さなど)として、別枠で記録されます。ただし住所を計算するAIは投稿全体を読むため、形式の特徴も住所にうっすら滲みます。「内容はほぼ同じで形式だけ違う投稿」は、住所は近いけれど、稼げる種目と刺さる相手の癖が違う――そういう関係です。

なお、形式ごとの行動がランキングでどう配点されるか(どの行動が何点か)は、ランキングの回で扱います。

新しい投稿は、いつからPhoenixの検索対象になるのか

公式文書には、検索対象の索引はモデルのチェックポイント保存時にまとめて作られ、配信サーバーはそれを読み込んで使う、と書かれています。これだけ読むと「新着投稿は索引の次回更新まで対象外」に見えます。

一方でリポジトリには、投稿の作成イベントを受け取って、その投稿の住所(セマンティックID)を引き、索引用のレコードを作る別の経路も存在します。この経路がおすすめの検索インデックスとどう繋がっているかは公開コードから確定できないため、「新着が投稿の何分後からPhoenixの対象になるか」は断定できません。

確実に言えるのは、フォロワーへの配信(前回のThunder)は投稿直後から確定的に始まる、ということです。フォロー外への広がりのタイミングは、公開コードからは幅を持って見る必要があります。

おまけ: 新規ユーザー用の分岐はあるが、デフォルトでは眠っている

コードには「行動履歴が一定数未満のユーザーは、専用の新規ユーザー向けモデルに振り分ける」という分岐が用意されています。

新規ユーザー振り分けのコード
新規ユーザー振り分け (home-mixer/sources/phoenix_source.rs L25-39)
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/sources/phoenix_source.rs#L25-L39

ただし、そのしきい値のデフォルト値は0で、この分岐は「0より大きいときだけ動く」条件になっています。つまり公開されている設定値のままでは、この振り分けは動きません(本番で有効化されている可能性はあります)。

最後に: フォロー外ルートは、実はもう1本ある

ここまでPhoenixを「フォロー外の候補集めの主役」として紹介してきましたが、実はもう1本、SimClusters(シムクラスター)というルートが並走しています。あなたが最近反応した投稿を1件ずつ検索キーにして、「それと似た投稿」を最大800件拾ってくる仕組みで、公開設定では現役(ON)です。

面白い点が2つあります。

1つ目。「似ている」の物差しがPhoenixとまったくの別物です。内容の近さではなく、「同じ界隈の人たちに反応されているか」。Xの中に「界隈の地図」があり、投稿はその地図の上で照合されます。あなたの「界隈」がどう計算されているのか――という話がここに眠っています。

2つ目。使われているモデルの名前には「2020」の文字が刻まれています。旧Twitter時代に作られた仕組みが、最新AIのPhoenixの隣で、5年間も現役で動き続けているのです。なぜ切られないのか。Phoenixだけでは何が足りないのか。

平たく言うと、Phoenixは「内容が近い投稿」を、SimClustersは「あなたの界隈で読まれている投稿」を拾う係。この新旧2つの係の関係は、第4回でじっくり解剖します。

https://github.com/xai-org/x-algorithm/blob/main/home-mixer/sources/simclusters_source.rs#L24-L35

分かっていること・分からないこと

分かっていること

  • Phoenix retrievalはフォロー外の候補を最大1,000件取得する(デフォルト値)
  • 入力はあなたの行動履歴・最大1,024アクション。フォロー関係は使わず、投稿の中身は「文字の一致」ではなく「意味の近さ」で照合される
  • フォローグラフを参照しないため、フォロー中の人の投稿もPhoenix経由で拾われうる
  • 行動の記録は形式ごとに細かい(写真拡大・「さらに表示」展開・引用元クリック・リンク滞在秒数など)
  • あなた=行動履歴、投稿=内容の指紋+投稿者、として照合する設計(公式文書に明記)
  • 同じ型の投稿は指紋を共有し、実績ゼロの新規投稿にも「型の実績」が汎化する
  • 公式文書では索引はチェックポイント時に作成とされる一方、投稿イベント起点の索引化経路もリポジトリに存在する(両者の接続関係は非公開)

分からないこと

  • モデルが実際に「どの行動をどれだけ重視するか」(学習済みの重みの中身は公開されていても解釈は困難)
  • 新着投稿がPhoenixの検索対象になるまでの実際のタイムラグ
  • 行動履歴の整形(DENSE_WITH_SHORT_DWELL)の正確な中身
  • 各設定値は公開コードのデフォルト値であり、本番では別の値の可能性

まとめ

  • フォロー外の投稿は、あなたの「直近1,024回の行動」を検索キーにして集められる
  • フォローの有無はPhoenixでは参照されない。効くのは「反応を通じた結びつき」
  • あなたを表すのはアカウントの格ではなく行動の中身。行動が変われば、集まる候補も変わる
  • 投稿が実績ゼロでも、「内容の型」に実績があればフォロー外の読者に届く経路がある
  • 新着がいつからPhoenixの対象になるかは断定できない。投稿直後から確実に動くのは前回のThunder(フォロワーへの配信)

次回は、今回登場した「セマンティックID」(投稿に振られる内容の住所)を深掘りします。住所はどう計算されるのか、読んでいるAIモデルの正体、そして「型」の投稿が伸びる理由の核心に迫ります。

参照(行番号付きリンク。開くと該当行がハイライトされます)

Phoenix retrievalの候補取得処理:
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/sources/phoenix_source.rs#L60-L118

最大取得件数1,000件の設定:
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/params/param.rs#L4-L9

行動履歴の最大長1,024件の設定:
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/params/param.rs#L964-L969

行動履歴を取得する処理:
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/query_hydrators/retrieval_sequence_query_hydrator.rs#L45-L59

Two-Towerの仕組みの公式解説(How Retrieval Works):
https://github.com/xai-org/x-algorithm/blob/main/phoenix/README.md#how-retrieval-works

Two-Towerの本番設定(履歴1,023件・1,024次元・学習の正解=いいね・ユーザー特徴):
https://github.com/xai-org/x-algorithm/blob/main/phoenix/xrex/configs/xrecsys_two_tower.py

行動の種類の定義:
https://github.com/xai-org/x-algorithm/blob/main/phoenix/xrex/data/recsys/constants.py#L23-L117

SimClustersの候補取得処理(最大800件・2020年製モデルの指定):
https://github.com/xai-org/x-algorithm/blob/main/home-mixer/sources/simclusters_source.rs#L24-L35

この記事のもとになったX記事

Xで公開した第2回の記事を、サイト用に読みやすく再構成したものです。

前の回:Thunderとフォロー中の投稿 次の回:セマンティックIDと投稿の住所 ブログ一覧へ