Комплексные числа минимизации методом наименьших квадратов

Я использую свой Matlab, но я планирую в конечном итоге переключиться на выполнение всего анализа на Python, поскольку это реальный язык программирования и по ряду других причин.

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

Уравнение импеданса выглядит следующим образом:

Z(w) = 1/( 1/R + j*w*C ) + j*w*L

Затем я пытаюсь найти значения R, C и L так, чтобы была найдена кривая наименьших квадратов.

Я пытался использовать пакет оптимизации, например, optimise.curve_fit или optimise.leastsq, но они не работают с комплексными числами.

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


person Mark    schedule 25.05.2013    source источник
comment
Это кажется точно такой же проблемой с многообещающим ответом: stackoverflow.com/questions/14296790/   -  person jmihalicza    schedule 25.05.2013


Ответы (2)


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

Вот функция замены остатков:

def residuals(params, w, Z):
    R, C, L = params
    diff = model(w, R, C, L) - Z
    z1d = np.zeros(Z.size*2, dtype = np.float64)
    z1d[0:z1d.size:2] = diff.real
    z1d[1:z1d.size:2] = diff.imag
    return z1d

Это единственное изменение, которое необходимо сделать. Ответы для seed 2013: [2.96564781, 1.99929516, 4.00106534].

Ошибки относительно ответа unutbu уменьшаются значительно более чем на порядок.

person Mike Sulzer    schedule 20.11.2013

В конечном счете, цель состоит в том, чтобы уменьшить абсолютное значение суммы квадратов разностей между моделью и наблюдаемым Z:

abs(((model(w, *params) - Z)**2).sum())

В моем исходном ответе предлагалось применить leastsq к функции residuals, которая возвращает скаляр, представляющий сумма квадратов действительной и мнимой разностей:

def residuals(params, w, Z):
    R, C, L = params
    diff = model(w, R, C, L) - Z
    return diff.real**2 + diff.imag**2

Майк Сульцер предложил использовать функцию невязки, которая возвращает вектор чисел с плавающей запятой.

Вот сравнение результата с использованием этих остаточных функций:

from __future__ import print_function
import random
import numpy as np
import scipy.optimize as optimize
j = 1j

def model1(w, R, C, L):
    Z = 1.0/(1.0/R + j*w*C) + j*w*L
    return Z

def model2(w, R, C, L):
    Z = 1.0/(1.0/R + j*w*C) + j*w*L
    # make Z non-contiguous and of a different complex dtype
    Z = np.repeat(Z, 2)
    Z = Z[::2]
    Z = Z.astype(np.complex64)
    return Z

def make_data(R, C, L):
    N = 10000
    w = np.linspace(0.1, 2, N)
    Z = model(w, R, C, L) + 0.1*(np.random.random(N) + j*np.random.random(N))
    return w, Z

def residuals(params, w, Z):
    R, C, L = params
    diff = model(w, R, C, L) - Z
    return diff.real**2 + diff.imag**2

def MS_residuals(params, w, Z):
    """
    https://stackoverflow.com/a/20104454/190597 (Mike Sulzer)
    """
    R, C, L = params
    diff = model(w, R, C, L) - Z
    z1d = np.zeros(Z.size*2, dtype=np.float64)
    z1d[0:z1d.size:2] = diff.real
    z1d[1:z1d.size:2] = diff.imag
    return z1d

def alt_residuals(params, w, Z):
    R, C, L = params
    diff = model(w, R, C, L) - Z
    return diff.astype(np.complex128).view(np.float64)

def compare(*funcs):
    fmt = '{:15} | {:37} | {:17} | {:6}'
    header = fmt.format('name', 'params', 'sum(residuals**2)', 'ncalls')
    print('{}\n{}'.format(header, '-'*len(header)))
    fmt = '{:15} | {:37} | {:17.2f} | {:6}'
    for resfunc in funcs:
        # params, cov = optimize.leastsq(resfunc, p_guess, args=(w, Z))
        params, cov, infodict, mesg, ier = optimize.leastsq(
            resfunc, p_guess, args=(w, Z),
            full_output=True)
        ssr = abs(((model(w, *params) - Z)**2).sum())
        print(fmt.format(resfunc.__name__, params, ssr, infodict['nfev']))
    print(end='\n')

R, C, L = 3, 2, 4
p_guess = 1, 1, 1
seed = 2013

model = model1
np.random.seed(seed)
w, Z = make_data(R, C, L)
assert np.allclose(model1(w, R, C, L), model2(w, R, C, L))

print('Using model1')
compare(residuals, MS_residuals, alt_residuals)

model = model2
print('Using model2')
compare(residuals, MS_residuals, alt_residuals)

урожаи

Using model1
name            | params                                | sum(residuals**2) | ncalls
------------------------------------------------------------------------------------
residuals       | [ 2.86950167  1.94245378  4.04362841] |              9.41 |     89
MS_residuals    | [ 2.85311972  1.94525477  4.04363883] |              9.26 |     29
alt_residuals   | [ 2.85311972  1.94525477  4.04363883] |              9.26 |     29

Using model2
name            | params                                | sum(residuals**2) | ncalls
------------------------------------------------------------------------------------
residuals       | [ 2.86590332  1.9326829   4.0450271 ] |              7.81 |    483
MS_residuals    | [ 2.85422448  1.94853383  4.04333851] |              9.78 |    754
alt_residuals   | [ 2.85422448  1.94853383  4.04333851] |              9.78 |    754

Таким образом, оказывается, какую функцию невязки использовать, может зависеть от функции модели. Я затрудняюсь объяснить разницу в результате, учитывая сходство model1 и model2.

person unutbu    schedule 25.05.2013
comment
Комплексное число может быть основано на поплавках разной ширины, на основе документации numpy это представление выполняет переинтерпретацию данных на уровне байтов, т.е. float может в конечном итоге ссылаться на np.float32 (есть также float16 и float64, при использовании np.complex128, это будет преобразовано в 4 числа с плавающей запятой, и фактические значения будут иметь меньшее значение из-за структуры чисел с плавающей запятой. Ответ Майка Сульцера безопасен, так как значение будет преобразовано в правильный размер с плавающей запятой.У вас также может быть непрерывная память, т.е. размер элемента = 16 байт, но шаг = 20 байт, это все еще массив 1d. - person Glen Fletcher; 02.03.2016
comment
@glenflet: Спасибо за исправление. Проблема несоответствия dtype может быть устранена путем изменения diff.view('float') на diff.astype(np.complex128).view(np.float64). (Я отредактировал ответ выше, чтобы отразить это.) Проблема несмежности не возникает, потому что diff - это разница двух массивов: diff = model(w, R, C, L) - Z. NumPy всегда будет создавать непрерывный массив, содержащий результат model(w, R, C, L) - Z, а Python привязывает к нему имя переменной diff. - person unutbu; 02.03.2016
comment
Я бы сказал, что разница между двумя моделями заключается в размере "остатков" с плавающей запятой, использующих float32 для модели2, поскольку Z - это np.complex64, т.е. 2 32-битных числа с плавающей запятой, две другие функции приводятся к float64, вы должны сравнить число вызовов функций, из документа я считаю, что размер шага по умолчанию равен 2 * sqrt (машинная точность), что больше, чем float32, если вы превышаете maxfev (800 в вашем коде), это важно. Также это может быть пропуск локального mim или ошибка в сумме квадратов, хотя это не должно иметь такого влияния. - person Glen Fletcher; 12.03.2016
comment
@glenflet: я добавил в вывод количество вызовов функций. Одного я не понимаю, так это почему residuals с model2 находит параметры, которые лучше подходят для model1, чем любая из остаточных функций в паре с самой model1. Как вы указали, model2 возвращает значения с меньшим разрешением, чем model1, поэтому (для меня) не имеет смысла, чтобы результат был лучше. Может, это глупое везение? Тем не менее, я хотел бы знать, как добиться того же или лучшего результата от leastsq при использовании model1. - person unutbu; 12.03.2016
comment
@glenflet интересный факт о размере шага. что бы вы сказали, это хороший размер шага, если вы используете float32? - person john; 09.03.2017
comment
Я запускаю подробный анализ вашего кода (я использовал наименьшие_квадраты, то же самое, но с большей функциональностью), разница в том, что регрессия заканчивается ftol для модели 1 и xtol для модели 2. Другим фактором является то, что остатки несопоставимы с двумя другими. , если вы внимательно посмотрите на остатки функции, это квадрат или величина комплексного числа, где, поскольку другие 2 являются суммами по компонентам, фактически остатки возводятся в квадрат дважды, следовательно, это дает большее значение ssr и поощряет более агрессивную регрессию (т.е. более крупные шаги ) если вы исправите эту разницу, результаты будут более сопоставимыми. - person Glen Fletcher; 11.03.2017
comment
Кроме того, хотя вы заставили модель 2 использовать 64-битный комплекс, Z все еще был 128-битным комплексом, поэтому, когда вы вычитаете 64-битное комплексное значение, расширяется до 128-битного комплекса путем заполнения нулями, результаты для модели 2 существенно зависят от того, является ли Z является сложным128 или сложным64 в момент запуска. наконец, вы должны запускать сострадание несколько раз с разными начальными значениями, т. е. зацикливать каждый раз после установки начального значения, результаты не всегда сходятся правильно, и лучшая модель меняется, это просто разные тесты ошибок, т. е. величина по сравнению с компонентами ошибка. - person Glen Fletcher; 11.03.2017