MNISTデータベースで通常の分類を実行しようとしていますが、数字がランダムに切り取られています。画像は次の方法でトリミングされます:最初/最後および/または行/列をランダムに削除します。
Keras(およびTensorflowバックエンド)を使用した畳み込みニューラルネットワークを使用して、畳み込みと通常の分類を実行したいと思います。
入力は可変サイズであり、それを機能させることができません。
数字を切り取る方法は次のとおりです
import numpy as np
from keras.utils import to_categorical
from sklearn.datasets import load_digits
digits = load_digits()
X = digits.images
X = np.expand_dims(X, axis=3)
X_crop = list()
for index in range(len(X)):
X_crop.append(X[index, np.random.randint(0,2):np.random.randint(7,9), np.random.randint(0,2):np.random.randint(7,9), :])
X_crop = np.array(X_crop)
y = to_categorical(digits.target)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_crop, y, train_size=0.8, test_size=0.2)
そして、これは私が使用したいモデルのアーキテクチャです
from keras.layers import Dense, Dropout
from keras.layers.convolutional import Conv2D
from keras.models import Sequential
model = Sequential()
model.add(Conv2D(filters=10,
kernel_size=(3,3),
input_shape=(None, None, 1),
data_format='channels_last'))
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
model.summary()
model.fit(X_train, y_train, epochs=100, batch_size=16, validation_data=(X_test, y_test))
誰かが私のニューラルネットワークで可変サイズの入力を処理する方法を知っていますか?
そして、分類を実行する方法は?
TL/DR-ポイント4に移動
それで-ポイントに達する前に-あなたのネットワークに関するいくつかの問題を修正しましょう:
アクティベーションのためにネットワークは機能しません:_categorical_crossentropy
_では、softmax
アクティベーションが必要です:
_model.add(Dense(10, activation='softmax'))
_
空間テンソルのベクトル化:ダニエルが述べたように-いくつかの段階で、ベクトルを空間(画像)からベクトル化(ベクトル)に切り替える必要があります。現在-Dense
を_Conv2D
_からの出力に適用することは_(1, 1)
_畳み込みと同等です。基本的に-ネットワークからの出力は空間的であり、ベクトル化されていないため、次元の不一致が発生します(ネットワークを実行するか、model.summary()
を確認することで確認できます。変更するには、 _GlobalMaxPooling2D
_ または _GlobalAveragePooling2D
_ 例:
_model.add(Conv2D(filters=10,
kernel_size=(3, 3),
input_shape=(None, None, 1),
padding="same",
data_format='channels_last'))
model.add(GlobalMaxPooling2D())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation='softmax'))
_
連結されたnumpy
配列は同じ形状である必要があります:_X_crop
_の形状をチェックすると、空間マトリックスではありません。異なる形状のマトリックスを連結したためです。悲しいことに-_numpy.array
_の形状を固定する必要があるため、この問題を克服することは不可能です。
ネットワークを異なる形状の例で訓練する方法:これを行う上で最も重要なことは、2つのことを理解することです。 1つ目-単一のバッチでは、すべての画像が同じサイズである必要があります。 2番目-fit
を複数回呼び出すのは悪い考えです-内部モデルの状態をリセットするためです。そのため、ここで何をする必要があります:
a。 単一バッチを切り取る関数を書く-例えばマトリックスを指定した_get_cropped_batches_generator
_は、バッチを切り取り、ランダムにトリミングします。
b。 _train_on_batch
_ メソッドを使用します。コードの例を次に示します。
_from six import next
batches_generator = get_cropped_batches_generator(X, batch_size=16)
losses = list()
for Epoch_nb in range(nb_of_epochs):
Epoch_losses = list()
for batch_nb in range(nb_of_batches):
# cropped_x has a different shape for different batches (in general)
cropped_x, cropped_y = next(batches_generator)
current_loss = model.train_on_batch(cropped_x, cropped_y)
Epoch_losses.append(current_loss)
losses.append(Epoch_losses.sum() / (1.0 * len(Epoch_losses))
final_loss = losses.sum() / (1.0 * len(losses))
_
したがって-上記のコードへのコメント:最初に、 _train_on_batch
_ はニースkeras
プログレスバーを使用しません。 (特定のバッチに対して)単一の損失値を返します。そのため、損失を計算するロジックを追加しました。 Progbar
コールバックも使用できます。 2番目-_get_cropped_batches_generator
_を実装する必要があります-私の答えをもう少し明確にするためのコードを書いていません。実装方法について別の質問をすることができます。最後に、six
を使用して_Python 2
_と_Python 3
_の間の互換性を保ちます。
通常、Dense
レイヤーを含むモデルは、出力も可変でない限り、可変サイズの入力を持つことはできません。しかし、回避策および_GlobalMaxPooling2D
_を使用した他の回答も参照してください-回避策は_GlobalAveragePooling2D
_と同等です。これらは、高密度レイヤーの前に可変サイズを排除し、空間次元を抑制することができるレイヤーです。
画像分類の場合、モデル外の画像のサイズを変更することができます。
画像がnumpy形式の場合、次のようにサイズを変更します。
_from PIL import Image
im = Image.fromarray(imgNumpy)
im = im.resize(newSize,Image.LANCZOS) #you can use options other than LANCZOS as well
imgNumpy = np.asarray(im)
_
なぜ?
畳み込み層には、フィルターとしての重みがあります。静的なフィルターサイズがあり、同じフィルターが何度も画像に適用されます。
ただし、密度の高いレイヤーには、入力に基づいた重みがあります。入力が1つある場合、重みのセットがあります。 2つの入力がある場合、2倍の重みがあります。ただし、ウェイトはトレーニングする必要があります。ウェイトの量を変更すると、モデルの結果が確実に変わります。
@Marcinがコメントしたように、密なレイヤーの入力形状が_(batchSize,inputFeatures)
_の2つの次元を持つ場合、私が言ったことは真実です。
しかし、実際には、ケラスの高密度レイヤーは、より多くの次元の入力を受け入れることができます。これらの追加の次元(畳み込み層から生じる)はサイズが異なる場合があります。しかし、これにより、これらの高密度レイヤーの出力のサイズも可変になります。
それにもかかわらず、最後には分類のために固定サイズが必要になります:10クラスとそれだけです。寸法を縮小するために、人々はFlatten
レイヤーを使用することが多く、ここにエラーが表示されます。
潜在的な魚の回避策(テストされていない):
モデルの畳み込み部分の最後で、ラムダ層を使用して、すべての値を固定サイズのテンソルに凝縮します。おそらく側面の平均を取り、チャネルを維持します(チャネルは可変ではありません)
最後の畳み込み層が次のとおりであると仮定します。
_model.add(Conv2D(filters,kernel_size,...))
#so its output shape is (None,None,None,filters) = (batchSize,side1,side2,filters)
_
空間次元を凝縮し、フィルター次元のみを保持するためにラムダレイヤーを追加しましょう。
_import keras.backend as K
def collapseSides(x):
axis=1 #if you're using the channels_last format (default)
axis=-1 #if you're using the channels_first format
#x has shape (batchSize, side1, side2, filters)
step1 = K.mean(x,axis=axis) #mean of side1
return K.mean(step1,axis=axis) #mean of side2
#this will result in a tensor shape of (batchSize,filters)
_
フィルターの量は固定されているため(None
ディメンションをキックアウトしました)、おそらく密なレイヤーが機能するはずです。
_model.add(Lambda(collapseSides,output_shape=(filters,)))
model.add(Dense.......)
.....
_
これがおそらく機能するためには、最後の畳み込み層のフィルターの数を少なくとも10にすることをお勧めします。
これにより、input_shape=(None,None,1)
を作成できます
これを行う場合は、バッチごとに固定サイズの入力データのみを渡すことができることに注意してください。そのため、データ全体を小さなバッチに分割する必要があります。各バッチはすべて同じサイズの画像を持ちます。こちらをご覧ください: Kerasはトレーニングデータの形状を誤って解釈します