21

ニューラル ネットワークの最後の層でSoftmax活性化関数を使用しています。しかし、この関数の安全な実装には問題があります。

単純な実装は次のようになります。

Vector y = mlp(x); // output of the neural network without softmax activation function
for(int f = 0; f < y.rows(); f++)
  y(f) = exp(y(f));
y /= y.sum();

NaNy が多くの場合 (y(f) > 709 の場合、exp(y(f)) は inf を返す) になるため、これは 100 個を超える隠しノードではうまく機能しません。私はこのバージョンを思いつきました:

Vector y = mlp(x); // output of the neural network without softmax activation function
for(int f = 0; f < y.rows(); f++)
  y(f) = safeExp(y(f), y.rows());
y /= y.sum();

は次のようにsafeExp定義されます

double safeExp(double x, int div)
{
  static const double maxX = std::log(std::numeric_limits<double>::max());
  const double max = maxX / (double) div;
  if(x > max)
    x = max;
  return std::exp(x);
}

この関数は exp の入力を制限します。ほとんどの場合、これは機能しますが、すべての場合ではなく、どの場合に機能しないかを実際に見つけることができませんでした. 前のレイヤーに 800 個の隠れニューロンがあると、まったく機能しません。

ただし、これが機能したとしても、ANNの結果を何らかの形で「歪め」ました。正しい解を計算する他の方法を考えられますか? この ANN の正確な出力を計算するために使用できる C++ ライブラリまたはトリックはありますか?

編集: Itamar Katz が提供するソリューションは次のとおりです。

Vector y = mlp(x); // output of the neural network without softmax activation function
double ymax = maximal component of y
for(int f = 0; f < y.rows(); f++)
  y(f) = exp(y(f) - ymax);
y /= y.sum();

そして、それは実際には数学的に同じです。ただし、実際には、浮動小数点の精度のために、いくつかの小さな値が 0 になります。これらの実装の詳細を誰も教科書に書き留めていないのはなぜだろうか。

4

2 に答える 2

14

最初に対数スケールに移動します。つまり、log(y)の代わりに計算しますy。分子の対数は自明です。分母の対数を計算するには、次の「トリック」を使用できます: http://lingpipe-blog.com/2009/06/25/log-sum-of-exponentials/

于 2012-03-28T11:49:32.437 に答える
8

すでに回答されていることは知っていますが、とにかくここに段階的に投稿します。

ログに置く:

zj = wj . x + bj
oj = exp(zj)/sum_i{ exp(zi) }
log oj = zj - log sum_i{ exp(zi) }

m を max_i { zi } とし、log-sum-exp トリックを使用します。

log oj = zj - log {sum_i { exp(zi + m - m)}}
   = zj - log {sum_i { exp(m) exp(zi - m) }},
   = zj - log {exp(m) sum_i {exp(zi - m)}}
   = zj - m - log {sum_i { exp(zi - m)}}

m が他の z_i よりもはるかに大きい場合、exp(zi-m) という項はアンダーフローを被る可能性がありますが、これは z_i が正規化後のソフトマックス出力に無関係であることを意味するため、問題ありません。最終結果は次のとおりです。

oj = exp (zj - m - log{sum_i{exp(zi-m)}})
于 2015-03-23T02:54:35.300 に答える