Главная страница
Навигация по странице:

  • Визуализация Простой способ изображения скалярного произведения показан на рис. 1.14.γγ= Умножение матриц[x ][y

  • Производные функции нескольких векторных переменных

  • Визуализация По сути, мы хотим сделать что-то, представленное на рис. 1.17.γγ= Умножение матриц[x ][y ] N Входные данные

  • Производные векторных функций: продолжение

  • Рис. 1.18.

  • Рис. 1.19.

  • Проверка корректности результата

  • Вычислительный граф для двух матриц

  • Математическое представление

  • Рис. 1.21.

  • Самое интересное: обратный проход

  • Курсовая работа. Глубокое обучение


    Скачать 4.97 Mb.
    НазваниеГлубокое обучение
    АнкорКурсовая работа
    Дата26.06.2022
    Размер4.97 Mb.
    Формат файлаpdf
    Имя файлаVeydman_S_Glubokoe_obuchenie_Legkaya_razrabotka_proektov_na_Pyth.pdf
    ТипДокументы
    #615357
    страница4 из 22
    1   2   3   4   5   6   7   8   9   ...   22
    Создание новых признаков
    из уже существующих
    Возможно, самая распространенная операция в нейронных сетях — опре- деление взвешенной суммы признаков. Именно этот параметр усиливает определенные признаки и снимает акцент с других. Его можно рассма- тривать как новую функцию, полученную комбинированием существую- щих. Математически это скалярное произведение вектора характеристик и имеющего такой же размер вектора весов: w
    1
    , w
    2
    и дальше до w
    n
    . Давайте рассмотрим эту концепцию более подробно.

    Создание новых признаков из уже существующих
    39
    Математическое представление
    Если определить вектор весов отдельных признаков как
    ,
    скалярное произведение двух векторов будет выглядеть так:
    Обратите внимание, что эта операция представляет собой частный слу- чай умножения матриц. Фактически мы умножаем вектор-строку X на вектор-столбец W.
    Теперь посмотрим, как изобразить это графически.
    Визуализация
    Простой способ изображения скалярного произведения показан на рис. 1.14.
    γ
    γ
    = Умножение матриц
    [
    x
    ]
    [
    y
    ]
    N
    Входные данные
    Выходные
    Рис. 1.14. Скалярное произведение двух векторов
    Мы видим, что функция принимает два аргумента, роль которых могут играть объекты ndarray
    , и превращает их в один объект ndarray
    Но работу функций нескольких переменных можно представить и дру- гими способами. Например, выделив отдельные операции и входные данные, как показано на рис. 1.15 и 1.16.

    40
    Глава 1. Математическая база
    Промежуточные
    значения
    x
    1
    w
    1
    M
    x
    2
    w
    2
    M
    A
    x
    3
    w
    3
    M
    M
    1
    M
    2
    N
    M
    3
    Рис. 1.15. Другой способ представления операции умножения матриц x
    1
    w
    1
    x
    2
    w
    2
    F
    x
    3
    w
    3
    N
    Рис. 1.16. Третий способ предоставления умножения матриц
    Дело в том, что произведение матриц, как и его частный случай, скаляр- ное произведение, — это краткое представление для набора отдельных операций, которое, как мы увидим в следующем разделе, сокращает и процедуру вычисления производных.
    Код
    И наконец, несложный код, реализующий операцию умножения:
    def matmul_forward(X: ndarray,
    W: ndarray) -> ndarray:
    '''
    Прямой проход при умножении матриц.
    '''
    assert X.shape[1] == W.shape[0], \
    '''
    Для операции умножения число столбцов первого массива должно совпадать с числом строк второго; у нас же число столбцов первого массива равно {0}, а число строк второго равно {1}.
    '''

    Производные функции нескольких векторных переменных
    41
    .format(X.shape[1], W.shape[0])
    # умножение матриц
    N = np.dot(X, W)
    return N
    Этот код содержит оператор проверки, гарантирующий допустимость операции умножения. Раньше такая проверка не требовалась, так как мы имели дело с объектами ndarray одинакового размера, операции над которыми выполнялись поэлементно.
    Производные функции нескольких
    векторных переменных
    Производная функции одной переменной, например или
    , находится очень легко — достаточно применить соот- ветствующее правило. А как должна выглядеть производная векторных функций? Обозначим скалярное произведение и попробуем ответить на вопрос, что такое и
    Визуализация
    По сути, мы хотим сделать что-то, представленное на рис. 1.17.
    γ
    γ
    = Умножение матриц
    [
    x
    ]
    [
    y
    ]
    N
    Входные данные
    N = выходные
    ∂ γ
    ∂ x
    ∂ γ
    ∂ y
    Рис. 1.17. Обратный проход операции умножения матриц
    В случае обычных операций сложения и умножения такие производные вычисляются просто, как вы видели в ранее рассмотренных примерах.

    42
    Глава 1. Математическая база
    Но что делать в случае умножения матриц? Давайте посмотрим, как это выглядит математически.
    Математическое представление
    Первым делом следует определить «производную по матрице». Если вспомнить, что матрица — это удобная форма представления набора чи- сел, легко понять, что производная по матрице означает производную по каждому из ее элементов. Для вектора-строки X она будет выглядеть так:
    Но функция
    ν дает число:
    . Посмотрев на него внимательно, мы увидим, что изменение x
    1
    на
    ϵ единиц меняет N на w
    1
    ×ϵ единиц. То же самое происходит с остальными x
    i
    элементами. Поэтому:
    ,
    ,
    В результате получаем:
    Этот удивительно элегантный результат дает ключ к тому, почему глубокое обучение работает и может быть так точно реализовано.
    Используя аналогичные рассуждения, получим:

    Производные векторных функций: продолжение
    43
    Код
    Сложную часть рассуждений, то есть математический вывод ответа, мы уже проделали. Написать код легко:
    def matmul_backward_first(X: ndarray,
    W: ndarray) -> ndarray:
    '''
    Обратный проход для операции умножения матриц по первому аргументу.
    '''
    # обратный проход dNdX = np.transpose(W, (1, 0))
    return dNdX
    Рассчитанный здесь параметр dNdX
    представляет собой частную произ- водную каждого элемента вектора X по скалярному произведению N.
    Этот показатель мы в дальнейшем будем называть градиентом X. Дело в том, что каждому элементу вектора X, например x
    3
    , в массиве dNdx соответствует элемент (в нашем случае это dNdX
    [2]
    ), представляющий собой частную производную скалярного произведения N по х
    3
    . Далее в книге термин «градиент» будет обозначать многомерный аналог частной производной; точнее говоря, массив частных производных функции по каждому из ее аргументов.
    Производные векторных функций: продолжение
    Разумеется, модели глубокого обучения включают в себя целые цепочки операций как с векторными аргументами, так и с поэлементной обработ- кой поданного на вход массива ndarray
    . Поэтому сейчас мы рассмотрим процесс вычисления производной составной функции, которая включает в себя оба вида аргументов. Предположим, что функция
    ν(X, W) вычис- ляет скалярное произведение векторов X и W и передает полученный результат в функцию
    σ. Мы хотим получить ее частные производные, или, если использовать новую терминологию, вычислить градиент этой новой функции по переменным X и W. Уже в следующей главе я подробно расскажу, как это связано с работой нейронных сетей, а пока же мы про- сто учимся находить градиенты вычислительных графов произвольной сложности.

    44
    Глава 1. Математическая база
    Визуализация
    Схема на рис. 1.18 отличается от схемы на рис. 1.17 только добавленной в конец функцией
    σ.
    γ
    [x]
    [w]
    N
    S
    δ
    Рис. 1.18. Уже знакомая схема, к которой добавлена еще одна функция
    Математическое представление
    Формула такой функции выглядит очень просто:
    Код
    Теперь напишем ее код:
    def matrix_forward_extra(X: ndarray,
    W: ndarray, sigma: Array_Function) -> ndarray:
    '''
    Вычисление функции, в которой результат умножения матриц передается в следующую функцию.
    '''
    assert X.shape[1] == W.shape[0]
    # умножение матриц
    N = np.dot(X, W)
    # подаем результат умножения матриц на выход функции сигма
    S = sigma(N)
    return S
    Вычисление производной
    Обратный проход в этом случае представляет собой всего лишь небольшое расширение предыдущего примера.

    Производные векторных функций: продолжение
    45
    Математическое представление
    Так как
    — это вложенная функция, то есть
    , ее частная производная, например, по X должна выглядеть так:
    Но первая часть этого уравнения — это всего лишь:
    Так как
    σ — непрерывная функция, производную которой можно вычислить в любой точке, просто подставим в нее значение
    В предыдущем разделе мы вывели, что
    . Сделаем подста- новку и получим:
    Как и в предыдущем примере, появляется вектор, совпадающий по направ- лению с вектором X, что в финале дает число
    , умноженное на вектор-строку.
    Визуализация
    Схема из рис. 1.19 обратного прохода рассматриваемой функции напо- минает схему из предыдущего примера, которую вы видели на рис. 1.17.
    Просто сейчас появился еще один множитель. Он представляет собой производную функции
    σ, оцененную для значения, которое мы получили в качестве результата умножения матриц.
    γ
    [x]
    [w]
    N
    (N)
    S
    σ
    ∂ γ
    ∂ x
    ∂ γ
    ∂ w
    ∂ σ
    ∂ u
    Рис. 1.19. Обратный проход для векторной составной функции

    46
    Глава 1. Математическая база
    Код
    Код для обратного прохода выглядит очень просто:
    def matrix_function_backward_1(X: ndarray,
    W: ndarray, sigma: Array_Function) -> ndarray:
    '''
    Вычисление частной производной функции по первому аргументу.
    '''
    assert X.shape[1] == W.shape[0]
    # умножение матриц
    N = np.dot(X, W)
    # передача результата умножения матриц в функцию сигма
    S = sigma(N)
    # обратный проход dSdN = deriv(sigma, N)
    # вычисление dNdX
    dNdX = np.transpose(W, (1, 0))
    # произведение результатов; так как dNdX имеет размерность 1x1,
    # порядок множителей не имеет значения return np.dot(dSdN, dNdX)
    Обратите внимание, что, как и в предыдущем примере, мы вычисляем значение функции во время прямого прохода (здесь оно обозначено просто как N), а затем используем это значение для обратного прохода.
    Проверка корректности результата
    Как определить, правильно ли мы нашли производные? Есть простой способ. Нужно немного поменять входное значение и посмотреть, как это отразится на результате. Например, в рассматриваемом случае мы увидим, что вектор X равен:
    print(X)
    [[ 0.4723 0.6151 -1.7262]]

    Вычислительный граф для двух матриц
    47
    Если увеличить х
    3
    на 0,01, то есть с –1,726 до –1,716, значение функции, которое мы вычисляем во время прямого прохода, тоже должно немного увеличиться, что мы и видим на рис. 1.20.
    Текущее значение
    Текущее значение
    + 0.01
    Выходное значение x
    3
    Выход: новый
    Выход: исходный
    0.01
    Градиент = наклон в точке
    Разница должна быть близка к (исходное значение) +
    (градиент в точке) ₓ (0.01)
    Рис. 1.20. Проверка градиента
    А теперь выведем значение производной matrix_function_backward_1.
    Мы увидим, что наш градиент равен
    -0.1121
    :
    print(matrix_function_backward_1(X, W, sigmoid))
    [[ 0.0852 -0.0557 -0.1121]]
    Если мы правильно рассчитали градиент, увеличение переменной x
    3
    на 0,01 должно привести к уменьшению производной примерно на
    0,01 × -0,1121 = -0,001121
    . Любой другой результат, как в большую, так и в меньшую сторону, будет означать, что цепное правило не работает.
    В данном случае вычисления
    1
    показывают, что мы все посчитали верно!
    В заключение рассмотрим пример, в основе которого лежит весь ранее изученный материл. Этот пример непосредственно касается моделей, которые мы будем создавать в следующей главе.
    Вычислительный граф для двух матриц
    Как в машинном обучении в целом, так и в глубоком обучении в част- ности на вход подаются два двумерных массива, один из которых пред-
    1
    Код для материалов этой главы вы найдете в репозитории GitHub oreil.ly/2ZUwKOZ

    48
    Глава 1. Математическая база ставляет набор данных X, а второй — их веса W. В следующей главе мы подробно поговорим о причинах такого представления данных, а пока сосредоточимся на математической стороне дела. В частности, мы по- кажем, что цепное правило работает даже после перехода от скалярного произведения векторов к произведению матриц и что написать код для вычисления такой производной по-прежнему очень просто.
    Математические расчеты, как и в предыдущих случаях, не сложные, но объемные. При этом они дают краткий и лаконичный результат. Разуме- ется, мы рассмотрим процесс пошагово и свяжем его с кодом и схемами.
    Математическое представление
    Пусть
    ,
    а
    Это может быть набор данных, в котором каждому наблюдению сопо- ставлены три признака. Три строки соответствуют трем наблюдениям, для которых мы хотим получить некий прогноз.
    Проделаем с этими матрицами следующие операции:
    1. Найдем их произведение N =
    ν(X, W).
    2. Подадим выходные данные функции N на вход дифференцируемой функции
    σ, обозначив это как S = σ(N).
    Как и прежде, требуется найти градиенты S по аргументам X и W и опре- делить, можно ли в этом случае воспользоваться цепным правилом.
    Отличие от ранее рассмотренных случаев состоит в том, что теперь при- дется иметь дело с матрицами, а не с числами. Возникает вопрос, что такое градиент одной матрицы по другой?

    Вычислительный граф для двух матриц
    49
    Нам доступны различные действия с многомерными массивами, но чтобы корректно определить понятие «градиент» по выходным данным, нужно суммировать (или каким-то другим способом превратить в одно число) последний массив последовательности. Только тогда будет иметь смысл вопрос: «Насколько изменение каждого элемента матрицы X повлияет на конечный результат?»
    Поэтому мы добавим функцию лямбда, суммирующую элементы функ- ции S.
    Теперь опишем это языком формул. Начнем с произведения матриц X и W:
    Элемент результирующей матрицы, расположенный в ряду i и столбце j, для удобства обозначим XW
    ij
    Передадим полученную матрицу в функцию
    σ, что означает применение этой функции ко всем элементам произведения матриц X
    ×W:
    =
    После этого остается найти сумму всех элементов:

    50
    Глава 1. Математическая база
    Теперь все свелось к уже знакомой задаче из математического анализа: есть функция L и нужно найти ее градиент по переменным X и W, чтобы узнать, насколько на нее повлияет изменение каждого элемента входных матриц (x
    11
    , w
    21
    и т. д.). Математически это записывается следующим образом:
    Теперь давайте посмотрим, как наша задача описывается языком схем, и напишем соответствующий код.
    Визуализация
    Концептуально мы делаем то же, что и в предыдущих примерах с вы- числительными графами для многих переменных. Вы без труда сможете понять рис. 1.21.
    γ
    [x]
    [w]
    N
    S
    δ
    L
    Λ
    Рис. 1.21. Граф прямого прохода сложной функции
    Мы просто передаем данные в функцию и утверждаем, что и в этом случае цепное правило позволит вычислить нужные градиенты.
    Код
    Вот как может выглядеть наш код:
    def matrix_function_forward_sum(X: ndarray,
    W: ndarray, sigma: Array_Function) -> float:
    '''
    Прямой проход функции объектов ndarray X и W и функции sigma.

    Самое интересное: обратный проход
    51
    '''
    assert X.shape[1] == W.shape[0]
    # умножение матриц
    N = np.dot(X, W)
    # передача результата умножения матриц в функцию сигма
    S = sigma(N)
    # сумма всех элементов
    L = np.sum(S)
    return L
    Самое интересное: обратный проход
    Пришло время выполнить обратный проход и показать, каким образом, даже в случае умножения матриц, мы можем вычислить градиент N по каждому элементу входных объектов ndarray
    1
    . Как только вы поймете, как все происходит, то без проблем сможете перейти к обучению моделей, которым мы и займемся в главе 2. Первым делом вспомним, что нужно делать.
    Визуализация
    Выполняем уже знакомую по предыдущим примерам процедуру; рис. 1.22 должен быть вам знаком, как и рис. 1.21.
    γ
    [x]
    [w]
    N
    (N)
    S
    σ
    L
    Λ
    ∂ γ
    ∂ x
    ∂ γ
    ∂ w
    ∂ σ
    ∂ u
    (S)
    ∂ Λ
    ∂ u
    Рис. 1.22. Обратный проход через сложную функцию
    1
    Так как градиенты N по X и по W вычисляется одинаково, мы рассмотрим только градиент по X.

    52
    Глава 1. Математическая база
    Нужно вычислить частную производную каждого элемента функции, оце- нить ее по входным данным и перемножить результаты, чтобы получить окончательную производную. Давайте по очереди рассмотрим каждую из частных производных.
    Математическое представление
    Отимечу, что все вычисления можно произвести, что называется, в лоб.
    Ведь L — это функция переменных x
    11
    , x
    12
    и т. д., до переменной x
    33
    Но эта задача выглядит очень сложной. В конце концов, смысл цепного правила в том, что оно позволяет разбивать сложные функции на со- ставные части, производить вычисления с этими частями и перемножать результаты. Именно это дает возможность легко писать код вычисления производных: достаточно пошагово выполнить прямой проход, сохранить результат и использовать его для оценки нужных нам производных во время обратного прохода.
    Я покажу, что этот подход работает и в случае матриц.
    Обозначим функцию как L. Будь это обычная функция скалярных переменных, то в соответствии с цепным правилом можно было бы написать:
    После чего оставалось бы по очереди вычислить каждую из трех частных производных. Именно так мы поступали раньше в примере с тремя вло- женными функциями. Согласно рис. 1.22, этот подход должен сработать и сейчас.
    Вычислить последний компонент просто. Мы хотим узнать, насколько возрастет функция L при увеличении каждого элемента функции S. Как вы помните, L представляет собой сумму всех элементов функции S, и производная будет:
    ,

    Самое интересное: обратный проход
    53
    так как увеличение любого элемента функции S, например, на 0.46 еди- ницы будет увеличивать
    Λ на те же 0.46 единицы.
    Дальше следует компонент
    . Это производная функции
    σ, оценен- ная для рассматриваемого значения элементов произведения матриц N.
    Перейдя к ранее использовавшемуся обозначению XW, получим:
    Ничто не мешает выполнить поэлементное умножение двух производных и вычислить
    :
    А дальше начинаются сложности. Ведь согласно схеме и цепному прави- лу, дальше нужно вычислить
    . Еще раз напомню, что N — это вывод функции
    ν, то есть результат умножения матриц X и W. Фактически мы хотим понять, насколько увеличение каждого элемента матрицы X (раз- мером 3
    × 3) повлияет на каждый элемент матрицы N (размером 3 × 2).
    Не совсем понятно, каким образом выполнить такой переход между матрицами разной формы и будет ли это вообще иметь смысл.
    Если помните, раньше нам везло. Ведь матрица X оказывалась транспо- нированной матрицей W, и наоборот. И мы получали, что
    ,

    1   2   3   4   5   6   7   8   9   ...   22


    написать администратору сайта