政安晨:【Keras机器学习实践要点】(五)—— 通过子类化创建新层和模型

目录

介绍

安装

层级:状态(权重)与某些计算的组合

层可以有不可训练的重量

最佳实践:推迟权重的创建,直到输入的形状已知。

层可以递归组合

后端不可知层和特定后端层

add_loss()方法

可以选择在您的层上启用序列化功能


政安晨的个人主页政安晨

欢迎 👍点赞✍评论⭐收藏

收录专栏: TensorFlow与Keras机器学习实战

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!

介绍

本文将涵盖构建自己的子类化层和模型所需的所有知识。

您将了解以下功能

层类

add_weight()方法

可训练和不可训练的权重

build()方法

确保您的层可以与任何后端一起使用

add_loss()方法

call()中的训练参数

call()中的掩码参数

确保您的层可以序列化

让我们开始吧。


安装

import numpy as np
from tensorflow import keras
from keras import ops
from keras import layers

层级:状态(权重)与某些计算的组合

Keras 的核心抽象之一是层类。

层封装了状态(层的 "权重")和从输入到输出的转换("调用",层的前向传递)。

下面是一个密集连接的层。它有两个状态变量:变量 w 和 b。

class Linear(keras.layers.Layer):def __init__(self, units=32, input_dim=32):super().__init__()self.w = self.add_weight(shape=(input_dim, units),initializer="random_normal",trainable=True,)self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)def call(self, inputs):return ops.matmul(inputs, self.w) + self.b

你可以通过调用一个层函数来使用它,就像调用Python函数一样,传入一些张量输入。

x = ops.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)

请注意,权重 w 和 b 在被设置为层属性后,会被层自动跟踪

assert linear_layer.weights == [linear_layer.w, linear_layer.b]

层可以有不可训练的重量

除了可训练权重外,还可以向层添加不可训练权重。在反向传播过程中,这些权重在训练层时不会被考虑在内。

下面介绍如何添加和使用不可训练权重

class ComputeSum(keras.layers.Layer):def __init__(self, input_dim):super().__init__()self.total = self.add_weight(initializer="zeros", shape=(input_dim,), trainable=False)def call(self, inputs):self.total.assign_add(ops.sum(inputs, axis=0))return self.totalx = ops.ones((2, 2))
my_sum = ComputeSum(2)
y = my_sum(x)
print(y.numpy())
y = my_sum(x)
print(y.numpy())

它是层权重的一部分,但被归类为不可训练的权重。

print("weights:", len(my_sum.weights))
print("non-trainable weights:", len(my_sum.non_trainable_weights))# It's not included in the trainable weights:
print("trainable_weights:", my_sum.trainable_weights)

最佳实践推迟权重的创建,直到输入的形状已知

我们上面的线性层接受了一个input_dim参数,该参数用于在__init__()函数中计算权重w和偏置b的形状。

class Linear(keras.layers.Layer):def __init__(self, units=32, input_dim=32):super().__init__()self.w = self.add_weight(shape=(input_dim, units),initializer="random_normal",trainable=True,)self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)def call(self, inputs):return ops.matmul(inputs, self.w) + self.b

在许多情况下,您可能无法预先知道输入的大小,并且希望在实例化图层后的某个时间,当该值变为已知时,才懒惰地创建权重。

在Keras API中,我们建议在您的图层的build(self, inputs_shape)方法中创建图层权重。

像这样:

class Linear(keras.layers.Layer):def __init__(self, units=32):super().__init__()self.units = unitsdef build(self, input_shape):self.w = self.add_weight(shape=(input_shape[-1], self.units),initializer="random_normal",trainable=True,)self.b = self.add_weight(shape=(self.units,), initializer="random_normal", trainable=True)def call(self, inputs):return ops.matmul(inputs, self.w) + self.b

您的层的__call__()方法在第一次被调用时会自动运行build。

现在您有一个懒惰的层,因此更容易使用。

# At instantiation, we don't know on what inputs this is going to get called
linear_layer = Linear(32)# The layer's weights are created dynamically the first time the layer is called
y = linear_layer(x)

将build()单独实现如上所示,很好地将只创建权重一次与在每次调用中使用权重进行了分离。


层可以递归组合

如果你将一个 Layer 实例分配为另一个 Layer 的属性,外层将开始跟踪内层创建的权重。

我们建议在 init() 方法中创建这样的子层,并在第一个 call() 中触发构建它们的权重。

class MLPBlock(keras.layers.Layer):def __init__(self):super().__init__()self.linear_1 = Linear(32)self.linear_2 = Linear(32)self.linear_3 = Linear(1)def call(self, inputs):x = self.linear_1(inputs)x = keras.activations.relu(x)x = self.linear_2(x)x = keras.activations.relu(x)return self.linear_3(x)mlp = MLPBlock()
y = mlp(ops.ones(shape=(3, 64)))  # The first call to the `mlp` will create the weights
print("weights:", len(mlp.weights))
print("trainable weights:", len(mlp.trainable_weights))

后端不可知层和特定后端层

只要一个层只使用 keras.ops 命名空间的 API(或者其他 Keras 命名空间,例如 keras.activations、keras.random 或 keras.layers),那么它就可以与任何后端一起使用——TensorFlow、JAX 或 PyTorch。

到目前为止,在本指南中看到的所有层都适用于所有Keras后端。

keras.ops命名空间提供了以下功能

NumPy API,例如ops.matmul,ops.sum,ops.reshape,ops.stack等。

神经网络特定的API,例如ops.softmax,ops.conv,ops.binary_crossentropy,ops.relu等。

您还可以在层中使用本机后端API(例如tf.nn函数),但是如果这样做,您的层只能与特定的后端一起使用。

例如,您可以使用jax.numpy编写以下特定于JAX的层:

import jaxclass Linear(keras.layers.Layer):...def call(self, inputs):return jax.numpy.matmul(inputs, self.w) + self.b

这将是等效的TensorFlow特定层:

import tensorflow as tfclass Linear(keras.layers.Layer):...def call(self, inputs):return tf.matmul(inputs, self.w) + self.b

这将是等效的PyTorch特定层:

import torchclass Linear(keras.layers.Layer):...def call(self, inputs):return torch.matmul(inputs, self.w) + self.b

由于跨后端兼容性是一种非常有用的特性,我们强烈建议您始终通过仅使用Keras APIs来使您的层与后端无关。


add_loss()方法

在编写层的call()方法时,您可以创建损失张量,以便在编写训练循环时稍后使用。通过调用self.add_loss(value)可以实现这一点。

# A layer that creates an activity regularization loss
class ActivityRegularizationLayer(keras.layers.Layer):def __init__(self, rate=1e-2):super().__init__()self.rate = ratedef call(self, inputs):self.add_loss(self.rate * ops.mean(inputs))return inputs

这些损耗(包括任何内层创建的损耗)可以通过 layer.losses 检索到。

该属性在每次调用顶层层的 __call__() 开始时重置,因此 layer.losses 总是包含上次向前传递时创建的损耗值。

class OuterLayer(keras.layers.Layer):def __init__(self):super().__init__()self.activity_reg = ActivityRegularizationLayer(1e-2)def call(self, inputs):return self.activity_reg(inputs)layer = OuterLayer()
assert len(layer.losses) == 0  # No losses yet since the layer has never been called_ = layer(ops.zeros((1, 1)))
assert len(layer.losses) == 1  # We created one loss value# `layer.losses` gets reset at the start of each __call__
_ = layer(ops.zeros((1, 1)))
assert len(layer.losses) == 1  # This is the loss created during the call above

此外,损失属性还包含为任何内层权重创建的正则化损失

class OuterLayerWithKernelRegularizer(keras.layers.Layer):def __init__(self):super().__init__()self.dense = keras.layers.Dense(32, kernel_regularizer=keras.regularizers.l2(1e-3))def call(self, inputs):return self.dense(inputs)layer = OuterLayerWithKernelRegularizer()
_ = layer(ops.zeros((1, 1)))# This is `1e-3 * sum(layer.dense.kernel ** 2)`,
# created by the `kernel_regularizer` above.
print(layer.losses)

打印结果:

[Array(0.00217911, dtype=float32)]

在编写自定义训练循环时,应考虑到这些损失。

它们也可以与fit()方法无缝配合使用(如果有的话,会自动将它们求和并添加到主要损失中):

inputs = keras.Input(shape=(3,))
outputs = ActivityRegularizationLayer()(inputs)
model = keras.Model(inputs, outputs)# If there is a loss passed in `compile`, the regularization
# losses get added to it
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))# It's also possible not to pass any loss in `compile`,
# since the model already has a loss to minimize, via the `add_loss`
# call during the forward pass!
model.compile(optimizer="adam")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))

可以选择在您的层上启用序列化功能

如果需要将自定义层作为功能模型的一部分进行序列化,可以选择实现 get_config() 方法

class Linear(keras.layers.Layer):def __init__(self, units=32):super().__init__()self.units = unitsdef build(self, input_shape):self.w = self.add_weight(shape=(input_shape[-1], self.units),initializer="random_normal",trainable=True,)self.b = self.add_weight(shape=(self.units,), initializer="random_normal", trainable=True)def call(self, inputs):return ops.matmul(inputs, self.w) + self.bdef get_config(self):return {"units": self.units}# Now you can recreate the layer from its config:
layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
{'units': 64}

请注意,基本的Layer类的__init__()方法接受一些关键字参数,特别是name和dtype。

在__init__()中将这些参数传递给父类,并将它们包含在层的配置中是一个好的做法。

class Linear(keras.layers.Layer):def __init__(self, units=32, **kwargs):super().__init__(**kwargs)self.units = unitsdef build(self, input_shape):self.w = self.add_weight(shape=(input_shape[-1], self.units),initializer="random_normal",trainable=True,)self.b = self.add_weight(shape=(self.units,), initializer="random_normal", trainable=True)def call(self, inputs):return ops.matmul(inputs, self.w) + self.bdef get_config(self):config = super().get_config()config.update({"units": self.units})return configlayer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
{'name': 'linear_7', 'trainable': True, 'dtype': 'float32', 'units': 64}

如果在从配置反序列化层时需要更大的灵活性,也可以覆盖 from_config() 类方法。

这是 from_config() 的基本实现:

def from_config(cls, config):return cls(**config)

顺序模型主题咱们就到这里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/780356.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言goto语句介绍

在C语言中,goto语句是一种流程控制语句,用于无条件地转移到程序中的特定标签位置。尽管goto语句在编程中具有一定的争议,但在某些情况下,它可以提供一种简单有效的解决方案。本文将深入介绍C语言中的goto语句,包括其基…

前端小白的学习之路(webpack)

提示:webpack简介,nvm,npm配置环境,常用命令,基本web项目构建 目录 webpack 1.配置环境 1)node.js node常用命令 2)nvm nvm常用命令: 3)npm npm常用命令 2.构建简易web项目 1)创建目录 2)安装webpack依赖 3)配置 webpac…

4核32G轻量云服务器优惠价格65元/月、951元一年

京东云4核32G轻量服务器优惠价格65元/月、195元3个月、476元6个月、951元一年,配置4C32G-100G SSD系统盘-8M带宽-1500G月流量 华北-北京,京东云优惠活动 yunfuwuqiba.com/go/jd 活动链接打开如下图: 京东云4核32G轻量服务器优惠价格 京东云&a…

Vue基础配置、组件通信

基础配置 Vue框架已经集成了webpack配置 小注意点 vbase 快速生成vue模板 组件名必须是多词格式(驼峰模式) 具体三种写法: ①小驼峰:abcDef.vue ②大驼峰:AbcDef.vue ③中横线:abc-def.vue 假如文件名不符合多次格式的补救办法: 导出重命名…

写作类AI推荐(二)

本章要介绍的写作AI如下: 火山写作 主要功能: AI智能创作:告诉 AI 你想写什么,立即生成你理想中的文章AI智能改写:选中段落句子,可提升表达、修改语气、扩写、总结、缩写等文章内容优化:根据全文…

【Vue】手写Vue工具函数hasChanged源码

function hasChanged(x,y) {}hasChanged函数接受两个参数,判断参数是否发生变化,即两个参数是否完全相同,发生了改变返回true,否则返回flase。 对象类型数据先比较值,相同则比较址,址不同也返回true。 首先…

Stable Diffusion 模型下载:epiCPhotoGasm(真实、照片)

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 该模型对照片是什么有很高的了解,所以…

泰迪智能科技荣获山东省“技能兴鲁”职业技能大赛优秀组织奖

近日,泰迪智能科技荣获了山东省“技能兴鲁”职业技能大赛——第四届山东省“云数”技能竞赛“优秀组织单位”。 据悉,山东省“技能兴鲁”职业技能大赛——第四届山东省“云数”技能竞赛是由山东电子学会、山东省信息产业协会主办的,该竞赛是通…

OpenHarmony OpenCV应用样例开发

背景 OpenCV 介绍 OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它由一系列的 C 函数和少量 C 类构成,同时提供 Python、Java 和 MATLAB 等语言的接口,实现了图像处理和计算机视觉方面…

java-pytorch 使用手动下载FashionMNIST数据集进行测试

java-pytorch 使用手动下载FashionMNIST数据集进行测试 先定义训练数据和测试数据的位置查看一下读取到的标签数据格式使用loc和iloc访问下数据,便于下面操作使用read_image函数查看下图片的数据大小开始写数据集使用DataLoader去加载我们自己的数据看下加载后的dat…

FANUC机器人故障诊断—报警代码(一)

一、SRVO-050碰撞检测报警 [原因]检测出碰撞 [对策] 1.确认机器人是否碰撞。 2.确认是否正确进行了负载设定。 3.确认是否有过载、过度的加速度附加指令。 4.在长期停用后启动,或者外部气温较低时发生该报警。启动后,先短时间内低速运转设备&#…

vue3使用UEditorPlus 、后端配置、上传图片等处理

前端安装 vue3安装vue-ueditor-wrap // vue-ueditor-wrap v3 仅支持 Vue 3 npm i vue-ueditor-wrap3.x -S // or yarn add vue-ueditor-wrap3.x 下载 UEditorPlus 仓库地址 把dist文件复制到vue3项目中的public下,重命名为UEditorPlus UEditorPlus文档 在main.…

ArcGIS支持下SWAT与CENTURY模型的结合:流域水碳氮综合模拟

目录 专题一 流域水碳氮建模 专题二 数据准备 专题三 流域水模拟 专题四 流域氮模拟 专题五 流域碳模拟 专题六 模型结果分析及地图制作 更多应用 基于ArcGIS的SWAT模型是一类比较典型的流域模型,结合SWAT模型和生物地球化学循环模型可以实现流域水碳氮综合模…

根据实例逐行分析NIO到底在做什么

Selector(选择器)是 Channel 的多路复用器,它可以同时监控多个 Channel 的 IO 状况,允许单个线程来操作多个 Channel。Channel在从Buffer中获取数据。 选择器、通道、缓冲池是NIO的核心组件。 一、新建选择器 此时选择器内只包含…

Nginx-记

Nginx是一个高性能的web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。 (1)更快 这表现在两个方面:一方面,在正常情况下&…

【OpenGL】使用 python + Qt + OpenGL 的现代渲染

伴随资源 目录 一、说明二、 关于PyQt6.x2.1 QOpenGLWidget详细说明2.2 绘画技巧 三、PyOpenGL四、OpenGL 管线五、Python集成开发环境5.1 Emacs配置5.2 pycharm环境 六、你好,OpenGL!七、QGL控件八、平截头体.svg九、定义几何9.1 立即模式与保留模式9…

OpenAI发布Voice Engine模型!用AI合成你的声音!

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,所以创建了“AI信息Gap”这个公众号,专注于分享AI全维度知识…

比torchvision更强大,从timm库引用预训练模型和本地加载的方法

1,介绍 torchvision是大家最常用的预训练模型来源,但是其包含的预训练模型种类很少,往往并不能满足研究者们的需求。 而timm库提供了一个更强大的替代选项。 利用如下代码查询 import timmprint(len(timm.list_models())) 输出 1032 可…

Android Studio 2023.2.1版本 kotlin编译报错踩坑

1、需求 由于最近在整理项目,做一些公共基础组件Maven仓库封装,由于之前项目jar包和kotlin版本很老,kotlin版本1.3.72版本 Gradle使用5.4.1 Android Studio版本是2023.2.1,分别依次顺序如下图所示。 如下图所示 2、分析编译报错…