web-dev-qa-db-ja.com

ロジスティック回帰のコスト関数は結果としてNaNを与える

バッチ勾配降下法を使用してロジスティック回帰を実装しています。入力サンプルが分類されるクラスは2つあります。クラスは1と0です。データのトレーニング中に、次のシグモイド関数を使用しています。

t = 1 ./ (1 + exp(-z));

どこ

z = x*theta

また、次のコスト関数を使用してコストを計算し、トレーニングを停止するタイミングを決定しています。

function cost = computeCost(x, y, theta)
    htheta = sigmoid(x*theta);
    cost = sum(-y .* log(htheta) - (1-y) .* log(1-htheta));
end

hthetaの値はほとんどの場合1または0であるため、各ステップのコストはNaNになります。各反復でのコスト値を決定するにはどうすればよいですか?

これはロジスティック回帰の勾配降下コードです。

function [theta,cost_history] = batchGD(x,y,theta,alpha)

cost_history = zeros(1000,1);

for iter=1:1000
  htheta = sigmoid(x*theta);
  new_theta = zeros(size(theta,1),1);
  for feature=1:size(theta,1)
    new_theta(feature) = theta(feature) - alpha * sum((htheta - y) .*x(:,feature))                         
  end
  theta = new_theta;
  cost_history(iter) = computeCost(x,y,theta);
end
end
15
Neel Shah

これが発生している理由は2つ考えられます。

データは正規化されていません

これは、シグモイド/ロジット関数を仮説に適用すると、出力確率がほぼすべて0またはすべて1になり、コスト関数を使用すると、log(1 - 1)またはlog(0)-Infを生成するためです。 。コスト関数にこれらの個々の項がすべて蓄積されると、最終的にはNaNになります。

具体的には、トレーニング例のy = 0で、仮説の出力がlog(x)の場合、xは0に近い非常に小さい数であり、コスト関数の最初の部分を調べると、 0*log(x)を提供してください。実際にNaNが生成されます。同様に、トレーニングの例でy = 1を使用し、仮説の出力もlog(x)である場合(xは非常に小さい数)、これにより0*log(x)NaNを生成します。簡単に言えば、仮説の出力は0に非常に近いか、1に非常に近いです。

これは、各機能のダイナミックレンジが大きく異なるため、仮説の一部、具体的には、各トレーニング例のx*thetaの加重和により、非常に大きな負の値または正の値が得られるためです。これらの値にシグモイド関数を適用すると、0または1に非常に近くなります。

これに対処する1つの方法は、勾配降下法を使用してトレーニングを実行する前に、行列のデータをnormalizeすることです。典型的なアプローチは、ゼロ平均と単位分散で正規化することです。入力フィーチャx_kがあり、k = 1, 2, ... nnフィーチャがある場合、新しい正規化されたフィーチャx_k^{new}は次のようにして見つけることができます。

m_kは特徴kの平均であり、s_kは特徴kの標準偏差です。これはstandardizingデータとも呼ばれます。詳細については、私がここで示した別の回答を参照してください。 データを標準化するためのこのコードはどのように機能しますか?

勾配降下法に線形代数アプローチを使用しているため、すべて1の列をデータマトリックスの先頭に追加していると想定しています。これを知っていれば、次のようにデータを正規化できます。

mX = mean(x,1); 
mX(1) = 0; 
sX = std(x,[],1); 
sX(1) = 1; 
xnew = bsxfun(@rdivide, bsxfun(@minus, x, mX), sX);

各特徴の平均と標準偏差は、それぞれmXsXに格納されます。このコードがどのように機能するかについては、上記でリンクした投稿を読むとわかります。これはこの投稿の範囲ではないので、ここでは繰り返しません。適切な正規化を確実にするために、最初の列の平均と標準偏差をそれぞれ0と1にしました。 xnewには、新しい正規化されたデータ行列が含まれています。代わりに、勾配降下アルゴリズムでxnewを使用してください。パラメータを見つけたら、予測を実行するために必須で、新しいテストインスタンスをトレーニングセット。学習されるパラメーターはトレーニングセットの統計に関連しているため、予測モデルに送信するテストデータにも同じ変換を適用する必要があります。

xxというマトリックスに新しいデータポイントが格納されていると仮定すると、正規化してから予測を実行します。

xxnew = bsxfun(@rdivide, bsxfun(@minus, xx, mX), sX);

これで、予測を実行できます。

pred = sigmoid(xxnew*theta) >= 0.5;

0.5のしきい値を変更して、例がポジティブクラスとネガティブクラスのどちらに属するかを決定するのに最適と思われる値に変更できます。

学習率が高すぎる

コメントで述べたように、データを正規化すると、コストは有限であるように見えますが、数回の反復後に突然NaNに移動します。正規化では、これまでのところしか得られません。学習率またはalphaが大きすぎる場合、各反復は最小に向かう方向にオーバーシュートするため、各反復でのコストが変動したり、発散したりして、発生しているように見えます。あなたの場合、コストは、浮動小数点精度を使用して表すことができないほど大きくなるまで、反復ごとに発散または増加しています。

そのため、他の1つのオプションは、反復ごとにコスト関数が減少していることがわかるまで、学習率alphaを減少させることです。最良の学習率を決定する一般的な方法は、対数的に間隔を空けたalphaの値の範囲で勾配降下を実行し、最終的なコスト関数の値を確認し、最小になる学習率を選択することです費用。


コスト関数が凸型であると仮定すると、上記の2つの事実を一緒に使用すると、勾配降下法が非常にうまく収束できます。この場合、ロジスティック回帰の場合、それは間違いありません。

22
rayryeng

次のような観察結果があるとします。

  • 真の値はy_i = 1です。
  • あなたのモデルはかなり極端であり、P(y_i = 1)= 1

次に、未定義の0 * log(0)を追加しているため、コスト関数はNaNの値を取得します。したがって:

コスト関数の数式に問題があります(微妙な0、無限大の問題があります)!

@rayryengが指摘したように、_0 * Inf_はコーシャではないため、0 * log(0)NaNを生成します。これは実際には大きな問題です。アルゴリズムが値を完全に予測できるとアルゴリズムが信じている場合、NaNのコストが誤って割り当てられます。

の代わりに:

_cost = sum(-y .* log(htheta) - (1-y) .* log(1-htheta));
_

Matlabでコスト関数を次のように記述することにより、0に無限大を掛けることを回避できます。

_y_logical = y == 1;
cost = sum(-log(htheta(y_logical))) + sum( - log(1 - htheta(~y_logical)));
_

_y_i_が1の場合、コストに-log(htheta_i)を追加しますが、_y_i_が0の場合、コストに-log(1 - htheta_i)を追加します。これは数学的には-y_i * log(htheta_i) - (1 - y_i) * log(1- htheta_i)と同等ですが、基本的に_htheta_i_が倍精度浮動小数点の範囲内で0または1に等しいことに起因する数値の問題にぶつかることはありません。

4
Matthew Gunn