文章を自動的に要約する、短くする(形態素解析、マルコフ連鎖)

始めにお断りしておきますが、自動的に…っていうのはどうしても不自然になるものです。
ここで紹介する方法で要約(?)してもちゃんとした文になる保証は全くないです。
用途としては、できた文章の変なところをおもしろがるとか、スパムブログ的な使い方しかないかもしれません。

ちなみに、マルコフ連鎖については良くというか全く知らないので、Web上で要約(?)するプログラムを作ってる方がいらしたので、PHPに書き直してクラス化しただけです。
下記のサイトのJavaScriptをリライトさせていただきました。特徴が分からないので、2種類のアルゴリズムを選べるようにしています。

使い方:

require_once('MarkovChainSummarizer.php');
$string='iPadの魅力を、そのままiPad miniに。美しいスクリーン。速くてなめらかなパフォーマンス。FaceTimeカメラとiSightカメラ。数えきれないほどそろった驚くようなアプリケーション。10時間駆動するバッテリー*。iPad miniは、iPadをまるごと、しかも片手に収まるサイズの中につめ込みました。';
$summarizer = new MarkovChainSummarizer;
echo $summarizer->summarize($string, 1); // 1を渡すか2を渡すかでアルゴリズムが変わる。デフォルトは1。1でも2でもない場合はなにもしません。

で、文章を短くしたりしなかったりします。
引数$stringが文字列の場合は、テキトーな形態素解析にかけられます(sloppy_analysis())。
引数にarrayを渡すと、各要素を単語として利用するので、MecabとかYahoo!の日本語形態素解析とかTinySegmenterとかを使って事前に分解して渡すとより精度が良くなります。
文字列でもarrayでもない場合は、falseを返して何もしません。

MarkovChainSummarizer.phpは次のような感じ。

class MarkovChainSummarizer {

function summarize($text, $var = 1) {
 if ($var == 1) return $this->summarize_1($text);
 if ($var == 2) return $this->summarize_2($text);
}

function summarize_1($text) {
 if (is_array($text)) $words = $text;
 elseif (is_string($text)) $words = $this->sloppy_analysis($text);
 else return false;

 $table = array(); 

 for ( $i = 0; $i < count($words) - 2; $i++ ) {
  $table[] = array(
   'head'	 => $words[$i],
   'middle'	 => $words[$i + 1],
   'end'	 => $words[$i + 2]
  );
 }

 $t1 = $table[0]['head'];
 $t2 = $table[0]['middle'];
 $summary = $t1 . $t2;

 while (true) {
  $a = array();
  foreach ($table as $h) {
   if ($h['head'] == $t1 && $h['middle'] == $t2) $a[] = $h;
  }

  if (count($a) == 0) break;
  $num = array_rand($a);
  $summary .= $a[$num]['end'];
  if ($a[$num]['end'] == "EOS") break ;
  $t1 = $a[$num]['middle'];
  $t2 = $a[$num]['end'];
 }
 return preg_replace('/EOS$/', '', $summary);
}

function summarize_2($text, $chain_max=500) {
 $dic = array();
 $sentences = array();
 if (is_string($text)) {
  $sentence_end = '。';
  foreach (explode($sentence_end, $text) as $sentence) {
   $sentences[] = $this->sloppy_analysis($sentence . $sentence_end);
  }
 }
 elseif (is_array($text)) $sentences[] = $text;
 else return false;
 foreach($sentences as $token) {
  array_splice($token, 0, 0, '_START_');
  $token[] ='_END_';
  while ($token[1]) {
   if ($dic[$token[0]]) {
    $dic[$token[0]][] = $token[1];
   }
   else {
    $dic[$token[0]] = array($token[1]);
   }
   array_shift($token);
  }
 }

 $w1 = $dic['_START_'][rand(0,count($dic['_START_'])-1)];
 $w2 = '';
 $sentence = $w1;
 while ($chain_max) {
  $w2 = $dic[$w1][rand(0, count($dic[$w1])-1)];
  if ($w2 == '_END_') { break; }
  $sentence .= $w2;
  $w3 = $w1;
  $w1 = $w2;
  $w2 = $w3;
  $chain_max--;
 }
 return $sentence;
}

function sloppy_analysis($text) {
 $re = '/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+|[、。!!??]+/u';
 preg_match_all($re, $text, $m);
 return $m[0];
}
}

Leave a Reply