成人av在线资源一区,亚洲av日韩av一区,欧美丰满熟妇乱XXXXX图片,狠狠做五月深爱婷婷伊人,桔子av一区二区三区,四虎国产精品永久在线网址,国产尤物精品人妻在线,中文字幕av一区二区三区欲色
    您正在使用IE低版瀏覽器,為了您的雷峰網賬號安全和更好的產品體驗,強烈建議使用更快更安全的瀏覽器
    此為臨時鏈接,僅用于文章預覽,將在時失效
    人工智能開發者 正文
    發私信給汪思穎
    發送

    0

    從零開始碼一個皮卡丘檢測器-CNN目標檢測入門教程(下)

    本文作者: 汪思穎 2017-08-30 11:43
    導語:皮卡!丘!~

    本文作者Zhreshold,原文載于其知乎主頁,雷鋒網(公眾號:雷鋒網)獲其授權發布。

    本文為大家介紹實驗過程中訓練、測試過程及結果。算法和數據集參見《從零開始碼一個皮卡丘檢測器-CNN目標檢測入門教程(上)》

    訓練 Train

    損失函數 Losses

    通過定義損失函數,我們可以讓網絡收斂到我們希望得到的目標檢測功能,也就是說,我們希望網絡能正確預測物體的類別,同時能預測出準確的預設框偏移量,以正確地顯示物體的真正大小和位置。

    這個預測的類別和偏移量都是可以通過真實標簽和網絡的當前預測值得到,在這里我們用MultiBoxTarget層來計算,其中包含了預測框和真實標簽的匹配,正類和負類的選擇,就不一一詳述了。(詳情見論文 SSD: Single Shot MultiBox Detector)。

    from mxnet.contrib.ndarray import MultiBoxTarget

    def training_targets(default_anchors, class_predicts, labels):
       class_predicts = nd.transpose(class_predicts, axes=(0, 2, 1))
       z = MultiBoxTarget(*[default_anchors, labels, class_predicts])
       box_target = z[0]  # 預設框偏移量 (x, y, width, height)
       box_mask = z[1]  # box_mask用來把負類的偏移量置零,因為背景不需要位置!
       cls_target = z[2]  # 每個預設框應該對應的分類
       return box_target, box_mask, cls_target

    gluon.loss中有很多預設的損失函數可以選擇,當然我們也可以快速地手寫一些損失函數。

    首先,對于物體分類的概率,平時我們往往用交叉墑,不過在目標檢測中,我們有大量非平衡的負類(背景),那么 Focal Loss會是一個很好的選擇(詳情見論文 Focal Loss for Dense Object Detection)。

    class FocalLoss(gluon.loss.Loss):
       def __init__(self, axis=-1, alpha=0.25, gamma=2, batch_axis=0, **kwargs):
           super(FocalLoss, self).__init__(None, batch_axis, **kwargs)
           self._axis = axis
           self._alpha = alpha
           self._gamma = gamma

       def hybrid_forward(self, F, output, label):
           output = F.softmax(output)
           pt = F.pick(output, label, axis=self._axis, keepdims=True)
           loss = -self._alpha * ((1 - pt) ** self._gamma) * F.log(pt)
           return F.mean(loss, axis=self._batch_axis, exclude=True)


    # cls_loss = gluon.loss.SoftmaxCrossEntropyLoss()

    cls_loss = FocalLoss()

    print(cls_loss)

    FocalLoss(batch_axis=0, w=None)

    接下來是一個流行的 SmoothL1 損失函數,用來懲罰不準確的預設框偏移量。

    class SmoothL1Loss(gluon.loss.Loss):
       def __init__(self, batch_axis=0, **kwargs):
           super(SmoothL1Loss, self).__init__(None, batch_axis, **kwargs)

       def hybrid_forward(self, F, output, label, mask):
           loss = F.smooth_l1((output - label) * mask, scalar=1.0)
           return F.mean(loss, self._batch_axis, exclude=True)


    box_loss = SmoothL1Loss()

    print(box_loss)

    SmoothL1Loss(batch_axis=0, w=None)

    衡量性能指標 Evaluate metrics

    我們在訓練時需要一些指標來衡量訓練是否順利,我們這里用準確率衡量分類的性能,用平均絕對誤差衡量偏移量的預測能力。這些指標對網絡本身沒有任何影響,只是用于觀測。

    cls_metric = mx.metric.Accuracy()

    box_metric = mx.metric.MAE()  # measure absolute difference between prediction and target

    選擇訓練用的設備 Set context for training

    ctx = mx.gpu()  # 用GPU加速訓練過程

    try:
       _ = nd.zeros(1, ctx=ctx)
       # 為了更有效率,cuda實現需要少量的填充,不影響結果
       train_data.reshape(label_shape=(3, 5))
       train_data = test_data.sync_label_shape(train_data)

    except mx.base.MXNetError as err:
       # 沒有gpu也沒關系,交給cpu慢慢跑
       print('No GPU enabled, fall back to CPU, sit back and be patient...')
       ctx = mx.cpu()

    初始化網絡參數 Initialize parameters

    net=ToySSD(num_class)

    net.initialize(mx.init.Xavier(magnitude=2),ctx=ctx)

    用gluon.Trainer簡化訓練過程 Set up trainer

    gluon.Trainer能簡化優化網絡參數的過程,免去對各個參數單獨更新的痛苦。

    net.collect_params().reset_ctx(ctx)

    trainer=gluon.Trainer(net.collect_params(),'sgd',{'learning_rate':0.1,'wd':5e-4})

    開始訓練 Start training

    既然是簡單的示例,我們不想花費太多的時間來訓練網絡,所以會預加載訓練過一段時間的網絡參數繼續訓練。

    如果你感興趣的話,可以設置

    from_scratch=True

    這樣網絡就會從初始的隨機參數開始訓練。

    一般從頭訓練用單個gpu會花費半個多小時。

    epochs = 150  # 設大一點的值來得到更好的結果

    log_interval = 20

    from_scratch = False  # 設為True就可以從頭開始訓練

    if from_scratch:
       start_epoch = 0

    else:
       start_epoch = 148
       pretrained = 'ssd_pretrained.params'
       sha1 = 'fbb7d872d76355fff1790d864c2238decdb452bc'
       url = 'https://apache-mxnet.s3-accelerate.amazonaws.com/gluon/models/ssd_pikachu-fbb7d872.params'
       if not osp.exists(pretrained) or not verified(pretrained, sha1):
           print('Downloading', pretrained, url)
           download(url, fname=pretrained, overwrite=True)
       net.load_params(pretrained, ctx)

    喝咖啡的時間

    import time

    from mxnet import autograd as ag

    for epoch in range(start_epoch, epochs):
       # 重置iterator和時間戳
       train_data.reset()
       cls_metric.reset()
       box_metric.reset()
       tic = time.time()
       # 迭代每一個批次
       for i, batch in enumerate(train_data):
           btic = time.time()
           # 用autograd.record記錄需要計算的梯度
           with ag.record():
               x = batch.data[0].as_in_context(ctx)
               y = batch.label[0].as_in_context(ctx)
               default_anchors, class_predictions, box_predictions = net(x)
               box_target, box_mask, cls_target = training_targets(default_anchors, class_predictions, y)
               # 損失函數計算
               loss1 = cls_loss(class_predictions, cls_target)
               loss2 = box_loss(box_predictions, box_target, box_mask)
               # 1比1疊加兩個損失函數,也可以加權重
               loss = loss1 + loss2
               # 反向推導
               loss.backward()
           # 用trainer更新網絡參數
           trainer.step(batch_size)
           # 更新下衡量的指標
           cls_metric.update([cls_target], [nd.transpose(class_predictions, (0, 2, 1))])
           box_metric.update([box_target], [box_predictions * box_mask])
           if (i + 1) % log_interval == 0:
               name1, val1 = cls_metric.get()
               name2, val2 = box_metric.get()
               print('[Epoch %d Batch %d] speed: %f samples/s, training: %s=%f, %s=%f'
                     %(epoch ,i, batch_size/(time.time()-btic), name1, val1, name2, val2))

       # 打印整個epoch的的指標
       name1, val1 = cls_metric.get()
       name2, val2 = box_metric.get()
       print('[Epoch %d] training: %s=%f, %s=%f'%(epoch, name1, val1, name2, val2))
       print('[Epoch %d] time cost: %f'%(epoch, time.time()-tic))


    # 還可以把網絡的參數存下來以便下次再用

    net.save_params('ssd_%d.params' % epochs)

    [Epoch 148 Batch 19] speed: 109.217423 samples/s, training: accuracy=0.997539, mae=0.001862
    [Epoch 148] training: accuracy=0.997610, mae=0.001806
    [Epoch 148] time cost: 17.762958
    [Epoch 149 Batch 19] speed: 110.492729 samples/s, training: accuracy=0.997607, mae=0.001824
    [Epoch 149] training: accuracy=0.997692, mae=0.001789
    [Epoch 149] time cost: 15.353258

    測試 Test

    接下來就是  從零開始碼一個皮卡丘檢測器-CNN目標檢測入門教程(下) 的時刻,我們用訓練好的網絡來測試一張圖片。

    網絡推導的過程和訓練很相似,只不過我們不再需要計算真值和損失函數,也不再需要更新網絡的參數,一次推導就可以得到結果。

    準備測試數據 Prepare the test data

    我們需要讀取一張圖片,稍微調整到網絡需要的結構,比如說我們需要調整圖片通道的順序,減去平均值等等慣用的方法。

    import numpy as np

    import cv2

    def preprocess(image):
       """Takes an image and apply preprocess"""
       # 調整圖片大小成網絡的輸入
       image = cv2.resize(image, (data_shape, data_shape))
       # 轉換 BGR 到 RGB
       image = image[:, :, (2, 1, 0)]
       # 減mean之前先轉成float
       image = image.astype(np.float32)
       # 減 mean
       image -= np.array([123, 117, 104])
       # 調成為 [batch-channel-height-width]
       image = np.transpose(image, (2, 0, 1))
       image = image[np.newaxis, :]
       # 轉成 ndarray
       image = nd.array(image)
       return image


    image = cv2.imread('img/pikachu.jpg')

    x = preprocess(image)

    print('x', x.shape)

    x (1, 3, 256, 256)

    網絡推導 Network inference

    只要一行代碼,輸入處理完的圖片,輸出我們要的所有預測值和預設框。

    # 如果有預先訓練好的網絡參數,可以直接加載

    # net.load_params('ssd_%d.params' % epochs, ctx)

    anchors, cls_preds, box_preds = net(x.as_in_context(ctx))

    print('anchors', anchors)

    print('class predictions', cls_preds)

    print('box delta predictions', box_preds)

    anchors
    [[[-0.084375 -0.084375 0.115625 0.115625 ]
    [-0.12037501 -0.12037501 0.15162501 0.15162501]
    [-0.12579636 -0.05508568 0.15704636 0.08633568]
    ...,
    [ 0.01949999 0.01949999 0.98049998 0.98049998]
    [-0.12225395 0.18887302 1.12225389 0.81112695]
    [ 0.18887302 -0.12225395 0.81112695 1.12225389]]]
    <NDArray 1x5444x4 @gpu(0)>
    class predictions
    [[[ 0.33754104 -1.64660323]
    [ 1.15297699 -1.77257478]
    [ 1.1535604 -0.98352218]
    ...,
    [-0.27562004 -1.29400492]
    [ 0.45524898 -0.88782215]
    [ 0.20327765 -0.94481993]]]
    <NDArray 1x5444x2 @gpu(0)>
    box delta predictions
    [[-0.16735925 -0.13083346 -0.68860865 ..., -0.18972112 0.11822788
    -0.27067867]]
    <NDArray 1x21776 @gpu(0)>

    是不是看著還很奇怪,別著急,還差最后一步

    轉換為可讀的輸出 Convert predictions to real object detection results

    要把網絡輸出轉換成我們需要的坐標,還要最后一步,比如我們需要softmax把分類預測轉換成概率,還需要把偏移量和預設框結合來得到物體的大小和位置。

    非極大抑制(Non-Maximum Suppression)也是必要的一步,因為一個物體往往有不只一個檢測框。

    from mxnet.contrib.ndarray import MultiBoxDetection

    # 跑一下softmax, 轉成0-1的概率

    cls_probs = nd.SoftmaxActivation(nd.transpose(cls_preds, (0, 2, 1)), mode='channel')

    # 把偏移量加到預設框上,去掉得分很低的,跑一遍nms,得到最終的結果

    output = MultiBoxDetection(*[cls_probs, box_preds, anchors], force_suppress=True, clip=False)

    print(output)

    [[[ 0. 0.61178613 0.51807499 0.5042429 0.67325425 0.70118797]
    [-1. 0.59466797 0.52491206 0.50917625 0.66228026 0.70489514]
    [-1. 0.5731774 0.53843218 0.50217044 0.66522425 0.7118448 ]
    ...,
    [-1. -1. -1. -1. -1. -1. ]
    [-1. -1. -1. -1. -1. -1. ]
    [-1. -1. -1. -1. -1. -1. ]]]
    <NDArray 1x5444x6 @gpu(0)>

    結果中,每一行都是一個可能的結果框,表示為[類別id, 得分, 左邊界,上邊界,右邊界,下邊界],有很多-1的原因是網絡預測到這些都是背景,或者作為被抑制的結果。

    顯示結果 Display results

    數字永遠不如圖片來得直觀

    把得到的轉換結果畫在圖上,就得到我們期待已久的幾十萬伏特圖了!

    def display(img, out, thresh=0.5):
       import random
       import matplotlib as mpl
       mpl.rcParams['figure.figsize'] = (10,10)
       pens = dict()
       plt.clf()
       plt.imshow(img)
       for det in out:
           cid = int(det[0])
           if cid < 0:
               continue
           score = det[1]
           if score < thresh:
               continue
           if cid not in pens:
               pens[cid] = (random.random(), random.random(), random.random())
           scales = [img.shape[1], img.shape[0]] * 2
           xmin, ymin, xmax, ymax = [int(p * s) for p, s in zip(det[2:6].tolist(), scales)]
           rect = plt.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, fill=False,
                                edgecolor=pens[cid], linewidth=3)
           plt.gca().add_patch(rect)
           text = class_names[cid]
           plt.gca().text(xmin, ymin-2, '{:s} {:.3f}'.format(text, score),
                          bbox=dict(facecolor=pens[cid], alpha=0.5),
                          fontsize=12, color='white')
       plt.show()


    display(image[:, :, (2, 1, 0)], output[0].asnumpy(), thresh=0.45)

    從零開始碼一個皮卡丘檢測器-CNN目標檢測入門教程(下)

    小結 Conclusion

    目標檢測不同于分類任務,需要考慮的不只是全圖尺度的單一分類,而是需要檢測到不同大小,不同位置的物體,難度自然提升了許多,用掃窗之類的傳統方法早已不適合神經網絡這種需要大量計算需求的新結構。幸好我們可以用本章節介紹的方法,利用卷積網絡的特性,一次推導得到全部的預測結果,相對來說快速且準確。

    我們希望能用較短的篇幅來描述一個足夠簡單的過程,但是難免會有疏漏,歡迎各種問題和建議,與此同時,我們會不斷更新教程,并且會帶來更多不同的算法,敬請期待。

    相關鏈接

    Apache MXNet官方網站:https://mxnet.incubator.apache.org/

    Github Repo: zackchase/mxnet-the-straight-dope

    英文版教程: Object Detection Using Convolutional Neural Networks

    Eric知乎介紹0.11 新特性:https://zhuanlan.zhihu.com/p/28648399

    0.11 Release:https://github.com/apache/incubator-mxnet/releases

    安裝指南:https://mxnet.incubator.apache.org/versions/master/get_started/install.html

    其他Gluon教程:http://gluon.mxnet.io/

    Gluon講座PPT: https://github.com/zackchase/mxnet-slides/blob/master/kdd-mxnet-slides.pdf

    Gluon深度學習樣例:https://github.com/apache/incubator-mxnet/tree/master/example/gluon

    SSD: Single Shot MultiBox Detector

    Focal Loss: [1708.02002] Focal Loss for Dense Object Detection

    雷峰網版權文章,未經授權禁止轉載。詳情見轉載須知。

    從零開始碼一個皮卡丘檢測器-CNN目標檢測入門教程(下)

    分享:
    相關文章

    編輯

    關注AI學術,例如論文
    當月熱門文章
    最新文章
    請填寫申請人資料
    姓名
    電話
    郵箱
    微信號
    作品鏈接
    個人簡介
    為了您的賬戶安全,請驗證郵箱
    您的郵箱還未驗證,完成可獲20積分喲!
    請驗證您的郵箱
    立即驗證
    完善賬號信息
    您的賬號已經綁定,現在您可以設置密碼以方便用郵箱登錄
    立即設置 以后再說