Python機器學習專案實戰
編輯推薦: |
本文來自於cloud.tencent.com,作者以淺顯易懂的語言和清晰的示例和程式碼教你從頭開始走過一個機器學習之旅,並且附詳細的程式碼,大家可以收藏和學習。 |
這是一篇完全手把手進行機器學習專案構建的教程,包含:1. 資料清理和格式化 2. 探索性資料分析 3. 特徵工程和特徵選擇 4. 在效能指標上比較幾種機器學習模型 5. 對最佳模型執行超引數調整 6. 在測試集合中評估最佳模型 7. 解釋模型結果 8. 得出結論。今天是(1-3)從資料清理,到資料分析,到特徵工程,再到Baseline的構建.
——Putting the machine learning pieces together
閱讀一本資料科學書籍或學習一門相關的課程,你可能感覺你有了獨立的碎片,但不知道如何將它們拼在一起。想要繼續推進下去並解決完整的機器學習問題可能令人望而生畏,但完成第一個專案後將使你有信心應對任何資料科學問題。本系列文章將介紹——使用了真實世界資料集的機器學習專案的完整解決方案,讓你瞭解所有碎片是如何拼接在一起的。
我們將按照一般機器學習的工作流程逐步進行:
1. 資料清理和格式化
2. 探索性資料分析
3. 特徵工程和特徵選擇
4. 在效能指標上比較幾種機器學習模型
5. 對最佳模型執行超引數調整
6. 在測試集合中評估最佳模型
7. 解釋模型結果
8. 得出結論
按照上述流程,我們將介紹每個步驟如何進入到下一步,以及如何用Python實現每個部分。完整的專案在GitHub上可以找到,第一個notebook在這裡。 第一篇文章將涵蓋步驟1-3,其餘的內容將在後面的文章中介紹。
GitHub完整 ofollow,noindex" target="_blank">專案連結 :
問題定義
編碼之前的第一步是瞭解我們試圖解決的問題和可用的資料。在這個專案中,我們將使用公共可用的紐約市的建築能源資料【1】。
目標是使用能源資料建立一個模型,來預測建築物的Energy Star Score(能源之星分數),並解釋結果以找出影響評分的因素。
資料包括Energy Star Score,意味著這是一個監督迴歸機器學習任務:
監督:我們可以知道資料的特徵和目標,我們的目標是訓練可以學習兩者之間對映關係的模型。
迴歸:Energy Star Score是一個連續變數。
我們想要開發一個模型,在準確性上——它可以實現預測Energy Star Score,並且結果接近真實值。在解釋上—— 我們可以理解模型的預測。
一旦我們知道了目標,在深入挖掘資料並構建模型時,就可以用它來指導我們的決策。
資料清洗
與大多數資料科學課程所相信的相反,並非每個資料集都是一組完美的觀測資料,沒有缺失值或異常值(你可以檢視你的mtcars【2】和iris資料集【3】)。 現實世界的資料很亂,這意味著在我們開始分析之前,我們需要清理並將其轉換為可接受的格式【4】。資料清理,是大多數實際的資料科學問題中不具吸引力,但必不可少的一部分。
首先,我們可以將資料用Pandas DataFrame載入並檢視:
import pandas as pd
import numpy as np
# Read in data into a dataframe
data = pd.read_csv('data/Energy_and_Water_Data_Disclosure_for_
Local_Law_84_2017__Data_for_Calendar_Year_2016_.csv')
# Display top of dataframe
data.head()
這是包含60列的完整資料的子集。 我們已經可以看到幾個問題:首先,我們知道我們想要預測的Energy Star Score的情況,但我們不知道任何一列的含義。 雖然這不一定是個問題——我們通常可以在沒有任何變數知識的情況下建立一個準確的模型——我們想把重點放在模型的可解釋性上,而至少了解一些列可能是重要的。
起初,我從初創階段得到任務時,我不想問所有的列名是什麼意思,所以我查看了csv檔案的名稱,
並決定搜尋“Local Law 84”。 所以在上文我解釋——這是紐約法律要求的,所有具有一定規模的建築物報告其能源使用情況。 關於列的更多搜尋內容在這裡。 也許看一個檔名是一個明顯的開始,但對我來說,這是提醒你要放慢速度,這樣你才不會錯過任何重要的東西!
我們不需要研究所有的列的定義,但我們至少應該瞭解Energy Star Score,它被描述為:
根據報告年度中,自我報告的能源使用情況而進行的1至100百分位的排名。 Energy Star Score是用於比較建築物能效的相對度量。【5】
這解決了第一個小問題,但第二個問題是缺少的值被編碼為“Not Available”。 這是Python中的一個字串,這意味著甚至包含數字的列都將被儲存為object資料型別,因為Pandas會將包含任何字串的列轉換為所有元素都為字串的列。我們可以使用dataframe.info()方法來檢視列的資料型別:
# See the column data types and non-missing values
data.info()
當然,一些明確包含數字(例如ft2)的列被儲存為object型別。 我們不能對字串進行數值分析,因此必須將其轉換為數字(特別是浮點數)資料型別!
這裡有一個簡短的Python程式碼,用不是數字(np.nan)代替所有“Not Available”條目,np.nan可以被解釋為數字,這樣就可以將相關列轉換為float資料型別:
# Replace all occurrences of Not Available with numpy not a number
# Iterate through the columns
for col in list(data.columns):
# Select columns that should be numeric
if ('ft2' in col or 'kBtu' in col or 'Metric Tons CO2e' in col
or 'kWh' in
col or 'therms' in col or 'gal' in col or 'Score' in col):
# Convert the data type to float
data[col] = data[col].astype(float)
一旦列是數字,我們就可以開始進行調查資料。
缺少資料和異常值
除了不正確的資料型別外,處理真實世界資料時的另一個常見問題是缺失值。 這可能是由於許多原因引起的,在我們訓練機器學習模型之前必須填寫或刪除。首先,讓我們瞭解每列中有多少缺失值(請參閱notebook中的程式碼)。
(為了建立這個表,我使用了這個Stack Overflow論壇的一個函式【6】)。
儘管我們總是希望小心刪除資訊,但如果列中缺失值的比例很高,那麼它對我們的模型可能不會有用。刪除列的閾值應該取決於實際問題,並且對於此專案,我們將刪除缺失值超過50%的列。
此時,我們可能還想要移除異常值。這些異常可能是由於資料輸入中的拼寫錯誤,單位中的錯誤,或者它們可能是合法但是極端的值。對於這個專案,我們將根據極端異常值的定義來消除異常:
1. the first quartile ? 3 ? interquartile range
2. he third quartile + 3 ? interquartile range
(有關刪除列和異常的程式碼,請參閱notebook)。 在資料清理和異常清除過程結束時,我們剩下11,000多個建築物和49個特徵。
探索性資料分析
現在,資料清理這個乏味但必要的步驟已經完成,我們可以繼續探索我們的資料!探索性資料分析(EDA)是一個開放式的過程,我們可以計算統計資料,並畫圖去發現數據中的趨勢,異常,模式或關係。(trends, anomalies, patterns, or relationships)
簡而言之,EDA的目標是瞭解我們的資料可以告訴我們什麼。它通常以高層概述開始,在我們發現有趣的資料部分後,再縮小到特定的區域。這些發現本身可能很有意思,或者可以用於通知我們的建模選擇,例如幫助我們決定使用哪些特徵。
單變數圖
目標是預測Energy Star Score(將其重新命名為score),因此合理的開始是檢查此變數的分佈。直方圖是視覺化單個變數分佈的簡單而有效的方法,使用matplotlib很容易。
import matplotlib.pyplot as plt
# Histogram of the Energy Star Score
plt.style.use('fivethirtyeight')
plt.hist(data['score'].dropna(), bins = 100, edgecolor = 'k');
plt.xlabel('Score'); plt.ylabel('Number of Buildings');
plt.title('Energy Star Score Distribution');
這看起來很可疑! Energy Star Score是百分位數,這意味著我們期望看到一個統一的分佈,即每個得分分配給相同數量的建築物。然而,不成比例的建築物具有最高的100分或最低的1分(對於Energy Star Score來說更高表示越好)。
如果我們回到score的定義,我們會看到它基於“自我報告能量使用”,這可能解釋了分數偏高——要求建築物所有者報告自己的能源使用情況就像要求學生在測試中報告自己的分數! 因此,這可能不是衡量建築能效的最客觀標準。
如果我們有無限的時間,我們可能想要調查為什麼這麼多建築物有非常高和非常低的分數——我們可以通過選擇這些建築物並檢視它們的共同點。但是,我們的目標只是預測分數,而不是設計更好的建築物評分方法! 我們可以在我們的報告中記下分數具有可疑分佈,但我們主要關注預測分數。
尋找關係
EDA的主要部分是搜尋特徵和目標之間的關係。與目標相關的變數對模型很有用,因為它們可用於預測目標。通過使用seaborn庫的密度圖可以檢查目標上的分類變數(僅採用有限的一組值)的效果。
密度圖可以被認為是平滑的直方圖,因為它顯示了單個變數的分佈。我們可以按類別對密度圖進行著色,以檢視分類變數如何改變分佈。下面的程式碼建立了一個用建築物型別(僅限於具有超過100個數據點的建築物型別)著色的Energy Star Score密度圖:
# Create a list of buildings with more than 100 measurements
types = data.dropna(subset=['score'])
types = types['Largest Property Use Type'].value_counts()
# Plot of distribution of scores for building categories
figsize(12, 10)
# Plot each building
for b_type in types:
# Select the building type
subset = data[data['Largest Property Use Type'] == b_type]
# Density plot of Energy Star scores
sns.kdeplot(subset['score'].dropna(),
label = b_type, shade = False, alpha = 0.8);
# label the plot
plt.xlabel('Energy Star Score', size = 20); plt.ylabel('Density',
size = 20);
plt.title('Density Plot of Energy Star Scores by Building Type',
size = 28);
我們可以看到建築型別對Energy Star Score有重大影響。 辦公樓往往有較高的分數,而酒店的分數較低。這告訴我們,我們應該在建模中包含建築型別,因為它確實對目標有影響。 作為分類變數,我們將不得不對建築物型別進行one-hot編碼。
同上,可以顯示自治市鎮的Energy Star Score:
自治市鎮對建築型別的評分似乎沒有太大的影響。 儘管如此,我們可能希望將其納入我們的模型中,因為各區之間存在細微的差異。
為了量化變數之間的關係,我們可以使用Pearson相關係數。它可以用來衡量兩個變數之間的線性關係的強度和方向。 +1分是完美的線性正相關關係,-1分是完美的負線性關係。 相關係數的幾個值如下所示:
雖然相關係數無法捕捉非線性關係,但它是開始計算變數如何相關的好方法。 在Pandas中,我們可以輕鬆計算資料框中任何列之間的相關性:
# Find all correlations with the score and sort
correlations_data = data.corr()['score'].sort_values()
與目標的最負面(左)和最正面(右)相關性:
特徵與目標之間存在幾個強烈的負相關性,而EUI對目標最為負面。(這些測量方法在計算方式上略有不同)EUI——能源使用強度(Energy Use Intensity)——是建築物使用的能源量除以建築物的平方英尺。它意味著衡量一個建築,效率越低越好。直觀地說,這些相關性是有意義的:隨著EUI的增加,Energy Star Score趨於下降。
雙變數圖
為了視覺化兩個連續變數之間的關係,我們使用散點圖。我們可以在點的顏色中包含附加資訊,例如分類變數。例如,下面的圖表顯示了不同建築物型別的Energy Star Score與site EUI的關係:
這個圖讓我們可以看到-0.7的相關係數。 隨著Site EUI減少,Energy Star Score增加,這種關係在建築型別中保持穩定。
我們將做的最後的探索性plot被稱為Pairs Plot。這是一個很好的探索工具,因為它可以讓我們看到多個變數對之間的關係以及單個變數的分佈。在這裡,我們使用seaborn視覺化庫和PairGrid函式來建立上三角上具有散點圖的配對圖,對角線上的直方圖以及下三角形上的二維核密度圖和相關係數。
# Extract the columns to plot
plot_data = features[['score', 'Site EUI (kBtu/ft2)',
'Weather Normalized Source EUI (kBtu/ft2)',
# Replace the inf with nan
plot_data = plot_data.replace({np.inf: np.nan, -np.inf: np.nan})
# Rename columns
plot_data = plot_data.rename(columns = {'Site EUI (kBtu/ft2)':
'Site EUI',
'Weather Normalized Source EUI (kBtu/ft2)': 'Weather Norm EUI',
'log_Total GHG Emissions (Metric Tons CO2e)': 'log GHG Emissions'})
# Drop na values
plot_data = plot_data.dropna()
# Function to calculate correlation coefficient between two columns
def corr_func(x, y, **kwargs):
r = np.corrcoef(x, y)[0][1]
ax = plt.gca()
ax.annotate("r = {:.2f}".format(r),
xy=(.2, .8), xycoords=ax.transAxes,
size = 20)
# Create the pairgrid object
grid = sns.PairGrid(data = plot_data, size = 3)
# Upper is a scatter plot
grid.map_upper(plt.scatter, color = 'red', alpha = 0.6)
# Diagonal is a histogram
grid.map_diag(plt.hist, color = 'red', edgecolor = 'black')
# Bottom is correlation and density plot
grid.map_lower(corr_func);
grid.map_lower(sns.kdeplot, cmap = plt.cm.Reds)
# Title for entire plot
plt.suptitle('Pairs Plot of Energy Data', size = 36, y = 1.02);
要檢視變數之間的互動,我們查詢行與列相交的位置。例如,要檢視Weather EUorm EUI與score的相關性,我們檢視Weather EUorm EUI行和score列,並檢視相關係數為-0.67。 除了看起來很酷之外,諸如這些圖可以幫助我們決定在建模中應該包含哪些變數。
特徵工程和選擇
特徵工程和選擇通常會為機器學習問題投入最大的時間。首先,讓我們來定義這兩個任務是什麼:
特徵工程:獲取原始資料並提取或建立新特徵的過程。這可能意味著需要對變數進行變換,例如自然對數和平方根,或者對分類變數進行one-hot編碼,以便它們可以在模型中使用。 一般來說,我認為特徵工程是從原始資料建立附加特徵。
特徵選擇:選擇資料中最相關的特徵的過程。在特徵選擇中,我們刪除特徵以幫助模型更好地總結新資料並建立更具可解釋性的模型。一般來說,我認為特徵選擇是減去特徵,所以我們只留下那些最重要的特徵。
機器學習模型只能從我們提供的資料中學習,因此確保資料包含我們任務的所有相關資訊至關重要。如果我們沒有給模型提供正確的資料,那麼我們將它設定為失敗,我們不應該期望它學習!
對於這個專案,我們將採取以下功能設計步驟:
One-hot編碼分類變數(borough and property use type)。
新增數值變數的自然對數轉換。
在模型中,分類變數的One-hot編碼是必要的。機器學習演算法無法理解像“office”這樣的建築型別,因此如果建築物是辦公室,則必須將其記錄為1,否則將其記錄為0。
新增轉換特徵可以幫助我們的模型學習資料中的非線性關係。採用平方根,自然對數或特徵的次冪是資料科學中的常見做法,也是基於領域知識或在實踐中最有效的方法。這裡我們將使用數字特徵的自然對數。
以下程式碼選擇數字特徵,對這些特徵進行對數轉換,選擇兩個分類特徵,對這些特徵進行one-hot編碼,然後將兩個特徵結合在一起。這似乎需要做很多工作,但在pandas中相對簡單!
# Copy the original data
# Select the numeric columns
numeric_subset = data.select_dtypes('number')
# Create columns with log of numeric columns
for col in numeric_subset.columns:
# Skip the Energy Star Score column
if col == 'score':
next
else:
numeric_subset['log_' + col] = np.log(numeric_subset[col])
# Select the categorical columns
categorical_subset = data[['Borough', 'Largest Property Use Type']]
# One hot encode
categorical_subset = pd.get_dummies(categorical_subset)
# Join the two dataframes using concat
# Make sure to use axis = 1 to perform a column bind
features = pd.concat([numeric_subset, categorical_subset], axis = 1)
在這個過程之後,我們有超過11,000個具有110列(特徵)的觀測值(建築物)。並非所有這些特徵都可能對預測Energy Star Score有用,所以現在我們將轉向特徵選擇從而去除一些變數。
特徵選擇
我們資料中的110個特徵中的許多特徵是多餘的,因為它們彼此高度相關。 例如,以下是Site EUI與Weather Normalized SiteEUI的相關係數為0.997的圖。
相互強相關的特徵被稱為共線,消除這些特徵對中的一個變數通常可以幫助機器學習模型推廣並更易於解釋。(我應該指出,我們正在討論特徵與其他特徵的相關性,而不是與目標的相關性,這有助於我們的模型!)
有許多方法可以計算特徵之間的共線性,其中最常見的是方差擴大因子。在這個專案中,我們將使用相關係數來識別和刪除共線特徵。如果它們之間的相關係數大於0.6,我們將放棄一對特徵中的一個。對於實現,看看notebook(和這個stack overflow答案)
雖然這個閾值可能看起來是任意的,但我嘗試了幾個不同的閾值,這個選擇產生了最好的模型機器學習是一個經驗性領域,通過試驗來發現效能最好的!特徵選擇後,我們剩下64個特徵和1個目標。
# Remove any columns with all na values
features = features.dropna(axis=1, how = 'all')
print(features.shape)
(11319, 65)
建立Baseline
我們現在已經完成了資料清理,探索性資料分析和特徵工程。開始建模之前要做的最後一步是建立一個Baseline。這實際上是我們可以比較我們的結果的一種猜測。如果機器學習模型沒有超越這個猜測,那麼我們可能必須得出結論,機器學習對於任務來說是不可接受的,或者我們可能需要嘗試不同的方法。
對於迴歸問題,合理的Baseline是猜測測試集中所有示例的訓練集上目標的中值。這設定了一個任何模型都要超越的相對較低的標準。
我們將使用的度量標準是平均絕對誤差(Mean Absolute Error)(MAE),它測量預測的平均絕對誤差。有很多回歸的指標,但我喜歡Andrew Ng的建議【7】,選擇一個指標,然後在評估模型時堅持使用它。平均絕對誤差很容易計算,並且可以解釋。
在計算Baseline之前,我們需要將我們的資料分成一個訓練集和一個測試集:
1. 訓練集是我們在訓練期間給我們的模型提供特徵以及答案的。目地是讓模型學習特徵與目標之間的對映。
2. 測試集合的特徵用於評估訓練的模型。模型不允許檢視測試集的答案,並且只能使用特徵進行預測。我們知道測試集的答案,因此我們可以將測試預測與答案進行比較。
我們將使用70%的資料進行訓練,30%用於測試:
# Split into 70% training and 30% testing set
X, X_test, y, y_test = train_test_split(features, targets,
test_size = 0.3,
random_state = 42)
現在我們可以計算出Baseline的效能:
# Function to calculate mean absolute error
def mae(y_true, y_pred):
baseline_guess = np.median(y)
print('The baseline guess is a score of %0.2f' % baseline_guess)
print("Baseline Performance on the test set: MAE = %0.4f" %
mae(y_test, baseline_guess))
The baseline guess is a score of 66.00
Baseline Performance on the test set: MAE = 24.5164
Baseline的估計在測試集中約為25分。 得分範圍從1到100,所以這代表25%的誤差,相當低的一個超越!
結論
在本文中,我們走過了機器學習問題的前三個步驟。 在定義問題之後,我們:
1. 清理並格式化原始資料
2. 進行探索性資料分析以瞭解資料集
3. 開發了一系列我們將用於模型的特徵
最後,我們還完成了建立我們可以判斷我們的機器學習演算法的Baseline的關鍵步驟。