我在从 .BIN 文件加载嵌入模型时遇到问题,当我尝试读取模型时,我收到此消息错误: UnicodeDecodeError:“utf-8”编解码器无法解码位置 6 中的字节 0xd6:无效的连续字节
这是我正在使用的脚本:
import numpy as np
import gensim
from tqdm import tqdm
## dict from .TXT file: nlp.stanford.edu/projects/glove >> glove.6B.zip >> glove.6B.50d.txt
word_to_embeddings = dict()
with open('glove.6B/glove.6B.50d.txt') as f:
for line in f:
word = line.split()[0]
embeddings = np.asarray(line.split()[1:], dtype='float32')
word_to_embeddings[word] = embeddings
w = word_to_embeddings
## write dictinary in .BIN file
def save_word2vec_format(fname, vocab, vector_size, binary=True):
"""Store the input-hidden weight matrix in the same format used by the original
C word2vec-tool, for compatibility.
Parameters
----------
fname : str
The file path used to save the vectors in.
vocab : dict
The vocabulary of words.
vector_size : int
The number of dimensions of word vectors.
binary : bool, optional
If True, the data wil be saved in binary word2vec format, else it will be saved in plain text.
"""
total_vec = len(vocab)
with gensim.utils.open(fname, 'wb') as fout:
fout.write(gensim.utils.to_utf8("%s %s\n" % (total_vec, vector_size)))
for word, row in tqdm(vocab.items()):
if binary:
row = row.astype(np.float32)
fout.write(gensim.utils.to_utf8(word) + b" " + row.tobytes())
else:
fout.write(gensim.utils.to_utf8("%s %s\n" % (word, ' '.join(repr(val) for val in row))))
save_word2vec_format(binary=True, fname='ppl6B50d.bin', vocab=w,vector_size=50)
## read Word2Vec model
new_model = gensim.models.keyedvectors.load_word2vec_format('ppl6B50d.bin',binary=True)
print(new_model)
当我尝试加载嵌入模型时,收到此消息错误:UnicodeDecodeError: 'utf-8' 编解码器无法解码位置 6 中的字节 0xd6: 无效的连续字节
我不明白问题是什么,.BIN 文件创建时没有错误,但我无法加载它。谁能帮我解决一下吗??
非常感谢。
即使您在保存时没有遇到错误,实际的问题可能就在那里:编写了未正确编码的内容以供以后读取。
但是您可能根本不需要编写自己的读写代码。 Gensim 已经可以直接读取 GLoVe 格式的向量,因为它们的格式“几乎”与 Gensim 所谓的“word2vec_format”相同(因为它是 Google 原始 word2vec.c
版本的保存/加载格式)。 GLoVe 格式只是省略了(有用的)第一行期望向量数量的声明。但是 Gensim 的
no_header
方法的 load_word2vec_format()
参数会告诉他们不要期望计数,而是要弄清楚。因此,如果您需要的只是将 GLoVe 向量放入 Gensim KeyedVector
,您可以这样做:
from gensim.models import KeyedVectors
glove_vectors = KeyedVectors.load_word2vec_format('glove.6B/glove.6B.50d.txt', binary=False, no_header=True)
瞧,你完成了。
现在,计算即将到来的向量的计数确实需要对文件进行一次额外的完整传递。在许多情况下,与其他事情相比,这只是一个很小的成本。但是,如果您处于加载时间至关重要的情况下,您可以通过以所需格式重新保存该
KeyedVectors
对象来实现问题代码的意图:
glove_vectors.save_word2vec_format('ppl6B50d.bin', binary=True)
我还没有注意到这种特定格式的
.bin
风格总体上节省了那么多空间/时间,特别是如果你通常将此类文件
压缩保存在磁盘上——这通常节省的IO时间比压缩/解压缩的成本更多CPU 时间。所以我怀疑你真的希望你的重新保存是:
glove_vectors.save_word2vec_format('ppl6B50d.bin.gz', binary=True)
.gz
后缀足以让这些方法自动压缩/解压缩文件。也就是说,您可以使用以下命令重新加载这些压缩向量:
reloaded_vectors = KeyedVectors.load_word2vec_format('ppl6B50d.bin.gz', binary=True)
最后,如果加载时间特别关键,另一个值得尝试的选项,可能
会提供额外的轻微加速,那就是让 Gensim 通过 .save()
方法以自己的格式重新保存向量。 Gensim 的格式是基于 Python pickle 的 - 这通常比更原始的自定义格式要慢一些 - 但它提供了一种将大型内部向量数组存储为一个单独的原始内存可映射文件的选项。
.load(filename, mmap='r')
上,操作系统可以直接内存映射整个文件(几乎立即,不进行任何初始读取),稍后当向量被
访问时,文件的相关范围将在操作系统中分页级别,没有额外的解析开销或过多的缓冲区复制。 因此,数组加载几乎是瞬时的,但稍后的初始访问可能会慢一些,直到整个文件结束分页。 (但是,触及每个向量的单个操作(例如
.most_similar()
)将用于对整个向量进行分页。)
要尝试此选项,您需要使用以下命令重新保存向量:glove_vectors.save('ppl6B50d.model')
...然后通过以下方式加载它们:
KeyedVectors.load('ppl6B50d.model', mmap='r')
请注意,使用此 Gensim 原生时
.save()
:
模型可能(通常是典型大小)保存为.gz
后缀)时,读取时的内存映射才会起作用如果您将相同的只读模型加载到多个单独的系统进程中,请操作系统像这样将文件映射到可寻址内存将使这些进程共享相同的内存 - 可能避免大量重复加载/内存使用(例如,如果某个 Web 服务器有许多进程都咨询同一组只读向量)