中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

用瀏覽器訓練Tensorflow.js模型的18個技巧

2018-10-16    來源:raincent

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

在移植現有模型(除tensorflow.js)進行物體檢測、人臉檢測、人臉識別后,我發(fā)現一些模型不能以最佳性能發(fā)揮。而tensorflow.js在瀏覽器中表現相當不錯,如果你想見證瀏覽器內部機器學習的潛力以及tensorflow.js為我們的Web開發(fā)人員提供的所有可能性,我個人建議你可以嘗試下。

但是,由于深度學習模型無法直接在瀏覽器中運行,因為這些模型不是專為在瀏覽器中運行而設計的,更不用說在移動端了。以現有技的物體探測器為例:它們通常需要大量的計算資源才能以合理的fps運行,更不用說以實時速度運行了。此外,在簡單的Web應用程序中將100MB+模型權重加載到客戶端瀏覽器是根本不可行的。
 

Web訓練高效的深度學模型


但讓我告訴你,我們能夠構建和訓練相當錯的模型,這些模型通過設計一些基本原則可以在Web環(huán)境中運行進行。信不信由你:我們可以訓練相當不錯的圖像分類-甚至物體檢測模型,最終只有幾兆字節(jié)大小甚至只有幾千字節(jié):

在本文中,我想你一些關于開始訓練你自己的卷CNN)的一般技巧,有一些技巧是直接針對瀏覽器中使用量流訓練CNN。

現在你可能想知道:為什么我可以在瀏覽器中使用tensorflow.js訓練我的模型,在我的機器上如何使用tensorflow訓練它們?當然,如果你的機器配備了NVIDIA卡,你也可以這樣做。在瀏覽器中訓練深度學習的一個巨大的優(yōu)勢,它的引擎蓋下利用WebGL的,這就意味著你不需要NVIDIA GPU訓練的模式,而是AMD GPU上訓練深度學習模型。

因此,如果你的機器配備了NVIDIA卡,你可以簡單地采用標準張量流方法(在這種情況下,你可以在python中編寫訓練代碼)并忽略瀏覽器的提示,F在,讓我們開始吧!
 

架構


在開始訓練我們自己的圖像分類器、對象檢測器之前,我們必須首先實現網絡架構。我通常建議選擇現有的架構,例如YoloSSD、ResNet、MobileNet等。

就個人而言,我認為在你自己的架構中使用這些架構所采用的一些概念是很有價值的。然而,正如我最初指出的那樣,簡單地采用這些架構不會讓我們的模型是體積小,推理快易于訓練成為可能。

無論你是想要適應現有架構還是從頭開始,我都想給你以下建議,這有助于為Web設計高效的CNN架構:
 

1.從小型網架構開始!


記住,我們的網絡越小,同時仍能在解決問題時獲得良好的準確性,它在推理時間內執(zhí)行的速度就越快,客戶端下載和緩存該模型就越容易。此外,較小的模型具有較少的參數,因此在訓練時會更快地收斂。

如果你發(fā)現當前的網絡架構性能不佳,或者達不到準確性水平,你仍然希望是它,你可以逐步增加網絡的大小,例如通過增加每層卷積濾波器的數量或堆疊更多層簡單地使你的網絡更深入。
 

2.采用深度可分的卷!


由于我們正在訓練一個新模型,我們希望明確使用深度可分卷積而不是普通2D卷積。深度可分離卷積將常規(guī)卷積運算分成深度卷積,然后是逐點(1x1)卷積。與常規(guī)卷積操作相比,它們具有更少的參數,這將使用更少的浮點運算并且更容易并行化,這意味著推斷將更快(我通過簡單地替換常規(guī)卷積來進行推斷的速度提升高達10倍)和較少的資源消耗。此外,因為它們具有較少的參數,所以訓練它們所花費的時間較少。

MobileNetXception采用了深度可分離卷積的思想,你可以在MobileNetPoseNet的tensorflow.js模型中找到它們。深度可分離卷積是否導致模型不太準確可能是一個公開的辯論,但根據我的經驗,它們絕對是網絡模型的方式。

長話短說:我建議在你的第一層使用常規(guī)的沒有那么多的參數的conv2d操作,以保留提取的特征中的RGB通道之間的關系。

 

export type ConvParams = {
filter: tf.Tensor4D
bias: tf.Tensor1D
}
export function convLayer(
x: tf.Tensor4D,
params: ConvParams,
stride: [number, number],
padding: string
): tf.Tensor4D {
return tf.tidy(() => {
let out = tf.conv2d(x, params.filter, stride, padding)
out = tf.add(out, params.bias)
return out
})
}

 

對于其余的卷積,只需使用深度可分離的卷積。因此,我將使用3x3xchannels_inx1深度波器和1x1x channels_in x channels_out逐點波器,而不是個內核。

 

export type SeparableConvParams = {
depthwise_filter: tf.Tensor4D
pointwise_filter: tf.Tensor4D
bias: tf.Tensor1D
}
export function depthwiseSeparableConv(
x: tf.Tensor4D,
params: SeparableConvParams,
stride: [number, number],
padding: string
): tf.Tensor4D {
return tf.tidy(() => {
let out = tf.separableConv2d(x, params.depthwise_filter: tf.Tensor4D, params.pointwise_filter, stride, padding)
out = tf.add(out, params.bias)
return out
})
}

 

并且,不是使用tf.conv2d與具有形狀的內核[3,3,32,64],我們將簡單地使用tf.separableConv2d深度方向內核的形狀為[3,3,32,1]以及形狀為[1,1,32,64]狀作為點狀核。
 

3.過連接(Skip connections)和密集接(Densely Connected)的


一旦決定建立更深層的網絡,很快就面臨著訓練神經網絡最常見的問題之一:梯度消失問題。在一些時期之后,損失只會在非常微小的步驟中減少,這會增加訓練時間或者導致模型不收斂。

ResNet和DenseNet中使用的跳過連接允許它們構建更深層的體系結構,同時減輕梯度消失問題。我們所要做的就是在應用激活函數之前,將先前層的輸出添加到位于網絡中更深層的層輸入中:

4

跳過連接

跳過連接工作,通過快捷方式連接圖層。這種技術背后的本質是,梯度不必僅通過卷積(或完全連接)反向傳播,這導致梯度一旦到達網絡的早期層就會減少。它們可以通過跳過連接的添加操作來“skip”層。

顯然,假設你想要將A層與B層連接,A的輸出形狀必須與B的輸入形狀相匹配。如果你想構建殘差或密集連接的塊,只需確保在該塊的卷積中保持相同數量的濾波器,并使用相同的填充保持1的步幅。正如旁注一樣,也有不同的方法,它們填充A的輸出,使其與輸入B的形狀匹配,或者連接來自先前層的特征映射,使得連接層的深度再次匹配。

起初,我嘗試使用類似ResNet的方法,只需在其他層之間引入跳過連接,如上圖所示,但很快就發(fā)現,密集連接的塊工作得更好,并且可以很快的收斂:

5

密集塊

這是一個密集塊實現的例子,我用它作為面部標志檢測器的基本構建塊face-api.js。其中一個塊涉及4個可深度分離的卷積層(注意,第一個密集塊的第一個卷積是常規(guī)卷積),每個塊的第一個卷積運算使用2的步幅來縮小輸入:

 

export type DenseBlock4Params = {
conv0: SeparableConvParams | ConvParams
conv1: SeparableConvParams
conv2: SeparableConvParams
conv3: SeparableConvParams
}


export function denseBlock4(
x: tf.Tensor4D,
denseBlockParams: DenseBlock4Params,
isFirstLayer: boolean = false
): tf.Tensor4D {
return tf.tidy(() => {
const out0 = isFirstLayer
? convLayer(x, denseBlockParams.conv0 as ConvParams, [2, 2], 'same')
: depthwiseSeparableConv(x, denseBlockParams.conv0 as SeparableConvParams, [2, 2], 'same')
as tf.Tensor4D
const in1 = tf.relu(out0) as tf.Tensor4D
const out1 = depthwiseSeparableConv(in1, denseBlockParams.conv1, [1, 1], 'same')


// first join
const in2 = tf.relu(tf.add(out0, out1)) as tf.Tensor4D
const out2 = depthwiseSeparableConv(in2, denseBlockParams.conv2, [1, 1], 'same')


// second join
const in3 = tf.relu(tf.add(out0, tf.add(out1, out2))) as tf.Tensor4D
const out3 = depthwiseSeparableConv(in3, denseBlockParams.conv3, [1, 1], 'same')


// final join
return tf.relu(tf.add(out0, tf.add(out1, tf.add(out2, out3)))) as tf.Tensor4D
})
}

4.使用ReLU類型激活函數!


除非你有特定的理由使用其他類型的激活函數,否則就使用tf.relu。原因很簡單,ReLU類型激活函數有助于緩解梯度消失的問題。

你還可以嘗試ReLU的變體,例如leaky ReLU,它正在Yolo架構中使用:

 

export function leakyRelu(x: tf.Tensor, epsilon: number) {
return tf.tidy(() => {
const min = tf.mul(x, tf.scalar(epsilon)) 
return tf.maximum(x, min)
})
}

或者正在移使用的ReLU-6

export function relu6(x: tf.Tensor) {
return tf.clipByValue(x, 0, 6)
}

 

訓練


一旦我們完成初始架構,我們就可以開始訓練我們的模型了。
 

5.如果有疑,只需使用Adam Optimizer


當第一次開始訓練自己的模型時,我想知道哪種優(yōu)化器最好?我一開始使用普通的SGD,它似乎有時會陷入局部最小值中,甚至導致梯度爆炸,以至于模型權重無限增長,最終導致NaNs。

我并不是說,Adam是所有問題的最佳選擇,但我發(fā)現它是訓練新模型最簡單且最強大的方法,只需使用默認參數和學習率為0.001Adam開始:

 

const optimizer = tf.train.adam(0.001)

 

6.調整學習


一旦損失沒有顯著下降,很可能,我們的模型確實收斂,并且無法進一步學習。此時我們不妨停止訓練過程,以防止我們的模型不會出現過度擬合。

但是,你可以通過調整(降低)學習率來避免它發(fā)生。特別是如果在訓練集上計算的總損失開始振蕩,這表明嘗試降低學習率可能是個好主意。

下面是一個示例,顯示了訓練面部標志模型時整體誤差的圖表。在46epoch,損失值開始振蕩。正如你所看到的那樣,繼續(xù)訓練從46epoch的檢查點再學習10個以上,學習率為0.0001而不是0.001這能夠進一步降低整體誤差:

6
 

7.權重初始化


如果你對如何正確初始化模型權重沒有任何線索:一個簡單的經驗法則,從某種正態(tài)分布中得出,用零初始化所有偏差(tf.zeros(shape))和你的權重(卷積的核和全連接層的權重)與非零值。例如,你可以簡單地使用tf.randomNormal(shape),但是現在我更喜歡使用glorot正態(tài)分布,這在tfjs-layers中是可用的,如下所示:

const initializer = tf.initializers.glorotNormal()
const depthwise_filter = initializer.apply([3, 3, 32, 1])
const pointwise_filter = initializer.apply([1, 1, 32, 64])
const bias = tf.zeros([64])

8.隨機你的入!


訓練神經網絡的一個常見建議是通過在每個時期開始時對輸入進行混洗來隨機化訓練樣本。我們可以使用tf.utils.shuffle來實現這個目的:

 

/** Shuffles the array using Fisher-Yates algorithm. */
export function shuffle(array: any[]|Uint32Array|Int32Array|Float32Array): void

 

9.使用FileSaver.js保存模型檢查


由于我們在瀏覽器中訓練我們的模型,你現在可能會問自己:我們如何在訓練時自動保存模型權重的檢查點?我們可以使用FileSaver.js,該腳本公開了一個名為saveAs的函數,我們可以使用它來存儲任意類型的文件,這些文件最終會出現在我們的下載文件夾中。

這樣我們就可以保存模型權重:

const weights = new Float32Array([... model weights, flat array])
saveAs(new Blob([weights]), 'checkpoint_epoch1.weights')

甚至是json文件:

const losses = { totalLoss: ... }
saveAs(new Blob([JSON.stringify(losses)]), 'loss_epoch1.json')

排除故障


在花費大量時間訓練模型之前,你需要確保你的模型實際上要學習是什么,并消除任何潛在的錯誤來源。如果你不考慮以下提示,你可能會浪費你的時間在訓練垃圾上:


 

10.檢查輸入數據,預處理和后處理邏輯!


如果你將垃圾傳遞到你的網絡,它定會把垃圾扔回你身邊。因此,請確保你的輸入數據標記正確,并確保你的網絡輸入符合你的預期。特別是如果你已經規(guī)定了一些預處理邏輯,如隨機裁剪、填充、平方、居中、平均減法或其他什么,請確保預處理后進行可視化輸入。此外,我強烈建議單元測試這些步驟。

這聽起來像是一項繁瑣的額外工作,但它很重要!
 

11.檢查你的損失函數!


現在在大多數情況下,tensorflow.js為你提供了你需要的損失函數。但是,如果你需要實現自己的損失函數,你絕對需要進行單元測試!不久前,我從頭開始使用tfjs-core API實現了Yolo v2 dropout函數,以便為網絡訓練yolo對象檢測器。

12.先裝上一個小型數據集!

通常,最好是在訓練數據的一小部分上過度擬合,以驗證損失是否正在收斂,以及你的模型實際上是否在學習一些有用的東西。因此,你應該只選擇10到20張訓練數據的圖像并訓練一些時期。一旦損失收斂,對這10到20張圖像進行推斷并可視化結果:

c5a03f829940958affae53ff85c0c65f228822a5

這是一個非常重要的步驟,它將幫助你消除網絡實施中的各種錯誤來源、前后處理邏輯。

特別是,如果你正在實現自己的損失函數,你肯定要確保,你的模型能夠在開始訓練之前收斂!
 

性能


最后,我想給你一些建議,通過考慮一些基本原則,這將有助于你盡可能地減少訓練時間并防止瀏覽器因內存泄漏而崩潰。
 

13.防止明的內存泄漏


除非你是tensorflow.js的新手,否則你可能已經知道,我們必須手動處理未使用的張量來釋放內存,方法是調用tensor.dispose()或將我們的操作包裝在tf.tidy塊中。確保由于未正確處理張量而導致沒有此類內存泄漏,否則你的應用程序遲早會耗盡內存。

識別這些類型的內存泄漏非常簡單,只需記錄tf.memory()幾次迭代即可驗證,每次迭代時張量的數量不會無意中增長:


 

14.調整Canvases大小而不是你的張量


注意,以下語句僅在tfjs-core的當前狀態(tài)時有效,直到最終得到修復。

這可能聽起來有點奇怪:為什么不使用tf.resizeBilineartf.pad等將輸入張量重塑為所需的網絡輸入形狀?tfjs目前有一個未解決的問題,說明了這個問題。

TLDR:在調用tf.fromPixels之前,要將Canvaes轉換為張量,請調整Canvaes的大小,使其具有網絡接受的大小,否則你將快速耗盡GPU內存,具體取決于各種不同的輸入大小。如果你的訓練圖像大小都相同,那么這個問題就不那么嚴重了,但是如果你必須明確調整它們的大小,你可以使用下面的代碼片段:

 

export function imageToSquare(img: HTMLImageElement | HTMLCanvasElement, inputSize: number): HTMLCanvasElement {
const dims = img instanceof HTMLImageElement 
? { width: img.naturalWidth, height: img.naturalHeight }
: img 
const scale = inputSize / Math.max(dims.height, dims.width)
const width = scale * dims.width
const height = scale * dims.height


const targetCanvas = document.createElement('canvas')
targetCanvas .width = inputSize
targetCanvas .height = inputSize
targetCanvas.getContext('2d').drawImage(img, 0, 0, width, height)
return targetCanvas
}

15.確定最佳批量大小


不要過分批量輸入!嘗試不同的批量大小并測量反向傳播所需的時間。最佳批量大小顯然取決于你的GPU統(tǒng)計信息,輸入大小以及網絡的復雜程度。在某些情況下,你根本不想批量輸入。

如果有疑問的話,我會一直使用1的批量大小。我個人認為,在某些情況下,增加批量大小對性能沒有任何幫助,但在其他情況下,我可以看到整體加速的因素通過創(chuàng)建大小為1624批次,在相當小的網絡尺寸下輸入圖像大小為112x112像素,大約1.5-2.0左右。
 

16.緩存、離線、Indexeddb


我們的訓練圖像可能相當大,可能高達1GB甚至更大,具體取決于圖像的大小和數量。由于我們不能簡單地在瀏覽器中從磁盤讀取圖像,我們將使用文件代理(可能是一個簡單的快速服務器)來托管我們的訓練數據,瀏覽器將獲取每個數據項。

顯然,這是非常低效的,但是在瀏覽器中進行訓練時我們必須記住這一點,如果你的數據集足夠小,你可能會嘗試將整個數據保存在內存中,但這顯然也不是很有效。最初,我試圖增加瀏覽器緩存大小以簡單地將整個數據緩存在磁盤上,但這在以后的Chrome版本中似乎不再起作用,而且我也沒有運氣FireFox。

最后,我決定只使用Indexeddb,這是一個瀏覽器數據庫,你可能不太熟悉,我們可以利用它來存儲我們的整個訓練和測試數據集。Indexeddb入門非常簡單,因為我們基本上只需幾行代碼即可將整個數據存儲和查詢?yōu)殒I值存儲。使用Indexeddb,我們可以方便地將標簽存儲為普通的json對象,將我們的圖像數據存儲為blob?纯催@篇博文,很好地解釋了如何在Indexeddb中保存圖像數據和其他文件。

查詢Indexeddb是非?斓模辽傥野l(fā)現查詢每個數據項的速度要快一些,而不是一遍又一遍地從代理服務器中獲取文件。此外,在將數據移動到Indexeddb之后,技術上的訓練現在完全脫機,這意味著我們可能不再需要代理服務器了。
 

17.異步


這是一個簡單但非常有效的提示,它幫助我減少了訓練時的迭代次數。主要的作用是,如果我們想要檢索由optimizer.minimize返回的損失張量的值,我們肯定會這樣做,因為我們想要在訓練時跟蹤我們的損失,我們希望避免等待損失返回的lose.data()及防止等待CPU和GPU在每次迭代時同步。相反,我們想要執(zhí)行類似以下的操作來報告迭代的損失值:

const loss = optimizer.minimize(() => {
const out = net.predict(someInput)
const loss = tf.losses.meanSquaredError(
groundTruth,
out,
tf.Reduction.MEAN
)
return loss
}, true)
loss.data().then(data => {
const lossValue = data[0]
window.lossValues[epoch] += (window.lossValues[epoch] || 0) + lossValue
loss.dispose()
})

只需住,我在是異步告的,所以如果我想在每個epoch的末尾將整體失保存到文件中,我將不得不等待最后的解決方案。我通常只是通使用setTimeout在一個epoch完成后10秒左右保存整體來解決問題

if (epoch !== startEpoch) {
// ugly hack to wait for loss datas for that epoch to be resolved
const previousEpoch = epoch - 1
setTimeout(() => storeLoss(previousEpoch, window.losses[previousEpoch]), 10000)
}

 

成功訓練模型后


18.權重量化


一旦我們完成了對模型的訓練并且我們對它的性能感到滿意,我建議通過應用權重量化來縮小模型大小。通過量化我們的模型權重,我們可以將模型的大小減小到原始大小的1/4!盡可能減小模型的大小對于將模型權重快速傳遞到客戶端應用程序至關重要,特別是如果我們基本上可以免費獲得它。

文章原標題《18-tips-for-training-your-own-tensorflow-js-models-in-the-browser》

作者:Vincent Mühler 譯者:虎說八道

標簽: isp ssd web環(huán)境 代理服務器 代碼 服務器 腳本 數據庫 網絡

版權申明:本站文章部分自網絡,如有侵權,請聯系:west999com@outlook.com
特別注意:本站所有轉載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權歸原作者所有,如需使用,請與原作者聯系。

上一篇:計算機科學專業(yè)畢業(yè)?這是給你的職業(yè)建議(亮點在最后)

下一篇:Uber開源Marmaray:基于Hadoop的通用數據攝取和分散框架