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

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

0

從一次 CycleGAN 實現(xiàn)聊聊 TF

本文作者: 汪思穎 2017-11-20 19:16
導語:用 TensorFlow 實現(xiàn) CycleGAN 時需要注意的小技巧

雷鋒網(wǎng) AI科技評論按,本文作者Coldwings,該文首發(fā)于知乎專欄為愛寫程序,雷鋒網(wǎng) AI科技評論獲其授權(quán)轉(zhuǎn)載。以下為原文內(nèi)容,有刪減。

CycleGAN是個很有趣的想法(Unpaired Image-to-Image Translationusing Cycle-Consistent Adversarial Networks [https://arxiv.org/pdf/1703.10593.pdf]),看完這篇論文之后,隱隱地覺得,這后面有更多的內(nèi)容可以挖,我盡我所能做出了各種嘗試,努力發(fā)掘更多的可能性。

實現(xiàn)過程中可以說還是略微糾結(jié)的,最初是用Keras快速實踐了一下,然而其實并不『快速』,后來反倒是用TensorFlow重寫以及嘗試各種意外想法時才感覺,當需要處理一些比較復雜的網(wǎng)絡結(jié)構(gòu)、訓練流程甚至op時,TF提供的可以細化到每個操作的體驗實際上要比各種上層API都來得更好,而結(jié)合TensorBoard,可視化的訓練將取得更好的效果。當然,我對Torch無感,或許用Torch能有更好的體驗,但我不擅長這個;Chainer(A flexible framework for neural networks)講道理寫出來的代碼會更好看,但是似乎身邊用的人并不多,姑且放過。

這篇文章倒不是來介紹什么是CycleGAN的,若是不甚了解,我妻子將會將她的發(fā)表整理一下再發(fā)布出來(CycleGAN(以及DiscoGAN和DualGAN)簡介 - 知乎專欄)。這一陣的嘗試中,我自己也對GAN,對Generator中的圖像甚至其它東西的生成,以及單純從寫代碼角度來看,怎么管理TF里的變量,怎么把代碼寫得好看,怎么更好地利用TensorBoard都有了更多地理解,算是不小的提高吧……

所以這里也就大概提一提一些實現(xiàn)中需要注意的小技巧吧。(雖然我覺得大概大多數(shù)真正拿著TF搞DL研究的人都不需要研究這篇文章)

CycleGAN比較麻煩的地方

其實CycleGAN麻煩的地方不少,這是一個挺復合的模型:兩個Generator,兩個Discriminator,這已經(jīng)是四個比較簡單的網(wǎng)絡了(是的,考慮到所有可能性,Generator和Discriminator完全可以各自都有兩種不同的結(jié)構(gòu));一組Generator+Discriminator復合成一個GAN,又一層復合模型,并且GAN的訓練還得控制,由于G和D的損失相反,訓練G時需要控制D的變量讓其不可訓練;我們還要讓Cycle loss作為模型loss的一部分,這個更高一層的復合模型由兩個GAN組成……

良好的代碼結(jié)構(gòu)

TensorFlow的自由度挺高的,類比的話,有那么點DL框架里的C++的意思;Python的語言靈活度也是高得不行,兩個很靈活的玩意放一起,寫個簡單模型自然想怎么玩就怎么玩,寫個復雜一些的模型,為了保證寫著方便,用著方便,改起來方便,還是需要比較好的代碼結(jié)構(gòu)的。

如果翻翻GitHub上一些比較熱的用TF寫的模型,通常都會發(fā)現(xiàn)大家比較習慣于把代碼分成op、module和model三個部分。

op里是一些通用層或者運算的簡化定義,例如寫個卷積層,總是包含定義變量和定義運算。習慣于Keras這樣不需要自己定義變量的玩意當然不會太糾結(jié),但用TF時,若是寫兩行定義一下變量總是挺讓人傷神的。

如果參照Keras的實現(xiàn),通過寫個類來定制op,變量管理看起來方便一點,未免太過繁瑣。實際上TF提供的variable scope已經(jīng)非常方便了,這一部分寫成這樣似乎也不錯

def conv2d(input, filter, kernel, strides=1, stddev=0.02, name='conv2d'):
   with tf.variable_scope(name):
       w = tf.get_variable(
           'w',
           (kernel, kernel, input.get_shape()[-1], filter),
           initializer=tf.truncated_normal_initializer(stddev=stddev)
       )
       conv = tf.nn.conv2d(input, w, strides=[1, strides, strides, 1], padding='VALID')
       b = tf.get_variable(
           'b',
           [filter],
           initializer=tf.constant_initializer(0.0)
       )
       conv = tf.reshape(tf.nn.bias_add(conv, b), tf.shape(conv))
       return conv

這樣定義幾個op之后,寫起代碼來就更有點類似于mxnet那樣的感覺了。

特別的,有些時候有些簡單結(jié)構(gòu),例如ResNet中的一個block這樣的玩意,我們也可以用類似的方式,用一個簡單函數(shù)包裝起來

def res_block(x, dim, name='res_block'):
   with tf.variable_scope(name):
       y = reflect_pad(x, name='rp1')
       y = conv2d(y, dim, 3, name='conv1')
       y = lrelu(y)
       y = reflect_pad(y, name='rp2')
       y = conv2d(y, dim, 3, name='conv2')
       y = lrelu(y)
       return tf.add(x, y)

對于重復的模塊,這樣的包裝也方便多次使用。

這些是很常見的做法。同時我們也發(fā)現(xiàn)了,幾乎每個這樣的函數(shù)里都少不了一個variable scope的使用,一方面避免定義變量時名字的重復以及訓練時變量的管理,另一方面也方便TensorBoard畫圖的時候能把有用的東西放到一起。但這樣每個函數(shù)里帶個name參數(shù)的做法寫多了也會煩,加上奇怪的縮進……我會更傾向于用一個裝飾器來解決這樣的問題,同時也能減少『忘了用variable scope』的情況。

def scope(default_name):
   def deco(fn):
       def wrapper(*args, **kwargs):
           if 'name' in kwargs:
               name = kwargs['name']
               kwargs.pop('name')
           else:
               name = default_name
           with tf.variable_scope(name):
               return fn(*args, **kwargs)
       return wrapper
   return deco@scope('conv2d')def conv2d(input, filter, kernel, strides=1, stddev=0.02):
   w = tf.get_variable(
       'w',
       (kernel, kernel, input.get_shape()[-1], filter),
       initializer=tf.truncated_normal_initializer(stddev=stddev)
   )
   conv = tf.nn.conv2d(input, w, strides=[1, strides, strides, 1], padding='VALID')
   b = tf.get_variable(
       'b',
       [filter],
       initializer=tf.constant_initializer(0.0)
   )
   conv = tf.reshape(tf.nn.bias_add(conv, b), tf.shape(conv))
   return conv

至于module,也就是一些稍微復雜的成型結(jié)構(gòu),例如GAN里的Discriminator和Generator,講道理這玩意其實和op大體上是類似的,就不多說了。

最后是model。通常大家都是用類來做,因為model中往往還包含了輸入數(shù)據(jù)用的placeholder、訓練用的op,甚至一些具體的方法等等內(nèi)容。這一塊的代碼建議,只不過是最好先寫一個抽象類,把需要的幾個接口給定義一下,然后讓實際的model類繼承,代碼會漂亮很多,也更便于利用諸如PyCharm這樣的IDE來提示你哪些東西該做而沒有做。

關于config/options

網(wǎng)上常見的代碼里,模型的一些參數(shù)信息大都設計成用命令行參數(shù)來傳入,更多是直接使用tf.flags來處理。但無論如何,我仍然覺得定義一個config類來管理參數(shù)是有一定必要性的,直接使用tf.flags主要是是有大段tf.flags.DEFINE_xxx,不好看,也不方便直觀地反應默認參數(shù)。相對的,如果定義一個參數(shù)類,在__init__里寫下默認參數(shù),然后寫個小方法自動地根據(jù)dir來添加這些tf.flags會漂亮許多。但這個只是個人觀點,似乎并沒有具體的優(yōu)劣之分。

關于TensorBoard

不得不說TensorBoard作為TF自帶的配套可視化工具,只要你不是太在意刷新頻率的問題(通常不會有人在意這個吧……),用起來實在太方便。加上能夠自動生成運算的各個符號的結(jié)構(gòu)圖,哪怕不說訓練,就是檢查模型結(jié)構(gòu)是否符合自己所想都是個非常好用的工具。比如封面圖,生成出來用來檢查代碼的模型邏輯,還可以根據(jù)需要點選觀察依賴關系。

從一次 CycleGAN 實現(xiàn)聊聊 TF

順帶一提,如果生成的模型圖長得非常奇怪,八成是代碼有問題……

不過要用好TensorBoard,有幾個小小的要點:首先是,至少,你的各個op和module里,得用上variable scope或者name scope。對于一個scope,在TensorBoard的Graph里會將其聚集成一個小塊,內(nèi)部結(jié)構(gòu)可以展開觀察,而如果不用scope,你會看到滿眼都是一堆一堆的基本op,當模型復雜時,圖基本沒法看……

此外,對于圖片處理,用好TensorBoard的ImageSummary當然是很不錯的選擇。但是記得一定要為添加圖片的summary op定義一個喂數(shù)據(jù)的placeholder。

self.p_img = tf.placeholder(tf.float32, shape=[1, 256 * 6, 256 * 4, 3])

self.img_op = tf.summary.image('sample', self.p_img)

……

img = np.array([img])

s_img = self.sess.run(self.img_op, feed_dict={self.p_img: img})

self.writer.add_summary(s_img, count)

這樣才是正確的。網(wǎng)上有些材料里告訴你可以直接用tf.summary.image('tag', data)來生成圖片summary,這樣其實每次都會構(gòu)造一個新的summary,不便于圖片歸類,但更大的問題是,這樣做會使得每次都申請一個新的變量(用來裝你的圖片數(shù)據(jù)),倘若你有定周期存儲訓練權(quán)重的習慣,會發(fā)現(xiàn)沒幾個小時就會因為權(quán)重變量總量超過2GB而使得程序跑崩……想想看晚上跑著訓練的代碼想著可以回家休息了,結(jié)果前腳剛進家門,程序就罷工了,大好的訓練時間就給直接浪費了。

另外,這里的圖片可以是重新歸為0~255的整形的數(shù)據(jù),也可以直接給浮點數(shù)據(jù)[-1, 1]。更不錯的想法是,先使用matplotlib/pil/numpy來合成、拼湊甚至生成圖像,然后再來添加,會讓效果更令人滿意,比如這樣:

從一次 CycleGAN 實現(xiàn)聊聊 TF

最后補充一句……雙顯示器確實有利于提高寫代碼、改代碼以及碼字的效率……

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

從一次 CycleGAN 實現(xiàn)聊聊 TF

分享:
相關文章

編輯

關注AI學術,例如論文
當月熱門文章
最新文章
請?zhí)顚懮暾埲速Y料
姓名
電話
郵箱
微信號
作品鏈接
個人簡介
為了您的賬戶安全,請驗證郵箱
您的郵箱還未驗證,完成可獲20積分喲!
請驗證您的郵箱
立即驗證
完善賬號信息
您的賬號已經(jīng)綁定,現(xiàn)在您可以設置密碼以方便用郵箱登錄
立即設置 以后再說