オーバーフローエラーなしで大きな指数項を確実に追加する方法は?


24

マルコフ連鎖モンテカルロ法の非常に一般的な問題には、大きな指数項の合計である確率の計算、

ea1+ea2+...

のコンポーネントは非常に小さいものから非常に大きいものまであります。私のアプローチは、最大指数項た。aK:=maxi(ai)

a=K+log(ea1K+ea2K+...)
eaea1+ea2+...

このアプローチは、aのすべての要素aが大きい場合は妥当ですが、そうでない場合はそれほど良い考えではありません。もちろん、小さな要素は浮動小数点の合計に寄与しませんが、それらを確実に処理する方法はわかりません。Rコードでは、私のアプローチは次のようになります。

if ( max(abs(a)) > max(a) )
  K <-  min(a)
else
  K <- max(a)
ans <- log(sum(exp(a-K))) + K

それは標準的な解決策があるべきだと十分に一般的な問題のようですが、それが何であるかはわかりません。提案をありがとう。


1
これは事です。「logsumexp」のGoogle。

回答:


15

データを2回パスするだけの簡単なソリューションがあります。

最初に計算し

K:=maxiai,

これは、用語がある場合、 n

ieaineK.

おそらくほどの大きさのはないはずなので、の計算でのオーバーフローの心配はありません 倍精度。n1020

τ:=ieaiKn

したがって、を計算すると、解はます。τeKτ


明確に表記してくれてありがとう- (?)が、私はこれは私が提案したものをいくつかのときに私はアンダーフローエラーを回避する必要がある場合は、本質的であると考えてい小さいですが、私はによって提案されたカハン総和アプローチ必要集まる@garethをai
cboettig

ああ、私は今あなたが何を得ているかを見る。アンダーフローを心配する必要はありません。非常に小さな結果をソリューションに追加しても変更されないはずです。それらの数が非常に多い場合は、最初に小さい値を合計する必要があります。
ジャックポールソン

ダウンボッターに:私の答えの何が悪いのか教えてくれませんか?
ジャックポールソン

非常に小さな用語がたくさんある場合はどうなりますか?これらに対してが発生する可能性があります。このような用語が多数ある場合、大きなエラーが発生します。eaiK0
-becko


10

doubleを加算するときに精度を維持するには、Kahan Summationを使用する必要があります。これは、キャリーレジスタを持つのと同等のソフトウェアです。

これはほとんどの値で問題ありませんが、オーバーフローが発生している場合、IEEE 754倍精度の制限(程度)にていることになります。この時点で、新しい表現が必要です。加算時にオーバーフローを検出できます。また、評価するための指数を検出することもできます。この時点で、指数をシフトし、このシフトを追跡することにより、doubleの解釈を変更できます。e709.783doubleMax - sumSoFar < valueToAddexponent > 709.783

ほとんどの場合、これは指数オフセットのアプローチに似ていますが、このバージョンは基数2に保持され、最大の指数を見つけるための初期検索を必要としません。したがって、。value×2shift

#!/usr/bin/env python
from math import exp, log, ceil

doubleMAX = (1.0 + (1.0 - (2 ** -52))) * (2 ** (2 ** 10 - 1))

def KahanSumExp(expvalues):
  expvalues.sort() # gives precision improvement in certain cases 
  shift = 0 
  esum = 0.0 
  carry = 0.0 
  for exponent in expvalues:
    if exponent - shift * log(2) > 709.783:
      n = ceil((exponent - shift * log(2) - 709.783)/log(2))
      shift += n
      carry /= 2*n
      esum /= 2*n
    elif exponent - shift * log(2) < -708.396:
      n = floor((exponent - shift * log(2) - -708.396)/log(2))
      shift += n
      carry *= 2*n
      esum *= 2*n
    exponent -= shift * log(2)
    value = exp(exponent) - carry 
    if doubleMAX - esum < value:
      shift += 1
      esum /= 2
      value /= 2
    tmp = esum + value 
    carry = (tmp - esum) - value 
    esum = tmp
  return esum, shift

values = [10, 37, 34, 0.1, 0.0004, 34, 37.1, 37.2, 36.9, 709, 710, 711]
value, shift = KahanSumExp(values)
print "{0} x 2^{1}".format(value, shift)

カハン加算は、「補償加算」方式のファミリーの1つにすぎません。何らかの理由でKahanが適切に機能しない場合は、さまざまな大きさの用語や反対の符号を適切に加算する他の方法がいくつかあります。
JM

@JMから他のメソッドの名前を教えてもらえますか。それらを読んでみたいと思います。ありがとう。
ガレスA.ロイド


0

「log-sum-expトリック」の高速で効率的な実装を提供するRパッケージがあります。

http://www.inside-r.org/packages/cran/matrixStats/docs/logSumExp

logSumExp関数は、数値ベクトルlXを受け入れ、説明した方法を使用してアンダーフローとオーバーフローの問題を回避しながらlog(sum(exp(lX)))を出力します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.