當前位置:
首頁 > 新聞 > 請快點粘貼複製,這是一份好用的TensorFlow代碼集

請快點粘貼複製,這是一份好用的TensorFlow代碼集

TensorFlow 雖然是目前最為流行的神經網路框架,卻以「難於上手」著稱(Jeff Dean:怪我咯)。有些時候,我們需要簡明扼要的代碼來指點迷津。最近,來自 NCsoft AI 研究部門的 Junho Kim 就放出了一份這樣的 TensorFlow 代碼集。它類似於一個迷你版的 Keras,只不過因為其簡單性,源碼要好讀得多。

項目鏈接:https://github.com/taki0112/Tensorflow-Cookbook

在這個項目中,作者重點突出這是一份易於使用的 TensorFlow 代碼集,它包括常見的正則化、卷積運算和架構模塊等代碼。實際上,在我們搭建自己的模型或系統時,複製並粘貼這些代碼就行了。它們以規範的形式定義不同的功能模塊,因此只要修改少量參數與代碼,它們就能完美地融入到我們項目中。

目前該項目包含一般深度學習架構所需要的代碼,例如初始化和正則化、各種卷積運算、基本網路架構與模塊、損失函數和其它數據預處理過程。此外,作者還特別增加了對 GAN 的支持,這主要體現在損失函數上,其中生成器損失和判別器損失可以使用推土機距離、最小二乘距離和 KL 散度等。

使用方法

使用方法其實有兩種,首先我們可以複製粘貼代碼,這樣對於模塊的定製化非常有利。其次我們可以直接像使用 API 那樣調用操作與模塊,這種方法會使模型顯得非常簡潔,而且導入的源碼也通俗易懂。首先對於第二種直接導入的方法,我們可以從 ops.py 和 utils.py 文件分別導入模型運算部分與圖像預處理過程。

  • from ops import *
  • from utils import *

from ops import conv
x = conv(x, channels=64, kernel=3, stride=2, pad=1, pad_type="reflect", use_bias=True, sn=True, scope="conv")

而對於第一種複製粘貼,我們可能會根據實際修改一些參數與結構,但這要比從頭寫簡單多了。如下所示,對於一般的神經網路,它會採用如下結構模板:

def network(x, is_training=True, reuse=False, scope="network"):
with tf.variable_scope(scope, reuse=reuse):
x = conv(...)
...return logit

其實深度神經網路就像一塊塊積木,我們按照上面的模板把 ops.py 中不同的模塊堆疊起來,最終就能得到完整的前向傳播過程。

代碼集目錄

項目頁面:https://www.notion.so/Simple-Tensorflow-Cookbook-6f4563d0cd7343cb9d1e60cd1698b54d

目前整個項目包含 20 種代碼塊,它們可用於快速搭建深度學習模型:

請快點粘貼複製,這是一份好用的TensorFlow代碼集

打開今日頭條,查看更多圖片

請快點粘貼複製,這是一份好用的TensorFlow代碼集

代碼示例

如下主要介紹幾段代碼示例,包括最常見的卷積操作和殘差模塊等。每一項代碼示例都能採用 API 式的調用或複製粘貼,所以它們不只能快速使用,學習各種操作的實現方法也是很好的資源。

卷積

卷積的原理相信大家都很熟悉,那就直接看調用代碼吧:

x = conv(x, channels=64, kernel=3, stride=2, pad=1, pad_type="reflect", use_bias=True, sn=True, scope="conv")

請快點粘貼複製,這是一份好用的TensorFlow代碼集

如下所示為實現以上 API 的代碼,相比於直接使用 padding="SAME",了解如何手給圖像 padding 零也是很好的。此外,這一段代碼嵌入了譜歸一化(spectral_normalization/sn),甚至我們可以截取這一小部分嵌入到自己的代碼中。

# padding="SAME" ======> pad = ceil[ (kernel - stride) / 2 ]
def conv(x, channels, kernel=4, stride=2, pad=0, pad_type="zero", use_bias=True, sn=False, scope="conv_0"):
with tf.variable_scope(scope):
if pad > 0:
h = x.get_shape().as_list()[1]
if h % stride == 0:
pad = pad * 2
else:
pad = max(kernel - (h % stride), 0)
pad_top = pad // 2
pad_bottom = pad - pad_top
pad_left = pad // 2
pad_right = pad - pad_left
if pad_type == "zero":
x = tf.pad(x, [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]])
if pad_type == "reflect":
x = tf.pad(x, [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]], mode="REFLECT")
if sn:
w = tf.get_variable("kernel", shape=[kernel, kernel, x.get_shape()[-1], channels], initializer=weight_init,
regularizer=weight_regularizer)
x = tf.nn.conv2d(input=x, filter=spectral_norm(w),
strides=[1, stride, stride, 1], padding="VALID")
if use_bias:
bias = tf.get_variable("bias", [channels], initializer=tf.constant_initializer(0.0))
x = tf.nn.bias_add(x, bias)
else:
x = tf.layers.conv2d(inputs=x, filters=channels,
kernel_size=kernel, kernel_initializer=weight_init,
kernel_regularizer=weight_regularizer,
strides=stride, use_bias=use_bias)
return x

部分卷積(Partial Convolution)

部分卷積是英偉達為圖像修復引入的卷積運算,它使模型能夠修復任意非中心、不規則的區域。在論文 Image Inpainting for Irregular Holes Using Partial Convolutions 中,實現部分卷積是非常關鍵的,如下展示了簡單的調用過程:

x = partial_conv(x, channels=64, kernel=3, stride=2, use_bias=True, padding="SAME", sn=True, scope="partial_conv")

請快點粘貼複製,這是一份好用的TensorFlow代碼集

請快點粘貼複製,這是一份好用的TensorFlow代碼集

讀者可根據以下定義 PConv 的代碼了解具體實現信息:

def partial_conv(x, channels, kernel=3, stride=2, use_bias=True, padding="SAME", sn=False, scope="conv_0"):
with tf.variable_scope(scope):
if padding.lower() == "SAME".lower():
with tf.variable_scope("mask"):
_, h, w, _ = x.get_shape().as_list()
slide_window = kernel * kernel
mask = tf.ones(shape=[1, h, w, 1])
update_mask = tf.layers.conv2d(mask, filters=1,
kernel_size=kernel, kernel_initializer=tf.constant_initializer(1.0),
strides=stride, padding=padding, use_bias=False, trainable=False)
mask_ratio = slide_window / (update_mask + 1e-8)
update_mask = tf.clip_by_value(update_mask, 0.0, 1.0)
mask_ratio = mask_ratio * update_mask
with tf.variable_scope("x"):
if sn:
w = tf.get_variable("kernel", shape=[kernel, kernel, x.get_shape()[-1], channels],
initializer=weight_init, regularizer=weight_regularizer)
x = tf.nn.conv2d(input=x, filter=spectral_norm(w), strides=[1, stride, stride, 1], padding=padding)
else:
x = tf.layers.conv2d(x, filters=channels,
kernel_size=kernel, kernel_initializer=weight_init,
kernel_regularizer=weight_regularizer,
strides=stride, padding=padding, use_bias=False)
x = x * mask_ratio
if use_bias:
bias = tf.get_variable("bias", [channels], initializer=tf.constant_initializer(0.0))
x = tf.nn.bias_add(x, bias)
x = x * update_mask
else:
if sn:
w = tf.get_variable("kernel", shape=[kernel, kernel, x.get_shape()[-1], channels],
initializer=weight_init, regularizer=weight_regularizer)
x = tf.nn.conv2d(input=x, filter=spectral_norm(w), strides=[1, stride, stride, 1], padding=padding)
if use_bias:
bias = tf.get_variable("bias", [channels], initializer=tf.constant_initializer(0.0))
x = tf.nn.bias_add(x, bias)
else:
x = tf.layers.conv2d(x, filters=channels,
kernel_size=kernel, kernel_initializer=weight_init,
kernel_regularizer=weight_regularizer,
strides=stride, padding=padding, use_bias=use_bias)
return x

殘差模塊

ResNet 最大的特點即解決了反向傳播過程中的梯度消失問題,因此它可以訓練非常深的網路而不用像 GoogLeNet 那樣在中間添加分類網路以提供額外的梯度。而 ResNet 是由殘差模塊堆疊起來的,一般根據需要可以定義幾種不同的殘差模塊:

x = resblock(x, channels=64, is_training=is_training, use_bias=True, sn=True, scope="residual_block")
x = resblock_down(x, channels=64, is_training=is_training, use_bias=True, sn=True, scope="residual_block_down")
x = resblock_up(x, channels=64, is_training=is_training, use_bias=True, sn=True, scope="residual_block_up")

如上展示了三種殘差模塊,其中 down 表示降採樣,輸入特徵圖的長寬都會減半;而 up 表示升採樣,輸入特徵圖的長寬都會加倍。在每一個殘差模塊上,殘差連接會將該模塊的輸入與輸出直接相加。因此在反向傳播中,根據殘差連接傳遞的梯度就可以不經過殘差模塊內部的多個卷積層,因而能為前一層保留足夠的梯度信息。

請快點粘貼複製,這是一份好用的TensorFlow代碼集

如下簡單定義了一般的 resblock 和採用升採樣的 resblock_up,因為它們調用的 conv()、deconv() 和 batch_norm() 等函數都是前面定義的不同計算模塊,因此整體上代碼看起來非常簡潔。

def resblock(x_init, channels, use_bias=True, is_training=True, sn=False, scope="resblock"):
with tf.variable_scope(scope):
with tf.variable_scope("res1"):
x = conv(x_init, channels, kernel=3, stride=1, pad=1, use_bias=use_bias, sn=sn)
x = batch_norm(x, is_training)
x = relu(x)
with tf.variable_scope("res2"):
x = conv(x, channels, kernel=3, stride=1, pad=1, use_bias=use_bias, sn=sn)
x = batch_norm(x, is_training)
return x + x_init
def resblock_up(x_init, channels, use_bias=True, is_training=True, sn=False, scope="resblock_up"):
with tf.variable_scope(scope):
with tf.variable_scope("res1"):
x = deconv(x_init, channels, kernel=3, stride=2, use_bias=use_bias, sn=sn)
x = batch_norm(x, is_training)
x = relu(x)
with tf.variable_scope("res2") :
x = deconv(x, channels, kernel=3, stride=1, use_bias=use_bias, sn=sn)
x = batch_norm(x, is_training)
with tf.variable_scope("skip") :
x_init = deconv(x_init, channels, kernel=3, stride=2, use_bias=use_bias, sn=sn)

這裡只展示了三種功能塊的代碼實現,可能我們會感覺該項目類似於一個迷你的 Keras。但因為這個項目實現的操作都比較簡單常見,因此源碼讀起來會比 Keras 之類的大型庫簡單地多,這對於嵌入使用還是學習都更有優勢。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 機器之心 的精彩文章:

結合人工智慧的高性能醫學:現狀、挑戰與未來
AI貓窩:一位工程師鏟屎官給流浪貓主子們的賀年禮

TAG:機器之心 |