Керас: правильность модели и проблемы с пользовательской метрикой - PullRequest
0 голосов
/ 15 мая 2018

Я пытаюсь создать авто-кодер для процессов.Каждый процесс представляет собой последовательность событий, и каждое событие представляет собой число от 0 до 461 (и важно, что события с близкими числами не похожи, числа были выданы случайным образом).Каждый процесс имеет длину 60, а общее количество процессов составляет n.Поэтому мои входные данные - массив (n, 60).

. Сначала я создал слой Embedding для преобразования номеров событий в одно горячее представление:

BLOCK_LEN = 60
EVENTS_CNT = 462

input = Input(shape=(BLOCK_LEN,))
embedded = Embedding(input_dim=EVENTS_CNT+1, input_length=BLOCK_LEN, output_dim=200)(input)
emb_model = Model(input, embedded)
emb_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 60)                0         
_________________________________________________________________
embedding_1 (Embedding)      (None, 60, 200)           92600     
=================================================================
Total params: 92,600
Trainable params: 92,600
Non-trainable params: 0
_________________________________________________________________
None

Во-вторых, я создал основную модель Seq2Seq.(используя эту библиотеку ):

seq_model = Seq2Seq(batch_input_shape=(None, BLOCK_LEN, 200), hidden_dim=200, output_length=BLOCK_LEN, output_dim=EVENTS_CNT)

Результирующая модель:

model = Sequential()
model.add(emb_model)
model.add(seq_model)
model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
model_1 (Model)              (None, 60, 200)           92600     
_________________________________________________________________
model_12 (Model)             (None, 60, 462)           1077124   
=================================================================
Total params: 1,169,724
Trainable params: 1,169,724
Non-trainable params: 0
_________________________________________________________________

Также у меня есть своя собственная метрика точности (потому что точность lib не подходит для моегоdata):

def symbol_acc(y_true, y_pred):
    isEqual = K.cast(K.equal(y_true, y_pred), K.floatx())
    return K.mean(isEqual)

И компиляция:

model.compile(loss=tf.losses.sparse_softmax_cross_entropy,optimizer='adam', target_tensors=[tf.placeholder(tf.int32, [None, 60])], metrics=[symbol_acc])

Почему компиляция выглядит так: сначала модель имела еще один слой model.add(TimeDistributed(Dense(EVENTS_CNT, activation='softmax'))), а компиляция была model.compile(loss=custom_categorical_crossentropy, optimizer='rmsprop', metrics=[symbol_acc]).Но такая модель выдает ошибку «ValueError: Ошибка при проверке цели: ожидалось, что time_distributed_2 будет иметь 3 измерения, но получил массив с формой (2714, 60)».Теперь все формы подходят.

Но теперь у меня есть новая проблема (ключевой момент моей истории): формы в метрике symbol_acc разыскиваются:

Фигуры (symbol_acc): (?, 60) (?,?, 462)

Итак, массив true имеет форму (?, 60) и прогнозируется - (?, ?, 462).Каждое значение в значениях true 60 представляет собой число от 0 до 461 (представляет истинное число событий), а каждое значение в значениях predicted 60 представляет собой вектор размера 462 распределения вероятностей для каждого числа от 0 до 461 (длякаждое из 462 событий) (для каждого из 462 событий).Я хочу сделать true такой же формы, как и predicted: для каждого из 60 значений создайте вектор размером 462 с 1 на позиции номера события и 0 на других.

Итак, мои вопросы:

  1. Как изменить форму массива в метрике, если до подгонки модели у меня нет данных?Максимум, который я получил, равен K.gather(K.eye(462), tf.cast(number, tf.int32)): этот код создает массив с одним «горячим» номером 1 в позиции number.Но я не понимаю, как применить его к массиву, не зная этого массива.
  2. Может быть, есть более простой способ решить эту проблему?

Я новичок в kerasи NNs, поэтому я не уверен, что все шаги верны.Если вы видите какую-либо ошибку, пожалуйста, сообщите.

1 Ответ

0 голосов
/ 15 мая 2018

Как я тестировал ранее, использование target_tensors не будет работать, если его форма не совпадает с прогнозируемой формой модели.

Итак, это общее правило не может быть нарушено:

Ваши выходные данные должны иметь ту же форму, что и выход вашей модели

Это составляет y_trueи y_pred определенно имеют одинаковую форму.

Вам нужно адаптировать выходные данные к форме вашей модели, используя to_categorical () .

from keras.utils import to_categorical
one_hot_X = to_categorical(X_train,462)

При этом вы просто тренируете свою модель в обычном режиме, не создавая обходных путей при потерях и погрешностях:

model.fit(X_train, one_hot_X,...)

Если у вас проблемы с памятью , сделав это, вы можете рассмотреть возможность создания генератора, который будет преобразовывать только часть данных для каждой партии:

def batch_generator(batch_size):

    while True: #keras generators must be infinite

        #you may want to manually shuffle X_train here

        for i in range(len(X_train)//batch_size): #make sure len is a multiple of batch_size

            x = X_train[i*batch_size:(i+1)*batch_size]
            y = to_categorical(x,462)

            yield (x,y)

Поезд с:

model.fit_generator(batch_generator(size),....)

Исправление вашегоТочность для этого случая

Теперь, когда мы лучше знаем, что вы делаете, ваша точность должна использовать K.argmax для получения точных результатов (а не учитывать 462 варианта, в то время как он должен быть 1, правильным или нет)

(Мой старый ответ был неправильным, потому что я забыл, что y_true является точным, но y_pred является приблизительным).

def symbol_acc(y_true, y_pred):
    y_true = K.argmax(y_true) #this gets the class as an integer (comparable to X_train)
    y_pred = K.argmax(y_pred) #transforming (any,60,462) into (any,60)

    isEqual = K.cast(K.equal(y_true, y_pred), K.floatx())
    return K.mean(isEqual)

Небольшое исправление:

Вложения не создают представление «одним щелчком», они просто создают многофункциональное представление.(Одна горячая точка строго для случаев, когда только один элемент вектора равен единице, но вложения свободны для любого значения в любом элементе).

...