?本案例通過設(shè)計(jì)和實(shí)現(xiàn)有關(guān)文本相似度比較的類Vector和Sketch,幫助大家進(jìn)一步掌握設(shè)計(jì)Python類來解決實(shí)際問題的能力。
01、文本相似度比較概述
通過計(jì)算并比較文檔的摘要可實(shí)現(xiàn)文本的相似度比較。
文檔摘要的最簡單形式可以使用文檔中的k-grams(k個(gè)連續(xù)字符)的相對(duì)頻率的向量來表示。假設(shè)字符的取值可能有128種不同的值(ASCII碼),則向量的維度d為128k,對(duì)于Unicode編碼,這更是天文數(shù)字。因此,一般使用哈希函數(shù)hash(s)?% d把k-grams字符串s映射到0到d-1之間的整數(shù),從而使得文檔摘要向量的維度為d。
創(chuàng)建文檔摘要向量之后,可通過比較兩個(gè)文檔摘要向量的距離的方法來判斷兩個(gè)文檔的相似度。
下面先闡述向量類(Vector)和文檔摘要類(Sketch)的設(shè)計(jì)與實(shí)現(xiàn),然后使用文檔摘要類來比較文檔的相似度。
02、向量(Vector)類設(shè)計(jì)和實(shí)現(xiàn)
向量是一種數(shù)學(xué)抽象,n維向量可以使用一個(gè)n個(gè)實(shí)數(shù)的有序列表(x0, x1, …, xn-1)。向量支持基本的四則算數(shù)運(yùn)算,故可通過運(yùn)算符重載來實(shí)現(xiàn)。
向量的基本運(yùn)算包括:兩個(gè)向量的加法、一個(gè)向量乘以一個(gè)標(biāo)量(一個(gè)實(shí)數(shù))、計(jì)算兩個(gè)向量的點(diǎn)積、計(jì)算向量大小和方向。
(1)加法:x + y =?( x0?+?y0,?x1?+?y1,?.?.?.,?xn-1?+?yn-1?)
(2)減法:x - y =?( x0?-?y0,?x1?-?y1,?.?.?.,?xn-1?-?yn-1?)
(3)標(biāo)量積:αx =?(αx0,?αx1,?.?.?.,?αxn-1)
(4)點(diǎn)積:x·y = x0y0?+?x1y1?+?.?.?.?+?xn-1yn-1
(5)大小:|x|?=?(x02?+?x12?+?.?.?.?+?xn-12)1/2
(6)方向:x /?|x|?=?( x0/|x|,?x1/|x|,?.?.?.,?xn-1/|x|)
基本的向量(Vector)類設(shè)計(jì)思路如下。
(1)定義帶一個(gè)列表參數(shù)(向量的坐標(biāo),可以是任意維度)的構(gòu)造函數(shù),用于初始化對(duì)應(yīng)向量的實(shí)例對(duì)象屬性_coords。
(2)重載方法__getitem__(),返回第i個(gè)元素,即第i維坐標(biāo)。
(3)重載方法__add__()、__sub__()、__abs__(),實(shí)現(xiàn)向量的運(yùn)算,即加法、減法、大小(模)。
(4)定義方法scale()、dot()、direction(),實(shí)現(xiàn)向量的運(yùn)算,即標(biāo)量積、點(diǎn)積、方向。
(5)重載方法__len__(),返回向量的維度。
(6)重載方法__str__(),返回向量的字符串表示。
基于上述設(shè)計(jì)思想,向量(Vector)的實(shí)現(xiàn)和測(cè)試代碼如下所示。
【例1】向量類(Vector)的實(shí)現(xiàn)和測(cè)試(vector.py)。
import math
class Vector:
"""笛卡爾坐標(biāo)系向量"""
def __init__(self, a):
"""構(gòu)造函數(shù):切片拷貝列表參數(shù)a到對(duì)象實(shí)例變量_coords"""
self._coords = a[:] # 坐標(biāo)列表
self._n = len(a) # 維度
def __getitem__(self, i):
"""返回第i個(gè)元素,即第i維坐標(biāo)"""
return self._coords[i]
def __add__(self, other):
"""返回2個(gè)向量之和"""
result = []
for i in range(self._n):
result.append(self._coords[i] + other._coords[i])
return Vector(result)
def __sub__(self, other):
"""返回2個(gè)向量之差"""
result = []
for i in range(self._n):
result.append(self._coords[i] - other._coords[i])
return Vector(result)
def scale(self, n):
"""返回向量與數(shù)值的乘積差"""
result = []
for i in range(self._n):
result.append(self._coords[i] * n)
return Vector(result)
def dot(self, other):
"""返回2向量的內(nèi)積"""
result = 0
for i in range(self._n):
result += self._coords[i] * other._coords[i]
return result
def __abs__(self):
"""返回向量的模"""
return math.sqrt(self.dot(self))
def direction(self):
"""返回向量的單位向量"""
return self.scale(1.0 / abs(self))
def __str__(self):
"""返回向量的字符串表示"""
return str(self._coords)
def __len__(self):
"""返回向量的維度"""
return self._n
#測(cè)試代碼
def main():
xCoords = [2.0, 2.0, 2.0]
yCoords = [5.0, 5.0, 0.0]
x = Vector(xCoords)
y = Vector(yCoords)
print('x = {}, y = {}'.format(x, y))
print('x + y = {}'.format(x + y))
print('10x = {}'.format(x.scale(10.0)))
print('|x| = {}'.format(abs(x)))
print(' = {}'.format(x.dot(y)))
print('|x-y| = {}'.format(abs(x-y)))
if __name__ == '__main__': main()
?文章來源地址http://www.zghlxwxcb.cn/news/detail-573726.html
程序運(yùn)行結(jié)果如下。
x?=?[2.0,?2.0,?2.0],?y?=?[5.0,?5.0,?0.0]
x?+?y?=?[7.0,?7.0,?2.0]
10x?=?[20.0,?20.0,?20.0]
|x|?=?3.4641016151377544
?=?20.0
|x-y|?=?4.69041575982343
03、文檔摘要類(Sketch)的設(shè)計(jì)和實(shí)現(xiàn)
文檔摘要類(Sketch)用于封裝文檔的摘要信息。設(shè)計(jì)思路如下。
(1)定義帶3個(gè)列表參數(shù)(text(文本)、k(k-grams)、d(文檔摘要向量的維度))的構(gòu)造函數(shù)。使用列表解析創(chuàng)建一個(gè)包含d個(gè)元素的列表freq(初始值為0),用于存儲(chǔ)k-grams的頻率。循環(huán)抽取文本的所有k-grams,并使用hash函數(shù)映射到0-d之間的整數(shù),從而更新對(duì)應(yīng)的列表freq的元素值(遞增)。然后使用freq創(chuàng)建Vector對(duì)象vector,并調(diào)用向量對(duì)象的direction()方法進(jìn)行歸一化。最后把文檔摘要向量vector并保存到實(shí)例對(duì)象屬性_sketch。
(2)定義方法similarTo(),計(jì)算兩個(gè)文檔摘要向量的余弦相似度。
比較兩個(gè)向量的常用方法包括歐幾里得距離和余弦相似性度。給定向量x和y,其歐幾里得距離定義為:
?余弦相似性度定義為:
?
基于Vector對(duì)象,給定向量x和y,其歐幾里得距離為abs(x – y),余弦相似性度的計(jì)算方法為x.dot(y)。
(3)重載方法__str__(),返回向量的字符串表示。
基于上述設(shè)計(jì)思想,向量(Sketch)的實(shí)現(xiàn)和測(cè)試代碼如下所示。
【例2】文檔摘要類(Sketch)的實(shí)現(xiàn)和測(cè)試(sketch.py)。
import sys
from vector import Vector
class Sketch:
"""計(jì)算文本text的k-grams的文檔摘要向量(d維)"""
def __init__(self, text, k, d):
"""初始化函數(shù):計(jì)算文本text的文檔摘要向量"""
freq = [0 for i in range(d)] #創(chuàng)建長度為d的列表,初始值0
for i in range(len(text) - k): #循環(huán)抽取k-grams,計(jì)算頻率
kgram = text[i:i+k]
freq[hash(kgram) % d] += 1
vector = Vector(freq) #創(chuàng)建文檔摘要向量
self._sketch = vector.direction() #歸一化并賦值給對(duì)象實(shí)例變量
def similarTo(self, other):
"""比較兩個(gè)文檔摘要對(duì)象Sketch的余弦相似度"""
return self._sketch.dot(other._sketch)
def __str__(self):
return str(self._sketch)
#測(cè)試代碼
def main():
with open("tomsawyer.txt","r") as f:
text = f.read()
sketch = Sketch(text, 5, 100)
print(sketch)
if __name__ == '__main__': main()
?
程序的運(yùn)行結(jié)果如下。
[0.09151094195152963,?…,?0.08903767325013694]
說明?/
哈希函數(shù)基于一個(gè)數(shù)值“種子”計(jì)算,在Python 3中,哈希種子會(huì)改變(缺省情況下),即給定對(duì)象的哈希值可能每次運(yùn)行結(jié)果都不一樣。因而,程序輸出結(jié)果可能不同。
?
04、通過比較文檔摘要確定文檔的相似度
使用前文設(shè)計(jì)和實(shí)現(xiàn)的類Sketch,可以比較文檔的相似度。
【例3】使用Sketch類比較文檔的相似度(document_compare.py)。
import sys
from vector import Vector
from sketch import Sketch
#測(cè)試文檔列表
filenames = [ 'gene.txt', 'pride.txt', 'tomsawyer.txt']
k = 5 #k-grams
d = 100000 #文檔摘要向量維度
sketches = [0 for i in filenames]
for i in range(len(filenames)):
with open(filenames[i], 'r') as f:
text = f.read()
sketches[i] = Sketch(text, k, d)
#輸出結(jié)果標(biāo)題
print(' '*15, end='')
for filename in filenames:
print('{:>22}'.format(filename), end='')
print()
#輸出結(jié)果比較明細(xì)
for i in range(len(filenames)):
print('{:15}'.format(filenames[i]), end='')
for j in range(len(filenames)):
print('{:22}'.format(sketches[i].similarTo(sketches[j])), end='')
print()
?程序運(yùn)行結(jié)果如下:
結(jié)果表明,相同文檔的相似度為1,相同類型的文檔(pride.txt和tomsawyer.txt)相似度比較大,而不同類型的文檔(gene.txt和pride.txt)的相似度則比較低。?文章來源:http://www.zghlxwxcb.cn/news/detail-573726.html
?
到了這里,關(guān)于Python案例分析|文本相似度比較分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!