目录
1. 基本概念
2. 数学原理
3. 代码实现
3.1 代码分析
3.2 数据集分类实现
4. 总结
Logistic回归也是一种分类方法,用于两分类问题。其基本思想为:(1)寻找合适的假设函数,即分类函数,用以预测输入数据的判断结果;(2)构造代价函数,即损失函数,用以表示预测的输出结果与训练数据的实际类别之间的偏差;(3)最小化代价函数,从而获取最优的模型参数。
Logistics回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法来完成。在最优化算法中,最常用的就是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升算法。
随机梯度上升算法与梯度上升算法的效果相当,但占用更少的计算资源。此外,随机梯度上升是一个在线算法,它可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集来进行批处理运算。
基本的线性回归函数的形式为:
线性回归模型产生的预测值是一系列实值。为了使输出的预测结果变为分类所需的0和1,我们最理想的函数是单位跃迁函数,图像如下:
这个函数的缺点也很明显:不连续。所以我们使用逻辑斯蒂函数(logistic/sigmoid function)来代替这个函数:
他的优点则是单调可微,任意阶可导。
我们可以使用极大似然法求解0和1概率。
但是使用这个方法我们求解出的w和b仍然无法得到我们想要的最小概率。在这里我们可以使用梯度下降法/牛顿法来进一步求解,得到我们所需的最小值,然后实现分类。
根据这个方法,我们即可实现使用逻辑斯蒂回归对数据进行分类。
下面这段代码是梯度上升算法的具体实现。loadDataSet()的主要功能是打开文本文件testSet.txt并逐行读取。每行前两个值分别为X1和X2,第三个值是数据对应的类别标签。此外,为了方便计算,该函数还将X0的值设置为1.0。接下来是sigmoid()函数。梯度上升算法的实际工作是在函数gradAscent()里完成的。该函数有两个参数。第一个参数是dataMathIn,这是一个二维NumPy数组,每列分别代表了每个不同的特征,每行则代表每个训练样本。第二个参数是类别标签,它是一个1×100的2行向量。
def loadDataSet():
dataMat = []; labelMat = []
fr = open('C:/Users/Kano/Desktop/Study/vscode python/Test3/testSet.txt')
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
def sigmoid(inX):
return 1.0/(1+exp(-inX))
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m,n = shape(dataMatrix)
weights = ones(n) #initialize to all ones
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.0001 #apha decreases with iteration, does not
randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant
h = sigmoid(sum(dataMatrix[randIndex]*weights))
error = classLabels[randIndex] - h
weights = weights + alpha * error * dataMatrix[randIndex]
del(dataIndex[randIndex])
return weights
下面这一段第一个函数为classifyVector(),它以回归系数和特征向量作为输入来计算对应的Sigmoid值。如果Sigmoid值大于0.5函数返回1,否则返回0。接下来是函数colicTest()。用于打开测试集和训练集,并对数据进行格式化处理的函数。嘎斯函数首先导入训练集,数据的最后一列为类别标签。数据导入之后,使用函数stocGradAscent1()来计算回归系数向量。最后一个函数为multiTest(),其功能是调用函数colicTest()10次并求结果的平均值。
def classifyVector(inX, weights):
prob = sigmoid(sum(inX*weights))
if prob > 0.5: return 1.0
else: return 0.0
def colicTest():
frTrain = open('C:/Users/Kano/Desktop/Study/vscode python/Test3/divorceTraining.txt'); frTest = open('C:/Users/Kano/Desktop/Study/vscode python/Test3/divorceTest.txt')
trainingSet = []; trainingLabels = []
for line in frTrain.readlines():
currLine = line.strip().split(';')
lineArr =[]
for i in range(54):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[54]))
trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 1000)
errorCount = 0; numTestVec = 0.0
for line in frTest.readlines():
numTestVec += 1.0
currLine = line.strip().split(';')
lineArr =[]
for i in range(54):
lineArr.append(float(currLine[i]))
if int(classifyVector(array(lineArr), trainWeights))!= int(currLine[54]):
errorCount += 1
errorRate = (float(errorCount)/numTestVec)
print ("the error rate of this test is: %f" % errorRate)
return errorRate
def multiTest():
numTests = 10; errorSum=0.0
for k in range(numTests):
errorSum += colicTest()
print ("after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests)))
我们使用了一个很有趣的数据集,离婚数据。他有54个属性,分别是对于伴侣的行为情况进行打分,最后根据这些分类结果给出是否离婚的情况。下面我们大概看一下这些属性信息。
1. 如果我们中的一个人在讨论恶化时道歉,讨论就结束了。
2. 我知道我们可以忽略我们的分歧, 即使事情有时变得艰难。
3. 当我们需要它时,我们可以从一开始就与我的配偶进行讨论并纠正它。
4. 当我与我的配偶讨论时,联系他最终会起作用。
5. 我和妻子在一起的时间对我们来说很特别。
...
下面是部分数据集的截图:
之后我们在命令提示符下测试这个分类情况,得到了还不错的效果:
通过这次实验了解了如何使用逻辑斯蒂回归对数据集进行训练和分类。也知道了逻辑斯蒂回归的数学原理:它在线性回归的基础上,在特征到结果的映射中加入了一层Sigmoid函数(非线性)映射,即先把特征线性求和,然后使用Sigmoid函数来预测。而Sigmoid函数用于机器学习分类的效果很好。最后带入数据集之后效果还不错,原因应该为:(1)数据集的属性特征值为0~4的数据,比较简单且易于分类,当我使用差异较大或者小数较多的数据集时效果并没有这个好;(2)属性较多,且结果仅有离婚与不离婚两种,更适合Sigmoid函数。相信在之后的学习中会有更多方法来适应不同类的数据集,给出更好的分类效果。