用Tensorflow建立image caption 模型(二)

第二部分 使用Tensorflow训练循环神经网络语言模型

前言:

  写这篇博客的目的是一开始想要使用Tensorflow建立一个image caption模型,在一开始觉得不就是一个LSTM模型,因为之前有用Tensorflow实现过CNN结构,所以觉得并不会很难。但实际上,在这个过程中也遇到了不少困难。
按照我自己的学习顺序,这个系列的博客将分为三个部分,最终将构建一个基于Tensorflow的简单的image caption模型:
1. 系列的第一部分,搬运于另一个博主,加上了自己的理解做了一些扩展性的,主要是对语言模型有关的注释。这篇博客主要是为了了解一些LSTM一些基本的结构。
2. 本篇是第二篇部分,同样搬运于另一个博主,这篇博客将《tensorflow 实战》用Tensorflows实现的循环神经网络语言模型大大简化,增加了书上例子的可读性,在我学习的过程中给了我很多帮助。
3. 这个系列的第三部分,也是最后一部分,参考了上述两篇博客和Assignment3中的部分,最后算是整合成了一个符合自己预期的模型,也是自己收获最多的一部分。
4. 4. 最后感谢所有我在学习过程中互联网上间接或直接提供帮助的朋友,世界因为你们而美好。无法一一罗列,所有参考资料会放在文章最后

首先处理数据

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

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

主要完整代码:

#读取文件中的数据,将换行符转换为<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[:10])#['aer', 'banknote', 'berlitz', 'calloway', 'centrust', 'cluett', 'fromstein', 'gitano', 'guterman', 'hydro-quebec']
print(len(f))#929589


#构建词汇表,词与id互转:   word_to_id
#返回一tuple,为所有单词,按词频降序排列
#和一个字典,  键为所有的单词,值为单词所在tuple的位置

from collections import Counter
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])#('the', '<unk>', '<eos>', 'N', 'of', 'to', 'a', 'in', 'and', "'s")
print(list(map(lambda x: words_to_id[x], words[:10])))#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 构建id_to_word
#将一个文件转换成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[:10])#[9982, 9986, 9970, 9999, 9972, 9975, 9991, 9981, 9980, 9976]

#将一个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)
    #D单词表从训练集获取,应用到验证集和测试集上

    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

# ptb_raw_data()将文本数据转换成了一个数字构成list
# 下边的ptb_producer继续整理这些数据,将list转换成多个批次的数据
# 即,[batch_len, batch_size, numsteps]
# 文本数据从这下手,这里做的是前num_steps个字符预测第num_steps+1个字符
# 想做image caption的话,用类似的函数制作caption_in和caption_out就行

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预测next_words
    #他们也就是对应的X,Y  ?
    sentences = np.array(sentences)
    next_words = np.array(next_words)
    batch_len = len(sentences) // batch_size
    #为了满足随机梯度下降的需要,我们还需要把数据整理成一个个小的批次,
    #每次喂一个批次的数据给TensorFlow来更新权重,这样,
    # 数据就整理为[batch_len, batch_size, num_steps]的格式。
    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

#看一下数据是什么
#可见我们得到了14524个批次的数据,每个批次的训练集维度为[64, 20]。
import os
import numpy as np
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)#(14524, 64, 20)
print(y_train.shape)#(14524, 64)

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

模型构建

#!/usr/bin/python
# -*- coding: utf-8 -*-

import tensorflow as tf

class LMConfig(object):
    """Configuration of language model"""
    batch_size = 64    
    num_steps = 20     
    stride = 3        
    embedding_dim = 64   #词向量的维度
    hidden_dim = 128     
    num_layers = 2
    rnn_model = 'gru'    
    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 用于分类,以找出各词的概率

        #tf.layers.dense()是把最后一位hidden_dim变成vocab_size
        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)
        print('#####################')
        print(type(cost))
        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()

#运行
if __name__ == '__main__':
    run_epoch()

感谢:
使用TENSORFLOW训练循环神经网络语言模型

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值