最終更新:2016-12-27 (火) 23:59:39 (356d)

FrontPage

TeX Q & A by 奥村晴彦氏

TeX ForumTeX Q&A は日本中の TeX ユーザが集う最大規模の掲示板です.そのコンテンツを有向活用したいと 画策中です.

スレッドごとにページを統合

投稿される題名(サブジェクト)を頼りに時系列ごとに一つの ページにスレッドをまとめられないか検討中.量が膨大なので, まとめたあとのページをどのように分類・整理するかが問題.

クラスタリング

文章内容をクラスタリングして「LaTeXファイルのコンパイルができません」のような 投稿を文単位でクラスタリングして

  • トラブル*
    • コンパイル*
      • 原稿 (LaTeX ファイル*)
    • プレビュー
    • 印刷

のような階層構造を抽出したい.これには形態素解析→係り受け解析が必要となるが, 類義語・同義語判定が難しいかもしれない.インタフェースという言葉はカタカナ表記上無限に存在すると言っても良い.

  • インタフェース
  • インタフェイス
  • インターフェース
  • インターフェイス

コスト最小化の原理で距離を測定してスペルミスと判定するなど,方法は 色々あるかもしれないが…検討中

FAQ の自動生成

QA: question and answer 形式で構成される対話を自動的に FAQ として 成形したい.しかし,一つのスレッドには複数の対話者がいるため, 次のような対話構造になる可能性もある.

  • 最初の質問者
    • 最初の回答者
      • それに対する質問者の返答
    • 別の回答者
      • 最初の回答者の返答
      • それに対する質問者の返答
      • 別の回答者による追加の回答

談話の構造に制限はなく,どのような構造でもあり得る.TeX Q & A の場合はあらかじめ 階層構造が規定されていないため,全ての記事がフラットに近い.ただし,ページ先頭には親記事へのポインタが存在するが,それをわざわざ消している初心者も多いため,完全な階層構造を復元する事が難しい.このため,投稿者と時系列,投稿内容を判断して適切な談話構造の復元を行う必要がある.

TeX Q & A から形態素解析プログラム用の TeX 用語辞書を作成する方法

TeX における専門用語や複合語を抽出するために以下のような方法で 辞書を作ろうと思います.

TeX Q & A の過去ログを入手

まずは,以下の URL から qa.tar.bz2 をダウンロードします(約 12 MB). 記事は 1 番から 48,125 番まで含まれています (欠番 -70 記事).

  • http://oku.edu.mie-u.ac.jp/~okumura/texfaq/qa.tar.bz2 これを
    tar xvf qa.tar.bz2
    として展開します.相当な量のファイルなので,Windows 環境の GUI アプリケーションなどで操作をすると,エクスプローラーが応答しなくなる場合がありますので,可能な限り PowerShell などのコンソールから操作してください.

HTML => TEXT

HTML ファイルから plain text ファイルに変換するためのスクリプトです。

#!/usr/bin/perl -w
# 本文のはじまり
$activation = '<hr>';
# 本文の終わり
$deactivation = 'この書き込みへの返事';
# 入力先
$dir = '/var/www/html/fortexqa/orgd';
# 出力先
$outdir = 'qatxt';
# 最初の記事
$first = 1;
# 最後の記事
$last = 48125;
#$last = 100;
# 処理済みファイルをカウントする変数
$count = 0;

if (-d "$outdir"){
    print "translated plain text file would be output $outdir.\n";
} else {
    mkdir ($outdir);
}

for ($i = $first; $i <= $last; $i++){
    $count ++;
    $in  = "$dir/$i.html";
    $out = "$outdir/$i.txt";
    if (-f "$out"){
       print ",";
    } elsif (-f "$in"){
       open (IN, "<$in");
       open (OUT, ">$out");
       while (<IN>){
           if (m/<h2>(.*?)<\/h2>/){
               $title = $1;
               $title =~ s/$/。/;
               $title =~ s/^Re:\s?//;
               $title =~ s/\&amp;/\&/g;
               $title =~ s/\&gt;/>/g;
               $title =~ s/\&lt;/</g;
           }
           last if (m/^($activation)/);
       }
       print OUT "$title\n";
       while (<IN>){
           last if (m/($deactivation)/);
           next if (m/^\&gt;\s?/);
           s/ +$//;
           chomp;
           s/<.*?>//g;
           s/\&gt;/>/g;
           s/\&lt;/</g;
           s/\&amp;/\&/g;
           s/ +/ /g;
           print OUT;
       }
       close (IN);
       close (OUT);
       print ".";
    } else {
       print "x";
    }
    if (($count % 60) == 0){
       print "\t[$count]\n";
    }
}
print "\n"

カタカナ用語

展開されたファイルについて,まずは余計な HTML タグとフッタを取り除きます.次に英単語とカタカナ用語を抽出します.今回はひらがなと漢字は除外します.

次のような perl script: parse.pl を用意します.

#!/usr/bin/perl -w
# encoding: euc-jp-unix
# 一番最初の記事番号
$first = 1;
# 最後の記事番号
$last  = 48125;
#$last = 100;
 
@words = ();

open (OUT, ">parse.log");
for ($i = $first; $i <= $last; $i++){
    # ファイルが存在するかどうかを検査
    if (-f "$i.html"){
       open (IN, "$i.html");
       while (<IN>){
           last if (m|<body>|);
       }
       while (<IN>){
           last if (m|</pre>|);
           next if (m/^(名前:|日時:|IPアドレス:)/);
           # HTML タグの除去
           s/<.*?>//gi;
           # < > &
           s/\&amp;/\&/g;
           s/\&gt;/\>/g;
           s/\&lt;/\</g;
           # 英数単語の抽出
           s/([a-zA-Z:\/\\][a-zA-Z0-9-_:\/\.\\]{1,})/push(@words, $1);/ge;
           # カタカナ用語の抽出 \xA1A6, \xA1B3, \xA1B4,
           s/((\xA5[\xA1-\xF6]|\xA1\xBC){2,})/push(@words, $1);/ge;
           # 膨大な配列から「ある文字列が含まれるかどうか」を判定する
           # よりも、あとから配列中で重複するものをスキップした方が早い?
       }
       close (IN);
    } else {
       print "ファイルは存在しません: $i.html\n";
    }
}

$previous = '';
foreach $word (sort(@words)){
    next if ($word eq $previous);
    print OUT "$word\n";
    $previous = $word;
}

close (OUT);

基本的には *.html が存在するディレクトリに移動し,このスクリプトを 実行するだけでカタカナ用語と英単語が抽出できる.

./parse.pl

とすれば parse.log が作成される. もちろん,生成されたファイルを手動である程度改変した方が良い.

生のデータ

何も加工していません.

加工したデータ

単語として不適切なものを除外した結果のデータです.

辞書の登録方法

例えば、最低限次のような情報があれば良い。

(品詞 (名詞 固有名詞 一般)) ((見出し語 (Linux 5000)) (読み リナックス) )
(品詞 (名詞 固有名詞 一般)) ((見出し語 (Apache 5000)) (読み アパッチ) )
(品詞 (名詞 固有名詞 一般)) ((見出し語 (PostgreSQL 5000)) (読み ポストグレスエスキューエル) )
(品詞 (名詞 固有名詞 一般)) ((見出し語 (MySQL 5000)) (読み マイエスキューエル) )

IPADIC やウェブ上で入手できる辞書にはカタカナ用語や専門用語がほとんど 含まれていない(漢字や人名は多い).コンピュータ用語が頻出するような ケースでは「アイコン」という用語が

アイコ
ン

に分割されてしまうという悲劇がおこる.そのため,カタカナ用語の辞書ファイルを作成する.

Sen 用辞書の作成方法

Sen は基本的に NAIST で公開されている ipadic を利用している。カスタム辞書 の登録には $SENHOME/dic/build.xml に次の記述を追加することで可能である。

 <java classname="net.java.sen.tools.MkSenDic"
        fork="true"
        maxmemory="140m">
    <arg line="customize_dic.csv" />

カスタム辞書は CSV 形式で、例えば次のような書式である。

あたり,2809,名詞,接尾,一般,*,*,*,あたり,アタリ,アタリ
片手間,3649,名詞,一般,*,*,*,*,片手間,カタテマ,カタテマ
ドラッグ,3378,名詞,一般,*,*,*,*,ドラッグ,ドラッグ,ドラッグ
アカメイモ,4000,名詞,一般,*,*,*,*,アカメイモ,アカメイモ,アカメイモ

語句の次の数字 2809, 4000 などは(形態素)生起コストと呼ばれるもので、 いわゆる出現頻度と似たようなものである。4000 となると、相当出現する 確率が低いことを示す。アカメイモが出現する文章というのは本当に限られている だろう。

sen の場合は 生起コストは 4000 にすれば概ね大丈夫だろう。

あとは、$SENHOME/dic ディレクトリで

ant

と実行すれば、カスタマイズ辞書が作成される。 オリジナルの ipadic とカスタム辞書をマージして使いたいときは

<arg line="customize_dic.csv dic.csv" />

として、デフォルトの辞書を並べれば良い。

カタカナ用語の登録

ここで上記の TeX Q & A から抽出したカタカナ用語があるが、既に登録されている ものを除外した用語ファイル katakana_dic.csv を作成した。

これを作成するために以下のようなプログラムを用いた。

import java.io.*;
import net.java.sen.StringTagger;
import net.java.sen.Token;

public class test {

  // カタカナ用語のエントリがあるディレクトリ
  private static final String dir = "/home/thor/words/";
  // 入力ファイル
  private static final String infile = "katakana.log";
  // Sen 用辞書ファイル (CSV 形式)
  private static final String outfile = "katakana_dic.log";
  // すでに登録されている用語
  private static final String okfile = "katakana.ok";

  private static StringTagger tagger;

  // Sen 用の辞書ファイル katakana_dic.csv を作成する
  public static void main(String args[]) throws Exception {
    // Sen の設定ファイルが置かれている場所を指定する
    tagger = StringTagger.getInstance("/opt/sen/conf/sen.xml");
    // 作業用のバッファ
    String tmp = "";
    // ファイルの読み込み
    BufferedReader br = new BufferedReader(new InputStreamReader(
        new FileInputStream(new File(dir + infile))));
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(new File(dir + outfile))));
    BufferedWriter bwx = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(new File(dir + okfile))));
    // 一行ずつ読み込む
    while ((tmp = br.readLine()) != null) {
      // トークンを取得する
      Token[] token = tagger.analyze(tmp);
      // トークンが存在する
      if (token != null) {
        // トークンが分割されている(ひとつの単語として認識されていない)場合
        if (token.length > 1) {
          bw.write(getCsvFormat(tmp));
        } else {
          // トークンはひとつとして認識されているが、未知語である場合
          if (token[0].getPos().equals("未知語")) {
            bw.write(getCsvFormat(token[0].toString()));
          } else {
            // 既知語である場合
            bwx.write(token[0].toString() + "\t("
                + token[0].getBasicString() + ")\t"
                + token[0].getPos() + "(" + token[0].start()
                + "," + token[0].end() + ","
                + token[0].length() + ")\t"
                + token[0].getReading() + "\t"
                + token[0].getPronunciation() + "\n");
          }
        }
      }
    }
    System.out.println("Finished.");
    bw.close();
    br.close();
    bwx.close();
    return;
  }

  // Sen の辞書ファイルに登録するフォーマット
  private static String getCsvFormat(String s) {
    return s + "," + 4000 + "," + "名詞,一般,*,*,*,*," + s + "," + s + "," + s
        + "\n";
  }

TeX 用コマンドの抽出

既存の LaTeX ファイル中に含まれている TeX 用コマンドを抽出することを 考える。環境は

\begin{環境名}
何何
\end{環境名}

で使われていると仮定する。

命令は バックスラッシュではじまり、アットマークを含んでも良いとする

^\[a-zA-Z\@]+

TeX Q & A の HTML ファイル(数万)と TeX のファイルの両方から コマンド(環境・命令)の出現回数をカウントするスクリプトを以下のように用意した。

#!/usr/bin/perl -w

# 検索対象のディレクトリ
@dirs = (
        '/usr/share/texmf/ptex',
        '/usr/share/texmf/tex',
        '/usr/share/texmf-dist/tex',
        '/var/www/html/fortexqa/orgd', # TeX Q & A のアーカイブ HTML
);

open (FOUT, ">find.txt");
# ファイルリストの作成
foreach $dir (@dirs){
    print FOUT `find $dir\n`;
}
close (FOUT);

# ハッシュの値には出現回数が保持される
%cmds = ();
%envs = ();

open (FIN, "find.txt");
$i=0;
print "[$i]\t";
while(<FIN>){
    chop;
    $file = $_;
    $i++;
    if (-f $file){
       print ".";
       open (SRC, $file);
       while(<SRC>){
           next if (m/^(名前:|日時:|IPアドレス:)/);
           # HTML タグの除去
           s/<.*?>//g;
           # 環境は \begin{<xxx>} で記述される
           s/\\begin\{(\w+)\}/$envs{"$1"} += 1;/ge;
           # コマンドは A-Z, a-z, @, \ で構成される文字列とする
           s/(\\[a-zA-Z\@]+)/$cmds{"$1"} += 1;/ge;
       }
       close (SRC);
    } else {
       $d ++;
       print "d";
    }
   if (($i % 50) == 0){ print "\n[$i]\t";}
}
close (FIN);

# コマンドリストの出力
open (FOUT, ">cmd.list");
foreach $key (sort keys %cmds){
    print FOUT "$key\t$cmds{$key}\n";
}
close (FOUT);

# 環境リストの出力
open (FOUT, ">env.list");
foreach $key (sort keys %envs){
    print FOUT "$key\t$envs{$key}\n";
}
close (FOUT);

$sum = $i - $d;
print "\n\nfinished $sum files (skipped $d directories).\n";

これにより cmd.list と env.list にそれぞれ命令のリスト(名前でソート)と 環境のリストが出力される。

出現回数に応じて、形態素生起コストを決定する。以下の用に資意的に対数的な尺度で決定する。

Sen に TeX 用コマンドを登録する

TeX で用いられているコマンドのほとんどを網羅したリストは作成したが、 メモリの関係上、70000 程度ある、TeX コマンド全てを登録するのは無理がある。

そこで、TeX の命令(バックスラッシュで始まるコマンド)は Sen の辞書には 登録せずに、文字列が入力された段階で処理するように仕様を決定する。ただし、 環境 (\begin{hoge} \end{hoge}) については数が少ないこと、バックスラッシュが 含まれないことから、Sen の辞書に登録することとした。

スレッド化の注意点

(お礼)Re: Blackletterフォントが生成されませんCannot resolv fontsに!: 42538
(お礼)Re: bookスタイルにおける章の概要: 24183
(お礼)丸ゴシック文字のPDF化: 0, 4872
(やはりダメ) Re: dvioutで2回目の印刷時に用紙サイズが設定できない: 34123, 34130
(再) frak の斜体!?: 7502
(年賀)葉書への楽譜貼り付けサンプルです.: 39063, 39064, 39065, 39067
(有難うございました)ispell -h でエラー: 0, 32058
(自己解決)fedora core1 + emacs + ispellでのエラー: 0, 25733
(補足です)dvipdfm,dvipdfmxでpdfに変換したらepsが読み込めない: 0, 24624
(解決&お礼)Re: \mathop{\left[\dfrac{1}{2}\right]}で左上に□が現われてしまいます: 2557
(解決その2)Re: babel でラテン語の韻律記号が正しく扱われない。: 47350
(解決)PDFに変換すると枠がでる: 0, 34219
(訂正)必要なファイルkpathsea333.dllが見つかりません: 5344
(訂正です)fancyhdr.styとtextwidthについて: 0, 10468
(誤記訂正 )fedora core1 + emacs + ispellでのエラー: 0, 25732
DOS窓の振る舞い(解決): 0, 45060
[Q] \sum_{1行目と2行目} を記述するには?: 13319, 13320
[Q] bibtex で、school記述の冒頭の "the" の自動除去は?: 26777, 27063
[Q] ?inhibitglue は?(otfの利用と非利用の場合): 0, 31630, 31658, 31663 
[お詫び] Re: PDF化するとはめ込みの図が縮小する: 45964
[お詫び] Re: \documentstyleを指定すると\usepackage{graphicx}が使えないことについて: 44643
[サマリ] 原稿執筆要領でMS Pゴシックと指定されたときの対処方法: 38408, 38409
[一部訂正] Re: 参考文献の最短コンパイル方法は?: 46516
[御礼] multline と fleqn: 0, 24290
[成功!!]Re: hyperrefとchappg併用時の索引について: 33715
[成功!!]Re: hyperrefとchappg併用時の索引について(test3とtest4の結果訂正): 0, 33716, 33717
[改訂3版]美文書作成入門に関して: 26003, 26007, 26011, 26014, 26027, 26029, 26030, 26034
[改訂版]LaTeX2e美文書作成入門のp344-p345: 11286, 11308, 11416, 11426
[新刊] LaTeX2εアドバンスドガイド: 6615, 7244
[解決!!]再度documentstyleのままでeps張り込み: 20708
[解決] Re: まる秘の書き方: 25935, 25936, 25938, 25942, 25943
[解決]Re: ddrp-platexコマンドのエラー: 27257
[解決]Re: ページレイアウトについて: 15119
[訂正]Re: TeX Wiki: 31302
[質問] Listingsパッケージ使用時でのエラー: 44886, 44887, 44890
[質問] Listingsパッケージ使用時でのエラー(解決、感謝): 0, 44891
[質問] Listingsパッケージ使用時でのエラー(解決済): 0, 44892
[質問]CID フォントとGS(Win32): 7529, 7530
[追記] Re: オフトピ:表記の哲学(Re: “txfonts.sty,”...): 44490, 44492, 44493 
図表の取り込みについて(毎度すみません): 0, 26169, 26171, 26172, 26173, 26184 
変なエラーがでて困ってます(再送): 32135, 32136
変なエラーがでて困ってます(解決): 0, 32138 
平行の記号について(お礼2): 0, 5574
平行の記号について(お礼): 0, 5572 
意外なエラー(後日談): 0, 35067

のように、タイトルの先頭に余計な文字が含まれている場合が、

一般論

Re: 木構造

名前: 奥村晴彦
日時: 2004-11-03 16:34:49
IPアドレス: 219.113.127.*

>>32332

一般論ですが,手抜きされているのか回答者が超能力を持っていると
思われているのか,どのようなTeXファイルを処理したらどのような
エラーが出たというふうに書かない方が多いのですが,何とかならない
でしょうか。