做射电天文数据处理的朋友,应该都在检查观测数据时见过那种“猫抓板”一样的动态谱图:竖着的亮线、横着的带状条纹、斜着的扫频痕迹,颜色刺眼,把本该平滑连续的宇宙信号搅得乱七八糟。这就是射频干扰,行内叫RFI。以前不少课题组靠人工目视检查和带通阈值来清理,效率低不说,碰上弱干扰和瞬态干扰经常漏得干干净净。最近几年,越来越多的人开始把深度学习搬进这条流水线,让模型直接对二维动态谱图做像素级分割,自动标出哪里被污染了。我自己实打实下场做过一轮,踩了不少坑,这篇文章就把整套AI射电频率干扰检测的思路、数据准备、模型选型、训练调参和实际对接观测数据流的全过程梳理出来,给想做这个方向的射电天文学者和机器学习工程师一个能直接抄作业的参考。
这个方向其实非常有代表性,它属于典型的“把图像分割问题迁移到科学数据上的案例”:输入是一张“时间×频率”的二维功率图,输出是一张同样大小的掩码图,标记每个时频格是否被干扰污染。和很多天文机器学习应用只有理论演示不同,RFI检测在国内外多个观测站已经进入了准实时数据处理流程,很多看似艰深的论文背后,落地逻辑其实非常朴素。
1. 从射电望远镜底噪到AI救场:RFI问题全景
1.1 射电天文学为什么对电磁环境这么敏感
射电望远镜本质上就是一个超大号的收音机,它接收的目标信号来自数万甚至数十亿光年外的天体。这些信号经过漫长传播到地面,功率已经极其微弱,到了什么程度呢?通常只有几央斯基,换算成功率密度大概在10的负二十几次方瓦量级,比手机信号弱上亿倍。为了捕捉这种信号,我们一方面把天线做得尽可能大,另一方面依靠高增益低噪声的接收机系统和长时间积分来提升信噪比。
但问题在于,射电望远镜工作的频段和人类社会使用的频段高度重叠。从几十兆赫的低频到数十吉赫的高频,移动通信、广播、雷达、卫星下行链路、微波点对点传输、工业设备泄露,甚至一台没有屏蔽好的微波炉,都在向空间辐射能量。当这些信号进入望远镜的接收链路,会在特定频率上形成一个能量尖峰,或者在一个较宽的频率范围内抬高底噪。这种污染不像普通噪声那样是随机平滑的,它会呈现出特定的结构和规律,比如持续存在、周期性出现、快速扫频等。
在观测数据里,RFI的危害是双重的。第一重危害是掩埋信号:强射频干扰会让目标源本身的辐射完全看不出特征,等于是这个频段的积分时间白费。第二重危害是污染统计:即使干扰比较弱,它也会改变数据的基础噪声分布,让后续的谱线拟合、偏振测量和成像重建产生系统性偏差。这种偏差一旦进入科学结论,很难靠后期处理弥补。所以天文学界有一个共识:观测数据的清理必须在数据处理早期做扎实,越往后拖越难收拾。
注意,RFI检测和RFI抑制是两个不同的概念。检测是“标出哪些时频格被污染”,抑制是“从被污染的数据里恢复原始信号”。目前多数AI方案集中在检测阶段,抑制通常靠剔除或插值完成。别被名词绕晕,这篇聊的是检测和掩码生成。
1.2 动态谱图里能看到哪些典型干扰形态
在说AI怎么做之前,先要把数据形态说清楚。射电望远镜经过频谱仪处理后,会得到一个二维数据块,横轴是频率,纵轴是时间,每个像素的颜色代表该时频格上的功率强度。这个二维数据块就叫动态谱图,频率分辨率由谱仪带宽和通道数决定,时间分辨率由积分时间决定。
不同射频干扰源在动态谱图上呈现出完全不同的形态特征:
- 窄带持续干扰:固定在某个或某几个频率通道上持续存在,画出来就是一条或几条垂直亮线。常见来源是广播电台、通信基站的下行载波。
- 宽带瞬态干扰:在某个很短的时间内,跨越大量频率通道同时出现高能量,画出来是一条水平亮带。常见来源是电火花、雷达脉冲、闪电等。
- 扫频干扰:干扰源的中心频率随时间快速变化,在动态谱图上留下斜线或曲线轨迹。典型代表是某些雷达系统和无线设备的跳频机制。
- 准周期干扰:在时间轴上表现出明显的规律性,有时是周期性窄带,有时是周期性宽带,和设备的扫描机制强相关。
- 强瞬态干扰:持续时间极短,同时出现在全部或大部分频段,在动态谱图上表现为一条又细又亮的水平线,脉冲星信号有时会被误判成这种形态,这也是AI方案要注意的一个具体陷阱。
实际观测中往往是多种形态叠加在一起,比如某个频率通道始终有窄带干扰,同时某个时段又出现扫频干扰,偶尔还夹杂着脉冲型宽带干扰。这种多源叠加的情况,恰恰是传统单点阈值方法最容易失效的场景之一,因为单个像素的能量高低已经不足以判断它是不是干扰,需要结合它周围的时间和频率上下文来看。
2. 为什么经典RFI清除手段越来越力不从心
2.1 传统自动标记方法的天花板
早年间,RFI标记用的主要是统计阈值法。思路很简单:对每个频率通道或者每个时频像素,假设背景噪声服从高斯分布,算出均值和标准差,然后规定超过若干个标准差的像素就标记为干扰。这方法对强窄带射频干扰很管用,尤其当干扰信号比背景噪声高几十分贝的时候,阈值法几乎不用动脑子就能把它们挑出来。
但它有几个明显的天花板。第一,噪声分布并不总是高斯的,尤其是在低纬度和太阳活动强的时候,电离层扰动会让背景电平出现缓慢波动,整体分布出现厚尾,这时候基于高斯假设的阈值会产生系统性误判。第二,阈值法本质上是逐像素操作,它看不见相邻像素之间形成的“线”和“面”结构。一条只在动态谱图上占3个频率通道宽、幅度刚刚超过噪声2倍的弱窄带干扰,单看每个像素根本不满足标记条件,但把这一整条线放在一起看,特征就非常明显。第三,阈值法难以处理形态复杂的干扰,比如扫频干扰每个时刻出现在不同频率上,单时刻切面看它只是一个瞬时尖峰,放到全时段看才呈现一条斜线,逐点判断的算法天然抓不到这种全局结构。
还有一类经典方法基于频率通道的时域统计,比如对每个频率通道沿时间方向做方差或中值滤波,然后标记那些统计特征异常高的通道。这个方法对持续窄带干扰有效,但对瞬态宽带干扰非常不敏感,因为宽带干扰沿时间方向只持续很短,拉到整段时间里做统计时它的异常信号被平均稀释掉了。基于阵列天线互相关的方法效果好一些,利用多个天线之间信号传播路径的差异来区分天空信号和地面干扰,但这种方法需要阵列几何信息,对单天线望远镜完全不适用,而全世界大量的教学型、监测型单天线观测站恰恰是RFI问题最严重的群体。
人工目视检查的可靠性倒是高,但效率低到不可接受。一次普通的连续观测就能产生上万个时间切片,逐屏检查对操作者的精力和眼力都是巨大消耗。更关键的是,人工标记的主观性很强,不同人标出来的掩码边界可能差别很大,这本身就给后续科学分析引入了新的不确定性。观测效率一提上来,数据量呈指数级增长,纯人工这条路的尽头已经看到了。
2.2 用语义分割重新定义RFI检测任务
把RFI检测看成图像语义分割问题,是一个很自然的思路转换。既然动态谱图本身就是一张二维图像,那么“判断某个时频格是不是干扰”本质上就是“给图像的每个像素打标签”。卷积神经网络天然擅长提取二维结构特征,它能在小范围感受野里看到“竖线”和“斜线”,也能通过多层卷积扩大感受野看到更大范围的上下文模式。
用这种眼光看,模型输入不再是一个一维向量或一个孤立像素值,而是一个二维patch,输出也不再是一个标量分类结果,而是一张和输入尺寸一致的概率图。每个位置的值表示该时频格属于RFI的概率,再通过阈值和形态学后处理转成最终的0/1掩码。
这里有一个和常规图像分割不太一样的点:常见语义分割任务里,目标物体通常占据画面相当大的比例;而射电观测数据里的RFI像素占比通常很低,比如百分之一到百分之五。这意味着类别极度不均衡,如果直接用普通的交叉熵损失训练,模型很容易偏向把所有像素都预测为干净数据的“消极路线”,精度指标看着还行,实际RFI却一个都没抓住。
另一个特点是虚假的惩罚不对称。把真实宇宙信号误标为RFI,会在干净数据上开出天窗,损失的是宝贵积分时间;把真正的RFI漏过去不加掩码,则会把污染带进后续所有科学分析流程,损失更大且更难察觉。对科学家来说,漏检比误检可怕得多。设计训练目标和评估指标时,一定要把这一点牢牢记住。
2.3 为什么现在这个方向能真正落地了
AI检测RFI并不是一个新idea,十年前就有人尝试用神经网络做,但那时候落地阻力确实很大。现在情况完全不同,主要是三个条件成熟了。
第一个是算力和框架成熟。一块消费级GPU就能把中小规模的U-Net训得很好,开源框架把大量繁琐的梯度计算和分布式封装都处理好了,入门门槛大幅度下降。第二个是模拟数据工具成熟。RFI无法穷尽列举,但可以通过工具软件人为生成各种形态,把模拟干扰注入到真实观测的干净数据里,自动得到像素级标注,这是人工标注完全做不到的规模。第三个是观测数据格式标准化。主流射电数据处理都用FITS格式,这个格式读写方便,各种科学数据处理工具链非常完善,AI模型生成的掩码可以直接以FITS扩展表的形式存进数据文件,后续处理流程无缝衔接。
这三个条件一碰齐,RFI检测就从“论文里的演示案例”变成了“可以在数据处理流水线里跑起来的实用工具”。
3. 搭建一套AI射频干扰检测系统
3.1 先想清楚输入输出,再动手写代码
很多初学者一上来就找模型、调参数,第一个大坑就是没把需求定义清楚。我自己做这套东西时,最先和观测组一起敲定了几个关键定义。
输入是什么?输入是一段动态谱图数据块,形状大概是(时间像素数, 频率像素数)。采样频率、积分时间和频率通道数因望远镜后端不同差异很大,但二维性质是一样的。我建议在模型输入层面统一把数据切分成固定尺寸的patch,比如256×256。
输出是什么?输出必须是和输入对应的二维掩码,每个元素取值0或1。为了下游处理灵活,更好的是先输出连续的RRFI概率值,再由后处理脚本转成布尔掩码。这样如果某次观测想用更严格的阈值,不需要重新跑模型,直接在后处理改参数就行。
还有一个容易被忽略的定义:掩码对应的是“观测数据本身”还是“经过某种归一化之后的数据”。这一点一定要和后续的数据处理流程对齐。如果下一步是要做谱线拟合,掩码要能按原始坐标映射回FITS头部的频率和时间坐标;如果下一步是做数据压缩或大数据存储,可能要按不同时频格的分块位置来切分掩码。提前把坐标转换关系存好,能省后面大量对位调试的时间。
3.2 数据准备:模拟干扰和真实数据混合使用
RFI检测的数据标注是个大难题。真实观测数据里虽然有大量RFI,但我们并不总是知道每个像素到底该标成什么;人工标注的效率又太低。所以实践上广泛采用的做法是:用模拟干扰注入到已知干净的观测数据里,自动生成像素级标签。
具体流程大致是:先从历史数据中筛选一段相对干净的“底噪观测”,把强烈的宇宙信号源部分也一起考虑进去,然后在这段基础数据上按预设模型注入各种形态的RFI。模拟时要覆盖的干扰类型至少应该包括窄带持续、宽带瞬态、扫频、准周期这几类,每类还要分不同信噪比级别。我建议把干扰相对背景噪声的信噪比从大约3dB一直做到30dB以上,形成梯度覆盖。为什么要这么做?因为强干扰被阈值法轻松解决,AI模型的价值恰恰体现在中低信噪比区域,也就是人眼要仔细辨认才能看出来的那种干扰。
单纯用模拟数据训练出来的模型往往和真实数据存在域差异,因为模拟干扰只是对真实干扰的近似。为了缓解这个问题,可以从真实观测数据里截取“已知存在RFI”的片段加入训练集。但真实数据没有标签,怎么让模型学习呢?一个可行做法是先让训练好的模拟模型对真实数据输出一个粗略概率图,再由有经验的观测者抽查修正,把修正后的高置信度样本加入训练集做第二轮回填。这种“模拟为主、真实校准”的混合策略,是我试过最稳的路线。
3.3 模型选型:U-Net是少走弯路的稳妥选择
关于模型结构,我强烈建议从U-Net入门。不要一上来就上Transformer或者各种注意力大模型,RFI检测对实时性有要求,而且动态谱图的结构相对规则,U-Net这种编码器-解码器结构已经在大量医学图像分割任务里被验证过,稳定、易训、效果好。
U-Net的核心思想是:编码器通过下采样逐步提取高层语义特征,解码器通过上采样逐步恢复空间分辨率,同时用跳连接把编码器每一层的细节特征拼接到解码器的对应层,这样既保留了大范围结构信息,又不丢失局部边界的细节。对RFI检测来说,窄带干扰的“竖线结构”和扫频干扰的“斜线结构”都能被U-Net的卷积层有效捕捉。
下面是一个非常简化的U-Net骨架,可以参考这个思路按自己的数据规模调整通道数:
import torch.nn as nn class ConvBlock(nn.Module): def __init__(self, in_channels, out_channels): super().__init__() self.block = nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True), ) def forward(self, x): return self.block(x) class MinimalUNet(nn.Module): def __init__(self, in_channels=1, out_channels=1, base_channels=32): super().__init__() self.pool = nn.MaxPool2d(kernel_size=2, stride=2) self.enc1 = ConvBlock(in_channels, base_channels) self.enc2 = ConvBlock(base_channels, base_channels * 2) self.enc3 = ConvBlock(base_channels * 2, base_channels * 4) self.middle = ConvBlock(base_channels * 4, base_channels * 8) self.up3 = nn.ConvTranspose2d(base_channels * 8, base_channels * 4, kernel_size=2, stride=2) self.dec3 = ConvBlock(base_channels * 8, base_channels * 4) self.up2 = nn.ConvTranspose2d(base_channels * 4, base_channels * 2, kernel_size=2, stride=2) self.dec2 = ConvBlock(base_channels * 4, base_channels * 2) self.up1 = nn.ConvTranspose2d(base_channels * 2, base_channels, kernel_size=2, stride=2) self.dec1 = ConvBlock(base_channels * 2, base_channels) self.out_conv = nn.Conv2d(base_channels, out_channels, kernel_size=1) def forward(self, x): e1 = self.enc1(x) p1 = self.pool(e1) e2 = self.enc2(p1) p2 = self.pool(e2) e3 = self.enc3(p2) p3 = self.pool(e3) m = self.middle(p3) d3 = self.dec3(torch.cat([self.up3(m), e3], dim=1)) d2 = self.dec2(torch.cat([self.up2(d3), e2], dim=1)) d1 = self.dec1(torch.cat([self.up1(d2), e1], dim=1)) return self.out_conv(d1)有几个地方要特别提醒。第一,动态谱图的频率轴和时间轴物理意义不同,频率通道之间的连续性很强,时间方向上则可能存在积分造成的平滑,所以可以考虑使用非正方形卷积核,比如kernel_size=(4, 3),或者把patch的形状做成时间短、频率长的矩形。第二,池化层下采样会降低输出分辨率,U-Net通过跳连接缓解了这个问题,但为了保证频谱通道数的有效覆盖,patch的宽度(频率方向)不要太小,至少要包含几十个频率通道。
3.4 损失函数、训练策略和评估指标
训练模型的第一个要点是处理类别不均衡。我试过普通二分类交叉熵,训练出来的模型在验证集上准确率能到97%,但实际扫一遍观测数据发现RFI漏检严重。后面把损失函数换成了加权交叉熵和Dice损失的组合,效果立刻好了很多。加权做的事情就是让少数类(RFI像素)在损失函数里占据更大的权重,Dice损失则直接从掩码重叠度角度优化,对正负样本比例天然不敏感。
一个比较稳的损失组合是:
- 主损失:Dice损失,让模型把RFI区域的整体轮廓学准;
- 辅助损失:加权二进制交叉熵,让每个像素的分类压力更直接;
- 权重比:Dice占0.7,WBCE占0.3。
训练时数据增强要克制。很多人直接把图像分类里那套随机裁剪、旋转、翻转、颜色抖动全搬过来,结果发现RFI检测效果变差了。为什么?因为动态谱图的物理方向性太强了,90度旋转会把“频率轴”和“时间轴”互换,翻转也可能让扫频干扰从上升变成下降,这些变换在某些场景下破坏了真实物理规律。我建议只做小幅度高斯噪声、轻微缩放和少量平移增广,保留物理结构不变。
评估指标方面,不要被整体准确率骗了。建议至少看三个指标:
- IoU(交并比):预测掩码与真实掩码交叠面积占并集面积的比例,直接反映分割准确度;
- 召回率(Recall):真实RFI被检出的比例,这是科学应用最看重的指标;
- 精确率(Precision):检出的RFI有多少是真正的干扰,精度太低意味着把大量真实信号都删了。
实际评估时,可以在测试集里人为构造几个极端场景,比如只有弱窄带干扰的数据、只含扫频干扰的数据、含密集叠加干扰的数据,分别看指标表现。这样比只看整体平均更能发现问题。
4. 实操过程:从训练到对接观测数据流
4.1 数据切块和推理拼接的完整流程
实际对接观测数据时,动态谱图往往非常大,一整晚的观测数据可能有数千兆字节。模型不可能直接把整张图读进显存,所以必须用滑窗或分块的方式处理。我常用的推理流程是这样的:
- 从FITS文件里读取动态谱图,得到二维数据矩阵,单位通常是任意单位或某个校准后的通量密度。
- 把功率值转换成dB标度,然后做标准化。标准化的方式很关键,我习惯用2%到98%的分位数做截断,再线性映射到[0,1]。为什么不用全局最大最小值?因为强RFI尖峰会把整体动态范围拉得很大,导致正常信号部分被压缩到几乎看不见,模型学到的特征就不稳定。
- 把标准化后的数据切割成不重叠的256×256块,如果边界不够整块,用镜像填充方式补足。
- 把每个patch批量送入模型,得到对应的概率图。
- 把所有patch的概率图按原位拼接回去,恢复成与原始数据相同尺寸的完整概率图。
- 对概率图做后处理:用0.5阈值二值化,然后用一个小尺寸的中值滤波(比如3×3)去掉孤立噪点,再用连通域分析过滤掉面积过小的掩码区域。
- 把最终布尔掩码按原始坐标写回FITS文件,作为新的扩展表存起来,或者在后续处理主流程中直接引用。
这个流程里有一个位置非常容易踩坑:标准化。不同望远镜的数据数值范围差异很大,同一望远镜不同观测频段、不同积分时间的数值分布也不一样。如果模型在一个数据集上标准化后训练,在另一个数据集上直接套用同样的归一化参数,效果通常会明显下降。实操上最好是在推理阶段也用滑窗方式重新计算每个patch的分位数,而不是用整个数据块的分位数,虽然计算量大了点,但稳定性好很多。
4.2 训练参数记录和一轮迭代实测
以256×256的patch输入为例,我把一次比较完整的训练过程记录下来供参考。训练集大约包含两万张patch,其中四分之三来自模拟注入数据,四分之一来自真实观测数据的人工复核样本。优化器用Adam,初始学习率1e-3,跑了大概60个epoch,在20个epoch时将学习率降到1e-4。显存占用在11GB左右,一个中等规模的GPU就能跑完。
第一轮训练出来的模型在模拟测试集上IoU约为0.81,召回率约0.93,看起来好像能用了。但把它放到一段真实观测数据上跑了一遍,立刻发现问题:模型对窄带持续干扰识别得很好,但对那种强度只有噪声水平两三倍、同时叠加在非稳态背景上的宽带弱干扰,几乎完全漏检。后来重新检查了训练集的构造,发现模拟宽带干扰时信噪比都设得偏高,等于训练时根本没让模型见过“倒挂”的难例。我调整了模拟参数分布,把大量宽带干扰的信噪比压到6dB以下之后重新训练,召回率很快就上去了。
这轮迭代让我印象很深:看似是模型不聪明,实际是数据分布偏了。RFI检测里很多“模型不行”的结论,最后都指向训练数据和真实场景不一致的问题。如果发现模型在验证集上很高、在真实数据上拉胯,优先怀疑数据分布,别第一时间去调网络结构。
4.3 把模型接进准实时流水线的经验
观测站的实际数据处理流水线通常不是为深度学习准备的,它往往是一个由Shell脚本、Python脚本和C程序串起来的链式流程。要把AI模型嵌进去,关键是处理好依赖环境和数据接口。
我的做法是把模型推理封装成一个独立的Python服务,对外只提供“输入FITS文件路径+输出FITS文件路径”的命令行接口。管线和模型之间通过文件系统交互,不做进程内耦合。这样即使模型框架版本升级、推理代码改得面目全非,管线其他部分完全不用动。第一次接进某观测站的离线处理流程时,我专门做了个压测,处理的延时大约平均每幅动态谱图需要4到7秒。对离线数据处理来说完全够用,但如果要做观测中的实时监测提醒,这个速度还需要配合GPU加速和批处理优化才能跟上实时节奏。
性能优化有一个很直接的技巧:把连续多幅动态谱图堆叠成batch同时推理,比单张逐次推理可以快好几倍。这是深度学习的底层的批量并行特性,实测下来吞吐量至少翻倍。另外,如果观测数据的时间分辨率很高,可以先做时间方向上的降采样。
5. 常见问题与排查技巧速查表
5.1 高频问题速查
在实际开发和调试过程中,下面的问题我几乎每轮迭代都会遇到,整理成速查表方便大家对照。
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 训练损失降到很低但真实数据表现差 | 训练集和真实数据存在域差异 | 增加真实数据样本,重新检查归一化参数 |
| 漏检大量弱宽带干扰 | 训练集中该类样本信噪比分布偏太高 | 把模拟干扰的信噪比往低端压,加入难例挖掘 |
| 把宇宙连续谱线误判成RFI | 训练数据缺少真实天体谱线形态 | 在干净数据中混合强谱线和连续谱源,让模型见过这种形态 |
| 预测掩码边缘破碎不连续 | 阈值后缺少形态学后处理,或输入patch过小 | 增加小尺寸中值滤波,适当增大输入patch |
| 同一套模型换到另一台望远镜效果变差 | 数值范围、频率分辨率、积分时间不同 | 针对新设备重新做标准化,用少量标注数据微调 |
| GPU显存不足 | patch过大或batchsize过高 | 减小patch到128×128,开启混合精度训练 |
| 扫频干扰只能检出中间一段 | patch边界截断了斜线结构,模型不认识不完整形态 | 使用重叠滑窗推理,后处理时跨patch做连通性合并 |
| 模型训练时loss曲线剧烈震荡 | 学习率过高或数据增强太激进 | 降低学习率,删除可能破坏物理结构的增强操作 |
5.2 几条值得抄走的工程经验
第一,漏检代价大于误检,设计模型时就要体现这种态度。比如把输出概率的阈值默认调到0.3而不是0.5,再用形态学后处理过滤明显误检。这样做的效果是:宁可多删一点疑似干扰的时频格,也要保证科学数据不被污染。
第二,对频率分辨率不同的数据,不要硬套同一个模型。如果训练时用的是高频谱分辨率的数据,转到低频谱分辨率的望远镜上推理时,要么先做频率重采样对齐,要么用少量新数据做微调。我见过有人把这个问题甩给“通用模型”幻想,结果在实际数据上杀得头破血流。
第三,推理阶段的置信度信息不要丢。除了输出布尔掩码,可以把概率图也一并存下来。这样如果后期发现某个区域的掩码明显误判,可以直接翻概率图追溯,而不需要重新跑一遍模型。这个习惯在很多工程场景里都帮我快速定位了问题。
第四,对观测数据进行RFI清理时,保留掩码的版本记录。也就是说每次跑完检测,把模型版本、输入数据版本、后处理参数全部写进输出FITS文件的历史记录里。科学数据讲究可复现,这个细节对后续发表结果、复核分析非常关键。
第五,无论模拟数据做得多逼真,真实数据永远才是试金石。建议在项目一开始就预留一小段“从没进过训练集”的真实观测数据作为最终验收集,只在最后阶段才拿出来评一次,避免无意中把验收集都调过拟合了。
把整套链路跑通之后,我的体会是RFI检测本质上并不是一个模型结构问题,而是一个数据工程和评估体系问题。U-Net这样的基础网络其实已经足够强大,真正决定效果好坏的,是你有没有把模拟干扰的信噪比分布做对、有没有把标准化方式统一好、有没有把漏检和误检的代价权衡清楚。最后再分享一个小技巧:如果观测数据非常充足,可以为重要科学目标专门定制一套“目标感知”的RFI检测器,把目标源的先验位置信息作为额外通道输入给模型,往往能在极弱信号场景下额外提升一截召回率。这个方向我还在继续实验,后面有结论再单独写一篇。