Python - минимизация хи-квадрат

Я пытался подогнать линейную модель к набору данных напряжения / деформации, минимизируя хи-квадрат. К сожалению, использование приведенного ниже кода неправильно минимизирует функцию chisqfunc. Он находит минимум в начальных условиях x0, что неверно. Я просмотрел scipy.optimize документацию и протестировал минимизацию других функций, которые работали правильно. Не могли бы вы предложить, как исправить приведенный ниже код, или предложить другой метод, который я могу использовать для подгонки линейной модели к данным путем минимизации хи-квадрат?

import numpy
import scipy.optimize as opt

filename = 'data.csv'

data = numpy.loadtxt(open(filename,"r"),delimiter=",")

stress = data[:,0]
strain = data[:,1]
err_stress = data[:,2]

def chisqfunc((a, b)):
    model = a + b*strain
    chisq = numpy.sum(((stress - model)/err_stress)**2)
    return chisq

x0 = numpy.array([0,0])

result =  opt.minimize(chisqfunc, x0)
print result

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

Привет, Уилл

РЕДАКТИРОВАТЬ: набор данных, который я сейчас использую: Ссылка на данные


person Will282    schedule 04.03.2014    source источник
comment
Код мне кажется прекрасным и дает хорошие результаты для некоторых фиктивных данных, которые я создал на своей машине. Одно замечание: верна ли ваша переменная err_stress неопределенность измеренного напряжения для различных значений деформации?   -  person gg349    schedule 04.03.2014
comment
один тривиальный момент: вы проверяете результат с помощью print result.x, верно? минимизировать НЕ обновляет x0.   -  person gg349    schedule 04.03.2014
comment
Спасибо за быстрый ответ, я использовал print result.x. Да err_stress - это массив ошибок для массива измеренных напряжений.   -  person Will282    schedule 04.03.2014
comment
Что такое result.success и result.status после звонка minimize?   -  person Warren Weckesser    schedule 05.03.2014
comment
result.success равно True, result.status равно 0 и result.x равно [0 0]. Спасибо за помощь, Уоррен.   -  person Will282    schedule 05.03.2014
comment
@flebool, я загрузил набор данных, который сейчас использую: Ссылка на данные   -  person Will282    schedule 05.03.2014


Ответы (1)


Проблема в том, что ваше первоначальное предположение очень далеко от реального решения. Если вы добавите оператор печати внутри chisqfunc(), например print (a,b), и повторно запустите свой код, вы получите что-то вроде:

(0, 0)
(1.4901161193847656e-08, 0.0)
(0.0, 1.4901161193847656e-08)

Это означает, что minimize оценивает функцию только в этих точках.

если вы теперь попытаетесь оценить chisqfunc() на этих 3 парах значений, вы увидите, что они ТОЧНО совпадают, например

print chisqfunc((0,0))==chisqfunc((1.4901161193847656e-08,0))
True

Это происходит из-за арифметики округления с плавающей запятой. Другими словами, при вычислении stress - model переменная stress слишком много на порядок больше, чем model, и результат усекается.

Затем можно было бы просто попробовать перебор, увеличив точность с плавающей запятой, записав data=data.astype(np.float128) сразу после загрузки данных с помощью loadtxt. minimize не удается, с result.success=False, но с полезным сообщением

Желаемая ошибка не обязательно достигается из-за потери точности.

Одна из возможностей состоит в том, чтобы обеспечить лучшее начальное предположение, чтобы при вычитании stress - model часть model имела тот же порядок величины, а другая - изменить масштаб данных, чтобы решение было ближе к вашему первоначальному предположению (0,0).

Будет НАМНОГО лучше, если вы просто измените масштаб данных, сделав, например, безразмерными по отношению к определенному значению напряжения (например, изгиб / растрескивание этого материала).

Это пример фитинга, в котором в качестве шкалы напряжений используется максимальное измеренное напряжение. В вашем коде очень мало изменений:

import numpy
import scipy.optimize as opt

filename = 'data.csv'

data = numpy.loadtxt(open(filename,"r"),delimiter=",")

stress = data[:,0]
strain = data[:,1]
err_stress = data[:,2]


smax = stress.max()
stress = stress/smax
#I am assuming the errors err_stress are in the same units of stress.
err_stress = err_stress/smax

def chisqfunc((a, b)):
    model = a + b*strain
    chisq = numpy.sum(((stress - model)/err_stress)**2)
    return chisq

x0 = numpy.array([0,0])

result =  opt.minimize(chisqfunc, x0)
print result
assert result.success==True
a,b=result.x*smax
plot(strain,stress*smax)
plot(strain,a+b*strain)

Ваша линейная модель довольно хороша, т.е. ваш материал имеет очень линейное поведение для этого диапазона деформации (какой это вообще материал?): введите описание изображения здесь

person gg349    schedule 05.03.2014
comment
Ах, я попытался использовать результаты stats.linregress в качестве начальных условий, которые, к сожалению, также не сработали. Я думаю, причина того, что это не сработало, заключается в том, что значения напряжения настолько велики, что незначительное изменение chisqfunc происходит при небольшом изменении x0. Изменение масштаба с использованием smax принесло пользу. Большое спасибо за вашу помощь в этом. Эти данные представляют собой образец набора данных (только линейный участок) для удлинения провода Cu98% / Be2%. - person Will282; 06.03.2014