「機械学習って結局、ライブラリに任せてるだけで中身が全然わかってない…」
そんなモヤモヤを感じたことはありませんか? 今回はNumPyだけで線形回帰をゼロから実装するというアプローチを通じて、機械学習の本質をわかりやすく解説していきます 🚀
scikit-learnで1行で書けるけど…それって理解してる?

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)による実装にも挑戦してみると、さらに深く理解できるようになりますよ 💪





