2011-11-14 11 views
3

英語のwikipediaダンプにある上位100Kワードに基づいてnグラム言語モデルを構築しようとしています。私は既に、Javaで書かれた変更されたXMLパーサーを使ってプレーンテキストを抽出しましたが、それをvocabファイルに変換する必要があります。Perlスクリプトの実行時にメモリ不足の問題を解決しました。

これを行うために、私は仕事をすると言われているが、実行方法に関する指示が不足しているperlスクリプトを見つけました。言うまでもなく、私はPerlの完全な初心者ですが、これは初めての使用の必要性に遭遇しました。

このスクリプトを実行すると、RAMを4GB搭載した2つの別個のデュアルコアマシン上で7.2GBのテキストファイルでこれを使用し、Ubuntu 10.04と10.10を実行すると、Out of Memoryエラーが発生します。

著者に連絡したところ、このスクリプトはMacBook Proで4GBのRAMを搭載していて、perl 5.12の6.6GBのテキストファイルで実行した場合、メモリ使用量は約78MBでした。著者はまた、スクリプトが入力ファイルを1行ずつ読み込み、メモリにハッシュマップを作成するとも述べています。

スクリプトは次のとおりです。

#! /usr/bin/perl 

use FindBin; 
use lib "$FindBin::Bin"; 

use strict; 
require 'english-utils.pl'; 

## Create a list of words and their frequencies from an input corpus document 
## (format: plain text, words separated by spaces, no sentence separators) 

## TODO should words with hyphens be expanded? (e.g. three-dimensional) 

my %dict; 
my $min_len = 3; 
my $min_freq = 1; 

while (<>) { 

    chomp($_); 
    my @words = split(" ", $_); 

    foreach my $word (@words) { 

     # Check validity against regexp and acceptable use of apostrophe 

     if ((length($word) >= $min_len) && ($word =~ /^[A-Z][A-Z\'-]+$/) 
     && (index($word,"'") < 0 || allow_apostrophe($word))) { 
      $dict{$word}++; 
     } 
    } 

} 

# Output words which occur with the $min_freq or more often 

foreach my $dictword (keys %dict) { 
    if ($dict{$dictword} >= $min_freq) { 
     print $dictword . "\t" . $dict{$dictword} . "\n"; 
    } 
} 

私は、コマンドラインからこのスクリプトを実行していmkvocab.pl corpus.txt

経由含ま余分なスクリプトは、単にアポストロフィ年代の配置をテストするための正規表現のスクリプトで、それらが一致するかどうかを英語の文法規則。

マシンに5.10がインストールされているため、メモリリークが異なるバージョンのためだと思っていました。だから私は5.14にアップグレードしましたが、エラーは依然として続きます。 free -mによれば、私のシステムには約1.5GBの空きメモリがあります。

私は言語の構文と構造に全く慣れていないので、問題が存在する理由とその解決方法とともに問題の領域を指摘できますか。

+0

入力ファイルに長い行がありますか?入力ファイルに改行がない場合、メモリ内に大量のデータが保持されます。あなたの言葉でいくつかの繰り返しを仮定しても、あなたのハッシュはおそらく非常に大きくなります。 – TLP

答えて

7

が可能かもしれない...あなたが1行としてファイル全体を読んでいることがあり

dos2unix corpus.txt 

を実行してみてください言い換えれば、例えばtheが17,000回発生します。しかし、かなり多くのようです。

スクリプトでは、ファイル内の行が適切に長いとみなします。ファイルに改行が含まれていない場合は、ファイル全体を$_にメモリにロードしてから、メモリの負荷をsplit倍にして、さらに多くをハッシュに追加します。どのシステムにも負担がかかります。

入力レコードセパレータとして、" "という空白を使用することが考えられます。それは他の空白文字だけを残すことを除いて、あなたがすでに分割していることをおおよそ行います。余分な空白をきれいに整えません。たとえば:

$/ = " "; 
while (<>) { 
    for my $word (split) { # avoid e.g. "foo\nbar" being considered one word 
     if (
       (length($word) >= $min_len) && 
       ($word =~ /^[A-Z][A-Z\'-]+$/) && 
       (index($word,"'") < 0 || allow_apostrophe($word)) 
     ) { 
      $dict{$word}++; 
     } 
    } 
} 

これは、あなたが言葉(とないタブや改行)の間にスペースを持っていると仮定して、でも非常に長い行は、一口サイズのチャンクで読み取ることができるようになります。

+0

これで問題は解決しました。 7200 rpmドライブのデュアルコアラップトップでは、メモリに問題がなくても実行に約1時間かかりました。ありがとう! – Jason

+0

@Jasonようこそ。 – TLP

3

は、いくつかの繰り返しがある場合は、ハッシュに7,2Gbファイルをロード

+0

これは、出力テキストがエディタの寸法だけでラップされるため、可能性があります。 – Jason

関連する問題