背景
これまでの経緯
t-SNE や UMAP に代わる次元削減を作るため、2020年から「各データ点を1体のエージェントとみなし、局所的なルールで動かして配置を決める」という方向で実験してきました。フォルダには約40本のノートブックが残っていました。
2020年前半
粒子群(PSO)風
各点が速度を持ち、似た点に引かれ、似ていない点を避ける。
2020年夏〜秋
セルオートマトン型 MADR2d → 連続空間版
格子上で「近傍の一致度」を満足度として1マスずつ動く。その後、連続空間で「本来の近傍に寄る(harmonic)」「近傍でない点から逃げる(escape)」の2ルールに。
2021年初
局所密度で移動半径を変える(AROB 発表)
メモに「各インスタンスが何らかの目的関数を最適化するのがよいのでは」とある。ルールから目的関数への転換点。
2022年
co-ranking Q スコアを直接最適化
順位のずれを測る Q スコアを Rust で実装し、ランダムな30点ずつの移動を差分進化(DE)で最適化。
2026年10月26日
再始動: drbench と ScaleRank 初期版
物差しを先に作り、その上で三つ組の損失を使う ScaleRank を試作。外れ点の原因を調査。
2026年10月27日
ソフトマックス型へ
1つの近傍に対して非近傍を20個まとめて比べる損失に切り替え、既定値にした。
2026年10月27日
7 万点とブラウザへ
N² の順位表をやめた版を作り、Rust で書き直して、データをブラウザの外に出さないアプリとして公開した。
調査 3
調整した t-SNE・UMAP との比較NEW
既定値の t-SNE や UMAP に負けなくても、パラメータを調整すれば ScaleRank の位置まで届くかもしれません。そこで t-SNE は perplexity {5, 30, 100, 300} × exaggeration {1, 2, 4} とランダム初期化の 13 設定、UMAP は n_neighbors {5, 15, 50, 200} × min_dist {0, 0.1, 0.5, 0.99} とランダム初期化の 17 設定を、8 データ × 3 seed で走らせました。
t-SNE(13 設定)
UMAP(17 設定)
ScaleRank(γ=1, 0.5, 0)
図20. 右上ほど良い。小さな点が調整した t-SNE と UMAP の各設定、赤い線が ScaleRank で γ を 1 → 0.5 → 0 と動かしたときの軌跡。点にカーソルを合わせると設定名が出ます。
- どのデータでも、30 設定のどれも、局所と大域の両方で ScaleRank を上回りませんでした。データごとに最良の設定を選んでも同じです。
- t-SNE は perplexity を上げると大域が上がる(8 データ平均 0.52 → 0.71)代わりに局所が下がります(0.543 → 0.415)。exaggeration を上げると局所が大きく下がり、外れ点も増えます。
- UMAP は n_neighbors を上げても大域が 0.43 前後で頭打ちになり、min_dist はほとんど効きません。
- 差が小さいのは s_curve と density_blobs で、調整した t-SNE が ScaleRank の近くまで来ます。
割り引いて読む点: 大域の指標(距離の順位相関)は、ScaleRank が小さい γ で最適化している「各点から見た全点の順位」に近い量です。t-SNE と UMAP はもともと距離を保つ設計ではないので、大域での差の一部は設計目標の違いによります。局所と大域の両立を狙った手法(次の節)とはまだ比べていません。
規模
N² を使わない版と 7 万点NEW
ここまでの ScaleRank は、各点から見た全点の順位表(N × N)を持っていたので、1 万点程度が限界でした。そこで順位表をやめ、O(N log N) の大きさの構造だけで同じ目的関数を最適化する版を作りました。
- 1〜100 位は、正確な k 近傍で求める。
- それより大きい順位は、何段階かのランダムな部分標本から推定する。大きさ n の部分標本で a 番目に近い点は、全体ではおよそ a·N/(n+1) 位にあたる。これで各点に「距離 → 順位」の表を作る。
- 負例は、エポックごとに一様に引き直した候補を表で順位付けし、順位が K を超えるものから選ぶ。
8 データ × 3 seed で元の版と比べると、差はどの指標でもほぼ ±0.01 に収まりました(表5)。評価も、1 万点を超えるデータでは 2000 行だけを使って推定するようにしました。各指標は行ごとの値の平均なので、この推定に偏りはありません。
| γ | 局所 RNX@10 | AUC_RNX | 大域 距離相関 | 密度相関 | kNN 精度 |
表5. N² を使わない版 − 元の版(8 データ × 3 seed の平均)。0 に近いほど元の版と同じ。
MNIST と Fashion-MNIST の全 7 万点(PCA で 50 次元)で試した結果が表6と図21です。
| 手法 | 局所 RNX@10 | AUC_RNX | 大域 距離相関 | 密度相関 | kNN 精度 | 時間 |
表6. 7 万点(seed 0、10 スレッド)。t-SNE は openTSNE、UMAP は umap-learn。ScaleRank は Rust 版の値で、Python 版とほぼ同じ。
図21. MNIST 7 万点のうちランダムな 5000 点。色は数字のクラスで、8 と 9 は三角形。
- 大域(距離相関)は、ScaleRank が t-SNE や UMAP よりはっきり高くなりました。
- 一方、局所(RNX@10)は N が大きくなると弱くなります。MNIST の γ=1 では 1 万点で 0.27 だったのが 7 万点で 0.10 になり、t-SNE(0.39)の約 4 分の 1、UMAP と同じくらいです。
図21 では、ScaleRank γ=1(neg_scale 0.5)は数字ごとのまとまりを保つものの、各クラスが小さな塊に分かれ、周りに散らばる点も多くなっています。γ=0 は全体の配置を保つ代わりに、クラスが重なります。
局所が N とともに弱くなる理由
負例の数 Q を固定したまま N を大きくすると、Q 個の負例が代表する「それ以外のすべての点」が増え、反発が相対的に弱まります。対比学習で知られている性質で(Damrich ら 2023)、負例を少なく引く UMAP に近い振る舞いになります。
そこで、負例の項に重み w = ((N−1−K)/Q)a をかける設定(neg_scale = a)を入れました。a = 0.5 で局所はほぼ 2 倍(MNIST 0.10 → 0.20、Fashion-MNIST 0.12 → 0.20)になりますが、t-SNE にはまだ届きません。小さいデータでは密度相関が下がる(digits で 0.66 → 0.48)ので、既定値にはしていません。
MNIST の 1 万点では、ほかの設定も試しました。γ を上げると局所は最大 0.36(γ=2)、負例を 100 個にすると 0.35、neg_scale 0.5 で 0.36 になりました(既定の γ=1 は 0.28、t-SNE は 0.45)。1 点あたりの組の数を 4 倍にしたり、エポックを 800 から 2000 に増やしたりしても、ほとんど変わりませんでした(0.29)。