LR算法詳解
前言
前景提要,前兩篇文章,借助于Scala Machine Learning Projects一書,利用保險服務的資料,進行了保險行業資料的預測分析,第一篇文章分别用了三種不同的算法進行相同資料的測試,來對比三種算法的模型性能;第二篇則詳細通過LR執行個體解釋了spark機器學習中各種輸入參數和模型評估的輸出參數所代表的的實際意義,本篇文章将詳細闡述LR算法的原理,作用以及代碼實作。
文章一:scala機器學習實戰(一) 保險資料預測分析
文章二:scala機器學習實戰(二) 保險資料預測分析(代碼參數詳解)
内容概要
本篇文章将主要着力于LR算法的原理和實作兩部分,希望能夠以比較非學術性的詞彙,來表達清楚LR算法所想要的表達含義,同時,也将通過代碼實作LR算法,更加深入的了解LR算法的實作過程。
LR産生背景
先看一下下面兩張圖,X表示惡性良性腫瘤大小,Y軸>0.5表示是癌症,否則不是,藍色直線表示預測直線,從圖1中可以看出,X對應到預測直線上的值之後,上面4點是癌症,下面4點不是癌症。再看圖二,當點排列發生變化時,0.5的門檻值就很可能不适用,這主要是線上性拟合中,Y值無限延伸造成的
為了解決Y值無限延伸的問題,LR算法應運而生,把Y值限定在0和1之間,就解決了這個問題,如圖三所示。

LR算法應用場景
LR算法主要解決分類問題的判别機率問題。例如垃圾郵件的判别,推薦系統,疾病預測等等。一般用來判決某件事屬于某個分類的機率來确定類别。例如:一封郵件是垃圾郵件的機率是90%,就可以判定是垃圾郵件;某個使用者可能機率最大的三類産品是華為,小米,OPPO,則給這個使用者推薦的産品就是對應的品牌産品。類似于如此的都可以根據特征機率,來判定屬于某個分類。
LR算法計算公式以及推導
計算公式
線性回歸: z = θ 0 + θ 1 x 1 + θ 2 x 2 + θ 3 x 3 + . . . + θ n x n = θ T x z=\theta_0+\theta_1x_1+\theta_2x_2+\theta_3x_3+...+\theta_nx_n=\theta^Tx z=θ0+θ1x1+θ2x2+θ3x3+...+θnxn=θTx
邏輯回歸:
h θ ( x ) = 1 1 + e − z = 1 1 + e − θ T x h_\theta(x)=\frac{1}{1+e^{-z}}=\frac{1}{1+e^{-\theta^Tx}} hθ(x)=1+e−z1=1+e−θTx1
推導過程
其中,
y = 1 1 + e − x y=\frac{1}{1+e^{-x}} y=1+e−x1 被稱作 s i g m o i d sigmoid sigmoid函數,我們可以看到,LR算法是将線性函數的結果映射到了 s i g m o i d sigmoid sigmoid函數中
s i g m o i d sigmoid sigmoid函數圖形如下:
s i g m o i d sigmoid sigmoid函數的輸出是在(0,1)之間,中間值是0.5,之前的公式 h θ ( x ) h_\theta(x) hθ(x)的含義就很好了解了。
h θ ( x ) h_\theta(x) hθ(x)函數輸出在(0,1)之間,也就表示了資料屬于某一類别的機率,例如當 h θ ( x ) h_\theta(x) hθ(x)<0.5時,資料屬于A類; h θ ( x ) h_\theta(x) hθ(x)>0.5時,資料屬于B類。
h θ ( x ) h_\theta(x) hθ(x)作為一個構造預測函數,他的值有特殊意義,他表示結果取一的機率,是以對于輸入X分類結果為類别A和類别B 的機率分别為:
P ( y = 1 ∣ x ; θ ) = h θ ( x ) P(y=1|x;\theta)=h_\theta(x) P(y=1∣x;θ)=hθ(x)
P ( y = 0 ∣ x ; θ ) = 1 − h θ ( x ) P(y=0|x;\theta)=1-h_\theta(x) P(y=0∣x;θ)=1−hθ(x) (1)
衡量 h θ ( x ) h_\theta(x) hθ(x)函數預測好壞的 C o s t Cost Cost函數和 J ( θ ) J(\theta) J(θ)函數:
C o s t ( h θ ( x ) , y ) = { − l o g ( h θ ( x ) ) if y = 1 − l o g ( 1 − h θ ( x ) ) if y = 0 Cost(h_\theta(x),y)= \begin{cases} -log(h_\theta(x))& \text{if y = 1}\\ -log(1-h_\theta(x))& \text{if y = 0} \end{cases} Cost(hθ(x),y)={−log(hθ(x))−log(1−hθ(x))if y = 1if y = 0 (2)
J ( θ ) = 1 m ∑ i = 1 m C o s t ( h θ ( x ( i ) , y i ) ) = − 1 m [ ∑ i = 1 m y ( i ) l o g h θ ( x i ) + ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ] J(\theta)=\frac{1}{m}\sum_{i=1}^mCost(h_\theta(x^{(i)},y^{i}))=-\frac{1}{m}[\sum_{i=1}^my^{(i)}logh_\theta(x^{i})+(1-y^{(i)})log(1-h_\theta(x^{(i)}))] J(θ)=m1∑i=1mCost(hθ(x(i),yi))=−m1[∑i=1my(i)loghθ(xi)+(1−y(i))log(1−hθ(x(i)))] (3)
将(1)式綜合可得:
P ( y ∣ x ; θ ) = ( h θ ( x ) ) y ( 1 − h θ ( x ) ) 1 − y P(y|x;\theta)=(h_\theta(x))^y(1-h_\theta(x))^{1-y} P(y∣x;θ)=(hθ(x))y(1−hθ(x))1−y (4)
對其取似然函數:
L ( θ ) = ∏ i = 1 m P ( y ( i ) ∣ x ( i ) ; θ ) = ∏ i = 1 m ( h θ ( x ( i ) ) y ( i ) ( 1 − h θ ( x ( i ) ) ) 1 − y ( i ) ) L(\theta)=\prod_{i=1}^mP(y^{(i)}|x{(i)};\theta)=\prod_{i=1}^m(h_\theta(x^{(i)})^{y^{(i)}}(1-h_\theta(x^{(i)}))^{1-y^{(i)}}) L(θ)=∏i=1mP(y(i)∣x(i);θ)=∏i=1m(hθ(x(i))y(i)(1−hθ(x(i)))1−y(i)) (5)
對數似然函數:
l ( θ ) = l o g L ( θ ) = ∑ i = 1 m ( y ( i ) l o g h θ ( x ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ) l(\theta)=logL(\theta)=\sum_{i=1}^{m}(y^{(i)}logh_\theta(x^{(i)})+(1-y^{(i)})log(1-h_\theta(x^{(i)}))) l(θ)=logL(θ)=∑i=1m(y(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i)))) (6)
求 J ( θ ) J(\theta) J(θ)的最小值:(運用的是梯度下降法)
θ j : = θ j − α 1 m s u m i = 1 m ( h θ ( x i ) − y i ) x i j \theta_j:=\theta_j-\alpha\frac{1}{m}sum_{i=1}^m(h_\theta(x_i)-y_i)x^j_i θj:=θj−αm1sumi=1m(hθ(xi)−yi)xij
算法代碼實作
代碼:
object LogisticRegression {
def loadDataSet() = {
//資料矩陣
val dataMat = new ArrayBuffer[Array[Double]]
//标簽矩陣
val labelMat = new ArrayBuffer[Double]
//讀取資料源
val data: BufferedSource = Source.fromFile("")
for (line <- data.getLines()) {
val lineArr = line.trim().split("\t").map(_.toDouble)
dataMat.append(Array(1.0, lineArr(0), lineArr(1)))
//标簽矩陣
labelMat.append(lineArr(2))
}
(dataMat.toArray, labelMat.toArray)
}
//定義sigmoid函數
def sigmoid(inX: Double) = {
1.0 / (1 + math.exp(-inX))
}
def stocGradAscent(dataMatrix: Array[Array[Double]], classLabels: Array[Double], numIter: Int = 150) = {
//訓練資料的長度,有多少條資料
val m = dataMatrix.length
//資料次元,每條資料的屬性
val n = dataMatrix(0).length
//初始權重
var weights = Array.fill(n)(1.0)
for (j <- 1 to numIter) {
val dataIndex: ArrayBuffer[Int] = ArrayBuffer.empty
//添加索引數字
for (loc <- 0 to m - 1) dataIndex.append(loc)
for (i <- 0 to m - 1) {
val alpha = 4 / (1.0 + j + i) + 0.0001
val randIndex = Random.nextInt(dataIndex.length)
val rowZipWeight = dataMatrix(dataIndex(randIndex)).zip((weights))
val h = sigmoid(rowZipWeight.map(x => x._1 * x._2).sum)
//計算預測誤差
val error = classLabels(randIndex) - h
//更新權重
weights = rowZipWeight.map(x => x._1 + alpha * error * x._2)
//移除此次計算的随機數組,下次計算重新生成
dataIndex.remove(randIndex)
}
}
weights
}
def classifyVector(inX:Array[Double],weights:Array[Double])={
//根據sigmoid函數計算的值,來判斷屬于哪一類
val prob = sigmoid(inX.zip(weights).map(x=> x._1* x._2).sum)
if (prob > 0.5) 1.0 else 0.0
}
def main(args: Array[String]): Unit = {
//加載資料
val dataSet = loadDataSet()
//資料矩陣
val dataMatrix = dataSet._1
//标簽矩陣
val classLabels = dataSet._2
val weights = stocGradAscent(dataMatrix,classLabels,100)
//分類結果
val res = dataMatrix.map(x=> classifyVector(x,weights))
//計算準确率
val erroe: Double = res.zip(classLabels).filter(x=> x._1 == x._2).length.toDouble/classLabels.length
}
}
結語
對于LR算法的解釋就到這裡,我們最後的代碼實作了一個簡易的LR算法,後續還會推出其他兩種算法的解釋和簡易代碼實作,盡請關注!!!
下篇文章内容概要
下篇文章将會接着分析GBT算法的原理和代碼實作,歡迎各位大佬指點
如有問題或者指導歡迎添加作者微信:ljelzl416108 ,一同交流學習大資料和機器學習的知識!!!