利用Logistic回归进行分类的思想:根据现有数据对分类边界线建立回归公式
优点:计算代价不高,易于理解和实现
缺点:容易欠拟合,分类精度可能不高
适用数据类型:数值型和标称型数据
Sigmoid函数:
Sigmod函数的输入Z:
采用向量的写法
梯度上升法:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻
函数f(x,y)的梯度上升的迭代公式:w=w+α∇wf(w)
学习率:学习率决定了参数移动到最优值的速度快慢。如果学习率过大,很可能会越过最优值;反而如果学习率过小,优化的效率可能过低,长时间算法无法收敛
- logistic回归梯度上升优化算法
import numpy as np
#打开文件,并逐行读取
def loadDataSet():
dataMat = []
labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #列表中的每个元素都是特征的列表,1.0?
labelMat.append(int(lineArr[2])) #类别特征值
return dataMat, labelMat
#定义sigmoid分类函数
def sigmoid(inX):
return 1.0/(1+np.exp(-inX))
#梯度上升:设置迭代次数,每次将特征矩阵乘以回归系数进行sigmoid处理,得到预测类别(0-1)。计算真实类别和预测类别的误差值,
#将特征矩阵*误差值得到误差矩阵,再乘以学习率,得到回归系数误差值,加上原来的回归系数,得到新的回归系数,再进行迭代计算
#把最终得到的回归系数放入直线中 这条直线为不同类别之间的分割线
#缺点:每次更新回归系数都需要遍历整个数据集,计算复杂度太高
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn) #将列表转换为矩阵 100*3
labelMat = np.mat(classLabels).transpose() #将矩阵倒置,方便与回归系数运算
m,n = np.shape(dataMatrix) #获取矩阵的行和列数
alpha = 0.001 #学习率
maxCycles = 500 #迭代次数
weights = np.ones((n, 1)) #初始化回归系数。权重参数的个数为数据集矩阵的列数,即特征的个数
for k in range(maxCycles):
# 将特征乘以回归系数,再使用sigmoid,所得值为0到1,又因为真实类别特征为0和1,所以相减可以看作是两者之间的误差
h = sigmoid(dataMatrix * weights)
error = labelMat - h #用类别特征减去 sigmoid后的特征值 即计算真实类别和预测类别的差值,即误差
weights = weights + alpha * dataMatrix.transpose() * error #新的回归系数+=特征矩阵*误差矩阵*学习率
return weights
测试dataArr, labelMat = loadDataSet()
gradAscent(dataArr, labelMat)
结果
matrix([[ 4.12414349],
[ 0.48007329],
[-0.6168482 ]])
- 画出数据集和Logistic回归最佳拟合直线的函数
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat, labelMat = loadDataSet()
#print(type(dataMat)) #<class 'list'>
dataArr = np.array(dataMat) #<class 'numpy.ndarray'>
n = np.shape(dataArr)[0] #获取数据的条数
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i]) == 1: #如果数据为类别1,将数据的特征赋给xcord1,ycord1
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2])
else: #如果数据为类别0,将数据的特征赋给xcord2,ycord2
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, 1.0) #根据图表 判定x的范围
y = (-weights[0]-weights[1] * x)/weights[2] # 0 = w0x0 + w1x1 + w2x2 x0=1? w0x0的作用? sigmoid函数为0?
ax.plot(x,y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
测试
weights = gradAscent(dataArr, labelMat)
#print(weights.getA()[0]) [ 4.12414349] print(type(weights)) <class 'numpy.matrixlib.defmatrix.matrix'>
#print(weights[0]) [[ 4.12414349]] print(type(weights.getA())) <class 'numpy.ndarray'>
#print(type(weights)) <class 'numpy.ndarray'>
plotBestFit(weights.getA()) #矩阵通过这个getA()这个方法可以将自身返回成一个n维数组对象 <class 'numpy.ndarray'>
结果
- 随机梯度上升算法
#较之于上边的算法,该方法一次仅用一个样本点来更新回归系数
#变量h和误差error都为向量,而上边的算法都是数值;上边的算法没有矩阵的转换过程,所有变量的数据类型都为numpy数组
def stocGradAscent0(dataMatrix, classLabels):
m,n = np.shape(dataMatrix)
alpha = 0.01
weights = np.ones(n)
for i in range(m):
h = sigmoid(sum(dataMatrix[i] * weights)) #sum()? 上个算法使用特征矩阵减去h 而在本算法使用类别特征值减h 所以要用一个数
error = classLabels[i] - h
weights = weights + alpha*error*dataMatrix[i]
return weights
weights = stocGradAscent0(np.array(dataArr), labelMat)
plotBestFit(weights)
结果
- 改进的随机梯度上升算法
#两处优化:1 alpha会随着迭代进行调整,2随即获取样本更新回归系数,一方面可以减少周期性波动,二是可以使函数收敛更快
def stocGradAscent1(dataMarix, classLabels, numIter=150):
m,n = np.shape(dataMarix)
weights = np.ones(n)
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
#alpha随着迭代次数不断减小,但不会变为0, 当j << max(i)时,alpha就不是严格下降
alpha = 4/(1.0 + j + i) + 0.01
#随即获取样本来更新回归系数,能够减少周期新波动,使函数收敛更快
randIndex = int(np.random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMarix[randIndex]*weights))
error = classLabels[randIndex] -h
weights = weights + alpha * error * dataMarix[randIndex]
del(dataIndex[randIndex])
return weights
weights = stocGradAscent1(np.array(dataArr), labelMat,500)
plotBestFit(weights)
结果
- Logistic回归分类函数
def classifyVector(inX, weights):
prob = sigmoid(sum(inX*weights))
if prob > 0.5 : return 1.0
else: return 0.0
def colicTest():
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet = []; trainingLabels = []
for line in frTrain.readlines():
currLine = line.strip().split("\t")
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500) #得到训练完后的回归系数
errorCount = 0; numTestVec = 0.0
for line in frTest.readlines():
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i])) #数据中除了类别特征的特征数据
#用特征数据乘以回归系数得到的分类特征和原分类特征是否相同
if int(classifyVector(np.array(lineArr), trainWeights)) != int(currLine[21]):
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)))
运行结果
the error rate of this test is: 0.417910
the error rate of this test is: 0.268657
the error rate of this test is: 0.313433
the error rate of this test is: 0.358209
the error rate of this test is: 0.298507
the error rate of this test is: 0.373134
the error rate of this test is: 0.417910
the error rate of this test is: 0.328358
the error rate of this test is: 0.492537
the error rate of this test is: 0.388060
after 10 iterations the average error rate is: 0.365672
遗留问题
1.在plotBestFit()函数中,计算Logistic回归最佳拟合直线时,根据方程0 = w0x0 + w1x1 + w2x2 x0=1
,可是数据集只有两个特征X1 和X2 那W0和X0的作用是什么?为什么要把X0设为1
2.在stocGradAscent0()函数中,计算预测值时的代码为h = sigmoid(sum(dataMatrix[i] * weights))
,计算误差的代码为error = classLabels[i] - h
误差 = 真实类别的特征值 - 该数据的特征值与回归系数的乘积之和 ,预测值 == 该数据的特征值与回归系数的乘积之和的sigmoid值 ???,如果只是为了得到一个相关的数字,那min()和max()为什么不行猜想
1.
w0x0
是为了满足y = ax + b ?w0x0
就是这里的b。如果不对,还请各位大牛指教!!
2.因为每个特征的回归系数是一样的,所以使用sum()能够保证一条数据中的每个特征值都有参与,而min()和max()则不符合。(如果不同的特征都有自己的回归系数,应该就不会是线性拟合?)最后
希望各位大佬多多指教,如果能帮我解决上面的两个问题就更好了!!!!