Tf.keras.Model class ~ 自变量性能

Tf.keras.Model class ~ self variables performance

已编辑:

事实证明,基础 class 在继承 Tf.keras.Model 的继承树中更高,当此 class 继承存在时,可以观察到下面描述的行为。与普通脚本相比,普通 Python class 的性能可以忽略不计。

我还没有找到任何与此行为相关的文档,如果有可用的更新将随之而来。

编辑 2:

我没有找到任何关于此的文档(因此需要确认),但似乎是: 如果我将任何对象分配给 self(在继承 class 的 Tf.keras.Model 中),所有包含的 tf.Variables 都将被提取并出现在 trainable_variables 的 [=] 属性中79=]。所以我的假设是 keras.Model 检查对 self 的任何分配,试图找到一些特定的对象,而这种检查导致 self 对一个巨大的 dict 的分配很慢。

供参考:检查深入嵌套列表和字典,但不检查 classes 除非它们扩展 ts.keras.Modeltf.keras.Layer

原问题:

我有一个字符串列表 col(约 30 万行,约 30 个字符的字符串给你一个想法)。准确地说是 pandas.DataGrid 而不是列表。

我正在创建一个查找字典以供将来使用,如下所示:

direct = {}
inverse = {}
# progressive
progressive = 0

# create direct map
for label in col:
    # skip if present
    if str(label) in direct:
        continue
    # else add to direct
    direct[str(label)] = progressive
    inverse[progressive] = str(label)
    progressive += 1

这里没有什么奇怪的,它需要 0.15 秒并且 python 进程内存使用是合理的。

然后我将我的代码移到 class 中,这里事情变得奇怪了。这里提供了同一功能的两个略有不同的版本。

版本 A:

def fromDataset(self, column):
    # reset map
    self.direct = {}
    self.inverse = {}

    # progressive
    progressive = 0

    # create direct map
    for label in column:
        # skip if present
        if str(label) in self.direct:
            continue
        # else add to direct
        self.direct[str(label)] = progressive
        self.inverse[progressive] = str(label)
        progressive += 1

版本 B:

def fromDataset(self, column):
    # reset map
    direct = {}
    inverse = {}

    # progressive
    progressive = 0

    # create direct map
    for label in column:
        # skip if present
        if str(label) in direct:
            continue
        # else add to direct
        direct[str(label)] = progressive
        inverse[progressive] = str(label)
        progressive += 1

    self.direct = direct
    self.inverse = inverse

所有建议的函数产生相同的结果(约 120k 个条目的字典,占用约 30MB RAM)

我可以接受版本 A 可能会比版本 B 慢访问 self 变量,但我无法理解的是版本 B 怎么可能花费 2.16 秒(比以前多 14 倍)天气版本 A 甚至无法测试(10 多分钟后还没有结果,进程内存使用量增加了 500+ MB)

更奇怪的是,版本 B 需要 0.17 秒来创建字典,~2 秒来执行:

self.direct = direct
self.inverse = inverse

在为此迷失了整整一天之后,我开始怀疑是否缺少与 Python 内存分配相关的内容。我得出的唯一有意义的假设是 self.direct = direct 导致 Python 实际上 move/copy ram 中的字典。

任何人都可以解释一下版本 A 和版本 B 中发生了什么,这与直接脚本版本有如此根本的不同吗?

我创建了一个可重现的示例并且没有遇到任何问题:

import random
import string

import pandas


def gen_random_word(word_length=30):
    return ''.join((random.choice(string.ascii_letters) for _ in range(word_length)))

# I create a list of 300000 labels (but only 150k distinct labels) of 30 characters
labels = [gen_random_word() for _ in range(150000)]
labels = labels + labels
random.shuffle(labels)

# A dataframe here is useless but I try to get close to your own example
df = pandas.DataFrame(
    {'labels': labels}
)

class A:
    def __init__(self):
        self.direct = {}
        self.inverse = {}
        self.progressive = 0

    def fromDataset(self, column):

        # create direct map
        for label in column:
            # skip if present
            if str(label) in self.direct:
                continue
            # else add to direct
            self.direct[str(label)] = self.progressive
            self.inverse[self.progressive] = str(label)
            self.progressive += 1

class B:
    def __init__(self):
        self.direct = {}
        self.inverse = {}
        self.progressive = 0

    def fromDataset(self, column):
        # reset map
        direct = {}
        inverse = {}

        # progressive
        progressive = 0

        # create direct map
        for label in column:
            # skip if present
            if str(label) in direct:
                continue
            # else add to direct
            direct[str(label)] = progressive
            inverse[progressive] = str(label)
            progressive += 1

        self.direct = direct
        self.inverse = inverse
        self.progressive = progressive

第一次测试:

%%time # remove that if you are not using jupyter and use another timing solution

direct = {}
inverse = {}
# progressive
progressive = 0

# create direct map
for label in df['labels']:
    # skip if present
    if str(label) in direct:
        continue
    # else add to direct
    direct[str(label)] = progressive
    inverse[progressive] = str(label)
    progressive += 1

挂墙时间:267 毫秒

第二次测试:

%%time
a = A()
a.fromDataset(df['labels'])

挂墙时间:249 毫秒

第三次测试:

%%time
b = B()
b.fromDataset(df['labels'])

挂墙时间:220 毫秒

所以...没什么重要的。