分类回归及预测性能评估(通过python的scikit-learn实现)
回归要拟合或者预测的因变量是一个连续变量,而分类要拟合或者预测的因变量是一个离线变量。如通过一系列的特征(收入、工作年限、公司规模、是否有房、是否有车、是否有贷款)来预测一个信用卡申请人是否可以发放信用卡(因变量,取值为0或者1)。
分类和回归区别仅仅在于因变量是否连续性,分类可以是二分类或者多分类。分类问题的核心也是如何找到合适的特征。
针对分类问题,最经典的就是逻辑(logistic)回归,逻辑回归是一个二分类回归模型,**回归所得到的数值可以看做属于类别1的概率**
这个回归看起来y值不应该是离散的,它的图像是下面这样的。
看上图,y的值接近0或者接近1的概率是非常大的,所以回归得到的y值是分类为1的概率,一般我们认为小于0.5就把它归类的0,大于0.5归类到1,这个区间如何划分,可以根据具体业务进行调整。
有一种叫One vs Rest (OvR)的方法来利用逻辑回归拓展到多分类,具体是这样的。
- 为每一个类别分别建立一个二分类逻辑回归
- 训练每一个回归,得到该分类的概率和不是该分类的概率
- 在所有二分类中,选择概率最高的那个类别
使用logistic进行多分类,Scikit-learn默认会采用OvR方式。我们不需要关心logistic是否是二分类,scikit-learn会自动处理的。
接下来我们看看如何在scikit-learn中是如何使用logistic回归的。包括训练、拟合、性能评估的核心代码如下:
from sklearn import linear_model lm = linear_model.LogisticRegression() model = lm.fit(X,y) corss_val_score(lm,X,y,cv=5,scoring='accuracy')
看用法和线性回归几乎一模一样,看代码就是在线性模型里面调用了logistic模型。交叉检验用法几乎一样,只是打分的方法不同,因为逻辑回归的结果只有正确和不正常,所以这里的打分函数是准确率accuracy,就是在我们的测试集中有多少样本的分类被正确预测了。
我们开始进行实际的分析过程。先导入一下模块和准备好数据。
from sklearn import linear_model import pandas from sklearn.model_selection import cross_val_score import numpy iris = pandas.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data',header=None) iris.columns=['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm','Species']
接下来我们开始进行建模、拟合和评估。
lm = linear_model.LinearRegression() features = ['PetalLengthCm'] X = iris[features] y = iris['Species'] model = lm.fit(X,y) score = numpy.mean(-cross_val_score(lm,X,y,cv=5,scoring='accuracy'))
运行后,报错。这里我有意给一个错误和大家分享的。
ValueError: could not convert string to float: ‘Iris-virginica’
就是说我们的因变量Species是字符型,不能转化为float,虽然我们认为类别就应该是字符串,但对于sklearn来说,分类也需要用数字来表示。我们对Species属性做一些预处理,sklearn提供一个预处理的工具LabelEncoder,它会自动的帮忙把字符串类型转换为离散的数值型。
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() y = le.transform(iris['Species'])
这样y就自动把Species的三个文本分类改为数值型0 1 2。接下来我们就可以进行logistic回归了。
le = LabelEncoder() le.fit(iris['Species']) lm = linear_model.LogisticRegression() features = ['PetalLengthCm'] X = iris[features] y = le.transform(iris['Species']) model = lm.fit(X,y) score = numpy.mean(cross_val_score(lm,X,y,cv=5,scoring='accuracy')) print('平均性能得分:'+str(score))
平均性能得分:0.786666666667
我们尝试遍历四个特征的所有组合进行分类回归,看看预测的性能得分。
第一步,定义一个遍历所有特征的函数
def combinations(ls): n=1<<len(ls) tmp=[] for i in range(n): bits=[i>>offset&1 for offset in range(len(ls)-1,-1,-1)] if numpy.sum(bits)>0: current=[ls[index] for (index,bit) in enumerate(bits) if bit==1] tmp= tmp+[current] return tmp
我们看看四个特征的所有组合,是否正确。
group = ['PetalLengthCm', 'SepalLengthCm', 'SepalWidthCm','PetalWidthCm'] group_combinations = combinations(group)
每一个组合都遍历了。我们循环一次,每循环一次进行性能评估。
for v in group_combinations: X = iris[v] y = le.transform(iris['Species']) model = lm.fit(X,y) score = numpy.mean(cross_val_score(lm,X,y,cv=5,scoring='accuracy')) print('特征'+str(v) +'的平均得分:'+ '%.4f' % score)
结果如下:
可以看出我们把四个特征同时作为自变量,性能得分是最高的。预测准确率达到96%。
至此我们的分类回归就完成。 关于cross_val_score交叉检验,请看我的《回归分析及预测性能评估(通过python的scikit-learn实现)》文章的后半部分有详细介绍。
全部代码
from sklearn import linear_model import pandas from sklearn.model_selection import cross_val_score import numpy from sklearn.preprocessing import LabelEncoder iris = pandas.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data',header=None) iris.columns=['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm','Species'] le = LabelEncoder() le.fit(iris['Species']) lm = linear_model.LogisticRegression() features = ['PetalLengthCm'] X = iris[features] y = le.transform(iris['Species']) model = lm.fit(X,y) score = numpy.mean(cross_val_score(lm,X,y,cv=5,scoring='accuracy')) print('平均性能得分:'+str(score)) def combinations(ls): n=1<<len(ls) tmp=[] for i in range(n): bits=[i>>offset&1 for offset in range(len(ls)-1,-1,-1)] if numpy.sum(bits)>0: current=[ls[index] for (index,bit) in enumerate(bits) if bit==1] tmp= tmp+[current] return tmp cv=5 group = ['PetalLengthCm', 'SepalLengthCm', 'SepalWidthCm','PetalWidthCm'] group_combinations = combinations(group) for v in group_combinations: X = iris[v] y = le.transform(iris['Species']) model = lm.fit(X,y) score = numpy.mean(cross_val_score(lm,X,y,cv=5,scoring='accuracy')) print('特征'+str(v) +'的平均得分:'+ '%.4f' % score)