基于AIC评价指标的逐步回归——Python语言实现

常用评价指标简介

       当前统计学以计算机科学作为支撑,机器于人工的优势是计算速度,但机器无法自行判断运算何时退出,因此需要定量指标作为运算退出的标志。对于预测类的统计模型来说,常见的指标有赤池信息准则(AIC)、贝叶斯信息准则(BIC)、R方、ROC曲线下方的面积大小(AUC)等指标。指标无优劣之分,在实际运用中很多情况甚至会出现不同评价指标相悖的情况,因此作为模型开发者不能只追求好看的评估指标。在本文中,实现模型时选择AIC作为评价指标,AIC是一种基于残差平方和进行变形的评价指标,因此AIC越小模型拟合效果越好。作为一种容易理解评估指标,AIC的数学公式为  AIC=2k+n(log(RSS/n)),其中RSS为残差平方和,n为观测数,k为变量数。从公式可以看出该指标综合考虑了模型的简洁性(变量个数)和准确性。

逻辑回归变量筛选方法简介

        逻辑回归的变量选择中,目前主要使用的方法有向前回归法、向后回归法和逐步回归。向前回归法是将自变量依次加入模型,每加入一个特征都利用检验指标检验,保留使模型指标更优的自变量,依次不断迭代加入,直到评估指标不能再优化。向后回归法是将所有变量都放入模型之后,一次迭代剔除变量,将某一自变量拿出模型,若模型评估指标更优化,则剔除此变量,对全部变量进行迭代,直到评估指标不能再优化。逐步回归综合向前和向后两种方法,在每一次加入新特征后(即向前法加入),再依次删掉每个特征(即向后法删除),考虑所加入的新变量对每个已有变量的影响。

        基于其原理,其运行效率从高到低依次为 向前≥向后≥逐步(不同的变量情况可能会导致向后和逐步法的效率不一致),这是因为向前回归依次加入自变量计算评估指标数值,但一般由于不断剔除特征并不会整的会对全部自变量进行运算,而向后法在运算之初就需要使用全部特征进行运算,其效率自然会低一些,逐步回归由于每一次迭代同时进行了依次向前回归和向后回归,因此其效率最低。根据原理其效果应该为 逐步≥向后≥向前,但在实际应用中三者差别并不会太大。

逐步回归的python代码实现

代码逻辑思路:

  1. 使用向前选择法将候选特征逐一加入模型,在加入特征后计算模型的AIC是否减小判断是否加入该特征;
  2. 当新加入的特征达到4个时,进行一次向后选择删掉显著性较差的特征;
  3. 循环执行第1、2步,每加入4个新变量就进行一次向后选择,直至无新特征加入也没用特征剔除。

基于以上原理,使用python进行实现

'''
1. 传入参数
data     : DataFrame格式,传入包含target的数据集
response :String格式,指明target的列名
2. 返回值
return   : 返回运算后的最优statsmodel模型
'''
def stepwise_select(data, response):
    def forward_method(data, response,selected):
        initial_selected_num = len(selected)
        remaining = set(data.columns)
        remaining.remove(response)
        # 去掉已选定的变量
        for a in selected:
            remaining.remove(a)
        current_score, best_new_score = float('inf'), float('inf')
        initial_selected_num = len(selected)
        while remaining:

            aic_with_candidates=[]
            for candidate in remaining:
                formula = "{} ~ {}".format(
                    response,' + '.join(selected + [candidate]))
                aic = smf.glm(
                    formula=formula, data=data, 
                    family=sm.families.Binomial(sm.families.links.logit)
                ).fit().aic
                aic_with_candidates.append((aic, candidate))
            aic_with_candidates.sort(reverse=True)
            best_new_score, best_candidate=aic_with_candidates.pop()
            if current_score > best_new_score: 
                remaining.remove(best_candidate)
                selected.append(best_candidate)
                current_score = best_new_score
                print ('aic is {},continuing!'.format(current_score))
            else:        
                print ('forward selection over!')
                break
            print(len(selected) - initial_selected_num)
            if (len(selected) - initial_selected_num) >=2:
                formula = "{} ~ {} ".format(response,' + '.join(selected))
                print('formula is {}'.format(formula))
                selected = backward_method(data, response,selected)

        formula = "{} ~ {} ".format(response,' + '.join(selected))
        print('formula is {}'.format(formula))
        model = smf.glm(
            formula=formula, data=data, 
            family=sm.families.Binomial(sm.families.links.logit)
        ).fit()
        return(model)

    def backward_method(data, response,selected):
        #selected = list(selected)
        print("-"*100)
        removed = []
        # 初始化赋值
        best_new_score = float('inf')
        # 全部特征的AIC作为初始参数
        formula = "{} ~ {}".format(
                    response,' + '.join(selected))
        current_score = smf.glm(
            formula=formula, data=data, 
            family=sm.families.Binomial(sm.families.links.logit)
        ).fit().aic
        print ('initial aic is {}!'.format(current_score))
        print('initial formula is {}'.format(formula))
        while selected:
            aic_with_candidates=[]
            for candidate in selected:
                select_tmp = selected.copy()
                select_tmp.remove(candidate)
                formula = "{} ~ {}".format(
                    response,' + '.join(select_tmp))
                aic = smf.glm(
                    formula=formula, data=data, 
                    family=sm.families.Binomial(sm.families.links.logit)
                ).fit().aic
                aic_with_candidates.append((aic, candidate))
            aic_with_candidates.sort(reverse=True)
            best_new_score, best_candidate=aic_with_candidates.pop()
            if current_score > best_new_score: 
                selected.remove(best_candidate)
                removed.append(best_candidate)
                current_score = best_new_score
                print ('aic is {},continuing!'.format(current_score))
            else:        
                print ('backward selection over!')
                break

        formula = "{} ~ {} ".format(response,' + '.join(selected))
        print('final formula is {}'.format(formula))
        return(selected)
    
    selected = []
    forward_method(data, response,selected)
    

从执行的原理来看,逐步回归适合变量较多的情况,但是在一些情况下逐步回归和向前回归会得到相通的结果,但是逐步回归会比向前回归耗费更多的资源。所以在资源有限的情况下,优先选择向前回归进行模型训练。

你可能感兴趣的:(统计模型,python,机器学习,逻辑回归,数据分析)