Вложения — это представления значений слов непосредственно из их распределения в текстах. Эти представления используются в каждом приложении НЛП, использующем значение.

Полный код этой статьи можно найти ЗДЕСЬ.

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

Хотя у слов не так много синонимов, у большинства слов есть много похожих терминов. Рис не является синонимом фасоли, но рис и фасоль, несомненно, являются похожими словами (оба продукта). Сходство представляет собой ценный переход от синонимов к разговору об отношениях между смыслом слова; это упростит нашу задачу.

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

Срок-документ Матрица

Векторные или дистрибутивные модели значения обычно основаны на матрице совместного появления, способе представления того, как часто слова встречаются одновременно. Мы реализуем две популярные матрицы; матрица термин-документ и матрица термин-контекст.

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

def term_document(self, word_list):
    for play, word in word_list:
        if (play, word) in self.term_document_matrix:
            self.term_document_matrix[(play, word)] += 1
        else:
            self.term_document_matrix[(play, word)] = 1
    return self.term_document_matrix

Пример данных в нашей матрице:

('Twelfth Night', 'love'): 77
('Julius Caesar', 'come'): 75
('Romeo and Juliet', 'enter'): 74

Вышеприведенное означает, что слово «войти» встречается 74 раза в предложениях пьесы «Ромео и Джульетта» и так далее.

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

Значения косинуса варьируются от 1 для векторов, указывающих в одном направлении, до 0 для ортогональных векторов.

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

def cos_sim(self, vector1, vector2):
    cosine_similarity = 1 - spatial.distance.cosine(vector1, vector2)
    print(cosine_similarity)

Мы можем легко проверить это на небольшой выборке:

Реализация этого для проверки:

apricot = [1, 0, 0]
digital = [0, 1, 2]
information = [1, 6, 1]
print(self.cos_sim(apricot, digital))
0.0

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

def cal_cosine(self, docs):
    tf = self.read_csv()
    play_dict = {}
    for play in self.play_names:
        play_vec = []
        for word in self.vocab:
            play_vec.append(tf[(play, word)])
        play_dict[play] = play_vec

    scores = []
    for k1, v1 in play_dict.items():
        for k2, v2 in play_dict.items():
            if k1 <= k2: continue
            scores.append((self.cos_sim(v1, v2), (k1 ,k2)))
    scores.sort(reverse=True)
    print(scores[:25])

Наши главные сходства косинусов:

(0.9885556994486011, ('Cymbeline', 'A Winters Tale')),(0.9817079988849085, ('Henry VI Part 2', 'Henry VI Part 1')),(0.9811981513422073, ('Alls well that ends well', 'A Winters Tale'))

Матрица терминов-контекстов

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

Решением этой проблемы является использование взвешивания tf-idf, которое является произведением двух терминов:

TF - это частота термина, т.е. частота слова t в документе d, это рассчитывается в пространстве журнала:

IDF — частота обратного документа N/df; где N — общее количество документов в коллекции, а df — количество документов, в которых встречается термин. Это дает более высокий вес словам, которые встречаются только в нескольких документах. Термины, которые ограничены несколькими документами, полезны для отличия этих документов от остальной части коллекции. Чем меньше документов, в которых встречается термин, тем выше этот вес, он также рассчитывается в лог-пространстве:

TF-IDF — это скалярное произведение TF и IDF, поэтому рассчитывается как:

Давайте попробуем это на нашем текущем корпусе. Мы начнем с реализации матрицы терминов и контекста, где мы будем смотреть на каждое слово и 4 слова, до и после него, а затем подсчитаем частоту его появления вокруг одного и того же слова.

def term_context(self):
    term_context = defaultdict(lambda: defaultdict(lambda: 0))
    with open("ShakespearePlays_text.csv", "r") as f:
        # load csv
        reader = csv.reader(f, delimiter=";")
        # loop through line in csv
        for line in reader:
            play_name = line[1]
            if play_name not in self.play_names:
                continue
            tokens = line[5].split()
            sentence = []
            for term in tokens:
                token = self.clean(term)
                if token in self.vocab:
                    sentence.append(token)
            for i in range(len(sentence)):
                word = sentence[i]
                for j in range(max(0, i - 4), min(len(sentence), i + 5)):
                    if i == j: continue
                    term_context[word][sentence[j]] += 1
    return term_context

Наша матрица термин-контекст выглядит примерно так:

'befortune': ({'i': 1, 'wish': 1, 'all': 1, 'good': 1, 'you': 1})
'bohemia': ({'chance': 1, 'camillo': 1, 'to': 4, 'visit': 1,'on': 1, 'difference': 1, 'betwixt': 1, 'our': 1, 'and': 5, 'your': 1, 'sicilia': 1, 'means': 1, 'pay': 1, 'the': 5, 'visitation': 1}

Далее мы построим матрицу частоты нашего документа, мы сделаем это, используя приведенный выше термин контекст (чтобы сократить время вычислений и работать с меньшим подмножеством),

def cal_doc_freq(self, term_frequency):
    values_per_key = {}
    for k, v in term_frequency:
        values_per_key.setdefault(k, set()).add(v)
    counts = {k: len(v) for k, v in values_per_key.items()}
    return counts

Мы рассчитаем нашу частоту документов для всех ячеек в нашем контексте термина по формуле ниже:

Мы вычислим TF-IDF, умножив каждую ячейку в нашей матрице контекста термина на частоту слова в документе из нашей матрицы частоты документа выше:

    t = self.read_csv()
    counts = self.cal_doc_freq(t)
    tc = self.term_context()
    for i in tc:
        for j in tc[i].keys():
            tc[i][j] = tc[i][j] * (1 / counts[j])
    return tc

Мы проверим небольшое подмножество нашего словарного запаса и найдем TF-IDF сходство слов romeo", "juliet", "дворянин", "цезарь", и «друг» против других слов.

def compute_word_similarity(self, words):
    tc = self.idf()

    def to_vec(w):
        vec = []
        for x in self.vocab:
            vec.append(tc[w][x])
        return vec

    for word in words:
        word_vec = to_vec(word)
        scores = []
        c = 0
        for k in tc.keys():
            if k == word: continue
            k_vec = to_vec(k)
            scores.append((self.cos_sim(word_vec, k_vec), k))
            c += 1
            # if  c > 10: break
        scores.sort(reverse=True)
        print("Top-5 matches for " + word + ": ", scores[:5])

Наши топ-5 похожих слов:

Top-5 matches for romeo:  [(0.7349592582145151, 'dead'), (0.7291389527324256, 'he'), (0.7256033280986567, 'then'), (0.7237096963536124, 'it'), (0.719416236702853, 'where')]
Top-5 matches for juliet:  [(0.7840722487008701, 'lucius'), (0.7700540752904482, 'servants'), (0.7692483251584336, 'soldiers'), (0.7682255792237922, 'warwick'), (0.7672900349518263, 'others')]
Top-5 matches for nobleman:  [(0.8139265551526883, 'woman'), (0.813455008644156, 'soldier'), (0.808373550553078, 'little'), (0.8053083580334184, 'gentleman'), (0.8046068607590102, 'stranger')]
Top-5 matches for caesar:  [(0.8897940437391335, 'him'), (0.8825311102107262, 'them'), (0.8718307075270313, 'that'), (0.8707738937545483, 'his'), (0.8674185090147457, 'me')]
Top-5 matches for friend:  [(0.9280138220686541, 'wife'), (0.9239327316145367, 'daughter'), (0.9111186066627961, 'father'), (0.9091224168395339, 'brother'), (0.9086148854991047, 'mother')]

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

Я уверен, что можно было бы улучшить наши прогнозы, но это хорошее начало.

Ждем вопросов, комментариев и отзывов. Спасибо.