在咱們平常生活中,常常會受到各類垃圾郵件,譬如來自商家的廣告、打折促銷信息、澳門博彩郵件、理財推廣信息等,通常來講郵件客戶端都會設置必定的關鍵詞屏蔽這種垃圾郵件,或者對郵件進行歸類,可是總會有一些漏網之魚。
不過,本身手動作一個垃圾郵件分類器也並非什麼難事。傳統的機器學習算法一般會採用樸素貝葉斯、支持向量機等算法對垃圾郵件進行過濾,今天咱們主要講如何用PaddlePaddle手寫一個垃圾郵件分類器。固然,在講PaddlePaddle作垃圾郵件處理以前,先回顧一下傳統的機器學習算法是如何對垃圾郵件進行分類的。python
首先先了解一下今天的數據集:trec06c。trec06c是一個公開的垃圾郵件語料庫,由國際文本檢索會議提供,分爲英文數據集(trec06p)和中文數據集(trec06c),其中所含的郵件均來源於真實郵件保留了郵件的原有格式和內容。
文件下載地址:trec06c
文件格式:git
trec06c │ └───data │ │ 000 │ │ 001 │ │ ... │ └───215 └───delay │ │ index └───full │ │ index
文件內容:github
垃圾郵件示例:本公司有部分普通發票(商品銷售發票)增值稅發票及海關代徵增值稅專用繳款書及其它服務行業發票,公路、內河運輸發票。能夠以低稅率爲貴公司代開,本公司具備內、外貿生意實力,保證我司開具的票據的真實性。 但願能夠合做!共同發展!敬侯您的來電洽談、諮詢! 聯繫人:李先生 聯繫電話:13632588281 若有打擾望諒解,祝商琪。
正常郵件示例:講的是孔子後人的故事。一個老領導回到家鄉,跟兒子感情不和,跟貪財的孫子孔爲本和氣。老領導的弟弟魏宗萬是趕馬車的。有個洋妞大概是考察民俗的,在他們家過年。孔爲本總想出國,被爺爺教育了。最後,一家人基本和解。 順便問另外一類電影,北京青年電影製片廠的。算法
拿到數據後咱們能夠很清楚的看到郵件的內容,但並非全部的內容都是咱們須要的,在這裏咱們僅提取了郵件中的中文來做爲訓練語料。若是仔細觀察的話,會發現不是全部的郵件都能直接打開,數據的編碼格式也須要轉換成utf-8格式方便咱們後面訓練使用。因此咱們須要對原始數據作一些數據預處理,包括如下幾個內容。bash
下面是具體的代碼 transfer.py:app
# -*- coding: utf-8 -*- #Created by huxiaoman 2018.1.28 #transfer.py:生成spam和ham數據 import jieba import sys import os import re # 判斷郵件中的字符是不是中文 def check_contain_chinese(check_str): for ch in check_str.decode('utf-8'): if u'\u4e00' <= ch <= u'\u9fff': return True return False # 加載郵件數據的label def load_label_files(label_file): label_dict ={} for line in open(label_file).readlines(): list1 = line.strip().split("..") label_dict[list1[1].strip()] = list1[0].strip() return label_dict # 加載停用詞詞表 def load_stop_train(stop_word_path): stop_dict = {} for line in open(stop_word_path).readlines(): line = line.strip() stop_dict[line] = 1 return stop_dict # 讀取郵件數據,並轉換爲utf-8格式,生成spam和ham樣本 def read_files(file_path,label_dict,stop_dict,spam_file_path,ham_file_path): parents = os.listdir(file_path) spam_file = open(spam_file_path,'a') ham_file = open(ham_file_path,'a') for parent in parents: child = os.path.join(file_path,parent) if os.path.isdir(child): read_files(child,label_dict,stop_dict,spam_file_path,ham_file_path) else: print child[10:] label = "unk" if child[10:] in label_dict: label = label_dict[child[10:]] # deal file temp_list = [] for line in open(child).readlines(): line = line.strip().decode("gbk",'ignore').encode('utf-8') if not check_contain_chinese(line): continue seg_list = jieba.cut(line, cut_all=False) for word in seg_list: if word in stop_dict: continue else: temp_list.append(word) line = " ".join(temp_list) print label if label == "spam": spam_file.write(line.encode("utf-8","ignore") + "\n") if label == "ham": ham_file.write(line.encode("utf-8","ignore")+"\n") # 生成word2vec詞表 def generate_word2vec(file_path,label_dict,stop_dict,word_vec): parents = os.listdir(file_path) fh1 = open(word_vec,'a') i = 0 for parent in parents: child = os.path.join(file_path,parent) if os.path.isdir(child): generate_word2vec(child,label_dict,stop_dict,word_vec) else: print child[10:] i += 1 print i label = "unk" if child[10:] in label_dict: label = label_dict[child[10:]] # deal file temp_list = [] for line in open(child).readlines(): line = line.strip().decode("gbk",'ignore').encode('utf-8') if not check_contain_chinese(line): continue if len(line) == 0: continue seg_list = jieba.cut(line, cut_all=False) for word in seg_list: if word in stop_dict: continue else: temp_list.append(word) line = " ".join(temp_list) fh1.write(line.encode("utf-8","ingore")+"\n") if __name__=="__main__": file_path = sys.argv[1] label_path = sys.argv[2] stop_word_path = "stop_words.txt" word_vec_path = "word2vec.txt" spam_data = "spam.txt" ham_data = "ham.txt" label_dict = load_label_files(label_path) stop_dict = load_stop_train(stop_word_path) read_files(file_path,label_dict,stop_dict,spam_data,ham_data)
run.sh:機器學習
bashif [ $1 = "test" ]; then echo "test" python transfer.py ../test/ ../trec06c/full/index else echo "whole" python transfer.py ../trec06c/data/ ../trec06c/full/index fi
sh run.sh
咱們知道,分詞後的數據是不能直接拿到模型裏去訓練的,咱們須要把詞語轉換成詞向量才能進行模型的訓練,這樣一個詞能夠有一個多維的詞向量組成。
傳統的方法是one-hot encoding,即用一個長向量來表示一個詞,向量的長度爲詞典的大小,向量的份量只有一個1,其他全爲0,1的位置即對應改詞在詞典中的位置,如電腦表示爲:[0 0 0 0 0 1 0 0 0 0 ],耳機表示爲[0 0 0 0 0 0 0 1 0 ]這種方式若是採用稀疏存儲,表達簡潔,佔用空間少,可是這種方法也有幾個缺點,一是容易受維數災難的困擾,尤爲是將其用於 Deep Learning的一些算法時;二是不能很好地刻畫詞與詞之間的類似性,即任意兩個詞之間都是孤立的。光從這兩個向量中看不出兩個詞是否有關係,損失大部分信息,致使結果會有較大誤差。性能
在1968年Hinton又提出了Distributed REpresentation,能夠解決One-hot encoding的缺點。其基本想法是直接用一個普通的向量表示一個詞,這種向量通常長成這個樣子:[0.792, −0.177, −0.107, 0.109, −0.542, ...],也就是普通的向量表示形式。維度以 50 維和 100 維比較常見。固然一個詞怎麼表示成這麼樣的一個向量須要經過訓練獲得,訓練方法較多,word2vec是最多見的一種。須要注意的是,每一個詞在不一樣的語料庫和不一樣的訓練方法下,獲得的詞向量多是不同的。詞向量通常維數不高,通常狀況下指定1000、500維就能夠了,因此用起來維數災難的機會現對於one-hot representation表示就大大減小了。
因爲是用向量表示,並且用較好的訓練算法獲得的詞向量的向量通常是有空間上的意義的,也就是說,將全部這些向量放在一塊兒造成一個詞向量空間,而每一貫量則爲該空間中的一個點,在這個空間上的詞向量之間的距離度量也能夠表示對應的兩個詞之間的「距離」。所謂兩個詞之間的「距離」,就是這兩個詞之間的語法,語義之間的類似性。
一個比較爽的應用方法是,獲得詞向量後,假如對於某個詞A,想找出這個詞最類似的詞,在創建好詞向量後的狀況,對計算機來講,只要拿這個詞的詞向量跟其餘詞的詞向量一一計算歐式距離或者cos距離,獲得距離最小的那個詞,就是它最類似的。
因此在這裏咱們選擇了word2vec方法來訓練生成詞向量。關於word2vec的原理你們能夠在網上搜索學習,此處再也不贅述。學習
在數據預處理中咱們生成的word2vec.txt就能夠放到此處訓練word2vec模型生成詞向量了,具體實現代碼以下: word2vec.py測試
# -*- coding: utf-8 -*- # Created by huxiaoman 2018.1.28 # word2vec.py:生成word2vec模型 import os import sys import numpy as np from gensim.models.word2vec import Word2Vec from gensim.corpora.dictionary import Dictionary import codecs reload(sys) sys.setdefaultencoding( "utf-8" ) class MySentences(object): def __init__(self, dirname): self.dirname = dirname def __iter__(self): for fname in os.listdir(self.dirname): for line in codecs.open(os.path.join(self.dirname, fname),"r", encoding="utf-8",errors="ignore"): yield line.strip().split() # word2vec.txt數據的地址 train_path = "rawData/" # 生成的word2vec模型的地址 model_path = "/modelPath/" sentences = MySentences(train_path) # 此處min_count=5表明5元模型,size=100表明詞向量維度,worker=15表示15個線程 model = Word2Vec(sentences,min_count = 5,size=100,workers=15) #保存模型 model.save(model_path+'/Word2vec_model.pkl')
python word2vec.py
Word2vec_model.pkl
生成正負樣本數據並將詞語所有轉化爲詞向量後咱們就能夠把數據灌倒模型裏進行訓練了,本篇中將採用傳統的機器學習算法svm來進行訓練。
驗證準確率
# 構建svm模型,加載數據等代碼詳見github def get_svm_model(x_train,y_train,x_val,y_val): model = SVC(C=1,kernel='rbf',max_iter=10,gamma=1,probability=True) model.fit(x_train,y_train) pred=model.predict(x_val) fpr,tpr,thresholds = roc_curve(y_val, pred, pos_label=2) score = metrics.f1_score(y_val,pred) print score
python train_svm.py
0.73343221
本篇文章做爲用PaddlePaddle處理垃圾郵件實戰系列的預熱,主要講了如何對文本數據進行數據預處理與過濾,如何生成詞向量以及用傳統的機器學習方法--支持向量機訓練模型,獲得的準確率爲0.73343221。其結果的好壞取決於詞典的大小,詞向量維度的大小,svm的基本參數的調整,在實際操做過程當中還須要不斷的調參才能達到最優的效果。下一篇咱們將帶領你們如何用PaddlePaddle來作垃圾郵件處理,用深度學習的方法對垃圾郵件進行分類,看看效果是否比傳統的機器學習方法要更好,性能和速度是否能有必定的提高。