hanlp 词频统计_10.HanLP实现k均值--文本聚类

AI

人工智能

10.HanLP实现k均值--文本聚类

10. 文本聚类

正所谓物以类聚,人以群分。人们在获取数据时需要整理,将相似的数据归档到一起,自动发现大量样本之间的相似性,这种根据相似性归档的任务称为聚类。

10.1 概述

聚类

聚类(cluster analysis )指的是将给定对象的集合划分为不同子集的过程,目标是使得每个子集内部的元素尽量相似,不同子集间的元素尽量不相似。这些子集又被称为簇(cluster),一般没有交集。

一般将聚类时簇的数量视作由使用者指定的超参数,虽然存在许多自动判断的算法,但它们往往需要人工指定其他超参数。

根据聚类结果的结构,聚类算法也可以分为划分式(partitional )和层次化(hierarchieal两种。划分聚类的结果是一系列不相交的子集,而层次聚类的结果是一棵树, 叶子节点是元素,父节点是簇。本章主要介绍划分聚类。

文本聚类

文本聚类指的是对文档进行聚类分析,被广泛用于文本挖掘和信息检索领域。

文本聚类的基本流程分为特征提取和向量聚类两步, 如果能将文档表示为向量,就可以对其应用聚类算法。这种表示过程称为特征提取,而一旦将文档表示为向量,剩下的算法就与文档无关了。这种抽象思维无论是从软件工程的角度,还是从数学应用的角度都十分简洁有效。

10.2 文档的特征提取

词袋模型

词袋(bag-of-words )是信息检索与自然语言处理中最常用的文档表示模型,它将文档想象为一个装有词语的袋子, 通过袋子中每种词语的计数等统计量将文档表示为向量。比如下面的例子:

人 吃 鱼。

美味 好 吃!

统计词频后如下:

人=1

吃=2

鱼=1

美味=1

好=1

文档经过该词袋模型得到的向量表示为[1,2,1,1,1],这 5 个维度分别表示这 5 种词语的词频。

一般选取训练集文档的所有词语构成一个词表,词表之外的词语称为 oov,不予考虑。一旦词表固定下来,假设大小为 N。则任何一个文档都可以通过这种方法转换为一个N维向量。词袋模型不考虑词序,也正因为这个原因,词袋模型损失了词序中蕴含的语义,比如,对于词袋模型来讲,“人吃鱼”和“鱼吃人”是一样的,这就不对了。

不过目前工业界已经发展出很好的词向量表示方法了: word2vec/bert 模型等。

词袋中的统计指标

词袋模型并非只是选取词频作为统计指标,而是存在许多选项。常见的统计指标如下:

布尔词频: 词频非零的话截取为1,否则为0,适合长度较短的数据集

TF-IDF: 适合主题较少的数据集

词向量: 如果词语本身也是某种向量的话,则可以将所有词语的词向量求和作为文档向量。适合处理 OOV 问题严重的数据集。

词频向量: 适合主题较多的数据集

定义由 n 个文档组成的集合为 S,定义其中第 i 个文档 di 的特征向量为 di,其公式如下:

[

d_{i}=left(operatorname{TF}left(t_{1}, d_{i}right), operatorname{TF}left(t_{2}, d_{i}right), cdots, operatorname{TF}left(t_{j}, d_{i}right), cdots, operatorname{TF}left(t_{m}, d_{i}right)right)

]

其中 tj表示词表中第 j 种单词,m 为词表大小, TF(tj, di) 表示单词 tj 在文档 di 中的出现次数。为了处理长度不同的文档,通常将文档向量处理为单位向量,即缩放向量使得 ||d||=1。

10.3 k均值算法

一种简单实用的聚类算法是k均值算法(k-means),由Stuart Lloyd于1957年提出。该算法虽然无法保证一定能够得到最优聚类结果,但实践效果非常好。基于k均值算法衍生出许多改进算法,先介绍 k均值算法,然后推导它的一个变种。

基本原理

形式化啊定义 k均值算法所解决的问题,给定 n 个向量 d1 到 dn,以及一个整数 k,要求找出 k 个簇 S1 到 Sk 以及各自的质心 C1 到 Ck,使得下式最小:

[

text { minimize } mathcal{I}_{text {Euclidean }}=sum_{r=1}^{k} sum_{d_{i} in S_{r}}left|boldsymbol{d}_{i}-boldsymbol{c}_{r}right|^{2}

]

其中 ||di - Cr|| 是向量与质心的欧拉距离,I(Euclidean) 称作聚类的准则函数。也就是说,k均值以最小化每个向量到质心的欧拉距离的平方和为准则进行聚类,所以该准则函数有时也称作平方误差和函数。而质心的计算就是簇内数据点的几何平均:

[

begin{array}{l}{s_{i}=sum_{d_{j} in S_{i}} d_{j}} \ {c_{i}=frac{s_{i}}{left|S_{i}right|}}end{array}

]

其中,si 是簇 Si 内所有向量之和,称作合成向量。

生成 k 个簇的 k均值算法是一种迭代式算法,每次迭代都在上一步的基础上优化聚类结果,步骤如下:

选取 k 个点作为 k 个簇的初始质心。

将所有点分别分配给最近的质心所在的簇。

重新计算每个簇的质心。

重复步骤 2 和步骤 3 直到质心不再发生变化。

k均值算法虽然无法保证收敛到全局最优,但能够有效地收敛到一个局部最优点。对于该算法,初级读者重点需要关注两个问题,即初始质心的选取和两点距离的度量。

初始质心的选取

由于 k均值不保证收敏到全局最优,所以初始质心的选取对k均值的运行结果影响非常大,如果选取不当,则可能收敛到一个较差的局部最优点。

一种更高效的方法是, 将质心的选取也视作准则函数进行迭代式优化的过程。其具体做法是,先随机选择第一个数据点作为质心,视作只有一个簇计算准则函数。同时维护每个点到最近质心的距离的平方,作为一个映射数组 M。接着,随机取准则函数值的一部分记作。遍历剩下的所有数据点,若该点到最近质心的距离的平方小于0,则选取该点添加到质心列表,同时更新准则函数与 M。如此循环多次,直至凑足 k 个初始质心。这种方法可行的原理在于,每新增一个质心,都保证了准则函数的值下降一个随机比率。 而朴素实现相当于每次新增的质心都是完全随机的,准则函数的增减无法控制。孰优孰劣,一目了然。

考虑到 k均值是一种迭代式的算法, 需要反复计算质心与两点距离,这部分计算通常是效瓶颈。为了改进朴素 k均值算法的运行效率,HanLP利用种更快的准则函数实现了k均值的变种。

更快的准则函数

除了欧拉准则函数,还存在一种基于余弦距离的准则函数:

[

text { maximize } mathcal{I}_{mathrm{cos}}=sum_{r=1}^{k} sum_{d_{i} in S_{r}} cos left(boldsymbol{d}_{i}, boldsymbol{c}_{r}right)

]

该函数使用余弦函数衡量点与质心的相似度,目标是最大化同簇内点与质心的相似度。将向量夹角计算公式代人,该准则函数变换为:

[

mathcal{I}_{mathrm{cos}}=sum_{r=1}^{k} sum_{d_{i} in S_{r}} frac{d_{i} cdot c_{r}}{left|c_{r}right|}

]

代入后变换为:

[

mathcal{I}_{cos }=sum_{r=1}^{k} frac{S_{r} cdot c_{r}}{left|c_{r}right|}=sum_{r=1}^{k} frac{left|S_{r}right| c_{r} cdot c_{r}}{left|c_{r}right|}=sum_{r=1}^{k}left|S_{r}right|left|c_{r}right|=sum_{r=1}^{k}left|s_{r}right|

]

也就是说,余弦准则函数等于 k 个簇各自合成向量的长度之和。比较之前的准则函数会发现在数据点从原簇移动到新簇时,I(Euclidean) 需要重新计算质心,以及两个簇内所有点到新质心的距离。而对于I(cos),由于发生改变的只有原簇和新簇两个合成向量,只需求两者的长度即可,计算量一下子减小不少。

基于新准则函数 I(cos),k均值变种算法流程如下:

选取 k 个点作为 k 个簇的初始质心。

将所有点分别分配给最近的质心所在的簇。

对每个点,计算将其移入另一个簇时 I(cos) 的增大量,找出最大增大量,并完成移动。

重复步骤 3 直到达到最大迭代次数,或簇的划分不再变化。

实现

在 HanLP 中,聚类算法实现为 ClusterAnalyzer,用户可以将其想象为一个文档 id 到文档向量的映射容器。

此处以某音乐网站中的用户聚类为案例讲解聚类模块的用法。假设该音乐网站将 6 位用户点播的歌曲的流派记录下来,并且分别拼接为 6 段文本。给定用户名称与这 6 段播放历史,要求将这 6 位用户划分为 3 个簇。实现代码如下:

from pyhanlp import *

ClusterAnalyzer = JClass('com.hankcs.hanlp.mining.cluster.ClusterAnalyzer')

if __name__ == '__main__':

analyzer = ClusterAnalyzer()

analyzer.addDocument("赵一", "流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 蓝调, 蓝调, 蓝调, 蓝调, 蓝调, 蓝调, 摇滚, 摇滚, 摇滚, 摇滚")

analyzer.addDocument("钱二", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲")

analyzer.addDocument("张三", "古典, 古典, 古典, 古典, 民谣, 民谣, 民谣, 民谣")

analyzer.addDocument("李四", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 金属, 金属, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲")

analyzer.addDocument("王五", "流行, 流行, 流行, 流行, 摇滚, 摇滚, 摇滚, 嘻哈, 嘻哈, 嘻哈")

analyzer.addDocument("马六", "古典, 古典, 古典, 古典, 古典, 古典, 古典, 古典, 摇滚")

print(analyzer.kmeans(3))

结果如下:

[[李四, 钱二], [王五, 赵一], [张三, 马六]]

通过 k均值聚类算法,我们成功的将用户按兴趣分组,获得了“人以群分”的效果。

聚类结果中簇的顺序是随机的,每个簇中的元素也是无序的,由于 k均值是个随机算法,有小概率得到不同的结果。

该聚类模块可以接受任意文本作为文档,而不需要用特殊分隔符隔开单词。

10.4 重复二分聚类算法

基本原理

重复二分聚类(repeated bisection clustering) 是 k均值算法的效率加强版,其名称中的bisection是“二分”的意思,指的是反复对子集进行二分。该算法的步骤如下:

挑选一个簇进行划分。

利用 k均值算法将该簇划分为 2 个子集。

重复步骤 1 和步骤 2,直到产生足够舒朗的簇。

每次产生的簇由上到下形成了一颗二叉树结构。

正是由于这个性质,重复二分聚类算得上一种基于划分的层次聚类算法。如果我们把算法运行的中间结果存储起来,就能输出一棵具有层级关系的树。树上每个节点都是一个簇,父子节点对应的簇满足包含关系。虽然每次划分都基于 k均值,由于每次二分都仅仅在一个子集上进行,输人数据少,算法自然更快。

在步骤1中,HanLP采用二分后准则函数的增幅最大为策略,每产生一个新簇,都试着将其二分并计算准则函数的增幅。然后对增幅最大的簇执行二分,重复多次直到满足算法停止条件。

自动判断聚类个数k

读者可能觉得聚类个数 k 这个超参数很难准确估计。在重复二分聚类算法中,有一种变通的方法,那就是通过给准则函数的增幅设定阈值 β 来自动判断 k。此时算法的停止条件为,当一个簇的二分增幅小于 β 时不再对该簇进行划分,即认为这个簇已经达到最终状态,不可再分。当所有簇都不可再分时,算法终止,最终产生的聚类数量就不再需要人工指定了。

实现

from pyhanlp import *

ClusterAnalyzer = JClass('com.hankcs.hanlp.mining.cluster.ClusterAnalyzer')

if __name__ == '__main__':

analyzer = ClusterAnalyzer()

analyzer.addDocument("赵一", "流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 蓝调, 蓝调, 蓝调, 蓝调, 蓝调, 蓝调, 摇滚, 摇滚, 摇滚, 摇滚")

analyzer.addDocument("钱二", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲")

analyzer.addDocument("张三", "古典, 古典, 古典, 古典, 民谣, 民谣, 民谣, 民谣")

analyzer.addDocument("李四", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 金属, 金属, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲")

analyzer.addDocument("王五", "流行, 流行, 流行, 流行, 摇滚, 摇滚, 摇滚, 嘻哈, 嘻哈, 嘻哈")

analyzer.addDocument("马六", "古典, 古典, 古典, 古典, 古典, 古典, 古典, 古典, 摇滚")

print(analyzer.repeatedBisection(3)) # 重复二分聚类

print(analyzer.repeatedBisection(1.0)) # 自动判断聚类数量k

运行结果如下:

[[李四, 钱二], [王五, 赵一], [张三, 马六]]

[[李四, 钱二], [王五, 赵一], [张三, 马六]]

与上面音乐案例得出的结果一致,但运行速度要快不少。

10.5 标准化评测

本次评测选择搜狗实验室提供的文本分类语料的一个子集,我称它为“搜狗文本分类语料库迷你版”。该迷你版语料库分为5个类目,每个类目下1000 篇文章,共计5000篇文章。运行代码如下:

from pyhanlp import *

import zipfile

import os

from pyhanlp.static import download, remove_file, HANLP_DATA_PATH

def test_data_path():

"""

获取测试数据路径,位于$root/data/test,根目录由配置文件指定。

:return:

"""

data_path = os.path.join(HANLP_DATA_PATH, 'test')

if not os.path.isdir(data_path):

os.mkdir(data_path)

return data_path

## 验证是否存在 MSR语料库,如果没有自动下载

def ensure_data(data_name, data_url):

root_path = test_data_path()

dest_path = os.path.join(root_path, data_name)

if os.path.exists(dest_path):

return dest_path

if data_url.endswith('.zip'):

dest_path += '.zip'

download(data_url, dest_path)

if data_url.endswith('.zip'):

with zipfile.ZipFile(dest_path, "r") as archive:

archive.extractall(root_path)

remove_file(dest_path)

dest_path = dest_path[:-len('.zip')]

return dest_path

sogou_corpus_path = ensure_data('搜狗文本分类语料库迷你版', 'http://file.hankcs.com/corpus/sogou-text-classification-corpus-mini.zip')

## ===============================================

## 以下开始聚类

ClusterAnalyzer = JClass('com.hankcs.hanlp.mining.cluster.ClusterAnalyzer')

if __name__ == '__main__':

for algorithm in "kmeans", "repeated bisection":

print("%s F1=%.2fn" % (algorithm, ClusterAnalyzer.evaluate(sogou_corpus_path, algorithm) * 100))

运行结果如下:

kmeans F1=83.74

repeated bisection F1=85.58

评测结果如下表:

算法

F1

耗时k均值

83.74

67秒

重复二分聚类

85.58

24秒

对比两种算法,重复二分聚类不仅准确率比 k均值更高,而且速度是 k均值的 3 倍。然而重复二分聚类成绩波动较大,需要多运行几次才可能得出这样的结果。

无监督聚类算法无法学习人类的偏好对文档进行划分,也无法学习每个簇在人类那里究竟叫什么。

10.6 GitHub

HanLP何晗--《自然语言处理入门》笔记:

项目持续更新中......

目录

内容来源于网络,如有侵权请联系客服删除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/572856.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

追本溯源 —— 汉语词汇含义的演化

1. 比喻义 枷:旧时一种套在脖子上的刑具(想象水浒传里的林冲,还要把手塞进去):~锁(旧时的两种刑具,喻束缚)。转载于:https://www.cnblogs.com/mtcnn/p/9422411.html

view 冒号作用 组件中属性_如何解析名称中带有冒号的JSON?安卓/ Java...

例如:{“ primary:title”:“小红帽”}由于主要名称和标题之间存在冒号,因此我在Java(Android)中的解析器总是卡住.我可以轻松解析其他任何内容,我只需要帮助.public class MainActivity extends Activity {/** Called when the activity is …

【工具与解决方案】从做项目中积累学习

【Java-Swing】 1.http://java-mans.iteye.com/blog/1650786 JAVA-SWT如何在Jtable单元格中加入复选框Jcheckbox,Jbutton,JcomboBox 2.环境搭建 http://blog.csdn.net/ghuil/article/details/40652645 http://www.cnblogs.com/yaowukonga/archive/2013/02/28/29…

目标检测排行榜_3D领域重大突破!大华股份人工智能取得KITTI Object 3D车辆检测排行榜第一名...

[导读]近日,大华股份基于深度学习技术研发的3D目标检测技术,刷新了The KITTI Vision Benchmark Suite中3D车辆类目检测任务(3D Object Detection Evaluation)排行榜,取得了3D车辆类目检测第一名,这标志着大华股份的人工智能技术在…

MYSQL-5.5二进制包安装

groupadd mysql 添加用户组 useradd mysql -s /sbin/nologin -g mysql -M 添加用户 mv mysql-5.5.54-linux2.6-x86_64.tar.gz /home/ye/software/ 下载软件到软件包 cd /home/ye/software/ tar -xf mysql-5.5.54-linux2.6-x86_64.tar.gz #解压mysql软件 cd mysql-5.5.54-lin…

mysql aes encrypt_mysql加密函数aes_encrypt()和aes_decrypt()使用教程

aes_encrypt()和aes_decrypt()在mysql中是进行加密了,我们今天一起来和各位看看关于mysql中aes_encrypt()和aes_decrypt()函数的使用例子.如果你需要对mysql某些字段进行加解密的话,使用mysql的加解密函数可能比程序中处理更方便.mysql-encrypt-funcs.pn…

为什么选择mysql_为什么选择MySQL数据库即MySQL优势介绍

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航,为用户…

窗口尺寸,文档高,元素宽高的获取方式

一.元素宽高: window.onload function() { var oDiv document.getElementById(div1); /* width height style.width : 样式宽 clientWidth : 可视区宽 offsetWidth : 占位宽 */ alert( oDiv.style.width ); /…

Mariadb使用总结

一、pt-quert-digest使用1、安装perl程序12yum -y install perl-Time-HiResyum -y install perl-DBI二、登录授权123456789service mysqld stop/usr/bin/mysqld_safe --skip-grant-tables &use mysql;update user set passwordpassword(yournewpasswordhere) where userroo…

python执行到input后执行下一程序_Python基础知识储备,如何开关一个while循环

一、什么是循环循环语句就是在某种条件下,一遍一遍重复的执行某个动作。如:从1加到100,重复执行加法的动作,就需要用到循环。二、循环的三要素虽然循环是反复的执行某个动作,但是循环也会停止的,没有停止的…

Intellij idea添加单元测试工具

1.idea 版本是14.0.0 ,默认带有Junit,但是不能自动生成单元测试,需要下载JunitGererator2.0插件 2.Settings -Plugins,下载 JunitGenerator V2.0插件 ,我的是已经下载好的。下载后提示需要重启 3.下载后 点击需要测试的…

string.empty , , null 以及性能的比较

一:这种结论,个人觉得仍然存疑 http://www.cnblogs.com/wangshuai901/archive/2012/05/06/2485657.html 1.null null 关键字是表示不引用任何对象的空引用的文字值。null 是引用类型变量的默认值。那么也只有引用型的变量可以为NULL,如果 int…

mysql查询当前库的实例名_oracle查询数据库名、实例名等

oracle中:1、查询数据库名:select name,dbid from v$database;或者show parameter db_name;2、查询实例名:select instance_name from v$instance;或者show parameter instance_name;3、查询数据库域名:select value from v$param…

JPA 系列教程21-JPA2.0-@MapKeyColumn

MapKeyColumn 用JoinColumn注解和MapKeyColumn处理一对多关系 ddl语句 CREATE TABLE t_employee (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8;CREATE TABLE t_employee_map…

错误:在keystone中无法找到默认角色user_Kubernetes RBAC角色权限控制

摘选:https://i4t.com/4448.html在Kubernetes中所有的API对象都保存在ETCD里,可是,对这些API对象的操作,却一定是通过访问kube-apiserver实现的。我们需要APIServer来帮助我们授权工作,而在Kubernetes项目中,负责完成授…

JAVAWEB项目如何实现验证码

验证码基础 一.什么是验证码及它的作用 :验证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计算机的公共全自动程序,这个问题可以由计算机生成并评判,但是必须只有人类才能解答.可以防止恶意破解密码、刷票、论坛灌水、有效防止某个黑客对某一个特定…

在线判题系统(oj)效果分析图_在线代码编写平台开发分享

计算机专业的大学生应该都了解acm比赛,这种通过使用oj(online judge)系统在线编程刷题,实时反馈学习排名的方式能很大程度激发学生的学习热情。oj学习排名界面oj个人学习记录界面只是oj平台一般都只适用后端语言,如java,c#, c,C&a…

BZOJ1298:[SCOI2009]骰子的学问

Description Input 第一行为两个整数n, m。第二行有n个整数,为a1,a2, …, an。 Output 包含n行,每行m个1~nm的正整数,各不相同,以空格分开。如果有多解,输出任意一组解;如果无解,输出…

mysql交叉连接后生成的记录总数_4.mysql数据库创建,表创建模等模板脚本,mysql_SQL99标准的连接查询(内连接,外连接,满外连接,交叉连接)_MySQL...

--用root用户登录系统,执行脚本--创建数据库create database mydb61 character set utf8 ;--选择数据库use mydb61;--增加 dbuser1用户--创建用户‘dbuser61’密码为‘dbuser61’拥有操作数据库mydb61的所有权限GRANT ALL ON mydb61.* TO dbuser61 IDENTIFIED BY &q…

IIS网站或系统验证码不显示问题——使用了托管的处理程序,但是未安装或未完整安装 ASP.NET...

在IIS上发布了一个系统,但是登陆页面的验证码图片一直出不来,尝试了各种办法,权限、路径、继承父类路径等都不管用,进入Login.html,对着无验证码图片的图标,右键复制图片的网址,粘贴到地址栏,出…