本文介绍在深度学习中如何应用预训练的词表征(word2vec等),应用到的框架包括numpy、PyTorch和TensorFlow
不同形式,见到了就补充总结一下。
最近更新时间:2023.3.3
最早更新时间:2022.12.15
文章目录
1. 将预训练文件加载为numpy.ndarray
/torch.Tensor
格式的矩阵对象
这两类很容易互相转换,所以我就混着写了。
padding向量(Padding
或BLANK
)一般都是全0(这样叠mask方便)
UNK
向量可以是其他向量的平均池化,也可以随便初始化一个向量
- word2id字典文件(用pickle保存)和预训练权重矩阵(用
np.save
保存)(范例:LADAN)- 写法1(这是LADAN自己写的)
with open('w2id_thulac.pkl', 'rb') as f:
word2id_dict = pk.load(f)
f.close()
emb_path='cail_thulac.npy'
word_embedding=np.cast[np.float32](np.load(emb_path))
2. 写法2(这是我自己写的,我觉得能比较明显地看出,我在代码简练程度上的这个,匮乏……)
with open('w2id_thulac.pkl', 'rb') as f:
word2id_dict = pickle.load(f)
embedding_file_path='cail_thulac.npy'
embedding_weight=np.load(embedding_file_path)
embedding_dim=embedding_weight.shape[1] #词向量维度
embedding_weight[word2id_dict['BLANK']]=[0 for _ in range(embedding_dim)]
- word2vec.json文件和idx2word.json文件(word2id.json文件也行,反正就是直接翻过来)(范例:NeurJudge)
需要注意的是,不能直接用np.ndarray
或torch.FloatTensor
来表征后不进行初始化,可以用zeros()
或zero_()
等函数来初始为全0- 写法1
word2vec = json.load(open('./word2vec.json', "r"))
word2vec['UNK'] = np.random.randn(200).tolist()
word2vec['Padding'] = [0. for i in range(200)]
embedding = torch.FloatTensor(339503, 200).zero_()
idx2word = json.load(open('./idx2word.json', "r"))
for i in tqdm(range(339503)):
word = idx2word[str(i)]
result = list(map(float, word2vec[word]))
embedding[i] = torch.from_numpy(np.array(result))
2. 写法2
word2id=json.load(open('word2vec/word2id.json'))
word2vec=json.load(open('word2vec/word2vec.json'))
word2vec['UNK'] = np.random.randn(200).tolist()
word2vec['Padding'] = [0. for i in range(200)]
embedding=np.zeros((339503,200))
for k in word2id:
embedding[word2id[k],:]=[float(factor) for factor in word2vec[k]]
embedding=torch.from_numpy(embedding)
2. 建立模型中的表征层
2.1 TensorFlow 1.14
示例代码改自LADAN项目
扫描二维码关注公众号,回复:
14935172 查看本文章
description_layer=tf.nn.embedding_lookup(word_embedding,num_input)
word_embedding是np.ndarray
格式的词向量矩阵,num_input是转换为数字格式并padding好的文本矩阵
2.2 PyTorch
示例代码改自NeurJudge项目
在torch.nn.Module
子类__init__()
中:
(本代码是不在训练过程中更新embedding权重的意思)
self.embs = nn.Embedding(339503, 200)
self.embs.weight.data.copy_(embedding)
self.embs.weight.requires_grad = False
改自CECP项目
word_embedding = embedding_weight.astype(np.float32)
2.3 直接池化为文档表征
这个主要是比如说KNN、SVM之类的传统机器学习模型,可以一波塞进去(跳过将文本转换为数字这一步,直接进行平均值池化)
total_vector=np.zeros((sample_num,300))
for i in range(len(segmentated_names)): #分词好的文本列表
segmentated_name=segmentated_names[i]
id_list=[]
for j in segmentated_name:
if j in word2id: #对于词表中存在的词
id_list.append(embedding_weight[word2id[j]])
else:
id_list.append(embedding_weight[word2id['UNK']])
if len(id_list)==0:
total_vector[i,:]=embedding_weight[word2id['UNK']]
else:
total_vector[i,:]=np.mean(np.array(id_list),axis=0)
print(total_vector)
3. 将文本转换为数字
- 分词后使用word2id词典:改自LADAN
遍历每个样本,在每个样本内:
for j in range(int(min(len(fact), max_length))): #遍历最长句长内的每个词
if fact[j] in word2id_dict: #对于词表中存在的词
id_list.append(int(word2id_dict[fact[j]]))
else:
id_list.append(int(word2id_dict['UNK']))
while len(id_list) < 512: #padding
id_list.append(int(word2id_dict['BLANK']))
- NeurJudge版的实现,是将整个过程打包成了很多小环节
def transform(self, word):
"""将词语转换为索引"""
if not (word in self.word2id.keys()):
return self.word2id["UNK"]
else:
return self.word2id[word]
def seq2tensor(self, sents, max_len=350):
"""将句子序列转换为词索引tensor,注意这里句子序列每个元素是一个已经分词好的list"""
sent_len_max = max([len(s) for s in sents])
sent_len_max = min(sent_len_max, max_len)
sent_tensor = torch.LongTensor(len(sents), sent_len_max).zero_()
sent_len = torch.LongTensor(len(sents)).zero_()
for s_id, sent in enumerate(sents):
sent_len[s_id] = len(sent)
for w_id, word in enumerate(sent):
if w_id >= sent_len_max: break
sent_tensor[s_id][w_id] = self.transform(word)
return sent_tensor,sent_len