не удалось передать входной массив из формы (27839,1) в форму (27839) - PullRequest
1 голос
/ 17 июня 2020

Я создаю цепной классификатор для задачи мультикласса, которая использует в цепочке бинарную модель классификатора Keras. У меня есть 17 меток в качестве цели классификации, а форма X_train - (111300,107), а y_train - (111300,17). После обучения я получил следующую ошибку в методе прогнозирования;

        *could not broadcast input array from shape (27839,1) into shape (27839)*

Мой код здесь:

def create_model():
  input_size=length_long_sentence
  embedding_size=128
  lstm_size=64
  output_size=len(unique_tag_set)
    #----------------------------Model--------------------------------
  current_input=Input(shape=(input_size,)) 
  emb_current = Embedding(vocab_size, embedding_size, input_length=input_size)(current_input)
  out_current=Bidirectional(LSTM(units=lstm_size))(emb_current )
  #out_current = Reshape((1,2*lstm_size))(out_current)
  output = Dense(units=1, activation=  'sigmoid')(out_current)
  #output = Dense(units=1, activation='softmax')(out_current)
  model = Model(inputs=current_input, outputs=output)
  #-------------------------------compile-------------
  model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
  return model
model = KerasClassifier(build_fn=create_model, epochs=1,batch_size=256, shuffle = True, verbose = 1,validation_split=0.2)
chain=ClassifierChain(model, order='random', random_state=42)
history=chain.fit(X_train, y_train)

результат для chain.classes_ приведен ниже:

[array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8),
 array([0, 1], dtype=uint8)]

затем пытается предсказать по тестовым данным:

Y_pred_chain = chain.predict(X_test)

Сводка модели приведена ниже: enter image description here

Полная трассировка ошибки здесь :

109/109 [==============================] - 22s 202ms/step
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-28-34a25ad06cd4> in <module>()
----> 1 Y_pred_chain = chain.predict(X_test)

/usr/local/lib/python3.6/dist-packages/sklearn/multioutput.py in predict(self, X)
    523             else:
    524                 X_aug = np.hstack((X, previous_predictions))
--> 525             Y_pred_chain[:, chain_idx] = estimator.predict(X_aug)
    526 
    527         inv_order = np.empty_like(self.order_)

ValueError: could not broadcast input array from shape (27839,1) into shape (27839)

Кто-нибудь может помочь, как исправить эту ошибку?

Ответы [ 2 ]

1 голос
/ 26 июня 2020

Этап 1

Следуя сводке модели, опубликованной в вопросе, я начинаю с того, что размер ввода 107, а размер вывода 1 (задача двоичной классификации)

Давайте разберемся на части и разберемся.

Архитектура модели

input_size = 107    
# define the model
def create_model():
  global input_size
  embedding_size=128
  lstm_size=64
  output_size=1
  vocab_size = 100

  current_input=Input(shape=(input_size,)) 
  emb_current = Embedding(vocab_size, embedding_size, input_length=input_size)(current_input)
  out_current=Bidirectional(LSTM(units=lstm_size))(emb_current )
  output = Dense(units=output_size, activation=  'sigmoid')(out_current)
  model = Model(inputs=current_input, outputs=output)
  model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
  return model

Некоторые фиктивные данные

X = np.random.randint(0,100,(111, 107))
y = np.random.randint(0,2,(111,1))  # NOTE: The y should have two dimensions

Давайте протестируем модель keras напрямую

model = KerasClassifier(build_fn=create_model, epochs=1, batch_size=8, shuffle = True, verbose = 1,validation_split=0.2)
model.fit(X, y)
y_hat = model.predict(X)

Вывод:

Train on 88 samples, validate on 23 samples
Epoch 1/1
88/88 [==============================] - 2s 21ms/step - loss: 0.6951 - accuracy: 0.4432 - val_loss: 0.6898 - val_accuracy: 0.5652
111/111 [==============================] - 0s 2ms/step
(111, 1)

Та-да! это работает

Теперь давайте объединим их и запустим

model=KerasClassifier(build_fn=create_model, epochs=1, batch_size=8, shuffle=True, verbose=1,validation_split=0.2)
chain=ClassifierChain(model, order='random', random_state=42)
chain.fit(X, y)
print (chain.predict(X).shape)

упс! он обучается, но прогнозы не срабатывают, поскольку OP указывает Ошибка:

ValueError: could not broadcast input array from shape (111,1) into shape (111)

Проблема

Эта ошибка вызвана строкой ниже в sklearn

--> 525             Y_pred_chain[:, chain_idx] = estimator.predict(X_aug)

Это потому, что Цепочка классификаторов запускает оценщики по одному и сохраняет прогнозы каждого оценщика в Y_pred_chain в индексе оценщиков (определяемом параметром order). Предполагается, что оценщики возвращают прогнозы в одномерном массиве. Но модели keras возвращают результат формы batch_size x output_size, который в нашем случае равен 111 x 1.

Решение

Нам нужен способ изменить предсказания формы 111 X 1 на 111 или обычно от batch_size x 1 до batch_size. Давайте опираться на концепции OOPS и перегрузим метод прогнозирования KerasClassifier

class MyKerasClassifier(KerasClassifier):
  def __init__(self, **args):
    super().__init__(**args)

  def predict(self, X):
    return super().predict(X).reshape(len(X)) # Here we are flattening 2D array to 1D

model=MyKerasClassifier(build_fn=create_model, epochs=1, batch_size=8, shuffle=True, verbose=1,validation_split=0.2)
chain=ClassifierChain(model, order='random', random_state=42)
chain.fit(X, y)
print (chain.predict(X).shape)

Результат:

Epoch 1/1
88/88 [==============================] - 2s 19ms/step - loss: 0.6919 - accuracy: 0.5227 - val_loss: 0.6892 - val_accuracy: 0.5652
111/111 [==============================] - 0s 3ms/step
(111, 1)

Ta-da! это работает

Этап 2

Давайте глубже рассмотрим ClassifierChain class

Модель с несколькими метками, которая объединяет двоичные классификаторы в цепочку.

Каждая модель делает прогноз в порядке, указанном цепочкой, используя все доступные функции, предоставленные модели, плюс прогнозы моделей, которые находятся ранее в цепочке.

Итак что нам действительно нужно, так это y формы 111 X 17, чтобы цепочка содержала 17 оценок. Давайте попробуем

Настоящая ClassifierChain

y = np.random.randint(0,2,(111,17))
model=MyKerasClassifier(build_fn=create_model, epochs=1, batch_size=8, shuffle=True, verbose=1,validation_split=0.2)
chain=ClassifierChain(model, order='random', random_state=42)
chain.fit(X, y)

Вывод:

ValueError: Error when checking input: expected input_62 to have shape (107,) but got array with shape (108,)

Он не может обучить модель; Причина довольно проста. Цепочка сначала обучает первую оценку с функцией 107, которая работает нормально. Затем цепочка выбирает следующий оценщик и затем обучает его с помощью 107 функций + единственный результат предыдущего оценщика (= 108). Но поскольку наша модель имеет входной размер 107, она завершится ошибкой, как и сообщение об ошибке. Каждый оценщик получит 107 входных характеристик + результат всех предыдущих оценщиков.

Решение [хакерское]

Нам нужен способ изменить input_size модели, поскольку они созданы из ClassifierChain. Кажется, что в ClassifierChain нет обратных вызовов или перехватов, поэтому у меня есть хакерское решение.

input_size = 107    

# define the model
def create_model():
  global input_size
  embedding_size=128
  lstm_size=64
  output_size=1
  vocab_size = 100

  current_input=Input(shape=(input_size,)) 
  emb_current = Embedding(vocab_size, embedding_size, input_length=input_size)(current_input)
  out_current=Bidirectional(LSTM(units=lstm_size))(emb_current )
  output = Dense(units=output_size, activation=  'sigmoid')(out_current)
  model = Model(inputs=current_input, outputs=output)
  model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])

  input_size += 1 # <-- This does the magic
  return model

X = np.random.randint(0,100,(111, 107))
y = np.random.randint(0,2,(111,17))
model=MyKerasClassifier(build_fn=create_model, epochs=1, batch_size=8, shuffle=True, verbose=1,validation_split=0.2)
chain=ClassifierChain(model, order='random', random_state=42)
chain.fit(X, y)
print (chain.predict(X).shape)

Вывод:

Train on 88 samples, validate on 23 samples
Epoch 1/1
88/88 [==============================] - 2s 22ms/step - loss: 0.6901 - accuracy: 0.6023 - val_loss: 0.7002 - val_accuracy: 0.4783
Train on 88 samples, validate on 23 samples
Epoch 1/1
88/88 [==============================] - 2s 22ms/step - loss: 0.6976 - accuracy: 0.5000 - val_loss: 0.7070 - val_accuracy: 0.3913
Train on 88 samples, validate on 23 samples
Epoch 1/1
----------- [Output truncated] ----------------
111/111 [==============================] - 0s 3ms/step
111/111 [==============================] - 0s 3ms/step
(111, 17)

Как и ожидалось, он обучает 17 оценщиков и Метод predict возвращает результат формы 111 x 17 каждого столбца, соответствующего прогнозам, сделанным соответствующим оценщиком.

1 голос
/ 25 июня 2020

вот полный рабочий пример ...

Я решил использовать последовательную модель и softmax в качестве последней активации

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from sklearn.multioutput import ClassifierChain

n_sample = 20
vocab_size = 33
input_size = 100

X = np.random.randint(0,vocab_size, (n_sample,input_size))
y = np.random.randint(0,2, (n_sample,17))

def create_model():
    
    global input_size
    embedding_size = 128
    lstm_size = 64
    
    model = Sequential([
        Embedding(vocab_size, embedding_size, input_length=input_size),
        Bidirectional(LSTM(units=lstm_size)),
        Dense(units=2, activation=  'softmax')
    ])

    model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
    input_size += 1
    
    return model

model = tf.keras.wrappers.scikit_learn.KerasClassifier(build_fn=create_model, epochs=1, batch_size=256, 
                        shuffle = True, verbose = 1, validation_split=0.2)
chain = ClassifierChain(model, order='random', random_state=42)
chain.fit(X, y)

chain.predict_proba(X)

здесь рабочий код: https://colab.research.google.com/drive/1aVjjh6VPmAyBddwU4ff2w9y_LmmC02W_?usp=sharing

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...