关于机器学习:广义学习矢量量化GLVQ分类算法介绍和代码实现

35次阅读

共计 10207 个字符,预计需要花费 26 分钟才能阅读完成。

狭义学习矢量量化(Generalized Learning Vector Quantization,GLVQ)是一种基于原型的分类算法,用于将输出数据调配到先前定义的类别中。GLVQ 是 LVQ(Learning Vector Quantization)的一种扩大模式,LVQ 在特色空间中利用一组代表性原型来对输出数据进行分类。

GLVQ

GLVQ 是一种监督学习算法,属于学习矢量量化 (LVQ) 算法的领域。LVQ 算法是一种基于原型的分类算法,它依据类与代表每个类的一组原型 (也称为范例或参考向量) 的间隔将类调配给数据点。GLVQ 通过容许类之间更灵便的决策边界来扩大 LVQ,这在数据类不可线性可分时尤其有用。

GLVQ 与 LVQ 的次要区别在于,GLVQ 将类别之间的区别示意为权重矩阵,而不是在特色空间中应用间隔度量。GLVQ 中的权重矩阵能够从数据中学习,使得分类器能够自适应地调整权重,以便更好地区分不同的类别。此外,GLVQ 还能够应用非线性映射将输出数据投影到一个新的特色空间中,这样就能够更好地解决非线性分类问题。

GLVQ 能够利用于各种分类问题,尤其是在须要解决高维度数据和非线性分类问题的状况下。以下是 GLVQ 的一些具体利用:

  1. 模式识别:GLVQ 能够用于模式识别,例如人脸识别、手写数字辨认、图像分类等。通过将输出数据映射到原型向量空间,GLVQ 能够在原型向量之间寻找最佳匹配,实现分类的目标。
  2. 计算机视觉:GLVQ 能够用于计算机视觉畛域的各种工作,例如指标检测、图像宰割、物体跟踪等。GLVQ 能够将输出数据投影到一个低维空间,而后利用原型向量来分类数据。
  3. 生物医学工程:GLVQ 能够利用于生物医学工程畛域,例如基因表白数据分析、心电信号分类等。GLVQ 能够用于剖析和分类简单的生物医学数据,帮忙钻研人员辨认疾病的特色并做出预测。
  4. 自然语言解决:GLVQ 能够用于自然语言解决畛域,例如文本分类、情感剖析、机器翻译等。GLVQ 能够通过将文本数据映射到原型向量空间来实现文本分类和情感剖析等工作。

GLVQ 的指标是学习一个权重矩阵,该矩阵将输出向量映射到类别空间中的向量。该映射能够通过最小化一个指标函数来学习,该指标函数将输出向量映射到它们正确的类别向量的间隔最小化。

具体来说,GLVQ 学习一个由原型向量组成的汇合,其中每个原型向量代表一个类别。对于一个新的输出向量,GLVQ 计算该向量与每个原型向量之间的间隔,并将其调配给与其间隔最近的原型向量所代表的类别。GLVQ 能够依据分类的准确性调整原型向量的地位,以便更好地区分不同的类别。

GLVQ 有许多变体,包含适应性 GLVQ(Adaptive GLVQ)、增强型 GLVQ(Enhanced GLVQ)和概率 GLVQ(Probabilistic GLVQ),它们在权重矩阵的模式、指标函数和学习算法等方面有所不同。这些变体广泛应用于模式识别、计算机视觉、语音辨认等畛域,以解决各种分类问题。

数学解释和 python 代码

狭义学习矢量量化(Generalized Learning Vector Quantization,GLVQ)的分类原理是将输出样本映射到原型向量上,并找到间隔最近的原型向量,将输出样本调配给与其间隔最近的原型向量所代表的类别。

在 GLVQ 中,每个类别都有一个原型向量,示意该类别在特色空间中的地位。输出样本与原型向量之间的间隔能够通过欧几里得间隔或曼哈顿间隔等度量办法来计算。下图为二元分类的代码

 # normalize the data
 defnormalization(self, input_data):
     minimum=np.amin(input_data, axis=0)
     maximum=np.amax(input_data, axis=0)
     normalized_data= (input_data-minimum)/(maximum-minimum)
     returnnormalized_data
 
 # define prototypes
 defprt(self, input_data, data_labels, prototype_per_class):
 
     # prototype_labels are
     prototype_labels=np.unique(data_labels)
     prototype_labels=list(prototype_labels) *prototype_per_class
 
     # prototypes are
     prt_labels=np.expand_dims(prototype_labels, axis=1)
     expand_dimension=np.expand_dims(np.equal(prt_labels, data_labels),
                                       axis=2)
 
     count=np.count_nonzero(expand_dimension, axis=1)
     proto=np.where(expand_dimension, input_data, 0)
     prototypes=np.sum(proto, axis=1)/count
 
     self.prt_labels=prototype_labels
     returnself.prt_labels, prototypes
 # define euclidean distance
 defeuclidean_dist(self, input_data, prototypes):
     expand_dimension=np.expand_dims(input_data, axis=1)
     distance=expand_dimension-prototypes
     distance_square=np.square(distance)
     sum_distance=np.sum(distance_square, axis=2)
     eu_dist=np.sqrt(sum_distance)
     returneu_dist
 
 # define d_plus
 defdistance_plus(self, data_labels, prototype_labels,
                   prototypes, eu_dist):
     expand_dimension=np.expand_dims(prototype_labels, axis=1)
     label_transpose=np.transpose(np.equal(expand_dimension, data_labels))
 
     # distance of matching prototypes
     plus_dist=np.where(label_transpose, eu_dist, np.inf)
     d_plus=np.min(plus_dist, axis=1)
 
     # index of minimum distance for best matching prototypes
     w_plus_index=np.argmin(plus_dist, axis=1)
     w_plus=prototypes[w_plus_index]
     returnd_plus, w_plus, w_plus_index
 
 # define d_minus
 defdistance_minus(self, data_labels, prototype_labels,
                    prototypes, eu_dist):
     expand_dimension=np.expand_dims(prototype_labels, axis=1)
     label_transpose=np.transpose(np.not_equal(expand_dimension,
                                                 data_labels))
 
     # distance of non matching prototypes
     minus_dist=np.where(label_transpose, eu_dist, np.inf)
     d_minus=np.min(minus_dist, axis=1)
 
     # index of minimum distance for non best matching prototypes
     w_minus_index=np.argmin(minus_dist, axis=1)
     w_minus=prototypes[w_minus_index]
     returnd_minus, w_minus, w_minus_index

 # define classifier function
 def classifier_function(self, d_plus, d_minus):
     classifier = (d_plus - d_minus) / (d_plus + d_minus)
     return classifier
 
 # define sigmoid function
 def sigmoid(self, x, beta=10):
     return (1/(1 + np.exp(-beta * x)))

GLVQ 将该样本映射到所有原型向量上,并计算每个映射后果与该输出样本的间隔。而后 GLVQ 将输出样本 x 分类为间隔最近的原型向量所代表的类别。

 # define delta_w_plus
 defchange_in_w_plus(self, input_data, prototypes, lr, classifier,
                      w_plus, w_plus_index, d_plus, d_minus):
 
     sai= (2) * (d_minus/ (np.square(d_plus+d_minus))) * \
         (self.sigmoid(classifier)) * (1-self.sigmoid(classifier))
 
     expand_dimension=np.expand_dims(sai, axis=1)
     change_w_plus=expand_dimension* (input_data-w_plus) *lr
 
     # index of w_plus
     unique_w_plus_index=np.unique(w_plus_index)
     unique_w_plus_index=np.expand_dims(unique_w_plus_index, axis=1)
 
     add_row_change_in_w=np.column_stack((w_plus_index, change_w_plus))
     check=np.equal(add_row_change_in_w[:, 0], unique_w_plus_index)
     check=np.expand_dims(check, axis=2)
     check=np.where(check, change_w_plus, 0)
     sum_change_in_w_plus=np.sum(check, axis=1)
     returnsum_change_in_w_plus, unique_w_plus_index
 
 # define delta_w_minus
 defchange_in_w_minus(self, input_data, prototypes, lr, classifier,
                       w_minus, w_minus_index, d_plus, d_minus):
 
     sai= (2) * (d_plus/ (np.square(d_plus+d_minus))) * \
         (self.sigmoid(classifier)) * (1-self.sigmoid(classifier))
 
     expand_dimension=np.expand_dims(sai, axis=1)
     change_w_minus= (expand_dimension) * (input_data-w_minus) *lr
 
     # index of w_minus
     unique_w_minus_index=np.unique(w_minus_index)
     unique_w_minus_index=np.expand_dims(unique_w_minus_index, axis=1)
 
     add_row_change_in_w=np.column_stack((w_minus_index, change_w_minus))
     check=np.equal(add_row_change_in_w[:, 0], unique_w_minus_index)
     check=np.expand_dims(check, axis=2)
     check=np.where(check, change_w_minus, 0)
     sum_change_in_w_minus=np.sum(check, axis=1)
     returnsum_change_in_w_minus, 

能够应用上面代码绘制数据:

 # plot  data
 defplot(self, input_data, data_labels, prototypes, prototype_labels):
     plt.scatter(input_data[:, 0], input_data[:, 2], c=data_labels,
                 cmap='viridis')
     plt.scatter(prototypes[:, 0], prototypes[:, 2], c=prototype_labels,
                 s=60, marker='D', edgecolor='k')

咱们的训练代码如下:

 # fit function
 deffit(self, input_data, data_labels, learning_rate, epochs):
     normalized_data=self.normalization(input_data)
     prototype_l, prototypes=self.prt(normalized_data, data_labels,
                                        self.prototype_per_class)
     error=np.array([])
     plt.subplots(8, 8)
     foriinrange(epochs):
         eu_dist=self.euclidean_dist(normalized_data, prototypes)
 
         d_plus, w_plus, w_plus_index=self.distance_plus(data_labels,
                                                           prototype_l,
                                                           prototypes,
                                                           eu_dist)
 
         d_minus, w_minus, w_minus_index=self.distance_minus(data_labels,
                                                               prototype_l,
                                                               prototypes,
                                                               eu_dist)
 
         classifier=self.classifier_function(d_plus, d_minus)
 
         sum_change_in_w_plus, unique_w_plus_index=self.change_in_w_plus(
             normalized_data, prototypes, learning_rate, classifier,
             w_plus, w_plus_index,  d_plus, d_minus)
         update_w_p=np.add(np.squeeze(prototypes[unique_w_plus_index]), sum_change_in_w_plus)
         np.put_along_axis(prototypes, unique_w_plus_index,
                           update_w_p, axis=0)
 
         sum_change_in_w_m, unique_w_minus_index=self.change_in_w_minus(
             normalized_data, prototypes, learning_rate, classifier,
             w_minus, w_minus_index, d_plus, d_minus)
         update_w_m=np.subtract(np.squeeze(prototypes[unique_w_minus_index]), sum_change_in_w_m)
         np.put_along_axis(prototypes, unique_w_minus_index, update_w_m, axis=0)
 
         err=np.sum(self.sigmoid(classifier), axis=0)
         change_in_error=0
 
         if (i==0):
             change_in_error=0
 
         else:
             change_in_error=error[-1] -err
 
         error=np.append(error, err)
         print("Epoch : {}, Error : {} Error change : {}".format(i+1, err, change_in_error))
 
         plt.subplot(1, 2, 1)
         self.plot(normalized_data, data_labels, prototypes, prototype_l)
         plt.subplot(1, 2, 2)
         plt.plot(np.arange(i+1), error, marker="d")
         plt.pause(0.5)
     plt.show()
     accuracy=np.count_nonzero(d_plus<d_minus)
     acc=accuracy/len(d_plus) *100
     print("accuracy = {}".format(acc))
     self.update_prototypes=prototypes
     returnself.update_prototypes

GLVQ 的训练过程是通过不断更新原型向量来进行的,这个过程能够迭代屡次,直到分类后果收敛或达到指定的迭代次数为止。同时,为了避免过拟合,能够引入正则化项对原型向量的更新进行限度。

GLVQ 的数学解释绝对简略,但须要留神的是,不同的间隔度量办法、学习率调度办法和正则化项都会对算法的性能产生影响,须要依据具体问题进行抉择和调整。

预测数据:

 # data predict
 defpredict(self, input_value):
     input_value=self.normalization(input_value)
     prototypes=self.update_prototypes
     eu_dist=self.euclidean_dist(input_value, prototypes)
     m_d=np.min(eu_dist, axis=1)
     expand_dims=np.expand_dims(m_d, axis=1)
     ylabel=np.where(np.equal(expand_dims, eu_dist),
                       self.prt_labels, np.inf)
     ylabel=np.min(ylabel, axis=1)
     print(ylabel)
     returnylabel

须要留神的是,GLVQ 的分类后果不肯定是惟一的,因为在一些状况下,输出样本可能与多个原型向量的间隔相等。为了解决这个问题,能够引入一些规定来确定分类后果,例如优先将输出样本调配给更靠近的原型向量所代表的类别,或依据原型向量所代表的类别的先验概率散布来确定分类后果。

Pytorch 实现

下面咱们介绍的是 python 的实现,上面咱们尝试应用 pytorch 来实现这个过程(留神:这里是依据原理编写,不保障 100% 正确,如果发现问题请留言指出)

在 PyTorch 中实现 GLVQ 的办法,次要分为以下几步:

  1. 筹备数据:须要筹备训练集和测试集的数据,并进行数据预处理,例如标准化、归一化等操作,以便进行训练和测试。
  2. 定义模型:须要定义 GLVQ 模型,包含原型向量、间隔度量、更新规定等。在 PyTorch 中,能够通过自定义 nn.Module 类来实现 GLVQ 模型,其中包含前向流传和反向流传的办法。
  3. 定义损失函数:GLVQ 的损失函数通常采纳类间间隔最小化和类内间隔最大化的准则,能够通过自定义 nn.Module 类来实现 GLVQ 的损失函数,其中包含计算损失值和反向流传的办法。
  4. 训练模型:须要利用训练集对 GLVQ 模型进行训练,并在测试集上进行测试。在 PyTorch 中,能够应用规范的训练和测试流程来训练和测试 GLVQ 模型。
 importtorch
 importtorch.nnasnn
 
 classGLVQ(nn.Module):
     def__init__(self, num_prototypes, input_size, output_size):
         super(GLVQ, self).__init__()
         self.num_prototypes=num_prototypes
         self.input_size=input_size
         self.output_size=output_size
         self.prototypes=nn.Parameter(torch.randn(num_prototypes, input_size))
         self.output_layer=nn.Linear(num_prototypes, output_size)
 
     defforward(self, x):
         distances=torch.cdist(x, self.prototypes)
         activations=-distances.pow(2)
         outputs=self.output_layer(activations)
         returnoutputs
 
 classGLVQLoss(nn.Module):
     def__init__(self, prototype_labels, prototype_lambda):
         super(GLVQLoss, self).__init__()
         self.prototype_labels=prototype_labels
         self.prototype_lambda=prototype_lambda
 
     defforward(self, outputs, targets):
         distances=torch.cdist(outputs, self.prototype_labels)
         class_distances=torch.gather(distances, 1, targets.unsqueeze(1))
         other_distances=distances.clone()
         other_distances[torch.arange(outputs.size(0)), targets] =float('inf')
         other_class_distances, _=other_distances.min(1)
         loss=torch.mean(class_distances-other_class_distances+self.prototype_lambda*distances.pow(2))
         returnloss
 
 # Training
 model=GLVQ(num_prototypes=10, input_size=784, output_size=10)
 criterion=GLVQLoss(prototype_labels=torch.tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), prototype_lambda=0.1)
 optimizer=torch.optim.SGD(model.parameters(), lr=0.01)
 
 forepochinrange(10):
     fori, (inputs, labels) inenumerate(train_loader):
         optimizer.zero_grad()
         outputs=model(inputs.view(inputs.size(0), -1))
         loss=criterion(outputs, labels)
         loss.backward()

上述代码实现了一个 GLVQ 模型,其中蕴含了一个原型向量矩阵 W,每一行示意一个类别的原型向量。训练过程中,输出样本 x 通过计算与每个类别的原型向量之间的间隔来进行分类,并将其调配给与其间隔最近的原型向量所代表的类别。而后,依据分类后果和样本实在标签之间的误差来更新原型向量,使其向输出样本的方向挪动。这样,通过不断更新原型向量,GLVQ 能够学习到特色空间中不同类别之间的边界,并用于分类新的输出样本。

次要阐明下 GLVQLoss:具体而言,假如输出样本 x 调配给类别 c_{min},则 GLVQ 损失能够示意为:

其中 W_{c_{min}} 和 W_{c_{not min}} 别离示意输出样本 x 调配给的类别和未调配给的其余类别的原型向量,||\cdot||_2 示意欧几里得范数。该损失示意了输出样本 x 与其正确类别原型向量之间的间隔与其余类别原型向量之间间隔之差,即正确类别的原型向量应该更靠近输出样本 x。GLVQLoss 是一个带有动静原型向量矩阵的损失函数,它能够依据输出样本和以后的原型向量矩阵来计算损失,并应用梯度降落算法来更新原型向量。

总结

综上所述,狭义学习向量量化 (GLVQ) 是一种弱小而灵便的基于原型的分类算法,能够解决非线性可分类。通过容许更灵便的决策边界,GLVQ 能够在许多分类工作上实现更高的精度。

总的来说,GLVQ 提供了一种独特而弱小的分类办法,其解决非线性可拆散数据的能力使其成为可用的分类算法集的一个有价值的补充。

https://avoid.overfit.cn/post/d5792b8b6f324ab184fbdfd5aefdbc28

正文完
 0