バッチ勾配降下法を使用してロジスティック回帰を実装しています。入力サンプルが分類されるクラスは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
これが発生している理由は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, ... n
がn
フィーチャがある場合、新しい正規化されたフィーチャ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);
各特徴の平均と標準偏差は、それぞれmX
とsX
に格納されます。このコードがどのように機能するかについては、上記でリンクした投稿を読むとわかります。これはこの投稿の範囲ではないので、ここでは繰り返しません。適切な正規化を確実にするために、最初の列の平均と標準偏差をそれぞれ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つの事実を一緒に使用すると、勾配降下法が非常にうまく収束できます。この場合、ロジスティック回帰の場合、それは間違いありません。
次のような観察結果があるとします。
次に、未定義の0 * log(0)
を追加しているため、コスト関数はNaN
の値を取得します。したがって:
@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に等しいことに起因する数値の問題にぶつかることはありません。