第五課第四周實驗一:Embedding_plus_Positional_encoding 嵌入向量加入位置編碼


變壓器預處理

歡迎來到第 4 周的第一個未分級實驗室。 在本筆記本中,您將深入研究應用於原始文本的預處理方法,然后再將其傳遞給轉換器架構的編碼器和解碼器塊。

完成這項任務后,您將能夠

  • 創建可視化以獲得對位置編碼的直覺
  • 可視化位置編碼如何影響詞嵌入

運行以下單元格以加載您需要的包。

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import os

from tensorflow.keras.layers import Embedding
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

1 - 位置編碼

以下是您在上一個作業中實現的位置編碼方程。 此編碼使用以下公式:

\[PE_{(pos, 2i)}= sin\left(\frac{pos}{{10000}^{\frac{2i}{d}}}\right) \]


$$ PE_{(pos, 2i+1)}= cos\left(\frac{pos}{{10000}^{\frac{2i}{d}}}\right) $$
  • 在將文本輸入語言模型之前,將句子轉換為標記是自然語言處理任務中的標准做法。然后將每個標記轉換為固定長度的數字向量,稱為嵌入,它捕獲單詞的含義。在 Transformer 架構中,位置編碼向量被添加到嵌入中以在整個模型中傳遞位置信息。

  • 僅通過檢查數字表示可能難以掌握這些向量的含義,但可視化有助於直觀了解單詞的語義和位置相似性。正如您在之前的作業中看到的那樣,當嵌入被縮減為二維並繪制時,語義相似的詞看起來更靠近,而不同的詞則被繪制得更遠可以使用位置編碼向量進行類似的練習 - 在笛卡爾平面上繪制時,句子中距離較近的單詞應該看起來更近,而在句子中距離更遠時,應該在平面上出現更遠

  • 在本筆記本中,您將創建一系列詞嵌入和位置編碼向量的可視化,以直觀了解位置編碼如何影響詞嵌入並幫助通過 Transformer 架構傳輸順序信息。

1.1 - 位置編碼可視化

以下代碼單元格具有您在 Transformer 賦值中實現的 positional_encoding 函數。 干得好! 您將利用此筆記本中的此功能構建更多的可視化效果。

def positional_encoding(positions, d):
    """
    預先計算所有位置編碼的矩陣
    
   參數:
         position (int) -- 要編碼的最大位置數
         d (int) -- 編碼大小
    
    返回:
         pos_encoding -- (1, position, d_model) 帶有位置編碼的矩陣
    """

    # 初始化所有角度的矩陣angle_rads
    angle_rads = np.arange(positions)[:, np.newaxis] / np.power(10000, (2 * (np.arange(d)[np.newaxis, :]//2)) / np.float32(d))
    angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])
    angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])
    
    pos_encoding = angle_rads[np.newaxis, ...]
    
    return tf.cast(pos_encoding, dtype=tf.float32)

將嵌入維度定義為 100。該值必須與詞嵌入的維度匹配。 在 "Attention is All You Need" 論文中,嵌入大小從 100 到 1024 不等,具體取決於任務。 作者還根據任務使用了從 40 到 512 不等的最大序列長度。 定義最大序列長度為 100,最大字數為 64。

EMBEDDING_DIM = 100
MAX_SEQUENCE_LENGTH = 100
MAX_NB_WORDS = 64
pos_encoding = positional_encoding(MAX_SEQUENCE_LENGTH, EMBEDDING_DIM)

plt.pcolormesh(pos_encoding[0], cmap='RdBu')
plt.xlabel('d')
plt.xlim((0, EMBEDDING_DIM))
plt.ylabel('Position')
plt.colorbar()
plt.show()

你已經在這個作業中創建了這個可視化,但讓我們深入一點。 注意矩陣的一些有趣的特性——第一個是每個向量的范數總是一個常數。 無論 pos 的值是多少,范數將始終是相同的值,在本例中為 7.071068。 從這個屬性你可以得出結論,兩個位置編碼向量的點積不受向量尺度的影響,這對相關計算有重要意義。

pos = 34
tf.norm(pos_encoding[0,pos,:])


輸出:<tf.Tensor: shape=(), dtype=float32, numpy=7.071068>

另一個有趣的特性是由k 個位置分隔的 2 個向量之間的差異的范數也是常數。 如果您保持 k 不變並更改 pos,則差異將大致相同。 這個屬性很重要,因為它表明差異不取決於每個編碼的位置,而是取決於編碼之間的相對分離。 能夠將位置編碼表示為彼此的線性函數,可以幫助模型通過關注單詞的相對位置來學習。

這種用向量編碼來反映詞的位置差異是很難實現的,特別是考慮到向量編碼的值必須保持足夠小,以便它們不會扭曲詞嵌入。

pos = 70
k = 2
print(tf.norm(pos_encoding[0,pos,:] -  pos_encoding[0,pos + k,:]))
輸出:tf.Tensor(3.2668781, shape=(), dtype=float32)

您已經觀察到了一些關於位置編碼向量的有趣屬性——接下來,您將創建一些可視化,看看這些屬性如何影響編碼和嵌入之間的關系!

1.2 - 比較位置編碼

1.2.1 - 相關性

位置編碼矩陣有助於可視化每個向量對於每個位置的唯一性。 但是,目前還不清楚這些向量如何表示句子中單詞的相對位置。 為了說明這一點,您將計算每個位置的向量對之間的相關性。 一個成功的位置編碼器將產生一個完美對稱的矩陣,其中最大值位於主對角線上——相似位置的向量應該具有最高的相關性。 按照相同的邏輯,相關值應該隨着遠離主對角線而變小

# 位置編碼相關
corr = tf.matmul(pos_encoding, pos_encoding, transpose_b=True).numpy()[0]
plt.pcolormesh(corr, cmap='RdBu')
plt.xlabel('Position')
plt.xlim((0, MAX_SEQUENCE_LENGTH))
plt.ylabel('Position')
plt.colorbar()
plt.show()

1.2.2 - 歐幾里得距離

您還可以使用歐幾里德距離而不是相關性來比較位置編碼向量。 在這種情況下,您的可視化將顯示一個矩陣,其中主對角線為 0,並且其非對角線值隨着它們遠離主對角線而增加

# Positional encoding euclidean distance
eu = np.zeros((MAX_SEQUENCE_LENGTH, MAX_SEQUENCE_LENGTH))
print(eu.shape)
for a in range(MAX_SEQUENCE_LENGTH):
    for b in range(a + 1, MAX_SEQUENCE_LENGTH):
        eu[a, b] = tf.norm(tf.math.subtract(pos_encoding[0, a], pos_encoding[0, b]))
        eu[b, a] = eu[a, b]
        
plt.pcolormesh(eu, cmap='RdBu')
plt.xlabel('Position')
plt.xlim((0, MAX_SEQUENCE_LENGTH))
plt.ylabel('Position')
plt.colorbar()
plt.show()


干得好! 您可以使用這些可視化來檢查您創建的任何位置編碼。

2 - 語義嵌入

通過創建相關矩陣和距離矩陣,您已經深入了解位置編碼向量與不同位置的其他向量之間的關系。 類似地,通過可視化這些向量的總和,您可以更直觀地了解位置編碼如何影響詞嵌入

2.1 - 加載預訓練嵌入

要將預訓練的詞嵌入與您創建的位置編碼相結合,請首先從 glove 項目加載預訓練的嵌入之一。 您將使用具有 100 個特征的嵌入。

embeddings_index = {}
GLOVE_DIR = "glove"
f = open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'))
for line in f:
    values = line.split()
    word = values[0]
    coefs = np.asarray(values[1:], dtype='float32')
    embeddings_index[word] = coefs
f.close()

print('Found %s word vectors.' % len(embeddings_index))
print('d_model: %s', embeddings_index['hi'].shape)
輸出:Found 400000 word vectors.
      d_model: %s (100,)

注意: 這個嵌入由 400,000 個詞組成,每個詞嵌入有 100 個特征。

考慮以下僅包含兩個句子的文本。 等一下 - 這些句子沒有意義! 相反,這些句子被設計為:

  • 每個句子由詞組組成,詞組之間有一定的語義相似性
  • 第一句中相似詞是連續的,而在第二句中,順序是隨機的
texts = ['king queen man woman dog wolf football basketball red green yellow',
         'man queen yellow basketball green dog  woman football  king red wolf']

首先,運行以下代碼單元以將標記化應用於原始文本。 不要太擔心這一步的作用 - 將在以后的未分級實驗中詳細解釋。 快速總結(對於理解實驗室並不重要):

  • 如果你輸入一個不同句子長度的純文本數組,它將為每個句子生成一個矩陣,每個句子由一個大小為MAX_SEQUENCE_LENGTH的數組表示。
  • 此數組中的每個值使用字典中的相應索引(word_index)表示句子中的每個單詞。
  • 短於MAX_SEQUENCE_LENGTH 的序列用零填充以創建統一長度

同樣,這將在以后的未分級實驗中詳細解釋,所以現在不要太擔心!

tokenizer = Tokenizer(num_words=MAX_NB_WORDS)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)

word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))

data = pad_sequences(sequences, padding='post', maxlen=MAX_SEQUENCE_LENGTH)

print(data.shape)

print(data)


為了簡化您的模型,您只需要獲取您正在檢查的文本中出現的不同單詞的嵌入。 在這種情況下,您將僅過濾掉出現在我們句子中的 11 個單詞。 第一個向量將是一個零數組,並將編碼所有未知單詞。

embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
for word, i in word_index.items():
    embedding_vector = embeddings_index.get(word)
    if embedding_vector is not None:
        # words not found in embedding index will be all-zeros.
        embedding_matrix[i] = embedding_vector
print(embedding_matrix.shape)
輸出:(12, 100)

使用從預訓練手套嵌入中提取的權重創建嵌入層。

embedding_layer = Embedding(len(word_index) + 1,
                            EMBEDDING_DIM,
                            embeddings_initializer=tf.keras.initializers.Constant(embedding_matrix),
                            trainable=False)

使用前一層將輸入標記化數據轉換為嵌入。 檢查嵌入的形狀以確保該矩陣的最后一個維度包含句子中單詞的嵌入。

embedding = embedding_layer(data)
print(embedding.shape)
輸出:(2, 100, 100)

2.2 - 笛卡爾平面上的可視化

現在,您將創建一個函數,允許您在笛卡爾平面中可視化我們的單詞編碼。 您將使用 PCA 將手套嵌入的 100 個特征減少到僅 2 個組件。

from sklearn.decomposition import PCA

def plot_words(embedding, sequences, sentence):
    pca = PCA(n_components=2)
    X_pca_train = pca.fit_transform(embedding[sentence,0:len(sequences[sentence]),:])


    fig, ax = plt.subplots(figsize=(12, 6)) 
    plt.rcParams['font.size'] = '12'
    ax.scatter(X_pca_train[:, 0], X_pca_train[:, 1])
    words = list(word_index.keys())
    for i, index in enumerate(sequences[sentence]):
        ax.annotate(words[index-1], (X_pca_train[i, 0], X_pca_train[i, 1]))

好的! 現在您可以繪制每個句子的嵌入。 每個圖都應該顯示不同單詞的嵌入。

plot_words(embedding, sequences, 0)


繪制第二個句子的詞嵌入。 回想一下,第二個句子包含與第一個句子相同的單詞,只是順序不同。 您可以看到單詞的順序不會影響向量表示

plot_words(embedding, sequences, 1)

3 - 語義和位置嵌入

接下來,您將原始手套嵌入與您之前計算的位置編碼相結合。 在本練習中,您將在語義嵌入和位置嵌入之間使用 1 比 1 的權重比

embedding2 = embedding * 1.0 + pos_encoding[:,:,:] * 1.0

plot_words(embedding2, sequences, 0)
plot_words(embedding2, sequences, 1)


哇看看情節之間的巨大差異! 與原始對應物相比,這兩個地塊都發生了巨大變化。 請注意,在第二個圖像中,對應於相似詞不在一起的句子,非常不同的詞(如 red 和 wolf)顯得更接近。

現在您可以嘗試不同的相對權重,看看這如何強烈影響句子中單詞的向量表示。

W1 = 1 # Change me
W2 = 10 # Change me
embedding2 = embedding * W1 + pos_encoding[:,:,:] * W2
plot_words(embedding2, sequences, 0)
plot_words(embedding2, sequences, 1)

# For reference
#['king queen man woman dog wolf football basketball red green yellow',
# 'man queen yellow basketball green dog  woman football  king red wolf']



如果你設置W1 = 1W2 = 10,你可以看到單詞的排列是如何根據單詞在句子中的位置開始呈順時針或逆時針順序的。在這些參數下,位置編碼向量主導了嵌入。

現在嘗試將權重反轉為“W1 = 10”和“W2 = 1”。觀察到在這些參數下,繪圖類似於原始嵌入可視化,並且繪制的單詞位置之間只有很少的變化。

在之前的 Transformer 賦值中,詞嵌入乘以 sqrt(EMBEDDING_DIM)。在這種情況下,使用 W1 = sqrt(EMBEDDING_DIM) = 10W2 = 1 將是等效的。

恭喜!

您已經完成了本筆記本,並且對 Transformer 網絡的輸入有了更好的了解!

到目前為止,您已經:

  • 創建位置編碼矩陣來可視化向量的關系屬性
  • 在笛卡爾平面上繪制嵌入和位置編碼以觀察它們如何相互影響

你應該記住的

  • 位置編碼可以表示為彼此的線性函數,允許模型根據單詞的相對位置進行學習。
  • 位置編碼會影響詞嵌入,但如果位置編碼的相對權重較小,則總和將保留詞的語義。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM