准備數據
訓練集和測試集的數據來源於很多地方,比如:數據庫,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_value
和median_income
存在一定的線性關系,也驗證了這個屬性相對別的屬性來說,更加重要。
我們放大該圖:
housing.plot(kind='scatter', x='median_income', y="median_house_value", alpha=0.2)
# 圖中橫線的部分就屬於干擾數據,不符合大的趨勢
如果仔細觀察上圖,能發現很多細節:
- 存在上升趨勢且點沒有分散,說明二者存在強關系
- 在圖形中的點組成的橫向顯示,$500000是一個價格上線,在下邊隱約還存在另外幾個“橫線”。這不符合線性關系,因此可能是干擾數據,可以考慮移除掉
屬性組合
在數據中,可能打個屬性的用處並不大,但是對這些屬性做一些特殊的重組后,會獲取到一些有用的信息。
在我們這個例子中,total_rooms
,total_bedrooms
單獨存在的意義不是很大,但是如果跟population
和households
做一些組合后,就會產生新的有意義的屬性。
# 有些屬性可能是我們不需要的,在這里,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不一致的問題,解決這樣的問題一般有兩種方式:
- Min-max scaling,也叫normalization, 主要是把值壓縮到0~1之間,用值減去最小值后,再除以最大值減最小值的值
- 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:\t", lin_reg.predict(some_data_prepared))
print("Labels:\t\t,", 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,那么我們利用五折交叉驗證的步驟就是:
- 將所有數據集分成5份
- 不重復地每次取其中一份做測試集,用其他四份做訓練集訓練模型,之后計算該模型在測試集上的MSE_i
- 將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
'''
總結
本文只是一個關於機器學習的小項目,但是包含了一個完整的分析過程,可以看出,對數據的理解和處理占據大部分的工作,要想處理好這些內容,需要一定的統計學知識,這個會在后期的文章中給出總結。