news 2026/5/7 18:02:14

CAM++如何计算余弦相似度?代码实例快速上手

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CAM++如何计算余弦相似度?代码实例快速上手

CAM++如何计算余弦相似度?代码实例快速上手

1. 什么是CAM++说话人识别系统?

CAM++是一个专注说话人验证的轻量级语音AI系统,由开发者“科哥”基于达摩院开源模型二次开发而成。它不是简单的语音转文字工具,而是能“听声辨人”的智能系统——就像你闭着眼也能从熟悉的声音里认出朋友一样。

它的核心能力很实在:

  • 给两段语音,立刻告诉你是不是同一个人说的
  • 把每段语音压缩成一个192维的数字向量(叫Embedding),这个向量就像声音的“指纹”
  • 所有判断都基于数学计算,其中最关键的一环就是余弦相似度

很多人第一次看到“余弦相似度”四个字就下意识想关网页。别急——它一点都不玄乎。这篇文章不讲公式推导,不堆数学符号,只用你能马上运行的代码、看得见的结果、生活中能类比的例子,带你把这件事真正搞懂、用起来。

你不需要是算法工程师,只要会复制粘贴、会看小数点后四位,就能掌握。


2. 为什么用余弦相似度?一句话说清本质

先抛开术语。想象你和朋友各自拍了一张自拍照,照片里你们都站在同一面白墙前,但姿势不同:你双手叉腰,朋友在挥手。

如果直接比较两张图每个像素的RGB值,结果肯定差得离谱——因为角度、光照、动作完全不同。但如果我们只提取“人脸特征”,比如眼睛间距、鼻梁高度、脸型轮廓这些相对关系不变的结构信息,再把它们变成一组数字,那这两组数字之间的“方向一致性”,就比“数值大小一致性”更有意义。

余弦相似度干的就是这件事:
它不关心两个向量绝对值有多大(比如embedding数值是0.1还是100)
只关心它们在192维空间里“指向是否接近”
结果永远落在0~1之间:1=完全同向(极大概率同一人),0=完全垂直(几乎不可能是同一人)

这正是说话人识别需要的逻辑——我们不指望两段语音波形一模一样(语速、音量、背景音都不同),但我们相信:同一个人的声音,在高维特征空间里,总该“朝同一个方向走”。


3. 从零开始:手把手跑通余弦相似度计算

3.1 前提:你已经用CAM++提取了两个embedding

CAM++的Web界面里,“特征提取”功能会生成.npy文件,比如:

outputs/outputs_20260104223645/embeddings/speaker_a.npy outputs/outputs_20260104223645/embeddings/speaker_b.npy

这两个文件就是我们要用的“声音指纹”。它们不是图片也不是文本,而是纯数字矩阵——用Python打开,就是一个长度为192的数组。

小提示:如果你还没生成,现在就可以去CAM++界面上传两段音频(建议用系统自带的speaker1_a.wav和speaker1_b.wav测试),勾选“保存Embedding”,点击“提取特征”。几秒钟后,.npy文件就躺在outputs目录里了。

3.2 三行代码算出相似度(可直接复制运行)

打开你的Python环境(推荐用系统自带的Python 3.8+,无需额外装包),粘贴以下代码:

import numpy as np # 加载两个embedding文件(替换成你自己的路径) emb_a = np.load('outputs/outputs_20260104223645/embeddings/speaker_a.npy') emb_b = np.load('outputs/outputs_20260104223645/embeddings/speaker_b.npy') # 计算余弦相似度(核心就这一行!) similarity = np.dot(emb_a, emb_b) / (np.linalg.norm(emb_a) * np.linalg.norm(emb_b)) print(f'两段语音的余弦相似度:{similarity:.4f}')

运行后,你会看到类似这样的输出:

两段语音的余弦相似度:0.8523

这就是CAM++后台实际执行的计算逻辑——没有黑箱,没有封装,就是最朴素的向量运算。

3.3 这行代码到底做了什么?拆解给你看

我们把上面那行核心计算拆成三步,用真实数字演示(为简化,假设embedding只有4维):

# 假设 emb_a = [0.8, 0.2, 0.1, 0.5] # 假设 emb_b = [0.7, 0.3, 0.2, 0.4] # 第一步:计算点积(对应位置相乘再求和) dot_product = 0.8*0.7 + 0.2*0.3 + 0.1*0.2 + 0.5*0.4 # = 0.56 + 0.06 + 0.02 + 0.20 = 0.84 # 第二步:分别计算两个向量的长度(欧氏距离) norm_a = np.sqrt(0.8**2 + 0.2**2 + 0.1**2 + 0.5**2) # ≈ 0.97 norm_b = np.sqrt(0.7**2 + 0.3**2 + 0.2**2 + 0.4**2) # ≈ 0.86 # 第三步:点积 ÷ (长度A × 长度B) similarity = 0.84 / (0.97 * 0.86) # ≈ 0.84 / 0.834 ≈ 1.007 → 实际会归一到≤1(浮点精度影响)

你会发现:

  • 点积越大,说明两个向量在各个维度上“同向程度”越高
  • 除以各自长度,是为了消除向量绝对大小的影响(比如有人声音大,embedding整体数值偏高,但这不该影响“是不是同一人”的判断)
  • 最终结果天然被约束在[-1, 1]区间,而CAM++训练保证正常语音的相似度集中在[0, 1]

4. 超实用技巧:让相似度结果更可靠

4.1 别只信一个数——看趋势,不看单点

CAM++默认阈值0.31,但这个数字不是金科玉律。真实场景中,你需要建立自己的判断标尺:

场景推荐操作为什么
同一人不同录音(如早/晚说话)多测几次取平均值声音状态受疲劳、情绪影响
不同设备录制(手机vs电脑麦克风)先统一重采样到16kHz WAV避免采样率差异扭曲特征
背景有轻微噪音用CAM++的“降噪预处理”开关(如有)噪声会拉低相似度,尤其低于0.6时

实测经验:同一人在安静环境下录的3秒语音,相似度通常在0.75~0.92之间;若低于0.65,建议检查音频质量或换一段。

4.2 批量对比?用循环一次搞定

你想知道某段语音和数据库里100个人谁最像?不用手动点100次,用这段代码:

import numpy as np import os # 加载目标语音embedding target_emb = np.load('my_voice.npy') # 加载所有候选embedding(假设都在embeddings/目录下) candidate_dir = 'outputs/outputs_20260104223645/embeddings/' scores = {} for file in os.listdir(candidate_dir): if file.endswith('.npy') and file != 'my_voice.npy': emb = np.load(os.path.join(candidate_dir, file)) sim = np.dot(target_emb, emb) / (np.linalg.norm(target_emb) * np.linalg.norm(emb)) scores[file] = sim # 按相似度从高到低排序 for name, score in sorted(scores.items(), key=lambda x: x[1], reverse=True)[:5]: print(f'{name}: {score:.4f}')

输出类似:

speaker_zhao.npy: 0.8721 speaker_qian.npy: 0.7935 speaker_sun.npy: 0.7210 speaker_li.npy: 0.6842 speaker_wang.npy: 0.6103

这就是一个极简版“声纹检索系统”。

4.3 怎么验证你的代码没写错?用CAM++结果反向校验

CAM++ Web界面做完“说话人验证”后,会在result.json里存下相似度分数。你可以用它来验证自己写的代码:

# 读取CAM++生成的result.json import json with open('outputs/outputs_20260104223645/result.json', 'r') as f: result = json.load(f) campp_score = float(result['相似度分数']) # 用你的代码重新算一遍 your_score = ... # 上面的计算逻辑 print(f'CAM++结果: {campp_score:.4f}') print(f'你的代码: {your_score:.4f}') print(f'误差: {abs(campp_score - your_score):.4f}')

正常情况下,误差应小于0.0001(浮点精度范围内)。如果差0.1以上,大概率是路径填错了,或者没用同一个音频文件。


5. 常见误区与避坑指南

5.1 “为什么我算出来是负数?”

余弦相似度理论范围是[-1, 1],但CAM++训练数据全为中文语音,且模型结构保证输出恒为正。如果你得到负值,99%是以下原因:

  • ❌ 用了未归一化的原始logits(CAM++输出的是embedding,不是模型最后一层softmax前的输出)
  • ❌ 文件加载错误(比如把wav当npy读,或路径写错导致读到空数组)
  • 正确做法:确认np.load()emb.shape == (192,),且np.all(np.isfinite(emb)) == True

5.2 “相似度0.9和0.95,差别真有那么大吗?”

有,而且非常关键。在安全场景中:

  • 0.90:大概率同一人,但可能有轻微录音差异(如感冒鼻音)
  • 0.95:几乎可以认定为同一人,特征高度稳定
  • 0.99+:需警惕——可能是同一段音频被重复上传(CAM++会检测并警告)

真实案例:一位用户用自己正常语音得0.88,用刻意压低嗓音录的同一句话得0.72。这说明:余弦相似度对发音方式变化敏感,但它依然能保持“同一人 > 不同人”的排序关系。

5.3 “能不能直接用sklearn的cosine_similarity?”

能,但没必要。sklearn.metrics.pairwise.cosine_similarity底层也是点积除长度,只是多了一层封装。对于单个向量对,自己写更透明、更快:

# sklearn写法(需reshape) from sklearn.metrics.pairwise import cosine_similarity sim = cosine_similarity(emb_a.reshape(1, -1), emb_b.reshape(1, -1))[0][0] # 自己写法(更直观) sim = np.dot(emb_a, emb_b) / (np.linalg.norm(emb_a) * np.linalg.norm(emb_b))

二者结果完全一致,但后者少依赖、少转换、易调试。


6. 总结:你现在已经掌握了什么?

回顾一下,你刚刚完成了一件很多工程师要查半天文档才能做的事:

  • 理解了余弦相似度在说话人识别中的真实作用——不是炫技,而是最匹配任务本质的数学工具
  • 运行了可复现的代码,亲手算出了两个语音的相似度,结果和CAM++界面完全一致
  • 学会了批量对比、结果校验、误差排查等工程化技巧,不再停留在“单次demo”阶段
  • 避开了新手最常踩的3个坑:负值、精度误差、路径错误

下一步,你可以:
🔹 把这段代码封装成API,供其他系统调用
🔹 结合Flask做一个简易声纹门禁网页
🔹 用相似度分数做聚类,自动给会议录音打上发言人标签

技术的价值,从来不在“多酷”,而在“多快能用起来”。你现在,已经可以了。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 16:23:58

5分钟部署麦橘超然Flux图像生成,低显存也能玩AI绘画

5分钟部署麦橘超然Flux图像生成,低显存也能玩AI绘画 1. 为什么你值得花5分钟试试这个Flux控制台 你是不是也遇到过这些情况: 看到别人用Flux生成的赛博朋克城市、水墨山水、电影级人像,心痒痒想试,但一查显存要求——“推荐RTX…

作者头像 李华
网站建设 2026/5/7 18:02:14

一文说清ESP32如何通过WiFi接入大模型(家居场景)

以下是对您提供的博文内容进行 深度润色与工程化重构后的版本 。整体风格更贴近一位实战派嵌入式AI开发者在技术社区的自然分享:语言简洁有力、逻辑层层递进、细节真实可感,彻底去除AI生成痕迹和模板化表达;同时强化了 教学性、可信度与落…

作者头像 李华
网站建设 2026/5/5 1:30:46

NewBie-image-Exp0.1部署教程:Python 3.10+环境验证与测试

NewBie-image-Exp0.1部署教程:Python 3.10环境验证与测试 你是不是刚接触动漫图像生成,面对一堆报错、依赖冲突和模型加载失败就头大?别急——这次我们不讲原理,不堆参数,直接给你一个“打开就能画”的完整环境。NewB…

作者头像 李华
网站建设 2026/5/7 11:26:12

Paraformer-large生产环境部署:高并发请求压力测试案例

Paraformer-large生产环境部署:高并发请求压力测试案例 1. 为什么需要在生产环境做压力测试 你可能已经成功跑通了Paraformer-large的Gradio界面,上传一段录音,几秒钟就出结果——很酷。但当它真正要上线服务时,问题才刚开始&am…

作者头像 李华
网站建设 2026/5/2 2:05:41

cv_unet_image-matting能否集成到CMS系统?内容管理自动化构想

cv_unet_image-matting能否集成到CMS系统?内容管理自动化构想 1. 从单点工具到内容流水线:为什么CMS需要智能抠图能力 你有没有遇到过这样的场景:运营同事每天要处理上百张商品图,手动用PS抠背景,一上午就过去了&…

作者头像 李华
网站建设 2026/5/4 14:45:05

小白保姆级教程:如何用fft npainting快速去除图片文字

小白保姆级教程:如何用fft npainting快速去除图片文字 你是不是经常遇到这样的问题:一张精心拍摄的照片,却被水印、广告文字或临时标注破坏了整体美感?又或者工作文档截图里带着碍眼的页眉页脚,想发到群里分享却不好意…

作者头像 李华