<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          (附代碼)YOLOF:速度和效果均超過YOLOv4的檢測模型

          共 9800字,需瀏覽 20分鐘

           ·

          2021-06-14 22:02

          點(diǎn)擊左上方藍(lán)字關(guān)注我們



          全網(wǎng)搜集目標(biāo)檢測相關(guān),人工篩選最優(yōu)價值內(nèi)容

          編者薦語
          YOLOF 全稱是 You Only Look One-level Feature, 其通過詳細(xì)的實(shí)驗(yàn)指出特征金字塔 FPN 模塊的成功在于其對目標(biāo)優(yōu)化問題的分治解決方案,而不是我們常說的多尺度特征融合。

          作者 | OpenMMLab @知乎

          鏈接 | https://zhuanlan.zhihu.com/p/370758213


          摘要


          YOLOF 論文核心可以總結(jié)如下:

          • 設(shè)計(jì)了多組實(shí)驗(yàn),深入探討了 FPN 模塊成功的主要因素

          • 基于實(shí)驗(yàn)結(jié)論,設(shè)計(jì)了無需 FPN 模塊,單尺度簡單高效的 Neck 模塊 Dilated Encoder

          • 基于 FPN 分治處理多尺度問題,配合 Neck 模塊提出 Uniform Matching 正負(fù)樣本匹配策略

          • 由于不存在復(fù)雜且耗內(nèi)存極多的 FPN 模塊,YOLOF 可以在保存高精度的前提下,推理速度快,消耗內(nèi)存也相對更小


          項(xiàng)目地址:github.com/open-mmlab/mmdetection,歡迎 star~

          1 FPN 模塊分析




          首先目標(biāo)檢測算法可以簡單按照上述結(jié)構(gòu)進(jìn)行劃分,網(wǎng)絡(luò)部分主要分為 Backbone、Encoder 和 Decoder,或者按照我們前系列解讀文章劃分方法分為 Backbone、Neck 和 Head。對于單階段算法來說,常見的 Backbone 是 ResNet,Encoder 或者 Neck 是 FPN,而 Head 就是對應(yīng)的輸出層結(jié)構(gòu)。

          一般我們都認(rèn)為 FPN 層作用非常大,不可或缺,其通過特征多尺度融合,可以有效解決尺度變換預(yù)測問題。而本文認(rèn)為 FPN 至少有兩個主要作用:

          • 多尺度特征融合

          • 分治策略,可以將不同大小的物體分配到不同大小的的輸出層上,克服尺度預(yù)測問題

          作者試圖分析上述兩個作用中,最核心的部分,故選擇最常用的 RetinaNet 進(jìn)行 FPN 模塊深入分析。



          對 FPN 模塊進(jìn)一步抽象,如上圖所示,可以分成 4 種結(jié)構(gòu) MiMo、SiMo、MiSo 和 SiSo,其中 MiMo 即為標(biāo)準(zhǔn)的 FPN結(jié)構(gòu),輸入和輸出都包括多尺度特征圖。將 FPN 替換為上述 4 個模塊,然后基于 RetinaNet 重新訓(xùn)練,計(jì)算 mAP 、 GFLOPs 和 FPS 指標(biāo)

          • 從 mAP 角度分析,SiMo 結(jié)果和 MiMo 差距不大,說明 C5 (Backbone 輸出)包含了足夠的檢測不同尺度目標(biāo)的上下文信息;而 MiSo 和 SiSo 則和 MiMo 差距較大,說明 FPN 分治優(yōu)化作用遠(yuǎn)遠(yuǎn)大于 多尺度特征融合

          • 從下表 GFLOPs 和 FPS 可以看出,MiMo 結(jié)構(gòu)由于存在高分辨率特征圖 C3 會帶來較大的計(jì)算量,并且拖慢速度

          綜上所示,可以得到一些結(jié)論:

          • FPN 模塊的主要增益來自于其分治優(yōu)化手段,而不是多尺度特征融合

          • FPN 模塊中存在高分辨率特征融合過程,導(dǎo)致消耗內(nèi)存比較多,訓(xùn)練和推理速度也比較慢,對部署不太優(yōu)化

          • 如果想在拋棄 FPN 模塊的前提下精度不丟失,那么主要問題是提供分治優(yōu)化替代手段

          2 YOLOF 原理簡析

          作者為了克服 FPN 存在的內(nèi)存占用多,速度慢問題,采用了 SiSo 結(jié)構(gòu),但是精度下降比較嚴(yán)重,從 35.9 變成了 24.6,故后續(xù)有針對性的改進(jìn),主要包括兩個部分: Dilated Encoder 和 Uniform Matching。

          2.1 Dilated Encoder


          雖然 FPN 的主要作用是分治優(yōu)化思想,但是多尺度融合也有一定作用,從上述實(shí)驗(yàn)也可以看出。并且雖然 C5 提供了足夠的上下文,但是其感受野所對應(yīng)的目標(biāo)尺寸范圍是有限的,無法應(yīng)對目標(biāo)檢測場景中變化劇烈的目標(biāo)尺寸。簡要理解如上圖所示,綠色點(diǎn)表示數(shù)據(jù)集中的多種目標(biāo)尺寸,粉紅色區(qū)域代表特征圖能夠有效表達(dá)的目標(biāo)尺寸范圍

          • 如果僅僅使用 C5 特征,會出現(xiàn)圖(a)所示的情況

          • 若使用空洞卷積操作來增大 C5 特征圖的感受野,則會出現(xiàn)圖(b)所示的情況,感受野變大,能夠有效地表達(dá)尺寸較大的目標(biāo),但是對小目標(biāo)表達(dá)能力會變差

          • 如果采用不同空洞率的疊加,則可以有效避免上述問題

          為此,作者設(shè)計(jì)了 Dilated Encoder 結(jié)構(gòu),串聯(lián)多個不同空洞率的模塊以覆蓋不同大小物體,改善感受野單一問題,如下所示:


          對 C5 特征先進(jìn)行壓縮通道,然后串聯(lián) 4 個不同空洞率的殘差模塊,從而得到不同感受野的特征圖。其實(shí)這種做法非常場景,在語義分割算法 ASPP 中和目標(biāo)檢測算法 RFBNet 都采用了類似思想,只不過這兩個都是并聯(lián)結(jié)構(gòu),而本文是串聯(lián), RFBNet 結(jié)構(gòu)如下所示:




          2.2 Uniform Matching
          前面說過 FPN 的核心功能是分治手段,但是我們知道雖然其輸出多個尺度特征圖,但是要想發(fā)揮分治功能則主要依靠 bbox 正負(fù)樣本分配策略,也就是說 FPN 和優(yōu)異的 bbox 正負(fù)樣本分配策略結(jié)合才能最大程度發(fā)揮功效,大部分最新的單階段目標(biāo)檢測算法都在 bbox 分配策略上面做文章,可以借用 AutoAssign 論文中的圖說明:




          為了充分發(fā)揮 FPN 功效,一般會從 scale 和 spatial 兩個方面著手進(jìn)行設(shè)計(jì),scale 用于處理不同尺度大小的 gt bbox 應(yīng)該屬于哪些輸出層負(fù)責(zé),而 spatial 用于處理在某個輸出特征圖上哪些位置才是最合適的正樣本點(diǎn)。不同的 bbox 正負(fù)樣本分配策略對最終性能影響極大。

          一般來說,由于自然場景中,大小物體分布本身就不均勻,并且大物體在圖片中所占區(qū)域較大,如果不設(shè)計(jì)好,會導(dǎo)致大物體的正樣本數(shù)遠(yuǎn)遠(yuǎn)多于小物體,最終性能就會偏向大物體,導(dǎo)致整體性能較差。YOLOF 算法采用單尺度特征圖輸出,錨點(diǎn)的數(shù)量會大量的減少(比如從 100K 減少到 5K),導(dǎo)致了稀疏錨點(diǎn),如果不進(jìn)行重新設(shè)計(jì),會加劇上述現(xiàn)象。為此作者提出了新的均勻匹配策略,核心思想就是不同大小物體都盡量有相同數(shù)目的正樣本。


          所提兩個模塊的作用如下所示:



          • Uniform Matching 作用非常大,說明該模塊其實(shí)發(fā)揮了 FPN 的分治作用

          • Dilated Encoder 配合 Uniform Matching 可以提供額外的變感受野功能,有助于多尺度物體預(yù)測

          需要特別注意:論文中所描述的 Uniform Matching 和代碼中實(shí)現(xiàn)的 Uniform Matching 有一定差距,在下一節(jié)源碼解讀時會詳細(xì)說明。

          3 YOLOF 源碼解析

          和前系列解讀一樣,依然按照 Backbone、Neck、Head、Bbox Coder、Bbox Assigner 和 Loss 順序解讀。

          3.1 Backbone
          Backbone 采用了 ResNet50,caffe 模式,和 FCOS 算法配置相同,只不過這里只需要輸出 C5 特征圖即可,不需要多尺度。

          pretrained='open-mmlab://detectron/resnet50_caffe', 
          backbone=dict(
          type='ResNet',
          depth=50,
          num_stages=4,
          out_indices=(3, ),
          frozen_stages=1,
          norm_cfg=dict(type='BN', requires_grad=False),
          norm_eval=True,
          style='caffe'),


          3.2 Neck
          Neck 模塊是本文新提出的 Dilated Encoder 模塊,包括一個通道壓縮模塊,然后串聯(lián) 4 個不同空洞率的殘差模塊,提供靈活變尺度的感受野。

          neck=dict( 
          type='DilatedEncoder',
          in_channels=2048,
          out_channels=512,
          block_mid_channels=128,
          num_residual_blocks=4),

          由于比較簡單,就不展開分析了。


          3.3 Head



          Head 模塊即上圖的 Decoder 結(jié)構(gòu),包括分類和回歸分支,借鑒 AutoAssign 算法,在回歸分支上并行引入一個 Objectness 分支,用于抑制背景區(qū)域的高響應(yīng),然后將其和分類分支相乘。故對外實(shí)際上兩個分支,Objectness是沒有監(jiān)督 label 的。其 Head forward 如下所示

          def forward_single(self, feature): 
          # 分類分支
          cls_score = self.cls_score(self.cls_subnet(feature))
          N, _, H, W = cls_score.shape
          cls_score = cls_score.view(N, -1, self.num_classes, H, W)

          # 回歸分支
          reg_feat = self.bbox_subnet(feature)
          bbox_reg = self.bbox_pred(reg_feat)
          objectness = self.object_pred(reg_feat)
          # implicit objectness
          objectness = objectness.view(N, -1, 1, H, W)

          normalized_cls_score = cls_score + objectness - torch.log(
          1. + torch.clamp(cls_score.exp(), max=INF) +
          torch.clamp(objectness.exp(), max=INF))
          normalized_cls_score = normalized_cls_score.view(N, -1, H, W)
          return normalized_cls_score, bbox_reg

          上述得到 normalized_cls_score 的計(jì)算過程看起來非常復(fù)雜,但是其實(shí)是為了能夠?qū)θ诤虾蟮?normalized_cls_score 采用 sigmoid 函數(shù)而已,其對應(yīng)的公式是:


            

          也就是說 normalized_cls_score 是不含 sigmoid 的包括 cls_score 和 objectness 融合的值??梢酝ㄟ^如下簡單代碼驗(yàn)證:

          import torch 

          if __name__ == '__main__':
          INF = 1e8
          N = 1
          num_classes = 2
          H = W = 3
          cls_score = torch.rand((N, 1, num_classes, H, W))
          objectness = torch.rand(N, 1, 1, H, W)

          normalized_cls_score = cls_score + objectness - torch.log(
          1. + torch.clamp(cls_score.exp(), max=INF) +
          torch.clamp(objectness.exp(), max=INF))

          cls_score_s = torch.sigmoid(cls_score) * torch.sigmoid(objectness)

          assert torch.allclose(cls_score_s, torch.sigmoid(normalized_cls_score))


          3.4 Bbox Coder
          YOLOF 輸出格式采用 RetinaNet 算法中定義的 deltaXYWH ,即回歸分支輸出的 4 個值表示相對于 anchor 的偏移

          anchor_generator=dict( 
          type='AnchorGenerator',
          ratios=[1.0],
          scales=[1, 2, 4, 8, 16],
          strides=[32]),
          bbox_coder=dict(
          type='DeltaXYWHBBoxCoder',
          target_means=[.0, .0, .0, .0],
          target_stds=[1., 1., 1., 1.],
          add_ctr_clamp=True,
          ctr_clamp=32),

          只有一個輸出特征圖,每個位置鋪設(shè)了 5 個 anchor,寬高比是 1,設(shè)置了 5 種 scale。為了穩(wěn)定訓(xùn)練過程,作者在 DeltaXYWHBBoxCoder 中引入了 add_ctr_clamp 參數(shù)即當(dāng)中心坐標(biāo)預(yù)測相比 anchor 偏離大于 32 個像素,則強(qiáng)制裁剪為 32,防止產(chǎn)生較大的梯度。

          3.5 Bbox Assigner
          這個部分是 YOLOF 的核心,需要重點(diǎn)分析。首先分析論文中描述,然后再基于代碼說明代碼和論文的差異。
          論文中描述的非常簡單,核心目的是保證不同尺度物體都盡可能有相同數(shù)目的正樣本

          • 遍歷每個 gt bbox,然后選擇 topk 個距離最近的 anchor 作為其匹配的正樣本

          • 由于存在極端比例物體和小物體,上述強(qiáng)制 topk 操作可能出現(xiàn) anchor 和 gt bbox 的不匹配現(xiàn)象,為了防止噪聲樣本影響,在所有正樣本點(diǎn)中,將 anchor 和 gt bbox 的 iou 低于 0.15 的正樣本(因?yàn)椴还芷ヅ淝闆r,topk 都會選擇出指定數(shù)目的正樣本)強(qiáng)制認(rèn)為是忽略樣本,在所有負(fù)樣本點(diǎn)中,將 anchor 和 gt bbox 的 iou 高于 0.75 的負(fù)樣本(可能該物體比較大,導(dǎo)致很多 anchor 都能夠和該 gt bbox 很好的匹配,這些樣本就不適合作為負(fù)樣本了)強(qiáng)制認(rèn)為是忽略樣本

          實(shí)際上作者代碼的寫法如下所示

          • 遍歷每個 gt bbox,然后選擇 topk 個距離最近的 anchor 作為其匹配的正樣本

          • 遍歷每個 gt bbox,然后選擇 topk 個距離最近的預(yù)測框作為補(bǔ)充的匹配正樣本

          • 計(jì)算 gt bbox 和預(yù)測框的 iou,在所有負(fù)樣本點(diǎn)中,將 iou 高于 0.75 的負(fù)樣本強(qiáng)制認(rèn)為是忽略樣本

          • 計(jì)算 gt bbox 和 anchor 的 iou,在所有正樣本點(diǎn)中,將 iou 低于 0.15 的正樣本強(qiáng)制認(rèn)為是忽略樣本

          可以發(fā)現(xiàn)相比于論文描述,實(shí)際上代碼額外動態(tài)補(bǔ)充了一定量的正樣本,同時也額外考慮了一些忽略樣本。相比于純粹采用 anchor 和 gt bbox 進(jìn)行匹配,額外引入預(yù)測框,可以動態(tài)調(diào)整正負(fù)樣本,理論上會更好。

          # 全部任務(wù)是負(fù)樣本 
          assigned_gt_inds = bbox_pred.new_full((num_bboxes, ),
          0,
          dtype=torch.long)

          # 計(jì)算兩兩直接的距離,包括 預(yù)測框和 gt bbox,以及 anchor 和 gt bbox
          cost_bbox = torch.cdist(
          bbox_xyxy_to_cxcywh(bbox_pred),
          bbox_xyxy_to_cxcywh(gt_bboxes),
          p=1)
          cost_bbox_anchors = torch.cdist(
          bbox_xyxy_to_cxcywh(anchor), bbox_xyxy_to_cxcywh(gt_bboxes), p=1)

          # 分別提取 topk 個樣本點(diǎn)作為正樣本,此時正樣本數(shù)會加倍
          index = torch.topk(
          C,
          k=self.match_times,
          dim=0,
          largest=False)[1]
          # self.match_times x n
          index1 = torch.topk(C1, k=self.match_times, dim=0, largest=False)[1]
          # (self.match_times*2) x n
          indexes = torch.cat((index, index1),
          dim=1).reshape(-1).to(bbox_pred.device)

          # 計(jì)算 iou 矩陣
          pred_overlaps = self.iou_calculator(bbox_pred, gt_bboxes)
          anchor_overlaps = self.iou_calculator(anchor, gt_bboxes)
          pred_max_overlaps, _ = pred_overlaps.max(dim=1)
          anchor_max_overlaps, _ = anchor_overlaps.max(dim=0)

          # 計(jì)算 gt bbox 和預(yù)測框的 iou,在所有負(fù)樣本點(diǎn)中,將 iou 高于 0.75 的負(fù)樣本強(qiáng)制認(rèn)為是忽略樣本
          ignore_idx = pred_max_overlaps > self.neg_ignore_thr
          assigned_gt_inds[ignore_idx] = -1

          # 計(jì)算 gt bbox 和 anchor 的 iou,在所有正樣本點(diǎn)中,將 iou 低于 0.15 的正樣本強(qiáng)制認(rèn)為是忽略樣本
          pos_gt_index = torch.arange(
          0, C1.size(1),
          device=bbox_pred.device).repeat(self.match_times * 2)
          pos_ious = anchor_overlaps[indexes, pos_gt_index]
          pos_ignore_idx = pos_ious < self.pos_ignore_thr
          pos_gt_index_with_ignore = pos_gt_index + 1
          pos_gt_index_with_ignore[pos_ignore_idx] = -1
          assigned_gt_inds[indexes] = pos_gt_index_with_ignore


          3.6 Loss
          在確定了每個特征點(diǎn)位置哪些是正樣本和負(fù)樣本后,就可以計(jì)算 loss 了,分類采用 focal loss,回歸采用 giou loss,都是常規(guī)操作。

          loss_cls=dict( 
          type='FocalLoss',
          use_sigmoid=True,
          gamma=2.0,
          alpha=0.25,
          loss_weight=1.0),
          loss_bbox=dict(type='GIoULoss', loss_weight=1.0))


          上述就是整個 YOLOF 核心實(shí)現(xiàn)過程。至于推理過程和 RetinaNet 算法完全相同。

          4 YOLOF 復(fù)現(xiàn)心得和體會

          如果不仔細(xì)思考,可能看不出上述代碼有啥問題,實(shí)際上在 Bbox Assigner 環(huán)節(jié)會存在重復(fù)索引分配問題,這個問題會帶來幾個影響。具體代碼是:

          # 對應(yīng) 3.5 小節(jié)的源碼分析第 44 行 
          assigned_gt_inds[indexes] = pos_gt_index_with_ignore

          前面說過,YOLOF 會引入額外的預(yù)測框點(diǎn)作為補(bǔ)充正樣本,當(dāng) 2 次 topk 選擇的位置相同時候就會出現(xiàn)意想不到問題。

          舉個簡單例子,當(dāng)前圖片中僅僅有一個 gt bbox,且預(yù)測輸出特征圖大小是 10x10,設(shè)置 anchor 個數(shù)是 1,那么說明輸出特征圖上只有 10x10 個anchor,并且對應(yīng)了 10x10 個預(yù)測框,topk 設(shè)置為 4

          • 計(jì)算該 gt bbox 和 100 個 anchor 的距離,然后選擇最近的前 4 個位置作為正樣本

          • 計(jì)算該 gt bbox 和 100 個預(yù)測框的距離,然后選擇最近的前 4 個位置作為正樣本,注意這里選擇的 4個位置很可能和前面選擇的 4 個位置有重復(fù)

          • 計(jì)算該 gt bbox 和預(yù)測框的 iou,在所有負(fù)樣本點(diǎn)中,將 iou 高于 0.75 的負(fù)樣本強(qiáng)制認(rèn)為是忽略樣本

          • 計(jì)算該 gt bbox 和 anchor 的 iou,在所有正樣本點(diǎn)中,將 iou 低于 0.15 的正樣本強(qiáng)制認(rèn)為是忽略樣本,注意和上一步的區(qū)別,由于 iou 計(jì)算的輸入是不一樣的,可能導(dǎo)致某個被重復(fù)計(jì)算的正樣本位置出現(xiàn) 2 種情況:1. 兩個步驟都認(rèn)為是忽略樣本;2. 一個認(rèn)為是忽略樣本,一個認(rèn)為是正樣本,而一旦出現(xiàn)第二種情況則在 CUDA 并行計(jì)算中出現(xiàn)不確定輸出

          簡單來說:indexes 中可能存在重復(fù)值,并且重復(fù)值位置對應(yīng)的 pos_gt_index_with_ignore 可能相同也可能不同,注意重復(fù)現(xiàn)象可能出現(xiàn)在兩個不同類別物體有重疊的情況下,那么上述賦值操作在 CUDA 中是不可預(yù)知的,可能會出現(xiàn)同一份數(shù)據(jù)跑兩次輸出結(jié)果不一樣。

          這個操作會給后面的回歸分支帶來歧義,因?yàn)榛貧w分支僅僅處理正樣本,那么會出現(xiàn)以下幾個情況:

          • 如果兩個重復(fù)索引處對應(yīng)的 gt bbox 是同一個,那么相當(dāng)于該 gt bbox 對應(yīng)的正樣本 loss 權(quán)重加倍

          • 如果兩個重復(fù)索引處對應(yīng)的 gt bbox 不是同一個,那么就會出現(xiàn)歧義,因?yàn)樘卣鲌D上同一個預(yù)測點(diǎn),被同時分配給了兩個不同的 gt bbox

          總的來說,對于上述重復(fù)索引分配現(xiàn)象,會帶來幾個影響:

          • 讀者理解代碼運(yùn)行流程會比較困惑

          • 同一個程序跑多次,可能輸出結(jié)果不一致

          • 訓(xùn)練過程不穩(wěn)定

          • 當(dāng)重復(fù)索引出現(xiàn)時候,回歸分支 loss 計(jì)算過程非常奇怪,難以理解

          • 低版本 CUDA 上會出現(xiàn)非法內(nèi)存越界錯誤, 實(shí)驗(yàn)發(fā)現(xiàn) CUDA9.0 會出現(xiàn)非法內(nèi)存越界錯誤,但是 CUDA10.1 則正常,其余版本沒有進(jìn)行測試

          關(guān)于第5點(diǎn),原因暫時不清楚,但是現(xiàn)象是 pos_gt_index_with_ignore、indexes 和 assigned_gt_inds 都不存在越界情況,只不過 indexes 如果存在相同值,在賦值后會出現(xiàn) 4294967295(2^32 -1) 和 -4294967295 (-2^32 +1) 異常值,然后后續(xù)基于 assigned_gt_inds 取值后就出現(xiàn)出現(xiàn) RuntimeError: CUDA error: an illegal memory access was encountered. 經(jīng)過多次實(shí)驗(yàn)發(fā)現(xiàn),錯誤是必現(xiàn)的。當(dāng)時也試過其他幾個方案,例如 1. 將上述賦值操作放置到 cpu 上進(jìn)行;2. 將賦值后異常值全部設(shè)置為忽略樣本,雖然可以避免報錯,但是實(shí)驗(yàn)結(jié)果顯示會存在一定程度的掉點(diǎn),所以最終沒有修改。這個問題我們也會持續(xù)關(guān)注,直到找到一個更加合適的方式以避免上述報錯問題。

          上述這個寫法,給代碼復(fù)現(xiàn)帶來了些問題,并且由于 YOLOF 學(xué)習(xí)率非常高 lr=0.12,訓(xùn)練過程偶爾會出現(xiàn) Nan 現(xiàn)象,訓(xùn)練不太穩(wěn)定,可能對參數(shù)設(shè)置例如 warmup 比較敏感。

          END



          雙一流大學(xué)研究生團(tuán)隊(duì)創(chuàng)建,專注于目標(biāo)檢測與深度學(xué)習(xí),希望可以將分享變成一種習(xí)慣!

          整理不易,點(diǎn)贊三連↓


          瀏覽 47
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  少妇无码喷水久久 | 伊伊成人网 | 欧美色婷| 91看逼| 婷婷五月天97干 |