• ML_评价


    如何评价模型的好坏。
    学习目标有三个:数据拆分:训练数据集&测试数据集(西瓜书第二章)

      

    (一)数据拆分的原因:防止过拟合,即只有训练数据在模型中表现好,而之外的数据都会出现拟合较差的情况——泛化能力差

      learning目的:寻找泛化误差小的模型,但是又依赖于经验误差——将样本集分为training &test(互斥),用test error近似泛化误差

      常见split方法:

       1. 留出hold_out:直接将数据集D划分为两个互斥的集合

          tips!training samples 和test samples的划分要尽可能保持数据分布的一致性,避免由于数据划分引起的偏差对结果产生影响。如——分层抽样

          单次使用留出法得到的结果不可靠,一般采用若干次随机划分,每次都评估,留出法返回的是多个结果的平均

          划分比例:若训练集S中包含绝大多数样本,则训练出来的模型会更加接近于D训练出来的模型,但是T比较小,评估结果可能会不稳定。若T中包含较多的样本,则S与D的差别较大,降低了评估的保真性fidelity。为了避免这些问题,常将2/3—4/5的样本用于training,剩下的testing

      2.cross validation较差验证法

      将D划分为k个大小相似,数据分布尽可能一致的互斥子集,(如分层),将K-个子集的并集作为training,剩余testing。则可以做k次检验,最终返回K次结果的均值

       留一法—K=n

      3.自助法bootstrapping——减少训练样本规模变小的影响

      bootstrap sample采样:每次都从D(m)中取出一个样本放到D‘中,最终D'中有m个样本

     调参:对每个参数选择范围和步长

    最终模型:先用训练集进行评估测试,最后选定算法和参数,再用D重新训练模型,这就是提交给用户的最终模型

       在训练集上训练模型(参数),在验证集上评估模型,一旦找到的最佳的参数(超参数),就在测试集上最后测试一次,测试集上的误差作为泛化误差的近似。

      吴恩达老师的视频中,如果当数据量不是很大的时候(万级别以下)的时候将训练集、验证集以及测试集划分为6:2:2;若是数据很大,可以将训练集、验证集、测试集比例调整为98:1:1;但是当可用的数据很少的情况下也可以使用一些高级的方法,比如留出方,K折交叉验证等。

    #%%数据拆分开
    #%%#0x01 判断模型好坏
    import numpy as np
    from sklearn import datasets
    import matplotlib.pyplot as  plt
    
    iris=datasets.load_iris()
    X=iris.data
    y=iris.target
    X.shape
    y.shape
    #拆分工作
    from sklearn.model_selection import train_test_split
    X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3) #方法一
    #将X和y合并为同一个矩阵,然后对矩阵进行shuffle,之后再分解
    #对y的索引进行乱序,根据索引确定与X的对应关系,最后再通过乱序的索引进行赋值
    #shuffle() 方法将序列的所有元素随机排序。
    tempConcat = np.concatenate((X, y.reshape(-1,1)), axis=1) #方法一
        #reshape(-1,1)表示行数自动计算,axis=1按列拼接
    np.random.shuffle(tempConcat)
    shuffle_X,shuffle_y = np.split(tempConcat, [4], axis=1) 
    test_ratio = 0.2
    test_size = int(len(X) * test_ratio) # 设置划分的比例
    X_train = shuffle_X[test_size:]
    y_train = shuffle_y[test_size:]
    X_test = shuffle_X[:test_size]
    y_test = shuffle_y[:test_size]
    
    shuffle_index = np.random.permutation(len(X))  # 方法2
        # 将x长度这么多的数,返回一个新的打乱顺序的数组,注意,数组中的元素不是原来的数据,而是混乱的索引
    test_ratio = 0.2
    test_size = int(len(X) * test_ratio)# 指定测试数据的比例
    test_index = shuffle_index[:test_size]
    train_index = shuffle_index[test_size:]
    X_train = X[train_index]
    X_test = X[test_index]
    y_train = y[train_index]
    y_test = y[test_index]
    y_test.shape
    #
    from sklearn.neighbors import KNeighborsClassifier
    knn_0307=KNeighborsClassifier(n_neighbors=3)
    knn_0307.fit(X_train,y_train)
    y_predict=knn_0307.predict(X_test)
    
    #对比
    np.row_stack([y_predict,y_test])  #合并
    #准确率
    sum(y_predict == y_test) / len(y_test)
    knn_0307.score(X_test,y_test)
    #%%0x02 分类准确度accuracy
    #数据探索
    digits = datasets.load_digits() # 手写数字数据集,封装好的对象,可以理解为一个字段
    X=digits.data
    X.shape
    y=digits.target
    y.shape
    digits.target_names#分类标签
    # 去除某一个具体的数据,查看其特征以及标签信息
    some_digit = X[666]
    y[666]
    some_digmit_image = some_digit.reshape(8, 8)
    plt.imshow(some_digmit_image, cmap = plt.cm.binary)
    plt.show()
    
    #%%超参数:所谓超参数,就是在机器学习算法模型执行之前需要指定的参数。(调参调的就是超参数) 如kNN算法中的k
    # 指定最佳值的分数,初始化为0.0;设置最佳值k,初始值为-1
    best_score = 0.0
    best_k = -1
    for k in range(1, 11):  # 暂且设定到1~11的范围内
        knn_clf = KNeighborsClassifier(n_neighbors=k)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)   
        if score > best_score:
            best_k = k
            best_score = score
    print("best_k = ", best_k)
    print("best_score = ", best_score)
    #权重
    #在 sklearn.neighbors 的构造函数 KNeighborsClassifier 中有一个参数:weights,默认是uniform即不考虑距离,也可以写distance来考虑距离权重
    best_method = ""
    best_score = 0.0
    best_k = -1
    for method in ["uniform","distance"]:    
        for k in range(1, 11):
            knn_clf = KNeighborsClassifier(n_neighbors=k, weights=method, p=2)
            knn_clf.fit(X_train, y_train)
            score = knn_clf.score(X_test, y_test)        
            if score > best_score:
                best_k = k
                best_score = score
                best_method = method
    print("best_method = ", method)
    print("best_k = ", best_k)
    print("best_score = ", best_score)
    
    #网格搜索——一次性地把我们想要得到最好的超参数组合列出来
    param_search = [
        { "weights":["uniform"], "n_neighbors":[i for i in range(1,11)] },
        { "weights":["distance"], "n_neighbors":[i for i in range(1,11)],"p":[i for i in range(1,6)]}
            ]
    knn_clf = KNeighborsClassifier()# 调用网格搜索方法
    from sklearn.model_selection import GridSearchCV
        # 定义网格搜索的对象grid_search,其构造函数的第一个参数表示对哪一个分类器进行算法搜索,第二个参数表示网格搜索相应的参数
    grid_search = GridSearchCV(knn_clf, param_search)
    grid_search.fit(X_train, y_train)
    grid_search.best_estimator_
    grid_search.best_score_

    #随机划分
    data = np.hstack([X,y[:,np.newaxis]])
    #先将数据集进行拼接,要不然我们只针对样本进行采样的话,会找不到对应的标签的
    train_set,test_set = train_test_split(data,test_size = 0.2,random_state = 42)
    print(len(train_set),len(test_set))
    #分层划分样本集
    from sklearn.model_selection import StratifiedShuffleSplit

    
    

    split = StratifiedShuffleSplit(n_splits = 1,test_size = 0.2,random_state = 42)

    
    

    for train_index,test_index in split.split(data,data[:,-1]):
    train_set = data[train_index,:]
    test_set = data[test_index,:]

    
    

    print(len(train_set),len(test_set))
    #如果想要知道抽取的各个样本的比例——转换为dataframe
    import pandas as pd
    train_data = pd.DataFrame(train_set)
    train_data.info()
    #查看各个类别的比例-count函数
    print(train_data[4].value_counts() / len(train_data))

     

    (二)评价分类结果:精准度、混淆矩阵、精准率、召回率、F1 Score、ROC曲线等

    1、分类算法的评价

      对于一个回归问题,可以使用MSE、RMSE、MAE、R方。对于一个分类问题,可以使用分类精准度。但是实际上,分类精准度是存在陷阱的,有时候我们会需要更加全面的信息。

      对于极度偏斜(Skewed Data)的数据,只使用分类准确度是不能衡量。这是就需要使用混淆矩阵(Confusion Matrix)做进一步分析。

      混淆矩阵:第二个字母:What's your judgement about the sample?;第一个字母:Is your judgement right(true) or not(false)?

    2、精准率和召回率

        precesion = TP/(TP+FP) 即,查准率,检索结果中,都是你认为应该为正的样本(第二个字母都是P),但是其中有你判断正确的和判断错误的(第一个字母有T ,F)。通常更关注值为1的特征,比如“患病”,比如“有风险”。精准率为我们关注的那个事件,预测的有多准。

        recall = TP/(TP+FN)即,查全率,检索结果中,你判断为正的样本也确实为正的,以及那些没在检索结果中被你判断为负但是事实上是正的(FN)。召回率也就是我们关注的那个事件真实的发生情况下,我们成功预测的比例是多少。

    思考

    ①是否只关注accuracy?

    虽然准确率可以判断总的正确率,但是如果存在样本不均衡的情况下,就不能使用accuracy来进行衡量了。比如,一个总样本中,正样本占90%,负样本占10%,那么只需要将所有样本预测为正,就可以拿到90%的准确率,而这显然是无意义的。正因为如此,我们就需要精准率和召回率了。

    ②那么精准率和召回率这两个指标如何解读?如果对于一个模型来说,两个指标一个低一个高该如何取舍呢?

    精准率的应用场景:预测癌症。医生预测为癌症,患者确实患癌症的比例。

    召回率的应用场景:网贷违约率,相对好用户,我们更关心坏用户。召回率越高,代表实际坏用户中被预测出来的概率越高。

    例如对一般搜索的情况是在保证召回率的情况下提升准确率,而如果是疾病监测、反垃圾邮件等,则是在保证准确率的条件下,提升召回率。但有时候,需要兼顾两者,那么就可以用F-score指标

    3、F1score_是调和平均数,只有两者都高的时候调和平均数才会高

    4、ROC曲线受试者工作特征曲线 receiver operating characteristic curve ) 的简写,又称为感受性曲线(sensitivity curve)

      阈值:为了将逻辑回归值映射到二元类别,您必须指定分类阈值(也称为判定阈值)。如果值高于该阈值,则表示“垃圾邮件”;如果值低于该阈值,则表示“非垃圾邮件”。人们往往会认为分类阈值应始终为 0.5,但阈值取决于具体问题,因此您必须对其进行调整 

      横轴假正率(FP率),纵轴为真正率(TP率)-TPR和FPR之间是成正比的,TPR高,FPR也高。ROC曲线就是刻画这两个指标之间的关系。

    TPR就是所有正例中,有多少被正确地判定为正;FPR是所有负例中,有多少被错误地判定为正。 分类阈值取不同值,TPR和FPR的计算结果也不同,最理想情况下,我们希望所有正例 & 负例 都被成功预测  TPR=1,FPR=0,即 所有的正例预测值 > 所有的负例预测值,此时阈值取最小正例预测值与最大负例预测值之间的值即可。

    TPR越大越好,FPR越小越好,但这两个指标通常是矛盾的。为了增大TPR,可以预测更多的样本为正例,与此同时也增加了更多负例被误判为正例的情况。

    ROC曲线是通过遍历所有阈值来描述整条曲线的。如果我们不断遍历所有阈值,预测的正样本与负样本数就在不断变化,相应的就会在ROC曲线移动。注意,此时ROC曲线本身的形状不会变化。

    思考:

    改变阈值并不会影响ROC曲线本身,那么如何判断ROC曲线的好坏?

    重新回归到曲线本身,横轴是假正率,纵轴是真正率,我们希望在FP率低的情况下,TP率高。也就是说,ROC曲越陡越好

     5、AUC

    一般在ROC曲线中,我们关注是曲线下面的面积, 称为AUC(Area Under Curve)。这个AUC是横轴范围(0,1 ),纵轴是(0,1)所以总面积是小于1的。

    ROC和AUC的主要应用:比较两个模型哪个好?主要通过AUC能够直观看出来。

    ROC曲线下方由梯形组成,矩形可以看成特征的梯形。因此,AUC的面积可以这样算:(上底+下底)* 高 / 2,曲线下面的面积可以由多个梯形面积叠加得到。AUC越大,分类器分类效果越好。

    • AUC = 1,是完美分类器,采用这个预测模型时,不管设定什么阈值都能得出完美预测。绝大多数预测的场合,不存在完美分类器。
    • 0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。
    • AUC = 0.5,跟随机猜测一样,模型没有预测价值。
    • AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。
    • #%%分类评价
      import numpy as np
      from sklearn import datasets
      from sklearn.model_selection import train_test_split
      from sklearn.linear_model import LogisticRegression
      
      digits=datasets.load_digits()
      X=digits.data
      y=digits.target.copy()
      y.shape
      # 要构造偏斜数据,将数字9的对应索引的元素设置为1,0~8设置为0
      y[digits.target==9]=1
      y[digits.target!=9]=0
      
      X_train,X_test,y_train,y_test=train_test_split(X, y, random_state=666)
      log_reg=LogisticRegression()
      log_reg.fit(X_train,y_train)
      y_log_predict=log_reg.predict(X_test)
      log_reg.score(X_test, y_test)
      #定义混淆矩阵的四个指标:TN
      def TN(y_true, y_predict):
          assert len(y_true) == len(y_predict)
          # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
          # 向量与向量按位与,结果还是布尔向量
          # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
          return np.sum((y_true == 0) & (y_predict == 0))  # 向量与向量按位与,结果还是向量
      TN(y_test, y_log_predict)
      
      #定义混淆矩阵的四个指标:FP
      def FP(y_true, y_predict):
          assert len(y_true) == len(y_predict)
          # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
          # 向量与向量按位与,结果还是布尔向量
          # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
          return np.sum((y_true == 0) & (y_predict == 1))  # 向量与向量按位与,结果还是向量
      FP(y_test, y_log_predict)
      
      #定义混淆矩阵的四个指标:FN
      def FN(y_true, y_predict):
          assert len(y_true) == len(y_predict)
          # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
          # 向量与向量按位与,结果还是布尔向量
          # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
          return np.sum((y_true == 1) & (y_predict == 0))  # 向量与向量按位与,结果还是向量
      FN(y_test, y_log_predict)
      #定义混淆矩阵的四个指标:TP
      def TP(y_true, y_predict):
          assert len(y_true) == len(y_predict)
          # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
          # 向量与向量按位与,结果还是布尔向量
          # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
          return np.sum((y_true == 1) & (y_predict == 1))  # 向量与向量按位与,结果还是向量
      TP(y_test, y_log_predict)
      #输出混淆矩阵
      def confusion_matrix(y_true, y_predict):
          return np.array([
              [TN(y_true, y_predict), FP(y_true, y_predict)],
              [FN(y_true, y_predict), TP(y_true, y_predict)]
          ])
      confusion_matrix(y_test, y_log_predict)
      #精准率
      def precision_score(y_true, y_predict):
          tp = TP(y_true, y_predict)
          fp = FP(y_true, y_predict)
          try:
              return tp / (tp + fp)
          except:
              return 0.0
      precision_score(y_test, y_log_predict)
      #召回率
      def recall_score(y_true, y_predict):
          tp = TP(y_true, y_predict)
          fn = FN(y_true, y_predict)
          try:
              return tp / (tp + fn)
          except:
              return 0.0
      recall_score(y_test, y_log_predict)
      # scikit-learn中的混淆矩阵,精准率和召回率
      from sklearn.metrics import confusion_matrix,precision_score,recall_score
      confusion_matrix(y_test,y_log_predict)
      precision_score(y_test,y_log_predict)
      recall_score(y_test,y_log_predict)
      #F1_SCORE
      import numpy as np
      def f1_score(precision, recall):
          try:
              return 2 * precision * recall / (precision + recall)
          except:
              return 0.0
      precision = 0.5
      recall = 0.5
      f1_score(precision, recall)
      precision = 0.9
      recall = 0.1
      f1_score(precision, recall)
      #decision_function,即返回分类阈值
      decision_scores = log_reg.decision_function(X_test)
      y_predict = np.array(decision_scores >= 5, dtype='int')  #大于等于5则为1
      
      # =============================================================================
      # #ROC
      # =============================================================================
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn import datasets
      from sklearn.model_selection import train_test_split
      from sklearn.linear_model import LogisticRegression
      
      digits = datasets.load_digits()
      X = digits.data
      y = digits.target.copy()
      
      # 要构造偏斜数据,将数字9的对应索引的元素设置为1,0~8设置为0
      y[digits.target==9]=1
      y[digits.target!=9]=0
      
      # 使用逻辑回归做一个分类
      X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
      
      log_reg = LogisticRegression()
      log_reg.fit(X_train,y_train)
      # 计算逻辑回归给予X_test样本的决策数据值
      # 通过decision_function可以调整精准率和召回率
      decision_scores = log_reg.decision_function(X_test)
      
      # TPR
      def TPR(y_true, y_predict):
          tp = TP(y_true, y_predict)
          fn = FN(y_true, y_predict)
          try:
              return tp / (tp + fn)
          except:
              return 0.0
      
      # FPR
      def FPR(y_true, y_predict):
          fp = FP(y_true, y_predict)
          tn = TN(y_true, y_predict)
          try:
              return fp / (fp + tn)
          except:
              return 0.0
      
      fprs = []    
      tprs = []
      
      # 以0.1为步长,遍历decision_scores中的最小值到最大值的所有数据点,将其作为阈值集合
      thresholds = np.arange(np.min(decision_scores), np.max(decision_scores), 0.1)
      for threshold in thresholds:
          # decision_scores >= threshold 是布尔型向量,用dtype设置为int
          # 大于等于阈值threshold分类为1,小于为0,用这种方法得到预测值
          y_predict = np.array(decision_scores >= threshold, dtype=int)
          #print(y_predict)
          # print(y_test)
          #print(FPR(y_test, y_predict))
          # 对于每个阈值,所得到的FPR和TPR都添加到相应的队列中
          fprs.append(FPR(y_test, y_predict))
          tprs.append(TPR(y_test, y_predict))
          
      # 绘制ROC曲线,x轴是fpr的值,y轴是tpr的值
      plt.plot(fprs, tprs)
      plt.show()
      #SKLEARN中的roc
      from sklearn.metrics import roc_curve
      fprs, tprs, thresholds = roc_curve(y_test, decision_scores)
      plt.plot(fprs, tprs)
      plt.show()
      #sklearn中的auc
      from sklearn.metrics import roc_auc_score
      roc_auc_score(y_test,decision_scores)

    (三)评价回归结果:MSE、RMSE、MAE、R Squared

    1、均方误差MSE 

    为了抵消掉数据量的形象,可以除去数据量,抵消误差。通过这种处理方式得到的结果叫做 均方误差MSE(Mean Squared Error)

    2、 均方根误差RMSE

    3、平均绝对误差MAE

     

    在之前确定损失函数时,我们提过,绝对值函数不是处处可导的,因此没有使用绝对值。但是在评价模型时不影响。因此模型的评价方法可以和损失函数不同。

    4、R_square

    • 对于分子来说,预测值和真实值之差的平方和,即使用我们的模型预测产生的错误。
    • 对于分母来说,是均值和真实值之差的平方和,即认为“预测值=样本均值”这个模型(Baseline Model)所产生的错误。
    • 我们使用Baseline模型产生的错误较多,我们使用自己的模型错误较少。因此用1减去较少的错误除以较多的错误,实际上是衡量了我们的模型拟合住数据的地方,即没有产生错误的相应指标

     

    #%%波士顿放假——回归评价
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn import datasets
    
    # 查看数据集描述
    boston = datasets.load_boston()
    print(boston.DESCR)
    # 查看数据集的特征列表
    boston.feature_names
    #RM average number of rooms per dwelling 每个住宅的平均房间数 选一个特征建模
    x = boston.data[:,5]
    x.shape
    # 取出样本标签
    y = boston.target
    y.shape
    plt.scatter(x,y)
    #数据清洗——去掉50
    
    np.max(y)
    # 这里有一个骚操作,用比较运算符返回一个布尔值的向量,将其作为索引,直接在矩阵里对每个元素进行过滤。
    x = x[y < 50.0]
    y = y[y < 50.0]
    plt.scatter(x,y)
    plt.show()
    x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.3)
    y.shape
    x.shape
    from sklearn import linear_model
    reg=linear_model.LinearRegression()
    reg.fit(x_train.reshape(-1, 1),y_train)
    reg.intercept_
    reg.coef_
    y_predict = reg.predict(x_test.reshape(-1, 1))
    print(y_predict)
    # =============================================================================
    #MSE
    mse_test = np.sum((y_predict - y_test) ** 2) / len(y_test)
    mse_test
    
    from math import sqrt
    rmse_test = sqrt(mse_test)
    rmse_test
    #MAE
    mae_test = np.sum(np.absolute(y_predict - y_test)) / len(y_test)
    mae_test
    #SKLEARN
    from sklearn.metrics import mean_squared_error
    from sklearn.metrics import mean_absolute_error
    mean_squared_error(y_test, y_predict)
    mean_absolute_error(y_test,y_predict)
    # =============================================================================
    #r^2
    1 - mean_squared_error(y_test, y_predict) / np.var(y_test)
    from sklearn.metrics import r2_score
    r2_score(y_test, y_predict)
  • 相关阅读:
    Hackerrank--Emma and sum of products (FFT)
    Hdu 1402 (FFT)
    Hackerrank--Divisibility of Power(Math)
    Hackerrank--Stock Maximize(DP Practice)
    Codeforces 455B
    vim 简明教程(转自飘过的小牛)
    ACdream 1007 (快速幂)
    编写可维护的JS 02
    编写可维护的JS 01
    图片加载-从模糊到清晰
  • 原文地址:https://www.cnblogs.com/wjAllison/p/12437339.html
Copyright © 2020-2023  润新知