Целью данного проекта является:
- Создайте помеченный набор данных изображений Мстителей - Капитан Америка, Железный Человек, Черная Вдова, Халк, Тор.
- Обучите CNN, способную классифицировать невидимое изображение с разумной точностью.
По сути, существует 4 основных этапа обработки данных в классификаторе изображений. Эти:
- Сбор данных
- Предварительная обработка данных
- Извлечение функций
- Модельное обучение
Теперь давайте рассмотрим пошаговую реализацию и различные доступные методы. Будет выбран наиболее подходящий метод.
Примечание. В этом проекте мы используем Google Colab, но можно использовать любое из доступных программ.
Шаг: 1 - Сбор данных
В этом проекте нам нужно собирать данные в виде изображений. Изображения могут быть получены путем ручного удаления отдельных (статических и / или динамических) веб-сайтов. Но поскольку требуется большое количество изображений, многие веб-сайты придется отказаться. Вместо того, чтобы вручную переходить на разные веб-сайты, можно выбросить изображения Google или Bing.
Способов удаления изображений может быть много. Три из них широко используются:
- Использование инструментов python и веб-скраппинга для автоматизации загрузки определенных типов изображений напрямую из Google. Этот метод является совершенно незаконным, поэтому веб-сайты и сам Google следят за тем, чтобы эти поисковые роботы не работали. По этой причине коды необходимо регулярно обновлять.
- Используя расширения браузера Chrome, такие как fatkun. Эти расширения гораздо более стабильны в использовании, чем предыдущий метод. Но в соответствии с требованиями этого проекта изображения должны быть удалены из Интернета.
- Использование инструментов Python, таких как Bing Image Downloader, для прямого экспорта необходимых изображений в каталог.
В этом проекте для удобства используется Bing Image Downloader.
Сначала мы установим загрузчик и импортируем необходимые библиотеки: -
!pip install bing-image-downloader from bing_image_downloader import downloader
Теперь он используется для удаления изображений с помощью следующих строк кода: -
downloader.download('Captain America Chris Evans', output_dir= './drive/MyDrive/datasets/collection', limit = 400, adult_filter_off = False, force_replace = False, timeout = 6000)
Аналогичным образом удаляются изображения Железного человека, Тора, Халка и Черной вдовы.
Шаг: 2 - Предварительная обработка данных
Этот шаг очень важен для выполнения классификации изображений. Это увеличивает общую эффективность алгоритма.
Этот шаг необходим в этом проекте, потому что после сбора данных было обнаружено, что было собрано много неважных изображений из комиксов. Эти типы изображений приведут к потреблению большего количества ресурсов системы. Более того, классификатор может ошибаться. Таким образом, эти изображения не должны использоваться напрямую.
В этом проекте OpenCV и метод под названием каскады хаара, которые используются для очистки данных. Они определят, хорошо ли видны лицо и два глаза. Если они видны, то изображение сохраняется, в противном случае изображение отбрасывается. Большая часть работы по очистке данных будет выполняться с использованием кода Python, но будет некоторая работа по очистке, которую придется выполнять вручную. Для удаления нежелательных лиц требуется ручная проверка изображений. Например, в папке «Железный человек» могут появиться лица других персонажей, что снижает эффективность Модели.
Шаги по очистке данных:
- Лица с двумя глазами извлекаются из необработанных изображений с помощью каскада Хаара.
- Снимки с двумя или более лицами вручную отбрасываются. Также фотографии с размытыми фотографиями и др.
Краткое описание функционирования и использования Haar Cascade: -
Каждое изображение имеет линии и края. Хаар Каскад использует движущееся окно этих краевых элементов, чтобы определить, где находятся глаза и лицо.
Например, чтобы обнаружить глаза, область глаз имеет тенденцию быть более темной, чем область ниже. Haar Cascades используют эту маску для обнаружения областей.
OpenCV имеет готовые API для обнаружения лиц, глаз и т. Д. 17 различных XML-файлов для запуска API загружаются вручную для использования каскадных функций haar для обнаружения различных функций.
Теперь давайте импортируем необходимые библиотеки и создадим функции для использования функций каскада лица и каскада глаз: -
import numpy as np import cv2 import matplotlib from matplotlib import pyplot as plt %matplotlib inline face_cascade = cv2.CascadeClassifier("./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_frontalface_default.xml") eye_cascade = cv2.CascadeClassifier("./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_eye.xml")
Давайте проведем пробный запуск, чтобы убедиться, что функции работают правильно или нет:
img = cv2.imread('/content/drive/My Drive/datasets/collection/Thor Chris Hemsworth/Image_8.jpg') #img.shape plt.imshow(img) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray.shape plt.imshow(gray, cmap='gray') face_cascade = cv2.CascadeClassifier('./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_frontalface_default.xml') eye_cascade = cv2.CascadeClassifier('./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_eye.xml') faces = face_cascade.detectMultiScale(gray) faces
Эти строки кода проверяют, есть ли у изображения лицо и два глаза. Если у него нет лиц или глаз, он вернет ошибку. Если изображение было, то оно вернуло результат, аналогичный показанному ниже: -
Теперь, поскольку функции работают правильно, давайте напишем код для проверки всех изображений в наборе данных. Изображения, соответствующие требованиям, будут преобразованы в серый цвет, а затем обрезаны. Эти изображения сохраняются в отдельной папке для использования в будущем.
def get_cropped_image_if_2_eyes(image_path): img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(roi_gray) if len(eyes) >= 2: return roi_color path_to_data = "./drive/My Drive/datasets/collection/" path_to_cr_data = "./drive/My Drive/datasets/cropped/" import os img_dirs = [] for entry in os.scandir(path_to_data): if entry.is_dir(): img_dirs.append(entry.path) import shutil if os.path.exists(path_to_cr_data): shutil.rmtree(path_to_cr_data) os.mkdir(path_to_cr_data) cropped_image_dirs = [] celebrity_file_names_dict = {} for img_dir in img_dirs: count = 1 celebrity_name = img_dir.split('/')[-1] print(celebrity_name) celebrity_file_names_dict[celebrity_name] = [] for entry in os.scandir(img_dir): roi_color = get_cropped_image_if_2_eyes(entry.path) if roi_color is not None: cropped_folder = path_to_cr_data + celebrity_name if not os.path.exists(cropped_folder): os.makedirs(cropped_folder) cropped_image_dirs.append(cropped_folder) print("Generating cropped images in folder: ",cropped_folder) #Checking whether the code is running successfully or not cropped_file_name = celebrity_name + str(count) + ".png" #changing file type of every image to png cropped_file_path = cropped_folder + "/" + cropped_file_name cv2.imwrite(cropped_file_path, roi_color) celebrity_file_names_dict[celebrity_name].append(cropped_file_path) count += 1
Шаг: 3 - Извлечение признаков с помощью вейвлет-преобразования
Важность этого шага заключается в том, что при извлечении функций цветные изображения вызывают множество ошибок. Цветные изображения могут иметь множество оттенков и цветов, что затрудняет определение такого изображения для классификатора.
Чтобы избежать этих ошибок, изображения преобразуются в черный и белый цвета с разным контрастом для разных областей. Вейвлет-преобразование позволяет извлекать из изображения важные особенности. В общем, на изображении, преобразованном вейвлет, область глаз будет отличаться от области лба, нос также будет отличаться и так далее.
Изучая литературу по обработке изображений, было обнаружено, что вейвлет-преобразования часто являются наиболее эффективным способом извлечения. Итак, в этом проекте используется вейвлет-преобразование.
После ввода изображения он выполнит вейвлет-преобразование поверх него, используя PYWT (библиотека вейвлет-преобразования pi), и вернет ваше новое изображение, которое является вейвлет-преобразованием. Для применения вейвлет-преобразования в основных кодах использовались концепции обработки сигналов, частотной области, временной области, преобразования Фурье. Ниже кратко объясняются некоторые из этих концепций:
Любой сигнал, как и звуковой сигнал, изображение также можно рассматривать как сигнал. Он может быть представлен в домене двух типов. Таким образом, изображение может быть представлено в пространственной области, такой как пространство (x и y), или может быть представлено как частотная область. Аудиосигнал может быть представлен во временной или частотной области.
Преобразование Фурье принимает сложный сигнал и возвращает базовые сигналы, которые образуют этот сложный сигнал. Например, давайте рассмотрим какое-нибудь блюдо, допустим, Доса. Если провести обратный инжиниринг на Dosa, будут получены основные ингредиенты: вода, рисовая мука, урад-дал и, возможно, другие ингредиенты.
Аналогичный случай со сложным сигналом, когда играют разные инструменты и есть шум. Есть много устройств шумоподавления, так как же они на самом деле подавляют шум? Это делается с использованием преобразования Фурье, потому что оно может отделить голос голосовой связки от шума. Он может разделить все эти сигналы на разные частоты и с помощью частотных фильтров можно подавить некоторые частоты или усилить их. Определенные частоты некоторых аудиоустройств, высокие или низкие частоты могут быть увеличены. Все это возможно благодаря преобразованию Фурье.
Вейвлет-преобразование похоже на преобразование Фурье, которое усиливает определенные особенности изображения.
Для дальнейших шагов входом будет вертикально сложенное цветное изображение и его преобразованное вейвлет-преобразование изображение. Код для которого выглядит следующим образом:
import numpy as np import pywt import cv2 def w2d(img, mode='haar', level=1): imArray = img #Datatype conversions #convert to grayscale imArray = cv2.cvtColor( imArray,cv2.COLOR_RGB2GRAY ) #convert to float imArray = np.float32(imArray) imArray /= 255; # compute coefficients coeffs=pywt.wavedec2(imArray, mode, level=level) #Process Coefficients coeffs_H=list(coeffs) coeffs_H[0] *= 0; # reconstruction imArray_H=pywt.waverec2(coeffs_H, mode); imArray_H *= 255; imArray_H = np.uint8(imArray_H) return imArray_H
Давайте назначим номер (или клавишу) каждому из 5 символов.
class_dict = {} count = 0 for celebrity_name in celebrity_file_names_dict.keys(): class_dict[celebrity_name] = count count = count + 1 class_dict
{«Черная вдова Скарлетт Йоханссон»: 2,
«Капитан Америка Крис Эванс»: 0,
«Халк Марк Руффало»: 3,
«Железный человек Тони Старк»: 1,
'Тор Крис Хемсворт': 4}
Создание словаря для ссылки на путь всех обрезанных изображений соответствующих символов:
celebrity_file_names_dict = {} for img_dir in cropped_image_dirs: celebrity_name = img_dir.split('/')[-1] file_list = [] for entry in os.scandir(img_dir): file_list.append(entry.path) celebrity_file_names_dict[celebrity_name] = file_list
Теперь давайте создадим словарь, в котором цветные изображения уложены вертикально вместе с изображениями, преобразованными вейвлет, для будущего использования.
X, y = [], [] for celebrity_name, training_files in celebrity_file_names_dict.items(): for training_image in training_files: img = cv2.imread(training_image) if img is None: continue scalled_raw_img = cv2.resize(img, (32, 32)) #resizing using openCV as images maybe of different sizes img_har = w2d(img,'db1',5) #getting the wavelet transformed image scalled_img_har = cv2.resize(img_har, (32, 32)) #resizing wavelet transformed image combined_img = np.vstack((scalled_raw_img.reshape(32*32*3,1),scalled_img_har.reshape(32*32,1))) #vertically stacking both the images X.append(combined_img) y.append(class_dict[celebrity_name]) X = np.array(X).reshape(len(X),4096).astype(float)
Шаг 4 - Обучение модели: использование SVM с эвристической тонкой настройкой
В этом проекте сначала SVM используется для обучения основной модели.
Затем с помощью GridSearch тестируются другие модели, чтобы решить, какая модель лучше всего подходит для проекта.
GridSearch CV используется для параметров Hypertuning. Это помогает решить, какая модель работает лучше всего.
В нашем проекте мы определяем модели-кандидаты для сравнения следующим образом:
- SVM с параметрами as - Значения C равны 1,10,100,1000, а значения ядра - rbf и linear.
- Случайный лес с параметрами as - Количество оценок (или деревьев решений) 1,5,10.
- Логистическая регрессия с параметрами as - Значения C равны 1,5,10.
Наконец, лучшая модель сохраняется в «Trained Model.pkl», а также сохраняется словарь классов.
Код для обучения SVM:
from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.metrics import classification_report X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) #Pipeline is created to scale the Data. pipe = Pipeline([('scaler', StandardScaler()), ('svc', SVC(kernel = 'rbf', C = 10))]) pipe.fit(X_train,y_train) pipe.score(X_test,y_test)
Это дало 0,9770992366412213 баллов.
А теперь давайте получим полный отчет о классификации.
print(classification_report(y_test, pipe.predict(X_test)))
Это дало следующий результат:
Обучение и тестирование других моделей, как упоминалось ранее:
from sklearn import svm from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline from sklearn.model_selection import GridSearchCV model_params = { 'svm': { 'model': svm.SVC(gamma='auto',probability=True), 'params' : { 'svc__C': [1,10,100,1000], 'svc__kernel': ['rbf','linear'] } }, 'random_forest': { 'model': RandomForestClassifier(), 'params' : { 'randomforestclassifier__n_estimators': [1,5,10] } }, 'logistic_regression' : { 'model': LogisticRegression(solver='liblinear',multi_class='auto'), 'params': { 'logisticregression__C': [1,5,10] } } } scores = [] best_estimators = {} import pandas as pd for algo, mp in model_params.items(): pipe = make_pipeline(StandardScaler(), mp['model']) clf = GridSearchCV(pipe, mp['params'], cv=5, return_train_score=False) # cv=5 => There will be 5 folds of testing the model and # then will avereage out the scores clf.fit(X_train, y_train) # Scores are appended and a data frame is created from it scores.append({ 'model': algo, 'best_score': clf.best_score_, 'best_params': clf.best_params_ }) best_estimators[algo] = clf.best_estimator_ df = pd.DataFrame(scores,columns=['model','best_score','best_params']) df
Итоговый отчет о полученных баллах выглядит следующим образом:
Эти оценки были на данных обучения. Теперь давайте получим оценки моделей по данным тестирования:
best_estimators['svm'].score(X_test,y_test) best_estimators['random_forest'].score(X_test,y_test) best_estimators['logistic_regression'].score(X_test,y_test)
Полученные оценки были следующими:
SVM: 0,9770992366412213
Случайный лес: 0,9389312977099237
Логистическая регрессия: 0,9847328244274809
Как было замечено, SVM хорошо работает как с данными обучения, так и с данными тестирования, поэтому в этом проекте будут использоваться SVM.
best_clf = best_estimators['svm']
Теперь рисуем матрицу путаницы:
from sklearn.metrics import confusion_matrix cm = confusion_matrix(y_test, best_clf.predict(X_test)) import seaborn as sn plt.figure(figsize = (10,7)) sn.heatmap(cm, annot=True) plt.xlabel('Predicted') plt.ylabel('Truth')
Сохранение модели в соответствующем файле pkl для будущего использования при создании веб-приложений и т. Д.
!pip install joblib import joblib # Save the model as a pickle in a file joblib.dump(best_clf, 'saved_model.pkl')
Сохранение словаря в виде файла Json для использования в будущем:
import json with open("class_dictionary.json","w") as f: f.write(json.dumps(class_dict))
Успешно создана модель, которую теперь можно использовать для создания веб-сайтов.
Ссылка на полный код: Github
Спасибо, что потратили драгоценное время на чтение этой статьи. Сообщите мне свои мнения и предложения в комментариях.