Javaでマルコフ連鎖を実験する

マルコフ連鎖で文章をつくる

割と語りつくされたネタですが、マルコフ連鎖は文章要約や生成などの分野で使われている技術です。これをJavaでやってみようというのが、今回の企画です。JavaScriptの事例はたくさんありますが、意外にもJavaの事例がないのです。検索の仕方が悪いのかもしれませんが。

形態素解析にはKuromojiを使っています。
ダウンロードは公式サイトからできます。

ソースコード

package summary;

import java.util.ArrayList;

import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;

class word {
	String key;
	String key_option; // 品詞
	String value;
	String value_option; // 品詞
	boolean used_flag = false; // 使用済みフラグ(繰り返し使用を抑止)

	public word(String key, String key_option, String value, String value_option) {
		this.key          = key;
		this.key_option   = key_option;
		this.value        = value;
		this.value_option = value_option;
	}
}

public class Summary {

	public Summary() {
        Tokenizer tokenizer = Tokenizer.builder().build();
        ArrayList<word> wordList = new ArrayList<word>();

        String key = "";
        String key_option = "";

        for (Token token : tokenizer.tokenize("河原和音の人気マンガを原作に、吹奏楽部員のつばさ(土屋太凰)と野球部員の大介(竹内涼真)が、ともに「甲子園の夢」を追いかける、ひたむきな姿を描いた映画『青空エール』。高校野球の余韻が残る夏の終わりに公開された本作は、9月10~11日の土日の時点で興行収入10億円を突破するスマッシュヒットになりました。実は私は、今から十数年前の高校生の時に吹奏楽部に所属してアルトサックスを吹いていた当時、なんと母校が全国高校野球選手権大会に出場し、甲子園のスタンドで応援をしたことがあります!今回は、甲子園で野球応援をした元吹奏楽部員の目線から本作を語ります。映画『青空エール』の理解を深めるための副読本的なテキストとなれば幸いです。")) {
            String value = token.getSurfaceForm();
            String value_option = token.getAllFeaturesArray()[0];

            wordList.add(new word(key, key_option, value, value_option));

            key = value;
            key_option = value_option;
        }

        // 開始ワード
        key = wordList.get(1).key;
        key_option = wordList.get(1).key_option;

        while (true) {
        	ArrayList<Integer> indexList = searchWord(wordList, key, key_option);
        	if (indexList.size() == 0) break;

        	// 候補からランダムに1件選択
        	int i = (int)(Math.random() * indexList.size());
        	String value = wordList.get(indexList.get(i)).value;
        	String value_option = wordList.get(indexList.get(i)).value_option;

        	// 使用済みフラグを立てる
        	wordList.get(indexList.get(i)).used_flag = true;
        	System.out.print(value);

        	// 句読点の場合は改行
        	if (value.compareTo("。") == 0) System.out.println();

        	// 次回検索条件をセット
        	key = value;
        	key_option = value_option;
        }

        System.out.println();
	}

	// キーと品詞が一致するデータのインデックスリストを作成
	ArrayList<Integer> searchWord(ArrayList<word> wordList, String key, String key_option) {
		ArrayList<Integer> resultList = new ArrayList<Integer>();

		for (int i = 0; i < wordList.size(); i++) {
			if (wordList.get(i).key.compareTo(key) == 0 && wordList.get(i).key_option.compareTo(key_option) == 0 && wordList.get(i).used_flag == false) {
				resultList.add(i);
			}
		}

		return resultList;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Summary();
	}

}

 

わりと適当ですが、こんな感じで動くプログラムになりました。ちゃんと品詞情報も反映して、比較的まともな文章を生成するようになっています。このプログラムを実行すると、こんな感じの文章が生成されます。

和音のつばさ(土屋太凰)と野球選手権大会に出場した本作をしてアルトサックスを深めるための夢」を描いた映画『青空エール』。
実は私は、9月10億円を追いかける、甲子園で興行収入10~11日の終わりに吹奏楽部員のスタンドで応援を突破するスマッシュヒットに、なんと母校があります。
映画『青空エール』の土日の余韻が全国高校野球応援をした当時、今から本作は、ともに「甲子園の目線から十数年前の高校生の時点で野球の人気マンガを語ります!今回は、吹奏楽部に公開された。
高校野球部員の副読本的な姿を吹いていたことが残る夏の大介(竹内涼真)が、甲子園の時になりました元吹奏楽部員の理解を原作に所属し、ひたむきなテキストとなれば幸いです。

 

ランダムに単語選択しているので、まったく同じ文章には(めったに)なりません。まあ、新しいキャッチコピーを作ったり、コーポレートメッセージを考えたりするのには使えるかもしれませんね。

アサルトサックスを深めるための夢

~ ひたむきなテキスト ~

 

とか、かっこよすぎるんじゃないでしょうか?意味がよくわからないですが、アサルトサックスというものが楽器として存在するということを知ることができました。実に有意義ではないでしょうか。

マルコフ連鎖という名前から、とても高度な処理のように思いますが、自分で書いてみると意外と単純な仕組みであることがわかります。これをつかっていろいろ実験してみてください。

参考文献

マルコフ連鎖については、C++だとこの辺かな?
http://h1dia.hateblo.jp/entry/2016/04/26/235154

特集記事

Posted by @erestage