机器学习实战:学习向量量化(LVQ)在多分类任务中的应用与优化

发布时间:2026/7/4 15:48:19

机器学习实战:学习向量量化(LVQ)在多分类任务中的应用与优化 1. 学习向量量化LVQ算法入门指南第一次听说学习向量量化LVQ时我也是一头雾水。这名字听起来就让人发怵但实际用起来才发现它比想象中简单得多。LVQ本质上是一种原型聚类算法特别适合处理分类问题。和K-Means这种无监督学习不同LVQ最大的特点就是会利用样本的真实标签来辅助训练这让它在分类任务中表现更加出色。想象一下你是个水果摊老板要教新员工区分西瓜的好坏。传统方法可能是让员工自己摸索规律无监督学习而LVQ则是你亲自示范先挑几个典型的好瓜和坏瓜作为样板然后让员工拿其他瓜和这些样板对比学习。这种有指导的学习方式就是LVQ的核心思想。LVQ的工作流程其实很直观初始化阶段从每个类别中随机选几个样本作为原型向量就是那些样板瓜训练阶段随机选个样本找到离它最近的原型向量调整阶段如果样本和原型的标签一致就让原型更靠近这个样本如果不一致就让原型远离它重复这个过程直到收敛这种机制让LVQ特别适合处理那些类别边界不是很清晰的数据。我在一个电商产品分类项目中使用LVQ准确率比传统KNN提高了近15%而且模型大小只有原来的1/10因为只需要存储那些关键的原型向量就够了。2. LVQ实现二分类任务全流程2.1 数据准备与预处理让我们用周志华《机器学习》中的西瓜数据集来实战。这个数据集虽然小但特别适合理解LVQ的工作原理。数据包含13个西瓜样本每个样本有密度和含糖率两个特征标签是好瓜(Y)或坏瓜(N)。import re import math import numpy as np import pylab as pl data 1,0.697,0.46,Y, 2,0.774,0.376,Y, 3,0.634,0.264,Y, 4,0.608,0.318,Y, 5,0.556,0.215,Y, 6,0.403,0.237,Y, 7,0.481,0.149,Y, 8,0.437,0.211,Y, 9,0.666,0.091,N, 10,0.639,0.161,N, 11,0.657,0.198,N, 12,0.593,0.042,N, 13,0.719,0.103,N # 数据预处理 a re.split(,, data.strip( )) dataset [] for i in range(int(len(a)/4)): temp tuple(a[i*4: i*44]) dataset.append((float(temp[1]), float(temp[2]), temp[3]))预处理时要注意两点一是字符串到数值的转换二是处理可能存在的缺失值。这个小数据集很干净但实际项目中你可能需要先做标准化处理特别是当特征量纲差异很大时。2.2 核心算法实现LVQ的核心代码其实很简洁主要就是距离计算和原型向量更新两部分def dist(a, b): 计算欧几里得距离 return math.sqrt(math.pow(a[0]-b[0], 2)math.pow(a[1]-b[1], 2)) def LVQ(dataset, a, max_iter): # 获取所有类别标签 labels list(set(i[2] for i in dataset)) # 初始化原型向量每类随机选一个样本 prototypes [dataset[i] for i in np.random.choice( [idx for idx, x in enumerate(dataset) if x[2]label], 1)[0] for label in labels] for _ in range(max_iter): # 随机选一个样本 sample dataset[np.random.choice(len(dataset), 1)[0]] # 找到最近的原型向量 distances [dist((sample[0], sample[1]), (p[0], p[1])) for p in prototypes] nearest_idx np.argmin(distances) # 更新原型向量 if prototypes[nearest_idx][2] sample[2]: # 同类则靠近 new_x (1 - a) * prototypes[nearest_idx][0] a * sample[0] new_y (1 - a) * prototypes[nearest_idx][1] a * sample[1] else: # 不同类则远离 new_x (1 a) * prototypes[nearest_idx][0] - a * sample[0] new_y (1 a) * prototypes[nearest_idx][1] - a * sample[1] prototypes[nearest_idx] (new_x, new_y, prototypes[nearest_idx][2]) return prototypes这里有几个关键点需要注意学习率a的选择很关键太大容易震荡太小收敛慢。我一般从0.1开始尝试迭代次数max_iter要足够大可以设置早停机制原型向量的初始化会影响结果可以多跑几次选最好的2.3 结果可视化与分析训练完成后我们可以用matplotlib直观地看看原型向量的位置def visualize(dataset, prototypes): # 分离不同类别的样本 good [x for x in dataset if x[2]Y] bad [x for x in dataset if x[2]N] # 绘制样本点 pl.scatter([x[0] for x in good], [x[1] for x in good], markero, colorg, labelGood) pl.scatter([x[0] for x in bad], [x[1] for x in bad], markers, colorr, labelBad) # 绘制原型向量 pl.scatter([x[0] for x in prototypes], [x[1] for x in prototypes], marker*, colorb, s200, labelPrototypes) pl.legend() pl.xlabel(Density) pl.ylabel(Sugar Rate) pl.show() prototypes LVQ(dataset, 0.1, 1000) visualize(dataset, prototypes)从可视化结果中你会发现原型向量通常位于两类数据的边界区域这正是LVQ的聪明之处——它不需要记住所有样本只需要找到那些最能代表类别边界的关键样本。3. 多分类任务中的LVQ进阶应用3.1 三分类问题实战让我们用scikit-learn生成一个更复杂的三分类数据集from sklearn import datasets import matplotlib.pyplot as plt # 生成1000个样本分为3类 X, y datasets.make_blobs(n_samples1000, centers3, random_state42) plt.scatter(X[:,0], X[:,1], cy) plt.show()多分类问题的LVQ实现与二分类类似只是原型向量数量增加了def lvq_multiclass(X, y, n_prototypes, learning_rate0.1, max_iter1000): classes np.unique(y) prototypes [] # 初始化每类随机选n_prototypes个原型 for c in classes: samples X[y c] prototypes.extend(samples[np.random.choice( len(samples), n_prototypes, replaceFalse)]) prototypes np.array(prototypes) prototype_labels np.repeat(classes, n_prototypes) for _ in range(max_iter): # 随机选一个样本 idx np.random.choice(len(X)) sample, label X[idx], y[idx] # 找到最近的原型 distances np.linalg.norm(prototypes - sample, axis1) nearest np.argmin(distances) # 更新原型 if prototype_labels[nearest] label: prototypes[nearest] learning_rate * (sample - prototypes[nearest]) else: prototypes[nearest] - learning_rate * (sample - prototypes[nearest]) return prototypes, prototype_labels3.2 多分类效果评估训练完成后我们可以用原型向量对整个数据集进行分类def predict(X, prototypes, prototype_labels): predictions [] for sample in X: distances np.linalg.norm(prototypes - sample, axis1) predictions.append(prototype_labels[np.argmin(distances)]) return np.array(predictions) prototypes, prototype_labels lvq_multiclass(X, y, n_prototypes3) preds predict(X, prototypes, prototype_labels) accuracy np.mean(preds y) print(f分类准确率: {accuracy:.2f})在我的测试中这个简单模型就能达到90%以上的准确率。不过要注意原型向量的数量(n_prototypes)是个关键参数太少会导致模型欠拟合无法捕捉类别多样性太多会导致过拟合且增加计算开销 我建议从每类3-5个开始尝试通过交叉验证确定最佳值。4. LVQ算法优化与调参技巧4.1 参数调优实战经验LVQ有几个关键参数需要仔细调整学习率(η)控制原型向量更新的步长太大原型向量震荡难以收敛太小训练速度慢我的经验从0.1开始每10轮衰减10%原型向量数量决定模型的表达能力简单数据每类1-2个足够复杂数据可能需要5-10个可以用肘部法则确定最佳数量迭代次数建议设置较大的值(如1000-5000)配合早停机制(连续N轮损失不下降就停止)这里有个实用的学习率衰减实现def lvq_with_decay(X, y, n_prototypes, initial_lr0.1, decay0.99, min_lr0.001): lr initial_lr # ...初始化代码同上... for epoch in range(max_iter): # ...更新代码同上... # 学习率衰减 lr max(lr * decay, min_lr) # 早停检查 if epoch % 10 0: preds predict(X, prototypes, prototype_labels) accuracy np.mean(preds y) # 记录准确率变化判断是否停止4.2 算法改进思路基础LVQ有几个可以改进的方向距离度量优化 默认的欧氏距离不一定总是最佳选择。对于高维数据可以尝试# 马氏距离 def mahalanobis_dist(x, y, cov): diff x - y return np.sqrt(diff.T np.linalg.inv(cov) diff)原型向量初始化 随机初始化可能导致次优解。可以先用K-Means聚类中心作为初始原型from sklearn.cluster import KMeans def init_prototypes(X, y, n_prototypes): prototypes [] for c in np.unique(y): kmeans KMeans(n_clustersn_prototypes) kmeans.fit(X[y c]) prototypes.extend(kmeans.cluster_centers_) return np.array(prototypes)样本加权 对难样本(分类边界附近的点)可以给予更大权重# 在更新时加入权重 if prototype_labels[nearest] label: weight 1 (1 - distances[nearest]/max_dist) prototypes[nearest] weight * lr * (sample - prototypes[nearest])我在一个电商评论情感分析项目中使用了改进版LVQ相比基础版本准确率提升了8%训练时间缩短了30%。关键是要根据具体问题选择合适的改进策略。

相关新闻