数据采集与存储:日志系统设计、数据清洗、特征工程、数据仓库选型

数据回放这件事,说白了就是「把历史数据喂给算法看」。但有个问题——你喂进去的是垃圾,吐出来的只能是垃圾。我在项目中见过太多团队,算法模型调得飞起,结果数据一换全崩了。为什么?数据采集和存储这步就没走稳。

今天咱们就聊聊,怎么把数据从源头管好,让后续的算法迭代少踩坑。

日志系统设计:别让数据丢了

日志系统是数据回放的起点。你想想看,如果日志丢了、格式乱了、时间戳对不上,后面所有工作都是白费。

我个人习惯,日志系统设计要抓住三个核心:

  • 完整性:每条日志都不能丢,尤其是关键事件
  • 时序性:时间戳必须精确到毫秒,最好用NTP同步
  • 可回溯:能根据某个ID把整条链路串起来

举个例子,我在做自动驾驶数据采集时,要求每帧数据必须包含:

{
  "timestamp": 1680000000123,   // 毫秒级时间戳
  "vehicle_id": "car_001",
  "sensor_type": "lidar",
  "data": "...",                // 原始数据
  "context": {                  // 上下文信息
    "weather": "sunny",
    "location": "lat,lon"
  }
}
注意:千万别只用「日期+时间」字符串做时间戳。不同时区、夏令时切换会让你痛不欲生。统一用Unix毫秒时间戳,省心。

我曾经遇到过一个项目,日志里时间戳格式五花八门,有的用UTC,有的用本地时间,还有的干脆没写时区。结果数据回放时,算法看到的事件顺序全是乱的。嗯,那次我们花了整整一周才把数据对齐。

数据清洗:脏数据是算法的天敌

数据清洗听着简单,做起来全是坑。我总结了一套「三步清洗法」:

  1. 格式清洗:统一字段类型、单位、编码
  2. 异常清洗:处理缺失值、重复值、离群点
  3. 逻辑清洗:检查数据间的逻辑一致性

举个实际例子,传感器数据里经常出现「跳变」——前一帧速度是30km/h,下一帧突然变成300km/h。这种数据不洗掉,算法会学出什么奇怪的东西?

def clean_sensor_data(df):
    # 1. 格式清洗:确保时间戳是数值型
    df['timestamp'] = pd.to_numeric(df['timestamp'], errors='coerce')
    
    # 2. 异常清洗:速度超过物理极限的,直接剔除
    df = df[df['speed'] < 200]  # 单位km/h
    
    # 3. 逻辑清洗:加速度变化不能太离谱
    df['accel'] = df['speed'].diff() / df['timestamp'].diff()
    df = df[df['accel'].abs() < 20]  # 单位m/s²
    
    return df
避坑指南:我曾经直接删掉所有缺失值,结果发现数据量少了30%,模型直接欠拟合。后来改用插值填充,效果好了很多。记住:清洗不是「删」,而是「修」。

特征工程:把原始数据变成算法能懂的语言

特征工程,说白了就是「翻译」。原始数据是传感器读数、日志文本,算法看不懂。你得把它翻译成数值、向量、特征组合。

我常用的特征工程方法:

  • 统计特征:均值、方差、最大值、最小值、分位数
  • 时序特征:滑动窗口、差分、傅里叶变换
  • 组合特征:两个字段的比值、乘积、差值
  • 编码特征:类别变量做one-hot或embedding

举个例子,处理车辆轨迹数据时,我习惯这样构造特征:

def extract_features(trajectory_df):
    features = {}
    
    # 统计特征
    features['avg_speed'] = trajectory_df['speed'].mean()
    features['max_accel'] = trajectory_df['accel'].max()
    
    # 时序特征:过去5秒的滑动窗口
    window = trajectory_df.tail(50)  # 假设10Hz采样
    features['window_avg_speed'] = window['speed'].mean()
    
    # 组合特征:速度与转向角的关系
    features['speed_turn_ratio'] = (
        trajectory_df['speed'] * trajectory_df['steering_angle']
    ).mean()
    
    return features

核心原则:特征不是越多越好。我见过有人一口气造了200个特征,结果模型过拟合得一塌糊涂。记住:每个特征都要有物理意义,能解释「为什么这个特征对算法有帮助」。

数据仓库选型:选对了省一半力气

数据仓库选型,我把它分成三个场景:

场景 推荐方案 原因
小规模(<1TB) PostgreSQL + TimescaleDB 部署简单,时序查询快,适合团队起步
中规模(1-10TB) ClickHouse 列式存储,聚合查询极快,适合分析场景
大规模(>10TB) Apache Doris / StarRocks 分布式架构,支持实时写入和高并发查询

我个人习惯,起步阶段先用PostgreSQL。为什么?因为团队小,折腾不起分布式。等数据量上来了,再平滑迁移到ClickHouse。我曾经见过一个团队,一上来就上Hadoop,结果三个月过去了,数据还没导进去。

注意:选型时别只看「性能」,还要看「生态」。比如你的团队熟悉SQL,就别选一个需要学新语法的数据库。工具是为人服务的,不是反过来。

知识体系总览

下面这张图,是我对数据采集与存储整个流程的理解:

数据采集与存储知识体系 日志系统设计 数据清洗 特征工程 数据仓库选型 完整性 · 时序性 · 可回溯 格式 · 异常 · 逻辑 统计 · 时序 · 组合 · 编码 PG · ClickHouse · Doris 高质量数据 → 高效算法迭代 四个环节环环相扣,缺一不可

你看,从日志系统到数据仓库,每个环节都在为「高质量数据」服务。数据质量上去了,算法迭代才能快起来。我见过太多团队在算法上花90%的精力,却忽略了数据基础——结果就是事倍功半。

我的建议:先把数据采集和存储这步做扎实。哪怕算法简单点,只要数据干净、特征合理,效果往往比那些「花架子模型」好得多。这是我在多个项目中验证过的经验。

好了,数据采集与存储这部分就聊到这儿。记住:数据是算法的燃料,燃料不纯,引擎再牛也跑不远。

可可精品资料库