0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

sklearnのLDAでsolverによって値が変わる話

Posted at

表題の通りです。

まずsklearnのLDAについて

LDA自体の説明について

結論

1次元の2値(bool)分類でのLDAの場合、xの値がある値以上ならTrue,未満ならFalseと予想するという内部構造になります。
そのある値なんですが、以下のようにモデルから取り出すことができます。

border = -model.intercept_/model.coef_

傾きがmodel.coef_で切片がmodel.intercept_、それぞれa,bとおくと
$$ax_{border}+b=0\ \ ,\ x_{border}=-b/a$$
な感じです。
solverはsvd,lsqr,eigenの3種類で標準はsvd。見ていきましょう。

#make_moonsで的当にデータ作成、特に意味はない
n_samples=100
X, y = make_moons(n_samples = n_samples, noise = 0.1)
X=X[:,[0]]

#TrueとFalseのバランスを1:1じゃなくする。
random_y=np.random.randint(0,2,n_samples)
print(f"n_True:n_False={np.sum(random_y==1)}:{np.sum(random_y==0)}")
>>n_True:n_False=53:47

#svd
model_svd=LDA(solver="svd")
model_svd.fit(X, random_y).score(X, random_y)
print(-model_svd.intercept_/model_svd.coef_)
>>[[-0.3641749]]

#lsqr
model_lsqr=LDA(solver="lsqr")
model_lsqr.fit(X, random_y).score(X, random_y)
print(-model_lsqr.intercept_/model_lsqr.coef_)
>>[[-0.34673599]]

#eigen
model_eigen=LDA(solver="eigen")
model_eigen.fit(X, random_y).score(X, random_y)
print(-model_eigen.intercept_/model_eigen.coef_)
>>[[-0.34673599]]

なんでかは知らないですが、svdの時だけ値が変わります。
そして、上記サイトの原理の通り作成するとlsqr,eigenの値と一致します。
ただし、TrueとFalseが同じ数だけある時には全て一致します。

経緯

以上のページを参考に、自分でLDAを実装しようとしました。
手始めにまず1次元の、2値(bool)分類で作成
以下のようになりました。

def LDA_1d(x,y):
    mu_T=np.mean(x[y])
    mu_F=np.mean(x[~y])
    sigma=(np.sum((x[y]-mu_T)**2)+np.sum((x[~y]-mu_F)**2))/(y.shape[0])
    f=(mu_T-mu_F)*x-(mu_T-mu_F)*(mu_T+mu_F)/2+sigma*np.log(np.sum(y)/np.sum(~y))
    return np.sum((f>=0)==y)/y.shape[0]

これをsklearnのものでチェックした時に、おかしいと言われてしまいます。
雑にですが、

N=10
for i in range(N):
    #データ作成、make_moonsで的当にXを作成して、random_yで的当に答えを作成
    n_samples=100
    X, y = make_moons(n_samples = n_samples, noise = 0.1)
    X=X[:,[0]]
    random_y=np.random.randint(0,2,n_samples)

    #入れてみる
    my_score_LDA=LDA_1d(X[:,0], random_y==1)                     #myfunc
    ans_score_LDA=model_LDA.fit(X, random_y).score(X, random_y)  #sklearn
    
    if my_score_LDA!=ans_score_LDA:
        print(f"diff_LDA(random),{np.sum(random_y==0)}:{np.sum(random_y==1)}" ,my_score_LDA,ans_score_LDA)

動かしてみたらdiff_LDAが表示されまくります。
いろいろ1日以上悩んでふと、solverなんて引数があったなーと思い、変更してみました。
その際に上の値を見て確信しました。かなり時間を無駄にしてしまいました。
だってsolverで変わるなんて流石に思わなくないですか??

以上です。何か誤解があったら(ありそう)教えていただければと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?