3. 农业数据采集与预处理:多源异构数据源

农业知识图谱的根基是什么?说白了,就是数据。没有干净、可靠的数据,再牛的模型也白搭。这一章咱们聊聊数据从哪来、怎么洗、怎么标、怎么存。我做了这么多年农业信息化项目,踩过的坑有一半都跟数据预处理有关。

核心观点:农业数据的特点是「多源、异构、脏」。多源指来源杂,异构指格式乱,脏指质量差。预处理的目标就是把这堆「原材料」加工成可用的「食材」。

3.1 多源异构数据源

农业数据源,我大致分成三类:文本、数据库、传感器。每一类都有自己的脾气。

3.1.1 文本数据

文本数据是最常见的,也是最难处理的。包括科研论文、技术文档、新闻报道、政府公告、农技问答等等。这些数据非结构化,没有固定格式。

我记得有一次做水稻病虫害知识图谱,从网上爬了上千篇农技文章。结果发现,同一篇文档里「稻瘟病」和「水稻瘟病」混着用,还有用「稻热病」的。这就是典型的实体对齐问题。

  • 来源:知网、万方、政府网站、农业论坛、公众号
  • 格式:PDF、Word、HTML、TXT
  • 难点:编码混乱(GBK vs UTF-8)、排版复杂、术语不统一

3.1.2 数据库数据

结构化数据相对好处理,但问题在于「异构」。不同系统、不同年代建的库,字段名、数据类型、编码规则都不一样。

举个例子,A 县的植保系统里「作物名称」字段叫 crop_name,B 县的叫 zwmc,C 县的直接存的是作物代码。你想想看,光是把这三个字段对齐,就得花不少功夫。

数据源 常见格式 典型问题
农业统计数据库 MySQL、Oracle 字段命名不统一、编码不一致
农技推广系统 SQL Server、Access 历史数据冗余、主键冲突
气象数据接口 JSON、XML 字段缺失、时间格式混乱

3.1.3 传感器数据

物联网在农业里用得越来越多了。温度、湿度、光照、土壤pH值、二氧化碳浓度……这些数据实时产生,量大,但噪声也大。

我曾经在某个智慧大棚项目里,发现一个温度传感器连续三天报 85°C。一开始以为是温室效应,后来才发现是传感器坏了。嗯,这里要注意:传感器数据必须做异常检测,否则你的知识图谱里会多出「耐高温番茄」这种离谱实体。

  • 来源:温湿度传感器、光照传感器、土壤传感器、无人机影像
  • 格式:CSV、JSON、二进制流
  • 难点:数据量大(高频采样)、缺失值多、异常值频发

3.2 数据清洗

数据清洗是预处理里最耗时的一步。我个人的经验是,清洗时间通常占整个项目周期的 60% 以上。别嫌多,这是常态。

3.2.1 缺失值处理

农业数据缺失太常见了。传感器没电、数据库字段为空、文本里漏了关键信息……

  • 删除法:缺失比例高(>70%)的字段直接删
  • 填充法:用均值、中位数、众数填充,或者用插值法
  • 模型预测:用其他字段预测缺失值(适合关键字段)

我的习惯:对于时间序列的传感器数据,我一般用前向填充(forward fill)。比如温度传感器每10分钟采一次,如果某次缺失,用上一次的值补上。简单有效。

3.2.2 异常值检测

异常值不处理,知识图谱里就会多出「亩产十万斤」这种荒唐数据。

# 简单的3σ原则检测异常值
import numpy as np

def detect_outliers(data, threshold=3):
    mean = np.mean(data)
    std = np.std(data)
    outliers = []
    for i, val in enumerate(data):
        z_score = (val - mean) / std
        if abs(z_score) > threshold:
            outliers.append(i)
    return outliers

# 传感器温度数据
temps = [25.3, 25.1, 85.0, 24.8, 25.2, 24.9]
outlier_idx = detect_outliers(temps)
print(f"异常值索引: {outlier_idx}")  # 输出 [2]

3.2.3 去重与标准化

文本数据里,「西红柿」和「番茄」是不是同一个实体?「2024-01-15」和「2024/01/15」是不是同一个日期?这些都需要标准化。

  • 实体对齐:建立同义词表,比如「马铃薯=土豆=洋芋」
  • 格式统一:日期统一为 YYYY-MM-DD,单位统一为国际单位
  • 去重:基于编辑距离或 Jaccard 相似度做模糊去重

我曾经踩过的坑:在做柑橘知识图谱时,我把「砂糖橘」和「沙糖桔」当成了两个实体。后来才发现是同一个品种,只是不同地区的写法差异。从那以后,我养成了先做实体对齐再做入库的习惯。

3.3 数据标注

知识图谱需要标注数据来做实体识别和关系抽取。标注质量直接决定模型效果。

3.3.1 标注类型

标注类型 说明 示例
实体标注 标记文本中的实体 [水稻] 感染了 [稻瘟病]
关系标注 标记实体间的关系 (水稻, 感染, 稻瘟病)
属性标注 标记实体的属性值 (稻瘟病, 防治方法, 三环唑)

3.3.2 标注工具

我推荐几个常用的:

  • Label Studio:开源,支持文本、图像、音频多种类型
  • BRAT:老牌工具,适合文本标注,上手快
  • Doccano:轻量级,适合小团队快速标注

3.3.3 标注策略

标注不是越多越好。我个人的经验是:

  • 先小批量试标:先标 100 条,看看标注规范是否合理
  • 多人标注+一致性校验:至少两个人标同一批数据,计算 Kappa 系数
  • 主动学习:用模型预测不确定的样本优先标注,提高效率

避坑指南:标注规范一定要写清楚。比如「水稻」和「杂交水稻」算不算同一个实体?「稻瘟病」和「水稻稻瘟病」呢?这些边界问题不提前定好,后期返工成本极高。

3.4 数据存储

数据洗好了、标好了,得找个地方存起来。农业知识图谱的数据存储,我一般分三层。

3.4.1 原始数据层

原始数据不动,保留一份「底稿」。用 HDFS 或对象存储(如 MinIO)存着,方便回溯。

3.4.2 清洗数据层

清洗后的结构化数据,用关系型数据库存。我习惯用 PostgreSQL,支持 JSON 字段,灵活。

-- 创建清洗后的作物表
CREATE TABLE crops (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    scientific_name VARCHAR(200),
    category VARCHAR(50),
    planting_season VARCHAR(50),
    created_at TIMESTAMP DEFAULT NOW()
);

-- 插入清洗后的数据
INSERT INTO crops (name, scientific_name, category, planting_season)
VALUES ('水稻', 'Oryza sativa', '粮食作物', '春季');

3.4.3 知识图谱层

最终的知识图谱用图数据库存。Neo4j 是主流选择,支持 Cypher 查询,方便做关系推理。

// 创建实体节点
CREATE (c:Crop {name: '水稻', scientific_name: 'Oryza sativa'})
CREATE (d:Disease {name: '稻瘟病', type: '真菌性病害'})

// 创建关系
CREATE (c)-[:INFECTED_BY]->(d)

你想想看,用图数据库查「水稻可能感染哪些病害?」,一条 Cypher 语句就搞定了。这在关系型数据库里得写多少个 JOIN?

3.5 本章知识体系

下面这张图概括了本章的核心流程。从多源数据到最终的知识图谱,每一步都离不开预处理。

农业数据采集与预处理流程 农业数据采集与预处理流程 多源异构数据源 文本数据 数据库数据 传感器数据 数据清洗 缺失值处理 · 异常值检测 · 去重与标准化 数据标注 实体标注 · 关系标注 · 属性标注 原始数据层 (HDFS) 清洗数据层 (PostgreSQL) 知识图谱层 (Neo4j) 数据预处理是知识图谱构建中最耗时但最关键的一步,直接影响最终质量

数据采集与预处理,说白了就是「把垃圾变成黄金」的过程。别嫌麻烦,这一步做扎实了,后面的知识图谱构建才能顺风顺水。我见过太多项目,模型调得再好,数据一塌糊涂,最后全白费。