{"cells":[{"metadata":{},"cell_type":"markdown","source":"**Импорт зависимостей**"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from tensorflow.keras.datasets import cifar10\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Flatten\nfrom tensorflow.keras.layers import Dropout, BatchNormalization, SpatialDropout2D, GaussianDropout\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, AveragePooling2D\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau, LearningRateScheduler, CSVLogger, LambdaCallback, TerminateOnNaN\nfrom tensorflow.keras.preprocessing import image\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras import utils\n%matplotlib inline\n(x_train, y_train), (x_test, y_test) = cifar10.load_data()\nclasses = ['самолет', 'автомобиль', 'птица', 'кот', 'олень', 'собака', 'лягушка', 'лошадь', 'корабль', 'грузовик']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Установка статических параметров**"},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 200\nnb_classes = 10\nnb_epoch = 40\nIMG_SIZE = [32, 32]\nimg_channels = 3","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Нормализация данных**"},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train = x_train.astype('float32')\nx_test = x_test.astype('float32')\nx_train /= 255\nx_test /= 255","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = utils.to_categorical(y_train, nb_classes)\ny_test = utils.to_categorical(y_test, nb_classes)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Функция для получения последовательной модели для экспериментов с keras callbacks**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_model():\n    model = Sequential()\n    #\n    model.add(Conv2D(\n        filters = 32,\n        kernel_size = (3, 3),\n        padding = 'same',\n        input_shape = (*IMG_SIZE, img_channels),\n        activation = 'relu'\n    ))\n    #\n    model.add(Conv2D(\n        filters = 32,\n        kernel_size = (3, 3),\n        padding = 'same',\n        activation = 'relu'\n    ))\n    #\n    model.add(AveragePooling2D(pool_size = (2, 2)))\n    #\n    model.add(BatchNormalization())\n    #\n    model.add(GaussianDropout(0.25))\n    #\n    model.add(Conv2D(\n        filters = 64,\n        kernel_size = (3, 3),\n        padding = 'same',\n        activation = 'relu'\n    ))\n    #\n    model.add(Conv2D(\n        filters = 64,\n        kernel_size = (3, 3),\n        padding = 'same',\n        activation = 'relu'\n    ))\n    #\n    model.add(AveragePooling2D(pool_size = (2, 2)))\n    #\n    model.add(Conv2D(\n        filters = 64,\n        kernel_size = (4, 4),\n        padding = 'same',\n        activation = 'elu'\n    ))\n    #\n    model.add(Conv2D(\n        filters = 128,\n        kernel_size = (3, 4),\n        activation = 'relu'\n    ))\n    #\n    model.add(BatchNormalization())\n    #\n    model.add(GaussianDropout(0.4))\n    #\n    model.add(Flatten())\n    #\n    model.add(Dense(1024, activation = 'relu'))\n    #\n    model.add(Dropout(0.6))\n    #\n    model.add(Dense(nb_classes, activation = 'softmax'))\n    #\n    model.compile(\n        loss = 'categorical_crossentropy',\n        optimizer = 'adam',\n        metrics = ['accuracy']\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Вспомогательная функция для представления графиков метрик**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_model_plot(history, metric):\n    plt.plot(\n        history[metric], \n        label = '{} на обучающем наборе'.format(metric)\n    )\n    plt.plot(\n        history['val_{}'.format(metric)],\n        label = 'val_{} на проверочном наборе'.format(metric)\n    )\n    plt.xlabel('Эпоха обучения')\n    plt.ylabel(metric)\n    plt.legend()\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Имитация жизненного цикла модели для экспериментов"},{"metadata":{"trusted":true},"cell_type":"code","source":"def model_life_cycle(callback_list, verbose = 1):\n    model = get_model()\n    history = model.fit(\n        x = x_train,\n        y = y_train,\n        batch_size = batch_size,\n        epochs = nb_epoch,\n        callbacks = callback_list,\n        validation_split = 0.1,\n        verbose = verbose\n    )\n    #\n    scores = model.evaluate(\n        x_test,\n        y_test,\n        verbose = 0\n    )\n    print('Evaluate val_accuracy: {}'.format(scores[1] * 100))\n    #\n    draw_model_plot(history.history, 'accuracy')\n    draw_model_plot(history.history, 'loss')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Callback** – набор функций, применяемых в определенные моменты во время процедуры обучения. Вы можете использовать функции callback чтобы получить информацию о внутреннем состоянии модели в процессе обучения. Нужно передавать список callback’ов (именованным аргументом callbacks) методу .fit() Sequential или Model классов. Подходящие методы callback будут вызваны на каждой стадии обучения."},{"metadata":{},"cell_type":"markdown","source":"**1) ModelCheckpoint**\n\nModelCheckpoint используется для сохранения модели или весов (в файле) через некоторый интервал, чтобы модель или веса можно было загрузить позже, чтобы продолжить обучение из сохраненного состояния\n\n**Аргументы:**\n\n— filepath: строка, путь сохранения модели\n\n— monitor: параметр для мониторинга\n\n— verbose: режим отображения, 0 или 1\n\n— save_best_only: если save_best_only=True, если результат текущей эпохи хуже предыдущей, он не будет сохранен.\n\n— save_weights_only: если True, тогда будут сохраняться только веса модели (model.save_weights(filepath)), в противном случае будет сохраняться вся модель (model.save(filepath)).\n\n— mode: один из {auto, min, max}. Если save_best_only=True, решение о перезаписи текущего файла будет приниматься в зависимости от уменьшения/увеличения параметра мониторинга. Для val_acc, необходим max, для val_loss необходим min. В auto режиме, mode выбирается в зависимости от имени monitor.\n\n— period: Интервал (число эпох) между сохранениями."},{"metadata":{"trusted":true},"cell_type":"code","source":"modelCheckpoint = ModelCheckpoint(\n              filepath = 'best_model.h5',\n              monitor = 'val_loss',\n              mode = 'min',\n              save_best_only = True,\n              verbose = 1,\n              period = 1,\n              save_weights_only = False\n            )\n\nmodel_life_cycle(callback_list = [modelCheckpoint], verbose = 0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**2) EarlyStopping**\n\nИспользуется для прекращение обучения, когда параметр monitor перестает улучшаться\n\n**Аргументы:**\n\n— monitor: параметр для мониторинга\n\n— min_delta: минимальное значение изменения величины monitor, расцениваемое как улучшение, то есть, если абсолютное изменение меньше min_delta, то улучшение не засчитывается\n\n— patience: число эпох, за которые величина monitor не улучшается, после которых обучение будет остановлено. Проверочные величины могут производиться не после каждой эпохи если validation_freq (model.fit(validation_freq=5)) больше единицы.\n\n— verbose: режим отображения, 0 или 1.\n\n— mode: один из {auto, min, max}. В режиме min, обучение остановится когда величина monitor перестанет уменьшаться; в режиме max, обучение остановится когда величина monitor перестанет увеличиваться; в режиме auto, mode выбирается в зависимости от имени monitor.\n\n— baseline: значение, которое должна достичь величина monitor, обучение прекращается по достижению этой величины[](http://)\n\n— restore_best_weights: восстанавливать ли веса модели с эпохи с лучшем значением параметра monitor. Если False, веса модели будут загружены из последней шага обучения."},{"metadata":{"trusted":true},"cell_type":"code","source":"earlyStopping = EarlyStopping(\n              monitor = 'val_loss',\n              min_delta = 0,\n              patience = 3,\n              verbose = 1,\n              restore_best_weights = True,\n              mode = 'min'\n            )\n\nmodel_life_cycle(callback_list = [earlyStopping], verbose = 0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**3) LearningRateScheduler**\n\nИспользуется для регулировки скорости обучения с течением времени, используя schedule, эта функция возвращает желаемую скорость обучения. Входной параметр — индекс эпохи. \n\n**Аргументы:**\n\n— schedule: функция, принимающая индекс эпохи (integer, отсчет с нуля) и текущую скорость обучения, и возвращающая новую скорость обучения (float).\n\n— verbose: int. 0: «тихий» режим, 1: выводить сообщения"},{"metadata":{"trusted":true},"cell_type":"code","source":"LR_START = 0.00001\nLR_MAX = 0.0001\nLR_MIN = 0.00001\nLR_RAMPUP_EPOCHS = 10\nLR_SUSTAIN_EPOCHS = 0\nLR_EXP_DECAY = 0.8\n\n\ndef lrfn(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n\nrng = [i for i in range(nb_epoch)]\ny = [lrfn(x) for x in rng]\nplt.plot(rng, y)\nplt.xlabel('Эпоха обучения')\nplt.ylabel('Скорость обучения')\nplt.show()\n\nlearningRateScheduler = LearningRateScheduler(\n              schedule = lrfn,\n              verbose = 1\n            )\n\nmodel_life_cycle(callback_list = [learningRateScheduler], verbose = 0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**4) ReduceLROnPlateau**\n\nИспользуется для изменения скорости обучения в factor раз, когда метрика перестала улучшаться patience эпох\n\n**Аргументы:**\n\n— monitor:  параметр для мониторинга\n\n— factor: коэффициент уменьшения скорости обучения. new_lr = lr * factor\n\n— patience: число эпох, за которые величина monitor не улучшается, после которых обучение будет остановлено. Проверочные величины могут производиться не после каждой эпохи если validation_freq (model.fit(validation_freq=5)) больше единицы.\n\n— verbose: int. 0: «тихий» режим, 1: выводить сообщения\n\n— mode: один из {auto, min, max}. В режиме min, скорость обучения уменьшится когда величина monitor перестанет уменьшаться; в режиме max, скорость обучения уменьшится когда величина monitor перестанет увеличиваться; в режиме auto, mode выбирается в зависимости от имени monitor.\n\n— min_delta: минимальное значение изменения величины monitor, расцениваемое как улучшение, то есть, если абсолютное изменение меньше min_delta, то улучшение не засчитывается\n\n— cooldown: число эпох после уменьшения скорости обучения, которые должны пройти, прежде чем стандартный процесс уменьшения возобновится.\n\n— min_lr: нижняя граница скорости обучения\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"reduceLROnPlateau = ReduceLROnPlateau(\n              monitor = 'val_loss',\n              factor = 0.2,\n              min_delta = 0,\n              patience = 2,\n              verbose = 1,\n              cooldown = 1,\n              mode = 'min'\n            )\n\nmodel_life_cycle(callback_list = [reduceLROnPlateau], verbose = 0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**5) CSVLogger**\n\nCallback, записывающий результаты эпох в csv файл\n\n**Аргументы:**\n\n— filename: имя csv файла (к примеру: ‘run/log.csv’)\n\n— separator: строка, используемая для разделения элементов в файле.\n\n— append: True: добавить к существующему файлу (если он существует). False: перезаписать существующий файл."},{"metadata":{"trusted":true},"cell_type":"code","source":"csvLogger = CSVLogger(\n              filename = 'learning.csv',\n              separator = ',',\n              append = False\n            )\n\nmodel_life_cycle(callback_list = [csvLogger])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**6) LambdaCallback**\n\nCallback для создания простых пользовательских callback’ов «на лету»\n\n**Аргументы:**\n\n— on_epoch_begin: вызывается в начале каждой эпохи\n\n— on_epoch_end: вызывается в конце каждой эпохи\n\n— on_batch_begin: вызывается в начале каждого батча\n\n— on_batch_end: вызывается в конце каждого батча\n\n— on_train_begin: вызывается в начале обучения модели\n\n— on_train_end: вызывается в конце обучения модели"},{"metadata":{"trusted":true},"cell_type":"code","source":"def foo(epochs, logs):\n    \"\"\" Любая пользовательская функция \"\"\"\n    return\n\nlambdaCallback = LambdaCallback(\n                    on_epoch_begin = foo,\n                    on_epoch_end = foo,\n                    on_batch_begin = foo,\n                    on_batch_end = foo,\n                    on_train_begin = lambda logs: print('on_train_begin'),\n                    on_train_end = lambda logs: print('on_train_end')\n                 )\n\nmodel_life_cycle(callback_list = [lambdaCallback], verbose = 0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**7) RemoteMonitor**\n\nИспользуется для отправки событий на удаленную машину\n\n**Аргументы:**\n\n— root: Строка;  корневой каталог целевого сервера\n\n— path: Строка; относительный к root путь, в который будут посылаться события\n\n— field: Строка;  поле JSON в котором будут храниться данные. Field используется только если данные пересылаются через форму(то есть send_as_json установлен как False)\n\n— headers: Словарь; дополнительные пользовательские заголовки HTTP.\n\n— send_as_json: Boolean; посылать ли запрос как приложение/json."},{"metadata":{},"cell_type":"markdown","source":"**Совместное использование keras callback**\n\nСовместно можно использовать любые callback'и, которые используются исключительно как side-effect, т.е. никак не влияют на процесс обучения нейронной сети.\n\nК таким callback'ам отсносятся:\n\n- CSVLogger\n\n- History (используется по умолчанию)\n\n- ProgbarLogger (используется по умолчанию)\n\n- RemoteMonitor\n\n- LambdaCallback (при условии, что пользовательские функции исключительно для side-effect)"},{"metadata":{},"cell_type":"markdown","source":"Нельзя использовать callback'и, которые изменяют одинаковые параметры. Такими являются\n\n- ReduceLROnPlateau\n\n- LearningRateScheduler\n\nт.к. оба изменяют скорость обучения\n\nВ случае их совместного использования результат обучения каждый раз будет непредсказуем."},{"metadata":{},"cell_type":"markdown","source":"Также корректность применения callback'ов зависит от их правильной конфигурации.\nНапример, при выставлении patience для EarlyStopping меньше чем для ReduceLROnPlateau, вызов второго может и не произойти, т.к. прерывание первым будет раньше."}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}