<jupyter_start><jupyter_text>Свёрточные сетиПришло время собрать нашу первую свёрточную нейросеть. Будем использовать для этого датасет [CIFAR10](https://www.cs.toronto.edu/~kriz/cifar.html) (Canadian Institute for Advanced Research). Он включает в себя картинки из 10 разных классов: самолёты, машины, птицы, кошки, олени, собаки, лягушки, лошади, корабли, грузовики.   Всего $60 000$ цветных картинок размера $32 \times 32$. В каждом из классов ровно по $6000$ картинок.  Есть расширение этого датасета, CIFAR_100. Думаю, что по названию вы догадались, что в нём сто видов картинок. Кстати говоря, если очень хочется, можете принять участие [в стареньком соревновании на Kagle,](https://www.kaggle.com/c/cifar-10) связанным с этим датасетом. Там же на форуме можно найти интересный код для его решения.<jupyter_code>import numpy as np
import random

from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt
%matplotlib inline
import tensorflow as tf
from tensorflow.keras import layers, models, Model, Sequential

tf.__version__ 
(X_train, y_train), (X_test, y_test) = tf.keras.datasets.cifar10.load_data()

NUM_CLASSES = 10
cifar10_classes = ["airplane", "automobile", "bird", "cat", "deer", 
                   "dog", "frog", "horse", "ship", "truck"]

print("Train samples:", X_train.shape, y_train.shape)
print("Test samples:", X_test.shape, y_test.shape)<jupyter_output>Train samples: (50000, 32, 32, 3) (50000, 1)
Test samples: (10000, 32, 32, 3) (10000, 1)<jupyter_text>Нарисуем несколько рандомных картинок из тренировочной выборки.<jupyter_code>cols = 8
rows = 2
fig = plt.figure(figsize=(2 * cols - 1, 2.5 * rows - 1))
for i in range(cols):
    for j in range(rows):
        random_index = np.random.randint(0, len(y_train))
        ax = fig.add_subplot(rows, cols, i * rows + j + 1)
        ax.grid('off')
        ax.axis('off')
        ax.imshow(X_train[random_index, :])
        ax.set_title(cifar10_classes[y_train[random_index, 0]])
plt.show()
# Нормализация пикселей на отрезок [-1; 1]
X_train_n, X_test_n = X_train / 127.5 - 1, X_test / 127.5 - 1
X_train_n.max(), X_train_n.min()<jupyter_output><empty_output><jupyter_text>1. Полносвязная сеть 1.1 Обучение моделиСоберите полносвязную сеть из 5 слоёв с $256$, $128$, $64$, $32$, $10$ нейронами. На последнем слое используйте в качестве активации `softmax`. На всех промежуточных `relu`.<jupyter_code>model_fc = Sequential()

model_fc.add(layers.Flatten(input_shape=(32, 32, 3)))

for unit in [256, 128, 64, 32]:
    model_fc.add(layers.Dense(unit, activation='relu'))

model_fc.add(layers.Dense(10, activation='softmax'))
model_fc(X_train[:2])<jupyter_output><empty_output><jupyter_text>Обучите получившуюся модель на __нормализованной__ выборке.<jupyter_code># model_fc = FCmodel()

model_fc.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(),
              metrics=['accuracy'])

history_fc = model_fc.fit(
    X_train_n, y_train,
    batch_size = 32,
    epochs = 10,
    validation_split=0.1,
    verbose=1
)
model_fc.evaluate(X_test,  y_test, verbose=2)
plt.plot(history_fc.history['loss'], label='loss')
plt.plot(history_fc.history['val_loss'], label = 'val_loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend(loc='lower right');<jupyter_output><empty_output><jupyter_text>__Упражнение:__ Объясните зачем нужно нормализовывать картинки.   1.2 Ошибки моделиПосмотрим на примеры картинок, где ошибалась наша модель.<jupyter_code>y_pred = np.argmax(model_fc.predict(X_test), axis=1)
errors =  y_pred != y_test.T[0]
errors[:5]
X_err = X_test[errors]
y_err = y_test[errors].T[0]
y_pred = y_pred[errors]
cols = 6
rows = 2
fig = plt.figure(figsize=(3 * cols - 1, 4 * rows - 1))
for i in range(cols):
    for j in range(rows):
        random_index = np.random.randint(0, len(y_err))
        ax = fig.add_subplot(rows, cols, i * rows + j + 1)
        ax.grid('off')
        ax.axis('off')
        ax.imshow(X_err[random_index, : ], cmap='gray')
        ax.set_title('real_class: {} \n  predict class: {}'.format(cifar10_classes[y_err[random_index]], 
                                                                   cifar10_classes[y_pred[random_index]]))
plt.show()<jupyter_output><empty_output><jupyter_text>Посмотрим на то, путает ли модель систематически одни классы с другими. Например, лошадей с оленями.  Чтобы лучше было видно занулите диагональ у `confusion_matrix`.<jupyter_code>from sklearn.metrics import confusion_matrix, accuracy_score

p_pred = model_fc.predict(X_test_n)
y_pred = np.argmax(p_pred, axis=1)

cm = confusion_matrix(y_test.T[0], y_pred, normalize='all')
cm = cm - np.eye(NUM_CLASSES)*cm.diagonal()

plt.figure(figsize=(7, 6))
plt.title('Confusion matrix', fontsize=16)
plt.imshow(cm)
plt.xticks(np.arange(10), cifar10_classes, rotation=45, fontsize=12)
plt.yticks(np.arange(10), cifar10_classes, fontsize=12)
plt.colorbar();<jupyter_output><empty_output><jupyter_text>2. Свёрточная сеть 2.1 Обучение моделиСоберите следующую свёрточную нейронную сетку:- Свёрточный слой с 16 выходными каналами и ядром 3х3. Поставьте опцию `padding='same'` для дополнения нулями и шаг для свёртки (stride) размера (2,1)- Max pooling с ядром 2х2- Свёрточный слой с 32 выходными каналами и ядром 3х3 Поставьте опцию `padding='same'` для дополнения нулями. - Avg pooling с ядром 2х2- Свёрточный слой с 64 выходными каналами и ядром 3х3. Поставьте опцию `padding='same'` для дополнения нулями.- Max pooling с ядром 2х2- Полносвязыный слой со 128 выходами- Полносвязыный слой с 64 выходами- Полносвязыный слой с 10 выходамиПосле каждого свёрточного и полносвзяного слоя (кроме последнего) будем использовать функцию активации ReLU.<jupyter_code>model_cnn = Sequential([
    layers.Conv2D(16, kernel_size=(3,3), 
                padding='same', strides=(2,1),
                activation='relu',
                input_shape=(32, 32, 3)),
    layers.MaxPool2D(pool_size=(2, 2)),

    layers.Conv2D(32, kernel_size=(3,3), 
                padding='same', activation='relu'),
    layers.AvgPool2D(pool_size=(2, 2)),

    layers.Conv2D(64, kernel_size=(3,3), 
                padding='same', activation='relu'),
    layers.MaxPool2D(pool_size=(2, 2)),

    layers.Flatten(),
    layers.Dense(128, activation='relu', name='fc_embedding_128'),
    layers.Dense(64, activation='relu', name='fc_embedding_64'),
    layers.Dense(10, activation='softmax')
])
model_cnn(X_train_n[:2])<jupyter_output><empty_output><jupyter_text>Обучите получившуюся модель.<jupyter_code>model_cnn.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(),
              metrics=['accuracy'])

history_cnn = model_cnn.fit(
    X_train_n, y_train,
    batch_size = 32,
    epochs = 10,
    validation_split=0.1,
    verbose=1
)
model_cnn.evaluate(X_test_n,  y_test, verbose=2)<jupyter_output>313/313 - 2s - loss: 0.9142 - accuracy: 0.6964 - 2s/epoch - 7ms/step<jupyter_text>Посмотрим на то, как ошибка вела себя на обучении и валидации.<jupyter_code>plt.plot(history_cnn.history['loss'], label='loss')
plt.plot(history_cnn.history['val_loss'], label = 'val_loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend(loc='lower right');<jupyter_output><empty_output><jupyter_text>2.2  Ошибки моделиСнова посмотрим на примеры картинок, где ошибалась наша модель.<jupyter_code>p_pred = model_cnn.predict(X_test_n)
y_pred = np.argmax(p_pred, axis=1)
errors =  y_pred != y_test.T[0]

X_err = X_test[errors]
y_err = y_test[errors].T[0]
y_pred = y_pred[errors]
p_pred = p_pred[errors]
cols = 6
rows = 2
fig = plt.figure(figsize=(3 * cols - 1, 4 * rows - 1))
for i in range(cols):
    for j in range(rows):
        random_index = np.random.randint(0, len(y_err))
        ax = fig.add_subplot(rows, cols, i * rows + j + 1)
        ax.grid('off')
        ax.axis('off')
        ax.imshow(X_err[random_index, : ], cmap='gray')
        ax.set_title('real_class: {} \n  predict class: {} \n score: {:.3}'.format(cifar10_classes[y_err[random_index]], 
                                                                   cifar10_classes[y_pred[random_index]], np.max(p_pred[random_index])))
plt.show()<jupyter_output><empty_output><jupyter_text>Посмотрим на то, путает ли модель систематически одни классы с другими. Например, лошадей с оленями.<jupyter_code>from sklearn.metrics import confusion_matrix, accuracy_score

p_pred = model_cnn.predict(X_test_n)
y_pred = np.argmax(p_pred, axis=1)

cm = confusion_matrix(y_test.T[0], y_pred, normalize='all')
cm = cm - np.eye(NUM_CLASSES)*cm.diagonal()

plt.figure(figsize=(7, 6))
plt.title('Confusion matrix', fontsize=16)
plt.imshow(cm)
plt.xticks(np.arange(10), cifar10_classes, rotation=45, fontsize=12)
plt.yticks(np.arange(10), cifar10_classes, fontsize=12)
plt.colorbar();<jupyter_output><empty_output><jupyter_text>Подсчитайте число параметров, которое оценивалось в полносвязной модели и число параметров, которое оценивалось в свёрточной модели.<jupyter_code>model_fc.summary()
model_cnn.summary()
np.sum([np.sum([np.prod(item.shape) for item in l.variables]) for l in model_fc.layers])
np.sum([np.sum([np.prod(item.shape) for item in l.variables]) for l in model_cnn.layers])<jupyter_output><empty_output><jupyter_text>__Упражнение:__ Как именнов свёрточной сетке увеличивается поле восприятия? Правда ли, что к `Flatten()`- слою поле восприятия достигает размера картинки? Зачем нам нужно, чтобы это происходило?  > __Если вам стало скучновато, попробуйте поэкспериментировать с размерами сетки и её архитектурой. Попытайтесь добиться максимально классных результатов.__  [При желании и хорошей регуляризации](https://www.kaggle.com/arianshf/cifar-10-classification-using-cnn) можно выбить Accuracy в районе $0.9,$ а то и выше. Также можете посмотреть, как [похожие архитектуры собрать в pytorch.](https://www.kaggle.com/xcwang21/cifar10-with-mlp-lenet-resnet-for-beginners) Различные трюки: 1. Начните с маленькой сети. Не забывайте прикидывать сколько наблдюдений  тратится на оценку каждого из  параметров. Если величина очень маленькая, не забывайте о регуляризации.2. Всегда оставляйте часть выборки под валидацию на каждой эпохе.3. Усложняйте модель, пока качество на валидации не начнёт падать.4. Не забывайте проскалировать ваши наблюдения для лучшей сходимости.5. Можно попробовать ещё целую серию различных трюков:  - Архитектура нейросети    - Больше/меньше нейронов    - Больше/меньше слоёв    - Другие функции активации (tanh, relu, leaky relu, elu etc)    - Регуляризация (dropout, l1,l2)  - Более качественная оптимизация    - Можно попробовать выбрать другой метод оптимизации    - Можно попробовать менять скорость обучения, моментум и др.    - Разные начальные значения весов  - Попробовать собрать больше данных  - Для случая картинок объёмы данных можно увеличить искусственно с помощью подхода, который называется Data augmemntationИ это далеко не полный список. Обратите внимание, что делать grid_search для больших сеток это довольно времязатратное занятие... Логгируйте свои эксперименты. За один прогон пробуйте одно изменение. Иначе будет непонятно какие именно изменения улучшили качество, а какие ухудшили.   3. Эмбеддинги __«Эмбеддинг» (embedding – вложение)__ — это сопоставление произвольной сущности (например, узла в графе, текста или кусочка картинки) некоторому вектору.На самом деле вcё глубокое обучение держится на идее эмбеддингов. Один из способов обучить эмбеддинг для картинок — обучить классификатор для какой-то сложной задачи, а затем срезать последние слои. Промежуточный слой будет выдавать вектор какого-то размера для каждой картинки. Эти вектора можно будет использовать в других своих задачах. Давайте вытащим представление картинок для тестовой выборки с одного из последних слоёв и построим для неё TSNE-визуализацию.<jupyter_code>model_cnn.summary()<jupyter_output>Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 16, 32, 16)        448       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 8, 16, 16)        0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 8, 16, 32)         4640      
                                                                 
 average_pooling2d (AverageP  (None, 4, 8, 32)         0         
 ooling2D)                                                       
                                                                 
 conv2d_2 (Conv2D)           (None, 4, 8, 64)          18496     
                                                      [...]<jupyter_text>Достать из сетки любой слой можно по имени. Имена можно дать слоям при объявлении архитектуры.<jupyter_code>embed_layer = model_cnn.get_layer('fc_embedding_64')<jupyter_output><empty_output><jupyter_text>У каждого слоя есть свои атрибуты: переменные, вход, выход и тп.<jupyter_code>embed_layer.output
embed_layer.variables<jupyter_output><empty_output><jupyter_text>Нас интересует выход из слоя. Мы хотим собрать новую модель. Она должна принимать на вход картинку и выплёвывать выход из конкретного слоя. Это можно сделать с помощью класса `Model`. В нём нужно перечислить входы и выходы.<jupyter_code>feature_extractor = Model(
    inputs=model_cnn.input,
    outputs=embed_layer.output
)<jupyter_output><empty_output><jupyter_text>Прогоним новую модель на какой-нибудь картинке.<jupyter_code>feature_extractor(X_test_n[:1])<jupyter_output><empty_output><jupyter_text>На вход пришла картинка, на выходе получился вектор, который её описывает. __Упражнение:__ ReLU-активация занулила на этом слое часть выходов. Как думаете, как это скажется на качестве эмбеддинга?<jupyter_code>X_emb = feature_extractor(X_test_n)
X_emb.shape<jupyter_output><empty_output><jupyter_text>Визуализируем данные с помощью TSNE. Вы знаете этот метод визуализации данных из курса по машинному обучению. Если подзабыли как он работает, почитайте [статью на DistillPub.](https://distill.pub/2016/misread-tsne/) Внутри sklearn есть реализация TSNE, но она не такая эффективная как [библиотека MulticoreTSNE,](https://github.com/DmitryUlyanov/Multicore-TSNE) в которой метод можно распараллелить.<jupyter_code>!pip install MulticoreTSNE
from MulticoreTSNE import MulticoreTSNE as TSNE

tsne = TSNE(n_jobs=4, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_emb)
plt.figure(figsize=(12,10))
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y_test.T[0], 
            edgecolor='none', alpha=0.7, s=40,
            cmap=plt.cm.get_cmap('nipy_spectral', 10))

plt.colorbar()
plt.title('CIFAR-10 t-SNE projection');
cifar10_classes<jupyter_output><empty_output><jupyter_text>Видно, что эмбеддинги впитали в себя информацию о том, к какому классу относится какая картинка. В больших технологических компаниях с картинками часто работают следующим образом. - Каким-то методом учат огромную нейросеть, которая хорошо понимает что изображено на картинках. Такую большую сетку называют __тушей__ или __тушкой.__- Берут эмбеддинги с одного из последних слоёв и доучивают на них маленькие модели. Например, модели для классификации. Их называют __головами.__ - Эмбеддинги картинок посылают на вход другим моделям. Например, в градиентный бустинг. Обычно фичи, полученные из нейросети называют __нейросетевыми фичами.__ Конечно же это не единственный способ получать для картинок эмбеддинги. Дальше мы будем обсуждать и другие методы.    4. Стреляем себе в ногу Давайте разберём несколько архитектур. Попытайтесь понять что именно с ними не так. Упражнения позаимствованы [из нескольких ШАДОвских семинаров.](https://github.com/yandexdataschool/Practical_DL/blob/master/week03_convnets/other_frameworks/how_to_shoot_yourself_in_the_foot_with_cnn.ipynb) a) Задача регрессии. Предсказываем цены на недвижимость.<jupyter_code>model = Sequential()
model.add(InputLayer([39]))
model.add(BatchNormalization())
model.add(Dense(128, kernel_initializer=keras.initializers.zeros()))
model.add(Dense(128, kernel_initializer=keras.initializers.zeros()))
model.add(Dense(1))

model.compile(optimizer='sgd', loss='mean_squared_error')<jupyter_output><empty_output><jupyter_text>б) Классификация картинок, например, fashion MNIST<jupyter_code>model = Sequential()

model.add(InputLayer([28, 28, 1]))
model.add(Conv2D(filters=512, kernel_size=(3, 3)))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(100))
model.add(Activation('softmax'))
model.add(Dropout(0.1))
model.add(Dense(10))
model.add(Activation('softmax'))
model.add(Dropout(0.1))

model.compile(optimizer='rmsprop', loss='mean_squared_error')<jupyter_output><empty_output><jupyter_text>в) И снова fashion MNIST, но теперь мы знаем, что размер картинки $100 \times 100$ пикселейЕсли попробовать скомпилировать эту сетку, вылезет ошибка.<jupyter_code>model = Sequential()
model.add(InputLayer([100, 100, 3]))

for filters in [32, 64, 128, 256]:
    model.add(Conv2D(filters, kernel_size=(5, 5)))
    model.add(Conv2D(filters, kernel_size=(1, 1)))
    model.add(MaxPooling2D(pool_size=(3, 3)))
    model.add(Activation('relu'))
    model.add(BatchNormalization())

model.add(Flatten())

model.add(Dense(100, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

model.compile(optimizer='adam', loss='sparse_categorical_accuracy')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.111243747157799, 'fraction_numerical': 0.026887221464301954, 'mean_word_length': 4.38176812480881, 'pattern_counts': {'":': 0, '<': 86, '<?xml version=': 0, '>': 87, 'https://': 7, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '608760', 'license': 'MIT', 'n_tokens_mistral': 7154, 'n_tokens_neox': 7412, 'n_words': 1476, 'path': 'week05_conv_nets/conv_nn_solve.ipynb', 'repo_name': 'FUlyankin/deep_learning_tf', 'size': '17592'}