取消声纹二次识别兜底(用户拍板):精确匹配制——判定链/滚动检索两处 nearestAttribution 硬归入全部移除,不中即新(不再对'最相似的人'做二次并人);nearestAttribution/nearestMargin/rollingFallbackMin 删除;verdictFallbackMin 专职切后命名的确认无人线;VERDICT-SCORE 日志去 fallback 字段;残余同类'兜底'仅剩:残料复用(嵌入算不出时的输入兜底)与灰带收编(不强制归属)

This commit is contained in:
liwenhao2 2026-09-16 17:18:13 +08:00
parent 12f008a4c1
commit 064fab39e0
1 changed files with 8 additions and 32 deletions

View File

@ -194,12 +194,9 @@ class MainActivity : AppCompatActivity() {
// 0.14~0.19,0.22 取安全带中枢——深低首票即切(无缝插话 4~6s→3~4s);
// 0.22~0.35 浅低维持连续两票,毛刺照旧被误断防护拦下
private val deepLowSwitch = 0.18f
// 最近邻兜底(P1 收紧,2026-09-11):精确匹配不中→top≥0.42 且领先第二名
// ≥0.06 才归入最相似身份;模糊判新人(修"错并到别人行")
// 切后命名的"确认无人"线(2026-09-16):纯净窗最好分 <0.42 → 判新面孔,
// 发新号。二次识别兜底(最近邻硬归入)已按用户拍板取消
private val verdictFallbackMin = 0.42f
private val nearestMargin = 0.06f
// 滚动检测 fast 空间的兜底阈值(仅影响换行标签显示,endpoint 仍权威)
private val rollingFallbackMin = 0.36f
// P3 防漂移:只有高置信同人窗(≥0.5)才吸收进参考,权重也降到 15%
private val emaAbsorbMin = 0.50f
// 滚动检测在 fast 空间查"这人是谁"仅用于**显示标签**,阈值放宽到 0.4;
@ -1145,12 +1142,10 @@ class MainActivity : AppCompatActivity() {
snap.copyOfRange(snap.size - 32000, snap.size) else snap
SpeakerRecognition.computeEmbeddingFast(tailSlice, sampleRateInHz)
} else null
// 精确匹配制(2026-09-16 用户拍板取消声纹二次识别兜底):建中即得,
// 不中即新——不再对"最相似的人"做二次硬归入(错并之源)
val label = if (emb == null) null else
(SpeakerRecognition.registry.search(emb, speakerThreshold)
// 兜底②(P1 收紧):最近邻+优势差;模糊→注册新人
?: nearestAttribution(
emb, SpeakerRecognition.registry, verdictFallbackMin
))
SpeakerRecognition.registry.search(emb, speakerThreshold)
?: run {
// 开口即查已在 fast 空间给新人占过号 → 优先复用,防同一
// 人双号(开口 S0 / 结算 S1);命中即顺带补全准确空间镜像
@ -1170,7 +1165,7 @@ class MainActivity : AppCompatActivity() {
Log.i(TAG, "VERDICT-SCORE sid=$sid" +
" best=" + "%.3f".format(
SpeakerRecognition.registry.bestScore(emb)) +
" thr=$speakerThreshold fallback=$verdictFallbackMin" +
" thr=$speakerThreshold" +
" label=$label dur=${snap.size / sampleRateInHz}s")
}
uiHandler.post { confirmSpeaker(sid, label, emb, fastEmb) }
@ -1546,18 +1541,6 @@ class MainActivity : AppCompatActivity() {
return FloatArray(n) { a[it] * 0.85f + b[it] * 0.15f }
}
// 最近邻归属(P1):top≥minScore 且对第二名优势≥nearestMargin 才归入,
// 两个已注册者难分高下时返回 null → 按新人处理(宁多勿错)
private fun nearestAttribution(
emb: FloatArray, registry: SpeakerRegistry, minScore: Float,
): String? {
val r = registry.nearestRank(emb)
val name = r.name ?: return null
return if (r.score >= minScore && r.score - r.secondScore >= nearestMargin) {
name
} else null
}
private fun flattenSamples(): FloatArray = flattenPrefix(samplesBuffer.size)
// fresh copy of the first k chunks — never hand the shared buffer to threads
@ -2305,20 +2288,13 @@ class MainActivity : AppCompatActivity() {
}
}
// 滚动窗口查"这是谁":有 fast 模型时查 fast 镜像(0.4 宽松阈值,仅供显示);
// 没有 base 文件时滚动检测本身回退在准确模型空间 → 直查准确 registry。
// 修复:base 缺失时镜像库恒空、换行标签全 null、误断防护失效的问题。
// 滚动窗口查"这是谁"(2026-09-16 精确匹配制):建中即得,不中即 null,
// 不做二次识别。单 CAM++ 下 fastReady=false,走准确注册表(0.5 阈值)
private fun rollingSpeakerSearch(emb: FloatArray): String? =
if (SpeakerRecognition.fastReady())
SpeakerRecognition.fastRegistry.search(emb, fastSearchThreshold)
?: nearestAttribution(
emb, SpeakerRecognition.fastRegistry, rollingFallbackMin
)
else
SpeakerRecognition.registry.search(emb, speakerThreshold)
?: nearestAttribution(
emb, SpeakerRecognition.registry, verdictFallbackMin
)
// ③ 滚动检测查到了已知人、但当前行还没有归属(占位"未识别"):
// 直接把活跃归属采纳为他,等 endpoint 权威判定给行冠名,不再多切一行