深度神经网络中的混合精度训练

Mixed-Precision Training of Deep Neural Networks | NVIDIA Technical Blog

目录

混合精度成功训练的技术

FP32 累加

损失缩放 loss scaling

FP32 Master Copy of Weights

混合精度训练迭代过程

AMP混合精度训练介绍

FP16和FP32的区别

FP16的优势

FP16的问题

解决P16的精度问题策略

1、混合精度计算

2、损失缩放(Loss scaling)

3、权重备份

Apex

代码格式

不同的训练模式说明

O0 纯FP32

O1 混合精度

O2混合精度

O3纯FP16

torch.cuda.amp

参考连接


深度神经网络 (DNN) 在许多领域取得了突破,包括图像处理和理解、语言建模、语言翻译、语音处理、游戏等。为了实现这些结果,DNN 的复杂性一直在增加,这反过来又增加了训练这些网络所需的计算资源。混合精度训练通过使用较低精度的计算(FP16)来降低所需的资源,这具有以下优点。

  • 减少所需的内存量。半精度浮点格式 (FP16) 使用 16 位,而单精度 (FP32) 使用 32 位。降低所需的内存可以训练更大的模型或使用更大的小批量进行训练。
  • 缩短训练或推理时间。执行时间可能对内存或算术带宽敏感。半精度将访问的字节数减半,从而减少了在内存受限层中花费的时间。与单精度相比,NVIDIA GPU 的半精度算术吞吐量提高了 8 倍,从而加快了数学受限层的速度。

图 1.bigLSTM 英语语言模型的训练曲线显示了本文中描述的混合精度训练技术的好处。Y 轴是训练损失。不带损耗缩放的混合精度(灰色)在一段时间后会发散,而带损耗缩放的混合精度(绿色)与单精度模型(黑色)匹配。

图 1.大型LSTM 英语语言模型的训练曲线显示了本文中描述的混合精度训练技术的好处。Y 轴是训练损失。不带损耗缩放的混合精度(灰色)在一段时间后会发散,而带损耗缩放的混合精度(绿色)与单精度模型(黑色)匹配。

由于 DNN 训练传统上依赖于 IEEE 单精度格式,因此本文的重点是半精度训练,同时保持单精度实现的网络精度(如图 1 所示)。这种技术称为混合精度训练,因为它同时使用单精度和半精度表示。

混合精度成功训练的技术

半精度浮点格式由 1 个符号位、5 个指数位和 10 个小数位组成。支持的指数值属于 [-24, 15] 范围,这意味着该格式支持 [2-24,65,504]范围。由于这比 [2-149, ~3.4×1038] 范围支持单精度格式,训练某些网络需要额外考虑。本节介绍了成功训练半精度 DNN 的三种技术:将 FP16 产品累积到 FP32 中;损失缩放;以及砝码的 FP32 主副本。借助这些技术, NVIDIA 和百度研究院能够匹配所有经过训练的网络的单精度结果准确性(混合精度训练)。请注意,并非所有网络都需要使用所有这些技术进行训练。

有关如何在各种框架中应用这些技术的详细说明,包括可用的代码示例,请参阅混合精度训练用户指南。

FP32 累加

NVIDIA Volta GPU 架构引入了 Tensor Core 指令,该指令将半精度矩阵相乘,将结果累积为单精度或半精度输出。我们发现,累积到单个精度对于获得良好的训练结果至关重要。累积值在写入内存之前转换为半精度。cuDNN 和 CUBLAS 库提供了多种依赖于 Tensor Core 进行算术运算的函数。

损失缩放 loss scaling

训练 DNN 时会遇到四种类型的张量:激活、激活梯度、权重和权重梯度。根据我们的经验,激活、权重和权重梯度落在半精度表示的值大小范围内。然而,对于某些网络,小幅度激活梯度低于半精度范围。例如,考虑图 2 中训练 Multibox SSD 检测网络时遇到的激活梯度直方图,该直方图显示了 log2 刻度上值的百分比。小于 2-24 的值在半精度格式中变为零。

请注意,激活梯度不使用大多数半精度范围,激活梯度往往是幅度小于 1 的小值。因此,我们可以通过将激活梯度乘以比例因子 S 来将它们“移位”到 FP16 表示的范围内。在SSD网络的情况下,将梯度乘以8就足够了。这表明激活梯度值小于 2-27与该网络的训练无关,而保留 [2-27, 2-24) 范围。

图2.以单精度训练 Multibox SSD 检测器网络时记录的激活梯度直方图。Y 轴是对数刻度上所有值的百分比。X 轴是绝对值的对数刻度,也是零的特殊条目。例如,在此训练课程中,66.8% 的值为零,而 4% 的值介于 2-32 和 2-30 之间。

图2.以单精度训练 Multibox SSD 检测器网络时记录的激活梯度直方图。Y 轴是对数刻度上所有值的百分比。X 轴是绝对值的对数刻度,也是零的特殊条目。例如,在此培训课程中,66.8% 的值为零,而 4% 的值介于 2 之间-32和 2-30.

确保梯度落入半精度表示的范围内的一种非常有效的方法是将训练损失乘以比例因子。这仅增加了一次乘法,并且通过链式规则,它确保所有梯度都按比例放大(或向上移动),而无需额外费用。损失缩放可确保恢复丢失到零的相关梯度值。在权重更新之前,权重梯度需要按相同的因子 S 缩小。缩减操作可以与权重更新本身融合(导致没有额外的内存访问)或单独执行。有关详细信息,请参阅《混合精度训练用户指南》和《混合精度训练》白皮书。

FP32 Master Copy of Weights

DNN 训练的每次迭代都会通过添加相应的权重梯度来更新网络权重。权重梯度幅度通常明显小于相应的权重,尤其是在与学习率相乘(或Adam或Adagrad等优化器的自适应计算因子)相乘之后。如果其中一个加法太小而无法产生半精度表示差异,则此幅度差异可能导致不会发生更新(例如,由于指数差大,较小的加法在移位以对齐二进制点后变为零)。

对于以这种方式丢失更新的网络,一个简单的补救措施是以单精度维护和更新权重的主副本。在每次迭代中,都会制作一个主权重的半精度副本,并将其用于正向和反向传播,从而获得性能优势。在权重更新期间,计算出的权重梯度将转换为单精度,并用于更新主副本,并在下一次迭代中重复该过程。因此,我们只在需要的地方将半精度存储与单精度存储混合使用。

混合精度训练迭代过程

上面介绍的三种技术可以组合到每个训练迭代的以下步骤序列中。对传统迭代过程的补充以粗体显示。

  1. 制作权重的 FP16 副本
  2. 使用 FP16 权重和激活进行前向传播
  3. 将产生的损失乘以比例因子 S
  4. 使用 FP16 权重、激活及其梯度向后传播
  5. 将权重梯度乘以 1/S
  6. (可选)处理权重梯度(梯度裁剪、权重衰减等)
  7. 更新 FP32 中权重的主副本

AMP混合精度训练介绍

AMP(Automatic mixed precision):自动混合精度,该方法在训练网络时将单精度(FP32)与半精度(FP16)结合在一起,它使用FP16即半精度浮点数存储和计算,从而实现节省显存和加快训练速度的目的。

常用的两种实现amp的方式:

  • NVIDIA Apex使用apex.amp (O1模式开启自研kernel会出现Nan值,禁用自研kernel之后没有Nan值;O2没有Nan值)
  • Pytorch 1.6版本后自带torch.cuda.amp (开启自研kernel 也会出现Nan值;禁用自研kernel之后没有Nan值)

FP16和FP32的区别

FP16和FP32在计算机的不同存储方法:

半精度浮点数 (FP16): 计算机使用 2 字节 (16 位) 存储,表示范围为 [5.9e-8,65504]

单精度浮点数 (FP32) :计算机使用 4 字节 (32 位) 存储,表示范围为 [1.4e-45,3.4e38],FP32 能够表示的范围要比 FP16 大的多得多。

FP16的优势

默认情况下,大多数深度学习框架都采用FP32进行训练。相比与FP32,FP16具有一下优势:

  •  1.减少显存占用,这使得我们可以用更大的 batch size
  •  2.加快训练和推断的计算
  •  3.NVIDIA Tensor Core支持

FP16的问题

如果我们简单地把模型权重和输入从 FP32 转化成 FP16,虽然可以加快速度,但是模型的精度会被严重影响,原因如下:

  • 1. 溢出错误:由于FP16的动态范围比FP32位的狭窄很多,因此,在计算过程中很容易出现上溢出和下溢出,溢出之后就会出现"NaN"的问题。

    Multibox SSD网络训练过程中激活梯度值的直方图。2%的值在[2−34,2−32)范围内,2%的数值在[2–24,2−23)范围内,以及67%的数值为零。

  • 2. 舍入误差:当梯度过小时,小于当前区间内的最小间隔时,该次梯度更新可能会失败

解决P16的精度问题策略

论文里提到下面三个策略:Micikevicius, Paulius, Sharan Narang, et al. “Mixed Precision Training.” ArXiv:1710.03740 [Cs, Stat], February 15, 2018. https://arxiv.org/abs/1710.03740

1、混合精度计算

 在内存中用FP16做储存和乘法从而加速计算,而用FP32做累加避免舍入误差。混合精度训练的策略有效地缓解了舍入误差的问题。

2、损失缩放(Loss scaling)

为了解决下溢出的问题,对计算出来的 loss 值进行缩放 (scale),由于链式法则的存在,对 loss 的缩放会作用在每个梯度上,这些梯度会平移到 FP16 的有效范围内。这样就可以用 FP16 存储梯度而又不会溢出了。此外,在进行更新之前,需要先将缩放后的梯度转化为 FP32,再将梯度反缩放(unscale)回去。 

反向传播前:将loss手动增大缩放因子 (loss_scale)倍

反向传播后:将权重梯度缩小缩放因子 (loss_scale)倍,恢复正常值

3、权重备份

将模型权重、激活值、梯度等数据用 FP16 来存储,同时维护一份 FP32 的模型权重副本(master-weight)用于更新。前向使用FP16,在反向传播得到 FP16 的梯度以后,将其转化成 FP32 并 unscale,最后更新 FP32 的模型权重。

尽管与单精度训练相比,保持额外的权重副本会使权重的内存需求增加50%,但对整体内存使用的影响要小得多。对于训练来说,由于更大的batch size和每层的输出值被保存以在反向传播过程中重复使用,因此内存消耗主要由这些输出值决定。由于输出值也以半精度格式存储,因此训练深度神经网络的总内存消耗大致减半。

Apex

代码格式

APEX中,用户不需要手动将模型或数据类型转换为.half(),只需要从现有的默认 (FP32) 脚本开始,添加与 Amp API 对应的三行,然后就可以使用混合精度进行训练。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

from apex.fp16_utils import *

from apex import amp,optimizers

model = ...

optimizer  = ...

model, optimizer = amp.initialize(model, optimizer,

                      opt_level= ,

                 keep_batchnorm_fp32= ,

                 loss_scale=,

                 )

with amp.scale_loss(loss, optimizer) as scaled_loss:

    scaled_loss.backward()

optimizer.step()

optimizer.zero_grad()

不同的训练模式说明

NVIDIA的APEX混合精度库为例,里面提供了多种策略,可以根据不同的场景进行使用:

  • opt_level:O0(纯FP32),O1和O2是混合精度的不同实现,O3(纯FP16),还有O4,O5使用BFLOAT16

  • cast_model_type:将模型的参数转换为所需的类型。

  • patch_torch_functions:patch所有 Torch 函数和 Tensor 方法以执行 Tensor Core 友好的操作,例如 FP16 中的 GEMM 和卷积,以及任何受益于 FP32 中的 FP32 精度的操作。

  • keep_batchnorm_fp32:将 batchnorm 权重保持在 FP32 ,模型的其余部分是 FP16。

  • master_weights:保持 FP32 权重。

  • loss_scale:float值 or "dynamic"(自适应调整损失比例)。动态损失放大(dynamic loss scaling),为了充分利用FP16的范围,缓解舍入误差,将loss*loss_scale。如果产生上溢出,则跳出参数更新,缩小放大倍数使其不溢出。在2000步后再尝试使用大的scale来充分利用FP16的范围。

蓝色为默认值

opt_level

O0

O1

O2

O3

cast_model_typetorch.float32Nonetorch.float16torch.float16
patch_torch_functionsFalseTrueFalseFalse

patch_torch_functions_type

None

torch.float16

NoneNone
keep_batchnorm_fp32NoneNone(自动设为TRUE)TrueFalse
master_weightsFalseNoneTrueFalse
loss_scale

1.0

"dynamic"

"dynamic"

1.0

O0 纯FP32

纯FP32训练,可作为accuracy的baseline

O1 混合精度

  • 权重为FP32
  • 前向:对Pytorch func和Tensor method进行自动转换,根据黑白名单自动决定使用FP16(GEMM,卷积),还是FP32(softmax)进行计算。
  • 白名单函数强制 FP16(将conv的input,weight,bias转换为FP16类型),黑名单函数使用FP32。
  • 其余函数则根据参数类型自动判断,如果参数都是 FP16,则以 FP16 运行,如果有一个参数为 FP32,则以 FP32 运行。 对于那些在 FP16 环境中运行不稳定的模块,我们会将其添加到黑名单中,强制它在 FP32 的精度下运行。
  • 动态损失缩放(dynamic loss scaling)

O1步骤:

  1. 根据黑白名单对 PyTorch 内置的函数和一些tensor进行包装 
  2.  将 loss_scale 初始化为一个很大的值 
  3.  对于每次迭代 
  • (a). 前向传播:按照黑白名单自动选择数据类型进行计算。 白名单:拷贝FP32模型并且转换成 FP16 进行计算
  • (b). 将 loss 乘以 loss_scale
  • (c). 反向传播: 计算出梯度FP16
  • (d). 将梯度 unscale ,即除以 loss_scale 
  • (e). 每次更新前检查溢出问题(检查梯度中有没有infnan),如果检测到 inf 或 nan

                      i. loss_scale /= 2

                      ii. 跳过此次更新

  • (f). optimizer.step(),利用 FP16 的梯度更新 FP32 的模型参数

  • (g). 如果连续2000次迭代都没有出现 inf 或 nan,则 loss_scale *= 2

以 nn.Linear 为例, 这个模块有两个权重参数 weight 和 bias,输入为 input,前向传播就是调用了 torch.nn.functional.linear(input, weight, bias),对于白名单来说,就是把权重参数 weight 和 bias和input转换为 FP16再进行计算。

黑白名单

lists里有三个文件:functional_overrides.py,tensor_overrides.py,torch_overrides.py分别定义了黑白名单(FP16/FP32的适用情况)

O2混合精度

  • 除了 BN 层以外的模型权重为 FP16
  • 创建一个FP32的权重副本

O2步骤:

  1. 除了 BN 层以外的模型权重和梯度转化为 FP16输入类型也转化为 FP16,模型输出类型不设置为FP32
  2. 维护一个 FP32 的模型权重副本用于更新
  3. 将 loss_scale 初始化为一个很大的值;
  4. 对于每次迭代
  • (a). 前向传播: 除了 BN 层权重是 FP32,模型其它部分都是 FP16,得到FP32的loss
  • (b). 将 loss 乘以 loss_scale转换为FP16
  • (c). loss反向传播,计算得到 FP16 的梯度 
  • (d). 将 FP16 梯度转化为 FP32,并unscale
  • (e). 如果检测到梯度 inf 或 nan

                  i. loss_scale /= 2

                  ii. 跳过此次更新

  • (f). optimizer.step(),利用 FP16 的梯度更新 FP32 的模型参数
  • sgd中params[i].add_(grads[i], alpha=-lr)FP32+FP16=FP32

  • (g). 如果连续2000次迭代都没有出现 inf 或 nan,则 loss_scale *= 2

O3纯FP16

纯FP16来当速度的baseline

torch.cuda.amp

PyTorch 从 1.6 以后开始支持amp,即torch.cuda.amp module,类似于apex的O1模式:
torch.cuda.amp 给用户提供了较为方便的混合精度训练机制,“方便”体现在两个方面:

1.amp 会自动为算子选择合适的数值精度(FP32、FP16)

该名单在 torch\testing\_internal\autocast_test_lists.py里定义

  • CUDA Ops that can autocast to float16
    __matmul__, addbmm, addmm, addmv, addr, baddbmm, bmm, chain_matmul, multi_dot, conv1d, conv2d, conv3d, conv_transpose1d, conv_transpose2d, conv_transpose3d, GRUCell, linear, LSTMCell, matmul, mm, mv, prelu, RNNCell
  • CUDA Ops that can autocast to float32
    __pow__, __rdiv__, __rpow__, __rtruediv__, acos, asin, binary_cross_entropy_with_logits, cosh, cosine_embedding_loss, cdist, cosine_similarity, cross_entropy, cumprod, cumsum, dist, erfinv, exp, expm1, group_norm, hinge_embedding_loss, kl_div, l1_loss, layer_norm, log, log_softmax, log10, log1p, log2, margin_ranking_loss, mse_loss, multilabel_margin_loss, multi_margin_loss, nll_loss, norm, normalize, pdist, poisson_nll_loss, pow, prod, reciprocal, rsqrt, sinh, smooth_l1_loss, soft_margin_loss, softmax, softmin, softplus, sum, renorm, tan, triplet_margin_loss
  • CUDA Ops that promote to the widest input type
    这些操作不需要特定的 dtype 来保持稳定性,但需要多个输入并要求输入的 dtype 匹配。如果所有输入都是 float16,则运算在 中运行float16。如果任何输入是float32,autocast 将所有输入转换为float32并运行 op in float32。

    addcdiv addcmul_ atan2_ bilinear_ cross_ dot_ grid_sample_ index_put_ scatter_add_ tensordot

2.amp 提供了loss_scaling 操作 ,为了防止下溢,将loss乘以一个比例因子,并对缩放后的loss反向传播,然后将梯度除以相同的比例因子

代码格式:

model = Net().cuda()

optimizer = optim.SGD(model.parameters(), ...)

scaler = torch.cuda.amp.GradScaler()

for epoch in epochs:

    for input, target in data:

        optimizer.zero_grad()

        with autocast():

            output = model(input)

            loss = loss_fn(output, target)

        # 用scaler,scale loss(FP16),backward得到scaled的梯度(FP16)

        scaler.scale(loss).backward()

        # scaler 更新参数,会先自动unscale梯度,如果有nan或inf,自动跳过

        scaler.step(optimizer)

        scaler.update() # scaler factor更新

参考连接

apex官方代码:GitHub - NVIDIA/apex: A PyTorch Extension: Tools for easy mixed precision and distributed training in Pytorch

nvidia apex官方文档: Apex (A PyTorch Extension) — Apex 0.1.0 documentation

自动混合精度简介 - 墨天轮

全网最全-混合精度训练原理 - 知乎

由浅入深的混合精度训练教程

Micikevicius, Paulius, Sharan Narang, et al. “Mixed Precision Training.” ArXiv:1710.03740 [Cs, Stat], February 15, 2018. https://arxiv.org/abs/1710.03740

torch.amp文档  https://pytorch.org/docs/stable/amp.html#

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

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

相关文章

多线程基础入门【Linux之旅】——上篇【线程控制,线程互斥,线程安全】

目录 前文 回望页表 一,什么是线程 二,使用 pthread_create (线程创建) 三,线程控制 1 ,线程共享进程数据,但也拥有自己的一部分数据: 2, 线程 VS 进程优点 3,…

DVenom:一款功能强大的Shellcode加密封装和加载工具

关于DVenom DVenom是一款功能强大的Shellcode加密封装和加载工具,该工具专为红队研究人员设计,可以帮助红队成员通过对Shellcode执行加密封装和加载实现反病毒产品的安全检测绕过。 功能介绍 1、支持绕过某些热门反病毒产品; 2、提供了多种…

【React系列】react-router

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. 认识react-router 1.2. 前端路由原理 前端路由是如何做到URL和内容进行映射呢?监听URL的改变。 UR…

自动生成表结构screw

采用的组件 screw 操作流程&#xff1a; 1、新建springboot 项目 2、引入相关的依赖 <!-- screw核心 --><dependency><groupId>cn.smallbun.screw</groupId><artifactId>screw-core</artifactId><version>1.0.4</version><…

超简单|配图详细| 双系统Ubuntu22.04 系统磁盘扩容

文章目录 1. 打开磁盘工具2. 格式化空闲分区3. 挂载该分区4. 数据迁移与备份5. 卸载原分区6. 挂载新的/home分区7. 重启系统8. 删除原来的数据8.1 直接格式化分区8.2 没有单独的/home分区1. 查看设备名2. 重新挂载该分区3. 删除原始分区/home目录中的内容4. 卸载原分区5. 重启 …

关于无人机上层控制的PID算法的思考

一、前言 背景介绍&#xff1a;PID虽然出现了很多年&#xff0c;但是目前工业界还是把PID作为主流的控制算法&#xff08;尽管学术界有很多非常时尚的控制算法&#xff0c;包括鲁邦控制&#xff0c;神经网络控制等等&#xff09;&#xff0c;PID的算法在于其不需要对系统进行复…

使用 PHP-FFMpeg 操作视频/音频文件

做音频合成的时候找到的一个php操作ffmpeg 的类库。GitHub地址&#xff1a;https://github.com/PHP-FFMpeg/PHP-FFMpeg/。本文的例子大部分都是上面的 在使用之前请安装好 FFMpeg 。如何安装&#xff1f;请看 FFmpeg 安装教程。 使用composer快速安装 > composer require …

【Spring实战】20 Spring Data REST+JPA构建基础的RESTful API

文章目录 1. 基础概念1&#xff09;Spring Data REST2&#xff09;JPA&#xff08;Java Persistence API&#xff09; 2. 添加依赖3. 创建JPA实体4. 创建JPA Repository5. 启用Spring Data REST6. 启动服务7. 测试8. 总结 Spring Data REST 是 Spring Framework 生态系统中的一…

加密算法和身份认证

前瞻概念 在了解加密和解密的过程前&#xff0c;我们先了解一些基础概念 明文&#xff1a;加密前的消息叫 “明文” &#xff08;plain text&#xff09;密文: 加密后的文本叫 “密文” (cipher text)密钥: 只有掌握特殊“钥匙”的人&#xff0c;才能对加密的文本进行解密,这里…

【深度学习:(Contrastive Learning) 对比学习】深入浅出讲解对比学习

对比学习允许模型从未标记的数据中提取有意义的表示。通过利用相似性和不相似性&#xff0c;对比学习使模型能够在潜在空间中将相似的实例紧密地映射在一起&#xff0c;同时将那些不同的实例分开。这种方法已被证明在计算机视觉、自然语言处理 &#xff08;NLP&#xff09; 和强…

STM32和ESP8266的WiFi模块控制与数据传输

基于STM32和ESP8266 WiFi模块的控制与数据传输是一种常见的嵌入式系统应用。在这种应用中&#xff0c;STM32作为主控制器负责控制和与外部传感器交互&#xff0c;而ESP8266 WiFi模块则用于实现无线通信和数据传输。本文将介绍如何在STM32上控制ESP8266模块&#xff0c;建立WiFi…

3D 纹理的综合指南

在线工具推荐&#xff1a;3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 我们经常看到超现实主义的视频游戏和动画电影角色出现在屏幕上。他们皮肤上的…

【Redux】自己动手实现redux和react-redux

1. React提供context的作用 在class组件的世界里&#xff0c;如果后代组件共享某些状态&#xff0c;比如主题色、语言键&#xff0c;则需要将这些状态提升到根组件&#xff0c;以props的方式从根组件向后代组件一层一层传递&#xff0c;这样则需要在每层写props.someData&#…

Java Arrays.copyOfRange的用法

Arrays.copyOfRange的使用方法&#xff1a; 将一个数组拷贝至另一个数组中 参数&#xff1a; original&#xff1a;第一个参数为要拷贝的数组对象 from&#xff1a;第二个参数为拷贝的开始位置&#xff08;包含&#xff09; to&#xff1a;第三个参数为拷贝的结束位置&#x…

django websocket

目录 核心代码 consumers.py from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer import datetime import time from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer):def websocket_conne…

ssm基于vue框架和elementui组件的手机官网论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本手机官网就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&#x…

Linux第3步_安装Ubuntu操作系统

创建好虚拟机后&#xff0c;就可以安装Ubuntu操作系统了。 1、双击“VMware Workstation Pro”&#xff0c;得到下面的界面。 2、点击“编辑虚拟机设置”&#xff0c;见下图&#xff1a; 3、等几秒钟&#xff0c;得到下面的界面&#xff1a; 4、点击“CD/DVD”&#xff0c;得到…

vscode无识别已有的maven java项目(visual studio code not recognizing java project)

文章目录 事情经过尝试疑惑问题解决结论 事情经过 未安装任何Java Extension Pack使用 Maven 的 archetype:generate 命令来创建一个新的项目使用vscode打开了该目录然后安装Java Extension Pack等java插件配置了vscode settings.json中的 java.configuration.runtimes和 java…

Python-1-字符串类型及方法

众所周知&#xff0c;Python面向对象&#xff0c;功能强大 | ू•ૅω•́)ᵎᵎᵎ

mybatis-flex与springBoot整合

mybatis-flex基本使用 1.测试sql2.导入坐标3.框架搭建1.pojo层2.mapper层3.service层4.controller层5.启动类6.配置类7.EmpMapper.xml 4.启动测试 本片文章在springBoot3&#xff0c;jdk21下测试通过 注意官方网站为&#xff1a;https://mybatis-flex.com/ 请点击&#xff1a;直…