在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. 【学习笔记】python2和python3的input()

    python2中的input()只接受变量作为传入值,非变量内容会报错. >>> user=input("Enter your name:") Enter you ...

  2. spring boot学习总结(二)-- 关于@SpringBootApplication的一些基础知识点

    1.@SpringBootApplication 它是一个复合Annotation,实际上对于SpringBoot应用来说,最重要的是只有三个 @Target(ElementType.TYPE) @R ...

  3. UVA 12436 - Rip Van Winkle&#39;s Code(线段树)

    UVA 12436 - Rip Van Winkle's Code option=com_onlinejudge&Itemid=8&page=show_problem&cate ...

  4. STM32F4 External event -- WFE 待机模式

    The STM32F4xx are able to handle external or internal events in order to wake up the core (WFE). The ...

  5. AES Test vectors

    Table of content List of test vectors for AES/ECB encryption mode AES ECB 128-bit encryption mode AE ...

  6. ICO如此疯狂为哪般?

    编者语: 独角兽一词起源于硅谷,是投资行业,尤其是风险投资业的术语,指的是那些估值超过十亿美元的创业公司.独角兽凤毛麟角,占创业公司总数的0.1%都不到.鑫根资本认为,一个独角兽能达到如此估值,肯定是 ...

  7. [Go] Cookie 使用简介

    Golang 的 Cookie web 开发免不了要和 cookie 打交道.Go 的 http 库也提供了 cookie 的相关操作. type Cookie struct { Name strin ...

  8. Unity3D实践系列04, 脚本的生命周期

    Unity3D脚本生命周期是指从脚本的最初唤醒到脚本最终销毁的整个过程.生命周期的各个方法被封装到了MonoBehaviour类中.具体来说如下: 1.In Editor Mode 编辑模式 当在编辑 ...

  9. 初步理解JWT并实践使用

    原文:https://www.jianshu.com/p/2fdc20a42c41 JWT是一种用于双方之间传递安全信息的简洁的.URL安全的表述性声明规范.JWT作为一个开放的标准(RFC 7519 ...

  10. 使用jQuery实现图片懒加载原理

    原文:https://www.liaoxuefeng.com/article/00151045553343934ba3bb4ed684623b1bf00488231d88d000 在网页中,常常需要用 ...