金融领域知识建模:金融本体设计方法论

好,咱们进入第二章。这一章,我打算聊聊金融知识图谱最核心的部分——本体设计

很多人一上来就急着写代码、搭图数据库,结果做着做着发现数据对不上、关系理不清。说白了,就是本体没设计好。我早期一个项目就吃过这个亏,吭哧吭哧搞了两个月,最后发现实体类型定义得太粗,根本没法支撑问答。嗯,从那以后,我养成了一个习惯:先花30%的时间做本体设计

什么是金融本体?

本体,听起来玄乎。其实就是一套共享的概念模型。你想想看,咱们要建一个金融问答系统,总得先定义清楚:

  • 有哪些东西?(实体类型)
  • 它们之间怎么关联?(关系类型)
  • 每个东西有什么特征?(属性定义)

这就是本体的三要素。我习惯用一句话概括:本体就是知识图谱的“骨架”。骨架搭好了,数据才能往上挂。

核心原则:本体设计要遵循“够用就好”原则。别想着一次把所有细节都定义完,迭代才是王道。

实体类型定义

金融领域,实体类型其实就四大类:公司、人物、事件、产品。我见过有人把“股票代码”也当成实体类型,这就过度设计了。代码只是属性,不是实体。

1. 公司(Company)

这是最核心的实体。包括上市公司、非上市公司、子公司、关联公司等。我建议用层级结构来组织:

  • 上市公司:有股票代码、交易所信息
  • 非上市公司:关注注册资本、股东结构
  • 子公司:需要关联母公司

我的经验:公司实体一定要有唯一标识。我习惯用“统一社会信用代码”作为主键,股票代码作为辅助标识。别只用公司名,重名的情况太多了。

2. 人物(Person)

人物包括高管、股东、实控人、分析师等。这里要注意:

  • 高管:关注任职时间、职位变动
  • 股东:关注持股比例、增减持记录
  • 实控人:这是监管重点关注的对象

我曾经踩过一个坑:把“董事长”和“总经理”当成同一个人处理。结果问答系统里问“谁是CEO”,返回了董事长。后来我加了个职位时间线属性,才解决这个问题。

3. 事件(Event)

金融事件是问答系统的高频需求。常见的有:

  • 投资事件:谁投了谁、投了多少
  • 收购事件:收购方、被收购方、交易金额
  • 财报发布:营收、利润、增长率
  • 监管处罚:处罚对象、原因、金额

事件实体有个特点:时间属性特别重要。我一般会给每个事件加一个“发生时间”和“披露时间”,因为金融数据经常有滞后。

4. 产品(Product)

产品包括基金、理财产品、保险、信托等。这里要区分:

  • 基金产品:关注净值、规模、基金经理
  • 理财产品:关注收益率、期限、风险等级
  • 保险产品:关注保障范围、保费、赔付条件

注意:产品实体容易和“公司”实体混淆。比如“余额宝”是产品,但背后是天弘基金公司。千万别搞混了。

关系类型定义

关系是知识图谱的灵魂。我总结了三类核心关系:

关系类型 示例 说明
投资 公司A 投资 公司B 包括股权投资、债权投资
收购 公司A 收购 公司B 注意区分“控股”和“全资收购”
任职 张三 任职于 公司A 需要记录职位和任职时间

除了这三类,还有持股、担保、关联交易等关系。但我建议先从核心关系做起,后面再扩展。

属性定义与约束

属性就是实体的特征。我习惯把属性分成三类:

  • 标识属性:唯一标识实体的,比如公司ID、股票代码
  • 描述属性:描述实体特征的,比如公司名称、注册地址
  • 数值属性:可量化的,比如注册资本、营收金额

属性约束也很重要。我常用的约束有:

  • 非空约束:比如公司名称不能为空
  • 唯一约束:比如股票代码必须唯一
  • 范围约束:比如持股比例在0-100%之间
  • 格式约束:比如日期格式必须是YYYY-MM-DD

避坑指南:我曾经在属性定义上犯过一个低级错误——把“注册资本”定义成字符串类型。结果查询“注册资本大于1亿的公司”时,字符串比较完全乱套。后来我改成数值类型,并加了单位属性“万元”。

知识体系结构图

下面这张图,是我做金融本体设计时常用的框架。它展示了实体、关系、属性之间的逻辑关系:

金融知识图谱本体设计框架 实体类型 公司 人物 事件 产品 关系类型 投资 收购 任职 持股 属性定义 标识属性 描述属性 数值属性 约束规则 实体通过关系连接,属性描述实体特征 约束确保数据质量和一致性 示例:张三(人物)→ 任职于 → 腾讯(公司) 属性:职位=CEO,任职时间=2018-2023

代码示例:本体定义的Python实现

理论说完了,咱们看看代码。我用Python的dataclass来定义本体,简洁又清晰:

from dataclasses import dataclass
from typing import List, Optional
from enum import Enum

class EntityType(Enum):
    COMPANY = "公司"
    PERSON = "人物"
    EVENT = "事件"
    PRODUCT = "产品"

class RelationType(Enum):
    INVEST = "投资"
    ACQUIRE = "收购"
    EMPLOY = "任职"
    HOLD = "持股"

@dataclass
class Entity:
    """实体基类"""
    id: str
    name: str
    type: EntityType
    
@dataclass
class Company(Entity):
    """公司实体"""
    stock_code: Optional[str] = None
    registered_capital: Optional[float] = None
    industry: Optional[str] = None
    
@dataclass
class Person(Entity):
    """人物实体"""
    position: Optional[str] = None
    start_date: Optional[str] = None
    end_date: Optional[str] = None
    
@dataclass
class Relation:
    """关系定义"""
    source_id: str
    target_id: str
    relation_type: RelationType
    properties: dict = None
    
# 使用示例
company = Company(
    id="C001",
    name="腾讯控股",
    type=EntityType.COMPANY,
    stock_code="0700.HK",
    registered_capital=1000000.0
)

person = Person(
    id="P001",
    name="马化腾",
    type=EntityType.PERSON,
    position="董事会主席"
)

relation = Relation(
    source_id=person.id,
    target_id=company.id,
    relation_type=RelationType.EMPLOY,
    properties={"start_date": "1998-11-01"}
)

我的习惯:本体定义一定要写单元测试。我每次改完本体,都会跑一遍测试,确保所有约束都生效。别嫌麻烦,后面能省很多事。

好了,这一章的内容就到这儿。本体设计是个需要反复打磨的过程,别指望一次就完美。记住:先搭骨架,再填血肉。下一章咱们聊聊数据采集和清洗,那又是另一番天地了。


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