我有两个字符串列表,A和B.对于A中的每个字符串,我想将它与B中的每个字符串进行比较,并选择最相似的匹配。我使用的比较函数是I found on this question的自定义余弦相似性度量。以下是它的工作原理:
import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer
nltk.download('punkt')
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]
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]
我的问题是,如果我有一些长列表(500-1000项),执行开始需要五或十分钟。以下是使用虚拟文本的示例:
import requests
url = 'https://gist.githubusercontent.com/WalkerHarrison/940c005aa23386a69282f373f6160221/raw/6537d999b9e39d62df3784d2d847d4a6b2602876/sample.txt'
sample = requests.get(url).text
A, B = sample[:int(len(sample)/2)], sample[int(len(sample)/2):]
A, B = list(map(''.join, zip(*[iter(A)]*100))), list(map(''.join, zip(*[iter(B)]*100)))
现在我有两个列表,每个列表有~500个字符串(每个字符100个字符),我计算相似之处并取得最重要的一个。这是通过从A中取一个字符串,迭代B,按cosine_sim得分排序,然后取最后一个元素,然后重复A中的所有元素来完成的:
matches = [(a, list(sorted([[b, cosine_sim(a, b)]
for b in B], key=lambda x: x[1]))[-1])
for a in A]
输出是匹配列表,其中每个项目包含两个字符串以及它们的计算相似度分数。最后一行需要7分钟才能运行。我想知道我的流程中效率低下是否会降低速度,或者是否计算得很多(500 * 500 = 250,000比较,加上最佳500次排序)?
答案 0 :(得分:1)
最大的问题可能是你为每一对文件计算tfidf(这里的文件仅仅意味着你的文本单元 - 这可能是一条推文,一个句子,一篇科学论文或一本书)。此外,如果已经存在,您不应该制作自己的相似性度量。最后,sklearn
有一个pairwise_distance
例程,可以执行您想要的操作并进行优化。把这一切放在一起,这是一个示例脚本:
import requests
import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import pairwise_distances
url = 'https://gist.githubusercontent.com/WalkerHarrison/940c005aa23386a69282f373f6160221/raw/6537d999b9e39d62df3784d2d847d4a6b2602876/sample.txt'
sample = requests.get(url).text.split('\n\n') # I'm splitting the document by "paragraphs" since it is unclear what you actually want
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]
def normalize(text):
return stem_tokens(nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))
vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')
doc_vectors = vectorizer.fit_transform(sample)
distances = pairwise_distances(doc_vectors, metric='cosine')
row_idx = list(enumerate(distances.argmax(axis=1)))
sorted_pairs = sorted(row_idx, key= lambda x: distances[x[0], x[1]], reverse=True)
# most similar documents:
i1, i2 = sorted_pairs[0] # index of most similar pair
print(sample[i1])
print("=="*50)
print(sample[i2])
我的sample
列表中有99个文档,这些文件在下载完成后立即运行。另外,输出:
艺术派对动物标本制作3虎月亮占据。手提包twee炸玉米饼 listicle,屠夫单一来源咖啡raclette gentrify生牛仔布 helvetica羽衣甘蓝筹码萨满威廉斯堡男子编织。戳normcore lomo 健康哥特背心kogi。 Af下一级banh mi,深v locavore 不对称的小浪波浪。地铁瓦片病毒灵性pok 素食主义者,开衫健康哥特venmo工匠。冰岛下一级twee adaptogen,dreamcatcher paleo lyft。 Selfies shoreditch microdosing vape,knausgaard热鸡干草叉打字机宝丽来lyft 滑板道德酒厂。农场到桌蓝瓶yr工匠 狼努力的素食主义者古代knausgaard深v salvia ugh内脏 snackwave。多肉植物动物标本剥制口cornhole wayfarers屠户,街道艺术 宝丽来牛仔短裤威廉斯堡la croix tumblr牛仔布。热 鸡健康哥特taiyaki truffaut弹出冰岛肖尔迪奇 fingerstache。
=============================================== ================================================== ===
有机微剂量keytar thundercats青年布,cray raclette。清潭 讽刺raclette chia,cornhole YOLO stumptown。无麸质palo santo 胡子嘉。无论什么bushwick stumptown seitan cred quinoa。小 批量自拍波特兰,羊毛衫,你可能还没有听说过它们 破旧别致的四美元吐司灵性palo santo胡子内脏 米加斯。 Kinfolk pour-over glossier,吊床poutine pinterest着色 书籍媚俗适应的旅行者+1纹身lomo yuccie副。格子花呢 fixie portland,letterpress knausgaard sartorial live-edge。奥斯汀 adaptogen YOLO云面包旅行者陈词滥调吊床班卓琴。可持续发展 有机空气植物胡子。