Как мне создать регрессионную модель с несколькими выходами в tf.keras?

Я пытаюсь обучить регрессионную модель предсказывать атрибуты музыки, такие как BPM. Модель принимает спектрограммы звуковых фрагментов, которые являются файлами PNG размером 256x128px, и выводит несколько непрерывных значений. У меня есть следующий код, который я разработал на основе этого руководства на сайт tensorflow:

import tensorflow as tf
import os
import random
import pathlib

AUTOTUNE = tf.data.experimental.AUTOTUNE
TRAINING_DATA_DIR = r'specgrams'

def gen_model():
    model = tf.keras.models.Sequential([
      tf.keras.layers.Flatten(input_shape=(256, 128, 3)),
      tf.keras.layers.Dense(256, activation='relu'),
      tf.keras.layers.Dense(2)
    ])

    model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
                  loss='mse',
                  metrics=['mse', 'mae'])

    return model


def fetch_batch(batch_size=1000):
    all_image_paths = []
    all_image_labels = []

    data_root = pathlib.Path(TRAINING_DATA_DIR)
    files = data_root.iterdir()

    for file in files:
        file = str(file)
        all_image_paths.append(os.path.abspath(file))
        label = file[:-4].split('-')[2:]
        label = float(label[0]) / 200, int(label[1]) / 1000.0
        all_image_labels.append(label)

    def preprocess_image(path):
        img_raw = tf.io.read_file(path)
        image = tf.image.decode_png(img_raw, channels=3)
        image = tf.image.resize(image, [256, 128])
        image /= 255.0
        return image

    def preprocess(path, label):
        return preprocess_image(path), label

    path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
    image_ds = path_ds.map(preprocess_image, num_parallel_calls=AUTOTUNE)
    label_ds = tf.data.Dataset.from_tensor_slices(all_image_labels)
    ds = tf.data.Dataset.zip((image_ds, label_ds))
    ds = ds.shuffle(buffer_size=len(os.listdir(TRAINING_DATA_DIR)))
    ds = ds.repeat()
    ds = ds.batch(batch_size)
    ds = ds.prefetch(buffer_size=AUTOTUNE)

    return ds

ds = fetch_batch()
model = gen_model()
model.fit(ds, epochs=1, steps_per_epoch=10)

Однако я считаю, что допустил ошибку со структурой моей модели или с тем, как я предварительно обрабатываю данные обучения, потому что я получаю ошибку о неправильных размерах, но я изо всех сил пытаюсь сузить, где именно проблема. Я понимаю, что руководство, которому я следовал, касалось проблемы классификации, а не регрессии, и мои «метки» представляют собой массив из двух значений, что и является причиной проблемы, но я не уверен, как решить эту проблему.

Для контекста имена файлов имеют формат xxx-xxx-A-B.png, где A и B - два желаемых выходных значения модели. A - это значение с плавающей запятой где-то между 70 и 180, а B - целочисленное значение от 0 до 1000. Таким образом, переменная label для каждого изображения выглядит примерно так: (0.64, 0.319).

Это ошибка, которую я вижу, когда пытаюсь выполнить приведенный выше сценарий:

Traceback (most recent call last):
  File "C:\Users\cainy\Desktop\BeatNet\training.py", line 60, in <module>
    model.fit(ds, epochs=1, steps_per_epoch=3)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\keras\engine\training.py", line 791, in fit
    initial_epoch=initial_epoch)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\keras\engine\training.py", line 1515, in fit_generator
    steps_name='steps_per_epoch')
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\keras\engine\training_generator.py", line 257, in model_iteration
    batch_outs = batch_function(*batch_data)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\keras\engine\training.py", line 1259, in train_on_batch
    outputs = self._fit_function(ins)  # pylint: disable=not-callable
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\keras\backend.py", line 3217, in __call__
    outputs = self._graph_fn(*converted_inputs)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\eager\function.py", line 558, in __call__
    return self._call_flat(args)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\eager\function.py", line 627, in _call_flat
    outputs = self._inference_function.call(ctx, args)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\eager\function.py", line 415, in call
    ctx=ctx)
  File "C:\Users\cainy\AppData\Local\Programs\Python\Python37\lib\site-packages\tensorflow\python\eager\execute.py", line 66, in quick_execute
    six.raise_from(core._status_to_exception(e.code, message), None)
  File "<string>", line 3, in raise_from
tensorflow.python.framework.errors_impl.InvalidArgumentError: Can not squeeze dim[1], expected a dimension of 1, got 2
     [[{{node metrics/accuracy/Squeeze}}]] [Op:__inference_keras_scratch_graph_734]

Изменить: я загрузил исходный код на GitHub здесь.


person cainy393    schedule 27.04.2019    source источник


Ответы (1)


В настоящее время у вас есть только 1 выход - тензор длиной 2 (для каждого элемента пакета). Если вы хотите использовать / отслеживать отдельные потери, вам необходимо разложить их как в выводе модели, так и на этикетках.

Не уверен, подойдет ли models.Sequential, но вы определенно можете использовать функциональный API:

def gen_model():
    inputs = tf.keras.layers.Input(shape=(256, 128, 3), dtype=tf.float32)
    x = inputs
    x = tf.keras.layers.Dense(256, activation='relu')
    x = tf.keras.layers.Dense(2)
    a, b = tf.keras.layers.Lambda(tf.unstack, arguments=dict(axis=-1))(x)
    model = tf.keras.models.Model(inputs=inputs, outputs=[a, b])
    model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
                  loss=['mse', 'mae'],
                  metrics=[['mse'], ['mae']])
    return model

И в вашей предварительной обработке:

def preprocess(path, label):
    return preprocess_image(path), tf.unstack(label, axis=-1)
person DomJack    schedule 28.04.2019
comment
Да, у меня есть еще один вопрос по этому поводу. Я понял, что моя метрика потерь должна иметь вид mse. Однако я вообще не могу запустить эту модель, потому что я не знаю, как вывести 2 значения, не имея 2 отдельных моделей для каждого значения. - person cainy393; 28.04.2019
comment
ответ обновлен. Не могу проверить это без ваших данных, поэтому опубликуйте здесь любые ошибки :) - person DomJack; 28.04.2019
comment
Ах, спасибо! Это выглядит многообещающим, я займусь исследованием. Меня беспокоят проблемы с авторским правом при распространении музыки, но я могу загрузить несколько сгенерированных мною образцов спектрограмм. Вот zip-файл с тысячей изображений :) - person cainy393; 28.04.2019