摘要:作者使用實(shí)現(xiàn)了,并將其開(kāi)源放在了上。在年的兩個(gè)問(wèn)題上分別取得了第一名和第二名。的獲取方式是第層,形狀為,的獲取方式是第層,形狀為。每個(gè)卷積核可以看做是圖形的一種特征抽取。相關(guān)性的描述使用余弦相似性,而余弦相似性又正比于兩種特征的點(diǎn)積。
Neural Style是一個(gè)非常有意思的深度學(xué)習(xí)應(yīng)用:輸入一張代表內(nèi)容的圖片和一張代表風(fēng)格的圖片,深度學(xué)習(xí)網(wǎng)絡(luò)會(huì)輸出一張融合了這個(gè)風(fēng)格和內(nèi)容的新作品。
TensorFlow是Google開(kāi)源的最流行的深度學(xué)習(xí)框架。作者anishathalye使用TensorFlow實(shí)現(xiàn)了Neural Style,并將其開(kāi)源放在了GitHub上。本文對(duì)他的代碼進(jìn)行深入剖析。代碼請(qǐng)點(diǎn)這里。
Pretrained VGG-19 ModelVGG在2014年的 ILSVRC localization and classification 兩個(gè)問(wèn)題上分別取得了第一名和第二名。VGG-19是其中的一個(gè)模型,官網(wǎng)上提供了預(yù)先訓(xùn)練好的系數(shù),經(jīng)常被業(yè)界用來(lái)做原始圖片的特征變換。
VGG-19是一個(gè)非常深的神經(jīng)網(wǎng)絡(luò),總共有19層,基本結(jié)構(gòu)如下:
前幾層為卷積和maxpool的交替,每個(gè)卷積包含多個(gè)卷積層,最后面再緊跟三個(gè)全連接層。具體而言,第一個(gè)卷積包含2個(gè)卷積層,第二個(gè)卷積包含2個(gè)卷積層,第三個(gè)卷積包含4個(gè)卷基層,第四個(gè)卷積包含4個(gè)卷積層,第五個(gè)卷積包含4個(gè)卷基層,所以一共有16個(gè)卷積層,加上3個(gè)全連接層,一共19層,因此稱(chēng)為VGG-19模型。VGG-19的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)如下表所示:
Neural Style只依賴(lài)于VGG-19的卷積層,需要使用神經(jīng)網(wǎng)絡(luò)層列舉如下:
VGG19_LAYERS = ( "conv1_1", "relu1_1", "conv1_2", "relu1_2", "pool1", "conv2_1", "relu2_1", "conv2_2", "relu2_2", "pool2", "conv3_1", "relu3_1", "conv3_2", "relu3_2", "conv3_3", "relu3_3", "conv3_4", "relu3_4", "pool3", "conv4_1", "relu4_1", "conv4_2", "relu4_2", "conv4_3", "relu4_3", "conv4_4", "relu4_4", "pool4", "conv5_1", "relu5_1", "conv5_2", "relu5_2", "conv5_3", "relu5_3", "conv5_4", "relu5_4" )
我們可以從MatCovNet下載頁(yè)獲取VGG-19模型預(yù)先訓(xùn)練好的模型系數(shù)文件。該文件為Matlab格式,我們可以使用Python的scipy.io進(jìn)行數(shù)據(jù)讀取。
該數(shù)據(jù)包含很多信息,我們需要的信息是每層神經(jīng)網(wǎng)絡(luò)的kernels和bias。kernels的獲取方式是data["layers"][0][第i層][0][0][0][0][0],形狀為[width, height, in_channels, out_channels],bias的獲取方式是data["layers"][0][第i層][0][0][0][0][0],形狀為[1,out_channels]。對(duì)于VGG-19的卷積,全部采用了3X3的filters,所以width為3,height為3。注意,這里面的層數(shù)i,指的是最細(xì)粒度的層數(shù),包括conv、relu、pool、fc各種操作。因此,i=0為卷積核,i=1為relu,i=2為卷積核,i=3為relu,i=4為pool,i=5為卷積核,……,i=37為全連接層,以此類(lèi)推。VGG-19的pooling采用了長(zhǎng)寬為2X2的max-pooling,Neural Style將它替換為了average-pooling,因?yàn)樽髡甙l(fā)現(xiàn)這樣的效果會(huì)稍微好一些。
VGG-19需要對(duì)輸入圖片進(jìn)行一步預(yù)處理,把每個(gè)像素點(diǎn)的取值減去訓(xùn)練集算出來(lái)的RGB均值。VGG-19的RGB均值可以通過(guò)np.mean(data["normalization"][0][0][0], axis=(0, 1)獲得,其取值為[ 123.68 116.779 103.939]。
綜上所述,我們可以使用下面的代碼vgg.py讀取VGG-19神經(jīng)網(wǎng)絡(luò),用于構(gòu)造Neural Style模型。
import tensorflow as tf import numpy as np import scipy.io def load_net(data_path): data = scipy.io.loadmat(data_path) mean = data["normalization"][0][0][0] mean_pixel = np.mean(mean, axis=(0,1)) weights = data["layers"][0] return weights, mean_pixel def net_preloaded(weights, input_image, pooling): net = {} current = input_image for i, name in enumerate(VGG19_LAYERS): kind = name[:4] if kind == "conv": kernels, bias = weights[i][0][0][0][0] # matconvnet: weights are [width, height, in_channels, out_channels] # tensorflow: weights are [height, width, in_channels, out_channels] kernels = np.transpose(kernels, (1, 0, 2, 3)) bias = bias.reshape(-1) current = _conv_layer(current, kernels, bias) elif kind == "relu": current = tf.nn.relu(current) elif kind == "pool": current = _pool_layer(current, pooling) net[name] = current return net def _conv_layer(input, weights, bias): conv = tf.nn.conv2d(input, tf.constant(weights), strides=(1, 1, 1, 1), padding="SAME") return tf.nn.bias_add(conv, bias) def _pool_layer(input, pooling): if pooling == "avg": return tf.nn.avg_pool(input, ksize=(1, 2, 2, 1), strides=(1, 2, 2, 1), padding="SAME") else: return tf.nn.max_pool(input, ksize=(1, 2, 2, 1), strides=(1, 2, 2, 1), padding="SAME") def preprocess(image, mean_pixel): return image - mean_pixel def unprocess(image, mean_pixel): return image + mean_pixelNeural Style
Neural Style的核心思想如下圖所示:
Part 1: Content Reconstruction基本思路如下:將content圖片p和一張隨機(jī)生成的圖片x,都經(jīng)過(guò)VGG-19的卷積網(wǎng)絡(luò)進(jìn)行特征變換,獲取某些層級(jí)輸出的特征變換結(jié)果,要求二者的差異最小。二者在l層的損失函數(shù)定義如下:
其中F_{ij}^l為隨機(jī)圖片的第i個(gè)卷積核filter在位置j的取值,P_{ij}^l為content圖片的第i個(gè)卷積核filter在位置j的取值。
計(jì)算content圖片的feature map邏輯實(shí)現(xiàn)如下:
# 參數(shù)說(shuō)明 # network為VGG-19文件的路徑 # content為內(nèi)容圖片轉(zhuǎn)化得到的數(shù)組 # pooling為池化方式 CONTENT_LAYERS = ("relu4_2", "relu5_2") # paper原文只使用了relu4_2 content_features = {} shape = (1,) + content.shape # input shape: [batch, height, width, channels], only one image, so batch=1. # 獲取VGG-19的訓(xùn)練系數(shù),和RGB均值 vgg_weights, vgg_mean_pixel = vgg.load_net(network) # 計(jì)算Content圖片的feature map g = tf.Graph() with g.as_default(), g.device("/cpu:0"), tf.Session() as sess: # 構(gòu)造Computation Graph,feed為image,輸出的net包含了VGG-19每個(gè)層級(jí)的輸出結(jié)果 image = tf.placeholder("float", shape=shape) net = vgg.net_preloaded(vgg_weights, image, pooling) # 將content進(jìn)行預(yù)處理 content_pre = np.array([vgg.preprocess(content, vgg_mean_pixel)]) # 將預(yù)處理后的content_pre feed給Computation Graph,得到計(jì)算結(jié)果 for layer in CONTENT_LAYERS: content_features[layer] = net[layer].eval(feed_dict={image: content_pre})
計(jì)算隨機(jī)圖片的feature map,并計(jì)算content loss的邏輯實(shí)現(xiàn)如下:
# 參數(shù)說(shuō)明 # image為隨機(jī)生成的圖片 # pooling為池化方式 # content_weight_blend為兩個(gè)content重構(gòu)層的占比,默認(rèn)為1,只使用更精細(xì)的重構(gòu)層relu4_2;更抽象的重構(gòu)層relu5_2占比為1-content_weight_blend. # content_weight為內(nèi)容損失的系數(shù) with tf.Graph().as_default(): net = vgg.net_preloaded(vgg_weights, image, pooling) content_layers_weights = {} content_layers_weights["relu4_2"] = content_weight_blend content_layers_weights["relu5_2"] = 1.0 - content_weight_blend content_loss = 0 content_losses = [] for content_layer in CONTENT_LAYERS: content_losses.append(content_layers_weights[content_layer] * content_weight * (2 * tf.nn.l2_loss(net[content_layer] - content_features[content_layer]) / content_features[content_layer].size)) content_loss += reduce(tf.add, content_losses)Part 2: Style Reconstruction
從數(shù)學(xué)上定義什么是風(fēng)格,是Neural Style比較有意思的地方。每個(gè)卷積核filter可以看做是圖形的一種特征抽取。風(fēng)格在這篇paper中被簡(jiǎn)化為任意兩種特征的相關(guān)性。相關(guān)性的描述使用余弦相似性,而余弦相似性又正比于兩種特征的點(diǎn)積。于是風(fēng)格的數(shù)學(xué)定義被表示為神經(jīng)網(wǎng)絡(luò)層里filter i和filter j的點(diǎn)積,用G_{ij}^l表示。
與Content Reconstruction中的損失定義相似,我們把style圖片和隨機(jī)生成的噪點(diǎn)圖片經(jīng)過(guò)相同的VGG-19卷積網(wǎng)絡(luò)進(jìn)行特征變換,選出指定層級(jí)的filters。對(duì)每個(gè)層級(jí),計(jì)算兩張圖片特征變換后$G_{ij}^l$的差異。
各個(gè)層級(jí)的加權(quán)和就是最后的style loss:
計(jì)算style圖片的feature map邏輯實(shí)現(xiàn)如下:
# 參數(shù)說(shuō)明 # styles為風(fēng)格圖片集,可以為多張圖片 # style_blend_weights為風(fēng)格圖片集之間的權(quán)重 # style_layers_weights為不同神經(jīng)網(wǎng)絡(luò)層的權(quán)重 STYLE_LAYERS = ("relu1_1", "relu2_1", "relu3_1", "relu4_1", "relu5_1") style_shapes = [(1,) + style.shape for style in styles] style_features = [{} for _ in styles] # 計(jì)算style圖片的feature map for i in range(len(styles)): g = tf.Graph() with g.as_default(), g.device("/cpu:0"), tf.Session() as sess: image = tf.placeholder("float", shape=style_shapes[i]) net = vgg.net_preloaded(vgg_weights, image, pooling) style_pre = np.array([vgg.preprocess(styles[i], vgg_mean_pixel)]) for layer in STYLE_LAYERS: features = net[layer].eval(feed_dict={image: style_pre}) features = np.reshape(features, (-1, features.shape[3])) # features.shape[3] is the number of filters gram = np.matmul(features.T, features) / features.size style_features[i][layer] = gram
計(jì)算隨機(jī)圖片的feature map,并計(jì)算style loss的邏輯實(shí)現(xiàn)如下:
# style loss style_loss = 0 for i in range(len(styles)): style_losses = [] for style_layer in STYLE_LAYERS: layer = net[style_layer] _, height, width, number = map(lambda i: i.value, layer.get_shape()) size = height * width * number feats = tf.reshape(layer, (-1, number)) gram = tf.matmul(tf.transpose(feats), feats) / size style_gram = style_features[i][style_layer] style_losses.append(style_layers_weights[style_layer] * 2 * tf.nn.l2_loss(gram - style_gram) / style_gram.size) style_loss += style_weight * style_blend_weights[i] * reduce(tf.add, style_losses) # tv_loss # 注:The total variation (TV) loss encourages spatial smoothness in the generated image. It was not used by Gatys et al in their CVPR paper but it can sometimes improve the results; for more details and explanation see Mahendran and Vedaldi "Understanding Deep Image Representations by Inverting Them" CVPR 2015. tv_loss = ... loss = content_loss + style_loss + tv_loss train_step = tf.train.AdamOptimizer(learning_rate, beta1, beta2, epsilon).minimize(loss)
將上述代碼有序組合在一起后,可以得到Neural Style TensorFlow代碼的第二個(gè)關(guān)鍵文件stylize.py。
參考資料VGG-19主頁(yè)
MatConvNet
Neural Style Paper
TensorFlow Neural Style Github開(kāi)源項(xiàng)目
Neural Style中文解讀
VGG中文解讀
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/38631.html
摘要:作者微信號(hào)微信公眾號(hào)簡(jiǎn)書(shū)地址我把這篇文章分為四個(gè)部分機(jī)器學(xué)習(xí),,和數(shù)學(xué)。在這篇文章中,我把每個(gè)主題的教程數(shù)量都是控制在五到六個(gè),這些精選出來(lái)的教程都是非常重要的。每一個(gè)鏈接都會(huì)鏈接到別的鏈接,從而導(dǎo)致很多新的教程。 作者:chen_h微信號(hào) & QQ:862251340微信公眾號(hào):coderpai簡(jiǎn)書(shū)地址:http://www.jianshu.com/p/2be3... showIm...
摘要:在這堂課中,學(xué)生將可以學(xué)習(xí)到深度學(xué)習(xí)的基礎(chǔ),學(xué)會(huì)構(gòu)建神經(jīng)網(wǎng)絡(luò),包括和等。課程中也會(huì)有很多實(shí)操項(xiàng)目,幫助學(xué)生更好地應(yīng)用自己學(xué)到的深度學(xué)習(xí)技術(shù),解決真實(shí)世界問(wèn)題。 深度學(xué)習(xí)入門(mén)首推課程就是吳恩達(dá)的深度學(xué)習(xí)專(zhuān)項(xiàng)課程系列的 5 門(mén)課。該專(zhuān)項(xiàng)課程最大的特色就是內(nèi)容全面、通俗易懂并配備了豐富的實(shí)戰(zhàn)項(xiàng)目。今天,給大家推薦一份關(guān)于該專(zhuān)項(xiàng)課程的核心筆記!這份筆記只能用兩個(gè)字形容:全面! showImg(...
閱讀 834·2023-04-25 17:54
閱讀 3057·2021-11-18 10:02
閱讀 1200·2021-09-28 09:35
閱讀 724·2021-09-22 15:18
閱讀 2928·2021-09-03 10:49
閱讀 3131·2021-08-10 09:42
閱讀 2647·2019-08-29 16:24
閱讀 1315·2019-08-29 15:08