项目文件的规范拆分和写法
知识点回顾
- 规范的文件命名
- 规范的文件夹管理
- 机器学习项目的拆分
- 编码格式和类型注解
**作业:**尝试针对之前的心脏病项目ipynb,将他按照今天的示例项目整理成规范的形式,思考下哪些部分可以未来复用
把一个文件,拆分成多个具有着独立功能的文件,然后通过import的方式,来调用这些文件,这样具有几个好处:
- 可以让项目文件变得更加规范和清晰
- 可以让项目文件更加容易维护,修改某一个功能的时候,只需要修改一个文件,而不需要修改多个文件
- 文件变得更容易复用,部分通用的文件可以单独拿出来,进行其他项目的复用
总结一下通用的拆分起步思路:
- 首先,按照机器学习的主要工作流程(数据处理、训练、评估等)将代码分离到不同的 `.py` 文件中,这是最基本也是最有价值的一步
- 然后,创建一个 `utils.py` 来存放通用的辅助函数
- 考虑将所有配置参数集中到一个 `config.py` 文件中
- 为你的数据和模型产出物创建专门的顶层目录,如 `data/` 和 `models/`,将它们与你的源代码(通常放在 `src/` 目录)分开
比如之前信贷数据集的代码就可以拆分成如下结构
credit_default_prediction/
│
├── data/ # 数据文件夹
│ ├── raw/ # 原始数据
│ └── processed/ # 处理后的数据
│
├── src/ # 源代码
│ ├── __init__.py
│ ├── data/ # 数据处理相关代码
│ │ ├── __init__.py
│ │ ├── preprocessing.py # 进行数据清洗、数据转换等操作
│ │ └── feature_engineering.py # 特征工程,创建新特征或对现有特征进行选择、优化
│ │
│ ├── models/ # 模型相关代码
│ │ ├── __init__.py
│ │ ├── train.py # 设置模型超参数,并执行训练过程,保存训练好的模型
│ │ └── evaluate.py # 使用合适的评估指标在测试集上评估模型性能,生成评估报告
│ │
│ └── visualization/ # 可视化相关代码
│ ├── __init__.py
│ └── plots.py
│
├── requirements.txt # 项目依赖
└── README.md # 项目说明文档
if name == "main"这个写法在项目拆分时很常见,如果直接运行某个文件,则__name__等于__main__,若这个文件被其他模块导入,则__name__不等于__main__
举个例子,当数据集进行加载时:
python
import pandas as pd
def load_data(file_path):
"""加载数据文件,返回 DataFrame"""
data = pd.read_csv(file_path)
return data
# 直接运行此模块时,测试数据加载功能
if __name__ == "__main__":
test_data = load_data("test_data.csv")
print("数据加载测试成功!前5行数据:")
print(test_data.head())
当直接运行这个文件时,测试数据加载功能;当被其他模块导入时,仅暴露 load_data
函数,不执行测试代码
规范的py文件,首行会有:# -*- coding: utf-8 -*-,主要目的是显式声明文件的编码格式,确保 Python 解释器能正确读取和解析文件中的非 ASCII 字符(如中文、日文、特殊符号等)。也就是说这个是写给解释器看的
Python 的类型注解是一种为变量、函数参数和返回值和类属性添加类型提示的语法,不会影响代码运行 ,但能提高代码可读性,并可用工具(如 mypy
)检查类型错误
1、变量类型注解语法为 变量名: 类型
python
name: str = "Alice" # 声明 name 是字符串类型
age: int = 25 # 声明 age 是整数类型
is_student: bool = True # 声明 is_student 是布尔类型
2、函数类型注解为函数参数和返回值指定类型,语法为 def 函数名(参数: 类型) -> 返回类型
python
def add(a: int, b: int) -> int:
return a + b
result: int = add(3, 5) # 正确
result = add("hello", 5) # 类型检查工具(如 mypy)会报错
3、类属性与方法的类型注解,语法和上面两种差不多
python
# 定义一个矩形类
class Rectangle:
width: float # 矩形宽度(浮点数),类属性的类型注解(不初始化值)
height: float # 矩形高度(浮点数)
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def area(self) -> float:
# 计算面积(宽度 × 高度)
return self.width * self.height
下面来具体举个例子如何拆分项目,假设一个项目的 jupyter 代码如下
python
# Jupyter Cell 1: 加载数据
import pandas as pd
data = pd.read_csv("data/raw_data.csv")
# Jupyter Cell 2: 数据预处理
def clean_data(data):
data = data.dropna()
data['feature'] = data['feature'].apply(lambda x: x*2)
return data
cleaned_data = clean_data(data)
# Jupyter Cell 3: 训练模型
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(cleaned_data[['feature']], cleaned_data['target'])
# Jupyter Cell 4: 可视化
import matplotlib.pyplot as plt
plt.scatter(cleaned_data['feature'], cleaned_data['target'])
plt.plot(cleaned_data['feature'], model.predict(cleaned_data[['feature']]), color='red')
plt.show()
拆成这样的层次结构:
python
your_project/
├── data/ # 存放数据
│ └── raw_data.csv
├── src/ # 源码目录
│ ├── data_loader.py
│ ├── preprocess.py
│ ├── model.py
│ └── visualize.py
├── config.py # 配置文件
├── main.py # 主程序入口
├── requirements.txt # 依赖库列表
└── README.md # 项目说明
(1) src/data_loader.py
python
import pandas as pd
def load_data(data_path: str) -> pd.DataFrame:
"""加载原始数据"""
return pd.read_csv(data_path)
(2) src/preprocess.py
python
from pandas import DataFrame
def clean_data(data: DataFrame) -> DataFrame:
"""数据清洗"""
data = data.dropna()
data['feature'] = data['feature'].apply(lambda x: x*2)
return data
(3) src/model.py
python
from sklearn.linear_model import LinearRegression
from pandas import DataFrame
def train_model(data: DataFrame, feature_col: str, target_col: str) -> LinearRegression:
"""训练模型"""
model = LinearRegression()
model.fit(data[[feature_col]], data[target_col])
return model
(4) src/visualize.py
python
import matplotlib.pyplot as plt
from pandas import DataFrame
from sklearn.linear_model import LinearRegression
def plot_results(data: DataFrame, model: LinearRegression, feature_col: str, target_col: str) -> None:
"""可视化结果"""
plt.scatter(data[feature_col], data[target_col])
plt.plot(data[feature_col], model.predict(data[[feature_col]]), color='red')
plt.savefig("results/plot.png") # 保存图片(脚本中需主动调用 plt.show())
plt.close() # 避免内存泄漏
配置文件 config.py
python
# 定义全局路径和参数
DATA_PATH = "data/raw_data.csv"
FEATURE_COL = "feature"
TARGET_COL = "target"
RESULT_DIR = "results/"
主程序 main.py
python
from src.data_loader import load_data
from src.preprocess import clean_data
from src.model import train_model
from src.visualize import plot_results
from config import DATA_PATH, FEATURE_COL, TARGET_COL, RESULT_DIR
import os
def main():
# 创建结果目录
os.makedirs(RESULT_DIR, exist_ok=True)
# 完整流程
raw_data = load_data(DATA_PATH)
cleaned_data = clean_data(raw_data)
model = train_model(cleaned_data, FEATURE_COL, TARGET_COL)
plot_results(cleaned_data, model, FEATURE_COL, TARGET_COL)
if __name__ == "__main__":
main() # 直接运行时执行
依赖管理 requirements.txt
python
pandas==1.3.5
scikit-learn==1.0.2
matplotlib==3.5.1
最后直接在终端测试运行
python
# 安装依赖
pip install -r requirements.txt
# 运行主程序
python main.py