原理部分已经在元学习—关系网络和匹配网络中讲述,这里不再赘述,实验包括了one-shot学习,few-shot(few=5)学习的两种学习过程。原始样本通过随机产生,类别一个有两个,损失函数使用的是均方误差的方式。具体的代码如下:
在few-shot中,需要生成各个类别的向量表示,然后在进行各个类别和查询样本的相似性计算。这里我们选择的类别向量的计算采用求和的方式进行计算。具体代码如下:
#encoding=utf-8 import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim import time import random def createData(): ''' 随机创建两类数据,每一类数据的数量为10,维度为32 :return: ''' classA = torch.rand(size=[10,32]) classB = torch.rand(size=[10,32]) # 将两类数据进行按行拼接 data = torch.cat([classA,classB],dim=0) # 分别给两类数据构造标签,其中A类数据集的标签为1,B类数据的标签为0 labelA = torch.ones(10) labelB = torch.zeros(10) label = torch.cat([labelA,labelB]) return data,label def createFSQ(data,label): ''' :param data: 总体数据 :param label:总体标签 :return: ''' indexA = random.sample(list(range(data.shape[0]//2)),k=5) indexB = random.sample(list(range(data.shape[0]//2,data.shape[0])),k=5) A_data = data[indexA,:].reshape(5,-1) B_data = data[indexB,:].reshape(5,-1) support_set = torch.cat([A_data,B_data],dim=0) indexQ = random.randint(0,data.shape[0]-1) while indexQ in indexA or indexQ in indexB: indexQ = random.randint(0,data.shape[0]-1) query_set = data[indexQ,:] query_label = label[indexQ] return support_set,query_set,query_label class RelationWork(nn.Module): def __init__(self,input_dim,hidden_dim,num_class): super(RelationWork, self).__init__() self.hidden_dim = hidden_dim self.input_dim = input_dim self.num_class = num_class self.weight = nn.Parameter(torch.zeros(size=[input_dim,hidden_dim])) nn.init.xavier_uniform_(self.weight.data,gain=1.414) self.bias = nn.Parameter(torch.zeros(hidden_dim)) nn.init.uniform_(self.bias.data,a=0,b=1) # 下面,定义一个单层的神经网络来实现Z函数 self.W = nn.Parameter(torch.zeros(size=[2 * hidden_dim,num_class])) nn.init.xavier_uniform_(self.W.data,gain=1.414) self.Bias = nn.Parameter(torch.zeros(num_class)) nn.init.uniform_(self.Bias.data,a=0,b=1) def f(self,x): ''' 定义编码函数 :param x: 需要编码的数据 :return: 编码之后的结果 ''' embeddings = torch.matmul(x,self.weight) + self.bias embeddings = F.relu(embeddings) return embeddings def Z(self,x_i,x_j): ''' :param x_i: Support Set中的编码数据 :param x_j: Query Set中编码数据 :return: ''' rows = x_i.shape[0] x_j = x_j.repeat((rows,1)) x = torch.cat([x_i,x_j],dim=1) result = torch.matmul(x,self.W) + self.Bias result = F.relu(result) return result def makeClassVector(self,inputs): ''' 生成类别的向量信息,前5个是A类,后五个是B类 :param inputs: 支持集的编码结果 :return: ''' classA = inputs[:5,:] classB = inputs[5:,:] classA = torch.sum(classA,dim=0).reshape(1,-1) classB = torch.sum(classB,dim=0).reshape(1,-1) classAll = torch.cat([classA,classB],dim=0) return classAll def g(self,x): ''' 定义g函数,这里使用softmax函数 转换成score :param x: 通过Z计算出来的相关性结果 :return: 当前样本属于各个分类的概率 ''' return F.softmax(x,dim=1) def forward(self,x_i,x_j): ''' 基本过程: 1. 编码 2. 相似性计算 3. 转换成分值 :param x_i: 支持集中的原始数据 :param x_j: 查询集中的原始数据 :return: ''' fxi = self.f(x_i) fxj = self.f(x_j) classi = self.makeClassVector(fxi) similar = self.Z(classi,fxj) return self.g(similar) model = RelationWork(32,8,2) optimer = optim.Adam(model.parameters(),lr=0.01) loss = nn.MSELoss(reduction="sum") data,label = createData() def train(epoch,support_set,query_set,query_label): time.time() optimer.zero_grad() out = model(support_set,query_set) out = torch.max(out,dim=1).indices.reshape(-1,1) query_label = query_label.repeat(2,1) loss_train = loss(out,query_label) loss_train = loss_train.requires_grad_() loss_train.backward() optimer.step() print("Epoch: {:04d}".format(epoch+1),"loss_train: {:.4f}".format(loss_train.data.item())) if __name__ == "__main__": for epoch in range(1000): support_set,query_set,query_label = createFSQ(data,label) train(epoch,support_set,query_set,query_label) print(support_set.shape)上述可以进行改进的部分包括对于损失函数的选择,以及few-shot中对于类别向量生成方式的选择。