山の斜面に立ったとき、「最も急に登れる方向はどちらか?」という問いに答えるのが 勾配(gradient)です。スカラー場(たとえば標高や温度)に対して勾配を計算すると、各点で「値が最も急に増加する方向」と「その増加率」を持つベクトルが得られます。
勾配はベクトル解析の最も基本的な微分演算子であり、物理学ではポテンシャルから力を求める操作($\bm{F} = -\nabla U$)に、機械学習では損失関数の最小化(勾配降下法)に、流体力学では圧力勾配による流体の駆動に活用されます。
本記事の内容
- 勾配の直感的な理解
- 勾配の数学的定義(ナブラ演算子)
- 方向微分と勾配の関係
- 勾配の幾何学的性質(等高線に垂直)
- 勾配降下法との関連
- Pythonでの勾配ベクトル場の可視化
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
勾配とは
直感的な理解
2次元のスカラー場 $f(x, y)$ を「地形の標高」だと思ってください。山の上の各点で「最も急に登れる方向」を矢印で描くと、それが勾配ベクトル場です。
勾配ベクトルの特徴は次の通りです:
- 方向: $f$ の値が最も急に増加する方向を指す
- 大きさ: その方向の変化率(傾きの急さ)を表す
- 等高線との関係: 等高線に対して 垂直 になる
平坦な場所では勾配は零ベクトルになり、急斜面では大きなベクトルになります。
勾配の数学的定義
ナブラ演算子
まず、ナブラ演算子(nabla operator)$\nabla$ を導入します。
$$ \begin{equation} \nabla = \bm{e}_x \frac{\partial}{\partial x} + \bm{e}_y \frac{\partial}{\partial y} + \bm{e}_z \frac{\partial}{\partial z} = \begin{pmatrix} \dfrac{\partial}{\partial x} \\[8pt] \dfrac{\partial}{\partial y} \\[8pt] \dfrac{\partial}{\partial z} \end{pmatrix} \end{equation} $$
$\nabla$ は「微分演算をベクトルのように扱う」ための記号です。
勾配の定義
スカラー場 $f(x, y, z)$ の 勾配(gradient)は、ナブラ演算子を $f$ に作用させたベクトル場です。
$$ \begin{equation} \mathrm{grad}\, f = \nabla f = \frac{\partial f}{\partial x}\bm{e}_x + \frac{\partial f}{\partial y}\bm{e}_y + \frac{\partial f}{\partial z}\bm{e}_z = \begin{pmatrix} \dfrac{\partial f}{\partial x} \\[8pt] \dfrac{\partial f}{\partial y} \\[8pt] \dfrac{\partial f}{\partial z} \end{pmatrix} \end{equation} $$
2次元の場合は:
$$ \nabla f = \begin{pmatrix} \dfrac{\partial f}{\partial x} \\[8pt] \dfrac{\partial f}{\partial y} \end{pmatrix} $$
つまり、各偏微分を成分に持つベクトルです。
方向微分と勾配の関係
方向微分の定義
単位ベクトル $\bm{n}$($|\bm{n}| = 1$)の方向に沿った $f$ の変化率を 方向微分(directional derivative)と呼びます。
$$ \begin{equation} \frac{\partial f}{\partial \bm{n}} = \lim_{h \to 0} \frac{f(\bm{r} + h\bm{n}) – f(\bm{r})}{h} \end{equation} $$
勾配との関係
方向微分は、勾配と方向ベクトルの内積で表せます。
$$ \begin{equation} \frac{\partial f}{\partial \bm{n}} = \nabla f \cdot \bm{n} \end{equation} $$
導出
$f(\bm{r} + h\bm{n})$ を $h$ について1次までテイラー展開すると:
$$ \begin{align} f(\bm{r} + h\bm{n}) &= f(\bm{r}) + h \left(\frac{\partial f}{\partial x} n_x + \frac{\partial f}{\partial y} n_y + \frac{\partial f}{\partial z} n_z \right) + O(h^2) \\ &= f(\bm{r}) + h (\nabla f \cdot \bm{n}) + O(h^2) \end{align} $$
したがって:
$$ \begin{align} \frac{\partial f}{\partial \bm{n}} &= \lim_{h \to 0} \frac{f(\bm{r} + h\bm{n}) – f(\bm{r})}{h} \\ &= \lim_{h \to 0} \frac{h(\nabla f \cdot \bm{n}) + O(h^2)}{h} \\ &= \nabla f \cdot \bm{n} \end{align} $$
最急降下方向の証明
勾配が「最も急に増加する方向」であることを証明します。
コーシー・シュワルツの不等式より:
$$ \frac{\partial f}{\partial \bm{n}} = \nabla f \cdot \bm{n} \leq |\nabla f| \cdot |\bm{n}| = |\nabla f| $$
等号が成立するのは $\bm{n}$ が $\nabla f$ と同じ方向のとき、すなわち:
$$ \bm{n} = \frac{\nabla f}{|\nabla f|} $$
のときです。したがって:
- 方向微分が最大 になるのは $\nabla f$ の方向(最急上昇方向)
- 方向微分が最小 になるのは $-\nabla f$ の方向(最急降下方向)
- 最大変化率は $|\nabla f|$
勾配の幾何学的性質
等高線(等値面)に垂直
勾配ベクトルは等高線(2D)や等値面(3D)に対して 常に垂直 です。
証明: 等値面 $f(x, y, z) = c$ 上の曲線 $\bm{r}(t)$ を考えます。曲線上では $f(\bm{r}(t)) = c$(定数)なので、合成関数の微分法により:
$$ \begin{align} \frac{d}{dt} f(\bm{r}(t)) &= 0 \\ \nabla f \cdot \frac{d\bm{r}}{dt} &= 0 \end{align} $$
$\dfrac{d\bm{r}}{dt}$ は等値面上の接線ベクトルなので、$\nabla f$ は等値面に垂直です。$\square$
この性質は非常に重要で、等高線の「坂を最も急に下る方向」が勾配の逆方向であることを意味します。
具体例
例1: $f(x, y) = x^2 + y^2$
$$ \nabla f = \begin{pmatrix} 2x \\ 2y \end{pmatrix} $$
原点から離れるほど勾配が大きくなり、放射状に外を向きます。等高線(同心円)に対して垂直(動径方向)です。
例2: $f(x, y) = x^2 – y^2$(鞍点を持つ)
$$ \nabla f = \begin{pmatrix} 2x \\ -2y \end{pmatrix} $$
原点 $(0, 0)$ では $\nabla f = \bm{0}$ となります。これが 鞍点(saddle point)で、$x$ 方向には極小、$y$ 方向には極大です。
例3: ポテンシャルから力へ
保存力場 $\bm{F}$ はポテンシャルエネルギー $U$ の負の勾配で与えられます:
$$ \bm{F} = -\nabla U $$
例えば、重力ポテンシャル $U = mgh$ に対して重力は $\bm{F} = -mg\bm{e}_z$(下向き)です。点電荷のクーロンポテンシャル $U = \frac{kq}{r}$ に対しては:
$$ \bm{F} = -\nabla U = \frac{kq}{r^2} \hat{\bm{r}} $$
(中心方向の引力/斥力)となります。
勾配降下法
機械学習では、損失関数 $L(\bm{w})$ を最小化するためにパラメータ $\bm{w}$ を勾配の逆方向に更新する 勾配降下法(gradient descent)が基本的な最適化手法です。
$$ \begin{equation} \bm{w}_{t+1} = \bm{w}_t – \eta \nabla L(\bm{w}_t) \end{equation} $$
ここで $\eta > 0$ は学習率です。勾配が「最も急に増加する方向」なので、その逆方向に進めば関数値を最も効率よく減少させることができます。
Pythonでの実装
勾配ベクトル場の可視化
import numpy as np
import matplotlib.pyplot as plt
# スカラー場: f(x, y) = x^2 + y^2
x = np.linspace(-3, 3, 200)
y = np.linspace(-3, 3, 200)
X, Y = np.meshgrid(x, y)
F = X**2 + Y**2
# 勾配の計算
dFdx = 2 * X # ∂f/∂x = 2x
dFdy = 2 * Y # ∂f/∂y = 2y
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# --- 等高線 + 勾配ベクトル ---
ax = axes[0]
contour = ax.contourf(X, Y, F, levels=20, cmap='viridis', alpha=0.7)
ax.contour(X, Y, F, levels=10, colors='white', linewidths=0.5)
plt.colorbar(contour, ax=ax, label='$f(x, y)$')
# 勾配ベクトルを間引いて表示
skip = 15
ax.quiver(X[::skip, ::skip], Y[::skip, ::skip],
dFdx[::skip, ::skip], dFdy[::skip, ::skip],
color='red', scale=50, width=0.004, label='$\\nabla f$')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_title('$f(x,y) = x^2 + y^2$ with $\\nabla f$\n(Gradient perpendicular to contours)')
ax.set_aspect('equal')
ax.legend(fontsize=10)
# --- 鞍点を持つ例: f(x, y) = x^2 - y^2 ---
ax = axes[1]
F2 = X**2 - Y**2
dF2dx = 2 * X
dF2dy = -2 * Y
contour2 = ax.contourf(X, Y, F2, levels=20, cmap='RdBu_r', alpha=0.7)
ax.contour(X, Y, F2, levels=15, colors='black', linewidths=0.3)
plt.colorbar(contour2, ax=ax, label='$f(x, y)$')
skip = 15
ax.quiver(X[::skip, ::skip], Y[::skip, ::skip],
dF2dx[::skip, ::skip], dF2dy[::skip, ::skip],
color='black', scale=50, width=0.004, label='$\\nabla f$')
ax.plot(0, 0, 'ko', markersize=8, label='Saddle point')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_title('$f(x,y) = x^2 - y^2$ with $\\nabla f$\n(Saddle point at origin)')
ax.set_aspect('equal')
ax.legend(fontsize=9)
plt.tight_layout()
plt.savefig("gradient_field.png", dpi=150, bbox_inches='tight')
plt.show()
方向微分の検証
import numpy as np
def f(x, y):
"""スカラー場"""
return x**2 + y**2
def grad_f(x, y):
"""解析的な勾配"""
return np.array([2*x, 2*y])
# 点 (1, 2) での方向微分を検証
x0, y0 = 1.0, 2.0
n = np.array([1, 1]) / np.sqrt(2) # 方向ベクトル(単位化)
# 解析的な方向微分: ∇f · n
analytical = grad_f(x0, y0) @ n
print(f"解析的な方向微分: {analytical:.6f}")
# 数値的な方向微分: [f(r + hn) - f(r)] / h
h = 1e-6
numerical = (f(x0 + h*n[0], y0 + h*n[1]) - f(x0, y0)) / h
print(f"数値的な方向微分: {numerical:.6f}")
# 最急上昇方向の検証
grad = grad_f(x0, y0)
max_dir = grad / np.linalg.norm(grad)
max_rate = np.linalg.norm(grad)
print(f"\n勾配: {grad}")
print(f"最急上昇方向: {max_dir}")
print(f"最大変化率: {max_rate:.6f}")
勾配降下法の可視化
import numpy as np
import matplotlib.pyplot as plt
def f(x, y):
"""目的関数: f(x, y) = (x-1)^2 + 2(y-2)^2"""
return (x - 1)**2 + 2*(y - 2)**2
def grad_f(x, y):
"""勾配"""
return np.array([2*(x - 1), 4*(y - 2)])
# 勾配降下法
eta = 0.1 # 学習率
w = np.array([-2.0, -1.0]) # 初期値
trajectory = [w.copy()]
for _ in range(30):
g = grad_f(w[0], w[1])
w = w - eta * g
trajectory.append(w.copy())
trajectory = np.array(trajectory)
# 可視化
x = np.linspace(-3, 4, 200)
y = np.linspace(-2, 5, 200)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
contour = ax.contourf(X, Y, Z, levels=30, cmap='viridis', alpha=0.6)
ax.contour(X, Y, Z, levels=15, colors='white', linewidths=0.3)
plt.colorbar(contour, ax=ax, label='$f(x, y)$')
# 軌跡をプロット
ax.plot(trajectory[:, 0], trajectory[:, 1], 'ro-', markersize=4, linewidth=1.5,
label='Gradient Descent')
ax.plot(trajectory[0, 0], trajectory[0, 1], 'rs', markersize=10, label='Start')
ax.plot(1, 2, 'g*', markersize=15, label='Minimum (1, 2)')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_title('Gradient Descent on $f(x,y) = (x-1)^2 + 2(y-2)^2$')
ax.legend()
ax.set_aspect('equal')
plt.tight_layout()
plt.savefig("gradient_descent.png", dpi=150, bbox_inches='tight')
plt.show()
まとめ
本記事では、勾配(grad)の定義と幾何学的意味を解説しました。
- 勾配 $\nabla f$ は、スカラー場 $f$ の各偏微分を成分に持つ ベクトル場 である
- 方向微分は勾配との内積で計算できる: $\dfrac{\partial f}{\partial \bm{n}} = \nabla f \cdot \bm{n}$
- 勾配は $f$ が 最も急に増加する方向 を指し、その大きさが最大変化率である
- 勾配は 等高線に垂直 であり、保存力場はポテンシャルの負の勾配で表される
- 機械学習の 勾配降下法 は、勾配の逆方向に進んで損失を最小化する手法である
次のステップとして、以下の記事も参考にしてください。