ノルム(norm)は、ベクトルや行列の「大きさ」を測る関数です。機械学習では正則化(L1/L2正則化)、距離計算、行列の条件数の評価など、あらゆる場面で登場する基本概念です。
本記事では、L1ノルム、L2ノルム、Lpノルム、フロベニウスノルムなどの定義と性質を丁寧に解説し、Pythonでの計算方法を紹介します。
本記事の内容
- ベクトルのノルム(L1, L2, Lp, L-infinity)
- 行列のノルム(フロベニウスノルム、スペクトルノルム)
- ノルムの公理(3つの性質)
- Pythonでの計算と可視化
ノルムの公理
ノルムは以下の3つの性質を満たす関数 $\|\cdot\|$ です。
- 非負性: $\|\bm{x}\| \geq 0$, $\|\bm{x}\| = 0 \iff \bm{x} = \bm{0}$
- 斉次性: $\|\alpha \bm{x}\| = |\alpha| \cdot \|\bm{x}\|$
- 三角不等式: $\|\bm{x} + \bm{y}\| \leq \|\bm{x}\| + \|\bm{y}\|$
ベクトルのノルム
L1ノルム(マンハッタン距離)
各要素の絶対値の和です。
$$ \|\bm{x}\|_1 = \sum_{i=1}^{n} |x_i| $$
L1ノルムは機械学習のLasso回帰(L1正則化)で利用され、パラメータのスパース性を促進します。
L2ノルム(ユークリッドノルム)
各要素の二乗和の平方根です。
$$ \|\bm{x}\|_2 = \sqrt{\sum_{i=1}^{n} x_i^2} = \sqrt{\bm{x}^T\bm{x}} $$
最も一般的なノルムで、ユークリッド空間での距離に対応します。Ridge回帰(L2正則化)で利用されます。
Lpノルム
L1とL2を一般化したものがLpノルムです($p \geq 1$)。
$$ \|\bm{x}\|_p = \left(\sum_{i=1}^{n} |x_i|^p\right)^{1/p} $$
L-infinityノルム(最大値ノルム)
$p \to \infty$ の極限で得られるノルムです。
$$ \|\bm{x}\|_\infty = \max_{i} |x_i| $$
行列のノルム
フロベニウスノルム
行列の全要素の二乗和の平方根です。ベクトルのL2ノルムを行列に拡張したものと考えられます。
$$ \|\bm{A}\|_F = \sqrt{\sum_{i=1}^{m}\sum_{j=1}^{n} |a_{ij}|^2} = \sqrt{\text{tr}(\bm{A}^T\bm{A})} $$
特異値分解 $\bm{A} = \bm{U}\bm{\Sigma}\bm{V}^T$ を用いると、
$$ \|\bm{A}\|_F = \sqrt{\sum_{i} \sigma_i^2} $$
と特異値の二乗和の平方根で表されます。
スペクトルノルム(作用素ノルム)
行列の最大特異値として定義されます。
$$ \|\bm{A}\|_2 = \sigma_{\max}(\bm{A}) = \max_{\|\bm{x}\|_2 = 1} \|\bm{A}\bm{x}\|_2 $$
スペクトルノルムは、行列がベクトルを最大どれだけ「引き伸ばす」かを表しています。
核ノルム(トレースノルム)
全特異値の和です。
$$ \|\bm{A}\|_* = \sum_{i} \sigma_i $$
低ランク行列推定の正則化に利用されます。
Pythonでの計算
import numpy as np
import matplotlib.pyplot as plt
# --- ベクトルのノルム ---
x = np.array([3, -4, 5, -2, 1])
l1_norm = np.linalg.norm(x, ord=1)
l2_norm = np.linalg.norm(x, ord=2)
linf_norm = np.linalg.norm(x, ord=np.inf)
print("=== ベクトルのノルム ===")
print(f"x = {x}")
print(f"L1ノルム: {l1_norm:.4f} (手計算: {np.sum(np.abs(x))})")
print(f"L2ノルム: {l2_norm:.4f} (手計算: {np.sqrt(np.sum(x**2)):.4f})")
print(f"L∞ノルム: {linf_norm:.4f} (手計算: {np.max(np.abs(x))})")
# --- 行列のノルム ---
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=float)
fro_norm = np.linalg.norm(A, 'fro')
spec_norm = np.linalg.norm(A, 2)
nuc_norm = np.linalg.norm(A, 'nuc')
# 特異値との関係
U, S, Vt = np.linalg.svd(A)
print(f"\n=== 行列のノルム ===")
print(f"A =\n{A}")
print(f"特異値: {S.round(4)}")
print(f"フロベニウスノルム: {fro_norm:.4f} (sqrt(sum(sigma^2)): {np.sqrt(np.sum(S**2)):.4f})")
print(f"スペクトルノルム: {spec_norm:.4f} (max(sigma): {S[0]:.4f})")
print(f"核ノルム: {nuc_norm:.4f} (sum(sigma): {np.sum(S):.4f})")
# --- Lpノルムの単位球の可視化 ---
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
p_values = [0.5, 1, 2, 10]
theta = np.linspace(0, 2 * np.pi, 1000)
for ax, p in zip(axes, p_values):
if p < 1:
# p < 1 はノルムではないが参考として表示
r = (np.abs(np.cos(theta))**p + np.abs(np.sin(theta))**p)**(-1/p)
else:
r = (np.abs(np.cos(theta))**p + np.abs(np.sin(theta))**p)**(-1/p)
x_plot = r * np.cos(theta)
y_plot = r * np.sin(theta)
ax.plot(x_plot, y_plot, 'b-', linewidth=2)
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
ax.set_aspect('equal')
ax.set_title(f'$L_{{{p}}}$ Unit Ball')
ax.axhline(y=0, color='gray', linewidth=0.5)
ax.axvline(x=0, color='gray', linewidth=0.5)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# --- L1, L2正則化とノルムの関係 ---
fig, ax = plt.subplots(figsize=(8, 5))
dims = range(1, 101)
x_random = np.random.randn(100)
l1_cumsum = np.cumsum(np.abs(np.sort(np.abs(x_random))[::-1]))
l2_cumsum = np.sqrt(np.cumsum(np.sort(x_random**2)[::-1]))
ax.plot(dims, l1_cumsum / l1_cumsum[-1], 'r-', label='L1 (cumulative)')
ax.plot(dims, l2_cumsum / l2_cumsum[-1], 'b-', label='L2 (cumulative)')
ax.set_xlabel('Number of Components')
ax.set_ylabel('Cumulative Ratio')
ax.set_title('L1 vs L2 Norm: Cumulative Contribution')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Lpノルムの単位球の形状を見ると、$p=1$ では菱形、$p=2$ では円、$p \to \infty$ では正方形になることがわかります。この形状の違いが、L1正則化のスパース性とL2正則化の滑らかさの違いに対応しています。
まとめ
本記事では、ベクトルと行列のノルムについて解説しました。
- ノルムはベクトルや行列の「大きさ」を測る関数で、非負性・斉次性・三角不等式を満たす
- L1ノルムは要素の絶対値の和、L2ノルムは二乗和の平方根
- フロベニウスノルムは行列の全要素の二乗和の平方根で、特異値の二乗和の平方根と等しい
- スペクトルノルムは最大特異値に対応し、行列の「最大引き伸ばし率」を表す
次のステップとして、以下の記事も参考にしてください。