文档章节

使用TensorFlow训练循环神经网络语言模型

Gaussic
 Gaussic
发布于 2017/08/25 14:16
字数 2449
阅读 130
收藏 2
点赞 0
评论 0

读了将近一个下午的TensorFlow Recurrent Neural Network教程,翻看其在PTB上的实现,感觉晦涩难懂,因此参考了部分代码,自己写了一个简化版的Language Model,思路借鉴了Keras的LSTM text generation

代码地址:Github

转载请注明出处:Gaussic

语言模型

Language Model,即语言模型,其主要思想是,在知道前一部分的词的情况下,推断出下一个最有可能出现的词。例如,知道了 The fat cat sat on the,我们认为下一个词为mat的可能性比hat要大,因为猫更有可能坐在毯子上,而不是帽子上。

这可能被你认为是常识,但是在自然语言处理中,这个任务是可以用概率统计模型来描述的。就拿The fat cat sat on the mat来说。我们可能统计出第一个词The出现的概率p(The),The后面是fat的条件概率为p(fat|The),The fat同时出现的联合概率:

输入图片说明

这个联合概率,就是The fat的合理性,即这句话的出现符不符合自然语言的评判标准,通俗点表述就是这是不是句人话。同理,根据链式规则,The fat cat的联合概率可求:

输入图片说明

在知道前面的词为The cat的情况下,下一个词为cat的概率可以推导出来:

输入图片说明

分子是The fat cat在语料库中出现的次数,分母是The fat在语料库中出现的次数。

因此,The fat cat sat on the mat整个句子的合理性同样可以推导,这个句子的合理性即为它的概率。公式化的描述如下:

wq

可以看出一个问题,每当计算下一个词的条件概率,需要计算前面所有词的联合概率。这个计算量相当的庞大。并且,一个句子中大部分词同时出现的概率往往少之又少,数据稀疏非常严重,需要一个非常大的语料库来训练。

一个简单的优化是基于马尔科夫假设,下一个词的出现仅与前面的一个或n个词有关。

最简单的情况,下一个词的出现仅仅和前面一个词有关,称之为bigram。

输入图片说明

再复杂点,下一个词的出现仅和前面两个词有关,称之为trigram。

输入图片说明

这样的条件概率虽然好求,但是会丢失大量的前面的词的信息,有时会对结果产生不良影响。因此如何选择一个有效的n,使得既能简化计算,又能保留大部分的上下文信息。

以上均是传统语言模型的描述。如果不太深究细节,我们的任务就是,知道前面n个词,来计算下一个词出现的概率。并且使用语言模型来生成新的文本。

在本文中,我们更加关注的是,如何使用RNN来推测下一个词。

数据准备

TensorFlow的官方文档使用的是Mikolov准备好的PTB数据集。我们可以将其下载并解压出来:

$ wget http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz
$ tar xvf simple-examples.tgz

部分数据如下,不常用的词转换成了<unk>标记,数字转换成了N:

we 're talking about years ago before anyone heard of asbestos having any questionable properties
there is no asbestos in our products now
neither <unk> nor the researchers who studied the workers were aware of any research on smokers of the kent cigarettes
we have no useful information on whether users are at risk said james a. <unk> of boston 's <unk> cancer institute
the total of N deaths from malignant <unk> lung cancer and <unk> was far higher than expected the researchers said

读取文件中的数据,将换行符转换为<eos>,然后转换为词的list:

def _read_words(filename):
    with open(filename, 'r', encoding='utf-8') as f:
        return f.read().replace('\n', '<eos>').split()
f = _read_words('simple-examples/data/ptb.train.txt')
print(f[:20])

得到:

['aer', 'banknote', 'berlitz', 'calloway', 'centrust', 'cluett', 'fromstein', 'gitano', 'guterman', 'hydro-quebec', 'ipo', 'kia', 'memotec', 'mlx', 'nahb', 'punts', 'rake', 'regatta', 'rubens', 'sim']

构建词汇表,词与id互转:

def _build_vocab(filename):
    data = _read_words(filename)

    counter = Counter(data)
    count_pairs = sorted(counter.items(), key=lambda x: -x[1])

    words, _ = list(zip(*count_pairs))
    word_to_id = dict(zip(words, range(len(words))))

    return words, word_to_id
words, words_to_id = _build_vocab('simple-examples/data/ptb.train.txt')
print(words[:10])
print(list(map(lambda x: words_to_id[x], words[:10])))

输出:

('the', '<unk>', '<eos>', 'N', 'of', 'to', 'a', 'in', 'and', "'s")
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

将一个文件转换为id表示:

def _file_to_word_ids(filename, word_to_id):
    data = _read_words(filename)
    return [word_to_id[x] for x in data if x in word_to_id]
words_in_file = _file_to_word_ids('simple-examples/data/ptb.train.txt', words_to_id)
print(words_in_file[:20])

词汇表已根据词频进行排序,由于第一句话非英文,所以id靠后。

[9980, 9988, 9981, 9989, 9970, 9998, 9971, 9979, 9992, 9997, 9982, 9972, 9993, 9991, 9978, 9983, 9974, 9986, 9999, 9990]

将一句话从id列表转换回词:

def to_words(sentence, words):
    return list(map(lambda x: words[x], sentence))

将以上函数整合:

def ptb_raw_data(data_path=None):
    train_path = os.path.join(data_path, 'ptb.train.txt')
    valid_path = os.path.join(data_path, 'ptb.valid.txt')
    test_path = os.path.join(data_path, 'ptb.test.txt')

    words, word_to_id = _build_vocab(train_path)
    train_data = _file_to_word_ids(train_path, word_to_id)
    valid_data = _file_to_word_ids(valid_path, word_to_id)
    test_data = _file_to_word_ids(test_path, word_to_id)

    return train_data, valid_data, test_data, words, word_to_id

以上部分和官方的例子有一定的相似之处。接下来的处理和官方存在很大的不同,主要参考了Keras例程处理文档的操作:

def ptb_producer(raw_data, batch_size=64, num_steps=20, stride=1):
    data_len = len(raw_data)

    sentences = []
    next_words = []
    for i in range(0, data_len - num_steps, stride):
        sentences.append(raw_data[i:(i + num_steps)])
        next_words.append(raw_data[i + num_steps])

    sentences = np.array(sentences)
    next_words = np.array(next_words)

    batch_len = len(sentences) // batch_size
    x = np.reshape(sentences[:(batch_len * batch_size)], \
        [batch_len, batch_size, -1])

    y = np.reshape(next_words[:(batch_len * batch_size)], \
        [batch_len, batch_size])

    return x, y

参数解析:

  • raw_data: 即ptb_raw_data()函数产生的数据
  • batch_size: 神经网络使用随机梯度下降,数据按多个批次输出,此为每个批次的数据量
  • num_steps: 每个句子的长度,相当于之前描述的n的大小,这在循环神经网络中又称为时序的长度。
  • stride: 取数据的步长,决定数据量的大小。

代码解析:

这个函数将一个原始数据list转换为多个批次的数据,即[batch_len, batch_size, num_steps]

首先,程序每一次取了num_steps个词作为一个句子,即x,以这num_steps个词后面的一个词作为它的下一个预测,即为y。这样,我们首先把原始数据整理成了batch_len * batch_size个x和y的表示,类似于已知x求y的分类问题。

为了满足随机梯度下降的需要,我们还需要把数据整理成一个个小的批次,每次喂一个批次的数据给TensorFlow来更新权重,这样,数据就整理为[batch_len, batch_size, num_steps]的格式。

打印部分数据:

train_data, valid_data, test_data, words, word_to_id = ptb_raw_data('simple-examples/data')
x_train, y_train = ptb_producer(train_data)
print(x_train.shape)
print(y_train.shape)

输出:

(14524, 64, 20)
(14524, 64)

可见我们得到了14524个批次的数据,每个批次的训练集维度为[64, 20]。

print(' '.join(to_words(x_train[100, 3], words)))

第100个批次的第3句话为:

despite steady sales growth <eos> magna recently cut its quarterly dividend in half and the company 's class a shares
print(words[np.argmax(y_train[100, 3])])

它的下一个词为:

the

构建模型

配置项

class LMConfig(object):
    """language model 配置项"""
    batch_size = 64       # 每一批数据的大小
    num_steps = 20        # 每一个句子的长度
    stride = 3            # 取数据时的步长

    embedding_dim = 64    # 词向量维度
    hidden_dim = 128      # RNN隐藏层维度
    num_layers = 2        # RNN层数

    learning_rate = 0.05  # 学习率
    dropout = 0.2         # 每一层后的丢弃概率

读取输入

让模型可以按批次的读取数据。

class PTBInput(object):
    """按批次读取数据"""
    def __init__(self, config, data):
        self.batch_size = config.batch_size
        self.num_steps = config.num_steps
        self.vocab_size = config.vocab_size # 词汇表大小

        self.input_data, self.targets = ptb_producer(data,
            self.batch_size, self.num_steps)

        self.batch_len = self.input_data.shape[0] # 总批次
        self.cur_batch = 0  # 当前批次

    def next_batch(self):
        """读取下一批次"""
        x = self.input_data[self.cur_batch]
        y = self.targets[self.cur_batch]

        # 转换为one-hot编码
        y_ = np.zeros((y.shape[0], self.vocab_size), dtype=np.bool)
        for i in range(y.shape[0]):
            y_[i][y[i]] = 1

        # 如果到最后一个批次,则回到最开头
        self.cur_batch = (self.cur_batch +1) % self.batch_len

        return x, y_

模型

class PTBModel(object):
    def __init__(self, config, is_training=True):

        self.num_steps = config.num_steps
        self.vocab_size = config.vocab_size

        self.embedding_dim = config.embedding_dim
        self.hidden_dim = config.hidden_dim
        self.num_layers = config.num_layers
        self.rnn_model = config.rnn_model

        self.learning_rate = config.learning_rate
        self.dropout = config.dropout

        self.placeholders()  # 输入占位符
        self.rnn()           # rnn 模型构建
        self.cost()          # 代价函数
        self.optimize()      # 优化器
        self.error()         # 错误率


    def placeholders(self):
        """输入数据的占位符"""
        self._inputs = tf.placeholder(tf.int32, [None, self.num_steps])
        self._targets = tf.placeholder(tf.int32, [None, self.vocab_size])


    def input_embedding(self):
        """将输入转换为词向量表示"""
        with tf.device("/cpu:0"):
            embedding = tf.get_variable(
                "embedding", [self.vocab_size,
                    self.embedding_dim], dtype=tf.float32)
            _inputs = tf.nn.embedding_lookup(embedding, self._inputs)

        return _inputs


    def rnn(self):
        """rnn模型构建"""
        def lstm_cell():  # 基本的lstm cell
            return tf.contrib.rnn.BasicLSTMCell(self.hidden_dim,
                state_is_tuple=True)

        def gru_cell():   # gru cell,速度更快
            return tf.contrib.rnn.GRUCell(self.hidden_dim)

        def dropout_cell():    # 在每个cell后添加dropout
            if (self.rnn_model == 'lstm'):
                cell = lstm_cell()
            else:
                cell = gru_cell()
            return tf.contrib.rnn.DropoutWrapper(cell,
                output_keep_prob=self.dropout)

        cells = [dropout_cell() for _ in range(self.num_layers)]
        cell = tf.contrib.rnn.MultiRNNCell(cells, state_is_tuple=True)  # 多层rnn

        _inputs = self.input_embedding()
        _outputs, _ = tf.nn.dynamic_rnn(cell=cell,
            inputs=_inputs, dtype=tf.float32)

        # _outputs的shape为 [batch_size, num_steps, hidden_dim]
        last = _outputs[:, -1, :]  # 只需要最后一个输出

        # dense 和 softmax 用于分类,以找出各词的概率
        logits = tf.layers.dense(inputs=last, units=self.vocab_size)   
        prediction = tf.nn.softmax(logits)  

        self._logits = logits
        self._pred = prediction

    def cost(self):
        """计算交叉熵代价函数"""
        cross_entropy = tf.nn.softmax_cross_entropy_with_logits(
            logits=self._logits, labels=self._targets)
        cost = tf.reduce_mean(cross_entropy)
        self.cost = cost

    def optimize(self):
        """使用adam优化器"""
        optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate)
        self.optim = optimizer.minimize(self.cost)

    def error(self):
        """计算错误率"""
        mistakes = tf.not_equal(
            tf.argmax(self._targets, 1), tf.argmax(self._pred, 1))
        self.errors = tf.reduce_mean(tf.cast(mistakes, tf.float32))

训练

def run_epoch(num_epochs=10):
    config = LMConfig()   # 载入配置项

    # 载入源数据,这里只需要训练集
    train_data, _, _, words, word_to_id = \
        ptb_raw_data('simple-examples/data')
    config.vocab_size = len(words)

    # 数据分批
    input_train = PTBInput(config, train_data)
    batch_len = input_train.batch_len

    # 构建模型
    model = PTBModel(config)

    # 创建session,初始化变量
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())

    print('Start training...')
    for epoch in range(num_epochs):  # 迭代轮次
        for i in range(batch_len):   # 经过多少个batch
            x_batch, y_batch = input_train.next_batch()

            # 取一个批次的数据,运行优化
            feed_dict = {model._inputs: x_batch, model._targets: y_batch}
            sess.run(model.optim, feed_dict=feed_dict)

            # 每500个batch,输出一次中间结果
            if i % 500 == 0:
                cost = sess.run(model.cost, feed_dict=feed_dict)

                msg = "Epoch: {0:>3}, batch: {1:>6}, Loss: {2:>6.3}"
                print(msg.format(epoch + 1, i + 1, cost))

                # 输出部分预测结果
                pred = sess.run(model._pred, feed_dict=feed_dict)
                word_ids = sess.run(tf.argmax(pred, 1))
                print('Predicted:', ' '.join(words[w] for w in word_ids))
                true_ids = np.argmax(y_batch, 1)
                print('True:', ' '.join(words[w] for w in true_ids))

    print('Finish training...')
    sess.close()

需要经过多次的训练才能得到一个较为合理的结果。

© 著作权归作者所有

共有 人打赏支持
Gaussic
粉丝 384
博文 28
码字总数 66788
作品 0
宝山
机器学习者必知的5种深度学习框架

雷锋网按:本文为雷锋字幕组编译的技术博客,原标题The 5 Deep Learning Frameworks Every Serious Machine Learner Should Be Familiar With,作者为James Le。 翻译 | 杨恕权 张晓雪 陈明霏...

雷锋字幕组 ⋅ 05/03 ⋅ 0

资源 | 概率编程工具:TensorFlow Probability官方简介

  选自Medium   作者:Josh Dillon、Mike Shwe、Dustin Tran   机器之心编译   参与:白妤昕、李泽南      在 2018 年 TensorFlow 开发者峰会上,谷歌发布了 TensorFlow Probabi...

机器之心 ⋅ 04/22 ⋅ 0

13- 深度学习之神经网络核心原理与算法-TensorFlow介绍与框架挑选

TensorFlow以及TensorFlow的应用 支持深度学习的框架。torch caffe TensorFlow 简介 使用图(Graph)来表示计算任务 图中的节点被称为op(operation) 一个op获取0个或多个tensor,执行计算,产生...

天涯明月笙 ⋅ 06/01 ⋅ 0

入门 | TensorFlow的动态图工具Eager怎么用?这是一篇极简教程

  选自Github   作者:Madalina Buzau   机器之心编译   参与:王淑婷、泽南      去年 11 月,Google Brain 团队发布了 Eager Execution,一个由运行定义的新接口,为 TensorFl...

机器之心 ⋅ 06/14 ⋅ 0

史上最全TensorFlow学习资源汇总

来源 悦动智能(公众号ID:aibbtcom) 本篇文章将为大家总结TensorFlow纯干货学习资源,非常适合新手学习,建议大家收藏。 ▌一 、TensorFlow教程资源 1)适合初学者的TensorFlow教程和代码示...

悦动智能 ⋅ 04/12 ⋅ 0

第1章 TensorFlow简介

TensorFlow是一个开源软件库,用于各种感知和语言理解任务的机器学习。目前被50个团队用于研究和生产许多Google商业产品,如语音识别、Gmail、Google 相册和搜索,其中许多产品曾使用过其前任...

u013162035 ⋅ 05/24 ⋅ 0

tensorflow入门---第三章

tensorflow程序分为两个阶段: 第一个阶段:定义计算图所有的计算 第二个阶段:执行计算 第一节:计算模型—–计算图 第二节:数据模型—–张量 第三节:运行模型—–会话 第一节:计算图 计...

cttacm ⋅ 05/05 ⋅ 0

【干货】史上最全的Tensorflow学习资源汇总,速藏!

一 、Tensorflow教程资源: 1)适合初学者的Tensorflow教程和代码示例:(https://github.com/aymericdamien/TensorFlow-Examples)该教程不光提供了一些经典的数据集,更是从实现最简单的“Hel...

技术小能手 ⋅ 04/16 ⋅ 0

《Scikit-Learn与TensorFlow机器学习实用指南》第9章 启动并运行TensorFlow

第9章 启动并运行TensorFlow 来源:ApacheCN《Sklearn 与 TensorFlow 机器学习实用指南》翻译项目 译者:@akonwang @WilsonQu 校对:@Lisanaaa @飞龙 TensorFlow 是一款用于数值计算的强大的...

apachecn_飞龙 ⋅ 04/23 ⋅ 0

开源的机器学习框架应当如何选择?

为何要选择机器学习框架呢?使用开源工具的好处不仅仅在于其可用性。通常来说,如此级别的项目均有大量的数据工程师和数据科学家愿意去分享数据集和前期训练模型。比如,你可以使用分类模型训...

小欣妹妹 ⋅ 04/20 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

笔试题之Java基础部分【简】【一】

基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法,其他 1.length、length()和size() length针对...

anlve ⋅ 28分钟前 ⋅ 2

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 53分钟前 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 57分钟前 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 8

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

Linux下php访问远程ms sqlserver

1、安装freetds(略,安装在/opt/local/freetds 下) 2、cd /path/to/php-5.6.36/ 进入PHP源码目录 3、cd ext/mssql进入MSSQL模块源码目录 4、/opt/php/bin/phpize生成编译配置文件 5、 . ./...

wangxuwei ⋅ 昨天 ⋅ 0

如何成为技术专家

文章来源于 -- 时间的朋友 拥有良好的心态。首先要有空杯心态,用欣赏的眼光发现并学习别人的长处,包括但不限于工具的使用,工作方法,解决问题以及规划未来的能力等。向别人学习的同时要注...

长安一梦 ⋅ 昨天 ⋅ 0

Linux vmstat命令实战详解

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令...

刘祖鹏 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部