解析 Moses 配置文件
Parsing a Moses config file
给定一个来自 Moses Machine Translation Toolkit 的配置文件:
#########################
### MOSES CONFIG FILE ###
#########################
# input factors
[input-factors]
0
# mapping steps
[mapping]
0 T 0
[distortion-limit]
6
# feature functions
[feature]
UnknownWordPenalty
WordPenalty
PhrasePenalty
PhraseDictionaryMemory name=TranslationModel0 num-features=4 path=/home/gillin/jojomert/phrase-jojo/work.src-ref/training/model/phrase-table.gz input-factor=0 output-factor=0
LexicalReordering name=LexicalReordering0 num-features=6 type=wbe-msd-bidirectional-fe-allff input-factor=0 output-factor=0 path=/home/gillin/jojomert/phrase-jojo/work.src-ref/training/model/reordering-table.wbe-msd-bidirectional-fe.gz
Distortion
KENLM lazyken=0 name=LM0 factor=0 path=/home/gillin/jojomert/ru.kenlm order=5
# dense weights for feature functions
[weight]
UnknownWordPenalty0= 1
WordPenalty0= -1
PhrasePenalty0= 0.2
TranslationModel0= 0.2 0.2 0.2 0.2
LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3
Distortion0= 0.3
LM0= 0.5
我需要从 [weights]
部分读取参数:
UnknownWordPenalty0= 1
WordPenalty0= -1
PhrasePenalty0= 0.2
TranslationModel0= 0.2 0.2 0.2 0.2
LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3
Distortion0= 0.3
LM0= 0.5
我一直是这样做的:
def read_params_from_moses_ini(mosesinifile):
parameters_string = ""
for line in reversed(open(mosesinifile, 'r').readlines()):
if line.startswith('[weight]'):
return parameters_string
else:
parameters_string+=line.strip() + ' '
得到这个输出:
LM0= 0.5 Distortion0= 0.3 LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3 TranslationModel0= 0.2 0.2 0.2 0.2 PhrasePenalty0= 0.2 WordPenalty0= -1 UnknownWordPenalty0= 1
然后使用
解析输出
moses_param_pattern = re.compile(r'''([^\s=]+)=\s*((?:[^\s=]+(?:\s|$))*)''')
def parse_parameters(parameters_string):
return dict((k, list(map(float, v.split())))
for k, v in moses_param_pattern.findall(parameters_string))
mosesinifile = 'mertfiles/moses.ini'
print (parse_parameters(read_params_from_moses_ini(mosesinifile)))
获得:
{'UnknownWordPenalty0': [1.0], 'PhrasePenalty0': [0.2], 'WordPenalty0': [-1.0], 'Distortion0': [0.3], 'LexicalReordering0': [0.3, 0.3, 0.3, 0.3, 0.3, 0.3], 'TranslationModel0': [0.2, 0.2, 0.2, 0.2], 'LM0': [0.5]}
当前的解决方案涉及从配置文件中读取一些疯狂的反转行,然后读取非常复杂的正则表达式以获取参数。
有没有更简单或更少hacky/verbose的方法来读取文件并实现所需的参数字典输出?
是否可以更改 configparser 使其读取 moses 配置文件?这很难,因为它有一些错误的部分实际上是参数,例如[distortion-limit]
并且没有值 6
的键。在经过验证的 configparse-able 文件中,它应该是 distortion-limit = 6
.
注意: 本机 python configparser
无法处理 moses.ini
配置文件。 How to read and write INI file with Python3? 的回答无效。
你可以简单地这样做。
x="""#########################
### MOSES CONFIG FILE ###
#########################
# input factors
[input-factors]
0
# mapping steps
[mapping]
0 T 0
[distortion-limit]
6
# feature functions
[feature]
UnknownWordPenalty
WordPenalty
PhrasePenalty
PhraseDictionaryMemory name=TranslationModel0 num-features=4 path=/home /gillin/jojomert/phrase-jojo/work.src-ref/training/model/phrase-table.gz input-factor=0 output-factor=0
LexicalReordering name=LexicalReordering0 num-features=6 type=wbe-msd-bidirectional-fe-allff input-factor=0 output-factor=0 path=/home/gillin/jojomert/phrase-jojo/work.src-ref/training/model/reordering-table.wbe-msd-bidirectional-fe.gz
Distortion
KENLM lazyken=0 name=LM0 factor=0 path=/home/gillin/jojomert/ru.kenlm order=5
# dense weights for feature functions
[weight]
UnknownWordPenalty0= 1
WordPenalty0= -1
PhrasePenalty0= 0.2
TranslationModel0= 0.2 0.2 0.2 0.2
LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3
Distortion0= 0.3
LM0= 0.5"""
print [(i,j.split()) for i,j in re.findall(r"([^\s=]+)=\s*([\d.\s]+(?<!\s))",re.findall(r"\[weight\]([\s\S]*?)(?:\n\[[^\]]*\]|$)",x)[0])]
输出:[('UnknownWordPenalty0', ['1']), ('PhrasePenalty0', ['0.2']), ('TranslationModel0', ['0.2', '0.2', '0.2', '0.2']), ('LexicalReordering0', ['0.3', '0.3', '0.3', '0.3', '0.3', '0.3']), ('Distortion0', ['0.3']), ('LM0', ['0.5'])]
`
这是另一个基于正则表达式的简短解决方案,returns 一个与您的输出相似的值字典:
import re
from collections import defaultdict
dct = {}
str="MOSES_INI_FILE_CONTENTS"
#get [weight] section
match_weight = re.search(r"\[weight][^\n]*(?:\n(?!$|\n)[^\n]*)*", str) # Regex is identical to "(?s)\[weight].*?(?:$|\n\n)"
if match_weight:
weight = match_weight.group() # get the [weight] text
dct = dict([(x[0], [float(x) for x in x[1].split(" ")]) for x in re.findall(r"(\w+)\s*=\s*(.*)\s*", weight)])
print dct
生成的字典内容:
{'UnknownWordPenalty0': [1.0], 'LexicalReordering0': [0.3, 0.3, 0.3, 0.3, 0.3, 0.3], 'LM0': [0.5], 'PhrasePenalty0': [0.2], 'TranslationModel0': [0.2, 0.2, 0.2, 0.2], 'Distortion0': [0.3], 'WordPenalty0': [-1.0]}
逻辑:
- 从文件中取出
[weight]
块。它可以用 r"\[weight][^\n]*(?:\n(?!$|\n)[^\n]*)*"
正则表达式匹配 [weight]
来完成,然后它匹配每个字符任意次数直到双 \n
符号(正则表达式使用展开循环技术和适用于跨越多行的较长文本)。相同的基于惰性点的正则表达式是 [r"(?s)\[weight].*?(?:$|\n\n)"
] 但效率不高(第一个正则表达式为 62 步,第二个正则表达式为 528 步以在当前 MOSES.ini 文件中找到匹配项),但是绝对更具可读性。
- 完成 运行 搜索后,检查匹配项。如果找到匹配项,运行
re.findall(r"(\w+)\s*=\s*(.*)\s*", weight)
方法收集所有键值对。使用的正则表达式是一个简单的 (\w+)\s*=\s*(.*)\s*
匹配并将一个或多个字母数字符号 ((\w+)
) 匹配并捕获到第 1 组中,后跟任意数量的空格,=
,再次任意数量的空格 (\s*=\s*
),然后将除换行符之外的任何符号匹配并捕获到第 2 组,直至字符串末尾。带有后续空间的尾随换行符将用最后的 \s*
. 修剪
- 收集键和值时,后者可以作为使用 comprehension.
解析为浮点值的数字列表返回
没有正则表达式,你可以这样做:
flag = False
result = dict()
with open('moses.ini', 'rb') as fh:
for line in fh:
if flag:
parts = line.rstrip().split('= ')
if len(parts) == 2:
result[parts[0]] = [float(x) for x in parts[1].split()]
else:
break
elif line.startswith('[weight]'):
flag = True
print(result)
循环逐行读取文件,当达到 [weight]
时,标志设置为 True
并且为所有下一行提取 key/value(s)直到空行或文件末尾。
这样,只有当前行被加载到内存中,一旦到达[weight]
块的末尾,程序就停止读取文件。
使用itertools
的另一种方式:
from itertools import *
result = dict()
with open('moses.ini', 'rb') as fh:
a = dropwhile(lambda x: not(x.startswith('[weight]')), fh)
a.next()
for k,v in takewhile(lambda x: len(x)==2, [y.rstrip().split('= ') for y in a]):
result[k] = [float(x) for x in v.split()]
print(result)
给定一个来自 Moses Machine Translation Toolkit 的配置文件:
#########################
### MOSES CONFIG FILE ###
#########################
# input factors
[input-factors]
0
# mapping steps
[mapping]
0 T 0
[distortion-limit]
6
# feature functions
[feature]
UnknownWordPenalty
WordPenalty
PhrasePenalty
PhraseDictionaryMemory name=TranslationModel0 num-features=4 path=/home/gillin/jojomert/phrase-jojo/work.src-ref/training/model/phrase-table.gz input-factor=0 output-factor=0
LexicalReordering name=LexicalReordering0 num-features=6 type=wbe-msd-bidirectional-fe-allff input-factor=0 output-factor=0 path=/home/gillin/jojomert/phrase-jojo/work.src-ref/training/model/reordering-table.wbe-msd-bidirectional-fe.gz
Distortion
KENLM lazyken=0 name=LM0 factor=0 path=/home/gillin/jojomert/ru.kenlm order=5
# dense weights for feature functions
[weight]
UnknownWordPenalty0= 1
WordPenalty0= -1
PhrasePenalty0= 0.2
TranslationModel0= 0.2 0.2 0.2 0.2
LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3
Distortion0= 0.3
LM0= 0.5
我需要从 [weights]
部分读取参数:
UnknownWordPenalty0= 1
WordPenalty0= -1
PhrasePenalty0= 0.2
TranslationModel0= 0.2 0.2 0.2 0.2
LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3
Distortion0= 0.3
LM0= 0.5
我一直是这样做的:
def read_params_from_moses_ini(mosesinifile):
parameters_string = ""
for line in reversed(open(mosesinifile, 'r').readlines()):
if line.startswith('[weight]'):
return parameters_string
else:
parameters_string+=line.strip() + ' '
得到这个输出:
LM0= 0.5 Distortion0= 0.3 LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3 TranslationModel0= 0.2 0.2 0.2 0.2 PhrasePenalty0= 0.2 WordPenalty0= -1 UnknownWordPenalty0= 1
然后使用
解析输出moses_param_pattern = re.compile(r'''([^\s=]+)=\s*((?:[^\s=]+(?:\s|$))*)''')
def parse_parameters(parameters_string):
return dict((k, list(map(float, v.split())))
for k, v in moses_param_pattern.findall(parameters_string))
mosesinifile = 'mertfiles/moses.ini'
print (parse_parameters(read_params_from_moses_ini(mosesinifile)))
获得:
{'UnknownWordPenalty0': [1.0], 'PhrasePenalty0': [0.2], 'WordPenalty0': [-1.0], 'Distortion0': [0.3], 'LexicalReordering0': [0.3, 0.3, 0.3, 0.3, 0.3, 0.3], 'TranslationModel0': [0.2, 0.2, 0.2, 0.2], 'LM0': [0.5]}
当前的解决方案涉及从配置文件中读取一些疯狂的反转行,然后读取非常复杂的正则表达式以获取参数。
有没有更简单或更少hacky/verbose的方法来读取文件并实现所需的参数字典输出?
是否可以更改 configparser 使其读取 moses 配置文件?这很难,因为它有一些错误的部分实际上是参数,例如[distortion-limit]
并且没有值 6
的键。在经过验证的 configparse-able 文件中,它应该是 distortion-limit = 6
.
注意: 本机 python configparser
无法处理 moses.ini
配置文件。 How to read and write INI file with Python3? 的回答无效。
你可以简单地这样做。
x="""#########################
### MOSES CONFIG FILE ###
#########################
# input factors
[input-factors]
0
# mapping steps
[mapping]
0 T 0
[distortion-limit]
6
# feature functions
[feature]
UnknownWordPenalty
WordPenalty
PhrasePenalty
PhraseDictionaryMemory name=TranslationModel0 num-features=4 path=/home /gillin/jojomert/phrase-jojo/work.src-ref/training/model/phrase-table.gz input-factor=0 output-factor=0
LexicalReordering name=LexicalReordering0 num-features=6 type=wbe-msd-bidirectional-fe-allff input-factor=0 output-factor=0 path=/home/gillin/jojomert/phrase-jojo/work.src-ref/training/model/reordering-table.wbe-msd-bidirectional-fe.gz
Distortion
KENLM lazyken=0 name=LM0 factor=0 path=/home/gillin/jojomert/ru.kenlm order=5
# dense weights for feature functions
[weight]
UnknownWordPenalty0= 1
WordPenalty0= -1
PhrasePenalty0= 0.2
TranslationModel0= 0.2 0.2 0.2 0.2
LexicalReordering0= 0.3 0.3 0.3 0.3 0.3 0.3
Distortion0= 0.3
LM0= 0.5"""
print [(i,j.split()) for i,j in re.findall(r"([^\s=]+)=\s*([\d.\s]+(?<!\s))",re.findall(r"\[weight\]([\s\S]*?)(?:\n\[[^\]]*\]|$)",x)[0])]
输出:[('UnknownWordPenalty0', ['1']), ('PhrasePenalty0', ['0.2']), ('TranslationModel0', ['0.2', '0.2', '0.2', '0.2']), ('LexicalReordering0', ['0.3', '0.3', '0.3', '0.3', '0.3', '0.3']), ('Distortion0', ['0.3']), ('LM0', ['0.5'])]
`
这是另一个基于正则表达式的简短解决方案,returns 一个与您的输出相似的值字典:
import re
from collections import defaultdict
dct = {}
str="MOSES_INI_FILE_CONTENTS"
#get [weight] section
match_weight = re.search(r"\[weight][^\n]*(?:\n(?!$|\n)[^\n]*)*", str) # Regex is identical to "(?s)\[weight].*?(?:$|\n\n)"
if match_weight:
weight = match_weight.group() # get the [weight] text
dct = dict([(x[0], [float(x) for x in x[1].split(" ")]) for x in re.findall(r"(\w+)\s*=\s*(.*)\s*", weight)])
print dct
生成的字典内容:
{'UnknownWordPenalty0': [1.0], 'LexicalReordering0': [0.3, 0.3, 0.3, 0.3, 0.3, 0.3], 'LM0': [0.5], 'PhrasePenalty0': [0.2], 'TranslationModel0': [0.2, 0.2, 0.2, 0.2], 'Distortion0': [0.3], 'WordPenalty0': [-1.0]}
逻辑:
- 从文件中取出
[weight]
块。它可以用r"\[weight][^\n]*(?:\n(?!$|\n)[^\n]*)*"
正则表达式匹配[weight]
来完成,然后它匹配每个字符任意次数直到双\n
符号(正则表达式使用展开循环技术和适用于跨越多行的较长文本)。相同的基于惰性点的正则表达式是 [r"(?s)\[weight].*?(?:$|\n\n)"
] 但效率不高(第一个正则表达式为 62 步,第二个正则表达式为 528 步以在当前 MOSES.ini 文件中找到匹配项),但是绝对更具可读性。 - 完成 运行 搜索后,检查匹配项。如果找到匹配项,运行
re.findall(r"(\w+)\s*=\s*(.*)\s*", weight)
方法收集所有键值对。使用的正则表达式是一个简单的(\w+)\s*=\s*(.*)\s*
匹配并将一个或多个字母数字符号 ((\w+)
) 匹配并捕获到第 1 组中,后跟任意数量的空格,=
,再次任意数量的空格 (\s*=\s*
),然后将除换行符之外的任何符号匹配并捕获到第 2 组,直至字符串末尾。带有后续空间的尾随换行符将用最后的\s*
. 修剪
- 收集键和值时,后者可以作为使用 comprehension. 解析为浮点值的数字列表返回
没有正则表达式,你可以这样做:
flag = False
result = dict()
with open('moses.ini', 'rb') as fh:
for line in fh:
if flag:
parts = line.rstrip().split('= ')
if len(parts) == 2:
result[parts[0]] = [float(x) for x in parts[1].split()]
else:
break
elif line.startswith('[weight]'):
flag = True
print(result)
循环逐行读取文件,当达到 [weight]
时,标志设置为 True
并且为所有下一行提取 key/value(s)直到空行或文件末尾。
这样,只有当前行被加载到内存中,一旦到达[weight]
块的末尾,程序就停止读取文件。
使用itertools
的另一种方式:
from itertools import *
result = dict()
with open('moses.ini', 'rb') as fh:
a = dropwhile(lambda x: not(x.startswith('[weight]')), fh)
a.next()
for k,v in takewhile(lambda x: len(x)==2, [y.rstrip().split('= ') for y in a]):
result[k] = [float(x) for x in v.split()]
print(result)