Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
深度学习系列文章目录
01-【深度学习-Day 1】为什么深度学习是未来?一探究竟AI、ML、DL关系与应用
02-【深度学习-Day 2】图解线性代数:从标量到张量,理解深度学习的数据表示与运算
03-【深度学习-Day 3】搞懂微积分关键:导数、偏导数、链式法则与梯度详解
04-【深度学习-Day 4】掌握深度学习的“概率”视角:基础概念与应用解析
05-【深度学习-Day 5】Python 快速入门:深度学习的“瑞士军刀”实战指南
06-【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
07-【深度学习-Day 7】精通Pandas:从Series、DataFrame入门到数据清洗实战
08-【深度学习-Day 8】让数据说话:Python 可视化双雄 Matplotlib 与 Seaborn 教程
09-【深度学习-Day 9】机器学习核心概念入门:监督、无监督与强化学习全解析
10-【深度学习-Day 10】机器学习基石:从零入门线性回归与逻辑回归
11-【深度学习-Day 11】Scikit-learn实战:手把手教你完成鸢尾花分类项目
文章目录
前言
欢迎来到深度学习系列课程的第11天!在前面的文章中,我们已经一起学习了机器学习的基本概念和一些简单的模型,如线性回归与逻辑回归。理论知识固然重要,但将所学应用于实践才能真正巩固理解并体会到机器学习的魅力。
今天,我们将通过一个经典且非常适合初学者的项目——鸢尾花(Iris)分类——来完成我们的第一个完整的机器学习项目。我们将使用强大的Python库 Scikit-learn 来简化开发流程,从数据加载、预处理,到模型训练、预测和评估,一步步带你体验机器学习的完整生命周期。
通过本篇文章,你将学会:
- 如何加载和理解一个标准数据集。
- 为什么要划分训练集和测试集,以及如何操作。
- 如何选择并训练一个简单的分类模型(逻辑回归)。
- 如何评估模型性能并解读结果。
准备好了吗?让我们一起动手,用代码解决第一个机器学习问题吧!
在开始之前,我们先来看一下一个典型机器学习项目的通用流程图:
本篇实战将重点覆盖上述流程中的2到9点。
一、项目概述与准备工作
1.1 项目目标
本次项目的目标非常明确:构建一个机器学习模型,该模型能够根据鸢尾花的四个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)来准确预测其所属的种类(Setosa, Versicolour, Virginica)。 这是一个典型的多分类问题。
1.2 认识我们的数据集:鸢尾花 (Iris)
1.2.1 数据集背景
鸢尾花数据集(Iris dataset)是机器学习和统计学中一个经典的数据集,由英国统计学家和生物学家罗纳德·费雪(Ronald Fisher)在1936年发表的论文中引入。它常被用作初学者入门分类算法的示例。
1.2.2 数据集结构
该数据集包含了150个鸢尾花样本,每个样本对应三个种类中的一个,每个种类有50个样本。每个样本包含4个特征:
- 特征 (Features):
- 花萼长度 (Sepal Length) - 单位:cm
- 花萼宽度 (Sepal Width) - 单位:cm
- 花瓣长度 (Petal Length) - 单位:cm
- 花瓣宽度 (Petal Width) - 单位:cm
- 目标变量 (Target Variable):
- 鸢尾花的种类 (Species):
- Setosa(山鸢尾)
- Versicolour(杂色鸢尾)
- Virginica(维吉尼亚鸢尾)
- 鸢尾花的种类 (Species):
在Scikit-learn中,这三个类别通常用数字 0, 1, 2 来表示。
1.2.3 数据集特点
- 均衡性:三个类别各有50个样本,属于均衡数据集。
- 特征相关性:花瓣的测量值(长度和宽度)通常比花萼的测量值在区分种类时更有用。
- 可分性:Setosa种类通常与其他两个种类线性可分,而Versicolour和Virginica之间则存在一些重叠,区分难度稍大。
1.3 环境准备:Scikit-learn库
1.3.1 Scikit-learn简介
Scikit-learn (通常简写为 sklearn
) 是一个针对Python语言的开源机器学习库。它提供了大量用于数据挖掘和数据分析的简单高效工具,涵盖了分类、回归、聚类、降维、模型选择、预处理等多种机器学习算法和实用功能。由于其一致的API设计和丰富的文档,Scikit-learn已成为Python机器学习项目的首选库之一。
1.3.2 安装Scikit-learn
如果你还没有安装Scikit-learn以及我们将用到的其他辅助库(如NumPy进行数值计算,Matplotlib进行可视化),可以通过pip进行安装:
pip install scikit-learn matplotlib numpy pandas seaborn
在Jupyter Notebook或Google Colab等环境中,可以在代码单元格中执行:
!pip install scikit-learn matplotlib numpy pandas seaborn
numpy
: 用于高效的数值计算,是Scikit-learn的基础。pandas
: 用于数据处理和分析,尤其适合处理表格数据。matplotlib
和seaborn
: 用于数据可视化,帮助我们理解数据和模型结果。
1.3.3 导入必要的库
在开始编码之前,我们先导入本次项目中需要用到的Python库和模块:
# 数据加载与处理
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 模型选择
from sklearn.linear_model import LogisticRegression
# 模型评估
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# 可视化 (可选,但推荐)
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
# 设置一些绘图参数,使图形更美观 (可选)
plt.style.use('ggplot')
sns.set_palette('husl')
print("所有库导入成功!")
二、数据加载与探索性分析 (EDA)
探索性数据分析(Exploratory Data Analysis, EDA)是机器学习项目中至关重要的一步。它帮助我们理解数据的基本特性、发现数据中的模式、识别异常值以及验证假设。
2.1 加载Iris数据集
2.1.1 使用Scikit-learn内置函数加载
Scikit-learn非常贴心地内置了一些经典数据集,Iris就是其中之一。我们可以使用load_iris()
函数轻松加载:
# 加载Iris数据集
iris_dataset = load_iris()
# 将特征数据赋值给 X,标签数据赋值给 y
X = iris_dataset.data
y = iris_dataset.target
2.1.2 理解数据结构
加载后的iris_dataset
对象是一个Bunch对象,类似于字典,包含了数据和元信息:
iris_dataset.data
: 包含样本特征的NumPy数组,形状为 (150, 4)。iris_dataset.target
: 包含样本标签(种类)的NumPy数组,形状为 (150,)。iris_dataset.feature_names
: 特征的名称列表。iris_dataset.target_names
: 标签的名称列表。iris_dataset.DESCR
: 数据集的完整描述。
让我们打印一些信息来具体看看:
print(f"特征数据 (X) 的形状: {X.shape}")
print(f"标签数据 (y) 的形状: {y.shape}")
print(f"\n特征名称: {iris_dataset.feature_names}")
print(f"类别名称: {iris_dataset.target_names}")
print(f"\n前5行特征数据 (X):\n{X[:5]}")
print(f"\n前5行标签数据 (y):\n{y[:5]}")
# 可以取消注释下一行来查看数据集的详细描述
# print(f"\n数据集描述:\n{iris_dataset.DESCR[:500]}...") # 仅显示前500字符
输出结果会显示150个样本,每个样本4个特征,以及对应0、1、2的类别标签。
2.2 (可选)数据可视化初步探索
可视化是理解数据最直观的方式。我们可以绘制一些图表来观察特征的分布和特征之间的关系。
2.2.1 理解特征间的关系
我们可以使用散点图矩阵(Pair Plot)来展示每对特征之间的关系,并根据鸢尾花的种类对数据点进行着色。这有助于我们初步判断哪些特征对于分类更有帮助。
# 为了方便使用seaborn进行绘图,我们将数据转换为Pandas DataFrame
iris_df = pd.DataFrame(data=X, columns=iris_dataset.feature_names)
iris_df['species'] = pd.Categorical.from_codes(iris_dataset.target, iris_dataset.target_names)
# 绘制散点图矩阵
plt.figure() # 创建一个新的图形,避免与之前的图形重叠
sns.pairplot(iris_df, hue='species', markers=["o", "s", "D"])
plt.suptitle("Iris Dataset Pair Plot (Features by Species)", y=1.02) # 添加总标题并调整位置
plt.show()
通过观察Pair Plot,我们可以发现:
petal length (cm)
和petal width (cm)
对于区分三种鸢尾花非常有效。Setosa种类的花瓣尺寸明显较小。- Versicolour和Virginica在某些特征上有所重叠,但仍可区分。
2.2.2 观察类别分布
确认一下数据集中各个类别的样本数量是否均衡。
plt.figure(figsize=(8, 6))
sns.countplot(x='species', data=iris_df)
plt.title('Class Distribution in Iris Dataset')
plt.xlabel('Species')
plt.ylabel('Count')
plt.show()
# 或者用代码确认
print("\n各类别样本数量:")
print(iris_df['species'].value_counts())
可以看到,每个种类都有50个样本,数据集是完美均衡的。
三、数据预处理:划分训练集与测试集
在训练机器学习模型之前,我们需要将数据集划分为两部分:训练集(Training Set)和测试集(Test Set)。
3.1 为什么需要划分数据集?
3.1.1 评估模型的泛化能力
我们使用训练集来“教”模型学习数据中的模式。但是,我们真正关心的是模型在未见过的新数据上的表现如何,这被称为模型的泛化能力。如果在训练模型的数据上评估模型,可能会得到过于乐观的结果,因为模型可能只是“记住”了训练数据(即过拟合),而不是真正学会了普适的规律。测试集就是用来模拟新数据,从而客观评估模型的泛化能力。
3.1.2 训练集、验证集与测试集回顾
在Day 9的文章中我们提到过:
- 训练集 (Training Set): 用于训练模型,即调整模型参数。
- 验证集 (Validation Set): 用于在模型训练过程中调整超参数(例如学习率、正则化强度)和进行模型选择。
- 测试集 (Test Set): 在模型训练和调优完成后,用于最终评估模型性能的独立数据集。
对于我们这个入门项目,为了简化流程,我们将仅划分训练集和测试集。在更复杂的项目中,验证集是必不可少的。
3.2 使用train_test_split
进行划分
Scikit-learn 提供了 train_test_split
函数,可以方便地将数据集随机划分为训练集和测试集。
3.2.1 函数介绍
train_test_split
的常用参数:
X, y
: 要划分的特征数据和标签数据。test_size
: 测试集所占的比例或绝对数量。通常取0.2到0.3之间(即20%-30%)。train_size
: 训练集所占的比例或绝对数量。如果设置了test_size
,则此参数通常可以省略。random_state
: 随机数种子。设置一个固定的整数(如0, 42等)可以确保每次划分的结果都一样,便于复现实验结果。stratify
: 分层抽样。传入y
(标签数组)时,函数会确保划分后的训练集和测试集中,各个类别的样本比例与原始数据集中大致相同。这对于分类问题,尤其是在类别不均衡时非常重要。
3.2.2 代码实现
我们将测试集比例设为30% (test_size=0.3
),并设置random_state
以保证结果可复现。由于这是一个分类任务,使用stratify=y
是一个好习惯。
X_train, X_test, y_train, y_test = train_test_split(
X, # 特征数据
y, # 标签数据
test_size=0.3, # 测试集占比30%
random_state=42, # 随机种子,确保结果可复现
stratify=y # 保持类别比例,对分类问题很重要
)
print(f"原始数据集特征形状: {X.shape}")
print(f"原始数据集标签形状: {y.shape}")
print("-" * 30)
print(f"训练集特征形状 (X_train): {X_train.shape}")
print(f"训练集标签形状 (y_train): {y_train.shape}")
print("-" * 30)
print(f"测试集特征形状 (X_test): {X_test.shape}")
print(f"测试集标签形状 (y_test): {y_test.shape}")
print("-" * 30)
# 检查分层抽样效果
print(f"原始数据集中各类别比例: {np.bincount(y) / len(y)}")
print(f"训练数据集中各类别比例: {np.bincount(y_train) / len(y_train)}")
print(f"测试数据集中各类别比例: {np.bincount(y_test) / len(y_test)}")
可以看到,数据被成功划分,并且由于stratify=y
,训练集和测试集中的类别比例与原始数据集基本一致。
3.3 (可选)特征缩放 (Feature Scaling)
3.3.1 为什么以及何时需要特征缩放?
特征缩放是将数据的不同特征调整到相似的数值范围的过程。
- 原因:许多机器学习算法(如基于梯度下降的算法如逻辑回归、支持向量机(SVM)、神经网络,以及基于距离计算的算法如K近邻(KNN))在特征值范围相差很大时,可能会表现不佳或收敛缓慢。数值较大的特征可能会主导模型的学习过程。
- 常用方法:
- 标准化 (Standardization): 将数据转换为均值为0,标准差为1的分布。公式为
z
=
f
r
a
c
(
x
−
m
u
)
s
i
g
m
a
z = \\frac{(x - \\mu)}{\\sigma}
z=frac(x−mu)sigma,其中
m
u
\\mu
mu 是均值,
s
i
g
m
a
\\sigma
sigma 是标准差。Scikit-learn中的
StandardScaler
实现此功能。 - 归一化 (Normalization / Min-Max Scaling): 将数据缩放到一个固定范围,通常是[0, 1]或[-1, 1]。公式为
x
_
s
c
a
l
e
d
=
f
r
a
c
(
x
−
x
_
m
i
n
)
(
x
_
m
a
x
−
x
_
m
i
n
)
x\_{scaled} = \\frac{(x - x\_{min})}{(x\_{max} - x\_{min})}
x_scaled=frac(x−x_min)(x_max−x_min)。Scikit-learn中的
MinMaxScaler
实现此功能。
- 标准化 (Standardization): 将数据转换为均值为0,标准差为1的分布。公式为
z
=
f
r
a
c
(
x
−
m
u
)
s
i
g
m
a
z = \\frac{(x - \\mu)}{\\sigma}
z=frac(x−mu)sigma,其中
m
u
\\mu
mu 是均值,
s
i
g
m
a
\\sigma
sigma 是标准差。Scikit-learn中的
3.3.2 在Iris项目中的考虑
Iris数据集的四个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)都是长度测量值,单位相同(cm),并且它们的数值范围差异不是特别巨大(例如,花萼宽度约2-4.4cm,花瓣长度约1-6.9cm)。因此,对于逻辑回归这样的模型,在这个特定数据集上,特征缩放可能不会带来显著的性能提升,有时甚至不缩放也能获得很好的结果。
为了保持我们第一个项目的简洁性,我们这里暂时不进行显式的特征缩放。但在实际应用中,特别是当你对数据特性不完全了解或特征间尺度差异较大时,进行特征缩放是一个推荐的预处理步骤。
如果需要进行特征缩放,代码大致如下:
# from sklearn.preprocessing import StandardScaler
#
# # 1. 初始化Scaler
# scaler = StandardScaler()
#
# # 2. 在训练集上拟合Scaler并转换训练集
# X_train_scaled = scaler.fit_transform(X_train)
#
# # 3. 使用训练集上拟合的Scaler转换测试集 (重要:不要在测试集上重新fit)
# X_test_scaled = scaler.transform(X_test)
#
# # 之后,模型训练和预测将使用 X_train_scaled 和 X_test_scaled
四、模型选择与训练
数据准备就绪后,下一步是选择一个合适的机器学习模型并用训练数据对其进行训练。
4.1 选择模型:逻辑回归 (Logistic Regression)
4.1.1 模型回顾
我们在Day 10的文章中学习过逻辑回归。它虽然名字中带有“回归”,但实际上是一种广泛用于分类问题的线性模型。
- 对于二分类问题,逻辑回归使用Sigmoid函数将线性输出映射到(0, 1)之间的概率值。
- 对于多分类问题(如Iris数据集的三个类别),可以采用“一对多”(One-vs-Rest, OvR)策略或使用Softmax函数(通常称为Softmax回归或多项逻辑回归)来处理。
为什么选择逻辑回归作为我们的第一个模型?
- 简单高效:模型相对简单,计算速度快,易于理解。
- 可解释性强:可以了解哪些特征对分类结果影响较大。
- 良好基线:是许多分类问题的良好起点和基准模型。
- Scikit-learn支持良好:实现起来非常方便。
4.1.2 Scikit-learn中的LogisticRegression
Scikit-learn的sklearn.linear_model.LogisticRegression
类实现了逻辑回归。其一些重要参数包括:
penalty
: 正则化类型,可选 ‘l1’, ‘l2’, ‘elasticnet’, ‘none’。默认为 ‘l2’。正则化用于防止过拟合。C
: 正则化强度的倒数。值越小,正则化强度越大。默认为1.0。solver
: 用于优化问题的算法。可选 ‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’。不同的solver支持不同的penalty和多分类策略。对于小型数据集,‘liblinear’ 是个不错的选择;‘lbfgs’ 是常用的默认值,支持多项损失。multi_class
: 处理多分类问题的方式。可选 ‘ovr’ (one-vs-rest), ‘multinomial’。‘multinomial’ 通常与 ‘lbfgs’ 或 ‘newton-cg’ solver 配合使用,直接拟合多项逻辑回归。‘ovr’ 则是为每个类别训练一个二分类器。max_iter
: 优化算法的最大迭代次数。如果算法不收敛,可能需要增加此值。
4.2 训练模型
模型训练就是让模型从训练数据中学习的过程。
4.2.1 实例化模型
我们首先创建一个LogisticRegression
类的实例。对于Iris这个多分类问题,我们可以设置multi_class='multinomial'
并使用solver='lbfgs'
。
# 实例化逻辑回归模型
# solver='lbfgs' 支持 'multinomial' 损失,适合多分类问题
# random_state 使某些solver的随机初始化过程可复现
# max_iter 确保算法有足够迭代次数收敛,Iris数据集较小,默认100通常够用,但可以适当增大
log_reg_model = LogisticRegression(solver='lbfgs',
multi_class='multinomial', # 或者 'ovr'
random_state=42,
max_iter=200) # 增加迭代次数以确保收敛
print("逻辑回归模型实例化成功:", log_reg_model)
4.2.2 使用训练集进行拟合 (fit
方法)
然后,我们调用模型的fit()
方法,传入训练集的特征 (X_train
) 和标签 (y_train
) 来训练模型。
# 使用训练数据训练模型
# 如果之前进行了特征缩放,这里应使用 X_train_scaled
log_reg_model.fit(X_train, y_train)
print("模型训练完成!")
训练完成后,log_reg_model
对象就包含了学习到的参数(权重和偏置)。
4.2.3 (进阶) 理解模型参数
训练好的逻辑回归模型会学习到每个特征对于每个类别的权重。对于多分类(假设有K个类别和P个特征):
model.coef_
: 权重系数矩阵,形状通常是(K, P)
(对于multi_class='multinomial'
或'ovr'
且类别数大于2)。每一行对应一个类别的权重向量。model.intercept_
: 截距(偏置)项,形状通常是(K,)
。
# 查看模型学习到的系数和截距 (可选)
print(f"\n模型系数 (model.coef_):\n{log_reg_model.coef_}")
print(f"形状: {log_reg_model.coef_.shape}") # (3 classes, 4 features)
print(f"\n模型截距 (model.intercept_):\n{log_reg_model.intercept_}")
print(f"形状: {log_reg_model.intercept_.shape}") # (3 classes)
这些系数可以提供一些关于特征重要性的洞察,但详细解读超出了本入门项目的范围。
五、模型预测与评估
模型训练完成后,我们需要使用它对测试集进行预测,并评估其性能。
5.1 使用模型进行预测
5.1.1 对测试集进行预测 (predict
方法)
我们使用训练好的模型的predict()
方法,并传入测试集的特征 (X_test
) 来得到模型对这些未知样本的预测类别。
# 使用训练好的模型对测试集进行预测
# 如果之前进行了特征缩放,这里应使用 X_test_scaled
y_pred = log_reg_model.predict(X_test)
5.1.2 查看预测结果
我们可以打印出部分预测结果与真实的测试集标签进行对比。
print(f"预测的类别 (y_pred) 前10个: {y_pred[:10]}")
print(f"真实的类别 (y_test) 前10个: {y_test[:10]}")
# 对比预测值和真实值
comparison_df = pd.DataFrame({'Actual': y_test, 'Predicted': y_pred})
comparison_df['Actual_Name'] = comparison_df['Actual'].map(lambda x: iris_dataset.target_names[x])
comparison_df['Predicted_Name'] = comparison_df['Predicted'].map(lambda x: iris_dataset.target_names[x])
print("\n部分预测结果与真实值对比:")
print(comparison_df.head(10))
5.2 评估模型性能
仅凭肉眼对比预测结果是不够的,我们需要使用量化的评估指标来衡量模型的表现。
5.2.1 准确率 (Accuracy)
准确率是最常用的分类评估指标之一,它衡量的是模型正确预测的样本占总样本数的比例。
Accuracy
=
Number of Correct Predictions
Total Number of Predictions
\text{Accuracy} = \frac{\text{Number of Correct Predictions}}{\text{Total Number of Predictions}}
Accuracy=Total Number of PredictionsNumber of Correct Predictions
Scikit-learn的sklearn.metrics.accuracy_score
函数可以计算准确率。
# 计算模型在测试集上的准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"\n模型在测试集上的准确率: {accuracy:.4f}") # 保留4位小数
# 通常,准确率会很高,例如1.0,表示所有测试样本都预测正确。
# 这是因为Iris数据集相对简单,且逻辑回归在这个任务上表现良好。
5.2.2 (进阶)混淆矩阵 (Confusion Matrix)
混淆矩阵是一个表格,用于更详细地展示分类模型在每个类别上的预测情况。矩阵的行代表真实类别,列代表预测类别。
对于一个三分类问题,混淆矩阵的形式如下:
预测为Setosa | 预测为Versicolour | 预测为Virginica | |
---|---|---|---|
真实Setosa | True Positive (Setosa) | False Negative (Setosa predicted as Versicolour) | False Negative (Setosa predicted as Virginica) |
真实Versicolour | False Positive (Versicolour predicted as Setosa) | True Positive (Versicolour) | False Negative (Versicolour predicted as Virginica) |
真实Virginica | False Positive (Virginica predicted as Setosa) | False Positive (Virginica predicted as Versicolour) | True Positive (Virginica) |
对角线上的元素表示正确预测的样本数量。非对角线元素表示错误预测的样本数量。
# 计算混淆矩阵
cm = confusion_matrix(y_test, y_pred)
print("\n混淆矩阵 (Confusion Matrix):")
print(cm)
# 使用seaborn可视化混淆矩阵,使其更直观
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=iris_dataset.target_names,
yticklabels=iris_dataset.target_names)
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.title('Confusion Matrix for Iris Classification')
plt.show()
如果准确率是100%,那么混淆矩阵的非对角线元素将全部为0。
5.2.3 (进阶)分类报告 (Classification Report)
分类报告提供了每个类别的精确率 (Precision)、召回率 (Recall) 和 F1分数 (F1-score),以及整体的准确率。
- 精确率 (Precision): 对于某个类别,模型预测为该类别的样本中,真正属于该类别的比例。 P r e c i s i o n = f r a c T P T P + F P Precision = \\frac{TP}{TP + FP} Precision=fracTPTP+FP (TP: True Positive, FP: False Positive)
- 召回率 (Recall): 对于某个类别,该类别的所有真实样本中,被模型成功预测出来的比例。 R e c a l l = f r a c T P T P + F N Recall = \\frac{TP}{TP + FN} Recall=fracTPTP+FN (FN: False Negative)
- F1分数 (F1-score): 精确率和召回率的调和平均数,是综合评价指标。 F 1 = 2 t i m e s f r a c P r e c i s i o n t i m e s R e c a l l P r e c i s i o n + R e c a l l F1 = 2 \\times \\frac{Precision \\times Recall}{Precision + Recall} F1=2timesfracPrecisiontimesRecallPrecision+Recall
- Support: 每个类别在真实标签中的样本数量。
# 生成分类报告
report = classification_report(y_test, y_pred, target_names=iris_dataset.target_names)
print("\n分类报告 (Classification Report):")
print(report)
分类报告能让我们更全面地了解模型在不同类别上的表现,尤其是在类别不均衡的数据集上。
六、实践:完整的代码流程回顾
下面我们将所有步骤整合到一个完整的代码块中,方便你复制和运行。
6.1 整合所有步骤
# 1. 导入必要的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# 设置绘图风格
plt.style.use('ggplot')
sns.set_palette('husl')
print("--- 开始执行Iris分类项目 ---")
# 2. 加载Iris数据集
print("\n--- 步骤1: 加载数据 ---")
iris_dataset = load_iris()
X = iris_dataset.data
y = iris_dataset.target
feature_names = iris_dataset.feature_names
target_names = iris_dataset.target_names
print(f"特征: {feature_names}")
print(f"类别: {target_names}")
print(f"数据形状: X-{X.shape}, y-{y.shape}")
# (可选) 创建DataFrame用于可视化
iris_df = pd.DataFrame(data=X, columns=feature_names)
iris_df['species_code'] = y
iris_df['species_name'] = iris_df['species_code'].map(lambda x: target_names[x])
# (可选) 数据可视化 Pair Plot
print("\n--- (可选) 步骤2: 数据可视化 ---")
# sns.pairplot(iris_df, hue='species_name', markers=["o", "s", "D"])
# plt.suptitle("Iris Dataset Pair Plot", y=1.02)
# plt.show() # 在脚本中运行时,如果不需要立即显示可以注释掉
# 3. 划分训练集与测试集
print("\n--- 步骤3: 划分训练集与测试集 ---")
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.3,
random_state=42, # 确保结果可复现
stratify=y # 保持类别比例
)
print(f"训练集形状: X_train-{X_train.shape}, y_train-{y_train.shape}")
print(f"测试集形状: X_test-{X_test.shape}, y_test-{y_test.shape}")
# 4. 选择并训练模型:逻辑回归
print("\n--- 步骤4: 模型训练 ---")
log_reg_model = LogisticRegression(solver='lbfgs', multi_class='multinomial', random_state=42, max_iter=200)
log_reg_model.fit(X_train, y_train)
print("逻辑回归模型训练完成。")
# 5. 模型预测
print("\n--- 步骤5: 模型预测 ---")
y_pred = log_reg_model.predict(X_test)
print(f"部分预测值: {y_pred[:5]}")
print(f"部分真实值: {y_test[:5]}")
# 6. 模型评估
print("\n--- 步骤6: 模型评估 ---")
# 准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"准确率 (Accuracy): {accuracy:.4f}")
# 混淆矩阵
cm = confusion_matrix(y_test, y_pred)
print("\n混淆矩阵:")
print(cm)
plt.figure(figsize=(8,6))
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", xticklabels=target_names, yticklabels=target_names)
plt.title("Confusion Matrix")
plt.xlabel("Predicted Label")
plt.ylabel("True Label")
plt.show() # 在脚本中运行时,如果不需要立即显示可以注释掉
# 分类报告
report = classification_report(y_test, y_pred, target_names=target_names)
print("\n分类报告:")
print(report)
print("\n--- Iris分类项目执行完毕 ---")
6.2 代码注释与解读
上面的代码块中已经包含了详细的注释。关键点回顾:
- 数据准备:使用
load_iris()
加载,train_test_split()
划分。 - 模型核心:实例化
LogisticRegression
,然后调用.fit()
训练,.predict()
预测。 - 结果分析:使用
accuracy_score
、confusion_matrix
、classification_report
进行评估。
七、常见问题与进一步思考 (Q&A and Further Thoughts)
7.1 模型表现不佳怎么办?
虽然在这个项目中我们的逻辑回归模型表现可能非常好(甚至100%准确),但在更复杂的实际问题中,模型初次表现不佳是很常见的。可以从以下方面考虑改进:
7.1.1 数据层面
- 获取更多数据:数据量越大,模型通常能学习得更好。
- 特征工程:创建新的、更有意义的特征,或对现有特征进行转换(如多项式特征、交互特征)。
- 特征选择:移除不相关或冗余的特征。
- 处理缺失值和异常值:这些都会影响模型性能。
- 特征缩放:对于某些算法(如SVM、KNN或使用梯度下降优化的算法),特征缩放非常重要。
7.1.2 模型层面
- 尝试不同的算法:逻辑回归是一个线性模型。如果数据本身是非线性可分的,可能需要更复杂的模型,如决策树、随机森林、支持向量机(SVM)、梯度提升机(Gradient Boosting)或神经网络(深度学习的核心)。
- 超参数调优:调整模型的超参数(如逻辑回归中的
C
值、solver
)。可以使用网格搜索(Grid Search)或随机搜索(Random Search)等方法系统地寻找最佳超参数组合。 - 正则化:调整正则化参数以防止过拟合。
7.2 如何选择random_state
?
random_state
参数在Scikit-learn中广泛用于任何涉及随机性的操作(如数据划分、模型初始化中的随机权重等)。它的作用是固定随机数生成器的种子。
- 目的:确保实验的可复现性。只要
random_state
设为同一个整数,每次运行代码时,随机过程的结果都会相同。这对于调试代码、分享结果或与他人协作非常重要。 - 取值:可以是任意整数。常用的有0, 1, 42等。选择哪个具体数字并不影响最终模型的“好坏”,只影响“这一次”随机操作的具体结果。
7.3 test_size
选多大合适?
test_size
(或train_size
)决定了训练集和测试集的划分比例。
- 常见选择:测试集通常占总数据的20%到30%(即
test_size=0.2
或test_size=0.3
)。 - 权衡:
- 较小的测试集:意味着有更多的训练数据,模型可能学习得更充分。但测试集较小,评估结果的置信度可能会降低,波动性较大。
- 较大的测试集:评估结果更可靠,但训练数据减少,可能导致模型欠拟合或未能充分学习。
- 数据集大小:如果总数据量非常大,即使测试集比例较小(如10%甚至更少),也能有足够多的样本进行可靠评估。如果总数据量很小,可能需要使用交叉验证等技术来更有效地利用数据。
7.4 为什么使用stratify=y
?
在train_test_split
中设置stratify=y
(其中y
是类别标签数组)是为了进行分层抽样。
- 目的:确保在划分数据后,训练集和测试集中的各个类别的样本比例与原始数据集中保持一致(或尽可能接近)。
- 重要性:
- 类别不均衡数据集:如果某个类别样本非常少,随机划分可能导致该类别在训练集或测试集中完全缺失,或者比例严重失调,从而影响模型的学习和评估。
- 分类问题:对于所有分类问题,使用分层抽样都是一个好习惯,即使数据集是均衡的(如Iris),它也能保证划分的稳定性。
7.5 如何保存和加载训练好的模型?
当你训练好一个满意的模型后,可能希望将其保存下来,以便将来可以直接加载使用,而无需重新训练。Python的pickle
模块或Scikit-learn推荐的joblib
库(对NumPy大型数组更高效)可以用来序列化和反序列化模型。
import joblib
# # 假设 log_reg_model 是我们训练好的模型
# model_filename = 'iris_logistic_regression_model.pkl'
#
# # 1. 保存模型
# joblib.dump(log_reg_model, model_filename)
# print(f"\n模型已保存到 {model_filename}")
#
# # 2. 加载模型 (在另一个脚本或稍后时间)
# loaded_model = joblib.load(model_filename)
# print(f"\n模型已从 {model_filename} 加载")
#
# # 3. 使用加载的模型进行预测
# accuracy_loaded = accuracy_score(y_test, loaded_model.predict(X_test))
# print(f"加载模型的准确率: {accuracy_loaded:.4f}")
我们将在后续的文章(如Day 29)中更详细地讨论模型保存与加载。
八、总结
恭喜你完成了第一个完整的机器学习实战项目!通过对鸢尾花数据集的分类,我们一起经历了从数据加载到模型评估的全过程。希望这个实践能让你对机器学习的工作流程有一个更具体和深入的理解。
在本篇文章中,我们主要学习和实践了以下核心内容:
- 项目流程认知:理解了一个典型机器学习项目从数据准备到模型评估的基本步骤。
- Scikit-learn初步应用:掌握了使用Scikit-learn库加载经典数据集(
load_iris
)、划分训练集和测试集(train_test_split
)的基本操作。 - 模型训练与预测:学会了如何实例化一个分类模型(
LogisticRegression
),使用训练数据进行拟合(.fit()
方法),并对新数据进行预测(.predict()
方法)。 - 模型性能评估:了解并实践了常用的分类模型评估指标,包括准确率(
accuracy_score
)、混淆矩阵(confusion_matrix
)和分类报告(classification_report
),并学会了如何解读这些指标。 - 实践的重要性:再次强调了动手实践对于巩固理论知识、培养解决问题能力的极端重要性。
- 承上启下:本次实战为我们后续学习更复杂的模型(如神经网络)和处理更具挑战性的任务(如图像分类、文本处理)打下了坚实的基础。
虽然鸢尾花分类相对简单,但其中蕴含的机器学习思想和方法是普适的。随着你学习的深入,你会发现这些基础概念和技能将在更广阔的领域中发挥作用。
在接下来的文章中,我们将开始探索神经网络的世界,敬请期待!如果你在实践过程中遇到任何问题,欢迎在评论区留言讨论。