Видео проигрыватель загружается.Воспроизвести видеоВоспроизвестиБез звукаТекущее время 0:00/Продолжительность 7:15Загрузка: 0.00%0:00Тип потока ОНЛАЙНSeek to live, currently behind liveОНЛАЙНОставшееся время -7:15 1xСкорость воспроизведения2x1.75x1.5x1.25x1x, выбрано0.75x0.5xГлавыГлавыОписанияОтключить описания, выбраноСубтитрынастройки субтитров, откроется диалог настройки субтитровСубтитры выкл., выбраноЗвуковая дорожкаPicture-in-PictureПолноэкранный режимThis is a modal window.Начало диалоговго окна. Кнопка Escape закроет или отменит окноТекстColorБелыйЧерныйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачностьПолупрозрачныйФонColorЧерныйБелыйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачностьПолупрозрачныйПрозрачныйОкноColorЧерныйБелыйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачныйПолупрозрачныйПрозрачностьРазмер шрифта50%75%100%125%150%175%200%300%400%Стиль края текстаНичегоПоднятыйПониженныйОдинаковыйТеньШрифтПропорциональный без засечекМоноширинный без засечекПропорциональный с засечкамиМоноширинный с засечкамиСлучайныйПисьменныйМалые прописныеСбросить сбросить все найстройки по умолчаниюГотовоЗакрыть модальное окноКонец диалогового окна.
По характеру изменения функции потерь от эпохи к эпохе мы иногда можем понять, что не так с процессом обучения, мы можем увидеть, что модель вообще не сходится или — она очень быстро достигает определённой точки и дальше не учится, или значение функции потерь изменяется практически случайно, с большой дисперсией. Это важный диагностический показатель. Далее мы выводим некоторую полезную информацию, которая говорит нам о том, как именно идёт процесс обучения. Ну что ж, эпоху обучения прошли, пора бы и оценить качество модели. Собственно, переводим модель в режим "eval" (то есть, в режим предсказания) и объявляем переменные для оценки среднего значения функции потерь на отложенной выборке. Далее мы повторяем практически те же самые действия, что и делали при обучении, но не делаем сам градиентный шаг — мы только получаем предсказание модели и оцениваем значение функции потерь. Важный момент, который позволит сэкономить память на видеокарте — это включить режим "torch no_grad". Когда этот режим включён, pytorch не сохраняет промежуточные данные, необходимые для вычисления градиентов. Далее мы сравниваем среднее значение функции потерь на валидации на последней эпохе и лучшее значение функции потерь, полученное аналогичным образом на предыдущих эпохах, и если новое среднее значение функции потерь — лучше, то мы сохраняем текущий вариант модели. Мы делаем это с помощью стандартной функции "copy.deepcopy()". А если улучшить значение функции потерь на отложенной выборке после этой эпохи не получилось, то мы проверяем — а как давно у нас вообще получалось улучшить модель? Если с последней хорошей эпохи прошло уже больше заданного количества эпох, то мы говорим — "ну кажется приехали — кажется, дальше улучшить модель не получится". И, в таком случае, мы прекращаем обучение. Ну, и напоследок — если пользователь задал расписание изменения скорости обучения, то мы обновляем скорость обучения с учётом нового значения функции потерь. Тело этого цикла мы обернули в try-except и добавили обработку двух видов исключений. Первое — это "interrupt", то есть — чтобы пользователь мог досрочно остановить обучение, нажав "Ctrl-C" в Jupyter ноутбуке. И второй обработчик ловит вообще все исключения и печатает их в удобоваримой виде. Вот и всё — наша функция возвращает два объекта. Первый — это лучшее значение функции потерь, а второй объект — это модель с лучшими весами, то есть это веса модели, которые получились после лучшей эпохи (не обязательно, эта эпоха — последняя). Давайте вернёмся в наш ноутбук и посмотрим на некоторые детали. Во-первых, мы говорим, что будем менять длину градиентного шага тогда, когда в течение пяти эпох значение функции потерь на валидации не улучшилось. То есть, если у нас функция потерь падает-падает, а потом вышла на плато, мы говорим — "ну OK, давайте теперь делать шаги поменьше", и иногда это позволяет спуститься в более узкие локальные минимумы, которые мы, в противном случае, перепрыгивали бы, и ещё чуть-чуть улучшить значение функции потерь, но это не всегда даёт прирост. В качестве функции потерь мы используем функцию CrossEntropy — это функция из pytorch, она реализует категориальную кросс-энтропию вместе с сигмоидой. Это позволяет нам убрать сигмоиду из самой модели и сделать процесс вычислений чуть более численно стабильным, то есть избежать слишком больших чисел или слишком маленьких. Это должно положительно сказаться на точности вычислений. Непосредственно в данном семинаре это не так важно, но, в целом, это — хорошая практика в реальной жизни. А также мы задаём здесь длину градиентного шага по умолчанию (то есть "learning rate") как 0.1, говорим, что — максимум, мы будем делать 200 проходов по датасету, то есть 200 эпох, размер батча — это 32, в общем-то и всё. Давайте посмотрим, как оно у нас всё учится. Датасет маленький, модель простая, одна эпоха занимает примерно 2 секунды. Мы видим, что на каждой эпохе модель всё улучшается и улучшается по валидации. Начиная с 25 эпохи нам не удаётся улучшить модель, поэтому, спустя пять эпох, мы решаем понизить скорость обучения, то есть разделить learning rate на 2. И мы продолжаем обучение с таким learning rate, но, в данном случае, нам это не помогает, и на 35 эпохе мы прекращаем обучение. Давайте оценим качество модели. Для того, чтобы оценить качество модели, нам нужно взять датасет и предсказать классы для объектов из этого датасета с помощью нашей модели. Для того, чтобы это было делать удобно, мы написали специальную функцию "predict_with_model". Давайте посмотрим, как она работает. Это очень простая функция, которая принимает на вход модель, датасет, идентификатор устройства (на котором необходимо производить вычисления), размер батча. И, в цикле, идёт по этому датасету, применяет модель и сохраняет результаты в список, а потом этот список преобразовывает в матрицу. Таким образом, на выходе у нас получается матрица, в которой количество строк соответствует количеству элементов в нашем датасете (количеству примеров в нашем датасете), а количество столбцов соответствует количеству классов. Для целей анализа процесса обучения мы вычисляем значение функции потерь на обучающей выборке, а также оцениваем "accuracy", то есть долю верных ответов. Как мы говорили ранее, эта метрика может использоваться только тогда, когда датасет идеально сбалансирован. В противном случае она приводит к завышенной оценке качества работы классификатора. Также мы проделываем все те же действия для валидационной выборки. Что же мы видим? Во-первых, мы видим, что обучающую выборку модель практически запомнила — она идеально работает на обучающей выборке. Но на валидационной выборке она даёт верные ответы только в 77% случаев. Значение функции потерь на обучении — порядка нескольких тысячных, а на валидации — почти 1, то есть, значение функции потерь на валидации на два порядка больше, чем значение функции потерь на обучении. Это верный сигнал к тому, что наша модель переобучилась. Но даже, несмотря на такое сильное переобучение, в целом, доля верных ответов не такая плохая.

К сожалению, у нас пока нет статистики ответов на данный вопрос, но мы работаем над этим.