数据挖掘入门——分词程序的入门程序

本文介绍了数据挖掘中的分词技术,旨在帮助初学者理解如何让计算机理解句子的语义。讨论了基于词典、统计和语义的分词方法,并重点讲解了基于词典的分词,包括正向、逆向和双向最大匹配以及最佳匹配法则。同时提出了分词过程中遇到的挑战,如时间识别、特定短语处理和知识库构建等问题。
摘要由CSDN通过智能技术生成
早上起来的时候看到CSDN上的

【问底】严澜:数据挖掘入门——分词

感觉这个专题很不错哦,里面讲到了一些简单的分词算法,看完了后用C++将代码重写了一遍,比较简单,但是很有用
下面是博文中提及的一些概念:

数据挖掘的目的——发现数据中价值。这个才是关键,如何发现数据中的价值。那什么是数据呢?比如大家要上网首先需要输入网址,打开网页后会自动判断哪些是图片、哪些是新闻、哪些是用户名称、游戏图标等。大脑可以存储大量的信息,包括文字、声音、视频、图片等,这些同样可以转换成数据存储在电脑。人的大脑可以根据输入自动进行判断,电脑可以通过输入判断吗?

答案是肯定的! 不过需要我们编写程序来判断每一种信息,就拿文字识别来说吧,怎么从一个人在社交网络的言论判断他今天的心情是高兴还是愤怒!比如:“你假如上午没给我吃冰淇淋,我绝对会不happy的。”  信息发布时间为下午2点。对于我们人类一看这个句子就知道他是吃过冰淇淋了,心情肯定不会是愤怒。那计算机怎么知道呢?       

这就是今天的主题,要让计算机理解句子的语义,必须要有个程序,上面的句子和发布时间是输入,输出就是 “高兴”。要得到“高兴”就要建立 “高兴”的规则,可以建一个感情色彩词库,比如高兴(识别词是高兴、happy),愤怒(识别词是愤怒、生气)。这里的识别词就是输入中出现的词语,比如上面的句子中的“happy”就识别出了“高兴”这个感情色彩词。但是光识别出“happy”肯定是不行的,前面的“假如……没……,我……不……”等关键词都需要识别出来,才能完整判断一个句子的意思。为了达到这个效果,就必须要用分词技术了。

分词

我们先人工对上面的句子来进行一下切词,使用斜线分割:“你/假如/上午/没/给/我/吃/冰淇淋/,/我/绝对/会/不/happy/的/。/”。但是程序如何做到自动切分?这个其实中国的前辈们已经做了很多中文分词的研究,常见的分词算法有:

1. 基于词典的分词,需要先预设一个分词词典,比如上面句子切分出来的“假如、上午”这些词先存放在词典,然后把句子切分成单字组合成词语去词典里查找,匹配上了就挑选出来一个词。没有匹配上的就切分成单字。

2. 基于统计的分词,需要先获取大量的文本语料库(比如新闻、微博等),然后统计文本里相邻的字同时出现的次数,次数越多就越可能构成一个词。当达到一定次数时就构成了一个词,即可形成语料概率库。再对上面句子进行单字切分,把字与字结合后在语料概率库里查找对应的概率,如果概率大于一定值就挑选出来形成一个词。这个是大概描述,实际生产环境中还需要对句子的上下文进行结合才能更准确的分词。

3. 基于语义的分词,简而言之就是模拟人类对句子的理解来进行分词。需要先整理出中文语句的句法、语义信息作为知识库,然后结合句子的上下文,对句子进行单字切分后组合成词逐个带入知识库进行识别,识别出来就挑选出一个词。目前还没有特别成熟的基于语义的分词系统。

基于词典的分词

为了让大家快速的了解分词技术,我们采用第一个方式来做测试:基于词典的分词,这种方式简单暴力可以解决百分之七八十的问题。基于词典的分词大概分为以下几种方式:

1. 正向最大匹配 ,沿着我们看到的句子逐字拆分后组合成词语到词典里去匹配,直到匹配不到词语为止。举个实际的例子:“人民大会堂真雄伟”,我们先拆分为单字“人”去词典里去查找,发现有“人”这个词,继续组合句子里的单字组合“人民”去词典里查找,发现有“人民”这个词,以此类推发现到“人民大会堂”,然后会结合“人民大会堂真”去词典里查找没有找到这个词,第一个词“人民大会堂”查找结束。最终分词的结果为:“人民大会堂/真/雄伟”。如下图演示了用正向最大匹配算法识别人民大会堂的过程,“真”,“雄伟”的识别类似。

2. 逆向最大匹配,这个和上面相反,就是倒着推理。比如“沿海南方向”,我们按正向最大匹配来做就会切分成 “沿海/南方/向”,这样就明显不对。采用逆向最大匹配法则来解决这个问题,从句子的最后取得“方向”这两个字查找词典找到“方向”这个词。再加上“南方向”组成三字组合查找词典没有这个词,查找结束,找到“方向”这个词。以此类推,最终分出“沿/海南/方向”。

3. 双向最大匹配,顾名思义就是结合正向最大匹配和逆向最大匹配,最终取其中合理的结果。最早由哈工大王晓龙博士理论化的取最小切分词数,比如“我在中华人民共和国家的院子里看书”,正向最大匹配切分出来为“我/在/中华人民共和国/家/的/院子/里/看书”工8个词语,逆向最大匹配切分出来为“我/在/中华/人民/共/和/国家/的/院子/里/看书”共11个词语。取正向最大匹配切出来的结果就是正确的。但是如果把上面那个例子“沿海南方向”双向切分,都是3个词语,改如何选择?看第4个《最佳匹配法则》。

4. 最佳匹配法则,先准备一堆文本语料库、一个词库,统计词库里的每一个词在语料库里出现的次数记录下来。最后按照词频高的优先选出,比如“沿海南方向”,正向切分为:“沿海/南方/向”,逆向切分为:“沿/海南/方向”。其中“海南”的频度最高,优先取出来。剩下“沿”、“方向”也就正常切分了。是不是这就是基于词典分词的最佳方案?比如数学之美中提到的:“把手抬起来” 和 “这扇门的把手”,可以分为“把”、“手”、“把手”,不管怎么分总有一句话的意思不对。后续再介绍如何通过统计的分词处理这些问题。

下面是C++实现的代码:
#include <iostream>
#include <string>
#include <set>
using namespace std;

/*首先确定数据结构:1 需要一个存储字典的数据结构选择SET,其余则按照最大正向匹配的算法进行匹配
功能函数   构造函数(传入需要匹配的字符串) 
构建字典的函数(向字典库中添加字符串) 
匹配函数(向后最大匹配获得分词结果存入到一个string类型中)*/

class TestPositiveMatch
{
private:
	string InputStr;
	set<string> DictionArry;
	void CreateDiction();
	void WordMatchString();
public:
	string OutputStr;
	TestPositiveMatch(string InputString)
	{
		InputStr = InputString;
	}
	void WordMatch()
	{
		CreateDiction();
		WordMatchString();
	}
};

void TestPositiveMatch::CreateDiction()
{
	DictionArry.insert(" ");
	DictionArry.insert("爱");
	DictionArry.insert("中华");
	DictionArry.insert("中华人民共和国");
}
void TestPositiveMatch::WordMatchString() //<其中注意中文占两个字符!!!
{
	string InputString = InputStr;
	int strLen = InputString.length();
	int j = 0;
	string matchWord = ""; //根据词库里识别出来的词
	int matchPos = 0;      //根据词库里识别出来词后当前句子中的位置
	while (j < strLen)      //从0字符匹配到字符串结束
	{
		int matchPosTemp = 0;   //截取字符串的位置
		int i = 2;
		while (matchPosTemp < strLen) //从当前位置到结束,匹配最大字符串
		{
			matchPosTemp = i+j;
			string keyTemp = InputString.substr(j,i);
			if (DictionArry.count(keyTemp))//判断当前word是否在字典当中
			{
				matchWord = keyTemp;
				matchPos = matchPosTemp;
			}
			i+=2;
		}
		if (matchWord != "")
		{
			j = matchPos;
			OutputStr = OutputStr + matchWord+ " / ";
		}
		else //从当前词开始往后都没有能够匹配上的词,则按照单字切分的原则切分
		{
			OutputStr = OutputStr+InputString.substr(j,2)+" / ";
			j += 2;
		}
		matchWord = "";
	}
}
void main()
{
	TestPositiveMatch testWord("我爱中华人民共和国");
	testWord.WordMatch();
	cout << testWord.OutputStr;
}
如何让程序识别文本中的感情色彩。现在我们先要构建一个感情色彩词库“高兴”,修饰词库“没”、"不”。再完善一下程序:
#include <iostream>
#include <string>
#include <set>
#include <map>
#include <vector>
using namespace std;

/*首先确定数据结构:1 需要一个存储字典的数据结构选择SET,其余则按照最大正向匹配的算法进行匹配
                    2 映射感情色彩的map
					3 修饰词库
					4 修饰词库对应的衡量分数
功能函数   构造函数(传入需要匹配的字符串) 
构建字典的函数(向字典库中添加字符串) 
匹配函数(向后最大匹配获得分词结果存入到一个string类型中)*/

class TestPositiveMatch
{
private:
	string InputStr;
	set<string> sentimentDict;  //<情感词
	set<string> decorativeDict; //<修饰词
	map<string, string> sentimentMap;
	map<string, double> decorativeScoreMap;

	void CreateDiction();
	void WordMatchString();
public:
	string OutputStr;
	string CurrentSentiment;
	TestPositiveMatch(string InputString)
	{
		InputStr = InputString;
	}
	void WordMatch()
	{
		CreateDiction();
		WordMatchString();
	}
};

void TestPositiveMatch::CreateDiction()
{
	sentimentDict.insert("高兴");
	decorativeDict.insert("不");
	decorativeDict.insert("没");

	sentimentMap.insert(make_pair("高兴","happy"));
	decorativeScoreMap.insert(make_pair("不",-0.5));
	decorativeScoreMap.insert(make_pair("没",-0.5));
}
void TestPositiveMatch::WordMatchString() //<其中注意中文占两个字符!!!
{
	string InputString = InputStr;
	int strLen = InputString.length();
	int j = 0;
	string matchSentimentWord = ""; //根据词库里识别出来的情感词
	string matchDecorativeWord = ""; //根据词库里识别出来的修饰词

	string sentiment;    //<判断心情的部分,因为这里只有一种测试情绪,如果不是,可能需要用vector存储后,经过复杂逻辑判断获得
	double totalScore = 1;

	int matchPos = 0;      //根据词库里识别出来词后当前句子中的位置
	while (j < strLen)      //从0字符匹配到字符串结束
	{
		int matchPosTemp = 0;   //截取字符串的位置
		int i = 2;
		while (matchPosTemp < strLen) //从当前位置到结束,匹配最大字符串
		{
			matchPosTemp = i+j;
			string keyTemp = InputString.substr(j,i);
			if (sentimentDict.count(keyTemp))//判断当前word是否在字典当中
			{
				matchSentimentWord = keyTemp;
				matchPos = matchPosTemp;
				sentiment = sentimentMap[keyTemp];
			}
			if (decorativeDict.count(keyTemp))
			{
				matchDecorativeWord = keyTemp;
				matchPos = matchPosTemp;
				totalScore *= decorativeScoreMap[keyTemp];  //<这里是只有单个字符的类型,如果情况较多,还是应该存储后在后面一起计算
			}
			i+=2;
		}
		if (matchSentimentWord != "")
		{
			j = matchPos;
			OutputStr = OutputStr + matchSentimentWord+ " / ";
		}
		if (matchDecorativeWord != "")
		{
			j = matchPos;
			OutputStr = OutputStr + matchDecorativeWord+ " / ";
		}
		else //从当前词开始往后都没有能够匹配上的词,则按照单字切分的原则切分
		{
			OutputStr = OutputStr+InputString.substr(j,2)+" / ";
			j += 2;
		}
		matchSentimentWord = "";
		matchDecorativeWord = "";
	}

	if (totalScore > 0) {
		CurrentSentiment = sentiment;
	} else {
		CurrentSentiment = "不"+sentiment;
	}

	
}
void main()
{
	TestPositiveMatch testWord("你假如上午没给我吃冰淇淋,我绝对会高兴的");
	testWord.WordMatch();
	cout << testWord.OutputStr<<endl;
	cout << testWord.CurrentSentiment << endl;

	TestPositiveMatch testWord2("你假如上午没给我吃冰淇淋,我绝对不会高兴的");
	testWord2.WordMatch();
	cout << testWord2.OutputStr<<endl;
	cout << testWord2.CurrentSentiment << endl;

}


当然现在的程序还是比较初步的,要做到真正的分词,还需要很多工作要做

不过这只是一个开始,抛出几个问题:

  1. 如何让程序识别句子中的时间?比如“上午”、“下午2点”。
  2. 如何处理“把手抬起来” 和 “这扇门的把手”中的“把”与“手”的问题?
  3. 如何构建海量的知识库,让程序从“婴儿”变成“成年人”?
  4. 如何使用有限的存储空间存储海量的知识库?
  5. 如何提高程序在海量知识库中查找定位信息的效率?
  6. 如何识别新词、人名、新鲜事物等未知领域?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值