(この質問に対する更新が追加されました。)
私はベルギーのゲント大学の大学院生です。私の研究は、深い畳み込みニューラルネットワークによる感情認識に関するものです。 Caffe フレームワークを使用してCNNを実装しています。
最近、クラスの不均衡に関する問題に遭遇しました。約9216のトレーニングサンプルを使用しています。 5%は正のラベルが付けられ(1)、残りのサンプルは負のラベルが付けられます(0)。
SigmoidCrossEntropyLoss レイヤーを使用して損失を計算しています。トレーニングすると、損失が減少し、数エポック後でも精度が非常に高くなります。これは不均衡が原因です。ネットワークは常に負(0)を予測するだけです。 (精度と再現率はどちらもゼロであり、この主張を裏付けています)
この問題を解決するために、私は予測-真の組み合わせに応じて損失への寄与をスケーリングする(偽陰性を厳しく罰する)にします。メンター/コーチはバックプロパゲーション時にスケール係数を使用するようにアドバイスしました確率的勾配降下法(sgd)を使用:係数はバッチ内の不均衡に相関します。負のサンプルのみを含むバッチは、重みをまったく更新しません。
Caffeにカスタムレイヤーを1つだけ追加しました:精度や再現率などの他のメトリックを報告します。 Caffeコードの使用経験は限られていますが、C++コードの記述には多くの専門知識があります。
SigmoidCrossEntropyLoss および Sigmoid レイヤーを調整して次の変更に対応する方法について、誰かが私を助けたり正しい方向に向けたりできますか:
前もって感謝します!
InfogainLossLayer を Shaiで推奨されているように組み込んでいます。また、infogainマトリックスH
を構築する別のカスタムレイヤーを追加しました現在のバッチの不均衡に基づいています。
現在、マトリックスは次のように構成されています。
H(i, j) = 0 if i != j
H(i, j) = 1 - f(i) if i == j (with f(i) = the frequency of class i in the batch)
私は将来、マトリックスのさまざまな構成を試すことを計画しています。
私はこれを10:1の不均衡でテストしました。結果は、ネットワークが現在有用なことを学習していることを示しています:(30エポック後の結果)
これらの数値は約20エポックで到達し、その後は大幅に変化しませんでした。
!!上記の結果は単なる概念実証であり、10:1の不均衡データセットで単純なネットワークをトレーニングすることで得られたものです。 !!
InfogainLoss レイヤーを使用して、トレーニングセットの不均衡を補正しませんか?
Infogainの損失は、重み行列H
(この場合は2行2列)を使用して定義されます。そのエントリの意味は次のとおりです。
[cost of predicting 1 when gt is 0, cost of predicting 0 when gt is 0
cost of predicting 1 when gt is 1, cost of predicting 0 when gt is 1]
したがって、H
のエントリを設定して、0または1の予測におけるエラー間の違いを反映できます。
this thread でcaffeの行列H
を定義する方法を見つけることができます。
サンプルの重みに関して、あなたは この投稿 を見つけるかもしれません:サンプルの重みを考慮に入れるためにSoftmaxWithLossレイヤーを変更する方法を示しています。
最近、クロスエントロピー損失の修正が提案されました Tsung-Yi Lin、Priya Goyal、Ross Girshick、Kaiming He、PiotrDollár高密度オブジェクト検出の焦点損失、(ICCV 2017) 。
フォーカルロスの背後にある考え方は、この例を予測することの相対的な難しさに基づいて(むしろクラスサイズなどに基づいて)各例に異なる重みを割り当てることです。この損失を試してみた短い時間から、クラスサイズの重みを持つ"InfogainLoss"
よりも優れていると感じています。
また、分類タスクでこのクラスの不均衡の問題に遭遇しました。現在私はCrossEntropyLossを重み付きで使用しています(ドキュメント here )が正常に機能します。アイデアは、画像の数が少ないクラスのサンプルにより多くの損失を与えることです。
各クラスの重みは、このクラスの画像番号に反比例します。以下は、numpyを使用してすべてのクラスの重みを計算するスニペットです。
cls_num = []
# train_labels is a list of class labels for all training samples
# the labels are in range [0, n-1] (n classes in total)
train_labels = np.asarray(train_labels)
num_cls = np.unique(train_labels).size
for i in range(num_cls):
cls_num.append(len(np.where(train_labels==i)[0]))
cls_num = np.array(cls_num)
cls_num = cls_num.max()/cls_num
x = 1.0/np.sum(cls_num)
# the weight is an array which contains weight to use in CrossEntropyLoss
# for each class.
weight = x*cls_num