Sklearn 線形回帰:Python 例で学ぶ完全ガイド
Updated on
あなたは特徴量と連続値の目的変数を含むデータセットを持っています。住宅価格、売上、気温のトレンドなどを予測したいものの、どのアプローチを使うべきか、また Python で正しくセットアップする方法が分からないかもしれません。モデル選択を誤ったり、必要な前処理を抜かしたりすると、予測精度が悪化し、デバッグに時間を浪費します。
線形回帰は連続値予測タスクで最も広く使われるアルゴリズムですが、正しく使うには .fit() と .predict() を呼ぶだけでは不十分です。モデルが内部でどう動くのか、どんなときに失敗するのか、どう評価すべきか、そして Ridge や Lasso のような正則化手法へいつ切り替えるべきかを理解する必要があります。これらを省くと、学習データではよく見えても新しい観測では破綻するモデルをデプロイしてしまいます。
Scikit-learn は LinearRegression に加えて、前処理・評価・正則化のためのツールが揃ったエコシステムを提供しています。このガイドでは、基本的な使い方から本番運用を意識した回帰パイプラインまでをカバーします。
線形回帰とは?
線形回帰は、1つ以上の入力特徴量と連続出力の関係を、残差平方和が最小となる直線(または超平面)を当てはめることでモデル化します。n 個の特徴量を持つモデルの式は次のとおりです。
y = b0 + b1*x1 + b2*x2 + ... + bn*xnここで b0 は切片(バイアス項)、b1...bn は各特徴量の係数(重み)、y は予測値です。
モデルは Ordinary Least Squares (OLS) のコスト関数を最小化する係数を見つけます。
Cost = Sum of (y_actual - y_predicted)^2閉形式解があるため、大規模データセットでも学習が高速です。
Sklearn による単回帰(Simple Linear Regression)
単回帰は、単一の特徴量で目的変数を予測します。以下は完全な例です。
from sklearn.linear_model import LinearRegression
import numpy as np
# Sample data: years of experience vs salary (in thousands)
X = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).reshape(-1, 1)
y = np.array([35, 40, 45, 55, 60, 62, 70, 75, 82, 90])
# Create and train the model
model = LinearRegression()
model.fit(X, y)
# Model parameters
print(f"Coefficient (slope): {model.coef_[0]:.4f}")
print(f"Intercept: {model.intercept_:.4f}")
# Predict salary for 12 years of experience
prediction = model.predict([[12]])
print(f"Predicted salary for 12 years: ${prediction[0]:.2f}k")
# Coefficient (slope): 5.9394
# Intercept: 28.3333
# Predicted salary for 12 years: $99.61k出力の読み方
| Attribute | Meaning | Example Value |
|---|---|---|
model.coef_ | 各特徴量の重み | [5.94] -- 年あたり給与が約 $5,940 増える |
model.intercept_ | 特徴量がすべて 0 のときの予測 y | 28.33 -- 基本給 $28,330 |
model.score(X, y) | 与えたデータに対する R-squared | 0.98 |
重回帰(Multiple Linear Regression)
特徴量が複数ある場合、直線ではなく超平面を当てはめます。
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing
import numpy as np
# Load California housing dataset
housing = fetch_california_housing()
X, y = housing.data, housing.target
feature_names = housing.feature_names
print(f"Features: {feature_names}")
print(f"Dataset shape: {X.shape}") # (20640, 8)
# Split into train and test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Train model
model = LinearRegression()
model.fit(X_train, y_train)
# Print coefficients for each feature
print("\nFeature Coefficients:")
for name, coef in zip(feature_names, model.coef_):
print(f" {name:12s}: {coef:+.6f}")
print(f" {'Intercept':12s}: {model.intercept_:+.6f}")
# Evaluate
train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
print(f"\nR² (train): {train_score:.4f}")
print(f"R² (test): {test_score:.4f}")モデル評価:R-squared、MSE、RMSE
R-squared だけでは全体像は分かりません。回帰モデルは複数の指標で評価しましょう。
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.datasets import fetch_california_housing
import numpy as np
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# Calculate metrics
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
print(f"R² Score: {r2:.4f}")
print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
# R² Score: 0.5758
# MSE: 0.5559
# RMSE: 0.7456
# MAE: 0.5332評価指標の説明
| Metric | Formula | Range | Interpretation |
|---|---|---|---|
| R-squared (R²) | 1 - (SS_res / SS_tot) | (-inf, 1] | 説明できた分散の割合。1.0 = 完璧、0 = 平均予測と同等 |
| MSE | mean((y - y_pred)²) | [0, inf) | 二乗誤差の平均。大きな誤差をより強く罰する |
| RMSE | sqrt(MSE) | [0, inf) | 目的変数と同じ単位。MSE より解釈しやすい |
| MAE | mean(|y - y_pred|) | [0, inf) | 絶対誤差の平均。外れ値に比較的頑健 |
R-squared が低いからといって必ずしも悪いモデルとは限りません。住宅価格のようなノイズの多い現実データでは、R² = 0.6 が妥当なこともあります。RMSE は必ず目的変数のスケールと比較して判断しましょう。
線形回帰における特徴量スケーリング
標準の LinearRegression は閉形式解の OLS を使うため、特徴量スケーリングは必須ではありません。ただし、正則化を使う場合はスケーリングが重要になります。
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
# Without scaling (fine for basic LinearRegression)
model_no_scale = LinearRegression()
model_no_scale.fit(X_train, y_train)
print(f"LinearRegression R² (no scaling): {model_no_scale.score(X_test, y_test):.4f}")
# With scaling via Pipeline (required for regularized models)
pipeline = Pipeline([
('scaler', StandardScaler()),
('ridge', Ridge(alpha=1.0))
])
pipeline.fit(X_train, y_train)
print(f"Ridge R² (with scaling): {pipeline.score(X_test, y_test):.4f}")正則化でスケーリングが重要な理由: Ridge と Lasso は大きな係数を一律に罰します。ある特徴量が 0〜1 の範囲で、別の特徴量が 0〜100,000 の範囲だと、ペナルティが不公平に働き、レンジの小さい特徴量の係数が過度に縮みやすくなります。スケーリングによって全特徴量のスケールを揃えることで、ペナルティが公平に適用されます。
多項式特徴量:非線形関係をモデル化する
特徴量と目的変数の関係が線形でない場合、多項式特徴量で曲線や相互作用を捉えられます。
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
import numpy as np
# Generate non-linear data
np.random.seed(42)
X = np.linspace(0, 10, 200).reshape(-1, 1)
y = 3 * X.ravel()**2 - 5 * X.ravel() + 10 + np.random.randn(200) * 15
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Linear model
linear = LinearRegression()
linear.fit(X_train, y_train)
print(f"Linear R²: {r2_score(y_test, linear.predict(X_test)):.4f}")
# Polynomial (degree 2) model
poly_pipeline = Pipeline([
('poly', PolynomialFeatures(degree=2, include_bias=False)),
('linear', LinearRegression())
])
poly_pipeline.fit(X_train, y_train)
print(f"Poly (d=2) R²: {r2_score(y_test, poly_pipeline.predict(X_test)):.4f}")
# Polynomial (degree 3) model
poly3_pipeline = Pipeline([
('poly', PolynomialFeatures(degree=3, include_bias=False)),
('linear', LinearRegression())
])
poly3_pipeline.fit(X_train, y_train)
print(f"Poly (d=3) R²: {r2_score(y_test, poly3_pipeline.predict(X_test)):.4f}")注意: 高次の多項式はすぐに過学習します。適切な次数はクロスバリデーションで選び、多項式モデルでは正則化を優先的に検討しましょう。
正則化:Ridge、Lasso、ElasticNet
特徴量や多項式項が多いとき、正則化は大きな係数へのペナルティを加えることで過学習を防ぎます。
Ridge 回帰(L2 ペナルティ)
Ridge はコスト関数に係数の二乗和を追加します。係数を 0 に近づけますが、完全に 0 にはしません。
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.datasets import fetch_california_housing
import numpy as np
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
# Find best alpha with cross-validation
pipeline = Pipeline([
('scaler', StandardScaler()),
('ridge', Ridge())
])
param_grid = {'ridge__alpha': [0.01, 0.1, 1.0, 10.0, 100.0]}
grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='r2')
grid.fit(X_train, y_train)
print(f"Best alpha: {grid.best_params_['ridge__alpha']}")
print(f"Best CV R²: {grid.best_score_:.4f}")
print(f"Test R²: {grid.score(X_test, y_test):.4f}")Lasso 回帰(L1 ペナルティ)
Lasso は係数の絶対値和を追加します。係数を厳密に 0 にできるため、自動的な特徴量選択として機能します。
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.datasets import fetch_california_housing
import numpy as np
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
pipeline = Pipeline([
('scaler', StandardScaler()),
('lasso', Lasso(max_iter=10000))
])
param_grid = {'lasso__alpha': [0.001, 0.01, 0.1, 1.0, 10.0]}
grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='r2')
grid.fit(X_train, y_train)
print(f"Best alpha: {grid.best_params_['lasso__alpha']}")
print(f"Test R²: {grid.score(X_test, y_test):.4f}")
# Show which features were selected (non-zero coefficients)
lasso_model = grid.best_estimator_.named_steps['lasso']
feature_names = housing.feature_names
for name, coef in zip(feature_names, lasso_model.coef_):
status = "KEPT" if abs(coef) > 1e-6 else "DROPPED"
print(f" {name:12s}: {coef:+.6f} [{status}]")ElasticNet(L1 + L2 ペナルティ)
ElasticNet は Ridge と Lasso のペナルティを組み合わせます。l1_ratio が混合比を制御します:0 = Ridge のみ、1 = Lasso のみ。
from sklearn.linear_model import ElasticNet
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
pipeline = Pipeline([
('scaler', StandardScaler()),
('elasticnet', ElasticNet(max_iter=10000))
])
param_grid = {
'elasticnet__alpha': [0.01, 0.1, 1.0],
'elasticnet__l1_ratio': [0.1, 0.3, 0.5, 0.7, 0.9]
}
grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='r2')
grid.fit(X_train, y_train)
print(f"Best alpha: {grid.best_params_['elasticnet__alpha']}")
print(f"Best l1_ratio: {grid.best_params_['elasticnet__l1_ratio']}")
print(f"Test R²: {grid.score(X_test, y_test):.4f}")比較:LinearRegression vs Ridge vs Lasso vs ElasticNet
| Model | Penalty | Feature Selection | When to Use | Scaling Required |
|---|---|---|---|---|
| LinearRegression | なし | なし | 特徴量が少ない、強い多重共線性がない、S/N 比が良い | いいえ |
| Ridge | L2(二乗) | なし(0 に近づける) | 相関の強い特徴量が多く、すべて残したい | はい |
| Lasso | L1(絶対値) | あり(0 にする) | 特徴量が多く、自動選択したい | はい |
| ElasticNet | L1 + L2 | あり(部分的) | 相関の強い特徴量が多く、ある程度選択もしたい | はい |
適切なモデルの選び方
まずは LinearRegression をベースラインにします。過学習している(train と test の R-squared に大きな差がある)場合は、最初に Ridge を試すのが一般的です。無関係な特徴量が多い疑いがあるなら Lasso、特徴量が相関していて選択もしたいなら ElasticNet を試しましょう。比較には必ずクロスバリデーションを使ってください。
線形回帰の仮定
線形回帰が信頼できる結果を出すのは、次の仮定が概ね成り立つときです。
- 線形性 -- 特徴量と目的変数の関係が線形(または変換で線形化可能)である。
- 独立性 -- 観測が互いに独立である。自己相関を考慮しない時系列データでは破られやすい。
- 等分散性(Homoscedasticity) -- 残差の分散が予測値の水準によらず一定である。
- 残差の正規性 -- 残差が正規分布に従う。予測精度よりも信頼区間や仮説検定で重要。
- 多重共線性がない -- 特徴量同士が強く相関していない。多重共線性は係数の分散を膨らませ、個々の係数解釈を不安定にする。
コードで仮定をチェックする
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing
import numpy as np
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
residuals = y_test - y_pred
# Check residual statistics
print(f"Residual mean: {residuals.mean():.6f}") # Should be near 0
print(f"Residual std: {residuals.std():.4f}")
print(f"Residual skewness: {float(np.mean((residuals - residuals.mean())**3) / residuals.std()**3):.4f}")
# Check for multicollinearity (correlation matrix)
corr_matrix = np.corrcoef(X_train, rowvar=False)
print(f"\nMax feature correlation: {np.max(np.abs(corr_matrix - np.eye(corr_matrix.shape[0]))):.4f}")完全なパイプライン:現実的な回帰ワークフロー
前処理、特徴量エンジニアリング、モデル選択を組み合わせた、本番運用を意識したパイプライン例です。
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.datasets import fetch_california_housing
import numpy as np
# Load data
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
# Define models to compare
models = {
'LinearRegression': Pipeline([
('scaler', StandardScaler()),
('model', LinearRegression())
]),
'Ridge (alpha=1)': Pipeline([
('scaler', StandardScaler()),
('model', Ridge(alpha=1.0))
]),
'Lasso (alpha=0.01)': Pipeline([
('scaler', StandardScaler()),
('model', Lasso(alpha=0.01, max_iter=10000))
]),
'Poly(2) + Ridge': Pipeline([
('poly', PolynomialFeatures(degree=2, include_bias=False)),
('scaler', StandardScaler()),
('model', Ridge(alpha=10.0))
])
}
# Evaluate all models
print(f"{'Model':<25} {'CV R² (mean)':>12} {'CV R² (std)':>12} {'Test R²':>10}")
print("-" * 62)
for name, pipeline in models.items():
cv_scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring='r2')
pipeline.fit(X_train, y_train)
test_r2 = pipeline.score(X_test, y_test)
print(f"{name:<25} {cv_scores.mean():>12.4f} {cv_scores.std():>12.4f} {test_r2:>10.4f}")PyGWalker で回帰結果を探索する
モデルを学習した後は、予測パターンを理解することが重要です。PyGWalker (opens in a new tab) を使うと、Jupyter 上でドラッグ&ドロップ式のインタラクティブ UI を通じて、残差、特徴量の重要度、予測値 vs 実測値の関係を視覚的に探索できます。
import pandas as pd
import pygwalker as pyg
# Build a results DataFrame
results = pd.DataFrame(housing.data[len(X_train):], columns=housing.feature_names)
results['actual'] = y_test
results['predicted'] = y_pred
results['residual'] = y_test - y_pred
results['abs_error'] = np.abs(y_test - y_pred)
# Launch interactive exploration
walker = pyg.walk(results)特徴量を軸にドラッグしたり、残差の大きさで色分けしたりして、モデルが苦手とするデータのセグメントを、プロットコードを書かずに特定できます。
Jupyter で反復的に実験する場合は、RunCell (opens in a new tab) が、異なる特徴量の組み合わせ、ハイパーパラメータ、前処理手順のテストを、セルを書き換え続けることなく支援する AI エージェントを提供します。
FAQ
sklearn の LinearRegression とは何ですか?
sklearn.linear_model.LinearRegression は Ordinary Least Squares (OLS) の回帰モデルです。実測値と予測値の差の二乗和を最小化することで、データに線形方程式を当てはめます。scikit-learn において最もシンプルで解釈しやすい回帰モデルです。
R-squared スコアはどう解釈すればよいですか?
R-squared は、目的変数の分散のうちモデルが説明できた割合を表します。R-squared が 0.80 なら、分散の 80% を説明できたことを意味します。1.0 は完全適合、0.0 は平均との差がない予測と同等、負の値は平均で予測するより悪いことを示します。
Ridge と Lasso と ElasticNet はいつ使い分けるべきですか?
Ridge は(相関のある特徴量を含む)過学習を抑えつつ、特徴量をすべて残したいときに使います。Lasso は自動的な特徴量選択をしたいときに有効で、不要な特徴量の係数を 0 にします。ElasticNet は特徴量が相関している場合に、Ridge の安定性と Lasso の疎性のバランスを取りたいときに使います。
LinearRegression には特徴量スケーリングが必要ですか?
基本の LinearRegression は OLS 解がスケール不変であるため、特徴量スケーリングは必須ではありません。ただし Ridge、Lasso、ElasticNet はペナルティが係数の大きさを同等に扱うため、スケーリングが必要です。正則化回帰の前には必ず特徴量をスケーリングしてください。
線形回帰でカテゴリ特徴量を扱うには?
OneHotEncoder や pd.get_dummies() を使ってカテゴリ特徴量を数値に変換してから学習します。Sklearn の LinearRegression は数値入力のみを受け付けます。パイプラインでは ColumnTransformer を使い、数値列とカテゴリ列に別々の変換を適用します。
MSE と RMSE の違いは何ですか?
MSE(Mean Squared Error)は、実測値と予測値の差を二乗したものの平均です。RMSE(Root Mean Squared Error)は MSE の平方根です。RMSE は目的変数と同じ単位になるため解釈しやすくなります。たとえば家の価格(ドル)を予測していて RMSE が 50,000 なら、平均的な予測誤差が約 $50,000 であることを意味します。
まとめ
Sklearn の LinearRegression は、Python による回帰タスクの出発点です。高速で解釈しやすく、基礎となる関係が概ね線形であれば効果的です。ノイズ、多重共線性、特徴量数の多さといった現実データ特有の要因がある場合には、Ridge、Lasso、ElasticNet の正則化が汎化性能の改善に役立ちます。評価は(R-squared、RMSE、MAE など)複数指標で行い、過学習を避けるために train-test split を使い、残差パターンを確認して仮定が成り立っているか検証しましょう。StandardScaler と PolynomialFeatures を Pipeline に組み込むことで、ワークフローをクリーンかつ再現可能に保てます。