数値微分の実装と精度の限界|Python
数値微分は、関数の解析的な導関数がわからないとき、あるいは計算が複雑なときに近似値を求める手法だ。アイデアはシンプルだが、刻み幅の選び方によって精度が大きく変わる。浮動小数点数の限界も理解しておく必要がある。
前進差分
微分の定義に基づく最も単純な近似が前進差分だ。
def forward_diff(f, x, h=1e-5):
"""前進差分による数値微分"""
return (f(x + h) - f(x)) / h
# f(x) = x^2 の微分(解析解は 2x)
f = lambda x: x**2
print(forward_diff(f, 3)) # 6.000009999951316(真の値は 6)誤差は約 のオーダーで、 と同程度になる。これは前進差分が一次精度であることを示している。
中心差分
中心差分は前進差分より高精度だ。
def central_diff(f, x, h=1e-5):
"""中心差分による数値微分"""
return (f(x + h) - f(x - h)) / (2 * h)
f = lambda x: x**2
print(central_diff(f, 3)) # 5.999999999999339(より正確)誤差は のオーダーになり、二次精度を持つ。同じ でも、前進差分より格段に正確な結果が得られる。
一次精度(誤差 )。計算が単純だが精度は低い。
二次精度(誤差 )。計算量はほぼ同じで精度が高い。通常はこちらを使う。
二階微分
二階微分は中心差分を拡張して計算できる。
def second_diff(f, x, h=1e-4):
"""二階微分の近似"""
return (f(x + h) - 2*f(x) + f(x - h)) / (h**2)
# f(x) = x^3 の二階微分(解析解は 6x)
f = lambda x: x**3
print(second_diff(f, 2)) # 12.000000000287557(真の値は 12)二階微分では で割るため、丸め誤差の影響を受けやすい。 を大きめ( 程度)に取る必要がある。
刻み幅 h の選び方
を小さくすれば打ち切り誤差は減るが、丸め誤差が増える。この二つのバランスで最適な が決まる。
import math
f = lambda x: math.sin(x)
x = 1.0
true_value = math.cos(x) # sin の微分は cos
for exp in range(-1, -17, -1):
h = 10 ** exp
approx = central_diff(f, x, h)
error = abs(approx - true_value)
print(f'h = 10^{exp:3d}: error = {error:.2e}')実行すると、 あたりで誤差が最小になり、それより小さい では丸め誤差が支配的になって精度が悪化する。
なぜ丸め誤差が問題になるか
が非常に小さいと、 と の差がごくわずかになる。浮動小数点数では有効桁数が限られているため、この差の計算で精度が失われる(桁落ち)。
import math
f = lambda x: math.sin(x)
x = 1.0
# h が極端に小さい場合
h = 1e-15
print(f(x + h)) # 0.8414709848078965
print(f(x - h)) # 0.8414709848078965
print(f(x + h) - f(x - h)) # 0.0(差がゼロになってしまう)差がゼロになると微分値もゼロになり、完全に間違った結果になる。
最適な刻み幅の理論値
中心差分の場合、打ち切り誤差と丸め誤差のバランスから、最適な は次のようになる。
ここで は機械イプシロン(約 )だ。
import sys
eps = sys.float_info.epsilon
h_opt = eps ** (1/3)
print(f'機械イプシロン: {eps:.2e}') # 2.22e-16
print(f'最適な h: {h_opt:.2e}') # 6.06e-06理論的には から あたりが最適ということになる。
高階の差分公式
より高精度な差分公式も存在する。5 点公式は四次精度を持つ。
def five_point_diff(f, x, h=1e-3):
"""5 点公式による数値微分(四次精度)"""
return (-f(x + 2*h) + 8*f(x + h) - 8*f(x - h) + f(x - 2*h)) / (12 * h)
import math
f = lambda x: math.sin(x)
x = 1.0
print(f'中心差分: {central_diff(f, x, 1e-3):.15f}')
print(f'5点公式: {five_point_diff(f, x, 1e-3):.15f}')
print(f'真の値: {math.cos(x):.15f}')同じ でも 5 点公式の方が高精度だ。ただし関数の評価回数が増えるため、計算コストとのトレードオフになる。
自動微分との比較
数値微分には本質的に精度の限界がある。高精度が必要な場合や、勾配を大量に計算する機械学習では、自動微分(Automatic Differentiation)が使われる。PyTorch や JAX などのフレームワークが自動微分を提供している。
実装が簡単。精度に限界があり、高次元では計算コストが高い。
機械精度で正確な勾配が得られる。フレームワークの導入が必要。
数値微分は「とりあえず微分値が欲しい」場面で便利だが、限界を理解した上で使うことが重要だ。











