You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于无监督KNN的姓名匹配问题求解入门思路咨询

无监督KNN做姓名匹配的入门指导(结合你的合成数据)

嘿,针对你用无监督KNN做姓名匹配的需求,结合你提供的合成数据,我给你梳理一套入门落地的思路,一步步来~

一、核心思路:无监督KNN不需要外部标签,靠「相似性集群」打分

你没有外部标签,所以不用像有监督KNN那样训练分类器,而是利用姓名之间的相似性——把每个姓名看作一个样本,找和目标姓名最像的K个“邻居”,再基于邻居和待匹配姓名的相似性,给每一对(alertname, custname)计算匹配分数。

二、第一步:数据预处理+特征/相似度准备

姓名是短文本,直接用字符串没法让KNN计算,得先转成可量化的相似度或特征向量,这里给你两种常用方案:

1. 基于字符串相似度(适配你熟悉的模糊匹配逻辑)

用Jaro-Winkler、归一化编辑距离这类专门针对短字符串的相似度指标,直接计算两两姓名的相似度(得分0-1,越接近1越像),再转成距离(距离=1-相似度)给KNN用。

2. 基于字符级特征向量

把姓名拆成字符n-gram(比如wilson拆成wi/il/ls/so/on),用TF-IDF把每个姓名转换成向量,再用余弦相似度计算距离。这种方式能捕捉更多字符组合的相似性,对带特殊符号或空格的姓名(比如scott-smith)更友好。

三、第二步:用Python实现无监督KNN打分(拿你的数据举例)

我用Python写个可运行的示例,直接适配你的合成数据:

1. 先加载并整理数据

先把你的数据转成DataFrame,特意处理了scott smith这种带空格的姓名:

import pandas as pd

# 你的原始数据(修正了分隔问题)
data = [
    ["0", "wlison", "wilson"],
    ["1", "dais", "said"],
    ["2", "4dams", "adams"],
    ["3", "ad4ms", "adams"],
    ["4", "ad48s", "adams"],
    ["5", "smyth", "smith"],
    ["6", "smythe", "smith"],
    ["7", "gillan", "gillan"],
    ["8", "gilen", "gillan"],
    ["9", "scott-smith", "scottsmith"],
    ["10", "scott smith", "scottsmith"],
    ["11", "perrson", "person"],
    ["12", "persson", "person"]
]
df = pd.DataFrame(data, columns=["index", "alertname", "custname"])
df["index"] = df["index"].astype(int)

2. 方案一:Jaro-Winkler相似度+KNN

from sklearn.neighbors import NearestNeighbors
import jellyfish

# 收集所有唯一姓名
all_names = pd.concat([df["alertname"], df["custname"]]).unique()

# 构建两两姓名的相似度矩阵
def build_similarity_matrix(names):
    n = len(names)
    sim_mat = [[0.0]*n for _ in range(n)]
    for i in range(n):
        for j in range(n):
            if i == j:
                sim_mat[i][j] = 1.0
            else:
                sim_mat[i][j] = jellyfish.jaro_winkler_similarity(names[i], names[j])
    return sim_mat

sim_mat = build_similarity_matrix(all_names)
dist_mat = 1 - sim_mat  # 转成KNN需要的距离矩阵

# 初始化KNN模型
nn_model = NearestNeighbors(metric="precomputed")
nn_model.fit(dist_mat)

# 定义打分函数:结合直接相似度+邻域平均相似度
def calculate_match_score(alert_name, cust_name, k=3):
    # 找到alertname在姓名列表中的索引
    alert_idx = list(all_names).index(alert_name)
    # 取K+1个近邻(+1是因为包含自己,后续去掉)
    _, neighbor_indices = nn_model.kneighbors([dist_mat[alert_idx]], n_neighbors=k+1)
    neighbors = all_names[neighbor_indices[0][1:]]
    
    # 计算直接相似度
    direct_sim = jellyfish.jaro_winkler_similarity(alert_name, cust_name)
    # 计算custname与邻居的平均相似度
    neighbor_sims = [jellyfish.jaro_winkler_similarity(cust_name, n) for n in neighbors]
    avg_neighbor_sim = sum(neighbor_sims) / len(neighbor_sims)
    
    # 综合打分(可按需调整权重)
    final_score = 0.7 * direct_sim + 0.3 * avg_neighbor_sim
    return round(final_score, 3)

# 给每行添加匹配分数
df["match_score"] = df.apply(lambda row: calculate_match_score(row["alertname"], row["custname"]), axis=1)

3. 方案二:字符n-gram向量+KNN

如果觉得字符串相似度不够灵活,可以试试特征向量方案:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.neighbors import NearestNeighbors
from sklearn.metrics.pairwise import cosine_similarity

# 用字符2-gram和3-gram构建TF-IDF向量
vectorizer = TfidfVectorizer(analyzer="char", ngram_range=(2, 3))
name_vectors = vectorizer.fit_transform(all_names)

# 初始化KNN模型(用余弦距离)
nn_model_vec = NearestNeighbors(metric="cosine")
nn_model_vec.fit(name_vectors)

def calculate_match_score_vec(alert_name, cust_name, k=3):
    # 转换为向量
    alert_vec = vectorizer.transform([alert_name])
    cust_vec = vectorizer.transform([cust_name])
    
    # 直接余弦相似度
    direct_sim = cosine_similarity(alert_vec, cust_vec)[0][0]
    # 找K个近邻
    _, neighbor_indices = nn_model_vec.kneighbors(alert_vec, n_neighbors=k+1)
    neighbor_vecs = name_vectors[neighbor_indices[0][1:]]
    
    # 邻居与custname的平均相似度
    neighbor_sims = cosine_similarity(cust_vec, neighbor_vecs)[0]
    avg_neighbor_sim = neighbor_sims.mean()
    
    final_score = 0.7 * direct_sim + 0.3 * avg_neighbor_sim
    return round(final_score, 3)

df["match_score_vec"] = df.apply(lambda row: calculate_match_score_vec(row["alertname"], row["custname"]), axis=1)

四、优化建议

  • 调整K值:K太小容易被噪声干扰,太大可能混入不相关姓名,建议试试K=3、5、7,看哪组结果更符合你的预期。
  • 权重调整:你可以根据需求修改直接相似度和邻域相似度的权重,比如如果更信任直接匹配,就把直接相似度的权重调高点。
  • 相似度指标替换:除了Jaro-Winkler,还可以试试Levenshtein编辑距离(归一化后)、Damerau-Levenshtein距离,找最适合姓名匹配的指标。

无监督KNN的核心就是利用数据本身的相似性结构,完全不需要外部标签,刚好贴合你的需求~

内容的提问来源于stack exchange,提问作者user2906657

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:29:24