ml笔记2: classification and logistic regression

笔记中理论部份来自 Andrew Ng 公开课,工程部份来自 spark 1.6. 我的理解 logistic regression 一般用于二元分类(binary classification), 有的翻译成逻辑回归,但是周志华的书是 对数回归, 算了还是不翻译。这一章涉及大量线性代数,概率论和统计分布,顺便复习下大一课程(:... 这篇本记写得有点跳跃和凌乱。


分类和线性回归

线性回归预测的数据是连续的,正如房价问题。但是分类只对应几个离散值,常见如垃圾邮件,只有0或1(二元分类 binary classification). 但是分类也是构建在线性回归问题之上的,回顾线性方程

h(x)=𝜃0 +𝜃1𝑥1 +𝜃2𝑥2 =∑𝜃𝑖𝑥𝑖 =𝜃T𝑥

分类就是在h(x)函数之上作用了g(z)的映射

g(z)=1/(1+e^(-z))

这层映射函数也叫做 sigmoid 函数,图形如下

sigmoid函数

其实很好理解,当z值很大时 e^(-z)拉近0,那么 g(z) 就越接近1, 同样当 z 值很小时,e^(-z) 就很大,那么 g(z) 就越接近0. 其实映射函数有多种选择,在初中就有条件函数,比如当 x >0 时 y=1, 当 x<=0 时 y =0 。之所以选择 g(z) 这样的 sigmoid 函数,是因为连续性数学上求导方便(可能还有其它原因,比如正态分布),另外g(z) 函数求导还有一个很重要的特性

g(z)求导特性

简单的链式求导,蛮容易理解的,后面会用到。

极大似然估计MLE

likelihood 思想在贝叶斯公式中很常见(极大似然太烧脑,头疼)

后验概率 = 先验概率 * 似然估计

这是一类人们对未知事物的一种估计,认为我们观察到的(样本),就是对应事件(结果),发生的最大概率的可能。还有一个奥卡姆剃刀原则

对于观察到的现象,有多种不同的解释,那么往往采取原理最简单的

机器学习三步

这段是在知乎上到的,原文 指出机器学习三个步骤:模型,目标和算法。比如 linear , logistic regression 就是不同的模型,选好模型,那么目标也就确定了。

对于linear regression, 目标是使 loss function 取值最小,对应最小二乘,算法就是梯度下降。那么对于 logistic regression 目标就是使 likelihood 尽可能最大,算法有梯度下降,牛顿法等等。

为什么同是回归,目标函数选取不一样呢?原因就在于 y 值是不同的,对于 linear,y 服从正态分布(y=𝜃T𝑥+e, 其中e是error 误差,由于IID, 符合正态分布, 所以预测值y也服从),使用最小二乘让 loss function 最小,就会拟合出最佳参数𝜃。但是 logistic regression, y 值是离散的,非0即1,或是只有几个,服从伯努利分布,无法计算 loss function,那么就返过来让这个概率最大,就是最大似然估计。

公式推导

现在开始了万恶的公式推导,由于分类只是在线性函数上多了一层映射(sigmoid 函数),最终函数模型为

h(𝜃) = g(𝜃T𝑥) = g(z)=1/(1+e^(-𝜃T𝑥))

h(𝜃) 函数

其中 𝜃T 是 𝜃转置,𝜃T𝑥 是1xn行向量与nx1列向量相乘,结果为矢量。根据上一节,我们的目标是使概率最大,那么有如下公式:

p(y=1|x;𝜃) = h(𝜃)
p(y=0|x;𝜃) = 1 - h(𝜃)

!!! 这块有些绕,为什么拟合的表达式可以做为概率呢? h(𝜃) 和 linear 的 h(𝜃) 不是一个,在 logistic regression中,这是经过 sigmoid 函数映射的,取值在[0,1] 之间,所以可以做为概率表达式。此时有如下通用推导

p(y=|x;𝜃) = h(𝜃)^(y) * (1-h(𝜃))^(1-y)

很好理解给定样本 x, 和参数 𝜃,那么 y (标记)发生的概率,如果y=0, 公式等价于 1 - h(𝜃),如果 y=1, 等价于h(𝜃),由此变成了概率问题。那么给定 m 个样本,假定L(𝜃) 为极大似然估计值

这块初次接触会比较绕,我们将测试样本 sample 标记成两个值,正例(y=1)和反例(y=0),但特征可能有几百个,面临的问题就是如何将离散的值与连续值h(𝜃)做对应,logistic regression 就是将线性函数经过 g(z) Sigmoid 映射到[0,1],再转换成概率问题,也即求极大似然。( 好像还是没解释透彻...sad...

move on, 然后对 L(𝜃) 取对数,设为l(𝜃)

由于我们是使 l 最大,所以使用梯度上升算法,有如下公式

𝜃 = 𝜃 + αl(𝜃)'

这里有点头晕,什么样的目标函数可以有极值呢?对于连续可微的目标函数,求导,那么导数为0的就是极值,前提一定是凸函数。

这里用到了公式 g(z)' = g(z)(1 - g(z))

经过求导最终梯度函数如上,和 linear regression 时的很相似啊(涉及到 General Linear Models),只不过这里 y, h(x) 什么的是0或1,而不是其它值。其实这里是梯度上升,但可以把 l(𝜃) 乘以 -1, 那么此时变成了上节的梯度下降算法。

Spark工程

测试数据为 SVM格式:标签 特征ID: 特征值 特征ID:特征值 ......

1 319:237 320:254 321:254 322:109...
0 155:53 156:255 157:253 158:253...
0 128:73 129:253 130:227 131:73 132:21...
1 152:1 153:168 154:242 155:28 180:10...
打开 sparl-shell 准备数据

实际生产环境测试集都很大,一般数据来源于 HDFS

scala> import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.mllib.util.MLUtils

scala> val data = MLUtils.loadLibSVMFile(sc, "/Users/dzr/code/spark-mllib-data/sample_libsvm_data.txt")
data: org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint] = MapPartitionsRDD[10742] at map at MLUtils.scala:108

scala> data.count // 查看原始数据数量
res250: Long = 100

scala> data.take(1) // 查看一个样本
res251: Array[org.apache.spark.mllib.regression.LabeledPoint] = Array((0.0,(692,[127,128,129,130,131,154,155,156,157,158,159,181,182,183,184,185,186,187,188,189,207,208,209,210,211,212,213,214,215,216,217,235,236,237,238,239,240,241,242,243,244,245,262,263,264,265,266,267,268,269,270,271,272,273,289,290,291,292,293,294,295,296,297,300,301,302,316,317,318,319,320,321,328,329,330,343,344,345,346,347,348,349,356,357,358,371,372,373,374,384,385,386,399,400,401,412,413,414,426,427,428,429,440,441,442,454,455,456,457,466,467,468,469,470,482,483,484,493,494,495,496,497,510,511,512,520,521,522,523,538,539,540,547,548,549,550,566,567,568,569,570,571,572,573,574,575,576,577,578,594,595,596,597,598,599,600,601,602,603,604,622,623,624,625,626,627,628,629,630,651,652,653,654,655,656,657],[51.0,159.0...
将样本划分为训练集和测试集

划分有很多方法(书上讲的很复杂),这里直接随机划分

scala> val splits = data.randomSplit(Array(0.6,0.4), seed = 11L)
splits: Array[org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint]] = Array(MapPartitionsRDD[10743] at randomSplit at <console>:40, MapPartitionsRDD[10744] at randomSplit at <console>:40)

scala> val training = splits(0).cache
training: org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint] = MapPartitionsRDD[10743] at randomSplit at <console>:40

scala> val test = splits(1)
test: org.apache.spark.rdd.RDD[org.apache.spark.mllib.regression.LabeledPoint] = MapPartitionsRDD[10744] at randomSplit at <console>:40

scala> training.count // 训练集数量
res252: Long = 57

scala> test.count // 测试集数量
res253: Long = 43
建立模型
scala> import org.apache.spark.mllib.evaluation._
import org.apache.spark.mllib.evaluation._

scala> import org.apache.spark.mllib.classification._
import org.apache.spark.mllib.classification._

scala> import org.apache.spark.mllib.regression._
import org.apache.spark.mllib.regression._

scala> val model = new LogisticRegressionWithLBFGS()
model: org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS = org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS@19d14c13

scala> val model = new LogisticRegressionWithLBFGS().setNumClasses(10).run(training)
model: org.apache.spark.mllib.classification.LogisticRegressionModel = org.apache.spark.mllib.classification.LogisticRegressionModel: intercept = 0.0, numFeatures = 6228, numClasses = 10, threshold = 0.5

通过模型得到, intercept = 0.0, numFeatures = 6228, numClasses = 10, threshold = 0.5

测试数据
scala> val predictionAndLabel = test.map {
     | case LabeledPoint(label,feature) =>
     |     val prediction = model.predict(feature)
     |     (prediction, label)}
predictionAndLabel: org.apache.spark.rdd.RDD[(Double, Double)] = MapPartitionsRDD[10774] at map at <console>:66

scala> val testPredict = predictionAndLabel.take(20)
scala> for (i<- 0 to 20) {
     | println(testPredict(i)._1 + "\t" + testPredict(i)._2)}
//预测值  真实值
1.0     1.0
1.0     1.0
0.0     0.0
1.0     1.0
0.0     0.0
0.0     0.0
1.0     1.0
1.0     1.0
1.0     1.0
0.0     0.0
1.0     1.0
1.0     1.0
0.0     0.0
1.0     1.0
0.0     0.0
0.0     0.0
1.0     1.0
1.0     1.0
0.0     0.0
0.0     0.0
误差计算

通过测量误差,发现精度为1,也就是模型对测试集完全有效

scala> val metrics = new MulticlassMetrics(predictionAndLabel)
metrics: org.apache.spark.mllib.evaluation.MulticlassMetrics = org.apache.spark.mllib.evaluation.MulticlassMetrics@18a62d7a

scala> val precision = metrics.precision
precision: Double = 1.0

Spark 2.0 这周五终于GA,普天同庆,scala 棒极了,路转粉(:

小结

这里只是二元分类(binary classification), 原理还得再反复咀嚼消化,工程实践最好。回归问题相当于ML里的 hello world。 争取下一篇笔记弄懂 GLMs,顺便分析下 Spark GML 实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容