1. cnn语言模型
先大概了解一下cnn语言模型的基本原理。
cnn语言模型的基本原理和cnn图像模型类似,也是先通过卷积层提取特征,然后通过池化层减少神经元数量,最后通过类似softmax层输出类别可能性。
不同点在于数据结构不一样,图像数据是3维度的,长、宽和通道数;而语言模型是2维的,是句子的长度和每个词的词向量长度。图像卷积一般用tf.nn.conv2d;而文本卷积一般用conv1d。
词向量卷积的时候通常利用多个长度不一的卷积核来提取特征。我们如果分别以3个连续单词、4个连续单词、5个连续单词为卷积核大小来提取特征,那么我们往往可以认为这种特征提取已经非常接近句子的意思了。语言模型的cnn原理大概就是这个样子了。
Cnn语言模型将多个不同大小的卷积核连起来作为特征提取,如3、4、5作为卷积核大小来提取特征,最后展开成总的特征向量。
我们以IMDB数据集来训练cnn文本模型,IMDB数据集是影戏评论的情绪数据,它的标签是2分类的,正面情绪和负面情绪。
2. 常量定义
首先我们定义一些常量。
vocab_size=10000 #词总数大小
max_seq_num = 256 #一个句子的最多单词数量
num_dimensions = 50 #词向量空间维度
batch_size = 64 # batch的尺寸
filter_sizes = [3,4,5] #conv1d的3种卷积核尺寸
num_filters = 32 # 卷积核数目
num_classes = 2 # 输出的类别数
iterations = 10000 # 迭代的次数
dropout_keep_prob = 0.5 # dropout保留比例
learning_rate = 1e-3
3. 下载数据集
下载训练数据集。tensorflow提供了现成的代码做这事(集成了keras)。
import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
import numpy as np
import os
import random
from tensorflow import keras
from tensorflow.python.ops.rnn import static_rnn
from tensorflow.python.ops.rnn_cell_impl import BasicLSTMCell
#1. download imdb data and show data examples
imdb = keras.datasets.imdb
# 参数num_words = 10000 表示数据集将会保留出现频率在前10000的单词
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=vocab_size)
print('imdb data lens:%d,%d'%(len(train_data[0]), len(train_data[1])))
print("Training entries: {}, labels: {}".format(len(train_data), len(train_labels)))
下载后数据会放到~/.keras/datasets/目录,也可以直接将imdb.npz文件直接放到该目录下,节省下载时间。这里的数据集是不定长的句子,比如有的句子是40个单词,而有的句子是200个单词,长度不等。一般对原始句子还要经过Embedding层和填充层进行预处理,将句子装换成固定长度的词向量,才能进行神经网络的训练。
4. 预处理
文本预处理主要是填充成固定长度的向量,填充的单词序号通常是表示为0的PAD字符。
#扩展整数数组让他们拥有相同的长度,这样每一个sequence会有共同的max_length(256),总计会占用 max_length*num_ dimensions大小,在sequence后面扩充0
train_data = keras.preprocessing.sequence.pad_sequences(train_data,maxlen=max_seq_num)
test_data = keras.preprocessing.sequence.pad_sequences(test_data,maxlen=max_seq_num)
5. 定义网络结构
首先定义输入图片数据和label的占位符:
tf.reset_default_graph()
X_holder = tf.placeholder(tf.int32, [None, max_seq_num])
Y_holder = tf.placeholder(tf.int32, [None])
然后定义Embedding层。Embedding是指将总数vocab_size的词空间(one-hot形式)映射到num_dimensions维度中去。
embedding = tf.get_variable('embedding', [vocab_size, num_dimensions])
embedding_inputs = tf.nn.embedding_lookup(embedding, X_holder)
接着就是卷积池化层,用[3,4,5]的卷积核来提取文本特征。
# conv1d方式卷积
pooled_outputs = []
for i, filter_size in enumerate(filter_sizes):
with tf.name_scope("conv_{0}".format(filter_size)):
# CNN layer
conv = tf.layers.conv1d(embedding_inputs, num_filters, filter_size, name='conv-%s' % filter_size)
# global max pooling layer
pooled = tf.reduce_max(conv, reduction_indices=[1], name='gmp')
pooled_outputs.append(pooled)
最后将多种卷积核提取的特征向量展开并连接在一起,最后加全连接层输出类别。
# 将每种尺寸的卷积核得到的特征向量进行拼接
num_filters_total = num_filters * len(filter_sizes)
h_pool = tf.concat(pooled_outputs, 1)
h_pool_flat = tf.reshape(h_pool, [-1, num_filters_total])
# 对最终得到的句子向量进行dropout
with tf.name_scope("dropout"):
h_drop = tf.nn.dropout(h_pool_flat, dropout_keep_prob)
# 全连接层
with tf.name_scope("output"):
W = tf.get_variable("W",shape=[num_filters_total, num_classes],
initializer=tf.contrib.layers.xavier_initializer())
b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")
l2_loss += tf.nn.l2_loss(W)+tf.nn.l2_loss(b)
logits = tf.nn.xw_plus_b(h_drop, W, b, name="scores")
pred = tf.argmax(logits, 1, name="predictions")
print("shape:%s ,%s"%(h_pool_flat.shape,logits.shape))
6. 损失函数
接着我们要定义损失函数了。
损失函数就是训练数据的标签Y_holder和神经网络输出的最大可能性的标签的交叉熵。损失函数定义如下。
# 损失函数
l2_lambda=0.3
with tf.name_scope("loss"):
losses = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=tf.one_hot(Y_holder, num_classes),) + l2_lambda*l2_loss
mean_loss = tf.reduce_mean(losses)
7.优化函数训练
最后一步就是选择合适的优化函数进行训练了,这部分代码按照常见的套路来做就行了,没有什么特别之处。
# 优化函数
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(mean_loss)
# 准确率
predict_Y = tf.argmax(logits, axis=1)
isCorrect = tf.equal(Y_holder, tf.cast(predict_Y, dtype=tf.int32))
accuracy = tf.reduce_mean(tf.cast(isCorrect, tf.float32))
init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
steps = np.zeros(iterations)
LOSS = np.zeros_like(steps)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print ("begin training")
for step in range(iterations):
selected_index = np.random.choice(len(train_data),size=batch_size)
batch_X = train_data[selected_index]
batch_Y = train_labels[selected_index]
feed_dict = {
X_holder: batch_X,
Y_holder: batch_Y
}
_, mean_loss_val,accuracy_value = sess.run([optimizer, mean_loss,accuracy], feed_dict=feed_dict)
steps[step]=step
LOSS[step]=accuracy_value
if step%10 == 0:
print ("step = {}\t mean loss ={} acc ={}".format(step, mean_loss_val,accuracy_value))