Step 1: 檢視源數(shù)據(jù)集
import numpy as np
import pandas as pd
1.1 讀入數(shù)據(jù)
file=open("D:/python練習(xí)/housing.csv")
train_df=pd.read_csv(file)
1.2 檢視原數(shù)據(jù)
train_df.head(5)
每一行都表示一個街區(qū)。共有 10 個屬性:經(jīng)度、維度、房屋年齡中位數(shù)、總房間數(shù)、臥室數(shù)量、人口數(shù)、家庭數(shù)、收入中位數(shù)、房屋價值中位數(shù)、離大海距離。
train_df.info()
可以看出total_bedrooms這一項(xiàng)有缺失值,后面要進(jìn)行處理。ocean_proximity這一項(xiàng)的數(shù)據(jù)類型為類別型數(shù)據(jù)。
train_df.ocean_proximity.value_counts()
value_counts()方法查看都有什么類型,每個類都有多少街區(qū)
train_df.describe()
畫出每個數(shù)值屬性的柱狀圖
%matplotlib inline
from matplotlib import pyplot as plt
plt.style.use('ggplot')
train_df.hist(bins=50,figsize=(16,9))
從柱狀圖中可以發(fā)現(xiàn)以下問題:
1.這些屬性的量度不一樣,在后面需要進(jìn)行特征縮放
2.許多柱狀圖的尾巴過長,對某些機(jī)器學(xué)習(xí)的算法檢測規(guī)律會變得更難,所以在后面要處理成正態(tài)分布。
3.房屋年齡中位數(shù)和房屋價值中位數(shù)也被設(shè)了上限。后者可能是個嚴(yán)重的問題,因?yàn)樗悄愕哪繕?biāo)屬性(你的標(biāo)簽)。你的機(jī)器學(xué)習(xí)算法可能學(xué)習(xí)到價格不會超出這個界限。你需要與下游團(tuán)隊(duì)核實(shí),這是否會成為問題。如果他們告訴你他們需要明確的預(yù)測值,即使超過 500000,你則有兩個選項(xiàng):1)對于設(shè)了上限的標(biāo)簽,重新收集合適的標(biāo)簽;2)將這些街區(qū)從訓(xùn)練集移除(也從測試集移除,因?yàn)槿舴績r超出 500000,你的系統(tǒng)就會被差評)。
4.收入數(shù)據(jù)經(jīng)過了縮放處理。
Step 2: 創(chuàng)建測試集
目的:防止數(shù)據(jù)透視偏差。如果一開始就看過測試集,就會不經(jīng)意間按照其中的規(guī)律選擇合適的算法,當(dāng)再用測試集進(jìn)行評估時,就會導(dǎo)致結(jié)果比較樂觀,而實(shí)際部署時就比較差了。
from sklearn.model_selection import train_test_split
train_set,test_set=train_test_split(train_df,test_size=0.2,random_state=42)
train_test_split函數(shù)用于將矩陣隨機(jī)劃分為訓(xùn)練子集和測試子集,并返回劃分好的訓(xùn)練集測試集樣本和訓(xùn)練集測試集標(biāo)簽。test_size:如果是浮點(diǎn)數(shù),在0-1之間,表示樣本占比;如果是整數(shù)的話就是樣本的數(shù)量。random_state:是隨機(jī)數(shù)的種子。產(chǎn)生總是相同的洗牌指數(shù);如果沒有這個每次運(yùn)行會產(chǎn)生不同的測試集,多次之后會得到整個數(shù)據(jù)集。
train_set.head(5)
不足之處:目前都是采用的純隨機(jī)抽樣,如果數(shù)據(jù)量比較小,可能會產(chǎn)生偏差。所以需要關(guān)注比較重要的屬性,對其進(jìn)行分層抽樣,比如說收入中位數(shù)。從直方圖中可以看出,大多數(shù)的值在2-5(萬美元),進(jìn)行分層抽樣時保證每一層都要有足夠多的數(shù)據(jù),這就意味著,層數(shù)不能過多。后面的代碼將收入中位數(shù)除以1.5(以限制分類的數(shù)量),創(chuàng)建收入類別屬性,用ceil對值舍入(產(chǎn)生離散的分類),并將大于5的分類歸入到分類5。inplace=True原數(shù)組內(nèi)容被改變。
train_df["income_cat"]=np.ceil(train_df.median_income/1.5)
train_df["income_cat"].where(train_df["income_cat"]<5,5.0,inplace=True)
train_df["income_cat"].head(5)
train_df["income_cat"].value_counts()/len(train_df)
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(train_df,train_df["income_cat"]):
start_train_set=train_df.loc[train_index]
start_test_set=train_df.loc[test_index]
start_train_set.head(5)
for set in(start_train_set,start_test_set):
set.drop(["income_cat"],axis=1,inplace=True)
start_test_set.head(5)
需要刪除income_cat屬性,使數(shù)據(jù)回到初始狀態(tài)。
Step 3: 數(shù)據(jù)探索及可視化
目的:以上只是粗略的查看了統(tǒng)計信息,還需要從樣本中發(fā)現(xiàn)更多的信息。如果訓(xùn)練集非常大,可能還需要采樣一個探索集。如果比較小,可以建立一個副本。
housing=start_train_set.copy()
3.1 地理數(shù)據(jù)可視化
原因:樣本中有經(jīng)度和緯度,所以考慮創(chuàng)建散點(diǎn)圖
housing.plot(kind="scatter",x="longitude",y="latitude",alpha=0.4,figsize=(15, 10)
)
高密度區(qū)域:灣區(qū)、洛杉磯和圣迭戈等
housing.plot(kind="scatter",x="longitude",y="latitude",alpha=0.5,
s=housing.population/50,label="population",
c=housing.median_house_value,cmap=plt.get_cmap('jet'),colorbar=True,
figsize=(15, 10))
plt.legend()
加入人口信息,用點(diǎn)的大小來展示(s)。加入房價信息,用顏色的深淺來表示(c),用預(yù)先定義的顏色圖“jet”,范圍從藍(lán)色到紅色,即低價到高價。從圖中可以發(fā)現(xiàn)房價和位置(沿海地區(qū))以及人口密度存在聯(lián)系。
import seaborn as sns
sns.set(style = "whitegrid")#設(shè)置樣式
x = housing.longitude#X軸數(shù)據(jù)
y = housing.latitude#Y軸數(shù)據(jù)
z = housing.median_income#用來調(diào)整各個點(diǎn)的大小s
cm = plt.get_cmap('jet')
fig,ax = plt.subplots(figsize = (15,10))
#注意s離散化的方法,因?yàn)樾枰ㄟ^點(diǎn)的大小來直觀感受其所表示的數(shù)值大小
#參數(shù)是X軸數(shù)據(jù)、Y軸數(shù)據(jù)、各個點(diǎn)的大小、各個點(diǎn)的顏色
bubble = ax.scatter(x, y , s = housing.population/50, c = z, cmap = cm, linewidth = 0.5, alpha = 0.5)
ax.grid()
fig.colorbar(bubble)
ax.set_xlabel('longitude', fontsize = 15)#X軸標(biāo)簽
ax.set_ylabel('latitude', fontsize = 15)#Y軸標(biāo)簽
plt.show()
利用seaborn作圖也可以達(dá)到同樣效果。加入人口信息,用點(diǎn)的大小來展示(s)。加入收入信息,用顏色的深淺來表示(c),用預(yù)先定義的顏色圖“jet”,范圍從藍(lán)色到紅色,即低價到高價。從圖中可以發(fā)現(xiàn)收入和位置(沿海地區(qū))以及人口密度存在一定的聯(lián)系,但是效果不是十分明顯。
3.2 查看特征之間的相關(guān)性
corr_matrix=housing.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)
用熱力圖的方式展現(xiàn)相關(guān)性。annot(annotate的縮寫):默認(rèn)取值False;如果是True,在熱力圖每個方格寫入數(shù)據(jù);vmax,vmin:分別是熱力圖的顏色取值最大和最小范圍,默認(rèn)是根據(jù)data數(shù)據(jù)表里的取值確定。
import seaborn as sns
fig=plt.figure(figsize=(12,8),dpi=80)
sns.heatmap(housing.corr(),annot =True,vmin = 0, vmax = 1)
用可視化的方式展現(xiàn)相關(guān)性,因?yàn)閷傩赃^多,所以選取部分屬性。
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))
對角線表示每個屬性的柱狀圖。圖中可以看出收入對房價的影響還是比較大的,所以可以放大進(jìn)行研究。
housing.plot(kind="scatter",x="median_income",y="median_house_value",alpha=0.5,figsize=(12,8))
可以看出相關(guān)性還是很高的。其次,可以看到一些直線:500000,450000,350000,280000美元,可能是收集資料時設(shè)立的邊界。
3.3 屬性組合試驗(yàn)
有一些屬性,比如總房間數(shù),在不知道街區(qū)有多少戶的情況下用處不大。同理總臥室數(shù)和總?cè)丝跀?shù)。
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)
間數(shù)和臥室數(shù)更有信息。而且每戶的房間數(shù)越多,意味著房屋更大,房價越高,比單純的看總房間數(shù)更有信息。
Step 4: 數(shù)據(jù)預(yù)處理
目的:為機(jī)器學(xué)習(xí)算法準(zhǔn)備數(shù)據(jù)。
housing=start_train_set.drop("median_house_value",axis=1)
housing_copy=housing.copy()
housing_labels=start_train_set.median_house_value.copy()
print(housing.head(5))
print(housing_labels.head(5))
將訓(xùn)練集中預(yù)測量和標(biāo)簽分開,因?yàn)橹髮ζ湟M(jìn)行不同的轉(zhuǎn)換;drop()創(chuàng)建備份,不影響原數(shù)據(jù)集。
4.1 數(shù)據(jù)清洗
目的:處理缺省值。因?yàn)楹芏鄼C(jī)器學(xué)習(xí)算法對缺省值比較敏感(例如LR和SVM,決策樹和樸素貝葉斯相對好一點(diǎn))
思路:1)去掉缺失的行數(shù)據(jù)dropna();2)去掉缺失的列drop();3)進(jìn)行賦值fillna(),可以是0、平均數(shù)和中位數(shù)。
median=housing.total_bedrooms.median()
housing.total_bedrooms=housing.total_bedrooms.fillna(median)
housing.isnull().sum().sum()
4.2 處理文本和類別屬性
housing=pd.get_dummies(housing)
housing.head(5)
pandas自帶的get_dummies方法,可以幫你一鍵做到One-Hot??梢钥闯鲱悇e屬性ocean_proximity被我們分成了5個column,每一個代表一個category。是就是1,不是就是0。
4.3 特征縮放
當(dāng)輸入的屬性量度不同時,會影響機(jī)器學(xué)習(xí)算法的性能。比如總房間數(shù)分布范圍在6-39320,收入中位數(shù)在0-15。兩種方法:歸一化和標(biāo)準(zhǔn)化。歸一化將數(shù)值縮放到0-1之間,標(biāo)準(zhǔn)化不會限定到某個范圍,對一些算法有影響,像神經(jīng)網(wǎng)絡(luò)算法輸入值就必須是0-1。但是異常值對標(biāo)準(zhǔn)化的影響較小。
numeric_cols=housing_copy.columns[housing_copy.dtypes!="object"]
print(numeric_cols)
numeric_col_means = housing.loc[:, numeric_cols].mean()
numeric_col_std = housing.loc[:, numeric_cols].std()
housing.loc[:, numeric_cols] = (housing.loc[:, numeric_cols] - numeric_col_means) /numeric_col_std
housing.head()
housing_labels=(housing_labels-housing_labels.mean())/housing_labels.std()
housing_labels.head()
Step 5:模型選擇和訓(xùn)練
5.1 在訓(xùn)練集上訓(xùn)練和評估
from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
將DF轉(zhuǎn)化為Numpy Array
X_train=housing.values
X_test=housing_labels.values
print(X_train)
print(X_test)
先訓(xùn)練一個線性回歸模型
lin_reg.fit(X_train,X_test)
訓(xùn)練完成,用交叉驗(yàn)證法進(jìn)行模型評估。交叉驗(yàn)證的基本思想是將訓(xùn)練數(shù)據(jù)集分為k份,每次用k-1份訓(xùn)練模型,用剩余的1份作為驗(yàn)證集。按順序訓(xùn)練k次后,計算k次的平均誤差來評價模型(改變參數(shù)后即為另一個模型)的好壞。
from sklearn.model_selection import cross_val_score
lin_reg_scores=cross_val_score(lin_reg,X_train,X_test,scoring="neg_mean_squared_error",cv=10)
lin_reg_rmse_scores=np.sqrt(-lin_reg_scores)
print(lin_reg_rmse_scores.mean())
均方根誤差(RMSE),回歸任務(wù)可靠的性能指標(biāo)。
利用決策樹模型試試看
from sklearn.tree import DecisionTreeRegressor
tree_reg=DecisionTreeRegressor()
tree_reg.fit(X_train,X_test)
tree_reg_scores=cross_val_score(tree_reg,X_train,X_test,scoring="neg_mean_squared_error",cv=10)
tree_reg_rmse_scores=np.sqrt(-tree_reg_scores)
print(tree_reg_rmse_scores.mean())
可以看出決策樹模型的誤差大于線性回歸,性能更差一點(diǎn)。現(xiàn)在選擇用隨機(jī)森林嘗試一下。
from sklearn.ensemble import RandomForestRegressor
RF_reg=RandomForestRegressor()
RF_reg.fit(X_train,X_test)
RF_reg_scores=cross_val_score(RF_reg,X_train,X_test,scoring="neg_mean_squared_error",cv=10)
RF_reg_rmse_scores=np.sqrt(-RF_reg_scores)
print(RF_reg_rmse_scores.mean())
隨機(jī)森林速度慢一點(diǎn),誤差更小,明顯更有希望。
5.2 利用網(wǎng)格搜索對模型進(jìn)行微調(diào)
只要提供超參數(shù)和試驗(yàn)的值,網(wǎng)格搜索就可以使用交叉驗(yàn)證試驗(yàn)所有可能超參數(shù)值的組合。
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(X_train,X_test)
首先調(diào)第一行的參數(shù)為n_estimators和max_features,即有34=12種組合,然后再調(diào)第二行的參數(shù),即23=6種組合,具體參數(shù)的代表的意思以后再講述。總共組合數(shù)為12+6=18種組合。每種交叉驗(yàn)證5次,即18*5=90次模型計算,雖然運(yùn)算量比較大,但運(yùn)行完后能得到較好的參數(shù)
grid_search.best_params_
可以看到最好參數(shù)中30是選定參數(shù)的邊緣,所以可以再選更大的數(shù)試驗(yàn),可能會得到更好的模型,還可以在8附近選定參數(shù),也可能會得到更好的模型。
grid_search.best_estimator_
得到最佳的估計器
5.3 用測試集去評估系統(tǒng)
from sklearn.metrics import mean_squared_error
final_model = grid_search.best_estimator_
X_test = start_test_set.drop("median_house_value", axis=1)
y_test = start_test_set["median_house_value"].copy()
median=X_test.total_bedrooms.median()
X_test.total_bedrooms=X_test.total_bedrooms.fillna(median)
X_test=pd.get_dummies(X_test)
numeric_col_means = X_test.loc[:, numeric_cols].mean()
numeric_col_std = X_test.loc[:, numeric_cols].std()
X_test.loc[:, numeric_cols] = (X_test.loc[:, numeric_cols] - numeric_col_means) /numeric_col_std
y_test=(y_test-y_test.mean())/y_test.std()
final_predictions = final_model.predict(X_test)
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse)
print(final_rmse)
把測試集進(jìn)行預(yù)處理,導(dǎo)入到系統(tǒng)中,誤差為0.462982088615,模型的表現(xiàn)還不錯。