summaryrefslogtreecommitdiff
path: root/src-splitter/lattice.c
diff options
context:
space:
mode:
Diffstat (limited to 'src-splitter/lattice.c')
-rw-r--r--src-splitter/lattice.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/src-splitter/lattice.c b/src-splitter/lattice.c
new file mode 100644
index 0000000..2a242e2
--- /dev/null
+++ b/src-splitter/lattice.c
@@ -0,0 +1,541 @@
+/*
+ * 確率を評価しビタビアルゴリズム(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);
+}