有效防止softmax計算時上溢出(overflow)和下溢出(underflow)的方法
《Deep Learning》(Ian Goodfellow & Yoshua Bengio & Aaron Courville)第四章「數值計算」中,談到了上溢出(overflow)和下溢出(underflow)對數值計算的影響,並以softmax函數和log softmax函數為例進行了講解。這裏我再詳細地把它總結一下。
『1』什麽是下溢出(underflow)和上溢出(overflow)
實數在計算機內用二進制表示,所以不是一個精確值,當數值過小的時候,被四舍五入為0,這就是下溢出。此時如果對這個數再做某些運算(例如除以它)就會出問題。反之,當數值過大的時候,情況就變成了上溢出。
『2』softmax函數是什麽
softmax函數如下:
從公式上看含義不是特別清晰,所以借用知乎上的一幅圖來說明(感謝原作者):
這幅圖極其清晰地表明了softmax函數是什麽,一圖勝千言。
『3』計算softmax函數值的問題
通常情況下,計算softmax函數值不會出現什麽問題,例如,當softmax函數表達式裏的所有 xi 都是一個“一般大小”的數值 c 時——也就是上圖中, z1=z2=z3=c 時,那麽,計算出來的函數值y1=y2=y3=1/3 。
但是,當某些情況發生時,計算函數值就出問題了:
- c 極其大,導致分子計算 ec
- c 為負數,且 |c| 很大,此時分母是一個極小的正數,有可能四舍五入為0,導致下溢出
『4』如何解決
所以怎樣規避這些問題呢?我們可以用同一個方法一口氣解決倆:
令 M=max(xi),i=1,2,?,n ,即 M 為所有 xi 中最大的值,那麽我們只需要把計算 f(xi)的值,改為計算 f(xi−M) 的值,就可以解決上溢出、下溢出的問題了,並且,計算結果理論上仍然和 f(xi)保持一致。
舉個實例:還是以前面的圖為例,本來我們計算 f(z2) ,是用“常規”方法來算的:
現在我們改成:
其中, M=3
通過這樣的變換,對任何一個 xi,減去M之後,e 的指數的最大值為0,所以不會發生上溢出;同時,分母中也至少會包含一個值為1的項,所以分母也不會下溢出(四舍五入為0)。所以這個技巧沒什麽高級的技術含量。
『5』延伸問題
看似已經結案了,但仍然有一個問題:如果softmax函數中的分子發生下溢出,也就是前面所說的 c 為負數,且 |c| 很大,此時分母是一個極小的正數,有可能四舍五入為0的情況,此時,如果我們把softmax函數的計算結果再拿去計算 log,即 log softmax,其實就相當於計算 log(0) ,所以會得到 −∞ ,但這實際上是錯誤的,因為它是由舍入誤差造成的計算錯誤。所以,有沒有一個方法,可以把這個問題也解決掉呢?
答案還是采用和前面類似的策略來計算 log softmax 函數值:
大家看到,在最後的表達式中,會產生下溢出的因素已經被消除掉了——求和項中,至少有一項的值為1,這使得log後面的值不會下溢出,也就不會發生計算 log(0) 的悲劇。在很多數值計算的library中,都采用了此類方法來保持數值穩定。
有效防止softmax計算時上溢出(overflow)和下溢出(underflow)的方法