教你用c++從頭開始實現決策樹

Python已經成為數據科學的語言之王。大多數新的數據科學傢和程序員繼續學習Python作為他們的第一門語言。這是有充分理由的;Python具有較淺的學習曲線、強大的社區和豐富的數據科學庫生態系統。

我用Python開始瞭我的數據科學之旅,它仍然是我解決數據科學問題最常用的工具。我很想更好地理解Python從您那裡抽象出瞭什麼,以及用性能更高的語言編寫更快代碼的成本與好處。

為瞭有代表性地介紹c++,我需要一個代表性的應用程序,c++將是一個合適的選擇。從頭實現一個分類決策樹分類器似乎是一個適當的挑戰。這已經被證明是一個測試但有益的學習旅程,我想分享一些我在這個過程中的主要經驗。

關鍵經驗:

  • c++很少提供代碼提示或保護
  • 盡早做出好的架構決策
  • 從長遠來看,編寫測試將為您節省時間
  • 語言的在線社區非常有價值
  • 可移植性是一個重要的考慮因素

在Python中,你可以做很多事情。您可以創建一個變量,隨心所欲地改變它的類型,然後不必擔心如何處理它。這能讓你在執行過程中改變想法。非常適合動態迭代原型設計。

在c++中,您必須預先決定您希望您的變量是什麼類型。您還必須預先決定希望函數返回的類型。如果您聲明錯誤,例如試圖從一個已經聲明為返回整數的函數返回一個字符串,那麼您的進程將會停止。在這種情況下,編譯器將阻止您編譯程序,通常帶有一個令人費解的錯誤消息。令人沮喪的是,編譯器是您的朋友,它會在這個問題導致後續問題之前預先提醒您。在Python中,隻有在太晚的時候才發現問題是很常見的,比如在代碼投入生產之後。

在上面的示例中,編譯器捕獲定義為返回試圖返回字符串的整數的函數。

也有編譯器不支持您的情況。訪問一個被認為存儲在特定內存地址的變量時,可能隻收到一個垃圾值,因為該變量已經被刪除瞭。在這裡,您通常不會在編譯時收到錯誤,而且很容易在代碼中留下錯誤,而您對此卻渾然不覺。

在上面的示例中,即使我們試圖訪問已被刪除的變量的內存地址的值,編譯也不會給出錯誤。

盡早做出好的架構決策

在Python中,很容易在嘗試解決問題的早期階段就開始編寫解決方案。由於c++的靈活性和較慢的開發速度,這種方法在使用c++時不能很好地工作。

在這個項目中,我最初使用的是我的python方法,即隻編寫代碼,而不繪制端到端解決方案。最後,我坐下來,想出瞭一個解決這個問題的總體架構。

下面列出瞭在實現決策樹分類器中開發的關鍵對象。它們包括一個Node類和一個Tree類,以及它們相關的屬性和方法,並且大部分可以在編寫任何代碼之前定義:

Node
- Node constructor
- Node destuctor
- Attributes
   - children nodes
   - data
   - best split feature chosen
   - best split category chosen
- Methods
   - giniImpurity() - metric for scoring quality of split
   - bestSplit() - best split feature and category

Tree
- Tree constructor
- Tree destructor
- Attributes
   - root node of tree
- Methods
   - traverse() - traverse nodes of tree
   - fit() - fit tree to dataset
   - predict() - make predictions classes with unseen data
   - CSVReader() - read a csv

決策樹項目的核心文件(不包括測試文件)如下所示,以供參考。

. 
├── CMakeLists.txt 
├── CSVReader.cpp 
├── CSVReader.hpp 
├── DecisionTree.cpp 
├── DecisionTree.hpp 
├── Main.cpp 
├── Node.cpp 
├── Node.hpp 
└── README.md

一旦該體系結構就位,解決方案自然就會遵循。對類及其成員函數(類和函數參數以及返回的對象)的接口進行前瞻性設計也可以使事情變得更加容易。

從長遠來看,編寫測試將為您節省時間

由於c++缺乏安全性,所以測試代碼的每個部分是否都成功地完成瞭預期的功能是至關重要的。用於c++的谷歌Test測試框架很適合這個項目,它使用CMake構建。

以可測試的方式編寫代碼可以更容易地識別和隔離bug。方法是為實現的類編寫靜態定義的成員函數。靜態定義的成員函數可以在沒有父類實例化的情況下獨立執行。這使得為完成決策樹業務邏輯的一個方面的每一個功能編寫特定的、獨立的測試用例成為可能。

上面顯示瞭在終端中通過測試的谷歌Test的輸出。

語言的在線社區非常有價值

Python開發人員有一個開發人員社區,使用像Stack Overflow和博客這樣的工具為集體知識做出貢獻。此資源是Python數據科學的命脈。c++沒有等價的社區。在谷歌上搜索開發c++代碼時遇到的許多問題和錯誤消息,往往會得到沒有幫助的結果。一種語言的社區價值很大。

從上面我們可以看到,現在每個月被回答的與Python相關的問題比c++多4倍。在這裡查看這些統計數據的當前狀態。

可移植性是一個重要的考慮因素

在Python中,你可以確信任何安裝瞭Python解釋器的系統都能夠執行你的Python程序。而在c++中,你就沒有這種特權瞭。由於c++是一種編譯語言,在運行程序之前必須先編譯程序,而且必須針對要運行程序的宿主的體系結構來編譯它。

當嘗試使用Github Actions遠程測試代碼時,這成為一個重要的問題。由於主機是不同的操作系統和架構,因此需要在虛擬機上測試代碼之前編譯代碼。這是部署代碼時需要管理的額外開銷。

總結

學習像c++這樣的低級語言可以讓你接觸到許多快速程序所需的核心概念,如內存管理、數據結構和編譯語言。它讓人們意識到Python中預先實現的數據結構,比如Pandas DataFrames,將擁有處理內存管理的系統,這些系統必須做出一系列假設,因此有局限性。

在實踐中,不太可能有很多數據科學傢會使用c++來解決實驗性的數據科學問題,但是Python不再是最好的工具,例如編寫快速的數據解析器或實現昂貴的算法。即使在這種情況下,我也將探索現代低級語言,如Go-lang和Rust,而不是c++。c++的語法讓人感覺很冗長,而且它缺乏許多可以從這些現代語言中獲得的安全特性。

您可以在這裡從頭看到c++決策樹分類器的完整源代碼。您還可以在這裡找到一個示例jupiter notebook,它直接從Python調用已實現的決策樹分類器,並在Titanic數據集上訓練決策樹。https://github.com/hlamotte/decision-tree

到此這篇關於教你用c++從頭開始實現決策樹的文章就介紹到這瞭,更多相關c++實現決策樹內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: