web-dev-qa-db-ja.com

ロジスティック回帰統計モデルからの確率予測の信頼区間

統計的学習の概要からプロットを再作成しようとしていますが、確率予測の信頼区間を計算する方法を理解するのに問題があります。具体的には、この図の右側のパネル( 図7.1 )を再作成しようとしています。これは、関連する95%信頼区間を持つ年齢4の多項式に基づいて、賃金> 250の確率を予測しています。 。賃金データは here だれかが気にかければ。

私は次のコードで予測された確率をうまく予測してプロットできます

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from sklearn.preprocessing import PolynomialFeatures

wage = pd.read_csv('../../data/Wage.csv', index_col=0)
wage['wage250'] = 0
wage.loc[wage['wage'] > 250, 'wage250'] = 1

poly = Polynomialfeatures(degree=4)
age = poly.fit_transform(wage['age'].values.reshape(-1, 1))

logit = sm.Logit(wage['wage250'], age).fit()

age_range_poly = poly.fit_transform(np.arange(18, 81).reshape(-1, 1))

y_proba = logit.predict(age_range_poly)

plt.plot(age_range_poly[:, 1], y_proba)

しかし、予測された確率の信頼区間がどのように計算されるかについては、私は途方に暮れています。データを何度もブートストラップして各年齢の確率の分布を取得することを考えましたが、私の理解を超えた簡単な方法があることはわかっています。

推定された係数の共分散行列と、各推定された係数に関連する標準誤差があります。この情報を前提として、上の図の右側のパネルに示されているように、信頼区間をどのように計算しますか?

ありがとう!

11
Taylor

delta method を使用して、予測確率のおおよその分散を見つけることができます。つまり、

var(proba) = np.dot(np.dot(gradient.T, cov), gradient)

ここで、gradientはモデル係数による予測確率の導関数のベクトルであり、covは係数の共分散行列です。

デルタ法は、すべての最尤推定に対して漸近的に機能することが証明されています。ただし、トレーニングサンプルが少ない場合は、漸近法がうまく機能しない可能性があるため、ブートストラップを検討する必要があります。

以下は、デルタ法をロジスティック回帰に適用するおもちゃの例です。

import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt

# generate data
np.random.seed(1)
x = np.arange(100)
y = (x * 0.5 + np.random.normal(size=100,scale=10)>30)
# estimate the model
X = sm.add_constant(x)
model = sm.Logit(y, X).fit()
proba = model.predict(X) # predicted probability

# estimate confidence interval for predicted probabilities
cov = model.cov_params()
gradient = (proba * (1 - proba) * X.T).T # matrix of gradients for each observation
std_errors = np.array([np.sqrt(np.dot(np.dot(g, cov), g)) for g in gradient])
c = 1.96 # multiplier for confidence interval
upper = np.maximum(0, np.minimum(1, proba + std_errors * c))
lower = np.maximum(0, np.minimum(1, proba - std_errors * c))

plt.plot(x, proba)
plt.plot(x, lower, color='g')
plt.plot(x, upper, color='g')
plt.show()

次の素敵な絵を描きます: enter image description here

あなたの例では、コードは

proba = logit.predict(age_range_poly)
cov = logit.cov_params()
gradient = (proba * (1 - proba) * age_range_poly.T).T 
std_errors = np.array([np.sqrt(np.dot(np.dot(g, cov), g)) for g in gradient])
c = 1.96 
upper = np.maximum(0, np.minimum(1, proba + std_errors * c))
lower = np.maximum(0, np.minimum(1, proba - std_errors * c))

plt.plot(age_range_poly[:, 1], proba)
plt.plot(age_range_poly[:, 1], lower, color='g')
plt.plot(age_range_poly[:, 1], upper, color='g')
plt.show()

そしてそれは次の絵を与えるでしょう

enter image description here

象が中にいるボアコンストリクターのように見えます。

あなたはそれをbootstrap見積もりと比較することができます:

preds = []
for i in range(1000):
    boot_idx = np.random.choice(len(age), replace=True, size=len(age))
    model = sm.Logit(wage['wage250'].iloc[boot_idx], age[boot_idx]).fit(disp=0)
    preds.append(model.predict(age_range_poly))
p = np.array(preds)
plt.plot(age_range_poly[:, 1], np.percentile(p, 97.5, axis=0))
plt.plot(age_range_poly[:, 1], np.percentile(p, 2.5, axis=0))
plt.show()

enter image description here

デルタ法とbootstrapの結果はほとんど同じに見えます。

しかし、本の著者は3番目の方法を採用しています。彼らは、

proba = np.exp(np.dot(x、params))/(1 + np.exp(np.dot(x、params)))

線形部分の信頼区間を計算してから、ロジット関数で変換します

xb = np.dot(age_range_poly, logit.params)
std_errors = np.array([np.sqrt(np.dot(np.dot(g, cov), g)) for g in age_range_poly])
upper_xb = xb + c * std_errors
lower_xb = xb - c * std_errors
upper = np.exp(upper_xb) / (1 + np.exp(upper_xb))
lower = np.exp(lower_xb) / (1 + np.exp(lower_xb))
plt.plot(age_range_poly[:, 1], upper)
plt.plot(age_range_poly[:, 1], lower)
plt.show()

だから彼らは発散間隔を得ます:

enter image description here

これらのメソッドは、異なるもの(予測確率と対数オッズ)が正常に分布していると想定しているため、非常に異なる結果を生成します。つまり、デルタ法は予測確率が正常であると仮定し、本では対数オッズが正常であると仮定しています。実際、それらは有限サンプルでは正常ではありませんが、すべてが無限サンプルで収束しますが、それらの分散は同時にゼロに収束します。最尤推定は再パラメーター化の影響を受けませんが、それらの推定分布はそうであり、それが問題です。

17
David Dale

これは、statsmodels Logit()。fit()オブジェクト( 'fit'の上にあるフィット( 'mean_se')と単一の観測( 'obs_se')の標準誤差( 'se')を計算するための有益で効率的な方法です)、ISLR本のメソッドと同じで、David Daleの回答の最後のメソッドです。

fit_mean = fit.model.exog.dot(fit.params)
fit_mean_se = ((fit.model.exog*fit.model.exog.dot(fit.cov_params())).sum(axis=1))**0.5
fit_obs_se = ( ((fit.model.endog-fit_mean).std(ddof=fit.params.shape[0]))**2 + \
                fit_mean_se**2 )**0.5

ISLRの本に似た図

網掛けの領域は、近似と単一の観測の95%信頼区間を表します。

改善のアイデアは大歓迎です。

0
Haeraeus