Data Preprocessing
数据预处理会将原始表格、文本和图像转换为模型所需的干净、经过缩放且格式固定的输入。介绍常见步骤、防止数据泄漏的规则和代码。
数据预处理是指将原始数据转换为干净、格式一致的数值输入的一系列变换,机器学习 (ML)模型需要这种输入;训练和推理时都必须采用相同的处理方式。对于表格数据,预处理包括处理缺失值、将类别编码为数字,以及缩放特征。对于图像,预处理包括解码文件、调整图像大小以匹配网络输入尺寸、排列颜色通道,以及缩放像素值。预处理实际上是模型的一部分:如果一个网络使用缩放到 0–1 的 RGB 图像进行训练,那么当它收到取值范围为 0 到 255 的 BGR 图像时,就会产生不可靠的预测,即使其权重没有变化。
常见预处理步骤#
- 数据清理:删除重复项、损坏文件和不可能的值,并修正不一致的标签或单位。
- 插补:使用列中位数等统计值、基于模型的估计值或单独的“缺失”类别填补缺失值,或者删除缺失过多的行。
- 编码:将类别转换为数字:无序类别(例如颜色)使用独热编码,有序类别(例如尺寸)使用有序代码,类别名称则使用整数索引。
- 缩放与归一化:最小-最大缩放将特征映射到 0–1 范围;标准化则减去均值并除以标准差。这样,在基于距离的模型中,以毫米计量的特征就不会压过以米计量的特征,梯度下降也会更快收敛。torchvision中的许多 ImageNet 预训练模型都要求对每个 RGB 通道进行标准化,使用的均值分别为 0.485、0.456 和 0.406,标准差分别为 0.229、0.224 和 0.225。
- 调整大小:通过拉伸、中心裁剪或信箱填充,将所有图像调整为网络输入尺寸。信箱填充会缩放图像以适应尺寸,并对剩余区域进行填充,从而保持对象的比例。
- 分词:将文本拆分为词元,并将每个词元映射到固定词表中的整数 ID。
此外还有特定领域的处理步骤。CT 扫描需要进行窗宽窗位处理,将一定范围的亨氏单位(例如显示肺组织的范围)映射为模型看到的显示强度;音频则需要重采样到固定频率,通常还要转换为频谱图。
在训练数据上拟合,应用于所有数据#
预处理中使用的统计信息,例如特征均值和标准差、插补值以及类别词表,只能根据训练集划分计算;计算结果应保存下来,并在验证、测试和生产数据上保持不变地应用。如果在划分数据前使用整个数据集计算这些统计信息,测试集中的信息就会影响训练输入,这是一种数据泄漏,会使评估分数虚高。scikit-learn Pipeline会强制遵守这一规则:它只会在传入 fit 的数据中拟合预处理步骤。在服务时采用不同的预处理方式,例如不同的缩放方法或图像解码器,会造成训练-服务偏差,使模型离线表现良好、生产环境表现变差。
视觉模型的图像预处理#
图像模型需要固定的张量布局。OpenCV 会将图像解码为高度 × 宽度 × 通道的 8 位整数数组,通道顺序为 BGR;而 PyTorch网络需要通道优先、RGB 顺序的浮点张量批次。Ultralytics 中的 YOLO26预测会自动执行这些步骤:将每张图像缩放到较长边匹配 imgsz(默认值为 640 像素),且不扭曲图像;对于固定形状的导出模型,用值为 114 的灰色像素填充至 640 × 640 正方形;对于 PyTorch 模型,则仅填充到 32 像素步幅的下一个倍数;随后将 BGR 转换为 RGB、将数组重排为通道优先布局,并除以 255。下面的 OpenCV 和 NumPy 脚本会手动执行正方形信箱填充:
import urllib.request
import cv2
import numpy as np
# Download a sample photo and read it as a BGR uint8 array of shape (height, width, 3)
urllib.request.urlretrieve("https://ultralytics.com/images/bus.jpg", "bus.jpg")
image = cv2.imread("bus.jpg")
# Resize so the longer side is 640 pixels, keeping the aspect ratio
scale = 640 / max(image.shape[:2])
resized = cv2.resize(image, None, fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)
# Letterbox: center the resized image on a 640x640 canvas of gray (114) padding
canvas = np.full((640, 640, 3), 114, dtype=np.uint8)
top, left = (640 - resized.shape[0]) // 2, (640 - resized.shape[1]) // 2
canvas[top : top + resized.shape[0], left : left + resized.shape[1]] = resized
# Reorder BGR to RGB and HWC to CHW, scale pixels to 0-1, and add a batch dimension
tensor = canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0
batch = tensor[None]
print(f"{image.shape} uint8 -> {batch.shape} float32 in [{batch.min():.1f}, {batch.max():.1f}]")该脚本会打印 (1080, 810, 3) uint8 -> (1, 3, 640, 640) float32 in [0.0, 1.0]:1080 × 810 的照片会变成单图像批次,布局和值范围符合 PyTorch 检测器的要求,两侧各用 80 像素的灰色条带填充。










