智能部署之巅:Amazon SageMaker 引领机器学习革新

本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 亚马逊云科技开发者社区, 知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道。

(全球 TMT 2023年12月6日讯)亚马逊云科技在 2023 re:Invent 全球大会上,宣布推出五项 Amazon SageMaker 新功能,帮助客户加速构建、训练和部署大语言模型和其他基础模型。自2017年推出以来,Amazon SageMaker已经新增了380多个功能和特性,为客户提供了规模化构建、训练和部署可投入生产的大规模模型所需的一切。

亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术,观点,和项目,并将中国优秀开发者或技术推荐给全球云社区。如果你还没有关注/收藏,看到这里请一定不要匆匆划过,点这里让它成为你的技术宝库!

其中包括了一项新功能,进一步增强了 Amazon SageMaker 的模型扩展能力并加速了模型的训练。此外,Amazon SageMaker 推出的另一项新功能,能够通过降低模型部署成本和延迟时间,优化了管理托管的机器学习基础设施。亚马逊云科技还推出了新的 SageMaker Clarify 功能,可以让客户在负责任地使用 AI 的基础上,根据质量参数更轻松地选择正确模型。为了帮助客户在企业范围内应用这些模型,亚马逊云科技还在 Amazon SageMaker Canvas 中引入了新的无代码功能,让客户更快、更容易地使用自然语言指令准备数据。同时,Amazon SageMaker Canvas 继续推动模型构建和定制的普及,让客户更轻松地使用模型提取洞察、进行预测和使用企业专有数据生成内容。这些创新均基于 Amazon SageMaker 丰富的功能,帮助客户实现规模化机器学习创新。

1. 模型训练速度和效果

一些测评显示,Amazon SageMaker 在模型训练方面表现出色。其支持分布式训练,利用弹性计算资源,能够显著缩短训练时间。用户可以选择使用内置算法或自定义脚本,根据实际需求选择合适的训练方式。此外,自动模型调优功能可以帮助用户优化模型性能,提高训练效果。

2. 部署和管理的便捷性

Amazon SageMaker 提供了简单而强大的模型部署功能。它支持一键式部署,并提供实时和离线推理选项,适用于不同的应用场景。此外,SageMaker Studio 作为一体化的集成开发环境,为用户提供了方便的模型管理和监控工具,使整个部署和管理过程更加便捷。

image.png

SageMaker pytorch Mnist

SageMaker 与 Kubeflow 一个区别就在于:

在 Kubeflow 中,我们可以为管道内各组件挂载相同 PV 卷,使其运行在同一文件系统环境下,或者像 Elyra,为整个 Pipeline 配置一个 Minio 的 Bucket,作为共同的文件系统工作环境;

在 SageMaker 里,我们会建立一个会话,并设置默认存储桶 Bucket,之后将要用于模型训练的数据集上传至该存储桶中;

下载、转化并上传数据至 S3

为当前 SageMaker 会话设置默认 S3 存储桶 URI,创建一个新文件夹 prefix,然后将数据集上传至该文件夹下。

import sagemaker

sagemaker_session = sagemaker.Session()

bucket = sagemaker_session.default_bucket()
prefix = "sagemaker/DEMO-pytorch-mnist"

role = sagemaker.get_execution_role()

### 下载数据
from torchvision.datasets import MNIST
from torchvision import transforms

MNIST.mirrors = ["https://sagemaker-sample-files.s3.amazonaws.com/datasets/image/MNIST/"]

MNIST(
    "data",
    download=True,
    transform=transforms.Compose(
        [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
    ),
)

### 上传数据至 Amazon S3
inputs = sagemaker_session.upload_data(path="data", bucket=bucket, key_prefix=prefix)
print("input spec (in this case, just an S3 path): {}".format(inputs))
训练模型脚本

和 Kubeflow 一样,准备一份可以直接运行的训练模型脚本:

### ------------------------   mnist.py   --------------------------

# Based on https://github.com/pytorch/examples/blob/master/mnist/main.py

import argparse
import json
import logging
import os
import sys

import torch
import torch.distributed as dist
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data
import torch.utils.data.distributed
from torchvision import datasets, transforms

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout))

class Net(nn.Module):
    def __init__(self):
        ...

    def forward(self, x):
        ...


def _get_train_data_loader(batch_size, training_dir, is_distributed, **kwargs):
    logger.info("Get train data loader")
    dataset = datasets.MNIST(
        training_dir,
        train=True,
        transform=transforms.Compose(
            [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
        ),
    )
    train_sampler = (
        torch.utils.data.distributed.DistributedSampler(dataset) if is_distributed else None
    )
    return torch.utils.data.DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=train_sampler is None,
        sampler=train_sampler,
        **kwargs
    )


def _get_test_data_loader(test_batch_size, training_dir, **kwargs):
    logger.info("Get test data loader")
    return torch.utils.data.DataLoader(
        datasets.MNIST(
            training_dir,
            train=False,
            transform=transforms.Compose(
                [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
            ),
        ),
        batch_size=test_batch_size,
        shuffle=True,
        **kwargs
    )


def _average_gradients(model):
    # Gradient averaging.
    size = float(dist.get_world_size())
    for param in model.parameters():
        dist.all_reduce(param.grad.data, op=dist.reduce_op.SUM)
        param.grad.data /= size


def train(args):
    is_distributed = len(args.hosts) > 1 and args.backend is not None
    logger.debug("Distributed training - {}".format(is_distributed))
    use_cuda = args.num_gpus > 0
    logger.debug("Number of gpus available - {}".format(args.num_gpus))
    kwargs = {"num_workers": 1, "pin_memory": True} if use_cuda else {}
    device = torch.device("cuda" if use_cuda else "cpu")

    if is_distributed:
        # Initialize the distributed environment.
        world_size = len(args.hosts)
        os.environ["WORLD_SIZE"] = str(world_size)
        host_rank = args.hosts.index(args.current_host)
        os.environ["RANK"] = str(host_rank)
        dist.init_process_group(backend=args.backend, rank=host_rank, world_size=world_size)
        logger.info(
            "Initialized the distributed environment: '{}' backend on {} nodes. ".format(
                args.backend, dist.get_world_size()
            )
            + "Current host rank is {}. Number of gpus: {}".format(dist.get_rank(), args.num_gpus)
        )

    # set the seed for generating random numbers
    torch.manual_seed(args.seed)
    if use_cuda:
        torch.cuda.manual_seed(args.seed)

    train_loader = _get_train_data_loader(args.batch_size, args.data_dir, is_distributed, **kwargs)
    test_loader = _get_test_data_loader(args.test_batch_size, args.data_dir, **kwargs)

    logger.debug(
        "Processes {}/{} ({:.0f}%) of train data".format(
            len(train_loader.sampler),
            len(train_loader.dataset),
            100.0 * len(train_loader.sampler) / len(train_loader.dataset),
        )
    )

    logger.debug(
        "Processes {}/{} ({:.0f}%) of test data".format(
            len(test_loader.sampler),
            len(test_loader.dataset),
            100.0 * len(test_loader.sampler) / len(test_loader.dataset),
        )
    )

    model = Net().to(device)
    if is_distributed and use_cuda:
        # multi-machine multi-gpu case
        model = torch.nn.parallel.DistributedDataParallel(model)
    else:
        # single-machine multi-gpu case or single-machine or multi-machine cpu case
        model = torch.nn.DataParallel(model)

    optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)

    for epoch in range(1, args.epochs + 1):
        model.train()
        for batch_idx, (data, target) in enumerate(train_loader, 1):
            data, target = data.to(device), target.to(device)
            optimizer.zero_grad()
            output = model(data)
            loss = F.nll_loss(output, target)
            loss.backward()
            if is_distributed and not use_cuda:
                # average gradients manually for multi-machine cpu case only
                _average_gradients(model)
            optimizer.step()
            if batch_idx % args.log_interval == 0:
                logger.info(
                    "Train Epoch: {} [{}/{} ({:.0f}%)] Loss: {:.6f}".format(
                        epoch,
                        batch_idx * len(data),
                        len(train_loader.sampler),
                        100.0 * batch_idx / len(train_loader),
                        loss.item(),
                    )
                )
        test(model, test_loader, device)
    save_model(model, args.model_dir)


def test(model, test_loader, device):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.nll_loss(output, target, size_average=False).item()  # sum up batch loss
            pred = output.max(1, keepdim=True)[1]  # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    logger.info(
        "Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n".format(
            test_loss, correct, len(test_loader.dataset), 100.0 * correct / len(test_loader.dataset)
        )
    )

# 当estimator.deploy时,需要显式定义出model_fn方法
def model_fn(model_dir):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = torch.nn.DataParallel(Net())
    with open(os.path.join(model_dir, "model.pth"), "rb") as f:
        model.load_state_dict(torch.load(f))
    return model.to(device)

# 部署函数的参数允许我们设置将用于端点的实例的数量和类型。这些值不需要与我们训练模型时设置的值相同。我们可以在一组基于 GPU 的实例上训练模型,然后在终端上部署基于CPU的模型实例;但这需要我们确保将模型返回或另存为 CPU 模型
# 因此,建议将模型返回或另存为 CPU 模型
def save_model(model, model_dir):
    logger.info("Saving the model.")
    path = os.path.join(model_dir, "model.pth")
    torch.save(model.cpu().state_dict(), path)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    # 模型训练参数
    parser.add_argument(
        "--batch-size",
        type=int,
        default=64,
        metavar="N",
        help="input batch size for training (default: 64)",
    )
    parser.add_argument(
        "--test-batch-size",
        type=int,
        default=1000,
        metavar="N",
        help="input batch size for testing (default: 1000)",
    )
    parser.add_argument(
        "--epochs",
        type=int,
        default=10,
        metavar="N",
        help="number of epochs to train (default: 10)",
    )
    parser.add_argument(
        "--lr", type=float, default=0.01, metavar="LR", help="learning rate (default: 0.01)"
    )
    parser.add_argument(
        "--momentum", type=float, default=0.5, metavar="M", help="SGD momentum (default: 0.5)"
    )
    parser.add_argument("--seed", type=int, default=1, metavar="S", help="random seed (default: 1)")
    parser.add_argument(
        "--log-interval",
        type=int,
        default=100,
        metavar="N",
        help="how many batches to wait before logging training status",
    )
    parser.add_argument(
        "--backend",
        type=str,
        default=None,
        help="backend for distributed training (tcp, gloo on cpu and gloo, nccl on gpu)",
    )

    # 与 SageMaker 相关的环境参数
    parser.add_argument("--hosts", type=list, default=json.loads(os.environ["SM_HOSTS"]))
    parser.add_argument("--current-host", type=str, default=os.environ["SM_CURRENT_HOST"])
    parser.add_argument("--model-dir", type=str, default=os.environ["SM_MODEL_DIR"])
    parser.add_argument("--data-dir", type=str, default=os.environ["SM_CHANNEL_TRAINING"])
    parser.add_argument("--num-gpus", type=int, default=os.environ["SM_NUM_GPUS"])

    train(parser.parse_args())
    

但这里,我们还需要通过访问环境变量获取部分有关训练环境的属性:

  • SM_HOSTS: 包含所有主机的 JSON 编码列表;在 Pytorch 中,该列表长度等于 WORLD_SIZE;

  • SM_CURRENT_HOST: 当前容器名称;在 Pytorch 中,该容器序号等于 RANK;

  • SM_MODEL_DIR: 模型的保存路径;该模型之后将上传至 S3;

  • SM_NUM_GOUS: 当前容器可用的 GPU 数;

: Pytorch 分布式训练时,dist.init_process_group(backend, rank, world_size),需要用到 WORLD_SIZE、RANK。

若在调用 PyTorch Estimator 的 fit() 方法时,使用了名为 training 的输入通道,则按照以下格式设置 SM_CHANNEL_[channel_name]:

  • SM_CHANNEL_TRAINING: 输入通道 training 中数据的存储路径;

该训练脚本从输入通道 training 的指定路径下加载数据,使用超参数配置训练,训练模型,并将模型保存至 model_dir,以便稍后托管。超参数作为参数传递给脚本,可以使用 argparse.ArgumentParser 实例进行检索。

在 SageMaker 中训练

from sagemaker.pytorch import PyTorch

estimator = PyTorch(
    entry_point="mnist.py",
    role=role,
    py_version="py38",
    framework_version="1.11.0",
    instance_count=2,
    instance_type="ml.c5.2xlarge",
    hyperparameters={"epochs": 1, "backend": "gloo"},
)

estimator.fit({"training": inputs})

sagemaker.pytorch.estimator.PyTorch 是 sagemaker 针对 Pytorch 开发的 Estimator,包含以下主要参数:

  • entry_point: 训练脚本的执行入口;

  • py_version、framework_version: python 及 pytorch 的版本;SageMaker 将分配满足该版本要求的计算资源;

  • instance_count、instance_type: 计算资源的数量、类型;

  • hyperparameters: 训练脚本的超参数;

  • image_uri: 若指定,Estimator 将使用此 Image 作为训练和部署的运行环境,而 py_version、framework_version 将失效;image_uri 必须是 ECR url 或 dockerhub image;

部署并测试该模型

mnist.py 中,model_fn 方法需要由我们显式定义出来;而 input_fn, predict_fn, output_fn 和 transform_fm 已经默认定义在 sagemaker-pytorch-containers 中。

### 部署该 Predictor
predictor = estimator.deploy(initial_instance_count=1, instance_type="ml.m4.xlarge")

### 生成测试数据
import gzip
import numpy as np
import random
import os

data_dir = "data/MNIST/raw"
with gzip.open(os.path.join(data_dir, "t10k-images-idx3-ubyte.gz"), "rb") as f:
    images = np.frombuffer(f.read(), np.uint8, offset=16).reshape(-1, 28, 28).astype(np.float32)

mask = random.sample(range(len(images)), 16)  # randomly select some of the test images
mask = np.array(mask, dtype=np.int)
data = images[mask]

### 测试该Predictor
response = predictor.predict(np.expand_dims(data, axis=1))
print("Raw prediction result:")
print(response)
print()

labeled_predictions = list(zip(range(10), response[0]))
print("Labeled predictions: ")
print(labeled_predictions)
print()

labeled_predictions.sort(key=lambda label_and_prob: 1.0 - label_and_prob[1])
print("Most likely answer: {}".format(labeled_predictions[0]))

### 删除部署端点并释放资源
sagemaker_session.delete_endpoint(endpoint_name=predictor.endpoint_name)

image.png

3. 使用 Amazon SageMaker Studio 可提高生产率,这是第一个用于机器学习的完全集成式开发环境 (IDE)

Amazon SageMaker Studio 提供了一个基于 Web 的单一视觉界面,您可以在其中执行所有 ML 开发步骤。对于构建、训练和部署模型所需的每个步骤,SageMaker Studio 为您提供了完整的访问、控制权和可见性。您可以快速上传数据、新建笔记本、训练和调试模型、在各个步骤之间来回移动,从而实现在一处调整实验、比较结果,并将模型部署到生产环境中,使您的生产效率大大提高。可以在统一的 SageMaker Studio 可视界面中执行所有的 ML 开发活动,包括笔记本、实验管理、自动模型创建,调试和模型偏差检测。

image.png

4. 使用 Amazon SageMaker 笔记本加速构建和协作

通过管理计算实例来查看、运行或共享笔记本非常繁琐。Amazon SageMaker 笔记本提供了一键式 Jupyter 笔记本,使您能够在几秒钟之内立即开始工作。底层的计算资源具有充分的弹性,因此您可以轻松地调高或调低可用资源,并且后台会自动进行更改,而不会中断您的工作。SageMaker 还支持一键分享笔记本。所有代码依赖项都是自动捕获的,因此您可以与他人轻松协作。他们会得到保存在同一位置的完全相同的笔记本。

5. 借助 Amazon SageMaker Autopilot 自动构建、训练和调试完全可视和可控的模型

Amazon SageMaker Autopilot 是业界首个自动机器学习工具,实现了 ML 模型的完全控制和可见性。典型的自动化机器学习方法无法让您深入了解用于创建模型的数据或模型创建背后的逻辑。因此,即使是普通的模型,也无法使之进化。另外,由于典型的自动化 ML 解决方案只能提供一个模型供选择,因此您无法灵活地权衡,例如牺牲一些准确性实现较低延迟的预测。

SageMaker Autopilot 会自动检查原始数据、应用特色处理器、选择最佳算法集、训练和调试多个模型、跟踪其性能,并在随后根据性能对模型进行排名,所有这些仅需几次单击。其结果是,部署性能最佳的模型所花费的时间只有该模型通常所需训练时间的几分之一。模型的创建方式以及内容对您完全可见,并且 SageMaker Autopilot 与 Amazon SageMaker Studio 相集成。在 SageMaker Studio 中,您可以了解多达 50 种由 SageMaker Autopilot 生成的不同模型,轻松地为您的用例选择最佳模型。没有机器学习经验的人可以使用 SageMaker Autopilot 轻松生成模型,而经验丰富的开发人员使用它可以快速开发出基础模型,供团队进一步迭代。

6. 使用 Amazon SageMaker Ground Truth 将数据标记成本降低多达 70%

成功的机器学习模型是建立在大量高质量训练数据的基础上的。但是,建立这些模型所需的训练数据的创建过程往往非常昂贵、复杂和耗时。Amazon SageMaker Ground Truth 可帮助您快速构建和管理高度准确的训练数据集。通过 Amazon Mechanical Turk,Ground Truth 提供了对标签机的便捷访问,并为它们提供了预构建工作流和接口,用于执行常见的标记任务。您还可以使用自己的标签机,或通过亚马逊云科技 Marketplace 使用 Amazon 推荐的供应商。此外,Ground Truth 还不断学习人类制作的标签,制作高质量的自动注释,显著降低标记成本。

7. Amazon SageMaker 支持领先的深度学习框架

支持的框架包括:TensorFlow、PyTorch、Apache MXNet、Chainer、Keras、glion、Horovod、Scikit-learn 和 Deep Graph Library。

使用体验

Amazon SageMaker 在模型部署方面表现卓越,通过多样的部署选项,包括云端和边缘计算,实现了极大的灵活性。一键式部署简化了流程,使用户能够迅速将模型推送到生产环境。支持多模型部署提高了资源利用率,而实时和离线推理选项满足了不同场景的需求。SageMaker Studio 作为集成开发环境提供便捷的模型管理工具,加强了整个部署过程的便捷性。这些特点使 SageMaker 在机器学习模型部署领域脱颖而出,为用户提供了全方位的灵活、简便、适应各种场景的解决方案。

总结

综合来看,Amazon SageMaker 在机器学习生命周期的各个阶段都提供了强大的功能和灵活性。其性能和便捷性得到了用户的一致好评。然而,一些用户也提到了成本方面的考虑,因此在选择时需要权衡各种因素。总体而言,Amazon SageMaker 在云端机器学习服务中占据着重要地位,为用户提供了一体化的解决方案,有助于简化和优化机器学习工作流程。

文章来源:智能部署之巅:Amazon SageMaker 引领机器学习革新

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

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

相关文章

vue element plus Upload 上传

通过点击或者拖拽上传文件。 基础用法# 通过 slot 你可以传入自定义的上传按钮类型和文字提示。 可通过设置 limit 和 on-exceed 来限制上传文件的个数和定义超出限制时的行为。 可通过设置 before-remove 来阻止文件移除操作。 Click to upload jpg/png files with a size …

python--宣传篇--personal-qrcode个性二维码

文章目录 准备代码效果 准备 代码 from MyQR import myqr import osdef get_img_qrcode(words, save_name, picture, colorizedTrue):if save_name[-3:] in ["jpg", "png", "gif"]:if picture[-3:] in ["png", "jpg", &qu…

我们是如何测试人工智能产品的

在当今数字化时代,人工智能(AI)技术已经成为我们生活中不可或缺的一部分。然而,要构建出可信赖的AI系统并非易事。这需要我们不仅深入理解人工智能的核心原理,还需要将这些理论知识应用到实际场景中。 为了帮助大家系…

CSS3笔记

1.相同优先级的样式以写在后面的为主。 2.交集选择器,并且 条件挨在一起 p.rich{...} /*p元素class有rich的元素*/ 3.并集选择器,或者 逗号隔开 .class1,class2{...}/*满足其中一个类名都会使用该样式*/ 4.后代选择器 空格 隔开 所有符合的包括孙子及…

动态规划:Leetcode 91.解码方法

题目 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : A -> "1" B -> "2" ... Z -> "26" 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法&am…

一个系列很多样式的wordpress外贸建站模板

菌菇干货wordpress跨境电商模板 食用菌、羊肚菌、牛肝菌、香菇、干黄花菜、梅干菜、松茸wordpress跨境电商模板。 https://www.jianzhanpress.com/?p3946 餐饮调味wordpress跨境电商模板 豆制品、蛋黄糖、烘焙、咖啡、调料、调味酱、餐饮调味wordpress跨境电商模板。 http…

vue3的基本使用(1)

Vue3的基本使用(1) 初识vue31. vue3简介2. 性能提升3. 源码升级 Vue3的创建1. vue-cli创建2. vite创建 Composition API的区别(组合式)setup函数响应式数据1. ref响应式2. reactive响应式 toRefs与toRef简单介绍 初识vue3 1. vue…

零售EDI:劳氏 Lowe‘s EDI项目案例

通过 EDI,企业与Lowes之间可以直接交换各种商业文档,如订单、发票、收据等,从而实现信息的实时交换,提高了供应链的效率和准确性。在现代供应链管理中,EDI 已经成为了不可或缺的重要工具。 作为一家拥有多条业务线的企…

笔记76:32位/64位操作系统的区别

64位系统和32位系统的区别: 操作系统只是硬件和应用软件中间的一个平台32位操作系统针对的32位的CPU设计64位操作系统针对的64位的CPU设计我们的CPU从原来的8位,16位,到现在的32位和64位;CPU处理计算的时候“数据”和“指令”是不同对待的 &…

HQYJ 3-7 作业

用两个信号量实现线程同步 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include <semap…

Spring Boot异常处理和单元测试

1.SpringBoot异常处理 1.1.自定义错误页面 SpringBoot默认的处理异常的机制&#xff1a;SpringBoot 默认的已经提供了一套处理异常的机制。一旦程序中出现了异常 SpringBoot 会向/error 的 url 发送请求。在 springBoot 中提供了一个叫 BasicErrorController 来处理/error 请…

大唐国际务实迎战两会保电,智能巡检机器人助力电力保障

全国两会召开在即。近年来&#xff0c;我国两会期间电力供应稳定性备受关注。作为国家重要的政治盛会&#xff0c;两会的顺利召开需要可靠的电力保障&#xff0c;以确保会议期间各项活动的正常进行。大唐国际作为国内领先的电力企业&#xff0c;面临着如何保障两会期间电力供应…

【b站咸虾米】ES6 Promise的用法,ES7 async/await异步处理同步化,异步处理进化史

课程地址&#xff1a;【ES6 Promise的用法&#xff0c;ES7 async/await异步处理同步化&#xff0c;异步处理进化史】 https://www.bilibili.com/video/BV1XW4y1v7Md/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 图文地址&#xff1a;https://www.b…

AI产品摄影丨香水

AI电商产品拍摄丨&#xff08;可指定产品&#xff09; 均为概念图 可换产品 可指定产品&#xff0c;可换logo 工具&#xff1a;StartAI 搭配“手机摄影”风格使用效果更佳哦 咒语&#xff1a;anha perfume in bottle on stone surface, in the style of everyday american…

RPC——远程过程调用

一、RPC介绍 1.1 概述 RPC&#xff08;Remote Procedure Call Protocol&#xff09; 远程过程调用协议。RPC是一种通过网络从远程计算机程序上请求服务&#xff0c;不需要了解底层网络技术的协议。RPC主要作用就是不同的服务间方法调用就像本地调用一样便捷。 1.2 RPC框架 …

Grafana dashboards as ConfigMaps

文章目录 1. 简介2. 创建 configmaps3. grafana 界面查看 1. 简介 将 Grafana 仪表板存储为 Kubernetes ConfigMap 相比传统的通过 Grafana 界面导入仪表板有以下一些主要优点: 版本控制&#xff1a; ConfigMap 可以存储在版本控制系统(如Git)中,便于跟踪和管理仪表板的变更历…

Docker:部署微服务集群

1. 部署微服务集群 实现思路&#xff1a; ① 查看课前资料提供的cloud-demo文件夹&#xff0c;里面已经编写好了docker-compose文件 ② 修改自己的cloud-demo项目&#xff0c;将数据库、nacos地址都命名为docker-compose中的服务名 ③ 使用maven打包工具&#xff0c;将项目…

集合拆分Lists.partition的使用

集合拆分Lists.partition的使用 集合拆分Lists.partition的使用 需要的包 import com.google.common.collect.Lists;引入maven依赖 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>21.0</…

动态规划(算法竞赛、蓝桥杯)--数位DP数字游戏

1、B站视频链接&#xff1a;E36 数位DP 数字游戏_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; const int N12; int a[N];//把整数的每一位数字抠出来&#xff0c;存入数组 int f[N][N];//f[i][j]表示一共有i位&#xff0c;且最高位数字是j的不降数…

类和对象 02【C++】

文章目录 一、 构造函数(初始化列表)1. 初始化列表2. explicit 关键字3. static成员 二、 友元1. 友元函数2.友元类 三、 内部函数四、 匿名对象五、 拷贝对象时的一些编译器优化 一、 构造函数(初始化列表) 进一步理解构造函数&#xff0c;我们知道创建对象时&#xff0c;编译…