【问题标题】:Load Pretrained glove vectors in python在 python 中加载预训练的手套向量
【发布时间】:2016-10-14 01:41:50
【问题描述】:

我从网上下载了预训练手套矢量文件。它是一个 .txt 文件。我无法加载和访问它。使用 gensim 很容易加载和访问词向量二进制文件,但是当它是文本文件格式时我不知道该怎么做。

提前致谢

【问题讨论】:

    标签: python-2.7 vector nlp


    【解决方案1】:

    手套模型文件采用词向量格式。您可以打开文本文件来验证这一点。以下是一小段可用于加载预训练手套文件的 sn-p 代码:

    import numpy as np
    
    def load_glove_model(File):
        print("Loading Glove Model")
        glove_model = {}
        with open(File,'r') as f:
            for line in f:
                split_line = line.split()
                word = split_line[0]
                embedding = np.array(split_line[1:], dtype=np.float64)
                glove_model[word] = embedding
        print(f"{len(glove_model)} words loaded!")
        return glove_model
    

    然后,您只需使用 gloveModel 变量即可访问词向量。

    print(gloveModel['hello'])

    【讨论】:

    • 我想知道是否有更快的方法来做到这一点。我正在使用与上面类似的代码,但处理整个 60 亿个令牌嵌入大约需要 27 小时。关于如何更快地做到这一点的任何想法?
    • @EdwardBurgin,我需要 1 分钟才能完成整个文件。请分享您在评论中提到的“类似代码”。
    • $ python test_glove.py 加载手套模型完成。 400000字加载!回溯(最后一次调用):文件“test_glove.py”,第 16 行,在 print(model['hello']) NameError: name 'model' is not defined
    • @MonaJalal Do model = loadGloveModel("filename.txt") 然后打印语句可以正常工作。
    • @jchook add f = open(gloveFile,'r', encoding='utf-8') 读取手套文件,它会工作
    【解决方案2】:

    你可以用 pandas 更快地做到这一点:

    import pandas as pd
    import csv
    
    words = pd.read_table(glove_data_file, sep=" ", index_col=0, header=None, quoting=csv.QUOTE_NONE)
    

    然后得到一个单词的向量:

    def vec(w):
      return words.loc[w].as_matrix()
    

    并找到最接近向量的词:

    words_matrix = words.as_matrix()
    
    def find_closest_word(v):
      diff = words_matrix - v
      delta = np.sum(diff * diff, axis=1)
      i = np.argmin(delta)
      return words.iloc[i].name
    

    【讨论】:

    • 虽然加载模型的时间减少了近一半,但访问时间却增加了 1000 倍。 loc 反对 dict 访问。我认为,我个人更喜欢较低的访问时间,因为这会影响培训时间。由于模型制作是一次性的工作,因此最好将时间投入其中并一劳永逸地保存。如果我错了,请纠正我。
    • 您应该在read_table 中使用更多参数:na_values=None, keep_default_na=False。否则,它会将许多有效字符串(例如“null”、“NA”等)视为nan 浮点值。
    • read_table 已弃用。请改用具有相同参数的read_csv
    【解决方案3】:

    我建议使用gensim 来做所有事情。您可以阅读该文件,还可以从这个出色的包上已经实现的许多方法中受益。

    假设您使用 C++ 程序生成了GloVe 向量,并且您的“-save-file”参数是“向量”。 Glove 可执行文件将为您生成两个文件,“vectors.bin”和“vectors.txt”。

    使用glove2word2vec将文本格式的GloVe向量转换为word2vec文本格式:

    from gensim.scripts.glove2word2vec import glove2word2vec
    glove2word2vec(glove_input_file="vectors.txt", word2vec_output_file="gensim_glove_vectors.txt")
    

    最后,使用KeyedVectors将word2vec txt读入gensim模型:

    from gensim.models.keyedvectors import KeyedVectors
    glove_model = KeyedVectors.load_word2vec_format("gensim_glove_vectors.txt", binary=False)
    

    现在您可以随意使用 gensim word2vec 方法(例如相似度)。

    【讨论】:

    • 看起来 glove2word2vec 发出警告This function is deprecated, use smart_open.open instead. See the migration notes for details: https://github.com/RaRe-Technologies/smart_open/blob/master/README.rst#migrating-to-the-new-open-function。我猜gensim函数需要更新
    • 这个警告在 gensim 3.8.3 版本中消失了。 glove2word2vec() 是 1000% 的路要走。
    【解决方案4】:

    我发现这种方法更快。

    import pandas as pd
    
    df = pd.read_csv('glove.840B.300d.txt', sep=" ", quoting=3, header=None, index_col=0)
    glove = {key: val.values for key, val in df.T.items()}
    

    保存字典:

    import pickle
    with open('glove.840B.300d.pkl', 'wb') as fp:
        pickle.dump(glove, fp)
    

    【讨论】:

      【解决方案5】:

      如果你想要的只是嵌入矩阵,这里有一个衬线

      np.loadtxt(path, usecols=range(1, dim+1), comments=None)

      path 是您下载的 GloVe 文件的路径,dim 是词嵌入的维度。

      如果你想要单词和对应的向量,你可以这样做

      glove = np.loadtxt(path, dtype='str', comments=None)

      并将单词和向量分开如下

      words = glove[:, 0]
      vectors = glove[:, 1:].astype('float')
      

      【讨论】:

        【解决方案6】:

        从文本文件加载词嵌入(在我的情况下是 glove.42B.300d 嵌入)需要一点时间(在我的机器上147.2s)。

        首先将文本文件转换为两个新文件有帮助:一个仅包含单词的文本文件(例如embeddings.vocab)和一个二进制文件文件,其中包含作为 numpy 结构的嵌入向量(例如 embeddings.npy)。

        转换后,我只需 4.96 秒 即可将相同的嵌入加载到内存中。这种方法最终得到与从文本文件中加载完全相同的字典。它在访问时间上同样高效,不需要任何额外的框架,但加载时间要快得多。

        使用此代码,您可以将嵌入文本文件转换为两个新文件:

        def convert_to_binary(embedding_path):
            f = codecs.open(embedding_path + ".txt", 'r', encoding='utf-8')
            wv = []
        
            with codecs.open(embedding_path + ".vocab", "w", encoding='utf-8') as vocab_write:
                count = 0
                for line in f:
                    splitlines = line.split()
                    vocab_write.write(splitlines[0].strip())
                    vocab_write.write("\n")
                    wv.append([float(val) for val in splitlines[1:]])
                count += 1
        
            np.save(embedding_path + ".npy", np.array(wv))
        

        通过这种方法,您可以有效地将其加载到您的内存中:

        def load_word_emb_binary(embedding_file_name_w_o_suffix):
            print("Loading binary word embedding from {0}.vocab and {0}.npy".format(embedding_file_name_w_o_suffix))
        
            with codecs.open(embedding_file_name_w_o_suffix + '.vocab', 'r', 'utf-8') as f_in:
                index2word = [line.strip() for line in f_in]
        
            wv = np.load(embedding_file_name_w_o_suffix + '.npy')
            word_embedding_map = {}
            for i, w in enumerate(index2word):
                word_embedding_map[w] = wv[i]
        
            return word_embedding_map
        

        免责声明:此代码无耻地从https://blog.ekbana.com/loading-glove-pre-trained-word-embedding-model-from-text-file-faster-5d3e8f2b8455 窃取。但它可能对这个线程有所帮助。

        【讨论】:

          【解决方案7】:

          Python3 版本也可以处理二元组和三元组:

          import numpy as np
          
          
          def load_glove_model(glove_file):
              print("Loading Glove Model")
              f = open(glove_file, 'r')
              model = {}
              vector_size = 300
              for line in f:
                  split_line = line.split()
                  word = " ".join(split_line[0:len(split_line) - vector_size])
                  embedding = np.array([float(val) for val in split_line[-vector_size:]])
                  model[word] = embedding
              print("Done.\n" + str(len(model)) + " words loaded!")
              return model
          

          【讨论】:

          • 您能否添加一个关于它如何处理二元组的简短说明?
          【解决方案8】:
          import os
          import numpy as np
          
          # store all the pre-trained word vectors
          print('Loading word vectors...')
          word2vec = {}
          with open(os.path.join('glove/glove.6B.%sd.txt' % EMBEDDING_DIM)) as f: #enter the path where you unzipped the glove file
            # is just a space-separated text file in the format:
            # word vec[0] vec[1] vec[2] ...
              for line in f:
                  values = line.split()
                  word = values[0]
                  vec = np.asarray(values[1:], dtype='float32')
                  word2vec[word] = vec
          print('Found %s word vectors.' % len(word2vec))
          

          【讨论】:

            【解决方案9】:

            此代码需要一些时间将手套嵌入存储在货架上,但与其他方法相比,加载它的速度要快得多。

            import os
            import numpy as np
            from contextlib import closing
            import shelve
            
            def store_glove_to_shelf(glove_file_path,shelf):
                print('Loading Glove')
                with open(os.path.join(glove_file_path)) as f:
                    for line in f:
                        values = line.split()
                        word = values[0]
                        vec = np.asarray(values[1:], dtype='float32')
                        shelf[word] = vec
            
            shelf_file_name = "glove_embeddings"
            glove_file_path = "glove/glove.840B.300d.txt"
            
            # Storing glove embeddings to shelf for faster load
            with closing(shelve.open(shelf_file_name + '.shelf', 'c')) as shelf:
                store_glove_to_shelf(glove_file_path,shelf)
                print("Stored glove embeddings from {} to {}".format(glove_file_path,shelf_file_name+'.shelf'))
            
            # To reuse the glove embeddings stored in shelf
            with closing(shelve.open(shelf_file_name + '.shelf')) as embeddings_index:
                # USE embeddings_index here , which is a dictionary
                print("Loaded glove embeddings from {}".format(shelf_file_name+'.shelf'))
                print("Found glove embeddings with {} words".format(len(embeddings_index)))
            

            【讨论】:

              【解决方案10】:

              每个语料库需要以包含词汇大小和向量大小的行开头。

              打开手套模型的.txt文件,在第一行输入向量的维度,先回车:

              例如,对于glove.6B.50d.txt,只需在第一行添加400000 50

              然后使用 gensim 将原始 .txt 矢量文件转换为 gensim 矢量格式:

              import gensim
              
              word_vectors = gensim.models.KeyedVectors.load_word2vec_format('path/glove.6B.50d.txt', binary=False)
              word_vectors.save('path/glove_gensim.txt')
              

              【讨论】:

                【解决方案11】:

                这里的其他一些方法需要更多存储空间(例如拆分文件),或者在我的个人笔记本电脑上运行速度很慢。我尝试了货架数据库,但它似乎在存储大小上爆炸了。这是一种“就地”方法,具有一次性文件读取时间成本和非常低的额外存储成本。我们将原始文本文件视为数据库,只存储每个单词的位置。例如,当您研究词向量的属性时,这非常有效。

                # First create a map from words to position in the file
                def get_db_mapping(fname):
                    char_ct = 0    # cumulative position in file
                    pos_map = dict()
                
                    with open(fname + ".txt", 'r', encoding='utf-8') as f:
                        for line in tqdm(f):
                            new_len = len(line)     # len of line
                
                            # get the word
                            splitlines = line.split()
                            word = splitlines[0].strip()
                
                            # store and increment counter
                            pos_map[word] = char_ct
                            char_ct += new_len
                
                    # write dict
                    with open(fname + '.db', 'wb') as handle:
                        pickle.dump(pos_map, handle)
                
                
                class Embedding:
                """Small wrapper so that we can use [] notation to fetch word vectors.
                It would be better to just have the file pointer and the pos_map as part
                of this class, but that's not how I wrote it initially."""
                    def __init__(self, emb_fn):
                        self.emb_fn = emb_fn
                
                    def __getitem__(self, item):
                        return self.emb_fn(item)
                
                
                def load_db_mapping(fname, cache_size=1000) -> Embedding:
                    """Creates a function closure that wraps access to the db mapping
                    and the text file that functions as db. Returns them as an
                    Embedding object"""
                    # get the two state objects: mapping and file pointer
                    with open(fname + '.db', 'rb') as handle:
                        pos_map = pickle.load(handle)
                    f = open(fname + ".txt", 'r', encoding='utf-8')
                
                    @lru_cache(maxsize=cache_size)
                    def get_vector(word: str):
                        pos = pos_map[word]
                        f.seek(pos, 0)
                
                        # special logic needed because of small count errors
                        fail_ct = 0
                        read_word = ""
                        while fail_ct < 5 and read_word != word:
                            fail_ct += 1
                            l = f.readline()
                            try:
                                splitlines = l.split()
                                read_word = splitlines[0].strip()
                            except:
                                continue
                        if read_word != word:
                            raise ValueError('word not found')
                
                        # actually return
                        return np.array([float(val) for val in splitlines[1:]])
                
                    return Embedding(get_vector)
                
                # to run
                k_glove_vector_name = 'glove.42B.300d'   # omit .txt
                get_db_mapping(k_glove_vector_name)      # run only once; creates .db
                word_embedding = load_db_mapping(k_glove_vector_name)
                word_embedding['hello']
                

                【讨论】:

                  【解决方案12】:
                  EMBEDDING_LIFE = 'path/to/your/glove.txt'
                  
                  def get_coefs(word,*arr): 
                        return word, np.asarray(arr, dtype='float32')
                  
                  embeddings_index = dict(get_coefs(*o.strip().split()) for o in open(EMBEDDING_FILE))
                  
                  all_embs = np.stack(embeddings_index.values())
                  emb_mean,emb_std = all_embs.mean(), all_embs.std()
                  word_index = tokenizer.word_index
                  nb_words = min(max_features, len(word_index))
                  
                  embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))
                  
                  for word, i in word_index.items():
                  if i >= max_features: continue
                  embedding_vector = embeddings_index.get(word)
                  if embedding_vector is not None: embedding_matrix[i] = embedding_vector
                  

                  【讨论】:

                  • 请对您的回答发表评论。为什么它比已经接受的更好?
                  • 这来自 kaggle,它在一些手套文件上爆炸了,例如800B.300d
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-11-21
                  • 2021-02-19
                  • 2020-02-07
                  • 2019-07-17
                  相关资源
                  最近更新 更多