1. Broadcasting trong Numpy là gì?

Thuật ngữ Broadcasting trong Numpy mô tả cách xử lý các mảng trong Numpy có kích thước khác nhau thông qua các phép tính toán học. Theo một số ràng buộc nhất định, mảng nhỏ hơn được sẽ được “Broadcasting ” trên mảng lớn hơn để chúng có hình dạng tương thích với nhau.

Broadcasting còn cung cấp sẵn một phương tiện vectơ hóa các hoạt động của mảng để lặp lại xảy ra trong C thay vì Python. Nó thực hiện điều này mà không cần tạo bản sao dữ liệu không cần thiết và thường dẫn đến việc triển khai thuật toán hiệu quả.

Các quy tắc Broadcasting trong Numpy:

  • Nếu 2 mảng khác kích thước, mảng có ndim nhỏ hơn mảng kia được thêm ‘1’ vào (bên trái) shape của mảng đó.
  • Nếu shape của 2 mảng không tương ứng ở bất cứ chiều nào, mảng có shape bằng 1 tại chiều nào thì chiều đó sẽ giãn ra để tương ứng với shape kia.
  • Nếu trong bất kỳ kích thước nào mà kích thước không bằng nhau và không bằng 1, thì sẽ báo lỗi.

2. Broadcasting mảng Numpy cùng kích thước

Các phép toán cơ bản trong NumPy thường được thực hiện trên các cặp phần tử với nhau. Trong trường hợp đơn giản nhất, hai mảng phải có hình dạng hoàn toàn giống nhau, như trong ví dụ sau thực hiện nhân hai mảng 1 chiều với nhau.

import numpy as np 

# Tao mang a,b co cung kich thuoc
a = np.array([1.0, 2.0, 3.0])
b = np.array([2.0, 2.0, 2.0])

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[2. 4. 6.]

Phép toán trên thực hiện nhân các cặp phần tử trong cả ma trận a và ma trận b. Hình ảnh dưới đây minh họa việc nhân hai ma trận 1 chiều có cùng kích thước:

Chúng ta cũng có thể nhân mảng với một số vô hướng bên ngoài của mảng, và cách thức thực hiện phép nhân này cũng sẽ là nhân từng phần tử trong mảng với số vô hướng đó. Cùng xem xét ví dụ dưới đây, chúng ta sẽ nhân một mảng cho một số sẽ cho ra kết quả tương tự với ví dụ trên:

import numpy as np 

# Tao mang a
a = np.array([1.0, 2.0, 3.0])
# Khai bao bien b = 2
b = 2

# Thuc hien phep nhan mang a * 2
result = a * b
print(result)

Kết quả:

[2. 4. 6.]

Việc nhân mảng 1 chiều với một số bên ngoài cũng thực hiện nhân từng phần tử có trong mảng với số đó. Hình ảnh dưới đây minh họa việc nhân mảng với một số:

Tương tự, ta cũng có thể thực hiện phép nhân có cùng kích thước giứa hai mảng hai chiều như sau:

import numpy as np 

# Tao mang a, b cung kich thuoc
a = np.array([
    [1.0, 2.0],
    [2.0, 3.0]
])

b = np.array([
    [2.0, 2.0],
    [2.0, 2.0]
])

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[[2. 4.]
 [4. 6.]]

Nhân mảng hai chiều với một số bên ngoài mảng cũng cho ra kết quả tương tự kết quả trên!

import numpy as np 

# Tao mang a
a = np.array([
    [1.0, 2.0],
    [2.0, 3.0]
])

# Khai bao bien b = 2
b = 2

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[[2. 4.]
 [4. 6.]]

3. Broadcasting mảng Numpy không cùng kích thước

Mảng không cùng kích thước khi thực hiện phép tính với nhau có thể là các giữa các mảng 1 chiều thực hiện phép toán với mảng 2 chiều hoặc các mảng 2 chiều thực hiện phép toán với mảng 3 chiều….. Nghĩa là, shape của hai mảng sẽ là không giống nhau để thực hiện phép tính trên từng cặp phần tử có trong hai mảng.

Ví dụ đơn giản nhất, ta có một mảng 2 chiều (ma trận) có kích thước 2 x 3 mảng này thực hiện phép toán cộng với mảng 1 chiều (vectơ) có kích thước 1 x 3 như sau:

import numpy as np 

# Tao mang 2 chieu 2 x 3 
a = np.array([
    [0, 0, 0],
    [10, 10, 10]
])

# Khai bao mang 1 chieu kich thuoc 3
b = np.array([1, 2, 3])


# Thuc hien phep cong giua mang a va mang b
result = a + b
print(result)

Kết quả:

[[ 1  2  3]
 [11 12 13]]

Mặc dù hai mảng trên không có chung shape thế nhưng khi thực hiện phép toán vẫn cho ra được kết quả là một ma trận có kích thước 2 x 3. Lý do là vì: Mảng a là mảng 2 chiều có kích thước 2 x 3, mảng b là mảng 1 chiều có kích thước 1 x 3 Numpy sẽ dựa theo các quy tắc để chuyển đổi shape của mảng như sau:

  • Theo quy tắc 1, sẽ thêm “1” vào phía bên trái shape của mảng – khi đó mảng b sẽ có kích thước mới là 2 x 3 và hoàn toàn có thể thực hiện phép toán cộng với mảng a giống như 2 mảng có cùng kích thước
  • Theo quy tắc 2, chiều có giá trị = 1 sẽ giãn ra để tương ứng với shape còn lại và khi đó mảng b từ kích thước 1 x 3 sẽ được giãn ra thành kích thước 2 x 3 để tương ứng với mảng a và thực hiện phép toán cộng.
  • Kết quả sau khi cộng hai mảng trên sẽ là một mảng 2 chiều có kích thước là 2 x 3 là do mảng nhỏ hơn được sẽ được “Broadcasting ” trên mảng lớn hơn để chúng có hình dạng tương thích với nhau.

Hình ảnh dưới đây miêu tả rõ hơn về việc thực hiện phép toán cộng giữa mảng 2 chiều (ma trận) có kích thước 2 x 3 với mảng 1 chiều (vectơ) có kích thước 1 x 3 và cho ra kết quả là 1 mảng có kích thước 2 x 3

Tương tự với phép toán cộng, chúng ta cũng có thể sử dụng phép toán nhân giữa hai mảng không cùng shape. Mảng a sẽ là mảng 2 chiều có kích thước x 3 và mảng b sẽ là mảng 1 chiều có kích thước x 3 – khi đó a * b sẽ được thực hiện như sau:

import numpy as np 

# Tao mang 2 chieu 2 x 3 
a = np.array([
    [0, 0, 0],
    [10, 10, 10]
])

# Khai bao mang 1 chieu kich thuoc 3
b = np.array([1, 2, 3])

# Thuc hien phep nhan giua mang a va mang b
result = a * b
print(result)

Kết quả:

[[ 0  0  0]
 [10 20 30]]