Day28 Python Study
·
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional, Tuple, Union
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import get_scorer
import joblib
import json
import warnings
warnings.filterwarnings('ignore')
class DataLoader(ABC):
"""抽象数据加载器"""
@abstractmethod
def load(self, data_path: str, **kwargs) -> Any:
pass
@abstractmethod
def validate(self, data: Any) -> bool:
pass
class FeatureEngineer(BaseEstimator, TransformerMixin):
"""特征工程处理器"""
def __init__(self, config: Dict = None):
self.config = config or {}
self.feature_names = []
def fit(self, X, y=None):
# 计算需要学习的特征(如标准化参数、编码映射等)
if self.config.get('normalize'):
self.mean_ = X.mean(axis=0) #计算均值
self.std_ = X.std(axis=0) #计算标准差
return self
def transform(self, X):
X_transformed = X.copy()
# 数值特征处理 标准化
if self.config.get('normalize'):
X_transformed = (X_transformed - self.mean_) / (self.std_ + 1e-8)
# 特征选择(示例)
if self.config.get('feature_selection'):
# 这里可以添加特征选择逻辑
pass
return X_transformed
class ModelFactory:
"""模型工厂,创建和管理模型"""
_model_registry = {}
@classmethod
def register_model(cls, name: str, model_class):
cls._model_registry[name] = model_class
@classmethod
def create_model(cls,
model_type: str,
task_type: str,
**params) -> Any:
"""根据任务类型创建模型"""
model_configs = {
'classification': {
'xgboost': 'xgboost.XGBClassifier',
'lightgbm': 'lightgbm.LGBMClassifier',
'random_forest': 'sklearn.ensemble.RandomForestClassifier',
'logistic': 'sklearn.linear_model.LogisticRegression',
'svm': 'sklearn.svm.SVC'
},
'regression': {
'xgboost': 'xgboost.XGBRegressor',
'lightgbm': 'lightgbm.LGBMRegressor',
'random_forest': 'sklearn.ensemble.RandomForestRegressor',
'linear': 'sklearn.linear_model.LinearRegression',
'svm': 'sklearn.svm.SVR'
},
'clustering': {
'kmeans': 'sklearn.cluster.KMeans',
'dbscan': 'sklearn.cluster.DBSCAN',
'gmm': 'sklearn.mixture.GaussianMixture'
}
}
if task_type not in model_configs:
raise ValueError(f"Unsupported task type: {task_type}")
if model_type not in model_configs[task_type]:
raise ValueError(f"Unsupported model type: {model_type}")
# 动态导入模型类
module_path, class_name = model_configs[task_type][model_type].rsplit('.', 1)
module = __import__(module_path, fromlist=[class_name])
model_class = getattr(module, class_name)
return model_class(**params)
class ModelSelector:
"""模型选择器,自动选择最佳模型"""
def __init__(self,
task_type: str,
metric: str = None,
cv: int = 5):
self.task_type = task_type
self.metric = metric or self._default_metric()
self.cv = cv
self.best_model = None
self.best_score = -np.inf
def _default_metric(self) -> str:
"""根据任务类型返回默认评估指标"""
metrics = {
'classification': 'accuracy',
'binary_classification': 'roc_auc',
'regression': 'neg_mean_squared_error',
'clustering': 'silhouette'
}
return metrics.get(self.task_type, 'accuracy')
def select(self, X, y, candidates: List[Dict]) -> Any:
"""从候选模型中选取最佳模型"""
for candidate in candidates:
model_type = candidate['type']
params = candidate.get('params', {})
try:
model = ModelFactory.create_model(
model_type,
self.task_type,
**params
)
# 交叉验证评估
if self.task_type in ['classification', 'regression']:
scores = cross_val_score(
model, X, y,
cv=self.cv,
scoring=self.metric
)
score = scores.mean()
if score > self.best_score:
self.best_score = score
self.best_model = model
except Exception as e:
print(f"Model {model_type} failed: {e}")
continue
if self.best_model is None:
raise ValueError("No suitable model found")
return self.best_model
class MLPipeline:
"""通用机器学习Pipeline"""
def __init__(self,
config: Dict = None,
task_type: str = 'auto'):
"""
初始化Pipeline
Args:
config: 配置字典
task_type: 任务类型 ('auto', 'classification', 'regression', 'clustering')
"""
self.config = config or {}
self.task_type = self._detect_task_type(task_type)
self.data_loader = None
self.feature_engineer = None
self.model = None
self.metrics = {}
self.history = []
def _detect_task_type(self, task_type: str) -> str:
"""自动检测或验证任务类型"""
if task_type != 'auto':
return task_type
# 这里可以添加自动检测逻辑
return 'classification' # 默认
def load_data(self,
data_path: str,
loader: Optional[DataLoader] = None,
**kwargs) -> Tuple:
"""加载数据"""
if loader is None:
# 默认使用CSV加载器
data = pd.read_csv(data_path, **kwargs)
else:
data = loader.load(data_path, **kwargs)
# 分离特征和标签
if 'target' in kwargs:
target_col = kwargs['target']
X = data.drop(columns=[target_col])
y = data[target_col]
else:
X = data
y = None
self._validate_data(X, y)
# 存储原始数据
self.X_raw = X.copy()
self.y_raw = y.copy() if y is not None else None
return X, y
def _validate_data(self, X, y):
"""数据验证"""
if X is None or len(X) == 0:
raise ValueError("Empty feature data")
# 检查缺失值
if isinstance(X, pd.DataFrame):
missing = X.isnull().sum().sum()
if missing > 0:
print(f"Warning: Found {missing} missing values")
# 检查数据类型
if not isinstance(X, (pd.DataFrame, np.ndarray)):
raise TypeError("X must be DataFrame or ndarray")
def preprocess(self,
X: Any,
y: Optional[Any] = None,
feature_config: Dict = None) -> Any:
"""数据预处理和特征工程"""
feature_config = feature_config or self.config.get('features', {})
self.feature_engineer = FeatureEngineer(feature_config)
# 拟合特征处理器(如果需要y)
if hasattr(self.feature_engineer, 'fit'):
self.feature_engineer.fit(X, y)
# 转换特征
X_processed = self.feature_engineer.transform(X)
# 特征名称(如果适用)
if hasattr(X_processed, 'columns'):
self.feature_names = X_processed.columns.tolist()
elif hasattr(self.feature_engineer, 'feature_names'):
self.feature_names = self.feature_engineer.feature_names
return X_processed
def split_data(self,
X: Any,
y: Any,
test_size: float = 0.2,
random_state: int = 42,
stratify: bool = True) -> Tuple:
"""划分训练集和测试集"""
stratify_y = y if stratify and self.task_type == 'classification' else None
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=test_size,
random_state=random_state,
stratify=stratify_y
)
return X_train, X_test, y_train, y_test
def select_model(self,
X: Any,
y: Any,
model_candidates: List[Dict] = None) -> Any:
"""自动选择模型"""
if model_candidates is None:
# 默认候选模型
if self.task_type == 'classification':
model_candidates = [
{'type': 'random_forest', 'params': {'n_estimators': 100}},
{'type': 'xgboost', 'params': {'n_estimators': 100}},
{'type': 'logistic', 'params': {'max_iter': 1000}}
]
elif self.task_type == 'regression':
model_candidates = [
{'type': 'random_forest', 'params': {'n_estimators': 100}},
{'type': 'xgboost', 'params': {'n_estimators': 100}},
{'type': 'linear', 'params': {}}
]
else:
model_candidates = [
{'type': 'kmeans', 'params': {'n_clusters': 3}}
]
selector = ModelSelector(
task_type=self.task_type,
metric=self.config.get('metric'),
cv=self.config.get('cv_folds', 5)
)
self.model = selector.select(X, y, model_candidates)
print(f"Selected model: {type(self.model).__name__}, CV score: {selector.best_score:.4f}")
return self.model
def train(self,
X_train: Any,
y_train: Any,
model: Optional[Any] = None,
**kwargs):
"""训练模型"""
if model is not None:
self.model = model
if self.model is None:
raise ValueError("No model specified. Use select_model() or provide a model.")
# 训练模型
self.model.fit(X_train, y_train, **kwargs)
# 记录训练历史
self.history.append({
'step': 'train',
'model': type(self.model).__name__,
'params': self.model.get_params() if hasattr(self.model, 'get_params') else {}
})
return self.model
def evaluate(self,
X_test: Any,
y_test: Any,
metrics: List[str] = None) -> Dict:
"""评估模型"""
if self.model is None:
raise ValueError("Model not trained yet")
if metrics is None:
metrics = self._get_default_metrics()
y_pred = self.predict(X_test)
# 计算指标
self.metrics = {}
for metric_name in metrics:
try:
if metric_name in ['accuracy', 'precision', 'recall', 'f1', 'roc_auc']:
from sklearn.metrics import get_scorer
scorer = get_scorer(metric_name)
score = scorer(self.model, X_test, y_test)
elif metric_name == 'mse':
from sklearn.metrics import mean_squared_error
score = mean_squared_error(y_test, y_pred)
elif metric_name == 'mae':
from sklearn.metrics import mean_absolute_error
score = mean_absolute_error(y_test, y_pred)
elif metric_name == 'silhouette':
from sklearn.metrics import silhouette_score
score = silhouette_score(X_test, y_pred)
else:
continue
self.metrics[metric_name] = score
except Exception as e:
print(f"Failed to compute {metric_name}: {e}")
# 打印评估结果
print("\n" + "="*50)
print("Model Evaluation Results:")
print("="*50)
for metric, value in self.metrics.items():
print(f"{metric}: {value:.4f}")
print("="*50)
return self.metrics
def _get_default_metrics(self) -> List[str]:
"""获取默认评估指标"""
if self.task_type in ['classification', 'binary_classification']:
return ['accuracy', 'precision', 'recall', 'f1', 'roc_auc']
elif self.task_type == 'regression':
return ['mse', 'mae', 'r2']
elif self.task_type == 'clustering':
return ['silhouette']
else:
return ['accuracy']
def predict(self, X: Any) -> Any:
"""预测"""
if self.model is None:
raise ValueError("Model not trained yet")
# 预处理(如果已定义特征处理器)
if self.feature_engineer is not None:
X = self.feature_engineer.transform(X)
return self.model.predict(X)
def predict_proba(self, X: Any) -> Any:
"""预测概率(分类任务)"""
if not hasattr(self.model, 'predict_proba'):
raise AttributeError("Model does not support probability predictions")
if self.feature_engineer is not None:
X = self.feature_engineer.transform(X)
return self.model.predict_proba(X)
def save(self,
path: str,
save_data: bool = False,
save_features: bool = True):
"""保存pipeline"""
save_dict = {
'model': self.model,
'feature_engineer': self.feature_engineer,
'config': self.config,
'task_type': self.task_type,
'metrics': self.metrics,
'feature_names': self.feature_names
}
if save_data:
save_dict.update({
'X_raw': self.X_raw,
'y_raw': self.y_raw
})
# 保存模型和pipeline
joblib.dump(save_dict, f"{path}_pipeline.pkl")
# 保存配置为JSON
with open(f"{path}_config.json", 'w') as f:
json.dump({
'config': self.config,
'task_type': self.task_type,
'metrics': self.metrics,
'feature_names': self.feature_names
}, f, indent=2)
print(f"Pipeline saved to {path}_pipeline.pkl")
@classmethod
def load(cls, path: str) -> 'MLPipeline':
"""加载pipeline"""
data = joblib.load(f"{path}_pipeline.pkl")
pipeline = cls(config=data['config'], task_type=data['task_type'])
pipeline.model = data['model']
pipeline.feature_engineer = data['feature_engineer']
pipeline.metrics = data.get('metrics', {})
pipeline.feature_names = data.get('feature_names', [])
if 'X_raw' in data:
pipeline.X_raw = data['X_raw']
pipeline.y_raw = data['y_raw']
return pipeline
def get_feature_importance(self) -> Optional[Dict]:
"""获取特征重要性"""
if hasattr(self.model, 'feature_importances_'):
importances = self.model.feature_importances_
return dict(zip(self.feature_names, importances))
elif hasattr(self.model, 'coef_'):
coef = self.model.coef_
return dict(zip(self.feature_names, coef.flatten()))
return None
# 注册自定义组件
class CSVLoader(DataLoader):
"""CSV数据加载器"""
def load(self, data_path: str, **kwargs):
return pd.read_csv(data_path, **kwargs)
def validate(self, data: Any) -> bool:
return isinstance(data, pd.DataFrame) and not data.empty
# ==================== 使用示例 ====================
def example_classification():
"""分类任务示例"""
from sklearn.datasets import load_iris
# 加载数据
data = load_iris()
X, y = data.data, data.target
# 创建pipeline
config = {
'features': {
'normalize': True
},
'metric': 'accuracy',
'cv_folds': 5
}
pipeline = MLPipeline(config=config, task_type='classification')
# 预处理
X_processed = pipeline.preprocess(X, y)
# 划分数据
X_train, X_test, y_train, y_test = pipeline.split_data(X_processed, y)
# 选择模型
pipeline.select_model(X_train, y_train)
# 训练
pipeline.train(X_train, y_train)
# 评估
metrics = pipeline.evaluate(X_test, y_test)
# 预测
predictions = pipeline.predict(X_test[:5])
print(f"\nSample predictions: {predictions}")
# 保存
pipeline.save('iris_model')
return pipeline
def example_regression():
"""回归任务示例"""
from sklearn.datasets import load_diabetes
data = load_diabetes()
X, y = data.data, data.target
pipeline = MLPipeline(task_type='regression')
# 完整流程
X_processed = pipeline.preprocess(X, y)
X_train, X_test, y_train, y_test = pipeline.split_data(X_processed, y)
# 自定义候选模型
candidates = [
{'type': 'xgboost', 'params': {'n_estimators': 200, 'learning_rate': 0.1}},
{'type': 'random_forest', 'params': {'n_estimators': 200}},
{'type': 'linear', 'params': {}}
]
pipeline.select_model(X_train, y_train, candidates)
pipeline.train(X_train, y_train)
pipeline.evaluate(X_test, y_test)
return pipeline
更多推荐
所有评论(0)