TorchTextの使用例と完全なコード


TorchText


最近PyTorchを用いたNLPニューラルネットワークモデルの構築が開始され,torchtextというテキスト処理神器が発見され,テキストの前処理が容易になり,例えば,切り捨て補長,ワードテーブルの構築などが可能となった.しかしnlpの熱はcvに及ばないため、torchtextが紹介した関連ブログの数もtorchvisionに及ばない.使用中に主にA Comprehensive Introduction to TorchtextとLanguage modeling tutorial in torchtextの2つのブログとtorchtextの公式ドキュメントを参考にして、torchtextの基本的な使い方について大まかに理解しました.私のもう一つのブログ:PyTorchはNLPタスクで予備訓練語ベクトルを使用する上でtorchtextの使い方にも少し関与しています.以上の2つのブログの基礎の上で、本文はtorchtextの使用に対して1つの概括的な総括をして、更に複雑で高級な用法は依然としてみんなに公式のドキュメントを読むことを推薦します.本明細書で説明する完全な実行可能コードは、次のとおりです.https://github.com/atnlp/torchtext-summary

torchtextの概要


torchtextによるデータの処理は,Field,Dataset,反復器の3つの部分に要約できる.

Fieldオブジェクト


Fieldオブジェクトは、フィールドをどのように処理するかを指定します.

Dataset


データセットは、データソース情報を定義する.

反復器


反復器は、モデルに必要な処理後のデータを返す.反復器は主にIterator,BucketIerator,BPTTIteratorの3種類に分けられる.
  • Iterator:標準反復器
  • BucketIerator:標準反復器と比較して、同じ長さのサンプルをバッチとして処理します.テキスト処理では、現在のバッチの最長シーケンスの長さに各サンプルの長さを揃える必要があることが多いため、サンプルの長さの差が大きい場合、BucketIeratorを使用すると充填効率が向上します.その他、Fieldでfix_を通過することもできます.lengthパラメータを使用して、サンプルを切断して整列させます.
  • BPTTIterator:BPTT(時間ベースの逆伝播アルゴリズム)に基づく反復器であり、言語モデルで一般的に用いられる.

  • データセットクラスの使用


    実験データセットはA Comprehensive Introduction to Torchtextで使用されている小ロットデータセットを依然として使用しており,コードを簡略化するためにtoxicというラベル列のみが保持されている.
  • データセット
  • の表示
  • torchtext関連パッケージ
  • をインポート
    from torchtext import data
    from torchtext.vocab import Vectors
    from torch.nn import init
    from tqdm import tqdm
    
  • Fieldオブジェクトを構築する
  • tokenize = lambda x: x.split()
    # fix_length , 
    TEXT = data.Field(sequential=True, tokenize=tokenize, lower=True, fix_length=200)
    LABEL = data.Field(sequential=False, use_vocab=False)
    
  • torchtext内蔵データセットを使用してデータセット
  • を構築する.
    torchtextプリセットのDatasetクラスのAPIは、少なくともexamplesとfieldsの2つのパラメータを入力する必要があります.examplesはtorchtextのExampleオブジェクトのリストであり、Exampleはデータセットの1つのデータの抽象である.Fieldsは、各列のデータとFieldオブジェクトのバインド関係として簡単に理解でき、以下のコードではtrain_牙列缺损examplesトレーニングセットとテストセットのexamplesオブジェクトを構築するために、train_fieldsとtest_fieldsデータセットのfieldsオブジェクト.
    class torchtext.data.Dataset(examples, fields, filter_pred=None)
    #  
    train_data = pd.read_csv('data/train_one_label.csv')
    valid_data = pd.read_csv('data/valid_one_label.csv')
    test_data = pd.read_csv("data/test.csv")
    TEXT = data.Field(sequential=True, tokenize=tokenize, lower=True)
    LABEL = data.Field(sequential=False, use_vocab=False)
    
    # get_dataset Dataset examples fields
    def get_dataset(csv_data, text_field, label_field, test=False):
    	# id , None field
        fields = [("id", None), # we won't be needing the id, so we pass in None as the field
                     ("comment_text", text_field), ("toxic", label_field)]       
        examples = []
    
        if test:
            #  , label
            for text in tqdm(csv_data['comment_text']):
                examples.append(data.Example.fromlist([None, text, None], fields))
        else:
            for text, label in tqdm(zip(csv_data['comment_text'], csv_data['toxic'])):
                examples.append(data.Example.fromlist([None, text, label], fields))
        return examples, fields
    
    #  Dataset examples fields
    train_examples, train_fields = get_dataset(train_data, TEXT, LABEL)
    valid_examples, valid_fields = get_dataset(valid_data, TEXT, LABEL)
    test_examples, test_fields = get_dataset(test_data, TEXT, None, test=True)
    
    #  Dataset 
    train = data.Dataset(train_examples, train_fields)
    valid = data.Dataset(valid_examples, valid_fields)
    test = data.Dataset(test_examples, test_fields)
    

        data.Exampleは単一のサンプルを返し、fromCSVやfromJSONなどの複数の方法を提供し、データセットに必要な標準データを様々な形式で構築することができます.また,idのようなモデルトレーニングに不要な特徴については,Datasetの構築中にNoneを直接用いることができる.特に、testのlabelについては、マシン学習試合では最終的なテストセットのラベルが分からないため、ここではfieldsとexamplesを構築する際に相応にNoneに設定しており、自分で区切ったテストセットであれば、このときテストセットにも対応するラベルlabelがあり、対応コードを修正し、対応するField項でNoneを置き換える必要がある.

    カスタムデータセットクラス


    簡単なデータセットを構築する場合、torchを直接使用することができる.text.Datasetは、元のデータセットに対して単純な分割処理のみを行う場合、例えば、データの読み出しやトレーニングセット検証セットの分割などの操作を行う場合にも、csv、tsvなどのフォーマットの読み取りをサポートするTabularDatasetクラスやsplitクラスメソッドを直接使用して実現することができる.しかし、shuffle、dropoutなどのデータ強化操作など、データのより多くの前処理が必要な場合、カスタムデータsetはより柔軟になります.
  • コアコードは以下の
  • である.
    from torchtext import data
    from torchtext.vocab import Vectors
    from tqdm import tqdm
    import pandas as pd
    import numpy as np
    import torch
    import random
    import os
    
    train_path = 'data/train_one_label.csv'
    valid_path = "data/valid_one_label.csv"
    test_path = "data/test.csv"
    # Dataset
    class MyDataset(data.Dataset):
    
        def __init__(self, path, text_field, label_field, test=False, aug=False, **kwargs):
            fields = [("id", None), # we won't be needing the id, so we pass in None as the field
                     ("comment_text", text_field), ("toxic", label_field)]
            
            examples = []
            csv_data = pd.read_csv(path)
            print('read data from {}'.format(path))
    
            if test:
                #  , label
                for text in tqdm(csv_data['comment_text']):
                    examples.append(data.Example.fromlist([None, text, None], fields))
            else:
                for text, label in tqdm(zip(csv_data['comment_text'], csv_data['toxic'])):
                    if aug:
                        # do augmentation
                        rate = random.random()
                        if rate > 0.5:
                            text = self.dropout(text)
                        else:
                            text = self.shuffle(text)
                    # Example: Defines a single training or test example.Stores each column of the example as an attribute.
                    examples.append(data.Example.fromlist([None, text, label - 1], fields))
            #  , super , Dataset 。
            super(MyDataset, self).__init__(examples, fields, **kwargs)
    
        def shuffle(self, text):
            text = np.random.permutation(text.strip().split())
            return ' '.join(text)
    
        def dropout(self, text, p=0.5):
            # random delete some text
            text = text.strip().split()
            len_ = len(text)
            indexs = np.random.choice(len_, int(len_ * p))
            for i in indexs:
                text[i] = ''
            return ' '.join(text)
    

    torchtextを継承するMyDatasetクラスを定義しました.data.Datasetクラスは、initメソッド内でデータとfieldsパラメータのバインドを実現します.クラス内部でもshuffleとdropoutの2つのデータ前処理方法が実現された.最後にsuperを使用して親クラスを初期化し、torchtextの標準データセットを実現する.
    MyDatasetオブジェクトの構築testデータセットにラベルlabelがないため、label_を作成する必要があります.fieldはNoneでtrainに関する情報を表示できます

    反復の作成


    ニューラルネットワークを訓練する際にはbatchのデータを操作するのでtorchtext内部の反復器を用いてデータを処理する必要がある.
    from torchtext.data import Iterator, BucketIterator
    #  
    # train_iter = data.BucketIterator(dataset=train, batch_size=8, shuffle=True, sort_within_batch=False, repeat=False)
    
    #  
    train_iter, val_iter = BucketIterator.splits(
            (train, valid), #  
            batch_sizes=(8, 8),
            device=-1, #  gpu, -1 GPU 
            sort_key=lambda x: len(x.comment_text), # the BucketIterator needs to be told what function it should use to group the data.
            sort_within_batch=False,
            repeat=False # we pass repeat=False because we want to wrap this Iterator layer.
    )
    
    test_iter = Iterator(test, batch_size=8, device=-1, sort=False, sort_within_batch=False, repeat=False)
    

    BucketIteratorは、Iteratorと比較して、サンプルの長さが似ているデータを自動的に選択してバッチ・データを構築する利点があります.しかし、テストセットでは通常、サンプルの順序を変更したくないため、テストセットはIterator反復器を使用して構築されます.    sort_within_batchパラメータがTrueに設定されている場合はsort_keyは、各小ロット内のデータを降順に並べ替えます.paddedシーケンスでpackを使用する必要がある場合はpadded_sequenceがPackedSequenceオブジェクトに変換される場合、これは非常に重要です.pack_padded_sequenceメソッドでは、バッチサンプルを降順に並べ替える必要があります.このことから,torchtextはテキストデータを四角化するだけでなく,torchtextの多くの組み込み方法と組み合わせて容易に使用できることが分かる.
    実験的に選択したトレーニングセットには25のサンプルがあり,次にpythonに組み込まれたiterを用いて構築された反復器情報を表示することができる.出力結果から分かるように、反復器は、各ロットのデータに対して、サンプル長を統一的な長さに構築し、第1ロットのデータに対するcomment_textフィーチャーは,反復器が[200*8]の次元に統一的に構築された.TEXTを構築する際にfix_を通過するのでlength=200はシーケンスの長さを指定します.

    バッチデータの使用


    反復器を使用してバッチデータを構築した後、pythonのforループを直接使用して遍歴し、出力結果を観察することができ、25個のデータを共有するトレーニングセットに対してtorchtextの反復器はトレーニングセットを4ロットのデータに構築した.これに基づいて、モデルにデータを転送することができます.

    用語集の作成


    用語表の構築とは,各単語を符号化する必要があり,すなわち各単語を数字で表す必要があり,モデルに伝達できる.

    最も簡単な方法、bulid_vocab()メソッドには、用語集を構築するためのデータセットが入力されます。

    TEXT.build_vocab(train)
    

    事前に訓練された語ベクトルを使用する


    pytorchやtensorflowなどのニューラルネットワークフレームワークを用いてnlpタスクの処理を行う場合は,対応するEmbedding層を用いてワードベクトルの処理を行うことができ,より多くの場合,予め訓練されたワードベクトルを用いることでより優れた性能をもたらすが,torchtextで予め訓練されたワードベクトルを用い,さらにニューラルネットワークモデルに伝達して訓練する方法を紹介する.

    方法1:torchtextデフォルトでサポートされているプリトレーニングワードベクトルを使用する


    デフォルトでは、対応するプリトレーニングワードベクトルファイルが現在のフォルダに自動的にダウンロードされます.vector_Cacheディレクトリの下で、.vector_Cacheは、デフォルトのワードベクトルファイルとキャッシュファイルのディレクトリです.
    from torchtext.vocab import GloVe
    from torchtext import data
    TEXT = data.Field(sequential=True)
    #  
    # TEXT.build_vocab(train, vectors="glove.6B.200d")
    TEXT.build_vocab(train, vectors=GloVe(name='6B', dim=300))
    #  , glove.6B.zip , glove.6B.50d.txt, glove.6B.100d.txt, glove.6B.200d.txt, glove.6B.300d.txt , glove.6B.zip glove.6B.200d.txt .vector_cache ( , )。
    

    方式2:外部予習した語ベクトルを使う


    上記の予備訓練語ベクトルファイルの使用方法には、nlpタスクを行うたびに、語表を作成する際に対応する必要があるという大きな問題がある.vector_Cacheフォルダにプリトレーニングワードベクトルファイルをダウンロードし、この問題をどのように解決しますか?torchtextを使用できます.vocab.Vectorsのnameパラメータとcachaeパラメータは、事前に訓練されたワードベクトルファイルとキャッシュファイルのディレクトリを指定します.したがって,word 2 vecなどのツールで訓練したワードベクトルファイルを用いて,ネームで指定したディレクトリにワードベクトルファイルを置くだけでよい.
  • nameパラメータにより、予め訓練する語ベクトルファイルが存在するディレクトリ
  • を指定することができる.
    デフォルトでは、プリトレーニングワードベクトルファイルとキャッシュファイルのディレクトリの位置は、いずれも現在のディレクトリの下にある.vector_Cacheディレクトリは、nameパラメータでプリトレーニングワードベクトルファイルが存在するディレクトリを指定するが、キャッシュファイルのディレクトリは特に指定されていないため、現在のディレクトリの下に存在する必要がある.vector_Cacheディレクトリ.
    # glove.6B.200d.txt 
    if not os.path.exists(.vector_cache):
        os.mkdir(.vector_cache)
    vectors = Vectors(name='myvector/glove/glove.6B.200d.txt')
    TEXT.build_vocab(train, vectors=vectors)
    
  • cacheパラメータによりキャッシュディレクトリ
  • を指定する.
    #  , name , .vector_cache 
    cache = '.vector_cache'
        if not os.path.exists(cache):
            os.mkdir(cache)
    vectors = Vectors(name='myvector/glove/glove.6B.200d.txt', cache=cache)
    TEXT.build_vocab(train, vectors=vectors)
    

    モデルでEmbeddingレイヤのウェイト値を指定するには


    事前に訓練された語ベクトルを用いる場合,埋め込み行列の初期重みをニューラルネットワークモデルのEmbedding層で明確に伝達する必要がある.ウェイトは語彙のvectorsプロパティに含まれます.Pytorchによって構築されたEmbeddingレイヤを例に挙げます.
    #  pytorch Embedding 
    embedding = nn.Embedding(2000, 256)
    #  
    weight_matrix = TEXT.vocab.vectors
    embedding.weight.data.copy_(weight_matrix )
    #  requires_grad=True
    # embeddings.weight = nn.Parameter(embeddings, requires_grad=True)
    

    torchtextを使用して構築されたデータセットはLSTM用である


    データセットが小さすぎて収束できないため、demoとしてtorchtextとpytorchの間の使い方を熟知しているだけです.
  • コアコードは以下の
  • である.
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.optim as optim
    weight_matrix = TEXT.vocab.vectors
    
    class LSTM(nn.Module):
    
        def __init__(self):
            super(LSTM, self).__init__()
            self.word_embeddings = nn.Embedding(len(TEXT.vocab), 300)  # embedding shape: torch.Size([200, 8, 300])
            #  , 
            # embedding.weight.data.copy_(weight_matrix)
            self.lstm = nn.LSTM(input_size=300, hidden_size=128, num_layers=1)  # torch.Size([200, 8, 128])
            self.decoder = nn.Linear(128, 2)
    
        def forward(self, sentence):
            embeds = self.word_embeddings(sentence)
            lstm_out = self.lstm(embeds)[0]  # lstm_out:200x8x128
            #  
            final = lstm_out[-1]  # 8*128
            y = self.decoder(final)  # 8*2 
            return y
    
    def main():
    	model = LSTM()
    	model.train()
    	optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.01)
    	loss_funtion = F.cross_entropy
    	
    	for epoch, batch in enumerate(train_iter):
    	    optimizer.zero_grad()
    	    start = time.time()
    	    # text = batch.text.permute(1, 0)
    	    predicted = model(batch.comment_text)
    	
    	    loss = loss_funtion(predicted, batch.toxic)
    	    loss.backward()
    	    optimizer.step()
    	    print(loss)
    	    
    if __name__ == '__main__':
        main()	    
    

    説明


    torchtextを使用するほか、kerasにおけるpreprocessingパッケージにおける関連方法を用いてデータの前処理を行い、torchを使用することもできる.utils.data.TensorDatasetはデータセットを構築し、torchを使用する.utils.data.DataLoaderは反復器を構築します.このコードの一部は後日更新されます.

    コードの例


    本明細書に関連するコンテンツの完全なコード

  • 完全demoコードは私のgithub倉庫を参照してください.https://github.com/atnlp/torchtext-summary

  • torchtextを使用してデータセットを内蔵する例

    import torch
    from torchtext import data
    from torchtext import datasets
    from torchtext.vocab import GloVe
    import numpy as np
    
    def load_data(opt):
        # use torchtext to load data, no need to download dataset
        print("loading {} dataset".format(opt.dataset))
        # set up fields
        text = data.Field(lower=True, include_lengths=True, batch_first=True, fix_length=opt.max_seq_len)
        label = data.Field(sequential=False)
    
        # make splits for data
        train, test = datasets.IMDB.splits(text, label)
    
        # build the vocabulary
        text.build_vocab(train, vectors=GloVe(name='6B', dim=300))
        label.build_vocab(train)
    
        # print vocab information
        print('len(TEXT.vocab)', len(text.vocab))
        print('TEXT.vocab.vectors.size()', text.vocab.vectors.size()
    

    昨年、個人ブログで書いたブログのオリジナル記事を転載し、許可を得ずに転載してはならない.