kaggle

作者: 冒绿光的盒子 | 来源:发表于2020-06-24 16:46 被阅读0次

    kaggle:房价预测

    首先,先把数据搞下来
    整整80个特征。label标签是不是正态分布,如果不是正态分布很多算法就用不上了,因为回归分析就是基于正态分布的。
    这看起来像是正态分布,但不完全是,右边的尾巴有点长,看看偏度是多少:
    偏度有点大,待会需要调整。

    特征太多了,不可能全用上,先画二部图看看相关性:



    挑选前十个与SalePrice最相关的特征。

    正式处理数据

    数据里面首先要看看有没有缺失值

    total = df_train.isnull().sum().sort_values(ascending=False)
    percent = ((df_train.isnull().sum())/df_train.isnull().count()).sort_values(ascending=False)
    missing_data = pd.concat([total, percent], axis=1, keys=['total', 'percent'])
    missing_data.head(20)
    

    不得不说,前面那几个特征几乎是没了的,要不填充他,要不就丢了。在处理缺失值之前,先去掉ID号,这种唯一标识符没什么用:

    train.drop('Id', axis = 1, inplace=True)
    test.drop('Id', axis = 1, inplace=True)
    

    一般情况下,房子的价格是和房子的面积息息相关,所以把房子的面积和房子的价格先画个二维散点图看看:



    很明显,右下角那两玩意肯定不是正常点,面积越大反而越便宜,除非是凶宅,这种情况也少见,不能把这两个点加入训练。

    train.drop(train[(train['GrLivArea'] > 4000) & (train['SalePrice'] < 300000)].index, inplace=True)
    
    var = 'GrLivArea'
    data = pd.concat([train['SalePrice'], train[var]], axis=1)
    data.plot.scatter(x=var, y='SalePrice', ylim=(0, 800000));
    

    我们目前要做的,是对label进行正态化:


    可以看到,大致是符合的,但是可惜右边太高,也就是说,随着价格升高,降低的速率放缓了。所以这就需要样本正态分布的变换:有一个样本量为n的样本,y_i(i=1...n),数据变换是指某一个函数经过f(y_i)变换,得到新样本f(y_i)
    选择这样的样本,首先要求样本之间的大小关系不能发生变化。
    现在要把右边的值变小,那么在数据变换的时候就需要选择数据越小,增加幅度越大,数据越大,增加幅度越大的函数,常见的比如\sqrt{y},lny
    train['SalePrice'] = np.log1p(train['SalePrice'])
    sns.distplot(train['SalePrice'], fit=norm)
    

    这里是ln(x+1),使其大于等于0,注意在算出结果后还需要还原。


    差不多了。

    填充

    接下来就是把那些缺失值给填了。

    挺多的,其实那几个缺失值比较多也可以丢了。如果是数字,可以取众数和中位数,平均值最好不要取,因为总有些人会乱写,平均值可能受影响。
    填充完了还有问题,就是离散值的问题,离散值可不能作为训练数据的:



    给他们编码。

    自变量分布

    回归问题就涉及到正态分布了,计算每一个特征的偏差,如果偏差过大,那么证明其偏离正态分布太远了,这样就需要用Box-cox变换调整。

    Box-cox变换

    如果按照之前那样先把偏差算出来太麻烦了,这里提供了一个自动的变换
    y(\lambda) = (if \quad \lambda != 0: y_i^{\lambda} - 1) \quad else : ln(y_i)
    一般情况下取0.15作为经验值

    from scipy.stats import norm, skew
    numeric_feats = all_data.dtypes[all_data.dtypes != 'object'].index
    skewed_feats = all_data[numeric_feats].apply(lambda x : skew(x.dropna())).sort_values(ascending=False)
    skewness = pd.DataFrame({'skew':skewed_feats})
    skewness.head(10)
    
    先把偏差值计算出来。

    大于0.75的全部处理了。

    skewness = skewness[abs(skewness) > 0.75]
    from scipy.special import boxcox1p
    skewed_features = skewness.index
    lam = 0.15
    for feat in skewed_features:
        all_data[feat] = boxcox1p(all_data[feat], lam)
    

    训练模型


    选取了4个模型进行训练,都差不多。但机器学习里面聚合模型一般是效果比较好的,选取平均模型:

    class AveragingModels(BaseEstimator, RegressorMixin, TransformerMixin):
        def __init__(self, models):
            self.models = models
        def fit(self, X, y):
            self.models_ = [clone(x) for x in self.models]
            for model in self.models_:
                model.fit(X, y)
            return self
        def predict(self, X):
            predictions = np.column_stack([model.predict(X) for model in self.models_])
            return np.mean(predictions, axis=1)
    

    比单个模型要好点。
    主要复杂的还是数据处理阶段,离群点,缺失值,回归问题注意正态分布,正态分布变化,偏差,使用Box-cox进行正态分布变换,还有最后模型阶段如何调包调参还不是特别熟悉。

    kaggle:房价预测 Another Solution

    https://www.kaggle.com/lavanyashukla01/how-i-made-top-0-3-on-a-kaggle-competition

    在kaggle上找了另外一个大佬的Solution,和我的差不多,但是别人的够专业。
    回归问题还是要看看label的分布是什么:

    sns.set_style("white")
    sns.set_color_codes(palette='deep')
    f, ax = plt.subplots(figsize = (8, 7))
    sns.distplot(train['SalePrice'], color = 'b')
    ax.xaxis.grid(False)
    ax.set(ylabel = 'Frequency')
    ax.set(xlabel = 'SalePrice')
    ax.set(title="SalePrice distribution")
    sns.despine(left=True)
    plt.show()
    

    然后把所有数字化的特征都打印出来,太多了,不写。
    二部图查看类别之间的联系。
    画箱线图:
    data = pd.concat([train['SalePrice'], train['OverallQual']], axis=1)
    plt.subplots(figsize = (8, 8))
    sns.boxplot(train['OverallQual'], train['SalePrice'], data=data)
    

    这一步我没有做,这一步可以看离群点以及数据整体的分布。
    然后就是画散点图了,看看特征和SalePrice的散点图,离群点有多少,是否符合一般情况。然后做Features Engineering。SalePrince的特征工程和我的做法一样。都是log化。丢弃离群点这些也是一样的,还有查看缺失值。缺失值的填充也差不多。这里的特征工程还有一步,即是增加特征这块,这里填充了许多:
    all_features['BsmtFinType1_Unf'] = 1*(all_features['BsmtFinType1'] == 'Unf')
    all_features['HasWoodDeck'] = (all_features['WoodDeckSF'] == 0) * 1
    all_features['HasOpenPorch'] = (all_features['OpenPorchSF'] == 0) * 1
    all_features['HasEnclosedPorch'] = (all_features['EnclosedPorch'] == 0) * 1
    all_features['Has3SsnPorch'] = (all_features['3SsnPorch'] == 0) * 1
    all_features['HasScreenPorch'] = (all_features['ScreenPorch'] == 0) * 1
    all_features['YearsSinceRemodel'] = all_features['YrSold'].astype(int) - all_features['YearRemodAdd'].astype(int)
    all_features['Total_Home_Quality'] = all_features['OverallQual'] + all_features['OverallCond']
    all_features = all_features.drop(['Utilities', 'Street', 'PoolQC',], axis=1)
    all_features['TotalSF'] = all_features['TotalBsmtSF'] + all_features['1stFlrSF'] + all_features['2ndFlrSF']
    all_features['YrBltAndRemod'] = all_features['YearBuilt'] + all_features['YearRemodAdd']
    
    all_features['Total_sqr_footage'] = (all_features['BsmtFinSF1'] + all_features['BsmtFinSF2'] +
                                     all_features['1stFlrSF'] + all_features['2ndFlrSF'])
    all_features['Total_Bathrooms'] = (all_features['FullBath'] + (0.5 * all_features['HalfBath']) +
                                   all_features['BsmtFullBath'] + (0.5 * all_features['BsmtHalfBath']))
    all_features['Total_porch_sf'] = (all_features['OpenPorchSF'] + all_features['3SsnPorch'] +
                                  all_features['EnclosedPorch'] + all_features['ScreenPorch'] +
                                  all_features['WoodDeckSF'])
    all_features['TotalBsmtSF'] = all_features['TotalBsmtSF'].apply(lambda x: np.exp(6) if x <= 0.0 else x)
    all_features['2ndFlrSF'] = all_features['2ndFlrSF'].apply(lambda x: np.exp(6.5) if x <= 0.0 else x)
    all_features['GarageArea'] = all_features['GarageArea'].apply(lambda x: np.exp(6) if x <= 0.0 else x)
    all_features['GarageCars'] = all_features['GarageCars'].apply(lambda x: 0 if x <= 0.0 else x)
    all_features['LotFrontage'] = all_features['LotFrontage'].apply(lambda x: np.exp(4.2) if x <= 0.0 else x)
    all_features['MasVnrArea'] = all_features['MasVnrArea'].apply(lambda x: np.exp(4) if x <= 0.0 else x)
    all_features['BsmtFinSF1'] = all_features['BsmtFinSF1'].apply(lambda x: np.exp(6.5) if x <= 0.0 else x)
    
    all_features['haspool'] = all_features['PoolArea'].apply(lambda x: 1 if x > 0 else 0)
    all_features['has2ndfloor'] = all_features['2ndFlrSF'].apply(lambda x: 1 if x > 0 else 0)
    all_features['hasgarage'] = all_features['GarageArea'].apply(lambda x: 1 if x > 0 else 0)
    all_features['hasbsmt'] = all_features['TotalBsmtSF'].apply(lambda x: 1 if x > 0 else 0)
    all_features['hasfireplace'] = all_features['Fireplaces'].apply(lambda x: 1 if x > 0 else 0)
    

    把缺失值特别多的特征丢弃,增加了许多布尔特征和一些总面积。
    另外,还对特征进行了log和均方变化:

    def logs(res, ls):
        m = res.shape[1]
        for l in ls:
            res = res.assign(newcol=pd.Series(np.log(1.01+res[l])).values)   
            res.columns.values[m] = l + '_log'
            m += 1
        return res
    
    log_features = ['LotFrontage','LotArea','MasVnrArea','BsmtFinSF1','BsmtFinSF2','BsmtUnfSF',
                     'TotalBsmtSF','1stFlrSF','2ndFlrSF','LowQualFinSF','GrLivArea',
                     'BsmtFullBath','BsmtHalfBath','FullBath','HalfBath','BedroomAbvGr','KitchenAbvGr',
                     'TotRmsAbvGrd','Fireplaces','GarageCars','GarageArea','WoodDeckSF','OpenPorchSF',
                     'EnclosedPorch','3SsnPorch','ScreenPorch','PoolArea','MiscVal','YearRemodAdd','TotalSF']
    
    all_features = logs(all_features, log_features)
    
    def squares(res, ls):
        m = res.shape[1]
        for l in ls:
            res = res.assign(newcol=pd.Series(res[l]*res[l]).values)   
            res.columns.values[m] = l + '_sq'
            m += 1
        return res 
    
    squared_features = ['YearRemodAdd', 'LotFrontage_log', 
                  'TotalBsmtSF_log', '1stFlrSF_log', '2ndFlrSF_log', 'GrLivArea_log',
                  'GarageCars_log', 'GarageArea_log']
    all_features = squares(all_features, squared_features)
    

    应该是想增强数字特征的显著性吧。
    然后就是one-hot编码那些非数字特征了。
    训练特征这块有一个混合模型比较特别,显示用7个模型做了堆叠,然后再把这7个模型和堆叠模型混合成一个aggregation model。

    def blended_predictions(X):
        return ((0.1 * ridge_model_full_data.predict(X)) + \
                (0.2 * svr_model_full_data.predict(X)) + \
                (0.1 * gbr_model_full_data.predict(X)) + \
                (0.1 * xgb_model_full_data.predict(X)) + \
                (0.1 * lgb_model_full_data.predict(X)) + \
                (0.05 * rf_model_full_data.predict(X)) + \
                (0.35 * stack_gen_model.predict(np.array(X))))
    

    在最后处理的阶段,还有一个调整预测值的操作。

    q1 = submission['SalePrice'].quantile(0.0045)
    q2 = submission['SalePrice'].quantile(0.99)
    submission['SalePrice'] = submission['SalePrice'].apply(lambda x: x if x > q1 else x*0.77)
    submission['SalePrice'] = submission['SalePrice'].apply(lambda x: x if x < q2 else x*1.1)
    submission.to_csv("submission_regression1.csv", index=False)
    

    这部分操作一开始看的我一脸懵逼,后来问了问原作者才知道,一般的预测都是低的变高点,高的变低一点,但是房价不一样,一般的越边远的地区,房价越低,低的可怜,高的一般会更高,所以我们算q1其实就是小到大排序0.0045%个房价,如果低于这个房价就把他再降一点,q2同理。


    最后的结果还差点但是相差不大,不过很专业。
    • 首先是正态分布化,我原来是默认lambda=0.5的,这里用了normax函数选择最优。
    • 二部图和箱线图。
    • 增加特征。
    • 堆叠模型和混合模型的结合。
    • 根据实际情况对预测数据做处理。

    10 monkey species

    这个数据集的存储方式有点奇怪,读取的时候用了图片生成器,这玩意也是第一次用。
    https://www.kaggle.com/greenarrow2018/10-monkeys-with-resnet50-93
    具体做法已有,已达到97%的准确率了,看了一下其他的kernel,好像最屌的也就和我差不多了。

    相关文章

      网友评论

        本文标题:kaggle

        本文链接:https://www.haomeiwen.com/subject/rvcuzhtx.html