CakePHP

Currently, 17 posts in this tag.

TinyTui2:带有降噪功能的Twitter客户端

| 8 comments 2010-03-02 17:53:22

去年春天的时候我写过一篇名为“一个更好的Twitter客户端”的文章,里面提到了使用贝叶斯算法来对tweets的内容进行过滤,把自己不喜欢的内容删除掉,这样可以解决信息过载的问题。

庄表伟对这个事情也很感兴趣。他本来写了一个名为TinyTui的Twitter客户端的,于是我们经过简单的讨论,决定就在TinyTui的基础上,融入降噪的功能,并将这个客户端命名为TinyTui2,简称TT2。

系统需求

TT2使用PHP+MySQL编写,使用了CURL库,服务器只要支持这3个条件就能用。

降噪算法

这是TT2的重头,目前主要从三方面入手:

  1. 内容过滤。按照内容对tweets进行过滤,使用贝叶斯算法

  2. 行为过滤。按照行为规则过滤,比如过滤掉我深恶痛绝的“用RT聊天”的行为。目前没有想好用什么算法,@tinyfool和@virushuo推荐了“隐马尔可夫模型(Hidden Markov model)”“协同过滤(Collaborative filtering)”,不过这两个坑都太深了,庄老师和我都还只是站在坑边上往下看了看,没敢跳。此外,我觉得算法可以有很多种,但每种算法要单独写到一个过滤器里面,然后用户加载不同的过滤器满足自己的需求(类似LT的VisitorSense);

  3. 好友过滤。简单地说,就是给每个你follow的人以不同的权重。

这三个方面经过计算后分别得到一个0到1之间的值,然后按照某种公式把这三个值汇总,得到一个总分,最后比较这个总分和事先设定好的阙值,就可以决定某条tweet是否应该显示或者被过滤掉。

其它功能

除了降噪以外,TT2还集成了一些其它的特色功能,比如它内建了一个简单的网页代理,可以让你直接查看那些被墙的网页(典型如短网址)。

项目地址

项目托管在Google Code上:http://code.google.com/p/tinytui2/。要注意的是,提供下载的0.1-0.4版是我参与之前的版本,没有降噪功能,最新的代码请从SVN中获取。

代码完全是“quick and dirty”风格的,我们都是实用派。

在Snow Leopard下编译PHP的pecl_http扩展库

| 4 comments 2010-01-14 10:31:50

由于Snow Leopard为64位系统,Apache无法加载直接编译pecl_http出来的http.so,查日志文件可以看到如下提示:

PHP Warning: PHP Startup: Unable to load dynamic library '/Applications/XAMPP/xamppfiles/lib/php/php-5.3.0/extensions/no-debug-non-zts-20090626/http.so' - dlopen(/Applications/XAMPP/xamppfiles/lib/php/php-5.3.0/extensions/no-debug-non-zts-20090626/http.so, 9): no suitable image found. Did find:\n\t/Applications/XAMPP/xamppfiles/lib/php/php-5.3.0/extensions/no-debug-non-zts-20090626/http.so: mach-o, but wrong architecture in Unknown on line 0

Google一圈后,发现要设置一下环境变量:

MACOSX_DEPLOYMENT_TARGET=10.6

CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp"

CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe"

CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe"

LDFLAGS="-arch i386 -arch x86_64 -bind_at_load"

export CFLAGS CXXFLAGS LDFLAGS CCFLAGS MACOSX_DEPLOYMENT_TARGET

然后在终端下照常编译即可:

phpize

./configure

make

成功加载这个扩展后,phpinfo()中会看到:

phpinfo()中显示的模块信息

在CakePHP中使用CSSTidy和JSMin优化前端性能

| 0 comments 2009-05-06 20:48:46

先说结果

借助CSSTidy和JSMin,你可以大幅度地提升前端的性能。下图是我的blog程序LT在优化以前的前端性能(用的YSlow v2):

优化前的得分

优化前的统计

可以看到得分只有D(65),比较低。此外由于没有设置expires headers,HTTP请求即使在缓存后也高达25个。

不过好在其中除了CDN一项我无法控制外,剩下的内容都可以优化。下面是优化后的结果:

优化后的得分

优化后的统计

优化后,得分上升到了B(85)。expires headers一项的得分F是由一些外部资源带来的,这个问题貌似目前没有什么好办法。

再说怎么做

参考这篇名为“Automatic JavaScript and CSS Packer”的文章,15分钟就能搞定。

另外要说的就是,你完全可以把文章作者提供的.htaccess中的内容,复制到网站根目录下的.htaccess中,这可以帮你给网页中图片也加上expires headers。

SPAM、Bayesian和中文 5 - 使b8贝叶斯算法支持中文

| 4 comments 2009-04-21 10:56:24

中文分词简介

在使用贝叶斯算法分类文本时,中文处理是个头疼的问题,这主要体现在中文分词上。所谓分词的意思就是让计算机能够拆分出一句话中的词,比如把“我们爱祖国”分为“我们”、“爱”和“祖国”,这是简单的情况,更复杂比如把“变态性心理”按上下文语境分别拆分为“变态性”“心理”和“变态”“性心理”[1]。由于词的定义、分词标准和词的判定都没有一个统一的共识,再加上计算机在处理语义上的困难,因此“汉语自动分词已经被研究了二十多年,但是目前仍然是制约汉语信息处理发展的一个瓶颈”[2]。目前公认的比较好的分词技术,来源于中科院计算所的“汉语词法分析系统ICTCLAS”,它提供了Windows和Linux平台下C#、C和Java JNI的API。

LT的简单做法:中文分字

不过本文并非讨论分词技术,因为这项技术本身已经超越了目前LT的需求。实际上,我们只要对b8做一些改动,让它在拆分文本时考虑到双字节字符(比如中文)的情况,并在实际拆分时以字为单位,就可以应付一般的情况。

LT从1月24日发布的0.4.4中开始使用改造过的b8贝叶斯算法,从实际运行情况来看,改造后的b8能够比较好的处理中文的情况。另外由于绝大部分SPAM都是英文,因此总的来说中文分字的做法可以接受。

改造b8

打开b8/lexer/lexer_default.php文件,首先修改lexer_default()这个constructor,在方法的最后添加如下代码:

//set the default encoding for mutil-byte characters
mb_internal_encoding('UTF-8');

接着修改getTokens(),第一是提取中文标点符号,在$tokens = "";一行代码下面添加如下代码:

//strip Chinese punctuation
$text = preg_replace('/[“”!◎#¥%……※×()——+§『』【】,。、‘’;:~·]/u', '', $text);

第二处理网址和IP地址,找到如下代码:

preg_match_all("/([A-Za-z0-9\_\-\.]+)/", $text, $raw_tokens);

将其修改为:

//exract all Chinese tokens
preg_match_all('/[一-龥]/u', $text, $raw_tokens_chinese);		
 
//exract all Latin tokens		
preg_match_all("/([A-Za-z0-9\_\-\.]+)/", $text, $raw_tokens_latin);

将提取出来的中文和拉丁字符合并在一起,组成raw tokens:

$raw_tokens = array_merge($raw_tokens_chinese[0], $raw_tokens_latin[0]);

第三继续提取剩余的中文和拉丁文字符,找到如下代码:

# Raw splitting of the remaining text
 
$raw_tokens = preg_split("/[^A-Za-z0-9!?\$дег'`─╓▄фЎ№▀╔щ╚ш╩ъ┴с└р┬т╙є╥Є╘Ї╟ч]/", $text);

将其修改为:

# Raw splitting of the remaining text
 
//exract all Chinese tokens, split each Chinese character with preg_match_all()
preg_match_all('/[一-龥]/u', $text, $raw_tokens_chinese);
 
//delete Chinese characters from the text, extract all Latin tokens
$raw_latin = $this->strip_whitespaces(preg_replace('/[一-龥]!?/u', ' ', $text));
$raw_tokens_latin = preg_split("/[^A-Za-z0-9!?\$дег'`─╓▄фЎ№▀╔щ╚ш╩ъ┴с└р┬т╙є╥Є╘Ї╟ч]/", $raw_latin);
 
//merge Chinese tokens and Latin's
$raw_tokens = array_merge($raw_tokens_chinese[0], $raw_tokens_latin);

然后修改isValid(),找到如下代码:

# Check for a proper length
if(strlen($token) < $this->config['minSize'] or strlen($token) > $this->config['maxSize'])
	return FALSE;

将其修改为:

# Check for a proper length
//only when the token is NOT Chinese we need to do the check
if(!preg_match('/[一-龥]/u', $token))
{
	if(strlen($token) < $this->config['minSize'] or strlen($token) > $this->config['maxSize'])
	return FALSE;
}

至此b8的中文化改造和本系列文章都完成了,你可以试试效果:),抵御SPAM这块我也接触不久,有问题欢迎讨论。

 

文中的一些标注:

  1. “变态性心理”这个词组来源于陈思和主编、复旦出版的《中国当代文学史教程》,是用来形容郁达夫在旅日留学时的苦闷、矛盾和抑郁的心情,这些心情连同背后的生活经历都如实地反映在了其代表作《沉沦》中。我上学的时候一眼发现“变态性心理”这个有歧义的词组,和同学玩笑了半天,因此记忆犹新。( 回到文中
  2. 张春霞,郝天永。“汉语自动分词的研究现状与困难”。系统仿真学报 Vol. 17 No. 1 Jan. 2005。( 回到文中

SPAM、Bayesian和中文 4 - 在CakePHP中集成贝叶斯算法

| 2 comments 2009-03-21 16:15:46

上文提到了贝叶斯算法的几种开源实现,本文说说如何将其中一种名为b8的开源实现集成进CakePHP。

下载b8及安装

  1. b8的站点下载最新版本,将其解压至vendors目录,文件位置如vendors/b8/b8.php;
  2. 用文本编辑器打开vendors/b8/etc/config_b8,修改databaseType为mysql;
  3. 用文本编辑器打开vendors/b8/etc/config_storage,修改tableName为你用来存储关键字的数据表的名字,修改createDB为TRUE,要注意的是,当你第一次运行b8后,它会建立上述数据表,然后你要重新把createDB改为FALSE;
  4. 用文本编辑器打开vendors/b8/lexer/shared_functions.php,将38行的代码(在echoError())注释掉,否则b8会直接把错误信息显示在你的Cake应用中,当然这在调试程序时还是有用的。

为b8写一个wrapper component

为了让你的Cake能够调用到b8,你需要写一个component。在controllers/components/新建一个spam_shield.php,加入如下代码:

class SpamShieldComponent extends Object
{
	/**
	 * b8 instance
	 */
	var $b8;
 
	/**
	 * standard rating
	 *
	 * comments with ratings which are higher than this one will be considered as SPAM
	 */
	var $standardRating = 0.7;
 
	/**
	 * text to be classified
	 */
	var $text;
 
	/**
	 * rating of the text
	 */
	var $rating;
 
 
    /**
     * Constructor
     * 
     * @date 2009-1-20
     */
    function startup(&$controller)
    {
    	//register a CommentModel to get the DBO resource link
    	$comment = ClassRegistry::init('Comment');
 
		//import b8 and create an instance
		App::import('Vendor', 'b8/b8');
		$this->b8 = new b8($comment->getDBOResourceLink());
 
		//set standard rating
		$this->standardRating = Configure::read('LT.bayesRating') ? Configure::read('LT.bayesRating') : $this->standardRating;
    }
 
    /**
     * Set the text to be classified
     * 
     * @param $text String the text to be classified
     * @date 2009-1-20
     */
    function set($text)
    {	
		$this->text = $text;
    }
 
    /**
     * Get Bayesian rating
     * 
     * @date 2009-1-20
     */
    function rate()
    {	
		//get Bayes rating and return
		return $this->rating = $this->b8->classify($this->text);
    }
 
    /**
     * Validate a message based on the rating, return true if it's NOT a SPAM
     * 
     * @date 2009-1-20
     */
    function validate()
    {
		return $this->rate() < $this->standardRating;
    }
 
    /**
     * Learn a SPAM or a HAM
     * 
     * @date 2009-1-20
     */
    function learn($mode)
    {
		$this->b8->learn($this->text, $mode);
    }
 
    /**
     * Unlearn a SPAM or a HAM
     * 
     * @date 2009-1-20
     */
    function unlearn($mode)
    {
		$this->b8->unlearn($this->text, $mode);
    }
}

几点说明:

  1. $standardRating是一个临界点。如果贝叶斯概率高于这个值,则此留言被认为是spam,否则是ham。我设置为0.7,你可以根据自己的情况修改;
  2. Configure::read('LT.bayesRating')是从系统运行配置中动态地获取上述临界点的值,这是我的做法,你可能用不到,根据情况稍微修改甚至不修改都行;
  3. Comment指的是评论的model;
  4. 由于b8需要获得数据库句柄以便能够操作数据表,所以在startup()中我写了$this->b8 = new b8($comment->getDBOResourceLink())一句,其中用到的getDBOResourceLink()马上会提及。

为b8传入数据库句柄

在models/comment.php中加入如下代码:

/**
	 * get the resource link of MySQL connection
	*/
	public function getDBOResourceLink()
	{
		return $this->getDataSource()->connection;	
	}

至此,准备工作全部做完,我们终于可以使用贝叶斯算法来分类留言。

使用b8分类留言

在controllers/comments_controller.php中,首先载入SpamShieldComponent:

var $components = array('SpamShield');

然后在add()方法中,做如下操作:

//set data for Bayesian validation
		$this->SpamShield->set($this->data['Comment']['body']);
 
		//validate the comment with Bayesian
		if(!$this->SpamShield->validate())
		{
			//set the status
			$this->data['Comment']['status'] = 'spam';
 
			//save
			$this->Comment->save($this->data);
 
			//learn it
			$this->SpamShield->learn("spam");
 
			//render
			$this->renderView('unmoderated');
			return;
		}
 
		//it's a normal post
		$this->data['Comment']['status'] = 'published';
 
		//save for publish
		$this->Comment->save($this->data);
 
		//learn it
		$this->SpamShield->learn("ham");

如此一来,b8就会在留言到来时自动的分类并学习,你基本上已经与spam绝缘了!

提醒一下:第一次运行后,别忘了把刚才提到的createDB改为FALSE。

About

我在厦门拍的照片

丁宇(Felix Ding),电脑Geek,狂热的爱书和爱乐分子,99年迷上网页设计,并从此一发不可收。现在在上海做用户体验/产品设计咨询。Email: felixding[AT]gmail.com。

订阅到RSS