www.machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Понимание PyTorch на примере: пошаговое руководство

Дата публикации May 7, 2019

фотоАллен КайнаUnsplash

Введение

PyTorchэтосамый быстрорастущийФреймворк глубокого обучения, и он также используетсяFast.aiв его MOOC,Глубокое обучение для кодерови этобиблиотека,

PyTorch тоже оченьвещийзначит, это более естественно использовать его, если вы уже являетесь разработчиком Python.

Кроме того, использование PyTorch может дажеулучшить свое здоровье, согласно сАндрей Карпати:-)

мотивация

Естьмного-многоУчебные руководства по PyTorch и его документация довольно полны и обширны. Так,ЗачемВы должны продолжать читать это пошаговое руководство?

Ну, хотя можно найти информацию очто-нибудьPyTorch может сделать, я пропустилструктурированная,дополнительныйа такжеиз первых принциповподход к этому.

В этом посте я проведу вас черезосновные причиныпочему PyTorch делает это многоПолегчеи большеинтуитивныйпостроить модель глубокого обучения в Python -Автоград,график динамических вычислений,модельные классыи многое другое - и я тоже покажу вамкак избежатьнесколькообщие подводные камниа такжеошибкипо пути.

Более того, так как это довольнодлиннаяпост, я построилОглавлениечтобы облегчить навигацию, следует использовать ее какмини-курси прорабатывайте контент по одной теме за раз.

Оглавление

Простая проблема регрессии

Большинство уроков начинаются с хороших и красивыхпроблема классификации изображенийчтобы проиллюстрировать, как использовать PyTorch. Это может показаться крутым, но я верю в этоотвлекаетсяты изОсновная цель:как работает PyTorch?

По этой причине в этом уроке я буду придерживатьсяпростоа такжезнакомыепроблема:линейная регрессияс одной функциейИкс! Это не намного проще, чем это ...

Простая модель линейной регрессии

Генерация данных

Давайте начнемпорождающийнекоторые синтетические данные: мы начинаем с вектора 100 точек для нашегохарактерная чертаИкси создать нашэтикеткис помощьюа = 1,б = 2и немного гауссовского шума.

Далее давайтеТрещинанаши синтетические данные впоезда такжеПроверкаустанавливает, перетасовывая массив индексов и используя первые 80 перетасованных точек для тренировки.

Генерация синтетического поезда и наборов валидации для линейной регрессии
Рисунок 1: Синтетические данные - Поезд и Наборы проверки

Мызнатьчто a = 1 и b = 2, но теперь давайте посмотрим, насколько близко мы можем добраться до истинных значений, используяградиентный спуски 80 очков вподготовкапоставил...

Градиентный спуск

Если вас устраивает внутренняя работа градиентного спуска,не стесняйтесь пропуститьэта секция. Это выходит за рамки этой статьи, чтобы полностью объяснить, как работает градиентный спуск, но я расскажу очетыре основных шагавам нужно пройти, чтобы вычислить это.

Шаг 1: Рассчитать убыток

Для проблемы регрессиипотерядаетсяСреднеквадратичная ошибка (MSE)то есть среднее значение всех квадратов различий междуэтикетки(у) ипрогнозы(+ BX).

Стоит отметить, что если мы используемвсе точкив тренировочном наборе (N) для расчета потерь мы выполняемпартияградиентный спуск. Если бы мы использовалиодна точкав каждый раз, это было быстохастическийградиентный спуск. Что-нибудь еще (n)между 1 и Nхарактеризуетмини-партияградиентный спуск.

Потеря: средняя квадратическая ошибка (MSE)

Шаг 2: вычислить градиенты

градиентэточастная производная-Зачемчастичный? Потому что каждый вычисляет это относительно (w.r.t.)не замужемпараметр, У нас есть два параметра,а такжеб, поэтому мы должны вычислить две частные производные.

производноеговорит тебекак многоданныйколичество измененийкогда тынемноговарьироватьнесколькодругое количество, В нашем случае сколько стоит нашMSEпотеряизменить, когда мы меняемсякаждый из наших двух параметров?

самый правыйЧасть уравнений ниже - это то, что вы обычно видите в реализациях градиентного спуска для простой линейной регрессии. впромежуточный шаг, Я покажу тебевсе элементычто всплывающее из приложенияПравило цепиИтак, вы знаете, как появилось последнее выражение.

Вычисление градиентов с коэффициентами a. и b

Шаг 3: Обновите параметры

На последнем этапе мыиспользовать градиенты для обновленияпараметры. Так как мы пытаемсяминимизироватьнашпотеримыпоменять знакградиента для обновления

Есть еще один параметр для рассмотрения:скорость обучения, обозначенныйГреческая букварасчетное время прибытия(это похоже на письмоN), какоймультипликативный факторчто нам нужно применить к градиенту для обновления параметра.

Обновление коэффициентов А и В с использованием вычисленных градиентов и скорости обучения

Каквыберитескорость обучения? Это отдельная тема, которая выходит за рамки этого поста.

Шаг 4: промойте и повторите!

Теперь мы используемобновленныйпараметрывернуться кШаг 1и перезапустите процесс.

эпоха завершается всякий раз, когда каждая точка уже была использована для вычисления потери, Дляпартияградиентный спуск, это тривиально, поскольку он использует все точки для расчета потерь -одна эпохатакой же какодно обновление, Длястохастическийградиентный спуск,одна эпохасредстваNобновленияв то время как длямини-партия(размера n),одна эпохаимеетN / N обновления,

Повторяя этот процесс снова и снова, длямного эпохв двух словах,подготовкамодель.

Линейная регрессия в Numpy

Пришло время реализовать нашу модель линейной регрессии, используя градиентный спуск, используяТолько Numpy,

Подождите минуту ... Я думал, что этот урок был о PyTorch!

Да, это так, но это служитдве цели:первый, чтобы ввестисоставнашей задачи, которая останется в основном той же, и,второй, чтобы показать вам главноеболевые точкитак что вы можете полностью оценить, насколько PyTorch делает вашу жизнь проще :-)

Для обучения модели естьдва шага инициализации:

  • Случайная инициализация параметров / весов (у нас есть только два,а такжеб) - строки 3 и 4;
  • Инициализация гиперпараметров (в нашем случае толькоскорость обученияа такжеколичество эпох) - строки 9 и 11;

Удостовериться ввсегда инициализируйте ваше случайное семядля обеспечениявоспроизводимостьваших результатов. Как обычно, случайное семя42,наименее случайныйиз всех случайных семян можно было выбрать :-)

Для каждой эпохи, имеютсячетыре шага тренировки:

  • Рассчитать прогнозы модели - этопрямой проход- строка 15;
  • Рассчитать убыток, используяпрогнозыи иэтикеткии соответствующийфункция потерьдля поставленной задачи - строки 18 и 20;
  • Вычислитьградиентыдля каждого параметра - строки 23 и 24;
  • Обновитьпараметры - строки 27 и 28;

Просто имейте в виду, что если вынеиспользовать пакетный градиентный спуск (наш пример это делает), вам придется написатьвнутренний циклвыполнитьчетыре шага тренировкидля каждогоиндивидуальная точка(стохастический) илиNточки(мини-партия). Позже мы увидим мини-пакетный пример.

Реализация градиентного спуска для линейной регрессии с использованием Numpy

Просто чтобы убедиться, что мы не сделали никаких ошибок в нашем коде, мы можем использоватьЛинейная регрессия Scikit-Learnчтобы соответствовать модели и сравнить коэффициенты.

# a and b after initialization
[0.49671415] [-0.1382643]
# a and b after our gradient descent
[1.02354094] [1.96896411]
# intercept and coef from Scikit-Learn
[1.02354075] [1.96896447]

Oнисовпадениедо 6 знаков после запятой - у нас естьполностью рабочая реализация линейной регрессиииспользуя Numpy.

ВремяФАКЕЛЭто :-)

PyTorch

Во-первых, нам нужно покрытьнесколько основных понятийэто может вывести вас из равновесия, если вы не поймете их достаточно хорошо, прежде чем приступить к моделированию.

В глубоком обучении мы видимтензорыгде угодно. Ну, фреймворк Google называетсяTensorFlowпо причине!Что такое тензор?

Тензор

ВNumpyВы можете иметьмассивкоторый имееттри измерения, правильно? То есть, технически говоря,тензор,

скаляр(один номер) имеетнульразмеры, авекторимеетодинразмерностьматрица имеет дваразмеры итензор имеет три или болееразмеры. Это оно!

Но, для простоты, обычным является также называть векторы и матрицы тензорами - так что теперьвсе скаляр или тензор,

Рисунок 2: Тензор - это просто многомерная матрица :-)Источник

Загрузка данных, устройств и CUDA

»Как нам перейти от массивов Numpy к тензорам PyTorch", ты спрашиваешь? Это то чтоfrom_numpyэто хорошо для. ВозвращаетТензор процессора, хотя.

«Но я хочу использовать свой модный графический процессор ...", ты говоришь. Не беспокойся, вот чтоto()это хорошо для. Он посылает ваш тензор в любойустройствоВы указываете, в том числе вашGPU(упоминается какcudaилиcuda:0).

«Что делать, если я хочу, чтобы мой код переключался на ЦП, если нет доступного графического процессора?”, Вы можете задаться вопросом… PyTorch снова получил вашу спину - вы можете использоватьcuda.is_available()чтобы выяснить, есть ли у вас графический процессор и настроить ваше устройство соответствующим образом.

Вы также можете легкобросатьэто с меньшей точностью (32-разрядное число с плавающей запятой), используяfloat(),

Загрузка данных: превращение массивов Numpy в тензоры PyTorch

Если вы сравнитетипыиз обеих переменных вы получите то, что ожидаете:numpy.ndarrayдля первого иtorch.Tensorдля второго

Но где живет твой симпатичный тензор? В вашем процессоре или вашем GPU? Вы не можете сказать ... но если вы используете PyTorchtype(), это покажет егоместо расположения-torch.cuda.FloatTensor- тензор GPU в этом случае.

Мы также можем пойти другим путем, превратив тензоры обратно в массивы Numpy, используяnumpy(), Это должно быть легко, какx_train_tensor.numpy()но...

TypeError: can't convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.

К сожалению, Numpyне могутобрабатывать тензоры GPU… сначала нужно сделать их тензорами CPU, используяcpu(),

Создание параметров

Что отличаеттензориспользуется дляданные- как те, которые мы только что создали - оттензориспользуется как (обучаемый)Параметр / вес?

Последние тензоры требуютвычисление его градиентов, значит мы можемОбновитьих значения (то есть значения параметров). Вот чтоrequires_grad=Trueаргумент хорош для. Он говорит PyTorch, что мы хотим, чтобы он вычислял градиенты для нас.

У вас может возникнуть соблазн создать простой тензор для параметра и затем отправить его на выбранное устройство, как мы сделали с нашими данными, верно? Не так быстро…

Попытка создать переменные для коэффициентов ...

Первый кусок кода создает два хороших тензора для наших параметров: градиенты и все. Но ониПроцессортензоры.

# FIRST
tensor([-0.5531], requires_grad=True)
tensor([-0.7314], requires_grad=True)

Во втором фрагменте кода мы попробовалинаивныйподход отправки их в наш графический процессор. Нам удалось отправить их на другое устройство, но мы"потерял"градиентыкак-то ...

# SECOND
tensor([0.5158], device='cuda:0', grad_fn=<CopyBackwards>) tensor([0.0246], device='cuda:0', grad_fn=<CopyBackwards>)

В третьем куске мыпервыйотправить наши тензоры вустройствоа такжетогдаиспользованиеrequires_grad_()способ установить егоrequires_gradвTrueна месте.

# THIRD
tensor([-0.8915], device='cuda:0', requires_grad=True) tensor([0.3616], device='cuda:0', requires_grad=True)

В PyTorch каждый метод, которыйконцысподчеркивать(_) вносит измененияна местезначит, они будутModifyосновная переменная.

Хотя последний подход работал нормально, гораздо лучшеправопреемниктензоры кустройствов момент ихсоздание,

Собственно создание переменных для коэффициентов :-)
tensor([0.6226], device='cuda:0', requires_grad=True) tensor([1.4505], device='cuda:0', requires_grad=True)

Намного проще, правда?

Теперь, когда мы знаем, как создавать тензоры, которые требуют градиентов, давайте посмотрим, как PyTorch обрабатывает их - в этом и заключается роль…

Автоград

Автоград - это PyTorch'sпакет автоматической дифференциации, Благодаря этому мыне нужно беспокоитьсяоколочастные производные, цепное правилоили что-нибудь подобное.

Итак, как мы говорим PyTorch делать свое дело ивычислить все градиенты? Это то чтоbackward()это хорошо для.

Ты помнишьотправная точкадлявычисление градиентов? Это былопотеря, так как мы вычислили его частные производные w.r.t. наши параметры. Следовательно, нам нужно вызватьbackward()метод из соответствующей переменной Python, например,loss.backward().

Что насчетфактические значенияизградиенты? Мы можем проверить их, посмотрев наgradатрибуттензор.

Если вы проверяете документацию метода, в ней четко указаноградиенты накапливаются, Таким образом, каждый раз, когда мы используемградиентывОбновитьпараметры, нам нужнообнулить градиенты впоследствии, И вот чтоzero_()это хорошо для.

Что этоподчеркивать(_) наконец имени методаозначать? Ты помнишь? Если нет, вернитесь к предыдущему разделу и узнайте.

Итак, начнемровруководство по эксплуатациирасчет градиентови использовать обаbackward()а такжеzero_()методы вместо.

Это оно? Ну, в значительной степени ... но всегда естьловитьи на этот раз это связано сОбновитьизпараметры...

В первой попытке, если мы используем ту же структуру обновления, что и в нашемNumpyкод, мы получим странныйошибканиже ... но мы можем получитьнамеко том, что происходит, посмотрев на сам тензор - еще раз мы"потерял"градиентпри переназначении результатов обновления нашим параметрам. Таким образомgradатрибут оказываетсяNoneи это поднимает ошибку ...

# FIRST ATTEMPT
tensor([0.7518], device='cuda:0', grad_fn=<SubBackward0>)
AttributeError: 'NoneType' object has no attribute 'zero_'

Затем мы немного изменим его, используя знакомуюлокальное назначение Pythonв нашей второй попытке. И, опять же, PyTorch жалуется на это и поднимаетошибка,

# SECOND ATTEMPT
RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.

Зачем?! Оказывается случаем"Слишком много хорошего", Виновником является способность PyTorch построитьграфик динамических вычисленийот каждогоОперация Pythonэто включает в себя любойтензор градиентных вычисленийилиего зависимости,

Мы углубимся во внутреннюю работу графа динамических вычислений в следующем разделе.

Итак, как мы говорим PyTorch"отвали"и позвольте намобновить наши параметрыне портямодный динамический граф вычислений? Это то чтоtorch.no_grad()это хорошо для. Это позволяет намвыполнять обычные операции Python над тензорами,не зависит от графика вычислений PyTorch,

Наконец, нам удалось успешно запустить нашу модель и получитьрезультирующие параметры, Конечно же, онисовпадениете, которые мы получили в нашемNumpyтолько реализация.

# THIRD ATTEMPT
tensor([1.0235], device='cuda:0', requires_grad=True) tensor([1.9690], device='cuda:0', requires_grad=True)

График динамических вычислений

«К сожалению, никто не может сказать, что такое график динамических вычислений. Вы должны увидеть это сами Морфеус

Как здорово былоМатрица«? Верно-верно? Но, шутки в сторону, я хочутывпосмотрите график самостоятельнослишком!

PyTorchVizпакет и егоmake_dot(variable)Метод позволяет нам легко визуализировать график, связанный с данной переменной Python.

Итак, давайте придерживатьсяминимум: два (градиентные вычисления)тензорыдля наших параметров, прогнозы, ошибки и потери.

Вычислительная MSE в три этапа

Если мы позвонимmake_dot(yhat)мы получимлеваяграфикна рисунке 3 ниже:

Рисунок 3: График вычислений для каждого шага в вычислении MSE

Давайте внимательнее посмотрим на его компоненты:

  • синие коробки: они соответствуюттензорымы используем какпараметрыте, которые мы просим PyTorchвычислять градиентыдля;
  • серая коробка:Операция Pythonэто включает в себятензор градиентных вычисленийилиего зависимости;
  • зеленая коробка: так же, как серая коробка, за исключением того, что этоотправная точка для расчетаградиентов (при условииbackward()метод вызывается изпеременная используется для визуализацииграфик) - они вычисляются извверх дномна графике.

Если мы построим графики дляerror(в центре) иloss(правильно)переменные,единственная разницамежду ними и первым номеромпромежуточные шаги(серые коробки).

Теперь внимательно рассмотримзеленая коробкаизлеваяграфик: естьдвастрелыуказывая на это, так как этодобавлениевверхдвапеременные,aа такжеb*x, Кажется очевидным, верно?

Затем посмотрите насерыйкоробкатого же графика: он выполняетумножениеа именноb*x, Но есть только одна стрелка, указывающая на это! Стрелка исходит отсинийкоробкачто соответствует нашемупараметрб,

Зачемразве у нас нет коробки для нашегоданные х? Ответ: мыне вычислять градиентыдля этого! Итак, хотя естьБольшетензоры, участвующие в операциях, выполняемых графом вычислений, этотолькошоуградиентно-вычислительные тензорыа такжеего зависимости,

Что будет с графом вычислений, если мы установимrequires_gradвFalseдля нашегопараметр а?

Рисунок 4: теперь переменная a НЕ рассчитывает свой градиент больше. Но это все еще используется в вычислениях

Неудивительно, чтосиняя коробкасоответствуетпараметр абольше не! Достаточно просто:нет градиентов, нет графика,

Лучшийвещь одинамический вычислительный графтот факт, что вы можете сделать этотак сложно, как вы хотитеЭто. Вы даже можете использоватьоператоры управления потоком(например, если заявления)контролировать поток градиентов(очевидно!) :-)

Рисунок 5 ниже показывает пример этого. И да, я знаю, что само вычислениеполностьюерунда...

Рисунок 5: Сложный граф вычислений, чтобы подчеркнуть :-)

оптимизатор

До сих пор мы быливручнуюобновление параметров с использованием вычисленных градиентов. Это, наверное, хорошо длядва параметра... но что если бы у нас былих много?! Мы используем один из PyTorch'sоптимизаторы, нравитсяSGDилиАдам,

Оптимизатор беретпараметрымы хотим обновить,скорость обучениямы хотим использовать (и, возможно, многие другие гиперпараметры!) ивыполняетобновлениячерез егоstep()метод.

Кроме того, нам больше не нужно обнулять градиенты один за другим. Мы просто вызываем оптимизаторzero_grad()метод и все!

В приведенном ниже коде мы создаемСтохастический градиентный спуск(SGD) оптимизатор для обновления наших параметрова такжеб,

Не обманывайте себяоптимизаторИмя: если мы используемвсе данные тренировкисразу для обновления - как мы на самом деле делаем в коде - оптимизатор выполняетпартияградиентный спуск, несмотря на его название.

Оптимизатор PyTorch в действии - больше не нужно обновлять параметры вручную!

Давайте проверим два наших параметра, до и после, просто чтобы убедиться, что все по-прежнему работает нормально:

# BEFORE: a, b
tensor([0.6226], device='cuda:0', requires_grad=True) tensor([1.4505], device='cuda:0', requires_grad=True)
# AFTER: a, b
tensor([1.0235], device='cuda:0', requires_grad=True) tensor([1.9690], device='cuda:0', requires_grad=True)

Круто! У насоптимизированныйоптимизацияпроцесс :-) Что осталось?

потеря

Теперь мы решаемрасчет потерь, Как и ожидалось, PyTorch снова нас охватил. Здесь очень многофункции потерьна выбор, в зависимости от поставленной задачи. Поскольку наша регрессия, мы используемСреднеквадратическая ошибка (MSE),

Заметить, чтоnn.MSELossна самом делесоздает функцию потерьдля нас -это не сама функция потерь, Кроме того, вы можете указатьметод сокращениябыть примененным, то естькак вы хотите агрегировать результаты по отдельным точкам- вы можете усреднить их (сокращение = «среднее») или просто суммировать их (сокращение = «сумма»).

Мы тогдаиспользованиесозданная функция потерь позже, в строке 20, чтобы вычислить потери с учетом нашегопрогнозыи нашэтикетки

Наш код выглядит следующим образом:

Потеря PyTorch в действии - больше не нужно вычислять потери вручную!

На данный момент осталось изменить только один фрагмент кода:прогнозы, Настало время представить PyTorch способ реализации…

модель

В PyTorch,модельпредставлен регулярнымКласс Pythonчто наследует отмодульучебный класс.

Наиболее фундаментальные методы, которые необходимо реализовать:

  • __init__(self):он определяет части, которые составляют модель- в нашем случае двапараметры,а такжеб,

Тынеограничено определениемпараметры, хотя…модели могут содержать другие модели (или слои) в качестве своих атрибутовтак что вы можете легко их вкладывать. Мы скоро увидим пример этого.

  • forward(self, x): он выполняетфактический расчетто естьвыводит прогноз, учитывая входИкс,

Вам следуетНЕ звонитеforward(x)метод, хотя. Вам следуетназвать всю модель самой, как вmodel(x)выполнять прямой проход и выводить прогнозы.

Давайте построим правильную (но простую) модель для нашей регрессионной задачи. Это должно выглядеть так:

Построение нашей «Ручной» модели, создание параметра за параметром!

в__init__метод, мы определяем нашдва параметра,а такжеб, используяParameter()класс, чтобы сказать PyTorch этитензорами следует считать параметры модели, которые они являются атрибутом,

Почему мы должны заботиться об этом? Таким образом, мы можем использовать наши моделиparameters()метод для извлеченияитератор по всем параметрам модели,четноеэти параметрывложенные модели, что мы можем использовать для подачи нашего оптимизатора (вместо того, чтобы самим составлять список параметров!).

Кроме того, мы можем получитьтекущие значения для всех параметровиспользуя наши моделиstate_dict()метод.

ВАЖНЫЙ: нам нужноотправить нашу модель на то же устройство, где данные, Если наши данные сделаны из тензоров графического процессора, наша модель также должна «жить» внутри графического процессора.

Мы можем использовать все эти удобные методы для изменения нашего кода, который должен выглядеть следующим образом:

Модель PyTorch в действии - больше никакого ручного предсказания / шаг вперед!

Теперь напечатанные операторы будут выглядеть так - конечные значения параметрова такжебвсе те же, так что все в порядке :-)

OrderedDict([('a', tensor([0.3367], device='cuda:0')), ('b', tensor([0.1288], device='cuda:0'))])
OrderedDict([('a', tensor([1.0235], device='cuda:0')), ('b', tensor([1.9690], device='cuda:0'))])

Я надеюсь, что вы заметили одно конкретное утверждение в коде, к которому я назначил комментарий"Что это?!?" -model.train(),

В PyTorch модели имеютtrain()метод, который, несколько разочаровывает,НЕ выполняет шаг тренировки, Его единственная цель состоит в том, чтобыустановить модель в режим обучения, Почему это важно? Некоторые модели могут использовать такие механизмы, какВыбыватьнапример, которые имеютотличное поведение в обучении и оценкефазы.

Вложенные модели

В нашей модели мы вручную создали два параметра для выполнения линейной регрессии. Давайте использовать PyTorch'sлинейныймодель как наш собственный атрибут, создавая таким образом вложенную модель.

Несмотря на то, что это явно надуманный пример, поскольку мы в значительной степени оборачиваем базовую модель, не добавляя в нее ничего полезного (или вообще!), Она хорошо иллюстрирует концепцию.

в__init__метод, мы создалиатрибуткоторый содержит нашвложенныйLinearмодель,

вforward()метод, мыназывать вложенную модель самойвыполнить прямой проход (обратите внимание, мынепризваниеself.linear.forward(x)!).

Построение модели с использованием линейного слоя PyTorch

Теперь, если мы называемparameters()метод этой модели,PyTorch будет рекурсивно определять параметры своих атрибутов, Вы можете попробовать это самостоятельно, используя что-то вроде:[*LayerLinearRegression().parameters()]чтобы получить список всех параметров. Вы также можете добавить новыйLinearатрибуты и, даже если вы не используете их в прямом проходе, они будутВсе ещебыть перечисленным подparameters(),

Последовательные модели

Наша модель была достаточно простой ... Вы можете подумать:«Зачем вообще строить для этого класс ?!»Ну, у вас есть точка ...

Дляпростые модели, что использоватьзаурядные слоигде выход слоя последовательно подается как вход для следующего, мы можем использовать, э, э…последовательныймодель :-)

В нашем случае мы бы построили последовательную модель с одним аргументом, то естьLinearслой, который мы использовали для обучения нашей линейной регрессии. Модель будет выглядеть так:

# Alternatively, you can use a Sequential model
model = nn.Sequential(nn.Linear(1, 1)).to(device)

Достаточно просто, верно?

Шаг обучения

Пока мы определилиоптимизатор,функция потерьимодель, Прокрутите немного вверх и взгляните на кодвнутри петли, Будет ли этоменятьесли бы мы использовалидругой оптимизатор, илипотеря, или дажемодель? Если нет, то как мы можем это сделать?более общий?

Ну, я думаю, мы могли бы сказать все эти строки кодавыполнить тренировочный шагучитывая тетри элемента(оптимизатор, потери и модель), Тофункциииэтикетки

Итак, как насчетнаписание функции, которая принимает эти три элементаа такжевозвращает другую функцию, которая выполняет шаг обучения,принимать набор функций и меток в качестве аргументов и возвращать соответствующие потери?

Затем мы можем использовать эту функцию общего назначения для созданияtrain_step() функция, которая будет вызываться внутри нашего цикла обучения. Теперь наш код должен выглядеть следующим образом ... посмотрим, каккрошечныйцикл обучения сейчас?

Построение функции для выполнения одного шага обучения!

Давайте отдохнем нашу тренировочную петлю и сосредоточимся на нашихданныекакое-то время ... до сих пор мы просто использовали нашиNumpy массивыоказалосьТензоры PyTorch, Но мы можем сделать лучше, мы можем построить ...

Dataset

В PyTorch,Набор данныхпредставлен регулярнымКласс Pythonчто наследует отDatasetучебный класс. Вы можете думать об этом как о некоем питонесписок кортежейкаждый кортеж соответствуетодна точка (особенности, метка),

Наиболее фундаментальные методы, которые необходимо реализовать:

  • __init__(self): занимаеткакие бы аргументынеобходимо построитьсписок кортежей- это может быть имя файла CSV, который будет загружен и обработан; это может бытьдва тензораодин для функций, другой для меток; или что-то еще, в зависимости от поставленной задачи.

Естьнет необходимости загружать весь набор данных в методе конструктора(__init__). Если вашнабор данных большой(десятки тысяч файлов изображений, например), загрузка его за один раз не будет эффективно использовать память. Рекомендуетсязагрузить их по требованию(когда бы ни__get_item__называется).

  • __get_item__(self, index): это позволяет набору данных бытьиндексированныйтак что это может работатькак список(dataset[i]) - это должновернуть кортеж (особенности, метка)соответствует запрашиваемой точке данных. Мы можем либо вернутьсоответствующие ломтикииз нашегопредварительно загруженынабор данных или тензоры или, как упоминалось выше,нагрузкаих по требованию(как в этомпример).
  • __len__(self): он должен просто вернутьразмервсего набора данных, поэтому, когда бы он ни отбирался, его индексирование ограничивалось фактическим размером.

Давайте создадим простой пользовательский набор данных, который принимает в качестве аргументов два тензора: один для функций, другой для меток. Для любого данного индекса наш класс набора данных вернет соответствующий срез каждого из этих тензоров. Это должно выглядеть так:

Создание наборов данных с использованием тензоров поездов

Еще раз, вы можете подумать:зачем проходить через все эти неприятности, чтобы обернуть пару тензоров в классе?». И, опять же, у вас есть точка ... если набор данных - это не что иное, какпара тензоровмы можем использовать PyTorch'sTensorDatasetкласс, который будет делать в значительной степени то, что мы сделали в нашем пользовательском наборе данных выше.

Вы заметили, что мы построили нашучебные тензорыиз массивов Numpy, но мыне отправил их на устройство? Итак, ониПроцессортензоры сейчас!Зачем?

Мыне хотим, чтобы все наши тренировочные данные загружались в тензоры графических процессоров, как мы делали в нашем примере до сих пор, потому чтозанимает местов нашем драгоценномОЗУ видеокарты.

Хорошо, хорошо, но опять же,Зачеммы строим набор данных в любом случае? Мы делаем это, потому что мы хотим использовать ...

DataLoader

До сих пор мы использоваливсе данные тренировкина каждом этапе обучения. Это былоградиентный спусквсе это время. Это хорошо для нашегосмешно маленький набор данныхКонечно, но если мы хотим серьезно относиться ко всему этому, мыдолжениспользованиемини-партияградиентный спуск. Таким образом, нам нужны мини-партии. Таким образом, нам нужнокусочекнаш набор данных соответственно. Хочешь ли ты это сделатьвручную?! И я нет!

Поэтому мы используем PyTorch'sDataLoaderкласс для этой работы. Мы говорим, чтоНабор данныхиспользовать (тот, который мы только что создали в предыдущем разделе), желаемыйразмер мини-партиии если мы хотимшарканьеэто или нет. Это оно!

нашпогрузчикбудет вести себя какитератор, значит мы можемперебратьа такжеполучить другую мини-партиюкаждый раз.

Создание загрузчика данных для наших данных обучения

Чтобы получить образец мини-пакета, можно просто выполнить команду ниже - она ​​вернет список, содержащий два тензора, один для функций, другой для меток.

next(iter(train_loader))

Как это меняет наш тренировочный цикл? Давайте проверим это!

Используя мини-пакетный градиентный спуск!

Две вещи сейчас разные: не только у нас естьвнутренний циклзагрузить каждыймини-партияот нашегоDataLoaderно, что более важно, мы сейчасотправка только одной мини-партии на устройство,

Для больших наборов данных,Загрузка данныхобразец за образцомПроцессортензор) используяDataset-х __get_item__а потомотправка всех образцовкоторые принадлежат одному и тому жемини-пакет сразу на ваш графический процессор(устройство) это путь, чтобы сделатьнаилучшее использование оперативной памяти вашей видеокарты,

Более того, если у вас естьмного графических процессоровДля обучения вашей модели лучше всего сохранять набор данных «независимым» и назначать партии для разных графических процессоров во время обучения.

Пока что мы сосредоточились наподготовкаданныетолько. Мы построилиНабор данныхизагрузчик данныхдля этого. Мы могли бы сделать то же самое дляПроверкаданные, используяТрещинамы выступили в начале этого поста ... или мы могли бы использоватьrandom_splitвместо.

Случайное разделение

PyTorch-хrandom_split()метод является простым и знакомым способом выполненияразделение обучения и валидации, Просто имейте в виду, что в нашем примере мы должны применить его квесь набор данных(не учебный набор данныхмы построили два раздела назад).

Затем для каждого подмножества данных мы строим соответствующийDataLoaderнаш код выглядит так:

Разделение набора данных на обучающие и проверочные наборы - путь PyTorch!

Теперь у нас естьзагрузчик данныхдля нашегопроверочный наборИтак, имеет смысл использовать его для…

оценка

Этопрошлойчасть нашего путешествия - нам нужно изменить цикл обучения, чтобы включитьоценка нашей моделивычислениепотеря проверки, Первым шагом является включение еще одного внутреннего цикла для обработкимини-партиикоторые приходят иззагрузчик проверкиотправив их туда жеустройствокак наша модель. Далее мы делаемпрогнозыиспользуя нашу модель (строка 23) и рассчитать соответствующийпотеря(строка 24).

Это в значительной степени, но естьдва маленьких,все же важно, вещи для рассмотрения:

  • torch.no_grad(): несмотря на то, что это не будет иметь никакого значения в нашей простой модели, этохорошая практикавзавернуть проверкувнутренний цикл с этимконтекстный менеджер, чтобы отключить любой расчет градиентачто вы можете случайно вызвать -градиенты относятся к обучениюне на этапах проверки;
  • eval(): единственное, что он делает, этоперевод модели в режим оценки(так же, как егоtrain()аналог), поэтому модель может корректировать свое поведение в отношении некоторых операций, таких какВыбывать,

Теперь наш тренировочный цикл должен выглядеть так:

Вычисление потери проверки

Естьчто-нибудь ещемы можем улучшить или изменить? Конечно, естьвсегда что-то ещедобавить в вашу модель - используяпланировщик скорости обучения, например. Но этот пост ужеслишком долготак что я остановлюсь прямо здесь.

«Где полный рабочий код со всеми наворотами?", ты спрашиваешь? Вы можете найти этоВот,

Последние мысли

Хотя этот пост былнамного длиннеечем я ожидал, когда я начал писать это, я бы не стал делать это иначе - я считаю, чтобольшинство необходимых шаговнужно идти к корыту, чтобыучиться, вструктурированнаяа такжедополнительныйКстати, какразрабатывать модели глубокого обучения с использованием PyTorch,

Надеемся, что после того, как вы закончите прорабатывать весь код в этом посте, вы сможете лучше оценить и легче пройти через официальный PyTorch.учебные пособия,

Если у вас есть какие-либо мысли, комментарии или вопросы, пожалуйста, оставьте комментарий ниже или свяжитесь со мной пощебет,

Оригинальная статья

Footer decor

© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map