wake-up-neo.com

Berechnen Sie die Cosinusähnlichkeit mit 2 Satzfolgen

Aus Python: tf-idf-cosine: Um die Dokumentähnlichkeit zu finden , ist es möglich, die Dokumentähnlichkeit mit tf-idf cosine zu berechnen. Gibt es eine Möglichkeit, die Cosinusähnlichkeit zwischen zwei Zeichenfolgen zu berechnen, ohne externe Bibliotheken zu importieren?

s1 = "This is a foo bar sentence ."
s2 = "This sentence is similar to a foo bar sentence ."
s3 = "What is this string ? Totally not related to the other two lines ."

cosine_sim(s1, s2) # Should give high cosine similarity
cosine_sim(s1, s3) # Shouldn't give high cosine similarity value
cosine_sim(s2, s3) # Shouldn't give high cosine similarity value
58
alvas

Eine einfache Pure-Python-Implementierung wäre:

import re, math
from collections import Counter

Word = re.compile(r'\w+')

def get_cosine(vec1, vec2):
     intersection = set(vec1.keys()) & set(vec2.keys())
     numerator = sum([vec1[x] * vec2[x] for x in intersection])

     sum1 = sum([vec1[x]**2 for x in vec1.keys()])
     sum2 = sum([vec2[x]**2 for x in vec2.keys()])
     denominator = math.sqrt(sum1) * math.sqrt(sum2)

     if not denominator:
        return 0.0
     else:
        return float(numerator) / denominator

def text_to_vector(text):
     words = Word.findall(text)
     return Counter(words)

text1 = 'This is a foo bar sentence .'
text2 = 'This sentence is similar to a foo bar sentence .'

vector1 = text_to_vector(text1)
vector2 = text_to_vector(text2)

cosine = get_cosine(vector1, vector2)

print 'Cosine:', cosine

Drucke:

Cosine: 0.861640436855

Die hier verwendete Cosinusformel wird beschrieben hier .

Dies beinhaltet keine Gewichtung der Wörter durch tf-idf, aber um tf-idf verwenden zu können, benötigen Sie einen relativ großen Korpus, von dem aus Sie die tfidf-Gewichtung abschätzen können.

Sie können es auch weiterentwickeln, indem Sie Wörter aus einem Text, einem Stiel oder einer Lemmatize auf eine differenziertere Weise extrahieren.

140
vpekar

Die kurze Antwort lautet: "Nein, es ist nicht möglich, dies auf prinzipielle Weise zu tun, die auch aus der Ferne gut funktioniert." Es ist ein ungelöstes Problem in der Forschung zur Verarbeitung natürlicher Sprache und zufällig auch Gegenstand meiner Doktorarbeit. Ich fasse ganz kurz zusammen, wo wir uns befinden, und verweise auf einige Veröffentlichungen:

Bedeutung der Wörter

Die wichtigste Annahme hierbei ist, dass es möglich ist, einen Vektor zu erhalten, der jedes Wort im fraglichen Satz darstellt. Dieser Vektor wird normalerweise ausgewählt, um die Kontexte zu erfassen, in denen das Wort vorkommen kann. Wenn wir beispielsweise nur die drei Kontexte "essen", "rot" und "flauschig" betrachten, könnte das Wort "Katze" als [98, 1] dargestellt werden , 87], denn wenn Sie ein sehr, sehr langes Stück Text lesen würden (ein paar Milliarden Wörter sind heutzutage keine Seltenheit), würde das Wort "Katze" im Kontext von "flauschig" und "essen" sehr häufig vorkommen. , aber nicht so oft im Zusammenhang mit "rot". Ebenso könnte "Hund" als [87,2,34] und "Regenschirm" als [1,13,0] dargestellt werden. Wenn man diese Vektoren als Punkte im 3D-Raum betrachtet, ist "Katze" eindeutig näher an "Hund" als an "Regenschirm", daher bedeutet "Katze" auch etwas, das "Hund" ähnlicher ist als einem "Regenschirm".

Diese Linie wurde seit den frühen 90er Jahren untersucht (z. B. this Arbeit von Greffenstette) und hat einige überraschend gute Ergebnisse erbracht. Zum Beispiel sind hier ein paar zufällige Einträge in einem Thesaurus, den ich kürzlich erstellt habe, indem mein Computer Wikipedia gelesen hat:

theory -> analysis, concept, approach, idea, method
voice -> vocal, tone, sound, melody, singing
james -> william, john, thomas, robert, george, charles

Diese Listen ähnlicher Wörter wurden vollständig ohne menschliches Eingreifen erstellt - Sie geben Text ein und kommen einige Stunden später wieder.

Das Problem mit Phrasen

Vielleicht fragen Sie sich, warum wir bei längeren Phrasen wie "Ingwerfüchse lieben Obst" nicht dasselbe tun. Das liegt daran, dass wir nicht genug Text haben. Damit wir zuverlässig feststellen können, wem X ähnlich ist, müssen wir viele Beispiele für die Verwendung von X im Kontext sehen. Wenn X ein einzelnes Wort wie "Stimme" ist, ist dies nicht zu schwer. Je länger X wird, desto langsamer wird jedoch die Wahrscheinlichkeit, natürliche Vorkommen von X zu finden. Zum Vergleich: Google hat ungefähr 1 Milliarde Seiten mit dem Wort "Fuchs" und keine einzige Seite mit dem Wort "Ingwerfüchse lieben Früchte", obwohl es sich um einen perfekt gültigen englischen Satz handelt und wir alle verstehen, was er bedeutet.

Zusammensetzung

Um das Problem der Datensparsamkeit anzugehen, möchten wir eine Komposition durchführen, d. H. Vektoren für Wörter verwenden, die leicht aus realem Text zu erhalten sind, und die auf eine Weise zusammensetzen, die ihre Bedeutung erfasst. Die schlechte Nachricht ist, dass bisher niemand in der Lage war, das gut zu machen.

Die einfachste und naheliegendste Möglichkeit besteht darin, die einzelnen Wortvektoren zu addieren oder zu multiplizieren. Dies führt zu der unerwünschten Nebenwirkung, dass "Katzen jagen Hunde" und "Hunde jagen Katzen" für Ihr System dasselbe bedeuten würden. Wenn Sie multiplizieren, müssen Sie besonders vorsichtig sein, da sonst jeder Satz durch [0,0,0, ..., 0] dargestellt wird, was den Punkt besiegt.

Weiterführende Literatur

Ich werde auf die bisher vorgeschlagenen komplexeren Kompositionsmethoden nicht eingehen. Ich schlage vor, Sie lesen Katrin Erks "Vektorraummodelle von Wortbedeutung und Ausdruckbedeutung: eine Umfrage" . Dies ist eine sehr gute Umfrage auf hohem Niveau, um Ihnen den Einstieg zu erleichtern. Leider ist nicht frei auf der Website des Herausgebers verfügbar, senden Sie eine E-Mail direkt an den Autor, um eine Kopie zu erhalten. In diesem Artikel finden Sie Verweise auf viele weitere konkrete Methoden. Die verständlicheren sind von Mitchel und Lapata (2008) und Baroni und Zamparelli (2010) .


Nach Kommentar von @vpekar editieren: Das Fazit dieser Antwort ist die Tatsache zu betonen, dass es zwar naive Methoden gibt (z. B. Addition, Multiplikation, Oberflächenähnlichkeit usw.), dies jedoch sind grundlegend fehlerhaft und im Allgemeinen sollte man von ihnen keine große Leistung erwarten.

51
mbatchkarov

Vielen Dank @vpekar für Ihre Implementierung. Es hat sehr geholfen. Ich habe gerade festgestellt, dass das tf-idf-Gewicht bei der Berechnung der Cosinus-Ähnlichkeit fehlt. Der Counter (Word) gibt ein Wörterbuch zurück, das die Liste der Wörter zusammen mit ihrem Vorkommen enthält.

cos (q, d) = sim (q, d) = (q · d)/(| q || d |) = (Summe (qi, di)/(sqrt (Summe (qi2)))) * (sqrt ( Summe (vi2))) wobei i = 1 bis v)

  • qi ist das tf-idf-Gewicht von Begriff i in der Abfrage.
  • di ist die tf-idf
  • gewicht des Begriffs i im Dokument. | q | und | d | sind die Längen von q und d. 
  • Dies ist die Cosinusähnlichkeit von q und d. . . . . . oder äquivalent der Kosinus des Winkels zwischen q und d.

Bitte zögern Sie nicht, meinen Code hier anzusehen. Zunächst müssen Sie jedoch das Anaconda-Paket herunterladen. Es wird automatisch der Python-Pfad in Windows festgelegt. Fügen Sie diesen Python-Interpreter in Eclipse hinzu.

1
novice_dev

Nun, wenn Sie wissen, dass Word-Einbettungen wie Glove/Word2Vec/Numberbatch, ist Ihre Arbeit zur Hälfte erledigt. Wenn nicht, lassen Sie mich erklären, wie dies angegangen werden kann. Konvertieren Sie jeden Satz in Word-Token und stellen Sie jeden dieser Token als Vektoren von hoher Dimension dar (unter Verwendung der vorab trainierten Word-Einbettungen, oder Sie könnten trainieren sie selbst!). Sie erfassen also nicht nur die Ähnlichkeit ihrer Oberfläche, sondern extrahieren die Bedeutung jedes Worts, das den gesamten Satz enthält. Berechnen Sie anschließend deren Kosinusähnlichkeit und Sie sind eingestellt.

1
TheSN

Versuche dies. Laden Sie die Datei 'numberbatch-de-17.06.txt' von https://conceptnet.s3.amazonaws.com/downloads/2017/numberbatch/numberbatch-en-17.06.txt.gz herunter und extrahieren Sie sie. Die Funktion 'get_sentence_vector' verwendet eine einfache Summe von Word-Vektoren. Sie kann jedoch durch die Verwendung einer gewichteten Summe verbessert werden, wobei die Gewichte proportional zu Tf-Idf jedes Wortes sind. 

import math
import numpy as np

std_embeddings_index = {}
with open('path/to/numberbatch-en-17.06.txt') as f:
    for line in f:
        values = line.split(' ')
        Word = values[0]
        embedding = np.asarray(values[1:], dtype='float32')
        std_embeddings_index[Word] = embedding

def cosineValue(v1,v2):
    "compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
    sumxx, sumxy, sumyy = 0, 0, 0
    for i in range(len(v1)):
        x = v1[i]; y = v2[i]
        sumxx += x*x
        sumyy += y*y
        sumxy += x*y
    return sumxy/math.sqrt(sumxx*sumyy)


def get_sentence_vector(sentence, std_embeddings_index = std_embeddings_index ):
    sent_vector = 0
    for Word in sentence.lower().split():
        if Word not in std_embeddings_index :
            Word_vector = np.array(np.random.uniform(-1.0, 1.0, 300))
            std_embeddings_index[Word] = Word_vector
        else:
            Word_vector = std_embeddings_index[Word]
        sent_vector = sent_vector + Word_vector

    return sent_vector

def cosine_sim(sent1, sent2):
    return cosineValue(get_sentence_vector(sent1), get_sentence_vector(sent2))

Ich lief für die gegebenen Sätze und fand die folgenden Ergebnisse

s1 = "This is a foo bar sentence ."
s2 = "This sentence is similar to a foo bar sentence ."
s3 = "What is this string ? Totally not related to the other two lines ."

print cosine_sim(s1, s2) # Should give high cosine similarity
print cosine_sim(s1, s3) # Shouldn't give high cosine similarity value
print cosine_sim(s2, s3) # Shouldn't give high cosine similarity value

0.9851735249068168
0.6570885718962608
0.6589335425458225
0