SiameseNet
SiameseNet
孪生网络(SiameseNet):输入是成对的,两部分网络结构和参数一模一样(即只有一个网络)
优点:
- 利用双向LSTM抽取语义特征
- 利用对比损失优化模型
- 提出多种文本数据增强方法
模型结构:
模型流程:
- 采用字嵌入,长度固定为100,超出部分进行截取,不足补0
- 利用双向LSTM提取文本表示特征,堆叠四层双向LSTM结构
- 平均池化操作:在文本长度维度上做平均
- 全连接层,提取出文本的最终表示
- 利用余弦相似度计算两个输入的文本相似度。
模型损失函数:
首先,模型输出为余弦相似度,即:
每一个样本数据的损失函数为:
其中:
损失函数的形式本质为两个函数合在一起:
数据增强:
- 增加拼写错误,即对于正类样本,随机替换20%字符并删除5%字符,操作后额外生成大量训练数据。另外,再取19928条作为测试数据,这些测试数据中被替换或删除的字符占比5%。(符合正常人类拼写错误)
- 同义词替换,对原数据进行同义词扩充。
- 添加多余信息,一些文本可能包含无用信息,为近似现实数据,在文本数据上添加一些多余信息。
- 人工反馈,人工检测数据是否存在标注错误,人为进行改正。
代码
import torch
import torch.nn as nn
import args
from data_load import load_char_data
from torch.utils.data import Dataset
class SiameseNet(nn.Module):
def __init__(self, embed):
super(SiameseNet, self).__init__()
self.embed = nn.Embedding(args.char_size, args.embedding_size)
self.embed.weight.data.copy_(torch.from_numpy(embed))
self.lstm = nn.LSTM(args.embedding_size, args.lstm_hidden_size,
num_layers=2, dropout=0.2,
bidirectional=True, batch_first=True)
self.dense = nn.Linear(args.lstm_hidden_size * 2, args.linear_hidden_size)
self.dropout = nn.Dropout(p=0.3)
def forward(self, a, b):
emb_a = self.embed(a)
emb_b = self.embed(b)
lstm_a, _ = self.lstm(emb_a)
lstm_b, _ = self.lstm(emb_b)
avg_a = torch.mean(lstm_a, dim=1)
avg_b = torch.mean(lstm_b, dim=1)
out_a = torch.tanh(self.dense(avg_a))
out_a = self.dropout(out_a)
out_b = torch.tanh(self.dense(avg_b))
out_b = self.dropout(out_b)
cosine = torch.cosine_similarity(out_a, out_b, dim=1, eps=1e-8)
return cosine
class LcqmcDataset(Dataset):
def __init__(self, data_path, vocab_file):
self.data_path = data_path
self.vocab_path = vocab_file
self.a_index, self.b_index, self.label = load_char_data(self.data_path, self.vocab_path)
def __len__(self):
return len(self.a_index)
def __getitem__(self, idx):
return self.a_index[idx], self.b_index[idx], self.label[idx]
class ContrastiveLoss(nn.Module):
def __init__(self):
super(ContrastiveLoss, self).__init__()
def forward(self, Ew, y):
l_1 = 0.25 * (1.0 - Ew) * (1.0 - Ew)
l_0 = torch.where(Ew < args.m * torch.ones_like(Ew), torch.full_like(Ew, 0), Ew) * torch.where(
Ew < args.m * torch.ones_like(Ew), torch.full_like(Ew, 0), Ew)
loss = y * 1.0 * l_1 + (1 - y) * 1.0 * l_0
return loss.sum()