在上一篇文章中,我们简述了Keras和PyTorch的区别,旨在帮助你选择更适合你需求的框架。现在,我们进行实战。我们将让Keras和PyTorch互相较量以展示他们的优劣。我们使用的问题是:区分异形和铁血战士。

图像分类,是计算机视觉任务之一。由于在大多数情况下从头开始训练很难实施(因为它很需要数据),我们使用在ImageNet上预训练的ResNet-50进行迁移学习。我们尽可能贴合实际地展示概念差异和惯例。同时,我们的代码保持简约,使其清晰、易于阅读和重用。

那么,什么是迁移学习?为什么使用ResNet-50?实际上,很少有人从头开始训练整个卷积网络(使用随机初始化),因为足够大小的数据集相对罕见的。因此,通常在非常大的数据集(例如ImageNet,其包含具有1000个类别的120万个图像)上预训练ConvNet,然后使用ConvNet作为自己任务的初始化或固定特征提取器(出自Andrej Karpathy,CS231n)。

迁移学习是对在给定任务上训练的网络进行微小调整以执行另一个类似任务的过程。在我们的案例中,我们使用经过训练的ResNet-50模型对ImageNet数据集中的图像进行分类。这足以学习很多可能在其他视觉任务中有用的纹理和模式,甚至可以辨别异形大战铁血战士中的异形。这样,我们使用更少的计算能力来取得更好的结果。

在我们的例子中,我们以最简单的方式做到:

保持预训练的卷积层(即,所谓的特征提取器),保持它们的权重不变。

删除原始稠密层,并用我们用于训练的新稠密层替换。

那么,应该选择哪个网络作为特征提取器?

ResNet-50是很流行的ImageNet图像分类模型(AlexNet,VGG,GoogLeNet,Inception,Xception也很流行的模型)。它是一种基于残余连接的50层深度神经网络架构,残连接差是为每层增加修改的连接(注意,是修改)。

我们通过七个步骤完成Alien vs. Predator任务:

准备数据集

导入依赖项

创建数据生成器

创建网络

训练模型

保存并加载模型

对样本测试图像进​​行预测

我们在Jupyter Notebooks(Keras-ResNet50.ipynb,PyTorch-ResNet50.ipynb)中使用Python代码补充了这篇博客文章。这种环境比裸脚本更便于原型设计,因为我们可以逐个单元地执行它并将峰值输出到输出中。

好的,我们走吧!

0.准备数据集

我们通过谷歌搜索“alien”和“predator”来创建数据集。我们保存了JPG缩略图(大约250×250像素)并手动过滤了结果。以下是一些例子:

我们将数据分为两部分:

训练数据(每类347个样本) – 用于训练网络。

验证数据(每类100个样本) – 在训练期间不使用,以检查模型在以前没有看过的数据上的性能。

Keras要求以下列方式将数据集放在文件夹中:

|-- train

|-- alien

|-- predator

|-- validation

|-- alien

|-- predator

如果要查看将数据组织到目录中的过程,可以查看data_prep.ipynb文件。

Kaggle下载数据集:https://www.kaggle.com/pmigdal/alien-vs-predator-images

1.导入依赖项

我们假设你有Python 3.5+,Keras 2.2.2(带有TensorFlow 1.10.1后端)和PyTorch 0.4.1。具体需求可查看requirements.txt文件。

首先,我们需要导入所需的模块。我们将Keras,PyTorch和他们共有的代码(两者都需要)分开。

共有

import numpy as np

import matplotlib.pyplot as plt

from PIL import Image

%matplotlib inline

KERAS

import keras

from keras.preprocessing.image import ImageDataGenerator

from keras.applications import ResNet50

from keras.applications.resnet50 import preprocess_input

from keras import Model, layers

from keras.models import load_model, model_from_json

PYTORCH

import torch

from torchvision import datasets, models, transforms

import torch.nn as nn

from torch.nn import functional as F

import torch.optim as optim

我们可以分别键入keras .__ version__ 和torch .__ version__来检查框架的版本。

2.创建数据生成器

通常,图像不能一次全部加载,因为这样内存会不够。并且,我们希望通过一次处理少量图像来从GPU中受益。因此,我们使用数据生成器分批加载图像(例如,一次32个图像)。每次遍历整个数据集都称为一个训练周期(epoch,或者说一次迭代)。

我们还使用数据生成器进行预处理:我们调整图像大小并将其标准化,以使它们像ResNet-50一样(224 x 224像素,带有缩放的颜色通道)。最后但并非最不重要的是,我们使用数据生成器随机扰动图像:

执行此类更改称为数据增强(data augmentation)。我们用它来告诉神经网络,哪种变化无关紧要。或者,换句话说,我们通过基于原始数据集生成的新图像来获得可能无限大的数据集。

几乎所有的视觉任务都在不同程度上受益于训练的数据增加。在我们的案例中,我们随机剪切,缩放和水平翻转我们的异形和铁血战士。

因此,我们创建生成器的步骤是:

从文件夹加载数据

标准化数据(训练和验证)

数据增强(仅限训练)

KERAS

train_datagen = ImageDataGenerator(

shear_range=10,

zoom_range=0.2,

horizontal_flip=True,

preprocessing_function=preprocess_input)

train_generator = train_datagen.flow_from_directory(

'data/train',

batch_size=32,

class_mode='binary',

target_size=(224,224))

validation_datagen = ImageDataGenerator(

preprocessing_function=preprocess_input)

validation_generator = validation_datagen.flow_from_directory(

'data/validation',

shuffle=False,

class_mode='binary',

target_size=(224,224))

PYTORCH

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],

std=[0.229, 0.224, 0.225])

data_transforms = {

'train':

transforms.Compose([

transforms.Resize((224,224)),

transforms.RandomAffine(0, shear=10, scale=(0.8,1.2)),

transforms.RandomHorizontalFlip(),

transforms.ToTensor(),

normalize]),

'validation':

transforms.Compose([

transforms.Resize((224,224)),

transforms.ToTensor(),

normalize])}

image_datasets = {

'train':

datasets.ImageFolder('data/train', data_transforms['train']),

'validation':

datasets.ImageFolder('data/validation', data_transforms['validation'])}

dataloaders = {

'train':

torch.utils.data.DataLoader(

image_datasets['train'],

batch_size=32,

shuffle=True,

num_workers=4),

'validation':

torch.utils.data.DataLoader(

image_datasets['validation'],

batch_size=32,

shuffle=False,

num_workers=4)}

在Keras中,你可以使用内置的增强和preprocess_input 方法来标准化图像,但你无法控制它们的顺序。在PyTorch中,必须手动标准化图像,但你可以以任何你喜欢的方式安排增强。

还有其他细微差别:例如,Keras默认使用边界像素填充增强图像的其余部分(如上图所示),而PyTorch用黑色。每当一个框架比另一个更好地处理你的任务时,请仔细查看它们是否执行相同的预处理(我几乎可以肯定他们不同)。

3.创建网络

下一步是导入预训练好的ResNet-50模型,这在两种情况下都是轻而易举的。我们保持所有ResNet-50的卷积层不变,仅训练最后两个完全连接(稠密)层。由于我们的分类任务只有2个类,我们需要调整最后一层(ImageNet有上千个)。

也就是说,我们:

加载预训练好的网络,减掉头部并固定权重,

添加自定义稠密层(我们选择128个神经元的隐藏层),

设置优化器和损失函数。

KERAS

conv_base = ResNet50(include_top=False,

weights='imagenet')

for layer in conv_base.layers:

layer.trainable = False

x = conv_base.output

x = layers.GlobalAveragePooling2D()(x)

x = layers.Dense(128, activation='relu')(x)

predictions = layers.Dense(2, activation='softmax')(x)

model = Model(conv_base.input, predictions)

optimizer = keras.optimizers.Adam()

model.compile(loss='sparse_categorical_crossentropy',

optimizer=optimizer,

metrics=['accuracy'])

PYTORCH

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model = models.resnet50(pretrained=True).to(device)

for param in model.parameters():

param.requires_grad = False

model.fc = nn.Sequential(

nn.Linear(2048, 128),

nn.ReLU(inplace=True),

nn.Linear(128, 2)).to(device)

criterion = nn.CrossEntropyLoss()

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

我们很容易从Keras和PyTorch加载ResNet-50。他们还提供了其他许多有名的预训练架构。那么,它们有什么区别?

在Keras中,我们可以仅导入特征提取层,不加载外来数据(include_top = False)。然后,我们使用基本模型的输入和输出以功能性的方式创建模型。然后我们使用 model.compile(…)将损失函数,优化器和其他指标放入其中。

在PyTorch中,模型是一个Python对象。在models.resnet50中,稠密层存储在model.fc属性中。我们重写它们。损失函数和优化器是单独的对象。对于优化器,我们需要显式传递我们希望它更新的参数列表。

在PyTorch中,我们应该使用.to(device)方法显式地指定要加载到GPU的内容。每当我们打算在GPU上放置一个对象时,我们都必须编写它。

冻结层的工作方式与此类似。然而,在 Keras 的批量标准化层中,它被破坏了 (截至当前版本,详见http://blog.datumbox.com/the-batch-normalization-layer-of-keras-is-broken/)。也就是说,无论如何都会修改一些层,即使 trainable = False。

Keras和PyTorch以不同的方式处理log-loss。

在Keras中,网络预测概率(具有内置的softmax函数),其内置成本函数假设它们使用概率工作。

在PyTorch中我们更加自由,但首选的方法是返回logits。这是出于数值原因,执行softmax然后log-loss意味着执行多余的log(exp(x))操作。因此,我们使用LogSoftmax(和NLLLoss)代替使用softmax,或者将它们组合成一个nn.CrossEntropyLoss 损失函数。

4.训练模型

我们继续进行最重要的一步 – 模型训练。我们需要传递数据,计算损失函数并相应地修改网络权重。虽然Keras和PyTorch在数据增强方面已经存在一些差异,但代码长度差不多。但在训练这一步,差的就很多了。

在这里,我们:

训练模型,

测量损失函数(log-loss)和训练和验证集的准确性。

KERAS

history = model.fit_generator(

generator=train_generator,

epochs=3,

validation_data=validation_generator)

PYTORCH

def train_model(model, criterion, optimizer, num_epochs=3):

for epoch in range(num_epochs):

print('Epoch {}/{}'.format(epoch+1, num_epochs))

print('-' * 10)

for phase in ['train', 'validation']:

if phase == 'train':

model.train()

else:

model.eval()

running_loss = 0.0

running_corrects = 0

for inputs, labels in dataloaders[phase]:

inputs = inputs.to(device)

labels = labels.to(device)

outputs = model(inputs)

loss = criterion(outputs, labels)

if phase == 'train':

optimizer.zero_grad()

loss.backward()

optimizer.step()

_, preds = torch.max(outputs, 1)

running_loss += loss.item() * inputs.size(0)

running_corrects += torch.sum(preds == labels.data)

epoch_loss = running_loss / len(image_datasets[phase])

epoch_acc = running_corrects.double() / len(image_datasets[phase])

print('{} loss: {:.4f}, acc: {:.4f}'.format(phase,

epoch_loss,

epoch_acc))

return model

model_trained = train_model(model, criterion, optimizer, num_epochs=3)

在Keras中,model.fit_generator执行训练……然后就没了!在Keras的训练就是这么简单。正如你在notebook中所看到的,Keras还为我们提供了进度条和计时功能。但如果你想做任何非标准的事情,那你就有的头疼了。

PyTorch与此截然不同。这里一切都是明确的。你需要更多行代码来构建基本训练,但你可以随意更改和自定义你想要的所有内容。

让我们剖析下PyTorch训练代码。我们有嵌套循环,迭代:

迭代次数,

训练和验证阶段,

批次。

epoch循环很好理解,只是重复里面的代码。训练和验证阶段:

一些特殊的层,如批量标准化(出现在ResNet-50中)和dropout(在ResNet-50中不存在),在训练和验证期间的工作方式不同。我们分别通过model.train()和model.eval()设置它们的行为。

当然,我们使用不同的图像进行训练和验证。

最重要但也很容易理解的事情:我们只在训练期间训练网络。magic命令optimizer.zero_grad(),loss.backward()和 optimizer.step()(按此顺序)完成工作。如果你理解什么是反向传播,你就会欣赏它们的优雅。

我们负责计算迭代的损失并打印。

5.保存并加载模型

保存

一旦我们的网络经过训练,通常这需要很高的计算和时间成本,最好将其保存以备以后使用。一般来说,有两种类型保存:

将整个模型结构和训练权重(以及优化器状态)保存到文件中,

将训练过的权重保存到文件中(将模型架构保留在代码中)。

你可以随意选择。在这里,我们保存模型。

KERAS

# architecture and weights to HDF5

model.save('models/keras/model.h5')

# architecture to JSON, weights to HDF5

model.save_weights('models/keras/weights.h5')

with open('models/keras/architecture.json', 'w') as f:

f.write(model.to_json())

PYTORCH

torch.save(model_trained.state_dict(),'models/pytorch/weights.h5')

两个框架中都有一行代码就足够了。在Keras中,可以将所有内容保存到HDF5文件,或将权重保存到HDF5,并将架构保存到可读的json文件中。另外,你可以加载模型并在浏览器中运行它。

目前,PyTorch创建者建议仅保存权重。他们不鼓励保存整个模型,因为API仍在不断发展。

加载

加载模型和保存一样简单。你需要记住你选择的保存方法和文件路径。

KERAS

# architecture and weights from HDF5

model = load_model('models/keras/model.h5')

# architecture from JSON, weights from HDF5

with open('models/keras/architecture.json') as f:

model = model_from_json(f.read())

model.load_weights('models/keras/weights.h5')

PYTORCH

model = models.resnet50(pretrained=False).to(device)

model.fc = nn.Sequential(

nn.Linear(2048, 128),

nn.ReLU(inplace=True),

nn.Linear(128, 2)).to(device)

model.load_state_dict(torch.load('models/pytorch/weights.h5'))

在Keras中,我们可以从JSON文件加载模型,而不是在Python中创建它(至少在我们不使用自定义层时不需要这样)。这种序列化方便了转换模型。

PyTorch可以使用任何Python代码。所以我们必须在Python中重新创建一个模型。在两个框架中加载模型权重比较类似。

6.对测试样本图像进​​行预测

为了公平地检查我们的解决方案的质量,我们要求模型预测未用于训练的图像中怪物的类型。我们可以使用验证集或者任何其他图像。

在这里,我们:

加载和预处理测试图像

预测图像类别

显示图像和预测

共有

validation_img_paths = ["data/validation/alien/11.jpg",

"data/validation/alien/22.jpg",

"data/validation/predator/33.jpg"]

img_list = [Image.open(img_path) for img_path in validation_img_paths]

KERAS

validation_batch = np.stack([preprocess_input(np.array(img.resize((img_size, img_size))))

for img in img_list])

pred_probs = model.predict(validation_batch)

PYTORCH

validation_batch = torch.stack([data_transforms['validation'](img).to(device)

for img in img_list])

pred_logits_tensor = loaded_model(validation_batch)

pred_probs = F.softmax(pred_logits_tensor, dim=1).cpu().data.numpy()

共有

fig, axs = plt.subplots(1, len(img_list), figsize=(20, 5))

for i, img in enumerate(img_list):

ax = axs[i]

ax.axis('off')

ax.set_title("{:.0f}% Alien, {:.0f}% Predator".format(100*pred_probs[i,0],

100*pred_probs[i,1]))

ax.imshow(img)

像训练一样,预测也分批进行(这里我们一批3个,也可以每批1个)。在Keras和PyTorch中,我们需要加载和预处理数据。新手常见的错误是忘记了预处理步骤(包括颜色缩放)。也许方法仍然有效,但会导致糟糕的预测(因为它能有效地看到相同的形状,但不能有效看到不同的颜色和对比度)。

在PyTorch中还有两个步骤,因为我们需要:

将logits转换为概率,

将数据传输到CPU并转换为NumPy(当我们忘记此步骤时,错误消息会很明白的告诉你)。

下面就是我们得到的:

成功了!你也可以使用其他图像。如果你无法想出任何其他(或任何人),可以尝试使用你同事的照片。

结论

现在你看到了,Keras和PyTorch在如何定义,修改,训练,评估和导出标准深度学习模型方面的差异。有些部分,它纯粹是针对不同的API约定,而其他部分,则涉及抽象级别之间的基本差异。

Keras在更高级别的抽象上运行。它更加即插即用,通常更简洁,但这是以灵活性为代价的。

PyTorch提供更明确和详细的代码。在大多数情况下,它意味着可调试和灵活的代码,只需多费一点时间。然而,PyTorch的训练更加冗长,但有时这会提供很大的灵活性。

github:https://github.com/deepsense-ai/Keras-PyTorch-AvP-transfer-learning

Kaggle kernels:https://www.kaggle.com/pmigdal/alien-vs-predator-images/kernels

Neptune:https://app.neptune.ml/deepsense-ai/Keras-vs-PyTorch

pytorch和python的区别_Keras和PyTorch的视觉识别与迁移学习对比相关推荐

  1. c语言和python的区别

    c语言和python的区别 1.语言类型不同. Python是一种动态类型语言,又是强类型语言.它们确定一个变量的类型是在您第一次给它赋值的时候.C 是静态类型语言,一种在编译期间就确定数据类型的语言 ...

  2. pytorch和Numpy的区别以及相互转换

    pytorch 数值转numpy int(x1.cpu().data.numpy()) pytorch和Numpy的区别以及相互转换 2018年01月17日 19:17:54 阅读数:2654

  3. MINIST深度学习识别:python全连接神经网络和pytorch LeNet CNN网络训练实现及比较(三)...

    版权声明:本文为博主原创文章,欢迎转载,并请注明出处.联系方式:460356155@qq.com 在前两篇文章MINIST深度学习识别:python全连接神经网络和pytorch LeNet CNN网 ...

  4. [PyTorch] 基于Python和PyTorch的线性拟合

    用神经网络实现线性拟合,代码源自<深度学习入门之pytorch>,本人根据新版本的PyTorch做了挺大的修改. <深度学习入门之pytorch>的使用体验 关于这个书好不好, ...

  5. PyTorch 和 TensorFlow的区别

    自 2012 年深度学习重新获得重视以来,许多机器学习框架便争相成为研究人员和行业从业人员的新宠.从早期的学术成果 Caffe 和 Theano ,到背靠庞大工业支持的 PyTorch 和 Tenso ...

  6. 01 Pytorch和CUDA对应的版本及Pytorch和Python对应的版本及Python与Anaconda的对应关系

    官方推荐的cuda版本为10.2和11.3,这两种 cuda 支持大多数的 pytorch 版本. 以下是Pytorch和CUDA对应的版本  CUDA 环境  PyTorch 版本   9.2 0. ...

  7. python出现THCudaCheck FAIL file=/pytorch/aten/src/THC/ThCGeneral.cpp line=405 error=11 : 情况

    python出现THCudaCheck FAIL file=/pytorch/aten/src/THC/ThCGeneral.cpp line=405 error=11 : invalid argum ...

  8. 在Python中使用LSTM和PyTorch进行时间序列预测

    全文链接:http://tecdat.cn/?p=8145 顾名思义,时间序列数据是一种随时间变化的数据类型.例如,24小时内的温度,一个月内各种产品的价格,一年中特定公司的股票价格(点击文末&quo ...

  9. Setting up Pytorch with Python 3 on Ubuntu(Source code compilation)

    1.安装yaml依赖:sudo apt-get install python-yaml python3-yaml 2.git clone pytorch 源码: git clone https://g ...

  10. pytorch与keras_Keras vs PyTorch:如何通过迁移学习区分外星人与掠食者

    pytorch与keras by Patryk Miziuła 通过PatrykMiziuła Keras vs PyTorch:如何通过迁移学习区分外星人与掠食者 (Keras vs PyTorch ...

最新文章

  1. 深入理解JavaScript系列(2):揭秘命名函数表达式(转)
  2. 曲阜有学计算机的学校吗,曲阜职业中专计算机专业课程上几年
  3. ES6:Reflect
  4. 区别于传统低效标注,两种基于自然语言解释的数据增强方法
  5. mysql分页查询_4种MySQL分页查询优化的方法,你知道几个?
  6. 个人博客作业第三周--必应词典分析
  7. rpm包安装mysql数据库
  8. C++(STL):33---hash_set、hash_map、hash_multiset、hash_multimap源码剖析
  9. 1.MySQL数据库的介绍
  10. JavaScript将iframe中控件的值传到主页面控件中
  11. django-模态框编辑学生
  12. so库调用java函数_linux下so动态库调用主程序函数
  13. 小米9尴尬了!红米K20搭载骁龙855或只卖2599元
  14. 禁止迅雷:迅雷服务器地址大全+ISA计算机集(xml)
  15. linux暂时不能域名解析,Kali Linux中暂时不能解析域名
  16. Java设计模式(7)——装饰者模式
  17. 高等代数——大学高等代数课程创新教材(丘维声)——2.2笔记+习题
  18. pptx文件怎么打开(ppt兼容包下载)
  19. pdfFactory Pro的打印首选项设置
  20. 【Python学习】最新版pyecharts之绘制Map地图

热门文章

  1. [转载] 过 DNF TP 驱动保护
  2. KGB成功破解特朗普的秘密消息
  3. 打砖块游戏-第12届蓝桥杯Scratch省赛1真题第5题
  4. 小水智能-智能楼宇智慧建筑3d可视化,让钢铁水泥也可以沟通交流
  5. 陈年再创业:B2C必须标准化 VANCL只做男装
  6. 华东交通大学计算机考研资料汇总
  7. 会员等级图标js脚本
  8. 嵌入式系统上电,程序的运行过程
  9. WPF开发之dll文件创建与调用
  10. 蓝桥杯试题 基础练习 十六进制转八进制