识猫辨狗!用TensorFlow和Keras构建卷积神经网络

ChristianaLindsay 发布于6月前
0 条问题

 

全文共 9940 字,预计学习时长 20 分钟或更长

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

不同神经网络结构各有所长。 本文主要介绍如何在Python中使用TensorFlow和Keras构建卷积神经网络。  

卷积神经网络是过去十年中深度学习成为一大热点的部分原因。 今天将使用TensorFlow的eager API来训练图像分类器,以辨别图像内容是狗还是猫。

人工神经网络在许多领域都展现出了其强大功能,最近已经应用到很多行业中。然而,不同深度学习结构各有以下优势:

· 图像分类(卷积神经网络)。

· 图像、音频和文本生成(GANS,RNN)。

· 时间序列预测(RNNS,LSTM)。

· 推荐系统(波尔兹曼机)。

· 等等 (如,回归)。

本文将集中讨论其中的第一项。 

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

卷积神经网络的概念

在多层感知器(Multilayer Perceptrons,简称MLP)中,每一层的神经元都连接到下一层的所有神经元。一般称这种类型的层为完全连接。 

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

多层感知器示例。图片来源: astroml

卷积神经网络则不同:它们包含卷积层。 

在完全连接层上,每个神经元的输出将是前一层的线性变换,由非线性激活函数(如ReLu或Sigmoid)组成。 

相反,卷积层中每个神经元的输出仅仅是前一层神经元的子集(通常很小)的函数。

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

图片来源: Brilliant

卷积层上的输出是对前一层神经元的子集进行卷积的结果,然后得出激活函数。 

卷积的概念

如果给定输入矩阵A(通常是前一层的值)以及称为卷积核或滤波器K的权值矩阵(通常小得多),卷积运算后将输出新的矩阵B。 

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

图片来自@RaghavPrabhu

如果K是C×C矩阵,则B中的第一个元素的计算方法为: 

· 取A的第一个C×C子矩阵。

· 将每个元素乘以K中相应的权值。

· 将所有结果相加。

最后两步相当于将A的子矩阵和K的子矩阵平面化,并计算结果的向量的点积。 

然后向右滑动K以获取下一个元素。依此类推,对A的每一行重复此过程。 

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

卷积图例 图片来自 @RaghavPrabhu

根据需要,只能从以C排和C列为中心的卷积核开始,以避免“越界”,或者假设“A之外”的所有元素都有一个默认值(通常为0)——这将决定B究竟是小于A还是等于A。 

可以看到,如果A是一个N×M矩阵,那么B中每个神经元的值将不取决于N×M权重,而只取决于其中的C×C(更少)。 

这使得卷积层比完全连接层更轻便,帮助卷积模型更快地学习。 

最终将在每一层上将使用大量卷积核(获取一个矩阵叠层作为每一层的输出)。然而,它仍然比曾经的MLP要轻便得多。 

工作原理

为什么每个神经元对其他大多数神经元的影响可以忽略不计呢?整个系统的前提是,每个神经元都受到它的“邻域”的强烈影响。距离较远的神经元却对此只有很小的影响。 

这一假设直观地表现在图像中——说到输入层,就想到每个神经元将是一个像素或像素的RGB值。这也是卷积神经网络方法在图像分类中如此有效的部分原因。

举个例子,如果抓取一张蓝天的照片的局域,附近的区域可能也会用类似的色调显示天空。 

像素的邻域通常具有与其相似的RGB值。如果没有,那么可能意味着它是一个图形或物体的边缘。 

如果用纸笔(或计算器)做一些卷积,就会意识到,如果是在某种边缘上,某些卷积核会增加输入的强度。在其他边缘,则会减少强度。 

下面是卷积核V和H的示例:

垂直和水平边缘的滤波器 

V过滤垂直边缘(上面的颜色与下面的颜色非常不同),H过滤水平边缘。注意其中一个是另一个的转置。 

卷积示例

以下是一组未经过滤的猫咪照片: 

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

如果分别应用水平和垂直边缘滤波器,会得出以下结果: 

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

可以看到某些特征是变得更加显著的,而另一些特征逐渐消失。有趣的是,每个过滤器都展示了不同的特征。

这就是卷积神经网络学习识别图像特征的方法。

让它们适应自己的卷积核权值比任何手动方法都容易得多。手动表达像素之间的关系是难以实现的。难以想象人应该如何徒手厘清像素之间的关系!

想要真正理解每一个卷积对图片的作用,强烈推荐此网站:http://setosa.io/ev/image-kernels/。它比任何一本书或教程的帮助都大。

之前已经介绍到了一些理论。现在进入到实践环节。 

如何在TensorFlow中训练卷积神经网络

TensorFlow是Python最流行的深度学习框架。

笔者也听说过PyTorch很好用,但从来没有机会尝试过。

笔者已经编写了一个如何使用TensorFlow的KerasAPI来训练神经网络的教程,着重介绍了自动编码器:http://www.datastuff.tech/machine-learning/autoencoder-deep-learning-tensorflow-eager-api-keras/

本文将尝试三种不同的体系结构,优中选优。

和往常一样,所有的代码都可以GitHub上找到(https://github.com/StrikingLoo/Cats-and-dogs-classifier-tensorflow-CNN),所以可以自己进行尝试,或者参考本文示例。当然,本文还会进行Python代码段的展示。

数据集

下面将训练一个神经网络来预测一幅图像包含的是一只狗还是一只猫。为此,将使用Kaggle的相关数据集,其中包含不同分辨率的12500只猫和12500只狗的图片。 

用NumPy加载和预处理图像数据

具有固定维数的特征向量或矩阵输入至神经网络中。那么它是如何从图片中生成的呢?

幸运的是,Python的图像库提供了一种简单方法,可将图像加载为NumPy数组,一个RGB值的高×宽矩阵。

在Python中进行图像过滤时,已经完成这个操作了,所以接下来将再次使用这段代码。

但仍然需要对固定维度进行修复。那么如何选择输入层的维度呢?

这一点很重要,因为必须根据所选分辨率调整每个图片的大小。纵横比不能扭曲太多,以免给网络带来太多噪声。 

下面是数据集中最常见的尺寸。

customAdam = keras.optimizers.Adam(lr=0.001)

model.compile(optimizer=customAdam,  # Optimizer

# Loss function to minimize

loss="mean_squared_error",

# List of metrics to monitor

metrics=["binary_crossentropy","mean_squared_error"])

 

print( # Fit model on training data )

history = model.fit(x_train, 

labels_train, 

batch_size=32, 

shuffle = True, #important since we loaded cats first, dogs second.

epochs=3,

validation_data=(x_valid, labels_valid))

 

#Train on 4096 samples, validate on 2048 samples

#loss: 0.5000 - binary_crossentropy: 8.0590 - mean_squared_error: 0.5000 - val_loss: 0.5000 - val_binary_crossentropy: 8.0591 - val_mean_squared_error: 0.5000

这是对1000张照片进行抽样的结果。对象增加至5000张时,结果不变。

最常见的尺寸是375×500,但笔者决定将其除以4作为网络的输入。

这就是目前的图像加载代码。

IMG_SIZE = (94, 125)

def pixels_from_path(file_path):

im = Image.open(file_path)

im = im.resize(IMG_SIZE)

np_im = np.array(im)

#matrix of pixel RGB values

return np_im

最后可以使用此代码段加载数据。由于笔者的电脑内存有限,选择4096张图片作为训练集,1024张图片作为验证集。 

读者试验时可随意将这些数字增加到最大值(如10000用于训练集,2500用于验证集)。 

SAMPLE_SIZE = 2048

print("loading training cat images...")

cat_train_set = np.asarray([pixels_from_path(cat) for cat in glob.glob( cats/* )[:SAMPLE_SIZE]])

print("loading training dog images...")

dog_train_set = np.asarray([pixels_from_path(dog) for dog in glob.glob( dogs/* )[:SAMPLE_SIZE]])

valid_size = 512

print("loading validation cat images...")

cat_valid_set = np.asarray([pixels_from_path(cat) for cat in glob.glob( cats/* )[-valid_size:]])

print("loading validation dog images...")

dog_valid_set = np.asarray([pixels_from_path(dog) for dog in glob.glob( dogs/* )[-valid_size:]])

# generate X and Y (inputs and labels).

x_train = np.concatenate([cat_train_set, dog_train_set])

labels_train = np.asarray([1 for _ in range(SAMPLE_SIZE)]+[0 for _ in range(SAMPLE_SIZE)])

x_valid = np.concatenate([cat_valid_set, dog_valid_set])

labels_valid = np.asarray([1 for _ in range(valid_size)]+[0 for _ in range(valid_size)])

训练神经网络

首先展示一个标准MLP的完成程度作为基准。笔者希望MLP的表现比较糟糕,这样才能显示出卷积神经网络具有如此大的开创性。 

下面是一个隐藏层,为完全连接的神经网络。 

from tensorflow import keras

from tensorflow.keras import layers

total_pixels = img_size[0] *img_size[1] * 3

fc_size = 512

inputs = keras.Input(shape=(img_size[1], img_size[0],3), name= ani_image )

x = layers.Flatten(name = flattened_img )(inputs) #turn image to vector.

x = layers.Dense(fc_size, activation= relu , name= first_layer )(x)

outputs = layers.Dense(1, activation= sigmoid , name= class )(x)

model = keras.Model(inputs=inputs, outputs=outputs)

本文的所有训练都使用AdamOptimizer,因为它速度最快。笔者只调整了每个模型的学习速率(原速率为1e-5)。 

笔者对本模型进行了10次训练,基本会进行随机推测。 

已确定对训练数据进行置乱,因为它是按顺序加载的,可能会使模型有偏差。

customAdam = keras.optimizers.Adam(lr=0.001)

model.compile(optimizer=customAdam,  # Optimizer

# Loss function to minimize

loss="mean_squared_error",

# List of metrics to monitor

metrics=["binary_crossentropy","mean_squared_error"])

 

print( # Fit model on training data )

history = model.fit(x_train, 

labels_train, 

batch_size=32, 

shuffle = True, #important since we loaded cats first, dogs second.

epochs=3,

validation_data=(x_valid, labels_valid))

 

#Train on 4096 samples, validate on 2048 samples

#loss: 0.5000 - binary_crossentropy: 8.0590 - mean_squared_error: 0.5000 - val_loss: 0.5000 - val_binary_crossentropy: 8.0591 - val_mean_squared_error: 0.5000

使用MSE作为损失函数,因为它通常展示的较为直观。如果MSE在二进制分类中是0.5,那么就可以像往常一样预测为0。然而,层数更多的或不同损失函数的MLP并没有发挥出较好的性能。 

其他一些成熟的监督学习算法,如Boosted Trees (使用 XGBoost)等,在图像分类上更为糟糕。 

训练卷积神经网络

单单一个卷积层究竟有多优秀?下面在模型中添加一个卷积层,然后进行观察。 

fc_layer_size = 128

img_size = IMG_SIZE

conv_inputs = keras.Input(shape=(img_size[1], img_size[0],3), name= ani_image )

conv_layer = layers.Conv2D(24, kernel_size=3, activation= relu )(conv_inputs)

conv_layer = layers.MaxPool2D(pool_size=(2,2))(conv_layer)

conv_x = layers.Flatten(name = flattened_features )(conv_layer) #turn image to vector.

conv_x = layers.Dense(fc_layer_size, activation= relu , name= first_layer )(conv_x)

conv_x = layers.Dense(fc_layer_size, activation= relu , name= second_layer )(conv_x)

conv_outputs = layers.Dense(1, activation= sigmoid , name= class )(conv_x)

conv_model = keras.Model(inputs=conv_inputs, outputs=conv_outputs)

customAdam = keras.optimizers.Adam(lr=1e-6)

conv_model.compile(optimizer=customAdam,  # Optimizer

# Loss function to minimize

loss="binary_crossentropy",

# List of metrics to monitor

metrics=["binary_crossentropy","mean_squared_error"])

 

#Epoch 5/5 loss: 1.6900 val_loss: 2.0413 val_mean_squared_error: 0.3688

在本网络中添加一个卷积层(具有24个卷积核),然后添加两个完全连接的层。

所有的最大池化都将四个神经元减少到一个,最高值取自四个神经元中。 

在仅仅5次训练之后,它的表现已经比以前的网络要好得多。 

验证MSE为0.36,已经比随机推测要好得多。但是请注意,必须使用一个小得多的学习率。 

尽管它在短短几次之内就掌握了,但每次训练所花的时间都要长得多,模型的体积也要大得多(200+MB)。 

笔者还测量了预测值和验证值之间的皮尔逊相关系数。本模型的得分为15.2%。 

preds = conv_model.predict(x_valid)

preds = np.asarray([pred[0] for pred in preds])

np.corrcoef(preds, labels_valid)[0][1] # 0.15292172

带有两个卷积层的神经网络

既然神经网络模型的完成度如此之高,笔者决定扩大体积——添加了另一个卷积层,使两者都增大(每个48个卷积核)。

这意味着模型可以从图像中学习更复杂的特征。然而,可以预见的是,这也意味着内存几乎要爆炸了。而且训练花费的时间要长得多(15次需要半小时)。 

fc_layer_size = 256

img_size = IMG_SIZE

conv_inputs = keras.Input(shape=(img_size[1], img_size[0],3), name= ani_image )

#first convolutional layer.

conv_layer = layers.Conv2D(48, kernel_size=3, activation= relu )(conv_inputs)

conv_layer = layers.MaxPool2D(pool_size=(2,2))(conv_layer)

#second convolutional layer.

conv_layer = layers.Conv2D(48, kernel_size=3, activation= relu )(conv_layer)

conv_layer = layers.MaxPool2D(pool_size=(2,2))(conv_layer)

conv_x = layers.Flatten(name = flattened_features )(conv_layer) #turn image to vector.

conv_x = layers.Dense(fc_layer_size, activation= relu , name= first_layer )(conv_x)

conv_x = layers.Dense(fc_layer_size, activation= relu , name= second_layer )(conv_x)

conv_outputs = layers.Dense(1, activation= sigmoid , name= class )(conv_x)

conv_model = keras.Model(inputs=conv_inputs, outputs=conv_outputs)

#Epoch 15/15

#4096/4096 [==============================] - 154s 38ms/sample - 

#loss: 0.8806 - binary_crossentropy: 0.8806 - mean_squared_error: 0.2402

#val_loss: 1.5379 - val_binary_crossentropy: 1.5379 - val_mean_squared_error: 0.3302

#Labels vs predictions correlation coefficient 0.2188213

结果极佳。预测值与标记值之间的皮尔逊相关系数达到0.21,验证MSE达到0.33。 

那么来测量一下网络的准确度。如果1代表一只猫,0代表一只狗,那么就可以说“如果模型预测的值高于某个阈值t,那么就预测猫,否则就预测狗。”

在试验了10个简单的阈值后,该网络的最大准确率为61%。 

cat_quantity = sum(labels_valid)

for i in range(1,10):

print( threshold : +str(.1*i))

print(sum(labels_valid[preds > .1*i])/labels_valid[preds > .1*i].shape[0])

更大的卷积神经网络

由于明显地增加模型的大小使它学习得更好,所以尝试使两个卷积层都增大,每个层有128个滤波器。 

模型的其他部分不变,学习速率也不变。 

fc_layer_size = 256

img_size = IMG_SIZE

conv_inputs = keras.Input(shape=(img_size[1], img_size[0],3), name= ani_image )

conv_layer = layers.Conv2D(128, kernel_size=3, activation= relu )(conv_inputs)

conv_layer = layers.MaxPool2D(pool_size=(2,2))(conv_layer)

conv_layer = layers.Conv2D(128, kernel_size=3, activation= relu )(conv_layer)

conv_layer = layers.MaxPool2D(pool_size=(2,2))(conv_layer)

conv_x = layers.Flatten(name = flattened_features )(conv_layer) #turn image to vector.

conv_x = layers.Dense(fc_layer_size, activation= relu , name= first_layer )(conv_x)

conv_x = layers.Dense(fc_layer_size, activation= relu , name= second_layer )(conv_x)

conv_outputs = layers.Dense(1, activation= sigmoid , name= class )(conv_x)

huge_conv_model = keras.Model(inputs=conv_inputs, outputs=conv_outputs)

#epoch 13 

# 445s  loss: 0.3749 - binary_crossentropy: 0.3749 - mean_squared_error: 0.1190

# val_loss: 0.8217 - val_binary_crossentropy: 0.8217 - val_mean_squared_error: 0.2470

# Pearson coerrcoef : 0.3037038

该模型的相关系数最终达到了30%!最佳准确度为67%,这意味着它的预测准确次数恰好是预测总数的三分之二。 

笔者仍然认为一个更大的模型可以更好地拟合数据。 

正因为如此,在下一次训练中,笔者决定将完全连接层的大小增加一倍至512个神经元。 

但是,笔者确实将第一个卷积层的大小减少了一半,仅用了64个滤波器。 

由此发现如果减小第一个卷积层,并且随着它们的深入增加它们的大小,将会获得更好的模型性能。 

幸运的是,这个结论是正确的!

尺寸大一倍的完全连接层的模型的确认损失为0.75,相关系数为42%。 

它的准确度是75%,这意味着它正确预测数可以达到预测总数的四分之三! 

这清楚地表明它已经完成学习,即使分数不是最高的(更不用说打败人类了)。 

这表明,至少在这种情况下,增加完全连接层的尺寸比增加卷积滤波器的数量更有效。 

笔者本可以继续尝试越来越大的模型,但收敛过程已经花了大约一个小时。 

通常,需要在模型的大小和时间限制之间进行权衡。 

大小限制了网络对数据的拟合程度(小模型会欠拟合),但也要考虑时间成本。

如果截止时间明确,也要考虑这种情况。

结论

可以看到,卷积神经网络在图像分类任务上明显优于普通的结构。本文还试验了不同方法来度量模型的性能(相关系数、准确性)。 

本文还介绍了模型的大小(防止欠拟合)和收敛速度之间的权衡。 

最后,使用了TensorFlow的eager API来轻松地训练一个深度神经网络,并使用NumPy进行图像预处理(尽管很简单)。 

对于以后的文章,相信可以用不同的池化层、滤波器大小、跨步和预处理对同一任务进行更多的实验。

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

推荐阅读专题

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

留言 点赞 发个朋友圈

我们一起分享AI学习与发展的干货

编译组:杨月、夏伊凡

相关链接:

https://www.kdnuggets.com/2019/07/convolutional-neural-networks-python-tutorial-tensorflow-keras.html

如需转载,请后台留言,遵守转载规范

推荐文章阅读

ACL2018论文集50篇解读

EMNLP2017论文集28篇论文解读

2018年AI三大顶会中国学术成果全链接

ACL2017 论文集:34篇解读干货全在这里

10篇AAAI2017经典论文回顾

长按识别二维码可添加关注

读芯君爱你

识猫辨狗!用TensorFlow和Keras构建卷积神经网络

查看原文: 识猫辨狗!用TensorFlow和Keras构建卷积神经网络

  • silverrabbit
  • lazyelephant
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。