为什么我的曲线拟合拟合对初始猜测参数如此敏感

why is my fitting with curve fit so sensitive to initial guess parameters

我知道,有很多例子,人们试图用曲线拟合来拟合 cos 但失败了。

可是看了很多,还是没头绪,我的脚本是怎么回事

数据比较清楚。我的代码有时确实有效,但不可靠。

这是我的最小示例:

import matplotlib.pyplot as plt
from numpy import cos, pi, inf
from numpy import diag as ndiag
from numpy import sqrt as nsqrt
from scipy.optimize import curve_fit
from scipy.signal import find_peaks


def test(power, transmission):
    # function for p0
    def find_initial_guess(power, transmission):
        # find extrema for estimating initial guess
        # skip minor minima, which are just noise bumps
        minima, _ = find_peaks([-t for t in transmission], width=len(transmission) / 10)
        maxima, _ = find_peaks(transmission, width=len(transmission) / 10)

        # find neighbouring maxima and minima
        # select arbitrary maxima from the middle of the list ( so it is not just an edge case
        maximum = maxima[int(len(maxima) / 2)]
        minimum = min([maximum - m for m in minima])
        # distance of minimum and maximum can be used as initial guess for par2
        return [sum(transmission) / (len(transmission)),  # offset is average of transmission
                (max(transmission) - min(transmission)) / 2,  # amplitude is half the max-min-difference
                pi,  # phase offset
                pi / (abs(power[maximum] - power[minimum])),  # periodicity is 1 / distance(max-min)
                ]  #

    # fit function
    def thermo_optic_coupling(p_heater, offset, amplitude, par1, par2):
        return offset + amplitude * cos((par1 + par2 * p_heater))

    initial_guess_popt = find_initial_guess(power, transmission)

    popt, pcov = curve_fit(thermo_optic_coupling,
                           power,
                           transmission,
                           p0=initial_guess_popt,
                           bounds=[(-inf, 0, -6 * pi, -inf),
                                   (inf, inf, 6 * pi, inf)],
                           maxfev=5000
                           )
    popt = list(popt.tolist())
    print('fit          :', popt)
    print('cov          :', pcov)
    print('summed root mean square error', sum(nsqrt(ndiag(pcov))))

    fit = [thermo_optic_coupling(p_i, *popt) for p_i in power]
    initial_guess = [thermo_optic_coupling(p_i, *initial_guess_popt) for p_i in power]

    plt.plot(power, fit, color='orange', label='fit by scipy')
    plt.plot(power, initial_guess, color='red', label='initial guess')
    plt.plot(power, transmission, 'b+', label='experiment')
    plt.xlabel('heater power ∝ V²')
    plt.legend()
    plt.ylabel('transmission in mW')
    plt.show()


power1, transmission1 = [
    [0.0, 1.5625, 3.1250000000000004, 4.687500000000001, 6.25, 7.812500000000001, 9.375, 10.9375,
     12.500000000000002, 14.0625, 15.625, 17.187500000000004, 18.750000000000004, 20.3125,
     21.875000000000004, 23.4375, 25.0, 26.562500000000004, 28.125000000000004,
     29.687500000000004, 31.250000000000004, 32.81249999999999, 34.37500000000001, 35.9375, 37.5,
     39.0625, 40.625, 42.1875, 43.75, 45.3125, 46.87500000000001, 48.43749999999999,
     50.00000000000001, 51.5625, 53.125, 54.6875, 56.25, 57.8125, 59.375, 60.93749999999999,
     62.5, 64.0625, 65.62499999999999, 67.1875, 68.75000000000001, 70.31249999999999, 71.875,
     73.4375, 75.00000000000001, 76.5625, 78.125, 79.68749999999999, 81.25, 82.81250000000001,
     84.37500000000001, 85.9375, 87.50000000000001, 89.06249999999999, 90.62500000000001,
     92.1875, 93.75, 95.31249999999999, 96.875, 98.4375],
    [1.6562295294369114e-06, 1.814877400886339e-06, 1.9415786380553917e-06,
     2.032434337241163e-06, 2.07977581456966e-06, 2.0990197432469184e-06, 2.0990197432469184e-06,
     2.0512402206219947e-06, 2.0045482927134235e-06, 1.9328632951064806e-06,
     1.839538286344573e-06, 1.742643390948694e-06, 1.640984668617779e-06, 1.5322308981965023e-06,
     1.3777010363167424e-06, 1.2493325696095153e-06, 1.1189650542693485e-06,
     9.868258640760248e-07, 8.674581337360867e-07, 7.562624260369136e-07, 6.558233781566861e-07,
     5.746673504788186e-07, 4.927385300164335e-07, 4.2957559516582234e-07, 3.70524153320037e-07,
     3.5060372305501673e-07, 3.70524153320037e-07, 3.950456580084038e-07, 4.4306013694031485e-07,
     5.232635985089236e-07, 6.256551763793945e-07, 7.528015828064533e-07, 8.754201193092522e-07,
     1.033904733256672e-06, 1.1609580401117936e-06, 1.294566057151215e-06,
     1.4211237849175981e-06, 1.539030734443489e-06, 1.648589477558925e-06,
     1.7586133663896758e-06, 1.8561647616293735e-06, 1.9415786380553917e-06,
     2.0045482927134235e-06, 2.0702201126993254e-06, 2.0990197432469184e-06,
     2.07977581456966e-06, 2.0138008672355043e-06, 1.9415786380553917e-06, 1.814877400886339e-06,
     1.6871454442159815e-06, 1.539030734443489e-06, 1.3655054776415766e-06,
     1.1989926575358521e-06, 1.0195499872527435e-06, 8.363277813856736e-07,
     6.614627809764817e-07, 4.950220262917964e-07, 3.554811066871505e-07, 2.3324724541289197e-07,
     1.422237348203287e-07, 7.730111429536002e-08, 3.5477242527119533e-08,
     2.6814427162334455e-08, 4.768628023291028e-08]]
# the data is small, so multiply transmission by 1e6
test(power1, [t_i * 1e6 for t_i in transmission1])

power2, transmission2 = [
    [0.0, 1.5625, 3.1250000000000004, 4.687500000000001, 6.25, 7.812500000000001, 9.375, 10.9375, 12.500000000000002,
     14.0625, 15.625, 17.187500000000004, 18.750000000000004, 20.3125, 21.875000000000004, 23.4375, 25.0,
     26.562500000000004, 28.125000000000004, 29.687500000000004, 31.250000000000004, 32.81249999999999,
     34.37500000000001, 35.9375, 37.5, 39.0625, 40.625, 42.1875, 43.75, 45.3125, 46.87500000000001, 48.43749999999999,
     50.00000000000001, 51.5625, 53.125, 54.6875, 56.25, 57.8125, 59.375, 60.93749999999999, 62.5, 64.0625,
     65.62499999999999, 67.1875, 68.75000000000001, 70.31249999999999, 71.875, 73.4375, 75.00000000000001, 76.5625,
     78.125, 79.68749999999999, 81.25, 82.81250000000001, 84.37500000000001, 85.9375, 87.50000000000001,
     89.06249999999999, 90.62500000000001, 92.1875, 93.75, 95.31249999999999, 96.875, 98.4375],
    [1.396174174553104e-06, 1.2437906644035389e-06, 1.0933734492030595e-06, 9.231950062379674e-07,
     7.493565772474859e-07, 5.942121443423389e-07, 4.470013463798467e-07, 3.209402716365006e-07, 2.0673619727046636e-07,
     1.2646679820627658e-07, 6.225003507478826e-08, 2.7063535041190432e-08, 1.5904518423724243e-08,
     3.301448473686211e-08, 6.953709180130387e-08, 1.3921171470244173e-07, 2.3002252671138602e-07,
     3.5060372305501673e-07, 4.927385300164335e-07, 6.586370438602071e-07, 8.556507267350914e-07, 1.038734421672639e-06,
     1.2045269561883742e-06, 1.3295627259399386e-06, 1.4593866518595935e-06, 1.5741007416815629e-06,
     1.6793627797419938e-06, 1.7507193165159197e-06, 1.823060741632163e-06, 1.8983913848020828e-06,
     1.9155497976900293e-06, 1.8983913848020828e-06, 1.847832823818452e-06, 1.7665430107889611e-06,
     1.633414940040678e-06, 1.4920581674760974e-06, 1.3295627259399386e-06, 1.1717002525967601e-06,
     1.024312620215478e-06, 8.635043377359954e-07, 7.092257962431407e-07, 5.480251928284404e-07, 3.897772826233099e-07,
     2.6497879338257794e-07, 1.584588586826325e-07, 9.344973238413863e-08, 5.650105063022198e-08, 5.224160649199307e-08,
     7.561515230224364e-08, 1.2531196020926962e-07, 2.001284497590038e-07, 3.0707095931959394e-07,
     4.352970804255852e-07, 5.857557001760154e-07, 7.632319170881868e-07, 9.480514529881339e-07, 1.129318710440296e-06,
     1.3061280439519783e-06, 1.4593866518595935e-06, 1.6034836040919702e-06, 1.7186384491686294e-06,
     1.814877400886339e-06, 1.847832823818452e-06, 1.872941513580031e-06]]
# the data is small, so multiply transmission by 1e6
test(power2, [t_i * 1e6 for t_i in transmission2])

power3, transmission3 = [
    [0.0, 1.5625, 3.1250000000000004, 4.687500000000001, 6.25, 7.812500000000001, 9.375, 10.9375, 12.500000000000002,
     14.0625, 15.625, 17.187500000000004, 18.750000000000004, 20.3125, 21.875000000000004, 23.4375, 25.0,
     26.562500000000004, 28.125000000000004, 29.687500000000004, 31.250000000000004, 32.81249999999999,
     34.37500000000001, 35.9375, 37.5, 39.0625, 40.625, 42.1875, 43.75, 45.3125, 46.87500000000001, 48.43749999999999,
     50.00000000000001, 51.5625, 53.125, 54.6875, 56.25, 57.8125, 59.375, 60.93749999999999, 62.5, 64.0625,
     65.62499999999999, 67.1875, 68.75000000000001, 70.31249999999999, 71.875, 73.4375, 75.00000000000001, 76.5625,
     78.125, 79.68749999999999, 81.25, 82.81250000000001, 84.37500000000001, 85.9375, 87.50000000000001,
     89.06249999999999, 90.62500000000001, 92.1875, 93.75, 95.31249999999999, 96.875, 98.4375],
    [1.663904987577944e-06, 1.8312809813253925e-06, 1.959127394435598e-06, 2.0512402206219947e-06,
     2.089375623555793e-06, 2.0990197432469184e-06, 2.128220016564131e-06, 2.1087083781719565e-06,
     2.0512402206219947e-06, 2.0045482927134235e-06, 1.9155497976900293e-06, 1.7986207544535706e-06,
     1.6871454442159815e-06, 1.552721071155355e-06, 1.414844892100276e-06, 1.294566057151215e-06,
     1.1503143128228655e-06, 1.038734421672639e-06, 9.069871600668335e-07, 7.989811320185081e-07, 7.027494648298712e-07,
     6.203210568077912e-07, 5.330310688570652e-07, 4.7348784131211617e-07, 4.4502637870081293e-07,
     4.2580311206559627e-07, 4.2957559516582234e-07, 4.6311990409702625e-07, 4.905614836185237e-07,
     5.66489055218778e-07, 6.558233781566861e-07, 7.562624260369136e-07, 8.754201193092522e-07, 1.0148094985771049e-06,
     1.1556239224450914e-06, 1.2717482214298512e-06, 1.3777010363167424e-06, 1.4986797225052762e-06,
     1.6034836040919702e-06, 1.7266031287264504e-06, 1.814877400886339e-06, 1.9241870735205803e-06,
     1.9953382299096967e-06, 2.0702201126993254e-06, 2.0990197432469184e-06, 2.089375623555793e-06,
     2.041815627896478e-06, 1.959127394435598e-06, 1.8561647616293735e-06, 1.7266031287264504e-06,
     1.5813955906972397e-06, 1.4023702092175955e-06, 1.2493325696095153e-06, 1.0731798840385246e-06,
     8.832073557363544e-07, 7.124863091749079e-07, 5.355012926902759e-07, 3.791547695631538e-07, 2.5433475611234004e-07,
     1.5230712743292288e-07, 8.20050359899154e-08, 3.9698159260983884e-08, 2.744154275033995e-08,
     4.559654207832307e-08]]
# the data is small, so multiply transmission by 1e6
test(power3, [t_i * 1e6 for t_i in transmission3])

有谁知道我如何为拟合提供更多参数,使其对初始猜测参数不那么敏感?

为什么 scipy 不能可靠地调整相位偏移?

提前致谢, 菲利克斯

首先,规范化数据是个好主意。那么,你的界限就很大了。以您的示例为例,我固定了靠近您的 'guess' 的边界并增加了 maxfex:

bounds=[(-10*initial_guess_popt[0], -10*initial_guess_popt[1], 0.75*initial_guess_popt[2], -10*initial_guess_popt[3]),
        (10*initial_guess_popt[0], 10*initial_guess_popt[1], 1.25*initial_guess_popt[2], 10*initial_guess_popt[3])],
maxfev=50000
)