[Интеллектуальный анализ данных] Преобразование Фурье временных рядов: интерпретация быстрой свертки с помощью numpy

1. Описание

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

2. Исторические корни БПФ

        Алгоритм преобразования Фурье считается одним из величайших открытий во всей математике. Французский математик Жан-Батист Жозеф Фурье заложил основы гармонического анализа в своей книге 1822 года «Аналитическая теория очарования».

        Гравированный портрет французского математика Жана-Батиста Жозефа Фурье (1768-1830), начало 19 века. [Источник: Википедия , изображение из общественного достояния]

        Эта фантастическая структура также предоставляет отличные инструменты для анализа временных рядов... вот почему мы здесь!

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

        

3. Определение дискретного преобразования Фурье (ДПФ)

        Начнем с основного определения. Дискретное преобразование Фурье N-элементного дискретного временного ряда x:

        Дискретное преобразование Фурье (ДПФ) Определение. Существуют и другие определения, вам просто нужно выбрать одно и придерживаться его (сделано автором)

        где k обозначает k-ю частоту спектра x. Обратите внимание, что некоторые авторы добавляют к этому определению масштабный коэффициент 1/N, но это не имеет большого значения для этой статьи — в общем, это просто вопрос определения, и придерживайтесь его.

        Тогда обратное преобразование Фурье (с учетом определения прямого преобразования Фурье):

        Обратное дискретное преобразование Фурье, основанное на приведенном выше прямом определении (сделанном автором).

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

        Умножение во временной области соответствует круговой свертке в области Фурье (сделанной автором).

        и

        Круговая свертка во временной области соответствует умножению в области Фурье (сделано автором).

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

        Два важных замечания:

  • Периодические сигналы . Структура анализа Фурье предполагает, что сигналы, с которыми мы имеем дело, являются периодическими . Другими словами, они повторяются от отрицательной бесконечности до бесконечности. Однако не всегда практично обрабатывать такие сигналы на компьютере с ограниченной памятью, поэтому мы «играем» только с одним циклом, как мы увидим позже.
  • Круговая свертка: Теорема свертки утверждает, что умножение эквивалентно круговой свертке , которая немного отличается от линейной свертки, к которой мы больше привыкли . Как мы увидим, это не так уж отличается и не так уж сложно.

4. Круговая свертка и линейная свертка

        Если вы знакомы с линейными извилинами (часто называемыми просто «извилинами»), то круговые извилины вас не смущают. По сути, круговая свертка — это просто способ свертки периодических сигналов . Как вы можете догадаться, линейная свертка имеет смысл только для сигналов конечной длины, которые не находятся в диапазоне от отрицательной бесконечности до бесконечности. В нашем случае, в контексте анализа Фурье, наш сигнал является периодическим и, следовательно, не удовлетворяет этому условию. Мы не можем говорить о (линейных) свертках.

        Тем не менее, мы все еще можем интуитивно выполнять операции в стиле линейной свертки над периодическими сигналами: просто свертка периодического сигнала по длине периода. Это то, что делает циклическая свертка: она сворачивает 2 периодических сигнала одинаковой длины в течение периода.

Чтобы еще больше убедиться в разнице, сравните две формулировки для дискретной линейной свертки и дискретной круговой свертки:

Уравнение линейной свертки: в большинстве случаев при обработке сигналов это уравнение используется путем заполнения нулями (сделано автором).

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

Обратите внимание на разницу:

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

- Циклическая индексация: в циклической свертке мы «обертываем» индекс y операцией по модулю длины N. Это всего лишь способ гарантировать, что y считается периодическим с периодом N: когда мы хотим узнать значение y в позиции k, мы используем только значение y в позиции k%N — поскольку y является периодом N, мы получаем правильное значение. Опять же, это всего лишь математический способ работы с периодической последовательностью выборок бесконечной длины.

5. Реализация в numpy

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

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

import numpy as np
import matplotlib.pyplot as plt

class PeriodicArray:
    """A class to represent a periodic signal, using a single
    period of the sequence.
    """

    def __init__(self, base):
        """base is the base sequence representing a full period."""
        self.base = base
    
    @property
    def N(self): 
        """Lenght of the base array, which is also the 
        period of our infinite-periodic sequence"""
        return len(self.base)
    
    def __getitem__(self, n):
        """We can get the value at any index, from -infinity
        to +infinity using the fact that the sequence is N-
        periodic, so we use the modulo operator.
        
        Examples
        --------
        >>> x = PeriodicArray([1, 2, 3])
        >>> x[0]
        1
        >>> x[4]
        2
        >>> x[5]
        3
        """
        return self.base[n%self.N]
    
    def plot(self, ax=None):
        """Quickly plot the sequence, with a period before and after
        the base array."""
        if ax is None:
            fig, ax = plt.subplots()
        line = ax.plot(self.base, '-o')
        ax.plot(np.arange(-self.N, 0), self.base, '--o', color=line[0].get_color(), alpha=0.5)
        ax.plot(np.arange(self.N, self.N*2), self.base, '--o', color=line[0].get_color(), alpha=0.5)
        return ax

        Давайте рассмотрим два примера: сначала выборочная синусовая последовательность, затем линейная последовательность. Оба считаются N-периодическими (в данном случае N=10).

periodic_sampled_sinus = PeriodicArray(np.sin(2*np.pi*1*np.linspace(0, np.pi/10, 10)))
periodic_sampled_sinus.plot()


periodic_slope = PeriodicArray(np.linspace(-5, 5, num=10)*0.5)
periodic_slope.plot()

2 примера PeriodicArray : «базовый» период нарисован темно-синим цветом от 0 до N, а остальные 2 периода добавлены до и после, чтобы представить тот факт, что мы имеем дело с периодической последовательностью (сделанной автором).

6. Круговая свертка, медленный режим

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

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

        Отлично, теперь мы можем видеть, как выглядит круговая свертка между двумя сигналами. Соедините все в одну цифру:

        Слева: первый периодический массив. Середина: массив второго периода. Справа: круговая свертка двух периодических массивов, которые также являются периодическими массивами (сделаны автором).

        Теперь это решение работает довольно хорошо, но у него есть один существенный недостаток: оно медленное. Как видите, нам нужно пройти через 2 вложенных цикла для вычисления результата: один для каждой позиции в массиве результатов и один для вычисления результата в этой позиции: мы говорим, что алгоритм равен O(N²), поскольку N увеличивается. , количество операций будет увеличиваться с квадратом N.

        Для небольшого массива в примере это не проблема, но по мере роста массива это станет серьезной проблемой.

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

7. Круговая свертка, метод Фурье

        Здесь в игру вступают преобразование Фурье и теорема свертки. Из-за способа реализации дискретного преобразования Фурье, реализованного очень быстрым и оптимизированным способом с использованием быстрого преобразования Фурье (БПФ), операция выполняется очень быстро ( скажем , БПФ равно O(N log N), что лучше, чем O(N²) гораздо больше).

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

        Круговая свертка между x и y с использованием прямого и обратного преобразования Фурье (сделана автором).

        Среди них DFT обозначает дискретное преобразование Фурье, а IDFT обозначает обратную операцию.

        Затем мы можем очень легко реализовать этот алгоритм для вычисления свертки x и y:

def circconv_fast(x, y):
    """Fast circular convolution using DFT.
    Return the full array of the circulard 
    convolution between x and y.
    """
    X = np.fft.fft(x)
    Y = np.fft.fft(y)
    return np.real(np.fft.ifft(np.multiply(X, Y)))

# let's compute the circular convolution for our 2 signals
circ_fast = circconv_fast(periodic_sampled_sinus.base, periodic_slope.base)
circ_fast = PeriodicArray(circ_fast)

Восемь, сравнение стоимости и времени

        Наконец, давайте проверим, что два метода дают одинаковые результаты, и сравним время, которое требуется Python для вычисления круговой свертки с использованием этих двух методов:

# compare both ways : "slow" way, and DFT-way
fig, ax = plt.subplots()
ax.plot(circ.base, '-o', label="slow-way")
ax.plot(circ_fast.base, '-o', label="DFT-way")
ax.legend()
ax.set_title('Comparison of 2 ways to compute convolution : \nslow-algebraic way VS using DFT and the convolution theorem')

        Сравнение двух методов вычисления круговой свертки между двумя периодическими последовательностями: «медленный способ» - это простая алгебра с использованием циклов и сложения, выделенная синим цветом, наложенная на «способ Фурье» оранжевого цвета. Оба метода дают совершенно одинаковые результаты (с численной точностью) (сделанные авторами).

        Это идеальное совпадение! Эти два строго эквивалентны в числовом выражении.

        Теперь для сравнения времени:

N = 1000
long_x = np.sin(2*np.pi*1*np.linspace(0, np.pi/10, N))
long_y = np.cos(2*np.pi*1*np.linspace(0, np.pi/10, N))

print(circconv(long_x, long_y))
print(circconv_fast(long_x, long_y))
# first make sure that both method yield the same result
assert np.allclose(circconv(long_x, long_y), circconv_fast(long_x, long_y))

%timeit circconv(long_x, long_y)
%timeit circconv_fast(long_x, long_y)

# for N = 10   :  90.2 µs ± 10.2 µs for the slow way VS 14.1 µs ± 161 ns  for the DFT-way
# for N = 1000 : 579   ms ± 9.14 ms for the slow way VS 69.4 µs ± 2.35 µs for the DFT-way

from physipy import units
ms = units['ms']
mus = units['mus']
print("Gain in speed for 10 samples length: ", 90*mus/(14*mus))
print("Gain in speed for 1000 samples length: ", 579*ms/(69*mus))

оказаться:

  • Для N=10 выборок ДПФ в 6 раз быстрее
  • Для N = 1000 выборок ДПФ примерно в 10000 раз быстрее.

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

Преобразование Фурье для временных рядов: объяснение быстрой свертки с помощью numpy | Йоанн Мокен | июль 2023 г. | На пути к науке о данных

Supongo que te gusta

Origin blog.csdn.net/gongdiwudu/article/details/131867755
Recomendado
Clasificación