Python 如何拟合时间序列多头模型?

Python 如何拟合时间序列多头模型?,python,tensorflow,keras,time-series,conv-neural-network,Python,Tensorflow,Keras,Time Series,Conv Neural Network,我试图通过将两个模型连接在一起来创建一个模型。 我想要使用的模型将处理时间序列,我正在试验Conv1D层。 由于它们有一个3D输入形状批处理形状+(步骤,输入尺寸),并且Keras TimeseriesGenerator提供了这样的功能,我很高兴能够在处理单头模型时使用它 import pandas as pd import numpy as np import random from sklearn.model_selection import train_test_split from te

我试图通过将两个模型连接在一起来创建一个模型。 我想要使用的模型将处理时间序列,我正在试验Conv1D层。 由于它们有一个3D输入形状批处理形状+(步骤,输入尺寸),并且Keras TimeseriesGenerator提供了这样的功能,我很高兴能够在处理单头模型时使用它

import pandas as pd
import numpy as np
import random
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import (Input, Dense, Conv1D, BatchNormalization, 
                                     Flatten, Dropout, MaxPooling1D,
                                     concatenate)
from tensorflow.keras.preprocessing.sequence import TimeseriesGenerator
from tensorflow.keras.utils import plot_model

data = pd.DataFrame(index=pd.date_range(start='2020-01-01', periods=300, freq='D'))
data['featureA'] = [random.random() for _ in range(len(data))]
data['featureB'] = [random.random() for _ in range(len(data))]
data['featureC'] = [random.random() for _ in range(len(data))]
data['featureD'] = [random.random() for _ in range(len(data))]
data['target']   = [random.random() for _ in range(len(data))]

Xtrain_AB, Xtest_AB, yTrain_AB, yTest_AB = train_test_split(data[['featureA', 'featureB']], 
                                                            data['target'], test_size=0.2,
                                                            shuffle=False)
Xtrain_CD, Xtest_CD, yTrain_CD, yTest_CD = train_test_split(data[['featureC', 'featureD']], 
                                                            data['target'], test_size=0.2,
                                                            shuffle=False)

n_steps = 5 

train_gen_AB = TimeseriesGenerator(Xtrain_AB, yTrain_AB,
                                length=n_steps, 
                                sampling_rate=1,
                                batch_size=64,
                                shuffle=False)
test_gen_AB = TimeseriesGenerator(Xtest_AB, yTest_AB,
                                length=n_steps, 
                                sampling_rate=1,
                                batch_size=64,
                                shuffle=False)

n_features_AB = len(Xtrain_AB.columns)
input_AB      = Input(shape=(n_steps, n_features_AB))
layer_AB      = Conv1D(filters=128, kernel_size=3, activation='relu', input_shape=(n_steps, n_features_AB))(input_AB)
layer_AB      = MaxPooling1D(pool_size=2)(layer_AB)
layer_AB      = Flatten()(layer_AB)
dense_AB      = Dense(50, activation='relu')(layer_AB)
output_AB     = Dense(1)(dense_AB)
model_AB      = Model(inputs=input_AB, outputs=output_AB)
model_AB.compile(optimizer='adam', loss='mse')
model_AB.summary()
model_AB.fit(train_gen_AB, epochs=1, verbose=1)
print(f'evaluation: {model_AB.evaluate(test_gen_AB)}')

#plot_model(model_AB)

train_gen_CD = TimeseriesGenerator(Xtrain_CD, yTrain_CD,
                                length=n_steps, 
                                sampling_rate=1,
                                batch_size=64,
                                shuffle=False)
test_gen_CD = TimeseriesGenerator(Xtest_CD, yTest_CD,
                                length=n_steps, 
                                sampling_rate=1,
                                batch_size=64,
                                shuffle=False)

n_features_CD = len(Xtrain_CD.columns)
input_CD      = Input(shape=(n_steps, n_features_CD))
layer_CD      = Conv1D(filters=128, kernel_size=3, activation='relu', input_shape=(n_steps, n_features_CD))(input_CD)
layer_CD      = MaxPooling1D(pool_size=2)(layer_CD)
layer_CD      = Flatten()(layer_CD)
dense_CD      = Dense(50, activation='relu')(layer_CD)
output_CD     = Dense(1)(dense_CD)
model_CD      = Model(inputs=input_CD, outputs=output_CD)
model_CD.compile(optimizer='adam', loss='mse')
model_CD.summary()
model_CD.fit(train_gen_CD, epochs=1, verbose=1)
print(f'evaluation: {model_CD.evaluate(test_gen_CD)}')

#plot_model(model_CD)
这适用于每种型号:)

现在,我想尝试将两个模型连接到一个模型(因为我认为这可能会使我以后在添加额外的“头”来并行训练它们时,使用这样的模型可能会更容易,因为一次可以处理很多分离的模型),并得到一个双头模型,它可以像这样轻松创建

merge=concatenate(inputs=[layer_AB, layer_CD])
dense_merge     = Dense(50, activation='relu')(merge)
output_merge    = Dense(1)(dense_merge)
model_dual_head = Model(inputs=[input_AB, input_CD], outputs=output_merge)
model_dual_head.compile(optimizer='adam', loss='mse')
model_dual_head.summary()
print(f'dual head model input_shape:{model_dual_head.input_shape}')
plot_model(model_dual_head) 
此双_头_模型具有2倍3D的输入_形状
[(无,5,2),(无,5,2)]
最后看起来是这样的

不幸的是,我不知道如何适应它,希望您能为我提供一个如何生成所需数据形状的解决方案。 我试图提供以前使用过的发电机列表
model\u dual\u head.fit([train\u gen\u AB,train\u gen\u CD],epochs=1,verbose=1)
,以及原始输入数据帧列表
model\u dual\u head.fit(x=[Xtrain\u AB,Xtrain\u CD],y=[yTrain\u AB,yTrain\u CD],epochs=1,verbose=1)
,但它的形状似乎不正确

提前谢谢

瓦西里


基于Jacks的评论 我尝试使用以下代码

def doubleGen(gen1, gen2):
  assert(len(gen1) == len(gen2))
  for feature1, label1, feature2, label2 in (train_gen_AB, train_gen_CD):
    yield (feature1, feature2), label1 

gen = doubleGen(train_gen_AB, train_gen_CD)
model_dual_head.fit(gen, epochs=1, verbose=1)
但不幸的是,它不起作用,因为输入的形状不同

ValueError: Layer model_2 expects 2 input(s), but it received 4 input tensors. Inputs received: [<tf.Tensor 'IteratorGetNext:0' shape=(None, None, None) dtype=float32>, <tf.Tensor 'ExpandDims:0' shape=(None, 1) dtype=float32>, <tf.Tensor 'IteratorGetNext:2' shape=(None, None, None) dtype=float32>, <tf.Tensor 'ExpandDims_1:0' shape=(None, 1) dtype=float32>]
4/4[=======================================]-0s 14ms/步-损耗:0.1119

我不知道这是否已经存在,但我相信您可以创建一个新的生成器来合并这两个数据集。假设这两个生成器是同步的,这应该可以工作:

for (input1, label1), (input2, label2) in generator1, generator2:
    assert label1 == label2
    yield (input1, input2), label1

现在,它提供了一个生成器,它以元组的形式生成输入,以一个数据项的形式生成公共标签。这可能是一个lambda,这样可以省去创建整个生成器类的麻烦。

谢谢Jack,请参阅我编辑的问题。我无法从新生成器中获得正确的形状。'for'行中的括号非常简单重要的是,它们应该匹配生成器创建的内容。我见过返回元组的生成器,以及返回元组列表(批量大小)的生成器。生成器可能给出(图像,标签)或[(图像,标签),…]或([图像,…],[标签,…])。生成器需要在zip中压缩(功能1,标签1),(功能2,标签2)的
(列车B、列车CD):
<ipython-input-8-e8971cd0f287> in doubleGen(gen1, gen2)
      1 def doubleGen(gen1, gen2):
      2   assert(len(gen1) == len(gen2))
----> 3   for (feature1, label1), (feature2, label2) in train_gen_AB, train_gen_CD:
      4     assert label1 == label2
      5     yield (feature1, feature2), label1

ValueError: too many values to unpack (expected 2)
def doubleGen(gen1, gen2):
  assert(len(gen1) == len(gen2))
  for i in range(len(gen1)):
    feature1, label1 = gen1[i]
    feature2, label2 = gen2[i]
    #assert label1.all() == label2.all()
    yield (feature1, feature2), label1 

gen = doubleGen(train_gen_AB, train_gen_CD)
model_dual_head.fit(gen, epochs=1, verbose=1)
TypeError                                 Traceback (most recent call last)
<ipython-input-24-6abda48a58c7> in <module>()
      8 
      9 gen = doubleGen(train_gen_AB, train_gen_CD)
---> 10 model_dual_head.fit(gen, epochs=1, verbose=1)

2 frames
/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/def_function.py in _call(self, *args, **kwds)
    853       # In this case we have created variables on the first call, so we run the
    854       # defunned version which is guaranteed to never create variables.
--> 855       return self._stateless_fn(*args, **kwds)  # pylint: disable=not-callable
    856     elif self._stateful_fn is not None:
    857       # Release the lock early so that multiple threads can perform the call

TypeError: 'NoneType' object is not callable
def doubleGen(gen1, gen2):
  assert(len(gen1) == len(gen2))
  for (feature1, label1), (feature2, label2) in zip(train_gen_AB, train_gen_CD):
    assert label1.all() == label2.all()
    yield (feature1, feature2), label1 

gen = doubleGen(train_gen_AB, train_gen_CD)
model_dual_head.fit(gen, epochs=1, verbose=1)
4/4 [==============================] - 0s 14ms/step - loss: 0.1119
<tensorflow.python.keras.callbacks.History at 0x7f0dfd4de5d0>
for (input1, label1), (input2, label2) in generator1, generator2:
    assert label1 == label2
    yield (input1, input2), label1