第一章 |
您所在的位置:网站首页 › 美国人口预测模型结果分析 › 第一章 |
目录
1. 实验目标2. 数据集展示3. 设计系统4. 探索数据5. 代码部分5.1 划分数据集5.2 探索训练集5.3 特征工程5.3.1 数值类型处理5.3.2 文本类型处理5.3.3 sklearn数值流水线处理
5.4 模型部分5.4.1 sklearn模型的保存和加载5.4.2 建立决策树和线性回归模型5.4.3 使用交叉验证评估模型5.4.4 建立随机森林模型5.4.5 建立支持向量机SVR模型5.4.6 网格搜索寻找最佳参数组合
5.5 通过测试集评估系统
6. 建议6.1 启动、监控和维护系统6.2 最后
1. 实验目标
选择加州房价数据集,基于1990年加州人口普查的数据,出于教学的目的,添加了一个分类属性,并且移除了一些特征。 模型需要从这个数据中学习,从而能够根据所有其他指标,预测任意区域的房价中位数 2. 数据集展示 housing.info() # 存在缺失值,将在 5.3 中进行处理 RangeIndex: 20640 entries, 0 to 20639 Data columns (total 10 columns): longitude 20640 non-null float64 # 位置 latitude 20640 non-null float64 # 位置 housing_median_age 20640 non-null float64 # 房龄 total_rooms 20640 non-null float64 # 总房数 total_bedrooms 20433 non-null float64 # 总卧室数 population 20640 non-null float64 # 总人口 households 20640 non-null float64 # 家庭数 median_income 20640 non-null float64 # 收入中位数 median_house_value 20640 non-null float64 # 房价中位数 ocean_proximity 20640 non-null object # 离海距离绘制每个特征的直方图: import matplotlib.pyplot as plt housing.hist(bins=50,figsize=(20,15),edgecolor="black") plt.show()
收入中位数这个属性看起来不像是用美元(USD)在衡量。通过核实,得知数据已经按比例缩小,并框处中位数的上限为15,下限为0.5.在机器学习中,使用经过预处理的属性是很常见的事情,倒不一定是个问题,但是至少要了解数据是如何计算的。 房龄中位数和房价中位数也被设定了上限,而后者正是我们需要的目标属性(标签),这是个问题,因为这样机器学习算法很可能永远也学不到超过这个上限的价格。因此,需要继续和客户进行核实,查看是否存在问题。如果他们说,需要精确的预测值,甚至会超过50w美元。那么,我们通常有两个选择: (1)对被设置了上限的区域,重新收集标签值。 (2)或是将这些区域的数据从训练集中移除(包括从测试中移除,因为如果预测值超过50w,系统表现会比较差) 最后,很多直方图都表现出重尾:图形在中位数右侧会比左侧要远得多。 这可能会导致某些机器学习算法难以检测模式。稍后我们会尝试一些转化方法,将这些属性转化为更偏向于钟形的分布(正态分布的一种) 5. 代码部分 5.1 划分数据集我们可以直接用 sklearn 中的 train_test_split from sklearn.model_selection import train_test_split train_set,test_set=train_test_split(housing,test_size=0.2,random_state=42) print(len(train_set),"train+",len(test_set),"test") >> 16512 train+ 4128 test但是使用上面的方法,对于这样的随机在面对小样本时,容易出现抽样偏差, 因此需要考虑分层抽样 使用StratifiedShuffleSplit,以median_income为依据进行分层抽样,因为median_income图像看起来更规则,符合钟形分布,并且,人群收入特征对于区域房价具有重要意义. housing["median_income"].hist(edgecolor="black")
按收入比例进行分层抽样 # 关于StratifiedShuffleSplit可自行百度 from sklearn.model_selection import StratifiedShuffleSplit # n_splits比例范围内的划分为1组 ss=StratifiedShuffleSplit(n_splits=1,test_size=0.2,random_state=42) for train_index,test_index in ss.split(housing,housing["income_cat"]): strat_train_set=housing.loc[train_index] strat_test_set=housing.loc[test_index] print(len(strat_train_set),len(strat_test_set))验证一下分层抽样后的训练集和测试集结果 strat_train_set["income_cat"].value_counts()/len(strat_train_set) strat_test_set["income_cat"].value_counts()/len(strat_test_set) >> 3 0.350533 2 0.318798 4 0.176357 5 0.114583 1 0.039729 Name: income_cat, dtype: float64删除income_cat属性 strat_train_set.drop('income_cat',axis=1,inplace=True) strat_test_set.drop('income_cat',axis=1,inplace=True) 5.2 探索训练集接下来我们把测试集扔一边,探索一下训练集 housing=strat_train_set.copy() # 带上地图 import matplotlib.image as mpimg california_img=mpimg.imread('./images/end_to_end_project/california.png') ax = housing.plot(kind="scatter", x="longitude", y="latitude", figsize=(10,7), s=housing['population']/100, label="Population", c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=False, alpha=0.4, ) plt.imshow(california_img, extent=[-124.55, -113.80, 32.45, 42.05], alpha=0.5, cmap=plt.get_cmap("jet")) plt.ylabel("Latitude", fontsize=14) plt.xlabel("Longitude", fontsize=14) prices = housing["median_house_value"] tick_values = np.linspace(prices.min(), prices.max(), 11) cbar = plt.colorbar() cbar.ax.set_yticklabels(["$%dk"%(round(v/1000)) for v in tick_values], fontsize=14) cbar.set_label('Median House Value', fontsize=16) plt.legend(fontsize=16) plt.show()
使用 corr() 在数据集上计算目标值对属性间的标准相关系数(也称为皮尔逊相关系数) 越接近1和-1 越相关,0代表没有线性关系 注意:只能测量线性相关性,会遗漏非线性相关性 corr_matrix=housing.corr() # median_house_value 目标值 corr_matrix["median_house_value"].sort_values(ascending=False) >> median_house_value 1.000000 median_income 0.687160 total_rooms 0.135097 housing_median_age 0.114110 households 0.064506 total_bedrooms 0.047689 population -0.026920 longitude -0.047432 latitude -0.142724 Name: median_house_value, dtype: float64以上可以看出: 1. 收入跟房价成正相关,是非常重要的特征 2. households, total_bedrooms,population,longitude这几个特征对于目标值来说,是没有多大相关性的 3. 可以尝试组合新的属性,看看对目标值的影响 根据目前信息创造有更大价值的特征数据 比如: 如果不知道一个地区有多少个家庭,那么知道一个地区的“总房屋数”也没什么。我们真正想知道的是每个家庭的房屋数量。同样,但看“卧室总数”这个属性本身,也没有什么意义,我们可能是想拿它和“房屋总数”来对比,或者每个家庭的人口数这个属性结合也似乎挺有意思。或许还可以组合成每个房屋的卧室数,也是日常生活中,衡量房价的重要指标 # 创造以上所说的三个特征 housing["rooms_per_household"] = housing["total_rooms"]/housing["households"] housing["bedrooms_per_room"] = housing["total_bedrooms"]/housing["total_rooms"] housing["population_per_household"]=housing["population"]/housing["households"] # 查看新特征与目标值的相关系数 corr_matrix = housing.corr() corr_matrix["median_house_value"].sort_values(ascending=False) >> median_house_value 1.000000 median_income 0.687160 rooms_per_household 0.146285 total_rooms 0.135097 housing_median_age 0.114110 households 0.064506 total_bedrooms 0.047689 population_per_household -0.021985 population -0.026920 longitude -0.047432 latitude -0.142724 bedrooms_per_room -0.259984 Name: median_house_value, dtype: float64新的特征,相较于“房屋总数”还是“卧室总数”与房价中位数的相关性都要高得多。显然卧室/房屋比例更低的房屋,往往价格越贵。 同样“每一个家庭的房间数量”也比“房间总数”更具信息量–房屋越大,价格越贵. 分离训练集和目标集 housing_train = strat_train_set.drop('median_house_value',axis=1) housing_label = strat_train_set['median_house_value'].copy() 5.3 特征工程 5.3.1 数值类型处理大部分机器学习算法无法在缺失的特征上工作,所以我们要创建一些函数来辅助它。 前面我们已经注意到total_bedrooms属性有部分值缺失,所以需要解决。 有以下三种选择: 放弃这些相应的地区放弃这个属性将缺失值设置为某个值方法一,删除空值对应的信息 sample_incomplete_rows.dropna(subset=["total_bedrooms"])方法二,放弃该属性 sample_incomplete_rows.drop("total_bedrooms", axis=1)方法三,用中位数填充 median = housing["total_bedrooms"].median() sample_incomplete_rows["total_bedrooms"].fillna(median, inplace=True)这里 我们选择方法三 使用sklearn中的SimpleImputer函数来实现,该函数只能处理纯数值型的dataframe # 删除文本型的特征 housing_num = housing_train.drop('ocean_proximity', axis=1) imputer = SimpleImputer(strategy='median') # 这里fit属于估算器,transform属于转换器,fit_transform是两者结合,进行优化后的 X = imputer.fit_transform(housing_num) housing_tr = pd.DataFrame(X,columns=housing_num.columns,index=housing_num.index) 5.3.2 文本类型处理文本属性 分类属性 这里有个注意点,下方代码的取值: 1.如果是[],那么housing_cat.value是1维的 2.如果是[[ ]] 那么housing_cat.value是2维的 类别转化需要2维d数据,所以使用第2种取值方式 housing_cat = housing_train[['ocean_proximity']]将文本属性进行数字化编码 使用sklearn中的OrdinalEncoder # 处理分类文本属性 from sklearn.preprocessing import OrdinalEncoder ordinal_encoder = OrdinalEncoder() housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat) print(housing_cat_encoded[:10]) # 获取他的索引和类别 for index,cate in enumerate(ordinal_encoder.categories_[0]): print(index,cate)
自定义转换器,添加5.2中的说明的新属性,使用sklearn封装,方便测试集调用流水线处理 from sklearn.base import BaseEstimator,TransformerMixin # rooms_id,bedrooms_id,population_id,households_id = 3,4,5,6 class CombineAttributesAdder(BaseEstimator,TransformerMixin): def __init__(self,add_bedrooms_per_room=True): self.add_bedrooms_per_room = add_bedrooms_per_room def fit(self,X,y=None): return self def transform(self,X): rooms_per_household = X[:,rooms_id]/X[:,households_id] population_per_household = X[:,population_id]/X[:,households_id] if self.add_bedrooms_per_room: bedrooms_per_room = X[:,bedrooms_id]/X[:,rooms_id] return np.c_[X,rooms_per_household,population_per_household,bedrooms_per_room] else: return np.c_[X, rooms_per_household, population_per_household]np.c_ 按行连接,矩阵左右相加,要求行数相同 np.r_ 按列连接,矩阵上下相加,要求列数相同 attr_adder = CombineAttributesAdder(add_bedrooms_per_room=False) housing_extra_attr = attr_adder.transform(housing_train.values)让我们来查看一下属性是否添加成功 经过以上实验,我们可以综合使用流水线进行处理,简化代码,一目了然 sklearn 流水线 pipline Pipline 构造函数会定义步骤的顺序,除了最后一个可以是估算器之外,其他的必须是转换器,也就是说必须有fit_transform方法 from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler num_pipline = Pipeline([ ('imputer',SimpleImputer(strategy='median')), # 使用中位数补充缺失值 ('attr_adder',CombineAttributesAdder()), # 添加额外的数值属性 ('std_scaler',StandardScaler()) # 将数值属性标准化 ])流水线的方法与最终估算器的方法相同,因此这个流水线有transform方法 housing_num_str = num_pipline.fit_transform(housing_num)查看一下是否按照我们代码正确执行 这里处理的是housing_num,剩下0-7共8个属性,而CombineAttributesAdder默认add_bedrooms_per_room=True,即添加之前提到过的三个新属性,所以,数值型的数据会出现0-10共11个属性,并且会进行按照流水线进行标准化 查看结果 housing_prepare = full_pipeline.fit_transform(housing_train) print(pd.DataFrame(housing_prepare))
回归模型的评价指标: mean_absolute_error:平均绝对误差(Mean Absolute Error,MAE),用于评估预测结果和真实数据集的接近程度的程度,其值越小说明拟合效果越好。 mean_squared_error:均方差(Mean squared error,MSE),该指标计算的是拟合数据和原始数据对应样本点的误差的平方和的均值,其值越小说明拟合效果越好。 关于评价回归模型的这些详解可查: https://www.jianshu.com/p/9ee85fdad150 下面建立模型并训练: 线性回归模型 from sklearn.linear_model import LinearRegression lin_reg = LinearRegression() lin_reg.fit(housing_prepare,housing_label) 决策树模型 from sklearn.tree import DecisionTreeRegressor tree_reg = DecisionTreeRegressor() tree_reg.fit(housing_prepare, housing_label)模型预测代码: def lin_predict(): some_data = housing_train.iloc[:5] #取出训练集的前5行 some_lable = housing_label.iloc[:5] some_data_prepare = full_pipeline.transform(some_data) # 前面fit过了,不需要再加fit some_data_prediction = lin_reg.predict(some_data_prepare) print(some_data_prediction) print(some_lable.values)到这一步,预测房价功能基本实现,效果还可以~~~~~ 接下来调整模型,计算RMSE,尽可能找到更好的模型 计算两个模型的RMSE: def lin_ca(): #68305.637 housing_prediction = lin_reg.predict(housing_prepare) lin_mse = mean_squared_error(housing_label,housing_prediction) lin_rmse = np.sqrt(lin_mse) print(lin_rmse) def tree_ca(): tree_prediction = tree_reg.predict(housing_prepare) tree_mse = mean_squared_error(housing_label,tree_prediction) tree_rmse = np.sqrt(tree_mse) print(tree_rmse)
接下来,我们尝试随机森林模型,以后会介绍随机森林的工作原理,通过对特征的随机子集进行多个决策树的训练,然后对其预测取平均值。 在多个模型的基础上建立模型,称为集成算法,这是进一步推动机器学习算法的好方法。 def random_forest(): # 随机森林 对特征的随机子集进行许多决策树的训练,是在多个模型基础之上建立的模型,称为集成学习 from sklearn.ensemble import RandomForestRegressor forest_reg = RandomForestRegressor() forest_reg.fit(housing_prepare,housing_label) forest_prediction = forest_reg.predict(housing_prepare) forest_mse = mean_squared_error(housing_label,forest_prediction) forest_rmse = np.sqrt(forest_mse) print(forest_rmse) forest_score = cross_val_score(forest_reg,housing_prepare,housing_label,scoring='neg_mean_squared_error',cv=10) forest_rmse_scores = np.sqrt(-forest_score) display_score(forest_rmse_scores)
这里的RMSE达到了111094,好吧,那我们就不继续尝试了,效果太差. 综上所述,我们找到了效果最好的就是随机森林的模型 5.4.6 网格搜索寻找最佳参数组合要了解随机森林模型的几个重要超参数 n_estimators 集成的估算器数量 max_depth 最大树深度 n_jobs 使用多少cpu训练 max_features 划分时考虑的最大特征数 等等等等,这里可以自行查看手册 from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestRegressor param_grid = [ {'n_estimators':[3,10,30],'max_features':[2,4,6,8]}, {'bootstrap':[False],'n_estimators':[3,10],'max_features':[2,3,4]} ] # 先是3*4=12 + 2*3=6 18*5=90次计算 forest_reg = RandomForestRegressor() # refit=True 一旦找到了最佳估算器,那么就会在整个训练集上重新训练 grid_sear = GridSearchCV(forest_reg,param_grid=param_grid,cv=5,scoring='neg_mean_squared_error', return_train_score = True) grid_sear.fit(housing_prepare,housing_label) # 最佳参数组合 print(grid_sear.best_params_) # 最好的估算器 对应的参数 print(grid_sear.best_estimator_) # 显示测试平均得分和对应的使用参数 cvres = grid_sear.cv_results_ for mean_score,params in zip(cvres["mean_test_score"],cvres["params"]): print(np.sqrt(-mean_score),params) # 分析每个属性(列)的重要程度 feature_importances = grid_sear.best_estimator_.feature_importances_ # print(feature_importances) # 添加列名 extra_attribs = ['rooms_per_hhold','pop_per_hhold','bedrooms_per_room'] cat_encoder_ = full_pipeline.named_transformers_["cat"] cat_one_hot_attribs = list(cat_encoder_.categories_[0]) # [' |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |