news 2026/9/7 11:42:58

Scikit-Learn花朵分类入门:鸢尾花数据集的模型训练与评估实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Scikit-Learn花朵分类入门:鸢尾花数据集的模型训练与评估实践

在实际的机器学习入门路径里,Scikit-Learn 是最适合作为第一站的工具库之一。它封装了大量经典算法、数据集和评估工具,能让初学者在不需要深入底层数学的情况下,先跑通“数据加载、模型训练、结果评估”这条完整流程。花朵分类是其中最常见、也最适合用来建立整体认知的入门任务:数据集小、特征明确、类别清晰,用它可以快速验证分类模型从训练到预测的每个环节。这篇文章会用 Scikit-Learn 完成一个可运行的花朵分类体验项目,覆盖环境准备、数据划分、模型训练、评估分析和常见报错排查,帮助读者在本地复现完整流程,并理解每一步背后的原因。

1. 为什么花朵分类适合作为机器学习入门项目

1.1 分类任务要先理解数据、特征和标签三个概念

花朵分类本质上是一个监督学习里的多分类问题。监督学习的含义是:我们拿到一批已经标注好类别的样本,用这些样本训练模型,让模型学会根据特征判断新样本的类别。

这里要先分清三个基本概念:

  • 特征(Feature):每个样本用于判断的属性,比如花瓣长度、花瓣宽度、花萼长度、花萼宽度。
  • 标签(Label):样本的真实类别,比如山鸢尾、变色鸢尾、维吉尼亚鸢尾。
  • 样本(Sample):一行数据,包含所有特征和对应的标签。

用一句话概括分类任务:给定一组特征,让模型输出它属于哪一个类别。

花朵分类最常用的数据集是鸢尾花数据集(Iris Dataset),它由统计学家 Ronald Fisher 在 1936 年引入,包含 150 条样本、4 个特征、3 个类别,每个类别 50 条样本。这个数据集被 Scikit-Learn 内置在sklearn.datasets中,不需要额外下载文件,非常适合验证安装环境是否正常。

1.2 Scikit-Learn 在机器学习流程中的位置

Scikit-Learn 是一个基于 Python 的机器学习库,底层依赖 NumPy 和 SciPy 处理数值计算,核心 API 设计高度统一。大多数模型的使用方式几乎一样:先创建模型对象,再调用fit训练,然后调用predict预测,最后用score或评估指标检查效果。

这种统一接口的好处在于:换算法时不需要重写数据处理逻辑,只需要替换模型类,就能对比不同算法在同一份数据上的表现。对于学习阶段而言,这可以避免把精力浪费在重复代码上,把注意力集中在理解算法差异和评估结果上。

Scikit-Learn 覆盖了分类、回归、聚类、降维、数据预处理、模型选择等常见任务。实际项目中的工作流通常可以抽象为下面这条链路:

  1. 加载数据。
  2. 划分训练集和测试集。
  3. 选择模型。
  4. 训练模型。
  5. 评估模型。
  6. 用模型对新样本做预测。

这篇文章后续的所有内容都会围绕这条链路展开。

2. 环境准备:安装 Python、Scikit-Learn 和依赖库

2.1 版本选择和安装方式

Scikit-Learn 是基于 Python 的库,安装之前需要确认 Python 版本。不同版本的 Scikit-Learn 对 Python 版本有要求,因此不要直接安装最新版,而是先检查本机环境。

推荐使用 Python 3.9 到 3.12 之间的版本,这些版本与当前主流 Scikit-Learn 版本兼容性较好。检查 Python 版本:

python --version

如果系统同时安装了 Python 2 和 Python 3,可能需要用python3命令:

python3 --version

确认版本后,使用 pip 安装:

pip install scikit-learn

国内网络环境下,可以指定镜像源加速下载:

pip install scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,验证版本:

python -c "import sklearn; print(sklearn.__version__)"

正常输出类似1.5.2的版本号,说明安装成功。

注意:如果本机同时存在多个 Python 环境,一定要确认pippython指向同一个环境,否则会出现“安装成功但 import 失败”的经典问题。

2.2 使用虚拟环境隔离项目依赖

实际开发中不推荐把依赖直接装到系统 Python 里,因为多个项目可能依赖不同版本的库,互相升级会造成冲突。推荐为这个入门项目单独创建虚拟环境。

使用 Python 内置的venv模块即可:

python -m venv ml-flower-env

激活环境:

Windows:

ml-flower-env\Scripts\activate

macOS 和 Linux:

source ml-flower-env/bin/activate

激活后,命令行提示符前面会出现环境名,此时再执行 pip 安装,依赖只会装进当前虚拟环境,不会污染系统环境。

除了 Scikit-Learn,通常还需要安装:

pip install numpy pandas matplotlib

各库的分工如下:

库名作用本项目的使用场景
scikit-learn提供数据集、模型和评估工具加载鸢尾花数据、训练分类器、计算评估指标
numpy提供数组和数值计算能力处理特征矩阵和标签数组
pandas提供 DataFrame 表格结构数据预览、统计描述
matplotlib提供绘图能力绘制数据分布图和混淆矩阵图

2.3 验证环境是否可用的最小脚本

环境是否真正可用,不能只看安装成功。运行下面这段最小脚本,确认数据能加载、模型能训练:

from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier data = load_iris() X_train, X_test, y_train, y_test = train_test_split( data.data, data.target, test_size=0.2, random_state=42 ) model = KNeighborsClassifier(n_neighbors=3) model.fit(X_train, y_train) print("测试集准确率:", model.score(X_test, y_test))

如果输出类似测试集准确率: 1.0的数字,说明数据集加载、数据划分、模型训练和评估这四个环节全部正常。

3. 加载数据并完成训练集与测试集划分

3.1 使用 Scikit-Learn 内置数据集

鸢尾花数据集不用手动下载,直接通过load_iris加载:

from sklearn.datasets import load_iris data = load_iris() print("特征名:", data.feature_names) print("类别名:", data.target_names) print("特征矩阵形状:", data.data.shape) print("标签数组形状:", data.target.shape)

运行结果:

特征名: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'] 类别名: ['setosa' 'versicolor' 'virginica'] 特征矩阵形状: (150, 4) 标签数组形状: (150,)

这里要理解data对象的结构。data.data是一个形状为(150, 4)的二维数组,150 行代表 150 条样本,4 列代表 4 个特征。data.target是一维数组,长度是 150,元素是 0、1、2 这样的整数,分别对应target_names里的三个类别。

3.2 数据划分为什么要保持随机性和分层

模型训练的理想目标是“学会规律”,而不是“背下数据”。如果直接用全部数据训练,再用同一批数据评估,模型得分会虚高,因为模型已经见过答案。因此在训练之前,必须把数据划分成训练集和测试集。

from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( data.data, data.target, test_size=0.2, random_state=42, stratify=data.target )

四个关键参数的含义:

参数含义推荐取值
test_size测试集占全部数据的比例0.2 到 0.3,数据量小可以取 0.2
random_state随机数种子,固定后结果可复现任意整数,如 42
stratify按标签比例分层抽样分类任务推荐设置
shuffle是否打乱数据默认True

stratify=data.target的作用是让训练集和测试集中的类别比例与原始数据保持一致。原始数据每个类别 50 条,按 0.8:0.2 划分后,理论上每个类别在训练集有 40 条、测试集有 10 条,分层抽样能保证这一比例稳定。

划分后的结果:

print("训练集样本数:", X_train.shape[0]) print("测试集样本数:", X_test.shape[0])

输出:

训练集样本数: 120 测试集样本数: 30

3.3 查看数据的统计信息

在训练之前先浏览数据分布,有助于判断特征量纲和类别是否均衡。这一步不依赖模型,但能避免后期出现“模型训练完才发现数据有问题”的情况。

import pandas as pd df = pd.DataFrame(data.data, columns=data.feature_names) df["label"] = data.target print(df.describe()) print(df.groupby("label").size())

describe()会输出每个特征的数量、均值、标准差、最小值和分位数。groupby("label").size()可以确认每个类别的样本数量。

4. 训练多个分类模型并比较效果

4.1 基线模型:K 近邻分类器

K 近邻(K-Nearest Neighbors,KNN)是最直观的分类算法。它的思路是:判断一个新样本属于哪一类,就找训练集中离它最近的 K 个样本,让这些邻居投票决定类别。

下面用三个最近邻训练模型:

from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors=3) knn.fit(X_train, y_train) y_pred = knn.predict(X_test) print("KNN 测试集准确率:", accuracy_score(y_test, y_pred))

这里需要解释fitpredict两个方法。fit(X_train, y_train)是让模型学习训练集中的特征与标签之间的关系;predict(X_test)是让模型基于学习到的规律对测试集样本做出预测。Scikit-Learn 的绝大多数分类模型都具有这两个方法,这是它接口统一的重要体现。

4.2 对比逻辑回归、决策树和支持向量机

入门阶段建议多试几种算法,感受不同模型在同一数据上的效果差异。这里选择三个有代表性的分类器:

from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.svm import SVC from sklearn.metrics import accuracy_score models = { "逻辑回归": LogisticRegression(max_iter=1000), "决策树": DecisionTreeClassifier(random_state=42), "支持向量机": SVC(gamma="scale"), "K近邻": KNeighborsClassifier(n_neighbors=3), } for name, model in models.items(): model.fit(X_train, y_train) y_pred = model.predict(X_test) acc = accuracy_score(y_test, y_pred) print(f"{name}: {acc:.4f}")

输出示例:

逻辑回归: 1.0000 决策树: 1.0000 支持向量机: 1.0000 K近邻: 1.0000

在鸢尾花数据集上,几个常见模型都能取得接近或达到 100% 的准确率,这是数据集本身特征可分性强的表现。不要因为得分高就认为所有任务都这么简单,真实项目的准确率通常远低于这个水平。

4.3 关键参数为什么这样设置

上面代码中有几个参数值得单独说明:

  • LogisticRegression(max_iter=1000):逻辑回归底层用迭代算法求解,默认最大迭代次数是 100。鸢尾花数据量小,默认值通常够用,但某些特征尺度较大的场景会超出迭代上限导致警告,因此显式设置为 1000 更稳妥。
  • DecisionTreeClassifier(random_state=42):决策树训练时存在随机性,固定随机种子可以保证复现结果。
  • SVC(gamma="scale"):SVC 的gamma参数控制单个训练样本的影响范围。"scale"是自动根据特征数量调整的默认值,适合大多数场景。如果数据没有标准化,手动设置过大的gamma容易过拟合。

4.4 数据标准化对模型的影响

KNN 和 SVM 这类基于距离的算法对特征量纲非常敏感。鸢尾花数据中,花萼长度在 4 到 8 厘米之间,花瓣宽度在 0.1 到 2.5 厘米之间,数值范围差异不大,所以不标准化也能得到不错的结果。但实际项目中特征量纲可能相差巨大,比如一个特征是年龄(0 到 100),另一个特征是月收入(几千到几万),后者会主导距离计算。

因此,实际项目中使用 KNN 和 SVM 前,通常先做标准化:

from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline model_svm = make_pipeline( StandardScaler(), SVC(gamma="scale") ) model_svm.fit(X_train, y_train) print("标准化后 SVM 准确率:", model_svm.score(X_test, y_test))

make_pipeline会把标准化器和分类器串联成一个整体:先对数据做标准化,再送入分类器。这比手动分开做要安全,因为fit时只使用训练集的均值和标准差,避免了用测试集信息“偷看”数据导致评估失真。

5. 模型评估:不能只看准确率

5.1 为什么准确率不够

准确率 = 预测正确的样本数 / 总样本数。它是最直观的指标,但在类别不均衡时容易误导。例如 95% 的样本属于类别 A,模型只要全部预测为 A,准确率就有 95%,但对类别 B 完全没有识别能力。

鸢尾花数据集三个类别各占三分之一,比较均衡,准确率是基本可信的。但入门阶段就要养成看完整评估指标的习惯,否则后面处理真实业务数据时会踩坑。

5.2 混淆矩阵和分类报告

使用 Scikit-Learn 输出更详细的评估结果:

from sklearn.metrics import classification_report, confusion_matrix print(classification_report(y_test, y_pred, target_names=data.target_names))

输出示例:

precision recall f1-score support setosa 1.00 1.00 1.00 10 versicolor 1.00 1.00 1.00 10 virginica 1.00 1.00 1.00 10 accuracy 1.00 30 macro avg 1.00 1.00 1.00 30 weighted avg 1.00 1.00 1.00 30

四个指标的含义:

  • 精确率(Precision):预测为该类别的样本中,真正属于该类别的比例。衡量“模型说对的时候有多靠谱”。
  • 召回率(Recall):真实属于该类别的样本中,被模型正确找出来的比例。衡量“模型有没有漏掉”。
  • F1 分数:精确率和召回率的调和平均,用于综合衡量两者。
  • Support:该类别的真实样本数量。

再看混淆矩阵:

cm = confusion_matrix(y_test, y_pred) print(cm)

输出示例:

[[10 0 0] [ 0 10 0] [ 0 0 10]]

矩阵的行代表真实类别,列代表预测类别。对角线上的数字是正确分类的数量,非对角线上的数字是混分类别的数量。

5.3 使用交叉验证获得更稳定的评估结果

单次划分训练集和测试集存在随机性。为了更稳定地评估模型效果,可以使用交叉验证。它的基本思路是把数据分成 K 份,每次用 K-1 份训练、1 份验证,轮流 K 次,最后取平均。

from sklearn.model_selection import cross_val_score scores = cross_val_score(KNeighborsClassifier(n_neighbors=3), data.data, data.target, cv=5) print("交叉验证得分:", scores) print("平均得分:", scores.mean())

输出示例:

交叉验证得分: [0.96666667 1. 0.93333333 0.96666667 1. ] 平均得分: 0.9733333333333334

使用cross_val_score时不需要手动划分训练集和测试集,函数会自行切割数据。cv=5表示 5 折交叉验证,每折数据量为 30 条,因此每次验证的得分波动比单次划分更接近真实水平。

6. 用训练好的模型预测新样本

模型通过评估后,最终要用于预测未见过的数据。下面模拟三条新样本,分别来自三个不同类别:

import numpy as np new_samples = np.array([ [5.1, 3.5, 1.4, 0.2], # 接近 setosa [6.0, 3.0, 4.8, 1.8], # 接近 versicolor [6.5, 3.0, 5.5, 2.0], # 接近 virginica ]) knn = KNeighborsClassifier(n_neighbors=3) knn.fit(X_train, y_train) predictions = knn.predict(new_samples) print("样本预测类别标签:", predictions) print("样本预测类别名称:", data.target_names[predictions])

输出示例:

样本预测类别标签: [0 1 2] 样本预测类别名称: ['setosa' 'versicolor' 'virginica']

这里要注意一个细节:预测新样本时,新样本的特征顺序必须和训练数据一致。训练时第 1 列是花萼长度,第 2 列是花萼宽度,第 3 列是花瓣长度,第 4 列是花瓣宽度,新样本也必须是这个顺序,否则输入的特征含义已经错位,模型输出的结果没有意义。

如果要查看模型对新样本的置信度,可以使用predict_proba

proba = knn.predict_proba(new_samples) print(proba)

输出展示每个样本属于三个类别的概率,例如[0.33333333 0.66666667 0. ]表示模型认为该样本有 1/3 概率属于类别 0,2/3 概率属于类别 1。

7. 常见问题、排查路径和环境差异

7.1 高频报错与解决方案

下面是入门阶段最常见的几类问题,按排查优先级排列:

问题现象常见原因检查方式解决建议
ModuleNotFoundError: No module named 'sklearn'库未安装,或安装到了别的 Python 环境pip list看是否有 scikit-learn,python -c "import sys; print(sys.executable)"看当前解释器路径激活正确的虚拟环境后重新安装
pip install很慢或超时网络不稳定,默认源访问慢观察下载速度使用清华、阿里云等镜像源
DataConversionWarning警告传入的X类型是列表或 DataFrame,而 sklearn 期望二维数组检查type(X)使用np.array(X)或确保 DataFrame 只包含数值列
逻辑回归提示ConvergenceWarning迭代次数不足或数据未标准化看警告文本增大max_iter,或先StandardScaler标准化
结果不可复现未设置random_state再次运行看结果是否变化在数据划分和模型初始化时固定random_state
ValueError: Expected 2D array, got 1D array insteadpredict传入了单条一维数组打印new_sample.shapenew_sample.reshape(1, -1)或写成二维数组

其中最后一条最常出现在“用模型预测单个新样本”的场景。predict方法要求输入是二维数组,因为一行是一个样本。单条样本要转为二维:

single_sample = np.array([[5.1, 3.5, 1.4, 0.2]]) print(knn.predict(single_sample))

7.2 排查顺序建议

遇到问题时,不要直接改代码,先按下面这条链路逐步排查:

  1. 确认当前 Python 解释器和 pip 指向同一环境。
  2. 确认import sklearn不会报错,版本号存在。
  3. 确认数据加载成功,X_train.shape是二维形状。
  4. 确认fit时特征矩阵不是空数组,也没有 NaN。
  5. 确认predict的输入特征数量和顺序与训练时一致。
  6. 查看完整报错堆栈,而不是只看最后一行异常。
  7. 如果跑通但结果异常,检查数据标准化、类别比例和评估指标选择。

7.3 学习环境与生产环境的差异

本项目在本地跑通属于学习环境。学习环境的重点是快速验证流程,因此可以直接使用 sklearn 内置数据集、单一脚本文件、默认参数和本地内存数据。

生产环境的机器学习项目需要额外考虑以下事项:

维度学习环境生产环境
数据来源内置数据集数据库、数据仓库、实时数据流
数据规模150 条百万级甚至更大
数据质量已清洗需要处理缺失值、异常值、重复值
特征处理可省略需要标准化、编码、特征工程
模型保存每次训练需要joblibpickle持久化
部署本地运行需提供 API 服务或批处理任务
监控不需要需要监控模型效果和数据漂移
权限与安全不考虑需要访问控制、日志审计、数据脱敏
回滚不需要需要模型版本管理和灰度发布

模型保存的示例代码:

import joblib joblib.dump(knn, "flower_knn_model.pkl") # 加载模型 loaded_model = joblib.load("flower_knn_model.pkl") print(loaded_model.predict(new_samples))

当流程从实验阶段转向正式服务时,模型文件、训练代码、数据版本和依赖版本都要纳入版本管理,否则会面临“模型能用但谁也不知道它怎么训练出来”的问题。

8. 实践建议:从跑通结果到真正理解模型

8.1 作为初学者,建议按这个顺序扩展练习

完成上面的基础流程后,不要急着学更多算法,先做几个针对性练习:

  1. 修改n_neighbors的值为 1、5、7、15,记录准确率变化,理解 K 值对模型复杂度和泛化能力的影响。
  2. 去掉stratify参数,手动查看训练集和测试集的类别比例,理解分层抽样的作用。
  3. 给决策树设置max_depth=2,输出决策树可视化结果,观察模型到底在用哪些特征做判断。
  4. 手动计算混淆矩阵中的精确率和召回率,与classification_report的输出对比,确认自己理解指标含义。
  5. random_state改为不同值,观察得分波动,理解为什么交叉验证比单次划分更可靠。

8.2 三个容易忽视却影响深远的习惯

第一个习惯:固定随机种子。

机器学习中存在大量随机操作,包括数据打乱、模型初始化。不定random_state,两次运行结果可能不同,这会干扰你对模型效果的判断。建议在train_test_split、决策树、随机森林等涉及随机的环节统一设置。

第二个习惯:不要用测试集反复调参。

如果反复在同一个测试集上调整参数,直到准确率满意为止,测试集实际上变成了训练数据的一部分,最终评估结果会偏乐观。正确做法是使用训练集做交叉验证来选参数,测试集只用来做最终评估。

第三个习惯:保存代码时连同库版本一起记录。

Scikit-Learn 的接口在不同版本之间可能存在调整,模型文件也可能有版本兼容问题。建议在项目根目录生成依赖清单:

pip freeze > requirements.txt

后续换机器或重新部署时,使用pip install -r requirements.txt恢复环境。

8.3 接下来可以往哪个方向深入

花朵分类跑通之后,可以在同一个逻辑上扩展不同方向:

  • 换更大的公开数据集,体验数据量增加后训练时间和模型效果的差异。
  • 加入数据预处理流程,处理缺失值和类别特征编码,理解标准化、独热编码的作用。
  • GridSearchCV做参数搜索,把“人工试参数”变成“用代码搜索较优参数”。
  • 使用joblib保存模型并封装成简单的 Web API,体验从训练到部署的完整链路。
  • 对比线性模型和树模型在不同任务上的表现差异,为后续学习具体算法建立直觉。

这个入门项目最重要的价值不是 100% 的准确率,而是帮助你建立一套标准化的机器学习实验流程。以后无论面对什么数据集,都可以套用“加载数据、划分样本、训练模型、评估结果、预测新数据”这条主线,再根据问题类型选择新的模型和评估指标。

建议把本文的代码保存为一个完整脚本,逐段添加注释,并针对每个模型记录实验参数和评估结果。这样积累下来,你得到的不仅是一段能运行的代码,而是一套可以复用到后续学习项目的学习笔记。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 11:42:02

三阶连续时间Delta-Sigma ADC的Matlab行为级仿真与性能分析

简介:这是一套3阶连续时间Delta-Sigma ADC的Matlab/Simulink仿真代码,适合正在学习过采样数据转换器、或从事模拟前端与信号处理算法验证的工程师和学生。资源共10个文件,包含3个Matlab脚本(用于主仿真、FFT频域计算和动态测试&am…

作者头像 李华
网站建设 2026/9/7 11:41:34

STM32F103驱动SPI屏幕移植LVGL:从底层显示驱动到应用层配置全解析

简介:面向STM32F103嵌入式开发者,一份自写可用的LVGL移植程序完整演示了通过SPI接口驱动显示屏并移植图形库的工程化流程,适用于课程设计、产品原型开发或GUI学习实践。压缩包共761个文件,整体仅3.77MB,以C/H源码为主体…

作者头像 李华
网站建设 2026/9/7 11:41:11

跑山智驾零接管背后:智驾系统如何应对复杂山路挑战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 11:39:39

飞书多维表格深度解析:从在线表格到轻量数据管理平台

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 11:38:53

AI Slop治理实战:从提示词到流程,彻底告别低质AI内容

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 11:38:40

从补全到智能体:Codex五年进化路线与2025实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华