Chuyển đến nội dung chính

Mô hình Bag of Words trong Python: Hướng dẫn đầy đủ

Khám phá mọi điều bạn cần biết về cách triển khai mô hình bag of words trong Python.
· 12 phút đọc

Khám phá cùng AI

Bag of Words (BoW) là một kỹ thuật trong Xử lý ngôn ngữ tự nhiên (NLP). Kỹ thuật này được sử dụng rộng rãi để chuyển đổi dữ liệu văn bản thành định dạng máy có thể đọc được, cụ thể là các giá trị số, mà không xét đến ngữ pháp và thứ tự từ. Hiểu BoW rất quan trọng với bất kỳ ai làm việc với dữ liệu văn bản. Python cung cấp nhiều công cụ và thư viện để triển khai Bag of Words hiệu quả.

Trong hướng dẫn này, chúng ta sẽ đi sâu vào BoW, giới thiệu các khái niệm, đề cập đến các cách sử dụng, và thực hiện từng bước chi tiết bằng Python. Kết thúc hướng dẫn, bạn sẽ có thể áp dụng mô hình Bag of Words vào các bài toán thực tế. Nếu bạn mới làm quen với NLP, hãy xem lộ trình kỹ năng Natural Language Processing in Python để tìm hiểu thêm. 

Bag of Words là gì?

Bag of Words là kỹ thuật trích xuất đặc trưng từ dữ liệu văn bản cho các tác vụ học máy, như phân loại văn bản và phân tích cảm xúc. Điều này quan trọng vì các thuật toán học máy không thể xử lý dữ liệu văn bản thô. Quá trình chuyển văn bản thành số được gọi là trích xuất đặc trưng hoặc mã hóa đặc trưng.

Bag of Words dựa trên số lần xuất hiện của các từ trong một tài liệu. Quá trình bắt đầu bằng việc tìm tập từ vựng trong văn bản và đo lường tần suất xuất hiện của chúng. Nó được gọi là “bag” (túi) vì thứ tự và cấu trúc của từ không được xét đến, chỉ quan tâm đến số lần xuất hiện. 

Mô hình Bag of Words khác với Continuous Bag of Words (CBOW), mô hình học các word embedding dạng đặc bằng cách sử dụng các từ xung quanh để dự đoán một từ mục tiêu, từ đó nắm bắt quan hệ ngữ nghĩa giữa các từ. CBOW cần được huấn luyện trên một kho văn bản lớn và tạo ra các vector chiều thấp, hữu ích cho các ứng dụng NLP phức tạp nơi ngữ cảnh từ rất quan trọng.

Khía cạnh

BOW

CBOW

Mục đích

Đếm số lần xuất hiện của từng từ

Dự đoán từ mục tiêu dựa trên ngữ cảnh

Kiểu đầu ra

Vector thưa, chiều cao

Embedding đặc, chiều thấp

Xét ngữ cảnh

Không (bỏ qua thứ tự từ)

Có (dùng các từ xung quanh)

Biểu diễn

Vector tần suất dạng thưa

Vector đặc nắm bắt ngữ nghĩa

Độ phức tạp

Thấp (không cần huấn luyện)

Cao (cần huấn luyện trên kho lớn)

Ứng dụng điển hình

Phân loại văn bản, phân tích cảm xúc

Word embedding, các tác vụ NLP cần ngữ cảnh

Vì sao dùng Bag of Words?

Bag of Words hữu ích trong nhiều tác vụ NLP, một số lý do sử dụng gồm:

  • Trích xuất đặc trưng: Chuyển đổi dữ liệu văn bản phi cấu trúc thành dữ liệu có cấu trúc, có thể dùng làm đầu vào cho nhiều thuật toán học máy.
  • Đơn giản và hiệu quả: BoW dễ triển khai về mặt tính toán, và hoạt động tốt với các kho văn bản nhỏ đến vừa.
  • Tương đồng tài liệu: Có thể sử dụng để tính độ tương đồng giữa các tài liệu văn bản bằng các kỹ thuật như cosine similarity.
  • Phân loại văn bản: Khi kết hợp với các kỹ thuật như Naive Bayes, BoW hiệu quả cho các tác vụ phân loại văn bản như phân loại spam và phân tích cảm xúc.

Tuy nhiên, cũng có hạn chế như không xét đến ngữ nghĩa, cấu trúc từ, hay trật tự từ.

Các bước triển khai Bag of Words trong Python

Để tạo mô hình bag-of-words, chúng ta lấy tất cả các từ trong một kho ngữ liệu và tạo một cột cho mỗi từ. Các hàng đại diện cho các câu. Nếu một từ nào đó tồn tại trong câu, nó được biểu diễn bằng 1, và nếu không tồn tại thì bằng 0. Mỗi từ trong cột đại diện cho một đặc trưng.

Kết quả cuối cùng là một ma trận thưa. Ma trận thưa là ma trận có rất nhiều số 0. 

Tiền xử lý dữ liệu

Để tạo mô hình Bag of Words trong Python, chúng ta cần thực hiện một vài bước tiền xử lý. Các bước này gồm tách từ (tokenization) và loại bỏ stopword.

Tokenization là quá trình chia nhỏ một đoạn văn bản thành các đơn vị nhỏ hơn, thường là từ. Bạn có thể thực hiện tokenization bằng NLTK. 

Stop words là các từ phổ biến trong tiếng Anh như "the," "that," và "a," vốn không đóng góp vào sắc thái (polarity) của câu.

import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

# Download stopwords and tokenizer if you haven't already
nltk.download("punkt")
nltk.download("stopwords")

# Example sentence
sentence = "This is an example showing how to remove stop words from a sentence."

# Tokenize the sentence into words
words = word_tokenize(sentence)

# Get the list of stop words in English
stop_words = set(stopwords.words("english"))

# Remove stop words from the sentence
filtered_sentence = [word for word in words if word.lower() not in stop_words]

# Join the words back into a sentence
filtered_sentence = " ".join(filtered_sentence)
print(filtered_sentence)

Kết quả:

example showing remove stop words sentence.

Tạo từ vựng

Từ vựng (vocabulary) là tập hợp các từ duy nhất xuất hiện trong một kho văn bản. Xây dựng từ vựng bao gồm thu thập tất cả các từ duy nhất từ kho văn bản và đếm số lần xuất hiện của chúng. Từ vựng này hữu ích cho nhiều tác vụ NLP như mô hình ngôn ngữ, word embedding và phân loại văn bản.

Đoạn mã dưới đây tạo phân bố tần suất đơn giản của các từ trong kho văn bản, hữu ích cho các tác vụ NLP cơ bản như xây dựng từ vựng hoặc hiểu nội dung văn bản:

  • Biến corpus chứa một vài câu ví dụ. Trong ứng dụng thực tế, nó sẽ chứa dữ liệu văn bản lớn hơn, đa dạng hơn.
  • vocab = defaultdict(int) giúp đơn giản hóa việc đếm tần suất, tự động khởi tạo mọi từ mới với giá trị 0, cho phép tăng đếm trực tiếp mà không cần kiểm tra.
  • Mỗi câu được token hóa bằng cách chuyển sang chữ thường và trích xuất từ bằng biểu thức chính quy. Mẫu \b\w+\b xác định các từ chỉ gồm ký tự chữ-số, bỏ qua dấu câu và ký hiệu khác.
  • Số đếm của mỗi từ được cập nhật trong từ điển vocab.
  • Từ vựng được sắp xếp theo tần suất giảm dần để dễ thấy các từ phổ biến nhất ở đầu và được hiển thị tham khảo.
import re  
# Import the regular expressions module to help with text processing
from collections import (
    defaultdict,
)  

# Import defaultdict to easily handle word frequency counting

# Sample corpus of text - a small dataset of sentences to analyze
corpus = [
    "Tokenization is the process of breaking text into words.",
    "Vocabulary is the collection of unique words.",
    "The process of tokenizing is essential in NLP.",
]

# Initialize a defaultdict with integer values to store word frequencies

# defaultdict(int) initializes each new key with a default integer value of 0
vocab = defaultdict(int)

# Loop through each sentence in the corpus to tokenize and normalize
for sentence in corpus:
    # Convert the sentence to lowercase to ensure consistency in counting (e.g., 'Tokenization' and 'tokenization' are treated as the same word)
    # Use regular expressions to find words composed of alphanumeric characters only
    words = re.findall(r"\b\w+\b", sentence.lower())
    # For each word found, increment its count in the vocab dictionary
    for word in words:
        vocab[word] += 1

# Convert the defaultdict vocab to a regular dictionary for easier handling and sorting

# Sort the dictionary by word frequency in descending order and convert it to a new dictionary
sorted_vocab = dict(sorted(vocab.items(), key=lambda x: x[1], reverse=True))

# Display the sorted vocabulary with each word and its frequency count
print("Vocabulary with Frequencies:", sorted_vocab)

Kết quả:

Vocabulary with Frequencies: {'is': 3, 'the': 3, 'of': 3, 'process': 2, 'words': 2, 'tokenization': 1, 'breaking': 1, 'text': 1, 'into': 1, 'vocabulary': 1, 'collection': 1, 'unique': 1, 'tokenizing': 1, 'essential': 1, 'in': 1, 'nlp': 1}

Tự xây dựng từ vựng có thể tốn thời gian, đặc biệt với các kho lớn. CountVectorizer của Scikit-learn tự động hóa quá trình này và cho phép xử lý văn bản linh hoạt hơn như chúng ta sẽ thấy sau. 

Triển khai Bag of Words bằng Python (từ đầu)

Hãy bắt đầu với một triển khai Bag of Words đơn giản từ đầu bằng Python. Điều này sẽ giúp bạn hiểu các khối xây dựng và cơ chế hoạt động bên dưới.

Triển khai thủ công

Bước 1: Tiền xử lý dữ liệu văn bản

Chúng ta sẽ bắt đầu bằng cách định nghĩa một hàm đơn giản để xử lý văn bản, gồm tokenization, chuyển chữ thường và loại bỏ dấu câu.

from collections import defaultdict
import string

# Sample text data: sentences
corpus = [
    "Python is amazing and fun.",
    "Python is not just fun but also powerful.",
    "Learning Python is fun!",
]
# Function to preprocess text
def preprocess(text):
    # Convert to lowercase
    text = text.lower()
    # Remove punctuation
    text = text.translate(str.maketrans("", "", string.punctuation))
    # Tokenize: split the text into words
    tokens = text.split()
    return tokens

# Apply preprocessing to the sample corpus
processed_corpus = [preprocess(sentence) for sentence in corpus]
print(processed_corpus)

Kết quả:

[['python', 'is', 'amazing', 'and', 'fun'], ['python', 'is', 'not', 'just', 'fun', 'but', 'also', 'powerful'], ['learning', 'python', 'is', 'fun']]

Bước 2: Xây dựng từ vựng

Giờ chúng ta cần quét qua tất cả tài liệu và xây dựng một danh sách đầy đủ các từ duy nhất, đó là từ vựng của chúng ta.

# Initialize an empty set for the vocabulary
vocabulary = set()

# Build the vocabulary
for sentence in processed_corpus:
    vocabulary.update(sentence)

# Convert to a sorted list
vocabulary = sorted(list(vocabulary))
print("Vocabulary:", vocabulary)

Bước 3: Tính tần suất từ và vector hóa

Bây giờ, chúng ta sẽ tính tần suất của mỗi từ trong từ vựng cho từng tài liệu trong kho đã xử lý.

def create_bow_vector(sentence, vocab):
    vector = [0] * len(vocab)  # Initialize a vector of zeros
    for word in sentence:
        if word in vocab:
            idx = vocab.index(word)  # Find the index of the word in the vocabulary
            vector[idx] += 1  # Increment the count at that index
    return vector

Tại thời điểm này, bạn đã tạo được biểu diễn Bag of Words cho mỗi tài liệu trong kho của mình.

# Create BoW vector for each sentence in the processed corpus
bow_vectors = [create_bow_vector(sentence, vocabulary) for sentence in processed_corpus]
print("Bag of Words Vectors:")
for vector in bow_vectors:
    print(vector)

Kết quả:

Bag of Words Vectors:
[0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1]
[1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1]
[0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]

Sử dụng CountVectorizer của Scikit-learn

Xây dựng mô hình Bag of Words thủ công giúp học tập, nhưng với ứng dụng sản xuất, bạn sẽ muốn dùng các thư viện tối ưu, hiệu quả như Scikit-learn.

Hàm Python dùng để tokenization là CountVectorizer, được import từ sklearn.feature_extraction.text. Một trong các tham số của CountVectorizer là max_features, đại diện cho số lượng từ tối đa bạn muốn có trong mô hình bag of words. Ở đây chúng ta dùng None, nghĩa là sẽ dùng tất cả đặc trưng. 

Sau khi tạo một thể hiện của CountVectorizer, dùng phương thức .fit_transform() để tạo mô hình bag of words. Tiếp theo, dùng .toarray() để chuyển mô hình bag of words thành các mảng numpy có thể đưa vào mô hình học máy.

Khi đã fit, CountVectorizer đã xây dựng một từ điển các chỉ số đặc trưng. Giá trị chỉ số của một từ trong từ vựng liên kết với tần suất của nó trong toàn bộ kho huấn luyện.

from sklearn.feature_extraction.text import CountVectorizer
# Original corpus
corpus = [
    "Python is amazing and fun.",
    "Python is not just fun but also powerful.",
    "Learning Python is fun!",
]
# Create a CountVectorizer Object
vectorizer = CountVectorizer()
# Fit and transform the corpus
X = vectorizer.fit_transform(corpus)
# Print the generated vocabulary
print("Vocabulary:", vectorizer.get_feature_names_out())
# Print the Bag-of-Words matrix
print("BoW Representation:")
print(X.toarray())

Kết quả:

markdownVocabulary: ['also' 'amazing' 'and' 'but' 'fun' 'is' 'just' 'learning' 'not'

 'powerful' 'python']

BoW Representation:

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

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

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

Ví dụ: Ứng dụng Bag of Words

Bây giờ hãy áp dụng mô hình BoW cho một kho văn bản nhỏ gồm ba bài đánh giá phim để minh họa toàn bộ quy trình. 

Chúng ta sẽ dùng CountVectorizer của Scikit-learn để áp dụng mô hình BoW cho kho văn bản nhỏ này.

Các bước chúng ta sẽ thực hiện:

  • CountVectorizer tự động token hóa văn bản, loại bỏ dấu câu và chuyển chữ thường.
  • .fit_transform(corpus) chuyển kho văn bản thành ma trận tài liệu–thuật ngữ, trong đó mỗi hàng đại diện cho một tài liệu và mỗi cột đại diện cho một từ trong từ vựng.
  • X_dense là ma trận đặc biểu diễn tần suất của mỗi từ trong từng tài liệu.
from sklearn.feature_extraction.text import CountVectorizer
# Sample corpus of movie reviews
corpus = [
    "I loved the movie, it was fantastic!",
    "The movie was okay, but not great.",
    "I hated the movie, it was terrible.",
]

# Initialize the CountVectorizer
vectorizer = CountVectorizer()

# Fit and transform the corpus to a document-term matrix
X = vectorizer.fit_transform(corpus)

# Convert the document-term matrix into a dense format (optional for visualization)
X_dense = X.toarray()

# Get the vocabulary (mapping of words to index positions)
vocab = vectorizer.get_feature_names_out()

# Print the vocabulary and document-term matrix
print("Vocabulary:", vocab)
print("Document-Term Matrix:\n", X_dense)

Kết quả:

Vocabulary: ['but' 'fantastic' 'great' 'hated' 'it' 'loved' 'movie' 'not' 'okay' 'terrible' 'the' 'was']
Document-Term Matrix:
 [[0 1 0 0 1 1 1 0 0 0 1 1]  # First review: "I loved the movie, it was fantastic!"
  [1 0 1 0 1 0 1 1 1 0 1 1]  # Second review: "The movie was okay, but not great."
  [0 0 0 1 1 0 1 0 0 1 1 1]] # Third review: "I hated the movie, it was terrible."

Cách diễn giải kết quả trên như sau:

  • Mỗi từ duy nhất trong kho được gán một chỉ số, và các từ được sắp theo thứ tự chữ cái. Ví dụ, "but" ở chỉ số 0, "fantastic" ở chỉ số 1, "movie" ở chỉ số 6, v.v.
  •  Mỗi hàng trong ma trận tài liệu đại diện cho một bài đánh giá phim, và mỗi cột tương ứng với một từ trong từ vựng. Các giá trị trong ma trận biểu diễn tần suất của mỗi từ trong tài liệu đó.
    • Bài đánh giá thứ nhất: [0 1 0 0 1 1 1 0 0 0 1 1] cho thấy rằng:
      • Từ "fantastic" xuất hiện một lần (1 tại chỉ số 1),
      • Từ "loved" xuất hiện một lần (1 tại chỉ số 5),
      • Từ "movie" xuất hiện một lần (1 tại chỉ số 6),
      • Từ "it" xuất hiện một lần (1 tại chỉ số 4),
      • Vân vân.

Vector BoW có thể được hiểu như sau:

  • Mỗi tài liệu là một vector số biểu diễn số lần đếm từ. Số chiều của vector bằng kích thước từ vựng. Trong trường hợp này, từ vựng có 12 từ, nên mỗi bài đánh giá được biến đổi thành vector 12 chiều.
  • Hầu hết các từ trong mỗi hàng là số 0 vì không phải tài liệu nào cũng chứa mọi từ trong từ vựng. Do đó, các mô hình BoW thường là thưa, tức có nhiều số 0.

Ưu điểm và hạn chế của Bag of Words

Giờ hãy cùng điểm qua một số ưu điểm và hạn chế của mô hình Bag of Words.

Ưu điểm

  1. Dễ triển khai và diễn giải: Bag of Words là một trong những kỹ thuật biểu diễn văn bản đơn giản nhất, phù hợp cho người mới bắt đầu. Sự đơn giản cho phép triển khai nhanh mà không cần tiền xử lý phức tạp hay mô hình chuyên biệt.
  2. Dễ dùng cho các tác vụ phân loại văn bản: Bag of Words phù hợp với các tác vụ cơ bản như phân loại văn bản, phân tích cảm xúc và phát hiện spam. Những tác vụ này thường không cần mô hình ngôn ngữ tinh vi, nên biểu diễn BOW là đủ và hiệu quả.

Hạn chế

  1. Kích thước từ vựng ảnh hưởng đến độ thưa của biểu diễn: Từ vựng càng lớn, biểu diễn càng thưa và chiều càng cao. Độ thưa này có thể khiến mô hình khó học hiệu quả và đòi hỏi tinh chỉnh kích thước từ vựng để tránh chi phí tính toán quá mức.
  2. Sinh ra ma trận thưa tốn kém tính toán: Vì mỗi tài liệu được biểu diễn bằng tần suất của mỗi từ trong một từ vựng có thể rất lớn, các ma trận kết quả thường hầu hết là số 0, kém hiệu quả khi lưu trữ và xử lý trong pipeline học máy. Ma trận thưa tiêu tốn bộ nhớ đáng kể và thường cần công cụ, thư viện chuyên biệt để lưu trữ và tính toán hiệu quả, đặc biệt với bộ dữ liệu lớn.
  3. Mất ý nghĩa và ngữ cảnh: BOW bỏ qua thứ tự từ và cấu trúc câu, dẫn đến mất quan hệ ngữ pháp và ý nghĩa. Hạn chế này khiến nó kém phù hợp cho các tác vụ mà ngữ cảnh, sắc thái và trật tự từ quan trọng, như dịch thuật hoặc phát hiện cảm xúc trong câu phức tạp.

Các chiến lược sau có thể được dùng để giảm kích thước từ vựng trong Bag of Words:

  • Bỏ qua phân biệt hoa/thường. 
  • Loại bỏ dấu câu.
  • Loại bỏ stopword, tức các từ phổ biến như the và a. 
  • Đảm bảo tất cả từ được đánh vần đúng. 
  • Dùng kỹ thuật stemming để rút gọn từ về gốc.

Bước tiếp theo: Vượt ra ngoài Bag of Words

Một hạn chế của mô hình Bag of Words là đối xử mọi từ như nhau. Điều này có thể dẫn tới việc một số từ được coi trọng hơn chỉ vì xuất hiện thường xuyên.

TF-IDF (Term Frequency–Inverse Document Frequency) là giải pháp cho vấn đề này, vì nó điều chỉnh trọng số của từ dựa trên tần suất xuất hiện của chúng trên toàn bộ tài liệu.

TF-IDF: Phần mở rộng của Bag of Words

Term Frequency (TF) đại diện cho tần suất một thuật ngữ trong một tài liệu. Inverse Document Frequency (IDF) giảm ảnh hưởng của các từ xuất hiện phổ biến trên nhiều tài liệu. Điểm TF-IDF được tính bằng cách nhân hai đại lượng này. 

Xét một tài liệu có 200 từ, trong đó từ love xuất hiện 5 lần. TF cho love là (5 / 200) = 0,025. Giả sử chúng ta có một triệu tài liệu và từ love xuất hiện trong một nghìn tài liệu, thì IDF được tính là log(1000000 / 1000) = 3. Trọng số TF-IDF là tích của hai đại lượng: 0,025 * 3 = 0,075.

Trong Scikit-learn, việc này khá dễ tính bằng lớp TfidfVectorizer.

from sklearn.feature_extraction.text import TfidfVectorizer
# Sample corpus
corpus = [
    "Python is amazing and fun.",
    "Python is not just fun but also powerful.",
    "Learning Python is fun!",
]

# Create the Tf-idf vectorizer
tfidf_vectorizer = TfidfVectorizer()

# Fit and transform the corpus
X_tfidf = tfidf_vectorizer.fit_transform(corpus)

# Show the Vocabulary
print("Vocabulary:", tfidf_vectorizer.get_feature_names_out())

# Show the TF-IDF Matrix
print("TF-IDF Representation:")
print(X_tfidf.toarray())

Kết quả:

Vocabulary: ['also' 'amazing' 'and' 'but' 'fun' 'is' 'just' 'learning' 'not'
 'powerful' 'python']
TF-IDF Representation:
[[0.         0.57292883 0.57292883 0.         0.338381   0.338381
  0.         0.         0.         0.         0.338381  ]
 [0.40667606 0.         0.         0.40667606 0.24018943 0.24018943
  0.40667606 0.         0.40667606 0.40667606 0.24018943]
 [0.         0.         0.         0.         0.41285857 0.41285857
  0.         0.69903033 0.         0.         0.41285857]]

Ma trận TF-IDF ở trên cung cấp thước đo có trọng số thay vì tần suất thô.

Dù mô hình Bag of Words có hạn chế, đặc biệt với bộ dữ liệu lớn và phức tạp, nó vẫn là nền tảng quan trọng trong nhiều ứng dụng NLP. Hiểu mô hình này sẽ giúp bạn khi khám phá các mô hình nâng cao hơn như word embedding và Transformers.

Từ đây, bạn có thể thử nghiệm BoW trong dự án của mình, bao gồm phát hiện spam, phân tích cảm xúc, phân cụm tài liệu, và nhiều hơn nữa.

Nếu muốn cải thiện hơn nữa so với Bag of Words, bạn có thể khám phá các phương pháp như Word2Vec, GloVe, hoặc các mô hình học sâu như BERT.

Tổng kết

Bag of Words là một kỹ thuật nền tảng trong Xử lý ngôn ngữ tự nhiên. Nó là cách đơn giản nhưng hiệu quả để chuyển văn bản phi cấu trúc thành các đặc trưng số mà các thuật toán học máy có thể sử dụng. Trong hướng dẫn này, chúng ta đã đề cập:

  • Bag of Words (BoW) là gì?
  • Lợi ích của mô hình Bag of Words trong việc xây dựng mô hình học máy.
  • Cách triển khai mô hình Bag of Words trong Python.
  •  Ưu điểm và hạn chế của Bag of Words.
  • Lý thuyết và động lực đằng sau mô hình Bag of Words.
  • Giới thiệu TF-IDF như một cải tiến cho cách tiếp cận Bag of Words truyền thống.

Hãy xem lộ trình kỹ năng Natural Language Processing in Python của chúng tôi để đào sâu vào xử lý ngôn ngữ tự nhiên. 

Derrick Mwiti

Tác giả

Derrick Mwiti

Chủ đề

Liên quan