Python深度學習基于Tensorflow(12)實戰生成式模型

文章目錄

        • Deep Dream
        • 風格遷移
        • 參考資料

Deep Dream

DeepDream 是一項將神經網絡學習模式予以可視化展現的實驗。與孩子們觀察云朵并嘗試解釋隨機形狀相類似,DeepDream 會過度解釋并增強其在圖像中看到的圖案。

DeepDream為了說明CNN學習到的各特征的意義,將采用放大處理的方式。具體來說就是使用梯度上升的方法可視化網絡每一層的特征,即用一張噪聲圖像輸入網絡,反向更新的時候不更新網絡權重,而是更新初始圖像的像素值,以這種“訓練圖像”的方式可視化網絡。DeepDream正是以此為基礎。

DeepDream如何放大圖像特征?這里我們先看一個簡單實例。比如:有一個網絡學習了分類貓和狗的任務,給這個網絡一張云的圖像,這朵云可能比較像狗,那么機器提取的特征可能也會像狗。假設對應一個特征最后輸入概率為[0.6, 0.4], 0.6表示為狗的概率, 0.4表示為貓的概率,那么采用L2范數可以很好達到放大特征的效果。對于這樣一個特征,L2 =〖x1〗2+〖x2〗2,若x1越大,x2越小,則L2越大,所以只需要最大化L2就能保證當x1>x2的時候,迭代的輪數越多x1越大,x2越小,所以圖像就會越來越像狗。每次迭代相當于計算L2范數,然后用梯度上升的方法調整圖像。優化的就不再是優化權重參數,而是特征值或像素點,因此,構建損失函數時,不使用通常的交叉熵,而是最大化特征值的L2范數。使圖片經過網絡之后提取的特征更像網絡隱含的特征。

使用基本圖像,它輸入到預訓練的CNN。 然后,正向傳播到特定層。為了更好理解該層學到了什么,我們需要最大化通過該層激活值。以該層輸出為梯度,然后在輸入圖像上完成漸變上升,以最大化該層的激活值。不過,光這樣做并不能產生好的圖像。為了提高訓練質量,需要使用一些技術使得到的圖像更好。可以進行高斯模糊以使圖像更平滑,使用多尺度(又稱為八度)的圖片進行計算。先連續縮小輸入圖像,然后,再逐步放大,并將結果合并為一個圖像輸出。

首先使用預訓練模型 InceptionV3 對圖像特征進行提取,其中 mixed 表示的是 InceptionV3 中的 mixed 層的特征值;

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as pltlayer_coeff = {"mixed4": 1.0,"mixed5": 1.5,"mixed6": 2.0,"mixed7": 2.5,
}model = tf.keras.applications.inception_v3.InceptionV3(weights="imagenet", include_top=False)
outputs_dict = dict([(layer.name, layer.output) for layer in [model.get_layer(name) for name in layer_coeff.keys()]])
feature_extractor = tf.keras.Model(inputs=model.inputs, outputs=outputs_dict)

計算損失:

def compute_loss(input_image):features = feature_extractor(input_image)loss_list = []for name in features.keys():coeff = layer_settings[name]activation = features[name]# 通過僅在損失中包含非邊界像素來避免邊界偽影scaling = tf.reduce_prod(tf.cast(tf.shape(activation), "float32"))loss_list.append(coeff * tf.reduce_sum(tf.square(activation[:, 2:-2, 2:-2, :])) / scaling)return tf.reduce_sum(loss_list)

定義訓練函數:

@tf.function
def train_step(img, learning_rate=1e-1):with tf.GradientTape() as tape:tape.watch(img)loss = compute_loss(img)grads = tape.gradient(loss, img)grads /= tf.math.reduce_std(grads)img += learning_rate * gradsimg = tf.clip_by_value(img, -1, 1)return loss, imgdef train_loop(img, iterations, learning_rate=1e-1, max_loss=None):for i in range(iterations):loss, img = gradient_ascent_step(img, learning_rate)if max_loss is not None and loss > max_loss:breakreturn img

定義超參數:

# 縮放次數 多尺度次數 也即八度 每一次縮放 octave_scale
num_octave = 1
# 縮放倍數
octave_scale = 1.4
# train_loop 訓練迭代次數
iterations = 80
# 最大損失
max_loss = 15
# 學習率
learning_rate = 1e-2

如下便是多尺度縮放的訓練過程:

![[Pasted image 20240520015509.png]]

定義數據:

img = preprocess_image('./dog.jpg')
plt.imshow(deprocess(img[0]))

![[Pasted image 20240520015056.png]]

開始訓練:

original_img = preprocess_image('./dog.jpg')
original_shape = original_img.shape[1:3]successive_shapes = [original_shape]
for i in range(1, num_octave):shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape])successive_shapes.append(shape)
successive_shapes = successive_shapes[::-1]shrunk_original_img = tf.image.resize(original_img, successive_shapes[0])img = tf.identity(original_img)  # Make a copy
for i, shape in enumerate(successive_shapes):print("Processing octave %d with shape %s" % (i, shape))img = tf.image.resize(img, shape)img = train_loop(img, iterations=iterations, learning_rate=learning_rate, max_loss=max_loss)upscaled_shrunk_original_img = tf.image.resize(shrunk_original_img, shape)same_size_original = tf.image.resize(original_img, shape)lost_detail = same_size_original - upscaled_shrunk_original_imgimg += lost_detailshrunk_original_img = tf.image.resize(original_img, shape)tf.keras.preprocessing.image.save_img('./dream-' + "dog.jpg", deprocess(img[0]))

![[Pasted image 20240520014920.png]]

總的來說,Deep Dream 相當于訓練可視化,其不對參數進行梯度更新,而是對圖像進行梯度更新,通過梯度上升讓圖像能夠最大程度的激活目標層的輸出結果;其模型實際意義不強,有稍微的模型解釋性;

風格遷移

風格遷移的本質和 Deep Dream 是一樣的,其主要還是因為風格轉換涉及到的樣本數量太少,基本就是兩張圖片之間進行轉化,因此對參數進行梯度更新是不現實的,我們只能利用預訓練模型,提取圖片特征然后定義特征之間的損失進而進行操作;實現風格遷移的核心思想就是定義損失函數。

風格遷移的損失函數由內容損失和風格損失組成,這里用 O i m a g e O_{image} Oimage? 表示原圖, R i m a g e R_{image} Rimage? 表示風格圖, G i m a g e G_{image} Gimage? 表示生成圖,那么損失如下: L = d i s t a n c e ( s t y l e ( R i m a g e ) ? s t y l e ( G i m a g e ) ) + d i s t a n c e ( c o n t e n t ( O i m a g e ) ? c o n t e n t ( G i m a g e ) ) \mathcal{L} = distance(style(R_{image}) - style(G_{image})) + distance(content(O_{image}) - content(G_{image})) L=distance(style(Rimage?)?style(Gimage?))+distance(content(Oimage?)?content(Gimage?))
卷積神經網絡不同層學到的圖像特征是不一樣的,靠近輸入端的卷積層學到的是圖像比較具體,局部的特征,如位置,形狀,顏色,紋理等。靠近輸出端的卷積層學到的是圖像更全面,更抽象的特征,但會丟失圖像的一些詳細信息;

風格損失

風格損失是利用 Gram矩陣 來計算的,Gram矩陣 將圖像的通道作為一個維度,將圖像的寬和高合并作為一個維度,得到 X X X 的尺寸為 [ c h a n n e l , w ? h ] [channel, w*h] [channel,w?h],然后計算 X ? X T X \cdot X^T X?XT ,用該值來衡量風格;

@tf.function
def gram_matrix(image):image = tf.transpose(image, (2, 0, 1))image = tf.reshape(image, [tf.shape(image)[0], -1])gram = tf.matmul(image, image, transpose_b=True)return gram@tf.function
def compute_style_loss(r_image, g_image):r_w, r_h, r_c = tf.shape(r_image)g_w, g_h, g_c = tf.shape(g_image)r_gram = gram_matrix(r_image)g_gram = gram_matrix(g_image)style_loss = tf.reduce_sum(tf.square(r_gram - g_gram))/  (4 * (r_c * g_c) * (r_w * r_h * g_w * g_h))

內容損失

內容損失很簡單,也就是生成圖像和原來圖像之間的區別;

@tf.function
def compute_content_loss(o_image, g_image):return tf.reduce_sum(tf.square(o_image - g_image))

這里不需要放縮是因為沒有像風格損失一樣經歷過 Gram矩陣 計算,這就導致原本的內容并沒有經過擴大,不過后面同樣會給內容損失和風格損失分配權重;

總損失

總損失讓生成的圖像具有連續性,不要這里一塊那里一塊;

def compute_variation_loss(x):a = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, 1:, :tf.shape(x)[2]-1, :])b = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, :tf.shape(x)[1]-1, 1:, :])return tf.reduce_sum(tf.pow(a+b, 1.25))

這里還是以上面的小狗圖片作為原圖片,風格圖片采取梵高的星空圖片;

![[Pasted image 20240520202401.png]]

首先導入預訓練模型 VGG19,以及圖像處理函數 preprocess_image deprocess_image

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as pltdef preprocess_image(image_path):img = tf.keras.preprocessing.image.load_img(image_path, target_size=(400, 600))img = tf.keras.preprocessing.image.img_to_array(img)img = np.expand_dims(img, axis=0)img = tf.keras.applications.vgg19.preprocess_input(img)return tf.convert_to_tensor(img)def deprocess_image(x):x = x.reshape((400, 600, 3))x[:, :, 0] += 103.939x[:, :, 1] += 116.779x[:, :, 2] += 123.68x = x[:, :, ::-1]x = np.clip(x, 0, 255).astype("uint8")return x# 用于風格損失的網絡層列表
style_layer_names = ["block1_conv1","block2_conv1","block3_conv1","block4_conv1","block5_conv1",
]
# 用于內容損失的網絡層
content_layer_names = ["block5_conv2",
]model = tf.keras.applications.vgg19.VGG19(weights="imagenet", include_top=False)
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers if layer.name in style_layer_names + content_layer_names])
feature_extractor = tf.keras.Model(inputs=model.inputs, outputs=outputs_dict)

定義三個損失:compute_style_loss compute_content_loss compute_variation_loss

def gram_matrix(image):image = tf.transpose(image, (2, 0, 1))image = tf.reshape(image, [tf.shape(image)[0], -1])gram = tf.matmul(image, image, transpose_b=True)return gramdef compute_style_loss(r_image, g_image):r_w, r_h, r_c = tf.cast(tf.shape(r_image)[0], tf.float32), tf.cast(tf.shape(r_image)[1], tf.float32), tf.cast(tf.shape(r_image)[2], tf.float32)g_w, g_h, g_c = tf.cast(tf.shape(g_image)[0], tf.float32), tf.cast(tf.shape(g_image)[1], tf.float32), tf.cast(tf.shape(g_image)[2], tf.float32)r_gram = gram_matrix(r_image)g_gram = gram_matrix(g_image)style_loss = tf.reduce_sum(tf.square(r_gram - g_gram))/  (4 * (r_c * g_c) * (r_w * r_h * g_w * g_h))return style_lossdef compute_content_loss(o_image, g_image):return tf.reduce_sum(tf.square(o_image - g_image))def compute_variation_loss(x):a = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, 1:, :tf.shape(x)[2]-1, :])b = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, :tf.shape(x)[1]-1, 1:, :])return tf.reduce_sum(tf.pow(a+b, 1.25))

定義損失比例以及總損失計算函數 compute_loss

total_weight = 1e-6
style_weight = 1e-6
content_weight = 2.5e-8def compute_loss(o_image, r_image, g_image):X = tf.concat([o_image, r_image, g_image], axis=0)features = feature_extractor(X)loss_list = []for content_layer_name in content_layer_names:temp = features[content_layer_name]o_image_ = temp[0,:,:,:]g_image_ = temp[2,:,:,:]loss = compute_content_loss(o_image_, g_image_)loss_list.append(loss*content_weight/len(content_layer_names))for style_layer_name in style_layer_names:temp = features[style_layer_name]r_image_ = temp[1,:,:,:]g_image_ = temp[2,:,:,:]loss = compute_style_loss(r_image_, g_image_)loss_list.append(loss*style_weight/len(style_layer_names))loss = compute_variation_loss(g_image)loss_list.append(loss*total_weight)return tf.reduce_sum(loss_list)

定義優化器,圖片以及開始訓練:

o_image = preprocess_image('./dog.jpg')
r_image = preprocess_image('./start-night.png')
g_image = tf.Variable(o_image)optimizer = tf.keras.optimizers.Adam(learning_rate=1)def train_step():with tf.GradientTape() as tape:loss = compute_loss(o_image, r_image, g_image)grads = tape.gradient(loss, g_image)optimizer.apply_gradients([(grads, g_image)])return lossfor epoch in range(100):plt.imshow(deprocess_image(g_image.numpy()))plt.axis('off')plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))plt.show()tf.print(train_step())

最后將 生成的圖片 轉化為 GIF

import imageio
from PIL import Image
import os
import numpy as np# 這里與 for epoch in range(100): 中的圖片名稱對應 image_at_epoch_{:04d}.png
converted_images = [np.array(Image.open(item)) for item in [file for file in os.listdir('./') if file.startswith('image')]]
imageio.mimsave("animation.gif", converted_images, fps=15)

得到如下結果:
![[animation 2.gif]]

參考資料

DeepDream | TensorFlow Core (google.cn)

【數學-20】格拉姆矩陣(Gram matrix)詳細解讀 - 知乎 (zhihu.com)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14046.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14046.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14046.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

「51媒體」線下活動媒體同步直播,云分發,分流直播

傳媒如春雨,潤物細無聲,大家好,我是51媒體網胡老師。 線下活動除了邀請嘉賓,邀請媒體,邀請行業大咖KOL,來為活動站臺,背書外,我們也可以將線下的活動同步在線上進行直播&#xff0c…

Codeforces Round 821 (Div. 2) C. Parity Shuffle Sorting (構造之全變成一樣的)

給你一個數組 a a a &#xff0c;其中有 n n n 個非負整數。你可以對它進行以下操作。 選擇兩個索引 l l l 和 r r r ( 1 ≤ l < r ≤ n ) ( 1≤l<r≤n ) (1≤l<r≤n)。 如果 a l a r a_la_r al?ar? 是奇數&#xff0c;則進行 a r : a l a_r:a_l ar?:al?…

react【框架原理詳解】JSX 的本質、SyntheticEvent 合成事件機制、組件渲染過程、組件更新過程

JSX 的本質 JSX 代碼本身并不是 HTML&#xff0c;也不是 Javascript&#xff0c;在渲染頁面前&#xff0c;需先通過解析工具&#xff08;如babel&#xff09;解析之后才能在瀏覽器中運行。 babel官網可查看 JSX 解析后的效果 更早之前&#xff0c;Babel 會把 JSX 轉譯成一個 R…

AI大模型探索之路-實戰篇4:DB-GPT數據應用開發框架調研實踐

目錄 前言一、DB-GPT總體概述二、DB-GPT關鍵特性1、私域問答&數據處理&RAG2、多數據源&GBI3、多模型管理4、自動化微調5、Data-Driven Multi-Agents&Plugins6、隱私安全 三、服務器資源準備1、創建實例2、打開jupyterLab 四、DB-GPT啟動1、激活 conda 環境2、切…

區塊鏈fisco聯盟鏈搭建(二)搭建多群組聯盟鏈

本文章只講搭建的命令方法 以單機、四機構、三群組、八節點的星形組網拓撲為例 第一步創建并進入工作目錄&#xff08;繼續以fisco為例&#xff09; mkdir /fisco cd /fisco 獲取搭鏈腳本上一篇文章區塊鏈fisco聯盟鏈搭建 (一)搭建單群組四節點聯盟鏈中有 第二步生成多群組…

抖音小店沒有流量不出單?歸根到底,就是轉化率不行!

哈嘍~我是電商月月 新手做抖音小店&#xff0c;最憂愁的就是&#xff1a;店鋪不出單怎么辦&#xff1f; 商家通常會把沒有銷量的原因&#xff0c;都推向于“店鋪沒有流量” 但在抖音&#xff0c;這個日活量高達9億的平臺來說&#xff0c;任何商鋪最不缺的應該就是流量了 但…

61850的總體建模原則

IEC 61850標準是電力系統自動化領域的一個重要標準,它定義了數據的模型和設備描述,使得不同廠家的設備之間能夠實現互操作性。下面將圍繞“61850的總體建模原則”展開討論,主要包括物理設備建模基礎、邏輯設備組合規則、邏輯節點功能劃分、數據模型統一標準、配置文件規范描…

炒股前你要知道的股票知識

一、股票組成 A股股票組成板塊有:地區板塊、行業板塊、證監會板塊,概念板塊。 其中各個板塊還可以分為: A農、林、牧、漁業; B采礦業; C制造業; D電力、熱力、燃氣及水生產和供應業; E建筑業; F批發和零售業; G交通運輸、倉儲和郵政業; H住宿和餐飲業; I…

《Qt》使用Windeployqt發布程序

之前都是使用QTVS開發&#xff0c;這次直接使用QT開發&#xff0c;記錄一下程序發布過程&#xff0c;方便后期使用查閱。 添加環境變量 在path目錄下添加如下路徑&#xff1a; 之前使用QTVS2013&#xff0c;添加如下路徑 D:\App\Qt5.9.3\5.9.3\msvc2013_64\bin; D:\App\Qt…

dll文件是什么?電腦丟失某個dll文件有什么解決辦法

Dll文件是什么&#xff1f;這個文件在電腦中是什么樣的地位&#xff1f;如果電腦提示丟失了某個dll文件那么有什么辦的解決這個問題呢&#xff1f;如何將丟失的dll文件進行修復呢&#xff1f;今天這篇文章將按就來教大家幾種修復丟失dll文件問題的方法。 DLL 文件&#xff0c;全…

[Redis]基本全局命令

Redis存儲方式介紹 在 Redis 中數據是以鍵值對的凡事存儲的&#xff0c;鍵&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;是基本的數據存儲單元。以下是對 Redis 鍵值對的詳細講解&#xff1a; 鍵&#xff08;Key&#xff09;&#xff1a; 類型&#xff1a;…

JVM、JRE和JDK的區別

首先需要確定的是JDK里是包含JRE的&#xff0c;而JRE里又包含JVM&#xff0c;它們區別在于面向的服務對象不同所以進行了不同的包裝。 JVM&#xff1a;JVM是面向操作系統&#xff0c;.Class字節碼->機器碼以及程序運行的內存的管理。 JRE&#xff1a;JRE是面向于程序的&am…

全局配置路徑無法識別的解決——后端

在全局配置路徑reggie.path的時候&#xff0c;無法正常啟動SpringBoot項目 Value("${reggie.path}")private String basePath; 查看application.yml的配置情況: 發現path沒有起作用&#xff0c;推測是格式問題&#xff0c;冒號后面空格后即可

Web API——獲取DOM元素

目錄 1、根據選擇器來獲取DOM元素 2.、根據選擇器來獲取DOM元素偽數組 3、根據id獲取一個元素 4、通過標簽類型名獲取所有該標簽的元素 5、通過類名獲取元素 目標&#xff1a;能查找/獲取DOM對象 1、根據選擇器來獲取DOM元素 語法&#xff1a; document.querySelector(css選擇…

關于性能問題優化的小討論

大家好&#xff0c;我是阿趙。 ??最近很流行把之前制作在安卓或者iOS端的游戲轉成微信小程序上架&#xff0c;我所在的項目也有這樣的操作。微信小程序是用WebGL來運行的&#xff0c;實際上它的性能很差&#xff0c;只有不到app端的三分之一的性能可用&#xff0c;內存方面也…

LabVIEW機器視覺技術對工業制造有什么影響?

LabVIEW機器視覺技術對工業制造產生了深遠的影響&#xff0c;主要體現在以下幾個方面&#xff1a; 1. 提高生產效率 LabVIEW機器視覺技術可以自動檢測和分析生產線上的產品&#xff0c;提高檢測速度和精度。傳統的人工檢測方式往往效率低下且容易出錯&#xff0c;而機器視覺系…

java 數組的常見操作

在 Java 中&#xff0c;數組是一種特殊的對象&#xff0c;用于存儲相同類型的多個元素。以下是一些常見的數組操作&#xff1a; 聲明數組&#xff1a;使用以下語法聲明一個數組&#xff0c;其中 type 是數組元素的數據類型&#xff0c;name 是數組的名稱。 type[] name;例如&…

第19講:自定義類型:結構體

目錄 1.結構體類型的聲明1.1 結構體回顧1.1.1 結構的聲明 特殊的結構聲明1.3 結構的?引? 2. 結構體內存的對齊2.2 為什么存在內存對??2.3 修改默認對?數 3. 結構體傳參4. 結構體實現位段4.1 什么是位段4.2 位段的內存分配4.3 位段的跨平臺問題4.5 位段使?的注意事項 正文…

梳理 JavaScript 中空數組調用 every方法返回true 帶來驚訝的問題

前言 人生總是在意外之中. 情況大概是這樣的. 前兩天版本上線以后, 無意中發現了一個bug, 雖然不是很大, 為了不讓用戶使用時感覺到問題. 還是對著一個小小的bug進行了修復, 并重新在上線一次, 雖然問題不大, 但帶來的時間成本還是存在的. 以及上線后用戶體驗并不是很好. 問題…

JVM學習-垃圾收集器(二)

Serial回收器&#xff1a;串行回收 Serial收集器是最基本、歷史最悠久的收集器JDK1.3之前新生代唯一的選擇Hotpot中Client模式下的默認新生代垃圾收集器采用復制算法&#xff0c;串行回收“Stop-the-world”機制的方式執行內存回收除了年輕代之外&#xff0c;Serial收集器還提…