政安晨:【Keras机器学习实践要点】(九)—— 保存、序列化和导出模型

政安晨的个人主页政安晨

欢迎 👍点赞✍评论⭐收藏

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

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

这篇文章是保存、序列化和导出模型的完整指南。

介绍

Keras 模型由多个组件组成:

架构或配置,指定模型包含哪些层,以及它们如何连接。
一组权重值("模型状态")。
一个优化器(通过编译模型定义)。
一组损失和度量(通过编译模型定义)。


Keras API 将所有这些部分保存为统一格式,以 .keras 扩展名标记。

这是一个由以下内容组成的压缩包:

基于 JSON 的配置文件 (config.json):模型、层和其他可跟踪配置的记录。
基于 H5 的状态文件,如 model.weights.h5(用于整个模型),其中包含层及其权重的目录键。
JSON 格式的元数据文件,存储当前 Keras 版本等信息。

让我们来看看它是如何工作的:

如何保存和加载模型

如果您只有 10 秒钟时间阅读本文,那么您需要了解以下内容:

保存一个Keras模型

model = ...  # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location.keras')  # The file needs to end with the .keras extension

装回模型

model = keras.models.load_model('path/to/location.keras')


接下来,让咱们来看看细节。

设置

import numpy as np
import keras
from keras import ops

保存

本文将把整个模型保存到一个文件中文件将包括

模型的架构/配置
模型的权重值(在训练过程中学到的权重值)
模型的编译信息(如果调用了编译()
优化器及其状态(如果有的话)(这样就可以在训练结束后重新开始训练

应用程序接口
您可以使用 model.save() 或 keras.models.save_model()(两者等效)保存模型。您可以使用 keras.models.load_model() 加载模型。

Keras 3 中唯一支持的格式是使用 .keras 扩展名的 "Keras v3 "格式。

例子:

def get_model():
    # Create a simple model.
    inputs = keras.Input(shape=(32,))
    outputs = keras.layers.Dense(1)(inputs)
    model = keras.Model(inputs, outputs)
    model.compile(optimizer=keras.optimizers.Adam(), loss="mean_squared_error")
    return model


model = get_model()

# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)

# Calling `save('my_model.keras')` creates a zip archive `my_model.keras`.
model.save("my_model.keras")

# It can be used to reconstruct the model identically.
reconstructed_model = keras.models.load_model("my_model.keras")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

演绎结果:

自定义对象

本文介绍在 Keras 保存和重新加载中处理自定义层、函数和模型的基本工作流程。

保存包含自定义对象(如子类层)的模型时,必须在对象类上定义 get_config() 方法。如果传给自定义对象的构造函数(__init__() 方法)的参数不是 Python 对象(除 ints、字符串等基本类型外的任何其他类型),则还必须在 from_config() 类方法中显式地反序列化这些参数。

就像这样:

class CustomLayer(keras.layers.Layer):
    def __init__(self, sublayer, **kwargs):
        super().__init__(**kwargs)
        self.sublayer = layer

    def call(self, x):
        return self.sublayer(x)

    def get_config(self):
        base_config = super().get_config()
        config = {
            "sublayer": keras.saving.serialize_keras_object(self.sublayer),
        }
        return {**base_config, **config}

    @classmethod
    def from_config(cls, config):
        sublayer_config = config.pop("sublayer")
        sublayer = keras.saving.deserialize_keras_object(sublayer_config)
        return cls(sublayer, **config)

保存的 .keras 文件是轻量级的,不会存储自定义对象的 Python 代码。

因此,要重新加载模型,load_model 需要通过以下方法之一访问任何自定义对象的定义:

1. 注册自定义对象(首选)
2. 加载时直接传递自定义对象,或
3. 使用自定义对象作用域

下面是每个工作流程的示例:

注册自定义对象(首选)

这是首选方法,因为自定义对象注册大大简化了保存和加载代码。在自定义对象的类定义中添加 @keras.saving.register_keras_serializable 装饰器,可在主列表中全局注册该对象,从而让 Keras 在加载模型时识别该对象。

让我们创建一个涉及自定义层和自定义激活函数的自定义模型来演示一下:

例子:

# Clear all previously registered custom objects
keras.saving.get_custom_objects().clear()


# Upon registration, you can optionally specify a package or a name.
# If left blank, the package defaults to `Custom` and the name defaults to
# the class name.
@keras.saving.register_keras_serializable(package="MyLayers")
class CustomLayer(keras.layers.Layer):
    def __init__(self, factor):
        super().__init__()
        self.factor = factor

    def call(self, x):
        return x * self.factor

    def get_config(self):
        return {"factor": self.factor}


@keras.saving.register_keras_serializable(package="my_package", name="custom_fn")
def custom_fn(x):
    return x**2


# Create the model.
def get_model():
    inputs = keras.Input(shape=(4,))
    mid = CustomLayer(0.5)(inputs)
    outputs = keras.layers.Dense(1, activation=custom_fn)(mid)
    model = keras.Model(inputs, outputs)
    model.compile(optimizer="rmsprop", loss="mean_squared_error")
    return model


# Train the model.
def train_model(model):
    input = np.random.random((4, 4))
    target = np.random.random((4, 1))
    model.fit(input, target)
    return model


test_input = np.random.random((4, 4))
test_target = np.random.random((4, 1))

model = get_model()
model = train_model(model)
model.save("custom_model.keras")

# Now, we can simply load without worrying about our custom objects.
reconstructed_model = keras.models.load_model("custom_model.keras")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

向 load_model() 传递自定义对象

model = get_model()
model = train_model(model)

# Calling `save('my_model.keras')` creates a zip archive `my_model.keras`.
model.save("custom_model.keras")

# Upon loading, pass a dict containing the custom objects used in the
# `custom_objects` argument of `keras.models.load_model()`.
reconstructed_model = keras.models.load_model(
    "custom_model.keras",
    custom_objects={"CustomLayer": CustomLayer, "custom_fn": custom_fn},
)

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

使用自定义对象范围

自定义对象作用域中的任何代码都能识别传递给作用域参数的自定义对象。因此,在作用域中加载模型将允许加载我们的自定义对象。

例子:

model = get_model()
model = train_model(model)
model.save("custom_model.keras")

# Pass the custom objects dictionary to a custom object scope and place
# the `keras.models.load_model()` call within the scope.
custom_objects = {"CustomLayer": CustomLayer, "custom_fn": custom_fn}

with keras.saving.custom_object_scope(custom_objects):
    reconstructed_model = keras.models.load_model("custom_model.keras")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

模型序列化

本文只涉及保存模型的配置,而不涉及其状态。模型的配置(或架构)规定了模型包含哪些层以及这些层的连接方式。如果有了模型的配置,就可以用全新的初始化状态(没有权重或编译信息)来创建模型。

APIs

以下是可用的序列化 API

keras.models.clone_model(model):制作模型的(随机初始化的)副本。
get_config() 和 cls.from_config():分别检索层或模型的配置,并根据其配置重新创建模型实例。
keras.models.model_to_json()和 keras.models.model_from_json():类似,但为 JSON 字符串。
keras.saving.serialize_keras_object():检索任意 Keras 对象的配置。
keras.saving.deserialize_keras_object():根据配置重新创建对象实例。

内存模型克隆

您可以通过 keras.models.clone_model() 在内存中克隆模型。这相当于获取配置,然后根据配置重新创建模型(因此不会保留编译信息或层权重值)。

例子:

new_model = keras.models.clone_model(model)

get_config() 和 from_config()

调用 model.get_config() 或 layer.get_config() 将分别返回一个包含模型或层配置的 Python dict。您应该定义 get_config(),使其包含模型或层的 __init__() 方法所需的参数。加载时,from_config(config) 方法将使用这些参数调用 __init__() 来重构模型或层。

层示例:

layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_config()
print(layer_config)
显示如下:

{'name': 'dense_4', 'trainable': True, 'dtype': 'float32', 'units': 3, 'activation': 'relu', 'use_bias': True, 'kernel_initializer': {'module': 'keras.initializers', 'class_name': 'GlorotUniform', 'config': {'seed': None}, 'registered_name': None}, 'bias_initializer': {'module': 'keras.initializers', 'class_name': 'Zeros', 'config': {}, 'registered_name': None}, 'kernel_regularizer': None, 'bias_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}

现在,让我们使用 from_config() 方法重建图层:

new_layer = keras.layers.Dense.from_config(layer_config)

顺序模型示例:

model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
config = model.get_config()
new_model = keras.Sequential.from_config(config)

功能模型示例:

inputs = keras.Input((32,))
outputs = keras.layers.Dense(1)(inputs)
model = keras.Model(inputs, outputs)
config = model.get_config()
new_model = keras.Model.from_config(config)

to_json() 和 keras.models.model_from_json()


这与 get_config / from_config 类似,但它会将模型转化为 JSON 字符串,然后无需原始模型类即可加载。它也是模型专用的,并不适用于层。

例子:

model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
json_config = model.to_json()
new_model = keras.models.model_from_json(json_config)

任意对象序列化和反序列化

keras.saving.serialize_keras_object() 和 keras.saving.deserialize_keras_object() API 是通用 API,可用于序列化或反序列化任何 Keras 对象和任何自定义对象。它是保存模型架构的基础,也是 keras 中所有序列化()/反序列化()调用的基础。

例子:

my_reg = keras.regularizers.L1(0.005)
config = keras.saving.serialize_keras_object(my_reg)
print(config)

显示如下:

{'module': 'keras.regularizers', 'class_name': 'L1', 'config': {'l1': 0.004999999888241291}, 'registered_name': None}

注意序列化格式,其中包含正确重建所需的所有信息:
 

module 包含对象来自的 Keras 模块或其他标识模块的名称。
class_name 包含对象的类名。
config 包含重构对象所需的所有信息
registered_name 用于自定义对象。

现在我们可以重构正则表达式了。

new_reg = keras.saving.deserialize_keras_object(config)

保存模型权重

您可以选择只保存和加载模型的权重。这在以下情况下非常有用:

您只需要模型进行推理:在这种情况下,您不需要重新开始训练,因此不需要编译信息或优化器状态。
您正在进行迁移学习:在这种情况下,您将重复使用先前模型的状态来训练一个新模型,因此您不需要先前模型的编译信息。

内存中的权重传递接口

使用 get_weights() 和 set_weights(),可以在不同对象之间复制权重

keras.layers.Layer.get_weights():返回权重值的 NumPy 数组列表。
keras.layers.Layer.set_weights(weights):将模型权重设置为所提供的值(NumPy 数组)。

例子:

在内存中将权重从一层转移到另一层

def create_layer():
    layer = keras.layers.Dense(64, activation="relu", name="dense_2")
    layer.build((None, 784))
    return layer


layer_1 = create_layer()
layer_2 = create_layer()

# Copy weights from layer 1 to layer 2
layer_2.set_weights(layer_1.get_weights())

在内存中将权重从一个模型转移到另一个具有兼容结构的模型上

# Create a simple functional model
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")


# Define a subclassed model with the same architecture
class SubclassedModel(keras.Model):
    def __init__(self, output_dim, name=None):
        super().__init__(name=name)
        self.output_dim = output_dim
        self.dense_1 = keras.layers.Dense(64, activation="relu", name="dense_1")
        self.dense_2 = keras.layers.Dense(64, activation="relu", name="dense_2")
        self.dense_3 = keras.layers.Dense(output_dim, name="predictions")

    def call(self, inputs):
        x = self.dense_1(inputs)
        x = self.dense_2(x)
        x = self.dense_3(x)
        return x

    def get_config(self):
        return {"output_dim": self.output_dim, "name": self.name}


subclassed_model = SubclassedModel(10)
# Call the subclassed model once to create the weights.
subclassed_model(np.ones((1, 784)))

# Copy weights from functional_model to subclassed_model.
subclassed_model.set_weights(functional_model.get_weights())

assert len(functional_model.weights) == len(subclassed_model.weights)
for a, b in zip(functional_model.weights, subclassed_model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())

无状态层的情况

由于无状态层不会改变权重的顺序或数量,因此,即使存在额外/缺失的无状态层,模型也能拥有兼容的架构。

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)

# Add a dropout layer, which does not contain any weights.
x = keras.layers.Dropout(0.5)(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model_with_dropout = keras.Model(
    inputs=inputs, outputs=outputs, name="3_layer_mlp"
)

functional_model_with_dropout.set_weights(functional_model.get_weights())

保存权重到存储介质与加载回权重的接口

权重可以通过调用 model.save_weights(filepath) 保存到磁盘。文件名应以 .weights.h5 结尾。

例子:

# Runnable example
sequential_model = keras.Sequential(
    [
        keras.Input(shape=(784,), name="digits"),
        keras.layers.Dense(64, activation="relu", name="dense_1"),
        keras.layers.Dense(64, activation="relu", name="dense_2"),
        keras.layers.Dense(10, name="predictions"),
    ]
)
sequential_model.save_weights("my_model.weights.h5")
sequential_model.load_weights("my_model.weights.h5")

请注意,当模型包含嵌套层时,更改 layer.trainable 可能会导致 layer.weights 排序不同。

class NestedDenseLayer(keras.layers.Layer):
    def __init__(self, units, name=None):
        super().__init__(name=name)
        self.dense_1 = keras.layers.Dense(units, name="dense_1")
        self.dense_2 = keras.layers.Dense(units, name="dense_2")

    def call(self, inputs):
        return self.dense_2(self.dense_1(inputs))


nested_model = keras.Sequential([keras.Input((784,)), NestedDenseLayer(10, "nested")])
variable_names = [v.name for v in nested_model.weights]
print("variables: {}".format(variable_names))

print("\nChanging trainable status of one of the nested layers...")
nested_model.get_layer("nested").dense_1.trainable = False

variable_names_2 = [v.name for v in nested_model.weights]
print("\nvariables: {}".format(variable_names_2))
print("variable ordering changed:", variable_names != variable_names_2)

 显示如下:

variables: ['kernel', 'bias', 'kernel', 'bias']
Changing trainable status of one of the nested layers...
variables: ['kernel', 'bias', 'kernel', 'bias']
variable ordering changed: False

迁移学习示例

从权重文件加载预训练的权重时,建议将权重加载到原始检查点模型中,然后将所需权重/图层提取到新模型中。

示例:

def create_functional_model():
    inputs = keras.Input(shape=(784,), name="digits")
    x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
    x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
    outputs = keras.layers.Dense(10, name="predictions")(x)
    return keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")


functional_model = create_functional_model()
functional_model.save_weights("pretrained.weights.h5")

# In a separate program:
pretrained_model = create_functional_model()
pretrained_model.load_weights("pretrained.weights.h5")

# Create a new model by extracting layers from the original model:
extracted_layers = pretrained_model.layers[:-1]
extracted_layers.append(keras.layers.Dense(5, name="dense_3"))
model = keras.Sequential(extracted_layers)
model.summary()

处理自定义对象

定义配置方法

Specifications:

get_config() 应返回一个可序列化的 JSON 字典,以便与 Keras 架构和模型保存 API 兼容。
from_config(config)(类方法)应返回根据配置创建的新层或模型对象。默认实现返回 cls(**config)。

注意:

如果您的所有构造函数参数(如字符串和 int 或非自定义 Keras 对象)都已可序列化,则无需重载 from_config。但是,对于传递给 __init__ 的层或模型等更复杂的对象,必须在 __init__ 本身或重载 from_config() 方法中明确处理反序列化。

例子:

@keras.saving.register_keras_serializable(package="MyLayers", name="KernelMult")
class MyDense(keras.layers.Layer):
    def __init__(
        self,
        units,
        *,
        kernel_regularizer=None,
        kernel_initializer=None,
        nested_model=None,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.hidden_units = units
        self.kernel_regularizer = kernel_regularizer
        self.kernel_initializer = kernel_initializer
        self.nested_model = nested_model

    def get_config(self):
        config = super().get_config()
        # Update the config with the custom layer's parameters
        config.update(
            {
                "units": self.hidden_units,
                "kernel_regularizer": self.kernel_regularizer,
                "kernel_initializer": self.kernel_initializer,
                "nested_model": self.nested_model,
            }
        )
        return config

    def build(self, input_shape):
        input_units = input_shape[-1]
        self.kernel = self.add_weight(
            name="kernel",
            shape=(input_units, self.hidden_units),
            regularizer=self.kernel_regularizer,
            initializer=self.kernel_initializer,
        )

    def call(self, inputs):
        return ops.matmul(inputs, self.kernel)


layer = MyDense(units=16, kernel_regularizer="l1", kernel_initializer="ones")
layer3 = MyDense(units=64, nested_model=layer)

config = keras.layers.serialize(layer3)

print(config)

new_layer = keras.layers.deserialize(config)

print(new_layer)

显示为:

{'module': None, 'class_name': 'MyDense', 'config': {'name': 'my_dense_1', 'trainable': True, 'dtype': 'float32', 'units': 64, 'kernel_regularizer': None, 'kernel_initializer': None, 'nested_model': {'module': None, 'class_name': 'MyDense', 'config': {'name': 'my_dense', 'trainable': True, 'dtype': 'float32', 'units': 16, 'kernel_regularizer': 'l1', 'kernel_initializer': 'ones', 'nested_model': None}, 'registered_name': 'MyLayers>KernelMult'}}, 'registered_name': 'MyLayers>KernelMult'}
<MyDense name=my_dense_1, built=False>

请注意,对于 MyDense 而言,无需覆盖 from_config,因为 hidden_units、kernel_initializer 和 kernel_regularizer 分别是 ints、字符串和 Keras 内置对象。

这意味着 cls(**config) 的默认 from_config 实现将如期工作。

对于更复杂的对象,例如传递给 __init__ 的层和模型,必须显式地反序列化这些对象。

下面我们来看一个需要重写 from_config 的模型示例。

例子:

@keras.saving.register_keras_serializable(package="ComplexModels")
class CustomModel(keras.layers.Layer):
    def __init__(self, first_layer, second_layer=None, **kwargs):
        super().__init__(**kwargs)
        self.first_layer = first_layer
        if second_layer is not None:
            self.second_layer = second_layer
        else:
            self.second_layer = keras.layers.Dense(8)

    def get_config(self):
        config = super().get_config()
        config.update(
            {
                "first_layer": self.first_layer,
                "second_layer": self.second_layer,
            }
        )
        return config

    @classmethod
    def from_config(cls, config):
        # Note that you can also use [`keras.saving.deserialize_keras_object`](/api/models/model_saving_apis/serialization_utils#deserializekerasobject-function) here
        config["first_layer"] = keras.layers.deserialize(config["first_layer"])
        config["second_layer"] = keras.layers.deserialize(config["second_layer"])
        return cls(**config)

    def call(self, inputs):
        return self.first_layer(self.second_layer(inputs))


# Let's make our first layer the custom layer from the previous example (MyDense)
inputs = keras.Input((32,))
outputs = CustomModel(first_layer=layer)(inputs)
model = keras.Model(inputs, outputs)

config = model.get_config()
new_model = keras.Model.from_config(config)

如何序列化自定义对象

序列化格式有一个特殊的键,用于通过 @keras.saving.register_keras_serializable 注册的自定义对象。这个 registered_name 密钥便于在加载/反序列化时进行检索,同时也允许用户添加自定义命名。

让我们来看看上面定义的 MyDense 自定义层序列化时的配置。

例子:

layer = MyDense(
    units=16,
    kernel_regularizer=keras.regularizers.L1L2(l1=1e-5, l2=1e-4),
    kernel_initializer="ones",
)
config = keras.layers.serialize(layer)
print(config)

显示如下:

{'module': None, 'class_name': 'MyDense', 'config': {'name': 'my_dense_2', 'trainable': True, 'dtype': 'float32', 'units': 16, 'kernel_regularizer': {'module': 'keras.regularizers', 'class_name': 'L1L2', 'config': {'l1': 1e-05, 'l2': 0.0001}, 'registered_name': None}, 'kernel_initializer': 'ones', 'nested_model': None}, 'registered_name': 'MyLayers>KernelMult'}

registered_name 关键字包含 Keras 主列表的查找信息,包括 MyLayers 包和我们在 @keras.saving.register_keras_serializable 装饰器中给出的自定义名称 KernelMult。

请注意,class_name 关键字包含类的原始名称,以便在 from_config 中重新初始化。

此外,请注意 module 关键字为 None,因为这是一个自定义对象。


这就是保存与加载模型的全部内容啦。(呵呵)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python实战之进阶篇(一)

定义类 1. 构造方法 2. 实例方法 3. 类方法 类似于Java中的静态方法, 使用方式: 类名.类方法 4. 私有变量 5. 私有方法 6. 使用属性set和get

vue3+threejs新手从零开发卡牌游戏(二十一):添加战斗与生命值关联逻辑

首先将双方玩家的HP存入store中&#xff0c;stores/common.ts代码如下&#xff1a; import { ref, computed } from vue import { defineStore } from piniaexport const useCommonStore defineStore(common, () > {const _font ref() // 字体const p1HP ref(4000) // 己…

4、Cocos Creator 动画系统

目录 1、Clip 参数 2、动画编辑器 3、基本操作 更改时间轴缩放比例 移动显示区域 更改当前选中的时间轴节点 播放 / 暂停动画 修改 clip 属性 快捷键 4、模拟实验 5、动画事件 6、注意事项 参考 Animation 组件是节点上的一个组件。Clip 动画剪辑就是一份动画的声…

【edge浏览器无法登录某些网站,以及迅雷插件无法生效的解决办法】

edge浏览器无法登录某些网站&#xff0c;以及迅雷插件无法生效的解决办法 edge浏览器无法登录某些网站&#xff0c;但chrome浏览器可以登录浏览器插件无法使用&#xff0c;比如迅雷如果重装插件重装浏览器重装迅雷后仍然出现问题 edge浏览器无法登录某些网站&#xff0c;但chro…

InfoNCE loss

InfoNCE loss是一种用于自监督学习的损失函数&#xff0c;通常用于训练对比学习模型&#xff0c;如自编码器或神经网络。全称是"InfoNCE: Contrastive Estimation of Neural Entropy"&#xff0c;基于对比学习的思想&#xff0c;旨在最大化正样本的相似性&#xff0c…

QSplashScreen

以前打红警的时候进入游戏界面会有一个启动界面&#xff0c;比如美国是有伞兵&#xff0c;英国有狙击手&#xff0c;韩国有黑鹰战机的一些介绍&#xff0c;这些就是启动界面&#xff0c;就是由QSplashScreen这个类来实现的。 QSplashScreen 是 Qt 框架中的一个类&#xff0c;用…

【每日跟读】常用英语500句(300~400)

【每日跟读】常用英语500句 I had to take a shower. 我洗了个澡 Go on in. 赶紧进去吧 Hold up. 等一下 They seem like nice people. 他们看起来像好人 Such a wonderful age. 如此美好的年纪 That’s very impressive. 真厉害 I can see that. 看得出来 You should …

绘制多个box箱型图

1.首先生成随机数据 import random # 创建一个153629行&#xff0c;13列的数据&#xff0c; random_data np.random.randn(153629, 13) #创建数据标签&#xff0c;后续将根据数据标签绘制不同的箱型图 label [0,1,2,3] labels np.asarray(random.choices(label,k 153629))…

U盘文件突然消失:原因分析与恢复策略

U盘遭遇“幽灵”之手&#xff0c;文件不翼而飞 你是否曾遭遇过这样的诡异情况&#xff1a;前一天还好好存放在U盘里的文件&#xff0c;第二天却突然消失得无影无踪&#xff1f;这简直就像是一场无声的灾难&#xff0c;令人措手不及。U盘作为我们日常工作和生活中不可或缺的数据…

Gitea 的详细介绍

什么是 Gitea&#xff1f; Gitea 是一个开源、轻量级的自托管 Git 服务&#xff0c;它允许用户搭建类似于 GitHub 或 GitLab 的代码托管平台。由于采用 Go 语言开发&#xff0c;Gitea 具有高效的性能和跨平台特性&#xff0c;适合个人开发者或小团队使用。 Gitea 的特点 轻量…

java数据结构与算法刷题-----LeetCode34. 在排序数组中查找元素的第一个和最后一个位置

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 二分查找 二分查找 解题思路&#xff1a;时间复杂度O( l o g 2 …

读所罗门的密码笔记05_新的力量平衡

1. 技术发展 1.1. 美国和中国俨然成为人工智能研究、开发和部署方面无可争议的领导者 1.2. 人类从未彻底阻止某种技术的发展&#xff0c;虽然不同国家在不同时间对克隆、化学武器和核武器等技术采取了暂停或禁止措施&#xff0c;但我们仍在继续推进最前…

mysql之MyBatis核心工作原理

MyBatis核心工作原理 一、源码环境 1.手动编译源码 工欲善其事必先利其器。为了方便我们在看源码的过程中能够方便的添加注释&#xff0c;我们可以自己来从官网下载源码编译生成对应的Jar包&#xff0c;然后上传到本地maven仓库&#xff0c;再引用这个Jar。大家可以自行去官…

第十四届蓝桥杯第十题:蜗牛分享

问题描述 输入格式 输出格式 输出共一行&#xff0c;一个浮点数表示答案&#xff08;四舍五入保留两位小数&#xff09;。 样例输入 3 1 10 11 1 1 2 1样例输出 4.20样例说明 蜗牛路线&#xff1a;(0,0)→(1,0)→(1,1)→(10,1)→(10,0)→(11,0)(0,0)→(1,0)→(1,1)→(10,1…

vsqt更改ui,cpp报错(唯二)解决方法,及ui界面布局在cpp文件的运用基本流程

qt的ui布局界面如下 点cpp文件->编译 此时就会自动生成ui_xxx.h 这里是ui文件里面就有类名&#xff1a;Ui_文件名字 下面就有一个类继承于这个类 你所使用的这个ui指针&#xff0c;就这么来的 ***报错解决方法有两种&#xff1a;***第一种&#xff1a;如果改了ui&#x…

Qt+OpenGL入门教程(三)——绘制三角形

通过前两篇文章的学习&#xff0c;我想大家应该有了基本的理解&#xff0c;我们接下来实操一下。 创建Qt OpenGL窗口 QOpenGLWidget QGLWidget是传统QtOpenGL模块的一部分&#xff0c;与其他QGL类一样&#xff0c;应该在新的应用程序中避免使用。相反&#xff0c;从Qt5.4开始…

(五)ROS2学习--创建调用其它包接口的一个发布者

这里写自定义目录标题 一、背景二、构建步骤1. 构建项目包2. 创建消息接口3. 修改“package.xml”4. 修改"src/smart_car/CMakeLists.txt"5. 创建发布者程序 三、编译及验证1. 编译2. 验证 一、背景 主机&#xff1a;Ubuntu20.04 介绍&#xff1a;基于上一篇&#x…

超卖问题的 4 种解决方案来了,太硬核了

大家好&#xff0c;我是路人&#xff0c;最近刚推出的《Java 高并发 & 微服务 & 性能调优实战案例 100 讲》&#xff0c;此课程目前已发布上线&#xff0c;正在连载中&#xff0c;文末有观看方法。 所有案例均源于个人工作实战&#xff0c;均提供原理讲解 & 亲手敲…

手写SpringBoot(三)之自动配置

系列文章目录 手写SpringBoot&#xff08;一&#xff09;之简易版SpringBoot 手写SpringBoot&#xff08;二&#xff09;之动态切换Servlet容器 手写SpringBoot&#xff08;三&#xff09;之自动配置 手写SpringBoot&#xff08;四&#xff09;之bean动态加载 手写SpringBoot…

重构销售话术和知识库,容联云找到了大模型的“钉子”

科技云报道原创。 从ChatGPT诞生起&#xff0c;大模型在营销、客服等场景的落地就被予以众望。然而在经历了一年多的“百模大战”洗礼之后&#xff0c;人们发现无论是算力成本还是内容生成的安全合规问题&#xff0c;都让大模型很难直接应用于机器与人对话的实际业务中。 这其…