Śledziłem samouczek, który był dostępny w części 1 i części 2 . Niestety autor nie miał czasu na ostatnią sekcję, która polegała na wykorzystaniu podobieństwa cosinusowego, aby faktycznie znaleźć odległość między dwoma dokumentami. Podążałem za przykładami w artykule za pomocą poniższego linku ze stackoverflow , w zestawie jest kod wymieniony w powyższym linku (tylko po to, aby ułatwić życie)
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()
w wyniku powyższego kodu mam następującą macierz
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]]
Nie jestem pewien, jak wykorzystać to wyjście, aby obliczyć podobieństwo cosinusowe, wiem, jak zaimplementować podobieństwo cosinusowe w odniesieniu do dwóch wektorów o podobnej długości, ale tutaj nie jestem pewien, jak zidentyfikować te dwa wektory.
python
machine-learning
nltk
information-retrieval
tf-idf
dodaj średniki
źródło
źródło
Odpowiedzi:
Po pierwsze, jeśli chcesz wyodrębnić funkcje zliczania i zastosować normalizację TF-IDF i normalizację euklidesową w wierszach, możesz to zrobić w jednej operacji za pomocą
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>
Teraz, aby znaleźć odległości cosinusowe jednego dokumentu (np. Pierwszego w zbiorze danych) i wszystkich pozostałych, wystarczy obliczyć iloczyn skalarny pierwszego wektora z wszystkimi pozostałymi, ponieważ wektory tfidf są już znormalizowane wierszowo.
Jak wyjaśnił Chris Clark w komentarzach i tutaj Cosinus Podobieństwo nie bierze pod uwagę wielkości wektorów. Wartość znormalizowana wierszami ma wielkość 1, więc liniowe jądro jest wystarczające do obliczenia wartości podobieństwa.
Scipy Sparse Matrix API jest trochę dziwne (nie tak elastyczne jak gęste N-wymiarowe tablice numpy). Aby otrzymać pierwszy wektor, musisz przeciąć macierz wierszami, aby uzyskać podmacierz z jednym wierszem:
>>> tfidf[0:1] <1x130088 sparse matrix of type '<type 'numpy.float64'>' with 89 stored elements in Compressed Sparse Row format>
scikit-learn już zapewnia metryki parami (zwane także jądrem w języku uczenia maszynowego), które działają zarówno dla gęstych, jak i rzadkich reprezentacji kolekcji wektorów. W tym przypadku potrzebujemy iloczynu skalarnego, znanego również jako jądro liniowe:
>>> 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])
W związku z tym, aby znaleźć 5 najważniejszych powiązanych dokumentów, możemy użyć
argsort
i niektórych ujemnych wycinków tablicowych (większość powiązanych dokumentów ma najwyższe wartości podobieństwa cosinusowego, stąd na końcu tablicy posortowanych indeksów):>>> 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 ])
Pierwszym wynikiem jest sprawdzenie poprawności: dokument zapytania jest najbardziej podobny z wynikiem podobieństwa cosinusowego równym 1 i ma następujący tekst:
>>> 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 ----
Drugim najbardziej podobnym dokumentem jest odpowiedź, która cytuje oryginalną wiadomość, dlatego zawiera wiele popularnych słów:
>>> 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 <1993Apr20.174246.14375@wam.umd.edu> [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
źródło
cosine_similarities = linear_kernel(tfidf, tfidf)
?Dzięki pomocy komentarza @ excray udaje mi się znaleźć odpowiedź: To, co musimy zrobić, to napisać prostą pętlę for, aby iterować po dwóch tablicach, które reprezentują dane pociągu i dane testowe.
Najpierw zaimplementuj prostą funkcję lambda, aby zachować wzór na obliczenie cosinusa:
cosine_function = lambda a, b : round(np.inner(a, b)/(LA.norm(a)*LA.norm(b)), 3)
A potem po prostu napisz prostą pętlę for, aby wykonać iterację po wektorze to, logika jest dla każdego „Dla każdego wektora w trainVectorizerArray musisz znaleźć podobieństwo cosinusowe z wektorem w testVectorizerArray”.
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()
Oto wynik:
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]]
źródło
transformer.fit
operacji itfidf.todense()
? Otrzymałeś wartości podobieństwa z pętli, a następnie kontynuujesz wykonywanie tfidf? Gdzie jest używana obliczona wartość cosinusa? Twój przykład jest zagmatwany.0.408
i0.816
jakie są te wartości?Wiem, że to stary post. ale wypróbowałem pakiet http://scikit-learn.sourceforge.net/stable/ . oto mój kod, aby znaleźć podobieństwo cosinusowe. Pytanie brzmiało, jak obliczyć podobieństwo cosinusowe z tym pakietem, a oto mój kod do tego
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
Załóżmy, że zapytanie jest pierwszym elementem train_set, a doc1, doc2 i doc3 to dokumenty, które chcę uszeregować za pomocą podobieństwa cosinusowego. wtedy mogę użyć tego kodu.
Również tutoriale zawarte w pytaniu były bardzo przydatne. Oto wszystkie części do tego część I , część II , część III
wynik będzie następujący:
[[ 1. 0.07102631 0.02731343 0.06348799]]
tutaj 1 oznacza, że zapytanie jest dopasowane do siebie, a pozostałe trzy to wyniki za dopasowanie zapytania do odpowiednich dokumentów.
źródło
ValueError: Incompatible dimension for X and Y matrices: X.shape[1] == 1664 while Y.shape[1] == 2
Pozwólcie, że przedstawię kolejny tutorial napisany przeze mnie. Odpowiada na twoje pytanie, ale także wyjaśnia, dlaczego robimy niektóre rzeczy. Starałem się też, żeby był zwięzły.
Więc masz jedną,
list_of_documents
która jest po prostu tablicą ciągów, a drugą,document
która jest po prostu łańcuchem. Musisz znaleźć taki dokument zlist_of_documents
najbardziej podobnego dodocument
.Połączmy je razem:
documents = list_of_documents + [document]
Zacznijmy od zależności. Stanie się jasne, dlaczego używamy każdego z nich.
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
Jednym z podejść, które można zastosować, jest worek słów , w którym każde słowo w dokumencie traktujemy niezależnie od innych i po prostu wrzucamy je wszystkie razem do dużej torby. Z jednego punktu widzenia traci wiele informacji (np. Sposób łączenia słów), ale z innego punktu widzenia upraszcza model.
W języku angielskim i każdym innym języku ludzkim istnieje wiele „bezużytecznych” słów, takich jak „a”, „the”, „in”, które są tak powszechne, że nie mają zbyt dużego znaczenia. Nazywa się je słowami stop i dobrze jest je usunąć. Inną rzeczą, którą można zauważyć, jest to, że słowa takie jak „analizować”, „analizator”, „analiza” są bardzo podobne. Mają wspólny rdzeń i wszystkie można zamienić na jedno słowo. Proces ten nazywany jest wynikająca i istnieją różne stemmery które różnią się prędkością, agresywność i tak dalej. Dlatego przekształcamy każdy z dokumentów w listę rdzeni słów bez słów stopujących. Odrzucamy również całą interpunkcję.
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]
Jak więc pomoże nam ten worek słów? Wyobraźmy sobie, mamy 3 torby:
[a, b, c]
,[a, c, a]
i[b, c, d]
. Możemy je przekonwertować na wektory w bazie[a, b, c, d]
. Więc skończyć z wektorami:[1, 1, 1, 0]
,[2, 0, 1, 0]
i[0, 1, 1, 1]
. Podobnie jest z naszymi dokumentami (tylko wektory będą znacznie dłuższe). Teraz widzimy, że usunęliśmy wiele słów i wyprowadziliśmy inne, aby zmniejszyć wymiary wektorów. Tutaj jest tylko ciekawa obserwacja. Dłuższe dokumenty będą miały znacznie więcej pozytywnych elementów niż krótsze, dlatego warto znormalizować wektor. Nazywa się to terminem częstotliwości TF, ludzie używali również dodatkowych informacji o tym, jak często słowo jest używane w innych dokumentach - odwrotna częstotliwość dokumentu IDF. Razem mamy metrykę TF-IDF, który ma kilka smaków. Można to osiągnąć za pomocą jednej linii w 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)
W rzeczywistości wektoryzator pozwala na wiele rzeczy, takich jak usuwanie słów ignorowanych i małe litery. Zrobiłem to w osobnym kroku tylko dlatego, że sklearn nie ma nieangielskich słów kluczowych, ale nltk ma.
Więc mamy obliczone wszystkie wektory. Ostatnim krokiem jest znalezienie najbardziej podobnego do ostatniego. Można to osiągnąć na wiele sposobów, jednym z nich jest odległość euklidesowa, która nie jest tak duża z omówionego tutaj powodu . Innym podejściem jest podobieństwo cosinusowe . Iterujemy wszystkie dokumenty i obliczamy podobieństwo cosinusowe między dokumentem a ostatnim:
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
Teraz minimum będzie zawierało informacje o najlepszym dokumencie i jego punktacji.
źródło
To powinno ci pomóc.
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
a wynik będzie:
[[ 0.34949812 0.81649658 1. ]]
źródło
Oto funkcja, która porównuje dane testowe z danymi szkoleniowymi, z transformatorem Tf-Idf wyposażonym w dane uczące. Zaletą jest to, że możesz szybko przestawiać lub grupować według, aby znaleźć n najbliższych elementów, a obliczenia są w dół macierzy.
def create_tokenizer_score(new_series, train_series, tokenizer): """ return the tf idf score of each possible pairs of documents Args: new_series (pd.Series): new data (To compare against train data) train_series (pd.Series): train data (To fit the tf-idf transformer) Returns: pd.DataFrame """ train_tfidf = tokenizer.fit_transform(train_series) new_tfidf = tokenizer.transform(new_series) X = pd.DataFrame(cosine_similarity(new_tfidf, train_tfidf), columns=train_series.index) X['ix_new'] = new_series.index score = pd.melt( X, id_vars='ix_new', var_name='ix_train', value_name='score' ) return score train_set = pd.Series(["The sky is blue.", "The sun is bright."]) test_set = pd.Series(["The sun in the sky is bright."]) tokenizer = TfidfVectorizer() # initiate here your own tokenizer (TfidfVectorizer, CountVectorizer, with stopwords...) score = create_tokenizer_score(train_series=train_set, new_series=test_set, tokenizer=tokenizer) score ix_new ix_train score 0 0 0 0.617034 1 0 1 0.862012
źródło