マルコフ連鎖による自動文章生成

hiromasaさんが作成した WordPress プラグイン WordPress Related Post for Japanese が生成する形態素解析の結果を利用して、マルコフ連鎖による自動文章生成をやってみました。
「Yahoo!のAPIを利用してマルコフ連鎖で文章生成(php)」「人工無脳は考える:学習ブロック入門編」を参照させていただきました。
マルコフ連鎖についての詳しい説明はリンク先をご覧ください。

今回は、形態素解析された結果を元に三階のマルコフ連鎖で生成してみました。
ただし、単語のつながりだけを見ていて接続はランダムなので、生成された文章は変です。
ちゃんと使用するにはチューニングが必要ですね。
# 素人がちゃちゃっとつまみ食いしても、精度の高いものはできませんね (^^;

スカイ・クロラ The Sky Crawlers を食わせてみて生成された文章がこちら。

戦争」を強調したいなら、もっと音の無い軽やかな戦闘シーンにした意図がわからない。
この際、原作の空中戦のあと、函南が赴任。

観てきました。

「いつも通る道だからって、景色は同じじゃない」

ティーチャの圧倒的な強さを見せつける冒頭の空中戦のあと、函南が赴任。
個人的には原作の空中戦で受けるイメージは音が無い。
個人的には原作のことは忘れて観ましょう。
個人的にはオススメなのだろう。
かつてのエースである司令官 草薙水素(クサナギ)の声。なんで、あんなに棒読みなんだろうか?

ちなみに複数のエントリをまとめることができるようにも作ってあります。
最近の10件を食わせてみて、生成された文章はこちら。

単語上位10個で、簡単なストップワード対応をしておくニャー
数学が好きな時間帯と言っても救いは訪れないので意気込み十分で行く。
# と言っても、しばらく出張のため、精度がイマイチ。
まあ、豆アジ用のサビキの方の連載も終わったようですし。

書き換えた箇所は 3:00。
日の出の時間もだいぶ遅くなってますね。
導入済みのプラグイン WordPress Related Post for Japanese 導入 Yahoo! Japan が提供する日本語形態素解析APIを利用してくるのだろうか。そんな気もしない
if($i=0; $morpheme->uniq_delimitar_text);
$pattern = '';
これで、私を取り巻く人たちは、リバーサイド千秋の屋上観覧席が当たったので、
色々な意味で楽しめます。

もちろん、完結の話の他、日本全国のサイノシン達が集まって
実際は手を差し伸べることが正しいとはいえ
それを結ぶか離すかはお前たち次第である
我ら日本国の神々が慈悲を以って選ぶ以上は此れ
すべての縁に意味が有り
目の前を過ぎる それだけでも縁

カオスですね。

ソースを以下にさらしておきます。
関数の第一引数は "ポストID(複数指定するときは配列を渡す)"、第二引数は "最大文字数"。

function markovTextGenerator($postID = false, $max_length = 300){
  global $wpdb, $post, $wpjr;

  //(wp-)morpheme テーブルから対象のデータを取得
  $ID = "";
  if (is_array($postID)) {
    foreach ($postID as $wk) {
      if(is_numeric($wk)) $ID .= ($ID != "" ? ',' : "")."'$wk'";
    }
  } else {
    if (!$postID) $postID = $post->ID;
    $ID = "'$postID'";
  }
  $results = $wpdb->get_results($wpdb->prepare(
     "select xml_ma, uniq_delimitar_text"
    ." from {$wpdb->prefix}morpheme"
    ." where appid = '%s' and ID in ({$ID});"
    ,$wpjr->model->yahooId
    ));

  //分かち書きデータと頻出単語を取得
  $pattern = "";    //頻出単語判定用
  $words = array();  //分かち書きの文章格納用
  foreach ($results as $morpheme) {
    //頻出単語上位5個を取得する。
    $keywords = split(',', $morpheme->uniq_delimitar_text, 6);
    if(count($keywords) >= 6) array_pop($keywords);
    foreach ($keywords as $keyword) {
      if (!preg_match('/'.preg_quote($keyword).'/', $pattern)) {
        $pattern .= ($pattern!="" ? '|' : "").$keyword;
      }
    }
    unset($keywords);
    // 元文章部分だけ取得(XMLパース部分は正規表現で手抜き)
    if (preg_match_all("/<surface>(.+?)</surface>/",  $morpheme->xml_ma, $match)) {
      foreach ($match[1] as $surface) $words[] = $surface;
    }
  }
  $pattern = '/'.$pattern.'/';
  unset($results);

  //分かち書きデータが無ければ、終了
  if (!$words) return false;

  //分かち書きデータがあれば、マルコフ連鎖用テーブル作成
  $head   = array();
  $word   = array(null, null, null);
  $markov = array();
  for ($i=0 ; $i<count($words) ; $i++) { 
    if ($word&#91;0&#93; === null) {
      $word&#91;0&#93; = $words&#91;$i&#93;;
    } elseif ($word&#91;1&#93; === null) {
      $word&#91;1&#93; = $words&#91;$i&#93;;
    } else {
      //頻出単語 Top5 を文頭候補にする
      if (preg_match($pattern,$word&#91;0&#93;)) {
        $head&#91;&#93; = array("", $word&#91;0&#93;, $word&#91;1&#93;);
      }
      //マルコフ連鎖用テーブル
      if (!(isset($markov&#91;$word&#91;0&#93;&#93;&#91;$word&#91;1&#93;&#93;) && is_array($markov&#91;$word&#91;0&#93;&#93;&#91;$word&#91;1&#93;&#93;))) {
        $markov&#91;$word&#91;0&#93;&#93;&#91;$word&#91;1&#93;&#93; = array();
      }
      $markov&#91;$word&#91;0&#93;&#93;&#91;$word&#91;1&#93;&#93;&#91;&#93; = array($word&#91;0&#93;, $word&#91;1&#93;, $words&#91;$i&#93;);
      $word&#91;0&#93; = $words&#91;$i - 1&#93;;
      $word&#91;1&#93; = $words&#91;$i&#93;;
    }
  }

  //頻出単語 Top5 の内のどれかを文頭にする
  $word = $head&#91;array_rand($head)&#93;;
  $create_text = $word&#91;0&#93;.$word&#91;1&#93;.$word&#91;2&#93;;
  unset($head);

  // マルコフ連鎖で文章生成
  for ($i=0 ; $i<500 ; $i++) { //無限ループ回避
    // つながりは選択できる中からランダムに選択
    if (isset($markov&#91;$word&#91;1&#93;&#93;&#91;$word&#91;2&#93;&#93;) && is_array($markov&#91;$word&#91;1&#93;&#93;&#91;$word&#91;2&#93;&#93;)) {
      $word = $markov&#91;$word&#91;1&#93;&#93;&#91;$word&#91;2&#93;&#93;&#91;array_rand($markov&#91;$word&#91;1&#93;&#93;&#91;$word&#91;2&#93;&#93;)&#93;;
      $create_text .= $word&#91;2&#93;;
      $create_text = preg_replace('/&#91;&#91;^&#93;&#93;*&#93;/', "", $create_text);
    } else {
      break;
    }
    //文章終わり
    if (mb_strlen($create_text) >= $max_length) break;
  }
  unset($word);
  unset($markov);

  // 生成した文章を返す
  $create_text .= (!preg_match('/(。|.|?|?|!|!)$/', rtrim($create_text)) ? '...' : "");
  return $create_text;
}

マルコフ連鎖による自動文章生成」への2件のフィードバック

  1. ピンバック: ツイートを解析して自動文章作成プラグイン

  2. ピンバック: Twitterの内容からマルコフ連鎖で自動文章

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください