[L1]使用LSTM实现语言模型-语料词典的生成

[L1]使用LSTM实现语言模型-语料词典的生成

这个专栏只会介绍一些代码技术实战,此文借鉴《tensorflow实战Google学习框架》不会涉及理论,如果想要了解理论可以去下面这个专栏:

深度学习与机器学习算法zhuanlan.zhihu.com图标

1.PTB数据集

PTB(Penn Treebank Dataset)文本数据集是目前语言模型学习中使用最为广泛的数据集。在使用数据集之前,我们肯定需要对PTB数据集做充分的了解,才能够为后续的处理提供方便。

PTB数据集的下载地址www.fit.vutbr.cz
下载解压后的文件目录

我们做RNN Language Mode的时候只需要使用data路径下的三个数据集即可:

ptb.test.txt #测试集数据文件
ptb.train.txt #训练集数据文件
ptb.valid.txt #验证集数据文件

当然这三个数据文件中的数据已经过预处理(未登录词都用<unk>进行替换,没有数字文本),相邻的单词之间用空格隔开。数据集中共包含了9998种不同的单词词汇。在构建词汇表的时候需要添加一些特殊的词汇:

  • <pad>填充词汇
  • <go>句子开始
  • <eos>句子结束
  • <unk>未知词

所以也就是说数据集中一共有10002种不同的词汇。

数据集的片段,可以看出已经处理了未登录词

这里需要注意的就是,虽然说PTB已经将输入数据中的低频词替换成了<unk>,但是可能我们有时候需要自己指定一个频率阈值,我们认为没有超过这个频率的值为一些低频词,都设置为<unk>。

2.数据的预处理

为了方便理解讲解说明时采用了代码段的方式实现,并没有使用函数进行抽象,后面会给出详细的函数代码。

2.1 文本文件 -- 》词汇表

为了将文本转换为模型可以读入的单词序列,需要将这些不同的词汇分别映射到0~10001(因为我们这里有10002种不同的单词)之间的整数编号。也就是说首先要按照词频的顺序为每个词汇分配一个编号,然后将这些词汇表保存到一个独立的vocab文件中。

import codecs
import collections
from operator import itemgetter
import re

RAW_DATA = r'./data/ptb.train.txt'
VOCAB_OUTPUT = r'./model/model_ptb/ptb.train.vocab'
_DIGIT_RE = re.compile(r"\d")
normalize_digits = True


counter = collections.Counter()
with codecs.open(RAW_DATA,'r','utf-8') as f:
    for line in f:
        for word in line.strip().split():
            #判断是否有数字,如果有的话就将其替换成"0"
            #当时ptb中没有数字,所以不需要对数字进行处理
            # word = _DIGIT_RE.sub(r"0", word) if normalize_digits else word
            counter[word] += 1

print(counter.items())

#按照词频顺序对单词进行排序
sorted_word_to_cnt = sorted(counter.items(),key = itemgetter(1),reverse = True)
sorted_words = [x[0] for x in sorted_word_to_cnt]


#然后我们需要加入那些特殊特殊的词汇,这里预先将其加入词汇表中
sorted_words = ["<pad>","<go>","<eos>"] + sorted_words

# 设置max_vocabulary_size词汇表的最大长度,如果没有超过一定频率的就认为是未登录词
# if len(sorted_words) > max_vocabulary_size:
#     sorted_words = sorted_words[:max_vocabulary_size]

with codecs.open(VOCAB_OUTPUT,'w','utf-8') as file_output:
    for word in sorted_words:
        file_output.write(word+"\n")

执行代码会发现在" r'./model/model_ptb/ptb.train.vocab'"这个路径下生成一个ptb.train.vocab的文件。当然无论是训练集、验证集还是测试集我们的字典都是一样的,这个其实很好理解,只有词与数字统一起来,在训练集上训练,验证集验证以及最后的测试才能够使其表示的单词一致。

目录结构
ptb.train.vocab文件内容

下面说一下代码中的几个关键点:

counter = collections.Counter()

Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型(所以需要后期进行排序的处理),以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。为hashable对象计数,是字典的子类。

from collections import Counter
cnt = Counter()
for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
    cnt[word] += 1
print(cnt)

'''
result:
    Counter({'blue': 3, 'red': 2, 'green': 1})
'''
sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list
iterable:是可迭代类型;
cmp:用于比较的函数,比较什么由key决定;
key:用列表元素的某个属性或函数进行作为关键字,有默认值,迭代集合中的一项;
reverse:排序规则. reverse = True 降序 或者 reverse = False 升序,有默认值。
返回值:是一个经过排序的可迭代类型,与iterable一样。

counter.items()返回dict_items,将键值对变成一个个的元组。指定sorted的key为itemgetter(1),便以每个键值对元组下标为 1 的元素进行排序。这样就完成按照词频的大小进行排序构建词汇表的工作。

2.2 文本文件 -- 》单词的编号

上面的2.1小节确定了词汇表以后,再将训练文本、测试文本等都根据词汇文件转换为单词编号。每个单词的编号就是他在词汇文件中的行号。这里需要注意的就是我们仅仅使用train样本构建词汇表,然后根据这个词汇表去替换ptb.test.txt,ptb.train.txt,ptb.valid.txt中的单词,也就是将单词换成对应词汇表中的词频。

下面是是对train样本中的单词进行替换:

import codecs

RAW_DATA = r"./data/ptb.train.txt"#原始的训练数据集文件
VOCAB = r"./model/model_ptb/ptb.train.vocab"#词汇表文件
OUTPUT_DATA = r"./model/model_ptb_id/ptb.train.id"#将单词替换为单词编号后的输出文件

#读取词汇表,并建立词汇到单词编号的映射
with codecs.open(VOCAB,'r',"utf-8") as f_vocab:
    vocab = [w.strip() for w in f_vocab.readlines()]

#以字典的形式构建单词与行号的一个映射关系
word_to_id = {k:v for (k,v) in zip(vocab,range(len(vocab)))}

#如果出现了被删除的低频词,则替换为'<unk>'
def get_id(word):
    return word_to_id[word] if word in word_to_id else word_to_id['<unk>']

fin = codecs.open(RAW_DATA,'r',"utf-8")
fout = codecs.open(OUTPUT_DATA,'w',"utf-8")
for line in fin:
    #读取单词并添加<go>以及<eos>
    words = ['<go>'] + line.strip().split() + ['<eos>']
    #将每个单词替换为词汇表中的编号
    output_line = ' '.join([str(get_id(w)) for w in words]) + '\n'
    fout.write(output_line)

fin.close()
fout.close()
处理后的结果

对valid以及test样本同理使train中的方法即可:

数据处理以及处理后的结构

那大致的流程:

  1. 构建词汇表
    1. 需要在训练样本中统计语料中出现的单词,按照词频进行排序,一行一个单词;
    2. 为每个单词分配一个ID,这个ID就是单词的(行数-1),因为ID从0开始;
    3. 将词汇表存放到一个vocab文件中;
  2. 替换文本单词
    1. 将文本转化为用单词编号的形式来表示;
编辑于 2018-07-29

文章被以下专栏收录