Обучение машины понимать человеческие языки было увлекательной областью в течение нескольких десятилетий, и она остается до сих пор. Обработка естественного языка с использованием python стала более продвинутой с момента появления таких технологий, как word2vec, Doc2vec, Gensim, библиотеки sumy и т. д. Одна из контрольных точек в этой области, Word2Vec, является важной концепцией для всех энтузиастов машинного обучения. .

Подготовка данных:

Начнем эксперимент с простого примера Word2Vec. Сама концепция Word2Vec была построена вокруг идеи кодирования заданного предложения или набора слов в представление, подходящее для обучения машин. Также широко известно, что модель Word2Vec способна создавать похожие слова, которые встречаются в тех же контекстах в предоставленном текстовом корпусе. Давайте попробуем это. Я буду использовать корпус Шерлок Холмс, доступный на https://norvig.com/big.txt. По сути, нам нужны только предложения текста, состоящие только из слов. Итак, все спецы, цифры и прочие ненужные символы можно убрать.

import gensim
import re

replacables = ['i.','ii.','iii.','iv.','v.','vi.','vii.','viii.',
'ix.','x.','xi.','xii.','\n','[', ']', '/', ':', '!', '^',
'(',')', '{', '}', '~', '\\', '*', '>', '<','+', '=',';
',',','-', '_','?','&','%','@','#','|']
#function to process text...
def preprocess(filename):
    train_data = []
    with open(filename, 'r') as fp :
        text = fp.read().lower() # convert to lowercase
        text = re.sub(r'\d+', '', text) # removing all the numericals in the text
        for char in replacables :
            text = text.replace(char, ' ') # replacing the replacable characters with spaces
        text = text.split('.')
        text = [sent.split() for sent in text]
    return text

Инициация и обучение модели Word2Vec:

Давайте запустим и обучим модель word2vec. Мы будем использовать gensim.models.Word2Vec для решения нашего варианта использования. При создании модели объекту Word2Vec в качестве входных данных требуются обучающие данные, которые в нашем случае представляют собой большой список предложений (список токенов), содержащих главы Шерлок Холмс. Модель настраивается путем настройки таких параметров, как количество эпох, минимальная частота слов и т. д. (дополнительные примеры использования gensim.models.Word2Vec @ https://radimrehurek.com/gensim /models/word2vec.html). Время обучения зависит от размера корпуса и памяти машины. В данном случае это заняло не много времени, а пару минут на машине с 8 ГБ оперативной памяти.

corpus = preprocess("big.txt")
model = gensim.models.Word2Vec(corpus, min_count=1) # setting the minimum word-frequency to 1...
model.train(corpus, total_examples=len(corpus), epochs=70)

Тестирование:

После обучения модели ее также можно сохранить для использования в будущем с помощью метода model.save(). В следующем фрагменте показаны результаты тестирования модели для случаев поиска похожих слов.

def similar(test_element):
    for i in model.wv.most_similar(positive=[test_element], topn=5):
        print (i)
similar('hot')
#### output #################
('frosty', 0.551217257976532)
('baths', 0.5126125812530518)
('sunny', 0.4752189815044403)
('boiling', 0.4654223620891571)
('fahrenheit', 0.46115079522132874)
similar('pink')
#### output #################
('brownish', 0.5488739013671875)
('grey', 0.5438659191131592)
('velvet', 0.5415809154510498)
('silk', 0.5414213538169861)
('bast', 0.5396704077720642)

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

Тестирование Word2Vec на структурированных случайных данных:

В качестве альтернативы, приведенный выше эксперимент, проведенный со случайно подготовленными данными предложений, дал довольно интересные результаты. Этот эксперимент начинается с создания трех случайных структур предложений, образованных однозначными, двузначными и трехзначными числами, которые будут называться контекстами. Затем к одному из них добавляется случайное слово, которого нет в этих контекстах, выбранное случайным образом для формирования списка, который мы будем считать предложением, разбитым на список токенов для обучения модели Word2Vec.

Примечание.Список чисел здесь называется предложением, а отдельные числа из списка — словами.

import random
temp_ctxt0 = ['0','1','2','3','4','5','6'] # context-1
temp_ctxt1 = ['00','11','22','33','44','55','66'] # context-2
temp_ctxt2 = ['000','111','222','333','444','555','666'] # context-3
ctxts = {'0':temp_ctxt0, '1':temp_ctxt1, '2':temp_ctxt2}
test_seqs = []
# initializing vocabulary with the words in contexts...
man_vocab = set(temp_ctxt0) | set(temp_ctxt1) | set(temp_ctxt2)
for i in range(100):
    # extra random word to be added to the context to make a sentence...
    random_int = str(random.randint(667,6667))
    # random context to be chosen...
    random_ctxt_id = str(random.randint(0,2)) 
    # random index where the extra word is to be added...
    random_index = random.randint(0,len(temp_ctxt0)) 
    test_seqs.append(ctxts[random_ctxt_id][0:random_index] + [random_int] + ctxts[random_ctxt_id][random_index:])
    # add the extra word to vocabulary...
    man_vocab.add(random_int)
print ("Number of sentences in the corpus :",len(test_seqs))
print ("Length of vocabulary :",len(man_vocab))
# display the first 5 sentences in the data...
for li in test_seqs[0:5] :
    print (li)
#### output(this could vary) ##############
Number of sentences in the corpus : 100
Length of vocabulary : 121
['00', '4398', '11', '22', '33', '44', '55', '66']
['000', '111', '222', '333', '1135', '444', '555', '666']
['000', '111', '222', '333', '444', '555', '666', '5374']
['00', '11', '22', '1569', '33', '44', '55', '66']
['000', '111', '222', '333', '444', '555', '5843', '666']

Вышеприведенный код создает сцену, подготавливая наш игрушечный корпус из 100 предложений, которые должны быть переданы в модель Word2Vec для дальнейшего продвижения. Здесь используется тот же фрагмент для построения модели из примера «Шерлок Холмс».

import gensim
model = gensim.models.Word2Vec(test_seqs, min_count=1)
model.train(test_seqs, total_examples=len(test_seqs), epochs=70)
print ("Length of vocabulary built by W2V : ",len(model.wv.vocab.keys()))
print ("Vocabulary : ", model.wv.vocab.keys())
#### Output(this may vary) #############
Length of vocabulary built by W2V :  121
Vocabulary :  dict_keys(['00', '4398', '11', '22', '33', '44', '55', '66', '000', '111', '222', '333', '1135', '444', '555', '666', '5374', '1569', '5843', '1369', '0', '1', '2', '3', '4', '5', '5854', '6', '5819', '4453', '3486', '2499', '2403', '4289', '6323', '3339', '4198', '5712', '5232', '4131', '5491', '4418', '5303', '6586', '5862', '4444', '4571', '5181', '3430', '4766', '3721', '3458', '3306', '1598', '977', '978', '6637', '3530', '5585', '3075', '2240', '5121', '2483', '5009', '4518', '1170', '3281', '5606', '1069', '3566', '1306', '2443', '3068', '5122', '5379', '3772', '3049', '5960', '2688', '1367', '2811', '3415', '5871', '3795', '1418', '4556', '4266', '5872', '1904', '5813', '4919', '4496', '5535', '5783', '6063', '6152', '5249', '3927', '6111', '2052', '4602', '3258', '4526', '928', '724', '680', '3910', '3894', '2552', '5920', '2112', '2151', '2755', '4051', '4118', '4841', '5027', '5179', '5802', '2719', '2179'])

Как только модель будет обучена, давайте воспользуемся функцией similar(), чтобы вернуть первые 10 похожих слов, определенных моделью. Затем мы будем использовать случайное слово из словаря для проверки модели.

#return similar words identified....
def similar(test_element):
    for i in model.wv.most_similar(positive=[test_element],topn=10):
        print (i)
similar('1569')
#### Output(this may vary) ##########
('22', 0.9961907267570496)
('33', 0.9961261749267578)
('55', 0.99610835313797)
('66', 0.9959882497787476)
('11', 0.9959814548492432)
('00', 0.995940089225769)
('44', 0.9958961606025696)
('5232', 0.9953192472457886)
('3306', 0.9947472810745239)
('6323', 0.9946649074554443)

Мы видим, что верхние 7 элементов в выводе указывают, к какому контексту (в приведенном выше примере это указывает двузначный числовой контекст) принадлежит слово. То же самое происходит при попытке с другими словами в словаре. Здесь интересно отметить, что слова, за которыми следуют базовые слова в контексте, принадлежат к тому же контексту, что и тестовое слово. В приведенном выше случае слова «5232», «3306» и «6323» также относятся к двузначному числовому контексту (хотя они здесь не показаны, они принадлежат созданным нами предложениям). Но что будет на выходе, когда модели будут переданы базовые слова контекста?

similar('22')
#### Output(this may vary) ###############
('33', 0.9996486306190491)
('55', 0.9996386766433716)
('00', 0.9996378421783447)
('11', 0.9995988607406616)
('44', 0.9995454549789429)
('66', 0.999488353729248)
('3306', 0.9979302883148193)
('5249', 0.9976852536201477)
('5379', 0.9976080656051636)
('6323', 0.9974949359893799)

Что ж, оказалось, что остальные слова, принадлежащие к тому же контексту, за которыми следуют те, которые появляются в том же контексте («3306», «5249», «5379»….), даны в результатах. Оценки результатов пока можно игнорировать, так как нас очень беспокоят слова, а оценки кажутся довольно высокими, но значимыми для небольшого корпуса. Подводя итог, мы увидели, насколько хорошо модель Word2Vec работает, когда дело доходит до извлечения контекста и слов, которые появляются в том же контексте, за исключением кодирования предложений и слов в векторы.

Благодарности:

Идея применить модель Word2Vec к последовательности некоторых случайных элементов пришла ко мне после прочтения блога, посвященного рекомендации музыки с использованием модели Word2Vec, обученной спискам воспроизведения. Вот ссылка, если кому тоже будет интересно:



Использованная литература: