Как полностью удалить слово из модели Word2Vec в gensim?

Учитывая модель, например

from gensim.models.word2vec import Word2Vec


documents = ["Human machine interface for lab abc computer applications",
"A survey of user opinion of computer system response time",
"The EPS user interface management system",
"System and human system engineering testing of EPS",
"Relation of user perceived response time to error measurement",
"The generation of random binary unordered trees",
"The intersection graph of paths in trees",
"Graph minors IV Widths of trees and well quasi ordering",
"Graph minors A survey"]

texts = [d.lower().split() for d in documents]

w2v_model = Word2Vec(texts, size=5, window=5, min_count=1, workers=10)

Можно удалить слово из словаря w2v, например

# Originally, it there.
>>> print(w2v_model['graph'])
[-0.00401433  0.08862179  0.08601206  0.05281207 -0.00673626]

>>> print(w2v_model.wv.vocab['graph'])
Vocab(count:3, index:5, sample_int:750148289)

# Find most similar words.
>>> print(w2v_model.most_similar('graph'))
[('binary', 0.6781558990478516), ('a', 0.6284914612770081), ('unordered', 0.5971308350563049), ('perceived', 0.5612867474555969), ('iv', 0.5470727682113647), ('error', 0.5346164703369141), ('machine', 0.480206698179245), ('quasi', 0.256790429353714), ('relation', 0.2496253103017807), ('trees', 0.2276223599910736)]

# We can delete it from the dictionary
>>> del w2v_model.wv.vocab['graph']
>>> print(w2v_model['graph'])
KeyError: "word 'graph' not in vocabulary"

Но когда мы делаем сходство с другими словами после удаления graph, мы видим, что слово graph появляется, например

>>> w2v_model.most_similar('binary')
[('unordered', 0.8710334300994873), ('ordering', 0.8463168144226074), ('perceived', 0.7764195203781128), ('error', 0.7316686511039734), ('graph', 0.6781558990478516), ('generation', 0.5770125389099121), ('computer', 0.40017056465148926), ('a', 0.2762695848941803), ('testing', 0.26335978507995605), ('trees', 0.1948457509279251)]

Как полностью удалить слово из модели Word2Vec в gensim?


обновленный

Ответить @vumaasha комментарий:

не могли бы вы дать некоторые сведения о том, почему вы хотите удалить слово?

  • Скажем, моя вселенная слов во всех словах в корпусе, чтобы узнать плотные отношения между всеми словами.

  • Но когда я хочу сгенерировать похожие слова, это должно происходить только из подмножества определенного домена.

  • Возможно генерировать более чем достаточно из .most_similar() затем фильтровать слова, но позволяет сказать, что пространство конкретного домена невелико, я мог бы искать слово, которое занимает 1000-е место, наиболее похожее, которое неэффективно.

  • Было бы лучше, если бы слово полностью удалено из векторов слов, тогда слова .most_similar() не будут возвращать слова за пределами определенного домена.

Ответ 1

Я написал функцию, которая удаляет слова из KeyedVectors, которых нет в предопределенном списке слов.

def restrict_w2v(w2v, restricted_word_set):
    new_vectors = []
    new_vocab = {}
    new_index2entity = []
    new_vectors_norm = []

    for i in range(len(w2v.vocab)):
        word = w2v.index2entity[i]
        vec = w2v.vectors[i]
        vocab = w2v.vocab[word]
        vec_norm = w2v.vectors_norm[i]
        if word in restricted_word_set:
            vocab.index = len(new_index2entity)
            new_index2entity.append(word)
            new_vocab[word] = vocab
            new_vectors.append(vec)
            new_vectors_norm.append(vec_norm)

    w2v.vocab = new_vocab
    w2v.vectors = new_vectors
    w2v.index2entity = new_index2entity
    w2v.index2word = new_index2entity
    w2v.vectors_norm = new_vectors_norm

Он переписывает все переменные, связанные со словами, на основе Word2VecKeyedVectors.

Использование:

w2v = KeyedVectors.load_word2vec_format("GoogleNews-vectors-negative300.bin.gz", binary=True)
w2v.most_similar("beer")

[('beers', 0.8409687876701355),
('lager', 0.7733745574951172),
('Beer', 0.71753990650177),
('drinks', 0.668931245803833),
('lagers', 0.6570086479187012),
('Yuengling_Lager', 0.655455470085144),
('microbrew', 0.6534324884414673),
('Brooklyn_Lager', 0.6501551866531372),
('suds', 0.6497018337249756),
('brewed_beer', 0.6490240097045898)]

restricted_word_set = {"beer", "wine", "computer", "python", "bash", "lagers"}
restrict_w2v(w2v, restricted_word_set)
w2v.most_similar("beer")

[('lagers', 0.6570085287094116),
('wine', 0.6217695474624634),
('bash', 0.20583480596542358),
('computer', 0.06677375733852386),
('python', 0.005948573350906372)]

Ответ 2

Нет прямого способа сделать то, что вы ищете. Однако вы не потерялись полностью. Метод most_similar реализован в классе WordEmbeddingsKeyedVectors (проверьте ссылку). Вы можете взглянуть на этот метод и изменить его в соответствии с вашими потребностями.

Строки, показанные ниже, выполняют фактическую логику вычисления похожих слов, вам нужно заменить переменную, limited векторами, соответствующими интересующим вас словам. Тогда вы закончите

limited = self.vectors_norm if restrict_vocab is None else self.vectors_norm[:restrict_vocab]
        dists = dot(limited, mean)
        if not topn:
            return dists
best = matutils.argsort(dists, topn=topn + len(all_words), reverse=True)

Обновить:

limited = self.vectors_norm if restrict_vocab is None else self.vectors_norm[:restrict_vocab]

Если вы видите эту строку, это означает, что если restrict_vocab используется ограничивает верхние п слова в Vocab, имеет смысл только тогда, когда вы отсортировали Vocab по частоте. Если вы не пропустите ограничение_vocab, self.vectors_norm - это то, что ограничивается

метод most_similar вызывает другой метод init_sims. Это инициализирует значение для [self.vector_norm][4] как показано ниже

        self.vectors_norm = (self.vectors / sqrt((self.vectors ** 2).sum(-1))[..., newaxis]).astype(REAL)

поэтому вы можете забрать слова, которые вас интересуют, подготовить свою норму и использовать ее вместо ограниченного. Это должно работать

Ответ 3

Обратите внимание, что это не урезает модель как таковую. Он обрезает объект KeyedVectors, на котором основан поиск сходства.

Предположим, вы хотите сохранить только 5000 лучших слов в вашей модели.

wv = w2v_model.wv
words_to_trim = wv.index2word[5000:]
# In op case 
# words_to_trim = ['graph'] 
ids_to_trim = [wv.vocab[w].index for w in words_to_trim]

for w in words_to_trim:
    del wv.vocab[w]

wv.vectors = np.delete(wv.vectors, ids_to_trim, axis=0)
wv.init_sims(replace=True)

for i in sorted(ids_to_trim, reverse=True):
    del(wv.index2word[i])

Это делает работу, потому что класс BaseKeyedVectors содержит следующие атрибуты: self.vectors, self.vectors_norm, self.vocab, self.vector_size, self.index2word.

Преимущество этого состоит в том, что если вы пишете KeyedVectors с использованием таких методов, как save_word2vec_format(), файл будет намного меньше.

Ответ 4

Попробовал и почувствовал, что самый простой способ заключается в следующем:

  1. Получите вложения Word2Vec в формате текстового файла.
  2. Определите строки, соответствующие векторам слов, которые вы хотели бы сохранить.
  3. Напишите новый текстовый файл Word2Vec для встраивания модели.
  4. Загрузите модель и наслаждайтесь (сохраните в бинарный файл, если хотите, и т.д.)...

Мой пример кода выглядит следующим образом:

line_no = 0 # line0 = header
numEntities=0
targetLines = []

with open(file_entVecs_txt,'r') as fp:
    header = fp.readline() # header

    while True:
        line = fp.readline()
        if line == '': #EOF
            break
        line_no += 1

        isLatinFlag = True
        for i_l, char in enumerate(line):
            if not isLatin(char): # Care about entity that is Latin-only
                isLatinFlag = False
                break
            if char==' ': # reached separator
                ent = line[:i_l]
                break

        if not isLatinFlag:
            continue

        # Check for numbers in entity
        if re.search('\d',ent):
            continue

        # Check for entities with subheadings '#' (e.g. 'ENTITY/Stereotactic_surgery#History')
        if re.match('^ENTITY/.*#',ent):
            continue

        targetLines.append(line_no)
        numEntities += 1

# Update header with new metadata
header_new = re.sub('^\d+',str(numEntities),header,count=1)

# Generate the file
txtWrite('',file_entVecs_SHORT_txt)
txtAppend(header_new,file_entVecs_SHORT_txt)

line_no = 0
ptr = 0
with open(file_entVecs_txt,'r') as fp:
    while ptr < len(targetLines):
        target_line_no = targetLines[ptr]

        while (line_no != target_line_no):
            fp.readline()
            line_no+=1

        line = fp.readline()
        line_no+=1
        ptr+=1
        txtAppend(line,file_entVecs_SHORT_txt)

FYI. Неудачная попытка я опробовал @zsozso метод (с np.array модификаций, предложенных @Taegyung), оставил ее работать на ночь в течение по крайней мере 12 часов, он все еще застряли в получении новых слов из ограниченного набора...). Возможно, это потому, что у меня много сущностей... Но мой метод текстового файла работает в течение часа.

НЕДОСТАТОЧНЫЙ КОД

# [FAILED] Stuck at Building new vocab...
def restrict_w2v(w2v, restricted_word_set):
    new_vectors = []
    new_vocab = {}
    new_index2entity = []
    new_vectors_norm = []

    print('Building new vocab..')

    for i in range(len(w2v.vocab)):

        if (i%int(1e6)==0) and (i!=0):
            print(f'working on {i}')

        word = w2v.index2entity[i]
        vec = np.array(w2v.vectors[i])
        vocab = w2v.vocab[word]
        vec_norm = w2v.vectors_norm[i]
        if word in restricted_word_set:
            vocab.index = len(new_index2entity)
            new_index2entity.append(word)
            new_vocab[word] = vocab
            new_vectors.append(vec)
            new_vectors_norm.append(vec_norm)

    print('Assigning new vocab')
    w2v.vocab = new_vocab
    print('Assigning new vectors')
    w2v.vectors = np.array(new_vectors)
    print('Assigning new index2entity, index2word')
    w2v.index2entity = new_index2entity
    w2v.index2word = new_index2entity
    print('Assigning new vectors_norm')
    w2v.vectors_norm = np.array(new_vectors_norm)