蛙蛙推荐:蛙蛙教你文本聚类

摘要:文本聚类是搜索引擎和语义web的基本技术,这次本蛙和大家一起学习一下简单的文本聚类算法,可能不能直接用于实际应用中,但对于想学搜索技术的初学者还是有一定入门作用的。这里会用到TF/IDF权重,用余弦夹角计算文本相似度,用方差计算两个数据间欧式距离,用k-means进行数据聚类等数学和统计知识。关于这些概念可以去google,或者参考文本后的参考链接。

思路:计算两篇文档的相似度,最简单的做法就是用提取文档的TF/IDF权重,然后用余弦定理计算两个多维向量的距离。能计算两个文本间的距离后,用标准的k-means算法就可以实现文本聚类了。

测试:首先我们准备以下数据
===================
奥运 拳击 入场券 基本 分罄 邹市明 夺冠 对手 浮出 水面
股民 要 清楚 自己 的 目的
印花税 之 股民 四季
杭州 股民 放 鞭炮 庆祝 印花税 下调
残疾 女 青年 入围 奥运 游泳 比赛 创 奥运 历史 两 项 第一
介绍 一 个 ASP.net MVC 系列 教程
在 asp.net 中 实现 观察者 模式 ,或 有 更 好 的 方法 (续)
输 大钱 的 股民 给 我们 启迪
Asp.Net 页面 执行 流程 分析
运动员 行李 将 “后 上 先 下” 奥运 相关 人员 行李 实名制
asp.net 控件 开发 显示 控件 内容
奥运 票务 网上 成功 订票 后 应 及时 到 银行 代售 网点 付款
某 心理 健康 站 开张 后 首 个 咨询 者 是 位 新 股民
ASP.NET 自定义 控件 复杂 属性 声明 持久性 浅析
==================
很明显以上数据可以分为三类:asp.net,奥运和股民,我们就写程序来实现它,各种算法的原理网上都有,我就大概只贴代码,声明一下,部分代码是从网上直接抄的,k-means代码是我从一篇文章的java示例代码转换过来的,我给代码加了不少注释,希望能帮助大家理解。

以下是入口函数

static   void  Main( string [] args)
{
    //1、获取文档输入
    string[] docs = getInputDocs("input.txt");
    if (docs.Length < 1)
    {
        Console.WriteLine("没有文档输入");
        Console.Read();
        return;
    }

    //2、初始化TFIDF测量器,用来生产每个文档的TFIDF权重
    TFIDFMeasure tf = new TFIDFMeasure(docs, new Tokeniser());

    int K = 3; //聚成3个聚类

    //3、生成k-means的输入数据,是一个联合数组,第一维表示文档个数,
    //第二维表示所有文档分出来的所有词
    double[][] data = new double[docs.Length][];
    int docCount = docs.Length; //文档个数
    int dimension = tf.NumTerms;//所有词的数目
    for (int i = 0; i < docCount; i++)
    {
        for (int j = 0; j < dimension; j++)
        {
            data[i] = tf.GetTermVector2(i); //获取第i个文档的TFIDF权重向量
        }
    }

    //4、初始化k-means算法,第一个参数表示输入数据,第二个参数表示要聚成几个类
    WawaKMeans kmeans = new WawaKMeans(data, K);
    //5、开始迭代
    kmeans.Start();

    //6、获取聚类结果并输出
    WawaCluster[] clusters = kmeans.Clusters;
    foreach (WawaCluster cluster in clusters)
    {
        List<int> members = cluster.CurrentMembership;
        Console.WriteLine("-----------------");
        foreach (int i in members)
        {
            Console.WriteLine(docs[i]);
        }

    }
    Console.Read();
}

以下是分词器的主要代码

/**/ /// <summary>
/// 以空白字符进行简单分词,并忽略大小写,
/// 实际情况中可以用其它中文分词算法
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public  IList < string >  Partition( string  input)
{
 Regex r=new Regex("([ //t{}():;. /n])");  
 input=input.ToLower() ;

 String [] tokens=r.Split(input);          

 List<string> filter=new  List<string>() ;

 for (int i=0; i < tokens.Length ; i++)
 {
  MatchCollection mc=r.Matches(tokens[i]);
  if (mc.Count <= 0 && tokens[i].Trim().Length > 0       
   && !StopWordsHandler.IsStopword (tokens[i]) )        
   filter.Add(tokens[i]) ;
        }
 
 return filter.ToArray();
}

以下是kmeans算法的基本代码

public   class  WawaKMeans
{
    /**//// <summary>
    /// 数据的数量
    /// </summary>
    readonly int _coordCount;
    /**//// <summary>
    /// 原始数据
    /// </summary>
    readonly double[][] _coordinates;
    /**//// <summary>
    /// 聚类的数量
    /// </summary>
    readonly int _k;
    /**//// <summary>
    /// 聚类
    /// </summary>
    private readonly WawaCluster[] _clusters;

    internal WawaCluster[] Clusters
    {
        get { return _clusters; }
    } 

    /**//// <summary>
    /// 定义一个变量用于记录和跟踪每个资料点属于哪个群聚类
    /// _clusterAssignments[j]=i;// 表示第 j 个资料点对象属于第 i 个群聚类
    /// </summary>
    readonly int[] _clusterAssignments;
    /**//// <summary>
    /// 定义一个变量用于记录和跟踪每个资料点离聚类最近
    /// </summary>
    private readonly int[] _nearestCluster;
    /**//// <summary>
    /// 定义一个变量,来表示资料点到中心点的距离,
    /// 其中—_distanceCache[i][j]表示第i个资料点到第j个群聚对象中心点的距离;
    /// </summary>
    private readonly double[,] _distanceCache;
    /**//// <summary>
    /// 用来初始化的随机种子
    /// </summary>
    private static readonly Random _rnd = new Random(1);

    public WawaKMeans(double[][] data, int K)
    {
        _coordinates = data;
        _coordCount = data.Length;
        _k = K;
        _clusters = new WawaCluster[K];
        _clusterAssignments = new int[_coordCount];
        _nearestCluster = new int[_coordCount];
        _distanceCache = new double[_coordCount,data.Length];
        InitRandom();
    }

    public void Start()
    {
        int iter = 0;
        while (true)
        {
            Console.WriteLine("Iteration " + (iter++) + "");
            //1、重新计算每个聚类的均值
            for (int i = 0; i < _k; i++)
            {
                _clusters[i].UpdateMean(_coordinates);
            }

            //2、计算每个数据和每个聚类中心的距离
            for (int i = 0; i < _coordCount; i++)
            {
                for (int j = 0; j < _k; j++)
                {
                    double dist = getDistance(_coordinates[i], _clusters[j].Mean);
                    _distanceCache[i,j] = dist;
                }
            }

            //3、计算每个数据离哪个聚类最近
            for (int i = 0; i < _coordCount; i++)
            {
                _nearestCluster[i] = nearestCluster(i);
            }

            //4、比较每个数据最近的聚类是否就是它所属的聚类
            //如果全相等表示所有的点已经是最佳距离了,直接返回;
            int k = 0;
            for (int i = 0; i < _coordCount; i++)
            {
                if (_nearestCluster[i] == _clusterAssignments[i])
                    k++;

            }
            if (k == _coordCount)
                break;

            //5、否则需要重新调整资料点和群聚类的关系,调整完毕后再重新开始循环;
            //需要修改每个聚类的成员和表示某个数据属于哪个聚类的变量
            for (int j = 0; j < _k; j++)
            {
                _clusters[j].CurrentMembership.Clear();
            }
            for (int i = 0; i < _coordCount; i++)
            {
                _clusters[_nearestCluster[i]].CurrentMembership.Add(i);
                _clusterAssignments[i] = _nearestCluster[i];
            }
            
        }

    }

    /**//// <summary>
    /// 计算某个数据离哪个聚类最近
    /// </summary>
    /// <param name="ndx"></param>
    /// <returns></returns>
    int nearestCluster(int ndx)
    {
        int nearest = -1;
        double min = Double.MaxValue;
        for (int c = 0; c < _k; c++)
        {
            double d = _distanceCache[ndx,c];
            if (d < min)
            {
                min = d;
                nearest = c;
            }
      
        }
        if(nearest==-1)
        {
            ;
        }
        return nearest;
    }
    /**//// <summary>
    /// 计算某数据离某聚类中心的距离
    /// </summary>
    /// <param name="coord"></param>
    /// <param name="center"></param>
    /// <returns></returns>
    static double getDistance(double[] coord, double[] center)
    {
        //int len = coord.Length;
        //double sumSquared = 0.0;
        //for (int i = 0; i < len; i++)
        //{
        //    double v = coord[i] - center[i];
        //    sumSquared += v * v; //平方差
        //}
        //return Math.Sqrt(sumSquared);

        //也可以用余弦夹角来计算某数据离某聚类中心的距离
        return 1- TermVector.ComputeCosineSimilarity(coord, center);

    } 
    /**//// <summary>
    /// 随机初始化k个聚类
    /// </summary>
    private void InitRandom()
    {
        for (int i = 0; i < _k; i++)
        {
            int temp = _rnd.Next(_coordCount);
            _clusterAssignments[temp] = i; //记录第temp个资料属于第i个聚类
            _clusters[i] = new WawaCluster(temp,_coordinates[temp]);
        }
    }
}

以下是聚类实体类的定义

internal   class  WawaCluster
{
    public WawaCluster(int dataindex,double[] data)
    {
        CurrentMembership.Add(dataindex);
        Mean = data;
    }

    /**//// <summary>
    /// 该聚类的数据成员索引
    /// </summary>
    internal List<int> CurrentMembership = new List<int>();
    /**//// <summary>
    /// 该聚类的中心
    /// </summary>
    internal double[] Mean;
    /**//// <summary>
    /// 该方法计算聚类对象的均值 
    /// </summary>
    /// <param name="coordinates"></param>
    public void UpdateMean(double[][] coordinates)
    {
        // 根据 mCurrentMembership 取得原始资料点对象 coord ,该对象是 coordinates 的一个子集;
        //然后取出该子集的均值;取均值的算法很简单,可以把 coordinates 想象成一个 m*n 的距阵 ,
        //每个均值就是每个纵向列的取和平均值 , //该值保存在 mCenter 中

        for (int i = 0; i < CurrentMembership.Count; i++)
        {
            double[] coord = coordinates[CurrentMembership[i]];
            for (int j = 0; j < coord.Length; j++)
            {
                Mean[j] += coord[j]; // 得到每个纵向列的和;
            }
            for (int k = 0; k < Mean.Length; k++)
            {
                Mean[k] /= coord.Length; // 对每个纵向列取平均值
            }
        }
    }
}

计算TF/IDF和利用余弦定理计算相似度的代码见完整版的代码下载,那两部分都是外国人写的,里面有它的联系方式,不懂的可以问他,反正我差不多懂了。

下面看看咱们的测试结果:
Iteration 0...
Iteration 1...
Iteration 2...
-----------------
奥运 拳击 入场券 基本 分罄 邹市明 夺冠 对手 浮出 水面
杭州 股民 放 鞭炮 庆祝 印花税 下调
残疾 女 青年 入围 奥运 游泳 比赛 创 奥运 历史 两 项 第一
运动员 行李 将 “后 上 先 下” 奥运 相关 人员 行李 实名制
奥运 票务 网上 成功 订票 后 应 及时 到 银行 代售 网点 付款
-----------------
股民 要 清楚 自己 的 目的
印花税 之 股民 四季
输 大钱 的 股民 给 我们 启迪
某 心理 健康 站 开张 后 首 个 咨询 者 是 位 新 股民
-----------------
介绍 一 个 ASP.net MVC 系列 教程
在 asp.net 中 实现 观察者 模式 ,或 有 更 好 的 方法 (续)
Asp.Net 页面 执行 流程 分析
asp.net 控件 开发 显示 控件 内容
ASP.NET 自定义 控件 复杂 属性 声明 持久性 浅析聚类聚的非常准确,而且只迭代了3次,模型就收敛了,当然了这是最理想的效果,其实聚类的结果受好多种因素制约,提取特征的算法,随机初始化函数,kmeans算法的实现等,都有优化的地方,不信你把输入的数据的顺序改改,聚类结果就不一样了,或者把随机数的种子变一下,结果也不一样,k-means算法加入一些变异系数的调整,结果也不一样,提取特征的地方不用TF/IDF权重算法用别的,结果肯定也不一样。
完整代码里还有另一组测试数据,结果也很不错,我的意思是我的算法不是针对一组测试数据,而是针对好多数据都有不错的结果。

总结:数学和英语真是写程序之根本呀,弄这个东西遇到了好多英语单词不会,查还查不出来,也理解不了,最后google一看,是个数学专用词,再搜索这个数学专用词的中文解释,发现还是理解不了那数学原理。所以还是得多学习数学和英语。

参考链接:
K-MEANS算法
http://beauty9235.javaeye.com/blog/161675
什么是变异系数
http://zhidao.baidu.com/question/15013015.html
TF/IDF实现
http://www.codeproject.com/KB/cs/tfidf.aspx

源码下载:WawaTextCluster.zip

文本聚类算法Java实现相关推荐

  1. 六种常用的文本聚类算法介绍

    文本聚类算法介绍 分类和聚类都是文本挖掘中常使用的方法,他们的目的都是将相似度高的对象归类,不同点在于分类是采用监督学习,分类算法按照已经定义好的类别来识别一篇文本,而聚类是将若干文本进行相似度比较, ...

  2. 机器学习-无监督学习-聚类:聚类方法(二)--- 基于密度的聚类算法【DBSCAN文本聚类算法,密度最大值文本聚类算法】

    密度聚类方法的指导思想是,只要样本点的密度大于某阀值,则将该样本添加到最近的簇中. 基于密度的聚类算法假设聚类结构能够通过样本分布的紧密程度确定,以数据集在空间分布上的稠密程度为依据进行聚类,即只要一 ...

  3. 基于神经网络语言模型的中文新闻文本聚类算法

    一.新闻文本集  其中  通过TF-IDF排序 中的词(由大到小),选择其中的 t 个词作为关键字,,是对应关键字的TF-IDF值. 二.神经网络语言模型 输入:该词的上下文中相邻的几个词向量(词袋模 ...

  4. 手动实现简单的 文本聚类算法 词袋模型+consin相似度+UPGMA聚类

    全手写文本聚类算法 词袋模型-->consin相似度-->UPGMA聚类 punctunation = [",", ":", ".&quo ...

  5. 基于K-Means的文本聚类算法

    源代码下载:TDIDF_Demo.rar        声明:本文代码思路完全来自蛙蛙池塘的博客,只为技术交流用途,无其他目的 昨天有幸拜读了蛙蛙池塘的<蛙蛙推荐:蛙蛙教你文本聚类>这篇文 ...

  6. K-Means聚类算法Java实现

    K-Means聚类算法 目的:将数据分为K组 基本思路 随机选取K个对象作为初始的聚类中心 计算每个对象与各个聚类中心之间的距离,将每个对象分配给距离它最近的聚类中心 将属于同一类的对象求均值,将这个 ...

  7. 基本k-mean聚类的文本聚类算法原理和例子

    基于质心的划分方法是研究最多的算法,包括k-mean聚类算法及其各种变体,这些变体依据初始簇的选择,对象的划分.相识度的计算方法.簇中心的计算方法不同而不同.基于质心的划分方法将簇中所有对象的平均值看 ...

  8. [python] Kmeans文本聚类算法+PAC降维+Matplotlib显示聚类图像

    0 前言 本文主要讲述以下几点:         1.通过scikit-learn计算文本内容的tfidf并构造N*M矩阵(N个文档 M个特征词):         2.调用scikit-learn中 ...

  9. python 文本聚类算法

    三体下载 将下载的文件重命名为santi.txt,放在文件的目录下 #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ ...

最新文章

  1. 求数组最大数,该数为数组中某两个数相加
  2. c语言 将url图片存到本地_一个22万张NSFW图片的鉴黄数据集?我有个大胆的想法……...
  3. ASP.net:Regex.Match 方法 中应该注意的几个问题
  4. MySQL的limit用法和分页查询的性能分析及优化
  5. Java算法-符号~
  6. 如何HttpWebRequest模拟登陆,获取服务端返回Cookie以便登录请求后使用
  7. System.out.println(Runtime.getRuntime().availableProcessors());获取cpu核数
  8. 通过Rancher部署并扩容Kubernetes集群基础篇一
  9. ORACLE日期时间函数大全
  10. MATLAB几何均值滤波
  11. C++之常用集合(总结)
  12. 淘宝API接口:item_search - 按关键字搜索淘宝商品
  13. 股票大作手操盘术---择时
  14. PostgreSQL 生成随机数字、字符串、日期、验证码以及 UUID
  15. 解决谷歌浏览器跨域问题
  16. 微信支付2.0版本,更换参数即可使用
  17. 五分钟实现 pdf 分页
  18. 微信公众号会替代手机APP吗?
  19. 云办公、智能办公时代,微软还能一枝独秀吗?(下)
  20. `Computer-Algorithm` 最小生成树MST,Prim,Kruskal,次小生成树

热门文章

  1. 国际短信系统平台后台功能详解-移讯云短信软件
  2. 微信小程序入门教程+案例demo
  3. Xiaomi Redmi Note7刷机
  4. 2020年自考计算机应用基础和实践是什么,2020年自考计算机应用基础复习重点8
  5. DB2数据库的简单使用
  6. [PowerBI]DAX虚拟连接函数:TREATAS()用法介绍
  7. 在一个公司死磕了5-10年的程序员,最后都哪里去了?
  8. Office 2.0是什么?
  9. 【67】PCIe Power Management和linux对PME的处理
  10. Jumpserver堡垒机部署(完整过程)