我在阅读这个链接,试图理解用于文本分类的多通道CNN模型。
代码基于这个教程。
我已经理解了大部分内容,但是我无法理解Keras如何定义某些层的输出形状。
这是代码:
定义一个模型,该模型具有三个输入通道,用于处理电影评论文本的4-gram、6-gram和8-gram。
#Skipped keras imports# 加载一个干净的数据集def load_dataset(filename): return load(open(filename, 'rb'))# 拟合一个分词器def create_tokenizer(lines): tokenizer = Tokenizer() tokenizer.fit_on_texts(lines) return tokenizer# 计算最大文档长度def max_length(lines): return max([len(s.split()) for s in lines])# 编码一组行def encode_text(tokenizer, lines, length): # 整数编码 encoded = tokenizer.texts_to_sequences(lines) # 填充编码序列 padded = pad_sequences(encoded, maxlen=length, padding='post') return padded# 定义模型def define_model(length, vocab_size): # 通道1 inputs1 = Input(shape=(length,)) embedding1 = Embedding(vocab_size, 100)(inputs1) conv1 = Conv1D(filters=32, kernel_size=4, activation='relu')(embedding1) drop1 = Dropout(0.5)(conv1) pool1 = MaxPooling1D(pool_size=2)(drop1) flat1 = Flatten()(pool1) # 通道2 inputs2 = Input(shape=(length,)) embedding2 = Embedding(vocab_size, 100)(inputs2) conv2 = Conv1D(filters=32, kernel_size=6, activation='relu')(embedding2) drop2 = Dropout(0.5)(conv2) pool2 = MaxPooling1D(pool_size=2)(drop2) flat2 = Flatten()(pool2) # 通道3 inputs3 = Input(shape=(length,)) embedding3 = Embedding(vocab_size, 100)(inputs3) conv3 = Conv1D(filters=32, kernel_size=8, activation='relu')(embedding3) drop3 = Dropout(0.5)(conv3) pool3 = MaxPooling1D(pool_size=2)(drop3) flat3 = Flatten()(pool3) # 合并 merged = concatenate([flat1, flat2, flat3]) # 解释 dense1 = Dense(10, activation='relu')(merged) outputs = Dense(1, activation='sigmoid')(dense1) model = Model(inputs=[inputs1, inputs2, inputs3], outputs=outputs) # 编译 model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) # 总结 print(model.summary()) plot_model(model, show_shapes=True, to_file='multichannel.png') return model# 加载训练数据集trainLines, trainLabels = load_dataset('train.pkl')# 创建分词器tokenizer = create_tokenizer(trainLines)# 计算最大文档长度length = max_length(trainLines)# 计算词汇量大小vocab_size = len(tokenizer.word_index) + 1print('最大文档长度: %d' % length)print('词汇量大小: %d' % vocab_size)# 编码数据trainX = encode_text(tokenizer, trainLines, length)print(trainX.shape)# 定义模型model = define_model(length, vocab_size)# 拟合模型model.fit([trainX,trainX,trainX], array(trainLabels), epochs=10, batch_size=16)# 保存模型model.save('model.h5')
运行代码:
运行示例首先打印了准备好的训练数据集的摘要。最大文档长度: 1380词汇量大小: 44277(1800, 1380)
____________________________________________________________________________________________________层 (类型) 输出形状 参数数 连接到====================================================================================================input_1 (InputLayer) (None, 1380) 0____________________________________________________________________________________________________input_2 (InputLayer) (None, 1380) 0____________________________________________________________________________________________________input_3 (InputLayer) (None, 1380) 0____________________________________________________________________________________________________embedding_1 (Embedding) (None, 1380, 100) 4427700 input_1[0][0]____________________________________________________________________________________________________embedding_2 (Embedding) (None, 1380, 100) 4427700 input_2[0][0]____________________________________________________________________________________________________embedding_3 (Embedding) (None, 1380, 100) 4427700 input_3[0][0]____________________________________________________________________________________________________conv1d_1 (Conv1D) (None, 1377, 32) 12832 embedding_1[0][0]____________________________________________________________________________________________________conv1d_2 (Conv1D) (None, 1375, 32) 19232 embedding_2[0][0]____________________________________________________________________________________________________conv1d_3 (Conv1D) (None, 1373, 32) 25632 embedding_3[0][0]____________________________________________________________________________________________________dropout_1 (Dropout) (None, 1377, 32) 0 conv1d_1[0][0]____________________________________________________________________________________________________dropout_2 (Dropout) (None, 1375, 32) 0 conv1d_2[0][0]____________________________________________________________________________________________________dropout_3 (Dropout) (None, 1373, 32) 0 conv1d_3[0][0]____________________________________________________________________________________________________max_pooling1d_1 (MaxPooling1D) (None, 688, 32) 0 dropout_1[0][0]____________________________________________________________________________________________________max_pooling1d_2 (MaxPooling1D) (None, 687, 32) 0 dropout_2[0][0]____________________________________________________________________________________________________max_pooling1d_3 (MaxPooling1D) (None, 686, 32) 0 dropout_3[0][0]____________________________________________________________________________________________________flatten_1 (Flatten) (None, 22016) 0 max_pooling1d_1[0][0]____________________________________________________________________________________________________flatten_2 (Flatten) (None, 21984) 0 max_pooling1d_2[0][0]____________________________________________________________________________________________________flatten_3 (Flatten) (None, 21952) 0 max_pooling1d_3[0][0]____________________________________________________________________________________________________concatenate_1 (Concatenate) (None, 65952) 0 flatten_1[0][0] flatten_2[0][0] flatten_3[0][0]____________________________________________________________________________________________________dense_1 (Dense) (None, 10) 659530 concatenate_1[0][0]____________________________________________________________________________________________________dense_2 (Dense) (None, 1) 11 dense_1[0][0]====================================================================================================总参数: 14,000,337可训练参数: 14,000,337不可训练参数: 0____________________________________________________________________________________________________
还有
轮次 6/101800/1800 [==============================] - 30s - 损失: 9.9093e-04 - 准确率: 1.0000轮次 7/101800/1800 [==============================] - 29s - 损失: 5.1899e-04 - 准确率: 1.0000轮次 8/101800/1800 [==============================] - 28s - 损失: 3.7958e-04 - 准确率: 1.0000轮次 9/101800/1800 [==============================] - 29s - 损失: 3.0534e-04 - 准确率: 1.0000轮次 10/101800/1800 [==============================] - 29s - 损失: 2.6234e-04 - 准确率: 1.0000
我对层和输出形状的理解如下:请帮助我确认这些是否正确,因为我在多维度上感到迷惑。
input_1 (InputLayer) (None, 1380) : —> 1380
是每个数据点的特征总数(即1380个输入神经元)。1800
是文档或数据点的总数。
embedding_1 (Embedding) (None, 1380, 100) 4427700 —-> 嵌入层是:1380作为特征(词),每个特征是一个100维的向量。
这里的参数数目是4427700
是怎么计算的?
conv1d_1 (Conv1D) (None, 1377, 32) 12832 ——> Conv1d的kernel size=4
。它是1*4
的滤波器,使用了32
次。那么维度怎么变成了(None, 1377, 32)
,参数数目是12832
?
max_pooling1d_1 (MaxPooling1D) (None, 688, 32) 使用MaxPooling1D(pool_size=2)维度怎么变成了(None, 688, 32)
?flatten_1 (Flatten) (None, 22016) 这是688和32的乘积吗?
** 每个轮次是否一次训练1800个数据点?**
请告诉我如何计算输出维度。任何参考或帮助都将不胜感激。
回答:
请查看下面的答案:
input_1 (InputLayer) (None, 1380)
: —> 1380 是每个数据点的特征总数(即1380个输入神经元)。1800 是文档或数据点的总数。
是的。model.fit([trainX,trainX,trainX], array(trainLabels), epochs=10, batch_size=16)
表示,你希望网络在整个训练数据集上训练10次(10个轮次),每次以16为批次大小。
这意味着,每16个数据点,反向传播算法就会启动并且权重会更新。这将发生1800/16
次,称为一个轮次。
1380
是第一层的neurons数目。
embedding_1 (Embedding) (None, 1380, 100) | 4427700
—-> 嵌入层是:1380作为特征(词),每个特征是一个100维的向量。
1380 是输入的大小(前一层的neurons数目),100 是嵌入向量的长度(大小)。
这里的参数数目是vocabulary_size * 100
,因为对于每个v in vocabulary
,你需要训练100个参数。嵌入层实际上是由词汇量大小的100维向量构成的矩阵,每一行代表词汇中每个词的向量表示。
conv1d_1 (Conv1D) (None, 1377, 32) | 12832
——> Conv1d的kernel size=4
。它是1*4
的滤波器,使用了32
次。那么维度怎么变成了(None, 1377, 32)
,参数数目是12832
?
1380 变为 1377 是由于kernel的大小。想象一下以下输入(为了简化,取大小为10)以及kernel大小为4的情况:
0123456789 #输入KKKK456789 0KKKK56789 12KKKK6789 123KKKK789 1234KKKK89 12345KKKK9123456KKKK
看,Kernel无法再向右移动,因此对于输入大小10和Kernel大小4,输出形状将是7。一般来说,对于输入形状n和kernel形状k,输出形状将是n - k + 1
,所以对于n=1380, k=4
结果是1377
。
参数数目等于12832,因为参数数目等于output_channels * (input_channels * window_size + 1)
。在你的情况下是32*(100*4 + 1)
。
max_pooling1d_1 (MaxPooling1D) (None, 688, 32) with MaxPooling1D(pool_size=2)
维度怎么变成了(None, 688, 32)
?
max_pooling
每两个连续的数取最大值,因此你最终会得到original_size/pool_size
个值。
flatten_1 (Flatten) (None, 22016)
这是688和32的乘积吗?
是的,这是688和32的乘积。因为,flatten操作做的是以下事情:
12345678 -> 1234567890129012
所以它将所有维度的值放入一个一维向量中。
每个轮次是否一次训练1800个数据点?
不是。它以16为批次大小来处理数据,正如第一个答案中指出的那样。每个轮次以随机顺序处理1800个数据点,每次处理16个数据点。一个轮次是一个时间段,之后我们将重新开始读取数据。
编辑:
我将澄清1d卷积层应用于嵌入层的地方。
你应该将嵌入层的输出解释为宽度为1380和100个通道的向量。
类似于2d图像,你有一个输入的RGB图像,有三个通道,它的形状是(width, height, 3),当你应用由32个滤波器构成的卷积层时(滤波器大小不相关),卷积操作同时应用于所有通道,输出形状将是(new_width, new_height, 32)。注意输出形状与滤波器数量相同。
回到你的例子。将嵌入层的输出形状视为(width, channels)。因此,1d卷积层具有32个滤波器且kernel大小等于4,应用于宽度1380和深度100的向量。结果,你将得到形状为(1377, 32)的输出。