Как полностью удалить слово из модели 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's 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() слова не будут возвращать слова за пределами определенного домена.

6 ответов

Я написал функцию, которая удаляет слова из 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")

[("пиво", 0.8409687876701355),
("лагер", 0,7733745574951172),
("Пиво", 0,71753990650177),
("напитки", 0,668931245803833),
("лагеры", 0,6570086479187012),
('Yuengling_Lager', 0.655455470085144),
("микровар", 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")

[("лагеры", 0,6570085287094116),
("вино", 0,6217695474624634),
('bash', 0,20583480596542358),
("компьютер", 0.06677375733852386),
('python', 0.005948573350906372)]

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

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

wv = w2v_model.wv
words_to_trim = wv.index2word[5000:]
# In op's 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(), файл намного меньше.

Нет прямого способа сделать то, что вы ищете. Тем не менее, вы не совсем потеряны. Метод 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 используется, он ограничивает верхние n слов в словаре, это имеет смысл, только если вы отсортировали слова по частоте. Если вы не передаете restrict_vocab, self.vectors_norm это то, что входит в ограниченное

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

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

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

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

  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)

Та же идея, что и в ответе zsozso, но для Gensim 4:

      def restrict_w2v(w2v, restricted_word_set):
    new_index_to_key = []
    new_key_to_index = {}
    new_vectors = []
    for ind, word in enumerate(w2v.index_to_key):
        if word in restricted_word_set:
            new_key_to_index[word] = len(new_index_to_key)
            new_index_to_key.append(word)
            new_vectors.append(w2v.vectors[ind])
    w2v.index_to_key = new_index_to_key
    w2v.key_to_index = new_key_to_index
    w2v.vectors = np.array(new_vectors)

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

      restricted_words = ...
vectors = KeyedVectors.load_word2vec_format(input_file)
restrict_w2v(vectors, restricted_words)
vectors.save_word2vec_format(output_file)

Проверил, у меня работает (Gensim 4.3.1)

Но когда я хочу сгенерировать похожие слова, они должны исходить только из подмножества слов, специфичных для предметной области.

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

Пример

      import gensim.downloader

w2v = gensim.downloader.load('glove-twitter-50')
w2v.most_similar_to_given("hotel", ["plane", "house", "penguin"]) # yieldshouse
Другие вопросы по тегам