• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    torchtext入门教程必看,带你轻松玩转文本数据处理

    用深度学习做nlp也有一段时间了,熟悉这块内容的同学都知道,实践算法的时候,写模型是个简单的事,最麻烦的是数据处理,数据处理不仅会浪费我们大部分时间,而且会消耗很大的计算资源,浪费人力物力。

    今年开始接触pytorch,简洁的API,动态图,更加灵活的编写模式,诸多优点不用多说。

    最近尝试使用torchtext工具,这里想先说明的是,torchtext并不是pytorch所独有的,使用其它深度学习框架,torchtext仍然可以使用。

    但是比较麻烦的是,并没有很好很全面的torchtext教程,给同学们入门造成了一定麻烦,这也是我写这篇文章的目的。

    首先整体介绍一下torchtext的组件

    torchtext包含以下组件:

    Field :主要包含以下数据预处理的配置信息,比如指定分词方法,是否转成小写,起始字符,结束字符,补全字符以及词典等等

    Dataset :继承自pytorch的Dataset,用于加载数据,提供了TabularDataset可以指点路径,格式,Field信息就可以方便的完成数据加载。同时torchtext还提供预先构建的常用数据集的Dataset对象,可以直接加载使用,splits方法可以同时加载训练集,验证集和测试集。

    Iterator : 主要是数据输出的模型的迭代器,可以支持batch定制

    1. Field

    Field 包含一写文本处理的通用参数的设置,同时还包含一个词典对象,可以把文本数据表示成数字类型,进而可以把文本表示成需要的tensor类型

    以下是Field对象包含的参数:

    sequential: 是否把数据表示成序列,如果是False, 不能使用分词 默认值: True.

    use_vocab: 是否使用词典对象. 如果是False 数据的类型必须已经是数值类型. 默认值: True.

    init_token: 每一条数据的起始字符 默认值: None.

    eos_token: 每条数据的结尾字符 默认值: None.

    fix_length: 修改每条数据的长度为该值,不够的用pad_token补全. 默认值: None.

    tensor_type: 把数据转换成的tensor类型 默认值: torch.LongTensor.

    preprocessing:在分词之后和数值化之前使用的管道 默认值: None.

    postprocessing: 数值化之后和转化成tensor之前使用的管道默认值: None.

    lower: 是否把数据转化为小写 默认值: False.

    tokenize: 分词函数. 默认值: str.split.

    include_lengths: 是否返回一个已经补全的最小batch的元组和和一个包含每条数据长度的列表 . 默认值: False.

    batch_first: Whether to produce tensors with the batch dimension first. 默认值: False.

    pad_token: 用于补全的字符. 默认值: "pad>".

    unk_token: 不存在词典里的字符. 默认值: "unk>".

    pad_first: 是否补全第一个字符. 默认值: False.

    重要的几个方法:

    pad(minibatch): 在一个batch对齐每条数据

    build_vocab(): 建立词典

    numericalize(): 把文本数据数值化,返回tensor

    简单的栗子如下,建一个Field对象

    TEXT = data.Field(tokenize=data.get_tokenizer('spacy'), 
                      init_token='SOS>', eos_token='EOS>',lower=True)

    2.Dataset

    torchtext的Dataset是继承自pytorch的Dataset,提供了一个可以下载压缩数据并解压的方法(支持.zip, .gz, .tgz)

    splits方法可以同时读取训练集,验证集,测试集

    TabularDataset可以很方便的读取CSV, TSV, or JSON格式的文件,例子如下:

    train, val, test = data.TabularDataset.splits(
            path='./data/', train='train.tsv',
            validation='val.tsv', test='test.tsv', format='tsv',
            fields=[('Text', TEXT), ('Label', LABEL)])
    

    加载数据后可以建立词典,建立词典的时候可以使用与训练的word vector

    TEXT.build_vocab(train, vectors="glove.6B.100d")

    3. Iterator

    Iterator是torchtext到模型的输出,它提供了我们对数据的一般处理方式,比如打乱,排序,等等,可以动态修改batch大小,这里也有splits方法 可以同时输出训练集,验证集,测试集

    参数如下:

    dataset: 加载的数据集

    batch_size: Batch 大小.

    batch_size_fn: 产生动态的batch大小 的函数

    sort_key: 排序的key

    train: 是否是一个训练集

    repeat: 是否在不同epoch中重复迭代

    shuffle: 是否打乱数据

    sort: 是否对数据进行排序

    sort_within_batch: batch内部是否排序

    device: 建立batch的设备 -1:CPU ;0,1 ...:对应的GPU

    使用方式如下:

    train_iter, val_iter, test_iter = data.Iterator.splits(
            (train, val, test), sort_key=lambda x: len(x.Text),
            batch_sizes=(32, 256, 256), device=-1)

    4.其他

    torchtext提供常用文本数据集,并可以直接加载使用:

    train,val,test = datasets.WikiText2.splits(text_field=TEXT)
    

    现在包含的数据集包括:

    Sentiment analysis: SST and IMDb
    Question classification: TREC
    Entailment: SNLI
    Language modeling: WikiText-2
    Machine translation: Multi30k, IWSLT, WMT14

    完整例子如下,短短几行就把词典和数据batch做好了。

    import spacy
    import torch
    from torchtext import data, datasets
    spacy_en = spacy.load('en')
    def tokenizer(text): # create a tokenizer function
        return [tok.text for tok in spacy_en.tokenizer(text)]
    TEXT = data.Field(sequential=True, tokenize=tokenizer, lower=True, fix_length=150)
    LABEL = data.Field(sequential=False, use_vocab=False)
    train, val, test = data.TabularDataset.splits(
            path='./data/', train='train.tsv',
            validation='val.tsv', test='test.tsv', format='tsv',
            fields=[('Text', TEXT), ('Label', LABEL)])
    TEXT.build_vocab(train, vectors="glove.6B.100d")
    train_iter, val_iter, test_iter = data.Iterator.splits(
            (train, val, test), sort_key=lambda x: len(x.Text),
            batch_sizes=(32, 256, 256), device=-1)
    vocab = TEXT.vocab
    

    补充:使用TorchText处理我们自己的数据集

    TorchText可以读取三种数据格式:json, tsv (tab separated values 制表分隔值)和csv(comma separated values 逗号分隔值)。

    处理JSON数据

    从json开始,你的数据必须是json行格式,也就是说,它必须是这样的:

    {"name": "John", "location": "United Kingdom", "age": 42, "quote": ["i", "love", "the", "united kingdom"]}
    {"name": "Mary", "location": "United States", "age": 36, "quote": ["i", "want", "more", "telescopes"]}
    

    也就是说,每一行都是一个json对象。data/trian.json为例。

    然后我们定义字段:

    from torchtext import data
    from torchtext import datasets
    NAME = data.Field()
    SAYING = data.Field()
    PLACE = data.Field()
    

    接下来,我们必须告诉TorchText哪个字段应用于json对象的哪个元素。

    对于json数据,我们必须创建一个字典:

    键与json对象的键匹配

    值为元组,其中:

    第一个元素成为batch对象的属性名

    第二个元素是字段的名称

    一些注意事项:

    fields字典中键的顺序并不重要,只要它的键与json数据键匹配即可。

    字段名不必与json对象中的键匹配,例如,我们使用PLACE来表示“location”字段。

    当处理json数据时,并不是所有的键都必须使用,例如,我们没有使用“age”字段。

    同样,如果json字段的值是一个字符串,那么将应用字段标记化(默认情况下是将字符串按空格分隔),然而,如果值是一个列表,则不应用标记化。通常情况下,将数据标记为一个列表是一个好主意,这节省了时间,因为您不必等待TorchText来做这件事。

    json字段的值不必是相同的类型。有些例子的“引号”可以是字符串,有些则是列表。标记化将只应用于那些以“引号”表示字符串的字符串。

    如果你正在使用一个json字段,每个例子必须有一个该字段的实例,例如在这个例子中所有的例子必须有一个name,location和quote。但是,由于我们没有使用age字段,因此示例中没有age字段也没有关系。

    fields = {'name': ('n', NAME), 'location': ('p', PLACE), 'quote': ('s', SAYING)}

    现在,在训练循环中,我们可以通过数据迭代器进行迭代并且通过batch.n访问name,通过batch.p访问location,通过batch.s访问quote。

    然后我们使用TabularDataset.splits函数创建我们的数据集(train_data和test_data)

    path参数指定两个数据集中共同的顶级文件夹,train和test参数指定每个数据集的文件名,例如,这里的train数据集位于data/train.json。

    我们告诉函数我们正在使用json数据,并将前面定义的fields字典传递给它。

    train_data, test_data = data.TabularDataset.splits(
                                path = 'data',
                                train = 'train.json',
                                test = 'test.json',
                                format = 'json',
                                fields = fields
    )

    如果已经有验证数据集,则可以将其路径作为validation 参数传递。

    train_data, valid_data, test_data = data.TabularDataset.splits(
                                            path = 'data',
                                            train = 'train.json',
                                            validation = 'valid.json',
                                            test = 'test.json',
                                            format = 'json',
                                            fields = fields
    )

    然后,我们可以查看一个示例,以确保它已经正确地工作。

    请注意字段名(n、p和s)是如何与fields字典中定义的内容匹配的。

    还请注意p中的单词“United Kingdom”是如何被标记化分开的,而s中的“United Kingdom”则没有。这是由于前面提到的原因,TorchText假设任何作为列表的json字段都已经被标记化了,并且不再应用进一步的标记化。

    print(vars(train_data[0]))
    {'n': ['John'], 'p': ['United', 'Kingdom'], 's': ['i', 'love', 'the', 'united kingdom']}
    

    现在我们可以使用train_data、test_data和valid_data来构建词汇表并创建迭代器。我们可以使用batch.n, batch.p and batch.s访问分别表示names、places和sayings的所有属性。

    处理CSV/TSV数据

    csv和tsv非常相似,只是csv的元素用逗号分隔,而tsv用制表符分隔。

    用上面的例子,我们的tsv数据将会是:

    name    location    age quote
    John    United Kingdom  42  i love the united kingdom
    Mary    United States   36  i want more telescopes

    也就是说,每一行的元素都由制表符分隔,每行有一个示例。第一行通常是标题(即每个列的名称),但你的数据也可能没有标题。

    tsv或csv数据中不能有列表。

    字段的定义方式与json稍有不同。现在我们使用一个元组列表,其中每个元素也是一个元组。这些内部元组的第一个元素将成为batch对象的属性名,第二个元素是字段名。

    与json数据不同,元组必须与tsv数据中的顺序相同。因此,当跳过一列数据时,需要使用一个none元组,如果没有,那么我们的SAYING字段将应用到tsv数据的age列,而quote列将不会被使用。

    但是,如果您只想使用name和age列,您可以只使用两个元组,因为它们是前两个列。

    我们更改TabularDataset以读取正确的.tsv文件,并将format参数更改为'tsv'。

    如果你的数据有一个标题头,我们的数据就有,它必须通过传递skip_header = True来跳过。如果没有,TorchText会认为头部是一个例子。默认情况下,skip_header为False。

    fields = [('n', NAME), ('p', PLACE), (None, None), ('s', SAYING)]
    train_data, valid_data, test_data = data.TabularDataset.splits(
                                            path = 'data',
                                            train = 'train.tsv',
                                            validation = 'valid.tsv',
                                            test = 'test.tsv',
                                            format = 'tsv',
                                            fields = fields,
                                            skip_header = True
    )
    print(vars(train_data[0]))
    {'n': ['John'], 'p': ['United', 'Kingdom'], 's': ['i', 'love', 'the', 'united', 'kingdom']}
    

    最后,我们将讨论csv文件。

    这与tsv文件几乎完全相同,只是格式参数设置为“csv”。

    fields = [('n', NAME), ('p', PLACE), (None, None), ('s', SAYING)]
    train_data, valid_data, test_data = data.TabularDataset.splits(
                                            path = 'data',
                                            train = 'train.csv',
                                            validation = 'valid.csv',
                                            test = 'test.csv',
                                            format = 'csv',
                                            fields = fields,
                                            skip_header = True
    )
    
    print(vars(train_data[0]))
    {'n': ['John'], 'p': ['United', 'Kingdom'], 's': ['i', 'love', 'the', 'united', 'kingdom']}

    为什么JSON好于CSV/TSV?

    csv或tsv数据无法存储列表。这意味着数据不能被标记化,因此每次运行通过TorchText读取数据的Python脚本时,数据都必须被标记化。使用高级的标记器,如spaCy标记器,需要不可忽略的大量时间。因此,最好是对数据集进行标记并以json行格式存储它们。

    如果tsv数据中出现制表符,或csv数据中出现逗号,TorchText会认为它们是列之间的分隔符。这将导致数据被错误地解析。最糟糕的是,TorchText不会提醒你这一点,因为它无法分辨字段中的制表符/逗号和作为分隔符的制表符/逗号之间的区别。由于json数据本质上是一个字典,您可以通过它的键访问字段中的数据,所以不必担心“surprise”分隔符。

    迭代器

    使用上面的任何数据集,我们就可以构建词汇表并创建迭代器。

    NAME.build_vocab(train_data)
    SAYING.build_vocab(train_data)
    PLACE.build_vocab(train_data)

    然后,我们可以在定义批处理大小和设备后,创建迭代器。

    默认情况下,训练数据在每个epoch进行洗牌,但验证/测试数据是排序的。然而,TorchText不知道该用什么来排序我们的数据,如果我们不告诉它,它就会抛出错误。

    有两种方法来处理这个问题,你可以通过传递sort = False来告诉迭代器不要对验证/测试数据进行排序,或者你可以通过传递sort_key来告诉迭代器如何对数据进行排序。sort key是一个函数,它返回一个用于对数据进行排序的键。例如,lambda x: x.s将根据它们的s属性(即它们的quote)对示例进行排序。理想情况下,您希望使用sort key,因为BucketIterator将能够对示例进行排序,然后最小化每个批处理中的填充量。

    然后,我们可以遍历迭代器来获得批量数据。注意,默认情况下TorchText的批处理维度是在第二维。

    import torch
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    BATCH_SIZE = 1
    train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
        (train_data, valid_data, test_data),
        sort = False, #don't sort test/validation data
        batch_size=BATCH_SIZE,
        device=device)
    train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
        (train_data, valid_data, test_data),
        sort_key = lambda x: x.s, #sort by s attribute (quote)
        batch_size=BATCH_SIZE,
        device=device)
    print('Train:')
    for batch in train_iterator:
        print(batch)
        
    print('Valid:')
    for batch in valid_iterator:
        print(batch)
        
    print('Test:')
    for batch in test_iterator:
        print(batch)
    
    Train:
    [torchtext.data.batch.Batch of size 1]
    	[.n]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.p]:[torch.cuda.LongTensor of size 2x1 (GPU 0)]
    	[.s]:[torch.cuda.LongTensor of size 5x1 (GPU 0)]
    [torchtext.data.batch.Batch of size 1]
    	[.n]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.p]:[torch.cuda.LongTensor of size 2x1 (GPU 0)]
    	[.s]:[torch.cuda.LongTensor of size 4x1 (GPU 0)]
    Valid:
    [torchtext.data.batch.Batch of size 1]
    	[.n]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.p]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.s]:[torch.cuda.LongTensor of size 2x1 (GPU 0)]
    [torchtext.data.batch.Batch of size 1]
    	[.n]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.p]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.s]:[torch.cuda.LongTensor of size 4x1 (GPU 0)]
    Test:
    [torchtext.data.batch.Batch of size 1]
    	[.n]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.p]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.s]:[torch.cuda.LongTensor of size 3x1 (GPU 0)]
    [torchtext.data.batch.Batch of size 1]
    	[.n]:[torch.cuda.LongTensor of size 1x1 (GPU 0)]
    	[.p]:[torch.cuda.LongTensor of size 2x1 (GPU 0)]
    	[.s]:[torch.cuda.LongTensor of size 3x1 (GPU 0)]
    

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

    您可能感兴趣的文章:
    • 使用pytorch和torchtext进行文本分类的实例
    • pytorch 数据处理:定义自己的数据集合实例
    • PyTorch加载自己的数据集实例详解
    • pytorch实现建立自己的数据集(以mnist为例)
    • pytorch加载自己的图像数据集实例
    上一篇:python处理emoji表情(两个函数解决两者之间的联系)
    下一篇:beam search及pytorch的实现方式
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    torchtext入门教程必看,带你轻松玩转文本数据处理 torchtext,入门教程,必看,