文本挖掘之文本聚类(DBSCAN)
2015-11-09 18:09
375 查看
刘 勇 Email:lyssym@sina.com
简介
鉴于基于划分的文本聚类方法只能识别球形的聚类,因此本文对基于密度的文本聚类算法展开研究。DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种典型的基于密度的聚类方法,可以找出形状不规则的聚类,而且聚类时无需事先知道聚类的个数。
基本概念
DBSCAN算法中有两个核心参数:Eps和MinPts(文献与程序中经常使用)。前者定义为邻域半径,后者定义为核心对象的阈值。本文为了描述方便,下文将Eps和MinPts分别简记为E和M。
(1) E 邻域:给定对象半径E内的区域成为该对象的E邻域。该E邻域为球形,其半径的界定可以采用距离(欧式距离)、余弦相似度、Word2Vec等表征,本文实现采用余弦相似度来表征。
(2) 核心对象:若给定对象E 邻域内的对象(样本点)个数大于等于M,则称该对象为核心对象。
(3) 直接密度可达:给定一个对象集合D,若对象p在q的E 邻域内,且q是一个核心对象,则称对象p从对象q出发是直接密度可达的(directly density-reachable)。
(4) 密度可达:给定一个对象集合D,若存在一个对象链p1,p2,p3,...,pn,p1=q,pn=p,对于pi属于D,i属于1~n,p(i+1)是从pi关于E和M直接密度可达的,则称对象p从对象q关于E和M密度可达的。
(5) 密度相连:给定一个对象集合D,若存在对象o属于D,使对象p和q均从o关于E和M密度可达的,那么对于对象p到q是关于E和M密度相连的。
(6) 边界对象:给定一个对象集合D,若核心对象p中存在对象q,但是q对象自身并非核心对象,则称q为边界对象。
(7) 噪声对象:给定一个对象集合D,若对象o既不是核心对象,也不是边界对象,则称o为噪声对象。
![](http://images2015.cnblogs.com/blog/798916/201511/798916-20151109173828119-1360488045.png)
图1 集合对象
如图1所示,其设定M=3,红色节点为核心对象,黄色节点为边界节点,蓝色为噪声节点。
DBSCAN算法不仅能对对象进行聚类,同时还能识别噪声对象,即不属于任何一个聚类。需要指出:密度可达是直接密度可达的传递闭包,这种关系是非对称的,只有核心对象之间相互密度可达;但是,密度相连是一种对称的关系。
DBSCAN的核心思想:寻找密度相连的最大集合,即从某个选定的核心对象(核心点)出发,不断向密度可达的区域扩张,从而得到一个包含核心对象和边界对象的最大化区域,区域中任意两点密度相连。
算法伪代码(参考维基百科):
程序源代码如下:
关于计算余弦相似度及其源代码,见本系列之文本挖掘之文本相似度判定。本文考虑到随着文本数量的递增,文本聚类结果会分化,即刚开始聚类的文本与后面文本数据相差甚远,本文非常粗略地采用门限值进行限定,本文后续系列联合KMeans和DBSCAN进行解决,若有更好的方法,请联系我。
需要指出:DBSCAN算法对输入参数E和M非常敏感,即参数稍有变化,其聚类结果则会有明显变化。此外,对E和M的选择在实际应用中,需要大规模数据集调优选择(小数据集另当别论),同时对该算法进行改进也是重要研究方向。
作者:志青云集
出处:http://www.cnblogs.com/lyssym
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【志青云集】。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
简介
鉴于基于划分的文本聚类方法只能识别球形的聚类,因此本文对基于密度的文本聚类算法展开研究。DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种典型的基于密度的聚类方法,可以找出形状不规则的聚类,而且聚类时无需事先知道聚类的个数。
基本概念
DBSCAN算法中有两个核心参数:Eps和MinPts(文献与程序中经常使用)。前者定义为邻域半径,后者定义为核心对象的阈值。本文为了描述方便,下文将Eps和MinPts分别简记为E和M。
(1) E 邻域:给定对象半径E内的区域成为该对象的E邻域。该E邻域为球形,其半径的界定可以采用距离(欧式距离)、余弦相似度、Word2Vec等表征,本文实现采用余弦相似度来表征。
(2) 核心对象:若给定对象E 邻域内的对象(样本点)个数大于等于M,则称该对象为核心对象。
(3) 直接密度可达:给定一个对象集合D,若对象p在q的E 邻域内,且q是一个核心对象,则称对象p从对象q出发是直接密度可达的(directly density-reachable)。
(4) 密度可达:给定一个对象集合D,若存在一个对象链p1,p2,p3,...,pn,p1=q,pn=p,对于pi属于D,i属于1~n,p(i+1)是从pi关于E和M直接密度可达的,则称对象p从对象q关于E和M密度可达的。
(5) 密度相连:给定一个对象集合D,若存在对象o属于D,使对象p和q均从o关于E和M密度可达的,那么对于对象p到q是关于E和M密度相连的。
(6) 边界对象:给定一个对象集合D,若核心对象p中存在对象q,但是q对象自身并非核心对象,则称q为边界对象。
(7) 噪声对象:给定一个对象集合D,若对象o既不是核心对象,也不是边界对象,则称o为噪声对象。
![](http://images2015.cnblogs.com/blog/798916/201511/798916-20151109173828119-1360488045.png)
图1 集合对象
如图1所示,其设定M=3,红色节点为核心对象,黄色节点为边界节点,蓝色为噪声节点。
DBSCAN算法不仅能对对象进行聚类,同时还能识别噪声对象,即不属于任何一个聚类。需要指出:密度可达是直接密度可达的传递闭包,这种关系是非对称的,只有核心对象之间相互密度可达;但是,密度相连是一种对称的关系。
DBSCAN的核心思想:寻找密度相连的最大集合,即从某个选定的核心对象(核心点)出发,不断向密度可达的区域扩张,从而得到一个包含核心对象和边界对象的最大化区域,区域中任意两点密度相连。
算法伪代码(参考维基百科):
DBSCAN(D, eps, MinPts) { C = 0 for each point P in dataset D { if P is visited continue next point mark P as visited NeighborPts = regionQuery(P, eps) if sizeof(NeighborPts) < MinPts mark P as NOISE else { C = next cluster expandCluster(P, NeighborPts, C, eps, MinPts) } } } expandCluster(P, NeighborPts, C, eps, MinPts) { add P to cluster C for each point P' in NeighborPts { if P' is not visited { mark P' as visited NeighborPts' = regionQuery(P', eps) if sizeof(NeighborPts') >= MinPts NeighborPts = NeighborPts joined with NeighborPts' } if P' is not yet member of any cluster add P' to cluster C } } regionQuery(P, eps) return all points within P's eps-neighborhood (including P)
程序源代码如下:
import java.util.List; import com.gta.cosine.ElementDict; public class DataNode { private List<ElementDict> terms; private boolean isVisited; private int category; public DataNode(List<ElementDict> terms) { this.terms = terms; this.isVisited = false; this.category = 0; } public void setVisitLabel(boolean isVisited) { this.isVisited = isVisited; } public void setCatagory(int category) { this.category = category; } public boolean getVisitLabel() { return isVisited; } public int getCategory() { return category; } public List<ElementDict> getAllElements() { return terms; } }
import java.util.List; import java.util.ArrayList; import com.gta.cosine.TextCosine; import com.gta.cosine.ElementDict; public class DBScan { private double eps; private int minPts; private TextCosine cosine; private int threshold; private List<DataNode> dataNodes; private int delta; public DBScan() { this.eps = 0.20; this.minPts = 3; this.threshold = 10000; this.cosine = new TextCosine(); this.delta = 0; dataNodes = new ArrayList<DataNode>(); } public DBScan(double eps, int minPts, int threshold) { this.eps = eps; this.minPts = minPts; this.threshold = threshold; this.cosine = new TextCosine(); this.delta = 0; dataNodes = new ArrayList<DataNode>(); } public void setThreshold(int threshold) { this.threshold = threshold; } public int getThreshold() { return threshold; } public double getEps() { return eps; } public int getMinPts() { return minPts; } public List<DataNode> getNeighbors(DataNode p, List<DataNode> nodes) { List<DataNode> neighbors = new ArrayList<DataNode>(); List<ElementDict> vec1 = p.getAllElements(); List<ElementDict> vec2 = null; double countDistance = 0; for (DataNode node : nodes) { vec2 = node.getAllElements(); countDistance = cosine.analysisText(vec1, vec2); if (countDistance >= eps) { neighbors.add(node); } } return neighbors; } public List<DataNode> cluster(List<DataNode> nodes) { int category = 1; for (DataNode node : nodes) { if (!node.getVisitLabel()) { node.setVisitLabel(true); List<DataNode> neighbors = getNeighbors(node, nodes); if (neighbors.size() < minPts) { node.setCatagory(-1); } else { node.setCatagory(category); expandCluster(neighbors, category, nodes); } } category ++; } return nodes; } public void expandCluster(List<DataNode> neighbors, int category, List<DataNode> nodes) { for (DataNode node : neighbors) { if (!node.getVisitLabel()) { node.setVisitLabel(true); List<DataNode> newNeighbors = getNeighbors(node, nodes); if (newNeighbors.size() >= minPts) { expandCluster(newNeighbors, category, nodes); } } if (node.getCategory() <= 0) // not be any of category { node.setCatagory(category); } } } public void showCluster(List<DataNode> nodes) { for (DataNode node : nodes) { List<ElementDict> ed = node.getAllElements(); for (ElementDict e: ed) { System.out.print(e.getTerm() + " "); } System.out.println(); System.out.println("所属类别: "+ node.getCategory()); } } public void addDataNode(String s) { List<ElementDict> ed = cosine.tokenizer(s); DataNode dataNode = new DataNode(ed); dataNodes.add(dataNode); delta ++; } public void analysis() { if (delta >= threshold) { showCluster(cluster(dataNodes)); delta = 0; } } }
关于计算余弦相似度及其源代码,见本系列之文本挖掘之文本相似度判定。本文考虑到随着文本数量的递增,文本聚类结果会分化,即刚开始聚类的文本与后面文本数据相差甚远,本文非常粗略地采用门限值进行限定,本文后续系列联合KMeans和DBSCAN进行解决,若有更好的方法,请联系我。
需要指出:DBSCAN算法对输入参数E和M非常敏感,即参数稍有变化,其聚类结果则会有明显变化。此外,对E和M的选择在实际应用中,需要大规模数据集调优选择(小数据集另当别论),同时对该算法进行改进也是重要研究方向。
作者:志青云集
出处:http://www.cnblogs.com/lyssym
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【志青云集】。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
相关文章推荐
- Jquery 获取 radio选中值
- C# Protobuf-Net 序列化
- [LeetCode] Different Ways to Add Parentheses
- C# Protobuf-Net 序列化
- MYSQL
- Android开发-工具:MAC下Android的Eclipse开发环境的搭建
- HDU HDU 5375 Gray code(二进制和格雷码)
- 常见WEB开发安全漏洞、原因分析及解决之道
- Python学习笔记-Day03 -第二部分(双向队列-deque和单向队列Queue)
- initWithFrame方法的理解
- 开发者账号的申请流程(个人以及公司)
- Oracle CASE WHEN 用法介绍
- jquery datatable 参数
- ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN)
- Android监听程序进入后台
- 自制Https证书并在Spring Boot和Nginx中使用
- Java 读写Properties配置文件(转)
- 关于INPUT 的光标的问题
- iOS 中捕获程序崩溃日志
- 解析字符串中所有的html中img标签的url