Wie berechnet man die Ähnlichkeit zwischen zwei Textdokumenten?

Lesezeit: 11 Minuten

Benutzer-Avatar
Reily Bourne

Ich möchte an einem NLP-Projekt in einer beliebigen Programmiersprache arbeiten (obwohl Python meine Präferenz sein wird).

Ich möchte zwei Dokumente nehmen und feststellen, wie ähnlich sie sind.

  • Ähnliche Frage hier stackoverflow.com/questions/101569/… mit einigen netten Antworten

    Benutzer963720

    30. Juni 2012 um 18:07 Uhr

Benutzer-Avatar
Fred Fu

Der übliche Weg, dies zu tun, besteht darin, die Dokumente in TF-IDF-Vektoren umzuwandeln und dann die Kosinus-Ähnlichkeit zwischen ihnen zu berechnen. Jedes Lehrbuch zum Informationsabruf (IR) deckt dies ab. Siehe insb. Einführung in die Informationsbeschaffungdas kostenlos und online verfügbar ist.

Berechnen von paarweisen Ähnlichkeiten

TF-IDF (und ähnliche Texttransformationen) sind in den Python-Paketen implementiert Gensim und scikit-lernen. Im letzteren Paket ist die Berechnung von Kosinusähnlichkeiten so einfach wie möglich

from sklearn.feature_extraction.text import TfidfVectorizer

documents = [open(f).read() for f in text_files]
tfidf = TfidfVectorizer().fit_transform(documents)
# no need to normalize, since Vectorizer will return normalized tf-idf
pairwise_similarity = tfidf * tfidf.T

oder, wenn es sich bei den Dokumenten um einfache Zeichenfolgen handelt,

>>> corpus = ["I'd like an apple", 
...           "An apple a day keeps the doctor away", 
...           "Never compare an apple to an orange", 
...           "I prefer scikit-learn to Orange", 
...           "The scikit-learn docs are Orange and Blue"]                                                                                                                                                                                                   
>>> vect = TfidfVectorizer(min_df=1, stop_words="english")                                                                                                                                                                                                   
>>> tfidf = vect.fit_transform(corpus)                                                                                                                                                                                                                       
>>> pairwise_similarity = tfidf * tfidf.T 

obwohl Gensim möglicherweise mehr Optionen für diese Art von Aufgabe hat.

Siehe auch diese Frage.

[Disclaimer: I was involved in the scikit-learn TF-IDF implementation.]

Interpretieren der Ergebnisse

Von oben, pairwise_similarity ist ein Scipy spärliche Matrix das ist quadratisch, wobei die Anzahl der Zeilen und Spalten gleich der Anzahl der Dokumente im Korpus ist.

>>> pairwise_similarity                                                                                                                                                                                                                                      
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 17 stored elements in Compressed Sparse Row format>

Sie können das Sparse-Array über in ein NumPy-Array konvertieren .toarray() oder .A:

>>> pairwise_similarity.toarray()                                                                                                                                                                                                                            
array([[1.        , 0.17668795, 0.27056873, 0.        , 0.        ],
       [0.17668795, 1.        , 0.15439436, 0.        , 0.        ],
       [0.27056873, 0.15439436, 1.        , 0.19635649, 0.16815247],
       [0.        , 0.        , 0.19635649, 1.        , 0.54499756],
       [0.        , 0.        , 0.16815247, 0.54499756, 1.        ]])

Angenommen, wir möchten das Dokument finden, das dem endgültigen Dokument am ähnlichsten ist: „Die scikit-learn-Dokumente sind orange und blau“. Dieses Dokument hat Index 4 in corpus. Den Index des ähnlichsten Dokuments finden Sie unter Nehmen Sie den Argmax dieser Zeile, aber zuerst müssen Sie die Einsen maskieren, die die Ähnlichkeit jedes Dokuments mit sich selbst darstellen. Letzteres können Sie durch np.fill_diagonal()und ersteres durch np.nanargmax():

>>> import numpy as np     
                                                                                                                                                                                                                                  
>>> arr = pairwise_similarity.toarray()     
>>> np.fill_diagonal(arr, np.nan)                                                                                                                                                                                                                            
                                                                                                                                                                                                                 
>>> input_doc = "The scikit-learn docs are Orange and Blue"                                                                                                                                                                                                  
>>> input_idx = corpus.index(input_doc)                                                                                                                                                                                                                      
>>> input_idx                                                                                                                                                                                                                                                
4

>>> result_idx = np.nanargmax(arr[input_idx])                                                                                                                                                                                                                
>>> corpus[result_idx]                                                                                                                                                                                                                                       
'I prefer scikit-learn to Orange'

Hinweis: Der Zweck der Verwendung einer Sparse-Matrix besteht darin, (eine beträchtliche Menge an Platz) für ein großes Korpus und Vokabular zu sparen. Anstatt in ein NumPy-Array zu konvertieren, könnten Sie Folgendes tun:

>>> n, _ = pairwise_similarity.shape                                                                                                                                                                                                                         
>>> pairwise_similarity[np.arange(n), np.arange(n)] = -1.0
>>> pairwise_similarity[input_idx].argmax()                                                                                                                                                                                                                  
3

  • @larsmans Können Sie das Array nach Möglichkeit ein wenig erklären, wie soll ich dieses Array lesen? Die ersten beiden Spalten sind Ähnlichkeiten zwischen den ersten beiden Sätzen?

    – Semikolons hinzufügen

    25. August 2012 um 0:47 Uhr

  • @Null-Hypothese: An Position (i,j) finden Sie den Ähnlichkeitswert zwischen Dokument i und Dokument j. An Position (0,2) befindet sich also der Ähnlichkeitswert zwischen dem ersten Dokument und dem dritten (bei nullbasierter Indizierung), der derselbe Wert ist, den Sie bei (2,0) finden, da Kosinusähnlichkeit kommutativ ist.

    – Fred Foo

    26. August 2012 um 11:24 Uhr

  • Wenn ich alle Werte außerhalb der Diagonalen von 1 mitteln würde, wäre das eine vernünftige Methode, um eine einzige Punktzahl zu erhalten, die zeigt, wie ähnlich die vier Dokumente einander sind? Wenn nicht, gibt es eine bessere Möglichkeit, die Gesamtähnlichkeit zwischen mehreren Dokumenten zu bestimmen?

    – Benutzer301752

    11. Dezember 2012 um 16:05 Uhr

  • @ user301752: Sie könnten den elementweisen Mittelwert der tf-idf-Vektoren nehmen (wie es k-means tun würde). X.mean(axis=0), dann berechnen Sie den durchschnittlichen/maximalen/medianen (∗) euklidischen Abstand von diesem Mittelwert. (∗) Wählen Sie, was Ihnen gefällt.

    – Fred Foo

    11. Dezember 2012 um 16:12 Uhr


  • @curious: Ich habe den Beispielcode auf die aktuelle scikit-learn-API aktualisiert; Vielleicht möchten Sie den neuen Code ausprobieren.

    – Fred Foo

    14. Januar 2013 um 15:34 Uhr

Benutzer-Avatar
Renaud

Identisch mit @larsman, aber mit etwas Vorverarbeitung

import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer

nltk.download('punkt') # if necessary...


stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)

def stem_tokens(tokens):
    return [stemmer.stem(item) for item in tokens]

'''remove punctuation, lowercase, stem'''
def normalize(text):
    return stem_tokens(nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))

vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words="english")

def cosine_sim(text1, text2):
    tfidf = vectorizer.fit_transform([text1, text2])
    return ((tfidf * tfidf.T).A)[0,1]


print cosine_sim('a little bird', 'a little bird')
print cosine_sim('a little bird', 'a little bird chirps')
print cosine_sim('a little bird', 'a big dog barks')

  • @ Renaud, wirklich gute und klare Antwort! Ich habe zwei Zweifel: I) was ist das [0,1] dass Sie nach tfidf * tfidf.T einbauen) und II) Die inverse Dokumentenhäufigkeit wird aus allen Artikeln gebildet oder nur aus zwei (wenn man bedenkt, dass Sie mehr als 2 haben)?

    – Ökonom_Ayahuasca

    21. April 2016 um 14:55 Uhr


  • @AndresAzqueta [0,1] sind die Positionen in der Matrix für die Ähnlichkeit, da zwei Texteingaben eine symmetrische 2×2-Matrix erzeugen.

    – Philipp Bergström

    1. Mai 2016 um 20:18 Uhr


  • @ Renaud, vielen Dank für Ihren vollständigen Code. Für diejenigen, die auf den Fehler gestoßen sind, nach nltk.download() zu fragen, können Sie einfach nltk.download(‘punkt’) tun. Sie müssen nicht alles herunterladen.

    – 1 Mann

    5. Oktober 2016 um 16:46 Uhr

  • @Renaud Ich bekomme kein grundlegenderes Problem. Welche Textzeichenfolgen sollten fitund welches transform?

    – John Strood

    21. August 2018 um 8:25 Uhr

  • @JohnStrood Ich verstehe deine Frage nicht, entschuldige, könntest du sie neu formulieren?

    – Renaud

    25. August 2018 um 20:53 Uhr

Benutzer-Avatar
Koustuv Sinha

Es ist eine alte Frage, aber ich fand, dass dies leicht getan werden kann Geräumig. Sobald das Dokument gelesen ist, wird eine einfache api similarity kann verwendet werden, um die Kosinus-Ähnlichkeit zwischen den Dokumentenvektoren zu finden.

Installieren Sie zunächst das Paket und laden Sie das Modell herunter:

pip install spacy
python -m spacy download en_core_web_sm

Dann so verwenden:

import spacy
nlp = spacy.load('en_core_web_sm')
doc1 = nlp(u'Hello hi there!')
doc2 = nlp(u'Hello hi there!')
doc3 = nlp(u'Hey whatsup?')

print (doc1.similarity(doc2)) # 0.999999954642
print (doc2.similarity(doc3)) # 0.699032527716
print (doc1.similarity(doc3)) # 0.699032527716

  • Ich frage mich, warum die Ähnlichkeit zwischen doc1 und doc2 0,999999954642 und nicht 1,0 beträgt

    – JordanBelf

    14. Juli 2017 um 1:19 Uhr

  • @JordanBelf Gleitkommazahlen wandern in den meisten Sprachen ein wenig herum – da sie in digitalen Darstellungen keine unbegrenzte Genauigkeit haben können. zB bei Gleitkommaoperationen oder bei der Erzeugung irrationaler Zahlen lassen sich immer winzige Rundungsfehler einschleichen die sich dann multiplizieren. Dies ist die Kehrseite einer solch flexiblen Darstellung in Bezug auf den Maßstab.

    – Scipilot

    16. Juli 2017 um 1:48 Uhr

  • Was ist die Abstandsfunktion, die diese Ähnlichkeitsmethode in diesem Fall verwendet?

    – ikel

    5. Februar 2018 um 3:08 Uhr

  • @Cybernetic Schau mal rein Wie wird die .similarity-Methode in SpaCy berechnet?

    Benutzer7075574

    12. November 2019 um 2:48 Uhr

  • Dies ist höchstwahrscheinlich die richtige Antwort für das OP, wenn es nach 2012 geschrieben worden wäre. Dies ist wirklich schnell einsatzbereit, nur Pip-Installationsspeicherplatz, und Sie können in weniger als 5 Minuten loslegen. Es gibt andere bessere, leistungsstarke Antworten da draußen, aber State-of-the-Art war nicht gefragt.

    – Dämongolem

    29. April 2020 um 13:49 Uhr


Benutzer-Avatar
Rohola Zandie

Wenn Sie nach etwas sehr Genauem suchen, müssen Sie ein besseres Tool als tf-idf verwenden. Universeller Satzkodierer ist eine der genauesten, um die Ähnlichkeit zwischen zwei beliebigen Textteilen zu finden. Google hat vortrainierte Modelle bereitgestellt, die Sie für Ihre eigene Anwendung verwenden können, ohne etwas von Grund auf neu trainieren zu müssen. Zuerst müssen Sie tensorflow und tensorflow-hub installieren:

    pip install tensorflow
    pip install tensorflow_hub

Mit dem folgenden Code können Sie jeden Text in eine Vektordarstellung mit fester Länge konvertieren und dann das Skalarprodukt verwenden, um die Ähnlichkeit zwischen ihnen herauszufinden

import tensorflow_hub as hub
module_url = "https://tfhub.dev/google/universal-sentence-encoder/1?tf-hub-format=compressed"

# Import the Universal Sentence Encoder's TF Hub module
embed = hub.Module(module_url)

# sample text
messages = [
# Smartphones
"My phone is not good.",
"Your cellphone looks great.",

# Weather
"Will it snow tomorrow?",
"Recently a lot of hurricanes have hit the US",

# Food and health
"An apple a day, keeps the doctors away",
"Eating strawberries is healthy",
]

similarity_input_placeholder = tf.placeholder(tf.string, shape=(None))
similarity_message_encodings = embed(similarity_input_placeholder)
with tf.Session() as session:
    session.run(tf.global_variables_initializer())
    session.run(tf.tables_initializer())
    message_embeddings_ = session.run(similarity_message_encodings, feed_dict={similarity_input_placeholder: messages})

    corr = np.inner(message_embeddings_, message_embeddings_)
    print(corr)
    heatmap(messages, messages, corr)

und der Code zum Plotten:

def heatmap(x_labels, y_labels, values):
    fig, ax = plt.subplots()
    im = ax.imshow(values)

    # We want to show all ticks...
    ax.set_xticks(np.arange(len(x_labels)))
    ax.set_yticks(np.arange(len(y_labels)))
    # ... and label them with the respective list entries
    ax.set_xticklabels(x_labels)
    ax.set_yticklabels(y_labels)

    # Rotate the tick labels and set their alignment.
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right", fontsize=10,
         rotation_mode="anchor")

    # Loop over data dimensions and create text annotations.
    for i in range(len(y_labels)):
        for j in range(len(x_labels)):
            text = ax.text(j, i, "%.2f"%values[i, j],
                           ha="center", va="center", color="w", 
fontsize=6)

    fig.tight_layout()
    plt.show()

das Ergebnis wäre:
die Ähnlichkeitsmatrix zwischen Textpaaren

Wie Sie sehen können, besteht die größte Ähnlichkeit zwischen Texten mit sich selbst und dann mit ihren nahen Texten in der Bedeutung.

WICHTIG: Wenn Sie den Code zum ersten Mal ausführen, ist er langsam, da er das Modell herunterladen muss. Wenn Sie verhindern möchten, dass das Modell erneut heruntergeladen wird, und das lokale Modell verwenden, müssen Sie einen Ordner für den Cache erstellen und ihn der Umgebungsvariable hinzufügen und dann nach der ersten Ausführung diesen Pfad verwenden:

tf_hub_cache_dir = "universal_encoder_cached/"
os.environ["TFHUB_CACHE_DIR"] = tf_hub_cache_dir

# pointing to the folder inside cache dir, it will be unique on your system
module_url = tf_hub_cache_dir+"/d8fbeb5c580e50f975ef73e80bebba9654228449/"
embed = hub.Module(module_url)

Mehr Informationen: https://tfhub.dev/google/universal-sentence-encoder/2

Als Ähnlichkeitsmaß von Dokumenten wird im Allgemeinen eine Kosinus-Ähnlichkeit zwischen zwei Dokumenten verwendet. In Java können Sie verwenden Lucene (wenn Ihre Sammlung ziemlich groß ist) oder LingPipe um dies zu tun. Das Grundkonzept wäre, die Begriffe in jedem Dokument zu zählen und das Skalarprodukt der Begriffsvektoren zu berechnen. Die Bibliotheken bieten mehrere Verbesserungen gegenüber diesem allgemeinen Ansatz, z. B. die Verwendung inverser Dokumenthäufigkeiten und die Berechnung von tf-idf-Vektoren. Wenn Sie nach etwas Komplexem suchen, bietet LingPipe auch Methoden zur Berechnung der LSA-Ähnlichkeit zwischen Dokumenten, was bessere Ergebnisse liefert als die Kosinus-Ähnlichkeit. Für Python können Sie verwenden NLTK.

  • Beachten Sie, dass es keine “LSA-Ähnlichkeit” gibt. LSA ist eine Methode, um die Dimensionalität eines Vektorraums zu reduzieren (entweder um die Dinge zu beschleunigen oder um eher Themen als Begriffe zu modellieren). Dieselben Ähnlichkeitsmetriken, die mit BOW und tf-idf verwendet werden, können mit LSA verwendet werden (Kosinusähnlichkeit, euklidische Ähnlichkeit, BM25, …).

    – Witiko

    29. August 2017 um 15:58 Uhr

Benutzer-Avatar
Ben

Hier ist eine kleine App für den Einstieg…

import difflib as dl

a = file('file').read()
b = file('file1').read()

sim = dl.get_close_matches

s = 0
wa = a.split()
wb = b.split()

for i in wa:
    if sim(i, wb):
        s += 1

n = float(s) / float(len(wa))
print '%d%% similarity' % int(n * 100)

  • Beachten Sie, dass es keine “LSA-Ähnlichkeit” gibt. LSA ist eine Methode, um die Dimensionalität eines Vektorraums zu reduzieren (entweder um die Dinge zu beschleunigen oder um eher Themen als Begriffe zu modellieren). Dieselben Ähnlichkeitsmetriken, die mit BOW und tf-idf verwendet werden, können mit LSA verwendet werden (Kosinusähnlichkeit, euklidische Ähnlichkeit, BM25, …).

    – Witiko

    29. August 2017 um 15:58 Uhr

Benutzer-Avatar
Shaurya Uppal

Für syntaktische Ähnlichkeit Es gibt drei einfache Möglichkeiten, Ähnlichkeiten zu erkennen.

  • Word2Vec
  • Handschuh
  • Tfidf oder countvectorizer

Für semantische Ähnlichkeit kann man BERT-Einbettung verwenden und verschiedene Word-Pooling-Strategien ausprobieren, um eine Dokumenteinbettung zu erhalten, und dann Kosinus-Ähnlichkeit auf die Dokumenteinbettung anwenden.

Eine fortgeschrittene Methodik kann BERT SCORE verwenden, um Ähnlichkeit zu erhalten.
BERT-ERGEBNIS

Link zum Forschungsbericht: https://arxiv.org/abs/1904.09675

1065290cookie-checkWie berechnet man die Ähnlichkeit zwischen zwei Textdokumenten?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy