【问题标题】:Deep Learning how to split 5 dimensions timeseries and pass some dimensions through embedding layer深度学习如何拆分 5 维时间序列并通过嵌入层传递一些维度
【发布时间】:2022-01-13 09:39:56
【问题描述】:

我有一个 5 维时间序列的输入:

a = [[8,3],[2] , [4,5],[1], [9,1],[2]...]#total 100 timestamps. For each element, dims 0,1 are numerical data and dim 2 is a numerical encoding of a category. This is per sample, 3200 samples

该类别有 3 个可能的值 (0,1,2)

我想构建一个神经网络,使最后一个维度(类别)通过输出大小为 8 的嵌入层,然后连接回前两个维度(数字数据)。

所以,这将是这样的:

input1 = keras.layers.Input(shape=(2,)) #the numerical features
input2 = keras.layers.Input(shape=(1,)) #the encoding of the categories. this part will be embedded to 5 dims
x2 = Embedding(input_dim=1, output_dim = 8)(input2) #apply it to every timestamp and take only dim 3, so [2],[1], [2] 
x = concatenate([input1,x2]) #will get 10 dims at each timepoint, still 100 timepoints
x = LSTM(units=24)(x) #the input has 10 dims/features at each timepoint, total 100 timepoints per sample
x = Dense(1, activation='sigmoid')(x)
model = Model(inputs=[input1, input2] , outputs=[x]) #input1 is 1D vec of the width 2 , input2 is 1D vec with the width 1 and it is going through the embedding
model.compile(
        loss='binary_crossentropy',
        optimizer='adam',
        metrics=['acc']
    )

我该怎么做? (最好在 keras 中)? 我的问题是如何将嵌入应用到每个时间点? 意思是,如果我有 1000 个时间点,每个时间点有 3 个昏暗,我需要将其转换为 1000 个时间点,每个时间点有 8 个昏暗(嵌入层应该将 input2 从(1000X1)转换为(1000X8)

【问题讨论】:

  • @AloneTogether 是的(我还在帖子中添加了另一层,因为我希望它们在嵌入后一起进入 LSTM)
  • @AloneTogether 我有 3200 个样本,每个都是 100X5 的 ndarray
  • @AloneTogether 也许我的解释很差,但我的意思是最后 3 个特征将一起进入嵌入,并将转换为 8 个特征,这些特征是这 3 个的嵌入。因此,例如,如果嵌入层是e,所以e([8,6,3]可以是[1,2,3,4,5,6,7,8]
  • 对不起,但正如 tensorflow 文档明确指出的那样,嵌入不是将形状 (1000, 3) 转换为 (1000, 8) 的正确层。请看@Andrew Wei 的回答。但请注意,那里没有发生任何类型的编码!但是你之前做的编码不正确吗?
  • @pythonic833 所以我不确定解决方案是什么 - 我不能放置 Dense 层,因为每个时间戳都会有不同的参数集,所以它没有意义。我能做什么?

标签: python tensorflow keras deep-learning neural-network


【解决方案1】:

您在这里遇到了几个问题。 首先让我给你一个可行的例子,并解释如何解决你的问题。

导入和数据生成

import tensorflow as tf
import numpy as np

from tensorflow.keras import layers
from tensorflow.keras.models import Model

num_timesteps = 100
max_features_values = [100, 100, 3]
num_observations = 2

input_list = [[[np.random.randint(0, v) for _ in range(num_timesteps)]
   for v in max_features_values]
    for _ in range(num_observations)]

input_arr = np.array(input_list)  # shape (2, 3, 100)

为了使用嵌入,我们需要将 voc_size 作为 input_dimension,如 LSTM documentation 中所述。

嵌入和连接

voc_size = len(np.unique(input_arr[:, 2, :])) + 1  # 4

现在我们需要创建输入。输入的大小应为[None, 2, num_timesteps][None, 1, num_timesteps],其中第一个维度是灵活的,将填充我们传入的观察数。让我们使用之前计算的voc_size 之后的嵌入。

inp1 = layers.Input(shape=(2, num_timesteps))  # TensorShape([None, 2, 100])
inp2 = layers.Input(shape=(1, num_timesteps))  # TensorShape([None, 1, 100])
x2 = layers.Embedding(input_dim=voc_size, output_dim=8)(inp2)  # TensorShape([None, 1, 100, 8])
x2_reshaped = tf.transpose(tf.squeeze(x2, axis=1), [0, 2, 1])  # TensorShape([None, 8, 100])

这不容易连接,因为除了连接轴上的维度之外,所有维度都必须匹配。但不幸的是,形状不匹配。因此我们重塑x2。我们通过删除第一个维度然后转置来做到这一点。

现在我们可以毫无问题地进行连接,一切都可以直接进行:

x = layers.concatenate([inp1, x2_reshaped], axis=1)
x = layers.LSTM(32)(x)
x = layers.Dense(1, activation='sigmoid')(x)
model = Model(inputs=[inp1, inp2], outputs=[x])

检查虚拟示例

inp1_np = input_arr[:, :2, :]
inp2_np = input_arr[:, 2:, :]
model.predict([inp1_np, inp2_np])

# Output
# array([[0.544262 ],
#       [0.6157502]], dtype=float32)

#这会按预期输出介于 0 和 1 之间的值。

【讨论】:

  • 我认为您的方法会产生形状为 (None, 100, 26) 而不是所需的 (None, 100, 10) 的张量
  • @pythonic833 谢谢,有没有办法让我看到 layers.concatenate(tensorlist, axis=2) 的输出?它应该是 [100, 8](100 个时间戳,8 个角钱)。相应地,layers.Embedding(input_dim=voc_size, output_dim=8)(inp2)的输出应该是[100,5]
  • 您可以打印出来。您可能想尝试在 colab 中运行该示例,它会打印出类似 KerasTensor(type_spec=TensorSpec(shape=(None, 100, 26), dtype=tf.float32, name=None), name='concatenate_4/concat:0', description="created by layer 'concatenate_4'") 的内容
  • @okuoub:我认为您实际上不想要一个嵌入,而是一个将您的 (None, 100, 3) 形状转换为 (None, 100, 8) 的层。因为嵌入会创建None, 3, 100, 8),这使得它无法与其他输入连接
  • @pythonic833 是的,我想要一个将你的 (None, 100, 3) 形状转换为 (None, 100, 8) 的图层。我认为嵌入是这样做的方法。有没有更好的办法?另外,为什么嵌入创建 [3,100,8] 而不是 [100,8]?
【解决方案2】:

如果您不是在寻找 Embeddings 时,它通常在 Keras 中使用(正整数映射到密集向量)。您可能正在寻找某种非投影或基础扩展,其中 3 个维度被映射(嵌入)到 8 并连接结果。这可以使用核技巧或其他方法来完成,但也隐含在具有非线性应用的神经网络中。

因此,您可以按照与 pythonic833 类似的格式执行类似的操作,因为它很好(但根据要求 [batch, timesteps, feature]Keras LSTM 文档在中间有时间戳):

输入生成

import tensorflow as tf
import numpy as np

from tensorflow.keras import layers
from tensorflow.keras.models import Model

num_timesteps = 100
num_features = 5
num_observations = 2

input_list = [[[np.random.randint(1, 100) for _ in range(num_features)]
   for _ in range(num_timesteps)]
    for _ in range(num_observations)]

input_arr = np.array(input_list)  # shape (2, 100, 5)

模型构建

然后你可以处理输入:

input1 = layers.Input(shape=(num_timesteps, 2,))
input2 = layers.Input(shape=(num_timesteps, 3))
x2 = layers.Dense(8, activation='relu')(input2)
x = layers.concatenate([input1,x2], axis=2) # This produces tensors of shape (None, 100, 10)
x = layers.LSTM(units=24)(x)
x = layers.Dense(1, activation='sigmoid')(x)
model = Model(inputs=[input1, input2] , outputs=[x])
model.compile(
    loss='binary_crossentropy',
    optimizer='adam',
    metrics=['acc']
)

结果

inp1_np = input_arr[:, :, :2]
inp2_np = input_arr[:, :, 2:]
model.predict([inp1_np, inp2_np])

产生

array([[0.44117224],
       [0.23611131]], dtype=float32)

关于基扩展的其他解释查看:

  1. https://stats.stackexchange.com/questions/527258/embedding-data-into-a-larger-dimension-space
  2. https://www.reddit.com/r/MachineLearning/comments/2ffejw/why_dont_researchers_use_the_kernel_method_in/

【讨论】:

  • 请注意我想在这里嵌入而不是密集:x2 = layers.Dense(8, activation='relu')(input2)
  • 我不认为它是这样工作的。 Keras 嵌入层将正数映射到密集向量,即接受形状为 (batch_size, input_length) 的 2D 输入并返回 (batch_size, input_length, output_dim) 的 3D 输出。这意味着它将[0, 1, 0] 的每个元素映射到一个向量中,例如[[a,b], [c,d], [a,b]]。将 one-hot 编码传递给密集向量应该仍然能够通过权重的相似性来了解激活索引之间的关联。
  • 那么这里可以做什么呢? Dense 不起作用(因为它会为每个时间戳学习一组不同的权重),有没有办法将其他扩展合并到 NN 中?
  • 我认为它成功了。来自 Dense 的 Keras 文档,“注意:如果层的输入的秩大于 2,则 Dense 沿输入的最后一个轴和内核的轴 0 计算输入和内核之间的点积(使用tf.tensordot). 例如,如果输入的维度为 (batch_size, d0, d1),那么我们创建一个形状为 (d1, units) 的内核,内核沿输入的轴 2 运行,在shape (1, 1, d1) (有 batch_size * d0 这样的子张量)。这种情况下的输出将具有 shape (batch_size, d0, units)。”
  • 让我们将示例转换为我们的案例,我们有形状为 (batch_size, timesteps, 3) 的张量,并且使用形状 (3, 8) 创建了一个内核。明确地说,这意味着它将创建 8 个线性函数 + 一个激活,每个函数接受 3 个输入(具有不同的权重)。然后它说这 8 个函数应用于每个子张量,其中有 (batch_size * timesteps many)。对我们来说,这意味着每个子张量 (1 x 1 x 3) 都被放入相同的 8 个函数中,从而产生形状 (1 x 1 x 8) 的结果。然后把它们放在一起,我们得到一个形状的输出 (batch_size, timesteps, 8)。
猜你喜欢
  • 2019-10-13
  • 2017-10-22
  • 2018-01-11
  • 2017-02-14
  • 2021-01-30
  • 2017-08-18
  • 2019-10-01
  • 2020-03-06
  • 1970-01-01
相关资源
最近更新 更多