web-dev-qa-db-ja.com

データジェネレーターをより効率的にするには?

ニューラルネットワークをトレーニングするために、YouTubeで見つけたコードを変更しました。次のようになります。

def data_generator(samples, batch_size, shuffle_data = True, resize=224):
  num_samples = len(samples)
  while True:
    random.shuffle(samples)

    for offset in range(0, num_samples, batch_size):
      batch_samples = samples[offset: offset + batch_size]

      X_train = []
      y_train = []

      for batch_sample in batch_samples:
        img_name = batch_sample[0]
        label = batch_sample[1]
        img = cv2.imread(os.path.join(root_dir, img_name))

        #img, label = preprocessing(img, label, new_height=224, new_width=224, num_classes=37)
        img = preprocessing(img, new_height=224, new_width=224)
        label = my_onehot_encoded(label)

        X_train.append(img)
        y_train.append(label)

      X_train = np.array(X_train)
      y_train = np.array(y_train)

      yield X_train, y_train

今、私はこのコードを使用してニューラルネットワークをトレーニングしようとしました。トレーニングサンプルサイズは105.000です(37の可能性のうち8文字、A〜Z、0〜9、および空白スペースを含む画像ファイル)。比較的小さいバッチサイズ(32、それはすでに小さすぎると思います)を使用してより効率的にしましたが、それでも最初のエポックの4分の1をトレーニングするのに永遠のようにかかりました(エポックごとに826ステップあり、90分かかりました) 199ステップ... steps_per_Epoch = num_train_samples // batch_size)。

データジェネレーターには次の関数が含まれています。

def shuffle_data(data):
  data=random.shuffle(data)
  return data

この関数をもっと効率的にしたり、ジェネレータから除外したりすることはできないと思います。

def preprocessing(img, new_height, new_width):
  img = cv2.resize(img,(new_height, new_width))
  img = img/255
  return img

データを前処理/サイズ変更するために、私はこのコードを使用して画像を一意のサイズにします。 (224、224、3)。ジェネレーターのこの部分は最も時間がかかると思いますが、ジェネレーターから除外する可能性はありません(バッチの外でイメージのサイズを変更すると、メモリがいっぱいになるため)。

#One Hot Encoding of the Labels
from numpy import argmax
# define input string

def my_onehot_encoded(label):
    # define universe of possible input values
    characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    # define a mapping of chars to integers
    char_to_int = dict((c, i) for i, c in enumerate(characters))
    int_to_char = dict((i, c) for i, c in enumerate(characters))
    # integer encode input data
    integer_encoded = [char_to_int[char] for char in label]
    # one hot encode
    onehot_encoded = list()
    for value in integer_encoded:
        character = [0 for _ in range(len(characters))]
        character[value] = 1
        onehot_encoded.append(character)

    return onehot_encoded 

この部分には、より効率的な方法が1つあると思います。このコードをジェネレーターから除外し、ジェネレーターの外に配列y_trainを生成することを考えています。これにより、ジェネレーターは毎回ラベルをホットエンコードする必要がなくなります。

どう思いますか?それとも、まったく異なるアプローチを取るべきでしょうか?

5
Tobitor

手がかりしか与えられていないので、あなたの質問は非常に興味深く感じました。これが私の調査です。

あなたのスニペットを使用して、私は GitHubリポジトリ およびPythonでジェネレーター関数を使用することの主に 利点について に焦点を当てたYouTubeの3部のビデオチュートリアルを見つけました。データは this kaggle に基づいています(別のCNNネットワークですでに試したアプローチと比較して、使用中のAPIを確認するために、その問題についてさまざまなカーネルを確認することをお勧めします)。

難しくはありませんが、データジェネレータを最初から作成する必要はありませんが、ホイールを発明しても生産的ではありません。

それにもかかわらず、カグルのタスクを解決するために、モデルは単一の画像のみを認識する必要があるため、モデルは単純なディープCNNです。しかし、私が理解しているように、8つのランダムな文字(クラス)を1つの画像に組み合わせて、一度に 複数のクラスを認識 にします。そのためには、モデルとしてR-CNNまたはYOLOが必要です。私は最近自分のためにオープンしました YOLO v4 、そして特定のタスクでそれを非常に迅速に動作させることが可能です。

デザインとコードに関する一般的なアドバイス。

  • ライブラリがGPUを使用していることを確認してください。それは多くの時間を節約します。 (CPUでの花の実験を非常に高速に繰り返しましたが、約10分ですが、結果の予測はランダムな推測に勝るものではありません。したがって、完全なトレーニングにはCPUで多くの時間が必要です。)
  • 異なるバージョンを比較してボトルネックを見つけます。 48個の画像(クラスごとに1つ)のデータセットを試し、クラスごとの画像数を増やして比較します。画像サイズの縮小、モデル構造の変更など。
  • 小さな人工データで新しいモデルをテストしてアイデアを証明するか、反復プロセスを使用し、タスクに変換できるプロジェクトから開始します( 手書き認識 ?)。
3
Mikolasan