用PyTorch重新創建Keras API

2021-01-11 人工智慧遇見磐創

介紹

Francois Chollet寫的《Deep Learning with Python》一書讓我進入了深度學習的世界。從那時起我就愛上了Keras的風格。

Keras是我的第一個框架,然後是Tensorflow,接著進入PyTorch。

老實說,在Keras的模型訓練中,我很興奮這個進度條,真是太棒了。

那麼,為什麼不嘗試把Keras訓練模型的經驗帶到PyTorch呢?

這個問題讓我開始了工作,最後我用所有那些花哨的進度條重現了Keras的Dense層、卷積層和平坦層。

模型可以通過堆疊一層到另一層來創建,並通過簡單地調用fit方法進行訓練,該方法類似於Keras的工作方式。

Keras的工作方式如下:

#一層一層疊起來#採用輸入數據的形狀inputs = keras.Input(shape=(784,))l1 = layers.Dense(64, activation="relu")(inputs)l2 = layers.Dense(64, activation="relu")(l1)outputs = layers.Dense(10)(l2)model = keras.Model(inputs=inputs, outputs=outputs)#輸出模型摘要model.summary()#模型訓練和評估model.fit(x_train, y_train, epochs=2)model.evaluate(x_test, y_test)1.導入所需的庫

你可能不熟悉庫pkbar,它用於顯示類似Keras的進度條。

!pip install pkbarimport torchfrom torch import nnfrom torch import optimfrom torch.autograd import Variablefrom torchsummary import summary as summary_import pkbarimport warningswarnings.filterwarnings('ignore')2.輸入層和dense層

輸入層只是以數據的單一實例的形式被傳遞到神經網絡並返回它,對於全連接的網絡,它將類似於(1,784),對於卷積神經網絡,它將是圖像的尺寸(高度×寬度×通道)。

使用大寫字母來命名python函數是違反規則的,但是我們暫時忽略它(Keras原始碼的某些部分使用相同的約定)。

def Input(shape): Input.shape = shape return Input.shapedef get_conv_output(shape, inputs): bs = 1 data = Variable(torch.rand(bs, *shape)) output_feat = inputs(data) return output_feat.size(1)def same_pad(h_in, kernal, stride, dilation): return (stride*(h_in-1)-h_in+(dilation*(kernal-1))+1) / 2.0Dense類通過傳遞該層的輸出神經元數量和激活函數來初始化。調用Dense層時,前一層作為輸入傳遞。

現在我們有了關於前一層的信息。如果前一層是輸入層,則創建一個PyTorch線性層,其中輸入層返回的形狀和輸出神經元的數量作為Dense類初始化期間的參數。

如果前一層是Dense層,我們通過在Dense類中增加一個PyTorch線性層和一個激活層來擴展神經網絡。

如果前一層是卷積層或平坦層,我們將創建一個名為get_conv_output()的實用函數,通過卷積層和平坦層得到圖像的輸出形狀。此維度是必需的,因為如果不向in_features參數傳遞值,則無法在PyTorch中創建線性層。

函數的作用是將圖像形狀和卷積神經網絡模型作為輸入。然後,它創建一個與圖像形狀相同的虛擬張量,並將其傳遞給卷積網絡(具有平坦層),並返回從中輸出的數據的大小,該大小作為值傳遞給PyTorch線性層中的in_features參數。

class Dense(nn.Module): def __init__(self, outputs, activation): super().__init__() self.outputs = outputs self.activation = activation def __call__(self, inputs): self.inputs_size = 1 if type(inputs) == tuple: for i in range(len(inputs)): self.inputs_size *= inputs[i] self.layers = nn.Sequential( nn.Linear(self.inputs_size, self.outputs), self.activation ) return self.layers elif isinstance(inputs[-2], nn.Linear): self.inputs = inputs self.layers = list(self.inputs) self.layers.extend([nn.Linear(self.layers[-2].out_features, self.outputs), self.activation]) self.layers = nn.Sequential(*self.layers) return self.layers else: self.inputs = inputs self.layers = list(self.inputs) self.layers.extend([nn.Linear(get_conv_output(Input.shape, self.inputs), self.outputs), self.activation]) self.layers = nn.Sequential(*self.layers) return self.layers3.平坦層

為了創建一個平坦層,我們將創建一個名為FlattenedLayer的自定義層類,它接受張量作為輸入,並在前向傳播期間返回張量的平坦版本。

我們將創建另一個名為flatten的類,當調用這個層時,前面的層作為輸入傳遞,然後flatten類通過在前面的層上添加我們自定義創建的FlattenedLayer類來擴展網絡。

因此,所有到達平坦層的數據都是使用我們自定義創建的平坦層進行平坦的。

class FlattenedLayer(nn.Module): def __init__(self): super().__init__() pass def forward(self, input): self.inputs = input.view(input.size(0), -1) return self.inputsclass Flatten(): def __init__(self): pass def __call__(self, inputs): self.inputs = inputs self.layers = list(self.inputs) self.layers.extend([FlattenedLayer()]) self.layers = nn.Sequential(*self.layers) return self.layers4.卷積層

我們將通過傳入濾波器數量、內核大小、步長、填充、膨脹和激活函數來初始化Conv2d層。

現在,當調用Conv2d層時,前面的層被傳遞給它,如果前一層是Input layer,則是一個Pytorch conv2d層,其中提供了濾波器數量、內核大小、步長、填充,擴張和激活函數被創建,其中in_channels的值取自輸入形狀中的通道數。

如果前一層是卷積層,則通過添加一個PyTorch Conv2d層和激活函數來擴展前一層,激活函數的值取自前一層的out_channels 。

在填充的情況下,如果用戶需要保留從該層傳出的數據的維度,則可以將padding的值指定為「same」,而不是整數。

如果padding的值被指定為「same」,那麼將使用一個名為same_pad()的實用函數來獲取padding的值,以保留給定輸入大小、內核大小、步長和膨脹的維度。

可以使用前面討論的get_conv_output()實用程序函數獲得輸入大小。

class Conv2d(nn.Module): def __init__(self, filters, kernel_size, strides, padding, dilation, activation): super().__init__() self.filters = filters self.kernel = kernel_size self.strides = strides self.padding = padding self.dilation = dilation self.activation = activation def __call__(self, inputs): if type(inputs) == tuple: self.inputs_size = inputs if self.padding == 'same': self.padding = int(same_pad(self.inputs_size[-2], self.kernel, self.strides, self.dilation)) else: self.padding = self.padding self.layers = nn.Sequential( nn.Conv2d(self.inputs_size[-3], self.filters, self.kernel, self.strides, self.padding, self.dilation), self.activation ) return self.layers else: if self.padding == 'same': self.padding = int(same_pad(get_conv_output(Input.shape, inputs), self.kernel, self.strides, self.dilation)) else: self.padding = self.padding self.inputs = inputs self.layers = list(self.inputs) self.layers.extend( [nn.Conv2d(self.layers[-2].out_channels, self.filters, self.kernel, self.strides, self.padding, self.dilation), self.activation] ) self.layers = nn.Sequential(*self.layers) return self.layers5.模型類

在構建了模型的體系結構之後,通過傳入輸入層和輸出層來初始化模型類。但是我已經給出了一個額外的參數,名為device,它在Keras中不存在,這個參數接受值為'CPU'或'CUDA',它將把整個模型移動到指定的設備。

model類的parameters方法用於返回要給PyTorch優化器的模型參數。

model類有一個名為compile的方法,它接受訓練模型所需的優化器和丟失函數。模型類的摘要方法是藉助torch的summary庫顯示所創建模型的摘要。

採用擬合方法對模型進行訓練,該方法以輸入特徵集、目標數據集和epoch數為參數。它顯示由損失函數計算的損失和使用pkbar庫的訓練進度。

評估會計算驗證數據集的損失和精度。

當使用PyTorch數據加載程序加載數據時,將使用fit_generator、evaluate_generator 和predict_generator 。fit_generator 以訓練集數據加載器和epoch作為參數。evaluate_generator和predict_generator分別使用驗證集數據加載器和測試數據加載器來衡量模型對未查看數據的執行情況。

class Model(): def __init__(self, inputs, outputs, device): self.input_size = inputs self.device = device self.model = outputs.to(self.device) def parameters(self): return self.model.parameters() def compile(self, optimizer, loss): self.opt = optimizer self.criterion = loss def summary(self): summary_(self.model, self.input_size, device=self.device) print("Device Type:", self.device) def fit(self, data_x, data_y, epochs): self.model.train() for epoch in range(epochs): print("Epoch {}/{}".format(epoch+1, epochs)) progress = pkbar.Kbar(target=len(data_x), width=25) for i, (data, target) in enumerate(zip(data_x, data_y)): self.opt.zero_grad() train_out = self.model(data.to(self.device)) loss = self.criterion(train_out, target.to(self.device)) loss.backward() self.opt.step() progress.update(i, values=[("loss: ", loss.item())]) progress.add(1) def evaluate(self, test_x, test_y): self.model.eval() correct, loss = 0.0, 0.0 progress = pkbar.Kbar(target=len(test_x), width=25) for i, (data, target) in enumerate(zip(test_x, test_y)): out = self.model(data.to(self.device)) loss += self.criterion(out, target.to(self.device)) correct += ((torch.max(out, 1)[1]) == target.to(self.device)).sum() progress.update(i, values=[("loss", loss.item()/len(test_x)), ("acc", (correct/len(test_x)).item())]) progress.add(1) def fit_generator(self, generator, epochs): self.model.train() for epoch in range(epochs): print("Epoch {}/{}".format(epoch+1, epochs)) progress = pkbar.Kbar(target=len(generator), width=25) for i, (data, target) in enumerate(generator): self.opt.zero_grad() train_out = self.model(data.to(self.device)) loss = self.criterion(train_out.squeeze(), target.to(self.device)) loss.backward() self.opt.step() progress.update(i, values=[("loss: ", loss.item())]) progress.add(1) def evaluate_generator(self, generator): self.model.eval() correct, loss = 0.0, 0.0 progress = pkbar.Kbar(target=len(generator), width=25) for i, (data, target) in enumerate(generator): out = self.model(data.to(self.device)) loss += self.criterion(out.squeeze(), target.to(self.device)) correct += (torch.max(out.squeeze(), 1)[1] == target.to(self.device)).sum() progress.update(i, values=[("test_acc", (correct/len(generator)).item()), ("test_loss", loss.item()/len(generator))]) progress.add(1) def predict_generator(self, generator): self.model.train() out = [] for i, (data, labels) in enumerate(generator): out.append(self.model(data.to(self.device))) return out結尾

我用Dense層和卷積神經網絡在CIFAR100、CIFAR10和MNIST數據集上測試了代碼。它工作得很好,但還有很大的改進空間。

這是一個有趣的項目,我已經工作了3-4天,它真的突破了我用PyTorch編程的極限。

你可以在這裡查看完整的代碼,並在上面提到的數據集上進行訓練,或者你可以自由地調整代碼以適合你在colab中的喜好:https://colab.research.google.com/github/bipinKrishnan/torchkeras/blob/master/functional_api_v1.ipynb

相關焦點

  • 初學AI神經網絡應該選擇Keras或是Pytorch框架?
    軟體開發者畢竟不是科學家,很多時候簡單易學易用是程式設計師選擇的第一要素。目前,兩個主要的深度學習庫Keras和Pytorch獲得了大量關注,主要是因為它們的使用比較簡單。keras是神經網絡的一個模型計算框架,嚴格來說不是神經網絡框架。本身沒有重量級計算,它使用其他AI框架作為計算後臺,傻瓜式的使用。它的計算後臺還支持 Theano、CNTK(微軟的一個AI計算後臺)等,也就是說keras是多個計算後臺的門面。官方設計哲學為Simple. Flexible.
  • Keras vs PyTorch:誰是第一深度學習框架?
    兩大框架的連結:  Keras:https://github.com/keras-team/keras (https://keras.io/)  PyTorch:https://github.com/pytorch/pytorch  你想學習深度學習嗎?
  • 用Java實現目標檢測|PyTorch
    這篇來自AWS軟體工程師的投稿,結合實例,詳細介紹了DJL這個為Java開發者設計的深度學習庫:5分鐘,你就能在PyTorch上,用Java實現目標檢測。5分鐘,用Java實現目標檢測文 / 知乎用戶@LankingPyTorch在深度學習領域中的應用日趨廣泛,得益於它獨到的設計。無論是數據的並行處理還是動態計算圖,一切都為Python做出了很多簡化。
  • 用Java實現目標檢測 | PyTorch
    這篇來自AWS軟體工程師的投稿,結合實例,詳細介紹了DJL這個為Java開發者設計的深度學習庫:5分鐘,你就能在PyTorch上,用Java實現目標檢測。5分鐘,用Java實現目標檢測文 / 知乎用戶@LankingPyTorch在深度學習領域中的應用日趨廣泛,得益於它獨到的設計。
  • 如何在Keras中創建自定義損失函數?
    Keras 是一個創建神經網絡的庫,它是開源的,用 Python 語言編寫。Keras 不支持低級計算,但它運行在諸如 Theano 和 TensorFlow 之類的庫上。在本教程中,我們將使用 TensorFlow 作為 Keras backend。
  • 新版PyTorch 1.2 已發布:功能更多、兼容更全、操作更快!
    TorchScript 是一種使用 PyTorch 代碼創建可序列化和可優化模型的方法;任何 TorchScript 程序都可以從 Python 進程中保存,並在不依賴於 Python 的進程中實現加載。
  • Keras vs PyTorch,哪一個更適合做深度學習?
    我們同時用 Keras 和 PyTorch 訓練一個簡單的模型。如果你是深度學習初學者,對有些概念無法完全理解,不要擔心。從現在開始,專注於這兩個框架的代碼樣式,儘量去想像哪個最適合你,使用哪個工具你最舒服,也最容易適應。
  • PyTorch 0.4:完全改變API,官方支持Windows
    特別是,在輸入可以變化的情況下,例如文本這樣的非結構化數據,這非常有用而且高效。主要變化和潛在的重要更新:Tensors 和 Variables已經合併有些操作會返回0維(標量)Tensors棄用了 volatile flag改進:添加了 dtypes,devices和 Numpy 風格的 Tensor 創建函數支持編寫與
  • 圖像分類入門,輕鬆拿下90%準確率|教你用Keras搞Fashion-MNIST
    原作 Margaret Maynard-Reid王小新 編譯自 TensorFlow的Medium量子位 出品 | 公眾號 QbitAI這篇教程會介紹如何用TensorFlow裡的tf.keras函數,對Fashion-MNIST數據集進行圖像分類。
  • 使用Keras構建具有自定義結構和層次圖卷積神經網絡
    讓我們加載數據,創建鄰接矩陣,把特徵矩陣準備好。最後,我們定義一些對神經網絡的訓練有用的參數。正如你可以從上面的代碼中推斷出的那樣,對於每個模型,我們將執行50次迭代,在每次迭代中,我們將隨機選擇一個標記為10%的集合(訓練集),並對模型進行100個epoch的訓練。需要指出的是,本教程的範圍不是訓練CORA數據集上最精確的模型。
  • 手把手教你用 Keras 實現 LSTM 預測英語單詞發音
    出於這個原因,我們將創建一個帶有編碼器 (encoder) 和解碼器 (decoder) 兩個部分的 seq2seq [博客連結](https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html) 模型。
  • 迎來PyTorch,告別 Theano,2017 深度學習框架發展大盤點
    GitHub 地址:https://github.com/pytorch/pytorchTensorFlow 遇強敵,引入動態圖機制等多個更新大家可以把它理解為一個數學表達式的編譯器:用符號式語言定義你想要的結果,該框架會對你的程序進行編譯,來高效運行於 GPU 或 CPU。在過去的很長一段時間內,Theano 都是深度學習開發與研究的行業標準。比起深度學習庫,它更像是一個研究平臺,需要開發者從底層開始做許多工作,來創建自己需要的模型。
  • 雲計算學習:用PyTorch實現一個簡單的分類器
    所以我總結了一下自己當初學習的路線,準備繼續深入鞏固自己的 pytorch 基礎;另一方面,也想從頭整理一個教程,從沒有接觸過 pytorch 開始,到完成一些最新論文裡面的工作。以自己的學習筆記整理為主線,大家可以針對參考。第一篇筆記,我們先完成一個簡單的分類器。
  • TensorFlow 1.9.0-rc0 升級 tf.keras 至 Keras 2.1.6 API
    該版本帶來了不少改進和新特性:Update tf.keras to the Keras 2.1.6 API.tfe.Network is deprecated. Please inherit from tf.keras.Model.
  • Keras官方中文版文檔正式發布了
    Keras 是一個用 Python 編寫的高級神經網絡 API,它能夠以 TensorFlow、CNTK、或者 Theano 作為後端運行。Keras 的開發重點是支持快速的實驗。能夠以最小的時延把你的想法轉換為實驗結果,是做好研究的關鍵。
  • 還不會使用PyTorch框架進行深度學習的小夥伴,看過來
    另一種防止歷史跟蹤的方法是用「torch.no_grad()」方法封裝代碼。你可以將張量「Tensor」和函數「Function」類相連接,構建一個編碼了完整計算歷史的無環圖。張量的「.grad_fn」屬性會引用創建了這個張量的「Function」。如果你要計算導數,可以調用張量的「.backward()」。
  • 《PyTorch中文手冊》來了
    1.1.3 重新介紹 PyTorchPyTorch is an open source machine learning library for Python, based打開 Anaconda Prompt#pytorch 為環境名,這裡創建 python3.6 版。
  • 從小白到入門:用Keras進行圖像基礎分類
    【IT168 資訊】在這篇文章中,將解釋一些在keras中經常需要的常見操作。首先,如何保存模型並使用它們進行預測,從數據集中顯示圖像並從加載系統中圖像並預測其類別。