講透一個強大算法模型,Transformer !!
哈嘍,我是cos大壯!~
這幾天,社群裡 Transformer 相關討論一直進行著,那今兒我準備給大家分享一個以「利用Transformer進行機器翻譯」為主題進行一個分享。
今兒的內容,有點趣味兒,也有點詳細,大家記得收藏起來慢慢學起來。
首先,官話:Transformer 模型是由 Vaswani 等人在 2017 年提出的一種新型神經網絡架構,用於解決序列到序列的任務,比如機器翻譯、文本生成等。它的核心思想是通過「注意力機制」來捕捉序列中的依賴關係,而不依賴傳統的循環神經網絡(RNN)。
其次,這是重點(劃重點):給大家用一個很簡單的方式來解釋Transformer。
Transformer 是一種不依賴於順序處理序列數據的新型模型,它利用注意力機制在處理每個詞時關注整個序列中的其他詞,從而捕捉全局的依賴關係。這使得它在處理長序列時比傳統的循環神經網絡更有效、更快速。
舉一個例子,句子翻譯:
假設我們要把英文句子 ‘I am a student’ 翻譯成中文 ‘我是學生’。下面是如何一步一步進行的。
1. 輸入序列
輸入序列是英文句子 ‘I am a student’。我們將這個句子送入模型。
2. 編碼器處理
編碼器的任務是理解輸入的英文句子。我們可以把它想像成一個特別聰明的閱讀員。
-
第一步:詞向量表示:
-
每個詞 ‘I’、’am’、’a’ 和 ‘student’ 都會被轉換成一個向量(一個包含數字的列表),這些向量代表了詞的意義。
-
編碼器會看整個句子,計算每個詞和其他詞之間的關係。
-
比如,它會理解 ‘I’ 和 ‘am’ 是緊密相關的,’student’ 和 ‘a’ 也是相關的。
-
編碼器由多層組成,每層都會重覆上面的自注意力機制,然後更新每個詞的向量表示。
-
經過多層處理,編碼器對每個詞的理解會越來越深刻,最後得到一組新的詞向量,這些向量包含了整個句子的上下文信息。
-
第一步:生成第一個詞:
-
解碼器先看編碼器的輸出(即英文句子的向量表示),然後生成第一個中文詞,比如 ‘我’。
-
解碼器會用一個特殊的開始標記來啟動翻譯過程。
-
解碼器不僅看編碼器的輸出,還會結合已經生成的中文詞。
-
假設我們已經生成了 ‘我’,解碼器會結合 ‘我’ 和編碼器的輸出,決定下一個詞是什麼。
-
解碼器也有自己的自注意力機制,用來理解已生成詞之間的關係,比如 ‘我’ 和 ‘是’ 的關係。
-
同時,解碼器還會使用交互注意力機制,結合編碼器的輸出,理解英文句子和已生成的中文詞的關係。
-
逐步生成下一個詞,比如生成 ‘是’ 後,解碼器結合 ‘我’、’是’ 和編碼器的輸出,再生成 ‘學生’。
-
最終,解碼器生成完整的中文句子 ‘我是學生’。
-
負責讀取輸入序列並生成特徵表示。
-
每層編碼器包含兩個子層:
-
多頭自注意力機制(Multi-Head Self-Attention):關注輸入序列中不同位置的依賴關係。
-
前饋神經網絡(Feed-Forward Neural Network):對每個位置的特徵進行獨立處理。
-
根據編碼器的輸出和前面的解碼器輸出,生成最終序列。
-
每層解碼器包含三個子層:
-
多頭自注意力機制:關註解碼器中之前位置的依賴關係。
-
編碼器-解碼器注意力機制:結合編碼器的輸出與當前解碼器的輸入。
-
前饋神經網絡:對每個位置的特徵進行獨立處理。
-
損失函數: 交叉熵損失函數(Cross-Entropy Loss)用於計算預測序列與目標序列之間的誤差。
-
優化方法: 常用Adam優化器,並結合學習率調度策略(如學習率預熱和衰減)。
第二步:自注意力機制:
第三步:多層處理:
3. 解碼器生成
解碼器的任務是生成翻譯後的中文句子。可以把它想像成一個翻譯員。
第二步:結合已生成的詞和編碼器的輸出:
第三步:自注意力機制和交互注意力機制:
第四步:逐詞生成:
主要構件
到這裏,大家應該已經有了一個初步的理解了。
上面提到了編碼器(Encoder) 和 解碼器(Decoder),是 Transformer 兩個主要部分。每個部分又包含多個相同的層。
下面的解釋,大家應該是很容易理解了:
1. 編碼器(Encoder):
2. 解碼器(Decoder):
注意力機制
注意力機制是 Transformer 的核心,它允許模型在處理當前詞語時「關注」輸入序列中與其相關的其他詞語,從而捕捉更全局的依賴關係。自注意力機制通過計算每個詞與其他詞的「相關性」(也叫注意力分數),然後對這些相關性進行加權求和,從而得到每個詞的新表示。
原理詳解
好的,我們將更詳細地探討Transformer模型的每一部分,包括自注意力機制、多頭注意力機制、位置編碼、編碼器和解碼器的結構以及具體的公式推導。
1. 自注意力機制(Self-Attention Mechanism)
計算注意力分數
自注意力機制的核心在於計算序列中每個元素與其他元素的關係,這通過以下步驟完成:
1. 線性變換生成查詢、鍵和值矩陣:
對於輸入序列 (形狀為 ),通過線性變換得到查詢矩陣 、鍵矩陣 和值矩陣 :
其中 是可學習的參數矩陣,形狀均為 。
2. 計算注意力分數:
注意力分數是通過點積計算得到的:
這裏的 是一個縮放因子,防止點積值過大導致softmax的梯度消失。
3. 應用softmax函數:
對注意力分數應用softmax函數,得到注意力權重:
4. 計算加權和:
最後,用注意力權重對值矩陣 進行加權求和,得到最終的輸出:
2. 多頭注意力機制(Multi-Head Attention)
多頭注意力機制允許模型關注不同位置的信息子空間,通過並行計算多個注意力頭,並將它們的輸出結合在一起:
1. 並行計算多個注意力頭:
對輸入序列 進行 次自注意力計算,每次計算使用不同的線性變換參數:
2. 連接注意力頭的輸出:
將 個注意力頭的輸出連接起來:
3. 線性變換多頭輸出:
對連接後的輸出進行線性變換,得到最終的多頭注意力輸出:
3. 位置編碼(Positional Encoding)
由於Transformer沒有內置的序列順序信息,必須通過位置編碼來引入位置信息。位置編碼通常通過正弦和餘弦函數生成:
其中 是序列中的位置, 是維度索引。
4. 編碼器(Encoder)
編碼器由多層堆疊的自注意力層和前饋神經網絡層組成。
自注意力層
每一層的自注意力機制如上所述,計算如下:
前饋神經網絡層
前饋神經網絡層包括兩個線性變換和一個激活函數(如ReLU):
5. 解碼器(Decoder)
解碼器結構與編碼器類似,但多了一個編碼-解碼注意力層。
自注意力層
與編碼器的自注意力層相同。
編碼-解碼注意力層
這個層的計算考慮到了編碼器的輸出:
這裏的 和 來自編碼器的輸出, 來自解碼器的輸入。
前饋神經網絡層
與編碼器中的前饋神經網絡層相同。
6. 訓練與優化
Transformer模型通常通過以下損失函數和優化方法進行訓練:
7. 公式總結
這裏,再給大家總結一下~
1. 自注意力:
2. 多頭注意力:
3. 位置編碼:
4. 前饋神經網絡:
通過這些公式和結構,Transformer模型能夠高效地處理序列數據,並捕捉長距離依賴關係,極大地提升了自然語言處理任務的性能。
完整案例
這裏,咱們完成一個 利用Transformer進行機器翻譯 的簡易項目。
數據集介紹
我們將使用一個簡單的中英文平行語料庫來訓練Transformer模型。這些數據可以從公開的多語言數據集(如Tatoeba項目)中獲取。
示例數據:
中文: 你好嗎?
英文: How are you?
算法流程
1. 數據預處理:
-
分詞、標記化、構建詞彙表。
-
轉換成模型輸入格式。
2. 模型構建:
-
使用Transformer架構,包括編碼器和解碼器。
3. 訓練模型:
-
定義損失函數和優化器。
-
訓練模型,監控損失。
4. 模型評估:
-
使用驗證集評估模型性能。
-
繪製訓練損失和驗證損失曲線。
5. 翻譯句子:
-
使用訓練好的模型翻譯新句子。
完整代碼
使用TensorFlow和Keras來實現Transformer進行機器翻譯。
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
# 數據預處理
# 示例數據
data = [
('你好', 'Hello'),
('你好嗎?', 'How are you?'),
('謝謝', 'Thank you'),
('再見', 'Goodbye'),
]
def preprocess_sentence(sentence):
sentence = sentence.lower().strip()
sentence = ' '.join(sentence)
return sentence
input_texts = []
target_texts = []
for src, tgt in data:
input_texts.append(preprocess_sentence(src))
target_texts.append('<start> ' + preprocess_sentence(tgt) + ' <end>')
# 構建詞彙表
input_vocab = sorted(set(''.join(input_texts)))
target_vocab = sorted(set(' '.join(target_texts).split(' ')))
input_vocab_size = len(input_vocab) + 1
target_vocab_size = len(target_vocab) + 1
input_token_index = dict([(char, i + 1) for i, char in enumerate(input_vocab)])
target_token_index = dict([(word, i + 1) for i, word in enumerate(target_vocab)])
max_encoder_seq_length = max([len(txt) for txt in input_texts])
max_decoder_seq_length = max([len(txt.split(' ')) for txt in target_texts])
encoder_input_data = np.zeros((len(input_texts), max_encoder_seq_length), dtype='float32')
decoder_input_data = np.zeros((len(input_texts), max_decoder_seq_length), dtype='float32')
decoder_target_data = np.zeros((len(input_texts), max_decoder_seq_length, target_vocab_size), dtype='float32')
for i, (input_text, target_text) in enumerate(zip(input_texts, target_texts)):
for t, char in enumerate(input_text):
encoder_input_data[i, t] = input_token_index[char]
for t, word in enumerate(target_text.split(' ')):
decoder_input_data[i, t] = target_token_index[word]
if t > 0:
decoder_target_data[i, t - 1, target_token_index[word]] = 1.0
# 構建Transformer模型
from tensorflow.keras.layers import Input, Embedding, LSTM, Dense
from tensorflow.keras.models import Model
# 定義編碼器
encoder_inputs = Input(shape=(None,))
encoder_embedding = Embedding(input_vocab_size, 256)(encoder_inputs)
encoder_lstm = LSTM(256, return_state=True)
encoder_outputs, state_h, state_c = encoder_lstm(encoder_embedding)
encoder_states = [state_h, state_c]
# 定義解碼器
decoder_inputs = Input(shape=(None,))
decoder_embedding = Embedding(target_vocab_size, 256)(decoder_inputs)
decoder_lstm = LSTM(256, return_sequences=True, return_state=True)
decoder_outputs, _, _ = decoder_lstm(decoder_embedding, initial_state=encoder_states)
decoder_dense = Dense(target_vocab_size, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)
# 定義模型
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
# 編譯模型
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
# 訓練模型
history = model.fit(
[encoder_input_data, decoder_input_data], decoder_target_data,
batch_size=64,
epochs=100,
validation_split=0.2
)
# 繪製訓練損失和驗證損失曲線
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.legend()
plt.show()
# 翻譯新句子
def decode_sequence(input_seq):
states_value = encoder_model.predict(input_seq)
target_seq = np.zeros((1, 1))
target_seq[0, 0] = target_token_index['<start>']
stop_condition = False
decoded_sentence = ''
while not stop_condition:
output_tokens, h, c = decoder_model.predict([target_seq] + states_value)
sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_word = target_vocab[sampled_token_index - 1]
decoded_sentence += ' ' + sampled_word
if (sampled_word == '<end>' or
len(decoded_sentence.split(' ')) > max_decoder_seq_length):
stop_condition = True
target_seq = np.zeros((1, 1))
target_seq[0, 0] = sampled_token_index
states_value = [h, c]
return decoded_sentence
# 構建編碼器和解碼器模型
encoder_model = Model(encoder_inputs, encoder_states)
decoder_state_input_h = Input(shape=(256,))
decoder_state_input_c = Input(shape=(256,))
decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
decoder_outputs, state_h, state_c = decoder_lstm(
decoder_embedding, initial_state=decoder_states_inputs)
decoder_states = [state_h, state_c]
decoder_outputs = decoder_dense(decoder_outputs)
decoder_model = Model(
[decoder_inputs] + decoder_states_inputs,
[decoder_outputs] + decoder_states)
# 測試翻譯
for seq_index in range(len(input_texts)):
input_seq = encoder_input_data[seq_index: seq_index + 1]
decoded_sentence = decode_sequence(input_seq)
print('-')
print('Input sentence:', input_texts[seq_index])
print('Decoded sentence:', decoded_sentence)

整個代碼,大家可以根據註釋讀懂。
算法優化點
1. 增加數據量:使用更大規模的平行語料庫,提高模型的泛化能力。
2. 調整模型架構:增加Transformer層數、調整每層的隱藏單元數量。使用多頭注意力機制增強模型性能。
3. 超參數調整:調整學習率、batch size等超參數,使用網格搜索或貝葉斯優化。
4. 正則化技術:使用dropout、L2正則化等方法防止過擬合。
5. 優化訓練過程:使用更高級的優化器(如Adam)。增加訓練輪數,使用學習率衰減策略。
6. 數據增強:使用數據增強技術,如回譯(back-translation)等,增強訓練數據的多樣性。
通過這些優化,可以進一步提高Transformer模型的機器翻譯性能。