在TensorFlow中基于lstm构建分词系统笔记(一)

https://www.jianshu.com/p/ccb805b9f014

前言

我打算基于lstm构建一个分词系统,通过这个例子来学习下TensorFlow中如何训练循环递归神经网络。我们将从最粗糙的版本开始搭建这个小系统,然后一步步优化其中的每一部分,包括网络架构的优化,数据处理的优化,甚至整个代码架构的优化。希望想我一样的入门选手看到其中的每一步实现以及如何去优化。
关于LSTM网络的介绍,可以看官网推荐的一篇博客,写的实在是太棒了http://colah.github.io/posts/2015-08-Understanding-LSTMs
另外这篇翻译也很赞啊http://www.jianshu.com/p/9dc9f41f0b29,这里不在详述。
我们第一个版本的模型来自官网的tutorials中Recurrent Neural Networks部分内容,官网的数据并不利于我们去直接感受模型训练的带来的结果,所以后来我想了下用它来实现一个中文分词,可能更有利于初学者去直观的感受。第一个版本会我写的很粗糙,主要是为了理解在TensorFlow中如何搭建LSTM网络。

模型搭建

我对官网中的例子用我自己更喜欢的结构重写了下。 首先我们来看下如何搭建这个模型。开始我把模型部分代码主要由inference(),loss()和training()三部分构成,inference()部分负责模型搭建,loss()负责计算损失,为优化做准备,training()负责优化部分,主要是对损失函数应用梯度下降,更新参数。我把上面三部分写封装一个类里面。后来发现这样实现会存在些问题,然后又把inference()的实现直接放在了类的init()函数里面。下面先看下模型的整体实现,

class ptb_lstm():

    def __init__(self,config):
...
def loss(self):
....
def train(self):
....

这里,我们在init()中传了一个config类,这个config主要是一些模型参数,大致形式是下面这样,这篇笔记就不详讲了

class config():
'''参数配置类'''
init_scale = 0.1
learning_rate = 1.0
max_grad_norm = 5
num_layers = 2
hidden_size = 200
keep_prob = 1.0
lr_decay = 0.5
batch_size = 50
num_steps = 50
vocab_size = 3000
output_size = 3
learningrate = 0.5

好了,接下来我们先看init()部分

class lstm_model():
def __init__(self, config):
'''
:param config:config类,一些训练参数设置
'''
self.config = config
self.x = tf.placeholder(tf.int32, shape=(config.batch_size, config.num_steps))
self.y = tf.placeholder(tf.int32, shape=(config.batch_size, config.num_steps)) def lstm_cell():
#构建lstm基本单元
return tf.contrib.rnn.BasicLSTMCell(
self.config.hidden_size, forget_bias=0.0, state_is_tuple=True) attn_cell = lstm_cell
if config.keep_prob < 1:
#如果config.keep_prob参数小于1,对lstm单元进行dropout,防止过拟合
def attn_cell():
return tf.contrib.rnn.DropoutWrapper(
lstm_cell(), output_keep_prob=config.keep_prob) cell = tf.contrib.rnn.MultiRNNCell(
[attn_cell() for _ in range(config.num_layers)], state_is_tuple=True)
#构建多层的lstm,config.num_layers是层数参数 self._initial_state = cell.zero_state(self.config.batch_size, tf.float32)
#初始化lstm的state with tf.device("/cpu:0"):
embedding = tf.get_variable(
"embedding", [self.config.vocab_size, self.config.hidden_size], dtype=tf.float32)
inputs = tf.nn.embedding_lookup(embedding,self.x)
#词嵌入 outputs = []
state = self._initial_state
with tf.variable_scope("RNN"):
for time_step in range(self.config.num_steps):
if time_step > 0: tf.get_variable_scope().reuse_variables()
(cell_output, state) = cell(inputs[:, time_step, :], state)
outputs.append(cell_output)
#前向传播,计算每个单元的cell_output和state,把cell_output添加到outputs,把state传递到下个单元,最终outputs的为(config.num_steps,config.batch_size,config.hidden_size) output = tf.reshape(tf.concat(axis=1, values=outputs), [-1, self.config.hidden_size])
#output的形状为(config.num_steps*config.batch_size,config.hedden_size) softmax_w = tf.get_variable(
"softmax_w", [self.config.hidden_size, self.config.output_size], dtype=tf.float32)
softmax_b = tf.get_variable("softmax_b", [self.config.output_size], dtype=tf.float32)
self.logits = tf.matmul(output, softmax_w) + softmax_b
#得到最终的网络输出logits形状为(config.num_steps*config.batch_size,config.output_size)

接着是loss(self,logits)

    def loss(self):

        logits = self.logits
loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example(
[logits],
[tf.reshape(self.y, [-1])],
[tf.ones([self.config.batch_size * self.config.num_steps], dtype=tf.float32)])
# 交叉熵损失函数,下一篇专门讲下tensorflow中的几个损失函数的实现
cost = tf.reduce_sum(loss) / self.config.batch_size

最后是后向传播参数更新部分training(self)

def training(self):
loss = self.loss() tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(loss, tvars),
self.config.max_grad_norm)
optimizer = tf.train.GradientDescentOptimizer(self.config.learningrate)
#优化器 train_op = optimizer.apply_gradients(
zip(grads, tvars),
global_step=tf.contrib.framework.get_or_create_global_step())
#梯度下降
return train_op

作者:MUTU洋
链接:https://www.jianshu.com/p/ccb805b9f014
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 
https://www.jianshu.com/p/142b4358aaf9
 

上一节我们介绍了我们的模型部分,这一节来介绍下我们的数据来源和数据预处理。对初学者可能常常面临的问题的是模型的输入到底是怎样的,例如,在rnn可以处理任意长度的句子,所以很多初学者可能会认为是不是在TensorFlow中输入不需要特殊处理。理论上rnn是可以处理任意长度的句子,但在工程实现上考虑到效率等一些列问题,TensorFlow中的rnn(包括它的变形,lstm,gru...)需要把不同长度的句子pading到同一个长度,一种是把所有句子都处理成同一个长度,另一种是我们只需要在同一个batch中的句子同一个长度。本节采用第一种。

数据

一 数据来源

这里我们自己构造了一份训练数据,我随意找了一份京东评论数据,你也可以采用其他的文本数据。然后利用jieba分词对这些文本进行分词。例如,有这样一句话,s1 = “迪士尼发行了四部票房超过10亿美元的电影”,我们对s1分词后成为s1_seg = ['迪士尼', '发行', '了', '电影'],其中,“迪士尼”就是一个词,我们把它处理成['B','I','I'],‘B’代表词的开始,‘I’代表词的中间。这样s1就可以标记为s1_tag=['B','I','I','B','I','B','B','I'],这样我们就可以得到我们的训练数据。注意,jieba本身就可能分错,我们这里只是想看下我们的模型能不能学习到训练数据的分布。

二 数据预处理

刚才我们已经得到我们的训练数据,现在我们要把它处理成符合输入要求的数据格式。为了简单,我这里把所以数据都处理成同样长度的序列(上一章中我们构建的模型就是要求的所有的序列长度一样)。由于在训练的时,我们需要在数据上不断的迭代更新参数。这里需要把数据处理成不同的batch,然后在每个batch上迭代。这里我们构造了一个类,这个类有一个next_batch方法。通过这个方法可以不断的产生batch_size的训练数据。


class DataSet(object):
def __init__(self,x_data,y_data,):
#这个类主要用于不断产生训练数据 self._x_data = np.array(x_data)
self._y_data = np.array(y_data)
self._epochs_completed = 0
self._index_in_epoch = 0
self._num_examples = len(x_data) @property
def x_data(self):
return self._x_data @property
def y_data(self):
return self._y_data @property
def num_examples(self):
return self._num_examples @property
def epochs_completed(self):
return self._epochs_completed def next_batch(self, batch_size, shuffle=True):
"""返回下一个`batch_size`数据""" start = self._index_in_epoch
# 第一个epoch时做乱序处理
if self._epochs_completed == 0 and start == 0 and shuffle:
perm0 = np.arange(self._num_examples)
np.random.shuffle(perm0)
self._x_data = self.x_data[perm0]
self._y_data = self.y_data[perm0] # 进入到下一个epoch
if start + batch_size > self._num_examples:
# Finished epoch
self._epochs_completed += 1
# Get the rest examples in this epoch
rest_num_examples = self._num_examples - start x_rest_part = self._x_data[start:self._num_examples]
y_rest_part = self._y_data[start:self._num_examples] # 数据乱序处理
if shuffle:
perm = np.arange(self._num_examples)
np.random.shuffle(perm)
self._x_data = self._x_data[perm]
self._y_data = self._y_data[perm] # 开始下一个epoch
start = 0
self._index_in_epoch = batch_size - rest_num_examples
end = self._index_in_epoch
x_new_part = self._x_data[start:end]
y_new_part = self._y_data[start:end]
return np.concatenate((x_rest_part, x_new_part), axis=0), np.concatenate(
(y_rest_part, y_new_part), axis=0)
else:
self._index_in_epoch += batch_size
end = self._index_in_epoch
return self._x_data[start:end], self._y_data[start:end] def word_to_id(dict_data):
#遍历所以的中文句子里的字符,建立一个Vocabulary,通过字符的频次把每个字符映射到一个数字
counter = collections.Counter(''.join(dict_data.keys()))
count_pairs = sorted(counter.items(), key=lambda x: (-x[1], x[0]))
words, _ = list(zip(*count_pairs))
word_id = dict(zip(words, range(3, len(words) + 3)))
word_id['B'] = 1
word_id['I'] = 2
return word_id def datas(dict_data,num_step):
#读取数据
x_data = []
y_data = []
word_id = word_to_id(dict_data)
for line in dict_data:
x_list = [word_id[word] for word in list(line)][:num_step]
y_list = [word_id[word] for word in dict_data[line]][:num_step]
x_len = len(x_list)
y_len = len(y_list)
assert x_len == y_len
if x_len<num_step:
x_list.extend([0]*(num_step-x_len))
y_list.extend([0]*(num_step-y_len))
x_data.append(x_list)
y_data.append(y_list)
return x_data,y_data def read_data_sets(fileName,num_step):
#通过调用这个函数不断的产生next batch的训练数据 with open(fileName) as f:
dict_data = json.load(f) x_data, y_data = datas(dict_data, num_step) return DataSet(x_data, y_data)

通过调用read_data_sets来产生训练数据,注意这里的参数dict_data参数指的是,key是字符串,例如前面的s1,value是该字符串的标记,例如s1的标记是s1_tag。
下一节我们将介绍训练过程。

作者:MUTU洋
链接:https://www.jianshu.com/p/142b4358aaf9
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

在TensorFlow中基于lstm构建分词系统笔记的更多相关文章

  1. tensorflow实现基于LSTM的文本分类方法

    tensorflow实现基于LSTM的文本分类方法 作者:u010223750 引言 学习一段时间的tensor flow之后,想找个项目试试手,然后想起了之前在看Theano教程中的一个文本分类的实 ...

  2. 一文详解如何用 TensorFlow 实现基于 LSTM 的文本分类(附源码)

    雷锋网按:本文作者陆池,原文载于作者个人博客,雷锋网已获授权. 引言 学习一段时间的tensor flow之后,想找个项目试试手,然后想起了之前在看Theano教程中的一个文本分类的实例,这个星期就用 ...

  3. Tensorflow之基于LSTM神经网络写唐诗

    最近看了不少关于写诗的博客,在前人的基础上做了一些小的改动,因比较喜欢一次输入很长的开头句,所以让机器人输出压缩为一个开头字生成两个诗句,写五言和七言诗,当然如果你想写更长的诗句是可以继续改动的. 在 ...

  4. 基于drone构建CI-CD系统

    kubernetes集群三步安装 CI 概述 用一个可描述的配置定义整个工作流 程序员是很懒的动物,所以想各种办法解决重复劳动的问题,如果你的工作流中还在重复一些事,那么可能就得想想如何优化了 持续集 ...

  5. 基于consul构建golang系统分布式服务发现机制

    原文地址-石匠的Blog: http://www.bugclosed.com/post/5 在分布式架构中,服务治理是一个重要的问题.在没有服务治理的分布式集群中,各个服务之间通过手工或者配置的方式进 ...

  6. 以lstm+ctc对汉字识别为例对tensorflow 中的lstm,ctc loss的调试

    #-*-coding:utf8-*- __author = "buyizhiyou" __date = "2017-11-21" ''' 单步调试,结合汉字的识 ...

  7. eclipse中基于maven构建的web项目pom.xml中指定的jar包无法发布到tomcat中

    eclipse运行maven web项目报错: 信息: Starting Servlet Engine: Apache Tomcat/7.0.57 一月 07, 2015 11:50:44 下午 or ...

  8. tensorflow中的lstm的state

        考虑 state_is_tuple     Output, new_state = cell(input, state)     state其实是两个 一个 c state,一个m(对应下图的 ...

  9. 浅谈分词算法(5)基于字的分词方法(bi-LSTM)

    目录 前言 目录 循环神经网络 基于LSTM的分词 Embedding 数据预处理 模型 如何添加用户词典 前言 很早便规划的浅谈分词算法,总共分为了五个部分,想聊聊自己在各种场景中使用到的分词方法做 ...

随机推荐

  1. hdu 5723 Abandoned country 最小生成树 期望

    Abandoned country 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5723 Description An abandoned coun ...

  2. 导入导出CSV

    const string dataPath = @"D:\Users\jin_h\Documents\Visual Studio 2013\Projects\ConsoleApplicati ...

  3. 面向企业级的开源WebGIS解决方案--MapGuide(对比分析)

    在技术特点.功能.架构等方面,MapGuide与其他WebGIS产品有什么区别?本文主要从此角度来介绍MapGuide的特性,以供参考.    本人选择了比较熟悉的几款WebGIS产品:MapServ ...

  4. oracle 树型结构数据的查询

    Oracle中start by prior子句用法 connect by 是结构化查询中用到的,其基本语法是: select ... from tablename start with 条件1 con ...

  5. ElasticSearch入门 :Windows下安装ElasticSearch

    这是ElasticSearch 2.4 版本系列的第一篇: ElasticSearch入门 第一篇:Windows下安装ElasticSearch ElasticSearch入门 第二篇:集群配置 E ...

  6. java系列之 原生数据类型

    在我看来,java里面里面除了原生类型不是对象,其他的都是对象.但java是面向对象的语言,很多地方还要要操作对象,所以java会自动把原生类型转为对应的包装类型.这个过程叫自动装箱.有装箱就有拆箱, ...

  7. sigmod2017.org

    http://sigmod2017.org/sigmod-program/#ssession20

  8. oracle linux dtrace

    http://docs.oracle.com/cd/E37670_01/E38608/html/pref.html

  9. mmc生产任务分配问题续

    mmc生产任务分配问题续,本题目比上个题目难, 要注意的是,生产,销售,库存的关系, 生产+上月库存-销售=本月库存, 期初,生产=库存,销售没有.

  10. kernel logo到开机动画之间闪现黑屏(android 5.X)

    在BootAnimation開始画图之前,会先做一次clear screen的动作,避免出现前面的图干扰到BootAnimation的显示. 通过check main_log先确认播放开机动画是哪个f ...