Tensorflow2.0 Keras之自定义网络

本文详细介绍了在Tensorflow 2.0中使用Keras自定义网络层和模型的方法,包括如何创建自定义网络层类及其实现前向传播逻辑,以及如何基于自定义层构建复杂网络结构。
部署运行你感兴趣的模型镜像

Tensorflow2.0 Keras之自定义网络


尽管 Keras 提供了很多的常用网络层类,但深度学习可以使用的网络层远远不止这些。科研工作者一般是自行实现了较为新颖的网络层,经过大量实验验证有效后,深度学习框架才会跟进,内置对这些网络层的支持。因此掌握自定义网络层、网络的实现非常重要。

对于需要创建自定义逻辑的网络层,可以通过自定义类来实现。在创建自定义网络层类时,需要继承自layers.Layer 基类;创建自定义的网络类时,需要继承自keras.Model 基类,这样建立的自定义类才能够方便的利用Layer/Model 基类提供的参数管理等功能,同时也能够与其他的标准网络层类交互使用。

自定义网络层

对于自定义的网络层,至少需要实现初始化__init__方法和前向传播逻辑call 方法。我们以某个具体的自定义网络层为例,假设需要一个没有偏置向量的全连接层,即bias 为0,同时固定激活函数为ReLU 函数。尽管这可以通过标准的Dense 层创建,但我们还是通过实现这个“特别的”网络层类来阐述如何实现自定义网络层。

首先创建类,并继承自Layer 基类。创建初始化方法,并调用母类的初始化函数,由于是全连接层,因此需要设置两个参数:输入特征的长度inp_dim 和输出特征的长度outp_dim,并通过self.add_variable(name, shape)创建shape 大小,名字为name 的张量𝑾,并设置为需要优化。代码如下:

class MyDense(layers.Layer):
	# 自定义网络层
	def __init__(self, inp_dim, outp_dim):
		super(MyDense, self).__init__()
		# 创建权值张量并添加到类管理列表中,设置为需要优化
		self.kernel = self.add_variable('w', [inp_dim, outp_dim],
		trainable=True)

需要注意的是,self.add_variable 会返回张量𝑾的Python 引用,而变量名name 由TensorFlow 内部维护,使用的比较少。我们实例化MyDense 类,并查看其参数列表,例如:

In [5]: net = MyDense(4,3) # 创建输入为4,输出为3 节点的自定义层
net.variables,net.trainable_variables # 查看自定义层的参数列表
Out[5]:
# 类的全部参数列表
([<tf.Variable 'w:0' shape=(4, 3) dtype=float32, numpy=# 类的待优化参数列表
[<tf.Variable 'w:0' shape=(4, 3) dtype=float32, numpy=

可以看到𝑾张量被自动纳入类的参数列表。
通过修改为self.kernel = self.add_variable(‘w’, [inp_dim, outp_dim], trainable=False),我们可以设置𝑾张量不需要被优化,此时再来观测张量的管理状态:

([<tf.Variable 'w:0' shape=(4, 3) dtype=float32, numpy=], # 类的全部参数列表
[])# 类的不需要优化参数列表

可以看到,此时张量并不会被trainable_variables 管理。此外,类初始化中创建为tf.Variable类型的类成员变量也会自动纳入张量管理中,例如:

	# 通过tf.Variable 创建的类成员也会自动加入类参数列表
	self.kernel = tf.Variable(tf.random.normal([inp_dim, outp_dim]),
							  trainable=False)

打印出管理的张量列表如下:

# 类的参数列表
([<tf.Variable 'Variable:0' shape=(4, 3) dtype=float32, numpy=],
[])# 类的不需要优化参数列表

完成自定义类的初始化工作后,我们来设计自定义类的前向运算逻辑,对于这个例
子,只需要完成𝑶 = 𝑿@𝑾矩阵运算,并通过固定的ReLU 激活函数即可,代码如下:

    def call(self, inputs, training=None):
        # X@W
        out = inputs @ self.kernel
        # 执行激活函数运算
        out = tf.nn.relu(out)
        return out

如上所示,自定义类的前向运算逻辑实现在call(inputs, training=None)函数中,其中inputs代表输入,由用户在调用时传入;training 参数用于指定模型的状态:training 为True 时执行训练模式,training 为False 时执行测试模式,默认参数为None,即测试模式。由于全连接层的训练模式和测试模式逻辑一致,此处不需要额外处理。对于部份测试模式和训练模式不一致的网络层,需要根据training 参数来设计需要执行的逻辑。

自定义网络

在完成了自定义的全连接层类实现之后,我们基于上述的“无偏置的全连接层”来实现MNIST 手写数字图片模型的创建。
自定义网络类可以和其他标准类一样,通过Sequential 容器方便地封装成一个网络模型:

network = Sequential([MyDense(784, 256), # 使用自定义的层
					MyDense(256, 128),
					MyDense(128, 64),
					MyDense(64, 32),
					MyDense(32, 10)])
network.build(input_shape=(None, 28*28))
network.summary()

可以看到,通过堆叠我们的自定义网络层类,一样可以实现5 层的全连接层网络,每层全连接层无偏置张量,同时激活函数固定地使用ReLU 函数。

Sequential 容器适合于数据按序从第一层传播到第二层,再从第二层传播到第三层,以此规律传播的网络模型。对于复杂的网络结构,例如第三层的输入不仅是第二层的输出,还有第一层的输出,此时使用自定义网络更加灵活。下面我们来创建自定义网络类,首先创建类,并继承自Model 基类,分别创建对应的网络层对象,代码如下:

class MyModel(keras.Model):
	# 自定义网络类,继承自Model 基类
	def __init__(self):
		super(MyModel, self).__init__()
		# 完成网络内需要的网络层的创建工作
		self.fc1 = MyDense(28*28, 256)
		self.fc2 = MyDense(256, 128)
		self.fc3 = MyDense(128, 64)
		self.fc4 = MyDense(64, 32)
		self.fc5 = MyDense(32, 10)

然后实现自定义网络的前向运算逻辑,代码如下:

	def call(self, inputs, training=None):
		# 自定义前向运算逻辑
		x = self.fc1(inputs)
		x = self.fc2(x)
		x = self.fc3(x)
		x = self.fc4(x)
		x = self.fc5(x)
		return x

这个例子可以直接使用第一种方式,即Sequential 容器包裹实现。但自定义网络的前向计算逻辑可以自由定义,更为通用

自定义网络的实战在我的另一篇博客当中有进行讲解,有兴趣的去看一看吧
https://blog.youkuaiyun.com/python_LC_nohtyp/article/details/104123126

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.15

TensorFlow-v2.15

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

<think> 我们正在讨论TensorFlow.NET(即TensorFlow.NET绑定)中使用Keras自定义正则化类时的模型保存和序列化问题。根据用户提供的引用,我们知道TensorFlow 2.0集成了Keras作为高级API,而TensorFlow.NET是.NET平台上的对应绑定。 在TensorFlowPython)中,自定义正则化类需要继承`tf.keras.regularizers.Regularizer`并实现`__call__`和`get_config`方法。当保存模型时(如使用`model.save`),正则化器的配置通过`get_config`方法被保存,这样在加载模型时可以重新实例化相同的正则化器。 在TensorFlow.NET中,由于是.NET绑定,我们期望有类似的方法。但是,请注意TensorFlow.NET的API设计可能会与Python版略有不同,但核心概念应保持一致。 步骤: 1. 定义自定义正则化类:继承自`Regularizer`(在TensorFlow.NET中,可能是`Tensorflow.Keras.Regularizers.Regularizer`),并实现必要的方法,包括一个计算正则化损失的方法和一个用于序列化的配置方法(类似于Python中的`get_config`)。 2. 在模型中使用这个自定义正则化器。 3. 保存模型:使用Keras模型的`Save`方法。在Python中,这会保存为一个HDF5文件或TensorFlow SavedModel格式,其中包含了模型结构、权重和配置(包括正则化器的配置)。在TensorFlow.NET中,同样应该支持模型保存,并且应该能够序列化自定义层和正则化器。 4. 加载模型:使用`Tensorflow.Keras.Models.Model.LoadModel`方法。在加载时,需要确保自定义正则化器已经被正确注册,以便框架能够识别并实例化。 关键点:自定义正则化器需要能够被序列化和反序列化。因此,在定义自定义正则化器时,必须重写一个返回配置字典的方法(类似于Python中的`get_config`),并且还需要一个接受配置字典的构造函数(或一个从配置创建对象的方法)。 在TensorFlow.NET中,具体步骤可能如下: 1. 创建自定义正则化类: ```csharp public class MyCustomRegularizer : Regularizer { private float myParam; // 需要一个构造函数,可以接受参数(如果有的话) public MyCustomRegularizer(float param = 0.01f) { myParam = param; } // 实现正则化计算 public override Tensor Apply(RegularizerArgs args) { // 计算正则化损失,例如L1正则化:myParam * tf.reduce_sum(tf.abs(args.X)) // 注意:args.X是权重张量 return myParam * tf.reduce_sum(tf.abs(args.X)); } // 重写GetConfig方法以支持序列化 public override object GetConfig() { return new { myParam = myParam }; } // 可选:提供一个从配置中创建正则化器的方法(静态方法) // 在加载模型时,框架会使用这个配置来重建正则化器 // 注意:TensorFlow.NET可能已经内置了从配置重建的机制,我们只需提供GetConfig和相应的构造函数即可。 // 另外,可能需要一个接受配置字典的构造函数 public MyCustomRegularizer(IConfigNode config) { // 假设config是一个字典或类似结构,从中解析参数 myParam = config["myParam"]; } } ``` 2. 在模型中使用: ```csharp var model = tf.keras.Sequential(); model.add(tf.keras.layers.Dense(64, kernel_regularizer: new MyCustomRegularizer(0.01f))); // ... 编译和训练模型 ``` 3. 保存模型: ```csharp model.save("my_model"); ``` 4. 加载模型: ```csharp var loaded_model = tf.keras.models.load_model("my_model", custom_objects: new Dictionary<string, Type> { { "MyCustomRegularizer", typeof(MyCustomRegularizer) } }); ``` 注意:在加载模型时,我们需要通过`custom_objects`参数告诉框架我们的自定义类,这样它才能正确地反序列化。 但是,请注意TensorFlow.NET的API可能仍在发展中,上述代码是基于Python API和TensorFlow.NET的常见模式推测的。具体实现时,请参考TensorFlow.NET的官方文档或示例。 另外,关于模型保存格式:TensorFlow 2.0支持两种格式:HDF5和SavedModel。在TensorFlow.NET中,可能同样支持这两种格式,但需要确认。 如果遇到问题,建议查看TensorFlow.NET的文档中关于自定义层和模型保存的部分,或者参考其GitHub仓库中的示例。 由于用户的问题非常具体,涉及TensorFlow.NET(.NET绑定),而我们的引用资料主要针对Python版的TensorFlow,因此我们根据KerasPython中的行为进行类推,并指出在.NET中应该类似,但需要注意API的差异。 总结:自定义正则化器的关键点在于实现`GetConfig`方法和一个接受配置的构造函数(或提供一种从配置重建的方式),并在加载模型时通过`custom_objects`注册自定义类。 注意:以上代码为示意代码,实际使用时请根据TensorFlow.NET的最新API进行调整。 如果用户需要更具体的示例,建议查阅TensorFlow.NET的文档或示例代码库。 </think>在 TensorFlow.NET 2.0 中使用 Keras 自定义正则化类时,模型保存和序列化的实现方法如下: ### 1. 自定义正则化类的实现 首先需要正确实现自定义正则化类,包含以下关键方法: ```csharp public class MyCustomRegularizer : Regularizer { private readonly float _strength; // 必须包含带参数的构造函数 public MyCustomRegularizer(float strength) { _strength = strength; } // 正则化计算逻辑 public override Tensor Apply(RegularizerArgs args) { return _strength * tf.reduce_sum(tf.square(args.X)); // 示例:L2正则 } // 序列化配置 - 关键方法 public override object GetConfig() { return new { strength = _strength }; } } ``` ### 2. 模型构建与使用 在模型中使用自定义正则化器: ```csharp var model = keras.Sequential(); model.add(keras.layers.Dense(64, kernel_regularizer: new MyCustomRegularizer(0.01f))); // ... 添加其他层 model.compile(optimizer: 'adam', loss: 'mse'); model.fit(...); // 训练模型 ``` ### 3. 模型保存方法 使用 Keras 的标准保存接口: ```csharp // 保存完整模型(含结构和权重) model.save("my_model.keras"); // 仅保存权重 model.save_weights("model_weights.ckpt"); ``` ### 4. 模型加载与反序列化 加载时需通过 `custom_objects` 注册自定义类: ```csharp // 关键步骤:注册自定义正则化器 var customObjects = new Dictionary<string, Type> { { "MyCustomRegularizer", typeof(MyCustomRegularizer) } }; // 加载完整模型 var loaded_model = keras.models.load_model( "my_model.keras", custom_objects: customObjects ); // 加载权重到新建模型 var new_model = ... // 构建相同结构的模型 new_model.load_weights("model_weights.ckpt"); ``` ### 关键注意事项 1. **序列化要求**: - 必须实现 `GetConfig()` 方法返回参数字典 - 必须提供参数匹配的构造函数 - 类名需与保存时的标识一致 2. **保存格式选择**: - `.keras` 格式 (推荐):完整保存模型架构和权重 - SavedModel 格式:支持生产环境部署 - H5 格式:旧版兼容格式 3. **工业部署**: ```csharp // 转换为TensorFlow Serving格式 model.save("saved_model_path", save_format: "tf"); ``` 4. **自定义层兼容**: 如果模型中同时包含自定义层,需要一并注册: ```csharp customObjects.Add("CustomLayer", typeof(CustomLayer)); ``` > **技术原理**:Keras 序列化时通过 `GetConfig()` 获取配置信息,反序列化时使用配置字典重建对象。自定义正则化器的配置会被嵌入模型架构的 JSON 描述中[^2][^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值