【Python机器学习预测分析算法实战三】预测模型性能评估及影响因素

  选择并拟合一个预测算法的最终目标是获得最佳可能的效果。能够达到的性能取决于3方面的因素:问题的复杂性,模型算法的复杂性,可用数据的丰富程度。
理解函数逼近
  预测问题包括两种变量:
  第一种变量是尝试要预测的变量;
  第二种变量是用来进行预测的变量。
  函数逼近问题是要构建以第二类变量作为输入的函数来预测第一类变量。要预测的变量一般有多种正式的名称,如标签、目标、结果。用于构建预测的输入变量也有多种名称,如预测因子、回归因子、特征以及属性。决定使用哪种属性进行预测被称为特征工程。数据清洗以及特征工程占据了数据科学家80%-90%的时间。特征工程一般需要通过一个由人工参与的、迭代的过程来完成特征选择,决定可能最优的特征。
选择线性模型还是非线性模型
  在选择预测模型时会遇到选择线性模型还是非线性模型的问题,通常情况下对于列数比行数多的数据集或者相对简单的问题,倾向于使用线性模型。对于行比列多很多的复杂问题,倾向于使用非线性模型。另一个考虑的因素是训练时间。线性方法要比非线性方法的训练时间短。

预测模型性能的评估及提升
  我们说一个预测模型的性能优秀,一般是指预测值能够非常“接近”真实值。对于回归问题, y i y_i yi是一个实数值,性能使用均方误差(MSE)或者平均绝对误差(MAE)来度量。
M e a n   s q u a r e d   e r r o r = 1 m ∑ i = 1 m ( y i − p r e d ( x i ) ) 2 Mean\ squared \ error=\frac{1}{m}\sum_{i=1}^m(y_i-pred(x_i))^2 Mean squared error=m1i=1m(yipred(xi))2
M e a n   a b s o l u t e   e r r o r = 1 m ∑ i = 1 m ∣ y i − p r e d ( x i ) ∣ Mean\ absolute\ error=\frac{1}{m}\sum_{i=1}^m|y_i-pred(x_i)| Mean absolute error=m1i=1myipred(xi)
  如果预测错误的MSE同目标方差几乎相等,这说明预测算法效果并不好,因为通过简单对目标值求平均就能达到模型的预测效果。如果预测错误RMSE大约是实际目标标准差的一半,就说明模型的性能已经非常不错了。
  除了计算错误的摘要统计量以外,查看错误的分布直方图、长尾分布以及正态分布程度等对于分析错误原因以及错误程度也有一定的帮助。
  如果问题是一个分类问题,那么需要使用不同的性能指标。最常见的性能指标是误分类率,即计算pred()函数预测错误的样本比例。例如,预测网站访问者是否会点击链接的一个分类问题,如果点击和不点击以概率的方式给出,假设80%的情况下会点击,数据科学家可以选择50%作为阈值来决定是否呈现链接。在一些情况下,设置更高或者更低的阈值会有更好的结果。
  在相关文献中,也有很多使用ROC曲线和AUC面积进行模型评价的。这两个指标除了评价外,还有助于优化模型。
  下面以一个例子介绍下混淆矩阵和ROC曲线

import urllib.request, urllib.error, urllib.parse
import numpy
import random
from sklearn import datasets, linear_model
from sklearn.metrics import roc_curve, auc
import pylab as pl


def confusionMatrix(predicted, actual, threshold):
    if len(predicted) != len(actual): return -1
    tp = 0.0
    fp = 0.0
    tn = 0.0
    fn = 0.0
    for i in range(len(actual)):
        if actual[i] > 0.5: #正样本
            if predicted[i] > threshold:
                tp += 1.0 #被正确分类的正例
            else:
                fn += 1.0 #本来是正例,错分为负例
        else:              #负样本
            if predicted[i] < threshold:
                tn += 1.0 #被正确分类的负例
            else:
                fp += 1.0 #本来是负例,被错分为正例
    rtn = [tp, fn, fp, tn]
    return rtn


#读取数据
target_url = "https://archive.ics.uci.edu/ml/machine-learning-databases/undocumented/connectionist-bench/sonar/sonar.all-data"
data = urllib.request.urlopen(target_url)

#数据分类
xList = []
labels = []
for line in data:
    #逗号分隔
    row = line.strip().decode().split(",")
    #assign label 1.0 for "M" and 0.0 for "R"
    if(row[-1] == 'M'):
        labels.append(1.0)
    else:
        labels.append(0.0)
    #remove label from row
    row.pop()
    #convert row to floats
    floatRow = [float(num) for num in row]
    xList.append(floatRow)

#分测试样本和训练样本
indices = range(len(xList))
xListTest = [xList[i] for i in indices if i%3 == 0 ]
xListTrain = [xList[i] for i in indices if i%3 != 0 ]
labelsTest = [labels[i] for i in indices if i%3 == 0]
labelsTrain = [labels[i] for i in indices if i%3 != 0]

#数据准备
xTrain = numpy.array(xListTrain); yTrain = numpy.array(labelsTrain); xTest = numpy.array(xListTest); yTest = numpy.array(labelsTest)

#查看数据规模
print("Shape of xTrain array", xTrain.shape)
print("Shape of yTrain array", yTrain.shape)
print("Shape of xTest array", xTest.shape)
print("Shape of yTest array", yTest.shape)

#训练模型
rocksVMinesModel = linear_model.LinearRegression()
rocksVMinesModel.fit(xTrain,yTrain)

#训练结果
trainingPredictions = rocksVMinesModel.predict(xTrain)
print("Some values predicted by model", trainingPredictions[0:5], trainingPredictions[-6:-1])

#训练接混淆矩阵
confusionMatTrain = confusionMatrix(trainingPredictions, yTrain, 0.5)
#计算混淆矩阵
tp = confusionMatTrain[0]; fn = confusionMatTrain[1]; fp = confusionMatTrain[2]; tn = confusionMatTrain[3]

print("tp = " + str(tp) + "\tfn = " + str(fn) + "\n" + "fp = " + str(fp) + "\ttn = " + str(tn) + '\n')

#测试集预测
testPredictions = rocksVMinesModel.predict(xTest)

#测试集混淆矩阵
conMatTest = confusionMatrix(testPredictions, yTest, 0.5)
#就删混淆矩阵
tp = conMatTest[0]; fn = conMatTest[1]; fp = conMatTest[2]; tn = conMatTest[3]
print("tp = " + str(tp) + "\tfn = " + str(fn) + "\n" + "fp = " + str(fp) + "\ttn = " + str(tn) + '\n')

#训练集下计算roc曲线

fpr, tpr, thresholds = roc_curve(yTrain,trainingPredictions)
roc_auc = auc(fpr, tpr)
print( 'AUC for in-sample ROC curve: %f' % roc_auc)

# 绘制
pl.clf()
pl.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc)
pl.plot([0, 1], [0, 1], 'k--')
pl.xlim([0.0, 1.0])
pl.ylim([0.0, 1.0])
pl.xlabel('False Positive Rate')
pl.ylabel('True Positive Rate')
pl.title('In sample ROC rocks versus mines')
pl.legend(loc="lower right")
pl.show()

#测试集上计算roc
fpr, tpr, thresholds = roc_curve(yTest,testPredictions)
roc_auc = auc(fpr, tpr)
print( 'AUC for out-of-sample ROC curve: %f' % roc_auc)

# 绘制
pl.clf()
pl.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc)
pl.plot([0, 1], [0, 1], 'k--')
pl.xlim([0.0, 1.0])
pl.ylim([0.0, 1.0])
pl.xlabel('False Positive Rate')
pl.ylabel('True Positive Rate')
pl.title('Out-of-sample ROC rocks versus mines')
pl.legend(loc="lower right")
pl.show()

【Python机器学习预测分析算法实战三】预测模型性能评估及影响因素_第1张图片
【Python机器学习预测分析算法实战三】预测模型性能评估及影响因素_第2张图片
  代码第一部分是读取数据,并分成训练数据和测试数据。其中训练数据占三分之二,测试数据为三分之一。标签M和R分别为1和0。每个决策阈值的错误率都可以从混淆矩阵中计算得到。总的错误率为FP和FN的加和。从结果可以看出,在训练集上的错误率为9%在测试集上的错误率为25%。一般来说测试集上的性能要差与训练集上的性能。在测试集上的结果更能代表错误率。
  决策阈值发生改变的时候,误分类率也会变化。阈值的确定需根据实际的情况而综合商定。数据科学家可能并没有正负样本误分类的代价值,但仍然想使用误分类率外的其他刻画错误的方法,通常会使用ROC曲线。

  前面说过测试集上的错误率更加接近真实情况。尽管目前没有明确规则来确定测试集的大小,一般测试集可以占所有数据的25%到35%。但是要注意的是模型训练的性能会随着训练规模的减少而下降,将过多的数据用于测试会影响模型的性能估计。
  另一种预留数据的方法叫做n折交叉验证。将数据集分成n份不相交的子集,训练和测试需要多次遍历数据。假设n为5,第一次遍历预留第一块为测试集,其余为训练集;第二次遍历预留第二块为测试集,以此类推,直到所有数据都被预留一遍。
  n折交叉验证可以预估预测错误:在多份样本上估计错误来估计错误边界。通过为训练集分配更多样本,生成的模型会产生更低的泛化错误,具备更好的预测性能。例如,如果选择10折交叉验证,每次训练只需要留出10%的数据进行预测。n折交叉验证是以更多的训练时间作为代价的。
  另一个值得注意的是测试样本要能够代表整个数据集。尽量要保证训练样本和测试样本的选择是随机的。如果研究对象有特殊的统计特征,抽样的过程要更加小心。此时需要注意在测试集上保留统计特征。这类例子包括稀疏事件(如欺诈和广告点击)预测。要建模的事件出现的频率比较低,如果采用随机抽样可能会导致过多或者过少的样本出现在测试集中,从而导致对错误率的预估出现较大的误差。这种情况下最好采用分层抽样,将正负样本分别进行随机抽样,形成训练集和测试集。

All things are difficult before they are easy.

你可能感兴趣的:(ML,预测分析,python,预测分析)