/*
* 確率を評価しビタビアルゴリズム(viterbi algorithm)によって
* 文節の区切りを決定してマークする。
*
*
* 外部から呼び出される関数
* anthy_mark_borders()
*
* Copyright (C) 2006-2007 TABATA Yusuke
* Copyright (C) 2004-2006 YOSHIDA Yuichi
* Copyright (C) 2006 HANAOKA Toshiyuki
*
*/
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* コンテキスト中に存在するmeta_wordをつないでグラフを構成します。
* (このグラフのことをラティス(lattice/束)もしくはトレリス(trellis)と呼びます)
* meta_wordどうしの接続がグラフのノードとなり、構造体lattice_nodeの
* リンクとして構成されます。
*
* ここでの処理は次の二つの要素で構成されます
* (1) グラフを構成しつつ、各ノードへの到達確率を求める
* (2) グラフを後ろ(右)からたどって最適なパスを求める
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <anthy/alloc.h>
#include <anthy/xstr.h>
#include <anthy/segclass.h>
#include <anthy/splitter.h>
#include <anthy/feature_set.h>
#include <anthy/diclib.h>
#include "wordborder.h"
static float anthy_normal_length = 20.0; /* 文節の期待される長さ */
static void *trans_info_array;
#define NODE_MAX_SIZE 50
/* グラフのノード(遷移状態) */
struct lattice_node {
int border; /* 文字列中のどこから始まる状態か */
enum seg_class seg_class; /* この状態の品詞 */
double real_probability; /* ここに至るまでの確率(文節数補正無し) */
double adjusted_probability; /* ここに至るまでの確率(文節数補正有り) */
struct lattice_node* before_node; /* 一つ前の遷移状態 */
struct meta_word* mw; /* この遷移状態に対応するmeta_word */
struct lattice_node* next; /* リスト構造のためのポインタ */
};
struct node_list_head {
struct lattice_node *head;
int nr_nodes;
};
struct lattice_info {
/* 遷移状態のリストの配列 */
struct node_list_head *lattice_node_list;
struct splitter_context *sc;
/* ノードのアロケータ */
allocator node_allocator;
};
/*
*/
static void
print_lattice_node(struct lattice_info *info, struct lattice_node *node)
{
if (!node) {
printf("**lattice_node (null)*\n");
return ;
}
printf("**lattice_node probability=%.128f\n", node->real_probability);
if (node->mw) {
anthy_print_metaword(info->sc, node->mw);
}
}
static double
get_poisson(double lambda, int r)
{
int i;
double result;
/* 要するにポワソン分布 */
result = pow(lambda, r) * exp(-lambda);
for (i = 2; i <= r; ++i) {
result /= i;
}
return result;
}
/* 文節の形式からスコアを調整する */
static double
get_form_bias(struct meta_word *mw)
{
double bias;
int r;
/* wrapされている場合は内部のを使う */
while (mw->type == MW_WRAP) {
mw = mw->mw1;
}
/* 文節長による調整 */
r = mw->len;
if (r > 6) {
r = 6;
}
if (r < 2) {
r = 2;
}
if (mw->seg_class == SEG_RENTAI_SHUSHOKU &&
r < 3) {
/* 指示語 */
r = 3;
}
bias = get_poisson(anthy_normal_length, r);
return bias;
}
static void
build_feature_list(struct lattice_node *node,
struct feature_list *features)
{
int pc, cc;
if (node) {
cc = node->seg_class;
} else {
cc = SEG_TAIL;
}
anthy_feature_list_set_cur_class(features, cc);
if (node && node->before_node) {
pc = node->before_node->seg_class;
} else {
pc = SEG_HEAD;
}
anthy_feature_list_set_class_trans(features, pc, cc);
if (node && node->mw) {
struct meta_word *mw = node->mw;
anthy_feature_list_set_dep_class(features, mw->dep_class);
anthy_feature_list_set_dep_word(features,
mw->dep_word_hash);
anthy_feature_list_set_mw_features(features, mw->mw_features);
anthy_feature_list_set_noun_cos(features, mw->core_wt);
}
anthy_feature_list_sort(features);
}
static double
calc_probability(int cc, struct feature_list *fl)
{
struct feature_freq *res, arg;
double prob;
/* 確率を計算する */
res = anthy_find_feature_freq(trans_info_array,
fl, &arg);
prob = 0;
if (res) {
double pos = res->f[15];
double neg = res->f[14];
prob = 1 - (neg) / (double) (pos + neg);
}
if (prob <= 0) {
/* 例文中に存在しないパターンなので0に近いスコア */
prob = 1.0f / (double)(10000 * 100);
}
if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_LN) {
anthy_feature_list_print(fl);
printf(" cc=%d(%s), P=%f\n", cc, anthy_seg_class_name(cc), prob);
}
return prob;
}
static double
get_transition_probability(struct lattice_node *node)
{
struct feature_list features;
double probability;
/**/
anthy_feature_list_init(&features);
build_feature_list(node, &features);
probability = calc_probability(node->seg_class, &features);
anthy_feature_list_free(&features);
/* 文節の形に対する評価 */
probability *= get_form_bias(node->mw);
return probability;
}
static struct lattice_info*
alloc_lattice_info(struct splitter_context *sc, int size)
{
int i;
struct lattice_info* info = (struct lattice_info*)malloc(sizeof(struct lattice_info));
info->sc = sc;
info->lattice_node_list = (struct node_list_head*)
malloc((size + 1) * sizeof(struct node_list_head));
for (i = 0; i < size + 1; i++) {
info->lattice_node_list[i].head = NULL;
info->lattice_node_list[i].nr_nodes = 0;
}
info->node_allocator = anthy_create_allocator(sizeof(struct lattice_node),
NULL);
return info;
}
static void
calc_node_parameters(struct lattice_node *node)
{
/* 対応するmetawordが無い場合は文頭と判断する */
node->seg_class = node->mw ? node->mw->seg_class : SEG_HEAD;
if (node->before_node) {
/* 左に隣接するノードがある場合 */
node->real_probability = node->before_node->real_probability *
get_transition_probability(node);
node->adjusted_probability = node->real_probability *
(node->mw ? node->mw->score : 1000);
} else {
/* 左に隣接するノードが無い場合 */
node->real_probability = 1.0;
node->adjusted_probability = node->real_probability;
}
}
static struct lattice_node*
alloc_lattice_node(struct lattice_info *info,
struct lattice_node* before_node,
struct meta_word* mw, int border)
{
struct lattice_node* node;
node = anthy_smalloc(info->node_allocator);
node->before_node = before_node;
node->border = border;
node->next = NULL;
node->mw = mw;
calc_node_parameters(node);
return node;
}
static void
release_lattice_node(struct lattice_info *info, struct lattice_node* node)
{
anthy_sfree(info->node_allocator, node);
}
static void
release_lattice_info(struct lattice_info* info)
{
anthy_free_allocator(info->node_allocator);
free(info->lattice_node_list);
free(info);
}
static int
cmp_node_by_type(struct lattice_node *lhs, struct lattice_node *rhs,
enum metaword_type type)
{
if (lhs->mw->type == type && rhs->mw->type != type) {
return 1;
} else if (lhs->mw->type != type && rhs->mw->type == type) {
return -1;
} else {
return 0;
}
}
static int
cmp_node_by_type_to_type(struct lattice_node *lhs, struct lattice_node *rhs,
enum metaword_type type1, enum metaword_type type2)
{
if (lhs->mw->type == type1 && rhs->mw->type == type2) {
return 1;
} else if (lhs->mw->type == type2 && rhs->mw->type == type1) {
return -1;
} else {
return 0;
}
}
/*
* ノードを比較する
*
** 返り値
* 1: lhsの方が確率が高い
* 0: 同じ
* -1: rhsの方が確率が高い
*/
static int
cmp_node(struct lattice_node *lhs, struct lattice_node *rhs)
{
struct lattice_node *lhs_before = lhs;
struct lattice_node *rhs_before = rhs;
int ret;
if (lhs && !rhs) return 1;
if (!lhs && rhs) return -1;
if (!lhs && !rhs) return 0;
while (lhs_before && rhs_before) {
if (lhs_before->mw && rhs_before->mw &&
lhs_before->mw->from + lhs_before->mw->len == rhs_before->mw->from + rhs_before->mw->len) {
/* 学習から作られたノードかどうかを見る */
ret = cmp_node_by_type(lhs_before, rhs_before, MW_OCHAIRE);
if (ret != 0) return ret;
/* COMPOUND_PARTよりはCOMPOUND_HEADを優先 */
ret = cmp_node_by_type_to_type(lhs_before, rhs_before, MW_COMPOUND_HEAD, MW_COMPOUND_PART);
if (ret != 0) return ret;
} else {
break;
}
lhs_before = lhs_before->before_node;
rhs_before = rhs_before->before_node;
}
/* 最後に遷移確率を見る */
if (lhs->adjusted_probability > rhs->adjusted_probability) {
return 1;
} else if (lhs->adjusted_probability < rhs->adjusted_probability) {
return -1;
} else {
return 0;
}
}
/*
* 構成中のラティスにノードを追加する
*/
static void
push_node(struct lattice_info* info, struct lattice_node* new_node,
int position)
{
struct lattice_node* node;
struct lattice_node* previous_node = NULL;
if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_LN) {
print_lattice_node(info, new_node);
}
/* 先頭のnodeが無ければ無条件に追加 */
node = info->lattice_node_list[position].head;
if (!node) {
info->lattice_node_list[position].head = new_node;
info->lattice_node_list[position].nr_nodes ++;
return;
}
while (node->next) {
/* 余計なノードを追加しないための枝刈り */
if (new_node->seg_class == node->seg_class &&
new_node->border == node->border) {
/* segclassが同じで、始まる位置が同じなら */
switch (cmp_node(new_node, node)) {
case 0:
case 1:
/* 新しい方が確率が大きいか学習によるものなら、古いのと置き換え*/
if (previous_node) {
previous_node->next = new_node;
} else {
info->lattice_node_list[position].head = new_node;
}
new_node->next = node->next;
release_lattice_node(info, node);
break;
case -1:
/* そうでないなら削除 */
release_lattice_node(info, new_node);
break;
}
return;
}
previous_node = node;
node = node->next;
}
/* 最後のノードの後ろに追加 */
node->next = new_node;
info->lattice_node_list[position].nr_nodes ++;
}
/* 一番確率の低いノードを消去する*/
static void
remove_min_node(struct lattice_info *info, struct node_list_head *node_list)
{
struct lattice_node* node = node_list->head;
struct lattice_node* previous_node = NULL;
struct lattice_node* min_node = node;
struct lattice_node* previous_min_node = NULL;
/* 一番確率の低いノードを探す */
while (node) {
if (cmp_node(node, min_node) < 0) {
previous_min_node = previous_node;
min_node = node;
}
previous_node = node;
node = node->next;
}
/* 一番確率の低いノードを削除する */
if (previous_min_node) {
previous_min_node->next = min_node->next;
} else {
node_list->head = min_node->next;
}
release_lattice_node(info, min_node);
node_list->nr_nodes --;
}
/* いわゆるビタビアルゴリズムを使用して経路を選ぶ */
static void
choose_path(struct lattice_info* info, int to)
{
/* 最後まで到達した遷移のなかで一番確率の大きいものを選ぶ */
struct lattice_node* node;
struct lattice_node* best_node = NULL;
int last = to;
while (!info->lattice_node_list[last].head) {
/* 最後の文字まで遷移していなかったら後戻り */
--last;
}
for (node = info->lattice_node_list[last].head; node; node = node->next) {
if (cmp_node(node, best_node) > 0) {
best_node = node;
}
}
if (!best_node) {
return;
}
/* 遷移を逆にたどりつつ文節の切れ目を記録 */
node = best_node;
if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_LN) {
printf("choose_path()\n");
}
while (node->before_node) {
info->sc->word_split_info->best_seg_class[node->border] =
node->seg_class;
anthy_mark_border_by_metaword(info->sc, node->mw);
/**/
if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_LN) {
print_lattice_node(info, node);
}
/**/
node = node->before_node;
}
}
static void
build_graph(struct lattice_info* info, int from, int to)
{
int i;
struct lattice_node* node;
struct lattice_node* left_node;
/* 始点となるノードを追加 */
node = alloc_lattice_node(info, NULL, NULL, from);
push_node(info, node, from);
/* info->lattice_node_list[index]にはindexまでの遷移が入っているのであって、
* indexからの遷移が入っているのではない
*/
/* 全ての遷移を左から試す */
for (i = from; i < to; ++i) {
for (left_node = info->lattice_node_list[i].head; left_node;
left_node = left_node->next) {
struct meta_word *mw;
/* i文字目に到達するlattice_nodeのループ */
for (mw = info->sc->word_split_info->cnode[i].mw; mw; mw = mw->next) {
int position;
struct lattice_node* new_node;
/* i文字目からのmeta_wordのループ */
if (mw->can_use != ok) {
continue; /* 決められた文節の区切りをまたぐmetawordは使わない */
}
position = i + mw->len;
new_node = alloc_lattice_node(info, left_node, mw, i);
push_node(info, new_node, position);
/* 解の候補が多すぎたら、確率の低い方から削る */
if (info->lattice_node_list[position].nr_nodes >= NODE_MAX_SIZE) {
remove_min_node(info, &info->lattice_node_list[position]);
}
}
}
}
/* 文末補正 */
for (node = info->lattice_node_list[to].head; node; node = node->next) {
struct feature_list features;
anthy_feature_list_init(&features);
build_feature_list(NULL, &features);
node->adjusted_probability = node->adjusted_probability *
calc_probability(SEG_TAIL, &features);
anthy_feature_list_free(&features);
}
}
void
anthy_mark_borders(struct splitter_context *sc, int from, int to)
{
struct lattice_info* info = alloc_lattice_info(sc, to);
trans_info_array = anthy_file_dic_get_section("trans_info");
build_graph(info, from, to);
choose_path(info, to);
release_lattice_info(info);
}
syntax highlighted by Code2HTML, v. 0.9.1