2012-08-25 8 views
60

Ich folgte einem Tutorial, das bei Part 1 & Part 2 verfügbar war Leider hatte Autor keine Zeit für den letzten Abschnitt, der Kosinus verwendet, um tatsächlich die Ähnlichkeit zwischen zwei Dokumenten zu finden . Ich folgte den Beispielen in dem Artikel mit Hilfe der folgenden Link von stackoverflow Ich habe den Code, der in der obigen Link erwähnt, nur um Antworten auf das Leben leicht gemacht.Python: tf-idf-cosine: um Dokumentähnlichkeit zu finden

from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.feature_extraction.text import TfidfTransformer 
from nltk.corpus import stopwords 
import numpy as np 
import numpy.linalg as LA 

train_set = ["The sky is blue.", "The sun is bright."] #Documents 
test_set = ["The sun in the sky is bright."] #Query 
stopWords = stopwords.words('english') 

vectorizer = CountVectorizer(stop_words = stopWords) 
#print vectorizer 
transformer = TfidfTransformer() 
#print transformer 

trainVectorizerArray = vectorizer.fit_transform(train_set).toarray() 
testVectorizerArray = vectorizer.transform(test_set).toarray() 
print 'Fit Vectorizer to train set', trainVectorizerArray 
print 'Transform Vectorizer to test set', testVectorizerArray 

transformer.fit(trainVectorizerArray) 
print 
print transformer.transform(trainVectorizerArray).toarray() 

transformer.fit(testVectorizerArray) 
print 
tfidf = transformer.transform(testVectorizerArray) 
print tfidf.todense() 

als Folge der oben genannten Code I

Matrix verfolgt haben
Fit Vectorizer to train set [[1 0 1 0] 
[0 1 0 1]] 
Transform Vectorizer to test set [[0 1 1 1]] 

[[ 0.70710678 0.   0.70710678 0.  ] 
[ 0.   0.70710678 0.   0.70710678]] 

[[ 0.   0.57735027 0.57735027 0.57735027]] 

Ich bin nicht sicher, wie diese Ausgabe verwenden Kosinusähnlichkeit zu berechnen, ich weiß, wie man mit zwei Vektoren Kosinusähnlichkeit Bezug zu implementieren ähnliche Länge, aber hier bin ich mir nicht sicher, wie man die zwei Vektoren identifiziert.

+3

Für jeden Vektor in trainVectorizerArray müssen Sie die Kosinusähnlichkeit mit dem Vektor in testVectorizerArray finden. – excray

+0

@excray Danke, mit deinem hilfreichen Punkt schaffe ich es, es herauszufinden, sollte ich die Antwort setzen? –

+0

@excray Aber ich habe kleine Frage, actuall tf * IDF Berechnung hat keinen Nutzen für diese, weil ich nicht die Endergebnisse verwenden, die in der Matrix angezeigt wird. –

Antwort

13

Mit Hilfe von @ excray's Kommentar, es gelingt mir, herauszufinden, die Antwort, Was wir tun müssen, ist eigentlich schreiben eine einfache for-Schleife über die zwei Arrays, die die Zugdaten und Testdaten darstellen.

Zunächst implementieren eine einfache Lambda-Funktion Formel für die Cosinus Berechnung zu halten:

cosine_function = lambda a, b : round(np.inner(a, b)/(LA.norm(a)*LA.norm(b)), 3) 

Und dann schreiben Sie einfach eine einfache for-Schleife über die zu Vektor laufen, ist die Logik für jeden „Für jeden Vektor in trainVectorizerArray , Sie müssen die Kosinusähnlichkeit mit dem Vektor in testVectorizerArray finden. " Hier

from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.feature_extraction.text import TfidfTransformer 
from nltk.corpus import stopwords 
import numpy as np 
import numpy.linalg as LA 

train_set = ["The sky is blue.", "The sun is bright."] #Documents 
test_set = ["The sun in the sky is bright."] #Query 
stopWords = stopwords.words('english') 

vectorizer = CountVectorizer(stop_words = stopWords) 
#print vectorizer 
transformer = TfidfTransformer() 
#print transformer 

trainVectorizerArray = vectorizer.fit_transform(train_set).toarray() 
testVectorizerArray = vectorizer.transform(test_set).toarray() 
print 'Fit Vectorizer to train set', trainVectorizerArray 
print 'Transform Vectorizer to test set', testVectorizerArray 
cx = lambda a, b : round(np.inner(a, b)/(LA.norm(a)*LA.norm(b)), 3) 

for vector in trainVectorizerArray: 
    print vector 
    for testV in testVectorizerArray: 
     print testV 
     cosine = cx(vector, testV) 
     print cosine 

transformer.fit(trainVectorizerArray) 
print 
print transformer.transform(trainVectorizerArray).toarray() 

transformer.fit(testVectorizerArray) 
print 
tfidf = transformer.transform(testVectorizerArray) 
print tfidf.todense() 

ist die Ausgabe:

Fit Vectorizer to train set [[1 0 1 0] 
[0 1 0 1]] 
Transform Vectorizer to test set [[0 1 1 1]] 
[1 0 1 0] 
[0 1 1 1] 
0.408 
[0 1 0 1] 
[0 1 1 1] 
0.816 

[[ 0.70710678 0.   0.70710678 0.  ] 
[ 0.   0.70710678 0.   0.70710678]] 

[[ 0.   0.57735027 0.57735027 0.57735027]] 
+1

nice..Ich lerne auch von Anfang an und Ihre Frage und Antwort sind am einfachsten zu folgen. Ich denke, dass Sie np.corrcoef() anstelle Ihrer eigenen Methode verwenden können. – wbg

+1

dann wieder, erlaubt Ihre Methode für jede Norm ... was ist cool ... – wbg

+1

@spicramen um es bis zu 3 Dezimalstellen zu runden –

113

Zunächst einmal, wenn Sie wollen Zählung Merkmale extrahieren und TF-IDF Normalisierung anwenden und zeilenweise euklidische Normierung Sie können es in einem Arbeitsgang mit TfidfVectorizer :

>>> from sklearn.feature_extraction.text import TfidfVectorizer 
>>> from sklearn.datasets import fetch_20newsgroups 
>>> twenty = fetch_20newsgroups() 

>>> tfidf = TfidfVectorizer().fit_transform(twenty.data) 
>>> tfidf 
<11314x130088 sparse matrix of type '<type 'numpy.float64'>' 
    with 1787553 stored elements in Compressed Sparse Row format> 

nun die Cosinus-Abstände eines Dokuments (zB die erste im Datensatz) und alle anderen müssen Sie nur zu finden, um die Punktprodukte des f berechnen erster Vektor mit allen anderen, da die tfidf-Vektoren bereits reihennormalisiert sind. Die Scipy-Sparse-Matrix-API ist ein bisschen seltsam (nicht so flexibel wie dichte N-dimensionale numpige Arrays). Um den ersten Vektor zu erhalten müssen Sie die Matrix zeilenweise schneiden eine Submatrix mit einer einzigen Reihe zu bekommen:

>>> tfidf[0:1] 
<1x130088 sparse matrix of type '<type 'numpy.float64'>' 
    with 89 stored elements in Compressed Sparse Row format> 

Scikit-Learn bietet bereits paarweise Metriken (auch bekannt als Kern in maschinellem Lernen parlance), dass die Arbeit für beide dicht und spärliche Darstellungen von Vektorsammlungen.In diesem Fall haben wir einen Punkt Produkt benötigen, das auch als lineare Kernel bekannt ist:

>>> from sklearn.metrics.pairwise import linear_kernel 
>>> cosine_similarities = linear_kernel(tfidf[0:1], tfidf).flatten() 
>>> cosine_similarities 
array([ 1.  , 0.04405952, 0.11016969, ..., 0.04433602, 
    0.04457106, 0.03293218]) 

daher die Top-5-Dokumente zu finden, die wir argsort und einige negative Array Slicing (die meisten verwandten Dokumenten haben höchste Kosinusähnlichkeit verwenden können Werte, also am Ende des Arrays sortierte Indizes):

>>> related_docs_indices = cosine_similarities.argsort()[:-5:-1] 
>>> related_docs_indices 
array([ 0, 958, 10576, 3277]) 
>>> cosine_similarities[related_docs_indices] 
array([ 1.  , 0.54967926, 0.32902194, 0.2825788 ]) 

Das erste Ergebnis ist eine Plausibilitätsprüfung: wir können das Abfragedokument als ähnlichste Dokument mit einem Kosinus-Ähnlichkeits-Score von 1 zu finden, die den folgenden Text hat :

>>> print twenty.data[0] 
From: [email protected] (where's my thing) 
Subject: WHAT car is this!? 
Nntp-Posting-Host: rac3.wam.umd.edu 
Organization: University of Maryland, College Park 
Lines: 15 

I was wondering if anyone out there could enlighten me on this car I saw 
the other day. It was a 2-door sports car, looked to be from the late 60s/ 
early 70s. It was called a Bricklin. The doors were really small. In addition, 
the front bumper was separate from the rest of the body. This is 
all I know. If anyone can tellme a model name, engine specs, years 
of production, where this car is made, history, or whatever info you 
have on this funky looking car, please e-mail. 

Thanks, 
- IL 
    ---- brought to you by your neighborhood Lerxst ---- 

Die zweite am ähnlichsten Dokument ist eine Antwort, die die ursprüngliche Nachricht hat viele gemeinsame Wörter damit zitiert:

>>> print twenty.data[958] 
From: [email protected] (Robert Seymour) 
Subject: Re: WHAT car is this!? 
Article-I.D.: reed.1993Apr21.032905.29286 
Reply-To: [email protected] 
Organization: Reed College, Portland, OR 
Lines: 26 

In article <[email protected]> [email protected] (where's my 
thing) writes: 
> 
> I was wondering if anyone out there could enlighten me on this car I saw 
> the other day. It was a 2-door sports car, looked to be from the late 60s/ 
> early 70s. It was called a Bricklin. The doors were really small. In 
addition, 
> the front bumper was separate from the rest of the body. This is 
> all I know. If anyone can tellme a model name, engine specs, years 
> of production, where this car is made, history, or whatever info you 
> have on this funky looking car, please e-mail. 

Bricklins were manufactured in the 70s with engines from Ford. They are rather 
odd looking with the encased front bumper. There aren't a lot of them around, 
but Hemmings (Motor News) ususally has ten or so listed. Basically, they are a 
performance Ford with new styling slapped on top. 

> ---- brought to you by your neighborhood Lerxst ---- 

Rush fan? 

-- 
Robert Seymour    [email protected] 
Physics and Philosophy, Reed College (NeXTmail accepted) 
Artificial Life Project   Reed College 
Reed Solar Energy Project (SolTrain) Portland, OR 
+3

ausgezeichnete Antwort! Danke Olivier! –

+0

Eine Folgefrage: Wenn ich eine sehr große Anzahl von Dokumenten habe, kann die Funktion linear_kernel in Schritt 2 der Leistungsengpass sein, da sie linear zur Anzahl der Zeilen ist. Irgendwelche Gedanken darüber, wie man es auf sublinear reduzieren kann? – Shuo

+0

Sie können die "eher so" -Anfragen von Elastic Search und Solr verwenden, die ungefähre Antworten mit einem sublinearen Skalierbarkeitsprofil liefern sollten. – ogrisel

15

Ich weiß, ihr einen alten Post. aber ich habe das http://scikit-learn.sourceforge.net/stable/ Paket versucht. Hier ist mein Code, um die Kosinusähnlichkeit zu finden. Die Frage war, wie Sie den Kosinus Ähnlichkeit mit diesem Paket berechnen und hier ist mein Code für das hier

from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.metrics.pairwise import cosine_similarity 
from sklearn.feature_extraction.text import TfidfVectorizer 

f = open("/root/Myfolder/scoringDocuments/doc1") 
doc1 = str.decode(f.read(), "UTF-8", "ignore") 
f = open("/root/Myfolder/scoringDocuments/doc2") 
doc2 = str.decode(f.read(), "UTF-8", "ignore") 
f = open("/root/Myfolder/scoringDocuments/doc3") 
doc3 = str.decode(f.read(), "UTF-8", "ignore") 

train_set = ["president of India",doc1, doc2, doc3] 

tfidf_vectorizer = TfidfVectorizer() 
tfidf_matrix_train = tfidf_vectorizer.fit_transform(train_set) #finds the tfidf score with normalization 
print "cosine scores ==> ",cosine_similarity(tfidf_matrix_train[0:1], tfidf_matrix_train) #here the first element of tfidf_matrix_train is matched with other three elements 

nehme an, die Abfrage ist das erste Element der train_set und doc1, doc2 und doc3 sind die Dokumente, die ich will Rang mit Hilfe der Kosinusähnlichkeit. dann kann ich diesen Code verwenden.

Auch die Tutorials in der Frage waren sehr hilfreich. Hier sind alle Teile für sie part-I, part-II, part-III

die Ausgabe wie folgt aus:

[[ 1.   0.07102631 0.02731343 0.06348799]] 

hier 1 für die Abfrage mit sich selbst abgestimmt ist und die anderen drei sind die Werte für die Anpassung der Abfrage mit den entsprechenden Dokumenten.

+1

cosine_similarity (tfidf_matrix_train [0: 1], tfidf_matrix_train) Was passiert, wenn 1 auf mehr als tausende geändert wird? Wie können wir damit umgehen? – ashim888

8

Dies sollte Ihnen helfen.

from sklearn.feature_extraction.text import TfidfVectorizer 
from sklearn.metrics.pairwise import cosine_similarity 

tfidf_vectorizer = TfidfVectorizer() 
tfidf_matrix = tfidf_vectorizer.fit_transform(train_set) 
print tfidf_matrix 
cosine = cosine_similarity(tfidf_matrix[length-1], tfidf_matrix) 
print cosine 

und Ausgang wird sein:

[[ 0.34949812 0.81649658 1.  ]] 
+3

Wie erhalten Sie die Länge? – spicyramen

12

mich Ihnen eine andere Tutorial ist von mir geschrieben Let geben. Es beantwortet Ihre Frage, macht aber auch eine Erklärung, warum wir einige Dinge tun. Ich habe auch versucht, es kurz zu machen.

So haben Sie eine list_of_documents, die nur ein Array von Strings und anderen document ist, die nur eine Zeichenfolge ist. Sie müssen ein solches Dokument aus dem list_of_documents finden, das dem document am ähnlichsten ist.

wir sie miteinander zu verknüpfen: documents = list_of_documents + [document]

Lasst uns mit Abhängigkeiten starten. Es wird klar werden, warum wir jeden von ihnen verwenden.

from nltk.corpus import stopwords 
import string 
from nltk.tokenize import wordpunct_tokenize as tokenize 
from nltk.stem.porter import PorterStemmer 
from sklearn.feature_extraction.text import TfidfVectorizer 
from scipy.spatial.distance import cosine 

Einer der Ansätze, die Anwendungen sein kann, ist ein bag-of-words Ansatz, bei dem wir jedes Wort in dem Dokument unabhängig von anderen zu behandeln und nur alle zusammen in der großen Tasche werfen.Von einem Gesichtspunkt verliert es viele Informationen (wie die Wörter verbunden sind), aber unter einem anderen Gesichtspunkt macht es das Modell einfach.

In Englisch und in jeder anderen menschlichen Sprache gibt es viele "nutzlose" Wörter wie "a", "the", "in", die so häufig sind, dass sie nicht viel Bedeutung besitzen. Sie heißen stop words und es ist eine gute Idee, sie zu entfernen. Eine andere Sache, die man bemerken kann, ist, dass Wörter wie "analysieren", "Analysator", "Analyse" wirklich ähnlich sind. Sie haben eine gemeinsame Wurzel und alle können in nur ein Wort umgewandelt werden. Dieser Prozess wird stemming genannt und es existieren verschiedene Stemmer, die sich in Geschwindigkeit, Aggressivität usw. unterscheiden. Also transformieren wir jedes der Dokumente in eine Liste von Wortstämmen ohne Stoppwörter. Außerdem verwerfen wir alle Interpunktionszeichen.

porter = PorterStemmer() 
stop_words = set(stopwords.words('english')) 

modified_arr = [[porter.stem(i.lower()) for i in tokenize(d.translate(None, string.punctuation)) if i.lower() not in stop_words] for d in documents] 

Also, wie hilft uns diese Tüte Worte? Stellen Sie sich vor, wir haben 3 Taschen: [a, b, c], [a, c, a] und [b, c, d]. Wir können sie in vectors in the basis[a, b, c, d] konvertieren. Also enden wir mit Vektoren: [1, 1, 1, 0], [2, 0, 1, 0] und [0, 1, 1, 1]. Ähnliches gilt für unsere Dokumente (nur die Vektoren sind viel zu lang). Jetzt sehen wir, dass wir viele Wörter entfernt haben und andere auch dazu gebracht haben, die Dimensionen der Vektoren zu verringern. Hier gibt es nur interessante Beobachtungen. Längere Dokumente werden viel positivere Elemente haben als kürzere, deshalb ist es schön, den Vektor zu normalisieren. Dies wird Begriff Häufigkeit TF genannt, Menschen verwendet auch zusätzliche Informationen darüber, wie oft das Wort in anderen Dokumenten verwendet wird - inverse Dokument Häufigkeit IDF. Zusammen haben wir eine Metrik TF-IDF which have a couple of flavors. Dies kann mit einer Zeile in sklearn :-)

modified_doc = [' '.join(i) for i in modified_arr] # this is only to convert our list of lists to list of strings that vectorizer uses. 
tf_idf = TfidfVectorizer().fit_transform(modified_doc) 

Eigentlich vectorizer allows to do a lot of things wie Entfernen von Stoppwörtern und Kleinschreibung erreicht werden. Ich habe sie in einem separaten Schritt gemacht, nur weil sklearn nicht englische Stoppwörter hat, aber nltk hat.

Also haben wir alle Vektoren berechnet. Der letzte Schritt besteht darin, herauszufinden, welcher dem letzten am ähnlichsten ist. Es gibt verschiedene Möglichkeiten, um das zu erreichen, eine davon ist die euklidische Distanz, die nicht so groß ist, aus dem Grund discussed here. Ein anderer Ansatz ist cosine similarity. Wir durchlaufen alle Dokumente und Berechnung Cosinus Ähnlichkeit zwischen dem Dokument und die letzte:

l = len(documents) - 1 
for i in xrange(l): 
    minimum = (1, None) 
    minimum = min((cosine(tf_idf[i].todense(), tf_idf[l + 1].todense()), i), minimum) 
print minimum 

Jetzt minimale Informationen über die beste Dokument und seine Punktzahl haben.

+2

Unterschreiben, das ist nicht das, wonach op gefragt wurde: die Suche nach dem besten Dokument ergab keine Anfrage "das beste Dokument" in einem Korpus. Bitte tu es nicht, Leute wie ich werden Zeit verschwenden, wenn du versuchst, dein Beispiel für die OP-Aufgabe zu verwenden und in Matrix-Grßenwahnsinn gezogen zu werden. – minerals

+0

Und wie ist es anders? Die Idee ist völlig gleich. Features extrahieren, Kosinusabstand zwischen einer Abfrage und Dokumenten berechnen. –

+0

Sie berechnen dies auf Matrizen gleicher Formen, versuchen Sie ein anderes Beispiel, wo Sie eine Abfrage-Matrix haben, die von unterschiedlicher Größe ist, op Zugverband und Test-Set. Ich konnte Ihren Code nicht ändern, damit er funktioniert. – minerals