无法将输入数组从形状 (27839,1) 广播到形状 (27839)

Posted

技术标签:

【中文标题】无法将输入数组从形状 (27839,1) 广播到形状 (27839)【英文标题】:could not broadcast input array from shape (27839,1) into shape (27839) 【发布时间】:2020-10-06 15:05:59 【问题描述】:

我正在为一个在链中使用 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)

模型总结如下:

这里有完整的错误跟踪:

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)

谁能帮忙解决这个错误?

【问题讨论】:

我会仔细检查 X_test 的形状。 X_test 的形状是 (27839, 107)。 训练结束后,能不能打印chain.classes_ 并写出它的输出? 我在主帖中添加了chain.classes_的输出。 这有点远,但我有一个类似的问题,我通过使用np.array(X_test) 作为输入解决了这个问题。我认为这与X_test 的指针是否实际“控制”内存有关 【参考方案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 1111 或一般 batch_size x 1batch_size。让我们依靠 OOPS 的概念,重载 KerasClassifier 的 predict 方法

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)

哒哒!它有效

第二阶段

让我们深入了解ClassifierChain 类

一种将二元分类器排列成链的多标签模型。

每个模型按照链指定的顺序进行预测 使用提供给模型的所有可用功能以及 链中较早的模型的预测。

所以我们真正需要的是一个形状为111 X 17y,以便链包含17 个估计器。来试试吧

真正的分类器链

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 输入特征 + 所有先前估计器的输出。

解决方案 [hacky]

我们需要一种方法来更改模型的input_size,因为它们是从ClassifierChain 创建的。 ClassifierChain 似乎没有回调或挂钩,所以我有一个 hacky 解决方案。

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 的输出,每列对应于相应估计器所做的预测。

【讨论】:

感谢您提供非常详细的回答。让我试试看。 我解决了输入大小的问题,将其设为无,因此它不会像我们的例子中的 107 那样被修复。 感谢您的回答。它通过自定义 keras 分类器和重载预测方法来工作。谢谢 自定义 kerasclassifier 预测方法在 google colab notebook 上工作,但是当我尝试在服务器上运行它时,它给了我这个错误'模型对象没有属性,'predict_classes'。有什么想法吗?【参考方案2】:

这里是一个完整的工作示例...

我使用顺序模型和 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

【讨论】:

以上是关于无法将输入数组从形状 (27839,1) 广播到形状 (27839)的主要内容,如果未能解决你的问题,请参考以下文章

Mask-RCNN,ValueError:无法将输入数组从形状(70)广播到形状(1)

如何将 RGB ImageItem 添加到 pyqtgraph ViewBox? ValueError:无法将输入数组从形状(256,256,4)广播到形状(256,256)

关于将输入数组从一种形状广播到另一种形状的正确方法

形状不匹配:索引数组无法与形状一起广播

ValueError: 操作数无法与形状 (5,) (30,) 一起广播

ValueError:操作数无法与形状一起广播 (2501,201) (2501,)