7

所以我正在学习自然语言处理课程,我需要创建一个三元语言模型来根据一些样本数据生成在一定程度上看起来“真实”的随机文本。

本质上需要创建一个“trigram”来保存各种 3 个字母的语法单词组合。我的教授暗示,这可以通过拥有我试图创建的字典字典来完成:

trigram = defaultdict( defaultdict(defaultdict(int)))

但是我收到一条错误消息:

trigram = defaultdict( dict(dict(int)))
TypeError: 'type' object is not iterable

我将如何创建一个 3 层嵌套字典或int值字典的字典?

如果人们不知道如何回答,我猜人们会否决有关堆栈溢出的问题。我将添加一些背景知识,以便为那些愿意提供帮助的人更好地解释这个问题。

此三元组用于跟踪三字模式。它们用于文本语言处理软件,几乎无处不在自然语言处理“现在想想 siri 或谷歌”。

如果我们将字典的 3 级指定为dict1 dict2 和 dict3则解析文本文件并读取语句“The boy runs”将具有以下内容:

具有“the”键的 dict1。访问该密钥将返回包含密钥“boy”的 dict2。访问该键将返回包含键“运行”的最终 dict3,现在访问该键将返回值 1。

这象征着在本文中“男孩跑”出现了1次。如果我们再次遇到它,那么我们将遵循相同的过程并将 1 增加到 2。如果我们遇到“the girl walks”,那么 dict2 的“the”键字典现在将包含另一个“girl”键,其中 dict3 的键为“walks”,值为 1,依此类推。最终在解析大量文本(并跟踪字数)之后,您将拥有一个三元组,它可以根据它们在先前解析的文本中出现的频率来确定某个起始词导致 3 个词组合的可能性.

这可以帮助您创建语法规则来识别语言,或者在我的情况下创建看起来非常像语法英语的随机生成的文本。我需要一个三层字典,因为在 3 个单词组合的任何位置都可能有另一个单词可以创建一组完全不同的组合。我尽我所能解释三元组及其背后的目的……当然,我几周前刚刚讲过这门课。

现在……说了这么多。我将如何创建一个字典的字典,其基本字典在 python 中包含 int 类型的值?

trigram = defaultdict(defaultdict(defaultdict(int)))

为我抛出错误

4

4 回答 4

13

我之前尝试过嵌套defaultdict的,解决方案似乎是一个lambda电话:

trigram = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))

trigram['a']['b']['c'] += 1

它不漂亮,但我怀疑嵌套字典的建议是为了高效查找。

于 2013-09-28T04:08:30.627 回答
6

通常,要创建一个嵌套的 trigrams 字典,已经发布的解决方案可能会起作用。如果您想扩展这个想法以获得更通用的解决方案,您可以执行以下操作之一,其中一个是从Perl 的 AutoVivification中采用的,另一个是使用collection.defaultdict

解决方案1:

class ngram(dict):
    """Based on perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return super(ngram, self).__getitem__(item)
        except KeyError:
            value = self[item] = type(self)()
            return value

解决方案2:

from collections import defaultdict
class ngram(defaultdict):
    def __init__(self):
        super(ngram, self).__init__(ngram)

使用解决方案 1 进行演示

>>> trigram = ngram()
>>> trigram['two']['three']['four'] = 4
>>> trigram
{'two': {'three': {'four': 4}}}
>>> a['two']
{'three': {'four': 4}}
>>> a['two']['three']
{'four': 4}
>>> a['two']['three']['four']
4

使用解决方案 2 进行演示

>>> a = ngram()
>>> a['two']['three']['four'] = 4
>>> a
defaultdict(<class '__main__.ngram'>, {'two': defaultdict(<class '__main__.ngram'>, {'three': defaultdict(<class '__main__.ngram'>, {'four': 4})})})
于 2013-09-28T04:47:24.090 回答
1

defaultdict __init__方法需要一个可调用的参数。传递给的可调用对象defaultdict必须是不带参数的可调用对象,并且必须返回默认值的实例。

defaultdict您所做的嵌套问题在于defaultdict's__init__需要一个参数。给出defaultdict该参数意味着它不是defaultdict以可调用对象作为其__init__参数的包装,而是具有defaultdict

lambda@pcoving的解决方案将起作用,因为它创建了一个匿名函数,该函数返回一个defaultdict初始化的函数,该函数返回defaultdict字典嵌套中每一层的正确类型。

于 2013-09-28T05:14:27.267 回答
0

如果它只是提取和检索三元组,你应该试试这个NLTK

>>> import nltk
>>> sent = "this is a foo bar crazycoder"
>>> trigrams = nltk.ngrams(sent.split(), 3)
[('this', 'is', 'a'), ('is', 'a', 'foo'), ('a', 'foo', 'bar'), ('foo', 'bar', 'crazycoder')]
# token "a" in first element of trigram
>>> first_a = [i for i in trigrams if i[0] == "a"]
[('a', 'foo', 'bar')]
# token "a" in 2nd element of trigram
>>> second_a = [i for i in trigrams if i[1] == "a"]
[('is', 'a', 'foo')]
# token "a" in third element of trigram
>>> third = [i for i in trigrams if i[2] == "a"]
[('this', 'is', 'a')]
# look for 2gram in trigrams
>> two_foobar = [i for i in trigrams if "foo" in i and "bar" in i]
[('a', 'foo', 'bar'), ('foo', 'bar', 'crazycoder')]
# look for a perfect 3gram
>> perfect = [i fof i in trigrams if "foo bar crazycoder".split() == i]
[('foo', 'bar', 'crazycoder')]
于 2013-09-28T04:56:02.953 回答