概要
PCAを使う用事があったのでscikit-learnを利用する。
バージョン情報
- scikit-learn==0.21.2
- Python 3.7.3
サンプルデータ
ランダムにユーザーにアイテムを割り振るサンプルデータを生成してみる。
要件は下記。
- ユーザー数は1万
- アイテム数は3000
- 1ユーザーにつき1〜100まで可変数のアイテムを持つ
import numpy as np import scipy.sparse # アイテムID一覧 ITEM_COUNT = 3000 item_ids = np.arange(ITEM_COUNT) # ユーザーのアイテムをランダム生成 USER_COUNT = 10000 mtrx = scipy.sparse.dok_matrix((USER_COUNT, ITEM_COUNT), dtype=np.int8) for user_id in range(USER_COUNT): user_item_count = np.random.randint(100) user_items = np.random.choice(item_ids, user_item_count) for item_id in user_items: mtrx[user_id, item_id] = 1 mtrx = mtrx.transpose().tocsr()
適当なデータなので何かしら特徴が取れるわけではないけど、これでPCAを実行してみる。
scikit-learnでPCA
PCAの実行。
とりあえず2次元にしてみる。
from sklearn.decomposition import PCA pca = PCA(n_components=2) pca.fit(mtrx)
実行結果
TypeError: PCA does not support sparse input. See TruncatedSVD for a possible alternative.
・・・あれ? SparseVectorだとダメなの?
TruncatedSVDを使えと言われる。PCAと意味合いとしてはだいたい同じらしい。
from sklearn.decomposition import TruncatedSVD pca = TruncatedSVD(n_components=2) new_mtrx = pca.fit_transform(mtrx) from matplotlib import pylab as plt plt.scatter(x=new_mtrx[:, 0], y=new_mtrx[:, 1], alpha=0.1)
ランダムに生成したので実行結果もいかにもランダムな形。
結果の保存
出来上がったモデルとsparseなテストデータ、denseな次元圧縮結果を保存しておく。
import pickle # 学習データの保存 scipy.sparse.save_npz('train_sparse.npz', scipy.sparse.coo_matrix(mtrx)) # 次元圧縮結果の保存 np.save('comp', new_mtrx) # モデルの保存 with open('pca_model.pkl', 'wb') as f: pickle.dump(pca, f)
保存結果のロード
# 学習データのロード mtrx = scipy.sparse.load_npz('train_sparse.npz') # 次元圧縮結果のロード new_mtrx = np.load('comp.npy') # モデルのロード with open('pca_model.pkl', 'rb') as f: pca = pickle.load(f) # ロードしたモデルで次元圧縮 pca.transform(mtrx)
手書き数字の2次元化
ランダムだと何やってるのかわかりづらいので手書き文字の0〜4を2次元にしてみる。
手書き文字のサンプルデータ取得。
import sklearn.datasets digits, label = sklearn.datasets.load_digits(return_X_y=True) digits = digits / 255
digitsに64次元(8x8)のデータが入ってるのでそれをPCAして0〜4をplot。
from sklearn.decomposition import TruncatedSVD pca = TruncatedSVD(n_components=2) digits2d = pca.fit_transform(digits) from matplotlib import pylab as plt for i in range(5): target = digits2d[label == i] plt.scatter(x=target[:, 0], y=target[:, 1], label=str(i)) plt.legend(bbox_to_anchor=(1.02, 1), loc='upper left')
ほどほどにわかれた結果が取れた。4は比較的分類しやすく、2と3や0と1はかなり混ざっている。
改定履歴
Author: Masato Watanabe, Date: 2019-06-22, 記事投稿