KerasによるSSDでのエラー
Q&A
Closed
解決したいこと
現在出ているエラーを解決して、学習を終わらせたい。
Keras-SSDで転移学習-顔検出-
上記の記事を参考にしてSSDを行いたいと思い、Google Colaboratoryを用いて書いてある通りに行っていったが、学習のところでうまくいかず、後述するエラーが発生してしまった。
作業手順としては
1. Google Colaboratoryを使えるようにした
2. Driveのマウントを行い、MyDrive下にColab Notebooksフォルダを作成
3. そこに移動して、'git clone'を行い、ssd_kerasフォルダをダウンロード
4. FDDB : MainからFDDB-foldsフォルダとoriginalPicsフォルダを、MEGAよりweights_SSD300.hdf5フォルダをダウンロードして、Driveのssd_kerasフォルダへ
5. Google Colaboratoryの「編集」⇨「ノートブックの設定」⇨「ハードウェア アクセラレータ」を「GPU」にして保存
6. ssd_kerasフォルダに移動し、あとは上記の記事のパッケージの節から、掲載されているコードを順に実行していった
7. すると学習の部分でエラーが発生した
環境
Google Colaboratory
Python 3.7.12
Keras 1.2.2
tensorflow-gpu 1.14.0
Numpy 1.19.5
scipy 1.1.0
lxml 4.2.6
opencv-python 4.1.2.30
発生している問題・エラー
WARNING:tensorflow:From /content/drive/MyDrive/Colab Notebooks/ssd_keras/ssd_utils.py:30: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.
WARNING:tensorflow:From /content/drive/MyDrive/Colab Notebooks/ssd_keras/ssd_utils.py:35: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.
WARNING:tensorflow:From /content/drive/MyDrive/Colab Notebooks/ssd_keras/ssd_utils.py:35: The name tf.ConfigProto is deprecated. Please use tf.compat.v1.ConfigProto instead.
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:634: calling RandomUniform.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:491: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:2866: The name tf.nn.max_pool is deprecated. Please use tf.nn.max_pool2d instead.
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:2531: calling l2_normalize (from tensorflow.python.ops.nn_impl) with dim is deprecated and will be removed in a future version.
Instructions for updating:
dim is deprecated, use axis instead
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:1047: calling reduce_prod_v1 (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.
Instructions for updating:
keep_dims is deprecated, use keepdims instead
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:993: calling reduce_max_v1 (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.
Instructions for updating:
keep_dims is deprecated, use keepdims instead
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:1029: calling reduce_sum_v1 (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.
Instructions for updating:
keep_dims is deprecated, use keepdims instead
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:112: The name tf.get_default_session is deprecated. Please use tf.compat.v1.get_default_session instead.
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/backend/tensorflow_backend.py:269: The name tf.global_variables is deprecated. Please use tf.compat.v1.global_variables instead.
WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/keras/optimizers.py:658: The name tf.train.Optimizer is deprecated. Please use tf.compat.v1.train.Optimizer instead.
WARNING:tensorflow:From /content/drive/MyDrive/Colab Notebooks/ssd_keras/ssd_training.py:87: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.
WARNING:tensorflow:From /content/drive/MyDrive/Colab Notebooks/ssd_keras/ssd_training.py:50: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where
WARNING:tensorflow:From /content/drive/MyDrive/Colab Notebooks/ssd_keras/ssd_training.py:111: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.
/device:GPU:0
/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:173: DeprecationWarning: `imread` is deprecated!
`imread` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.
Use ``imageio.imread`` instead.
Epoch 1/100
/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:177: DeprecationWarning: `imresize` is deprecated!
`imresize` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.
Use ``skimage.transform.resize`` instead.
1200/1206 [============================>.] - ETA: 1s - loss: 1.6244/usr/local/lib/python3.7/dist-packages/keras/engine/training.py:1573: UserWarning: Epoch comprised more than `samples_per_epoch` samples, which might affect learning results. Set `samples_per_epoch` correctly to avoid this warning.
warnings.warn('Epoch comprised more than '
/device:GPU:0
/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:198: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
Exception in thread Thread-13:
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 926, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/lib/python3.7/dist-packages/keras/engine/training.py", line 429, in data_generator_task
generator_output = next(self._generator)
File "<timed exec>", line 198, in generate
ValueError: could not broadcast input array from shape (300,300,3) into shape (300,300)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-4-913007b18986> in <module>()
----> 1 get_ipython().run_cell_magic('time', '', "os.makedirs('checkpoints', exist_ok=True)#学習途中の重みも保存するためのフォルダ作成\ncheckpoints_dir='/content/drive/My Drive/Colab Notebooks/ssd_keras/checkpoints'\n\nnp.set_printoptions(suppress=True)\n\nNUM_CLASSES = 2\ninput_shape = (300, 300, 3)\n\n# 計算済みのデフォルトボックスの位置を保存したprior_boxes_ssd300を読み込む\n# SSDは画像上に大きさや形の異なるデフォルトボックスを乗せ、その枠ごとに予測値を計算\n# VGGやResNetのような画像分類で大きな成果をあげたネットワーク構造を用いて画像から特徴マップを抽出\n# そして特徴マップの位置毎に候補を用意(SSD論文ではdefault boxと呼ばれている)\npriors = pickle.load(open('prior_boxes_ssd300.pkl', 'rb'))\n# バウンティボックス\nbbox_util = BBoxUtility(NUM_CLASSES, priors)\n\n# FDDBデータの情報まとめたファイル\ngt = pickle.load(open('FDDB.pkl', 'rb'))\nkeys = sorted(gt.keys())\nnum_train = int(round(0.8 * len(keys)))\ntrain_keys = keys[:num_train]\nval_keys = keys[num_train:]\nnum_val = len(val_keys)\n\nclass Generator(object):# pkl上にpathも含めているのでpath_prefixは消した\n def __init__(self, gt, bbox_util,\n batch_size,\n train_keys, val_keys, image_size,\n saturation_var=0.5,\n brightness_var=0.5,\n contrast_var=0.5,\n lighting_std=0.5,\n hflip_prob=0.5,\n vflip_prob=0.5,\n do_crop=True,\n crop_area_range=[0.75, 1.0],\n aspect_ratio_range=[3./4., 4./3.]):\n self.gt = g...
4 frames
<decorator-gen-53> in time(self, line, cell, local_ns)
<timed exec> in <module>()
/usr/local/lib/python3.7/dist-packages/keras/engine/training.py in evaluate_generator(self, generator, val_samples, max_q_size, nb_worker, pickle_safe)
1664 raise ValueError('output of generator should be a tuple '
1665 '(x, y, sample_weight) '
-> 1666 'or (x, y). Found: ' + str(generator_output))
1667 if len(generator_output) == 2:
1668 x, y = generator_output
ValueError: output of generator should be a tuple (x, y, sample_weight) or (x, y). Found: None
該当するソースコード
%%time
os.makedirs('checkpoints', exist_ok=True)#学習途中の重みも保存するためのフォルダ作成
checkpoints_dir='/content/drive/My Drive/Colab Notebooks/ssd_keras/checkpoints'
np.set_printoptions(suppress=True)
NUM_CLASSES = 2
input_shape = (300, 300, 3)
# 計算済みのデフォルトボックスの位置を保存したprior_boxes_ssd300を読み込む
# SSDは画像上に大きさや形の異なるデフォルトボックスを乗せ、その枠ごとに予測値を計算
# VGGやResNetのような画像分類で大きな成果をあげたネットワーク構造を用いて画像から特徴マップを抽出
# そして特徴マップの位置毎に候補を用意(SSD論文ではdefault boxと呼ばれている)
priors = pickle.load(open('prior_boxes_ssd300.pkl', 'rb'))
# バウンティボックス
bbox_util = BBoxUtility(NUM_CLASSES, priors)
# FDDBデータの情報まとめたファイル
gt = pickle.load(open('FDDB.pkl', 'rb'))
keys = sorted(gt.keys())
num_train = int(round(0.8 * len(keys)))
train_keys = keys[:num_train]
val_keys = keys[num_train:]
num_val = len(val_keys)
class Generator(object):# pkl上にpathも含めているのでpath_prefixは消した
def __init__(self, gt, bbox_util,
batch_size,
train_keys, val_keys, image_size,
saturation_var=0.5,
brightness_var=0.5,
contrast_var=0.5,
lighting_std=0.5,
hflip_prob=0.5,
vflip_prob=0.5,
do_crop=True,
crop_area_range=[0.75, 1.0],
aspect_ratio_range=[3./4., 4./3.]):
self.gt = gt
self.bbox_util = bbox_util
self.batch_size = batch_size
self.train_keys = train_keys
self.val_keys = val_keys
self.train_batches = len(train_keys)
self.val_batches = len(val_keys)
self.image_size = image_size
self.color_jitter = []
if saturation_var:
self.saturation_var = saturation_var
self.color_jitter.append(self.saturation)
if brightness_var:
self.brightness_var = brightness_var
self.color_jitter.append(self.brightness)
if contrast_var:
self.contrast_var = contrast_var
self.color_jitter.append(self.contrast)
self.lighting_std = lighting_std
self.hflip_prob = hflip_prob
self.vflip_prob = vflip_prob
self.do_crop = do_crop
self.crop_area_range = crop_area_range
self.aspect_ratio_range = aspect_ratio_range
# 画像のチャンネル数変えたり明度変えたりして学習用の画像を増やしているのかな??関数群
def grayscale(self, rgb):
return rgb.dot([0.299, 0.587, 0.114])
def saturation(self, rgb):
gs = self.grayscale(rgb)
alpha = 2 * np.random.random() * self.saturation_var
alpha += 1 - self.saturation_var
rgb = rgb * alpha + (1 - alpha) * gs[:, :, None]
return np.clip(rgb, 0, 255)
def brightness(self, rgb):
alpha = 2 * np.random.random() * self.brightness_var
alpha += 1 - self.saturation_var
rgb = rgb * alpha
return np.clip(rgb, 0, 255)
def contrast(self, rgb):
gs = self.grayscale(rgb).mean() * np.ones_like(rgb)
alpha = 2 * np.random.random() * self.contrast_var
alpha += 1 - self.contrast_var
rgb = rgb * alpha + (1 - alpha) * gs
return np.clip(rgb, 0, 255)
def lighting(self, img):
cov = np.cov(img.reshape(-1, 3) / 255.0, rowvar=False)
eigval, eigvec = np.linalg.eigh(cov)
noise = np.random.randn(3) * self.lighting_std
noise = eigvec.dot(eigval * noise) * 255
img += noise
return np.clip(img, 0, 255)
def horizontal_flip(self, img, y):
if np.random.random() < self.hflip_prob:
img = img[:, ::-1]
y[:, [0, 2]] = 1 - y[:, [2, 0]]
return img, y
def vertical_flip(self, img, y):
if np.random.random() < self.vflip_prob:
img = img[::-1]
y[:, [1, 3]] = 1 - y[:, [3, 1]]
return img, y
# 画像のアスペクト比変えたりして、それに合うようにBBOXも変えている感じ??
def random_sized_crop(self, img, targets):
img_w = img.shape[1]
img_h = img.shape[0]
img_area = img_w * img_h
random_scale = np.random.random()
random_scale *= (self.crop_area_range[1] -
self.crop_area_range[0])
random_scale += self.crop_area_range[0]
target_area = random_scale * img_area
random_ratio = np.random.random()
random_ratio *= (self.aspect_ratio_range[1] -
self.aspect_ratio_range[0])
random_ratio += self.aspect_ratio_range[0]
w = np.round(np.sqrt(target_area * random_ratio))
h = np.round(np.sqrt(target_area / random_ratio))
if np.random.random() < 0.5:
w, h = h, w
w = min(w, img_w)
w_rel = w / img_w
w = int(w)
h = min(h, img_h)
h_rel = h / img_h
h = int(h)
x = np.random.random() * (img_w - w)
x_rel = x / img_w
x = int(x)
y = np.random.random() * (img_h - h)
y_rel = y / img_h
y = int(y)
img = img[y:y+h, x:x+w]
new_targets = []
for box in targets:
cx = 0.5 * (box[0] + box[2])
cy = 0.5 * (box[1] + box[3])
if (x_rel < cx < x_rel + w_rel and
y_rel < cy < y_rel + h_rel):
xmin = (box[0] - x_rel) / w_rel
ymin = (box[1] - y_rel) / h_rel
xmax = (box[2] - x_rel) / w_rel
ymax = (box[3] - y_rel) / h_rel
xmin = max(0, xmin)
ymin = max(0, ymin)
xmax = min(1, xmax)
ymax = min(1, ymax)
box[:4] = [xmin, ymin, xmax, ymax]
new_targets.append(box)
new_targets = np.asarray(new_targets).reshape(-1, targets.shape[1])
return img, new_targets
# 上の関数群使って学習させる画像を生成する関数かな??前処理とかも実施している
def generate(self, train=True):
print(tf.test.gpu_device_name())
while True:
# 1epochごとに画像の順番をシャッフル
if train:# 学習の時は学習用ファイルを使う
shuffle(self.train_keys)
keys = self.train_keys
else:
shuffle(self.val_keys)
keys = self.val_keys
inputs = []
targets = []
for key in keys:
img_path = key
#print(img_path)
img = imread(img_path).astype('float32')
y = self.gt[key].copy()
if train and self.do_crop:
img, y = self.random_sized_crop(img, y)
img = imresize(img, self.image_size).astype('float32')
# boxの位置は正規化されているから画像をリサイズしても
# 教師信号としては問題ない らしいです
if train:
shuffle(self.color_jitter)
for jitter in self.color_jitter:
if len(img.shape) == 2:#グレイスケール画像の場合エラーが出るので3チャンネルの画像に変換する
img=cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
img = jitter(img)
if self.lighting_std:
img = self.lighting(img)
if self.hflip_prob > 0:
img, y = self.horizontal_flip(img, y)
if self.vflip_prob > 0:
img, y = self.vertical_flip(img, y)
# 訓練データ生成時にbbox_utilを使っているのはここだけらしい
y = self.bbox_util.assign_boxes(y)
inputs.append(img)
targets.append(y)
# 1 iter 分終わったら返す
if len(targets) == self.batch_size:
tmp_inp = np.array(inputs)
tmp_targets = np.array(targets)
inputs = []
targets = []
# 前処理。preprocess_input()の中ではモデルによって画像の正規化、ImageNetデータセットのRGB各チャンネルごとの平均値を引く、などの処理が行われているようです。
# VGG16では画像をRGBからBGRに変換し、スケーリングせずにImageNetデータセットに対して各カラーチャネルをゼロ中心にします。
yield preprocess_input(tmp_inp), tmp_targets
gen = Generator(gt, bbox_util, 16,
train_keys, val_keys,
(input_shape[0], input_shape[1]), do_crop=False)
# 学習済みモデル読み込み
model = SSD300(input_shape, num_classes=NUM_CLASSES)
model.load_weights('weights_SSD300.hdf5', by_name=True)
# 再学習しないレイヤー
freeze = ['input_1', 'conv1_1', 'conv1_2', 'pool1',
'conv2_1', 'conv2_2', 'pool2',
'conv3_1', 'conv3_2', 'conv3_3', 'pool3']#,
# 'conv4_1', 'conv4_2', 'conv4_3', 'pool4']
# 再学習しないように設定
for L in model.layers:
if L.name in freeze:
L.trainable = False
def schedule(epoch, decay=0.9):
return base_lr * decay**(epoch)
# driveの容量が危ないので5 epochずつ保存することにする
callbacks = [keras.callbacks.ModelCheckpoint(checkpoints_dir + '/weights.{epoch:02d}-{val_loss:.2f}.hdf5',
verbose=1,monitor='val_loss',save_best_only=True,mode='min',
save_weights_only=True,period=5),
keras.callbacks.LearningRateScheduler(schedule)]
base_lr = 3e-4
optim = keras.optimizers.Adam(lr=base_lr)
# neg_pos_ratio:ハードネガティブマイニング負例と正例の最大の比
model.compile(optimizer=optim,
loss=MultiboxLoss(NUM_CLASSES, neg_pos_ratio=2.0).compute_loss)
nb_epoch = 100
# 学習
# ミニバッチごとに入力の前処理を行うのでfit_generatorを使う
history = model.fit_generator(gen.generate(True), gen.train_batches,
nb_epoch, verbose=1,
callbacks=callbacks,
validation_data=gen.generate(False),
nb_val_samples=gen.val_batches,
nb_worker=1)
model.save('FDDB_LEARN.h5')
with open('FDDB_HISTORY.dat', 'wb') as file_pi:
pickle.dump(history.history, file_pi)
自分で試したこと
ERROR: Could not find a version that satisfies the requirement opencv-python==3.1.0.5 (from versions: 3.4.0.14, 3.4.2.17, 3.4.3.18, 3.4.4.19, 3.4.5.20, 3.4.6.27, 3.4.7.28, 3.4.8.29, 3.4.9.31, 3.4.9.33, 3.4.10.35, 3.4.10.37, 3.4.11.39, 3.4.11.41, 3.4.11.43, 3.4.11.45, 3.4.13.47, 3.4.14.51, 3.4.14.53, 3.4.15.55, 3.4.16.57, 3.4.16.59, 3.4.17.61, 4.0.0.21, 4.0.1.23, 4.0.1.24, 4.1.0.25, 4.1.1.26, 4.1.2.30, 4.2.0.32, 4.2.0.34, 4.3.0.36, 4.3.0.38, 4.4.0.40, 4.4.0.42, 4.4.0.44, 4.4.0.46, 4.5.1.48, 4.5.2.52, 4.5.2.54, 4.5.3.56, 4.5.4.58, 4.5.4.60, 4.5.5.62)
ERROR: No matching distribution found for opencv-python==3.1.0.5
「!pip install opencv-python==3.1.0.5」を実行したときに起きた、上記のエラーが関係しているのかもしれないと考えたが、どのバージョンを入れるべきかわからなかった。また、エラー箇所の後に「print(tmp_inp.shape)」を追加してみたら、(16, 300, 300, 3)と表示され続けた。