ワードサラダ技術について

ちょっと前にはてブで注目されていた「ワードサラダというスパムを知っていますか? (ブログウォッチャー編集長日記)」ってエントリがありました。

ワードサラダというものをご存じでしょうか?調べてみたら日本ではあまり話題になっていないスパムなので紹介します。
ワードサラダというものを使ったブログスパムが最近こっそりと流行しているようです。ワードサラダとは簡単に言うと、複数の単語を適当に組み合わせて、人間が見ても全く意味がないが、ロボットにはちゃんとした文章に見える文章を作ることです。
この文章をブログのエントリーとして投稿することで、検索エンジンなどにひっかけさせて、誘導するという手法です

単純に、その文章自動生成の技術について興味がわきました。文章自動生成の技術は人工無能時代に流行った技術が参考になります。

- スポンサーリンク -

人工無能のアルゴリズムのおさらい

ここで一度、人工無能のメジャーなアルゴリズムについておさらいしてみます。2年ほど前に流行った技術なので良くも悪くも簡易的なアルゴリズムのものが多いと思います。

※以下説明は、「人工無脳レビュー」より引用させて頂きました。

マルコフ連鎖

マルコフ連鎖を用いて文を生成する。後述の辞書型人工無脳は辞書を拡大することでよい反応をするようになるが、マルコフ文生成型は逆で、生成する文の質はどれだけソースの辞書を小さくできるかにかかっている。形態素解析器によって言葉を細切れにしすぎても同様に文の品質低下を招く。この型の人工無脳は人名ではなく無人格的な名前を与えられていることが多い。マルコフ連鎖で文をつぎはぎする結果人工無脳のキャラクタ性が低い、という特徴を反映しているようで面白い。

ログ型

ログを検索し、ユーザ発言に似た文の次の行を返答のベースにする。このベース文に現れる既知の単語をユーザ発言に含まれる単語に適当に置き換える。ここでユーザ発言を使うことで人間の連想に似た効果が得られていると思われる。また、既知の単語が見つからない場合に無理してしゃべろうとしないのも理解しやすい。会話における人間の挙動をよく観察した結果に基づいて作られているという印象を受ける。

辞書型

辞書を使って文を生成する。教育システムとの兼ね合いを考えておけばキャラクタ性を維持することができる。反面、辞書を作る作業があまりに大変なため、実際に作りこむに至った人工無脳はなかなかない。


日本語処理の分野はまだまだ未開拓の分野ではあり今後更なる進歩があると思うし、この分野はやっていて非常に楽しいです。さて、話を元に戻して、ワードサラダのような文章を作成するなら、上記の3つの中ではマルコフ連鎖による文章自動生成がよいでしょう。それ以外はチャット向けのアルゴリズムだし。

ここで、マルコフ連鎖についての説明をしておきます。

マルコフ連鎖とは?(Wikipedia より

マルコフ連鎖とは、確率過程の一種であるマルコフ過程のうち、とりうる状態が離散的(有限または可算)なもの(離散状態マルコフ過程)をいう。また特に、時間が離散的なもの(時刻は添え字で表される)を指すことが多い。(他に連続時間マルコフ過程というものもあり、これは時刻が連続である。)
マルコフ連鎖は、未来の挙動が現在の値だけで決定され、過去の挙動と無関係である(マルコフ性)。各時刻において起こる状態変化(遷移または推移)に関して、マルコフ連鎖は遷移確率が過去の状態によらず、現在の状態のみによる系列である。

後半部分が重要で、未来の挙動が現在の値だけで決定され、過去の挙動と無関係である ということです。

さて、実例です。たとえば次の文章を考えてみます。

「通信販売大手セシールは9日、生命保険の販売に本格参入する方針を明らかにした。」

まず形態素解析するとこんな感じになります。

通信    名詞,サ変接続,*,*,*,*,通信,ツウシン,ツーシン
販売    名詞,サ変接続,*,*,*,*,販売,ハンバイ,ハンバイ
大手    名詞,一般,*,*,*,*,大手,オオテ,オーテ
セシール        名詞,固有名詞,組織,*,*,*,セシール,セシール,セシール
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
9      名詞,数,*,*,*,*,9,キュウ,キュー
日      名詞,接尾,助数詞,*,*,*,日,ニチ,ニチ
、      記号,読点,*,*,*,*,、,、,、
生命    名詞,一般,*,*,*,*,生命,セイメイ,セイメイ
保険    名詞,一般,*,*,*,*,保険,ホケン,ホケン
の      助詞,連体化,*,*,*,*,の,ノ,ノ
販売    名詞,サ変接続,*,*,*,*,販売,ハンバイ,ハンバイ
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
本格    名詞,一般,*,*,*,*,本格,ホンカク,ホンカク
参入    名詞,サ変接続,*,*,*,*,参入,サンニュウ,サンニュー
する    動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
方針    名詞,一般,*,*,*,*,方針,ホウシン,ホーシン
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
明らか  名詞,形容動詞語幹,*,*,*,*,明らか,アキラカ,アキラカ
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
し      動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。      記号,句点,*,*,*,*,。,。,。
       記号,空白,*,*,*,*, , , 

次に単純なマルコフ連鎖(二階のマルコフ連鎖)について考えてみます。

「通信」   → 「販売」
「販売」   → 「大手」
「大手」   → 「セシール」
「セシール」 → 「は」
「は」    → 「9」
「9」    → 「日」
 ・・・・
「明らか」  → 「に」
「に」    → 「し」
「し」    → 「た」
「た」    → 「。」

この連鎖規則を単純に辿っていくだけのモデルがマルコフ連鎖です。この二階のマルコフ連鎖を用いて文章生成を行った場合、組合せが発散傾向にあるため、支離滅裂な文章になりがちです。そこで三階のマルコフ連鎖が良く用いられます。n階のマルコフ連鎖を考えていけばより単語の連結が意味のあるものになるが、その代償として生成される文章は限定されていきます。

三階のマルコフ連鎖は以下のようになります。

「通信」「販売」   → 「大手」
「販売」「大手」   → 「セシール」
「大手」「セシール」 → 「は」
「セシール」「は」  → 「9」
「は」「9」     → 「日」
・・・・
「明らか」「に」   → 「し」
「に」「し」     → 「た」
「し」「た」     → 「。」

となります。ネットでマルコフ連鎖による自動文章生成を検索すると多くはこのアルゴリズムによるものが見つかると思いますが、これだけでは文章の意味がやはり支離滅裂な場合が多いと思います。そこで三階のマルコフ連鎖で文章の連続性に意味を持たせるために品詞情報を活用します。こうすることで、ある程度は主語述語のような関係も考慮された形の文章が生成されやすいと思います。
※生成された辞書をマジメに比較したわけではないので、思っているだけかもしれませんが・・・(^^ゞ

つまり辞書はこんな感じになります。

「通信\t名詞,サ変接続」「販売\t名詞,サ変接続」  → 「大手\t名詞,一般」
「販売\t名詞,サ変接続」「大手\t名詞,一般」    → 「セシール\t名詞,固有名詞」
「大手\t名詞,一般」「セシール\t名詞,固有名詞」  → 「は\t助詞,係助詞」
「セシール\t名詞,固有名詞」「は\t助詞,係助詞」  → 「9\t名詞,数」
「は\t助詞,係助詞」「9\t名詞,数」        → 「日\t名詞,接尾」
・・・・
「明らか\t名詞,形容動詞語幹」「に\t助詞,格助詞」 → 「し\t動詞,自立」
「に\t助詞,格助詞」「し\t動詞,自立」       → 「た\t助動詞,*」
「し\t動詞,自立」「た\t助動詞,* 」        → 「。\t記号,句点」

後は、このアルゴリズムを perl のハッシュの概念に落とすだけでマルコフ連鎖辞書が完成します。さらに、文章の開始単語も記憶しておくことで不自然な文章開始をさけることができます。
エラー処理とか文字コードとかもろもろの細かい処理を考えない場合、こんなコードになるかと。このコードだと三階層にならない文章は辞書に入らないけど、サンプルと言うことでご了承下さい。
mizzy.org : perlで人工無脳 #1 のプログラムを大いに参考にさせて頂きました
※悪用されないように所々、*** にしています。あしからず。

#!/usr/local/bin/perl

use strict;
use warnings;
use MeCab;
use Storable qw/lock_nstore lock_retrieve/;

my $infile = $ARGV[0] or die 'usage: make_dic.pl filename';
open( my $fh, $infile ) or die $!;

my $dic_file   = "markov.dic";
my $start_file = "markov_start.dic";
my $dic        = {};
my $startdic   = [];

$dic      = lock_retrieve($dic_file)   if ( -e $dic_file );
$startdic = lock_retrieve($start_file) if ( -e $start_file );

my $mecab = new MeCab::Tagger("-Ochasen");
while (<$fh>) {
    my $flg  = 0;
    my $term = '';
    my @word;
    for ( my $n = $mecab->parseToNode($text); $n; $n = $n->{next} ) {
        my ($feature) = $n->{feature} =~ /^([^,]+,[^,]+),/;
        next if ( $feature eq 'BOS/EOS,*' );
        $term = $n->{surface} . "\t" . $feature;
        if ( $flg == 0 ) {
            next if ( ****** );
            push @$startdic, $term;
            $flg++;
        }
        push @word, $term;
        if ( scalar @word == 3 ) {
            push( ******, **** );
            *****;
        }
    }
}
close $fh;
lock_nstore( $dic,      $dic_file );
lock_nstore( $startdic, $start_file );

後はできあがった辞書を元に文章生成。

このプログラムを公開しちゃうとスパマーが喜ぶだけなので各自考えて下さいと言うことで。ちなみに、マルコフ連鎖による文章生成 : blog.nomadscafe.jp と同じ元文章から学習した辞書を使って文章生成してみるとこんな感じの文章が出力されるようになりました。

よーしパパ特盛頼んじゃうぞー、とか言ってるんです。
吉野家通の俺から言わせてもらえば今、吉野家通の俺から言わせてもらえば今、吉野家通の間での最新流行はやっぱり、ねぎだく、これだね。
お前、つゆだくなんてきょうび流行んねーんだよ。
吉野家ってのはな、150円。
得意げな顔して何が、つゆだくなんてきょうび流行んねーんだよ、ボケが。
大盛りねぎだくギョク。
大盛りギョク(玉子)。

う〜ん・・・文章単位で見ると前後のつながりはさっぱりですね。より自然な文章にするなら、cabocha を使って係り受け辞書を作ったり、文章単位でのつながりのルールを作ったりといったことも必要ですね。すぐにいい案はでてきませんが、楽しそうな分野です。

P.S.
情報販売でブログの記事自動生成とか見かけますが、所詮はこれに毛が生えたようなものかと。検索DBが汚れるので全くお奨めできるものではありませんね。

- スポンサーリンク -