При интересных уровнях шума, возможно, нельзя избежать грубой силы.
Вот квадратные ошибки (с использованием расстояния обтекания) как функция наклона (лучший перехват выбран в каждой точке) для трех моделейс уровнями шума 90, 180, 180 и 64, 96, 128 точек данных (см. скрипт ниже).
Я не уверен, что тамэто умный способ надежного поиска глобальных минимумов из них.
OTOH, грубая сила работает достаточно хорошо, даже в случаях, которые выглядят довольно сложно, как нижний.Пунктирная линия - истинная модель без шума, точки - фактические данные, полученные путем добавления шума к истинной модели, сплошная линия - реконструкция.
Код:
import numpy as np
import scipy.optimize as so
from operator import attrgetter
from matplotlib import pylab
def setup(interc, slope, sigma, N):
x = np.random.uniform(0.1, 2.0, (N,)).cumsum()
y = (interc + x*slope + np.random.normal(0, sigma, (N,)) + 360) % 720 - 360
return x, y
def err_model_full(params, x, y):
interc, slope = params
err = (interc + x*slope - y + 360) % 720 - 360
return np.dot(err, err)
def err_model(interc, slope, x, y):
err = (interc + x*slope - y + 360) % 720 - 360
return np.dot(err, err)
for i, (interc, slope, sigma, N) in enumerate([(100, -12, 90, 64),
(-30, 20, 180, 96),
(66, -49, 180, 128)]):
# create problem
x, y = setup(interc, slope, sigma, N)
# brute force through slopes
slps = np.linspace(-128, 128, 257)
ics, err = zip(*map(attrgetter('x', 'fun'), (so.minimize(err_model, (0,), args = (sl, x, y)) for sl in slps)))
best = np.argmin(err)
# polish
res = so.minimize(err_model_full, (ics[best], slps[best]), args = (x, y))
# plot
pylab.figure(1)
pylab.subplot(3, 1, i+1)
pylab.plot(slps, err)
pylab.figure(2)
pylab.subplot(3, 1, i+1)
pylab.plot(x, y, 'o')
ic_rec, sl_rec = res.x
pylab.plot(x, (ic_rec + x*sl_rec + 360) % 720 - 360)
pylab.plot(x, (interc + x*slope + 360) % 720 - 360, '--')
print('true (intercept, slope)', (interc, slope), 'reconstructed',
tuple(res.x))
print('noise level', sigma)
print('squared error for true params', err_model_full((interc, slope), x, y))
print('squared error for reconstructed params', err_model_full(res.x, x, y))
pylab.figure(1)
pylab.savefig('bf.png')
pylab.figure(2)
pylab.savefig('recon.png')