本ページは t-SNE(t-Distributed Stochastic Neighbor Embedding)を多角的に解説します。 上のチップは、 検索・関連語の手がかりです。
🍰 まずはやさしく
複雑なデータを地図にする道具です。
データのまとまりを視覚的に見るために使います。
スマホのアプリを種類別に分けるようなイメージです。
この章ではt-SNEの結論を学びます。
🍰 まずはやさしく
たくさんの情報を整理する手法です。
数多くの項目を2次元の図にするために使います。
部活のメンバーを性格や能力で分ける時に便利です。
この章ではt-SNEを使う場面を学びます。
t-SNE(t-distributed Stochastic Neighbor Embedding)は、 高次元データの可視化のデファクトスタンダード。 単細胞 RNA-seq、 単語埋め込み、 画像特徴量など、 「数百〜数千次元のデータを 2 次元で見たい」場面で活躍。 ただし「クラスタ間の距離」を素朴に解釈すると誤るので、 使い方の注意が多い手法でもあります。
🍰 まずはやさしく
似たもの同士を近くに集める仕組みです。
データの隠れたグループを見つけるために使います。
似た好みの友達が自然に集まる様子に似ています。
この章ではt-SNEの直感的な仕組みを学びます。
t-SNE の発想:
結果として:近い点は近く、 遠い点は遠く配置される。 ただし「2 つのクラスタの間の距離」は 意味なし(perplexity 等で歪む)。
用途:単語ベクトル(Word2Vec)の可視化、 細胞種の発見、 画像特徴量のクラスタ確認、 異常検知の探索。 ML パイプラインの 探索ツールとして強力。
t-SNE が登場する以前、 高次元データの可視化は PCA(主成分分析)が標準でした。 しかし PCA は線形な手法で、 「曲がった多様体」(例: スイスロール状の構造)を平面に展開できません。 一方 t-SNE は 確率分布の保存という発想で、 非線形な近傍関係を 2D に押し込みます。
具体的に考えるべき問題:たとえば SSDSE-B-2026 の 47 都道府県データには、 人口・出生率・死亡率・年平均気温・小売店数など 100 以上の指標があります。 これを 2D に落とすとき、 単純な PCA では「人口が大きい東京・大阪・神奈川」が一方に偏り、 細かい地域差は潰れます。 t-SNE は 近い県は近く、 遠い県は遠く配置することに集中するため、 「東北の県が一塊り」「九州が一塊り」のような地理・社会的近接構造を可視化できる場合があります。
ただし t-SNE は 大域構造を保証しません。 「東北クラスタと九州クラスタの距離」は perplexity の値次第で大きく変動します。 これが後述の落とし穴の核心です。
| 手法 | 線形性 | 大域構造 | 速度 | 新規データ投影 | 主用途 |
|---|---|---|---|---|---|
| PCA | 線形 | ○ 完全保存 | 非常に速い | ○ 可能 | 分散保存・前処理 |
| t-SNE | 非線形 | △ 弱い | 遅い(O(n²)) | × 不可 | 可視化のみ |
| UMAP | 非線形 | ○ 比較的保持 | 速い | ○ 可能 | 可視化+特徴抽出 |
| MDS | 線形/非線形 | ○ 距離行列保存 | 遅い | △ 限定的 | 距離行列ベース |
| Isomap | 非線形 | ○ 測地距離 | 遅い | ○ 可能 | 多様体学習 |
annotate で各点に名前を付ける(点数が少ない場合)| n(点数) | dim | sklearn 標準 | openTSNE | UMAP |
|---|---|---|---|---|
| 100 | 50 | 0.5 秒 | 0.3 秒 | 2 秒 |
| 1,000 | 50 | 2 秒 | 1 秒 | 3 秒 |
| 10,000 | 50 | 2 分 | 15 秒 | 10 秒 |
| 100,000 | 50 | 2 時間 | 5 分 | 30 秒 |
| 1,000,000 | 50 | 非実用 | 1 時間 | 5 分 |
数値は CPU 1 コアでの目安。 GPU(cuML 等)を使えば 10-100 倍速。 「100 万点の可視化」は 2026 年現在 UMAP / cuML の独擅場です。
SSDSE-B-2026 は 「社会・人口統計体系」の都道府県データ(独立行政法人 統計センター提供)で、 t-SNE の教材として理想的です。 47 都道府県 × 100 以上の指標。
| 列コード | 意味 | 典型値範囲 |
|---|---|---|
| A1101 | 総人口 | 540,000(鳥取)〜 14,000,000(東京) |
| A1102 | 日本人人口 | 総人口 × 0.97〜0.99 |
| A1301 | 15 歳未満人口 | 総人口 × 11-15% |
| A1302 | 15-64 歳人口 | 総人口 × 56-60% |
| A1303 | 65 歳以上人口 | 総人口 × 25-37% |
| A4101 | 出生数 | 3,000(鳥取)〜 100,000(東京) |
| A4103 | 合計特殊出生率 | 0.99(東京)〜 1.60(沖縄)※2023年実測 |
| A4200 | 死亡数 | 8,290(鳥取)〜 137,241(東京)※2023年実測 |
| A5101 | 転入者数 | 5,000〜 400,000 |
| A5102 | 転出者数 | 5,000〜 380,000 |
| A9101 | 労働力人口(千人) | 20〜 800 |
📝 より正確な分析: SSDSE-B-2026 の実ヘッダでは A4200 は「死亡数」(2023 年: 8,290〜137,241 人)であり、 合計特殊出生率は A4103(2023 年: 0.99〜1.60)です。 列コードは必ず CSV の 2 行目(日本語ヘッダ)で意味を確認してから使いましょう。
| 列コード | 意味 | 典型値範囲 |
|---|---|---|
| B4101 | 年平均気温(℃) | 9(北海道)〜 23(沖縄) |
| B4102 | 年最高気温 | 30〜 38 |
| B4103 | 年最低気温 | −15(北海道)〜 +15(沖縄) |
| B4106 | 年降水量(mm) | 800〜 3,000 |
| 列コード | 意味 | 典型値範囲 |
|---|---|---|
| F3101 | 事業所数 | 10,000〜 700,000 |
| F3102 | 従業者数 | 100,000〜 9,000,000 |
| J2503 | 小売店数 | 5,000〜 130,000 |
| J2505 | 飲食店数 | 3,000〜 80,000 |
| 列コード | 意味 | 典型値範囲 |
|---|---|---|
| I510120 | 一般病院数 | 50〜 700 |
| I5102 | 診療所数 | 500〜 14,000 |
| E1101 | 幼稚園数 | 30〜 1,200 |
| E1301 | 小学校数 | 100〜 1,500 |
| E1501 | 中学校数 | 50〜 800 |
| E2101 | 高等学校数 | 30〜 500 |
これらの列を組み合わせて t-SNE することで、 「人口だけでなく気候・経済・医療・教育で見た都道府県の類型化」が可能。 教材として様々な切り口の演習が組めます。
| 性質 | PCA | t-SNE | UMAP | PHATE |
|---|---|---|---|---|
| 線形/非線形 | 線形 | 非線形 | 非線形 | 非線形 |
| 近傍保存 | × | ◎ | ○ | ○ |
| 大域構造 | ○ | × | △ | ◎ |
| 連続的構造 | ○ | × | △ | ◎ |
| 計算速度 | ◎ 最速 | × 遅い | ○ 速い | △ |
| 新規データ投影 | ○ | × | ○ | × |
| ハイパラ数 | 0 | 1(perp) | 2(neigh, min_dist) | 2-3 |
| 主用途 | 前処理・軸解釈 | クラスタ可視化 | 可視化+特徴抽出 | 連続軌跡解析 |
| 理論的基礎 | 分散最大化 | KL 最小化 | ファジー位相 | 拡散 |
| scikit-learn 標準 | ○ | ○ | ×(外部) | ×(外部) |
使い分けの目安:
2000 次元以上のデータ(scRNA-seq、 画像 CNN 特徴量)では、 まず PCA で 50 次元に圧縮、 その後 t-SNE で 2 次元へ。 これによりノイズ除去と計算速度の両立が可能。 「PCA で 50 → t-SNE で 2」が定番ワークフロー。
t-SNE で 2D 埋め込み → HDBSCAN(密度ベースクラスタリング)で自動的にクラスタ数を決定。 k-means と違い「クラスタ数を事前に指定不要」「形状自由」というメリット。
t-SNE 軸自体に意味はないが、 各点に 外部変数(例: SSDSE の「高齢化率」)を持たせ、 「軸方向にどの変数が変化するか」を回帰で調べる。 「右に行くほど高齢化が進む」のような 事後的な軸解釈が可能。
t-SNE 結果を plotly.express の散布図にすると、 マウスホバーで都道府県名・元の値が表示できます。 ダッシュボードや論文補助資料として有効。
2008 年の van der Maaten & Hinton 論文(JMLR)は、 2024 年時点で 引用数 4 万件超。 機械学習・バイオインフォマティクス分野のトップ引用論文の 1 つです。
特に scRNA-seq 分野では、 2014-2019 年の主要論文の 9 割が t-SNE 図を含む状況でした。 2020 年以降は UMAP に置き換わりつつありますが、 t-SNE の概念的影響は揺るがず、 「確率分布の保存」というアイデアは UMAP・PaCMAP・TriMap といった後継手法すべてに継承されています。
Hinton は深層学習・カプセルネットワーク等で著名なトロント大学(後に Google Brain)の研究者で、 2024 年に「物理学のニューラルネットへの貢献」でノーベル物理学賞を受賞。 t-SNE はその主要業績の 1 つです。
解答は本文中に散りばめられています。 全問正解できれば「t-SNE を実務で使える」レベルに到達した証です。
下のミニ t-SNE は、 8 次元空間に決定論的に生成した 3 クラスタ(各 16 点・計 48 点)を、 実際の t-SNE と同じ手順で 2D に埋め込みます。 「1 ステップ」または「自動再生」で勾配降下を進めると、 最初はごちゃ混ぜだった点が徐々に 3 つの塊に分離していく様子が見られます。 perplexity と 学習率を動かして、 見え方がどう変わるかを体感してください。 点をドラッグ(タッチ可)して引き剥がすと、 最適化がまた引き戻す「バネと反発」の力学も観察できます。
計算の正確さについて: この実装は本物の t-SNE と同じ骨格です。 (1) 高次元でガウス親和性 $p_{j|i}$ を perplexity から二分探索で決める、 (2) 対称化して $p_{ij}$ を得る、 (3) 低次元で自由度 1 の $t$ 分布 $q_{ij}\propto(1+\lVert y_i-y_j\rVert^2)^{-1}$ を使う、 (4) KL ダイバージェンスを勾配降下(モメンタム+ゲイン適応+序盤の early exaggeration)で最小化。 教材用に n=48・8 次元・O(n²) の素朴実装に簡略化しており、 Barnes-Hut 近似や PCA 初期化は省いています。 本質的な挙動(分離・perplexity 依存・確率性)はそのまま再現されます。
random_state を変えるたびに見た目が変わります。t-SNE の後継としてよく使われるのが UMAP です。 UMAP は近傍グラフとファジー集合の理論に基づき、 大域構造をより保ちやすく・高速で・新規データを後から投影できるという利点があります。 一方で UMAP にも「クラスタ間距離を鵜呑みにしない」「n_neighbors / min_dist 依存」という t-SNE と共通の注意点があります。 まずはこのデモで t-SNE の力学を掴み、 次に UMAP と比較すると理解が深まります。
関連ページ: PCA(線形次元削減・前処理) / UMAP(後継手法) / クラスタリング / 標準化(前処理の必須ステップ)
🍰 まずはやさしく
確率(起こりやすさ)を使った計算方法です。
点と点の距離を正しく配置するために使います。
買い物リストの似ている商品を分ける計算のようなものです。
この章ではt-SNEの数式と定義を学びます。
混雑問題(Crowding Problem)とは、 高次元空間の「中庸な距離」を低次元に押し込もうとすると、 「全てが中央に集まってつぶれてしまう」現象です。 たとえば 100 次元のガウス球面上では、 ほとんどの点ペアが「平均距離付近」に集中します(次元の呪い)。 これを 2 次元に投影すると、 中央が混雑して構造が見えなくなります。
解決策(t 分布の採用): van der Maaten & Hinton (2008) は、 低次元側に 自由度 1 の Student-t 分布を使うことを提案しました。 ガウス分布よりも裾が重いため、 高次元で「中庸距離」だったペアを、 低次元で「遠く」配置するインセンティブが生まれます。
この「裾の重さ」が、 t-SNE のクラスタを はっきり分離して見せる魔法の正体です。 同じ仕組みを「自由度 ν を可変にする」よう拡張したのが t-SNE variants(α-t-SNE 等)。
条件付き確率 $p_{j|i}$ は 非対称(i から見た j ≠ j から見た i)。 そのままだと最適化が不安定になるため、 対称化します:
最適化初期の数百ステップで、 $p_{ij}$ を α 倍(典型 12 倍)して使うテクニックがあります。 これにより「クラスタを最初に強く引き寄せ、 その後ゆっくり緩める」効果があり、 大域構造のロバスト性が高まります。 scikit-learn の early_exaggeration パラメータで制御可能。
素朴実装は O(n²) ですが、 重力多体問題で使われる Barnes-Hut アルゴリズムを応用すると O(n log n) に削減できます。 これにより数万点でも数分で計算可能。 scikit-learn のデフォルト(method='barnes_hut')。
| パラメータ | 既定 | 役割 | 調整指針 |
|---|---|---|---|
n_components | 2 | 出力次元数 | 2D 可視化は 2、 3D は 3、 4 以上は非推奨 |
perplexity | 30 | 近傍数の効果的指定 | 5-50、 n < 100 なら n/3 以下 |
early_exaggeration | 12.0 | 初期クラスタ強調倍率 | 大データなら 30-50 に上げる |
learning_rate | 'auto' | 勾配ステップサイズ | 'auto' は max(N/12, 50)。 旧版互換なら 200 |
max_iter(旧 n_iter) | 1000 | 最大イテレーション数 | 収束しないときは 3000-5000 |
n_iter_without_progress | 300 | 進捗なしで停止する閾値 | 大データなら 500-1000 |
min_grad_norm | 1e-7 | 勾配の収束判定 | 通常は触らない |
metric | 'euclidean' | 高次元側の距離尺度 | 'cosine' / 'manhattan' / カスタム |
init | 'pca' | 初期配置 | 'pca' 推奨('random' は再現性低) |
random_state | None | 乱数シード | 論文では必ず固定(例: 0, 42) |
method | 'barnes_hut' | 計算アルゴリズム | n > 5000 で 'barnes_hut'、 小なら 'exact' |
angle | 0.5 | BH 法の近似角度 | 0.2-0.8、 大きいと速いが粗い |
論文・実務での実用ルール:「perplexity と random_state だけは必ず明示し、 他は既定値」が安全。 細かい調整は学術論文でのチューニングが必要なときのみ。
| 項目 | t-SNE | UMAP | 勝者 |
|---|---|---|---|
| 速度(n=10,000) | 数分 | 数十秒 | UMAP |
| 速度(n=100,000) | 困難(時間オーダー) | 数分 | UMAP |
| 大域構造保存 | △ 弱い | ○ 比較的良い | UMAP |
| 局所構造保存 | ◎ 最強 | ○ 良い | t-SNE |
| 新規データ投影 | × 不可 | ○ transform() あり | UMAP |
| 確率モデルの直感 | ○ 明快(KL) | △ ファジーシンプリシャル複体(難解) | t-SNE |
| 学術的歴史 | ◎ 引用多数(2008-) | ○ 増加中(2018-) | t-SNE |
| ハイパラ感度 | perplexity 1 つで揺れる | n_neighbors, min_dist で 2 軸 | 互角 |
| scikit-learn 統合 | ◎ 標準 | ○ umap-learn 別パッケージ | t-SNE |
| GPU 加速 | cuML 等で対応 | cuML 等で対応 | 互角 |
| 「論文での説得力」 | ○ 定番 | ○ 増加中 | 互角 |
結論: 2026 年現在は「速度」「大域構造」「新規データ投影」の 3 点で UMAP 優位。 ただし t-SNE は 定番として論文比較・既存資産・教育目的で残ります。 新規プロジェクトでは UMAP をデフォルトに、 比較用に t-SNE も併記が現実解。
ステップ 1: 高次元側の近傍確率を作る。 各点 $\mathbf{x}_i$ に対して、 「ガウスカーネル」で他の点との近さを 確率に変換します:
この $\sigma_i$ は perplexity という入力値から決まります。 perplexity は概ね「各点が何個の近傍を持つと考えるか」の指定(5〜50 が標準)。
ステップ 2: 対称化。 $p_{ij} = \frac{p_{j|i} + p_{i|j}}{2n}$ で対称な確率にします。 これで「i から見た j の近さ」と「j から見た i の近さ」が均等になります。
ステップ 3: 低次元側の確率(t 分布)。 出力空間 $\mathbb{R}^2$ の点 $\mathbf{y}_i$ に対し、 自由度 1 の Student-t 分布を用いて:
最適化: $p$ と $q$ の差を Kullback-Leibler ダイバージェンスで測り、 これを最小化するように $\mathbf{y}_i$ を勾配降下で動かします:
KL は非対称なので、 「高次元で近い→低次元でも近く」は重く罰せられますが、 「高次元で遠い→低次元で近い」は軽い罰しか付きません。 これが「t-SNE は局所構造を優先」する数学的根拠です。
KL を $\mathbf{y}_i$ について微分すると、 各点に「引っ張り合う力」と「押し離す力」の合力が働きます:
perplexity の効果(同じデータでも見え方が変わる):
| perplexity | 見え方 | 注意 |
|---|---|---|
| 5 | 局所構造重視、 細かいクラスタ | 大域構造は壊れる |
| 30(既定) | バランス | 標準的 |
| 50 | 大域構造重視、 大まかなクラスタ | 細部消える |
| 100+ | ほぼ単一クラスタに | サンプル数次第 |
複数の perplexity で実行し、 ロバストな構造を確認するのが推奨。
SSDSE-B-2026(社会・人口統計体系、 47 都道府県 × 100 列以上)から、 次の 5 つの代表的な社会経済指標を選んで t-SNE にかけてみます:
| 列コード | 意味 | 抜粋値(例) |
|---|---|---|
| A1101 | 総人口(人) | 北海道 5,092,000 / 東京 14,000,000 級 / 鳥取 540,000 |
| A4101 | 出生数 | 北海道 24,430 / 沖縄 比較的高 / 秋田 低 |
| A4103 | 合計特殊出生率 | 沖縄 1.6 前後 / 東京 1.0 前後 |
| A5101 | 転入者数 | 東京・神奈川・大阪が突出 |
| A9101 | 労働力人口比率 | 地方は高め、 都市部は低め |
これら 5 列を 標準化(Z-score)してから t-SNE に渡すのが定石です。 標準化しないと、 単位の大きい「総人口」だけが距離計算を支配し、 他の指標が無視されます。
標準化済み 5 次元空間で「北海道」と「東京」の Euclidean 距離 $d_{\text{北,東}}$ を仮に 3.2、 「北海道」と「青森」の距離を 0.8 とします。 perplexity に対応する $\sigma_i$ が 1.0 のとき:
これが「近い県には大きな確率」「遠い県には小さな確率」という t-SNE の出発点です。
SSDSE-B-2026 の 47 都道府県データ (n=47) を入力にしたときの t-SNE perplexity の推奨値を、 シャノンエントロピー H と perplexity = 2^H の関係から逆算する。 n=47 では perplexity 5-10 程度が標準的レンジ (典型 n=100 の半分以下)。
| n データ | 推奨 perplexity |
|---|---|
| 100 | 5-10 |
| 1000 | 20-30 |
| 10000 | 30-50 |
1 2 3 4 5 6 7 8 | import numpy as np # 例: 等確率 k 近傍 ks = [5, 10, 30] for k in ks: p = 1/k H = -k * p * np.log2(p) perp = 2**H print(f"k={k}: H={H:.2f}, perp={perp:.1f}") |
💬 手計算 (Step 1) perp ≈ k (等確率時) と Python 出力が完全一致。
最小コードで動かしてみる例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sklearn.manifold import TSNE from sklearn.datasets import load_digits import matplotlib.pyplot as plt digits = load_digits() # 64次元 → 2次元へ # 全 1797 点だと 2 分以上かかるので、先頭 500 点で試す(傾向は変わらない) _X, _y = digits.data[:500], digits.target[:500] tsne = TSNE(n_components=2, perplexity=30, random_state=0) emb = tsne.fit_transform(_X) plt.scatter(emb[:, 0], emb[:, 1], c=_y, cmap='tab10', s=10) plt.title('t-SNE 可視化(MNIST 数字)') plt.colorbar() plt.show() |
🎯 このコードでやること:SSDSE-B-2026 の 47 都道府県データから 5 列(人口・出生数・出生率・転入数・労働力比率)を抜粋、 標準化したうえで sklearn.manifold.TSNE で 2D に射影します。
📥 入力データ(SSDSE-B-2026.csv、 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.manifold import TSNE # SSDSE-B-2026 を読み込む(1 行目はヘッダコード、 2 行目は日本語ヘッダ) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932') # 年度で絞らないと 564 行(47 県 × 12 年)になり、下の (47, 5) と食い違う df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()] # 5 列を選ぶ cols = ['A1101', 'A4101', 'A4103', 'A5101', 'A9101'] X = df[cols].values # shape: (47, 5) prefs = df['Prefecture'].values # 都道府県名(ラベル用) # 標準化(Z-score)— これを忘れると人口だけが支配する X_std = StandardScaler().fit_transform(X) # t-SNE — perplexity=10(n=47 なので小さめ) tsne = TSNE(n_components=2, perplexity=10, random_state=0, init='pca') emb = tsne.fit_transform(X_std) print('emb shape:', emb.shape) for p, (x, y) in zip(prefs[:5], emb[:5]): print(f'{p}: ({x:.2f}, {y:.2f})') |
📤 実行例(出力):
💬 結果の読み方: 出力された 2D 座標を散布図にすると、 東北系(青森・岩手・秋田)が右上に固まり、 大都市圏(東京・神奈川・大阪)が左下に集まる傾向が見えます。 これは「人口規模・出生率・転入数」が似ている都道府県が 近傍確率を共有したため。 ただし座標の絶対値や軸方向には意味がない(回転・反転は perplexity と random_state に依存)。
🎯 このコードでやること:同じ SSDSE-B-2026 データに対して perplexity を 5 / 15 / 30 / 45 と変えて 4 通りの t-SNE 埋め込みを作り、 結果がどれだけ変わるかを 2×2 のサブプロットで可視化します。
📥 入力データ: 上のセクションで作成した X_std(shape (47, 5))。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import matplotlib.pyplot as plt from sklearn.manifold import TSNE perps = [5, 15, 30, 45] fig, axes = plt.subplots(2, 2, figsize=(11, 9)) for ax, p in zip(axes.ravel(), perps): emb = TSNE(n_components=2, perplexity=p, random_state=0, init='pca').fit_transform(X_std) ax.scatter(emb[:, 0], emb[:, 1], s=30, c='#00695C') for i, name in enumerate(prefs): ax.annotate(name, (emb[i, 0], emb[i, 1]), fontsize=7) ax.set_title(f'perplexity = {p}') plt.tight_layout() plt.savefig('tsne_perp_compare.png', dpi=120) print('saved') |
📤 実行例(出力):
💬 結果の読み方: perplexity が変わるだけで クラスタ数も配置も全く変わる ことが目で見て分かります。 n=47 という小さな標本では perplexity 5-15 が現実的。 「絶対正しい perplexity」は存在しないため、 必ず複数試して ロバストな構造(どの perplexity でも一緒に固まる点群)を見つけるのが鉄則。
🎯 このコードでやること:同じ SSDSE-B-2026 データに対して PCA(線形)と t-SNE(非線形)を並べて表示し、 「線形と非線形でクラスタの見え方がどう違うか」を可視化します。
📥 入力データ: 上の X_std。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from sklearn.decomposition import PCA from sklearn.manifold import TSNE import matplotlib.pyplot as plt pca = PCA(n_components=2).fit_transform(X_std) tsne = TSNE(n_components=2, perplexity=15, random_state=0, init='pca').fit_transform(X_std) fig, (a, b) = plt.subplots(1, 2, figsize=(13, 6)) a.scatter(pca[:, 0], pca[:, 1], s=30, c='#1565C0') a.set_title('PCA (線形)') b.scatter(tsne[:, 0], tsne[:, 1], s=30, c='#00695C') b.set_title('t-SNE (非線形, perplexity=15)') for ax, emb in [(a, pca), (b, tsne)]: for i, name in enumerate(prefs): ax.annotate(name, (emb[i, 0], emb[i, 1]), fontsize=7) plt.tight_layout() plt.savefig('pca_vs_tsne_47pref.png', dpi=120) print('PCA 第 1-2 成分の説明分散比:', PCA(n_components=2).fit(X_std).explained_variance_ratio_) |
📤 実行例(出力):
💬 結果の読み方: PCA は「総人口」のような 分散の大きな軸に支配されて、 細かな地域差が潰れます。 t-SNE は確率分布を保存するため、 各地方ブロックを「クラスタ」として浮かび上がらせやすい。 ただし PCA は累積寄与率という客観指標があるのに対し、 t-SNE は「正しさ」を定量化しにくい。 役割分担: PCA で軸の意味を見て、 t-SNE でクラスタを探す。
🎯 このコードでやること:t-SNE の最終的な kl_divergence_ 値を取得し、 さらに silhouette_score で「埋め込み空間で k-means クラスタリングが綺麗に分かれているか」を定量化します。 これにより複数の perplexity を「数値」で比較できます。
📥 入力データ: 同じ X_std(SSDSE-B-2026 の 5 列標準化済み)。
1 2 3 4 5 6 7 8 9 10 11 12 13 | from sklearn.manifold import TSNE from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score print(f'{"perp":>6} | {"KL_final":>10} | {"silhouette(k=5)":>16}') print('-' * 42) for p in [5, 10, 15, 20, 30]: t = TSNE(n_components=2, perplexity=p, random_state=0, init='pca').fit(X_std) emb = t.embedding_ km = KMeans(n_clusters=5, n_init=10, random_state=0).fit(emb) sil = silhouette_score(emb, km.labels_) print(f'{p:>6d} | {t.kl_divergence_:>10.4f} | {sil:>16.3f}') |
📤 実行例(出力):
💬 結果の読み方: KL 発散は perplexity を上げるほど単調に下がりますが、 これは「全点が滑らかな雲」になるからで、 クラスタ品質を保証しません。 別途 silhouette(-1 〜 1、 大きいほどクラスタが綺麗)でチェックすると、 SSDSE 47 県では perplexity=15 が最良(0.561)。 つまり「KL だけ」を見ると 30 が良いが、 クラスタ的には 15 が良い。 評価軸を 2 つ持つことが重要。
🎯 このコードでやること:47 都道府県を 8 つの地方ブロック(北海道・東北・関東・中部・近畿・中国・四国・九州沖縄)に分け、 t-SNE 散布図をブロック別に色分けします。 「t-SNE が自動的に地方を分離できるか」を視覚的に検証。
📥 入力データ: X_std(標準化済 5 次元)と都道府県名のリスト。 地方ブロック定義は Python リテラル(公開地理区分)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import matplotlib.pyplot as plt from sklearn.manifold import TSNE # 地方ブロック定義(総務省標準区分) region = { '北海道': '北海道', '青森県': '東北', '岩手県': '東北', '宮城県': '東北', '秋田県': '東北', '山形県': '東北', '福島県': '東北', '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東', '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部', '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿', '鳥取県': '中国', '島根県': '中国', '岡山県': '中国', '広島県': '中国', '山口県': '中国', '徳島県': '四国', '香川県': '四国', '愛媛県': '四国', '高知県': '四国', '福岡県': '九州沖縄', '佐賀県': '九州沖縄', '長崎県': '九州沖縄', '熊本県': '九州沖縄', '大分県': '九州沖縄', '宮崎県': '九州沖縄', '鹿児島県': '九州沖縄', '沖縄県': '九州沖縄', } colors = {'北海道': '#1565C0', '東北': '#00838F', '関東': '#C62828', '中部': '#EF6C00', '近畿': '#6A1B9A', '中国': '#2E7D32', '四国': '#558B2F', '九州沖縄': '#AD1457'} emb = TSNE(n_components=2, perplexity=12, random_state=0, init='pca').fit_transform(X_std) fig, ax = plt.subplots(figsize=(11, 9)) for blk, c in colors.items(): idx = [i for i, p in enumerate(prefs) if region[p] == blk] ax.scatter(emb[idx, 0], emb[idx, 1], s=70, c=c, label=blk, alpha=0.85) for i in idx: ax.annotate(prefs[i], (emb[i, 0], emb[i, 1]), fontsize=8) ax.legend(loc='upper left', fontsize=10) ax.set_title('t-SNE (perplexity=12) — 47 都道府県を地方ブロックで色分け') plt.tight_layout() plt.savefig('tsne_region_colored.png', dpi=120) print('saved tsne_region_colored.png') |
📤 実行例(出力):
💬 結果の読み方: t-SNE は 地理ラベルを知らないのに、 純粋に 5 つの社会経済指標から地方ブロックを浮かび上がらせます。 これは「同じ地方の県は同じような人口動態を持つ」という仮説の 定量的裏付けです。 ただし「東北と九州の距離」を文字通り解釈してはいけない(perplexity 依存)。
🎯 このコードでやること:t-SNE 埋め込み空間で、 各点の k-NN 距離平均を計算し、 上位 3 つを「最も孤立した都道府県」として抽出します。 外れ値の自動検出です。
📥 入力データ: 上で計算した emb(shape (47, 2))と prefs。
1 2 3 4 5 6 7 8 9 10 11 | import numpy as np from sklearn.neighbors import NearestNeighbors nn = NearestNeighbors(n_neighbors=4).fit(emb) dist, _ = nn.kneighbors(emb) # 自分自身を含むので n=4 で k=3 mean_dist = dist[:, 1:].mean(axis=1) # 自分以外の 3 近傍平均 rank = np.argsort(mean_dist)[::-1] # 距離が大きい順 print('--- t-SNE 空間で孤立している都道府県 TOP 5 ---') for r in rank[:5]: print(f'{prefs[r]:>6s} mean k=3 dist = {mean_dist[r]:.2f}') |
📤 実行例(出力):
💬 結果の読み方: 上位は予想通り 大都市圏(東京・神奈川・大阪)と 地理的特異点(沖縄・北海道)です。 t-SNE は数値特徴のみから「日本のどこが特殊か」を客観的に教えてくれます。 異常検知の素朴な応用例として、 機械の振動センサ・顧客行動ログ・医療検査値などに同じ手法を適用できます。
| 年 | 出来事 | 意義 |
|---|---|---|
| 2002 | SNE(Stochastic Neighbor Embedding)登場 (Hinton & Roweis) | 確率による近傍保存の原型 |
| 2008 | t-SNE 発表 (van der Maaten & Hinton, JMLR) | 低次元側に t 分布を採用、 混雑問題を解決 |
| 2014 | Barnes-Hut t-SNE | $O(n \log n)$ への高速化(数万点まで実用化) |
| 2018 | UMAP 登場 (McInnes et al.) | 大域構造保存・新規データ投影可能・高速 |
| 2019 | openTSNE 公開 | 新規データ投影機能を追加した派生実装 |
| 2020- | scRNA-seq 分野で UMAP がデファクト | 細胞種発見・系統樹推定で t-SNE を置換 |
| 2022 | scikit-learn 1.2 で init='pca' がデフォルト推奨に | 大域構造のロバスト性が向上 |
| 2024-2026 | 論文での t-SNE 使用率は減少、 UMAP・PaCMAP・TriMap に置換進行 | 「t-SNE 一強」時代は終焉 |
t-SNE は 2008-2020 年の機械学習可視化の 事実上の標準でしたが、 UMAP 以後は「使ってもいいが UMAP も併記」がベストプラクティスになっています。
random_state を 固定したかinit='pca'(または既定)を確認したかmax_iter が収束に十分か確認したか(最終 KL 値で判定)sklearn.manifold.TSNE — API リファレンス🎯 このコードでやること:SSDSE-B-2026 から 10 列(人口・出生・死亡・転入・転出・労働力比率・気温・降水・小売店数・病院数)を抜粋し、 5 列版と比較して「次元が増えると t-SNE の見え方がどう変わるか」を検証。
📥 入力データ: 同じ SSDSE-B-2026.csv、 ただし 10 列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.manifold import TSNE import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932') cols_5 = ['A1101', 'A4101', 'A4103', 'A5101', 'A9101'] cols_10 = cols_5 + ['A5102', 'B4101', 'B4103', 'F3101', 'I510120'] # A5102=転出, B4101=年平均気温, B4103=最低気温, F3101=小売店数, I510120=一般病院数 X5 = StandardScaler().fit_transform(df[cols_5].values) X10 = StandardScaler().fit_transform(df[cols_10].values) emb5 = TSNE(n_components=2, perplexity=12, random_state=0, init='pca').fit_transform(X5) emb10 = TSNE(n_components=2, perplexity=12, random_state=0, init='pca').fit_transform(X10) fig, (a, b) = plt.subplots(1, 2, figsize=(13, 6)) a.scatter(emb5[:, 0], emb5[:, 1], s=40, c='#00695C') a.set_title(f't-SNE on {len(cols_5)} columns') b.scatter(emb10[:, 0], emb10[:, 1], s=40, c='#1565C0') b.set_title(f't-SNE on {len(cols_10)} columns') plt.tight_layout() plt.savefig('tsne_5vs10cols.png', dpi=120) print(f'Saved. 5 列入力 shape = {X5.shape}, 10 列入力 shape = {X10.shape}') |
📤 実行例(出力):
💬 結果の読み方: 入力次元を増やすと、 t-SNE は より細かい構造を拾います。 5 列では「人口だけ」が支配していたものが、 10 列では気候・社会インフラの違いも反映され、 地理的に意味のある配置になります。 これは「特徴量設計が t-SNE の結果を大きく変える」ことの実証。
🎯 このコードでやること:t-SNE の学習中に KLDivergenceCallback(カスタム)で各イテレーションの KL 発散値を記録、 「収束はいつ起こったか」を可視化します。
📥 入力データ: 上のセクションの X_std(SSDSE-B-2026 の 5 列標準化済)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # ── この抜粋で使うデータを用意します(SSDSE-B の 47 都道府県・最新年度)── import pandas as pd from sklearn.preprocessing import StandardScaler _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932') _d = _d[_d['SSDSE-B-2026'] == _d['SSDSE-B-2026'].max()] X_std = StandardScaler().fit_transform( _d[['A1101', 'A1303', 'A4101', 'L3221', 'C5401']].astype(float)) from sklearn.manifold import TSNE import matplotlib.pyplot as plt # 異なる反復回数で実行して最終 KL を記録(sklearn 1.5 以降 n_iter → max_iter) # 250 以下は sklearn の早期誇張フェーズだけで終わり KL が発散するので 300 から iters = [300, 500, 1000, 2000, 3000, 5000] kl_vals = [] for n in iters: t = TSNE(n_components=2, perplexity=12, random_state=0, init='pca', max_iter=n).fit(X_std) kl_vals.append(t.kl_divergence_) print(f'max_iter={n:>4d}: KL = {t.kl_divergence_:.4f}') plt.figure(figsize=(8, 5)) plt.plot(iters, kl_vals, 'o-', color='#00695C', linewidth=2) plt.xlabel('max_iter') plt.ylabel('Final KL divergence') plt.title('t-SNE 収束曲線 (SSDSE-B-2026, 47 都道府県)') plt.grid(True, alpha=0.3) plt.savefig('tsne_kl_curve.png', dpi=120) |
📤 実行例(出力):
💬 結果の読み方: KL は 1000 イテレーションで大きく落ち、 それ以後はゆっくり収束。 SSDSE 47 県という小データなら 既定の max_iter=1000 で十分です。 大規模データ(数万点)では max_iter を 3000-5000 にしたほうが安定する場合があります。 KL の絶対値はデータ依存(人数比較は無意味)、 「下げ止まる」位置だけが重要。
各点 $i$ の $\sigma_i$ は、 「perplexity が指定値になるよう」二分探索で決定されます:
これにより、 密な領域では $\sigma_i$ が小さく、 疎な領域では大きく自動調整されます。 局所スケール適応がこのアルゴリズムの核心の 1 つ。
勾配は次の 2 項に分解できます:
この 2 項のバランスで、 「高次元で近い同士が引き合い、 全体としては適度に広がる」自己組織化が起こります。 物理学のシミュレーションに似た描像です。
最初の 250 イテレーションだけ $p_{ij}$ を 12 倍にすると、 クラスタが先に強く凝集します。 その後通常値に戻すと、 クラスタ内部の細かい配置が決まる。 「粗く分けてから細部を整える」二段階最適化。
勾配降下に運動量項を加えることで、 「振動を抑え滑らかに収束」します。 デフォルトは初期 0.5、 後半 0.8。
「Pairwise Controlled Manifold Approximation Projection」。 局所・中域・大域構造を 3 種類のペアで同時に保存。 t-SNE と UMAP の良いとこ取り。
「triplet」(近・中・遠の 3 点組)の関係を保存。 t-SNE よりも大域構造に強い。
大規模データ向け t-SNE の高速版。 数百万点でも実用化。
t-SNE の前身。 低次元側もガウス分布を使う。 混雑問題が解決されておらず、 現在は使われない。
「Potential of Heat-diffusion for Affinity-based Transition Embedding」。 拡散ベースの埋め込み。 細胞分化系統樹のような連続的構造に強い。
「データを開く」から「論文に載せる図」まで、 一連の流れを通しで示します。
1 2 3 4 | import pandas as pd df_all = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932') df = df_all[df_all['SSDSE-B-2026'] == 2023].copy() # 最新年のみ print(f'shape: {df.shape}, 都道府県数: {df["Prefecture"].nunique()}') |
1 2 3 4 5 6 7 8 | from sklearn.preprocessing import StandardScaler feats = ['A1101', 'A4101', 'A4103', 'A5101', 'A5102', 'A9101', 'B4101', 'F3101', 'I510120', 'J2503'] X = df[feats].dropna().values prefs = df.loc[df[feats].dropna().index, 'Prefecture'].values X_std = StandardScaler().fit_transform(X) print(f'X shape: {X_std.shape}') # (47, 10) |
1 2 3 4 5 | from sklearn.manifold import TSNE embs = {p: TSNE(n_components=2, perplexity=p, random_state=0, init='pca').fit_transform(X_std) for p in [5, 10, 15, 20, 30]} print('Computed embeddings for perplexities:', list(embs.keys())) |
1 2 3 4 5 6 7 8 9 10 11 | from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score best_perp, best_sil = None, -1 for p, e in embs.items(): labels = KMeans(n_clusters=5, n_init=10, random_state=0).fit_predict(e) sil = silhouette_score(e, labels) print(f'perplexity={p}: silhouette={sil:.3f}') if sil > best_sil: best_sil, best_perp = sil, p print(f'\nBest perplexity = {best_perp} (silhouette = {best_sil:.3f})') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import matplotlib.pyplot as plt emb = embs[best_perp] fig, ax = plt.subplots(figsize=(10, 8)) ax.scatter(emb[:, 0], emb[:, 1], s=60, c='#00695C', alpha=0.85) for i, name in enumerate(prefs): ax.annotate(name, (emb[i, 0], emb[i, 1]), fontsize=8) ax.set_xlabel('t-SNE 1') ax.set_ylabel('t-SNE 2') ax.set_title(f't-SNE 可視化 — 47 都道府県 (SSDSE-B-2026, perp={best_perp})') plt.tight_layout() plt.savefig('final_tsne_47pref.pdf') # 論文用 PDF plt.savefig('final_tsne_47pref.png', dpi=150) print('Saved 2 figures.') |
以上で 「データ → 標準化 → t-SNE → 品質評価 → 最終図」のフルパイプラインが完成。 論文・レポートにそのまま使える品質です。
| 症状 | 原因 | 対処 |
|---|---|---|
perplexity must be less than n_samples | perplexity がデータ数以上 | perplexity を n/3 以下に |
| 「全点が一塊り」になる | perplexity が大きすぎ | 5-10 に下げる |
| 「点がバラバラに散る」 | perplexity が小さすぎ / 学習率不適切 | perplexity を上げる、 init='pca' |
| 毎回違う結果 | random_state 未指定 | random_state=0 等で固定 |
| 計算が遅い | 素朴法または n が大きい | method='barnes_hut'、 先に PCA 圧縮 |
| 収束しない(KL 値が下がらない) | n_iter 不足 / 学習率不適切 | n_iter=3000、 learning_rate='auto' |
| NaN や inf が出る | 入力に NaN が混入 | dropna() や fillna で前処理 |
| マイナス値だらけ | 標準化していない | StandardScaler を必ず通す |
| cosine 距離を使いたい | metric が違う | metric='cosine' 指定 |
| 新規データを後から追加したい | t-SNE には transform() なし | UMAP / openTSNE を使う |
論文・スライド・ブログで t-SNE 図を見たら、 次のチェックを:
これらを満たさない t-SNE 図は、 結論に 定性的記述(「3 つのクラスタが見える」程度)以上の意味を持たせるべきではありません。
理解を深めるための実践的な演習を 8 問用意しました。 SSDSE-B-2026.csv が手元にある前提です。
人口・出生数・出生率の 3 列だけ/ 教育系(小中高校数)10 列だけ/ 経済系(事業所数・小売店数)10 列だけ、 という 3 通りで t-SNE を実行し、 「地方ブロックの見え方」がどう変わるか観察せよ。
10 列 → PCA で 3 次元 → t-SNE と、 10 列 → 直接 t-SNE を比較。 高次元 → PCA → t-SNE が「ノイズ除去」として機能するか確認。
random_state を 0, 1, 2, ..., 9 と 10 通り変えて、 「クラスタの相対位置」がどれだけ変わるか確認せよ。
metric='euclidean' と metric='cosine'、 metric='manhattan' で結果を比較。 SSDSE データではどれが「地方ブロック分離」に向くか?
pip install umap-learn の上、 同じデータで UMAP(n_neighbors=10, min_dist=0.1)を実行し、 t-SNE 図と並べて比較せよ。 どちらが地方ブロックの分離を綺麗に示すか?
2018-2023 年の SSDSE-B-2026 から各年の都道府県データを取り、 全年 × 47 県 = 282 点を一括 t-SNE。 同じ都道府県の年次推移を線で結ぶと、 「東京は常に外れ」「沖縄は徐々に独自パスを描く」などが見える。
t-SNE 後の埋め込みで k-means の k=2, 3, ..., 10 を試し、 silhouette score の最大値を取る k を見つけよ。 47 都道府県は何クラスタに分かれるのが最適か?
t-SNE 図の各点を「一般病院数(色濃淡)」で塗り分け、 「t-SNE で固まる地域は本当に医療資源が似ているか」を視覚的に検証。
| 場面 | 推奨設定 |
|---|---|
| とりあえず可視化 | TSNE(n_components=2, perplexity=30, random_state=0) |
| 小データ(n < 100) | perplexity=min(15, n/3) |
| 中データ(n=100-10,000) | perplexity=30, init='pca' |
| 大データ(n > 10,000) | 先に PCA で 50 次元、 perplexity=50、 method='barnes_hut' |
| 論文用最終図 | 3 通り perplexity 試行 → silhouette 最大を採用、 random_state 固定 |
| 新規データ投影が必要 | UMAP / openTSNE に切り替え |
| 計算速度優先 | UMAP / cuML.TSNE(GPU) |
| 解釈性優先 | PCA(軸に意味あり)に切り替え検討 |
| テキスト埋め込み(300 次元) | perplexity=30-50、 標準化不要(既に正規化済) |
| 画像特徴量(2048 次元) | 先に PCA で 50 次元、 perplexity=30 |
| 分野 | 応用例 | 典型 n / 次元 |
|---|---|---|
| 分子生物学 | scRNA-seq の細胞種クラスタ | n=10⁵, dim=2000 |
| 計算化学 | 分子記述子の類似度可視化 | n=10⁴, dim=200 |
| 金融 | 株価リターンの相関構造 | n=10³, dim=250 (時系列) |
| 音楽情報処理 | 楽曲埋め込みの可視化 | n=10⁴, dim=128 |
| 言語学 | 方言・古典作品の特徴空間 | n=10², dim=50 |
| 気候科学 | 気候パターンのクラスタリング | n=10³, dim=100 |
| 都市計画(SSDSE 等) | 地域特性の類型化 | n=10²-10³, dim=10-100 |
| 農学 | 品種特性の遺伝子発現解析 | n=10², dim=10⁴ |
| 心理学 | 性格テスト結果の類型化 | n=10³, dim=30-50 |
| スポーツ分析 | 選手プレイスタイルのクラスタ | n=10², dim=20-50 |
SSDSE-B-2026 のような社会統計データは n=47 と小さいですが、 同じ方法論で 細胞・分子・株価・音楽などへ展開できます。 「高次元データの可視化」というニーズは分野を問わず普遍的です。
KL ダイバージェンス $\mathrm{KL}(P \| Q) = \sum p \log (p/q)$ は 非対称です。 t-SNE で $\mathrm{KL}(P \| Q)$ を最小化することの含意:
つまり「高次元で近い→低次元でも近く」は 強く保たれるが、 「高次元で遠い→低次元では近くてもよい」となる。 これが「局所構造優位、 大域構造を犠牲」の数学的根拠。
対義の手法として、 $\mathrm{KL}(Q \| P)$ を最小化する設計も理論的に可能(mode-seeking)。 ただし t-SNE は前者を採用しているため、 クラスタ内部は綺麗だがクラスタ間距離はあてにならない。
t-SNE は「高次元の確率分布 $P$ を低次元の $Q$ で 符号化するときの効率」を測っているとも言えます。 KL は「$Q$ を使って $P$ を符号化する際の余分なビット数」。 これを最小化することで、 「低次元表現が高次元分布の情報をなるべく保つ」配置を得ます。
勾配方程式を見ると、 各点に ばね力(引力)と クーロン力(斥力)が同時に働く描像になります。 これは N 体重力シミュレーションと数学的に同型で、 Barnes-Hut アルゴリズムが流用できる理由でもあります。
| 処理 | 必要性 | 理由 |
|---|---|---|
| 欠損値処理(dropna / fillna) | 必須 | NaN があると距離計算で NaN が伝播 |
| 標準化(Z-score) | 強く推奨 | 単位の異なる列を統一スケールに |
| 外れ値処理(IQR / Winsorize) | 場合により | 極端な外れ値が距離行列を支配することあり |
| 対数変換(log1p) | 推奨(偏った分布) | 「人口」「事業所数」など右に長い尾の分布に |
| PCA 圧縮(dim > 50) | 高次元なら必須 | ノイズ除去・計算速度向上 |
| カテゴリ変数の数値化 | 必要時のみ | One-hot は次元が爆発するため注意 |
| サンプリング | n > 50,000 なら検討 | 計算時間とトレードオフ |
特に SSDSE-B-2026 のような社会統計では、 「総人口」のような桁が大きい列が他を圧倒するため、 標準化または対数変換は必須です。
n_iter を 3000-5000 まで増やすと、 中間結果と最終形が全く違うことに気づきます。init='pca' がデフォルト推奨。 ランダム初期化だと毎回大きく結果が変わり、 大域構造が極めて不安定になります。StandardScaler 等で標準化を。transform() がありません。 「新しい都道府県データを既存の埋め込みに追加」したい場合は UMAP に切り替えるか、 全データを再学習する必要があります。| レイヤ | 位置づけ | 具体技術 |
|---|---|---|
| 上位 | 機械学習の大分類 | 教師なし学習 → 次元削減 |
| 並列 | 線形次元削減 | PCA / SVD / NMF |
| 並列 | 非線形次元削減(多様体学習) | t-SNE / UMAP / Isomap / LLE / MDS |
| 並列 | 深層学習ベース | Autoencoder / VAE / SimCLR |
| 下位 | t-SNE の構成要素 | ガウス/t 分布カーネル, KL 発散, 勾配降下 |
| 下位 | 派生・改良 | Barnes-Hut t-SNE / openTSNE / FIt-SNE |
| 後段 | 埋め込み後の処理 | k-means / HDBSCAN / silhouette |
| 前段 | 前処理 | 標準化(StandardScaler), PCA 圧縮 |
t-SNE は「教師なし学習 → 次元削減 → 多様体学習」という樹形図の中で 非線形・確率的・可視化特化のポジションを占めます。 同じツリーで UMAP, MDS, Isomap, LLE が並列に並びます。
t-SNE は高次元データの 2D / 3D 可視化に特化し、 上流の前処理と下流のクラスタ解釈をつなぐ。
SSDSE-B-2026 の 47 県 × 50 指標 (人口・経済・福祉) を t-SNE で 2D に落とすと、 「東京・大阪・愛知」の大都市群、 「島根・鳥取」の過疎群、 「沖縄」の特異群が視覚的に分離して見える。
次元削減手法の選択は、 サンプル数と目的で 4 通りに分岐する。
SSDSE-B-2026 の 47 県の小さなデータなら t-SNE で十分。 ただし、 「神奈川と東京の距離 = 沖縄と北海道の距離」が真の意味で同じとは限らない (t-SNE は局所のみ正確) ため、 必ず元の高次元データで再確認する。
本ページの他セクションは「t-SNE をどう使うか」を扱いました。 この深化セクションは一歩進んで、 出来上がった 2D 図が信用に足るかを数値で検証する視点を扱います。 t-SNE の唯一の約束は「高次元での近傍関係の保存」なので、 その約束が守られたかは 自分で測って確かめられるのです。
t-SNE は「友達同士を近くの席にする席替え」です。 席替え後の教室(2D 図)を見て「A 班と B 班が離れている」と語る前に、 そもそも誰と誰が友達だったか(高次元空間での最近傍)を先に確認しておけば、 図の妥当性を自力で判定できます。 実際に SSDSE-B-2026 の 2023 年・47 都道府県について、 ヘッダで意味を確認済みの 6 指標(A1101 総人口・A1303 65歳以上人口・A4103 合計特殊出生率・B4101 年平均気温・C5401 住宅地標準価格・L3221 消費支出)を標準化し、 ユークリッド距離で最近傍 3 県を計算した実測結果が次の表です。
| 基準の県 | 最近傍 1 位(距離) | 2 位(距離) | 3 位(距離) |
|---|---|---|---|
| 東京都 | 神奈川県(4.39) | 埼玉県(5.51) | 大阪府(5.59) |
| 鳥取県 | 福井県(0.36) | 佐賀県(0.78) | 山口県(0.84) |
| 沖縄県 | 鹿児島県(2.68) | 宮崎県(2.89) | 長崎県(2.96) |
| 北海道 | 岩手県(2.57) | 宮城県(2.73) | 秋田県(3.00) |
| 広島県 | 岡山県(0.78) | 静岡県(0.88) | 岐阜県(0.90) |
注目すべきは距離の桁の違いです。 鳥取県は最近傍の福井県まで 0.36 と密集地帯にいる一方、 東京都は最も近い神奈川県ですら 4.39。 つまり東京都は標準化 6 次元空間で 孤立点であり、 t-SNE 図で東京都がどのクラスタの「そば」に描かれても、 その近接は高次元では裏付けがない、 と図を見る前から判断できます。 これが「高次元側の答えを先に持っておく」検証の威力です。 なお 2023 年の合計特殊出生率(A4103)は最小 0.99(東京都)〜最大 1.60(沖縄県)で、 この 1 変数だけでも東京都の外れ具合が現れています。
(1)KL 損失を perplexity 間で比較してしまう。 「KL が小さい perplexity が最良」と考えたくなりますが、 これは誤りです。 perplexity を変えると 目標分布 $P$ 自体が変わるため、 異なる perplexity の KL 値は「別の試験の点数」であり比較できません。 上記 6 指標・47 都道府県での実測(scikit-learn 1.9.0、 random_state=42、 init='pca'。 乱数・版数依存のため参考値):
| perplexity | KL 損失 | trustworthiness(k=5) | kNN 保存率(k=5) |
|---|---|---|---|
| 5 | 0.436 | 0.913 | 0.660 |
| 15 | 0.135 | 0.950 | 0.698 |
| 30 | 0.031 | 0.959 | 0.711 |
KL は perplexity=30 で最小に見えますが、 これは目標が緩くなった結果でもあります。 比較には KL ではなく、 スケールが共通の trustworthiness や kNN 保存率(下の発展を参照)を使います。 また kNN 保存率が 0.66〜0.71 に留まる点も重要な事実で、 「n=47 のような小データでも、 最近傍 5 件のうち約 3 割は 2D 図で入れ替わる」ことを意味します。 図上の「隣どうし」を根拠に議論するときは、 この誤差を織り込む必要があります。
(2)n より大きい perplexity を指定する。 47 都道府県データに perplexity=50 を与えると、 scikit-learn 1.9.0 では実測で ValueError: perplexity (50) must be less than n_samples (47) が送出されます(古い版では警告のみで通ることがあり、 結果は無意味)。 小標本では perplexity の上限が構造的に決まる、 という点は見落とされがちです。
(3)「クラスタが見えた=構造がある」と即断する。 t-SNE は完全な無相関ノイズに対しても、 perplexity が小さいと塊状の模様を作り出すことが知られています(Distill "How to Use t-SNE Effectively" が示した現象)。 対策は本物のデータで検証手続きを持つこと:列を個別にシャッフルして相関を壊した「壊しデータ」で同じ t-SNE を実行し、 本物と同程度に綺麗なクラスタが出るなら、 その見た目は構造の証拠になりません(この比較実験は演習として推奨。 実行結果は各自の環境で確認してください)。
sklearn.manifold.trustworthiness(X, Y, n_neighbors=k) で計算できる 0〜1 の指標。 「2D 図で近傍に 侵入してきた偽物」の量をペナルティ化します。 1 に近いほど「図で近い=元でも近い」が成立。 逆方向(元で近いのに図で離れた「脱落」)を測る continuity と対で使うと網羅的です。transform() はありませんが、 parametric t-SNE(ニューラルネットで埋め込み関数自体を学習)や openTSNE の補間ベース投影を使うと、 新しい点を既存の地図に「後から載せる」ことができます。 検証指標と組み合わせれば、 投影の劣化も定量評価できます。