Курсовая работа. Глубокое обучение
Скачать 4.97 Mb.
|
12 Предисловие возможности пошагового подхода для определения моделей глубо- кого обучения. И тут на сцену выходит глава 3. 3. В главе 3 мы возьмем строительные блоки из подхода «сначала суть» из первых двух глав и используем их для построения компонентов более высокого уровня, которые составляют все модели глубокого обучения: слои, модели, оптимизаторы и т. д. Мы закончим эту главу обучением модели глубокого обучения, заданной с нуля, на наборе данных из главы 2 и покажем, что она работает лучше, чем простая нейронная сеть. 4. Как выясняется, существует несколько теоретических предпосылок того, что нейронная сеть с заданной архитектурой действительно найдет хорошее решение для данного набора данных при обучении с использованием стандартных методов обучения, которые мы будем использовать в этой книге. В главе 4 мы поговорим о хитростях, при- меняемых в процессе обучения, которые обычно увеличивают веро- ятность того, что нейронная сеть найдет хорошее решение, и по воз- можности дадим математическое описание, почему они работают. 5. В главе 5 мы обсудим фундаментальные идеи, лежащие в основе сверточных нейронных сетей (CNN), разновидности архитектуры нейронных сетей, специализирующихся на распознавании изобра- жений. Существует множество объяснений принципов работы CNN, поэтому я сосредоточусь на самых основных понятиях о CNN и их отличиях от обычных нейронных сетей: в частности, как CNN делают так, что каждый слой нейронов превращается в «карты признаков», и как два из этих слоев (каждый из которых состоит из нескольких карт объектов) связываются друг с другом посредством сверточных фильтров. Кроме того, что мы будем писать обычные слои в нейрон- ной сети с нуля, а также сверточные слои с нуля, чтобы укрепить понимание того, как они работают. 6. В первых пяти главах мы создадим миниатюрную библиотеку ней- ронных сетей, которая определяет нейронные сети как серию слоев, которые сами состоят из серии операций, которые передают вход- ные данные вперед и градиенты назад. Но большинство нейронных сетей реализуются на практике не так. Вместо этого используется техника, называемая автоматическим дифференцированием. Я при- веду краткий обзор автоматического дифференцирования в начале главы 6 и далее использую его для основной темы главы: рекур- Условные обозначения 13 рентных нейронных сетей (RNN), архитектуры нейронных сетей, обычно используемых для анализа данных, в которых точки данных появляются последовательно, например временнˆых данных или естественного языка. Я объясню работу «классических RNN» и двух вариантов: GRU и LSTM (и конечно, мы реализуем все их с нуля). Далее мы опишем элементы, которые являются общими для всех этих вариантов RNN, и некоторые различия между этими вариантами. 7. В заключение, в главе 7, я покажу, как все, что мы делали с нуля в главах 1–6, может быть реализовано с использованием высокопро- изводительной библиотеки с открытым исходным кодом PyTorch. Изучение такой структуры очень важно для развития вашего знания нейронных сетей; но погружение и изучение структуры без пред- варительного понимания того, как и почему работают нейронные сети, серьезно ограничит ваше обучение в долгосрочной перспекти- ве. Цель такого порядка глав в этой книге — дать вам возможность писать чрезвычайно высокопроизводительные нейронные сети (с помощью PyTorch), настраивая при этом вас на долгосрочное об- учение и успех (через изучение основ). В конце мы приведем крат- кую иллюстрацию того, как нейронные сети могут использоваться для обучения без учителя. Моя идея состояла в том, чтобы написать книгу, которую я сам бы хотел почитать, когда только начинал изучать эту тему несколько лет назад. Надеюсь, книга будем вам полезна. Вперед! Условные обозначения В книге используются следующие типографские обозначения: Курсив Используется для обозначения новых терминов. Моноширинный шрифт Применяется для оформления листингов программ и программных элементов внутри обычного текста, таких как имена переменных и функций, баз данных, типов данных, переменных среды, операторов и ключевых слов. 14 Предисловие Моноширинный жирный Обозначает команды или другой текст, который должен вводиться пользователем. Моноширинный курсив Обозначает текст, который должен замещаться фактическими значе- ниями, вводимыми пользователем или определяемыми из контекста. Теорема Пифагора: a 2 + b 2 = c 2 Так обозначаются советы, предложения и примечания общего ха- рактера. Использование примеров кода Дополнительный материал (примеры кода, упражнения и т. д.) можно скачать по адресу репозитория книги на GitHub по адресу oreil.ly/deep- learning-github Благодарности Благодарю своего редактора Мелиссу Поттер вместе с командой из O'Reilly, которые были внимательны и отвечали на мои вопросы на про- тяжении всего процесса. Выражаю особую благодарность нескольким людям, чья работа по созданию технических концепций в области машинного обучения, до- ступная для более широкой аудитории, вдохновила меня, и тем, кого мне посчастливилось узнать лично: это, в частности, Брэндон Рорер, Джоэл Грус, Джереми Уотт и Эндрю Траск. Благодарю своего босса в Metis и директора в Facebook, которые под- держивали меня, когда я пытался выкроить время на работу над этим проектом. Благодарю Мэта Леонарда, который недолгое время был моим соавтором, после чего наши пути разошлись. Мэт помог организовать код в мини- От издательства 15 малистичном стиле — lincoln — и дал очень полезную обратную связь по поводу сырых вариантов первых двух глав, написав свои собственные версии больших разделов этих глав. Наконец, благодарю своих друзей Еву и Джона, которые вдохновили меня на решительный шаг и фактически заставили меня начать писать. Я также хотел бы поблагодарить моих многочисленных друзей в Сан-Франциско, которые терпели мое волнение, переживали вместе со мной по поводу книги и оказывали всяческую поддержку, хотя в течение многих месяцев я не мог найти время потусоваться с ними. От издательства Некоторые иллюстрации снабжены QR-кодом. Перейдя по ссылке, вы сможете посмотреть их цветную версию. Ваши замечания, предложения, вопросы отправляйте по адресу comp@ piter.com (издательство «Питер», компьютерная редакция). Мы будем рады узнать ваше мнение! На веб-сайте издательства www.piter.com вы найдете подробную информа- цию о наших книгах. ГЛАВА 1 Математическая база Не нужно запоминать эти формулы. Если вы поймете прин- ципы, по которым они строятся, то сможете придумать соб- ственную систему обозначений. Джон Кохран, методическое пособие Investments Notes, 2006 В этой главе будет заложен фундамент для понимания работы нейронных сетей — вложенные математические функции и их производные. Мы пройдем весь путь от простейших строительных блоков до «цепочек» со- ставных функций, вплоть до функции многих переменных, внутри которой происходит умножение матриц. Умение находить частные производные таких функций поможет вам понять принципы работы нейронных сетей, речь о которых пойдет в следующей главе. Каждую концепцию мы будем рассматривать с трех сторон: y математическое представление в виде формулы или набора урав- нений; y код, по возможности содержащий минимальное количество до- полнительного синтаксиса (для этой цели идеально подходит язык Python); y рисунок или схема, иллюстрирующие происходящий процесс. Благодаря такому подходу мы сможем исчерпывающе понять, как и по- чему работают вложенные математические функции. С моей точки зрения, любая попытка объяснить, из чего состоят нейронные сети, не раскрывая все три аспекта, будет неудачной. И начнем мы с такой простой, но очень важной математической концеп- ции, как функция. Функции 17 Функции Как описать, что такое функция? Разумеется, я мог бы ограничиться фор- мальным определением, но давайте рассмотрим эту концепцию с разных сторон, как ощупывающие слона слепцы из притчи. Математическое представление Вот два примера функций в математической форме записи: y y Записи означают, что функция f 1 преобразует входное значение x в x 2 , а функция f 2 возвращает наибольшее значение из набора (x, 0). Визуализация Вот еще один способ представления функций: 1. Нарисовать плоскость xy (где x соответствует горизонтальной оси, а y — вертикальной). 2. Нарисовать на этой плоскости набор точек, x-координаты которых (обычно равномерно распределенные) соответствуют входным зна- чениям функции, а y-координаты — ее выходным значениям. 3. Соединить эти точки друг с другом. Французский философ и математик Рене Декарт первым использовал подобное представление, и его начали активно применять во многих областях математики, в частности в математическом анализе. Пример графиков функций показан на рис. 1.1. Есть и другой способ графического представления функций, который почти не используется в матанализе, но удобен, когда речь заходит о мо- делях глубокого обучения. Функцию можно сравнить с черным ящиком, который принимает значение на вход, преобразует его внутри по каким- то правилам и возвращает новое значение. На рис. 1.2 показаны две уже знакомые нам функции как в общем виде, так и для отдельных входных значений. 18 Глава 1. Математическая база Квадратичная функция Активационная функция ReLU Входные данные Входные данные Вы хо дные данны е Вы хо дные данны е Рис. 1.1. Две непрерывные дифференцируемые функции Квадра- тичная n n 2 ReLU x max(x, 0) 2 4 ReLU 2 2 -3 9 ReLU -3 0 Квадра- тичная Квадра- тичная Определение Рис. 1.2. Другой способ представления тех же функций Код Наконец, можно описать наши функции с помощью программного кода. Но для начала я скажу пару слов о библиотеке NumPy, которой мы вос- пользуемся. Примечание № 1. NumPy Библиотека NumPy для Python содержит реализации вычислительных алгоритмов, по большей части написанные на языке C и оптимизиро- Функции 19 ванные для работы с многомерными массивами. Данные, с которыми работают нейронные сети, всегда хранятся в многомерных массивах, чаще всего в дву- или трехмерных. Объекты ndarray из библиотеки NumPy дают возможность интуитивно и быстро работать с этими массивами. Напри- мер, если сохранить данные в виде обычного или многомерного списка, обычный синтаксис языка Python не позволит выполнить поэлементное сложение или умножение списков, зато эти операции прекрасно реали- зуются с помощью объектов ndarray : print("операции со списками на языке Python:") a = [1,2,3] b = [4,5,6] print("a+b:", a+b) try: print(a*b) except TypeError: print("a*b не имеет смысла для списков в языке Python") print() print("операции с массивами из библиотеки numpy:") a = np.array([1,2,3]) b = np.array([4,5,6]) print("a+b:", a+b) print("a*b:", a*b) операции со списками на языке Python: a+b: [1, 2, 3, 4, 5, 6] a*b не имеет смысла для списков в языке Python операции с массивами из библиотеки numpy: a+b: [5 7 9] a*b: [ 4 10 18] Объект ndarray обладает и таким важным для работы с многомерными массивами атрибутом, как количество измерений. Измерения еще на- зывают осями. Их нумерация начинается с 0, соответственно первая ось будет иметь индекс 0, вторая — 1 и т. д. В частном случае двумерного массива нулевую ось можно сопоставить строкам, а первую — столбцам, как показано на рис. 1.3. Эти объекты позволяют интуитивно понятным способом совершать различные операции с элементами осей. Например, суммирование строки или столбца двумерного массива приводит к «свертке» вдоль 20 Глава 1. Математическая база соответствующей оси, возвращая массив на одно измерение меньше исходного: print('a:') print(a) print('a.sum(axis=0):', a.sum(axis=0)) print('a.sum(axis=1):', a.sum(axis=1)) a: [[1 2] [3 4]] a.sum(axis=0): [4 6] a.sum(axis=1): [3 7] Ось 1 Ось 0 Рис. 1.3. Двумерный массив из библиотеки NumPy, в котором ось с индексом 0 соответствует строкам, а ось с индексом 1 — столбцам Наконец, объект ndarray поддерживает такую операцию, как сложение с одномерным массивом. Например, к двумерному массиву a, состоящему из R строк и C столбцов, можно прибавить одномерный массив b дли- ной C, и библиотека NumPy выполнит сложение для элементов каждой строки массива a 1 : a = np.array([[1,2,3], [4,5,6]]) b = np.array([10,20,30]) print("a+b:\n", a+b) a+b: [[11 22 33] [14 25 36]] 1 Позднее это позволит нам легко добавлять смещение к результатам умножения матриц. Функции 21 Примечание № 2. Функции с аннотациями типов Как я уже упоминал, код в этой книге приводится как дополнительная иллюстрация, позволяющая более наглядно представить объясняемые концепции. Постепенно эта задача будет усложняться, так как функции с несколькими аргументами придется писать как часть сложных клас- сов. Для повышения информативности такого кода мы будем добавлять в определение функций аннотации типов; например, в главе 3 нейронные сети будут инициализироваться вот так: def __init__(self, layers: List[Layer], loss: Loss, learning_rate: float = 0.01) -> None: Такое определение сразу дает представление о назначении класса. Вот для сравнения функция operation : def operation(x1, x2): Чтобы понять назначение этой функции, потребуется вывести тип каждо- го объекта и посмотреть, какие операции с ними выполняются. А теперь переопределим эту функцию следующим образом: def operation(x1: ndarray, x2: ndarray) -> ndarray: Сразу понятно, что функция берет два объекта ndarray , вероятно, каким-то способом комбинирует и выводит результат этой комбинации. В дальней- шем мы будем снабжать аннотациями типов все определения функций. Простые функции в библиотеке NumPy Теперь мы готовы написать код определенных нами функций средствами библиотеки NumPy: def square(x: ndarray) -> ndarray: ''' Возведение в квадрат каждого элемента объекта ndarray. ''' return np.power(x, 2) def leaky_relu(x: ndarray) -> ndarray: ''' Применение функции "Leaky ReLU" к каждому элементу ndarray. ''' return np.maximum(0.2 * x, x) 22 Глава 1. Математическая база Библиотека NumPy позволяет применять многие функции к объ- ектам ndarray двумя способами: np.function_name (ndarray) или ndarray.function_name . Например, функцию relu можно было на- писать как x.clip (min = 0) . В дальнейшем мы будем пользоваться записью вида np.function_name (ndarray) . И даже когда альтерна- тивная запись короче, как, например, в случае транспонирования двумерного объекта ndarray , мы будем писать не ndarray.T , а np. trans pose (ndarray, ( 1, 0)) Постепенно вы привыкнете к трем способам представления концепций, и это поможет по-настоящему понять, как происходит глубокое обучение. Производные Понятие производной функции, скорее всего, многим из вас уже знако- мо. Производную можно определить как скорость изменения функции в рассматриваемой точке. Мы подробно рассмотрим это понятие с разных сторон. Математическое представление Математически производная определяется как предел отношения прира- щения функции к приращению ее аргумента при стремлении приращения аргумента к нулю: Можно численно оценить этот предел, присвоив переменной Δ маленькое значение, например 0.001: Теперь посмотрим на графическое представление нашей производной. Производные 23 Визуализация Начнем с общеизвестного способа: если нарисовать касательную к де- картову представлению функции f, производная функции в точке каса- ния будет равна угловому коэффициенту касательной. Вычислить этот коэффициент, или тангенс угла наклона прямой, можно, взяв разность значений функции f при a – 0.001 и a + 0.001 и поделив на величину при- ращения, как показано на рис. 1.4. a a + 0.001 f(a + 0.001) f(a – 0.001) f(a) a – 0.001 Наклон = f(a + 0.001) – f(a – 0.001) (0.002) Рис. 1.4. Производная как угловой коэффициент На рисунке производную можно представить в виде множителя, кратно которому меняется выходное значение функции при небольшом изме- нении подаваемого на вход значения. Фактически мы меняем значение входного параметра на очень маленькую величину и смотрим, как при этом поменялось значение на выходе. Схематично это представлено на рис. 1.5. Квадрат. функция 2 0.001 4 ? Рис. 1.5. Альтернативный способ визуализации концепции производной Со временем вы увидите, что для понимания глубокого обучения второе представление оказывается важнее первого. 24 Глава 1. Математическая база Код И наконец, код для вычисления приблизительного значения производной: from typing import Callable def deriv(func: Callable[[ndarray], ndarray], input_: ndarray, delta: float = 0.001) -> ndarray: ''' Вычисление производной функции "func" в каждом элементе массива "input_". ''' return (func(input_ + delta) — func(input_ — delta)) / (2 * delta) Выражение «P — это функция E» (я намеренно использую тут случай- ные символы) означает, что некая функция f берет объекты E и пре- вращает в объекты P, как показано на рисунке. Другими словами, P — это результат применения функции f к объектам E: f E P А вот так выглядит соответствующий код: def f(input_: ndarray) -> ndarray: # Какое-то преобразование return output P = f(E) Вложенные функции Вот мы и дошли до концепции, которая станет фундаментом для пони- мания нейронных сетей. Это вложенные, или составные, функции. Дело в том, что две функции f 1 и f 2 можно связать друг с другом таким образом, что выходные данные одной функции станут входными для другой. |