3
$\begingroup$

So I define my data like this:

x_train = keras.utils.io_utils.HDF5Matrix('dataset.h5', 'x_train')
y_train = keras.utils.io_utils.HDF5Matrix('dataset.h5', 'y_train')
x_test = keras.utils.io_utils.HDF5Matrix('dataset.h5', 'x_test')
y_test = keras.utils.io_utils.HDF5Matrix('dataset.h5', 'y_test')

But then when I try to fit the model like this:

model.fit(x_train, y_train, epochs=epochs, 
          validation_data=(x_test, y_test), 
          callbacks=[tensorboard, modelcheckpoint], 
          batch_size=batch_size, shuffle=False)

I get this error:

File "G:\CryptoForecast\cryptomodel.py", line 34, in train_model
    model.fit(x_train, y_train, epochs=epochs, validation_data=(x_test, y_test), callbacks=[tensorboard, modelcheckpoint], batch_size=batch_size, shuffle=False)
  File "C:\ProgramData\Anaconda3\envs\TradingBot\lib\site-packages\keras\engine\training.py", line 952, in fit
    batch_size=batch_size)
  File "C:\ProgramData\Anaconda3\envs\TradingBot\lib\site-packages\keras\engine\training.py", line 670, in _standardize_user_data
    'You passed: x=' + str(x))
ValueError: Please provide as model inputs either a single array or a list of arrays. You passed: x=<keras.utils.io_utils.HDF5Matrix object at 0x000002342575E4E0>

But it should be working, no?

$\endgroup$
4
  • $\begingroup$ The GitHub issue of this question $\endgroup$
    – n1k31t4
    Oct 16, 2018 at 10:41
  • $\begingroup$ What version of Keras are you using? Could you show the relevant output of running conda list in terminal, using the same environment as your model. $\endgroup$
    – n1k31t4
    Oct 16, 2018 at 10:46
  • 1
    $\begingroup$ @n1k3t4 pastebin.com/V59WrZet $\endgroup$
    – Jordy
    Oct 16, 2018 at 11:58
  • $\begingroup$ So you are running the latest versions, and my analysis below looks at the latest version of the code in Keras. $\endgroup$
    – n1k31t4
    Oct 16, 2018 at 16:50

2 Answers 2

1
+100
$\begingroup$

You can see in the fit() method on your Model instance, that the input is first sent through a method called _standardize_user_data at line 643.

Source of error

Your error message comes from the checks that happen across lines 650 to 671 in that file:

if not self.built:
    # We need to use `x` to set the model inputs.
    # We type-check that `x` and `y` are either single arrays
    # or lists of arrays.
    if isinstance(x, (list, tuple)):
        if not all(isinstance(v, np.ndarray) or
                   K.is_tensor(v) for v in x):
            raise ValueError('Please provide as model inputs '
                             'either a single '
                             'array or a list of arrays. '
                             'You passed: x=' + str(x))
        all_inputs += list(x)
    elif isinstance(x, dict):
        raise ValueError('Please do not pass a dictionary '
                         'as model inputs.')
    else:
        if not isinstance(x, np.ndarray) and not K.is_tensor(x):
            raise ValueError('Please provide as model inputs '
                             'either a single '
                             'array or a list of arrays. '
                             'You passed: x=' + str(x))
        all_inputs.append(x)

They use isinstance() to check the type, and your HDF5 type is not covered anywhere.

Possible hack

I linked you GitHub issue as a comment on your post. Hpowever...

You could alter the code above in your local version of Keras to cover your case, essentially converting the received input into a NumPy array, which would then pass then checks and be used.

I would probably just enter a second elif to the conditions above, like this:

elif isinstance(x, dict):
    raise ValueError('Please do not pass a dictionary '
                     'as model inputs.')                   # original code

#### add this snippet #############    
elif isinstance(x, h5py._hl.dataset.Dataset):
    x = np.array(x)      # you might need to find a more elegant way of converting the HDF5 block to a numpy array
###################################

else:
    if not isinstance(x, np.ndarray) and not K.is_tensor(x):   # original code

You can confirm that the correct type of data for you is that h5py._hl.dataset.Dataset by checking the output of type(keras.utils.io_utils.HDF5Matrix('dataset.h5', 'x_train')).

This should get things working, although it might cost you some of the other benefits of the HDF5 loading system, such as specifying start and end indices.

Testing

Just an example to show how the above transformation should really end up with your data being fed to the model as a numpy array:

import numpy as np
import keras

a = np.arange(0, 75).reshape((5, 5, 3))    # like a 5x5 RGB image
f = h5py.File('tester.h5', 'w') 
f.create_dataset(name='a', data=a)                                     
  # output:  <HDF5 dataset "a": shape (5, 5), type "<i8">
f.close()

# Later on ...

data = h5py.File('trial.h5', 'r')
data = np.array(data['a'])

np.array_equal(data, a)
  # True

Given the documentation on the the HDF5 utility:

keras.utils.HDF5Matrix(datapath, dataset, start=0, end=None, normalizer=None) Representation of HDF5 dataset to be used instead of a Numpy array.

it does feel like there is a bug, or at least a discrepancy between the documentation and the code.

EDIT

You can fit via custom generator that would load blocks from your HDF5Matrix.

def generate_arrays_from_file(path):
    while True:
        with h5py.File(path) as f:

            for batch in f:
                # read the data and reshape as necessary
                trainX, trainY, testX, testY = split_batch(batch)
                yield (trainX, trainY)

model.fit_generator(generate_arrays_from_file('dataset.h5'),
                    steps_per_epoch=100, epochs=10)

You will have to obviously write a generator function yourself that matches you exact h5 format. Perhaps have a look at the fancy indexing options of h5 files.

$\endgroup$
3
  • $\begingroup$ The reason I'm using HDF5 is because the data set is too large for memory so converting it to a numpy array isn't an option. And that's so strange, did it use to work in older versions of keras maybe then? Odd how I'm the first one to report this exact issue. $\endgroup$
    – Jordy
    Oct 16, 2018 at 17:09
  • $\begingroup$ @Jordy - You can write your own training loop in which you read the chunks into memory and pass them to the fit method. Alternatively, you could try repurposing the flow_from_directory method on the (ImageDataGenerator)[https://keras.io/preprocessing/image/] class. It is meant for images, but you can leave out the augmentation flags and the output is just a tensor anyway. $\endgroup$
    – n1k31t4
    Oct 16, 2018 at 18:21
  • $\begingroup$ @Jordy - Check out the alternative solution in my edit. $\endgroup$
    – n1k31t4
    Oct 16, 2018 at 19:25
0
$\begingroup$

This might be a bug in Keras. However, you might have some luck if the "validation_split" parameter is set to 0.001 as mentioned on the corresponding Github page.

$\endgroup$
1
  • $\begingroup$ Didn't make any difference in my case unfortunately. $\endgroup$
    – Jordy
    Oct 16, 2018 at 12:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.