Keras:如何为验证集随机取样?

5

我目前正在训练一个Keras模型,其对应的fit调用如下:

model.fit(X,y_train,batch_size=myBatchSize,epochs=myAmountOfEpochs,validation_split=0.1,callbacks=myCallbackList)

这个评论在Keras Github页面上解释了"validation_split=0.1"的含义:

验证数据不一定来自每个类别,只是数据的最后10%(假设您要求10%)。

我的问题是:是否有一种简单的方法可以随机选择,例如,将我的训练数据的10%作为验证数据?我想使用随机选择的样本的原因是,在我的情况下,数据的最后10%并不一定包含所有类别。

非常感谢。


你可以使用numpy手动采样您的验证数据,然后将其传递给Keras。 - Dr. Snoopy
我知道这种方法,并且已经在这里看到过了。然而,如果Keras提供这样的功能,我正在寻找一个简单的内置解决方案。 - Hagbard
2
不,Keras没有提供这样的功能。 - Dr. Snoopy
好的,非常感谢您的回答。在这种情况下,我将自己实现它。 - Hagbard
6个回答

3

Keras没有提供比仅仅从训练数据中取出一部分用于验证更高级的功能。如果您需要更高级的功能,例如分层抽样以确保样本中各类别得到充分代表,则需要在Keras之外手动执行此操作(例如使用scikit-learn或numpy),然后通过model.fit中的validation_data参数将该验证数据传递给Keras。


2

感谢Matias Valdenegro的评论,我受到启发并想出了以下解决方案来解决我的问题:

from sklearn.model_selection import train_test_split
[input: X and Y]
XTraining, XValidation, YTraining, YValidation = train_test_split(X,Y,stratify=Y,test_size=0.1) # before model building
[The model is built here...]
model.fit(XTraining,YTraining,batch_size=batchSize,epochs=amountOfEpochs,validation_data=(XValidation,YValidation),callbacks=callbackList)

3
请使用随机种子来确保每次生成相同的测试和验证数据。 train_test_split(X,Y,stratify=Y,test_size=0.1, random_state=0) #您可以使用任何整数作为种子。 - priojeet priyom

1
这篇帖子中,我建议使用split-folders包将主数据目录随机分成训练和验证目录,同时保留类别子文件夹。然后,您可以使用keras的.flow_from_directory方法指定您的训练和验证路径。
从文档中分离您的文件夹:
import split_folders

# Split with a ratio.
# To only split into training and validation set, set a tuple to `ratio`, i.e, `(.8, .2)`.
split_folders.ratio('input_folder', output="output", seed=1337, ratio=(.8, .1, .1)) # default values

# Split val/test with a fixed number of items e.g. 100 for each set.
# To only split into training and validation set, use a single number to `fixed`, i.e., `10`.
split_folders.fixed('input_folder', output="output", seed=1337, fixed=(100, 100), oversample=False) # default values

输入文件夹应具有以下格式:
input/
    class1/
        img1.jpg
        img2.jpg
        ...
    class2/
        imgWhatever.jpg
        ...
    ...

为了给您提供这个:
output/
    train/
        class1/
            img1.jpg
            ...
        class2/
            imga.jpg
            ...
    val/
        class1/
            img2.jpg
            ...
        class2/
            imgb.jpg
            ...
    test/            # optional
        class1/
            img3.jpg
            ...
        class2/
            imgc.jpg
            ...

使用Keras的ImageDataGenerator构建训练和验证数据集:
import tensorflow as tf
import split_folders
import os

main_dir = '/Volumes/WMEL/Independent Research Project/Data/test_train/Data'
output_dir = '/Volumes/WMEL/Independent Research Project/Data/test_train/output'

split_folders.ratio(main_dir, output=output_dir, seed=1337, ratio=(.7, .3))

train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1./224)

train_generator = train_datagen.flow_from_directory(os.path.join(output_dir,'train'),
                                                    class_mode='categorical',
                                                    batch_size=32,
                                                    target_size=(224,224),
                                                    shuffle=True)

validation_generator = train_datagen.flow_from_directory(os.path.join(output_dir,'val'),
                                                        target_size=(224, 224),
                                                        batch_size=32,
                                                        class_mode='categorical',
                                                        shuffle=True) # set as validation data

base_model = tf.keras.applications.ResNet50V2(
    input_shape=IMG_SHAPE,
    include_top=False,
    weights=None)

maxpool_layer = tf.keras.layers.GlobalMaxPooling2D()
prediction_layer = tf.keras.layers.Dense(4, activation='softmax')

model = tf.keras.Sequential([
    base_model,
    maxpool_layer,
    prediction_layer
])

opt = tf.keras.optimizers.Adam(lr=0.004)
model.compile(optimizer=opt,
              loss=tf.keras.losses.CategoricalCrossentropy(),
              metrics=['accuracy'])

model.fit(
    train_generator,
    steps_per_epoch = train_generator.samples // 32,
    validation_data = validation_generator,
    validation_steps = validation_generator.samples // 32,
    epochs = 20)

0

这并没有回答我的问题,因为我想要洗牌的是验证数据而不是训练数据。我引用Keras FAQ中的话:“如果model.fit中的shuffle参数设置为True(默认值),则每个epoch训练数据将被随机洗牌。验证数据永远不会被洗牌。” - Hagbard

0
model.fit() 的参数中,validation_data 会覆盖 validation_split,因此没有必要同时配置两者。
validation_split: Float between 0 and 1.
            Fraction of the training data to be used as validation data.
            The model will set apart this fraction of the training data,
            will not train on it, and will evaluate
            the loss and any model metrics
            on this data at the end of each epoch.

validation_data: Data on which to evaluate
            the loss and any model metrics at the end of each epoch.
            The model will not be trained on this data.
            `validation_data` will override `validation_split`

但是有一个选项可以实现您的目的,那就是参数shuffle

shuffle: Boolean (whether to shuffle the training data
            before each epoch) or str (for 'batch').
            'batch' is a special option for dealing with the
            limitations of HDF5 data; it shuffles in batch-sized chunks.

所以你可以做的是:

model.fit(**other_kwargs, validation_split = 0.1, shuffle=True)

非常感谢您的回复,但正如我之前所述(请参见上文):“这并没有回答我的问题,因为我想要洗牌验证数据而不是训练数据。我引用Keras FAQ中的话:“如果model.fit中的shuffle参数设置为True(默认值),则每个时期的训练数据将被随机洗牌。验证数据永远不会被洗牌。” - Hagbard

0

评论不够长,所以我在这里发表。

如果你有1000个训练数据、100个测试数据、validation_split=0.1和batch_size=100,它会做的是:在训练数据上进行拆分(批次1:90个训练和10个验证,批次2:90个训练和10个验证,...,所有的原始顺序,90,10,90,10...90,10),这与100个测试数据无关(你的模型永远不会看到它)。所以我猜你只想打乱所有大小为10的验证集,而不触及90大小的训练集。我可能会手动洗牌我的数据的10%部分,因为这就是shuffle=True的作用,它只是洗牌索引并用新的洗牌索引替换旧的训练数据,像这样):

import numpy as np
train_index = np.arange(1000,dtype=np.int32)
split = 0.1
batch_size = 100
num_batch = int(len(train_index)/batch_size)
train_index = np.reshape(train_index,(num_batch,batch_size))
for i in range(num_batch):
    r = np.random.choice(range(10),10,replace=False)
    print(r)
    train_index[i,int((1-split)*batch_size):] = np.array(r+((1-split)*batch_size)+i*batch_size)
    print(train_index[i])

flatten_index = train_index.reshape(-1)
print(flatten_index)

x_train = np.arange(1000,2000)
x_train = x_train[flatten_index]
print(x_train)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接