机器学习实战(用Scikit-learn和TensorFlow进行机器学习)(二)
上一節講述了真實數據(csv表格數據)的查看以及如何正確的分開訓練測試集。今天接著往下進行實戰操作,會用到之前的數據和代碼,如果有問題請查看上一節。?
三、開始實戰(處理CSV表格數據)?
5、查看訓練集的特征圖像信息以及特征之間的相關性
??上一節粗略地查看了數據的統計信息,接下來需要從訓練樣本中得到更多的信息,從而對數據進行一些處理。
查看訓練集的特征圖像信息
??為了防止誤操作在查看的時候修改了訓練集,所以先復制一份進行操作。對longitude和latitude(經緯度)以散點(scatter)的形式輸出,看數據的地區分布。
train_housing = strat_train_set.copy() train_housing.plot(kind="scatter", x="longitude", y="latitude")- 1
- 2
??????????
??若加上參數alpha=0.1可以看到數據高密度的區域,若alpha設為1,則和上面一樣,alpha越靠近0則只加深越高密度的地方。
train_housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)- 1
??????????
??為了查看目標median_house_value的在各個地區的分布情況,所以可以增加參數c(用于對某參數顯示顏色),參數s(表示某參數圓的半徑),cmap表示一個colormap,jet為一個藍到紅的顏色,colorbar為右側顏色條
train_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()- 1
- 2
- 3
- 4
- 5
??????????
查看特征之間的相關性
??查看median_house_value與其他變量的線性相關性,并排序輸出,數據越靠近1則越相關,靠近-1則越負相關,接近0為不相關。
corr_matrix = train_housing.corr() corr_matrix["median_house_value"].sort_values(ascending=False)- 1
- 2
??????????
??可以看到除了與自己最相關以外,和median_income線性相關性很強。
??然而上面只是計算了線性相關性,而特征之間可能是非線性的關系,因此需要畫出圖來看一下變量之間是否相關。(代碼中只取其中的幾個來看)
from pandas.tools.plotting import scatter_matrix attributes = ["median_house_value", "median_income", "total_rooms"] scatter_matrix(housing[attributes], figsize=(12, 8))- 1
- 2
- 3
??
??從第一行第二幅圖,可以看到median_house_value和median_income的正線性相關性還是比較強的,但是還是看到一些問題,比如大于500000后的點可能在收集資料或預處理時設立的邊界,使得變為一條直線一樣;而且還有右下角一些奇異的點。為了讓算法不學習到這些有問題的點,你可以去除這些相關區域的點。
特征之間的組合
兩個特征對目標的相關性都不強,但是組合起來可能有較大的提升。最后還可以嘗試一下特征的組合(不是特別重要)
train_housing["rooms_per_household"] = housing["total_rooms"]/housing["households"] train_housing["bedrooms_per_room"] = housing["total_bedrooms"]/housing["total_rooms"] train_housing["population_per_household"]=housing["population"]/housing["households"] corr_matrix = train_housing.corr() corr_matrix["median_house_value"].sort_values(ascending=False)- 1
- 2
- 3
- 4
- 5
??????????
經過特征組合,可以看到新特征bedrooms_per_room對median_house_value的影響比較大(-0.2599),呈一定的負相關,即每個房子的臥室越少,價格反而越貴。?
6、準備數據(數據預處理)
??首先分開特征(feature)和目標標簽(label),以median_house_value作為標簽,其他作為特征。
train_housing = strat_train_set.drop("median_house_value", axis=1) train_housing_labels = strat_train_set["median_house_value"].copy()- 1
- 2
數據清洗
??在第一節我們得知total_bedrooms存在一些缺失值,對于缺失值的處理有三種方案:
??1、去掉含有缺失值的個體(dropna)
??2、去掉含有缺失值的整個特征(drop)
??3、給缺失值補上一些值(0、平均數、中位數等)(fillna)
#train_housing.dropna(subset=["total_bedrooms"]) # option 1 #train_housing.drop("total_bedrooms", axis=1) # option 2 median = train_housing["total_bedrooms"].median() train_housing["total_bedrooms"].fillna(median) # option 3- 1
- 2
- 3
- 4
??為了得到更多可利用的數據,在這里我們選擇方案3。
??當然非常方便的Scikit-Learn也存在對缺失值處理的類Imputer。我們打算對所有地方的缺失值都補全,以防運行模型時發生錯誤。使用Imputer函數需要先定義一個補缺失值的策略(如median),由于median策略只能對實數值有效,所以需要將文本屬性先去除,然后再補缺失值。最后使用fit方法對變量執行相應操作。
from sklearn.preprocessing import Imputer imputer = Imputer(strategy="median") housing_num = train_housing.drop("ocean_proximity", axis=1) imputer.fit(housing_num)- 1
- 2
- 3
- 4
??對數據缺失值補全以后,一般需要轉化為Numpy的矩陣格式,方便模型的輸入,所以可以調用Imputer的transform()方法,當然fit和transform也可以合起來使用,即fit_transform(),這個函數會比分開調用要快一些。
X = imputer.transform(housing_num) #X = imputer.fit_transform(housing_num)#也可以將numpy格式的轉換為pd格式 #housing_tr = pd.DataFrame(X, columns=housing_num.columns)- 1
- 2
- 3
- 4
- 5
處理類別文本特征
??由于文本屬性不能作median等操作,所以需要將文本特征編碼為實數特征,對應Scikit-Learn中的類為LabelEncoder,通過調用LabelEncoder類,再使用fit_transform()方法自動將文本特征編碼
from sklearn.preprocessing import LabelEncoder encoder = LabelEncoder() housing_cat = train_housing["ocean_proximity"] housing_cat_encoded = encoder.fit_transform(housing_cat) print(housing_cat_encoded) print(encoder.classes_)- 1
- 2
- 3
- 4
- 5
- 6
??輸出的數字編碼與編碼對應的類型為:
????
??由于0到1的距離比0到3的距離要近,所以這種數字編碼暗含了0和1的相似性比0到3的相似性要強,然而事實上并非如此,每個元素的相似性應趨于相等。如果該數字編碼作為label,則只是一個標簽,沒有什么影響。但是如果用于特征,則這種數字編碼不適用,應該采用one hot編碼(形式可以看下面的圖),對應Scikit-Learn中的類為OneHotEncoder
from sklearn.preprocessing import OneHotEncoder encoder = OneHotEncoder() housing_cat_1hot = encoder.fit_transform(housing_cat_encoded.reshape(-1,1))- 1
- 2
- 3
??默認的輸出結果為稀疏矩陣Scipy(sparse matrix),而不是Numpy,由于矩陣大部分為0,浪費空間,所以使用稀疏矩陣存放,如果想看矩陣的具體樣子,則用toarray()方法變為dense matrix(Numpy)。
housing_cat_1hot.toarray()- 1
??????????
??上述文本編碼先經過數字編碼再轉為one hot編碼用了兩步,當然也可以一步到位,直接從文本編碼到one hot,對應Scikit-Learn中的類為LabelBinarizer
from sklearn.preprocessing import LabelBinarizer encoder = LabelBinarizer() #encoder = LabelBinarizer(sparse_output=True) housing_cat_1hot = encoder.fit_transform(housing_cat)- 1
- 2
- 3
- 4
??要注意的是,不設參數sparse_output=True的話,默認輸出的是Numpy矩陣。
自定義Transformer
??由于Scikit-Learn中的函數中提供的Transformer方法并不一定適用于真實情形,所以有時候需要自定義一個Transformer,與Scikit-Learn能夠做到“無縫結合”,比如pineline(以后會說到)。定義類時需要加入基礎類:BaseEstimator(必須),以及TransformerMixin(用于自動生成fit_transformer()方法)。下面是一個例子:用于增加組合特征的Trainsformer
from sklearn.base import BaseEstimator, TransformerMixin rooms_ix, bedrooms_ix, population_ix, household_ix = 3, 4, 5, 6class CombinedAttributesAdder(BaseEstimator, TransformerMixin):def __init__(self, add_bedrooms_per_room = True): # no *args or **kargsself.add_bedrooms_per_room = add_bedrooms_per_roomdef fit(self, X, y=None):return self # nothing else to dodef transform(self, X, y=None):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]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(add_bedrooms_per_room=False) housing_extra_attribs = attr_adder.transform(housing.values)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
??在該代碼中,init中設置參數,可以調整是否加入該元素,用于嘗試確定加入該元素是否對模型效果提升,方便修改,節省時間。
特征縮放
??由于機器學習算法在不同尺度范圍的特征之間表現的不好,比如total number of rooms范圍是6-39320,而median_incomes范圍是0-15。因此需要對特征的范圍進行縮放,對應Scikit-Learn中的類為:
??1、MinMaxScaler:將特征縮放到0-1之間,但異常值對這個影響比較大,比如異常值為100,縮放0-15為0-0.15;
??2、feature_range:可以自定義縮放的范圍,不一定是0-1;
??3、StandardScaler:標準化(減均值,除方差),對異常值的影響較小,但可能會不符合某種范圍
??需要注意:每次縮放只能針對訓練集或只是測試集,而不能是整個數據集,這是由于測試集(或新數據)不屬于訓練范圍。
Transformation Pipelines
??可以看到,上述有非常多的轉換操作,并按一定的順序執行,但是再次處理其他數據(如測試數據)時需要重新調用執行眾多步驟,代碼看起來過于繁瑣。所以Scikit-Learn提供了Pineline類來幫助這種一系列的轉換,把這些轉換封裝為一個轉換。下面是一個簡單的例子。
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler num_pipeline = Pipeline([ ('imputer', Imputer(strategy="median")), ('attribs_adder', CombinedAttributesAdder()), ('std_scaler', StandardScaler()), ]) housing_num_tr = num_pipeline.fit_transform(housing_num)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
??Pipeline是由(name(名字),Estimator(類))對組成,但最后一個必須為transformer,這是因為要形成fit_transform()方法
??上面的pineline只是用于處理實數特征的,對于處理類別特征的還有另一個Pineline,這就可以使用FearureUnion類來結合多個pineline,多個Pineline可以并行處理,最后將結果拼接在一起輸出。
??由于Scikit-Learn沒有處理Pandas數據的DataFrame,因此需要自己自定義一個如下:
from sklearn.base import BaseEstimator, TransformerMixin class DataFrameSelector(BaseEstimator, TransformerMixin):def __init__(self, attribute_names):self.attribute_names = attribute_namesdef fit(self, X, y=None):return selfdef transform(self, X):return X[self.attribute_names].values- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
??然后就可以通過FeatureUnion類結合兩個Pineline
from sklearn.pipeline import FeatureUnion num_attribs = list(housing_num) cat_attribs = ["ocean_proximity"] num_pipeline = Pipeline([ ('selector', DataFrameSelector(num_attribs)), ('imputer', Imputer(strategy="median")), ('attribs_adder', CombinedAttributesAdder()), ('std_scaler', StandardScaler()), ]) cat_pipeline = Pipeline([ ('selector', DataFrameSelector(cat_attribs)), ('label_binarizer', LabelBinarizer()), ]) full_pipeline = FeatureUnion(transformer_list=[ ("num_pipeline", num_pipeline), ("cat_pipeline", cat_pipeline), ]) housing_prepared = full_pipeline.fit_transform(train_housing)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
??需要注意:在scikit-learn == 0.18.0及以前版本LabelBinarizer()用在Pineline沒有問題;而在0.19.0版本則會報錯,因此需要自己定義一個新的LabelBinarizer_new(),代碼如下;0.20.0版本以后可以使用新的類CategoricalEncoder()
from sklearn.base import BaseEstimator, TransformerMixin class LabelBinarizer_new(TransformerMixin, BaseEstimator):def fit(self, X, y = 0):self.encoder = Nonereturn selfdef transform(self, X, y = 0):if(self.encoder is None):print("Initializing encoder")self.encoder = LabelBinarizer();result = self.encoder.fit_transform(X)else:result = self.encoder.transform(X)return result總結
以上是生活随笔為你收集整理的机器学习实战(用Scikit-learn和TensorFlow进行机器学习)(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何用50行代码构建情感分类器
- 下一篇: 机器学习实战(用Scikit-learn