from typing import Tuple
from tqdm import tqdm
import gdown
import zipfile
import os
import random
import shutil
from sklearn.model_selection import train_test_split, GridSearchCV, RandomizedSearchCV
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score, f1_score, classification_report
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import cv2
import numpy as np
RANDOM_STATE = 42
random.seed(RANDOM_STATE)
file_id = "1EbvcZbzVXSmB2N1SZYNeUfUuXb8wp3-k"
gdown.download(f"https://drive.google.com/uc?id={file_id}", "dataset.zip", quiet=False)
zip_file_name = "dataset.zip"
os.makedirs("dataset", exist_ok=True)
with zipfile.ZipFile(zip_file_name, "r") as zip_ref:
zip_ref.extractall("dataset")
Downloading... From (original): https://drive.google.com/uc?id=1EbvcZbzVXSmB2N1SZYNeUfUuXb8wp3-k From (redirected): https://drive.google.com/uc?id=1EbvcZbzVXSmB2N1SZYNeUfUuXb8wp3-k&confirm=t&uuid=1ca57790-9764-4c45-bb7c-5a47b993b9eb To: /content/dataset.zip 100%|██████████| 652M/652M [00:04<00:00, 146MB/s]
DATASET_DIR = "./dataset"
TEMP_DATASET_DIR = "./temp_dataset"
SIZE_IMG = {"64px": 64, "100px": 100, "128px": 128, "192px": 192}
def get_SIFT_descriptors(img):
sift = cv2.SIFT_create()
keypoints, descriptors = sift.detectAndCompute(img, None)
return descriptors
Сделаем ограничение на количество дискрипторов в 128 шт. По опыту других ниболее оптимальный выбор. И так как в картинках может встретиться разное количество признаков, то выполним преобразование:
- Если количество дескрипторов меньше num_features, функция вычисляет среднее значение всех дескрипторов и присваивает его вектору признако
- Если количество дескрипторов больше или равно num_features, функция берет только первые num_features дескрипторов и вычисляет их среднее значение
def create_feature_vector(descriptors, num_features=128):
feature_vector = np.zeros(num_features)
if descriptors is not None and len(descriptors) > 0:
if descriptors.shape[0] < num_features:
feature_vector = np.mean(descriptors, axis=0)
else:
feature_vector = np.mean(descriptors[:num_features], axis=0)
return feature_vector
Функция предназначена для перебора всех картинок с датасета, преобразования их к типу SIFT и получения дискрипторов и названий классов.
def analyze_dataset(image_folder, size_img):
features = []
labels = []
for class_name in os.listdir(image_folder):
class_path = os.path.join(image_folder, class_name)
if os.path.isdir(class_path):
for filename in tqdm(os.listdir(class_path), desc=f"Обработка {class_name}", unit="image"):
if filename.endswith(".jpg") or filename.endswith(".jpeg"):
image_path = os.path.join(class_path, filename)
img = cv2.imread(image_path)
img = cv2.resize(img, (size_img, size_img), interpolation=cv2.INTER_CUBIC)
descriptors = get_SIFT_descriptors(img)
feature_vector = create_feature_vector(descriptors)
features.append(feature_vector)
labels.append(class_name)
features_array = np.array(features)
labels_array = np.array(labels)
return features_array, labels_array
Так как датасет довольно большой и модели обучаются долго, для тестирования и выбора наиболее подходящей модели имеет смысл сократить датасет. Я его помещу в отдельную папку temp_dataset. Изначально было 1400 изображений каждого класса, сокращу их до 500.
def reduce_dataset(source_dir, temp_dir, num_images):
if not os.path.exists(temp_dir):
os.makedirs(temp_dir)
for class_name in os.listdir(source_dir):
class_folder = os.path.join(source_dir, class_name)
if os.path.isdir(class_folder):
target_class_folder = os.path.join(temp_dir, class_name)
os.makedirs(target_class_folder, exist_ok=True)
all_images = os.listdir(class_folder)
selected_images = random.sample(all_images, min(num_images, len(all_images)))
for image in selected_images:
shutil.copy(os.path.join(class_folder, image), os.path.join(target_class_folder, image))
print(f"Скопировано {len(selected_images)} изображений для класса {class_name} в {target_class_folder}")
!rm -r temp_dataset
reduce_dataset(DATASET_DIR, TEMP_DATASET_DIR, 500)
Скопировано 500 изображений для класса Pumpkin в ./temp_dataset/Pumpkin Скопировано 500 изображений для класса Nut в ./temp_dataset/Nut Скопировано 500 изображений для класса Apple в ./temp_dataset/Apple Скопировано 500 изображений для класса Cauliflower в ./temp_dataset/Cauliflower Скопировано 500 изображений для класса Bottle_Gourd в ./temp_dataset/Bottle_Gourd Скопировано 500 изображений для класса Pepper в ./temp_dataset/Pepper Скопировано 500 изображений для класса Peach в ./temp_dataset/Peach Скопировано 500 изображений для класса Mango в ./temp_dataset/Mango Скопировано 500 изображений для класса Stawberries в ./temp_dataset/Stawberries Скопировано 500 изображений для класса Brinjal в ./temp_dataset/Brinjal Скопировано 500 изображений для класса Bitter_Gourd в ./temp_dataset/Bitter_Gourd Скопировано 500 изображений для класса Radish в ./temp_dataset/Radish Скопировано 500 изображений для класса Potato в ./temp_dataset/Potato Скопировано 500 изображений для класса Cucumber в ./temp_dataset/Cucumber Скопировано 500 изображений для класса Avocado в ./temp_dataset/Avocado Скопировано 500 изображений для класса Pinenapple в ./temp_dataset/Pinenapple Скопировано 500 изображений для класса Banana в ./temp_dataset/Banana Скопировано 500 изображений для класса Carrot в ./temp_dataset/Carrot Скопировано 500 изображений для класса Pear в ./temp_dataset/Pear Скопировано 500 изображений для класса Plum в ./temp_dataset/Plum Скопировано 500 изображений для класса Capsicum в ./temp_dataset/Capsicum Скопировано 500 изображений для класса Grape в ./temp_dataset/Grape Скопировано 500 изображений для класса Cherry в ./temp_dataset/Cherry Скопировано 500 изображений для класса Orange в ./temp_dataset/Orange Скопировано 500 изображений для класса Bean в ./temp_dataset/Bean Скопировано 500 изображений для класса Cabbage в ./temp_dataset/Cabbage Скопировано 500 изображений для класса Strawberry в ./temp_dataset/Strawberry Скопировано 500 изображений для класса Broccoli в ./temp_dataset/Broccoli Скопировано 500 изображений для класса Onion в ./temp_dataset/Onion Скопировано 500 изображений для класса Tomato в ./temp_dataset/Tomato Скопировано 500 изображений для класса Watermelon в ./temp_dataset/Watermelon Скопировано 500 изображений для класса Papaya в ./temp_dataset/Papaya Скопировано 500 изображений для класса Kiwi в ./temp_dataset/Kiwi
Сильного улучшения не увидел, только метрики прям плохие, поэтому отказываюсь от этой идеи. Буду обучать на полном датасете. Анализ проводил в IDE, там все это дело быстрее. Буду благодарен если научить в colabe ускорять этот процесс
Визуализация признаков на изображениях¶
Проведу анализ поиска признаков на цветных и чб изображениях и сделаю предположения, как влияет размер и цвет на выделение признаков
# функция для получения признаков по всем каналам изображения RGB
def color_sift(img):
channels = cv2.split(img)
keypoints_all = []
descriptors_all = []
sift = cv2.SIFT_create()
for channel in channels:
keypoints, descriptors = sift.detectAndCompute(channel, None)
keypoints_all.extend(keypoints)
if descriptors is not None:
descriptors_all.append(descriptors)
if descriptors_all:
descriptors_combined = np.vstack(descriptors_all)
else:
descriptors_combined = None
return keypoints_all, descriptors_combined
def show_sift_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_64 = cv2.resize(img, (64, 64), interpolation=cv2.INTER_CUBIC)
img_100 = cv2.resize(img, (100, 100), interpolation=cv2.INTER_CUBIC)
img_128 = cv2.resize(img, (128, 128), interpolation=cv2.INTER_CUBIC)
sift = cv2.SIFT_create()
keypoints_rgb, descriptors_rgb = color_sift(img)
keypoints_gray, descriptors_gray = sift.detectAndCompute(img_gray, None)
keypoints_64, descriptors_gray_64 = sift.detectAndCompute(img_64, None)
keypoints_100, descriptors_gray_100 = sift.detectAndCompute(img_100, None)
keypoints_128, descriptors_gray_128 = sift.detectAndCompute(img_128, None)
sift_image_rgb = cv2.drawKeypoints(img, keypoints_rgb, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
sift_image_gray = cv2.drawKeypoints(
img_gray, keypoints_gray, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)
sift_image_64 = cv2.drawKeypoints(img_64, keypoints_64, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
sift_image_100 = cv2.drawKeypoints(img_100, keypoints_100, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
sift_image_128 = cv2.drawKeypoints(img_128, keypoints_128, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
plt.figure(figsize=(10, 5))
plt.subplot(1, 6, 1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title("исходник")
plt.subplot(1, 6, 2)
plt.imshow(cv2.cvtColor(sift_image_rgb, cv2.COLOR_BGR2RGB))
plt.title("в цвете")
plt.subplot(1, 6, 3)
plt.imshow(sift_image_gray, cmap="gray")
plt.title("в ЧБ")
plt.subplot(1, 6, 4)
plt.imshow(cv2.cvtColor(sift_image_64, cv2.COLOR_BGR2RGB))
plt.title("64px")
plt.subplot(1, 6, 5)
plt.imshow(cv2.cvtColor(sift_image_100, cv2.COLOR_BGR2RGB))
plt.title("100px")
plt.subplot(1, 6, 6)
plt.imshow(cv2.cvtColor(sift_image_128, cv2.COLOR_BGR2RGB))
plt.title("128px")
plt.show()
for class_name in os.listdir(DATASET_DIR):
class_path = os.path.join(DATASET_DIR, class_name)
img_name = random.sample(os.listdir(class_path), 1)[0]
img_path = os.path.join(DATASET_DIR, class_name, img_name)
img = cv2.imread(img_path, cv2.IMREAD_COLOR)
show_sift_img(img)
Визуальное представление показало, что признаков в черно-белом формате меньше, чем в цветном. Разница незначительная, но для повышения точности модели будем обучаться на цветных изображениях. Для хорошей точности лучше использовать изображения в цвете, но есть риск переобучения моделли. Поэтому если будет возникать переобучения, то попробую испозовать чб размером 128px. P.S. на представленных выше картинках, где указаны размеры, поиск признаков тоже проводился на чб изображении, вывод сделал в цвете (SIFT внутри всегда преобразует в чб и ищет признаки)
Обучение моделей¶
- Получим дескрипторы с каждого изображения и названия классов исходя из преположений сделанных раннее
- Разобьем выборку на трейн и тест
- Подберем гиперпараметры для моделей LinearRegression и SVC c помощью гридсерча
- Посмотрим метрики на различных модельках
descriptors, labels = analyze_dataset(DATASET_DIR, 128)
X_train, X_test, y_train, y_test = train_test_split(
np.array(descriptors), labels, test_size=0.20, random_state=RANDOM_STATE
)
Обработка Pumpkin: 100%|██████████| 500/500 [00:02<00:00, 214.11image/s] Обработка Nut: 100%|██████████| 500/500 [00:01<00:00, 414.77image/s] Обработка Apple: 100%|██████████| 500/500 [00:01<00:00, 301.44image/s] Обработка Cauliflower: 100%|██████████| 500/500 [00:05<00:00, 92.83image/s] Обработка Bottle_Gourd: 100%|██████████| 500/500 [00:02<00:00, 191.39image/s] Обработка Pepper: 100%|██████████| 500/500 [00:01<00:00, 345.62image/s] Обработка Peach: 100%|██████████| 500/500 [00:01<00:00, 350.23image/s] Обработка Mango: 100%|██████████| 500/500 [00:02<00:00, 186.93image/s] Обработка Stawberries: 100%|██████████| 500/500 [00:07<00:00, 69.54image/s] Обработка Brinjal: 100%|██████████| 500/500 [00:03<00:00, 143.23image/s] Обработка Bitter_Gourd: 100%|██████████| 500/500 [00:02<00:00, 198.92image/s] Обработка Radish: 100%|██████████| 500/500 [00:02<00:00, 249.42image/s] Обработка Potato: 100%|██████████| 500/500 [00:01<00:00, 254.72image/s] Обработка Cucumber: 100%|██████████| 500/500 [00:03<00:00, 127.77image/s] Обработка Avocado: 100%|██████████| 500/500 [00:01<00:00, 282.84image/s] Обработка Pinenapple: 100%|██████████| 500/500 [00:01<00:00, 261.19image/s] Обработка Banana: 100%|██████████| 500/500 [00:01<00:00, 280.31image/s] Обработка Carrot: 100%|██████████| 500/500 [00:02<00:00, 230.09image/s] Обработка Pear: 100%|██████████| 500/500 [00:01<00:00, 346.11image/s] Обработка Plum: 100%|██████████| 500/500 [00:02<00:00, 225.62image/s] Обработка Capsicum: 100%|██████████| 500/500 [00:03<00:00, 133.00image/s] Обработка Grape: 100%|██████████| 500/500 [00:01<00:00, 389.62image/s] Обработка Cherry: 100%|██████████| 500/500 [00:01<00:00, 274.35image/s] Обработка Orange: 100%|██████████| 500/500 [00:01<00:00, 307.10image/s] Обработка Bean: 100%|██████████| 500/500 [00:02<00:00, 212.77image/s] Обработка Cabbage: 100%|██████████| 500/500 [00:02<00:00, 227.57image/s] Обработка Strawberry: 100%|██████████| 500/500 [00:03<00:00, 128.59image/s] Обработка Broccoli: 100%|██████████| 500/500 [00:03<00:00, 161.28image/s] Обработка Onion: 100%|██████████| 500/500 [00:03<00:00, 151.26image/s] Обработка Tomato: 100%|██████████| 500/500 [00:03<00:00, 152.83image/s] Обработка Watermelon: 100%|██████████| 500/500 [00:03<00:00, 136.04image/s] Обработка Papaya: 100%|██████████| 500/500 [00:02<00:00, 168.44image/s] Обработка Kiwi: 100%|██████████| 500/500 [00:02<00:00, 243.58image/s]
Функции для обучения различных моделей и просмотра метрик
- SVC модель - выбор обоснован ее точностью при работе с изображениями, хоть она и медленная. С помощью gridSearch подберу оптимальное ядро и параметр c
- LogisticRegression - выбор обоснован скоростью ее работы на больших данных и возможность посмотреть вероятность правильного предсказания. Также подберу оптимальный параметр с
- Decision Trees - более чем уверен, что результат будет плох, но для интереса посмотреть можно
def SVC_model(X_train, X_test, y_train, y_test, c_parametr, type_kernel):
scaler = StandardScaler()
pipeline = Pipeline([("scaler", MinMaxScaler()), ("svc", SVC(C=c_parametr, kernel=type_kernel))])
pipeline.fit(X_train, y_train)
y_pred_test = pipeline.predict(X_test)
accuracy_test = accuracy_score(y_test, y_pred_test)
f1_test = f1_score(y_test, y_pred_test, average="weighted")
print("SVC TEST:")
print(f"Accuracy: {accuracy_test:.6f}")
print(f"F1 Score: {f1_test:.6f}")
y_pred_train = pipeline.predict(X_train)
accuracy_train = accuracy_score(y_train, y_pred_train)
f1_train = f1_score(y_train, y_pred_train, average="weighted")
print("SVC TRAIN:")
print(f"Accuracy: {accuracy_train:.6f}")
print(f"F1 Score: {f1_train:.6f}")
# print("SVC TRAIN:")
# print(classification_report(y_train, y_pred_train))
# print("SVC TEST:")
# print(classification_report(y_test, y_pred_test))
def LogReg_model(X_train, X_test, y_train, y_test, c_parametr):
scaler = StandardScaler()
pipeline = Pipeline(
[
("scaler", MinMaxScaler()),
("log_reg", LogisticRegression(C=c_parametr, solver="lbfgs", max_iter=500)),
]
)
pipeline.fit(X_train, y_train)
y_pred_test = pipeline.predict(X_test)
accuracy_test = accuracy_score(y_test, y_pred_test)
f1_test = f1_score(y_test, y_pred_test, average="weighted")
print("LogReg Performance TEST:")
print(f"Accuracy: {accuracy_test:.6f}")
print(f"F1 Score: {f1_test:.6f}")
y_pred_train = pipeline.predict(X_train)
accuracy_train = accuracy_score(y_train, y_pred_train)
f1_train = f1_score(y_train, y_pred_train, average="weighted")
print("LogReg Performance TRAIN:")
print(f"Accuracy: {accuracy_train:.6f}")
print(f"F1 Score: {f1_train:.6f}")
# print("LogReg TRAIN:")
# print(classification_report(y_train, y_pred_train))
# print("LogReg TEST:")
# print(classification_report(y_test, y_pred_test))
def DT_model(X_train, X_test, y_train, y_test, param):
# param = {
# 'criterion': 'entropy',
# 'max_depth': 5,
# 'min_samples_split': 20,
# 'min_samples_leaf': 10
# }
dt_model = DecisionTreeClassifier()
dt_model.set_params(**param)
dt_model.fit(X_train, y_train)
y_pred_test = dt_model.predict(X_test)
accuracy_test = accuracy_score(y_test, y_pred_test)
f1_test = f1_score(y_test, y_pred_test, average="weighted")
print("Decision Trees TEST:")
print(f"Accuracy: {accuracy_test:.6f}")
print(f"F1 Score: {f1_test:.6f}")
y_pred_train = dt_model.predict(X_train)
accuracy_train = accuracy_score(y_train, y_pred_train)
f1_train = f1_score(y_train, y_pred_train, average="weighted")
print("Decision Trees TRAIN:")
print(f"Accuracy: {accuracy_train:.6f}")
print(f"F1 Score: {f1_train:.6f}")
# поиск лучших гиперпараметров у опорных векторов
def get_best_param_svc(X_train, y_train):
param_grid = {
"svc__C": np.arange(0.1, 10, 1),
"svc__kernel": ["linear", "poly", "rbf", "sigmoid"],
}
pipeline = Pipeline([("scaler", MinMaxScaler()), ("svc", SVC())])
svс = RandomizedSearchCV(pipeline, param_grid, cv=3)
svс.fit(X_train, y_train)
print("Лучшие параметры:", svс.best_params_)
print("Лучшая оценка:", svс.best_score_)
return svс.best_params_
# поиск лучших гиперпараметров у логистической регрессии
def get_best_param_lr(X_train, y_train):
param_grid = {"log_reg__C": np.arange(0.1, 10, 1)}
pipeline = Pipeline([("scaler", MinMaxScaler()), ("log_reg", LogisticRegression(solver="lbfgs", max_iter=1000))])
log_reg = RandomizedSearchCV(pipeline, param_grid, cv=3)
log_reg.fit(X_train, y_train)
print("Лучшие параметры:", log_reg.best_params_)
print("Лучшая оценка:", log_reg.best_score_)
return log_reg.best_params_
# поиск лучших гиперпараметров у дерева решений
def get_best_param_dt(X_train, y_train):
dt_model = DecisionTreeClassifier()
param_grid = {
"criterion": ["gini", "entropy"],
"max_depth": [5, 10, 20, 30],
"min_samples_split": [2, 10, 20],
"min_samples_leaf": [1, 5, 10],
}
dt = RandomizedSearchCV(dt_model, param_grid, cv=5, scoring="f1", n_iter=10)
dt.fit(X_train, y_train)
print("Лучшие параметры:", dt.best_params_)
print("Лучшая оценка:", dt.best_score_)
return dt.best_params_
# посмотрим кросс валидацию на предмет качества модели линейной регрессии и метода опорных векторов
scores_lr = []
scores_svm = []
for c in np.arange(0.1, 10, 1):
pipeline_lr = Pipeline(
[
("scaler", MinMaxScaler()),
("log_reg", LogisticRegression(C=c, solver="lbfgs", max_iter=500)),
]
)
pipeline_svc = Pipeline([("scaler", MinMaxScaler()), ("svc", SVC(C=c, kernel="rbf"))])
pipeline_lr.fit(X_train, y_train)
pipeline_svc.fit(X_train, y_train)
pred_lr = pipeline_lr.predict(X_test)
pred_svc = pipeline_svc.predict(X_test)
scores_lr.append(
{
"acc": accuracy_score(y_test, pred_lr),
"f1": f1_score(y_test, pred_lr, average="weighted"),
}
)
scores_svm.append(
{
"acc": accuracy_score(y_test, pred_svc),
"f1": f1_score(y_test, pred_svc, average="weighted"),
}
)
scores_lr
[{'acc': 0.30627705627705626, 'f1': 0.2633904564786971},
{'acc': 0.39285714285714285, 'f1': 0.36747243816479375},
{'acc': 0.41125541125541126, 'f1': 0.38757209876957754},
{'acc': 0.4145021645021645, 'f1': 0.3924627704066258},
{'acc': 0.41233766233766234, 'f1': 0.3922591099837431},
{'acc': 0.41125541125541126, 'f1': 0.3920915565031611},
{'acc': 0.408008658008658, 'f1': 0.3893902596518665},
{'acc': 0.4101731601731602, 'f1': 0.39122998287420324},
{'acc': 0.4069264069264069, 'f1': 0.3890214817561307},
{'acc': 0.4036796536796537, 'f1': 0.3855299214991083}]
scores_svm
[{'acc': 0.20887445887445888, 'f1': 0.1575339366369778},
{'acc': 0.4458874458874459, 'f1': 0.4208645687046207},
{'acc': 0.4686147186147186, 'f1': 0.45021229778432326},
{'acc': 0.4816017316017316, 'f1': 0.4630607715602206},
{'acc': 0.48268398268398266, 'f1': 0.4677861925477941},
{'acc': 0.4880952380952381, 'f1': 0.47564100898122563},
{'acc': 0.4880952380952381, 'f1': 0.47805457445259075},
{'acc': 0.49134199134199136, 'f1': 0.482104377990633},
{'acc': 0.4902597402597403, 'f1': 0.48038728370045725},
{'acc': 0.49134199134199136, 'f1': 0.4815526396576269}]
svc_param = get_best_param_svc(X_train, y_train)
SVC_model(X_train, X_test, y_train, y_test, svc_param["svc__C"], svc_param["svc__kernel"])
SVC TEST: Accuracy: 0.491342 F1 Score: 0.481553 SVC TRAIN: Accuracy: 0.961580 F1 Score: 0.961776
lr_param = get_best_param_lr(X_train, y_train)
LogReg_model(X_train, X_test, y_train, y_test, lr_param["log_reg__C"])
LogReg Performance TEST: Accuracy: 0.410173 F1 Score: 0.391230 LogReg Performance TRAIN: Accuracy: 0.563582 F1 Score: 0.550504
dt_param = get_best_param_dt(X_train, y_train)
DT_model(X_train, X_test, y_train, y_test, dt_param)
Таблица результатов¶
| Модель | Гиперпараметры | Размер изображения | Цветное | accuracy Test | accuracy Train | переобучение | |||
|---|---|---|---|---|---|---|---|---|---|
| SVC | c = 8.1, kernel = rbf | 64px | чб | 0,704 | 0.87 | да | |||
| SVC | c = 8.1, kernel = rbf | 64px | цветное | 0.76 | 0.88 | меньше | |||
| SVC | c = 7.1, kernel = rbf | 128px | чб | 0.76 | 0.86 | думаю нет | |||
| SVC | c =8.1 kernel = rbf | 128px | цветное | 0.79 | 0.88 | думаю нет | |||
| LogReg | c = 9.1 | 64px | чб | 0.47 | 0.49 | нет | |||
| LogReg | с = 8.1 | 64px | цветное | 0.54 | 0.55 | нет | |||
| LogReg | c = 9.1 | 128px | чб | 0.56 | 0.57 | нет | |||
| LogReg | c = 9.1 | 128px | цветное | 0.57 | 0.58 | нет | |||
| Decision Trees | min_samples_split=20, min_samples_leaf=10, max_depth=5, criterion= 'entropy' | 128px | чб | 0.25 | 0.25 | нет |
Выводы:¶
- Наилучшие результаты дает модель SVC с гиперпараметрами c=8.1 kernel=rbf,размер изображения 128px, цветное
- Для ускорения можно использовать модель в оттенках серого с гиперпараметрами с=7.1, kernel=8.1, размер 128px
- Логистическая регрессия не совсем подходит для данного датасета, модель должна быть сложнее
- Модель дерево решений не подходит от слова совсем, что и стоило ожидать
P.S.
- Обучение моделей проводилось только после подбора наилучших гиперпараметров методом RandomizedSearchCV
- Для оценки качества модели использовалась метрика accuracy, так как легко интерпритируема и дает хорошее качество на сбаллансированных классах, а они у нас такие. Также смотрел f1 метрику, хотя она и применяется больше для несбалансированных классов, но тем не менее дает неплохую точность.