Xử Đẹp Ma Trận Cơ Bản Với Thư Viện NumPy

Xử Đẹp Ma Trận Cơ Bản Với Thư Viện NumPy

Với những người đang học và làm về Machine Learning, có lẽ việc phải thao tác với ma trận là không thể tránh khỏi khi phải xử lý với những bộ dữ liệu rất lớn, nhiều hàng nhiều cột. Việc áp dụng các kiến thức về ma trận là một công cụ vô cùng mạnh mẽ giúp xử lý số liệu nhanh chóng và chính xác.

Trong bài viết này, mình cùng các bạn sẽ ôn lại một chút kiến thức cơ bản về ma trận đã học trong môn Đại số tuyến tính, cũng như các hàm xử lý ma trận bằng thư viện Numpy của Python. Mình cũng sẽ cố gắng gửi đến các bạn những kiến thức toán học dễ hiểu nhất có thể, tránh đi tính hàn lâm và khô khan để ai cũng có thể dễ dàng tiếp cận được.

Cài đặt thư viện Numpy

Để cài đặt Numpy, hãy mở Command Prompt lên và gõ: 

pip install numpy

Đối với các bạn sử dụng IDE là Pycharm, có một cách khác để cài đặt, đó là mở Setting -> Python Interpreter -> Nhấn vào dấu cộng và tải thư viện Numpy về máy nha.

Để sử dụng thư viện Numpy, ta tiến hành khai báo như sau:

import numpy as np

Ma trận trong Numpy

Ma trận A là một bảng dữ liệu gồm m hàng và n cột. Trong Numpy, người ta dùng một mảng ndarray hai chiều để biểu diễn một ma trận. Hay nói cách khác, ma trận trong Numpy là mảng của các vecto một chiều thông thường.

Trong Numpy, ta dùng cú pháp như sau để khai báo một ma trận:

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr)

Kết quả thu được sẽ là:

[[1 2 3]
 [4 5 6]
 [7 8 9]]

Để truy cập vào từng phần tử của ma trận, ta chỉ cần dựa vào chỉ số của phần tử đó như với mảng trong các ngôn ngữ lập trình khác thôi:

print(arr[1, 2])

Kết quả trả về: 

6

Để trả về kích cỡ của ma trận, ta dùng hàm shape. Cần chú ý rằng kiểu dữ liệu trả về ở đây là tuples.

arr = np.array([[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10]])
print(arr.shape)

Kết quả sẽ là

(3, 4)

Các phép toán với ma trận

1. Cộng/ trừ hai ma trận

Để cộng hay trừ hai ma trận cho nhau, ta tiến hành cộng hay trừ các phần tử tương ứng của hai ma trận đó. Nhớ rằng các ma trận phải có cùng kích cỡ mới có thể thực hiện được nha.

Trong python, ta có thể tiến hành cộng trừ hai ma trận như sau:

A = np.array([[1, 3, 4], [-2, 6, 0], [-5, 7, 2]])
B = np.array([[2, 3, 4], [-1, -2, -3], [0, 4, -4]])
print("A + B = \n", A + B)
print("A - B = \n", A - B)

Kết quả thu được là:

A + B = 
 [[ 3  6  8]
 [-3  4 -3]
 [-5 11 -2]]
A - B = 
 [[-1  0  0]
 [-1  8  3]
 [-5  3  6]]

2. Phép nhân/ chia ma trận với một số

Để nhân/ chia ma trận với một số, ta chỉ cần nhân/ chia từng phần tử của ma trận với số đó thôi. Trong Python có thể thực hiện như sau:

A = np.array([[1, 3, 4], [-2, 6, 0], [-5, 7, 2]])
print("A * 3 = \n", A * 3)
print("A  / 4 = \n", A / 4)

Và kết quả thu được như sau:

A * 3 = 
 [[  3   9  12]
 [ -6  18   0]
 [-15  21   6]]
A  / 4 = 
 [[ 0.25  0.75  1.  ]
 [-0.5   1.5   0.  ]
 [-1.25  1.75  0.5 ]]

3. Phép nhân hai ma trận

Phép nhân hai ma trận không giống như nhân ma trận với một số, không phải là lấy phần tử của ma trận này nhân với phần tử tương ứng của ma trận kia. Một cách tổng quát nhất, phép nhân hai ma trận được thể hiện như sau:

Ta cần chú ý rằng thứ tự của phép nhân hai ma trận rất quan trọng, chỉ có thể thực hiện được nếu số cột của ma trận thứ nhất bằng số hàng của ma trận thứ hai, và ma trận trả về có số hàng bằng số hàng của ma trận thứ nhất và số cột bằng số cột của ma trận thứ hai. Phép nhân hai ma trận cũng không có tính chất giao hoán, nghĩa là A.B không bằng B.A

Trong Numpy, ta có thể dùng toán tử @ tiến hành nhân hai ma trận như sau:

A = np.array([[1, 3, 4], [-2, 6, 0], [-5, 7, 2]])
B = np.array([[2, 3, 4], [-1, -2, -3], [0, 4, -4]])
print("A * B = \n", A @ B)
print("B * A = \n", B @ A)

Ngoài toán tử @, ta có thể nhân hai ma trận bằng cách như sau:

print("A * B = \n", A.dot(B))
print("B * A = \n", B.dot(A))

Kết quả thu được đều sẽ là:

A * B = 
 [[ -1  13 -21]
 [-10 -18 -26]
 [-17 -21 -49]]
B * A = 
 [[-24  52  16]
 [ 18 -36 -10]
 [ 12  -4  -8]]

Tuy vậy, Python vẫn hỗ trợ nếu như ta muốn lấy phần tử của ma trận này nhân với phần tử tương ứng của ma trận kia qua toán tử * thông thường

A = np.array([[1, 3, 4], [-2, 6, 0], [-5, 7, 2]])
B = np.array([[2, 3, 4], [-1, -2, -3], [0, 4, -4]])
print(A * B)

Kết quả trả về là:

[[  2   9  16]
 [  2 -12   0]
 [  0  28  -8]]

Với các phép toán đã đề cập ở trên, ta cần chú ý một vài tính chất sau đây để tránh nhầm lẫn:

Ma trận đơn vị

Giống như trong tập số thực ta có phần tử đơn vị là 1, thì với ma trận, ta cũng có ma trận đơn vị. Ma trận đơn vị là ma trận vuông (Ma trận có số hàng bằng số cột) có các phần tử trên đường chéo chính bằng 1, các phần tử còn lại bằng 0. Như vậy, ma trận đơn vị cấp n sẽ bao gồm n hàng và n cột.

Ma trận đơn vị có tính chất như sau:

Ta thử khai báo một ma trận đơn vị cấp 3 trong Numpy như sau:

arr = np.eye(3)
print(arr)

Kết quả thu được là:

[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

Ma trận đường chéo

Ma trận đường chéo là ma trận vuông cấp n thỏa mãn mọi phần tử không nằm trên đường chéo chính đều bằng 0

Ta có thể khai báo một ma trận đường chéo theo cách sau đây:

A = np.diag([1, 2, 3])
print(A)

Kết quả thu được là:

[[1 0 0]
 [0 2 0]
 [0 0 3]]

Ngoài ra, ta cũng có thể lấy ra các phần tử nằm trên đường chéo chính của ma trận bằng cách sau:

A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(np.diag(A))

Kết quả trả về là:

[1 5 9]

Ma trận chuyển vị

Hiểu một cách đơn giản, thì ma trận B được gọi là ma trận chuyển vị của ma trận A, nếu các hàng của ma trận B là các cột của ma trận A (hoặc các cột của ma trận B là các hàng của ma trận A).Như vậy, một ma trận có m hàng và n cột, khi chuyển vị sẽ thành ma trận mới có n hàng và m cột. Ta có thể xem qua ví dụ dưới đây để hiểu rõ hơn:

Tất nhiên, ma trận nào cũng có thể chuyển vị được. Chuyển vị của ma trận đơn vị sẽ bằng chính nó.

Trong Python, ta có 2 cách sau để khởi tạo một ma trận chuyển vị:

A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(A.T)
print(np.transpose(A))

Cả 2 cách đều ra cùng một kết quả

[[1 4 7]
 [2 5 8]
 [3 6 9]]

Một vài tính chất của ma trận chuyển vị như sau:

Định thức của ma trận vuông

Định thức của ma trận vuông A, kí hiệu là det(A) (định thức là một số thực cụ thể chứ không phải là ma trận), được định nghĩa dần dần như sau:

Với A là ma trận cấp n, ta có công thức tính det(A) như sau:

với M là ma trận thu được bằng cách bỏ đi hàng i và cột j của ma trận A. Như vậy, ta có thể thấy, bản chất của công thức tính định thức chính là việc quy nạp tính các định thức của ma trận có cấp nhỏ hơn. Nếu định thức của một ma trận bằng 0, người ta gọi ma trận này là ma trận suy biến. Và ngược lại, nếu định thức khác 0 thì ta sẽ có ma trận không suy biến.

Numpy có hỗ trợ một hàm tính gần đúng định thức, rất tiện phải không nào, ta không phải tính một cách loằng ngoằng như trên nữa.

A = np.array([[2, 1, 3], [5, 3, 2], [1, 4, 3]])
print(np.linalg.det(A))

Kết quả thu được là:

40.000000000000014

Ma trận nghịch đảo

Cần phải nhớ rằng, điều kiện để một ma trận vuông A có ma trận nghịch đảo, là định thức của nó phải khác 0, hay nói cách khác là ma trận A phải là ma trận không suy biến.

Có nhiều con đường để tìm ra ma trận nghịch đảo. Các bạn có thể tìm hiểu thêm về các phương pháp Gauss, Gauss - Jordan, phương pháp viền quanh hay các phương pháp lặp,.... Tuy nhiên, các phương pháp kể trên khá phức tạp, mất thời gian để code. Thật may là, trong Numpy cũng hỗ trợ một hàm tính gần đúng ma trận nghịch đảo, là hàm np.linalg.inv(). Ta hãy thử xem nó hoạt động ra sao nhé:

A = np.array([[1, 2, 3], [2, 5, 3], [1, 0, 8]])
print(np.linalg.inv(A))

Kết quả thu được là:

[[-40.  16.   9.]
 [ 13.  -5.  -3.]
 [  5.  -2.  -1.]]

Tổng kết

Như vậy, qua bài viết này, mình cùng các bạn đã ôn tập lại các kiến thức cơ bản nhất về ma trận cũng như các hàm xử lý trong thư viện Numpy của Python. Nhờ Numpy, các phép tính phức tạp với ma trận đôi khi chỉ cần vài dòng lệnh là có thể hoàn thành. Tất nhiên, những kiến thức đề cập ở trên chưa phải là đủ để các bạn có thể bước chân vào thế giới Machine Learning. Những vấn đề nâng cao hơn của ma trận và Numpy, xin được hẹn các bạn ở một bài viết khác trong thời gian gần nhất. Mọi sai sót cũng như thắc mắc về bài viết của mình mong được các bạn để lại comment phía bên dưới.