[英]how do I fit a time-series multi head model?
I try to create a model by concatenating 2 models together.我尝试通过将 2 个模型连接在一起来创建 model。 The models I want to use, shall handle time series, and I'm experimenting with Conv1D layers.我想使用的模型应处理时间序列,并且我正在尝试使用 Conv1D 层。 As these have an 3D input shape batch_shape + (steps, input_dim) and the Keras TimeseriesGenerator is providing such, I'm happy being able to make use of it when handling single head models.由于它们有一个 3D 输入形状 batch_shape + (steps, input_dim) 和 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)
This works fine for each of the models:)这适用于每个模型:)
Now I would like to experiment with concatenating both models to one (as I think it might enable me later when adding additional 'heads' to train them in parallel, and I guess using such models might be easier as handling a lot of separated once) and get a dual-head model, which can easily be created like this现在我想尝试将两个模型连接到一个模型(因为我认为它可以让我稍后添加额外的“头”来并行训练它们,我猜使用这样的模型可能更容易处理很多分离一次)并得到一个双头 model,可以像这样轻松创建
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)
This dual_head_model has an input_shape of 2 time 3D [(None, 5, 2), (None, 5, 2)]
and looks finally this way这个 dual_head_model 的 input_shape 为 2 倍 3D [(None, 5, 2), (None, 5, 2)]
最终看起来是这样
Unfortunately I don't know how to fit it:( and hope you will be able to provide me a solution on how to generate the needed shape of data. I tried to provide the previously used generators as list model_dual_head.fit([train_gen_AB, train_gen_CD], epochs=1, verbose=1)
, and also lists of the raw input data frames model_dual_head.fit(x=[Xtrain_AB, Xtrain_CD], y=[yTrain_AB, yTrain_CD], epochs=1, verbose=1)
, but it seems not to be in the right shape.不幸的是,我不知道如何适应它:(并希望您能够为我提供有关如何生成所需数据形状的解决方案。我尝试将以前使用的生成器提供为列表model_dual_head.fit([train_gen_AB, train_gen_CD], epochs=1, verbose=1)
,以及原始输入数据帧model_dual_head.fit(x=[Xtrain_AB, Xtrain_CD], y=[yTrain_AB, yTrain_CD], epochs=1, verbose=1)
的列表,但它似乎不是正确的形状。
Thanks in advance提前致谢
Wasili瓦西里
based on Jacks comment I tried the using following code根据 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)
but unfortunatelly it does not work, as the input_shape is not the same但不幸的是它不起作用,因为 input_shape 不一样
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>]
I adapted the parenthesis in the generator based on Jacks note and get a different error now.我根据 Jacks note 调整了生成器中的括号,现在得到了不同的错误。
def doubleGen(gen1, gen2):
assert(len(gen1) == len(gen2))
for (feature1, label1), (feature2, label2) in train_gen_AB, train_gen_CD:
assert label1 == label2
yield (feature1, feature2), label1
gen = doubleGen(train_gen_AB, train_gen_CD)
model_dual_head.fit(gen, epochs=1, verbose=1)
<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)
I thought about using a plain index iterating the generators which does fix the shape topic, but this results in a NonType error我考虑过使用一个普通索引迭代生成器来修复形状主题,但这会导致 NonType 错误
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
The Notebook can be reached here可以在此处访问笔记本
Finally Jacks input helped to find the solution.最后,杰克斯的意见帮助找到了解决方案。 There was just missing to zip both generators to be able to iterated through them:) zip 只是缺少两个生成器,以便能够迭代它们:)
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>
I do not know if this already exists, but I believe you can create a new Generator that merges the two datasets.我不知道这是否已经存在,但我相信您可以创建一个新的生成器来合并两个数据集。 Assuming the two Generators are in lockstep, this should work:假设两个生成器步调一致,这应该可以工作:
for (input1, label1), (input2, label2) in generator1, generator2:
assert label1 == label2
yield (input1, input2), label1
This now gives a generator that yields both inputs as a tuple, and the common label as one data item.现在,这给出了一个生成器,该生成器将两个输入作为一个元组生成,并将公共 label 作为一个数据项。 This could be a lambda, which would save the trouble of creating an entire Generator class.这可能是 lambda,这样可以省去创建整个发电机 class 的麻烦。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.