2008年11月20日
摘要: 在上一篇文章中对复旦语料库进行分词,去停止词,去无用词性的词的基础上,再进行一次根据DF的处理,去除所有文档频率小于等于3的词,得到的对比结果如下
阅读全文
2008年11月13日
经过词频统计,看到复旦大学中文语料库的总词数为116558个(而且还是去掉了停止词及代词,介词,数词和时间短语等无关内容之后的结果),数量十分巨大.
而各个类别的词汇数量分别为:
类别名称:Agriculture 总文档数:1949 总词数:29163
类别名称:Art 总文档数:1237 总词数:40816
类别名称:Communication 总文档数:52 总词数:2283
类别名称:Computer 总文档数:2591 总词数:19340
类别名称:Economy 总文档数:2912 总词数:37021
类别名称:Education 总文档数:111 总词数:5719
类别名称:Electronics 总文档数:51 总词数:2693
类别名称:Energy 总文档数:63 总词数:2848
类别名称:Environment 总文档数:2347 总词数:25155
类别名称:History 总文档数:708 总词数:47205
类别名称:Law 总文档数:103 总词数:3834
类别名称:Literature 总文档数:65 总词数:5844
类别名称:Medical 总文档数:98 总词数:3877
类别名称:Military 总文档数:147 总词数:4615
类别名称:Mine 总文档数:63 总词数:3708
类别名称:Philosophy 总文档数:86 总词数:5190
类别名称:Politics 总文档数:1920 总词数:35292
类别名称:Space 总文档数:1226 总词数:14557
类别名称:Sports 总文档数:2344 总词数:42665
类别名称:Transport 总文档数:112 总词数:4644
很容易看出词汇的数量基本与类别包含的文档数成正比,但也有一些极其特殊的类别,比如艺术(Art)和历史(History),其文档数量仅有计算机文章数量的一半,但包含的词汇量却是计算机类别的两倍以上(分别是40816:19340和47205:19340,尤以历史类文章为甚,其文档数量仅有计算机类的三分之一还不到)。直观上的想法是,历史和艺术类文章包含了大量的人名,地名或者事件名等专有名词,因而词汇数量上表现得很巨大。计算机类文章包含词汇较少,一是因为其为新兴学科,包含的内容本就较少,另一个更重要的原因则在于前期对文章的处理忽略了所有的英文单词及缩写,而这些内容在计算机相关的文章中所占比重很大。
如果我们看整个语料库出现次数最多的十个词,会发现他们大致也是我们的国计民生所关注的几个方面(巧合?未必!)它们是:
词内容:经济 词性:名词 词频:233906 文档频率:8975
词内容:发展 词性:动词 词频:189181 文档频率:11847
词内容:农业 词性:名词 词频:126603 文档频率:4105
词内容:社会 词性:名词 词频:108988 文档频率:8686
词内容:政治 词性:名词 词频:106847 文档频率:4971
词内容:大 词性:形容词 词频:106111 文档频率:14729
词内容:中国 词性:名词 词频:105269 文档频率:10885
词内容:人 词性:名词 词频:98034 文档频率:11037
词内容:问题 词性:名词 词频:94458 文档频率:12538
词内容:个 词性:量词 词频:91717 文档频率:14428
通过与某些类别中排名前十位的词对比,我们可以看出很多问题,例如计算机类别:
词内容:系统 词性:形容词 词频:45496 文档频率:2244
词内容:控制 词性:动词 词频:21937 文档频率:1734
词内容:图 词性:名词 词频:20396 文档频率:1914
词内容:方法 词性:名词 词频:20073 文档频率:2141
词内容:个 词性:量词 词频:19661 文档频率:2207
词内容:算法 词性:名词 词频:18879 文档频率:1336
词内容:数据 词性:名词 词频:17691 文档频率:1357
词内容:模型 词性:名词 词频:17182 文档频率:1423
词内容:网络 词性:名词 词频:16980 文档频率:1159
词内容:进行 词性:动词 词频:16406 文档频率:2094
词内容:问题 词性:名词 词频:14617 文档频率:1965
再比如交通类别:
词内容:铁路 词性:名词 词频:280 文档频率:51
词内容:运输 词性:动词 词频:205 文档频率:74
词内容:交通 词性:名词 词频:158 文档频率:54
词内容:大 词性:形容词 词频:147 文档频率:59
词内容:工程 词性:名词 词频:136 文档频率:31
词内容:个 词性:量词 词频:117 文档频率:51
词内容:年 词性:量词 词频:114 文档频率:52
词内容:建设 词性:动词 词频:108 文档频率:40
词内容:公路 词性:名词 词频:106 文档频率:34
词内容:条 词性:量词 词频:105 文档频率:38
我们会发现,
第一:整个语料库出现最多的词未必在各个类别中也最多,实际上通过计算机和交通类别可以看出,几乎完全不同!这意味着在进行文本分类的训练阶段,针对各个类取不同的特征集合(即所谓local的特征选择)很有必要,如果所有的类别都使用相同的特征集合(而且毫无悬念的,这个特征集合就是语料库的特征集合),那么分类效果会因为没有为各个类找到最佳的特征而大打折扣;
第二,注意到“个”这个词出现在所有类别排名靠前的词汇中间,但直觉告诉我们,这个词很难对分类产生什么贡献(行话叫区分度很差)。此结论与信息论中所说的“一个词分布越广越均匀,则区分度越差”是一个意思。当然,在这里“个”会如明星般的出现在所有类别靠前的位置上,完全是因为我们的排名是根据词频来统计的(根据文档频率排序也会产生相似的结果),而使用像开方检验,信息增益这样的特征选择算法,就是为了避免这种区分度差的词出现在最终的特征集合中,从而影响分类效果。
在后续的文章里,我还会给出使用了开方检验计算特征得分以后的排名情况,“个”这个词会不会从前十名中消失呢?又有哪些词会冲上头排呢?我们拭目以待。(音乐响,幕布缓慢拉上,灯光渐暗)
2008年11月9日
复旦大学的中文语料库分为训练集和验证集两部分,两部分的文档数量基本相等,但现在做测评一般都不采用这种预先划分的方法,而多用交叉验证,因此在将训练集与验证集合并之后,得到该语料库的一些基本信息如下:
类别总数量:20
文档总数量:19637
类别名称(类别代码):文档数量
Agriculture(C32):2043篇
Art(C3):1482篇
Communication(C17):52篇
Computer(C19):2715篇
Economy(C34):3201篇
Education(C5):120篇
Electronics(C16):55篇
Energy(C15):65篇
Enviornment(C31):2435篇
History(C7):934篇
Law(C35):103篇
Literature(C4):67篇
Medical(C36):104篇
Military(C37):150篇
Mine(C23):67篇
Philosophy(C6):89篇
Politics(C38):2050篇
Space(C11):1282篇
Sports(C39):2507篇
Transport(C29):116篇
同时,在使用ictclas4j分词包对其进行分词的过程中,发现复旦语料库中存在一些文章会使得ictclas4j报错,其中因为分词包本身字库缺少某些文字,以及一些神秘的字符组合(确实很神秘)会导致分词过程出错,因此能够被成功分词而供后续使用的文档数并不如上面所列这么多,在分词之后,情况如下:
类别总数量:20
文档总数量:18185
类别名称(类别代码):文档数量
Agriculture(C32):1949篇
Art(C3):1237篇
Communication(C17):52篇
Computer(C19):2591篇
Economy(C34):2912篇
Education(C5):111篇
Electronics(C16):51篇
Energy(C15):63篇
Environment(C31):2347篇
History(C7):708篇
Law(C35):103篇
Literature(C4):65篇
Medical(C36):98篇
Military(C37):147篇
Mine(C23):63篇
Philosophy(C6):86篇
Politics(C38):1920篇
Space(C11):1226篇
Sports(C39):2344篇
Transport(C29):112篇
在已分词后的语料库里,可以看出这样几个特点,一,文档总数比未分词的版本少了1448篇(可见ictclas4j的错误还是满普遍的);第二,文档数量分布仍不均衡,最多的经济类文章有2912篇,而最少的电子类与通信类文章仅有51篇与52篇,往好的方向说可以考察你所开发的系统如何应对数据集偏斜的问题,往坏的方向说给要上线的系统作训练集恐怕不太合适。
在下一篇文章中,我将进一步总结词频统计的结果.
2008年10月27日
假设我们有以下的类别层次:
Layer1 --> Layer2 --> Layer3 --> Layer4
其中Layer1是位于最高位置的基类,Layer2是Layer1的直接子类,而Layer3又是Layer2的直接子类,等等.
我们在使用数组时会有这样的用法:
Layer1[] layerArray=new Layer2[10];
此时尽管运行时可能产生某些错误,例如往一个明明是Layer2的数组中加入一个Layer1的实例,但编译期并不会有什么问题.这是因为Java的数组存在称之为”协变”的现象,即如果Layer2是Layer1的子类,那么Layer2的数组也是Layer1的数组的子类.我们可以这样来验证:
Layer2[] layer2Array=new Layer2[10];
System.out.println(layer2Array
instanceof Layer1[]);
程序输出的结果为true.
像Layer1[] layerArray=new Layer2[10];这样写有一个很大的缺点,那就是你可以写
layerArray[0]=new Layer1();
这样的代码,编译器不会报错,但实际上我们往一个Layer2的数组里塞了一个Layer1的实例,这在运行时会扔出一个java.lang.ArrayStoreException。即是说数组没有提供编译期的类型检查。
而容器(例如List)很容易被我们认为是另一种数组,只是能够容纳各种类型以及动态改变大小而已。在JavaSE5之前,即使这样认为也不会给编程带来麻烦,因为容器没有办法指定所容纳的具体类型。但在引入泛型之后,我们可能为了编译期的类型检查以及动态改变大小两个理由而迫不及待的用容器彻底代替数组,也就有可能想当然的写出这样的代码:
List<Layer1> list=new ArrayList<Layer2>();
这样写的理由很简单,总是希望用基类型的引用来提供灵活性。然而遗憾的是,上述代码编译不能通过(或许编译期就不能通过这一点,反而是好事),原因就在于容器没有“协变”现象,一个Layer2的List并不是Layer1的List的子类,而是完全不同的类。因此上面的代码同
Integer i=new
String("你好");
一样荒谬,自然逃不过编译器的法眼。
难道就不能用泛型创建这样一种List的引用,类型参数只使用基类型,而在实例化的时候可以使用子类型,但又可以借助泛型的编译期检查么?
例如我想要一个List<Layer1>类型的引用,这个引用可以指向ArrayList<Layer2>或ArrayList<Layer4>(因为我不想关心实例化时的具体类型),但是一旦实例化一个ArrayList<Layer2>以后,又能借助编译器来检查向其中添加的确实是Layer2的实例,而非String或Integer。这能不能办到呢?
答案是不能,虽然初试之下,我们可能会利用泛型通配符想当然的写出如下代码:
List<? extends
Layer1> layer1List=new
ArrayList<Layer2>();
乍看之下,代码的本意是要建立一个泛型List的引用,而泛型的类别参数是任何Layer1的子类均可,这不正好合我们刚才所说的意思吗?而且这回编译器也没有报错,应该OK了吧!
接下来的事情却让人哭笑不得,这个List竟然不能添加任何元素,无论是写
layer1List.add(new Layer1());
还是写
layer1List.add(new Layer2());
均报错(编译期即错,不用待到运行时)。甚至是
layer1List.add(new Object());
也报错(这证明了无论提供的类型参数是Layer1的子类还是超类,亦或者Layer1本身,均报错)。
这是为什么呢?(给读者3分钟思考,然后带着诡异的微笑揭晓答案)
原来extends关键字圈定的是泛型参数的上界,回头单看
List<? extends
Layer1>
这一句,其实说的是,我有一个泛型的List,它的类别参数是Layer1的一个子类,因此如果这个子类是Layer3,那么Layer2以上的类别就不能向该List中添加(这个没什么问题吧,别绕进去了哦),如果这个子类别是Layer4,那么Layer3以上的类别就不能向该List中添加(想象我们的类别体系还存在Layer5,Layer6等等,显然子类别是哪一个都有可能,那就是说拒绝任何一个类别都是说得通的)。因此编译器根本无法确定这个List到底可以放什么不可以放什么,所以统统拒绝。
更正式一点说,extends给了一个上界,只有该界以下的类别才能合法的添加到List中,但是这个上界本身都是不确定的(它可以无限往类别体系的下方移动),自然也就说不出哪些类别是合法的了。
结论:想要容器提供类似于数组那样的协变效果,而又要有类型检查,至少在我所知范围,办不到。
2008年10月6日
自从有了范型,Java的容器操作便利了不少,但因为还存在int,float这里原始数据类型而磨合得还不够好.
例如下面的这个小例子:
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("1",1);
System.out.println(map.get("2"));
实际上map中并没有键为"2"的值,不过代码运行正常,输出为
null
现在来做一点小改动,
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("1",1);
int i=map.get("2");
System.out.println(i);
注意到只是用中间变量i暂时存放了一下取出的值,这个时候就会报错啦:
Exception in thread "main" java.lang.NullPointerException
仔细想想倒也觉得错得在理,因为不存在的对象可以以null来表示,但不存在的数字在Java中却没有对应的表示(例如Ruby中就有NAN,表示这不是一个数字)。乍看之下好像也没什么大不了,但是这样的小缺陷使得在Java编程中想像一般类型一样的来使用数字和容器变得不太可能,如果用一个容器来做数字的存取,则只能在取之前很小心的先查看使用的键值对是否已经在容器中,而不能像一般对象的存取那样,直接取出,通过结果来判断罢了。
发现这个小纰漏仅在偶然间,JDK的文档我看得不多,也许SUN的工程师早就在哪里提醒过大家了吧,只是我孤陋寡闻而已,大家看着玩玩。
2008年9月21日
ICTCLAS是中科院计算所出品的中文分词程序包,在国内一直有着良好的口碑和很高的使用率。之前一直只有 C++的版本提供,而现在C#,Delphi和Java版本已经纷纷出炉。下面用一个极小的例子,让大家10分钟之内就能用上ICTCLAS ,从此也开始自己的文本分类和搜索引擎开发之路。
需要首先说明的是,不同于以前的C++版提供的JNI调用,本次使用的是纯Java版本的ICTCLAS,下载地址在http://ictclas.org/Down_OpenSrc.asp。
好,假设你已经下载了我们需要使用的Java版本ictclas4j,现在把它解压缩,然后把Data文件夹整个拷贝到Eclipse项目的文件夹下,而bin目录下的org文件夹整个拷贝到你Eclipse项目的bin目录下,把src目录下的org文件夹整个拷贝到Eclipse项目的src目录下(最简单快捷的使用方式,或者你自己打成jar包,这样无论放到哪里,都可以在build
path里面导入这个jar包啦)。
现在就可以在你的项目里新建一个类来试试。我新建了一个类,代码如下:
import
org.ictclas4j.bean.SegResult;
import
org.ictclas4j.segment.SegTag;
publicclass OneMain {
publicstaticvoid main(String[] args) {
System.out.println("This is OneMain");
SegTag
st = new SegTag(1);
SegResult
sr = st
.split("一块勤奋地漂亮的一块钱,/打造经济的航空母舰。ABCD.#$% Hello World!\n又一段文本123辆 !3.0");
System.out.println(sr.getFinalResult());
}
}
很显然文本“一块勤奋地漂亮的一块钱,/打造经济的航空母舰。ABCD.#$%
Hello World!"n又一段文本123辆 !3.0”就是我们用来测试的文本,其中包含了中文,英文,标点符号,乱七八糟符号(笑)及阿拉伯数字。
我们运行刚才的程序,看下输出结果:
This is
OneMain
一块/s 勤奋/a 地/u 漂亮/a 的/u 一/m 块/q 钱/n
,/w //nx 打造/v 经济/n 的/u 航空母舰/n 。/w
ABCD.#$%/nx Hello/nx World/nx !/w 又/d 一/m 段/q 文本/n
123/m 辆/q
看到了么,分词的结果是一个长长的String类数据,用空格区分出每个词,每个词还用/后面的英文标号标出了词性。一起来看看几个有趣的地方。
原文中其实有两个“一块”,一处是“一块勤奋”,这里很正确的识别为了副词,而后面的“一块钱”中的“一块”也正确的识别为数量词。
阿拉伯数字正确识别为数词,包括小数形式的“3.0”。而英文和乱七八糟符号(包括那个不可见的换行符,你找到它在哪了吗?)则都被划为一类——/nx!(因为我也不知道ICTCLAS内部人员管它叫什么啦,非法字符啊,还是无效字符啊,或者其它字符啊,名字可以自己取嘛)
测试文本中还有两个叹号,一个是英文半角的!,一个是中文全角的!,两者也都被正确识别为标点符号,但英文的句号“.“就被认为是/nx啦。
测试文本中的空格被完全忽略。
好,十分简单对不对?去玩玩吧。
2008年9月12日
在系列文章《SVM入门(三)》中论述间隔的重要性之时,由于疏忽和大意有一处不明确和一处错误。
于该部分我曾经提到过
“之所以如此关心间隔这个东西,是因为间隔与样本的误分次数间存在关系:
”
此处更确切的说法应该是几何间隔与样本的误分次数存在上述关系。
就在刚才这段话的下面,我解释R代表的含义时说它是“R是空间中一个能完全包含样本数据的球的半径”,很遗憾这是错误的,正确的定义是
R=max ||xi|| i=1,...,n
即R是所有样本中(xi是以向量表示的第i个样本)向量长度最长的值。
犯下上述错误,委实显得不专业,在此道歉。
2008年8月31日
前文提到过,除了分类算法以外,为分类文本作处理的特征提取算法也对最终效果有巨大影响,而特征提取算法又分为特征选择和特征抽取两大类,其中特征选择算法有互信息,文档频率,信息增益,开方检验等等十数种,这次先介绍特征选择算法中效果比较好的开方检验方法。
大家应该还记得,开方检验其实是数理统计中一种常用的检验两个变量独立性的方法。(什么?你是文史类专业的学生,没有学过数理统计?那你做什么文本分类?在这捣什么乱?)
开方检验最基本的思想就是通过观察实际值与理论值的偏差来确定理论的正确与否。具体做的时候常常先假设两个变量确实是独立的(行话就叫做“原假设”),然后观察实际值(也可以叫做观察值)与理论值(这个理论值是指“如果两者确实独立”的情况下应该有的值)的偏差程度,如果偏差足够小,我们就认为误差是很自然的样本误差,是测量手段不够精确导致或者偶然发生的,两者确确实实是独立的,此时就接受原假设;如果偏差大到一定程度,使得这样的误差不太可能是偶然产生或者测量不精确所致,我们就认为两者实际上是相关的,即否定原假设,而接受备择假设。
那么用什么来衡量偏差程度呢?假设理论值为E(这也是数学期望的符号哦),实际值为x,如果仅仅使用所有样本的观察值与理论值的差值x-E之和
来衡量,单个的观察值还好说,当有多个观察值x1,x2,x3的时候,很可能x1-E,x2-E,x3-E的值有正有负,因而互相抵消,使得最终的结果看上好像偏差为0,但实际上每个都有偏差,而且都还不小!此时很直接的想法便是使用方差代替均值,这样就解决了正负抵消的问题,即使用
这时又引来了新的问题,对于500的均值来说,相差5其实是很小的(相差1%),而对20的均值来说,5相当于25%的差异,这是使用方差也无法体现的。因此应该考虑改进上面的式子,让均值的大小不影响我们对差异程度的判断
式(1)
上面这个式子已经相当好了。实际上这个式子就是开方检验使用的差值衡量公式。当提供了数个样本的观察值x1,x2,……xi ,……xn之后,代入到式(1)中就可以求得开方值,用这个值与事先设定的阈值比较,如果大于阈值(即偏差很大),就认为原假设不成立,反之则认为原假设成立。
在文本分类问题的特征选择阶段,我们主要关心一个词t(一个随机变量)与一个类别c(另一个随机变量)之间是否相互独立?如果独立,就可以说词t对类别c完全没有表征作用,即我们根本无法根据t出现与否来判断一篇文档是否属于c这个分类。但与最普通的开方检验不同,我们不需要设定阈值,因为很难说词t和类别c关联到什么程度才算是有表征作用,我们只想借用这个方法来选出一些最最相关的即可。
此时我们仍然需要明白对特征选择来说原假设是什么,因为计算出的开方值越大,说明对原假设的偏离越大,我们越倾向于认为原假设的反面情况是正确的。我们能不能把原假设定为“词t与类别c相关“?原则上说当然可以,这也是一个健全的民主主义社会赋予每个公民的权利(笑),但此时你会发现根本不知道此时的理论值该是多少!你会把自己绕进死胡同。所以我们一般都使用”词t与类别c不相关“来做原假设。选择的过程也变成了为每个词计算它与类别c的开方值,从大到小排个序(此时开方值越大越相关),取前k个就可以(k值可以根据自己的需要选,这也是一个健全的民主主义社会赋予每个公民的权利)。
好,原理有了,该来个例子说说到底怎么算了。
比如说现在有N篇文档,其中有M篇是关于体育的,我们想考察一个词“篮球”与类别“体育”之间的相关性(任谁都看得出来两者很相关,但很遗憾,我们是智慧生物,计算机不是,它一点也看不出来,想让它认识到这一点,只能让它算算看)。我们有四个观察值可以使用:
1.
包含“篮球”且属于“体育”类别的文档数,命名为A
2.
包含“篮球”但不属于“体育”类别的文档数,命名为B
3.
不包含“篮球”但却属于“体育”类别的文档数,命名为C
4.
既不包含“篮球”也不属于“体育”类别的文档数,命名为D
用下面的表格更清晰:
|
特征选择
|
1.属于“体育”
|
2.不属于“体育”
|
总 计
|
|
1.包含“篮球”
|
A
|
B
|
A+B
|
|
2.不包含“篮球”
|
C
|
D
|
C+D
|
|
总 数
|
A+C
|
B+D
|
N
|
如果有些特点你没看出来,那我说一说,首先,A+B+C+D=N(这,这不废话嘛)。其次,A+C的意思其实就是说“属于体育类的文章数量”,因此,它就等于M,同时,B+D就等于N-M。
好,那么理论值是什么呢?以包含“篮球”且属于“体育”类别的文档数为例。如果原假设是成立的,即“篮球”和体育类文章没什么关联性,那么在所有的文章中,“篮球”这个词都应该是等概率出现,而不管文章是不是体育类的。这个概率具体是多少,我们并不知道,但他应该体现在观察结果中(就好比抛硬币的概率是二分之一,可以通过观察多次抛的结果来大致确定),因此我们可以说这个概率接近
(因为A+B是包含“篮球”的文章数,除以总文档数就是“篮球”出现的概率,当然,这里认为在一篇文章中出现即可,而不管出现了几次)而属于体育类的文章数为A+C,在这些个文档中,应该有
篇包含“篮球”这个词(数量乘以概率嘛)。
但实际有多少呢?考考你(读者:切,当然是A啦,表格里写着嘛……)。
此时对这种情况的差值就得出了(套用式(1)的公式),应该是
同样,我们还可以计算剩下三种情况的差值D12,D21,D22,聪明的读者一定能自己算出来(读者:切,明明是自己懒得写了……)。有了所有观察值的差值,就可以计算“篮球”与“体育”类文章的开方值
把D11,D12,D21,D22的值分别代入并化简,可以得到
词t与类别c的开方值更一般的形式可以写成
式(2)
接下来我们就可以计算其他词如“排球”,“产品”,“银行”等等与体育类别的开方值,然后根据大小来排序,选择我们需要的最大的数个词汇作为特征项就可以了。
实际上式(2)还可以进一步化简,注意如果给定了一个文档集合(例如我们的训练集)和一个类别,则N,M,N-M(即A+C和B+D)对同一类别文档中的所有词来说都是一样的,而我们只关心一堆词对某个类别的开方值的大小顺序,而并不关心具体的值,因此把它们从式(2)中去掉是完全可以的,故实际计算的时候我们都使用
式(3)
好啦,并不高深对不对?
针对英文纯文本的实验结果表明:作为特征选择方法时,开方检验和信息增益的效果最佳(相同的分类算法,使用不同的特征选择算法来得到比较结果);文档频率方法的性能同前两者大体相当,术语强度方法性能一般;互信息方法的性能最差(文献[17])。
但开方检验也并非就十全十美了。回头想想A和B的值是怎么得出来的,它统计文档中是否出现词t,却不管t在该文档中出现了几次,这会使得他对低频词有所偏袒(因为它夸大了低频词的作用)。甚至会出现有些情况,一个词在一类文章的每篇文档中都只出现了一次,其开方值却大过了在该类文章99%的文档中出现了10次的词,其实后面的词才是更具代表性的,但只因为它出现的文档数比前面的词少了“1”,特征选择的时候就可能筛掉后面的词而保留了前者。这就是开方检验著名的“低频词缺陷“。因此开方检验也经常同其他因素如词频综合考虑来扬长避短。
好啦,关于开方检验先说这么多,有机会还将介绍其他的特征选择算法。
附:给精通统计学的同学多说几句,式(1)实际上是对连续型的随机变量的差值计算公式,而我们这里统计的“文档数量“显然是离散的数值(全是整数),因此真正在统计学中计算的时候,是有修正过程的,但这种修正仍然是只影响具体的开方值,而不影响大小的顺序,故文本分类中不做这种修正。
2008年7月7日
上回说到对于文本分类这样的不适定问题(有一个以上解的问题称为不适定问题),需要有一个指标来衡量解决方案(即我们通过训练建立的分类模型)的好坏,而分类间隔是一个比较好的指标。
在进行文本分类的时候,我们可以让计算机这样来看待我们提供给它的训练样本,每一个样本由一个向量(就是那些文本特征所组成的向量)和一个标记(标示出这个样本属于哪个类别)组成。如下:
Di=(xi,yi)
xi就是文本向量(维数很高),yi就是分类标记。
在二元的线性分类中,这个表示分类的标记只有两个值,1和-1(用来表示属于还是不属于这个类)。有了这种表示法,我们就可以定义一个样本点到某个超平面的间隔:
δi=yi(wxi+b)
这个公式乍一看没什么神秘的,也说不出什么道理,只是个定义而已,但我们做做变换,就能看出一些有意思的东西。
首先注意到如果某个样本属于该类别的话,那么wxi+b>0(记得么?这是因为我们所选的g(x)=wx+b就通过大于0还是小于0来判断分类),而yi也大于0;若不属于该类别的话,那么wxi+b<0,而yi也小于0,这意味着yi(wxi+b)总是大于0的,而且它的值就等于|wxi+b|!(也就是|g(xi)|)
现在把w和b进行一下归一化,即用w/||w||和b/||w||分别代替原来的w和b,那么间隔就可以写成
![clip_image002[28]](http://www.blogjava.net/images/blogjava_net/zhenandaci/WindowsLiveWriter/SVMPart2_C019/clip_image002%5B28%5D_thumb.gif)
这个公式是不是看上去有点眼熟?没错,这不就是解析几何中点xi到直线g(x)=0的距离公式嘛!(推广一下,是到超平面g(x)=0的距离, g(x)=0就是上节中提到的分类超平面)
小Tips:||w||是什么符号?||w||叫做向量w的范数,范数是对向量长度的一种度量。我们常说的向量长度其实指的是它的2-范数,范数最一般的表示形式为p-范数,可以写成如下表达式
向量w=(w1, w2, w3,…… wn)
它的p-范数为
![clip_image004[10]](http://www.blogjava.net/images/blogjava_net/zhenandaci/WindowsLiveWriter/SVMPart2_C019/clip_image004%5B10%5D_thumb.gif)
看看把p换成2的时候,不就是传统的向量长度么?当我们不指明p的时候,就像||w||这样使用时,就意味着我们不关心p的值,用几范数都可以;或者上文已经提到了p的值,为了叙述方便不再重复指明。
当用归一化的w和b代替原值之后的间隔有一个专门的名称,叫做几何间隔,几何间隔所表示的正是点到超平面的欧氏距离,我们下面就简称几何间隔为“距离”。以上是单个点到某个超平面的距离(就是间隔,后面不再区别这两个词)定义,同样可以定义一个点的集合(就是一组样本)到某个超平面的距离为此集合中离超平面最近的点的距离。下面这张图更加直观的展示出了几何间隔的现实含义:
H是分类面,而H1和H2是平行于H,且过离H最近的两类样本的直线,H1与H,H2与H之间的距离就是几何间隔。
之所以如此关心间隔这个东西,是因为间隔与样本的误分次数间存在关系:

其中的δ是样本集合到分类面的间隔,R是空间中一个能完全包含样本数据的球的半径(也就是说代表样本的分布有多么广)。先不必追究误分次数的具体定义和推导过程,只要记得这个误分次数一定程度上代表分类器的误差。而从上式可以看出,误分次数的上界由间隔决定!(当然,是样本已知的时候)
至此我们就明白为何要选择间隔来作为评价一个解优劣的指标了,原来间隔越大的解,它的误差上界越小。因此最大化间隔成了我们训练阶段的目标,而且,与二把刀作者所写的不同,最大化分类间隔并不是SVM的专利,而是早在线性分类时期就已有的思想。
但是看过一些关于SVM的论文的人一定记得什么优化的目标是要最小化||w||这样的说法,这是怎么回事呢?回头再看看
![clip_image006[11]](http://www.blogjava.net/images/blogjava_net/zhenandaci/WindowsLiveWriter/SVMPart2_C019/clip_image006%5B11%5D_thumb.gif)
这个公式,这里的|g(x)|代表样本集到超平面g(x)=0距离最近的点的值,因此是一个定值,注意到间隔与||w||是成反比的,因此最大化间隔与最小化||w||完全是一回事。而我们常用的方法并不是固定||w||的大小而寻求最大间隔,而是固定间隔(例如固定为1),寻找最小的||w||。
现在有了一个线性分类函数,也有了判断解优劣的标准(有了优化的目标),接下来自然关心如何求解,且听下回分解。
线性分类器(一定意义上,也可以叫做感知机) 是最简单也很有效的分类器形式.在一个线性分类器中,可以看到SVM形成的思路,并接触很多SVM的核心概念.
用一个二维空间里仅有两类样本的分类问题来举个小例子。如图所示
C1和C2是要区分的两个类别,在二维平面中它们的样本如上图所示。中间的直线就是一个分类函数,它可以将两类样本完全分开。一般的,如果一个线性函数能够将样本完全正确的分开,就称这些数据是线性可分的,否则称为非线性可分的。
什么叫线性函数呢?在一维空间里就是一个点,在二维空间里就是一条直线,三维空间里就是一个平面,可以如此想象下去,如果不关注空间的维数,这种线性函数还有一个统一的名称——超平面(Hyper Plane)!
实际上,一个线性函数是一个实值函数(即函数的值是连续的实数),而我们的分类问题(例如这里的二元分类问题——回答一个样本属于还是不属于一个类别的问题)需要离散的输出值,例如用1表示某个样本属于类别C1,而用0表示不属于(不属于C1也就意味着属于C2),这时候只需要简单的在实值函数的基础上附加一个阈值即可,通过分类函数执行时得到的值大于还是小于这个阈值来确定类别归属。 例如我们有一个线性函数
g(x)=wx+b
我们可以取阈值为0,这样当有一个样本xi需要判别的时候,我们就看g(xi)的值。若g(xi)>0,就判别为类别C1,若g(xi)<0,则判别为类别C2(等于的时候我们就拒绝判断,呵呵)。此时也等价于给函数g(x)附加一个符号函数sgn(),即f(x)=sgn [g(x)]是我们真正的判别函数。
关于g(x)=wx+b这个表达式要注意三点:一,式中的x不是二维坐标系中的横轴,而是样本的向量表示,例如一个样本点的坐标是(3,8),则xT=(3,8) ,而不是x=3(一般说向量都是说列向量,因此以行向量形式来表示时,就加上转置)。二,这个形式并不局限于二维的情况,在n维空间中仍然可以使用这个表达式,只是式中的w成为了n维向量(在二维的这个例子中,w是二维向量,注意这里的w严格的说也应该是转置的形式,为了表示起来方便简洁,以下均不区别列向量和它的转置,聪明的读者一看便知);三,g(x)不是中间那条直线的表达式,中间那条直线的表达式是g(x)=0,即wx+b=0,我们也把这个函数叫做分类面。
实际上很容易看出来,中间那条分界线并不是唯一的,我们把它稍微旋转一下,只要不把两类数据分错,仍然可以达到上面说的效果,稍微平移一下,也可以。此时就牵涉到一个问题,对同一个问题存在多个分类函数的时候,哪一个函数更好呢?显然必须要先找一个指标来量化“好”的程度,通常使用的都是叫做“分类间隔”的指标。下一节我们就仔细说说分类间隔,也补一补相关的数学知识。