【问题标题】:Get Keras model.summary() as table获取 Keras model.summary() 作为表
【发布时间】:2021-04-28 14:08:36
【问题描述】:

我在 Keras 中创建了相当大的模型,我正在用 LaTeX 写一篇关于它的文章。为了很好地描述 LaTeX 中的 keras 模型,我想用它创建一个 LaTeX 表。我可以手动实现它,但我想知道是否有任何“更好”的方法来实现它。

我到处找找,发现了一些像is there a nice output of Keras model.summary( )? 这样的帖子,通过绘制到图像来解决。但是,我希望将其作为文本数据(是的,拥有MRE :)),表格看起来更好并且格式也很好。如果有类似的情况,最好的选择:statsmodels summary to latex。然而,我找不到任何将model.summary() 输出转换为表格表示的方法。

我在想是否有办法以某种方式将其转换为 pandas 数据帧,然后可以使用df.to_latex() 进行处理。我试图用model.to_json() 来做这件事,但是这个函数不会像model.summary() 打印那样返回任何关于输出形状的信息。这是我的尝试:

df = pd.DataFrame(model.to_json())
df2 = pd.DataFrame(df.loc["layers","config"])
#for example select filters, need to do it like this as it is not always contained
filters = ["-" if "filters" not in x else x["filters"] for x in df2.loc[:,"config"]]

model.to_json() 为我的模型返回以下 json:

{"class_name": "Model", "config": {"name": "Discriminator", "layers": [{"name": "input_3", "class_name": "InputLayer", "config": {"batch_input_shape": [null, 256, 256, 1], "dtype": "float32", "sparse": false, "name": "input_3"}, "inbound_nodes": []}, {"name": "input_4", "class_name": "InputLayer", "config": {"batch_input_shape": [null, 256, 256, 1], "dtype": "float32", "sparse": false, "name": "input_4"}, "inbound_nodes": []}, {"name": "concatenate_2", "class_name": "Concatenate", "config": {"name": "concatenate_2", "trainable": true, "dtype": "float32", "axis": -1}, "inbound_nodes": [[["input_3", 0, 0, {}], ["input_4", 0, 0, {}]]]}, {"name": "conv2d_6", "class_name": "Conv2D", "config": {"name": "conv2d_6", "trainable": true, "dtype": "float32", "filters": 8, "kernel_size": [4, 4], "strides": [2, 2], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_2", 0, 0, {}]]]}, {"name": "leaky_re_lu_5", "class_name": "LeakyReLU", "config": {"name": "leaky_re_lu_5", "trainable": true, "dtype": "float32", "alpha": 0.20000000298023224}, "inbound_nodes": [[["conv2d_6", 0, 0, {}]]]}, {"name": "conv2d_7", "class_name": "Conv2D", "config": {"name": "conv2d_7", "trainable": true, "dtype": "float32", "filters": 16, "kernel_size": [4, 4], "strides": [2, 2], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["leaky_re_lu_5", 0, 0, {}]]]}, {"name": "leaky_re_lu_6", "class_name": "LeakyReLU", "config": {"name": "leaky_re_lu_6", "trainable": true, "dtype": "float32", "alpha": 0.20000000298023224}, "inbound_nodes": [[["conv2d_7", 0, 0, {}]]]}, {"name": "batch_normalization_4", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_4", "trainable": true, "dtype": "float32", "axis": -1, "momentum": 0.8, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"class_name": "Zeros", "config": {}}, "gamma_initializer": {"class_name": "Ones", "config": {}}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}}, "moving_variance_initializer": {"class_name": "Ones", "config": {}}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "inbound_nodes": [[["leaky_re_lu_6", 0, 0, {}]]]}, {"name": "conv2d_8", "class_name": "Conv2D", "config": {"name": "conv2d_8", "trainable": true, "dtype": "float32", "filters": 32, "kernel_size": [4, 4], "strides": [2, 2], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["batch_normalization_4", 0, 0, {}]]]}, {"name": "leaky_re_lu_7", "class_name": "LeakyReLU", "config": {"name": "leaky_re_lu_7", "trainable": true, "dtype": "float32", "alpha": 0.20000000298023224}, "inbound_nodes": [[["conv2d_8", 0, 0, {}]]]}, {"name": "batch_normalization_5", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_5", "trainable": true, "dtype": "float32", "axis": -1, "momentum": 0.8, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"class_name": "Zeros", "config": {}}, "gamma_initializer": {"class_name": "Ones", "config": {}}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}}, "moving_variance_initializer": {"class_name": "Ones", "config": {}}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "inbound_nodes": [[["leaky_re_lu_7", 0, 0, {}]]]}, {"name": "conv2d_9", "class_name": "Conv2D", "config": {"name": "conv2d_9", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": [4, 4], "strides": [2, 2], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["batch_normalization_5", 0, 0, {}]]]}, {"name": "leaky_re_lu_8", "class_name": "LeakyReLU", "config": {"name": "leaky_re_lu_8", "trainable": true, "dtype": "float32", "alpha": 0.20000000298023224}, "inbound_nodes": [[["conv2d_9", 0, 0, {}]]]}, {"name": "batch_normalization_6", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_6", "trainable": true, "dtype": "float32", "axis": -1, "momentum": 0.8, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"class_name": "Zeros", "config": {}}, "gamma_initializer": {"class_name": "Ones", "config": {}}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}}, "moving_variance_initializer": {"class_name": "Ones", "config": {}}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "inbound_nodes": [[["leaky_re_lu_8", 0, 0, {}]]]}, {"name": "conv2d_10", "class_name": "Conv2D", "config": {"name": "conv2d_10", "trainable": true, "dtype": "float32", "filters": 1, "kernel_size": [4, 4], "strides": [1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["batch_normalization_6", 0, 0, {}]]]}], "input_layers": [["input_3", 0, 0], ["input_4", 0, 0]], "output_layers": [["conv2d_10", 0, 0]]}, "keras_version": "2.3.1", "backend": "tensorflow"}

虽然我想要model.summary()-like 信息:

Model: "Discriminator"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_3 (InputLayer)            (None, 256, 256, 1)  0                                            
__________________________________________________________________________________________________
input_4 (InputLayer)            (None, 256, 256, 1)  0                                            
...

如果我将摘要输出转换为字符串 (Keras model.summary() object to string) 并解析字符串输出,也许有一些好方法?

【问题讨论】:

标签: python keras


【解决方案1】:

我写了一个方法来修改 model.summary() 的输出,这样它就可以被复制到 Latex 中并且看起来和原来的一样(除了行间距更小一点)。

def m2tex(model):
    stringlist = []
    model.summary(line_length=70, print_fn=lambda x: stringlist.append(x))
    del stringlist[1:-4:2]
    del stringlist[-1]
    for ix in range(1,len(stringlist)-3):
        tmp = stringlist[ix]
        stringlist[ix] = tmp[0:31]+"& "+tmp[31:59]+"& "+tmp[59:]+"\\\\ \hline"
    stringlist[0] = "Model: test \\\\ \hline"
    stringlist[1] = stringlist[1]+" \hline"
    stringlist[-4] = stringlist[-4]+" \hline"
    stringlist[-3] = stringlist[-3]+" \\\\"
    stringlist[-2] = stringlist[-2]+" \\\\"
    stringlist[-1] = stringlist[-1]+" \\\\ \hline"
    prefix = ["\\begin{table}[]", "\\begin{tabular}{lll}"]
    suffix = ["\end{tabular}", "\caption{Model summary for test.}", "\label{tab:model-summary}" , "\end{table}"]
    stringlist = prefix + stringlist + suffix 
    out_str = " \n".join(stringlist)
    out_str = out_str.replace("_", "\_")
    out_str = out_str.replace("#", "\#")
    print(out_str)

如您所见,它非常丑陋,但对我有用。 如果图层名称较长,则可能需要相应地增加 line_length=70 参数和第 8 行中的索引。

一个示例输出是:

\begin{table}[] 
\begin{tabular}{lll} 
Model: test \\ \hline 
Layer (type)                   & Output Shape                & Param \#    \\ \hline \hline 
input\_1 (InputLayer)           & [(None, 28, 28, 1)]         & 0          \\ \hline 
dropout (Dropout)              & (None, 28, 28, 1)           & 0          \\ \hline 
conv2d (Conv2D)                & (None, 26, 26, 8)           & 80         \\ \hline 
dropout\_1 (Dropout)            & (None, 26, 26, 8)           & 0          \\ \hline 
conv2d\_1 (Conv2D)              & (None, 24, 24, 8)           & 584        \\ \hline 
dropout\_2 (Dropout)            & (None, 24, 24, 8)           & 0          \\ \hline 
max\_pooling2d (MaxPooling2D)   & (None, 12, 12, 8)           & 0          \\ \hline 
conv2d\_2 (Conv2D)              & (None, 10, 10, 10)          & 730        \\ \hline 
dropout\_3 (Dropout)            & (None, 10, 10, 10)          & 0          \\ \hline 
conv2d\_3 (Conv2D)              & (None, 8, 8, 10)            & 910        \\ \hline 
dropout\_4 (Dropout)            & (None, 8, 8, 10)            & 0          \\ \hline 
max\_pooling2d\_1 (MaxPooling2D) & (None, 4, 4, 10)            & 0          \\ \hline 
flatten (Flatten)              & (None, 160)                 & 0          \\ \hline 
dense (Dense)                  & (None, 16)                  & 2576       \\ \hline 
dropout\_5 (Dropout)            & (None, 16)                  & 0          \\ \hline 
dense\_1 (Dense)                & (None, 10)                  & 170        \\ \hline \hline 
Total params: 5,050 \\ 
Trainable params: 5,050 \\ 
Non-trainable params: 0 \\ \hline 
\end{tabular} 
\caption{Model summary for test.} 
\label{tab:model-summary} 
\end{table} 

【讨论】:

    【解决方案2】:

    根据 nico 的回复,我摸了一下他的代码并创建了一个github repo

    def m2tex(model,modelName):
        stringlist = []
        model.summary(line_length=70, print_fn=lambda x: stringlist.append(x))
        del stringlist[1:-4:2]
        del stringlist[-1]
        for ix in range(1,len(stringlist)-3):
            tmp = stringlist[ix]
            stringlist[ix] = tmp[0:31]+"& "+tmp[31:59]+"& "+tmp[59:]+"\\\\ \hline"
        stringlist[0] = "Model: {} \\\\ \hline".format(modelName)
        stringlist[1] = stringlist[1]+" \hline"
        stringlist[-4] += " \hline"
        stringlist[-3] += " \\\\"
        stringlist[-2] += " \\\\"
        stringlist[-1] += " \\\\ \hline"
        prefix = ["\\begin{table}[]", "\\begin{tabular}{lll}"]
        suffix = ["\end{tabular}", "\caption{{Model summary for {}.}}".format(modelName), "\label{tab:model-summary}" , "\end{table}"]
        stringlist = prefix + stringlist + suffix 
        out_str = " \n".join(stringlist)
        out_str = out_str.replace("_", "\_")
        out_str = out_str.replace("#", "\#")
        print(out_str)
    

    Image example

    【讨论】:

      【解决方案3】:

      随着时间的增加,我自己找到了解决方案。解析模型摘要并不是一个好主意。我宁愿在模型层上使用循环并提取所需的信息,将其保存到数据帧中,以便以后使用。代码如下:

      table=pd.DataFrame(columns=["Name","Type","Shape"])
      for layer in model.layers:
          table = table.append({"Name":layer.name, "Type": layer.__class__.__name__,"Shape":layer.output_shape}, ignore_index=True)
      

      table.head()的输出:

                   Name         Type                  Shape
      0        input_21   InputLayer  [(None, 256, 256, 1)]
      1        input_22   InputLayer  [(None, 256, 256, 1)]
      2  concatenate_32  Concatenate    (None, 256, 256, 2)
      3       conv2d_96       Conv2D    (None, 128, 128, 8)
      4  leaky_re_lu_60    LeakyReLU    (None, 128, 128, 8)
      

      【讨论】:

      • 如果您的答案显示了model.summary() 中可用的所有属性,那么可能会更好。我为参数尝试了layer.count_params,但得到了对象而不是值。 <bound method Layer.count_params of <keras.layers.convolutional.Conv2D object at 0x7f95dd4013d0>>
      • 知道了。它实际上是一个函数而不是属性。layer.count_params()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-30
      • 1970-01-01
      • 1970-01-01
      • 2020-03-15
      • 1970-01-01
      • 2022-12-15
      • 2016-08-25
      相关资源
      最近更新 更多