当前位置:   article > 正文

构建词表与抽样——【torch学习笔记】_load_data_time_machine

load_data_time_machine

构建词表与抽样

引用翻译:《动手学深度学习

语言符号(又称词)的数量很大,而且分布很不均匀。因此,预测下一个符号的简单多类分类方法并不总是很有效。此外,我们需要把文本变成我们可以优化的格式,即我们需要把它映射到向量。在其极端情况下,我们有两种选择。一种是将每个词作为一个独特的实体,例如Salton.Wong.Yang.1975。这种策略的问题是,对于非常大的、多样化的语料库,我们很可能要处理100,000到1,000,000个向量。

另一个极端是每次预测一个字符的策略,如Ling等人,2015年提出的。两种策略之间的一个很好的平衡点是字节对编码,如Sennrich、Haddow和Birch, 2015年为神经机器翻译的目的所描述的。它将文本分解为经常出现的类似音节的片段。这使得模型能够根据先前查看的单词,如异质、同质、图和五边形,生成异质或五边形等单词。探讨这些模型的细节已经超出了本章的范围。我们将在以后讨论自然语言处理(chapter_nlp)时更详细地讨论这个问题。我只想说,它可以大大促进自然语言处理模型的准确性。

为了简单起见,我们将把自己限制在纯字符序列上。我们像以前一样使用H.G. Wells的The Timemachine。我们首先对文本进行过滤,并将其转换为一个字符ID的序列。

一、数据加载

和以前一样,我们开始加载数据,并将其映射为一连串的空白处、标点符号和常规字符。预处理是最小的,我们只限于去除多个空白。

import sys
sys.path.insert(0, '..')

import torch
import random
import collections

with open('../data/timemachine.txt', 'r') as f:
    raw_text = f.read()
    
print(raw_text[0:210])  #  raw_text存储的是文本,未经过任何处理
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
The Time Machine, by H. G. Wells [1898]

The Time Traveller (for so it will be convenient to speak of him)
was expounding a recondite matter to us. His grey eyes shone and
twinkled, and his usually pale 
  • 1
  • 2
  • 3
  • 4
  • 5

二、符号化

接下来,我们需要将数据集,即一个字符串,分割成标记。一个标记是模型要训练和预测的一个数据点。我们通常使用一个词或一个字符作为一个标记。

lines = raw_text.split('\n')
text = ' '.join(' '.join(lines).lower().split())  # 全转化为小写,且将各句用空格连接起来
print('# of chars:', len(text))
print(text[0:70])
  • 1
  • 2
  • 3
  • 4
# of chars: 178605
the time machine, by h. g. wells [1898] i the time traveller (for so i
  • 1
  • 2

三、词汇表

然后,我们需要将令牌映射成数字索引。我们通常称它为词汇表。它的输入是一个标记的列表,称为语料库。

然后,它计算每个标记在这个语料库中的频率,然后根据其频率给每个标记分配一个数字索引。很少出现的标记经常被删除以减少复杂性。

一个在语料库中不存在或已被删除的标记被映射为一个特殊的未知(“< unk>”)标记。我们还可以选择添加另外三个特殊标记。"< pad>“是一个用于填充的标记,”< bos>“表示一个句子的开始,”< eos>"表示一个句子的结束。

class Vocab(object):  
    def __init__(self, tokens, min_freq=0, use_special_tokens=False):
        # 通过frequency and token排序
        counter = collections.Counter(tokens)  # 对词进行统计计数
        token_freqs = sorted(counter.items(), key=lambda x: x[0])  # 根据统计的结果进行排序,全量的排序结果
        token_freqs.sort(key=lambda x: x[1], reverse=True)  # 根据token名称进行排序,相当于优先以频率排,再以名称排
        if use_special_tokens:
            # 填充,句首,句尾,未知
            self.pad, self.bos, self.eos, self.unk = (0, 1, 2, 3)
            tokens = ['<pad>', '<bos>', '<eos>', '<unk>']  # 对四种类型标记建立标签映射
        else:
            self.unk = 0
            tokens = ['<unk>'] 
        tokens +=  [token for token, freq in token_freqs if freq >= min_freq]  # 对于频率较低的token进行过滤
        self.idx_to_token = []
        self.token_to_idx = dict()  # 建立token和id的映射词典,即vocab.txt
        for token in tokens: 
            self.idx_to_token.append(token) # 记录token词
            self.token_to_idx[token] = len(self.idx_to_token) - 1  # 按顺序从0开始编号

    def __len__(self):
        return len(self.idx_to_token)

    def __getitem__(self, tokens):
        if not isinstance(tokens, (list, tuple)):
            return self.token_to_idx.get(tokens, self.unk)  # 未出现词添加unk标签
        else:
            return [self.__getitem__(token) for token in tokens]  # 出现过的就不管

    def to_tokens(self, indices):
        if not isinstance(indices, (list, tuple)):
            return self.idx_to_token[indices]
        else:
            return [self.idx_to_token[index] for index in indices]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

我们以时间机器数据集为语料库构建了一个词汇表,然后打印标记与索引之间的映射。

vocab = Vocab(text)
print(vocab.token_to_idx)
  • 1
  • 2
{'<unk>': 0, ' ': 1, 'e': 2, 't': 3, 'a': 4, 'i': 5, 'n': 6, 'o': 7, 's': 8, 'h': 9, 'r': 10, 'd': 11, 'l': 12, 'm': 13, 'u': 14, 'c': 15, 'f': 16, 'w': 17, 'g': 18, 'y': 19, 'p': 20, ',': 21, 'b': 22, '.': 23, 'v': 24, 'k': 25, "'": 26, '-': 27, 'x': 28, 'z': 29, ';': 30, 'j': 31, '?': 32, 'q': 33, '!': 34, '"': 35, '_': 36, ':': 37, '(': 38, ')': 39, '8': 40, '[': 41, ']': 42, '1': 43, '9': 44}
  • 1

之后,训练数据集中的每个字符都被转换为一个索引ID。为了说明问题,我们打印前20个字符和它们相应的索引。

corpus_indices = [vocab[char] for char in text]  # 将字符转化为index编码
sample = corpus_indices[:15]
print('chars:', [vocab.idx_to_token[idx] for idx in sample])  # 将idx转化为字符
print('indices:', sample)
  • 1
  • 2
  • 3
  • 4
chars: ['t', 'h', 'e', ' ', 't', 'i', 'm', 'e', ' ', 'm', 'a', 'c', 'h', 'i', 'n']
indices: [3, 9, 2, 1, 3, 5, 13, 2, 1, 13, 4, 15, 9, 5, 6]
  • 1
  • 2

四、训练数据准备

在训练过程中,我们需要随机读取小型批次的例子和标签。由于序列数据在本质上是有顺序的,我们需要解决处理它的问题。当我们在chapter_sequence中介绍时,我们是以一种相当临时的方式进行的。让我们把这个问题正式化一下。考虑一下我们刚刚处理的这本书的开头。如果我们想把它分割成每个5个符号的序列,我们有相当大的自由,因为我们可以选择一个任意的偏移量。

在这里插入图片描述

图:拆分文本时,不同的偏移量会导致不同的子序列。

事实上,这些偏移量中的任何一个都是可以的。因此,我们应该选择哪一个呢?

事实上,所有的偏移量都是一样好的。但是如果我们挑选所有的偏移量,由于重叠,我们最终会得到相当多余的数据,特别是如果序列很长的话。

仅仅选取一组随机的初始位置也不好,因为它不能保证阵列的均匀覆盖。

例如,如果我们从一组

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/365867
推荐阅读
相关标签