次元の呪いをマッチングアプリで実験したら、無関係な質問1問で最良の相手が31位まで埋もれた(hit@1は100%→23%)
約15分で読めます

次元の呪いをマッチングアプリで実験したら、無関係な質問1問で最良の相手が31位まで埋もれた(hit@1は100%→23%)


前回、次元の呪いを実測した記事では、[0,1]d[0,1]^d の中に一様分布させた点同士の距離が、次元数を上げるほどどんどん均されていく(最大距離/最小距離の比が82倍から1.21倍まで潰れる)ことを確認した。あの記事を書きながら、noteで見かけた「マッチングアプリで質問項目を増やすほど、実は良い相手を見つけにくくなっているのでは」という趣旨の投稿をふと思い出した。プロフィールの質問を増やせば増やすほど相性の良い人が見つかりやすくなる、というのは直感的には正しそうに聞こえるが、これも「次元の呪い」の一種だとしたら、むしろ逆の効果があってもおかしくない。今回はこれをシミュレーションで確かめてみる。

実験に使ったコードの全文はGitHubに置いています

実験設計: 「本当の相性」は3次元、それ以外は全部ノイズ

マッチングアプリの候補者プールを模して、N=300人の候補者と、相手を探す側のユーザー(以下「自分」)を1人用意する。距離を測るのは常に「自分と各候補者の間」で、300人の中から自分に一番近い人を探す、という設定だ。ここでの前提はこうだ。

  • 本当に相性を決めているのは、k=3次元の「価値観」(例えば「人生で何を優先するか」「対人距離の取り方」「将来設計への価値観」のような、根本的に相性に効くもの)だけだとする
  • アプリはそれとは別に、d個の「雑多な質問」(朝型か夜型か、犬派か猫派か、といった、実際には相性そのものには寄与しない質問)を追加で聞いてくる
  • 価値観・雑多な質問とも、自分も各候補者も [0,1][0,1] の一様分布から独立に値を持つ

「本当の最良マッチ」は、価値観3次元だけでのユークリッド距離が自分に一番近い候補者と定義する。一方「アプリが実際に薦める最良マッチ」は、価値観+雑多な質問の全部の次元を素朴に合算した距離が一番近い候補者、とする。多くのマッチングアプリの相性診断が実際にやっていそうな、「回答した質問の一致度をまとめて距離やスコアにする」というやり方の単純化だ。

true_dist = np.linalg.norm(cand_signal - query_signal, axis=1)      # 価値観3次元だけ
full_dist = np.linalg.norm(full_cand - full_query, axis=1)          # 価値観+雑多な質問 全部
best_true_idx = np.argmin(true_dist)                                # 本当の最良マッチ
order = np.argsort(full_dist)                                       # アプリの推薦順
rank_of_best_true = np.where(order == best_true_idx)[0][0] + 1      # 本当の最良マッチが何位に埋もれるか

雑多な質問の数 dnoised_{noise}0, 1, 2, 3, 5, 8, 12, 20, 35, 60, 100, 200 と振り、各設定で300試行繰り返して、本当の最良マッチがアプリの推薦順で何位になるか(rank)、ちょうど1位に推薦できるか(hit@1)、トップ10には入るか(hit@10)を平均した。1試行というのは、自分と候補者プール300人を丸ごと新しく引き直すことを指す。つまり「たまたまこのプールでは運が悪かった」だけの結果にならないよう、300通りの別々の状況で平均を取っている。候補者数の300と試行回数の300が同じ数字なのはただの偶然で、両者に関係はない。

結果: 雑多な質問1問で、1位が31位に転落した

dnoise=0d_{noise}=0(雑多な質問なし、価値観3次元だけで判断)のときは当然、本当の最良マッチは常に1位(hit@1=100%)になる。ところが、そこにたった1問だけ雑多な質問を足すと、状況は一変する。

横軸に雑多な質問の数(ノイズ次元数、0から200)、縦軸に本当の最良マッチの平均順位(候補者N=300人中)を取った折れ線グラフ。オレンジの実線が実測平均順位で、d_noise=0では1位だが、d_noise=1で平均31位まで跳ね上がり、その後もd_noiseの増加とともにおおむね上昇して、d_noise=200では約146位に達している(60問→100問で一度わずかに下がるなど、試行のばらつきによる小さな凸凹はある)。灰色の破線は完全ランダムに推薦した場合の期待順位150.5位を示し、実測の折れ線がその基準線に近づいていく様子が分かる。オレンジの塗りつぶし範囲は標準偏差を示し、次元が増えるほど試行間のばらつきも大きくなっている

雑多な質問0問 (計3次元):  平均順位  1.0± 0.0   hit@1=100.0%  hit@10=100.0%
雑多な質問1問 (計4次元):  平均順位 30.8±45.3   hit@1= 23.0%  hit@10= 51.3%
雑多な質問2問 (計5次元):  平均順位 49.4±55.7   hit@1= 13.0%  hit@10= 33.7%
雑多な質問3問 (計6次元):  平均順位 56.5±65.1   hit@1=  9.7%  hit@10= 32.0%
雑多な質問5問 (計8次元):  平均順位 65.5±66.4   hit@1=  6.0%  hit@10= 25.3%
雑多な質問8問 (計11次元): 平均順位 84.2±70.4   hit@1=  2.3%  hit@10= 13.7%
雑多な質問12問(計15次元): 平均順位 97.5±75.8   hit@1=  1.3%  hit@10=  9.3%
雑多な質問20問(計23次元): 平均順位 98.1±79.5   hit@1=  2.3%  hit@10= 13.7%
雑多な質問35問(計38次元): 平均順位112.2±82.0   hit@1=  1.3%  hit@10=  8.7%
雑多な質問60問(計63次元): 平均順位127.0±83.6   hit@1=  1.0%  hit@10=  7.0%
雑多な質問100問(計103次元):平均順位121.1±81.2   hit@1=  1.0%  hit@10=  5.0%
雑多な質問200問(計203次元):平均順位145.9±87.3   hit@1=  1.0%  hit@10=  5.0%

完全ランダム推薦の期待値:  平均順位150.5        hit@1=  0.3%  hit@10=  3.3%

雑多な質問がたった1問増えただけで、本当の最良マッチの平均順位は1位から30.8位まで跳ね上がった。hit@1(ちょうど1位を当てられる確率)も100%から23%まで急落している。そこからさらに質問を増やしても状況は改善するどころか(後述する試行ばらつきの凸凹はありつつ)悪化を続け、200問まで増やすと平均順位は145.9位となり、完全ランダムに推薦した場合の期待順位(150.5位)とほとんど見分けがつかなくなった

横軸に雑多な質問の数、縦軸に確率(%)を取った折れ線グラフ。オレンジの丸がhit@1(ちょうど1位に推薦できる確率)、青い四角がhit@10(トップ10に入れられる確率)。どちらもd_noise=0では100%だが、d_noiseが増えるにつれて急激に低下し、hit@1は数問でほぼ0%近くまで落ち、hit@10も100問の時点で5%まで落ちてランダム推薦の基準線(3.3%)に近づいている。hit@10には12問から20問にかけて一度小さく持ち直す凸凹があるが、これは試行のばらつきによるもの

hit@10(トップ10に入れられる確率)で見ても、20問で13.7%、100問では5%まで落ち込み、ランダム推薦の基準線(3.3%)まであと一歩のところに来ている。「候補を絞り込むための質問を増やしている」つもりが、実際には候補を絞り込む能力そのものを壊していることになる。なお、グラフをよく見ると12問→20問でhit@10が9.3%→13.7%と一度持ち直したり、平均順位も60問→100問で127.0位→121.1位とわずかに下がったりする凸凹があるが、これは各設定を独立した300試行で測っていることによるサンプリングのばらつきで、傾向を覆すものではない(実際、hit@1に至ってはこの領域でずっと1〜2%台に張り付いている)。

順位の分布そのものを見る

平均値だけでなく、300試行それぞれで本当の最良マッチが何位になったか、その分布自体も見ておきたかった。

雑多な質問0問・12問・60問・200問それぞれについて、本当の最良マッチの順位(横軸、候補者N=300人中)のヒストグラムを2x2に並べた図。0問のときは順位1に全300試行が集中する1本の棒(このパネルだけ縦軸が0〜300)。12問では順位が300位までの全域に散らばるが、分布はまだ明確に低順位側に偏っている(平均97.5位)。60問では分布はかなり平らになるがまだわずかに低順位寄り(平均127.0位)、200問では完全一様の場合の期待値(点線、1本あたり10試行)とほぼ見分けがつかない形になる(平均145.9位)。各図の黒い破線はその条件での平均順位を示す

雑多な質問0問のときは全300試行が「1位」の1本の棒に完全に集中している。12問になると順位は300位までの全域に散らばるが、分布をよく見るとまだ明確に低順位側に偏っていて(平均97.5位。完全なくじ引きなら150.5位)、「価値観の近さ」の情報がかろうじて生き残っているのが分かる。それが60問では分布がかなり平らになり(平均127.0位)、200問では完全一様の場合の期待値(図の点線、1本あたり10試行)とほとんど見分けがつかなくなる(平均145.9位)。つまりアプリの「相性診断」は、雑多な質問が十数問でくじ引きに半分足を突っ込み、数百問でほぼ完全なくじ引きになってしまう。

なぜこんなに急激に壊れるのか

前回の記事の教訓と同じで、これは「本当に意味のある3次元のシグナル」が「無関係なd次元のノイズ」に埋もれる現象だ。距離を全次元まとめて計算すると、各次元は(重み付けしない限り)平等に距離へ寄与する。価値観3次元だけなら、本当に近い人ほど距離が小さくなるという関係がはっきり出るが、そこに無関係な次元を1つ混ぜると、その1次元での「たまたまの近さ/遠さ」が全体の距離を大きく揺さぶってしまう。次元が増えるほど、この「たまたまのブレ」が積み重なり、本当のシグナル(価値観3次元の差)は相対的にどんどん小さな寄与に埋もれていく。

言葉だけだと抽象的なので、雑多な質問1問(dnoise=1d_{noise}=1)の300試行の中から、順位が平均に近かった1試行を選んで中身を具体的に見てみる。横軸が「価値観3次元だけの距離」(=本当の相性)、縦軸が「価値観3次元+雑多な質問1問の距離」(=アプリが見ている距離)で、300人の候補者を全員プロットした。

縦横それぞれに距離を取った散布図。横軸は価値観3次元だけの距離(本当の相性、左ほど良い)、縦軸は価値観3次元+雑多な質問1問の距離(アプリが見ている距離)で、1試行分の候補者300人を全員プロットしている。全ての点は左下から右上に伸びる対角線(雑多な質問の答えがたまたま完全一致だった場合に相当)の上側に乗り、対角線からの持ち上がり量がそのまま雑多な質問1問のたまたまの不一致を表す。緑の星が本当の最良マッチで、横軸では最も左(価値観距離が最小)なのに、雑多な質問の不一致で大きく上に持ち上げられている。オレンジの点は、本当の相性では劣るのに雑多な質問がたまたま一致していたせいでアプリの距離では本当の最良マッチを追い抜いた30人の候補者で、その結果この試行では本当の最良マッチはアプリ順位31位になった

全部の点が対角線より上に乗っているのは、雑多な質問の分の距離が必ず上乗せされるからだ(たまたま答えが完全一致なら対角線上に残る)。緑の星が本当の最良マッチで、横軸では文句なしの1位——なのに、雑多な質問1問の答えがたまたま合わなかったせいで大きく上に持ち上げられ、縦軸(アプリが見ている距離)では、価値観はそこそこ程度でも雑多な質問がたまたま一致していたオレンジの30人に追い抜かれてしまった。その結果、この試行での本当の最良マッチのアプリ順位は31位。順位を壊しているのは候補者本人の何かではなく、たった1問分の「たまたま」だというのが、この図で見たかったことだ。

これは前回の記事で見た「距離が縮む」というより、「意味のある差と意味のない差が、区別できない形で混ざり合う」という言い方の方が近い。実際、雑多な質問がたった1つ、2つの段階で急激に劣化しているのが特徴的で、次元をたくさん積み増す前の、ごく序盤の数次元で被害の大半が出てしまっている。

正直に書いておくべきこと

いくつか単純化している点がある。まず、雑多な質問を完全に相性と無関係なノイズとして扱ったが、実際のマッチングアプリの質問は多かれ少なかれ相性と相関がある可能性が高い。今回はあくまで「本当に無関係な質問が紛れ込んだ場合の最悪ケース」を意図的に再現した実験だと捉えてほしい。

次に、距離の計算に重み付けを一切していない。現実のマッチングアルゴリズムがもう少し賢く、質問ごとの重要度を学習して重み付けしているなら、今回ほど劇的な崩壊は起きないかもしれない。ただし、その重み付け自体をデータから正しく学習するのもまた別の困難な問題で、「質問を増やせば増やすほど安全」という単純な話にはならない、という結論自体は変わらないはずだ。

また、価値観の次元数を3に固定して実験したが、これがもっと多い(例えば10次元)場合は、無関係な質問1問あたりの破壊力は今回よりは弱まると予想される。シグナル次元とノイズ次元の比率がどこまで崩壊を左右するかは、今回の設定だけでは分からない。

手を動かして意外だったこと

一番意外だったのは、劣化のほとんどが最初の数問で起きてしまうことだった。「質問を100問追加したら流石にまずいだろう」くらいの直感はあったが、実際には1問追加した時点でhit@1がすでに100%から23%まで落ちていて、200問まで増やしてもそこから先の劣化はもう緩やかだった。「少しくらいの追加質問なら大丈夫」という感覚は、この実験の結果を見る限り通用しない。

まとめ

  • マッチングアプリを模したシミュレーションで、本当に相性を決める「価値観」をk=3次元、それとは無関係な「雑多な質問」をd次元として、N=300人の候補者プールから最良マッチを探す実験をした
  • 雑多な質問が0問なら本当の最良マッチは常に1位(hit@1=100%)だが、たった1問追加しただけで平均順位は30.8位、hit@1は23%まで急落した
  • 雑多な質問を200問まで増やすと、本当の最良マッチの平均順位は145.9位となり、完全ランダム推薦の期待順位(150.5位)とほぼ見分けがつかなくなった
  • 順位の分布そのものも、雑多な質問が十数問で(低順位側への偏りをわずかに残しつつ)N=300のほぼ全域に散らばり、200問では完全な一様分布(くじ引き)とほとんど見分けがつかなくなる様子が確認できた
  • 劣化の大半は最初の数問で起きており、「少しの追加質問なら大丈夫」という直感は今回の実験では支持されなかった
  • 質問の重み付けをしていない・ノイズを完全な無関係と仮定しているなど、最悪ケースを意図的に再現した単純化はある

最後に少し私事を。この実験をしていて、自分が過去にした選択の基準のことを思い出した。何かを選ぶとき、比較のための項目(質問)をとにかくたくさん並べて、それぞれに点数をつけて合計する、という比較表を作ったことが何度かある。物件選びのときも、転職先を比べたときも、条件を思いつく限り書き出して、Excelで重み付けもせずに単純に合計していた。あのとき自分がやっていたのは、まさに今回シミュレーションした「雑多な質問を全部同じ重みで足し合わせる」というやり方そのものだったのだと、今回の数字を見て気づかされた。本当に自分の人生にとって効いてくる軸は、実はそのうちの2つか3つくらいしかなかったはずなのに、駅からの距離や築年数、周辺のコンビニの数といった、正直そこまで重要ではない項目まで同じ重みで足し合わせて、「総合点」で選んでいた。次に何かを本気で比較するときは、まず「本当に効いてくる軸は何個で、それは何か」を先に絞り込んでから、比較を始めようと思う。項目を増やすことは、情報を増やしているようで、実は判断そのものを薄めてしまっていることがある。