1. 程式人生 > 實用技巧 >Keras 和 PyTorch 的對比選擇

Keras 和 PyTorch 的對比選擇

原文:https://medium.com/@karan_jakhar/keras-vs-pytorch-dilemma-dc434e5b5ae0

作者:Karan Jakhar

前言

上一篇2020年計算機視覺學習指南介紹了兩種深度學習框架--Keras 和 PyTorch ,這篇文章的作者就對這兩個框架進行了對比,分別通過實現一個簡單的模型來對比兩個不同的程式碼風格,最後還給出了他的個人建議。

當你決定開始學習深度學習,那麼應該選擇使用什麼工具呢?目前有很多深度學習的框架或者庫,但本文會對比兩個框架,Keras 和 PyTorch ,這是兩個非常好開始使用的框架,並且它們都有一個很低的學習曲線,初學者可以很快就學會它們,因此在本文,我將分享一個辦法來解決如何選擇其中一個框架進行使用。

最好的辦法就是檢視兩個框架各自的程式碼風格。設計任何方案的前提和最重要的事情就是你的工具,當你開始一個專案前必須安裝配置好你的工具,並且一旦開始專案後,就不應該更改時用的工具。它會影響到你的生產力。作為一個初學者,你應該儘量嘗試不同的工具,並且找到合適你的,但如果你正在參加一個非常正式的專案工作,那麼這些事情都應該提早計劃好。

每天都會有新的框架和工具面世,對你最好的工具應該是在個性化和抽象做好平衡的,它應該可以同步你的思考和程式碼風格,那麼如何找到這樣合適的工具呢,答案就是你需要嘗試不同的工具。

接下來,讓我們分別用 Keras 和 PyTorch 訓練一個簡單的模型吧。如果你是深度學習的初學者,那麼不要擔心理解不了某些名詞概念,目前你只需要關注這兩個框架的程式碼風格,並思考哪個才是最合適你的,也就是讓你感覺更舒適並且更容易上手的。

這兩個框架的主要不同點是 PyTorch 預設是eager模式,而 Keras 是在 TensorFlow 和其他框架的基礎上進行工作,但目前主要是基於 TensorFlow 框架的,因此其預設是圖(graph)模式。當然,最新版本的 TensorFlow 也提供了和 PyTorch 一樣的eager模式。如果你對 NumPy 很熟悉的話,你可以把 PyTorch 看作是有 GPU 支援的 NumPy 。此外,也有不少類似 Keras 一樣的第三方庫作為高階 API 介面,它們使用 PyTorch 作為後端支援,比如Fastai(提供了免費的很好的課程)、Lightning,Ignite等等。也可以去了解這些框架,如果你發現它們很有趣,那你就多了一個理由使用 PyTorch 。

這兩種框架都有不同的方法來實現一個模型。這裡都分別選擇了一種簡單的實現方式。下面是分別在谷歌的 Colab 上實現的程式碼的連結,開啟連結並執行程式碼,這更加有助於找到更合適你的框架:

Keras:https://colab.research.google.com/drive/1QH6VOY_uOqZ6wjxP0K8anBAXmI0AwQCm?usp=sharing#forceEdit=true&sandboxMode=true

PyTorch:https://colab.research.google.com/drive/1irYr0byhK6XZrImiY4nt9wX0fRp3c9mx?usp=sharing#scrollTo=FoKO0mEScvXi&forceEdit=true&sandboxMode=true

本文並不會介紹太細節的東西,因為我們的目標只是對兩個框架的程式碼結構和風格進行檢視和了解。


基於 Keras 的模型實現

下面是實現數字識別的程式碼實現。程式碼非常容易理解,你最好在 colab 中檢視並且進行實驗,至少要開始執行起來。

from keras.datasets import mnist
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D

img_rows, img_cols = 28, 28
num_classes = 10
batch_size = 128
epochs = 10

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
x_train = x_train/255
x_test  = x_test/255
y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)

在 Keras 中有一些作為樣例的資料集,其中一個就是 MNIST 手寫數字資料集,上述程式碼主要是實現載入資料集的功能,圖片是 NumPy 的陣列格式。另外,上述程式碼也做了一點的影象處理來將資料可以應用到模型中。

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
     activation='relu',
     input_shape=(img_rows, img_cols, 1)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
      optimizer='adam',
      metrics=['accuracy'])

上述程式碼就是模型的程式碼實現。在 Keras(TensorFlow) 中,我們需要先定義想使用的所有東西,然後它們會只執行一次。我們不能對它們進行實驗,但是在 PyTorch 中是可以做到的。

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))

score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
model.save("test_model.h5")

# load the model
from keras.models import load_model
model = load_model("test_model.h5")

# predict digit
prediction = model.predict(gray)
print(prediction.argmax())

上述程式碼就是訓練和驗證模型,可以使用save()方法來儲存模型,然後通過load_model()方法來載入儲存的模型檔案,predict()方法是用於對測試資料進行預測得到預測結果。

這就是使用 Keras 簡單實現一個模型的概覽,下面看看 PyTorch 是怎麼實現模型的吧。


基於 PyTorch 的模型實現

研究者主要用 PyTorch ,因為它的靈活性以及偏實驗的程式碼風格,這包括可以對 PyTorch 的一切都進行修改調整,對 也就是可以完全控制一切,進行實驗也是非常容易。在 PyTorch 中,不需要先定義所有的事情再執行,對每個單獨的步驟的測試都非常容易。因此,它比 Keras 更容易除錯。

下面也是利用 PyTorch 實現一個簡單的數字識別模型。

import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

n_epochs = 3
batch_size_train = 64
batch_size_test = 1000
learning_rate = 0.01
momentum = 0.5
log_interval = 10

random_seed = 1
torch.backends.cudnn.enabled = False
torch.manual_seed(random_seed)

上述程式碼主要是匯入需要的庫以及定義了一些變數,這些變數如n_epochs, momentum等都是必須設定的超引數,但這裡不會詳細展開說明,因為我們也說過本文的目標是理解框架的程式碼結構和風格。

train_loader = torch.utils.data.DataLoader(
  torchvision.datasets.MNIST('/files/', train=True, download=True,
                             transform=torchvision.transforms.Compose([
                               torchvision.transforms.ToTensor(),
                               torchvision.transforms.Normalize(
                                 (0.1307,), (0.3081,))
                             ])),
  batch_size=batch_size_train, shuffle=True)

test_loader = torch.utils.data.DataLoader(
  torchvision.datasets.MNIST('/files/', train=False, download=True,
                             transform=torchvision.transforms.Compose([
                               torchvision.transforms.ToTensor(),
                               torchvision.transforms.Normalize(
                                 (0.1307,), (0.3081,))
                             ])),
  batch_size=batch_size_test, shuffle=True)

examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)

example_data.shape

這段程式碼則是聲明瞭一個數據載入器用於載入訓練資料集進行訓練和測試。資料集有多種下載資料的方法,這和框架沒有關係。當然上面這段程式碼對於深度學習的初學者可能是有些複雜了。

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x)

接下來這段程式碼就是定義模型。這是一個很通用的建立一個網路模型的方法,定義一個類繼承nn.Moduleforward()方法是實現網路的前向傳播。PyTorch 的實現是非常直接,並且可以根據需要進行修改。

network = Net()
optimizer = optim.SGD(network.parameters(), lr=learning_rate,
                      momentum=momentum)
train_losses = []
train_counter = []
test_losses = []
test_counter = [i*len(train_loader.dataset) for i in range(n_epochs + 1)]

def train(epoch):
  network.train()
  for batch_idx, (data, target) in enumerate(train_loader):
    optimizer.zero_grad()
    output = network(data)
    loss = F.nll_loss(output, target)
    loss.backward()
    optimizer.step()
    if batch_idx % log_interval == 0:
      print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
        epoch, batch_idx * len(data), len(train_loader.dataset),
        100. * batch_idx / len(train_loader), loss.item()))
      train_losses.append(loss.item())
      train_counter.append(
        (batch_idx*64) + ((epoch-1)*len(train_loader.dataset)))
      torch.save(network.state_dict(), 'model.pth')
      torch.save(optimizer.state_dict(), 'optimizer.pth')

def test():
  network.eval()
  test_loss = 0
  correct = 0
  with torch.no_grad():
    for data, target in test_loader:
      output = network(data)
      test_loss += F.nll_loss(output, target, size_average=False).item()
      pred = output.data.max(1, keepdim=True)[1]
      correct += pred.eq(target.data.view_as(pred)).sum()
  test_loss /= len(test_loader.dataset)
  test_losses.append(test_loss)
  print('\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
    test_loss, correct, len(test_loader.dataset),
    100. * correct / len(test_loader.dataset)))

test()
for epoch in range(1, n_epochs + 1):
  train(epoch)
  test()

接下來這段程式碼,我們分別定義了訓練和測試函式,train()test()。在 Keras 中直接呼叫fit()函式,然後所有事情都給我們實現好了,但是在 PyTorch 中我們需要手動實現這些步驟。當然,在一些高階 API 庫,比如Fastai裡將這部分也變得很簡單,減少了需要的程式碼量。

#loading the model
continued_network = Net()
continued_optimizer = optim.SGD(network.parameters(), lr=learning_rate,
                                momentum=momentum)
network_state_dict = torch.load('model.pth')
continued_network.load_state_dict(network_state_dict)

optimizer_state_dict = torch.load('optimizer.pth')
continued_optimizer.load_state_dict(optimizer_state_dict)

最後就是儲存和載入模型用於再次訓練或者進行預測的程式碼。PyTorch 的模型檔案通常是以pt或者pth為字尾名。


個人的建議

當你開始學習一個模型,並且理解它的理念後,從一個框架轉移到另一個並不困難,這隻需要幾天的工作。我的建議就是兩個框架都要學習,但不需要學得非常深入。你應該選擇一個框架並開始實現你的模型程式碼,但同時也需要對另一個框架有所瞭解。這有助於你閱讀用另一個框架實現的模型程式碼。你不應該被框架所約束,它們都是很好的框架。

我最初開始使用的是 Keras,但現在我在工作中使用 PyTorch,因為它可以更好的進行實驗。我喜歡 PyTorch 的 python 風格。所以首先使用一個你覺得更適合你的框架,然後同時也嘗試去學習另一個框架,如果學習後發現它使用更加舒適,那就改為使用它,並且這兩個框架的核心概念都是非常相似的,兩者的相互轉換都非常容易。

最後祝你在深度學習之旅中好運。你應該更專注演算法的理論概念以及它們在現實生活中如何使用和實現的。

最後再次給出兩份模型程式碼實現的 colab 連結: