四元數(shù)奇點處 NaN/Inf 梯度:`quaternion_exp_to_log` 與 `euler_from_quaternion` 的邊界防護實現(xiàn)解析)
計算機視覺人工智能深度學(xué)習(xí)圖像處理【免費下載鏈接】kornia Geometric Computer Vision Library for Spatial AI項目地址https://gitcode.com/gh_mirrors/ko/kornia點擊查看免費下載導(dǎo)讀位姿優(yōu)化與剛體運動學(xué)中四元數(shù)對數(shù)和歐拉角提取是高頻操作但在旋轉(zhuǎn)奇點單位四元數(shù)、萬向鎖處其內(nèi)部的反三角函數(shù)acos/asin導(dǎo)數(shù)無界導(dǎo)致反向傳播出現(xiàn)0 * inf nan或inf梯度直接摧毀整個優(yōu)化過程。本文基于 Kornia 倉庫的遷移記錄changelog.d/migration-089.fixed.md對應(yīng) issue #4007、PR #4228深入解析這一缺陷的數(shù)學(xué)根源、torch.wheredetach的防護實現(xiàn)模式、字節(jié)級一致的前向結(jié)果驗證以及測試套件中如何跨 PyTorch 版本鎖定該行為。讀完本文你將理解旋轉(zhuǎn)參數(shù)化在反向傳播中的數(shù)值陷阱并能復(fù)現(xiàn)、驗證這一修復(fù)。一、問題背景旋轉(zhuǎn)奇點處的梯度災(zāi)難Kornia 的幾何模塊kornia/geometry/conversions.py提供了一批旋轉(zhuǎn)表示之間的互轉(zhuǎn)函數(shù)包括四元數(shù)對數(shù)/指數(shù)映射與歐拉角轉(zhuǎn)換quaternion_exp_to_log源碼將(w, x, y, z)順序、形狀為(*, 4)的四元數(shù)映射到形狀為(*, 3)的對數(shù)向量是四元數(shù)指數(shù)映射quaternion_log_to_exp的逆操作euler_from_quaternion源碼將四個分離張量w, x, y, z轉(zhuǎn)換為(roll, pitch, yaw)三元組弧度制。這兩個函數(shù)的數(shù)值核心都依賴反三角函數(shù)quaternion_exp_to_log計算acos(w)euler_from_quaternion計算asin(sinp)其中sinp 2 * (w * y - z * x)經(jīng)clamp(-1, 1)后作為asin的參數(shù)。問題恰恰出在這些反三角函數(shù)的導(dǎo)數(shù)上d(acos)/dw -1 / sqrt(1 - w2) 在 w ±1 處無界 d(asin)/dx 1 / sqrt(1 - x2) 在 x ±1 處無界1.1 單位四元數(shù)0 * inf nanquaternion_exp_to_log在w 1處恰好命中acos的定義域邊界——而w 1正是單位四元數(shù)(1, 0, 0, 0)這是位姿優(yōu)化最標準的初始化起點。此時acos的導(dǎo)數(shù)在w 1處為-inf單位四元數(shù)的向量部分(0, 0, 0)恰好為零兩者相乘得到0 * inf nan。從源碼注釋conversions.py可以看到torch在所有受支持的版本上于邊界處返回-inf因此這一缺陷并非某個 torch 版本特有。測試用例test_convention_gradient_is_finite_at_the_acos_boundary_4007tests/geometry/test_conversions.py明確記錄每個反向傳播只要經(jīng)過單位四元數(shù)這一最常見輸入梯度就會變成nan。1.2 萬向鎖pitch梯度爆掉euler_from_quaternion的pitch由asin(sinp)求得當pitch ±π/2萬向鎖時sinp ±1同樣命中asin的定義域邊界asin的導(dǎo)數(shù)在該處為inf。源碼注釋conversions.py指出此前只要下游有任何東西對pitch求導(dǎo)所有四個四元數(shù)系數(shù)的梯度都會變成nan或inf——與被返回的三元組本身是否代表輸入旋轉(zhuǎn)無關(guān)。1.3 一個被版本掩蓋的假修復(fù)修復(fù)前的代碼用clamp限制的是值而非梯度。torch.clamp的反向傳播行為隨版本變化torch 2.14clamp的反向在閉邊界處直接透傳梯度-inf/inf原樣到達乘法處產(chǎn)生nantorch 2.14clamp的反向在閉邊界處返回0-inf在到達乘法之前被清零缺陷被掩蓋而非消除。測試注釋tests/geometry/test_conversions.py明確指出這是clamp行為的變化而非acos的變化因此修復(fù)絕不能依賴任何特定版本的clamp反向語義——這正是防護方案要獨立于版本工作的原因。二、修復(fù)方案值取真實參數(shù)梯度走安全參數(shù)修復(fù)采用分離值路徑與梯度路徑的經(jīng)典技巧前向值從真實可能位于邊界上的參數(shù)的detach副本計算梯度則在一個替換過的安全參數(shù)上微分使 autograd 根本不會在邊界點對acos/asin求導(dǎo)。2.1quaternion_exp_to_log的防護conversions.pyw_clamped torch.clamp(quaternion_scalar, min-1.0, max1.0) at_boundary w_clamped.abs() 1.0 safe_w torch.where(at_boundary, torch.zeros_like(w_clamped), w_clamped) acos_w torch.where(at_boundary, w_clamped.detach().acos(), safe_w.acos()) quaternion_log: torch.Tensor (quaternion_vector * acos_w / norm_q).to(orig_dtype)逐行拆解w_clamped把標量部分限制到[-1, 1]保證acos合法輸入at_boundary標記|w| 1的邊界元素safe_w把邊界元素的參數(shù)替換為0acos(0) π/2導(dǎo)數(shù)有界非邊界元素保持原值acos_w是一個torch.where混合張量邊界元素取w_clamped.detach().acos()——真實值但已detach不參與求導(dǎo)非邊界元素取safe_w.acos()——參與求導(dǎo)但參數(shù)遠離邊界導(dǎo)數(shù)有界。由于acos在±1處連續(xù)只是導(dǎo)數(shù)發(fā)散detach副本上算出的值與未防護調(diào)用逐位一致前向結(jié)果完全不變。2.2euler_from_quaternion的防護conversions.pyat_boundary sinp.abs() 1.0 safe_sinp torch.where(at_boundary, torch.zeros_like(sinp), sinp) pitch torch.where(at_boundary, sinp.detach().asin(), safe_sinp.asin())與 2.1 完全同構(gòu)邊界元素用sinp.detach().asin()取真實值asin(±1) ±π/2梯度走safe_sinp.asin()參數(shù)替換為0asin在0處導(dǎo)數(shù)恰為1有界。測試注釋tests/geometry/test_conversions.py說明該防護是鏡像了quaternion_exp_to_log的acos邊界修復(fù)。2.3 為什么detach副本的前向值安全關(guān)鍵數(shù)學(xué)事實acos/asin在邊界處值收斂acos(±1) 0/πasin(±1) ±π/2只有導(dǎo)數(shù)發(fā)散。因此從真實參數(shù)可能恰好是 ±1取detach值 → 前向結(jié)果與未防護實現(xiàn)完全相同從替換參數(shù)0求梯度 → 梯度路徑上不存在無界導(dǎo)數(shù)torch.where的反向只會對被選中分支的輸入回傳梯度邊界元素的safe_w/safe_sinp分支不會收到梯度。三、驗證字節(jié)級一致與跨版本穩(wěn)定修復(fù)的正確性由測試套件tests/geometry/test_conversions.py從兩個維度鎖定3.1 前向結(jié)果字節(jié)級不變test_convention_gradient_is_finite_at_the_acos_boundary_4007tests/geometry/test_conversions.py驗證單位四元數(shù)的對數(shù)為全零[0, 0, 0]前向值不變對接近邊界的輸入w 1 - eps防護實現(xiàn)與未防護表達式直接clamp(...).acos()輸出完全一致——這證明了torch.where的非邊界分支在遠離w ±1處完全惰性防護不會移動任何前向位變更記錄還提到在 500 個隨機輸入含多個強制落在精確邊界上的樣本上前向結(jié)果與舊實現(xiàn)字節(jié)級相同。3.2 梯度在所有邊界場景有限同一測試覆蓋三類邊界輸入tests/geometry/test_conversions.pyidentity torch.tensor((1.0, 0.0, 0.0, 0.0), ...) # w 1單位四元數(shù) antipode torch.tensor((-1.0, 0.0, 0.0, 0.0), ...) # w -1對跖點 non_unit torch.tensor((1.0, 0.1, 0.0, 0.0), ...) # 非單位、向量部分非零的邊界輸入三者梯度均斷言torch.isfinite(...).all()。其中非單位輸入尤其關(guān)鍵它說明即使0 * inf的抵消不再成立向量部分不為零梯度也必須保持有限——防護不依賴任何恰好相乘消掉的巧合。對euler_from_quaterniontest_convention_pitch_gradient_is_finite_at_gimbal_lock_4007tests/geometry/test_conversions.py構(gòu)造w1, x0, y0.5, z0使sinp 2*(w*y - z*x) 1.0精確命中邊界斷言pitch等于π/2且對w/x/y/z四個系數(shù)的梯度全部有限。3.3 為什么 CI 矩陣里 torch 2.5.1 能區(qū)分修復(fù)測試注釋tests/geometry/test_conversions.py指出由于clamp的反向在 torch 2.14 上已把-inf清零該缺陷只在 torch 2.5.1 等舊版本上暴露。因此這兩個測試在 base 分支上2.5.1 失敗、2.14 通過正是用來跨版本鑒別缺陷是否存在、防護是否真正生效的判別用例。四、已知邊界對跖點的有限大梯度不在本次范圍變更記錄明確劃定了本次修復(fù)的范圍邊界quaternion_exp_to_log的對跖點(-1, 0, 0, 0)仍會返回一個大但有限的梯度約π/eps其來源是既有實現(xiàn)中用clamp(mineps)夾取零范數(shù)后的除法與acos導(dǎo)數(shù)無界無關(guān)屬預(yù)先存在且本次不處理的行為。從源碼conversions.py可確認norm_q torch.norm(quaternion_vector, ...).clamp(mineps)對跖點的向量部分為零范數(shù)被夾到eps 1e-8acos(-1) π除以eps即產(chǎn)生約π × 10?的梯度。這是有限值不會破壞優(yōu)化但數(shù)值量級很大使用時應(yīng)知曉。五、使用注意事項與相關(guān)約定結(jié)合源碼文檔字符串與測試實際使用這兩個函數(shù)時需留意以下約定5.1 輸入不歸一化的警告兩者都不對輸入四元數(shù)做歸一化conversions.pyquaternion_exp_to_log([0.5, 0.5, 0, 0])返回[1.0471975511965976, 0, 0]比歸一化后同一旋轉(zhuǎn)的[0.7853981633974484, 0, 0]大 33%[2, 0, 0, 0]因w被clamp到 1 而返回原點euler_from_quaternion傳入2q會使sinp飽和到 ±1被誤報為萬向鎖。測試以kornia#3953為消息對這兩個行為做了鎖定tests/geometry/test_conversions.py。5.2 float16/bfloat16 的自動升檔quaternion_exp_to_log會將float16/bfloat16輸入升檔為float32計算再轉(zhuǎn)回conversions.py保證默認eps 1e-8可表示——否則在float16下默認eps下溢為 0單位四元數(shù)會返回[nan, nan, nan]issue #3966測試見 tests/geometry/test_conversions.py。5.3 歐拉角萬向鎖的值缺陷仍在本次修復(fù)只處理梯度。文檔警告conversions.py明確在pitch ±π/2處返回的三元組通常不代表輸入旋轉(zhuǎn)且沒有萬向鎖分支來聲明這一點——roll/yaw由兩個在此處相消為零的量做atan2得到結(jié)果由舍入決定隨 dtype、torch 版本和輸入的一個 ulp 變化而漂移issue #3950。值問題與梯度問題需分開對待。5.4eps參數(shù)建議quaternion_exp_to_log的eps默認1e-8。當輸入為float64且希望精確落到邊界時可像測試那樣傳torch.finfo(dtype).epstests/geometry/test_conversions.py——注意 docstring 示例中單位四元數(shù)使用epstorch.finfo(...).eps得到[0., 0., 0.]。六、總結(jié)一類可復(fù)用的梯度奇點防護模式本次變更changelog 條目見 changelog.d/migration-089.fixed.md解決的是一類普遍存在的數(shù)值問題任何在定義域邊界求導(dǎo)的函數(shù)acos、asin、sqrt、log等都可能讓梯度變?yōu)閚an/inf而修復(fù)模式高度可復(fù)用用torch.where標記邊界元素邊界元素的前向值取自真實參數(shù)的detach副本值連續(xù)無損邊界元素的梯度走替換后的安全參數(shù)導(dǎo)數(shù)有界無 nan配套測試同時斷言前向值逐位不變、梯度在多個邊界場景下有限并覆蓋多個 torch 版本以抵御版本掩蓋缺陷。Kornia 正是以這種方式讓位姿優(yōu)化最常用的初始化點——單位四元數(shù)——在反向傳播中不再產(chǎn)生nan同時保證euler_from_quaternion在萬向鎖處的pitch梯度有限且前向數(shù)值結(jié)果與修復(fù)前逐字節(jié)一致實現(xiàn)了梯度修復(fù)值不動的穩(wěn)健變更。贊分享計算機視覺人工智能深度學(xué)習(xí)圖像處理【免費下載鏈接】kornia Geometric Computer Vision Library for Spatial AI項目地址https://gitcode.com/gh_mirrors/ko/kornia點擊查看免費下載相關(guān)推薦Kornia 四元數(shù)梯度修復(fù)深度解析quaternion_to_axis_angle 在恒等旋轉(zhuǎn)處的正確梯度4237Kornia 四元數(shù)梯度修復(fù)深度解析 quaternion_to_axis_angle 在恒等旋轉(zhuǎn)處的正確梯度 4237 導(dǎo)讀 本文以 changelo計算機視覺深度學(xué)習(xí)人工智能圖像處理Kornia 3D 邊界框非有限坐標校驗修復(fù)解析validate_bbox3d 的 NaN/Inf 處理與錯誤信息改進Kornia 3D 邊界框非有限坐標校驗修復(fù)解析validate_bbox3d 的 NaN/Inf 處理與錯誤信息改進 導(dǎo)讀 本文圍繞 Kornia 幾何模塊計算機視覺人工智能深度學(xué)習(xí)圖像處理Kornia solve_cubic 三實根分支的 acos 邊界梯度修復(fù)從 nan 梯度到安全的替參求導(dǎo)約定Kornia solve_cubic 三實根分支的 acos 邊界梯度修復(fù)從 nan 梯度到安全的替參求導(dǎo)約定 本文講解 Kornia 多項式求解器在 sol計算機視覺深度學(xué)習(xí)人工智能圖像處理創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考