滨州树库标注实例
句法模型训练最基础的一步,就是从树库中抽取规则。而规则是由一些非终结符,词汇等信息组成的,所以Training第一步是要能提取这些信息。滨州树库(Penn Tree Bank) WSJ mrg标注风格的树库是这样的。
1: ( (S
2: (NP-SBJ
3: (NP (NNP Pierre) (NNP Vinken) )
4: (, ,)
5: (ADJP
6: (NP (CD 61) (NNS years) )
7: (JJ old) )
8: (, ,) )
9: (VP (MD will)
10: (VP (VB join)
11: (NP (DT the) (NN board) )
12: (PP-CLR (IN as)
13: (NP (DT a) (JJ nonexecutive) (NN director) ))
14: (NP-TMP (NNP Nov.) (CD 29) )))
15: (. .) ))
很明显这种树的标记由三类不同的符号组成。左括号(,右括号),以及像S、NP-SBJ、director这样的字符串。
树库Token读取类EdgeLexer
Spear中提供了一个类EdgeLexer来读取这三种Token,并且从文件角度考虑加入了一个终止的Token。这个类在模型训练的时候,复杂读取这四种Token,并且在Token是字符串的情况下,返回读取的内容。
EdgeLexer的声明如下所示。
1: class EdgeLexer
2: {
3: public:
4: /*几种不同的Token*/
5: static const int TOKEN_EOF = 0;/*终止符*/
6: static const int TOKEN_STRING = 1;/*字符串*/
7: static const int TOKEN_LP = 2;/*左括号*/
8: static const int TOKEN_RP = 3;/*右括号*/
9: EdgeLexer(IStream &);
10: /*核心函数*/
11: int lexem(String &);
12: int getLineCount() const { return _lineCount; };
13: private:
14: /** The stream */
15: IStream & _stream;
16: /** Line count */
17: int _lineCount;
18: /** Advance over white spaces */
19: void skipWhiteSpaces();
20: bool isSpace(Char c) const;
21: };
从代码的声明可以看出,EdgeLexer完成了行数统计,空白符判断,Token读取的三种行为。EdgeLexer的实现如下所示。
1: /**构造函数*/
2: EdgeLexer::EdgeLexer(IStream & stream)
3: : _stream(stream), _lineCount(1){}
4: /**判断一个字符是否是空白符
5: *@ c 要判断的字符
6: */
7: bool EdgeLexer::isSpace(Char c) const
8: {
9: if(c != W(' ') &&
10: c != W('\t') &&
11: c != W('\n') &&
12: c != W('\r')){
13: return false;
14: }
15: return true;
16: }
17: /**跳过空白符**/
18: void EdgeLexer::skipWhiteSpaces()
19: {
20: Char c;
21: while((c = _stream.get()) != EOF && isSpace(c)){
22: if(c == W('\n')){
23: _lineCount ++;
24: }
25: }
26: _stream.unget();
27: }
28: /**判断是否是空白、左右括号的宏*/
29: #define STRING_CHAR(c) ( \
30: c != W('(') && \
31: c != W(')') && \
32: ! isSpace(c) \
33: )
34: /**
35: *读一个词条,并且返回词的类型,将词的内容存到text中
36: *如果不是STRING,而是括号,EOF,则只返回类型,不返回内容
37: *@text 存储返回的字符串
38: *如果终止则返回TOKEN_EOF,如果为字符串则返回TOKEN_STRING
39: */
40: int EdgeLexer::lexem(String & text)
41: {
42: skipWhiteSpaces();
43: Char c = _stream.get();
44: if(c == EOF){
45: return TOKEN_EOF;
46: } else if(c == W('(')){
47: return TOKEN_LP;
48: } else if(c == W(')')){
49: return TOKEN_RP;
50: } else if(STRING_CHAR(c)){
51: OStringStream buffer;
52: buffer << c;
53: while((c = _stream.get()) != TOKEN_EOF && STRING_CHAR(c)){
54: buffer << c;
55: }
56: text = buffer.str();
57: _stream.unget();
58: return TOKEN_STRING;
59: }
60: // should never get here
61: return TOKEN_EOF;
62: }
EdgeLexer的使用实例
读取TreeBank文件,输出所有的非终结符和词汇信息。
1: #include <fstream>
2: using namespace std;
3: int main(int argc,char **argv)
4: {
5: if(argc!=2){
6: printf("[Usage]:%s [treebank]\n",argv[0]);
7: exit(0);
8: }
9: ifstream is(argv[1]);
10: EdgeLexer lex(is);
11: string text;
12: int l;
13: while((l = lex.lexem(text)) != EdgeLexer::TOKEN_EOF){
14: //cout << l;
15: if(l == EdgeLexer::TOKEN_STRING){//输出字符串
16: cout << " " << text;
17: cout <<endl;
18: }
19: //cout << endl;
20: }
21: }