Diffusion Models 简单代码示例

news2025/5/26 5:07:38

一、关于Diffusion 模型的简单介绍

 

首先diffusion模型和VAE、Flow、Gan等模型类似,均属于生成模型,可以和GCN、CNN等其他深度学习网络相结合,完成特定的生成任务,如下图:

基于 GAN 生成模型,基于 VAE 的生成模型,以及基于 flow 的生成模型它们都可以生成较高质量的样本,但每种方法都有其局限性。GAN 在对抗训练过程中会出现模式崩塌和训练不稳定的问题;VAE 则严重依赖于目标损失函数;流模型则必须使用专门的框架来构建可逆变换。

扩散模型的灵感来自于非平衡热力学。他们定义了一个扩散步骤的马尔可夫链,慢慢地向数据添加随机噪声,然后学习反向扩散过程,从噪声中构建所需的数据样本。与VAE或flow模型不同,扩散模型的学习过程是固定的,潜变量具有高维数(与原始数据相同)。

扩散模型的目标是通过数据在潜在空间(latent space)的扩散过程,学习数据的潜在向量结构(latent structure),通俗点说,扩散模型学习利用数据逐步变成噪声的过程,学习反向的去噪声过程。在计算机视觉中,这意味着训练神经网络通过学习反向扩散过程对高斯噪声模糊的图像进行去噪。计算机视觉中使用的三个通用扩散建模框架的例子是去噪扩散概率模型(DDPM)、噪声条件评分网络(noise conditioned score networks)和随机微分方程(stochastic differential equations)(注:该分类为维基百科,也有分成: diffusion probabilistic models (Sohl-Dickstein et al., 2015), noise-conditioned score network (NCSN; Yang & Ermon, 2019), and denoising diffusion probabilistic models (DDPM; Ho et al. 2020))。

 接下来搬运各种连接,以便大家进一步了解diffusion 模型。

通俗的理解diffusion 模型:

Diffusion Models: A Practical Guide | Scale AIWith the Release of Dall-E 2, Google’s Imagen, Stable Diffusion, and Midjourney, diffusion models have taken the world by storm, inspiring creativity and pushing the boundaries of machine learning. In this guide we help to denoise diffusion models, describing how they work and discussing practical applications for today and tomorrow.https://scale.com/guides/diffusion-models-guide

非常详细的公式推导过程: 

What are Diffusion Models? | Lil'Log[Updated on 2021-09-19: Highly recommend this blog post on score-based generative modeling by Yang Song (author of several key papers in the references)]. [Updated on 2022-08-27: Added classifier-free guidance, GLIDE, unCLIP and Imagen. [Updated on 2022-08-31: Added latent diffusion model.So far, I’ve written about three types of generative models, GAN, VAE, and Flow-based models. They have shown great success in generating high-quality samples, but each has some limitations of its own.https://lilianweng.github.io/posts/2021-07-11-diffusion-models/

How diffusion models work: the math from scratch | AI SummerA deep dive into the mathematics and the intuition of diffusion models. Learn how the diffusion process is formulated, how we can guide the diffusion, the main principle behind stable diffusion, and their connections to score-based models.https://theaisummer.com/diffusion-models/

训练difussion模型视频:

https://www.youtube.com/watch?v=TBCRlnwJtZUhttps://www.youtube.com/watch?v=TBCRlnwJtZU

简单的Diffusion模型代码:

GitHub - dome272/Diffusion-Models-pytorch: Pytorch implementation of Diffusion Models (https://arxiv.org/pdf/2006.11239.pdf)Pytorch implementation of Diffusion Models (https://arxiv.org/pdf/2006.11239.pdf) - GitHub - dome272/Diffusion-Models-pytorch: Pytorch implementation of Diffusion Models (https://arxiv.org/pdf/2006.11239.pdf)https://github.com/dome272/Diffusion-Models-pytorch

这里从上述github的扩散模型代码为例.

这是一个在100行代码内实现扩散模型的案例,且过程是易于理解的。与其他实现代码不同的是,这段代码没有使用下界公式进行采样(lower-bound formulation for sampling),并严格遵循DDPM(denoising diffusion probabilistic models)论文中的算法1,这使得它非常简短,易于理解。有两种实现:条件和无条件(conditional and unconditional )。此外,条件的代码还实现了分类器自由引导( Classifier-Free-Guidance,CFG)和指数移动平均( Classifier-Free-Guidance,EMA)。你可以从下面两个视频中的解释,理解扩散模型背后的理论和实现。

一、复制环境代码

复制项目代码:

git clone https://github.com/dome272/Diffusion-Models-pytorch.git

使用env.yml创建项目环境:

conda env create -f env.yml

三、训练unconditional diffusion model

根据github的内容,训练所需的数据集,可以从以下链接中下载:

 

可以选择其中一种类型来下载,也可以选择下载全部,这与生成的图片内容有关。下载完成后,可以放在./dataset中。也可以分为./dataset/train, ./dataset/test。

然后将ddmp.py中的以下部分换成刚才数据集保存的路径,例如:

./dataset/train

 

对于GPU只有16G显存的设备,还需要将args.batch_size改小一些,比如:6。原来的batch_size为12会导致爆内存。

 

接下来,对ddpm.py的代码进行解析,以期了解diffusion模型结构。从train()函数开始。

def train(args):
    #创建models,results,run_name文件夹
    setup_logging(args.run_name)
    device = args.device
    #加载数据为dataloader
    dataloader = get_data(args)
    model = UNet().to(device)
    #AdamW是类似adam的优化器,但是好一些?对正则化处理更正确
    optimizer = optim.AdamW(model.parameters(), lr=args.lr)
    mse = nn.MSELoss()
    #扩散模型
    diffusion = Diffusion(img_size=args.image_size, device=device)
    #记录log
    logger = SummaryWriter(os.path.join("runs", args.run_name))
    l = len(dataloader)

    for epoch in range(args.epochs):
        logging.info(f"Starting epoch {epoch}:")
        pbar = tqdm(dataloader)
        for i, (images, _) in enumerate(pbar):
            images = images.to(device)
            #随机采样扩散步数
            t = diffusion.sample_timesteps(images.shape[0]).to(device)
            #添加噪声,也称向前过程
            x_t, noise = diffusion.noise_images(images, t)
            #预测噪声,也称逆向过程
            predicted_noise = model(x_t, t)
            #噪声损失
            loss = mse(noise, predicted_noise)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            pbar.set_postfix(MSE=loss.item())
            logger.add_scalar("MSE", loss.item(), global_step=epoch * l + i)
        #扩单模型采样(生成)照片
        sampled_images = diffusion.sample(model, n=images.shape[0])
        save_images(sampled_images, os.path.join("results", args.run_name, f"{epoch}.jpg"))
        torch.save(model.state_dict(), os.path.join("models", args.run_name, f"ckpt.pt"))

在for循环迭代中,对于每一个图片样本,都使用了随机采样扩散步数(即噪声添加步数t),添加噪声,预测噪声t步,噪声损失的过程。在生成(sample)部分,基于已经带有噪声的X,先预测噪声,然后基于噪声和X计算真实的X。所以接下来仔细看看Diffusion模块,如何实现这些过程。

#扩散模型
class Diffusion:
    def __init__(self, noise_steps=1000, beta_start=1e-4, beta_end=0.02, img_size=256, device="cuda"):
        self.noise_steps = noise_steps
        self.beta_start = beta_start
        self.beta_end = beta_end
        self.img_size = img_size
        self.device = device

        self.beta = self.prepare_noise_schedule().to(device)
        self.alpha = 1. - self.beta
        #1-β累乘,其中每个元素的值等于其在x及之前所有元素的乘积
        self.alpha_hat = torch.cumprod(self.alpha, dim=0)

    def prepare_noise_schedule(self):
        #β,线性变化
        return torch.linspace(self.beta_start, self.beta_end, self.noise_steps)

    def noise_images(self, x, t):
         #添加噪声过程,向前过程,t步,由于高斯分布的特征不需要一步步添加。
         #根号下1-β的累乘
        sqrt_alpha_hat = torch.sqrt(self.alpha_hat[t])[:, None, None, None]
        #根号下1-(1-β)的累乘
        sqrt_one_minus_alpha_hat = torch.sqrt(1 - self.alpha_hat[t])[:, None, None, None]
        Ɛ = torch.randn_like(x)
        return sqrt_alpha_hat * x + sqrt_one_minus_alpha_hat * Ɛ, Ɛ

    def sample_timesteps(self, n):
        #随机添加噪声的步数
        return torch.randint(low=1, high=self.noise_steps, size=(n,))

    def sample(self, model, n):
        logging.info(f"Sampling {n} new images....")
        model.eval()
        with torch.no_grad():
            #随机初始化
            x = torch.randn((n, 3, self.img_size, self.img_size)).to(self.device)
            for i in tqdm(reversed(range(1, self.noise_steps)), position=0):
                t = (torch.ones(n) * i).long().to(self.device)
                #第t步的噪声,注意这里的t是从大到小的过程
                predicted_noise = model(x, t)
                alpha = self.alpha[t][:, None, None, None]
                alpha_hat = self.alpha_hat[t][:, None, None, None]
                beta = self.beta[t][:, None, None, None]
                if i > 1:
                    noise = torch.randn_like(x)
                else:
                    noise = torch.zeros_like(x)
                #还原样本
                x = 1 / torch.sqrt(alpha) * (x - ((1 - alpha) / (torch.sqrt(1 - alpha_hat))) * predicted_noise) + torch.sqrt(beta) * noise
        model.train()
        x = (x.clamp(-1, 1) + 1) / 2
        x = (x * 255).type(torch.uint8)
        return x

扩散模型的添加噪音过程是不需要一步步添加的,以下公式:

 

Xt为第t步的特征,可以直接表示为X0和高斯分布Z的叠加。所以显然这里不需要一步步叠加噪声。但是去噪过程需要一步步进行的。

由于这里没有采用下界公式进行采样(lower-bound formulation for sampling),所以这里的损失会简单一些,简化了,直接使用MSE损失。

以下是unconditional diffusion 的测试代码:

import os
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
from tqdm import tqdm
from torch import optim
from utils import *
from modules import UNet
import logging

from ddpm import Diffusion

device = "cuda"
model = UNet().to(device)
ckpt = torch.load("unconditional_ckpt.pt")
model.load_state_dict(ckpt)
diffusion = Diffusion(img_size=64, device=device)
x = diffusion.sample(model, n=16)
plot_images(x)

生成的图片如下。质量还是比较差的,可能是模型没有训练好或者模型不完整的原因。

四、conditional diffusion model

train函数与unconditional的类似,不同的是,在预测噪声时候,是带有标签的,同时在采样时也需要有标签信息(标签和标签类别。)

def train(args):
	    setup_logging(args.run_name)
	    device = args.device
	    dataloader = get_data(args)
        # conditional 的UNet
	    model = UNet_conditional(num_classes=args.num_classes).to(device)
	    optimizer = optim.AdamW(model.parameters(), lr=args.lr)
	    mse = nn.MSELoss()
	    diffusion = Diffusion(img_size=args.image_size, device=device)
	    logger = SummaryWriter(os.path.join("runs", args.run_name))
	    l = len(dataloader)
	    ema = EMA(0.995)
	    ema_model = copy.deepcopy(model).eval().requires_grad_(False)
	
	    for epoch in range(args.epochs):
	        logging.info(f"Starting epoch {epoch}:")
	        pbar = tqdm(dataloader)
	        for i, (images, labels) in enumerate(pbar):
	            images = images.to(device)
	            labels = labels.to(device)
	            t = diffusion.sample_timesteps(images.shape[0]).to(device)
	            x_t, noise = diffusion.noise_images(images, t)
	            if np.random.random() < 0.1:
	                labels = None
                #预测噪声时,带有condition,即labels
	            predicted_noise = model(x_t, t, labels)
	            loss = mse(noise, predicted_noise)
	
	            optimizer.zero_grad()
	            loss.backward()
	            optimizer.step()
	            ema.step_ema(ema_model, model)
	
	            pbar.set_postfix(MSE=loss.item())
	            logger.add_scalar("MSE", loss.item(), global_step=epoch * l + i)
	
	        if epoch % 10 == 0:
	            labels = torch.arange(10).long().to(device)
	            sampled_images = diffusion.sample(model, n=len(labels), labels=labels)
	            ema_sampled_images = diffusion.sample(ema_model, n=len(labels), labels=labels)
	            plot_images(sampled_images)
	            save_images(sampled_images, os.path.join("results", args.run_name, f"{epoch}.jpg"))
	            save_images(ema_sampled_images, os.path.join("results", args.run_name, f"{epoch}_ema.jpg"))
	            torch.save(model.state_dict(), os.path.join("models", args.run_name, f"ckpt.pt"))
	            torch.save(ema_model.state_dict(), os.path.join("models", args.run_name, f"ema_ckpt.pt"))
	            torch.save(optimizer.state_dict(), os.path.join("models", args.run_name, f"optim.pt"))

conditional diffusion 模型的代码:与unconditional的不同,conditional diffusion在预测噪音是需要输入标签信息,同时使用unconditional的噪音,进行线性插值,获得用于复原样本的噪音。

class Diffusion:
    def __init__(self, noise_steps=1000, beta_start=1e-4, beta_end=0.02, img_size=256, device="cuda"):
        self.noise_steps = noise_steps
        self.beta_start = beta_start
        self.beta_end = beta_end

        self.beta = self.prepare_noise_schedule().to(device)
        self.alpha = 1. - self.beta
        self.alpha_hat = torch.cumprod(self.alpha, dim=0)

        self.img_size = img_size
        self.device = device

    def prepare_noise_schedule(self):
        return torch.linspace(self.beta_start, self.beta_end, self.noise_steps)

    def noise_images(self, x, t):
        sqrt_alpha_hat = torch.sqrt(self.alpha_hat[t])[:, None, None, None]
        sqrt_one_minus_alpha_hat = torch.sqrt(1 - self.alpha_hat[t])[:, None, None, None]
        Ɛ = torch.randn_like(x)
        return sqrt_alpha_hat * x + sqrt_one_minus_alpha_hat * Ɛ, Ɛ

    def sample_timesteps(self, n):
        return torch.randint(low=1, high=self.noise_steps, size=(n,))

    def sample(self, model, n, labels, cfg_scale=3):
        logging.info(f"Sampling {n} new images....")
        model.eval()
        with torch.no_grad():
            x = torch.randn((n, 3, self.img_size, self.img_size)).to(self.device)
            for i in tqdm(reversed(range(1, self.noise_steps)), position=0):
                t = (torch.ones(n) * i).long().to(self.device)
                predicted_noise = model(x, t, labels)
                if cfg_scale > 0:
                    uncond_predicted_noise = model(x, t, None)
                    #基于没有条件的噪音,带条件的噪音,类别信息进行线性插值,生成条件噪音
                    predicted_noise = torch.lerp(uncond_predicted_noise, predicted_noise, cfg_scale)
                alpha = self.alpha[t][:, None, None, None]
                alpha_hat = self.alpha_hat[t][:, None, None, None]
                beta = self.beta[t][:, None, None, None]
                if i > 1:
                    noise = torch.randn_like(x)
                else:
                    noise = torch.zeros_like(x)
                x = 1 / torch.sqrt(alpha) * (x - ((1 - alpha) / (torch.sqrt(1 - alpha_hat))) * predicted_noise) + torch.sqrt(beta) * noise
        model.train()
        x = (x.clamp(-1, 1) + 1) / 2
        x = (x * 255).type(torch.uint8)
        return x

基于上述内容,可以发现,扩散模型中的Unet模型是用来预测噪音的,而不是直接生成图片,这一点与其他的生成模型不同。

conditional diffusion 的测试代码(conditional_model_test.py):

import os
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
from tqdm import tqdm
import os
import copy
import numpy as np
import torch
import torch.nn as nn
from tqdm import tqdm
from torch import optim
from utils import *
from modules import UNet_conditional, EMA
import logging

from ddpm_conditional import Diffusion

n = 10
device = "cuda"
model = UNet_conditional(num_classes=10).to(device)
ckpt = torch.load("conditional_ema_ckpt.pt")
model.load_state_dict(ckpt)
diffusion = Diffusion(img_size=64, device=device)
y = torch.Tensor([6] * n).long().to(device)
x = diffusion.sample(model, n, y, cfg_scale=3)
plot_images(x, 'conditional_ema_test')

生成的图片如下:

看起来还像那么回事。

整个项目代码,可以从百度网盘上下载:

 我通过百度网盘分享的文件:Diffusio....zip
链接:https://pan.baidu.com/s/1wm-ITu_q_kNQ8f2euovHfg 
提取码:4896 
复制这段内容打开「百度网盘APP即可获取」

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

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

相关文章

初识二叉树

树的概念与结构 树是一种非线性的数据结构&#xff0c;它是由n个有限结点组成一个具有层次关系的集合&#xff0c;把它叫做树是因为它看起来像一颗倒挂的树&#xff0c;也就是说它的根朝上&#xff0c;而叶朝下的。 子树之间不能有交集&#xff0c;否则就不是树型结构 比如下面…

3dmax间隔选择与循环选择你会用吗?真传干货来了!

文章目录一、循环选择1、AltL&#xff08;线性循环&#xff09;2、AltR&#xff08;环向循环&#xff09;3、在线选择上altR和altL的差别二、 间隔选1、选一隔一2、选二隔一3、选二隔二4、隔三选一5、面的间隔选择总结6、线的间隔选文章原出处&#xff1a; https://blog.csdn.n…

TCP拆包粘包问题

什么是粘包&#xff1f; 首先明确TCP时面向字节流的协议&#xff08;当用户消息通过 TCP 协议传输时&#xff0c;消息可能会被操作系统分组成多个的 TCP 报文&#xff0c;也就是一个完整的用户消息被拆分成多个 TCP 报文进行传输&#xff09;&#xff0c;UDP是面向报文的协议&…

【ChatGPT】AI发展如此火热,程序员的发展呢?

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; 目录一、AI已来&#xff0c;ChatGPT你用上了吗&#x1f33e;二、AI之路&#xff0c;这是社会在发展&#x1f331;三、AI时代&#xff0c;程序员应该怎么做&#x1f334;一、AI已来&#xff0c;ChatGPT你用上了吗&…

【Python】基于ML307A的位置读取系统(通过UART串口实现AT指令和flask来实现自动化读取并推流)

【Python】基于ML307A的位置读取系统&#xff08;通过UART串口实现AT指令和flask来实现自动化读取并推流&#xff09; Python下的串口serial库 串行口的属性&#xff1a; name:设备名字 portstr:已废弃&#xff0c;用name代替 port&#xff1a;读或者写端口 baudrate&#xf…

CUDA效率优化之CUDA Graph的使用

CUDA系列文章 文章目录CUDA系列文章前言一、优化方案简单顺序调用二、Overlapping三、使用CUDA Graph总结前言 GPU 架构的性能随着每一代的更新而不断提高。 现代 GPU 每个操作&#xff08;如kernel运行或内存复制&#xff09;所花费的时间现在以微秒为单位。 但是&#xff0c…

【C++跬步积累】——时间复杂度

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;C跬步积累 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日一题 &#x1f7e1; 每日反刍 &#x1f7e2; 读书笔记 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称王&a…

马云回国,首谈ChatGPT

马云今天回国了&#xff0c;这是一个备受关注的消息。 作为中国最具代表性的企业家之一&#xff0c;马云在过去的二十多年里&#xff0c;带领阿里巴巴从一个小小的创业公司&#xff0c;发展成为全球最大的电商平台之一&#xff0c;同时也推动了中国互联网行业的发展。 他的回…

使用向量机(SVM)算法的推荐系统

系统整体结构 运行环境 包括Python环境、TensorFlow环境、安装模块、MySQL数据库。 Python环境 需要Python 3.6及以上配置&#xff0c;在Windows环境下推荐下载Anaconda完成Python所需的配置&#xff0c;下载地址为https://www.anaconda.com/&#xff0c;也可下载虚拟机在Li…

项目经理如何做好项目数据分析?

“周一到周五哪天更适合工作&#xff1f;” 周一困倦&#xff0c;周二认命&#xff0c;周三亢奋&#xff0c;周四疲惫&#xff0c;周五又像打了鸡血…… 项目经理&#xff1a;哪天都不适合工作&#xff0c;项目日报、周报、月报一个都少不了&#xff1b;不是在加班整理项目数…

Direct3D 12——灯光——镜面光照

反射的发生是根据一种名为菲涅耳效应(Fresnel effect,也译作菲涅 尔效应)的物理现象。当光线到达两种不同折射率(index of refraction )介质之间的界面时&#xff0c;一部分光将 被反射&#xff0c;而剩下的光则发生折射(refract),折射率是一种介质的物理性质&#xff0c;即 光…

Day939.如何小步安全地升级数据库框架 -系统重构实战

如何小步安全地升级数据库框架 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于如何小步安全地升级数据库框架的内容。 当消息组件的数据存储都是采用 SQL 拼写的方式来操作&#xff0c;这样不便于后续的扩展及维护。除此之外&#xff0c;相比前面的其他重构&#x…

线性代数 --- Gram-Schmidt, 格拉姆-施密特正交化(上)

在求解最小二乘的问题时&#xff0c;已经介绍了类似于Gram-Schmidt的一些想法。在这里要继续介绍这些想法&#xff0c;那就是如何“改写”矩阵A中的列向量&#xff0c;使得最小二乘解的计算越来越简单&#xff0c;甚至可以直接写出答案。 标准正交基(Orthonormal Bases) 上一篇…

3.1 多维度随机变量及其分布

思维导图&#xff1a; 学习目标&#xff1a; 要学习二维随机变量及联合分布&#xff0c;我会按照以下步骤进行&#xff1a; 了解基本概念&#xff1a;首先要了解二维随机变量的概念&#xff0c;即同时包含两个随机变量的变量。还要了解二维随机变量的取值范围以及联合概率密…

【CSS】定位 ④ ( 绝对定位特点 | 相对定位不脱标示例 | 绝对定位脱标示例 )

文章目录一、绝对定位特点二、相对定位不脱标示例三、绝对定位脱标示例一、绝对定位特点 绝对定位 以 带有定位的 父级元素 为基准 , 通过 边偏移 移动位置 ; 如果 绝对定位 的元素 的 父级元素 没有定位 , 那么会 一直向上查找有定位的父级元素 , 直到浏览器 ; 绝对定位 元素…

【Linux】组管理和权限管理

目录1 Linux组的基本介绍2 文件/目录所有者2.1 查看文件的所有者2.2 修改文件所有者3 组的创建3.1 基本指令3.2 应用实例4 文件/目录 所在组4.1 查看文件/目录所在组4.2修改文件/目录所在的组5 其他组6 改变用户所在组6.1 改变用户所在的组6.2 应用实例7 权限介绍8 rwx权限详解…

多线程(八):常见锁策略

目录 前言 1. 乐观锁 VS 悲观锁 乐观锁 悲观锁 2. 轻量级锁 VS 重量级锁 轻量级锁 3. 自旋锁 VS 挂起等待锁 自旋锁 挂起等待锁 4. 读写锁 VS 互斥锁 5. 可重入锁 vs 不可重入锁 死锁 发生死锁的情况 死锁产生的四个必要条件如下&#xff1a; 6. 公平锁和非公平锁…

Hibernate多表关联——(一对多关系)

Hibernate多表关联——&#xff08;一对多关系&#xff09; 文章目录Hibernate多表关联——&#xff08;一对多关系&#xff09;1.分别在类中添加属性&#xff1a;2.hibernate建表3.使用测试类在表中添加数据hibernate是连接数据库使得更容易操作数据库数据的一个框架&#xff…

ERROR:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing错误

一、问题 重新安装hbase后&#xff0c;在hbase shell中查看所有命名空间时&#xff0c;出现了ERROR:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing错误。 二、方法 1、root用户下&#xff0c;关闭hbase stop-hbase.sh 2、执行以下命令删除HDFS下的hb…

go进阶篇gin框架系列三

一、模板引擎的语法 {{.}} 模板语法都包含在{{和}}中间&#xff0c;其中{{.}}中的点表示当前对象。 当我们传入一个结构体对象时&#xff0c;我们可以根据.来访问结构体的对应字段。 pipeline pipeline是指产生数据的操作。比如{{.}}、{{.Name}}等。Go的模板语法中支持使用管道…