大きな言語モデルの 1 つ 必要なのは注意だけです ---Transformer

大規模な言語モデルは多くの分野で使用されており、そのアプリケーションには、インテリジェントなライティング、音楽作成、知識クイズ、チャット、顧客サービス、広告のコピーライティング、論文、ニュース、小説の執筆、推敲、会議/記事の要約などが含まれます。ビジネスでは、モデルが製品であり、サービスが製品であり、プラグインが製品です。ユーザーがアクセスできるあらゆる形式の製品が製品になる可能性があります。商用支払いは通常、メンバーシップ ベースまたは従量課金制です。現在の大予言モデルの核となる構造はトランスフォーマーに基づいています。

大規模モデルの効果が期待を上回る最も重要な理由の 1 つは、モデルが一定のレベルに達すると質的変化が起こり、モデルの記憶と一般化を組み合わせることができることです。Transformer はモデルを非常に大規模にすることができ、NLP 分野のモデルを質的に変化させることができるため、さまざまな分野での応用が可能になりますが、さらに解決する必要がある問題がまだいくつかあります。コンテンツは、
役立つ (Helpful)、
信頼できる (Honest)、
無害 (Harmless)の 3 つの原則に準拠しています。

Transformer フレームワークに基づく大きな予測モデル (事前トレーニング モデルとも呼ばれます) だけでは、商用アプリケーションの要件を完全に満たすのに十分ではありません。業界の発展については、フォローアップ ブログで詳しく説明します。この記事では、最初に説明します。大きな言語モデルの中核となるアーキテクチャである Transformer について。

Transformer は、2017 年に Google Brain が提案した機械翻訳タスクに由来します。論文「Attending is all you need」では、ネットワーク構造につ​​いて詳しく説明されています。それ以前のネットワーク構造は、主に RNN、CNN、LSTM、GRU などのネットワーク形式を使用していました。論文では、機械翻訳における RNN ネットワークの弱点を解決するために、新しいコア構造 Transformer を再設計することを提案しています 従来の Encoder-Decoder アーキテクチャのモデリングプロセスでは、次の瞬間の計算プロセスは、前の瞬間の出力に依存します。そして、この固有の特性により、従来のエンコーダー デコーダー モデルが並列計算を実行できないように制限されます。

この記事のソース コードは、github リンク アドレスでホストされています。

モデル構造の紹介

Google が提案した Transformer にも、エンコーダとデコーダの 2 つの部分が含まれていますが、これら 2 つの部分の中核となるのは CNN、LSTM、GRU などの構造ではなく、Attention 構造です。

エンコーダーの場合、セルフ アテンション レイヤーとフィードフォワード ニューラル ネットワークの 2 つのレイヤーが含まれています。セルフ アテンションは、現在のノードが現在の単語に焦点を合わせるだけでなく、コンテキストのセマンティクスを取得するのにも役立ちます。Decoder には、encoder で説明した 2 層ネットワークも含まれていますが、現在のノードが現在注目する必要がある主要なコンテンツを取得できるように、2 つの層の間には注目層があります。
ここに画像の説明を挿入
Transformer モデルのアーキテクチャ
モデルは入力データ (図の赤枠) に対して埋め込み演算を行う必要があり、Attendance は目的の情報を抽出できますが、タイミング情報を持たず、タイミング情報を位置に変換することで Position Encoding を実現します。埋め込みが完了した後、位置コードが追加され、エンコーダ層に入力され、セルフアテンションでデータが処理された後、フィードフォワード ニューラル ネットワーク (青色のフィードフォワード) にデータが送信されます。フィードフォワードニューラルネットワークの並列化が可能であり、得られた出力は次のエンコーダへの入力となります。
画像の説明を追加してください

  • エンコーダ エンコーダ
    • マルチヘッドアテンション
      • マルチヘッド セルフ アテンション メカニズムは、情報を入力することでクエリ キー 値 (Query-Key-Value) を並行して計算できるため、後続のネットワークはコンテキストを使用して、どの情報に注意を払う必要があるかを知ることができます。現在の動作。ここで QKV を計算するための行列もネットワーク パラメーターの一部であり、トレーニングによりネットワークの注意をより効果的に集中させることができることに注意してください。NLP フィールドは時系列で因果的であるため、改良されたモデルでは因果的マルチヘッド自己注意モデルが使用されます。
    • 残りの接続を追加
      • ここでのメイン残差接続の主な役割は、アイデンティティ マッピングを使用してより深いネットワーク (入力と出力のアイデンティティ)、マルチヘッド アテンションと層正規化、フィードフォワード ニューラル ネットワークと層正規化をトレーニングすることであり、どちらの部分も残差接続を使用します。
    • ノーム層の正規化
      • レイヤー正規化の機能は、サンプル次元をレイヤーとして使用してニューラル ネットワークを正規化し、トレーニング速度を高速化し、収束を加速することです。新しい改善により、レイヤー正規化が後ろではなく前に配置されました。
    • フィードフォワード フィードフォワード ニューラル ネットワーク
      • 注目層を通過した後、重み付けメカニズムを通じて抽出された注目情報は、注目情報に従って意味空間内で変換されます。
      • そこで、MLP では、Multi-Head Attendance で得られたベクトルをより大きな空間に再投影し (論文では空間を 4 倍に拡大)、その大きな空間で必要な情報を抽出する方が便利です (Relu 活性化関数を使用) )、最後にトークン ベクトルの元の空間に射影します。
  • デコーダ
    • 基本的にエンコーダと同様に、構成はマスクドマルチヘッドアテンション、マスクドエンコーダ・デコーダアテンションに分かれます(このレイヤはエンコーダとデコーダを接続するアテンションレイヤであり、GPTはエンコーダのみを使用するため、このレイヤは削除されています)。 .) そして、フィード フォワード ニューラル ネットワークでは、3 つの部分のそれぞれに残留接続があり、その後に層正規化が続きます。以下では、デコーダのマスクされたセルフ アテンションとエンコーダ デコーダ アテンションの 2 つの部分について説明します。
    • マスクされたマルチヘッド アテンション
      • セルフ アテンション メカニズムに問題があります。トレーニング プロセス中に完全なラベル付きデータがデコーダーに公開されます。これは明らかに間違っています。デコーダーの入力に対して何らかの処理を実行する必要があります。この処理はマスクと呼ばれます。データは選択的に Decoder に公開されます (GPT では、ネットワークによって順次生成される背後のすべてのデータをカバーすることに相当します)。
    • マルチヘッドアテンション
    • 残りの接続を追加
    • ノーム層の正規化
    • フィードフォワード フィードフォワード ニューラル ネットワーク
  • 線形層と Softmax は
    エンコーダとデコーダを通過し、最後に完全に接続された層と SoftMax の層になります (後で改良された大規模言語モデルではガウス誤差線形単位関数が使用されます)。線形層は、デコーダー スタックによって生成されたベクトルをロジット ベクトルと呼ばれるより大きなベクトルに投影する、単純な完全接続ニューラル ネットワークです。Softmax 層 (Softmax はマルチクラス分類問題の活性化関数) は、ベクトルを確率 (すべて正で合計が 1.0) に変換します。最も高い確率を持つ単位が選択され、それに関連付けられた単語がこのタイム ステップの出力として生成されます。

Pytorch 実装のモデル化

赤い部分は入出力のエンベディングです。

class Embedder(nn.Module):
    def __init__(self, vocab_size, d_model):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, d_model)

    def forward(self, x):
        #[123, 0, 23, 5] -> [[..512..], [...512...], ...]
        return self.embed(x)

場所コード

次のコードに示すように、その値は上記の埋め込みに追加され、コーデック モジュールに入力されます。

class PositionalEncoder(nn.Module):

 def __init__(self, d_model: int, max_seq_len: int = 80):
     super().__init__()
     self.d_model = d_model

     #Create constant positional encoding matrix
     pos_matrix = torch.zeros(max_seq_len, d_model)

     # for pos in range(max_seq_len):
     #     for i in range(0, d_model, 2):
     #         pe_matrix[pos, i] = math.sin(pos/1000**(2*i/d_model))
     #         pe_matrix[pos, i+1] = math.cos(pos/1000**(2*i/d_model))
     #
     # pos_matrix = pe_matrix.unsqueeze(0) # Add one dimension for batch size

     den = torch.exp(-torch.arange(0, d_model, 2) * math.log(1000) / d_model)
     pos = torch.arange(0, max_seq_len).reshape(max_seq_len, 1)
     pos_matrix[:, 1::2] = torch.cos(pos * den)
     pos_matrix[:, 0::2] = torch.sin(pos * den)
     pos_matrix = pos_matrix.unsqueeze(0)


     self.register_buffer('pe', pos_matrix) #Register as persistent buffer

 def forward(self, x):
     # x is a sentence after embedding with dim (batch, number of words, vector dimension)
     seq_len = x.size()[1]
     x = x + self.pe[:, :seq_len]
     return x

自意識

ここに画像の説明を挿入

## Scaled Dot-Product Attention layer
def scaled_dot_product_attention(q, k, v, mask=None, dropout=None):
    # Shape of q and k are the same, both are (batch_size, seq_len, d_k)
    # Shape of v is (batch_size, seq_len, d_v)
    attention_scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(q.shape[-1]) # size (bath_size, seq_len, d_k)

    # Apply mask to scores
    # <pad>
    if mask is not None:
        attention_scores = attention_scores.masked_fill(mask == 0, value=-1e9)

    # Softmax along the last dimension
    attention_weights = F.softmax(attention_scores, dim=-1)

    if dropout is not None:
        attention_weights = dropout(attention_weights)

    output = torch.matmul(attention_weights, v)
    return output

マルチヘッド アテンション レイヤー

ここに画像の説明を挿入

# Multi-Head Attention layer
class MultiHeadAttention(nn.Module):
    def __init__(self, n_heads, d_model, dropout=0.1):
        super().__init__()

        self.n_heads = n_heads
        self.d_model = d_model
        self.d_k = self.d_v = d_model // n_heads

        # self attention linear layers
        #Linear layers for q, k, v vectors generation in different heads
        self.q_linear_layers = []
        self.k_linear_layers = []
        self.v_linear_layers = []
        for i in range(n_heads):
            self.q_linear_layers.append(nn.Linear(d_model, self.d_k))
            self.k_linear_layers.append(nn.Linear(d_model, self.d_k))
            self.v_linear_layers.append(nn.Linear(d_model, self.d_v))

        self.dropout = nn.Dropout(dropout)
        self.out = nn.Linear(n_heads*self.d_v, d_model)

    def forward(self, q, k, v, mask=None):
        multi_head_attention_outputs = []
        for q_linear, k_linear, v_linear in zip(self.q_linear_layers,
                                                self.k_linear_layers,
                                                self.v_linear_layers):
            new_q = q_linear(q) # size: (batch_size, seq_len, d_k)
            new_k = q_linear(k) # size: (batch_size, seq_len, d_k)
            new_v = q_linear(v) # size: (batch_size, seq_len, d_v)

            # Scaled Dot-Product attention
            head_v = scaled_dot_product_attention(new_q, new_k, new_v, mask, self.dropout) # (batch_size, seq_len,
            multi_head_attention_outputs.append(head_v)

        # Concat
        # import pdb; pdb.set_trace()
        concat = torch.cat(multi_head_attention_outputs, -1) # (batch_size, seq_len, n_heads*d_v)

        # Linear layer to recover to original shap
        output = self.out(concat) # (batch_size, seq_len, d_model)

        return output

翻訳例

ここの github リンクには、
Transformer の記事で説明されているネットワーク モデルの構造と使用法が、英語からフランス語への翻訳例とともに示されています。Python のインストールバージョン情報は次のとおりです。

Python 3.7.16
torch==2.0.1
torchdata==0.6.1
torchtext==0.15.2
spacy==3.6.0
numpy==1.25.2
pandas
times
portalocker==2.7.0

情報処理

トークン化とテンソル化された数値への単語のマッピング

torchtext が提供するツールを使用して、元のテキストからの単語の分割から始めて語彙を構築し、それをデジタル テンソルとしてマークする、反復音声翻訳モデルの処理に便利なデータ セットを作成する方が便利です。torchtext は基本的な英語の単語分割サポートを提供しますが、ここでの翻訳には英語に加えてフランス語も含まれるため、単語分割 Python ライブラリ Spacy が使用されます。

最初に環境を作成し、次に英語とフランス語のトークナイザーをダウンロードします。これは非常に小さな例なので、ニュースのスペイシーな言語処理モデルを使用します。

#python3 -m spacy download en_core_web_sm
#python3 -m spacy download fr_core_news_sm

以下に示すように:
画像の説明を追加してください

次のステップでは、データを単語に分割し、その単語をテンソル化された数値にマッピングします。

#Data processing
import spacy
from torchtext.data.utils import get_tokenizer
from collections import Counter
import io
from torchtext.vocab import vocab

src_data_path = 'data/english.txt'
trg_data_path = 'data/french.txt'

en_tokenizer = get_tokenizer('spacy', language='en_core_web_sm')
fr_tokenizer = get_tokenizer('spacy', language='fr_core_news_sm')


def build_vocab(filepath, tokenizer):
    counter = Counter()
    with io.open(filepath, encoding="utf8") as f:
        for string_ in f:
            counter.update(tokenizer(string_))
    return vocab(counter, specials=['<unk>', '<pad>', '<bos>', '<eos>'])

en_vocab = build_vocab(src_data_path, en_tokenizer)
fr_vocab = build_vocab(trg_data_path, fr_tokenizer)

def data_process(src_path, trg_path):
    raw_en_iter = iter(io.open(src_path, encoding="utf8"))
    raw_fr_iter = iter(io.open(trg_path, encoding="utf8"))
    data = []
    for (raw_en, raw_fr) in zip (raw_en_iter, raw_fr_iter):
        en_tensor_ = torch.tensor([en_vocab[token] for token in en_tokenizer(raw_en)], dtype=torch.long)
        fr_tensor_ = torch.tensor([fr_vocab[token] for token in fr_tokenizer(raw_fr)], dtype= torch.long)
        data.append((en_tensor_, fr_tensor_))
    return data

train_data = data_process(src_data_path, trg_data_path)

データローダー

DataLoader は、torch.utils.data によって提供されるメソッドで、データセットとサンプラーを組み合わせて、特定のデータセットに反復可能なオブジェクトを提供します。DataLoader は、単一プロセスまたはマルチプロセスの読み込み、カスタムの読み込み順序、およびオプションの自動バッチ処理 (マージ) と、マップされた反復可能なデータセットのメモリ固定をサポートします。
Collat​​e_fn (オプション)。サンプルのリストをマージしてテンソルのミニバッチを形成します。マップ スタイル データセットを一括読み込みするときに使用されます。

#Train transformer
d_model= 512
n_heads = 8
N = 6
src_vocab_size = len(en_vocab.vocab)
trg_vocab_size = len(fr_vocab.vocab)

BATH_SIZE = 32
PAD_IDX = en_vocab['<pad>']
BOS_IDX = en_vocab['<bos>']
EOS_IDX = en_vocab['<eos>']

from torch.nn.utils.rnn import pad_sequence
from torch.utils.data import DataLoader

def generate_batch(data_batch):
    en_batch, fr_batch = [], []
    for (en_item, fr_item) in data_batch:
        en_batch.append(torch.cat([torch.tensor([BOS_IDX]), en_item, torch.tensor([EOS_IDX])], dim=0))
        fr_batch.append(torch.cat([torch.tensor([BOS_IDX]), fr_item, torch.tensor([EOS_IDX])], dim=0))
    en_batch = pad_sequence(en_batch, padding_value=PAD_IDX)
    fr_batch = pad_sequence(fr_batch, padding_value=PAD_IDX)
    return en_batch, fr_batch

train_iter = DataLoader(train_data, batch_size=BATH_SIZE, shuffle=True, collate_fn=generate_batch)

トレーニングの出力は次のとおりです。
画像の説明を追加してください

おすすめ

転載: blog.csdn.net/shichaog/article/details/132156049