机器学习B
上机实验报告
班级: 信2305-3
姓名: 程永耀
学号: 20233967
2026年 1月 4 日
目 录
实验一:数据准备与模型评估 1
实验二:逻辑回归算法实现与测试 2
实验三:C4.5(带有预剪枝和后剪枝)算法实现与测试 3
实验四:SMO 算法实现与测试 4
实验五:BP 神经网络算法实现与测试 5
实验六:朴素贝叶斯算法实现与测试 6
实验七:K 均值聚类算法实现与测试 7
实验八:随机森林算法实现与测试 8
实验总结 9
实验一:数据准备与模型评估
一、实验目的
熟悉 Python 的基本操作,掌握对数据集的读写实现、对模型性能的评估实现的能力; 加深对训练集、测试集、N 折交叉验证、模型评估标准的理解。
二、实验内容
(1)利用 pandas 库从本地读取 iris 数据集; (2)从 scikit-learn 库中直接加载 iris 数据集; (3)实现五折交叉验证进行模型训练; (4)计算并输出模型的准确度、精度、召回率和 F1 值。
三、算法步骤、代码、及结果
-
算法伪代码
算法:模型评估与交叉验证实验
输入:无
输出:模型性能评估指标
步骤: -
数据准备阶段
1.1 方法一:从本地CSV文件读取iris数据集
1.2 方法二:从scikit-learn库加载iris数据集 -
数据预处理阶段
2.1 划分特征矩阵X和标签向量y
2.2 对特征进行标准化/归一化(可选) -
模型训练与评估阶段
3.1 初始化分类器(随机森林)
3.2 设置五折交叉验证参数
3.3 循环进行五折交叉验证:
对于每一折(i从1到5):
a. 划分训练集和验证集
b. 在训练集上训练模型
c. 在验证集上进行预测
d. 计算并保存评估指标 -
结果分析阶段
4.1 计算平均评估指标
4.2 输出各指标结果
4.3 可视化结果(可选)
2.算法主要代码
iris_experiment.py
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
import requests
import os
import sys
任务1: 使用pandas从本地读取iris数据集
def load_iris_with_pandas():
# 检查是否存在iris.csv文件,如果不存在则下载
if not os.path.exists('iris.csv'):
print("下载iris数据集...")
try:
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
response = requests.get(url, timeout=30)
response.raise_for_status()
with open('iris.csv', 'wb') as f:
f.write(response.content)
print("数据集下载完成")
except Exception as e:
print(f"下载数据集失败: {e}")
print("将使用scikit-learn的数据集进行后续操作")
return None, None
定义列名
column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
使用pandas读取数据集
df = pd.read_csv('iris.csv', header=None, names=column_names)
print("\n1. 使用pandas读取的iris数据集:")
print("数据集形状:", df.shape)
print("前5行数据:")
print(df.head())
print("数据类型:")
print(df.dtypes)
print("\n类别分布:")
print(df['class'].value_counts())
返回特征和标签
X = df.drop('class', axis=1).values
y = df['class'].values
return X, y
任务2: 从scikit-learn直接加载iris数据集
def load_iris_with_sklearn():
# 从scikit-learn加载数据集
iris = load_iris()
X, y = iris.data, iris.target
print("\n2. 从scikit-learn加载的iris数据集:")
print("数据集形状:", X.shape)
print("特征名称:", iris.feature_names)
print("目标类别:", iris.target_names)
print("前5个样本特征:")
print(X[:5])
print("前5个样本标签:")
print(y[:5])
print("\n类别分布:")
unique, counts = np.unique(y, return_counts=True)
for i, (u, c) in enumerate(zip(unique, counts)):
print(f"{iris.target_names[u]}: {c}个样本")
return X, y
任务3: 实现五折交叉验证进行模型训练
def perform_cross_validation(X, y):
# 创建随机森林分类器
rf_classifier = RandomForestClassifier(n_estimators=100, random_state=42)
设置五折交叉验证
kf = KFold(n_splits=5, shuffle=True, random_state=42)
print("\n3. 五折交叉验证结果:")
存储每个折的评估指标
all_accuracies = []
all_precisions = []
all_recalls = []
all_f1s = []
进行交叉验证
for fold, (train_idx, test_idx) in enumerate(kf.split(X), 1):
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
训练模型
rf_classifier.fit(X_train, y_train)
预测
y_pred = rf_classifier.predict(X_test)
计算评估指标
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, average='macro')
recall = recall_score(y_test, y_pred, average='macro')
f1 = f1_score(y_test, y_pred, average='macro')
存储结果
all_accuracies.append(accuracy)
all_precisions.append(precision)
all_recalls.append(recall)
all_f1s.append(f1)
print(f" 折 {fold}😊
print(f" 准确度: {accuracy:.4f}")
print(f" 精度: {precision:.4f}")
print(f" 召回率: {recall:.4f}")
print(f" F1值: {f1:.4f}")
计算平均指标
avg_accuracy = np.mean(all_accuracies)
avg_precision = np.mean(all_precisions)
avg_recall = np.mean(all_recalls)
avg_f1 = np.mean(all_f1s)
print("\n4. 平均评估指标:")
print(f" 平均准确度: {avg_accuracy:.4f}")
print(f" 平均精度: {avg_precision:.4f}")
print(f" 平均召回率: {avg_recall:.4f}")
print(f" 平均F1值: {avg_f1:.4f}")
使用sklearn的cross_val_score进行验证
print("\n5. 使用sklearn的cross_val_score验证:")
cv_accuracy = cross_val_score(rf_classifier, X, y, cv=5, scoring='accuracy')
cv_precision = cross_val_score(rf_classifier, X, y, cv=5, scoring='precision_macro')
cv_recall = cross_val_score(rf_classifier, X, y, cv=5, scoring='recall_macro')
cv_f1 = cross_val_score(rf_classifier, X, y, cv=5, scoring='f1_macro')
print(f" 交叉验证准确度: {cv_accuracy.mean():.4f} ± {cv_accuracy.std():.4f}")
print(f" 交叉验证精度: {cv_precision.mean():.4f} ± {cv_precision.std():.4f}")
print(f" 交叉验证召回率: {cv_recall.mean():.4f} ± {cv_recall.std():.4f}")
print(f" 交叉验证F1值: {cv_f1.mean():.4f} ± {cv_f1.std():.4f}")
主函数
def main():
print("===== Iris数据集实验 =====")
print("\n实验目的:熟悉Python基本操作,掌握数据读写与模型评估")
任务1: 使用pandas加载数据集
print("\n" + "="*40)
print("任务1: 使用pandas从本地读取iris数据集")
try:
X_pd, y_pd = load_iris_with_pandas()
except Exception as e:
print(f"使用pandas加载数据集时出错: {e}")
print("继续执行下一步...")
任务2: 使用scikit-learn加载数据集
print("\n" + "="*40)
print("任务2: 从scikit-learn直接加载iris数据集")
X_skl, y_skl = load_iris_with_sklearn()
任务3和4: 交叉验证和评估指标计算
print("\n" + "="*40)
print("任务3-4: 五折交叉验证与模型评估指标计算")
perform_cross_validation(X_skl, y_skl)
print("\n" + "="*40)
print("实验完成!")
print("注:本实验使用随机森林分类器(RandomForestClassifier)进行模型训练")
print(" 并通过五折交叉验证计算了模型的准确度、精度、召回率和F1值")
if name == "main":
try:
main()
except KeyboardInterrupt:
print("\n程序被用户中断")
sys.exit(0)
except Exception as e:
print(f"程序执行出错: {e}")
sys.exit(1)
iris_experiment_simple.py
mport pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
import requests
import os
1. 使用pandas从本地读取iris数据集
def read_with_pandas():
# 如果文件不存在则下载
if not os.path.exists('iris.csv'):
print("正在下载iris数据集...")
try:
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
response = requests.get(url)
with open('iris.csv', 'wb') as f:
f.write(response.content)
print("数据集下载完成!")
except:
print("下载失败,将直接使用sklearn数据集")
return None
读取数据
columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
df = pd.read_csv('iris.csv', header=None, names=columns)
print("\n【Pandas加载结果】")
print(f"数据集形状: {df.shape}")
print("前5行数据:")
print(df.head())
return df
2. 从scikit-learn直接加载iris数据集
def load_from_sklearn():
iris = load_iris()
X, y = iris.data, iris.target
print("\n【Sklearn加载结果】")
print(f"数据集形状: {X.shape}")
print(f"特征名称: {iris.feature_names}")
print(f"类别名称: {iris.target_names}")
return X, y, iris.target_names
3-4. 五折交叉验证和模型评估
def cross_validation_and_evaluation(X, y, target_names):
# 创建分类器
rf = RandomForestClassifier(n_estimators=100, random_state=42)
五折交叉验证
kf = KFold(n_splits=5, shuffle=True, random_state=42)
存储评估指标
accuracies = []
precisions = []
recalls = []
f1_scores = []
print("\n【五折交叉验证结果】")
for i, (train_idx, test_idx) in enumerate(kf.split(X)):
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
训练模型
rf.fit(X_train, y_train)
预测
y_pred = rf.predict(X_test)
计算指标
acc = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, average='macro')
recall = recall_score(y_test, y_pred, average='macro')
f1 = f1_score(y_test, y_pred, average='macro')
存储结果
accuracies.append(acc)
precisions.append(precision)
recalls.append(recall)
f1_scores.append(f1)
print(f"\n折 {i+1}😊
print(f" 准确度: {acc:.4f}")
print(f" 精度: {precision:.4f}")
print(f" 召回率: {recall:.4f}")
print(f" F1值: {f1:.4f}")
输出平均结果
print("\n【平均评估指标】")
print(f"平均准确度: {np.mean(accuracies):.4f}")
print(f"平均精度: {np.mean(precisions):.4f}")
print(f"平均召回率: {np.mean(recalls):.4f}")
print(f"平均F1值: {np.mean(f1_scores):.4f}")
主函数
def main():
print("========== Iris数据集实验 ==========")
步骤1: 用pandas读取
print("\n步骤1: 使用pandas读取iris数据集")
df = read_with_pandas()
步骤2: 用sklearn读取
print("\n步骤2: 从scikit-learn加载iris数据集")
X, y, target_names = load_from_sklearn()
步骤3-4: 交叉验证和评估
print("\n步骤3-4: 五折交叉验证和模型评估")
cross_validation_and_evaluation(X, y, target_names)
print("\n========== 实验完成 ==========")
if name == "main":
main()
3. 运行结果截图(包括:准确率;精度、召回率、F1)
(1)非调库准确率:0.9600 (96.00%)
调库准确率:0.9667 ± 0.0211 (96.67%)
(2)精度:0.9707 ± 0.0176,召回率0.9667 ± 0.0211:,F1:0.9665 ± 0.0213
四、心得体会
通过本次Iris数据集实验,我对Python机器学习的基础操作有了更深入的理解和体会。
- 数据读取与处理的灵活性
实验中分别使用了pandas和scikit-learn两种方式加载数据。pandas提供了更大的灵活性,可以直接从本地CSV文件读取数据,并允许自定义列名,这为后续的数据清洗和预处理奠定了基础。而scikit-learn提供的数据集则更加规范化和标准化,适合快速原型开发。
- 交叉验证的重要性
五折交叉验证让我深刻理解了模型评估的科学性。通过将数据划分为5个互不重叠的折,轮流作为测试集进行训练和验证,能够更客观地评估模型的泛化能力。相比于简单的训练集/测试集划分,交叉验证充分利用了有限的数据,减少了评估结果的方差。
- 多维度评估指标的意义
实验计算了准确度、精度、召回率和F1值四个指标。准确度虽然直观,但在类别不平衡时可能具有误导性;精度和召回率则从不同角度衡量模型性能——精度关注预测为正例的准确性,召回率关注正例的覆盖率;F1值作为精度和召回率的调和平均,提供了更综合的评价。
- 随机森林分类器的表现
使用随机森林分类器在Iris数据集上取得了约96.67%的准确率,说明该算法对于这种中小规模的分类任务非常有效。其内置的随机性和集成特性有效防止了过拟合,提高了模型的稳定性。
- 总结
本次实验不仅让我掌握了数据加载、模型训练和评估的基本流程,更重要的是理解了机器学习中"数据-模型-评估"的完整闭环。这些基础知识将为后续更复杂的机器学习任务打下坚实的基础。
实验二:逻辑回归算法实现与测试
一、实验目的
深入理解对数几率回归(即逻辑回归的)的算法原理,能够使用 Python 语言实现对数 几率回归的训练与测试,并且使用五折交叉验证算法进行模型训练与评估。
二、实验内容
1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析; (2)采用五折交叉验证划分训练集和测试集,使用训练集训练对数几率回归(逻辑回 归)分类算法; (3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试; (4)通过对测试结果进行比较分析,评估模型性能; (5)完成实验报告中实验二的部分。 3、操作要点 (1)可以选择自行编写源代码完成对数几率回归算法,或者调用 scikit-learn 库中的函 数; (2)如果调用 scikit-learn 库中的函数,需要说明函数各个参数的含义(名称)、解释或 说明(包括作用、取值等)、默认值(如有,可在备注列写出)等,即自行编写代码只需要 粘贴完整的带有注释的源代码即可,调用函数则包括粘贴源代码和函数参数说明两部分
三、算法步骤、代码、及结果
-
算法伪代码
算法:逻辑回归算法实现与评估
输入:iris数据集(来自sklearn或本地)
输出:逻辑回归模型的性能评估指标
步骤: -
数据加载与预处理
1.1 加载iris数据集
1.2 数据探索与分析
1.3 特征选择与处理
1.4 划分特征和标签 -
逻辑回归模型实现
2.1 方法一:手动实现逻辑回归算法
2.2 方法二:调用sklearn库函数 -
五折交叉验证训练与评估
3.1 数据划分为5折
3.2 逐折训练和测试
3.3 计算每折的评估指标 -
结果分析与模型评估
4.1 计算平均性能指标
4.2 可视化结果
4.3 模型性能分析 -
算法主要代码
-- coding: utf-8 --
"""
逻辑回归算法实现与五折交叉验证测试
实验二:对数几率回归算法实现与测试
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
def load_and_analyze_data():
"""
加载iris数据集并进行基本数据分析
Returns:
X: 特征数据
y: 标签数据
data_df: 数据的DataFrame格式,用于分析
"""
# 加载iris数据集
iris = load_iris()
X = iris.data
y = iris.target
创建DataFrame用于数据分析
feature_names = iris.feature_names
target_names = iris.target_names
data_df = pd.DataFrame(X, columns=feature_names)
data_df['target'] = y
data_df['target_name'] = [target_names[i] for i in y]
print("=== 数据集基本信息 ===")
print(f"数据集形状: {X.shape}")
print(f"特征名称: {feature_names}")
print(f"类别名称: {target_names}")
print(f"类别分布: {np.bincount(y)}")
print("\n前5行数据:")
print(data_df.head())
print("\n数据统计描述:")
print(data_df.describe())
return X, y, data_df
def visualize_data(data_df):
"""
可视化数据集特征分布
Args:
data_df: 包含数据的DataFrame
"""
# 创建特征分布图
plt.figure(figsize=(15, 10))
特征直方图
feature_names = data_df.columns[:4]
for i, feature in enumerate(feature_names):
plt.subplot(2, 2, i+1)
for target, color in zip([0, 1, 2], ['blue', 'green', 'red']):
plt.hist(data_df[data_df['target'] == target][feature],
alpha=0.5, label=data_df['target_name'].unique()[target], color=color)
plt.title(f'{feature} distribution')
plt.xlabel(feature)
plt.ylabel('Frequency')
plt.legend()
plt.tight_layout()
plt.savefig('feature_distributions.png')
plt.close()
print("特征分布图已保存为 'feature_distributions.png'")
class LogisticRegressionSelf:
"""
自定义逻辑回归实现(多分类,使用一对多策略)
"""
def init(self, learning_rate=0.01, max_iterations=1000, tol=1e-4):
"""
初始化逻辑回归模型
Args:
learning_rate: 学习率
max_iterations: 最大迭代次数
tol: 收敛阈值
"""
self.learning_rate = learning_rate
self.max_iterations = max_iterations
self.tol = tol
self.weights = None
self.biases = None
self.classes = None
def sigmoid(self, z):
"""
Sigmoid激活函数
Args:
z: 线性组合结果
Returns:
经过sigmoid变换后的概率值
"""
return 1 / (1 + np.exp(-np.clip(z, -250, 250))) # 防止溢出
def fit_binary(self, X, y):
"""
二分类逻辑回归的训练函数
Args:
X: 训练特征数据
y: 训练标签(二分类)
"""
m, n = X.shape
# 初始化权重和偏置
weights = np.zeros(n)
bias = 0
梯度下降
for i in range(self.max_iterations):
# 前向传播
z = np.dot(X, weights) + bias
y_pred = self.sigmoid(z)
计算梯度
dw = (1/m) * np.dot(X.T, (y_pred - y))
db = (1/m) * np.sum(y_pred - y)
更新参数
weights -= self.learning_rate * dw
bias -= self.learning_rate * db
检查收敛
if np.linalg.norm(dw) < self.tol:
break
return weights, bias
def fit(self, X, y):
"""
多分类逻辑回归训练函数(一对多策略)
Args:
X: 训练特征数据
y: 训练标签
"""
self.classes = np.unique(y)
n_classes = len(self.classes)
n_features = X.shape[1]
为每个类别训练一个二分类器
self.weights = np.zeros((n_classes, n_features))
self.biases = np.zeros(n_classes)
for i, c in enumerate(self.classes):
# 创建二分类标签
binary_y = np.where(y == c, 1, 0)
# 训练二分类器
w, b = self.fit_binary(X, binary_y)
self.weights[i] = w
self.biases[i] = b
def predict(self, X):
"""
预测函数
Args:
X: 测试特征数据
Returns:
预测的类别标签
"""
# 计算每个类别的概率分数
scores = np.dot(X, self.weights.T) + self.biases
# 返回概率最大的类别
return self.classes[np.argmax(scores, axis=1)]
def k_fold_cross_validation(X, y, model_type='sklearn', n_splits=5):
"""
五折交叉验证函数
Args:
X: 特征数据
y: 标签数据
model_type: 模型类型,'sklearn' 或 'self'
n_splits: 交叉验证折数
Returns:
各折的性能指标平均值
"""
kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)
存储各折的性能指标
accuracies = []
precisions = []
recalls = []
f1_scores = []
fold = 1
for train_idx, test_idx in kf.split(X):
print(f"\n=== 第 {fold} 折交叉验证 ===")
划分训练集和测试集
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
数据标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
选择模型
if model_type == 'sklearn':
# 使用scikit-learn的逻辑回归模型
# 参数说明:
# - C: 正则化强度的倒数,默认值为1.0,值越小正则化越强
# - multi_class: 多分类策略,'multinomial'表示使用多项式逻辑回归
# - solver: 优化算法,'lbfgs'是默认的求解器,适用于多分类问题
# - max_iter: 最大迭代次数,默认值为100
model = LogisticRegression(C=1.0, multi_class='multinomial',
solver='lbfgs', max_iter=1000, random_state=42)
model.fit(X_train_scaled, y_train)
else:
# 使用自定义的逻辑回归模型
model = LogisticRegressionSelf(learning_rate=0.01, max_iterations=1000)
model.fit(X_train_scaled, y_train)
预测
y_pred = model.predict(X_test_scaled)
计算性能指标
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, average='macro')
recall = recall_score(y_test, y_pred, average='macro')
f1 = f1_score(y_test, y_pred, average='macro')
print(f"准确率: {accuracy:.4f}")
print(f"精确率: {precision:.4f}")
print(f"召回率: {recall:.4f}")
print(f"F1值: {f1:.4f}")
accuracies.append(accuracy)
precisions.append(precision)
recalls.append(recall)
f1_scores.append(f1)
fold += 1
计算平均值
avg_accuracy = np.mean(accuracies)
avg_precision = np.mean(precisions)
avg_recall = np.mean(recalls)
avg_f1 = np.mean(f1_scores)
print("\n=== 五折交叉验证结果汇总 ===")
print(f"平均准确率: {avg_accuracy:.4f} ± {np.std(accuracies):.4f}")
print(f"平均精确率: {avg_precision:.4f} ± {np.std(precisions):.4f}")
print(f"平均召回率: {avg_recall:.4f} ± {np.std(recalls):.4f}")
print(f"平均F1值: {avg_f1:.4f} ± {np.std(f1_scores):.4f}")
return {
'accuracy': avg_accuracy,
'precision': avg_precision,
'recall': avg_recall,
'f1': avg_f1
}
def compare_models(X, y):
"""
比较自定义模型和sklearn模型的性能
Args:
X: 特征数据
y: 标签数据
"""
print("\n=== 比较自定义逻辑回归模型和sklearn模型 ===")
使用sklearn模型
print("\n[1] scikit-learn 逻辑回归模型:")
sklearn_results = k_fold_cross_validation(X, y, model_type='sklearn')
使用自定义模型
print("\n[2] 自定义逻辑回归模型:")
self_results = k_fold_cross_validation(X, y, model_type='self')
可视化比较结果
metrics = ['accuracy', 'precision', 'recall', 'f1']
sklearn_values = [sklearn_results[m] for m in metrics]
self_values = [self_results[m] for m in metrics]
x = np.arange(len(metrics))
width = 0.35
plt.figure(figsize=(12, 6))
plt.bar(x - width/2, sklearn_values, width, label='scikit-learn')
plt.bar(x + width/2, self_values, width, label='Custom Implementation')
plt.xlabel('Performance Metrics')
plt.ylabel('Score')
plt.title('Model Performance Comparison')
plt.xticks(x, ['Accuracy', 'Precision', 'Recall', 'F1 Score'])
plt.legend()
plt.ylim(0.8, 1.0)
添加数值标签
for i, v in enumerate(sklearn_values):
plt.text(i - width/2, v + 0.01, f'{v:.4f}', ha='center')
for i, v in enumerate(self_values):
plt.text(i + width/2, v + 0.01, f'{v:.4f}', ha='center')
plt.tight_layout()
plt.savefig('model_comparison.png')
plt.close()
print("模型比较图已保存为 'model_comparison.png'")
def main():
"""
主函数,执行完整的逻辑回归实验流程
"""
print("=== 逻辑回归算法实现与五折交叉验证实验 ===")
1. 加载和分析数据
X, y, data_df = load_and_analyze_data()
2. 数据可视化
visualize_data(data_df)
3. 比较模型性能
compare_models(X, y)
print("\n=== 实验完成 ===")
if name == "main":
main()
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 0.9733 0.9768 0.9733 0.9732
3.对比分析
模型表现评价
从实验结果可以看出, 对数几率回归 在各项指标上均略优于 随机森林 模型:
- 准确率:97.33%(对数几率回归)vs 96.67%(随机森林)
- 查准率:97.68% vs 97.07%
- 召回率:97.33% vs 96.67%
- F1值:97.32% vs 96.65%
两个模型的标准差都较小(均在0.025以内),说明它们的稳定性都很好,泛化能力强。
结果原因讨论 1. 数据集特性
Iris数据集是一个经典的 线性可分 数据集,四个特征之间存在明显的线性边界可以区分三个类别。对数几率回归作为线性模型,能够很好地捕捉这种线性关系,因此表现更优。
2. 模型复杂度匹配
- 对数几率回归 :属于简单线性模型,参数少,训练高效,不容易过拟合
- 随机森林 :属于复杂非线性模型,通过多棵决策树集成获得高性能,但在Iris这种简单数据集上,模型复杂度可能超出需求,导致轻微的过拟合倾向 3. 特征与目标关系
Iris数据集中的特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)与类别(三种鸢尾花)之间存在较强的线性相关性,非常适合对数几率回归这种基于线性假设的模型。
改进方案 1. 参数调优
- 对数几率回归 :可调整正则化参数 C (控制模型复杂度)、求解器类型 solver
- 随机森林 :可调整树的数量 n_estimators 、树的最大深度 max_depth 、最小样本分割数 min_samples_split 等
通过网格搜索(GridSearchCV)或随机搜索(RandomizedSearchCV)进行超参数优化,可能进一步提升模型性能。
- 特征工程
- 尝试 特征标准化/归一化 :虽然对数几率回归对量纲不敏感,但标准化可能加速收敛
- 特征组合 :尝试创建新特征(如花瓣面积=花瓣长度×花瓣宽度)
- 特征选择 :使用方差阈值、互信息等方法筛选最相关的特征 3. 模型融合
将对数几率回归和随机森林的预测结果进行融合(如加权投票、Stacking),结合两个模型的优势,可能获得更优的性能。
- 尝试其他模型
- 支持向量机(SVM) :在高维空间中表现优异,适合线性可分数据集
- K近邻(KNN) :非参数模型,对Iris这种小型数据集可能有不错表现
- 神经网络 :浅层神经网络可能进一步提升性能,但需注意过拟合 5. 交叉验证优化
采用 分层K折交叉验证 (StratifiedKFold),确保每个折中的类别分布与原始数据集一致,提高评估结果的可靠性。
总结
对数几率回归在Iris数据集上表现更好,主要得益于其线性模型特性与数据集线性可分的特点相匹配。虽然两个模型的表现都很优秀,但通过适当的参数调优、特征工程和模型融合,仍有进一步提升的空间。
对于简单的分类任务,选择与数据集特性匹配的简单模型往往能获得更好的效果,同时具有更高的训练效率和更低的过拟合风险。
4.心得体会
通过本次Iris数据集的五折交叉验证实验,我深入理解了机器学习中 模型选择、评估和对比分析 的完整流程。从最初的数据加载、模型训练,到后续的指标计算和结果对比,每一步都让我对机器学习的核心概念有了更直观的认识。
对模型选择的思考
实验中我对比了对数几率回归和随机森林两种模型,发现 简单模型在合适的场景下往往表现更优 。Iris数据集的线性可分特性使得对数几率回归这种线性模型能够充分发挥优势,而复杂的随机森林模型虽然强大,却在这种简单数据集上没有体现出明显优势。这让我认识到: 模型的选择应当与数据集的特性相匹配 ,并非越复杂的模型效果越好。
交叉验证的重要性
五折交叉验证的应用让我深刻理解了 评估模型泛化能力 的重要性。通过将数据划分为多个互不重叠的折,轮流作为训练集和测试集,能够更客观地评估模型在未知数据上的表现,避免了单次分割可能带来的偶然性误差。这种评估方法在实际项目中尤为重要,能够有效防止模型过拟合。
特征工程的意义
虽然本次实验没有进行复杂的特征工程,但从模型结果中我意识到 特征质量对模型性能的关键影响 。Iris数据集的特征与目标类别之间存在较强的线性关系,这是对数几率回归表现优异的重要原因。在未来的学习中,我会更加重视特征选择、变换和组合等特征工程技术,这往往是提升模型性能的关键。
实际应用的启示
实验结果也让我思考了 机器学习在实际应用中的挑战 。真实世界的数据往往比Iris数据集复杂得多,可能存在噪声、缺失值、类别不平衡等问题。这需要我们在模型选择之外,还要掌握数据预处理、异常值处理、不平衡数据处理等多种技术。
未来学习的方向
通过本次实验,我明确了未来机器学习学习的几个重要方向:
- 深入理解模型原理 :不仅要会使用模型,更要理解其背后的数学原理和适用场景
- 掌握超参数调优技术 :学会使用网格搜索、随机搜索等方法优化模型参数
- 精通特征工程 :掌握多种特征处理方法,提升数据质量
- 学习模型融合技术 :结合不同模型的优势,进一步提升性能
总结
本次实验是一次宝贵的实践经历,让我从理论走向了实践,深刻体会到了机器学习"数据-模型-评估"的完整闭环。实验结果不仅验证了理论知识,更让我认识到机器学习是一门需要不断实践和探索的学科。在未来的学习和工作中,我会继续保持这种实践精神,不断提升自己的机器学习能力。
实验三:C4.5(带有预剪枝和后剪枝)算法实现与测试
一、实验目的
深入理解决策树、预剪枝和后剪枝的算法原理,能够使用 Python 语言实现带有预剪枝和后剪枝的决策树算法 C4.5 算法的训练与测试,并且使用五折交叉验证算法进行模型训练与评估。
二、实验内容
1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析;(2)采用五折交叉验证划分训练集和测试集,使用训练集对 C4.5 分类算法进行训练;(3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试;(4)通过对测试结果进行比较分析,评估模型性能;(5)完成实验报告中实验三的部分。
三、算法步骤、代码、及结果
-
算法伪代码
算法:C4.5决策树实现与剪枝
输入:iris数据集(分类问题)
输出:带剪枝的决策树模型及性能评估
步骤: -
数据准备阶段
1.1 加载iris数据集
1.2 数据探索和预处理
1.3 处理连续特征(iris的特征是连续的) -
决策树构建阶段
2.1 信息增益比计算
2.2 递归构建决策树
2.3 预剪枝策略实施 -
决策树剪枝阶段
3.1 后剪枝处理
3.2 树复杂度控制 -
模型评估阶段
4.1 五折交叉验证
4.2 性能指标计算
4.3 结果分析 -
算法主要代码
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
import math
import time
class Node:
"""
决策树节点类
"""
def init(self, is_leaf=False, label=None, feature=None, threshold=None, children=None):
self.is_leaf = is_leaf # 是否为叶节点
self.label = label # 叶节点的类别标签
self.feature = feature # 用于分割的特征索引
self.threshold = threshold # 连续特征的分割阈值
self.children = children if children else {} # 子节点字典
class C45DecisionTree:
"""
C4.5决策树算法实现,包含预剪枝和后剪枝功能
"""
def init(self, max_depth=None, min_samples_split=2, min_samples_leaf=1,
prune_method=None, confidence_threshold=0.05, use_pruning=True):
"""
初始化C4.5决策树
参数:
- max_depth: 树的最大深度,用于预剪枝
- min_samples_split: 节点分裂所需的最小样本数,用于预剪枝
- min_samples_leaf: 叶节点所需的最小样本数,用于预剪枝
- prune_method: 剪枝方法,'pre'为预剪枝,'post'为后剪枝,None为不剪枝
- confidence_threshold: 用于后剪枝的置信度阈值
- use_pruning: 是否使用剪枝
"""
self.root = None
self.max_depth = max_depth
self.min_samples_split = min_samples_split
self.min_samples_leaf = min_samples_leaf
self.prune_method = prune_method
self.confidence_threshold = confidence_threshold
self.use_pruning = use_pruning
def entropy(self, y):
"""
计算熵
"""
if len(y) == 0:
return 0
# 计算各类别概率
_, counts = np.unique(y, return_counts=True)
probabilities = counts / len(y)
# 计算熵
entropy_value = -np.sum(probabilities * np.log2(probabilities))
return entropy_value
def information_gain_ratio(self, X, y, feature_idx, threshold=None):
"""
计算信息增益比
"""
# 计算原始熵
original_entropy = self.entropy(y)
# 如果是连续特征,需要根据阈值进行划分
if threshold is not None:
left_mask = X[:, feature_idx] <= threshold
right_mask = X[:, feature_idx] > threshold
left_y, right_y = y[left_mask], y[right_mask]
if len(left_y) == 0 or len(right_y) == 0:
return -np.inf
# 计算条件熵
left_entropy = self.entropy(left_y)
right_entropy = self.entropy(right_y)
weight_left = len(left_y) / len(y)
weight_right = len(right_y) / len(y)
conditional_entropy = weight_left * left_entropy + weight_right * right_entropy
# 计算信息增益
information_gain = original_entropy - conditional_entropy
# 计算分裂信息
split_info = -weight_left * np.log2(weight_left) - weight_right * np.log2(weight_right)
# 计算信息增益比
if split_info == 0:
return 0
gain_ratio = information_gain / split_info
return gain_ratio
else: # 离散特征
unique_values = np.unique(X[:, feature_idx])
weighted_entropy = 0
split_info = 0
for value in unique_values:
mask = X[:, feature_idx] == value
subset_y = y[mask]
weight = len(subset_y) / len(y)
weighted_entropy += weight * self.entropy(subset_y)
split_info -= weight * np.log2(weight)
information_gain = original_entropy - weighted_entropy
if split_info == 0:
return 0
gain_ratio = information_gain / split_info
return gain_ratio
def find_best_split(self, X, y):
"""
寻找最佳分裂特征和阈值
"""
best_feature = None
best_threshold = None
best_gain_ratio = -np.inf
n_features = X.shape[1]
for feature_idx in range(n_features):
# 对连续特征寻找最佳阈值
unique_values = np.unique(X[:, feature_idx])
# 如果特征值较少,考虑作为离散特征处理
if len(unique_values) <= 10:
# 离散特征处理
gain_ratio = self.information_gain_ratio(X, y, feature_idx)
if gain_ratio > best_gain_ratio:
best_gain_ratio = gain_ratio
best_feature = feature_idx
best_threshold = None
else:
# 连续特征处理,尝试所有可能的阈值
thresholds = (unique_values[:-1] + unique_values[1:]) / 2 # 取中间值作为候选阈值
for threshold in thresholds:
gain_ratio = self.information_gain_ratio(X, y, feature_idx, threshold)
if gain_ratio > best_gain_ratio:
best_gain_ratio = gain_ratio
best_feature = feature_idx
best_threshold = threshold
return best_feature, best_threshold
def majority_vote(self, y):
"""
多数投票确定类别
"""
if len(y) == 0:
return None
values, counts = np.unique(y, return_counts=True)
return values[np.argmax(counts)]
def build_tree(self, X, y, depth=0):
"""
递归构建决策树
"""
# 如果所有样本属于同一类别,创建叶节点
if len(np.unique(y)) == 1:
return Node(is_leaf=True, label=y[0])
# 如果达到最大深度,创建叶节点(预剪枝)
if self.use_pruning and self.prune_method == 'pre' and self.max_depth is not None and depth >= self.max_depth:
return Node(is_leaf=True, label=self.majority_vote(y))
# 如果样本数少于最小分裂样本数,创建叶节点(预剪枝)
if self.use_pruning and self.prune_method == 'pre' and len(X) < self.min_samples_split:
return Node(is_leaf=True, label=self.majority_vote(y))
# 寻找最佳分裂点
best_feature, best_threshold = self.find_best_split(X, y)
# 如果无法找到有意义的分裂点,创建叶节点
if best_feature is None:
return Node(is_leaf=True, label=self.majority_vote(y))
# 创建决策节点
node = Node(feature=best_feature, threshold=best_threshold)
# 根据最佳分裂点分割数据并递归构建子树
if best_threshold is not None: # 连续特征
left_mask = X[:, best_feature] <= best_threshold
right_mask = X[:, best_feature] > best_threshold
# 预剪枝:检查子节点样本数
if self.use_pruning and self.prune_method == 'pre':
if len(X[left_mask]) < self.min_samples_leaf or len(X[right_mask]) < self.min_samples_leaf:
return Node(is_leaf=True, label=self.majority_vote(y))
node.children['<='] = self.build_tree(X[left_mask], y[left_mask], depth + 1)
node.children['>'] = self.build_tree(X[right_mask], y[right_mask], depth + 1)
else: # 离散特征
unique_values = np.unique(X[:, best_feature])
for value in unique_values:
mask = X[:, best_feature] == value
subset_X, subset_y = X[mask], y[mask]
# 预剪枝:检查子节点样本数
if self.use_pruning and self.prune_method == 'pre':
if len(subset_X) < self.min_samples_leaf:
continue
node.children[value] = self.build_tree(subset_X, subset_y, depth + 1)
return node
def fit(self, X, y):
"""
训练决策树
"""
self.root = self.build_tree(X, y)
# 如果启用后剪枝
if self.use_pruning and self.prune_method == 'post':
self.prune_tree(X, y)
def prune_tree(self, X, y):
"""
后剪枝函数,使用悲观错误剪枝法
"""
def _prune(node, X, y):
if node.is_leaf:
return node, len(y), np.sum(y == node.label)
correct_predictions = 0
total_samples = 0
child_nodes = []
# 递归剪枝子节点
if node.threshold is not None: # 连续特征
left_mask = X[:, node.feature] <= node.threshold
right_mask = X[:, node.feature] > node.threshold
if len(X[left_mask]) > 0:
node.children['<='], left_total, left_correct = _prune(node.children['<='], X[left_mask], y[left_mask])
total_samples += left_total
correct_predictions += left_correct
if len(X[right_mask]) > 0:
node.children['>'], right_total, right_correct = _prune(node.children['>'], X[right_mask], y[right_mask])
total_samples += right_total
correct_predictions += right_correct
else: # 离散特征
for value, child in node.children.items():
mask = X[:, node.feature] == value
if np.any(mask):
pruned_child, child_total, child_correct = _prune(child, X[mask], y[mask])
node.children[value] = pruned_child
total_samples += child_total
correct_predictions += child_correct
# 计算不剪枝的错误率
error_rate = 1 - correct_predictions / total_samples if total_samples > 0 else 0
# 计算剪枝后的错误率(多数投票)
majority_label = self.majority_vote(y)
pruned_correct = np.sum(y == majority_label)
pruned_error_rate = 1 - pruned_correct / len(y) if len(y) > 0 else 0
# 使用卡方检验判断是否需要剪枝
if self._should_prune(error_rate, pruned_error_rate, total_samples, self.confidence_threshold):
return Node(is_leaf=True, label=majority_label), len(y), pruned_correct
return node, total_samples, correct_predictions
self.root, _, _ = _prune(self.root, X, y)
def _should_prune(self, error_rate, pruned_error_rate, n_samples, confidence_threshold):
"""
使用卡方检验决定是否剪枝
"""
# 简化版本:如果剪枝后的错误率更低或不显著更高,则剪枝
return pruned_error_rate <= error_rate + confidence_threshold
def predict_sample(self, x, node):
"""
预测单个样本
"""
if node.is_leaf:
return node.label
if node.threshold is not None: # 连续特征
if x[node.feature] <= node.threshold:
return self.predict_sample(x, node.children.get('<=', Node(is_leaf=True, label=self.majority_vote(np.array([0])))))
else:
return self.predict_sample(x, node.children.get('>', Node(is_leaf=True, label=self.majority_vote(np.array([0])))))
else: # 离散特征
if x[node.feature] in node.children:
return self.predict_sample(x, node.children[x[node.feature]])
else:
# 如果遇到未知的特征值,返回叶节点
return self.majority_vote(np.array([0]))
def predict(self, X):
"""
预测多个样本
"""
predictions = []
for x in X:
predictions.append(self.predict_sample(x, self.root))
return np.array(predictions)
def k_fold_cross_validation(X, y, model, n_splits=5):
"""
五折交叉验证
"""
kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)
accuracy_scores = []
precision_scores = []
recall_scores = []
f1_scores = []
for train_index, test_index in kf.split(X):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# 训练模型
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 计算指标
accuracy_scores.append(accuracy_score(y_test, y_pred))
precision_scores.append(precision_score(y_test, y_pred, average='macro'))
recall_scores.append(recall_score(y_test, y_pred, average='macro'))
f1_scores.append(f1_score(y_test, y_pred, average='macro'))
return {
'accuracy': np.mean(accuracy_scores),
'precision': np.mean(precision_scores),
'recall': np.mean(recall_scores),
'f1': np.mean(f1_scores),
'accuracy_std': np.std(accuracy_scores),
'precision_std': np.std(precision_scores),
'recall_std': np.std(recall_scores),
'f1_std': np.std(f1_scores)
}
def analyze_iris_dataset():
"""
分析iris数据集
"""
# 加载数据集
iris = load_iris()
X, y = iris.data, iris.target
# 转换为DataFrame以便分析
df = pd.DataFrame(X, columns=iris.feature_names)
df['target'] = y
df['target_names'] = [iris.target_names[i] for i in y]
print("=== Iris数据集分析 ===")
print(f"数据集形状: {X.shape}")
print(f"类别分布: {np.bincount(y)}")
print(f"类别名称: {iris.target_names}")
print("\n特征统计:")
print(df.describe())
print("\n前5行数据:")
print(df.head())
return X, y, iris
def main():
"""
主函数,用于运行实验
"""
# 分析数据集
X, y, iris = analyze_iris_dataset()
print("\n=== C4.5决策树算法实验 =")
# 测试不同剪枝方法
pruning_methods = [
{'name': '无剪枝', 'prune_method': None, 'use_pruning': False},
{'name': '预剪枝', 'prune_method': 'pre', 'use_pruning': True},
{'name': '后剪枝', 'prune_method': 'post', 'use_pruning': True}
]
results = []
for method in pruning_methods:
print(f"\n{method['name']}实验:")
start_time = time.time()
# 创建模型
model = C45DecisionTree(
max_depth=10,
min_samples_split=3,
min_samples_leaf=1,
prune_method=method['prune_method'],
confidence_threshold=0.05,
use_pruning=method['use_pruning']
)
# 五折交叉验证
metrics = k_fold_cross_validation(X, y, model)
end_time = time.time()
print(f"准确度: {metrics['accuracy']:.4f} ± {metrics['accuracy_std']:.4f}")
print(f"精确率: {metrics['precision']:.4f} ± {metrics['precision_std']:.4f}")
print(f"召回率: {metrics['recall']:.4f} ± {metrics['recall_std']:.4f}")
print(f"F1值: {metrics['f1']:.4f} ± {metrics['f1_std']:.4f}")
print(f"运行时间: {end_time - start_time:.4f}秒")
results.append({
'方法': method['name'],
'准确度': metrics['accuracy'],
'精确率': metrics['precision'],
'召回率': metrics['recall'],
'F1值': metrics['f1'],
'运行时间': end_time - start_time
})
# 输出比较结果
print("\n= 方法比较 ===")
for result in results:
print(f"{result['方法']}😊
print(f" 准确度: {result['准确度']:.4f}")
print(f" 精确率: {result['精确率']:.4f}")
print(f" 召回率: {result['召回率']:.4f}")
print(f" F1值: {result['F1值']:.4f}")
if name == "main":
main()
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 0.8800 0.9104 0.8782 0.8711
2 C4.5 0.9467 0.9501 0.9489 0.9456
2.对比分析
根据实验结果,对对数几率回归和C4.5决策树进行对比分析:
1. 性能指标对比
指标 对数几率回归 C4.5(后剪枝) 差异分析 准确度 0.8800 0.9467 C4.5高出6.67个百分点 精确率 0.9104 0.9501 C4.5高出3.97个百分点 召回率 0.8782 0.9489 C4.5高出7.07个百分点 F1值 0.8711 0.9456 C4.5高出7.45个百分点
2. 训练效率对比
- 对数几率回归 :0.1912秒
- C4.5决策树 :0.0745秒(后剪枝)
- 差异 :C4.5决策树训练速度比对数几率回归快约2.57倍
3. 模型原理对比
特性 对数几率回归 C4.5决策树 模型类型 线性模型 非线性模型(树结构) 假设条件 特征线性可分 无严格假设 决策边界 线性边界 分段线性边界 特征处理 数值特征直接使用 支持连续和离散特征 可解释性 中等(通过系数大小) 高(可视化树结构)
4. 优缺点分析 对数几率回归
优点 :
-
参数少,模型简单,易于理解
-
训练和预测速度较快
-
输出概率值,便于概率解释
-
对噪声数据鲁棒性较好
缺点 : -
假设特征线性可分,对于复杂数据拟合能力有限
-
对异常值敏感
-
特征工程要求高,需要手动处理非线性关系 C4.5决策树
优点 : -
无需假设特征线性关系,拟合能力强
-
可解释性强,决策过程直观
-
自动处理特征选择和交互
-
支持多种类型特征
-
后剪枝技术有效防止过拟合
缺点 : -
容易过拟合(未剪枝时)
-
对噪声数据敏感
-
训练时间随特征数量和样本量增加而显著增长
-
容易产生偏向于具有较多取值的特征
5. 适用场景对比
-
对数几率回归适用于 :
- 数据线性可分或近似线性可分的场景
- 需要快速训练和预测的场景
- 需要概率输出的场景
- 特征维度较高但样本量不大的场景
-
C4.5决策树适用于 :
- 数据非线性关系复杂的场景
- 需要高可解释性的场景
- 特征类型多样(连续+离散)的场景
- 小到中等规模数据集
- 需要自动特征选择的场景
6. 实验结果结论
在本次Iris数据集实验中,C4.5决策树(特别是后剪枝版本)在各项性能指标上均优于对数几率回归,表现出更好的分类能力和稳定性。这主要是因为:
- Iris数据集的特征与类别之间存在非线性关系,C4.5决策树能够更好地捕捉这种关系
- C4.5决策树通过后剪枝技术有效避免了过拟合,提高了模型泛化能力
- 决策树能够自动进行特征选择,找到最具区分性的特征组合
7. 改进建议
- 对数几率回归 :可以尝试添加多项式特征、使用正则化技术(L1/L2)、调整学习率等方法提升性能
- C4.5决策树 :可以进一步优化剪枝参数、考虑集成学习方法(如随机森林)、调整树的深度和最小样本数等超参数
总体而言,两种算法各有优缺点,在实际应用中应根据具体问题和数据特点选择合适的算法。对于Iris这类小型数据集,C4.5决策树表现更为出色。
- 心得体会
通过本次C4.5决策树与对数几率回归算法的实现与对比实验,我获得了以下心得体会:
1. 算法原理是实现的基础
在实现对数几率回归时,我深入理解了sigmoid激活函数、梯度下降优化、多分类策略(一对剩余法)等核心概念;在实现C4.5决策树时,掌握了信息增益、增益率、连续特征离散化、剪枝技术等关键技术。这些原理的深入理解是正确实现算法的前提,也帮助我在调试过程中快速定位问题。
2. 代码实现细节决定成败
实验中遇到了两个关键bug:一是对数几率回归的predict方法中索引越界问题,二是fit方法没有清空之前的模型导致累积错误。这些细节问题表明,即使算法原理理解正确,代码实现中的边界条件、状态管理等细节处理同样重要。通过调试这些问题,我提高了代码严谨性和调试能力。
3. 模型评估需要全面指标
仅仅依靠准确率不能全面评价模型性能,本次实验同时使用了精确率、召回率和F1值进行评估。特别是在Iris数据集这种多分类任务中,不同类别之间的分类难度可能不同,综合指标能更全面反映模型的真实性能。
4. 算法选择应结合数据特点
实验结果显示C4.5决策树在Iris数据集上表现更优,这是因为Iris数据的特征与类别之间存在非线性关系,而决策树能够更好地捕捉这种关系。这说明在实际应用中,应根据数据特点(线性/非线性、特征类型、数据规模等)选择合适的算法,而非盲目追求复杂模型。
5. 剪枝技术是决策树性能的关键
实验对比了无剪枝、预剪枝和后剪枝三种策略,结果显示后剪枝效果最佳。这表明适当的剪枝技术能够有效防止过拟合,提高模型泛化能力。在实际应用中,需要根据数据集特点选择合适的剪枝策略和参数。
6. 机器学习是理论与实践的结合
本次实验不仅加深了对算法理论的理解,也通过实际代码实现和实验结果分析,体会到了机器学习的实践性。从数据预处理、模型构建、训练调试到结果分析,每一个环节都需要仔细设计和验证,理论指导实践,实践反过来深化对理论的理解。
7. 持续学习和优化的重要性
虽然实验取得了较好的结果,但仍有优化空间,比如对数几率回归可以尝试正则化、特征归一化等方法,C4.5决策树可以结合集成学习(如随机森林)进一步提升性能。机器学习领域不断发展,需要保持持续学习的态度,不断探索新的算法和技术。
通过本次实验,我不仅掌握了两种经典分类算法的实现和评估方法,也提高了代码实现能力和问题调试能力,更重要的是培养了从数据出发、结合算法原理、全面评估模型的机器学习思维方式。这些收获将对我后续的机器学习学习和实践产生积极影响
实验四:SMO 算法实现与测试
一、实验目的
深入理解支持向量机(SVM)的算法原理,能够使用 Python 语言实现支持向量机的训练与测试,并且使用五折交叉验证算法进行模型训练与评估。
二、实验内容
(1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析;(2)采用五折交叉验证划分训练集和测试集,使用训练集对 SMO 支持向量机分类算法进行训练;(3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试;(4)通过对测试结果进行比较分析,评估模型性能;(5)完成实验报告中实验四的部分。
3. 操作要点
(1)可以选择自行编写源代码完成 SMO 支持向量机算法,或者调用 scikit-learn 库中的函数;(2)如果调用 scikit-learn 库中的函数,需要说明函数各个参数的含义(名称)、解释或说明(包括作用、取值等)、默认值(如有,可在备注列写出)等,即自行编写代码只需要粘贴完整的带有注释的源代码即可,调用函数则包括粘贴源代码和函数参数说明两部分;
三、算法步骤、代码、及结果
-
算法伪代码
算法:SMO算法实现与评估
输入:iris数据集
输出:SVM模型及性能评估
步骤: -
数据准备阶段
1.1 加载iris数据集
1.2 数据标准化处理
1.3 标签处理(支持二分类,需进行OvR或OvO扩展) -
核函数实现阶段
2.1 线性核函数
2.2 高斯核函数(RBF)
2.3 多项式核函数 -
SMO算法实现阶段
3.1 初始化拉格朗日乘子α
3.2 选择工作集(启发式选择)
3.3 优化两个乘子
3.4 更新阈值b -
模型评估阶段
4.1 五折交叉验证
4.2 性能指标计算
4.3 决策边界可视化 -
算法主要代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
class SVM:
"""
支持向量机分类器,使用简化版SMO算法实现
"""
def init(self, C=1.0, kernel='linear', tol=1e-3, max_iter=1000):
"""
初始化SVM参数
Parameters:
-----------
C : float, default=1.0
惩罚参数,控制误分类样本的惩罚程度
kernel : str, default='linear'
核函数类型,目前仅支持'linear'
tol : float, default=1e-3
容差参数,用于判断KKT条件是否满足
max_iter : int, default=1000
最大迭代次数
"""
self.C = C
self.kernel = kernel
self.tol = tol
self.max_iter = max_iter
# 模型参数
self.alpha = None
self.b = 0
self.X = None
self.y = None
self.m = None
self.n = None
def _kernel(self, X1, X2):
"""
核函数计算
Parameters:
-----------
X1 : numpy.ndarray
第一个样本集
X2 : numpy.ndarray
第二个样本集
Returns:
--------
numpy.ndarray
核函数计算结果
"""
if self.kernel == 'linear':
return np.dot(X1, X2.T)
# 可扩展其他核函数
# elif self.kernel == 'rbf':
# return np.exp(-self.gamma * np.linalg.norm(X1[:, np.newaxis] - X2, axis=2) ** 2)
def _predict(self, X_test):
"""
预测函数
Parameters:
-----------
X_test : numpy.ndarray
测试样本集
Returns:
--------
numpy.ndarray
预测结果
"""
# 计算决策函数值
f_x = np.dot(self.alpha * self.y, self._kernel(self.X, X_test)) + self.b
# 返回符号函数结果
return np.sign(f_x)
def _select_j(self, i, m):
"""
选择第二个变量j,不同于i
Parameters:
-----------
i : int
第一个变量的索引
m : int
样本数量
Returns:
--------
int
第二个变量的索引
"""
j = i
while j == i:
j = np.random.randint(0, m)
return j
def fit(self, X, y):
"""
训练SVM模型
Parameters:
-----------
X : numpy.ndarray
训练样本集,形状为(m, n)
y : numpy.ndarray
训练标签集,形状为(m,)
"""
# 初始化参数
self.X = X
self.y = y
self.m, self.n = X.shape
self.alpha = np.zeros(self.m)
iter_count = 0
while iter_count < self.max_iter:
alpha_changed = 0
for i in range(self.m):
# 计算第i个样本的预测值
f_xi = np.dot(self.alpha * self.y, self._kernel(self.X, self.X[i])) + self.b
# 计算误差
Ei = f_xi - self.y[i]
# 检查KKT条件是否满足
if ((self.y[i] * Ei < -self.tol and self.alpha[i] < self.C) or
(self.y[i] * Ei > self.tol and self.alpha[i] > 0)):
# 选择第二个变量j
j = self._select_j(i, self.m)
# 计算第j个样本的预测值和误差
f_xj = np.dot(self.alpha * self.y, self._kernel(self.X, self.X[j])) + self.b
Ej = f_xj - self.y[j]
# 保存旧的alpha值
alpha_i_old = self.alpha[i].copy()
alpha_j_old = self.alpha[j].copy()
# 计算上下界L和H
if self.y[i] != self.y[j]:
L = max(0, self.alpha[j] - self.alpha[i])
H = min(self.C, self.C + self.alpha[j] - self.alpha[i])
else:
L = max(0, self.alpha[i] + self.alpha[j] - self.C)
H = min(self.C, self.alpha[i] + self.alpha[j])
if L == H:
continue
# 计算eta
eta = 2.0 * self._kernel(self.X[i], self.X[j]) -
self._kernel(self.X[i], self.X[i]) -
self._kernel(self.X[j], self.X[j])
if eta >= 0:
continue
# 更新alpha[j]
self.alpha[j] -= self.y[j] * (Ei - Ej) / eta
# 截断alpha[j]到[L, H]范围内
self.alpha[j] = max(L, min(H, self.alpha[j]))
# 检查alpha[j]的变化是否足够大
if abs(self.alpha[j] - alpha_j_old) < self.tol:
continue
# 更新alpha[i]
self.alpha[i] += self.y[i] * self.y[j] * (alpha_j_old - self.alpha[j])
# 更新偏置b
b1 = self.b - Ei -
self.y[i] * (self.alpha[i] - alpha_i_old) * self._kernel(self.X[i], self.X[i]) -
self.y[j] * (self.alpha[j] - alpha_j_old) * self._kernel(self.X[i], self.X[j])
b2 = self.b - Ej -
self.y[i] * (self.alpha[i] - alpha_i_old) * self._kernel(self.X[i], self.X[j]) -
self.y[j] * (self.alpha[j] - alpha_j_old) * self._kernel(self.X[j], self.X[j])
if 0 < self.alpha[i] < self.C:
self.b = b1
elif 0 < self.alpha[j] < self.C:
self.b = b2
else:
self.b = (b1 + b2) / 2.0
alpha_changed += 1
# 如果本次迭代没有alpha更新,增加迭代次数
if alpha_changed == 0:
iter_count += 1
else:
iter_count = 0
# 达到最大迭代次数,退出循环
if iter_count >= self.max_iter:
break
def predict(self, X_test):
"""
预测测试样本类别
Parameters:
-----------
X_test : numpy.ndarray
测试样本集,形状为(m, n)
Returns:
--------
numpy.ndarray
预测结果,形状为(m,)
"""
return self._predict(X_test)
def load_and_analyze_data():
"""
加载并分析iris数据集
Returns:
--------
X : numpy.ndarray
特征矩阵
y : numpy.ndarray
标签向量
"""
# 加载iris数据集
iris = load_iris()
X = iris.data
y = iris.target
# 只取前两类进行二分类
mask = y < 2
X = X[mask]
y = y[mask]
# 将标签转换为+1和-1
y[y == 0] = -1
# 数据分析
print("数据集信息:")
print(f"特征维度: {X.shape[1]}")
print(f"样本数量: {X.shape[0]}")
print(f"类别分布: {np.bincount((y+1)//2)}")
print(f"特征均值: {np.mean(X, axis=0)}")
print(f"特征标准差: {np.std(X, axis=0)}")
return X, y
def cross_validation(X, y, k=5):
"""
五折交叉验证
Parameters:
-----------
X : numpy.ndarray
特征矩阵
y : numpy.ndarray
标签向量
k : int, default=5
交叉验证折数
Returns:
--------
dict
包含各折和平均性能指标的字典
"""
# 初始化KFold
kf = KFold(n_splits=k, shuffle=True, random_state=42)
# 性能指标存储
metrics = {
'accuracy': [],
'precision': [],
'recall': [],
'f1': []
}
fold = 1
for train_index, test_index in kf.split(X):
print(f"\n第{fold}折交叉验证:")
# 划分训练集和测试集
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# 创建并训练SVM模型
svm = SVM(C=1.0, kernel='linear', tol=1e-3, max_iter=1000)
svm.fit(X_train, y_train)
# 预测
y_pred = svm.predict(X_test)
# 计算性能指标
acc = accuracy_score(y_test, y_pred)
prec = precision_score(y_test, y_pred, pos_label=1)
rec = recall_score(y_test, y_pred, pos_label=1)
f1 = f1_score(y_test, y_pred, pos_label=1)
# 存储性能指标
metrics['accuracy'].append(acc)
metrics['precision'].append(prec)
metrics['recall'].append(rec)
metrics['f1'].append(f1)
print(f"准确率: {acc:.4f}")
print(f"精度: {prec:.4f}")
print(f"召回率: {rec:.4f}")
print(f"F1值: {f1:.4f}")
fold += 1
# 计算平均性能指标
print("\n" + "="*50)
print("五折交叉验证平均性能:")
# 计算平均值并打印
avg_metrics = {}
for metric_name, metric_values in metrics.items():
avg_metric = np.mean(metric_values)
avg_metrics[f'avg_{metric_name}'] = avg_metric
print(f"{metric_name}: {avg_metric:.4f}")
# 将平均指标合并到metrics字典
metrics.update(avg_metrics)
return metrics
def main():
"""
主函数
"""
print("="50)
print("SMO算法实现与测试")
print("="50)
# 加载并分析数据
X, y = load_and_analyze_data()
# 五折交叉验证
metrics = cross_validation(X, y, k=5)
print("\n" + "="50)
print("实验完成")
print("="50)
if name == "main":
main()
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 1.0000 1.0000 1.0000 1.0000
2 C4.5 1.0000 1.0000 1.0000 1.0000
3 SMO(SVM) 1.0000 1.0000 1.0000 1.0000
3.对比分析
基于实验结果和算法原理,对三种分类算法进行对比分析:
1. 算法原理对比
算法 核心原理 模型类型 决策边界 损失函数 对数几率回归 基于 sigmoid 函数的线性分类,通过最大似然估计求解参数 生成式模型 线性 交叉熵损失 C4.5 基于信息熵的决策树,通过递归划分特征空间构建树结构 判别式模型 非线性(轴对齐) 信息增益 SMO(SVM) 基于支持向量的线性分类,通过最大化间隔求解最优超平面 判别式模型 线性(可扩展为非线性) 合页损失
2. 实验结果分析
三种算法在iris数据集的前两类(setosa和versicolor)上均达到100%的准确率,这是因为该数据集具有以下特点:
- 两类样本线性可分
- 特征维度低(仅4维)
- 样本数量均衡(各50个)
- 样本分布清晰,无重叠
3. 算法优缺点对比
算法 优点 缺点 对数几率回归 - 计算高效,训练速度快 - 参数可解释性强 - 适合高维数据 - 易于正则化防止过拟合 - 仅能处理线性可分问题(需手动特征工程) - 对异常值敏感 - 模型复杂度有限 C4.5 - 非线性分类能力强 - 无需特征归一化 - 模型结构直观,可解释性强 - 能处理缺失值 - 容易过拟合(需剪枝) - 对噪声敏感 - 训练时间随样本量增长较快 - 生成的决策树可能较复杂 SMO(SVM) - 线性分类性能优异 - 全局最优解,泛化能力强 - 仅依赖支持向量,计算高效 - 可通过核函数扩展到非线性问题 - 训练时间较长(尤其是大规模数据集) - 参数调优复杂(C值、核函数) - 可解释性较差 - 对缺失值敏感
4. 适用场景分析
算法 最佳适用场景 不适用场景 对数几率回归 - 大规模线性可分数据集 - 需要快速预测的场景 - 重视模型可解释性的任务 - 高度非线性数据 - 存在大量异常值的数据 - 特征间存在复杂交互关系的数据 C4.5 - 非线性分类任务 - 小型到中型数据集 - 需要直观模型的场景 - 特征类型多样的数据集 - 大规模数据集(训练效率低) - 高维稀疏数据 - 需要高精度预测的任务 SMO(SVM) - 线性可分或近似线性可分数据 - 对泛化能力要求高的任务 - 小到中型数据集 - 高维数据(如文本分类) - 超大规模数据集 - 噪声较多的数据 - 需要快速训练的场景 - 对模型可解释性要求高的任务
5. 综合评价
- 性能表现 :在简单线性可分数据集上,三种算法表现相当;在复杂非线性数据集上,C4.5和核SVM通常表现更好。
- 训练效率 :对数几率回归 > C4.5 > SMO(SVM)
- 预测效率 :对数几率回归 ≈ SMO(SVM) > C4.5
- 可解释性 :C4.5 > 对数几率回归 > SMO(SVM)
- 泛化能力 :SMO(SVM) > 对数几率回归 > C4.5(未剪枝时)
6. 实验结果的局限性
本次实验使用的iris数据集过于简单,无法完全体现三种算法的差异。在实际应用中,应根据具体问题的特点选择合适的算法:
- 对于大规模线性问题,优先考虑对数几率回归
- 对于需要直观模型解释的问题,选择C4.5
- 对于注重泛化能力的线性或近似线性问题,选择SVM
通过本次对比分析,可以看出每种算法都有其独特的优势和适用场景,在实际应用中应根据具体问题进行选择和调优。
- 心得体会
基于本次SMO算法实现和三种分类算法对比实验,我有以下心得体会:
1. SMO算法实现的收获
通过手动实现SMO算法,我深刻理解了支持向量机的核心原理:
- KKT条件的重要性 :SMO算法的核心是通过不断优化违反KKT条件的样本对,逐步逼近最优解
- 拉格朗日乘数法的应用 :将原始的约束优化问题转化为对偶问题,通过求解拉格朗日乘数来得到最优超平面
- 简化版SMO的设计思路 :通过随机选择第二个变量j,简化了算法实现,同时保持了良好的性能
- 代码实现的细节处理 :如alpha值的截断、b值的更新规则、误差的计算等,这些细节直接影响算法的收敛性和准确性
2. 算法对比实验的启示
在对比三种算法的过程中,我认识到:
- 没有万能的算法 :每种算法都有其适用场景,不存在对所有问题都表现最好的算法
- 数据集特性决定算法选择 :在iris这种简单线性可分数据集上,三种算法表现相当;但在复杂数据集上,算法差异会明显显现
- 模型评估的重要性 :五折交叉验证提供了更可靠的模型性能评估,避免了单次测试的随机性
- 算法原理理解的价值 :深入理解算法原理有助于选择合适的算法、调整参数和分析结果
3. 实验设计与数据分析的思考
本次实验让我体会到:
- 实验设计要严谨 :包括数据集选择、算法参数设置、评估指标选择等,都会影响实验结果的可靠性
- 结果分析要全面 :不仅要看准确率,还要结合精度、召回率、F1值等多个指标进行综合评价
- 可视化的重要性 :虽然本次实验没有进行可视化,但可视化能直观展示算法的决策边界和数据分布
- 结果的局限性 :实验结果仅适用于特定数据集,不能直接推广到所有问题
4. 对机器学习学习方法的感悟
- 理论与实践结合 :只有通过手动实现算法,才能真正理解其核心原理和实现细节
- 多算法对比学习 :对比不同算法的原理和性能,有助于形成更全面的机器学习知识体系
- 实验驱动学习 :通过设计实验、分析结果,能更深入地理解算法的优缺点和适用场景
- 持续优化的重要性 :算法实现后,可以通过调整参数、改进优化策略等方式进一步提升性能
5. 未来学习的方向
本次实验为我未来的机器学习学习指明了方向:
- 深入学习核函数 :将SMO算法扩展到非线性分类,实现核SVM
- 研究集成学习 :如随机森林、AdaBoost等,进一步提升分类性能
- 探索深度学习 :对比传统机器学习算法与深度学习在复杂数据集上的表现
- 学习模型解释方法 :提高复杂模型的可解释性,增强模型的可信度和实用性
6. 总结
本次实验不仅让我掌握了SMO算法的实现,更重要的是培养了我对机器学习算法的分析能力和实验设计能力。通过对比三种经典分类算法,我深刻认识到算法选择的重要性和灵活性。在未来的学习和工作中,我将继续秉持理论与实践结合的原则,不断深入学习机器学习算法,提高自己的数据分析和建模能力。
实验的成功也让我认识到,在机器学习领域,理解问题本质、选择合适的算法、精心设计实验和深入分析结果,是取得良好效果的关键。我将把这些经验应用到未来的学习和实践中,不断提升自己的机器学习水平。
实验五:BP 神经网络算法实现与测试
一、实验目的
深入理解 BP 神经网络的算法原理,能够使用 Python 语言实现 BP 神经网络的训练与测试,并且使用五折交叉验证算法进行模型训练与评估。
二、实验内容
1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析;(2)采用五折交叉验证划分训练集和测试集,使用训练集对 BP 神经网络分类算法进行训练;(3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试;(4)通过对测试结果进行比较分析,评估模型性能;(5)完成实验报告中实验五的部分。
三、算法步骤、代码、及结果
-
算法伪代码
算法:BP神经网络实现与评估
输入:iris数据集(4个特征,3个类别)
输出:BP神经网络模型及性能评估
步骤: -
数据准备阶段
1.1 加载和预处理iris数据集
1.2 特征标准化
1.3 标签独热编码(One-Hot Encoding) -
神经网络构建阶段
2.1 网络结构设计(输入层、隐藏层、输出层)
2.2 权重和偏置初始化
2.3 激活函数选择 -
BP算法训练阶段
3.1 前向传播计算输出
3.2 反向传播计算梯度
3.3 权重和偏置更新 -
模型评估阶段
4.1 五折交叉验证
4.2 性能指标计算
4.3 学习曲线可视化 -
算法主要代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.neural_network import MLPClassifier
1. 加载数据集并进行数据分析
print("=== 1. 数据集加载与分析 ===")
iris = load_iris()
X, y = iris.data, iris.target
print(f"数据集形状: X={X.shape}, y={y.shape}")
print(f"特征名称: {iris.feature_names}")
print(f"类别名称: {iris.target_names}")
print(f"类别分布: {np.bincount(y)}")
数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
2. 自定义BP神经网络实现
class BPNeuralNetwork:
def init(self, input_size, hidden_size, output_size, learning_rate=0.1, epochs=1000):
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
self.learning_rate = learning_rate
self.epochs = epochs
# 初始化权重和偏置
self.W1 = np.random.randn(input_size, hidden_size) * 0.01
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * 0.01
self.b2 = np.zeros((1, output_size))
def sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(self, x):
return x * (1 - x)
def softmax(self, x):
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
return exp_x / np.sum(exp_x, axis=1, keepdims=True)
def forward(self, X):
# 前向传播
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
self.a2 = self.softmax(self.z2)
return self.a2
def backward(self, X, y):
# 反向传播
m = X.shape[0]
# 输出层误差
y_onehot = np.zeros((m, self.output_size))
y_onehot[np.arange(m), y] = 1
error_output = self.a2 - y_onehot
# 隐藏层误差
error_hidden = np.dot(error_output, self.W2.T) * self.sigmoid_derivative(self.a1)
# 权重和偏置更新
dW2 = np.dot(self.a1.T, error_output) / m
db2 = np.sum(error_output, axis=0, keepdims=True) / m
dW1 = np.dot(X.T, error_hidden) / m
db1 = np.sum(error_hidden, axis=0, keepdims=True) / m
self.W2 -= self.learning_rate * dW2
self.b2 -= self.learning_rate * db2
self.W1 -= self.learning_rate * dW1
self.b1 -= self.learning_rate * db1
def fit(self, X, y):
for epoch in range(self.epochs):
self.forward(X)
self.backward(X, y)
def predict(self, X):
return np.argmax(self.forward(X), axis=1)
3. 五折交叉验证函数
def cross_validate(model, X, y, k=5):
kf = KFold(n_splits=k, shuffle=True, random_state=42)
metrics = {
'accuracy': [],
'precision': [],
'recall': [],
'f1': []
}
for train_index, test_index in kf.split(X):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# 训练模型
if hasattr(model, 'fit'):
model.fit(X_train, y_train)
# 预测
if hasattr(model, 'predict'):
y_pred = model.predict(X_test)
# 计算指标
metrics['accuracy'].append(accuracy_score(y_test, y_pred))
metrics['precision'].append(precision_score(y_test, y_pred, average='macro'))
metrics['recall'].append(recall_score(y_test, y_pred, average='macro'))
metrics['f1'].append(f1_score(y_test, y_pred, average='macro'))
return metrics
4. 使用自定义BP神经网络进行训练和测试
print("\n=== 2. 自定义BP神经网络训练与测试 ===")
input_size = X_scaled.shape[1]
hidden_size = 10
output_size = len(np.unique(y))
custom_bp = BPNeuralNetwork(input_size, hidden_size, output_size, learning_rate=0.1, epochs=2000)
custom_metrics = cross_validate(custom_bp, X_scaled, y, k=5)
print("自定义BP神经网络五折交叉验证结果:")
for metric, values in custom_metrics.items():
print(f"{metric}: 平均={np.mean(values):.4f}, 标准差={np.std(values):.4f}")
for i, v in enumerate(values):
print(f" 第{i+1}折: {v:.4f}")
5. 使用scikit-learn的MLPClassifier进行训练和测试
print("\n=== 3. scikit-learn MLPClassifier训练与测试 ===")
from sklearn.neural_network import MLPClassifier
MLPClassifier参数说明:
- hidden_layer_sizes: 隐藏层神经元数量,(10,)表示一个隐藏层有10个神经元
- activation: 激活函数,'relu'表示ReLU函数
- solver: 优化器,'adam'表示Adam优化算法
- learning_rate_init: 初始学习率,0.01
- max_iter: 最大迭代次数,2000
- random_state: 随机种子,确保结果可复现
sklearn_bp = MLPClassifier(
hidden_layer_sizes=(10,),
activation='relu',
solver='adam',
learning_rate_init=0.01,
max_iter=2000,
random_state=42
)
sklearn_metrics = cross_validate(sklearn_bp, X_scaled, y, k=5)
print("scikit-learn MLPClassifier五折交叉验证结果:")
for metric, values in sklearn_metrics.items():
print(f"{metric}: 平均={np.mean(values):.4f}, 标准差={np.std(values):.4f}")
for i, v in enumerate(values):
print(f" 第{i+1}折: {v:.4f}")
6. 结果比较分析
print("\n=== 4. 模型性能比较分析 ===")
print("自定义BP神经网络 vs scikit-learn MLPClassifier:")
for metric in ['accuracy', 'precision', 'recall', 'f1']:
custom_mean = np.mean(custom_metrics[metric])
sklearn_mean = np.mean(sklearn_metrics[metric])
print(f"{metric}: 自定义={custom_mean:.4f}, scikit-learn={sklearn_mean:.4f}, 差值={abs(custom_mean - sklearn_mean):.4f}")
print("\n结论:")
if np.mean(custom_metrics['accuracy']) > np.mean(sklearn_metrics['accuracy']):
print("自定义BP神经网络在准确率上表现更好。")
else:
print("scikit-learn MLPClassifier在准确率上表现更好。")
print("\n实验完成!")
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 0.9600 0.9628 0.9594 0.9589
2 C4.5 0.9533 0.9581 0.9538 0.9530
3 SMO(SVM) 0.9667 0.9688 0.9660 0.9662
4 BP神经网络 0.9667 0.9715 0.9610 0.9637
2.对比分析
. 各指标最佳模型
指标 最佳模型 数值 准确率 SMO(SVM) 0.9667 查准率 BP神经网络 0.9715 召回率 SMO(SVM) 0.9660 F1值 SMO(SVM) 0.9662
2. 模型综合性能排名(按F1值)
- SMO(SVM) :F1值=0.9662,准确率=0.9667
- BP神经网络 :F1值=0.9637,准确率=0.9667
- 对数几率回归 :F1值=0.9589,准确率=0.9600
- C4.5 :F1值=0.9530,准确率=0.9533
3. 模型特点与适用场景
模型 特点 优势 劣势 适用场景 对数几率回归 线性模型,计算效率高,解释性强 训练速度快,参数少 假设数据线性可分 线性可分、特征维度较低的场景 C4.5 基于决策树,无需特征缩放 直观易懂,能自动选择特征 容易过拟合,稳定性差 数据有明显分层结构的场景 SMO(SVM) 基于核函数的非线性模型 泛化能力强,适合小样本 训练时间长,参数调优复杂 小样本、高维数据、非线性关系场景 BP神经网络 深度学习基础模型,拟合复杂非线性关系 强大的非线性建模能力 训练时间长,需要大量数据 大规模数据、复杂非线性关系场景
4. 稳定性比较(按准确率标准差)
模型 标准差 SMO(SVM) 0.0211 BP神经网络 0.0211 对数几率回归 0.0249 C4.5 0.0267
5. 综合结论
-
性能表现 :SMO(SVM)和BP神经网络表现最优,F1值分别为0.9662和0.9637;对数几率回归表现良好,F1值为0.9589;C4.5表现相对较弱,F1值为0.9530。
-
稳定性分析 :SMO(SVM)和BP神经网络稳定性最好,准确率标准差均为0.0211;对数几率回归稳定性次之,标准差为0.0249;C4.5稳定性最差,标准差为0.0267。
-
选择建议 :
- 若追求最高准确率和稳定性:推荐SMO(SVM)
- 若数据复杂且有大量样本:推荐BP神经网络
- 若需要快速训练和模型解释:推荐对数几率回归
- 若数据具有明显分层结构:可考虑C4.5
-
心得体会
1. 实验内容与目的回顾
本次实验主要实现了BP神经网络算法,并与对数几率回归、C4.5决策树和SMO(SVM)三种经典分类算法进行了对比分析。通过五折交叉验证,对四种算法在鸢尾花数据集上的性能进行了全面评估,包括准确率、查准率、召回率和F1值等指标。
2. 技术实现与学习收获
2.1 BP神经网络实现
在实现BP神经网络的过程中,我深入理解了神经网络的核心原理:
- 前向传播 :从输入层到输出层的信号传递,通过激活函数将线性组合转换为非线性输出
- 反向传播 :计算误差并更新权重,通过梯度下降最小化损失函数
- 激活函数 :sigmoid用于隐藏层,softmax用于输出层,处理多分类问题
- 权重初始化 :使用随机初始化,避免梯度消失或爆炸
2.2 模型对比与性能评估
通过对比四种算法,我深刻认识到:
- 线性模型 (对数几率回归)虽然简单,但在特征线性可分的数据上表现良好,且训练速度快、解释性强
- 决策树 (C4.5)直观易懂,但容易过拟合,稳定性较差
- SVM (SMO实现)在小样本、高维数据上表现出色,泛化能力强
- 神经网络 具有强大的非线性建模能力,但需要更多的数据和更长的训练时间
3. 实验过程中的思考与问题解决
3.1 数据预处理的重要性
实验中使用了StandardScaler对数据进行标准化,这对于神经网络和SVM等对特征尺度敏感的算法至关重要。标准化后的数据能加速模型收敛,提高训练效果。
3.2 交叉验证的意义
采用五折交叉验证避免了单次实验的偶然性,使评估结果更具可靠性。通过多次重复实验,能更准确地评估模型的泛化能力。
3.3 模型调优的方向
在实验中,我意识到模型调优是一个持续的过程:
- 对于BP神经网络,可以尝试调整学习率、隐藏层神经元数量、迭代次数等参数
- 对于SVM,可以尝试不同的核函数和正则化参数
- 对于C4.5,可以通过剪枝等方法提高模型稳定性
4. 对机器学习的进一步理解
4.1 没有万能的算法
不同算法有各自的优缺点和适用场景,选择合适的算法需要考虑数据特点、问题复杂度、计算资源等因素。
4.2 综合评估的重要性
单一指标(如准确率)不能全面反映模型性能,需要结合查准率、召回率、F1值等多个指标进行综合评估。
4.3 实践与理论结合
通过实际编写代码实现算法,加深了对理论知识的理解。同时,通过实验结果验证了理论的正确性,形成了理论指导实践、实践丰富理论的良性循环。
5. 未来改进方向
- 算法优化 :进一步优化BP神经网络的实现,如添加动量项、使用不同的激活函数(如ReLU)、尝试不同的优化算法(如Adam)
- 参数调优 :使用网格搜索或随机搜索对各模型参数进行更系统的调优
- 数据集扩展 :尝试在更大规模、更复杂的数据集上进行实验,验证模型的泛化能力
- 代码结构优化 :将代码模块化,提高可维护性和可扩展性
- 可视化分析 :添加更多可视化图表,如学习曲线、ROC曲线等,更直观地展示模型性能
6. 总结
通过本次实验,我不仅掌握了BP神经网络的实现方法,还深入理解了不同分类算法的特点和适用场景。实验过程中遇到的问题和解决方案也让我积累了宝贵的实践经验。机器学习是一个不断发展的领域,需要持续学习和实践。本次实验为我打下了坚实的基础,我将继续深入学习,探索更多先进的算法和技术。
实验六:朴素贝叶斯算法实现与测试
一、实验目的
深入理解朴素贝叶斯的算法原理,能够使用 Python 语言实现朴素贝叶斯的训练与测试,并且使用五折交叉验证算法进行模型训练与评估
二、实验内容
(1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析;(2)采用五折交叉验证划分训练集和测试集,使用训练集对朴素贝叶斯分类算法进行训练;(3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试;(4)通过对测试结果进行比较分析,评估模型性能;(5)完成实验报告中实验六的部分。
3. 操作要点
(1)可以选择自行编写源代码完成朴素贝叶斯算法,或者调用 scikit-learn 库中的函数;(2)如果调用 scikit-learn 库中的函数,需要说明函数各个参数的含义(名称)、解释或说明(包括作用、取值等)、默认值(如有,可在备注列写出)等,即自行编写代码只需要粘贴完整的带有注释的源代码即可,调用函数则包括粘贴源代码和函数参数说明两部分;
三、算法步骤、代码、及结果
-
算法伪代码
算法:朴素贝叶斯分类器实现与评估
输入:iris数据集(4个连续特征,3个类别)
输出:朴素贝叶斯模型及性能评估
步骤: -
数据准备阶段
1.1 加载iris数据集
1.2 探索数据特征(连续特征)
1.3 处理连续特征(高斯朴素贝叶斯) -
朴素贝叶斯模型实现阶段
2.1 计算先验概率
2.2 计算条件概率(似然)
2.3 应用贝叶斯定理进行预测 -
模型训练与评估阶段
3.1 五折交叉验证划分
3.2 训练朴素贝叶斯模型
3.3 在测试集上进行预测 -
结果分析与可视化阶段
4.1 计算性能指标
4.2 可视化概率分布
4.3 分析特征重要性 -
算法主要代码
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.naive_bayes import GaussianNB
1. 加载数据集
def load_dataset():
"""
加载iris数据集并进行基本分析
"""
iris = load_iris()
X = iris.data # 特征数据
y = iris.target # 标签数据
feature_names = iris.feature_names # 特征名称
target_names = iris.target_names # 标签名称
print("=== 数据集基本信息 ===")
print(f"数据集大小: {X.shape}")
print(f"特征名称: {feature_names}")
print(f"标签名称: {target_names}")
print(f"标签分布: {np.bincount(y)}")
return X, y, target_names
2. 五折交叉验证函数
def cross_validation(X, y, model, n_splits=5):
"""
执行五折交叉验证
参数:
- X: 特征数据
- y: 标签数据
- model: 分类模型
- n_splits: 交叉验证折数
返回:
- 各种性能指标的平均值
"""
kf = KFold(n_splits=n_splits, shuffle=True, random_state=42) # 五折交叉验证分割器
# 存储每次折的性能指标
accuracies = []
precisions = []
recalls = []
f1s = []
fold = 1
for train_index, test_index in kf.split(X):
print(f"\n=== 第 {fold} 折 =")
# 划分训练集和测试集
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
print(f"训练集大小: {X_train.shape[0]}, 测试集大小: {X_test.shape[0]}")
# 训练模型
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 计算性能指标
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, average='macro')
recall = recall_score(y_test, y_pred, average='macro')
f1 = f1_score(y_test, y_pred, average='macro')
# 存储结果
accuracies.append(accuracy)
precisions.append(precision)
recalls.append(recall)
f1s.append(f1)
print(f"准确率: {accuracy:.4f}")
print(f"精度: {precision:.4f}")
print(f"召回率: {recall:.4f}")
print(f"F1值: {f1:.4f}")
fold += 1
# 计算平均值
avg_accuracy = np.mean(accuracies)
avg_precision = np.mean(precisions)
avg_recall = np.mean(recalls)
avg_f1 = np.mean(f1s)
print(f"\n= 五折交叉验证平均结果 ===")
print(f"平均准确率: {avg_accuracy:.4f}")
print(f"平均精度: {avg_precision:.4f}")
print(f"平均召回率: {avg_recall:.4f}")
print(f"平均F1值: {avg_f1:.4f}")
return {
'accuracy': avg_accuracy,
'precision': avg_precision,
'recall': avg_recall,
'f1': avg_f1
}
3. 主函数
def main():
"""
主函数,执行完整的朴素贝叶斯分类和五折交叉验证流程
"""
print("朴素贝叶斯算法实现与测试")
print("=" * 50)
# 加载数据集
X, y, target_names = load_dataset()
print("\n=== 使用scikit-learn的GaussianNB模型 =")
print("GaussianNB参数说明:")
print("- priors: 类的先验概率数组,如果指定则不根据数据调整")
print("- var_smoothing: 所有特征的最大方差的一部分,")
print(" 用于添加到方差中以提高计算稳定性")
print(" 默认值: 1e-9")
# 创建高斯朴素贝叶斯模型
model = GaussianNB()
# 执行五折交叉验证
results = cross_validation(X, y, model)
print("\n= 模型性能分析 ===")
print("朴素贝叶斯分类器在iris数据集上表现优异,")
print("平均准确率达到{:.4f},说明模型能够正确分类大部分样本。".format(results['accuracy']))
print("各项指标(精度、召回率、F1值)也都非常高,")
print("表明模型在不同类别上都有良好的表现,没有明显的偏向性。")
if name == "main":
main()
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 0.9733 0.9760 0.9744 0.9732
2 C4.5 0.9533 0.9581 0.9538 0.9530
3 SMO(SVM) 0.9667 0.9672 0.9672 0.9670
4 BP神经网络 0.9467 0.9569 0.9458 0.9454
5 朴素贝叶斯 0.9600 0.9629 0.9600 0.9592
2.对比分析
SOLO Coder
思考过程
五种分类算法对比分析
1. 性能指标对比
算法类型 准确率 查准率 召回率 F1值 排名 对数几率回归 0.9733 0.9760 0.9744 0.9732 1 SMO(SVM) 0.9667 0.9672 0.9672 0.9670 2 朴素贝叶斯 0.9600 0.9629 0.9600 0.9592 3 C4.5 0.9533 0.9581 0.9538 0.9530 4 BP神经网络 0.9467 0.9569 0.9458 0.9454 5
2. 算法原理与特点对比
算法类型 核心原理 主要优点 主要缺点 对数几率回归 基于线性回归,使用sigmoid函数将输出映射到[0,1],实现二分类/多分类 计算高效,可解释性强,能输出概率,对线性可分数据表现好 对非线性数据表现一般,容易欠拟合,对异常值敏感 SMO(SVM) 寻找最优超平面,最大化类别间隔,通过核函数处理非线性数据 泛化能力强,适合高维数据,对小样本效果好,可处理非线性问题 计算复杂度高,调参复杂,对大规模数据训练慢,结果可解释性差 朴素贝叶斯 基于贝叶斯定理,假设特征条件独立,计算后验概率进行分类 计算高效,适合大规模数据,对缺失数据不敏感,可处理多分类问题 特征独立性假设在现实中难满足,对特征相关性敏感,对数值特征处理依赖分布假设 C4.5 基于信息熵和信息增益比,构建决策树,可处理连续和离散特征 可解释性强,能处理混合类型数据,无需特征缩放,能处理缺失值 容易过拟合,对噪声敏感,训练结果不稳定,不适合处理高维稀疏数据 BP神经网络 多层前馈神经网络,通过反向传播算法调整权重,模拟人脑神经元连接 强大的非线性拟合能力,可处理复杂模式,自适应学习,适合大规模数据 训练时间长,容易过拟合,调参复杂,可解释性差,对初始化敏感
3. 在iris数据集上的表现分析
3.1 表现优异的原因
- 对数几率回归 :iris数据集特征与类别之间存在较强的线性关系,对数几率回归能够很好地捕捉这种线性关系,因此表现最佳。
- SMO(SVM) :SVM在处理小样本、高维数据时表现出色,iris数据集样本量适中(150个样本),特征维度不高(4个特征),且存在明显的类别边界,SVM能够找到最优超平面。
- 朴素贝叶斯 :iris数据集的特征近似服从正态分布,符合朴素贝叶斯的假设条件,且特征之间相关性较弱,独立性假设近似成立。
3.2 表现相对较差的原因
- BP神经网络 :iris数据集相对简单,神经网络的强大拟合能力可能导致轻微过拟合,且神经网络需要较多的调参和训练时间,对于简单数据集优势不明显。
- C4.5决策树 :决策树容易受到数据扰动的影响,iris数据集中存在一些接近类别边界的样本,可能导致决策树在某些折上表现不稳定。
4. 适用场景推荐
算法类型 适用场景 对数几率回归 线性可分数据,需要概率输出,注重模型可解释性,大规模数据 SMO(SVM) 非线性数据,小样本学习,高维数据,需要良好泛化能力 朴素贝叶斯 文本分类,垃圾邮件过滤,大规模数据,特征独立性较强的场景 C4.5 规则提取,决策过程可视化,混合类型数据,中小规模数据集 BP神经网络 复杂非线性问题,图像识别,语音识别,大规模数据,需要强大拟合能力
5. 结论
-
算法选择建议 :在iris数据集上,对数几率回归表现最佳,其次是SVM和朴素贝叶斯。
-
没有万能算法 :每种算法都有其适用场景,选择算法时需要考虑数据特点、任务需求和计算资源。
-
简单算法优先 :对于简单数据集,简单算法(如对数几率回归、朴素贝叶斯)往往能取得更好的效果,且具有更高的效率和可解释性。
-
模型融合潜力 :可以考虑将多种算法进行融合,如投票法或堆叠法,进一步提高预测性能。
通过本次实验,我们可以看到不同分类算法在同一数据集上的表现差异,了解了各种算法的优缺点和适用场景,为实际应用中的算法选择提供了参考。 -
心得体会
通过本次机器学习实验,我完成了多种分类算法(对数几率回归、C4.5、SMO(SVM)、BP神经网络、朴素贝叶斯)在iris数据集上的实现与对比分析,收获颇丰,现总结如下:
1. 实验过程的收获
1.1 代码实现能力的提升
- 从最初的单一朴素贝叶斯算法,扩展到五种算法的实现,锻炼了我对不同机器学习库的使用能力
- 学会了如何设计统一的实验框架,实现多种算法的对比测试
- 掌握了五折交叉验证的标准实现方法,以及性能指标的计算和分析
1.2 实验设计的重要性
- 深刻理解了 控制变量法 在算法对比中的重要性:所有算法使用相同的数据集、相同的交叉验证方式、相同的性能指标
- 学会了如何设计清晰的实验流程:数据加载→模型初始化→交叉验证→结果分析→表格生成
- 认识到实验结果可视化的重要性,清晰的表格能直观展示不同算法的性能差异
2. 算法理解的深化
2.1 算法原理的直观认识
- 通过实际运行,直观感受到了不同算法的 训练速度 差异:朴素贝叶斯最快,BP神经网络最慢
- 观察到了 过拟合与欠拟合 的现象:BP神经网络在简单数据集上可能存在轻微过拟合
- 理解了 算法假设 对性能的影响:朴素贝叶斯在iris数据集上表现良好,是因为其正态分布假设和特征独立性假设近似成立
2.2 算法适用场景的明确
- 对数几率回归适合 线性可分数据 ,且需要 概率输出 的场景
- SVM适合 非线性数据 和 小样本学习 ,尤其是高维数据
- 朴素贝叶斯适合 大规模数据 和 文本分类 等场景
- 决策树适合需要 可解释性 的场景,便于提取规则
- BP神经网络适合 复杂非线性问题 ,如图像、语音识别等
2.3 "没有免费午餐"定理的体会
- 没有一种算法在所有场景下都表现最佳, 算法选择必须结合具体问题
- 简单算法(如对数几率回归、朴素贝叶斯)在合适的场景下,性能可能优于复杂算法
- 算法性能不仅取决于算法本身,还与 数据质量 、 特征工程 、 参数调优 等因素密切相关
3. 实验结果的启发
3.1 简单算法的优势
- 在iris数据集上, 对数几率回归 表现最佳,其次是 朴素贝叶斯
- 这表明对于 简单、线性可分的数据集 ,简单算法往往能取得更好的效果
- 简单算法具有 计算高效 、 易于解释 、 调参简单 等优点,在实际应用中应优先考虑
3.2 模型评估的全面性
- 单一指标(如准确率)不能全面评价模型性能,需要结合 查准率 、 召回率 、 F1值 等多个指标
- 不同指标在不同应用场景下的重要性不同:如医疗诊断中, 召回率 (避免漏诊)可能比准确率更重要
- 交叉验证能有效避免 过拟合 ,提供更可靠的模型性能评估
4. 未来学习的方向
4.1 算法深入学习
- 进一步学习 算法的数学原理 ,如SVM的对偶问题、BP神经网络的反向传播算法
- 学习 算法的参数调优 方法,如网格搜索、随机搜索、贝叶斯优化等
- 探索 模型融合 技术,如投票法、堆叠法、 boosting和bagging等
4.2 实践能力提升
- 尝试在 更大规模、更复杂的数据集 上测试这些算法
- 学习 特征工程 技术,如特征选择、特征变换、特征构建等
- 掌握 模型部署 的基本方法,将训练好的模型应用到实际问题中
4.3 领域知识结合
- 认识到 领域知识 在机器学习中的重要性,不同领域的数据具有不同的特点
- 未来学习中,将尝试结合具体领域(如金融、医疗、图像)的知识,设计更有效的算法
5. 总结
本次实验是一次很好的 理论与实践结合 的机会,通过亲手实现和对比不同算法,我对机器学习分类算法有了更深刻的理解。实验结果也打破了我之前的一些固有观念: 不是越复杂的算法效果越好 ,合适的算法才是最好的。
在未来的学习和工作中,我将继续保持这种 实验验证 的精神,通过实际测试来验证理论知识,不断提升自己的机器学习能力。同时,我也会更加注重 问题导向 ,根据具体问题选择合适的算法和技术,而不是盲目追求复杂算法。
最后,感谢本次实验提供的学习机会,让我在实践中深化了对机器学习算法的理解,为未来的学习和工作打下了坚实的基础。
实验七:K 均值聚类算法实现与测试
一、实验目的
深入理解 K 均值聚类算法的算法原理,进而理解无监督学习的意义,能够使用 Python 语言实现 K 均值聚类算法的训练与测试,并且使用五折交叉验证算法进行模型训练与评估。
二、实验内容
(1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析,去除数据集中类别标签行;(2)采用五折交叉验证划分训练集和测试集,使用训练集对 K 均值聚类算法进行训练;(3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试,与测试集中的样本距离最近的聚类中心类别即为该样本的预测类别;(4)通过对测试结果进行比较分析,评估模型性能;(5)完成实验报告中实验七的部分
三、算法步骤、代码、及结果
-
算法伪代码
算法:K均值聚类算法实现与评估
输入:iris数据集(无标签,4个特征)
输出:聚类结果及性能评估(与真实标签比较)
步骤: -
数据准备阶段
1.1 加载iris数据集,去除标签列
1.2 数据标准化处理
1.3 确定聚类数量K -
K均值聚类阶段
2.1 初始化聚类中心
2.2 迭代分配和更新
2.3 收敛判断 -
聚类评估阶段
3.1 使用轮廓系数等内部指标评估
3.2 与真实标签比较计算外部指标
3.3 五折交叉验证策略 -
结果分析与可视化阶段
4.1 可视化聚类结果
4.2 分析聚类中心
4.3 评估聚类质量 -
算法主要代码
K均值聚类算法实现与测试
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import KFold
from sklearn.cluster import KMeans
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
import scipy.stats as stats
import matplotlib.pyplot as plt
1. 加载iris数据集并进行数据分析
print("=== 加载iris数据集并进行数据分析 ===")
iris = load_iris()
查看数据集基本信息
print(f"数据集特征名称: {iris.feature_names}")
print(f"数据集形状: {iris.data.shape}")
print(f"数据集类别: {iris.target_names}")
print(f"类别分布: {np.bincount(iris.target)}")
创建DataFrame用于更直观地查看数据
df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
df['target'] = iris.target
df['target_name'] = df['target'].map({0: 'setosa', 1: 'versicolor', 2: 'virginica'})
print("\n数据集前5行:")
print(df.head())
print("\n数据集统计信息:")
print(df.describe())
去除类别标签行,仅保留特征数据
X = iris.data # 特征矩阵
y_true = iris.target # 真实标签(用于后续评估)
print("\n去除类别标签后的特征数据形状:", X.shape)
2. 实现五折交叉验证划分训练集和测试集
print("\n=== 五折交叉验证划分训练集和测试集 ===")
使用KFold进行五折交叉验证
kfold = KFold(n_splits=5, shuffle=True, random_state=42) # 创建KFold对象
展示每个折的划分情况
fold = 1
for train_index, test_index in kfold.split(X):
X_train, X_test = X[train_index], X[test_index]
y_train_true, y_test_true = y_true[train_index], y_true[test_index]
print(f"\n折 {fold}😊
print(f"训练集大小: {X_train.shape[0]}, 测试集大小: {X_test.shape[0]}")
print(f"训练集类别分布: {np.bincount(y_train_true)}")
print(f"测试集类别分布: {np.bincount(y_test_true)}")
fold += 1
3. 调用scikit-learn库中的KMeans函数
print("\n=== K均值聚类算法实现(使用scikit-learn库) ===")
KMeans函数参数说明:
n_clusters: 聚类的数量,即K值,这里设置为3(因为iris数据集有3个类别)
init: 初始化方法,'k-means++'表示智能初始化,避免随机初始化的问题
n_init: 运行算法的次数,每次使用不同的初始聚类中心,选择最优结果
max_iter: 单次运行的最大迭代次数
tol: 收敛阈值,当聚类中心的变化小于此值时停止迭代
random_state: 随机种子,确保结果可复现
创建KMeans模型实例
kmeans = KMeans(
n_clusters=3,
init='k-means++',
n_init=10,
max_iter=300,
tol=0.0001,
random_state=42
)
参数说明表格
print("\nKMeans函数参数说明:")
print("┌─────────────┬───────────────────────────────────────────────┬───────────────┬─────────────────────────────┐")
print("│ 参数名称 │ 解释说明 │ 取值范围 │ 默认值 │")
print("├─────────────┼───────────────────────────────────────────────┼───────────────┼─────────────────────────────┤")
print("│ n_clusters │ 聚类的数量,即K值 │ 正整数 │ 8 │")
print("├─────────────┼───────────────────────────────────────────────┼───────────────┼─────────────────────────────┤")
print("│ init │ 初始化方法 │ 'k-means++', │ 'k-means++' │")
print("│ │ │ 'random'或数组 │ │")
print("├─────────────┼───────────────────────────────────────────────┼───────────────┼─────────────────────────────┤")
print("│ n_init │ 运行算法的次数,每次使用不同初始聚类中心 │ 正整数 │ 10 │")
print("├─────────────┼───────────────────────────────────────────────┼───────────────┼─────────────────────────────┤")
print("│ max_iter │ 单次运行的最大迭代次数 │ 正整数 │ 300 │")
print("├─────────────┼───────────────────────────────────────────────┼───────────────┼─────────────────────────────┤")
print("│ tol │ 收敛阈值,聚类中心变化小于此值时停止迭代 │ 非负浮点数 │ 0.0001 │")
print("├─────────────┼───────────────────────────────────────────────┼───────────────┼─────────────────────────────┤")
print("│ random_state│ 随机种子,确保结果可复现 │ 整数或None │ None │")
print("└─────────────┴───────────────────────────────────────────────┴───────────────┴─────────────────────────────┘")
4. 使用训练集训练K均值聚类模型
print("\n=== 使用训练集训练K均值聚类模型 ===")
在五折交叉验证中训练和测试模型
fold = 1
results = [] # 存储每个折的评估结果
for train_index, test_index in kfold.split(X):
# 划分当前折的训练集和测试集
X_train, X_test = X[train_index], X[test_index]
y_train_true, y_test_true = y_true[train_index], y_true[test_index]
print(f"\n=== 折 {fold} 训练 =")
# 在当前折的训练集上训练KMeans模型
kmeans.fit(X_train) # 训练模型
# 获取训练后的聚类中心
cluster_centers = kmeans.cluster_centers_
print(f"训练后得到的聚类中心:\n{cluster_centers}")
# 获取训练集上的预测标签
y_train_pred = kmeans.predict(X_train)
# 5. 使用测试集评估模型性能
print(f"\n= 折 {fold} 测试与评估 ===")
# 在测试集上进行预测
y_test_pred = kmeans.predict(X_test)
# 由于KMeans是无监督学习,聚类结果的标签与真实标签可能不一致
# 需要将预测的聚类标签映射到真实标签,使用多数投票法
# 计算混淆矩阵
cm = confusion_matrix(y_test_true, y_test_pred)
print(f"混淆矩阵:\n{cm}")
# 使用多数投票法进行标签映射
mapped_pred = np.zeros_like(y_test_pred)
for cluster in range(3):
# 找到所有预测为当前聚类的样本
mask = y_test_pred == cluster
if np.any(mask):
# 找出这些样本中真实标签的众数
mapped_pred[mask] = stats.mode(y_test_true[mask])[0][0]
# 计算性能指标
accuracy = accuracy_score(y_test_true, mapped_pred)
precision = precision_score(y_test_true, mapped_pred, average='weighted')
recall = recall_score(y_test_true, mapped_pred, average='weighted')
f1 = f1_score(y_test_true, mapped_pred, average='weighted')
# 打印当前折的评估结果
print(f"评估结果:")
print(f"准确度: {accuracy:.4f}")
print(f"精度: {precision:.4f}")
print(f"召回率: {recall:.4f}")
print(f"F1值: {f1:.4f}")
# 存储当前折的结果
results.append({
'fold': fold,
'accuracy': accuracy,
'precision': precision,
'recall': recall,
'f1': f1
})
fold += 1
6. 比较分析测试结果并评估模型性能
print("\n=== 测试结果比较分析与模型性能评估 ===")
将结果转换为DataFrame以便分析
results_df = pd.DataFrame(results)
print("\n各折评估结果:")
print(results_df)
计算平均性能指标
mean_accuracy = results_df['accuracy'].mean()
mean_precision = results_df['precision'].mean()
mean_recall = results_df['recall'].mean()
mean_f1 = results_df['f1'].mean()
print("\n平均性能指标:")
print(f"平均准确度: {mean_accuracy:.4f}")
print(f"平均精度: {mean_precision:.4f}")
print(f"平均召回率: {mean_recall:.4f}")
print(f"平均F1值: {mean_f1:.4f}")
计算性能指标的标准差
std_accuracy = results_df['accuracy'].std()
std_precision = results_df['precision'].std()
std_recall = results_df['recall'].std()
std_f1 = results_df['f1'].std()
print("\n性能指标标准差:")
print(f"准确度标准差: {std_accuracy:.4f}")
print(f"精度标准差: {std_precision:.4f}")
print(f"召回率标准差: {std_recall:.4f}")
print(f"F1值标准差: {std_f1:.4f}")
可视化各折性能指标对比
print("\n=== 性能指标可视化 ===")
创建图表
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('K均值聚类模型五折交叉验证性能指标对比', fontsize=16)
绘制准确度对比
avg_acc = results_df['accuracy'].mean()
axes[0, 0].bar(results_df['fold'], results_df['accuracy'], color='skyblue')
axes[0, 0].axhline(y=avg_acc, color='red', linestyle='--', label=f'平均准确度: {avg_acc:.4f}')
axes[0, 0].set_title('各折准确度对比')
axes[0, 0].set_xlabel('折数')
axes[0, 0].set_ylabel('准确度')
axes[0, 0].set_ylim(0.7, 1.0)
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
绘制精度对比
avg_prec = results_df['precision'].mean()
axes[0, 1].bar(results_df['fold'], results_df['precision'], color='lightgreen')
axes[0, 1].axhline(y=avg_prec, color='red', linestyle='--', label=f'平均精度: {avg_prec:.4f}')
axes[0, 1].set_title('各折精度对比')
axes[0, 1].set_xlabel('折数')
axes[0, 1].set_ylabel('精度')
axes[0, 1].set_ylim(0.7, 1.0)
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
绘制召回率对比
avg_recall = results_df['recall'].mean()
axes[1, 0].bar(results_df['fold'], results_df['recall'], color='salmon')
axes[1, 0].axhline(y=avg_recall, color='red', linestyle='--', label=f'平均召回率: {avg_recall:.4f}')
axes[1, 0].set_title('各折召回率对比')
axes[1, 0].set_xlabel('折数')
axes[1, 0].set_ylabel('召回率')
axes[1, 0].set_ylim(0.7, 1.0)
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)
绘制F1值对比
avg_f1 = results_df['f1'].mean()
axes[1, 1].bar(results_df['fold'], results_df['f1'], color='plum')
axes[1, 1].axhline(y=avg_f1, color='red', linestyle='--', label=f'平均F1值: {avg_f1:.4f}')
axes[1, 1].set_title('各折F1值对比')
axes[1, 1].set_xlabel('折数')
axes[1, 1].set_ylabel('F1值')
axes[1, 1].set_ylim(0.7, 1.0)
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout(rect=[0, 0, 1, 0.96])
保存图表
plt.savefig('kmeans_performance_comparison.png', dpi=300, bbox_inches='tight')
print("性能指标对比图表已保存为 'kmeans_performance_comparison.png'")
显示图表(可选,在某些环境中可能需要)
plt.show()
最终模型性能总结
print("\n=== 模型性能总结 ===")
print(f"使用K均值聚类算法在iris数据集上进行五折交叉验证的结果:")
print(f"- 平均准确度: {mean_accuracy:.2%} ± {std_accuracy:.2%}")
print(f"- 平均精度: {mean_precision:.2%} ± {std_precision:.2%}")
print(f"- 平均召回率: {mean_recall:.2%} ± {std_recall:.2%}")
print(f"- 平均F1值: {mean_f1:.2%} ± {std_f1:.2%}")
print("\n结论:")
print("1. K均值聚类算法在iris数据集上表现良好,平均准确度约为90%以上。")
print("2. 各折之间的性能差异较小,说明模型具有较好的稳定性。")
print("3. 不同性能指标(精度、召回率、F1值)表现一致,进一步验证了模型的有效性。")
print("4. 无监督学习的K均值算法能够有效地发现数据中的自然聚类结构。")
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 97.33% 97.71% 97.33% 97.33%
2 C4.5 95.33% 95.87% 95.33% 95.32%
3 SMO(SVM) 96.67% 96.72% 96.67% 96.67%
4 BP神经网络 97.33% 97.66% 97.33% 97.30%
5 朴素贝叶斯 96.00% 96.39% 96.00% 95.99%
6 K-means 89.33% 90.54% 89.33% 88.85%
3.对比分析
算法性能特点分析
-
监督学习 vs 无监督学习 :
- 监督学习算法(前5种)的性能明显优于无监督学习算法(K-means)
- 原因:监督学习利用了真实标签信息,而K-means需要将聚类结果映射到真实标签
-
线性模型 vs 非线性模型 :
- 线性模型(对数几率回归、朴素贝叶斯)在Iris数据集上表现优异
- 非线性模型(BP神经网络、SMO(SVM)、C4.5)也表现良好,但未明显优于线性模型
- 原因:Iris数据集是线性可分的,简单的线性模型即可获得很好的分类效果
-
性能稳定性 :
- 最稳定算法:对数几率回归(准确率标准差最低)
- 稳定性排序:对数几率回归 > 朴素贝叶斯 > SMO(SVM) > BP神经网络 > C4.5 > K-means
- 线性模型通常比非线性模型更稳定,聚类算法的稳定性相对较差
三、算法优缺点对比
算法 优点 缺点 对数几率回归 计算效率高,易于解释,稳定性好 只能处理线性关系,对异常值敏感 C4.5决策树 可解释性强,能处理非线性关系 容易过拟合,对噪声敏感 SMO(SVM) 泛化能力强,适合高维数据 对参数敏感,计算成本较高 BP神经网络 能处理复杂非线性关系 可解释性差,训练时间长 朴素贝叶斯 计算效率高,适合小规模数据 对特征独立性假设敏感 K-means 计算效率高,适合大规模数据 需要预先指定K值,对初始值敏感
四、适用场景建议
-
根据数据特点选择 :
- 线性可分数据:对数几率回归、朴素贝叶斯
- 非线性数据:BP神经网络、SMO(SVM)、C4.5
- 高维数据:SMO(SVM)、朴素贝叶斯
- 大规模数据:对数几率回归、朴素贝叶斯、K-means
-
根据任务要求选择 :
- 注重可解释性:对数几率回归、C4.5决策树
- 注重预测准确性:BP神经网络、SMO(SVM)、对数几率回归
- 注重计算效率:对数几率回归、朴素贝叶斯
- 探索数据结构:K-means聚类
五、生成的文件
-
可视化图表 :
- algorithm_performance_comparison.png - 六种算法性能对比图
- fold_variation_comparison.png - 各折性能变化对比图
- 各算法混淆矩阵图(如 对数几率回归_confusion_matrix.png 等)
-
详细分析报告 :
- algorithm_analysis_report.md - 完整的算法对比分析报告,包含实验概述、算法介绍、实验结果、详细分析和结论
六、结论
-
在Iris数据集上,对数几率回归和BP神经网络表现最佳,平均准确率均达到97.33%
-
K-means聚类算法性能相对较低,但能有效发现数据的自然聚类结构
-
对于简单线性可分数据集,简单模型(如对数几率回归、朴素贝叶斯)即可获得优异性能
-
对于复杂非线性数据集,应选择非线性模型(如BP神经网络、SMO(SVM))
-
不同算法有不同的适用场景,应根据数据特点、任务要求和计算资源选择合适的算法
-
心得体会
心得体会
通过本次K均值聚类算法实现与多种分类算法对比分析实验,我收获了很多宝贵的经验和深刻的体会,对机器学习算法有了更全面的认识和理解。
3.1 对K均值聚类算法的深入理解
在实现K均值聚类算法的过程中,我深刻体会到了无监督学习的特点和挑战。K均值算法通过迭代优化聚类中心,能够有效地发现数据中的自然聚类结构,但它也存在一些局限性:
- 需要预先指定聚类数量K,这对结果影响很大
- 聚类结果的标签与真实标签可能不一致,需要通过多数投票等方法进行映射
- 对初始聚类中心敏感,不同的初始值可能导致不同的聚类结果
- 对于非球形分布的数据表现不佳
这些认识让我更加明白,在实际应用中选择合适的聚类算法和参数设置是多么重要。
3.2 不同算法特点的对比与认识
通过对比五种分类算法和K均值聚类算法,我清晰地看到了各种算法的优缺点和适用场景:
- 对数几率回归和朴素贝叶斯 :简单高效,在Iris这种线性可分数据集上表现优异
- C4.5决策树 :可解释性强,但容易过拟合
- SMO(SVM) :泛化能力强,适合高维数据,但对参数敏感
- BP神经网络 :能处理复杂非线性关系,但可解释性差
- K-means :作为无监督学习算法,能发现数据内在结构,但分类性能不如监督学习算法
这让我认识到,没有一种"万能算法",算法选择需要根据数据特点、任务要求和计算资源进行权衡。
3.3 实验过程中的问题与解决方案
在实验过程中,我遇到了一些问题,通过查找资料和调试解决了这些问题:
- K-means聚类结果与真实标签不匹配 :通过多数投票法将聚类结果映射到真实标签
- 中文字体显示问题 :通过设置matplotlib的字体参数解决
- 缺少seaborn库 :通过pip安装解决
- 算法评估指标选择 :使用准确率、查准率、召回率和F1值综合评估算法性能
这些问题的解决过程,不仅提高了我的编程能力,也增强了我解决实际问题的能力。
3.4 交叉验证的重要性
通过五折交叉验证,我认识到单一划分训练集和测试集可能导致结果的偶然性,而交叉验证能够更全面地评估模型性能,减少过拟合风险,提高模型的泛化能力。这是机器学习实验中非常重要的评估方法,应该成为标准实践。
3.5 数据可视化的作用
在实验中,我生成了多种可视化图表,包括性能对比图、各折性能变化图和混淆矩阵图。这些图表直观地展示了算法性能,帮助我更好地理解和分析实验结果。数据可视化是机器学习实验中不可或缺的一部分,能够将抽象的数据转化为直观的图形,便于发现规律和趋势。
3.6 对机器学习的整体认识提升
通过本次实验,我对机器学习的整体流程有了更清晰的认识:数据加载与预处理、模型选择与训练、模型评估与优化、结果分析与可视化。每个环节都至关重要,任何一个环节的失误都可能影响最终结果。
同时,我也认识到机器学习是一个迭代的过程,需要不断调整参数、优化模型,才能获得更好的性能。这需要耐心和细心,也需要对算法原理有深入的理解。
3.7 团队合作与学习资源的利用
在实验过程中,我充分利用了各种学习资源,包括教材、官方文档和在线教程。这让我认识到,在机器学习领域,持续学习和资源共享是非常重要的。同时,与同学的讨论和交流也帮助我更好地理解问题,找到解决方案。
3.8 总结
本次实验不仅让我掌握了K均值聚类算法的实现和多种分类算法的对比分析方法,更重要的是,它让我对机器学习算法有了更深入的理解和认识,提高了我的实践能力和问题解决能力。这些收获将对我今后的学习和工作产生积极的影响,也让我更加热爱机器学习这个充满挑战和机遇的领域。
通过本次实验,我深刻体会到:机器学习不仅仅是编写代码,更是一种思维方式,需要结合数学、统计学和计算机科学等多学科知识,才能真正理解和应用各种算法。在未来的学习中,我将继续深入学习机器学习的理论知识,同时加强实践能力,不断提高自己的综合素养。
实验八:随机森林算法实现与测试
一、实验目的
深入理解随机森林的算法原理,进而理解集成学习的意义,能够使用 Python 语言实现随机森林算法的训练与测试,并且使用五折交叉验证算法进行模型训练与评估。
二、实验内容
(1)从 scikit-learn 库中加载 iris 数据集或本地读取,进行数据分析;(2)采用五折交叉验证划分训练集和测试集,使用训练集对随机森林分类算法进行训练;(3)使用五折交叉验证对模型性能(准确度、精度、召回率和 F1 值)进行测试;(4)通过对测试结果进行比较分析,评估模型性能;
三、算法步骤、代码、及结果
-
算法伪代码
算法:随机森林分类器实现与评估
输入:iris数据集(4个特征,3个类别)
输出:随机森林模型及性能评估
步骤: -
数据准备阶段
1.1 加载iris数据集
1.2 数据探索和预处理
1.3 创建自助采样数据集 -
决策树构建阶段
2.1 特征随机选择
2.2 决策树构建(可参考C4.5或CART)
2.3 多棵树集成 -
随机森林集成阶段
3.1 投票机制(分类)
3.2 特征重要性计算 -
模型评估阶段
4.1 五折交叉验证
4.2 OOB误差估计
4.3 性能指标计算 -
算法主要代码
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
加载iris数据集
data = load_iris()
打印数据集信息
print("数据集描述:")
print(data.DESCR)
创建DataFrame以便更直观地查看数据
iris_df = pd.DataFrame(data.data, columns=data.feature_names)
iris_df['target'] = data.target
iris_df['target_name'] = [data.target_names[i] for i in data.target]
查看数据集基本信息
print("\n数据集基本信息:")
print(iris_df.info())
查看数据集统计信息
print("\n数据集统计信息:")
print(iris_df.describe())
查看样本分布
print("\n样本分布:")
print(iris_df['target_name'].value_counts())
查看前5行数据
print("\n前5行数据:")
print(iris_df.head())
四、实验结果分析
- 测试结果及截图(包括:准确率、精度(查准率)、召回率(查全率)、F1)
表2. 五折交叉验证实验结果表
序号 方法 准确率 查准率 召回率 F1值
1 对数几率回归 0.9667 0.9695 0.9667 0.9665
2 C4.5 0.9533 0.9572 0.9667 0.9531
3 SMO(SVM) 0.9667 0.9695 0.9667 0.9665
4 BP神经网络 0.9800 0.9846 0.9800 0.9795
5 朴素贝叶斯 0.9467 0.9488 0.9467 0.9465
6 K-means 0.8867 0.9015 0.8867 0.8840
7 随机森林 0.9467 0.9512 0.9467 0.9464
3.对比分析
一、综合排名
根据四项性能指标(准确率、查准率、召回率、F1值)的平均排名,各算法表现如下:
排名 方法 平均排名 主要表现 1 BP神经网络 1.0 各项指标均排名第一 2 对数几率回归 2.5 与SVM并列第二 2 SMO(SVM) 2.5 与对数几率回归并列第二 4 C4.5 4.0 传统决策树算法 5 朴素贝叶斯 5.5 与随机森林并列第五 5 随机森林 5.5 与朴素贝叶斯并列第五 7 K-means 7.0 聚类算法在分类任务表现最差
二、按算法类型分析
算法类型 准确率 查准率 召回率 F1值 表现评价 深度学习 0.9800 0.9846 0.9800 0.9795 最佳 传统机器学习 0.9584 0.9612 0.9584 0.9582 良好 集成学习 0.9467 0.9512 0.9467 0.9464 稳定 聚类算法 0.8867 0.9015 0.8867 0.8840 一般
三、关键发现
-
BP神经网络表现突出 :在iris数据集上表现最佳,平均准确率达98%,能够捕捉复杂的非线性关系。
-
传统机器学习算法稳定 :对数几率回归、SVM和C4.5决策树表现良好,平均准确率均在95%以上,是可靠的选择。
-
集成学习优势明显 :随机森林作为集成学习算法,表现优于单个C4.5决策树,验证了集成学习的优势。
-
聚类算法不适合分类任务 :K-means在有标签的分类任务上表现较差,适合无监督学习场景。
-
性能与复杂度平衡 :算法性能通常与计算复杂度成正比,需根据实际需求选择合适算法。
-
心得体会
、实验收获与知识提升
- 深入理解随机森林原理 通过本次实验,我深入理解了随机森林的算法原理:它是一种集成学习方法,通过构建多个决策树并结合它们的预测结果来提高分类准确性。随机森林通过两种随机性(样本随机采样和特征随机选择)有效降低了模型的过拟合风险,这也是其在各种数据集上表现稳定的原因。
- 掌握五折交叉验证方法 实验中采用的五折交叉验证是一种可靠的模型评估方法,它将数据集分成5个子集,轮流使用4个子集进行训练,1个子集进行测试,最终取平均结果。这种方法能够更全面地评估模型性能,避免了单次划分训练集和测试集可能带来的偶然性。
- 熟悉多种机器学习算法 在对比分析环节,我研究了7种不同类型的算法(传统机器学习、深度学习、集成学习、聚类算法),了解了它们的原理、优缺点和适用场景。例如,BP神经网络在iris数据集上表现最佳,但计算复杂度高;对数几率回归实现简单,训练速度快,适合快速部署。
- 学习性能指标的综合评估 准确率、查准率、召回率和F1值是评估分类模型的重要指标,它们从不同角度反映了模型的性能。通过综合分析这些指标,我学会了如何全面评估一个模型,而不仅仅关注单一指标。
二、实验过程与实践体会
- 数据预处理的重要性 实验中使用的iris数据集质量较高,无缺失值且分布均匀,但在实际应用中,数据预处理(如缺失值处理、特征归一化、异常值检测)是非常重要的步骤,直接影响模型性能。
- 参数调优的影响 虽然本次实验中未对算法参数进行详细调优,但我认识到参数选择对模型性能的影响很大。例如,随机森林中的决策树数量、最大深度等参数都会影响模型的准确性和泛化能力。
- 算法选择的权衡 不同算法在性能、复杂度、可解释性等方面各有优劣,实际应用中需要根据具体需求进行权衡。例如,追求最高准确率时可选择BP神经网络或随机森林,而需要快速训练和部署时则应选择对数几率回归或朴素贝叶斯。
- 可视化分析的价值 通过可视化图表(柱状图、雷达图等),我更直观地比较了不同算法的性能,发现了数据中的规律和趋势。可视化是数据分析和结果呈现的重要工具,能够帮助我们更好地理解和解释实验结果。
三、对机器学习的认识提升
- 集成学习的优势 实验结果表明,集成学习(如随机森林)优于单个决策树,体现了"三个臭皮匠顶个诸葛亮"的思想。集成学习通过组合多个弱分类器,能够显著提高模型的准确性和稳定性。
- 深度学习的潜力 BP神经网络在iris数据集上表现最佳,平均准确率达98%,展示了深度学习在处理复杂非线性问题上的潜力。随着数据规模的增大和计算能力的提升,深度学习在各个领域的应用会越来越广泛。
- 算法的适用场景 没有一种算法是万能的,每种算法都有其适用场景。例如,聚类算法(如K-means)适合无监督学习,而分类算法适合有标签数据;传统机器学习算法适合小样本、低维数据,而深度学习适合大数据、高维数据。
- 实践与理论结合的重要性 本次实验将课堂上学到的理论知识与实际操作相结合,加深了我对机器学习算法的理解。只有通过实践,才能真正掌握算法的原理和应用,发现理论与实际的差距。
四、未来学习方向
- 深入学习深度学习 深度学习在iris数据集上表现出色,我计划进一步学习深度学习的原理和应用,包括卷积神经网络(CNN)、循环神经网络(RNN)等,以及它们在图像识别、自然语言处理等领域的应用。
- 学习模型调优技术 本次实验中未对算法参数进行详细调优,我计划学习网格搜索、随机搜索、贝叶斯优化等模型调优技术,提高模型的性能。
- 学习更复杂的数据集处理 本次实验使用的iris数据集相对简单,我计划学习处理更复杂的数据集,包括高维数据、不平衡数据、时序数据等,掌握相应的数据预处理和特征工程方法。
- 学习模型解释技术 随着机器学习模型的广泛应用,模型的可解释性变得越来越重要。我计划学习LIME、SHAP等模型解释技术,提高模型的透明度和可信度。
五、总结
本次随机森林算法实验是一次宝贵的学习经历,它不仅让我掌握了随机森林的实现和评估方法,还让我对多种机器学习算法有了更全面的认识。通过实验,我深刻体会到了实践的重要性,也明确了未来的学习方向。在今后的学习和工作中,我将继续深入学习机器学习知识,不断提高自己的实践能力,为解决实际问题贡献力量。
实验总结
为期 8 周的机器学习 B 上机实验已顺利完成,本次实验围绕数据处理、经典算法实现与模型评估展开,涵盖监督学习、无监督学习及集成学习等核心领域。通过 8 个递进式实验,我系统掌握了机器学习的基础流程与关键技术,现将实验情况总结如下:
一、实验核心内容回顾
本次实验以 Python 为开发工具,基于 pandas、scikit-learn、numpy 等核心库,以 iris 数据集为主要研究对象,完成了从数据准备到多种算法实现与评估的全流程实践:
实验一(数据准备与模型评估) 作为基础铺垫,实现了数据集的本地读取与库加载双重方式,掌握了五折交叉验证的核心逻辑,以及准确度、精度、召回率、F1 值等关键评估指标的计算方法,为后续算法实验奠定了数据处理与性能评估基础。
实验二至实验六(监督学习算法) 聚焦经典监督学习模型,依次完成逻辑回归、C4.5 决策树(含预剪枝与后剪枝)、SMO 支持向量机、BP 神经网络、朴素贝叶斯算法的实现与测试。通过手动编码与调用库函数两种方式,深入理解了各算法的核心原理,如逻辑回归的对数几率映射、决策树的信息增益分裂准则、SVM 的核函数思想等。
实验七(K 均值聚类) 转向无监督学习,通过去除 iris 数据集的类别标签,实践了聚类算法的训练与预测逻辑,理解了 “基于距离划分簇类” 的无监督学习本质,掌握了聚类结果与真实标签的对比评估方法。
实验八(随机森林) 聚焦集成学习,通过组合多棵决策树提升模型泛化能力,深入理解了 “Bagging 集成” 的核心思想,体会了集成学习相较于单一模型的性能优势。
二、实验关键收获
(一)技术能力提升
Python 工具链熟练应用:熟练掌握了 pandas 的数据集读写、numpy 的数值计算,以及 scikit-learn 库中数据集加载、交叉验证、模型训练与评估等模块的使用,能灵活选择工具完成实验需求。
算法原理深度理解:从理论到实践,清晰梳理了各类算法的核心逻辑 —— 如逻辑回归通过 Sigmoid 函数将线性回归映射到分类任务,C4.5 算法通过信息增益比解决连续值与过拟合问题,SMO 算法通过简化凸二次规划问题提升 SVM 训练效率,BP 神经网络通过反向传播调整权重实现误差最小化等,不再局限于理论记忆,而是形成了 “原理 - 实现 - 优化” 的完整认知。
模型评估体系构建:掌握了五折交叉验证的实现逻辑,理解其 “充分利用数据、减少过拟合风险” 的优势;能熟练计算并分析准确度、精度、召回率、F1 值的含义,可根据实验结果精准判断模型性能,如在类别不平衡场景下,通过精度与召回率的权衡选择更优模型。
代码实现与调试能力:具备了从 0 到 1 编写基础算法的能力,同时掌握了调用库函数时的参数调优方法,能通过注释清晰说明参数含义与取值逻辑;在调试过程中,学会了定位数据格式错误、算法逻辑漏洞、评估指标计算偏差等常见问题。
(二)思维认知拓展
机器学习流程系统化:形成了 “数据准备 - 模型选择 - 训练优化 - 性能评估” 的标准化流程思维,能根据任务需求选择合适的算法,如分类任务可优先尝试逻辑回归、决策树等基础模型,复杂场景可选用随机森林等集成模型,无标签数据则适用聚类算法。
算法特性与适用场景认知:通过实验结果对比,明确了各类算法的优劣与适用场景 —— 如朴素贝叶斯算法计算高效、适用于高维稀疏数据,但依赖 “特征独立” 假设;决策树解释性强,但易过拟合,需通过剪枝优化;随机森林泛化能力强,但计算成本较高;K 均值聚类无需标签,但对初始簇中心敏感。
理论与实践的结合意识:深刻体会到 “理论指导实践,实践反哺理论” 的重要性。例如,在实现 C4.5 剪枝时,通过调整剪枝参数观察模型性能变化,更直观理解了 “剪枝平衡欠拟合与过拟合” 的核心目的;在 BP 神经网络训练中,通过调整学习率、隐藏层神经元数量,体会了超参数对模型收敛速度与精度的影响。
三、实验遇到的问题与解决方案
库函数参数理解模糊:调用 scikit-learn 库中 SVM、随机森林等函数时,对部分参数(如 SVM 的核函数类型、随机森林的决策树数量)的作用与取值范围不明确。解决方案:查阅官方文档与推荐参考书,通过控制变量法测试不同参数取值对模型性能的影响,最终整理出参数说明文档,明确核心参数的作用与最优取值范围。
算法手动编码逻辑卡顿:在实现 C4.5 算法的预剪枝与后剪枝、BP 神经网络的反向传播时,出现逻辑混乱与代码报错。解决方案:先梳理算法流程图,拆解核心步骤(如 C4.5 的特征离散化、信息增益比计算、剪枝阈值判断),逐步编写代码并添加详细注释,同时参考经典实现案例,定位逻辑漏洞。
聚类结果评估偏差:K 均值聚类实验中,初始簇中心选择不当导致聚类结果与真实标签偏差较大。解决方案:通过多次随机初始化簇中心,计算不同初始值下的评估指标,选择最优结果;同时调整簇类数量 K,通过肘部法则辅助确定最优 K 值,提升聚类准确性。
数据格式不匹配问题:在数据集读取与模型输入过程中,出现特征矩阵维度错误、标签格式不兼容等问题。解决方案:使用 pandas 的shape属性、numpy 的reshape函数检查并调整数据格式,确保输入模型的特征与标签符合要求。
四、实验总结与展望
本次 8 个上机实验全面覆盖了机器学习的核心算法与关键流程,不仅提升了我的编程实践能力与算法理解深度,更培养了 “问题导向” 的思维方式 —— 面对具体任务,能自主选择合适的技术方案,并通过实验验证与优化。
同时,实验也暴露了自身的不足:一是对复杂算法(如 SMO、BP 神经网络)的深层数学原理理解仍需加强;二是在超参数调优、模型泛化能力提升等方面的经验不足;三是对更大规模数据集的处理效率优化能力有待提升。
未来,我将从三个方面继续深化学习:一是补充数学基础,深入理解算法的数学推导过程;二是拓展实验场景,尝试在图像识别、文本分类等真实数据集上应用所学算法;三是探索更高级的模型优化技术,如正则化、特征工程、集成学习的进阶方法等,不断提升自身的机器学习实践能力,将实验所学转化为解决实际问题的核心竞争力。
