機器學習之基於文本內容的垃圾短信識別
1.背景與目標
2.數據探索
3.數據預處理
4.文本的向量表示
5.模型訓練與評價
1.背景與目標
- 我國目前的垃圾短信現狀:
垃圾短信黑色利益鏈
缺乏法律保護
短信類型日益多變
- 案例目標:垃圾短信識別。
基於短信文本內容,建立識別模型,準確地識別出垃圾短信,以解決垃圾短信過濾問題
2.數據探索
- 總體流程
- 數據展示
*觀察數據,請思考:
建模前需要對文本數據做哪些處理?
需要怎麼評價模型的好壞? - 數據分佈
對原始80萬條數據進行數據探索,發現數據中並無存在空值,進一步查看垃圾短信和非垃圾短信的分佈情況。
- 欠抽樣
- 數據抽取
隨機抽取上文的2W條文本處理後的數據的80%作爲訓練樣本,其餘作爲測試集樣本。
3.數據預處理
步驟:數據清洗----分詞—添加詞典去停用詞—繪製詞雲
- 去除空格 (空格及全角情況下的空格)
- x序列 (將銀行賬戶、電話、固話、QQ、價格、日期替換成x序列)
- 文本去重
在數據的的儲存和提取過程中,由於技術和某些客觀的原因,造成了相同短信文本內容缺失等情況,因此需要對文本數據進行去重,去重即僅保留重複文本中的一條記錄。
-
中文分詞
中文分詞是指以詞作爲基本單元,使用計算機自動對中文文本進行詞語的切分,即使詞之間有空格,這樣方便計算機識別出各語句的重點內容。
-
正向最大匹配法
-
NLP概率圖:HMM針對中文分詞應用-Viterbi算法
利用Viterbi算法找出一條概率最大路徑。
-
python 結巴分詞(jieba)支持三種分詞模式
支持繁體分詞
支持自定義詞典 -
停用詞過濾
中文表達中最常用的功能性詞語是限定詞,如“的”、“一個”、“這”、“那”等。這些詞語的使用較大的作用僅僅是協助一些文本的名詞描述和概念表達,並沒有太多的實際含義。
而大多數時候停用詞都是非自動生產、人工篩選錄入的,因爲需要根據不同的研究主題人爲地判斷和選擇合適的停用詞語。
-
繪製詞雲圖
詞雲圖是文本結果展示的有利工具,通過詞雲圖的展示可以對短信文本數據分詞後的高頻詞予以視覺上的強調突出效果,使得閱讀者一眼就可獲取到主旨信息。
垃圾短信
正常短信
如何將文本數據放入模型?
4.文本的向量表示
- 文本分類實例
1.'My dog has flea problems, help please.’
2.'Maybe not take him to dog park is stupid.’
3.'My dalmation is so cute. I love him.’
4.'Stop posting stupid worthless garbage.’
5.'Mr licks ate mu steak, what can I do?.’
6.'Quit buying worthless dog food stupid’
labels = [0,1,0,1,0,1] #文檔標籤:是否是消極情感 - One-Hot表達
- TF-IDF權重策略
權重策略文檔中的高頻詞應具有表徵此文檔較高的權重,除非該詞也是高文檔頻率詞
TF:Term frequency即關鍵詞詞頻,是指一篇文檔中關鍵詞出現的頻率
IDF:Inverse document frequency指逆向文本頻率,是用於衡量關鍵詞權重的指數,由公式
- 文本分類實例
1.分詞;去除停用詞;
2.轉換成詞頻向量
3.轉換成TF-IDF權重矩陣
4.特徵提取,構建模型
sklearn.feature_extraction.text #文本特徵提取模塊
CountVectorizer #轉化詞頻向量函數
fit_transform() #轉化詞頻向量方法
get_feature_names() #獲取單詞集合方法
toarray() #獲取數值矩陣方法
TfidfTransformer #轉化tf-idf權重向量函數
fit_transform(counts) #轉成tf-idf權重向量方法
#相應的庫
from sklearn.feature_extraction.text import CountVectorizer,TfidfTransformer
from sklearn.naive_bayes import GaussianNB
transformer = TfidfTransformer() #轉化tf-idf權重向量函數
vectorizer = CountVectorizer() #轉化詞頻向量函數
word_vec = vectorizer.fit_transform(corpus) #轉成詞向量
words = vectorizer.get_feature_names() #單詞集合
word_cout = word_vec.toarray() #轉成ndarray
tfidf = transformer.fit_transform(word_cout) #轉成tf-idf權重向量
tfidf_ma= tfidf.toarray() #轉成ndarray
clf = GaussianNB().fit(tfidf_ma[:4,:],labels[:4])
res = clf.predict(tfidf_ma[4:,:])
5.模型訓練與評價
sklearn. model_selection.train_test_split
隨機劃分訓練集和測試集
train_test_split是交叉驗證中常用的函數,功能是從樣本中隨機的按比例選取train data和testdata,形式爲:
X_train,X_test, y_train, y_test = model_selection.train_test_split(x_data, y_target, test_size=0.4, random_state=0)
train_test_split參數解釋:
x_data:所要劃分的樣本特徵集
y_target:所要劃分的樣本結果
test_size:樣本佔比,如果是整數的話就是樣本的數量
random_state:是隨機數的種子。
隨機數種子:其實就是該組隨機數的編號,在需要重複試驗的時候,保證得到一組一樣的隨機數。比如你每次都填1,其他參數一樣的情況下你得到的隨機數組是一樣的。但填0或不填,每次都會不一樣。
隨機數的產生取決於種子,隨機數和種子之間的關係遵從以下兩個規則:
種子不同,產生不同的隨機數;種子相同,即使實例不同也產生相同的隨機數。
- 在train和test上提取的feature維度不同,那麼怎麼讓它們相同呢?
讓兩個CountVectorizer共享vocabulary!
cv1 = CountVectorizer(vocabulary=cv.vocabulary_)
cv_test = cv1.fit_transform(x_test)
print(cv_test.toarray()) # 測試集的文檔詞條矩陣
#cv_test、cv_train向量長度應保持一致 - 混淆矩陣就是彙總分類模型中分類正確和不正確的樣本數目的矩陣。對於簡單的二分類問題的混淆矩陣如下表:
另,sklearn中自帶混淆矩陣及分類報告
from sklearn.metrics import classification_report,confusion_matrix
- 精確度表示的是分類爲負類的樣本中實際爲負類的樣本所佔的比例,精確度越高,模型某類的分類效果越好。
- 召回率表示被正確分類的負類的比例,召回率越高,表示模型將負類誤分爲正類的模型概率越低,模型效果越好。
- F1值
F-Measure(又稱爲F-Score)綜合考慮精確度與召回率,其中P指精確率,R指召回率。F-Measure是精確度和召回率的加權調和平均:
當參數α=1時,就是最常見的F1值,即:
利用處理後的訓練集通過訓練樸素貝葉斯模型,並由測試集進行分類得到模型分類結果,整理彙總成如下混淆矩陣:
相關代碼如下:(其中可以點擊此處獲取基於文本內容的垃圾短信識別的所需數據)
#data_process (數據預處理)
import pandas as pd
import re
import jieba
def data_process(file='message80W1.csv'):
data = pd.read_csv(file, header=None, index_col=0) #把數據讀取進來
#處理數據
# data.shape#數據的結構
# data.head() #看一下前5行,發現頭部多了無關標題,用header=None去掉,3列第1列不需要用index_col=0,使第一列爲行索引
# 欠抽樣操作
data.columns = ['label', 'message'] #列名賦值->標籤 內容
n = 5000
a = data[data['label'] == 0].sample(n) #反例正常
b = data[data['label'] == 1].sample(n) #正例垃圾
data_new = pd.concat([a, b], axis=0) #縱向拼接
#data['label'].value_counts()
data_dup = data_new['message'].drop_duplicates() #短信去重
data_qumin = data_dup.apply(lambda x: re.sub('x', '', x)) #對敏感字符x替換成空
jieba.load_userdict('newdic1.txt') #將自定義的詞典加入
data_cut = data_qumin.apply(lambda x: jieba.lcut(x)) #data去敏的句子進行分詞操作,返回列表
#去除停用詞
stopWords = pd.read_csv('stopword.txt', encoding='GB18030', sep='hahaha', header=None) #導入停用詞,編碼,設置分隔符號
stopWords = ['≮', '≯', '≠', '≮', ' ', '會', '月', '日', '–'] + list(stopWords.iloc[:, 0]) #增加的分詞與以列表爲形式的原有分詞拼接起來
data_after_stop = data_cut.apply(lambda x: [i for i in x if i not in stopWords]) #將短信中的停用詞去掉
#數據預處理函數封裝
labels = data_new.loc[data_after_stop.index, 'label'] #標籤
adata = data_after_stop.apply(lambda x: ' '.join(x)) #將列表進行拼接
#' '.join(data_after_stop[236042 ]
return adata, data_after_stop, labels
#word_cloud (繪製詞雲)
from data_process import data_process
from wordcloud import WordCloud
import matplotlib.pyplot as plt
adata, data_after_stop, labels = data_process()
word_fre = {}
for i in data_after_stop[labels == 0]:
for j in i:
if j not in word_fre.keys():
word_fre[j] = 1
else:
word_fre[j] += 1
mask = plt.imread('duihuakuan.jpg')
wc = WordCloud(mask=mask, background_color='white', font_path=r'C:\Windows\Fonts\simhei.ttf')
wc.fit_words(word_fre)
plt.imshow(wc)
#model(模型構建與性能評估)
from data_process import data_process
from sklearn.naive_bayes import GaussianNB #導入高斯樸素貝葉斯
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer #導入文本特徵提取模塊 轉換成詞頻 向量轉換成TF-IDF權重矩陣
adata, data_after_stop, lables = data_process()
data_tr, data_te, labels_tr, labels_te = train_test_split(adata, lables, test_size=0.2)
countVectorizer = CountVectorizer() #使訓練集與測試集的列數相同
data_tr = countVectorizer.fit_transform(data_tr)
X_tr = TfidfTransformer().fit_transform(data_tr.toarray()).toarray() #訓練集TF-IDF權值
data_te = CountVectorizer(vocabulary=countVectorizer.vocabulary_).fit_transform(data_te)
X_te = TfidfTransformer().fit_transform(data_te.toarray()).toarray() #測試集TF-IDF權值
model = GaussianNB()
model.fit(X_tr, labels_tr)
model.score(X_te, labels_te)
運行結果: