如何快速上手資料科學專案流程全攻略?(上)

A Step-by-Step Guide For A Data Science Project I.

學.誌|Chris Kang
17 min readJan 4, 2021
Photo by Clay Banks on Unsplash

會想寫這一篇文章,其實主要有兩個目的:一個是想要完整釐清一下資料科學一般專案的流程,給自己一個比較明確的學習框架;另一個則是想要給當初踏入資料科學的我一個敲門磚。

網路上其實不乏有許多高手分享他們在 Kaggle 上的競賽過程;但當時其實很常看不懂為什麼這個步驟要做這件事?為什麼 Code 這麼寫?所有對流程的認知都來自於自己的歸納,也就覺得要自己獨力執行一個專案是可望而不可及的挑戰。

所以這一篇不是什麼?

這一篇不是給高手或專家的讀者閱讀的進階指南,因為自己也經歷過剛開始學習時,看別人厲害的專案連他的流程和概念都看不懂,所以再美妙的框架也會看得很痛苦。

因此這篇文章希望著重在基礎的框架或流程,裡面的框架和流程參考了數篇 Kaggle 上的文章,與數本歐來禮機器學習的書籍整合而成,也是我目前最熟悉的工作流程,希望能提供讀者在學習上一個方便而易懂的流程框架。

框架拆分與流程說明

下面的步驟不一定在所有的專案中都會出現,但幾乎都會有混合執行或提前使用的情況。因此如果在閱讀其他人的流程時,可以在內心問自己:「他是不是已經有其他的資訊,來使他跳過這些流程?」若有,他需要哪些資訊來跳過這些流程?若沒有,他這麼做的原因是什麼?怎麼做可以更好?

接下來,就羅列出一般 Data Science 會經歷的七大步驟。框架主要可以分成七個步驟,分別是:

  1. 定義問題/敘述
  2. 區分與整理 Train/Test 的資料集
  3. 進行資料的 EDA
  4. 進行假說設計/設定
  5. 清洗/整理資料
  6. 模型建置與預測
  7. 視覺化/報告撰寫/結果分析

因為篇幅,會把文章切成上下兩篇,第一篇會先切到 3. 的 EDA 章節,之後再補上 5. ~ 7. 的步驟。那我們就直接開始吧!

一、定義問題/瞭解敘述

這個步驟是最容易被忽略,但卻是整個專案中最重要的一環。為什麼這麼說呢?以過去筆者在遊戲業工作的例子來說,當時在設計遊戲活動獎勵機制時,最重要的產出在營運的眼中必定是獎勵與機制的設定,因此當時花了大部分的時間在設計獎勵的內容與機制。

然而在整個研究的過程中,卻忽略最重要的前提其實是「這整個專案的目的是為了營收,還是為了維持活躍?」「對這個專案來說,預期投入多少資源?」「要獲得多少 % 的淨利潤?」。必須先回答這些問題,後面的機制設計才有意義。

因此在重新定義問題後,最關鍵的反而不是花時間在選擇獎勵,而是瞭解有多少資源能夠投入,並據此計算整個活動的「機率表」。唯有一份完整的機率表,才能讓公司與主管知道這個活動的最終結果,是否合乎當初設定的目的。這便是定義問題與瞭解敘述的重要。

那麼,要怎麼才能定義問題呢?

在一般的 Kaggle 或 KDD 的競賽,競賽通常會提供明確的問題與前情提要,因此確實理解競賽想要解決的問題,是在開始動手清理資料時的必要行動。

Screenshot from Kaggle

可以看到多數會有 Background 等資訊可以瞭解比賽的背景和問題。筆者者裡提供一些簡單而直覺的問題,如果能夠回答這些問題,代表這些資訊通常已經理解到一定程度可以進入下個階段了。問題的清單如下:

  • 這個問題或比賽是如何產生的?
  • 他提供了哪一些資訊、資料與變數?
  • 這些變數是怎麼獲得的?
  • 這些資訊幫助我們理解什麼事情?
  • 這個比賽或問題的預期產出是什麼?
  • 會對這個問題/比賽產生什麼 Impact?

在理解/回答這些問題後,最關鍵的問題則是要問兩類的問題:

  1. 該問題是監督還是非監督學習?
  2. 該問題的結果是分類還是回歸?

監督學習是指「資料已經給你答案,所以你可以直接對照學習」,例如回歸、KNN 等模型;而非監督則是沒有明確的答案,必須要自己分類或分群。

而回歸類型的問題通常是回答「一個連續的數值」,例如預測房價或機率等;而分類則通常回答「這是屬於哪一種」,例如這是狗還是貓、該用戶屬於什麼類別。給個簡單的對照表來參考:

監督式學習(Supervised learning)

  • 分類(Classification)
  • 迴歸(Regression)

非監督式學習(Unsupervised learning)

  • 分群(Clustering)
  • 降維(Dimensionality reduction)

當然,在工作時問題就會變的更為複雜。筆者之前工作的經驗是,有時候資料明明都是同個資料庫抓出來的;但兩個資訊系統顯示出來的結果就是不一樣。這時候確認資料的「來源」與「計算方式」就顯得非常非常重要。

針對上面提到的問題,可以舉個真實的例子來說明。例如主管常常會說:「我想要改善 X 產品的銷量,因為該產品好像一直都賣的不好」。這時你擁有的資訊就只有「 X 產品的銷量需要改善」,以及老闆所說的「賣不好」。

這時候就需要特別花時間來理解問題。以上述提到的例子來拆解,可以先針對「賣不好這件事」進行釐清。通常賣不好的意思,可能代表相較於其他同類型的商品的銷售額較低,亦或是同產品比過去幾年的銷量差。

因此需要先詢問所謂的「賣不好」,具體來說是怎麼判斷的

接著主管可能會說「它賣的比過去同類型的商品還要差」,所以希望改善該商品的銷量。如果是實體產品,這時就需要更深入地以 4C/4P 等框架來拆分分析,並提出一系列的問題如:

  1. 消費者是同一群嗎?
  2. 同類型的產品行銷方式有哪一些?有差異嗎?
  3. 同類型的銷售方式有哪一些?有差異嗎?
  4. 消費者有哪些回饋?和其他的產品的回饋差在哪裡?

更深入地去定義究竟是什麼問題,把問題具體化到能夠回答上面提到的那六大問題,才能聚焦在真正該解決的問題。

二、區分與整理 Train/Test 的資料集

Kaggle.com

對於 Kaggle 這類型的比賽,通常都會有兩個檔案 train.csv 和 test.csv,預期會產出 submission.csv 這個繳交檔。

最重要的第一步,是嘗試把 train 跟 submission 的格式塞進去。通常我們會把 submission 的檔案稱為主檔,有許多變數的檔案稱為描述檔,只要能夠把這兩個檔案整理到一起,就已經具備可以跑模型的資格了。

通常我會看 test.csv 的檔案有沒有值得參考的描述變數,如果有就會嘗試把兩個檔案合併,以增加模型可用的訓練資料。

train_df = pd.read_csv('../input/train.csv')
test_df = pd.read_csv('../input/test.csv')
combine = [train_df, test_df]

但真實世界中其實就是一堆 SQL 的資料庫甚至 Excel,這時在前面的問題定義就需要更清晰一些,甚至建議請教已經熟悉該領域的同事,來探索可能有用的變數,因為有時候不是沒有變數可用,只是當下的我們想不到。

三、進行 Data 的 EDA

這個部分是當初我入門時,覺得學的最痛苦的地方,因為變數這麼多,我要從何看起?又要看哪一些資料?要在什麼階段設定假設?因此,我希望在這個篇章,能提供給讀者一個簡單的框架。它絕對不是最完整或最萬用的,但卻是我希望我如果在剛開始學習時,能夠一步一步參考的框架。

目前我常用歸納出來的,總共有三個步驟:

  1. 確認資料狀況
  2. 建立觀察假說
  3. 樞紐分析與視覺化

第一步:確認資料狀況

我在處理資料科學的專案時,通常會分成五個步驟:

  1. 檢視欄位的種類
  2. 檢視資料的型態
  3. 檢視錯誤與分布

I. 檢視欄位的種類

將訓練資料讀入後,最重要的便是判別總共有哪一些欄位。這時通常會使用來看一下:

print(train_df.columns.value)

或是直接以 Head 來檢視前五行的資料:

train_df.head()

II. 檢視資料的型態

接著來瞭解上述看到的這些欄位中,哪一些資料屬於哪一些類別?常見的資料有四種類別:

  1. 類別尺度(Nominal scale)
  2. 順序尺度(Ordinal scale)
  3. 區間尺度(Interval scale)
  4. 比例尺度(Ratio scale)

此處特別說明區間與類別尺度的差異。對於區間尺度,數字間只有「等級」的差異而不具有「倍數」差異。例如我們不能說 4 分的餐廳就比 2 分的餐廳好吃兩倍;但可以說 20 元是 10 元的兩倍,在應用資料時需要避免誤用。

另外在資料型態的檢視上,還有一種類別的資料會和接下來的特徵工程有關。有些欄位的資料是「混合資料(mixed data)」。例如去電影院的座位 C16,實際上代表的可能是 C 區和第 16 排,甚至 C 區可能和其他的影廳排序不同,那原先的資料就能夠處理成個別的 A、B、C… 和 16 排這兩組類別資料,讓模型能更有機會抓到重要的特徵來處理。

III. 檢視錯誤與分布

實際上收集進來的資料,很有可能並非都是正確的資料,例如缺值或錯誤。如果不處理就直接丟進去跑模型,就很容易導致模型怎麼調整都無法再更精確一些。

a. 通常我會先確認數量變數

train_df.describe()

接著,我會開始逐行檢查資料的最大、最小、四分位距等資料。舉經典的 Titanic 資料的例子來說,可以看到 Age 的欄位竟然出現 0.42 的數字,同時也看到第一四分位距的數字顯然就正常的多,這時我就會拿出紙和筆(或是任何筆記本),把這個資料的問題記錄下來。

同時我也發現,Age 這一行的資料其實是有缺陷的(有時會用 df.isnull() 來檢視),所以我一樣會把這件事記錄下來,並簡單說明等等會嘗試一些插值法,可能就會考慮拿其他的資料來跑 Random Forest Regression 來預測空缺的 Age。

有時也會看要資料是否為稀疏資料(sparse data),例如垃圾郵件、交易異常記錄等,如果資料屬於這部分的類別,就需要特別進行其他的資料處理,例如最簡單的 log 或 boxing 起來。

處理遺失值和錯誤值的方式,可以參考 Python 資料分析機器學習特徵工程 這兩本書,裡面提到的很多資料處理方式都很實用而簡單。

b. 接著來看類別變數:

詳細的使用方式可以參考 df.describe() 來調整成自己想檢視的資料。

train_df.describe(include=['O'])

c. 檢視資料的差異與變化

CountUniq = train_df.apply(lambda x: len(x.unique()))
print(CountUniq)

通常可以看到資料的差異程度,也能快速檢視是否有不合理的資料型態。舉例來說,Survived 理論上應該只會有 0 和 1,但如果結果卻出現 3 個不同值,很明顯這筆資料就有問題,需要進一步去檢視數據與分布。

第二步:建立觀察假說

Photo by Roman Mager on Unsplash

這個步驟是我當初學的最久,一開始最容易迷失的環節。當初一直有一個迷思是,只要學會 Data Scientist 的各種工具,就可以利用相關性矩陣、跑模型時的 Antoencoder 等自動選取特徵;但卻一直在練習的過程中卡住。因為很多特徵可能單一欄看沒有幫助,但轉換成關連特徵後就有驚人的效果。

舉個簡單易懂的例子,如果在預測房價時我們不分青紅皂白的把房子的長跟寬放進去關係矩陣來跑關連,用直覺想就知道這項變數的預測率不會太高。但如果讓這兩個特徵相乘組成關連特徵,其代表的意義「面積」馬上就成為非常有用的預測特徵。

因此想以踩過一堆坑的經驗來分享,在初次踏入機器學習或數據分析的領域時,行業經驗或直覺在選擇特徵時,比想像中的重要很多。甚至我可以說,在機器學習的商業領域應用,瞭解如何做特徵工程比瞭解模型背後的數學原理推導更加重要,因為商業知識有時候會決定一個模型準確度的天花板。

接著,我就以實際的例子來說明如何建立觀察假說。

Elo Merchant 的競賽來說,在一開始的時候知道題目是要預測哪一些人的購買忠誠度高,因此優先判斷為回歸的問題,接著檢視一下數據量發現有 30 幾萬筆且參數量並不多僅 10 幾列,因此可以考慮使用 xgboostLightBGM Rregressor 來計算。

有了這些粗淺的前提,加上前面在 EDA 的過程瞭解參數的組成,就可以開始進行假說設立。這裡僅列出一些作為說明。

  • 在 historical_transactions 的檔案中有交易時間戳記,這時我想到如果跟忠誠度有關,瞭解他們多久沒有買(現在時間 — 最新交易時間)應該是很重要的特徵,一如 RFM 的客戶分類模型,最後一次的交易時間確實是一個很重要的參考特徵。
  • 購買的月份應該會影響每個人的購買意願,因此月份的資料應該也會是重要的特徵。
  • 購買不同物品的消費者,通常對於該商店的忠誠度也不一樣,例如買電器和零食的很明顯就不會一樣,因此值得將不同類別都放入特徵。

列出這些假說之後,就要開始進行特徵工程(Feature Engineering)的前置作業清洗資料(Clean Data);但在真的坐下來寫 Code 之前,務必要把「任務列表清單」寫一份下來

為什麼需要有這個清單?過去我在清洗資料時,有時會忘記哪一些變數已經清洗,特別是當欄位高達四五十個時,常常處理到一半後就需要回去看哪些欄位處理完了,無形中增加了資料處理的難度與錯誤率。因此我開始採用新的分類方式來處理資料。

清洗資料可以分成四個類別:

  • 修正資料(Correcting):部分資料可能有誤植,在這個環節先進行資料修正,如果資料缺失值比例很低,則可以簡單地用 median、mean 來填補。
  • 填補缺值(Completing):如果缺失值的比例較高,就需要以預測的方式來填補。通常會使用其他的欄位資料來進行填補,根據不同目的使用如 KNN 或馬可夫蒙地卡羅等。
  • 創造特徵(Creating):有時候某些欄位屬於 Mix data,這時就可以把他切開取用其中重要的資料;亦或是交易時間的頻率或間隔等,可以藉由 groupby 或 datetime 相減獲得新資料。
  • 降維資料(Classifying):如果資料量非常龐大或是在 EDA 時發現雜訊很多,這時就會考慮使用簡單的裝箱法,複雜一些則可以 PCA、t-SNE 等方式進行降維。

第三步:樞紐分析與視覺化

這個步驟通常會和 EDA 跟建立假說配合執行,列在這裡只是為了方便說明。這裡就列出常用的檢視方式與工具:

a. 檢視資料的分布:

count_classes = pd.value_counts(data['Category'],sort=True).sort_index()ax = sns.barplot(x=count_classes.index, y=tuple(count_classes/len(data)))
ax.set_title('Frequency Percentage by Cat')
ax.set_xlabel('Cate')
ax.set_ylabel('Frequency Percentage')

直接將資料以 barplot 檢視,初步確認資料是否和想像的分布一致,抑或需要進一步進行資料縮放或是 log 轉換等步驟。

b. 相關性矩陣檢視:

通常有兩種看法,一種是顯示整張圖表,另一種則是以三角相關性來檢視,可以忽略掉重複的另外半邊資料。

df.corr() # 計算相關性
sns.heatmap(df.corr())
# 三角相關性 Heatmap
np.triu(np.ones_like(df.corr()))
plt.figure(figsize=(16, 6))# 讓資料只顯示下半部分
mask = np.triu(np.ones_like(dataframe.corr(), dtype=np.bool))
heatmap = sns.heatmap(df.corr(), mask=mask, vmin=-1, vmax=1, annot=True, cmap='BrBG')
heatmap.set_title('Triangle Correlation Heatmap', fontdict={'fontsize':18}, pad=16);

這是在初步判斷特徵和結果間的相關性時,最常被使用的方式。基本上會以 Pearson R 相關性來確認不同的特徵是否對資料有影響力。

c. 樞紐分析檢視:

常用的有兩個工具,分別是 Groupby Pivot_table

train_df[['Category', 'Purchase']].groupby(['Category'], as_index=False).count().sort_values(by='Category', ascending=False)

如果想要深入理解 pivot_table 如何使用 快速瞭解 Pivot Table 與應用。使用時機的差異是,如果你想要快速的 aggregate 其中某些欄位,那用 Groupby 即可;但如果你想要生成複雜一些的樞紐分析表,則可使用 Pivot_table

四、上篇小總結

這一篇主要說明了從一開始理解問題、讀取資料,一路到如何進行簡單的 EDA,如果有興趣可以參考 Exploratory Data Analysis 來進一步瞭解 EDA 的概念。也許每一個部分並沒有太多深入的 code 或工具;但對於初步理解數據科學的專案我相信能起到拋磚引玉之效。

這一篇其實也是寫給當初的我自己來看,因為當時在自學資料科學時,其實並不是有系統地去學習,因此當時就算把 DataCamp 一整個 Track 都爬完,我還是不知道到底要怎麼獨自進行一次數據的分析,而這個困擾到現在終於算是踏出第一步了,也算是給自己的學習一個比較完整的交代。

下一篇文章則會提到,我在建模時如何使用 Random Forest 和 xgboost 等套件的流程。過程會說明如 K-fold validation、model training 和效益評估。一樣是給剛入門,想要瞭解整個資料科學完整流程的朋友參考。

有興趣的讀者,也可以直接點開資料科學專案流程(下篇)來繼續閱讀喔!

謝謝你看到這裡,如果文中有任何錯誤還望不吝指正或建議。

**【希望用你的掌聲來投票與支持】**
拍 5~10 下:簽個到,表示支持(感謝你的鼓勵啊啊啊)
拍 10~30 下:希望我可以多寫一些文章!有你這位讀者,寫這篇也心滿意足了!
拍 30~50 下:內容對你感覺很有共鳴,希望能多分享給周圍的朋友!

--

--

學.誌|Chris Kang

嗨!我是 Chris,一位擁有技術背景的獵頭,熱愛解決生活與職涯上的挑戰。專注於產品管理/資料科學/前端開發 / 人生成長,在這條路上,歡迎你找我一起聊聊。歡迎來信合作和交流: chriskang0917@gmail.com