[Anthy-dev 1925] Re: anthy-6300

Back to archive index

yusuk****@cheru***** yusuk****@cheru*****
2005年 3月 3日 (木) 22:40:12 JST


田畑です。

> > 6300→6300bで文節の学習(?)がおかしくなってしまったようです.
> そのようですね、手元でも再現しました。

> [業務連絡]
> hmm.cでpush_node()する前にposition = i + mw->lenになってますけど、
> 学習で作られたmeta_wordの右端ってこれでよかったんでしたっけ?

どれとのdiffを作ったら良いのかわからないので、全部付けますが
mw_real_len()という関数を作ってなんとかしてみました。

--
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <alloc.h>
#include <xstr.h>
#include <segclass.h>
#include <splitter.h>
#include <parameter.h>
#include "wordborder.h"

float anthy_normal_length = 20.0; /* 文節の期待される長さ */

#define NODE_MAX_SIZE 100

/* 各遷移状態 */
struct hmm_node {
  int border; /* 文字列中のどこから始まる状態か */
  int nth; /* 現在いくつめの文節か */
  enum seg_class seg_class; /* この状態の品詞 */

  double real_probability;  /* ここに至るまでの確率(文節数補正無し) */
  double probability;  /* ここに至るまでの確率(文節数補正有り) */
  int score_sum; /* 使用しているmetawordのスコアの合計*/

  struct hmm_node* before_node; /* 一つ前の遷移状態 */
  struct meta_word* mw; /* この遷移状態に対応するmeta_word */

  struct hmm_node* next; /* リスト構造のためのポインタ */
};

struct hmm_info {
  int size;
  struct hmm_node** hmm_list; /* 遷移状態のリストの配列 */
  struct splitter_context *sc;
  allocator node_allocator;
};

static double g_transition[SEG_SIZE][SEG_SIZE] = {
#include "transition.h"
};


static void
print_hmm_node(struct hmm_info *info, struct hmm_node *node)
{
  if (!node) {
    printf("**hmm_node (null)*\n");
    return ;
  }
  printf("**hmm_node score_sum=%d, nth=%d*\n", node->score_sum, node->nth);
  printf("probability=%.128f\n", node->real_probability);
  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_probability(struct hmm_node *node)
{
  double probability;
  if (anthy_seg_class_is_depword(node->seg_class)) {
    probability = 1.0 / SEG_SIZE;
  } else if (node->before_node->seg_class == SEG_HEAD &&
	     (node->seg_class == SEG_FUKUSHI)) {
    probability = 1.0 / SEG_SIZE;
  } else if (node->before_node->seg_class == SEG_HEAD &&
	     (node->seg_class == SEG_SETSUZOKUGO)) {
    probability = 1.0 / SEG_SIZE;
  } else {
    probability = g_transition[node->before_node->seg_class][node->seg_class]
      * get_poisson(anthy_normal_length, node->mw->len - node->mw->weak_len);
  }
  return probability;
}

static struct hmm_info*
alloc_hmm_info(struct splitter_context *sc, int size)
{
  struct hmm_info* info = (struct hmm_info*)malloc(sizeof(struct hmm_info));
  info->sc = sc;
  info->size = size + 1;
  info->hmm_list = (struct hmm_node**)calloc(size, sizeof(struct hmm_node*) * (info->size));
  info->node_allocator = anthy_create_allocator(sizeof(struct hmm_node), NULL);
  return info;
}

static struct hmm_node*
alloc_hmm_node(struct hmm_info *info, struct hmm_node* before_node,
	       struct meta_word* mw, int border)
{
  struct hmm_node* node;
  node = anthy_smalloc(info->node_allocator);
  node->before_node = before_node;
  node->border = border;
  node->next = NULL;
  node->mw = mw;
  
  /* 対応するmetawordが無い場合は文頭と判断する */
  node->seg_class = mw ? mw->seg_class : SEG_HEAD; 

  if (before_node) {
    node->nth = before_node->nth + 1;
    node->score_sum = before_node->score_sum + (mw ? mw->score : 0);
    node->real_probability = before_node->real_probability * get_probability(node);
    node->probability = node->real_probability * node->score_sum;
  } else {
    node->nth = 0;
    node->score_sum = 0;
    node->real_probability = 1.0;
    node->probability = node->real_probability;
  }

  return node;
}

static void 
release_hmm_node(struct hmm_info *info, struct hmm_node* node)
{
  anthy_sfree(info->node_allocator, node);
}

static void
release_hmm_info(struct hmm_info* info)
{
  anthy_free_allocator(info->node_allocator);
  free(info->hmm_list);
  free(info);
}

static int
cmp_node_by_type(struct hmm_node *lhs, struct hmm_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 hmm_node *lhs, struct hmm_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 hmm_node *lhs, struct hmm_node *rhs)
{
  struct hmm_node *lhs_before = lhs;
  struct hmm_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->border == rhs_before->border && lhs_before->mw && rhs_before->mw) {
      /* 学習から作られたノードかどうかを見る */
      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->probability > rhs->probability) {
    return 1;
  } else if (lhs->probability < rhs->probability) {
    return -1;
  } else {
    return 0;
  }
}

static void
push_node(struct hmm_info* info, struct hmm_node* new_node, int position)
{
  struct hmm_node* node = info->hmm_list[position];
  struct hmm_node* previous_node = NULL;

  if (!node) {
    info->hmm_list[position] = new_node;
    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->hmm_list[position] = new_node;
	}
	new_node->next = node->next;
	release_hmm_node(info, node);
	break;
      case -1:
	/* そうでないなら削除 */
	release_hmm_node(info, new_node);
	break;
      }
      return;
    }
    previous_node = node;
    node = node->next;
  }
  node->next = new_node;
}

static int
list_length(struct hmm_info* info, int position)
{
  int length = 1;
  struct hmm_node* node = info->hmm_list[position];
  while(node) {
    ++length;
    node = node->next;
  }
  return length;
}

static void
remove_min_node(struct hmm_info* info, int position)
{
  struct hmm_node* node = info->hmm_list[position];
  struct hmm_node* previous_node = NULL;
  struct hmm_node* min_node = node;
  struct hmm_node* previous_min_node = NULL;
  int list_len = 0;

  /* 一番確率の低いノードを消去する*/
  while (node) {
    if (cmp_node(node, min_node) < 0) {
      previous_min_node = previous_node;
      min_node = node;
    }
    previous_node = node;
    node = node->next;
    ++list_len;
  }
  if (previous_min_node) {
    previous_min_node->next = min_node->next;
  } else {
    info->hmm_list[position] = min_node->next;
  }
  release_hmm_node(info, min_node);
}

static int
mw_real_len(struct meta_word *mw)
{
  int len = 0;
  while (mw) {
    len += mw->len;
    mw = mw->mw1;
  }
  return len;
}

static void
hmm(int from, int to, struct hmm_info* info)
{
  int i;
  int before = 0; /* 遷移の一番後ろ */
  struct hmm_node* node;
  struct hmm_node* first_node;  

  first_node = alloc_hmm_node(info, NULL, NULL, from);
  push_node(info, first_node, from);

  /* info->hmm_list[index]にはindexまでの遷移が入っているのであって、
   * indexからの遷移が入っているのではない 
   */

  /* 全ての遷移を試す */
  for (i = from; i < to; ++i) {
    /* 
     * mwの遷移が途中で切れていたら仕方がないので、一番後ろまで来ている遷移を使う
     */
    if (info->hmm_list[i] && info->sc->word_split_info->cnode[i].mw) {
      before = i;
    }

    for (node = info->hmm_list[before]; node; node = node->next) {
      struct meta_word *mw;
      for (mw = info->sc->word_split_info->cnode[i].mw; mw; mw = mw->next) {
	int position;
	struct hmm_node* new_node;
	if (mw->can_use != ok) continue; /* 決められた文節の区切りをまたぐmetawordは使わない */
	position = i + mw_real_len(mw);
	new_node = alloc_hmm_node(info, node, mw, i);
	push_node(info, new_node, position);

	/* 解の候補が多すぎたら、確率の低い方から削る */
	if (list_length(info, position) >= NODE_MAX_SIZE) {
	  remove_min_node(info, position);
	}
      }
    }
  }

  /* 文末補正 */
  for (node = info->hmm_list[to]; node; node = node->next) {
    node->probability = node->probability * g_transition[node->seg_class][SEG_TAIL];
  }

  {
    /* 最後まで到達した遷移のなかで一番確率の大きいものを選ぶ */
    struct hmm_node* best_node = NULL;
    int last = to; 
    while (!info->hmm_list[last]) --last; /* 最後まで遷移していなかったら後戻り */
    for (node = info->hmm_list[last]; node; node = node->next) {
      if (cmp_node(node, best_node) > 0) {
	best_node = node;
      }
    }
    if (!best_node) return;

    /* 遷移を逆にたどりつつ文節の切れ目を記録 */
    node = best_node;
    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);
      node = node->before_node;
    }
  }
}

void
anthy_hmm(struct splitter_context *sc, int from, int to)
{
  struct hmm_info* info = alloc_hmm_info(sc, to);
  hmm(from, to, info);
  release_hmm_info(info);
}




Anthy-dev メーリングリストの案内
Back to archive index