歡迎光臨
每天分享高質量文章

手把手教你用 PyTorch 快速準確地建立神經網路

(給資料分析與開發加星標,提升資料技能

英文:Shivam Bansal,轉自:資料派(ID:datapi),翻譯:陳之炎

 

簡介

 

你可能已經在社交媒體上看到過N次關於PyTorch和 TensorFlow的兩極分化的爭論。這些框架的普及推動了近年來深度學習的興起。二者都不乏堅定的支持者,但在過去的一年裡,一個明顯的贏家已經開始出現。

 

PyTorch是2018年最流行的框架之一。它已迅速成為學術界和工業界研究人員的首選深度學習框架。在過去幾周使用了PyTorch之後,我體會到它是一個非常靈活且易於使用的深度學習庫。

 

在本文中,我們將探討PyTorch的全部內容。我們將不止學習理論-還包括編寫4個不同的用例,看看PyTorch的表現如何。建立深度學習模型從來沒有這麼有趣過!

 

內容

 

  • 什麼是PyTorch?
  • 利用PyTorch構建神經網路
  • 用例1:手寫數字分類(數字資料,MLP)
  • 用例2:物體影象分類(影象資料,CNN)
  • 用例3:情感文字分類(文字資料,RNN)
  • 用例4:影象樣式的遷移(遷移學習)

 

什麼是PyTorch?

 

在深入研究PyTorch的實現之前,讓我們先瞭解一下PyTorch是什麼,以及為什麼它最近會變得如此流行。

 

PyTorch是一個基於Python的科學計算包,類似於NumPy,它具備GPU附加功能。與此同時,它也是一個深度學習框架,為實現和構建深層神經網路體系結構提供了最大程度的靈活性和速度。

 

最近釋出的PyTorch 1.0幫助研究人員應對以下四大挑戰:

 

  • 大面積的返工
  • 耗時的訓練
  • Python語言缺乏靈活性
  • 慢速擴充套件

 

從本質上講,PyTorch與其他深度學習框架有兩個不同點:

 

  • 指令式程式設計
  • 動態計算圖

 

指令式程式設計:PyTorch在遍歷每一行程式碼的同時執行計算,這與Python程式的執行方式非常類似,這一概念稱為指令式程式設計,它的最大優點是可以動態地除錯程式碼和程式設計邏輯。

 

動態計算圖:PyTorch被稱為“由執行定義的”框架,這意味著計算圖結構(神經網路體系結構)是在執行時生成的。該屬性的主要優點是:它提供了一個靈活的程式設計執行時介面,透過連線操作來方便系統的構建和修改。在PyTorch中,每個前向通路處定義一個新的計算圖,這與使用靜態圖的TensorFlow形成了鮮明的對比。

 

PyTorch1.0附帶了一個名為torch.jit的重要特性,它是一個高階編譯器,允許使用者分離模型和程式碼。此外,它還支援在定製硬體(如GPU或TPU)上進行有效的模型最佳化。

 

用PyTorch構建神經網路

 

讓我們透過一個實際案例來理解PyTorch。學習理論固然好,但是如果你不把它付諸實踐的話,它就沒有多大用處了!

 

神經網路的PyTorch實現看起來與NumPy實現完全一樣。本節的標的是展示PyTorch和NumPy的等效性質。為此,讓我們建立一個簡單的三層網路,在輸入層中有5個節點,在隱藏層中有3個節點,在輸出層中有1個節點。我們只使用一個帶有五個特徵和一個標的的單行訓練示例。

 

import torch

n_input, n_hidden, n_output = 5, 3, 1

 

第一步是進行引數初始化。這裡,每個層的權重和偏置引數被初始化為張量變數。張量是PyTorch的基本資料結構,用於建立不同型別的神經網路。可以將它們當作是陣列和矩陣的推廣,換句話說,張量是N維矩陣。

 

## initialize tensor for inputs, and outputs

x = torch.randn((1, n_input))

y = torch.randn((1, n_output))

## initialize tensor variables for weights

w1 = torch.randn(n_input, n_hidden) # weight for hidden layer

w2 = torch.randn(n_hidden, n_output) # weight for output layer

## initialize tensor variables for bias terms

b1 = torch.randn((1, n_hidden)) # bias for hidden layer

b2 = torch.randn((1, n_output)) # bias for output layer

 

在引數初始化完成之後,可以透過以下四個關鍵步驟來定義和訓練神經網路:

 

  • 前向傳播
  • 損失計算
  • 反向傳播
  • 更新引數

 

讓我們更詳細地瞭解每一個步驟。

 

前向傳播:在這個步驟中,每個層都使用以下兩個公式計算啟用流。這些啟用流從輸入層流向輸出層,以生成最終輸出。

 

1. z = weight * input + bias

2. a = activation_function (z)

 

下麵的程式碼塊顯示瞭如何用PyTorch編寫這些步驟。請註意,大多數函式,如指數和矩陣乘法,均與NumPy中的函式相類似。

 

## sigmoid activation function using pytorch

def sigmoid_activation(z):

    return 1 / (1 + torch.exp(-z))

## activation of hidden layer

z1 = torch.mm(x, w1) + b1

a1 = sigmoid_activation(z1)

## activation (output) of final layer

z2 = torch.mm(a1, w2) + b2

output = sigmoid_activation(z2)

 

損失計算:這一步在輸出層中計算誤差 (也稱為損失)。一個簡單的損失函式可以用來衡量實際值和預測值之間的差異。稍後,我們將檢視PyTorch中可用的不同型別的損失函式。

 

loss = y - output

 

反向傳播:這一步的目的是透過對偏差和權重進行邊際變化,從而將輸出層的誤差降到最低,邊際變化是利用誤差項的導數計算出來的。

 

根據鏈規則的微積分原理,將增量變化傳回到隱藏層,並對其權重和偏差進行相應的修正。透過對權重和偏差的調整,使得誤差最小化。

 

## function to calculate the derivative of activation

def sigmoid_delta(x):

  return x * (1 - x)

## compute derivative of error terms

delta_output = sigmoid_delta(output)

delta_hidden = sigmoid_delta(a1)

## backpass the changes to previous layers

d_outp = loss * delta_output

loss_h = torch.mm(d_outp, w2.t())

d_hidn = loss_h * delta_hidden

 

更新引數:最後一步,利用從上述反向傳播中接收到的增量變化來對權重和偏差進行更新。

 

learning_rate = 0.1

w2 += torch.mm(a1.t(), d_outp) * learning_rate

w1 += torch.mm(x.t(), d_hidn) * learning_rate

b2 += d_outp.sum() * learning_rate

b1 += d_hidn.sum() * learning_rate

 

當使用大量訓練示例對多個歷元執行這些步驟時,損失將降至最小值。得到最終的權重和偏差值之後,用它對未知資料進行預測。

 

用例1:手寫數字分類

 

在上一節中,我們看到了用PyTorch編寫神經網路的簡單用例。在本節中,我們將利用PyTorch提供的不同的實用程式包(nn、autograd、Optimm、torchvision、torchtext等)來建立和訓練神經網路。

 

利用這些包可以方便地定義和管理神經網路。在這個用例中,我們將建立一個多層感知器(MLP)網路,用於構建手寫數字分類器。我們將使用torchvision包中的MNIST資料集。

 

與你將要從事的任何專案一樣,第一步是資料預處理:首先需要將原始資料集轉換為張量,併在固定範圍內將其歸一化。torchvision包提供了一個名為 transforms的實用程式,利用它可以將不同的轉換組合在一起。

 

from torchvision import transforms

_tasks = transforms.Compose([

    transforms.ToTensor(),

    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))

    ])

 

第一個轉換是將原始資料轉換為張量,第二個轉換是透過以下操作執行歸一化:

 

x_normalized = x-mean / std

 

數值為0.5,0.5表示紅色、綠色和藍色三個通道的均值和標準差。

 

from torchvision.datasets import MNIST

## Load MNIST Dataset and apply transformations

mnist = MNIST("data", download=True, train=True, transform=_tasks)

 

PyTorch的另一個出色的實用工具是DataLoader迭代器,它為多個處理器之間並行地批處理、搬移和載入資料提供了實現的可能。為了評估這個模型,我們將資料集劃分為訓練集和驗證集。

 

from torch.utils.data import DataLoader

from torch.utils.data.sampler import SubsetRandomSampler

## create training and validation split

split = int(0.8 * len(mnist))

index_list = list(range(len(mnist)))

train_idx, valid_idx = index_list[:split], index_list[split:]

## create sampler objects using SubsetRandomSampler

tr_sampler = SubsetRandomSampler(train_idx)

val_sampler = SubsetRandomSampler(valid_idx)

## create iterator objects for train and valid datasets

trainloader = DataLoader(mnist, batch_size=256, sampler=tr_sampler)

validloader = DataLoader(mnist, batch_size=256, sampler=val_sampler)

 

PyTorch中的神經網路架構可以定義為一個類,這個類繼承了稱為Module的nn包的基礎類的所有屬性。來自nn.Module類的繼承使得我們可以輕鬆地實現、訪問和呼叫多個方法,還可以定義類的建構式中的各個層,以及前向傳播步驟中的前向函式。

 

我們將定義一個具有以下層配置的網路:[784,128,10]。此配置表示輸入層中有784個節點(28*28畫素)、隱藏層中有128個節點,輸出層中有10個節點。在前向函式中,我們將在隱藏層(可以透過nn模組訪問)中使用Sigmoid啟用函式。

 

import torch.nn.functional as F

class Model(nn.Module):

    def __init__(self):

        super().__init__()

        self.hidden = nn.Linear(784, 128)

        self.output = nn.Linear(128, 10)

  

   def forward(self, x):

        x = self.hidden(x)

        x = F.sigmoid(x)

        x = self.output(x)

        return x

model = Model()

 

利用nn和Optim包定義損失函式和最佳化器:

 

from torch import optim

loss_function = nn.CrossEntropyLoss()

optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay= 1e-6, momentum = 0.9, nesterov = True)

 

現在已經準備好,可以開始訓練模型了,其核心步驟與前一節相同:前向傳播、損失計算、反向傳播和更新引數。

 

for epoch in range(1, 11): ## run the model for 10 epochs

    train_loss, valid_loss = [], []

    ## training part

    model.train()

    for data, target in trainloader:

        optimizer.zero_grad()

        ## 1. forward propagation

        output = model(data)

        

        ## 2. loss calculation

        loss = loss_function(output, target)

        

        ## 3. backward propagation

        loss.backward()

        

        ## 4. weight optimization

        optimizer.step()

        

        train_loss.append(loss.item())

        

    ## evaluation part

    model.eval()

    for data, target in validloader:

        output = model(data)

        loss = loss_function(output, target)

        valid_loss.append(loss.item())

    print ("Epoch:", epoch, "Training Loss: ", np.mean(train_loss), "Valid Loss: ", np.mean(valid_loss))

 

>> Epoch: 1  Training Loss:  0.645777 Valid Loss:  0.344971

>> Epoch: 2  Training Loss:  0.320241 Valid Loss:  0.299313

>> Epoch: 3  Training Loss:  0.278429 Valid Loss:  0.269018

>> Epoch: 4  Training Loss:  0.246289 Valid Loss:  0.237785

>> Epoch: 5  Training Loss:  0.217010 Valid Loss:  0.217133

>> Epoch: 6  Training Loss:  0.193017 Valid Loss:  0.206074

>> Epoch: 7  Training Loss:  0.174385 Valid Loss:  0.180163

>> Epoch: 8  Training Loss:  0.157574 Valid Loss:  0.170064

>> Epoch: 9  Training Loss:  0.144316 Valid Loss:  0.162660

>> Epoch: 10 Training Loss:  0.133053 Valid Loss:  0.152957

 

完成了模型的訓練之後,即可在驗證資料基礎上進行預測。

 

## dataloader for validation dataset

dataiter = iter(validloader)

data, labels = dataiter.next()

output = model(data)

_, preds_tensor = torch.max(output, 1)

preds = np.squeeze(preds_tensor.numpy())

print ("Actual:", labels[:10])

print ("Predicted:", preds[:10])



>>> Actual: [0 1 1 1 2 2 8 8 2 8]

>>> Predicted: [0 1 1 1 2 2 8 8 2 8]

 

用例2:物體影象分類

 

現在讓我們更進一步。

 

在這個用例中,我們將在PyTorch中建立摺積神經網路(CNN)架構,利用流行的CIFAR-10資料集進行物體影象分類,此資料集也包含在torchvision包中。定義和訓練模型的整個過程將與以前的用例相同,唯一的區別隻是在網路中引入了額外的層。

 

載入並轉換資料集:

 

## load the dataset

from torchvision.datasets import CIFAR10

cifar = CIFAR10('data', train=True, download=True, transform=_tasks)

## create training and validation split

split = int(0.8 * len(cifar))

index_list = list(range(len(cifar)))

train_idx, valid_idx = index_list[:split], index_list[split:]

## create training and validation sampler objects

tr_sampler = SubsetRandomSampler(train_idx)

val_sampler = SubsetRandomSampler(valid_idx)

## create iterator objects for train and valid datasets

trainloader = DataLoader(cifar, batch_size=256, sampler=tr_sampler)

validloader = DataLoader(cifar, batch_size=256, sampler=val_sampler)

 

我們將建立三個用於低層特徵提取的摺積層、三個用於最大資訊量提取的池化層和兩個用於線性分類的線性層。

 

class Model(nn.Module):

    def __init__(self):

        super(Model, self).__init__()

        

        ## define the layers

        self.conv1 = nn.Conv2d(3, 16, 3, padding=1)

        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)

        self.conv3 = nn.Conv2d(32, 64, 3, padding=1)

        self.pool = nn.MaxPool2d(2, 2)

        self.linear1 = nn.Linear(1024, 512)

        self.linear2 = nn.Linear(512, 10)

    

    def forward(self, x):

        x = self.pool(F.relu(self.conv1(x)))

        x = self.pool(F.relu(self.conv2(x)))

        x = self.pool(F.relu(self.conv3(x)))

        x = x.view(-1, 1024) ## reshaping

        x = F.relu(self.linear1(x))

        x = self.linear2(x)

        return x

model = Model()

 

定義損失函式和最佳化器:

 

import torch.optim as optim

loss_function = nn.CrossEntropyLoss()

optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay= 1e-6, momentum = 0.9, nesterov = True)

## run for 30 Epochs

for epoch in range(1, 31):

    train_loss, valid_loss = [], []

    ## training part

    model.train()

    for data, target in trainloader:

        optimizer.zero_grad()

        output = model(data)

        loss = loss_function(output, target)

        loss.backward()

        optimizer.step()

        train_loss.append(loss.item())

        

    ## evaluation part

    model.eval()

    for data, target in validloader:

        output = model(data)

        loss = loss_function(output, target)

        valid_loss.append(loss.item())

 

完成了模型的訓練之後,即可在驗證資料基礎上進行預測。

 

## dataloader for validation dataset

dataiter = iter(validloader)

data, labels = dataiter.next()

output = model(data)

_, preds_tensor = torch.max(output, 1)

preds = np.squeeze(preds_tensor.numpy())

print ("Actual:", labels[:10])

print ("Predicted:", preds[:10])

Actual: ['truck', 'truck', 'truck', 'horse', 'bird', 'truck', 'ship', 'bird', 'deer', 'bird']

Pred:   ['truck', 'automobile', 'automobile', 'horse', 'bird', 'airplane', 'ship', 'bird', 'deer', 'bird']

 

用例3:情感文字分類

 

我們將從計算機視覺用例轉向自然語言處理,目的是展示PyTorch在不同領域的不同應用。

 

在本節中,我們將利用基於RNN(遞迴神經網路)和LSTM(長短期記憶)層的Pyotch來完成文字分類任務。首先,載入包含兩個欄位(文字和標的)的資料集。標的包含兩個類:class1和class2,我們的任務是將每個文字分為其中一個類。

 

可以在下麵的連結中下載資料集。

 

https://s3-ap-south-1.amazonaws.com/av-blog-media/wp-content/uploads/2019/01/train.csv

 

train = pd.read_csv("train.csv")

x_train = train["text"].values

y_train = train['target'].values

 

強烈建議在編碼之前先設定種子,它可以保證你看到的結果與我的相同-這是在學習新概念時非常有用(也很有益)的特徵。

 

np.random.seed(123)

torch.manual_seed(123)

torch.cuda.manual_seed(123)

torch.backends.cudnn.deterministic = True

 

在預處理步驟中,首先將文字資料轉換為tokens序列,之後便可以將其傳遞到嵌入層。我將利用Keras包中提供的實用程式來進行預處理,利用torchtext包也同樣可以實現。

 

from keras.preprocessing import text, sequence

## create tokens

tokenizer = Tokenizer(num_words = 1000)

tokenizer.fit_on_texts(x_train)

word_index = tokenizer.word_index

## convert texts to padded sequences

x_train = tokenizer.texts_to_sequences(x_train)

x_train = pad_sequences(x_train, maxlen = 70)

 

接下來,需要將tokens轉換成向量。為此,利用預先訓練過的GloVe詞嵌入。我們將載入這些單詞嵌入,並建立一個包含單詞向量的嵌入矩陣。

 

GloVe:

https://github.com/stanfordnlp/GloVe

 

EMBEDDING_FILE = 'glove.840B.300d.txt'

embeddings_index = {}

for i, line in enumerate(open(EMBEDDING_FILE)):

    val = line.split()

    embeddings_index[val[0]] = np.asarray(val[1:], dtype='float32')

embedding_matrix = np.zeros((len(word_index) + 1, 300))

for word, i in word_index.items():

    embedding_vector = embeddings_index.get(word)

    if embedding_vector is not None:

        embedding_matrix[i] = embedding_vector

 

使用嵌入層和LSTM層定義模型架構:

 

class Model(nn.Module):

    def __init__(self):

        super(Model, self).__init__()

        

        ## Embedding Layer, Add parameter

        self.embedding = nn.Embedding(max_features, embed_size)

        et = torch.tensor(embedding_matrix, dtype=torch.float32)

        self.embedding.weight = nn.Parameter(et)

        self.embedding.weight.requires_grad = False

        self.embedding_dropout = nn.Dropout2d(0.1)

        self.lstm = nn.LSTM(300, 40)        

        self.linear = nn.Linear(40, 16)

        self.out = nn.Linear(16, 1)

        self.relu = nn.ReLU()

   def forward(self, x):

        h_embedding = self.embedding(x)        

        h_lstm, _ = self.lstm(h_embedding)

        max_pool, _ = torch.max(h_lstm, 1)        

        linear = self.relu(self.linear(max_pool))

        out = self.out(linear)

        return out

model = Model()

 

建立訓練和驗證集:

 

from torch.utils.data import TensorDataset

## create training and validation split

split_size = int(0.8 * len(train_df))

index_list = list(range(len(train_df)))

train_idx, valid_idx = index_list[:split], index_list[split:]

## create iterator objects for train and valid datasets

x_tr = torch.tensor(x_train[train_idx], dtype=torch.long)

y_tr = torch.tensor(y_train[train_idx], dtype=torch.float32)

train = TensorDataset(x_tr, y_tr)

trainloader = DataLoader(train, batch_size=128)

x_val = torch.tensor(x_train[valid_idx], dtype=torch.long)

y_val = torch.tensor(y_train[valid_idx], dtype=torch.float32)

valid = TensorDataset(x_val, y_val)

validloader = DataLoader(valid, batch_size=128)

 

定義損失和最佳化器:

 

loss_function = nn.BCEWithLogitsLoss(reduction='mean')

optimizer = optim.Adam(model.parameters())

 

訓練模型:

 

## run for 10 Epochs

for epoch in range(1, 11):

    train_loss, valid_loss = [], []

## training part

    model.train()

    for data, target in trainloader:

        optimizer.zero_grad()

        output = model(data)

        loss = loss_function(output, target.view(-1,1))

        loss.backward()

        optimizer.step()

        train_loss.append(loss.item())

        

    ## evaluation part

    model.eval()

    for data, target in validloader:

        output = model(data)

        loss = loss_function(output, target.view(-1,1))

        valid_loss.append(loss.item())

 

最後得到預測結果:

 

dataiter = iter(validloader)

data, labels = dataiter.next()

output = model(data)

_, preds_tensor = torch.max(output, 1)

preds = np.squeeze(preds_tensor.numpy())

Actual: [0 1 1 1 1 0 0 0 0]

Predicted: [0 1 1 1 1 1 1 1 0 0]

 

用例4:影象樣式遷移

 

讓我們來看最後一個用例,在這裡我們將執行圖形樣式的遷移。這是我經歷過的最有創意的專案之一,希望你也能玩得開心。樣式遷移概念背後的基本理念是:

 

  • 從一幅影象中獲取物件/內容
  • 從另一幅影象中獲取樣式/紋理
  • 生成二者混合的最終影象

 

“利用摺積網路進行影象樣式遷移”這篇論文中對這一概念做了介紹,樣式遷移的一個例子如下:

 

太棒了,對吧?讓我們看看它在PyTorch中是如何實現的。這一行程包括六個步驟:

 

  • 從兩個輸入影象中提取低層特徵。這可以使用VGG 19這樣的預訓練的深度學習模型。

 

from torchvision import models

# get the features portion from VGG19

vgg = models.vgg19(pretrained=True).features

 

# freeze all VGG parameters

for param in vgg.parameters():

    param.requires_grad_(False)

# check if GPU is available

device = torch.device("cpu")

if torch.cuda.is_available():

    device = torch.device("cuda")

vgg.to(device)

 

  • 將這兩幅影象載入到裝置上,並從VGG中獲取特徵。另外,也可以應用以下轉換:調整張量的大小,以及值的歸一化。

 

from torchvision import transforms as tf

def transformation(img):

    tasks = tf.Compose([tf.Resize(400), tf.ToTensor(),

               tf.Normalize((0.44,0.44,0.44),(0.22,0.22,0.22))])

    img = tasks(img)[:3,:,:].unsqueeze(0)    

    return img

img1 = Image.open("image1.jpg").convert('RGB')

img2 = Image.open("image2.jpg").convert('RGB')

img1 = transformation(img1).to(device)

img2 = transformation(img2).to(device)

 

  • 現在,我們需要獲得這兩幅影象的相關特徵。從第一個影象中,我們需要提取內容或與存在的物件相關的特徵;從第二張影象中,我們需要提取與樣式和紋理相關的特徵。

 

物件相關特徵:在最初的文章中,作者建議可以從網路的初始層中提取更有價值的物件和內容,這是因為在較高層上,資訊空間變得更為複雜,畫素資訊細節在高層往往會丟失。

 

樣式相關特徵:為了從第二幅影象中獲取樣式和紋理資訊,作者在不同層次上使用了不同特徵之間的相關性,下文第4點對此作了詳細解釋。

 

在實現這一標的之前,讓我們來看看一個典型的VGG 19模型的結構:

 

物件資訊提取用到的是CONV42層,它位於第4個摺積塊中,深度為512。對於樣式的表達,用到的層是網路中每個摺積塊的第一卷積層,即CONV11、CONV21、CONV31、CONV41和CONV51,這些層的選取純粹是根據作者的經驗來做出選擇,我僅在本文中複製它們的結果。

 

def get_features(image, model):

    layers = {'0': 'conv1_1', '5': 'conv2_1',  '10': 'conv3_1',

              '19': 'conv4_1', '21': 'conv4_2', '28': 'conv5_1'}

    x = image

    features = {}

    for name, layer in model._modules.items():

        x = layer(x)

        if name in layers:

            features[layers[name]] = x     

    return features

img1_features = get_features(img1, vgg)

img2_features = get_features(img2, vgg)

 

  • 正如前面提到的,作者使用不同層次的相關性來獲得與樣式相關的特徵。這些特徵的相關性由Gram矩陣G給出,其中G中的每個單元(i,j)都是層中向量特徵對映i和j之間的內積。

 

def correlation_matrix(tensor):

    _, d, h, w = tensor.size()    

    tensor = tensor.view(d, h * w)    

    correlation = torch.mm(tensor, tensor.t())

    return correlation

correlations = {l: correlation_matrix(img2_features[l]) for l in 

                                                    img2_features}

 

  • 最終,可以利用這些特徵和相關性進行樣式轉換。現在,為了將樣式從一個影象轉換到另一個影象,需要設定用於獲取樣式特徵的每一層的權重。如上所述,由於初始層提供了更多的資訊,因此可以為初始層設定更高的權重。此外,定義最佳化器函式和標的影象,也即是影象1的副本。

 

weights = {'conv1_1': 1.0, 'conv2_1': 0.8, 'conv3_1': 0.25,

           'conv4_1': 0.21, 'conv5_1': 0.18}

 

target = img1.clone().requires_grad_(True).to(device)

optimizer = optim.Adam([target], lr=0.003)

 

  • 啟動損失最小化處理過程:即在迴圈中執行大量步驟,來計算與物件特徵提取和樣式特徵提取相關的損失。利用最小化後的損失,更新網路引數,進一步修正標的影象。經過一些迭代之後,將生成更新後的影象。

 

for ii in range(1, 2001):

    

    ## calculate the content loss (from image 1 and target)

    target_features = get_features(target, vgg)

    loss = target_features['conv4_2'] - img1_features['conv4_2']

    content_loss = torch.mean((loss)**2)

    

    ## calculate the style loss (from image 2 and target)

    style_loss = 0

    for layer in weights:

        

        target_feature = target_features[layer]

        target_corr = correlation_matrix(target_feature)

        style_corr = correlations[layer]

        

        layer_loss = torch.mean((target_corr - style_corr)**2)

        layer_loss *= weights[layer]

        

        _, d, h, w = target_feature.shape

        style_loss += layer_loss / (d * h * w)   

    

    total_loss = 1e6 * style_loss + content_loss

    

    optimizer.zero_grad()

    total_loss.backward()

    optimizer.step()

 

最後,我們可以看到預測的結果,在這裡我只運行了一小部分迭代,還可以執行多達3000次迭代(如果計算資源足夠多的話!)。

 

def tensor_to_image(tensor):

    image = tensor.to("cpu").clone().detach()

    image = image.numpy().squeeze()

    image = image.transpose(1, 2, 0)

    image *= np.array((0.22, 0.22, 0.22))

                       + np.array((0.44, 0.44, 0.44))

    image = image.clip(0, 1)

    return image

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))

ax1.imshow(tensor_to_image(img1))

ax2.imshow(tensor_to_image(target))

 

後記

 

PyTorch還可以實現大量的其他用例,它很快成為全球研究人員的寵兒。絕大多數PyTorch實現的開源庫和開發應用可以在Github上看到。

 

在本文中,我闡述了什麼是PyTorch,以及如何用PyTorch實現不同的用例,當然,這個指南只是一個出發點。如果能提供更多的資料,或進行更多的網路引數微調,那麼每個用例的效能都可以得到大幅度提高,最重要的是如果在構建網路體系架構時應用創新技能,也能提高用例的效能。感謝你的閱讀,並請在下麵的評論部分留下你的反饋。

 

參考文獻

 

1. 官方PyTorch指南:

https://pytorch.org/tutorials/

 

2. 用PyTorch進行深度學習

https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html

 

3. Faizan在 Analytics Vidhya上發表的文章:

https://www.analyticsvidhya.com/blog/2018/02/pytorch-tutorial/

 

4. 使用Pytorch的Udacity深度學習: 

https://github.com/udacity/deep-learning-v2-pytorch

 

5. 圖片樣式遷移原始論文:

https://www.cvfoundation.org/openaccess/content_cvpr_2016/papers/Gatys_Image_Style_Transfer_CVPR_2016_paper.pdf

 

 

推薦閱讀

(點選標題可跳轉閱讀)

手把手教你用 Kaggle 開啟機器學習之旅

機器學習 GitHub 專案及 Reddit 熱門話題

教你用摺積神經網路對視覺神經元進行建模

看完本文有收穫?請轉發分享給更多人

關註「資料分析與開發」加星標,提升資料技能

喜歡就點一下「好看」唄~

    閱讀原文

    贊(0)

    分享創造快樂