Как полностью удалить слово из модели 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)
Итак, вы можете подобрать слова, которые вас интересуют, подготовить их норму и использовать вместо ограниченных. Это должно работать
Попробовал и почувствовал, что самый простой способ заключается в следующем:
- Получите вложения Word2Vec в формате текстового файла.
- Определите строки, соответствующие векторам слов, которые вы хотели бы сохранить.
- Напишите новый текстовый файл Word2Vec для встраивания модели.
- Загрузите модель и наслаждайтесь (сохраните в бинарный файл, если хотите, и т. Д.)...
Мой пример кода выглядит следующим образом:
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