我正在尝试对电子邮件文档(包含单词的字符串)进行分类练习。
我将距离函数定义如下:
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;电子邮件文档之间?
答案 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)
您没有提及wordset1
和wordset2
的类型。我假设他们都是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;单词(例如a
,an
,is
,and
,that
),这在语法上是正确的。第二对中的电子邮件是不同的:只包含关键字,它非常干燥。你看,第一对可能比第二对更相似。之所以发生这种情况,是因为我们目前所有的单词都是相同的,而我们应该优先考虑每个文本中有意义的单词。为此,请使用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中详细了解此信息。还有,重复吗?
你现在应该具有相当好的准确性。试试看。如果它仍然没有你想要的那么好,那么我们必须更深入......(得到它?因为......深度学习)。首先,我们需要一个数据集来训练或已经训练过的模型。这是必需的,因为网络有许多参数必须调整才能提供有用的转换。
到目前为止缺少的是理解。我们对这些单词进行了直方图化,将它们与任何背景或含义区分开来。相反,让它们保持原样,并尝试识别模式块。怎么办呢?
让我们使用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中的暹罗经常性网络解决了同样的问题。
嗯,我希望这会给你一些方向。 : - )