Reuters-21578数据集预处理遇到的血泪史

Reuters-21578 是文本分析任务中比较重要的数据集,但是它不像20Newsgroup是现成的纯文本,而是SGML格式的文件,需要我们进行预处理。

插播一条:如果是做词袋模型,那么公开数据集大家已经处理的很成熟了,可以借鉴的如,Deng Cai的工作:
http://www.cad.zju.edu.cn/home/dengcai/Data/TextData.html

里面可以下载TDT2, Reuters21578, Reuters21578, RCV1@Top4 的Matlab格式。

但是用于DNN的话,基于词袋模型的数据集就无可以用之地,因而我们还是要老老实实的提取纯文本数据。

Reuters21578数据集的下载地址:http://www.daviddlewis.com/resources/testcollections/reuters21578/

Reuters21578提前要注意的是:
===============下面摘自andy_tsg 的经验分享=====================
1. 这个数据集中的所有记录并不是一股脑拿来全部都用的,学术界有好几种划分的方法,通常最最常用的是ModApte划分方法(关于如何划分的细节可以参见下载包里README.txt文件的介绍);

2. 这些数据文件貌似是有一定的格式的,我刚开始也试图把他们当做标准的xml文档来处理(因为下载包里还像模像样的包含了一个SGML DTD 的文件),但老是报错。最终发现很多的记录格式是错误的,而且错误千奇百怪。所以干脆放弃,直接把它们全部看做文本文件来处理得了;

3. 并不是所有按照ModApte划分得到的记录都能拿来使用,因为有些记录的与之间并没有包含任何的topic信息(比如reut2-000.sgm文件的第307行记录,写个程序可以检测出有不少这种情况,仅举一例),所以只好丢弃;

4. 并不是所有的包含topic的记录都包含有< BODY >字段和< TITLE >字段(比如reut2-000.sgm文件中的第3099行记录,同样这种情况也有不少),但是因为这种情况通常在< TEXT >字段中还是包含了一些文字信息的,所以我选择并不将它丢弃;

5. 由于有4,所以我处理时并不是只将< BODY >字段和< TITLE >字段看作记录的正文,而是将去除所以包含在成对的之间的内容和有关topic的内容后得到的所剩下的部分当做记录正文来看待。这样处理的后果是包含了< DATA >、< PLACES >、< PEOPLE >、< ORGS >、< EXCHANGES >、< COMPANIES >、< UNKNOWN >等字段的信息。但实际上这些字段提供的信息相对是很少的,而且如果有提供的话也是跟记录的正文有关联的,所以我认为这种处理方式还是比较合适的;

6. 抽取出记录内容后将其中所有的非英文字符去除,并将英文字符全部转换为小写。
======================分享完毕===================================

感谢Craig A. Struble 的无私奉献精神,公开了文本转换的python源码:

  1. #!/usr/bin/env python   
  2. “”"Convert Reuters 21578 SGML files to text files
  3.  
  4. This program converts Reuters SGML files from David D. Lewis into text  
  5. files usable for NLP analysis. Each document is created into its own  
  6. text file instead of keeping the files containing approximately 1000  
  7. documents. Newlines within the body of the text are collapsed to make  
  8. the sentence identification task more challenging.  
  9.  
  10. Usage:  
  11.     reuters2txt.py filename  
  12.  
  13. Example:  
  14.     reuters2txt.py reut2-000.sgm  
  15.  
  16. Known Bugs:  
  17.     - Does not handle documents containing unprocessed text.  
  18. “”"  
  19.   
  20. ###############################################################################   
  21. # Program information   
  22. ###############################################################################   
  23. __author__ = ”Craig A. Struble”   
  24. __date__ = ”23 August 2005″   
  25. __version__ = ”$Revision: 1.1.1.1 $”   
  26. __credits__ = “”"David D. Lewis, the creator of the Reuters collection
  27. Yuen-Hsien Tseng, wrote perl tools to do something similar  
  28. “”"  
  29.   
  30. ###############################################################################   
  31. # Imports   
  32. ###############################################################################   
  33. import sgmllib  
  34.   
  35. ###############################################################################   
  36. # ReutersParser - an SGML parser   
  37. ###############################################################################   
  38. class ReutersParser(sgmllib.SGMLParser):   
  39.     “”"A class to parse text from Reuters SGML files.”"”  
  40.   
  41.     def parse(self, s):   
  42.         “”"Parse the given string ’s', which is an SGML encoded file.”"”  
  43.   
  44.         self.feed(s)   
  45.         self.close()   
  46.   
  47.     def __init__(self, verbose=0):   
  48.         “”"Initialize an object, passing ’verbose’ to the superclass.”"”  
  49.   
  50.         sgmllib.SGMLParser.__init__(self, verbose)   
  51.         self.in_title = 0   
  52.         “”"Flag indicating whether or not we’re parsing the title.”"”  
  53.   
  54.         self.in_dateline = 0   
  55.         “”"Flag indicating whether or not we’re parsing the dateline”"”  
  56.   
  57.         self.in_body = 0   
  58.         “”"Flag indicating whether or not we’re parsing the body”"”  
  59.   
  60.         self.title = ”"   
  61.         “”"Title of the document”"”  
  62.   
  63.         self.doc_id = 0   
  64.         “”"Document ID”"”  
  65.   
  66.         self.dateline = ”"   
  67.         “”"Date line for the document”"”  
  68.   
  69.         self.body = ”"   
  70.         “”"Body of the document”"”  
  71.   
  72.     def handle_data(self, data):   
  73.         “”"Print out data in TEXT portions of the document.”"”  
  74.   
  75.         if self.in_body:   
  76.             self.body += data   
  77.         elif self.in_title:   
  78.             self.title += data   
  79.         elif self.in_dateline:   
  80.             self.dateline += data   
  81.   
  82.     ####   
  83.     # Handle the Reuters tag   
  84.     ####   
  85.     def start_reuters(self, attributes):   
  86.         “”"Process Reuters tags, which bracket a document. Create a new
  87.         file for each document encountered.  
  88.         ”"”  
  89.   
  90.         for name, value in attributes:   
  91.             if name == ”newid”:   
  92.                 self.doc_id = value   
  93.   
  94.     def end_reuters(self):   
  95.         “”"Write out the contents to a file and reset all variables.”"”  
  96.   
  97.         from textwrap import fill   
  98.         import re  
  99.   
  100.         # Print out the contents to a file. For the body of the   
  101.         # text, merge into 70 character lines using python’s fill   
  102.         # utility   
  103.         filename = ”text/” + str(self.doc_id) + ”.txt”   
  104.         doc_file = open(filename, ”w”)   
  105.         doc_file.write(self.title + ”\n”)   
  106.         doc_file.write(self.dateline + ”\n”)   
  107.         # Strip out multiple spaces in the body   
  108.         self.body = re.sub(r’\s+’, r’ ’, self.body)   
  109.         doc_file.write(fill(self.body) + ”\n”)   
  110.         doc_file.close()   
  111.   
  112.         # Reset variables   
  113.         self.in_title = 0   
  114.         self.in_dateline = 0   
  115.         self.in_body = 0   
  116.         self.doc_id = 0   
  117.         self.title = ”"   
  118.         self.body = ”"   
  119.         self.dateline = ”"   
  120.   
  121.     ####   
  122.     # Handle TITLE tags   
  123.     ####   
  124.     def start_title(self, attributes):   
  125.         “”"Indicate that the parser is in the title portion of the document.
  126.         ”"”  
  127.   
  128.         self.in_title = 1   
  129.   
  130.     def end_title(self):   
  131.        “”"Indicate that the parser is no longer in the title portion of the
  132.        document.  
  133.        ”"”  
  134.   
  135.        self.in_title = 0   
  136.   
  137.     ####   
  138.     # Handle DATELINE tags   
  139.     ####   
  140.     def start_dateline(self, attributes):   
  141.         “”"Indicate that the parser is in the dateline portion of the document.
  142.         ”"”  
  143.   
  144.         self.in_dateline = 1   
  145.   
  146.     def end_dateline(self):   
  147.        “”"Indicate that the parser is no longer in the dateline portion of the
  148.        document.  
  149.        ”"”  
  150.   
  151.        self.in_dateline = 0   
  152.   
  153.     ####   
  154.     # Handle BODY tags   
  155.     ####   
  156.     def start_body(self, attributes):   
  157.         “”"Indicate that the parser is in the body portion of the document.
  158.         ”"”  
  159.   
  160.         self.in_body = 1   
  161.   
  162.     def end_body(self):   
  163.        “”"Indicate that the parser is no longer in the body portion of the
  164.        document.  
  165.        ”"”  
  166.   
  167.        self.in_body = 0   
  168.   
  169. ###############################################################################   
  170. # Main Program   
  171. ###############################################################################   
  172. import sys  
  173. import os  
  174. import os.path   
  175.   
  176. if __name__ == ’__main__‘:   
  177.     # Get the filename as the first argument of the program,   
  178.     # open and read the file.   
  179.     # filename = sys.argv[1]   
  180.     filename = ”reut2-000.sgm”   
  181.     f = open(filename, ”r”)   
  182.     s = f.read()   
  183.   
  184.     # Create a text directory if one does not exist   
  185.     if not os.path.isdir(“text”):   
  186.         os.mkdir(“text”)   
  187.   
  188.     # Parse the file and output the results   
  189.     parser = ReutersParser()   
  190.     parser.parse(s)  

这里有一份处理好的单标签数据集,但是预处理的过多,暂且放到一遍,有需要的同学,可以自取:http://web.ist.utl.pt/acardoso/datasets/

即将即将开始前的说明:
Reuters-21578语料说明
1,Reuters-21578
Reuters-21578分布在22个文件中,从reu2-000.dgm到reut2-020.sgm每个文件包含1000个文档,reut2-021.sgm包含578个文档。
(1)文件格式:
22个文件每个都以一个文档类型声明开始,格式如下:
< !DOCTYPE levis SYSTEM “levis.dtd” >
每篇文档都以以下以标记开始(其中??代表一个恰当的值):
< REUTERS TOPICS=?? LEWISSPLIT=?? CGISPLIT=?? ULDID=?? NEWID=?? >
每篇文档都以标记< /REUTERS >作为结尾。每个REUTERS标记都包含TOPICS、LEWISSPLIT、CGISPLIT、ULDID和NEWID五个属性,每个属性的含义如下:
 TOPIC:它的值可能是YES、NO或BYPASS。
 LEWISSPLIT:它的值可能是TRAINING、TEST或UOT_USED。TRAINING代表当前文档在实验LEWIS91d、LEWIS92b、LEWIS92e、LEWIS94b中被当作训练样本使用;TEST代表当前文档在上述实验中被作为测试样本使用;NOT-USED代表当前样本在上述实验中没有使用。
 CGISPLIT:它的可能值是TRAINING-SET或PUBLISHED-TESTSET。表示当前文档是否在实验HAYES89和HAYES90b中作为训练样本和测试样本使用。
 OLDID:代表每篇文档在语料REUTERS-22173中的ID。
 NEWID:代表每篇文档在语料REUTERS-21578中的ID,ID值按照年代顺序进行分配的。
此外,一些TEUTERS标记中还包含第六个属性CSECS,这个标记可以忽略。
(2)文档内部标记:
正如< RETUTERS >和< /RETUTERS >是用来分割文件中的每一篇文档的,还有一些标记用来区分每篇文档中的不同元素。为了便于描述这些标记,我们先介绍一下有关标记的一些规定。如果一个标记在一篇文档中只能出现一次,我们使用ONCE来注解,否则使用VARIABLE;如果一个标记的开始标记和结束标记必须出现在同一行上,我们使用SAMELINE来注解。
 < DATE >,< /DATE >[ONCE,SAMPLE]:表示当前文档的日期和时间;
 < MKNOTE >,< /MKNOTE >[VARIABLE]:Setve Finch对原始Retuers语料所做的某些手工校正的注解;
 < TOPICS >,< TOPICS >[ONCE,SAMPLE]:表示当前文档所属TOPICS类型的类型列表,每个类型间使用< D >和< /D >分割开;
 < PLACES >,< /PLACES >[ONCE,SAMPLE]:表示当前文档所属PLACES类型的类型列表,每个类型间使用< D >和< /D >分割开;
 < PEOPLES >,< /PEOPLES >[ONCE,SAMPLE]:表示当前文档所属PEOPLES类型的类型列表,每个类型间使用< D >和< /D >分割开;
 < ORGS >,< /ORGS >[ONCE,SAMPLE]:表示当前文档所属ORGS类型的类型列表,每个类型间使用< D >和< /D >分割开;
 < EXCHANGES >,< /EXCHANGES >[ONCE,SAMPLE]:表示当前文档所属EXCHANGES类型的类型列表,每个类型间使用< D >和< /D >分割开;
 < COMPANIES >,< /COMPANIES >[ONCE,SAMPLE]:表示当前文档所属COMPANIES类型的类型列表,每个类型间使用< D >和< /D >分割开;
 < UNKNOWN >,< /UNKNOWN >[ONCE,SAMPLE]:这个标记用来包括Retuers语料中的控制符和一些噪声文本,或某些难解的素材;
 < TEXT >,< /TEXT >[ONCE]:用来表示当前文档中的文本信息。这些文本信息中也许会包括一些控制符和垃圾素材,而且空格也保留下来。< TEXT >具有以下属性:
 TYPE:它的可能值为NORM、BRIEF或UNPROC。NORM是此属性的默认值,表示文档中的普通文本,在这种情况下TEXT标记以简单的形式< TEXT >出现。BRIEF表示当前文档仅仅是一些一两行的短文。UNPROC表示文档的格式在某些风格方面不同寻常,限制了我们对它进行进一步的结构化。
 < AUTHOR >,< /AUTHOR >:表示当前文档的作者。
 < DATELINE >,< /DATELINE >:表示当前文档的原始出处和日期。
 < TITLE >,< /TITLE >:表示当前文档的标题。
 < BODY >,< /BODY >:表示当前文档的内容。
(3)类型:
Reuters-21578语料共有5套分类体系(Category Set),这五套分类体下如下表所示:

Number of Number of Categories Number of Categories
Category Set Categories w/ 1+ Occurrences w/ 20+ Occurrences
************ ********** ******************** ********************
EXCHANGES 39 32 7
ORGS 56 32 9
PEOPLE 267 114 15
PLACES 175 147 60
TOPICS 135 120 57

TOPICS是一个有关经济的类别体系,例如:coconut, gold, inventories和money-supply等,过去的研究几乎都是使用的这个分类体系。HAYES90b中讨论了是依据什么原则来确定一篇文档应该属于TOPICS中的哪一个类别的。EXCHANGES, ORGS,PEOPLE和PLACES类型对应于命名实体类型,例如:nasdaq(EXCHANGES), gatt(ORGS), perez-de-cuellar(PEOPLE), australia(PLACES)等。但是,不是所有文档都对应着这四个分类体系中的一个类别,因为必须要求文档的内容充分体现实体。
Reuters-21578包含了五个描述每个分类体系中包含类别的文件,文件名如下:
all-exchanges-strings.lc.txt,
all-orgs-strings.lc.txt,
all-people-strings.lc.txt,
all-places-strings.lc.txt,
all-topics-strings.lc.txt。
还有一个文件cat-descriptions_120396.txt描述了一些分类体系的附属信息。

Note that a sixth category field, COMPANIES, was present in the
original Reuters materials distributed by Carnegie Group, but no
company information was actually included in these fields. In the
Reuters-21578 collection this field is always empty.

In the table above we note how many categories appear in at least 1 of
the 21,578 documents in the collection, and how many appear at least
20 of the documents. Many categories appear in no documents, but we
encourage researchers to include these categories when evaluating the
effectiveness of their categorization system.

Additional details of the documents, categories, and corpus
preparation process appear in LEWIS92b, and at greater length in
Section 8.1 of LEWIS91d.

(4)文件示例:
< !DOCTYPE lewis SYSTEM “lewis.dtd” >
< REUTERS TOPICS=”YES” LEWISSPLIT=”TRAIN” CGISPLIT=”TRAINING_SET” OLDID=”5544” NEWID=”1” >
< DATE >26-FEB-1987 15:01:01.79< /DATE >
< TOPICS >< D >cocoa< /D >< TOPICS >
< PLACES >< D >el_Salvador< /D >< D >usa< /D >< D >Uruguay< /D >< PLACES >
< PEOPLE >< /PEOPLE >
< ORGS >< /ORGS >
< EXCHANGES< < /EXCHANGES >
< COMPANIES >< /COMPANIES >
< UNKNOWN >
C T
  f0704reute
u f BC_BAHIA-COCOA-REVIEW 02-26 0105< /UNKNOWN >
< TEXT >
< TITLE >BAHIA COCOA REVIEW< /TITLE >
< DATELINE >SALVADOR, Feb 26 – < /DATELINE >
< BODY >Showers continued throughout the week in the Bahia cocoa zone, alleviating the drought since early January and improving prospects for the coming temporao, although normal humidity levels have not been restored, Comissaria Smith said in its weekly review.
The dry period means the temporao will be late this year.
Arrivals for the week ended February 22 were 155,221 bags of 60 kilos making a cumulative total for the season of 5.93 mln against 5.81 at the same stage last year. Again it seems that cocoa delivered earlier on consignment was included in the arrivals figures.
.
.
Final figures for the period to February 28 are expected to be published by the Brazilian Cocoa Trade Commission after carnival which ends midday on February 27.
Reuter
< /BODY >< /TEXT >
< /REUTERS >
< REUTERS TOPICS=”NO” LEWISSPLIT=”TRAIN” CGISPLIT=”TRAINING_SET” OLDID=”5545” NEWID=”2” >

2,文件cate90.test.smart.txt和cate90.train.smart.txt说明

(1) 标记
a) .I 对应于REUTERS标记中的NEWID属性
b) .C 这个标记的下一行列出当前文章所属的类别
c) .T 这个标记的下一行列出当前文章的标题
d) .W 这个标记的下一行列出当前文章的内容
(2) Modapte划分:这个划分将整个文档集划分为9603个训练文档和3299个测试文档。其中135个主体类别(topic categories)只有使用了90个类别,这些类别中至少包含一个训练文档和一个测试文档。

【参考】:
1. 感谢andy_tsg的经验分享http://blog.163.com/andy_tsg/blog/static/1617002462010521027588
2. 感谢 read.pudn.com/downloads106/doc/437296/21578.doc 对Reuters21578数据集的详细说明。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>