最近老师布置了一项作业,编码实现MICD分类器,我觉得在网上找一堆类似的代码意义不大,因为编码的目的是让我们对理论知识有更深入的了解,所以应该自己写。网上别人的代码各种调用,长的甚至有几百行,没得看。刚好学过一点PyTorch,用这个框架实现矩阵和向量的计算还是很容易的。
import torch
data = torch.tensor([[2,3],[3,3],[7,5],[9,4],[1,4],[8,5],[9,3],[2,2],[8,9],[6,8],[2,4],[3,2]])
label=['A','A','B','B','A','B','B','A','B','B','A','A']
test=torch.tensor([5,7])
我先不考虑一般性的问题,数据集data是我自己设置的,样本数量也很有限,但是对初学者来说很合适。label就是标签,跟data是一一对应的。我要解决的是二分类问题,用A和B表示两个不同的类别,数据全是有标签的。test是测试样本,先只用一个样本测试吧。
#计算A类样本的均值和协方差矩阵
countA=0
meanA=torch.tensor([0,0])
sigmaA=torch.tensor([[0,0],[0,0]])
for i in range(len(data)):
if label[i]=='A':
countA+=1
meanA+=data[i]
if countA!=0:
meanA=meanA/countA
for i in range(len(data)):
if label[i]=='A':
sigmaA=torch.mm((data[i]-meanA).reshape(2,1),(data[i]-meanA).reshape(2,1).T)
if countA!=0:
sigmaA=sigmaA/countA
count其实是为了计算每一类样本的数量,因为需要计算均值和协方差矩阵,所以最后要除以count。meanA就是A类样本的均值向量,初始化为零向量,扫描训练样本的时候,先把A类所有样本相加,同时统计目前A类样本的个数,最后再做除法。sigma是协方差矩阵,这个必须等均值向量计算完成后才能开始算。
我一开始没有关注到矩阵和向量的大小,所以代码跑不动。后来用了reshape()函数,统一转换成矩阵计算,比如矩阵乘法就是torch.mm(),矩阵转置就是.T,并不复杂。
#计算B类样本的均值和协方差矩阵
countB=0
meanB=torch.tensor([0,0])
sigmaB=torch.tensor([[0,0],[0,0]])
for i in range(len(data)):
if label[i]=='B':
countB+=1
meanB+=data[i]
if countB!=0:
meanB=meanB/countB
for i in range(len(data)):
if label[i]=='B':
sigmaB=torch.mm((data[i]-meanB).reshape(2,1),(data[i]-meanB).reshape(2,1).T)
if countB!=0:
sigmaB=sigmaB/countB
B类样本的均值向量和协方差矩阵的计算,跟A类是一样的。
#计算协方差矩阵的逆
invA=torch.inverse(sigmaA)
invB=torch.inverse(sigmaB)
因为MICD分类器是基于马氏距离的,而计算马氏距离需要用到协方差矩阵的逆,所以调用了torch.inverse()函数。
#计算马氏距离
M1A=torch.mm((test-meanA).reshape(2,1).T,invA)
M2A=torch.mm(M1A,(test-meanA).reshape(2,1))
d2A=abs(torch.det(M2A))
M1B=torch.mm((test-meanB).reshape(2,1).T,invB)
M2B=torch.mm(M1B,(test-meanB).reshape(2,1))
d2B=abs(torch.det(M2B))
#预测样本类别
if d2A<=d2B:
print("测试样本预测为A类")
else:
print("测试样本预测为B类")
对马氏距离有了解的朋友,就会知道这其实是做了几次矩阵乘法而已。我是把向量转换成矩阵来计算了。abs()函数是给距离加个绝对值,保证距离非负。最后结果如下:
测试样本预测为B类
完整的代码如下:
import torch
data = torch.tensor([[2,3],[3,3],[7,5],[9,4],[1,4],[8,5],[9,3],[2,2],[8,9],[6,8],[2,4],[3,2]])
label=['A','A','B','B','A','B','B','A','B','B','A','A']
test=torch.tensor([5,7])
#计算A类样本的均值和协方差矩阵
countA=0
meanA=torch.tensor([0,0])
sigmaA=torch.tensor([[0,0],[0,0]])
for i in range(len(data)):
if label[i]=='A':
countA+=1
meanA+=data[i]
if countA!=0:
meanA=meanA/countA
for i in range(len(data)):
if label[i]=='A':
sigmaA=torch.mm((data[i]-meanA).reshape(2,1),(data[i]-meanA).reshape(2,1).T)
if countA!=0:
sigmaA=sigmaA/countA
#计算B类样本的均值和协方差矩阵
countB=0
meanB=torch.tensor([0,0])
sigmaB=torch.tensor([[0,0],[0,0]])
for i in range(len(data)):
if label[i]=='B':
countB+=1
meanB+=data[i]
if countB!=0:
meanB=meanB/countB
for i in range(len(data)):
if label[i]=='B':
sigmaB=torch.mm((data[i]-meanB).reshape(2,1),(data[i]-meanB).reshape(2,1).T)
if countB!=0:
sigmaB=sigmaB/countB
#计算协方差矩阵的逆
invA=torch.inverse(sigmaA)
invB=torch.inverse(sigmaB)
#计算马氏距离
M1A=torch.mm((test-meanA).reshape(2,1).T,invA)
M2A=torch.mm(M1A,(test-meanA).reshape(2,1))
d2A=abs(torch.det(M2A))
M1B=torch.mm((test-meanB).reshape(2,1).T,invB)
M2B=torch.mm(M1B,(test-meanB).reshape(2,1))
d2B=abs(torch.det(M2B))
#预测样本类别
if d2A<=d2B:
print("测试样本预测为A类")
else:
print("测试样本预测为B类")