R語言與機器學習學習筆記(分類算法)(6)logistic迴歸

邏輯迴歸研究因變量Y爲分類變量與多個自變量X之間的迴歸問題。隨機變量X的取值爲實數,隨機變量Y的取值爲1或0。常用於預測某隨機事件發生概率的大小。

Logistic迴歸問題的最優化問題可以表述爲:

尋找一個非線性函數Sigmoid的最佳擬合參數,求解過程可使用最優化算法完成。它可以看做是用Sigmoid函數作爲二閾值分類器的感知器問題。

一、logistic迴歸及其MLE

當我們考慮解釋變量爲分類變量,如考慮一個企業是否會被併購,一個企業是否會上市這些問題時,考慮線性概率模型 P(yi=1)=β0+β1xi 顯然是不合適的,它至少有兩個致命的缺陷:

1、概率估計值可能超過1,使得模型失去了意義;(要解決這個問題並不麻煩,我們將預測超過1的部分記爲1,低於0的部分記爲0,就可以解決。這個解決辦法就是計量裏有一定歷史的tobit模型)

2、邊際效應假定爲不變,通常來說不合經濟學常識。考慮一個邊際效應遞減的模型(假定真實值爲藍線),可以看到線性模型表現很差。

但是sigmoid函數去擬合藍線確實十分合適的。Sigmoid函數形式爲


圖形如下所示


於是我們可以考慮logistic迴歸模型:

對輸入X進行分類的線性函數,其值域爲實數域,通過Logistic迴歸模型定義式將線性函數轉換爲概率。線性函數的值越接近於正無窮,概率值越接近於1;線性函數越接近於負無窮,概率值越接近於0。

假定有N個觀測樣本Y1,Y2,…,YN,設P(Yi=1|Xi)=π(Xi)爲給定條件Xi下得到結果Yi=1的條件概率;而在同樣條件下得到結果Yi=0的條件概率爲P(Yi=0|Xi)=1-π(Xi)。似然函數爲

1-Yi假設各觀測獨立,對logistic迴歸模型來說,其對數似然函數爲:

因爲

所以

將問題變成了以對數似然函數爲目標函數的最優化問題,可以採用梯度下降法或擬牛頓法求解出logistic模型的MLE。


二、logit還是probit?

雖說Sigmoid函數對邊際遞減的模型擬合良好,但是我們也要知道S型函數並非僅sigmoid函數一個,絕大多數的累積分佈函數都是S型的。

於是考慮F-1(P)(F爲標準正態分佈的累積分佈函數)也不失爲一個很好的選擇。像這樣的,對概率P做一點變換,讓變換後的取值範圍變得合理,且變換後我們能夠有辦法進行參數估計的,就涉及到廣義線性模型理論中的連接函數。在廣義線性模型中我們把log(P/(1-P))稱爲logit,F-1(P)(F爲標準正態分佈的累積分佈函數)稱爲probit。那麼這裏就涉及到一個選擇的問題:連接函數選logit還是probit?

logistic迴歸認爲二分類變量服從伯努利分佈,應當選擇logit,而且從解釋的角度說,p/(1-p)就是我們常說的機率(odds ratio),也就是軟件報告中出現的OR值。但是probit也有它合理的一面,首先,中心極限定理告訴我們,伯努利分佈在樣本夠多的時候就是近似正態分佈的;其次,從不確定性的角度考慮,probit認爲我們的線性概率模型服從正態分佈,這也是更爲合理的。

我們來看一下經過變換後,自變量和P的關係是什麼樣子的:

 

如果你確實想知道到底你的數據用哪一個方法好,也不是沒有辦法,你可以看一下你的殘差到底是符合logit函數呢還是符合probit函數,當然,憑肉眼肯定是看不出來的,因爲這兩個函數本來就很接近,你可以通過函數的假定,用擬合優度檢驗一下。但通常,估計不會有人非要這麼較真,因爲沒有必要。但是有一點是要注意的,logit模型較probit模型而言具有厚尾的特徵,這也是爲什麼經濟學論文愛用logit的原因。

 我們以鳶尾花數據中的virginica,versicolor兩類數據分類爲例,看看兩種辦法分類有無差別。

 

probit.predictions

versicolor virginica

versicolor 47 3

virginica 3 47

logit.predictions

versicolor virginica

versicolor 47 3

virginica 3 47

從上圖與比較表格均可以看出,兩者差別不大。

三、多項式logit與order logit

對於二項分類模型的一個自然而然的推廣便是多項分類模型。

我們借鑑神經網絡裏提到的異或模型,有:

 

按照上面這種方法,給定一個輸入向量x,獲得最大hθ(x)的類就是x所分到的類。

選擇最大的 hθ(x)十分好理解:在類別選擇問題中,不論要選的類別是什麼,每一個類別對做選擇的經濟個體來說都有或多或少的效用(沒有效用的類別當然不會被考慮) ,一個類別的脫穎而出必然是因爲該類別能產生出最高的效用。

我們將多項logit模型的數學表述敘述如下:

假定對於第i個觀測,因變量Yi有M個取值:1,2,…,M,自變量爲Xi,則多項logit模型爲:

 

 與logistic迴歸的似然估計類似,我們可以很容易寫出多項logit的對數似然函數:

多項 Logit模型雖然好用,但從上面的敘述可以看出,多項 Logit 模型最大的限制在於各個類別必須是對等的,因此在可供選擇的類別中,不可有主要類別和次要類別混雜在一起的情形。例如在研究旅遊交通工具的選擇時,可將交通工具的類別粗分爲航空、火車、公用汽車、自用汽車四大類,但若將航空類別再依三家航空公司細分出三類而得到總共六個類別,則多項 Logit 模型就不適用,因爲航空、火車、公用汽車、自用汽車均屬同一等級的主要類別,而航空公司的區別則很明顯的是較次要的類別,不應該混雜在一起。在這個例子中,主要類別和次要類別很容易分辨,但在其他的研究中可能就不是那麼容易,若不慎將不同層級的類別混在一起,則由多項 Logit 模型所得到的實證結果就會有誤差。

對於分類模型,我們還會遇到被解釋變量中有分類變量的情形。對於連續變量解釋離散變量,且被解釋的離散變量是有順序的(這個是和多項logit最大的區別)的情形,我們就需要考慮到order logit模型。

其數學模型敘述如下:


其中,F(.)表示累積分佈函數,當F表示正態分佈的分佈函數時,對應的是order probit;F表示logistic分佈時,變對應order logit。

與logistic分佈類似,我們可以很容易寫出其對數似然函數:

四、啞變量(dummy variable)

在logistic迴歸中,經常會遇到解釋變量爲分類變量的情形,比如收入:高、中、低;地域:北京、上海、廣州等。這裏對分類變量而言就涉及一個問題:要不要將分類變量設置dummy variable?

這個問題的答案在線性模型中很顯然,必須要這麼做!!!如果我們不設置啞變量,而是單純地賦值:北京=1,上海=2,廣州=3,即我們將自變量視作連續性的數值變量,但這僅僅是一個代碼而己,並不意味着地域間存在大小次序的關係,即並非代表被解釋變量(響應變量)會按此順序線性增加或減少。即使是有序多分類變量,如家庭收入分爲高、中、低三檔,各類別間的差距也是無法準確衡量的,按編碼數值來分析實際上就是強行規定爲等距,這顯然可能引起更大的誤差。

但是在logistic迴歸中,由於logit(p)變化的特殊性,在解釋定序變量時,爲了減少自由度(即解釋變量個數),我們常常將定序變量(如家庭收入分爲高、中、低)視爲連續的數值變量,而且經濟解釋可以是XX每提高一個檔次,相應的概率會提高expression(delta(XX))(expression的表達式還是很複雜的,不打了)。當然減少變量個數是以犧牲預測精度爲代價的。畢竟數據處理是一門藝術而非一門技術,如何取捨還得具體問題具體分析。當然,非定序的分類變量是萬萬不可將其視爲數值變量的。

五、廣義線性模型的R實現

R語言提供了廣義線性模型的擬合函數glm(),其調用格式如下:

glm(formula, family = gaussian, data,weights, subset,na.action, start = NULL, etastart, mustart, offset,control= list(...), model = TRUE, method = "glm.fit",x =FALSE, y = TRUE, contrasts = NULL, ...)

參數說明:

Formula:迴歸形式,與lm()函數的formula參數用法一致

Family:設置廣義線性模型連接函數的典則分佈族,glm()提供正態、指數、gamma、逆高斯、Poisson、二項分

布。我們的logistic迴歸使用的是二項分佈族binomial。Binomial族默認連接函數爲logit,可設置爲probit。

Data:數據集

鳶尾花例子使用的R代碼:

 logit.fit <- glm(Species~Petal.Width+Petal.Length,

family = binomial(link = 'logit'),

data = iris[51:150,])

logit.predictions <- ifelse(predict(logit.fit) > 0,'virginica', 'versicolor')

table(iris[51:150,5],logit.predictions)

 

probit.fit <- glm(Species~Petal.Width+Petal.Length,

family = quasibinomial(link = 'probit'),

data = iris[51:150,])

probit.predictions <- ifelse(predict(probit.fit) >0,'virginica', 'versicolor')

table(iris[51:150,5],probit.predictions)

 

 

程序包mlogit提供了多項logit的模型擬合函數:

mlogit(formula, data, subset, weights,na.action, start = NULL,

alt.subset = NULL, reflevel = NULL,

nests = NULL, un.nest.el = FALSE, unscaled = FALSE,

heterosc = FALSE, rpar = NULL, probit = FALSE,

R = 40, correlation = FALSE, halton = NULL,

random.nb = NULL, panel = FALSE, estimate = TRUE,

seed = 10, ...)

mlogit.data(data, choice, shape = c("wide","long"), varying = NULL,

sep=".",alt.var = NULL, chid.var = NULL, alt.levels = NULL,id.var = NULL, opposite = NULL, drop.index = FALSE,

ranked = FALSE, ...)

參數說明:

formula:mlogit提供了條件logit,多項logit,混合logit多種模型,對於多項logit的估計模型應寫爲:因變量~0|自變量,如:mode ~ 0 | income

data:使用mlogit.data函數使得數據結構符合mlogit函數要求。

Choice:確定分類變量是什麼

Shape:如果每一行是一個觀測,我們選擇wide,如果每一行是表示一個選擇,那麼就應該選擇long。

alt.var:對於shape爲long的數據,需要標明所有的選擇名稱

選擇wide的數據示例:

 

選擇long的數據示例:

以fishing數據爲例,來說明如何使用mlogit。

library(mlogit)

data("Fishing", package = "mlogit")

Fish <- mlogit.data(Fishing,shape = "wide",choice = "mode")

summary(mlogit(mode ~ 0 | income, data = Fish))

 

這個輸出的結果與nnet包中的multinom()函數一致。由於mlogit包可以做的logit模型更多,所以這裏就不在對nnet

 

包的multinom作介紹了,可以參見《根據Econometrics in R一書,將回歸方法總結一下》一文。

程序包MASS提供polr()函數可以進行ordered logit或probit迴歸。用法如下:

polr(formula, data, weights, start, ..., subset, na.action,

contrasts = NULL, Hess = FALSE, model = TRUE,

method = c("logistic", "probit", "cloglog", "cauchit"))

參數說明:

Formula:迴歸形式,與lm()函數的formula參數用法一致

Data:數據集

Method:默認爲order logit,選擇probit時變爲order probit模型。

以housing數據爲例說明函數用法:

house.plr <- polr(Sat ~ Infl + Type + Cont, weights = Freq, data = housing)

house.plr

summary(house.plr, digits = 3)

 

這些結果十分直觀,易於解讀,所以我們在這裏省略所有的輸出結果。

再看手寫數字案例:

最後,我們回到最開始的那個手寫數字的案例,我們試着利用多項logit重做這個案例。(這個案例的描述與數據參見《kNN算法》一章)

特徵的選擇可參見《神經網絡》一章。

由於手寫數字的特徵選取很容易導致迴歸係數矩陣是降秩的,所以我們使用nnet包的multinom()函數代替mlogit()。

運行下列代碼:

setwd("D:/R/data/digits/trainingDigits")

names<-list.files("D:/R/data/digits/trainingDigits")

data<-paste("train",1:1934,sep="")

for(i in 1:length(names))

assign(data[i],as.matrix(read.fwf(names[i],widths=rep(1,32))))

 

label<-factor(rep(0:9,c(189,198,195,199,186,187,195,201,180,204)))

 

feature<-matrix(rep(0,length(names)*25),length(names),25)

for(i in 1:length(names)){

feature[i,1]<-sum(get(data[i])[,16])

feature[i,2]<-sum(get(data[i])[,8])

feature[i,3]<-sum(get(data[i])[,24])

feature[i,4]<-sum(get(data[i])[16,])

feature[i,5]<-sum(get(data[i])[11,])

feature[i,6]<-sum(get(data[i])[21,])

feature[i,7]<-sum(diag(get(data[i])))

feature[i,8]<-sum(diag(get(data[i])[,32:1]))

feature[i,9]<-sum((get(data[i])[17:32,17:32]))

feature[i,10]<-sum((get(data[i])[1:8,1:8]))

feature[i,11]<-sum((get(data[i])[9:16,1:8]))

feature[i,12]<-sum((get(data[i])[17:24,1:8]))

feature[i,13]<-sum((get(data[i])[25:32,1:8]))

feature[i,14]<-sum((get(data[i])[1:8,9:16]))

feature[i,15]<-sum((get(data[i])[9:16,9:16]))

feature[i,16]<-sum((get(data[i])[17:24,9:16]))

feature[i,17]<-sum((get(data[i])[25:32,9:16]))

feature[i,18]<-sum((get(data[i])[1:8,17:24]))

feature[i,19]<-sum((get(data[i])[9:16,17:24]))

feature[i,20]<-sum((get(data[i])[17:24,17:24]))

feature[i,21]<-sum((get(data[i])[25:32,17:24]))

feature[i,22]<-sum((get(data[i])[1:8,25:32]))

feature[i,23]<-sum((get(data[i])[9:16,25:32]))

feature[i,24]<-sum((get(data[i])[17:24,25:32]))

feature[i,25]<-sum((get(data[i])[25:32,25:32]))

}

data1 <- data.frame(feature,label)

 

#降秩時mlogit不可用

#data10<- mlogit.data(data1,shape = "wide",choice = "label")

#m1<-mlogit(label~0|X1+X2+X3+X4+X5+X6+X7+X8+X9+X10+X11+X12+X13+X14+X15+X16+X17+X18+X19+X20+X21+X22+X23+X24+X25,data=data10)

 

library(nnet)

m1<-multinom(label ~ ., data = data1)

pred<-predict(m1,data1)

table(pred,label)

sum(diag(table(pred,label)))/length(names)

 

 

 

setwd("D:/R/data/digits/testDigits")

name<-list.files("D:/R/data/digits/testDigits")

data1<-paste("train",1:1934,sep="")

for(i in 1:length(name))

assign(data1[i],as.matrix(read.fwf(name[i],widths=rep(1,32))))

 

feature<-matrix(rep(0,length(name)*25),length(name),25)

for(i in 1:length(name)){

feature[i,1]<-sum(get(data1[i])[,16])

feature[i,2]<-sum(get(data1[i])[,8])

feature[i,3]<-sum(get(data1[i])[,24])

feature[i,4]<-sum(get(data1[i])[16,])

feature[i,5]<-sum(get(data1[i])[11,])

feature[i,6]<-sum(get(data1[i])[21,])

feature[i,7]<-sum(diag(get(data1[i])))

feature[i,8]<-sum(diag(get(data1[i])[,32:1]))

feature[i,9]<-sum((get(data1[i])[17:32,17:32]))

feature[i,10]<-sum((get(data1[i])[1:8,1:8]))

feature[i,11]<-sum((get(data1[i])[9:16,1:8]))

feature[i,12]<-sum((get(data1[i])[17:24,1:8]))

feature[i,13]<-sum((get(data1[i])[25:32,1:8]))

feature[i,14]<-sum((get(data1[i])[1:8,9:16]))

feature[i,15]<-sum((get(data1[i])[9:16,9:16]))

feature[i,16]<-sum((get(data1[i])[17:24,9:16]))

feature[i,17]<-sum((get(data1[i])[25:32,9:16]))

feature[i,18]<-sum((get(data1[i])[1:8,17:24]))

feature[i,19]<-sum((get(data1[i])[9:16,17:24]))

feature[i,20]<-sum((get(data1[i])[17:24,17:24]))

feature[i,21]<-sum((get(data1[i])[25:32,17:24]))

feature[i,22]<-sum((get(data1[i])[1:8,25:32]))

feature[i,23]<-sum((get(data1[i])[9:16,25:32]))

feature[i,24]<-sum((get(data1[i])[17:24,25:32]))

feature[i,25]<-sum((get(data1[i])[25:32,25:32]))

}

labeltest<-factor(rep(0:9,c(87,97,92,85,114,108,87,96,91,89)))

data2<-data.frame(feature,labeltest)

pred1<-predict(m1,data2)

table(pred1,labeltest)

sum(diag(table(pred1,labeltest)))/length(name)

 

 

經整理,輸出結果如下:(左邊爲訓練集,右邊爲測試集)

 

 

Tips: oddsratio=p/1-p 相對風險指數貝努力模型中 P是發生A事件的概率,1-p是不發生A事件的概率所以p/1-p是 發生與不發生的相對風險。OR值等於1,表示該因素對A事件發生不起作用;OR值大於1,表示A事件發生的可能性大於不發生的可能性;OR值小於1,表示A事件不發生的可能性大於發生的可能性。

 

 

Further reading:

Yves Croissant:Estimation of multinomial logit models in R : The mlogit Packages

發佈了41 篇原創文章 · 獲贊 41 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章