Хорошо, как и было обещано, вот первая часть (вторая, если считать обзор), посвященная первому, создавая модель для классификации изображений для наборов данных о дорожных знаках Бельгии.
Давайте попробуем разбить ее на заголовки. Я изо всех сил старался объяснить свой образ мышления, стоящий за принимаемыми решениями.

Код, которому мы будем следовать, можно найти в ядре kaggle здесь, а некоторые эксперименты, которые привели к этому финальному ядру, можно найти на моем github здесь. В нем есть несколько экспериментов, которые я проводил с трансферным обучением, но они не дали хороших результатов. Я все еще учусь и планирую дальнейшее улучшение модели, так что любые отзывы приветствуются!

Что ж, без дальнейших промедлений, давайте приступим!

Шаг 1 - Сбор данных

Выбор набора данных - самая важная часть процесса машинного обучения, поэтому будьте очень внимательны, поскольку он определяет направление работы приложения.

Мне потребовалось время, чтобы выбрать набор данных, но, к счастью для вас, вы можете найти набор данных здесь. Загрузите наборы изображений, предоставленные для Бельгии для классификации. В Kaggle есть набор данных трафика, если вы работаете в ядре. Но если вы работаете локально или в записной книжке Colab, вы можете извлечь файлы в соответствующие папки, используя следующий фрагмент:

Теперь, прежде чем мы сможем начать исследовать наш набор данных, нам нужно загрузить изображения в память. Мы делаем это с помощью метода imread, предоставляемого модулем Scikit-learn. Мы создаем метод, который принимает в качестве входных данных путь к каталогу и возвращает два массива numpy: один для изображений и один для соответствующих меток.
Для справки см. Суть ниже:

Шаг 2 - Изучение набора данных

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

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

Dimension of Image at index 0: (74, 83, 3) 
Label for Image at index 0:  1
Number of training Images : 4575 

Number of Classes :  62 
Some additional tidbits about the memory requirements of data 
Size of an individual image:  8

По размеру нашего набора меток мы видим, что входные изображения имеют 62 уникальных класса. Позже нам нужно будет создать метки для всех этих 62 классов.

Я попытался изобразить различные размеры изображения и взять как можно более близкий размер к наивысшей частоте, присутствующей в наборе данных. В текущем наборе данных высота Mode была 97, а ширина 84, что было немного меньше, чем ожидалось. Я решил пойти дальше с размером входного изображения 128x128. Этот размер может быть гиперпараметром в будущем, и с ним можно поэкспериментировать.

Затем мы преобразуем все наши изображения в размер 128x128, чтобы сохранить согласованность.

Теперь вместо того, чтобы использовать все изображения из наших обучающих папок для обучения, я объединил все изображения (обучающие + тестовые папки), а затем разделил их на 3 набора для обучения, проверки и тестовых данных.

Tensorflow также предоставляет нам утилиту Data-Generator, которая помогает обучать модели, применяя несколько преобразований к входным изображениям. Я применил преобразования к входному изображению, включая масштабирование, смещение центра (как по горизонтали, так и по вертикали) и сдвиг. Мы сохраняем режим заливки «ближайшим», чтобы он заполнял пустые пиксели их ближайшими доступными соседями. Мы используем следующий код для предварительного просмотра сгенерированных изображений.

Что-то, что можно попробовать позже: - Мы также можем попытаться уменьшить размерность наших изображений, преобразовав их в оттенки серого вместо RGB.

4. Определение модели

Теперь о самой захватывающей части, здесь мы можем попробовать разные архитектуры для нашей модели и посмотреть, какая из них работает лучше всего. Критериями приемки, которые я установил, были проверка и точность набора тестов ~ 90% для разделения данных по классам.

Я пробовал разные модели, начиная с минимально жизнеспособной модели без сверточных слоев. В результате точность набора тестов составила ~ 56%, что дало мне минимальный тест, который можно было постепенно улучшать.
Следующим шагом был переход к сверточной нейронной сети. Я пробовал несколько архитектур с разным количеством слоев и количеством узлов активации. Чтобы получить обзор сверточных нейронных сетей и математических расчетов, стоящих за ними, вы можете посмотреть эти ресурсы здесь и здесь.

Потери после 30 итераций (20 + 5) показали более высокую точность на обучающем наборе по сравнению с проверочным набором. Это могло свидетельствовать о предвзятости модели.

После стагнации производительности около пикового значения ~ 88% пришло время обратиться к всемогущим властителям регуляризации, чтобы уменьшить предвзятость, присутствующую в нашей модели. И вот, наш верный спаситель Dropout пришел нам на помощь. Вы должны поместить слой с выпадением после плотных слоев. При тестировании текущая модель показала лучшие результаты со значением Dropout 0,5.

Прежде чем мы начнем обучать модель, давайте поговорим о небольшом объекте, называемом обратными вызовами! Обратный вызов позволяет нам прервать процесс обучения модели в зависимости от журналов, записанных для каждой эпохи или шага цикла обучения.
Есть несколько готовых простых в использовании обратных вызовов, предоставленных нам Keras, включая обратный вызов Early Stopping. Чтобы использовать его, мы определяем новый обратный вызов, предоставляя значения для атрибутов, как описано ниже:

  • monitor: определите, какое свойство в журнале эпох вы хотите, чтобы обратный вызов
    отслеживал.
  • терпение: определяет, сколько эпох вычислять дальше / ждать после того, как будет обнаружено минимальное изменение в отслеживаемом значении.
  • min_delta: определяет изменение отслеживаемого значения, после которого следует остановить обучение, если не указано значение терпения. (в этом случае прекращается после терпения, если не обнаружено новое изменение min_delta)
  • restore_best_weights: восстанавливает веса эпохи, когда min_delta была нарушена.

Теперь давайте начнем обучение и подождем ……
Вы должны определить свои обратные вызовы, чтобы прекратить их, когда потеря проверки начнет расти, как определено в обратном вызове выше, или вы достигли точности, близкой к вашим целям.

Как вы можете видеть выше, точность модели значительно увеличилась примерно до ~ 96% на тестовом наборе и 88% на классовом, равномерно распределенном наборе тестовых изображений.
Это указывает на то, что некоторые классы трудно предсказать для модели, тогда как для другой части входных классов она показывает высокую точность. Модель может быть подвергнута дальнейшей доработке для повышения ее точности. Но пока я решил заморозить эту модель и начать следующий этап разработки модели. Я с нетерпением жду, когда вы расскажете мне о том, как вы улучшили модель!

Визуализация потерь

Не можем забыть визуализировать потери, не так ли? Для этого не забудьте сохранить результаты обучения модели в объекте (здесь история), и мы можем использовать это для визуализации наших потерь при проверке и обучении. Если вы не используете ядро ​​kaggle, обратитесь к приведенному ниже коду.

Сохраняем нашу модель на потом

У меня было несколько доступных способов сохранить модель для будущего использования. На данный момент мы сохраняем модель как файл H5, используя методы «save», предоставляемые самой моделью.

# SAVE THE MODEL AS H5 File
model_regularized.save('final_model.h5')

Ядро также экспериментирует с сохранением, используя библиотеки Saved Model и Tensorflow JS.

Некоторые идеи, которые удалось воплотить в жизнь

Стратифицированное разделение

Позже я узнал о несоответствии производительности модели на тестовых данных. Это может быть из-за неравных экземпляров классовых изображений, то есть большее количество изображений одного класса заставляет модель работать лучше для этого класса. Это, в свою очередь, делает тестовые примеры смещенными в сторону тех классов, которые обеспечивают лучшую производительность в тестовом наборе.
Возможным решением проблемы было бы разделение всех изображений на наборы для обучения, проверки и тестирования на основе классов. То есть мы гарантируем, что все 62 наших класса одинаково представлены во всех трех разделах набора данных.

Бонусный метод тестирования изображений для вашей модели прямо из блокнота colab

def predict_image(model):
  uploaded = files.upload()
  
  for file_name in uploaded.keys():
    # path of the image  
    path = file_name
    
    img = tf.keras.preprocessing.image.load_img(path, target_size = (128,128))  # no color mode as it defaults to rgb loads a PIL image instance
    
    img_arr = tf.keras.preprocessing.image.img_to_array(img)  # converts a PIL image to a 3D numpy array
    show_img = img
    #print('Input image shape :{}'.format(img_arr.shape))
    img_arr = np.expand_dims(img_arr,axis=0)                # change shape to match expected input 
    #print('Input image shape :{}'.format(img_arr.shape))
    
    classes = model.predict(img_arr,batch_size=16)
    #print( 'for Image : {}, Dimensions of Predicted Classes : {}, Classes zero index value : {}'.format(path,classes.shape,classes[0]))
    plt.imshow(show_img,vmin= 0,vmax=255)
    plt.axis('off')
    plt.show()
    predicted_class = np.argmax(classes)
    print('The Image belongs to class :{}, with the description : {}'.format(predicted_class,classnames[predicted_class]))

Привет! Ты сделал это! Я признаю, что на этот раз содержание было немного запутанным. Но я исследовал и многому научился за последние несколько недель, пытаясь создать модель. Надеемся, что сегодня вы узнали что-то новое! Поделитесь своим прогрессом и мыслями ниже.