介紹
大家好,博主又來和大家分享自然語言處理領域的知識了。今天給大家分享的內容是自然語言處理中的文本聚類。
文本聚類在自然語言處理領域占據著重要地位,它能將大量無序的文本按照內容的相似性自動劃分成不同的類別,極大地提高了文本處理和信息提取的效率。就好比在一個大型圖書館中,文本聚類能夠像智能管理員一樣,把各種書籍按照主題分類擺放,方便讀者快速找到所需資料。
而實現文本聚類的方法有很多,其中k均值聚類算法、基于高斯混合模型的最大期望值算法,以及無監督樸素貝葉斯模型是比較常用的。好了,話不多說,我們直接進入正題。
文本聚類
k均值聚類算法
k均值聚類算法是一種簡單且經典的聚類算法。它的核心思想就像是在一群人中尋找幾個代表,讓其他人都圍繞著這些代表“站隊”。
具體來說,我們首先要確定聚類的數量k,這個k就像是我們設定的幾個“代表小組”的數量。然后隨機選擇k個文本作為初始的聚類中心,就好像是先選了幾個有代表性的人。接下來,計算每個文本與這k個聚類中心的距離,把文本分配到距離最近的聚類中心所代表的類別中。之后,不斷地重新計算每個類別中所有文本的中心位置,也就是更新聚類中心,就像代表們根據自己所在小組的人員情況調整位置一樣。重復這個過程,直到聚類中心不再變化或者變化很小,此時就完成了文本聚類。
這種算法的優點是簡單易懂、計算效率高,對于大規模文本數據的處理有較好的可擴展性。但它也有一些缺點,比如需要預先指定聚類的數量k,這個k值如果選擇不當,可能會導致聚類結果不理想;而且它對初始聚類中心的選擇比較敏感,不同的初始中心可能會得到不同的聚類結果。
接下來,我們將著手實現k均值算法,以此來開展文本聚類的工作。此次所運用的數據集涵蓋了大約1萬本圖書的相關信息,并且這些圖書被劃分為3種不同的主題類別。在這個數據集中,我們所關注的文本內容并非圖書的標題,而是圖書的摘要部分。我們用代碼來演示:
預處理完整代碼
nlp_book_data_collection.py
# 導入用于處理JSON數據的庫
import json
# 導入defaultdict類,用于創建具有默認值的字典
from collections import defaultdict
# 導入spaCy庫,用于自然語言處理
import spacy
# 從spaCy的中文停用詞模塊中導入停用詞集合
from spacy.lang.zh.stop_words import STOP_WORDS
# 導入tqdm庫,用于顯示進度條
from tqdm import tqdm# 加載中文語言模型
nlp_model = spacy.load('zh_core_web_sm')# 定義一個類,用于收集和處理書籍數據
class BookDataCollection:# 類的初始化方法def __init__(self):# 訓練集和測試集的文件名self.training_file, self.testing_file = 'train_original.jsonl', 'test_original.jsonl'self.init_data()# 定義一個內部函數,用于讀取JSONL文件def read_json_file(self, file_path):# 以只讀模式打開文件,并指定編碼為UTF-8with open(file_path, 'r', encoding='utf-8') as file:# 將文件的每一行讀取為一個列表json_lines = list(file)# 初始化一個空列表,用于存儲解析后的JSON數據data_list = []# 遍歷每一行JSON數據for json_str in json_lines:# 將JSON字符串解析為Python對象,并添加到數據列表中data_list.append(json.loads(json_str))# 返回解析后的數據列表return data_listdef init_data(self):# 調用read_json_file函數讀取訓練集和測試集數據self.training_data, self.testing_data = self.read_json_file(self.training_file), self.read_json_file(self.testing_file)# 打印訓練集和測試集的大小print('訓練集大小 =', len(self.training_data), ', 測試集大小 =', len(self.testing_data))# 初始化兩個空字典,用于存儲標簽到ID和ID到標簽的映射self.label_to_id, self.id_to_label = {}, {}# 遍歷訓練集和測試集數據for data_group in [self.training_data, self.testing_data]:# 遍歷每個數據項for data_item in data_group:# 獲取數據項的類別標簽category = data_item['class']# 如果類別標簽不在標簽到ID的映射中if category not in self.label_to_id:# 生成一個新的IDindex = len(self.label_to_id)# 將類別標簽和對應的ID添加到標簽到ID的映射中self.label_to_id[category] = index# 將ID和對應的類別標簽添加到ID到標簽的映射中self.id_to_label[index] = category# 獲取類別標簽對應的IDlabel_index = self.label_to_id[category]# 在數據項中添加標簽ID字段data_item['label'] = label_index# 定義一個方法,用于對文本進行分詞處理def tokenize_text(self, attribute='book'):# 遍歷訓練集和測試集數據for data_group in [self.training_data, self.testing_data]:# 使用tqdm顯示進度條,遍歷每個數據項for data_item in tqdm(data_group):# 獲取數據項指定屬性的文本內容,并轉換為小寫text_content = data_item[attribute].lower()# 使用spaCy模型對文本進行處理,過濾掉停用詞,并提取詞元tokens = [token.text for token in nlp_model(text_content) if token.text not in STOP_WORDS]# 在數據項中添加分詞結果字段data_item['tokens'] = tokens# 定義一個方法,用于創建詞匯表def create_vocabulary(self, min_frequency=3, min_length=2, max_vocab_size=None):# 初始化一個defaultdict,用于統計詞元的頻率word_frequency = defaultdict(int)# 遍歷訓練集數據for data_item in self.training_data:# 獲取數據項的分詞結果tokens = data_item['tokens']# 遍歷每個詞元for token in tokens:# 統計詞元的頻率word_frequency[token] += 1# 打印唯一詞元數量、總詞元數量、最大詞頻和最小詞頻print(f'唯一詞元數量 = {len(word_frequency)}, 總詞元數量 = {sum(word_frequency.values())}, 'f'最大詞頻 = {max(word_frequency.values())}, 最小詞頻 = {min(word_frequency.values())}')# 初始化兩個空字典,用于存儲詞元到ID和ID到詞元的映射self.token_to_id = {}self.id_to_token = {}# 初始化總詞頻為0total_frequency = 0# 按詞頻從高到低對詞元進行排序,并遍歷for token, freq in sorted(word_frequency.items(), key=lambda x: -x[1]):# 如果指定了最大詞匯表大小,且當前詞匯表大小已達到最大值if max_vocab_size and len(self.token_to_id) >= max_vocab_size:# 跳出循環break# 如果詞元的頻率大于最小頻率if freq > min_frequency:# 如果未指定最小詞長,或者詞元長度大于等于最小詞長if (min_length is None) or (min_length and len(token) >= min_length):# 將詞元添加到詞元到ID的映射中,并分配一個新的IDself.token_to_id[token] = len(self.token_to_id)# 將ID添加到ID到詞元的映射中,并關聯對應的詞元self.id_to_token[len(self.id_to_token)] = token# 累加詞元的頻率到總詞頻中total_frequency += freqelse:# 跳出循環break# 打印最小詞頻、最小詞長、最大詞匯表大小、剩余詞元數量和詞表內詞元占比print(f'最小詞頻 = {min_frequency}, 最小詞長 = {min_length}, 最大詞表大小 = {max_vocab_size}, 'f'剩余詞元數量 = {len(self.token_to_id)}, 'f'詞表內詞元占比 = {total_frequency / sum(word_frequency.values())}')# 定義一個方法,用于將詞元轉換為對應的IDdef convert_tokens_to_indices(self):# 遍歷訓練集和測試集數據for data_group in [self.training_data, self.testing_data]:# 遍歷每個數據項for data_item in data_group:# 初始化一個空列表,用于存儲詞元對應的IDdata_item['token_indices'] = []# 遍歷數據項的分詞結果for token in data_item['tokens']:# 如果詞元在詞元到ID的映射中if token in self.token_to_id:# 將詞元對應的ID添加到列表中data_item['token_indices'].append(self.token_to_id[token])
nlp_text_clustering.py
# 從book_data_container模塊導入BookDataContainer類
from nlp_book_data_collection import BookDataCollection# 定義一個用于自然語言處理文本聚類的類
class NLPTextClustering:def __init__(self):passdef text_clustering(self):# 創建書籍數據集對象book_dataset = BookDataCollection()# 打印標簽到ID的映射print(book_dataset.id_to_label)# 對文本進行分詞處理book_dataset.tokenize_text(attribute='abstract')# 創建詞匯表book_dataset.create_vocabulary(min_frequency=3)# 將詞元轉換為索引book_dataset.convert_tokens_to_indices()# 程序入口,當作為腳本直接運行時執行以下代碼
if __name__ == "__main__":# 創建NLPTextClustering類的一個實例nlp_text_clustering = NLPTextClustering()# 調用實例的text_clustering方法進行文本聚類操作nlp_text_clustering.text_clustering()
預處理運行結果
訓練集大小 = 8626 , 測試集大小 = 2156
{0: '計算機類', 1: '藝術傳媒類', 2: '經管類'}
100%|██████████| 8626/8626 [02:29<00:00, 57.74it/s]
100%|██████████| 2156/2156 [00:37<00:00, 56.76it/s]
唯一詞元數量 = 34251, 總詞元數量 = 806791, 最大詞頻 = 19194, 最小詞頻 = 1
最小詞頻 = 3, 最小詞長 = 2, 最大詞表大小 = None, 剩余詞元數量 = 9504, 詞表內詞元占比 = 0.8910448926673699進程已結束,退出代碼為 0
然后,我們引入實現TF-IDF算法的相關函數,并把經過處理的數據集輸入至該函數內,從而獲取文檔的特征信息。我們用代碼來演示:
特征信息完整代碼
nlp_tfidf_transformer.py
# 導入numpy庫,用于進行數值計算
import numpy as np# 定義一個類,用于進行TF-IDF轉換
class TFIDFTransformer:# 類的初始化方法def __init__(self, vocabulary_size, normalization='l2', smooth_idf_flag=True, sublinear_tf_flag=True):# 存儲詞匯表大小self.vocabulary_size = vocabulary_size# 存儲歸一化方法self.normalization = normalization# 存儲是否進行平滑IDF計算的標志self.smooth_idf_flag = smooth_idf_flag# 存儲是否進行子線性TF計算的標志self.sublinear_tf_flag = sublinear_tf_flag# 定義一個方法,用于擬合數據,計算IDF值def fit_data(self, input_data):# 初始化一個全零數組,用于存儲每個詞元的文檔頻率document_frequency = np.zeros(self.vocabulary_size, dtype=np.float64)# 遍歷輸入數據for data_item in input_data:# 遍歷數據項中唯一的詞元IDfor token_index in set(data_item):# 統計詞元的文檔頻率document_frequency[token_index] += 1# 根據平滑IDF標志,對文檔頻率進行平滑處理document_frequency += int(self.smooth_idf_flag)# 計算樣本數量,并根據平滑IDF標志進行調整num_samples = len(input_data) + int(self.smooth_idf_flag)# 計算每個詞元的IDF值self.idf = np.log(num_samples / document_frequency) + 1# 定義一個方法,用于對輸入數據進行轉換def transform_data(self, input_data):# 確保已經計算了IDF值assert hasattr(self, 'idf')# 初始化一個全零矩陣,用于存儲詞頻矩陣term_frequency = np.zeros((len(input_data), self.vocabulary_size), dtype=np.float64)# 遍歷輸入數據for i, data_item in enumerate(input_data):# 遍歷數據項中的詞元IDfor token in data_item:# 統計詞元的詞頻term_frequency[i, token] += 1# 如果啟用了子線性TF計算if self.sublinear_tf_flag:# 對詞頻矩陣進行子線性變換term_frequency = np.log(term_frequency + 1)# 計算TF-IDF矩陣transformed_data = term_frequency * self.idf# 如果啟用了歸一化if self.normalization:# 計算每行的L2范數row_norm = (transformed_data ** 2).sum(axis=1)# 將范數為0的行的范數設為1,避免除零錯誤row_norm[row_norm == 0] = 1# 對TF-IDF矩陣進行歸一化處理transformed_data /= np.sqrt(row_norm)[:, None]# 返回轉換后的矩陣return transformed_data# 定義一個方法,用于擬合數據并進行轉換def fit_and_transform(self, input_data):# 調用fit_data方法擬合數據self.fit_data(input_data)# 調用transform_data方法進行轉換return self.transform_data(input_data)
nlp_text_clustering.py
# 從nlp_book_data_collection模塊導入BookDataCollection類
from nlp_book_data_collection import BookDataCollection
# 從nlp_tfidf_transformer模塊導入TFIDFTransformer類
from nlp_tfidf_transformer import TFIDFTransformer# 定義一個用于自然語言處理文本聚類的類
class NLPTextClustering:def __init__(self):passdef text_clustering(self):# 創建書籍數據集對象book_dataset = BookDataCollection()# 打印標簽到ID的映射print(book_dataset.id_to_label)# 對文本進行分詞處理book_dataset.tokenize_text(attribute='abstract')# 創建詞匯表book_dataset.create_vocabulary(min_frequency=3)# 將詞元轉換為索引book_dataset.convert_tokens_to_indices()# 獲取訓練集和測試集數據training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 獲取詞匯表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化訓練輸入列表training_input = []# 遍歷訓練數據for data_item in training_data:# 將詞元索引添加到訓練輸入列表training_input.append(data_item['token_indices'])# 創建TF-IDF轉換器對象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 擬合訓練數據tfidf_transformer.fit_data(training_input)# 轉換訓練數據training_features = tfidf_transformer.transform_data(training_input)# 打印訓練特征的形狀print(training_features.shape)# 程序入口,當作為腳本直接運行時執行以下代碼
if __name__ == "__main__":# 創建NLPTextClustering類的一個實例nlp_text_clustering = NLPTextClustering()# 調用實例的text_clustering方法進行文本聚類操作nlp_text_clustering.text_clustering()
特征信息運行結果
訓練集大小 = 8626 , 測試集大小 = 2156
{0: '計算機類', 1: '藝術傳媒類', 2: '經管類'}
100%|██████████| 8626/8626 [02:29<00:00, 57.74it/s]
100%|██████████| 2156/2156 [00:37<00:00, 56.76it/s]
唯一詞元數量 = 34251, 總詞元數量 = 806791, 最大詞頻 = 19194, 最小詞頻 = 1
最小詞頻 = 3, 最小詞長 = 2, 最大詞表大小 = None, 剩余詞元數量 = 9504, 詞表內詞元占比 = 0.8910448926673699
(8626, 9504)進程已結束,退出代碼為 0
當獲取到數據后,便可運用k均值聚類算法對文本展開聚類操作。在此過程中,一個關鍵的前提是要預先設定簇數。為了便于與實際的標簽數據進行對照分析,此次設定
值為 3。下面,我們用代碼來演示:
聚類操作完整代碼
nlp_kmeans_clusterer.py
# 導入NumPy庫,用于數值計算
import numpy as np# 定義一個類,用于實現K-Means聚類算法
class KMeansClusterer:# 類的初始化方法def __init__(self, num_clusters, data_dimension, stop_threshold=1e-4, max_steps=100):# 存儲聚類的數量self.num_clusters = num_clusters# 存儲數據的維度self.data_dimension = data_dimension# 存儲停止迭代的閾值self.stop_threshold = stop_threshold# 存儲最大迭代步數self.max_steps = max_steps# 定義一個方法,用于更新聚類中心def update_cluster_centers(self, input_data):# 初始化一個全零矩陣,用于存儲聚類中心cluster_centers = np.zeros([self.num_clusters, self.data_dimension])# 遍歷每個聚類for k in range(self.num_clusters):# 獲取屬于當前聚類的數據點cluster_data = input_data[self.cluster_assignments == k]# 如果當前聚類中有數據點if len(cluster_data) > 0:# 計算當前聚類的中心cluster_centers[k] = cluster_data.mean(axis=0)# 返回更新后的聚類中心return cluster_centers# 定義一個方法,用于擬合模型def fit_model(self, input_data):# 打印初始化信息print('-----------初始化-----------')# 獲取輸入數據的樣本數量num_samples = len(input_data)# 獲取輸入數據的維度data_dim = len(input_data[0])# 隨機初始化每個數據點的聚類分配self.cluster_assignments = np.random.randint(0, self.num_clusters, num_samples)# 調用update_cluster_centers方法更新聚類中心self.cluster_centers = self.update_cluster_centers(input_data)# 打印初始化完成信息print('-----------初始化完成-----------')# 初始化當前迭代步數為0current_step = 0# 當當前迭代步數小于最大迭代步數時,繼續迭代while current_step < self.max_steps:# 迭代步數加1current_step += 1# 初始化每個數據點的聚類分配為0self.cluster_assignments = np.zeros(num_samples, int)# 遍歷每個數據點for i, data_point in enumerate(input_data):# 計算每個數據點和每個簇中心的L2距離distances = np.linalg.norm(data_point[None, :] - self.cluster_centers, ord=2, axis=-1)# 找到每個數據點所屬新的聚類self.cluster_assignments[i] = distances.argmin(-1)# 調用update_cluster_centers方法更新聚類中心new_cluster_centers = self.update_cluster_centers(input_data)# 計算聚類中心的平均移動距離center_movement = np.linalg.norm(new_cluster_centers - self.cluster_centers, ord=2, axis=-1).mean()# 打印當前迭代步數和聚類中心的平均移動距離print(f"第{current_step}步,中心點平均移動距離:{center_movement}")# 如果聚類中心的平均移動距離小于停止迭代的閾值if center_movement < self.stop_threshold:# 打印停止迭代信息print("中心點不再移動,退出程序")# 跳出循環break# 更新聚類中心self.cluster_centers = new_cluster_centers
nlp_text_clustering.py
# 從nlp_book_data_collection模塊導入BookDataCollection類
from nlp_book_data_collection import BookDataCollection
# 從nlp_tfidf_transformer模塊導入TFIDFTransformer類
from nlp_tfidf_transformer import TFIDFTransformer
# 從nlp_kmeans_clusterer模塊導入KMeansClusterer類
from nlp_kmeans_clusterer import KMeansClusterer# 聚類的數量
num_clusters = 3# 定義一個用于自然語言處理文本聚類的類
class NLPTextClustering:def __init__(self):passdef text_clustering(self):# 創建書籍數據集對象book_dataset = BookDataCollection()# 打印標簽到ID的映射print(book_dataset.id_to_label)# 對文本進行分詞處理book_dataset.tokenize_text(attribute='abstract')# 創建詞匯表book_dataset.create_vocabulary(min_frequency=3)# 將詞元轉換為索引book_dataset.convert_tokens_to_indices()# 獲取訓練集和測試集數據training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 獲取詞匯表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化訓練輸入列表training_input = []# 遍歷訓練數據for data_item in training_data:# 將詞元索引添加到訓練輸入列表training_input.append(data_item['token_indices'])# 創建TF-IDF轉換器對象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 擬合訓練數據tfidf_transformer.fit_data(training_input)# 轉換訓練數據training_features = tfidf_transformer.transform_data(training_input)# 打印訓練特征的形狀print(training_features.shape)# 創建K-Means聚類器對象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 擬合訓練特征kmeans_clusterer.fit_model(training_features)# 程序入口,當作為腳本直接運行時執行以下代碼
if __name__ == "__main__":# 創建NLPTextClustering類的一個實例nlp_text_clustering = NLPTextClustering()# 調用實例的text_clustering方法進行文本聚類操作nlp_text_clustering.text_clustering()
訓練集大小 = 8626 , 測試集大小 = 2156
{0: '計算機類', 1: '藝術傳媒類', 2: '經管類'}
100%|██████████| 8626/8626 [03:05<00:00, 46.54it/s]
100%|██████████| 2156/2156 [00:36<00:00, 58.80it/s]
唯一詞元數量 = 34251, 總詞元數量 = 806791, 最大詞頻 = 19194, 最小詞頻 = 1
最小詞頻 = 3, 最小詞長 = 2, 最大詞表大小 = None, 剩余詞元數量 = 9504, 詞表內詞元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心點平均移動距離:0.06890890571733081
第2步,中心點平均移動距離:0.05478241286369078
第3步,中心點平均移動距離:0.018892396863314006
第4步,中心點平均移動距離:0.013429342825043227
第5步,中心點平均移動距離:0.013566480916429499
第6步,中心點平均移動距離:0.013989222795632706
第7步,中心點平均移動距離:0.011440477943083664
第8步,中心點平均移動距離:0.007103963059848575
第9步,中心點平均移動距離:0.006074946365616077
第10步,中心點平均移動距離:0.00477369638045405
第11步,中心點平均移動距離:0.0037873833801874888
第12步,中心點平均移動距離:0.003169778238863545
第13步,中心點平均移動距離:0.0026544650799152428
第14步,中心點平均移動距離:0.0029003953549483793
第15步,中心點平均移動距離:0.002845907460494008
第16步,中心點平均移動距離:0.0029565530700650586
第17步,中心點平均移動距離:0.0046409302413431995
第18步,中心點平均移動距離:0.007601779316323283
第19步,中心點平均移動距離:0.008556943854755123
第20步,中心點平均移動距離:0.007326270837156583
第21步,中心點平均移動距離:0.004774654456463306
第22步,中心點平均移動距離:0.004131223162570739
第23步,中心點平均移動距離:0.0034665383237183632
第24步,中心點平均移動距離:0.0028326750163131242
第25步,中心點平均移動距離:0.0024085910806691804
第26步,中心點平均移動距離:0.0025467882048793647
第27步,中心點平均移動距離:0.0024867293424594233
第28步,中心點平均移動距離:0.00232851482144344
第29步,中心點平均移動距離:0.002858996918517763
第30步,中心點平均移動距離:0.0027176858630063097
第31步,中心點平均移動距離:0.0024908107307621374
第32步,中心點平均移動距離:0.002725824149863784
第33步,中心點平均移動距離:0.0029918663142641765
第34步,中心點平均移動距離:0.0021179114918197877
第35步,中心點平均移動距離:0.0019590759238497786
第36步,中心點平均移動距離:0.0021350571707413643
第37步,中心點平均移動距離:0.0029617190015900938
第38步,中心點平均移動距離:0.0033936270370122233
第39步,中心點平均移動距離:0.0029231439612436316
第40步,中心點平均移動距離:0.0032854686271343914
第41步,中心點平均移動距離:0.0031848632066834794
第42步,中心點平均移動距離:0.003748152169792271
第43步,中心點平均移動距離:0.0034990023467288468
第44步,中心點平均移動距離:0.003588666039850141
第45步,中心點平均移動距離:0.003444720739168902
第46步,中心點平均移動距離:0.004132389860665119
第47步,中心點平均移動距離:0.0042080010326193
第48步,中心點平均移動距離:0.004440204058509182
第49步,中心點平均移動距離:0.002555459672126323
第50步,中心點平均移動距離:0.0025740606612964256
第51步,中心點平均移動距離:0.0017377711466915063
第52步,中心點平均移動距離:0.001173105458515561
第53步,中心點平均移動距離:0.0014038477302960907
第54步,中心點平均移動距離:0.002010183332161079
第55步,中心點平均移動距離:0.0025465099651279664
第56步,中心點平均移動距離:0.003412787991913555
第57步,中心點平均移動距離:0.00491581405490893
第58步,中心點平均移動距離:0.006009725805675484
第59步,中心點平均移動距離:0.0030275349972018945
第60步,中心點平均移動距離:0.0023261247949924505
第61步,中心點平均移動距離:0.0019728143627690867
第62步,中心點平均移動距離:0.0021625513846364737
第63步,中心點平均移動距離:0.001665616845718412
第64步,中心點平均移動距離:0.000931590997003939
第65步,中心點平均移動距離:0.001013041855168734
第66步,中心點平均移動距離:0.0008405371362220621
第67步,中心點平均移動距離:0.001367543177768866
第68步,中心點平均移動距離:0.0014425643303775304
第69步,中心點平均移動距離:0.001116963591121085
第70步,中心點平均移動距離:0.001491048450742236
第71步,中心點平均移動距離:0.002040608920857972
第72步,中心點平均移動距離:0.0036833183433489704
第73步,中心點平均移動距離:0.010667221023078093
第74步,中心點平均移動距離:0.022518116498817724
第75步,中心點平均移動距離:0.019497945687248934
第76步,中心點平均移動距離:0.012781228163674337
第77步,中心點平均移動距離:0.006360174137399897
第78步,中心點平均移動距離:0.004201484107008095
第79步,中心點平均移動距離:0.0035826113950724998
第80步,中心點平均移動距離:0.0037105073591507513
第81步,中心點平均移動距離:0.00219649791629415
第82步,中心點平均移動距離:0.0015802391154533663
第83步,中心點平均移動距離:0.001539134331937778
第84步,中心點平均移動距離:0.0
中心點不再移動,退出程序進程已結束,退出代碼為 0
為了能更直觀地呈現聚類效果,我們定義了display_clusters()函數,它的作用是展示每個真實分類下各個簇的占比情況。接下來,我們會利用這個函數展示k均值算法的聚類結果,借此觀察3個標簽中不同簇的占比情況。 我們用代碼來演示:
聚類結果完整代碼
nlp_text_clustering.py
# 從nlp_book_data_collection模塊導入BookDataCollection類
from nlp_book_data_collection import BookDataCollection
# 從nlp_tfidf_transformer模塊導入TFIDFTransformer類
from nlp_tfidf_transformer import TFIDFTransformer
# 從nlp_kmeans_clusterer模塊導入KMeansClusterer類
from nlp_kmeans_clusterer import KMeansClusterer# 聚類的數量
num_clusters = 3# 定義一個用于自然語言處理文本聚類的類
class NLPTextClustering:def __init__(self):passdef display_clusters(self, cluster_assignments, num_clusters, book_dataset, labels_list):# 初始化標簽聚類字典label_clusters = {label_id: {} for label_id in book_dataset.id_to_label}# 初始化每個標簽下每個聚類的計數為0for k, v in label_clusters.items():label_clusters[k] = {i: 0 for i in range(num_clusters)}# 遍歷標簽和聚類分配for label_id, cluster_id in zip(labels_list, cluster_assignments):# 增加對應標簽和聚類的計數label_clusters[label_id][cluster_id] += 1# 按標簽ID排序遍歷for label_id in sorted(book_dataset.id_to_label.keys()):# 初始化聚類信息字符串cluster_str = book_dataset.id_to_label[label_id] + ':\t{ '# 遍歷每個聚類for cluster_id in range(num_clusters):# 獲取當前聚類的計數count = label_clusters[label_id][cluster_id]# 計算總數total = sum(label_clusters[label_id].values())# 拼接聚類信息cluster_str += f'{str(cluster_id)}: {count}({count / total:.2f}), '# 結束聚類信息字符串cluster_str += '}'# 打印聚類信息print(cluster_str)def text_clustering(self):# 創建書籍數據集對象book_dataset = BookDataCollection()# 打印標簽到ID的映射print(book_dataset.id_to_label)# 對文本進行分詞處理book_dataset.tokenize_text(attribute='abstract')# 創建詞匯表book_dataset.create_vocabulary(min_frequency=3)# 將詞元轉換為索引book_dataset.convert_tokens_to_indices()# 獲取訓練集和測試集數據training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 獲取詞匯表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化訓練輸入列表training_input = []# 遍歷訓練數據for data_item in training_data:# 將詞元索引添加到訓練輸入列表training_input.append(data_item['token_indices'])# 創建TF-IDF轉換器對象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 擬合訓練數據tfidf_transformer.fit_data(training_input)# 轉換訓練數據training_features = tfidf_transformer.transform_data(training_input)# 打印訓練特征的形狀print(training_features.shape)# 創建K-Means聚類器對象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 擬合訓練特征kmeans_clusterer.fit_model(training_features)# 初始化標簽列表labels_list = []# 遍歷訓練數據for data_item in training_data:# 將標簽添加到標簽列表labels_list.append(data_item['label'])# 打印標簽列表的長度print(len(labels_list))# 獲取K-Means聚類結果cluster_assignments = kmeans_clusterer.cluster_assignments# 顯示K-Means聚類結果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 程序入口,當作為腳本直接運行時執行以下代碼
if __name__ == "__main__":# 創建NLPTextClustering類的一個實例nlp_text_clustering = NLPTextClustering()# 調用實例的text_clustering方法進行文本聚類操作nlp_text_clustering.text_clustering()
聚類結果運行結果
訓練集大小 = 8626 , 測試集大小 = 2156
{0: '計算機類', 1: '藝術傳媒類', 2: '經管類'}
100%|██████████| 8626/8626 [03:00<00:00, 47.89it/s]
100%|██████████| 2156/2156 [00:36<00:00, 58.84it/s]
唯一詞元數量 = 34251, 總詞元數量 = 806791, 最大詞頻 = 19194, 最小詞頻 = 1
最小詞頻 = 3, 最小詞長 = 2, 最大詞表大小 = None, 剩余詞元數量 = 9504, 詞表內詞元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心點平均移動距離:0.07187923654672244
第2步,中心點平均移動距離:0.044157507688436216
第3步,中心點平均移動距離:0.022896531888343605
第4步,中心點平均移動距離:0.0159488039593934
第5步,中心點平均移動距離:0.012182250613021412
第6步,中心點平均移動距離:0.012091131336056611
第7步,中心點平均移動距離:0.011953069179042916
第8步,中心點平均移動距離:0.007551366337940332
第9步,中心點平均移動距離:0.00607274740895354
第10步,中心點平均移動距離:0.0051005041228621905
第11步,中心點平均移動距離:0.0037475278494419353
第12步,中心點平均移動距離:0.002873947676155657
第13步,中心點平均移動距離:0.0032856197227294393
第14步,中心點平均移動距離:0.0037597515304094073
第15步,中心點平均移動距離:0.005154214384698833
第16步,中心點平均移動距離:0.01429702264635499
第17步,中心點平均移動距離:0.011662801294254196
第18步,中心點平均移動距離:0.01061592695683227
第19步,中心點平均移動距離:0.01964312337838108
第20步,中心點平均移動距離:0.012665767903761787
第21步,中心點平均移動距離:0.005392861470868378
第22步,中心點平均移動距離:0.002772730322582759
第23步,中心點平均移動距離:0.0018288923370850228
第24步,中心點平均移動距離:0.0014922387193858154
第25步,中心點平均移動距離:0.0010964808376660166
第26步,中心點平均移動距離:0.0011809417903345324
第27步,中心點平均移動距離:0.0008189027391701968
第28步,中心點平均移動距離:0.0008794506802535549
第29步,中心點平均移動距離:0.0003873751480754425
第30步,中心點平均移動距離:0.00023008235702405927
第31步,中心點平均移動距離:0.0
中心點不再移動,退出程序
8626
計算機類: { 0: 397(0.10), 1: 845(0.22), 2: 2600(0.68), }
藝術傳媒類: { 0: 177(0.08), 1: 2115(0.92), 2: 7(0.00), }
經管類: { 0: 2408(0.97), 1: 36(0.01), 2: 41(0.02), }進程已結束,退出代碼為 0
基于高斯混合模型的最大期望值算法
基于高斯混合模型的最大期望值算法則是另一種強大的文本聚類方法。高斯混合模型認為文本數據是由多個高斯分布混合而成的,就好像是把不同顏色的顏料混合在一起。
最大期望值算法通過不斷地迭代,分為期望步驟和最大化步驟。在期望步驟中,根據當前的模型參數,計算每個文本屬于不同高斯分布的概率,就像是猜測每個文本更可能屬于哪一種“顏料”;在最大化步驟中,利用期望步驟得到的概率,重新計算模型的參數,比如高斯分布的均值、方差和權重,就像調整顏料的混合比例。通過這樣反復迭代,讓模型更好地擬合文本數據,從而實現文本聚類。
這種算法的優勢在于能夠靈活地擬合復雜的數據分布,對于多模態的數據聚類效果很好,而且它有完善的理論基礎。不過,它也存在一些問題,比如計算復雜度比較高,對超參數的選擇很敏感,像高斯分布的數量、協方差類型等,而且容易陷入局部最優解,就像在一個復雜的迷宮中,容易被困在某個局部區域,找不到全局最優的聚類結果。
下面,我們來演示怎樣運用高斯混合模型進行聚類操作。需要注意的是,高斯混合模型會計算每個數據點屬于各個簇的概率分布,在實際應用中,我們通常把概率最高的簇作為最終的聚類輸出結果。通過這種方式,我們就能依據數據點的概率分布情況,將它們劃分到最合適的簇中,實現文本數據的有效聚類。我們用代碼來演示:
高斯混合聚類完整代碼
nlp_gaussian_mixture_model.py
# 導入NumPy庫,用于數值計算
import numpy as np
# 從scipy庫中導入多元正態分布函數
from scipy.stats import multivariate_normal as gaussian# 定義高斯混合模型類
class GaussianMixtureModel:# 初始化方法,設置聚類數量、數據維度和最大迭代次數def __init__(self, num_clusters, data_dimension, max_iterations=100):# 存儲聚類數量self.num_clusters = num_clusters# 存儲數據維度self.data_dimension = data_dimension# 存儲最大迭代次數self.max_iterations = max_iterations# 初始化混合系數,每個聚類初始權重相同self.mixing_coefficients = np.ones(num_clusters) / num_clusters# 隨機初始化每個聚類的均值self.means = np.random.rand(num_clusters, data_dimension) * 2 - 1# 初始化協方差矩陣為全零矩陣self.covariances = np.zeros((num_clusters, data_dimension, data_dimension))# 遍歷每個聚類for i in range(num_clusters):# 將每個聚類的協方差矩陣設為單位矩陣self.covariances[i] = np.eye(data_dimension)# 期望步驟,計算每個數據點屬于每個聚類的概率def expectation_step(self, input_data):# 遍歷每個聚類for i in range(self.num_clusters):# 計算每個數據點屬于當前聚類的概率self.responsibilities[:, i] = self.mixing_coefficients[i] * gaussian.pdf(input_data, mean=self.means[i],cov=self.covariances[i])# 對概率進行歸一化處理self.responsibilities /= self.responsibilities.sum(axis=1, keepdims=True)# 最大化步驟,更新模型參數def maximization_step(self, input_data):# 計算每個聚類的責任權重總和responsibility_sum = self.responsibilities.sum(axis=0)# 更新混合系數self.mixing_coefficients = responsibility_sum / self.num_samples# 更新每個聚類的均值self.means = np.matmul(self.responsibilities.T, input_data) / responsibility_sum[:, None]# 遍歷每個聚類for i in range(self.num_clusters):# 計算數據點與當前聚類均值的差值delta = np.expand_dims(input_data, axis=1) - self.means[i]# 計算協方差矩陣covariance = np.matmul(delta.transpose(0, 2, 1), delta)# 更新當前聚類的協方差矩陣self.covariances[i] = np.matmul(covariance.transpose(1, 2, 0), self.responsibilities[:, i]) / \responsibility_sum[i]# 計算對數似然值def log_likelihood_value(self, input_data):# 初始化對數似然值為0log_likelihood = 0# 遍歷每個數據點for data_point in input_data:# 初始化概率為0probability = 0# 遍歷每個聚類for i in range(self.num_clusters):# 累加數據點屬于當前聚類的概率probability += self.mixing_coefficients[i] * gaussian.pdf(data_point, mean=self.means[i],cov=self.covariances[i])# 累加對數概率log_likelihood += np.log(probability)# 返回平均對數似然值return log_likelihood / self.num_samples# 擬合模型def fit_model(self, input_data):# 存儲輸入數據的樣本數量self.num_samples = len(input_data)# 初始化責任矩陣為全零矩陣self.responsibilities = np.zeros((self.num_samples, self.num_clusters))# 計算初始對數似然值log_likelihood = self.log_likelihood_value(input_data)# 打印開始迭代信息print('開始迭代')# 開始迭代for i in range(self.max_iterations):# 執行期望步驟self.expectation_step(input_data)# 執行最大化步驟self.maximization_step(input_data)# 計算新的對數似然值new_log_likelihood = self.log_likelihood_value(input_data)# 打印當前迭代步數和對數似然值print(f'第{i}步, log-likelihood = {new_log_likelihood:.4f}')# 如果對數似然值變化小于閾值,停止迭代if new_log_likelihood - log_likelihood < 1e-4:print('log-likelihood不再變化,退出程序')breakelse:# 更新對數似然值log_likelihood = new_log_likelihood# 轉換數據,返回每個數據點所屬的聚類def transform_data(self, input_data):# 確保責任矩陣已計算且長度與輸入數據一致assert hasattr(self, 'responsibilities') and len(self.responsibilities) == len(input_data)# 返回每個數據點所屬的聚類索引return np.argmax(self.responsibilities, axis=1)# 擬合并轉換數據def fit_and_transform(self, input_data):# 擬合模型self.fit_model(input_data)# 轉換數據return self.transform_data(input_data)
和k均值聚類方法相似,在運用基于最大期望值法的高斯混合模型時,我們來觀察一下在Books數據集中,3個真實類別里不同簇各自所占的比例情況:
nlp_text_clustering.py
# 從nlp_book_data_collection模塊導入BookDataCollection類
from nlp_book_data_collection import BookDataCollection
from nlp_gaussian_mixture_model import GaussianMixtureModel
# 從nlp_tfidf_transformer模塊導入TFIDFTransformer類
from nlp_tfidf_transformer import TFIDFTransformer
# 從nlp_kmeans_clusterer模塊導入KMeansClusterer類
from nlp_kmeans_clusterer import KMeansClusterer
# 從sklearn庫中導入主成分分析(PCA)類
from sklearn.decomposition import PCA# 聚類的數量
num_clusters = 3# 定義一個用于自然語言處理文本聚類的類
class NLPTextClustering:def __init__(self):passdef display_clusters(self, cluster_assignments, num_clusters, book_dataset, labels_list):# 初始化標簽聚類字典label_clusters = {label_id: {} for label_id in book_dataset.id_to_label}# 初始化每個標簽下每個聚類的計數為0for k, v in label_clusters.items():label_clusters[k] = {i: 0 for i in range(num_clusters)}# 遍歷標簽和聚類分配for label_id, cluster_id in zip(labels_list, cluster_assignments):# 增加對應標簽和聚類的計數label_clusters[label_id][cluster_id] += 1# 按標簽ID排序遍歷for label_id in sorted(book_dataset.id_to_label.keys()):# 初始化聚類信息字符串cluster_str = book_dataset.id_to_label[label_id] + ':\t{ '# 遍歷每個聚類for cluster_id in range(num_clusters):# 獲取當前聚類的計數count = label_clusters[label_id][cluster_id]# 計算總數total = sum(label_clusters[label_id].values())# 拼接聚類信息cluster_str += f'{str(cluster_id)}: {count}({count / total:.2f}), '# 結束聚類信息字符串cluster_str += '}'# 打印聚類信息print(cluster_str)def text_clustering(self):# 創建書籍數據集對象book_dataset = BookDataCollection()# 打印標簽到ID的映射print(book_dataset.id_to_label)# 對文本進行分詞處理book_dataset.tokenize_text(attribute='abstract')# 創建詞匯表book_dataset.create_vocabulary(min_frequency=3)# 將詞元轉換為索引book_dataset.convert_tokens_to_indices()# 獲取訓練集和測試集數據training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 獲取詞匯表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化訓練輸入列表training_input = []# 遍歷訓練數據for data_item in training_data:# 將詞元索引添加到訓練輸入列表training_input.append(data_item['token_indices'])# 創建TF-IDF轉換器對象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 擬合訓練數據tfidf_transformer.fit_data(training_input)# 轉換訓練數據training_features = tfidf_transformer.transform_data(training_input)# 打印訓練特征的形狀print(training_features.shape)# 創建K-Means聚類器對象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 擬合訓練特征kmeans_clusterer.fit_model(training_features)# 初始化標簽列表labels_list = []# 遍歷訓練數據for data_item in training_data:# 將標簽添加到標簽列表labels_list.append(data_item['label'])# 打印標簽列表的長度print(len(labels_list))# 獲取K-Means聚類結果cluster_assignments = kmeans_clusterer.cluster_assignments# 顯示K-Means聚類結果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 創建PCA降維器對象pca_reducer = PCA(n_components=50)# 對訓練特征進行降維training_pca = pca_reducer.fit_transform(training_features)# 創建高斯混合模型對象gmm_model = GaussianMixtureModel(num_clusters, data_dimension=training_pca.shape[1])# 擬合并轉換訓練數據cluster_assignments = gmm_model.fit_and_transform(training_pca)# 打印高斯混合模型聚類結果print(cluster_assignments)# 顯示高斯混合模型聚類結果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 程序入口,當作為腳本直接運行時執行以下代碼
if __name__ == "__main__":# 創建NLPTextClustering類的一個實例nlp_text_clustering = NLPTextClustering()# 調用實例的text_clustering方法進行文本聚類操作nlp_text_clustering.text_clustering()
高斯混合聚類運行結果
訓練集大小 = 8626 , 測試集大小 = 2156
{0: '計算機類', 1: '藝術傳媒類', 2: '經管類'}
100%|██████████| 8626/8626 [02:36<00:00, 54.95it/s]
100%|██████████| 2156/2156 [00:36<00:00, 59.32it/s]
唯一詞元數量 = 34251, 總詞元數量 = 806791, 最大詞頻 = 19194, 最小詞頻 = 1
最小詞頻 = 3, 最小詞長 = 2, 最大詞表大小 = None, 剩余詞元數量 = 9504, 詞表內詞元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心點平均移動距離:0.07821463876668384
第2步,中心點平均移動距離:0.05703827383757104
第3步,中心點平均移動距離:0.0227878128221168
第4步,中心點平均移動距離:0.012985270767233279
第5步,中心點平均移動距離:0.010560323354748833
第6步,中心點平均移動距離:0.011526155533891311
第7步,中心點平均移動距離:0.014447526451567915
第8步,中心點平均移動距離:0.01480361614434264
第9步,中心點平均移動距離:0.007805741764265261
第10步,中心點平均移動距離:0.004696899248898042
第11步,中心點平均移動距離:0.003858954772666219
第12步,中心點平均移動距離:0.0037634051652670757
第13步,中心點平均移動距離:0.0036342433215190346
第14步,中心點平均移動距離:0.004037746948698562
第15步,中心點平均移動距離:0.0037074902463420808
第16步,中心點平均移動距離:0.0025126860295755764
第17步,中心點平均移動距離:0.0017742952721109479
第18步,中心點平均移動距離:0.001311558516669006
第19步,中心點平均移動距離:0.0008183675564787064
第20步,中心點平均移動距離:0.0011545852276947674
第21步,中心點平均移動距離:0.0006864544106957334
第22步,中心點平均移動距離:0.0009260959472041178
第23步,中心點平均移動距離:0.0004974572613901904
第24步,中心點平均移動距離:0.00047763554726450624
第25步,中心點平均移動距離:0.000561811847711657
第26步,中心點平均移動距離:0.00030713124482862106
第27步,中心點平均移動距離:0.0
中心點不再移動,退出程序
8626
計算機類: { 0: 1233(0.32), 1: 2572(0.67), 2: 37(0.01), }
藝術傳媒類: { 0: 74(0.03), 1: 282(0.12), 2: 1943(0.85), }
經管類: { 0: 26(0.01), 1: 2452(0.99), 2: 7(0.00), }
開始迭代
第0步, log-likelihood = 77.2117
第1步, log-likelihood = 87.3795
第2步, log-likelihood = 90.4587
第3步, log-likelihood = 91.7301
第4步, log-likelihood = 92.7042
第5步, log-likelihood = 93.5868
第6步, log-likelihood = 94.6852
第7步, log-likelihood = 95.8890
第8步, log-likelihood = 96.4726
第9步, log-likelihood = 96.8001
第10步, log-likelihood = 96.9560
第11步, log-likelihood = 97.0857
第12步, log-likelihood = 97.5848
第13步, log-likelihood = 97.5959
第14步, log-likelihood = 97.6033
第15步, log-likelihood = 97.6140
第16步, log-likelihood = 97.6341
第17步, log-likelihood = 97.6695
第18步, log-likelihood = 97.6801
第19步, log-likelihood = 97.6888
第20步, log-likelihood = 97.7062
第21步, log-likelihood = 97.7420
第22步, log-likelihood = 97.7711
第23步, log-likelihood = 97.8122
第24步, log-likelihood = 97.8207
第25步, log-likelihood = 97.8232
第26步, log-likelihood = 97.8244
第27步, log-likelihood = 97.8255
第28步, log-likelihood = 97.8284
第29步, log-likelihood = 97.8330
第30步, log-likelihood = 97.8486
第31步, log-likelihood = 97.9543
第32步, log-likelihood = 98.0578
第33步, log-likelihood = 98.0620
第34步, log-likelihood = 98.0625
第35步, log-likelihood = 98.0626
第36步, log-likelihood = 98.0628
第37步, log-likelihood = 98.0628
log-likelihood不再變化,退出程序
[0 2 0 ... 2 0 2]
計算機類: { 0: 2742(0.71), 1: 299(0.08), 2: 801(0.21), }
藝術傳媒類: { 0: 9(0.00), 1: 421(0.18), 2: 1869(0.81), }
經管類: { 0: 829(0.33), 1: 1625(0.65), 2: 31(0.01), }進程已結束,退出代碼為 0
無監督樸素貝葉斯模型
無監督樸素貝葉斯模型也是文本聚類的有力工具。它基于貝葉斯定理和特征條件獨立假設,認為文本中的各個詞之間是相互獨立的,就好像每個人都是獨立行動的個體。在文本聚類時,它通過統計文本中詞的出現頻率,結合貝葉斯定理,計算文本屬于不同主題(類別)的概率,然后把概率相近的文本歸為一類。
這種算法的優點是簡單高效,對數據量的要求相對較低,可擴展性也不錯。然而,它的特征條件獨立假設在實際情況中往往不太符合,因為文本中的詞之間其實是有語義關聯的;而且它對多義詞的處理能力有限,可能會把不同含義的同一個詞都當作相同的來處理,影響聚類的準確性;此外,模型的性能很大程度上依賴于文本的表示方式,如果文本表示不準確,聚類效果也會受到影響。
接下來,為大家展示基于樸素貝葉斯模型的聚類算法具體是如何實現的:
樸素貝葉斯模型聚類完整代碼
nlp_unsupervised_naive_bayes_classifier.py
# 導入NumPy庫,用于數值計算
import numpy as np
# 從scipy庫中導入對數求和指數函數
from scipy.special import logsumexp# 定義無監督樸素貝葉斯分類器類
class UnsupervisedNaiveBayesClassifier:# 初始化方法,設置聚類數量、數據維度和最大迭代次數def __init__(self, num_clusters, data_dimension, max_iterations=100):# 存儲聚類數量self.num_clusters = num_clusters# 存儲數據維度self.data_dimension = data_dimension# 存儲最大迭代次數self.max_iterations = max_iterations# 初始化先驗概率,每個聚類初始概率相同self.prior_probabilities = np.ones(num_clusters) / num_clusters# 隨機初始化條件概率self.conditional_probabilities = np.random.random((num_clusters, data_dimension))# 對條件概率進行歸一化處理self.conditional_probabilities /= self.conditional_probabilities.sum(axis=1, keepdims=True)# 期望步驟,計算每個數據點屬于每個聚類的概率def expectation_step(self, input_data):# 遍歷每個數據點for i, data_point in enumerate(input_data):# 計算對數概率self.responsibilities[i, :] = np.log(self.prior_probabilities) + (np.log(self.conditional_probabilities) * data_point).sum(axis=1)# 減去對數求和指數,避免數值溢出self.responsibilities[i, :] -= logsumexp(self.responsibilities[i, :])# 計算概率self.responsibilities[i, :] = np.exp(self.responsibilities[i, :])# 最大化步驟,更新模型參數def maximization_step(self, input_data):# 計算每個聚類的責任權重總和self.prior_probabilities = self.responsibilities.sum(axis=0) / self.num_samples# 對先驗概率進行歸一化處理self.prior_probabilities /= self.prior_probabilities.sum()# 遍歷每個聚類for i in range(self.num_clusters):# 更新條件概率self.conditional_probabilities[i] = (self.responsibilities[:, i:i + 1] * input_data).sum(axis=0) / \(self.responsibilities[:, i] * input_data.sum(axis=1)).sum()# 避免條件概率為0self.conditional_probabilities += 1e-10# 對條件概率進行歸一化處理self.conditional_probabilities /= self.conditional_probabilities.sum(axis=1, keepdims=True)# 計算對數似然值def log_likelihood_value(self, input_data):# 初始化對數似然值為0log_likelihood = 0# 遍歷每個數據點for data_point in input_data:# 存儲每個聚類的對數概率log_probabilities = []# 遍歷每個聚類for i in range(self.num_clusters):# 計算對數概率log_probabilities.append(np.log(self.prior_probabilities[i]) + (np.log(self.conditional_probabilities[i]) * data_point).sum())# 累加對數求和指數log_likelihood += logsumexp(log_probabilities)# 返回平均對數似然值return log_likelihood / len(input_data)# 擬合模型def fit_model(self, input_data):# 存儲輸入數據的樣本數量self.num_samples = len(input_data)# 初始化責任矩陣為全零矩陣self.responsibilities = np.zeros((self.num_samples, self.num_clusters))# 計算初始對數似然值log_likelihood = self.log_likelihood_value(input_data)# 打印初始對數似然值print(f'初始化log-likelihood = {log_likelihood:.4f}')# 打印開始迭代信息print('開始迭代')# 開始迭代for i in range(self.max_iterations):# 執行期望步驟self.expectation_step(input_data)# 執行最大化步驟self.maximization_step(input_data)# 計算新的對數似然值new_log_likelihood = self.log_likelihood_value(input_data)# 打印當前迭代步數和對數似然值print(f'第{i}步, log-likelihood = {new_log_likelihood:.4f}')# 如果對數似然值變化小于閾值,停止迭代if new_log_likelihood - log_likelihood < 1e-4:print('log-likelihood不再變化,退出程序')breakelse:# 更新對數似然值log_likelihood = new_log_likelihood# 轉換數據,返回每個數據點所屬的聚類def transform_data(self, input_data):# 確保責任矩陣已計算且長度與輸入數據一致assert hasattr(self, 'responsibilities') and len(self.responsibilities) == len(input_data)# 返回每個數據點所屬的聚類索引return np.argmax(self.responsibilities, axis=1)# 擬合并轉換數據def fit_and_transform(self, input_data):# 擬合模型self.fit_model(input_data)# 轉換數據return self.transform_data(input_data)
nlp_text_clustering.py
# 從nlp_book_data_collection模塊導入BookDataCollection類
from nlp_book_data_collection import BookDataCollection
from nlp_gaussian_mixture_model import GaussianMixtureModel
# 從nlp_tfidf_transformer模塊導入TFIDFTransformer類
from nlp_tfidf_transformer import TFIDFTransformer
# 從nlp_kmeans_clusterer模塊導入KMeansClusterer類
from nlp_kmeans_clusterer import KMeansClusterer
# 從sklearn庫中導入主成分分析(PCA)類
from sklearn.decomposition import PCA
# 從nlp_unsupervised_naive_bayes_classifier模塊導入UnsupervisedNaiveBayesClassifier類
from nlp_unsupervised_naive_bayes_classifier import UnsupervisedNaiveBayesClassifier
# 導入NumPy庫,用于數值計算
import numpy as np# 聚類的數量
num_clusters = 3# 定義一個用于自然語言處理文本聚類的類
class NLPTextClustering:def __init__(self):passdef display_clusters(self, cluster_assignments, num_clusters, book_dataset, labels_list):# 初始化標簽聚類字典label_clusters = {label_id: {} for label_id in book_dataset.id_to_label}# 初始化每個標簽下每個聚類的計數為0for k, v in label_clusters.items():label_clusters[k] = {i: 0 for i in range(num_clusters)}# 遍歷標簽和聚類分配for label_id, cluster_id in zip(labels_list, cluster_assignments):# 增加對應標簽和聚類的計數label_clusters[label_id][cluster_id] += 1# 按標簽ID排序遍歷for label_id in sorted(book_dataset.id_to_label.keys()):# 初始化聚類信息字符串cluster_str = book_dataset.id_to_label[label_id] + ':\t{ '# 遍歷每個聚類for cluster_id in range(num_clusters):# 獲取當前聚類的計數count = label_clusters[label_id][cluster_id]# 計算總數total = sum(label_clusters[label_id].values())# 拼接聚類信息cluster_str += f'{str(cluster_id)}: {count}({count / total:.2f}), '# 結束聚類信息字符串cluster_str += '}'# 打印聚類信息print(cluster_str)def text_clustering(self):# 創建書籍數據集對象book_dataset = BookDataCollection()# 打印標簽到ID的映射print(book_dataset.id_to_label)# 對文本進行分詞處理book_dataset.tokenize_text(attribute='abstract')# 創建詞匯表book_dataset.create_vocabulary(min_frequency=3)# 將詞元轉換為索引book_dataset.convert_tokens_to_indices()# 獲取訓練集和測試集數據training_data, testing_data = book_dataset.training_data, book_dataset.testing_data# 獲取詞匯表大小vocabulary_size = len(book_dataset.token_to_id)# 初始化訓練輸入列表training_input = []# 遍歷訓練數據for data_item in training_data:# 將詞元索引添加到訓練輸入列表training_input.append(data_item['token_indices'])# 創建TF-IDF轉換器對象tfidf_transformer = TFIDFTransformer(vocabulary_size, normalization='l2', smooth_idf_flag=True,sublinear_tf_flag=True)# 擬合訓練數據tfidf_transformer.fit_data(training_input)# 轉換訓練數據training_features = tfidf_transformer.transform_data(training_input)# 打印訓練特征的形狀print(training_features.shape)# 創建K-Means聚類器對象kmeans_clusterer = KMeansClusterer(num_clusters, training_features.shape[1])# 擬合訓練特征kmeans_clusterer.fit_model(training_features)# 初始化標簽列表labels_list = []# 遍歷訓練數據for data_item in training_data:# 將標簽添加到標簽列表labels_list.append(data_item['label'])# 打印標簽列表的長度print(len(labels_list))# 獲取K-Means聚類結果cluster_assignments = kmeans_clusterer.cluster_assignments# 顯示K-Means聚類結果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 創建PCA降維器對象pca_reducer = PCA(n_components=50)# 對訓練特征進行降維training_pca = pca_reducer.fit_transform(training_features)# 創建高斯混合模型對象gmm_model = GaussianMixtureModel(num_clusters, data_dimension=training_pca.shape[1])# 擬合并轉換訓練數據cluster_assignments = gmm_model.fit_and_transform(training_pca)# 打印高斯混合模型聚類結果print(cluster_assignments)# 顯示高斯混合模型聚類結果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 初始化訓練計數矩陣training_count_matrix = np.zeros((len(training_input), vocabulary_size))# 遍歷訓練輸入for i, data_item in enumerate(training_input):# 遍歷詞元索引for token_index in data_item:# 增加對應位置的計數training_count_matrix[i, token_index] += 1# 創建無監督樸素貝葉斯分類器對象naive_bayes_classifier = UnsupervisedNaiveBayesClassifier(num_clusters, data_dimension=vocabulary_size,max_iterations=100)# 擬合并轉換訓練數據cluster_assignments = naive_bayes_classifier.fit_and_transform(training_count_matrix)# 打印無監督樸素貝葉斯分類器聚類結果print(cluster_assignments)# 顯示無監督樸素貝葉斯分類器聚類結果self.display_clusters(cluster_assignments, num_clusters, book_dataset, labels_list)# 程序入口,當作為腳本直接運行時執行以下代碼
if __name__ == "__main__":# 創建NLPTextClustering類的一個實例nlp_text_clustering = NLPTextClustering()# 調用實例的text_clustering方法進行文本聚類操作nlp_text_clustering.text_clustering()
樸素貝葉斯模型聚類運行結果
訓練集大小 = 8626 , 測試集大小 = 2156
{0: '計算機類', 1: '藝術傳媒類', 2: '經管類'}
100%|██████████| 8626/8626 [02:36<00:00, 54.95it/s]
100%|██████████| 2156/2156 [00:36<00:00, 59.32it/s]
唯一詞元數量 = 34251, 總詞元數量 = 806791, 最大詞頻 = 19194, 最小詞頻 = 1
最小詞頻 = 3, 最小詞長 = 2, 最大詞表大小 = None, 剩余詞元數量 = 9504, 詞表內詞元占比 = 0.8910448926673699
(8626, 9504)
-----------初始化-----------
-----------初始化完成-----------
第1步,中心點平均移動距離:0.07821463876668384
第2步,中心點平均移動距離:0.05703827383757104
第3步,中心點平均移動距離:0.0227878128221168
第4步,中心點平均移動距離:0.012985270767233279
第5步,中心點平均移動距離:0.010560323354748833
第6步,中心點平均移動距離:0.011526155533891311
第7步,中心點平均移動距離:0.014447526451567915
第8步,中心點平均移動距離:0.01480361614434264
第9步,中心點平均移動距離:0.007805741764265261
第10步,中心點平均移動距離:0.004696899248898042
第11步,中心點平均移動距離:0.003858954772666219
第12步,中心點平均移動距離:0.0037634051652670757
第13步,中心點平均移動距離:0.0036342433215190346
第14步,中心點平均移動距離:0.004037746948698562
第15步,中心點平均移動距離:0.0037074902463420808
第16步,中心點平均移動距離:0.0025126860295755764
第17步,中心點平均移動距離:0.0017742952721109479
第18步,中心點平均移動距離:0.001311558516669006
第19步,中心點平均移動距離:0.0008183675564787064
第20步,中心點平均移動距離:0.0011545852276947674
第21步,中心點平均移動距離:0.0006864544106957334
第22步,中心點平均移動距離:0.0009260959472041178
第23步,中心點平均移動距離:0.0004974572613901904
第24步,中心點平均移動距離:0.00047763554726450624
第25步,中心點平均移動距離:0.000561811847711657
第26步,中心點平均移動距離:0.00030713124482862106
第27步,中心點平均移動距離:0.0
中心點不再移動,退出程序
8626
計算機類: { 0: 1233(0.32), 1: 2572(0.67), 2: 37(0.01), }
藝術傳媒類: { 0: 74(0.03), 1: 282(0.12), 2: 1943(0.85), }
經管類: { 0: 26(0.01), 1: 2452(0.99), 2: 7(0.00), }
開始迭代
第0步, log-likelihood = 77.2117
第1步, log-likelihood = 87.3795
第2步, log-likelihood = 90.4587
第3步, log-likelihood = 91.7301
第4步, log-likelihood = 92.7042
第5步, log-likelihood = 93.5868
第6步, log-likelihood = 94.6852
第7步, log-likelihood = 95.8890
第8步, log-likelihood = 96.4726
第9步, log-likelihood = 96.8001
第10步, log-likelihood = 96.9560
第11步, log-likelihood = 97.0857
第12步, log-likelihood = 97.5848
第13步, log-likelihood = 97.5959
第14步, log-likelihood = 97.6033
第15步, log-likelihood = 97.6140
第16步, log-likelihood = 97.6341
第17步, log-likelihood = 97.6695
第18步, log-likelihood = 97.6801
第19步, log-likelihood = 97.6888
第20步, log-likelihood = 97.7062
第21步, log-likelihood = 97.7420
第22步, log-likelihood = 97.7711
第23步, log-likelihood = 97.8122
第24步, log-likelihood = 97.8207
第25步, log-likelihood = 97.8232
第26步, log-likelihood = 97.8244
第27步, log-likelihood = 97.8255
第28步, log-likelihood = 97.8284
第29步, log-likelihood = 97.8330
第30步, log-likelihood = 97.8486
第31步, log-likelihood = 97.9543
第32步, log-likelihood = 98.0578
第33步, log-likelihood = 98.0620
第34步, log-likelihood = 98.0625
第35步, log-likelihood = 98.0626
第36步, log-likelihood = 98.0628
第37步, log-likelihood = 98.0628
log-likelihood不再變化,退出程序
[0 2 0 ... 2 0 2]
計算機類: { 0: 2742(0.71), 1: 299(0.08), 2: 801(0.21), }
藝術傳媒類: { 0: 9(0.00), 1: 421(0.18), 2: 1869(0.81), }
經管類: { 0: 829(0.33), 1: 1625(0.65), 2: 31(0.01), }
初始化log-likelihood = -779.6002
開始迭代
第0步, log-likelihood = -589.5137
第1步, log-likelihood = -583.1897
第2步, log-likelihood = -579.2382
第3步, log-likelihood = -577.0352
第4步, log-likelihood = -575.8822
第5步, log-likelihood = -575.3356
第6步, log-likelihood = -574.8611
第7步, log-likelihood = -574.5028
第8步, log-likelihood = -574.2625
第9步, log-likelihood = -574.1030
第10步, log-likelihood = -573.9690
第11步, log-likelihood = -573.7704
第12步, log-likelihood = -573.6187
第13步, log-likelihood = -573.4686
第14步, log-likelihood = -573.3280
第15步, log-likelihood = -573.2186
第16步, log-likelihood = -573.1160
第17步, log-likelihood = -572.9857
第18步, log-likelihood = -572.8356
第19步, log-likelihood = -572.7076
第20步, log-likelihood = -572.4683
第21步, log-likelihood = -572.3301
第22步, log-likelihood = -572.2375
第23步, log-likelihood = -572.0611
第24步, log-likelihood = -571.8587
第25步, log-likelihood = -571.7117
第26步, log-likelihood = -571.6412
第27步, log-likelihood = -571.6158
第28步, log-likelihood = -571.5863
第29步, log-likelihood = -571.5397
第30步, log-likelihood = -571.5281
第31步, log-likelihood = -571.5163
第32步, log-likelihood = -571.4902
第33步, log-likelihood = -571.4693
第34步, log-likelihood = -571.4604
第35步, log-likelihood = -571.4457
第36步, log-likelihood = -571.4339
第37步, log-likelihood = -571.4301
第38步, log-likelihood = -571.4288
第39步, log-likelihood = -571.4272
第40步, log-likelihood = -571.4257
第41步, log-likelihood = -571.4254
第42步, log-likelihood = -571.4176
第43步, log-likelihood = -571.4060
第44步, log-likelihood = -571.4028
第45步, log-likelihood = -571.4027
log-likelihood不再變化,退出程序
[0 1 0 ... 1 0 1]
計算機類: { 0: 2266(0.59), 1: 1479(0.38), 2: 97(0.03), }
藝術傳媒類: { 0: 64(0.03), 1: 703(0.31), 2: 1532(0.67), }
經管類: { 0: 2237(0.90), 1: 117(0.05), 2: 131(0.05), }進程已結束,退出代碼為 0
結束
好了,以上就是本次分享的全部內容了。不知道大家是否對自然語言處理中的文本聚類有了更深入的了解呢?
本次分享主要圍繞文本聚類展開,介紹了幾種常見的文本聚類方法,包括 k 均值聚類算法、基于高斯混合模型的最大期望值算法和無監督樸素貝葉斯模型。
- k均值聚類算法:簡單且經典,計算效率高,但需要預先指定聚類數量 k,對初始聚類中心的選擇比較敏感。
- 基于高斯混合模型的最大期望值算法:能夠靈活地擬合復雜的數據分布,對于多模態的數據聚類效果較好,但計算復雜度高,對超參數的選擇敏感,容易陷入局部最優解。
- 無監督樸素貝葉斯模型:簡單高效,對數據量的要求相對較低,可擴展性不錯,但特征條件獨立假設在實際情況中往往不太符合,對多義詞的處理能力有限,模型性能依賴于文本的表示方式。
文本聚類在自然語言處理領域有著廣泛的應用前景,如信息檢索、文本分類、情感分析等。隨著技術的不斷發展,未來可能會出現更高效、更準確的文本聚類算法。同時,結合深度學習等技術,也有望進一步提升文本聚類的效果。
那么本次分享就到這里了。最后,博主還是那句話:請大家多去大膽的嘗試和使用,成功總是在不斷的失敗中試驗出來的,敢于嘗試就已經成功了一半。如果大家對博主分享的內容感興趣或有幫助,請點贊和關注。大家的點贊和關注是博主持續分享的動力🤭,博主也希望讓更多的人學習到新的知識。?