丁香五月天婷婷久久婷婷色综合91|国产传媒自偷自拍|久久影院亚洲精品|国产欧美VA天堂国产美女自慰视屏|免费黄色av网站|婷婷丁香五月激情四射|日韩AV一区二区中文字幕在线观看|亚洲欧美日本性爱|日日噜噜噜夜夜噜噜噜|中文Av日韩一区二区

您正在使用IE低版瀏覽器,為了您的雷峰網(wǎng)賬號(hào)安全和更好的產(chǎn)品體驗(yàn),強(qiáng)烈建議使用更快更安全的瀏覽器
此為臨時(shí)鏈接,僅用于文章預(yù)覽,將在時(shí)失效
人工智能開(kāi)發(fā)者 正文
發(fā)私信給汪思穎
發(fā)送

0

從零開(kāi)始碼一個(gè)皮卡丘檢測(cè)器-CNN目標(biāo)檢測(cè)入門(mén)教程(下)

本文作者: 汪思穎 2017-08-30 11:43
導(dǎo)語(yǔ):皮卡!丘!~

本文作者Zhreshold,原文載于其知乎主頁(yè),雷鋒網(wǎng)(公眾號(hào):雷鋒網(wǎng))獲其授權(quán)發(fā)布。

本文為大家介紹實(shí)驗(yàn)過(guò)程中訓(xùn)練、測(cè)試過(guò)程及結(jié)果。算法和數(shù)據(jù)集參見(jiàn)《從零開(kāi)始碼一個(gè)皮卡丘檢測(cè)器-CNN目標(biāo)檢測(cè)入門(mén)教程(上)》

訓(xùn)練 Train

損失函數(shù) Losses

通過(guò)定義損失函數(shù),我們可以讓網(wǎng)絡(luò)收斂到我們希望得到的目標(biāo)檢測(cè)功能,也就是說(shuō),我們希望網(wǎng)絡(luò)能正確預(yù)測(cè)物體的類(lèi)別,同時(shí)能預(yù)測(cè)出準(zhǔn)確的預(yù)設(shè)框偏移量,以正確地顯示物體的真正大小和位置。

這個(gè)預(yù)測(cè)的類(lèi)別和偏移量都是可以通過(guò)真實(shí)標(biāo)簽和網(wǎng)絡(luò)的當(dāng)前預(yù)測(cè)值得到,在這里我們用MultiBoxTarget層來(lái)計(jì)算,其中包含了預(yù)測(cè)框和真實(shí)標(biāo)簽的匹配,正類(lèi)和負(fù)類(lèi)的選擇,就不一一詳述了。(詳情見(jiàn)論文 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]  # 預(yù)設(shè)框偏移量 (x, y, width, height)
   box_mask = z[1]  # box_mask用來(lái)把負(fù)類(lèi)的偏移量置零,因?yàn)楸尘安恍枰恢茫?/em>
   cls_target = z[2]  # 每個(gè)預(yù)設(shè)框應(yīng)該對(duì)應(yīng)的分類(lèi)
   return box_target, box_mask, cls_target

gluon.loss中有很多預(yù)設(shè)的損失函數(shù)可以選擇,當(dāng)然我們也可以快速地手寫(xiě)一些損失函數(shù)。

首先,對(duì)于物體分類(lèi)的概率,平時(shí)我們往往用交叉墑,不過(guò)在目標(biāo)檢測(cè)中,我們有大量非平衡的負(fù)類(lèi)(背景),那么 Focal Loss會(huì)是一個(gè)很好的選擇(詳情見(jiàn)論文 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)

接下來(lái)是一個(gè)流行的 SmoothL1 損失函數(shù),用來(lái)懲罰不準(zhǔn)確的預(yù)設(shè)框偏移量。

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)

衡量性能指標(biāo) Evaluate metrics

我們?cè)谟?xùn)練時(shí)需要一些指標(biāo)來(lái)衡量訓(xùn)練是否順利,我們這里用準(zhǔn)確率衡量分類(lèi)的性能,用平均絕對(duì)誤差衡量偏移量的預(yù)測(cè)能力。這些指標(biāo)對(duì)網(wǎng)絡(luò)本身沒(méi)有任何影響,只是用于觀測(cè)。

cls_metric = mx.metric.Accuracy()

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

選擇訓(xùn)練用的設(shè)備 Set context for training

ctx = mx.gpu()  # 用GPU加速訓(xùn)練過(guò)程

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

except mx.base.MXNetError as err:
   # 沒(méi)有g(shù)pu也沒(méi)關(guān)系,交給cpu慢慢跑
   print('No GPU enabled, fall back to CPU, sit back and be patient...')
   ctx = mx.cpu()

初始化網(wǎng)絡(luò)參數(shù) Initialize parameters

net=ToySSD(num_class)

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

用gluon.Trainer簡(jiǎn)化訓(xùn)練過(guò)程 Set up trainer

gluon.Trainer能簡(jiǎn)化優(yōu)化網(wǎng)絡(luò)參數(shù)的過(guò)程,免去對(duì)各個(gè)參數(shù)單獨(dú)更新的痛苦。

net.collect_params().reset_ctx(ctx)

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

開(kāi)始訓(xùn)練 Start training

既然是簡(jiǎn)單的示例,我們不想花費(fèi)太多的時(shí)間來(lái)訓(xùn)練網(wǎng)絡(luò),所以會(huì)預(yù)加載訓(xùn)練過(guò)一段時(shí)間的網(wǎng)絡(luò)參數(shù)繼續(xù)訓(xùn)練。

如果你感興趣的話,可以設(shè)置

from_scratch=True

這樣網(wǎng)絡(luò)就會(huì)從初始的隨機(jī)參數(shù)開(kāi)始訓(xùn)練。

一般從頭訓(xùn)練用單個(gè)gpu會(huì)花費(fèi)半個(gè)多小時(shí)。

epochs = 150  # 設(shè)大一點(diǎn)的值來(lái)得到更好的結(jié)果

log_interval = 20

from_scratch = False  # 設(shè)為T(mén)rue就可以從頭開(kāi)始訓(xùn)練

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)

喝咖啡的時(shí)間

import time

from mxnet import autograd as ag

for epoch in range(start_epoch, epochs):
   # 重置iterator和時(shí)間戳
   train_data.reset()
   cls_metric.reset()
   box_metric.reset()
   tic = time.time()
   # 迭代每一個(gè)批次
   for i, batch in enumerate(train_data):
       btic = time.time()
       # 用autograd.record記錄需要計(jì)算的梯度
       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)
           # 損失函數(shù)計(jì)算
           loss1 = cls_loss(class_predictions, cls_target)
           loss2 = box_loss(box_predictions, box_target, box_mask)
           # 1比1疊加兩個(gè)損失函數(shù),也可以加權(quán)重
           loss = loss1 + loss2
           # 反向推導(dǎo)
           loss.backward()
       # 用trainer更新網(wǎng)絡(luò)參數(shù)
       trainer.step(batch_size)
       # 更新下衡量的指標(biāo)
       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))

   # 打印整個(gè)epoch的的指標(biāo)
   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))


# 還可以把網(wǎng)絡(luò)的參數(shù)存下來(lái)以便下次再用

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

測(cè)試 Test

接下來(lái)就是  從零開(kāi)始碼一個(gè)皮卡丘檢測(cè)器-CNN目標(biāo)檢測(cè)入門(mén)教程(下) 的時(shí)刻,我們用訓(xùn)練好的網(wǎng)絡(luò)來(lái)測(cè)試一張圖片。

網(wǎng)絡(luò)推導(dǎo)的過(guò)程和訓(xùn)練很相似,只不過(guò)我們不再需要計(jì)算真值和損失函數(shù),也不再需要更新網(wǎng)絡(luò)的參數(shù),一次推導(dǎo)就可以得到結(jié)果。

準(zhǔn)備測(cè)試數(shù)據(jù) Prepare the test data

我們需要讀取一張圖片,稍微調(diào)整到網(wǎng)絡(luò)需要的結(jié)構(gòu),比如說(shuō)我們需要調(diào)整圖片通道的順序,減去平均值等等慣用的方法。

import numpy as np

import cv2

def preprocess(image):
   """Takes an image and apply preprocess"""
   # 調(diào)整圖片大小成網(wǎng)絡(luò)的輸入
   image = cv2.resize(image, (data_shape, data_shape))
   # 轉(zhuǎn)換 BGR 到 RGB
   image = image[:, :, (2, 1, 0)]
   # 減mean之前先轉(zhuǎn)成float
   image = image.astype(np.float32)
   # 減 mean
   image -= np.array([123, 117, 104])
   # 調(diào)成為 [batch-channel-height-width]
   image = np.transpose(image, (2, 0, 1))
   image = image[np.newaxis, :]
   # 轉(zhuǎn)成 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)

網(wǎng)絡(luò)推導(dǎo) Network inference

只要一行代碼,輸入處理完的圖片,輸出我們要的所有預(yù)測(cè)值和預(yù)設(shè)框。

# 如果有預(yù)先訓(xùn)練好的網(wǎng)絡(luò)參數(shù),可以直接加載

# 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)>

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

轉(zhuǎn)換為可讀的輸出 Convert predictions to real object detection results

要把網(wǎng)絡(luò)輸出轉(zhuǎn)換成我們需要的坐標(biāo),還要最后一步,比如我們需要softmax把分類(lèi)預(yù)測(cè)轉(zhuǎn)換成概率,還需要把偏移量和預(yù)設(shè)框結(jié)合來(lái)得到物體的大小和位置。

非極大抑制(Non-Maximum Suppression)也是必要的一步,因?yàn)橐粋€(gè)物體往往有不只一個(gè)檢測(cè)框。

from mxnet.contrib.ndarray import MultiBoxDetection

# 跑一下softmax, 轉(zhuǎn)成0-1的概率

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

# 把偏移量加到預(yù)設(shè)框上,去掉得分很低的,跑一遍nms,得到最終的結(jié)果

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)>

結(jié)果中,每一行都是一個(gè)可能的結(jié)果框,表示為[類(lèi)別id, 得分, 左邊界,上邊界,右邊界,下邊界],有很多-1的原因是網(wǎng)絡(luò)預(yù)測(cè)到這些都是背景,或者作為被抑制的結(jié)果。

顯示結(jié)果 Display results

數(shù)字永遠(yuǎn)不如圖片來(lái)得直觀

把得到的轉(zhuǎn)換結(jié)果畫(huà)在圖上,就得到我們期待已久的幾十萬(wàn)伏特圖了!

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)

從零開(kāi)始碼一個(gè)皮卡丘檢測(cè)器-CNN目標(biāo)檢測(cè)入門(mén)教程(下)

小結(jié) Conclusion

目標(biāo)檢測(cè)不同于分類(lèi)任務(wù),需要考慮的不只是全圖尺度的單一分類(lèi),而是需要檢測(cè)到不同大小,不同位置的物體,難度自然提升了許多,用掃窗之類(lèi)的傳統(tǒng)方法早已不適合神經(jīng)網(wǎng)絡(luò)這種需要大量計(jì)算需求的新結(jié)構(gòu)。幸好我們可以用本章節(jié)介紹的方法,利用卷積網(wǎng)絡(luò)的特性,一次推導(dǎo)得到全部的預(yù)測(cè)結(jié)果,相對(duì)來(lái)說(shuō)快速且準(zhǔn)確。

我們希望能用較短的篇幅來(lái)描述一個(gè)足夠簡(jiǎn)單的過(guò)程,但是難免會(huì)有疏漏,歡迎各種問(wèn)題和建議,與此同時(shí),我們會(huì)不斷更新教程,并且會(huì)帶來(lái)更多不同的算法,敬請(qǐng)期待。

相關(guān)鏈接

Apache MXNet官方網(wǎng)站: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深度學(xué)習(xí)樣例: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

雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見(jiàn)轉(zhuǎn)載須知

從零開(kāi)始碼一個(gè)皮卡丘檢測(cè)器-CNN目標(biāo)檢測(cè)入門(mén)教程(下)

分享:
相關(guān)文章

編輯

關(guān)注AI學(xué)術(shù),例如論文
當(dāng)月熱門(mén)文章
最新文章
請(qǐng)?zhí)顚?xiě)申請(qǐng)人資料
姓名
電話
郵箱
微信號(hào)
作品鏈接
個(gè)人簡(jiǎn)介
為了您的賬戶安全,請(qǐng)驗(yàn)證郵箱
您的郵箱還未驗(yàn)證,完成可獲20積分喲!
請(qǐng)驗(yàn)證您的郵箱
完善賬號(hào)信息
您的賬號(hào)已經(jīng)綁定,現(xiàn)在您可以設(shè)置密碼以方便用郵箱登錄