精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Transformer 模型結(jié)構(gòu)詳解及代碼實(shí)現(xiàn)!

人工智能 架構(gòu)
Transformer 默認(rèn)都是大模型,除了一些特例(如 DistilBERT)外,實(shí)現(xiàn)更好性能的一般策略是增加模型的大小以及預(yù)訓(xùn)練的數(shù)據(jù)量。

一、Transformer簡要發(fā)展史

以下是Transformer模型發(fā)展歷史中的關(guān)鍵節(jié)點(diǎn):

Transformer架構(gòu)于2017年6月推出。原本研究的重點(diǎn)是翻譯任務(wù)。隨后推出了幾個(gè)有影響力的模型,包括:

時(shí)間

模型

簡要說明

2017 年 6 月

「Transformer」

Google 首次提出基于 Attention 的模型,用于機(jī)器翻譯任務(wù)

2018 年 6 月

「GPT」

第一個(gè)使用 Transformer 解碼器模塊進(jìn)行預(yù)訓(xùn)練的語言模型,適用于多種 NLP 任務(wù)

2018 年 10 月

「BERT」

使用 Transformer 編碼器模塊,通過掩碼語言建模生成更強(qiáng)大的句子表示

2019 年 2 月

「GPT-2」

更大更強(qiáng)的 GPT 版本,由于潛在風(fēng)險(xiǎn)未立即發(fā)布,具備出色的文本生成能力

2019 年 10 月

「DistilBERT」

BERT 的輕量化版本,在保留 97% 性能的同時(shí),速度更快、內(nèi)存占用更低

2019 年 10 月

「BART、T5」

使用完整的 Encoder-Decoder 架構(gòu),在各種 NLP 任務(wù)中表現(xiàn)優(yōu)異

2020 年 5 月

「GPT-3」

超大規(guī)模語言模型,支持“零樣本學(xué)習(xí)”,無需微調(diào)即可完成新任務(wù)

這個(gè)列表并不全面,只是為了突出一些不同類型的 Transformer 模型。大體上,它們可以分為三類:

類別

構(gòu)成

特點(diǎn)

典型模型

「GPT-like」

(自回歸 Transformer)

只使用解碼器

自回歸方式預(yù)測(cè)下一個(gè)詞,適合文本生成任務(wù)

GPT、GPT-2、GPT-3

「BERT-like」

(自動(dòng)編碼 Transformer)

只使用編碼器

掩碼機(jī)制學(xué)習(xí)上下文表示,適合理解類任務(wù)如問答、情感分析

BERT、RoBERTa、DistilBERT

「BART/T5-like」

(序列到序列 Transformer)

編碼器 + 解碼器

完整的 encoder-decoder 架構(gòu),適合翻譯、摘要等生成+理解結(jié)合的任務(wù)

BART、T5

Transformer 默認(rèn)都是大模型,除了一些特例(如 DistilBERT)外,實(shí)現(xiàn)更好性能的一般策略是增加模型的大小以及預(yù)訓(xùn)練的數(shù)據(jù)量。其中,GPT-2 是使用「transformer 解碼器模塊」構(gòu)建的,而 BERT 則是通過「transformer 編碼器」模塊構(gòu)建的。

二、Transformer 整體架構(gòu)

論文中給出用于中英文翻譯任務(wù)的 Transformer 整體架構(gòu)如下圖所示:

可以看出Transformer架構(gòu)由Encoder和Decoder兩個(gè)部分組成:其中Encoder和Decoder都是由N=6個(gè)相同的層堆疊而成。Multi-Head Attention 結(jié)構(gòu)是 Transformer 架構(gòu)的核心結(jié)構(gòu),其由多個(gè) Self-Attention 組成的。其中,

部件

結(jié)構(gòu)

層數(shù)

主要模塊

Encoder

編碼器層堆疊

N=6層

Self-Attention+Feed Forward

Decoder

解碼器層堆疊

N=6層

Self-Attention+Encoder-Decoder Attention+Feed Forward

Transformer 架構(gòu)更詳細(xì)的可視化圖如下所示:

1. 輸入模塊

(1) Tokenizer預(yù)處理

在基于Transformer的大模型LLM中,輸入通常為字符串文本。由于模型無法直接處理自然語言,因此需要借助Tokenizer對(duì)輸入進(jìn)行預(yù)處理。具體流程如下:

  • 分詞(Tokenization):將輸入文本按規(guī)則切分為一個(gè)個(gè)詞元(token),如單詞、子詞或特殊符號(hào)。
  • 詞表映射(Vocabulary Mapping):每個(gè) token 被映射到一個(gè)唯一的整數(shù) ID,該 ID 來自預(yù)訓(xùn)練模型所使用的詞匯表。
  • 生成 input_ids 向量(矩陣):最終輸出是一個(gè)由 token ID 構(gòu)成的向量(或矩陣),作為模型輸入。

以下是以 Hugging Face 的 transformers 庫為例,展示如何使用 BertTokenizer 和 BertModel 完成輸入文本的預(yù)處理和編碼:

from transformers import BertTokenizer, BertModel
import torch

# 1. 加載預(yù)訓(xùn)練的 BERT tokenizer 和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

# 2. 輸入文本
text = "A Titan RTX has 24GB of VRAM"

# 3. 分詞并映射為 token ID 序列
inputs = tokenizer(text, return_tensors="pt", truncatinotallow=True, padding=True)

# 輸出 token IDs
print("Token IDs:", inputs['input_ids'])

# 4. 傳入模型,獲取輸出
outputs = model(**inputs)

# 5. 獲取最后一層的隱藏狀態(tài)表示
last_hidden_states = outputs.last_hidden_state
print("Last hidden states shape:", last_hidden_states.shape)

原始輸入文本 "A Titan RTX has 24GB of VRAM" 通過 tokenizer 完成分詞和詞表映射工作,生成的輸入 ID 列表:

[101, 138, 28318, 56898, 12674, 10393, 10233, 32469, 10108, 74727, 36535, 102]

其中,

  • 101 表示 [CLS] 標(biāo)記;
  • 102 表示 [SEP] 標(biāo)記;
  • 其余為對(duì)應(yīng) token 在詞表中的索引。

在所有基于 Transformer 的 LLM 中,唯一必須的輸入是 input_ids,它是由 Tokenizer 映射后的 token 索引組成的整數(shù)向量,代表了輸入文本在詞表中的位置信息。

(2) Embedding 層

在基于 Transformer 的大型語言模型(LLM)中,嵌入層(Embedding Layer)是將輸入 token ID 映射為向量表示的核心組件。其作用是將離散的整數(shù)索引轉(zhuǎn)換為連續(xù)、稠密的向量空間表示,從而便于后續(xù)神經(jīng)網(wǎng)絡(luò)進(jìn)行語義建模。

? 萬物皆可 Embedding:雖然最常見的是詞嵌入(Word Embedding),但圖像、語音等也可以通過嵌入層映射為向量形式,實(shí)現(xiàn)統(tǒng)一建模。

例如,mnist 數(shù)據(jù)集中的圖片,可以通過嵌入層來表示,如下圖所示,每個(gè)點(diǎn)代表一個(gè)圖片(10000*784),通過嵌入層,將圖片的像素點(diǎn)轉(zhuǎn)化為稠密的向量,然后通過 t-SNE/pca 降維,可以看到圖片的空間分布。

LLM 中,單詞 token 需要經(jīng)過 Embedding 層,Embedding 層的作用是將輸入的離散化表示(例如 token ids)轉(zhuǎn)換為連續(xù)的低維向量表示,其由單詞 Embedding 和位置 Embedding (Positional Encoding)相加得到,通常定義為 TransformerEmbedding 層。

① 單詞嵌入(Token Embedding)」

自然語言處理中,輸入文本通常是以符號(hào)形式存在的詞匯,而這些離散符號(hào)無法直接被神經(jīng)網(wǎng)絡(luò)處理。因此需要一個(gè)可學(xué)習(xí)的嵌入矩陣將每個(gè) token 轉(zhuǎn)換為固定維度的向量。

工作原理:

a. 輸入是一個(gè)形狀為 [batch_size, seq_len] 的整數(shù)張量,表示每個(gè) token 在詞表中的索引;

b. 輸出是一個(gè)形狀為 [batch_size, seq_len, d_model] 的三維張量,其中:

  • d_model 是嵌入維度(如 512 或 768);
  • 每個(gè) token 對(duì)應(yīng)一個(gè) d_model 維的向量;

c. 嵌入層權(quán)重矩陣大小為 [vocab_size, d_model],參數(shù)量為:

在 PyTorch 中,詞嵌入層通常使用 torch.nn.Embedding 模塊實(shí)現(xiàn),其作用是將 token 的索引轉(zhuǎn)換為低維語義向量表示。

? 輸入與輸出說明

類型

描述

輸入

一個(gè)整數(shù)張量,表示每個(gè) token 在詞表中的索引

輸入形狀

(batch_size, sequence_length)
其中:
- batch_size:批次大小(即一次處理多少條文本)
- sequence_length:每條文本包含的 token 數(shù)量

輸出

每個(gè) token 被映射到 embedding_dim 維度的稠密向量

輸出形狀

(batch_size, sequence_length, embedding_dim)

  • embedding_dim 是嵌入向量的維度,也稱為詞向量維度;
  • 它通常被設(shè)置為 d_model 或 h,即后續(xù) Transformer 層使用的隱藏層維度(如 512 或 768).

?? 示例代碼:構(gòu)建 Token Embedding 層

from transformers import BertTokenizer
import torch.nn as nn

## 1, 使用 BERT tokenizer 將批量輸入的字符串文本序列轉(zhuǎn)化為 input_ids
tokenizer = BertTokenizer.from_pretrained("bert-base-multilingual-cased") 
batch_text = ["A Titan RTX has 24GB of VRAM", "I have a dog and cat"]
inputs = tokenizer(batch_text, return_tensors="pt", truncation=True, padding=True)
input_ids = inputs["input_ids"]

# 2. 創(chuàng)建一個(gè) nn.Embedding 層
vocab_size = tokenizer.vocab_size  # 詞表大小取決于你加載的具體 tokenizer 模型
embedding_dim = 512  # 嵌入向量的維度,參考 transformer 論文的大小
embedding_layer = nn.Embedding(vocab_size, embedding_dim)

# 3. 通過 nn.Embedding 層,將輸入的 IDs 映射到嵌入向量
embedded_output = embedding_layer(input_ids)

# 4. 輸出嵌入向量的形狀
print("嵌入向量的形狀:", embedded_output.shape)  # (batch_size, sequence_length, embedding_dim), torch.Size([2, 12, 512])

# 5. 打印嵌入向量
print(embedded_output)

程序運(yùn)行后輸出結(jié)果如下所示:

② 位置嵌入(Positional Encoding)」

由于 Transformer 不依賴于 RNN 的順序性建模方式,它必須顯式地引入位置信息,以保留 token 在序列中的位置特征。

為此,Transformer 使用了 Sinusoidal Positional Encoding(正弦/余弦位置編碼):

其中:

  • pos:token 在序列中的位置;
  • i:維度索引;
  • d_model:嵌入維度。

③ TransformerEmbedding 層集成

transformer 輸入模塊有三個(gè)組成部分:文本/提示詞、分詞器(Tokenizer)和嵌入層(Embeddings)。輸入模塊的工作流程和代碼實(shí)現(xiàn)如下所示:

矩陣的每一列表示一個(gè) token 的嵌入向量。

class PositionalEncoding(nn.Module):
    """
    compute sinusoid encoding.
    """
    def __init__(self, d_model, max_len, device):
        """
        constructor of sinusoid encoding class

        :param d_model: dimension of model
        :param max_len: max sequence length
        :param device: hardware device setting
        """
        super(PositionalEncoding, self).__init__()

        # same size with input matrix (for adding with input matrix)
        self.encoding = torch.zeros(max_len, d_model, device=device)
        self.encoding.requires_grad = False  # we don't need to compute gradient

        pos = torch.arange(0, max_len, device=device)
        pos = pos.float().unsqueeze(dim=1)
        # 1D => 2D unsqueeze to represent word's position

        _2i = torch.arange(0, d_model, step=2, device=device).float()
        # 'i' means index of d_model (e.g. embedding size = 50, 'i' = [0,50])
        # "step=2" means 'i' multiplied with two (same with 2 * i)

        self.encoding[:, 0::2] = torch.sin(pos / (10000 ** (_2i / d_model)))
        self.encoding[:, 1::2] = torch.cos(pos / (10000 ** (_2i / d_model)))
        # compute positional encoding to consider positional information of words

    def forward(self, x):
        # self.encoding
        # [max_len = 512, d_model = 512]

        batch_size, seq_len = x.size()
        # [batch_size = 128, seq_len = 30]

        return self.encoding[:seq_len, :]
        # [seq_len = 30, d_model = 512]
        # it will add with tok_emb : [128, 30, 512]         

class TokenEmbedding(nn.Embedding):
    """
    Token Embedding using torch.nn
    they will dense representation of word using weighted matrix
    """

    def __init__(self, vocab_size, d_model):
        """
        class for token embedding that included positional information
        :param vocab_size: size of vocabulary
        :param d_model: dimensions of model
        """
        super(TokenEmbedding, self).__init__(vocab_size, d_model, padding_idx=1)

class TransformerEmbedding(nn.Module):
    """
    token embedding + positional encoding (sinusoid)
    positional encoding can give positional information to network
    """

    def __init__(self, vocab_size, max_len, d_model, drop_prob, device):
        """
        class for word embedding that included positional information
        :param vocab_size: size of vocabulary
        :param d_model: dimensions of model
        """
        super(TransformerEmbedding, self).__init__()
        self.tok_emb = TokenEmbedding(vocab_size, d_model)
        # self.position_embedding = nn.Embedding(max_len, embed_size)
        self.pos_emb = PositionalEncoding(d_model, max_len, device)
        self.drop_out = nn.Dropout(p=drop_prob)

    def forward(self, x):
        tok_emb = self.tok_emb(x)
        pos_emb = self.pos_emb(x)
        return self.drop_out(tok_emb + pos_emb)

2. Multi-Head Attention 結(jié)構(gòu)

Encoder 和 Decoder 結(jié)構(gòu)中公共的 layer 之一是 Multi-Head Attention,其是由多個(gè) Self-Attention 并行組成的。Encoder block 只包含一個(gè) Multi-Head Attention,而 Decoder block 包含兩個(gè) Multi-Head Attention (其中有一個(gè)用到 Masked)。

(1) Self-Attention 結(jié)構(gòu)

Self-Attention 中文翻譯為自注意力機(jī)制,論文中叫作 Scale Dot Product Attention,它是 Transformer 架構(gòu)的核心,使得每個(gè) token 能夠關(guān)注整個(gè)序列中的其他 token,從而建立全局依賴關(guān)系。其結(jié)構(gòu)如下圖所示:

(2) Self-Attention 實(shí)現(xiàn)

? 在本文中,Self-Attention 層與論文中的 ScaleDotProductAttention 層意義一致,實(shí)現(xiàn)方式完全相同。

?? 數(shù)學(xué)定義

Self-Attention 的計(jì)算過程可以表示為:

其中:

  •  :Query 向量;
  • :Key 向量;
  • :Value 向量;
  • :Query 和 Key 的維度;
  • Softmax 對(duì)注意力分?jǐn)?shù)按最后一個(gè)維度歸一化;
  • :用于縮放點(diǎn)積,防止 softmax 梯度消失;

輸入來源:

  • 輸入詞向量經(jīng)過 Embedding 層后,進(jìn)入位置編碼層;
  • 再通過線性變換(Linear 層),分別生成 Query、Key 和 Value 向量;
  • 這三個(gè)向量的形狀通常為 [batch_size, seq_len, d_k] 或 [seq_len, d_k]。

計(jì)算步驟如下:

① 計(jì)算注意力分?jǐn)?shù)矩陣

  • 其中 是 Key 張量的轉(zhuǎn)置;
  • 點(diǎn)積結(jié)果是一個(gè) [seq_len, seq_len] 的注意力得分矩陣;
  • 使用 softmax 歸一化,得到注意力權(quán)重。

② 應(yīng)用掩碼(可選)

  • 在 Decoder 中使用 Masked Self-Attention,防止未來信息泄露;
  • 若傳入 mask,將對(duì)應(yīng)位置設(shè)為極小值(如 -1e9)以抑制其影響。

③ 加權(quán)聚合 Value 向量

  • 將 softmax 后的注意力權(quán)重與 Value 相乘,得到上下文感知的輸出張量;
  • 輸出維度保持與輸入一致:[batch_size, seq_len, d_v]。

?? 代碼實(shí)現(xiàn):

import torch
import math
import torch.nn as nn

class ScaleDotProductAttention(nn.Module):
    def __init__(self):
        """
        初始化 Self-Attention 層,僅包含一個(gè) softmax 操作。
        """
        super(ScaleDotProductAttention, self).__init__()
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor, mask: torch.Tensor = None):
        """
        Self-Attention 前向傳播函數(shù)

        :param Q: Query 向量,形狀為 [batch_size, seq_len, d_k]
        :param K: Key 向量,形狀為 [batch_size, seq_len, d_k]
        :param V: Value 向量,形狀為 [batch_size, seq_len, d_v]
        :param mask: 掩碼張量,形狀為 [batch_size, seq_len, seq_len]
        :return: 
            output: 加權(quán)后的 Value 向量,形狀為 [batch_size, seq_len, d_v]
            attn_weights: 注意力權(quán)重矩陣,形狀為 [batch_size, seq_len, seq_len]
        """
        # 1. 計(jì)算 QK^T 得到注意力分?jǐn)?shù)
        K_T = K.transpose(-1, -2)  # [batch_size, d_k, seq_len]
        scores = torch.matmul(Q, K_T) / math.sqrt(Q.size(-1))  # [batch_size, seq_len, seq_len]

        # 2. 如果有 mask,應(yīng)用掩碼(例如 Decoder 中防止看到未來詞)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)

        # 3. 應(yīng)用 softmax 得到注意力權(quán)重
        attn_weights = self.softmax(scores)  # [batch_size, seq_len, seq_len]

        # 4. 權(quán)重 × Value 得到最終輸出
        output = torch.matmul(attn_weights, V)  # [batch_size, seq_len, d_v]

        return output, attn_weights

?? 示例調(diào)用與輸出解析:

# 創(chuàng)建 Q、K、V 張量
Q = torch.randn(5, 10, 64)  # [batch_size=5, seq_len=10, d_k=64]
K = torch.randn(5, 10, 64)
V = torch.randn(5, 10, 64)

# 創(chuàng)建 Self-Attention 層
attention = ScaleDotProductAttention()

# 前向傳播
output, attn_weights = attention(Q, K, V)

# 打印輸出形狀
print(f"ScaleDotProductAttention output shape: {output.shape}")      # [5, 10, 64]
print(f"attn_weights shape: {attn_weights.shape}")                # [5, 10, 10]

變量

形狀

描述

Q, K, V

[5, 10, 64]

batch=5,序列長度=10,嵌入維度=64

scores

[5, 10, 10]

注意力得分矩陣,反映 token 之間的相似度

attn_weights

[5, 10, 10]

softmax 后的注意力權(quán)重,用于加權(quán)聚合 Value

output

[5, 10, 64]

最終輸出,融合了上下文信息的 Value 加權(quán)表示

(3) Multi-Head Attention

Multi-Head Attention(MHA)是在Self-Attention基礎(chǔ)上引入的一種增強(qiáng)機(jī)制。其核心理念是:將輸入向量空間劃分為多個(gè)子空間,在每個(gè)子空間中獨(dú)立計(jì)算Self-Attention,最后將多個(gè)子空間的輸出拼接在一起并進(jìn)行線性變換,從而得到最終的輸出。

對(duì)于 MHA,之所以需要對(duì) Q、K、V 進(jìn)行多頭(head)劃分,其目的是為了增強(qiáng)模型對(duì)不同信息的關(guān)注。具體來說,多組 Q、K、V 分別計(jì)算 Self-Attention,每個(gè)頭自然就會(huì)有獨(dú)立的 Q、K、V 參數(shù),從而讓模型同時(shí)關(guān)注多個(gè)不同的信息,這有些類似 CNN 架構(gòu)模型的多通道機(jī)制。

下圖是論文中 Multi-Head Attention 的結(jié)構(gòu)圖。

從圖中可以看出, MHA 結(jié)構(gòu)的計(jì)算過程可總結(jié)為下述步驟:

  • 將輸入 Q、K、V 張量進(jìn)行線性變換(Linear 層),輸出張量尺寸為 [batch_size, seq_len, d_model];
  • 將前面步驟輸出的張量,按照頭的數(shù)量(n_head)拆分為 n_head 子張量,其尺寸為 [batch_size, n_head, seq_len, d_model//n_head];
  • 每個(gè)子張量并行計(jì)算注意力分?jǐn)?shù),即執(zhí)行 dot-product attention 層,輸出張量尺寸為 [batch_size, n_head, seq_len, d_model//n_head];
  • 將這些子張量進(jìn)行拼接 concat ,并經(jīng)過線性變換得到最終的輸出張量,尺寸為 [batch_size, seq_len, d_model]。

?? 數(shù)學(xué)表達(dá)式

其中:

  • :第 i 個(gè) head 的可學(xué)習(xí)參數(shù);
  • :最終輸出的線性變換矩陣;
  • Concat表示將各個(gè) head 的輸出拼接在一起。

(4) Multi-Head Attention 實(shí)現(xiàn)

import torch
import math
import torch.nn as nn

class MultiHeadAttention(nn.Module):
    """Multi-Head Attention Layer"""
    
    def __init__(self, d_model, n_head):
        """
        Args:
            d_model: 模型嵌入維度(通常為 512 或 768);
            n_head: 注意力頭的數(shù)量(如 8);
        """
        super(MultiHeadAttention, self).__init__()
        
        # 初始化參數(shù)
        self.n_head = n_head
        self.attention = ScaleDotProductAttention()  # 使用前面定義的 Self-Attention
        
        # 線性變換層
        self.w_q = nn.Linear(d_model, d_model)       # Query 變換
        self.w_k = nn.Linear(d_model, d_model)       # Key 變換
        self.w_v = nn.Linear(d_model, d_model)       # Value 變換
        self.fc = nn.Linear(d_model, d_model)         # 輸出投影層

    def forward(self, q, k, v, mask=None):
        """
        Args:
            q: Query 張量,[batch_size, seq_len, d_model]
            k: Key 張量,[batch_size, seq_len, d_model]
            v: Value 張量,[batch_size, seq_len, d_model]
            mask: 掩碼張量,[batch_size, seq_len, seq_len]
        """
        # Step 1: 線性變換
        q, k, v = self.w_q(q), self.w_k(k), self.w_v(v)

        # Step 2: 拆分到多個(gè) head
        q = self.split(q)   # [batch_size, n_head, seq_len, d_tensor]
        k = self.split(k)   # [batch_size, n_head, seq_len, d_tensor]
        v = self.split(v)   # [batch_size, n_head, seq_len, d_tensor]

        # Step 3: 計(jì)算每個(gè) head 的 attention
        sa_output, attn_weights = self.attention(q, k, v, mask)

        # Step 4: 拼接所有 head 的輸出
        mha_output = self.concat(sa_output)  # [batch_size, seq_len, d_model]

        # Step 5: 最終線性變換
        mha_output = self.fc(mha_output)

        return mha_output, attn_weights

    def split(self, tensor):
        """
        拆分輸入張量為多個(gè) head

        Args:
            tensor: [batch_size, seq_len, d_model]
        Returns:
            [batch_size, n_head, seq_len, d_tensor]
        """
        batch_size, seq_len, d_model = tensor.size()
        d_tensor = d_model // self.n_head  # 每個(gè) head 的維度
        
        # reshape + transpose 實(shí)現(xiàn)拆分
        tensor = tensor.view(batch_size, seq_len, self.n_head, d_tensor)
        tensor = tensor.transpose(1, 2)  # [batch_size, n_head, seq_len, d_tensor]
        
        return tensor

    def concat(self, sa_output):
        """
        拼接多個(gè) head 的輸出

        Args:
            sa_output: [batch_size, n_head, seq_len, d_tensor]
        Returns:
            [batch_size, seq_len, d_model]
        """
        batch_size, n_head, seq_len, d_tensor = sa_output.size()
        d_model = n_head * d_tensor
        
        # transpose + reshape 實(shí)現(xiàn)合并
        sa_output = sa_output.transpose(1, 2).contiguous().view(batch_size, seq_len, d_model)
        
        return sa_output

?? 示例調(diào)用與輸出解析:

# 定義參數(shù)
d_model = 512
n_head = 8
seq_len = 10
batch_size = 32

# 創(chuàng)建 Q、K、V 張量
Q = torch.randn(batch_size, seq_len, d_model)
K = torch.randn(batch_size, seq_len, d_model)
V = torch.randn(batch_size, seq_len, d_model)

# 構(gòu)建 MHA 層
mha_layer = MultiHeadAttention(d_model=d_model, n_head=n_head)

# 前向傳播
output, weights = mha_layer(Q, K, V)

# 打印輸出形狀
print("MHA Output Shape:", output.shape)      # [32, 10, 512]
print("Attn Weights Shape:", weights.shape)   # [32, 8, 10, 10]

變量

形狀

描述

Q, K, V

[32, 10, 512]

輸入張量,表示 batch=32,seq_len=10,d_model=512

q, k, v

[32, 8, 10, 64]

拆分后的 Q/K/V,每個(gè) head 64 維

sa_output

[32, 8, 10, 64]

每個(gè) head 的 attention 輸出

mha_output

[32, 10, 512]

拼接后的最終輸出

attn_weights

[32, 8, 10, 10]

每個(gè) head 的注意力權(quán)重矩陣

3. Encoder結(jié)構(gòu)

Transformer 中的 Encoder 是整個(gè)模型中用于編碼輸入序列的部分。它由 N=6 個(gè)相同的 encoder block 堆疊而成。每個(gè) encoder block 主要包含兩個(gè)子層(sub-layers):多頭自注意力機(jī)制(Multi-Head Self-Attention)和位置全連接前饋網(wǎng)絡(luò)(Position-wise Feed Forward Network)。

這兩個(gè)子層之間都使用了 殘差連接(Residual Connection) 和 層歸一化(Layer Normalization),以增強(qiáng)訓(xùn)練穩(wěn)定性和模型表達(dá)能力。

下圖中紅色框選部分表示一個(gè)標(biāo)準(zhǔn)的 Encoder Block,其內(nèi)部結(jié)構(gòu)如下:

由以下四個(gè)關(guān)鍵部分構(gòu)成。

模塊

描述

Multi-Head Attention

使用多個(gè) attention head 并行提取序列中的不同特征

Add & Norm (1)

殘差連接(Residual Connection)+ 層歸一化(LayerNorm)

Position-wise FeedForward

兩層線性變換 + 激活函數(shù),對(duì)每個(gè)詞獨(dú)立建模

Add & Norm (2)

同樣應(yīng)用殘差連接和 LayerNorm

(1) 每一層的計(jì)算流程(以單個(gè) encoder block 為例)

① 多頭自注意力機(jī)制(Multi-Head Self-Attention)

  • 輸入:嵌入后的張量 
  • 輸出:通過自注意力加權(quán)后的新張量 sa_output
sa_output, attn_weights = MultiHeadAttention(q=x, k=x, v=x, mask=src_mask)

其中,

  • Query、Key 和 Value 來自同一個(gè)輸入 ;
  • 可選 mask 通常用于屏蔽 padding token 或控制位置感知范圍。

② 殘差連接 + 層歸一化(Sublayer 1)

x = x + dropout(sa_output)
x = layer_norm(x)
  • 應(yīng)用殘差映射,緩解梯度消失問題;
  • 使用 LayerNorm 對(duì)每個(gè) token 的向量進(jìn)行標(biāo)準(zhǔn)化處理;
  • 整體目標(biāo):提升模型表達(dá)能力與訓(xùn)練穩(wěn)定性。

③ 位置全連接前饋網(wǎng)絡(luò)(Position-wise FeedForward)

定義為: 

nn.Sequential(
    nn.Linear(d_model, d_ff),
    nn.ReLU(),
    nn.Linear(d_ff, d_model),
    nn.Dropout(drop_prob)
)
  • d_model:模型隱層維度(如 512);
  • d_ff:FeedForward 網(wǎng)絡(luò)中間維度(如 2048);
  • ReLU 導(dǎo)致非線性更強(qiáng)的語義表達(dá)。

④ 再次殘差連接 + 層歸一化(Sublayer 2)

x = x + dropout(ffn_output)
x = layer_norm(x)
  • 保證模型在經(jīng)過復(fù)雜變換后仍能保留原始信息;
  • 達(dá)成對(duì)上下文感知表示的穩(wěn)定學(xué)習(xí)。

(2) 維度變化說明(輸入輸出保持一致)

無論經(jīng)過多少層 Encoder block,每個(gè) block 的輸入與輸出形狀始終一致:

張量

形狀

描述

輸入

[batch_size, seq_len, d_model]

批次大小 × 序列長度 × 模型維度

MHA 輸出

[batch_size, seq_len, d_model]

注意力加權(quán)后的輸出

FFN 輸出

[batch_size, seq_len, d_model]

每個(gè) Token 的前饋網(wǎng)絡(luò)輸出

最終輸出

[batch_size, seq_len, d_model]

經(jīng)過兩次 Sublayer 后仍然保持相同維度

(3) PyTorch 模塊封裝示例

import torch
import torch.nn as nn

class EncoderBlock(nn.Module):
    def __init__(self, d_model, n_head, d_ff, drop_prob=0.1):
        """
        Args:
            d_model: 嵌入維度(例如 512)
            n_head: 多頭數(shù)量(通常設(shè)為 8)
            d_ff: Feed Forward 網(wǎng)絡(luò)中間維度(通常為 2048)
            drop_prob: Dropout 概率
        """
        super(EncoderBlock, self).__init__()
        self.attention = MultiHeadAttention(d_model, n_head)
        self.norm1 = nn.LayerNorm(d_model)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Linear(d_ff, d_model),
            nn.Dropout(drop_prob)
        )
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(drop_prob)

    def forward(self, x, src_mask=None):
        # Step 1: Multi-Head Self-Attention
        sa_output, _ = self.attention(x, x, x, src_mask)
        x = self.norm1(x + self.dropout(sa_output))

        # Step 2: Position-wise FeedForward
        ffn_output = self.ffn(x)
        x = self.norm2(x + self.dropout(ffn_output))

        return x

(4) 封裝整個(gè) Encoder 模塊

有了 EncoderBlock 后,我們可以將它 重復(fù) N 次 構(gòu)建完整的 Encoder:

class TransformerEncoder(nn.Module):
    def __init__(self, num_layers, d_model, n_head, d_ff, drop_prob=0.1):
        """
        Args:
            num_layers: encoder block 堆疊層數(shù)(原論文為 6)
            d_model: 模型維度(如 512)
            n_head: 注意力頭數(shù)(如 8)
            d_ff: FeedForward 網(wǎng)絡(luò)維度(如 2048)
        """
        super(TransformerEncoder, self).__init__()
        self.blocks = nn.ModuleList([
            EncoderBlock(d_model, n_head, d_ff, drop_prob)
            for _ in range(num_layers)
        ])

    def forward(self, x, mask=None):
        for block in self.blocks:
            x = block(x, mask)
        return x

?? 示例調(diào)用

# 創(chuàng)建輸入張量
x = torch.randn(batch_size=32, seq_len=20, d_model=512)  # [32, 20, 512]

# 構(gòu)建 Encoder
encoder = TransformerEncoder(num_layers=6, d_model=512, n_head=8, d_ff=2048)
output = encoder(x)

print("Encoder 輸出形狀:", output.shape)  # [32, 20, 512]

4. Decoder結(jié)構(gòu)

Decoder是Transformer架構(gòu)中用于生成輸出序列的部分。與Encoder類似,它由N=6個(gè)相同的Decoder block堆疊而成,但結(jié)構(gòu)更為復(fù)雜。

(1) Decoder Block 的核心組件

一個(gè)標(biāo)準(zhǔn)的Decoder block包含三個(gè)主要子層:

  • Masked Multi-Head Self-Attention
  • Encoder-Decoder Attention
  • Position-wise Feed Forward Network

每個(gè)子層后面都跟隨 殘差連接(Residual Connection) 和 層歸一化(Layer Normalization)。

如下圖右側(cè)紅框表示一個(gè)標(biāo)準(zhǔn)的 Decoder Block。

①「Masked Multi-Head Self-Attention」

這是Decoder的第一個(gè)注意力機(jī)制,用于處理目標(biāo)語言的輸入序列(即解碼器自身的輸入)。

  • 使用 masking 技術(shù) 防止在預(yù)測(cè)當(dāng)前詞時(shí)看到未來的詞,保持因果關(guān)系。
  • 實(shí)現(xiàn)方式:通過 trg_mask 屏蔽未來信息。
x = self.mha1(q=dec_out, k=dec_out, v=dec_out, mask=trg_mask)

②「Encoder-Decoder Attention」

這是 Decoder 的第二個(gè)注意力機(jī)制,用于將 Encoder 的輸出信息融合到 Decoder 中。

  • Query (Q) 來自上一層 Decoder 的輸出;
  • Key (K) 和 Value (V) 來自 Encoder 的輸出;
  • 這樣 Decoder 在生成每個(gè)詞時(shí)都能關(guān)注到整個(gè)輸入句子的信息。
x = self.mha2(q=x, k=enc_out, v=enc_out, mask=src_mask)

③ 「Position-wise Feed Forward Network」

這是一個(gè)簡單的兩層全連接網(wǎng)絡(luò),對(duì)每個(gè)位置的向量進(jìn)行非線性變換。

x = self.ffn(x)

④「殘差連接 + 層歸一化(Add & Norm)」

每個(gè)子層都應(yīng)用:

x = self.ln(x_residual + dropout(sublayer_output))
  • 提升訓(xùn)練穩(wěn)定性;
  • 緩解梯度消失問題。

⑤「Decoder的完整實(shí)現(xiàn)」

DecoderLayer類:

class DecoderLayer(nn.Module):
    def __init__(self, d_model, ffn_hidden, n_head, drop_prob):
        super(DecoderLayer, self).__init__()
        # 第一個(gè) Multi-Head Attention: Masked Self-Attention
        self.mha1 = MultiHeadAttention(d_model, n_head)
        self.ln1 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(p=drop_prob)

        # 第二個(gè) Multi-Head Attention: Encoder-Decoder Attention
        self.mha2 = MultiHeadAttention(d_model, n_head)
        self.ln2 = nn.LayerNorm(d_model)
        self.dropout2 = nn.Dropout(p=drop_prob)

        # 前饋網(wǎng)絡(luò)
        self.ffn = PositionwiseFeedForward(d_model, ffn_hidden)
        self.ln3 = nn.LayerNorm(d_model)
        self.dropout3 = nn.Dropout(p=drop_prob)

    def forward(self, dec_out, enc_out, trg_mask, src_mask):
        x_residual1 = dec_out
        # Step 1: Masked Self-Attention
        x = self.mha1(q=dec_out, k=dec_out, v=dec_out, mask=trg_mask)
        x = self.ln1(x_residual1 + self.dropout1(x))

        if enc_out is not None:
            # Step 2: Encoder-Decoder Attention
            x_residual2 = x
            x = self.mha2(q=x, k=enc_out, v=enc_out, mask=src_mask)
            x = self.ln2(x_residual2 + self.dropout2(x))

        # Step 3: Position-wise Feed Forward
        x_residual3 = x
        x = self.ffn(x)
        x = self.ln3(x_residual3 + self.dropout3(x))

        return x

Decoder 類:

class Decoder(nn.Module):
    def __init__(self, dec_voc_size, max_len, d_model, ffn_hidden, n_head, n_layers, drop_prob, device):
        super().__init__()
        # 輸入嵌入 + 位置編碼
        self.emb = TransformerEmbedding(
            d_model=d_model,
            drop_prob=drop_prob,
            max_len=max_len,
            vocab_size=dec_voc_size,
            device=device
        )

        # 堆疊多個(gè) Decoder Layer
        self.layers = nn.ModuleList([
            DecoderLayer(
                d_model=d_model,
                ffn_hidden=ffn_hidden,
                n_head=n_head,
                drop_prob=drop_prob
            ) for _ in range(n_layers)
        ])

        # 最終輸出層:映射到目標(biāo)詞匯表大小
        self.linear = nn.Linear(d_model, dec_voc_size)

    def forward(self, trg, src, trg_mask, src_mask):
        # trg: 目標(biāo)序列 [batch_size, trg_seq_len]
        # src: Encoder 輸出 [batch_size, src_seq_len, d_model]
        trg = self.emb(trg)

        for layer in self.layers:
            trg = layer(trg, src, trg_mask, src_mask)

        # 輸出:[batch_size, trg_seq_len, dec_voc_size]
        output = self.linear(trg)
        return output

三、Transformer實(shí)現(xiàn)

基于前面實(shí)現(xiàn)的 Encoder 和 Decoder 組件,我們就可以實(shí)現(xiàn) Transformer 模型的完整代碼,如下所示:

import torch
from torch import nn

class Transformer(nn.Module):
    def __init__(self, src_pad_idx, trg_pad_idx, trg_sos_idx, enc_voc_size, dec_voc_size, 
                 d_model, n_head, max_len, ffn_hidden, n_layers, drop_prob, device):
        super().__init__()
        
        # 保存特殊標(biāo)記的索引
        self.src_pad_idx = src_pad_idx  # 源語言填充索引
        self.trg_pad_idx = trg_pad_idx  # 目標(biāo)語言填充索引
        self.trg_sos_idx = trg_sos_idx  # 目標(biāo)語言起始符號(hào)索引
        self.device = device  # 設(shè)備信息
        
        # 構(gòu)建 Encoder
        self.encoder = Encoder(
            d_model=d_model,  # 模型維度
            n_head=n_head,  # 注意力頭數(shù)量
            max_len=max_len,  # 最大序列長度
            ffn_hidden=ffn_hidden,  # 前饋網(wǎng)絡(luò)隱藏層維度
            enc_voc_size=enc_voc_size,  # 源語言詞匯表大小
            drop_prob=drop_prob,  # Dropout 概率
            n_layers=n_layers,  # Encoder 層數(shù)
            device=device  # 設(shè)備信息
        )
        
        # 構(gòu)建 Decoder
        self.decoder = Decoder(
            d_model=d_model,  # 模型維度
            n_head=n_head,  # 注意力頭數(shù)量
            max_len=max_len,  # 最大序列長度
            ffn_hidden=ffn_hidden,  # 前饋網(wǎng)絡(luò)隱藏層維度
            dec_voc_size=dec_voc_size,  # 目標(biāo)語言詞匯表大小
            drop_prob=drop_prob,  # Dropout 概率
            n_layers=n_layers,  # Decoder 層數(shù)
            device=device  # 設(shè)備信息
        )

    def forward(self, src, trg):
        # 創(chuàng)建源序列的 padding mask
        src_mask = self.make_pad_mask(src, src, self.src_pad_idx, self.src_pad_idx)
        
        # 創(chuàng)建目標(biāo)序列到源序列的 mask
        src_trg_mask = self.make_pad_mask(trg, src, self.trg_pad_idx, self.src_pad_idx)
        
        # 創(chuàng)建目標(biāo)序列的 padding mask 和因果mask的組合
        trg_mask = self.make_pad_mask(trg, trg, self.trg_pad_idx, self.trg_pad_idx) * \
                   self.make_no_peak_mask(trg, trg)

        # 編碼器前向傳播
        enc_src = self.encoder(src, src_mask)
        
        # 解碼器前向傳播
        output = self.decoder(trg, enc_src, trg_mask, src_trg_mask)
        
        return output

    def make_pad_mask(self, q, k, q_pad_idx, k_pad_idx):
        # 獲取輸入序列長度
        len_q, len_k = q.size(1), k.size(1)

        # 創(chuàng)建針對(duì) key 的 mask
        # batch_size x 1 x 1 x len_k
        k_mask = k.ne(k_pad_idx).unsqueeze(1).unsqueeze(2)
        # batch_size x 1 x len_q x len_k
        k_mask = k_mask.repeat(1, 1, len_q, 1)

        # 創(chuàng)建針對(duì) query 的 mask
        # batch_size x 1 x len_q x 1
        q_mask = q.ne(q_pad_idx).unsqueeze(1).unsqueeze(3)
        # batch_size x 1 x len_q x len_k
        q_mask = q_mask.repeat(1, 1, 1, len_k)
        
        # 組合兩個(gè) mask
        mask = k_mask & q_mask
        
        return mask

    def make_no_peak_mask(self, q, k):
        # 創(chuàng)建因果mask,防止解碼器看到未來信息
        len_q, len_k = q.size(1), k.size(1)
        
        # 創(chuàng)建下三角矩陣,保證解碼器只能關(guān)注當(dāng)前詞及之前的詞
        mask = torch.tril(torch.ones(len_q, len_k)).type(torch.BoolTensor).to(self.device)
        
        return mask
責(zé)任編輯:趙寧寧 來源: 小喵學(xué)AI
相關(guān)推薦

2025-01-02 15:40:23

2025-01-16 12:30:00

2020-11-12 18:53:34

代碼Transformer編程

2010-09-10 14:24:27

CSS盒狀模型

2024-08-15 11:37:05

2023-12-05 13:38:11

架構(gòu)模型

2023-01-03 10:06:08

模型計(jì)算

2023-06-05 14:04:59

模型AI

2021-10-11 09:38:46

模型Transformer代碼

2021-01-07 08:12:47

數(shù)據(jù)結(jié)構(gòu)二叉樹

2022-07-22 07:18:53

代碼DeepMind

2024-01-30 01:12:37

自然語言時(shí)間序列預(yù)測(cè)Pytorch

2022-06-20 07:16:25

機(jī)器學(xué)習(xí)模型Codex

2011-07-20 15:20:14

IPhone AVAudioRec

2024-08-19 02:35:00

模型量化深度學(xué)習(xí)

2023-12-08 09:15:53

Java單表樹形結(jié)構(gòu)Tree

2024-10-22 17:24:32

2009-08-13 15:41:50

C#結(jié)構(gòu)體指針

2025-01-16 08:30:00

LLMAI訓(xùn)練

2025-04-25 09:00:00

Transforme模型代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

产国精品偷在线| 精品精品国产高清a毛片牛牛| 欧美日韩精品综合| 亚洲午夜无码久久久久| 欧美成人自拍| 日韩精品一区在线观看| 国产最新免费视频| se在线电影| 国产一区二区免费看| 97久久久久久| 能直接看的av| 视频一区中文字幕精品| 欧美性开放视频| 欧美 另类 交| 天堂av手机版| 精品在线播放免费| 91国产精品91| 亚洲精品卡一卡二| 亚洲制服欧美另类| 日韩一区二区三区三四区视频在线观看| 97成人精品视频在线观看| 97伦理在线四区| 久久99久久98精品免观看软件| 亚洲区小说区图片区qvod按摩 | 丝袜亚洲另类欧美重口| 男女性杂交内射妇女bbwxz| 欧美特大特白屁股xxxx| 亚洲自拍另类综合| 日韩中文字幕一区| 色网站免费观看| 国产真实乱子伦精品视频| 日本成人黄色片| 国产亚洲精品av| 91欧美大片| 亚洲男人第一av网站| 国产精品嫩草69影院| 欧美黄页在线免费观看| 亚洲高清不卡在线| 超级碰在线观看| 瑟瑟视频在线| 久久久久久免费毛片精品| 99国产精品久久久久老师| 亚洲图片小说视频| 日韩成人精品在线观看| 国产91|九色| 日韩欧美大片在线观看| 国产综合欧美| 欧美大胆a视频| 国精产品一区一区二区三区mba| 国产一区日韩| 亚洲欧美中文日韩在线v日本| 欧美一级片黄色| 成午夜精品一区二区三区软件| 欧美美女视频在线观看| 亚洲成色www.777999| 色老太综合网| 色综合久久久久网| 任你操这里只有精品| av日韩电影| 色综合网色综合| 亚洲色欲综合一区二区三区| 五月天av在线| 精品久久久精品| 777精品久无码人妻蜜桃| 国产美女精品写真福利视频| 亚洲电影第三页| 妞干网在线视频观看| 91九色在线看| 欧美日韩国产在线看| 黄色免费视频大全| 欧美成人黑人| 欧美亚日韩国产aⅴ精品中极品| 青青青在线播放| 久久久一本精品| 精品视频一区 二区 三区| 国产成人黄色网址| 国产色99精品9i| 日韩精品一区二区三区视频播放| 亚洲图片欧美另类| 天天躁日日躁狠狠躁欧美巨大小说| 亚洲精品国产精品久久清纯直播| 黄色工厂在线观看| 精品无人区麻豆乱码久久久| 综合久久五月天| 免费看特级毛片| 韩日精品在线| 人九九综合九九宗合| 亚洲综合精品国产一区二区三区| 精品一区中文字幕| 国产aⅴ精品一区二区三区黄| 色窝窝无码一区二区三区| 久久亚洲精品国产精品紫薇 | 欧美黄色aaaa| 97视频在线观看免费高清完整版在线观看| 视频一区二区三区四区五区| 奇米一区二区三区| 99re国产| 国产在线观看高清视频| 亚洲免费观看高清完整版在线观看| 欧美国产视频一区| 自拍网站在线观看| 欧美夫妻性生活| 日本50路肥熟bbw| 欧美色女视频| 久久久久国产精品免费| 91丝袜一区二区三区| 激情综合色播激情啊| 国产精品日韩一区二区三区| 国产高清免费av在线| 一区二区三区在线视频播放| 久久久久人妻精品一区三寸| 91成人短视频在线观看| 日韩精品日韩在线观看| 成人涩涩小片视频日本| 国产精品视频| 91观看网站| 蜜桃视频在线免费| 亚洲午夜影视影院在线观看| 国产精品久久久毛片| 老司机精品视频在线播放| 中文字幕亚洲欧美日韩高清| 国产午夜福利片| 看片的网站亚洲| 蜜桃久久影院| tube8在线hd| 91精品国产综合久久婷婷香蕉 | 欧美尿孔扩张虐视频| 最近2019年日本中文免费字幕| 欧美精品xxxxx| 九九热在线视频观看这里只有精品| 久久综合九色欧美狠狠| 麻豆蜜桃在线| 91精品国产日韩91久久久久久| 高潮毛片无遮挡| 在线免费高清一区二区三区| 91夜夜揉人人捏人人添红杏| av在线资源网| 欧美性猛交xxxx乱大交3| 日韩成人av影院| 亚洲91中文字幕无线码三区| 国产精品91在线| 四虎精品在线| 偷拍日韩校园综合在线| 在线播放第一页| 自产国语精品视频| 成人免费高清完整版在线观看| 3p在线观看| 欧美性受xxxx| 日本免费www| 日韩二区三区在线观看| 日本一区二区三不卡| 亚洲国产成人二区| 精品视频www| 国产成人愉拍精品久久| 99re热这里只有精品视频| 婷婷五月综合缴情在线视频| 91亚洲无吗| 国外成人在线视频| 色欲av伊人久久大香线蕉影院| 亚洲一区二区三区中文字幕| 精品人妻二区中文字幕| 国内精品嫩模av私拍在线观看| 999精品在线观看| 婷婷丁香在线| 欧美一级片在线看| 久草视频免费播放| 国产91精品欧美| 青草青青在线视频| 久久a爱视频| 18一19gay欧美视频网站| 亚洲av成人精品毛片| 欧美午夜精品久久久久久人妖| av在线网站观看| 肉肉av福利一精品导航| 中文字幕人成一区| 日韩一区二区三区色| 欧美精品久久久久久久久久| 水莓100国产免费av在线播放| 婷婷综合另类小说色区| 亚洲第一香蕉网| 蜜臀av在线播放一区二区三区| 中文字幕中文字幕在线中心一区| 伊色综合久久之综合久久| 国产综合在线看| 青青草在线免费观看| 在线观看视频一区二区欧美日韩| 国产又粗又长又黄的视频| 国产精品一二三四五| 青青草国产精品视频| 国产一区不卡| 69堂成人精品视频免费| aa国产成人| 在线电影av不卡网址| 国产视频手机在线| 精品国产999| 日本黄区免费视频观看| 成人免费毛片片v| 日本在线视频www| 五月开心六月丁香综合色啪 | 免费看污污网站| 中文字幕亚洲综合久久五月天色无吗'' | 国产一区二区三区四区在线观看 | 欧美巨大xxxx做受沙滩| 亚洲三级黄色在线观看| www.狠狠干| 在线免费观看成人短视频| 亚洲天堂黄色片| 久久蜜桃av一区二区天堂| 亚洲精品国产久| 久热综合在线亚洲精品| 黄色一级片国产| 欧美三级伦理在线| 国产一区二区不卡视频在线观看| 性高爱久久久久久久久| 欧美激情综合亚洲一二区| 国产视频第一区| 亚洲精品久久久久中文字幕二区 | 蜜桃视频动漫在线播放| www亚洲精品| 国产精品久久久久一区二区国产| 日韩美女主播在线视频一区二区三区| 无码aⅴ精品一区二区三区| 亚洲综合清纯丝袜自拍| 天堂资源在线视频| 91麻豆国产精品久久| 三上悠亚 电影| 老司机午夜精品| 日本激情视频在线| 日韩图片一区| 丁香色欲久久久久久综合网| 91嫩草亚洲精品| 亚洲二区三区四区| 国产99久久久国产精品成人免费| 国产精品日本一区二区| 精品国产一区二区三区性色av| 国产精品久久999| 成人免费av电影| 日本一区二区三区四区视频| 国产ktv在线视频| 久久免费高清视频| 欧美日韩色网| 欧美巨猛xxxx猛交黑人97人| 欧美精品电影| 中文字幕亚洲精品| 91xxx在线观看| 在线日韩第一页| 国产视频第一页在线观看| 亚洲欧美综合另类中字| 色中色在线视频| 亚洲精品中文字幕av| 污视频网站免费观看| 亚洲国产福利在线| 蜜桃在线一区二区| 精品99999| 色网站免费观看| 国产丝袜视频一区| 免费动漫网站在线观看| 亚洲午夜精品久久久久久久久久久久 | 精品丝袜在线| 91高清免费视频| 黑人精品一区| 国产精品jizz在线观看麻豆| 成人在线视频播放| 国产精品午夜一区二区欲梦| 欧美在线一级| av一本久道久久波多野结衣| 91麻豆精品国产91久久久久推荐资源| 电影午夜精品一区二区三区| 国产精品x8x8一区二区| 久久偷窥视频| 国产传媒欧美日韩成人精品大片| 日韩女优中文字幕| 亚洲激情久久| 人妻无码久久一区二区三区免费| 一本色道精品久久一区二区三区| 国产一区亚洲二区三区| 免费成人性网站| 特黄特黄一级片| www.99精品| 日本少妇高潮喷水xxxxxxx| 欧美激情中文不卡| 欧美色图一区二区| 精品久久久久久| 中文资源在线播放| 日韩欧美激情一区| 日本aaa在线观看| 日韩亚洲综合在线| hd国产人妖ts另类视频| 国产精品久久久久久久app| 久久久久久久久久久久电影| 国内精品**久久毛片app| 欧美影院三区| a级免费在线观看| 日韩在线观看一区二区| 在线成人免费av| 久久久久久久久伊人| 免费在线观看黄色小视频| 亚洲一区二区三区四区五区中文| 亚洲欧美另类在线视频| 欧美日韩二区三区| 网站黄在线观看| 美女精品久久久| 手机看片久久| 国产精品三区www17con| 欧美成人激情| 国产男女在线观看| 国产不卡高清在线观看视频| 欧美 日韩 成人| 亚洲一区二区三区四区中文字幕| 日韩欧美一级大片| 日韩av资源在线播放| 黄av在线免费观看| 国产成人精品久久亚洲高清不卡| 久久精品九色| 一区二区三区视频| 久久精品官网| 人妻av一区二区| 亚洲欧美激情插 | 欧美精品一区三区在线观看| 一本一道久久a久久精品蜜桃| 日韩免费高清在线| av不卡在线观看| 欧美日韩国产精品综合| 欧美三级乱人伦电影| 男男电影完整版在线观看| 久久久久久国产| 日本精品视频| 少妇高潮流白浆| 秋霞影院一区二区| xxx在线播放| 欧美午夜美女看片| 日韩一区免费视频| 欧美极品xxxx| 亚洲一区二区三区在线免费| 一卡二卡3卡四卡高清精品视频| 久久美女性网| 深爱五月激情网| 精品久久中文字幕| 天天摸夜夜添狠狠添婷婷| 久久久欧美精品| 国产精品videossex| 免费在线看黄色片| 国产成人亚洲精品青草天美| 性猛交娇小69hd| 日本高清免费不卡视频| 嫩草在线播放| 国产福利精品在线| 欧美精美视频| 日韩一级在线免费观看| 久久久久久久久99精品| 午夜影院免费在线观看| 亚洲欧美国产另类| 性欧美gay| 亚洲国产精品毛片| 麻豆精品一区二区| 少妇被躁爽到高潮无码文| 日韩一区二区影院| 欧美v亚洲v| 精品一区二区三区视频日产| 国产一区二区三区的电影 | 亚洲黄页在线观看| 92看片淫黄大片一级| 久久欧美中文字幕| 欧美一区二区三区久久久| 永久免费毛片在线播放不卡| 本网站久久精品| 综合色婷婷一区二区亚洲欧美国产| 国产在线精品免费| 久草视频在线资源| 日韩高清av在线| 91tv亚洲精品香蕉国产一区| 在线精品亚洲一区二区| 国产精品一区三区| 日韩精品――中文字幕| 亚洲欧洲黄色网| 欧美日韩伦理一区二区| av在线免费观看国产| 99久久免费国产| 最新中文字幕第一页| 久久精品视频免费播放| 波多野结衣在线一区二区| 亚洲国产精品久久久久婷蜜芽| 国产清纯美女被跳蛋高潮一区二区久久w | 91美女视频在线| 91手机在线视频| 免费精品视频| 国产精品国产三级国产传播| 亚洲精品成人久久电影| 国产精品无码久久久久| www.夜夜爱| 欧美国产日韩精品免费观看| a在线观看免费| 欧美一级片久久久久久久| 国产精品久久久久久久久久10秀| 亚洲av无码一区东京热久久| 色哟哟亚洲精品| 羞羞视频在线观看不卡| 欧洲av一区| 国产成人久久精品77777最新版本| 免费黄色网址在线|