scikit-learn SGDClassifier partial_fit() 日本語解説とプログラミング例

2025-05-26

linear_model.SGDClassifier.partial_fit() は、scikit-learn ライブラリの線形モデルである SGDClassifier (確率的勾配降下法を用いた分類器) の持つメソッドの一つです。このメソッドの主な特徴は、データを一度に全てメモリに読み込む必要がなく、ミニバッチと呼ばれる小さなデータチャンクごとにモデルを逐次的に学習できる点です。

より詳しく説明すると、以下のようになります。

partial_fit() の役割

  • 初期化 (Initialization)
    最初の partial_fit() 呼び出し時には、学習するクラスラベルのリスト (classes) を指定する必要があります。これは、モデルが予測する可能性のある全てのクラスを事前に知っておく必要があるためです。
  • ミニバッチ学習 (Mini-batch Learning)
    データ全体を一度に処理するのではなく、小さなサブセット (ミニバッチ) を使ってモデルの重みを更新します。これにより、計算コストを抑えつつ、効率的な学習が可能になります。
  • オンライン学習 (Online Learning)
    大量のデータセットがあり、一度にメモリに収まらない場合や、新しいデータが継続的に入ってくるような状況で、モデルを少しずつ更新していくのに適しています。

基本的な使い方

from sklearn.linear_model import SGDClassifier
import numpy as np

# モデルの初期化 (最初の partial_fit でクラスラベルを指定)
clf = SGDClassifier(random_state=42)

# 最初のミニバッチデータと対応するラベル
X_batch1 = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_batch1 = np.array([0, 1, 1, 0])

# 最初の partial_fit 呼び出し (クラスラベルを指定)
clf.partial_fit(X_batch1, y_batch1, classes=np.array([0, 1]))

# 次のミニバッチデータとラベル
X_batch2 = np.array([[2, 2], [2, 3], [3, 2], [3, 3]])
y_batch2 = np.array([1, 0, 0, 1])

# 2回目の partial_fit 呼び出し (クラスラベルの指定は不要)
clf.partial_fit(X_batch2, y_batch2)

# ... 必要に応じてさらに partial_fit を呼び出す ...

# 学習済みモデルを使った予測
new_data = np.array([[4, 4], [0.5, 0.5]])
predictions = clf.predict(new_data)
print(predictions)

重要なポイント

  • 他のパラメータ
    SGDClassifier の他のハイパーパラメータ (学習率、正則化など) は、モデルの初期化時に指定できます。これらのパラメータは、partial_fit() を繰り返しても維持されます。
  • ミニバッチのサイズ
    partial_fit() に渡すミニバッチのサイズは、学習の効率や安定性に影響を与える可能性があります。適切なサイズはデータセットや問題によって異なります。
  • 後続の呼び出し
    2回目以降の partial_fit() の呼び出しでは、classes パラメータは不要です。モデルは最初の呼び出しで学習したクラスに基づいて学習を継続します。
  • 最初の呼び出しでの classes パラメータ
    最初の partial_fit() を呼び出す際には、classes パラメータに、データセットに存在する可能性のある全てのクラスラベルのリストを NumPy の配列として渡す必要があります。これにより、モデルは予測するべきクラスを事前に認識できます。

partial_fit() の利点

  • 高速なイテレーション
    ミニバッチごとにモデルを更新するため、全体を一度に学習するよりも早く結果を得られる場合があります。
  • オンライン学習
    新しいデータが到着するたびにモデルを更新できるため、動的な環境に適しています。
  • メモリ効率
    大規模なデータセットを扱う際に、一度にメモリにロードする必要がないため、メモリ使用量を抑えられます。
  • 収束の判断
    オンライン学習では、いつ学習を停止するかの判断が難しい場合があります。適切な評価指標を監視し、学習の進行状況を確認する必要があります。
  • データの順序
    データの提示順序がモデルの学習に影響を与える可能性があります。必要に応じて、ミニバッチをシャッフルすることを検討してください。
  • 最初のクラスラベル指定
    最初の partial_fit() で正しいクラスラベルを全て指定しないと、モデルが正しく学習できない可能性があります。


よくあるエラーとトラブルシューティング

    • エラー内容
      partial_fit() を最初に呼び出す際に、学習対象のクラスラベルのリスト (classes パラメータ) を指定し忘れた場合に発生します。SGDClassifier は、最初に与えられたクラスラベルの情報を基に内部状態を初期化するため、この情報が不可欠です。
    • トラブルシューティング
      • 最初の partial_fit() 呼び出し時に、classes パラメータに予測する可能性のある全てのクラスラベルを NumPy の配列として渡してください。例えば、二値分類の場合は classes=np.array([0, 1])、多クラス分類の場合は classes=np.array([0, 1, 2, ...]) のように指定します。
  1. ValueError: Number of features of the model must match the input. Model n_features is ... and input n_features is .... (ValueError: モデルの特徴量の数と入力の特徴量の数が一致する必要があります。モデルの特徴量の数は ... で、入力の特徴量の数は ... です。)

    • エラー内容
      partial_fit() に渡すデータの特徴量の数 (列数) が、モデルが期待する特徴量の数と一致しない場合に発生します。これは、異なる特徴量を持つデータで partial_fit() を呼び出したり、最初の partial_fit() と異なる特徴量のデータを与えたりした場合に起こります。
    • トラブルシューティング
      • partial_fit() に渡す全てのデータが、モデルが学習時に想定したのと同じ数の特徴量を持っていることを確認してください。
      • 特徴量エンジニアリングやデータの前処理のステップで、特徴量の数が意図せず変更されていないか確認してください。
  2. ValueError: y should be a 1d array, got an array of shape (n_samples, n_outputs) instead. (ValueError: y は 1次元の配列であるべきですが、形状 (n_samples, n_outputs) の配列が与えられました。)

    • エラー内容
      partial_fit() のターゲット変数 (ラベル) y が、scikit-learn が期待する1次元の配列の形式ではない場合に発生します。例えば、多クラス分類で one-hot エンコーディングされたラベルなどを誤って渡してしまうと、このエラーが出ることがあります。
    • トラブルシューティング
      • ターゲット変数 y が、各サンプルに対応するクラスラベルの1次元配列であることを確認してください。多クラス分類の場合でも、各サンプルは一つのクラスラベルを持つべきです。
  3. パフォーマンスの低下または収束の遅延

    • 問題
      エラーは発生しないものの、モデルの学習が遅かったり、期待する性能が得られなかったりする場合があります。
    • トラブルシューティング
      • 学習率 (learning_rate)
        SGDClassifier の学習率は、収束の速さや最終的な性能に大きく影響します。適切な学習率を設定するために、eta0 パラメータを調整したり、learning_rate パラメータに 'optimal''adaptive' などのスケジュールを設定したりすることを検討してください。
      • ミニバッチのサイズ
        partial_fit() に渡すミニバッチのサイズを調整してみてください。小さすぎるバッチサイズはノイズが多くなり、収束が不安定になる可能性があります。大きすぎるバッチサイズは、確率的勾配降下法のメリットを損なう可能性があります。
      • データのスケーリング
        線形モデルは、特徴量のスケールに敏感です。StandardScalerMinMaxScaler などを用いて、事前にデータをスケーリングすることを検討してください。
      • 正則化 (regularization)
        過学習を防ぐために、適切な正則化パラメータ (alpha パラメータ) を設定してください。l1_ratio パラメータで L1 正則化と L2 正則化の割合を調整することもできます。
      • エポック数 (学習回数)
        partial_fit() を呼び出す回数 (エポック数に相当) が不足している可能性があります。適切な回数だけ partial_fit() を繰り返してモデルを学習させてください。
      • データのシャッフル
        データの提示順序が学習に影響を与える可能性があるため、各エポックの前にデータをシャッフルすることを検討してください。ただし、オンライン学習のシナリオでは、新しいデータが順次到着するため、必ずしもシャッフルが適切とは限りません。
      • 損失関数 (loss)
        分類タスクに適した損失関数が選択されているか確認してください ('hinge' (SVM), 'log_loss' (ロジスティック回帰), 'modified_huber' など)。
  4. メモリ使用量の増加

    • 問題
      大量のデータを partial_fit() で繰り返し学習させていると、モデルの内部状態が肥大化し、メモリ使用量が増加する可能性があります。
    • トラブルシューティング
      • SGDClassifier は比較的メモリ効率の良いアルゴリズムですが、非常に高次元の特徴量を持つデータセットの場合には注意が必要です。
      • 特徴量選択や次元削減の手法を検討し、モデルに入力する特徴量の数を減らすことを検討してください。

一般的なトラブルシューティングのヒント

  • 公式ドキュメントを参照する
    scikit-learn の公式ドキュメントには、各クラスやメソッドの詳細な説明、パラメータ、使用例などが記載されています。
  • 最小限のコードで再現性を確認する
    問題が発生するコードをできるだけ小さくして、再現性を確認することで、原因の特定が容易になります。
  • エラーメッセージをよく読む
    エラーメッセージは、問題の原因を特定するための重要な情報を含んでいます。


例1: 基本的なオンライン学習の例 (二値分類)

この例では、小さなデータチャンク (ミニバッチ) を使って SGDClassifier を段階的に学習させる基本的な流れを示します。

from sklearn.linear_model import SGDClassifier
import numpy as np
from sklearn.metrics import accuracy_score

# 1. モデルの初期化 (最初の partial_fit でクラスラベルを指定)
clf = SGDClassifier(random_state=42)
classes = np.array([0, 1])  # 予測するクラスラベル

# 2. データの準備 (ここでは簡単な例としてNumPy配列を使用)
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1], [2, 2], [2, 3], [3, 2], [3, 3]])
y = np.array([0, 1, 1, 0, 1, 0, 0, 1])

# 3. データをミニバッチに分割 (簡単な例として手動で分割)
batch_size = 4
n_batches = len(X) // batch_size

for i in range(n_batches):
    start = i * batch_size
    end = (i + 1) * batch_size
    X_batch = X[start:end]
    y_batch = y[start:end]

    # 最初のバッチのみクラスラベルを指定
    if i == 0:
        clf.partial_fit(X_batch, y_batch, classes=classes)
    else:
        clf.partial_fit(X_batch, y_batch)

# 4. 学習済みモデルによる予測
X_test = np.array([[0.5, 0.5], [2.5, 2.5]])
y_pred = clf.predict(X_test)
print(f"予測結果: {y_pred}")

# 5. モデルの評価 (ここでは簡単な例として学習データ全体で評価)
y_true_all = y
y_pred_all = clf.predict(X)
accuracy = accuracy_score(y_true_all, y_pred_all)
print(f"学習データ全体の精度: {accuracy}")

例2: 大規模データセットに対するオンライン学習 (ジェネレータの使用)

この例では、メモリに全てロードできないような大規模なデータセットを想定し、ジェネレータを使ってデータをミニバッチごとに供給する方法を示します。

from sklearn.linear_model import SGDClassifier
import numpy as np
from sklearn.metrics import accuracy_score

# 1. モデルの初期化
clf = SGDClassifier(random_state=42)
classes = np.array([0, 1])

# 2. 大規模データセットをシミュレートするジェネレータ関数
def data_generator(n_samples, batch_size):
    for _ in range(n_samples // batch_size):
        X_batch = np.random.rand(batch_size, 2)  # ランダムな特徴量
        y_batch = np.random.randint(0, 2, batch_size)  # ランダムなラベル
        yield X_batch, y_batch

# 3. ジェネレータからミニバッチを取得して学習
n_total_samples = 1000
batch_size = 100
for i, (X_batch, y_batch) in enumerate(data_generator(n_total_samples, batch_size)):
    if i == 0:
        clf.partial_fit(X_batch, y_batch, classes=classes)
    else:
        clf.partial_fit(X_batch, y_batch)

# 4. 学習済みモデルによる予測 (新しいランダムデータで予測)
X_test = np.random.rand(5, 2)
y_pred = clf.predict(X_test)
print(f"予測結果: {y_pred}")

例3: 異なる特徴量を持つデータに対するエラーハンドリング

この例では、partial_fit() に異なる特徴量のデータを与えた場合に発生する ValueError を捕捉する方法を示します。

from sklearn.linear_model import SGDClassifier
import numpy as np

# 1. モデルの初期化
clf = SGDClassifier(random_state=42)
classes = np.array([0, 1])

# 2. 最初のミニバッチで学習 (2つの特徴量)
X_batch1 = np.array([[0, 0], [0, 1]])
y_batch1 = np.array([0, 1])
clf.partial_fit(X_batch1, y_batch1, classes=classes)

# 3. 次のミニバッチ (3つの特徴量)
X_batch2 = np.array([[1, 2, 3], [4, 5, 6]])
y_batch2 = np.array([1, 0])

# 4. 異なる特徴量のデータで partial_fit を呼び出すとエラーが発生する
try:
    clf.partial_fit(X_batch2, y_batch2)
except ValueError as e:
    print(f"エラーが発生しました: {e}")
    print("入力データの特徴量の数がモデルの期待する数と一致しません。")

例4: ストリームデータからのオンライン学習と評価

この例では、継続的に到着するストリームデータを想定し、学習と評価を並行して行う基本的な方法を示します。

from sklearn.linear_model import SGDClassifier
import numpy as np
from sklearn.metrics import accuracy_score

# 1. モデルの初期化
clf = SGDClassifier(random_state=42)
classes = np.array([0, 1])

# 2. ストリームデータをシミュレートするジェネレータ
def stream_data(n_samples):
    for _ in range(n_samples):
        X = np.random.rand(1, 2)
        y = np.random.randint(0, 2, 1)
        yield X, y

# 3. ストリームデータから学習と評価
n_stream_samples = 100
predictions = []
true_labels = []
for i, (X_stream, y_stream) in enumerate(stream_data(n_stream_samples)):
    if i < 10:  # 最初の数サンプルでクラスラベルを指定して学習
        clf.partial_fit(X_stream, y_stream, classes=classes)
    else:
        clf.partial_fit(X_stream, y_stream)
        y_pred = clf.predict(X_stream)
        predictions.append(y_pred[0])
        true_labels.append(y_stream[0])

# 4. 評価 (最初の数サンプル以降)
if predictions:
    accuracy = accuracy_score(true_labels, predictions)
    print(f"ストリームデータに対する精度 (最初の10サンプル以降): {accuracy}")


SGDClassifier を使ったバッチ学習 (通常の方法)

  • コード例

    from sklearn.linear_model import SGDClassifier
    import numpy as np
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import accuracy_score
    
    # データの準備
    X = np.array([[0, 0], [0, 1], [1, 0], [1, 1], [2, 2], [2, 3], [3, 2], [3, 3]])
    y = np.array([0, 1, 1, 0, 1, 0, 0, 1])
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    
    # モデルの初期化と学習 (fit メソッドを使用)
    clf = SGDClassifier(random_state=42)
    clf.fit(X_train, y_train)
    
    # 予測と評価
    y_pred = clf.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    print(f"精度: {accuracy}")
    
  • 欠点
    大規模なデータセットではメモリ不足になる可能性があります。オンライン学習や逐次的なモデル更新には向きません。

  • 利点
    実装がシンプルで、多くの場合、partial_fit() を細かく制御するよりも容易です。

  • 説明
    partial_fit() を使わず、データ全体を一度に fit() メソッドに渡して学習させる、scikit-learn の標準的な学習方法です。データセットがメモリに収まる場合に適しています。

他の線形モデルクラスの利用

  • コード例 (LogisticRegression)

    from sklearn.linear_model import LogisticRegression
    import numpy as np
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import accuracy_score
    
    # データの準備 (上記と同じ)
    X = np.array([[0, 0], [0, 1], [1, 0], [1, 1], [2, 2], [2, 3], [3, 2], [3, 3]])
    y = np.array([0, 1, 1, 0, 1, 0, 0, 1])
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    
    # モデルの初期化と学習
    clf = LogisticRegression(random_state=42)
    clf.fit(X_train, y_train)
    
    # 予測と評価
    y_pred = clf.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    print(f"精度: {accuracy}")
    
  • 欠点
    オンライン学習を直接サポートしていないモデルもあります。

  • 利点
    問題設定に適したアルゴリズムを選択できる可能性があります。

  • 代替モデルの例

    • LogisticRegression: ロジスティック回帰(二値分類、多クラス分類)。fit() メソッドを使用します。
    • LinearSVC: 線形サポートベクターマシン(二値分類、多クラス分類)。fit() メソッドを使用します。大規模データには loss='hinge' を指定した SGDClassifier が適している場合があります。
    • Perceptron: パーセプトロン(単純な線形分類器)。fit() メソッドを使用します。
  • 説明
    SGDClassifier 以外にも、scikit-learn には様々な線形モデルのクラスが存在します。データセットの特性や学習の目的に応じて、これらのモデルを使い分けることができます。

外部ライブラリの利用

  • コード例 (river を用いたオンライン学習)

    from river import linear_model
    from river import metrics
    from river import stream
    import numpy as np
    
    # モデルの初期化
    model = linear_model.LogisticRegression()
    metric = metrics.Accuracy()
    
    # ストリームデータのシミュレーション
    def generate_data(n_samples):
        for _ in range(n_samples):
            x = {"feature1": np.random.rand(), "feature2": np.random.rand()}
            y = 1 if x["feature1"] + x["feature2"] > 1 else 0
            yield x, y
    
    # ストリームデータで学習と評価
    n_samples = 200
    for x, y in stream.iter_array(generate_data(n_samples)):
        y_pred = model.predict_one(x)
        metric = metric.update(y, y_pred)
        model = model.fit_one(x, y)
    
    print(f"オンライン学習後の精度: {metric}")
    
  • 欠点
    scikit-learn とは異なるAPIや概念を学習する必要がある場合があります。

  • 利点
    大規模データや高頻度のデータストリームに対する効率的な学習が可能です。

  • 代替ライブラリの例

    • vowpal wabbit: 高速なオンライン学習アルゴリズムを提供します。コマンドラインツールとしても、Python ライブラリとしても利用できます。
    • river: オンライン機械学習に特化した比較的新しい Python ライブラリです。様々なオンライン学習アルゴリズムや評価指標を提供します。
  • 説明
    scikit-learn 以外にも、オンライン学習や大規模データ処理に特化したライブラリが存在します。これらのライブラリを利用することで、より高度なオンライン学習パイプラインを構築できる場合があります。

自作のオンライン学習ループ

  • 欠点
    実装が複雑で、バグを生みやすいです。scikit-learn のような最適化された実装と比較して、効率が劣る可能性があります。

  • 利点
    学習プロセスを完全に制御でき、特定のニーズに合わせたカスタマイズが可能です。

  • 説明
    scikit-learn の機能に頼らず、勾配降下法などの最適化アルゴリズムを自分で実装し、ミニバッチごとにモデルの重みを更新する学習ループを構築することも可能です。