18.1. Các phép toán Hình học và Đại số Tuyến tính

Trong Section 2.3, chúng ta đã đề cập tới những kiến thức cơ bản về đại số tuyến tính và cách nó được dùng để thể hiện các phép biến đổi dữ liệu cơ bản. Đại số tuyến tính là một trong những trụ cột toán học chính hỗ trợ học sâu và rộng hơn là học máy. Dù Section 2.3 đề cập đủ kiến thức cần thiết để tìm hiểu các mô hình học sâu hiện đại, vẫn còn rất nhiều điều cần thảo luận trong lĩnh vực này. Trong mục này, chúng ta sẽ đi sâu hơn, nhấn mạnh một số diễn giải hình học của các phép toán đại số tuyến tính, và giới thiệu một vài khái niệm cơ bản, bao gồm trị riêng và vector riêng.

18.1.1. Ý nghĩa Hình học của Vector

Trước hết, chúng ta cần thảo luận hai diễn giải hình học phổ biến của vector: điểm hoặc hướng trong không gian. Về cơ bản, một vector là một danh sách các số giống như danh sách trong Python dưới đây:

v = [1, 7, 0, 1]

Các nhà toán học thường viết chúng dưới dạng một vector cột hoặc hàng, tức:

(18.1.1)\[\begin{split}\mathbf{x} = \begin{bmatrix}1\\7\\0\\1\end{bmatrix},\end{split}\]

hoặc

(18.1.2)\[\mathbf{x}^\top = \begin{bmatrix}1 & 7 & 0 & 1\end{bmatrix}.\]

Những biểu diễn này thường có những cách diễn giải khác nhau. Các mẫu dữ liệu được biểu diễn bằng các vector cột và các trọng số dùng để để tính các tổng có trọng số được biểu diễn bằng các vector hàng. Tuy nhiên, việc linh động sử dụng 2 cách biểu diễn này mang lại nhiều lợi ích. Như mô tả trong Section 2.3, dù cách biểu diễn mặc định của một vector đơn là theo cột, trong các ma trận biểu diễn các tập dữ liệu dạng bảng, các mẫu dữ liệu thường được coi như các vector hàng.

Cho trước một vector bất kỳ, cách hiểu thứ nhất là coi nó như một điểm trong không gian. Trong không gian hai hoặc ba chiều, chúng ta có thể biểu diễn điểm này bằng việc sử dụng các thành phần của vector để định nghĩa vị trí của điểm đó trong không gian so với một điểm tham chiếu được gọi là gốc tọa độ, như trong Fig. 18.1.1.

../_images/GridPoints.svg

Fig. 18.1.1 Mô tả việc biểu diễn vector như các điểm trong mặt phẳng. Thành phần thứ nhất của vector là tọa độ \(x\), thành phần thứ hai là tọa độ \(y\). Biểu diễn tương tự với vector nhiều chiều hơn, mặc dù khó hình dung hơn.

Góc nhìn hình học này cho phép chúng ta xem xét bài toán ở mức trừu tượng hơn. Không giống như khi đối mặt với các bài toán khó hình dung như phân loại ảnh chó mèo, chúng ta có thể bắt đầu xem xét các bài toán dạng này một cách trừu tượng hơn: cho một tập hợp các điểm trong không gian, hãy tìm cách phân biệt hai nhóm điểm riêng biệt.

Cách thứ hai để giải thích một vector là coi nó như một phương hướng trong không gian. Chúng ta không những có thể coi vector \(\mathbf{v} = [2,3]^\top\) là một điểm nằm bên phải \(2\) đơn vị và bên trên \(3\) đơn vị so với gốc tọa độ, chúng ta cũng có thể coi nó thể hiện một hướng – hướng về bên phải \(2\) đơn vị và hướng lên phía trên \(3\) đơn vị. Theo cách này, ta coi tất cả các vector trong Fig. 18.1.2 là như nhau.

../_images/ParVec.svg

Fig. 18.1.2 Bất kỳ vector nào cũng có thể biểu diễn bằng một mũi tên trong mặt phẳng. Trong trường hợp này, mọi vector trong hình đều biểu diễn vector \((3,2)^\top\).

Một trong những lợi ích của cách hiểu này là phép cộng vector có thể được hiểu theo nghĩa hình học. Cụ thể, chúng ta đi theo một hướng được cho bởi một vector, sau đó tiếp tục đi theo hướng cho bởi một vector khác, như trong Fig. 18.1.3.

../_images/VecAdd.svg

Fig. 18.1.3 Phép cộng vector có thể biểu diễn bằng cách đầu tiên đi theo một vector, sau đó đi theo vector kia.

Hiệu của hai vector có cách diễn giải tương tự. Bằng cách biểu diễn \(\mathbf{u} = \mathbf{v} + (\mathbf{u}-\mathbf{v})\), ta thấy rằng vector \(\mathbf{u}-\mathbf{v}\) là hướng mang điểm \(\mathbf{v}\) tới điểm \(\mathbf{u}\).

18.1.2. Tích vô hướng và Góc

Như đã thấy trong Section 2.3, tích vô hướng của hai vector cột \(\mathbf{u}\)\(\mathbf{v}\) có thể được tính như sau:

(18.1.3)\[\mathbf{u}^\top\mathbf{v} = \sum_i u_i\cdot v_i.\]

Vì biểu thức (18.1.3) là đối xứng, chúng ta có thể mượn ký hiệu của phép nhân truyền thống và viết:

(18.1.4)\[\mathbf{u}\cdot\mathbf{v} = \mathbf{u}^\top\mathbf{v} = \mathbf{v}^\top\mathbf{u},\]

để nhấn mạnh rằng việc đổi chỗ hai vector sẽ cho kết quả như nhau.

Tích vô hướng (18.1.3) cũng có một cách diễn giải hình học: nó liên quan mật thiết tới góc giữa hai vector. Hãy xem xét góc trong Fig. 18.1.4.

../_images/VecAngle.svg

Fig. 18.1.4 Luôn tồn tại một góc xác định (\(\theta\)) giữa hai vector bất kỳ trong không gian. Ta sẽ thấy rằng góc này có liên hệ chặt chẽ tới tích vô hướng.

Xét hai vector:

(18.1.5)\[\mathbf{v} = (r,0) \; \text{and} \; \mathbf{w} = (s\cos(\theta), s \sin(\theta)).\]

Vector \(\mathbf{v}\) có độ dài \(r\) và song song với trục \(x\), vector \(\mathbf{w}\) có độ dài \(s\) và tạo một góc \(\theta\) với trục \(x\). Nếu tính tích vô hướng của hai vector này, ta sẽ thấy rằng

(18.1.6)\[\mathbf{v}\cdot\mathbf{w} = rs\cos(\theta) = \|\mathbf{v}\|\|\mathbf{w}\|\cos(\theta).\]

Với một vài phép biến đổi đại số đơn giản, chúng ta có thể sắp xếp lại các thành phần để được

(18.1.7)\[\theta = \arccos\left(\frac{\mathbf{v}\cdot\mathbf{w}}{\|\mathbf{v}\|\|\mathbf{w}\|}\right).\]
Một cách ngắn gọn, với hai vector cụ thể này, tích vô hướng kết hợp với chuẩn (norm) cho ta góc giữa hai vector. Điều này cũng đúng trong trường hợp tổng quát.
Chúng tôi sẽ không suy ra biểu thức đó ở đây; tuy nhiên, nếu viết \(\|\mathbf{v} - \mathbf{w}\|^2\) bằng hai cách: cách thứ nhất với tích vô hướng, và cách thứ hai sử dụng công thức tính cô-sin, ta có thể thấy được quan hệ giữa chúng. Thật vậy, với hai vector \(\mathbf{v}\)\(\mathbf{w}\) bất kỳ, góc giữa chúng là
(18.1.8)\[\theta = \arccos\left(\frac{\mathbf{v}\cdot\mathbf{w}}{\|\mathbf{v}\|\|\mathbf{w}\|}\right).\]

Đây là một điều tốt vì trong công thức không hề chỉ định bất cứ điều gì đặc biệt về không gian hai chiều. Thật vậy, ta có thể sử dụng công thức này trong không gian ba chiều hoặc ba triệu chiều mà không gặp vấn đề gì.

Xét ví dụ đơn giản tính góc giữa cặp vector:

%matplotlib inline
from d2l import mxnet as d2l
from IPython import display
from mxnet import gluon, np, npx
npx.set_np()
def angle(v, w):
    return np.arccos(v.dot(w) / (np.linalg.norm(v) * np.linalg.norm(w)))
angle(np.array([0, 1, 2]), np.array([2, 3, 4]))
array(0.41899002)

Chúng ta sẽ không sử dụng đoạn mã này bây giờ, nhưng sẽ hữu ích để biết rằng nếu góc giữa hai vector là \(\pi/2\) (hay \(90^{\circ}\)) thì hai vector đó trực giao với nhau. Xem xét kỹ biểu thức trên, ta thấy rằng việc này xảy ra khi \(\theta = \pi/2\), tức \(\cos(\theta) = 0\). Điều này chứng tỏ tích vô hướng phải bằng không, và hai vector là trực giao khi và chỉ khi \(\mathbf{v}\cdot\mathbf{w} = 0\). Đẳng thức này sẽ hữu ích khi xem xét các đối tượng dưới con mắt hình học.

Ta sẽ tự hỏi tại sao việc tính góc lại hữu ích? Câu trả lời nằm ở tính bất biến ta mong đợi từ dữ liệu. Xét một tấm ảnh, và một tấm ảnh thứ hai giống hệt nhưng với các điểm ảnh với độ sáng chỉ bằng \(10\%\) ảnh ban đầu. Giá trị của từng điểm ảnh trong ảnh thứ hai nhìn chung khác xa so với ảnh ban đầu. Bởi vậy, nếu tính khoảng cách giữa ảnh ban đầu và ảnh tối hơn, giá trị này có thể rất lớn. Tuy nhiên, trong hầu hết các ứng dụng học máy, nội dung của hai tấm ảnh là như nhau – nó vẫn là tấm ảnh của một con mèo đối với một bộ phân loại chó mèo. Tiếp đó, nếu xem xét góc giữa hai ảnh, không khó để thấy rằng với vector \(\mathbf{v}\) bất kỳ, góc giữa \(\mathbf{v}\)\(0.1\cdot\mathbf{v}\) bằng không. Việc này tương ứng với việc nhân vector với một số (dương) đồng hướng và chỉ thay đổi độ dài của vector đó. Như vậy khi xét tới góc, hai tấm ảnh được xem là như nhau.

Ví dụ tương tự có thể tìm thấy bất cứ đâu. Trong văn bản, chúng ta có thể muốn chủ đề thảo luận không thay đổi cho dù tăng gấp đôi độ dài văn bản. Trong một số cách mã hóa (như đếm số lượng xuất hiệncủa một từ trong từ điển), việc này tương đương với nhân đôi vector mã hóa của văn bản, bởi vậy chúng ta lại có thể sử dụng góc.

18.1.2.1. Độ tương tự Cô-sin

Trong văn cảnh học máy với góc được dùng để đo lường khoảng cách giữa hai vector, người làm học máy sử dụng thuật ngữ độ tương tự cô-sin để chỉ đại lượng

(18.1.9)\[\cos(\theta) = \frac{\mathbf{v}\cdot\mathbf{w}}{\|\mathbf{v}\|\|\mathbf{w}\|}.\]

Hàm cô-sin có giá trị lớn nhất bằng \(1\) khi hai vector chỉ cùng một hướng, giá trị nhỏ nhất bằng \(-1\) khi chúng cùng phương nhưng ngược hướng, và \(0\) khi hai vector trực giao. Chú ý rằng nếu các thành phần của hai vector nhiều chiều được lấy mẫu ngẫu nhiên với kỳ vọng \(0\), cô-sin giữa chúng sẽ luôn gần với \(0\).

18.1.3. Siêu phẳng

Ngoài làm việc với vector, một đối tượng quan trọng khác bạn phải nắm vững khi đi sâu vào đại số tuyến tính là siêu phẳng, một khái niệm tổng quát của đường thẳng (trong không gian hai chiều) hoặc một mặt phẳng (trong không gian ba chiều). Trong một không gian vector \(d\) chiều, một siêu phẳng có \(d-1\) chiều và chia không gian thành hai nửa không gian.

Xét ví dụ sau. Giả sử ta có một vector cột \(\mathbf{w}=[2,1]^\top\). Ta muốn biết “những điểm \(\mathbf{v}\) nào thỏa mãn \(\mathbf{w}\cdot\mathbf{v} = 1\)?” Sử dụng mối quan hệ giữa tích vô hướng và góc ở (18.1.8) phía trên, ta có thể thấy điều này tương đương với

(18.1.10)\[\|\mathbf{v}\|\|\mathbf{w}\|\cos(\theta) = 1 \; \iff \; \|\mathbf{v}\|\cos(\theta) = \frac{1}{\|\mathbf{w}\|} = \frac{1}{\sqrt{5}}.\]
../_images/ProjVec.svg

Fig. 18.1.5 Nhắc lại trong lượng giác, chúng ta coi \(\|\mathbf{v}\|\cos(\theta)\) là độ dài hình chiếu của vector \(\mathbf{v}\) lên hướng của vector \(\mathbf{w}\)

Nếu xem xét ý nghĩa hình học của biểu thức này, chúng ta thấy rằng nó tương đương với việc độ dài hình chiếu của \(\mathbf{v}\) lên hướng của \(\mathbf{w}\) chính là \(1/\|\mathbf{w}\|\), như được biểu diễn trong Fig. 18.1.5. Tập hợp các điểm thỏa mãn điều kiện này là một đường thẳng vuông góc với vector \(\mathbf{w}\). Ta có thể tìm được phương trình của đường thẳng này là \(2x + y = 1\) hoặc \(y = 1 - 2x\).

Tiếp theo, nếu ta muốn biết tập hợp các điểm thỏa mãn \(\mathbf{w}\cdot\mathbf{v} > 1\) hoặc \(\mathbf{w}\cdot\mathbf{v} < 1\), ta có thể thấy rằng đây là những trường hợp mà hình chiếu của chúng lên \(\mathbf{w}\) lần lượt dài hơn hoặc ngắn hơn \(1/\|\mathbf{w}\|\). Vì thế, hai bất phương trình này định nghĩa hai phía của đường thẳng. Bằng cách này, ta có thể cắt mặt phẳng thành hai nửa: một nửa chứa tất cả các điểm có tích vô hướng nhỏ hơn một mức ngưỡng và nửa còn lại chứa những điểm có tích vô hướng lớn hơn mức ngưỡng đó, như trong hình Fig. 18.1.6.

../_images/SpaceDivision.svg

Fig. 18.1.6 Nếu nhìn từ dạng bất phương trình, ta thấy rằng siêu phẳng (trong trường hợp này là một đường thẳng) chia không gian ra thành hai nửa.

Câu chuyện trong không gian đa chiều cũng tương tự. Nếu lấy \(\mathbf{w} = [1,2,3]^\top\) và đi tìm các điểm trong không gian ba chiều với \(\mathbf{w}\cdot\mathbf{v} = 1\), ta có một mặt phẳng vuông góc với vector cho trước \(\mathbf{w}\). Hai bất phương trình một lần nữa định nghĩa hai phía của mặt phẳng như trong hình Fig. 18.1.7.

../_images/SpaceDivision3D.svg

Fig. 18.1.7 Siêu phẳng trong bất kỳ không gian nào chia không gian đó ra thành hai nửa.

Mặc dù không thể minh họa trong không gian nhiều chiều hơn, ta vẫn có thể tổng quát điều này cho không gian mười, một trăm hay một tỷ chiều. Việc này thường xuyên xảy ra khi nghĩ về các mô hình học máy. Chẳng hạn, ta có thể hiểu các mô hình phân loại tuyến tính trong Section 3.4 cũng giống như những phương pháp đi tìm siêu phẳng để phân chia các lớp mục tiêu khác nhau. Ở trường hợp này, những siêu phẳng như trên thường được gọi là các mặt phẳng quyết định. Phần lớn các mô hình phân loại tìm được qua học sâu đều kết thúc với một tầng tuyến tính và theo sau là một tầng softmax, bởi vậy ta có thể diễn giải ý nghĩa của mạng nơ-ron sâu giống như việc tìm một embedding phi tuyến sao cho các lớp mục tiêu có thể được phân chia bởi các siêu phẳng một cách gọn gàng.

Xét ví dụ sau. Để ý rằng, ta có thể tạo một mô hình đủ tốt để phân loại những tấm ảnh áo thun và quần với kích thước nhỏ từ tập dữ liệu Fashion MNIST (Xem Section 3.5) bằng cách lấy vector giữa điểm trung bình của mỗi lớp để định nghĩa một mặt phẳng quyết định và chọn thủ công một ngưỡng. Trước tiên, chúng ta nạp dữ liệu và tính hai ảnh trung bình:

# Load in the dataset
train = gluon.data.vision.FashionMNIST(train=True)
test = gluon.data.vision.FashionMNIST(train=False)

X_train_0 = np.stack([x[0] for x in train if x[1] == 0]).astype(float)
X_train_1 = np.stack([x[0] for x in train if x[1] == 1]).astype(float)
X_test = np.stack(
    [x[0] for x in test if x[1] == 0 or x[1] == 1]).astype(float)
y_test = np.stack(
    [x[1] for x in test if x[1] == 0 or x[1] == 1]).astype(float)

# Compute averages
ave_0 = np.mean(X_train_0, axis=0)
ave_1 = np.mean(X_train_1, axis=0)

Để có cái nhìn rõ hơn, ta có thể xem xét một cách chi tiết các ảnh trung bình này bằng cách in chúng ra màn hình. Quả thật, ảnh đầu tiên trông như một chiếc áo thun bị mờ.

# Plot average t-shirt
d2l.set_figsize()
d2l.plt.imshow(ave_0.reshape(28, 28).tolist(), cmap='Greys')
d2l.plt.show()
../_images/output_geometry-linear-algebraic-ops_vn_5ce91b_7_0.svg

Trong ảnh thứ hai, chúng ta cũng thấy ảnh trung bình chứa một chiếc quần dài bị mờ.

# Plot average trousers
d2l.plt.imshow(ave_1.reshape(28, 28).tolist(), cmap='Greys')
d2l.plt.show()
../_images/output_geometry-linear-algebraic-ops_vn_5ce91b_9_0.svg

Trong một lời giải học máy hoàn chỉnh thì mức ngưỡng cũng sẽ được học từ tập dữ liệu. Trong trường hợp này, ta chỉ đơn thuần chọn thủ công một ngưỡng mang lại kết quả khá tốt trên tập huấn luyện.

# Print test set accuracy with eyeballed threshold
w = (ave_1 - ave_0).T
predictions = X_test.reshape(2000, -1).dot(w.flatten()) > -1500000
# Accuracy
np.mean(predictions.astype(y_test.dtype) == y_test, dtype=np.float64)
array(0.801, dtype=float64)

18.1.4. Ý nghĩa Hình học của các Phép biến đổi Tuyến tính

Thông qua Section 2.3 và các phần thảo luận phía trên, ta đã có kiến thức vững chắc về ý nghĩa hình học của vector, độ dài, và góc. Tuy nhiên, có một đối tượng quan trọng chúng ta đã bỏ qua, đó là ý nghĩa hình học của các phép biến đổi tuyến tính thể hiện bởi các ma trận. Để hoàn toàn hiểu cách ma trận được dùng để biến đổi dữ liệu giữa hai không gian nhiều chiều khác nhau cần thực hành thường xuyên và nằm ngoài phạm vi của phần phụ lục này. Tuy nhiên, chúng ta có thể xây dựng các ý niệm trực quan trong không gian hai chiều.

Giả sử ta có một ma trận:

(18.1.11)\[\begin{split}\mathbf{A} = \begin{bmatrix} a & b \\ c & d \end{bmatrix}.\end{split}\]

Nếu muốn áp dụng ma trận này lên một vector \(\mathbf{v} = [x, y]^\top\) bất kỳ, ta thực hiện phép nhân và thấy rằng

(18.1.12)\[\begin{split}\begin{aligned} \mathbf{A}\mathbf{v} & = \begin{bmatrix}a & b \\ c & d\end{bmatrix}\begin{bmatrix}x \\ y\end{bmatrix} \\ & = \begin{bmatrix}ax+by\\ cx+dy\end{bmatrix} \\ & = x\begin{bmatrix}a \\ c\end{bmatrix} + y\begin{bmatrix}b \\d\end{bmatrix} \\ & = x\left\{\mathbf{A}\begin{bmatrix}1\\0\end{bmatrix}\right\} + y\left\{\mathbf{A}\begin{bmatrix}0\\1\end{bmatrix}\right\}. \end{aligned}\end{split}\]

Thoạt nhìn đây là một phép tính khá kỳ lạ, nó biến một thứ vốn rõ ràng trở nên khó hiểu. Tuy nhiên, nó cho thấy một ma trận có thể biến đổi bất kỳ vector nào bằng việc biến đổi hai vector cụ thể: \([1,0]^\top\)\([0,1]^\top\). Quan sát một chút, chúng ta thực tế đã thu gọn một bài toán vô hạn (tính toán cho bất kỳ vector nào) thành một bài toán hữu hạn (tính toán cho chỉ hai vector). Tập hợp hai vector này là ví dụ của một cơ sở (basis), và bất kì vector nào trong không gian đều có thể được biểu diễn dưới dạng tổng có trọng số của những vector cơ sở này.

Cùng xét ví dụ với một ma trận cụ thể

(18.1.13)\[\begin{split}\mathbf{A} = \begin{bmatrix} 1 & 2 \\ -1 & 3 \end{bmatrix}.\end{split}\]

Xét vector \(\mathbf{v} = [2, -1]^\top\), ta thấy rằng vector này có thể viết dưới dạng \(2\cdot[1,0]^\top + -1\cdot[0,1]^\top\). Bởi vậy ta biết ma trận \(A\) sẽ biến đổi nó thành \(2(\mathbf{A}[1,0]^\top) + -1(\mathbf{A}[0,1])^\top = 2[1, -1]^\top - [2,3]^\top = [0, -5]^\top\). Xét mạng lưới cấu thành từ tất cả các cặp điểm có tọa độ nguyên, ta có thể thấy rằng phép nhân ma trận có thể làm nghiêng, xoay và co giãn lưới đó, nhưng cấu trúc của lưới phải giữ nguyên như minh họa trong Fig. 18.1.8.

../_images/GridTransform.svg

Fig. 18.1.8 Ma trận \(\mathbf{A}\) biến đổi các vector cơ sở cho trước. Hãy để ý việc toàn bộ lưới cũng bị biến đổi theo.

Đây là điểm quan trọng nhất về các phép biến đổi tuyến tính thông qua ma trận mà ta cần phải tiếp thu. Một ma trận không thể làm biến dạng các phần không gian khác nhau theo các cách khác nhau. Chúng chỉ có thể làm nghiêng, xoay và co giãn các tọa độ ban đầu.

Một vài phép biển đổi có thể có ảnh hưởng rất lớn. Chẳng hạn ma trận

(18.1.14)\[\begin{split}\mathbf{B} = \begin{bmatrix} 2 & -1 \\ 4 & -2 \end{bmatrix},\end{split}\]

nén toàn bộ mặt phẳng hai chiều thành một đường thẳng. Việc nhận dạng và làm việc với các phép biến đổi này là chủ đề của phần sau, nhưng nhìn từ khía cạnh hình học, ta có thể thấy rằng nó khác hẳn so với các phép biến đổi ở trên. Ví dụ, kết quả từ ma trận \(\mathbf{A}\) có thể bị “biến đổi lại” thành dạng ban đầu. Kết quả từ ma trận \(\mathbf{B}\) thì không thể vì ta không biết vector \([1,2]^\top\) được biến đổi từ vector nào – \([1,1]^\top\) hay \([0, -1]^\top\)?

Dù câu chuyện vừa rồi là về ma trận \(2\times2\), ta hoàn toàn có thể tổng quát hóa những kiến thức vừa học vào các không gian nhiều chiều hơn. Nếu chúng ta lấy các vector cơ sở như \([1,0, \ldots,0]\) và xem cách ma trận đó biến đổi các vector này, ta có thể phần nào hình dung được việc phép nhân ma trận làm biến dạng toàn bộ không gian như thế nào, bất kể số chiều của không gian đó.

18.1.5. Phụ thuộc Tuyến tính

Quay lại với ma trận

(18.1.15)\[\begin{split}\mathbf{B} = \begin{bmatrix} 2 & -1 \\ 4 & -2 \end{bmatrix}.\end{split}\]

Ma trận này nén toàn bộ mặt phẳng xuống thành một đường thằng \(y = 2x\). Câu hỏi đặt ra là: có cách nào phát hiện ra điều này nếu chỉ nhìn vào ma trận không? Câu trả lời tất nhiên là có. Đặt \(\mathbf{b}_1 = [2,4]^\top\)\(\mathbf{b}_2 = [-1, -2]^\top\) là hai cột của \(\mathbf{B}\). Nhắc lại rằng chúng ta có thể biểu diễn bất cứ vector nào được biến đổi bởi ma trận \(\mathbf{B}\) dưới dạng tổng có trọng số của các cột trong ma trận này: \(a_1\mathbf{b}_1 + a_2\mathbf{b}_2\). Tổng này được gọi là tổ hợp tuyến tính (linear combination). Vì \(\mathbf{b}_1 = -2\cdot\mathbf{b}_2\), ta có thể biểu diễn tổ hợp bất kỳ của hai cột này mà chỉ dùng \(\mathbf{b}_2\):

(18.1.16)\[a_1\mathbf{b}_1 + a_2\mathbf{b}_2 = -2a_1\mathbf{b}_2 + a_2\mathbf{b}_2 = (a_2-2a_1)\mathbf{b}_2.\]

Điều này chỉ ra rằng một trong hai cột là dư thừa vì nó không định nghĩa một hướng độc nhất trong không gian. Việc này cũng không quá bất ngờ bởi ma trận này đã biến toàn bộ mặt phẳng xuống thành một đường thẳng. Hơn nữa, điều này có thể được nhận thấy do hai cột trên phụ thuộc tuyến tính \(\mathbf{b}_1 = -2\cdot\mathbf{b}_2\). Để thấy sự đối xứng giữa hai vector này, ta sẽ viết dưới dạng

(18.1.17)\[\mathbf{b}_1 + 2\cdot\mathbf{b}_2 = 0.\]
Nhìn chung, ta nói một tập hợp các vector \(\mathbf{v}_1, \ldots, \mathbf{v}_k\)
phụ thuộc tuyến tính nếu tồn tại các hệ số \(a_1, \ldots, a_k\) không đồng thời bằng không sao cho
(18.1.18)\[\sum_{i=1}^k a_i\mathbf{v_i} = 0.\]

Trong trường hợp này, ta có thể biểu diễn một vector dưới dạng một tổ hợp nào đó của các vector khác, khiến cho nó trở nên dư thừa. Bởi vậy, sự phụ thuộc tuyến tính giữa các cột của một ma trận là một bằng chứng cho thấy ma trận đó đang làm giảm số chiều không gian. Nếu không có sự phụ thuộc tuyến tính, chúng ta nói rằng các vector này độc lập tuyến tính (linearly independent). Nếu các cột của một ma trận là độc lập tuyến tính, việc nén sẽ không xảy ra và phép toán này có thể nghịch đảo.

18.1.6. Hạng

Với một ma trận tổng quát \(n\times m\), câu hỏi tự nhiên được đặt ra là ma trận đó ánh xạ vào không gian bao nhiêu chiều. Để trả lời cho câu hỏi này, ta dùng khái niệm hạng (rank). Trong mục trước, ta thấy một hệ phụ thuộc tuyến tính nén không gian xuống một không gian khác có số chiều ít hơn. Chúng ta sẽ sử dụng tính chất này để định nghĩa hạng. Cụ thể, hạng của một ma trận \(\mathbf{A}\) là số lượng cột độc lập tuyến tính lớn nhất trong mọi tập con các cột của ma trận đó. Ví dụ, ma trận

(18.1.19)\[\begin{split}\mathbf{B} = \begin{bmatrix} 2 & 4 \\ -1 & -2 \end{bmatrix},\end{split}\]

\(\mathrm{rank}(B)=1\) vì hai cột của nó phụ thuộc tuyến tính và mỗi cột đơn lẻ không phụ thuộc tuyến tính. Xét một ví dụ phức tạp hơn

(18.1.20)\[\begin{split}\mathbf{C} = \begin{bmatrix} 1& 3 & 0 & -1 & 0 \\ -1 & 0 & 1 & 1 & -1 \\ 0 & 3 & 1 & 0 & -1 \\ 2 & 3 & -1 & -2 & 1 \end{bmatrix},\end{split}\]
Ta có thể chứng minh được \(\mathbf{C}\) có hạng bằng hai, bởi hai cột đầu tiên là độc lập tuyến tính,
trong khi tập hợp ba cột bất kỳ trong ma trận đều phụ thuộc tuyến tính.

Quá trình trên rất không hiệu quả, vì đòi hỏi xét mọi tập con các cột của một ma trận cho trước, số tập con này tăng theo hàm mũ khi số cột tăng lên. Sau này chúng ta sẽ thấy một cách hiệu quả hơn để tính hạng của ma trận, hiện tại định nghĩa trên là đủ để hiểu khái niệm và ý nghĩa của hạng.

18.1.7. Tính nghịch đảo (khả nghịch)

Như chúng ta đã thấy ở trên, phép nhân một ma trận có các cột phụ thuộc tuyến tính là không thể hoàn tác, tức là không tồn tại thao tác nghịch đảo nào có thể khôi phục lại đầu vào. Tuy nhiên, trong phép biến đổi bằng một ma trận có hạng đầy đủ (ví dụ, với ma trận \(\mathbf{A}\) nào đó kích thước \(n \times n\) có hạng \(n\)), ta luôn có thể hoàn tác nó. Xét ma trận

(18.1.21)\[\begin{split}\mathbf{I} = \begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix}.\end{split}\]

đây là ma trận với các phần tử trên đường chéo có giá trị 1 và các phẩn tử còn lại có giá trị 0. Ma trận này được gọi là ma trận đơn vị (identity matrix). Dữ liệu sẽ không bị thay đổi khi nhân với ma trận này. Để có một ma trận hoàn tác những gì ma trận \(\mathbf{A}\) đã làm, ta tìm một ma trận \(\mathbf{A}^{-1}\) sao cho

(18.1.22)\[\mathbf{A}^{-1}\mathbf{A} = \mathbf{A}\mathbf{A}^{-1} = \mathbf{I}.\]

Nếu xem đây là một hệ phương trình, ta có \(n \times n\) biến (các giá trị của \(\mathbf{A}^{-1}\)) và \(n \times n\) phương trình (đẳng thức cần thỏa mãn giữa mỗi giá trị của tích \(\mathbf{A}^{-1}\mathbf{A}\) và giá trị tương ứng của \(\mathbf{I}\)) nên nhìn chung hệ phương trình có nghiệm. Thật vậy, phần tiếp theo sẽ giới thiệu một đại lượng được gọi là định thức (determinant) với tính chất: nghiệm tồn tại khi đại lượng này khác 0. Ma trận \(\mathbf{A}^{-1}\) như vậy được gọi là ma trận nghịch đảo. Ví dụ, nếu \(\mathbf{A}\) là ma trận \(2 \times 2\)

(18.1.23)\[\begin{split}\mathbf{A} = \begin{bmatrix} a & b \\ c & d \end{bmatrix},\end{split}\]

thì nghịch đảo của ma trận này là

(18.1.24)\[\begin{split} \frac{1}{ad-bc} \begin{bmatrix} d & -b \\ -c & a \end{bmatrix}.\end{split}\]

Việc này có thể kiểm chứng bằng công thức ma trận nghịch đảo trình bày ở trên.

M = np.array([[1, 2], [1, 4]])
M_inv = np.array([[2, -1], [-0.5, 0.5]])
M_inv.dot(M)
array([[1., 0.],
       [0., 1.]])

18.1.7.1. Vấn đề Tính toán

Mặc dù ma trận nghịch đảo khá hữu dụng trong lý thuyết, chúng ta nên tránh sử dụng chúng khi giải quyết các bài toán thực tế. Nhìn chung, có rất nhiều phương pháp tính toán ổn định hơn trong việc giải các phương trình tuyến tính dạng

(18.1.25)\[\mathbf{A}\mathbf{x} = \mathbf{b},\]

so với việc tính ma trận nghịch đảo và thực hiện phép nhân để có

(18.1.26)\[\mathbf{x} = \mathbf{A}^{-1}\mathbf{b}.\]

Giống như việc thực hiện phép chia một số nhỏ có thể dẫn đến sự mất ổn định tính toán, việc nghịch đảo một ma trận có hạng thấp cũng có ảnh hưởng tương tự.

Thêm vào đó, thông thường \(\mathbf{A}\) là ma trận thưa (sparse), có nghĩa là nó chỉ chứa một số lượng nhỏ các số khác 0. Nếu thử một vài ví dụ, chúng ta có thể thấy điều này không có nghĩa ma trận nghịch đảo cũng là một ma trận thưa. Kể cả khi ma trận A là ma trận \(1\) triệu nhân \(1\) triệu với chỉ \(5\) triệu giá trị khác 0 (có nghĩa là chúng ta chỉ cần lưu trữ \(5\) triệu giá trị đó), ma trận nghịch đảo thông thường vẫn giữ lại các thành phần không âm và đòi hỏi chúng ta phải lưu trữ \(1\text{M}^2\) phần tử—tương đương với \(1\) nghìn tỉ phần tử!

Mặc dù không đủ thời gian để đi sâu vào các vấn đề tính toán phức tạp thường gặp khi làm việc với đại số tuyến tính, chúng tôi vẫn mong muốn có thể cung cấp một vài lưu ý quan trọng, và quy tắc chung trong thực tiễn là hạn chế việc tính nghịch đảo.

18.1.8. Định thức

Góc nhìn hình học của đại số tuyến tính cung cấp một cách hiểu trực quan về một đại lượng cơ bản được gọi là định thức. Xét lưới không gian trong phần trước với một vùng in đậm (Fig. 18.1.9).

../_images/GridTransformFilled.svg

Fig. 18.1.9 Ma trận \(\mathbf{A}\) vẫn làm biến dạng lưới. Lần này, tôi muốn dồn sự chú ý vào điều đã xảy ra với hình vuông được tô màu.

Cùng nhìn vào hình vuông được tô màu, nó có diện tích bằng một với các cạnh được tạo bởi \((0, 1)\)\((1, 0)\). Sau khi ma trận \(\mathbf{A}\) biến đổi hình vuông này, ta thấy rằng nó trở thành một hình bình hành. Không có lý do nào để hình bình hành này có cùng diện tích với hình vuông ban đầu. Ví dụ, với ma trận

(18.1.27)\[\begin{split}\mathbf{A} = \begin{bmatrix} 1 & 2 \\ -1 & 3 \end{bmatrix},\end{split}\]

bạn có thể tính được diện tích hình bình hành bằng \(5\) như một bài tập hình học tọa độ đơn giản.

Tổng quát, với:

(18.1.28)\[\begin{split}\mathbf{A} = \begin{bmatrix} a & b \\ c & d \end{bmatrix},\end{split}\]

ta có thể tính ra diện tích của hình bình hành là \(ad-bc\). Diện tích này được coi là định thức.

Cùng kiểm tra nhanh điều này với một đoạn mã ví dụ.

import numpy as np
np.linalg.det(np.array([[1, -1], [2, 3]]))
5.000000000000001

Bạn đọc tinh mắt có thể nhận ra biểu thức này có thể bằng không hoặc thậm chí âm. Khi biểu thức này âm, đó là quy ước toán học thường dùng: nếu ma trận đó “lật” một hình, nó sẽ đảo dấu diện tích hình đó. Còn khi định thức bằng không thì sao?

Xét

(18.1.29)\[\begin{split}\mathbf{B} = \begin{bmatrix} 2 & 4 \\ -1 & -2 \end{bmatrix}.\end{split}\]

Định thức của ma trận này là \(2\cdot(-2 ) - 4\cdot(-1) = 0\). Điều này là hợp lý bởi ma trận \(\mathbf{B}\) đã nén hình vuông ban đầu xuống thành một đoạn thẳng với diện tích bằng không. Thật vậy, nén không gian xuống ít chiều hơn là cách duy nhất để có diện tích bằng không sau phép biến đổi. Do đó chúng ta suy ra được hệ quả sau: ma trận \(A\) khả nghịch khi và chỉ khi nó có định thức khác không.

Hãy tưởng tượng ta có một hình bất kỳ trên mặt phẳng. Ta có thể chia nhỏ hình này thành một tập hợp các hình vuông nhỏ, như vậy diện tích hình đó sẽ bằng tổng diện tích các hình vuông nhỏ. Bây giờ nếu ta biến đổi hình đó bằng một ma trận, các hình vuông nhỏ sẽ được biến đổi thành các hình bình hành với diện tích bằng với định thức của ma trận. Ta thấy rằng với một hình bất kỳ, định thức của một ma trận là hệ số co dãn diện tích (có dấu) của hình đó gây ra bởi ma trận.

Việc tính định thức cho các ma trận lớn có thể phức tạp hơn, nhưng ý tưởng là như nhau. Định thức giữ nguyên tính chất rằng ma trận \(n\times n\) co giãn các khối thể tích trong không gian \(n\) chiều.

18.1.9. Tensor và các Phép toán Đại số Tuyến tính thông dụng

Khái niệm về tensor đã được giới thiệu ở Section 2.3. Trong mục này, chúng ta sẽ đi sâu hơn vào phép co tensor (tương đương với phép nhân ma trận), và xem chúng cung cấp cái nhìn nhất quán về một số phép toán ma trận và vector như thế nào.

Chúng ta đã biết biến đổi dữ liệu bằng cách nhân với ma trận và vector. Để tensor trở nên hữu ích, ta cần một định nghĩa tương tự như thế. Xem lại phép nhân ma trận:

(18.1.30)\[\mathbf{C} = \mathbf{A}\mathbf{B},\]

tương đương với:

(18.1.31)\[c_{i, j} = \sum_{k} a_{i, k}b_{k, j}.\]

Cách thức biểu diễn này có thể lặp lại với tensor. Với tensor, không có thứ tự tổng quát để chọn tính tổng theo chỉ số nào. Bởi vậy, cần chỉ ra chính xác ta muốn tính tổng trên chỉ số nào. Ví dụ, xét:

(18.1.32)\[y_{il} = \sum_{jk} x_{ijkl}a_{jk}.\]

Phép biến đổi này được gọi là một phép co tensor (tensor contraction). Nó có thể biểu diễn được các phép biến đổi một cách linh động hơn nhiều so với phép nhân ma trận đơn thuần.

Để đơn giản cho việc ký hiệu, ta có thể để ý rằng tổng chỉ được tính theo những chỉ số xuất hiện nhiều hơn một lần trong biểu thức. Bởi vậy, người ta thường làm việc với ký hiệu Einstein với quy ước rằng phép tính tổng sẽ được lấy trên các chỉ số xuất hiện lặp lại. Từ đó, ta có một phép biểu diễn ngắn gọn:

(18.1.33)\[y_{il} = x_{ijkl}a_{jk}.\]

18.1.9.1. Một số Ví dụ thông dụng trong Đại số Tuyến tính

Hãy xem ta có thể biểu diễn bao nhiêu khái niệm đại số tuyến tính đã biết với biểu thức tensor thu gọn này:

  • \(\mathbf{v} \cdot \mathbf{w} = \sum_i v_iw_i\)
  • \(\|\mathbf{v}\|_2^{2} = \sum_i v_iv_i\)
  • \((\mathbf{A}\mathbf{v})_i = \sum_j a_{ij}v_j\)
  • \((\mathbf{A}\mathbf{B})_{ik} = \sum_j a_{ij}b_{jk}\)
  • \(\mathrm{tr}(\mathbf{A}) = \sum_i a_{ii}\)

Với cách này, ta có thể thay thế hàng loạt ký hiệu chi tiết bằng những biểu diễn tensor ngắn.

18.1.9.2. Biểu diễn khi Lập trình

Tensor cũng có thể được thao tác linh hoạt dưới dạng mã. Như trong Section 2.3, ta có thể tạo các tensor như sau.

# Define tensors
B = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
A = np.array([[1, 2], [3, 4]])
v = np.array([1, 2])
# Print out the shapes
A.shape, B.shape, v.shape
((2, 2), (2, 2, 3), (2,))

Phép tính tổng Einstein đã được lập trình sẵn và có thể sử dụng một cách trực tiếp. Các chỉ số xuất hiện trong phép tổng Einstein có thể được truyền vào dưới dạng chuỗi ký tự, theo sau là những tensor cần thao tác. Ví dụ, để thực hiện phép nhân ma trận, ta có thể sử dụng phép tổng Einstein ở trên (\(\mathbf{A}\mathbf{v} = a_{ij}v_j\)) và tách riêng các chỉ số như sau:

# Reimplement matrix multiplication
np.einsum("ij, j -> i", A, v), A.dot(v)
(array([ 5, 11]), array([ 5, 11]))

Đây là một ký hiệu cực kỳ linh hoạt. Giả sử ta muốn tính toán một phép tính thường được ghi một cách truyền thống là

(18.1.34)\[c_{kl} = \sum_{ij} \mathbf{b}_{ijk}\mathbf{a}_{il}v_j.\]

nó có thể được thực hiện thông qua phép tổng Einstein như sau:

np.einsum("ijk, il, j -> kl", B, A, v)
array([[ 90, 126],
       [102, 144],
       [114, 162]])

Cách ký hiệu này vừa dễ đọc và hiệu quả cho chúng ta, tuy nhiên lại khá rườm rà nếu ta cần tạo ra một phép co tensor tự động bằng cách lập trình. Vì lý do này, einsum có một cách ký hiệu thay thế bằng cách cung cấp các chỉ số nguyên cho mỗi tensor. Ví dụ, cùng phép co tensor ở trên có thể viết lại như sau:

np.einsum(B, [0, 1, 2], A, [0, 3], v, [1], [2, 3])
array([[ 90, 126],
       [102, 144],
       [114, 162]])

Cả hai cách ký hiệu đều biểu diễn phép co tensor một cách chính xác và hiệu quả.

18.1.10. Tóm tắt

  • Về phương diện hình học, vector có thể được hiểu như là điểm hoặc hướng trong không gian.
  • Tích vô hướng định nghĩa khái niệm góc trong không gian đa chiều bất kỳ.
  • Siêu phẳng (hyperplane) là sự khái quát hóa của đường thẳng và mặt phẳng trong không gian đa chiều. Chúng có thể được dùng để định nghĩa các mặt phẳng quyết định dùng trong bước cuối cùng của bài toán phân loại.
  • Ta có thể hiểu phép nhân ma trận theo cách hình học là việc biến đổi một cách đồng nhất các hệ tọa độ. Cách biểu diễn sự biến đổi vector này tuy có nhiều hạn chế nhưng lại gọn gàng về mặt toán học.
  • Phụ thuộc tuyến tính cho biết khi một tập các vector tồn tại trong một không gian ít chiều hơn so với dự kiến (chẳng hạn bạn có \(3\) vector nhưng chúng chỉ nằm trong không gian \(2\) chiều). Hạng của ma trận là số lượng cột độc lập tuyến tính lớn nhất trong ma trận đó.
  • Khi phép nghịch đảo của một ma trận là xác định, việc nghịch đảo ma trận cho phép chúng ta tìm một ma trận khác giúp hoàn tác lại thao tác trước đó. Việc nghịch đảo ma trận hữu dụng trong lý thuyết, nhưng yêu cầu cẩn trọng khi sử dụng vì tính bất ổn định số học (numerical instability) của nó.
  • Định thức cho phép đo lường mức độ một ma trận làm co dãn không gian. Một ma trận là khả nghịch khi và chỉ khi định thức của nó khác không.
  • Phép co tensor và phép lấy tổng Einstein cho ta cách biểu diễn gọn gàng và súc tích các phép toán thường gặp trong học máy.

18.1.11. Bài tập

  1. Góc giữa hai vectors dưới đây là bao nhiêu?
(18.1.35)\[\begin{split}\vec v_1 = \begin{bmatrix} 1 \\ 0 \\ -1 \\ 2 \end{bmatrix}, \qquad \vec v_2 = \begin{bmatrix} 3 \\ 1 \\ 0 \\ 1 \end{bmatrix}?\end{split}\]
  1. \(\begin{bmatrix}1 & 2\\0&1\end{bmatrix}\)\(\begin{bmatrix}1 & -2\\0&1\end{bmatrix}\) là nghịch đảo của nhau, đúng hay sai?
  1. Giả sử ta vẽ một hình trong mặt phẳng với diện tích \(100\mathrm{m}^2\). Diện tích hình đó sẽ bằng bao nhiêu sau khi biến đổi nó với ma trận
(18.1.36)\[\begin{split}\begin{bmatrix} 2 & 3\\ 1 & 2 \end{bmatrix}.\end{split}\]
  1. Trong các nhóm vector sau, nhóm nào là độc lập tuyến tính?
  • \(\left\{\begin{pmatrix}1\\0\\-1\end{pmatrix}, \begin{pmatrix}2\\1\\-1\end{pmatrix}, \begin{pmatrix}3\\1\\1\end{pmatrix}\right\}\)
  • \(\left\{\begin{pmatrix}3\\1\\1\end{pmatrix}, \begin{pmatrix}1\\1\\1\end{pmatrix}, \begin{pmatrix}0\\0\\0\end{pmatrix}\right\}\)
  • \(\left\{\begin{pmatrix}1\\1\\0\end{pmatrix}, \begin{pmatrix}0\\1\\-1\end{pmatrix}, \begin{pmatrix}1\\0\\1\end{pmatrix}\right\}\)
  1. Giả sử ta có ma trận \(A = \begin{bmatrix}c\\d\end{bmatrix}\cdot\begin{bmatrix}a & b\end{bmatrix}\) với các giá trị \(a, b, c\), và \(d\) nào đó. Ma trận đó luôn có định thức bằng \(0\), đúng hay sai?
  1. Các vector \(e_1 = \begin{bmatrix}1\\0\end{bmatrix}\)\(e_2 = \begin{bmatrix}0\\1\end{bmatrix}\) là trực giao. Ma trận \(A\) cần thỏa mãn điều kiện gì để \(Ae_1\)\(Ae_2\) trực giao?
  1. Viết \(\mathrm{tr}(\mathbf{A}^4)\) theo cách biểu diễn Einstein như thế nào với ma trận \(A\)? tùy ý?

18.1.12. Thảo luận

18.1.12.1. Những người thực hiện

Bản dịch trong trang này được thực hiện bởi:

  • Đoàn Võ Duy Thanh
  • Vũ Hữu Tiệp
  • Lê Khắc Hồng Phúc
  • Hoàng Trọng Tuấn
  • Nguyễn Cảnh Thướng
  • Nguyễn Xuân Tú
  • Phạm Hồng Vinh
  • Trần Thị Hồng Hạnh
  • Nguyễn Lê Quang Nhật
  • Mai Sơn Hải
  • Nguyễn Văn Cường