"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В этом туториале речь пойдет про распознавание дорожных знаков с использованием сверточных нейронных сетей.\n",
"В сети можно легко найти материалы по сверточным нейронным сетям, поэтому здесь не будет большого количества теории и описания библиотеки TensorFlow, с помощью которой мы и будем сегодня строить сеть. В конце статьи находится список полезной литературы, который поможет глубже узнать сверточные нейронные сети."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## СNN"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Особенностью сверточных нейронных сетей является наличие так называемых сверточных слоев, которые выполняют операцию свертки. Выражение «свернуть изображение» означает, что необходимо пространственно пробежать по изображению и вычислить скалярные произведения.\n",
"\tКаждый сверточный слой передаёт в новый слой так называемые карты признаков, которые и являются результатами скалярного произведения.\n",
"\tЭти карты признаков поступают на вход субдискретизирующих слоёв, задача которых состоит в уменьшении размерность карт признаков.\n",
"\tДля уменьшения размерности часто используют одну из двух функций: max pooling, average pooling. Первая функция выбирает максимальное значение уменьшаемого участка карты признаков. Вторая функция выбирает уже среднее значение. Таким образом находится признак, наличие которого является наиболее ключевым фактором отнесения классифицируемого изображение к одному из классов."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Сразу в бой\n",
"Не будем раскачиваться на теории, а сразу возьмемся за практику. По [ссылке](https://drive.google.com/file/d/0ByDNm-bvLJQEN2xtTHo4STgyMkE/view?usp=sharing) вы можете скачать тренировочные и тестовые наборы картинок в усеченном и расширенном объеме. В туториале используется усеченная выборка для экономии времени - в тренировочной выборке содержится всего 11100 изображений(в то время как в расширенном - 473000).\n",
"\n",
"Для построения модели, обучения и тестирования используется Tensorflow версии 0.12.1.\n",
"\n",
"Приступим."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Импортируем все необходимые библиотеки\n",
"import warnings\n",
"\n",
"warnings.filterwarnings('ignore')\n",
"%matplotlib inline\n",
"import pickle\n",
"import sys\n",
"import time\n",
"\n",
"import matplotlib\n",
"import numpy as np\n",
"from pandas.io.parsers import read_csv\n",
"\n",
"matplotlib.use('TkAgg', warn = False)\n",
"import tensorflow as tf\n",
"from matplotlib import pyplot"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Функция для загрузки dataset'ов\n",
"def load_pickled_data(file, columns):\n",
" with open(file, mode='rb') as f:\n",
" dataset = pickle.load(f)\n",
" return tuple(map(lambda c: dataset[c], columns))\n",
"\n",
"# Функция для подсчета времени- понадобится при логировании времени обучения\n",
"def get_time_hhmmss(start = None):\n",
" if start is None:\n",
" return time.strftime(\"%Y/%m/%d %H:%M:%S\")\n",
" end = time.time()\n",
" m, s = divmod(end - start, 60)\n",
" h, m = divmod(m, 60)\n",
" time_str = \"%02d:%02d:%02d\" % (h, m, s)\n",
" return time_str"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Посмотрим на тренировочный и тестовый наборы данных"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"train_dataset_path = \"../../../data/german-traffic-signs/truncated/original/train.p\"\n",
"test_dataset_path = \"../../../data/german-traffic-signs/truncated/original/test.p\"\n",
"\n",
"X_train, Y_train = load_pickled_data(train_dataset_path, ['features', \n",
" 'labels'])\n",
"X_test, Y_test = load_pickled_data(test_dataset_path, ['features',\n",
" 'labels'])\n",
"n_train = len(Y_train)\n",
"n_test = len(Y_test)\n",
"image_shape = X_train[0].shape\n",
"image_size = image_shape[0]\n",
"sign_classes, class_indices, class_counts = np.unique(Y_train, return_index=True, return_counts=True)\n",
"n_classes = len(class_counts)\n",
"print(\"Число тренировочных изображений =\", n_train)\n",
"print(\"Число тестовых изображений =\", n_test)\n",
"print(\"Shape изображений =\", image_shape)\n",
"print(\"Число классов =\", n_classes)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"signnames = read_csv(\"../../../data/german-traffic-signs/truncated/signnames.csv\").values[:, 1]\n",
"\n",
"col_width = max(len(name) for name in signnames)\n",
"\n",
"for c, c_index, c_count in zip(sign_classes, class_indices, class_counts):\n",
" print(\"Класс %i: %-*s %s изображений\" %(c, col_width, signnames[c], str(c_count)))\n",
" fig = pyplot.figure(figsize = (6, 1))\n",
" fig.subplots_adjust(left = 0, right = 1, bottom = 0, top = 1, hspace = 0.05, wspace = 0.05)\n",
" random_indices = random.sample(range(c_index, c_index + c_count), 10)\n",
" for i in range(10):\n",
" axis = fig.add_subplot(1, 10, i + 1, xticks=[], yticks=[])\n",
" axis.imshow(X_train[random_indices[i]])\n",
" pyplot.show()\n",
" \n",
"fig = pyplot.figure(figsize = (9, 5))\n",
"\n",
"pyplot.bar( np.arange( n_classes ), class_counts, align='center' )\n",
"pyplot.xlabel('Класс')\n",
"pyplot.ylabel('Количество изображений')\n",
"pyplot.xlim([-1, n_classes])\n",
"pyplot.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В тренировочной выборке присутствуют 6 классов дорожных знаков, изображения в 5 из которых распределены примерно равномерно, за исключением 6-го класса, в котором всего лишь 300 объектов. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Видим, что изображения цветные – содержат три канала. Но наша модель будет распознавать изображения заранее обработанные – лишь с одним каналом.\n",
"\n",
"Исходные изображения находятся в цветовом пространстве RGB. Удобным способом снижения размерности является преобразование исходного цветного изображения в черно-белое изображение с помощью компоненты яркости Y. Y – одна из составляющих цветового пространства YCbCr.\n",
"\n",
"Обработанные данные уже содержатся в скачанном архиве, поэтому обработкой заниматься вам не придется."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"train_preprocessed_dataset_path = \"../../../data/german-traffic-signs/truncated/preprocessed/train_preprocessed.p\"\n",
"test_preprocessed_dataset_path = \"../../../data/german-traffic-signs/truncated/preprocessed/test_preprocessed.p\"\n",
"\n",
"X_train_preprocessed, Y_train_preprocessed = load_pickled_data(train_preprocessed_dataset_path, ['features', \n",
" 'labels'])\n",
"X_test_preprocessed, Y_test_preprocessed = load_pickled_data(test_preprocessed_dataset_path, ['features',\n",
" 'labels'])\n",
"image_shape = X_train_preprocessed[0].shape\n",
"image_size = image_shape[0]\n",
"print(\"Shape изображений =\", image_shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"col_width = max(len(name) for name in signnames)\n",
"\n",
"for c, c_index, c_count in zip(sign_classes, class_indices, class_counts):\n",
" print(\"Класс %i: %-*s %s изображений\" %(c, col_width, signnames[c], str(c_count)))\n",
" fig = pyplot.figure(figsize = (6, 1))\n",
" fig.subplots_adjust(left = 0, right = 1, bottom = 0, top = 1, hspace = 0.05, wspace = 0.05)\n",
" random_indices = random.sample(range(c_index, c_index + c_count), 10)\n",
" for i in range(10):\n",
" axis = fig.add_subplot(1, 10, i + 1, xticks=[], yticks=[])\n",
" axis.imshow(X_train_preprocessed[random_indices[i]].reshape(32, 32), cmap='gray')\n",
" pyplot.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Архитектура нашей сети будет иметь следующий вид\n",
"\n",
"