PyTorch LSTM:テキスト生成チュートリアル


長期短期記憶(lstm)は,人気のリカレントニューラルネットワーク(rnn)アーキテクチャである.このチュートリアルでは、テキストを生成するためのPyTorchでlstmsを使用していますこの場合では-かなり不気味なジョーク.
このチュートリアルで必要なのは、
  • Python、Pytorch、および機械学習の基礎知識
  • ローカルインストールPython V 3 +,
    PyTorch V 1 +NumPy V 1 +
  • LSTMとは


    lstmは深い学習に使用されるrnnの変形である.データのシーケンスで作業している場合は、lstmsを使用できます.
    LSTMネットワークにとって最も簡単なユースケースです.
  • 時系列予測(株予測)
  • テキスト生成
  • ビデオ分類
  • 音楽世代
  • 異常検出
  • RNN


    LSTMSを使用する前に、RNNがどのように動作するかを理解する必要があります.
    rnnsは順序データと良いニューラルネットワークである.これは、ビデオ、オーディオ、テキスト、株式市場時系列、または単一のイメージもその部分のシーケンスにカットすることができます.
    標準ニューラルネットワーク(畳み込みまたはバニラ)は、RNNと比較すると、1つの主要な欠点があります.過去の入力のメモリのいくつかの種類なしでいくつかのマシンの学習問題を解決することはできません.
    例えば、あなたがボールに移動し、ボールの方向を予測したいいくつかのビデオフレームを持っているときに問題に実行することがあります.標準的なニューラルネットワークは、問題を見ている方法です:あなたは1つのイメージでボールを持っているし、別の画像でボールを持っている.これらの2つの画像をシーケンスとして接続する機構はありません.標準的なニューラルネットワークは、「ボールが動いている」概念にボールの2つの別々のイメージを接続することができません見ているのは、画像がボール1で1、ボールが2・2でイメージがあるということですが、ネットワーク出力は別々です.

    これは、最後のフレームを記憶し、その次の予測を知らせるために使用することができますRnnにこれを比較する.

    LSTM対RNN


    典型的なRNNは長い配列を記憶できない.「消失勾配」と呼ばれる効果は、RNNセルネットワークの逆伝搬段階で起こる.シーケンスの開始から情報を運ぶセルの勾配は、小さな数による行列乗算を通り抜けて、長い系列で0に近づきます.言い換えると、シーケンスの開始時の情報はシーケンスの終わりにほとんど影響を与えません.
    リカレントニューラルネットワークの例で示すことができます.長い十分なシーケンスを与えられて、シーケンスの最初の要素からの情報は、シーケンスの最後の要素の出力に影響を与えません.
    LSTMは長いシーケンスを記憶できるRNNアーキテクチャである.LSTMは、長期メモリがLSTMセルに流れ続けることを可能にするメモリゲート機構を有する.

    Pytorchによるテキスト生成


    あなたはPPTRCHでLSTMネットワークを使用してジョークテキストジェネレータを訓練し、最善の実践に従ってください.コードを保存する新しいフォルダを作成します.
    $ mkdir text-generation
    

    モデル


    LSTMモデルを作成するには、ファイルを作成しますmodel.pytext-generation 次のコンテンツを持つフォルダ
    import torch
    from torch import nn
    
    class Model(nn.Module):
        def __init__(self, dataset):
            super(Model, self).__init__()
            self.lstm_size = 128
            self.embedding_dim = 128
            self.num_layers = 3
    
            n_vocab = len(dataset.uniq_words)
            self.embedding = nn.Embedding(
                num_embeddings=n_vocab,
                embedding_dim=self.embedding_dim,
            )
            self.lstm = nn.LSTM(
                input_size=self.lstm_size,
                hidden_size=self.lstm_size,
                num_layers=self.num_layers,
                dropout=0.2,
            )
            self.fc = nn.Linear(self.lstm_size, n_vocab)
    
        def forward(self, x, prev_state):
            embed = self.embedding(x)
            output, state = self.lstm(embed, prev_state)
            logits = self.fc(output)
    
            return logits, state
    
        def init_state(self, sequence_length):
            return (torch.zeros(self.num_layers, sequence_length, self.lstm_size),
                    torch.zeros(self.num_layers, sequence_length, self.lstm_size))
    
    これは標準的なピルッチモデルです.Embedding レイヤー
    単語のインデックスを単語のベクトルに変換します.LSTM ネットワークの主な学習可能な部分です- Pytorch実装は、内部に実装されたゲート機構を持っていますLSTM データの長いシーケンスを学ぶことができるセル.
    前述の通りWhat is LSTM? セクション- RNNSとLSTMSは、彼らがトレーニングエピソードの間で運ぶ余分の州の情報を持っています.forward 関数はprev_state 引数.この状態は、モデル外に手動で渡される.
    またinit_state 関数.すべてのエポックの開始時にこれを呼び出して、状態の正しい形を初期化します.

    データセット


    このチュートリアルでは、ネットワークを訓練するためにredditのクリーンジョークデータセットを使用します.Download (139KB) データセットを入れ、text-generation/data/ フォルダ.
    データセットには1623個のジョークがあります.
    ID,Joke
    1,What did the bartender say to the jumper cables? You better not try to start anything.
    2,Don't you hate jokes about German sausage? They're the wurst!
    3,Two artists had an art contest... It ended in a draw
    …
    
    データをPyTorchにロードするには、PyTorchDataset クラス.クリエイトアdataset.py 以下の内容を持つファイル
    import torch
    import pandas as pd
    from collections import Counter
    
    class Dataset(torch.utils.data.Dataset):
        def __init__(
            self,
            args,
        ):
            self.args = args
            self.words = self.load_words()
            self.uniq_words = self.get_uniq_words()
    
            self.index_to_word = {index: word for index, word in enumerate(self.uniq_words)}
            self.word_to_index = {word: index for index, word in enumerate(self.uniq_words)}
    
            self.words_indexes = [self.word_to_index[w] for w in self.words]
    
        def load_words(self):
            train_df = pd.read_csv('data/reddit-cleanjokes.csv')
            text = train_df['Joke'].str.cat(sep=' ')
            return text.split(' ')
    
        def get_uniq_words(self):
            word_counts = Counter(self.words)
            return sorted(word_counts, key=word_counts.get, reverse=True)
    
        def __len__(self):
            return len(self.words_indexes) - self.args.sequence_length
    
        def __getitem__(self, index):
            return (
                torch.tensor(self.words_indexes[index:index+self.args.sequence_length]),
                torch.tensor(self.words_indexes[index+1:index+self.args.sequence_length+1]),
            )
    
    このDataset Pytorchのものから相続するtorch.utils.data.Dataset クラスと2つの重要なメソッドを定義します__len__ and __getitem__ . 続きを読むDataset のクラスの仕事Data loading tutorial .load_words 関数はデータセットを読み込みます.ネットワークの語彙と埋め込みサイズのサイズを定義するために、データセットでユニークな単語が計算されます.index_to_word and word_to_index 数のインデックスとビザverdに単語を変換します.
    これがプロセスの一部である.将来的にはtorchtext チームプラン
    この部分を改善します、しかし、彼らはそれを再設計しています、そして、新しいAPIは今日このチュートリアルのためにあまりに不安定です.

    トレーニング


    クリエイトアtrain.py ファイルと定義train 関数.
    import argparse
    import torch
    import numpy as np
    from torch import nn, optim
    from torch.utils.data import DataLoader
    from model import Model
    from dataset import Dataset
    
    def train(dataset, model, args):
        model.train()
    
        dataloader = DataLoader(dataset, batch_size=args.batch_size)
        criterion = nn.CrossEntropyLoss()
        optimizer = optim.Adam(model.parameters(), lr=0.001)
    
        for epoch in range(args.max_epochs):
            state_h, state_c = model.init_state(args.sequence_length)
    
            for batch, (x, y) in enumerate(dataloader):
                optimizer.zero_grad()
    
                y_pred, (state_h, state_c) = model(x, (state_h, state_c))
                loss = criterion(y_pred.transpose(1, 2), y)
    
                state_h = state_h.detach()
                state_c = state_c.detach()
    
                loss.backward()
                optimizer.step()
    
                print({ 'epoch': epoch, 'batch': batch, 'loss': loss.item() })
    
    ピーチを使うDataLoader and Dataset 抽象化はジョークデータをロードします.
    用途CrossEntropyLoss 損失関数としてAdam デフォルトのparamsでオプティマイザとして.後でそれを微調整することができます.
    有名にpost アンドリューKarpathyも、最初にこの部分を単純にしておくことを勧めます.

    テキスト生成


    追加predict 関数をtrain.py ファイル
    def predict(dataset, model, text, next_words=100):
        model.eval()
    
        words = text.split(' ')
        state_h, state_c = model.init_state(len(words))
    
        for i in range(0, next_words):
            x = torch.tensor([[dataset.word_to_index[w] for w in words[i:]]])
            y_pred, (state_h, state_c) = model(x, (state_h, state_c))
    
            last_word_logits = y_pred[0][-1]
            p = torch.nn.functional.softmax(last_word_logits, dim=0).detach().numpy()
            word_index = np.random.choice(len(last_word_logits), p=p)
            words.append(dataset.index_to_word[word_index])
    
        return words
    

    予測を実行する


    次のコードを追加しますtrain.py 定義した関数を実行するファイル
    parser = argparse.ArgumentParser()
    parser.add_argument('--max-epochs', type=int, default=10)
    parser.add_argument('--batch-size', type=int, default=256)
    parser.add_argument('--sequence-length', type=int, default=4)
    args = parser.parse_args()
    
    dataset = Dataset(args)
    model = Model(dataset)
    
    train(dataset, model, args)
    print(predict(dataset, model, text='Knock knock. Whos there?'))
    
    を実行するtrain.py 以下のスクリプトを実行します.
    $ python train.py
    
    あなたはエポックと一緒に損失を見ることができます.モデルは、次の100の単語をKnock knock. Whos there? トレーニングが終わるとき.デフォルトでは、10エポックのために実行し、トレーニングを終了するために約15分かかります.
    {'epoch': 9, 'batch': 91, 'loss': 5.953955173492432}
    {'epoch': 9, 'batch': 92, 'loss': 6.1532487869262695}
    {'epoch': 9, 'batch': 93, 'loss': 5.531163215637207}
    ['Knock', 'knock.', 'Whos', 'there?', '3)', 'moostard', 'bird', 'Book,',
    'What', 'when', 'when', 'the', 'Autumn', 'He', 'What', 'did', 'the',
    'psychologist?', 'And', 'look', 'any', 'jokes.', 'Do', 'by', "Valentine's",
    'Because', 'I', 'papa', 'could', 'believe', 'had', 'a', 'call', 'decide',
    'elephants', 'it', 'my', 'eyes?', 'Why', 'you', 'different', 'know', 'in',
    'an', 'file', 'of', 'a', 'jungle?', 'Rock', '-', 'and', 'might', "It's",
    'every', 'out', 'say', 'when', 'to', 'an', 'ghost', 'however:', 'the', 'sex,',
    'in', 'his', 'hose', 'and', 'because', 'joke', 'the', 'month', '25', 'The',
    '97', 'can', 'eggs.', 'was', 'dead', 'joke', "I'm", 'a', 'want', 'is', 'you',
    'out', 'to', 'Sorry,', 'the', 'poet,', 'between', 'clean', 'Words', 'car',
    'his', 'wife', 'would', '1000', 'and', 'Santa', 'oh', 'diving', 'machine?',
    'He', 'was']
    
    この部分にスキップしてコードを実行したい場合は、Githubrepository あなたはクローンすることができます.

    次の手順


    おめでとう!あなたの最初のPytorch LSTMネットワークを書いて、いくつかのジョークを生成しました.
    次に、モデルを改善するために次に行うことができます.
  • 非文字文字を削除することでデータをクリーンアップします.
  • より多くを加えることによって、モデル能力を増やしてくださいLinear or LSTM レイヤー.
  • データセットを列車、テスト、および検証セットに分割します.
  • あなたが予測を実行するたびにモデルを訓練する必要はありませんので、チェックポイントを追加します.