AI 技术讲座精选:菜鸟学深度学习(二)

2018-04-26 14:28:39 浏览数 (1)

【AI100 导读】如何才能创建出自己的卷积神经网络呢?在本篇文章中我们会一起来探讨一下这个问题。我们将会继续处理在该系列第一部分谈到的图像分割问题。

可用来创建卷积神经网络的深度学习库有很多。我们就选 Keras 和 Tensorflow。那么第一个要思考的问题就是:

为什么要选这两个?只选 Tensorflow 不行吗?

机器学习库里有很多的深度学习库。Tensorflow、Theano、PyTorch、Caffe 和 Torch 是少数几个还不错的学习库。这里想大大赞一下 Soumith Chintala 和他的团队研发的 PyTorch,创建的深度学习库真的是很棒!大有占领全球市场的希望。

PyTorch计划接管世界:P Andrej Karpathy对Tensorflow抱有很高的期望

这些都是低端学习库,涉及矩阵计算中 GPU 或者 CPU 的升级和优化。所以用刚刚提到的低端学习库来创建网络会很有挑战性。Keras 是一个高级的学习库,它能够帮助你建立神经层,将计算的复杂过程抽象化。Theano 或 Tensorflow 可以作为后端和 Keras 一起使用。我会选择 Tensorflow 作为后端,因为其有更强大的社区支持。

安装

现在让我们动手开始吧!不要想着去哪能完成这个事。一般的系统在训练数据集时都会崩溃。我们首先需要 AWS 服务器。如果你有一台配置惊人的游戏主机,那就直接设置本地服务器就好了。我们要用到 AWS 里的 g2.2xLarge 系统。它有26个 GPU 核,每小时只要0.65美元。为什么我们选 AWS 呢?因为这是云计算中价格最便宜的 GPU 系统了,比我们使用的大多数硬件好很多。接着就是选择用哪种操作系统了,毫无疑问用 Ubuntu 16.04 LTS 最合理。但是等一下,我们要用到安装了很多工具的预焙 AMI, 这样的话就大大减少了更新项。在 AWS 里搜索深度学习 AMI。当然还有很多其他深度学习 AMI 也可以去搜索看看。我们需要 AMI 里至少安装了 Python 2.7 和 Tensorflow。

选择完实例类型和 AMI 之后,去创建一个键。如果你预先植入了一个键,那就用那个就好了。在本文中,我们将会创建一个新的键。建设该键的名字叫 deepkey.pem。下载之后,把它存在一个安全的地方。运行实例。通常创建一个实例要花5-10分钟的时间。在这期间,把这个键的权限调到400,否则 ssh 不会让你登录。

chmod 400 ~/deepkey.pem

接着去 EC2 实例查看列表。选择已经创建的实例。复制 AWS 实例的公共 DNS,它的样子大概是这样的:ec2–52–24–183–62.us-west-2.compute.amazonaws.com#

# Next lets login to the system

ssh ec2-user@ec2–52–24–183–62.us-west-2.compute.amazonaws.com -i ~/deepkey.pem

# The AMI might be a bit backdated, so it's always better to update

sudo yum update

# Install pip to get Keras

sudo yum install python-pip

# Upgrade the pip master that got installed

sudo /usr/local/bin/pip install — upgrade pip

# Install Keras

sudo /usr/local/bin/pip install keras

当 Keras 和 Theano 在默认情况下完成基本配置后,我们就要使用 Tensorflow 了。所以让我们来修改一下。打开 ~/.keras/keras.conf,如下图所示更新。文件应该如下所示。

{ “image_dim_ordering”: “tf”, “epsilon”: 1e-07, “floatx”: “float32”, “backend”: “tensorflow”

}

我希望你跟上了所有的步骤,没犯什么错误。现在就来测试下我们的安装。把 python 打开,导入 keras 进行测试,测试结果应如下所示。

现在你已经安装了 Python、Tensorflow 和 Keras。AMI 预先还安装了 Theano和其他一些东西,但是我们不会用到它们。不要费心去卸载它们,因为它们一点都不碍事。安装完了之后,我们就来看看编码了。

不要在安装上浪费时间,将时间花在学习过程和执行过程上。

我们要来训练能够把 Kaggle 里面猫狗区分开来的网络。在这之前,我们要开始写一个简单的模型。这会帮助你了解 Keras 是怎么运行的。我会从编码开始,你可能注意到了,在每行编码的前面都有些评论。这些评论的作用是解释一下每行编码到底在写些什么。为了运行这些编码,你可以用自己下载的猫狗数据集,也可以从 Kaggle 上下载数据集。但是你必须要注册,加入 Kaggle 比赛,才能从 Kaggle 上下载数据样本。这是 Kaggle 的链接:https://www.kaggle.com/c/dogs-vs-cats/data

from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential from keras.layers import Convolution2D, MaxPooling2D from keras.layers import Activation, Dropout, Flatten, Dense # expected image size

img_width, img_height = 150, 150

# folder containing the images on which

# the network will train. The train folder # has two sub folders, dogs and cats.

train_data_dir = 'data/train'

# folder containing the validation samples

# folder structure is same as the training folder

validation_data_dir = 'data/validation'

# how many images to be considered for training

train_samples = 2000

# how many images to be used for validation

validation_samples = 800

# how many runs will the network make

# over the training set before starting on # validation

epoch = 50

# ** Model Begins **

model = Sequential() model.add(Convolution2D(32, 3, 3, input_shape=(3, img_width, img_height))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(32, 3, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(64, 3, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(64)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(1)) model.add(Activation('sigmoid'))

# ** Model Ends **

model.compile(loss='binary_crossentropy',

optimizer='rmsprop', metrics=['accuracy']) # this is the augmentation configuration we will use for training # we are generating a lot of transformed images so that the model # can handle variety in the real world scenario train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) # this is the augmentation configuration we will use for testing: # only rescaling

test_datagen = ImageDataGenerator(rescale=1./255)

# this section is actually taking images from the folder

# and passing on to the ImageGenerator which then # creates a lot of transformed versions train_generator = train_datagen.flow_from_directory( train_data_dir, target_size=(img_width, img_height), batch_size=32, class_mode='binary') validation_generator = test_datagen.flow_from_directory( validation_data_dir, target_size=(img_width, img_height), batch_size=32,

class_mode='binary')

# this is where the actual processing happens

# it will take some time to run this step. model.fit_generator( train_generator, samples_per_epoch=train_samples, nb_epoch=epoch, validation_data=validation_generator, nb_val_samples=validation_samples)

model.save_weights('trial.h5')

编码都是自解的。用其他模型来替换“ModelBeings”和“ModelEnds”之间的那一部分。你需要自己的分类编码。我会和你们一起编码。首先,你要导入 Keras 依赖包。接着你要定义可进入网络的图像维度。然后你需要告诉编码图像集的位置。这样一来,不仅训练了数据集也激活了数据集。最后是建模过程,从模型开始一直到模型结束。我不会深入讲解模型,因为其为 VGGNet 标准执行。关于网络机构的细节可以在下篇 arXiv 论文中看到。

Very Deep Convolutional Networks for Large-Scale Image Recognition K. Simonyan, A. Zisserman

arXiv:1409.1556

编码中,数据不会有什么迁移。你需要剪切、拉伸、倾斜数据集,这样网络才不会训练过度。你要创建生成器,这样代码就能从指定的文件夹中读取图像。运行过程开始后,系统会根据指定的迭代次数进行训练和激活。最后我们会保存这些权重,这样今后使用的时候就不用再把网络训练一遍了。

以上模型非常简单,也是为了让我的讲解更加浅显易懂。猫狗分类可能不会非常成功,因为我们的数据有限。所以我们现在来进行迁移学习。在迁移学习中,我们会使用训练过的模型解决语句相似的问题。我们要重新使用训练后的权重来解决不同语句。我们用先前用图片训练过的模型去为不同的事物分类。为什么能这样做呢?因为我们用到的模型之前训练过,进行过图像分类,所以其内部深层就能永远进行分类。这些深层能够探测边缘和曲线。这就是迁移学习该术语的来源。把一个问题语句迁移学习到另外一个中。模型的运行情况很可能本身就不错,但我们还可以让它更完善。接着我们训练顶层。这些层其实和实际分类元素相关。我们在训练的数据集上训练层。我们可以把这个数据叫做领域特定。这能够帮助我们理解网络,确切明白我们想给什么分类。所以编码如下所示:

import os import h5py import numpy as np from keras.preprocessing.image import ImageDataGenerator from keras import optimizers from keras.models import Sequential from keras.layers import Convolution2D, MaxPooling2D, ZeroPadding2D from keras.layers import Activation, Dropout, Flatten, Dense # path to the model weights files. weights_path = 'vgg16_weights.h5' top_model_weights_path = 'fc_model.h5' # dimensions of our images. img_width, img_height = 150, 150 train_data_dir = 'data/train' validation_data_dir = 'data/validation' nb_train_samples = 2000 nb_validation_samples = 800 nb_epoch = 50 # build the VGG16 network model = Sequential() model.add(ZeroPadding2D((1, 1), input_shape=(3, img_width, img_height))) model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_2')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(128, 3, 3, activation='relu', name='conv2_1')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(128, 3, 3, activation='relu', name='conv2_2')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_1')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_2')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_3')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_1')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_2')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_3')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_1')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_2')) model.add(ZeroPadding2D((1, 1))) model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_3')) model.add(MaxPooling2D((2, 2), strides=(2, 2))) # load the weights of the VGG16 networks # (trained on ImageNet, won the ILSVRC competition in 2014) # note: when there is a complete match between your model definition # and your weight savefile, you can simply call model.load_weights(filename) assert os.path.exists(weights_path), 'Model weights not found (see "weights_path" variable in script).' f = h5py.File(weights_path) for k in range(f.attrs['nb_layers']): if k >= len(model.layers): # we don't look at the last (fully-connected) layers in the savefile break g = f['layer_{}'.format(k)] weights = [g['param_{}'.format(p)] for p in range(g.attrs['nb_params'])] model.layers[k].set_weights(weights) f.close() print('Model loaded.') # build a classifier model to put on top of the convolutional model top_model = Sequential() top_model.add(Flatten(input_shape=model.output_shape[1:])) top_model.add(Dense(256, activation='relu')) top_model.add(Dropout(0.5)) top_model.add(Dense(1, activation='sigmoid')) # note that it is necessary to start with a fully-trained # classifier, including the top classifier, # in order to successfully do fine-tuning top_model.load_weights(top_model_weights_path) # add the model on top of the convolutional base model.add(top_model) # set the first 25 layers (up to the last conv block) # to non-trainable (weights will not be updated) for layer in model.layers[:25]: layer.trainable = False # compile the model with a SGD/momentum optimizer # and a very slow learning rate. model.compile(loss='binary_crossentropy', optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), metrics=['accuracy']) # prepare data augmentation configuration train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_data_dir, target_size=(img_height, img_width), batch_size=32, class_mode='binary') validation_generator = test_datagen.flow_from_directory( validation_data_dir, target_size=(img_height, img_width), batch_size=32, class_mode='binary') # fine-tune the model model.fit_generator( train_generator, samples_per_epoch=nb_train_samples, nb_epoch=nb_epoch, validation_data=validation_generator,

nb_val_samples=nb_validation_samples)

你可以在我的 Github gist 中获取 VGG16 的权重,你也可以在你的数据集中运行这个代码以获取 fc 模型权重文件,同时你还可以在分享的 VGG16 链接中获得一样的权重集。可以通过改变迭代次数的方法获得更好的迁移学习,但是次数太多可能会导致过度拟合。我已经用这个技术进行了多次实践,其中一次就是来区分处方和非处方药物。我们采用的区分药物的模型,正是在 ImageNet 上训练的用于区分猫狗的模型。我希望你们能够把这些都用在实际操作中。

本文作者 Debarko De 是一名计算机科学工程师,目前在 Practo 工作。在这之前曾在 Facebook 上班,先后从事平台游戏和手机游戏的工作。

本文由 AI100 编译,转载需得到本公众号同意。


编译:AI100

原文链接:https://hackernoon.com/deep-learning-for-noobs-part-2-43d5098e61f6


0 人点赞