做AI大模型应用开发,绕不开一件事:喂给模型的数据,得先变成模型能理解的样子;模型吐出来的结果,也得能转成我们能分析的东西。这个过程中,Numpy、Pandas、Matplotlib就是最趁手的三件基础工具。这篇内容不是讲枯燥的API文档,而是结合我实际做数据处理时的场景,聊聊这三个库在大模型开发里到底怎么用、为什么这么用、踩过哪些坑,以及如何用它们搭建一套从数据清洗到结果可视化的完整工作流。适合刚入门大模型开发、又对数据处理不太熟的朋友参考,也欢迎老手一起交流。
1. 为什么大模型开发绕不开这三个数据工具
1.1 数据工具在大模型项目中的位置
很多人一提到大模型应用开发,第一反应是调API、写Prompt、搭Agent,觉得数据处理是传统机器学习的事。但实际上,大模型项目里的数据工作比想象中多得多。以微调一个开源大模型为例,前期的数据收集、清洗、格式转换、样本筛选,直接决定了最终的效果上限:垃圾数据进去,再强的模型也白搭。即使只是做RAG应用,文档切分后也要做向量化前的结构整理,Embedding结果要批量计算相似度,检索结果要排序过滤,这些全是数组和表格的活儿。
我一般把数据工作理解成四个环节:数据获取、数据清洗、数据运算、数据可视化。Numpy负责数据运算中最核心的数值计算部分,比如向量、矩阵、批量数学操作;Pandas负责把各种格式的数据统一整理成表格结构,清洗、筛选、分组、聚合一条龙;Matplotlib负责把最终的分析结果用折线图、柱状图画出来,直观判断效果。三者配合起来,基本覆盖了从原始数据到可视化结论的全部链路。
1.2 三件套的分工逻辑与选型思考
这三个库各有侧重,也各有不可替代的地方。Numpy的核心是ndarray,也就是多维数组。它比Python原生列表强在哪儿?向量化运算。同样的批量加法、乘法,用列表写for循环可能慢上几十倍,而Numpy直接用C语言级别的底层实现,一行代码搞定。在大模型场景里,Embedding向量的批量相似度计算、数据归一化、矩阵乘法等需求,没有Numpy几乎寸步难行。
Pandas的价值则在于“结构化”。它把数据抽象成DataFrame,类似Excel表格,但是带索引、支持复杂筛选和聚合。实际开发中,API返回的JSON、爬下来的CSV、日志文件等格式五花八门,Pandas可以把它们统一读进来,做完清洗和转换,再输出成模型训练需要的格式。尤其是做指令微调数据时,经常要把问题、回答、上下文等字段整理成统一的JSONL格式,Pandas的操作效率非常高。
Matplotlib则是“最后一公里”。很多时候,模型评估结果是一堆数字,光看数字很难发现问题。把Token消耗、Loss下降、各类指标对比画成图,一眼就能找到异常点。比如训练Loss出现震荡、某个类别准确率明显偏低,通过可视化能快速定位问题。
从选型角度看,这三个库是大模型开发的第一层基石。这套组合的好处是生态成熟、资料多、代码随便一搜都是一大片案例,遇到问题不愁没人踩过。而且后续如果需要更高级的功能,比如交互式图表Plotly、深度学习框架PyTorch,它们的基本数据结构和Numpy、Pandas是打通的,学习成本能平滑过渡。
2. 环境搭建与安装避坑指南
2.1 Python环境与虚拟环境准备
工欲善其事,必先利其器。我建议先用虚拟环境隔离项目依赖,强烈不建议直接往系统Python里装一堆包。因为大模型开发经常会涉及PyTorch、Transformers等框架,它们对Numpy版本有严格限制,装混了很容易出现“版本不匹配”的怪病。
推荐用conda或者venv创建独立环境。以conda为例:
conda create -n ai-data python=3.10 conda activate ai-dataPython版本我一般选3.9到3.11之间,太老的版本对部分库兼容性差,太新的版本偶尔会遇到个别依赖还没跟上。实测下来Python 3.10是当前最稳妥的选择,绝大多数深度学习相关库都兼容。
2.2 安装三件套的正确姿势
环境激活后,安装三个库非常简单:
pip install numpy pandas matplotlib如果只需要安装某一个,比如Numpy:
pip install numpy这里需要注意,pandas和matplotlib依赖numpy,安装pandas时会自动拉取匹配的numpy版本,无需单独操心。但如果你之前装过某个特殊版本的numpy,再装pandas时可能出现依赖冲突,报错信息里会明确提示,这时候最简单的办法是让pip自己统一解决版本关系:
pip install --upgrade numpy pandas matplotlib不要硬保留旧版本。类似的还有PyTorch和Numpy之间的版本配合问题:如果先装了某个特定numpy,再装torch时,pip会尝试调整numpy来匹配。如果调不动,就得手动指定兼容版本。常规做法是先装torch,再按它的要求装numpy等依赖。
2.3 国内镜像源与清华源报错解析
安装时最常遇到的就是网络超时或者找不到包。比如报错:
ERROR: Could not find a version that satisfies the requirement pandas (from versions: none) ERROR: No matching distribution found for pandas这个报错通常有三层原因。第一层是网络问题,默认的PyPI源在部分网络环境下极其不稳定,安装包下载一半就超时;第二层是Python版本与pandas版本不兼容,pip找不到能匹配的版本;第三层是本地pip版本太旧,解析能力弱。
解决办法就是用国内镜像源。我自己一直用清华源,配置一次全局生效:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple之后所有pip安装都会走清华源,速度和稳定性都大幅提升。如果只想临时用一次,也可以不修改全局配置:
pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple镜像源也不是永远不出问题。偶尔清华源的同步会滞后,导致最新版本的包暂时找不到。这时候换阿里源或者中科大源试试:
pip install pandas -i https://mirrors.aliyun.com/pypi/simple/另外,如果pip版本太老,可以先升级pip再安装:
pip install --upgrade pip这套流程我反复用了很多次,基本上能把九成以上的安装问题解决掉。
2.4 版本管理与验证
安装完之后,进Python环境验证一下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt print(np.__version__) print(pd.__version__) print(matplotlib.__version__)如果导入没有报错,就说明基础环境没问题了。这里提醒一个细节:pandas和numpy的版本不要追求“最新”,而是要追求“稳定兼容”。实际项目中,如果pandas版本突然跳到2.x,部分旧代码可能有行为变化。锁定版本可以这样:
pip install numpy==1.26.4 pandas==2.2.2 matplotlib==3.8.4这个组合是我近期实测比较稳的搭配,当然也要根据自己项目依赖(特别是torch版本)灵活调整。
3. Numpy实战:用数组计算能力支撑模型数据处理
3.1 从列表到数组:ndarray与dtype
Numpy的核心是ndarray对象。创建一个数组非常简单:
import numpy as np data = np.array([1, 2, 3, 4, 5]) print(data) print(data.shape) print(data.dtype)输出结果类似:
[1 2 3 4 5] (5,) int64这里的关键区别在于dtype。原生列表里的元素类型是灵活的,而ndarray要求所有元素类型一致,好处是计算时内存连续、效率极高。如果你希望数组里存浮点数:
data_float = np.array([1, 2, 3], dtype=np.float32)在大模型开发中,dtype直接影响到内存占用。比如一个1万条、每条768维的Embedding向量矩阵,用float64存储需要约122MB,转成float32只需要约61MB。批量处理时这个差距非常明显。
3.2 向量化运算与广播机制
Numpy的精髓在于向量化运算。举个例子,要给所有Embedding向量做归一化,如果没学过Numpy,可能会写成嵌套循环:
for i in range(len(vectors)): for j in range(len(vectors[i])): vectors[i][j] = vectors[i][j] / np.linalg.norm(vectors[i])用Numpy的话,几行搞定:
norms = np.linalg.norm(vectors, axis=1, keepdims=True) vectors_norm = vectors / norms这背后的逻辑是广播机制(Broadcasting),简单理解就是维度不匹配时,Numpy会自动把小维度的数组扩展到能运算的形状。生活化类比:你把一个杯子里的水倒进一排杯子,不需要一个个倒,提起壶沿着一排杯子均匀倒就行。Numpy的广播就是“自动倒水”。
广播机制虽然方便,但也很容易踩坑。最常见的错误是数组维度不匹配。比如A形状是(25, 768),B形状是(768,),直接用A + B没问题,广播会沿第一维展开;但如果B形状是(1, 768)和(768, 1)混用,就得出完全不同的结果。我自己的习惯是:运算前用assert a.shape == b.shape或者先reshape统一维度,避免“想当然”的运算错误。
3.3 大模型数据场景的典型应用
Numpy在大模型开发里的主要场景有几个。第一个就是Embedding相似度计算。比如做RAG时,把用户查询向量和文档向量矩阵做批量余弦相似度:
def cosine_similarity(query_vec, doc_matrix): query_norm = query_vec / np.linalg.norm(query_vec) doc_norms = np.linalg.norm(doc_matrix, axis=1, keepdims=True) doc_normed = doc_matrix / doc_norms return np.dot(doc_normed, query_norm)这段代码返回每个文档与查询的相似度,排序后取top-k就是检索结果。整个过程没有写一个for循环,数据量上万条也能秒级完成。
第二个场景是数据统计分析。比如分析Prompt中Token长度分布,可以把所有样本的Token数量放到一个Numpy数组里,直接计算均值、分位数、标准差:
token_counts = np.array([120, 230, 180, 90, 300, 150]) print(token_counts.mean()) print(np.percentile(token_counts, [25, 50, 75]))这些统计量在决定上下文窗口设置、判断数据是否存在过长样本时非常有用。
第三个场景是批量数据增广。比如对向量做随机扰动、拼接、截断等操作,Numpy的随机模块提供了丰富的API:
noise = np.random.normal(0, 0.01, size=vectors.shape) vectors_aug = vectors + noise这个操作在样本数量不足时很有用,但要注意扰动幅度不能太大,否则会改变语义。
3.4 索引、切片与聚合统计的常用套路
Numpy的索引和切片是处理矩形批数据的利器。比如你有一个训练集向量矩阵,形状是(N, 768),其中第0列是样本ID,1~767列是向量内容。切出向量部分:
vec_part = data[:, 1:]随机抽取一批样本:
sample_idx = np.random.choice(len(data), size=32, replace=False) sample_data = data[sample_idx]聚合统计也很常用,比如按行求均值、按列求max:
row_mean = data.mean(axis=1) col_max = data.max(axis=0)这里的axis参数很多人第一次用时容易蒙。我的记忆习惯是:axis=1表示把每一行的元素拉通统计,axis=0表示把每一列的元素拉通统计。比如一个类似Excel表格的数组,axis=0就是竖着看,axis=1就是横着看。
4. Pandas实战:把零散数据变成可用的训练数据
4.1 DataFrame与Series:用表格思维整理数据
Pandas最核心的两个数据结构是Series和DataFrame,理解起来非常简单:Series是一列数据,DataFrame是多列数据组成的表格。实际开发中,我大多数时间都在和DataFrame打交道。
创建一个DataFrame非常灵活:
import pandas as pd data = { "query": ["什么是大模型", "如何微调模型", "RAG是什么"], "answer": ["大模型是以海量参数...", "微调是指...", "RAG是检索增强生成..."], "token_count": [25, 20, 10] } df = pd.DataFrame(data) print(df.head())输出如下:
query answer token_count 0 什么是大模型 大模型是以海量参数... 25 1 如何微调模型 微调是指... 20 2 RAG是什么 RAG是检索增强生成... 10这个结构天然适合做指令微调数据集。每个样本是“问题-回答-元信息”的结构,修改、筛选、导出都非常方便。
4.2 数据读取:CSV、JSON与API返回结果的整理
大模型开发的数据来源通常是CSV、JSON、JSONL或者API返回。Pandas读取这些格式都很方便。
读取CSV:
df = pd.read_csv("samples.csv")读取JSON:
df = pd.read_json("samples.json")读取JSONL,也就是每行一个JSON对象的数据集:
df = pd.read_json("samples.jsonl", lines=True)比较特殊的情况是API返回的结构化数据。比如调用一个模型接口,返回数据是嵌套的JSON,比如列表里套字典、字典里套列表。可以先用json库把数据转成Python对象,再用pd.json_normalize把它展平成表格:
import json import pandas as pd with open("api_result.json", "r", encoding="utf-8") as f: result = json.load(f) df = pd.json_normalize(result)这是一个非常常用但很多人不知道的技巧。模型返回结构复杂时,用json_normalize能省下大量手写解析逻辑的时间。
4.3 数据清洗与类型转换
数据清洗是Pandas用得最多的场景,也是热搜词里常提到的“pandas 数据类型转换”。拿到原始数据后,第一件事是看结构:
print(df.info()) print(df.describe()) print(df.isnull().sum())info()会列出每列的非空数量、数据类型、内存占用;isnull().sum()会输出每列的缺失值数量,非常直观。
最常见的类型转换场景是:某列明明是数值,但加载进来却是字符串,导致无法计算。比如一个“回复耗时”列的原始数据里混入了“0.5s”这样的带单位文本,Pandas会把它读成object类型。处理办法:
df["latency"] = df["latency"].str.replace("s", "", regex=False) df["latency"] = pd.to_numeric(df["latency"], errors="coerce")pd.to_numeric是数据类型转换的一个关键API,errors="coerce"表示无法转换的数值变成NaN,方便后续统一处理。注意这里的str.replace会原样替换字符串里的"s",操作前最好确认数据的格式统一。
另一个常用场景是批量把某个字段从字符串转为datetime:
df["timestamp"] = pd.to_datetime(df["timestamp"])转成datetime后,就能按时间做排序、筛选、聚合,比如统计每小时调用量。
4.4 结构化转换与批量构建Prompt
Pandas不仅能清洗数据,还能批量生成和转换结构化训练数据。比如你有用户问题和标准答案两个字段,需要生成一个带Prompt模板的新列:
def build_prompt(row): return f"用户提问:{row['query']}\n请给出专业、简洁的回答。" df["prompt"] = df.apply(build_prompt, axis=1)apply配合axis=1表示逐行处理,这是Pandas里扩展性非常强的功能。如果处理逻辑简单,也可以用列表推导式:
df["prompt"] = [f"用户提问:{q}\n请给出专业、简洁的回答。" for q in df["query"]]后者通常更快,但可读性略差。数据量大时我会优先用列表推导式,处理几万行数据也不会有明显卡顿。
合并多个数据源也是常见操作。比如把向量文件和原始文本文件按ID合并:
df_text = pd.read_csv("texts.csv") df_vec = pd.read_csv("vectors.csv") df_merged = pd.merge(df_text, df_vec, on="sample_id", how="left")how="left"表示以左边的表为主,右边没有匹配到的数据补NaN,这个语义在多表合并时非常常用。
导出的格式也很关键。生成微调数据集时,经常要导出为JSONL格式:
df.to_json("train.jsonl", orient="records", lines=True, force_ascii=False)force_ascii=False用来保留中文字符,否则全都会被转成\uXXXX的Unicode转义序列,虽然读取没问题,但查数据时极不友好。
5. Matplotlib实战:把模型结果画成看得懂的图
5.1 折线图:训练Loss曲线的绘制
模型训练过程中,最关心的就是Loss下降是否正常。用Matplotlib画一张Loss曲线,信息一目了然。
import matplotlib.pyplot as plt epochs = [1, 2, 3, 4, 5] train_loss = [1.2, 0.8, 0.6, 0.45, 0.32] val_loss = [1.3, 0.95, 0.75, 0.6, 0.5] plt.plot(epochs, train_loss, label="train_loss") plt.plot(epochs, val_loss, label="val_loss") plt.xlabel("Epoch") plt.ylabel("Loss") plt.legend() plt.grid(True) plt.show()这里的核心逻辑是plt.plot(x, y),x轴对应训练轮数,y轴对应该轮的Loss值。加label之后,plt.legend()会自动生成图例。加上plt.grid(True)后,网格线能帮助更精细地对比数值变化,我一般都会开。
训练过程中另一个常见的需求是在每个Epoch结束后往列表里追加Loss值,最后一并画图:
train_losses = [] val_losses = [] for epoch in range(1, 6): # 假设train_one_epoch返回本轮训练loss t_loss = train_one_epoch() v_loss = evaluate() train_losses.append(t_loss) val_losses.append(v_loss) plt.plot(range(1, len(train_losses)+1), train_losses, marker="o") plt.plot(range(1, len(val_losses)+1), val_losses, marker="s")加marker参数是为了让数据点更明显,尤其在样本点较少时非常有效。
5.2 柱状图:模型效果对比
柱状图最常见的使用场景是多个模型或多项指标对比。比如三个模型在准确率、召回率、F1上的对比:
import numpy as np models = ["ModelA", "ModelB", "ModelC"] accuracy = [0.87, 0.91, 0.89] recall = [0.82, 0.88, 0.90] f1 = [0.84, 0.89, 0.89] x = np.arange(len(models)) width = 0.25 plt.bar(x - width, accuracy, width, label="Accuracy") plt.bar(x, recall, width, label="Recall") plt.bar(x + width, f1, width, label="F1") plt.xticks(x, models) plt.legend() plt.show()柱状图的细节在于x - width、x、x + width的偏移。这样三根柱子才不会重叠在一起,每组柱子之间也能留出自然间距。用np.arange(len(models))生成x轴位置,是这类分组柱状图的标准写法。
如果需要在柱子上显示具体数值,可以配合plt.text实现。比如:
for xi, val in zip(x, accuracy): plt.text(xi, val, f"{val:.2f}", ha="center", va="bottom", fontsize=9)这里ha="center"控制水平居中,va="bottom"让文字位于柱子顶部偏上一点。对于需要写报告的场景,这个细节很加分。
5.3 样式调整与颜色配置
Matplotlib默认的样式比较朴素,而且不同版本之间的默认色系还有差别。好在我们能手动控制颜色。颜色参数可以写成多种格式:
plt.plot(x, y1, color="red") plt.plot(x, y2, color="#3498db") plt.plot(x, y3, color=(0.2, 0.6, 0.8))第一种是颜色名称,第二种是十六进制HEX值,第三种是RGB元组。实际画图时我喜欢从项目的品牌色或者专业配色站点取色,统一指定。比如用一套蓝、橙、绿的配色,对比时非常清晰。
如果需要画出多条颜色差异大、又兼顾色盲友好的曲线,可以用Matplotlib自带的colormap:
cmap = plt.cm.viridis colors = [cmap(i / 9) for i in range(10)] for i in range(10): plt.plot(x, data[i], color=colors[i], label=f"line_{i}")viridis、plasma、cool这些colormap都有很好的视觉区分度,比手动硬凑十种颜色靠谱多了。另有一个常用细节是plt.tight_layout(),它能让图例、坐标轴标签等元素自动调整布局,避免图片边缘被截断。我在出图最后一刻总会加上这一行。
5.4 中文字体处理与图片、动画导出
Matplotlib默认不支持中文,标签里出现中文时,会显示成一个个方框。处理方法非常简单,在画图前指定中文字体:
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "PingFang SC"] plt.rcParams["axes.unicode_minus"] = False第一行指定可用的中文字体,第二行解决负号显示异常的问题。我长期在Windows和macOS之间切换,配置里一般会同时写上多个字体候选,代码拿到任何机器上都能显示中文。如果系统里没有SimHei或微软雅黑,Linux下可以装Noto Sans CJK SC并同样把名字加进候选列表。
导出图片用plt.savefig:
plt.savefig("loss_curve.png", dpi=150, bbox_inches="tight")dpi=150可以保证图片在文档里放大后依然清晰。bbox_inches="tight"会自动裁剪掉多余的留白,非常实用。如果画布尺寸不合适,还可以在画图前指定:
plt.figure(figsize=(8, 5))figsize的单位是英寸,我一般用(8, 5)或者(10, 6),比例接近黄金分割,视觉上比较舒服。
除了静态图片,有时候还想把训练过程录制成动画,比如持续追加新的Loss点、保存为GIF。Matplotlib配合matplotlib.animation.FuncAnimation可以做到:
from matplotlib.animation import FuncAnimation from IPython.display import HTML fig, ax = plt.subplots() def update(frame): ax.clear() ax.plot(epochs[:frame+1], train_loss[:frame+1], color="#3498db") ax.set_title(f"Epoch {frame+1}") return ax anim = FuncAnimation(fig, update, frames=len(train_loss), interval=500) anim.save("loss_animation.gif", writer="pillow", fps=2)生成GIF需要安装pillow库:
pip install pillow这种方式适合做训练过程的可视化演示,效果非常直观。但要注意帧数太多时GIF文件会很大,建议控制帧数或者降低fps。
6. 实践中的常见问题与排查实录
6.1 安装与版本类问题
我把踩过的坑整理成一张速查表,方便对照排查:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
pip报No matching distribution | 镜像源同步滞后、pip版本过旧 | 加--upgrade pip,换阿里源/中科大源 |
导入numpy报DLL load failed | numpy与Python版本不匹配 | 卸载重装对应Python版本的numpy |
pandas操作时提示Version mismatch | 某个依赖库如Numpy被手动改过版本 | pip install --upgrade numpy pandas统一版本 |
| torch与numpy冲突 | 两者版本要求不一致 | 先装torch,后按提示装指定numpy版本 |
特别提醒:如果项目里同时使用深度学习框架,安装顺序很关键。我自己是先把torch装好,再装numpy、pandas、matplotlib。因为torch在安装时会自动选择兼容的numpy版本,之后其他库再去适配这个numpy版本,冲突概率小很多。
6.2 pandas类型转换与读数据踩坑
数据处理时最让人头疼的三个坑,我逐个说。
第一个是CSV编码问题。读取CSV时如果包含中文,直接pd.read_csv经常报UnicodeDecodeError,因为默认编码不是UTF-8。解决办法:
df = pd.read_csv("file.csv", encoding="utf-8")如果还是报错,就尝试encoding="gbk"或者encoding="utf-8-sig"。utf-8-sig专门处理带BOM头部的UTF-8文件,Excel导出的CSV经常有这个头。
第二个是astype和pd.to_numeric的选择问题。很多人喜欢用astype直接转类型:
df["col"] = df["col"].astype(float)如果这一列里混入了一个非数值字符,会直接抛异常。而pd.to_numeric(error="coerce")则会把异常值变成NaN,后续可以用fillna、dropna处理,明显更稳健。我的习惯是:数据来源干净时才用astype,任何可能不干净的数据都用to_numeric。
第三个是JSONL数据的嵌套结构。读进来后经常发现某个字段是“列表套字典”,要展开才能分析。处理办法是用pd.json_normalize:
df_expanded = pd.json_normalize(df["raw_json"].apply(json.loads))或者用df.explode()把列表拆成多行。比如每条样本有多个标签,就能拆成一行一个标签,方便分组统计。
6.3 matplotlib中文乱码与图片清晰度
中文乱码的根因是matplotlib默认字体库不含中文字形。前面提过的配置:
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "PingFang SC"] plt.rcParams["axes.unicode_minus"] = False如果配置了还是不生效,可以打印出当前可用字体列表,看看候选名是否正确:
import matplotlib.font_manager as fm fonts = [f.name for f in fm.fontManager.ttflist] print("SimHei" in fonts)优先级上,SimHei(黑体)在绝大多数Windows环境都能用,macOS环境用PingFang SC,Linux下有人装了Noto Sans CJK SC也可以直接用。还有一个终极办法:直接用英文标签,避免字体问题,但在中文项目文档里观感差一些,不推荐首选。
图片清晰度方面,保存时dpi设置过低会导致图片放大后模糊。我一般输出给PPT或文档用的图都设150-200 dpi,输出给打印材料则用300 dpi。同时配合bbox_inches="tight"裁剪边缘,图片看起来更专业。
6.4 性能和内存相关的经验
数据量大的时候,性能和内存也是不能忽视的。有几个实操经验:
第一,尽量使用向量化运算代替循环。Pandas里能一行写清楚的聚合、筛选操作,尽量不要用自定义函数配合iterrows()逐行处理,两者性能差距是数量级的。
第二,读取大CSV时,可以先看看文件有多大,然后只读取需要的列:
df = pd.read_csv("large.csv", usecols=["query", "answer", "score"])usecols参数能显著减少内存占用,读取速度也快很多。
第三,对于超大语料,可以考虑用chunksize分块读取,边读边处理:
chunk_iter = pd.read_csv("huge.csv", chunksize=10000) for chunk in chunk_iter: process(chunk)第四,Numpy数组如果确定只读,可以主动设置setflags(write=False),在某些场景下能提升处理效率,但这个是锦上添花,最关键的还是避免不必要的数据拷贝。
6.5 数据一致性检查清单
最后分享一个我每次做完数据清洗都会过的检查清单,很笨但很有效:
- 用
df.info()检查每列数据类型是否符合预期,object类型过多的列重点关注 - 用
df.isnull().sum()确认缺失值情况,决定是删行还是填充 - 用
samples["token_count"].describe()检查数值分布,尤其看min和max,是否存在异常 - 用
df["query"].str.len()检查文本长度是否符合预期,尤其是0长度样本是否出现 - 用
df.drop_duplicates()删除重复样本,重复样本会让模型对某些模式过拟合 - 导出前用
head()抽查几条数据,确认内容没有错乱
这套流程下来,能过滤掉绝大部分“脏数据”。我有一次在微调模型前没有检查文本长度分布,结果里面混入了几个长度为1的极端样本,训练时Loss直接飙升。后来加了这一步检查,再也没出过类似问题。
最后再分享一个小技巧:在做数据清洗时,每完成一步转换,就顺手保存一份中间结果。比如清洗完存一份clean.csv,类型转换完再存一份typed.csv。这样即使后续步骤出错,也不需要从头开始。我见过太多人辛苦清洗了一半,结果一个误操作全部丢失,又得重来。这个过程本身也是在积累一套可复用的数据处理流水线,对大模型项目开发来说,比单纯写Prompt要实在得多。