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

Наборы данных

В этом блоге используются следующие наборы данных:

  • Fashion-MNIST: состоит из обучающего набора из 60 000 примеров и тестового набора из 10 000 примеров. Каждый пример представляет собой изображение в градациях серого 28x28, связанное с меткой из 10 классов. С каждым пикселем связано одно значение пикселя, целое число от 0 до 255, указывающее на светлость или темноту этого пикселя, причем более высокие числа означают темнее. Наборы обучающих и тестовых данных содержат 785 столбцов. Первый столбец состоит из ярлыков классов и представляет предмет одежды. Остальные столбцы содержат значения пикселей связанного изображения.

  • Набор данных эмодзи. Этот набор данных содержит 2470 бинарных изображений нарисованных от руки смайликов. Каждый пример представляет собой изображение размером 32x32. Классы в этом наборе данных: Happy, Sad, Angry, Surprise и Poop!

  • Набор данных спутниковых изображений. Эта база данных состоит из 2000 спутниковых фотографий различных ландшафтов Мексики. Каждое изображение относится к одному из следующих классов: Вода, Лес, Город, Урожай, Пустыня или Гора. Важно отметить, что изображения также HD и цветные.

Извлечение признаков

Для набора данных Fasion-MNIST значения пикселей можно удобно использовать в качестве функций (которые мы позже будем использовать в качестве входных данных для нашей нейронной сети). Мы также преобразуем каждый пиксель в двоичный (черный или белый, 0 или 1), а не в оттенки серого. Хотя мы могли бы оставить их в оттенках серого, модель достаточно хорошо работает с бинарными (черными или белыми) изображениями. Чтобы преобразовать их в двоичные, мы должны масштабировать значения в диапазоне от 0 до 1 из наборов данных X.

# Scale the values to a range of 0 to 1 of both data sets
X_train = X_train / 255.0
X_test = X_test / 255.0

С другой стороны, для спутниковых изображений некоторые изображения слишком велики по размеру для эффективной работы нейронной сети. Поэтому изображения были уменьшены до более низкого разрешения 240 x 135 пикселей, а затем были извлечены такие функции, как цветовые гистограммы и матрицы совпадений, и преобразованы в два вектора, которые вместе сформировали значения матрицы X. Следующая функция обрабатывает каждое изображение из каждого класса (каждый класс находился в отдельной папке), а также разделяет нашу матрицу X.

ef build_x(folder):
  x = []
  
  scale = 8
  img_width = int(1920/scale)
  img_height = int(1080/scale)

  nbins =16
  
  for img in os.listdir(folder):

    #load
    full_path = folder+'/'+ img

    rgb = io.imread(full_path)
    rgb_resized = resize(rgb, (img_height, img_width), anti_aliasing=True)  

    #color histograms
    rh = np.histogram(rgb_resized[:,:,0].flatten(), nbins, density = True)
    gh = np.histogram(rgb_resized[:,:,1].flatten(), nbins, density = True)
    bh = np.histogram(rgb_resized[:,:,2].flatten(), nbins, density = True)

    hist_descriptor = np.concatenate((rh[0], gh[0], bh[0]))

    # Texture descriptors
    gray_resized = img_as_ubyte(rgb2gray(rgb_resized))
    glcm = greycomatrix(gray_resized, distances=[5], angles=[0, np.pi/4, np.pi/2, 3*np.pi/4])
    
    texture_desc = [greycoprops(glcm, 'dissimilarity')[0, 0], greycoprops(glcm, 'homogeneity')[0, 0], greycoprops(glcm, 'energy')[0, 0], greycoprops(glcm, 'correlation')[0, 0]]

    # Concatenate arrays
    row = np.concatenate((hist_descriptor, texture_desc))
    x.append(row)

  return(x)

Следующий код извлекает наш вектор y из данных и объединяет наши X’s (помните, что приведенная выше функция работает только для определенной папки или класса) вместе:

# Add labels
label_des = [1]* len(x_des)
label_mont = [2]* len(x_mont)
label_ci = [3]* len(x_ci)
label_bos = [4]* len(x_bos)
label_agua = [5]* len(x_agua)
label_cult = [6]* len(x_cult)


x_des =  np.column_stack((label_des, x_des))
x_mont =  np.column_stack((label_mont, x_mont))
x_ci =  np.column_stack((label_ci, x_ci))
x_bos =  np.column_stack((label_bos, x_bos))
x_agua =  np.column_stack((label_agua, x_agua))
x_cult =  np.column_stack((label_cult, x_cult))

# Add arrays

data = np.concatenate((x_des, x_mont, x_ci, x_bos, x_agua, x_cult))
x = data[:,1:]
y = data[:,0]

Реализация моделей

  • Линейный SVM и радиальный SVM:

Для реализации этих моделей настоятельно рекомендуется импортировать библиотеку sklearn. Используемый тип машины опорных векторов (SVM) был указан внутри параметра «ядро», которое получила модель, именно здесь было сделано различие между линейным или нелинейным классификатором; если ядро ​​указано как «линейное», это означает, что применяется линейный классификатор, а если ядро ​​указано как «rbf», это нелинейный классификатор. Для более математического различия между ними обратитесь к документации scikit-learn:

https://scikit-learn.org/stable/modules/svm.html

Одна и та же модель использовалась для всех трех наборов данных. Ниже вы можете найти фрагмент кода, в котором построена модель.

#Linear Model

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn import svm
from sklearn.model_selection import train_test_split, cross_validate, StratifiedKFold, KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, make_scorer, confusion_matrix

kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

scoring = {'accuracy' : make_scorer(accuracy_score), 
       'precision' : make_scorer(precision_score, average = "weighted"), 
       'recall' : make_scorer(recall_score, average = "weighted")}

# Note:  I used "weighted" in precision and recall which calculates metrics for each label, and find 
# their average weighted by support (the number of true instances for each label)

pipe = Pipeline([('scaler', StandardScaler()),  ('svm', svm.SVC(kernel = 'linear'))])
scores = cross_validate(pipe, x, y, cv=kfold, scoring= scoring, return_train_score= True)
print("Mean Accuracy: ", scores['test_accuracy'].mean())
print("Mean Precision: ", scores['test_precision'].mean())
print("Mean Recall: ", scores['test_recall'].mean())
scores

Многослойная нейронная сеть Perceptron: для реализации этих моделей использовалась библиотека «Keras». Функция активации, используемая в каждом скрытом слое, представляла собой функцию выпрямленной линейной активации (ReLu). Обратите внимание, что количество нейронов и количество слоев варьировались для каждой задачи, чтобы обеспечить высокую точность и высокую полноту для каждого класса. Также важно указать в начале количество входных данных (через параметр input_dim), которые будет иметь нейронная сеть, то есть количество столбцов, которые Xматрица имеет (количество признаков). Наконец, количество нейронов в выходном слое будет соответствовать количеству классов, по которым вы хотите классифицировать свои данные. Ниже приведен пример многослойного перцептрона, используемого с набором данных Emoji.

import keras
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense

n = len(class_names)
clf = Sequential()

clf.add(Dense(300, input_dim=x.shape[1], activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(300, activation='relu'))
clf.add(Dense(n, activation='softmax'))

Для каждой нейронной сети не забудьте указать тип используемого оптимизатора, во всех этих случаях был выбран оптимизатор «адам». Во-вторых, вы должны указать функцию потерь, которая вычисляет количество, которое модель должна стремиться минимизировать во время обучения. Поскольку мы работаем с моделями классификации, требуется вероятностная функция потерь, поэтому была выбрана «sparse_categorical_crossentropy». Последний параметр, который нужно определить, — это количество эпох, то есть количество раз, когда модель проходит набор обучающих данных. Для представленных решений количество эпох варьировалось и зависело от набора данных, используемого для каждой задачи.

clf.compile(optimizer='adam', 
            loss='sparse_categorical_crossentropy', 
            metrics=["accuracy"])

clf.fit(X_train, y_train, epochs=20, verbose=0)

Результаты

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

Набор данных MNIST о моде:

  • Линейный SVM:
Mean Accuracy: 0.8464
Recall Scores: [0.815, 0.962, 0.769, 0.842, 0.774, 0.936, 0.562, 0.934, 0.925, 0.945]
  • Радиальный SVM:
Mean Accuracy: 0.8828
Recall Scores: [0.857, 0.962, 0.816, 0.89 , 0.815, 0.951, 0.654, 0.955, 0.977, 0.951]
  • Многослойный персептрон:
Mean Accuracy: .8816999793052673
Recall Score: [0.85 , 0.968, 0.795, 0.897, 0.814, 0.95 , 0.647, 0.96 , 0.972, 0.964]

Набор данных эмодзи:

  • Линейный SVM:
Mean Accuracy:  0.7975708502024292
Mean Precision:  0.7994591419650259
Mean Recall:  0.7975708502024292
Recall:  [0.68602924 0.81986491 0.99595035 0.61673181 0.8226255 ]
  • Радиальный SVM:
Mean Accuracy:  0.8348178137651822
Mean Precision:  0.8335015727040787
Mean Recall:  0.8348178137651822
RECALL :  [0.74116076 0.8615258  1.   0.68873798 0.86274972]
  • Многослойный персептрон:
Accuracy =  0.8238866396761134
Recall =  [0.70776906 0.8574075  0.99629598 0.74150388 0.81498786]

Набор данных спутниковых изображений:

  • Линейный SVM:
Mean Accuracy:  0.865079969535415
Mean Precision:  0.8685106870579032
Mean Recall:  0.865079969535415
RECALL:  [0.9054896  0.86511701 0.90998437 0.87057464 0.85317846 0.73164635]
  • Радиальный SVM:
Mean Accuracy:  0.847213964572636
Mean Precision:  0.853908229483045
Mean Recall:  0.847213964572636
RECALL:  [0.92381119 0.8916909  0.88132593 0.84234372 0.84333833 0.70294995]
  • Многослойный персептрон:
Accuracy =  0.8467238287104145
Recall =  [0.95441784 0.76645569 0.93646669 0.90235906 0.83284262 0.69095238]

Сравнение результатов

Из приведенных выше результатов мы видим, что нейронная сеть почти всегда была на высоте с лучшим отзывом для каждого класса и лучшей точностью для каждой задачи. Кроме того, время вычислений для линейных моделей SVM и нелинейных моделей SVM намного превышает время нейронной сети. Для задач, в которых эти традиционные модели действительно превзошли нейронную сеть, это было лишь с небольшим отрывом, и если мы рассмотрим время вычислений, используемые традиционные методы были слишком медленными по сравнению с ними. Однако это функция не для всех традиционных методов, а только для SVM, которая, как известно, занимает больше времени, чем другие традиционные методы машинного обучения. Чтобы увидеть математическое объяснение, нажмите на следующую ссылку



Что касается результатов каждого набора данных, мы можем усреднить отзыв из каждой модели, чтобы увидеть, какие классы были отмечены лучше или хуже всего. В базе данных Fashion MNIST лучший показатель припоминания был получен для класса «брюки», а худший — для «рубашки».

В наборе Emoji Data самым высоким показателем запоминания по классу был «какашки», а худшим — ярлык «грустный». Возможно, очень четкие «какашки» на изображениях фекалий помогли моделям легче распознать эту метку, поскольку она сильно отличалась от других очертаний других классов. Тем не менее, «грустный» смайлик отделен от других классов очень небольшим числом активированных пикселей, поскольку контуры классов очень похожи.

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

Выводы

Из 3 баз данных, рассмотренных в этом блоге, мы видим, что традиционные методы машинного обучения для машин опорных векторов и нейронных сетей имели аналогичную производительность, но с очень разными характеристиками. Например, нейронная сеть была значительно быстрее, чем SVM, и ее показатель точности и показатель отзыва в целом не препятствовали. Интересным будущим проектом может быть изучение того, что произойдет, если мы обучим модели глубокой нейронной сети или модели сверточной нейронной сети с помощью этих баз данных. Возможно, большой объем данных, доступных в этих базах данных, принесет пользу Глубокой нейронной сети, поскольку она процветает на больших объемах данных. И, возможно, сверточная нейронная сеть будет процветать с большими объемами данных изображений (она могла бы особенно хорошо работать со спутниковой базой данных, поскольку их исходные размеры изображений больше и имеют цвет).