OpenMed 时间归一化完全指南:TIMEX3 风格临床时间表达解析与事件锚定
OpenMed 时间归一化完全指南TIMEX3 风格临床时间表达解析与事件锚定【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed本文聚焦 OpenMed本地优先的医疗 AI 去标识化与临床 NLP 套件中的**时间归一化Temporal Normalization**模块它将临床笔记中混杂交错的绝对日期与相对表达如3 weeks ago、since last March、POD 2、q6h x5 days转换为确定性的 TIMEX3 风格DATE、TIME、DURATION、SET记录并输出 ISO 8601 值。读完本文你将掌握normalize_temporal与anchor_events两个核心 API 的完整用法、输出数据模型、歧义处理策略以及事件锚定Event Anchoring如何以隐私安全的方式将临床事件挂接到文档创建时间DCT之上。为什么需要时间归一化临床笔记的时间表达天然碎片化Symptoms began 3 weeks ago; continue q6h x5 days.一句里既有相对日期偏移又有带重复次数的用药频率病程记录中还会出现POD 2术后第 2 天、last March、since admission等依赖上下文的表达。如果直接把这些文本丢给下游的去标识化或时序推理既无法统一比较先后也无法判断一个时间表达是否携带 PHI。OpenMed 的做法是把时间表达与事件分离成两个层次。normalize_temporal只负责把调用方提供的 span 归一化为带 ISO 值的 TIMEX3 记录而since X这类区间起点与事件先后顺序的推断则交给时间线解析器timeline resolver。归一化层保持纯函数、确定性与离线运行不读墙钟、不联网、不打日志——这一点对 PHI 处理和可复现性至关重要。快速上手normalize_temporal核心入口从openmed.clinical包直接导入导出声明见 openmed/clinical/init.pyfrom openmed.clinical import normalize_temporal text Symptoms began 3 weeks ago; continue q6h x5 days. spans [ {start: 15, end: 26}, {start: 37, end: 48}, ] records normalize_temporal( text, spans, reference_time2026-06-15T10:30:00Z, ) records[0].to_dict() # { # text: 3 weeks ago, # span: [15, 26], # start: 15, # end: 26, # type: DATE, # value: 2026-05-25, # anchor: 2026-06-15T10:30:0000:00, # granularity_flags: [day], # } records[1].value # R20/PT6H几点关键语义源码实现见 temporal_normalizer.pyspans既可以是一组包含整数start/end的映射也可以是二元(start, end)序列每个 span 被独立归一化。返回的records与输入 spans 保持相同的顺序。偏移采用左闭右开inclusive start / exclusive end约定因此恒有text[result.start:result.end] result.text。_coerce_span会强制校验0 start end len(text)非法偏移抛出ValueError见 temporal_normalizer.py。每个 span 会被text[start:end]截取为短语再做全匹配归一化而不是在全文里自由搜索——调用方通常是 NER 抽取器负责给出时间表达的确切位置。NormalizedTimex 输出模型归一化结果是一个不可变 dataclassNormalizedTimex定义见 temporal_normalizer.py字段如下字段类型含义textstr原始时间表达文本start/endint左闭右开源偏移timex_typeDATE \| TIME \| DURATION \| SETTIMEX3 类型valuestr \| NoneISO 8601 归一化值无法确定时为Noneanchorstr \| None相对表达所基于的归一化参考时间granularity_flagstuple[str, ...]精度与限定符标记为了方便 JSON 输出还提供了两个便捷属性span属性返回(start, end)元组type属性返回timex_type即 JSON 字段名typeto_dict()返回 JSON-ready 字典字段为text、span、start、end、type、value、anchor、granularity_flagsflags 以列表形式输出。值与锚点类型对照与 reference_time 语义不同表达类型对应的值形式与文档一致并有源码正则支持类型示例值形式DATE2026-06-01、3 weeks ago、last March、POD 2ISO 日期、月、年或 datetimeTIME14:30、2:30 PM有锚点时输出 ISO datetime无锚点时输出 ISO timeDURATIONfor 5 days、2 weeksISO 时长如P5D、P2WSETdaily、q6h、q6h x5 daysISO 重复区间如R/P1D、R20/PT6Hreference_time决定相对表达ago、last、POD、today等的日期运算基准接受四类输入ISO 字符串、date、datetime、None。关键行为_coerce_reference_time实现于 temporal_normalizer.py传入date时会被组合为当日00:00:00的 datetimeanchor记录原始日期字符串。传入None时永不回退到当前墙钟所有依赖参考时间的相对表达如POD 2会得到valueNone并附带unanchored标记而不是偷偷发明一个时间。空字符串或非法 ISO 字符串抛出ValueError不支持的 Python 类型抛出TypeError。对since X表达归一化器把归一化后的起点存进value把调用方提供的文档时间存为anchor并追加since限定符。这样value层只表达从何时开始区间与事件排序仍交由时间线解析器负责——值层与排序层保持解耦见_normalize_sincetemporal_normalizer.py。since admission、since encounter、since now等特殊对象会直接解析为参考时间当天。支持的时间表达模式归一化器按优先级对短语做全匹配_normalize_phrasetemporal_normalizer.py。从源码中的正则字典可以梳理出完整支持面模式示例结果示例相对偏移含近似前缀与方向词3 weeks ago、in 2 days、about 10 minutes from now锚定后返回移位日期about/around/roughly/approximately追加approximate标记模糊相对量several weeks ago、a few days earliervalueNoneambiguousunanchoredlast/next系列last March、next Monday、last week、next year按日历语义推算周输出YYYY-Www月输出YYYY-MM术后日POD 2、post-op day 3、postoperative day #4锚定时reference N days绝对 ISO 日期/时间2026-06-15、2026-06-15T08:45:30Z直接透传粒度取自源码精度命名日期June 1, 2026、1st June 2026输出YYYY-MM-DD数字日期03/04/2026视歧义程度返回或标注年份/月2026、June 2026YYYY、YYYY-MM月份名March无修饰valueNoneambiguous24/12 小时制时间14:30、2:30 PM、6 pm锚定时合并参考日期输出 datetime日内时段morning、this evening、midnightTIMEpart_of_day时长for 5 days、2 weeksP5D、P2W小时/分钟/秒用PT前缀命名重复频率daily、bid、tid、qid、monthlyR/P1D、R/PT12H、R/PT8H、R/PT6H、R/P1M间隔重复频率q6h、every 2 hoursR/PT6H、R/PT2H有界重复频率q6h x5 days、every 8 hours for 2 weeksR20/PT6H等其中q6h x5 days的重复次数由_repeat_count精确换算把间隔与总时长统一到小时后整除求商6h间隔 × 20 次 120h 5 天见 temporal_normalizer.py。无法整除的有界频率如q5h x1 day不会强行输出一个看似精确的值而是valueNone并同时打上boundedambiguous——避免制造虚假精确性。数字解析支持阿拉伯数字与英文单词one到thirty月份与星期均支持全称与缩写_MONTHS/_WEEKDAYS字典覆盖temporal_normalizer.py。粒度与歧义宁可标注不可猜测granularity_flags的第一个元素总是最窄的受支持精度second、minute、hour、day、week、month、year、part_of_day月份加part_of_month。随后按需追加限定符unanchored相对日期没有提供参考时间或时间没有文档日期ambiguous源表达允许多种解释——例如03/04/2026月日与日月不明确时valueNone绝不按某个地区惯例强行选定approximate源文本明确写了about、around、roughly或approximatelybounded重复频率带时长限定如q6h x5 dayssince值是某个 since 区间的起点区间终点为记录的 anchor。归一化器坚持标注而非捏造的原则以下情况一律返回valueNone并打上ambiguous对应_ambiguous_date与各分支均有单元测试佐证见 test_temporal_normalizer.py无last/next/显式年份的纯月份表达March两位数的年份03/04/26不猜测世纪不可能存在的日期如2026-02-30零长度或负的重复周期如q0h无法整除的有界频率q5h x1 day模糊数量several weeks ago。对应测试test_ambiguous_and_unanchored_values_are_not_guessed、test_two_digit_year_does_not_guess_a_century、test_invalid_or_inexact_sets_do_not_emit_unbounded_values直接验证了这些行为。隐私与确定性归一化模块被刻意设计为纯规则、离线、确定无任何网络访问不读取环境状态不产生日志输出test_normalization_is_offline_deterministic_and_emits_no_logs用caplog验证了这一点test_temporal_normalizer.py从不默认读取墙钟。由于时间 span 可能包含敏感日期出生日期、就诊日期等调用方必须对归一化返回值采取与源文档相同的 PHI 处理策略——归一化不会帮你免去除标识义务它只是把时间表达变成结构化、可审计的 ISO 值。另外normalize_temporal还支持显式的language参数en默认或de德语规则支持 ISO 与D.M.YYYY日期、完整德语月份名及显式相对日/周/月/年表达无法识别的 span 保持未解析源文本不会被翻译temporal_normalizer.py。事件锚定anchor_events 实战时间归一化的典型下游需求是把已抽取的 EVENT 锚定到文档时间。anchor_events完成两件事把 EVENT span 挂到同句内最近的已解析 DATE/TIME 上并把相对 TIMEX 值按必需的文档创建时间DCT解析。from openmed.clinical import anchor_events text Fever started 3 days ago. result anchor_events( text, event_spans[(6, 13)], document_creation_time2026-06-15, timex_spans[(14, 24)], ) result.anchors[0].anchor_value # 2026-06-12 result.anchors[0].dct_position # before关键行为实现见 resolver.pydocument_creation_time必填它是相对日期归一化与dct_position计算的基准接受 ISO 字符串、date或datetime。timex_spans省略时模块会用本地的确定性检测器detect_timexes找出候选 TIMEX 偏移再归一化_event_anchor_timexesresolver.py。传入空的可迭代对象则禁用自动检测——此时事件只能走 DCT 兜底。传入的NormalizedTimex记录保留其已解析值不会被二次归一化但会校验其text必须与源偏移处的原文一致否则抛出ValueError。只有已解析value非空的DATE/TIME才能充当锚点其他类型或未解析值一律导致 DCT 兜底。候选连接绝不跨越句子边界且在同一句内按确定性规则选取最近的候选_nearest_event_timexresolver.py默认最大字符距离max_character_distance160。每个输入 EVENT 恰好输出一个锚点anchor_source为timex或dct_fallback。测试test_event_anchoring_autodetects_nearby_timex_without_crossing_sentences演示了混合场景第一个事件锚定到句内2026-06-01第二句的事件因跨句而被排除、回退到 DCTtest_timeline.py。锚定输出的隐私安全表示锚定结果EventAnchoringResultresolver.py由document_creation_time、anchors元组和一段固定免责声明组成每个EventTemporalAnchor包含event_start/event_end左闭右开偏移event_text_hash事件文本的 SHA-256 哈希anchor_sourcetimex或dct_fallbackanchor_value归一化锚点值TIMEX 值或 DCTdct_positionbefore/after/overlap/unknowntimex可选的TimexAnchorReference含 TIMEX 偏移、text_hash、类型、归一化值与粒度标记。锚定结果不保留原始 EVENT 或 TIMEX 文本——源证据仅由偏移量与 SHA-256 哈希表示。测试test_event_anchoring_output_contains_offsets_and_hashes_not_note_text明确断言序列化后的 JSON 中不包含started、3 days ago等原文event与timex对象里没有text字段test_timeline.py。这与整个 OpenMed 的不留原始 PHI 于日志/输出设计一脉相承。与时间线解析的衔接从源码结构看anchor_events位于openmed/clinical/timeline/resolver.py与order_events、resolve_timeline、assemble_timeline、build_linked_document_timeline等时间线能力同属一个模块并统一从 openmed/clinical/init.py 导出。order_events在提供 DCT显式传入或由is_dct/DCTspan 提供时会为每个事件附带归一化 TIMEX 或 DCT 兜底锚点然后经由 span 图解码器施加无环约束与传递约简输出有序Timelineresolver.py。since标记在位置判定时会把区间终点延展到 DCT_normalized_timex_dct_positionresolver.py保证自某日起至今的语义正确映射为overlap/after关系。也就是说一条完整的链路是NER 抽取时间 span →normalize_temporal产出 TIMEX3 记录 →anchor_events把 EVENT 锚定到 DCT 与邻近时间 →resolve_timeline排序事件。每一层都保持确定性、可审计并且只输出偏移 哈希 ISO 值原始临床文本始终留在源文档侧。测试与验证归一化器的行为有完备的测试覆盖可作参考实现与回归基准tests/unit/clinical/test_temporal_normalizer.py金标 fixture 校验、歧义不猜测、两位数年、last/next日历运算与月末钳制、绝对时间/时长/重复集类型、非法 span 拒绝、JSON 表示、离线确定性tests/unit/clinical/test_timeline.py事件锚定日期算术金标、自动检测不跨句、复用已归一化 TIMEX 值、输出仅含偏移与哈希tests/fixtures/clinical/temporal_normalization_gold.json合成金标数据含synthetic: true标记openmed/eval/suites/temporal_tlinks.py时间关系TLINK评测套件用于验证与事件排序相关的评测指标。金标 fixture 驱动的test_temporal_normalization_gold_fixture与test_event_anchoring_date_arithmetic_fixture保证任何对规则引擎的修改都必须保持已有输入的输出完全一致这正是去标识化管线最看重的可复现性。小结OpenMed 的时间归一化模块用一套小而严的规则引擎把临床笔记中形态各异的时间表达收敛为统一的 TIMEX3 风格 ISO 值DATE/TIME/DURATION/SET四类全覆盖相对表达显式锚定歧义宁可标注也不猜测全程离线、确定、无日志。在此基础上anchor_events以 DCT 为基准把事件挂接到最近的时间表达并用偏移 SHA-256 哈希的输出形式守住隐私底线。无论你是要在去标识化前结构化时间信息、构建患者时间线还是做基于时间的 PHI 风险评估都可以直接复用这套 API把文本里的时间变成可计算、可审计、可复现的时间。【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →