如何改善python中的距离函数

时间:2016-10-16 18:51:19

标签: python distance knn

我正在尝试对电子邮件文档(包含单词的字符串)进行分类练习。

我将距离函数定义如下:

def distance(wordset1, wordset2):

 if len(wordset1) < len(wordset2):
    return len(wordset2) - len(wordset1)
 elif len(wordset1) > len(wordset2):
    return len(wordset1) - len(wordset2)
 elif len(wordset1) == len(wordset2):
    return 0    

然而,最终的准确性非常低(0.8)。我想这是因为距离函数不太准确。我该如何改进功能?或者有什么其他方法来计算距离&#34;电子邮件文档之间?

2 个答案:

答案 0 :(得分:2)

在这种情况下使用的一种常见的相似度衡量指标是Jaccard similarity。范围从0到1,其中0表示完全不相似,1表示两个文档相同。它被定义为

wordSet1 = set(wordSet1)
wordSet2 = set(wordSet2)
sim = len(wordSet1.intersection(wordSet2))/len(wordSet1.union(wordSet2))

基本上,它是单词集的交集与单词集合的比率的比率。这有助于控制不同大小的电子邮件,同时仍能提供良好的相似性。

答案 1 :(得分:1)

您没有提及wordset1wordset2的类型。我假设他们都是strings

您将距离定义为单词计数并得分较差。它明显的文本长度并不是一个很好的差异性衡量标准:两个不同大小的电子邮件可以谈论相同的事情,而两个相同规模的电子邮件则谈论完全不同的事情。

因此,如上所述,您可以尝试检查SIMILAR WORDS:

import numpy as np

def distance(wordset1, wordset2):
    wordset1 = set(wordset1.split())
    wordset2 = set(wordset2.split())

    common_words = wordset1 & wordset2
    if common_words:
        return 1 / len(common_words) 
    else:
        # They don't share any word. They are infinitely different.
        return np.inf

问题在于,两个大电子邮件比两个小电子邮件更有可能共享单词,而这个指标会有利于这些,使它们彼此更相似#34;与小的相比。我们如何解决这个问题?好吧,我们以某种方式规范化指标:

import numpy as np

def distance(wordset1, wordset2):
    wordset1 = set(wordset1.split())
    wordset2 = set(wordset2.split())

    common_words = wordset1 & wordset2
    if common_words:
        # The distance, normalized by the total 
        # number of different words in the emails.
        return 1 / len(common_words) / (len(wordset1 | wordset2))
    else:
        # They don't share any word. They are infinitely different.
        return np.inf

这看起来很酷,但完全忽略了单词的频率。为了解决这个问题,我们可以使用Bag-of-words模型。也就是说,创建所有可能单词的列表,并在每个文档中对其外观进行直方图。让我们使用scikit中的CountVectorizer实现来学习我们的工作:

from sklearn.feature_extraction.text import CountVectorizer

def distance(wordset1, wordset2):
    model = CountVectorizer()
    X = model.fit_transform([wordset1, wordset2]).toarray()

    # uses Euclidean distance between bags.
    return np.linalg.norm(X[0] - X[1])

但现在考虑两对电子邮件。第一对中的电子邮件由完美的英文写成,其中包括&#34; small&#34;单词(例如aanisandthat),这在语法上是正确的。第二对中的电子邮件是不同的:只包含关键字,它非常干燥。你看,第一对可能比第二对更相似。之所以发生这种情况,是因为我们目前所有的单词都是相同的,而我们应该优先考虑每个文本中有意义的单词。为此,请使用term frequency–inverse document frequency。幸运的是,在scikit-learn中有一个非常类似的实现:

from sklearn.feature_extraction.text import TfidfVectorizer

def distance(wordset1, wordset2):
    model = TfidfVectorizer()
    X = model.fit_transform([wordset1, wordset2]).toarray()

    similarity_matrix = X.dot(X.T)
    # The dissimilarity between samples wordset1 and wordset2.
    return 1-similarity_matrix[0, 1]

在此question中详细了解此信息。还有,重复吗?

你现在应该具有相当好的准确性。试试看。如果它仍然没有你想要的那么好,那么我们必须更深入......(得到它?因为......深度学习)。首先,我们需要一个数据集来训练或已经训练过的模型。这是必需的,因为网络有许多参数必须调整才能提供有用的转换。

到目前为止缺少的是理解。我们对这些单词进行了直方图化,将它们与任何背景或含义区分开来。相反,让它们保持原样,并尝试识别模式块。怎么办呢?

  1. 将单词嵌入数字中,这将处理不同大小的单词。
  2. 将每个数字(单词嵌入)序列填充到一个长度。
  3. 使用卷积网络从序列中提取有意义的特征。
  4. 使用完全连接的网络将提取的要素投影到最小化类似电子邮件之间距离的空间,并最大化非相似电子邮件之间的距离。
  5. 让我们使用Keras来简化我们的生活。看起来应该是这样的:

    # ... imports and params definitions
    
    model = Sequential([
        Embedding(max_features,
                  embedding_dims,
                  input_length=maxlen,
                  dropout=0.2),
        Convolution1D(nb_filter=nb_filter,
                      filter_length=filter_length,
                      border_mode='valid',
                      activation='relu',
                      subsample_length=1),
        MaxPooling1D(pool_length=model.output_shape[1]),
        Flatten(),
        Dense(256, activation='relu'),
    ])
    
    # ... train or load model weights.
    
    def distance(wordset1, wordset2):
        global model
        # X = ... # Embed both emails.
        X = sequence.pad_sequences(X, maxlen=maxlen)
        y = model.predict(X)
        # Euclidean distance between emails.
        return np.linalg.norm(y[0]-y[1])
    

    有一个关于句子处理的实际例子,您可以查看Keras github repo。此外,有人使用此stackoverflow question中的暹罗经常性网络解决了同样的问题。

    嗯,我希望这会给你一些方向。 : - )