web-dev-qa-db-ja.com

MNISTでトレーニングされたモデルの数字認識を改善するにはどうすればよいですか?

Javaライブラリを使用して、前処理とセグメンテーションにOpenCVを使用し、MNISTでトレーニングされたKerasモデル(精度0.98)を使用して、手書きの多桁認識に取り組んでいます。認識。

認識は、1つの点を除けば、かなりうまく機能しているようです。ネットワークでは、1(番号「1」)を認識できないことがよくあります。セグメンテーションの前処理/不適切な実装が原因で発生したのか、標準のMNISTでトレーニングされたネットワークが、テストケースのように見える一番のものが見当たらないのかわかりません。

以下は、前処理とセグメンテーション後の問題のある数字の様子です。

enter image description hereになりますenter image description here であり、4として分類されます。

enter image description hereになりますenter image description here であり、7として分類されます。

enter image description hereになりますenter image description here であり、4として分類されます。等々...

これは、セグメンテーションプロセスを改善することで修正できるものですか?それとも、トレーニングセットを強化することによってですか。

編集:トレーニングセット(データ拡張)を強化することは間違いなく役立ちます。これは既にテストしていますが、正しい前処理の問題はまだ残っています。

私の前処理は、サイズ変更、グレースケールへの変換、2値化、反転、および膨張で構成されています。これがコードです:

Mat resized = new Mat();
Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);

Mat grayscale = new Mat();
Imgproc.cvtColor(resized, grayscale, Imgproc.COLOR_BGR2GRAY);

Mat binImg = new Mat(grayscale.size(), CvType.CV_8U);
Imgproc.threshold(grayscale, binImg, 0, 255, Imgproc.THRESH_OTSU);

Mat inverted = new Mat();
Core.bitwise_not(binImg, inverted);

Mat dilated = new Mat(inverted.size(), CvType.CV_8U);
int dilation_size = 5;
Mat kernel = Imgproc.getStructuringElement(Imgproc.CV_SHAPE_CROSS, new Size(dilation_size, dilation_size));
Imgproc.dilate(inverted, dilated, kernel, new Point(-1,-1), 1);

次に、前処理された画像は、次のように個々の数字に分割されます。

List<Mat> digits = new ArrayList<>();
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(preprocessed.clone(), contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

// code to sort contours
// code to check that contour is a valid char

List rects = new ArrayList<>();

for (MatOfPoint contour : contours) {
     Rect boundingBox = Imgproc.boundingRect(contour);
     Rect rectCrop = new Rect(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height);

     rects.add(rectCrop);
}

for (int i = 0; i < rects.size(); i++) {
    Rect x = (Rect) rects.get(i);
    Mat digit = new Mat(preprocessed, x);

    int border = 50;
    Mat result = digit.clone();
    Core.copyMakeBorder(result, result, border, border, border, border, Core.BORDER_CONSTANT, new Scalar(0, 0, 0));

    Imgproc.resize(result, result, new Size(28, 28));
    digits.add(result);
}
12
youngpanda

いくつかの研究と実験の後、画像の前処理自体は問題ではないという結論に達しました(拡張サイズや形状などのいくつかの提案されたパラメーターを変更しましたが、結果には重要ではありませんでした)。ただし、次の2つが役立ちました。

  1. @ f4fが気づいたように、私は実際のデータを含む自分のデータセットを収集する必要がありました。これはすでに非常に役立ちました。

  2. セグメンテーションの前処理に重要な変更を加えました。個々の輪郭を取得した後、まず20x20ピクセルボックスに収まるように画像をサイズ正規化します(MNISTと同様)。その後、質量の中心を使用して、ボックスを28x28画像の中央に配置します(これは、バイナリイメージの場合、両方の次元の平均値です)。

もちろん、桁の重複や接続など、難しいセグメンテーションのケースはまだありますが、上記の変更により、最初の質問に答え、分類のパフォーマンスが向上しました。

1
youngpanda