简介
机器学习的回归(Regression)和分类(Classification)是两种常见的预测任务,它们在目标和方法上有一些关键区别:
1、目标的性质:
回归:旨在预测一个连续的数值。例如,预测房价、温度或者产品的销售量。
分类:旨在将实例分配到预定义的类别中。例如,判断邮件是否为垃圾邮件、识别图片中的物体1类别(如猫、狗等)。
2、输出类型:
回归:输出是一个连续的数值,可以是任何实数。
分类:输出是类别标签,通常为离散的集合(如“是”或“否”,“红色”、“蓝色”、“绿色”等)。
3、评估指标:
回归:通常使用均方误差(Mean Squared Error,MSE)、均方根误差(Root Mean Squared Error,RMSE)或平均绝对误差(Mean Absolute Eroor,MAE)等指标来评估模型性能。
分类:常用的评估指标包括准确率(Accuracy)、精确度(Precision)、召回率(R
一、Logistic回归
1.1什么是Logistic回归
Logistic回归是一种最优化算法。用一条直线对一些数据点进行拟合的过程被称为回归。利用Logistic回归进行分类的主要思想是:根据现有数据利用Logistic回归生成最佳拟合线,并以此作为数据的分类边界线。逻辑回归假设数据服从伯努利分布,通过极大似然估计的方法,运用梯度下降来求解参数,来达到数据二分类的目的。
1.2线性回归函数
线性回归函数的模型
1.3逻辑函数(Sigmoid函数)
若要处理的是二分类问题,我们期望的函数输出会是0或1,类似于单位阶跃函数,可是该函数是不连续的,不连续不可微。
因此我们换一个函数——Sigmoid函数,当x=0时,y为0.5;随着x的增大,y值趋近于1,随着x的减小,y趋近于0,当横坐标足够大时,Sigmoid函数就会看起来像一个阶跃函数。Sigmoid函数的值域在(0,1)之间,而概率P也是在0到1之间,因此我们可以把Sigmoid的值域和概率联系起来
1.4Logistic回归函数
1.5Logistic回归的优缺点
优点
(1)对率函数任意阶可导,具有很好的数学性质,许多现有的数值优化算法都可以用来求最优解,训练速度快;
(2)简单易理解,模型的可解释性非常好,从特征的权重可以看到不同的特征对最后结果的影响;
(3)适合二分类问题,不需要缩放输入特征;
(4)内存资源占用小,因为只需要存储各个维度的特征值;
(5)直接对分类可能性进行建模,无需事先假设数据分布,避免了假设分布不准确所带来的问题;
(6)以概率的形式输出,而非知识0.1判定,对许多利用概率辅助决策的任务很有用。
缺点
(1)不能用逻辑回归去解决非线性问题,因为Logistic的决策面试线性的;
(2)对多重共线性数据较为敏感;
(3)很难处理数据不平衡的问题;
(4)准确率并不是很高,因为形式非常的简单(非常类似线性模型),很难去拟合数据的真实分布;
(5)逻辑回归本身无法筛选特征,有时会用gbdt来筛选特征,然后再上逻辑回归。
1.6Logistic回归的一般过程
1.收集数据:采用任意方法收集
2.准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳
3.分析数据:采用任意方法对数据进行分析
4.训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数
5.测试算法:一旦训练步骤完成,分类将会很快。
6.使用算法:首 先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。
三、Logistic回归的实现
# -*- coding:UTF-8 -*-
import matplotlib.pyplot as plt
import numpy as np
"""
函数说明:加载数据
Returns:
dataMat - 数据列表
labelMat - 标签列表
"""
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])]) #添加数据
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 = 'black', marker = 's',alpha=.5)#绘制正样本
ax.scatter(xcord2, ycord2, s = 20, c = 'red',alpha=.5) #绘制负样本
plt.title('DataSet') #绘制title
plt.xlabel('x1'); plt.ylabel('x2') #绘制label
plt.show() #显示
if __name__ == '__main__':
plotDataSet()
训练算法
"""
函数说明:sigmoid函数
Parameters:
inX - 数据
Returns:
sigmoid函数
"""
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
"""
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLabels - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
"""
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))
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) #梯度上升矢量化公式
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error
return weights.getA() #将矩阵转换为数组,返回权重数组
分析数据
# 绘制数据集和Logistic回归最佳拟合直线
def plotBestFit(weights):
dataMat, labelMat = loadDataSet() # 加载数据集,标签
dataArr = np.array(dataMat) # 转换成umPy的数组
n = np.shape(dataArr)[0] # 获取数据总数
xcord1 = []; ycord1 = [] # 存放正样本
xcord2 = []; ycord2 = [] # 存放负样本
for i in range(n): # 依据数据集的标签来对数据进行分类
if int(labelMat[i]) == 1: # 数据的标签为1,表示为正样本
xcord1.append(dataArr[i, 1]); ycord1.append(dataArr[i, 2])
else: # 否则,若数据的标签不为1,表示为负样本
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) # x区间
y = (-weights[0] - weights[1] * x) / weights[2] # 最佳拟合直线
ax.plot(x, y)
plt.title('BestFit') # 标题
plt.xlabel('X1'); plt.ylabel('X2') # x,y轴的标签
plt.show()
if __name__ == '__main__':
weights = gradAscent(dataMat, labelMat)
plotBestFit(weights)
训练算法:随机梯度上升
# 随机梯度上升算法
def stocGradAscent0(dataMatrix, classLabels): # dataMatIn数据集、classLabels数据标签
m, n = np.shape(dataMatrix) # 获取数据集矩阵的大小,m为行数,n为列数
alpha = 0.01 # 目标移动的步长
weights = np.ones(n) # 所以初始化为1
for i in range(m): # 重复矩阵运算
h = sigmoid(sum(dataMatrix[i] * weights)) # 矩阵相乘,计算sigmoid函数
error = classLabels[i] - h # 计算误差
weights = weights + alpha * error * dataMatrix[i] # 矩阵相乘,更新权重
return weights
# 运行测试代码
dataMat, labelMat = loadDataSet()
weigths = stocGradAscent0(np.array(dataMat), labelMat)
plotBestFit(weigths)
print("w0: %f, w1: %f, W2: %f" % (weigths[0], weigths[1], weigths[2]))
实例:从疝气病症预测病马的死亡率
测试算法:
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, 1000) #得到最佳回归系数
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() #调用colicTest()函数十次并求结果的平均值
print ("after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests)))
if __name__=='__main__':
multiTest()
结果:
五、问题及总结
问题:为什么与Sigmoid相似的Sign之类的函数不行?
Sign函数,他长得跟Sigmoid函数很类似,也能够将 X 限制在 0 到 1 的范围内。 我们知道,Logistic回归只是在线性回归上增加了一个 g(x) 的限制,而在模型训练的过程中实际上还是对线性回归中的进行训练。我们通过梯度下降在线性回归中进行计算,这就存在一个前提,即损失函数可导。而以 Sign函数为假设函数列出来的损失函数明显存在不可导(左导 = 0,右导 = 1)。而反观 Sigmoid函数,在左右范围内均有导数存在,所以采用Sigmoid函数。
总结
Logistic回归进行分类的主要思想是:根据训练数据利用Logistic回归生成最佳回归系数,并以此进行待测数据的分类。逻辑回归假设数据服从伯努利分布,通过极大似然估计的方法,运用(随机)梯度下降来求解参数,来达到数据二分类的目的。这次实验让我对Logistic回归有了一定的了解和认识,能运用其解决实际问题,但还不能熟练使用,要继续加强对机器学习相关知识的学习。总的来说是一次收获满满的实验。