【问题标题】:How to Augment the Training Set using the tf.keras.utils.Sequence API?如何使用 tf.keras.utils.Sequence API 增强训练集?
【发布时间】:2020-09-11 17:04:51
【问题描述】:

TensorFlow 文档有以下示例,可以说明当训练集太大而无法放入内存时,如何创建批处理生成器以将训练集批量输入模型:

from skimage.io import imread
from skimage.transform import resize
import tensorflow as tf
import numpy as np
import math

# Here, `x_set` is list of path to the images
# and `y_set` are the associated classes.

class CIFAR10Sequence(tf.keras.utils.Sequence):

    def __init__(self, x_set, y_set, batch_size):
        self.x, self.y = x_set, y_set
        self.batch_size = batch_size

    def __len__(self):
        return math.ceil(len(self.x) / self.batch_size)

    def __getitem__(self, idx):
        batch_x = self.x[idx * self.batch_size:(idx + 1) *
        self.batch_size]
        batch_y = self.y[idx * self.batch_size:(idx + 1) *
        self.batch_size]

        return np.array([
            resize(imread(file_name), (200, 200))
               for file_name in batch_x]), np.array(batch_y)

我的目的是通过将每张图像旋转 3 倍 90º 来进一步增加训练集的多样性。在训练过程的每个 Epoch 中,模型将首先输入“0º 训练集”,然后分别输入 90º、180º 和 270º 旋转集。

如何修改前面的代码以在CIFAR10Sequence() 数据生成器中执行此操作?

请不要使用tf.keras.preprocessing.image.ImageDataGenerator(),这样答案就不会失去对性质不同的另一类类似问题的普遍性。

注意:我们的想法是在输入模型时“实时”创建新数据,而不是(预先)创建并在磁盘上存储比原始训练集更大的新的增强训练集以供以后使用(也分批)在模型的训练过程中。

提前谢谢

【问题讨论】:

  • 你的图片是存放在以标签名作为目录名的目录吗?
  • 是的,我的图像存储在目录中,标签名称为目录名称。
  • 我不确定您在问题中的最终评论是什么意思。您能否详细说明为什么ImageDataGenerator() 不适合您的情况?
  • 我认为ImageDataGenerator() 可能适合这种特殊情况。但是,我想知道如何通过修改前面的代码来了解如何使用不同性质的数据(例如立方体积和更复杂的旋转类型)执行类似的过程。
  • 处理大数据和稀缺的计算资源是全球许多数据科学家面临的问题。正是在这种情况下,“批处理生成器”才能发挥作用。它们可以帮助数据科学家处理太大而无法容纳内存的数据集,并且仍然可以用于数据增强(从而提高模型的准确性),而无需更多的内存或磁盘空间。这绝对是一个有趣的话题。

标签: python tensorflow keras


【解决方案1】:

使用自定义Callback 并挂钩到on_epoch_end。在每个 epoch 结束后改变数据迭代器对象的角度。

示例(内嵌文档)

from skimage.io import imread
from skimage.transform import resize, rotate
import numpy as np

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from keras.utils import Sequence 
from keras.models import Sequential
from keras.layers import Conv2D, Activation, Flatten, Dense

# Model architecture  (dummy)
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(15, 15, 4)))
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

# Data iterator 
class CIFAR10Sequence(Sequence):
    def __init__(self, filenames, labels, batch_size):
        self.filenames, self.labels = filenames, labels
        self.batch_size = batch_size
        self.angles = [0,90,180,270]
        self.current_angle_idx = 0

    # Method to loop throught the available angles
    def change_angle(self):
      self.current_angle_idx += 1
      if self.current_angle_idx >= len(self.angles):
        self.current_angle_idx = 0
  
    def __len__(self):
        return int(np.ceil(len(self.filenames) / float(self.batch_size)))

    # read, resize and rotate the image and return a batch of images
    def __getitem__(self, idx):
        angle = self.angles[self.current_angle_idx]
        print (f"Rotating Angle: {angle}")

        batch_x = self.filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]
        return np.array([
            rotate(resize(imread(filename), (15, 15)), angle)
               for filename in batch_x]), np.array(batch_y)

# Custom call back to hook into on epoch end
class CustomCallback(keras.callbacks.Callback):
    def __init__(self, sequence):
      self.sequence = sequence

    # after end of each epoch change the rotation for next epoch
    def on_epoch_end(self, epoch, logs=None):
      self.sequence.change_angle()               


# Create data reader
sequence = CIFAR10Sequence(["f1.PNG"]*10, [0, 1]*5, 8)
# fit the model and hook in the custom call back
model.fit(sequence, epochs=10, callbacks=[CustomCallback(sequence)])

输出:

Rotating Angle: 0
Epoch 1/10
Rotating Angle: 0
Rotating Angle: 0
2/2 [==============================] - 2s 755ms/step - loss: 1.0153 - accuracy: 0.5000
Epoch 2/10
Rotating Angle: 90
Rotating Angle: 90
2/2 [==============================] - 0s 190ms/step - loss: 0.6975 - accuracy: 0.5000
Epoch 3/10
Rotating Angle: 180
Rotating Angle: 180
2/2 [==============================] - 2s 772ms/step - loss: 0.6931 - accuracy: 0.5000
Epoch 4/10
Rotating Angle: 270
Rotating Angle: 270
2/2 [==============================] - 0s 197ms/step - loss: 0.6931 - accuracy: 0.5000
Epoch 5/10
Rotating Angle: 0
Rotating Angle: 0
2/2 [==============================] - 0s 189ms/step - loss: 0.6931 - accuracy: 0.5000
Epoch 6/10
Rotating Angle: 90
Rotating Angle: 90
2/2 [==============================] - 2s 757ms/step - loss: 0.6932 - accuracy: 0.5000
Epoch 7/10
Rotating Angle: 180
Rotating Angle: 180
2/2 [==============================] - 2s 757ms/step - loss: 0.6931 - accuracy: 0.5000
Epoch 8/10
Rotating Angle: 270
Rotating Angle: 270
2/2 [==============================] - 2s 761ms/step - loss: 0.6932 - accuracy: 0.5000
Epoch 9/10
Rotating Angle: 0
Rotating Angle: 0
2/2 [==============================] - 1s 744ms/step - loss: 0.6932 - accuracy: 0.5000
Epoch 10/10
Rotating Angle: 90
Rotating Angle: 90
2/2 [==============================] - 0s 192ms/step - loss: 0.6931 - accuracy: 0.5000
<tensorflow.python.keras.callbacks.History at 0x7fcbdf8bcdd8>

【讨论】:

  • 但是我怎样才能创建一个在 epoch 结束时运行的函数,它改变角度值并在同一 epoch 上继续训练(不改变到下一个 epoch)?
  • 尽管这不是我想要的解决方案,但我必须认识到您的解决方案也解决了我提出的问题。所以,我接受了你的回答。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2020-01-03
  • 2019-10-06
  • 2016-07-23
  • 1970-01-01
  • 1970-01-01
  • 2017-02-27
  • 1970-01-01
  • 2012-08-27
相关资源
最近更新 更多