【機器學(xué)習(xí)(三)邏輯回歸】

邏輯回歸屬于廣義線性模型(Generalized linear model, GLM)
特征:
1.輸入特征進行線性組合
2.輸出都是指數(shù)分布一類的相關(guān)概率分布:正態(tài)分布、泊松分布、二項分布
3.輸出分布和輸入特征的線性組合通過函數(shù)關(guān)聯(lián)
一文搞懂極大似然估計
貝葉斯公式的直觀理解(先驗概率/后驗概率)
一篇文章完全弄懂Logistic回歸(含極大似然估計詳細推導(dǎo)和實現(xiàn)代碼)
{高中生能看懂的}梯度下降是個啥?
邏輯回歸筆記總結(jié)

1.利用線性回歸進行分類

用簡單線性回歸進行分類,增加了兩個極端點會導(dǎo)致結(jié)果變化。

set.seed(8967563)
##構(gòu)建隨機的分類數(shù)據(jù)
no_funding = rnorm(200, 15,15)
#把小于0的數(shù)字變成0,大于0的數(shù)字不變
#sapply()函數(shù)的作用是將列表、向量或數(shù)據(jù)幀作為輸入,并以向量或矩陣的形式給出輸出。sapply(X, FUN):X向量或?qū)ο?,F(xiàn)UN作用于x中每個元素的函數(shù)。
no_funding = sapply(no_funding,function(x) max(x,0)) 
#兩列組合,一列是0,一列是no_funding
no_funding_df = cbind(0,no_funding)
funding = rnorm(120,50,15)
funding = sapply(funding,function(x) max(x,0))
#兩列組合,一列是1,一列是funding
funding_df = cbind(1,funding)
#以上構(gòu)建了兩個類別,均值大的是1類,均值小的是0類
#行綁定,前面120行是funding_df,屬于1類;后200行是no_funding_df,屬于0類
funding_ds = rbind(funding_df, no_funding_df)
fdata = data.frame(funding_ds)
#列命名
names(fdata)=c("y","x1")
#sample隨機抽取行,即打亂行的順序
fdata = fdata[sample(nrow(fdata)),]
library(data.table)
#設(shè)置table屬性,改行名
(setattr(fdata, "row.names", 1:nrow(fdata)))
head(fdata)

myfit = lm(y~x1, data=fdata)
int0.5 <- (0.5 - coef(myfit)[1]) / coef(myfit)[2] 
#coef(myfit)[1]截距,coef(myfit)[2]斜率
#用大于0.5的y是1,小于0.5的y是0來判斷,找到邏輯回歸的臨界點 34.05678

library(ggplot2)
p <- qplot(x1,y,data=fdata) 
p <- p + ggtitle("Classifying with Simple Linear Regression")  
p <- p + geom_abline(intercept=coef(myfit)[1],slope=coef(myfit)[2],  size=1.2, show_guide=T) 
p <- p + geom_vline(xintercept = int0.5, linetype = "longdash") #中間的虛線
p <- p + theme(plot.title = element_text(lineheight=.8, face="bold"))
p

#增加兩行數(shù)據(jù),明顯偏離(1,50)的均值
skewed_fdata=rbind(fdata,c(1,120),c(1,150))
myfit2 = lm(y~x1, data=skewed_fdata)
new_int0.5 <- (0.5 - coef(myfit2)[1]) / coef(myfit2)[2]
#因為兩個很大的偏離點c(1,120),c(1,150),邏輯回歸的臨界點變大了

p2 <- qplot(x1,y,data=skewed_fdata) 
p2 <- p2 + ggtitle("Classifying with Simple Linear Regression")  
p2 <- p2 + geom_abline(intercept=coef(myfit2)[1],slope=coef(myfit2)[2],  size=1.2, show_guide=T, linetype = "dashed") 
p2 <- p2 + geom_abline(intercept=coef(myfit)[1],slope=coef(myfit)[2],  size=1.2, show_guide=T) 
p2 <- p2 + theme(plot.title = element_text(lineheight=.8, face="bold"))
p2

直接使用線性回歸模型來進行判斷,魯棒性非常差。

邏輯函數(shù)

例如,用頭發(fā)長短預(yù)測性別(male/female)
假設(shè)需要知道?? (??) = Pr (?? = 1|??) 和 ??的關(guān)系?? (??) = ??0 + ??1??
預(yù)測某些??的??(??) > 0,或者?? (??)< 0


對logistic函數(shù)進行變換

兩邊取log
logit_seq=seq(-10,10,0.01) #-10到10,以0.01為步長,2001個數(shù)據(jù)
logit_f = 1/(1+exp(-logit_seq))

p <- qplot(logit_seq,logit_f) #設(shè)置x軸,y軸
p <- p + ggtitle("The Logistic Function")  #坐標(biāo)名稱
p <- p + theme(plot.title = element_text(lineheight=.8, face="bold"))
p <- p + xlab("x")  
p <- p + ylab("f(x)")  
p

y的取值范圍在[0,1]之間,在遠離0的地方函數(shù)的值會很快接近0或者1。當(dāng)x>0時,y取值為1的可能性變大,當(dāng)x<0時,y取值的0的可能性變大


2.預(yù)測心臟病

#讀取數(shù)據(jù)
heart <- read.table("heart.dat", quote="\"")
names(heart) <- c("AGE", "SEX", "CHESTPAIN", "RESTBP", "CHOL", "SUGAR", "ECG", "MAXHR", "ANGINA", "DEP", "EXERCISE", "FLUOR", "THAL", "OUTPUT")

#將一些特征因子化
heart$CHESTPAIN = factor(heart$CHESTPAIN)
heart$ECG = factor(heart$ECG)
heart$THAL = factor(heart$THAL)
heart$EXERCISE = factor(heart$EXERCISE)
#將輸出范圍限制在0和1之間(之前是1和2)
heart$OUTPUT = heart$OUTPUT-1

#劃分訓(xùn)練集和測試集
library(caret)
set.seed(987954)
#按照OUTPUT進行索引,提取85%作為訓(xùn)練集
heart_sampling_vector <- createDataPartition(heart$OUTPUT, p = 0.85, list = FALSE)
# 獲取訓(xùn)練集數(shù)據(jù)
heart_train <- heart[heart_sampling_vector,]
heart_train_labels <- heart$OUTPUT[heart_sampling_vector]
# 獲取測試集數(shù)據(jù)
heart_test <- heart[-heart_sampling_vector,]
heart_test_labels <- heart$OUTPUT[-heart_sampling_vector]

#進行邏輯回歸
heart_model = glm(OUTPUT~.,data=heart_train,family=binomial("logit"))

#查看回歸后的結(jié)果
summary(heart_model)

從上面的輸出,可以看到:
glm函數(shù)會自動將一些因子化的變量根據(jù)level的數(shù)量自動轉(zhuǎn)換為n-1個虛擬變量。其中n=level的數(shù)量。(caret包中的dummyVars函數(shù)也有同樣的功能)。

注意到,年齡的系數(shù)是負的,這跟常識不符(年齡越大,得心臟病的幾率越大),因此,懷疑存在多重共線性的問題。

heart_model2 = glm(OUTPUT~AGE,data=heart_train,family=binomial("logit"))
summary(heart_model2)

單獨對年齡進行回歸,發(fā)現(xiàn)年齡跟實際的心臟病是成正相關(guān)的,且P-value 變小了,顯著性明顯增強,因此更能證明heart_model2存在多重共線性問題。


3.評估邏輯回歸模型

? 偏差殘差
由于邏輯回歸中,

本身就是一個概率函數(shù),因此邏輯回歸方程中沒有誤差項。因此在,glm中,看不到殘差項,但是為了跟線性回歸保持一致,引入了一個偏差殘差。這里的偏差類似于上面提到的代價函數(shù)(損失函數(shù)),它是似然函數(shù)取對數(shù)再乘以-2,偏差殘差就是在偏差的基礎(chǔ)上取根號后,再求平方和。

? 空偏差
是指沒有任何特征,只有常數(shù)項??0的偏差。類似于線性回歸中的總平方和(TSS)。其對應(yīng)關(guān)系如下:

##【以下全部都是3大評價函數(shù)R方、P值、方差的準(zhǔn)備函數(shù)】
#【準(zhǔn)備函數(shù)1】
# 計算個體觀測數(shù)據(jù)的對數(shù)似然
log_likelihoods <- function(y_labels,y_probs) {
  y_a = as.numeric(y_labels)
  y_p = as.numeric(y_probs)
  y_a * log(y_p) + (1 - y_a) * log(1 - y_p)
}

#【準(zhǔn)備函數(shù)2】
# 計算整個數(shù)據(jù)集的對數(shù)似然
dataset_log_likelihood <- function(y_labels,y_probs) {
  sum(log_likelihoods(y_labels,y_probs))
}

#【準(zhǔn)備函數(shù)3】
# 計算一個數(shù)據(jù)的偏差
deviances <- function(y_labels,y_probs) {
  -2*log_likelihoods(y_labels,y_probs)
}

#【準(zhǔn)備函數(shù)4】
# 計算整個數(shù)據(jù)集的偏差,所有觀測數(shù)據(jù)的偏差和
dataset_deviance <- function(y_labels, y_probs) {
  sum(deviances(y_labels,y_probs))
}

#【準(zhǔn)備函數(shù)5】
# 計算模型的偏差
model_deviance <- function(model, data, output_column) {
  y_labels = data[[output_column]]
  y_probs = predict(model, newdata=data, type = "response")
  dataset_deviance(y_labels, y_probs)
}

#【準(zhǔn)備函數(shù)6】
# 計算空模型偏差
null_deviance <- function(data, output_column) {
  y_labels = data[[output_column]]
  y_probs = mean(data[[output_column]])
  dataset_deviance(y_labels, y_probs)
}

#【評價方式1】Rseudo R^2,代表解釋了多少比例的空偏差
model_pseudo_r_squared <- function(model, data, output_column) {
  1 - (model_deviance(model, data, output_column)/null_deviance(data, output_column))
}

model_pseudo_r_squared(heart_model,data=heart_train,output_column="OUTPUT")

#【評價方式2】P值
model_chi_squared_p_value <-  function(model, data, output_column) {
  null_df = nrow(data) - 1
  model_df = nrow(data) - length(model$coefficients)
  difference_df = null_df - model_df
  null_deviance = null_deviance(data, output_column)
  m_deviance = model_deviance(model, data, output_column)
  difference_deviance = null_deviance - m_deviance
  pchisq(difference_deviance, difference_df,lower.tail=F)
}

#【評價方式3】殘差
model_deviance_residuals <- function(model, data, output_column) {
  y_labels = data[[output_column]]
  y_probs = predict(model, newdata=data, type = "response")
  residual_sign = sign(y_labels - y_probs)
  residuals = sqrt(deviances(y_labels,y_probs))
  residual_sign*residuals
}

4.模型性能測試

有了模型后,就可以來進行預(yù)測:

#對訓(xùn)練集進行預(yù)測,查看模型在訓(xùn)練集上的擬合程度:
train_predictions = predict(heart_model, newdata=heart_train, type="response")  #type=response則返回為概率值
#對于每一個觀測值,大于0.5的判斷為有?。?),小于等于0.5的判斷為沒有病
train_class_predictions = as.numeric(train_predictions>0.5)
#統(tǒng)計預(yù)測正確率
mean(train_class_predictions==heart_train$OUTPUT)
#查看預(yù)測錯誤的數(shù)據(jù)
train_predictions[train_class_predictions!=heart_train$OUTPUT]

#對測試集進行預(yù)測,查看我們模型在訓(xùn)練集上的擬合程度:
test_predictions = predict(heart_model, newdata=heart_test, type="response")
test_class_predictions = as.numeric(test_predictions>0.5)
mean(test_class_predictions==heart_test$OUTPUT) 

返回數(shù)值越高,意味著其精度越高。


5.分類指標(biāo)

  • 查全率(Recall):正確1/真實1
  • 查準(zhǔn)率(Precise):真實1/(真實1+錯誤1)
  • 特異性(Specificity):真實0/(真實0+錯誤0)

在這里引入一個評判分類模型好壞的統(tǒng)計量——AUC。
ROC(Receiver Operating Characteristic Curve),即受試者工作特征曲線。
該曲線的橫坐標(biāo)為假陽性率(False Positive Rate, FPR),F(xiàn)PR=FP/(FP+FN),縱坐標(biāo)為真陽性率(True Positive Rate, TPR), TPR=TP/(TP+TN),可以看到,其實真陽性率就是之前說到的查全率。
ROC的圖形一般如下:



對于同一個模型,判斷的閾值不同,對應(yīng)的TPR和FPR也會不同。所有閾值對應(yīng)的PR和FPR形成的點連起來,就形成了ROC曲線。

#計算混淆矩陣
confusion_matrix <- table(predicted = train_class_predictions, actual = heart_train$OUTPUT)
#計算查準(zhǔn)率
precision <- confusion_matrix[2,2]/sum(confusion_matrix[2,])
#計算查全率
recall <- confusion_matrix[2,2]/sum(confusion_matrix[,2])
#計算F分?jǐn)?shù)
f = 2 * precision * recall / (precision + recall)

#畫ROC曲線或者P-R曲線
library(ROCR)
train_predictions = predict(heart_model, newdata=heart_train, type="response")
pred <- prediction(train_predictions, heart_train$OUTPUT)
perf <- performance(pred, measure = "prec", x.measure = "rec") #畫p-R曲線
perf <- performance(pred,"tpr","fpr"); #畫ROC曲線

plot(perf, main = "Precision-Recall Curve for Heart Model", lwd = 2)

一般會希望真陽性率越高越好,而假陽性率越低越好,也就是點越靠近左上角,結(jié)果是越好的。
所以,定義了曲線下面積 AUC 。這個面積越大就說明整體更靠近左上,效果也就越好。


6.正則化與過擬合

對于邏輯回歸,當(dāng)特征數(shù)較多時,也會有可能存在過擬合的情況。在線性回歸中的有偏估計(Lasso和嶺回歸),對于邏輯回歸同樣有用:

library(glmnet)
#使用model.matrix創(chuàng)建特征矩陣,確保各列都是數(shù)值型
heart_train_mat <- model.matrix(OUTPUT~., heart_train)[,-1]
#給定lamda范圍,從10^8 到 10^-4,平均生成250個lamda
lambdas <- 10 ^ seq(8,-4,length=250)
#lasso回歸
heart_models_lasso= glmnet(heart_train_mat,heart_train$OUTPUT,alpha=1,lambda=lambdas, family="binomial")

#使用cv.glmnet選擇最優(yōu)的??,family="binomial"二分類
lasso.cv <- cv.glmnet(heart_train_mat,heart_train$OUTPUT,alpha=1,lambda=lambdas,family="binomial")
lambda_lasso <- lasso.cv$lambda.min
lambda_lasso

#輸出新數(shù)據(jù)的預(yù)測值,type參數(shù)允許選擇預(yù)測的類型并提供預(yù)測值,newx代表要預(yù)測的數(shù)據(jù)
predict(heart_models_lasso, type="coefficients", s = lambda_lasso)

lasso_train_predictions <- predict(heart_models_lasso, s = lambda_lasso, newx = heart_train_mat, type="response")
lasso_train_class_predictions = as.numeric(lasso_train_predictions>0.5)
mean(lasso_train_class_predictions==heart_train$OUTPUT)

heart_test_mat <- model.matrix(OUTPUT~., heart_test)[,-1]
lasso_test_predictions <- predict(heart_models_lasso, s = lambda_lasso, newx = heart_test_mat, type="response")
lasso_test_class_predictions = as.numeric(lasso_test_predictions>0.5)
mean(lasso_test_class_predictions==heart_test$OUTPUT)

7.多元邏輯回歸模型

1)無序多元邏輯回歸

對于無序多元邏輯回歸,可建立K-1個二元回歸模型(其中k表示類別個數(shù))。給定一個數(shù)據(jù)點,K-1個模型都會運行,概率最大的類別將會被選為預(yù)測類別。對于輸入點x,分類結(jié)果為各類別的概率分別為如下公式:


預(yù)測玻璃的類型

在R中,無序邏輯回歸可以使用glmnet::multinorm函數(shù)來預(yù)測:

#讀取數(shù)據(jù)
glass <- read.csv("glass.data", header=FALSE)
#重命名屬性
names(glass) <- c("id","RI","Na", "Mg", "Al", "Si", "K", "Ca", "Ba", "Fe", "Type")
#去掉第一列(ID不是特征)
glass <- glass[,-1]

#劃分訓(xùn)練集和測試集
set.seed(4365677)
glass_sampling_vector <- createDataPartition(glass$Type, p = 0.80, list = FALSE)
glass_train <- glass[glass_sampling_vector,]
glass_test <- glass[-glass_sampling_vector,]

#使用multinom函數(shù)來進行多元邏輯回歸
library(nnet)
glass_model <- multinom(Type ~ ., data = glass_train, maxit=1000)
#查看模型結(jié)果
summary(glass_model)

Type中有6種輸出變量水平(1,2,3,5,6,7),所以有5組系數(shù)(2,3,5,6,7)。
其原因是,多項邏輯回歸的原理是,在結(jié)果變量的類別中,會選擇一個類別作為參考類別,其他各個類別的系數(shù)結(jié)果都是以參考類別作為參考(即定義為1),進一步計算及進行詮釋。
在二分類的邏輯回歸中,也應(yīng)用到了這一點,0/1變量中,默認的0是參考類別,1是感興趣類別。但是由于二分類結(jié)果沒有第三個類別,所以不需要額外提出所謂的參考和非參考類別。但是多項邏輯回歸不同,有多個類別在結(jié)果變量,需要考慮參考類別的選擇。

#查看訓(xùn)練集的回歸結(jié)果
glass_predictions <- predict(glass_model, glass_train)
mean(glass_predictions==glass_train$Type)
table(predicted=glass_predictions,actual=glass_train$Type)

#查看測試集的回歸結(jié)果
glass_test_predictions <- predict(glass_model, glass_test)
mean(glass_test_predictions==glass_test$Type)
table(predicted = glass_test_predictions,actual =glass_test$Type)
  • 1和2預(yù)測得較差
  • 3完全被1和2混為一談
  • 6被正確區(qū)分,7只有1個錯誤

關(guān)于模型結(jié)果的解讀,參考R-多項邏輯回歸/多類別邏輯回歸

2)有序多元邏輯回歸

對于有序邏輯回歸,如果有K個類別,需要對一個二元邏輯回歸模型的輸出設(shè)置K-1個閾值。該模型用決定該直線斜率的一組βi系數(shù),但是包含K-1個截距項。

預(yù)測葡萄酒品質(zhì)


在R中,有序邏輯回歸可以使MASS::polr 函數(shù)來進行模型預(yù)測。

winequality.white <- read.csv("winequality-white.csv", sep=";")
wine <- winequality.white
#由于wine的品質(zhì)有1,2,3,4,5,6,7等多種類型,但是實際上不需要這么多類型
#劃分好、中、壞三種類型,小于5的歸為0,大于6的歸為2,其余歸為1
wine$quality <- factor(ifelse(wine$quality < 5, 0, ifelse(wine$quality > 6, 2, 1)))

#劃分測試集和訓(xùn)練集
set.seed(7644)
wine_sampling_vector <- createDataPartition(wine$quality, p = 0.80, list = FALSE)
wine_train <- wine[wine_sampling_vector,]
wine_test <- wine[-wine_sampling_vector,]

#使用polr函數(shù)來進行有序邏輯回歸
library(MASS)
wine_model <- polr(quality ~., data = wine_train, Hess=T)
#查看回歸結(jié)果
summary(wine_model)

#查看訓(xùn)練集的回歸結(jié)果
wine_predictions <- predict(wine_model, wine_train)
mean(wine_predictions==wine_train$quality)
table(predicted=wine_predictions,actual=wine_train$quality)

#查看測試集的回歸結(jié)果
wine_test_predictions <- predict(wine_model, wine_test)
mean(wine_test_predictions==wine_test$quality)
table(predicted = wine_test_predictions,actual =wine_test$quality)

從上面的結(jié)果可以看到,有3個類別,產(chǎn)生了兩個截距項。
關(guān)于有序邏輯回歸的更多解釋,可以參考《有序logistic回歸》。

對于有序多元邏輯回歸,我們也可以采用多分類器的方法來進行回歸預(yù)測。

wine_model2 <- multinom(quality ~ ., data = wine_train, maxit=1000)
wine_predictions2 <- predict(wine_model2, wine_test)
mean(wine_predictions2==wine_test$quality)
table(predicted=wine_predictions2,actual=wine_test$quality)

#比較兩種分類器的優(yōu)劣
AIC(wine_model)
AIC(wine_model2)

#有序邏輯回歸,也可以使用step函數(shù)來進行參數(shù)的選擇(默認是向后剔除)
wine_model3 <- step(wine_model)
wine_test_predictions3 <- predict(wine_model3, wine_test)
mean(wine_test_predictions3==wine_test$quality)
table(predicted = wine_test_predictions3,actual =wine_test$quality)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容