日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Pytorch快速入门笔记

發布時間:2023/12/4 编程问答 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Pytorch快速入门笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Pytorch 入門筆記

  • 1. Pytorch下載與安裝
  • 2. Pytorch的使用教程
    • 2.1 Pytorch設計理念及其基本操作
    • 2.2 使用torch.nn搭建神經網絡
    • 2.3 創建屬于自己的Dataset和DataLoader
      • 2.3.1 編寫Dataset類
      • 2.3.2 編寫Transform類
      • 2.3.3 將Transform融合到Dataset中去
      • 2.3.4 編寫DataLoader類
    • 2.4 使用Tensorboard來可視化訓練過程
      • 2.4.1 Images可視化
      • 2.4.2 Graph可視化
      • 2.4.3 訓練中的Loss,Accuracy可視化
    • 2.5 Pytorch實現DQN算法
    • 2.6 Pytorch實現Policy Gradient算法

1. Pytorch下載與安裝

在Pytorch官網進行官方提供的下載方法:Pytorch官網,打開官網后選擇對應的操作系統和cuda版本。如果需要安裝GPU版本的Pytorch則需要下載對應CUDA版本的Torch版本,例如我裝的是CUDA 10.1,Python版本是3.7。

查看已安裝CUDA、cuDNN版本的方法:
Windows:
訪問文件夾 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA 看該目錄下文件夾名稱,如果是v10.0則代表10.0版本的CUDA;
訪問C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\include\cudnn.h來查看cuDNN的版本。
Linux:
使用命令 cat /usr/local/cuda/version.txt 查看cuda版本;
使用命令 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 查看cuDNN版本。

如果沒有安裝cuda和cuDNN,則需要先安裝這兩樣東西,再裝Pytorch。先安裝cuda,進入cuda官網,根據自己的操作系統選擇對應版本(version 一欄代表的是你Windows的操作系統版本):

下載exe(local)安裝程序,之后一路選擇默認安裝路徑就好了。安裝完成后再cmd窗口內輸入:nvcc -V,若看到以下信息證明cuda安裝成功:

cuda安裝好了之后下載對應的cnDNN,進入cuDNN安裝鏈接,這里需要先注冊一個Nvidia的賬號并登錄才能進行下一步,登錄后看到以下網址:

根據安裝的cuda版本選擇對應的cuDNN的版本即可,比如我們演示的是cuda 10.2版本,這里就選擇對應的7.6.5版本的cuDNN就好。下載好后是個壓縮文件,解壓后得到3個文件夾:bin,include,x64,將這三個文件下的內容分別拷貝到安裝的cuda目錄對應的bin,include,x64文件夾下即可,cuda的默認安裝路徑如下:

完成了cuda和cuDNN的安裝后,我們就可以回到Pytorch的安裝中去了,看文章最開始的那張圖,找到對應的版本后,復制圖片最下方的pip命令在cmd窗口執行即可開始進行安裝:
pip install torch===1.3.1 torchvision===0.4.2 -f https://download.pytorch.org/whl/torch_stable.html -i https://pypi.tuna.tsinghua.edu.cn/simple
這里使用了清華源來加速下載,等待一段時間后,Pytorch就安裝完畢了。

2. Pytorch的使用教程

2.1 Pytorch設計理念及其基本操作

Pytorch在設計的時候最大的亮點是在于,其可以替代Numpy庫,numpy庫中的array運算是依賴于將數組編譯成機器碼再到CPU運行進行加速的。而Torch是將array(在Pytorch里面叫Tensor)變成GPU上的變量進行運算的,因此在進行大規模運算是Torch能更快速。

  • Tensor的運算

Tensor的定義和運算都和np.array()差不多,很多操作都是一樣的,下面是一些基本操作的介紹:

import torch""" 創建 Tensor 的方法 """ x = torch.tensor([3, 3]) # 創建一個值為[3, 3]的Tensor x = torch.empty(5, 3) # 5x3空矩陣 x = torch.ones(3, 3) # 值全為1的3x3矩陣 y = torch.randn_like(x, dtype=torch.long) # 創建一個和x一樣shape的隨機矩陣,并將每一個數字dtype變成long""" Tensor 的屬性 """ print(x.size()) # 相當于np.array中的.shape屬性,x.size()本質是一個tuple,可以使用tuple的全部屬性""" Resize Tensor """ x = torch.tensor([[1, 2], [3, 4]]) y = x.view(-1, 4) # y = ([1, 2, 3, 4]), 和np.reshape()是一樣的""" Tensor 和 Numpy 的互相轉換 """ x_np = np.arrary([1, 1]) x_tensor = torch.from_numpy(x_np) # 從numpy轉換到tensor x_np_2 = x_tensor.numpy() # 從tensor轉換到numpy""" CPU 到 GPU 的轉換 """ if torch.cuda.is_available():device = torch.device("cuda")x = torch.ones(5, 3)x_dev = x.to(device) # CPU tensor 轉 GPU tensorx_host= x_dev.to("cpu", torch.doule) # GPU tensor 轉 CPU tensor
  • 自動求導機制

torch.Tensor這個對象擁有自動求導的功能,每個Tensor在被計算的時候都會自動記錄這些運算過程,在最后計算梯度的時候就能夠追蹤的這些計算過程快速的進行梯度計算。如果要打開計算追蹤這個功能需要將Tensor.requires_grad這個屬性設置為True(默認是False的),當該追蹤計算功能打開后,只需要調用.backward()就能夠進行自動的梯度求導了。
在我們訓練的時候通常會為Tenosor打開.requires_grad屬性,但在訓練好一個模型后,我們在使用這個模型的時候,這時候我們就不需要計算梯度了(因為不會再做反向傳播了),這時候我們就可以關閉.requires_grad這個屬性來減少運算時間。使用with torch.no_grad():來關閉Tensor的計算追蹤功能:

with torch.no_grad(): # 整個過程中x, y, z三個tensor均不會開啟計算追蹤torch.add(x, y, out=z) # 將tensor x和tensor y相加后放入到tensor z中

2.2 使用torch.nn搭建神經網絡

在Pytorch中,搭建一個神經網絡需要自己寫一個類,該類需要繼承自torch.nn.Module類,nn.Module主要包含以下兩個特點:

  • nn.Module類中已經預先定義好了多種類型的Layer(例如:Conv2D,Linear等),使用時直接調用即可。
  • nn.Module中包含一個forward(input)方法,該方法接收一個input輸入,并return一個output值,通常這個forward()方法都需要用戶自己來實現,構建從輸入到輸出的網絡結構。
  • 通常訓練一個網絡模型的流程如下:

  • 首先定義所有的網絡架構,模型一共有幾層,每一層的類型是什么(Convolution?Linear?),可學習的參數有哪些(weight + bias)。
  • 遍歷所有的數據集,輸入一個 input,通過 forward() 過程計算得到一個 output。
  • 比較 output 和 label 的值,計算出 loss。
  • 根據梯度和 Loss 計算網絡模型中所有可學習參數的梯度信息。
  • 進行參數更新, 更新公式通常為:weight = weight - learning_rate * gradient
  • 我們使用 torch.nn 來搭建一個卷積神經網絡用于識別 32*32 的單通道灰度圖。首先,我們先來理一下整個網絡的結構:

    1. Convolutional2D Shape Change
    IwI_wIw?代表輸入圖層的width,OwO_wOw?代表經過Conv2D層后圖層的width,FwF_wFw?代表卷積核Filter的width,SwS_wSw?代表在width上橫跨的步長,PwP_wPw?代表在width方向上的padding值,則有以下公式(Height方向上同理):Ow=Iw?Fw+2PwSw+1O_w=\frac{I_w - F_w + 2P_w}{S_w}+1Ow?=Sw?Iw??Fw?+2Pw??+1
    2. Maxpooling Shape Change
    IwI_wIw?代表輸入圖層的width,OwO_wOw?代表經過Maxpooling2D層后圖層的width,MwM_wMw?代表池化層的width,SwS_wSw?代表在width上橫跨的步長(通常是等于池化層的寬度的),則有以下公式(Height方向上同理):Ow=Iw?MwSw+1O_w=\frac{I_w - M_w}{S_w}+1Ow?=Sw?Iw??Mw??+1

    因此,整個網絡架構如下:

    根據上述網絡設計編寫代碼,搭建一個神經網絡需要編寫:

  • 自己寫一個類 net(名字自取)繼承自torch.nn.Module()
  • 利用 torch.nn 中的 Layers 構建自己的Layer
  • 實現 net 類中的 forward() 方法
  • 以下是代碼實現:

    import torch import torch.nn as nn import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.Conv1 = nn.Conv2d(1, 6, 3) # params (input_channal_num, out_channal_num, filter_size)self.Conv2 = nn.Conv2d(6, 16, 3)self.fc1 = nn.Linear(6*6*16, 120)self.fc2 = nn.Linear(120, 80)self.fc3 = nn.Linear(80, 10) # predict 10 classesdef forward(self, x):x = F.max_pool2d(F.relu(self.Conv1(x)), 2)x = F.max_pool2d(F.relu(self.Conv2(x)), 2)x = x.view(-1, self.get_flatten_num(x))x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = F.relu(self.fc3(x))return xdef get_flatten_num(x):features_num = x[1:] # drop out the batch size (which is x[0])flatten_num = 1for feature_num in features_num:flatten_num *= feature_numreturn flatten_numnet = Net() print(net)

    這樣我們就定義好了一個神經網絡,打印一下這個神經網絡可以看到:

    這樣一來,整個前向傳播就寫完了,現在我們來寫反向傳播的過程,首先我們需要計算 Loss 再反向梯度傳播,下圖是一個簡單的反向傳播示意圖:

    Input 層有兩個神經元,hidden layer 也有兩個神經元,最終 output 層有兩個神經元。target 代表 Label 數據。假設我們網絡內不存再bias,只有 weight 是需要學習的,那么我們需要根據 target 和神經網絡的 output 之間的誤差值(Loss)來修改 weight 值,以達到學習的效果。在這里我們以w5w_5w5?這個學習參數為例,通過反向鏈式求導來計算它的梯度:Loss 是指兩個 output 值與 target 之間的 MSE Error。對w5w_5w5?進行反向求導,由于從h1h_1h1?o1o1o1是先經過LinearLinearLinear層(FC層)再經過一個sigmoidsigmoidsigmoid層,再輸出到o1o_1o1?的。因此從o1o_1o1?h1h_1h1?的反向求導需要先對sigmoidsigmoidsigmoid這個計算過程求導,再對LinearLinearLinear計算過程求導,這就是為什么上圖中會先對Os1Os_1Os1?求導,再對O1O_1O1?求導。

    我們計算隨機生成一個 target 讓其與我們的 output 進行 Loss 計算:

    net.zero_grad() # 先清空網絡中的梯度變量中的值target = torch.rand(1, 10) # 隨機生成target標簽 criterion = nn.MSELoss() loss = criterion(target, out) # 計算loss值 loss.backward() # 反向求導函數

    這時,我們已經計算出所有的梯度了,現在需要我們去 update 這些 weights 的值,最簡單的更新的方式是使用 SGD 的方法,即 weight=weight?gradient?learningrateweight = weight - gradient * learning_rateweight=weight?gradient?learningr?ate,除此之外還有 Adam,RMSProp等等,Pytorch 將這些更新的算法稱作“優化器”(optimizer)封裝在了函數里,我們只需要調用就可以了:

    import torch.optim as optimoptimizer = optim.SGD(net.parameters(), lr=0.01)# 每一輪訓練的時候 optimizer.zero_grad() # 一定要記得清空優化器中的上一輪梯度信息 output = net(input) loss = criterion(output, target) loss.backward() optimizer.step() # 更新梯度信息

    至此我們已經完成了 Pytorch 的安裝并使用 torch 搭建了一個前向神經網絡以及反向傳播的全過程。

    2.3 創建屬于自己的Dataset和DataLoader

    現在我們已經成功搭建了一個CNN網絡了,問題是我們的CNN網絡只能接收數字類型的 Input,而我們往往數據集都是圖片類型的,因此為了將圖片數據轉換為神經網絡能夠接收的數字類型,我們需要寫 Dataset 類和 DataLoader 類,這兩個類的作用分別為:

    Dataset 類的作用
    Dataset 用于讀取對應路徑的數據集(比如說某個特定文件夾下的圖片)并轉換成神經網絡能夠接收的數據類型,舉例來說,我們要用人臉識別數據集做訓練,那么我們就需要編寫一個 FaceDataset() 來把人臉的圖片轉換向量數據。

    DataLoader 類的作用
    往往在我們訓練神經網絡時都會使用到 mini-batch 的方法,一次讀取多個樣本而不是只讀一個樣本,那么如何從數據集中去采樣一個batch的數據樣本就是 DataLoader 要實現的功能了。

    我們使用 Face Landmark 的數據集來舉例,下載一個 Landmark 數據集,這個數據集中包含【人臉圖片】和【Landmark】兩個數據,Landmark 是指人臉上的68個特征點(不清楚的可以查查dlib這個庫)。如下圖所示:

    Landmark 就是指紅色的68個點,在數據集中存放的是這68個點的(x,y)坐標。

    2.3.1 編寫Dataset類

    我們現在來寫一個 FaceLandmarksDataset() 類用于讀取數據中的【人臉圖片】和【Landmark】信息,對于 FaceLandmarksDataset() 這個類我們繼承自 torch 的 Dataset 類,并且實現三個方法:__init__, __len__, __getitem__。

    • __len__ 方法用于返回這個 Dataset 的數據集長度共有多少個。
    • __getitem__ 可以讓你在調用 dataset[i] 的時候直接調用這個方法,非常方便。
    import os import torch import pandas as pd from skimage import io, transform import numpy as np import matplotlib.pyplot as plt from torch.utils.data import Dataset, DataLoader from torchvision import transforms, utils# Create a Dataset Class to transform the image data to array dataclass FaceLandmarksDataset(Dataset):def __init__(self, root, transform=None):self.root = root # root 是指你存放數據集的路徑self.transform = transform # transform 是數據預處理的方法,后面會講,這里先跳過data = pd.read_csv(os.path.join(root, "face_landmarks.csv"))self.image_names = data.iloc[:, 0].as_matrix() # 數據集的第一列是圖片的名字self.land_marks = data.iloc[:, 1:].as_matrix() # 數據集的后面列全是landmarksdef __len__(self):return len(self.image_names)def __getitem__(self, idx):assert isinstance(idx, int), "Idx must be int."land_marks = self.land_marks[idx, :].reshape(-1, 2)image_name = self.image_names[idx]image = io.imread(os.path.join(self.root, image_name))sample = {"image": image, "landmarks": land_marks} # 定義sample的數據結構是個字典{人臉圖片(np.array),landmarks(np.array)}if self.transform:sample = self.transform(sample)return sample

    這樣我們就寫好了一個讀取自己數據集的 Dataset 類,現在來嘗試使用這個類:

    def show_landmarks(sample, ax):print(sample["image"].shape)ax.imshow(sample["image"])ax.scatter(sample["landmarks"][:, 0], sample["landmarks"][:, 1])face_dataset = FaceLandmarksDataset("./faces") # 實例化 for i in range(5):sample = face_dataset[i] # 這里會調用類中的 __getitem__()方法ax = plt.subplot(1, 5, i+1)ax.set_title("Sample #" + str(i+1))show_landmarks(sample, ax)plt.show()

    結果如下所示,畫出了五張人臉以及他們對應的 landmark 點:

    2.3.2 編寫Transform類

    為什么會有 Transform 類?這是因為在很多時候我們并不是把原圖傳入神經網絡,在輸入數據之前會對原數據進行一些列的預處理(preprocessing),例如:數據集中的圖片不會全部都是相同尺寸的,但大多神經網絡都需要輸入圖片的尺寸是固定的,因此我們需要對數據集中的做預處理,resize 或是 crop 以保證輸入數據的尺寸是符合神經網絡要求的。Transform 類就是用于做數據預處理而設計的類,我們一共設計三個類來組成 Transform 類:Rescale(), RandomCrop(),ToTensor():

    • Rescale類:這個類用于對圖片進行 resize。
    • RandomCrop類:這個用于對圖片進行裁剪,且是任意位置隨機裁剪。
    • ToTensor類:這個類用于將 np 數組格式轉換成 torch 中 tensor 的格式,圖片的 np 數組是 [height, width, channel],而 torch 中圖片的 tensor 格式是 [channel, height, width]。

    這些類中都要實現 __call__ 方法,call 方法可以使得這些類在被實例化的時候就調用 __call__ 函數下的方法。

    class Rescale(object):def __init__(self, output_size):assert isinstance(output_size, (list, tuple)), "output size must be tuple or list."self.output_size = output_sizedef __call__(self, sample):image, land_marks = sample["image"], sample["landmarks"]h, w = image.shape[:2]new_h, new_w = self.output_sizeimg = transform.resize(image, (int(new_w), int(new_h)))land_marks = land_marks * [new_w / w, new_h / h]return {'image': img, "landmarks": land_marks}class RandomCrop(object):def __init__(self, output_size):assert isinstance(output_size, (int, tuple)), "output size must be int or tuple."if isinstance(output_size, int):self.output_size = [output_size, output_size]else:assert len(output_size) == 2, "output size tuple's length must be 2."self.output_size = output_sizedef __call__(self, sample):image, land_marks = sample["image"], sample["landmarks"]h, w = image.shape[:2]crop_h, crop_w = self.output_sizetop = np.random.randint(0, h - crop_h)left = np.random.randint(0, w - crop_w)image = image[top:top+crop_h, left:left+crop_w]land_marks -= [left, top]return {"image":image, "landmarks":land_marks}class ToTensor(object):def __call__(self, sample):image, land_marks = sample["image"], sample["landmarks"]image = image.transpose((2, 0, 1))return {"image":image, "landmarks":land_marks}

    這樣我們就實現好了這三個功能的類,接下來我們將這三個方法合成一個方法,可以依次按順序對一個 sample 做這三個操作。通過 torchvision.transforms() 來融合這三個方法。

    scale = Rescale((256, 256)) # 單實現 Resacle 處理 random_crop = RandomCrop(128) # 單實現 RandomCrop 處理 compose = transforms.Compose([Rescale((256, 256)), RandomCrop(224)]) # 對一個sample先Rescale再RandomCropsample = face_dataset[66]for i, tsfm in enumerate([scale, random_crop, compose]): # 同時展示Rescale、RandomCrop和先Resacle再RandomCrop的效果transformed_data = tsfm(sample)ax = plt.subplot(1, 3, i+1)ax.set_title(type(tsfm).__name__)show_landmarks(transformed_data, ax) plt.show()

    效果如下,Compose 是指結合了前兩種方法,先 resize 后再隨機 Crop 后的樣子:

    2.3.3 將Transform融合到Dataset中去

    前面提到,Transform 的作用是做數據預處理的,因此我們期望每次從 Dataset 中讀取數據的時候,這些數據都被預處理一次,因此我們可以把 Transform 融合到 Dataset 中去,還記得我們在寫 Dataset 的時候有留一個參數 transform 嗎?我們現在可以傳進去就好了:

    transformed_dataset = FaceLandmarksDataset(root='./faces',transform=transforms.Compose([Rescale((256, 256)),RandomCrop(224), ToTensor()]))

    這樣我們就寫好了一個帶有 Transform 的 Dataset 了。

    2.3.4 編寫DataLoader類

    在有了 Dataset 類后,我們就可以開始編寫 DataLoader 類了,Dataloader 主要是從Dataset 中去 sample 一個batch 的數據。我們可以直接使用 Pytorch 提供的 Dataloader 類:

    dl = torch.utils.data.DataLoader(transformed_dataset, batch_size=4, shuffle=True, num_workers=4) # windows下num_workers參數要等于0,不然會報錯

    這樣就可以從我們的 Dataset 中取一個batch 的數據了,下面我們來看看怎么用 dataloader 取出 batch data:

    def show_landmark_batch(sample_batch):images_batch, landmarks_batch = sample_batch["image"], sample_batch["landmarks"]batch_size = len(images_batch)im_size = images_batch.size(2)grid = utils.make_grid(images_batch)plt.imshow(grid.numpy().transpose(1, 2, 0))grid_border_size = 2for i in range(batch_size):plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size + (i + 1) * grid_border_size, landmarks_batch[i, :, 1].numpy() + grid_border_size, s=10)plt.title("Batched Samples")for i_batch, sample_batch in enumerate(dl): # dataloader是一個迭代器,迭代一次取出一個batch的sampleif i_batch == 0:show_landmark_batch(sample_batch)plt.show()

    可視化結果如下:


    2.4 使用Tensorboard來可視化訓練過程

    Tensorboard 無疑是一個非常好用且直觀的神經網絡訓練可視化利器,Pytorch 中也集成了使用 Tensorboard 方法,建立一個SummaryWriter() 類即可完成各種類型的可視化,下面我們就一起嘗試使用 Tensorboard 來做可視化。

    2.4.1 Images可視化

    有時候我們拿到的只是圖片信息的向量信息(例如MNIST數據集),我們想查看這些圖片到底長什么樣,這時候我們可以往 Tensorboard 中添加圖片數據信息來可視化這些圖片,首先我們先建立一個 writer 對象。

    from torch.utils.tensorboard SummaryWriter writer = SummaryWriter("run/experience_1") # 將log信息都保存在run/experience_1的目錄下

    這樣我們就建立好了一個 writer 對象,之后的所有操作都通過 writer 對象來完成。

    import matplotlib.pyplot as plt import numpy as np import torch import torchvision import torchvision.transforms as transforms import torch.nn as nn import torch.nn.functional as F import torch.optim as optimtransform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) trainset = torchvision.datasets.FashionMNIST('./data', download=True, train=True, transform=transform) # 加載數據集 testset = torchvision.datasets.FashionMNIST('./data', download=True, train=False, transform=transform) # 加載數據集 trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True) testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=True)classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat','Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle Boot')dataiter = iter(trainloader) images, labels = dataiter.next() img_grid = torchvision.utils.make_grid(images) # 獲取一個batch的數據并將4張圖片合并成一張圖片writer.add_image("four_fashion_samples", img_grid) # 將圖片數據添加到 Tensorboard->(圖片名稱,圖片數據)

    這時候我們前往“run”文件夾存放的目錄,在終端中輸入tensorboard --logdir=run來開啟Tensorboard:

    打開后如下所示,可以看到在 Images 這一欄已經顯示了我們剛才添加進去的圖片信息:


    2.4.2 Graph可視化

    如果我們想更清楚的看到我們建立的神經網絡模型長什么樣,我們可以使用writer.add_graph()方法來將模型添加入 Tensorboard:

    class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(1, 6, 5)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(6, 16, 5)self.fc1 = nn.Linear(16 * 4 * 4, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = x.view(-1, 16 * 4 * 4)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xnet = Net() # 實例化CNN網絡 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) writer.add_graph(net, images) # 將網絡添加入Tensorboard->(神經網絡對象,隨機傳一組Input)

    之后,我們可以在 Tensorboard 的Graphs這一欄看到我們整個神經網絡的架構:


    2.4.3 訓練中的Loss,Accuracy可視化

    在模型訓練過程中,我們想實時的監測 Loss,Accuracy 等值的變化,我們可以通過writer.add_scalar()方法往 Tensorboard 中加入監測變量:

    running_loss = 0.0 right_num = 0 for epoch in range(1): # loop over the dataset multiple timesfor i, data in enumerate(trainloader, 0):# get the inputs; data is a list of [inputs, labels]inputs, labels = data# zero the parameter gradientsoptimizer.zero_grad()# forward + backward + optimizeoutputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()_, preds_tensor = torch.max(outputs, 1)right_num += (preds_tensor.numpy() == labels.numpy()).sum()running_loss += loss.item()if i % 1000 == 999: # 每1000步更新一次running_loss /= 1000accaracy = right_num / 4000writer.add_scalar('training/loss', running_loss, epoch * len(trainloader) + i) # 添加Loss監測變量->(變量名稱,變量值,step)writer.add_scalar('training/accaracy', accaracy, epoch * len(trainloader) + i) # 添加Accuracy監測變量->(變量名稱,變量值,step)print("[%d] Loss: %.4f Acc: %.2f" % (i, running_loss, accaracy))running_loss = 0.0right_num = 0print('Finished Training')

    在訓練過程中,在Scalars一欄可以監測“Loss”和“Accuracy”的變化情況(Tensorboard默認每30s刷新一次),訓練結果如下所示:


    2.5 Pytorch實現DQN算法

    Deep Reinforcement Learning 結合了深度學習和增強學習,使得增強學習能有更好的效用。這次我們使用Pytorch來實現一個DQN,學會玩 CartPole-v0 的立桿游戲。DQN 一共分為target network 和 evaluate network,我們在 DQN 類中會實現這兩個網絡。在更新 evaluate network 時,我們使用qevalq_{eval}qeval?rt+γ?qnextr_t+\gamma*q_{next}rt?+γ?qnext? 之間的差作為 Loss 值來更新網絡,其中 qevalq_{eval}qeval? 來自evaluate network,qnextq_{next}qnext?來自target network。DQN架構圖和 net 示意圖如下所示:(這里默認已經有DQN的基礎,如果不清楚DQN可以參考強化學習入門筆記)。

    import torch import torch.nn as nn import torch.nn.functional as F import numpy as np import gym from torch.utils.tensorboard import SummaryWriter# Hyper Parameters EPOCH = 400 BATCH_SIZE = 32 LR = 0.01 # learning rate EPSILON = 0.9 # greedy policy GAMMA = 0.9 # reward discount TARGET_REPLACE_ITER = 100 # target update frequency MEMORY_CAPACITY = 2000 env = gym.make('CartPole-v0') env = env.unwrapped N_ACTIONS = env.action_space.n N_STATES = env.observation_space.shape[0]

    import 一些包和定義一些超參數,這里要用到 gym 庫,需要先pip install gym。

    class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.fc1 = nn.Linear(N_STATES, 20)self.fc1.weight.data.normal_(0, 0.1) self.fc2 = nn.Linear(20, N_ACTIONS)self.fc2.weight.data.normal_(0, 0.1) def forward(self, x):x = F.relu(self.fc1(x))return self.fc2(x)

    定義一個Net類,這里我們只有一層 Hidden Layer。輸入為 gym 給的 observation 的維度,輸出為 action_space 的維度,下面我們實現 DQN 類。

    class DQN(object):def __init__(self):self.target_net, self.evaluate_net = Net(), Net()self.memory = np.zeros((MEMORY_CAPACITY, N_STATES * 2 + 2))self.loss_Function = nn.MSELoss()self.optimizer = torch.optim.Adam(self.evaluate_net.parameters(), lr=LR)self.point = 0self.learn_step = 0def choose_action(self, s):s = torch.unsqueeze(torch.FloatTensor(s), 0) # torch 不支持傳入單樣本,只能傳入batch的data,因此這里單樣本數據需要提升一個維度if np.random.uniform() < EPSILON: # epsilon-greedy探索 return torch.max(self.evaluate_net.forward(s), 1)[1].data.numpy()[0]else:return np.random.randint(0, N_ACTIONS)def store_transition(self, s, a, r, s_):self.memory[self.point % MEMORY_CAPACITY, :] = np.hstack((s, [a, r], s_))self.point += 1def sample_batch_data(self, batch_size):perm_idx = np.random.choice(len(self.memory), batch_size)return self.memory[perm_idx]def learn(self) -> float:if self.learn_step % TARGET_REPLACE_ITER == 0:self.target_net.load_state_dict(self.evaluate_net.state_dict())self.learn_step += 1batch_memory = self.sample_batch_data(BATCH_SIZE)batch_state = torch.FloatTensor(batch_memory[:, :N_STATES])batch_action = torch.LongTensor(batch_memory[:, N_STATES : N_STATES + 1].astype(int))batch_reward = torch.FloatTensor(batch_memory[:, N_STATES + 1 : N_STATES + 2])batch_next_state = torch.FloatTensor(batch_memory[:, -N_STATES:])q_eval = self.evaluate_net(batch_state).gather(1, batch_action) # 由于返回的是對每個action的value,因此用gather()去獲得采取的action對應的valueq_next = self.target_net(batch_next_state).detach() # target network是不做更新的,所以要detach()q_target = batch_reward + GAMMA * q_next.max(1)[0].view(BATCH_SIZE, 1)loss = self.loss_Function(q_eval, q_target)self.optimizer.zero_grad()loss.backward()self.optimizer.step()return loss.data.numpy()

    DQN中一共有幾個需要注意的部分:memory,target network,evaluate network。其中 memory 是一個環形的隊列,當經驗池滿了之后就會把舊的數據給覆蓋掉。target network會在一定的steps之后把evaluate network的參數直接復制到自己的網絡中。

    dqn = DQN()writer = SummaryWriter("run/MemoryCapacity_100_CustomReward/") writer.add_graph(dqn.evaluate_net, torch.randn(1, N_STATES))global_step = 0 for i in range(EPOCH):s = env.reset()running_loss = 0cumulated_reward = 0step = 0while True:global_step += 1env.render()a = dqn.choose_action(s)s_, r, done, _ = env.step(a)# 自定義reward,不用原始的rewardx, x_dot, theta, theta_dot = s_r1 = (env.x_threshold - abs(x)) / env.x_threshold - 0.8r2 = (env.theta_threshold_radians - abs(theta)) / env.theta_threshold_radians - 0.5r = r1 + r2dqn.store_transition(s, a, r, s_)cumulated_reward += rif dqn.point > MEMORY_CAPACITY: # 在經驗池滿了之后才開始進行學習loss = dqn.learn()running_loss += lossif done or step > 2000:print("【FAIL】Episode: %d| Step: %d| Loss: %.4f, Reward: %.2f" % (i, step, running_loss / step, cumulated_reward))writer.add_scalar("training/Loss", running_loss / step, global_step)writer.add_scalar("training/Reward", cumulated_reward, global_step)breakelse:print("\rCollecting experience: %d / %d..." %(dqn.point, MEMORY_CAPACITY), end='')if done:breakif step % 100 == 99:print("Episode: %d| Step: %d| Loss: %.4f, Reward: %.2f" % (i, step, running_loss / step, cumulated_reward))step += 1s = s_

    以上是學習的過程,在 reward 的那個地方,使用了自定義的 reward,而不是使用 gym 給定的 r,合理的制定 reward 可以幫助模型學的更好的效果,我們后面會對使用不同的 reward 學習的效果進行對比。模型結構如下圖所示:

    訓練過程中,按照上一節講的內容利用 tensorboard 追蹤 Loss 和 Reward 兩個變量的值變化情況:

    可以看到,Loss 的值在逐步趨于0,這說明模型是趨于收斂的,Reward 值也在總體上升,最高達到600左右(做了 smooth 后的值,不是原本值),倒立擺能夠穩住很長一段時間。下面我們來看一下,如果我們修改部分超參數的值會出現什么樣的訓練效果,我一共做了4個對比試驗:

  • 使用自定義回報值,經驗池容量設置為100
  • 使用默認回報值,經驗池容量為100
  • 使用默認回報值,使用更為復雜的網絡結構,經驗池容量為100
  • 使用默認回報值,使用更為復雜的網絡結構,經驗池容量為2000
    • 回報值設定對模型效果的影響
      在經驗池容量和模型結構都相同的情況下,使用自定義回報(橘色)和默認回報(藍色)的效果如下圖所示:

    可以看到,Loss 函數最終都是趨于很小的值,這說明使用兩種方法模型都是收斂的,但是 Reward 值差別非常大。為什么同樣都是收斂的情況下,效果差這么多?這是因為,模型收斂是指:訓練出來的Q-Net總會選擇含有最大Q值的Action(這樣的Loss最小)。但是Action的Q值是通過 Reward 來計算的:rt+argmaxaQπ(s,a)r_t + argmax_aQ^\pi(s, a)rt?+argmaxa?Qπ(s,a)。因此, reward 的制定直接決定了模型效果的好壞。所以,如果 reward 不能很好的與最終效用產生緊密的聯系,模型雖然能有找到最大效用行為的能力,但這個最大效用的行為并不能產生很好的效果。因此,如果看到 Loss 已經很小,但最終效用不怎么好的情況下,一般就考慮重新制定 reward,選擇一個能夠更精準表示最終效用的評價函數。

    • 模型復雜度對效用的影響
      在經驗池容量相同且均使用默認回報的情況下,嘗試修改原有模型,使得模型變得稍微復雜一些,看看效果會不會更好一些,將模型再加一層 hidden layer 并且增加每一層神經元的個數,代碼如下:
    class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.fc1 = nn.Linear(N_STATES, 80)self.fc1.weight.data.normal_(0, 0.1) self.fc2 = nn.Linear(80, 40)self.fc2.weight.data.normal_(0, 0.1) self.fc3 = nn.Linear(40, N_ACTIONS)self.fc3.weight.data.normal_(0, 0.1) def forward(self, x):x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))return self.fc3(x)

    模型結構如下:

    使用復雜模型(紅色)和使用簡單模型(藍色)的 Reward 情況對比如下:

    可以看到,兩個模型 Loss 都是趨于0,證明模型均收斂,紅色(復雜)模型的得比藍色(簡單)模型要稍微高一些,但和使用自定義回報的模型比起來還是差很遠。

    • 經驗池容量對模型效果的影響
      對于相同復雜度的模型,使用相同回報值,通過改變經驗池容量來觀察對模型效果有什么影響,容量為100(紅色)和容量為2000(藍色)模型的對比結果如下:

    觀察可得,擴展經驗池后的效果會比小經驗池的效果要好一些。因此,通過以上總結,我們可以得出在DQN中不同超參數對模型效果影響程度的結論,即:回報函數制定 > 經驗池容量 > 模型復雜度。

    2.6 Pytorch實現Policy Gradient算法

    在實現了 DQN 這種 value-based 的算法之后,我們嘗試實現一種 policy-based 的方法:Policy-Gradient。策略梯度是很多經典算法的基石,包括 A3C,PPO在內的多種算法都是基于策略梯度的基本思想來實現的,因此這次我們同樣基于 CartPole 的簡單場景來實現 Policy Gradient 算法(默認已經有PG的基礎,如果不清楚PG可以參考強化學習入門筆記)。
    Policy Gradinet 的示意圖如下,根據輸入observation,策略決策網絡會輸出每一個action對應被采取的概率(效用越高的action概率就會被預測的越大)。我們可以根據這個概率來進行行為選擇,這里和上面的DQN不一樣,DQN的行為選擇是一定概率選擇最大效用的行為,一定概率隨機選行為,在PG算法中直接按照概率來選行為,即結合了多選擇高效用的行為的標準,又結合了一定概率進行行為探索的標準。一旦選擇好了一個行為后,我們就去計算這個行為的效用是多少,如果效用高,我們就增加這個動作的概率;反之則降低選擇該行為的概率。

    先定義神經網絡層,這里使用1個 hidden layer,10個神經元:

    import gym import numpy as np import torch import torch.nn.functional as F import torch.nn as nn import matplotlib.pyplot as plt from torch.utils.tensorboard import SummaryWriterWRITE_TENSORBOARD_FLAG = Trueclass Net(nn.Module):def __init__(self, observation_dim, action_dim):super(Net, self).__init__()self.observation_dim = observation_dimself.action_dim = action_dimself.fc1 = nn.Linear(self.observation_dim, 10)self.fc2 = nn.Linear(10, self.action_dim)def forward(self, x):x = F.tanh(self.fc1(x))return F.softmax(self.fc2(x))

    隨后我們定義PolicyGradient類,由于Policy Gradient中梯度計算公式為:▽θlogπθ(a∣s)R(a)\bigtriangledown{_\theta}log\pi_\theta(a|s)R(a)θ?logπθ?(as)R(a)πθ(a∣s)\pi_\theta(a|s)πθ?(as)是在網絡參數為θ\thetaθ的情況下,行為a被選擇的概率,R(a)R(a)R(a)是行為a選擇后能得到的總回報值,這個回報值通常使用R(a)=∑t=0TγtRnR(a) = \sum_{t=0}^{T}\gamma^tR_nR(a)=t=0T?γtRn?來計算,因此我們必須完整的收集了一個Epoch的數據信息后才能進行一次梯度更新(不然無法計算累計回報)。這一點也和DQN不一樣,DQN可以進行單步更新,而PG不可以。

    class PolicyGradient(object):def __init__(self, observation_dim, action_dim, learning_rate=0.01, gamma=0.95):self.observation_dim = observation_dimself.action_dim = action_dimself.gamma = gammaself.ep_obs, self.ep_r, self.ep_a = [], [], []self.net = Net(observation_dim, action_dim)self.optimizer = torch.optim.Adam(self.net.parameters(), lr=learning_rate)def choose_action(self, observation):prob_list = self.net(observation)action = np.random.choice(range(prob_list.size(0)), p=prob_list.data.numpy())return actiondef store_transition(self, obs, r, a):self.ep_obs.append(obs)self.ep_r.append(r)self.ep_a.append(a)def learn(self):cumulative_reward_list = self.get_cumulative_reward()batch_obs = torch.FloatTensor(np.vstack(self.ep_obs))batch_a = torch.LongTensor(np.array(self.ep_a).reshape(-1, 1))batch_r = torch.FloatTensor(cumulative_reward_list.reshape(-1, 1))action_prob = self.net(batch_obs)action_prob.gather(1, batch_a)gradient = torch.log(action_prob) * batch_rloss = -torch.mean(gradient) # 網絡會對loss進行minimize,但我們是想做梯度上升,所以加一個負號self.optimizer.zero_grad()loss.backward()self.optimizer.step()self.reset_epoch_memory()return loss.data.numpy()def get_cumulative_reward(self):running_r = 0cumulative_reward = np.zeros_like(self.ep_r)for i in reversed(range(len(self.ep_r))):running_r = running_r * self.gamma + self.ep_r[i]cumulative_reward[i] = running_r# normalize cumulative rewardcumulative_reward -= np.mean(cumulative_reward)cumulative_reward /= np.std(cumulative_reward)return cumulative_rewarddef reset_epoch_memory(self):self.ep_a.clear()self.ep_obs.clear()self.ep_r.clear()

    值得注意的是,在策略梯度中其實是沒有loss這個概念的(因為根本就沒有標簽),這個loss的梯度是我們通過logπθ(a∣s)R(a)log\pi_\theta(a|s)R(a)logπθ?(as)R(a)算出來的,由于在神經網絡中我們做的是梯度下降:θ?=gradient?LearningRate\theta -= gradient*LearningRateθ?=gradient?LearningRate,但在策略梯度算法中我們是希望做梯度上升的θ+=gradient?LearningRate\theta += gradient*LearningRateθ+=gradient?LearningRate,因此我們在求得了梯度之后要添加一個負號再將其作為loss。最后,我們建立 CartPole 場景并將該算法用在訓練場景上:

    def main():DISPLAY_REWARD_THRESHOLD = 200RENDER = Falseenv = gym.make('CartPole-v0')env = env.unwrappedPG = PolicyGradient(env.observation_space.shape[0], env.action_space.n)if WRITE_TENSORBOARD_FLAG:writer = SummaryWriter("run/simple_model_experiment")writer.add_graph(PG.net, torch.rand(env.observation_space.shape))for i_eposide in range(120):obs = env.reset()obs = torch.FloatTensor(obs)while True:if RENDER: env.render()action = PG.choose_action(obs)obs_, reward, done, _ = env.step(action)PG.store_transition(obs, reward, action)if done:ep_r = sum(PG.ep_r)if ep_r > DISPLAY_REWARD_THRESHOLD: RENDER = Trueprint("episode: %d | reward: %d" % (i_eposide, ep_r))loss = PG.learn()if WRITE_TENSORBOARD_FLAG:writer.add_scalar("training/loss", loss)writer.add_scalar("training/reward", ep_r)breakobs = torch.FloatTensor(obs_)if __name__ == '__main__':main()

    Policy Gradient 訓練結果如下:

    loss對應的就是每一次更新的梯度,reward對應的是模型的得分。可以看到使用策略梯度的效果還是蠻不錯的,在100個Epoch之后桿子基本就可以被很好的穩住了。

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的Pytorch快速入门笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    亚洲资源片 | 久草在线久草在线2 | 日日夜夜人人精品 | 精品国产乱码一区二 | 日p视频在线观看 | 久久免费看视频 | 久草在线最新免费 | 玖玖在线免费视频 | 亚洲成人国产 | 亚洲丁香久久久 | 久久久伊人网 | 国产正在播放 | 午夜视频免费播放 | 欧美高清成人 | 激情久久小说 | 中文字幕乱码电影 | 黄色毛片电影 | 99久久国产免费免费 | av资源免费观看 | 五月婷婷激情六月 | 日韩黄色免费看 | 九九热免费视频在线观看 | 成人av在线直播 | 日韩av成人在线观看 | 九九久久免费 | 精品 激情| 青春草免费视频 | 热久久视久久精品18亚洲精品 | 99精品欧美一区二区三区黑人哦 | 91传媒免费在线观看 | 特黄特黄的视频 | 国产福利在线免费观看 | 亚洲电影在线看 | av免费电影在线观看 | 麻豆传媒在线免费看 | 久草亚洲视频 | 一区二区三区四区精品视频 | 色综合久久五月天 | 久久婷婷精品视频 | 国产手机免费视频 | 国产色就色 | 欧美巨乳网| 久久国产精彩视频 | 久av电影 | 91麻豆.com| 中文字幕最新精品 | 国产精品视频全国免费观看 | 国内精品久久久久久中文字幕 | 五月色丁香 | 欧美日韩网址 | 在线观看中文 | 国产精品99久久免费观看 | 97在线视频网站 | 成人国产一区 | 久久精品伊人 | av片免费播放 | 日韩二区三区在线观看 | 国产精品第二十页 | 美女视频黄免费 | 91丨九色丨高潮丰满 | 亚洲精品日韩av | 成人一级片免费看 | 天天操天天干天天插 | 综合中文字幕 | 色吊丝在线永久观看最新版本 | 欧美精品在线观看 | 99欧美| 免费在线观看av网址 | 亚洲一区二区三区91 | 国产在线无| 久热色超碰 | 丁香激情五月婷婷 | 一区二区三区四区久久 | 五月婷婷在线观看视频 | 天天玩夜夜操 | 天天玩天天干 | www操操操 | 国产成人精品午夜在线播放 | 国产视频99 | 天天干天天摸天天操 | 国产精品久久久久久久久久直播 | 99久久婷婷国产 | 成年人网站免费在线观看 | 国内一级片在线观看 | 日本中文字幕在线 | 99热这里只有精品在线观看 | 天天亚洲| 天天天天天干 | 欧美亚洲专区 | 亚洲国产免费看 | 中文字幕日本在线 | 国产精品久久毛片 | 久久久午夜视频 | 黄色国产在线 | 久久视频 | 亚州欧美视频 | 91精品国自产在线 | 欧美精品久久久久久久免费 | 亚洲精品乱码久久 | 人人躁| 日韩中文字幕免费 | 欧美伦理一区二区三区 | 亚洲精品综合久久 | 成人免费观看网址 | 91精品在线免费观看视频 | 国产精品一区二区免费 | 99久久精品国产一区二区成人 | 久久国产精品免费视频 | 中文字幕一二 | 亚洲成色 | 四虎在线视频 | 午夜在线观看影院 | 中文字幕色在线视频 | 2023国产精品自产拍在线观看 | 黄色在线观看免费网站 | 综合天天| 色网站国产精品 | 日韩日韩日韩日韩 | 欧美一级乱黄 | 最新av网址在线观看 | 粉嫩高清一区二区三区 | 久久电影网站中文字幕 | 日韩电影在线观看一区二区 | 黄色在线观看污 | 永久免费毛片 | 九九爱免费视频在线观看 | 免费午夜网站 | 日韩精品一区二区三区在线播放 | 在线观看日韩精品视频 | 插综合网 | 欧美久草视频 | 91精品麻豆 | 91大片网站 | 国产.精品.日韩.另类.中文.在线.播放 | 操一草 | 在线视频免费观看 | 久久艹国产视频 | av网站在线观看免费 | 蜜臀av夜夜澡人人爽人人桃色 | 色欧美成人精品a∨在线观看 | 国产资源精品在线观看 | av免费在线观看网站 | 最新国产在线观看 | 99成人精品 | 亚洲国产精彩中文乱码av | 欧美一进一出抽搐大尺度视频 | 国产精品第 | 日日操日日插 | 久久婷婷国产色一区二区三区 | 成人a视频| 欧美aⅴ在线观看 | 国产精品99久久久精品 | 久久av网 | 99视频免费播放 | 亚洲乱码在线 | 欧美久久电影 | 色婷婷在线播放 | 久久久久久久久久久久电影 | 福利网址在线观看 | 国产精品一区二区麻豆 | 色婷婷天天干 | 色婷婷免费视频 | a电影在线观看 | 日韩免费视频一区二区 | 狠狠干狠狠插 | 91视频啊啊啊 | 亚洲精品久久久久999中文字幕 | 最近中文字幕大全中文字幕免费 | 91.精品高清在线观看 | 欧美国产一区在线 | 亚洲精品国精品久久99热 | 免费黄色av. | 亚洲成av人片一区二区梦乃 | 亚洲欧美国产精品久久久久 | 国产精品初高中精品久久 | 婷婷在线不卡 | 成人午夜黄色影院 | 亚洲三级视频 | 五月开心六月伊人色婷婷 | 日韩在线一区二区免费 | 免费高清在线视频一区· | 久久久久久久久久久成人 | 色爱成人网 | 国产91在线观看 | 日本在线观看中文字幕 | 99九九视频 | 日韩欧美一区二区三区视频 | 日韩欧美精品在线视频 | 99热99re6国产在线播放 | 成年人在线视频观看 | 亚洲一区二区三区四区精品 | 69视频在线 | 国产一区二区电影在线观看 | 欧美日韩国产二区三区 | 国产精品欧美久久久久三级 | 国产精品日韩在线播放 | 国产美女精品视频 | 成人黄色资源 | 波多野结衣一区二区 | 激情自拍av | 国产美女主播精品一区二区三区 | 亚洲va欧洲va国产va不卡 | 成人精品久久 | 美女网站色在线观看 | 国产精品久久久久一区二区国产 | 97超碰人| 91九色丨porny丨丰满6 | 伊人首页 | 国产在线视频在线观看 | 亚洲精品啊啊啊 | 日日射天天射 | 日韩在线观看第一页 | 午夜av一区 | 大荫蒂欧美视频另类xxxx | 欧美91av| 色在线最新 | 国产精品美女免费 | 在线观看视频你懂 | 久久国产精品影视 | 国产精品一区二区免费视频 | 色先锋av资源中文字幕 | 麻豆传媒在线免费看 | 91在线观看视频网站 | 日韩高清在线一区 | 成年人电影免费在线观看 | 天天做综合网 | 久久国产精品99久久久久久老狼 | 一级黄网 | 日韩av成人在线观看 | 免费精品视频在线 | 91av蜜桃 | 国产淫片免费看 | 男女男视频 | 中文字幕一区2区3区 | 国产又粗又猛又黄又爽的视频 | 国产亚洲精品成人av久久影院 | 国产精品中文久久久久久久 | 国产电影黄色av | 美女露久久 | 开心色插 | 草久久av| 国产免费观看av | 天天搞夜夜骑 | 国产亚洲一区二区三区 | 激情喷水 | 亚洲成人精品在线观看 | 亚洲欧美日韩精品久久久 | 999成人| 人人草在线视频 | 欧美少妇18p | 96国产在线 | 婷婷丁香六月天 | 外国av网 | 98精品国产自产在线观看 | 国产亚洲视频中文字幕视频 | 人成在线免费视频 | 国产成人精品一区一区一区 | 97在线观看免费 | 免费观看性生交 | 国产一区二区久久久久 | 欧美伦理一区二区三区 | 久久夜色精品亚洲噜噜国4 午夜视频在线观看欧美 | 国产精品一区免费观看 | 国产黄色美女 | 91九色porny蝌蚪主页 | 成人97人人超碰人人99 | 亚洲涩涩一区 | 久久综合国产伦精品免费 | 精品视频区 | 日韩免费av网址 | 成人av资源站| 丁香色婷婷| 久久视| 五月婷在线| 最新av网址在线观看 | 国内99视频| 日韩在线视频免费播放 | 日韩videos高潮hd | 国产精品专区h在线观看 | www免费视频com━ | 亚洲va在线va天堂 | 又爽又黄又刺激的视频 | 国产精品普通话 | 午夜男人影院 | 日韩天天操 | 婷婷色在线观看 | 久草久草在线 | 久草视频在线免费看 | 亚洲精品影院在线观看 | 国产剧情一区二区在线观看 | 亚洲成 人精品 | 中文字幕一区二区三区四区 | 久久情爱| 久久久免费精品视频 | 99视屏| 免费看色网站 | 日韩精品五月天 | 国产99一区二区 | 欧美日韩精品网站 | 亚洲网站在线看 | 国产成人精品免高潮在线观看 | 久久精品一二三 | 国产小视频国产精品 | 五月天婷婷免费视频 | 1024久久| 丁香婷婷色月天 | 欧美日韩不卡一区二区 | 国产一区二区视频在线 | 国产高清在线观看av | 美女网站视频一区 | 欧美网址在线观看 | 国产精品久久久999 国产91九色视频 | 天堂在线一区二区三区 | 新版资源中文在线观看 | 久草在线资源观看 | 久草网站在线观看 | 亚洲三级在线免费观看 | 色小说av | 午夜影院一级 | 手机成人av在线 | 不卡电影一区二区三区 | 国产精品毛片一区二区在线看 | 欧美另类xxx | 一区二区三区高清在线 | 一级黄色片在线观看 | www.888av| 91麻豆看国产在线紧急地址 | 久色伊人 | 丁五月婷婷 | 综合久久网站 | 在线观看免费中文字幕 | 日韩最新在线视频 | 丁香视频在线观看 | 中文字幕在线观看免费观看 | 美女免费视频观看网站 | 五月婷婷影院 | 久久人人看 | 国产中文字幕91 | 欧美日韩精品在线播放 | 在线观看免费黄视频 | 午夜免费视频网站 | 97热在线观看 | 亚洲免费视频观看 | 91av在线播放视频 | 日韩艹| 三级免费黄色 | 日韩精品一区二区三区免费观看视频 | 久久久久电影网站 | 不卡的av | 欧美视屏一区二区 | 亚洲天堂精品视频 | 日韩成年视频 | 色射色| 探花视频免费观看 | 在线超碰av | 久草免费色站 | 国产婷婷一区二区 | 中国一级片免费看 | 亚洲经典视频 | 99视频这里有精品 | 伊人成人激情 | 亚洲桃花综合 | 久久视频免费在线观看 | 91视频高清免费 | 国产视频在线播放 | 成人毛片久久 | 婷婷在线色 | 天天爱天天爽 | 久久久久久久久久影视 | 国产成人一区二区三区免费看 | 在线观看日本高清mv视频 | 久久久免费精品国产一区二区 | 91大神在线看 | 久久亚洲福利视频 | 国产综合在线视频 | 日日爽日日操 | 亚洲午夜精品久久久久久久久 | 天天色天天色 | 亚洲片在线 | 国产日产av | 久久狠狠一本精品综合网 | 特级西西444www高清大视频 | 九九交易行官网 | 91高清完整版在线观看 | 麻豆传媒一区二区 | 日韩在线视频播放 | 黄色软件视频大全免费下载 | 国产精品久久久久久久久久ktv | 免费av高清 | 欧美日韩视频网站 | 国产精品久久久久婷婷二区次 | 久久国产福利 | 国产高清视频在线免费观看 | 亚洲一级电影 | 日本大尺码专区mv | 美女视频黄是免费的 | 国产一二三区av | 婷婷综合激情 | 九九热免费精品视频 | 免费精品国产va自在自线 | 亚洲精色 | 黄色tv视频| 中文字幕国语官网在线视频 | 国产亚洲精品久久久久久电影 | 免费黄色一区 | 欧美激情综合色 | 国产午夜精品一区二区三区嫩草 | 奇米先锋 | 97在线视频观看 | 日韩三级视频在线看 | 日韩精品视频一二三 | av电影免费看 | 成人高清av在线 | 成人av影视在线 | 国产精品成人自拍 | 精品亚洲午夜久久久久91 | 成人精品一区二区三区电影免费 | 四虎影视精品永久在线观看 | 九九九九免费视频 | 亚洲乱码国产乱码精品天美传媒 | 日韩精品五月天 | 久久色中文字幕 | 亚洲日日日 | 日韩精品一区二区久久 | 国产精品久久久一区二区 | 国产精品美女久久久久久网站 | 免费福利视频网 | 中文字幕在线观看完整版电影 | 欧美一级欧美一级 | 国产精品成人一区二区三区吃奶 | 亚洲成人黄色在线观看 | 欧美日韩精品免费观看视频 | 国产精品免费小视频 | 欧美久久久久久久 | 日韩视频a| 婷婷日 | 91在线蜜桃臀 | 三日本三级少妇三级99 | 999久久久欧美日韩黑人 | 日本久久久精品视频 | 美女久久久久久久 | 日韩中文字幕电影 | 中文字幕文字幕一区二区 | 最新免费中文字幕 | 亚洲自拍av在线 | 伊人网站| 久久国产精品久久w女人spa | 久久久久一区二区三区 | 在线看一区 | 国产韩国精品一区二区三区 | 综合久久网站 | 欧美黄色成人 | 精品视频免费观看 | 国产a网站 | 99久久一区| 亚洲另类视频 | 久久夜靖品| 亚洲精品小视频 | 在线 高清 中文字幕 | 国产一级二级在线播放 | 黄色1级毛片 | 国产精品入口66mio女同 | 精品欧美一区二区在线观看 | 日韩亚洲在线观看 | 人人干人人干人人干 | 91入口在线观看 | 久久免费视频3 | 成人av免费播放 | 免费中文字幕在线观看 | 一级一级一片免费 | 亚洲激情小视频 | 女人18精品一区二区三区 | 国产超碰在线 | 欧美三人交 | 深夜福利视频在线观看 | 国产资源网 | 丝袜美腿在线 | 狠狠干夜夜操天天爽 | 色婷婷电影网 | 国产色 在线 | 九色激情网| 麻豆视频免费入口 | 蜜臀久久99精品久久久无需会员 | 久久精品一区八戒影视 | 国产999精品久久久久久麻豆 | a视频在线播放 | 欧美一区中文字幕 | 人人要人人澡人人爽人人dvd | 国产最新福利 | 激情五月婷婷 | 久草干| 日韩在线视频在线观看 | 免费观看一级成人毛片 | 免费高清看电视网站 | 日日干 天天干 | 深夜激情影院 | 欧美一级淫片videoshd | 久久五月网| 激情综合网五月 | 亚洲精品黄色片 | 久草久草在线观看 | 国产一区高清在线 | 香蕉久久国产 | 亚洲一区 av | 夜色资源网 | 97视频免费在线 | 亚洲综合婷婷 | 91亚洲精品久久久中文字幕 | 日本最新高清不卡中文字幕 | 三级动态视频在线观看 | 最新av在线网站 | 国产成人亚洲精品自产在线 | 91 中文字幕| 97超碰人人澡人人爱 | 国产成人一区二区三区在线观看 | 久久久久久久久久久综合 | 在线免费观看黄色av | 国产一区二区不卡视频 | 久久久污 | 欧美激情h | 亚洲精品在线观看中文字幕 | 天天干亚洲 | 亚洲欧美日韩精品一区二区 | www日韩在线观看 | 六月丁香综合 | 少妇bbb好爽| 国产中文在线字幕 | 国产免费高清视频 | 91伊人影院 | 天天色天天射综合网 | 在线观看视频在线观看 | 精品久久福利 | 玖玖在线播放 | 国产在线日本 | 久久综合九色综合欧美就去吻 | 97视频免费观看2区 亚洲视屏 | 免费的黄色av | 亚洲精品影视在线观看 | mm1313亚洲精品国产 | 亚洲我射av| 99精品成人 | 又黄又爽又色无遮挡免费 | 性色va| 成人资源在线观看 | 亚洲最新合集 | 国产成人99久久亚洲综合精品 | 久久久久国产精品午夜一区 | 欧美日韩另类视频 | 国产精品成人自产拍在线观看 | av成人免费在线 | 中文字幕在线视频一区二区 | 成人久久18免费网站图片 | 99精品偷拍视频一区二区三区 | 精品亚洲免费视频 | 丝袜美腿亚洲 | 精品一区二区6 | www国产亚洲精品久久网站 | 91pony九色丨交换 | 人人要人人澡人人爽人人dvd | 亚洲黄色激情小说 | 一区二区三区高清在线观看 | 97手机电影网 | 久久99久久99精品免费看小说 | 美女视频黄免费 | 色美女在线 | 免费久久99精品国产 | 欧美激情在线看 | 国产精品一区二区久久精品爱微奶 | 国产视频在线观看一区 | 欧美在线观看禁18 | av在线等 | 日本深夜福利视频 | 国产精品久久在线 | 国产精品短视频 | 五月婷久 | 亚洲精品国产免费 | 欧美一级在线观看视频 | 91亚洲精品国偷拍 | 狠狠狠干 | 亚洲精品av在线 | 在线观看黄色国产 | 九九久久视频 | 国产最新91| 日韩中文字幕91 | 九九九毛片| av成人资源| 亚洲永久精品在线 | 国产 中文 日韩 欧美 | 中文字幕中文中文字幕 | 看全黄大色黄大片 | 在线视频99 | 高清有码中文字幕 | 日韩a级免费视频 | 免费99视频| 国产成人99久久亚洲综合精品 | 天天综合网在线 | 久久99亚洲精品久久 | 4438全国亚洲精品在线观看视频 | 97精品国产aⅴ | 午夜在线观看影院 | 久 久久影院 | 在线观看av网 | 欧美日韩首页 | 久久久免费看视频 | av官网| 五月天六月婷婷 | 色偷偷人人澡久久超碰69 | 国产精品免费视频久久久 | 国产永久网站 | 中文免费在线观看 | 国产麻豆电影在线观看 | 亚洲国产午夜 | 欧美va天堂va视频va在线 | 国产在线a视频 | 91成品人影院| 免费高清国产 | 日本婷婷色 | 丁香婷五月 | 在线国产一区二区 | 国产精品99久久久久久大便 | 国产免费视频一区二区裸体 | 福利在线看片 | 天天干视频在线 | 日韩精品一区不卡 | 日韩电影在线一区 | 成人国产网站 | 久久99精品久久只有精品 | 国产亚洲激情视频在线 | 五月天免费网站 | 日韩免费看片 | 天天操人| 在线播放日韩av | 在线观看免费av网 | 国产精品一区二区免费看 | 国产精品综合av一区二区国产馆 | 免费欧美 | 国产在线精品国自产拍影院 | 69av网| 一级片视频免费观看 | av成人在线播放 | 香蕉久久久久 | 黄色成人av | 久久精品综合网 | 日韩 在线观看 | 麻豆成人在线观看 | 成人在线观看资源 | 狠狠色丁香婷婷综合久小说久 | 欧美精品v国产精品 | 午夜视频黄 | 国产一区二区在线免费 | 成年人网站免费在线观看 | 日韩色高清 | 成年人免费av | 激情视频在线观看网址 | 一区二区激情 | 91精品在线免费视频 | 中文字幕精品一区二区精品 | 亚洲精品美女久久久久 | 亚洲欧美国产日韩在线观看 | 国产裸体永久免费视频网站 | 美女视频黄网站 | 久久免费视频4 | 欧美91精品久久久久国产性生爱 | 久久久久免费精品视频 | 在线观看中文字幕av | 玖玖在线观看视频 | 国产精品福利午夜在线观看 | 色狠狠干| 国产精品自产拍在线观看 | 成人avav| 精品国内自产拍在线观看视频 | 天天综合网 天天 | 公开超碰在线 | 999久久久久久| 中文字幕在线影院 | 日韩精品欧美精品 | 黄污网站在线观看 | 午夜成人免费电影 | 成年人免费电影在线观看 | 色偷偷网站视频 | 深夜精品福利 | 91在线免费公开视频 | 欧美激情综合五月色丁香 | 久久综合视频网 | 夜夜天天干 | 国产无套一区二区三区久久 | 免费a v视频 | 国产99区 | 国产视频一区二区在线观看 | 亚洲无毛专区 | 日韩在线视 | 久久久久久电影 | 四虎国产精品免费观看视频优播 | 久久久人| 日韩欧美国产激情在线播放 | 亚洲精品国产电影 | 欧产日产国产69 | 8x成人免费视频 | 久精品视频免费观看2 | 久久影院一区 | 国产精品av免费观看 | www.午夜视频 | 亚洲综合干 | 色中射| 永久中文字幕 | 婷婷六月丁香激情 | 久久国产露脸精品国产 | 日韩av免费在线电影 | 97在线视| 99综合电影在线视频 | 青青草视频精品 | 黄色资源在线 | 亚洲视频精选 | 欧美日韩高清一区二区三区 | 最新日韩中文字幕 | 久久久久久伊人 | 日韩一级电影在线观看 | 91理论片午午伦夜理片久久 | 最新国产精品视频 | 亚洲精品视频在线观看免费视频 | 狠狠网| 中文字幕免费高 | 深夜免费网站 | 婷婷色在线| 91在线你懂的 | 中文字幕在线观看完整版 | 国产高清黄色 | 成人黄色片在线播放 | 国产美女永久免费 | 久久精品这里热有精品 | 可以免费观看的av片 | 黄色三级在线观看 | 亚洲视频在线视频 | 美女免费视频网站 | 网站在线观看日韩 | av网站播放 | 欧美片一区二区三区 | 久久久午夜电影 | 午夜视频在线观看一区二区三区 | 亚洲国产精品成人综合 | 国产精品久久久久久a | 日韩欧美有码在线 | 日韩av成人在线观看 | 国产精品久久久一区二区三区网站 | 国产亚洲精品女人久久久久久 | 亚洲精品人人 | 日本电影久久 | 欧美韩国日本在线观看 | 亚洲深爱激情 | 精品久久福利 | 亚洲精品无| 免费午夜视频在线观看 | 69人人| 不卡av免费在线观看 | 在线看一级片 | 亚州日韩中文字幕 | 西西444www大胆高清图片 | 日韩中文字幕在线不卡 | 欧美日韩在线精品 | 中文字幕网站 | 成人黄色在线观看视频 | 婷婷五月色综合 | 久草精品在线播放 | 久久免费在线观看 | 一区二区成人国产精品 | 欧美日韩视频在线播放 | 国产精品视频999 | 国产白浆视频 | 成人午夜精品福利免费 | 四虎成人精品永久免费av九九 | 中文字幕在线资源 | 国产精品 欧美 日韩 | 免费欧美精品 | 日韩精品最新在线观看 | 在线播放 日韩专区 | 成人久久免费 | 成人xxxx | 日日操天天操夜夜操 | 色噜噜在线观看视频 | 色网站国产精品 | 成年人免费电影 | 国产午夜精品久久 | 日韩av影视 | 91精品国产91 | 日韩在线网址 | 中国黄色一级大片 | 日b黄色片| a级片在线播放 | 91激情视频在线观看 | 91av在线免费 | 天天插天天爱 | 日本久热| 激情丁香5月 | 免费在线日韩 | 国产视频在线播放 | 久久精品视频日本 | 国产精品久久久久久久久久久免费看 | 人人精品久久 | 蜜臀精品久久久久久蜜臀 | 久久免费视频在线 | 国产精品99久久99久久久二8 | 五月婷婷丁香激情 | 中文字幕黄色av | 中文字幕在线第一页 | 久草国产视频 | 亚洲a成人v | 五月开心色| 蜜桃av综合网 | 精品电影一区 | 亚洲精品在线二区 | 在线色亚洲 | 久久新视频 | 国产成年人av | 免费三级黄 | 韩国三级一区 | 欧美日韩亚洲第一 | 久草网首页 | 爱av在线网 | 在线久热 | 99久久精品久久久久久清纯 | 日韩av电影手机在线观看 | 久久免费精品 | 人人精品 | 在线天堂8√ | 国产va在线 | 国产高清日韩欧美 | 天天拍天天爽 | 日韩av高潮 | 91久久奴性调教 | 婷婷香蕉 | 99国产视频 | 久久精品官网 | 国产在线一区二区 | 中文字幕成人一区 | 在线免费观看黄色av | 色吊丝在线永久观看最新版本 | av在线h| 日本视频网 | 欧美人人爱| 在线观看 国产 | 午夜视频免费在线观看 | 久久国产片 | 久久久久久免费 | 国产精品乱码久久久久 | 日韩精品一区二区三区水蜜桃 | 婷婷网站天天婷婷网站 | 午夜在线免费观看 | 天天操天天插 | 欧洲精品久久久久毛片完整版 | 日韩在线视频不卡 | 国产一级精品在线观看 | 日韩视频在线观看免费 | 免费黄色网址大全 | 日韩免费在线一区 | 亚洲视频第一页 | av中文字幕网址 | www.xxxx变态.com| 天天鲁一鲁摸一摸爽一爽 | 又爽又黄在线观看 | 久艹视频在线免费观看 | 久久久精品成人 | 国产伦理精品一区二区 | 国产区精品在线 | 西西444www | 二区视频在线观看 | 日韩免费一级a毛片在线播放一级 | 欧美精品久久久久 | 亚洲精品视频一二三 | 久草网视频 | 最新国产精品亚洲 | 中文字幕在线免费看线人 | 久久国产乱 | 国产精品久久久久av福利动漫 | 国产很黄很色的视频 | 99热99热 | 狠狠的干狠狠的操 | 亚洲精品白浆高清久久久久久 | 国产精品久久三 | 中文字幕乱码在线播放 | 午夜视频在线观看一区二区三区 | 久久国产精品电影 | 黄色av一区二区三区 | 精品国产亚洲一区二区麻豆 | 伊香蕉大综综综合久久啪 | 精品视频国产 | 国产精品久久久久久久久免费看 | 久久综合色综合88 | 亚洲精品国偷拍自产在线观看蜜桃 | 91精品视频在线观看免费 | 欧洲在线免费视频 | 欧美另类高潮 | 99久久精品国产欧美主题曲 | 国产盗摄精品一区二区 | 国产日韩精品在线观看 | 特黄免费av | 国产亚洲综合性久久久影院 | 亚洲欧美国产精品 | 久久天堂影院 | 开心综合网 | 国产精品美女网站 | 成人av免费 | 精品美女在线视频 | 亚洲精品视频一 | 五月综合激情网 | 91香蕉视频污在线 | 亚洲精品乱码久久久久久按摩 | 日本午夜免费福利视频 | 欧美日韩精品在线视频 | 国产精品视频你懂的 | 最新成人av | 亚洲va天堂va欧美ⅴa在线 | 色婷av | 精品视频不卡 | 激情视频一区二区 | 日韩在线视频免费播放 | 国产一区二区三区免费视频 | 欧美污污视频 | 成人av中文字幕在线观看 | 91在线免费看片 | 国产69精品久久久久99尤 | 色综合咪咪久久网 | 亚洲在线成人精品 | 亚洲一区二区三区miaa149 | 九九天堂| 日韩综合在线观看 | 日本夜夜草视频网站 | 日韩专区一区二区 | 久久国产免 | 亚洲精品在线观看免费 | 亚洲人人av | 婷婷在线看 | 中文字幕免费一区二区 | 日韩专区在线观看 | 狠狠干狠狠久久 | 国产精品国产三级在线专区 | 国产麻豆剧传媒免费观看 | 国产v在线 | 2022中文字幕在线观看 | 欧美一区二区在线免费观看 | 99r精品视频在线观看 | 黄色国产成人 | 国产91免费在线 | 精品国产乱码久久久久 | 三级黄色片子 | 欧美日韩国产精品一区二区亚洲 | 日本资源中文字幕在线 | 一区二区三区日韩视频在线观看 | 亚洲综合视频在线观看 | 天天在线免费视频 | 日韩精品免费一区 | 久久只精品99品免费久23小说 | 免费在线播放视频 | 麻豆免费视频观看 | 亚洲精品午夜久久久久久久久久久 | 97精品国自产拍在线观看 | 亚洲午夜精品在线观看 | 天天干天天拍天天操 | www.人人草| 99久久99热这里只有精品 | 日韩v在线91成人自拍 | 成人一区影院 | 久久男人视频 | 色五月激情五月 | 天天综合操 | 激情久久一区二区三区 | 国产在线精品二区 | 91传媒免费观看 | 激情综合亚洲 | 国产裸体视频网站 | 欧洲精品久久久久毛片完整版 | 天堂网中文在线 | 日韩国产欧美在线播放 | av免费看看| 日韩精品久久一区二区三区 | 国产91电影在线观看 | 播五月婷婷 | 缴情综合网五月天 | 毛片一区二区 | 日韩国产精品久久久久久亚洲 | 亚洲免费国产视频 | 精品久久久影院 | 激情五月婷婷激情 | 在线观看网站你懂的 | 国产护士av | 国产91精品看黄网站在线观看动漫 | 久草av在线播放 | 国产精品一区二区果冻传媒 | 国产精品久久在线观看 | 草久中文字幕 | 国产精品久久久久av福利动漫 | 97操碰| 欧美十八| 国产精品成人免费一区久久羞羞 | 黄色片网站 | 久久久久这里只有精品 | av免费成人| 黄色av电影一级片 | 欧美日韩高清在线 |