<jupyter_start><jupyter_text>week03: Логистическая регрессия и анализ изображенийВ этом ноутбуке предлагается построить классификатор изображений на основе логистической регрессии. *Забегая вперед, мы попробуем решить задачу классификации изображений используя лишь простые методы. В третьей части нашего курса мы вернемся к этой задаче.*<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import h5py

%matplotlib inline<jupyter_output><empty_output><jupyter_text>1. Постановка задачи **Задача**: Есть датасет [прямая ссылка](https://drive.google.com/file/d/15tOimf2QYWsMtPJXTUCwgZaOTF8Nxcsm/view?usp=sharing) ("catvnoncat.h5") состоящий из:    - обучающей выборки из m_train изображений, помеченных "cat" (y=1) или "non-cat" (y=0)    - тестовой выборки m_test изображений, помеченных "cat" или "non-cat"    - каждое цветное изображение имеет размер (src_size, src_size, 3), где 3 - число каналов (RGB).    Таким образом, каждый слой - квадрат размера src_size x src_size$.Давайте построим простой алгоритм классификации изображений на классы "cat"/"non-cat".Автоматическая загрузка доступна ниже. **Recap**:Для каждого примера $x^{(i)}$:$$z^{(i)} = w^T x^{(i)} + b \tag{1}$$$$\hat{y}^{(i)} = a^{(i)} = sigmoid(z^{(i)})\tag{2}$$ $$ \mathcal{L}(a^{(i)}, y^{(i)}) =  - y^{(i)}  \log(a^{(i)}) - (1-y^{(i)} )  \log(1-a^{(i)})\tag{3}$$Функция потерь:$$ J = \frac{1}{m} \sum_{i=1}^m \mathcal{L}(a^{(i)}, y^{(i)})\tag{6}$$<jupyter_code># Uncomment this cell to download the data

# !wget "https://downloader.disk.yandex.ru/disk/7ef1d1e30e23740a4a30799a825319154815ddc85bf689542add0a3d11ccb91c/5d7fdcb0/3dcxK38Q0fG3ui0g2gMZgKkLls8ULwVpoYNkWpBm9d24EceJ6mIoH5l3_wKkFv3PfZ0WMGYjfJULynuJkuGaug%3D%3D?uid=76549735&filename=data.zip&disposition=attachment&hash=&limit=0&content_type=application%2Fzip&owner_uid=76549735&fsize=2815580&hid=084389255415f71a92d0f1024ab741d4&media_type=compressed&tknv=v2&etag=2b348ac8eca72d223108e36b2a671210" -O data.zip
# !unzip data.zip<jupyter_output><empty_output><jupyter_text>1.1 Загрузка данных и визуализация<jupyter_code>def load_dataset():
    train_data = h5py.File("data/train_catvnoncat.h5", "r")
    train_set_x_orig = np.array(train_data["train_set_x"][:]) # признаки
    train_set_y_orig = np.array(train_data["train_set_y"][:]) # метки классов

    test_data = h5py.File("data/test_catvnoncat.h5", "r")
    test_set_x_orig = np.array(test_data["test_set_x"][:]) # признаки
    test_set_y_orig = np.array(test_data["test_set_y"][:]) # метки классов

    classes = np.array(test_data["list_classes"][:]) # the list of classes
    classes = np.array(list(map(lambda x: x.decode('utf-8'), classes)))
    
    train_set_y = train_set_y_orig.reshape(train_set_y_orig.shape[0])
    test_set_y = test_set_y_orig.reshape(test_set_y_orig.shape[0])
    return train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes
train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()<jupyter_output><empty_output><jupyter_text>Цветные изображения в формате RGB представлены в виде трёхмерных numpy.array.Порядок измерений $H \times W \times C$: $H$ - высота, $W$ - ширина и $C$ - число каналов.Значение каждого пиксела находится в интервале $[0;255]$.<jupyter_code>from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets

def show_image_interact(i=0):
    f, ax = plt.subplots(1,4, figsize=(15,20), sharey=True)
    
    ax[0].imshow(train_set_x_orig[i])
    ax[0].set_title('RGB image')
    ax[1].imshow(train_set_x_orig[i][:,:,0], cmap='gray')
    ax[1].set_title('R channel')
    ax[2].imshow(train_set_x_orig[i][:,:,1], cmap='gray')
    ax[2].set_title('G channel')
    ax[3].imshow(train_set_x_orig[i][:,:,2], cmap='gray')
    ax[3].set_title('B channel')
    
    print("y = {} belongs to '{}' class.".format(str(train_set_y[i]),classes[np.squeeze(train_set_y[i])]))

interact(show_image_interact,
         i=widgets.IntSlider(min=0, max=len(train_set_y)-1, step=1))<jupyter_output><empty_output><jupyter_text>При работе с данными полезно будет сохранить размерности входных изображений для дальнейшей обработки.<jupyter_code>m_train = train_set_x_orig.shape[0]
m_test = test_set_x_orig.shape[0]
src_size = train_set_x_orig.shape[1]

print ("Размер обучающей выборки: m_train = " + str(m_train))
print ("Размер тестовой выборки: m_test = " + str(m_test))
print ("Ширина/Высота каждого изображения: src_size = " + str(src_size))
print ("Размерны трёхмерной матрицы для каждого изображения: (" + str(src_size) + ", " + str(src_size) + ", 3)")
print ("Размерность train_set_x: " + str(train_set_x_orig.shape))
print ("Размерность train_set_y: " + str(train_set_y.shape))
print ("Размерность test_set_x: " + str(test_set_x_orig.shape))
print ("Размерность test_set_y: " + str(test_set_y.shape))<jupyter_output><empty_output><jupyter_text>2. Предварительная обработка Преобразуем входные изображения размера (num_px, num_px, 3) в вектор признаков размера (num_px $*$ num_px $*$ 3, 1), чтобы сформировать матрицы объект-признак в виде numpy-array для обучающей и тестовой выборок.Каждой строке матрицы объект-признак соответствует входное развёрнутое в вектор-строку изображение.Помимо этого, для предварительной обработки (препроцессинга) изображений применяют центрирование значений: из значения каждого пиксела вычитается среднее и делят полученное значение на среднеквадратичное отклонение значений пикселей всего изображения.Однако, на практике обычно просто делят значения пикселей на 255 (максимальное значение пикселя).Оформим эти шаги в функцию предварительной обработки<jupyter_code>def image_preprocessing_simple(data):
    assert type(data) == np.ndarray
    assert data.ndim == 4
    
    n,h,w,c = data.shape
    data_vectorized = <ваш код>
    data_normalized = <ваш код>
    
    return data_normalized
# Изменить размеры входных данных

train_set_x_vectorized = image_preprocessing_simple(train_set_x_orig)
test_set_x_vectorized = image_preprocessing_simple(test_set_x_orig)

print('Train set:')
print("Размеры train_set_x_vectorized:  {}".format(str(train_set_x_vectorized.shape)))
print("Размеры train_set_y:             {}".format(str(train_set_y.shape)))
print("Размеры классов 'cat'/'non-cat': {} / {}".format(sum(train_set_y==1), sum(train_set_y==0)))
print('Test set:')
print("Размеры test_set_x_vectorized:   {}".format(str(test_set_x_vectorized.shape)))
print("Размеры test_set_y:              {}".format(str(test_set_y.shape)))
print("Размеры классов 'cat'/'non-cat': {} / {}".format(sum(test_set_y==1), sum(test_set_y==0)))<jupyter_output><empty_output><jupyter_text>3. Классификация<jupyter_code>from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
import warnings
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>**Вопрос**: Какую метрику качества стоит использовать?  3.1 Построение модели Построим  модель с параметрами по умолчанию и посмотрим, как хорошо она справится с задачей.<jupyter_code>clf = # <ваш  код>

score = <ваш код> 
print('Точность для простой модели с параметрами по умолчанию: {:.4f}'.format(score))
# from sklearn.metrics import f1_score

y_predicted = clf.predict(test_set_x_vectorized)
correct_score = <ваш код>
print('<Имя метрики> для простой модели: {:.4f}'.format(correct_score))<jupyter_output><empty_output><jupyter_text>Попробуем подобрать параметры регуляризации в надежде, что это повысит точность предсказаний.<jupyter_code><ваш код>
print('Оптимальные параметры: {}'.format(<ваш код>))
print('Наилучшее значение метрики качества: {}'.format(<ваш код>))<jupyter_output><empty_output><jupyter_text>Обучим модель с оптимальными параметрами на всей обучающей выборке и посмотрим на метрики качества:<jupyter_code>best_clf = <ваш код>
best_clf.fit(train_set_x_vectorized, train_set_y)

y_predicted = best_clf.predict(test_set_x_vectorized)
metric_score = <ваш код>(y_predicted, test_set_y)
print('Optimal model hyperparameters accuracy score: {:.4f}'.format(metric_score))<jupyter_output><empty_output><jupyter_text>3.2 Анализ ошибок<jupyter_code>is_outlier = (y_predicted != test_set_y)
test_outliers_x, test_outliers_y, predicted_y = test_set_x_orig[is_outlier], test_set_y[is_outlier], y_predicted[is_outlier]
def show_image_outliers(i=0):
    f = plt.figure(figsize=(5,5))
    plt.imshow(test_outliers_x[i])
    plt.title('RGB image')
    
    fmt_string = "Sample belongs to '{}' class, but '{}' is predicted'"
    print(fmt_string.format(classes[test_outliers_y[i]], classes[predicted_y[i]]))

interact(show_image_outliers,
         i=widgets.IntSlider(min=0, max=len(test_outliers_y)-1, step=1))<jupyter_output><empty_output><jupyter_text>**Вопрос**: Как по-вашему можно повысить точность? Каким недостатком обладает данный подход к классификации?  3.3 Модель с аугментациями Как можно увеличить количество данных для обучения?Сформировать новые примеры из уже имеющихся!Например, можно пополнить class 'cat' обучающей выборки [зеркально отображёнными](https://docs.scipy.org/doc/numpy/reference/generated/numpy.fliplr.html) изображениями котов.<jupyter_code>def augment_sample(src, label):
    <ваш код>

def image_preprocessing_augment(data, labels):
    assert type(data) == np.ndarray
    assert data.ndim == 4
    
    ## ВАШ КОД ##
    
    
    data_augmented = 
    labels_augmented = 
    ## ВАШ КОД ЗАКАНЧИВАЕТСЯ ЗДЕСЬ ##
    
    n,h,w,c = data_augmented.shape
    data_vectorized = data_augmented.reshape(n, -1) # <ваш код>
    data_normalized = data_vectorized / 255
    
    return data_normalized, labels_augmented
train_set_x_augmented, train_set_y_augmented = image_preprocessing_augment(train_set_x_orig, train_set_y)
clf = LogisticRegression(solver='liblinear')
clf.fit(train_set_x_augmented, train_set_y_augmented)
y_pred = clf.predict(test_set_x_vectorized)
print('F-мера для модели с аугментациями: {:.4f}'.format(f1_score(y_pred, test_set_y)))<jupyter_output><empty_output><jupyter_text>4. Проверьте работу классификатора на своей картинке Библиотека [OpenCV](https://opencv.org) для работы с изображениями для [python](https://pypi.org/project/opencv-python/):`pip install opencv-python`Вместе с contrib-модулями:`pip install opencv-contrib-python`<jupyter_code>import cv2

# Путь к картинке на вашем ПК
fname = "cat-non-cat.jpg"
# Считываем картинку через scipy
src = cv2.cvtColor(cv2.imread((fname)), cv2.COLOR_BGR2RGB)
src_resized = cv2.resize(src, (src_size,src_size), interpolation=cv2.INTER_LINEAR).reshape(1, src_size*src_size*3)
my_image_predict = clf.predict(src_resized)[0]

plt.imshow(src)
print("Алгоритм говорит, что это '{}': {}".format(my_image_predict, classes[my_image_predict]))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1272659316015698, 'fraction_numerical': 0.022612595776490375, 'mean_word_length': 6.7166546503244415, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 66, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '2810', 'license': 'MIT', 'n_tokens_mistral': 4582, 'n_tokens_neox': 4794, 'n_words': 743, 'path': 'week0_03_logistic/week03_extra_image_classifier.ipynb', 'repo_name': 'GendalfSeriyy/ml-mipt', 'size': '10702'}