データ増強|データが限られている場合のディープラーニングの使用方法—パート2

この記事は、画像に固有の、ディープラーニングのデータ増強手法の包括的なレビューです。これは、データが限られている場合にディープラーニングを使用する方法のパート2です。チェックアウトパート1はこちら。

我々はすべてそこにいた。機械学習モデルを使用して実装できる優れたコンセプトがあります。快活であると感じたら、Webブラウザーを開いて関連データを検索します。おそらく、数百の画像を含むデータセットを見つけるでしょう。

最も人気のあるデータセットには、数万(またはそれ以上)のオーダーの画像があることを思い出してください。また、優れたパフォーマンスを実現するには、大規模なデータセットを持っていることが重要だと言っている人もいます。がっかりして、不思議に思うでしょう。私の「最先端の」ニューラルネットワークは、私が持っているわずかな量のデータで十分に機能しますか?

答えは、はいです!しかし、それを実現する魔法に取りかかる前に、いくつかの基本的な質問を熟考する必要があります。

なぜ大量のデータが必要なのですか?

一般的なニューラルネットワークのパラメーターの数(百万単位)。

機械学習モデルをトレーニングするとき、実際にやっているのは、特定の入力(画像など)を何らかの出力(ラベル)にマッピングできるようにパラメーターを調整することです。最適化の目標は、モデルの損失が少ないスイートスポットを追跡することです。これは、パラメーターが適切に調整されたときに発生します。

最先端のニューラルネットワークには通常、数百万のオーダーのパラメーターがあります!

当然、多くのパラメーターがある場合、機械学習モデルに比例した量の例を示して、良好なパフォーマンスを得る必要があります。また、必要なパラメーターの数は、モデルが実行する必要のあるタスクの複雑さに比例します。

「追加データ」がない場合、どのようにデータを取得しますか?

データセットに追加できる新しい画像を探す必要はありません。どうして?なぜなら、ニューラルネットワークは最初は賢くないからです。たとえば、十分に訓練されていないニューラルネットワークは、以下に示すこれらの3つのテニスボールは、別個のユニークな画像であると考えます。

同じテニスボールですが、翻訳されました。

したがって、より多くのデータを取得するには、既存のデータセットにわずかな変更を加えるだけです。フリップ、平行移動、回転などの小さな変更。いずれにせよ、私たちのニューラルネットワークは、これらが別個の画像であると考えます。

プレイ中のデータ増強

異なる向きに配置されたオブジェクトをロバストに分類できる畳み込みニューラルネットワークは、不変性と呼ばれる特性を持つと言われています。より具体的には、CNNは、平行移動、視点、サイズ、または照明(または上記の組み合わせ)に対して不変である場合があります。

これは本質的にデータ増大の前提です。実世界のシナリオでは、限られた条件で撮影された画像のデータセットがある場合があります。ただし、ターゲットアプリケーションは、さまざまな方向、位置、スケール、明るさなどのさまざまな条件で存在する可能性があります。これらの状況は、合成的に変更された追加データでニューラルネットワークをトレーニングすることで説明します。

大量のデータがある場合でも、増強は役立ちますか?

はい。データセット内の関連データの量を増やすのに役立ちます。これは、ニューラルネットワークの学習方法に関連しています。例で説明しましょう。

仮想データセットの2つのクラス。左側の1つはブランドA(フォード)を表し、右側の1つはブランドB(シボレー)を表します。

上記のように、2つのブランドの車で構成されるデータセットがあるとします。ブランドAのすべての車が左の写真とまったく同じように並んでいると仮定します(つまり、すべての車が左を向いている)。同様に、ブランドBのすべての車は、右の写真とまったく同じように配置されています(つまり、右向き)。次に、このデータセットを「最先端の」ニューラルネットワークにフィードし、トレーニングが完了したら印象的な結果を得たいと考えています。

フォード車(ブランドA)、右向き。

トレーニングが完了し、上記の画像(ブランドAの車)をフィードするとします。しかし、ニューラルネットワークは、ブランドBの車だと出力します!混乱しています。 「最先端の」ニューラルネットワークを使用して、データセットの95%の精度を得ただけではありませんか?誇張しているわけではありませんが、過去にも同様の事件や間抜けが発生しています。

なぜこれが起こるのですか?それは、ほとんどの機械学習アルゴリズムが機能するためです。あるクラスと別のクラスを区別する最も明白な機能を見つけます。ここでは、ブランドAのすべての車が左を向き、ブランドBのすべての車が右を向いているという特徴がありました。

ニューラルネットワークは、フィードするデータと同じくらい良いです。

これをどうやって防ぐのですか?データセット内の無関係なフィーチャの量を減らす必要があります。上記の自動車モデル分類子の場合、簡単な解決策は、元のデータセットに他の方向を向いた両方のクラスの自動車の写真を追加することです。さらに良いのは、既存のデータセットの画像を水平方向に反転させて、反対側に向けることです!ここで、この新しいデータセットでニューラルネットワークをトレーニングすると、目的のパフォーマンスが得られます。

拡張を実行することにより、ニューラルネットワークが無関係なパターンを学習することを防ぎ、基本的に全体的なパフォーマンスを向上させることができます。

入門

さまざまな増強技術に飛び込む前に、事前に考慮しなければならない問題が1つあります。

MLパイプラインのどこでデータを増強しますか?

答えは非常に明白に思えるかもしれません。データをモデルにフィードする前に拡張を行いますか?はい。ただし、ここには2つのオプションがあります。 1つのオプションは、必要なすべての変換を事前に実行して、データセットのサイズを本質的に増やすことです。もう1つのオプションは、機械学習モデルに供給する直前に、これらの変換をミニバッチで実行することです。

最初のオプションは、オフライン拡張として知られています。この方法は、比較的小さなデータセットに適しています。実行する変換の数に等しい係数でデータセットのサイズを大きくすることになります(たとえば、すべての画像を反転することで、データセットのサイズを係数2)。

2番目のオプションは、オンライン拡張、またはその場での拡張として知られています。サイズが爆発的に増加する余裕がないため、この方法は大規模なデータセットに適しています。代わりに、モデルにフィードするミニバッチで変換を実行します。一部の機械学習フレームワークは、GPUで高速化できるオンライン拡張をサポートしています。

人気のある拡張テクニック

このセクションでは、一般的に使用されるいくつかの基本的かつ強力な増強技術を紹介します。これらの手法を検討する前に、簡単にするために、1つの仮定を立てましょう。前提は、画像の境界を越えたものを考慮する必要がないということです。仮定が有効になるように、以下の手法を使用します。

画像の境界を越えて何が存在するかを推測させる技術を使用するとどうなりますか?この場合、いくつかの情報を補間する必要があります。拡張の種類について説明した後、これについて詳しく説明します。

これらの手法のそれぞれについて、データセットのサイズが増加する要因(別名、データ増加要因)も指定します。

1.フリップ

画像を水平および垂直に反転できます。一部のフレームワークは、垂直反転の機能を提供しません。ただし、垂直反転は、画像を180度回転してから水平反転を実行するのと同じです。以下は、反転された画像の例です。

左から元の画像があり、続いて画像が水平に反転し、次に画像が垂直に反転しています。

お気に入りのパッケージから、次のコマンドのいずれかを使用してフリップを実行できます。データ増加係数= 2から4x

#NumPy.'img '=単一の画像。
flip_1 = np.fliplr(img)
#TensorFlow。 'x' =画像のプレースホルダー。
形状= [高さ、幅、チャネル]
x = tf.placeholder(dtype = tf.float32、shape = shape)
flip_2 = tf.image.flip_up_down(x)
flip_3 = tf.image.flip_left_right(x)
flip_4 = tf.image.random_flip_up_down(x)
flip_5 = tf.image.random_flip_left_right(x)

2.回転

この操作に関する重要な点の1つは、回転後に画像の寸法が保持されない場合があることです。画像が正方形の場合、直角に回転すると画像サイズが維持されます。長方形の場合、180度回転するとサイズが維持されます。より細かい角度で画像を回転させると、最終的な画像サイズも変わります。次のセクションでこの問題に対処する方法を確認します。以下は、直角に回転した正方形の画像の例です。

左から右に移動すると、画像は前の画像に対して時計回りに90度回転します。

お気に入りのパッケージから、次のコマンドのいずれかを使用してローテーションを実行できます。データ増加係数= 2から4x

#プレースホルダー: 'x' =単一の画像、 'y' =画像のバッチ
# 'k'は90度反時計回りの回転数を示します
形状= [高さ、幅、チャネル]
x = tf.placeholder(dtype = tf.float32、shape = shape)
rot_90 = tf.image.rot90(img、k = 1)
rot_180 = tf.image.rot90(img、k = 2)
#任意の角度で回転します。以下の例では、「角度」はラジアン単位です
形状= [バッチ、高さ、幅、3]
y = tf.placeholder(dtype = tf.float32、shape = shape)
rot_tf_180 = tf.contrib.image.rotate(y、angles = 3.1415)
#Scikit-Image。 'angle' =度。 'img' =入力画像
#「モード」の詳細については、以下の補間セクションをご覧ください。
rot = skimage.transform.rotate(img、angle = 45、mode = 'reflect')

3.スケール

画像は外側または内側に拡大縮小できます。外側にスケーリングしている間、最終的な画像サイズは元の画像サイズより大きくなります。ほとんどの画像フレームワークは、元の画像と同じサイズで新しい画像からセクションを切り取ります。次のセクションでは、画像サイズを縮小し、境界を越えたものについて推測することを余儀なくされるため、内側のスケーリングについて扱います。以下は、スケーリングされた例または画像です。

左から、元の画像があります。画像は外側に10%スケーリングされ、画像は外側に20%スケーリングされています

scikit-imageを使用して、次のコマンドを使用してスケーリングを実行できます。データ増加係数=任意。

#Scikitイメージ。 'img' =入力画像、 'scale' =スケール係数
#「モード」の詳細については、以下の補間セクションをご覧ください。
scale_out = skimage.transform.rescale(img、scale = 2.0、mode = 'constant')
scale_in = skimage.transform.rescale(img、scale = 0.5、mode = 'constant')
#画像を元のサイズに切り戻すことを忘れないでください(
# 規格外)

4.切り抜き

スケーリングとは異なり、元の画像からセクションをランダムにサンプリングします。次に、このセクションのサイズを元の画像サイズに変更します。この方法は、一般にランダムクロッピングとして知られています。以下はランダムクロッピングの例です。よく見ると、この方法とスケーリングの違いに気づくでしょう。

左から、元の画像、左上から切り取られた正方形のセクション、右下から切り取られた正方形のセクションがあります。トリミングされたセクションは、元の画像サイズにサイズ変更されました。

TensorFlowの次のコマンドを使用して、ランダムクロップを実行できます。データ増加係数=任意。

#TensorFlow。 'x' =画像のプレースホルダー。
original_size = [高さ、幅、チャンネル]
x = tf.placeholder(dtype = tf.float32、shape = original_size)
#次のコマンドを使用して、ランダムクロップを実行します
crop_size = [new_height、new_width、channels]
シード= np.random.randint(1234)
x = tf.random_crop(x、サイズ= crop_size、シード=シード)
出力= tf.images.resize_images(x、サイズ= original_size)

5.翻訳

平行移動には、X方向またはY方向(またはその両方)に沿って画像を移動するだけです。次の例では、画像の境界を超えた背景が黒であり、適切に変換されると想定しています。ほとんどのオブジェクトは画像のほぼどこにでも配置できるため、この増強方法は非常に便利です。これにより、畳み込みニューラルネットワークがどこにでも見えるようになります。

左から、元の画像、右に翻訳された画像、上に翻訳された画像があります。

次のコマンドを使用して、TensorFlowで変換を実行できます。データ増加係数=任意。

#pad_left、pad_right、pad_top、pad_bottomはピクセルを示します
#変位。それらの1つを目的の値に設定し、残りを0に設定します
形状= [バッチ、高さ、幅、チャネル]
x = tf.placeholder(dtype = tf.float32、shape = shape)
#2つの関数を使用して、目的の増強を取得します
x = tf.image.pad_to_bounding_box(x、pad_top、pad_left、height + pad_bottom + pad_top、width + pad_right + pad_left)
出力= tf.image.crop_to_bounding_box(x、pad_bottom、pad_right、height、width)

6.ガウスノイズ

過剰適合は、通常、ニューラルネットワークが役に立たない可能性のある高周波機能(頻繁に発生するパターン)を学習しようとしたときに発生します。平均がゼロのガウスノイズは、本質的にすべての周波数でデータポイントを持ち、高周波の特徴を効果的に歪めます。これはまた、より低い周波数成分(通常、目的のデータ)も歪むことを意味しますが、ニューラルネットワークはそれを超えて学習することができます。適切な量​​のノイズを追加すると、学習機能を強化できます。

これをトーンダウンしたバージョンは、塩と胡pepperのノイズです。これは、画像全体に広がるランダムな黒と白のピクセルとして現れます。これは、画像にガウスノイズを追加することで生じる効果に似ていますが、情報の歪みレベルが低くなる場合があります。

左から、元の画像、ガウスノイズが追加された画像、塩と胡pepperのノイズが追加された画像があります。

TensorFlowで次のコマンドを使用して、画像にガウスノイズを追加できます。データ増加係数= 2倍。

#TensorFlow。 'x' =画像のプレースホルダー。
形状= [高さ、幅、チャネル]
x = tf.placeholder(dtype = tf.float32、shape = shape)
#ガウスノイズの追加
ノイズ= tf.random_normal(shape = tf.shape(x)、mean = 0.0、stddev = 1.0、
dtype = tf.float32)
出力= tf.add(x、ノイズ)

高度な拡張テクニック

現実の世界では、自然なデータは、上記の簡単な方法では説明できないさまざまな条件で存在する可能性があります。たとえば、写真の風景を識別するタスクを考えてみましょう。凍結するツンドラ、草原、森林など、風景は何でもかまいません。かなり簡単な分類タスクのようですね。 1つのことを除いて、あなたは正しいでしょう。パフォーマンスに影響を与える写真の重要な機能、つまり写真が撮影された季節を見落としています。

特定の景観がさまざまな条件(雪、湿気、明るいなど)で存在する可能性があるという事実をニューラルネットワークが理解していない場合、凍った湖shoreを氷河または湿地として湿地として誤ってラベル付けする可能性があります。

この状況を緩和する1つの方法は、季節の変化をすべて考慮に入れるように写真を追加することです。しかし、それは骨の折れる作業です。データ増大の概念を拡張し、異なる季節などの効果を人為的に生成することがどれほどクールか想像してみてください。

条件付きGANによる救助!

条件付きGANは、詳細を説明することなく、あるドメインから別のドメインへの画像に画像を変換できます。曖昧すぎると思われる場合は、そうではありません。それは文字通り、このニューラルネットワークがどれほど強力なのかです!以下は、夏の風景の写真を冬の風景に変換するために使用される条件付きGANの例です。

CycleGANを使用した季節の変更(出典:https://junyanz.github.io/CycleGAN/)

上記の方法は堅牢ですが、計算量が多くなります。より安価な代替手段は、ニューラルスタイル転送と呼ばれるものです。 1つの画像(別名「スタイル」)のテクスチャ/雰囲気/外観を取得し、別の画像のコンテンツと混合します。この強力な手法を使用して、条件付きGANと同様の効果を生成します(実際、この方法はcGANが発明される前に導入されました!)。

この方法の唯一の欠点は、出力が現実的ではなく芸術的に見える傾向があることです。ただし、以下に示すDeep Photo Style Transferなどの特定の進歩には、素晴らしい結果があります。

ディープフォトスタイルの転送。データセットに必要な効果をどのように生成できるかに注目してください。 (出典:https://arxiv.org/abs/1703.07511)

私たちはそれらの内部の働きに関心がないので、これらのテクニックを深く掘り下げていません。既存の訓練されたモデルを、転移学習の魔法とともに使用して、増強に使用できます。

補間に関する簡単なメモ

黒い背景のない画像を翻訳したい場合はどうしますか?内側に拡張したい場合はどうしますか?または、より細かい角度で回転しますか?これらの変換を実行した後、元の画像サイズを保持する必要があります。画像には境界外の情報がないため、いくつかの仮定を行う必要があります。通常、画像の境界を超えるスペースは、すべてのポイントで定数0と見なされます。したがって、これらの変換を行うと、画像が定義されていない黒い領域が得られます。

左から、反時計回りに45度回転した画像、右に移動した画像、内側に拡大縮小した画像。

しかし、それは正しい仮定ですか?現実の世界のシナリオでは、ほとんどが「いいえ」です。画像処理およびMLフレームワークには、未知のスペースを埋める方法を決定できるいくつかの標準的な方法があります。それらは次のように定義されます。

左から、定数、エッジ、反射、対称、ラップのモードがあります。

1.定数

最も単純な補間方法は、未知の領域を一定の値で埋めることです。これは自然な画像では機能しない場合がありますが、単色の背景で撮影された画像では機能します

2.エッジ

画像のエッジ値は、境界の後に拡張されます。この方法は、軽度の翻訳に有効です。

3.反映する

画像のピクセル値は、画像の境界に沿って反映されます。この方法は、木や山などを含む連続的または自然な背景に役立ちます。

4.対称

この方法は反射に似ていますが、反射の境界でエッジピクセルのコピーが作成されるという事実を除きます。通常、リフレクトと対称は同じ意味で使用できますが、非常に小さな画像やパターンを処理しているときに違いが見えます。

5.ラップ

画像は、タイルのように境界を越えて繰り返されます。この方法は、多くのシナリオでは意味がないため、他の方法ほど一般的には使用されていません。

これらに加えて、未定義のスペースを処理するための独自のメソッドを設計できますが、通常、これらのメソッドはほとんどの分類問題に対してうまく機能します。

したがって、これらの手法をすべて使用すると、私のMLアルゴリズムは堅牢になりますか?

あなたが正しい方法でそれを使用する場合、はい!あなたが尋ねる正しい方法は何ですか?まあ、時々、すべての増強技術がデータセットに意味をなさないことがあります。車の例をもう一度考えてみましょう。以下は、画像を変更する方法の一部です。

最初の画像(左から)は元の画像で、2番目の画像は水平に反転し、3番目の画像は180度回転し、最後の画像は90度(時計回り)回転します。

確かに、それらは同じ車の写真ですが、ターゲットアプリケーションはこれらの向きで提示された車を見ることはないかもしれません。

たとえば、道路上のランダムな車を分類するだけの場合、2番目の画像のみがデータセット上にあることが理にかなっています。しかし、あなたが自動車事故に対処する保険会社を所有しており、逆さまに壊れた車のモデルも特定したい場合、3番目のイメージは理にかなっています。最後の画像は、上記の両方のシナリオで意味をなさない場合があります。

重要なのは、増強技術を使用している間は、無関係なデータが増えないようにする必要があるということです。

本当に努力する価値はありますか?

おそらく、いくつかの結果があなたに1マイル以上歩くように動機付けると期待しているでしょう。けっこうだ;それもカバーしています。おもちゃの例を使用して、増強が実際に機能することを証明させてください。この実験を再現して確認できます。

2つのニューラルネットワークを作成して、データを4つのクラス(猫、ライオン、トラ、またはヒョウ)のうちの1つに分類します。キャッチは、一方はデータ拡張を使用しませんが、もう一方は使用します。データセットはこちらのリンクからダウンロードできます。

データセットをチェックアウトすると、トレーニングとテストの両方でクラスごとに50枚の画像しかないことに気付くでしょう。明らかに、分類子の1つに拡張を使用することはできません。オッズをより公平にするために、Transfer Learningを使用して、データ量の少ないモデルにチャンスを与えます。

データセットの4つのクラス。

拡張機能がない場合は、VGG19ネットワークを使用してみましょう。この実装に基づいたTensorFlow実装をここに記述しました。リポジトリのクローンを作成したら、ここからデータセットを取得でき、vgg19.npy(転送学習に使用)はここから取得できます。これで、モデルを実行してパフォーマンスを検証できます。

私は同意しますが、データ増強のために余分なコードを書くことは確かに少しの努力です。そこで、2番目のモデルを構築するために、Nanonetsに注目しました。内部的に転送学習とデータ拡張を使用して、最小限のデータを使用して最高の結果を提供します。必要なのは、ウェブサイトにデータをアップロードし、サーバーでトレーニングされるまで待機することです(通常約30分)。何を知っていますか、比較実験に最適です。

トレーニングが終了したら、APIの呼び出しをリクエストして、テストの精度を計算できます。サンプルコードスニペットのレポジトリを確認してください(コードスニペットにモデルのIDを挿入することを忘れないでください)。

結果
VGG19(拡張なし)-76%のテスト精度(最高)
ナノネット(増強あり)-94.5%のテスト精度

印象的ではありません。ほとんどのモデルがより多くのデータを使用して良好に機能することは事実です。したがって、具体的な証拠を提供するために、以下の表に言及しました。 Cifar 10(C10)およびCifar 100(C100)データセットで一般的なニューラルネットワークのエラー率を示しています。 C10 +列とC100 +列は、データ増加を伴うエラー率です。

Cifar 10およびCifar 100データセットで一般的なニューラルネットワークのエラー率。 (出典:DenseNet)

この記事を読んでくれてありがとう!拍手ボタンを押してください!データの増強について少しでも光を当てることを願っています。ご質問がある場合は、ソーシャルメディアで私に連絡するか、メール(bharathrajn98@gmail.com)を送信してください。

Nanonetsについて:Nanonetsは、開発者向けの深層学習を簡素化するAPIを構築しています。詳細については、https://www.nanonets.comをご覧ください)