我是 DL 和 NLP 的新手,最近开始通过 gensim 使用预训练的 fastText 嵌入模型 (cc.en.300.bin)。
我希望自己能够通过将单词拆分为 n-gram 并查找每个 n-gram 的向量来计算词汇表外单词的向量。
我找不到导出模型一部分的 n-gram 向量的方法。我意识到它们是散列的,但也许有办法(不一定使用 gensim)来获取它们?
任何见解将不胜感激!
我最近自己也遇到了这个问题,不得不写一个脚本来减小模型大小。fasttext C 代码包含一个方便的函数“threshold”来减少字典大小,但它不暴露给 python 绑定。在字典缩减之后,您还需要重新构建输入矩阵,包括主要词向量之后的 ngram 桶。这样保存模型后,所有的词向量都会单独从子词信息中生成(字典中剩下的词除外)
对于单词相似度搜索,不使用 output_ 和 model_。为了节省更多内存,您也可以只注释掉在 saveModel 中写入 output_ 的部分
请注意,在英语预训练模型中,ngram 条目本身约为 2Gb,因此即使删除了所有字典单词,这也是您可以制作模型的最小尺寸。
/* note: some dict_ members are public for easier access */
void FastText::quantize(const Args& qargs) {
/*if (args_->model != model_name::sup) {
throw std::invalid_argument(
"For now we only support quantization of supervised models");
}*/
args_->input = qargs.input;
args_->qout = qargs.qout;
args_->output = qargs.output;
std::shared_ptr<DenseMatrix> input =
std::dynamic_pointer_cast<DenseMatrix>(input_);
std::shared_ptr<DenseMatrix> output =
std::dynamic_pointer_cast<DenseMatrix>(output_);
bool normalizeGradient = (args_->model == model_name::sup);
if (qargs.cutoff > 0 && qargs.cutoff < input->size(0)) {
/*auto idx = selectEmbeddings(qargs.cutoff);
dict_->prune(idx);*/
int32_t rows = dict_->size_+args_->bucket;
dict_->threshold(2000, 2000);
std::cerr << "words: " << dict_->size_ << std::endl;
std::cerr << "rows: " << rows << std::endl;
/*std::shared_ptr<DenseMatrix> ninput =
std::make_shared<DenseMatrix>(idx.size(), args_->dim);*/
int32_t new_rows = dict_->size_+args_->bucket;
std::shared_ptr<DenseMatrix> ninput = std::make_shared<DenseMatrix>(dict_->size_+args_->bucket, args_->dim);
for (auto i = 0; i < dict_->size_; i++) {
for (auto j = 0; j < args_->dim; j++) {
int32_t index = dict_->getId(dict_->words_[i].word);
ninput->at(i, j) = input->at(index, j);
}
}
int32_t offset = rows-new_rows;
for (auto i = dict_->size_; i < new_rows; i++) {
for (auto j = 0; j < args_->dim; j++) {
ninput->at(i, j) = input->at(i+offset, j);
}
}
/*input = ninput;*/
input_ = ninput;
if (qargs.retrain) {
args_->epoch = qargs.epoch;
args_->lr = qargs.lr;
args_->thread = qargs.thread;
args_->verbose = qargs.verbose;
auto loss = createLoss(output_);
model_ = std::make_shared<Model>(input, output, loss, normalizeGradient);
startThreads();
}
}
/*input_ = std::make_shared<QuantMatrix>(
std::move(*(input.get())), qargs.dsub, qargs.qnorm);*/
/*if (args_->qout) {
output_ = std::make_shared<QuantMatrix>(
std::move(*(output.get())), 2, qargs.qnorm);
}
*/
/*quant_ = true;*/
auto loss = createLoss(output_);
model_ = std::make_shared<Model>(input_, output_, loss, normalizeGradient);
}
gensim
您可以通过直接检查其FastTextKeyedVectors
类word_vec()
方法的源代码来准确查看代码如何为词汇表外的单词创建 FastText 词向量:
(请注意,这个分支中gensim
的源代码develop
可能反映了最近的 FastText 修复,这些修复与您安装的包到gensim
版本 3.7.1 所做的不匹配;您可能需要查阅已安装包的本地源代码,或等待这些修复在正式版本中。)
因为 Python 不保护相关对象的任何部分免受外部访问(例如强制“私有”指定),所以您可以从类外部执行完全相同的操作。
ngram_weights
请特别注意,在当前代码中(与 Facebook 原始实现的行为相匹配),无论您当前的 n-gram 在训练数据中是否真正已知,n-gram 向量都将从哈希表结构中的桶中提取. 在那些 n-gram 在训练数据中已知且有意义的情况下,这应该有助于 OOV 向量。在它获得任意其他向量的情况下,这种随机性应该不会造成太大伤害。