在 Keras 中实现 Dependency Sensitive CNN (DSCNN)

数据挖掘 神经网络 nlp 喀拉斯
2022-02-26 20:10:36

我目前正在尝试在 Keras 中实现Rui Zhang 的 Dependency Sensitive Convolutional Neural Network for Modeling Documents对我来说,这是第一个在 Keras 中实现的网络,所以我提出了一些问题。

网络如下所示:

图 1:Rui Zhang 的 DSCNN(文档建模),抄自他的论文。

所以我从输入开始,我的想法是我必须输入序列序列,例如句子列表,其中句子是单词嵌入的单词列表。句子应该被填充,以便所有句子的长度相等。还应该对文档进行填充,以使每个文档的句子数量相等。

我们将有一批以下类型(仅符号 -> 根据 `fit()´):

s_i = np.array(shape=(max_sentence_length, word_embedding_dimensions))
doc_i = [s_1, s_2, s_3, ..., s_(max_sentences_per_doc)]
batch = [doc_1, doc_2, doc_3, ...]

其中we_idx_ij是某个文档的第 i 个句子中第 j 个单词的词嵌入索引。

问题1: 这有意义吗?

我仍然需要考虑如何准确地包含 w2v 词嵌入,但是对于 Keras 中的模型,我将按如下方式进行:

包括:

包括所需的方法/层并初始化符号常量

import numpy as np
from keras.layers import Activation, Input, Embedding, LSTM, concatenate, AveragePooling1D, MaxPool1D, Conv1D, TimeDistributed

# constants (for testing)
max_sentences_per_document = 10
max_words_per_sentence = 100
w2v_dimensions = 300
vocab_size = 20000
batch_size = 60

模型:

# preparing some shared layers
shared_embedding = Embedding(input_dim=w2v_dimensions, output_dim=vocab_size, weights=[W])  # optional,cause no training
shared_sentence_lstm = TimeDistributed(
    LSTM(input_dim=max_words_per_sentence, return_sequences=True, activation='tanh'),
    input_shape=(max_words_per_sentence, w2v_dimensions)
)
shared_sentence_lstm_2 = LSTM(activation='tanh')

# sentence modeling
sentence_inputs = [Input(shape=(batch_size, max_words_per_sentence, )) for i in range(max_sentences_per_document)]
sentence_modeling = [shared_embedding(sentence_inputs[i]) for i in range(max_sentences_per_document)]
sentence_modeling = [shared_sentence_lstm(sentence_modeling[i]) for i in range(max_sentences_per_document)]
sentence_modeling = [AveragePooling1D()(sentence_modeling[i]) for i in range(max_sentences_per_document)]
sentence_modeling = [shared_sentence_lstm_2(sentence_modeling[i]) for i in range(max_sentences_per_document)]

# document modeling
doc_modeling = concatenate(sentence_modeling)
doc_modeling = Conv1D(filters=100, kernel_size=[3, 4, 5], activation='relu')(doc_modeling)
doc_modeling = MaxPool1D()(doc_modeling)
doc_modeling = Activation('softmax')
doc_modeling.compile(loss='hinge', optimizer='sgd', metrics=['accuracy'])

问题2:

将单词嵌入已经在批处理数据中是否有意义,或者我应该s_i只为每个单词提供一个与单词嵌入矩阵中的索引相关的索引?(我想后者对内存更有意义)

问题 3:

如果我在批处理数据中提供单词嵌入,我就不需要嵌入层,对吗?

问题4:

这个网络能用吗?

问题 5:

这段代码是否按照论文中的建议实现了网络?

问题 6:

您对提高性能有什么建议吗?

我希望我在这个实现方面不会偏离轨道,并期待您的回答:-) 在此先感谢!

1个回答

中间我完成了网络,它运行良好:-) 任何也在寻找这个网络的人都可以在我的Github 页面上下载它。

直接参考我的问题:

问题 1: 提议的代码实际上是有部分意义的:

  • 应该使用共享嵌入层来节省内存使用,所以这部分是有意义的
  • 在最初提出的代码中,我使用了 TimeDistributed 层,这是出于网络目的的错误方式。我们实际上必须使用一个简单的共享 LSTM 层。多对多网络需要时间分布层。有关这方面的更多信息,我参考了karpathy 的博客Brownlees 的博客
  • 我在连接之前应用了第二个 LSTM 层,为此我应用了两个 LSTM 来捕获句子的联合含义。这不是 DSCNN 的意图,所以我们必须在连接之后应用一个 LSTM 层

到目前为止,代码如下所示:

sentence_inputs = [Input(shape=(max_sentence_len, embedding_dim,), name="input_" + str(i))
                   for i in range(max_sentences_per_doc)]

# LSTMs and Average Pooling (sentence-level)
shared_sentence_lstm = LSTM(units=embedding_dim, return_sequences=True, activation='tanh')
shared_average_pooling = AveragePooling1D(pool_size=max_sentence_len)
sentence_modeling = [shared_sentence_lstm(sentence_inputs[i]) for i in range(max_sentences_per_doc)]
sentence_modeling = [shared_average_pooling(sentence_modeling[i]) for i in range(max_sentences_per_doc)]

doc_modeling = Concatenate(axis=1)(sentence_modeling)
doc_modeling = LSTM(units=embedding_dim, activation='tanh', return_sequences=True)(doc_modeling)

具有多个过滤器大小的卷积层必须“手动”应用,因此我们在循环中运行卷积并在我们展平它们之后连接结果层(您也可以应用 GlobalMaxPooling,那么您不必展平层,但是这样你会丢失你的特征向量的许多信息)。

conv_blocks = []
for sz in kernel_sizes:
    conv = Convolution1D(filters=filters,
                         kernel_size=sz,
                         padding="valid",
                         activation="relu",
                         strides=1)(doc_modeling)
    conv = MaxPooling1D(pool_size=2)(conv)
    conv = Flatten()(conv)
    conv_blocks.append(conv)
doc_modeling = Concatenate()(conv_blocks) if len(conv_blocks) > 1 else conv_blocks[0]
  • 在此之后,我们通常可以应用激活层(例如 softmax 或 sigmoid,有或没有 dropout)并编译模型

问题 2 和 3:我们需要直接在批处理数据中嵌入或嵌入层。如果您将网络实现为已在批处理数据中嵌入,则只需生成一次。它适用于小数据集和小嵌入大小(小取决于您的内存大小等),但对于大数据集(例如每个文档 2000 个单词和 300 个嵌入维度),您会迅速超过 30gb 的内存。因此使用嵌入层绝对是有意义的,因为它只存储每个词向量一次。这个嵌入层的缺点是,它必须在每批之后执行词索引到词向量替换,这会花费一点运行时间,但由于它只是一个查找操作,所以成本不是很高。所以鼓励使用嵌入层。

问题4: 不会。首先,由于一些参数问题(例如,Conv1D 只接受一个整数作为 kernel_size),其次,它实现了另一种没有意义的架构(参见问题 1,TimeDistributed 层)。

问题 5:否 :-),见问题 4