Hands-On Data Preprocessing in Python-6 預測
能夠利用數據來預測未來正變得越來越可能。不僅如此;很快,能夠進行成功的預測性建模將不再是一種競爭優(yōu)勢--它將成為生存的必需品。為了提高預測性建模的有效性,許多人把注意力集中在用于預測的算法上;然而,你可以采取許多有意義的步驟,通過進行更有效的數據預處理來提高預測的成功率。這就是本書的最終目標:學習如何更有效地預處理數據。然而,在本章中,我們將朝著這個目標邁出非常重要的一步。在本章中,我們將學習預測性建模的基礎知識。當我們學習了數據預處理的概念和技術后,我們將依靠這些基礎知識來做出更好的數據預處理決策。
雖然許多不同的算法可以應用于預測性建模,但這些算法的基本概念都是一樣的。在本章中介紹了這些基本原理后,我們將介紹其中兩種在復雜性和透明度方面有區(qū)別的算法:線性回歸和多層感知器(MLP)。

1 預測模型
利用數據來預測未來是令人興奮的,而且利用數據分析是可以做到的。在數據分析領域,有兩種類型的未來預測,概述如下。
- 預測一個數值--例如,預測明年亞馬遜股票市場的價格。
- 預測一個標簽或一個類別--例如,預測一個客戶是否有可能停止購買你的服務而轉向你的競爭對手。
總的來說,當我們使用預測一詞時,我們指的是預測一個數值。
要預測一個類別或一個標簽,所使用的術語是分類。在本章中,我們將重點討論數據分析的預測目標,下一章將介紹分類。
對未來數值的預測也分為兩個主要的總體類型:預測和回歸分析。我們將簡要地解釋預測,然后再把注意力轉向回歸分析。
1.1 預測
在數據分析中,預測是指用于預測時間序列數據的未來數值的技術。預測的獨特之處在于它對時間序列數據的應用--例如,最簡單的預測方法是簡單移動平均線(SMA:simple moving average )。在這種方法下,你將使用最近的數據點來預測你的時間序列數據中未來數據點的數值。
1.1.1 使用預測法來預測未來的例子
我們來看看一個以移動平均數(MA)為特征的預測例子。下表顯示了密西西比州立大學(MSU)從2006年到2021年收到的學生申請的數量。
import pandas as pd
msu_df = pd.read_csv('https://raw.githubusercontent.com/PacktPublishing/Hands-On-Data-Preprocessing-in-Python/main/Chapter06/MSU%20applications.csv')
msu_df.set_index('Year',drop=True,inplace=True)
msu_df.head()

application_df = pd.DataFrame(msu_df.N_Applications)
application_df.transpose()

下面的截圖用線圖將前述表格中的數據可視化。
import matplotlib.pyplot as plt
plt.figure(figsize=(7,3))
plt.plot(application_df)
plt.xticks(application_df.index,rotation=90)
plt.show()

application_df.loc[2017:2021].mean()

average_df = pd.DataFrame(application_df.loc[2017:2021])
average_df['N_Applications']=15901.2
average_df.loc[2022]=15901.2
average_df

predict_df = pd.DataFrame(application_df.loc[2021]).transpose()
predict_df.loc[2022] = 15901.2
predict_df

plt.figure(figsize=(7,3))
plt.plot(application_df)
plt.plot(average_df,linestyle='--',c='C1')
plt.plot(predict_df,c='C1')
plt.xticks(application_df.index,rotation=90)
plt.show()
MSU,出于規(guī)劃的目的,希望對他們在2022年將收到多少新的申請有一些想法。一種方法是使用MA方法。
對于這種方法,你需要指定你想用于預測的數據點的數量。這通常用n來表示。讓我們使用五個數據點(n=5)。在這種情況下,你將在預測中使用2017、2018、2019、2020和2021年的數據。簡單地說,你計算這些年的平均申請數量,并將其作為下一年的估計預測。13,930、13,817、17,363、18,269和16,127的平均值是15,901.2,這可以作為2022年申請數量的估計。
下面的截圖描述了n=5的MA的應用。

使用時間序列數據進行預測還有更復雜的方法,如加權MA,指數平滑,雙指數平滑,等等。
我們在本書中不涉及這些方法,因為所有時間序列數據所需的數據預處理都是一樣的。然而,你想從預測中記住的是,這些方法在單維時間序列數據上的預測工作。
例如,在MSU的例子中,我們唯一的數據維度是N_ Applications。
這種單一維度與我們將要介紹的下一個預測方法形成了鮮明的對比?;貧w分析,與預測相反,找到多個屬性之間的關系來估計其中一個屬性的數值。
1.2 回歸分析
回歸分析的任務是利用預測屬性和目標屬性之間的關系來預測數值。
目標屬性是指我們對其數值的預測感興趣的屬性。從屬屬性一詞是用于同一概念的另一個名稱。從屬屬性的含義來自于這樣一個事實:目標屬性的值取決于其他屬性;我們把這些屬性稱為預測器或獨立屬性。
許多不同的方法可用于回歸分析。只要這些方法是為了尋找獨立屬性和因果屬性之間的關系以預測因果屬性,我們就把這些方法歸入回歸分析。當然,線性回歸是最簡單而又廣泛使用的回歸分析方法之一,也是這些方法之一。然而,其他技術,如MLP和回歸樹,也被歸入回歸分析。
1.2.1 設計回歸分析以預測未來價值的例子
例如,對下一年MSU申請數量的預測也可以用回歸分析來建模。下圖顯示了有可能預測 "申請人數 "因果屬性的兩個獨立屬性。在這個例子中,你可以看到預測模型涉及到不止一個維度,我們有三個維度--兩個獨立屬性和一個因果屬性。
第一個獨立屬性,前一年的足球表現,是MSU足球隊的勝率。第二個獨立屬性是過去兩年的平均申請數量。

第二個獨立屬性很有意思,因為它描述了你可以通過將預測方法的值作為回歸分析的獨立屬性來對接預測方法和回歸分析。過去兩年的平均申請數量是SMA方法的值,n=2。
我們如何找到可能的獨立屬性?你可以看到在回歸分析中,擁有適當的獨立屬性來預測感興趣的屬性(因果屬性)的重要作用。
設想和收集可能的預測因素(獨立屬性)是進行成功回歸分析的最重要部分。
到目前為止,你已經在本書中學到了寶貴的技能,可以幫助你設想可能的預測器。你在第4章 "數據庫 "中積累的理解將使你能夠想象可能的情況,并搜索和收集這些數據。
在未來的一章,即第12章,數據融合和整合,你將學到所有的技能,去整合來自不同來源的數據,以支持你的回歸分析。
一旦確定了獨立和因果屬性,我們就完成了回歸分析并建立了模型。接下來,我們需要采用適當的算法來尋找這些屬性之間的關系,并利用這些關系進行預測。
在本章中,我們將介紹兩種非常不同的算法,可以做到這一點:線性回歸和MLP。
2 線性回歸
線性回歸這個名字會告訴你所有你需要知道的事情--回歸部分告訴你這個方法進行回歸分析,線性部分告訴你這個方法假設屬性之間存在線性關系。
為了找到屬性之間的可能關系,線性回歸假設并建立一個通用方程,將目標(因果屬性)與預測因子(獨立屬性)聯系起來。這個方程在這里被描繪出來。

這個方程采用的是參數法。在這個方程中,N代表預測因子的數量,顯示了線性回歸的通用方程。
線性回歸的工作非常簡單。該方法首先估計βs,使方程最適合數據,然后使用估計的βs進行預測。
讓我們通過一個例子來學習這個方法。我們將在下面的例子中繼續(xù)解決MSU的應用數量。
2.1 應用線性回歸來進行回歸分析的例子
到目前為止,我們已經確定了我們的獨立和因果屬性,所以我們可以顯示這個例子的線性回歸方程。該方程顯示在這里。

MSU applications.csv數據集有我們需要的所有屬性來估計βs。
讓我們先讀一下這個數據,看一下它。下面的截圖顯示了我們?yōu)樽x取數據和整個數據集所運行的代碼。
import pandas as pd
msu_df = pd.read_csv('https://raw.githubusercontent.com/PacktPublishing/Hands-On-Data-Preprocessing-in-Python/main/Chapter06/MSU%20applications.csv')
msu_df.set_index('Year',drop=True,inplace=True)
msu_df.head()

在這個數據集中,我們有以下屬性。
- P_Football_Performance。這個屬性是MSU足球隊在上一學年的總勝率。
- SMAn2。這個屬性是n=2的SMA的計算值。例如,2009行的SMAn2是2008年和2007年的N_Applications屬性的平均值。在繼續(xù)閱讀之前,請確認這一計算結果。
- N_Applications。這與我們在圖6.1和圖6.2中看到的數據相同。這是我們感興趣的預測的從屬屬性。
我們將使用scikit-learn模塊用msu_df來估計這些βs,所以首先,我們需要在Anaconda平臺上安裝這個模塊。運行下面的代碼將安裝該模塊。
%pip install scikit-learn
一旦安裝完畢,你需要導入該模塊以開始每次使用它,就像我們一直在使用的其他模塊一樣。然而,由于scikit-learn相當大,我們每次都會準確地導入我們想要使用的東西。例如,下面的代碼只從模塊中導入 LinearRegression 函數:from sklearn.linear_model import LinearRegression
現在,我們有一個函數可以使用msu_df無縫地計算我們模型的βs。我們現在只需要以適當的方式將數據引入LinearRegression()函數。
我們可以分四步來完成,如下所示。
1.首先,我們將指定我們的獨立和因果屬性,通過指定X和Y的變量列表。請看下面的代碼片斷。
X = ['P_Football_Performance','SMAn2']
y= 'N_Applications'
2.其次,我們將使用X和Y的列表從msu_df創(chuàng)建兩個獨立的數據集:data_X和data_y。data_X是一個包含所有獨立屬性的DataFrame,data_y是一個系列,是依賴屬性。下面的代碼顯示了這一點。
這一步和上一步本可以與下一步合并;然而,最好是保持你的代碼干凈整潔,我強烈建議使用我的指導方針,至少在開始時是這樣。
data_X = msu_df[X]
data_y = msu_df[y]
3.接下來,我們將創(chuàng)建模型并引入數據。下面的代碼將做到這一點。我們創(chuàng)建一個線性回歸模型,稱其為lm,并向其引入數據。
lm = LinearRegression()
lm.fit(data_X,data_y)
當你運行下面的代碼時,幾乎什么都沒有發(fā)生,但是不要擔心--模型已經完成了它的工作,我們只需要在下一步訪問估計的βs。
4.如圖所示,估計的βs是在訓練好的lm模型內。我們可以用lm.intercept_來訪問β0,lm.coef_將顯示β1和β2。下面的代碼會打印出一份有組織的報告,其中包括所有的β0實例。
print('intercept (b0)',lm.intercept_)
coef_name = ['b1','b2']
print(pd.DataFrame({
??? 'Predictor':data_X.columns,
??? 'coefficient Name':coef_name,
??? 'coefficient Value':lm.coef_
}))
完整代碼
from sklearn.linear_model import LinearRegression
X = ['P_Football_Performance','SMAn2']
y= 'N_Applications'
data_X = msu_df[X]
data_y = msu_df[y]
lm = LinearRegression()
lm.fit(data_X,data_y)
print('intercept (b0)',lm.intercept_)
coef_name = ['b1','b2']
print(pd.DataFrame({
??? 'Predictor':data_X.columns,
??? 'coefficient Name':coef_name,
??? 'coefficient Value':lm.coef_
}))

現在我們已經估計了回歸模型的βs,我們可以引入我們的訓練模型。下面的方程式顯示了訓練后的回歸方程式。

2.2 如何使用訓練好的回歸方程進行預測
為了使用該方程式來預測2022年MSU的申請數量,MSU需要把2022年的P_Football_performance和SMAn2屬性放在一起。這里,我們介紹一下尋找這些數值的過程。
- P_Football_performance。在撰寫本章時(2021年4月),2020-21年的大學橄欖球賽季已經結束,MSU在11場比賽中取得了4場勝利,勝率達到0.364。
- SMAn2。2021年和2020年的N_Applications值分別為18,269和16,127。這些數字的平均值是17,198。
下面是預測2022年的N_Applications值的計算。

我們不需要自己做前面的計算,我們這樣做是為了學習。
我們可以使用所有scikit-learn預測模型附帶的.predict()函數。下面的屏幕截圖顯示了如何做到這一點。
newData = pd.DataFrame({
??? 'P_Football_Performance':0.364,
??? 'SMAn2':17198,
??? },index=[2022])
newData

lm.predict(newData) #array([16726.78787061])
前面的方程式計算和編程計算之間有一些區(qū)別。一個達到了16777.82,另一個達到了16726.78。這個差異是由于我們在呈現回歸方程時做了四舍五入的處理。.predict()函數得出的數值,16726.78,是比較準確的。
請注意! 線性回歸,以及一般的回歸分析,是一個非常成熟的分析領域。有許多評估方法和程序來確保我們所創(chuàng)建的模型具有良好的質量。在本書中,我們將不涉及這些概念,因為本章的目標是介紹可能需要數據預處理的技術。通過了解這些技術的機制,你將能夠更有效地進行數據預處理。
??? https://realpython.com/linear-regression-in-python/??
??? https://www.statsmodels.org/stable/generated/statsmodels.regression.linear_model.RegressionResults.html??

現在我們已經完成了這個預測,回過頭來看看,研究一下線性回歸的工作。在這里,線性回歸實現了以下兩個目標。
1. 線性回歸用它的普遍性和線性方程來尋找獨立屬性和依賴屬性之間的關系。每個獨立屬性的β系數告訴你獨立屬性與因果屬性的關系--例如,SMAn2的系數,β2,得出的是0.91。這意味著,即使MSU橄欖球隊輸掉了所有的比賽(這使得N_Football_Performance的值為零),明年,申請人數將是一個-890.71+0.91×SMAn2的等式。
2. 線性回歸方程已將估計的關系打包成一個方程,可用于未來的觀察。
這兩個事項,即關系的提取和估計以及為未來的數據對象包裝估計的關系,對于任何預測模型的正常工作都是至關重要的。
線性回歸的偉大之處在于,這些事項的簡單性可以被看到和欣賞到。這種簡單性有助于理解線性回歸的工作和理解它所提取的模式。然而,就其在估計和包裝獨立和因果屬性之間更復雜的非線性關系的范圍而言,這種簡單性對該方法不利。
接下來在本章中,我們將簡要介紹另一種處于另一端的預測算法。MLP是一種復雜的算法,能夠找到并包裝獨立和依賴屬性之間更復雜的模式,但它缺乏線性回歸的透明度和直觀性。
3 MLP(Multilayer Perceptron:多層感知器)
MLP是一個非常復雜的算法,有很多細節(jié),抽象地去看它的運作和不同的部分會很難理解。因此,讓我們通過一個例子來深入了解。在本節(jié)中,我們將繼續(xù)使用MSU的應用數量。
線性回歸使用一個方程式,而MLP使用一個神經元網絡來連接獨立屬性和因果屬性。這樣一個網絡的例子顯示在下面的截圖中。

每個MLP網絡都有六個不同的部分。讓我們用圖來看看這些部分,如下所示。
- 神經元(Neurons)。圖中的每個圓圈都被稱為神經元。一個神經元可以在輸入層、輸出層和隱藏層。我們將在下一節(jié)中介紹三種樹型的層。
- 輸入層(Input layer)。一個由神經元組成的層,數值從這里輸入到網絡。
在預測任務中,對于獨立屬性的數量之多,我們將在輸入層有神經元。在圖中,你可以看到我們在輸入層有兩個神經元,每個獨立屬性一個。
- 輸出層(Output layer)。一個由神經元組成的層,網絡的處理值從這里出來。在預測任務中,只要有多少個從屬屬性,我們就會在輸出層有神經元。更多時候,我們只有一個從屬屬性。圖也是如此,因為我們的預測任務只有一個依賴屬性,網絡在輸出層只有一個神經元。
- 隱蔽層(Hidden layers)。在輸入和輸出層之間的一個或多個神經元層。隱蔽層的數量和每個隱蔽層中的神經元數量可以--而且應該--根據所需的模型復雜性和計算成本水平進行調整。例如,圖只有一個隱藏層,隱藏層中有六個神經元。
- 連接(Connections)。將一個層的神經元連接到下一層的線被稱為連接。這些連接必須從一層到下一層詳盡地存在;詳盡地意味著左層的所有神經元都與右層的所有神經元相連。
3.1 MLP怎樣工作的
MLP的工作方式既與線性回歸相似,又與線性回歸不同。讓我們先看看它們的相似之處,然后再介紹它們的區(qū)別。這里列出了它們的相似之處。
- 線性回歸依靠其結構化方程來捕捉獨立屬性和因果屬性之間的關系。MLP也依靠其網絡結構來捕捉相同的關系。
- 線性回歸估計βs是一種使用其結構化方程來適應數據的方式,從而找到獨立屬性和因果屬性之間的關系。MLP也為其結構上的每一個連接估計一個值,以使自己適合數據;這些值被稱為連接的權重。
因此,線性回歸和MLP都使用數據來更新自己,以便它們能夠使用其預定義的結構來解釋數據。
- 一旦線性回歸的βs和MLP的連接權重被正確地用數據估計出來,這兩種算法就可以用來預測新的案例。
我們可以看到,這兩種算法非常相似;但是,它們也有許多不同之處?,F在讓我們來看看這些,如下。
- 線性回歸算法的結構方程是固定的、簡單的,而MLP的結構是可調整的,可以設置為非常復雜。從本質上講,MLP結構的隱藏層和神經元越多,該算法就越能捕捉到更復雜的關系。
- 線性回歸依靠成熟的數學公式來估計βs,而MLP則必須借助啟發(fā)式方法和計算來估計數據的最佳連接權重。
最著名的啟發(fā)式方法是用來估計MLP的連接權重的,叫做反向傳播法。這種啟發(fā)式方法在本質上是非常簡單的;然而,對其進行編碼并使其發(fā)揮作用可能是很棘手的。對我們來說,好消息是我們不必擔心編碼問題,因為我們可以使用一些穩(wěn)定的模塊。然而,在看看我們如何使用上述模塊之前,讓我們先把它的簡單想法看一遍。
3.1.1 反向傳播
對于反向傳播,每個連接的權重首先被分配一個介于-1和1之間的隨機數。是的,這是完全隨機的,它被稱為MLP的隨機初始化。
在MLP的隨機初始化之后,該算法將能夠對任何輸入的數據對象進行預測值。當然,這些預測將是錯誤的。逆向傳播利用這些錯誤和這些錯誤的程度來學習。
每當一個數據對象暴露在MLP網絡中時,MLP就會期望其依賴屬性。如前所述,這種期望是錯誤的,至少在開始時是如此。因此,反向傳播計算每次接觸的網絡錯誤,在網絡上向后移動,并更新連接的權重,這樣如果再次接觸相同的數據對象,錯誤量會少一點。
網絡將不止一次地暴露在數據集中的所有數據對象面前。每次所有的數據對象被暴露給網絡,我們稱之為一個學習周期(epoch of learning)。
逆向傳播使網絡經歷了足夠多的歷時學習,從而使網絡的集體誤差量可以接受。
現在我們對MLP及其主要的啟發(fā)式方法有了大致的了解,讓我們一起看看使用scikit-learn模塊來執(zhí)行MLP預測任務的例子。
3.2 應用MLP進行回歸分析的例子
為了使用scikit-learn模塊實現MLP,我們需要采取與線性回歸相同的四個步驟。簡而言之,這四個步驟列舉如下。
1. 指定我們的獨立和依賴屬性
2. 創(chuàng)建兩個獨立的數據集:Data_X和Data_y
3. 創(chuàng)建一個模型并引入數據
4. 預測
下面的代碼片段顯示了這四個步驟被應用于MSU應用程序的數量問題。它顯示了首先從sklearn.neural_network模塊導入MLPRegressor類。
from sklearn.neural_network import MLPRegressor
X = ['P_Football_Performance','SMAn2']
y = 'N_Applications'
data_X = msu_df[X]
data_Y = msu_df[y]
mlp = MLPRegressor(hidden_layer_sizes=6,max_iter=10000)
mlp.fit(data_X,data_y)
newData = pd.DataFrame({
??? 'P_Football_Performance':0.364,
??? 'SMAn2':17198,
??? },index=[2022])
mlp.predict(newData) #array([18812.15879652])
該代碼與我們用于線性回歸的代碼幾乎相同,但有一些小的改動。讓我們來看看這些,如下所示。
- 我們沒有使用LinearRegression()創(chuàng)建lm,而是使用MLPRegressor()創(chuàng)建mlp。
- LinearRegression()函數不需要任何輸入,因為線性回歸是一個簡單的算法,沒有超參數。但是MLPRegressor()至少需要兩個輸入,即hidden_layer_sizes=6和max_iter=10000。第一個輸入(hidden_layer_sizes=6)指定了網絡結構。通過只輸入一個數字,我們表明我們只有一個隱藏層,而通過使用數字6,我們表明隱藏層有六個神經元。這與我們在圖中看到的網絡設計是一致的。第二個輸入(max_ iter=10000)表示在模塊放棄收斂之前,你希望至少有10,000個epochs的學習。
如果你成功地運行了幾次前面的代碼,你會觀察到以下兩個總體趨勢。
- 代碼每次都會輸出一個有點不同的newData的預測值,但這些值都在18000左右。
- 在一些運行中,代碼也會產生一個警告。這個警告是指MLP算法在經過1萬次的學習后也無法收斂。
現在,讓我們來討論這兩個趨勢。
3.2.1 MLP在每次運行中達到不同的預測結果
我們來討論一下第一個觀察結果。代碼每次都會為newData輸出一個有些不同的預測值,但這些值都在18000左右。
MLP是一種基于隨機的算法。如果你還記得我們的反向傳播學習,每次網絡被初始化時,都會給每個連接分配一個介于-1和1之間的隨機數。然后,這些值會被更新,以便網絡更好地適應數據;然而,開始是隨機的,因此結果也會不同。
然而,如果你注意基于隨機的模型得出的這些不同的結論,你會發(fā)現,即使它們是不同的,但它們在某種程度上是一致的。
它們都在18,000左右。這表明,基于隨機的程序能夠在數據中找到類似的、有意義的模式。
3.2.2 需要大量歷時學習的MLP
現在我們來討論第二個觀察結果。在某些運行中,代碼也會產生一個警告。這個警告是指MLP算法在經過10000個epochs的學習后也無法收斂。
由于我們永遠不知道基于隨機的算法何時會收斂,所以我們必須給學習的epochs數量設置一個上限。事實上,有10,000個歷時的學習是很奢侈的,我們能負擔得起只是因為數據只有16個數據對象。
MLPRegressor()的max_iter的默認值是200。這意味著如果我們沒有指定max_iter=10000,該函數就會假定max_iter=200。在這種情況下,這就意味著算法不會更頻繁地收斂,其結論也不會那么一致。試一試,觀察一下前述的模式。
請注意! MLP是一個非常復雜和靈活的算法;在這里,我們只討論了它的兩個超參數(hidden_layer_sizes和max_iter),但它還有很多,要成功使用MLP,你需要先對它進行調整。調整算法就是要找到對數據集最有效的超參數。我們不會在這里介紹MLP是如何調整的,因為我們只需要對該算法有一個基本的了解,這樣它就能支持我們的數據預處理之旅。
此外,就像線性回歸一樣,MLP在實施前應嚴格評估其有效性和可靠性。出于同樣的原因,我們在本書中也不會使用這些概念和技術。
??? http://zh.d2l.ai/chapter_multilayer-perceptrons/index.html??
參考資料:
[1] Jafari R. Hands-On Data Preprocessing in Python: Learn how to effectively prepare data for successful data analytics[M]. 第 1st 版. Packt Publishing, 2022.