因子分析で斜交回転すると累積寄与率が1を超える(pythonのfactor-analyzer モジュール, Rのfactanal 関数)

学習済み Doc2Vec の 文章ベクトルを 因子分析しています。
複数のプログラムで試したのですが、いずれも累積寄与率が1を超える場合がありました。

原因の解明をお手伝いいただけませんでしょうか?

python の factor-analyzer モジュール (https://pypi.org/project/factor-analyzer/)
および、 Rの標準関数 factanal の2つでそれぞれ因子分析を行いました。

どちらも次の条件下で、「累積寄与率が1を超える」という問題が発生しました。
累積寄与率とは、各因子が全体のどれだけの割合を説明できているかを表す値なので、1を超えることは通常起こりえないはずです。

項目
データ 後述する 300項目、サイズ1135267のデータ
データの前処理 各項目で平均値0, 標本標準偏差(ddof=1)1に標準化
因子軸回転方法 promax(斜交回転)
因子数 100

参考: 問題が発生しない条件

  • 因子軸回転方法を varimax(直交回転) にすれば、因子数に拘わらず、問題は発生しませんでした。
  • 因子数が充分小さければ、因子軸回転方法に拘わらず、問題は発生しませんでした。

2-1. データについて

データは 1135267行300列の numpy 行列 "jawiki.doc2vec.dmpv300d.model.docvecs.vectors_docs.npy"です。(カレントディレクトリに保存)

中身は300次元の学習済み doc2vec 文章ベクトルたちです。
具体的には https://yag-ays.github.io/project/pretrained_doc2vec_wikipedia/ の dmpv300d を利用しました。

上記モデルでは 1135267 個の 日本語版 Wikipedia 記事 を学習していました。

データは、各次元ごとに標準化して利用しました。

2-2. 目的

文章ベクトルに対して因子軸を取り出すことで、「基底」となる文章を取り出す目的で、今回因子分析を行いました。

例えば、 因子数が 2 で、各因子軸に最も近い文章ベクトルが 「正義」 と 「悪」 であり、累積寄与率が0.6 だった場合、任意の文章 は 実数 a,b を用いて、a 正義 + b 悪 により、60% 程度説明がつくのではないかと考えた次第です。

まず、 python の factor-analyzer モジュールで当該の問題が起こりました。

同モジュールのバグの可能性を疑い、別の環境としてRの標準関数 factanal でも同じ実験を行ったのですが、同様の問題が発生しました。

3-1. factor-analyzer モジュールの場合

python

1import numpy as np 2vecs = np.load("jawiki.doc2vec.dmpv300d.model.docvecs.vectors_docs.npy")3assert vecs.shape == (1135267, 300)4 5def standardize(v):6 v = np.array(v)7 v_mean = v.mean(axis=0, keepdims=True)8 v_std = v.std(axis=0, keepdims=True, ddof=1)9 return (v - v_mean) / v_std 10vecs = standardize(vecs)11# np.mean(vecs[:,0]) は-2.9144826e-06, 12# np.std(vecs[:,0],ddof=1) は 1.0004338 だった13 14from factor_analyzer import FactorAnalyzer 15fa = FactorAnalyzer(n_factors = 100, rotation="promax")16fa.fit(docvecs_std)17 18cumulative_vars = fa.get_factor_variance()[2]19# [0]が 負荷量二乗和, [1]が寄与率, [2]が累積寄与率 20cumulative_var = cumulative_vars[-1]21# 最後の因子における累積寄与率22print(cumulative_var)

結果

1.7611056920601675

3-2. factanal 関数の場合

python

1import numpy as np 2vecs = np.load("jawiki.doc2vec.dmpv300d.model.docvecs.vectors_docs.npy")3assert vecs.shape == (1135267, 300)4 5def standardize(v):6 v = np.array(v)7 v_mean = v.mean(axis=0, keepdims=True)8 v_std = v.std(axis=0, keepdims=True, ddof=1)9 return (v - v_mean) / v_std 10vecs = standardize(vecs)11 12from rpy2 import robjects 13from rpy2.robjects.conversion import py2rpy 14from rpy2.robjects import numpy2ri 15numpy2ri.activate()16R = robjects.r 17def numpy2r(npy, name):18 R.assign(name, py2rpy(npy))19R.assign_numpy = numpy2r 20 21R.assign_numpy(vecs, "data") # R の "data" 変数に vecs を入れる22R("factanal_result <- factanal(x=data, factor=100, rotation='promax')")23 24R("sink('output.txt')")25R("print(factanal_result)")26R("sink()")27# output.txt に 因子分析結果を出力

結果

Factor100 SS loadings 1.743 Proportion Var 0.006 Cumulative Var 1.593

累積寄与率が 1.593 となっています。

  1. そもそも累積寄与率は1を超えても問題ない?
  2. factor-analyzerfactanal から累積寄与率を正しく取得できていない?
  3. 300項目、1135267サイズ、あるいは100因子が大きすぎる?

計算中の丸め誤差などが積み重なり、累積寄与率が実際と異なる数値になっている?

コメントを投稿

0 コメント