原文来源:Medium
作者:Imaad Mohamed Khan
「雷克世界」编译:嗯~阿童木呀、多啦A亮
在我来到德国之前,我对德国道路上没有车速限制的事感到着迷。令我失望的是,几乎所有地方都有限速。公路(高速公路)上只有一些区域被指定为无限速区域,汽车和其他车辆可以测试其引擎极限。
如果一个司机正在超速行驶,他突然进入一个必须遵循一定速度限制的区域,他将如何操作?解决这个问题的一种方式,也是最常见的方式是驾驶员手动刹车并减少加速度。但是,我们现处于2017年,电脑可以识别比猫和狗更多东西!所以让我们试着让电脑自动识别限速标志!
我基于我的实现,以Oliver Zeigermann的“使用TensorFlow和Keras的卷积神经网络介绍”(https://www.youtube.com/watch?v=WIhI1W6NoZ0)的教程为基础,这是一个非常好的教程,我喜欢他从工程师的角度而不是研究员的角度来探索实施。我一直在积极尝试两种方法,在这里我学习一些数学知识,并且学习如何使用各种库来实现它。
我在本教程中发现的关键点之一是微软Azure notebooks。在他的教程中,Zeigermann要求观众在本地或在云上运行notebooks。他说他喜欢云,因为云机功能更强大,规格也比他的机器更高。我的机器也是如此。在此之前,我一直在CPU上使用Jupyter notebooks,并且遇到了我的机器快速发热的问题,所以我决定尝试Azure notebooks,并取得了一个很好的经验。它们速度很快,可以立即设置,并且它们是免费的!对于刚开始使用Python和数据科学的人来说,Azure notebooks可以帮助他们迅速起步。
实施任何数据科学或AI项目的首要任务是找到一个合适的数据集。演示者提供了本教程的数据集。他在视频中提到,该教程是基于Waleed Abdulla的类似项目(https://medium.com/@waleedka/traffic-sign-recognition-with-tensorflow-629dffc391a6)。我假设他从另一个项目中获得了数据。在这种情况下,它基于一个名为比利时交通标志数据集(Belgian Traffic Sign Dataset)的数据集(http://btsd.ethz.ch/shareddata/)。
在获得数据之后,我们仍然需要以机器学习模型可以使用的形式准备我们的数据。数据集中的图像为.ppm格式。我们将其转换成可用于使用skimage库分析的形式。这是执行转换的函数。
import os
import skimage.data
def load_data(data_dir):
directories = [d for d in os.listdir(data_dir)
labels = []
images = []
for d in directories:
for f in os.listdir(label_dir) if f.endswith(".ppm")]
for f in file_names:
images.append(skimage.data.imread(f))
labels.append(int(d))
return images, labels
在我们使用load_data函数加载数据之后,我们试着看看图像和标签是如何组织的。我们写了一个函数用来可视化我们的数据。
import matplotlib
import matplotlib.pyplot as plt
def display_images_and_labels(images, labels):
"""Display the first image of each label."""
unique_labels = set(labels)
plt.figure(figsize=(15, 15))
i = 1
for label in unique_labels:
# Pick the first image for each label.
image = images[labels.index(label)]
plt.subplot(8, 8, i) # A grid of 8 rows x 8 columns
plt.axis('off')
plt.title("Label ()".format(label, labels.count(label)))
i += 1
_ = plt.imshow(image)
plt.show()
display_images_and_labels(images, labels)
这导致以下结果。
具有各自标签和计数的限速图像
从上图可以看到,我们的数据集中有6个不同类别的限速图像,分别显示为30、50、70、80、100和120,且每个种类中分别有 79、81、68、53、41和57个样本。这并不是一个非常大的数据集,但是对于我们的问题来说,它的运行效果已经足够好了。
既然已经观察过我们图像的组织方式后,现在来看一下每个图像的形状,以及RGB颜色的最小值和最大值。
for image in images[:5]:
print("shape: , min: , max: ".format(image.shape, image.min(), image.max()))
Output:
shape: (21, 22, 3), min: 27, max: 248
shape: (23, 23, 3), min: 10, max: 255
shape: (43, 42, 3), min: 11, max: 254
shape: (61, 58, 3), min: 3, max: 255
shape: (28, 27, 3), min: 8, max: 65
从输出中我们可以清楚地看到,每个图像的形状随着最小和最大RGB值分布的变化而变化。我们的神经网络模型期望所有的图像都具有相同的形状。在调整大小的同时,我们将最小和最大RGB值在0到1之间进行正则化处理。我们使用skimage的变换函数将图像转换为64x64像素图像,其中有3个用于RGB的信道。
import skimage.transform
最后,我们执行数据准备的最后一步,将图像和标签转换为numpy数组。我们还使用to_categorical函数将我们的标签转换为不同的分类数组,其中,如果那个特定的类别被表示为0,则其表示为1。
import numpy as np
y = np.array(labels)
X = np.array(images64)
num_categories = 6
y = to_categorical(y, num_categories)
在这个阶段之后,Zeigermann解释了他是如何通过训练一个简单的keras模型来进行实验的,其中,该模型在训练数据方面表现出色,但在测试数据方面表现不佳。他还讨论了与SGD,Adagrad等其他方法相比,RMSProp似乎是更好的优化器。
最后,随着该实验的进展,他开始涉及卷积神经网络方面的知识。卷积神经网络或循环神经网络(CNN)是一种明确假定输入是图像的神经网络。在计算机视觉方面的深度学习革命大部分是由CNN领导的。下面是其架构的一个示例:
CNN架构,来源http://cs231n.github.io/convolutional-netw
由于我们所关注的重点是CNN的应用程序方面的知识,所以我不会详细介绍每个层的细节以及我们是如何调整超参数的。我们使用Keras库运行CNN。 Keras是一个建立在Tensorflow和Theano上的高级API(Theano不再进行维护)。现在让看一看代码:
from keras.models import Model
from keras.layers import Dense, Flatten, Input, Dropout
from keras.layers import Convolution2D, MaxPooling2D
from sklearn.model_selection import train_test_split
inputs = Input(shape=(64,64,3))
x = Convolution2D(32, 4,4, border_mode='same', activation='relu')(inputs)
x = Convolution2D(32, 4,4, border_mode='same', activation='relu')(x)
x = MaxPooling2D(pool_size=(2,2))(x)
x = Dropout(0.25)(x)
# one more block
x = Convolution2D(64, 4, 4, border_mode='same', activation='relu')(x)
x = MaxPooling2D(pool_size=(2, 2))(x)
x = Dropout(0.25)(x)
x = Flatten()(x)
# fully connected, 256 nodes
x = Dense(256, activation='relu')(x)
x = Dropout(0.50)(x)
# softmax activation, 6 categories
predictions = Dense(6, activation='softmax')(x)
model = Model(input=inputs, output=predictions)
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(X_train, y_train, nb_epoch=50, batch_size=100)
该模型现在运行50轮,批量大小为100,它在80%的总数上进行训练,而这些数据在使用sklearn的train_test_split函数之前就已经进行分割。以下是我们运行了50轮之后得到的结果:
进行50轮数据训练之后的CNN输出
该模型的收敛性为0.2489,精确度约为92%。对于一个没有进行任何超参数调整的模型来说这是很好的性能表现,让我们看看模型在训练数据集上的精确度。
train_loss, train_accuracy = model.evaluate(X_train,y_train, batch_size=32)
train_loss, train_accuracy
训练数据的精确度为83.49%,而损失为0.41。接下来看看在测试数据集上的性能表现:
test_loss, test_accuracy = model.evaluate(X_test, y_test, batch_size=32)
test_loss, test_accuracy
我们得到约68%的精确度,损失为1.29。这个结果并不是很理想。但是这里也有一些有趣的事情发生,这也是Zeigermann所提到的。这是我第二次运行该模型,而在我之前的测试中,测试精确度达到95%左右。显然,模型的输出是不确定的。也许有办法让它变得更稳定,而关于该如何实现这一点,还需要掌握更多的知识。
最后,我们在测试数据集上对模型的一些预测进行可视化。我们从测试数据集中随机抽取10个图像,并使用matplotlib绘制预测图。我们显示预测的类以及其对照比标准。如果模型的预测结果是正确的,那么我们将将其涂成绿色,否则涂成红色。
import random
random.seed(3)
sample_indexes = random.sample(range(len(X_test)), 10)
sample_images = [X_test[i] for i in sample_indexes]
sample_labels = [y_test[i] for i in sample_indexes]
#get the indices of the array using argmax
ground_truth = np.argmax(sample_labels, axis=1)
X_sample = np.array(sample_images)
prediction = model.predict(X_sample)
predicted_categories = np.argmax(prediction, axis=1)
# Display the predictions and the ground truth visually.
def display_prediction (images, true_labels, predicted_labels):
fig = plt.figure(figsize=(10, 10))
for i in range(len(true_labels)):
truth = true_labels[i]
prediction = predicted_labels[i]
plt.subplot(5, 2,1+i)
plt.axis('off')
color='green' if truth == prediction else 'red'
plt.text(80, 10, "Truth: \nPrediction: ".format(truth, prediction),
fontsize=12, color=color)
plt.imshow(images[i])
display_prediction(sample_images, ground_truth, predicted_categories)
正如你在下面看到的,我们的模型对于来自测试数据集的10个随机采样图像,正确预测出了8个图像。结果很不错,而之前当我在测试数据集上获得了95%的精确度时,它正确预测出了10/10个图像。
领取专属 10元无门槛券
私享最新 技术干货