精品欧美一区二区三区在线观看 _久久久久国色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??


?著作權歸作者所有,如需轉載,請注明出處,否則將追究法律責任
標簽
收藏
回復
舉報
回復
相關推薦
污污在线观看| 99精品人妻国产毛片| 亚洲久草在线| 亚洲视频一区二区免费在线观看| 亚洲影视中文字幕| 久久久久国产精品夜夜夜夜夜| 一区二区三区在线资源| 欧美日韩一区二区在线| 日韩欧美国产二区| 国产免费视频一区二区三区| 99av国产精品欲麻豆| 亚洲色图第三页| 日本77777| 男人久久天堂| 亚洲国产电影在线观看| 91网站在线看| 无码视频在线观看| 欧美私人啪啪vps| 亚洲人av在线影院| 日本黄色www| 偷拍精品精品一区二区三区| 亚洲精选视频免费看| 精品日韩美女| 国产成人久久精品77777综合 | 日本三级片在线观看| 精品欧美久久| 亚洲精品动漫100p| 日韩成人精品视频在线观看| 亚洲天堂av影院| 亚洲激情网站免费观看| 日韩一区二区三区高清| 天堂在线视频网站| 国产乱码精品一区二区三区忘忧草 | 糖心vlog免费在线观看| 偷拍自拍一区| 337p日本欧洲亚洲大胆色噜噜| 亚洲综合欧美在线| 成人福利av| 亚洲成av人片www| 在线观看18视频网站| 成年网站在线| 久久综合久久99| 亚洲综合在线中文字幕| 岳乳丰满一区二区三区| 可以免费看不卡的av网站| 久久免费国产精品1| 久久中文免费视频| 97色伦图片97综合影院| 国产亚洲人成网站在线观看| 野花社区视频在线观看| 国产精品45p| 欧美不卡一区二区三区| 少妇丰满尤物大尺度写真| 一区在线不卡| 777久久久精品| 中文字幕 日韩 欧美| 国产成人精品一区二区三区视频| 都市激情亚洲色图| 日韩欧美亚洲天堂| 一个人www视频在线免费观看| 亚洲福利视频导航| 欧美一级视频在线播放| 超碰91在线观看| 亚洲电影一区二区| 国产毛片视频网站| yellow在线观看网址| 亚洲成av人**亚洲成av**| 日本午夜激情视频| 国产粉嫩在线观看| 一本到不卡免费一区二区| 色诱视频在线观看| a屁视频一区二区三区四区| 在线观看亚洲精品视频| 在线观看的毛片| 日韩在线你懂得| 91精品久久久久久久91蜜桃| 色偷偷中文字幕| 91精品啪在线观看国产爱臀| 亚洲成人1234| 中国毛片在线观看| 日韩成人三级| 色中色综合影院手机版在线观看| 日本熟妇毛耸耸xxxxxx| 久久精品官网| 成人免费视频a| 亚洲欧美强伦一区二区| 91蜜桃在线观看| 午夜精品一区二区三区在线观看| 久久77777| 天天综合网 天天综合色| 日本黄网站免费| 日韩黄色三级| 亚洲国产天堂网精品网站| 91网站免费入口| 中文在线播放一区二区 | 日韩污视频在线观看| 久久久蜜桃一区二区人| 成人有码视频在线播放| 神马午夜电影一区二区三区在线观看| 久久久高清一区二区三区| 爱爱爱视频网站| 理论不卡电影大全神| 欧美午夜精品久久久久久孕妇| 国产亚洲色婷婷久久| 欧美亚洲国产日韩| 久久激情视频久久| 男人的天堂一区二区| 免费一级片91| 国产日韩欧美精品| 婷婷成人激情| 欧美视频在线免费| 欧美污在线观看| 岳的好大精品一区二区三区| 欧美成人免费在线观看| 无码人妻丰满熟妇精品| 成人性生交大片免费看视频在线| 天天综合狠狠精品| 欧亚在线中文字幕免费| 欧美一区二区视频在线观看| 欧美老熟妇乱大交xxxxx | 欧美激情精品久久久久久蜜臀| 亚洲乱码国产乱码精品| 国产成人免费视| 亚洲区一区二区三区| 欧美男男激情videos| 日韩欧美国产系列| 日韩一级片在线免费观看| 99综合视频| 97人人香蕉| 精品国产丝袜高跟鞋| 色成人在线视频| 国产午夜在线一区二区三区| 亚洲精品久久| 国产三级精品网站| 国产区在线视频| 欧美日韩综合视频| 日本不卡视频一区| 欧美午夜精品| 97视频中文字幕| 精精国产xxxx视频在线| 欧美日韩你懂的| 熟女少妇内射日韩亚洲| 国产一区91| 国产一区福利视频| 欧美男男video| 日韩你懂的在线观看| 欧美被狂躁喷白浆精品| 国产在线视频精品一区| 国产精品h视频| 日本在线一区二区| 久久精品小视频| 91午夜交换视频| 综合久久久久久| 五月六月丁香婷婷| 亚洲精品电影| 成人av网站观看| 成人免费高清观看| 亚洲国产成人精品一区二区| 亚洲国产综合久久| 337p粉嫩大胆色噜噜噜噜亚洲| 国产原创中文在线观看| 日韩深夜影院| 日韩av日韩在线观看| 免费毛片在线| 欧美视频在线播放| 女性裸体视频网站| 国产一区二区女| 国产爆乳无码一区二区麻豆| 国产精品成人观看视频国产奇米| 粉嫩av在线播放| 欧美日韩视频不卡| 国产少妇在线观看| 成人av网站在线观看| 午夜精品久久久久久久无码| 亚洲最大在线| 国产日韩精品在线播放| av网站在线免费看推荐| 亚洲国产小视频在线观看| 无码人妻久久一区二区三区| 国产精品久久久99| 亚洲视频天天射| 亚洲伊人观看| 亚洲一区美女| 亚洲精品一区在线| 日本一欧美一欧美一亚洲视频| 在线免费看黄| 日韩精品一区二区三区视频在线观看 | 一级特黄大欧美久久久| 波多野结衣先锋影音| 青青草国产成人av片免费| 欧洲精品视频在线| 台湾亚洲精品一区二区tv| 国产精品免费在线免费| 欧美人动性xxxxz0oz| 国产一区二区三区久久精品| 99国产精品久久久久久久成人 | 欧美高清videos高潮hd| 色资源在线观看| 337p亚洲精品色噜噜狠狠| 日韩欧美中文字幕一区二区| 国产精品理论片在线观看| www.四虎精品| 美女视频免费一区| 国产免费黄色小视频| 久久密一区二区三区| 国产一区二区在线网站| 日韩成人在线一区| 欧美一区二区影院| 色屁屁www国产馆在线观看| 亚洲人成在线观看网站高清| 丰满少妇在线观看bd| 欧美日韩中文一区| 在线观看免费国产视频| 亚洲三级在线看| 亚洲av成人无码久久精品| 丁香亚洲综合激情啪啪综合| 日本黄大片一区二区三区| 亚洲巨乳在线| 日韩一级片一区二区| 久久精品播放| 日本10禁啪啪无遮挡免费一区二区| 99a精品视频在线观看| 国产日韩换脸av一区在线观看| 亚洲私拍视频| 韩日欧美一区二区| 中文字幕免费高清电视剧网站在线观看| 亚洲欧美国产精品| 色婷婷av一区二区三区之e本道| 欧美一区二区三区在线看| 999视频在线| 精品久久久久久国产91| 国产亚洲精品码| 亚洲麻豆国产自偷在线| 国产免费嫩草影院| 国产视频视频一区| 免费a级黄色片| 99久久婷婷国产综合精品| 中文字幕无人区二| 国产传媒一区在线| 永久看看免费大片| 国产福利精品导航| 青青草原播放器| 精一区二区三区| 成人亚洲免费视频| 另类成人小视频在线| 亚洲精品高清无码视频| 日日欢夜夜爽一区| 国产一级不卡毛片| 日韩**一区毛片| 午夜激情福利在线| 免费观看30秒视频久久| 超碰在线播放91| 美腿丝袜在线亚洲一区| 欧美成人福利在线观看| 韩国一区二区在线观看| 欧美一级特黄aaa| 国产综合色视频| 欧美激情第四页| 成人少妇影院yyyy| av网页在线观看| 久久婷婷国产综合国色天香| www.久久av| 欧美国产丝袜视频| 精品少妇一区二区三区密爱| 中文字幕中文字幕一区二区| 国语对白在线播放| 又紧又大又爽精品一区二区| 久久久精品人妻一区二区三区四| 亚洲国产sm捆绑调教视频| 一级免费在线观看| 一本色道久久加勒比精品| 超碰在线97观看| 4438成人网| 国产三级第一页| 亚洲白虎美女被爆操| 日本天堂影院在线视频| 伊人亚洲福利一区二区三区| 黄在线免费看| 91精品国产乱码久久久久久蜜臀| 大胆人体一区二区| 成人国产精品免费视频| 91精品入口| 欧美日韩另类丝袜其他| 天天射—综合中文网| 国产乱淫av片杨贵妃| 蘑菇福利视频一区播放| 欧美美女性视频| 不卡免费追剧大全电视剧网站| 久久精品国产亚洲av久| 日韩美女久久久| www.日本精品| 欧美日韩国产a| 色哟哟国产精品色哟哟| 在线日韩精品视频| 狂野欧美性猛交xxxxx视频| 日本高清视频精品| 日本少妇精品亚洲第一区| 麻豆av一区| 9191国语精品高清在线| 欧美一区二区三区爽大粗免费| 免费成人在线观看视频| 欧美图片自拍偷拍| 国产精品亲子乱子伦xxxx裸| 久久精品视频国产| 欧美午夜视频网站| 手机看片一区二区| 久久久国产精品亚洲一区| 亚洲第一av| 99影视tv| 亚洲成人精品| 色诱视频在线观看| 9久草视频在线视频精品| 一区二区三区在线播放视频| 黑人巨大精品欧美一区二区三区 | 久久精品在线视频| 蜜桃视频在线网站| 999精品视频一区二区三区| 国产精品一区二区99| av免费看网址| 国产精品一卡二| 国产尤物在线播放| 欧美在线观看一区二区| 日本啊v在线| 26uuu国产精品视频| 96sao在线精品免费视频| 中文字幕一区二区三区5566| 日本不卡一区二区三区| 国产精品jizz| 香蕉加勒比综合久久| 亚洲国产日韩在线观看| 欧美乱大交做爰xxxⅹ性3| 国产精品亚洲欧美日韩一区在线| 色综合久久久久久久久五月| 另类天堂av| 亚洲成人网在线播放| 午夜精品久久久久久久久久| 成人午夜免费在线观看| 九九热这里只有精品免费看| 精品一区二区三区在线观看视频| 亚洲精品在线免费| 青青草97国产精品免费观看无弹窗版| 特级西西人体wwwww| 精品久久久久久久久久国产 | 欧美美女操人视频| 国语精品视频| 国产激情在线看| 粉嫩一区二区三区在线看| 青青草在线观看视频| 日韩一级免费一区| 一色桃子av在线| 高清视频一区| 亚洲看片一区| 30一40一50老女人毛片| 在线视频综合导航| sese一区| 亚洲一区二区三区毛片 | 男女午夜激情视频| 91色在线porny| 亚洲欧美综合另类| 亚洲精品福利视频| 欧洲亚洲两性| 91pron在线| 亚洲午夜极品| 97人妻精品一区二区三区免费| 午夜精品久久久久| 天天干天天爽天天操| 日本视频久久久| 精品无人区麻豆乱码久久久| 亚洲综合欧美在线| 亚洲欧美经典视频| 少妇高潮久久久| 91a在线视频| 西野翔中文久久精品字幕| 欧美少妇性生活视频| 国产精品初高中害羞小美女文| 99精品视频免费看| 久久久久国产一区二区三区| 亚洲精品小区久久久久久| 国产一线二线三线在线观看| 亚洲欧美日韩精品久久久久| 精品毛片一区二区三区| 欧美日韩成人在线观看| 天美av一区二区三区久久| 免费激情视频在线观看| 亚洲视频小说图片| 狠狠躁日日躁夜夜躁av| 国产成人小视频在线观看| 久久看人人摘| 欧美在线一级片| 欧美影院一区二区| 成人av影院在线观看| 欧美另类视频在线| 国产精品小仙女| 99热国产在线观看| 国产一区二区三区日韩欧美| 精品国产一区二区三区2021| 国产一区二区片| 国产精品午夜久久| 精品人妻少妇嫩草av无码专区| 国产精品69久久|