元学习—关系网络实现
原理部分已经在元学习—关系网络和匹配网络中讲述,这里不再赘述,实验包括了one-shot学习,few-shot(few=5)学习的两种学习过程。原始样本通过随机产生,类别一个有两个,损失函数使用的是均方误差的方式。具体的代码如下:
1 One-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 createSQ(data,label):
'''
:param data: 总体数据
:param label:总体标签
:return:
'''
indexA = random.randint(0,data.shape[0]/2-1)
indexB = random.randint(data.shape[0]/2,data.shape[0]-1)
A_data = data[indexA,:].reshape(1,-1)
B_data = data[indexB,:].reshape(1,-1)
support_set = torch.cat([A_data,B_data],dim=0)
indexQ = random.randint(0,data.shape[0]-1)
while indexQ == indexA or indexQ == 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 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)
similar = self.Z(fxi,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 = createSQ(data,label)
train(epoch,support_set,query_set,query_label)
2 few-shot
在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)
3 总结
上述可以进行改进的部分包括对于损失函数的选择,以及few-shot中对于类别向量生成方式的选择。