データ分析・GA4

t-SNEとは?仕組みとperplexityの決め方、Python(scikit-learn)での2D・3D可視化

t-SNEとは?仕組みとperplexityの決め方、Python(scikit-learn)での2D・3D可視化

t-SNE(ティースニー)は、数十〜数千次元のデータを2次元か3次元の散布図に落とし、似たデータどうしが近くに集まる様子を目で確かめるための手法です。2008年にvan der MaatenとHintonが発表し、scikit-learnでは sklearn.manifold.TSNE として数行で使えます。一方で、図の大きさや距離には読んではいけない情報が混ざり、perplexityや初期化の設定で見た目が変わります。この記事では、原論文に沿った仕組みの説明、PCA・MDS・UMAPとの違い、scikit-learn 1.9.1での2D・3D可視化の手順を、手元で実行した数値つきでまとめました。

まとめ

  • t-SNEは高次元データの近傍関係を保って2次元・3次元に配置する可視化手法で、クラスタリング手法でも汎用の次元削減手法でもありません。
  • SNEからの改良点は、低次元側に自由度1のt分布を使って混雑問題を和らげたことと、対称化で最適化しやすくしたことです。
  • scikit-learn 1.9.1の既定は perplexity=30.0・init="pca"・method="barnes_hut" で、barnes_hutは3次元までです。4次元以上を指定するとValueErrorになります。
  • perplexityは5〜50が原論文の目安です。手書き数字1,797件の実測では、近い点の保存度は30で最大、広めの近傍の保存度はperplexityを上げるほど高くなりました。
  • クラスタの大きさとクラスタ間の距離は信用せず、乱数データからもかたまりが生じます(実測でシルエット係数0.018が0.388に上昇)。
  • scikit-learnのTSNEはtransformを持たず、新しいデータを既存の座標系へ変換する用途にはそのまま使えません。その用途ではopenTSNEかUMAPを選びます。

以下、仕組み、他の手法との違い、Pythonでの手順、パラメータの実測、図の読み方の順に説明します。

t-SNEの意味と読み方

t-SNEは t-distributed Stochastic Neighbor Embedding の略で、日本語では「t分布型確率的近傍埋め込み法」と呼ばれます。読み方は「ティースニー」が一般的です。原論文は Laurens van der Maaten、Geoffrey Hinton「Visualizing Data using t-SNE」(Journal of Machine Learning Research 9巻、2579〜2605頁、2008年)で、要旨は「各データ点に2次元または3次元の地図上の位置を与えて高次元データを可視化する手法」と説明しています。

名前の「SNE」は、2002年にHintonとRoweisが提案したStochastic Neighbor Embeddingを指し、「t」はその改良で低次元側にスチューデントのt分布を使うことを表します。目的は最初から人が見るための地図づくりであり、機械学習モデルに渡す特徴量を作る用途は想定の中心にありません。この前提が、後で述べる「使うべきでない場面」につながります。

t-SNEの仕組み:SNEから変わった点

高次元側の近傍確率とperplexityの役割

t-SNEはまず、高次元空間で点iから見た点jの近さを、点iを中心とするガウス分布にもとづく条件付き確率として表します。ガウス分布の幅σiは点ごとに異なり、ユーザーが指定したperplexityに合うよう二分探索で決まります。原論文はperplexityを「Perp(Pi) = 2のH(Pi)乗(H(Pi)はビット単位のシャノンエントロピー)」と定義し、「実効的な近傍数のなめらかな尺度」と解釈できるとしています。密な領域では幅が狭く、疎な領域では幅が広くなるため、点ごとにおよそperplexity個ぶんの近傍を見る計算になります。

t-SNEではこの条件付き確率を対称化した同時確率pijを使います。SNEは条件付き確率のまま最適化していたため、勾配が複雑で最適化が難しいという問題がありました。

低次元側に自由度1のt分布を使う理由

SNEの弱点として原論文が挙げるのが「混雑問題(crowding problem)」です。高次元では中程度に離れた点を大量に配置できますが、2次元の地図には同じ数を収める面積がありません。結果として点が地図の中央に押し込まれ、かたまりが分離しなくなります。

t-SNEは低次元側の近さqijを、ガウス分布より裾の重い自由度1のスチューデントのt分布(コーシー分布と同じ)で定義します。裾が重いと、高次元で中程度に離れていた点を地図上ではより遠くに置いても確率が大きく下がりません。これでかたまりどうしの間に余白が生まれ、中央への密集が和らぎます。散布図でクラスタがくっきり分かれて見えるのはこの設計の効果で、裏返せば、かたまりの間の余白の広さは元データの距離をそのまま表していません。

KLダイバージェンスが近い点を優先する理由

最適化の目的関数は、高次元側の分布Pと低次元側の分布QのKLダイバージェンスです。原論文は、KLダイバージェンスが非対称なので距離の誤りが等しく扱われず、「近いデータ点を離れた地図点で表すコストは大きいが、離れたデータ点を近い地図点で表すコストは小さい」と説明しています。t-SNEが局所構造(近所の関係)の保存を優先し、大域構造(遠い関係)を犠牲にするのはこのためです。目的関数は凸でもないため、初期値が違えば別の局所解に落ちます。

計算量とBarnes-Hut近似

原論文の時点では、計算量とメモリ量はデータ点数の2乗に比例し、1万点を大きく超えるデータには標準版を適用できないとされていました。2014年の van der Maaten「Accelerating t-SNE using Tree-Based Algorithms」(JMLR 15巻、3221〜3245頁)が、勾配をO(N log N)で近似するBarnes-Hut版を示し、数百万件の埋め込みを可能にしました。scikit-learnの既定 method="barnes_hut" はこの近似で、method="exact" がO(N²)の厳密計算です。

手元の環境(Intel Mac、scikit-learn 1.9.1)で1,000件・30次元のデータを比べると、barnes_hutは5.0秒、exactは48.5秒でした。件数が増えるほど差は広がります。exactを選ぶのは、件数が少なく近傍の誤差を詰めたいときに限られます。

PCA・MDS・UMAPとの違い

t-SNEと並んで比べられる3手法との違いを、保つ構造と新データへの対応で整理します。

手法 写像 主に保つ構造 同じ入力の結果 新データの変換
PCA 線形 分散が大きい方向 乱択解法では乱数に依存 transformで可
MDS 距離を合わせる配置 点どうしの距離 反復解法は初期値で変わる 不可
t-SNE 非線形 近傍関係(局所) 初期化で変わる 不可(scikit-learn)
UMAP 非線形 近傍グラフの構造 乱数で変わる transformで可

PCAとの差は、局所の保存度を数値にすると分かりやすくなります。scikit-learnの trustworthiness(低次元で近い点が元の空間でも近い度合い、1が最大)を手書き数字データで測ると、PCAの2次元は0.8181、t-SNEの2次元は0.9917でした。PCAは軸の意味(寄与率・負荷量)を説明できるのが強みで、読み方は主成分分析(PCA)とは?固有値・寄与率・負荷量の読み方を実データで解説にまとめています。

MDSは、scikit-learnのユーザーガイドの説明どおり「低次元での距離が元の高次元の距離に近くなる配置」を探す手法です。遠い点どうしの距離も合わせにいくので、全体の位置関係を読みたいときに向きます。t-SNEは近所の関係だけを優先し、遠い距離は捨てます。ストレス値の目安などは多次元尺度構成法(MDS)とは?主成分分析との違い、ストレス値の目安とPythonでの実例で扱っています。

Python(scikit-learn 1.9.1)でt-SNEを動かす手順

ここでは、scikit-learnに同梱されていてダウンロード不要な手書き数字データ(load_digits、1,797件×64次元、0〜9の10クラス)を使います。scikit-learnそのものの導入と基本はScikit-learnとは?読み方・できること・使い方を実行例で解説を参照してください。

前処理での標準化とPCAによる次元削減

TSNEのAPIリファレンスは、特徴量が非常に多い場合は先に密なデータならPCA、疎なデータならTruncatedSVDで50次元程度に落とすことを強く推奨しています。ノイズが抑えられ、距離計算も速くなるためです。必須の手順ではなく、目的は計算の軽量化とノイズの抑制です。今回のデータは64次元なので、PCAで30次元に落としています。

3次元t-SNEとPlotlyによる散布図の表示

次のコードは、3次元のt-SNEを実行して回転できる散布図をHTMLに書き出します。Plotly 7のexpress(px.scatter_3d)は配列を直接渡すとpandasを要求するため、依存の少ない graph_objects を使っています。

import plotly.graph_objects as go
from sklearn.datasets import load_digits
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE, trustworthiness

X, y = load_digits(return_X_y=True)          # 手書き数字 1,797件×64次元
X_std = StandardScaler().fit_transform(X)
X_pca = PCA(n_components=30, random_state=0).fit_transform(X_std)

tsne = TSNE(n_components=3, perplexity=30, random_state=0)
emb = tsne.fit_transform(X_pca)
print(emb.shape, round(tsne.kl_divergence_, 3), tsne.learning_rate_)
print(round(trustworthiness(X_std, emb, n_neighbors=5), 4))

fig = go.Figure(go.Scatter3d(
    x=emb[:, 0], y=emb[:, 1], z=emb[:, 2], mode="markers",
    marker=dict(size=2, color=y, colorscale="Turbo", showscale=True),
    text=[f"digit={d}" for d in y],
))
fig.write_html("tsne_digits_3d.html", include_plotlyjs="cdn")

実行結果は次のとおりです(Python 3.12.13、scikit-learn 1.9.1、numpy 2.5.4、plotly 7.1.0)。

(1797, 3) 0.692 50.0
0.9948

2次元にするなら n_components=2 に変え、go.Scattergl などで描きます。同じデータの2次元はKLダイバージェンス0.829、trustworthiness 0.9917、実行時間4.4秒で、3次元は0.692、0.9948、10.7秒でした。3次元のほうが保存度はわずかに高い反面、計算時間は約2.4倍で、紙やスライドに載せると奥行きが伝わりません。報告資料なら2次元、手元で回して確かめる探索なら3次元、という分け方が実用的です。

Barnes-Hut法の出力次元数の上限

既定のbarnes_hutは四分木(2次元)と八分木(3次元)で近似するため、4次元以上を指定すると次のエラーで止まります。

ValueError: 'n_components' should be inferior to 4 for the barnes_hut algorithm as it relies on quad-tree or oct-tree.

method="exact" にすれば4次元以上も計算できますが、計算量は件数の2乗です。原論文は、3次元を超える次元に落とす用途では自由度1より大きいt分布のほうが適切だろうと述べています。scikit-learnのTSNEには自由度を直接指定する引数はなく、内部で max(n_components - 1, 1) として決まります(2次元では1、3次元では2、4次元では3)。つまりscikit-learnの3次元t-SNEは、原論文の自由度1ではなく自由度2のt分布を使っています。4次元以上の特徴量が欲しいなら、計算量の面からもPCAかUMAPを使います。

1.2・1.5で変わった引数と旧コード

古い記事のコードをそのまま動かすと、止まる変更と結果が変わる変更の2種類に当たります。

  • 止まる変更:1.5で n_iter が max_iter に改名され、1.9.1では旧名を受け付けません。TSNE(n_iter=500) は TypeError: TSNE.__init__() got an unexpected keyword argument 'n_iter' になります。
  • 図が変わる変更:1.2で learning_rate の既定が "auto"、init の既定が "pca" になりました。旧コードと同じ図を再現したいなら、当時の値を明示します。

"auto" の学習率は max(N / early_exaggeration / 4, 50) で、1,797件なら1,797÷12÷4≒37.4となり、下限の50が使われます(上の出力の50.0)。また、scikit-learnの learning_rate はbhtsne・FIt-SNE・openTSNEなどより4倍大きい定義で、APIリファレンスは「scikit-learnの200が他実装の800に相当する」と明記しています。他実装の記事の値を持ち込むときは4で割ります。

perplexityと乱数の実測比較

perplexityの選び方

原論文は、SNEの性能はperplexityの変化に比較的頑健で、典型的な値は5〜50としています。Distillの「How to Use t-SNE Effectively」(Wattenberg、Viégas、Johnson、2016年10月13日)は、そのうえで複数のperplexityで描いた図を見比べるよう勧めています。次の表は、上のコードと同じ標準化・PCA処理を行った手書き数字データで、n_components=2、random_state=0とし、perplexityだけを変えた結果です。trustworthinessは標準化後の64次元データを基準に計算しています。

perplexity trustworthiness(5近傍) trustworthiness(30近傍) 実行時間
5 0.9910 0.9565 3.1秒
30(既定) 0.9917 0.9709 4.7秒
50 0.9912 0.9717 5.9秒
100 0.9886 0.9733 6.3秒

5近傍で見た保存度は30が最も高く、30近傍で見た保存度はperplexityを上げるほど高くなりました。小さい値は隣どうしの関係に、大きい値は広めのまとまりに寄るということで、正解の値が1つあるわけではありません。既定の30から始め、5前後と50前後でも描いて、複数の設定に共通するかたまりを候補として、元の特徴空間でも分離を確認するのが無難です。

KLダイバージェンス(kl_divergence_)はperplexity 5で0.962、100で0.667と下がりますが、perplexityを変えると比べる分布P自体が変わるため、値の大小でperplexityを選んではいけません。KLで比べてよいのは、同じperplexityで乱数だけを変えた実行どうしです。なお、perplexityはサンプル数未満でなければならず、20件で既定の30を使うと ValueError: perplexity (30) must be less than n_samples (20) になります。

random_stateとinitの影響

既定の init="pca" のまま random_state を0・1・2と変えると、今回のデータでは3回ともKLダイバージェンス0.829で、Procrustes解析(回転・反転・拡大縮小を合わせたうえでの座標の差)による差は0.0(小数第4位で丸め)で、ほぼ同じ配置になりました。init="random" にして乱数を0と1で比べると差は0.6518で、配置が別物になります。

ユーザーガイドは、乱数を変えて複数回実行し、誤差(KLダイバージェンス)が最小の埋め込みを選ぶのは正当だとしています。大域構造が保存されない弱点はPCA初期化で和らぐとも書かれています。今回のデータで乱数の差が出なかったのは、1.2で初期化の既定がrandomからpcaに変わったためです。ただし、TSNE内部のPCA初期化には random_state が渡されており、データの形によってPCAが乱択解法を選ぶと乱数の影響を受けます。再現性が必要なら init="pca" と random_state の両方を固定します。

t-SNE図の読み方で誤りやすい3点

クラスタの見かけの大きさ

Distillの記事は、t-SNE図のクラスタの大きさは意味を持たないと説明しています。t-SNEは点ごとにガウス分布の幅を変え、密なかたまりは広げ、疎なかたまりは縮めるからです。「このかたまりは広がっているからばらつきが大きい」という読み方はできません。件数の多さを伝えたいなら、点の数を数えるか、凡例に件数を書きます。

クラスタ間の距離

同じ記事は、よく分かれたクラスタ間の距離は意味を持たない場合があるとしています。前述のとおり、t分布の重い裾がかたまりの間に余白を作り、KLダイバージェンスは遠い点の誤配置をほとんど罰しません。「AとBのかたまりが近いので似ている」と言いたいなら、元の空間でかたまりの重心どうしの距離を計算して確かめます。

乱数データから現れるかたまり

Distillは、ランダムなデータがいつもランダムに見えるとは限らないとも指摘しています。手元で、構造の無い正規乱数(500件×50次元)にt-SNEをかけ、k-means(k=5)のシルエット係数を測りました。元の50次元では0.018とほぼ構造なしですが、t-SNE後の2次元ではperplexity 2で0.388、30でも0.313まで上がりました。図の上できれいに分かれていることは、元データにかたまりがある証拠になりません。

t-SNE座標でのクラスタリングの適否

t-SNEの出力は座標だけで、クラスタ番号は返しません。そこで「t-SNEの2次元座標にk-meansをかければよい」と考えがちです。手書き数字データ(正解ラベルあり)で、k-means(k=10)の結果と正解の一致度(調整ランド指数、1が完全一致)を比べると、PCAの30次元にかけた場合は0.555、t-SNEの2次元座標にかけた場合は0.759でした。よく分かれたデータでは、t-SNE座標のほうが正解に近いグループ分けになることがあります。

それでも、t-SNE座標でのクラスタリングを結論や本番処理に使うのは避けるべきです。理由は3つの実測と仕様にあります。前節のとおり乱数データでもシルエット係数が0.018から0.313〜0.388に上がるので、座標上の分離を根拠に「グループが存在する」と判断できません。perplexityやinitで配置が変わり、グループ分けもそれに引きずられます。さらに、scikit-learnのTSNEには新規データ用のtransformがなく、学習済みの座標系で新規データのクラスタを割り当てる処理には、そのまま使えません。

実務では、グループ分けは元の空間(またはPCA後)でk-meansや階層的クラスタリングを行い、t-SNEはその結果を色として重ねて確かめる表示装置にとどめます。クラスタ数の決め方はデンドログラムとは?階層的クラスタリングの見方とクラスタ数の決め方、検索キーワードを意味で束ねる具体的な手順はキーワードクラスタリングとは?GSCデータをPythonで束ねる手順と日本語の落とし穴で解説しています。

t-SNEを使うべきでない場面

新規データ追加におけるtransformの必要性

scikit-learnのTSNEが持つのは fit と fit_transform だけで、transform はありません(1.9.1で hasattr(TSNE(), "transform") はFalse)。毎月のデータを同じ座標系に追加する定点観測や、学習済みの地図に新規データを投影するダッシュボードには組み込めません。全件で毎回作り直すと配置も変わります。この用途なら、既存の埋め込みに新しい点を配置する openTSNE(1.0.4の TSNEEmbedding.transform)か、umap-learn(0.5.12の UMAP.transform)を選びます。

大規模データの計算負荷と特徴量生成の制約

ユーザーガイドは、t-SNEは計算コストが高く、PCAが数秒〜数分で終わる百万件規模のデータで数時間かかることがあると述べています。件数が多いなら、まずPCAやUMAPで全体を見て、t-SNEは抽出したサンプルに絞ります。また、後段のモデル用に新規データも同じ規則で変換する必要があるなら、transformを持たないscikit-learnのTSNEは不向きです。この場合はPCAかUMAPを使います。データの規模やモデルの組み込み方から手法を選ぶ段階で迷う場合は、機械学習モデル開発の支援でもご相談を受けています。

t-SNEの軸の解釈とPCAの説明可能性

t-SNEの軸には意味がなく、「横軸が大きいほど〇〇」とは言えません。報告で「どの変数が差を生んでいるか」を説明する必要があるなら、負荷量で軸を解釈できるPCAが向いています。t-SNEの図は「かたまりがありそうか」を見当づける探索段階の道具と割り切るのが安全です。

よくある質問

t-SNEの読み方は?

「ティースニー」と読むのが一般的です。正式名称はt-distributed Stochastic Neighbor Embeddingで、日本語では「t分布型確率的近傍埋め込み法」と呼ばれます。tは低次元側に使う自由度1のスチューデントのt分布を指します。

PCAとt-SNEの違いは何ですか?

PCAは分散が大きい方向を線形に探す手法で、乱択解法では再現性のために乱数の固定が必要です。また、新しいデータもtransformで変換できます。t-SNEは近傍関係を保つ非線形の手法で、かたまりの分離はPCAより見やすい一方、軸の意味や距離は解釈できません。scikit-learnはt-SNEの前処理としてPCAを推奨しており、競合ではなく併用する関係です。

多次元尺度構成法(MDS)とt-SNEの違いは何ですか?

MDSは低次元での点どうしの距離を元の距離に近づける手法で、遠い点の位置関係も保とうとします。t-SNEは近い点の関係だけを優先し、遠い点の距離は保存しません。全体の配置を読みたいならMDS、近いもの同士のかたまりを見たいならt-SNEです。

TSNEでn_iterを指定するとエラーになるのはなぜですか?

scikit-learn 1.5で引数名が n_iter から max_iter に変わり、1.9.1では旧名を受け付けないためです。max_iter=1000 のように書き換えれば動きます。既定値は1000です。

t-SNEで4次元以上に圧縮できますか?

既定の method="barnes_hut" では3次元までで、4以上はValueErrorになります。method="exact" なら計算できますが、計算量は件数の2乗です。4次元以上が必要な用途は可視化ではないことが多いため、PCAかUMAPを検討してください。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.29 テックブログ ソニー生命の不正アクセス:38.5万件の対象確認と窓口、停止中の手続き
  2. 2026.10.31 テックブログ osinaの情報漏洩:約23万人の対象確認とSNS連携の解除、分析ツールへ渡すデータの絞り方
  3. 2026.10.31 テックブログ 松屋の不正アクセス:237万件の対象確認と窓口、漏れたパスワードの守り方
  4. 2026.11.01 テックブログ RAGTAGの情報漏洩:延べ約120万件の対象項目と会員の備え、侵入から検知まで11日の意味
  5. 2026.05.18 テックブログ マネーフォワードで連携できない・失敗するときの対処法|エラー別の原因と再連携のやり方

RELATED POSTS 関連記事

目次