使用housing.csv 训练数据git
$$RESM = \sqrt{ \frac{1}{m}\sum_{i=1}^m(y'-y)^2}$$
$$MAE = \sqrt{ \frac{1}{m}\sum_{i=1}^m|y'-y|}$$算法
MSE 和 MAE 都是测量预测值和目标值两个向量距离的方法。有多种测量距离的方法,或范数,更通常的K 阶闵氏范数写成。p=0时,ℓ0(汉明范数)只显示了这个向量的基数(即,非零元素的个数),p趋于无穷时,ℓ∞(切比雪夫范数)是向量中最大的绝对值。
$$RESM = \sqrt{ \frac{1}{m}\sum_{i=1}^m|y'-y|^p}$$数组
import numpy as np def split_train_test(data, test_ratio): shuffled_indices = np.random.permutation(len(data)) test_set_size = int(len(data) * test_ratio) # 拆分比例 test_indices = shuffled_indices[:test_set_size] train_indices = shuffled_indices[test_set_size:] return data.iloc[train_indices], data.iloc[test_indices] train_set, test_set = split_train_test(housing, 0.2) # housing数据二八拆分
这个方法可行,可是并不完美:若是再次运行程序,就会产生一个不一样的测试集。屡次运行以后,你(或你的机器学习算法)就会获得整个数据集,这是须要避免的。安全
一个一般的解决办法是使用每一个实例的识别码,以断定是否这个实例是否应该放入测试集(假设实例有单一且不变的识别码)。例如,你能够计算出每一个实例识别码的哈希值,只保留其最后一个字节,若是值小于等于 51(约为 256 的 20%),就将其放入测试集。这样能够保证在屡次运行中,测试集保持不变,即便更新了数据集。新的测试集会包含新实例中的 20%,但不会有以前位于训练集的实例。下面是一种可用的方法:app
import hashlib # 参数identifier为单一且不变的识别码,能够为索引id # hash(np.int64(identifier)).digest()[-1]返回识别码的哈希摘要值的最后一个字节 def test_set_check(identifier, test_ratio, hash): return hash(np.int64(identifier)).digest()[-1] < 256 * test_ratio # 记录知足条件的索引 def split_train_test_by_id(data, test_ratio, id_column, hash=hashlib.md5): ids = data[id_column] in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio, hash)) return data.loc[~in_test_set], data.loc[in_test_set]
不过,房产数据集没有识别码这一列。最简单的方法是使用行索引做为 ID:dom
housing_with_id = housing.reset_index() # 增长一个索引列,放在数据的第一列 train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")
若是使用行索引做为惟一识别码,你须要保证新数据放到现有数据的尾部,且没有行被深处。若是作不到,则能够用最稳定的特征来建立惟一识别码。例如,一个区的维度和经度在几百万年以内是不变的,因此能够将二者结合成一个 ID。机器学习
若是你想简单地拆分数据作预测模型示例,使用split_train_test进行拆分便可。Scikit-Learn 提供了一些函数,能够用多种方式将数据集分割成多个子集。最简单的函数是train_test_split,它的做用和以前的函数split_train_test很像,并带有其它一些功能。首先,它有一个random_state参数,能够设定前面讲过的随机生成器种子;第二,你能够将种子传递到多个行数相同的数据集,能够在相同的索引上分割数据集(这个功能很是有用,好比你有另外一个DataFrame做为标签):ide
from sklearn.model_selection import train_test_split train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
另一种拆分形式:分层采样
将人群分红均匀的子分组,称为分层,从每一个分层去除合适数量的实例,以保证测试集对总人数有表明性。例如,美国人口的 51.3% 是女性,48.7% 是男性。因此在美国,严谨的调查须要保证样本也是这个比例:513 名女性,487 名男性做为数据样本。函数
数据集中的每一个分层都要有足够的实例位于你的数据中,这点很重要。不然,对分层重要性的评估就会有误差。这意味着,你不能有过多的分层,且每一个分层都要足够大。后面的代码经过将收入中位数除以 1.5(以限制收入分类的数量),建立了一个收入类别属性,用ceil对值舍入(以产生离散的分类),而后将全部大于 5的分类纳入到分类5 :性能
# 预处理,建立"income_cat"属性 # 凡是会对原数组做出修改并返回一个新数组的,每每都有一个 inplace可选参数 # inplace=True,原数组名对应的内存值直接改变;inplace=False,原数组名对应的内存值并不改变,新的结果赋给一个新的数组. housing["income_cat"] = np.ceil(housing["median_income"] / 1.5) housing["income_cat"].where(housing["income_cat"] < 5, 5.0, inplace=True) # 如今,就能够根据收入分类,进行分层采样。你可使用 Scikit-Learn 的StratifiedShuffleSplit类 from sklearn.model_selection import StratifiedShuffleSplit # random_state为随机种子生成器,能够获得相同的随机结果 # n_splits是将训练数据分红train/test对的组数,这里汇总成一组数据 split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42) 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] # 如今,你须要删除income_cat属性,使数据回到初始状态: for set in (strat_train_set, strat_test_set): set.drop(["income_cat"], axis=1, inplace=True)
可视化数据寻找规律:
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4, s=housing["population"]/100, label="population", c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True, ) plt.legend()
每一个圈的半径表示街区的人口(选项s),颜色表明价格(选项c)。咱们用预先定义的名为jet的颜色图(选项cmap),它的范围是从蓝色(低价)到红色(高价):
这张图说明房价和位置(好比,靠海)和人口密度联系密切。你还能够很容易地使用corr()方法计算出每对属性间的标准相关系数(也称做皮尔逊相关系数):
corr_matrix = housing.corr()
如今来看下每一个属性和房价中位数的关联度:
>>> corr_matrix["median_house_value"].sort_values(ascending=False) median_house_value 1.000000 median_income 0.687170 total_rooms 0.135231 housing_median_age 0.114220 households 0.064702 total_bedrooms 0.047865 population -0.026699 longitude -0.047279 latitude -0.142826 Name: median_house_value, dtype: float64
相关系数的范围是 -1 到 1。当接近 1 时,意味强正相关;例如,当收入中位数增长时,房价中位数也会增长。当相关系数接近 -1 时,意味强负相关;你能够看到,纬度和房价中位数有轻微的负相关性(即,越往北,房价越可能下降)。最后,相关系数接近 0,意味没有线性相关性。(没有直接的线性关系,不是没有关系)
另外一种检测属性间相关系数的方法是使用 Pandas 的scatter_matrix函数,它能画出每一个数值属性对每一个其它数值属性的图。由于如今共有 11 个数值属性,你能够获得11 ** 2 = 121张图。
from pandas.tools.plotting import scatter_matrix attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"] scatter_matrix(housing[attributes], figsize=(12, 8))
获得两个属性的散点图
大多机器学习算法不能处理特征丢失,所以先建立一些函数来处理特征丢失的问题。前面,你应该注意到了属性total_bedrooms有一些缺失值。有三个解决选项:
用DataFrame的dropna(),drop(),和fillna()方法,能够方便地实现:
housing.dropna(subset=["total_bedrooms"]) # 选项1 housing.drop("total_bedrooms", axis=1) # 选项2 axis=0对行操做,axis=1对列操做 median = housing["total_bedrooms"].median() housing["total_bedrooms"].fillna(median) # 选项3
若是选择选项 3,你须要计算训练集的中位数,用中位数填充训练集的缺失值,不要忘记保存该中位数。后面用测试集评估系统时,须要替换测试集中的缺失值,也能够用来实时替换新数据中的缺失值。
Scikit-Learn 提供了一个方便的类来处理缺失值:Imputer。下面是其使用方法:首先,须要建立一个Imputer实例,指定用该属性的中位数替换它的每一个缺失值:
from sklearn.preprocessing import Imputer imputer = Imputer(strategy="median") # 进行中位数赋值
由于只有数值属性才能算出中位数,咱们须要建立一份不包括文本属性ocean_proximity的数据副本:
housing_num = housing.drop("ocean_proximity", axis=1) # 去除ocean_proximity不为数值属性的特征
如今,就能够用fit()方法将imputer实例拟合到训练数据:
imputer.fit(housing_num)
imputer计算出了每一个属性的中位数,并将结果保存在了实例变量statistics_中。只有属性total_bedrooms有缺失值,可是咱们确保一旦系统运行起来,新的数据中没有缺失值,因此安全的作法是将imputer应用到每一个数值:
>>> imputer.statistics_ # 实例变量statistics_和housing_num数值数据获得的中位数是同样的 array([ -118.51 , 34.26 , 29. , 2119. , 433. , 1164. , 408. , 3.5414]) >>> housing_num.median().values array([ -118.51 , 34.26 , 29. , 2119. , 433. , 1164. , 408. , 3.5414])
如今,你就可使用这个“训练过的”imputer来对训练集进行转换,经过将缺失值替换为中位数:
X = imputer.transform(housing_num)
结果是一个普通的 Numpy 数组,包含有转换后的特征。若是你想将其放回到 PandasDataFrame中,也很简单:
housing_tr = pd.DataFrame(X, columns=housing_num.columns) # 获得处理缺失值后的DF数据
前面,咱们丢弃了类别属性ocean_proximity,由于它是一个文本属性,不能计算出中位数。大多数机器学习算法跟喜欢和数字打交道,因此让咱们把这些文本标签转换为数字。Scikit-Learn 为这个任务提供了一个转换器LabelEncoder:
# 简单来讲 LabelEncoder 是对不连续的数字或者文本进行编号 # le.fit([1,5,67,100]) # le.transform([1,1,100,67,5]) # 输出: array([0,0,3,2,1]) >>> from sklearn.preprocessing import LabelEncoder >>> encoder = LabelEncoder() >>> housing_cat = housing["ocean_proximity"] >>> housing_cat_encoded = encoder.fit_transform(housing_cat) >>> housing_cat_encoded array([1, 1, 4, ..., 1, 0, 3])
处理离散特征这还不够,Scikit-Learn 提供了一个编码器OneHotEncoder,用于将整书分类值转变为独热向量。注意fit_transform()用于 2D 数组,而housing_cat_encoded是一个 1D 数组,因此须要将其变形:
>>> from sklearn.preprocessing import OneHotEncoder >>> encoder = OneHotEncoder() >>> housing_cat_1hot = encoder.fit_transform(housing_cat_encoded.reshape(-1,1)) >>> housing_cat_1hot <16513x5 sparse matrix of type '<class 'numpy.float64'>' with 16513 stored elements in Compressed Sparse Row format>
注意输出结果是一个 SciPy 稀疏矩阵,而不是 NumPy 数组。当类别属性有数千个分类时,这样很是有用。通过独热编码,咱们获得了一个有数千列的矩阵,这个矩阵每行只有一个 1,其他都是 0。使用大量内存来存储这些 0 很是浪费,因此稀疏矩阵只存储非零元素的位置。你能够像一个 2D 数据那样进行使用,可是若是你真的想将其转变成一个(密集的)NumPy 数组,只需调用toarray()方法:
>>> housing_cat_1hot.toarray() array([[ 0., 1., 0., 0., 0.], [ 0., 1., 0., 0., 0.], [ 0., 0., 0., 0., 1.], ..., [ 0., 1., 0., 0., 0.], [ 1., 0., 0., 0., 0.], [ 0., 0., 0., 1., 0.]])
使用类LabelBinarizer,咱们能够用一步执行这两个转换(从文本分类到整数分类,再从整数分类到独热向量):
>>> from sklearn.preprocessing import LabelBinarizer >>> encoder = LabelBinarizer() >>> housing_cat_1hot = encoder.fit_transform(housing_cat) >>> housing_cat_1hot array([[0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1], ..., [0, 1, 0, 0, 0], [1, 0, 0, 0, 0], [0, 0, 0, 1, 0]])
注意默认返回的结果是一个密集 NumPy 数组。向构造器LabelBinarizer传递sparse_output=True,就能够获得一个稀疏矩阵。