
5 khái niệm Python nhà khoa học dữ liệu cần nắm vững
Trong bài viết này, chúng ta sẽ đi sâu vào 5 khái niệm Python cần biết, giúp bạn chuyển đổi từ việc viết mã spaghetti (mã lộn xộn, khó đọc) chậm chạp, cồng kềnh sang xây dựng các đường ống dữ liệu (data pipeline) nhanh như chớp, đạt chuẩn sản xuất và hoạt động hiệu quả.
5 Khái niệm Python nhà khoa học dữ liệu cần biết – KDnuggets
**Giới thiệu**
Không nên sử dụng Python cho khoa học dữ liệu chỉ "vì mọi người đều làm vậy!". Sự thống trị của Python trong lĩnh vực dữ liệu không phải là ngẫu nhiên. Đây là một ngôn ngữ được xây dựng trên cú pháp dễ đọc, biểu cảm cao, trừu tượng hóa việc quản lý bộ nhớ cấp thấp. Tuy nhiên, chính sự trừu tượng hóa cấp cao này cũng đi kèm với một cái giá: việc thực thi Python tiêu chuẩn được định kiểu động và thông dịch, điều này có thể làm cho việc lặp lại thô trở nên chậm chạp một cách đáng sợ.
Để viết các hệ thống dữ liệu hiệu suất cao, một nhà khoa học dữ liệu phải chuyển từ các mẫu mã hóa thủ tục tiêu chuẩn sang các phương pháp chuyên biệt, được vector hóa và nhận biết bộ nhớ. Trong bài viết này, chúng ta sẽ đi sâu vào năm khái niệm Python cần biết sẽ giúp bạn chuyển đổi từ việc viết mã "mì spaghetti" cồng kềnh, chậm chạp sang xây dựng các đường ống dữ liệu nhanh như chớp, cấp độ sản xuất và hoạt động đẹp mắt.
**1. Vector hóa NumPy**
Các vòng lặp Python tiêu chuẩn rất chậm. Vì Python là một ngôn ngữ thông dịch, mỗi lần lặp của vòng lặp `for` đều phát sinh chi phí đáng kể: kiểm tra kiểu, tra cứu phương thức động và đếm tham chiếu. Khi xử lý hàng triệu điểm dữ liệu, những chi phí nhỏ này tích lũy thành các nút thắt cổ chai kéo dài nhiều giây.
Giải pháp là vector hóa NumPy. Thay vì xử lý các phần tử tuần tự trong mã bytecode Python, NumPy chuyển các vòng lặp sang các phần mở rộng C được biên dịch trước, tối ưu hóa cao. Các hoạt động này tác động lên toàn bộ mảng cùng một lúc, thực thi các khối mảng liền kề ở cấp độ máy, thường sử dụng các lệnh Single Instruction, Multiple Data (SIMD).
// Cách cồng kềnh
Giả sử chúng ta có một danh sách một triệu giá trị float đại diện cho các số đọc cảm biến thô, và chúng ta cần chia tỷ lệ mỗi số đọc cho 1,5 và áp dụng một hằng số hiệu chuẩn là 10,0. Sử dụng vòng lặp Python lặp lại:
```python
import time
# Một danh sách lớn gồm 10 triệu số đọc cảm biến
n_elements = 10_000_000
data_list = [float(x) for x in range(n_elements)]
# Chia tỷ lệ giá trị bằng cách sử dụng một vòng lặp python rõ ràng
start_time = time.time()
scaled_list = []
for val in data_list:
scaled_list.append(val * 1.5 + 10.0)
loop_duration = time.time() - start_time
print(f"Loop implementation took: {loop_duration:.6f} seconds")
```
Đầu ra:
`Loop implementation took: 0.378866 seconds`
// Cách vector hóa
Đây là giải pháp thay thế thanh lịch, được vector hóa. Chúng ta tải dữ liệu vào một mảng NumPy liền kề và thực hiện các phép toán số học trực tiếp trên đối tượng mảng:
```python
import numpy as np
import time
# Một danh sách lớn gồm 10 triệu giá trị đọc từ cảm biến
n_elements = 10_000_000
# Cách tiếp cận vector hóa: NumPy thực hiện toàn bộ phép tính trong các vòng lặp C đã được biên dịch trước
data_array = np.arange(n_elements, dtype=float)
start_time = time.time()
scaled_array = data_array * 1.5 + 10.0
numpy_duration = time.time() - start_time
print(f"Thời gian thực hiện của NumPy: {numpy_duration:.6f} giây")
print(f"Tốc độ tăng: nhanh hơn {loop_duration / numpy_duration:.1f} lần!")
```
Kết quả đầu ra:
Thời gian thực hiện của vòng lặp: 0.348456 giây
Thời gian thực hiện của NumPy: 0.013395 giây
Tốc độ tăng: nhanh hơn 26.0 lần!
Bằng cách vector hóa các phép tính số học, chúng ta có thể đạt được hiệu suất tăng đáng kể với mã nguồn gọn gàng, súc tích hơn. Vòng lặp được loại bỏ khỏi không gian Python và được thực thi hoàn toàn trong không gian C tốc độ cao.
# 2. Broadcasting: Quy tắc toán học cho các chiều không khớp
Trong đại số tuyến tính, các phép toán ma trận thường yêu cầu cả hai toán hạng phải có cùng hình dạng chính xác. Tuy nhiên, trong khoa học dữ liệu, chúng ta thường cần thực hiện các phép toán trên các mảng có kích thước khác nhau, chẳng hạn như trừ giá trị trung bình của cột đặc trưng khỏi một tập dữ liệu, hoặc chuẩn hóa các giá trị hàng.
Thay vì sao chép dữ liệu để buộc các hình dạng khớp nhau, NumPy sử dụng một tập hợp các quy tắc toán học được gọi là broadcasting. Broadcasting cho phép các phép toán theo từng phần tử trên các mảng có hình dạng khác nhau bằng cách mở rộng ảo mảng nhỏ hơn dọc theo các chiều bị thiếu hoặc các chiều có một phần tử, mà không sao chép bất kỳ dữ liệu nào trong bộ nhớ.
Các quy tắc broadcasting là:
Nếu các mảng không có cùng hạng (số chiều), hãy thêm 1 vào hình dạng của mảng có hạng thấp hơn cho đến khi cả hai hình dạng có cùng độ dài.
Hai chiều tương thích nếu chúng bằng nhau, hoặc nếu một trong số chúng là 1.
Nếu tương thích, mảng hoạt động như thể nó được kéo dài dọc theo chiều có kích thước 1 để khớp với hình dạng của mảng kia.
// Cách làm cồng kềnh
Giả sử chúng ta có một ma trận đặc trưng 3x4 (3 mẫu, 4 đặc trưng) và muốn trừ giá trị trung bình của cột để "khử trung bình" các đặc trưng:
```python
import numpy as np
features = np.array([
[10.0, 20.0, 30.0, 4.0],
[12.0, 24.0, 36.0, 8.0],
[14.0, 28.0, 42.0, 12.0]
])
# Giá trị trung bình của mỗi cột đặc trưng (hình dạng: (4,))
col_means = np.mean(features, axis=0)
# Sử dụng các vòng lặp lồng nhau để khử trung bình thủ công
demeaned_clunky = np.zeros_like(features)
for idx in range(features.shape[0]):
for col_idx in range(features.shape[1]):
demeaned_clunky[idx, col_idx] = features[idx, col_idx] - col_means[col_idx]
# Cách khác: lát mảng để buộc các hình dạng khớp nhau
tiled_means = np.tile(col_means, (features.shape[0], 1))
demeaned_tiled = features - tiled_means
```
// Cách làm theo kiểu Pythonic
Với broadcasting, chúng ta thực hiện phép trừ.
Nguồn tin: KDnuggets — Tác giả: Matthew Mayo. Bản dịch tiếng Việt do AI thực hiện, có thể có sai sót.