讀完這篇博文,你能夠收獲什么?
- 從數據處理到利用朴素貝葉斯進行分類的整個過程
- 本文更關注於數據處理階段,朴素貝葉斯模型直接使用sklearn庫中自帶的
先給出整個算法的流程:
采用的是sogou語料庫的部分數據,每個C開頭的文件各代表一類,里面包含着若干篇txt類型的文章,具體類別如下:
1.數據審視階段(查看是否有不符合規范或異常的數據),由於我們這里的數據是比較規整的,就直接進行下一階段了;
2.要想訓練一個模型,我們必須得有訓練集和測試集。我們要明確訓練集和測試集里面是什么。這里,我們使用的是詞袋,即包含有不同單詞的列表。
首先導入相應的包:
#用於處理文件路徑 import os #用於打亂數據,產生隨機的訓練集和測試集 import random #用於分詞 import jieba #朴素貝葉斯模型 from sklearn.naive_bayes import MultinomialNB
然后是詞袋模型的建立:
def data_process(): #獲取當前文件的絕對路徑 cur_path = os.path.dirname(os.path.abspath(__file__)) #定位包含數據的那級目錄 path = cur_path + '/Database/SogouC/Sample/' #測試集占總數據的百分比 test_size = 0.2 #Sample下的所有文件 folder_list = os.listdir(path) #存儲分詞后的列表 data_list =[] #存儲標簽列表 class_list = [] #遍歷C000008等類型的文件夾 for folder in folder_list: #取得該文件夾絕對路徑 new_folder_path = os.path.join(path,folder) #取得該文件夾下所有txt類型的數據,並返回 files=os.listdir(new_folder_path) #讀取txt文件 for file in files: #打開txt文件 with open(os.path.join(new_folder_path,file),'r',encoding='utf-8') as fp: #讀取里面的內容 raw = fp.read() #進行結巴分詞 word_cut=jieba.cut(raw,cut_all=False) #將分詞后的結果轉成列表,即單詞列表 word_list=list(word_cut) #將該文件夾下的所有txt分詞后添加到data_list中 data_list.append(word_list) #獲得標簽列表,就是文件夾名稱 class_list.append(folder) #將分詞列表和標簽對應並返回 data_class_list = list(zip(data_list,class_list)) #打亂數據以獲得隨機的訓練集和測試集 random.shuffle(data_class_list) #通過索引來切分數據 index = int(len(data_class_list)*test_size)+1 #訓練集(包含數據和標簽) train_list=data_class_list[index:] #測試集(包含數據和標簽 test_list=data_class_list[:index] #拆分 train_data_list,train_class_list=zip(*train_list) #拆分 test_data_list,test_class_list=zip(*test_list) #取得所有文章分詞后構成的詞袋 all_words_dict ={} #取得訓練集中的每一篇分詞后的列表 for word_list in train_data_list: #取得每一個單詞 for word in word_list: #判斷是否存在於詞袋中,如果沒有,則出現次數為1,否則+1 if word in all_words_dict: all_words_dict[word]+=1 else: all_words_dict[word]=1 #將所有詞語按出現次數由大到小排列 all_words_tuple_dict=sorted(all_words_dict.items(),key=lambda x:x[1],reverse=True) #取出單詞,並轉為列表 all_words_list=list(list(zip(*all_words_tuple_dict))[0]) #返回詞袋,訓練集,訓練集標簽,測試集,測試集標簽 return all_words_list,train_data_list,train_class_list,test_data_list,test_class_list
我們雖然得到了詞袋模型,但是,我們發現里面的詞並不是我們所需要的,我們還要進行下一步操作:去除一些不必要的詞和一些沒有意義的詞,這里得用到stopwods_cn.txt:
上圖展示的是部分停用詞。首先,我們必須從txt中獲得停用詞列表:
def get_stopwords_cn(): stopwords_cn_path = os.path.dirname(os.path.abspath(__file__)) + "\\stopwords_cn.txt" with open(stopwords_cn_path,'r',encoding='utf-8') as fp: stopwords_cn=fp.read().split("\n") return set(stopwords_cn)
然后,我們詞袋中的每一個單詞,如果不在停用詞中,就加入到新的列表中:
def word_dicts(all_words_list,deleteN,stopwords_set=set()): #用來存儲不位於停詞中的單詞 features_words=[] #用於指定詞袋的長度 n=1 for t in range(deleteN,len(all_words_list),1):
#限定詞袋的長度為1000 if n>1000: break #如果不是數字且不在停詞列表中且1<長度<5 if not all_words_list[t].isdigit() and all_words_list[t] not in stopwords_set and 1<len(all_words_list[t])<5: #加入到新的詞袋中 features_words.append(all_words_list[t]) n+=1 return features_words
接下來,我們得到修正過后的詞袋后,還需要將原本文章的分詞列表轉換成One-hot編碼,這才是我們真正需要的特征:
def text_features(train_data_list,test_data_list,features_words): #text是每一條train_data_list中或test_data_list的數據 #輔助函數 def helper(text,features_words): #首先過濾掉重復的值 text_words = set(text) #如果該詞位於詞袋中,則編碼成1,否則為0 features = [1 if word in text_words else 0 for word in features_words] return features #對訓練集進行編碼 train_feature_list=[helper(text,features_words) for text in train_data_list] #對測試集進行編碼 test_feature_list = [helper(text, features_words) for text in test_data_list] #返回新的特征 return train_feature_list,test_feature_list
我們已經擁有特征了,最后需要定義朴素貝葉斯模型:
def text_classifier(train_feature_list,train_class_list,test_feature_list,test_class_list): classifier = MultinomialNB().fit(train_feature_list,train_class_list) test_accuracy=classifier.score(test_feature_list,test_class_list) print(classifier.predict(test_feature_list)) print(test_class_list) return test_accuracy
最后,將所有部件組合起來,就大功告成了:
def main(): all_words_list, train_data_list, train_class_list, test_data_list, test_class_list = data_process() #去除掉停用詞 features_words = word_dicts(all_words_list,0,get_stopwords_cn()) train_feature_list, test_feature_list=text_features(train_data_list,test_data_list,features_words) accuracy = text_classifier(train_feature_list,train_class_list,test_feature_list,test_class_list) print(accuracy) if __name__ == '__main__': main()
我們來看下輸出:
由於只是個較為基礎的實現,所以准確率並不算太高,最主要的還是掌握整個過程。雖然代碼比較多,但是畫了流程圖和基本上都會有注釋,看起來應該會簡單些。
相關代碼和資料:鏈接: https://pan.baidu.com/s/1odwgJ8Vy_h1QyMrWpsi68Q 提取碼: d74g