AI・機械学習

NumPyだけで線形回帰をスクラッチ実装!scikit-learnに頼らずゼロから理解する

「機械学習って結局、ライブラリに任せてるだけで中身が全然わかってない…」

そんなモヤモヤを感じたことはありませんか? 今回はNumPyだけで線形回帰をゼロから実装するというアプローチを通じて、機械学習の本質をわかりやすく解説していきます 🚀

scikit-learnで1行で書けるけど…それって理解してる?

linear regression scratch python
linear regression scratch python / Photo by Mathews Jumba via Pexels

scikit-learn を使えば、線形回帰はこんな感じで一瞬ですよね。

from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(X, y)

でも「この fit() の中で何が起きてるの?」と聞かれたら、すぐ答えられますか? そこが今回のポイントです。

線形回帰って何をしてるの?

イメージとしては、散らばったデータ点をなるべくうまくカバーする直線を引く作業です。

数式で書くとこうなります。

y = a * x + b

この a(傾き)と b(切片)をどうやって求めるか、それが線形回帰の本体です。

たとえば「勉強時間」と「テストの点数」の関係を直線で表すなら、

  • x = 勉強時間
  • y = テストの点数
  • a = 1時間増えるごとに点数が何点上がるか(傾き)
  • b = 勉強時間ゼロのときの点数(切片)

という対応になります。この直線を「データに一番フィットするように」求めるのが線形回帰です。

最小二乗法って何?

直線を引くとき、「どの直線が一番いいか」を決める基準が必要です。そこで使われるのが最小二乗法(Least Squares Method)です。

考え方はシンプルで、「実際の値」と「直線が予測した値」の差(残差)を二乗して全部足したものを最小にする直線を選びます。

# 残差とは?
残差 = 実際のy - 予測したy

# 残差の二乗和を最小にする → それが最小二乗法

なぜ「二乗」するのかというと、プラスとマイナスの誤差が打ち消し合わないようにするためです。また二乗することで、大きな誤差により大きなペナルティがかかる、という性質もあります。

NumPyだけで約15行!スクラッチ実装コード

ポイントをまとめるとこんな感じです 👇

  • 最小二乗法を使って傾きと切片を計算する
  • NumPyの mean() と基本演算だけで実現できる
  • 難しい数式も「平均との差」を使った直感的な計算に落とし込める
import numpy as np

# サンプルデータ
x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([2.1, 4.0, 5.9, 8.1, 9.8], dtype=float)

# 平均を計算
x_mean = np.mean(x)
y_mean = np.mean(y)

# 傾き(slope)を計算
numerator   = np.sum((x - x_mean) * (y - y_mean))  # 共分散の分子
denominator = np.sum((x - x_mean) ** 2)             # 分散の分母
slope = numerator / denominator

# 切片(intercept)を計算
intercept = y_mean - slope * x_mean

# 予測してみる
y_pred = slope * x + intercept

# 結果を表示
print(f"傾き(slope)    : {slope:.4f}")
print(f"切片(intercept): {intercept:.4f}")
print(f"予測値           : {y_pred}")

実行するとこんな結果が得られます。

傾き(slope)    : 1.9300
切片(intercept): 0.1900
予測値           : [2.12 4.05 5.98 7.91 9.84]

実際のデータ(2.1, 4.0, 5.9, 8.1, 9.8)と比べてみると、かなり近い値が出ていますね!

コードの中身を1行ずつ解説


① 平均を計算する

x_mean = np.mean(x)  # xの平均
y_mean = np.mean(y)  # yの平均

最小二乗法の計算では「各データが平均からどれだけずれているか」が重要になります。まずは平均を求めておきます。

② 傾きを計算する

numerator   = np.sum((x - x_mean) * (y - y_mean))
denominator = np.sum((x - x_mean) ** 2)
slope = numerator / denominator

数式で表すとこうです。

a = Σ[(xi - x̄)(yi - ȳ)] / Σ[(xi - x̄)²]

分子は「xのずれ × yのずれ」の合計、分母は「xのずれの二乗」の合計です。これは統計で言う共分散 ÷ 分散に相当します。

③ 切片を計算する

intercept = y_mean - slope * x_mean

傾きが求まれば、直線がデータの平均点 (x̄, ȳ) を通ることを利用して切片が計算できます。とてもシンプルですね。

④ 予測する

y_pred = slope * x + intercept

求めた傾きと切片を使って y = ax + b にxを代入するだけです。

scikit-learnと比較してみよう

スクラッチ実装の結果が正しいか、scikit-learn と答え合わせしてみましょう。

from sklearn.linear_model import LinearRegression
import numpy as np

x = np.array([1, 2, 3, 4, 5], dtype=float).reshape(-1, 1)
y = np.array([2.1, 4.0, 5.9, 8.1, 9.8], dtype=float)

model = LinearRegression().fit(x, y)

print(f"傾き(slope)    : {model.coef_[0]:.4f}")
print(f"切片(intercept): {model.intercept_:.4f}")
傾き(slope)    : 1.9300
切片(intercept): 0.1900

ぴったり一致しました 🎉 スクラッチ実装が正しく動いていることが確認できましたね。

モデルの精度を評価してみよう(MSE・R²)

作ったモデルがどれくらい「良いか」を数値で確認するには、評価指標を使います。よく使われるのがこの2つです。

MSE(平均二乗誤差)

予測値と実際の値の差の二乗平均です。値が小さいほど予測精度が高い。

mse = np.mean((y - y_pred) ** 2)
print(f"MSE: {mse:.4f}")
MSE: 0.0150

R²スコア(決定係数)

0〜1の値を取り、1に近いほどモデルがデータをうまく説明できていることを示します。

ss_res = np.sum((y - y_pred) ** 2)          # 残差平方和
ss_tot = np.sum((y - y_mean) ** 2)          # 全平方和
r2 = 1 - (ss_res / ss_tot)
print(f"R²スコア: {r2:.4f}")
R²スコア: 0.9983

R²が0.9983ということは、このモデルはデータの変動の約99.8%を説明できているということ。かなり精度の高いフィットですね!

matplotlibでグラフを描いてみよう

数値だけじゃなく、視覚的に確認するのも大切です。

import matplotlib.pyplot as plt
import numpy as np

x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([2.1, 4.0, 5.9, 8.1, 9.8], dtype=float)

x_mean = np.mean(x)
y_mean = np.mean(y)
slope = np.sum((x - x_mean) * (y - y_mean)) / np.sum((x - x_mean) ** 2)
intercept = y_mean - slope * x_mean
y_pred = slope * x + intercept

plt.figure(figsize=(7, 5))
plt.scatter(x, y, color='steelblue', s=80, label='実データ', zorder=5)
plt.plot(x, y_pred, color='tomato', linewidth=2, label='回帰直線')
plt.xlabel('x(説明変数)')
plt.ylabel('y(目的変数)')
plt.title('NumPyスクラッチ線形回帰')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()

青い点が実データ、赤い線が求めた回帰直線です。ぴったりフィットしているのが視覚的に確認できます 📈

まとめ:スクラッチ実装で得られるもの

今回やったことを振り返ってみましょう。

  • ✅ 最小二乗法の仕組みを数式レベルで理解した
  • ✅ NumPyの基本演算だけで線形回帰を実装できた
  • ✅ scikit-learnの結果と一致することで実装の正しさを確認した
  • ✅ MSEとR²スコアでモデルの精度を評価した
  • ✅ matplotlibで結果を可視化した

「ライブラリを使えば動く」から「なぜ動くのかがわかる」へ。このステップを踏むだけで、機械学習への理解度がグッと上がります。

次のステップとして、重回帰(説明変数が複数)勾配降下法(Gradient Descent)による実装にも挑戦してみると、さらに深く理解できるようになりますよ 💪

📚 関連商品・おすすめ書籍

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

もしも

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

初心者に定番のPython入門書

Amazonで見る

実践Claude Code入門―現場で活用するためのAIコーディングの思考法

もしも

実践Claude Code入門―現場で活用するためのAIコーディングの思考法

AIコーディングの現場活用法を学ぶ一冊

Amazonで見る

Python Web開発実践入門 ―― FastAPIによるWebAPI開発と非同期処理

もしも

Python Web開発実践入門 ―― FastAPIによるWebAPI開発と非同期処理

FastAPIでWebAPI開発を実践的に学ぶ

Amazonで見る

※本記事にはアフィリエイトリンクが含まれます。

ABOUT ME
やまちゃん
これまで学生と社会人を合わせて5000人以上にプログラミング学習を指導。 ゼロからイチをわかりやすく解説する専門家として活動しており、本業ではArduinoを用いたIoT開発とロボットプログラミングが専門。 Pythonを用いたアプリ開発、ウェブアプリケーションの開発で業務の効率化をサポートしています。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です