垃圾郵件識別,算是一個二分類問題,也是一個相對簡單的文本分類問題。
本文數據來自UCI機器學習倉庫中的垃圾信息數據集,從http://archive.ics.uci.edu/ml/datasets/sms+spam+collection下載。
文件中每一行由兩部分組成,行首是標籤 spam/ham(spam 代表垃圾郵件),中間空一格,之後是郵件內容。
以下代碼是一個整體,如需複製,將除了函數部分的代碼按順序複製,函數需要放在庫和其它代碼之間
需要用到的庫:
import nltk
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
數據提取
源文件無類型後綴,就當作普通文本文件讀取:
sms_data = open('.\SMSSpamCollection', 'r', encoding='utf-8')
x = []
y = []
for line in sms_data.readlines():
data = line.split('\t')
y.append(data[0])
x.append(data[1].split('\n')[0])
sms_data.close()
數據預處理
上面倒數第二行裏面使用了一個自定義函數,把與郵件內容關係不大的單詞(停用詞等)去除,這樣做能減少特徵數量但對訓練精度沒什麼影響
因爲數據集中的內容爲英文,這裏我使用 NLTK 庫進行處理,如果選擇中文數據更推薦使用 jieba 庫
def text_dispose(text):
# 將每個單詞和符號分開
sentences = nltk.sent_tokenize(text)
words = [word for sent in sentences for word in nltk.word_tokenize(sent)]
stops = stopwords.words('english')
# 去除停用詞
words = [word for word in words if words not in stops]
words = [words.lower() for words in words if len(words) >= 3]
# 詞彙化
lemmatizer = WordNetLemmatizer()
words = [lemmatizer.lemmatize(words) for words in words]
dispose_text = ' '.join(words)
return dispose_text
數據拆分及文本特徵處理
數據拆分成常見的三七開
特徵處理是文本分類的關鍵部分,因爲我們不可能把整個文本內容直接放進模型中訓練,所以需要將文本表示爲計算機可以識別的、能夠代表該文檔特徵的特徵矩陣
# 拆分
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=1)
# 特徵處理
vectorizer = TfidfVectorizer(min_df=2, ngram_range=(1,2), stop_words='english', strip_accents='unicode',norm='l2')
X_train = vectorizer.fit_transform(x_train)
X_test = vectorizer.transform(x_test)
TfidfVectorizer
可以把原始文本轉化爲 TF-IDF
的特徵矩陣
選擇模型並訓練
這裏使用樸素貝葉斯分類器
model = MultinomialNB()
model.fit(X_train,y_train)
score = model.score(X_test, y_test)
print(score)
因爲 sklearn 的高度封裝,只需幾行代碼就可以使用特定算法。我們不需要寫出算法的流程,只要知道其功能即可,所以 sklearn 適用於快速構建模型。當然如果想讓算法定製性更強,則需要自行構建
看評分結果:
0.9689181111775254