【问题标题】:fasttext: is there a way export ngrams?fasttext:有没有办法导出 ngram?
【发布时间】:2019-08-02 21:39:52
【问题描述】:

我是 DL 和 NLP 的新手,最近开始通过 gensim 使用预训练的 fastText 嵌入模型 (cc.en.300.bin)。

我希望能够自己计算词汇外单词的向量,方法是将单词拆分为 n-gram 并查找每个 n-gram 的向量。

我找不到导出模型一部分的 n-gram 向量的方法。我意识到它们是散列的,但也许有办法(不一定使用 gensim)来获取它们?

任何见解将不胜感激!

【问题讨论】:

    标签: export gensim n-gram fasttext oov


    【解决方案1】:

    您可以通过直接检查其FastTextKeyedVectorsword_vec() 方法的源代码来准确了解gensim 代码如何为词汇表外的单词创建FastText 词向量:

    https://github.com/RaRe-Technologies/gensim/blob/3aeee4dc460be84ee4831bf55ca4320757c72e7b/gensim/models/keyedvectors.py#L2069

    (请注意,gensimdevelop 分支中的此源代码可能反映了最近的 FastText 修复,这些修复与您安装的软件包直到 gensim 版本 3.7.1 所做的不匹配;您可能需要咨询您的安装包的本地源代码,或等待这些修复程序在正式版本中发布。)

    由于 Python 不保护相关对象的任何部分免受外部访问(例如强制“私有”指定),因此您可以从类外部执行完全相同的操作。

    请特别注意,在当前代码中(与 Facebook 原始实现的行为相匹配),无论您当前的 n-gram 是否真正已知,都会从哈希表 ngram_weights 结构中的桶中提取 n-gram 向量是否在训练数据中。在那些 n-gram 在训练数据中已知且有意义的情况下,这应该有助于 OOV 向量。在它获得任意其他向量的情况下,这种随机性应该不会造成太大伤害。

    【讨论】:

    • 感谢 gojomo!我能够看到一个特定的 n-gram (ngram_weights) 的嵌入向量,但我希望能够为 all n-grams 得到这个,所以我可以创建一个所有的“字典” n-gram。我想我什至可以通过model.wv.wv.syn0_vocab一次访问所有向量,但是这是散列的,我不知道如何将向量链接到它们所代表的子词......有什么想法吗?
    • 为什么要字典? Facebook 的 FT 实现选择了它的数据结构,一个不关心桶冲突的哈希表,有充分的理由:它节省空间,避免维护所有“已知”n-gram 的列表,并且返回一个用于先前未见过的 n-gram 的随机/任意向量(或偶尔为超过 1 个唯一 n-gram 提供相同的向量)。而且,如果您想计算与 FB FT 相同的向量,则必须做出相同的权衡。所以我想知道是什么推动了对更精确的 (n-gram)->(vector) 映射的需求。
    • 也就是说,虽然FastText 模型结构不包含任何已知 n-gram 的显式列表,或 (exact n-gram)->(exact vector) 的精确映射,但您可以合成一样。您将遍历已知完整单词的列表,将它们分成适当长度的 n-gram。然后,在哈希表中查找那些 n-gram。您可以从中创建自己的 (string n-gram)->(vector) dict。它会比原生结构占用更多空间,并且无法以兼容的方式处理从未见过的 n-gram。
    • 再次感谢 gojomo!这样做的原因是我的应用程序只能使用有限数量的 RAM,并且加载多个千兆 *.bin 模型是不可行的。到目前为止,我们所做的是将模型的 *.vec 版本加载到磁盘。这在延迟方面效果很好,但不支持子词......这更有意义吗?你有更好的解决方案吗?再次感谢!
    • n-gram 哈希表大小由名为 bucket 的模型设置参数控制——默认值为 2,000,000。除非bucket 比必要的大得多,否则将模型精简到仅“已知的 n-gram”不太可能节省任何内存。 (大多数桶槽应该由已知的 n-gram 使用,对于完整的哈希表,在查找时执行哈希到槽使用 less 内存而不是维护(字符串)->(槽)列表。这看起来像是一个具有巨大词汇量的普通爬网派生模型;我希望几乎所有的桶都是相关的,但你可以通过上面提到的 synthesize-n-grams 技巧来检查。)
    【解决方案2】:

    我最近自己也遇到了这个问题,不得不编写一个脚本来减小模型大小。 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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-05
      • 2013-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      相关资源
      最近更新 更多