国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

「新生手冊」:PyTorch分布式訓(xùn)練

共 26324字,需瀏覽 53分鐘

 ·

2021-04-06 15:37

↑ 點擊藍(lán)字 關(guān)注極市平臺

作者丨花花@知乎(已授權(quán))
來源丨h(huán)ttps://zhuanlan.zhihu.com/p/360405558
編輯丨極市平臺

極市導(dǎo)讀

 

本文重點介紹了PyTorch原生的分布式數(shù)據(jù)并行(DDP) 及其用法。 >>加入極市CV技術(shù)交流群,走在計算機視覺的最前沿

目錄

  • 0X01 分布式并行訓(xùn)練概述

  • 0X02 Pytorch分布式數(shù)據(jù)并行

  • 0X03 手把手漸進(jìn)式實戰(zhàn)

    • A. 單機單卡
    • B. 單機多卡DP
    • C. 多機多卡DDP
    • D. Launch / Slurm 調(diào)度方式
  • 0X04 完整框架 Distribuuuu

  • 0X05 Reference

文中所有教學(xué)代碼和日志見:Tutorialgithub.com

文中提到的框架見:Distribuuuugithub.com

希望本文對你有幫助

0X01 分布式并行訓(xùn)練概述

最常被提起,容易實現(xiàn)且使用最廣泛的,莫過于數(shù)據(jù)并行(Data Parallelism) 技術(shù),其核心思想是將大batch劃分為若干小barch分發(fā)到不同device并行計算,解決單GPU顯存不足的限制。與此同時,當(dāng)單GPU無法放下整個模型時,我們還需考慮 模型并行(Model / Pipeline Parallelism)。如考慮將模型進(jìn)行縱向切割,不同的Layers放在不同的device上。或是將某些模塊進(jìn)行橫向切割,通過矩陣運算進(jìn)行加速。當(dāng)然,還存在一些非并行的技術(shù)或者技巧,用于解決訓(xùn)練效率或者訓(xùn)練顯存不足等問題。

本文的重點是介紹PyTorch原生的分布式數(shù)據(jù)并行(DDP) 及其用法,其他的內(nèi)容,我們后面再聊(如果有機會的話qwq)。
這里我草率地將當(dāng)前深度學(xué)習(xí)的大規(guī)模分布式訓(xùn)練技術(shù)分為如下三類:

  • Data Parallelism (數(shù)據(jù)并行)

    • Naive:每個worker存儲一份model和optimizer,每輪迭代時,將樣本分為若干份分發(fā)給各個worker,實現(xiàn)并行計算
    • ZeRO: Zero Redundancy Optimizer,微軟提出的數(shù)據(jù)并行內(nèi)存優(yōu)化技術(shù),核心思想是保持Naive數(shù)據(jù)并行通信效率的同時,盡可能降低內(nèi)存占用(https://arxiv.org/abs/1910.02054
  • Model/Pipeline Parallelism (模型并行)

    • Naive: 縱向切割模型,將不同的layers放到不同的device上,按順序進(jìn)行正/反向傳播(https://pytorch.org/tutorials/intermediate/model_parallel_tutorial.html
    • GPipe:小批量流水線方式的縱向切割模型并行(https://proceedings.neurips.cc/paper/2019/file/093f65e080a295f8076b1c5722a46aa2-Paper.pdf
    • Megatron-LM:Tensor-slicing方式的模型并行加速(https://github.com/NVIDIA/Megatron-LM
  • Non-parallelism approach (非并行技術(shù))

    • Gradient Accumulation: 通過梯度累加的方式解決顯存不足的問題,常用于模型較大,單卡只能塞下很小的batch的并行訓(xùn)練中(https://www.zhihu.com/question/303070254
    • CPU Offload: 同時利用 CPU 和 GPU 內(nèi)存來訓(xùn)練大型模型,即存在GPU-CPU-GPU的 transfers操作(https://www.deepspeed.ai/tutorials/zero-offload/
    • etc.:還有很多不一一羅列(如Checkpointing, Memory Efficient Optimizer等)

不過這里我 強推 一下 DeepSpeed,微軟在2020年開源的一個對PyTorch的分布式訓(xùn)練進(jìn)行優(yōu)化的庫,讓訓(xùn)練百億參數(shù)的巨大模型成為可能,其提供的 3D-parallelism (DP+PP+MP)的并行技術(shù)組合,能極大程度降低大模型訓(xùn)練的硬件條件以及提高訓(xùn)練的效率

0X02 Pytorch分布式數(shù)據(jù)并行

將時間撥回2017年,我第一次接觸深度學(xué)習(xí),早期的TensorFlow使用的是PS(Parameter Server)架構(gòu),在結(jié)點數(shù)量線性增長的情況下,帶寬瓶頸格外明顯。而隨后百度將Ring-Allreduce技術(shù)運用到深度學(xué)習(xí)分布式訓(xùn)練,PyTorch1.0之后香起來的原因也是因為在分布式訓(xùn)練方面做了較大改動,適配多種通信后端,使用RingAllReduce架構(gòu)。

小提醒 ? ,確保你對PyTorch有一定的熟悉程度,此前提下,對如下內(nèi)容進(jìn)行學(xué)習(xí)和了解,基本上就能夠handle住大部分的數(shù)據(jù)并行任務(wù)了:

  • DataParallel 和 DistributedDataParallel 的原理和使用
  • 進(jìn)程組 和 torch.distributed.init_process_group 的原理和使用
  • 集體通信(Collective Communication) 的原理和使用

關(guān)于理論的東西,我寫了一大堆,最后又全刪掉了。原因是我發(fā)現(xiàn)已經(jīng)有足夠多的文章介紹 PS/Ring-AllReducePyTorch DP/DDP 的原理,給出具有代表性的幾篇:

  • PYTORCH DISTRIBUTED OVERVIEW(https://pytorch.org/tutorials/beginner/dist_overview.html
  • PyTorch 源碼解讀之 DP & DDP(https://zhuanlan.zhihu.com/p/343951042
  • Bringing HPC Techniques to Deep Learning(https://andrew.gibiansky.com/blog/machine-learning/baidu-allreduce/

0X03 手把手漸進(jìn)式實戰(zhàn)

那么接下來我們以Step by Step的方式進(jìn)行實踐,你可以直接通過下面的快速索引進(jìn)行跳轉(zhuǎn),大部分的解釋都包含在代碼中,每份代碼最后也有使用說明和訓(xùn)練Log記錄

  • 單機單卡 [snsc.py] https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/snsc.py

  • 單機多卡 (with DataParallel) [snmc_dp.py] https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/snmc_dp.py

  • 多機多卡 (with DistributedDataParallel)

    • torch.distributed.launch [mnmc_ddp_launch.py] https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/mnmc_ddp_launch.py
    • torch.multiprocessing [mnmc_ddp_mp.py] https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/mnmc_ddp_mp.py
    • Slurm Workload Manager [mnmc_ddp_slurm.py] https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/mnmc_ddp_slurm.py
    • ImageNet training example [imagenet.py] https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/imagenet.py

A. 單機單卡

Single Node Single GPU Card Training, 源碼見 snsc.py,后續(xù)我們會在此代碼上進(jìn)行修改。簡單看一下,單機單卡要做的就是定義網(wǎng)絡(luò),定義dataloader,定義loss和optimizer,開訓(xùn),很簡單的幾個步驟。

"""(SNSC) Single Node Single GPU Card Training"""import torchimport torch.nn as nnimport torchvisionimport torchvision.transforms as transforms
BATCH_SIZE = 256EPOCHS = 5
if __name__ == "__main__":
# 1. define network device = "cuda" net = torchvision.models.resnet18(num_classes=10) net = net.to(device=device)
# 2. define dataloader trainset = torchvision.datasets.CIFAR10( root="./data", train=True, download=True, transform=transforms.Compose( [ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize( (0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010) ), ] ), ) train_loader = torch.utils.data.DataLoader( trainset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True, )
# 3. define loss and optimizer criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD( net.parameters(), lr=0.01, momentum=0.9, weight_decay=0.0001, nesterov=True, )
print(" ======= Training ======= \n")
# 4. start to train net.train() for ep in range(1, EPOCHS + 1): train_loss = correct = total = 0
for idx, (inputs, targets) in enumerate(train_loader): inputs, targets = inputs.to(device), targets.to(device) outputs = net(inputs)
loss = criterion(outputs, targets) optimizer.zero_grad() loss.backward() optimizer.step()
train_loss += loss.item() total += targets.size(0) correct += torch.eq(outputs.argmax(dim=1), targets).sum().item()
if (idx + 1) % 50 == 0 or (idx + 1) == len(train_loader): print( " == step: [{:3}/{}] [{}/{}] | loss: {:.3f} | acc: {:6.3f}%".format( idx + 1, len(train_loader), ep, EPOCHS, train_loss / (idx + 1), 100.0 * correct / total, ) )
print("\n ======= Training Finished ======= \n")
"""usage:>>> python snsc.py
Files already downloaded and verified ======= Training =======
== step: [ 50/196] [1/5] | loss: 1.959 | acc: 28.633% == step: [100/196] [1/5] | loss: 1.806 | acc: 33.996% == step: [150/196] [1/5] | loss: 1.718 | acc: 36.987% == step: [196/196] [1/5] | loss: 1.658 | acc: 39.198% == step: [ 50/196] [2/5] | loss: 1.393 | acc: 49.578% == step: [100/196] [2/5] | loss: 1.359 | acc: 50.473% == step: [150/196] [2/5] | loss: 1.336 | acc: 51.372% == step: [196/196] [2/5] | loss: 1.317 | acc: 52.200% == step: [ 50/196] [3/5] | loss: 1.205 | acc: 56.102% == step: [100/196] [3/5] | loss: 1.185 | acc: 57.254% == step: [150/196] [3/5] | loss: 1.175 | acc: 57.755% == step: [196/196] [3/5] | loss: 1.165 | acc: 58.072% == step: [ 50/196] [4/5] | loss: 1.067 | acc: 60.914% == step: [100/196] [4/5] | loss: 1.061 | acc: 61.406% == step: [150/196] [4/5] | loss: 1.058 | acc: 61.643% == step: [196/196] [4/5] | loss: 1.054 | acc: 62.022% == step: [ 50/196] [5/5] | loss: 0.988 | acc: 64.852% == step: [100/196] [5/5] | loss: 0.983 | acc: 64.801% == step: [150/196] [5/5] | loss: 0.980 | acc: 65.052% == step: [196/196] [5/5] | loss: 0.977 | acc: 65.076%
======= Training Finished ======= """

B. 單機多卡DP

Single Node Multi-GPU Crads Training (with DataParallel),源碼見 snmc_dp.py, 和 snsc.py 對比一下,DP只需要花費最小的代價,既可以使用多卡進(jìn)行訓(xùn)練(其實就一行???),但是因為GIL鎖的限制,DP的性能是低于DDP的。

"""(SNMC) Single Node Multi-GPU Crads Training (with DataParallel)Try to compare with smsc.py and find out the differences."""import torchimport torch.nn as nnimport torchvisionimport torchvision.transforms as transforms
BATCH_SIZE = 256EPOCHS = 5
if __name__ == "__main__":
# 1. define network device = "cuda" net = torchvision.models.resnet18(pretrained=False, num_classes=10) net = net.to(device=device) # Use single-machine multi-GPU DataParallel, # you would like to speed up training with the minimum code change. net = nn.DataParallel(net)
# 2. define dataloader trainset = torchvision.datasets.CIFAR10( root="./data", train=True, download=True, transform=transforms.Compose( [ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize( (0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010) ), ] ), ) train_loader = torch.utils.data.DataLoader( trainset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True, )
# 3. define loss and optimizer criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD( net.parameters(), lr=0.01, momentum=0.9, weight_decay=0.0001, nesterov=True, )
print(" ======= Training ======= \n")
# 4. start to train net.train() for ep in range(1, EPOCHS + 1): train_loss = correct = total = 0
for idx, (inputs, targets) in enumerate(train_loader): inputs, targets = inputs.to(device), targets.to(device) outputs = net(inputs)
loss = criterion(outputs, targets) optimizer.zero_grad() loss.backward() optimizer.step()
train_loss += loss.item() total += targets.size(0) correct += torch.eq(outputs.argmax(dim=1), targets).sum().item()
if (idx + 1) % 50 == 0 or (idx + 1) == len(train_loader): print( " == step: [{:3}/{}] [{}/{}] | loss: {:.3f} | acc: {:6.3f}%".format( idx + 1, len(train_loader), ep, EPOCHS, train_loss / (idx + 1), 100.0 * correct / total, ) )
print("\n ======= Training Finished ======= \n")
"""usage: 2GPUs for training>>> CUDA_VISIBLE_DEVICES=0,1 python snmc_dp.py
Files already downloaded and verified ======= Training =======
== step: [ 50/196] [1/5] | loss: 1.992 | acc: 26.633% == step: [100/196] [1/5] | loss: 1.834 | acc: 32.797% == step: [150/196] [1/5] | loss: 1.742 | acc: 36.201% == step: [196/196] [1/5] | loss: 1.680 | acc: 38.578% == step: [ 50/196] [2/5] | loss: 1.398 | acc: 49.062% == step: [100/196] [2/5] | loss: 1.380 | acc: 49.953% == step: [150/196] [2/5] | loss: 1.355 | acc: 50.810% == step: [196/196] [2/5] | loss: 1.338 | acc: 51.428% == step: [ 50/196] [3/5] | loss: 1.242 | acc: 55.727% == step: [100/196] [3/5] | loss: 1.219 | acc: 56.801% == step: [150/196] [3/5] | loss: 1.200 | acc: 57.195% == step: [196/196] [3/5] | loss: 1.193 | acc: 57.328% == step: [ 50/196] [4/5] | loss: 1.105 | acc: 61.102% == step: [100/196] [4/5] | loss: 1.098 | acc: 61.082% == step: [150/196] [4/5] | loss: 1.087 | acc: 61.354% == step: [196/196] [4/5] | loss: 1.086 | acc: 61.426% == step: [ 50/196] [5/5] | loss: 1.002 | acc: 64.039% == step: [100/196] [5/5] | loss: 1.006 | acc: 63.977% == step: [150/196] [5/5] | loss: 1.009 | acc: 63.935% == step: [196/196] [5/5] | loss: 1.005 | acc: 64.024%
======= Training Finished ======= """

C. 多機多卡DDP

Okay, 下面進(jìn)入正題,來看一下多機多卡怎么做,雖然上面給出的文章都講得很明白,但有些概念還是有必要提一下:

  • 進(jìn)程組的相關(guān)概念

    • GROUP:進(jìn)程組,大部分情況下DDP的各個進(jìn)程是在同一個進(jìn)程組下
    • WORLD_SIZE:總的進(jìn)程數(shù)量 (原則上一個process占用一個GPU是較優(yōu)的)
    • RANK:當(dāng)前進(jìn)程的序號,用于進(jìn)程間通訊,rank = 0 的主機為 master 節(jié)點
    • LOCAL_RANK:當(dāng)前進(jìn)程對應(yīng)的GPU號

舉個栗子 :4臺機器(每臺機器8張卡)進(jìn)行分布式訓(xùn)練
通過 init_process_group() 對進(jìn)程組進(jìn)行初始化
初始化后 可以通過 get_world_size() 獲取到 world size
在該例中為32, 即有32個進(jìn)程,其編號為0-31<br/>通過 get_rank() 函數(shù)可以進(jìn)行獲取 在每臺機器上,local rank均為0-8,這是 local rank 與 rank 的區(qū)別, local rank 會對應(yīng)到實際的 GPU ID 上
(單機多任務(wù)的情況下注意CUDA_VISIBLE_DEVICES的使用
控制不同程序可見的GPU devices)

  • DDP的基本用法 (代碼編寫流程)

    • 使用 torch.distributed.init_process_group 初始化進(jìn)程組
    • 使用 torch.nn.parallel.DistributedDataParallel 創(chuàng)建 分布式模型
    • 使用 torch.utils.data.distributed.DistributedSampler 創(chuàng)建 DataLoader
    • 調(diào)整其他必要的地方(tensor放到指定device上,S/L checkpoint,指標(biāo)計算等)
    • 使用 torch.distributed.launch / torch.multiprocessing 或 slurm 開始訓(xùn)練
  • 集體通信的使用

    • torch.distributed
    • NCCL-Woolley
    • scaled_all_reduce
    • 將各卡的信息進(jìn)行匯總,分發(fā)或平均等操作,需要使用集體通訊操作(如算accuracy或者總loss時候需要用到allreduce),可參考:

  • 不同啟動方式的用法

    • torch.distributed.launch:mnmc_ddp_launch.py
    • torch.multiprocessing:mnmc_ddp_mp.py
    • Slurm Workload Manager:mnmc_ddp_slurm.py
"""(MNMC) Multiple Nodes Multi-GPU Cards Training    with DistributedDataParallel and torch.distributed.launchTry to compare with [snsc.py, snmc_dp.py & mnmc_ddp_mp.py] and find out the differences."""
import os
import torchimport torch.distributed as distimport torch.nn as nnimport torchvisionimport torchvision.transforms as transformsfrom torch.nn.parallel import DistributedDataParallel as DDP
BATCH_SIZE = 256EPOCHS = 5

if __name__ == "__main__":
# 0. set up distributed device rank = int(os.environ["RANK"]) local_rank = int(os.environ["LOCAL_RANK"]) torch.cuda.set_device(rank % torch.cuda.device_count()) dist.init_process_group(backend="nccl") device = torch.device("cuda", local_rank)
print(f"[init] == local rank: {local_rank}, global rank: {rank} ==")
# 1. define network net = torchvision.models.resnet18(pretrained=False, num_classes=10) net = net.to(device) # DistributedDataParallel net = DDP(net, device_ids=[local_rank], output_device=local_rank)
# 2. define dataloader trainset = torchvision.datasets.CIFAR10( root="./data", train=True, download=False, transform=transforms.Compose( [ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize( (0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010) ), ] ), ) # DistributedSampler # we test single Machine with 2 GPUs # so the [batch size] for each process is 256 / 2 = 128 train_sampler = torch.utils.data.distributed.DistributedSampler( trainset, shuffle=True, ) train_loader = torch.utils.data.DataLoader( trainset, batch_size=BATCH_SIZE, num_workers=4, pin_memory=True, sampler=train_sampler, )
# 3. define loss and optimizer criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD( net.parameters(), lr=0.01 * 2, momentum=0.9, weight_decay=0.0001, nesterov=True, )
if rank == 0: print(" ======= Training ======= \n")
# 4. start to train net.train() for ep in range(1, EPOCHS + 1): train_loss = correct = total = 0 # set sampler train_loader.sampler.set_epoch(ep)
for idx, (inputs, targets) in enumerate(train_loader): inputs, targets = inputs.to(device), targets.to(device) outputs = net(inputs)
loss = criterion(outputs, targets) optimizer.zero_grad() loss.backward() optimizer.step()
train_loss += loss.item() total += targets.size(0) correct += torch.eq(outputs.argmax(dim=1), targets).sum().item()
if rank == 0 and ((idx + 1) % 25 == 0 or (idx + 1) == len(train_loader)): print( " == step: [{:3}/{}] [{}/{}] | loss: {:.3f} | acc: {:6.3f}%".format( idx + 1, len(train_loader), ep, EPOCHS, train_loss / (idx + 1), 100.0 * correct / total, ) ) if rank == 0: print("\n ======= Training Finished ======= \n")
"""usage:>>> python -m torch.distributed.launch --help
exmaple: 1 node, 4 GPUs per node (4GPUs)>>> python -m torch.distributed.launch \ --nproc_per_node=4 \ --nnodes=1 \ --node_rank=0 \ --master_addr=localhost \ --master_port=22222 \ mnmc_ddp_launch.py
[init] == local rank: 3, global rank: 3 ==[init] == local rank: 1, global rank: 1 ==[init] == local rank: 0, global rank: 0 ==[init] == local rank: 2, global rank: 2 == ======= Training =======
== step: [ 25/49] [0/5] | loss: 1.980 | acc: 27.953% == step: [ 49/49] [0/5] | loss: 1.806 | acc: 33.816% == step: [ 25/49] [1/5] | loss: 1.464 | acc: 47.391% == step: [ 49/49] [1/5] | loss: 1.420 | acc: 48.448% == step: [ 25/49] [2/5] | loss: 1.300 | acc: 52.469% == step: [ 49/49] [2/5] | loss: 1.274 | acc: 53.648% == step: [ 25/49] [3/5] | loss: 1.201 | acc: 56.547% == step: [ 49/49] [3/5] | loss: 1.185 | acc: 57.360% == step: [ 25/49] [4/5] | loss: 1.129 | acc: 59.531% == step: [ 49/49] [4/5] | loss: 1.117 | acc: 59.800%
======= Training Finished =======
exmaple: 1 node, 2tasks, 4 GPUs per task (8GPUs)>>> CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch \ --nproc_per_node=4 \ --nnodes=2 \ --node_rank=0 \ --master_addr="10.198.189.10" \ --master_port=22222 \ mnmc_ddp_launch.py
>>> CUDA_VISIBLE_DEVICES=4,5,6,7 python -m torch.distributed.launch \ --nproc_per_node=4 \ --nnodes=2 \ --node_rank=1 \ --master_addr="10.198.189.10" \ --master_port=22222 \ mnmc_ddp_launch.py
======= Training =======
== step: [ 25/25] [0/5] | loss: 1.932 | acc: 29.088% == step: [ 25/25] [1/5] | loss: 1.546 | acc: 43.088% == step: [ 25/25] [2/5] | loss: 1.424 | acc: 48.032% == step: [ 25/25] [3/5] | loss: 1.335 | acc: 51.440% == step: [ 25/25] [4/5] | loss: 1.243 | acc: 54.672%
======= Training Finished =======
exmaple: 2 node, 8 GPUs per node (16GPUs)>>> python -m torch.distributed.launch \ --nproc_per_node=8 \ --nnodes=2 \ --node_rank=0 \ --master_addr="10.198.189.10" \ --master_port=22222 \ mnmc_ddp_launch.py
>>> python -m torch.distributed.launch \ --nproc_per_node=8 \ --nnodes=2 \ --node_rank=1 \ --master_addr="10.198.189.10" \ --master_port=22222 \ mnmc_ddp_launch.py
[init] == local rank: 5, global rank: 5 ==[init] == local rank: 3, global rank: 3 ==[init] == local rank: 2, global rank: 2 ==[init] == local rank: 4, global rank: 4 ==[init] == local rank: 0, global rank: 0 ==[init] == local rank: 6, global rank: 6 ==[init] == local rank: 7, global rank: 7 ==[init] == local rank: 1, global rank: 1 == ======= Training =======
== step: [ 13/13] [0/5] | loss: 2.056 | acc: 23.776% == step: [ 13/13] [1/5] | loss: 1.688 | acc: 36.736% == step: [ 13/13] [2/5] | loss: 1.508 | acc: 44.544% == step: [ 13/13] [3/5] | loss: 1.462 | acc: 45.472% == step: [ 13/13] [4/5] | loss: 1.357 | acc: 49.344%
======= Training Finished ======= """

D. Launch / Slurm 調(diào)度方式

這里單獨用代碼 imagenet.py 講一下不同的啟動方式,更詳細(xì)的內(nèi)容請看源碼。

我們來看一下這個 setup_distributed 函數(shù):

  • 通過 srun 產(chǎn)生的程序在環(huán)境變量中會有 SLURM_JOB_ID, 以判斷是否為slurm的調(diào)度方式
  • rank 通過 SLURM_PROCID 可以拿到
  • world size 實際上就是進(jìn)程數(shù), 通過 SLURM_NTASKS 可以拿到
  • IP地址通過 subprocess.getoutput(f"scontrol show hostname {node_list} | head -n1") 巧妙得到,栗子來源于 MMCV
  • 否則,就使用launch進(jìn)行調(diào)度,直接通過 os.environ["RANK"] 和 os.environ["WORLD_SIZE"] 即可拿到 rank 和 world size
# 此函數(shù)可以直接移植到你的程序中,動態(tài)獲取IP,使用很方便# 默認(rèn)支持launch 和 srun 兩種方式def setup_distributed(backend="nccl", port=None):    """Initialize distributed training environment.    support both slurm and torch.distributed.launch    see torch.distributed.init_process_group() for more details    """    num_gpus = torch.cuda.device_count()
if "SLURM_JOB_ID" in os.environ: rank = int(os.environ["SLURM_PROCID"]) world_size = int(os.environ["SLURM_NTASKS"]) node_list = os.environ["SLURM_NODELIST"] addr = subprocess.getoutput(f"scontrol show hostname {node_list} | head -n1") # specify master port if port is not None: os.environ["MASTER_PORT"] = str(port) elif "MASTER_PORT" not in os.environ: os.environ["MASTER_PORT"] = "29500" if "MASTER_ADDR" not in os.environ: os.environ["MASTER_ADDR"] = addr os.environ["WORLD_SIZE"] = str(world_size) os.environ["LOCAL_RANK"] = str(rank % num_gpus) os.environ["RANK"] = str(rank) else: rank = int(os.environ["RANK"]) world_size = int(os.environ["WORLD_SIZE"])
torch.cuda.set_device(rank % num_gpus)
dist.init_process_group( backend=backend, world_size=world_size, rank=rank, )

那提交任務(wù)就可以靈活切換,下面給出32卡使用Slurm調(diào)度,以及8卡單結(jié)點的Launch調(diào)度:

# ======== slurm 調(diào)度方式 ========# 32張GPU,4個node,每個node8張卡,8192的batch size,32個進(jìn)程# see:https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/imagenet.pyslurm example:     32GPUs (batch size: 8192)    128k / (256*32) -> 157 itertaion>>> srun --partition=openai -n32 --gres=gpu:8 --ntasks-per-node=8 --job-name=slrum_test \    python -u imagenet.py[init] == local rank: 7, global rank: 7 ==[init] == local rank: 1, global rank: 1 ==[init] == local rank: 4, global rank: 4 ==[init] == local rank: 2, global rank: 2 ==[init] == local rank: 6, global rank: 6 ==[init] == local rank: 3, global rank: 3 ==[init] == local rank: 5, global rank: 5 ==[init] == local rank: 4, global rank: 12 ==[init] == local rank: 1, global rank: 25 ==[init] == local rank: 5, global rank: 13 ==[init] == local rank: 6, global rank: 14 ==[init] == local rank: 0, global rank: 8 ==[init] == local rank: 1, global rank: 9 ==[init] == local rank: 2, global rank: 10 ==[init] == local rank: 3, global rank: 11 ==[init] == local rank: 7, global rank: 15 ==[init] == local rank: 5, global rank: 29 ==[init] == local rank: 2, global rank: 26 ==[init] == local rank: 3, global rank: 27 ==[init] == local rank: 0, global rank: 24 ==[init] == local rank: 7, global rank: 31 ==[init] == local rank: 6, global rank: 30 ==[init] == local rank: 4, global rank: 28 ==[init] == local rank: 0, global rank: 16 ==[init] == local rank: 5, global rank: 21 ==[init] == local rank: 7, global rank: 23 ==[init] == local rank: 1, global rank: 17 ==[init] == local rank: 6, global rank: 22 ==[init] == local rank: 3, global rank: 19 ==[init] == local rank: 2, global rank: 18 ==[init] == local rank: 4, global rank: 20 ==[init] == local rank: 0, global rank: 0 ==            =======  Training  =======    == step: [ 40/157] [0/1] | loss: 6.781 | acc:  0.703%   == step: [ 80/157] [0/1] | loss: 6.536 | acc:  1.260%   == step: [120/157] [0/1] | loss: 6.353 | acc:  1.875%   == step: [157/157] [0/1] | loss: 6.207 | acc:  2.465%

# ======== launch 調(diào)度方式 ========# nproc_per_node: 每個node的卡數(shù)# nnodes: node數(shù)量# node_rank:node編號,從0開始# see: https://github.com/BIGBALLON/distribuuuu/blob/master/tutorial/mnmc_ddp_launch.pydistributed.launch example: 8GPUs (batch size: 2048) 128k / (256*8) -> 626 itertaion>>> python -m torch.distributed.launch \ --nproc_per_node=8 \ --nnodes=1 \ --node_rank=0 \ --master_addr=localhost \ --master_port=22222 \ imagenet.py[init] == local rank: 0, global rank: 0 ==[init] == local rank: 2, global rank: 2 ==[init] == local rank: 6, global rank: 6 ==[init] == local rank: 5, global rank: 5 ==[init] == local rank: 7, global rank: 7 ==[init] == local rank: 4, global rank: 4 ==[init] == local rank: 3, global rank: 3 ==[init] == local rank: 1, global rank: 1 == ======= Training ======= == step: [ 40/626] [0/1] | loss: 6.821 | acc: 0.498% == step: [ 80/626] [0/1] | loss: 6.616 | acc: 0.869% == step: [120/626] [0/1] | loss: 6.448 | acc: 1.351% == step: [160/626] [0/1] | loss: 6.294 | acc: 1.868% == step: [200/626] [0/1] | loss: 6.167 | acc: 2.443% == step: [240/626] [0/1] | loss: 6.051 | acc: 3.003% == step: [280/626] [0/1] | loss: 5.952 | acc: 3.457% == step: [320/626] [0/1] | loss: 5.860 | acc: 3.983% == step: [360/626] [0/1] | loss: 5.778 | acc: 4.492% == step: [400/626] [0/1] | loss: 5.700 | acc: 4.960% == step: [440/626] [0/1] | loss: 5.627 | acc: 5.488% == step: [480/626] [0/1] | loss: 5.559 | acc: 6.013% == step: [520/626] [0/1] | loss: 5.495 | acc: 6.520% == step: [560/626] [0/1] | loss: 5.429 | acc: 7.117% == step: [600/626] [0/1] | loss: 5.371 | acc: 7.580% == step: [626/626] [0/1] | loss: 5.332 | acc: 7.907%

0X04 完整框架 Distribuuuu

Distribuuuu 是我閑(沒)來(事)無(找)事(事)寫的一個完整的純DDP分類訓(xùn)練框架,足夠精簡且足夠有效率。支持launch和srun兩種啟動方式,可以作為新手學(xué)習(xí)和魔改的樣板工程。

# 1 node, 8 GPUspython -m torch.distributed.launch \    --nproc_per_node=8 \    --nnodes=1 \    --node_rank=0 \    --master_addr=localhost \    --master_port=29500 \    train_net.py --cfg config/resnet18.yaml# see srun --help # and https://slurm.schedmd.com/ for details
# example: 64 GPUs# batch size = 64 * 128 = 8192# itertaion = 128k / 8192 = 156 # lr = 64 * 0.1 = 6.4
srun --partition=openai-a100 \ -n 64 \ --gres=gpu:8 \ --ntasks-per-node=8 \ --job-name=Distribuuuu \ python -u train_net.py --cfg config/resnet18.yaml \ TRAIN.BATCH_SIZE 128 \ OUT_DIR ./resnet18_8192bs \ OPTIM.BASE_LR 6.4

下面是用 Distribuuuu 做的一些簡單的實驗,botnet50 是復(fù)現(xiàn)了今年比較火的 Transformer+CNN 的文章 Bottleneck Transformers for Visual 的精度,主要是證明這個框架的可用性, resnet18最后小測了 64卡/16384BS 的訓(xùn)練, 精度尚可。另外稍微強調(diào)一下SyncBN不要隨便亂用,如果單卡Batch已經(jīng)足夠大的情況下不需要開SyncBN。

Distribuuuu benchmark (ImageNet)

如果是出于學(xué)習(xí)目的,想進(jìn)行一些魔改和測試,可以試試我的Distribuuuu(https://github.com/BIGBALLON/distribuuuu),因為足夠簡單很容易改吖 ,如果你想做research的話推薦用FAIR的 pycls, 有model zoo 而且代碼足夠優(yōu)雅。另外,打比賽的話就不建議自己造輪子了,分類可直接魔改 pycls 或 MMClassification, 檢測就魔改 MMDetection 和 Detectron2 就完事啦

Reference

  • PYTORCH DISTRIBUTED OVERVIEW
  • PyTorch 源碼解讀之 DP & DDP
  • Bringing HPC Techniques to Deep Learning
  • Parameter Servers
  • Ring-Allreduce:Launching and configuring distributed data parallel applications
  • PyTorch Distributed Training
  • Kill PyTorch Distributed Training Processes
  • NCCL: ACCELERATED MULTI-GPUCOLLECTIVE COMMUNICATIONS
  • WRITING DISTRIBUTED APPLICATIONS WITH PYTORCH
  • PyTorch Distributed: Experiences on Accelerating Data Parallel Training
  • Pytorch多機多卡分布式訓(xùn)練
  • Launching and configuring distributed data parallel applications

那今天就到這里吧,如果你有問題,用任何方式聯(lián)系我都闊以,我康到就會解答啦(如果我會的話啦) ?? ,另外如果大家感興趣的話,康康要不要出第二篇(如果有時間的話啦) ??

推薦閱讀


詳解PyTorch編譯并調(diào)用自定義CUDA算子的三種方式

2021-03-27

PyTorch 1.8來了!正式支持AMD GPU,煉丹不必NVIDIA

2021-03-05

Pytorch翻車記錄:單卡改多卡踩坑記!

2021-01-17



# CV技術(shù)社群邀請函 #

△長按添加極市小助手
添加極市小助手微信(ID : cvmart2)

備注:姓名-學(xué)校/公司-研究方向-城市(如:小極-北大-目標(biāo)檢測-深圳)


即可申請加入極市目標(biāo)檢測/圖像分割/工業(yè)檢測/人臉/醫(yī)學(xué)影像/3D/SLAM/自動駕駛/超分辨率/姿態(tài)估計/ReID/GAN/圖像增強/OCR/視頻理解等技術(shù)交流群


每月大咖直播分享、真實項目需求對接、求職內(nèi)推、算法競賽、干貨資訊匯總、與 10000+來自港科大、北大、清華、中科院、CMU、騰訊、百度等名校名企視覺開發(fā)者互動交流~


△點擊卡片關(guān)注極市平臺,獲取最新CV干貨

覺得有用麻煩給個在看啦~  
瀏覽 69
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 色哟哟无码精品一区二区三区| 成人精品一区二区区别解析| 少妇人妻一级A毛片| 北条麻妃在线观看香蕉| 免费在线观看黄色视频网站| 在线黄色小视频| 亚洲欧美视频一区| 男女福利视频| 吴梦梦一区二区在线观看| 欧美在线观看网站18| 青春草在线免费视频| 国产精品成人在线| 91国黄色毛片在线观看| 人妻人人妻| 99精品视频在线观看免费| 丁香五月婷婷五月天| 亚洲色图自拍| 久久久久久久久久久久成人| 国产欧美精品在线观看| 亚洲人妻av| 9i看片成人免费视频| 江苏妇搡BBB搡BBBB| 国产美女做爱视频| 野花Av| 国产成人片色情AAAA片| 草逼免费视频| 青青成人| 日韩毛片中文字幕| 免费看三级网站| 熟妇女人妻丰满少妇中文字幕| 99视频精品在线| 怡红院麻豆| 国产一级a毛一级做a爱| 大肉大捧一出免费观看| 国产成人AV免费无码| 最近最火中文字幕mv歌词| 中文乱码在线观看| 白虎高清无码大尺度免费在线观看 | 国产十八岁在线观看| 你懂的网址在线观看| 亚洲精品一二| 亚洲人成电影网| 二级黄色视频| 西西人体大胆ww4444图片 | 成人性生交大片免费看小芳| 日本A在线| 九九九精品| 精品伊人久久| www.俺去也| 色丁香视频在线观看的| 丰满少妇在线观看网站| 亚洲一级AV| 国产69精品久久久久久| 免看一级a毛片一片成人不卡| 91av在线观看视频| 欧美成人乱码一区二区三区 | 成人a片视频| AV第一福利大全导航| 欧美日韩一级二级三级| 日本家庭乱伦视频| 欧美日韩精品一区二区三区| 精品国产va久久久久久| 欧美一卡二卡| 狠狠综合| 十八禁无码网站在线观看| 91人妻一区二区| 国产麻豆精品成人毛片| 日本色色色| 欧洲第一无人区观看| 狠狠操2019| 国产老女人农村HD| 欧美男女操逼视频| 先锋资源男人站| 久久久久久一| 一本色道88久久加勒比精品| 亚洲婷婷三级成人网| 无码一区二区三区四区五区| 国产精品久久久久久久久久王安宇| 人人操大香蕉| 国产乱子伦视频国产印度| 人人摸人人插| 国产精品成人一区二区| 神马午夜激情| 亚洲卡一卡二| 国产黄色一级电影| 五月天婷婷在线观看| 麻豆AV免费看| 苍井空中文字幕在线观看| 色婷操逼| 大香蕉精品欧美色综合2025| 午夜黄电影| 躁BBB躁BBB躁BBBBBB| 熟妇无码| 五月天黄色电影| 日本色综合| 亚洲精品黄色| 天堂免费视频| 天天日天天操天天射| 日韩无码电影网站| 亚洲一级内射| 91狠狠综合久久久| 大香蕉三级片| 在线观看中文字幕无码| 亚洲AV成人片无码网站| 亚洲免费人妻| 日本少妇午夜福利| 骚逼av| 国产女人18水真多18精品一级做 | av不卡在线观看| 少妇厨房愉情理伦BD在线观| 丝瓜视频黄| 台湾精品一区二区三区| 大香蕉av在线观看| 在线免费看黄视频| 午夜综合在线| 要操逼网| 香蕉视频一区| 思思在线视频| 9l农村站街老熟女| 欧美性少妇| 欧美Aⅴ| 热久久免费| 五月婷婷视频在线观看| 色狠狠AV| 加勒比无码综合| 午夜AV福利影院| 思思热在线视频播放| 亚洲aaaaaa| 精品国产一二三区| 成人A片免费在线观看| 亚洲天堂AV在线观看| 丁香五月天激情网| 国产69久久精品成人看| 日韩一区二区不卡| 99视频这里有精品| 欧美精品人妻| 一级A黄色片| 亚洲午夜视频在线观看| 日韩乱伦小说| 亚洲欧美日韩在线| 无码啪啪| 女人久久久久| 国产白嫩精品久久久久久| 超碰碰97| 狠狠狠狠狠狠狠狠| 无码AV免费观看| 免费观看在线黄片| 国产一区二区波多野结衣| 日本黄色视频大全| 特黄AAAAAAAAA真人毛片| 性爱麻豆| AV中文字幕网| 伊人91| 水果派av| 黄色内射在线播放| 大香蕉伊人成人网| 水果派解说AV无码一区| 婷婷情色| 日韩专区在线观看| 精品欧美一区二区三区久久久| 婷婷五月天综合| 秋霞午夜| 国产AA| 黄网站在线观看| 精品国产免费观看久久久_久久天天| 久久538| 欧美18成人| 国产老女人农村HD| 欧美三级理论片| 黄片视频国产| 欧美成人激情视频| 中文字幕欧美在线| 亚洲V| 韩国午夜电影| 午夜精品18视频国产| 免费的黄色视频在线观看| 中文字幕日韩一级| 亚洲AV大片| 精品国产一区二区三区久久久蜜月 | 嫩草久久99www亚洲红桃| 人人艹人人| 亚洲黄色免费在线观看| 日韩免费在线| 亚洲天堂2014| 国产无码一二三区| 国产噜噜噜噜久久久久久久久 | 久草免费在线视频| 在线观看免费黄色视频| 樱桃码一区二区三区| 国精品无码一区二区三区在线秋菊 | 黄片一区二区| 在线成人一区二区| 青青操原| 亚洲AV无码成人精品区久| 少妇推油呻吟白浆啪啪成人片| 亚洲无码大全| 国产91在线播放| 午夜黄片| 五月天深爱激情网| 四虎影院色| 成人一卡二卡| 亚州AV无码| 国产又粗又大又爽91嫩草| 一级无码A片| 精品三级在线观看| 影音av资源| 国产色在线| 三级视频网站| 狼友视频在线看| 伊人黄色视频| 粉嫩小泬BBBB免费看-百度| 亚洲操逼片| 欧美成人毛片AAAAAA| 91福利视频网| 91熊猫| 日本欧美一级| 亚洲一区中文字幕| 天堂素人约啪| 制服丝袜一区| 亚洲丁香网| 婷婷视频在线观看| 精品久久久久久久| 国精产品九九国精产品| 亚洲中文无码电影| 一区二区三区黄色| 18国产免费视频| 爱无码| 免费在线观看无码| 日韩大香蕉在线| 欧美、日韩、中文、制服、人妻 | 免费在线观看黄色网址| 婷婷五月欧美| 久操视频网| 亚洲天堂一级片| 久久久精品淫秽色情| 高清无码专区| 中文字幕在线不卡| 久久永久免费精品人妻专区| 精品中文字幕视频| 日韩爱爱免费视频| 成年人观看视频| 开心色婷婷| 91无码人妻精品一区二区三区四 | 正在播放ADN156松下纱荣子| 欧美老女人性| 天天射网| 欧美在线视频99| 五月色婷婷综合| 亚洲精品秘一区二区三区在线观看| 中文字幕视频在线播放| 撸一撸成人在线做爱视频。| 大肉大捧一进一出免费阅读| 午夜乱论| 一级欧美一级日韩| 色婷婷Av一区| 久久久久久91| 婷婷丁香激情五月天| 亚洲国产成人精品综合99| 日韩免费视频一区| a片在线视频| 亚洲福利视频97| 日韩不卡av| 亚洲成人色色| 日韩免费在线| 日韩精品观看| 亚洲图片小说区| 欧美操B视频| 欧美国产日韩视频| 丁香婷婷激情| 一区色| 思思热在线视频播放| 京东热av| 国产ts在线| 西西西444www无码视频| 国产精品国产三级国产专区53| 91久久国产综合| 黄色视频日韩| 国产手机拍视频推荐2023| 欧美精品一二三区| xxx久久| 3D动漫精品一区二区在线播放免费| 99久久久久久久| 久久91人妻无码精品蜜桃HD| 亚洲高清视频免费| 国产一区二区AV| 中文字幕在线播放AV| 91人妻人人澡人人爽人人爽| 久久精品国产视频| 国产午夜福利免费视频在线观看| 午夜福利国产| 一区精品| 国产福利电影在线| 丁香在线视频| 美国无码黄片| 久色视频在线| 69视频网| 96精品久久久久久久久久| 操逼中文字幕| 亚洲第一伊人| 日韩精品视频免费| 中文字幕人妻互换av久久| 黃色A片一級二級三級免費久久久| 亚洲人成人无码.www粉色| 国产激情网| 日本成人A| 在线观看免费黄色| 91人妻无码| 全国男人的天堂网站| 国产精品1区| 青青三级片| 天天无码视频| 在线视频你懂得| 无码欧美精品一区二区| 日日夜夜AV| 日本三级片视频不卡| 91在线视频免费| 中文字幕在线网站| 91精品国产综合久久久蜜臀图片 | 成人毛片一区二区三区| 欧美性性生交XXXXX无码| 午夜毛片| 亚洲视频精品| 亚洲成人视频网站| 亚洲日韩在线视频观看| 91精品久久久久久粉嫩| 西西www444无码免费视频| 人人免费操| 久草天堂| 在线免费中文字幕| 黄片aaa| 欧洲在线观看| 制服乱伦| 西西人体大胆ww4444图片| 精品一区二区三区在线观看| 亚洲精品成人| 国产毛片久久久久久久| 91九色蝌蚪| 3D动漫操逼视频| 国内精品久久久| 日韩无码黄色电影| www.一区二区| av国产精品| 亚洲AV无码国产综合专区| 国产精品果冻传媒| 久久久精品欧美| 国产在线小视频| 天天操天天日天天操| 中文字幕无码在线视频| 搡BBB搡BBBB搡BBBB'| 天天操夜夜操狠狠操| 亚洲成人视频在线播放| 91久久香蕉囯产熟女线看蜜桃| 亚洲高清视频一区| 日本久久综合| 亚洲一线在线观看| 国产在线网址| 另类老妇性bbwbbwbbw| 精品乱伦视频| 天天操免费| 久久久久成人精品无码| 亚洲国产精品成人综合色五月 | 日韩精品在线视频观看| 久久久久久久久久久高清毛片一级| 人人人爽| 97超碰在线免费观看| 99热99re6国产线播放| 婷婷中文在线| 免费在线a视频| 中文在线字幕电视剧免费平台| 三级乱伦视频| www.99爱| 精品AAA| jiujiuav| 懂色av蜜臀av粉嫩av分| 在线视频你懂得| 嫩BBB槡BBBB槡BBBB免费视频| 国产欧美自拍| gogogo日本免费观看高清电视剧的注意 | 日韩艹| 熟女视频网| 大香蕉在线网| 国产av小电影| 安徽扫搡BBBB揉BBBB| 另类无码| 国产精品操逼| 国产精品国产三级国产专区53| 久久精品电影| 国产精品五月天| 99成人乱码一区二区三区在线 | 日韩操b| 伊人逼逼| www.91av| 国内久久婷婷| 国产性爱一级片| 国产女人18毛片水18精| 在线观看成人三级片| 免费无码毛片| 欧美一级片免费观看| 成人免费版欧美州| www黄色com| 成人视频网站在线观看| AV天天干| 亚洲天堂2014| 久久午夜影院| 免费的黄色视频网站| 国产传媒在线观看| 色色色无码| 久久免费在线视频| av在线资源网站| www.一区二区| 蜜桃免费网站| 天天激情| 色婷婷久综合久久一本国产AV| 青青操逼| 最近中文字幕在线中文字幕7| 777欧美| 天堂在线中文字幕| 青青无码视频| 色噜噜狠狠色综无码久久合欧美| 69视频在线播放| 久久久精品黄色网址| 亚洲成人第一页| 不卡AV在线播放| 最近中文字幕免费MV第一季歌词怀孕| 日韩无码视频一区二区| 四虎永久www成人影院| 国产九九在线视频| 奇米影视av| 成人毛片在线| 亚洲色涩| 亚洲一级二级| 国产精品婷婷午夜在线观看| 欧美精品操逼| 中文字幕第五页| 免费成人一级片| 婷婷开心色四房播播免费| 久草免费电影| 天堂视频在线观看亚洲美女| 午夜无码视频| 人妻无码一区二区| 亚洲网站免费在线观看| 操逼免费网站| 亚洲成a| 国产精品一级无码免费播放| 日韩AV小说| 国产精品秘ThePorn| 四虎2025在线51| 男人天堂色男人| 亚洲日韩在线看| 国产成人秘一区二区三区东京热| 国产9熟妇视频网站| 亚洲欧美另类图片| 无码一道本| 伊人精品A片一区二区三区| 成人尤物网站| 亚洲51| 中文字幕成人在线观看| 国产高清精品无码| 久久亚洲天堂| 柠檬AV导航| 777国产盗摄偷窥精品0000| 老司机免费视频| 天天射夜夜操| 毛片网站大全| 水果派av解说| 国产精品第二页| 色婷婷Av一区| 高清无码18| 久久久人妻熟妇精品无码蜜桃| 特黄视频在线观看| 狼友视频在线观看18| 国产9熟妇视频网站| 欧美一级片网站| 成人视频网站在线观看| 中文字幕在线视频免费观看| 在线国产激情视频| 欧美操B电影| 俺去啦在线视频| 特级西西西88大胆无码| 美女做爱在线观看| 韩国精品无码一区二区三区18| 中文字幕亚洲视频在线观看| 操一线天逼| 欧美级毛片一进一出| 中文区中文字幕免费看| 婷婷五月亚洲精品AAA片在| 一区二区亚洲| 大荫蒂hd大荫蒂视频| 99久久精品国产成人一区二区 | www.色老板| 欧美肉大捧一进一出小说| 超碰人人爱人人操| 日韩视频播放在线综合| 操操日| 天天天天天天天干| 国产一级A片在线观看| 国产一级18片视频| 丁香五月天AV| 精品999999| 求毛片网址| 中文黄片| 国产V视频| 亚洲AV无码乱码国产精品黑人| A级毛片网站| 日本大香蕉视频| 日韩天堂网| 日韩不卡中文字幕| 五月天激情爱爱| av不卡在线| 日韩欧美成人网站| 日本有码中文字幕| 肥臀AV在线| 欧洲成人在线| 粉嫩小泬BBBB免费看-百度| 人妻HDHDHD96XXXX| 亚洲国产精品成人网站| 欧美成人久久| 日批免费视频| 先锋成人av| 性欧美一区二区| 91麻豆精品| 亚洲久草| 五月丁香婷婷色色| 韩国无码中文| 午夜国产精品AV| 中文字幕第5页| 精品久久一区| 久久久久999| 丁香六月婷婷激情| 国产三级片网站| 亚洲无码中文字幕在线播放| 午夜理伦| 免费观看亚洲视频| 97操逼网| 大香蕉伊人久久| 亚洲无码av在线播放| 精品99999| 波多野结衣与黑人| 大地8免费高清视频观看大全| 18禁在线播放| 久操免费在线视频| 男人的天堂免费视频| 婷婷久热| 日韩在线视频不卡| 一区二区三区在线看| 97成人在线| 欧美激情在线观看| 国产精品久久久久久无人区 | 久久无码影院| 樱桃av| 三级成人网| 超碰9999| 黄色a级毛片| 欧一美一婬一伦一区二区三区自慰国 | 无码人妻视频| a在线观看视频| 国产操逼大全| 嫩BBB槡BBBB槡BBBB免费视频 | 国产国产国产在线无码视频| 免费黄色电影在线观看| 国产艹逼视频| 色色婷婷五月| 青草无码| 国产白嫩精品久久久久久| 久久免费看视频| 久久高清免费视频| 久久成人精品视频| 亚洲视频成人| 一级A色情大片| 人人看,人人摸| 日韩av小说| 国产三级日本三级国产三级| 日韩在线一区二区| 99在线观看免费视频| 久久久穴| 日日骚av一区二区三区| 福利一区在线观看| 一起操影院| 久久国产精品影院| 加勒比DVD手机在线播放观看视频 日韩精品一区二区三区四区蜜桃视频 | 日日操夜夜| 成人在线H| 大香蕉网站视频| 中文字幕高清无码在线播放| 国产福利在线| 免费在线观看黄色视频| 在线h片| 色老板亚洲| 日韩成人观看| 亚洲激情自拍| 日本99视频| 嫩BBB搡BBBB搡BBBB| 欧美A级视频在线观看| 天天干夜夜爽| 精产国品一区二区区别| 中文AV在线播放| 四lll少妇BBBB槡BBBB| 老婆被黑人杂交呻吟视频| 午夜久久视频| 色欲AV秘无码一区二区三区| 欧美成人午夜影院| 俩小伙3p老熟女露脸| 丁香婷婷色五月激情综合三级三级片欧美日韩国 | 人人操人人干人人爽| 婷婷亚洲天堂| 狠狠干网站| 免费在线观看黄片视频| 青青综合网| a片免费网址| 狼友视频免费| www.日韩系列| 三级一区二区| 久久性爱视频| 免费一级黄色电影| 91av在线看| 久久无码一区二区三区| 九九九中文字幕| ppypp电影频道| 亚洲最大黄色视频| 草草影院第一页| 日韩黄色一级| 国产精品XXX视频| 亚洲高清无码中字| 国产福利在线导航| 97精品人人A片免费看| 成人资源站| 麻豆AV电影| 免费一级婬片AA片观看| 阿宾MD0165麻豆沈娜娜| 无码三级视频| 中文字幕视频2023| 国产成人a| 国产精品无码无套在线| 影音先锋亚洲资源| 天天爽| 日韩艹| 亚洲免费一区二区| 蜜臀精品| 国产资源在线观看| 色情电影网站| 夜色福利在线| 日韩有码电影| 久久久久久久久国产精品| 超碰9999| 麻豆传媒电影| 午夜AV在线观看| 午夜成人在线视频| 中文子幕免费毛片| 日韩一级片免费看| 欧美老妇操逼视频| 日韩在线一| 色欲欲www成人网站| 91熟女乱伦| 成年人免费视频在线观看| 美日韩精品| 一级黄色视频免费观看| 日本久久综合网| 国产高清小视频| 国产网站免费| 中文字幕免费MV第一季歌词| 91麻豆精品国产91久久久久久| 九九黄色| 91看片看婬黄大片女跟女| 狠狠狠狠狠狠狠狠| 青青色在线视频| 中文字幕在线视频第一页| 99热这里只有精品7| 俄罗斯白嫩BBwBBwBBw91| 亚洲伊人综合| 中文字幕成人在线观看| av无码免费观看| 亚洲天堂视频在线观看免费| 再深点好爽灬轻点久久国产 | 99人妻人人爽人人添人人精品| 91亚洲视频在线观看| 久久精品视频在线| 日本三级无码| 国产欧美熟妇另类久久久| 九九热只有精品| 日韩性视频| 黄片免费网站| 蜜臀AⅤ在线| 免费精品黄色网页| 国产精品无码永久免费A片| 黄色在线网| 亚洲性图第一页| 永久免费黄色视频网站| 探花在线播放| 丁香六月激情婷婷| 91精品久久久久久久久久久久 | 成人做爰69片免费观看| 黄色A视频| www.99爱| 亚洲a视频| 97福利视频| 久久v| 亚洲AV无码第一区二区三区蜜桃 | 99久久99久国产黄毛片| 中文字幕黑人无码| 人人妻日日摸狠狠躁| 成人中文字幕无码| 亚洲精品无| 99国产综合| 免费无码蜜臀在线观看| 91AV在线电影| 69婷婷国产精品| 熟女乱论| 91小仙女jK白丝袜呻吟| 久久久9999| 伊人乱伦| 午夜av免费在线| 韩国午夜福利视频| 起碰在线视频| av视屏| 日本中文无码视频| 免费十无码| 欧洲成人在线观看| 97香蕉久久国产超碰青草专区| 亚洲视频二区| 亚洲成人AV在线| 91成人片| 九九热播精品| 中文字幕精品一区久久久久| 国产91丝袜在线播放| 7x7x7x人成免费观学生视频| 麻豆自拍偷拍视频| 乱子伦国产精品www| 欧美中文日韩| 色欲插插| 亚洲资源在线| gogogo高清在线观看免费直播中国| 亚洲AV无码日韩AV无码导航| 91麻豆国产在线| 无码在线专区| 国产1区2区3区中文字幕| 中文字幕在线观看视频免费| 亚洲精品无码视频在线观看| 在线看v| 亚洲秘无码一区二区三区观看| 奇米影视色偷偷| 女同一区二区三区| 亚洲天堂电影网| 日本一级特黄大片AAAAA级| 西西特级WWW444无码| 日韩一二三四区| 欧美性爱无码| 亚洲欧美性爱| 欧美亚洲成人精品| 16一17女人毛片| 国产精品99精品| 97中文字幕在线| www.一区二区| 欧美午夜福利视频| 亚洲无码视频在线| 国产精品国产三级片| 成年人免费毛片| 日韩日韩日韩日韩| 午夜天堂网| 婷婷99狠狠躁天天躁| 99在线精品视频| 精品少妇视频| 欧亚一区二区| 乱伦视频91| 天天日夜夜添| 麻豆精品久久久久久久99蜜桃| 一级黄片免费看| 久操精品视频| 日韩色情电影| 亚洲无码在线播放视频| 国产A片免费观看| 久久午夜夜伦鲁鲁一区二区| 屌国产精品| 国产特黄级AAAAA片免| 欧美日韩婷婷| 好男人WWW一区二区三区| 欧美精品三级| 三级视频在线观看| 欧美高清久久| 国产一区二区不卡亚洲涩情| 亚洲国产精品二二三三区| 人妻无码电影推荐| 久草资源在线| 蜜臀av一区二区三区| 狠狠干2022| 丰满人妻一区二区三区视频54| 国产精品一区二区性色AV| 国产无遮挡又黄又爽又色学生软件| 免费一级片视频| 操人在线观看| 欧美精品一卡二卡| 日本精品在线视频| 久久久久久亚洲| 五月欧美激情| 91av一区二区三区| 国产精品久久久久无码| 69人妻人人澡人人爽人人精品 | 性欧美xxxx| 天天舔天天干| 欧美日本在线观看| 欧美三P囗交做爰| 91蜜桃传媒| 91欧美精品成人综合在线观看| 欧美精品日韩| 亚洲精品AⅤ一区二| 成人AV电影在线观看| 动漫人物插画动漫人物的视频软件 | 伊人激情| 九九综合精品| AV在线免费网站| 蜜桃BBwBBWBBwBBw| 日韩毛片在线| 无码日韩电影| 色操网| 色片在线观看| 久久肏| 亚洲黄色视频在线免费观看| 囯产精品久久久| 成人亚洲AV日韩AV无码| 女生自慰在线观看| 中文字幕成人网站中文字幕| 一本无码高清| 青青草亚洲| 欧美在线不卡综合| 在线视频福利| 亚洲综合精品| 中文字幕亞洲高清手機版第617| 龙泽美曦土豪| 亚洲香蕉影院| 美女白嫩嫩大BBB欣赏| 91无码精品一区二区| 丁香花中文字幕| 色综合999| 国产一级片在线播放| 强伦人妻一区二区三区| 欧美男女操逼视频| 成人高清无码在线观看| 99re久久| 日本一级理论片在线大全| 大香蕉com| 亚洲成人黄色网| 超碰免费在线| 伊人大香蕉婷婷| 欧美成人内射| 男人先锋| 男人的天堂视频在线观看| 在线观看视频亚洲| 欧美精品在线视频| 日韩三级片AV| 国产女人高潮毛片| 国精品伦一区一区三区有限公司| 久久R5| 欧美性综合网| 中文字幕无码亚| 亚洲成人自拍| jzzijzzij亚洲成熟少妇在线观看 九色蝌蚪9l视频蝌蚪9l视频成人熟妇 | 国产激情视频在线| 久艹在线视频| 一级黄色片免费| 黄色免费网站在线观看| 91一区二区在线播放精品| 毛片A级| 亚洲精品成人电影| 亚洲精品A片| 学生妹毛片视频| 夜夜操影院| 一牛影视精品av| 亚洲成人视频在线免费观看| 亚洲AV第二区国产精品| 黄色18禁| 久热精品在线观看视频| 亚洲福利视频在线| 大黑逼AV| 婷婷射图| 操嫩逼| 91女色| 一级性爱| 青青草无码成人AV片| 国产精品国产三级国产AⅤ| jizz视频| 色色加勒比综合| 日韩在线网址| 国产欧美综合视频| 在线观看视频日韩| 中文字幕av免费观看| 日韩人妻无码一区二区三区99| 广东BBW搡BBBB搡| 日韩中文字幕永久| 色婷婷18禁| 九九大香蕉| 亚洲欧美成人片| 麻豆传媒电影| 亚洲一级黄色片| 亚洲欧洲高清无码| 日逼免费视频| 草视频在线| 久久久久久黄色| 亚洲a在线观看| 97精品无码| 色99视频| 黑人AV七|