原文链接:https://hackernoon.com/deep-learning-for-noobs-part-2-43d5098e61f6
如何才能创建出自己的卷积神经网络呢?在本篇文章中我们会一起来探讨一下这个问题。我们将会继续处理在该系列第一部分谈到的图像分割问题。
可用来创建卷积神经网络的深度学习库有很多。我们就选 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。
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
fromkeras.preprocessing.imageimportImageDataGenerator
fromkeras.modelsimportSequential
fromkeras.layersimportConvolution2D,MaxPooling2D
fromkeras.layersimportActivation,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 论文中看到。
VeryDeepConvolutionalNetworksforLarge-ScaleImageRecognition
K.Simonyan,A.Zisserman
arXiv:1409.1556
编码中,数据不会有什么迁移。你需要剪切、拉伸、倾斜数据集,这样网络才不会训练过度。你要创建生成器,这样代码就能从指定的文件夹中读取图像。运行过程开始后,系统会根据指定的迭代次数进行训练和激活。最后我们会保存这些权重,这样今后使用的时候就不用再把网络训练一遍了。
以上模型非常简单,也是为了让我的讲解更加浅显易懂。猫狗分类可能不会非常成功,因为我们的数据有限。所以我们现在来进行迁移学习。在迁移学习中,我们会使用训练过的模型解决语句相似的问题。我们要重新使用训练后的权重来解决不同语句。我们用先前用图片训练过的模型去为不同的事物分类。为什么能这样做呢?因为我们用到的模型之前训练过,进行过图像分类,所以其内部深层就能永远进行分类。这些深层能够探测边缘和曲线。这就是迁移学习该术语的来源。把一个问题语句迁移学习到另外一个中。模型的运行情况很可能本身就不错,但我们还可以让它更完善。接着我们训练顶层。这些层其实和实际分类元素相关。我们在训练的数据集上训练层。我们可以把这个数据叫做领域特定。这能够帮助我们理解网络,确切明白我们想给什么分类。所以编码如下所示:
importos
importh5py
importnumpyasnp
fromkeras.preprocessing.imageimportImageDataGenerator
fromkerasimportoptimizers
fromkeras.modelsimportSequential
fromkeras.layersimportConvolution2D,MaxPooling2D,ZeroPadding2D
fromkeras.layersimportActivation,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)
assertos.path.exists(weights_path),'Model weights not found (see "weights_path" variable in script).'
f=h5py.File(weights_path)
forkinrange(f.attrs['nb_layers']):
ifk>=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)]forpinrange(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)
forlayerinmodel.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 上班,先后从事平台游戏和手机游戏的工作。
Major术业