《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 可編程邏輯 > 業(yè)界動態(tài) > 僅17 KB,、一萬個權(quán)重的微型風(fēng)格遷移網(wǎng)絡(luò),!

僅17 KB、一萬個權(quán)重的微型風(fēng)格遷移網(wǎng)絡(luò),!

2018-11-30

今天 reddit 上一篇帖子引起了熱議,,博主 jamesonatfritz 稱他將原本具備 1.7M 參數(shù)的風(fēng)格遷移網(wǎng)絡(luò)減少到只有 11,868 個參數(shù),,該網(wǎng)絡(luò)仍然能夠輸出風(fēng)格化的圖像。且量化后的最終網(wǎng)絡(luò)體積僅有 17 kB,,非常適合移動 app,。


jamesonatfritz 想解決神經(jīng)網(wǎng)絡(luò)的過參數(shù)化問題,想要創(chuàng)建體積小但性能優(yōu)的神經(jīng)網(wǎng)絡(luò),。他所試驗的第一個任務(wù)便是藝術(shù)風(fēng)格遷移,。


GitHub 鏈接:https://github.com/fritzlabs/fritz-style-transfer


現(xiàn)在有很多用來訓(xùn)練藝術(shù)風(fēng)格遷移模型的現(xiàn)成工具,還有上千種開源實現(xiàn),。其中的多數(shù)工具利用 Johnson 等人在《Perceptual Losses for Real-Time Style Transfer and Super-Resolution》中提出的網(wǎng)絡(luò)架構(gòu)的變體來實現(xiàn)快速,、前饋的風(fēng)格化。因此,,多數(shù)遷移模型的大小是 7MB,。對于你的應(yīng)用來說,這個負(fù)擔(dān)并非不可承受,,但也并非無足輕重,。


研究表明,神經(jīng)網(wǎng)絡(luò)的體積通常遠(yuǎn)遠(yuǎn)大于所需,,數(shù)百萬的權(quán)重中有很多并不重要,。因此作者創(chuàng)造了一個體積大大縮小的可靠風(fēng)格遷移模型:一個只有 11686 個訓(xùn)練權(quán)重的 17KB 神經(jīng)網(wǎng)絡(luò)。

微信圖片_20181130192911.jpg

左:原圖,;中:來自上述 17KB 模型的風(fēng)格化圖像,;右:來自 7MB 模型的風(fēng)格化圖像,。


快速概覽:


原始模型:

大小:7MB

權(quán)重數(shù):1.7M

在 iPhone X 上的速度:18 FPS


小模型:

大?。?7KB

權(quán)重數(shù):11,868

在 iPhone X 上的速度:29 FPS


如何縮小風(fēng)格遷移模型


作者主要使用了兩種技術(shù),而且都可以泛化到其他模型:


1. 大刀闊斧地修剪層和權(quán)重,;

2. 通過量化將 32 位浮點權(quán)重轉(zhuǎn)換為 8 位整型


機(jī)器之心Synced剪枝小程序


修剪策略


卷積神經(jīng)網(wǎng)絡(luò)通常包含數(shù)百萬甚至上億個需要在訓(xùn)練階段進(jìn)行調(diào)整的權(quán)重,。通常來講,權(quán)重越多準(zhǔn)確率越高,。但這種增加權(quán)重提高準(zhǔn)確率的做法非常低效,。谷歌 MobileNetV2 的 stock 配置具有 347 萬個權(quán)重,內(nèi)存占用達(dá) 16MB,。InceptionV3 架構(gòu)大小約為前者的 6 倍,,具備 2400 萬個權(quán)重,內(nèi)存占用達(dá) 92MB,。盡管多了 2000 多萬個權(quán)重,,但 InceptionV3 在 ImageNet 上的 top-1 分類準(zhǔn)確率只比 MobileNetV2 高出 7 個百分點(80% vs 73%)。


因此,,我們可以假設(shè)神經(jīng)網(wǎng)絡(luò)中的多數(shù)權(quán)重沒有那么重要并將其移除,。但重點是怎么做呢?我們可以選擇在三個層面進(jìn)行修剪:單個權(quán)重,、層,、塊。


權(quán)重層面:假設(shè)某個神經(jīng)網(wǎng)絡(luò)上的多數(shù)(>95%)權(quán)重都沒有什么用,。如果能找出那些對準(zhǔn)確率有影響的權(quán)重,,就可以將其留下并將其他移除。


層層面:每個層中都包含一些權(quán)重,。例如,,2D 卷積層具有一個權(quán)重張量,即卷積核,,用戶可以定義其寬度,、高度和深度??s小卷積核可以減小整個網(wǎng)絡(luò)的大小,。


塊層面:多個層通常可以結(jié)合成可重復(fù)利用的子圖,,即塊,。以 ResNet 為例,它的名字來源于重復(fù) 10-50 次的「殘差塊」,。在塊層面進(jìn)行修剪可以移除多個層,,從而一次性移除多個參數(shù),。


在實踐中,稀疏張量運(yùn)算沒有很好的實現(xiàn),,因此權(quán)重層面的修剪沒有多大價值,。那么就只剩下層和塊層面的修剪了。


實踐中的修剪


作者使用的層修剪技術(shù)是引入 width multiplier 作為超參數(shù),。width multiplier 最初由谷歌在其論文《MobileNets: Efficient Convolutional Neural Networks for Mobile Vision》中提出,,非常簡單、高效,。


width multiplier 利用一個恒定系數(shù)調(diào)整每個卷積層中的卷積核數(shù)量,。對于給定的層及 width multiplier alpha,卷積核數(shù)量 F 變?yōu)?alpha * F,。


有了這個超參數(shù),,我們就可以生成一系列架構(gòu)相同但權(quán)重數(shù)不同的網(wǎng)絡(luò)。訓(xùn)練每種配置,,就可以在模型速度,、大小及準(zhǔn)確率之間做出權(quán)衡。


下面是作者模仿 Johnson 等人在《The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks》提出的網(wǎng)絡(luò)架構(gòu)構(gòu)建快速風(fēng)格遷移模型的方法,,不同之處在于添加了 width multiplier 作為超參數(shù),。


@classmethod
def build(
        cls,
        image_size,
        alpha=1.0,
        input_tensor=None,
        checkpoint_file=None):
    """Build a Transfer Network Model using keras' functional API.
    Args:
        image_size - the size of the input and output image (H, W)
        alpha - a width parameter to scale the number of channels by
    Returns:
        model: a keras model object
    """
    x = keras.layers.Input(
        shape=(image_size[0], image_size[1], 3), tensor=input_tensor)
    out = cls._convolution(x, int(alpha * 32), 9, strides=1)
    out = cls._convolution(out, int(alpha * 64), 3, strides=2)
    out = cls._convolution(out, int(alpha * 128), 3, strides=2)
    out = cls._residual_block(out, int(alpha * 128))
    out = cls._residual_block(out, int(alpha * 128))
    out = cls._residual_block(out, int(alpha * 128))
    out = cls._residual_block(out, int(alpha * 128))
    out = cls._residual_block(out, int(alpha * 128))
    out = cls._upsample(out, int(alpha * 64), 3)
    out = cls._upsample(out, int(alpha * 32), 3)
    out = cls._convolution(out, 3, 9, relu=False, padding='same')
    # Restrict outputs of pixel values to -1 and 1.
    out = keras.layers.Activation('tanh')(out)
    # Deprocess the image into valid image data. Note we'll need to define
    # a custom layer for this in Core ML as well.
    out = layers.DeprocessStylizedImage()(out)
    model = keras.models.Model(inputs=x, outputs=out)

注意,模型構(gòu)建器類的其余部分沒有顯示,。


當(dāng) alpha=1.0 時,,得到的網(wǎng)絡(luò)包含 170 萬個權(quán)重。當(dāng) alpha=0.5 時,,得到的網(wǎng)絡(luò)僅有 424,102 個權(quán)重,。


你可以構(gòu)建一些寬度參數(shù)很小的網(wǎng)絡(luò),但是也有相當(dāng)多的重復(fù)塊,。作者決定修剪掉一些,,但實際操作后卻發(fā)現(xiàn)不能移除太多。即使參數(shù)量保持不變,,較深的網(wǎng)絡(luò)能夠產(chǎn)生更好的結(jié)果,。作者最終刪除了五個殘差塊中的兩個,并將每層的默認(rèn)濾波器數(shù)量減少至 32 個,。得到的微型網(wǎng)絡(luò)如下所示:


@classmethod
def build(
        cls,
        image_size,
        alpha=1.0,
        input_tensor=None,
        checkpoint_file=None):
    """Build a Small Transfer Network Model using keras' functional API.
    This architecture removes some blocks of layers and reduces the size
    of convolutions to save on computation.
    Args:
        image_size - the size of the input and output image (H, W)
        alpha - a width parameter to scale the number of channels by
    Returns:
        model: a keras model object
    """
    x = keras.layers.Input(
        shape=(image_size[0], image_size[1], 3), tensor=input_tensor)
    out = cls._convolution(x, int(alpha * 32), 9, strides=1)
    out = cls._convolution(out, int(alpha * 32), 3, strides=2)
    out = cls._convolution(out, int(alpha * 32), 3, strides=2)
    out = cls._residual_block(out, int(alpha * 32))
    out = cls._residual_block(out, int(alpha * 32))
    out = cls._residual_block(out, int(alpha * 32))
    out = cls._upsample(out, int(alpha * 32), 3)
    out = cls._upsample(out, int(alpha * 32), 3)
    out = cls._convolution(out, 3, 9, relu=False, padding='same')
    # Restrict outputs of pixel values to -1 and 1.
    out = keras.layers.Activation('tanh')(out)
    # Deprocess the image into valid image data. Note we'll need to define
    # a custom layer for this in Core ML as well.
    out = layers.DeprocessStylizedImage()(out)
    model = keras.models.Model(inputs=x, outputs=out)

帶有寬度參數(shù)的較小風(fēng)格遷移網(wǎng)絡(luò),。


通過反復(fù)嘗試,作者發(fā)現(xiàn)仍然可以用上述架構(gòu)實現(xiàn)良好的風(fēng)格遷移,,一直到寬度參數(shù)為 0.3,,在每一層上留下 9 個濾波器。最終結(jié)果是一個只有 11,868 個權(quán)重的神經(jīng)網(wǎng)絡(luò),。任何權(quán)重低于 10000 的網(wǎng)絡(luò)都不能持續(xù)訓(xùn)練,,并且會產(chǎn)生糟糕的風(fēng)格化圖像,。


值得一提的是,剪枝技術(shù)是在網(wǎng)絡(luò)訓(xùn)練之前應(yīng)用的,。在訓(xùn)練期間或訓(xùn)練后反復(fù)修剪,,你可以在很多任務(wù)上實現(xiàn)更高的性能。


量化


最后一段壓縮是在網(wǎng)絡(luò)訓(xùn)練完成后進(jìn)行的,。神經(jīng)網(wǎng)絡(luò)權(quán)重通常存儲為 64 位或 32 位浮點數(shù),。量化過程將每一個浮點權(quán)重映射到具有較低位寬的整數(shù)。從 32 位浮點權(quán)重變?yōu)?8 位整型,,使得存儲大小減少了 4 倍。作者利用 Alexis Creuzot 在博客中提出的方法(https://heartbeat.fritz.ai/reducing-coreml2-model-size-by-4x-with-quantization-in-ios12-b1c854651c4),,在不怎么影響風(fēng)格的情況下使浮點數(shù)降低到了 8 位量化,。


現(xiàn)在所有主要的移動框架都支持量化,如 TensorFlow Mobile,、TensorFlow Lite,、Core ML 和 Caffe2Go。


最終結(jié)果


該微型網(wǎng)絡(luò)架構(gòu)有 11,868 個參數(shù),,相比之下,,Johnson 最初的模型具有 170 萬個參數(shù),大小為 1.7MB,。當(dāng)轉(zhuǎn)化為 Core ML 并量化時,,最終大小僅為 17KB——為原始大小的 1/400。以下是在梵高的《Starry Night》上的訓(xùn)練結(jié)果,。


此微型風(fēng)格遷移結(jié)果的實時視頻可在 Heartbeat App 上查看:

http://bit.ly/heartbeat-ios

微信圖片_20181130192956.gif


作者驚訝地發(fā)現(xiàn),,盡管尺寸相差 400 倍,但在 iPhone X 上,,這款微型模型的運(yùn)行速度僅快了 50%,。原因可能是計算與這一通用架構(gòu)相關(guān),也可能是將圖像遷移到 GPU 進(jìn)行處理時造成的,。


如果你對結(jié)果表示懷疑,,可以自己下載并運(yùn)行此微型模型。甚至訓(xùn)練自己的模型,!


下載地址:https://github.com/fritzlabs/fritz-style-transfer/blob/master/example/starry_night_640x480_small_a03_q8.mlmodel


總而言之,,作者用兩種簡單的技術(shù)將風(fēng)格遷移神經(jīng)網(wǎng)絡(luò)的規(guī)模減小了 99.75%。使用簡單的 width multiplier 超參數(shù)修剪層,,訓(xùn)練后的權(quán)重從 32 位浮點數(shù)量化為 8 位整數(shù),。未來,作者期待看到將這些方法泛化到其它神經(jīng)網(wǎng)絡(luò)的效果,。風(fēng)格遷移相對簡單,,因為「準(zhǔn)確率」肉眼可見,。對于圖像識別這樣更加可以量化的任務(wù)而言,如此極端的修剪可能帶來更明顯的性能下降,。


Reddit 討論


這篇帖子下有一些 reddit 網(wǎng)友對該項目提出了質(zhì)疑:


gwern:


看你的博客,,剪枝部分似乎沒有移除任何層,只是更改了層的寬度/濾波器,,然后對所有參數(shù)進(jìn)行量化,。如果所有層都在(因為你沒有做任何類似于訓(xùn)練較寬的淺層網(wǎng)絡(luò)的工作來模仿原始深度教師網(wǎng)絡(luò)),那么它們?nèi)詫暮罄m(xù)計算的每一層中引入大量延遲,,即使每一層都很小,。(由于你可以在手機(jī) GPU 上安裝更多模型,每個模型使用較少的 FLOPS,,因此整體吞吐量會變得更好,。但是每個模型的迭代仍然需要一段時間,在特定大小之后,,每一層基本上是即時的,。)


jamesonatfritz 回復(fù):


你說得對,濾波器剪枝部分確實沒有移除層,,但是我去掉了兩個殘差塊,,從而消除了一些層。整體 FLOPs 的降低情況不如全部權(quán)重數(shù)量的減少情況,,這一點你說得對,。不幸的是,Apple 沒法讓你較好地控制模型運(yùn)行的位置,。你無法強(qiáng)制該模型使用 GPU,。一些啟發(fā)式方法導(dǎo)致較小的模型僅在 CPU 上運(yùn)行,這是可能的,。


gwern:


「去掉了兩個殘差塊,,從而消除了一些層?!?/p>


你借此獲得了一些加速,,但是使用更扁平的模型或許會實現(xiàn)更多加速。


jamesonatfritz 回復(fù):


確實如此,。我試過的最扁平模型只有一個卷積層,、一個殘差模塊和一個上采樣模塊,但我發(fā)現(xiàn)這些變體無法收斂,。


gwern:


這似乎有些過了:只有一個層有些過于難了,。我想的是三四個層這樣,在預(yù)訓(xùn)練風(fēng)格遷移模型的確切像素輸出上進(jìn)行訓(xùn)練?;蛟S值得一試,。


Ikuyas:


這個方法和直接使用小模型有什么區(qū)別嗎?我確定使用 11,868 個參數(shù)進(jìn)行訓(xùn)練結(jié)果會更好,。另外,,1.7M 參數(shù)太大了,每個參數(shù)的貢獻(xiàn)估計會很小,。但是,,真實情況是只有幾百個參數(shù)是真正重要的,其他參數(shù)只是到處吸收一點微小的噪聲,。


從標(biāo)準(zhǔn)回歸的角度來看,,這似乎是完美的預(yù)期結(jié)果。


作者回復(fù):


我應(yīng)該在文章里寫清楚的,,事實上你所說的正是我所做的,。剪枝發(fā)生在訓(xùn)練之前。反直覺的一件事是,,實際上使用較少的參數(shù)從頭開始訓(xùn)練模型無法確保能得到一樣的結(jié)果。盡管一小部分權(quán)重比較重要,,但你很難提前知道哪些權(quán)重是重要的,。詳情參見論文:《The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks》。


Ikuyas:


神經(jīng)網(wǎng)絡(luò)中的參數(shù)缺乏有意義的解釋,,這是第一堂機(jī)器學(xué)習(xí)課程中就學(xué)過的,。這并不反直覺,而是預(yù)料之中,。剪枝后的參數(shù)甚至并不被認(rèn)為是吸收噪聲的神經(jīng)元,。對于標(biāo)準(zhǔn)回歸模型來說,噪聲有時似乎像是正態(tài)分布的實現(xiàn),。而神經(jīng)網(wǎng)絡(luò)擬合并不假設(shè)任何此類事情,。因此使用較少的參數(shù),你可以用完全不同的模型擬合數(shù)據(jù),。剪枝技術(shù)并沒有什么用,。


本站內(nèi)容除特別聲明的原創(chuàng)文章之外,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,,并不代表本網(wǎng)站贊同其觀點,。轉(zhuǎn)載的所有的文章、圖片,、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有,。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者。如涉及作品內(nèi)容、版權(quán)和其它問題,,請及時通過電子郵件或電話通知我們,,以便迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失,。聯(lián)系電話:010-82306118,;郵箱:[email protected]