使用 TFIDF 的余弦相似度
Cosine similarity using TFIDF
SO 和网络上有几个问题描述了如何在两个字符串之间,甚至在两个以 TFIDF 作为权重的字符串之间取 cosine similarity
。但是像 scikit 的 linear_kernel
这样的函数的输出让我有点困惑。
考虑以下代码:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
a = ['hello world', 'my name is', 'what is your name?']
b = ['my name is', 'hello world', 'my name is what?']
df = pd.DataFrame(data={'a':a, 'b':b})
df['ab'] = df.apply(lambda x : x['a'] + ' ' + x['b'], axis=1)
print(df.head())
a b ab
0 hello world my name is hello world my name is
1 my name is hello world my name is hello world
2 what is your name? my name is what? what is your name? my name is what?
问题:
我想要一个列,它是 a
中的字符串与 b
中的字符串之间的余弦相似度。
我试过的:
我在 ab
上训练了一个 TFIDF 分类器,以便包括所有单词:
clf = TfidfVectorizer(ngram_range=(1, 1), stop_words='english')
clf.fit(df['ab'])
然后我得到了 a
和 b
列的稀疏 TFIDF 矩阵:
tfidf_a = clf.transform(df['a'])
tfidf_b = clf.transform(df['b'])
现在,如果我使用 scikit 的 linear_kernel
,这是其他人推荐的,我会得到一个 (nfeatures,nfeatures) 的 Gram 矩阵,如他们的文档中所述。
from sklearn.metrics.pairwise import linear_kernel
linear_kernel(tfidf_a,tfidf_b)
array([[ 0., 1., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
但我需要的是一个简单的向量,其中第一个元素是a
第一行和b
第一行之间的cosin_sim,第二个元素是cos_sim(a[1],b[1]), 等等。
使用 python3,scikit-learn 0.17。
我认为你的例子有点失败,因为你的 TfidfVectorizer 过滤掉了你的大部分单词,因为你有 stop_words = 'english' 参数(你几乎包含了所有停止示例中的单词)。我已经删除了它并使你的矩阵密集,这样我们就可以看到发生了什么。如果你做了这样的事情会怎样?
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from scipy import spatial
a = ['hello world', 'my name is', 'what is your name?']
b = ['my name is', 'hello world', 'my name is what?']
df = pd.DataFrame(data={'a':a, 'b':b})
df['ab'] = df.apply(lambda x : x['a'] + ' ' + x['b'], axis=1)
clf = TfidfVectorizer(ngram_range=(1, 1))
clf.fit(df['ab'])
tfidf_a = clf.transform(df['a']).todense()
tfidf_b = clf.transform(df['b']).todense()
row_similarities = [1 - spatial.distance.cosine(tfidf_a[x],tfidf_b[x]) for x in range(len(tfidf_a)) ]
row_similarities
[0.0, 0.0, 0.72252389079716417]
这显示了每行之间的距离。我并不完全同意你如何构建完整的语料库,但这个例子根本没有优化,所以我暂时不说了。希望这有帮助。
dfs = {}
idfs = {}
speeches = {}
speechvecs = {}
total_word_counts = {}
def tokenize(doc):
tokens = mytokenizer.tokenize(doc)
lowertokens = [token.lower() for token in tokens]
filteredtokens = [stemmer.stem(token) for token in lowertokens if not token in sortedstopwords]
return filteredtokens
def incdfs(tfvec):
for token in set(tfvec):
if token not in dfs:
dfs[token]=1
total_word_counts[token] = tfvec[token]
else:
dfs[token] += 1
total_word_counts[token] += tfvec[token]
def calctfidfvec(tfvec, withidf):
tfidfvec = {}
veclen = 0.0
for token in tfvec:
if withidf:
tfidf = (1+log10(tfvec[token])) * getidf(token)
else:
tfidf = (1+log10(tfvec[token]))
tfidfvec[token] = tfidf
veclen += pow(tfidf,2)
if veclen > 0:
for token in tfvec:
tfidfvec[token] /= sqrt(veclen)
return tfidfvec
def cosinesim(vec1, vec2):
commonterms = set(vec1).intersection(vec2)
sim = 0.0
for token in commonterms:
sim += vec1[token]*vec2[token]
return sim
def query(qstring):
qvec = getqvec(qstring.lower())
scores = {filename:cosinesim(qvec,tfidfvec) for filename, tfidfvec in speechvecs.items()}
return max(scores.items(), key=operator.itemgetter(1))[0]
def docdocsim(filename1,filename2):
return cosinesim(gettfidfvec(filename1),gettfidfvec(filename2))
SO 和网络上有几个问题描述了如何在两个字符串之间,甚至在两个以 TFIDF 作为权重的字符串之间取 cosine similarity
。但是像 scikit 的 linear_kernel
这样的函数的输出让我有点困惑。
考虑以下代码:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
a = ['hello world', 'my name is', 'what is your name?']
b = ['my name is', 'hello world', 'my name is what?']
df = pd.DataFrame(data={'a':a, 'b':b})
df['ab'] = df.apply(lambda x : x['a'] + ' ' + x['b'], axis=1)
print(df.head())
a b ab
0 hello world my name is hello world my name is
1 my name is hello world my name is hello world
2 what is your name? my name is what? what is your name? my name is what?
问题:
我想要一个列,它是 a
中的字符串与 b
中的字符串之间的余弦相似度。
我试过的:
我在 ab
上训练了一个 TFIDF 分类器,以便包括所有单词:
clf = TfidfVectorizer(ngram_range=(1, 1), stop_words='english')
clf.fit(df['ab'])
然后我得到了 a
和 b
列的稀疏 TFIDF 矩阵:
tfidf_a = clf.transform(df['a'])
tfidf_b = clf.transform(df['b'])
现在,如果我使用 scikit 的 linear_kernel
,这是其他人推荐的,我会得到一个 (nfeatures,nfeatures) 的 Gram 矩阵,如他们的文档中所述。
from sklearn.metrics.pairwise import linear_kernel
linear_kernel(tfidf_a,tfidf_b)
array([[ 0., 1., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
但我需要的是一个简单的向量,其中第一个元素是a
第一行和b
第一行之间的cosin_sim,第二个元素是cos_sim(a[1],b[1]), 等等。
使用 python3,scikit-learn 0.17。
我认为你的例子有点失败,因为你的 TfidfVectorizer 过滤掉了你的大部分单词,因为你有 stop_words = 'english' 参数(你几乎包含了所有停止示例中的单词)。我已经删除了它并使你的矩阵密集,这样我们就可以看到发生了什么。如果你做了这样的事情会怎样?
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from scipy import spatial
a = ['hello world', 'my name is', 'what is your name?']
b = ['my name is', 'hello world', 'my name is what?']
df = pd.DataFrame(data={'a':a, 'b':b})
df['ab'] = df.apply(lambda x : x['a'] + ' ' + x['b'], axis=1)
clf = TfidfVectorizer(ngram_range=(1, 1))
clf.fit(df['ab'])
tfidf_a = clf.transform(df['a']).todense()
tfidf_b = clf.transform(df['b']).todense()
row_similarities = [1 - spatial.distance.cosine(tfidf_a[x],tfidf_b[x]) for x in range(len(tfidf_a)) ]
row_similarities
[0.0, 0.0, 0.72252389079716417]
这显示了每行之间的距离。我并不完全同意你如何构建完整的语料库,但这个例子根本没有优化,所以我暂时不说了。希望这有帮助。
dfs = {}
idfs = {}
speeches = {}
speechvecs = {}
total_word_counts = {}
def tokenize(doc):
tokens = mytokenizer.tokenize(doc)
lowertokens = [token.lower() for token in tokens]
filteredtokens = [stemmer.stem(token) for token in lowertokens if not token in sortedstopwords]
return filteredtokens
def incdfs(tfvec):
for token in set(tfvec):
if token not in dfs:
dfs[token]=1
total_word_counts[token] = tfvec[token]
else:
dfs[token] += 1
total_word_counts[token] += tfvec[token]
def calctfidfvec(tfvec, withidf):
tfidfvec = {}
veclen = 0.0
for token in tfvec:
if withidf:
tfidf = (1+log10(tfvec[token])) * getidf(token)
else:
tfidf = (1+log10(tfvec[token]))
tfidfvec[token] = tfidf
veclen += pow(tfidf,2)
if veclen > 0:
for token in tfvec:
tfidfvec[token] /= sqrt(veclen)
return tfidfvec
def cosinesim(vec1, vec2):
commonterms = set(vec1).intersection(vec2)
sim = 0.0
for token in commonterms:
sim += vec1[token]*vec2[token]
return sim
def query(qstring):
qvec = getqvec(qstring.lower())
scores = {filename:cosinesim(qvec,tfidfvec) for filename, tfidfvec in speechvecs.items()}
return max(scores.items(), key=operator.itemgetter(1))[0]
def docdocsim(filename1,filename2):
return cosinesim(gettfidfvec(filename1),gettfidfvec(filename2))