3.3 聚类
最后,我们得到了特征向量,我们相信它足以捕捉到帖子的特征。有很多方法可以把帖子聚合分组,这并不奇怪。多数聚类算法都属于下面两个方法之一:扁平和层次聚类。
扁平聚类会将帖子分成一系列相互之间没有关联的簇。它的目标是通过一个划分,使一个簇中的帖子相互之间非常相似,而所有不同簇中的帖子很不相似。很多扁平聚类算法需要预先指定簇的个数。
在层次聚类中,并不需要指定簇的个数。相反,层次聚类可以构造出簇之间的层次关系。在相似帖子类聚到一个簇中的同时,相似的簇将会进一步聚到一个超级簇中。这个步骤递归下去,直到只剩下一个簇,它包含了所有东西。在层次结构中,人们可以选择所需要的簇的个数。但这样会降低效率。
Scikit在sklearn.cluster 包中提供了范围广泛的聚类方法。你可以在http://scikit-learn.org/dev/modules/clustering.html 快速浏览它们的优缺点。
接下来,我们将会使用扁平聚类方法K均值,并试验一下所需的簇个数。
3.3.1 K均值
K均值是应用最广泛的一个扁平聚类算法。当使用所需的簇个数num_clusters 进行初始化之后,该算法把这个值作为簇质心的数目。起初,它选出任意num_clusters 个帖子,并将它们的特征向量作为这些簇的质心。然后它遍历其他所有帖子,并将离它们最近的质心所在的簇分配给它们。再次,它将每个质心移向该簇中所有特征向量的中心点。当然,这将会改变簇的分配。一些帖子现在距离另一个簇更近了。因此该算法将会更新这些帖子的簇分配。只要质心移动相当一段距离,就可以做到这一点。经过一定的迭代,当移动量低于一定阈值的时候,我们就认为聚类已经收敛了。
提示 下载示例代码
如果你是通过 http://www.packtpub.com 的注册账户购买的图书,你可以从该账户中下载你购买过的所有Packt图书的示例代码。如果你是从其他地方购买的本书,你可以访问 http://www.packtpub.com/support 并进行注册,我们将会给你发送一封附有示例代码文件的电子邮件。
让我们通过一个简单的例子来验证这个算法,这个例子包含只有两个词语的帖子。下图中的每个数据点都代表了一个文档:
经过一次K均值迭代之后,以任意两个向量作为起始点,将标签赋予余下的样本,然后更新簇的中心,使之成为该簇中所有数据点的中心点,我们得到以下聚类:
由于簇中心的移动,我们必须重新分配簇的标签,并重新计算簇的中心点。在第二轮迭代之后,得到以下聚类:
箭头显示了簇中心的移动。在这个例子中,经过5轮迭代,簇中心点不再显著移动(Scikit的默认容许阈值是0.000 1)。
在聚类停当之后,我们只需要记录下簇中心及其标识。当每个新文档进来的时候,我们对它进行向量化,并与所有的簇中心进行比较。我们得到与新帖向量距离最小的簇中心所在的簇,然后把这个簇分配给该新帖子。
3.3.2 让测试数据评估我们的想法
为了测试聚类效果,让我们抛开这个简单的文本示例,寻找一个可以模拟所期待的能够测试我们方法的数据集。为此,我们需要一些已经类聚好的关于技术话题的文档。这样,之后我们在得到的帖子上应用算法时,就可以检验算法的效果是否符合预期。
20newsgroup 数据集是机器学习中的一个标准数据集。它包含18 286个帖子,来自于20个不同的新闻组。在这些新闻组的话题中,有的是技术话题,如comp.sys.mac.hardware 或sci.crypt ,有的是政治话题或和宗教相关话题,如talk.politics.guns 或soc.religion.christian 。我们把范围限制在技术话题的新闻组中。如果我们假定每个新闻组是一个簇,那么很容易测试出我们寻找相关帖子的方法是否有效。
这个数据集可以从http://people.csail.mit.edu/jrennie/20Newsgroups 下载。而更简单的方式是从MLComp(http://mlcomp.org/datasets/379 )下载(需要免费注册)。Scikit已经包含了定制的读取器来读取这个数据集,并提供了非常方便的读取选项。
这个数据集是用ZIP文件形式存放的:dataset-379-20news-18828_WJQIG.zip。我们需要解压得到文件夹379,它包含这个数据集。我们还要告诉Scikit数据目录的路径。它包含一个原信息文件和3个目录:test、train和raw。测试和训练目录将整个数据集切分成60%的训练和40%的测试帖子。为方便起见,dataset 模块还包含了函数fetch_20newsgroups ,它将数据下载到预期目录中。
注意 http://mlcomp.org 是一个用于在多种数据集上比较机器学习算法程序的网站。它有两个目的:找到正确的数据集来调优机器学习程序,以及探究其他人如何使用某个特定的数据集。例如,你可以看到他人的算法在特定数据集上的效果,并与之比较。
在读取数据集的时候,可以设置环境变量MLCOMP_DATASETS_HOME ,或者通过mlcomp_root 参数直接指定路径,如下所示:
>>> import sklearn.datasets >>> MLCOMP_DIR = r"D:\data" >>> data = sklearn.datasets.load_mlcomp("20news-18828", mlcomp_ root=MLCOMP_DIR) >>> print(data.filenames) array(['D:\\data\\379\\raw\\comp.graphics\\1190-38614', 'D:\\data\\379\\raw\\comp.graphics\\1383-38616', 'D:\\data\\379\\raw\\alt.atheism\\487-53344', ..., 'D:\\data\\379\\raw\\rec.sport.hockey\\10215-54303', 'D:\\data\\379\\raw\\sci.crypt\\10799-15660', 'D:\\data\\379\\raw\\comp.os.ms-windows.misc\\2732-10871'], dtype='|S68') >>> print(len(data.filenames)) 18828 >>> data.target_names ['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys. ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc. forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec. sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.
我们可以在训练和测试集合中进行选取,如下所示:
>>> train_data = sklearn.datasets.load_mlcomp("20news-18828", "train", mlcomp_root=MLCOMP_DIR) >>> print(len(train_data.filenames)) 13180 >>> test_data = sklearn.datasets.load_mlcomp("20news-18828", "test", mlcomp_root=MLCOMP_DIR) >>> print(len(test_data.filenames)) 5648
为方便起见,我们把范围限制在某些新闻组中,使整个实验流程更短。我们可以通过categories 参数实现这一点:
>>> groups = ['comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys. ibm.pc.hardware', 'comp.sys.ma c.hardware', 'comp.windows.x', 'sci. space'] >>> train_data = sklearn.datasets.load_mlcomp("20news-18828", "train", mlcomp_root=MLCOMP_DIR, categories=groups) >>> print(len(train_data.filenames)) 3414
3.3.3 对帖子聚类
你肯定已经注意到了一件事——真实数据含有很多噪声。新闻组数据也不例外。它甚至包含了不合法的字符,这会导致UnicodeDecodeError 。
我们必须要让向量化处理器忽略它们:
>>> vectorizer = StemmedTfidfVectorizer(min_df=10, max_df=0.5, ... stop_words='english', charset_error='ignore') >>> vectorized = vectorizer.fit_transform(dataset.data) >>> num_samples, num_features = vectorized.shape >>> print("#samples: %d, #features: %d" % (num_samples, num_features)) #samples: 3414, #features: 4331
我们现在有一个大小为3414的帖子池,每个帖子的特征向量的维度是4331。这些就是K均值算法的输入。本章中我们把簇的大小固定在50。希望你可以带着好奇心去尝试不同的值,把它当做一个练习。如下列代码所示:
>>> num_clusters = 50 >>> from sklearn.cluster import KMeans >>> km = KMeans(n_clusters=num_clusters, init='random', n_init=1, verbose=1) >>> km.fit(vectorized)
就是这样。在拟合之后,我们可以从km 的成员变量中获得聚类信息。针对每个拟合过的帖子向量,km.labels_ 都给出了一个对应的整数标签:
>>> km.labels_ array([33, 22, 17, ..., 14, 11, 39]) >>> km.labels_.shape (3414,)
簇的中心可以通过km.cluster_centers_ 访问。
在下一节中我们将会学习如何通过km.predict 给新来的帖子分配一个簇。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论