Tensorflow文本分类练习
初学tensorflow,借鉴了很多别人的经验,参考博客对评论分类(感谢博主的一系列好文),本人也尝试着实现了对文本数据的分类。
1、数据
这里借用此博客提供的负类数据和正类数据对程序进行验证(再次感谢此博主)。这些数据的每一个样本是对电影的正面或负面的评价。
2、nltk包的安装和使用
对文本数据进行处理,需要借助自然语言处理包NLTK (Natural Language Toolkit) 对每一个样本进行预处理。
(1) 安装 nltk
nltk的安装可采用如下代码:
# pip install nltk
(2) 下载 nltk data, 这是必要的nltk数据包,实现分词、词性标注、命名实体识别等功能都要用到这个数据包
$ python # 进入python
>>> import nltk # 导入nltk
>>> nltk.download() # 下载 nltk data
注意 nltk.download() 会弹出 NLTK Downloader 下载界面,此过程持续时间较长,请耐心等待。
下载过程中可能会出现某些 package 下载失败的情况,此时可点击 All Packages 标签,进而双击下载失败的 package 可单独下载,一般情况下都能下载成功。如果依然有问题,可移步至 nltk data 进行手动下载。
(3) 测试 nltk 是否安装成功
$ python
from nltk.book import *
* **
text1
…
…
…
text9
若出现以上结果,则恭喜你, nltk 安装成功。
3. 分类代码实现
# 用 one-hot vector 表示每个评论样本
import numpy as np
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from collections import Counter
import tensorflow as tf
import random
# 从文件中获得所包含的所有单词,以及每句话所包含的单词列表
def _get_words(fname):
with open(fname, 'rb') as f:
lines = f.readlines()
lex = [] # 文件中包含的所有单词
wwords = [] # 每句话包含的单词列表
for line in lines:
try:
# 将每个句子拆成一个个单词
words = word_tokenize(line.lower())
# 将每个词还原成原始词性
lemmatizer = WordNetLemmatizer()
words = [lemmatizer.lemmatize(word) for word in words]
lex += words
wwords.append(words)
except:
pass
return lex, wwords
# 汇集所有文件所包含的词,生成词典
def get_dict(fnames):
lexs = []
wwords = []
for fname in fnames:
tmp_lex, tmp_words = _get_words(fname)
lexs += tmp_lex
wwords.append(tmp_words)
word_count = Counter(lexs)
dicts = []
# 去掉常见词和生僻词,也可用比例进行限制
for word in word_count:
if word_count[word] >= 6000 or word_count[word] <= 200:
continue
dicts.append(word)
return dicts, wwords
# 将每个样本用向量表示,向量长度=词典长度,样本中出现的词在词典的对应位置用1表示,否则用0表示
def vector_file(dicts, wwords, tags):
datas =[]
for words, tag in zip(wwords, tags):
codes = np.zeros((len(words), len(dicts)))
for line_id, line_words in enumerate(words):
for ix, word in enumerate(line_words):
if word in dicts:
codes[line_id, ix] = 1
datas.append([codes[line_id], tag])
datas = np.array(datas)
return datas
# 将数据集拆成训练集和测试集
def get_train_test(fnames, tags, ratio=0.1):
dicts, wwords = get_dict(fnames)
data = vector_file(dicts, wwords, tags)
te_size = np.int(ratio*len(data))
tr_data = data[:-te_size]
te_data = data[-te_size:]
return tr_data, te_data
#==========================================
# 前馈神经网络,hidden_layers 中每个元素代表对应隐藏层包含的神经元数;input_layer 为输入向量的维数,这里等于词典的长度;output_layer 为样本的类别数
def nn(data, input_layer, hidden_layers, output_layer):
# ly_wbs 存放每层网络的参数
ly_wbs = []
ly_wb = {'w': tf.Variable(tf.random_normal([input_layer, hidden_layers[0]])),
'b': tf.Variable(tf.random_normal([hidden_layers[0]]))}
ly_wbs.append(ly_wb)
for ix in xrange(len(hidden_layers)):
if ix < len(hidden_layers)-1:
ly_wb = {'w': tf.Variable(tf.random_normal([hidden_layers[ix], hidden_layers[ix+1]])),
'b': tf.Variable(tf.random_normal([hidden_layers[ix+1]]))}
ly_wbs.append(ly_wb)
else:
ly_wb = {'w': tf.Variable(tf.random_normal([hidden_layers[-1], output_layer])),
'b': tf.Variable(tf.random_normal([output_layer]))}
ly_wbs.append(ly_wb)
# 通过前馈网络,由输入 data 得到神经网路的预测输出
out = data
for ly_wb in ly_wbs:
out = tf.add(tf.matmul(out, ly_wb['w']), ly_wb['b'])
return out
# 训练神经网络
def train(X, Y,
tr_data, te_data,
input_layer, hidden_layers, output_layer,
batch_size=50, epoch=10):
# 构建 tensor graph
# 预测 op
preY = nn(X, input_layer, hidden_layers, output_layer)
# cost op,采用 softmax cross entropy
cost_func = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=preY))
# optim op,可选不同的优化方法
optim = tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost_func)
# 执行 ops
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
i = 0
for ix in xrange(epoch):
epoch_loss = 0
random.shuffle(tr_data)
tr_x = tr_data[:, 0]
tr_y = tr_data[:, 1]
while i < len(tr_data):
start = i
end = i + batch_size
if end > len(tr_x):
end = len(tr_x)
batch_x = tr_x[start:end]
batch_y = tr_y[start:end]
_, c = sess.run([optim, cost_func], feed_dict={X:list(batch_x), Y:list(batch_y)})
epoch_loss += c
i += batch_size
print 'epoch %d, loss %f' % (ix+1, c)
te_x = te_data[:, 0]
te_y = te_data[:, 1]
# 计算分类准确率
correct = tf.equal(tf.argmax(preY, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct, 'float'))
print 'test accuracy:', accuracy.eval({X:list(te_x), Y:list(te_y)})
#==========================================
# 主程序,数据预处理与分类
def main():
pos_file = 'pos.txt'
neg_file = 'neg.txt'
fnames, tags = [pos_file, neg_file], [[0, 1], [1, 0]]
tr_data, te_data = get_train_test(fnames, tags, ratio=0.1)
input_layer, hidden_layers, output_layer = len(tr_data[0][0]), (1000, 800), len(tr_data[0][1])
X = tf.placeholder('float', [None, input_layer])
Y = tf.placeholder('float')
train(X, Y,
tr_data, te_data,
input_layer, hidden_layers, output_layer,
batch_size=50, epoch=10)
#=========================================================
# test codes
#=========================================================
if __name__ == '__main__':
main()
以上程序可以完成文本分类任务,但准确率很低。究其原因,可能有三个:1)样本量小;2)文本的预处理仅使用了简单的 one-hot vector,处理过于粗糙;3)分类器使用了基本的三层前馈神经网络,参数也没有调到最优。所以只能说提升空间还很大,仍需继续努力…
不过当练手还是有所得的,姑且记下。