Skip to content

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 という略称でインポートします。これは慣習的な書き方で、ほとんどのコードで使われています。

import pandas as pd
import numpy as np

5.1.3 主要なデータ構造

pandas には 2 つの主要なデータ構造があります。

Series(シリーズ)

Series は、1 次元のデータ構造です。NumPy の配列に「インデックス(ラベル)」が付いたものと考えることができます。

Series を作成するためには、pandas.Series() 関数を使用します。

リストから Series を作成
import pandas as pd
s = pd.Series([10, 20, 30, 40, 50])
print(s)
Output
0    10
1    20
2    30
3    40
4    50
dtype: int64
インデックスを指定して作成
import pandas as pd
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'])
print(s)
Output
a    10
b    20
c    30
d    40
e    50
dtype: int64
値へのアクセス
print(s['c'])  # 30
print(s.iloc[2])    # 30(位置でもアクセス可能)

DataFrame(データフレーム)

DataFrame は、2 次元のデータ構造(表形式データ)です。Excel のスプレッドシートや SQL のテーブルのようなものと考えることができます。

DataFrame を作成するためには、pandas.DataFrame() 関数を使用します。

辞書から DataFrame を作成
import pandas as pd

df = pd.DataFrame({
    'name': ['太郎', '花子', '次郎', '梅子'],
    'age': [25, 30, 35, 28],
    'city': ['茨城', '大阪', '京都', '東京']
})
print(df)
Output
  name  age city
0   太郎   25   茨城
1   花子   30   大阪
2   次郎   35   京都
3   梅子   28   東京
列へのアクセス(Series として取得)
print(df['name'])
Output
0    太郎
1    花子
2    次郎
3    梅子
Name: name, dtype: object
行へのアクセス
print(df.loc[0])     # インデックスで指定
Output
name    太郎
age     25
city    茨城
Name: 0, dtype: object
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()))
Output
=== 元のデータ ===
     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)
Output
=== 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)を行う
  • 例:ビジネスデータ分析、統計分析、機械学習の前処理

実務でのワークフロー:

  1. データ読み込み・前処理: pandas
  2. 機械学習モデルの学習: pandas または NumPy(どちらも可)
  3. 高度な数値計算: NumPy

pandas と NumPy は相互に変換可能なので、状況に応じて使い分けられます。

5.2 単回帰分析の復習と実践

5.2.1 単回帰分析とは

前回学んだように、単回帰分析(Simple Linear Regression) は、1 つの特徴量から予測を行う最もシンプルな回帰分析です。

\[ y = ax + b \]

ここで、\(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 を使った場合も全く同じで、以下の画像が出力されます。

単回帰分析(広告費 vs 売上)

機械学習における実装の違い

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) は、複数の特徴量を使って予測を行う回帰分析です。

\[ y = w_1x_1 + w_2x_2 + \cdots + w_nx_n + b \]

ここで、\(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()
Output
予測式: 価格 = 714.7 + 49.4 × 面積 + -30.4 × 築年数

重回帰分析

係数の解釈

学習された係数から、以下のような解釈ができます:

  • 面積の係数が正で大きい → 面積が広いほど価格が高くなる
  • 築年数の係数が負 → 築年数が経つほど価格が下がる

係数の絶対値が大きいほど、その特徴量が予測に強く影響していることを示します。

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) は、予測値と実際の値の差の二乗の平均です。

\[ \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 \]

特徴:

  • 値が小さいほど良い
  • 大きな誤差を重視する(二乗しているため)
  • 単位が元の値の二乗になる

5.4.3 RMSE(平均二乗平方根誤差)

平均二乗平方根誤差(Root Mean Squared Error) は、MSE の平方根です。

\[ \text{RMSE} = \sqrt{\text{MSE}} = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2} \]

特徴:

  • 値が小さいほど良い
  • 元のデータと同じ単位(解釈しやすい)
  • 最も一般的に使われる指標

5.4.4 MAE(平均絶対誤差)

平均絶対誤差(Mean Absolute Error) は、予測値と実際の値の差の絶対値の平均です。

\[ \text{MAE} = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i| \]

特徴:

  • 値が小さいほど良い
  • 外れ値の影響を受けにくい
  • すべての誤差を均等に扱う

5.4.5 R²(決定係数)

決定係数(R-squared) は、モデルがデータの変動をどれだけ説明できているかを示します。

\[ R^2 = 1 - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{n} (y_i - \bar{y})^2} \]

特徴:

  • 範囲は通常 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("→ 過学習が発生")
Output
========================================
モデルの性能評価
========================================
訓練データのR²:    0.9814
テストデータのR²:  0.9427  ← 汎化性能
テストRMSE:       2.8407  ← 予測誤差

========================================
過学習チェック
========================================
R²の差: 0.0387
→ 過学習なし(良好)

5.5 多項式回帰

5.5.1 多項式回帰とは

線形回帰モデルでは直線でしか近似できませんが、多項式回帰(Polynomial Regression) を使うことで、曲線的な関係も表現できます。

例えば、2 次の多項式回帰は以下のように表されます:

\[ y = w_2x^2 + w_1x + b \]
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}")
Output
次数  R²
---------------
1次:  0.4921
2次:  0.6792
3次:  0.6803
5次:  0.6874

多項式回帰

多項式回帰の注意点

次数を上げすぎると、過学習が発生しやすくなります。

  • 次数が低すぎる:モデルがデータの複雑さを捉えられない(未学習)
  • 次数が適切:データのパターンをうまく捉えられる
  • 次数が高すぎる:訓練データに過度に適合し、新しいデータでの性能が悪化(過学習)

適切な次数は、交差検証などで決定します。

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 正則化を使った線形回帰です。係数の二乗和を小さく抑えます。

\[ \text{Loss} = \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + \alpha \sum_{j=1}^{p} w_j^2 \]

ここで、\(\alpha\) は正則化の強さを制御するハイパーパラメータです。

Ridge 回帰を実装するためには、sklearn.linear_model.Ridge() クラスを使用します。

5.6.3 Lasso 回帰

Lasso 回帰 は、L1 正則化を使った線形回帰です。不要な特徴量の係数を 0 にする効果があります。

\[ \text{Loss} = \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + \alpha \sum_{j=1}^{p} |w_j| \]

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())
Output
=== データの先頭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)

データの読み込み例:

import seaborn as sns
iris = sns.load_dataset('iris')
print(iris.head())

単回帰の例: 花弁の長さ(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}")

重回帰の例: がく片・花弁の長さから花弁の幅を予測

X = iris[['sepal_length', 'sepal_width', '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}")

重回帰の例: カラット・寸法から価格を予測

X = diamonds_sample[['carat', 'depth', 'table', 'x', 'y', 'z']]
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 の列に変換する手法です。

例えば、sexMale / Female の 2 値を取る場合:

元データ sex_Male
Male 1
Female 0

dayThur / 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_Malesex_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}")
Output
=== ダミー変数化前 ===
   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 を指定して多重共線性を回避
  • カテゴリが多い場合、列数が大幅に増加する
  • 順序がある場合(例: 小・中・大)は、別の方法(順序エンコーディング)も検討

実データでの実践のまとめ

  1. Seabornのデータセット: sns.load_dataset() さまざまなデータセットを読み込める
  2. データの確認: head(), describe(), dtypes, isnull() でデータを把握
  3. データの可視化: 散布図でデータの傾向を視覚的に確認
  4. カテゴリ変数の処理: pd.get_dummies() でダミー変数化
  5. データセットの選び方: 目的に応じて適切なデータセットを選択

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)を予測する単回帰分析を実装してください。

タスク:

  1. データの読み込みと基本情報の確認(先頭5行、統計情報)
  2. 散布図を作成して、total_bill と tip の関係を可視化
  3. データを訓練データとテストデータに分割(8:2)
  4. 線形回帰モデルを学習
  5. 係数と切片を表示
  6. テストデータで予測し、RMSE と R² を計算
  7. 訓練データ、テストデータ、回帰直線を1つのグラフに可視化

演習 5-2: tips データセットで重回帰分析

演習 5-2

tips データセットを使って、食事代(total_bill)とグループサイズ(size)から、チップ(tip)を予測する重回帰分析を実装してください。

タスク:

  1. データを訓練データとテストデータに分割(8:2)
  2. 重回帰モデルを学習
  3. 各特徴量の係数を表示
  4. テストデータで予測し、RMSE と R² を計算
  5. 実際の値 vs 予測値のグラフを作成

5.9 課題 5: 回帰分析の実践

課題 5

Seaborn から irispenguinsdiamonds のいずれかのデータセットを選び、適切な特徴量とラベルを設定したうえで、単回帰分析と重回帰分析を実装してください。また、モデルの性能を比較し、考察してください。

提出物:

  • 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: 誤差の絶対値の平均
  • : モデルの説明力(0〜1、1に近いほど良い)
  • 訓練データとテストデータの両方で評価し、過学習を検出

(以下はおまけ)

多項式回帰

  • 非線形な関係を表現できる
  • 次数を上げすぎると過学習のリスク
  • 適切な次数は交差検証で決定

正則化(Ridge・Lasso)

  • Ridge回帰: すべての特徴量を使用、係数を小さく抑える
  • Lasso回帰: 不要な特徴量の係数を0にする(特徴量選択)
  • 過学習を防ぐための重要な手法

次回以降は、分類問題や、より高度な機械学習手法を学んでいきます。