从HTML文件中抽取正文的简单方案 试验结果
标签:人工智能、AI、BP网络、html净化、分类、正文抽取
一、简介
本文是根据alexjc的
http://ai-depot.com/articles/the-easy-way-to-extract-useful-text-from-arbitrary-html/
——alexjc原文
http://blog.csdn.net/lanphaday/archive/2007/08/13/1741185.aspx
——恋花蝶翻译的中英对照版本
该文章主要内容是讲述如何利用正文相对于其他文本,正文文本与生成该正文所需的html字节码的比值较大的规律,利用神经网络识别出正文过滤过滤广告的效果。主要设计如下:
1. 解析HTML代码并记下处理的字节数。
2. 以行或段的形式保存解析输出的文本。
3. 统计每一行文本相应的HTML代码的字节数
4. 通过计算文本相对于字节数的比率来获取文本密度
5. 最后用神经网络来决定这一行是不是正文的一部分。
二、设计方案
本实验相对原本alexjc设计方案有几点修改:
1. 用RPROP(弹性BP网络)代替原文的感知器;
2. 由于原文并没有把文本长度、html字节长度做归一化,所以不采用原始文本长度、html字节长度作为特征值。相对的,对归一化后的文本长度、html字节长度,以及前向后向N行等各种组合进行试验。
3. 试验文本为任意在网上选取的10个网页,见附件。
4. 原文并没有提及,如何定义一行文本是否正文,所以这里定义了几个正文类型:
a) 内容型正文,特征是有长的连续文字段,定义这些文字段为正文;
b) 论坛型,有短的不连续的文字段,定义这些文字段为正文;
c) 论坛帖子列表型(部分试验将会对这类型进行训练查看效果,对于论坛帖子列表是否属于正文这里不做讨论……),帖子标题为正文;
d) 首页型,定义为没有正文(厄,谁能说出,新浪首页哪些是正文?)
实验环境:
1. 语言:JAVA,JRE1.5
2. 操作系统:windows xp
三、实验过程:
1. 设计实现一个三层RPROP网络(令人惊讶的是,居然在这个领域没有人写一个开源的组件,apache等的开源巨头们都对neural network不感兴趣么?)。
/*
*初始化RPROP对象
*
*本函数用于创建训练前的RPROP对象
*参数:
* int in_num 输入层个数;
* int hidden_unit_num 隐含层节点个数
* int out_num 输出层个数
*
*/
public RPROP(int in_num, int hidden_unit_num, int out_num)
/*
*初始化RPROP对象
*
*本函数用于创建训练后的RPROP对象
*参数:
* int in_num 输入层个数;
* int hidden_unit_num 隐含层节点个数
* int out_num 输出层个数
* double[][] w1 隐含层权重
* double[][] w2 输出层权重
* double[] b1 隐含层偏离值
* double[] b2 输出层偏离值
*/
public RPROP(int in_num, int hidden_unit_num, int out_num, double[][] w1, double[][] w2, double[] b1, double[] b2)
/*
*计算输出结果
*
*参数:
* double[] p 输入参数
*返回值:
* double[] 输出结果
*/
public double[] output(double[] p)
/*
*训练
*
*参数:
* double[][] p
* 训练样本集
* double[][][] t
* 期望结果集, t[i][j][0] 期望结果, t[i][j][1]误差放大系数
* double goal
* 目标误差,注意,本网络用的是“方差”作为误差判断条件
* int epochs
* 训练最大次数
*/
public void train(double[][] p, double[][][] t, double goal,int epochs)
对于这个实现,有兴趣的朋友在本文最后下载附件。
2. 选取特征值
在实验中,笔者尝试了各种特征值组合:
1) 文本密度,文本长度,html字节码长度,前后各一行的同样数值;(原文设定)
2) 文本密度,文本长度倒数(归一化),前后各两行的同样数值;
3) 文本所在的html的链接密度(全文文本长度/总链接数,用于加强判断文本类型),文本密度,文本长度/5000(归一化,大于1的当1处理,下文简称为文本长度2),前后两行相同的数值;
4) 文本所在的html的链接密度,文本密度,文本长度2,前后两行相同的数值;
5) 文本所在的html的链接密度,文本密度,文本长度2,前后一行相同的数值;
6) 文本所在的html的链接密度,文本密度,文本长度2,前一行是否正文;
并规定,网络输出结果0为非正文,1为正文。
在训练过程中,发现训练过的网络命中率大部分落在0值部分,这是由于论坛这种短文段类型的网页会导致0值过多,训练时对0值过拟合。为了避免这一点,对某一篇网页的某一行的误差乘以该网页的0值与1值数量的比值。
3. 训练集获取
见附件。这是在笔者常浏览的网页中任意抽取的10个网页。对于期望输出的定义见上文。
四、实验结果
1. 1~5的实验,任意抽取部分样本集作为训练集,对于训练集拟合的很好,但对于测试集的表现却非常糟糕(请原谅笔者并没有记录实验数据);
这部分结果表明,以文本密度作为判断是否正文的特征值是有问题的。观察样本集的数据可以发现,即使是内容型的大段文字,也有可能文本密度很低——为了让网页变得更漂亮美观,现在有很多网站都对文字内容加了大段大段修饰用html代码……
鉴于这一点,笔者最终放弃文本密度作为特征值。而考虑到广告都是带链接的文本,相对的正文连接数则比较少,所以笔者认为,用文本长度/链接数 作为特征值或许会是一个更好的选择。
2. 6的实验,表现意外的非常的好(好到差点让笔者以为终于找到完美的解决方案……)
确实,即使是在测试集部分的表现也惊人好,但实际上有一个问题:每一行的计算受上一行计算的结果影响。测试集是事先定义每一行的上一行的结果,但在实际使用时,上一行的结果是实时计算出来的,所以就会出现,在某一行出错,导致后面的结果全部出错的情况……
至此,假如仍然坚持神经网络的解决方案,或许,采用:
文本长度,文本长度链接数,上一行的结果 做特征值, 采用三个弱分类器的ada-boost组合分类或许会是一个好的选择。
除此之外,实际上对正文的定义对结果也是有很大的影响。实际上,假如能根据数据化的东西定义某一个类别,那么对于该类别的划分,或许其实已经是可预知的,不如直接设计阈值处理。
笔者的实验则到此为止,并放弃了神经网络这个解决方案——直接采用这些特征值进行阈值判断,并对一些特殊部分设定过滤规则,这似乎比神经网络的表现来的简单、有效……
如果有哪位朋友感兴趣,并用ada-boost进行实验,笔者将非常期待这位朋友来交流下心得:)
附件:
neralNetwork.rar 源代码
res.rar 训练集
厄,俺不知道csdn的附件怎么上传,就丢个链接吧,俺的javaeye的博客同一个位置:
http://hzxdark.javaeye.com/admin/blogs/119239
关于html文本抽取部分,这里用的是HtmlParser,这里就不贴出来了,有兴趣的朋友可以去:
http://htmlparser.sourceforge.net/
看看。