'Linalg и NumPy это.решить " и " линалг.lstsq не давать же ответ как Матлаб, или mldivide
Я пытаюсь реализовать алгоритм подгонки кривой наименьших квадратов на Python, уже написав его на Matlab. Однако у меня возникли проблемы с получением правильной матрицы преобразования, и проблема, похоже, происходит на этапе решения. (Edit: моя матрица преобразования невероятно точна с Matlab, но полностью отключена с Python.)
Я просмотрел множество источников в интернете, и все они указывают, что для перевода "mldivide" Matlab вам нужно использовать " np.linalg.решить" если матрица квадратная и неособая, и ' np.linalg.lstsq ' в противном случае. Но мои результаты не совпадают.
в чем проблема? Если это связано с реализацией функций, каков правильный перевод mldivide в numpy?
я прикрепил обе версии кода ниже. Они по существу являются одной и той же реализацией, за исключением части решения.
код Matlab:
%% Least Squares Fit
clear, clc, close all
% Calibration Data
scr_calib_pts = [0,0; 900,900; -900,900; 900,-900; -900,-900];
cam_calib_pts = [-1,-1; 44,44; -46,44; 44,-46; -46,-46];
cam_x = cam_calib_pts(:,1);
cam_y = cam_calib_pts(:,2);
% Least Squares Fitting
A_matrix = [];
for i = 1:length(cam_x)
A_matrix = [A_matrix;1, cam_x(i), cam_y(i), ...
cam_x(i)*cam_y(i), cam_x(i)^2, cam_y(i)^2];
end
A_star = A_matrix'*A_matrix
B_star = A_matrix'*scr_calib_pts
transform_matrix = mldivide(A_star,B_star)
% Testing Data
test_scr_vec = [200,400; 1600,400; -1520,1740; 1300,-1800; -20,-1600];
test_cam_vec = [10,20; 80,20; -76,87; 65,-90; -1,-80];
test_cam_x = test_cam_vec(:,1);
test_cam_y = test_cam_vec(:,2);
% Coefficients for Transform
coefficients = [];
for i = 1:length(test_cam_x)
coefficients = [coefficients;1, test_cam_x(i), test_cam_y(i), ...
test_cam_x(i)*test_cam_y(i), test_cam_x(i)^2, test_cam_y(i)^2];
end
% Mapped Points
results = coefficients*transform_matrix;
% Plotting
test_scr_x = test_scr_vec(:,1)';
test_scr_y = test_scr_vec(:,2)';
results_x = results(:,1)';
results_y = results(:,2)';
figure
hold on
load seamount
s = 50;
scatter(test_scr_x, test_scr_y, s, 'r')
scatter(results_x, results_y, s)
Python код:
# Least Squares fit
import numpy as np
import matplotlib.pyplot as plt
# Calibration data
camera_vectors = np.array([[-1,-1], [44,44], [-46,44], [44,-46], [-46,-46]])
screen_vectors = np.array([[0,0], [900,900], [-900,900], [900,-900], [-900,-900]])
# Separate axes
cam_x = np.array([i[0] for i in camera_vectors])
cam_y = np.array([i[1] for i in camera_vectors])
# Initiate least squares implementation
A_matrix = []
for i in range(len(cam_x)):
new_row = [1, cam_x[i], cam_y[i],
cam_x[i]*cam_y[i], cam_x[i]**2, cam_y[i]**2]
A_matrix.append(new_row)
A_matrix = np.array(A_matrix)
A_star = np.transpose(A_matrix).dot(A_matrix)
B_star = np.transpose(A_matrix).dot(screen_vectors)
print A_star
print B_star
try:
# Solve version (Implemented)
transform_matrix = np.linalg.solve(A_star,B_star)
print "Solve version"
print transform_matrix
except:
# Least squares version (implemented)
transform_matrix = np.linalg.lstsq(A_star,B_star)[0]
print "Least Squares Version"
print transform_matrix
# Test data
test_cam_vec = np.array([[10,20], [80,20], [-76,87], [65,-90], [-1,-80]])
test_scr_vec = np.array([[200,400], [1600,400], [-1520,1740], [1300,-1800], [-20,-1600]])
# Coefficients of quadratic equation
test_cam_x = np.array([i[0] for i in test_cam_vec])
test_cam_y = np.array([i[1] for i in test_cam_vec])
coefficients = []
for i in range(len(test_cam_x)):
new_row = [1, test_cam_x[i], test_cam_y[i],
test_cam_x[i]*test_cam_y[i], test_cam_x[i]**2, test_cam_y[i]**2]
coefficients.append(new_row)
coefficients = np.array(coefficients)
# Transform camera coordinates to screen coordinates
results = coefficients.dot(transform_matrix)
# Plot points
results_x = [i[0] for i in results]
results_y = [i[1] for i in results]
actual_x = [i[0] for i in test_scr_vec]
actual_y = [i[1] for i in test_scr_vec]
plt.plot(results_x, results_y, 'gs', actual_x, actual_y, 'ro')
plt.show()
изменить (в соответствии с предложением):
# Transform matrix with linalg.solve
[[ 2.00000000e+01 2.00000000e+01]
[ -5.32857143e+01 7.31428571e+01]
[ 7.32857143e+01 -5.31428571e+01]
[ -1.15404203e-17 9.76497106e-18]
[ -3.66428571e+01 3.65714286e+01]
[ 3.66428571e+01 -3.65714286e+01]]
# Transform matrix with linalg.lstsq:
[[ 2.00000000e+01 2.00000000e+01]
[ 1.20000000e+01 8.00000000e+00]
[ 8.00000000e+00 1.20000000e+01]
[ 1.79196935e-15 2.33146835e-15]
[ -4.00000000e+00 4.00000000e+00]
[ 4.00000000e+00 -4.00000000e+00]]
% Transform matrix with mldivide:
20.0000 20.0000
19.9998 0.0002
0.0002 19.9998
0 0
-0.0001 0.0001
0.0001 -0.0001
2 ответов
интересно то, что вы получите совершенно разные результаты с np.linalg.lstsq
и np.linalg.solve
.
x1 = np.linalg.lstsq(A_star, B_star)[0]
x2 = np.linalg.solve(A_star, B_star)
оба должны предложить решение для уравнения AX = B. Однако, они дают два совершенно разных массива:
In [37]: x1
Out[37]:
array([[ 2.00000000e+01, 2.00000000e+01],
[ 1.20000000e+01, 7.99999999e+00],
[ 8.00000001e+00, 1.20000000e+01],
[ -1.15359111e-15, 7.94503352e-16],
[ -4.00000001e+00, 3.99999999e+00],
[ 4.00000001e+00, -3.99999999e+00]]
In [39]: x2
Out[39]:
array([[ 2.00000000e+01, 2.00000000e+01],
[ -4.42857143e+00, 2.43809524e+01],
[ 2.44285714e+01, -4.38095238e+00],
[ -2.88620104e-18, 1.33158696e-18],
[ -1.22142857e+01, 1.21904762e+01],
[ 1.22142857e+01, -1.21904762e+01]])
оба должны дать точное (вплоть до точности вычисления) решение группы линейных уравнений, а для неособой матрицы существует ровно одно решение.
что-то должно быть не так. Давайте посмотрим, если это оба кандидата могут быть решениями исходного уравнения:
In [41]: A_star.dot(x1)
Out[41]:
array([[ -1.11249392e-08, 9.86256055e-09],
[ 1.62000000e+05, -1.65891834e-09],
[ 0.00000000e+00, 1.62000000e+05],
[ -1.62000000e+05, -1.62000000e+05],
[ -3.24000000e+05, 4.47034836e-08],
[ 5.21540642e-08, -3.24000000e+05]])
In [42]: A_star.dot(x2)
Out[42]:
array([[ -1.45519152e-11, 1.45519152e-11],
[ 1.62000000e+05, -1.45519152e-11],
[ 0.00000000e+00, 1.62000000e+05],
[ -1.62000000e+05, -1.62000000e+05],
[ -3.24000000e+05, 0.00000000e+00],
[ 2.98023224e-08, -3.24000000e+05]])
они, похоже, дают одно и то же решение, которое по существу совпадает с B_star
так и должно быть. Это подводит нас к объяснению. С простой линейной алгебры, мы могли бы предсказать, что . (x1-x2) должно быть очень близко к нулю:
In [43]: A_star.dot(x1-x2)
Out[43]:
array([[ -1.11176632e-08, 9.85164661e-09],
[ -1.06228981e-09, -1.60071068e-09],
[ 0.00000000e+00, -2.03726813e-10],
[ -6.72298484e-09, 4.94765118e-09],
[ 5.96046448e-08, 5.96046448e-08],
[ 2.98023224e-08, 5.96046448e-08]])
и это действительно. Таким образом, кажется, что существует нетривиальное решение для уравнения Ax = 0, решение которого x = x1-x2, что означает, что матрица сингулярна, и поэтому существует бесконечное число различных решений для Ax=B.
таким образом, проблема не в NumPy или Matlab, она находится в самой матрице.
однако, в случае этой матрицы ситуация немного сложнее. A_star
кажется сингулярным по определению выше (Ax=0 для x0). С другой стороны, его определитель не равен нулю и не является сингулярным.
в этом случае A_star
является примером матрицы, которая численно неустойчивый, но не сингулярный. The solve
метод решает его с помощью простого умножения на обратный. В этом случае это плохой выбор, так как инверсия содержит очень большие и очень маленькие значения. Это делает multiplicaion склонным к ошибкам округления. Это можно увидеть, посмотрев на номер условия матрицы:
In [65]: cond(A_star)
Out[65]: 1.3817810855559592e+17
это очень высокое число условий, и матрица плохо обусловлена.
в этом случае использование обратного к решить проблему-плохой подход. Как вы можете видеть, подход наименьших квадратов дает лучшие результаты. Однако лучшим решением является масштабирование входных значений так, чтобы x и X^2 находились в одном диапазоне. Один очень хороший масштабирование масштабировать все между -1 и 1.
одна вещь, которую вы могли бы рассмотреть, - попытаться использовать возможности индексирования NumPy. Например:
cam_x = np.array([i[0] for i in camera_vectors])
эквивалентно:
cam_x = camera_vectors[:,0]
и вы можете построить свой массив A
таким образом:
A_matrix = np.column_stack((np.ones_like(cam_x), cam_x, cam_y, cam_x*cam_y, cam_x**2, cam_y**2))
нет необходимости создавать списки списков или каких-либо циклов.
матрица A_matrix является матрицей 6 на 5, поэтому A_star является сингулярной матрицей. В результате нет уникального решения, и результат обеих программ правильный. Это соответствует исходной стадии, а не над определенной проблемой.