scikit-learnのDigitsでロジスティック回帰とSVMを比較する:正解率だけで決めない機械学習入門

実験日: 2026-08-24 / Python 3.14.4 / scikit-learn 1.9.0
データ: 8×8手書き数字1,797枚、10クラス

機械学習の入門では「データを分けて、学習して、正解率を出す」ところまで簡単に進めます。しかし研究で重要なのは、その正解率を信用できる条件で測り、モデル間の差を説明することです。

正解率98%という1つの値だけでは、seedを変えたときの安定性までは分かりません。

今回はscikit-learn同梱のDigitsを使い、最多クラスを答えるだけのダミー分類器、ロジスティック回帰、RBF-SVMを比較しました。1回の分割だけで順位を決めず、6種類の乱数seedで学習/テスト分割を作り直し、精度の平均とばらつきも測ります。

まず結果

6分割のテスト正解率は次の通りでした。

モデル 平均正解率 標本SD 最小~最大
最多クラスDummy 10.09% 0.14ポイント 10.00~10.28%
ロジスティック回帰 97.04% 0.67ポイント 96.39~98.06%
RBF-SVM 98.06% 0.53ポイント 97.22~98.61%

RBF-SVMが平均で約1.02ポイント高精度でした。一方、seed=42の360枚を予測する時間は、ロジスティック回帰0.63 msに対してRBF-SVM 12.69 msで、SVMは約20倍かかりました。

最高正解率だけならSVM、軽い推論も重視するならロジスティック回帰という判断になります。これは8×8の小さなデータでの結果であり、別のデータにもそのまま一般化はできません。

Digitsでダミー分類器・ロジスティック回帰・RBF-SVMの正解率と混同行列を比較した実測図
Digitsでダミー分類器・ロジスティック回帰・RBF-SVMの正解率と混同行列を比較した実測図

データを「画像」と「64個の特徴量」の両方で見る

Digitsは0~9の手書き数字1,797枚です。各画像は8×8画素なので、分類器へ渡すときは64個の数値へ平坦化します。画素値は0~16です。

from sklearn.datasets import load_digits

digits = load_digits()
print(digits.images.shape)  # (1797, 8, 8)
print(digits.data.shape)    # (1797, 64)
print(digits.target.shape)  # (1797,)
  • images[i]:人間が見る8×8画像
  • data[i]:モデルへ入れる長さ64の特徴ベクトル
  • target[i]:正解ラベル

画像を平坦化すると、「左上の画素」と「その隣の画素」が隣接していた事実をモデルは明示的には扱いません。それでもこの小さな問題では古典手法が高精度を出せます。CNNとの比較記事では、空間構造を使う価値を同じ観点で確認します。

学習データとテストデータを分ける理由

学習に使った答案で試験をすれば、高得点でも未知データへの性能は分かりません。今回は80%を学習、20%をテストにしました。

x_train, x_test, y_train, y_test = train_test_split(
    digits.data,
    digits.target,
    test_size=0.2,
    random_state=42,
    stratify=digits.target,
)

random_stateは分割を再現するseedです。stratify=digits.targetは、0~9のクラス比率が学習側とテスト側で大きく崩れないようにします。

seed=42だけなら結果は一つですが、分割の偶然に左右されます。そこで0、1、2、3、4、42の6分割で同じ実験を繰り返しました。これは厳密な最終評価の代わりではありませんが、「1回だけ良い分割だった」を見抜く最初の一歩です。

最低限の比較相手としてDummyを入れる

10クラス分類なら、何も学習せず常に最多クラスを答えても約10%当たります。

from sklearn.dummy import DummyClassifier

dummy = DummyClassifier(strategy="most_frequent")

高度なモデルが60%を出して「高精度」と見えても、ベースラインが70%なら負けています。今回のDummy 10.09%に対し、ロジスティック回帰は97.04%なので、画素からラベルを予測する規則を学べていると判断できます。

標準化をPipelineへ入れてデータ漏洩を防ぐ

ロジスティック回帰とRBF-SVMの前にStandardScalerを入れました。

from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

model = make_pipeline(
    StandardScaler(),
    LogisticRegression(max_iter=3000, random_state=42),
)
model.fit(x_train, y_train)

標準化は各特徴量から学習データの平均を引き、標準偏差で割ります。大事なのは、テストデータを含めた全体で平均・標準偏差を先に計算しないことです。それをすると本来未知であるテスト分布の情報が学習工程へ漏れます。

Pipelineなら、fit(x_train, y_train)のときに学習データだけでScalerをfitし、テスト側には同じ変換を適用できます。精度を上げる魔法というより、処理順序の事故を防ぐ仕組みです。

ロジスティック回帰とRBF-SVMは何が違うか

ロジスティック回帰

64特徴量の重み付き和を基礎にクラスを分けます。決定境界は基本的に線形で、学習・推論が軽く、係数も調べやすいのが利点です。「回帰」という名前でも、ここでは分類に使います。

RBF-SVM

RBFカーネルにより、元の特徴空間で直線だけでは分けにくい境界を表現できます。今回はC=10.0gamma="scale"です。高精度になりやすい一方、サポートベクトルとの計算が必要で、推論コストが増える場合があります。

svm = make_pipeline(
    StandardScaler(),
    SVC(kernel="rbf", C=10.0, gamma="scale"),
)

今回はハイパーパラメータ探索をしていません。テストデータを見ながらCgammaを調整すると、テストセットまで実質的に学習へ使ってしまいます。調整するなら、学習データ内で交差検証し、最後のテストは一度だけ使います。

seed=42の速度と過学習を見る

モデル 学習正解率 テスト正解率 fit 360枚predict 誤分類
Dummy 10.0% 10.0% 0.14 ms 0.02 ms 324枚
ロジスティック回帰 100.0% 97.0% 18.32 ms 0.63 ms 10枚
RBF-SVM 100.0% 98.06% 41.97 ms 12.69 ms 7枚

両モデルとも学習正解率100%ですが、テストは下がりました。学習データへの適合と未知データへの汎化には差があります。ただし3ポイント程度の差だけで「深刻な過学習」と断定はしません。学習曲線や交差検証も合わせて判断します。

速度はこのPCの単発計測で、厳密な性能ベンチマークではありません。それでも、1ポイントの精度向上に推論約20倍という交換条件があることは確認できます。組込みや大量処理なら無視できない差です。

混同行列と誤分類画像を読む

正解率98%だけでは、どのクラスを間違えたか分かりません。混同行列は行が正解、列が予測です。対角成分が正解で、対角線から外れた値が誤分類です。

seed=42のRBF-SVMは、8→1、9→7、4→7、9→6、7→5、1→4、8→4の7枚を間違えました。

RBF-SVMが誤分類した7枚の8×8手書き数字と正解・予測ラベル
RBF-SVMが誤分類した7枚の8×8手書き数字と正解・予測ラベル

8×8まで縮小された数字は、人間にも曖昧に見えます。ここから「4と7の形状差を表す特徴が弱いのでは」「画素の空間構造を使うCNNなら変わるか」という次の仮説が生まれます。

この結果から言えること/言えないこと

言えること

  • 6分割すべてでロジスティック回帰とRBF-SVMはDummyを大幅に上回った
  • この設定ではRBF-SVMが平均約1.02ポイント高精度だった
  • seed=42ではRBF-SVMの360枚推論がロジスティック回帰より約20倍長かった
  • 誤分類は特定の数字対に集中し、画像を見れば曖昧さを検討できる

言えないこと

  • RBF-SVMがあらゆる画像分類で最良
  • 6 seedだけで母集団性能を完全に推定できる
  • このfit/predict時間が別PCでも再現する
  • テストセットを見ながら調整しても同じ98%を公正に報告できる

ニューラルネットと比較するなら

同じDigitsをNumPyだけで実装した2層ニューラルネットへ入力し、さらに同じ構造をPyTorchで書き直すと比較を広げられます。データ分割と評価条件を揃えれば、「ライブラリを変えたから」ではなく、モデルと最適化の違いを比較できます。

全体の学習順序は「機械学習からディープラーニングまでをちゃんと理解して、FPGAに持ち込みたい」にまとめています。本記事はPhase 1の実践編です。

再現性メモ

  • 実験コード:work/ai_lab/experiment_digits_classic.py
  • 生データ:work/ai_lab/results/digits_classic.json
  • 図:outputs/digits-classic-ml-results.pngoutputs/digits-rbf-svm-mistakes.png
  • Python 3.14.4、scikit-learn 1.9.0、NumPy 2.5.2
  • 6 seed、test 20%、stratifyあり、Scalerは学習データだけでfit

参考:

関連記事