基于余弦夹角计算句子相似度的应用——房型名称匹配

项目背景:

        将平台所售卖的房型与竞争对手的房型进行匹配,以节省人力及提高效率和匹配准确率~

数据的处理:

       竞对的房型名称相对于平台来说,显得非常的复杂,但是配合平台的强大的业务能力,在和运营人员讨论数据清洗规则方面花费了大量的时间,不过好在最后的数据清洗的已经足够干净了,不过这部分数据量庞大,已经在hive上面进行清洗好了。(ps:个人认为数据库真是的一个强大的工具,尤其在hive平台上进行数据的清洗,带来极大的便利~)

方法:

         利用jieba分词与余弦夹角相似度算法进行计算。

首先来说说余弦夹角即余弦相似度算法:

三角形中余弦计算公式:

\cos (\alpha )=(a^2+b^2-c^2)/(2ab)

用向量可以表示为:

\cos (\alpha )=(\underset{\alpha }{\rightarrow}*\underset{\beta }{\rightarrow})/(||\alpha ||*||\beta ||)                                     (1)

其中\alpha\beta为n维向量。

       其是利用向量空间中两个向量的夹角的余弦值作为度量两个样本或是个体之间的相似程度。当两个向量的夹角越小时,余弦值就越大,两个向量就越相似,此可称为余弦相似性。

下面给出一个例子来说明余弦夹角是如何计算句子的相似度。

 举一个例子来说明,用上述理论计算文本的相似性。为了简单起见,先从句子着手。
句子A:我在上海工作了一段时间
句子B:我在上海陆家嘴工作的时间有三个月
那么句子A和句子B的相似度如何计算呢?

基本思路是:如果这两句话的用词越相似,它们的内容就应该越相似。因此,可以从词频入手,计算它们的相似程度。
首先需要对两句话进行分词:
句子A:我在/上海/工作/了/一段/时间
句子B:我在/上海/陆家嘴/工作/的/时间/有/三个月
接下来要列出句子A和句子B的所有的词
我在 上海 工作 了 一段 时间 陆家嘴 的 有 三个月
最后写出词频向量:
句子A:(1,1,1,1,1,1,0,0,0,0)
句子B:(1,1,1,0,0,1,1,1,1,1)

 到这里,问题就变成了如何计算这两个向量的相似程度。我们可以把它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为0度,意味着方向相同、线段重合,这是表示两个向量代表的文本完全相等;如果夹角为90度,意味着形成直角,方向完全不相似;如果夹角为180度,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。

将句子A和句子B的词频向量带入到上式(1)中即可计算两个句子的相似度。

代码实现:

       在笔者用到的这个案例中,由于房型名称不像句子切割,词向量的长度并不会那么长,比如高级大床房和高级双床房,两者之间切割形成的长度可能只有四维,即[高级,大床,双床,房];因此较为简单,难点在于依据业务的规则进行数据的清洗,比如高级双人床和高级大床代表的可能是一个意思。

# -*- coding: utf-8 -*-
import pandas as pd
import jieba
import math

#计算关键字
def KeyWords(line1,line2):
    word_freq={} 
    word_freq2={}
    #分词
    words1=jieba.cut(line1,cut_all=False)
    words2=jieba.cut(line2,cut_all=False)
    #得出第一列的关键词
    for word in words1:   
         word_freq[word]=1
    freq_word = []  
    for word, freq in word_freq.items():  
         freq_word.append((word, freq)) 
    #得出第二列的关键词
    for word2 in words2:
         word_freq2[word2] = 1 
    freq_word2 = []  
    for word2, freq2 in word_freq2.items():  
         freq_word2.append((word2, freq2))
    return freq_word,freq_word2

#统计关键词及个数 并计算相似度  
def MergeKeys(freq_word,freq_word2):  
    #合并关键词 采用三个数组实现  
    arrayKey = []  
    for i in range(len(freq_word)):  
        arrayKey.append(freq_word[i][0])       #向数组中添加元素  
    for i in range(len(freq_word2)):         
        if freq_word2[i][0] in arrayKey:  
           pass  
        else:                             #合并  
            arrayKey.append(freq_word2[i][0])  

    #计算词频 infobox可忽略TF-IDF  
    arrayNum1 = [0]*len(arrayKey)  
    arrayNum2 = [0]*len(arrayKey)  
      
    #赋值arrayNum1  
    for i in range(len(freq_word)):       
        key = freq_word[i][0]  
        value = freq_word[i][1]  
        j = 0  
        while j < len(arrayKey):  
            if key == arrayKey[j]:  
                arrayNum1[j] = value  
                break  
            else:  
                j = j + 1  
  
    #赋值arrayNum2  
    for i in range(len(freq_word2)):       
        key = freq_word2[i][0]  
        value = freq_word2[i][1]  
        j = 0  
        while j < len(arrayKey):  
            if key == arrayKey[j]:  
                arrayNum2[j] = value  
                break  
            else:  
                j = j + 1  
      
    #print arrayNum1  
    #print arrayNum2  
   # print len(arrayNum1),len(arrayNum2),len(arrayKey)  
  
    #计算两个向量的点积  
    x = 0  
    i = 0  
    while i < len(arrayKey):  
        x = x + arrayNum1[i] * arrayNum2[i]  
        i = i + 1  
    #print x  
  
    #计算两个向量的模  
    i = 0  
    sq1 = 0  
    while i < len(arrayKey):  
        sq1 = sq1 + arrayNum1[i] * arrayNum1[i]   #pow(a,2)  
        i = i + 1  
    #print sq1  
      
    i = 0  
    sq2 = 0  
    while i < len(arrayKey):  
        sq2 = sq2 + arrayNum2[i] * arrayNum2[i]  
        i = i + 1  
    #print sq2  
      
    result = float(x) / ( math.sqrt(sq1) * math.sqrt(sq2) )  
    return  result

猜你喜欢

转载自blog.csdn.net/weixin_37536446/article/details/81284025