DIY chatgpt: seq2seq 変換モデルにおける GRU モジュールの原理とデータ前処理

chatgpt は、典型的な NLP タイプのアプリケーションです。つまり、主に言語処理と出力に焦点を当てています。したがって、そのモデル設計は、言語翻訳における深層学習の初期のアルゴリズム設計を利用する必要があります。したがって、chatgpt を理解するには、深層学習アルゴリズムが自然言語翻訳の問題にどのように対処するかを理解する必要があり、微積分を学習する場合と同様に、足し算と掛け算を事前に習得する必要があります。

言語翻訳は基本的に、ある入力シーケンスを別の入力シーケンスに変換します。たとえば、Google 翻訳や百度翻訳に「お元気ですか」というシーケンスを入力すると、中国語に翻訳されると、「こんにちは」という別の記号シーケンスが出力されます。したがって、用語的にはシーケンスからシーケンス、つまり seq2seq になります。直感的には、二つの言語が同じ「意味」を表現するとき、その記号表現は異なります。英語と中国語では対応する記号は明らかに全く異なって見えますが、記号に隠された「意味」は同じです。この 2 つの言語を区別する場合、「お元気ですか」と「こんにちは」という 2 つの記号列を見ると、2 つの記号列の「本質」が同じであることがわかります。アルゴリズムの目的は、「本質」をどのように表現するかです。第1の記号列の「」を抽出し、この「エッセンス」を用いて第2の記号列の言語の組み合わせ規則に従って第2の記号列を生成する。

したがって、ネットワークの最初のステップは、最初の記号列に基づいてその「本質」を特定することです。この「本質」は当然構造化データでは記述できないので、前述したようにベクトルを用いて記述します。したがって、ネットワークの最初の機能は、入力されたシンボル列を識別し、「本質」を表すシーケンスを出力することです。このシーケンスの「Vector」、この部分はエンコーダとも呼ばれます。その基本的な処理は次のとおりです。
画像の説明を追加してください
上の図で、GRU はネットワーク コンポーネントです。端的に言えば、1 つまたは 2 つの入力を受け取ることができる特定の機能ですベクトルを取得し、ベクトルを出力します。SOS は、入力シーケンスがどこから始まるかをネットワークに伝える特定のシンボルです。h1 は、ネットワークが「how」という単語を読んだときに生成される理解を表し、h2 は、ネットワークが「how are」という 2 つの記号を読んだときに生成される理解を表し、最後の h3 は、「」を読んだ後にネットワークによって生成される理解を表します。ここの h3 は、文字列「お元気ですか」を読んだ後に人々が受け取る「意味」をシミュレートするために使用されます。

ネットワークが「お元気ですか」というステートメントの意味を「理解」すると、つまりベクトル h3 を取得すると、この「意味」を使用して、別の言語での「意味」の文字列表現を生成できます。これはネットワーク デコーダと呼ばれ、その構造は次のとおりです。
画像の説明を追加してください
基本的なプロセスは、デコーダがエンコーダによって出力された最終ベクトル h3 と開始シンボルを表すベクトルを GRU ユニットに入力し、対応するベクトルを生成することです。次のステップは、h3 を変換することです。「you」に対応するベクトルが GRU ユニットに入力され、「good」に対応するベクトルが取得されます。

ここでは深層学習を使用する必要があります。大規模な言語モデルが出現する前に一般的に使用されていた NLP アルゴリズム処理ユニットは GRU と呼ばれます。それは 2 つの入力ベクトル hidden_​​state、input、および hidden_​​state を受け取ります。前に述べたネットワークは、一連の文字列の後に得られる認識または知識の場合、入力は現在の入力単語に対応するベクトルです。GRU の基本的な仕事は「古いものを破壊し、新しいものを確立する」ことであり、いわゆる「粉砕」とは、元の「古くなった」情報の一部を破壊して削除することを指します。いわゆる「元の情報」は、それに入力される hidden_​​state ベクトルであり、このベクトル内の一部のデータがクリアされます。プロセスは次のとおりです。
まず、W_hidden_​​state と W_input という 2 つの行列が含まれており、それぞれベクトル hidden_​​state と input を使用して積演算を実行させます。次に、演算の後に 2 つの結果ベクトルを加算して結果ベクトルを取得し、最後にsigmod 関数この結果ベクトルの計算プロセスは次のとおりです。

画像の説明を追加してください
その中で、W_hidden_​​state と W_input は、ネットワークがトレーニングに使用するパラメーターです。これらは、hidden_​​state ベクトルのどのデータをクリアする必要があり、どのデータを保持する必要があるかを決定します。シグモイド計算では、入力ベクトルの各成分がマッピングされることに注意してください。 [0,1]。値 0 に近いコンポーネントがクリアされるデータに対応します。

次に、最初に行列 W_h を使用して入力 hidden_​​state ベクトルを乗算して結果ベクトルを取得し、次にこの結果ベクトルを使用して上記の Gate_reset ベクトルと要素ごとの乗算を実行して「みすぼらしい」ベクトルを取得し、別の行列 W_i を使用して次の処理を実行します。入力ベクトル input と同じです。乗算後、得られた 2 つのベクトルが合計され、tanh 演算が実行されます。そのプロセスは次のとおりです。

r = Tanh ( Gatereset Θ ( W h ∗ hhidden State ) + W i ∗ vinput ) r = Tanh(Gate_{reset} \Theta(W_{h}*h_{hiddenState}) + W_{i}*v_{入力})r=ゲート_ _ _ _ _ _リセット_Θ ( Wh隠れ_ _ _ _ _+W私はvでは_ _ _

ここでの Theta 演算は、2 つのベクトルの対応するコンポーネントの乗算を表すことに注意してください。つまり、
画像の説明を追加してください
Gate_rese ベクトルのコンポーネントの値は [0,1] の間であるため、0 に近い値を持つコンポーネントが実行されます。対応するコンポーネントを 0 に近づけることは、「クリア」操作を実行することと同じです。

「新しいものを確立する」方法を考えてみましょう。まず、GRU ユニットには、W_input_update と W_hidden_​​update という 2 つの特定の行列が含まれています。これら 2 つの行列は、それぞれ対応するベクトルで乗算され、結果のベクトルが合計されます。最後に、これに基づいてシグモイド演算が実行されます。手順は次のとおりです。

画像の説明を追加してください

次に、Gate_update ベクトルと hidden_​​state ベクトルに対してシータ演算を実行します。

u = ゲート更新 Θ hhiddenstateu = ゲート_{更新} \シータ h_{隠し状態}あなた=ゲート_ _ _更新_ _ _ _ _Θh _隠れ状態_ _ _ _ _ _

画像の説明を追加してください
最後に、r と u に対して次の操作を実行した後、その結果が最終出力として使用されます。
画像の説明を追加してください
ここでの W_output は、現在の入力単語を識別した後にネットワークによって取得された出力に対応し、h_hiddenstate はネットワークによって取得された「知識」に対応します。現在のすべての単語で構成される文字列を識別した後。

上記の基礎知識を踏まえて、深層学習に基づく対訳翻訳システムを実装します。システムを段階的にアップグレードします. 最初に最も単純な seq2seq モデルを使用して効果を確認します. 次に, アテンション機構を追加して改善効果を確認します. 最後に, トランスモデルを使用して以前のモデルと比較します.改善ですか?

まず、トレーニング用のデータを準備します。次の一連のコードを実行する前に、torchtext のバージョン 0.6 をインストールする必要があります。対応するコマンドは次のとおりです。

pip install torchtext==0.6 

pytorch フレームワークは、ドイツ語、英語、フランス語を含む 3 つの言語の翻訳用コーパスを提供します。各言語の各文には 1 対 1 の対応関係があります。たとえば、英語には「Hello」という文が含まれ、ドイツ語とフランス語には1 対 1 対応同じ文が含まれているため、ネットワークを使用して 1 対 1 の翻訳トレーニングが可能トレーニング コーパスを読み込むコードは次のとおりです。

import torch
import torch.nn as nn
import torch.optim as optim
from torchtext.lagecy.dataset import Multi30k
from torchtext.legacy.data import Field, BucketIterator
import spacy
import random
import math
import time

上記のコードでは、torchtex 部分を詳しく説明する必要があります。NLP 深層学習モデルの設計では、データの前処理は面倒ですが必要かつ慎重な作業です。前処理が適切に行われず、モデルに入力されたデータに問題があると、モデルは間違ったデータでトレーニングされてしまいます。間違った結果。前の章では、深層学習ネットワークがベクトルのみを受信できることを見ましたが、データは言語テキストであるため、認識またはトレーニングのためにネットワークに入力する前に、事前にテキスト記号をベクトルに変換する必要があります。また、上記のコードにはレガシーがあること、つまり上記で呼ばれたライブラリは古いコードであることがわかりますが、ここでは引き続き使用できます。

テキストをベクトルに変換するには、いくつかの手順が必要です。最初のステップは単語の分割です。これは、文を構成要素に分割することを意味します。英語では、文の構成要素は単語であり、単語はスペースで区切られているため、英語の単語の分割は比較的簡単です単純ですが、中国語の文の基本単位は単語であり、単語間にスペースがないため、中国語の単語の分割は非常に難しく、どの単語を組み合わせて意味のある単語にできるかを区別するのはより複雑な問題になります。たとえば、中国語の文「「私は北京天安門が大好きです」。正しい分詞は「私」、「愛」、「北京」、「天安門」です。後のモデルの主題は西洋言語を扱うため、中国語の分詞は無視します。今のところ。実際、英語の単語の分割はスペースを使用するほど単純ではありません。たとえば、「おはようございます!」をスペースだけで分割すると、「good」と「Morning!」に分割されます。感嘆符は単語の一部ではないため、単語を正しく分割するには、「良い」、「朝」、「!」に分割する必要があります。幸いなことに、それらの間で呼び出すことができる既製のコード ライブラリがあるため、単語の分割は難しいことではありません。

2 番目のステップは、単語を数値に変換することです。このステップは build_vocab とも呼ばれます。その目的は、単語と数値を照合することです。たとえば、「おはようございます!」このステップを実行すると、次のようなマップ オブジェクトが得られます。続く:

{
    
    
"good"-> 0,
"morning"->1,
}

3 番目のステップは、センテンスを埋めることです。ネットワークに入力されるセンテンスは、通常、事前に指定された長さを満たす必要があるためです。センテンスの長さが不十分な場合は、特定の文字列またはラベルで埋める必要があります。事前に指定された文の長さは 4 です。すると、「おはよう」の最後に「<pad>」のような 2 つの文字列が埋め込まれ、単語の分割後は「good」、「Morning」、「 <pad> 」となります。 " <パッド> "

4 番目のステップは、文の開始フラグと終了フラグを設定することです。ネットワークに入力するときに、どこが文の始まりで、どこが文の終わりであるかをネットワークに伝える必要があるため、「<」を使用する必要があります。 sos>" は文の始まりを示し、"<eos" > " は終わりを示します。そのため、「おはよう」は " <sos> " "good", "Morning", " <pad> ", " <pad> になります。セグメンテーション後の「" <eos> "。もちろん、これらの充填文字も、2 番目のステップで対応する番号を持ちます。

上記の作業は、上記のコードにおける Field クラスの役割です。具体的な例を見てみましょう。次の内容のトレーニング データを含む test.json ファイルがローカル ディレクトリにあるとします。

{
    
    
    "name": "Jocko",
    "quote": "You must own everything in your world. There is no one else to blame.",
    "score": 1
}
{
    
    
    "name": "Bruce",
    "quote": "Do not pray for an easy life, pray for the strength to endure a diffcult one.",
    "score": 1
}
{
    
    
    "name": "Random Potato",
    "quote": "Stand tall, and rice like a potato!",
    "score": 0
}

次に、次のコードを使用して上記のデータを読み取ります。

from torchtext.data import TabularDataset
#分词先使用简单的按照空格分割
tokenizer = lambda x: x.split()
quote = Field(init_token='<sos>',eos_token='<eos>',sequential=True, use_vocab=True, tokenize=tokenizer, lower=True)
#因为 score 只有数字因此不需要进行文字到数字的转换步骤
score = Field(sequential=False, use_vocab=False)
#设定哪些字段需要读取
fields = {
    
    'quote':('q', quote), 'score':('s', score)}
#加载数据文件,由于是测试,我们把训练数据和测试数据都指向同一个文件
train_data, test_data = TabularDataset.splits(path='/content/', train='test.json',
                                             test='test.json',format='json',
                                              fields=fields)
print(train_data[0].__dict__.keys())
print(train_data[0].__dict__.values())

print(train_data[1].__dict__.keys())
print(train_data[1].__dict__.values())

print(train_data[2].__dict__.keys())
print(train_data[2].__dict__.values())

'''
建立单词与编号的关联, max_size 表示编号最大值,min_freq 表示出现过指定次数的单词才能赋予编号
'''
quote.build_vocab(train_data, max_size=10000, min_freq=1)
train_iterator, test_iterator = BucketIterator.splits((train_data, test_data), 
                                                      batch_size = 2,
                                                      )
'''
在上面设置的 batch_size = 2,也就是一个集合有两个句子,但是因为数据有 3 个句子,
因此数据就分成两个集合,其中一个集合包含一个句子,另一个集合包含2 个句子,
因此下面代码打印结果中,其中有一个的向量只包含 1 个元素,另一个包含 2 个元素
'''
for batch in train_iterator:
  print(f"batch.q: {
      
      batch.q}")
  print(f"batch.s:{
      
      batch.s}")

コードが許可されると、出力結果は次のようになります。

dict_keys(['q', 's'])
dict_values([['you', 'must', 'own', 'everything', 'in', 'your', 'world.', 'there', 'is', 'no', 'one', 'else', 'to', 'blame.'], 1])
dict_keys(['q', 's'])
dict_values([['do', 'not', 'pray', 'for', 'an', 'easy', 'life,', 'pray', 'for', 'the', 'strength', 'to', 'endure', 'a', 'diffcult', 'one.'], 1])
dict_keys(['q', 's'])
dict_values([['stand', 'tall,', 'and', 'rice', 'like', 'a', 'potato!'], 0])
batch.q: tensor([[ 2],
        [12],
        [23],
        [ 6],
        [ 5],
        [ 8],
        [13],
        [19],
        [ 6],
        [ 5],
        [32],
        [30],
        [ 7],
        [15],
        [ 4],
        [11],
        [25],
        [ 3]])
batch.s:tensor([1])
batch.q: tensor([[ 2,  2],
        [29, 35],
        [31, 21],
        [ 9, 26],
        [28, 16],
        [20, 17],
        [ 4, 36],
        [27, 34],
        [ 3, 33],
        [ 1, 18],
        [ 1, 22],
        [ 1, 24],
        [ 1, 14],
        [ 1,  7],
        [ 1, 10],
        [ 1,  3]])
batch.s:tensor([0, 1])

上記の出力では、文を分割するときにスペースのみを使用しているため、感嘆符がポテトという単語と組み合わされていることに注意してください。次に、単語分割方法を改善しましょう。まず、次の 2 つのコマンドを実行して、単語分割アルゴリズムをダウンロードします。

#下载英语分词算法
!python -m spacy download en_core_web_sm
#下载德语分词算法
!python -m spacy download de_core_news_sm

次に、対応する単語分割アルゴリズムをロードします。

spacy_de = spacy.load('de_core_news_sm')
spacy_en = spacy.load('en_core_web_sm')

次に、単語の分割にスペースを使用する元の方法を置き換えます。

#分词先使用简单的按照空格分割
#tokenizer = lambda x: x.split()
#使用新加载的分词算法
import spacy
def tokenizer(text):
  return [tok.text for tok in spacy_en.tokenizer(text)]

次に、前のコードを再度実行すると、感嘆符とジャガイモが分離されていることがわかります。

dict_keys(['q', 's'])
dict_values([['stand', 'tall', ',', 'and', 'rice', 'like', 'a', 'potato', '!'], 0])

単語は数値に対応すると前述しましたが、次のコードを使用すると、対応するケアを表示できます。

#单词转换为编号
print(f'the number for word:"You" is {
      
      quote.vocab.stoi["You"]}')
#查看!对应编号
print(f'the number for word:"!" is {
      
      quote.vocab.stoi["!"]}')
#查看起始标志的编号
print(f'the number for word:"<sos>" is {
      
      quote.vocab.stoi["<sos>"]}')
#查看结束标志的编号
print(f'the number for word:"<eos>" is {
      
      quote.vocab.stoi["<eos>"]}')

#查看编号转换为单词
print(f'the word for number:5 is {
      
      quote.vocab.itos[5]}')
print(f'the word for number:6 is {
      
      quote.vocab.itos[6]}')
print(f'the word for number:2 is {
      
      quote.vocab.itos[2]}')
print(f'the word for number:10 is {
      
      quote.vocab.itos[10]}')

上記のコードを実行した後に得られる結果は次のとおりです。

the number for word:"You" is 0
the number for word:"!" is 11
the number for word:"<sos>" is 2
the number for word:"<eos>" is 3
the word for number:5 is ,
the word for number:6 is a
the word for number:2 is <sos>
the word for number:10 is to

出力からわかるように、単語分割後の「!」、「,」などの対応する句読点には、対応する番号が付いています。最後に、トレーニング データ セット Multi30k を見てみましょう。これには、英語、ドイツ語、フランス語の 3 つの言語が含まれています。各言語には 1 対 1 の文があります。つまり、英語の文がある場合は、ドイツ語とフランス語の対応する文翻訳、このデータセットは、後で行う翻訳システムのトレーニングに使用できます。

まず、英語とドイツ語の単語分割メソッドを設定し、対応する Field オブジェクトを設定します。コードは次のとおりです。

def tokenizer_eng(text):
  return [tok.text for tok in spacy_en.tokenizer(text)]

def tokenizer_ger(text):
  return [tok.text for tok in spacy_de.tokenizer(text)]

#设置英语和德语的两种预处理方式
english = Field(sequential=True, use_vocab=True, tokenize=tokenizer_eng, lower=True)
german = Field(sequential=True, use_vocab=True, tokenize=tokenizer_ger, lower=True)

次に、次のコードを使用して、Multi30k データベースからドイツ語と英語の両方のコーパスをロードします。

train_data, validation_data, test_data = Multi30k.splits(exts=('.de', '.en'), 
                                                         fields=(german, english))

#建立英语与德语单词与数字编号的对应
english.build_vocab(train_data, max_size=10000, min_freq=2)
german.build_vocab(train_data, max_size=10000, min_freq=2)

次に、3 種類のデータのトラバーサーを作成し、読み取ったデータを出力して確認します。

#建立英语与德语单词与数字编号的对应
english.build_vocab(train_data, max_size=10000, min_freq=2)
german.build_vocab(train_data, max_size=10000, min_freq=2)

#建立对应数据集的遍历器
train_iterator, validation_iterator, test_iterator = BucketIterator.splits(
    (train_data, validation_data, test_data),
    batch_size=64
)

for batch in train_iterator:
  print(batch)

上記のコードを実行すると、Multi30k.splits の実行時に FileNotFound エラーが発生する可能性があります。これは、データ セットをダウンロードするための内部リンクが無効であるため、最初にデータ セットを自分でダウンロードする必要があります。
https://github.com/neychev/small_DL_repo/tree/master/datasets/Multi30k
training.tar.gz、validation.tar.gz、mmt_task1_test2016.tar.gz をローカルに解凍してダウンロードしますファイル train.de、train.en、val.de、val.en、test2016.de、test2016.en に移動し、パス内に新しいパス ./data/multi30k を作成します。コードが配置されている場所に、以前のすべてのファイルを新しく作成したパスの下に置きます。colab でテストしている場合は、現在のパスの下にパス /data/multi30k を作成し、以前のすべてのファイルをアップロードする必要があります。データの準備
画像の説明を追加してください
完了したら、上記のコードを実行すると、結果は次のようになります。

[torchtext.data.batch.Batch of size 64 from MULTI30K]
	[.src]:[torch.LongTensor of size 23x64]
	[.trg]:[torch.LongTensor of size 31x64]

[torchtext.data.batch.Batch of size 64 from MULTI30K]
	[.src]:[torch.LongTensor of size 24x64]
	[.trg]:[torch.LongTensor of size 22x64]

[torchtext.data.batch.Batch of size 64 from MULTI30K]
	[.src]:[torch.LongTensor of size 27x64]
	[.trg]:[torch.LongTensor of size 28x64]
	
	...

出力 .src と .trg には 64 があることがわかります。これは、セット内に 64 個の文があることを意味します。前の数字は各文の長さを示します。もちろん、セット内の各文の長さは異なる必要があります。最も長いものが標準として選択され、標準の長さより短い文は記号「<pad>」で埋められます。次のコードを使用して、この記号に対応する数値を出力できます。

print(f"number for word: '<pad>' is : {
      
      english.vocab.stoi['<pad>']}")

上記のコードを実行した後に得られる結果は次のとおりです。

number for word: '<pad>' is : 1

以上が
ネットワークモデル実装前のデータ準備であり、次節ではseq2seq翻訳モデルの設計方法を見ていきます。このセクションのすべてのコードとデータのダウンロード アドレス:
https://github.com/wycl16514/seq2seq-gru-data-preprocess.git

おすすめ

転載: blog.csdn.net/tyler_download/article/details/134849301