草庐IT

python - 为keras模型添加预处理层并设置张量值

coder 2023-08-16 原文

如何最好地将预处理层(例如,减去均值并除以标准差)添加到 keras (v2.0.5) 模型,以便模型完全独立部署(可能在 C++ 环境中)。我试过:

    def getmodel():
       model = Sequential()
       mean_tensor = K.placeholder(shape=(1,1,3), name="mean_tensor")
       std_tensor = K.placeholder(shape=(1,1,3), name="std_tensor")

       preproc_layer = Lambda(lambda x: (x - mean_tensor) / (std_tensor + K.epsilon()),
                              input_shape=im_shape)

       model.add(preproc_layer)

       # Build the remaining model, perhaps set weights,
       ...

       return model

然后,在其他地方设置模型的均值/标准。我找到了 set_value功能所以尝试了以下:

m = getmodel()
mean, std = get_mean_std(..)

graph = K.get_session().graph
mean_tensor = graph.get_tensor_by_name("mean_tensor:0")
std_tensor = graph.get_tensor_by_name("std_tensor:0")

K.set_value(mean_tensor, mean)
K.set_value(std_tensor, std)

但是 set_value 失败了

AttributeError: 'Tensor' object has no attribute 'assign'

所以 set_value 并不像(有限的)文档建议的那样工作。这样做的正确方法是什么?获取 TF session ,将所有训练代码包装在 with (session) 中并使用 feed_dict?我原以为会有一种原生的 keras 方法来设置张量值。

我没有使用占位符,而是尝试使用 K.variableK.constant 设置模型构建的均值/标准差:

mean_tensor = K.variable(mean, name="mean_tensor")
std_tensor = K.variable(std, name="std_tensor")

这避免了任何 set_value 问题。虽然我注意到如果我尝试训练该模型(我知道这不是特别有效,因为您正在为每个图像重新进行标准化)它可以工作但是在第一个纪元结束时 ModelCheckpoint处理程序失败并显示非常深的堆栈跟踪:

...
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 102, in save_model
  'config': model.get_config()
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 1193, in get_config
  return copy.deepcopy(config)
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 163, in deepcopy
  y = copier(x, memo)
...
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 190, in deepcopy
  y = _reconstruct(x, rv, 1, memo)
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 343, in _reconstruct
  y.__dict__.update(state)
AttributeError: 'NoneType' object has no attribute 'update'

更新 1:

我还尝试了一种不同的方法。像往常一样训练一个模型,然后添加第二个进行预处理的模型:

# Regular model, trained as usual
model = ...

# Preprocessing model
preproc_model = Sequential()
mean_tensor = K.constant(mean, name="mean_tensor")
std_tensor = K.constant(std, name="std_tensor")
preproc_layer = Lambda(lambda x: (x - mean_tensor) / (std_tensor + K.epsilon()),
                       input_shape=im_shape, name="normalisation")
preproc_model.add(preproc_layer)

# Prepend the preprocessing model to the regular model    
full_model = Model(inputs=[preproc_model.input],
              outputs=[model(preproc_model.output)])

# Save the complete model to disk
full_model.save('full_model.hdf5')

这似乎一直有效,直到 save() 调用失败,并显示与上面相同的深层堆栈跟踪。 也许 Lambda 层是问题所在,但从 this issue 来看不过,它似乎应该正确序列化。

总的来说,如何在不影响序列化(和导出到 pb)能力的情况下将规范化层附加到 keras 模型?

我确定您可以通过直接下拉到 TF(例如 this thread 或使用 tf.Transform )来让它工作,但我认为它可以直接在 keras 中实现。

更新 2:

所以我发现可以通过以下方式避免深层堆栈跟踪

def foo(x):
    bar = K.variable(baz, name="baz")
    return x - bar

因此在函数内部定义 bar 而不是从外部范围捕获。

然后我发现我可以保存到磁盘但无法从磁盘加载。围绕这个有一套github问题。我使用了 #5396 中指定的解决方法将所有变量作为参数传递,这让我可以保存和加载。

我以为我快到了,我继续使用上面更新 1 中的方法,将预处理模型堆叠在经过训练的模型前面。 然后导致 Model is not compiled 错误。解决了这些问题,但最终我从未设法使以下方法起作用:

  • 构建和训练模型
  • 将其保存到磁盘
  • 加载它,添加一个预处理模型
  • 将堆叠模型作为卡住的 pb 文件导出到磁盘
  • 从磁盘加载卡住的 pb
  • 将其应用于一些看不见的数据

我已经达到了没有错误的地步,但无法让归一化张量传播到卡住的 pb。在这上面花了太多时间,然后我放弃并转向不太优雅的方法:

  • 从一开始就使用模型中的预处理操作构建模型,但设置为无操作(mean=0,std=1)
  • 训练模型,构建一个相同的模型,但这次使用正确的均值/标准值。
  • 转移重量
  • 将模型导出并卡住到pb

现在所有这些都按预期完全正常工作。训练开销很小,但对我来说可以忽略不计。

仍然无法弄清楚如何在 keras 中设置张量变量的值(不引发 assign 异常)但现在可以不用它。

将接受@Daniel 的回答,因为它让我朝着正确的方向前进。

相关问题:

最佳答案

创建变量时,必须给它“值”,而不是形状:

mean_tensor = K.variable(mean, name="mean_tensor")
std_tensor = K.variable(std, name="std_tensor")

现在,在 Keras 中,您不必处理 session 、图形和类似的东西。您只使用层,在 Lambda 层(或损失函数)内您可以使用张量。

对于我们的 Lambda 层,我们需要一个更复杂的函数,因为在进行计算之前形状必须匹配。因为我不知道 im_shape,所以我认为它有 3 个维度:

def myFunc(x):

    #reshape x in a way it's compatible with the tensors mean and std:
    x = K.reshape(x,(-1,1,1,3)) 
        #-1 is like a wildcard, it will be the value that matches the rest of the given shape.     
        #I chose (1,1,3) because it's the same shape of mean_tensor and std_tensor

    result = (x - mean_tensor) / (std_tensor + K.epsilon())

    #now shape it back to the same shape it was before (which I don't know)    
    return K.reshape(result,(-1,im_shape[0], im_shape[1], im_shape[2]))
        #-1 is still necessary, it's the batch size

现在我们创建 Lambda 层,考虑到它还需要一个输出形状(因为您的自定义操作,系统不一定知道输出形状)

model.add(Lambda(myFunc,input_shape=im_shape, output_shape=im_shape))

在此之后,只需编译模型并对其进行训练。 (通常使用 model.compile(...)model.fit(...))


如果你想包含所有内容,包括函数内部的预处理,也可以:

def myFunc(x):

    mean_tensor = K.mean(x,axis=[0,1,2]) #considering shapes of (size,width, heigth,channels)    
    std_tensor = K.std(x,axis=[0,1,2])

    x = K.reshape(x, (-1,3)) #shapes of mean and std are (3,) here.    
    result = (x - mean_tensor) / (std_tensor + K.epsilon())

    return K.reshape(result,(-1,width,height,3))

现在,所有这些都是您模型中的额外计算,并且会消耗处理。 最好只在模型之外做所有事情。首先创建预处理数据并存储它,然后创建没有这个预处理层的模型。这样你就可以获得更快的模型。 (如果您的数据或模型太大,这可能很重要)。

关于python - 为keras模型添加预处理层并设置张量值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44835223/

有关python - 为keras模型添加预处理层并设置张量值的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  4. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  5. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  6. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  7. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  8. ruby - 将 Bootstrap Less 添加到 Sinatra - 2

    我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它

  9. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  10. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

随机推荐