2018年06月15日

Pythonで自作ライブラリのパスを設定する

エモトです.みなさまDP2はご覧になりましたでしょうか.こまけぇことはいいんだよのAAがよく似合うエピローグ(いや全編でか),これが映画なんだよと圧巻でした.

Pythonに限らず,作成したメソッドやライブラリを他の自プロジェクトに使いたいことはあると思います.今回はそんなときのお話です.

ライブラリを公開して,pip コマンドでインストール可能にすれば便利ですが,一般公開するまでもないものや,公開してはダメなものもあるので,今回はローカル環境でパスを設定します.

PYTHONPATH を設定すればできるようですが,私の環境は pyenv で作成しているのか,うまく反映されませんでした.
$ export PYTHONPATH="hogehoge/mylib"
そこで,すでにパスが通っているところに pthファイル を置いて,新たにパスを設定します.それらのパスは以下のようにして確認できます.
import sys
for path in sys.path:
  print(path)
pthファイルは site-packages に保存します.私の環境では以下のパスになりました.
/Users/user/.pyenv/versions/3.6.1/lib/python3.6/site-packages
次に,pth ファイル名を mylib.pth,設定したいパスを hogehoge/mylib としたとき,以下のようにすれば pth ファイルを追加できます.
pth_file = "mylib.pth"
pth_body = "hogehoge/mylib"
for path in sys.path:
  if ".pyenv" in path and path.endswith("site-packages") is True:
    save_path = os.path.join(path, pth_file)
    with open(save_path, "w") as f:
      f.write(pth_body)
また,ファイル追加をしたら,削除も用意した方もいいでしょう.ここは簡単にコマンドラインの引数から,削除を追加しました.
# $ python add_pth_file.py delete
if len(args) > 1 and args[1] == "delete":
  if os.path.exists(save_path):
    os.remove(save_path)
いまのところ,他プロジェクトでも使うライブラリは,このpthファイル作成と一緒に1つのリポジトリに入れて管理しています.新しい環境でも,gitクローンしたらパス追加コマンドを使えば,簡単にライブラリが使えるようになります.また,パス指定のところでディレクトリ位置を動的に取得して反映するように変更し,クローンを行うディレクトリに依存しないようにしています.
posted by Seesaa京都スタッフ at 12:00| Comment(0) | 機械学習 | このブログの読者になる | 更新情報をチェックする

2018年05月11日

Bayesian Gaussian mixture model(BGMM)を使ってクラスタリング数を求める

エモトです.みなさま某IWはご覧になりましたでしょうか.私は原作を知らなかったのでこんな展開になるのかよ!と驚きました.しかし幸いなことに,直前に某アニメの王位争奪戦を見直していたので,心のダメージは最小に抑えられました.アタルありがとう.フェイスフラッシュで無事解決したように,来年公開の4を待ちましょう.

前回,混合ガウスモデル(Gaussian mixture model, GMM)を使用して,クラスタリングを行いました.クラスタリングで問題になるのは,やはり最適なクラスタリング数は何かです.

前回はBIC(ベイズ情報量規準)から最適なクラスタリング数を求めました.ここで,潜在的ディリクレ配分法(Latent Dirichlet Allocation, LDA)に階層ディリクレ過程(Hierechical Dirichlet Process, HDP)があるように,GMMにもクラスタリング数を自動推定する Bayesian Gaussian mixture model(BGMM)があります.今回は,そのBGMMを簡単に説明します.

なお,同様な手法として Dirichlet Process Gaussian Mixture Model (DPGMM) があるのですが,今回使用するライブラリ scikit-learn では deprecated になっていました(これらの経緯は勉強不足です,すみません).

今回は簡単な例を示した後に,複雑なデータを与えたときの結果を紹介します.なお,前半のコード例は こちら で公開しています.

BayesianGaussianMixtureの使い方

まず,検証用のデータを生成します.今回は2クラスに分類されそうなデータを作成しました.
from numpy.random import rand
vec_list = []
for base in [[10, 10], [0, 0]]:
  for _ in range(100):
    temp = [base[0] + rand(), base[1] + rand()]
    vec_list.append(temp)
1F1BC2E6D3FAEFA026E5FE9BEF6FC562.png


BGMMの学習を行います.前述通り,scikit-learn を使用しています.ここでは,BGMMの推定クラスターの最大候補数 n_components を10に,学習回数 max_iter を1000にしています.この例では学習回数はデフォルトの100のままでも大丈夫ですが,実際に複雑なデータでは100より大きい数値に設定しておくと良いと思います.
import numpy as np
nvec = np.array(vec_list)

from sklearn.mixture import BayesianGaussianMixture
gmm = BayesianGaussianMixture(n_components=10, verbose=1, max_iter=1000)
gmm.fit(nvec)
分類結果を見ると,今回はクラスター番号2と5のクラスターに分かれました.クラスターごとに色付けた散布図をみると,綺麗に2クラスに分かれたことがわかります.
clusters = gmm.predict(nvec)
print("clusters:", clusters)

'''
clusters: [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
'''
5A5F4268F47E0D856FBF4F181D9BEBF0.png


また,分類結果のクラスター番号を集合(set)に変換すると,クラスター数がわかりやすくなると思います.
clusters_set = set(clusters)
print("clusters_set:", clusters_set)
print("len(clusters_set):", len(clusters_set))
'''
clusters_set: {2, 5}
len(clusters_set): 2
'''
次に,BGMMで学習した各クラスター番号の重みを確認します.クラスタリング番号2と5に重みが集中して,他がほとんど0になっています.この重みからも2クラスターになったのが判断できます.
weights = gmm.weights_
components = gmm.n_components
x_clusters = np.arange(components)
plt.bar(x_clusters, weights, tick_label=x_clusters)
plt.show
1197D336BADA29F45CCB19C8EF080875.png


より複雑なデータはどうなるか?

上記の例ではきれいに分かれましたが.実際のデータ(より複雑なデータ)ではそう簡単にはいきません.下図は,あるデータに対してBGMMを行ったときの各カテゴリーごとの重みの計算結果です.
94C04508A40C2F2A81EA26BF037F7DAE.png


グラフを比べてわかるように,後者の図からクラスター数をポンっと決めるのは難しいです.しかしながら,いくつか0に近い重みのクラスターがあるので,それら考慮して取捨選択していけば,ある程度の目星はつきそうです.クラスター数の候補を絞って,GMMを行い,結果を比較すれば,最適なクラスター数を絞り込めそうです.
posted by Seesaa京都スタッフ at 12:00| Comment(0) | 機械学習 | このブログの読者になる | 更新情報をチェックする

2018年04月24日

Topic Model で学習したデータをGMMでクラスタリングする

エモトです.GWは某VR,吹奏楽,ヒーロー大集合,オリジンの映画と映画館に連日通いつめそうです.さすがGW.あ,サッカーも見に行くよ.

以前 Topic Model を使って,文章の解析を行いました.そこでも問題になったのは,最適なトピック数は何か?です.今回は別のクラスタリング手法を加えて,最適なクラスタリング数を探してみます.

クラスタリングの有名な手法だとk-meansがありますが,今回は混合ガウスモデル(Gaussian mixture model, GMM)を選択しました.調べていると,単一のドキュメントが幾つかの異なるトピックに属すためLDAと相性が良い,とのことで採用しました.

Topic Modelの計算結果から,コーパスデータを特徴量に変換します.可変長のコーパスデータが,LDAで指定したトピック数だけの次元の特徴量に変換されます.
corpus_vec_list = []
for corpus in corpus_data:
  temp_vec = [0 for i in range(topic_count)]
  for tp in lda.get_document_topics(corpus):
    temp_vec[tp[0]] = tp[1]
  corpus_vec_list.append(temp_vec)
GMMは scikit-learn を使用しました.
def calc_gmm(vec_list, component):
	from sklearn.mixture import GaussianMixture
	gmm = GaussianMixture(n_components=component, covariance_type='full')
	
	import numpy as np
	nvec = np.array(vec_list)
	
	gmm.fit(nvec)
	clusters = gmm.predict(nvec)
	proba = gmm.predict_proba(nvec)
	bic = gmm.bic(nvec)
	return clusters, proba, bic
候補のクラスタリング数だけ,GMMを計算します.BIC(ベイズ情報量規準)から最適なクラスタリング数を求めます.単純に大小の比較を行いましたが,最適化計算からの乱数によるブレが生じるので,候補それぞれでの数値や複数回処理するなどを加味すると良いと思われます.
import sys
min_bic = sys.float_info.max
best_component = 0
        
for component in range(20, 30):
	clusters, proba, bic = calc_gmm(corpus_vec_list, component)
	if bic < min_bic:
		min_bic = bic
		best_component = component
今回は.文章が持つトピック数を求めるために使用したTopic Modelを,トピック特徴空間への写像方法として使用して,その特徴空間でGMMを使ってクラスタリングを行いました.これで,文章の最適なトピック数・クラスタリング数が絞れれば良いのですが,逆に候補が増えて混乱になったりも.自然言語の扱いは難しいですね.
posted by Seesaa京都スタッフ at 13:00| Comment(0) | 機械学習 | このブログの読者になる | 更新情報をチェックする