我正在做一个较小的项目,以更好地理解RNN,尤其是LSTM和GRU。我根本不是专家,所以请记住这一点。
我面临的问题以数据的形式给出:
>>> import numpy as np >>> import pandas as pd >>> pd.DataFrame([[1, 2, 3],[1, 2, 1], [1, 3, 2],[2, 3, 1],[3, 1, 1],[3, 3, 2],[4, 3, 3]], columns=['person', 'interaction', 'group']) person interaction group 0 1 2 3 1 1 2 1 2 1 3 2 3 2 3 1 4 3 1 1 5 3 3 2 6 4 3 3
这仅是为了说明。我们有不同的人以不同的方式与不同的群体进行交互。我已经编码了各种功能。用户的最后一次互动始终是3
,这意味着选择某个组。在上面的简短示例中,person 1
选择组2
,person 2
选择组1
等。
我的整个数据集要大得多,但是我想先了解概念部分,然后再投入模型。我要学习的任务有一系列互动,该互动由个人选择。更具体一点,我想输出一个列表,其中所有组(有3个组1, 2, 3
)按最可能的选择排序,然后是第二和第三个最喜欢的组。因此,损失函数是平均倒数。
我知道在Keras Grus / LSTM中可以处理各种长度的输入。所以我的三个问题是。
输入的格式为:
(samples, timesteps, features)
编写高级代码:
import keras.layers as L import keras.models as M model_input = L.Input(shape=(?, None, 2))
timestep=None
应该暗示变化的大小,2
并且用于特征interaction
和group
。但是样品呢?如何定义批次?
对于输出,我有点困惑,在这个示例中应该是什么样子?我认为,对于一个人的每一次最后一次互动,我都希望有一份长度清单3
。假设我已经设置了输出
model_output = L.LSTM(3, return_sequences=False)
然后,我想对其进行编译。有没有一种方法可以使用平均倒数排名?
model.compile('adam', '?')
我知道问题的水平很高,但是我想首先了解全局并开始尝试。因此,任何帮助将不胜感激。
您在问题中提出的概念已经是一个不错的开始。我将添加一些东西使其工作,以及下面的代码示例:
您可以LSTM(n_hidden, input_shape=(None, 2))
直接指定,而不必插入额外的Input
图层;该定义的批次尺寸将被省略。
由于您的模型将执行某种分类(基于时间序列数据),因此最后一层也是我们期望的“常规”分类Dense(num_classes, action='softmax')
。将LSTM
和Dense
层链接在一起将首先使时间序列输入通过该LSTM
层,然后将其输出(由隐藏单位的数量确定)馈送到该Dense
层中。activation='softmax'
允许为每个课程计算课程得分(我们将在数据预处理步骤中使用单热编码,请参见下面的代码示例)。这意味着班级分数没有排序,但是您始终可以通过np.argsort
或来进行排序np.argmax
。
分类交叉熵损失适合比较分类得分,因此我们将使用其中一个:model.compile(loss='categorical_crossentropy', optimizer='adam')
。
由于互动次数。也就是说,模型输入的长度因样本而异,我们将使用1的批次大小,并一次输入一个样本。
以下是针对上述考虑的示例实现。请注意,我稍微修改了样本数据,以便在组选择后面提供更多的“理由”。每个人还需要在选择一个组之前执行至少一个交互(即,输入序列不能为空);如果您的数据不是这种情况,那么引入额外的无操作交互(例如0
)会有所帮助。
import pandas as pd import tensorflow as tf model = tf.keras.models.Sequential() model.add(tf.keras.layers.LSTM(10, input_shape=(None, 2))) # LSTM for arbitrary length series. model.add(tf.keras.layers.Dense(3, activation='softmax')) # Softmax for class probabilities. model.compile(loss='categorical_crossentropy', optimizer='adam') # Example interactions: # * 1: Likes the group, # * 2: Dislikes the group, # * 3: Chooses the group. df = pd.DataFrame([ [1, 1, 3], [1, 1, 3], [1, 2, 2], [1, 3, 3], [2, 2, 1], [2, 2, 3], [2, 1, 2], [2, 3, 2], [3, 1, 1], [3, 1, 1], [3, 1, 1], [3, 2, 3], [3, 2, 2], [3, 3, 1]], columns=['person', 'interaction', 'group'] ) data = [person[1][['interaction', 'group']].values for person in df.groupby('person')] x_train = [x[:-1] for x in data] y_train = tf.keras.utils.to_categorical([x[-1, 1]-1 for x in data]) # Expects class labels from 0 to n (-> subtract 1). print(x_train) print(y_train) class TrainGenerator(tf.keras.utils.Sequence): def __init__(self, x, y): self.x = x self.y = y def __len__(self): return len(self.x) def __getitem__(self, index): # Need to expand arrays to have batch size 1. return self.x[index][None, :, :], self.y[index][None, :] model.fit_generator(TrainGenerator(x_train, y_train), epochs=1000) pred = [model.predict(x[None, :, :]).ravel() for x in x_train] for p, y in zip(pred, y_train): print(p, y)
以及相应的样本输出:
[...] Epoch 1000/1000 3/3 [==============================] - 0s 40ms/step - loss: 0.0037 [0.00213619 0.00241093 0.9954529 ] [0. 0. 1.] [0.00123938 0.99718493 0.00157572] [0. 1. 0.] [9.9632275e-01 7.5039308e-04 2.9268670e-03] [1. 0. 0.]
使用自定义生成器表达式:根据文档,我们可以使用任何生成器来生成数据。预期生成器将产生大量数据,并无限期地遍历整个数据集。使用时,tf.keras.utils.Sequence
无需指定参数,steps_per_epoch
因为该参数默认为len(train_generator)
。因此,在使用自定义生成器时,我们还将提供此参数:
import itertools as it model.fit_generator(((x_train[i % len(x_train)][None, :, :], y_train[i % len(y_train)][None, :]) for i in it.count()), epochs=1000, steps_per_epoch=len(x_train))