Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.4 Математический анализ

2500 лет назад древние греки нашли способ вычислять площадь многоугольника: они разбивали его на треугольники и складывали их площади. Чтобы найти площадь фигуры криволинейной формы (например, круга), древние греки вписывали в неё многоугольники. Как показано на рис. 2.4.1, чем больше равных сторон у вписанного многоугольника, тем он ближе к кругу. Этот процесс также называют методом исчерпывания (method of exhaustion).

Рис. 2.4.1. Вычисление площади круга методом исчерпывания

На самом деле метод исчерпывания — начало интегрального исчисления (integral calculus). Более 2000 лет спустя появилась другая ветвь математического анализа — дифференциальное исчисление (differential calculus). Важнейшее применение дифференциального исчисления — задачи оптимизации, то есть поиск наилучшего способа что-то сделать. Как обсуждалось в разделе 2.3.10.1, такие задачи повсеместны в глубоком обучении.

В глубоком обучении мы «обучаем» модели, постоянно их обновляя, чтобы по мере поступления всё большего количества данных модели становились лучше. Обычно «стать лучше» означает минимизацию функции потерь (loss function) — оценки того, насколько «плоха» модель. В конечном счёте нас интересует создание модели, которая хорошо работает на данных, которых она никогда не видела. Но «обучение» модели может лишь подогнать её под данные, которые мы реально видим. Поэтому задачу подгонки модели можно разбить на две ключевые части:

  • оптимизация (optimization): подгонка модели под наблюдаемые данные;
  • обобщение (generalization): математические принципы и практический опыт, которые помогают получать модели, работающие на данных, выходящих за пределы обучающего набора.

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

2.4.1 Производные и дифференциалы

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

Пусть есть функция , входные и выходные значения которой — скаляры. Если у существует производная, её определяет предел

Если существует, то функция называется дифференцируемой (differentiable) в точке . Если дифференцируема в каждой точке некоторого интервала, то она дифференцируема на этом интервале. Производную из формулы (2.4.1) можно интерпретировать как мгновенную (instantaneous) скорость изменения по . Такая мгновенная скорость изменения вычисляется по изменению аргумента при стремлении к .

Чтобы лучше понять производную, проведём эксперимент. Определим следующим образом:

MXNet:

%matplotlib inline
from d2l import mxnet as d2l
from matplotlib_inline import backend_inline
from mxnet import np, npx
npx.set_np()

def f(x):
    return 3 * x ** 2 - 4 * x

PyTorch:

%matplotlib inline
from d2l import torch as d2l
from matplotlib_inline import backend_inline
import numpy as np

def f(x):
    return 3 * x ** 2 - 4 * x

TensorFlow:

%matplotlib inline
from d2l import tensorflow as d2l
from matplotlib_inline import backend_inline
import numpy as np

def f(x):
    return 3 * x ** 2 - 4 * x

PaddlePaddle:

%matplotlib inline
from d2l import paddle as d2l
from matplotlib_inline import backend_inline
import numpy as np

def f(x):
    return 3 * x ** 2 - 4 * x

Положив и устремив к , получим, что численное значение в формуле (2.4.1) приближается к . Хотя этот эксперимент не является математическим доказательством, позже мы увидим, что при производная действительно равна .

def numerical_lim(f, x, h):
    return (f(x + h) - f(x)) / h

h = 0.1
for i in range(5):
    print(f'h={h:.5f}, численный предел={numerical_lim(f, 1, h):.5f}')
    h *= 0.1

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

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

  • ( — константа)
  • (степенное правило (power rule), — любое действительное число)

Чтобы продифференцировать функцию, составленную из простых функций, полезны следующие правила. Пусть функции и дифференцируемы, а — константа. Тогда:

Правило константного множителя

Правило суммы

Правило произведения

Правило частного

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

Чтобы наглядно изобразить эту интерпретацию производной, будем использовать matplotlib — популярную библиотеку построения графиков на Python. Чтобы настроить свойства графиков, создаваемых matplotlib, нужно определить несколько вспомогательных функций. Функция use_svg_display указывает пакету matplotlib выводить графики в формате svg для более чёткого изображения.

Обратите внимание: комментарий #@save — специальная отметка, которая сохраняет соответствующую функцию, класс или оператор в пакете d2l. Поэтому их можно вызывать напрямую (например, d2l.use_svg_display()), не определяя заново.

def use_svg_display():  #@save
    """Показывает графики в формате svg в Jupyter"""
    backend_inline.set_matplotlib_formats('svg')

Функция set_figsize задаёт размер графиков. Здесь можно напрямую использовать d2l.plt, поскольку импорт from matplotlib import pyplot as plt помечен для сохранения в пакете d2l.

def set_figsize(figsize=(3.5, 2.5)):  #@save
    """Задаёт размер графика matplotlib"""
    use_svg_display()
    d2l.plt.rcParams['figure.figsize'] = figsize

Функция set_axes задаёт свойства осей графика, построенного с помощью matplotlib.

#@save
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
    """Задаёт оси графика matplotlib"""
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel)
    axes.set_xscale(xscale)
    axes.set_yscale(yscale)
    axes.set_xlim(xlim)
    axes.set_ylim(ylim)
    if legend:
        axes.legend(legend)
    axes.grid()

Имея эти три функции для настройки графика, определим функцию plot, которая позволяет кратко строить несколько кривых: в книге нам часто придётся визуализировать множество кривых.

#@save
def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,
         ylim=None, xscale='linear', yscale='linear',
         fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):
    """Строит точки данных"""
    if legend is None:
        legend = []

    set_figsize(figsize)
    axes = axes if axes else d2l.plt.gca()

    # Если у X одна ось, возвращаем True
    def has_one_axis(X):
        return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
                and not hasattr(X[0], "__len__"))

    if has_one_axis(X):
        X = [X]
    if Y is None:
        X, Y = [[]] * len(X), X
    elif has_one_axis(Y):
        Y = [Y]
    if len(X) != len(Y):
        X = X * len(Y)
    axes.cla()
    for x, y, fmt in zip(X, Y, fmts):
        if len(x):
            axes.plot(x, y, fmt)
        else:
            axes.plot(y, fmt)
    set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)

Теперь можем построить график функции и её касательной в точке , где коэффициент — угловой коэффициент касательной.

x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Касательная (x=1)'])

2.4.2 Частные производные

До сих пор мы рассматривали дифференцирование только функций одной переменной. В глубоком обучении функции обычно зависят от многих переменных. Поэтому нужно обобщить идею дифференцирования на функции многих переменных (multivariate function).

Пусть — функция от переменных. Частная производная (partial derivative) по -му аргументу равна:

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

2.4.3 Градиенты

Частные производные многомерной функции по всем её переменным можно собрать в вектор — градиент (gradient) функции. Точнее, пусть функция принимает -мерный вектор и возвращает скаляр. Градиент функции по — это вектор из частных производных:

где при отсутствии неоднозначности часто записывают как .

Пусть — -мерный вектор. При дифференцировании многомерных функций часто используются следующие правила:

  • для всех справедливо ;
  • для всех справедливо ;
  • для всех справедливо ;
  • .

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

2.4.4 Цепное правило

Однако с помощью описанных выше правил вычислять градиенты может быть трудно. Дело в том, что в глубоком обучении многомерные функции обычно составные (composite), поэтому применить перечисленные выше правила к их дифференцированию трудно. К счастью, для дифференцирования составных функций можно использовать цепное правило (chain rule).

Начнём с функций одной переменной. Пусть функции и дифференцируемы. Тогда по цепному правилу:

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

Резюме

  • Дифференцирование и интегрирование — две ветви математического анализа; первое применимо к задачам оптимизации в глубоком обучении.
  • Производную можно интерпретировать как мгновенную скорость изменения функции по её переменной; она также равна угловому коэффициенту касательной к графику функции.
  • Градиент — это вектор, компонентами которого служат частные производные многомерной функции по всем её переменным.
  • Цепное правило позволяет дифференцировать составные функции.

Упражнения

  1. Постройте график функции и её касательной в точке .
  2. Найдите градиент функции .
  3. Чему равен градиент функции ?
  4. Запишите цепное правило для функции , где , , .

MXNet: Обсуждение

PyTorch: Обсуждение

TensorFlow: Обсуждение

PaddlePaddle: Обсуждение