65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Python+Keras 进行基础深度学习

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2018年5月20日

CPOL

6分钟阅读

viewsIcon

24062

这是介绍 Python 和 Keras 框架中深度学习编码的文章系列的第一篇文章。

引言

监督式深度学习广泛用于机器学习,例如计算机视觉系统。在本文中,我们将介绍使用 Keras 框架进行监督式深度学习的一些关键注意事项。

Keras 是一个高级机器学习框架,我们可以用 Python 编写代码,并且可以在最知名的机器学习框架(如 TensorFlow、CNTK 或 Theano)上运行。它旨在使实验过程变得轻松快捷。

背景

本文不介绍深度学习入门。您应该了解深度学习的基础知识以及一些 Python 编码知识。本文的主要目的是向您介绍 Keras 框架的基础知识,并与其他知名库一起使用,进行快速实验并得出初步结论。

Using the Code

在本第一篇文章中,我们将训练一个简单的神经网络,而在接下来的文章中,我们将介绍一些已知的深度学习架构并进行一些比较。

所有实验均出于教育目的,训练过程将非常快速,结果也不会完美。

第一步:加载库

首先,我们将加载所需的库:numpy、TensorFlow(在本实验中,我们将使用此框架运行 Keras)、Keras、Scikit Learn、Pandas……以及更多。

import numpy as np 
from scipy import misc 
from PIL import Image 
import glob 
import matplotlib.pyplot as plt 
import scipy.misc 
from matplotlib.pyplot import imshow 
%matplotlib inline 
from IPython.display import SVG 
import cv2 
import seaborn as sn 
import pandas as pd 
import pickle 
from keras import layers 
from keras.layers import Flatten, Input, Add, Dense, Activation, 
                  ZeroPadding2D, BatchNormalization, Flatten, 
                  Conv2D, AveragePooling2D, 
                  MaxPooling2D, GlobalMaxPooling2D, Dropout 
from keras.models import Sequential, Model, load_model 
from keras.preprocessing import image 
from keras.preprocessing.image import load_img 
from keras.preprocessing.image import img_to_array 
from keras.applications.imagenet_utils import decode_predictions 
from keras.utils import layer_utils, np_utils 
from keras.utils.data_utils import get_file 
from keras.applications.imagenet_utils import preprocess_input 
from keras.utils.vis_utils import model_to_dot 
from keras.utils import plot_model 
from keras.initializers import glorot_uniform 
from keras import losses 
import keras.backend as K 
from keras.callbacks import ModelCheckpoint 
from sklearn.metrics import confusion_matrix, classification_report 
import tensorflow as tf

设置数据集

在本练习中,我们将使用 CIFAR-100 数据集。该数据集已被使用很长时间。它每个类别有 600 张图像,总共 100 个类别。每个类别有 500 张用于训练的图像和 100 张用于验证的图像。100 个类别中的每一个都分为 20 个超类别。每张图像都有一个“精细”标签(主要类别)和一个“粗略”标签(其超类别)。

Keras 框架提供了直接下载的模块

from keras.datasets import cifar100 

(x_train_original, y_train_original), 
(x_test_original, y_test_original) = cifar100.load_data(label_mode='fine')

实际上,我们已经下载了训练集和测试集。x_train_originalx_test_original 分别包含训练图像和测试图像,而 y_train_originaly_test_original 包含标签。

让我们看看 y_train_original

array([[19], [29], [ 0], ..., [ 3], [ 7], [73]])

如您所见,它是一个数组,其中每个数字对应一个标签。因此,我们要做的第一件事是将这些数组转换为独热编码版本(参见 Wikipedia)。

y_train = np_utils.to_categorical(y_train_original, 100)

y_test = np_utils.to_categorical(y_test_original, 100)

好的,现在让我们看看训练集(x_train_original

array([[[255, 255, 255], 
[255, 255, 255], 
[255, 255, 255], 
..., 
[195, 205, 193], 
[212, 224, 204], 
[182, 194, 167]], 

[[255, 255, 255], 
[254, 254, 254], 
[254, 254, 254], 
..., 
[170, 176, 150], 
[161, 168, 130], 
[146, 154, 113]], 

[[255, 255, 255], 
[254, 254, 254], 
[255, 255, 255], 
..., 
[189, 199, 169], 
[166, 178, 130], 
[121, 133, 87]], 

..., 

[[148, 185, 79], 
[142, 182, 57], 
[140, 179, 60], 
..., 
[ 30, 17, 1], 
[ 65, 62, 15], 
[ 76, 77, 20]], 

[[122, 157, 66], 
[120, 155, 58], 
[126, 160, 71], 
..., 
[ 22, 16, 3], 
[ 97, 112, 56], 
[141, 161, 87]], 

...and more...

], dtype=uint8)

该数据集代表 256 个 RGB 像素的 3 个通道。想看看吗?

imgplot = plt.imshow(x_train_original[3])

plt.show()

接下来,我们需要对图像进行归一化。也就是说,将数据集的每个元素除以总像素数:255。完成后,数组的值将在 0 和 1 之间。

x_train = x_train_original/255

x_test = x_test_original/255

设置训练环境

在训练之前,我们必须在 Keras 环境中设置两个参数。首先,我们必须告诉 Keras 通道在数组中的哪个位置。在图像数组中,通道可以位于最后一个索引或第一个索引。这被称为通道优先或通道后。在本练习中,我们将设置为通道后。

K.set_image_data_format('channels_last')

第二件事是告诉 Keras 当前是哪个阶段。在我们的例子中,是学习阶段。

K.set_learning_phase(1)

训练一个简单的神经网络

我们将训练一个简单的神经网络,因此我们必须编写一个方法来返回一个简单的神经网络模型。

def create_simple_nn():
  model = Sequential()
  model.add(Flatten(input_shape=(32, 32, 3), name="Input_layer")) 
  model.add(Dense(1000, activation='relu', name="Hidden_layer_1")) 
  model.add(Dense(500, activation='relu', name="Hidden_layer_2")) 
  model.add(Dense(100, activation='softmax', name="Output_layer")) 

  return model

代码中的一些关键点。Flatten 指令将输入(图像矩阵)转换为一维数组。接下来,Dense 指令向模型添加一个隐藏层。第一个隐藏层将有 1000 个节点,第二个隐藏层有 500 个节点,第三个(输出层)有 100 个节点。在隐藏层中,我们将使用 ReLu 激活函数,对于输出层,我们将使用 SoftMax 函数。

模型定义完成后,我们对其进行编译,指定优化函数、损失函数以及我们想要使用的度量。在本系列的所有文章中,我们将使用完全相同的函数。我们将使用随机梯度下降优化函数、分类交叉熵损失函数以及准确率mse(均方误差)度量。所有这些都已在 Keras 中预先编码。

snn_model = create_simple_nn() 
snn_model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc', 'mse'])

完成后,让我们看看模型摘要。

snn_model.summary()

_________________________________________________________________ 
Layer (type) Output Shape Param # 
================================================================= 
Input_layer (Flatten) (None, 3072) 0 
_________________________________________________________________ 
Hidden_layer_1 (Dense) (None, 1000) 3073000 
_________________________________________________________________ 
Hidden_layer_2 (Dense) (None, 500) 500500 
_________________________________________________________________ 
Output_layer (Dense) (None, 100) 50100 
================================================================= 
Total params: 3,623,600 
Trainable params: 3,623,600 
Non-trainable params: 0 
_________________________________________________________________

正如我们所见,尽管这是一个简单的神经网络模型,但它需要训练超过 300 万个参数。这将是深度学习存在的主要原因,因为如果您想训练非常复杂的网络,就需要以这种方式训练大量的参数。

现在,我们只需训练。执行以下操作

snn = snn_model.fit(x=x_train, y=y_train, batch_size=32, 
      epochs=10, verbose=1, validation_data=(x_test, y_test), shuffle=True)

我们告诉 Keras 我们想使用训练好的归一化图像数据集和独热编码的训练标签数组进行训练。我们将使用 32 个块的批次(以减少内存使用)并进行 10 个 epoch。对于验证,我们将使用 x_testy_test。训练结果将分配给 snn 变量。 从中,我们将提取训练历史以进行模型比较。

Train on 50000 samples, validate on 10000 samples 
Epoch 1/10 
50000/50000 [==============================] - 16s 318us/step - loss: 4.1750 - 
acc: 0.0740 - mean_squared_error: 0.0097 - val_loss: 3.9633 - val_acc: 0.1051 - 
val_mean_squared_error: 0.0096 
Epoch 2/10 
50000/50000 [==============================] - 15s 301us/step - loss: 3.7919 - 
acc: 0.1298 - mean_squared_error: 0.0095 - val_loss: 3.7409 - val_acc: 0.1427 - 
val_mean_squared_error: 0.0094 
Epoch 3/10 
50000/50000 [==============================] - 15s 294us/step - loss: 3.6357 - 
acc: 0.1579 - mean_squared_error: 0.0093 - val_loss: 3.6429 - val_acc: 0.1525 - 
val_mean_squared_error: 0.0093 
Epoch 4/10 
50000/50000 [==============================] - 15s 301us/step - loss: 3.5300 - 
acc: 0.1758 - mean_squared_error: 0.0092 - val_loss: 3.6055 - val_acc: 0.1626 - 
val_mean_squared_error: 0.0093 
Epoch 5/10 
50000/50000 [==============================] - 15s 300us/step - loss: 3.4461 - 
acc: 0.1904 - mean_squared_error: 0.0091 - val_loss: 3.5030 - val_acc: 0.1812 - 
val_mean_squared_error: 0.0092 
Epoch 6/10 
50000/50000 [==============================] - 15s 301us/step - loss: 3.3714 - 
acc: 0.2039 - mean_squared_error: 0.0090 - val_loss: 3.4600 - val_acc: 0.1912 - 
val_mean_squared_error: 0.0091 
Epoch 7/10 
50000/50000 [==============================] - 15s 301us/step - loss: 3.3050 - 
acc: 0.2153 - mean_squared_error: 0.0089 - val_loss: 3.4329 - val_acc: 0.1938 - 
val_mean_squared_error: 0.0091 
Epoch 8/10 
50000/50000 [==============================] - 15s 300us/step - loss: 3.2464 - 
acc: 0.2275 - mean_squared_error: 0.0089 - val_loss: 3.3965 - val_acc: 0.2013 - 
val_mean_squared_error: 0.0090 
Epoch 9/10 
50000/50000 [==============================] - 15s 301us/step - loss: 3.1902 - 
acc: 0.2361 - mean_squared_error: 0.0088 - val_loss: 3.3371 - val_acc: 0.2133 - 
val_mean_squared_error: 0.0089 
Epoch 10/10 
50000/50000 [==============================] - 15s 299us/step - loss: 3.1354 - 
acc: 0.2484 - mean_squared_error: 0.0087 - val_loss: 3.3233 - val_acc: 0.2154 - 
val_mean_squared_error: 0.0089

尽管我们一直在训练过程中评估训练情况,但我们应该使用新的测试数据集。我将展示如何在 Keras 中实现这一点。

evaluation = snn_model.evaluate(x=x_test, y=y_test, batch_size=32, verbose=1) 
evaluation 

10000/10000 [==============================] - 1s 127us/step 
[3.323309226989746, 0.2154, 0.008915210169553756]

让我们以图形方式查看结果度量(我们将使用 matplotlib 库)。

plt.figure(0) 
plt.plot(snn.history['acc'],'r') 
plt.plot(snn.history['val_acc'],'g') 
plt.xticks(np.arange(0, 11, 2.0)) 
plt.rcParams['figure.figsize'] = (8, 6) 
plt.xlabel("Num of Epochs") 
plt.ylabel("Accuracy") 
plt.title("Training Accuracy vs Validation Accuracy") 
plt.legend(['train','validation']) 

plt.figure(1) 
plt.plot(snn.history['loss'],'r') 
plt.plot(snn.history['val_loss'],'g') 
plt.xticks(np.arange(0, 11, 2.0)) 
plt.rcParams['figure.figsize'] = (8, 6) 
plt.xlabel("Num of Epochs") 
plt.ylabel("Loss") 
plt.title("Training Loss vs Validation Loss") 
plt.legend(['train','validation']) 

plt.show()

首先,模型泛化性不佳。如果您看到,准确率差异为 4%。

使用 SciKit Learn 的混淆矩阵

模型训练完成后,我们希望在对我们创建的模型可用性得出任何结论之前查看其他度量。为此,我们将创建混淆矩阵,并从中查看精确率召回率F1 分数度量(参见 wikipedia)。

要创建混淆矩阵,我们需要对测试集进行预测,然后才能创建混淆矩阵并显示这些度量。预测数组中的每个最大值都将是实际预测。实际上,通常的方法是采用一个偏差值来区分预测值是否可以为正。

snn_pred = snn_model.predict(x_test, batch_size=32, verbose=1) 
snn_predicted = np.argmax(snn_pred, axis=1)

Scikit Learn 库提供了制作混淆矩阵的方法。

#Creamos la matriz de confusión
snn_cm = confusion_matrix(np.argmax(y_test, axis=1), snn_predicted) 

# Visualiamos la matriz de confusión 
snn_df_cm = pd.DataFrame(snn_cm, range(100), range(100)) 
plt.figure(figsize = (20,14)) 
sn.set(font_scale=1.4) #for label size 
sn.heatmap(snn_df_cm, annot=True, annot_kws={"size": 12}) # font size 
plt.show()

最后,显示度量

snn_report = classification_report(np.argmax(y_test, axis=1), snn_predicted)
print(snn_report)

             precision    recall  f1-score   support

          0       0.47      0.32      0.38       100
          1       0.29      0.34      0.31       100
          2       0.24      0.12      0.16       100
          3       0.14      0.10      0.12       100
          4       0.06      0.02      0.03       100
          5       0.14      0.17      0.16       100
          6       0.19      0.13      0.15       100
          7       0.14      0.26      0.19       100
          8       0.22      0.18      0.20       100
          9       0.23      0.39      0.29       100
         10       0.29      0.02      0.04       100
         11       0.27      0.09      0.14       100
         12       0.34      0.23      0.28       100
         13       0.26      0.16      0.20       100
         14       0.19      0.13      0.15       100
         15       0.16      0.14      0.15       100
         16       0.28      0.19      0.23       100
         17       0.32      0.25      0.28       100
         18       0.18      0.26      0.21       100
         19       0.42      0.08      0.13       100
         20       0.35      0.45      0.40       100
         21       0.27      0.43      0.33       100
         22       0.27      0.18      0.22       100
         23       0.30      0.46      0.37       100
         24       0.49      0.31      0.38       100
         25       0.14      0.10      0.11       100
         26       0.17      0.11      0.13       100
         27       0.06      0.29      0.09       100
         28       0.32      0.37      0.34       100
         29       0.12      0.21      0.15       100
         30       0.50      0.13      0.21       100
         31       0.24      0.04      0.07       100
         32       0.29      0.19      0.23       100
         33       0.18      0.28      0.22       100
         34       0.17      0.03      0.05       100
         35       0.17      0.07      0.10       100
         36       0.21      0.19      0.20       100
         37       0.24      0.06      0.10       100
         38       0.17      0.06      0.09       100
         39       0.12      0.07      0.09       100
         40       0.26      0.23      0.24       100
         41       0.62      0.45      0.52       100
         42       0.10      0.05      0.07       100
         43       0.09      0.44      0.16       100
         44       0.10      0.12      0.11       100
         45       0.20      0.03      0.05       100
         46       0.22      0.19      0.20       100
         47       0.37      0.19      0.25       100
         48       0.14      0.48      0.22       100
         49       0.38      0.11      0.17       100
         50       0.14      0.05      0.07       100
         51       0.16      0.15      0.16       100
         52       0.43      0.60      0.50       100
         53       0.27      0.61      0.37       100
         54       0.48      0.26      0.34       100
         55       0.07      0.01      0.02       100
         56       0.45      0.13      0.20       100
         57       0.10      0.42      0.16       100
         58       0.35      0.17      0.23       100
         59       0.13      0.36      0.19       100
         60       0.40      0.65      0.50       100
         61       0.42      0.34      0.38       100
         62       0.25      0.49      0.33       100
         63       0.31      0.21      0.25       100
         64       0.14      0.03      0.05       100
         65       0.13      0.02      0.03       100
         66       0.00      0.00      0.00       100
         67       0.20      0.35      0.25       100
         68       0.24      0.66      0.35       100
         69       0.26      0.30      0.28       100
         70       0.37      0.22      0.28       100
         71       0.37      0.46      0.41       100
         72       0.11      0.01      0.02       100
         73       0.22      0.22      0.22       100
         74       0.09      0.06      0.07       100
         75       0.27      0.28      0.27       100
         76       0.29      0.38      0.33       100
         77       0.20      0.01      0.02       100
         78       0.19      0.03      0.05       100
         79       0.25      0.02      0.04       100
         80       0.14      0.02      0.04       100
         81       0.13      0.02      0.03       100
         82       0.59      0.50      0.54       100
         83       0.14      0.15      0.14       100
         84       0.18      0.06      0.09       100
         85       0.20      0.52      0.28       100
         86       0.31      0.23      0.26       100
         87       0.21      0.27      0.23       100
         88       0.07      0.02      0.03       100
         89       0.16      0.44      0.24       100
         90       0.20      0.03      0.05       100
         91       0.30      0.34      0.32       100
         92       0.20      0.10      0.13       100
         93       0.18      0.17      0.17       100
         94       0.46      0.25      0.32       100
         95       0.23      0.41      0.29       100
         96       0.24      0.17      0.20       100
         97       0.10      0.16      0.12       100
         98       0.09      0.13      0.11       100
         99       0.39      0.15      0.22       100

avg / total       0.24      0.22      0.20     10000

ROC 曲线

ROC 曲线由二元分类器使用,因为它是一个很好的工具,可以查看真阳性率与假阳性率。这是一种用于评估二进制分类器性能的图形化方法。

我们将为多类分类编写 ROC 曲线。此代码来自 DloLogy,但您可以访问 Scikit Learn 文档页面。

from sklearn.datasets import make_classification
from sklearn.preprocessing import label_binarize
from scipy import interp
from itertools import cycle

n_classes = 100

from sklearn.metrics import roc_curve, auc

# Plot linewidth.
lw = 2

# Compute ROC curve and ROC area for each class
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_test[:, i], snn_pred[:, i])
    roc_auc[i] = auc(fpr[i], tpr[i])

# Compute micro-average ROC curve and ROC area
fpr["micro"], tpr["micro"], _ = roc_curve(y_test.ravel(), snn_pred.ravel())
roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])

# Compute macro-average ROC curve and ROC area

# First aggregate all false positive rates
all_fpr = np.unique(np.concatenate([fpr[i] for i in range(n_classes)]))

# Then interpolate all ROC curves at this points
mean_tpr = np.zeros_like(all_fpr)
for i in range(n_classes):
    mean_tpr += interp(all_fpr, fpr[i], tpr[i])

# Finally average it and compute AUC
mean_tpr /= n_classes

fpr["macro"] = all_fpr
tpr["macro"] = mean_tpr
roc_auc["macro"] = auc(fpr["macro"], tpr["macro"])

# Plot all ROC curves
plt.figure(1)
plt.plot(fpr["micro"], tpr["micro"],
         label='micro-average ROC curve (area = {0:0.2f})'
               ''.format(roc_auc["micro"]),
         color='deeppink', linestyle=':', linewidth=4)

plt.plot(fpr["macro"], tpr["macro"],
         label='macro-average ROC curve (area = {0:0.2f})'
               ''.format(roc_auc["macro"]),
         color='navy', linestyle=':', linewidth=4)

colors = cycle(['aqua', 'darkorange', 'cornflowerblue'])
for i, color in zip(range(n_classes-97), colors):
    plt.plot(fpr[i], tpr[i], color=color, lw=lw,
             label='ROC curve of class {0} (area = {1:0.2f})'
             ''.format(i, roc_auc[i]))

plt.plot([0, 1], [0, 1], 'k--', lw=lw)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Some extension of Receiver operating characteristic to multi-class')
plt.legend(loc="lower right")
plt.show()

# Zoom in view of the upper left corner.
plt.figure(2)
plt.xlim(0, 0.2)
plt.ylim(0.8, 1)
plt.plot(fpr["micro"], tpr["micro"],
         label='micro-average ROC curve (area = {0:0.2f})'
               ''.format(roc_auc["micro"]),
         color='deeppink', linestyle=':', linewidth=4)

plt.plot(fpr["macro"], tpr["macro"],
         label='macro-average ROC curve (area = {0:0.2f})'
               ''.format(roc_auc["macro"]),
         color='navy', linestyle=':', linewidth=4)

colors = cycle(['aqua', 'darkorange', 'cornflowerblue'])
for i, color in zip(range(3), colors):
    plt.plot(fpr[i], tpr[i], color=color, lw=lw,
             label='ROC curve of class {0} (area = {1:0.2f})'
             ''.format(i, roc_auc[i]))

plt.plot([0, 1], [0, 1], 'k--', lw=lw)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Some extension of Receiver operating characteristic to multi-class')
plt.legend(loc="lower right")
plt.show()

最后,我们将保存训练历史数据。

#Histórico
with open(path_base + '/simplenn_history.txt', 'wb') as file_pi:
  pickle.dump(snn.history, file_pi)

关注点

尽管使用此模型训练 10 个 epoch 已经足够好,但从准确率和损失图表中我们可以看到,通过增加 epoch,模型不会有太大改进。ROC 曲线具有良好的真阳性率与假阳性率(这意味着在预测一个类标签时,其成为假阳性的概率很低)。但是,对于准确率召回率精确率,该比率非常低。

在下一章中,我们将使用与本章相同的度量、损失和优化函数,对相同的数据集使用一个非常简单的卷积神经网络进行训练。再见!

历史

  • 2018 年 5 月 20 日:初始版本
© . All rights reserved.