1.理解朴素貝葉斯
1)基本概念
- 依據概率原則進行分類。如天氣預測概率。
- 朴素貝葉斯(Naive Bayes, NB)適合場景:為估計一個結果的概率,從眾多屬性中提取的信息應該被同時考慮。
- 很多算法忽略了弱影響的特征(若有大量弱影響的特征,它們組合在一起的影響可能會很大),但NB算法利用了所有可以獲得的證據來修正預測。
- 貝葉斯方法的基本概念:事件,試驗,概率,聯合概率,獨立事件,相關事件(建立預測模型的基礎),條件概率,先驗概率,似然概率,邊際似然概率,后驗概率,頻率表
- 條件概率公式(事件B已經發生的條件下,事件A發生的概率):
- 后驗概率(如商業垃圾郵件過濾器:判斷viagra是垃圾郵件spam的概率):
2)朴素貝葉斯算法
- NB優點:簡單快速有效;能處理噪音及缺失值數據;訓練集不限大小;容易獲得估計概率值。
- NB缺點:依賴同樣重要和獨立的特征(錯誤假設);應用在大量數值特征的數據集中不理想;概率估計值比預測的類更不可靠。
- “朴素”的含義:基於這樣一個假設:數據集的所有特征都具有相同的重要性和獨立性,但在大多數實際應用中,假設不成立。
- 朴素貝葉斯算法具通用性和准確性,在分類學習任務中很強大。
①朴素貝葉斯分類
假設有4個單詞的100封郵件的似然表來訓練朴素貝葉斯算法(如下表),收到新郵件時(包含了單詞viagra和unsubscribe,但不包含money和groceries),通過計算后驗概率來判斷它是否為垃圾郵件。
原始的基於貝葉斯定理的后驗概率:
將4個單詞事件視為獨立事件(類條件獨立),可簡化公式:
計算垃圾郵件總似然為:
計算非垃圾郵件總似然為:
是垃圾郵件的概率為:
②拉普拉斯估計
對於類中一個或多個水平,如果一個時間從沒有發生過,那它出現的概率為0,從而導致后驗概率值也為0(抵消或否決了所有其他的證據)。
比如,這次的新郵件中包含了前述的4個單詞,則計算垃圾郵件的似然:
該郵件是垃圾郵件的概率為:
拉普拉斯估計就是給頻率表中每個計數加上一個很小的數(一般設為1),保證每一類中每個特征發生的概率是非零的。
拉普拉斯估計后的垃圾郵件似然:
③數值型特征值離散化
前面的頻率表要求特征必須為分類變量,如果是數值變量,需要將數值離散化(分段),如根據時間尋找分割點。如果沒有明顯的分割點,也可利用分位數進行分段。
但將數值特征離散化總是會導致信息量的減少,因為特征的原始粒度減少為幾個數目較少的類別。分段太少會導致重要趨勢被掩蓋,分段太多會導致頻率表中的計數值很小,因此需要平衡分段數。
2.朴素貝斯分類應用
示例:基於貝葉斯算法的手機垃圾短信過濾。
1)收集數據
數據下載sms_spam.csv
:
鏈接: https://pan.baidu.com/s/1fAufKXCSufwd8It_DHXyWQ 提取碼: vgyj
2)探索和准備數據
## Example: Filtering spam SMS messages ----
## Step 2: Exploring and preparing the data ----
# read the sms data into the sms data frame
sms_raw <- read.csv("sms_spam.csv", stringsAsFactors = FALSE)
# examine the structure of the sms data
str(sms_raw)
# convert spam/ham to factor.
sms_raw$type <- factor(sms_raw$type)
# examine the type variable more carefully
str(sms_raw$type)
table(sms_raw$type)
處理和分析文本數據
文本挖掘包tm創建語料庫(文本集合),inspect函數查看語料庫內容,tm_map函數轉換tm語料庫(如去數字,變小寫等),stopwords函數去除填充詞(如to/and/or/but等)。
清理完后標記分解單詞形成的組,並創建稀疏矩陣。再進行訓練集和測試集划分,並利用詞雲進行可視化文本數據。最后為高頻詞創建指示特征。
PS:運行過程中tm包的tolower參數一直報錯,未解決,因此本示例最終沒有用此參數。
# build a corpus using the text mining (tm) package
library(tm)
sms_corpus <- VCorpus(VectorSource(sms_raw$text))
# examine the sms corpus
print(sms_corpus)
inspect(sms_corpus[1:2])
as.character(sms_corpus[[1]])
lapply(sms_corpus[1:2], as.character)
# clean up the corpus using tm_map()
# sms_corpus_clean <- tm_map(sms_corpus, content_transformer(tolower)) #Error
sms_corpus_clean <- sms_corpus
# show the difference between sms_corpus and corpus_clean
as.character(sms_corpus[[1]])
as.character(sms_corpus_clean[[1]])
sms_corpus_clean <- tm_map(sms_corpus_clean, removeNumbers) # remove numbers
sms_corpus_clean <- tm_map(sms_corpus_clean, removeWords, stopwords()) # remove stop words
sms_corpus_clean <- tm_map(sms_corpus_clean, removePunctuation) # remove punctuation
# tip: create a custom function to replace (rather than remove) punctuation
removePunctuation("hello...world")
replacePunctuation <- function(x) { gsub("[[:punct:]]+", " ", x) }
replacePunctuation("hello...world")
# illustration of word stemming
library(SnowballC)
wordStem(c("learn", "learned", "learning", "learns"))
sms_corpus_clean <- tm_map(sms_corpus_clean, stemDocument)
sms_corpus_clean <- tm_map(sms_corpus_clean, stripWhitespace) # eliminate unneeded whitespace
# examine the final clean corpus
lapply(sms_corpus[1:3], as.character)
lapply(sms_corpus_clean[1:3], as.character)
# create a document-term sparse matrix
sms_dtm <- DocumentTermMatrix(sms_corpus_clean)
# alternative solution: create a document-term sparse matrix directly from the SMS corpus
sms_dtm2 <- DocumentTermMatrix(sms_corpus, control = list(
# tolower = TRUE, #注釋掉也報錯
removeNumbers = TRUE,
stopwords = TRUE,
removePunctuation = TRUE,
stemming = TRUE
))
# alternative solution: using custom stop words function ensures identical result
sms_dtm3 <- DocumentTermMatrix(sms_corpus, control = list(
# tolower = TRUE, #注釋掉也報錯
removeNumbers = TRUE,
stopwords = function(x) { removeWords(x, stopwords()) },
removePunctuation = TRUE,
stemming = TRUE
))
# compare the result
sms_dtm
sms_dtm2
sms_dtm3
# creating training and test datasets
sms_dtm_train <- sms_dtm[1:4169, ]
sms_dtm_test <- sms_dtm[4170:5558, ]
# also save the labels
sms_train_labels <- sms_raw[1:4169, ]$type
sms_test_labels <- sms_raw[4170:5558, ]$type
# check that the proportion of spam is similar
prop.table(table(sms_train_labels))
prop.table(table(sms_test_labels))
# word cloud visualization
library(wordcloud)
wordcloud(sms_corpus_clean, min.freq = 50, random.order = FALSE)
# subset the training data into spam and ham groups
spam <- subset(sms_raw, type == "spam")
ham <- subset(sms_raw, type == "ham")
wordcloud(spam$text, max.words = 40, scale = c(3, 0.5))
wordcloud(ham$text, max.words = 40, scale = c(3, 0.5))
sms_dtm_freq_train <- removeSparseTerms(sms_dtm_train, 0.999)
sms_dtm_freq_train
# indicator features for frequent words
findFreqTerms(sms_dtm_train, 5)
# save frequently-appearing terms to a character vector
sms_freq_words <- findFreqTerms(sms_dtm_train, 5)
str(sms_freq_words)
# create DTMs with only the frequent terms
sms_dtm_freq_train <- sms_dtm_train[ , sms_freq_words]
sms_dtm_freq_test <- sms_dtm_test[ , sms_freq_words]
# convert counts to a factor
convert_counts <- function(x) {
x <- ifelse(x > 0, "Yes", "No")
}
# apply() convert_counts() to columns of train/test data
sms_train <- apply(sms_dtm_freq_train, MARGIN = 2, convert_counts)
sms_test <- apply(sms_dtm_freq_test, MARGIN = 2, convert_counts)
得到的sms_train
和sms_test
的單詞稀疏矩陣如下表所示:
3)訓練模型
上例已經將原始短信轉換為可以用一個統計模型代表的形式,因此用NB算法根據單詞的存在與否來估計一條給定的短信是垃圾短信的概率。
使用e1071::naiveBays()
或klaR::NaiveBayes()
函數。
## Step 3: Training a model on the data ----
library(e1071)
sms_classifier <- naiveBayes(sms_train, sms_train_labels)
4)評估模型性能
基於測試集中的未知短信來檢驗分類器的預測值。比較預測值和真實值,仍然通過混淆矩陣來計算。
## Step 4: Evaluating model performance ----
sms_test_pred <- predict(sms_classifier, sms_test)
library(gmodels)
CrossTable(sms_test_pred, sms_test_labels,
prop.chisq = FALSE, prop.t = FALSE, prop.r = FALSE,
dnn = c('predicted', 'actual'))
沒怎么處理效果也比較好,所以NB是文本分類的一種標准算法。同樣地,假陰性問題帶來的代價較大(把正常短信過濾掉了),進一步提升模型性能試試。
5)提升模型性能
上面訓練時,沒有設置拉普拉斯估計,此處設為1,性能有所提升。
## Step 5: Improving model performance ----
sms_classifier2 <- naiveBayes(sms_train,
sms_train_labels,
laplace = 1) #拉普拉斯估計值
sms_test_pred2 <- predict(sms_classifier2, sms_test)
CrossTable(sms_test_pred2, sms_test_labels,
prop.chisq = FALSE, prop.t = FALSE, prop.r = FALSE,
dnn = c('predicted', 'actual'))
機器學習與R語言系列推文匯總:
【機器學習與R語言】1-機器學習簡介
【機器學習與R語言】2-K近鄰(kNN)
【機器學習與R語言】3-朴素貝葉斯(NB)
【機器學習與R語言】4-決策樹
【機器學習與R語言】5-規則學習
【機器學習與R語言】6-線性回歸
【機器學習與R語言】7-回歸樹和模型樹
【機器學習與R語言】8-神經網絡
【機器學習與R語言】9-支持向量機
【機器學習與R語言】10-關聯規則
【機器學習與R語言】11-Kmeans聚類
【機器學習與R語言】12-如何評估模型的性能?
【機器學習與R語言】13-如何提高模型的性能?