恭喜各位,經過前面 10 天的前情提要???之後,我們終於邁入機器學習(machine learning)的第一步----決策樹(Decision Tree)了!!!首先,我們先來了解一下何謂機器學習~
機器學習,簡稱 ML 是人工智慧 AI 的一種。是讓機器透過大量的資料來學習並改善其性能。機器學習和 AI 常常同時被提及。然而,二者之含義並不相同。不論其複雜的結構,簡單總結二者的區別就是:雖然所有機器學習都屬於 AI,但並非所有 AI 都是機器學習(可能是深度學習 Deep Learning)。
機器學習主要可分為兩種學習方式,分別為監督式學習(Supervised learning)與非監督式學習(Unsupervised Learning)。
監督式學習:監督式學習的演算法是由已標示完成(labeled dataset),且能預先定義輸出的資料組訓練的。簡單來說,就是有「標準答案」的訓練方式。在此模式下,監督者(工程師、科學家......)會指導並修正、改善演算法,再讓演算法做出結論。經過一次次的修正和調配,讓機器的表現更好。此類模型包含線性(Linear Regression)及邏輯斯迴歸(Logistic Regression)、支援向量機(Support Vector Machine)等演算法。
非監督式學習:顧名思義,與監督式學習相反,此類機器學習沒有以標示完成、有「標準答案」的資料集來做訓練。非監督式機器學習較為獨立,由電腦自行觀察學習輸入的資料集,整理歸納並定義出一套電腦的的流程和模式,人類不會持續提供修正。因為此過程是讓電腦自己去學習,因此很多時候人類無法得知電腦是如何做出這樣的判斷,依據又是什麼,只能透過電腦每一個階段的產出來「推測」其流程,再進行可能相應的改善。
今天要介紹的決策樹屬於一種多元酚類模型,是監督式學習。那廢話不多說,我們來看看該如何做決策樹的訓練吧~
在進行機器學習前,有幾個非常重要的步驟要事先想好或定義好:
第一次做相關的訓練時,可以向我底下示範的那樣,將各個步驟進行的方式、流程與目的列出來。
資料集大致介紹:今天的示範我將沿用上一篇的 IMDB 資料集做分類。此資料集包含電影名稱、電影描述、類別、導演、主演、上映年份、評分、電影時長、票房、投票數以及 Metascore 評分等資訊。而我在這裡要處理的分類問題是判斷電影的好壞(我將評分大於6設定為好電影),希望透過這樣的分類,找出影響電影評分高低的關鍵因素。
確認分類目的:我的分類目的為找出影響電影評分高低的關鍵因素,判斷電影類別的多寡、電影主要類別、電影描述、投票數、票房以及 Metascore 是否為影響評分(好或壞的電影)的原因。
資料預處理與提取之特徵描述(因為是 NLP 相關任務,故會使用較多的語言特徵):
首先,刪除我不需要的資料欄位,再將原始資料評分(Rating)這一項轉換為直觀的文字描述並設定為"Comments"欄位(Good movie: 6分以上,和 Bad movie),作為作為我希望模型分類、預測且回傳的指標。
另外,我將原始資料中的電影類別轉換為相對應的數量。例如 Guardians of the Galaxy 這部電影中包含 Action, Adventure, Sci-Fi 三種類別,則將其轉換為 3,將電影類別多寡作為一項特徵。另一項和 Genre 有關的特徵則是保留 Genre 中的第一項,視為電影主要類別,作為特徵加入決策樹的分類依據。
電影描述的部分則是分別抽取與家庭相關的關鍵詞(family, parents, daughter, son, children, wife, husband)和低落、害怕、寂寞等常見的負面情緒形容詞(lonely, sad, depressed, fear, hopeless, grief, isolated, lifeless, lost, frustrated, terrible),分別作為不同的語言特徵,用 TRUE 和 FALSE 呈現,即有這些關鍵詞的資料為 TRUE,沒有的就是 FALSE,用以了解與家庭相關或帶有許多負面情緒之電影內容是否也是評分的關鍵。
投票數、票房以及Metascore這三項數字相關的資料則是探究判斷電影的好壞是否與這些資訊呈現正相關。
library(tm)
library(tokenizers)
movie = read.csv("IMDB-Movie-Data.csv") # 讀取資料
movie<-movie[!is.na(movie$Metascore), ]
movie <- movie[!is.na(movie$Revenue..Millions.), ] # 刪除 NA
result <-c()
for(i in seq_along(movie$Rating)){
if ((movie$Rating[i] >= '6.0')){
result[i]= "Good movie"
}else{
result[i]= "Bad movie"
}
}
movie$Comments = result # 第一項特徵
g_type1 <- gsub("\\,\\w+", "", movie$Genre)
g_type2 <- gsub("\\-\\w+", "", g_type1)
g_type3 <- gsub("\\s", "", g_type2)
movie$Genre_type = g_type3 # 提取電影類別
g_clean<-sapply(strsplit(movie$Genre, ","), length)
movie$Genre = g_clean # 根據提取的電影類別轉換為類別數量,放進 Genre 這個欄位,即第二項特徵
# 第三項特徵
d1<-grepl('family|parents|daughter|son|children|wife|husband', movie$Description)
movie$Description_family = d1 # family 相關特徵
d2<-grepl('lonely|sad|depressed|fear|hopeless|grief|isolated|lifeless|lost|frustrated|terrible', movie$Description)
movie$Description_ng = d2 # negative sentiment 特徵
head(movie, 10) # 看看整理完的資料
執行結果為:
# Features + Model Training
set.seed(53) # 隨意設定種子數量
shuffle_index <- sample(1:nrow(movie))
movie <- movie[shuffle_index, ] # 打散資料
movie <- subset(movie, select = -c(Rank, Title, Actors, Director, Year, Runtime..Minutes., Rating, Description)) # 刪除不需要放進去訓練的欄位
movie$Genre <- as.character(movie$Genre)
movie$Description_family <- as.factor(movie$Description_family)
movie$Description_ng <- as.factor(movie$Description_ng)
movie$Comments <- as.factor(movie$Comments)
movie$Genre_type <- as.factor(movie$Genre_type) # 把要放進去訓練的「非」數字特徵換成 factor 讓電腦理解
library(caret)
set.seed(111)
# 將資料分成 訓練集和測試集 8:2,百分之 80 的資料拿來訓練模型,百分之 20 的資料拿來測試模型的表現
trainIndex <- createDataPartition(movie$Comments, p=0.8, list=FALSE) # 以 Comments 這欄作為標準答案
train_set <- movie[trainIndex,]
test_set <- movie[-trainIndex,]
prop.table(table(train_set$Comments))
prop.table(table(test_set$Comments)) # 分別看看 train set & test set 資料分割的情況
執行結果為:
Bad movie Good movie
0.1773472 0.8226528
Bad movie Good movie
0.1736527 0.8263473 (train set & test set 都成功分割成大約 8:2 了)
library(rpart)
library(rpart.plot) # 引用 decision tree package
trained_model <- rpart(Comments~., data = train_set, method = 'class') # 以 Comments 為標準做分類
rpart.plot(trained_model, extra= 104) # 畫出決策樹在做分類時的流程
執行結果為:
可以看到最上方,也就根部(它是一棵倒著的樹xd)就是所有特徵中對決策樹分類時來說最重要的特徵。接著依次分類。
# Result + F score
predict_labels <- predict(trained_model, test_set, type = 'class')
table_matrix <- confusionMatrix(predict_labels, test_set$Comments, mode='prec_recall')
table_matrix
執行結果為:
可以看到雖然此模型的準確率(Accuracy)很高,但是 F-score 卻很低。可以推測模型可能亂猜居多,因為資料不平衡(Good Movie 在整份資料的佔比大於 Bad Movie 太多,導致模型只要猜 Good Movie,正確率就會很高。)
最後,我們列出分類錯誤的資料,方便分析、改進吧!
test_set[which(predict_labels != test_set$Comments), ]
今天就先到這裡,明天再做更多模型表現分析的介紹與 Random Forest 吧!