知識點回顧
- 規范的文件命名
- 規范的文件夾管理
- 機器學習項目的拆分
- 編碼格式和類型注解
昨天我們已經介紹了如何在不同的文件中,導入其他目錄的文件,核心在于了解導入方式和python解釋器檢索目錄的方式。
搞清楚了這些,那我們就可以來看看,如何把一個文件,拆分成多個具有著獨立功能的文件,然后通過import的方式,來調用這些文件。這樣具有幾個好處:
- 可以讓項目文件變得更加規范和清晰
- 可以讓項目文件更加容易維護,修改某一個功能的時候,只需要修改一個文件,而不需要修改多個文件。
- 文件變得更容易復用,部分通用的文件可以單獨拿出來,進行其他項目的復用。
機器學習項目的流程
一個典型的機器學習項目通常包含以下階段:
- 數據加載:從文件、數據庫、API 等獲取原始數據。
- 命名參考:
load_data.py
?、data_loader.py
- 命名參考:
- 數據探索與可視化:了解數據特性,初期可用 Jupyter Notebook,成熟后固化繪圖函數。
- 命名參考:
eda.py
?、visualization_utils.py
- 命名參考:
- 數據預處理:處理缺失值、異常值,進行標準化、歸一化、編碼等操作。
- 命名參考:
preprocess.py
?、data_cleaning.py
?、data_transformation.py
- 命名參考:
- 特征工程:創建新特征,選擇、優化現有特征。
- 命名參考:
feature_engineering.py
- 命名參考:
- 模型訓練:構建模型架構,設置超參數并訓練,保存模型。
- 命名參考:
model.py
?、train.py
- 命名參考:
- 模型評估:用合適指標評估模型在測試集上的性能,生成報告。
- 命名參考:
evaluate.py
- 命名參考:
- 模型預測:用訓練好的模型對新數據預測。
- 命名參考:
predict.py
?、inference.py
- 命名參考:
文件的組織
1. 項目核心代碼組織
- src/(source的縮寫):存放項目的核心源代碼。按照機器學習項目階段進一步細分:
- src/data/:放置與數據相關的代碼。
src/data/load_data.py
:負責從各類數據源(如文件系統、數據庫、API 等)讀取原始數據。src/data/preprocess.py
:進行數據清洗(處理缺失值、異常值)、數據轉換(標準化、歸一化、編碼等)操作。src/data/feature_engineering.py
:根據業務和數據特點,創建新特征或對現有特征進行選擇、優化。
- src/models/:關于模型的代碼。
src/models/model.py
:定義模型架構,比如神經網絡結構、機器學習算法模型設定等。src/models/train.py
:設置模型超參數,并執行訓練過程,保存訓練好的模型。src/models/evaluate.py
:使用合適的評估指標(如準確率、召回率、均方誤差等),在測試集上評估模型性能,生成評估報告。src/models/predict.py
?或?src/models/inference.py
:利用訓練好的模型對新數據進行預測。
- src/utils/:存放通用輔助函數代碼,可進一步細分:
src/utils/io_utils.py
:包含文件讀寫相關幫助函數,比如讀取特定格式文件、保存數據到文件等。src/utils/logging_utils.py
:實現日志記錄功能,方便記錄項目運行過程中的信息,便于調試和監控。src/utils/math_utils.py
:特定的數值計算函數,像自定義的矩陣運算、統計計算等。src/utils/plotting_utils.py
:繪圖工具函數,用于生成數據可視化圖表(如繪制損失函數變化曲線、特征分布直方圖等 )。
- src/data/:放置與數據相關的代碼。
2. 配置文件管理
- config/ 目錄:集中存放項目的配置文件,方便管理和切換不同環境(開發、測試、生產)的配置。
config/config.py
?或?config/settings.py
:以 Python 代碼形式定義配置參數。config/config.yaml
?或?config/config.json
:采用 YAML 或 JSON 格式,清晰列出文件路徑、模型超參數、隨機種子、API 密鑰等可配置參數。.env
?文件:通常放在項目根目錄,用于存儲敏感信息(如數據庫密碼、API 密鑰等),在代碼中通過環境變量的方式讀取,一般會被?.gitignore
?忽略,防止敏感信息泄露。
3. 實驗與探索代碼
-
notebooks/ 或 experiments/ 目錄:用于初期的數據探索、快速實驗、模型原型驗證。
notebooks/initial_eda.ipynb
:在項目初期,使用 Jupyter Notebook 進行數據探索與可視化,了解數據特性,分析數據分布、相關性等。experiments/model_experimentation.py
:編寫腳本對不同模型架構、超參數組合進行快速實驗,對比實驗結果,尋找最優模型設置。
這部分往往是最開始的探索階段,后面跑通了后拆分成了完整的項目,留作紀念用。
4. 項目產出物管理
- data/ 目錄:存放項目相關數據。
data/raw/
:放置從外部獲取的未經處理的原始數據,保持數據原始狀態。data/processed/
:存放經過預處理(清洗、轉換、特征工程等操作)后的數據,供模型訓練和評估使用。data/interim/
:(可選)保存中間處理結果,比如數據清洗過程中生成的臨時文件、特征工程中間步驟產生的數據等。
- models/ 目錄:專門存放訓練好的模型文件,根據模型保存格式不同,可能是?
.pkl
(Python pickle 格式,常用于保存 sklearn 模型 )、.h5
(常用于保存 Keras 模型 )、.joblib
?等。 - reports/ 或 output/ 目錄:存儲項目運行產生的各類報告和輸出文件。
reports/evaluation_report.txt
:記錄模型評估的詳細結果,包括各項評估指標數值、模型性能分析等。reports/visualizations/
:存放數據可視化圖片,如損失函數收斂圖、預測結果對比圖等。output/logs/
:保存項目運行日志文件,記錄項目從開始到結束過程中的關鍵信息,如訓練開始時間、訓練過程中的損失值變化、預測時間等。
總結一下通用的拆分起步思路:
- 首先,按照機器學習的主要工作流程(數據處理、訓練、評估等)將代碼分離到不同的?
.py
?文件中。?這是最基本也是最有價值的一步。 - 然后,創建一個?
utils.py
?來存放通用的輔助函數。 - 考慮將所有配置參數集中到一個?
config.py
?文件中。 - 為你的數據和模型產出物創建專門的頂層目錄,如?
data/
?和?models/
,將它們與你的源代碼(通常放在?src/
?目錄)分開。
當遵循這些通用的拆分思路和原則時,項目結構自然會變得清晰。
注意事項
if?name?== "main"
常常會看到if?name?== "main"這個寫法,實際上,每個文件都是一個對象,對象就會有屬性和方法。
如果直接運行這個文件,則__name__等于__main__,若這個文件被其他模塊導入,則__name__不等于__main__。
這個寫法有如下好處:
-
明確程序起點:一個 Python 項目往往由多個模塊組成。if?name?== "main" 可清晰界定程序執行的起始位置。比如一個包含數據處理模塊 data_processing.py、模型訓練模塊 model_training.py 的機器學習項目,在 model_training.py 中用 if?name?== "main" 包裹訓練相關的主邏輯代碼,運行該文件時就知道需要從這里開始執行(其他文件都是附屬文件),讓項目結構和執行流程更清晰。(大多時候如此)
-
避免執行:python遵從模塊導入即執行機制,當你使用 import xxx 導入一個模塊時,Python 會執行該模塊中的所有頂層代碼(即不在任何函數或類內部的代碼)。如果頂層代碼中定義了全局變量或執行了某些操作(如讀取文件、初始化數據庫連接),這些操作會在導入時立即生效,并可能影響整個程序的狀態。為了避免執行不必要的代碼,我們可以使用 if?name?== "main" 來避免在導入時執行不必要的代碼。這樣,只有當模塊被直接運行時(即被執行 python xxx.py),才會執行頂層代碼,而導入時則不會執行。這樣,我們就可以確保在導入模塊時,不會執行不必要的代碼,從而提高程序的性能和可維護性。
-
合理的資源管理:if?name?== "main" 與定義 main 函數結合使用,函數內變量在函數執行完這些變量被釋放,能及時回收內存資源,避免內存泄漏,保證程序高效運行。
編碼格式
規范的py文件,首行會有:# -- coding: utf-8 --
主要目的是 顯式聲明文件的編碼格式,確保 Python 解釋器能正確讀取和解析文件中的非 ASCII 字符(如中文、日文、特殊符號等)。也就是說這個是寫給解釋器看的。
因為,在 Python 2.x 時代,默認編碼是 ASCII,不支持直接在代碼中寫入非 ASCII 字符(如中文注釋、字符串中的中文),否則會報錯(SyntaxError: Non-UTF-8 code starting with...)。但是Python 3.x 默認為 UTF-8 編碼,理論上可以省略編碼聲明。但實際開發中,為了兼容舊代碼、明確文件編碼規則,或在團隊協作中避免因編輯器 / 環境配置不同導致的亂碼問題,許多開發者仍會保留這一行聲明。
ps:
- 編碼聲明必須出現在文件的前兩行(通常是首行),否則會被忽略。
- 如果編碼格式沒問題,可能是vscode的編碼格式不是utf-8,可以嘗試修改編碼格式。
- 常見的編碼報錯是因為字符串編碼問題,可以嘗試顯式轉化,即讀取的時候轉化為utf-8編碼。
非 ASCII 字符的代碼如下所示:
# -*- coding: utf-8 -*-
msg = "你好,世界!" # 中文字符串
print(msg)
你好,世界!
很多時候,項目中會包含gitattribute文件,來確保在不同操作系統和編輯器中,文件的編碼格式一致。這里我們后面說到git工具在介紹
類型注解
Python 的類型注解是在 Python 3.5+ 引入的特性,用于為變量、函數參數、返回值和類屬性等添加類型信息。雖然 Python 仍是動態類型語言,但類型注解可以提高代碼可讀性、可維護性,并支持靜態類型檢查工具(如 mypy)。
其次你在安裝python插件的時候,附帶安裝了2個插件
- 一個是python debugger用于斷點調試,我們已經介紹了
- 另一個是pylance,用于代碼提示和類型檢查,這個插件會根據你的代碼中的類型注解,給出相應的提示和檢查,比如你定義了一個函數,參數類型是int,那么當你傳入一個字符串時,它會提示你傳入的參數類型不正確。
變量類型注解語法為 變量名: 類型
# 變量的類型注解
name: str = "Alice"
age: int = 30
height: float = 1.75
is_student: bool = False
函數類型注解為函數參數和返回值指定類型,語法為 def 函數名(參數: 類型) -> 返回類型。
def add(a: int, b: int) -> int:return a + bdef greet(name: str) -> None:print(f"Hello, {name}")
類屬性與方法的類型注解:為類的屬性和方法添加類型信息。
# 定義一個矩形類
class Rectangle:width: float # 矩形寬度(浮點數),類屬性的類型注解(不初始化值)height: float # 矩形高度(浮點數)def __init__(self, width: float, height: float):self.width = widthself.height = heightdef area(self) -> float:# 計算面積(寬度 × 高度)return self.width * self.height
上述的width: float # 矩形寬度(浮點數)
這個寫法由于沒有對變量賦值,所以是一種類型注解寫法
作業:嘗試針對之前的心臟病項目,準備拆分的項目文件,思考下哪些部分可以未來復用。
1. 項目結構設計
heart-disease-project/
├── config.py # 配置參數
├── data/ # 數據相關文件
│ ├── raw/ # 原始數據
│ └── processed/ # 預處理后的數據
├── models/ # 模型文件
├── src/ # 源代碼
│ ├── data_processing.py # 數據處理
│ ├── model_training.py # 模型訓練
│ ├── evaluation.py # 模型評估
│ └── utils.py # 通用工具函數
├── README.md # 項目說明
└── requirements.txt # 依賴庫
2. 項目結構初始化
mkdir -p heart-disease-project/data/{raw,processed}
mkdir -p heart-disease-project/models
mkdir -p heart-disease-project/src
touch heart-disease-project/config.py
touch heart-disease-project/README.md
touch heart-disease-project/requirements.txt
3.配置文件 config.py
# config.py
import os# 數據路徑
RAW_DATA_PATH = os.path.join("data", "raw", "heart_disease.csv")
PROCESSED_DATA_PATH = os.path.join("data", "processed", "heart_data_processed.csv")# 模型路徑
MODEL_SAVE_PATH = os.path.join("models", "heart_model.pkl")# 超參數
TEST_SIZE = 0.2
RANDOM_STATE = 42
4. 數據處理模塊 src/data_processing.py
# src/data_processing.py
import pandas as pd
from sklearn.model_selection import train_test_split
from config import RAW_DATA_PATH, PROCESSED_DATA_PATHdef load_and_preprocess_data():# 加載原始數據df = pd.read_csv(RAW_DATA_PATH)# 處理缺失值(示例:填充均值)df.fillna(df.mean(), inplace=True)# 特征與標簽分離X = df.drop("target", axis=1)y = df["target"]# 劃分訓練集和測試集X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 保存預處理后的數據processed_data = {"X_train": X_train,"X_test": X_test,"y_train": y_train,"y_test": y_test,}pd.to_pickle(processed_data, PROCESSED_DATA_PATH)return processed_data
5.模型訓練模塊 src/model_training.py
# src/model_training.py
import joblib
from sklearn.ensemble import RandomForestClassifier
from config import MODEL_SAVE_PATH
from src.data_processing import load_and_preprocess_datadef train_model():# 加載預處理后的數據data = pd.read_pickle(config.PROCESSED_DATA_PATH)X_train, y_train = data["X_train"], data["y_train"]# 初始化模型model = RandomForestClassifier(n_estimators=100, random_state=42)# 訓練模型model.fit(X_train, y_train)# 保存模型joblib.dump(model, MODEL_SAVE_PATH)print(f"Model saved to {MODEL_SAVE_PATH}")
6. 模型評估模塊 src/evaluation.py
# src/evaluation.py
import joblib
from sklearn.metrics import classification_report, roc_auc_score
from config import MODEL_SAVE_PATH, PROCESSED_DATA_PATHdef evaluate_model():# 加載模型和數據model = joblib.load(MODEL_SAVE_PATH)data = pd.read_pickle(PROCESSED_DATA_PATH)X_test, y_test = data["X_test"], data["y_test"]# 預測結果y_pred = model.predict(X_test)y_proba = model.predict_proba(X_test)[:, 1]# 輸出評估指標print("Classification Report:")print(classification_report(y_test, y_pred))print("ROC AUC Score:")print(roc_auc_score(y_test, y_proba))
7.工具函數 src/utils.py
# src/utils.py
import os
import loggingdef create_directory(path):"""確保目錄存在"""if not os.path.exists(path):os.makedirs(path)logging.info(f"Created directory: {path}")
8.依賴管理 requirements.txt
# requirements.txt
pandas
scikit-learn
joblib
9.項目運行流程
* 準備數據
將原始數據文件 heart_disease.csv 放入 data/raw/ 目錄。
* 運行數據處理python src/data_processing.py
* 訓練模型python src/model_training.py
* 評估模型python src/evaluation.py
@浙大疏錦行