決定係数(coefficient of determination, $R^2$)は、回帰モデルの当てはまりの良さを評価する最も基本的な指標です。「モデルがデータの変動をどの程度説明できているか」を0から1の値で表し、1に近いほどモデルの説明力が高いことを意味します。
本記事では、決定係数の定義を全変動の分解から丁寧に導出し、Pythonでの計算方法と注意点を解説します。
本記事の内容
- 全変動・回帰変動・残差変動の定義と関係
- 決定係数 $R^2$ の導出
- 自由度調整済み決定係数 $\bar{R}^2$
- Pythonでの計算と可視化
前提知識
この記事を読む前に、重回帰分析の基礎を押さえておくと理解が深まります。
変動の分解
回帰分析では、目的変数 $y$ の変動を「モデルで説明できる部分」と「説明できない部分」に分解します。
$n$ 個のデータ $(x_i, y_i)$ に対し、モデルの予測値を $\hat{y}_i$、目的変数の平均を $\bar{y} = \frac{1}{n}\sum_{i=1}^{n} y_i$ とすると、以下の3つの量を定義します。
全変動(Total Sum of Squares, SST):
$$ \text{SST} = \sum_{i=1}^{n} (y_i – \bar{y})^2 $$
回帰変動(Regression Sum of Squares, SSR):
$$ \text{SSR} = \sum_{i=1}^{n} (\hat{y}_i – \bar{y})^2 $$
残差変動(Residual Sum of Squares, SSE):
$$ \text{SSE} = \sum_{i=1}^{n} (y_i – \hat{y}_i)^2 $$
最小二乗法を用いた回帰では、以下の分解が成り立ちます。
$$ \text{SST} = \text{SSR} + \text{SSE} $$
イメージとしては、データのばらつき(SST)は「モデルが捉えたばらつき(SSR)」と「モデルが捉えきれなかったばらつき(SSE)」の和になっています。
決定係数の定義
決定係数 $R^2$ は、全変動のうちモデルで説明できる割合として定義されます。
$$ R^2 = \frac{\text{SSR}}{\text{SST}} = 1 – \frac{\text{SSE}}{\text{SST}} $$
$$ R^2 = 1 – \frac{\sum_{i=1}^{n}(y_i – \hat{y}_i)^2}{\sum_{i=1}^{n}(y_i – \bar{y})^2} $$
$R^2$ の解釈:
- $R^2 = 1$: 全てのデータ点をモデルが完全に予測($\hat{y}_i = y_i$ for all $i$)
- $R^2 = 0$: モデルの予測力が平均値の予測と同等
- $R^2 < 0$: モデルの予測が平均値の予測よりも悪い(過学習等で発生)
自由度調整済み決定係数
$R^2$ には、説明変数を増やすと必ず値が大きくなる(または不変)という性質があります。これではモデルの複雑さを考慮した適切な評価ができません。
自由度調整済み決定係数 $\bar{R}^2$ は、説明変数の数 $p$ とサンプル数 $n$ で補正します。
$$ \bar{R}^2 = 1 – \frac{n – 1}{n – p – 1}(1 – R^2) $$
$$ = 1 – \frac{\text{SSE} / (n – p – 1)}{\text{SST} / (n – 1)} $$
$\bar{R}^2$ は不要な説明変数を加えるとペナルティが働き、値が下がる場合があります。
Pythonでの実装
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score
np.random.seed(42)
# --- データ生成 ---
n = 50
X = np.random.uniform(0, 10, n).reshape(-1, 1)
y_true = 2 * X.ravel() + 0.5 * X.ravel()**2 - 10
y = y_true + np.random.randn(n) * 5
# --- スクラッチでR^2を計算 ---
def r_squared(y_true, y_pred):
"""決定係数の計算"""
ss_res = np.sum((y_true - y_pred) ** 2)
ss_tot = np.sum((y_true - np.mean(y_true)) ** 2)
return 1 - ss_res / ss_tot
def adjusted_r_squared(y_true, y_pred, n_features):
"""自由度調整済み決定係数"""
n = len(y_true)
r2 = r_squared(y_true, y_pred)
return 1 - (n - 1) / (n - n_features - 1) * (1 - r2)
# --- 多項式次数ごとのR^2を比較 ---
degrees = range(1, 10)
r2_train_list = []
r2_adj_list = []
X_plot = np.linspace(0, 10, 200).reshape(-1, 1)
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
for i, deg in enumerate([1, 2, 5, 9]):
ax = axes[i // 2, i % 2]
poly = PolynomialFeatures(degree=deg)
X_poly = poly.fit_transform(X)
X_plot_poly = poly.transform(X_plot)
model = LinearRegression()
model.fit(X_poly, y)
y_pred = model.predict(X_poly)
y_plot = model.predict(X_plot_poly)
r2 = r_squared(y, y_pred)
r2_adj = adjusted_r_squared(y, y_pred, deg)
ax.scatter(X, y, c='red', s=20, alpha=0.7, label='Data')
ax.plot(X_plot, y_plot, 'b-', label=f'degree={deg}')
ax.set_title(f'Degree {deg}: $R^2$={r2:.4f}, $\\bar{{R}}^2$={r2_adj:.4f}')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# --- R^2とadjusted R^2の次数依存性 ---
r2_list = []
r2_adj_list = []
for deg in degrees:
poly = PolynomialFeatures(degree=deg)
X_poly = poly.fit_transform(X)
model = LinearRegression()
model.fit(X_poly, y)
y_pred = model.predict(X_poly)
r2_list.append(r_squared(y, y_pred))
r2_adj_list.append(adjusted_r_squared(y, y_pred, deg))
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(list(degrees), r2_list, 'bo-', label='$R^2$')
ax.plot(list(degrees), r2_adj_list, 'rs--', label='Adjusted $R^2$')
ax.set_xlabel('Polynomial Degree')
ax.set_ylabel('Score')
ax.set_title('$R^2$ vs Adjusted $R^2$')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# scikit-learnとの比較
poly = PolynomialFeatures(degree=2)
X_poly = poly.fit_transform(X)
model = LinearRegression()
model.fit(X_poly, y)
y_pred = model.predict(X_poly)
print(f"スクラッチ R^2: {r_squared(y, y_pred):.6f}")
print(f"sklearn R^2: {r2_score(y, y_pred):.6f}")
多項式の次数を上げるとR^2は単調に増加しますが、自由度調整済みR^2はある次数で頭打ちになり、不要な変数を追加するとむしろ低下することが確認できます。
まとめ
本記事では、決定係数 $R^2$ について解説しました。
- $R^2$ はデータの全変動のうち、モデルで説明できる割合を表す指標
- $R^2 = 1 – \text{SSE}/\text{SST}$ で定義され、1に近いほどモデルの当てはまりが良い
- 自由度調整済み $\bar{R}^2$ はモデルの複雑さにペナルティを課し、過学習を抑制する
- 回帰モデルの評価では $R^2$ だけでなく、残差プロットや他の指標も合わせて確認することが重要
次のステップとして、以下の記事も参考にしてください。