Home
1993 words
10 minutes
自動微分における特異点近傍の数値安定化:逆余弦関数を含む合成関数の勾配計算とテイラー展開による回避策

1. 序論:解析的極限と計算機精度の乖離#

微分積分学において、不定形(0/00/00\infty \cdot 0)の極限値は、ロピタルの定理(L’Hôpital’s rule)等を用いることで解析的に確定する場合が多い。しかし、コンピュータ上の浮動小数点演算、特にディープラーニングフレームワークにおける自動微分(Automatic Differentiation)において、この解析的な連続性は必ずしも保証されない。

本稿では、逆余弦関数 arccos(u)\arccos(u) を含むエネルギー関数 EE を対象とし、u=1u=1 における勾配計算の挙動を検証する。解析的には有限の勾配値を持つにもかかわらず、計算機上では連鎖律(chain rule)の過程で「数値的破綻」が発生し、勾配が NaN (Not a Number) に陥る現象が確認された。

PyTorchのバージョン: 2.6.0

2. 理論的枠組み:特異点を持つ合成関数の微分#

検証対象として、以下の合成関数を定義する。

E(u)=12θ2,where θ=arccos(u)(1)E(u) = \frac{1}{2} \theta^2, \quad \text{where } \theta = \arccos(u) \tag{1}

2.1 解析的な勾配の導出#

連鎖律を用いると、勾配は以下のように記述される。

dEdu=dEdθdθdu=θ(11u2)(2)\frac{dE}{du} = \frac{dE}{d\theta} \cdot \frac{d\theta}{du} = \theta \cdot \left( -\frac{1}{\sqrt{1-u^2}} \right) \tag{2}

ここで u1u \to 1 の極限を考える。u=1u=1 のとき θ=arccos(1)=0\theta = \arccos(1) = 0 であるため、式(2)は 0()0 \cdot (-\infty) の不定形となる。

変数を θ\theta に置換し極限をとると、以下の通り有限値に収束する。

limu1dEdu=limθ0(θ1sinθ)=1(3)\lim_{u \to 1} \frac{dE}{du} = \lim_{\theta \to 0} \left( \theta \cdot \frac{-1}{\sin\theta} \right) = -1 \tag{3}

すなわち、数学的には u=1u=1 における勾配は -1.0 と厳密に定義される。

3. 数値実験:自動微分の「自滅」検証#

PyTorchを用いた実装により、この解析解が数値計算上でどのように破綻するかを検証した。検証結果は以下の3つのケースに分類される。

3.1 ケース1:完全な u=1.0u=1.0 におけるNaN発生#

入力 uu を厳密に 1.0 と設定した場合、Forward計算は正常に E=0E=0 を返すが、Backward計算(勾配計算)において NaN が発生した。

現象: 連鎖律の適用時に 0.0×(Inf)0.0 \times (-\text{Inf}) の演算が発生

結果: Gradient (u.grad): nan

これは理論通り、計算グラフ上での不定形の出現によるものである。

3.2 ケース2:u1.0u \approx 1.0 における数値不安定性(摂動の限界)#

次に、uu1.01.0 からわずかにずらした値(1.010171.0 - 10^{-17} 等)で検証を行った。一般に、特異点を回避するために微小な ϵ\epsilon を加算する手法(clippingやperturbation)が用いられるが、本検証ではこの手法も破綻することが確認された。

現象: 倍精度(float64)においても、1u21-u^2 の項が計算機イプシロンの限界付近で情報落ち(loss of significance)を起こし、ゼロ除算に近い状態、あるいはアンダーフローが発生したと考えられる

結果: Gradient (u.grad): nan

この結果は、特異点近傍においては「値をわずかにずらす」だけの対策では不十分であり、数値計算として完全に制御不能な領域が存在することを示唆している。

3.3 ケース3:テイラー展開による特異点の除去(解決策)#

u1u \approx 1 近傍において、関数定義そのものをテイラー展開による近似式に置き換えるアプローチを検証した。

E12(2(1u))2=1u(u1)(4)E \approx \frac{1}{2} \left( \sqrt{2(1-u)} \right)^2 = 1 - u \quad (u \to 1) \tag{4}

この近似式を用いることで、特異点を含む項(分母の 1u2\sqrt{1-u^2})が計算グラフから排除される。

結果: Gradient (u.grad): -1.0

解析解と一致する正常な勾配が得られた。これにより、特異点近傍では計算グラフを近似式に切り替える実装が必須であることが実証された。

4. 実装コードと出力ログ#

以下に、本検証に使用されたPythonコードとその実行結果を示す。

検証コード(test_diversence.py)#

import torch

def verify_autograd_failure():
    """
    自動微分における特異点の数値的破綻を検証する関数
    
    検証内容:
    - ケース1: u=1.0での厳密な特異点における勾配計算
    - ケース2: u≈1.0での数値的不安定性の検証
    - ケース3: テイラー展開による安定化手法
    """
    print("=== PyTorch 自動微分の自滅検証: d/du [0.5 * acos(u)^2] at u=1 ===")
    print("理論上の勾配: -theta / sin(theta) --> -1.0 になるはず\n")

    # ---------------------------------------------------------
    # ケース1: 完全に u = 1.0 (theta = 0) の場合
    # ---------------------------------------------------------
    u_exact = torch.tensor([1.0], requires_grad=True)
    
    # Forward計算 (エネルギー計算)
    # ここは問題なく通ります。 acos(1) = 0 なので Energy = 0
    theta_exact = torch.acos(u_exact)
    energy_exact = 0.5 * theta_exact**2
    
    print(f"[Case 1] u = {u_exact.item()} (theta = 0)")
    print(f"  Energy (Forward): {energy_exact.item()} (正常)")

    # Backward計算 (勾配計算)
    # ここで連鎖律が発動: theta * (-1/sin(theta))
    # 0.0 * (-Inf) = NaN になる瞬間
    try:
        energy_exact.backward()
        print(f"  Gradient (u.grad): {u_exact.grad.item()}  <-- 自滅 (NaN)")
    except Exception as e:
        print(f"  Error: {e}")

    print("-" * 50)

    # ---------------------------------------------------------
    # ケース2: u = 1.0 に極めて近いが 1.0 ではない場合 (数値不安定性)
    # ---------------------------------------------------------
    # 1.0 からわずかにズラす (double精度でも表現できるギリギリなど)
    u_near = torch.tensor([1.0 - 1e-17], dtype=torch.float64, requires_grad=True)
    
    theta_near = torch.acos(u_near)
    energy_near = 0.5 * theta_near**2
    
    # 注: 実際には theta は極めて小さいが非ゼロの値をとる
    print(f"[Case 2] u = {u_near.item()} (theta ≈ 1.49e-8)")
    
    # 勾配計算
    energy_near.backward()
    
    # 結果検証
    print(f"  Gradient (u.grad): {u_near.grad.item()}")
    
    print("-" * 50)

    # ---------------------------------------------------------
    # ケース3: テイラー展開アプローチ (uでなく theta^2 を直接近似)
    # ---------------------------------------------------------
    # テイラー展開: acos(u)^2 ≈ 2(1-u) + ...
    u_taylor = torch.tensor([1.0 - 1e-17], requires_grad=True)
    
    # 簡易的な1次近似: theta^2 ≈ 2(1-u)
    # Energy = 0.5 * 2(1-u) = 1 - u
    # この形式では特異点を含む項が計算グラフから完全に除去される
    energy_taylor = 0.5 * (2.0 * (1.0 - u_taylor))
    
    print(f"[Case 3] Taylor Approximation (Order 1) at u = 1.0")
    energy_taylor.backward()
    
    print(f"  Gradient (u.grad): {u_taylor.grad.item()}  <-- 正常 (-1.0)")

if __name__ == "__main__":
    verify_autograd_failure()

実行結果(ログ)#

=== PyTorch 自動微分の自滅検証: d/du [0.5 * acos(u)^2] at u=1 ===
理論上の勾配: -theta / sin(theta) --> -1.0 になるはず

[Case 1] u = 1.0 (theta = 0)
  Energy (Forward): 0.0 (正常)
  Gradient (u.grad): nan  <-- 自滅 (NaN)
--------------------------------------------------
[Case 2] u = 1.0 (theta ≈ 1.49e-8)
  Gradient (u.grad): nan
  -> 本来 -1.0 になるはずが、桁落ちとゼロ除算寸前で値が荒れる、
     あるいは運良く -1.0 になることもある不安定な領域
--------------------------------------------------
[Case 3] Taylor Approximation (Order 1) at u = 1.0
  Gradient (u.grad): -1.0  <-- 正常 (-1.0)

5. 結論#

本検証により、以下の事実が確認された。

まず第一に、自動微分の限界が明らかになった。torch.autograd 等の自動微分エンジンは、連鎖律を局所的な数値演算の積として実行するため、解析的な極限操作(ロピタルの定理)を自動的に適用することはできない。これは自動微分システムの設計上の制約であり、回避不可能な特性である。

第二に、摂動法の脆弱性を示した。特異点近傍において単に入力値を微小量ずらす(Case 2)だけでは、桁落ちやアンダーフローの影響を排除できず、依然として NaN が発生するリスクがある。この手法は根本的な解決策ではないことを示した。

第三に、近似式による安定化の必要性が確認された。数値的安定性を確保するためには、特異点近傍(u1<ϵ|u - 1| < \epsilon)において条件分岐を行い、解析的に導出された有限級数(テイラー展開)へ計算経路を切り替える実装(Case 3)が不可欠である。この手法により、計算グラフから特異点を完全に除去し、理論値と一致する正確な勾配を得ることができる。

自動微分における特異点近傍の数値安定化:逆余弦関数を含む合成関数の勾配計算とテイラー展開による回避策
https://ss0832.github.io/posts/20260205_numerical_stability_autograd/
Author
ss0832
Published at
2026-02-05
License
CC BY-NC-SA 4.0