房价预测模型

房价预测模型,第1张

目录

1.模型目标

预测某一区域的房价中位数

2.选择框架

有监督学习任务:训练集中的每个实例都有标签(该区域的房价中位数)
回归任务:因为你要对某个值进行预测。更具体地说,这是一个多重回归问题,因为系统要使用多个特征进行预测(使用区域的人口、收入中位数等)。这也是一元回归问题,因为我们仅尝试预测每个区域的单个值。
简单的批量学习应该就能胜任:我们没有一个连续的数据流不断流进系统,所以不需要针对变化的数据做出特别调整。批量学习:当系统投入生产环境后,学习就停止。在线学习:系统发布了,还需要根据输入的训练数据逐步累积学习成果。

3.选择性能指标 均方根误差(RMSE)(Root Mean Square Error) --- 回归问题的首选性能指标

m表示数据集中的实例数
是数据集中第i个实例的所有特征值的向量,是其标签(该实例的期望输出值)
h是系统的预测函数,也称为假设。当给系统输入一个实例的特征向量时,它会为该实例输出一个预测值=h()
X是一个矩阵,包含数据集中所有实例的所有特征值。每个实例占一行,第 i 行等于的转置
RMSE(X,h)表示使用预测函数h在一组示例中测量的成本函数

平均绝对误差(MAE)(Mean Absolute Error)

我们将包含n个元素的向量v的范数定义为

RMSE对应范数,又称欧几里得范数,其实就是几何空间中两点的距离
MAE对应范数,又称曼哈顿范数。如果你只能沿着正交的城市街区行动,它能够测量城市中两点之间的距离。
范数指标越高,它越关注大值而忽略小值。这就是RMSE对异常值比MAE更敏感的原因。

4.获取数据
import os
import tarfile
import urllib.request
import pandas as pd

DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml2/master/"
HOUSING_PATH = os.path.join("datasets", "housing")
HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/housing.tgz"

def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
    if not os.path.isdir(housing_path):
        os.makedirs(housing_path)
    tgz_path = os.path.join(housing_path, "housing.tgz")
    urllib.request.urlretrieve(housing_url, tgz_path) # 将远程数据下载到本地
    housing_tgz = tarfile.open(tgz_path)
    housing_tgz.extractall(path=housing_path) # 解压文件
    housing_tgz.close()

def load_housing_data(housing_path=HOUSING_PATH):
    csv_path = os.path.join(housing_path, "housing.csv")
    return pd.read_csv(csv_path)

fetch_housing_data() # 下载房价数据
housing = load_housing_data() # 获取数据对象,返回一个包含所有数据的pandas DataFrame对象
5.分析数据

①返回dataframe最上面的五行,可以发现共有十列 

housing.head()

②快速获取数据集的简单描述,特别是总行数、每个属性的类型和非空值的数量 

housing.info()

数据集中包含20640个实例
total_bedrooms这个属性只有20433个非空值,这意味着有207个区域缺失这个特征

所有属性的字段都是数字,除了ocean_proximity,它的类型是object,查看一下有多少种分类存在

 housing["ocean_proximity"].value_counts()

③通过describe()方法可以显示数值属性的描述

std行显示的是标准差(用来测量数值的离散程度)
关于25%、50%和75%,以housing_median_age为例,它的含义是:25%的值小于18,50%的值小于29,75%的值小于37

④绘制每个数值属性的直方图

%matplotlib inline # 指定Jupyter作为Matplotlib的后端
import matplotlib.pyplot as plt
# x轴数据集中房价的值,纵轴表示这个值出现的次数
# bins表示直方图的“柱”的个数,每个“柱”的值=该“柱”跨越的所有x对应的y值之和
housing.hist(bins=50, figsize=(20,15))
plt.show()

收入中位数为万美元
许多直方图都表现出重尾分布(Heavy-tailed distribution):图形在中位数右侧的延伸比左侧要远得多。这可能会导致某些机器学习算法难以检测模式。
稍后我们会尝试一些转化方法,将这些属性转化为更偏向钟形的分布

6.创建测试集

随机选择一些实例,通常是数据集的20%

纯随机抽样 

from sklearn.model_selection import train_test_split

train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

分层抽样
随机抽样很可能带来误差,假设我们要对一个地区的人进行抽样调查,该地区的男女比例是3:1,那么抽取的样本应该保持这一比例。
同理,如果我们被告知:预测房价中位数,收入中位数是一个非常重要的属性
我们先看看收入中位数的直方图:大多数的值聚集在1.5~6左右

创建5个收入类别属性income_cat(用1~5来做标签),0~1.5是类别1,1.5~3是类别2,以此类推

import numpy as np
housing["income_cat"] = pd.cut(housing["median_income"],bins=[0., 1.5, 3.0, 4.5, 6., np.inf], labels=[1, 2, 3, 4, 5])
housing["income_cat"].hist()

根据收入类别进行分层抽样

from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42) # n_splits表示生成一组train/test对
# split(housing,housing["income_cat"])表示按照housing["income_cat"]各种值的比例在housing中抽取样本,样本的形式为数组下标
for train_index, test_index in split.split(housing, housing["income_cat"]):
    strat_train_set = housing.loc[train_index]
    strat_test_set = housing.loc[test_index]

检验:查看原数据集、随机test集、分层test集根据income_cat分类情况,并计算误差

def income_cat_proportions(data):
    return data["income_cat"].value_counts() / len(data)

train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

compare_props = pd.DataFrame({
    "Overall": income_cat_proportions(housing),
    "Stratified": income_cat_proportions(strat_test_set),
    "Random": income_cat_proportions(test_set),
}).sort_index()
compare_props["Rand. %error"] = 100 * compare_props["Random"] / compare_props["Overall"] - 100
compare_props["Strat. %error"] = 100 * compare_props["Stratified"] / compare_props["Overall"] - 100

compare_props

删除income_cat属性

for set_ in (strat_train_set, strat_test_set):
    set_.drop("income_cat", axis=1, inplace=True)
7.可视化数据探索

首先,把测试集放在一边,你能探索的只有训练集。如果训练集非常庞大,你可以抽样一个探索集。

explore_set = strat_train_set.copy()

①由于存在地理位置信息(经度和纬度),因此建立一个各区域的分布图来看看

# 通过调整alpha,可以更清楚地看出高密度数据点的位置
explore_set.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)

②现在,再来看看房价

# 每个圆的半径大小代表了每个区域的人口数量(选项s)
# 颜色代表价格(选项c),颜色范围从蓝(低)到红(高)
explore_set.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4,
             s=explore_set["population"]/100, label="population", figsize=(10,7),
             c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True,
             sharex=False)

③寻找相关性

可以使用corr()方法计算每对属性之间的标准相关系数(也称为皮尔逊相关系数)

# 看看每个属性与房价中位数的相关性分别是多少
# 相关系数的范围从-1变化到1。越接近1,表示有越强的正相关
# 系数靠近0则说明二者之间没有线性相关性
corr_matrix = explore_set.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)

由此可见,收入中位数median_income确实和房价关系很大

④试验不同属性的组合

如果你不知道一个区域有多少个家庭,单看total_rooms(房间总数)、total_bedrooms(卧室总数)似乎没啥意义,所以我们可以考虑组合新的属性

explore_set["rooms_per_household"] = explore_set["total_rooms"]/housing["households"] # 平均每户人家拥有的房间数目
explore_set["bedrooms_per_room"] = explore_set["total_bedrooms"]/housing["total_rooms"] # 房间总数和卧室总数的比例
explore_set["population_per_household"]=explore_set["population"]/housing["households"] # 平均每户人家有几口人
# 查看相关矩阵
corr_matrix = explore_set.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)

8.数据准备

先准备一份训练数据和对应的标签 

train_data = strat_train_set.drop("median_house_value", axis=1) # 去掉"median_house_value"这一列作为训练集
train_labels = strat_train_set["median_house_value"].copy() # "median_house_value
①数据清理

前面我们已经注意到total_bedrooms属性有部分值缺失,所以我们要解决它。我们有三种选择:
1.放弃缺失属性所在的整个实例
2.放弃整个属性
3.将缺失的值设置为某个值(0、平均数或者中位数等)

train_data.dropna(subset=["total_bedrooms"])    # option 1
train_data.drop("total_bedrooms", axis=1)       # option 2
median = train_data["total_bedrooms"].median()  # option 3
train_data["total_bedrooms"].fillna(median, inplace=True)

Scikit-Learn提供了一个SimpleImputer类来处理缺失值。

from sklearn.impute import SimpleImputer
# 创建一个SimpleImputer实例,指定你要用属性的中位数值替换该属性的缺失值
imputer = SimpleImputer(strategy="median")
# 由于中位数值只能在数值属性上计算,所以我们需要创建一个没有文本属性ocean_proximity的数据副本
train_data_num = train_data.drop("ocean_proximity", axis=1)
# 使用fit()方法将imputer实例适配到训练数据
# imputer仅仅只是计算了每个属性的中位数值,并将结果存储在其实例变量statistics_中
# 也就是说,imputer.statistics_和train_data_num.median().values的值是一样的
imputer.fit(train_data_num)
# 利用imputer将缺失值替换成中位数值
X = imputer.transform(train_data_num)
# 结果是一个NumPy数组,转换为pandasDataFrame
housing_tr = pd.DataFrame(X, columns=train_data_num.columns, index=train_data_num.index)
②处理文本和分类属性

在此数据集中,ocean_proximity是文本属性,但它不是任意文本,它的取值是有限的,每个值代表一个类别,因此,此属性是分类属性。

train_data_cat = train_data[["ocean_proximity"]]
train_data_cat.head(10)

使用Scikit-Learn的OrdinalEncoder类,将这些类别从文本转到数字。

from sklearn.preprocessing import OrdinalEncoder

ordinal_encoder = OrdinalEncoder()
train_data_cat_encoded = ordinal_encoder.fit_transform(train_data_cat)
train_data_cat_encoded[:10]
# 可以使用Categories_实例变量获取类别列表
ordinal_encoder.categories_

整数编码的一个问题是,机器学习算法会认为两个相近的值比两个离得较远的值更为相似一些。
在某些情况下这是对的(对一些有序类别,像“坏”“平均”“好”“优秀”)
但是,对ocean_proximity而言并非如此(例如,类别0和类别4就比类别0和类别1的相似度更高)
解决:one-hot编码

from sklearn.preprocessing import OneHotEncoder

cat_encoder = OneHotEncoder()
# 这里返回的是一个SciPy稀疏矩阵,该稀疏矩阵仅存储非零元素,因为0太多了,占内存
train_data_cat_1hot = cat_encoder.fit_transform(train_data_cat)
# 你也可以将它转为numpy矩阵
train_data_cat_1hot.toarray()

当有很多这样的类别属性需要转换,one-hot编码会导致输入特征很多,会减慢训练并降低性能。
所以,你也可以考虑用相关的数字特征代替类别输入。
例如,你可以用与海洋的距离来替换ocean_proximity特征。

虽然Scikit-Learn提供了许多有用的转换器,但是你仍然需要为一些诸如自定义清理 *** 作或组合特定属性等任务编写自己的转换器。这里限于篇幅就不展开了。

9.选择和训练模型

10.微调模型

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/870636.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存