第三章:数据采集与预处理

数据采集与预处理,说白了就是给削峰填谷算法「喂饭」。

饭喂得不好,算法再牛也白搭。我在项目中见过太多次了——数据没洗干净,模型跑出来全是噪音,最后还得回头查数据源。

这一章,咱们就把数据采集和预处理这摊事聊透。

3.1 数据源类型:你从哪儿搞数据?

削峰填谷算法需要的数据,通常来自三个地方:

  • 服务器指标:CPU、内存、磁盘IO、网络带宽。这些数据一般通过监控系统(比如Prometheus、Zabbix)采集。
  • 数据库记录:业务表里的订单量、用户活跃数、交易流水。直接从MySQL、PostgreSQL或者ClickHouse里拉。
  • 业务日志:Nginx访问日志、应用日志(比如Java的log4j)。日志里藏着用户行为模式,对预测峰值很有用。

我个人习惯,先把数据源分类画个图,这样心里有数。

数据源分类与采集路径 服务器指标 CPU/内存/IO/网络 数据库记录 订单/用户/交易流水 业务日志 访问日志/应用日志 Prometheus / Zabbix SQL / 数据同步工具 Filebeat / Fluentd 统一数据湖 / 消息队列
我的经验:别一上来就全量采集。先搞清楚你削峰填谷要预测什么指标,再决定数据源。比如你要预测数据库连接数,那服务器CPU和业务日志可能比数据库记录更直接。

3.2 数据清洗:脏数据必须处理

数据采集上来,第一件事不是分析,是清洗。

你想想看,服务器偶尔宕机、网络闪断、业务代码bug——这些都会产生脏数据。脏数据不处理,模型学到的全是错误模式。

常见的脏数据有:

  • 重复数据:同一时间戳采集了多条记录
  • 缺失字段:某条记录CPU使用率为null
  • 格式不一致:时间戳有的是"2024-01-01 12:00:00",有的是"2024/01/01 12:00"
  • 越界值:CPU使用率出现-5%或者150%

清洗策略我一般这么定:

脏数据类型 处理方法 适用场景
重复数据 去重(保留第一条或最后一条) 所有场景
缺失字段 前向填充 / 插值 / 删除 时间序列数据用前向填充
格式不一致 统一转换为标准格式 时间戳必须统一为Unix时间戳
越界值 截断或标记为异常 物理上限明确的指标用截断
注意:我曾经在一个项目中,因为没处理重复数据,导致削峰填谷模型把同一个峰值算了两次,结果扩容策略直接翻倍。嗯,那天的运维同学差点没骂死我。

3.3 异常值处理:别让离群点带偏了

异常值跟脏数据不太一样。脏数据是明显错误的,异常值可能是真实发生的极端情况。

比如双十一零点,订单量突然暴涨100倍——这不是脏数据,这是真实的业务峰值。削峰填谷算法恰恰要学习这种模式。

那怎么区分?我一般用两种方法:

  1. 3σ原则:数据服从正态分布时,超过均值±3倍标准差的值视为异常。适合CPU、内存这类相对稳定的指标。
  2. IQR(四分位距):超过Q1-1.5*IQR或Q3+1.5*IQR的值视为异常。适合订单量这类分布不均匀的指标。

处理方式上,我的建议是:

  • 如果是明显错误(比如CPU使用率-5%),直接删除或修正
  • 如果是真实极端值(比如双十一峰值),保留并打标签
  • 如果是传感器抖动导致的毛刺,用滑动平均平滑掉
# 一个简单的异常值检测示例(Python伪代码)
import numpy as np

def detect_outliers_iqr(data):
    Q1 = np.percentile(data, 25)
    Q3 = np.percentile(data, 75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    outliers = (data < lower_bound) | (data > upper_bound)
    return outliers

# 使用:标记异常但不删除,留给后续判断
data['is_outlier'] = detect_outliers_iqr(data['cpu_usage'])
避坑指南:我曾经把双十一的峰值当异常值删掉了,结果模型预测出来的扩容策略完全不够用。后来我学乖了——异常值先标记,再结合业务判断。削峰填谷的场景里,峰值往往是最重要的信号。

3.4 时间序列对齐:让数据在同一个节拍上

不同数据源的时间粒度不一样。服务器指标可能是10秒一个点,数据库记录是1分钟一个点,业务日志可能是5分钟聚合一次。

削峰填谷算法要求所有输入数据在同一个时间轴上。这就需要对时间序列进行对齐。

对齐方式有两种:

  • 降采样:把高频数据聚合到低频。比如把10秒的CPU数据聚合成1分钟的平均值。
  • 升采样:把低频数据插值到高频。比如把1分钟的订单量插值到10秒一个点。

我个人习惯用降采样。原因很简单——升采样会引入人为构造的数据,反而可能误导模型。

# 时间序列对齐示例(Python伪代码)
import pandas as pd

# 假设有两个数据源
cpu_data = pd.read_csv('cpu_10s.csv', parse_dates=['timestamp'])
order_data = pd.read_csv('order_1min.csv', parse_dates=['timestamp'])

# 统一降采样到1分钟
cpu_1min = cpu_data.resample('1T', on='timestamp').mean()
order_1min = order_data.resample('1T', on='timestamp').sum()

# 合并到同一个DataFrame
aligned_data = pd.merge(cpu_1min, order_1min, on='timestamp', how='inner')
注意:对齐的时候要小心时区问题。我遇到过服务器用UTC,数据库用东八区,日志用本地时间——三个时间轴对不上,模型训练出来全是乱的。统一转成Unix时间戳再对齐,最保险。

嗯,数据采集与预处理这部分,说白了就是「垃圾进,垃圾出」的反面——你把数据搞干净了,算法才能好好干活。下一章咱们聊聊特征工程,怎么从这些干净数据里挖出真正有用的信号。


公众号:蓝海资料掘金营,微信deep3321