Время прочтения: 5 мин.

В рамках дипломного проекта, мне необходимо создать программу, которая сможет классифицировать различные дорожные знаки. В идеале будет, если она сможет отличать все знаки, которые мы можем встретить на дорогах общего пользования. Но так как их больше 100, то добиться удовлетворимой точности будет очень трудно, а затраты на подготовку датасета займут несоизмеримое количество времени. Поэтому за основу я возьму 3 распространённых дорожных знака, чтобы на их примере оценить работоспособность и в дальнейшем принимать решения по улучшению нейронки.

Итак, я буду классифицировать следующие знаки: пешеходный переход, главная дорога и ограничения скорости. Повезло найти датасет с 600 000 изображений на сайте Kaggle (https://www.kaggle.com/dmitryyemelyanov/chinese-traffic-signs).  В Наборе представлено огромное количество уже сегментированных изображений, поэтому среди этого количества я выбрал для себя по 1000 изображений для каждого из своих трёх классов.

Как я уже писал в статье про аугментацию датасета, есть возможность увеличить набор данных за счёт применения различных преобразований фотографий. Я решил использовать только отзеркаливание, так как 2000 изображений, я считаю, будет достаточно. Также сразу приведу все изображения к одному размеру. Код для этих преобразований ниже.

print('Start_program')
resize_and_rescale = tf.keras.Sequential([
  # layers.Rescaling(1./255),
  layers.RandomFlip("horizontal")
])

directory = 'cross_walk/'
finish = 'train/transform_cross_walk'
images = os.listdir(path=directory)
print('Найдено ' + str(len(images)) + ' файлов')

i = 0
for name in images:
    i += 1

    image = cv.imread(directory + name)

    try:

        image = cv.resize(image, (100, 100))
        img = cv.cvtColor(image, cv.COLOR_BGR2RGB)

        img = tf.convert_to_tensor(img)
        result = resize_and_rescale(img)
        cv.imwrite(finish + 'resize' + name, image)
        tf.keras.preprocessing.image.save_img(finish + 'mirror' + name, result)

    except:

        print(i)

    if i % 100 == 0:
        print('Обработано ' + str(i) + ' изображений и сохранено в папку ' + finish)

На выходе у меня получается по 2000 изображений для каждого класса. Несколько примеров привёл ниже.

 Следующее обучение я проводил в Google Colab (https://colab.research.google.com/drive/1Z5iu6G1dyEwA7G8BzUcaefXrqqos4Bed). Весь датасет я загружал как один архивный файл, который разархивируется стандартными командами консоли Linux.

!ls
!unrar x train.rar

В tensotflow есть утилита для создания датасета из директории. Для этого я заранее распределил все классы по трём разным папкам. Разделил тренировочный и проверочный наборы в соотношении 8:2, то есть 80% тренировочных и 20% для проверки.

train_ds = tf.keras.utils.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)

val_ds = tf.keras.utils.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="validation",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size

Также можно получить метки классов.

class_names = train_ds.class_names
print(class_names)

Я выбрал свёрточную модель для построения с чередующимися слоями Conv2D и Maxpooling2D. Также очень важен слой Dropout, который помогает не допустить переобучения.

num_classes = 3

model = Sequential([
  layers.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Dropout(0.1),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])

В качестве активации нейрона выбрана функция relu. Метрика точности – аккуратность. Стандартная метрика в задачах классификации. Для неё очень важно, чтобы классы были сбалансированы. Я учёл этот нюанс при создании датасета, поэтому могу использовать при обучении. Ну и самый долгий процесс – обучение. Я поставил 5 эпох, чтобы не допускать переобучения.

В процессе обучения на проверочной выборке точность достигает 99,5%. Я не ожидал настолько хорошего результата. Потери при этом остаются порядка 0.01, что не самые маленькие значения. Но, с другой стороны, потери равные 10-5 скорее будут сигнализировать о переобучении, нежели о реальной точности. Теперь для того, чтобы убедиться в точности стоит протестировать на тестовом наборе данных. У меня для этого было 9 фото. И опять к моему удивлению, 100% результат. Модель верно распознала все 9 фото.

Также решил проверить с какой вероятностью модель принимает решение о присвоении класса на одном из тестовых изображений.

Собственно, модель с уверенностью в 100% присваивает верную метку класса. Меня это очень радует, и есть смысл продолжить работать с этой моделью. В планах остаётся собрать еще 2 класса: уступи дорогу и кирпич. Думаю, на 5 классах модель не сильно потеряет в точности.

          Спасибо всем за внимание и желаю вам удачи в повторении кода из этой статьи и в изучении глубокого обучения.