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

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT 原創

發布于 2024-6-14 14:56
瀏覽
0收藏

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

The Annotated Transformer這篇文章從零復現了2017年那篇Transformer論文,The Annotated Transformer注釋加量版在此基礎上追加注釋和輸出數據維度信息進一步揭開Transformer的一些細節,原始Transformer是一個Encoder-Decoder架構的模型,今天我將用同樣的方法繼續學習GPT系列模型中最簡單的GPT2,GPT是Decoder only架構模型,所以,我們只需要關注Transformer的右側部分。

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

由于代碼過長,所以沒有把全部代碼拷貝過來,建議打開下面代碼作為參照閱讀本文。

代碼運行環境:google colab

??https://github.com/AIDajiangtang/annotated-transformer/blob/master/gpt_model_from_scratch.ipynb??

-1.超參數

GPT_CONFIG_124M = {
"vocab_size": 50257, #詞表大小
"context_length": 256, #上下文長度
"emb_dim": 768,#詞嵌入維度
"n_heads": 12,#頭的個數
"n_layers": 12,#N=12
"drop_rate": 0.1,
"qkv_bias": False
}

0.下載訓練數據

訓練數據來自伊迪絲·華頓(Edith Wharton)的一篇短篇小說《Roman Fever》。

import requests
url = "https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt"
#下載數據
# Send a GET request to the URL
response = requests.get(url)
text_data = response.tex

total_characters = len(text_data)
total_tokens = len(tokenizer.encode(text_data))
print(f"Total characters: {total_characters}")
print(f"Total tokens: {total_tokens}")

Total characters: 20479
Total tokens: 5145

訓練數據包含20479個字符,5145個tokens,下面是打印的部分內容。

I HAD always thought Jack Gisburn rather a cheap genius--though a good fellow enough--so it was no great surprise to me to hear that, in the height of his glory, he had dropped his painting, married a rich widow, and established himself in a villa on the Riviera. (Though I rather thought it would have been Rome or Florence.)
"The height of his glory"--that was what the women called it. I can hear Mrs. Gideon Thwing--his last Chicago sitter--deploring his unaccountable abdication. "Of course it's going to send the value of my picture 'way up; but I don't think of that, Mr. Rickham--the loss to Arrt is all I think of." The word, on Mrs. Thwing's lips, multiplied its rs
Well!--even through the prism of Hermia's tears I felt able to face the fact with equanimity. Poor Jack Gisburn! The women had made him--it was fitting that they should mourn him. Among his own sex fewer regrets were heard, and in his own trade hardly a murmur. Professional jealousy? Perhaps. If it were, the honour of the craft was vindicated by little Claude Nutley, who, in all good faith, brought out in the Burlington a very handsome "obituary" on Jack--one of those showy articles stocked with random technicalities that I have heard (I won't say by whom) compared to Gisburn's painting. And so--his resolve being apparently irrevocable--the discussion gradually died out, and, as Mrs. Thwing had predicted, the price of "Gisburns" went up.
It was not till three years later that, in the course of a few weeks' idling on the Riviera, it suddenly occurred to me to wonder why Gisburn had given up his painting. On reflection, it really was a tempting problem. To accuse his wife would have been too easy--his fair sitters had been denied the solace of saying that Mrs. Gisburn had "dragged him down." For Mrs. Gisburn--as such--had not existed till nearly a year after Jack's resolve had been taken. It might be that he had married her--since he liked his ease--because he didn't want to go on painting; but it would have been hard to prove that he had given up his painting because he had married her.
Of course, if she had not dragged him down, she had equally, as Miss Croft contended, failed to "lift him up"--she had not led him back to the easel. To put the brush into his hand again--what a vocation for a wife! But Mrs. Gisburn appeared to have disdained it--and I felt it might be interesting to find out why.

1.分詞

我們本次要實現的GPT2使用的是一種基于子詞(BPE)的分詞方法。

分詞是將上面的文本轉換成數字索引,根據超參數"vocab_size": 50257,整個詞表有50257單詞(子詞),所以數字索引范圍是0-50256。

import tiktoken
# 加載gpt2 tokenizer
tokenizer = tiktoken.get_encoding("gpt2")

# 文本轉換成token ids
def text_to_token_ids(text, tokenizer):
    encoded = tokenizer.encode(text, allowed_special={'<|endoftext|>'})
    encoded_tensor = torch.tensor(encoded).unsqueeze(0)
    # .unsqueeze(0) adds the batch dimension
    return encoded_tensor

# token ids轉換成文本
def token_ids_to_text(token_ids, tokenizer):
    flat = token_ids.squeeze(0) # Remove batch dimension
    return tokenizer.decode(flat.tolist())

如果想詳細了解分詞方法的請參考下面這篇文章。

1.圖解tokenization

為了查看后面輸出日志中的token ids對應的文本,可以通過text_to_token_ids和token_ids_to_text進行文本和ids相互轉換。

也可以使用在線工具:The Tokenizer Playground,但是在Playground中沒有找到GPT2選項,可以選擇GPT3。

??https://huggingface.co/spaces/Xenova/the-tokenizer-playground??

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

如上圖,BPE分詞方法將lowest分成兩個子詞low和est。

2.構造輸入X和標簽Y

訓練數據一共有5145個token,90%*5145作為訓練集,10%*5145作為驗證集。

train_ratio = 0.90 # 90% of data will be training, 10% will be validation
split_index = int(train_ratio * len(text_data))
train_data = text_data[:split_index]
val_data = text_data[split_index:]

根據超參數的設置上下文長度"context_length": 256,也就是訓練時輸入到模型的每個樣本長度256個token,stride=256,所以會從訓練數據的頭開始,每隔256個token取256個tokens作為輸入X,標簽則是將窗口向右移動一位。

import torch
from torch.utils.data import Dataset, DataLoader

 # Create a data loader
class GPTDatasetV1(Dataset):
    def __init__(self, txt, tokenizer, max_length, stride):
        self.input_ids = []
        self.target_ids = []

        token_ids = tokenizer.encode(txt)

        for i in range(0, len(token_ids) - max_length, stride):
            # The input chunk
            input_chunk = token_ids[i:i + max_length]
            # The target chunk is the input chunk, offset by 1 character
            target_chunk = token_ids[i + 1: i + max_length + 1]

            # Append chunk to list of chunks
            self.input_ids.append(torch.tensor(input_chunk))
            self.target_ids.append(torch.tensor(target_chunk))

    def __len__(self):
        return len(self.input_ids)

    def __getitem__(self, idx):
        return self.input_ids[idx], self.target_ids[idx]

因為batch_size=2,所以每個batch的輸入和標簽維度都是torch.Size([2, 256]) 。

torch.manual_seed(123)
train_loader = create_dataloader_v1(
train_data,
batch_size=2,
max_length=GPT_CONFIG_124M["context_length"],# 256
stride=GPT_CONFIG_124M["context_length"],# 256
drop_last=True,
shuffle=True,
num_workers=0
)
val_loader = create_dataloader_v1(
val_data,
batch_size=2,
max_length=GPT_CONFIG_124M["context_length"],
stride=GPT_CONFIG_124M["context_length"],
drop_last=False,
shuffle=False,
num_workers=0,
)

# Load the data
def create_dataloader_v1(txt, batch_size=4, max_length=256, stride=128, shuffle=True, drop_last=True, num_workers=0):
    tokenizer = tiktoken.get_encoding("gpt2")
    dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
    dataloader = DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=shuffle,
        drop_last=drop_last,
        num_workers=0
    )

    return dataloader

為了便于理解,舉個簡單的例子,假設訓練數據的token_ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],context_length=4,stride=4,batch_size=2。

Input IDs: [tensor([1, 2, 3, 4]), tensor([5, 6, 7, 8])]
Target IDs: [tensor([2, 3, 4, 5]),tensor([6, 7, 8, 9])]
X=[tensor([1, 2, 3, 4]), tensor([5, 6, 7, 8])]
Y=[tensor([2, 3, 4, 5]),tensor([6, 7, 8, 9])]

3.詞嵌入

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

接下來將[2, 256]個token ids轉換成詞嵌入,根據超參數設置:"emb_dim": 768,也就是每個token id映射成一個768維的向量。

簡單地講,這768個數字中每個數字都可以表示一個屬性,維度越高能表述的屬性越豐富,想詳細了解詞嵌入的請閱讀下面文章。

另外,在計算注意力時,沒有考慮token之間的相對位置,所以要在詞嵌入上加一個位置編碼,位置編碼向量維度與詞嵌入維度相同,都是768。

class GPTModel(nn.Module):
  def __init__(self, config):
    super().__init__()

    self.token_embedding = nn.Embedding(config["vocab_size"], config["emb_dim"])#[50257,768]
    self.positional_embedding = nn.Embedding(config["context_length"], config["emb_dim"])#[256,768]
    #隨機將一些元素的值設置為零,不改變維度
    self.drop_embedding = nn.Dropout(config["drop_rate"])

    self.transformer_blocks = nn.Sequential(
        *[TransformerBlock(config) for _ in range(config["n_layers"])]
    )
    #對數據進行平滑,不改變維度
    self.final_norm = LayerNorm(config["emb_dim"])
    self.out_head = nn.Linear(config["emb_dim"], config["vocab_size"], bias=False)

  def forward(self, in_idx):
    batch_size, sequence_length = in_idx.shape #in_idx.shape:訓練時[2, 256],推理時[1, N]
    token_embeddings = self.token_embedding(in_idx)#訓練時[2, 256,768],推理時[1, N,768]
    positional_embeddings = self.positional_embedding(
        torch.arange(sequence_length, device=in_idx.device)
    )#[256,768]
    x = token_embeddings + positional_embeddings #訓練時[2, 256,768],推理時[1, N,768]
    x = self.drop_embedding(x)#[2, 256,768]

    x = self.transformer_blocks(x)#訓練時[2, 256,768],推理時[1, N,768]
    x = self.final_norm(x)#訓練時[2, 256,768],推理時[1, N,768]
    logits = self.out_head(x)#訓練時[2, 256,50257],推理時[1, N,50257]
    return logits

詞嵌入和位置編碼通過下面這兩個可學習的嵌入層完成,嵌入層可以簡單地理解成是一個映射表(map)。將token id或者位置索引映射成一個向量。

self.token_embedding = nn.Embedding(config["vocab_size"], config["emb_dim"])
self.positional_embedding = nn.Embedding(config["context_length"], config["emb_dim"])

在訓練過程中,token_embedding嵌入層將一個batch的輸入數據[2, 256]個token id映射成[2, 256, 768]維詞嵌入。

在推理過程中,token_embedding嵌入層將輸入數據[1, N]映射成[1, N, 768]維詞嵌入,N是隨著不斷地預測下一個token而增長的,直到遇到結束符。

positional_embeddings = self.positional_embedding(
torch.arange(sequence_length, device=in_idx.device)
)

positional_embeddings 嵌入層將256個位置映射成[256,768]位置編碼向量,位置通過torch.arange生成,元素內容是0,1....255。

位置編碼之所以是[256,768]而非[2, 256, 768],說明每個batch下所有樣本共用同一個位置編碼。

與前一篇采用固定計算方式不同,gpt2采用的是可學習的方法,也就是在訓練過程中positional_embedding中的內容會通過反向傳播進行更改。

最終輸出[2, 256, 768]維詞嵌入。

4.TransformerBlock

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

根據超參數設置"n_layers": 12,模型會經過12個結構相同,但參數獨立的TransformerBlock模塊。def forward(self, x): shortcut = x

class TransformerBlock(nn.Module):
  def __init__(self, config):
    super().__init__()

    self.attention = MultiHeadAttention(
        d_in=config["emb_dim"],
        d_out=config["emb_dim"],
        context_length=config["context_length"],
        dropout=config["drop_rate"],
        num_heads=config["n_heads"],
        qkv_bias=config["qkv_bias"]
    )

    self.ff = FeedForward(config)
    self.norm1 = LayerNorm(config["emb_dim"])
    self.norm2 = LayerNorm(config["emb_dim"])
    self.drop_shortcut = nn.Dropout(config["drop_rate"])

  def forward(self, x):
    shortcut = x

    # Attention layer
    x = self.norm1(x)
    x = self.attention(x)
    x = self.drop_shortcut(x)
    x = x + shortcut         # Add the original input back

    # Feedforward layer
    shortcut = x
    x = self.norm2(x)
    x = self.ff(x)
    x = self.drop_shortcut(x)
    x = x + shortcut         # Add the original input back
    return x

TransformerBlock是由MultiHeadAttention、FeedForward和LayerNorm構成。

接下來我們看看數據是如何流經這些層的。

5.MultiHeadAttention

class MultiHeadAttention(nn.Module):
  def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
    super().__init__()

    assert d_out % num_heads == 0, "d_out must be divisible by num_heads"

    self.d_out = d_out                  # 768
    self.num_heads = num_heads          # 12
    self.head_dim = d_out // num_heads  # 64
    self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
    self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
    self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
    self._out_proj = nn.Linear(d_out, d_out)
    self.dropout = nn.Dropout(dropout)
    self.register_buffer(
        'mask',
        torch.triu(torch.ones(
            context_length,             # 256
            context_length,             # 256
          ), diagonal=1)
    )

  def forward(self, x):
    batch_size, num_tokens, embedding_length = x.shape
    keys = self.W_key(x)
    queries = self.W_query(x)
    values = self.W_value(x)

    # Add the num_heads and head_dim dimensions
    keys = keys.view(batch_size, num_tokens, self.num_heads, self.head_dim)       # Transform to a tensor of dimensions: 2 x 256 x 12 x 64
    queries = queries.view(batch_size, num_tokens, self.num_heads, self.head_dim) # Transform to a tensor of dimensions: 2 x 256 x 12 x 64
    values = values.view(batch_size, num_tokens, self.num_heads, self.head_dim)   # Transform to a tensor of dimensions: 2 x 256 x 12 x 64

    # Transpose from (batch_size, num_tokens, num_heads, head_dim) to (batch_size, num_heads, num_tokens, head_dim)
    queries = queries.transpose(1, 2)
    keys = keys.transpose(1, 2)
    values = values.transpose(1, 2)

    # Calculate attention scores
    attention_scores = queries @ keys.transpose(2, 3)
    mask_bool = self.mask.bool()[:num_tokens, :num_tokens]

    # Mask the attention scores
    attention_scores.masked_fill_(mask_bool, -torch.inf)

    # Calculate attention weights
    attention_weights = torch.softmax(attention_scores / keys.shape[-1]**0.5, dim=-1)

    # Apply dropout to attention weights
    attention_weights = self.dropout(attention_weights)

    # Calculate context vectors
    context_vectors = (attention_weights @ values).transpose(1, 2)

    # Concatenate the context vectors
    context_vectors = context_vectors.contiguous().view(batch_size, num_tokens, self.d_out)
    return self._out_proj(context_vectors)

輸入的詞嵌入[2, 256, 768]先經過三個矩陣[768, 768]變換,分別得到q、k、v,維度都是[2, 256, 768]。

根據超參數設置"n_heads": 12,將q、k、v reshape成[2, 256, 12,64],再轉置成[2, 12,256, 64]。將原始768維詞嵌入劃分到12個頭中,每個頭64維,這就實現了多頭注意力機制。

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區圖片

然后計算每個頭的注意力,注意力分數矩陣維度[2, 12, 256, 256]。

為了防止看到未來時刻的內容,構造一個上三角掩碼矩陣[256, 256],其對角線以上的部分設置True, 再將注意力分數矩陣中對應掩碼矩陣為True的位置設置為負無窮,這樣softmax 之后接近于零,以屏蔽未來位置的注意力得分。

self.register_buffer(
    'mask',
    torch.triu(torch.ones(
        context_length,             # 256
        context_length,             # 256
      ), diagonal=1)
)
mask_bool = self.mask.bool()[:num_tokens, :num_tokens]

# Mask the attention scores
attention_scores.masked_fill_(mask_bool, -torch.inf)


The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

然后將注意力分數矩陣[2, 12, 256, 256]與v[2, 12,256, 64]相乘,輸出[2, 12,256, 64]。

最后將多個頭的輸出通過reshape拼接在一起輸出[2, 256, 768],再經過一個線性層[768, 768]輸出[2, 256, 768],最終與輸入進行殘差鏈接輸出[2, 256, 768]。

與原始Transformer的Encoder-Decoder架構不同,GPT2是Decoder only架構,所以q、k、v全部來自輸入或者前一層輸出,而不是來自Encoder。

6.LaynerNorm

class LayerNorm(nn.Module):
def init(self, emb_dim):
super().init()
self.eps = 1e-5
self.scale = nn.Parameter(torch.ones(emb_dim))
self.shift = nn.Parameter(torch.zeros(emb_dim))
def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
normalized_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * normalized_x + self.shift

LaynerNorm的目的是為了計算穩定,不改變維度,LaynerNorm層的輸入輸出維度均是[2, 256, 768]。

7.FeedForward

FeedForward是一個MLP層,前面LaynerNorm層的輸出[2, 256, 768],2256個詞嵌入并行通過MLP層,先升維到4768,再恢復到768,中間使用GELU非線性激活函數。

# Implement feed-forward neural network
class FeedForward(nn.Module):
  def __init__(self, config):
    super().__init__()
    self.layers = nn.Sequential(
        nn.Linear(config["emb_dim"], 4 * config["emb_dim"]),
        GELU(),
        nn.Linear(4 * config["emb_dim"], config["emb_dim"]),
    )

  def forward(self, x):
    return self.layers(x)

MLP層不會改變輸入維度[2, 256, 768],但會通過非線性變換會進一步修改詞嵌入的值,以次提升模型的表示能力,生成更高層次的抽象特征。

8.輸出

The Annotated GPT2注釋加量版,讀懂代碼才算讀懂了GPT -AI.x社區

class GPTModel(nn.Module):
  def __init__(self, config):
    super().__init__()

    self.token_embedding = nn.Embedding(config["vocab_size"], config["emb_dim"])
    self.positional_embedding = nn.Embedding(config["context_length"], config["emb_dim"])
    self.drop_embedding = nn.Dropout(config["drop_rate"])

    self.transformer_blocks = nn.Sequential(
        *[TransformerBlock(config) for _ in range(config["n_layers"])]
    )

    self.final_norm = LayerNorm(config["emb_dim"])
    self.out_head = nn.Linear(config["emb_dim"], config["vocab_size"], bias=False)

  def forward(self, in_idx):
    batch_size, sequence_length = in_idx.shape
    token_embeddings = self.token_embedding(in_idx)
    positional_embeddings = self.positional_embedding(
        torch.arange(sequence_length, device=in_idx.device)
    )
    x = token_embeddings + positional_embeddings
    x = self.drop_embedding(x)

    x = self.transformer_blocks(x)
    x = self.final_norm(x)
    logits = self.out_head(x)
    return logits

MLP層的輸出[2, 256, 768]先經過一個LaynerNorm進行平滑操作。

最后2*256個token并行經過一個輸出線性層[768, 50257],將[2, 256, 768]映射成[2, 256, 50257]。

也就是每個token都會輸出一個概率分布,這50257個概率值表示下一個token屬于詞表中50257個詞的概率。

9.計算損失

訓練過程中需要通過計算損失來更新參數,如何根據輸出[2, 256, 50257]計算損失呢?

在準備訓練數據時已經構造了標簽,維度與輸入X一致,也是[2, 256]。

def calc_loss_batch(input_batch, target_batch, model, device):
  """
  Calculates the loss for a single batch.
  """
  input_batch = input_batch.to(device)
  target_batch = target_batch.to(device)


  # Run the model
  logits = model(input_batch)
  print("target_batch loss")
  print(target_batch.flatten().shape)
  print("logits.flatten(0, 1)")
  print(logits.flatten(0, 1).shape)
  # Calculate the loss
  loss = torch.nn.functional.cross_entropy(
      logits.flatten(0, 1),
      target_batch.flatten(),
  )
  return loss

input_batch是輸入X,維度[2, 256],target_batch是標簽,維度[2, 256],輸入經過模型后輸出[2, 256, 50257],展平后[512, 50257],標簽展平后[512],每個元素表示詞表中位置。

cross_entropy估計是根據這[512]位置構造one-hot編碼,然后與輸出logits計算損失值。

推理階段

def generate_text_simple(model, idx, max_new_tokens, context_size):
    for _ in range(max_new_tokens):
        idx_cond = idx[:, -context_size:]
        with torch.no_grad():
            logits = model(idx_cond)

        logits = logits[:, -1, :]#取最后一個詞的概率分布
        probas = torch.softmax(logits, dim=-1)
        idx_next = torch.argmax(probas, dim=-1, keepdim=True)#取概率最大的
        idx = torch.cat((idx, idx_next), dim=1)#將其加到輸入后面

    return idx

GPT通過自回歸方式預測下一個token,但在訓練過程中,通過矩陣,掩碼實現了并行計算。

但在預測階段每次前向計算只能預測一個token,然后將這個新token添加到輸入末尾在此作為輸入,直到輸出結束符。

設置生成50個詞。

第一輪:

初始上下文:"Oh Juliet, where is"

轉換成token后:

torch.Size([1, 5])
tensor([[ 5812, 38201,    11,   810,   318]])

只取is對應輸出的概率分布,模型預測is的下一個詞是“,”,將“,”加到輸入后面作為下一輪輸入。

第二輪:

輸入:"Oh Juliet, where is,"

轉換成token后:

torch.Size([1, 6])
tensor([[ 5812, 38201,    11,   810,   318,    13]])

只取“,”對應輸出的概率分布,模型預測“,”的下一個詞是 "and",將“and”加到輸入后面作為下一輪輸入。

以此類推,直到生成50個詞。

Oh Juliet, where is, and, and,, and,,,, and, and,,,,,,,,, and,,,, and,,,, and,, and,,,,, and,,,,,, and


本文轉載自公眾號人工智能大講堂 

原文鏈接:????https://mp.weixin.qq.com/s/YZU9rPPyYZTbSPhCZtbGZg??


?著作權歸作者所有,如需轉載,請注明出處,否則將追究法律責任
標簽
收藏
回復
舉報
回復
相關推薦
精品久久久久久久免费人妻| 国产在线精品一区二区三区| 熟女av一区二区| 精品一区二区三区视频在线播放| 一区二区在线看| 久久超碰亚洲| 国产精品毛片一区视频播| 欧美欧美天天天天操| 亚洲老司机av| 日韩不卡的av| 美脚恋feet久草欧美| 国产精品美女一区二区三区| 国产精品久久亚洲7777| 日本成人一级片| 激情另类综合| 色噜噜狠狠狠综合曰曰曰 | 国产凹凸在线观看一区二区| 日本高清+成人网在线观看| 日本福利片在线观看| 国产探花一区| 亚洲国产精品热久久| 午夜国产福利在线观看| 最近在线中文字幕| 夜夜嗨av一区二区三区| 亚洲精品视频一区二区三区| 无码精品在线观看| 国产精品正在播放| 国产精品久久久久久久久久三级| 久草视频在线免费看| 久久性感美女视频| 亚洲男人天堂网站| 一级黄色片毛片| 激情久久免费视频| 欧美日韩一二三区| 免费黄色日本网站| zzzwww在线看片免费| 亚洲免费在线电影| 亚洲AV无码成人精品一区| 国产高清在线观看| 国产欧美一区二区精品性| 99久久一区三区四区免费| 在线免费看毛片| 日本不卡一区二区三区高清视频| 91av免费观看91av精品在线| 免费人成视频在线| 欧美国产专区| 久久av中文字幕| 大地资源高清在线视频观看| 色综合五月天| 久久久国产精品x99av | 亚洲午夜成aⅴ人片| 久久综合亚洲精品| 最爽无遮挡行房视频在线| 综合色中文字幕| 三年中国中文在线观看免费播放 | 欧美高清不卡在线| 久久综合激情网| 欧美日韩国产一区精品一区| 蜜臀久久99精品久久久久久宅男| 全网免费在线播放视频入口| 欧美精品导航| 97久久超碰福利国产精品…| 日韩精品在线不卡| 香蕉视频成人在线观看| 国产成人av网址| 91麻豆精品在线| 久久精品99久久久| 91在线观看免费网站| 亚洲av色香蕉一区二区三区| av中文字幕亚洲| 免费在线观看91| 成人欧美亚洲| 亚洲同性同志一二三专区| 六月婷婷激情网| 草莓视频丝瓜在线观看丝瓜18| 亚洲成人久久影院| 日本熟妇人妻xxxxx| 久久精品97| 日韩精品一区二区三区视频在线观看| 无码人妻精品一区二区三| 欧美亚洲国产日韩| 中文字幕日韩欧美| 青青草成人免费| 国产亚洲在线观看| 国产中文字幕日韩| 刘亦菲毛片一区二区三区| 久久综合成人精品亚洲另类欧美 | 亚洲东热激情| 欧美做爰性生交视频| 中文字幕免费在线看| 国产激情视频一区二区在线观看| 久久99精品国产99久久| 91在线高清| 亚洲国产另类av| 成人动漫av在线| 国产成人极品视频| a级片免费视频| 99久久99久久精品免费看蜜桃| 日本在线视频不卡| 香蕉久久aⅴ一区二区三区| 精品国产91久久久久久| 亚洲怡红院在线| 红杏一区二区三区| 日韩天堂在线视频| 中文字幕亚洲精品在线| 精品一区在线看| 久久免费一区| 91麻豆免费在线视频| 色综合中文字幕| 国产av一区二区三区传媒| 国产一区网站| 91chinesevideo永久地址| 91theporn国产在线观看| 99精品在线免费| 免费的av在线| 欧美日韩激情电影| 亚洲韩国欧洲国产日产av| 欧美日韩色视频| 日韩av中文在线观看| 狠狠色伊人亚洲综合网站色| 成人无遮挡免费网站视频在线观看| 色婷婷综合五月| 国产黑丝在线观看| 欧美精品偷拍| 国产日韩中文字幕在线| 黄色大片在线看| 五月天中文字幕一区二区| 中文字幕乱妇无码av在线| 成人一区而且| 国产成一区二区| 日本视频在线观看一区二区三区| 亚洲午夜精品久久久久久久久| av中文字幕网址| 日韩夫妻性生活xx| 国产精品黄色影片导航在线观看| 色丁香婷婷综合久久| 亚洲国产综合色| 中文字幕在线播放一区二区| 婷婷色综合网| 成人做爽爽免费视频| 欧美成人性生活视频| 欧美私模裸体表演在线观看| 欧美人与性囗牲恔配| 性色av一区二区怡红| 久久av一区二区三区亚洲| а√天堂中文在线资源8| 欧美变态口味重另类| 国产九色91| 亚洲最大成人av| 中文字幕高清不卡| 色噜噜狠狠一区二区| 第九色区aⅴ天堂久久香| 国产精品福利在线| 午夜在线观看视频| 欧美日韩国产美| av激情在线观看| 国产成人综合网| 成人一级生活片| 麻豆一区二区| 国产成人精品av| 9色在线视频| 制服丝袜亚洲精品中文字幕| 99久久婷婷国产综合| 国产一区二区三区av电影| 欧美在线观看黄| 久久365资源| 日本中文字幕不卡免费| 在线观看二区| 欧美一区二区精品| 伊人久久综合视频| 久久精品视频一区二区三区| 小泽玛利亚视频在线观看| 中文字幕免费精品| 国产一区再线| www.久久| 久久97精品久久久久久久不卡 | 欧美大片免费高清观看| 中文字幕日韩在线观看| 国产高清第一页| 欧美日韩另类在线| 日韩精品电影一区二区三区| 国产一区二区导航在线播放| 久久久久久久久久久99| 日韩国产欧美| 国产精品三区www17con| 日韩精品三区| 欧美激情视频三区| 国产在线一二三| 日韩一区二区三区在线| 日韩 欧美 中文| 亚洲欧洲一区二区三区| 亚洲视频在线播放免费| 久久国产生活片100| 国产欧美日韩网站| 久久亚洲成人| 久久伦理网站| 7m精品国产导航在线| 国产精品福利在线观看| 丁香花在线影院| 色黄久久久久久| 青青草视频在线免费观看| 欧美一区二区视频免费观看| 欧美精品久久久久久久久久| 性做久久久久久久久久| 91福利国产精品| 久久久一二三区| 国产精品另类一区| 国产美女喷水视频| 国产a视频精品免费观看| 亚洲福利精品视频| 日韩午夜激情| 可以在线看黄的网站| 成人一区而且| 久久久久一区二区三区| 中文字幕一区二区三区四区久久| 国产精品久久久久久av福利软件 | 欧洲成人免费aa| 天堂av在线电影| 日韩在线观看av| 国产在线观看黄| 亚洲精品美女在线观看播放| 国内毛片毛片毛片毛片| 欧美三级电影一区| 国产真人无遮挡作爱免费视频| 午夜欧美2019年伦理| 午夜69成人做爰视频| 国产精品国产成人国产三级| 91l九色lporny| 久久精品亚洲一区二区三区浴池 | 国模视频一区二区| 大地资源网3页在线观看| 色黄久久久久久| 中文字幕在线免费| 色偷偷偷综合中文字幕;dd| 精品影院一区| 一区二区欧美激情| 久久99久久| 亚洲三级黄色在线观看| 久久免费看视频| 国产亚洲精品成人av久久ww| 国产原创av在线| 亚洲欧洲偷拍精品| 搞黄视频在线观看| 国产午夜一区二区| 高清av电影在线观看| 在线a欧美视频| 草草影院在线观看| 亚洲人成网站色ww在线| 男操女在线观看| 亚洲天堂网在线观看| 在线观看美女网站大全免费| 日韩色av导航| 伊人影院在线视频| 久久久女女女女999久久| av老司机在线观看| 欧美亚洲在线播放| av电影一区| 欧美一个色资源| 国产成人精品视频免费看| 亚洲天天影视网| 男人天堂新网址| 亚洲三级色网| 乱子伦视频在线看| 免费在线看一区| 日韩不卡的av| 97se亚洲国产综合自在线观| 精品人妻一区二区三区香蕉| 国产亚洲一本大道中文在线| 黄色av片三级三级三级免费看| 1024成人网| 国产精品第九页| 91黄色激情网站| 国产精品探花视频| 日韩av在线天堂网| bbbbbbbbbbb在线视频| 欧美成人黑人xx视频免费观看| 欧美xxxx少妇| 国产精品www色诱视频| 国产精品免费精品自在线观看| 国产欧美日韩亚洲| 人人狠狠综合久久亚洲婷婷 | 亚洲午夜黄色| 北条麻妃av高潮尖叫在线观看| 国产一区视频在线看| 亚洲第九十七页| 中文字幕亚洲精品在线观看| 日韩欧美亚洲国产| 欧美视频一区二区三区在线观看 | 成人国产在线| 成人黄色在线免费观看| 国产一区二区欧美| 一级特黄妇女高潮| 免费亚洲视频| 国产免费a级片| 国产精品色哟哟网站| 亚洲精品77777| 欧美一区二区三区四区高清| 你懂得网站在线| 欧美剧在线观看| 精品三级在线| 欧美高清视频一区二区三区在线观看 | av成人福利| 成人网在线免费看| 国产成人黄色| 免费人成在线观看视频播放| 蜜桃91丨九色丨蝌蚪91桃色| 国产成人精品无码片区在线| 国产精品的网站| 黄色av网站免费观看| 精品国产a毛片| 亚洲免费视频一区二区三区| 992tv在线成人免费观看| 国产精品国产亚洲精品| 日本精品一区| 99亚洲精品| 免费观看污网站| 亚洲视频资源在线| 中文字幕你懂的| 亚洲视频在线播放| 久草在线资源福利站| 亚洲国产综合91精品麻豆| 午夜免费高清视频| 99久久免费国产| 久久免费视频6| 9191精品国产综合久久久久久| 国产在线91| 国产福利视频一区二区| 一区二区美女| 看av免费毛片手机播放| 99久久99久久免费精品蜜臀| 国产在线视频卡一卡二| 欧美不卡在线视频| 色网在线观看| 成人免费看片网站| 欧美二区不卡| 日本精品一二三| 亚洲伊人伊色伊影伊综合网| 99er热精品视频| 欧美成人精品一区| 91精品丝袜国产高跟在线| 成年人视频大全| 高清视频一区二区| 国产在线一二区| 亚洲精品国产电影| 2019国产精品| 亚洲天堂一区在线| 亚洲精品动漫100p| 九色porny丨入口在线| 精品久久久久久综合日本| 亚洲视频播放| 日本黄色特级片| 91成人免费电影| 69视频在线观看| 91久久国产综合久久91精品网站| 婷婷综合亚洲| 永久免费看片在线观看| 亚洲高清免费一级二级三级| 成人午夜精品福利免费| 国产69精品久久久久9| 亚洲三级精品| 久久婷五月综合| 亚洲人成亚洲人成在线观看图片| 精品欧美在线观看| 高清欧美性猛交xxxx| 国产精品片aa在线观看| 性chinese极品按摩| 亚洲欧美日韩国产一区二区三区| 精品人妻一区二区三区含羞草| 久久全球大尺度高清视频| 国产欧美日韩精品一区二区三区 | 奇米精品一区二区三区在线观看一 | 亚洲图片小说视频| 欧美国产日产韩国视频| 四虎884aa成人精品最新| 99热这里只有精品在线播放| ●精品国产综合乱码久久久久| 亚洲AV无码乱码国产精品牛牛| 欧美一级高清免费| 天天操综合网| 丰满少妇一区二区三区| 欧美日本国产视频| 操人在线观看| 亚洲一区二区三区乱码 | 国产成人av一区二区三区| 亚洲一区成人| 欧美另类videoxo高潮| 国产一区二区三区在线观看免费视频 | 日本精品免费在线观看| 中文字幕免费不卡| 国产极品999| 国产精品第一页在线| 国产精品99一区二区| 韩国三级hd中文字幕| 亚洲成人亚洲激情| 懂色aⅴ精品一区二区三区| 国产无限制自拍| 国产精品久久久久久久久久久免费看 | 国产精品黄色片| 日韩精品一区在线视频| 国产精品久久久久久久久免费相片 |