zoukankan      html  css  js  c++  java
  • 机器学习一个完整的项目过程

    准备数据

    训练集和测试集的数据来源于很多地方,比如:数据库,csv文件或者其他存储数据的方式,为了操作的简便性,可以写一些小的脚本来下载并解析这些数据。在本文中,我们先写一个脚本来演示:

    import os
    import tarfile
    from six.moves import urllib
    
    DOWNLOAD_ROOT = 'https://raw.githubusercontent.com/ageron/handson-ml/master/'
    HOUSING_PATH = 'chapter02/datasets/housing'
    HOUSING_URL = DOWNLOAD_ROOT + 'datasets/housing' + '/housing.tgz'
    
    
    def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
        print(housing_url)
        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)
        print(tgz_path)
        housing_tgz = tarfile.open(tgz_path)
        housing_tgz.extractall(path=housing_path)
        housing_tgz.close()
        
    
    fetch_housing_data()
    

    执行上边的代码后,数据就已经下载到本地了,接下来在使用pandas加载数据

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

    数据预览

    使用pandas解析后的数据是DataFrames格式,我们可以调用变量的head()方法,获取默认的前5条数据

    可以看出,总共有10条属性,在这5条中,显示数据都很完整,没有发现数值有空的情况,使用info(),我们可以对整个数据的信息进行预览:

    一共有20640条数据,这点数据对于ML来说是很小的,只有total_bedrooms的属性下存在数据为空的情况。

    通过观察数据,我们发现,除了ocean_proximity之外的属性的值都是数值类型,数值类型很容易在ML算法中实现,再次观察上边5条数据的ocean_proximity值,可以推断出ocean_proximity应该存在几种类型,跟枚举有点像,使用value_counts()方法可以查看每个值得数量:

    除此之外,使用describe()可以查看每一行更多的信息:

    名词解释:

    名称 解释
    count 数量
    mean 均值
    min 最小值
    max 最大值
    std 标准差
    25%/50%.75% 低于该值所占的比例

    如果想查看每个属性更加详细的信息,我们可以使用hist()方法,查看每个属性的矩形图:

    %matplotlib inline 
    import matplotlib.pyplot as plt
    housing.hist(bins=50, figsize=(20, 15))
    plt.show()
    

    通过观察矩形图可以很容易的看出值的分布情况,矩形图的x轴表示值,y轴表示数量。针对我们这份数据,我们发现了如下信息:

    • 对于median_income来说,它的值并不是表示的是真实的收入,而是通过计算的结果,取值范围在0.5~15之间,明白数值是如何计算的,也很重要。
    • 数据受限的情况,housing_median_age和median_house_value存在明显的值得限制,在他们的矩形图的右边有一条很长的条,这说明存在限制的情况,这会对ML算法产生一定的影响,比如,在使用算法预测的时候,是否需要也添加该限制?如果答案是不限制,需要对当前受限制的数据做进一步的处理:
      • 收集受限制的数据的真实值
      • 删除这些受限制的数据
    • 这些属性的取值范围有很大的区别,这个会在下文中解决这个问题
    • 图形中有存在尾重的现象,这个也会在下文中解决

    创建test集

    在创建test set的过程中, 能够进一步让我们了解数据,这对选择机器学习算法很有帮助。最简单的就是随机收取大约20%的数据作为test set。

    使用随机函数的缺点是,每次运行程序得到的结果都不一样,因此,为处理这个问题,我们需要给每一行一个唯一的identifier,然后对identifier进行hash化,取它的最后一个字节值小于或等于51(20%)就可以了。

    在原有的数据中,并不存在这样的identifier,因此需要调用reset_index()函数,为每行添加索引,作为identifier。

    import hashlib
    import numpy as np
    
    
    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]
    
    # 给housing添加index
    housing_with_id = housing.reset_index()
    train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")
    print(len(train_set), 'train +', len(test_set), "test")
    
    # 也可以使用这种方式来创建id
    # housing_with_id["id"] = housing["longitude"] * 1000 + housing["latitude"]
    # train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "id")
    

    在上边的代码中,使用index作为identifier有一个缺点,需要把新的数据拼接到数据整体的最后边,同时不能删除中间的数据,解决的方法是,使用其他属性的组合来计算identifier。

    当然sklearn也提供了生成test set的方法

    from sklearn.model_selection import train_test_split
    train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
    

    随机抽样比较适用于数据量大的样本,如果样本不够大,就会引入很大的抽样偏差。对于当前的数据,我们采取分层抽样。当你询问专家那个属性最重要的时候,他回答说median_income最重要,我们就要考虑基于median_income进行分层抽样。

    观察上图,可以发现,median_income的值主要集中在几个层次上,由于层次不够多,这也侧面说明了不太适合使用随机抽样。

    我们为数据新增一个属性,用于标记每行数据属于哪个层次。对于大于5.0的,都归到5.0中。

    # 随机抽样会在某些情况下存在偏差,这时候可以考虑分层抽样,每层的实例个数不能太少,分层不能太多
    housing["income_cat"] = np.ceil(housing["median_income"] / 1.5)
    housing["income_cat"].where(housing["income_cat"] < 5, 5.0, inplace=True)
    print(housing.head(10))
    

    接下来就需要根据income_cat,使用sklearn对数据进行分层抽样。

    # 使用sklearn的tratifiedShuffleSplit类进行分层抽样
    from sklearn.model_selection import StratifiedShuffleSplit
    
    
    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]
        
    print(housing["income_cat"].value_counts() / len(housing))
    
    # 得到训练集和测试集后删除income_cat
    for s in (strat_train_set, strat_test_set):
        s.drop(["income_cat"], axis=1, inplace=True)
        
    print(strat_train_set.head(10))
    

    上边的代码在抽样成功后,删除了income_cat属性,结果如下:

    如果我们计算test set和原数据的误差,能够得到下边这张表格,可以看出,分层抽样的错误明显小于随机抽样。

    发现数据的更多信息

    要想找到数据中隐藏的信息,就要使用可视化的手段,对于我们的housing数据来说,它包含经纬度信息,基于地理位置应该是一个好的切入口。

    housing = strat_train_set.copy()
    housing.plot(kind="scatter", x="longitude", y="latitude", figsize=(20, 12))
    
    

    这张图如果绘制成这样的,很难发现有什么特点,我们调整点的透明度试一试。

    housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1, figsize=(20, 12))
    
    

    这样我们的头脑自动分析后,很容易得出数据浓度高的地方存在特殊性,那么这些是否与价格相关?更进一步,我们用点的半径表示相应点的人口规模,用颜色表示价格,然后绘图:

    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, figsize=(20, 12))
    plt.legend()
    
    

    从这张图,可以观察到,价格跟位置和人口密度有很大的关系,和ocean_proximity同样有关系,因此,从直觉上,我们可以考虑使用聚类算法。

    相关性

    由于当前的数据集比较小,我们可以直接用corr()计算标准相关系数:

    # 标准相关系数, 查看feature的关联度
    corr_matrix = housing.corr()
    corr_matrix["median_house_value"].sort_values(ascending=False)
    
    

    为什么要了解相关性? 相关性只能查看属性之间的线性关系,取值范围是-1~1,越靠近1越正相关,越靠近-1表示越负相关,靠近0表示不相关。

    上图中显示了1, 0, -1各种情况下的图形的样子。另一种直观的方式是使用用pandas的scatter_matrix函数绘图:

    from pandas.plotting import scatter_matrix
    attributes = ["median_house_value", "median_income", "total_rooms",
                      "housing_median_age"]
    scatter_matrix(housing[attributes], figsize=(12, 8));
    
    

    可以很清楚的看出,median_house_valuemedian_income存在一定的线性关系,也验证了这个属性相对别的属性来说,更加重要。

    我们放大该图:

    housing.plot(kind='scatter', x='median_income', y="median_house_value", alpha=0.2)
    # 图中横线的部分就属于干扰数据,不符合大的趋势
    
    

    如果仔细观察上图,能发现很多细节:

    • 存在上升趋势且点没有分散,说明二者存在强关系
    • 在图形中的点组成的横向显示,$500000是一个价格上线,在下边隐约还存在另外几个“横线”。这不符合线性关系,因此可能是干扰数据,可以考虑移除掉

    属性组合

    在数据中,可能打个属性的用处并不大,但是对这些属性做一些特殊的重组后,会获取到一些有用的信息。

    在我们这个例子中,total_rooms,total_bedrooms单独存在的意义不是很大,但是如果跟populationhouseholds做一些组合后,就会产生新的有意义的属性。

    # 有些属性可能是我们不需要的,在这里,bedrooms的总数,不是我们关心的
    # 因此我们可以使用已有的一些属性生成新的组合属性
    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)
    
    

    bedrooms_per_room比,total_rooms,total_bedrooms的相关性都要高,说明我们做的属性重组起到了作用。

    对数据的操作是一个循序渐进的过程。

    数据清洗

    在清洗数据之前,我们先保存好数据。

    # 分离labels
    housing = strat_train_set.drop("median_house_value", axis=1)
    housing_labels = strat_train_set["median_house_value"].copy()
    
    

    在本文上半部分,我们提到过total_bedrooms有一些值为空的情况,对于这种情况,我们一般会采取以下几种方式“

    • 放弃值为空的整行的数据
    • 放弃该属性
    • 重新赋值

    通常会采取第三种方式,为空的值重新附一个新值,比方说均值。

    sklearn提供了一个Imputer来专门处理这个问题:

    # 机器学习算法不能运行在值缺失的情况,因此需要对值缺失做一些处理
    # 1. 放弃那一行数据 2. 放弃整个属性 3. 给缺失的值重新赋值
    from sklearn.impute import SimpleImputer
    
    
    # 使用中位数作为策略
    imputer = SimpleImputer(strategy="median")
    # 移除不是数值类型的项
    housing_num = housing.drop("ocean_proximity", axis=1)
    # fit只用来计算数据的策略值
    imputer.fit(housing_num)
    print(imputer.statistics_)
    # 转换数据,就是补齐missing value
    X = imputer.transform(housing_num)
    
    

    其中imputer的fit()函数,只是计算了各个属性的均值,并没有做其他额外的事情,这就好比对imputer进行了‘训练’,然后调用transfom()转化数据。

    其中均值如下:

    处理text类型的属性

    在我们这个例子中,ocean_proximity是text类型,需要把它转为数值类型。sklearn提供了LabelEncoder模块来把这些text类型的值转换成数值。

    # 对于不是数值的属性值,sk页提供了转换方法
    from sklearn.preprocessing import LabelEncoder
    
    
    encoder = LabelEncoder()
    housing_cat = housing["ocean_proximity"]
    housing_cat_encoded = encoder.fit_transform(housing_cat)
    print(housing_cat_encoded)
    print(encoder.classes_)
    
    '''
    [3 3 3 ... 1 1 1]
    ['<1H OCEAN' 'INLAND' 'ISLAND' 'NEAR BAY' 'NEAR OCEAN']
    '''
    
    

    但是这么做存在的问题是,在机器学习中,认为相近的数值往往相似性更高,为了解决这个问题,sklearn提供了OneHotEncoder模块,把整数映射为一个只有0和1的向量,只有相对的位置是1,其他都是0:

    # 在上边的例子中有个很大的问题,ml的算法会任务0和1比较接近,但是<1H OCEAN和NEAR OCEAN更相似
    # 为了解决这个问题,需要引入one hot的方式,用所在的位置设为1
    from sklearn.preprocessing import OneHotEncoder
    
    
    encoder = OneHotEncoder()
    housing_cat_1hot = encoder.fit_transform(housing_cat_encoded.reshape(-1, 1))
    print(housing_cat_1hot.toarray())
    
    '''
    [[1. 0. 0. 0. 0.]
     [1. 0. 0. 0. 0.]
     [0. 0. 0. 0. 1.]
     ...
     [0. 1. 0. 0. 0.]
     [1. 0. 0. 0. 0.]
     [0. 0. 0. 1. 0.]]
     '''
    
    

    当然,sklearn还提供了把上边两步合为一步的模块LabelBinarizer:

    # 也可以把label和one hot的步骤合成一个
    from sklearn.preprocessing import LabelBinarizer
    
    
    encoder = LabelBinarizer()
    housing_cat_1hot = encoder.fit_transform(housing_cat)
    print(housing_cat_1hot)
    
    

    自定义Transforms

    尽管sklearn提供了很多有用的transfoms,但是我们还是希望能够自定义一些transforms,而且这些自定义的模块,最好用起来和sklearn提供的一样,很简单,下边的代码实现了一个很简单的数据转换:

    之前:

    # 有些属性可能是我们不需要的,在这里,bedrooms的总数,不是我们关心的
    # 因此我们可以使用已有的一些属性生成新的组合属性
    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)
    
    

    现在:

    # 自定义Transformation
    from sklearn.base import BaseEstimator, TransformerMixin
    
    
    rooms_ix, bedrooms_ix, population_ix, household_ix = 3, 4, 5, 6
    
    
    class CombinedAttributesAdder(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, y=None):
            print("==============")
            rooms_per_household = X[:, rooms_ix] / X[:, household_ix]
            population_per_household = X[:, population_ix] / X[:, household_ix]
            if self.add_bedrooms_per_room:
                bedrooms_per_room = X[:, bedrooms_ix] / X[:, rooms_ix]
                print("aaaa", np.c_[X, rooms_per_household, population_per_household, bedrooms_per_room][0])
                return np.c_[X, rooms_per_household, population_per_household, bedrooms_per_room]
            else:
                return np.c_[X, rooms_per_household, population_per_household]
        
    
    attr_adder = CombinedAttributesAdder()
    housing_extra_attribs = attr_adder.transform(housing.values)
    print(len(housing_extra_attribs[0])) # 在每一行的后边拼接了两个值
    print(housing_extra_attribs) # 在每一行的后边拼接了两个值
    
    '''
    [[-121.89 37.29 38.0 ... 4.625368731563422 2.094395280235988
      0.22385204081632654]
     [-121.93 37.05 14.0 ... 6.008849557522124 2.7079646017699117
      0.15905743740795286]
     [-117.2 32.77 31.0 ... 4.225108225108225 2.0259740259740258
      0.24129098360655737]
     ...
     [-116.4 34.09 9.0 ... 6.34640522875817 2.742483660130719
      0.1796086508753862]
     [-118.01 33.82 31.0 ... 5.50561797752809 3.808988764044944
      0.19387755102040816]
     [-122.45 37.77 52.0 ... 4.843505477308295 1.9859154929577465
      0.22035541195476574]]
      '''
    

    这个转换的另一个好处是,可以很方便的加入到pipeline中,这个下边也讲到了。

    特征缩放

    对于机器学习,数据的scaling同样很重要,不同scaling的特征,会产生不同的结果,在我们的数据中,就存在scaling不一致的问题,解决这样的问题一般有两种方式:

    1. Min-max scaling,也叫normalization, 主要是把值压缩到0~1之间,用值减去最小值后,再除以最大值减最小值的值
    2. Standardization,减去均值后再除以方差,这个跟也叫normalization不一样的地方在于,他的取值范围不是0~1,它可以避免数据中存在极大值造成的误差

    sklearn提供了StandardScaler模块用于特征缩放,我们使用的是第二种Standardization。

    Transformation Pipelines

    我们上边的一系列过程,包含数据清洗,属性重组,数据缩放,text类型的转换,都可以使用sklearn的Pipeline来组合成一个整体的过程,支持异步的方式,同时进行多个pipeline

    # 使用属性组合的方式
    from sklearn.pipeline import FeatureUnion
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    
    
    class DataFrameSelector(BaseEstimator, TransformerMixin):
        def __init__(self, attribute_names):
            self.attribute_names = attribute_names
            
        def fit(self, X, y=None):
            return self
        
        def transform(self, X):
            return X[self.attribute_names].values
        
    
    class CustomLabelBinarizer(BaseEstimator, TransformerMixin):
        def __init__(self, *args, **kwargs):
            self.encoder = LabelBinarizer(*args, **kwargs)
            
        def fit(self, x, y=None):
            self.encoder.fit(x)
            return self
        
        def transform(self, x, y=None):
            print(self.encoder.transform(x))
            return self.encoder.transform(x)
            
    
    num_attribs = list(housing_num)
    cat_attribs = ["ocean_proximity"]
    
    
    num_pipeline = Pipeline([("selector", DataFrameSelector(num_attribs)), 
                             ("imputer", SimpleImputer(strategy="median")), 
                             ("attribs_adder", CombinedAttributesAdder()), 
                              ("std_scaler", StandardScaler())])
    
    cat_pipeline = Pipeline([("selector", DataFrameSelector(cat_attribs)), 
                            ("label_binarizer", CustomLabelBinarizer())])
    
    
    full_pipeline = FeatureUnion(transformer_list=[("num_pipeline", num_pipeline), 
                                                   ("cat_pipeline", cat_pipeline)])
    
    housing_prepared = full_pipeline.fit_transform(housing)
    print(housing_prepared[0])
    

    上边的代码实现了从数据清洗到特征缩放的整个过程。

    选择和训练模型

    在完成了数据的准备任务后,我们对数据应该有了很清晰的了解,接下来就需要选择训练模型,这个过程也是一个不断选择的过程。

    我们首先用linear regression model来试一下:

    # 我们先用线性回归模型试一下
    from sklearn.linear_model import LinearRegression
    
    
    lin_reg = LinearRegression()
    lin_reg.fit(housing_prepared, housing_labels)
    
    # 准备一些测试数据
    some_data = housing.iloc[:5]
    some_labels = housing_labels.iloc[:5]
    some_data_prepared = full_pipeline.transform(some_data)
    print(some_data_prepared)
    print("Predictions:	", lin_reg.predict(some_data_prepared))
    print("Labels:		,", list(some_labels))
    

    用sklearn写模型还是很简单的,通过打印,我们能够看到预测值和观测值还有差距,这时候,就需要一个error信息,来监控错误率

    mean_squared_error表示均方误差,公式为:

    一般使用RMSE进行评估(这个回归分析模型中最常用的评估方法):

    用代码表示为:

    # 使用RMSE测错误
    from sklearn.metrics import mean_squared_error
    
    
    housing_predictions = lin_reg.predict(housing_prepared)
    lin_mse = mean_squared_error(housing_labels, housing_predictions)
    lin_rmse = np.sqrt(lin_mse)
    lin_rmse # 这种错误误差已经很大,说明当前的features不能提供预测的足够的信息或者当前模型不够强大
    
    '''
    68628.19819848923
    '''
    

    从本文上部分的分布应该不难看出,用线性回归的话误差应该很大,更进步,我们考虑使用决策树模型来训练试一下。

    # 使用决策树来训练数据
    from sklearn.tree import DecisionTreeRegressor
    
    
    tree_reg = DecisionTreeRegressor()
    tree_reg.fit(housing_prepared, housing_labels)
    
    tree_predictions = tree_reg.predict(housing_prepared)
    tree_mse = mean_squared_error(housing_labels, tree_predictions)
    tree_rmse = np.sqrt(tree_mse)
    tree_rmse
    
    '''
    0.0
    '''
    

    误差为0,这说明过拟合了。过拟合不是一件好事,为了解决这个问题,我们可以对当前的训练数据做交叉验证Cross-Validation。它的本质是把当前的数据分割成n份,同时生成n个误差。

    这里用到的是K-fold Cross Validation叫做K折交叉验证,和LOOCV的不同在于,我们每次的测试集将不再只包含一个数据,而是多个,具体数目将根据K的选取决定。比如,如果K=5,那么我们利用五折交叉验证的步骤就是:

    1. 将所有数据集分成5份
    2. 不重复地每次取其中一份做测试集,用其他四份做训练集训练模型,之后计算该模型在测试集上的MSE_i
    3. 将5次的MSE_i取平均得到最后的MSE

    # 上边出现了error为0的情况,说明过拟合了,可以使用sk的交叉验证
    # 把训练数据分成一定的分数,相互验证
    from sklearn.model_selection import cross_val_score
    
    
    scores = cross_val_score(tree_reg, housing_prepared, housing_labels, 
                             scoring="neg_mean_squared_error", cv=10)
    tree_rmse_scores = np.sqrt(-scores)
    
    
    def display_scores(scores):
        print("Scores:", scores)
        print("Mean:", scores.mean())
        print("Standard deviation:", scores.std())
        
        
    display_scores(tree_rmse_scores)
    

    可以看出决策树的误差也很高,我们在对线性回归模型做交叉验证:

    # 使用交叉验证看看回归的error
    line_scores = cross_val_score(lin_reg, housing_prepared, housing_labels, 
                             scoring="neg_mean_squared_error", cv=10)
    line_rmse_scores = np.sqrt(-line_scores)
    
    
    display_scores(line_rmse_scores)
    
    

    最后,我们使用随机森林来训练模型:

    # 随机森林
    from sklearn.ensemble import RandomForestRegressor
    
    
    random_forest = RandomForestRegressor()
    random_forest.fit(housing_prepared, housing_labels)
    
    forest_predictions = random_forest.predict(housing_prepared)
    forest_mse = mean_squared_error(housing_labels, forest_predictions)
    forest_rmse = np.sqrt(forest_mse)
    forest_rmse
    
    '''
    22100.915917968654
    '''
    
    

    看上去,这次错误明显小了很多,这个模型目前来说是比较理想的。

    在经历过选择模型后,我们一般会得到一个模型列表,只需选择最优的那个就行了。

    微调模型

    一般来说,机器学习算法都有一些hyperparameter,这些参数可以影响结果,我们对模型的优化也包括如何找到最优的参数。

    sklearn的GridSearchCV能够方便的创建参数组合,比如:

    # 在得到一系列可用的模型列表后,需要对该模型做微调
    # Grid Search 网络搜索,使用sk对各种不同的参数组合做训练,获取最佳参数组合
    from sklearn.model_selection import GridSearchCV
    
    
    param_grid = [{'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
                  {'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]}]
    forest_reg = RandomForestRegressor()
    grid_search = GridSearchCV(forest_reg, param_grid, cv=5, scoring='neg_mean_squared_error')
    grid_search.fit(housing_prepared, housing_labels)
    
    grid_search.best_params_
    
    '''
    {'max_features': 8, 'n_estimators': 30}
    '''
    
    

    上边的代码中一共尝试了34 + 23 = 18种组合。

    # 获取最优的estimator
    grid_search.best_estimator_
    
    '''
    RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=None,
               max_features=8, max_leaf_nodes=None, min_impurity_decrease=0.0,
               min_impurity_split=None, min_samples_leaf=1,
               min_samples_split=2, min_weight_fraction_leaf=0.0,
               n_estimators=30, n_jobs=None, oob_score=False,
               random_state=None, verbose=0, warm_start=False)
    '''
    
    cvres = grid_search.cv_results_
    for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
        print(np.sqrt(-mean_score), params)
        
    
    

    可以很直观的看到每个参数下的误差。

    用测试集验证

    最后,当有了可用的模型后,就可以对test set进行验证了,但首先需要使用上文的pipeline对test set进行转换:

    # 使用最终的模型来评估测试数据
    final_model = grid_search.best_estimator_
    
    X_test = strat_test_set.drop("median_house_value", axis=1)
    y_test = strat_test_set["median_house_value"].copy()
    
    X_test_prepared = full_pipeline.transform(X_test)
    
    final_predictions = final_model.predict(X_test_prepared)
    
    final_mse = mean_squared_error(y_test, final_predictions)
    final_rmse = np.sqrt(final_mse)
    final_rmse
    
    '''
    47732.7520382174
    '''
    
    

    总结

    本文只是一个关于机器学习的小项目,但是包含了一个完整的分析过程,可以看出,对数据的理解和处理占据大部分的工作,要想处理好这些内容,需要一定的统计学知识,这个会在后期的文章中给出总结。

  • 相关阅读:
    java实现第六届蓝桥杯立方尾不变
    java实现第六届蓝桥杯立方尾不变
    java实现第六届蓝桥杯隔行变色
    java实现第六届蓝桥杯隔行变色
    java实现第七届蓝桥杯交换瓶子
    java实现第七届蓝桥杯交换瓶子
    java实现第七届蓝桥杯寒假作业
    Ubuntu 美团sql优化工具SQLAdvisor的安装(转)
    mapper提示Could not autowire. No beans of … type found?
    aop 中joinpoint的使用方法
  • 原文地址:https://www.cnblogs.com/machao/p/11460956.html
Copyright © 2011-2022 走看看