2011-11-14 117 views
3

我试图根据英文wikipedia转储中找到的前100K个单词建立一个n-gram语言模型。我已经使用用Java编写的修改过的XML解析器提取了纯文本,但需要将其转换为vocab文件。解决执行Perl脚本时出现内存不足错误

为了做到这一点,我找到了一个据说可以完成这项工作的perl脚本,但是缺少关于如何执行的说明。不用说,我是一个Perl的新手,这是我第一次遇到它的使用需求。

当我运行此脚本时,在使用4GB RAM和runnung Ubuntu 10.04和10.10的两个独立双核心机器上的7.2GB文本文件上使用此脚本时,出现内存不足错误。

当我联系作者时,他说这个脚本在配备4GB RAM的MacBook Pro上运行良好,并且在使用perl 5.12的6.6GB文本文件上执行时,总内存使用量约为78 MB。作者还说,该脚本逐行读取输入文件,并在内存中创建一个散列表。

的脚本是:

#! /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

加载7,2Gb文件转换成散列可能是可能的,如果有一些重复用词,例如the发生17,000次,等等。但它似乎相当多。

您的脚本假定文件中的行适当长。如果你的文件不包含换行符,你将把整个文件加载到$_的内存中,然后使用split加倍加载内存,然后在你的散列中添加更多内容。这会对任何系统造成压力。

一个想法可能是使用空间" "作为输入记录分隔符。它将大致执行你已经在使用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驱动器的双核笔记本电脑上,大约需要一个小时才能执行,而且不会出现内存问题。谢谢! – Jason

+0

@Jason不客气。 – TLP

3

尝试运行

dos2unix corpus.txt 

这可能是您正在阅读的整个文件作为一行...

+0

这可能是一种可能性,因为输出文本仅包含编辑器的尺寸... – Jason

相关问题