通常文を単純疑問文に改訂するprolog

先の記事では、node(か,[],[]) というフレーズを挿入して、疑問文を作る方法を示したが、もう少し柔軟に作成する方法を試みよう。

%%
%% 言語二分木を疑問文に変換する
%% mkquestionは、単純な確認疑問文を作成する
%%

%% -------------------------

confirm :- knowledge(_,Node),mkquestion(Node,Q),write(Q).

% 再帰の終端処理
mkquestion(node(N,L,R),P) :- atom(R),atom_concat(R,'か',R1),P = node(N,L,R1).
mkquestion(node(N,L,[]),P) :- atom(N),atom_concat(N,'か',N1),P = node(N1,L,[]).
mkquestion(node([],L,[]),P) :- atom(L),atom_concat(L,'か',L1),P = node([],L1,[]).
% こんなことがあるのだろうか?
mkquestion(node([],L,[]),P) :- node(N2,L2,R2) = L,mkquestion(R2,P2),P = node(N2,L2,P2).
% 基本的な再帰処理
mkquestion(node(N,L,R),P) :- mkquestion(R,P1),P = node(N,L,P1).


%% line = アトムはロボットですか
%% phrases: [ 0 r1 ] 
question(testline_0_0,
    node(ですか,
        node(は,
            [アトム, 'S:普/C:自然物/D:科学・技術'],
            [ロボット, 'S:普/C:人工物-その他/D:科学・技術']
        ),
        [ ]
    )
).

%% line = アトムはロボットです
%% phrases: [ r0 1 ] 
knowledge(testline_0_0,
    node(は,
        [アトム, 'S:普/C:自然物/D:科学・技術'],
        node(です,
            [ロボット, 'S:普/C:人工物-その他/D:科学・技術'],
            [ ]
        )
    )
).

これは、knowledgeの宣言文を単純疑問文に改訂するprologである。状況としては、知識は持っているが、それが確かであることを確認する単純な疑問文と考えれば良い。次のようになる。

?- ['quetion.swi'].
true.

?- confirm.
%出力、タブは事後的に入力

node(は,
    [アトム,S:普/C:自然物/D:科学・技術],
    node(ですか,
        [ロボット,S:普/C:人工物-その他/D:科学・技術],
        []
    )
)
true .

プログラムの方には、questionの頭部で、単純疑問文の例を書いておいた(javaの二分木作成システムで作ったもの)。が、それとは違う二分木システムの単純疑問文になる。プログラムのものは、左の葉に長いシステムで、こちらは、元の文章に引きづられているので、右に長い文章になっている。

 

大規模コーパスとjuman++

この間、形態素解析システムをjumanからjuman++に変更した。jumanは、サーバーモードで動かしていたが、juman++には、内臓でその機能がないので、標準ストリームをjavaで制御する方式に変更した。

日本語wikipediaの全本文など、大規模コーパスをjuman++で制御すると、いくつか深刻な問題が発生する。処理は、24スレッド、128Gメモリのubuntuマシンで処理するが、100を超えるjava スレッドごとにjuman++を立ち上げて処理する。

第1の問題は、jumanと比べ、数倍以上、遅いことである。これは、どうしようもない。プログラムを微調整して、少しでも挽回するしかない。

第2の大きな問題は、メモリを深刻に食べることだ。当初は、javaプログラムに、メモリーリークがあるのではないかと思ったが、javaのガベージコレクションは適切に機能し、メモリーも確保しているのでそちら側の問題ではないと結論づけた。juman++にメモリーリークがあるとは思えないが、どうしようもないので、一定処理ごとに、juman++を再起動し、一旦使っているメモリを解放させることで対応した。大体、1000個の文章を処理するごとに自動で再起動させている。

第3の問題は、数万の規模で文章を処理させていると、途中再起動しても、何かの拍子に、無反応になることである。エラーを吐くわけでもなく、文章を標準入力に入れても、出力しなくなる。標準エラーストリームに文章がたまるとそういうことになる経験があったのでそれかと思ったが、それを系統的に吐き出させていても発生する。これは、javaのバッファリーダーに標準出力をリダイレクトして取り込んでいるのであるが、工夫して、ready()で文章があることを確認してから読み込むようにし、一定期間(30秒)、ループが空回りすると、juman++を再起動するようにしたら、なんとかなっている。

ただ、その詰まりを起こした一つの文章は処理しないので、ちょっとした穴ができるが、全体の文章が膨大なので、影響は無視できる。

色々あっても、jumanと比べて、juman++が吐き出す形態素情報は魅力的で、それ以外のものにしようという気持ちは全く起きていない。