メインコンテンツへスキップ
ブログ一覧に戻る
統計・データサイエンス

多重共線性とVIFとは?「広告費を増やすと売上が下がる」係数が出たときに疑うこと

2026年8月29日
最終更新: 2026年10月8日
10分で読めます

この記事でわかること

回帰係数が想定と逆の符号になったとき、VIFだけで変数を消していませんか。補助回帰のR²、分散と標準誤差の増幅、残差分散と入力の広がりを区別します。架空4行のNumPy教材でVIF2・5・17・101と係数反転を計算し、相関表が見落とす線形従属、変数削除、交差検証、予測と因果の限界まで具体的な表と図で確認します。

2026年10月8日にstatsmodels 0.15のVIF関数とscikit-learnの前処理・交差検証本文を再確認した。VIFはある説明変数を他の説明変数で補助回帰したR²から、係数分散の膨張を点検する指標であり、万能の閾値や変数削除の自動規則ではない。相関表が見逃す線形関係、スケール、欠測、予測と係数解釈、モデル目的、交差検証、因果仮定を分け、VIFの数値だけで広告や施策の原因を判断しない。

広告費・営業人数・店舗数を使って売上を回帰したら、広告費の係数がマイナスになった。「広告を増やすと売上が下がる」と報告してよいでしょうか。

その係数は、他の入力を条件とした統計的な関連です。説明変数が一緒に動くと、それぞれの関連を切り分けにくくなります。ただし、符号が逆になる理由は多重共線性だけではありません。入力の単位、集計、モデルの形、交絡も確認します。

ここではVIFが表す分散の増幅と、係数そのもの・予測・因果を分けるため、架空4行の計算を使います。実在する会社の広告効果を推定した例ではありません。

30秒で要点

  • VIFは、ある説明変数を他の説明変数で回帰した補助回帰のR²から計算します。売上などの目的変数のR²とは別です。
  • VIFが5なら分散の増幅は5倍、標準誤差の相対増幅は√5倍です。同じノイズと変数の広がりを比較する、という条件があります。
  • 大きいVIFだけでは、標準誤差が何円なのか、予測が悪いのか、変数を消すべきかは決まりません。
  • 2変数ずつの相関が極端でなくても、複数を組み合わせた線形従属があり得ます。
  • 分割を変えた感度確認は役立ちますが、2回一致しただけで安定性や因果効果を保証しません。

係数・標準誤差・VIFは違う数字

表が収まらない場合は、横にスクロールできます

用語何を表すか取り違えやすい点
説明変数モデルに渡す入力。広告費、店舗数など単位・集計対象・欠測処理をそろえる
係数他の入力を固定したときの、モデル上の平均応答の変化その変数への介入効果とは限らない
標準誤差仮定の下での係数推定量の標準偏差の推定残差の標準偏差や、個々の予測誤差とは別
多重共線性入力の間に強い線形関係がある状態2列の相関だけでは見つからない場合がある
VIF入力の線形関係による係数分散の増幅要因係数の絶対値・因果・モデル全体の予測精度ではない
交差検証データを分け、学習と評価や推定の変動を見る手順独立性・時系列・同一対象の重複に合う分け方が必要
statsmodelsのVIF資料は、多重共線性の指標としてVIFを説明し、5より大きい値への注意を「ひとつの推奨」としています。5を合否の境界にしたり、5以下なら解釈が正しいと考えたりしないでください。

補助回帰のR²を使う

説明変数x1のVIFを知りたいなら、x1を目的変数として、残りの説明変数で回帰します。その決定係数を R1² として、VIF1 = 1/(1 - R1²) です。x1をほぼ再現できるほどR1²は1に近づき、独自に動く情報は少なくなります。

切片を含む完全ランクの通常の最小二乗法(OLS)で、誤差の条件付き平均が0、分散が一定σ²、誤差間の共分散が0という条件を置くと、係数の条件付き分散は次の形になります。

Var(β̂1 | X) = σ² / Sxx1 × VIF1、ただし Sxx1 = Σ(x1 - x1の平均)²。

σ²を残差平方和/残差自由度で推定した値s²に置き換えると、通常の標準誤差は SE(β̂1) = √(s² × VIF1 / Sxx1)。相関のない仮想設計との比較で、s²とSxx1を固定したときの相対増幅が√VIFです。

したがって、VIFだけを見て標準誤差の絶対的な大きさは決められません。ノイズ、入力の単位・広がり、標本構成も関係します。異分散に頑健な標準誤差などを使う場合も、この単純な式をそのまま代入しないでください。

架空4行で、増幅の条件を固定する

平均0のベクトル u=(-1,-1,1,1) と v=(-1,1,-1,1) を使います。内積は0、各平方和は4です。x1=u、x2=u+εv とすると、x2がx1に近づくほど切り分けが難しくなります。

この構成では R1²=1/(1+ε²)、VIF1=(1+ε²)/ε²。s²を0.01、Sxx1を4に固定した値を計算します。

表が収まらない場合は、横にスクロールできます

ε補助回帰R1²VIF1SE(β̂1)
1.000.50000020.070711
0.500.80000050.111803
0.250.941176170.206155
0.100.9900991010.502494

これは母集団から精度を測った実験ではなく、式の分母を確かめる教材です。下の応答を使う例は4行に切片と2係数を推定するので、残差自由度は1しかありません。実務に十分な標本数を示すものではありません。

架空設計のVIF2、5、17、101に対して標準誤差が0.070711、0.111803、0.206155、0.502494へ増える条件付き比較。応答を各行0.1変えると係数が反転する別パネル
自作の架空教材。左はs²=0.01とSxx1=4を固定した標準誤差で、棒の長さはSEに比例。右はε=0.1で同じ入力を使い、応答だけを変えた結果。観測点上の当てはめの差は0.1だが、未知データの予測精度や因果効果は検証していない。数字と条件は本文表にも記載。

小さい応答の変更で、係数の符号が変わる例

ε=0.1に固定し、さらに w=(1,-1,-1,1) を用意します。wは切片、u、vに直交します。応答は y=10+2u+kv+0.05w とし、kだけを0.15から0.25に変えます。

表が収まらない場合は、横にスクロールできます

行x1x2yA(k=0.15)yB(k=0.25)
1-1-1.17.907.80
2-1-0.98.108.20
310.911.8011.70
411.112.2012.30

x2=u+0.1vなので、モデル y≈β0+β1x1+β2x2 の最小二乗解は β0=10、β2=k/0.1、β1=2-k/0.1 になります。w成分は残差として残ります。

表が収まらない場合は、横にスクロールできます

応答切片x1の係数x2の係数残差平方和残差自由度
yA100.51.50.011
yB10-0.52.50.011

各応答の差の絶対値は0.1なのに、x1の係数は正から負へ変わります。一方、観測した4点での当てはめの差も各0.1です。係数をそれぞれ識別する難しさと、観測点への当てはまりを分けて読む例です。「予測精度は変わらない」と一般化するための外部評価はしていません。

相関表だけでは見つからない線形従属

別の設計として、入力をu、v、u+vの3列にすると、相関はuとvで0、残りの2組で約0.707です。それでも第3列は第1列と第2列の和なので、完全な線形従属です。どの相関も0.8未満だから安全、とは言えません。

数学上、完全に説明される列のVIFは無限大に向かい、係数の一意な分解ができません。ソフトウェアではR²の丸めやクリップにより、無限大でなく大きな有限値が返ることもあります。確認したstatsmodels 0.15.0の掲載ソースはR²を上限でクリップしています。関数の数値だけでなく、行列のランクも確認します。

Pythonで係数・VIF・ランクを別々に計算する

次はNumPyで補助回帰と最小二乗解を直接計算する教材です。切片を明示的に追加します。statsmodelsのOLS仕様でも、配列入力には通常切片を自分で追加する必要があります。以下はstatsmodelsの関数を実行した結果ではありません。

import numpy as np

u = np.array([-1., -1., 1., 1.])
v = np.array([-1., 1., -1., 1.])
w = np.array([1., -1., -1., 1.])
ones = np.ones(4)
for eps in [1., 0.5, 0.25, 0.1]:
    x2 = u + eps * v
    aux = np.column_stack([ones, x2])
    residual = u - aux @ np.linalg.lstsq(aux, u, rcond=None)[0]
    r2 = 1 - residual @ residual / (u @ u)
    vif = 1 / (1 - r2)
    se = np.sqrt(0.01 * vif / (u @ u))
    print("eps, R2, VIF, SE", eps, r2, vif, se)
    np.testing.assert_allclose(vif, (1 + eps**2) / eps**2)

x = np.column_stack([ones, u, u + 0.1 * v])
for k in [0.15, 0.25]:
    y = 10 + 2*u + k*v + 0.05*w
    beta = np.linalg.lstsq(x, y, rcond=None)[0]
    residual = y - x @ beta
    df = len(y) - np.linalg.matrix_rank(x)
    s2 = (residual @ residual) / df
    se = np.sqrt(np.diag(s2 * np.linalg.inv(x.T @ x)))
    print("k, coefficients, s2, df, SE", k, beta, s2, df, se)
    np.testing.assert_allclose(beta, [10, 2-k/0.1, k/0.1])
    np.testing.assert_allclose(s2, 0.01)
    np.testing.assert_allclose(se[1], np.sqrt(0.01*101/4))

dependent = np.column_stack([ones, u, v, u+v])
print("dependent rank / columns", np.linalg.matrix_rank(dependent), 4)
assert np.linalg.matrix_rank(dependent) == 3

完全従属の設計では係数を解釈する処理へ進めません。欠測行の除去で診断対象が変わっていないか、定数列、同じ列の重複、切片と全カテゴリのダミー変数の重複も確認します。単位をそろえるだけでは、元の線形関係は消えません。

AGEとEXPERIENCEの例を、一般則にしない

scikit-learnの公式教材は、賃金データのAGEとEXPERIENCEを使い、交差検証の分割間で係数が動く例を示しています。片方の係数が正、他方が負になる領域が現れ、AGEを除いた例ではEXPERIENCEの変動が小さくなります。

これは、そのデータ・前処理・モデルでの観測です。年齢と経験年数を扱えば必ず同じ符号になる、片方を消せばすべて解決する、という結論ではありません。

変数を消すと、「他の何を一定とした関連か」という問いも変わります。交絡調整に必要な変数を、VIFを下げる目的だけで除くと別の問題を作ります。正則化で係数が安定しても、推定対象やバイアスは変わります。予測目的なら未使用データで評価し、説明・因果目的なら識別の条件に戻る必要があります。

符号に驚いたときの、小さい確認手順

最初に、報告しようとしている係数と、その単位・他の調整変数を1つの文で書きます。「広告費を動かす効果」と言い換えず、「この入力を条件とした関連」と書けるかを確かめます。

  1. 設計行列を確認する:重複・定数・ダミー変数・欠測行を確認し、ランクと相関表を見ます。
  2. 補助回帰と不確実性を見る:VIF、通常または設計に合う標準誤差、区間、残差の構造を並べます。VIFだけで絶対SEは判断しません。
  3. 分け方を理由付きで変える:時系列なら未来を学習へ混ぜず、同一対象の反復なら対象単位を守るなど、利用場面に合う分割にします。係数の感度と予測の評価は別欄に残します。
  4. 問いに戻る:入力を削除する、設計・収集を改善する、予測に正則化を使う、今のデータでは個別の関連を切り分けられないと報告する、という選択肢を比べます。

旧来の「期間を前半・後半に分けて2回一致すれば使える」「大きさが2倍違えば不可」という単独基準は置きません。2分割の一致は一つの観測で、母集団の安定性や施策の妥当性の証明にはならないからです。

最後に、VIFが低く、符号が安定し、標準誤差が小さくても因果は別です。計測していない交絡があり得ます。冒頭のマイナス係数から広告削減額を直接決めず、何を検証すれば判断が変わるかを整理してください。状況を整理する(15分)も、その問いを言語化する入口として使えます。

よくある質問(FAQ)