在作天然语言处理的过程当中,咱们常常会遇到须要找出类似语句的场景,或者找出句子的近似表达,这时候咱们就须要把相似的句子归到一块儿,这里面就涉及到句子类似度计算的问题,那么本节就来了解一下怎么样来用 Python 实现句子类似度的计算。
html
句子类似度计算咱们一共归类了如下几种方法:git
编辑距离计算github
杰卡德系数计算算法
TF 计算数据库
TFIDF 计算数组
Word2Vec 计算bash
下面咱们来一一了解一下这几种算法的原理和 Python 实现。微信
编辑距离,英文叫作 Edit Distance,又称 Levenshtein 距离,是指两个字串之间,由一个转成另外一个所需的最少编辑操做次数,若是它们的距离越大,说明它们越是不一样。许可的编辑操做包括将一个字符替换成另外一个字符,插入一个字符,删除一个字符。
网络
例如咱们有两个字符串:string 和 setting,若是咱们想要把 string 转化为 setting,须要这么两步:框架
第一步,在 s 和 t 之间加入字符 e。
第二步,把 r 替换成 t。
因此它们的编辑距离差就是 2,这就对应着两者要进行转化所要改变(添加、替换、删除)的最小步数。
那么用 Python 怎样来实现呢,咱们能够直接使用 distance 库:
import distance
def edit_distance(s1, s2):
return distance.levenshtein(s1, s2)
s1 = 'string'
s2 = 'setting'
print(edit_distance(s1, s2))复制代码
这里咱们直接使用 distance 库的 levenshtein() 方法,传入两个字符串,便可获取两个字符串的编辑距离了。
运行结果以下:
2复制代码
这里的 distance 库咱们能够直接使用 pip3 来安装:
pip3 install distance复制代码
这样若是咱们想要获取类似的文本的话能够直接设定一个编辑距离的阈值来实现,如设置编辑距离为 2,下面是一个样例:
import distance
def edit_distance(s1, s2):
return distance.levenshtein(s1, s2)
strings = [
'你在干什么',
'你在干啥子',
'你在作什么',
'你好啊',
'我喜欢吃香蕉'
]
target = '你在干啥'
results = list(filter(lambda x: edit_distance(x, target) <= 2, strings))
print(results)复制代码
这里咱们定义了一些字符串,而后定义了一个目标字符串,而后用编辑距离 2 的阈值进行设定,最后获得的结果就是编辑距离在 2 及之内的结果,运行结果以下:
['你在干什么', '你在干啥子']复制代码
经过这种方式咱们能够大体筛选出相似的句子,可是发现一些句子例如“你在作什么” 就没有被识别出来,但他们的意义确实是相差不大的,所以,编辑距离并非一个好的方式,可是简单易用。
杰卡德系数,英文叫作 Jaccard index, 又称为 Jaccard 类似系数,用于比较有限样本集之间的类似性与差别性。Jaccard 系数值越大,样本类似度越高。
实际上它的计算方式很是简单,就是两个样本的交集除以并集获得的数值,当两个样本彻底一致时,结果为 1,当两个样本彻底不一样时,结果为 0。
算法很是简单,就是交集除以并集,下面咱们用 Python 代码来实现一下:
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
def jaccard_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 将字中间加入空格
s1, s2 = add_space(s1), add_space(s2)
# 转化为TF矩阵
cv = CountVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 求交集
numerator = np.sum(np.min(vectors, axis=0))
# 求并集
denominator = np.sum(np.max(vectors, axis=0))
# 计算杰卡德系数
return 1.0 * numerator / denominator
s1 = '你在干吗呢'
s2 = '你在干什么呢'
print(jaccard_similarity(s1, s2))复制代码
这里咱们使用了 Sklearn 库中的 CountVectorizer 来计算句子的 TF 矩阵,而后利用 Numpy 来计算两者的交集和并集,随后计算杰卡德系数。
这里值得学习的有 CountVectorizer 的用法,经过它的 fit_transform() 方法咱们能够将字符串转化为词频矩阵,例如这里有两句话“你在干吗呢”和“你在干什么呢”,首先 CountVectorizer 会计算出不重复的有哪些字,会获得一个字的列表,结果为:
['么', '什', '你', '呢', '嘛', '在', '干']复制代码
这个其实能够经过以下代码来获取,就是获取词表内容:
cv.get_feature_names()复制代码
接下来经过转化以后,vectors 变量就变成了:
[[0 0 1 1 1 1 1]
[1 1 1 1 0 1 1]]复制代码
它对应的是两个句子对应词表的词频统计,这里是两个句子,因此结果是一个长度为 2 的二维数组,好比第一句话“你在干吗呢”中不包含“么”字,那么第一个“么”字对应的结果就是0,即数量为 0,依次类推。
后面咱们使用了 np.min() 方法并传入了 axis 为 0,实际上就是获取了每一列的最小值,这样实际上就是取了交集,np.max() 方法是获取了每一列的最大值,实际上就是取了并集。
两者分别取和便是交集大小和并集大小,而后做商便可,结果以下:
0.5714285714285714复制代码
这个数值越大,表明两个字符串越接近,不然反之,所以咱们也可使用这个方法,并经过设置一个类似度阈值来进行筛选。
第三种方案就是直接计算 TF 矩阵中两个向量的类似度了,实际上就是求解两个向量夹角的余弦值,就是点乘积除以两者的模长,公式以下:
cosθ=a·b/|a|*|b|复制代码
上面咱们已经得到了 TF 矩阵,下面咱们只须要求解两个向量夹角的余弦值就行了,代码以下:
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
from scipy.linalg import norm
def tf_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 将字中间加入空格
s1, s2 = add_space(s1), add_space(s2)
# 转化为TF矩阵
cv = CountVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 计算TF系数
return np.dot(vectors[0], vectors[1]) / (norm(vectors[0]) * norm(vectors[1]))
s1 = '你在干吗呢'
s2 = '你在干什么呢'
print(tf_similarity(s1, s2))复制代码
在在这里咱们使用了 np.dot() 方法获取了向量的点乘积,而后经过 norm() 方法获取了向量的模长,通过计算获得两者的 TF 系数,结果以下:
0.7302967433402214复制代码
另外除了计算 TF 系数咱们还能够计算 TFIDF 系数,TFIDF 实际上就是在词频 TF 的基础上再加入 IDF 的信息,IDF 称为逆文档频率,不了解的能够看下阮一峰老师的讲解:http://www.ruanyifeng.com/blog/2013/03/tf-idf.html,里面对 TFIDF 的讲解也是十分透彻的。
下面咱们仍是借助于 Sklearn 中的模块 TfidfVectorizer 来实现,代码以下:
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
from scipy.linalg import norm
def tfidf_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 将字中间加入空格
s1, s2 = add_space(s1), add_space(s2)
# 转化为TF矩阵
cv = TfidfVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 计算TF系数
return np.dot(vectors[0], vectors[1]) / (norm(vectors[0]) * norm(vectors[1]))
s1 = '你在干吗呢'
s2 = '你在干什么呢'
print(tfidf_similarity(s1, s2))复制代码
这里的 vectors 变量实际上就对应着 TFIDF 值,内容以下:
[[0. 0. 0.4090901 0.4090901 0.57496187 0.4090901 0.4090901 ]
[0.49844628 0.49844628 0.35464863 0.35464863 0. 0.35464863 0.35464863]]复制代码
运行结果以下:
0.5803329846765686复制代码
因此经过 TFIDF 系数咱们也能够进行类似度的计算。
Word2Vec,顾名思义,其实就是将每个词转换为向量的过程。若是不了解的话能够参考:https://blog.csdn.net/itplus/article/details/37969519。
这里咱们能够直接下载训练好的 Word2Vec 模型,模型的连接地址为:https://pan.baidu.com/s/1TZ8GII0CEX32ydjsfMc0zw,是使用新闻、百度百科、小说数据来训练的 64 维的 Word2Vec 模型,数据量很大,总体效果还不错,咱们能够直接下载下来使用,这里咱们使用的是 news_12g_baidubaike_20g_novel_90g_embedding_64.bin 数据,而后实现 Sentence2Vec,代码以下:
import gensim
import jieba
import numpy as np
from scipy.linalg import norm
model_file = './word2vec/news_12g_baidubaike_20g_novel_90g_embedding_64.bin'
model = gensim.models.KeyedVectors.load_word2vec_format(model_file, binary=True)
def vector_similarity(s1, s2):
def sentence_vector(s):
words = jieba.lcut(s)
v = np.zeros(64)
for word in words:
v += model[word]
v /= len(words)
return v
v1, v2 = sentence_vector(s1), sentence_vector(s2)
return np.dot(v1, v2) / (norm(v1) * norm(v2))复制代码
在获取 Sentence Vector 的时候,咱们首先对句子进行分词,而后对分好的每个词获取其对应的 Vector,而后将全部 Vector 相加并求平均,这样就可获得 Sentence Vector 了,而后再计算其夹角余弦值便可。
调用示例以下:
s1 = '你在干吗'
s2 = '你正作什么'
vector_similarity(s1, s2)复制代码
结果以下:
0.6701133967824016复制代码
这时若是咱们再回到最初的例子看下效果:
strings = [
'你在干什么',
'你在干啥子',
'你在作什么',
'你好啊',
'我喜欢吃香蕉'
]
target = '你在干啥'
for string in strings:
print(string, vector_similarity(string, target))复制代码
依然是前面的例子,咱们看下它们的匹配度结果是多少,运行结果以下:
你在干什么 0.8785495016487204
你在干啥子 0.9789649689827049
你在作什么 0.8781992402695274
你好啊 0.5174225914249863
我喜欢吃香蕉 0.582990841450621复制代码
能够看到相近的语句类似度都能到 0.8 以上,而不一样的句子类似度都不足 0.6,这个区分度就很是大了,能够说有了 Word2Vec 咱们能够结合一些语义信息来进行一些判断,效果明显也好不少。
因此整体来讲,Word2Vec 计算的方式是很是好的。
另外学术界还有一些可能更好的研究成果,这个能够参考知乎上的一些回答:https://www.zhihu.com/question/29978268/answer/54399062。
以上即是进行句子类似度计算的基本方法和 Python 实现,本节代码地址:https://github.com/AIDeepLearning/SentenceDistance。
嗨~ 给你们重磅推荐一本书!上市两月就已经重印 4 次的 Python 爬虫书!它就是由静觅博客博主崔庆才所做的《Python3网络爬虫开发实战》!!!同时文末还有抽奖赠书活动,不容错过!!!
本书《Python3网络爬虫开发实战》全面介绍了利用 Python3 开发网络爬虫的知识,书中首先详细介绍了各类类型的环境配置过程和爬虫基础知识,还讨论了 urllib、requests 等请求库和 Beautiful Soup、XPath、pyquery 等解析库以及文本和各种数据库的存储方法,另外本书经过多个真实新鲜案例介绍了分析 Ajax 进行数据爬取,Selenium 和 Splash 进行动态网站爬取的过程,接着又分享了一些切实可行的爬虫技巧,好比使用代理爬取和维护动态代理池的方法、ADSL 拨号代理的使用、各种验证码(图形、极验、点触、宫格等)的破解方法、模拟登陆网站爬取的方法及 Cookies 池的维护等等。
此外,本书的内容还远远不止这些,做者还结合移动互联网的特色探讨了使用 Charles、mitmdump、Appium 等多种工具实现 App 抓包分析、加密参数接口爬取、微信朋友圈爬取的方法。此外本书还详细介绍了 pyspider 框架、Scrapy 框架的使用和分布式爬虫的知识,另外对于优化及部署工做,本书还包括 Bloom Filter 效率优化、Docker 和 Scrapyd 爬虫部署、分布式爬虫管理框架Gerapy 的分享。
全书共 604 页,足足两斤重呢~ 订价为 99 元!
看书就先看看谁写的嘛,咱们来了解一下~
崔庆才,静觅博客博主(https://cuiqingcai.com),博客 Python 爬虫博文阅读量已过百万,北京航空航天大学硕士,天善智能、网易云课堂讲师,微软小冰大数据工程师,有多个大型分布式爬虫项目经验,乐于技术分享,文章通俗易懂 ^_^
附皂片一张 ~(@^_^@)~
更多详请点击➡️juejin.im/post/5b1eb3…