なげなわがペナルティパラメータに収束しないのはなぜですか?


7

LASSO回帰がどのように機能するかを調べるためにLASSO、最適なアルファパラメーターを選択することで回帰を最適化する小さなコードを書きました。

LASSO交差検証後、回帰がアルファパラメーターに対してこのような不安定な結果をもたらす理由を理解できません。

これが私のPythonコードです:

from sklearn.linear_model import Lasso
from sklearn.cross_validation import KFold
from matplotlib import pyplot as plt

# generate some sparse data to play with
import numpy as np
import pandas as pd 
from scipy.stats import norm
from scipy.stats import uniform

### generate your own data here

n = 1000

x1x2corr = 1.1
x1x3corr = 1.0
x1 = range(n) + norm.rvs(0, 1, n) + 50
x2 =  map(lambda aval: aval*x1x2corr, x1) + norm.rvs(0, 2, n) + 500
y = x1 + x2 #+ norm.rvs(0,10, n)

Xdf = pd.DataFrame()
Xdf['x1'] = x1
Xdf['x2'] = x2

X = Xdf.as_matrix()

# Split data in train set and test set
n_samples = X.shape[0]
X_train, y_train = X[:n_samples / 2], y[:n_samples / 2]
X_test, y_test = X[n_samples / 2:], y[n_samples / 2:]

kf = KFold(X_train.shape[0], n_folds = 10, )
alphas = np.logspace(-16, 8, num = 1000, base = 2)

e_alphas = list()
e_alphas_r = list()  # holds average r2 error
for alpha in alphas:
    lasso = Lasso(alpha=alpha, tol=0.004)
    err = list()
    err_2 = list()
    for tr_idx, tt_idx in kf:
        X_tr, X_tt = X_train[tr_idx], X_test[tt_idx]
        y_tr, y_tt = y_train[tr_idx], y_test[tt_idx]
        lasso.fit(X_tr, y_tr)
        y_hat = lasso.predict(X_tt)

        # returns the coefficient of determination (R^2 value)
        err_2.append(lasso.score(X_tt, y_tt))

        # returns MSE
        err.append(np.average((y_hat - y_tt)**2))
    e_alphas.append(np.average(err))
    e_alphas_r.append(np.average(err_2))

## print out the alpha that gives the minimum error
print 'the minimum value of error is ', e_alphas[e_alphas.index(min(e_alphas))]
print ' the minimizer is ',  alphas[e_alphas.index(min(e_alphas))]

##  <<< plotting alphas against error >>>

plt.figsize = (15, 15)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(alphas, e_alphas, 'b-')
ax.plot(alphas, e_alphas_r, 'g--')
ax.set_ylim(min(e_alphas),max(e_alphas))
ax.set_xlim(min(alphas),max(alphas))
ax.set_xlabel("alpha")
plt.show()

このコードを繰り返し実行すると、alphaの結果が大きく異なります。

>>> 
the minimum value of error is  3.99254192539
 the minimizer is  1.52587890625e-05
>>> ================================ RESTART ================================
>>> 
the minimum value of error is  4.07412455842
 the minimizer is  6.45622425334
>>> ================================ RESTART ================================
>>> 
the minimum value of error is  4.25898253597
 the minimizer is  1.52587890625e-05
>>> ================================ RESTART ================================
>>> 
the minimum value of error is  3.79392968781
 the minimizer is  28.8971008254
>>> 

アルファ値が正しく収束しないのはなぜですか?私のデータは合成ですが、分布は同じです。また、およびの変動は非常に小さくなっx1ていx2ます。

これが非常に不安定になる原因は何ですか?

Rで書かれた同じものは異なる結果を与えます-それは常に "optimal_alpha"としてアルファの可能な最高値を返します。

私はこれもRで書いたので、少し違う答えが返ってきました。理由はわかりませんか?

library(glmnet)
library(lars)
library(pracma)

set.seed(1)
k = 2 # number of features selected 

n = 1000

x1x2corr = 1.1
x1 = seq(n) + rnorm(n, 0, 1) + 50
x2 =  x1*x1x2corr + rnorm(n, 0, 2) + 500
y = x1 + x2 

filter_out_label <- function(col) {col!="y"}

alphas = logspace(-5, 6, 100)

for (alpha in alphas){
  k = 10
  optimal_alpha = NULL
  folds <- cut(seq(1, nrow(df)), breaks=k, labels=FALSE)
  total_mse = 0
  min_mse = 10000000
  for(i in 1:k){
    # Segement your data by fold using the which() function
    testIndexes <- which(folds==i, arr.ind=TRUE)
    testData <- df[testIndexes, ]
    trainData <- df[-testIndexes, ]

    fit <- lars(as.matrix(trainData[Filter(filter_out_label, names(df))]),
                trainData$y,
                type="lasso")
    # predict
    y_preds <- predict(fit, as.matrix(testData[Filter(filter_out_label, names(df))]),
                       s=alpha, type="fit", mode="lambda")$fit # default mode="step"

    y_true = testData$y
    residuals = (y_true - y_preds)
    mse=sum(residuals^2)
    total_mse = total_mse + mse
  }
  if (total_mse < min_mse){
    min_mse = total_mse
    optimal_alpha = alpha
  }
}

print(paste("the optimal alpha is ", optimal_alpha))

上記のRコードからの出力は次のとおりです。

> source('~.....')
[1] "the optimal alpha is  1e+06"

実際、ライン " alphas = logspace(-5, 6, 100)"に何を設定しても、常にアルファの最高値を返します。

ここには実際には2つの異なる質問があると思います。

  1. Pythonで記述されたバージョンでアルファ値が不安定になるのはなぜですか?

  2. Rで書かれたバージョンが異なる結果をもたらすのはなぜですか?(私はlogspace関数がRto pythonとは異なることを理解していますが、で書かれたバージョンはR常にalpha最適なアルファ値の最大値を私に与えますが、Pythonバージョンはそうではありません)。

これらのことを知っていただければ幸いです...


2
これが問題の原因であるかどうかはわかりませんが、scikit-learnのlassoモデル(呼び出している場合)では、データを中央に配置する必要がありますが、実際にはそうではありません。トレーニングセットのxとyの平均を減算してから、これらの同じ値をテストセットから減算する必要があります(相互検証の前にデータを中央揃えしないか、独自の平均を使用してテストセットを中央揃えします!)。別の方法はfit_intercept、投げ縄モデルを構築するときにパラメーターを使用することです。
user20160 2016

これが不安定性に影響を与えるとは思えませんが、試すことはできます...
Candic3

3
(1)Pythonスクリプトでは、毎回ランダムデータを生成していますよね?最適な正則化パラメーターがすべてのランダム描画で同じになると予想するのはなぜですか?データが異なれば、最適な正則化パラメーターも異なります。(2)Rスクリプトはどのデータを使用していますか?Rスクリプトからの結果は、Pythonとどのように異なりますか?出力や比較は提供しません。
amoeba

1
演習の重要な部分として校正コードが含まれているため、ここでは問題はほとんど取り上げられていないと思います。おそらく、「奇妙な」結果のいくつかは、単にコーディングエラーが原因です?しかし、質問は一般的に興味深いものです。また、アルファとは何ですか?たとえば、私はがLASSOまたはリッジ回帰内のペナルティ強度であり、次にがLASSOと弾性ネット回帰におけるリッジの重みであることに慣れています。あなたのアルファは私の対応していますか?λαλ
Richard Hardy

1
また、PythonとRの違いは、最適なアルファの不安定性に関する主な質問に本当に関連していますか?PythonとRの比較を含めることで、複雑さが増し、新しい問題が発生し、質問の本質であるIMHOが部分的にマスクされます。PythonとRのLASSO実装の違いは、おそらく別の質問として提起されるべきです。
Richard Hardy

回答:


14

私はpythonをよく知りませんが、Rコードに1つの問題を見つけました。

あなたは2行があります:

residuals = sum(y_true - y_preds)
mse=residuals^2

これは残差を合計し、それらを二乗します。これは、残差を二乗してからそれらを合計すること(Pythonコードが正しく行うように見える)とは大きく異なります。これがRコードとpythonコードの違いの大きな部分であるのではないかと思います。Rコードを修正して再度実行し、Pythonコードのように動作するかどうかを確認します。

「最良の」アルファとそれに対応するMSEを保存するだけでなく、それらすべてを保存して関係をプロットすることもお勧めします。設定によっては、非常に平坦な領域があり、異なるポイントでのMSEの差がそれほど大きくない場合があります。これが事実である場合、データへの非常に小さな変更(交差検証の順序でさえ)は、多くの中で本質的に同じで、どのポイントが最小になるかを変更できます。最適な領域の周りに平坦な領域が発生する状況になると、多くの場合、表示される結果につながり、すべてのアルファ値と対応するmse値のプロットがわかりやすくなります。


あなたの最初のコメントは素晴らしいキャッチでした-ありがとう。そのバグを修正した後も問題は解決しません。2番目の提案を試してみましょう。
Candic3

@ Candic3これは素晴らしい提案です。また、両方のアルゴリズムは確定的であるため、シードを修正すると、DIYバージョンで最小角度ソリューションパスを正確に再現できるはずです。
シャドウトーカー2016

ステップサイズが完全に同じ場合にのみ、どちらも同じソリューションパスを生成します。また、sklearn@ JennyLuが指摘したように、バージョンには組み込みの相互検証があるため、少し異なるエラーが生成されます。
Candic3

6

sklearnには、ここで実行しようとしているものとほぼ同じ例があります。http://scikit-learn.org/stable/auto_examples/exercises/plot_cv_diabetes.html

実際、この例は、その例で実行された3つのフォールドのそれぞれについて、アルファの結果が大きく異なることを示しています。これは、アルファのトレーニングと選択に使用しているデータの部分に大きく依存しているため、アルファの選択を信頼できないことを意味します。

私は、あなたが完全な答えを与えるために「収束」するものとして交差検証を考えるべきではないと思います。実際、概念的には収束とは正反対だと思います。あなたはあなたのデータを分離していて、それぞれのフォールドのためにあなたは「別々の方向」に進んでいます。テストデータとトレーニングデータの分割方法に応じて異なる結果が得られるという事実は、1つの完全な結果に収束することは不可能であり、望ましくないことを示しています。常に一貫したアルファ値を取得する唯一の方法は、トレーニングにすべてのデータを使用する場合です。ただし、これを行うと、学習結果は最高になりますが、検証結果は最悪になります。


1
x-validationについてのあなたのコメントは興味深いです-私は完全には従いません。ハイパーパラメーターの選択にはx検証が使用されると思いました。x検証が収束しない場合、ハイパーパラメーターを選択するために何を使用しますか?
Candic3

sklearnplot_cv_diabetesで引用した例では、データポイント(150)が非常に少ないため、その例だけではが不安定であるとは確信できません。α
Candic3

これは実際には相互検証の良い見方です。@ Candic3収束しない場合は、別の方法を試してください。それは私があなたが湖を横切って車を運転することができないとあなたに言ったようであり、そしてあなたは「しかし私は乗り越えなければならない!」と不平を言う。橋を見つけるか、周りを回る
シャドウトーカー

@ Candic3糖尿病データセットのすべてのデータ(442ポイント)を使用して迅速に実行しました。結果は次のとおりです:[fold 0] alpha:0.00010、score:0.50126 [fold 1] alpha:0.10405、score:0.48495 [fold 2]アルファ:0.04520、スコア:0.50332
ジェニールー

1
@JennyLu私が理解k folds cross-validationする方法、および(上記の例で私が行った方法)の値は、すべてのフォールドで同じである必要があります。推定しているパラメーターの値(エラー、スコアまたは、MSEなど)は、フォールド間で変更する必要があるものです。なぜなら、 基本的にあなたは推定しているパラメータ(estimand)の条件付き平均を計算しようとしています。だから、私はの値がフォールド間で変わるべきではないと思います。αR2k folds cross-validationα
Candic3

5

マルチ共線x1x2するものである Pythonコードの値が不安定。これらの変数を生成する分布の分散は非常に小さいため、係数の分散は大きくなります。これを説明するために、分散インフレ係数(VIF)を計算できます。から分散が増加した後α

x1 = range(n) + norm.rvs(0, 1, n) + 50
x2 =  map(lambda aval: aval*x1x2corr, x1) + norm.rvs(0, 2, n) + 500

....に....

x1 = range(n) + norm.rvs(0, 100, n) + 50
x2 =  map(lambda aval: aval*x1x2corr, x1) + norm.rvs(0, 200, n) + 500

その後、値は安定します。α

Rただし、Pythonコードとは異なるコードの問題は謎のままです...


ありがとう-以上です。RPythonもう少し違いを見てみましょう。
Candic3

@ Candic3は、悪条件の問題で異なる障害モードを持つ異なる実装を使用しているためです。ドキュメントを読むと、Python Lassoは座標降下を使用します。これは、RのLARソリューションと比較しています
shadowtalker

2

Rコードについてコメントします。

変数を間違った場所でリセットしています。つまり、変数min_mseはループのInf外側として初期化し 、forループの 外側optimal_alphaで初期化する必要がNULLあります。これは次のようになります。

library(glmnet)
library(lars)
library(pracma)

set.seed(1)
k = 2 # number of features selected 

n = 100

x1x2corr = 1.1
x1 = seq(n) + rnorm(n, 0, 1) + 50
x2 =  x1*x1x2corr + rnorm(n, 0, 2) + 500
y = x1 + x2 +rnorm(n,0,0.5)
df = data.frame(x1 = x1, x2 = x2, y = y)
filter_out_label <- function(col) {col!="y"}

alphas = logspace(-5, 6, 50)

###
# INITIALIZE here before loop
###
min_mse = Inf
optimal_alpha = NULL
# Let's store the mse values for good measure
my_mse = c()

for (alpha in alphas){
  k = 10
  folds <- cut(seq(1, nrow(df)), breaks=k, labels=FALSE)
  # DO NOT INITIALIZE min_mse and optimal_alpha here, 
  # then you cannot find them...
  total_mse = 0
  for(i in 1:k){
    # Segement your data by fold using the which() function
    testIndexes <- which(folds==i, arr.ind=TRUE)
    testData <- df[testIndexes, ]
    trainData <- df[-testIndexes, ]

    fit <- lars(as.matrix(trainData[Filter(filter_out_label, names(df))]),
                trainData$y,
                type="lasso")
    # predict
    y_preds <- predict(fit, as.matrix(testData[Filter(filter_out_label,
                       names(df))]),
                       s=alpha, type="fit", mode="lambda")$fit 

    y_true = testData$y
    residuals = (y_true - y_preds)
    mse=sum(residuals^2)
    total_mse = total_mse + mse
  }
  # Let's store the MSE to see the effect
  my_mse <- c(my_mse, total_mse)
  if (total_mse < min_mse){
    min_mse = total_mse
    optimal_alpha = alpha
    # Let's observe the output
    print(min_mse)
  }
}

print(paste("the optimal alpha is ", optimal_alpha))
# Plot the effect of MSE with varying alphas
plot(my_mse)

予測子には強い共線性があり、応答は利用可能な予測子からのみ構築されるため、出力は一貫してアルファの最小値になるはずです。つまり、LASSOでゼロにしたい冗長変数がないため、この場合は正則化を実行しないでalphaください。つまり、最小化が最適です。ここでMSEの効果を確認できます。

MSEへの影響

私はあなたと同じスケールで50のアルファを使用していることに注意してください。アルファインデックスが付けられた35の周りでは、両方の変数がゼロにスラミングされます。これは、モデルが常に同じことを行い、mseが停滞することを意味します。

MSE、CV、およびLASSOを研究するためのより良い問題

上記の問題はLASSOにとってそれほど興味深いものではありません。LASSOはモデル選択を実行するため、対象のパラメーターを実際に選択することを確認します。モデルが実際にMSEを低下させるアルファを実際に選択していることを確認すると、より印象的です。つまり、いくつかの変数をスローすることで、より良い予測が得られます。これは、冗長な予測子の束を追加するより良い例です。

set.seed(1)
k = 100 # number of features selected 

n = 100

x1x2corr = 1.1
x1 = seq(n) + rnorm(n, 0, 1) + 50
x2 =  x1*x1x2corr + rnorm(n, 0, 2) + 500
# Rest of the variables are just noise
x3 = matrix(rnorm(k-2,0,(k-2)*n),n,k-2)
y = x1 + x2 +rnorm(n,0,0.5)
df = data.frame(x1 = x1, x2 = x2, y = y)
df <- cbind(df,x3)
filter_out_label <- function(col) {col!="y"}

alphas = logspace(-5, 1.5, 100)
min_mse = Inf
optimal_alpha = NULL
my_mse = c()

次に、上のコードのようにforループを実行するだけです!alphas下のプロットで効果を確認するために、6から1.5に下げの最大値を置いていることに注意してください。これで、最良のalpha値は最低値ではありませんが、交差検証MSEが低下し、最終的に再び急上昇していることがプロットで確認できます。そのグラフの最も低い点は、CVエラーが最も低いアルファインデックスに対応します。

LASSOのより良いCV問題

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.