5. 基本的なモデル¶
第 5 回では、機械学習における基本的な回帰モデルについて学びます。前回までに学んだ Python の文法やデータ可視化の技術、そして機械学習の基礎を活用しながら、実践的なデータ分析のスキルを身につけていきましょう。
本講義では、これまで主に使用してきた NumPy に加えて、pandas(パンダス) というデータ分析ライブラリも本格的に使用していきます。pandas は実務でのデータ分析において必須のツールであり、データの読み込み、加工、分析を効率的に行うことができます。
5.1 pandas の基礎知識¶
5.1.1 pandas とは¶
pandas は、Python でデータ分析を行うための最も重要なライブラリの一つです。表形式のデータを扱うのに特化しており、Excel や CSV ファイルのようなデータを簡単に操作できます。
pandas の名前の由来
pandas という名前は、「Panel Data(パネルデータ)」と「Python Data Analysis(Pythonデータ分析)」に由来しています。
pandas の主な特徴:
- 表形式データ(テーブルデータ)を扱いやすい
- CSV、Excel、SQL データベースなど、さまざまなデータ形式の読み込み・書き出しが簡単
- 欠損値の処理が容易
- データの集計、グループ化、結合などの操作が直感的
- NumPy や matplotlib、scikit-learn との連携がスムーズ
graph LR
A[データソース] --> B[pandas]
B --> C[データの読み込み]
B --> D[データの加工]
B --> E[データの分析]
B --> F[データの可視化]
C --> G[CSV, Excel, SQL...]
D --> H[欠損値処理<br/>データ変換<br/>特徴量作成]
E --> I[統計量計算<br/>グループ化<br/>集計]
F --> J[matplotlib<br/>seaborn]
style B fill:#e6ccff,color:#000
style C fill:#ccffcc,color:#000
style D fill:#ffffcc,color:#000
style E fill:#ffcccc,color:#000
style F fill:#e6f3ff,color:#000
5.1.2 pandas のインポート¶
pandas は通常、pd という略称でインポートします。これは慣習的な書き方で、ほとんどのコードで使われています。
5.1.3 主要なデータ構造¶
pandas には 2 つの主要なデータ構造があります。
Series(シリーズ)¶
Series は、1 次元のデータ構造です。NumPy の配列に「インデックス(ラベル)」が付いたものと考えることができます。
Series を作成するためには、pandas.Series() 関数を使用します。
import pandas as pd
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])
print(s)
DataFrame(データフレーム)¶
DataFrame は、2 次元のデータ構造(表形式データ)です。Excel のスプレッドシートや SQL のテーブルのようなものと考えることができます。
DataFrame を作成するためには、pandas.DataFrame() 関数を使用します。
import pandas as pd
df = pd.DataFrame({
'name': ['太郎', '花子', '次郎', '梅子'],
'age': [25, 30, 35, 28],
'city': ['茨城', '大阪', '京都', '東京']
})
print(df)
graph TB
A[pandas のデータ構造] --> B[Series<br/>1次元]
A --> C[DataFrame<br/>2次元]
B --> B1[インデックス付き配列<br/>例: 時系列データ]
C --> C1[表形式データ<br/>例: CSVファイル、Excelシート]
C --> D[複数のSeriesが<br/>集まったもの]
style A fill:#e6ccff
style B fill:#ccffcc
style C fill:#ffffcc
5.1.4 DataFrame の基本操作¶
データの確認¶
データの先頭を確認するためには、.head() メソッドを使用します。同様に、末尾を確認するには .tail() メソッドを使用します。
データの概要を確認するには、.info() メソッドや .describe() メソッドを使用します。
import pandas as pd
import numpy as np
# サンプルデータの作成
np.random.seed(42)
df = pd.DataFrame({
'A': np.random.randint(1, 100, 10),
'B': np.random.randint(1, 100, 10),
'C': np.random.randint(1, 100, 10)
})
# 先頭5行を表示
print("=== head() ===")
print(df.head())
# 末尾5行を表示
print("\n=== tail() ===")
print(df.tail())
# データの形状(行数、列数)
print(f"\n形状: {df.shape}") # (10, 3)
# データの情報
print("\n=== info() ===")
df.info()
# 統計情報
print("\n=== describe() ===")
print(df.describe())
列の選択¶
import pandas as pd
df = pd.DataFrame({
'name': ['太郎', '花子', '次郎'],
'age': [25, 30, 35],
'salary': [400, 500, 600]
})
# 1つの列を選択(Series として返る)
print(df['name'])
# 複数の列を選択(DataFrame として返る)
print(df[['name', 'age']])
# 新しい列を追加
df['bonus'] = df['salary'] * 0.1
print(df)
行の選択とフィルタリング¶
行を選択するためには、.loc[](ラベルベース)や .iloc[](位置ベース)を使用します。
import pandas as pd
df = pd.DataFrame({
'name': ['太郎', '花子', '次郎', '梅子'],
'age': [25, 30, 35, 28],
'salary': [400, 500, 600, 450]
})
# 条件でフィルタリング
print("=== 年齢が30以上 ===")
print(df[df['age'] >= 30])
# 複数条件
print("\n=== 年齢が30以上かつ給与が500以上 ===")
print(df[(df['age'] >= 30) & (df['salary'] >= 500)])
# loc: ラベルベースの選択
print("\n=== loc[0:2, ['name', 'age']] ===")
print(df.loc[0:2, ['name', 'age']])
# iloc: 位置ベースの選択
print("\n=== iloc[0:2, 0:2] ===")
print(df.iloc[0:2, 0:2])
基本的な統計量¶
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [10, 20, 30, 40, 50],
'C': [100, 200, 300, 400, 500]
})
# 各列の平均
print("平均:")
print(df.mean())
# 各列の合計
print("\n合計:")
print(df.sum())
# 各列の最大値・最小値
print("\n最大値:")
print(df.max())
print("\n最小値:")
print(df.min())
# 特定の列の統計量
print(f"\nAの平均: {df['A'].mean()}")
print(f"Bの標準偏差: {df['B'].std()}")
5.1.5 欠損値の処理¶
実際のデータには欠損値(NaN: Not a Number)が含まれることがよくあります。pandas では欠損値の処理が簡単に行えます。
欠損値を確認するためには、.isnull() メソッドを使用します。欠損値を削除するには .dropna()、欠損値を埋めるには .fillna() を使用します。
import pandas as pd
import numpy as np
# 欠損値を含むデータ
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [10, np.nan, 30, 40, 50],
'C': [100, 200, 300, np.nan, 500]
})
print("=== 元のデータ ===")
print(df)
# 欠損値の確認
print("\n=== 欠損値の有無 ===")
print(df.isnull())
print("\n=== 各列の欠損値の数 ===")
print(df.isnull().sum())
# 欠損値を含む行を削除
print("\n=== 欠損値を含む行を削除 ===")
print(df.dropna())
# 欠損値を特定の値で埋める
print("\n=== 欠損値を0で埋める ===")
print(df.fillna(0))
# 欠損値を平均値で埋める
print("\n=== 欠損値を平均値で埋める ===")
print(df.fillna(df.mean()))
=== 元のデータ ===
A B C
0 1.0 10.0 100.0
1 2.0 NaN 200.0
2 NaN 30.0 300.0
3 4.0 40.0 NaN
4 5.0 50.0 500.0
=== 欠損値の有無 ===
A B C
0 False False False
1 False True False
2 True False False
3 False False True
4 False False False
=== 各列の欠損値の数 ===
A 1
B 1
C 1
dtype: int64
=== 欠損値を含む行を削除 ===
A B C
0 1.0 10.0 100.0
4 5.0 50.0 500.0
=== 欠損値を0で埋める ===
A B C
0 1.0 10.0 100.0
1 2.0 0.0 200.0
2 0.0 30.0 300.0
3 4.0 40.0 0.0
4 5.0 50.0 500.0
=== 欠損値を平均値で埋める ===
A B C
0 1.0 10.0 100.0
1 2.0 32.5 200.0
2 3.0 30.0 300.0
3 4.0 40.0 275.0
4 5.0 50.0 500.0
5.1.6 CSV ファイルの読み込み¶
pandas の最も強力な機能の一つは、さまざまな形式のファイルを簡単に読み書きできることです(CHIKUWA Editor ではファイル書き出しを制限しています)。
CSV ファイルを読み込むためには、pandas.read_csv() 関数を使用します。Excel ファイルを読み込むには、pandas.read_excel() 関数を使用します。
import pandas as pd
# CSV ファイルの読み込み
df = pd.read_csv('data.csv')
# ヘッダーがない場合
df = pd.read_csv('data.csv', header=None)
# 列名を指定
df = pd.read_csv('data.csv', names=['col1', 'col2', 'col3'])
# 特定の列のみ読み込む
df = pd.read_csv('data.csv', usecols=['col1', 'col2'])
# Excel ファイルの読み込み
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
5.1.7 pandas と NumPy の相互変換¶
pandas と NumPy は相互に変換できます。
DataFrame を NumPy 配列に変換するためには、.values 属性または .to_numpy() メソッドを使用します。
import pandas as pd
import numpy as np
# DataFrame から NumPy 配列へ
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6]
})
arr = df.values # または df.to_numpy()
print("=== DataFrame → NumPy 配列 ===")
print(type(arr))
print(arr)
# NumPy 配列から DataFrame へ
arr = np.array([[1, 2, 3], [4, 5, 6]])
df = pd.DataFrame(arr, columns=['A', 'B', 'C'])
print("\n=== NumPy 配列 → DataFrame ===")
print(type(df))
print(df)
=== DataFrame → NumPy 配列 ===
<class 'numpy.ndarray'>
[[1 4]
[2 5]
[3 6]]
=== NumPy 配列 → DataFrame ===
<class 'pandas.core.frame.DataFrame'>
A B C
0 1 2 3
1 4 5 6
pandas と NumPy の使い分け
NumPy を使うべき場合:
- 純粋な数値計算が中心(行列演算、数学関数など)
- 大規模な数値配列の高速処理が必要
- メモリ効率を重視する場合
- 例:画像処理、科学技術計算、線形代数
pandas を使うべき場合:
- 列に意味のある名前を付けたい(可読性向上)
- CSV や Excel からデータを読み込む
- 欠損値を含むデータを扱う
- データの集計・グループ化・結合が必要
- データの探索的分析(EDA)を行う
- 例:ビジネスデータ分析、統計分析、機械学習の前処理
実務でのワークフロー:
- データ読み込み・前処理: pandas
- 機械学習モデルの学習: pandas または NumPy(どちらも可)
- 高度な数値計算: NumPy
pandas と NumPy は相互に変換可能なので、状況に応じて使い分けられます。
5.2 単回帰分析の復習と実践¶
5.2.1 単回帰分析とは¶
前回学んだように、単回帰分析(Simple Linear Regression) は、1 つの特徴量から予測を行う最もシンプルな回帰分析です。
ここで、\(a\) は傾き(係数)、\(b\) は切片です。
5.2.2 NumPy を使った実装¶
まずは、NumPy の配列を使った実装を見ていきましょう。
データを訓練データとテストデータに分割するためには、sklearn.model_selection.train_test_split() 関数を使用します。
線形回帰モデルを作成するためには、sklearn.linear_model.LinearRegression() クラスを使用します。モデルを学習するには .fit() メソッド、予測を行うには .predict() メソッドを使用します。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# サンプルデータの作成(広告費と売上の関係)
# X: 広告費(万円)、y: 売上(万円)
X = np.array([[5], [10], [15], [20], [25], [30], [35], [40], [45], [50]])
y = np.array([15, 28, 35, 48, 55, 62, 70, 80, 88, 95])
# データを訓練データとテストデータに分割(8:2)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 線形回帰モデルの作成と学習
model = LinearRegression()
model.fit(X_train, y_train)
# テストデータで予測
y_pred = model.predict(X_test)
# 可視化
plt.scatter(X_train, y_train, color='blue', s=100, label='訓練データ', alpha=0.6)
plt.scatter(X_test, y_test, color='green', s=100, label='テストデータ', alpha=0.6)
plt.plot(X, model.predict(X), color='red', label='回帰直線')
plt.xlabel('広告費(万円)')
plt.ylabel('売上(万円)')
plt.title('単回帰分析(広告費 vs 売上)')
plt.legend()
plt.grid(alpha=0.3)
plt.show()
5.2.3 pandas を使った実装¶
次に、pandas の DataFrame を使った実装例です。実際のデータ分析では、pandas を使うことが多いです。
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# pandas DataFrame でデータを作成
data = pd.DataFrame({
'広告費': [5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
'売上': [15, 28, 35, 48, 55, 62, 70, 80, 88, 95]
})
# 特徴量とラベルを分離
X = data[['広告費']] # DataFrame として保持
y = data['売上'] # Series として保持
# データを訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# モデルの学習
model = LinearRegression()
model.fit(X_train, y_train)
# 予測
y_pred = model.predict(X_test)
# 可視化
plt.scatter(X_train, y_train, color='blue', s=100, label='訓練データ', alpha=0.6)
plt.scatter(X_test, y_test, color='green', s=100, label='テストデータ', alpha=0.6)
plt.plot(X, model.predict(X), color='red', label='回帰直線')
plt.xlabel('広告費(万円)')
plt.ylabel('売上(万円)')
plt.title('単回帰分析(広告費 vs 売上)')
plt.legend()
plt.grid(alpha=0.3)
plt.show()
上記コードの結果は NumPy を使った場合も pandas を使った場合も全く同じで、以下の画像が出力されます。
機械学習における実装の違い
NumPy 配列での実装:
- コード例:
X = np.array([[5], [10], [15]]) - 2次元配列として明示的に形状を指定する必要がある
- シンプルで高速な数値計算に適している
pandas での実装:
- コード例:
X = df[['広告費']] - 列名で指定できるため、コードの可読性が高い
- データの前処理(欠損値処理、統計情報確認など)が容易
どちらの方法でも scikit-learn の機械学習モデルは同じように動作します。実務では pandas を使うことが多いですが、両方使えるようになっておきましょう(詳細は 5.1.7 項 を参照)。
5.3 重回帰分析¶
5.3.1 重回帰分析とは¶
重回帰分析(Multiple Linear Regression) は、複数の特徴量を使って予測を行う回帰分析です。
ここで、\(w_1, w_2, \ldots, w_n\) は各特徴量の重み(係数)、\(b\) は切片です。
graph LR
A1[x1: 面積] --> B[重回帰モデル<br/>y = w1·x1 + w2·x2 + w3·x3 + b]
A2[x2: 築年数] --> B
A3[x3: 駅距離] --> B
B --> C[y: 価格]
style A1 fill:#e6f3ff
style A2 fill:#e6f3ff
style A3 fill:#e6f3ff
style B fill:#fff4cc
style C fill:#ffffcc
5.3.2 重回帰分析の実装¶
pandas を使用した重回帰分析の実装例は以下のようになります。基本的な流れは単回帰分析を変わりません。
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# データ作成
data = pd.DataFrame({
'面積': [60, 80, 100, 50, 70, 90, 55, 85, 75, 95],
'築年数': [15, 5, 2, 20, 10, 3, 18, 7, 12, 4],
'価格': [3200, 4500, 5800, 2800, 3800, 5200, 2900, 4300, 3900, 5400]
})
# モデル学習
X = data[['面積', '築年数']]
y = data['価格']
model = LinearRegression().fit(X, y)
# 結果表示
print(f"予測式: 価格 = {model.intercept_:.1f} + {model.coef_[0]:.1f} × 面積 + {model.coef_[1]:.1f} × 築年数")
# 可視化
y_pred = model.predict(X)
plt.scatter(y, y_pred, s=100, alpha=0.6)
plt.plot([y.min(), y.max()], [y.min(), y.max()], color='red', label='理想的な予測')
plt.xlabel('実測値(万円)')
plt.ylabel('予測値(万円)')
plt.title('住宅価格予測')
plt.legend()
plt.grid(alpha=0.3)
plt.show()
係数の解釈
学習された係数から、以下のような解釈ができます:
- 面積の係数が正で大きい → 面積が広いほど価格が高くなる
- 築年数の係数が負 → 築年数が経つほど価格が下がる
係数の絶対値が大きいほど、その特徴量が予測に強く影響していることを示します。
5.4 モデルの性能評価指標¶
5.4.1 回帰問題の評価指標¶
回帰問題では、主に以下の評価指標が使用されます。
graph TB
A[回帰モデルの評価指標] --> B[MSE<br/>平均二乗誤差]
A --> C[RMSE<br/>平均二乗平方根誤差]
A --> D[MAE<br/>平均絶対誤差]
A --> E[R²<br/>決定係数]
B --> B1[単位: 元の値の二乗<br/>大きな誤差に敏感]
C --> C1[単位: 元の値と同じ<br/>最も一般的]
D --> D1[単位: 元の値と同じ<br/>外れ値に強い]
E --> E1[範囲: -∞~1<br/>モデルの説明力]
style A fill:#e6ccff
style B fill:#ffcccc
style C fill:#ccffcc
style D fill:#ffffcc
style E fill:#e6f3ff
5.4.2 MSE(平均二乗誤差)¶
平均二乗誤差(Mean Squared Error) は、予測値と実際の値の差の二乗の平均です。
特徴:
- 値が小さいほど良い
- 大きな誤差を重視する(二乗しているため)
- 単位が元の値の二乗になる
5.4.3 RMSE(平均二乗平方根誤差)¶
平均二乗平方根誤差(Root Mean Squared Error) は、MSE の平方根です。
特徴:
- 値が小さいほど良い
- 元のデータと同じ単位(解釈しやすい)
- 最も一般的に使われる指標
5.4.4 MAE(平均絶対誤差)¶
平均絶対誤差(Mean Absolute Error) は、予測値と実際の値の差の絶対値の平均です。
特徴:
- 値が小さいほど良い
- 外れ値の影響を受けにくい
- すべての誤差を均等に扱う
5.4.5 R²(決定係数)¶
決定係数(R-squared) は、モデルがデータの変動をどれだけ説明できているかを示します。
特徴:
- 範囲は通常 0 から 1(負の値もあり得る)
- 1 に近いほど良い
- モデルの説明力を示す最も一般的な指標
R² の解釈
- R² = 0.9: 非常に良い(データの90%を説明)
- R² = 0.7: まあまあ良い(データの70%を説明)
- R² = 0.5: 改善の余地あり(データの50%を説明)
- R² = 0.1: モデルを見直すべき(データの10%しか説明できていない)
5.4.6 評価指標の実装¶
評価指標を計算するためには、scikit-learn の metrics モジュールを使用します。MSE を計算するには sklearn.metrics.mean_squared_error() 関数、R² を計算するには sklearn.metrics.r2_score() 関数を使用します。
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
# サンプルデータ作成
np.random.seed(42)
X = np.random.rand(100, 3) * 10
y = 2 * X[:, 0] + 3 * X[:, 1] - 1.5 * X[:, 2] + 10 + np.random.randn(100) * 2
# データ分割(訓練80% / テスト20%)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# モデル学習
model = LinearRegression()
model.fit(X_train, y_train)
# 性能評価
train_r2 = model.score(X_train, y_train)
test_r2 = model.score(X_test, y_test)
test_rmse = np.sqrt(mean_squared_error(y_test, model.predict(X_test)))
# 結果表示
print("=" * 40)
print("モデルの性能評価")
print("=" * 40)
print(f"訓練データのR²:\t{train_r2:.4f}")
print(f"テストデータのR²:\t{test_r2:.4f} ← 汎化性能")
print(f"テストRMSE:\t{test_rmse:.4f} ← 予測誤差")
# 過学習チェック
print("\n" + "=" * 40)
print("過学習チェック")
print("=" * 40)
diff = abs(train_r2 - test_r2)
print(f"R²の差: {diff:.4f}")
if diff < 0.05:
print("→ 過学習なし(良好)")
elif diff < 0.15:
print("→ 若干の過学習あり")
else:
print("→ 過学習が発生")
========================================
モデルの性能評価
========================================
訓練データのR²: 0.9814
テストデータのR²: 0.9427 ← 汎化性能
テストRMSE: 2.8407 ← 予測誤差
========================================
過学習チェック
========================================
R²の差: 0.0387
→ 過学習なし(良好)
5.5 多項式回帰¶
5.5.1 多項式回帰とは¶
線形回帰モデルでは直線でしか近似できませんが、多項式回帰(Polynomial Regression) を使うことで、曲線的な関係も表現できます。
例えば、2 次の多項式回帰は以下のように表されます:
graph LR
A[元の特徴量<br/>x] --> B[多項式特徴量生成<br/>x, x²]
B --> C[線形回帰<br/>y = w1·x + w2·x² + b]
C --> D[予測値 y]
style A fill:#e6f3ff
style B fill:#fff4cc
style C fill:#ffcccc
style D fill:#ffffcc
5.5.2 多項式回帰の実装例¶
多項式特徴量を生成するためには、sklearn.preprocessing.PolynomialFeatures() クラスを使用します。
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)
X = np.sort(np.random.rand(50, 1) * 10, axis=0)
y = 0.5 * X.squeeze() ** 2 - 3 * X.squeeze() + 20 + np.random.randn(50) * 5
# 異なる次数で多項式回帰
degrees = [1, 2, 3, 5]
fig, axes = plt.subplots(2, 2, figsize=(12, 9))
axes = axes.ravel()
for i, degree in enumerate(degrees):
# 多項式変換して学習
poly = PolynomialFeatures(degree=degree)
X_poly = poly.fit_transform(X)
model = LinearRegression().fit(X_poly, y)
# 予測曲線の生成
X_plot = np.linspace(0, 10, 100).reshape(-1, 1)
y_plot = model.predict(poly.transform(X_plot))
# R²の計算
r2 = r2_score(y, model.predict(X_poly))
# グラフ描画
axes[i].scatter(X, y, alpha=0.6, s=50)
axes[i].plot(X_plot, y_plot, 'r-', linewidth=2)
axes[i].set_title(f'{degree}次多項式 (R² = {r2:.3f})')
axes[i].grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 結果比較
print("次数 R²")
print("-" * 15)
for degree in degrees:
poly = PolynomialFeatures(degree=degree)
X_poly = poly.fit_transform(X)
model = LinearRegression().fit(X_poly, y)
r2 = r2_score(y, model.predict(X_poly))
print(f"{degree}次: {r2:.4f}")
多項式回帰の注意点
次数を上げすぎると、過学習が発生しやすくなります。
- 次数が低すぎる:モデルがデータの複雑さを捉えられない(未学習)
- 次数が適切:データのパターンをうまく捉えられる
- 次数が高すぎる:訓練データに過度に適合し、新しいデータでの性能が悪化(過学習)
適切な次数は、交差検証などで決定します。
5.6 正則化(Ridge 回帰・Lasso 回帰)¶
5.6.1 正則化とは¶
正則化(Regularization) は、過学習を防ぐための手法です。モデルの係数が大きくなりすぎないように制約を加えます。本講義資料では概要だけ紹介しますので、興味のある人は調べてみてください。
graph TB
A[線形回帰モデル] --> B[通常の線形回帰<br/>LinearRegression]
A --> C[Ridge回帰<br/>L2正則化]
A --> D[Lasso回帰<br/>L1正則化]
A --> E[ElasticNet<br/>L1+L2正則化]
B --> B1[係数に制約なし<br/>過学習しやすい]
C --> C1[係数を小さく抑える<br/>すべての特徴量を使用]
D --> D1[不要な特徴量の係数を0に<br/>特徴量選択の効果]
E --> E1[RidgeとLassoの両方の性質]
style A fill:#e6ccff
style B fill:#ffffcc
style C fill:#ccffcc
style D fill:#ffcccc
style E fill:#e6f3ff
5.6.2 Ridge 回帰¶
Ridge 回帰 は、L2 正則化を使った線形回帰です。係数の二乗和を小さく抑えます。
ここで、\(\alpha\) は正則化の強さを制御するハイパーパラメータです。
Ridge 回帰を実装するためには、sklearn.linear_model.Ridge() クラスを使用します。
5.6.3 Lasso 回帰¶
Lasso 回帰 は、L1 正則化を使った線形回帰です。不要な特徴量の係数を 0 にする効果があります。
Lasso 回帰を実装するためには、sklearn.linear_model.Lasso() クラスを使用します。
Ridge vs Lasso
Ridge 回帰:
- すべての特徴量を使用する(係数は小さくなるが0にはならない)
- 多重共線性に強い
- 予測精度を重視する場合に使用
Lasso 回帰:
- 不要な特徴量の係数を0にする(自動的に特徴量選択)
- 解釈性を重視する場合に使用
- 特徴量が多い場合に有効
どちらを使うべきか:
- 特徴量が多く、重要なものを選びたい → Lasso
- すべての特徴量が重要だが、過学習を防ぎたい → Ridge
- 両方の性質が欲しい → ElasticNet(Ridge + Lasso)
5.7 実データでの実践¶
これまで学んだ技術を使って、実際のデータセットで回帰分析を実践してみましょう。ここでは Seaborn ライブラリに含まれる tips データセット(レストランのチップのデータ)を使用します。
5.7.1 Seaborn のデータセット¶
Seaborn は可視化ライブラリですが、学習用のサンプルデータセットも提供しています。主なデータセットには以下のようなものがあります:
- tips: レストランのチップに関するデータ
- iris: アヤメの花の分類データ
- penguins: ペンギンの種類と身体測定データ
- diamonds: ダイヤモンドの価格と特徴
その他のデータセット
Seabornには他にも以下のようなデータセットがあります:
- titanic: タイタニック号の乗客データ(生存予測)
- flights: 月別・年別の航空機の乗客数
- mpg: 自動車の燃費データ
- planets: 太陽系外惑星のデータ
- taxis: ニューヨークのタクシーの乗車データ
すべてのデータセット一覧は sns.get_dataset_names() で確認できます。
今回は、チップの金額を予測する回帰問題として tips データセット を使います。
5.7.2 データセットの読み込みと確認¶
Seaborn のデータセットを読み込むためには、seaborn.load_dataset() 関数を使用します。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
# Seaborn から tips データセットを読み込み
tips = sns.load_dataset('tips')
# データの先頭を確認
print("=== データの先頭5行 ===")
print(tips.head())
# データの形状
print(f"\nデータの形状: {tips.shape}")
print(f"({tips.shape[0]}件のデータ、{tips.shape[1]}個の列)")
# データの統計情報
print("\n=== 数値データの統計情報 ===")
print(tips.describe())
# データ型の確認
print("\n=== データ型 ===")
print(tips.dtypes)
# 欠損値の確認
print("\n=== 欠損値の確認 ===")
print(tips.isnull().sum())
=== データの先頭5行 ===
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
データの形状: (244, 7)
(244件のデータ、7個の列)
=== 数値データの統計情報 ===
total_bill tip size
count 244.000000 244.000000 244.000000
mean 19.785943 2.998279 2.569672
std 8.902412 1.383638 0.951100
min 3.070000 1.000000 1.000000
25% 13.347500 2.000000 2.000000
50% 17.795000 2.900000 2.000000
75% 24.127500 3.562500 3.000000
max 50.810000 10.000000 6.000000
=== データ型 ===
total_bill float64
tip float64
sex category
smoker category
day category
time category
size int64
dtype: object
=== 欠損値の確認 ===
total_bill 0
tip 0
sex 0
smoker 0
day 0
time 0
size 0
dtype: int64
tips データセットの列
- total_bill: 食事代の合計金額(ドル)
- tip: チップの金額(ドル)← 予測対象
- sex: 性別(Male / Female)
- smoker: 喫煙者かどうか(Yes / No)
- day: 曜日(Thur / Fri / Sat / Sun)
- time: 時間帯(Lunch / Dinner)
- size: グループの人数
5.7.3 データの可視化¶
データの関係性を視覚的に確認してみましょう。
# 食事代とチップの関係を散布図で表示
plt.figure(figsize=(8, 6))
plt.scatter(tips['total_bill'], tips['tip'], alpha=0.6, s=50)
plt.xlabel('食事代(ドル)')
plt.ylabel('チップ(ドル)')
plt.title('食事代 vs チップ')
plt.grid(True, alpha=0.3)
plt.show()
データの傾向
散布図から、食事代が高いほどチップも高くなる傾向が見て取れます。このような関係性を数式で表現するのが回帰分析です。
演習問題では、このデータセットを使って実際に単回帰分析と重回帰分析を実装し、予測精度を評価していきます。
5.7.4 他のデータセットの紹介¶
Seaborn には他にもさまざまなデータセットがあります。ここでは、回帰分析の練習に使える 3 つのデータセットを紹介します。
iris データセット
iris(アヤメ)データセット は、アヤメの花の測定データです(150件)。
列の説明:
- sepal_length: がく片の長さ(cm)
- sepal_width: がく片の幅(cm)
- petal_length: 花弁の長さ(cm)
- petal_width: 花弁の幅(cm)
- species: アヤメの種類(setosa / versicolor / virginica)
データの読み込み例:
単回帰の例: 花弁の長さ(petal_length)から幅(petal_width)を予測
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
X = iris[['petal_length']]
y = iris['petal_width']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
print(f"R² = {model.score(X_test, y_test):.4f}")
重回帰の例: がく片・花弁の長さから花弁の幅を予測
penguins データセット
penguins(ペンギン)データセット は、南極のペンギン3種の測定データです(344件、欠損値あり)。
列の説明:
- species: ペンギンの種類(Adelie / Chinstrap / Gentoo)
- island: 生息する島(Torgersen / Biscoe / Dream)
- bill_length_mm: くちばしの長さ(mm)
- bill_depth_mm: くちばしの深さ(mm)
- flipper_length_mm: ひれの長さ(mm)
- body_mass_g: 体重(g)
- sex: 性別(Male / Female)
データの読み込み例:
import seaborn as sns
penguins = sns.load_dataset('penguins')
# 欠損値を削除
penguins_clean = penguins.dropna()
print(penguins_clean.head())
単回帰の例: ひれの長さ(flipper_length_mm)から体重(body_mass_g)を予測
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
penguins_clean = penguins.dropna()
X = penguins_clean[['flipper_length_mm']]
y = penguins_clean['body_mass_g']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
print(f"R² = {model.score(X_test, y_test):.4f}")
重回帰の例: くちばし・ひれの測定値から体重を予測
X = penguins_clean[['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm']]
y = penguins_clean['body_mass_g']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
print(f"R² = {model.score(X_test, y_test):.4f}")
diamonds データセット
diamonds(ダイヤモンド)データセット は、ダイヤモンドの特徴と価格のデータです(約54,000件)。
列の説明:
- carat: カラット(重さ)
- cut: カットの品質(Fair / Good / Very Good / Premium / Ideal)
- color: 色(D / E / F / G / H / I / J)← Dが最高品質
- clarity: 透明度(I1 / SI2 / SI1 / VS2 / VS1 / VVS2 / VVS1 / IF)
- depth: 全体の深さの割合(%)
- table: テーブル面の幅の割合(%)
- price: 価格(ドル)
- x: 長さ(mm)
- y: 幅(mm)
- z: 深さ(mm)
データの読み込み例:
import seaborn as sns
diamonds = sns.load_dataset('diamonds')
# データが大きいのでサンプリングして使用
diamonds_sample = diamonds.sample(5000, random_state=42)
print(diamonds_sample.head())
単回帰の例: カラット(carat)から価格(price)を予測
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
diamonds_sample = diamonds.sample(5000, random_state=42)
X = diamonds_sample[['carat']]
y = diamonds_sample['price']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
print(f"R² = {model.score(X_test, y_test):.4f}")
重回帰の例: カラット・寸法から価格を予測
データセットの選び方
- iris: 小規模でシンプル、初めての実践に最適
- penguins: 欠損値の処理を学べる、現実的なデータ
- diamonds: 大規模データ、サンプリングの実践
これらのデータセットを使って、自分でさまざまな特徴量の組み合わせを試してみましょう。演習問題でこれらのデータセットを使った回帰分析に挑戦します。
5.7.5 おまけ:カテゴリ変数の扱い方(ダミー変数化)¶
これまでは数値データのみを使った回帰分析を行ってきましたが、実際のデータには カテゴリ変数(文字列や分類データ)が含まれることがよくあります。tips データセットの例では、sex(性別)、smoker(喫煙者かどうか)、day(曜日)、time(時間帯)などがカテゴリ変数です。
カテゴリ変数とは
カテゴリ変数は、数値ではなく分類や名前で表現されるデータです:
- 性別: Male, Female
- 曜日: Mon, Tue, Wed, Thu, Fri, Sat, Sun
- 地域: 東京, 大阪, 福岡
機械学習モデル(線形回帰など)は数値データしか扱えないため、カテゴリ変数を数値に変換する必要があります。
ダミー変数化(One-Hot Encoding)¶
ダミー変数化 は、カテゴリ変数を 0 と 1 の列に変換する手法です。
例えば、sex が Male / Female の 2 値を取る場合:
| 元データ | sex_Male |
|---|---|
| Male | 1 |
| Female | 0 |
day が Thur / Fri / Sat / Sun の 4 値を取る場合(drop_first=True を使用):
| 元データ | day_Fri | day_Sat | day_Sun |
|---|---|---|---|
| Thur | 0 | 0 | 0 |
| Fri | 1 | 0 | 0 |
| Sat | 0 | 1 | 0 |
| Sun | 0 | 0 | 1 |
drop_first=True について
drop_first=True を指定すると、最初のカテゴリを削除します。これは 多重共線性 を避けるためです。
例えば、sex_Male と sex_Female の両方を含めると、一方が分かればもう一方も自動的に決まってしまい、モデルが不安定になります。
pandas でのダミー変数化¶
カテゴリ変数をダミー変数に変換するためには、pandas.get_dummies() 関数を使用します。
import pandas as pd
import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
# tips データセットの読み込み
tips = sns.load_dataset('tips')
# カテゴリ変数をダミー変数化
tips_encoded = pd.get_dummies(tips, columns=['sex', 'smoker', 'day', 'time'],
drop_first=True)
print("=== ダミー変数化前 ===")
print(tips.head())
print(f"列数: {tips.shape[1]}")
print("\n=== ダミー変数化後 ===")
print(tips_encoded.head())
print(f"列数: {tips_encoded.shape[1]}")
# 特徴量とターゲットを分離(tipを除くすべての列を特徴量として使用)
X = tips_encoded.drop('tip', axis=1)
y = tips_encoded['tip']
# データ分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# モデルの学習
model = LinearRegression()
model.fit(X_train, y_train)
# 係数の確認(上位5つ)
coef_df = pd.DataFrame({
'Feature': X.columns,
'Coefficient': model.coef_
}).sort_values('Coefficient', key=abs, ascending=False)
print("\n=== 各特徴量の係数(上位5つ) ===")
print(coef_df.head())
# 性能評価
r2 = model.score(X_test, y_test)
print(f"\n=== 性能評価 ===")
print(f"R²: {r2:.4f}")
=== ダミー変数化前 ===
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
列数: 7
=== ダミー変数化後 ===
total_bill tip size sex_Female ... day_Fri day_Sat day_Sun time_Dinner
0 16.99 1.01 2 True ... False False True True
1 10.34 1.66 3 False ... False False True True
2 21.01 3.50 3 False ... False False True True
3 23.68 3.31 2 False ... False False True True
4 24.59 3.61 4 True ... False False True True
[5 rows x 9 columns]
列数: 9
=== 各特徴量の係数(上位5つ) ===
Feature Coefficient
1 size 0.233484
3 smoker_No 0.192353
4 day_Fri 0.179721
6 day_Sun 0.128928
7 time_Dinner -0.094957
=== 性能評価 ===
R²: 0.4373
ダミー変数化のポイント
いつ使うか:
- カテゴリ変数(文字列や分類データ)を機械学習モデルで使いたい場合
使い方:
pd.get_dummies(df, columns=['列名'], drop_first=True)
注意点:
drop_first=Trueを指定して多重共線性を回避- カテゴリが多い場合、列数が大幅に増加する
- 順序がある場合(例: 小・中・大)は、別の方法(順序エンコーディング)も検討
実データでの実践のまとめ
- Seabornのデータセット:
sns.load_dataset()さまざまなデータセットを読み込める - データの確認:
head(),describe(),dtypes,isnull()でデータを把握 - データの可視化: 散布図でデータの傾向を視覚的に確認
- カテゴリ変数の処理:
pd.get_dummies()でダミー変数化 - データセットの選び方: 目的に応じて適切なデータセットを選択
5.8 演習問題¶
以下の演習問題では、5.7 で学んだ tips データセットを使って、実際に単回帰分析と重回帰分析を実装していきます。
まず、データを読み込んでおきましょう:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
# tips データセットの読み込み
tips = sns.load_dataset('tips')
print(tips.head())
演習 5-1: tips データセットで単回帰分析¶
演習 5-1
tips データセットを使って、食事代(total_bill)からチップ(tip)を予測する単回帰分析を実装してください。
タスク:
- データの読み込みと基本情報の確認(先頭5行、統計情報)
- 散布図を作成して、total_bill と tip の関係を可視化
- データを訓練データとテストデータに分割(8:2)
- 線形回帰モデルを学習
- 係数と切片を表示
- テストデータで予測し、RMSE と R² を計算
- 訓練データ、テストデータ、回帰直線を1つのグラフに可視化
演習 5-2: tips データセットで重回帰分析¶
演習 5-2
tips データセットを使って、食事代(total_bill)とグループサイズ(size)から、チップ(tip)を予測する重回帰分析を実装してください。
タスク:
- データを訓練データとテストデータに分割(8:2)
- 重回帰モデルを学習
- 各特徴量の係数を表示
- テストデータで予測し、RMSE と R² を計算
- 実際の値 vs 予測値のグラフを作成
5.9 課題 5: 回帰分析の実践¶
課題 5
Seaborn から iris、penguins、diamonds のいずれかのデータセットを選び、適切な特徴量とラベルを設定したうえで、単回帰分析と重回帰分析を実装してください。また、モデルの性能を比較し、考察してください。
提出物:
- Word ファイルで作成し、PDF 形式で出力して提出(学生番号_氏名_5.pdf)
- Python のソースコードと結果を PDF 内に含めること(参考:
機械学習_レポートサンプル.docx)
提出期限: 2025年11月5日(水)23:59
提出先:manaba
ヒント
特徴量(説明変数)とラベル(目的変数)の組み合わせ例:
- iris: 花弁の長さ(petal_length)から幅(petal_width)を予測
- penguins: ひれの長さ(flipper_length_mm)から体重(body_mass_g)を予測
- diamonds: カラット(carat)から価格(price)を予測
※データセットの詳細は「5.7.4 他のデータセットの紹介」を参照
5.10 まとめ¶
本章では、機械学習における基本的なモデルについて学びました。
第 5 回のまとめ
pandas の基礎
- pandas は表形式データを扱うための超重要なライブラリ
- Series: 1次元データ構造(インデックス付き配列)
- DataFrame: 2次元データ構造(表形式データ)
- CSV、Excelファイルの読み込み・書き出しが簡単
- 欠損値の処理が容易(
isnull(),dropna(),fillna()) - 統計情報の確認が簡単(
describe(),mean(),sum()など) - NumPyとの相互変換が可能(
df.values,pd.DataFrame())
単回帰分析
- 1つの特徴量から予測を行う最もシンプルな手法
- NumPy配列とpandas DataFrameの両方で実装できる
- 実務ではpandasを使うことが多い
重回帰分析
- 複数の特徴量を使ってより精度の高い予測を実現
- 係数を見ることで、各特徴量の影響度を理解できる
モデルの性能評価
- MSE: 誤差の二乗の平均
- RMSE: MSEの平方根(最も一般的)
- MAE: 誤差の絶対値の平均
- R²: モデルの説明力(0〜1、1に近いほど良い)
- 訓練データとテストデータの両方で評価し、過学習を検出
(以下はおまけ)
多項式回帰
- 非線形な関係を表現できる
- 次数を上げすぎると過学習のリスク
- 適切な次数は交差検証で決定
正則化(Ridge・Lasso)
- Ridge回帰: すべての特徴量を使用、係数を小さく抑える
- Lasso回帰: 不要な特徴量の係数を0にする(特徴量選択)
- 過学習を防ぐための重要な手法
次回以降は、分類問題や、より高度な機械学習手法を学んでいきます。


