Оптимальная аппроксимация сплайнами

Пусть нам дан набор точек f0d4038c02204f1cbb7cba71c5dba9fc.png и соответствующий им набор положительных весов 3fcd83f893424620a0d1a78d9b8e1c4d.png. Мы считаем, что некоторые точки могут быть важнее других (если нет, то все веса одинаковые). Неформально говоря, мы хотим, чтобы на соответствующем интервале была проведена красивая кривая таким образом, чтобы она «лучше всего» проходила через эти данные.

76793c7dedd64b16ae3e957bc852163c.png

Под катом находится алгоритм, раскрывающий, каким образом сплайны позволяют строить подобную красивую регрессию:
e3ce94ff72bd4305837c9d530b8cb4a4.png


Основные определения


Функция s (x) на интервале [a, b] называется сплайном степени k на сетке с горизонтальными узлами eaba4f26aea143d6aed5aaf7944a7baf.png, если выполняются следующие свойства:
  • На интервалах bfa7c57cc8a24009b66d6241bd78e5ac.png функция s (x) является полиномом k-й степени.
  • n-ая производная функции s (x) непрерывна в любой точке [a, b] для любого n = 1, …, k-1.

Заметим, что для построения сплайна нужно для начала задать сетку из горизонтальных узлов. Расположим их таким образом, чтобы внутри интервала (a, b) стояло g узлов, а по краям — k+1: c59cf0cdaadc4f55965f1cc2cc0ec020.png и 9e85eb1ea5264a2e90f9f3ba3a842145.png.

Каждый сплайн в точке 1fca368c69b04ca38cfd6920d12c7218.png может быть представлен в базисной форме:

bf03a902279e4f5fa11a848b6066487b.png

где 199957081ea943ff90133bc9e3221c35.png — B-сплайн k+1-го порядка:
872584874fd641e0b646b529d084dc0c.png

d5ab7f8826684361aaabd0c691814276.png

e21e928c3cef4d8b8c2bfb9d74b7184e.png

Вот как, например, выглядит базис на сетке из g = 9 узлов, равномерно распределенных на интервале [0, 1]:
9cbfc26b33a4471cad716a718a464d39.png
Сходу разобраться в построении сплайнов через B-сплайны очень сложно. Больше информации можно найти здесь: www.brnt.eu/phd/node11.

Аппроксимация с заданными горизонтальными узлами


Итак, мы выяснили что сплайн определяется однозначно узлами и коэффициентами. Допустим, что узлы eaba4f26aea143d6aed5aaf7944a7baf.png нам известны. Также на вход подается набор данных f0d4038c02204f1cbb7cba71c5dba9fc.png с соответствующими весами 3fcd83f893424620a0d1a78d9b8e1c4d.png. Необходимо найти коэффициенты 61a9d29dffdd46d3a6a57bc978d9f1ce.png, максимально приближающие кривую сплайна к данным. Строго говоря, они должны доставлять минимум функции
a7e2f5ce2b5447988640e14f84690a5f.png

Для удобства запишем в матричном виде:
214cd7e5a62d44fc852e89e655e4f057.png

где
2388d28b33424d4bb01f40c62329dca8.png

f5bf4423cd7a44c787b2e177f5955ca3.png

Заметим, что матрица E блочно-диагональная. Минимум достигается когда градиент ошибки по коэффициентам будет равен нулю:
def3f960f95c404890b97d5bb097ee3c.png

Зададим оператор c91ee4982a2046beb5aea4b4252b8fb6.png, обозначающий взвешенное скалярное произведение:
72ee09bcb9434b8d995f457d8c960526.png

79b39f9e774f4b1e9c5388b66c3d31ba.png

Пусть также
e475fc12fb73485ea25db1167dae04a3.png

c0e44b2279344ecabbaaa3779a651f8f.png

Тогда вся задача и все предыдущие формулы сводятся к решению простой системы линейных уравнений:

6c9aa45ddc144cdc95131c613bf9b46d.png

где матрица А (2k+1)-диагональная, так как b25e32f26ff7419aba245d38d2f7d2a7.png, если |i — j| > k. Также матрица А симметричная и положительно-определенная, следовательно решение возможно быстро найти с помощью разложения Холецкого (существует также алгоритм для разреженных матриц).
И вот, решая систему, получаем желаемый результат:
e3ce94ff72bd4305837c9d530b8cb4a4.png

Сглаживание


Однако, далеко не всегда все так хорошо. При малом количестве данных по отношению к количеству узлов и степени сплайна может возникнуть проблема т.н. сверхподгонки (overfitting). Вот пример «плохого» кубического сплайна, при этом идеально проходящего сквозь данные:
940b7aebae8d4602bdb1d3bc670f1870.png
Окей, кривая уже не такая уж и красивая. Попытаемся уменьшить так называемые колебания сплайна. Для этого мы попробуем «сгладить» его k-ю производную. Другими словами, мы минимизируем разницу между производной слева и производной справа от каждого узла:
eb4dcca4b0764a86b60d1610758eaf67.png

Разложив сплайн в базисную форму, мы получаем:
2d589ea39a1048cf8bb9b908f3a1eab4.png

edd82615ac1343978f6149c7e72848bd.png

Давайте рассмотрим ошибку
943f3a8bc46346dc9d08e7f37202b0c5.png

Здесь q — вес функции, влияющей на сглаживание, и
c63b60da69224b00bec24aeb66d6b2c9.png

ea27c487b2f943c1b647054e9e8acce6.png

Новая система уравнений:
b4868635a1af4aedb962633290bf6331.png

где
299b532601c04f3b837210d86e699717.png

Ранг матрицы B равен g. Она симметричная и, так как q > 0, A + qB будет положительно определенной. Поэтому разложение Холецкого по-прежнему применимо к новой системе уравнений. Однако, матрица B вырожденная и при слишком больших значениях q могут возникнуть численные ошибки.
При совсем маленьком значении q = 1e-9 вид кривой изменяется очень слабо.
75b70f6fc8094d9aa91d46d3677dea74.png
Но при q = 1e-7 в данном примере уже достигается достаточное сглаживание.
cbdaf8918b274f03b854757993dbbe7a.png

Аппроксимация с неизвестными горизонтальными узлами


Представим теперь, что задача такая же, как и прежде, за исключением того, что мы не знаем как узлы расположены на сетке. На вход кроме данных подается только количество узлов g, интервал [a, b] и степень сплайна k. Попробуем наивно предположить, что лучше всего расположить узлы равномерно на интервале:
47681f56f4ad48d08f1a168e996bd6d9.png

Упс. Видимо, необходимо все-таки расположить узлы как-то иначе. Формально, расположим узлы таким образом, чтобы значение ошибки

c0a7c16c44bc4f5b99d7530c81882de7.png

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

Положительный параметр p — вес штрафной функции. Чем больше его значение, тем быстрее узлы будут удаляться друг от друга и стремиться к равномерному расположению.
Для решения данной задачи мы используем метод сопряженных градиентов. Его прелесть заключается в том, что для квадратичной функции он сходится за фиксированное (в данном случае g) количество шагов.
  1. Инициализируем направление 625ccbcbcae7400aab2ff5a57fdb8823.png.
    Как рассчитать производную ошибки по узлам?
    Производная суммы квадратов по узлу:
    c310a85a12a148069091c91f66558802.png

    Для того, чтобы рассчитать влияние положения узла на значения сплайна, нужно рассмотреть B-сплайны ae91fdf1c0754871adac188519043b4b.png на новых узлах 46d7aa0a3c75422fb2dac4b4644babb3.png и с новыми коэффициентами 356cd73833794ef09164961bbaa01287.png
    5310f244765b42d0af68a5740cb31a15.png

    7d64d92abd2f40809cfad525fee47362.png

    f716414c18af4eb1ac42d929c33df8ac.png

    Производная штрафной функции:

    bcc1756ea6394b7b88de8b9cf152e046.png

    На производную функции сглаживания без слез не взглянешь:

    976a923bd2544dce81631d08981af741.png
  2. Для j = 0, …, g-1
    • Зададим функцию
      fb9f08d2777b4c258edfb8027db7a86b.png

      возвращающую ошибку в зависимости от выбора шага вдоль заданного направления. На этом шаге мы находим оптимальное значение α*, доставляющее минимум этой функции. Для этого мы решаем задачу одномерной оптимизации. О том, каким образом, будет сказано позже.
    • Обновляем значения узлов:
      e0562e893acf47ed9a5b409a58086b6f.png
    • Обновляем вектор направления:
      de94423761724153b771a34e58b023b8.png
  3. Если
    b88fb763023e4e9eaf911931614aad0f.png

    и
    ba281a20bb824aa89fdf40d0ba6c7daa.png

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

Решение задачи одномерной минимизации


Для того, чтобы найти значение 950af084b8b1434fbb7013548c67eef0.png, доставляющее минимум функции
fb9f08d2777b4c258edfb8027db7a86b.png

мы используем алгоритм, позволяющий сократить количество обращений к оракулу, а именно количество операций аппроксимации с заданными узлами и подсчета функции ошибки. Мы будем использовать нотацию 14f35558faa54eb8a49465e73a1ad990.png.
  1. Пусть первая и последняя компоненты вектора направления равны нулю: 37ef4d765da04e20a441dfa7c04f03f4.png. Зададим также максимально возможный шаг вдоль этого направления:
    d1c4c6c852354885a91c36cda21aefc0.png

    Такой выбор обусловлен тем, что узлы не должны пересекаться.
  2. Инициируем k = 0 и начальные шаги: a7e0b96540fa4dbea6aaa25578c2a1d9.png, cf56c833a31c44aca9307f62006bace0.png, c80770b32fb445899dd7290a18c629ba.png.
  3. До тех пор, пока a847ea5514924f57a989a3e0bf63fff6.png:
    • Задаём
      aaacf1bf69d44571ae45045355f99eeb.png

      и уменьшаем шаг
      903325864dba460b9ac5c8b8f705c63f.png
    • k = k + 1

  4. Если k > 0, то возвращаем α* = α1. Иначе:
    • До тех пор, пока ed971093ab75420fad0c2ca5431b9c47.png: α0 = α1, α1 = α2 и
      0ec1c122f5634e0db3a2dc70a8ef5f4c.png

    • Возвращаем 78719930528c49bf9570b7bb2beea3e3.png, где ec2ac75fe19b4d898a6a55ee6a3ac878.png — корень уравнения I'(α) = 0 и I (α) — аппроксимация функции ошибки:
      33ebcebac78d48b69eeb628dd2a5a66c.png

      где
      1b649b9b4ff04bca900247509c975c41.png

      e9e210da039e4f3599883a2569c6b396.png


Коэффициенты ai и bi могут быть найдены из уравнений
1dfb31ede82f4be48811dce41aadb8c2.png

и 
6ffc4041bf6a4ae09a3073f0b2ce6d55.png

Объяснение алгоритма:
Идея заключается в том, чтобы расставить три точки α0 < α1 < α2 таким образом, чтобы по значениям ошибок, достигаемых в этих точках, можно было построить простую аппроксимирующую функцию и вернуть её минимум. Притом значение ошибки в α1 должно быть меньше, чем значение ошибки в α0 и α2.

Находим начальное приближение α1 из условия S'(α1)=0, где S (α) — функция вида

609fc20256e04407a04fa5f08d07b554.png

Константы c0 и c1 находятся из условий f21827296c954412a8f48306f71b623d.png и 8cfa0295c09e40159a26a274bdc3e548.png.

Если мы просчитались с начальным приближением, то мы уменьшаем шаг α1 до тех пор, пока он доставляет большее значение ошибки, чем α0. Выбор fb03e409006041698caf2b4973a3befb.png исходит из условия f1da000b0aec4f8cb59975abedad7992.png, где Q (α) — парабола интерполирующая функцию ошибки 5d6a6d98b48d41e98ff5ea7bd8d90084.png: c35cb31c9c9c4e2ba38f0bb7b1f5d3b7.png, e2da52986f4e4dd6b4ffc18798d92b83.png и 5ffed2e5fd8847719863502d52f173b0.png.
Если k > 0, то мы нашли значение α1, такое что при его выборе значение ошибки будет меньше, чем при выборе α0 и α2, и мы возвращаем его в качестве грубого приближения α*.

Если же наше первоначальное приближение было верным, то мы пытаемся найти шаг α2, такой что 8201b293ce7242e281ca84ff030df83b.png. Он будет найден между α1 и αmax, так как αmax — точка сингулярности для штрафной функции.

Когда найдены все три значения α0, α1 и α2, мы представляем функцию ошибки в виде суммы двух функций, приближающих разность квадратов и функцию штрафа. Функция Q (α) — парабола, чьи коэффициенты могут быть найдены, так как мы знаем её значения в трех точках. Функция R (α) уходит на бесконечность при α, стремящемся к αmax. Коэффициенты bi также могут быть найдены из системы из трех уравнений. В результате, мы приходим к уравнению, которое может быть приведено к квадратному и легко решено:

f94e9891d64542b498220427981fa3fa.png

И вот, для сравнения, результат оптимально построенного сплайна:
d802d4ec3af342ffb4aebb2473e29b48.png

Ну и для тех, кому может пригодиться: реализация на Python.

Комментарии (1)

  • 7 декабря 2016 в 21:54

    0

    У вас есть возможность собрать пакет для PyPI?

© Habrahabr.ru