1.logistics回归
优点:计算代价不高,易于理解和实现。
缺点:容易欠拟合,分类精度可能不高。
适用类型数据:数值型和标称型数据。
2.logistics回归的一般步骤:
(1)收集数据:采用任意方法收集数据
(2)准备数据:由于需要计算,因此要求数据类型位数值型。另外,结构化数据格式则最佳。
(3)分析数据:任意方法分析。
(4)训练算法:大部分时间用于训练,训练目的是为了找到最佳的分类系数
(5)测试算法:一旦训练步骤完成,分类将会很快
(6)使用算法:首先,我们社需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定他们属于哪个类别;在这之后,我们就可以在输出类别上做一些其他分析工作。
3.基于logistics回归和sigmoid函数的分类
我们先说一个概念,事件的几率(odds),是指该事件发生的概率与该事件不发生的概率的比值。如果事件发生的概率是p,那么该事件的几率是p/(1-p)。取该事件发生几率的对数,定义为该事件的对数几率(log odds)或logit函数:
事件发生的概率p的取值范围为[0,1],对于这样的输入,计算出来的几率只能是非负的 。而通过取对数,便可以将输出转换到整个实数范围内,下面是log函数的在二维坐标系中的图像,依照图像就会对标黄的那句话有一个形象的了解了。那我们将输出转换到整个实数范围内的目的是什么呢?因为这样,我们就可以将对数几率记为输入特征值的线性表达式 :
其中,p(y =1|x)是条件概率分布,表示当输入为x时,实例被分为1类的概率,依据此概率我们能得到事件发生的对数几率。但是,我们的初衷是做分类器,简单点说就是通过输入特征来判定该实例属于哪一类别或者属于某一类别的概率。所以我们取logit函数的反函数,令w T x w^{T}xw
T x的线性组合为输入,p为输出,经如下推导
公式1就是logistic函数。大家应该对Φ(x)很熟悉,是一个sigmoid函数,类似于阶跃函数的S型生长曲线。
两种坐标尺度下的sigmoid函数图
4.基于最优化方法的的最佳回归系数的确定:
如何才能获得最佳的回归系数呢?这里就要用到最优化方法。在很多分类器中,都会将预测值与实际值的误差的平方和作为损失函数(代价函数),通过梯度下降算法求得函数的最小值来确定最佳系数。前面我们提到过某件事情发生的概率为p,在逻辑斯蒂回归中所定义的损失函数就是定义一个似然函数做概率的连乘,数值越大越好,也就是某个样本属于其真实标记样本的概率越大越好。如,一个样本的特征x所对应的标记为1,通过逻辑斯蒂回归模型之后,会给出该样本的标记为1和为-1的概率分别是多少,我们当然希望模型给出该样本属于1的概率越大越好。既然是求最大值,那我们用到的最优化算法就是梯度上升,其实也就是与梯度下降相反而已。
5.梯度上升法
梯度上升法基于的思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。如果梯度记为∇,则函数f(x,y)的梯度由下式表示:
一个函数例子如下:
6.下面我们来实现一下logistics回归:
1. 数据准备
-0.017612 14.053064 0
-1.395634 4.662541 1
-0.752157 6.538620 0
-1.322371 7.152853 0
0.423363 11.054677 0
0.406704 7.067335 1
0.667394 12.741452 0
-2.460150 6.866805 1
0.569411 9.548755 0
-0.026632 10.427743 0
0.850433 6.920334 1
1.347183 13.175500 0
1.176813 3.167020 1
-1.781871 9.097953 0
-0.566606 5.749003 1
0.931635 1.589505 1
-0.024205 6.151823 1
-0.036453 2.690988 1
-0.196949 0.444165 1
1.014459 5.754399 1
1.985298 3.230619 1
-1.693453 -0.557540 1
-0.576525 11.778922 0
-0.346811 -1.678730 1
-2.124484 2.672471 1
1.217916 9.597015 0
-0.733928 9.098687 0
-3.642001 -1.618087 1
0.315985 3.523953 1
1.416614 9.619232 0
-0.386323 3.989286 1
0.556921 8.294984 1
1.224863 11.587360 0
-1.347803 -2.406051 1
1.196604 4.951851 1
0.275221 9.543647 0
0.470575 9.332488 0
-1.889567 9.542662 0
-1.527893 12.150579 0
-1.185247 11.309318 0
-0.445678 3.297303 1
1.042222 6.105155 1
-0.618787 10.320986 0
1.152083 0.548467 1
0.828534 2.676045 1
-1.237728 10.549033 0
-0.683565 -2.166125 1
0.229456 5.921938 1
-0.959885 11.555336 0
0.492911 10.993324 0
0.184992 8.721488 0
-0.355715 10.325976 0
-0.397822 8.058397 0
0.824839 13.730343 0
1.507278 5.027866 1
0.099671 6.835839 1
-0.344008 10.717485 0
1.785928 7.718645 1
-0.918801 11.560217 0
-0.364009 4.747300 1
-0.841722 4.119083 1
0.490426 1.960539 1
-0.007194 9.075792 0
0.356107 12.447863 0
0.342578 12.281162 0
-0.810823 -1.466018 1
2.530777 6.476801 1
1.296683 11.607559 0
0.475487 12.040035 0
-0.783277 11.009725 0
0.074798 11.023650 0
-1.337472 0.468339 1
-0.102781 13.763651 0
-0.147324 2.874846 1
0.518389 9.887035 0
1.015399 7.571882 0
-1.658086 -0.027255 1
1.319944 2.171228 1
2.056216 5.019981 1
-0.851633 4.375691 1
-1.510047 6.061992 0
-1.076637 -3.181888 1
1.821096 10.283990 0
3.010150 8.401766 1
-1.099458 1.688274 1
-0.834872 -1.733869 1
-0.846637 3.849075 1
1.400102 12.628781 0
1.752842 5.468166 1
0.078557 0.059736 1
0.089392 -0.715300 1
1.825662 12.693808 0
0.197445 9.744638 0
0.126117 0.922311 1
-0.679797 1.220530 1
0.677983 2.556666 1
0.761349 10.693862 0
-2.168791 0.143632 1
1.388610 9.341997 0
0.317029 14.739025 0
# -*- coding: UTF-8 -*-
import re
import numpy as np
import time
import random
import matplotlib.pyplot as plt
"""
从 testSet.txt 中加载数据
"""
def loadDataSet():
dataMat = [] #创建数据列表
labelMat = [] #创建标签列表
fr = open('D:\\dataset\\testSet.txt') #打开文件
for line in fr.readlines(): #逐行读取
lineArr = line.strip().split() #去回车,放入列表
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #添加数据(x,y)
labelMat.append(int(lineArr[2])) #添加标签(分类结果)
fr.close() #关闭文件
return dataMat, labelMat #返回
"""
绘制数据点图
"""
def plotDataSet():
dataMat, labelMat = loadDataSet() #加载数据集
dataArr = np.array(dataMat) #转换成numpy的array数组
n = np.shape(dataMat)[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]) #1为正样本
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) #0为负样本
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = 's',alpha=.5,label='1') #绘制1样本
ax.scatter(xcord2, ycord2, s = 20, c = 'green',alpha=.5,label='0') #绘制0样本
plt.legend()
plt.title('DataSet') #绘制title
plt.xlabel('x1'); plt.ylabel('x2') #绘制label
plt.show() #显示
if __name__ == '__main__':
plotDataSet()
2.训练Logistic回归算法:
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn) #变量转换成numpy的mat
labelMat = np.mat(classLabels).transpose() #标签转换成numpy的mat,并进行转置
m, n = np.shape(dataMatrix) #返回dataMatrix的大小。m为行数,n为列数。
alpha = 0.001 #移动步长,也就是学习速率,控制更新的幅度。
maxCycles = 500 #最大迭代次数
weights = np.ones((n,1)) #weights就是要求的特征系数w,全部初始化为1
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) #梯度上升矢量化公式
dY = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * dY #对w执行梯度更新
return weights.getA() #将矩阵转换为数组,返回权重数组
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
print(gradAscent(dataMat, labelMat))
3.根据得出的特征值绘制一下预测函数的图像:
def plotBestFit(weights):
dataMat, labelMat = loadDataSet() #加载数据集
dataArr = np.array(dataMat) #转换成numpy的array数组
n = np.shape(dataMat)[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]) #1为正样本
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) #0为负样本
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = 's',alpha=.5)#绘制正样本
ax.scatter(xcord2, ycord2, s = 20, c = 'green',alpha=.5) #绘制负样本
x1 = np.arange(-3.0, 3.0, 0.1)
x2 = (-weights[0] - weights[1] * x1) / weights[2] #w0+w1x1+w2x2=0 => x2=(-w0-w1x1)/w2
ax.plot(x1, x2)
plt.title('BestFit') #绘制title
plt.xlabel('x1'); plt.ylabel('x2') #绘制label
plt.show()
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
plotBestFit(weights)
4.随机梯度算法
随机梯度上升法的思想是,每次只使用一个数据样本点来更新回归系数。这样就大大减小计算开销。
"""
改进后的随机梯度下降法
"""
def stocGradAscentBetter(dataMatrix, classLabels, numIter=150):
m,n = np.shape(dataMatrix) #返回dataMatrix的大小。m为行数,n为列数。
weights = np.ones(n) #参数初始化
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01 #降低alpha的大小,每次减小1/(j+i)。
randIndex = int(random.uniform(0,len(dataIndex))) #随机选取样本
h = sigmoid(sum(dataMatrix[randIndex]*weights)) #选择随机选取的一个样本,计算h
error = classLabels[randIndex] - h #计算误差
weights = weights + alpha * error * np.array(dataMatrix[randIndex]) #更新回归系数,注意这里要转换为numpy.array才能正确运行
del(dataIndex[randIndex]) #删除已经使用的样本
return weights
def plotBestFit(weights1,weights2):
dataMat, labelMat = loadDataSet() #加载数据集
dataArr = np.array(dataMat) #转换成numpy的array数组
n = np.shape(dataMat)[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]) #1为正样本
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) #0为负样本
fig = plt.figure()
plt.title('BestFit')
plt.xlabel('x1'); plt.ylabel('x2') #绘制label
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = 's',alpha=.5)#绘制正样本
ax.scatter(xcord2, ycord2, s = 20, c = 'green',alpha=.5) #绘制负样本
x1 = np.arange(-3.0, 3.0, 0.1)
x2 = (-weights1[0] - weights1[1] * x1) / weights1[2]
ax.plot(x1, x2,label='GradAscent')
x22 = (-weights2[0] - weights2[1] * x1) / weights2[2]
ax.plot(x1, x22,label='StocGradAscent')
plt.legend()
plt.show()
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
start1 = time.time()
weights = gradAscent(dataMat, labelMat)
start2 = time.time()
weightsBetter = stocGradAscentBetter(dataMat, labelMat)
plotBestFit(weights,weightsBetter)
7.回归系数与迭代次数的关系
def gradAscent(dataMatIn, classLabels):
weights_array = np.array([])
dataMatrix = np.mat(dataMatIn) #变量转换成numpy的mat
labelMat = np.mat(classLabels).transpose() #标签转换成numpy的mat,并进行转置
m, n = np.shape(dataMatrix) #返回dataMatrix的大小。m为行数,n为列数。
alpha = 0.001 #移动步长,也就是学习速率,控制更新的幅度。
maxCycles = 500 #最大迭代次数
weights = np.ones((n,1)) #weights就是要求的特征系数w,全部初始化为1
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) #梯度上升矢量化公式
dY = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * dY #对w执行梯度更新
weights_array = np.append(weights_array,weights)
weights_array = weights_array.reshape(maxCycles,n)
return weights.getA(),weights_array #将矩阵转换为数组,返回权重数组
def stocGradAscentBetter(dataMatrix, classLabels, numIter=5):
m,n = np.shape(dataMatrix) #返回dataMatrix的大小。m为行数,n为列数。
weights = np.ones(n) #参数初始化
weights_array = np.array([])
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01 #降低alpha的大小,每次减小1/(j+i)。
randIndex = int(random.uniform(0,len(dataIndex))) #随机选取样本
h = sigmoid(sum(dataMatrix[randIndex]*weights)) #选择随机选取的一个样本,计算h
error = classLabels[randIndex] - h #计算误差
weights = weights + alpha * error * np.array(dataMatrix[randIndex]) #更新回归系数,注意这里要转换为numpy.array才能正确运行
weights_array = np.append(weights_array,weights,axis=0)
del(dataIndex[randIndex]) #删除已经使用的样本
return weights,weights_array
"""
绘制回归参数和迭代次数的关系
"""
def plotWeights(weights_array0,weights_array1):
#将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8)
#当nrow=3,nclos=2时,代表fig画布被分为六个区域,axs[0][0]表示第一行第一列
fig, axs = plt.subplots(nrows=3, ncols=2,sharex=False, sharey=False, figsize=(20,10))
x0 = np.arange(0, len(weights_array0), 1)
#绘制w0与迭代次数的关系
axs[0][1].plot(x0,weights_array0[:,0])
axs0_title_text = axs[0][0].set_title('GradAscent:weight and times')
axs0_ylabel_text = axs[0][0].set_ylabel('w0')
plt.setp(axs0_title_text, size=20, color='black')
plt.setp(axs0_ylabel_text, size=20, color='black')
#绘制w1与迭代次数的关系
axs[1][1].plot(x0,weights_array0[:,1])
axs0_ylabel_text = axs[1][0].set_ylabel('w1')
plt.setp(axs0_ylabel_text, size=20, color='black')
#绘制w2与迭代次数的关系
axs[2][1].plot(x0,weights_array0[:,2])
axs0_xlabel_text = axs[2][0].set_xlabel('times')
axs0_ylabel_text = axs[2][0].set_ylabel('w2')
plt.setp(axs0_xlabel_text, size=20, color='black')
plt.setp(axs0_ylabel_text, size=20, color='black')
x1 = np.arange(0, len(weights_array1)/3, 1) #由于weights_array1是一个一行n列的数组,保存列所有的参数值,这里要处以参数的个数3
#绘制w0与迭代次数的关系
axs[0][0].plot(x1,weights_array1[0::3]) #[0::3]表示从位置0开始每隔3个取一位
axs1_title_text = axs[0][1].set_title('BetterStocGradAscent:weight and times')
axs1_ylabel_text = axs[0][1].set_ylabel('w0')
plt.setp(axs1_title_text, size=20, color='black')
plt.setp(axs1_ylabel_text, size=20, color='black')
#绘制w1与迭代次数的关系
axs[1][0].plot(x1,weights_array1[1::3]) #[1::3]表示从位置1开始每隔3个取一位
axs1_ylabel_text = axs[1][1].set_ylabel('w1')
plt.setp(axs1_ylabel_text, size=20, color='black')
#绘制w2与迭代次数的关系
axs[2][0].plot(x1,weights_array1[2::3]) #[2::3]表示从未知2开始每隔3个取一位
axs1_xlabel_text = axs[2][1].set_xlabel('times')
axs1_ylabel_text = axs[2][1].set_ylabel('w2')
plt.setp(axs1_xlabel_text, size=20, color='black')
plt.setp(axs1_ylabel_text, size=20, color='black')
plt.show()
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
start1=time.time()
weights0,weights_array0 = gradAscent(dataMat, labelMat)
print(time.time()-start1)
start2=time.time()
weights1,weights_array1 = stocGradAscentBetter(np.array(dataMat), labelMat)
print(time.time()-start2)
plotWeights(weights_array0, weights_array1)
plotBestFit(weights0,weights1)
结果: