KNN算法中如何合理处理作为特征的音乐调(音阶)
KNN中音乐调(音阶)类循环分类特征的处理方法
直接对循环特征做1-N线性序数编码的核心问题,是强行把环形分布的特征映射到了线性数轴上,完全破坏了真实的相邻关系:以24个大小调为例,编号24的调和编号1的调实际为相邻关系,但线性编码下二者距离为23,远大于编号7和8的距离1,完全不符合KNN基于距离计算相似度的逻辑。
这类问题和“不能给美国州名这类无内在线性顺序的分类特征直接做1-50编号”的本质完全一致,根据是否需要保留调之间的音程相似性,可以选以下两种成熟方案处理:
方案1:纯分类特征处理(通用分类特征方案,等价于州名处理逻辑)
如果你的场景不需要引入音乐理论中“相邻调相似度更高”的先验,只把调作为独立的无顺序分类标签,直接使用独热编码(One-Hot Encoding)即可:
- 若直接处理24个大小调:生成24个取值为0/1的特征列,每一列对应当前样本是否属于某一个特定调
- 若提前拆分了大小调、音名两个维度:大小调仅2类,可直接用0/1二元值表示(大调=0/小调=1);音名维度生成12个0/1的独热列对应12个音级即可
该方案下任意两个不同调的特征距离完全相等,不会区分近关系调和远关系调的差异,适合不需要用到音乐领域先验的通用场景。
方案2:循环编码(音乐场景推荐,完美保留环形相邻关系)
针对音名、调这类固定周期的循环特征,正余弦循环编码是最适配KNN距离计算的方案,可以100%复现环形结构下的真实距离:
- 先做基础数值映射:将12个音名按顺序映射为0-11的整数(例:C=0,升C=1,D=2……B=11);大小调单独拆为二元特征即可。如果不拆分大小调,直接把24个大小调按顺序映射为0-23的整数即可。
- 用三角函数把线性数值映射到二维单位圆上,替换原来的线性编号特征,以12个音名的处理为例,代码如下:
注意:必须同时保留正弦、余弦两个特征,单维度三角函数值无法唯一确定环形上的位置,会出现不同音名对应相同特征值的错误。import numpy as np # pitch_val 为0-11的音名映射值 pitch_feature_sin = np.sin(2 * np.pi * pitch_val / 12) pitch_feature_cos = np.cos(2 * np.pi * pitch_val / 12) - 编码完成后,两个特征构成的二维空间里,12个音名均匀分布在单位圆上,任意两个音的欧氏距离完全符合实际环形相邻关系:比如映射值为0的C和映射值为11的B的距离,和C与升C(映射值1)的距离完全相等,彻底解决线性编码的距离错位问题。如果处理24个大小调的整体循环,只需要把代码里的除数12替换为24即可。
避坑提示
- 任何调整线性编号顺序的方案都无法解决首尾相邻的问题,只要是单维度的线性编码,就必然存在距离计算偏差,不要在这类问题上浪费时间调整编号映射规则。
- 循环编码仅对KNN、SVM、线性模型这类依赖特征空间距离/尺度的模型有效,树模型对特征的单调顺序不敏感,用独热编码的效果差异不大。
内容的提问来源于stack exchange,提问作者Shy Cohen
相关产品推荐
相关产品推荐

