QR Decomposition là gì và chi tiết cách tính


Giới thiệu về QR Decomposition và các khái niệm liên quan, hướng dẫn chi tiết cách tính QR Decomposition và triển khai bằng Python.

Sunday, October 22, 2023
2849 words-15 min read-––– views
QR Decomposition là gì và chi tiết cách tính

Các thuật toán phân rã (decomposition) cho ma trận (matrix) là một trong những thuật toán quan trọng nhất trong machine learning nói chung và toán ứng dụng nói riêng. Những phép phân rã này không những giúp ta giảm độ phức tạp và chi phí khi thực hiện các phép tính với các ma trận lớn mà còn có những ứng dụng đặc biệt khác.

Ở bài viết này, chúng ta sẽ cùng tìm hiểu về QR Decomposition (Phân rã QR) là gì và chi tiết cách tính bằng Gram-Schmidt Process (Chu trình Gram-Schmidt).

Khái niệm

QR Decomposition là một phương pháp phân rã một ma trận bất kì thành tích của hai ma trận Q\mathbf{Q}R\mathbf{R}, trong đó Q\mathbf{Q} là một ma trận trực chuẩn (orthonormal matrix)R\mathbf{R} là một ma trận tam giác trên (upper triangular matrix).

Lưu ý

Thuật ngữ "Orthonormal Matrix" hiếm khi được sử dụng mà thay vào đó, những ma trận như vậy sẽ được gọi là "Special Orthogonal Matrix". Tuy nhiên, trong bài viết này, chúng ta sẽ sử dụng thuật ngữ "Orthonormal Matrix" để phân biệt với "Orthogonal Matrix" một cách tường minh và dễ hiểu hơn.

Nhắc lại

Ma trận

Ma trận là một tập hợp các vector đơn vị (unit vector) được sắp xếp theo hàng hoặc cột. Một ma trận ARm×n\mathbf{A} \in \mathbb{R}^{m \times n} có thể được biểu diễn như sau:

A=[a1a2an]=[a11a12a1na21a22a2nam1am2amn]\begin{align} \mathbf{A} &= \begin{bmatrix} \mathbf{a}_1 & \mathbf{a}_2 & \dots & \mathbf{a}_n \end{bmatrix} \notag \\ &= \begin{bmatrix} \mathbf{a}_{11} & \mathbf{a}_{12} & \dots & \mathbf{a}_{1n} \\ \mathbf{a}_{21} & \mathbf{a}_{22} & \dots & \mathbf{a}_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ \mathbf{a}_{m1} & \mathbf{a}_{m2} & \dots & \mathbf{a}_{mn} \end{bmatrix} \\ \end{align}

Các vector đơn vị đóng vai trò định nghĩa lại một không gian mới bằng tổ hợp tuyến tính (linear combination) của chúng. Ví dụ như không gian 3 chiều cơ bản được định nghĩa bởi 3 vector đơn vị i=(1,0,0)i = (1, 0, 0), j=(0,1,0)j = (0, 1, 0)k=(0,0,1)k = (0, 0, 1):

R3=[ijk]=[100010001]\begin{align} R^3 &= \begin{bmatrix} i & j & k \end{bmatrix} \notag \\ &= \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \\ \end{align}

Ý nghĩa hình học của ma trận là nó biểu diễn một biến đổi tuyến tính (linear transformation), hay nói cách khác nó giống như một phép biến đổi một không gian này sang một không gian khác mà phép biến đổi này có thể là tổ hợp của các phép quay (rotation), co giãn (stretch), trượt (shear),...

Ma trận ARm×n\mathbf{A} \in \mathbb{R}^{m \times n} biểu diễn một phép biến đổi từ không gian Rn\mathbb{R}^n sang không gian Rm\mathbb{R}^m.

Phụ thuộc Tuyến tính và Độc lập Tuyến tính

Phụ thuộc Tuyến tính

Một tập hợp các vectorv1,v2,,vn\mathbf{v}_1, \mathbf{v}_2, \dots, \mathbf{v}_n được gọi là phụ thuộc tuyến tính (linearly dependent) nếu tồn tại nghiệm là một tập hợp các hệ số c1,c2,,cnc_1, c_2, \dots, c_n không đồng thời bằng 00 sao cho:

c1v1+c2v2++cnvn=0\begin{align} c_1 \mathbf{v}_1 + c_2 \mathbf{v}_2 + \dots + c_n \mathbf{v}_n = 0 \end{align}

Điều này có nghĩa là một trong các hệ số phải khác 00, giả sử nếu c10c_1 \neq 0 thì:

{v1=c2c1v2cnc1vnkhin>1v1=0khin=1\begin{align} \begin{cases} \mathbf{v}_1 = -\frac{c_2}{c_1} \mathbf{v}_2 - \dots - \frac{c_n}{c_1} \mathbf{v}_n &\text{khi} \enspace n > 1 \\ \mathbf{v}_1 = 0 & \text{khi} \enspace n = 1 \\ \end{cases} \end{align}

Lúc này, ta có thể thấy v1\mathbf{v}_1 đã không tạo ra một chiều không gian mới mà nó chỉ nằm trong không gian của các vector khác. Nói cách khác, v1\mathbf{v}_1 đã phụ thuộc tuyến tính vào các vector còn lại.

Độc lập Tuyến tính

Ngược lại với phụ thuộc tuyến tính, một tập hợp các vector v1,v2,,vn\mathbf{v}_1, \mathbf{v}_2, \dots, \mathbf{v}_n được gọi là độc lập tuyến tính (linearly independent) nếu phương trình (3) chỉ có một nghiệm duy nhất là c1=c2==cn=0c_1 = c_2 = \dots = c_n = 0.

Vì lúc này các vector đều độc lập tuyến tính với nhau và phương trình chỉ bằng 00 khi và chỉ khi tất cả các hệ số đều đồng thời bằng 00.

Ma trận Trực giao và Ma trận Trực chuẩn

Ma trận Trực giao

Hai vector u\mathbf{u}v\mathbf{v} được gọi là trực giao (orthogonal) khi và chỉ khi chúng vuông góc với nhau, tức là uTv=0\mathbf{u}^T \mathbf{v} = 0.

Một ma trận trực giao (orthogonal matrix) là một tập hợp các vector mà mọi cặp vector đơn vị trong đó đều trực giao với nhau.

Gọi A\mathbf{A} là một ma trận trực giao khi đó:

ATA=I=AAT    A1=AT\begin{align} \mathbf{A}^T \mathbf{A} = \mathbf{I} = \mathbf{A} \mathbf{A}^T \implies \mathbf{A}^{-1} = \mathbf{A}^T \end{align}

Ma trận Trực chuẩn

Hai vector u\mathbf{u}v\mathbf{v} được gọi là trực chuẩn (orthonormal) khi và chỉ khi chúng là trực giao và đồng thời có độ dài bằng 11, tức là uTv=0\mathbf{u}^T \mathbf{v} = 0u=v=1||\mathbf{u}|| = ||\mathbf{v}|| = 1.

Một ma trận trực chuẩn là một trường hợp đặc biệt của ma trận trực giao khi nó bị ràng buộc bởi điều kiện rằng mọi vector đơn vị trong đó đều có độ dài bằng 11.

Vì các tính chất trên, nếu A\mathbf{A} là một ma trận trực chuẩn thì nó sẽ thỏa mãn toàn bộ tính chất của một ma trận trực giaođịnh thức (determinant) det(A)=1det(\mathbf{A}) = 1.

Phép chiếu Vector

Cho trước hai vector u\mathbf{u}v\mathbf{v}, ta có thể tính được phép chiếu vector (vector projection) của v\mathbf{v} lên u\mathbf{u} bằng công thức:

proju(v)=u,vu,uu\begin{align} proj_{\mathbf{u}} (\mathbf{v}) &= \frac{\langle \mathbf{u}, \mathbf{v} \rangle}{\langle \mathbf{u}, \mathbf{u} \rangle} \mathbf{u} \\ \end{align}

Trong đó:

  • u,v\langle \mathbf{u}, \mathbf{v} \rangle: tích vô hướng (inner product) của u\mathbf{u}v\mathbf{v}.
  • proju(v)proj_{\mathbf{u}} (\mathbf{v}): phép chiếu vector (hình chiếu) của v\mathbf{v} lên u\mathbf{u}.

Cách tính

Có hai phương pháp để thực hiện bài toán này là Gram-Schmidt ProcessHouseholder Reflection (Phép phản xạ Householder).

Chúng ta sẽ được giới thiệu về Gram-Schmidt Process trong bài viết này.

Cho trước ma trận độc lập tuyến tính ARm×n\mathbf{A} \in \mathbb{R}^{m \times n} có các vector đơn vị a1,a2,,an\mathbf{a}_1, \mathbf{a}_2, \dots, \mathbf{a}_n:

A=[a1a2an]=[a11a12a1na21a22a2nam1am2amn]\begin{align} \mathbf{A} &= \begin{bmatrix} \mathbf{a}_1 & \mathbf{a}_2 & \dots & \mathbf{a}_n \end{bmatrix} \notag \\ &= \begin{bmatrix} \mathbf{a}_{11} & \mathbf{a}_{12} & \dots & \mathbf{a}_{1n} \\ \mathbf{a}_{21} & \mathbf{a}_{22} & \dots & \mathbf{a}_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ \mathbf{a}_{m1} & \mathbf{a}_{m2} & \dots & \mathbf{a}_{mn} \end{bmatrix} \\ \end{align}

Bước 1: Trực giao hóa các unit vector

Chúng ta sẽ dùng Gram-Schmidt Process để tìm ra hệ trực giao là ma trận VRm×n\mathbf{V} \in \mathbb{R}^{m \times n} từ A\mathbf{A}. Quá trình trực giao hóa từng vector của A\mathbf{A} được thực hiện như sau:

v1=a1\begin{align} \mathbf{v}_1 &= \mathbf{a}_1 \\ \end{align}
  • v2\mathbf{v}_2 sẽ được trực giao dựa trên v1\mathbf{v}_1 bằng cách lấy a2\mathbf{a}_2 trừ đi hình chiếu của chính nó lên v1\mathbf{v}_1. Điều này sẽ giúp v2\mathbf{v}_2 vuông góc với v1\mathbf{v}_1.
v2=a2projv1(a2)\begin{align} \mathbf{v}_2 &= \mathbf{a}_2 - proj_{\mathbf{v}_1} (\mathbf{a}_2) \\ \end{align}
  • Tương tự với v3\mathbf{v}_3, tuy nhiên để đảm bảo vector này vuông góc với tất cả các vector trước đó, nó sẽ phức tạp hơn một chút vì ta sẽ phải trực giao nó dựa trên cả v1\mathbf{v}_1v2\mathbf{v}_2 (sau khi đã trực giao với v1\mathbf{v}_1, ta sẽ lấy nó đi trực giao tiếp với v2\mathbf{v}_2).
v3=a3projv1(a3)projv2(a3)\begin{align} \mathbf{v}_3 &= \mathbf{a}_3 - proj_{\mathbf{v}_1} (\mathbf{a}_3) - proj_{\mathbf{v}_2} (\mathbf{a}_3) \\ \end{align}
  • Cuối cùng, ta có công thức tổng quát cho vector thứ kk:
vk=akj=1k1projvj(ak)\begin{align} \mathbf{v}_k = \mathbf{a}_k - \sum_{j=1}^{k-1} proj_{\mathbf{v}_j} (\mathbf{a}_k) \end{align}

Bước 2: Trực chuẩn hóa không gian vừa tính được

Ta đã có ma trận V\mathbf{V} là một ma trận trực giao, tuy nhiên nó chưa phải là một ma trận trực chuẩn. Để trực chuẩn hóa nó, ta sẽ chia mỗi vector cột của V\mathbf{V} cho độ dài chính nó để đưa độ dài này về 11.

Lúc này, ta tìm được ma trận Q\mathbf{Q} là từ các vector đã được chuẩn hóa của V\mathbf{V}.

qk=vkvk\begin{align} \mathbf{q}_k &= \frac{\mathbf{v}_k}{||\mathbf{v}_k||} \\ \end{align}

Bước 3: Tìm ma trận R\mathbf{R}

Ta có thể tìm được ma trận R\mathbf{R} bằng cách nhân ma trận QT\mathbf{Q}^T với A\mathbf{A}:

A=QRQTA=QTQRQTA=IRR=QTA\begin{align} \mathbf{A} &= \mathbf{Q} \mathbf{R} \notag \\ \mathbf{Q}^T \mathbf{A} &= \mathbf{Q}^T \mathbf{Q} \mathbf{R} \notag \\ \mathbf{Q}^T \mathbf{A} &= \mathbf{I} \mathbf{R} \tag*{\text{xem lại (5)}} \\ \mathbf{R} &= \mathbf{Q}^T \mathbf{A} \\ \end{align}

Ngoài ra, ta cũng có thể tìm được ma trận R\mathbf{R} bằng cách tính tích vô hướng của các vector đơn vị từ ma trận Q\mathbf{Q} với các vector đơn vị từ ma trận A\mathbf{A}.

R=[q1,a1q1,a2q1,an0q2,a2q2,an00qn,an]\begin{align} \mathbf{R} = \begin{bmatrix} \langle \mathbf{q}_1 , \mathbf{a}_1 \rangle & \langle \mathbf{q}_1 , \mathbf{a}_2 \rangle & \dots & \langle \mathbf{q}_1 , \mathbf{a}_n \rangle \\ 0 & \langle \mathbf{q}_2 , \mathbf{a}_2 \rangle & \dots & \langle \mathbf{q}_2 , \mathbf{a}_n \rangle \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \dots & \langle \mathbf{q}_n , \mathbf{a}_n \rangle \end{bmatrix} \end{align}

Từ đây ta có thể hiểu được lí do R\mathbf{R} là một ma trận tam giác trên vì trong quá trình trực giao hóa, chúng ta cho vector vk\mathbf{v}_k trực giao với toàn bộ các vector i<ki < k, dẫn đến các tích vô hướng của chúng bằng 00, xem lại (9)(9).

vk,ai=0    qk,ai=0i<k\begin{align} &\langle \mathbf{v}_k , \mathbf{a}_i \rangle = 0 \notag \\ \iff &\langle \mathbf{q}_k , \mathbf{a}_i \rangle = 0 \enspace \forall i < k \end{align}

Ví dụ

Hãy thực hiện QR Decomposition cho ma trận A\mathbf{A} được tạo bởi 3 vector a1\mathbf{a}_1, a2\mathbf{a}_2a3\mathbf{a}_3 như sau:

a1=(1,3,2)a2=(2,2,1)a3=(3,1,2)\begin{align} \mathbf{a}_1 &= (1, 3, 2) \notag \\ \mathbf{a}_2 &= (2, 2, 1) \notag \\ \mathbf{a}_3 &= (3, 1, 2) \\ \end{align} A=[a1a2a3]=[123321212]\begin{align} \mathbf{A} &= \begin{bmatrix} \mathbf{a}_1 & \mathbf{a}_2 & \mathbf{a}_3 \end{bmatrix} \notag \\ &= \begin{bmatrix} 1 & 2 & 3 \\ 3 & 2 & 1 \\ 2 & 1 & 2 \end{bmatrix} \\ \end{align} v1=a1=(1,3,2)v2=a2projv1(a2)=(2,2,1)v1,a2v1,v1v1=(2,2,1)2×1+2×3+1×212+32+22(1,3,2)=(2,2,1)57(1,3,2)=17(9,1,3)v3=a3projv1(a3)projv2(a3)=(3,1,2)v1,a3v1,v1v1v2,a3v2,v2v2=(3,1,2)3×1+1×3+2×212+32+22(1,3,2)17(3×9+1×1+2×3)172(92+(1)2+(3)2)17(9,1,3)=(3,1,2)57(1,3,2)2091(9,1,3)=413(1,3,4)\begin{align} \mathbf{v}_1 &= \mathbf{a}_1 \notag \\ &= (1, 3, 2) \\ \mathbf{v}_2 &= \mathbf{a}_2 - proj_{\mathbf{v}_1} (\mathbf{a}_2) \notag \\ &= (2, 2, 1) - \frac{\langle \mathbf{v}_1, \mathbf{a}_2 \rangle}{\langle \mathbf{v}_1, \mathbf{v}_1 \rangle} \mathbf{v}_1 \notag \\ &= (2, 2, 1) - \frac{2 \times 1 + 2 \times 3 + 1 \times 2}{1^2 + 3^2 + 2^2} (1, 3, 2) \notag \\ &= (2, 2, 1) - \frac{5}{7} (1, 3, 2) \notag \\ &= \frac{1}{7} (9, -1, -3) \\ \mathbf{v}_3 &= \mathbf{a}_3 - proj_{\mathbf{v}_1} (\mathbf{a}_3) - proj_{\mathbf{v}_2} (\mathbf{a}_3) \notag \\ &= (3, 1, 2) - \frac{\langle \mathbf{v}_1, \mathbf{a}_3 \rangle}{\langle \mathbf{v}_1, \mathbf{v}_1 \rangle} \mathbf{v}_1 - \frac{\langle \mathbf{v}_2, \mathbf{a}_3 \rangle}{\langle \mathbf{v}_2, \mathbf{v}_2 \rangle} \mathbf{v}_2 \notag \\ &= (3, 1, 2) - \frac{3 \times 1 + 1 \times 3 + 2 \times 2}{1^2 + 3^2 + 2^2} (1, 3, 2) - \frac{\frac{1}{7} (3 \times 9 + 1 \times -1 + 2 \times -3)}{\frac{1}{7^2} (9^2 + (-1)^2 + (-3)^2)} \frac{1}{7} (9, -1, -3) \notag \\ &= (3, 1, 2) - \frac{5}{7} (1, 3, 2) - \frac{20}{91} (9, -1, -3) \notag \\ &= \frac{4}{13} (1, -3, 4) \\ \end{align} V=[v1v2v3]=[191313234]\begin{align} \mathbf{V} &= \begin{bmatrix} \mathbf{v}_1 & \mathbf{v}_2 & \mathbf{v}_3 \end{bmatrix} \notag \\ &= \begin{bmatrix} 1 & 9 & 1 \\ 3 & -1 & -3 \\ 2 & -3 & 4 \end{bmatrix} \\ \end{align} q1=v1v1=112+32+22(1,3,2)=114(1,3,2)q2=v2v2=192+(1)2+(3)2(9,1,3)=191(9,1,3)q3=v3v3=112+(3)2+42(1,3,4)=126(1,3,4)\begin{align} \mathbf{q}_1 &= \frac{\mathbf{v}_1}{||\mathbf{v}_1||} \notag \\ &= \frac{1}{\sqrt{1^2 + 3^2 + 2^2}} (1, 3, 2) \notag \\ &= \frac{1}{\sqrt{14}} (1, 3, 2) \\ \mathbf{q}_2 &= \frac{\mathbf{v}_2}{||\mathbf{v}_2||} \notag \\ &= \frac{1}{\sqrt{9^2 + (-1)^2 + (-3)^2}} (9, -1, -3) \notag \\ &= \frac{1}{\sqrt{91}} (9, -1, -3) \\ \mathbf{q}_3 &= \frac{\mathbf{v}_3}{||\mathbf{v}_3||} \notag \\ &= \frac{1}{\sqrt{1^2 + (-3)^2 + 4^2}} (1, -3, 4) \notag \\ &= \frac{1}{\sqrt{26}} (1, -3, 4) \\ \end{align} Q=[q1q2q3]=[114991126314191326214391426][0.2670.9430.1960.8020.1050.5880.5350.3140.784]\begin{align} \mathbf{Q} &= \begin{bmatrix} \mathbf{q}_1 & \mathbf{q}_2 & \mathbf{q}_3 \end{bmatrix} \notag \\ &= \begin{bmatrix} \frac{1}{\sqrt{14}} & \frac{9}{\sqrt{91}} & \frac{1}{\sqrt{26}} \\ \frac{3}{\sqrt{14}} & -\frac{1}{\sqrt{91}} & -\frac{3}{\sqrt{26}} \\ \frac{2}{\sqrt{14}} & -\frac{3}{\sqrt{91}} & \frac{4}{\sqrt{26}} \end{bmatrix} \notag \\ &\approx \begin{bmatrix} 0.267 & 0.943 & 0.196 \\ 0.802 & -0.105 & -0.588 \\ 0.535 & -0.314 & 0.784 \end{bmatrix} \\ \end{align}
  • Tính tích QTA\mathbf{Q}^T \mathbf{A}:
q1,a1=114(1×1+3×3+2×2)=14q1,a2=114(1×2+3×2+2×1)=1014q1,a3=114(1×3+3×1+2×2)=1014q2,a2=391(9×21×23×1)=1391q2,a3=391(9×31×13×2)=2091q3,a3=126(1×33×1+4×2)=826\begin{align} \langle \mathbf{q}_1 , \mathbf{a}_1 \rangle &= \frac{1}{\sqrt{14}} (1 \times 1 + 3 \times 3 + 2 \times 2) = \sqrt{14} \\ \langle \mathbf{q}_1 , \mathbf{a}_2 \rangle &= \frac{1}{\sqrt{14}} (1 \times 2 + 3 \times 2 + 2 \times 1) = \frac{10}{\sqrt{14}} \\ \langle \mathbf{q}_1 , \mathbf{a}_3 \rangle &= \frac{1}{\sqrt{14}} (1 \times 3 + 3 \times 1 + 2 \times 2) = \frac{10}{\sqrt{14}} \\ \langle \mathbf{q}_2 , \mathbf{a}_2 \rangle &= \frac{3}{\sqrt{91}} (9 \times 2 - 1 \times 2 - 3 \times 1) = \frac{13}{\sqrt{91}} \\ \langle \mathbf{q}_2 , \mathbf{a}_3 \rangle &= \frac{3}{\sqrt{91}} (9 \times 3 - 1 \times 1 - 3 \times 2) = \frac{20}{\sqrt{91}} \\ \langle \mathbf{q}_3 , \mathbf{a}_3 \rangle &= \frac{1}{\sqrt{26}} (1 \times 3 - 3 \times 1 + 4 \times 2) = \frac{8}{\sqrt{26}} \\ \end{align} R=[q1,a1q1,a2q1,a30q2,a2q2,a300q3,a3]=[141014101401391209100826][3.741657392.672612422.6726124201.362770292.09656967001.56892908]\begin{align} \mathbf{R} &= \begin{bmatrix} \langle \mathbf{q}_1 , \mathbf{a}_1 \rangle & \langle \mathbf{q}_1 , \mathbf{a}_2 \rangle & \langle \mathbf{q}_1 , \mathbf{a}_3 \rangle \\ 0 & \langle \mathbf{q}_2 , \mathbf{a}_2 \rangle & \langle \mathbf{q}_2 , \mathbf{a}_3 \rangle \\ 0 & 0 & \langle \mathbf{q}_3 , \mathbf{a}_3 \rangle \end{bmatrix} \notag \\ &= \begin{bmatrix} \sqrt{14} & \frac{10}{\sqrt{14}} & \frac{10}{\sqrt{14}} \\ 0 & \frac{13}{\sqrt{91}} & \frac{20}{\sqrt{91}} \\ 0 & 0 & \frac{8}{\sqrt{26}} \end{bmatrix} \notag \\ &\approx \begin{bmatrix} 3.74165739 & 2.67261242 & 2.67261242 \\ 0 & 1.36277029 & 2.09656967 \\ 0 & 0 & 1.56892908 \end{bmatrix} \\ \end{align}
  • Như vậy, ta đã tìm được ma trận Q\mathbf{Q}R\mathbf{R} thỏa mãn:
QR=[114991126314191326214391426][141014101401391209100826]=[123321212]=A\begin{align} \mathbf{Q} \mathbf{R} &= \begin{bmatrix} \frac{1}{\sqrt{14}} & \frac{9}{\sqrt{91}} & \frac{1}{\sqrt{26}} \\ \frac{3}{\sqrt{14}} & -\frac{1}{\sqrt{91}} & -\frac{3}{\sqrt{26}} \\ \frac{2}{\sqrt{14}} & -\frac{3}{\sqrt{91}} & \frac{4}{\sqrt{26}} \end{bmatrix} \begin{bmatrix} \sqrt{14} & \frac{10}{\sqrt{14}} & \frac{10}{\sqrt{14}} \\ 0 & \frac{13}{\sqrt{91}} & \frac{20}{\sqrt{91}} \\ 0 & 0 & \frac{8}{\sqrt{26}} \end{bmatrix} \notag \\ &= \begin{bmatrix} 1 & 2 & 3 \\ 3 & 2 & 1 \\ 2 & 1 & 2 \end{bmatrix} \notag \\ &= \mathbf{A} \\ \end{align}

Kiểm tra phép tính tại đây: Matrix Calculator

Triển khai code Python

Toàn bộ code có thể xem chi tiết tại: snowyfield1906/ai-general-research/least_squares/least_squares.py.

Thuật toán chính

def qr_decomposition(A: np.array) -> (np.array, np.array):
    m, n = A.shape

    # Orthogonalization
    V = np.empty((m, 0), float)
    for i in range(n):
        u_i = get(A, i)

        for j in range(i):
            v_j = get(V, j)
            u_i -= orthogonalize(u_i, v_j)

        v_i = u_i
        V = append(V, v_i)

    # Orthonormalization
    Q = np.empty((m, 0), float)
    for i in range(n):
        v_i = get(V, i)
        q_i = v_i / normalize(v_i)
        Q = append(Q, q_i)

    # Compute R
    R = Q.T @ A

    return Q, R

Các hàm phụ trợ

def inner_product(v1: np.array, v2: np.array) -> float:
    product = 0
    for i in range(len(v1)):
        product += v1[i] * v2[i]
    return product

def normalize(v: np.array) -> float:
    norm = 0
    for elem in v:
        norm += (elem ** 2)
    return norm ** 0.5

def orthogonalize(u: np.array, v: np.array) -> np.array:
    return (inner_product(u, v) * v) / (normalize(v) ** 2)

def get(A: np.array, i: int) -> np.array:
    a_i = A.copy()[:, i]
    return a_i

def append(A: np.array, v: np.array) -> np.array:
    return np.insert(A, len(A[0]), v, axis=1)

Kiểm thử

A = np.array([
    [1, 2, 3],
    [3, 2, 1],
    [2, 1, 2],
], dtype=float)

Q, R = qr_decomposition(A)
print("Q:", Q)
print("R:", R)
print("Q @ R:", Q @ R)
> Q: [[ 0.26726124  0.94345635  0.19611614]
 [ 0.80178373 -0.10482848 -0.58834841]
 [ 0.53452248 -0.31448545  0.78446454]]
> R: [[3.74165739e+00 2.67261242e+00 2.67261242e+00]
 [0.00000000e+00 1.36277029e+00 2.09656967e+00]
 [0.00000000e+00 1.11022302e-16 1.56892908e+00]]
> Q @ R: [[1. 2. 3.]
 [3. 2. 1.]
 [2. 1. 2.]]

Sử dụng thư viện numpy

import numpy as np

A = np.array([
    [1, 2, 3],
    [3, 2, 1],
    [2, 1, 2],
], dtype=float)

Q, R = np.linalg.qr(A)
print("Q:", Q)
print("R:", R)
print("Q @ R:", Q @ R)
> Q: [[-0.26726124  0.94345635  0.19611614]
 [-0.80178373 -0.10482848 -0.58834841]
 [-0.53452248 -0.31448545  0.78446454]]
> R: [[-3.74165739 -2.67261242 -2.67261242]
 [ 0.          1.36277029  2.09656967]
 [ 0.          0.          1.56892908]]
> Q @ R: [[1. 2. 3.]
 [3. 2. 1.]
 [2. 1. 2.]]