前言
上一篇文章我们谈了谈基于概率论的分类,这篇我们继续谈论分类问题,这篇讲述的是一种最优化问题,即通过简单计算并不能得出来最终结果,需要一步步来优化求最优值,这种分类方法应用广泛,也是我们必须要熟练掌握的分类算法,它的地位属于十大机器学习算法其中之一,可以说是里面的老大哥人物,不废话进入正题。
原理
逻辑回归的目的是根据已有的数据训练出来一个模型,用于对未知数据分类,实现思路是给每一个样本的每一个特征赋予一个权重因子,预测结果等于样本的特征值乘以每个特征的影响因子,这个影响因子也就是我们通常方程的系数,因此该问题即转为了求解最优化系数的问题了,顺着这个思路,求解方程最优化问题,一般我们常用的方法有求导让导数等于0, 可以得到极值点 ,还有一种是逐渐向目标值靠近一点一点迭代出来结果 称为梯度上升或下降方法,我觉得用这两种方法是都可以的,根据回归场景哪一个更精准了、更适合些我们就选择哪一个。
求出来最佳回归系数之后,我们最终的预测值也就出来了,那如何利用这些预测结果分类呢,这些值得范围其中并不统一,一般是0到无穷大,当时为了解决这个问题很多人尝试了各种办法,尝试了各种数学上的公式,最后找到了一个叫做sigmoid的函数,试验之后觉得出其的好,以后逻辑回归大家就都使用这个函数了。
解惑
- 逻辑回归与KNN算法关系
这两种算法感觉他们处理的样本特点不同,假如样本为一个圆形那么knn算法是不好处理的,而逻辑回归可以处理这种情况,每种算法都有自身的优点也有不适合之处,具体要根据业务数据自行选择。 - 线性回归与逻辑回归关系
想必对于很多刚刚接触回归的人来说经常搞不清楚什么叫回归、逻辑回归、线性回归等概念,开始时我也是经常弄混了,随着学习的深入对他们之间的区别,才逐渐清晰起来。
首先我们从回归说起,回归其实就是通过模型拟合样本数据这一个过程叫做回归,生活中有各种样本数据堆积在我们周围,其特点各不相同,从最简单的数据开始利用线性模型来预测样本点的值分布,样本点分布为线性规律,如下图:
上图中表示的是二维平面空间的样本分布图,可见样本都在围绕直线周围,说明样本的分布规律是一条直线 ,当然就可以利用直线来模拟,但后来又发现很多实际情况存在分类问题,线性回归对于这类问题预测经常出现错误,如下图癌细胞大小与是否是癌症的关系图:
从图上可见并不是癌细胞越大与是否得癌症成正相关的线性规律,中间的几个样本即不符合的样本,于是乎开始了寻找其它新模型的道路,这就是经典的逻辑回归模型,一般用来解决实际中的二分类问题,这种方法考虑到了每个样本的特征,算出来一个综合评分,评分出来后为了找到一个能将评分映射到最终分类结果上,找到了一个叫做sigimod的函数,发挥出来神奇的作用,解决了二分类问题。
有时我们也把逻辑回归归为广义的线性回归,除了逻辑和线性还有其它几种回归,同属于一个回归家族,他们的自变量基本都相同,不同的是因变量不同,如线性回归结果为连续值,逻辑回归结果为0或1属于二分类问题,如下:
- 如果是连续的,即线性回归
- 如果是二项分布,即逻辑回归
- 如果是Poisson分布,即Poisson回归
- 如果是负二项分布,即负二项回归
- 损失函数、代价函数、似然函数
- 为什么要有sigmod函数?
个人感觉sigmod函数并不一定非得有,但是有它能起到很好的效果,假如我们的样本很好,分出来的类别很容易判断属于哪一个类或者有其他类型的函数都可以不适用它,sigmod函数似乎是逻辑回归必备函数,把这个函数去掉其它就是标准的线性回归方程。
- 判定边界
这一概念要好理解一些,指分类的时候分好类后类别之间的边界线,由于战国时期画好地界之后你就是属于那一国家,国与国之间的边界线将几个国家分割开来,这里的分类也是这个意思,判定边界即是我们要找到分类模型的分类界限,模型的含义其实也是代表着判定边界,不管在哪个算法中,模型一直追求的完美分类或回归的边界问题。
概念
- 梯度下降(Gradient Descent, GD)
是一种优化算法,在机器学习中常用语优化模型系数,优点计算量小,适合大规模数据集 适用场景也比较多,基本思路可以这样理解,有一座凹凸不平的山,我们要找一个下降最快的路下上,也就是梯度下降最快的方向走,由于山路不平选择的走动方向可能每次走一步,下一步的方向会放生改变,如下图所示:
在真实场景中,经常用到梯度法还有几种变形后的梯度法,如批量梯度下降(BGD)、随机梯度下降(SGD),它们各有优缺点,SGD每次更新系数时只选取一个样本,这种方法可能有点极端,实际中也经常使用批量样本更新模型,这种方式介于全量和单个之间可谓是一种权衡的结果,本篇我们会涉及到随机梯度法。
实现代码
下面采用梯度下降法实现了求最佳系数过程,然后,优化使用了随机梯度下降。
def loadDataSet():
'''
获取数据集
:return:测试数据集、分类标签
'''
# 测试数据集
dataMat = []
# 分类标签
labelMat = []
# 数据文件
fr = open('testSet.txt')
# readlines 返回一个字符串列表
for line in fr.readlines():
# 去空格和特殊字符
lineArr = line.strip().split()
# 默认设置第一列为1
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
def sigmoid(intX):
'''
sigmoid阶跃函数
:param intX:输入转换向量
:return:0-1之间值
'''
return 1.0/(1+np.exp(-intX))
def gradAscent(dataMatIn,classLables):
'''
logistic回归梯度上升算法
:param dataMatIn:数据集
:param classLables:分类标签
:return: 最佳拟合参数向量
'''
# 将数据集和标签转为矩阵类型
print "dataMatIn = %s" % dataMatIn
print "classLables = %s" % classLables
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLables).transpose()
# shape返回矩阵的大小 行列数
m,n = np.shape(dataMatrix)
print "dataMatrix = %s" % dataMatrix
print "labelMat = %s" % labelMat
print "m = %s ,n = %s" % (m,n)
# 向目标移动的步长
alpha = 0.001
# 迭代次数
maxCycles = 500
# 返回一个n行 1 列矩阵
weights = np.ones((n,1))
# 对回归系数进行多少次梯度上升
for k in range(maxCycles):
# dataMatrix*weights代表了300次乘机
h = sigmoid(dataMatrix*weights)
print "h = %s " % h
error = (labelMat-h)
print "error = %s " % error
print "dataMatrix.transpose() = %s" % dataMatrix.transpose()
print np.shape(dataMatrix.transpose())
weights = weights + alpha*dataMatrix.transpose()*error
# 返回训练好的回归系数
return weights
def plotBestFit(weights):
'''
画出数据集和logistic回归最佳拟合直线的函数
:param weights:
:return:
'''
dataMat,labelMat = loadDataSet()
dataArr = np.array(dataMat)
n = np.shape(dataArr)[0]
xcord1 = [];ycord1 = []
xcord2 = [];ycord2 = []
for i in range(n):
if int(labelMat[i] == 1):
xcord1.append(dataArr[i,1]) ;ycord1.append(dataArr[i,2])
else:
xcord2.append(dataArr[i, 1]);ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1,ycord1,s=30,c='red',marker='s')
ax.scatter(xcord2,ycord2,s=30,c='green')
x = np.arange(-3.0,3.0,0.1)
y = (-weights[0]-weights[1]*x)/weights[2]
ax.plot(x,y)
plt.xlabel('X1');plt.ylabel('X2')
plt.show()
def stocGradAscent0(dataMatrix,classLabels):
'''
获取最佳拟合参数向量
:param dataMatrix:数据集
:param classLabels:类别标签
:return:最佳拟合参数向量
'''
m,n = np.shape(dataMatrix)
print "m = %s,n = %s " % (m,n)
# 步长
alpha = 0.01
# 初始化拟合参数向量
weights = np.ones(n)
print "weights %s = " % weights
# 对回归系数进行样本数量的梯度上升,每次仅仅使用一个样本
for i in range(m):
print "dataMatrix[i] = %s" % dataMatrix[i]
# h为单一样本的预测结果
h = sigmoid(sum(dataMatrix[i] * weights))
# error为单一样本的误差
error = classLabels[i] - h
# 根据单一样本结果来更新回归系数
weights = weights +alpha * error * dataMatrix[i]
return weights
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
'''
基于随机梯度上升法的logistic回归分类器
:param dataMatrix: 数据集
:param classLabels: 类别标签
:return:
'''
m,n = np.shape(dataMatrix)
alpha = 0.01
weights = np.ones(n)
# 迭代次数控制
for j in range(numIter):
# 样本选择集
dataIndex = range(m)
# 随机选取样本遍历一轮
for i in range(m):
# 每次迭代时调整alpha的值
alpha = 4/(1.0+j+i) + 0.01
# 随机选取样本来更新回归系数
randIndex = int(np.random.uniform(0,len(dataIndex)))
# 单个样本的预测值
h = sigmoid(sum(dataMatrix[randIndex] * weights))
# 单个样本的误差
error = classLabels[randIndex] - h
weights = weights +alpha * error * dataMatrix[randIndex]
# 删除已经使用过的样本
del(dataIndex[randIndex])
return weights
总结
逻辑回归是比较常用的分析数据方法,需要知道其原理是如何过滤数据求最佳系数的,有时觉得算法就是一种思路一种想法,其它没有什么难不难之分,算法都是在常规想法之上优化出来的思路,堪称某某算法,要想掌握某个算法需要知道其解决的什么问题,优势与劣势,才能在真实场景中灵活应用,达到学以致用的目的。
题外思考
2选一思维
在生活中这种想法还是比较常见的,我们做每一件事情的时候都存在二选一思维,认为非黑即白,对或者错,其实除了二选一都是两种极端的两发,生活中不存在完全正确与错误的答案或事实,需要开阔思维多角度和维度去想问题,思考问题。