viterbi 中文分词-超简单版

use encoding "gbk";
#load score哈希,这个Score是自己定的
open(Inscore, "<$ARGV[0]") or die "无法打开信息文件。\n";
%score=();
while(<Inscore>)
{
	chomp($_);
	if($_ eq "")
	{
		next;
	}
	@pair=();
	@pair=split("\t",$_); 
	$score{$pair[0]}->{one}=$pair[1]/$pair[4];#频率	
}
close(Inscore);
print "load done......\n";
# 句子 $sentence 
# $num当前字在句子中的下标,$n_max字符串最大长度
# @scoretag[$num][$n_max]:记录当前最大分值矩阵
# @father[$num][$n_max]:记录父节点矩阵
 
			if($scoretag[$num][$my_j]==-1)
			{
				$scoretag[$num][$my_j]=0;			
			}$n_pinghua_str=0.02;$n_pinghua=0.01;#这个平滑系数,要根据具体问题重新设定
open(Insentence,"<$ARGV[1]") or die "无法打开待分词文件\n";
while(<Insentence>)
{
	chomp($_);
	@word=split //,$_;
	$num=0;
	@scoretag=();
	@father=();


	#初始化第一个字
	for($i=0;$i<$n_max;$i++)
	{
		$scoretag[$num][$i]=0;
		$father[$num][$i]=-1;
	}
	$scoretag[$num][0]=$n_pinghua;#以后为了做平滑,再说平滑系数
	if(defined $score{$word[$num]})
	{
		$scoretag[$num][0]=$score{$word[$num]}->{one};
	}
	
	
	#从第二字开始,viterbi 生成网格
	for($num=1;$num<@word;$num++)
	{
		#my_j=0 代表当前字,=1 为当前字与前一个字,每层有$n_max个选择
		for($my_j=0;$my_j<$n_max and $my_j < $num+1 ;$my_j++)
		{
			#找到父节点所在层,计算最大值,记录父节点
			$fatherlevel=$num - $my_j -1;#从$my_j 推算出,父节点应该在$num的哪一层。回退重要下标
			$scoretag[$num][$my_j]=-1;
			$father[$num][$my_j]=-1;

			if($fatherlevel > -1)
			{
				for($father_j=0;$father_j <$n_max;$father_j++)
				{
					if($scoretag[$num][$my_j] < $scoretag[$fatherlevel][$father_j])#分数相同取短者
					{
						$scoretag[$num][$my_j]=$scoretag[$fatherlevel][$father_j];
						$father[$num][$my_j]=$father_j;#回退重要下标
					}
				}
			}
			if($scoretag[$num][$my_i]==-1)$scoretag[$num][$my_i]=0;
			#生成当前字符串
			$keyword="";			
			for($sub=0;$sub<$my_j+1;$sub++)
			{
				$keyword=$word[$num-$sub].$keyword;
			}

			#得到当前字符串的发射概率,本算法中暂时没有"转移概率"
			if(not defined $score{$keyword})
			{
				if($my_j ==0)#单字与字符串所给的频次不同,平滑
				{
					$nowscore_my_j=$n_pinghua;
				}else{
					$nowscore_my_j=$n_pinghua_str;
				}
			}
			else{
					$nowscore_my_j=$score{$keyword}->{one};
			}
			$scoretag[$num][$my_j]+=$nowscore_my_j;
			#print "$my_j\n";
		}
	}
	#回退
	#先找到回退入口,即$num-1最后一个字的值,哪个值最大。
	$maxfinal=0;
	$maxfather=-1;
	for($i=0;$i<$n_max;$i++)
	{
		if($maxfinal < $scoretag[$num-1][$i])
		{
			$maxfinal = $scoretag[$num-1][$i];
			$maxfather=$i;
		}
	}
        #开始回退
	$i=$num-1;
	$output="";
	while($i>-1)
	{
		$maxnextfather=$father[$i][$maxfather];
		for($j=0;$j<$maxfather+1 and $i>-1;$j++)
		{
			$output=$word[$i].$output;
			$i--;
		}
		if($i >-1)
		{
			$output="  ".$output;
		}
		$maxfather=$maxnextfather;
	}
	print "$output\n";
	
}
close(Insentence);
#简单的viterbi分词代码,score即要用到的概率、语言模型、自己想什么作为分数就初始化score为什么;
#perl编程,简单版本,分数相加,(暂时没用转移概率矩阵)

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值