<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用Pytorch實現(xiàn)風格遷移(Neural-Transfer)

          共 9642字,需瀏覽 20分鐘

           ·

          2021-08-26 22:10


          # 前言

          本文主要向大家分享一個小編剛剛學習的神經(jīng)網(wǎng)絡應用的實例:風格遷移(Neural-Transfer)。這是一個由 Leon A. Gatys,Alexander S. Ecker和Matthias Bethge提出的算法。通過這個算法,我們可以用一種新的風格對指定圖片進行重構(gòu),更通俗一點即:風格圖片+內(nèi)容圖片=輸出圖片,即:

          1b8b55026a80114182cb346df880b34d.webp

          如上圖所示,神經(jīng)風格遷移可以將內(nèi)容圖像的內(nèi)容、風格圖像的風格混合在一起,使得輸出的圖片看起來像內(nèi)容圖像,但采用了風格圖像的風格。這就生成了一個帶有《星空》風格的風景圖,是不是很有趣呢?下面讓我們一起來看看它是怎么實現(xiàn)的吧!

          01

          基本原理

          原理很簡單:我們定義了兩個距離,一個為內(nèi)容D_c和一個用于樣式D_s。D_c 測量兩個圖像之間的內(nèi)容有多大不同,而?D_s衡量兩個圖像之間風格的差異。然后,我們生成第三張圖片,并利用D_c和D_s來優(yōu)化這張圖片,使其與內(nèi)容圖片的內(nèi)容差別和風格圖片的風格差別最小化。當然,我們的第一步是利用卷積神經(jīng)網(wǎng)絡提取出圖片的特征,如圖所示:

          fdbb81bc6e29eef018d7eb7a07e7b4f6.webp


          02

          讀入圖片

          現(xiàn)在我們導入content和style的原圖,我們需要使用PIL中的Image來讀取內(nèi)存中的圖片(PS:opencv也可以,但PIL的效果更好一點),再用torchvision中的transforms將讀入圖片轉(zhuǎn)化為tensor以便之后的操作。另外,我們還需要定義一個用于輸出圖像的函數(shù),以便輸出最終所得到的圖片,該函數(shù)會將tensor轉(zhuǎn)化為圖像。

          代碼如下:

          # load_img模塊import PIL.Image as Imageimport torchimport torchvision.transforms as transforms
          img_size = 512 if torch.cuda.is_available() else 128#根據(jù)設備選擇改變后項數(shù)大小def load_img(img_path):#圖像讀入 ? ?img = Image.open(img_path).convert('RGB')#將圖像讀入并轉(zhuǎn)換成RGB形式 ? ?img = img.resize(img_size, img_size)#調(diào)整讀入圖像像素大小 ? ?img = transforms.ToTensor()(img)#將圖像轉(zhuǎn)化為tensor ? ?img = img.unsqueeze(0)#在0維上增加一個維度 ? ?return img
          def show_img(img):#圖像輸出 ? ?img = img.squeeze(0)#將多余的0維通道刪去 ? ?img = transforms.ToPILImage()(img)#將tensor轉(zhuǎn)化為圖像 ? ?img.show()


          03

          損失函數(shù)

          下一步是定義我們的損失函數(shù),為了實現(xiàn)神經(jīng)風格遷移,我們需要定義一個關于生成圖像(Generated image)G的損失函數(shù),用于評價生成圖像的好壞。通過最小化損失函數(shù)的方式,來生成所要的圖像。損失函數(shù)需要分成兩部分,一個是內(nèi)容損失函數(shù),它是關于生成圖像G與內(nèi)容圖像C的函數(shù),用于衡量生成圖像與內(nèi)容圖像在內(nèi)容上有多相似;一個是風格損失函數(shù),關于生成圖像G與風格圖像S的函數(shù),用于衡量生成圖像與風格圖像在風格上的相似度。最后需要用兩個超參數(shù)α和β來確定兩個函數(shù)之間的權(quán)重,我們的總損失是它們兩個的加權(quán)和,即

          6e725c728f49b46590cca4b882d9efbb.webp

          那么我們的關鍵就在于先單獨求出內(nèi)容損失和風格損失,計算它們的損失也很簡單,首先我們看一下內(nèi)容損失,我們使用最簡單的均方誤差:

          38d13a6256b00ee6ff879099bf7893e9.webp

          上式是內(nèi)容損失函數(shù)的定義。其中l(wèi)代表第l層的特征表示,p是原始內(nèi)容圖片特征圖,x是生成圖片特征圖。公式的含義就是對于每一層,原始圖片的特征圖(feature map)和生成圖片的特征圖的一一對應做差值平方和。


          然后我們需要對損失梯度下降求導來優(yōu)化參數(shù)

          f6cdc0f113a33e0f92e17136b01c9d03.webp

          對于風格損失我們還需要引入Gram矩陣來幫助我們表示圖像的風格特征,我們讀入圖像卷積層的輸出形狀為C × H × W ,C是卷積核的通道數(shù),每個卷積核學習圖像不同特征,每個卷積核輸出H × W 代表這張圖像的一個feature map,讀入RGB圖像的三色通道相當于三個feature map,我們用Gram矩陣來計算feature map間的相似性,得到圖像的風格特征。

          關于Gram矩陣

          由于計算風格損失需要Gram矩陣,所以我們先來了解一下它吧。

          Gram矩陣的定義:

          74b6d00840c2676952e501e0d6e674bf.webp

          Gram矩陣計算公式:

          0c94fb619d981e95d6628b8cfb173fff.webp

          F表示生成圖像的feature map。上面式子的含義:第l層的Gram矩陣第i行,第j列的數(shù)值等于把生成圖像在第l層的第i個feature map與第j個feature map分別拉成一維后相乘求和,即Gram矩陣中的每個值都是每個通道 i 的feature map與每個通道 j 的feature map的內(nèi)積,由于內(nèi)積可以判斷兩個向量之間的夾角和方向關系(如:若a·b>0,則二者方向相同,夾角在0°到90°之間),所以Gram矩陣中的值可以反映出兩個feature map之間的某種關系。

          Gram矩陣可以看是feature之間的偏心協(xié)方差矩陣,feature map中的每個數(shù)字都來自于一個特定濾波器在特定位置的卷積,因此每個數(shù)字代表一個特征的強度,而Gram計算的實際上是兩兩特征之間的相關性。Gram的對角線元素提供了不同特征圖各自的信息,而其余元素提供了不同特征圖之間的相關信息,因此,Gram有助于把握整個圖像的大體風格。有了表示風格的Gram矩陣,要度量兩個圖像風格的差異,只需比較他們Gram矩陣的差異即可。


          然后我們就可以利用Gram矩陣提取的風格特征計算損失了,這里我們?nèi)匀皇褂镁秸`差,并進行歸一化操作。

          cd10988698e88f0f8a8078933fe34d7b.webp

          上面是第l層的風格損失函數(shù),N是指生成圖feature map數(shù)量,M是圖片寬乘高,G是生成圖像的Gram矩陣,A是風格圖像的Gram矩陣。


          然后是梯度下降:

          3c38fbd05a35cf431215a92f9536033a.webp

          最終的風格損失函數(shù)為每一層的風格損失加權(quán)求和可得。式中,a是指風格圖像,x是指生成圖像,w是指權(quán)重:

          0f0947e6d69fb4d23b56b492b2233e9a.webp

          下面我們看看如何用代碼實現(xiàn)它:

          # loss板塊import torch.nn as nnimport torch
          class Content_Loss(nn.Module):#內(nèi)容損失 ? ?def __init__(self, target, weight): ? ? ? ?super(Content_Loss, self).__init__()#繼承父類的初始化 ? ? ? ?self.weight = weight ? ? ? ?self.target = target.detach() * self.weight ? ? ? ?# 必須要用detach來分離出target,這時候target不再是一個Variable,這是為了動態(tài)計算梯度,否則forward會出錯,不能向前傳播 ? ? ? ?self.criterion = nn.MSELoss()#利用均方誤差計算損失 ? ? ? ? ? ?def forward(self, input):#向前計算損失 ? ? ? ?self.loss = self.criterion(input * self.weight, self.target) ? ? ? ?out = input.clone() ? ? ? ?return out ? ? ? ? ? ?def backward(self, retain_graph=True):#反向求導 ? ? ? ?self.loss.backward(retain_graph=retain_graph) ? ? ? ?return self.loss ? ? ? ? ? ? ? ?class Gram(nn.Module):#定義Gram矩陣 ? ?def __init__(self): ? ? ? ?super(Gram, self).__init__() ? ? ? ? ? ?def forward(self, input):#向前計算Gram矩陣 ? ? ? ?a, b, c, d = input.size()#a為批量大小,b為feature map的數(shù)量,c*d為feature map的大小 ? ? ? ?feature = input.view(a * b, c * d) ? ? ? ?gram = torch.mm(feature, feature.t()) ? ? ? ?gram /= (a * b * c * d) ? ? ? ?return gram ? ? ? ? ? ? ? ?class Style_Loss(nn.Module):#風格損失 ? ?def __init__(self, target, weight): ? ? ? ?super(Style_Loss, self).__init__() ? ? ? ?self.weight = weight ? ? ? ?self.target = target.detach() * self.weight ? ? ? ?self.gram = Gram() ? ? ? ?self.criterion = nn.MSELoss() ? ? ? ? ? ?def forward(self, input): ? ? ? ?G = self.gram(input) * self.weight ? ? ? ?self.loss = self.criterion(G, self.target) ? ? ? ?out = input.clone() ? ? ? ?return out ? ? ? ? ? ?def backward(self, retain_graph=True): ? ? ? ?self.loss.backward(retain_graph=retain_graph) ? ? ? ?return self.loss


          04

          模型構(gòu)建

          接下來就該構(gòu)建我們的模型啦!雖然我們用的是已經(jīng)預訓練好的vgg框架,但我們還需要對它做一些“改造”:把我們之前構(gòu)造好的損失函數(shù)加進去。畢竟“白嫖”也是有限度的嘛!話不多說,上代碼!

          # build_model模塊import torch.nn as nnimport torchimport torchvision.models as modelsimport loss ?# 指的是上文中已經(jīng)寫好的loss模塊
          device = torch.device("cuda" if torch.cuda.is_available() else "cpu")#選擇運行設備,如果你的電腦有g(shù)pu就在gpu上運行,否則在cpu上運行vgg = models.vgg19(pretrained=True).features.to(device)#這里我們使用預訓練好的vgg19模型'''所需的深度層來計算風格/內(nèi)容損失:'''content_layers_default = ['conv_4']style_layers_default = ['conv_1', 'conv_2', 'conv_3', 'conv_4', 'conv_5']
          def get_style_model_and_loss(style_img, ? ? ? ? ? ? ? ? ? ? ? ? ? ? content_img, ? ? ? ? ? ? ? ? ? ? ? ? ? ? cnn=vgg, ? ? ? ? ? ? ? ? ? ? ? ? ? ? style_weight=1000, ? ? ? ? ? ? ? ? ? ? ? ? ? ? content_weight=1, ? ? ? ? ? ? ? ? ? ? ? ? ? ? content_layers=content_layers_default, ? ? ? ? ? ? ? ? ? ? ? ? ? ? style_layers=style_layers_default): ? ?content_loss_list = [] #內(nèi)容損失 ? ?style_loss_list = [] #風格損失 ? ?model = nn.Sequential() #創(chuàng)建一個model,按順序放入layer ? ?model = model.to(device) ? ?gram = loss.Gram().to(device) ? ? ? ?'''把vgg19中的layer、content_loss以及style_loss按順序加入到model中:''' ? ?i = 1 ? ?for layer in cnn: ? ? ? ?if isinstance(layer, nn.Conv2d): ? ? ? ? ? ?name = 'conv_' + str(i) ? ? ? ? ? ?model.add_module(name, layer) ? ? ? ? ? ?if name in content_layers_default: ? ? ? ? ? ? ? ?target = model(content_img) ? ? ? ? ? ? ? ?content_loss = loss.Content_Loss(target, content_weight) ? ? ? ? ? ? ? ?model.add_module('content_loss_' + str(i), content_loss) ? ? ? ? ? ? ? ?content_loss_list.append(content_loss) ? ? ? ? ? ?if name in style_layers_default: ? ? ? ? ? ? ? ?target = model(style_img) ? ? ? ? ? ? ? ?target = gram(target) ? ? ? ? ? ? ? ?style_loss = loss.Style_Loss(target, style_weight) ? ? ? ? ? ? ? ?model.add_module('style_loss_' + str(i), style_loss) ? ? ? ? ? ? ? ?style_loss_list.append(style_loss) ? ? ? ? ? ?i += 1 ? ? ? ?if isinstance(layer, nn.MaxPool2d): ? ? ? ? ? ?name = 'pool_' + str(i) ? ? ? ? ? ?model.add_module(name, layer) ? ? ? ?if isinstance(layer, nn.ReLU): ? ? ? ? ? ?name = 'relu' + str(i) ? ? ? ? ? ?model.add_module(name, layer) ? ? ? ? ? ? ? ?return model, style_loss_list, content_loss_list


          05

          執(zhí)行

          一切工作準備就緒,我們就可以開始定義我們的run_code模塊了,經(jīng)歷過這么多操作終于走到了最后一步,是不是有點小激動呢?我們的執(zhí)行模塊也很簡單,先定義一個LBFGS優(yōu)化器,然后就可以開始我們一次一次的訓練了。這里我們定義每訓練50次輸出一次我們的損失來評估學習效果。

          # run_code模塊import torch.nn as nnimport torch.optim as optimfrom build_model import get_style_model_and_loss
          def get_input_param_optimier(input_img): ? ?"""input_img is a Variable""" ? ?input_param = nn.Parameter(input_img.data)#獲取參數(shù) ? ?optimizer = optim.LBFGS([input_param])#用LBFGS優(yōu)化參數(shù) ? ?return input_param, optimizer ? ?def run_style_transfer(content_img, style_img, input_img, num_epoches=300): ? ?print('Building the style transfer model..') ? ?model, style_loss_list, content_loss_list = get_style_model_and_loss( ? ? ? ?style_img, content_img) ? ?input_param, optimizer = get_input_param_optimier(input_img) ? ?print('Opimizing...') ? ?epoch = [0] ? ?while epoch[0] < num_epoches:#每隔50次輸出一次loss ? ? ? ?def closure(): ? ? ? ? ? ?input_param.data.clamp_(0, 1)#修正輸入圖像的值 ? ? ? ? ? ?model(input_param) ? ? ? ? ? ?style_score = 0 ? ? ? ? ? ?content_score = 0 ? ? ? ? ? ?optimizer.zero_grad() ? ? ? ? ? ?for sl in style_loss_list: ? ? ? ? ? ? ? ?style_score += sl.backward() ? ? ? ? ? ?for cl in content_loss_list: ? ? ? ? ? ? ? ?content_score += cl.backward() ? ? ? ? ? ?epoch[0] += 1 ? ? ? ? ? ?if epoch[0] % 50 == 0: ? ? ? ? ? ? ? ?print('run {}'.format(epoch)) ? ? ? ? ? ? ? ?print('Style Loss: {:.4f} Content Loss: {:.4f}'.format( ? ? ? ? ? ? ? ? ? ?style_score.item(), content_score.item())) ? ? ? ? ? ? ? ?print() ? ? ? ? ? ?return style_score + content_score ? ? ? ?optimizer.step(closure) ? ? ? ?input_param.data.clamp_(0, 1)#再次修正 ? ?return input_param.data


          06

          運行

          最最最最最激動人心的時刻就要到了!敲了這么多代碼,現(xiàn)在我們就可以開始驗證我們的成果了!現(xiàn)在我們只需要挑選一張中意的圖片,再為它尋找一個你想要的風格圖片,只需短短幾分鐘(甚至幾十秒),你,就創(chuàng)造出了屬于自己的藝術(shù)畫作(其實一般)!!!

          # start模塊from torch.autograd import Variablefrom torchvision import transformsimport torchfrom run_code import run_style_transferfrom load_img import load_img, show_img
          device = torch.device("cuda" if torch.cuda.is_available() else "cpu")style_img = load_img('./picture/style1.png')#風格圖片地址style_img = Variable(style_img).to(device)content_img = load_img('./picture/content5.png')#內(nèi)容圖片地址content_img = Variable(content_img).to(device)input_img = content_img.clone()
          out = run_style_transfer(content_img, style_img, input_img, num_epoches=200)#進行200次訓練save_pic = transforms.ToPILImage()(out.cpu().squeeze(0))save_pic.save('./picture/result4.png')#選擇你要保存的地址save_pic.show()

          下面我們做幾組演示:

          輸入:

          d4721719664f1b011a4eda42bb9baf49.webpf55990613a25a4e9e23929c2619469cf.webp

          輸出:

          9b0022904fa8b3be3519b3dbddc9d1cb.webp


          輸入:

          95a55b228cbafedef011fd153d9dd5bf.webpd3af3adde5a57c1f60aa4a9c2707422a.webp

          輸出:

          68976b34d427f1ef90b3967512ad7001.webp


          最后我們試一試人像:

          bd8f1ee43fc08da7fc256a2521f1199a.webpc00e5c82ce1a03a9f6b3b124e21d013a.webp


          輸出:

          4b30b09bf3da50041410a95e21311ce1.webp


          下面是其中一組loss的輸出:

          Opimizing...run [50]Style Loss: 0.7299 Content Loss: 3.7480run [100]Style Loss: 0.3702 Content Loss: 3.3560run [150]Style Loss: 0.2747 Content Loss: 3.1843run [200]Style Loss: 0.2058 Content Loss: 3.0981

          我們可以看出內(nèi)容損失還是挺大的,損失的大小不僅跟我們的模型有關,我們選擇的圖片也會對損失有很大的影響,不得不說有的圖片特征確實難以捕獲,小編曾經(jīng)還有過訓練1000次仍然有40多損失的慘痛經(jīng)歷,這種情況就不是單純增加訓練次數(shù)就能解決的了,我們需要更強大的模型去捕獲信息,大家也可以自己試試改進一下模型,也許會有意外的收獲呦!


          reference


          1] Gatys L A, Ecker A S, Bethge M. A neural algorithm of artistic style[J]. arXiv preprint arXiv:1508.06576, 2015.


          2] https://pytorch.org/tutorials/advanced/neural_style_tutorial.html#importing-packages-and-selecting-a-device


          1

          END

          1

          文案&排版:王心怡(華中科技大學管理學院本科一年級)

          ? ? ? ? ? ? ? ? ? ? 潘云飛(華中科技大學管理學院本科一年級)


          指導老師:秦虎老師(華中科技大學管理學院)


          審稿:張宇(華中科技大學管理學院本科二年級)


          如對文中內(nèi)容有疑問,歡迎交流。PS:部分資料來自網(wǎng)絡。


          如有需求,可以聯(lián)系:


          秦虎老師(華中科技大學管理學院:[email protected]


          王心怡(華中科技大學管理學院本科一年級:[email protected]


          潘云飛(華中科技大學管理學院本科一年級:[email protected]


          張宇(華中科技大學管理學院本科二年級:[email protected]


          瀏覽 78
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜在线无码 | 看一级一级黄色免费视频 | 丁香五月无码 | 国产又爽又黄A片 | 亚洲一区二区政府在线播放IV |