简介:EQTransformer是基于注意力机制深度神经网络的地震信号检测与相位拾取工具,面向地震学研究人员和AI应用开发者。其预训练模型使用全球地震数据训练,可同时完成P波、S波检测与到时拾取,并输出预测概率及模型不确定性,适合快速处理连续波形记录。配套Python软件包整合了数据下载、预处理、检测拾取、模型构建与相位关联等完整模块。压缩包共九十二个文件,约31.34MB,包含二十个Python源码、六个Jupyter示例教程、两个预训练模型与多种样本数据,另有图像、说明文档等辅助材料。目前已有1173人学习下载。借助其中notebook示例,读者可逐步操作训练、检测、可视化与关联流程,复现论文实验,还能基于预训练权重调整适配自己的数据,适用于科研、工程与教学多种场景。
1. EQTransformer 到底解决了什么:一段连续波形里的地震事件与 P/S 到时
凌晨三点处理一批流动台站数据,某台记录了一整天的连续波形,肉眼扫描看不出几个像样的地震事件,但用 EQTransformer 跑一遍,它在 60 秒的窗口里同时输出事件概率、P 波概率和 S 波概率,几十个微震被标了出来,P 波、S 波到时也直接落在采样点上。这就是 EQTransformer 的核心价值:它把地震信号检测和相位拾取当成一个序列标注问题,用 AI 模型从三分量波形里直接学习“哪里有事件、P 波在哪、S 波在哪”,而不是靠人工设置长短时窗比。适合测震台网数据处理、流动台阵事件筛选、以及想从海量连续波形里批量提取震相却受传统算法误报漏报困扰的人。它解决的问题非常明确:不用调一堆 STA/LTA 参数,也能在低信噪比场景下拿到可用的事件检测结果。
EQTransformer 不是“地震版 Transformer”这么简单。它的输入是一段三分量波形窗口,输出是三条和输入等长的概率曲线。检测概率告诉你窗口内有没有地震事件,P 波概率和 S 波概率告诉你每个采样点属于 P 震相或 S 震相的可能性。你不再需要自己写峰值搜索逻辑,也基本不需要担心 P 波和 S 波靠得太近时被传统算法合并成一个尖峰。
2. EQTransformer 为什么是地震检测里的新解法:架构、训练方式与三条概率曲线的意义
2.1 传统 STA/LTA 为什么在低信噪比场景下不够用
STA/LTA 的原理很简单:短时窗均值代表瞬时振幅水平,长时窗均值代表背景噪声水平,两者比值超过阈值就判定为事件。它很快、无监督、还能在线运行,绝大多数台网到现在仍在用它做实时触发。但它的短板恰恰藏在“全局阈值”里:同一个比值阈值在白天和深夜、在硬基岩台站和第四系沉积台站上表现完全不同。低信噪比微震的振幅只比背景噪声高一点点,短窗和长窗的比值可能只有 1.5 到 2,而一次卡车经过产生的波形能量比微震还大,比值轻松超过 5,于是误报和漏报同时存在。
另一个更棘手的问题是 P 波和 S 波的分辨。STA/LTA 在震中距较小、P 波和 S 波间隔只有一两秒时,常把两个震相合并成一次触发,事后人工复盘要花大量时间重新拆分。传统方法还特别依赖台站自身的噪声水平,遇到强背景干扰时表现为每小时触发几十次,真事件反而被海量误触发淹没。从业者切身的感受是:单台做大震相拾取还能忍受,但要对连续波形做批量回顾处理,STA/LTA 的参数调试几乎是一场玄学。
EQTransformer 的做法是把这个任务交给一个端到端模型:输入窗口内不需要任何人工特征,输出直接给出 P、S 相位的逐点概率。模型学到的不是“短时窗比长时窗”,而是“波形形态 + 相位关系 + 全局上下文”,明显更适合低信噪比和复杂噪声下的批量事件提取。
2.2 从三通道波形到“每个采样点的概率”:EQTransformer 的序列到序列结构
我一般会这样理解 EQTransformer 的结构:它先拿一维卷积网络对三通道波形做局部特征提取,相当于带通滤波器和短时形态特征的自动学习;然后接双向 LSTM 来巩固时序上下文,让网络知道“这个突跳之前有没有别的突跳”;最后是 Transformer 风格的多头自注意力层,用于捕捉长距离依赖。P 波往往先到,S 波在几秒到几十秒之后才到,自注意力机制能让模型把远端 S 波与近端 P 波关联起来,这正是传统算法最薄弱的地方。
模型默认输入是 6000 个采样点。如果你把采样率统一到 100 Hz,那这就是正好 60 秒的波形窗口;如果原始记录是 50 Hz,你也可以相应地把窗口调成 3000 点,但更稳妥的做法是重采样到 100 Hz 后仍用 6000 点。窗口太长会稀释短时事件的能量,太短又放不下远震的 P-S 时间差。官方预训练模型就是按 6000 点采样的格式训练,所以首次上手不建议轻易改这个数字。
模型输出为三条与输入等长的概率曲线:
| 输出通道 | 含义 | 典型用途 |
|---|---|---|
| 检测概率 | 每个采样点属于“地震事件”的概率 | 判定窗口内是否存在有效事件 |
| P 波概率 | 每个采样点属于 P 波到时的概率 | 提取 P 波到时 |
| S 波概率 | 每个采样点属于 S 波到时的概率 | 提取 S 波到时 |
这里的“每个点”指的是逐采样点输出,而不是像传统分类模型那样只给一个窗口标签。你拿到的不是“这段波形有事件”的笼统结论,而是能找到具体震相位置的标注结果。
2.3 检测概率与 P、S 概率的配合:阈值设置和峰值后处理
模型输出概率后,还需要一套简单的后处理规则。最常用的做法是:检测概率曲线中连续超过阈值的区间视为一个事件候选;在这个区间内,分别找 P 波概率曲线和 S 波概率曲线的局部最大值,最大值所在采样点就是到时,最大概率值就是该震相的置信度。这个流程可以用几行代码实现,但有几个细节会影响结果质量。
阈值不要设得过高。官方权重在公开大样本数据集上训练,迁移到你的台站数据后,概率分布会整体偏低。我通常把初始阈值设在 0.3 左右,如果检测率过低,再往 0.1 到 0.2 方向调;如果误报太多,再往 0.4 到 0.5 方向调。峰值后处理还有一个可调项:最小峰值间距。两个局部最大值距离太近时,通常属于同一个震相的震荡,应保留更高更尖的那一个。下面是一个常见的峰值提取思路:
def pick_from_prob(prob, threshold=0.3, min_gap=20): idxs = np.where(prob > threshold)[0] if len(idxs) == 0: return None, None # 按连通区间分组,区间内取概率最大点 groups = np.split(idxs, np.where(np.diff(idxs) > min_gap)[0] + 1) best_prob, best_idx = 0, None for g in groups: local_best = g[np.argmax(prob[g])] if prob[local_best] > best_prob: best_prob, best_idx = prob[local_best], local_best return best_idx, best_prob这段代码把“连续超过阈值的采样点”分成一个个连通区间,再在每个区间内找峰值。min_gap 设为 20 意味着两个区间之间至少要间隔 20 个采样点才能算各自独立的候选,否则合并处理。实际使用时,你还可以按事件候选区间分别对 P 和 S 做独立搜索,而不是在整个窗口里只取一个最大值,因为窗口内可能有多个地震事件。
3. 本地跑通 EQTransformer 的最小环境与一条文件预测命令
3.1 环境要求与依赖安装:Python 版本、GPU 可选与版本对齐
EQTransformer 是一个基于深度学习框架的 Python 包,首次接触时最容易在环境上翻车。我建议直接用 Python 3.9 左右的虚拟环境,把仓库克隆下来后,先看 requirements.txt 里的依赖列表,再一次性安装。仓库本身对 GPU 不是强依赖,CPU 也能跑,只是预测速度会慢一些。
git clone <EQTransformer 官方仓库地址> cd EQTransformer python -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt这里有几个值得注意的点:依赖列表中包含地震波形读取库、数值计算库和深度学习框架。如果你机器上有独立显卡,且希望用 GPU 加速,常见做法是手动重装对应深度学习框架的 GPU 版本,再装其他依赖,否则容易遇到“框架装好了但显卡不识别”的问题。如果暂时没有 GPU 环境,完全可以直接用 CPU 跑,单台 60 秒窗口的推理在普通桌面 CPU 上也就一两秒。
安装完成后不要急着写代码,先跑一下仓库自带的测试或示例脚本,确认模型能加载、目录结构完整。这一步能省掉后面很多定位问题的时间。
3.2 下载预训练模型:目录结构、权重文件与入口脚本
仓库本身一般不带大体积权重文件,需要单独下载预训练模型。常见做法是把权重文件保存到 models 目录下,这样默认的相对路径就能直接命中。目录结构大致如下:
EQTransformer/ ├── eqtransformer/ │ ├── core/ │ │ └── eqtransformer.py │ ├── utils/ │ └── ... ├── models/ │ └── eqt_model.h5 ├── data/ │ └── event_example.mseed └── requirements.txt权重文件的命名以你实际下载的为准,我这里用 eqt_model.h5 代替。下载完成后,建议先确认文件大小是对的,如果只有几十 KB,大概率是下载页面跳转到了错误页面,权重没真正落盘。官方预训练权重是在大规模公开标注数据上训练出来的,里面包含了对不同台站、不同震源机制的泛化能力,初次使用不需要自己训练。
3.3 用预训练模型检测一个 mseed 文件的最小代码
把环境配好、权重放好后,最小预测代码非常简单。核心入口是一个名为 eqtransformer 的类,传入模型路径和推理参数,再调用预测方法即可。下面这段代码是我常用的最小模板:
from EQTransformer.core.eqtransformer import eqtransformer eqt = eqtransformer( input_model="models/eqt_model.h5", threshold=0.3, number_of_sampling_points=6000, ) detections, picks = eqt.eqtransformer_predict( input_data="data/event_example.mseed", input_format="mseed", output_name="example_result", batch_size=1, overlap=0.3, ) print("检测到的事件数:", len(detections.events)) print("拾取的震相数:", len(picks.pick_attributes))代码里几个参数值得细说:threshold 是事件判定阈值,上面已经提到建议从 0.3 起步;number_of_sampling_points 必须和模型训练时一致,默认 6000;batch_size 决定了每次同时推理多少个窗口,显存紧张时降到 1;overlap 是滑窗重叠比例,0.3 表示相邻两个窗口重叠 30%,用来防止事件刚好卡在窗口边缘被截断。
detections 和 picks 是返回的对象,events 属性里保存了每个检测到的事件的起止时间,pick_attributes 里保存了震相到时、类型和置信度。为了把结果打印成可读格式,可以再加一段遍历代码:
for pick in picks.pick_attributes: print(f"台站: {pick['station']} | 相位: {pick['phase']} | 时间: {pick['phase_time']} | 置信度: {pick['confidence']:.2f}")这一步跑通后,你就已经完成了“拿现成 AI 模型做地震信号检测”的最小闭环。接下来要解决的是:如何把手头成批的连续波形切好、规整好,再喂给模型。
4. 把连续波形喂给 EQTransformer:切窗、采样率与批量处理
4.1 输入格式与规范化:mseed 读取、ZNE 通道匹配和归一化
EQTransformer 的输入是三分量波形,通道顺序一般按垂直、南北、东西排列。实际数据里经常出现通道命名不规范的情况,比如 Z 分量叫 HHZ、EHZ 甚至 BHZ,N 分量和 E 分量也各有不同前缀。所以第一步不是直接读数据,而是先按通道代码筛出 Z、N、E 三个分量。
from obspy import read import numpy as np st = read("data/station_day.mseed") st.merge(method=1, fill_value=0) tr_z = st.select(channel="*Z")[0] tr_n = st.select(channel="*N")[0] tr_e = st.select(channel="*E")[0] # 统一采样率 tr_z.resample(100) tr_n.resample(100) tr_e.resample(100) z = tr_z.data n = tr_n.data e = tr_e.data这段代码先把同一台站的分量数据合并,消除文件接缝造成的重复或空缺,再按通道后缀选出三分量,最后统一重采样到 100 Hz。这里有个容易被忽视的点:merge 时的 fill_value 参数决定了缺口位置填零还是填常数,通常填零即可。如果某个分量完全缺失,我一般会直接跳过这台数据,而不是把缺失通道填零后送进模型,因为模型会把这个零值通道当作真实的零振幅记录,影响概率输出。
归一化方面,常见做法是对每个窗口做中心化和最大绝对值归一化。注意不要对整条连续波形做全局归一化再切窗,因为一天数据里如果有几次强震,全局最大值会被它们主导,其他弱震的波形在归一化后被压得极低,模型可能全部漏检。应在切窗后对每个窗口单独归一化。
4.2 连续波形切窗与 overlap 取值:参数含义和推荐区间
连续波形切窗需要和模型固定输入长度对齐。一个 60 秒窗口在 100 Hz 采样率下就是 6000 个采样点。如果直接用非重叠窗口切,事件在窗口边界的概率约等于窗口重叠比例的倒数:30% 重叠时,事件落在窗口前 30% 被截断的概率明显降低。overlap 越大,丢失事件越少,但重复推理的次数越多,计算成本随之上升。
window_size = 6000 stride = int(window_size * (1 - 0.3)) # 4200 点,即 42 秒 windows = [] for start in range(0, len(z) - window_size, stride): w_z = z[start:start + window_size] w_n = n[start:start + window_size] w_e = e[start:start + window_size] w_z = (w_z - w_z.mean()) / (np.abs(w_z).max() + 1e-10) w_n = (w_n - w_n.mean()) / (np.abs(w_n).max() + 1e-10) w_e = (w_e - w_e.mean()) / (np.abs(w_e).max() + 1e-10) windows.append(np.stack([w_z, w_n, w_e], axis=-1))这段代码用 stride 控制窗口滑动距离,窗口长度为 6000,步长 4200,意味着相邻窗口重叠 1800 个点。每个窗口内先减均值再除以该窗口内最大绝对值,加一个 1e-10 防止静默段除零。这样处理以后,每个窗口的振幅量级都一致,模型不会因为台站增益差异而出现系统性偏差。
overlap 的推荐区间我一般给 0.2 到 0.4。取值低于 0.2 时,短时事件的漏检风险明显上升;高于 0.4 时,计算量浪费在重复推理上,收益递减。对于固定台网的连续波形批处理,0.3 是一个性价比很高的起点。
4.3 批量场景的数据组织:按台站预测、结果落地与事件筛选
批量处理时,我习惯按“一个台站一天的数据”作为一个单元,逐文件调用预测接口,再汇总所有拾取结果。汇总的字段至少包括:台网、台站、通道、相位类型、到时、置信度、所属窗口起始时间。这样后续无论是做震相配对、定位输入还是人工复核,都有一份干净的表格可用。
import csv results = [] for mseed_file in mseed_files: detections, picks = eqt.eqtransformer_predict( input_data=mseed_file, input_format="mseed", output_name=f"batch_{idx}", batch_size=1, overlap=0.3, ) for pick in picks.pick_attributes: results.append([ pick["network"], pick["station"], pick["channel"], pick["phase"], pick["phase_time"], round(pick["confidence"], 3) ]) with open("picks.csv", "w", newline="") as f: writer = csv.writer(f) writer.writerow(["network", "station", "channel", "phase", "time", "confidence"]) writer.writerows(results)批量预测返回的 pick_attributes 里已经带了台站和通道信息,省去了手动关联。这里的输出名每次都要保证唯一,否则后一次覆盖前一次的中间文件。事件筛选中,我通常先按检测概率大于阈值保留事件,再按 P/S 概率峰值是否超过阈值决定是否记录震相。如果一个窗口内检测概率很高但 P、S 概率都很低,大概率是爆破或干扰信号,可以根据置信度直接滤掉。
5. EQTransformer 使用避坑指南:五个高频翻车点与排查方案
5.1 输入长度不符导致 predict 阶段直接报 shape 错误
现象:调用 eqtransformer_predict 后立即报错,错误信息指向维度不匹配或输入 shape 不是模型期望的值。原因绝大多数是输入波形采样率不是 100 Hz,或者文件里一个分量被截断,导致喂进模型的数组长度不等于 6000。有些台网记录的是 50 Hz 或 250 Hz 采样率,直接读进来长度不是 6000 的整数倍,切窗后末尾不足 6000 个点。
解决:统一在预处理阶段重采样到 100 Hz,并检查长度。下面这段检查代码能定位 90% 的问题:
st = read(mseed_file) st.resample(100) lengths = [len(tr.data) for tr in st] print(lengths) if len(set(lengths)) != 1: print("分量长度不一致,需要裁剪或补零")实际上有些连续波形文件在记录中断后会多出几秒数据,三个分量长度不完全一致。我会在切窗循环里用 padding 补到整窗口长度,或者直接跳过末尾不足一个窗口的部分。宁可少处理最后几秒,也不要让模型拿到畸形输入。
5.2 模型跑完但事件概率全部低于 0.1,一个事件都检不到
现象:预测流程正常结束,但检测概率曲线最高值只在 0.05 附近,事件结果为空。这种情况常见于两类数据:一类是信号极弱、信噪比接近 1 的微震;另一类是数据的增益系数太大或太小,归一化后波形形态被扭曲。还有一种很容易忽略的情况:你喂进去的窗口没有做任何归一化,而模型在训练时见过的是归一化后的波形,概率输出自然会塌缩到接近 0。
解决:先确认预处理里做了窗口内最大绝对值归一化;然后把 threshold 降到 0.1 重跑一次,看检测概率是否出现尖峰。如果降到 0.1 仍然全空,打开波形文件用简单绘图工具人工看一眼,排除数据本身是空白记录或断记导致的全零段。换数据试跑前,先用仓库自带的示例文件验证模型权重是否正常,避免把权重问题误判成数据问题。
5.3 安装阶段 GPU 不可用,预测卡得让人怀疑人生
现象:模型能加载,但跑一个窗口要几十秒,或者运行时直接提示找不到 GPU。原因通常是深度学习框架的 GPU 版本和显卡驱动、计算平台版本不匹配。很多人先装了 CPU 版框架,后面又装了 GPU 版,两者互相覆盖,最终实际调用的是 CPU 后端。
解决:在虚拟环境里重建环境,先装 GPU 版框架,再装剩余依赖。安装完成后用一句话验证:
import tensorflow as tf print("GPU available:", tf.config.list_physical_devices("GPU"))如果输出为空列表,不要继续调代码,先检查和驱动是否匹配。其实大多数预测任务 CPU 也能接受,单个 60 秒窗口推理一两秒,一天连续波形按 0.3 重叠滑窗也就几千次推理,整晚挂着能跑完。所以 GPU 不是必需品,只是批量处理时能明显缩短等待时间。
5.4 把一整天连续波形整段塞进官方接口,进程被直接 kill
现象:有人图省事,直接把 24 小时的连续波形文件路径丢给 eqtransformer_predict,结果内存占用飙升,进程被系统杀掉。原因在于接口内部会把读入的波形按窗口切分后一次性加载到内存,长波形意味着上万个窗口数组同时驻留内存。
解决:把长文件在预处理阶段先按小时或半小时切片,再逐段预测。切段时间不宜过短,否则 P-S 间隔很快的远震没问题,但近震的大 P-S 间隔可能跨段丢失。我一般按 30 分钟一段,配合 overlap=0.3,既控制内存峰值,又能保证多数事件完整落在一个段内。如果内存还是吃紧,batch_size 从 1 调到 8 或 16,模型单次处理更多窗口,循环次数变少,整体速度会更快。
5.5 自己标注数据再训练后效果反而变差,甚至出现 loss 为 NaN
现象:用自己的台站数据训练或微调后,测试集准确率比预训练模型还低,训练过程中 loss 偶尔变成 NaN。原因集中在三个地方:标签错误、学习率过大、以及从随机初始化而不是预训练权重出发训练。EQTransformer 这种深层模型,数据量只有几千条时从零训练几乎必定过拟合,表现为训练集 loss 很低,验证集上检测率一塌糊涂。
解决:先确认训练标签里 P/S 到时的标注精度至少在一个采样点以内,宽松标签会让模型学到“差不多就行”的模糊映射;再确认学习率不超过 1e-4,大学习率会让注意力层的权重更新幅度过大,直接导致数值溢出;最后必须用官方预训练权重作为初始权重,冻结前面的卷积和 LSTM 层,只训练后面的输出层,等模型稳定后再解冻全部层做低学习率微调。
6. 进阶:用迁移学习把 EQTransformer 调成你台网的“专属检测器”
6.1 小数据集微调的关键参数与训练命令
通用预训练权重在你的台站数据上通常已经能跑出八成效果,剩下两成差在噪声背景和仪器响应上。很多台站有固定频率的工业干扰、风电噪声或周期性车辆振动,这些在通用数据集里很少出现。微调的价值就是让模型见过你的噪声,而不是只见过通用背景。
第一步是把你的标注数据整理成训练集。仓库里通常配有把原始波形转成 HDF5 训练文件的工具,输入是波形目录,输出包含窗口波形和 P/S 标签的二进制文件。生成后,用类似下面的方式触发训练:
python train.py \ --input_hdf5 data/my_station.h5 \ --input_model models/eqt_model.h5 \ --output_model models/my_station_eqt.h5 \ --training_fraction 0.8 \ --learning_rate 0.0001 \ --epochs 30以我自己的经验,训练比例 0.8 比较合适,剩余 0.2 用作验证。学习率从 1e-4 起步,跑完 30 轮后如果验证 loss 还在下降,再加 20 轮。数据量少于 2000 个窗口时,不要解开所有层,只微调 Transformer attention 部分;超过 5000 个窗口再考虑全层微调。batch size 一般 32 到 64,过小会导致训练震荡,过大会让显存吃紧。
6.2 验证微调效果:用已知震相数据回测的快速检查
微调完成后,不要只看 loss。我习惯的做法是从台网历史事件里挑 30 个不同震中距、不同信噪比的记录,做成一个不参与训练的小回测集,然后用微调前后的模型分别做预测。对比指标就是两个:事件检测召回率和 P/S 到时误差。
# 以回测集某个已知 P 到时为例 known_p_time = ... predicted_p_time = ... error = abs((predicted_p_time - known_p_time).seconds)到时误差通常在 0.1 秒以内就算优秀,0.1 到 0.5 秒可以接受,超过 1 秒就要检查是不是拾到了 S 波残留或噪声尖峰。回测时我会特别注意微震和小震,因为通用模型对它们最容易漏检。微调后如果这些样本的检测概率明显提高,说明模型确实学到了你的台站噪声特征。
我一直保留一个习惯:每换一批台站数据,先用 5 个已知事件在预训练权重上跑一遍冒烟测试,把检测概率和到时误差记录下来,再决定要不要微调。这不只是验证模型,也是在验证自己的预处理流程有没有埋坑。EQTransformer 是一个能把大量重复劳动自动化成几行代码的工具,但它的输出质量始终取决于输入数据的规范化水平和标签质量。希望这套从原理到迁移学习的思路能帮到你,少走几步弯路。
本文还有配套的精品资源,点击获取