了解用于 NLP 文本分类的 LSTM 和 RNN 中的词嵌入、卷积层和最大池化层
Understanding word embeddings, convolutional layer and max pooling layer in LSTMs and RNNs for NLP Text Classification
这是我的输入数据:
data['text'].head()
0 process however afforded means ascertaining di...
1 never occurred fumbling might mere mistake
2 left hand gold snuff box which capered hill cu...
3 lovely spring looked windsor terrace sixteen f...
4 finding nothing else even gold superintendent ...
Name: text, dtype: object
这里是一个热编码标签(multi-class class化,其中 classes = 3)
[[1 0 0]
[0 1 0]
[1 0 0]
...
[1 0 0]
[1 0 0]
[0 1 0]]
这是我一步一步想到的,如果有错请指正:
将我的输入文本data['text']
转换为一袋索引(序列)
vocabulary_size = 20000
tokenizer = Tokenizer(num_words = vocabulary_size)
tokenizer.fit_on_texts(data['text'])
sequences = tokenizer.texts_to_sequences(data['text'])
data = pad_sequences(sequences, maxlen=50)
正在发生的事情是我的 data['text'].shape
形状 (19579, )
正在被转换为形状为 (19579, 50)
的索引数组,其中每个单词都被找到的索引替换在 tokenizer.word_index.items()
加载glove 100d
词向量
embeddings_index = dict()
f = open('/Users/abhishekbabuji/Downloads/glove.6B/glove.6B.100d.txt')
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()
print(embedding_index)
{'the': array([-0.038194, -0.24487 , 0.72812 , -0.39961 , 0.083172, 0.043953,
-0.39141 , 0.3344 , -0.57545 , 0.087459, 0.28787 , -0.06731 ,
0.30906 , -0.26384 , -0.13231 , -0.20757 , 0.33395 , -0.33848 ,
-0.31743 , -0.48336 , 0.1464 , -0.37304 , 0.34577 , 0.052041,
0.44946 , -0.46971 , 0.02628 , -0.54155 , -0.15518 , -0.14107 ,
-0.039722, 0.28277 , 0.14393 , 0.23464 , -0.31021 , 0.086173,
0.20397 , 0.52624 , 0.17164 , -0.082378, -0.71787 , -0.41531 ,
0.20335 , -0.12763 , 0.41367 , 0.55187 , 0.57908 , -0.33477 ,
-0.36559 , -0.54857 , -0.062892, 0.26584 , 0.30205 , 0.99775 ,
-0.80481 , -3.0243 , 0.01254 , -0.36942 , 2.2167 , 0.72201 ,
-0.24978 , 0.92136 , 0.034514, 0.46745 , 1.1079 , -0.19358 ,
-0.074575, 0.23353 , -0.052062, -0.22044 , 0.057162, -0.15806 ,
-0.30798 , -0.41625 , 0.37972 , 0.15006 , -0.53212 , -0.2055 ,
-1.2526 , 0.071624, 0.70565 , 0.49744 , -0.42063 , 0.26148 ,
-1.538 , -0.30223 , -0.073438, -0.28312 , 0.37104 , -0.25217 ,
0.016215, -0.017099, -0.38984 , 0.87424 , -0.72569 , -0.51058 ,
-0.52028 , -0.1459 , 0.8278 , 0.27062 ], dtype=float32),
所以我们现在拥有的是每个 100 维单词的单词向量。
使用手套词向量创建嵌入矩阵
vocabulary_size = 20000
embedding_matrix = np.zeros((vocabulary_size, 100))
for word, index in tokenizer.word_index.items():
if index > vocabulary_size - 1:
break
else:
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
embedding_matrix[index] = embedding_vector
因此,对于 20000 个单词中的每个单词,我们现在都有 100 个维度的 vector
。
架构如下:
model_glove = Sequential()
model_glove.add(Embedding(vocabulary_size, 100, input_length=50, weights=[embedding_matrix], trainable=False))
model_glove.add(Dropout(0.5))
model_glove.add(Conv1D(64, 5, activation='relu'))
model_glove.add(MaxPooling1D(pool_size=4))
model_glove.add(LSTM(100))
model_glove.add(Dense(3, activation='softmax'))
model_glove.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model_glove.summary())
我明白了
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_7 (Embedding) (None, 50, 100) 2000000
_________________________________________________________________
dropout_7 (Dropout) (None, 50, 100) 0
_________________________________________________________________
conv1d_7 (Conv1D) (None, 46, 64) 32064
_________________________________________________________________
max_pooling1d_7 (MaxPooling1 (None, 11, 64) 0
_________________________________________________________________
lstm_7 (LSTM) (None, 100) 66000
_________________________________________________________________
dense_7 (Dense) (None, 3) 303
=================================================================
Total params: 2,098,367
Trainable params: 98,367
Non-trainable params: 2,000,000
_________________________________________________________________
上述架构的输入将是训练数据
array([[ 0, 0, 0, ..., 4867, 22, 340],
[ 0, 0, 0, ..., 12, 327, 2301],
[ 0, 0, 0, ..., 255, 388, 2640],
...,
[ 0, 0, 0, ..., 17, 15609, 15242],
[ 0, 0, 0, ..., 9517, 9266, 442],
[ 0, 0, 0, ..., 3399, 379, 5927]], dtype=int32)
形状(19579, 50)
并标记为一种热编码..
我的问题是了解以下每一行中我的 (19579, 50)
到底发生了什么:
model_glove = Sequential()
model_glove.add(Embedding(vocabulary_size, 100, input_length=50, weights=[embedding_matrix], trainable=False))
model_glove.add(Dropout(0.5))
model_glove.add(Conv1D(64, 5, activation='relu'))
model_glove.add(MaxPooling1D(pool_size=4))
明白为什么需要model_glove.add(Dropout(0.5))
,这是为了关闭一些概率为0.5的隐藏单元,避免模型过于复杂。但我不知道为什么我们需要 Conv1D(64, 5, activation='relu')
、MaxPooling1D(pool_size=4)
以及它如何进入我的 model_glove.add(LSTM(100))
单元..
理解 convolution
的最简单方法是将其视为一个映射,告诉神经网络哪些特征(图像识别中的像素,您将在其中使用 2D 卷积;或给定文本单词之前或之后的单词,您将在其中使用一维卷积)在附近。如果没有这个,网络就无法知道给定单词之前或之后的单词比距离更远的单词更相关。它通常还会导致信息以更密集的格式呈现,从而大大减少参数数量(在您的情况下从 200 万减少到 3 万)。我发现这个答案很好地解释了它如何工作的技术性:
Max pooling
是一种对数据进行下采样的方法。它经常在卷积之后直接使用,实现两件事:
- 它再次减少了参数的数量。在您的情况下,它将代表四个值和一个值(四个值中的最大值)。它通过获取前四个值,然后获取大小为 4 的 "stride" 并获取接下来的四个值等来实现这一点。换句话说,池之间不会重叠。 (这是 keras 默认情况下所做的,但您也可以将步幅设置为
2
例如)
- 其次,因为它取
max
值,理论上"sharpens"池之间的对比取最大值而不是例如取平均值。
最大池化不是"learnt";这只是一个简单的算术计算。这就是参数数量为零的原因。 dropout
.
也一样
LSTM
需要形状为 (number of samples, number of timesteps, number of features)
的三维输入。执行之前的卷积和最大池化步骤后,您已将初始嵌入的表示形式减少为 number of timesteps = 11
和 number of features = 64
。第一个值 number of samples = None
是您计划使用的 batch size
的占位符。通过使用 100 units
(也称为 hidden states
)初始化 LSTM,您正在参数化 LSTM "memory" 的大小:本质上是其输入、输出和遗忘门随时间的累积。
这是我的输入数据:
data['text'].head()
0 process however afforded means ascertaining di...
1 never occurred fumbling might mere mistake
2 left hand gold snuff box which capered hill cu...
3 lovely spring looked windsor terrace sixteen f...
4 finding nothing else even gold superintendent ...
Name: text, dtype: object
这里是一个热编码标签(multi-class class化,其中 classes = 3)
[[1 0 0]
[0 1 0]
[1 0 0]
...
[1 0 0]
[1 0 0]
[0 1 0]]
这是我一步一步想到的,如果有错请指正:
将我的输入文本
data['text']
转换为一袋索引(序列)vocabulary_size = 20000 tokenizer = Tokenizer(num_words = vocabulary_size) tokenizer.fit_on_texts(data['text']) sequences = tokenizer.texts_to_sequences(data['text']) data = pad_sequences(sequences, maxlen=50)
正在发生的事情是我的 data['text'].shape
形状 (19579, )
正在被转换为形状为 (19579, 50)
的索引数组,其中每个单词都被找到的索引替换在 tokenizer.word_index.items()
加载
glove 100d
词向量embeddings_index = dict() f = open('/Users/abhishekbabuji/Downloads/glove.6B/glove.6B.100d.txt') for line in f: values = line.split() word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefs f.close() print(embedding_index) {'the': array([-0.038194, -0.24487 , 0.72812 , -0.39961 , 0.083172, 0.043953, -0.39141 , 0.3344 , -0.57545 , 0.087459, 0.28787 , -0.06731 , 0.30906 , -0.26384 , -0.13231 , -0.20757 , 0.33395 , -0.33848 , -0.31743 , -0.48336 , 0.1464 , -0.37304 , 0.34577 , 0.052041, 0.44946 , -0.46971 , 0.02628 , -0.54155 , -0.15518 , -0.14107 , -0.039722, 0.28277 , 0.14393 , 0.23464 , -0.31021 , 0.086173, 0.20397 , 0.52624 , 0.17164 , -0.082378, -0.71787 , -0.41531 , 0.20335 , -0.12763 , 0.41367 , 0.55187 , 0.57908 , -0.33477 , -0.36559 , -0.54857 , -0.062892, 0.26584 , 0.30205 , 0.99775 , -0.80481 , -3.0243 , 0.01254 , -0.36942 , 2.2167 , 0.72201 , -0.24978 , 0.92136 , 0.034514, 0.46745 , 1.1079 , -0.19358 , -0.074575, 0.23353 , -0.052062, -0.22044 , 0.057162, -0.15806 , -0.30798 , -0.41625 , 0.37972 , 0.15006 , -0.53212 , -0.2055 , -1.2526 , 0.071624, 0.70565 , 0.49744 , -0.42063 , 0.26148 , -1.538 , -0.30223 , -0.073438, -0.28312 , 0.37104 , -0.25217 , 0.016215, -0.017099, -0.38984 , 0.87424 , -0.72569 , -0.51058 , -0.52028 , -0.1459 , 0.8278 , 0.27062 ], dtype=float32),
所以我们现在拥有的是每个 100 维单词的单词向量。
使用手套词向量创建嵌入矩阵
vocabulary_size = 20000 embedding_matrix = np.zeros((vocabulary_size, 100)) for word, index in tokenizer.word_index.items(): if index > vocabulary_size - 1: break else: embedding_vector = embeddings_index.get(word) if embedding_vector is not None: embedding_matrix[index] = embedding_vector
因此,对于 20000 个单词中的每个单词,我们现在都有 100 个维度的 vector
。
架构如下:
model_glove = Sequential()
model_glove.add(Embedding(vocabulary_size, 100, input_length=50, weights=[embedding_matrix], trainable=False))
model_glove.add(Dropout(0.5))
model_glove.add(Conv1D(64, 5, activation='relu'))
model_glove.add(MaxPooling1D(pool_size=4))
model_glove.add(LSTM(100))
model_glove.add(Dense(3, activation='softmax'))
model_glove.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model_glove.summary())
我明白了
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_7 (Embedding) (None, 50, 100) 2000000
_________________________________________________________________
dropout_7 (Dropout) (None, 50, 100) 0
_________________________________________________________________
conv1d_7 (Conv1D) (None, 46, 64) 32064
_________________________________________________________________
max_pooling1d_7 (MaxPooling1 (None, 11, 64) 0
_________________________________________________________________
lstm_7 (LSTM) (None, 100) 66000
_________________________________________________________________
dense_7 (Dense) (None, 3) 303
=================================================================
Total params: 2,098,367
Trainable params: 98,367
Non-trainable params: 2,000,000
_________________________________________________________________
上述架构的输入将是训练数据
array([[ 0, 0, 0, ..., 4867, 22, 340],
[ 0, 0, 0, ..., 12, 327, 2301],
[ 0, 0, 0, ..., 255, 388, 2640],
...,
[ 0, 0, 0, ..., 17, 15609, 15242],
[ 0, 0, 0, ..., 9517, 9266, 442],
[ 0, 0, 0, ..., 3399, 379, 5927]], dtype=int32)
形状(19579, 50)
并标记为一种热编码..
我的问题是了解以下每一行中我的 (19579, 50)
到底发生了什么:
model_glove = Sequential()
model_glove.add(Embedding(vocabulary_size, 100, input_length=50, weights=[embedding_matrix], trainable=False))
model_glove.add(Dropout(0.5))
model_glove.add(Conv1D(64, 5, activation='relu'))
model_glove.add(MaxPooling1D(pool_size=4))
明白为什么需要model_glove.add(Dropout(0.5))
,这是为了关闭一些概率为0.5的隐藏单元,避免模型过于复杂。但我不知道为什么我们需要 Conv1D(64, 5, activation='relu')
、MaxPooling1D(pool_size=4)
以及它如何进入我的 model_glove.add(LSTM(100))
单元..
理解 convolution
的最简单方法是将其视为一个映射,告诉神经网络哪些特征(图像识别中的像素,您将在其中使用 2D 卷积;或给定文本单词之前或之后的单词,您将在其中使用一维卷积)在附近。如果没有这个,网络就无法知道给定单词之前或之后的单词比距离更远的单词更相关。它通常还会导致信息以更密集的格式呈现,从而大大减少参数数量(在您的情况下从 200 万减少到 3 万)。我发现这个答案很好地解释了它如何工作的技术性:
Max pooling
是一种对数据进行下采样的方法。它经常在卷积之后直接使用,实现两件事:
- 它再次减少了参数的数量。在您的情况下,它将代表四个值和一个值(四个值中的最大值)。它通过获取前四个值,然后获取大小为 4 的 "stride" 并获取接下来的四个值等来实现这一点。换句话说,池之间不会重叠。 (这是 keras 默认情况下所做的,但您也可以将步幅设置为
2
例如) - 其次,因为它取
max
值,理论上"sharpens"池之间的对比取最大值而不是例如取平均值。
最大池化不是"learnt";这只是一个简单的算术计算。这就是参数数量为零的原因。 dropout
.
LSTM
需要形状为 (number of samples, number of timesteps, number of features)
的三维输入。执行之前的卷积和最大池化步骤后,您已将初始嵌入的表示形式减少为 number of timesteps = 11
和 number of features = 64
。第一个值 number of samples = None
是您计划使用的 batch size
的占位符。通过使用 100 units
(也称为 hidden states
)初始化 LSTM,您正在参数化 LSTM "memory" 的大小:本质上是其输入、输出和遗忘门随时间的累积。