Каков наиболее точный метод в python для вычисления решения минимальной нормы или решения, полученного из псевдообратного?
Моя цель - решить:
Kc=y
с псевдообратным (то есть <сильным > решением минимальной нормы):
c=K^{+}y
так что модель (надеюсь) - полиномиальная модель высокого уровня f(x) = sum_i c_i x^i
. Меня особенно интересует недоопределенный случай, когда мы имеем больше полиномиальных функций, чем данных (несколько уравнений слишком много переменных/неизвестных) columns = deg+1 > N = rows
. Примечание K
является матрицей вандермодов полиномиальных признаков.
Я изначально использовал функцию python np.linalg.pinv, но потом я заметил, что что-то фанки происходит, как я отметил здесь: Почему разные методы решения Xc = y в python дают разные решения, если они не должны?. В этом вопросе я использую квадратную матрицу для изучения функции на интервале [-1.+1]
с полиномом высокой степени. Ответ там предложил мне снизить степень полинома и/или увеличить размер интервала. Основная проблема заключается в том, что неясно, как выбрать интервал или максимальную степень, прежде чем вещь станет ненадежной. Я думаю, что моя основная проблема заключается в том, что выбор такого численно устойчивого диапазона зависит от метода, который я могу использовать. В конце концов, я действительно забочусь о том, что
- метод, который я использую, точно (или действительно близок) к псевдо-обратному для этой задачи полиномиального подбора
- что его численно стабильный
В идеале я хочу попробовать многочлен большой степени, но это может быть ограничено моей машинной точностью. Можно ли увеличить числовую точность машины, используя что-то более точное, чем поплавки?
Кроме того, мне очень важно, что любая функция из используемого python, она обеспечивает самый близкий ответ на псевдо-обратный (и, надеюсь, его численно стабильный, поэтому я действительно могу его использовать). Чтобы проверить, какой ответ для псевдоинверсивного, я написал следующий script:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
def l2_loss(y,y_):
N = y.shape[0]
return (1/N)*np.linalg.norm(y-y_)
## some parameters
lb,ub = -200,200
N=100
D0=1
degree_mdl = 120
## target function
freq_cos = 2
f_target = lambda x: np.cos(freq_cos*2*np.pi*x)
## evaluate target_f on x_points
X = np.linspace(lb,ub,N) # [N,]
Y = f_target(X) # [N,]
# get pinv solution
poly_feat = PolynomialFeatures(degree=degree_mdl)
Kern = poly_feat.fit_transform( X.reshape(N,D0) ) # low degrees first [1,x,x**2,...]
c_pinv = np.dot(np.linalg.pinv( Kern ), Y)
## get polyfit solution
c_polyfit = np.polyfit(X,Y,degree_mdl)[::-1] # need to reverse to get low degrees first [1,x,x**2,...]
##
c_lstsq,_,_,_ = np.linalg.lstsq(Kern,Y.reshape(N,1))
##
print('lb,ub = {} '.format((lb,ub)))
print('differences with c_pinv')
print( '||c_pinv-c_pinv||^2 = {}'.format( np.linalg.norm(c_pinv-c_pinv) ))
print( '||c_pinv-c_polyfit||^2 = {}'.format( np.linalg.norm(c_pinv-c_polyfit) ))
print( '||c_pinv-c_lstsq||^2 = {}'.format( np.linalg.norm(c_pinv-c_lstsq) ))
##
print('differences with c_polyfit')
print( '||c_polyfit-c_pinv||^2 = {}'.format( np.linalg.norm(c_polyfit-c_pinv) ))
print( '||c_polyfit-c_polyfit||^2 = {}'.format( np.linalg.norm(c_polyfit-c_polyfit) ))
print( '||c_polyfit-c_lstsq||^2 = {}'.format( np.linalg.norm(c_polyfit-c_lstsq) ))
##
print('differences with c_lstsq')
print( '||c_lstsq-c_pinv||^2 = {}'.format( np.linalg.norm(c_lstsq-c_pinv) ))
print( '||c_lstsq-c_polyfit||^2 = {}'.format( np.linalg.norm(c_lstsq-c_polyfit) ))
print( '||c_lstsq-c_lstsq||^2 = {}'.format( np.linalg.norm(c_lstsq-c_lstsq) ))
##
print('Data set errors')
y_polyfit = np.dot(Kern,c_polyfit)
print( 'J_data(c_polyfit) = {}'.format( l2_loss(y_polyfit,Y) ) )
y_pinv = np.dot(Kern,c_pinv)
print( 'J_data(c_pinv) = {}'.format( l2_loss(y_pinv,Y) ) )
y_lstsq = np.dot(Kern,c_lstsq)
print( 'J_data(c_lstsq) = {}'.format( l2_loss(y_lstsq,Y) ) )
используя это, мне удалось заметить, что редко polyfit
когда-либо соответствует параметрам, которые использует pinv
. Я знаю, что pinv окончательно возвращает псевдоинверсию, поэтому я предполагаю, что если моя главная цель - "убедиться, что я использую псевдоинверсию", то это хорошая идея использовать np.pinv
. Тем не менее, я также математически знаю, что псевдоинверс всегда минимизирует ошибку наименьших квадратов J(c) = || Kc - y ||^2
независимо от того, что (доказательство здесь, теорема 11.1.2 446). Таким образом, может быть, моя цель должна состоять в том, чтобы просто использовать функцию python, которая возвращает наименьшую квадратичную ошибку J
. Таким образом, я побежал (в недоопределенном случае) сравнение трех методов
- Полигит,
np.polyfit
- псевдо-обратный,
np.linalg.pinv
- наименьшие квадраты,
np.linalg.lstsq
и сравнили, какую ошибку наименьших квадратов они дали мне по данным:
![введите описание изображения здесь]()
Затем я просмотрел странные провалы, которые, как кажется, испытывает функция (что, кстати, похоже на полную тайну, почему есть провалы, если алгоритмы не являются стохастическими), а числа обычно меньше для полифита, например:
lb,ub = (-100, 100)
Data set errors
J_data(c_polyfit) = 5.329753025633029e-12
J_data(c_pinv) = 0.06670557822873546
J_data(c_lstsq) = 0.7479733306782645
учитывая эти результаты, и что псевдоинверс является минимизатором наименьших квадратов, кажется, что лучше всего игнорировать np.pinv
. Это лучшее, что нужно сделать? Или я пропущу что-то очевидное?
В качестве дополнительной заметки я вошел в код полифита, чтобы узнать, что именно делает его, имеют более мелкие квадратные ошибки (которые прямо сейчас я использую как способ сказать его наилучшее приближение для псевдо-обратного), и, похоже, он имеет некоторый странный код состояния/числовой стабильности:
# scale lhs to improve condition number and solve
scale = NX.sqrt((lhs*lhs).sum(axis=0))
lhs /= scale
c, resids, rank, s = lstsq(lhs, rhs, rcond)
c = (c.T/scale).T # broadcast scale coefficients
который, как я полагаю, является причиной дополнительной устойчивости к полифиту, который pinv
не имеет?
Правильно ли это решение использовать polyfit
для моей задачи о приближении линейной системы полиномов высокой степени?
также в этот момент я готов использовать другое программное обеспечение, такое как matlab, если оно дает мне правильную псевдоинверсивную и более численную стабильность (для большинства градусов и любых границ).
Другая случайная идея, которую я только что имел, заключалась в том, что, может быть, есть хороший способ пробовать функцию, так что стабильность псевдо-обратного является хорошей. Я предполагаю, что для аппроксимации косинуса с полиномом требуется некоторый тип числа выборок или расстояние между ними (например, теорема выборки шанквона шеннонов гласит, что базовые функции являются синусоидальными...)
Следует отметить, что, вероятно, инвертирование (или псевдоисследование), а затем умножение - плохая идея. См:
https://www.johndcook.com/blog/2010/01/19/dont-invert-that-matrix/
что речь идет только об обратном, но я предполагаю, что он распространяется и на псевдоинверсы.
прямо сейчас моя путаница заключается в том, что обычно мы не хотим явно вычислять псевдоинверсию явно и делать A^+y=x_min_norm
для получения решения минимальной нормы. Однако я бы подумал, что np.lstsq
даст ответ, который я хотел, но его ошибка сильно отличается от другой. Я нахожу это чрезвычайно запутанным... заставляю меня думать, что я использую неправильный способ получить решение минимальной нормы в python.
Я не пытаюсь получить регуляризованное решение. Я пытаюсь получить решение минимальной нормы и ничего больше, как можно точнее.
Ответы
Ответ 1
Моя область исследований включает алгоритм сжатия, который по существу называется расширением Фурье. Что является наиболее точным? Он сильно зависит от вектора, который я считаю из-за свойств гладкости. Летом я использовал что-то, называемое Savitsky Golay. Существуют довольно численные и точные способы фильтрации этого. Тем не менее, мой советник имеет метод, который является относительно быстрым и численно стабильным. Область называется продолжением или продолжением Фурье. Как? Я не знаю, разрешено ли мне опубликовать его, вот статья . Если я полагаю, что я, возможно, уже опубликовал летом здесь в python частично.
Это не имеет ничего общего с python, потому что python использует те же базовые библиотеки, что и большинство схем численного кодирования, которые являются BLAS и LAPACK. Netlib находится в режиме онлайн.
Есть несколько других подобных быстрых и численно устойчивых идей, которые могут быть подходящими, я бы рекомендовал. Существует целая книга, посвященная этому b y Boyd. Главы 6 и 7 посвящены этому. Речь идет об общей вариации с регуляризацией из-за основного шума, который у вас может быть в сигнале, который я себе представляю.
Другие аспекты. Возможно, вам придется регулировать СВД из-за плохого кондиционирования. Есть книги, посвященные этому обычно. Просто ответьте на вопрос, какой алгоритм лучше всего. Для алгоритмов существует несколько измерений, и вы не указали на свойства проблемы. Если вы не знали о явление Рунге. Это использование высокопоставленных многочленов является неблагоприятным.
Существует целый класс полиномов Эрмита для решения явлений Гиббса и других методов фильтрации, но это не так хорошо. Вы используете общие функции. Я бы рекомендовал получить Трефтен и Бау. Иногда они делают Революцию Чебычева.
Каково условие числа K. Кроме того, есть что-то, что происходит при подгонке полиномов, называемых явлениями Runges. Вы должны это учитывать. Используйте общие функции, необходимые для приближения низкого ранга для регуляризации, если число условий слишком велико. Я просто прочитал его. Вы используете матрицу Вандермонда. Я собираюсь продемонстрировать это довольно легко. Матрицы Вандермонда плохи. Не используйте их. У них есть узлы.
v = (1:.5:6);
V = vander(v);
c1 = cond(V)
v2 = (1:.5:12);
c2 = cond(vander(v2));
display(c1)
display(c2)
c1 =
6.0469e + 12
c2 =
9.3987e + 32
Я попытался приблизить низкое ранжирование, но матрицы вандермонда не очень приятны. Видеть.
function B = lowapprox(A)
% Takes a matrix A
% Returns a low rank approx of it
% utilizing the SVD
chi = 1e-10;
[U,S,V] = svd(A,0);
DS = diag(S);
aa = find(DS > chi);
s= S(aa,aa);
k = length(aa);
Un = U(:,1:k);
Vn = V(:,1:k)';
B = Un*s*Vn;
end
V2 = vander(v2);
r2 = rank(V2);
c2=cond(V2);
B = lowapprox(V2);
c3 = cond(B);
display(c3)
c2 =
9.3987e+32
c3 =
3.7837e+32
ничего не делает... Если вы не знаете, что происходит, когда вы получаете это обратное, число условий равно максимальному сингулярному значению над минимумом, так что у вас есть очень маленькие особые значения при точности машины.
Кроме того, я думаю, у вас есть некоторая путаница в отношении минимальной нормы и регуляризации. Вы сказали, что хотите минимальную норму в смысле наименьших квадратов. SVD дает наименьшие квадраты.
Свойство 9, A построено из базы SVD. Это покрыто трефетеном, но матрица вандермонда плохо обусловлена.
даже небольшие плохо построенные матрицы вандермонда будут терять его. Теперь о вашем решении. Не используйте матрицы вандермонда. Построим полином иначе. Лучшая идея - барицентрическая интерполяция лагранжа. Библиотека здесь
Вот пример в Matlab.
t= (0:.01:pi)';
f = cos(t);
data = [t,f];
f1 = barylag(data,t)
display(err =norm(f1-f1))
err =
0
barylag берется с сайта Matlab. Поскольку я не могу действительно прокомментировать ваши несоответствия, вы должны понять, как именно выполняется lsqr. Алгоритмы Lsqr - это методы Крылова. Это описано в Трефетене. SVD также У меня есть пример на моей странице quora о числовой стабильности с QR, который является тем, как вы на самом деле строите эти алгоритмы