昇思25天学习打卡营第4天|扩散模型

文章目录

      • 昇思MindSpore应用实践
        • 基于MindSpore的Diffusion扩散模型
          • 1、Diffusion Models 简介
          • 2、构建 Diffusion Model 的准备工作
          • 3、Attention 机制
          • 4、条件 U-Net
          • 5、Diffusion 正向过程
          • 6、Diffusion 反向过程
          • 7、Diffusion 模型训练
      • Reference

昇思MindSpore应用实践

本系列文章主要用于记录昇思25天学习打卡营的学习心得。

基于MindSpore的Diffusion扩散模型
1、Diffusion Models 简介

关于生成式模型,主要有GANs,VAEs,Flow Models,Diffusion Models,其中最常用的模型主要是生成对抗网络(GANs)和扩散模型(Diffusion Models),两种方法都能从潜在空间的随机噪声中采用并以对抗学习或反向扩散的方式,生成符合真实数据分布的结果,从而应用在各种生成式任务,如:图生图(风格转换)、文生图、语音转换、图像修复等。

在这里插入图片描述

采用GANs的生成方式训练和推理速度相比Diffusion通常较快,但容易产生模式崩塌。重复生成相同结果等问题。而扩散模型的训练更加稳定,且正向加噪过程和反向复原过程都可以通过严格的条件概率和贝叶斯公式推导证明,更具可解释性,缺点是由于需要多步的噪声去除过程,以计算出最符合原始数据分布的像素值从而生成高质量真实类别的图片,速度相比GANs慢。

2、构建 Diffusion Model 的准备工作

以 DDPM 生成图像为例,其前向过程就是给真实图片逐步添加噪声的过程,这个过程是固定的(Frozen),逐渐将高斯噪声添加到图像中,直到最终得到纯噪声。

扩散模型的正向与反向过程示意图:
在这里插入图片描述

实验部分,首先导入必要的mindspore接口:

import math
from functools import partial
%matplotlib inline
import matplotlib.pyplot as plt
from tqdm.auto import tqdm
import numpy as np
from multiprocessing import cpu_count
from download import downloadimport mindspore as ms
import mindspore.nn as nn
import mindspore.ops as ops
from mindspore import Tensor, Parameter
from mindspore import dtype as mstype
from mindspore.dataset.vision import Resize, Inter, CenterCrop, ToTensor, RandomHorizontalFlip, ToPIL
from mindspore.common.initializer import initializer
from mindspore.amp import DynamicLossScalerms.set_seed(0)def linear_beta_schedule(timesteps):  # 正向扩散时间步设置beta_start = 0.0001beta_end = 0.02return np.linspace(beta_start, beta_end, timesteps).astype(np.float32)

mindspore官方文档中给出了构建一款Diffusion模型的帮助函数和类,用于神经网络的实现:

def rearrange(head, inputs):  # 调整张量分布b, hc, x, y = inputs.shape  # 输入形状c = hc // head            # 显然是用到了多头注意力机制,将特征维度分割为多个头处理return inputs.reshape((b, head, c, x * y))def rsqrt(x):  # 计算逆平方根res = ops.sqrt(x)return ops.inv(res)def randn_like(x, dtype=None):  # 生成与输入张量x形状一致的随机张量if dtype is None:dtype = x.dtyperes = ops.standard_normal(x.shape).astype(dtype)return resdef randn(shape, dtype=None):  # 生成指定形状的随机张量if dtype is None:dtype = ms.float32res = ops.standard_normal(shape).astype(dtype)return resdef randint(low, high, size, dtype=ms.int32):  # 生成一个指定形状的整数随机数张量res = ops.uniform(size, Tensor(low, dtype), Tensor(high, dtype), dtype=dtype)return resdef exists(x):  # 判断某个变量是否被赋值return x is not Nonedef default(val, d):       # 用于设置默认值if exists(val):return valreturn d() if callable(d) else ddef _check_dtype(d1, d2):  # 检验数据类型是否兼容if ms.float32 in (d1, d2):return ms.float32if d1 == d2:return d1raise ValueError('dtype is not supported.')class Residual(nn.Cell):  # 定义残差模块,源自何神CVPR2015的神来之笔def __init__(self, fn):super().__init__()self.fn = fndef construct(self, x, *args, **kwargs):return self.fn(x, *args, **kwargs) + xdef Upsample(dim):  # 上采样return nn.Conv2dTranspose(dim, dim, 4, 2, pad_mode="pad", padding=1)def Downsample(dim):  # 下采样return nn.Conv2d(dim, dim, 4, 2, pad_mode="pad", padding=1)# 位置向量,源自Transformer中的位置编码,让网络知道它在哪个特定时间步长(噪声水平)上运行
class SinusoidalPositionEmbeddings(nn.Cell):def __init__(self, dim):super().__init__()self.dim = dimhalf_dim = self.dim // 2emb = math.log(10000) / (half_dim - 1)emb = np.exp(np.arange(half_dim) * - emb)self.emb = Tensor(emb, ms.float32)def construct(self, x):emb = x[:, None] * self.emb[None, :]emb = ops.concat((ops.sin(emb), ops.cos(emb)), axis=-1)return embclass Block(nn.Cell):def __init__(self, dim, dim_out, groups=1):super().__init__()self.proj = nn.Conv2d(dim, dim_out, 3, pad_mode="pad", padding=1)self.proj = c(dim, dim_out, 3, padding=1, pad_mode='pad')self.norm = nn.GroupNorm(groups, dim_out)self.act = nn.SiLU()def construct(self, x, scale_shift=None):x = self.proj(x)x = self.norm(x)if exists(scale_shift):scale, shift = scale_shiftx = x * (scale + 1) + shiftx = self.act(x)return x# ResNet/ConvNeXT模块
class ConvNextBlock(nn.Cell):def __init__(self, dim, dim_out, *, time_emb_dim=None, mult=2, norm=True):super().__init__()self.mlp = (nn.SequentialCell(nn.GELU(), nn.Dense(time_emb_dim, dim))if exists(time_emb_dim)else None)self.ds_conv = nn.Conv2d(dim, dim, 7, padding=3, group=dim, pad_mode="pad")self.net = nn.SequentialCell(nn.GroupNorm(1, dim) if norm else nn.Identity(),nn.Conv2d(dim, dim_out * mult, 3, padding=1, pad_mode="pad"),nn.GELU(),nn.GroupNorm(1, dim_out * mult),nn.Conv2d(dim_out * mult, dim_out, 3, padding=1, pad_mode="pad"),)self.res_conv = nn.Conv2d(dim, dim_out, 1) if dim != dim_out else nn.Identity()def construct(self, x, time_emb=None):h = self.ds_conv(x)if exists(self.mlp) and exists(time_emb):assert exists(time_emb), "time embedding must be passed in"condition = self.mlp(time_emb)condition = condition.expand_dims(-1).expand_dims(-1)h = h + conditionh = self.net(h)return h + self.res_conv(x)
3、Attention 机制

接下来是大名鼎鼎的Attention模块,其中使用了多头自注意力机制和线性注意力机制,其时间和内存要求在序列长度上线性缩放,而不是在常规注意力中缩放:

class Attention(nn.Cell):def __init__(self, dim, heads=4, dim_head=32):super().__init__()self.scale = dim_head ** -0.5self.heads = headshidden_dim = dim_head * headsself.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, pad_mode='valid', has_bias=False)self.to_out = nn.Conv2d(hidden_dim, dim, 1, pad_mode='valid', has_bias=True)self.map = ops.Map()self.partial = ops.Partial()def construct(self, x):b, _, h, w = x.shapeqkv = self.to_qkv(x).chunk(3, 1)q, k, v = self.map(self.partial(rearrange, self.heads), qkv)q = q * self.scale# 'b h d i, b h d j -> b h i j'sim = ops.bmm(q.swapaxes(2, 3), k)attn = ops.softmax(sim, axis=-1)# 'b h i j, b h d j -> b h i d'out = ops.bmm(attn, v.swapaxes(2, 3))out = out.swapaxes(-1, -2).reshape((b, -1, h, w))return self.to_out(out)class LayerNorm(nn.Cell):def __init__(self, dim):super().__init__()self.g = Parameter(initializer('ones', (1, dim, 1, 1)), name='g')def construct(self, x):eps = 1e-5var = x.var(1, keepdims=True)mean = x.mean(1, keep_dims=True)return (x - mean) * rsqrt((var + eps)) * self.gclass LinearAttention(nn.Cell):def __init__(self, dim, heads=4, dim_head=32):super().__init__()self.scale = dim_head ** -0.5self.heads = headshidden_dim = dim_head * headsself.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, pad_mode='valid', has_bias=False)self.to_out = nn.SequentialCell(nn.Conv2d(hidden_dim, dim, 1, pad_mode='valid', has_bias=True),LayerNorm(dim))self.map = ops.Map()self.partial = ops.Partial()def construct(self, x):b, _, h, w = x.shapeqkv = self.to_qkv(x).chunk(3, 1)q, k, v = self.map(self.partial(rearrange, self.heads), qkv)q = ops.softmax(q, -2)k = ops.softmax(k, -1)q = q * self.scalev = v / (h * w)# 'b h d n, b h e n -> b h d e'context = ops.bmm(k, v.swapaxes(2, 3))# 'b h d e, b h d n -> b h e n'out = ops.bmm(context.swapaxes(2, 3), q)out = out.reshape((b, -1, h, w))return self.to_out(out)

DDPM将U-Net的卷积和注意力层在注意力层之前进行组归一化:

class PreNorm(nn.Cell):def __init__(self, dim, fn):super().__init__()self.fn = fnself.norm = nn.GroupNorm(1, dim)def construct(self, x):x = self.norm(x)return self.fn(x)
4、条件 U-Net

相比于普通的编解码结构(Encoder-Decoder),U-Net在编码器和解码器之间引入了跳跃连接,极大地改善了梯度流:
在这里插入图片描述
基于mindspore的U-Net实现:
将先前预定义的位置编码、ResNet/ConvNeXT、Self-Attention和组归一化,用于定义现在的神经网络中。
网络获取了一批(batch_size, num_channels, height, width)形状的噪声图像和一批(batch_size, 1)形状的噪音水平作为输入,并返回(batch_size, num_channels, height, width)形状的张量。

网络构建过程如下:

  • 首先,将卷积层应用于噪声图像批上,并计算噪声水平的位置

  • 接下来,应用一系列下采样级。每个下采样阶段由2个ResNet/ConvNeXT块 + groupnorm + attention + 残差连接 + 一个下采样操作组成

  • 在网络的中间,再次应用ResNet或ConvNeXT块,并与attention交织

  • 接下来,应用一系列上采样级。每个上采样级由2个ResNet/ConvNeXT块+ groupnorm + attention + 残差连接 + 一个上采样操作组成

  • 最后,应用ResNet/ConvNeXT块,然后应用卷积层

最终,神经网络将层堆叠起来,并构成前向传播。

class Unet(nn.Cell):def __init__(self,dim,init_dim=None,out_dim=None,dim_mults=(1, 2, 4, 8),channels=3,with_time_emb=True,convnext_mult=2,):super().__init__()self.channels = channelsinit_dim = default(init_dim, dim // 3 * 2)self.init_conv = nn.Conv2d(channels, init_dim, 7, padding=3, pad_mode="pad", has_bias=True)dims = [init_dim, *map(lambda m: dim * m, dim_mults)]in_out = list(zip(dims[:-1], dims[1:]))block_klass = partial(ConvNextBlock, mult=convnext_mult)if with_time_emb:time_dim = dim * 4self.time_mlp = nn.SequentialCell(SinusoidalPositionEmbeddings(dim), # 位置编码nn.Dense(dim, time_dim),nn.GELU(),nn.Dense(time_dim, time_dim),)else:time_dim = Noneself.time_mlp = Noneself.downs = nn.CellList([])self.ups = nn.CellList([])num_resolutions = len(in_out)for ind, (dim_in, dim_out) in enumerate(in_out):is_last = ind >= (num_resolutions - 1)# 下采样阶段self.downs.append(nn.CellList([block_klass(dim_in, dim_out, time_emb_dim=time_dim),block_klass(dim_out, dim_out, time_emb_dim=time_dim),Residual(PreNorm(dim_out, LinearAttention(dim_out))),Downsample(dim_out) if not is_last else nn.Identity(),]))mid_dim = dims[-1]self.mid_block1 = block_klass(mid_dim, mid_dim, time_emb_dim=time_dim)self.mid_attn = Residual(PreNorm(mid_dim, Attention(mid_dim)))self.mid_block2 = block_klass(mid_dim, mid_dim, time_emb_dim=time_dim)for ind, (dim_in, dim_out) in enumerate(reversed(in_out[1:])):is_last = ind >= (num_resolutions - 1)# 上采样阶段self.ups.append(nn.CellList([block_klass(dim_out * 2, dim_in, time_emb_dim=time_dim),block_klass(dim_in, dim_in, time_emb_dim=time_dim),Residual(PreNorm(dim_in, LinearAttention(dim_in))),Upsample(dim_in) if not is_last else nn.Identity(),]))out_dim = default(out_dim, channels)self.final_conv = nn.SequentialCell(block_klass(dim, dim), nn.Conv2d(dim, out_dim, 1))def construct(self, x, time):  # 前向传播x = self.init_conv(x)t = self.time_mlp(time) if exists(self.time_mlp) else Noneh = []for block1, block2, attn, downsample in self.downs:  # 下采样阶段x = block1(x, t)x = block2(x, t)x = attn(x)h.append(x)x = downsample(x)x = self.mid_block1(x, t)x = self.mid_attn(x)x = self.mid_block2(x, t)len_h = len(h) - 1for block1, block2, attn, upsample in self.ups:  # 上采样阶段x = ops.concat((x, h[len_h]), 1)len_h -= 1x = block1(x, t)x = block2(x, t)x = attn(x)x = upsample(x)return self.final_conv(x)
5、Diffusion 正向过程

首先使用T=200的时间步长,定义噪声添加过程中需要的各种变量:

# 扩散200步
timesteps = 200# 定义 beta schedule
betas = linear_beta_schedule(timesteps=timesteps)# 定义 alphas
alphas = 1. - betas
alphas_cumprod = np.cumprod(alphas, axis=0)
alphas_cumprod_prev = np.pad(alphas_cumprod[:-1], (1, 0), constant_values=1)sqrt_recip_alphas = Tensor(np.sqrt(1. / alphas))
sqrt_alphas_cumprod = Tensor(np.sqrt(alphas_cumprod))
sqrt_one_minus_alphas_cumprod = Tensor(np.sqrt(1. - alphas_cumprod))# 计算 q(x_{t-1} | x_t, x_0)
posterior_variance = betas * (1. - alphas_cumprod_prev) / (1. - alphas_cumprod)p2_loss_weight = (1 + alphas_cumprod / (1 - alphas_cumprod)) ** -0.
p2_loss_weight = Tensor(p2_loss_weight)def extract(a, t, x_shape):b = t.shape[0]out = Tensor(a).gather(t, -1)return out.reshape(b, *((1,) * (len(x_shape) - 1)))def q_sample(x_start, t, noise=None):  # 前向扩散过程if noise is None:noise = randn_like(x_start)return (extract(sqrt_alphas_cumprod, t, x_start.shape) * x_start +extract(sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise)def p_losses(unet_model, x_start, t, noise=None):  # 模型的损失函数if noise is None:noise = randn_like(x_start)x_noisy = q_sample(x_start=x_start, t=t, noise=noise)predicted_noise = unet_model(x_noisy, t)loss = nn.SmoothL1Loss()(noise, predicted_noise)# todoloss = loss.reshape(loss.shape[0], -1)loss = loss * extract(p2_loss_weight, t, loss.shape)return loss.mean()
6、Diffusion 反向过程

在这里插入图片描述
可以通过上述过程,即Diffusion Models生成高质量的结果,如下图中的人脸图像均是生成出来的,并不是现实中实际的人脸:
在这里插入图片描述

7、Diffusion 模型训练

以Fashion_Mnist为例的 Diffusion 模型训练过程如下,具体完整代码可登录昇思大模型平台扩散模型的Jupyter notebook进行尝试,打印log如下:

# 定义动态学习率
lr = nn.cosine_decay_lr(min_lr=1e-7, max_lr=1e-4, total_step=10*3750, step_per_epoch=3750, decay_epoch=10)# 定义 Unet模型
unet_model = Unet(dim=image_size,channels=channels,dim_mults=(1, 2, 4,)
)name_list = []
for (name, par) in list(unet_model.parameters_and_names()):name_list.append(name)
i = 0
for item in list(unet_model.trainable_params()):item.name = name_list[i]i += 1# 定义优化器
optimizer = nn.Adam(unet_model.trainable_params(), learning_rate=lr)
loss_scaler = DynamicLossScaler(65536, 2, 1000)# 定义前向过程
def forward_fn(data, t, noise=None):loss = p_losses(unet_model, data, t, noise)return loss# 计算梯度
grad_fn = ms.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=False)# 梯度更新
def train_step(data, t, noise):loss, grads = grad_fn(data, t, noise)optimizer(grads)return lossimport timeepochs = 10for epoch in range(epochs):begin_time = time.time()for step, batch in enumerate(dataset.create_tuple_iterator()):unet_model.set_train()batch_size = batch[0].shape[0]t = randint(0, timesteps, (batch_size,), dtype=ms.int32)noise = randn_like(batch[0])loss = train_step(batch[0], t, noise)if step % 500 == 0:print(" epoch: ", epoch, " step: ", step, " Loss: ", loss)end_time = time.time()times = end_time - begin_timeprint("training time:", times, "s")# 展示随机采样效果unet_model.set_train(False)samples = sample(unet_model, image_size=image_size, batch_size=64, channels=channels)plt.imshow(samples[-1][5].reshape(image_size, image_size, channels), cmap="gray")
print("Training Success!")

由于时间原因,只训练几个Epoch的可视化效果不是很好,loss未进入收敛状态。Fashion_Mnist单张输入只是28x28x1的shape,可以感受到diffusion的训练速度要比GAN慢了很多。如果时间充足,可以用Asend910尝试多跑几个epoch;
在这里插入图片描述
在这里插入图片描述

Reference

综述 - 扩散模型 - Diffusion Models
MindSpore官方文档-Diffusion扩散模型
annotated-diffusion
由浅入深了解Diffusion Model

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

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

相关文章

【Qt知识】Geometry属性

一、走进Geometry的世界 Geometry属性是Qt框架中用于处理和操作几何形状的一系列类的集合。它包括了QPoint、QPointF、QSize、QSizeF、QRect和QRectF等。这些类分别代表点、大小、矩形等基本几何概念,它们的存在让图形界面的创建变得既简单又直观。 位置和尺寸。 其…

css 滚动词云

css javascript 实现滚动词云效果 // 163css.js var radius 120; var dtr Math.PI / 180; var d 300; var mcList []; var active false; var lasta 1; var lastb 1; var distr true; var tspeed 10; var size 250; var mouseX 0; var mouseY 0; var howElliptic…

MySQL高级-MVCC-隐藏字段

文章目录 1、介绍2、测试2.1、进入服务器中的 /var/lib/mysql/atguigu/2.2、查看有主键的表 stu2.3、查看没有主键的表 employee2.3.1、创建表 employee2.3.2、查看表结构及其其中的字段信息 1、介绍 ---------------- | id | age | name | ---------------- | 1 | 1 | Js…

python读取语文成绩 青少年编程电子学会python编程等级考试三级真题解析2022年3月

目录 python读取语文成绩 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python读取语文成绩 2022年3月 python编程等级考试级编程题 一、题目…

【Qt】之【Bug】大量出现“未定义的标识符”问题

背景 构建时出现大量错误 原因 中文注释问题 解决 方法1. 报错代码附近的中文注释全部删掉。。。 方法2. 报错的文件添加 // Chinese word comment solution #pragma execution_character_set("utf-8")

第二天:ALOAM前端讲解【第3部分】

(2)面特征 点到面的距离公式: d H = ∣ ( X ~ ( k + 1 , i ) L − X ˉ ( k , j ) L ) ⋅ ( ( X ˉ ( k , j ) L − X ˉ ( k , l ) L ) ( X ˉ ( k , j ) L − X ˉ ( k , m ) L ) ) ∣ ∣ ( X ˉ ( k , j ) L − X ˉ ( k , l ) L ) ( X ˉ ( k , j ) L − X ˉ ( k ,…

Linux常用命令大全(超详细!!!)

文章目录 1.Linux是什么1.1 关于Linux我们主要学习什么1.1 学习Linux常见命令的前置知识 2. Linux常见命令2.1 ls命令2.2 cd命令2.3 pwd命令2.4 touch命令2.5 cat命令2.6 echo命令2.7 vim命令2.8 mkdir 命令2.9 rm命令2.10 cp命令2.11 mv命令2.12 grep命令2.13 ps命令2.14 nets…

文华财经通达信同花顺期货通盘立方博易大师主图指标公式源码

买线:EMA(C,2); 卖线:EMA(SLOPE(C,21)*20C,42); BU:CROSS(买线,卖线); SEL:CROSS(卖线,买线); STICKLINE1(买线>卖线,LOW,MIN(O,C),0.1,1),COLORRED; STICKLINE1(买线>卖线,MAX(O,C),HIGH,0.1,1),COLORRED; STICKLINE(买线>卖线,CLOSE,OPEN,8,1),COLORRED; STI…

【简单讲解下OneFlow深度学习框架】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…

AD如何将厂家的元器件库,添加到自己的元器件库

首先,去官网下载对应芯片的原理图和封装,之后用分别双击打开原理图和封装。之后打开自己的原理图库和封装库。如下图: 打开原理图和封装后框选,之后crlC复制,之后点开自己的原理图库和封装库,随便单击一个元…

Linux(Ubuntu20.04)系统中安装deb软件包错误(依赖关系问题-仍未被配置)解决的办法

在Ubuntu16.04下采用如下dpkg命令安装deb软件安装包时: sudo dpkg -i XXXX.deb 发生安装失败,返回信息为"正处理时有错误发生",并且在安装过程中出现"依赖关系问题-仍未被配置"的提示&#xff0…

51单片机第11步_在C语言中插入汇编语言

本章重点介绍如何在C语言中插入汇编语言。要不是有记录,真不知道怎么搞。 /* 你在 Project Workspace窗口中,将光标移到DELAY.c处,点下鼠标右键,选择"Options for file DELAY.c", 点击右边的"Generate Assembler SRC File"和“Assemble SRC …

【PL理论深化】(12) Ocaml 语言:高阶函数 | map 函数 | filter 函数 | fold 函数

💬 写在前面:在函数式编程中,除了递归函数外,还经常使用高阶函数。高阶函数是指接收其他函数作为参数或返回另一个函数的函数。高阶函数通过抽象编程模式以实现重用,使程序可以在更高层次上进行编写。让我们重点看看常…

K8S基础简介

用于自动部署,扩展和管理容器化应用程序的开源系统。 功能: 服务发现和负载均衡; 存储编排; 自动部署和回滚; 自动二进制打包; 自我修复; 密钥与配置管理; 1. K8S组件 主从方式架…

socket编程常见操作

1、连接的建立 分为两种:服务端处理接收客户端的连接;服务端作为客户端连接第三方服务 //作为服务端 int listenfd socket(AF_INET, SOCK_STREAM, 0); bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) listen(listenfd, 10); //…

SARscape打开Sentinel1A SAR SLC产品(CSDB_20240630)

1.打开envi,在右侧工具包栏输入“sentinel-1”,并点击打开工具包。 2. 弹出文件导入界面,点击右侧Browse按钮。 3. 选在本地下载好的Sentinel1产品,文件路径最好全是英文,不要出现中文和特殊字符。 4 点击下方“Exec”…

BLACKBOX.AI:解锁编程学习新纪元,加速开发的AI得力助手

文章目录 💯BLACKBOX.AI 官网🍁1 BLACKBOX.AI 工具使用教程🍁2 BLACKBOX.AI工具使用界面介绍🍁3 Chat(聊天)功能🍁4 Explore (探索)功能💎4.1 Terminal(终端)功能💎4.2 Discover(发现)功能&…

ros1仿真导航机器人 hector_mapping gmapping

仅为学习记录和一些自己的思考&#xff0c;不具有参考意义。 1 hector_mapping 建图过程 &#xff08;1&#xff09;gazebo仿真 roslaunch why_simulation why_slam.launch <launch><!-- We resume the logic in empty_world.launch, changing only the name of t…

助农扶贫网站

摘要&#xff1a;随着信息科技的快速发展和互联网的普及&#xff0c;信息技术在助力农业发展、促进农村振兴以及扶贫工作中发挥着越来越重要的作用。本文基于Spring Boot框架和Vue.js前端开发技术&#xff0c;设计完成了一个助农扶贫电商网站。网站提供便捷的农产品信息发布、农…

three.js - MeshStandardMaterial(标准网格材质)- 金属贴图、粗糙贴图

金属贴图、粗糙贴图 金属贴图&#xff1a;metalnessMap 和 粗糙贴图&#xff1a;roughnessMap&#xff0c;是用于模拟物体表面属性的两种重要贴图技术&#xff0c;这两种贴图&#xff0c;通常与基于物理的渲染&#xff08;PBR&#xff09;材质&#xff08;如&#xff1a;MeshSt…