擅長用通俗易懂的方式講解深度學習和機器學習算法,熟悉Tensorflow,PaddlePaddle等深度學習框架,負責過多個機器學習落地項目,如垃圾評論自動過濾,用戶分級精準營銷,分布式深度學習平臺搭建等,都取了的不錯的效果。
背景介紹
在我們日常生活中,經常會受到各種垃圾郵件,譬如來自商家的廣告、打折促銷信息、澳門博彩郵件、理財推廣信息等,一般來說郵件客戶端都會設置一定的關鍵詞屏蔽這種垃圾郵件,或者對郵件進行歸類,但是總會有一些漏網之魚。??不過,自己手動做一個垃圾郵件分類器也并不是什么難事。傳統的機器學習算法通常會采用樸素貝葉斯、支持向量機等算法對垃圾郵件進行過濾,今天我們主要講如何用PaddlePaddle手寫一個垃圾郵件分類器。當然,在講PaddlePaddle做垃圾郵件處理之前,先回顧一下傳統的機器學習算法是如何對垃圾郵件進行分類的。
了解數據集
首先先了解一下今天的數據集:trec06c。trec06c是一個公開的垃圾郵件語料庫,由國際文本檢索會議提供,分為英文數據集(trec06p)和中文數據集(trec06c),其中所含的郵件均來源于真實郵件保留了郵件的原有格式和內容。文件下載地址:trec06c文件格式:
trec06c│└───data│ │ 000│ │ 001│ │ ...│ └───215└───delay│ │ index└───full│ │ index
文件內容:
垃圾郵件示例:本公司有部分普通發票(商品銷售發票)增值稅發票及海關代征增值稅專用繳款書及其它服務行業發票,公路、內河運輸發票。可以以低稅率為貴公司代開,本公司具有內、外貿生意實力,保證我司開具的票據的真實性。 希望可以合作!共同發展!敬侯您的來電洽談、咨詢! 聯系人:李先生 聯系電話:13632588281 如有打擾望諒解,祝商琪。正常郵件示例:講的是孔子后人的故事。一個老領導回到家鄉,跟兒子感情不和,跟貪財的孫子孔為本和睦。老領導的弟弟魏宗萬是趕馬車的。有個洋妞大概是考察民俗的,在他們家過年。孔為本總想出國,被爺爺教育了。最后,一家人基本和解。 順便問另一類電影,北京青年電影制片廠的。
數據預處理
拿到數據后我們可以很清楚的看到郵件的內容,但并不是所有的內容都是我們需要的,在這里我們僅提取了郵件中的中文來作為訓練語料。如果仔細觀察的話,會發現不是所有的郵件都能直接打開,數據的編碼格式也需要轉換成utf-8格式方便我們后面訓練使用。所以我們需要對原始數據做一些數據預處理,包括以下幾個內容。
基本步驟
轉換源數據編碼格式為utf-8格式
過濾字符
去除所有非中文字符,如標點符號、英文字符、數字、網站鏈接等特殊字符。
過濾停用詞
對郵件內容進行分詞處理
訓練代碼
下面是具體的代碼 transfer.py:
# -*- coding: utf-8 -*-#Created by huxiaoman 2018.1.28#transfer.py:生成spam和ham數據import jiebaimport sysimport osimport 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# 加載郵件數據的labeldef 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/indexelse echo "whole" python transfer.py ../trec06c/data/ ../trec06c/full/indexfi
運行方式:
sh run.sh
運行結果:
ham.txt: 正樣本,正常郵件。共21373條數據。
示例:我 就 鬧 不 明白 了 只要 你 本人 不介意 跟 你 爸爸媽媽 有 何干 為啥 要說 呢 ..... 首先 謝謝 大家 安慰 我 。 但是 我 確實 很 難受 , 我 有 自己 的 苦衷 。 我 不敢 和 我 媽媽 說 的 這種 情況 。 我 媽媽 是 那種 特別 容易 擔心 的 那種 類型 。 而且 我 又 不 在 她 身邊 。 我家 是 外地 的 。 如果 和 媽媽 說 了 , 她 一定 不會 同意 我 和 在 一起 的 。 媽媽 對 身體健康 看 的 特別 重要 。 有 一年 姐夫 那年 經常 流鼻血 , 媽媽 都 特別 擔心 , 老 催 姐姐 帶 著 去 看看 。
spam.txt: 負樣本,垃圾郵件。共41627條數據。
示例:您好 以下 是 特別 為 閣下 發 的 香港 信息 圖片 、 景點 等 不 知道 閣下 是否 喜 希望 沒有 打擾到 閣下 如果 無法 看到 下面 內容 請 稍侯 或者 直接 進入 香港 行網 域名論壇 地址 真誠 為您服務
word2vec.txt: 包含所有郵件分詞的內容,為Word2Vec提供訓練預料。共63000條數據。
示例:我 覺得 , 負債 不要緊 , 最 重要 的 是 能 負得起 這個 責任 來 , 欠 了 那么 多錢 , 至少 對 當初 拿出 愛心 來 的 網友 們 有 個 交待 , 還 , 還是 不 還 了 , 或者 , 是 有 這個 心 但 實在 沒 能力 , 說明 一聲 還 都 好 不要 連 ID 都 不 激活 了 , 連 手機號 都 換 了 … … 別說 外地 的 了 , 就 連 北京 的 網友 都 找 不到 他 … … 他 當時 在 水木 fl 版 的 那陣 , 我 旁觀 了 全過程 。
生成詞向量
傳統方法的局限性
我們知道,分詞后的數據是不能直接拿到模型里去訓練的,我們需要把詞語轉換成詞向量才能進行模型的訓練,這樣一個詞可以有一個多維的詞向量組成。??傳統的方法是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的一些算法時;二是不能很好地刻畫詞與詞之間的相似性,即任意兩個詞之間都是孤立的。光從這兩個向量中看不出兩個詞是否有關系,損失大部分信息,導致結果會有較大偏差。
Word2Vec方法的優勢
在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 osimport sysimport numpy as npfrom gensim.models.word2vec import Word2Vecfrom gensim.corpora.dictionary import Dictionaryimport codecsreload(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來進行訓練。
具體步驟
加載數據集
劃分訓練集train、驗證集val與測試集test
定義訓練模型,并訓練
驗證準確率
實現代碼
# 構建svm模型,加載數據等代碼詳見githubdef 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來做垃圾郵件處理,用深度學習的方法對垃圾郵件進行分類,看看效果是否比傳統的機器學習方法要更好,性能和速度是否能有一定的提升。
-
機器學習
+關注
關注
66文章
8441瀏覽量
133088 -
數據集
+關注
關注
4文章
1209瀏覽量
24835
原文標題:PaddlePaddle垃圾郵件處理實戰(一)
文章出處:【微信號:AI_shequ,微信公眾號:人工智能愛好者社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論