大数据学习进度
今日学习:训练完模型,不能凭感觉判断好坏,得用指标量化。线性回归常用 2 个指标:①R²(决定系数):越接近 1,说明模型拟合得越好;②均方误差(MSE):数值越小,说明预测值和真实值差距越小。新手不用记复杂公式,会用 Scikit-learn 自带函数计算就行,重点是理解 “指标越好,模型越靠谱” 的逻辑。
贝叶斯朴素
import numpy as np
import pandas as pd
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report, confusion_matrix
from sklearn.preprocessing import LabelEncoder
from sklearn.naive_bayes import GaussianNB
import matplotlib.pyplot as plt
import seaborn as sns
class GaussianNaiveBayes:
"""
自行实现的高斯朴素贝叶斯分类器
"""
def init(self):
"""
初始化高斯朴素贝叶斯分类器
"""
self.classes_ = None # 类别标签
self.class_prior_ = None # 类别先验概率
self.theta_ = None # 每个类别每个特征的均值
self.sigma_ = None # 每个类别每个特征的方差
self.epsilon = 1e-9 # 防止方差为0的小常数
def fit(self, X, y):
"""
训练高斯朴素贝叶斯模型
参数:
- X: 训练集特征数据,形状为(n_samples, n_features)
- y: 训练集目标变量,形状为(n_samples,)
"""
# 获取所有类别
self.classes_ = np.unique(y)
n_classes = len(self.classes_)
n_samples, n_features = X.shape
初始化参数
self.class_prior_ = np.zeros(n_classes)
self.theta_ = np.zeros((n_classes, n_features))
self.sigma_ = np.zeros((n_classes, n_features))
对每个类别计算参数
for i, c in enumerate(self.classes_):
# 获取属于当前类别的样本
X_c = X[y == c]
计算类别先验概率 P(y=c)
self.class_prior_[i] = len(X_c) / n_samples
计算每个特征的均值 μ
self.theta_[i] = np.mean(X_c, axis=0)
计算每个特征的方差 σ²
self.sigma_[i] = np.var(X_c, axis=0)
防止方差为0
self.sigma_[i] = np.maximum(self.sigma_[i], self.epsilon)
return self
def _gaussian_pdf(self, x, mu, sigma):
"""
计算高斯概率密度函数
参数:
- x: 特征值
- mu: 均值
- sigma: 方差
返回:
- 高斯概率密度值
"""
exponent = np.exp(-((x - mu) ** 2) / (2 * sigma))
return (1.0 / np.sqrt(2 * np.pi * sigma)) * exponent
def predict(self, X):
"""
预测样本的类别
参数:
- X: 测试集特征数据,形状为(n_samples, n_features)
返回:
- 预测的类别标签,形状为(n_samples,)
"""
# 计算每个样本属于每个类别的后验概率
posterior_probs = self.predict_proba(X)
返回后验概率最大的类别
return self.classes_[np.argmax(posterior_probs, axis=1)]
def predict_proba(self, X):
"""
预测样本属于每个类别的概率
参数:
- X: 测试集特征数据,形状为(n_samples, n_features)
返回:
- 每个样本属于每个类别的概率,形状为(n_samples, n_classes)
"""
n_samples = X.shape[0]
n_classes = len(self.classes_)
初始化后验概率矩阵
posterior_probs = np.zeros((n_samples, n_classes))
对每个类别计算后验概率
for i, c in enumerate(self.classes_):
# 计算先验概率的对数(避免数值下溢)
log_prior = np.log(self.class_prior_[i])
计算似然概率的对数
log_likelihood = np.sum(np.log(self.gaussian_pdf(X, self.theta[i], self.sigma_[i])), axis=1)
后验概率 = 先验概率 × 似然概率
使用对数空间计算避免数值下溢
posterior_probs[:, i] = log_prior + log_likelihood
将对数概率转换回概率(使用softmax归一化)
减去最大值以提高数值稳定性
posterior_probs = posterior_probs - np.max(posterior_probs, axis=1, keepdims=True)
posterior_probs = np.exp(posterior_probs)
posterior_probs = posterior_probs / np.sum(posterior_probs, axis=1, keepdims=True)
return posterior_probs
def score(self, X, y):
"""
计算模型在测试集上的准确率
参数:
- X: 测试集特征数据
- y: 测试集真实标签
返回:
- 准确率
"""
y_pred = self.predict(X)
return accuracy_score(y, y_pred)
def load_iris_data(file_path="d:\机器学习\iris\iris.data"):
"""
加载iris数据集
参数:
- file_path: 数据文件路径
返回:
- X: 特征数据
- y: 编码后的标签
- feature_names: 特征名称列表
- class_names: 类别名称列表
"""
# 读取数据
data = pd.read_csv(file_path, header=None)
设置列名
data.columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
提取特征和标签
X = data.iloc[:, :-1].values
y = data.iloc[:, -1].values
将类别标签转换为数值
le = LabelEncoder()
y_encoded = le.fit_transform(y)
获取特征名称和类别名称
feature_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
class_names = le.classes_.tolist()
return X, y_encoded, feature_names, class_names
def evaluate_model(y_true, y_pred):
"""
评估模型性能
参数:
- y_true: 真实标签
- y_pred: 预测标签
返回:
- 包含各项评估指标的字典
"""
accuracy = accuracy_score(y_true, y_pred)
precision = precision_score(y_true, y_pred, average='weighted')
recall = recall_score(y_true, y_pred, average='weighted')
f1 = f1_score(y_true, y_pred, average='weighted')
return {
'accuracy': accuracy,
'precision': precision,
'recall': recall,
'f1': f1
}
def cross_validate_custom(X, y, k=5):
"""
使用自行实现的高斯朴素贝叶斯进行五折交叉验证
参数:
- X: 特征数据
- y: 目标变量
- k: 折数
返回:
- 每折的评估结果列表
"""
# 创建K折交叉验证对象
kf = KFold(n_splits=k, shuffle=True, random_state=42)
存储每折的结果
results = []
for fold, (train_index, test_index) in enumerate(kf.split(X)):
print(f"\n===== 自行实现 - 第 {fold+1} 折 =====")
划分训练集和测试集
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
创建并训练模型
model = GaussianNaiveBayes()
model.fit(X_train, y_train)
在测试集上进行预测
y_pred = model.predict(X_test)
评估模型性能
metrics = evaluate_model(y_test, y_pred)
metrics['fold'] = fold + 1
results.append(metrics)
打印当前折的结果
print(f"准确率: {metrics['accuracy']:.4f}")
print(f"精确率: {metrics['precision']:.4f}")
print(f"召回率: {metrics['recall']:.4f}")
print(f"F1值: {metrics['f1']:.4f}")
计算平均性能
avg_accuracy = np.mean([r['accuracy'] for r in results])
avg_precision = np.mean([r['precision'] for r in results])
avg_recall = np.mean([r['recall'] for r in results])
avg_f1 = np.mean([r['f1'] for r in results])
print("\n===== 自行实现 - 平均性能 =====")
print(f"平均准确率: {avg_accuracy:.4f}")
print(f"平均精确率: {avg_precision:.4f}")
print(f"平均召回率: {avg_recall:.4f}")
print(f"平均F1值: {avg_f1:.4f}")
return results
def cross_validate_sklearn(X, y, k=5):
"""
使用scikit-learn的高斯朴素贝叶斯进行五折交叉验证
参数:
- X: 特征数据
- y: 目标变量
- k: 折数
返回:
- 每折的评估结果列表
"""
# 创建K折交叉验证对象
kf = KFold(n_splits=k, shuffle=True, random_state=42)
存储每折的结果
results = []
for fold, (train_index, test_index) in enumerate(kf.split(X)):
print(f"\n===== scikit-learn - 第 {fold+1} 折 =====")
划分训练集和测试集
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
创建并训练模型
model = GaussianNB()
model.fit(X_train, y_train)
在测试集上进行预测
y_pred = model.predict(X_test)
评估模型性能
metrics = evaluate_model(y_test, y_pred)
metrics['fold'] = fold + 1
results.append(metrics)
打印当前折的结果
print(f"准确率: {metrics['accuracy']:.4f}")
print(f"精确率: {metrics['precision']:.4f}")
print(f"召回率: {metrics['recall']:.4f}")
print(f"F1值: {metrics['f1']:.4f}")
计算平均性能
avg_accuracy = np.mean([r['accuracy'] for r in results])
avg_precision = np.mean([r['precision'] for r in results])
avg_recall = np.mean([r['recall'] for r in results])
avg_f1 = np.mean([r['f1'] for r in results])
print("\n===== scikit-learn - 平均性能 =====")
print(f"平均准确率: {avg_accuracy:.4f}")
print(f"平均精确率: {avg_precision:.4f}")
print(f"平均召回率: {avg_recall:.4f}")
print(f"平均F1值: {avg_f1:.4f}")
return results
def plot_confusion_matrix(y_true, y_pred, class_names, title="Confusion Matrix"):
"""
绘制混淆矩阵
参数:
- y_true: 真实标签
- y_pred: 预测标签
- class_names: 类别名称列表
- title: 图表标题
"""
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=class_names, yticklabels=class_names)
plt.title(title)
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.tight_layout()
plt.savefig('confusion_matrix_naive_bayes.png', dpi=300, bbox_inches='tight')
plt.show()
def main():
"""
主函数
"""
print("===== 朴素贝叶斯算法实验 =====")
加载数据
print("\n1. 加载iris数据集...")
X, y, feature_names, class_names = load_iris_data()
print(f"数据集大小: {X.shape}")
print(f"类别数量: {len(np.unique(y))}")
print(f"特征数量: {X.shape[1]}")
数据分析
print("\n2. 数据分析...")
for i, name in enumerate(feature_names):
print(f"{name}: 最小值={np.min(X[:, i]):.2f}, 最大值={np.max(X[:, i]):.2f}, "
f"平均值={np.mean(X[:, i]):.2f}, 标准差={np.std(X[:, i]):.2f}")
实验一:自行实现的高斯朴素贝叶斯
print("\n3. 实验一:自行实现的高斯朴素贝叶斯")
results_custom = cross_validate_custom(X, y, k=5)
实验二:scikit-learn的高斯朴素贝叶斯
print("\n4. 实验二:scikit-learn的高斯朴素贝叶斯")
results_sklearn = cross_validate_sklearn(X, y, k=5)
结果比较
print("\n5. 结果比较")
methods = ["自行实现", "scikit-learn"]
all_results = [results_custom, results_sklearn]
print("\n方法\t\t准确率\t精确率\t召回率\tF1值")
print("-" * 70)
for i, method in enumerate(methods):
avg_accuracy = np.mean([r['accuracy'] for r in all_results[i]])
avg_precision = np.mean([r['precision'] for r in all_results[i]])
avg_recall = np.mean([r['recall'] for r in all_results[i]])
avg_f1 = np.mean([r['f1'] for r in all_results[i]])
print(f"{method}\t{avg_accuracy:.4f}\t{avg_precision:.4f}\t{avg_recall:.4f}\t{avg_f1:.4f}")
训练最终模型并可视化
print("\n6. 训练最终模型并可视化")
使用自行实现的模型
print("\n自行实现的模型参数:")
custom_model = GaussianNaiveBayes()
custom_model.fit(X, y)
print(f"类别: {custom_model.classes_}")
print(f"类别先验概率: {custom_model.class_prior_}")
print(f"每个类别的特征均值:")
for i, c in enumerate(custom_model.classes_):
print(f" 类别 {c}: {custom_model.theta_[i]}")
print(f"每个类别的特征方差:")
for i, c in enumerate(custom_model.classes_):
print(f" 类别 {c}: {custom_model.sigma_[i]}")
使用scikit-learn的模型
print("\nscikit-learn模型参数:")
sklearn_model = GaussianNB()
sklearn_model.fit(X, y)
print(f"类别: {sklearn_model.classes_}")
print(f"类别先验概率: {sklearn_model.class_prior_}")
print(f"每个类别的特征均值:")
for i, c in enumerate(sklearn_model.classes_):
print(f" 类别 {c}: {sklearn_model.theta_[i]}")
print(f"每个类别的特征方差:")
for i, c in enumerate(sklearn_model.classes_):
print(f" 类别 {c}: {sklearn_model.var_[i]}")
绘制混淆矩阵
print("\n7. 绘制混淆矩阵")
y_pred = sklearn_model.predict(X)
plot_confusion_matrix(y, y_pred, class_names, "Naive Bayes Confusion Matrix")
详细分类报告
print("\n8. 详细分类报告")
print(classification_report(y, y_pred, target_names=class_names))
if name == "main":
main()
浙公网安备 33010602011771号