見出し画像

第13章: 教師なし学習編 ~線形次元削減~ 第0節 次元の呪い

この章では主に主成分分析(PCA)について学びます。

本章のモチベーション

言いたいこととしては、特徴量のサンプル数が多ければ多いほど分散が大きくなりやすいということです。同じ特徴量だったとしても、100個しか観測値がない場合と、10万個観測値がある場合で、例え相関性が強かったとしても、フィッティングする際に評価関数で用いる距離がどうしても大きくなるので、サンプル数が少ない方が良いモデルと誤認される恐れがあります。

そのため、ここでは、観測値の数に応じてどの程度距離が異なるのかについて示していき、本章では次元削減を行ってより良いモデルを作れるデータセットを作っていきたいと思います。

次元の呪い

データセットの次元数が増加することは、ユークリッド空間における各観測値を表す特徴量のベクトルがより多いことを表します。ここでは、正則化されたリッジ回帰モデルを訓練するために線形回帰係数のベクトルに適用したユークリッド距離(L2ノルムとも呼ばれる)を使用して、ベクトル空間の距離を測定します。

p=(p_1, p_2, ..., p_n)と q=(q_1, q_2, ..., q_n)の距離dは
d(p, q) = √{Σ(p_i - q_i)^2}
と定義します。(数式を使う場合はやはりQiitaの方が便利ですね...)

したがって、新しい次元ごとに合計に非負の項が追加されるため、距離は個別のベクトルの次元数とともに増加します。言い換えると、特定の数の観測値に対して特徴量の数が増えると、特徴量空間はますます疎らになります。つまり、密度が低くなるか、空になります。反対に、データ密度が低いほど、観測値間の平均距離を同じに保つために、より多くの観測が必要になります。

インポートと設定

%matplotlib inline

import pandas as pd
import numpy as np
from numpy import clip, full, fill_diagonal
from numpy.random import uniform, multivariate_normal, seed
from scipy.spatial.distance import pdist, squareform
import matplotlib.pyplot as plt
import seaborn as sns
seed(42)
sns.set_style('white')

R^nの観測点でnが増加した時の距離をシミュレーション

シミュレーションでは、無相関の均一分布または相関のある正規分布から[0, 1]の範囲の特徴を描画し、特徴量の数を徐々に2,500に増やします。

距離の計算

def get_distance_metrics(points):
   """Calculate mean of pairwise distances and 
       mean of min pairwise distances"""
   pairwise_dist = squareform(pdist(points))
   fill_diagonal(pairwise_dist, np.nanmean(pairwise_dist, axis=1))
   avg_distance = np.mean(np.nanmean(pairwise_dist, axis=1))
   fill_diagonal(pairwise_dist, np.nanmax(pairwise_dist, axis=1))
   avg_min_distance = np.mean(np.nanmin(pairwise_dist, axis=1))
   return avg_distance, avg_min_distance

距離のシミュレーション

def simulate_distances(m, n, mean, var, corr):
   """Draw m random vectors of dimension n 
       from uniform and normal distributions
       and return pairwise distance metrics"""
   uni_dist = get_distance_metrics(uniform(size=(m, n)))
   cov = full(shape=(n, n), fill_value=var * corr)
   fill_diagonal(cov, var)
   normal_points = multivariate_normal(
       full(shape=(n,), fill_value=mean), cov, m)
   normal_points = clip(normal_points, a_min=0, a_max=1)
   norm_dist = get_distance_metrics(normal_points)
   return uni_dist, norm_dist

パラメーターのサンプリング

n_points = 1000
min_dim, max_dim, step = 1, 2502, 100 # from 1 - 2501
dimensions = range(min_dim, max_dim, step)

正規分布のパラメーター

mean = 0.5 
var = (mean/3)**2 # 99% of sample in [0, 1]
corr = 0.25

シミュレーションの実行

col_names = ['Avg. Uniform', 'Min. Uniform', 'Avg. Normal', 'Min. Normal']
avg_dist = []
for dim in dimensions:
   uni_dist, norm_dist = simulate_distances(n_points, dim, mean, var, corr)
   avg_dist.append([*uni_dist, *norm_dist])

distances = pd.DataFrame(data=avg_dist, columns=col_names, index=dimensions)

単位超立方体の点の平均距離と最小距離をプロット

title = 'Distance of {:,.0f} Data Points in a Unit Hypercube'.format(n_points)
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(14, 8))
distances[[ 'Avg. Uniform', 'Avg. Normal']].plot.bar(title='Average ' + title, ax=axes[0], rot=0)
distances[[ 'Min. Uniform', 'Min. Normal']].plot.bar(title='Minimum ' + title, ax=axes[1], rot=0)

for ax in axes:
   ax.grid(axis='y', lw=1, ls='--')
   for p in ax.patches:
       ax.annotate('{:.1f}'.format(p.get_height()), (p.get_x() + .005, p.get_height() + .25), fontsize=10)
   ax.set_ylabel('Distance')

axes[1].set_xlabel('Dimensionality')
sns.despine()        
fig.tight_layout();

画像1



この記事が気に入ったらサポートをしてみませんか?