【问题标题】:Parsing a Moses config file解析 Moses 配置文件
【发布时间】:2016-03-12 01:20:30
【问题描述】:

给定一个来自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]}

当前的解决方案涉及从配置文件中读取一些疯狂的反转行,然后读取非常复杂的正则表达式来获取参数。

是否有更简单或更简单的方法来读取文件并实现所需的参数字典输出?

是否可以更改 configparser 使其读取 moses 配置文件?这很难,因为它有一些实际上是参数的错误部分,例如[distortion-limit] 并且没有值 6 的密钥。在经过验证的 configparse-able 文件中,它应该是 distortion-limit = 6。


注意:原生 python configparser 无法处理 moses.ini 配置文件。 How to read and write INI file with Python3? 的回答无效。

【问题讨论】:

  • 如果this post 不适合您,请告知。
  • @stribizhev,答案不起作用,如问题中所述,标准配置解析器无法使用没有密钥的错误参数。
  • [input-factors]\\n0\\n 之类的东西会导致 ConfigParser 失败。
  • 这很简单。可以使用正则表达式来完成。不同之处在于它可以有不同的键/值形式,机器人单个和多个,具体取决于哪个部分。这意味着这些表格是基于恒定部分预先设计的。我是对的吗?那么,您是要解析整个内容,还是只解析 weight 部分?如果您只是在寻找该部分,则可以使用 import regex 然后使用 \G 锚来查找键/值。无需将其分解并加入特殊形式。

标签: python regex parsing configparser moses


【解决方案1】:

你可以简单地做到这一点。

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'])] `

【讨论】:

  • 你能解释一下正则表达式吗?谢谢!
  • @alvas 这个正则表达式只是取出块[weight] 然后解析它的内容。
  • 不,它并没有真正起作用,它应该得到('TranslationModel0', ['0.2', '0.2', '0.2' ,'0.2' ])而不是('TranslationModel0', '0.2')
【解决方案2】:

这是另一个基于正则表达式的简短解决方案,它返回与您的输出相似的值的字典:

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

见IDEONE demo

生成的字典内容:

{'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] 块。可以使用与 [weight] 字面匹配的 r"\[weight][^\n]*(?:\n(?!$|\n)[^\n]*)*" 正则表达式来完成,然后它匹配每个字符任意多次,直到出现双 \n 符号(正则表达式使用展开循环技术并且适用于较长的文本跨越几行)。相同的基于惰性点的正则表达式是 [r"(?s)\[weight].*?(?:$|\n\n)"] 但效率不高(第一个正则表达式需要 62 步,而第二个正则表达式需要 528 步才能在当前 MOSES.ini 文件中找到匹配项),但绝对更具可读性.
  • 运行搜索后,检查匹配项。如果找到匹配,则运行re.findall(r"(\w+)\s*=\s*(.*)\s*", weight) 方法收集所有键值对。使用的正则表达式是一个简单的(\w+)\s*=\s*(.*)\s* 匹配并捕获到第 1 组中的一个或多个字母数字符号 ((\w+)),后跟任意数量的空格 =,再次是任意数量的空格 (\s*=\s*),然后匹配并将除换行符之外的任何符号捕获到第 2 组中,直到字符串末尾。带有后续空格的尾随换行符被最后的 \s* 修剪。
  • 在收集键和值时,后者可以返回为使用comprehension解析为浮点值的数字列表。

【讨论】:

  • 从技术上讲,如果[weight] 部分不是最后一个部分,r"\[weight][^\n]* 仍然可以工作,对吧?因为它一直读到换行符。
  • @alvas:抱歉,我试图将整个代码塞进successive_match 方法中的解决方案过于复杂。实际上,两个正则表达式的解决方案确实是最方便、可读且更高效的。我已经用新的答案替换了原来的答案。请注意,r"\[weight][^\n]*" 根本不会完成这项工作,因为它不会匹配整个 [weight] 块。它一直持续到双换行符或字符串结尾。展开的正则表达式是完成该任务的最有效的正则表达式(当然对于正则表达式)。请查看我的解释并询问我是否有任何不清楚的地方。
【解决方案3】:

没有正则表达式,你可以这样做:

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,并且为所有下一行提取键/值,直到空行或结尾文件。

这样,只有当前行被加载到内存中,一旦到达[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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-16
    • 2021-08-14
    • 1970-01-01
    • 2019-03-22
    • 1970-01-01
    • 2012-08-31
    • 2012-02-16
    • 1970-01-01
    相关资源
    最近更新 更多