前言
经过数据预处理,现在开始正式编写代码。。。
数据预处理部分,请参考:Tensorflow使用LSTM实现中文文本分类(一)
整体结构
代码流程图:
- 对词表进行 embeding
- 构建 lstm 层
- 构建 fc 层
- 构建 train_op
- 训练流程
其中需要封装的几个方法:
- 数据集的封装
api: next_batch(batch_size) 获得样本batch - 词表封装
api:sentence2id(text_sentence) 将 句子 转化为 id - 类别的封装
api:categoryid2id(text_category) 将 类别 转化为 id
代码演示
# -*- coding:utf-8 -*-
import tensorflow as tf
import os
import sys
import numpy as np
import math
# 打印出 log
tf.logging.set_verbosity(tf.logging.INFO)
# lstm 需要的参数
def get_default_params():
return tf.contrib.training.HParams(
num_embedding_size = 16, # 每个词语的向量的长度
# 指定 lstm 的 步长, 一个sentence中会有多少个词语
# 因为执行的过程中是用的minibatch,每个batch之间还是需要对齐的
# 在测试时,可以是一个变长的
num_timesteps = 50, # 在一个sentence中 有 50 个词语
num_lstm_nodes = [32, 32], # 每一层的size是多少
num_lstm_layers = 2, # 和上句的len 是一致的
# 有 两层 神经单元,每一层都是 32 个 神经单元
num_fc_nodes = 32, # 全连接的节点数
batch_size = 100,
clip_lstm_grads = 1.0,
# 控制lstm的梯度,因为lstm很容易梯度爆炸或者消失
# 这种方式就相当于给lstm设置一个上限,如果超过了这个上限,就设置为这个值
learning_rate = 0.001,
num_word_threshold = 10, # 词频太少的词,对于模型训练是没有帮助的,因此设置一个门限
)
hps = get_default_params() # 生成 参数 对象
# 设置文件路径
train_file = './news_data/cnews.train.seg.txt'
val_file = './news_data/cnews.val.seg.txt'
test_file = './news_data/cnews.test.seg.txt'
vocab_file = './news_data/cnews.vocab.txt' # 统计的词频
category_file = './news_data/cnews.category.txt' # 标签
output_folder = './news_data/run_text_rnn'
if not os.path.exists(output_folder):
os.mkdir(output_folder)
class Vocab:
'''
词表的封装
'''
def __init__(self, filename, num_word_threahold):
# 每一个词,给她一个id,另外还要统计词频。ps:前面带下划线的为私有成员
self._word_to_id = {
}
self._unk = -1 # 先给 unk 赋值一个 负值,然后根据实际情况在赋值
self._num_word_theshold = num_word_threahold # 低于 这个值 就忽略掉该词
self._read_dict(filename) # 读词表方法
def _read_dict(self, filename):
'''
读这个词表
:param filename: 路径
:return: none
'''
with open(filename, 'r') as f:
lines = f.readlines()
for line in lines:
word, frequency = line.strip('\n').split('\t')
word = word # 获得 单词
frequency = int(frequency) # 获得 频率
if frequency < self._num_word_theshold:
continue # 门限过滤一下
idx = len(self._word_to_id) #这里使用了一个id递增的小技巧
if word == '<UNK>': # 如果是空格,就把上一个id号给它
# 如果是 unk的话, 就特殊处理一下
self._unk = idx
self._word_to_id[word] = idx
# 如果 word 存在,就把 idx 当做值,将其绑定到一起
# 如果 word 在词表中不存在,就把nuk的值赋予它
def word_to_id(self, word):
'''
为单词分配id值
:param word: 单词
:return:
'''
# 字典.get() 如果有值,返回值;无值,返回默认值(就是第二个参数)
return self._word_to_id.get(word, self._unk)
def sentence_to_id(self, sentence):
'''
将句子 转换成 id 向量
:param sentence: 要输入的句子(分词后的句子)
:return:
'''
# 单条句子的id vector
word_ids = [self.word_to_id(cur_word) for cur_word in sentence.split(' ')]
# cur_word 有可能不存在,需要使用函数进行过滤一下