kaggle-(house prediction, titanic, 10-monkey)

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函数选择最优。
  • 二部图和箱线图。
  • 增加特征。
  • 堆叠模型和混合模型的结合。
  • 根据实际情况对预测数据做处理。

Titanic prediction

不得不说sklearn的文档写的真好。
首先查看变量


Screen Shot 2020-08-09 at 12.57.18 PM.png

PassengerId肯定是没有是没用的,name也没有什么用。Ticket我也感觉用处不大,都删除了。
把字符串用数字替换:

train['Sex'].replace('male', 1, inplace=True)
train['Sex'].replace('female', 0, inplace=True)
train['Embarked'].replace('C', 0, inplace=True)
train['Embarked'].replace('Q', 1, inplace=True)
train['Embarked'].replace('S', 2, inplace=True)
train.head(10)

查看热图:

heatmap_name = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
heatmap_dataframe = train[heatmap_name]

plt.figure(figsize=(15, 10))
sns.heatmap(heatmap_dataframe.corr(), cmap = 'Reds')
plt.show()</pre>


接着就是空值替换。



Carbin基本空完了。所以我直接丢掉,其他用均值替换。

模型训练

模型基本都是采用树结构。 原先采用了支持向量机,高斯贝叶斯等等,但是效果都不好,cross-validation都是在60-70左右,而采用树结构基本都是80以上。随机森林,决策树,adaboost,GBT等等,再进行blending和stacking。

最后效果最好的还是blending。

Titanic prediction another solution

https://www.kaggle.com/ldfreeman3/a-data-science-framework-to-achieve-99-accuracy
这篇文章讲的贼几把专业。
把Titanic预测分成了7个部分。

  • 首先是认识并定义问题。只有知道这个问题是要解决市面才能选择工具。
  • 收集数据
  • 预处理数据
  • 探索性分析,主要是为变量定性。创建,完善或者丢弃变量。
  • 模拟数据
  • 训练数据
  • 优化及其策略调整
    前面两个步骤直接丢了,预处理数据文章里面提到了主要的几个手段。收集,完善,创建,转换。
    收集主要是指把一些异常数据,离群点去除掉。完善主要是是把空值完善。如果缺失值太多就可以丢掉了。age用均值填充,carbine缺失太多直接丢弃,而embark用mode填充。还有Name,name这个变量我一直以为他是类似于ID的标识符,但实际上他还是有一定信息的。比如可以从姓氏知道家庭成员的多少,家庭成员也有很多作用,比如你掉水了尼玛一把拉你上来;要是一个人就惨了,从doctor这些职业名词知道他是什么职业的。我之前做就是直接把name丢了。
    创建就是合并一下变量创建出新的变量了。比如把家庭成员标出来。转换即是把一些object转换成计算机能处理的数据类型。比如dummy函数。
    完善数据这块基本和我差不多,但是在特征工程构建新特征增加了不少的信息。
    首先是家庭成员数量添加了进去,其次还有是否是一个人。姓氏也添加了。年来和收入也有四分数和五分数标明。家庭数量多的可能更容易获救,一个人一般就死了吧。姓氏应该也是配合家庭数量的。
    然后就是one-hot了,直接get_dummies即可。
    接下来就是进一步探索了。
    主要是是通过图表和统计来了解数据。这方面我也不是很会,我的原问题也只是画了一个热图看看而已。

箱线图

首先介绍一下什么是箱线图。


上图是一个箱线图的基本形状,箱子的上下限是表示上四分位和下四分位数,中间是平均值,所以这个箱子里面是包含了百分之50的数据。顶线和底线代表最大值和最小值,冒出去的点可以看成是异常值就好了。如果这个图像很均匀,那么说明数据很正常,如果图像很扁,都快一条线了,那就说明偏科有点严重了。如果全部是正数就可以用对数变换一下。但是,其实并不是所有的数据都适合用箱线图来描述,如果数据存在很大或者很小的异常值,就可能导致箱子被压缩。还有一种可能就是数据量过小。
箱线图一般比较好的做法了,配合其他变量做分组箱线图对比。
所以总的来说,1)箱线图是对于连续性变量来说的,主要就是观测平均值,离群点等等。2)当箱线图太扁的时候,可以做对数变换。3)连续变量太过单一的时候不适合,直接画平布直方图就好了。4)箱线图最好就是定量对比。


回到这个solution,他这里的对比也是用箱线图和直方图对比。


Fare的数据分布不太均匀,箱线图比较扁,离群值多一些,可能需要做一个变换。直方图看起来好像是有钱人生存多一些,但是基数不同没办法对比。年龄稍微要均衡不少,参考价值稍微高一些。family size数据有点偏了。
这篇文章在后面画了非常多的图观测数据,但是可惜没有提到如何观察,讲道理我也只是看了他做了这么多图。不过确实是对数据了解不少。
最后的训练讲的不是特别清晰,我也不知道他这个99%accuracy是怎么来的。
看了不少notebook,发现有好多人都喜欢创建一些再我看来是有点无聊的feature。比如年龄,家庭成员大小也会做个分类,small,large等等,age也会分类,是否marry也会添加。
这些solution与我的做法区别就在于数据分析以及feature engineering,特别是增加新的一些特征。比如对连续变量做进一步分类提取更多的数据,还有ML里面他们大多数都是用xgd,这个模型我不常用,没有想到。
https://philpapers.org/archive/BARPTS-6
这篇论文提到了神经网络解决。从feasible的角度分析神经网络的训练应该需要大量的数据分析,所以我一开始就没有考虑。



尝试完回来了。
我按照上面notebook的做法把Titanic重新搞了一下,还是只有最高78左右,不知道为什么他们可以做到85等等。我首先是创建了很多的新feature,名字的title,家庭成员的多少,还有等等分级。在另外一篇文章中读到,可以通过热力图里面的相关系数来把缺失值填上,这倒是一个很好的做法,大概提高了0.2左右。通过观察相关系数矩阵来尝试发现哪一些feature之间是有关联的。
最后做的最好的分类器是randomforest,adaboost,xgboost。最后尝试了一下MLP,由于怕过拟合,炮了200次左右


实际上我是炮了2000次,看到在200次左右大概就是最高的了,然后就只训练200次。总的来说尝试之后效果并没有很大的提升,MLP的是77,比ML还要低0.1。MLP那篇是看了kaggle有一个discussion提到的一篇论文后照着做的。
这篇文章是之前提到的:https://philpapers.org/archive/BARPTS-6,感觉有点问题,Carbin丢失了百分之80的值,然后他这里没有处理就直接进去计算了,估计不是同一个数据来的。

10 monkey species

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,635评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,543评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,083评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,640评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,640评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,262评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,833评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,736评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,280评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,369评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,503评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,870评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,340评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,460评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,909评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,512评论 2 359