LingPipe POS taggerを使用して、大量の (~180MB) 電子メールのコーパスで最も頻繁に使用される品詞をカウントするのに問題があります。具体的には、膨大な量のメモリ (少なくとも 4 GB) を消費するため、JVM にどれだけのメモリを割り当てても、OutOfMemoryError で失敗します。あきらめて別のタグ付けライブラリを試す前に、LingPipe に精通している人がいるかどうかを尋ねて、自分の何が間違っているかを知っているかどうかを確認してみようと思いました。
まず、Java シリアライゼーション コードのボイラープレートである LingPipe ライブラリに含まれている pos-en-general-brown.HiddenMarkovModel ファイルから HiddenMarkovModel オブジェクトを読み取ります。次に、次のように使用しようとします。
HmmDecoder decoder = new HmmDecoder(hmm, new FastCache<String, double[]>(1000),
new FastCache<String, double[]>(1000));
List<Email> emails = FileUtil.loadMLPosts(new File(args[1]));
Multiset<String> rHelpTagCounts = countTagsInEmails(decoder, emails);
は次countTagsInEmails
のように定義されます。
static TokenizerFactory TOKENIZER_FACTORY = IndoEuropeanTokenizerFactory.INSTANCE;
public static Multiset<String> countTagsInEmails(HmmDecoder decoder, List<Email> emails) {
Multiset<String> tagCounts = HashMultiset.create();
for(Email email : emails) {
char[] bodyChars = email.body.toCharArray();
Tokenizer tokenizer = TOKENIZER_FACTORY.tokenizer(bodyChars, 0, bodyChars.length);
List<String> bodyTokens = new ArrayList<>();
tokenizer.tokenize(bodyTokens, new ArrayList<String>()); //Throw away the whitespaces list, we don't care
Tagging<String> taggedTokens = decoder.tag(bodyTokens);
tagCounts.addAll(taggedTokens.tags());
}
return tagCounts;
}
FileUtil.loadMLPosts()
詳細は重要ではないと思います。Email
これは、180MB の電子メール アーカイブ ファイルからオブジェクトのリストを作成するだけでbody
、それぞれのフィールドはEmail
電子メールの本文を含む文字列です。Multiset
これは Guava の実装であることに注意してください。
プログラムの実行中に Java のメモリ使用量を監視すると、最初は 1GB (すでに驚くほど高くなっています) であり、タグ付けされた電子メールが増えるにつれて着実に増加します。いくつかの時点で、一度に数百メガバイトずつ劇的にジャンプします。コーパスへのタグ付けが完了する前に、4GB (JVM に与えたメモリ量) に達してクラッシュします。
LingPipe の HmmDecoder はメモリ効率が悪いのでしょうか? それとも私の使い方が悪いのでしょうか?LingPipe の (かなりまばらな) ドキュメント ページにある POS タグ付けの例では、一度に 1 文ずつタグ付けするデコーダが常に示されていることに気付きましたdecoder.tag()
。