怎么建設(shè)微信網(wǎng)站南寧百度推廣seo
特征縮放和轉(zhuǎn)換
您需要應(yīng)用于數(shù)據(jù)的最重要的轉(zhuǎn)換之一是功能擴展。除了少數(shù)例外,機器學(xué)習(xí)算法
在輸入數(shù)值屬性具有非常不同的尺度時表現(xiàn)不佳。住房數(shù)據(jù)就是這種情況:房間總數(shù)約為6至39320間,而收入中位數(shù)僅為0至15間。如果沒有任何縮放,大多數(shù)模型將傾向于忽略收入中位數(shù),而更多地關(guān)注房間數(shù)。
有兩種常見的方法使所有屬性具有相同的尺度:最小-最大尺度和標準化。
與所有估計器一樣,重要的是僅將標量擬合到訓(xùn)練數(shù)據(jù):永遠不要對訓(xùn)練集以外的任何對象使用fit()或fit_transform()。一旦你有了一個訓(xùn)練好的定標器,你就可以用它來變換()任何其他的集合,包括驗證集、測試集和新的數(shù)據(jù)。請注意,雖然培訓(xùn)集值將始終縮放到指定的范圍,但如果新的數(shù)據(jù)包含異常值,則這些值最終可能會縮放到該范圍之外。如果要避免這種情況,只需將剪輯超參數(shù)設(shè)置為True即可。
最小-最大縮放(許多人稱之為標準化)是最簡單的:對于每個屬性,值被移位和重新縮放,以便它們最終的范圍從0到1。這是通過減去最小值并除以最小值和最大值之間的差來實現(xiàn)的。Scikit-Learn為此提供了一個名為MinMaxScaler的轉(zhuǎn)換器。它有一個feature_range超參數(shù),如果出于某種原因,不希望0-1(例如,神經(jīng)網(wǎng)絡(luò)在零均值輸入下工作得最好,所以最好在-1到1的范圍內(nèi)工作)。是相當好用的:
from sklearn.preprocessing import MinMaxScaler
min_max_scaler = MinMaxScaler(feature_range=(-1, 1))
housing_num_min_max_scaled = min_max_scaler.fit_transform(housing_num)
標準化是不同的:首先它減去平均值(所以標準化值有一個零均值),然后它除以標準差(所以標準-化值的標準差等于1)。不像最小最大縮放,標準化化不會將值限制在特定的范圍內(nèi)。然而,標準化受到離群值的影響要小得多。例如,假設(shè)一個地區(qū)的收入中位數(shù)等于100(誤打誤撞),而不是通常的0-15。將最小最大值縮放到0-1范圍會將這個離群值映射到1并將所有其他值壓縮到0-0.15,而標準化不會受到太大影響。Scikit-Learn提供了一個名為StandardScaler的轉(zhuǎn)換器用于標準化:
from sklearn.preprocessing import StandardScaler
std_scaler = StandardScaler()
housing_num_std_scaled = std_scaler.fit_transform(housing_num)
如果你想縮放一個稀疏矩陣而不首先將其轉(zhuǎn)換為稠密矩陣,你可以使用一個標準縮放器,它的with_mean超參數(shù)設(shè)置為False它只會除以標準差,而不會減去均值(因為這會破壞稀疏性
當特征的分布具有重尾時(即,當遠離平均值的值不是指數(shù)罕見時),最小-最大縮放和標準化都會將大多數(shù)值壓縮到一個小范圍內(nèi)。 機器學(xué)習(xí)
模型通常根本不喜歡這種情況,因此,在縮放特征之前,您應(yīng)該首先對其進行變換以縮小重尾,并且如果可能的話,使分布大致對稱。 例如,對于右側(cè)有重尾部的正特征,執(zhí)行此操作的常見方法是用其平方根替換特征(或?qū)⑻卣魈岣叩?0 到 1 之間的冪)。 如果該特征具有非常長且重的尾部,例如冪律分布,則用其對數(shù)替換該特征可能會有所幫助。 例如,人口特征大致遵循冪律:擁有 10,000 名居民的地區(qū)的出現(xiàn)頻率僅比擁有 1,000 名居民的地區(qū)低 10 倍,而不是呈指數(shù)級下降。 下圖 顯示了當你計算它的對數(shù)時這個特征看起來有多好:它非常接近高斯分布(即鐘形)。
另一種處理重尾特征的方法是對特征進行反向轉(zhuǎn)換。這意味著將其分布切割成大致相等大小的桶,并將每個特征值替換為它所屬的桶的索引,就像我們創(chuàng)建收入貓?zhí)卣饕粯?盡管我們只在分層抽樣時使用它)。例如,您可以將每個值替換為其百分位數(shù)。使用相同大小的bucket處理會產(chǎn)生一個幾乎均勻分布的特性,因此不需要進一步的縮放,或者您可以只除以bucket的數(shù)目來強制值到0-1的范圍。
當一個特征具有多峰分布(即,有兩個或兩個以上清晰的峰值,稱為模式)時,例housing_median_age特征,對它進行bucket ization也是有幫助的,但這次將bucket ID作為類別處理,而不是作為數(shù)值。這意味著必須對桶索引進行編碼,例如使用OneHotEncoder(所以你通常不想用太多桶)。這種方法將允許回歸模型更容易地為該特征值的不同范圍學(xué)習(xí)不同的規(guī)則。例如,也許35年前建造的房子有一種獨特的風格,已經(jīng)過時了,因此它們比它們的年齡更便宜。
轉(zhuǎn)換多峰分布的另一種方法是為每個模式(至少是主要模式)添加一個特征,表示住房中位年齡和該特定模式之間的相似性。相似性度量通常使用徑向基函數(shù)(RBF)來計算–任何只依賴于輸入值與不動點之間距離的函數(shù)。最常用的RBF是高斯RBF,其輸出值隨著輸入值遠離固定點而呈指數(shù)衰減。例如,高斯RBF相似度與房齡x和35由方程exp (-y (x-35)2)
給出。超參數(shù)y(gamma)
確定當x遠離35時相似性度量衰減的速度。使用Scikit-Learn
的rbf_kernel()
函數(shù),您可以創(chuàng)建一個新的高斯RBF特征來測量房屋中位年齡和35:
from sklearn.metrics.pairwise import rbf_kernel
age_simil_35 = rbf_kernel(housing[["housing_median_age"]], [[35]], gamma=0.1)
下圖顯示了這一新特征作為住房中位數(shù)年齡的函數(shù)(實線)。它還顯示了如果使用較小的gamma值,該功能將是什么樣子。如圖所示,新的年齡相似性特征在35歲時達到峰值,正好在住房中位年齡分布的峰值附近:如果這個特定的年齡組與較低的價格有很好的相關(guān)性,那么這個新特征將有很好的機會發(fā)揮作用。
到目前為止,我們只看了輸入特性,但是目標值可能也需要轉(zhuǎn)換。例如,若目標分布具有較重的尾部,您可以選擇將目標替換為其對數(shù)。但是,如果你這樣做,回歸模型現(xiàn)在將預(yù)測的房屋價值中位數(shù)的對數(shù),而不是房屋價值中位數(shù)本身。如果您想要預(yù)測的房屋中值,則需要計算模型預(yù)測的指數(shù)值。
幸運的是,大多數(shù)Scikit-Learn
的轉(zhuǎn)換器都有一個inverse_transform()
方法,這使得計算轉(zhuǎn)換的逆運算變得很容易。例如,下面的代碼示例顯示了如何使用StandardScaler
縮放標簽(就像我們對輸入所做的那樣),然后在縮放后的標簽上訓(xùn)練一個簡單的線性回歸模型,并使用它對一些新數(shù)據(jù)進行預(yù)測,然后使用經(jīng)過訓(xùn)練的縮放器的inverse_transform()
方法將這些數(shù)據(jù)轉(zhuǎn)換回原始尺度。請注意,我們將標簽從Pandas Series
轉(zhuǎn)換為DataFrame
,因為標準Scaler期望2D輸入。此外,為了簡單起見,在本示例中,我們僅使用單個原始輸入特征(中值收入)對模型進行訓(xùn)練:
from sklearn.linear_model import LinearRegression
target_scaler = StandardScaler()
scaled_labels = target_scaler.fit_transform(housing_labels.to_frame())
model = LinearRegression()
model.fit(housing[["median_income"]], scaled_labels)
some_new_data = housing[["median_income"]].iloc[:5] # pretend this is new data
scaled_predictions = model.predict(some_new_data)
predictions = target_scaler.inverse_transform(scaled_predictions)
這工作得很好,但更簡單的選擇是使用TransformedTargetRegressor
。我們只需要構(gòu)造它,給它回歸模型和標簽轉(zhuǎn)換器,然后使用原始的未縮放標簽將其擬合到訓(xùn)練集上。它將自動使用轉(zhuǎn)換器縮放標簽,并在縮放后的標簽上訓(xùn)練回歸模型,就像我們之前所做的那樣。然后,當我們想要進行預(yù)測時,它將調(diào)用回歸模型的predict()
方法,并使用scaler
的inverse_trans form()
方法生成預(yù)測:
from sklearn.compose import TransformedTargetRegressor
model = TransformedTargetRegressor(LinearRegression(),
transformer=StandardScaler())
model.fit(housing[["median_income"]], housing_labels)
predictions = model.predict(some_new_data)
自定義Transformers
雖然Scikit-Learn提供了許多有用的轉(zhuǎn)換器,但您需要編寫自己的任務(wù),如自定義轉(zhuǎn)換、清理操作或組合特定屬性。
對于不需要任何訓(xùn)練的轉(zhuǎn)換,您可以只編寫一個函數(shù),該函數(shù)接受NumPy數(shù)組作為輸入,并輸出轉(zhuǎn)換后的數(shù)組。例如,如前一節(jié)所述,通過將具有重尾分布的特征替換為它們的對數(shù)(假設(shè)特征為正數(shù)且尾部位于右側(cè)),通常是一個好主意。讓我們創(chuàng)建一個log-transformer,并將其應(yīng)用于人口特征:
from sklearn.preprocessing import FunctionTransformer
log_transformer = FunctionTransformer(np.log, inverse_func=np.exp)
log_pop = log_transformer.transform(housing[["population"]])
inverse_func參數(shù)是可選的。它允許您指定一個逆變換函數(shù),例如,如果您計劃在
TransformedTargetRegressor中使用您的轉(zhuǎn)換器。轉(zhuǎn)換函數(shù)可以接受超參數(shù)作為附加參數(shù)。例如,以下是如何創(chuàng)建一個轉(zhuǎn)換器,計算與前面相同的高斯RBF相似性度量:
rbf_transformer = FunctionTransformer(rbf_kernel,
kw_args=dict(Y=[[35.]], gamma=0.1))
age_simil_35 = rbf_transformer.transform(housing[["housing_median_age"]]
注意,RBF核沒有反函數(shù),因為在距離一個固定點的給定距離上總是有兩個值(距離0除外)。還要注意rbf_kernel()不會單獨處理這些特性。如果你給它傳遞一個有兩個特征的數(shù)組,它會測量二維距離(歐幾里得)來測量相似性。例如,以下是如何添加一個功能,該功能將測量每個區(qū)和舊金山之間的地理相似性:
sf_coords = 37.7749, -122.41
sf_transformer = FunctionTransformer(rbf_kernel,
kw_args=dict(Y=[sf_coords], gamma=0.1))
sf_simil = sf_transformer.transform(housing[["latitude", "longitude"]])
自定義transformers
對于組合功能也很有用。例如,這里有一個FunctionTransformer,它計算輸入特征0和1之間的比率:
FunctionTransformer非常方便,但是如果您希望您的轉(zhuǎn)換器是可訓(xùn)練的,在fit()方法中學(xué)習(xí)一些參數(shù),然后在transform()方法中使用它們,那該怎么辦呢?為此,您需要編寫一個自定義類。Scikit-Learn依賴于duck類型,所以這個類不必從任何特定的基類繼承。它只需要三個方法:fit()(必須返回self)、
transform () 和fit_transform()。
只需添加TransformerMixin作為基類,就可以免費獲得fit_transform():默認實現(xiàn)將只調(diào)用fit(),然后再調(diào)用transform()。如果將BaseEstimator添加為基類(并避免使用*args和**kwargs),您還將獲得兩個額外的方法:get_params ()和set_params()。這些對于自動超參數(shù)調(diào)優(yōu)非常有用。
例如,這里有一個自定義 transformer,其行為與StandardScaler非常相似:
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils.validation import check_array, check_is_fitted
class StandardScalerClone(BaseEstimator, TransformerMixin):def __init__(self, with_mean=True): # no *args or **kwargs!self.with_mean = with_meandef fit(self, X, y=None): # y is required even though we don't use itX = check_array(X) # checks that X is an array with finite float valuesself.mean_ = X.mean(axis=0)self.scale_ = X.std(axis=0)self.n_features_in_ = X.shape[1] # every estimator stores this in fit()return self # always return self!def transform(self, X):check_is_fitted(self) # looks for learned attributes (with trailing _)X = check_array(X)assert self.n_features_in_ == X.shape[1]if self.with_mean:X = X - self.mean_return X / self.scal
這里有幾點需要注意:
- sklearn.utils.validation包包含幾個我們可以用來驗證輸入的函數(shù)。為了簡單起見,我們將在本書的其余部分跳過這樣的測試,但是產(chǎn)品代碼應(yīng)該有這些測試。
- Scikit-Learn管道要求fit()方法有兩個參數(shù)X和y,這就是為什么我們需要y=沒有參數(shù),即使我們不使用y。
- 所有Scikit-Learn估計器都在fit()方法中設(shè)置n_features_in_,并且它們確保傳遞給transform()或predict()的數(shù)據(jù)具有此數(shù)量的特征。_
- .fit ()方法必須返回self。
- 這個實現(xiàn)不是100%完成的:所有的估算器都應(yīng)該在擬合中設(shè)置feature_
names_in_ ()方法時,它們被傳遞一個DataFrame。此外,所有的轉(zhuǎn)換器都
應(yīng)該提供get_feature_names_out()方法,以及當它們的轉(zhuǎn)換可以被逆轉(zhuǎn)時
的inverse_transform()方法。更多細節(jié)請參見本章最后一個練習(xí)。
自定義轉(zhuǎn)換器可以(而且經(jīng)常)在其實現(xiàn)中使用其他估計器。例如,以下代碼演示了自定義轉(zhuǎn)換器,該轉(zhuǎn)換器在fit()方法中使用KMeans聚類器來識別訓(xùn)練數(shù)據(jù)中的主要聚類,然后在transform()方法中使用rbf
kernel ()來衡量每個樣本與每個聚類中心的相似程度:
from sklearn.cluster import KMeans
class ClusterSimilarity(BaseEstimator, TransformerMixin):def __init__(self, n_clusters=10, gamma=1.0, random_state=None):self.n_clusters = n_clustersself.gamma = gammaself.random_state = random_statedef fit(self, X, y=None, sample_weight=None):self.kmeans_ = KMeans(self.n_clusters, random_state=self.random_state)self.kmeans_.fit(X, sample_weight=sample_weight)return self # always return self!def transform(self, X):return rbf_kernel(X, self.kmeans_.cluster_centers_, gamma=self.gamma)def get_feature_names_out(self, names=None):return [f"Cluster {i} similarity" for i in range(self.n_clusters)]
k-means是一種在數(shù)據(jù)中定位聚類的聚類算法。它搜索的數(shù)量由n_clusters超參數(shù)控制。訓(xùn)練后,可以通過cluster_centers_屬性使用聚類中心。KMeans的fit()方法支持一個可選參數(shù)sample_weight,該參數(shù)允許用戶指定樣本的相對權(quán)重。K-means是一種隨機算法,這意味著它依賴隨機性來定位聚類,因此若您想要可重現(xiàn)的結(jié)果,則必須設(shè)置random_state參數(shù)。正如您所看到的,盡管任務(wù)很復(fù)雜,但代碼相當簡單?,F(xiàn)在讓我們使用這個自定義的變壓器:
cluster_simil = ClusterSimilarity(n_clusters=10, gamma=1., random_state=42)
similarities = cluster_simil.fit_transform(housing[["latitude", "longitude"]],
sample_weight=housing_labels)
這段代碼創(chuàng)建一個ClusterSimilarity轉(zhuǎn)換器,將集群數(shù)設(shè)置為10.然后調(diào)用fit_transform(),輸入訓(xùn)練集中每個區(qū)的緯度和經(jīng)度,并根據(jù)每個區(qū)的房屋中值對每個區(qū)進行加權(quán)。變壓器采用k-均值定位聚類,
然后測量每個區(qū)域與所有10個聚類中心之間的高斯RBF相似性。結(jié)果是一個矩陣,每個區(qū)有一行,每個集群有一列。先看前三行,四舍五入到小數(shù)點后兩位:
下圖顯示了通過k-means找到的10個聚類中心。根據(jù)它們與最近的集群中心的地理相似性,對這些區(qū)域進行著色。正如您所看到的,大多數(shù)集群都位于人口密集且價格昂貴的地區(qū)。