web-dev-qa-db-ja.com

ニューラルネットワークは常に同じクラスを予測します

画像を2つの個別のカテゴリのいずれかに分類するニューラルネットワークを実装しようとしています。ただし、問題は、現在どの入力でも常に0が予測されることであり、その理由はよくわかりません。

これが私の特徴抽出方法です:

def extract(file):
    # Resize and subtract mean pixel
    img = cv2.resize(cv2.imread(file), (224, 224)).astype(np.float32)
    img[:, :, 0] -= 103.939
    img[:, :, 1] -= 116.779
    img[:, :, 2] -= 123.68
    # Normalize features
    img = (img.flatten() - np.mean(img)) / np.std(img)

    return np.array([img])

勾配降下ルーチンは次のとおりです。

def fit(x, y, t1, t2):
    """Training routine"""
    ils = x.shape[1] if len(x.shape) > 1 else 1
    labels = len(set(y))

    if t1 is None or t2 is None:
        t1 = randweights(ils, 10)
        t2 = randweights(10, labels)

    params = np.concatenate([t1.reshape(-1), t2.reshape(-1)])
    res = grad(params, ils, 10, labels, x, y)
    params -= 0.1 * res

    return unpack(params, ils, 10, labels)

これが私の前方および後方(勾配)伝播です。

def forward(x, theta1, theta2):
    """Forward propagation"""

    m = x.shape[0]

    # Forward prop
    a1 = np.vstack((np.ones([1, m]), x.T))
    z2 = np.dot(theta1, a1)

    a2 = np.vstack((np.ones([1, m]), sigmoid(z2)))
    a3 = sigmoid(np.dot(theta2, a2))

    return (a1, a2, a3, z2, m)

def grad(params, ils, hls, labels, x, Y, lmbda=0.01):
    """Compute gradient for hypothesis Theta"""

    theta1, theta2 = unpack(params, ils, hls, labels)

    a1, a2, a3, z2, m = forward(x, theta1, theta2)
    d3 = a3 - Y.T
    print('Current error: {}'.format(np.mean(np.abs(d3))))

    d2 = np.dot(theta2.T, d3) * (np.vstack([np.ones([1, m]), sigmoid_prime(z2)]))
    d3 = d3.T
    d2 = d2[1:, :].T

    t1_grad = np.dot(d2.T, a1.T)
    t2_grad = np.dot(d3.T, a2.T)

    theta1[0] = np.zeros([1, theta1.shape[1]])
    theta2[0] = np.zeros([1, theta2.shape[1]])

    t1_grad = t1_grad + (lmbda / m) * theta1
    t2_grad = t2_grad + (lmbda / m) * theta2

    return np.concatenate([t1_grad.reshape(-1), t2_grad.reshape(-1)])

そして、ここに私の予測関数があります:

def predict(theta1, theta2, x):
    """Predict output using learned weights"""
    m = x.shape[0]

    h1 = sigmoid(np.hstack((np.ones([m, 1]), x)).dot(theta1.T))
    h2 = sigmoid(np.hstack((np.ones([m, 1]), h1)).dot(theta2.T))

    return h2.argmax(axis=1)

繰り返しごとにエラー率が徐々に減少し、一般的に1.26e-05付近で収束していることがわかります。

私が今まで試したこと:

  1. PCA
  2. 異なるデータセット(sklearnのアイリスとCoursera MLコースの手書きの数字。両方で約95%の精度を達成)。ただし、これらは両方ともバッチで処理されたため、一般的な実装は正しいと想定できますが、機能の抽出方法または分類器のトレーニング方法に何か問題があります。
  3. SklearnのSGDClassifierを試してみましたが、パフォーマンスはそれほど良くなく、精度は最大50%でした。では、機能に何か問題がありますか?

編集:h2の平均出力は次のようになります。

[0.5004899   0.45264441]
[0.50048522  0.47439413]
[0.50049019  0.46557124]
[0.50049261  0.45297816]

したがって、すべての検証例で非常によく似たシグモイド出力です。

28
Yurii Dolhikh

一週間半の研究の後、私は問題が何であるかを理解したと思います。コード自体に問題はありません。実装が正常に分類されない2つの問題は、学習に費やした時間と学習率/正則化パラメーターの適切な選択です。

私は今、いくつかの本のために学習ルーチンを実行しており、それはすでに75%の精度を押し上げていますが、まだ改善の余地はたくさんあります。

9
Yurii Dolhikh

私のネットワークは常に同じクラスを予測します。何が問題ですか?

これは何回かありました。私は現在あなたのコードを検証するのが面倒ですが、同じ症状を持っているがおそらく根本的な問題が異なる他の人にも役立つかもしれないいくつかの一般的なヒントを与えることができると思います。

ニューラルネットワークのデバッグ

1つのアイテムデータセットのフィッティング

ネットワークが予測できるすべてのクラスiについて、以下を試してください。

  1. クラスiの1つのデータポイントのみのデータセットを作成します。
  2. このデータセットにネットワークを適合させます。
  3. ネットワークは「クラスi」を予測することを学習しますか?

これが機能しない場合は、4つのエラーソースが考えられます。

  1. バギートレーニングアルゴリズム:より小さいモデルを試して、その間に計算された多くの値を印刷し、それらが期待に一致するかどうかを確認します。
    1. 0で割る:分母に小さな数を追加します
    2. 0の対数/負の数:0で割ったような
  2. Data:データのタイプが間違っている可能性があります。たとえば、データのタイプが float32 しかし、実際は整数です。
  3. Model:必要なものを予測できないモデルを作成した可能性もあります。これは、より単純なモデルを試すときに明らかになるはずです。
  4. 初期化/最適化:モデルによっては、初期化と最適化アルゴリズムが重要な役割を果たす場合があります。標準の確率的勾配降下法を使用する初心者の場合、主に重みをランダムに初期化することが重要です(それぞれの重みは異なる値です)。 -参照: この質問/回答

学習曲線

詳細については sklearn を参照してください。

Learning Curve showing the training error / test error curves to approach each other

アイデアは、小さなトレーニングデータセット(おそらく1つのアイテムのみ)から始めることです。次に、モデルはデータに完全に適合できる必要があります。これが機能する場合、少し大きいデータセットを作成します。トレーニングエラーは、ある時点でわずかにupになるはずです。これにより、データをモデル化するモデルの能力が明らかになります。

データ分析

他のクラスが表示される頻度を確認します。 1つのクラスが他のクラスを支配している場合(たとえば、1つのクラスがデータの99.9%である場合)、これは問題です。 「異常値検出」技術を探してください。

もっと

  • 学習率:ネットワークが改善せず、ランダムチャンスよりも少しだけ良くなった場合は、学習率を下げてみてください。コンピュータービジョンの場合、学習率は0.001はよく使用されます/動作します。これは、Adamをオプティマイザーとして使用する場合にも関係します。
  • 前処理:トレーニングとテストに必ず同じ前処理を使用してください。混同マトリックスに違いがある場合があります( この質問 を参照)

よくある間違い

これは reddit に触発されています:

  • 前処理を適用するのを忘れた
  • Dying ReL
  • 学習率が小さすぎる/大きすぎる
  • 最終層の誤ったアクティベーション関数:
    • あなたの目標は合計ではありませんか? -> softmaxを使用しないでください
    • ターゲットの単一要素が負です-> Softmax、ReLU、Sigmoidを使用しないでください。 tanhはオプションかもしれません
  • ネットワークが深すぎる:トレーニングに失敗します。最初に、より単純なニューラルネットワークを試してください。
  • 非常に不均衡なデータ: imbalanced-learn
65
Martin Thoma

私にも同じことが起こりました。不均衡なデータセット(クラス0と1の間でそれぞれ約66%-33%のサンプル分布)があり、ネットは最初の反復後のすべてのサンプルに対して常に0.0を出力していました。

私の問題は単に学習率が高すぎるでした。 1e-05に切り替えると、問題は解決しました。

より一般的には、パラメーターの更新前に印刷することをお勧めします。

  • ネット出力(1バッチ)
  • 対応するラベル(同じバッチ用)
  • (同じバッチでの)損失の値は、サンプルごとまたは集計されます。

そして、パラメーターの更新後に同じ3つの項目を確認します。次のバッチで表示されるのは、ネット出力のgradualの変化です。学習率が高すぎると、すでに2回目の反復で、ネット出力はバッチ内のすべてのサンプルのすべての1.0sまたはすべての0.0sのいずれかを撃ちます。

0
Tommaso Di Noto

他の誰かがこの問題に遭遇した場合に備えてください。私はdeeplearning4jLenet(CNN)アーキテクチャを使用していました。すべてのテストで最後のトレーニングフォルダーの最終出力を提供し続けました。 increasing my batchsizeshuffling the training dataで解決できたので、各バッチには少なくとも複数のサンプルが含まれていましたフォルダ。データクラスのバッチサイズは1で、これは実際にはdangerousでした。

編集:最近見た別のことは、datasetが大きいにもかかわらず、クラスごとにトレーニングサンプルのセットが限られていることです。 egneural-networkをトレーニングしてhuman facesを認識するが、最大の発言数21の異なる顔= personは、データセットがたとえば10,0personsで構成されていることを意味します。したがって、_20,0datasetは合計でfacesです。より良いdataset10異なるfaces for 10,0personsです。したがってdataset10,000,00faces合計。これは、データを1つのクラスにオーバーフィットしないようにする場合に必要です。したがって、networkは簡単に一般化し、より良い予測を生成できます。

0
LiNKeR