
简介EQTransformer 是面向地震学与地球物理研究者的 Python 开源工具包用深度学习完成地震信号检测与 P/S 相位拾取。资源定位于降低地震信号智能处理门槛模型采用带注意力机制的分层 Transformer 网络并结合 LSTM 与多任务学习可同时输出检测概率、震相到时和模型不确定性适用于连续波形处理、震相编目与模型复现实验分层设计也便于理解地震信号专用网络的组织方式对 Python 和深度学习基础有一定要求。 压缩包共 92 个文件约 31.34MB涵盖 py 源码、ipynb 示例、png 图示、rst 文档及 h5/hdf5 模型与样本数据等类型源码模块覆盖连续数据下载、预处理、检测、拾取和简单相位关联示例展示训练、检测、可视化、下载与关联流程预训练权重与样例数据便于快速验证测试代码和分层目录则有助于理解模型结构、复现实验和二次开发。 目前已有 1176 人学习下载适合希望将 AI 方法落地到地震信号处理的研究者与开发者参考。1. 地震波形检测为什么一个 Transformer 能同时干三件事一段三分量地震波形扔进模型,几秒后吐出三类结果:哪里发生了事件、P 波到哪儿、S 波到哪儿。传统流程里这是三条独立流水线——检测靠 STA/LTA 阈值,拾取靠 AIC 或人工标注,各自调参、各自翻车。EQTransformer 把三件事塞进一个编码器加三个并行解码头,靠的是同一个隐层表示同时服务三个任务。值得搞懂的地方在于:这不是「大模型套小任务」的粗暴堆叠,而是多任务学习里特征共享到底共享了什么。检测要的是「这段波形有没有能量突变」的全局判断,拾取要的是「采样点级突变位置」的局部精度,两个目标的感受野差了好几个数量级。一个网络凭什么同时满足?答案藏在编码器的层级结构和注意力机制的分配方式里。搞懂这一点,你再看其它波形类任务(微震、声发射、结构健康监测)的模型设计,会有一把通用的尺子。2. 编码器:金字塔式下采样为什么是多任务的物理前提2.1 从波形到特征图,每一层在丢什么、留什么输入是固定长度的三分量窗口,常见的实现思路是把它当作一维信号序列处理:形状(batch, 6000, 3),对应 60 秒、100 Hz 采样。编码器由若干个卷积块串起来,每块做完卷积后接池化,序列长度按 2 或 5 的倍数逐级压缩,通道数逐级上升。走到最深层时,时间轴可能只剩原始长度的几十分之一,通道数却涨到几十甚至上百。这个「长度换通道」的过程,本质是在做时间分辨率和语义抽象度的交换。浅层保留采样点级的细节,那是相位拾取赖以生存的信息;深层把几十个采样点压成一个向量,那个向量编码的是「这一小段整体像不像一次事件起始」,那正是检测头需要的判据。我一般会这么理解:编码器输出的不是一个特征,而是一族特征。同一个输入在不同深度上有不同粒度的表示,三个解码头各自去取自己需要的那一层。这就是为什么多任务能塞进一个 backbone——它们不是抢同一份特征,而是共享同一棵特征树的不同枝干。实现上有个细节容易被忽略:池化会破坏时间对齐。如果检测头设在深层、拾取头设在浅层,两者的输出时间轴根本对不上。所以解码阶段必须做上采样和跳跃连接,把浅层的高分辨率特征「接」回深层语义上,这也是 U 型结构的由来。# 编码器的一个卷积块示意,注意 BN 与激活的顺序 def conv_block(x, filters, kernel11, stride1, pool2): x Conv1D(filters, kernel, stridesstride, paddingsame)(x) x BatchNormalization()(x) # 先归一化,稳定不同台站的振幅差异 x Activation(relu)(x) if pool 1: x MaxPooling1D(pool_sizepool)(x) # 时间轴压缩,通道承载语义 return x卷积核取到 11 这种「偏大」的尺寸,不是随手写的。波形里的 P 波起始是个缓变到陡变的过渡,太小的核只能看到陡变那一下,看不到前面的能量积累;大核能把「突变前后的对比」一起卷进来,信噪比低的时候这点尤其关键。paddingsame保证长度只被池化改变,不被卷积改变,后面算时间偏移时少一层换算。BN 放在激活前面,是为了让不同台站、不同增益的波形先被拉到同一量纲——地震数据里振幅跨几个数量级是常态,不归一化,梯度会被大振幅台站主导。2.2 残差连接解决的是深度带来的梯度衰减编码器堆深之后,梯度回传要穿过几十层,浅层参数几乎收不到有效信号。残差连接把输入直接加到输出上,梯度有了一条恒等通路,深层网络的训练才不至于发散。这在波形任务里比在图像里更要紧:地震信号的有效特征往往在浅层就已经成型,深层只是在做组合,如果浅层学不动,整个模型就废了。常见的做法是每两个卷积块加一条 shortcut,并在维度不匹配时用 1x1 卷积对齐通道数。这一步的代价很小,收益是把可用深度从个位数推到二十层以上。2.3 为什么不用纯 LSTM 做编码器LSTM 在序列建模上确实是老牌选手,能捕捉长程依赖,对波形的时序特性天然友好。但纯 LSTM 编码器有个硬伤:它是串行计算的,60 秒窗口逐点推进,训练时无法并行,样本吞吐被卡死;而且长序列上的梯度传播路径太长,即便有门控,远端信息仍会衰减。EQTransformer 的选择是卷积做局部特征提取 注意力做全局关联,再叠一层双向 LSTM 补时序。卷积负责并行、负责局部形态,注意力负责「这段和那段有没有关系」,LSTM 负责把前后文揉进每个时间步的表示。三者各管一段,不是互相替代的关系。这也是这个名字里同时出现 Transformer 和 LSTM 的原因——它不是纯 Transformer,是个混合体。3. 自注意力:检测头需要的「全局视野」到底怎么算出来的3.1 多头注意力在波形上的实际语义把编码器深层的特征序列送进多头注意力,每个头学一组 Q、K、V 投影,算出注意力权重矩阵,再对各位置的值加权求和。抽象说法是「捕捉长程依赖」,落到波形上,它捕捉的是不同时间位置之间的事件相关性。举个具体场景:P 波到达后若干秒 S 波才到,两者在时间轴上离得很远,但物理上是同一个事件。卷积核再大也覆盖不了这个跨度,池化更是把细节抹平了。注意力可以直接让 P 波位置的表征「看见」S 波位置的表征,把两者绑成一组。检测头判断「这里有没有事件」时,靠的就是这种跨时间段的证据聚合。# 多头注意力的核心计算,缩放因子不是可有可无的 def scaled_dot_product_attention(q, k, v, maskNone): d_k tf.shape(q)[-1] scores tf.matmul(q, k, transpose_bTrue) scores scores / tf.math.sqrt(tf.cast(d_k, tf.float32)) # 防止点积过大导致 softmax 饱和 if mask is not None: scores (mask * -1e9) # 填充位置屏蔽,避免 attend 到无意义的补零区 weights tf.nn.softmax(scores, axis-1) return tf.matmul(weights, v), weightssqrt(d_k)这个缩放,是血泪经验堆出来的。维度一高,点积的方差随维度线性增长,softmax 会被推到极端,梯度接近零,模型学不动。除以维度的平方根把方差拉回 1 附近,softmax 保持在一个有梯度的区间。mask 在地震任务里尤其不能省。为了凑 batch,短窗会被补零到统一长度,这些补零位置如果参与注意力,模型会学到「一堆零等于没有事件」这种伪相关。mask 掉它们,等于告诉模型「这些位置不存在,别参考」。提示:多头数不是越多越好。头数上去,每个头的维度就下来,单头能表达的语义变窄。波形任务里 4 到 8 个头是比较常见的取值区间,再多收益递减明显。3.2 注意力权重能当解释性证据用吗权重矩阵可视化看着很漂亮,但把它直接当作「模型关注了哪里」的证据,是要打折扣的。注意力权重只反映当前层的加权分配,经过残差、归一化、后续层组合之后,最终输出的成因已经很难回溯到某一层的某组权重上。更靠谱的验证方式是对抗性检查:把权重最高的那几个时间位置替换成噪声,看检测输出是否真的掉下来。掉,说明那几个位置确实承重;不掉,说明权重只是表象。这套方法我在排查「模型看起来学会了其实在抄捷径」时反复用,比看图靠谱。4. 三个解码头:检测与拾取的目标冲突怎么调和4.1 分类头与回归头的输出设计检测头输出的是逐时间步的事件概率,本质是二分类,用 sigmoid 加二元交叉熵。拾取头输出的是 P 波和 S 波各自的时间偏移,本质是回归,但 EQTransformer 没有直接回归偏移值,而是把偏移编码成一个以真值为中心的高斯概率分布,让网络预测这个分布,再用期望或峰值位置反推时间。这个设计是整个方案里最见功力的一笔。直接回归偏移,遇到标注歧义就崩——同一次事件,不同标注员标的 P 波到达点可能差一两个采样点,损失函数被这些矛盾样本拉扯,模型学出一个「平均位置」,精度反而下降。换成高斯分布预测,歧义被建模成方差:标注一致的样本,学到的方差小,预测尖锐;标注分歧大的样本,方差大,预测平缓。损失函数用 KL 散度或交叉熵,模型不会因为分歧样本被重罚。# 相位拾取的高斯标签构造:sigma 控制容忍度 def gaussian_label(length, pick_sample, sigma20): t np.arange(length, dtypenp.float32) label np.exp(-0.5 * ((t - pick_sample) / sigma) ** 2).astype(np.float32) return label / label.max() # 归一化,峰值恒为 1sigma是这套方案的核心超参。取太小,模型被逼着做采样点级的精确匹配,标注噪声直接变成梯度噪声;取太大,分布太平,峰值位置模糊,拾取精度上不去。常见的做法是按采样率折算,让sigma对应若干个毫秒的物理时长,而不是凭感觉定一个数。4.2 多任务损失加权:谁该说话大声三个头的损失量纲完全不同:检测是交叉熵,值域小;拾取是分布匹配,值域也小但梯度形态不一样。简单相加,量级大的那个会主导训练。处理方式有两种常见思路。一是手工给每个损失配权重,按验证集表现调;二是用不确定性加权,让模型自己学每个任务的噪声参数,损失写成L_i / (2 * sigma_i^2) log(sigma_i),噪声大的任务自动降权。前者可控但费调参,后者自动化但收敛慢,还得防止某个任务的 sigma 爆炸导致它被彻底忽略。我第一次搭这类多任务模型时踩的坑是:检测损失收敛得漂亮,拾取精度死活上不去。查了半天才发现拾取头的梯度被检测头压了两个数量级,拾取那一路几乎没在学。后来把拾取损失的权重提上来才正常。4.3 共享编码器的梯度冲突多任务学习有个绕不开的问题:不同任务的梯度方向可能相反。检测希望深层特征平滑、鲁棒于局部扰动;拾取希望深层特征保留细节、对采样点敏感。两个目标在共享参数上打架,学出来的表示是个妥协解,可能两边都不够好。缓解手段主要是梯度归一化和任务特定的解码头深度。让每个任务在共享编码器之上先过几层独立的卷积再做预测,等于给冲突留了缓冲带——冲突在独立层里被消化掉一部分,传回共享层的梯度就干净一些。这也解释了为什么三个头不是简单的一个全连接层了事。5. 避坑与排查:训练和部署里最容易翻车的几处5.1 现象:验证集指标漂亮,换台站就崩原因通常是数据泄漏。窗口切分时如果随机打乱,同一个事件的相邻窗口会同时落进训练集和验证集,模型其实是在「回忆」而不是「泛化」。地震数据的窗口高度重叠,这个坑踩得极其普遍。解决方式是按事件 ID 或按时间段做划分,同一事件的所有窗口只能出现在一个集合里。划分完再检查一遍两组的时间分布是否有重叠。5.2 现象:模型对高信噪比样本完美,低信噪比全错原因多半是标签噪声和类别不平衡叠加。低信噪比区域的标注本身就不可靠,模型学到的是标注员的习惯而不是物理信号。另外,事件窗口远少于噪声窗口,不加权的交叉熵会让模型倾向于全判为噪声。解决:对正样本加权,并把sigma调大以容忍低信噪比区域的标注分歧;训练时做振幅扰动和加噪增强,让模型见过各种信噪比条件。5.3 现象:注意力可视化很漂亮,但删掉那些位置输出不变原因见 3.2 节,权重不等于因果贡献。解决:用遮挡实验代替看图,逐个屏蔽高权重位置验证输出变化。若变化可忽略,说明模型依赖的是别的通路。5.4 现象:推理时同一个事件在不同 batch 里结果不一致原因通常是变长序列的 padding 和 mask 处理不统一。batch 内最长序列决定 padding 长度,如果 mask 没在每一层都正确传递,补零区会污染注意力。解决:统一在数据管道里做定长裁剪或填充,mask 张量随特征一路传到每个注意力层;推理阶段单独跑 batch1 的结果作为基准,对比差异定位问题。6. 设计取舍:混合架构的边界在哪里纯 Transformer 处理长序列的代价是注意力矩阵随长度平方增长。60 秒、100 Hz 的窗口是 6000 个点,直接全局注意力,矩阵规模难以承受。所以编码器先用卷积和池化把序列压下来,注意力只在压缩后的序列上算,这是混合架构的第一层取舍——用卷积的归纳偏置换计算可行性。第二层取舍在感受野的分配上。卷积核大小决定了局部上下文,注意力范围决定了全局关联,两者叠加出的有效感受野不是简单相加。核太大,局部细节被平滑掉,拾取精度受损;注意力范围太小,跨相位的关联建不起来。这套参数没有普适最优解,得按采样率和目标事件的持续时间去推。第三层取舍在输出形式上。高斯分布预测换了直接回归,精度更稳但引入了sigma这个敏感超参;分类式的检测头简单直接,但对事件边界的刻画粗糙。每个选择都在拿鲁棒性换精度,或者反过来。验证这套设计是否真的有效,我一般会做消融:去掉注意力看检测召回掉多少,去掉 LSTM 看拾取误差涨多少,把高斯标签换成 one-hot 看精度变化。三个消融跑下来,哪个模块在真正承重一清二楚。比读论文里的对比表可信得多,因为消融是在你自己的数据分布上做的。部署前的最后一关是跨台站验证。在 A 台站训练、B 台站测试,精度掉得厉害说明模型学的是台站特性而不是物理特征。这时候回头去看 BN 层和振幅归一化的处理,往往能定位到问题。从那以后我每次做波形类多任务模型,都强制先跑一遍「去掉共享编码器、三个任务各训一个」的基线,拿它当锚点。共享带来的收益必须能量化出来,否则就是在给自己加复杂度。希望帮到你。本文还有配套的精品资源点击获取