Tensorflor实现文本分类

Tensorflor实现文本分类

下面我们使用CNN做文本分类

cnn实现文本分类的原理

下图展示了如何使用cnn进行句子分类。输入是一个句子,为了使其可以进行卷积,首先需要将其转化为向量表示,通常使用word2vec实现。d=5表示每个词转化为5维的向量,矩阵的形状是[sentence_length × 5],即[7 ×5]。6个filter(卷积核),与图像中使用的卷积核不同的是,nlp使用的卷积核的宽与句子矩阵的宽相同,只是长度不同。这里有(2,3,4)三种size,每种size有两个filter,一共有6个filter。然后开始卷积,从图中可以看出,stride是1,因为对于高是4的filter,最后生成4维的向量,(7-4)/1+1=4。对于高是3的filter,最后生成5维的向量,(7-3)/1+1=5。卷积之后,我们得到句子的特征,使用activation function和1-max-pooling得到最后的值,每个filter最后得到两个特征。将所有特征合并后,使用softmax进行分类。图中没有用到chanel,下文的实验将会使用两个通道,static和non-static,有相关的具体解释。

本文使用的模型

主要包括五层,第一层是embedding layer,第二层是convolutional layer,第三层是max-pooling layer,第四层是fully connected layer,最后一层是softmax layer.接下来依次介绍相关代码实现。

Input placeholder

# Placeholders for input, output and dropout

self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x")

self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y")

self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")

tf.placeholder 创建一个占位符变量,在训练或者测试的时候,需要将占位符输入到网络中进行计算,其中的第二个参数是输入张量的形状。None 意味着它可以是任何维度的长度,在我们的实验中它代表批处理的大小,None使得网络可以处理任意长度的batches。 
失活率同样也是输入的一部分,在训练的时候使用dropout ,测试的时候不使用dropout 。

EMBEDDING LAYER

这一层将单词索引映射到低维的向量表示,它本质上是一个查找表,我们从数据中通过学习得到。

with tf.device(‘/cpu:0‘), tf.name_scope("embedding"):

W = tf.Variable(tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0), name="W")

self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x)

self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)

其中,W 是 在训练时得到的embedding matrix.,用随机均匀分布进行初始化。tf.nn.embedding_lookup 实现embedding操作,得到 一个3-dimensional 的张量,形状是 [None, sequence_length, embedding_size].sequence_length 是数据集中最长句子的长度,其他句子都通过添加“PAD”补充到这个长度。embedding_size是词向量的大小。

TensorFlow的卷积函数-conv2d 需要四个参数, 分别是batch, width, height 以及channel。 embedding之后不包括 channel, 所以我们人为地添加上它,并设置为1。现在就是[None, sequence_length, embedding_size, 1]

CONVOLUTION AND MAX-POOLING LAYERS

由图中可知, 我们有不同size的filters。因为每次卷积都会产生不同形状的张量,所以我们要遍历每个filter,然后将结果合并成一个大的特征向量。

pooled_outputs = []

for i, filter_size in enumerate(filter_sizes):

with tf.name_scope("conv-maxpool-%s" % filter_size):

# Convolution Layer

filter_shape = [filter_size, embedding_size, 1, num_filters]

W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")

b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")

conv = tf.nn.conv2d(

self.embedded_chars_expanded,

W,

strides=[1, 1, 1, 1],

padding="VALID",

name="conv")

# Apply nonlinearity

h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")

# Max-pooling over the outputs

pooled = tf.nn.max_pool(

h,

ksize=[1, sequence_length - filter_size + 1, 1, 1],

strides=[1, 1, 1, 1],

padding=‘VALID‘,

name="pool")

pooled_outputs.append(pooled)

# Combine all the pooled features

num_filters_total = num_filters * len(filter_sizes)

self.h_pool = tf.concat(3, pooled_outputs)

self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])

这里W 是filter 矩阵,h 是对卷积结果进行非线性转换之后的结果。每个 filter都从整个embedding划过,不同之处在于覆盖多少单词。 “VALID” padding意味着没有对句子的边缘进行padding,也就是用了narrow convolution,输出的形状是 [1, sequence_length - filter_size + 1, 1, 1]。narrow convolution与 wide convolution的区别是是否对边缘进行填充。例如,对一个有7个词的句子来说, filter size是5,使用narrow convolution,输出的大小是(7-5)+1=3;使用wide convolution,输出的大小是(7+2*4-5)+1=11.

对输出进行max-pooling后得到形状是 [batch_size, 1, 1, num_filters] 的张量,本质上是一个特征向量,最后一个维度是特征代表数量。把每一个max-pooling之后的张量合并起来之后得到一个长向量[batch_size, num_filters_total]. in tf.reshape 中的 -1表示T将向量展平。

DROPOUT LAYER

Dropout也许是cnn中最流行的正则化方法。dropout的想法很简单,dropout layer随机地选择一些神经元,使其失活。这样可以阻止co-adapting,迫使它们每一个都学习到有用的特征。失活的神经单元个数由dropout_keep_prob 决定。在训练的时候设为 0.5 ,测试的时候设为 1 (disable dropout) .

# Add dropout

with tf.name_scope("dropout"):

self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)

SCORES AND PREDICTIONS

利用特征向量,我们可以用矩阵相乘计算两类的得分,也可以用 softmax函数计算两类的概率值。

with tf.name_scope("output"):

W = tf.Variable(tf.truncated_normal([num_filters_total, num_classes], stddev=0.1), name="W")

b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")

self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")

self.predictions = tf.argmax(self.scores, 1, name="predictions")

LOSS AND ACCURACY

可以用得分定义损失值。损失计算的是网络的误差,我们的目标是将其最小化,分类问题标准的损失函数是交叉熵损失。

# Calculate mean cross-entropy loss

with tf.name_scope("loss"):

losses = tf.nn.softmax_cross_entropy_with_logits(self.scores, self.input_y)

self.loss = tf.reduce_mean(losses)

计算正确率

# Calculate Accuracy

with tf.name_scope("accuracy"):

correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))

self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")

MINIMIZING THE LOSS

利用TensorFlow 内置的optimizers,例如 Adam optimizer,优化网络损失。

global_step = tf.Variable(0, name="global_step", trainable=False)

optimizer = tf.train.AdamOptimizer(1e-4)

grads_and_vars = optimizer.compute_gradients(cnn.loss)

train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)

train_op 是一个新建的操作,我们可以在参数上进行梯度更新。每执行一次 train_op 就是一次训练步骤。 TensorFlow 可以自动地计算才那些变量是“可训练的”然后计算他们的梯度。通过global_step 这个变量可以计算训练的步数,每训练一次自动加一。

CHECKPOINTING

TensorFlow 中可以用checkpointing 保存模型的参数。checkpointing中的参数也可以用来继续训练。

# Checkpointing

checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))

checkpoint_prefix = os.path.join(checkpoint_dir, "model")

# Tensorflow assumes this directory already exists so we need to create it

if not os.path.exists(checkpoint_dir):

os.makedirs(checkpoint_dir)

saver = tf.train.Saver(tf.all_variables())

DEFINING A SINGLE TRAINING STEP

用一个batch的数据进行一次训练。

def train_step(x_batch, y_batch):

"""

A single training step

"""

feed_dict = {

cnn.input_x: x_batch,

cnn.input_y: y_batch,

cnn.dropout_keep_prob: FLAGS.dropout_keep_prob

}

_, step, summaries, loss, accuracy = sess.run(

[train_op, global_step, train_summary_op, cnn.loss, cnn.accuracy],

feed_dict)

time_str = datetime.datetime.now().isoformat()

print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))

train_summary_writer.add_summary(summaries, step)

train_op 什么也不返回,只是更新网络中的参数。最终,打印出当前训练的损失值与正确率。如果batch的size很小的话,这两者在不同的batch中差别很大。因为使用了dropout,训练的metrics可能要比测试的metrics糟糕。

同样的函数也可以用在测试时。

def dev_step(x_batch, y_batch, writer=None):

"""

Evaluates model on a dev set

"""

feed_dict = {

cnn.input_x: x_batch,

cnn.input_y: y_batch,

cnn.dropout_keep_prob: 1.0

}

step, summaries, loss, accuracy = sess.run(

[global_step, dev_summary_op, cnn.loss, cnn.accuracy],

feed_dict)

time_str = datetime.datetime.now().isoformat()

print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))

if writer:

writer.add_summary(summaries, step)

TRAINING LOOP

通过迭代数据进行训练。

# Generate batches

batches = data_helpers.batch_iter(

zip(x_train, y_train), FLAGS.batch_size, FLAGS.num_epochs)

# Training loop. For each batch...

for batch in batches:

x_batch, y_batch = zip(*batch)

train_step(x_batch, y_batch)

current_step = tf.train.global_step(sess, global_step)

if current_step % FLAGS.evaluate_every == 0:

print("\nEvaluation:")

dev_step(x_dev, y_dev, writer=dev_summary_writer)

print("")

if current_step % FLAGS.checkpoint_every == 0:

path = saver.save(sess, checkpoint_prefix, global_step=current_step)

print("Saved model checkpoint to {}\n".format(path))

VISUALIZING RESULTS IN TENSORBOARD

从上图中我们可以观察到:

  • 我们的训练 metrics不平滑,因为用的batch sizes很小。如果用大的batches (或者在整个测试集上进行评估),会得到平滑的线。
  • 测试集的 accuracy明显比训练集的低,说明网络过拟合了,我们应该用更大的数据集,更强的正则化,更少的模型参数。
  • 训练集上的 loss 和 accuracy比测试集低的原因是用了dropout.
时间: 2024-10-11 11:20:57

Tensorflor实现文本分类的相关文章

Spark ML下实现的多分类adaboost+naivebayes算法在文本分类上的应用

1. Naive Bayes算法 朴素贝叶斯算法算是生成模型中一个最经典的分类算法之一了,常用的有Bernoulli和Multinomial两种.在文本分类上经常会用到这两种方法.在词袋模型中,对于一篇文档$d$中出现的词$w_0,w_1,...,w_n$, 这篇文章被分类为$c$的概率为$$p(c|w_0,w_1,...,w_n) = \frac{p(c,w_0,w_1,...,w_n)}{p(w_0,w_1,...,w_n)} = \frac{p(w_0,w_1,...,w_n|c)*p(c

LingPipe-TextClassification(文本分类)

What is Text Classification? Text classification typically involves assigning a document to a category by automated or human means. LingPipe provides a classification facility that takes examples of text classifications--typically generated by a huma

使用libsvm实现文本分类

文本分类,首先它是分类问题,应该对应着分类过程的两个重要的步骤,一个是使用训练数据集训练分类器,另一个就是使用测试数据集来评价分类器的分类精度.然而,作为文本分类,它还具有文本这样的约束,所以对于文本来说,需要额外的处理过程,我们结合使用libsvm从宏观上总结一下,基于libsvm实现文本分类实现的基本过程,如下所示: 选择文本训练数据集和测试数据集:训练集和测试集都是类标签已知的: 训练集文本预处理:这里主要包括分词.去停用词.建立词袋模型(倒排表): 选择文本分类使用的特征向量(词向量):

文本分类--多分类

文本分类算是自然语言处理领域最最常见的问题了,开源的工具也很好用,但是苦于训练速度缓慢,需要引进多核的版本,开源提供的多核支持参数有限,而同事提供的又有语言障碍,觉得自己探索下多分类器. 分类算法有很多,但是效果较好的基本就是LR和SVM,而这两个算法业内著名的开源代码应该就是liblinear和libsvm,libsvm支不支持多核暂时还未了解,但是liblinear支持的多核版本也就三组(0.2.11),正好避开了我需要用的那组参数,于是就摸索下liblinear的train代码. 一.先说

基于weka的文本分类实现

weka介绍 参见 1)百度百科:http://baike.baidu.com/link?url=V9GKiFxiAoFkaUvPULJ7gK_xoEDnSfUNR1woed0YTmo20Wjo0wYo7uff4mq_wg3WzKhTZx4Ok0JFgtiYY19U4q 2)weka官网: http://www.cs.waikato.ac.nz/ml/weka/ 简单文本分类实现: 此处文本为已处理好的文本向量空间模型,关于文本特征提取主要是基于TF-IDF算法对已分词文档进行特征抽取,然后基于

统计学习方法文本分类

一个文本分类问题就是将一篇文档归入预先定义的几个类别中的一个或几个,而文本的自动分类则是使用计算机程序来实现这样的分类.通俗点说,就好比你拿一篇文章,问计算机这文章要说的究竟是体育,经济还是教育,计算机答不上,说明计算机弱爆了就打它的屁屁. 注意这个定义当中着重强调的两个事实. 第一,用于分类所需要的类别体系是预先确定的.例如新浪新闻的分类体系,Yahoo!网页导航的分类层次.这种分类层次一旦确定,在相当长的时间内都是不可变的,或者即使要变更,也要付出相当大的代价(基本不亚于推倒并重建一个分类系

机器学习经典算法详解及Python实现---朴素贝叶斯分类及其在文本分类、垃圾邮件检测中的应用

摘要: 朴素贝叶斯分类是贝叶斯分类器的一种,贝叶斯分类算法是统计学的一种分类方法,利用概率统计知识进行分类,其分类原理就是利用贝叶斯公式根据某对象的先验概率计算出其后验概率(即该对象属于某一类的概率),然后选择具有最大后验概率的类作为该对象所属的类.总的来说:当样本特征个数较多或者特征之间相关性较大时,朴素贝叶斯分类效率比不上决策树模型:当各特征相关性较小时,朴素贝叶斯分类性能最为良好.另外朴素贝叶斯的计算过程类条件概率等计算彼此是独立的,因此特别适于分布式计算.本文详述了朴素贝叶斯分类的统计学

如何用机器学习对文本分类

需求 使用监督学习对历史数据训练生成模型,用于预测文本的类别. 样本清洗 主要将重复的数据删除掉,将错误无效的数据纠正或删除,并检查数据的一致性等.比如我认为长度小于少于13的数据是无效的遂将之删掉. def writeFile(text): file_object = open('result.txt','w') file_object.write(text) file_object.close() def clear(): text = "" file_obj = open(&qu

NLP系列(3)_用朴素贝叶斯进行文本分类(下)

作者: 龙心尘 && 寒小阳 时间:2016年2月. 出处: http://blog.csdn.net/longxinchen_ml/article/details/50629110 http://blog.csdn.net/han_xiaoyang/article/details/50629587 声明:版权所有,转载请联系作者并注明出处 1. 引言 上一篇文章我们主要从理论上梳理了朴素贝叶斯方法进行文本分类的基本思路.这篇文章我们主要从实践上探讨一些应用过程中的tricks,并进一步分