Hướng dẫn thực hành thuật toán t-SNE cho trực quan hóa dữ liệu chiều cao

Giới thiệu

Trong lĩnh vực khoa học dữ liệu, một thách thức thường gặp là xử lý các tập dữ liệu chứa hàng trăm đặc trưng (biến) mà không có kiến thức sâu về lĩnh vực liên quan. Nhiệm vụ đặt ra là cần nhận diện các trạng thái ẩn, khám phá và phân tích cấu trúc của dữ liệu đó. Mặc dù Phân tích thành phần chính (PCA) là phương pháp tuyến tính phổ biến để giảm chiều dữ liệu, nó có những hạn chế nhất định. Bài viết này sẽ hướng dẫn bạn sử dụng t-SNE (t-Distributed Stochastic Neighbor Embedding), một thuật toán phi tuyến tiên tiến hơn (ra đời năm 2008) để giải quyết vấn đề này hiệu quả hơn.

t-SNE là gì?

t-SNE là một kỹ thuật giảm chiều dữ liệu phi tuyến, chuyên dụng用于 khám phá dữ liệu chiều cao. Thuật toán này ánh xạ các dữ liệu đa chiều vào không gian thấp (thường là 2 hoặc 3 chiều) phù hợp với thị giác con người, giúp các cụm dữ liệu (clusters) trở nên dễ phân biệt hơn.

Vị trí của t-SNE trong không gian các thuật toán giảm chiều

Các thuật toán giảm chiều được chia thành hai nhóm chính: tuyến tính và phi tuyến. PCA thuộc nhóm tuyến tính. Trong khi đó, t-SNE cùng với các thuật toán như Sammon Mapping, Isomap, LLE (Locally Linear Embedding) thuộc nhóm phi tuyến.

Hạn chế của PCA: PCA là một thuật toán tuyến tính, do đó nó không thể giải thích được các mối quan hệ đa thức phức tạp giữa các đặc trưng. Ngược lại, t-SNE dựa trên phân phối xác suất của các bước đi ngẫu nhiên trên đồ thị lân cận để tìm ra cấu trúc nội tại của dữ liệu. Các thuật toán tuyến tính có xu hướng đặt các điểm dữ liệu không tương đồng xa nhau trong không gian thấp, nhưng để biểu diễn đa dạng các đa tạp (manifolds) phi tuyến, các điểm dữ liệu tương đồng cần được đặt gần nhau, điều mà t-SNE thực hiện tốt hơn.

Chi tiết thuật toán t-SNE

Cơ chế hoạt động:

  1. Tính xác suất trong không gian gốc: Thuật toán bắt đầu bằng cách chuyển đổi khoảng cách Euclide giữa các điểm dữ liệu trong không gian chiều cao thành xác suất có điều kiện biểu thị sự tương đồng. Nếu một điểm $x_j$ nằm gần $x_i$, xác suất $p_{j|i}$ sẽ cao.
  2. Tính xác suất trong không gian thấp: Tương tự, ta tính toán xác suất $q_{j|i}$ cho các điểm dữ liệu tương ứng $y_i$ và $y_j$ trong không gian chiều thấp.
  3. Tối ưu hóa: Mục tiêu của t-SNE là giảm thiểu sự khác biệt giữa hai phân phối xác suất $P$ và $Q$. Sự khác biệt này được đo bằng độ phân kỳ Kullback-Leibler (KL divergence). Thuật toán sử dụng Gradient Descent để tối ưu hóa hàm chi phí này.
  4. Phân phối t: Một sự cải tiến quan trọng của t-SNE so với SNE gốc là việc sử dụng phân phối t-Student (đuôi dài) cho không gian chiều thấp thay vì phân phối Gaussian. Điều này giúp giải quyết vấn đề "đông đúc" (crowding problem) và hỗ trợ việc tối ưu hóa tốt hơn.
  5. Độ phức tạp (Perplexity): Đây là một siêu tham số quan trọng, liên quan đến số lượng lân cận hiệu quả mà mỗi điểm xem xét. Nó được định nghĩa dựa trên entropy Shannon của phân phối xác suất.

Độ phức tạp tính toán: Do phải tính toán ma trận xác suất giữa tất cả các cặp điểm dữ liệu, t-SNE có độ phức tạp về thời gian và không gian là bậc hai ($O(N^2)$) theo số lượng điểm dữ liệu. Điều này đòi hỏi nhiều tài nguyên hệ thống khi xử lý tập dữ liệu lớn.

Ứng dụng thực tế

t-SNE có thể áp dụng cho hầu hết các tập dữ liệu chiều cao. Nó được sử dụng rộng rãi trong:

  • Xử lý hình ảnh: Nhận diện biểu cảm khuôn mặt.
  • Xử lý ngôn ngữ tự nhiên (NLP): So sánh văn bản sử dụng word2vec.
  • Genomics: Nhận diện các nhóm khối u.
  • Xử lý giọng nói.

So sánh với các thuật toán khác

Nếu so sánh độ chính xác với PCA và các mô hình tuyến tính khác, t-SNE thường cho kết quả tốt hơn trong việc phân tách các cụm dữ liệu. Lý do là thuật toán thiết lập ranh giới mềm giữa cấu trúc cục bộ và toàn cục của dữ liệu, giúp bảo tồn cấu trúc dữ liệu tốt hơn trong không gian chiều thấp.

Ví dụ minh họa

1. Triển khai với R

Chúng ta sẽ sử dụng thư viện Rtsne trên bộ dữ liệu MNIST.

## Cài đặt và tải thư viện
# install.packages("Rtsne")
library(Rtsne)

## Giả định dữ liệu đã được nạp vào biến 'mnist_data'
## mnist_data có chứa cột nhãn 'label_vector' và các cột đặc trưng còn lại
# mnist_data <- read.csv("path/to/mnist.csv")

## Chuẩn bị dữ liệu
class_labels <- as.factor(mnist_data$label_vector)
feature_data <- mnist_data[, -which(names(mnist_data) == "label_vector")]

## Thiết lập màu sắc cho việc vẽ
unique_classes <- length(unique(class_labels))
palette_colors <- rainbow(unique_classes)
names(palette_colors) <- unique(class_labels)

## Thực thi thuật toán t-SNE
# dims: số chiều đầu ra (2)
# perplexity: liên quan đến số lượng láng giềng
# max_iter: số vòng lặp tối đa
set.seed(123) # Để đảm bảo tính tái tạo
tsne_model <- Rtsne(feature_data, dims = 2, perplexity = 30, verbose = TRUE, max_iter = 500)

## Trực quan hóa kết quả
plot(tsne_model$Y, t = 'n', main = "Trực quan hóa t-SNE MNIST")
text(tsne_model$Y, labels = class_labels, col = palette_colors[class_labels])

Lưu ý rằng thời gian thực thi của t-SNE sẽ lâu hơn đáng kể so với PCA trên cùng một bộ dữ liệu do độ phức tạp tính toán cao hơn.

2. Triển khai với Python

Dưới đây là ví dụ sử dụng thư viện sklearn trên bộ dữ liệu chữ số viết tay.

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, manifold, decomposition

## Nạp bộ dữ liệu chữ số
digit_dataset = datasets.load_digits(n_class=10)
X_features = digit_dataset.data
y_targets = digit_dataset.target

## Hàm vẽ biểu đồ nhúng dữ liệu
def visualize_embedding(data, labels, title):
    # Chuẩn hóa dữ liệu để hiển thị trong khoảng [0, 1]
    x_min, x_max = np.min(data, 0), np.max(data, 0)
    normalized_data = (data - x_min) / (x_max - x_min)

    plt.figure(figsize=(8, 6))
    ax = plt.subplot(111)
    
    for i in range(normalized_data.shape[0]):
        # Hiển thị nhãn số tại vị trí tương ứng
        plt.text(normalized_data[i, 0], normalized_data[i, 1], str(labels[i]),
                 color=plt.cm.Set1(labels[i] / 10.),
                 fontdict={'weight': 'bold', 'size': 9})

    plt.xticks([]), plt.yticks([])
    plt.title(title)

## 1. Tính toán và vẽ PCA để so sánh
print("Đang tính toán PCA...")
pca_model = decomposition.TruncatedSVD(n_components=2)
pca_result = pca_model.fit_transform(X_features)
visualize_embedding(pca_result, y_targets, 
                    "Chiếu hình ảnh qua PCA (thuật toán tuyến tính)")

## 2. Tính toán và vẽ t-SNE
print("Đang tính toán t-SNE...")
# init='pca' giúp khởi tạo tốt hơn và nhanh hơn
tsne_model = manifold.TSNE(n_components=2, init='pca', random_state=0, perplexity=30)
tsne_result = tsne_model.fit_transform(X_features)
visualize_embedding(tsne_result, y_targets, 
                    "Nhúng dữ liệu bằng t-SNE (thuật toán phi tuyến)")

plt.show()

Các lỗi thường gặp khi sử dụng

Khi giải thích kết quả từ t-SNE, cần tránh những sai lầm sau:

  • Chọn sai Perplexity: Tham số perplexity nên nhỏ hơn số lượng điểm dữ liệu. Giá trị phổ biến nằm trong khoảng 5 đến 50.
  • Tính ngẫu nhiên: Các lần chạy khác nhau với cùng một siêu tham số có thể tạo ra các hình dạng khác nhau. Cần thiết lập random_state để tái tạo kết quả.
  • Quy mô cụm: Kích thước của các cụm trong biểu đồ t-SNE không mang ý nghĩa về độ lệch chuẩn hay độ phân tán của dữ liệu gốc. Không nên dùng kích thước cụm để đánh giá độ variance.
  • Khoảng cách cụm: Khoảng cách giữa các cụm có thể thay đổi tùy thuộc vào tham số và không phản ánh chính xác khoảng cách thực tế trong không gian gốc.
  • Mô hình trong nhiễu: Đôi khi thuật toán có thể tìm ra các pattern ngay cả trong dữ liệu nhiễu ngẫu nhiên.
  • Đánh giá đơn lẻ: Không nên đưa ra kết luận về cấu trúc topology chỉ dựa trên một biểu đồ t-SNE duy nhất. C cần quan sát nhiều biểu đồ với các tham số khác nhau.

Thẻ: t-SNE Machine Learning Dimensionality Reduction R python

Đăng vào ngày 16 tháng 9 lúc 08:40