Steps for Implementing a Machine Learning Project
Data Science in Banking Course
2018-03-13
#======================================================================================
# Project Name: Breast Cancer (breast-cancer-wisconsin.data.txt)
# Data Source + research papers:
# - https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29
# - http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.56.707&rep=rep1&type=pdf
#======================================================================================
#---------------------------------
# Initial Data Analysis (IDA)
#---------------------------------
# Đọc dữ liệu:
rm(list = ls())
bc_data <- read.table("D:/Teaching/data_science_banking/breast_cancer/breast-cancer-wisconsin.data.txt",
sep = ",")
# Đổi tên cho cột biến:
colnames(bc_data) <- c("sample_code_number",
"clump_thickness",
"uniformity_of_cell_size",
"uniformity_of_cell_shape",
"marginal_adhesion",
"single_epithelial_cell_size",
"bare_nuclei",
"bland_chromatin",
"normal_nucleoli",
"mitosis",
"classes")
library(tidyverse)
library(magrittr)
# Xem qua dữ liệu:
# bc_data %>% head()
# bc_data %>% str()
# Viết hàm đánh giá tỉ lệ thiếu của dữ liệu:
ti_le_na <- function(x) {100*sum(is.na(x) / length(x))}
# Cách 1:
bc_data %>% summarise_all(ti_le_na)
## sample_code_number clump_thickness uniformity_of_cell_size
## 1 0 0 0
## uniformity_of_cell_shape marginal_adhesion single_epithelial_cell_size
## 1 0 0 0
## bare_nuclei bland_chromatin normal_nucleoli mitosis classes
## 1 0 0 0 0 0
# Cách 2:
sapply(bc_data, ti_le_na)
## sample_code_number clump_thickness
## 0 0
## uniformity_of_cell_size uniformity_of_cell_shape
## 0 0
## marginal_adhesion single_epithelial_cell_size
## 0 0
## bare_nuclei bland_chromatin
## 0 0
## normal_nucleoli mitosis
## 0 0
## classes
## 0
#-----------------------
# Pre-processing Data
#-----------------------
# Dán lại nhãn cho biến classes:
dan_nhan <- function(x) {
case_when(x == 2 ~ "B",
x == 4 ~ "M")
}
# Thay thế "?" thành nhãn có tần suất cao nhất:
bc_data$bare_nuclei %>% table()
## .
## ? 1 10 2 3 4 5 6 7 8 9
## 16 402 132 30 28 19 30 4 8 21 9
replace_na <- function(x) {
ELSE <- TRUE
case_when(x == "?" ~ "1",
ELSE ~ x)
}
# Sử dụng các hàm trên:
bc_data %<>% mutate(classes = dan_nhan(classes),
bare_nuclei = as.character(bare_nuclei),
bare_nuclei = replace_na(bare_nuclei))
# bc_data %>% head()
bc_data$bare_nuclei %>% table()
## .
## 1 10 2 3 4 5 6 7 8 9
## 418 132 30 28 19 30 4 8 21 9
# Chuyển hóa bare_nuclei về đúng bản chất số:
bc_data %<>% mutate(bare_nuclei = as.numeric(bare_nuclei))
# bc_data %>% head()
# Loại bỏ biến không cần thiết:
bc_data %<>% select(-sample_code_number)
#------------------------------------
# Exploratory Data Analysis (EDA)
#------------------------------------
# Tỉ lệ của các nhãn:
bc_data$classes %>% table() / nrow(bc_data)
## .
## B M
## 0.6552217 0.3447783
# Hình ảnh hóa tỉ lệ này:
theme_set(theme_minimal())
bc_data %>%
group_by(classes) %>%
count() %>%
ggplot(aes(classes, n)) +
geom_col()

# Cách 1:
bc_data %>%
gather(features, value, -classes) %>%
ggplot(aes(classes, value)) +
geom_boxplot() +
facet_wrap(~ features, scales = "free")

# Cách 2:
bc_data %>%
ggplot(aes(bare_nuclei, fill = classes, color = classes)) +
geom_density(alpha = 0.3)

# Cách 3:
bc_data %>%
gather(features, value, -classes) %>%
ggplot(aes(value, fill = classes, color = classes)) +
geom_density(alpha = 0.3) +
facet_wrap(~ features, scales = "free")

# Tương quan giữa các biến là không cao:
library(corrplot)
bc_data %>%
select(-classes) %>%
cor() %>%
corrplot(method = "color", order = "hclust")

#---------------------------------
# Support Vector Machine
#---------------------------------
# Để sử dụng SVM cho phân loại cần chuyển hóa biến đích về factor:
bc_data %<>% mutate_if(is.character, as.factor)
# Chuẩn bị dữ liệu:
set.seed(1)
train <- bc_data %>%
group_by(classes) %>%
sample_frac(0.8) %>%
ungroup()
test <- setdiff(bc_data, train)
# Chạy SVM với cost = 10 và gamma = 0.1:
library(e1071)
set.seed(29)
svm_radial <- svm(classes ~.,
data = train,
method = "C-classification",
kernel = "radial",
cost = 10,
gamma = 0.1)
# Sơ bộ mô hình:
summary(svm_radial)
##
## Call:
## svm(formula = classes ~ ., data = train, method = "C-classification",
## kernel = "radial", cost = 10, gamma = 0.1)
##
##
## Parameters:
## SVM-Type: C-classification
## SVM-Kernel: radial
## cost: 10
## gamma: 0.1
##
## Number of Support Vectors: 62
##
## ( 22 40 )
##
##
## Number of Classes: 2
##
## Levels:
## B M
#-----------------------------------
# Evaluating Model Performance
#-----------------------------------
# Sử dụng mô hình cho dự báo:
pred1 <- predict(svm_radial, test, decision.values = TRUE)
# Đánh giá mô hình (cách 1):
mean(pred1 == test$classes)
## [1] 0.9135802
# Đánh giá mô hình (cách 2):
library(caret)
confusionMatrix(pred1, test$classes, positive = "M")
## Confusion Matrix and Statistics
##
## Reference
## Prediction B M
## B 29 1
## M 6 45
##
## Accuracy : 0.9136
## 95% CI : (0.83, 0.9645)
## No Information Rate : 0.5679
## P-Value [Acc > NIR] : 7.279e-12
##
## Kappa : 0.8209
## Mcnemar's Test P-Value : 0.1306
##
## Sensitivity : 0.9783
## Specificity : 0.8286
## Pos Pred Value : 0.8824
## Neg Pred Value : 0.9667
## Prevalence : 0.5679
## Detection Rate : 0.5556
## Detection Prevalence : 0.6296
## Balanced Accuracy : 0.9034
##
## 'Positive' Class : M
##
# SVM với một cặp tham số khác:
set.seed(123)
tune_svm <- svm(classes ~.,
data = train,
method = "C-classification",
kernel = "radial",
cost = 10,
gamma = 0.01)
# Dự báo trên mô hình tốt nhất này:
best_pred <- predict(tune_svm, test, decision.values = TRUE)
confusionMatrix(best_pred, test$classes, positive = "M")
## Confusion Matrix and Statistics
##
## Reference
## Prediction B M
## B 30 1
## M 5 45
##
## Accuracy : 0.9259
## 95% CI : (0.8457, 0.9723)
## No Information Rate : 0.5679
## P-Value [Acc > NIR] : 8.746e-13
##
## Kappa : 0.847
## Mcnemar's Test P-Value : 0.2207
##
## Sensitivity : 0.9783
## Specificity : 0.8571
## Pos Pred Value : 0.9000
## Neg Pred Value : 0.9677
## Prevalence : 0.5679
## Detection Rate : 0.5556
## Detection Prevalence : 0.6173
## Balanced Accuracy : 0.9177
##
## 'Positive' Class : M
##
#-------------------------------------------
# Comparing with some alternatives
#-------------------------------------------
# Đánh giá mô hình:
acc_model1 <- c()
acc_model2 <- c()
for (i in 1:100) {
set.seed(i)
test100 <- bc_data %>%
group_by(classes) %>%
sample_frac(0.3) %>%
ungroup()
pred1 <- predict(svm_radial, test100, decision.values = TRUE)
acc1 <- mean(pred1 == test100$classes)
acc_model1 <- c(acc_model1, acc1)
pred2 <- predict(tune_svm, test100, decision.values = TRUE)
acc2 <- mean(pred2 == test100$classes)
acc_model2 <- c(acc_model2, acc2)
}
# Các kết quả ở dạng data frame:
all_df <- data.frame(Accuracy = c(acc_model1, acc_model2),
Model = c(rep("Model1", 100), rep("Model2", 100)))
# Đánh giá sơ bộ hiệu quả phân loại của hai mô hình bằng hình ảnh:
all_df %>%
ggplot(aes(Model, Accuracy)) +
geom_boxplot()

all_df %>%
ggplot(aes(Accuracy)) + geom_density(alpha = 0.3, color = "blue", fill = "blue") +
geom_histogram(aes(y = ..density..), alpha = 0.3, color = "red", fill = "red") +
facet_wrap(~ Model)

t.test(all_df$Accuracy ~ all_df$Model)
##
## Welch Two Sample t-test
##
## data: all_df$Accuracy by all_df$Model
## t = 9.4719, df = 198, p-value < 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## 0.008524204 0.013006897
## sample estimates:
## mean in group Model1 mean in group Model2
## 0.9804306 0.9696651
#---------------------------------
# Improving Model Performance
#---------------------------------
# Viết hàm chuẩn hóa dữ liệu:
scale_01 <- function(x) {
y <- (x - min(x)) / (max(x) - min(x))
return(y)
}
# Chuẩn hóa dữ liệu:
bc_data_scaled <- bc_data %>% mutate_if(is.numeric, scale_01)
set.seed(1995)
train <- bc_data_scaled %>%
group_by(classes) %>%
sample_frac(0.8) %>%
ungroup()
test <- setdiff(bc_data_scaled, train)
# Chạy SVM với cost = 10 và gamma = 0.1:
set.seed(29)
svm_radial_scaled <- svm(classes ~.,
data = train,
method = "C-classification",
kernel = "radial",
cost = 10,
gamma = 0.1)
pred1 <- predict(svm_radial_scaled, test, decision.values = TRUE)
mean(pred1 == test$classes)
## [1] 0.9518072
acc_model3 <- c()
for (i in 1:100) {
set.seed(i)
test100 <- bc_data_scaled %>%
group_by(classes) %>%
sample_frac(0.3) %>%
ungroup()
pred3 <- predict(svm_radial_scaled, test100, decision.values = TRUE)
acc3 <- mean(pred3 == test100$classes)
acc_model3 <- c(acc_model3, acc3)
}
all_df3 <- data.frame(Accuracy = c(acc_model1, acc_model2, acc_model3),
Model = c(rep("Model1", 100),
rep("Model2", 100),
rep("Model3", 100)))
all_df3 %>%
ggplot(aes(Model, Accuracy)) +
geom_boxplot()

all_df3 %>%
group_by(Model) %>%
summarise_each(funs(mean, median, min, max, sd), Accuracy) %>%
arrange(-Accuracy_mean)
## # A tibble: 3 x 6
## Model Accuracy_mean Accuracy_median Accuracy_min Accuracy_max
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 Model3 0.988 0.990 0.976 1.00
## 2 Model1 0.980 0.981 0.952 1.00
## 3 Model2 0.970 0.971 0.943 0.986
## # ... with 1 more variable: Accuracy_sd <dbl>