何为回归?
假设现在有一些数据点,现在要用一条直线对这些点进行拟合(最佳拟合直线)。这个拟合过程就叫回归。
何为回归分析?
是一种预测性的建模技术,它研究的是因变量y和自变量x之间的关系。使用直线或曲线来拟合数据点,目的是使直线或曲线到数据点的距离差异最小,满足这个要求的直线或曲线就称为最佳拟合直线或曲线。
线性回归
实例参考:
线性回归的缺陷:


利用Logistic回归进行分类的主要思想:
根据现有数据对分类边界线建立回归公式,以此进行分类。该处的“回归”一词源于最佳拟合,表示要找到最佳拟合参数集。
训练分类器时的做法就是寻找最佳拟合参数。使用的是最优化算法。
基于Logistic和Sigmoid函数的分类
Logistic回归的一般过程
收集数据:任意方法。
准备数据:由于需要进行距离计算,故要求数据类型为数值型。此外,结构化数据格式更佳。
分析数据:任意方法。
训练算法:大部分的时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
测试算法:模型训练一旦完成,分类将会很快。
使用算法:输入数据 —> 转换为结构化数值 —> 基于训练好的回归系数对这些数值进行回归计算,判定其属于的类别 —> (在输出的类别上做其他分析…)
Sigmoid函数的数学形式:

由函数图像可见,在X = 0附近,Sigmoid函数很像一个阶跃函数。
为了实现Logistic回归分类器,可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将该总和代入Sigmoid函数,进而得到一个范围在0~1之间的数值。任何大于0.5的数据被分入1类,小于0.5的归入0类。
基于最优化方法的回归系数确定
梯度上升法
基本思想:
要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。

训练算法—使用梯度上升找到最佳参数

伪代码:
**每个回归系数初始化为1:****重复R次:****计算整个数据集的梯度****使用alpha × gradient更新回归系数的向量****返回回归系数**
数据集:testSet.txt
`def loadDataSet():
dataMat=[]# 存X=[[1,-0.017612,14.053064],[1,-1.395634,4.662541],….] 共100个元素
_labelMat=[]# 存Y=[0,1,0,0,1,1,…]共100个元素
fr=open(‘testSet.txt’) # 获取文件对象
for line in fr.readlines():# [[],[],[*]]
#复习:readline()读取文件的一行,readlines()读取整个文件,read()也是读取整个文件
# 区别:read()将整个文件读成一个字符串,readlines()将整个文件读成一个列表
lineArr=line.strip().split() #strip()去掉首尾两边的空格、换行符\n、水平制表符\t
# split()默认以空格切分
# line=-0.017612 14.053064 0\n
# lineArr=[-0.017612,14.053064,0]
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])]) # 将每条数据的特征值以float类型添加到dataMat
labelMat.append(int(lineArr[2]))#lineArr[2]=’0’,int(‘0’)=0 将每条数据的类别添加到labelMat
return dataMat,labelMat # 返回特征值列表型矩阵,类别列表
对数据进行sigmoid函数运算,得到的结果是分类的依据
_def sigmoid(z):
return 1.0/(1+np.exp(-z))
# 求sigmoid(z)函数的参数z
_def gradAscent(dataMatIn,classLabels):
dataMatrix=np.mat(dataMatIn)# 将类型为列表的输入转换成矩阵类型
labelMat=np.mat(classLabels).transpose() # 先把列表转成矩阵,再由transpose()函数将矩阵转置
m,n=np.shape(dataMatrix)#m=100是矩阵行数,n=3是矩阵列数
alpha=0.001 # 步长
maxCycles=500 # 最大迭代次数
weights=np.ones((n,1)) # W=[[w0],[w1],[w2]]
# y=W^TX=[w0,w1,w2]^T[1,x1,x2]
for k in range(maxCycles):
h=sigmoid(dataMatrix*weights)# 输入z=W^TX h=[0.3,0,8,0.4,0.9,…….]
# 100行3列3行1列=100行1列=h 矩阵A 矩阵B 的前提是A的列数 = B的行数
# 计算真实类别与预测类别的差值
error=(labelMat-h) #labelMat= [0, 1, 0, 0, 0, 1….]
weights=weights+alphadataMatrix.transpose()error # 逻辑回归的梯度上升公式 w = w + alphaΔwf(w)error
_return weights`
运行及结果:
dataMat, labelMat = loadDataSet()<br />print("dataMat=", dataMat)<br />print("labelMat = ",labelMat)<br />weights = gradAscent(dataMat, labelMat)<br />print("W=[w0,w1,w2]=\n", weights)
数据可视化—绘制决策边界
_# 绘制数据集和Logistic回归最佳拟合直线的函数<br />_def plotBestFit(dataMat,labelMat,weights):<br /> dataArr = np.array(dataMat) _# 将特征值列表矩阵化<br /> _n = np.shape(dataArr)[0] _# 获取特征值矩阵的行数,及数据的条数<br /> _xcord1 = [];ycord1 = [] _# 分别存放类别为1的数据的两个特征值<br /> _xcord2 = [];ycord2 = [] _# 分别存放类别为0的数据的两个特征值<br /> _for i in range(n): _# 遍历每一条数据<br /> _if int(labelMat[i]) == 1: _# 如果该条数据的类别是1<br /> # 将该条数据的两个特征值分别添加到存放1类数据的特征值存放列表<br /> _xcord1.append(dataArr[i,1])<br /> ycord1.append(dataArr[i,2])<br /> else:<br /> xcord2.append(dataArr[i,1])<br /> ycord2.append(dataArr[i,2])<br /> fig = plt.figure()<br /> ax = fig.add_subplot(111) _# 把绘图区划分为1行1列,当前图形绘制在第1个空间<br /> # 绘制散点图<br /> _ax.scatter(xcord1,ycord1,s=30,c='red',marker='s')<br /> ax.scatter(xcord2,ycord2,s=30,c='blue')<br /> _# 绘制分界线<br /> _x = np.arange(-3.0,3.0,0.1)<br /> y = (-weights[0] - weights[1] * x) / weights[2] _# 分界线的方程<br /> _ax.plot(x,y)<br /> plt.xlabel('x1')<br /> plt.ylabel('x2')<br /> plt.show()
运行及结果:
dataMat, labelMat = loadDataSet()<br />print("dataMat=", dataMat)<br />print("labelMat = ",labelMat)<br />_# weights = gradAscent(dataMat, labelMat)<br />_weights = [[4.12414349], #该数据就是gradAscent()函数得到的最佳参数,为了节省时间,将参数直接导入<br /> [0.48007329],<br /> [-0.6168482]]<br />_# print("W=[w0,w1,w2]=\n", weights)<br />_plotBestFit(dataMat,labelMat,np.array(weights))
由图可见,训练得到的分类器的分类结果很不错,从图上看,只有4个点的类别被分错。但是,即使例子简单,数据集也小,但却仍需要大量的计算(300次乘法)。
训练算法—随机梯度上升
梯度上升算法在每次更新回归系数时都要遍历整个数据集,该方法在处理小型数据集时尚可,当面对成千上万的海量数据集和特征值,那么该方法的计算复杂度将会大大提升。<br />改进的方法是一次仅仅用一个样本点来更新回归系数,该方法称为**随机梯度上升算法。**<br />因为可以在新样本到来时对分类器进行增量式的更新,因此随机梯度上升算法是一个 在线学习算法。与之相对应,一次处理所有数据被称作“批处理”。
伪代码:
**所有回归系数初始化为1****对数据集中每个样本:****计算该样本的梯度****使用alpha * gradient更新回归系数值****返回回归系数值**_# 随机梯度上升算法<br />_def stocGradAscent0(dataMatrix,classLabels):<br /> m,n = np.shape(dataMatrix) _# 获取数据的条数和特征值的个数<br /> _alpha = 0.01 _# 设置步长<br /> _weights = np.ones(n) _# 初始化最优参数<br /> _dataMatrix = np.array(dataMatrix)<br /> for i in range(m):<br /> h = sigmoid(sum(dataMatrix[i] * weights)) _# 此时h是一个数值,预测类别<br /> _error = classLabels[i] - h _# 计算实际类别与预测类别的误差<br /> _weights = weights + alpha * error * dataMatrix[i] _# 对参数weights进行更新<br /> _return weights
运行及结果:
得到的最优参数

从结果来看,拟合出来的直线效果还不错,但并不如梯度上升算法那样完美。此时的分类器分类错误了大概1/3的样本。
但需要注意的是,梯度上升算法得到的结果是在整个数据集上迭代了500次才得到的。
接下来对随机梯度算法进行一些修改,使其在整个数据集上迭代200次:_# 优化后的随机梯度上升算法<br />_def stocGradAscent1(dataMatrix,classLabels,numIter=150):<br /> m,n = np.shape(dataMatrix)<br /> weights = np.ones(n)<br /> for j in range(numIter): _# 迭代150次<br /> _dataIndex = range(m) _# [0,1,...,99]<br /> _for i in range(m):<br /> alpha = 4 / (1.0+j+i) + 0.01 _# alpha每次迭代都需要调整<br /> _randIndex = int(random.uniform(0,len(dataIndex))) _# 随机生成一个0~m之间的整数赋值给randIndex<br /> _h = sigmoid(sum(dataMatrix[randIndex] * weights)) _# 随机使用一条数据的特征值生成预测值<br /> _error = classLabels[randIndex] - h _# 计算误差<br /> _weights = weights + alpha * error * dataMatrix[randIndex]<br /> np.delete(dataMatrix,randIndex,0)<br /> return weights
运行及结果:
weights = stocGradAscent1(np.array(dataMat),labelMat,200)<br />print(weights)<br />plotBestFit(dataMat,labelMat,weights)
得到的最优参数:

由图可见,采用随机梯度上升算法,在本次的训练过程中,仅迭代了200次就达到了准确度接近采用梯度上升算法迭代500次的分类结果。




