【擴散模型】生成模型中的Residual Self-Attention UNet 以及 DDPM的pytorch代碼

參考:
[1] https://github.com/xiaohu2015/nngen/blob/main/models/diffusion_models/ddpm_cifar10.ipynb
[2] https://www.bilibili.com/video/BV1we4y1H7gG/?spm_id_from=333.337.search-card.all.click&vd_source=9e9b4b6471a6e98c3e756ce7f41eb134

TOC

1 UNet部分

1.1 SelfAttention

1)自注意力模塊可以調用pytorch的 nn.MultiheadAttention(channels, head, batch_first),避免重復造輪子;
2)執行順序為:

  • 將輸入由(B,C,H,W) -> (B,C,H*W) -> (B,H*W,C)
  • 通過LayerNorm模塊,得到x_ln
  • 將x_ln作為三個qkv參數傳入到多頭注意力模塊,得到attention_value
  • 將attention_value和原始輸入x進行殘差連接
  • (可加可不加)再通過前饋神經網絡
  • 將attention_value變回(B,C,H,W)
class SelfAttention(nn.Module):def __init__(self,channels):super().__init__()self.channels = channelsself.mha = nn.MultiheadAttention(channels, 4, batch_first=True)self.ln = nn.LayerNorm([channels])self.ff = nn.Sequential(nn.LayerNorm([channels]),nn.Linear(channels,channels),nn.GELU(),nn.Linear(channels,channels))def forward(self,x):B,C,H,W = x.shapex = x.reshape(-1,self.channels,H*W).swapaxes(1,2)x_ln = self.ln(x)attention_value = self.mha(x_ln)attention_value = attention_value + xattention_value = self.ff(attention_value)+ attention_valuereturn attention_value.swapaxes(1,2).view(-1,self.channels,H,W)

測試:

    # here testing MHAmha = SelfAttention(32)x = torch.rand(3,32,64,64)out = mha(x)print(x.shape)# torch.Size([3, 32, 64, 64])

1.2 DoubleConv

相當于UNet中的double conv,只不過這里把一些模塊換了,并且新增了residual結構。

class DoubleConv(nn.Module):def __init__(self,in_c,out_c,mid_c=None,residual=False):super().__init__()self.residual = residualif mid_c is None:mid_c = out_cself.double_conv = nn.Sequential(nn.Conv2d(in_c,mid_c,kernel_size=3,padding=1),nn.GroupNorm(1,mid_c),nn.GELU(),nn.Conv2d(mid_c,out_c,kernel_size=3,padding=1),nn.GroupNorm(1,mid_c))if in_c != out_c:self.shortcut = nn.Conv2d(in_c,out_c,kernel_size=1)else:self.shortcut = nn.Identity()def forward(self,x):if self.residual:return F.gelu(self.shortcut(x)+self.double_conv(x))else:return F.gelu(self.double_conv(x))

1.3 Down

down模塊其實就是一個maxpooling層,再接兩個double_conv層,其中double_conv的維度變化為in_c -> out_c -> out_c;
其次,還有一個timestep_embedding層,既一個激活函數+一個線性層,目的是為了讓timestep的維度(B, emb_dim) 和要相加的數據一致(B, out_c, h,w)

class Down(nn.Module):def __init__(self,in_c,out_c,emb_dim=256):self.maxpool_conv = nn.Sequential(nn.MaxPool2d(2) # kernel_size=2, stride default equal to kDoubleConv(in_c,out_c,residual=True),DoubleConv(out_c,out_c))self.emb_layer = nn.Sequential(nn.SiLU(),nn.Linear(emb_dim,out_c))def forward(self,x,t):x = self.maxpool_conv(x)emb = self.emb_layer(t)[:,:,None,None].repeat(1,1,x.shape[-2],x.shape[-1])# 擴維后,在最后兩維重復h和w次,此時和x的尺寸相同return x+emb

1.4 Up

Up模塊先進行雙線性插值上采樣,然后在channel維度進行拼接,之后在進行兩次double conv。
同樣要有timestep_embeding

class Up(nn.Module):def __init__(self,in_c,out_c,emb_dim=256):self.up =  nn.UpSample(scale_factor=2,mode='bilinear', align_corner=True)self.conv = nn.Sequential(nn.Conv2d(in_c,in_c,residual=True),nn.Conv2d(in_c,out_c))self.emb_layer = nn.Sequential(nn.SiLU(),nn.Linear(emb_dim,out_c))def forward(self,x,skip_x, t):x = self.up(x)x = torch.cat([x,skip_x],dim=1)x = self.conv(x)emb = self.emb_layer(t)[:,:,None,None].repeat(1,1,x.shape[-2],x.shape[-1])return x + emb

1.5 UNet模型

根據常規的UNet模型拼接起來,在每次下采樣和上采樣之后加上self-attention層

class UNet(nn.Module):def __init__(self,in_c, out_c, time_dim=256, device='cuda'):super().__init__()self.device = deviceself.time_dim = time_dimself.inc = DoubleConv(c_in, 64)self.down1 = Down(64, 128)self.sa1 = SelfAttention(128)self.down2 = Down(128, 256)self.sa2 = SelfAttention(256)self.down3 = Down(256, 512)self.sa3 = SelfAttention(512)self.bot1 = DoubleConv(512, 512)self.bot2 = DoubleConv(512, 512)self.bot3 = DoubleConv(512, 256)self.up1 = Up(512, 128)self.sa4 = SelfAttention(128)self.up2 = Up(256, 64)self.sa5 = SelfAttention(64)self.up3 = Up(128, 64)self.sa6 = SelfAttention(64)self.outc = nn.Conv2d(64, c_out, kernel_size=1)def pos_encoding(self,t,channels):freq = 1.0/(10000**torch.arange(0,channels,2,device=self.device).float()/channels)args = t[:,None].float()*freq[None]embedding = torch.cat([torch.sin(args), torch.cos(args)],dim=-1)if channels % 2 != 0:embedding = torch.cat([embedding,torch.zeros_like(embedding[:,:1])],dim=-1)return embeddigdef forward(self,x,t):t = self.pos_encoding(t,self.time_dim)x1 = self.inc(x)x2 = self.down1(x1, t)x2 = self.sa1(x2)x3 = self.down2(x2, t)x3 = self.sa2(x3)x4 = self.down3(x3, t)x4 = self.sa3(x4)x4 = self.bot1(x4)x4 = self.bot2(x4)x4 = self.bot3(x4)x = self.up1(x4, x3, t)x = self.sa4(x)x = self.up2(x, x2, t)x = self.sa5(x)x = self.up3(x, x1, t)x = self.sa6(x)output = self.outc(x)return output

關于positional_encoding的講解:

  • freq = 1.0/(10000**torch.arange(0,channels,2,device=self.device).float()/channels)
    這里是從1到 1 1000 0 256 / 256 \frac{1}{10000^{256}/256} 10000256/2561?
  • args = t[:,None].float()*freq[None]
    t為1-d向量,所以這里先進行擴維,freq是一個[channels//2]維,最終args為[3,128]
  • embedding = torch.cat([torch.sin(args), torch.cos(args)],dim=-1)
    最后一維進行拼接
  • embedding = torch.cat([embedding,torch.zeros_like(embedding[:,:1])],dim=-1)
    這里是防止維度為奇數的情況,若為奇數則在最后一維補0.
  • 使用方法:
    在每一層residual block之后,使用emb_layer對timestep_embedding進行維度變換,之后加到數據上即可。

2 Diffusion部分以及回顧

2.1 beta_schedule

linear_beta_schedule

    def linear_beta_schedule(self):scale = 1000/self.noise_stepsbeta_start = self.beta_start*scalebeta_end = self.beta_end*scalereturn torch.linspace(beta_start, beta_end, self.noise_steps)

cosine_beta_schedule
公式為: f ( t ) = c o s ( t / T + s 1 + s × π 2 ) 2 α t = f ( t ) / f ( 0 ) β t = 1 ? α t α t ? 1 f(t)=cos(\frac{t/T+s}{1+s}\times\frac{\pi}{2})^2\\\alpha_t=f(t)/f(0)\\\beta_t=1-\frac{\alpha_t}{\alpha_{t-1}} f(t)=cos(1+st/T+s?×2π?)2αt?=f(t)/f(0)βt?=1?αt?1?αt??

    def cosine_beta_schedule(self,s=0.008):"""as proposed in Improved ddpm paper;"""steps = self.noise_steps + 1x = torch.linspace(0, self.noise_steps, steps, dtype=torch.float64) # 從0到self.noise_stepsalphas_cumprod = torch.cos(((x / self.noise_steps) + s) / (1 + s) * math.pi * 0.5) ** 2alphas_cumprod = alphas_cumprod / alphas_cumprod[0]betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1]) # alpha_cumprod包含了noise_steps+1個值,則alpha_t是第一個到最后一個;alpha_{t-1}是第0個到倒數第二個(第0個為0)return torch.clip(betas, 0, 0.999) # 不大于0.999

2.2 初始化

回顧一下DDPM所有的公式:

  1. 前向過程
    x t = α t x t ? 1 + 1 ? α t ? x t = α ˉ t x 0 + 1 ? α ˉ t ? x_t = \sqrt\alpha_tx_{t-1}+\sqrt{1-\alpha_t}\epsilon\\ x_t = \sqrt{\bar\alpha_t}x_0+\sqrt{1-\bar\alpha_t}\epsilon xt?=α ?t?xt?1?+1?αt? ??xt?=αˉt? ?x0?+1?αˉt? ??

  2. 后驗分布 q ( x t ? 1 ∣ x t , x 0 ) q(x_{t-1}|x_t,x_0) q(xt?1?xt?,x0?)的均值和方差
    μ q = α t ( 1 ? α ˉ t ? 1 ) x t + α ˉ t ? 1 ( 1 ? α t ) x ^ θ 1 ? α ˉ t = 1 α t x t ? 1 ? α t 1 ? α ˉ t α t ? ^ ( x t , t ) \mu_q =\frac{\sqrt{\alpha_t}(1-\bar\alpha_{t-1})x_t+\sqrt{\bar\alpha_{t-1}}(1-\alpha_t)\hat x_\theta}{1-\bar\alpha_t}= \frac{1}{\sqrt{\alpha_t}}x_t-\frac{1-\alpha_t}{\sqrt{1-\bar\alpha_t}\sqrt{\alpha_t}}\hat\epsilon(x_t,t) μq?=1?αˉt?αt? ?(1?αˉt?1?)xt?+αˉt?1? ?(1?αt?)x^θ??=αt? ?1?xt??1?αˉt? ?αt? ?1?αt???^(xt?,t)
    Σ = ( 1 ? α t ) ( 1 ? α ˉ t ? 1 ) 1 ? α ˉ t I = β t ( 1 ? α ˉ t ? 1 ) 1 ? α ˉ t I \Sigma=\frac{(1-\alpha_t)(1-\bar\alpha_{t-1})}{1-\bar\alpha_t}I=\frac{\beta_t(1-\bar\alpha_{t-1})}{1-\bar\alpha_t}I Σ=1?αˉt?(1?αt?)(1?αˉt?1?)?I=1?αˉt?βt?(1?αˉt?1?)?I

  3. 每一次采樣得到的估計 x ^ 0 \hat x_0 x^0? x t ? 1 x_{t-1} xt?1?
    x ^ 0 = 1 α ˉ t ( x t ? 1 ? α ˉ t ? ) x t ? 1 = μ ~ + σ t z \hat x_0 = \frac{1}{\sqrt{\bar\alpha_t}}(x_t-\sqrt{1-\bar\alpha_t}\epsilon)\\ x_{t-1} = \tilde\mu+\sigma_t z x^0?=αˉt? ?1?(xt??1?αˉt? ??)xt?1?=μ~?+σt?z

class Diffusion:def __init__(self, noise_steps=1000, beta_start=1e-4, beta_end=0.02, img_size=256, beta_schedule='linear',device="cuda"):self.noise_steps = noise_stepsself.beta_start = beta_startself.beta_end = beta_endself.img_size = img_sizeself.device = deviceif beta_schedule == 'linear':self.beta = self.linear_beta_schedule().to(device)elif beta_schedule == 'cosine':self.beta = self.cosine_beta_schedule().to(device)else:raise ValueError(f'Unknown beta schedule {beta_schedule}')# all parametersself.alpha = 1. - self.beta self.alpha_hat = torch.cumprod(self.alpha, dim=0) self.alpha_hat_prev = F.pad(self.alpha_hat[:-1],(1,0),value=1.)self.sqrt_alpha_hat = torch.sqrt(self.alpha_hat)self.sqrt_one_minus_alpha_hat = torch.sqrt(1.-self.alpha_hat)self.sqrt_recip_alpha_hat = torch.sqrt(1./self.alpha_hat) # 用于估計x_0,估計x_0后用于計算p(x_{t-1}|x_t) 均值self.sqrt_recip_minus_alpha_hat = torch.sqrt(1./self.alpha_hat-1) self.posterior_variance = (self.beta*(1.-self.alpha_hat_prev)/(1.-self.alpha_hat)) # 用于計算p(x_{t-1}|x_t)的方差self.posterior_mean_coef1 = (self.beta * torch.sqrt(self.alpha_hat_prev) / (1.0 - self.alphas_hat)) # 用于計算p(x_{t-1}|x_t)的均值self.posterior_mean_coef2 = ((1.0 - self.alphas_hat_prev)* torch.sqrt(self.alphas)/ (1.0 - self.alphas_hat))

2.3 提取數組中的對應timestep的值

    def _extract(self,arr,t,x_shape):# 根據timestep t從arr中提取對應元素并變形為x_shapebs = x_shape[0]out = arr.to(t.device).gather(0,t).float()out = out.reshape(bs,*((1,)*(len(x_shape)-1))) # reshape為(bs,1,1,1)return out

2.4 從 x 0 x_0 x0?提取 x t x_t xt?

根據公式 x t = α ˉ t x 0 + 1 ? α ˉ t ? x_t = \sqrt{\bar\alpha_t}x_0+\sqrt{1-\bar\alpha_t}\epsilon xt?=αˉt? ?x0?+1?αˉt? ??
模型訓練首先要根據隨機采樣的t和 x 0 x_0 x0?來得到加噪后的 x t x_t xt?以及 n o i s e noise noise,所以返回兩個值。

    def q_sample(self, x, t, noise=None):# q(x_t|x_0)if noise is None:? = torch.randn_like(x)sqrt_alpha_hat = self._extract(self.sqrt_alpha_hat,t,x.shape)sqrt_one_minus_alpha_hat = self._extract(self.sqrt_one_minus_alpha_hat,t,x.shape)return sqrt_alpha_hat * x + sqrt_one_minus_alpha_hat * ?, ?

2.5 真實后驗分布 q ( x t ? 1 ∣ x t , x 0 ) q(x_{t-1}|x_t,x_0) q(xt?1?xt?,x0?)的均值和方差

參考上面的公式。
實際上我們將 p ( x t ? 1 ∣ x t ) p(x_{t-1}|x_t) p(xt?1?xt?)的分布(也就是模型擬合分布)也設為類似形式,然后將模型估計出來的重建 x 0 x_0 x0?連同 x t x_t xt?丟進這個函數來,預測出 p p p的均值和方差。重建 x 0 x_0 x0?怎么來呢?模擬預測噪聲,然后根據 x t x_t xt?和預測噪聲得來。

    def q_posterior_mean_variance(self,x,x_t,t):# calculate mean and variance of q(x_{t-1}|x_t,x_0), we send parameters x0 and x_t into this function# in fact we use this function to predict p(x_{t-1}|x_t)'s mean and variance by sending x_t, \hat x_0, tposterior_mean =  (self._extract(self.posterior_mean_coef1,t,x.shape) * x + self._extract(self.posterior_mean_coef2,t,x.shape) * x_t)posterior_variance = (self.posterior_variance,t,x.shape)return posterior_mean, posterior_variance

2.6 估計重建 x 0 x_0 x0?

參考上面公式,根據 x t x_t xt?和預測出的噪聲pred_noise來估計,相當于 x t ? ? p r e d x_t - \epsilon_{pred} xt???pred?

def estimate_x0_from_noise(self,x_t,t,noise):# \hat x_0return (self._extract(self.sqrt_recip_alpha_hat,t,x_t.shape)*x_t + self._extract(self.sqrt_recip_minus_alpha_hat,t,x_t.shape)*noise)

2.7 計算 p θ p_\theta pθ?的均值和方差

首先通過 x t , t x_t,t xt?,t預測噪聲,然后估計出重建x0,將值裁剪到(-1,1),然后去估計均值和方差

    def p_mean_variance(self,model,x_t,t,clip_denoised=True):pred_noise = model(x_t,t)x_recon = self.estimate_x0_from_noise(x_t,t,pred_noise)if clip_denoised:x_recon = torch.clamp(x_recon,min=-1.,max=1.)p_mean,p_var = self.q_posterior_mean_variance(x_recon,x_t,t)return p_mean,p_var

2.8 采樣

采樣就是 x t ? 1 = μ + σ t z x_{t-1}= \mu+\sigma_t z xt?1?=μ+σt?z,這個 σ t \sigma_t σt?是固定的,z是隨機采樣的,并且當t=0的時候,也就是最后一步不加噪聲。 loop函數采樣從noise_step到0。

    def p_sample(self, model, x_t, t, clip_denoised=True):logging.info(f"Sampling {n} new images....")model.eval()with torch.no_grad():p_mean,p_var = self.p_mean_variance(model,x_t,t,clip_denoised=clip_denoised)noise = torch.randn_like(x_t)nonzero_mask = ((t!=0).float().view(-1,*([1]*len(x_t.shape)-1))) # 當t!=0時為1,否則為0pred_img = p_mean + nonzero_mask*(torch.sqrt(p_var))*noisereturn pred_imgdef p_sample_loop(self,model,shape):model.eval()with torch.no_grad():bs = shape[0]device = next(model.parameters()).to(device)img = torch.randn(shape,device=device)imgs = []for i in tqdm(reversed(range(0,self.noise_steps)),desc='sampling loop time step',total=self.noise_steps):img = self.p_sample(model,img,torch.full((bs,),i,device=device,dtype=torch.long)) # 從T到0imgs.append(img)return imgs@torch.no_grad()def sample(self,model,img_size,bs=8,channels=3):return self.p_sample_loop(model,(bs,channels,img_size,img_size))

3 訓練部分

def train(args):setup_logging(args.run_name)device = args.devicedataloader = get_data(args)model = UNet().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)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.q_sample(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"))def launch():import argparseparser = argparse.ArgumentParser()args = parser.parse_args()args.run_name = "DDPM_Uncondtional"args.epochs = 500args.batch_size = 12args.image_size = 64args.dataset_path = r"C:\Users\dome\datasets\landscape_img_folder"args.device = "cuda"args.lr = 3e-4train(args)if __name__ == '__main__':launch()

4 其他實驗

4.1 加噪過程

import ddpm 
from PIL import Image
from torchvision import transforms
import torch
import matplotlib.pyplot as plt
import numpy as npimage = Image.open("giraffe.jpg")image_size = 128
transform = transforms.Compose([transforms.Resize(image_size),transforms.CenterCrop(image_size),transforms.PILToTensor(),transforms.ConvertImageDtype(torch.float),transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
])x_start = transform(image).unsqueeze(0)diffusion_linear = ddpm.Diffusion(noise_steps=500)
diffusion_cosine = ddpm.Diffusion(noise_steps=500,beta_schedule='cosine')plt.figure(figsize=(16, 8))
for idx, t in enumerate([0, 50, 100, 200, 499]): x_noisy,_ = diffusion_linear.q_sample(x_start, t=torch.tensor([t])) # 使用q_sample去生成x_tx_noisy2,_ = diffusion_cosine.q_sample(x_start,t=torch.tensor([t])) # [1,3,128,128]noisy_image = (x_noisy.squeeze().permute(1, 2, 0) + 1) * 127.5  # 我們的x_t被裁剪到(-1,1),所以+1后乘以127.5noisy_img2 = (x_noisy2.squeeze().permute(1,2,0)+1)*127.5 # # [128,128,3] -> (0,2) noisy_image = noisy_image.numpy().astype(np.uint8)noisy_img2 = noisy_img2.numpy().astype(np.uint8)plt.subplot(2, 5, 1 + idx)plt.imshow(noisy_image)plt.axis("off")plt.title(f"t={t}")plt.subplot(2, 5, 6+idx)plt.imshow(noisy_img2)plt.axis('off')
plt.figtext(0.5, 0.95, 'Linear Beta Schedule', ha='center', fontsize=16)  # 在第一行上方添加大標題
plt.figtext(0.5, 0.48, 'Cosine Beta Schedule', ha='center', fontsize=16)  # 在第二行上方添加大標題
plt.savefig('temp_img/add_noise_process.png')

在這里插入圖片描述

4.2 多GPU分布式代碼

4.3 去噪過程

忘記了十萬八千次的知識點(隨筆記)

  1. viewreshape的區別
    https://zhuanlan.zhihu.com/p/593664378

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/719035.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/719035.shtml
英文地址,請注明出處:http://en.pswp.cn/news/719035.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

視黃酸誘導基因-1敲除誘導樹突狀細胞的不成熟特性并延長異體移植小鼠的存活時間研究【AbMole】

器官移植是一種用于替換因疾病、損傷或其他原因受損的人體器官的醫療程序。盡管器官移植可以挽救生命并顯著提高生活質量,但存在供體器官短缺、排斥反應、器官功能障礙、感染和藥物副作用等問題。為了提高移植成功率和受體健康,需要有效的免疫策略。樹突…

如何使用支付寶沙箱環境本地配置模擬支付并實現公網遠程訪問【內網穿透】

文章目錄 前言1. 下載當面付demo2. 修改配置文件3. 打包成web服務4. 局域網測試5. 內網穿透6. 測試公網訪問7. 配置二級子域名8. 測試使用固定二級子域名訪問 前言 在沙箱環境調試支付SDK的時候,往往沙箱環境部署在本地,局限性大,在沙箱環境…

原理篇-- 定時任務xxl-job-服務端(admin)項目啟動過程--JobFailMonitorHelper初始化 (5)

文章目錄 前言一、JobFailMonitorHelper作用:二、JobFailMonitorHelper源碼內容:2.1 start() 初始化2.1.1 任務失敗重試:2.1.2 任務失敗告警信息發送:2.1.2.1 JobAlarmer 告警類:2.1.2.2 alarm 告警信息發送: 2.2 toStop() 終止線…

數據可視化原理-騰訊-3D網格熱力圖

在做數據分析類的產品功能設計時,經常用到可視化方式,挖掘數據價值,表達數據的內在規律與特征展示給客戶。 可是作為一個產品經理,(1)如果不能夠掌握各類可視化圖形的含義,就不知道哪類數據該用…

壁爐火焰溫和而寧靜,警惕火焰兇猛的潛在危害

在寒冷的冬夜,壁爐散發的溫暖和閃爍的火焰成為家庭的心靈港灣。然而,我們在享受壁爐帶來的溫馨時,有時候也要關注火焰的兇猛度,因為它可能引發一系列潛在危害。 首先,壁爐的火焰過于兇猛可能導致空氣質量下降。當火焰過…

從零開始手寫RPC框架(4)

這一節主要講述網絡傳輸模塊的代碼,并且幾乎每一行代碼都加上了我個人理解的注釋,同時也講述了其中一些以前沒見過的函數,和大致的底層運行邏輯。 目錄 網絡傳輸實體類網絡傳輸實現基于Socket實現網絡傳輸基于Netty實現網絡傳輸客戶端服務端 …

【JavaEE進階】 Linux常用命令

文章目錄 🍃前言🌴ls 與 pwd🚩ls🚩pwd 🎍cd🚩認識Linux目錄結構 🍀touch與cat🚩touch🚩cat 🌲mkdir與rm🚩mkdir🚩rm 🎄cp與…

GD庫沒有安裝FreeType 支持Call to undefined function App\Services\imagettfbbox()

GD庫是一個功能強大的圖像處理庫,廣泛用于生成和處理圖像。然而,默認情況下,GD庫不包含FreeType擴展,該擴展用于處理字體和文本。如果您需要在GD庫中使用更多的字體和文本效果,您可以按照以下步驟安裝和啟用FreeType擴…

十五、單詞造句

描述 GG Bond在和妹妹做一個游戲,GG Bond給定了妹妹一些單詞字符串,他想讓妹妹把這些單詞拼接成以空格間隔開的句子,很可惜妹妹Python沒有學好,你能使用join函數幫幫她嗎? 輸入描述: 多行輸入多個字符串…

Java基礎 - 7 - 常用API(二)

API(全稱 Application Programming Interface:應用程序編程接口) API就是Java幫我們已經寫好的一些程序,如類、方法等,可以直接拿過來用 JDK8 API文檔:Java Platform SE 8 一. Object Object類的作用 Ob…

mybatis多數據源切換

1.前提 項目中有可能需要去其他的數據庫取其他的表的信息 2.思路 2.1 直接使用原生jdbc(不推薦) 2.2 不使用我們全局配置的mybatis,對指定文件夾下使用我們指定的Session 3.解決辦法 指定該配置的范圍 package com.maycur.openapi.dao.my…

『Linux從入門到精通』第 ? 期 - 管道

文章目錄 💐專欄導讀💐文章導讀🐧進程間通信的目的🐧如何進行進程間通信🐧進程間通信的分類🐧管道🐦什么是管道🐦管道原理 🐧實例代碼🐧管道的特點&#x1f4…

Protobuf學習筆記以及序列化的一些概念要點(暫放C++筆記專欄)

Protobuf學習筆記以及序列化的一些概念要點 —— 杭州 2024-03-03 文章目錄 Protobuf學習筆記以及序列化的一些概念要點1.Protobuf概念2.實際測試2.1.準備一個test.proto2.2.使用 protoc 命令行工具來編譯一個 Protocol Buffers 文件 test.proto3.3.創建一個main.cpp寫C++代碼…

mysql 事務的隔離級別

一、事務的隔離級別要解決的問題: 1)臟讀:讀到了其它事務未提交的數據即臟讀,未提交意味著數據有可能會被回滾,也就是最終有可能不會存儲到數據庫中,即讀到了最終不一定存在存在的數據,即為臟讀…

如何選擇程序員職業賽道:挑戰與機遇并存的職業探索指南

程序員如何選擇職業賽道? 作為程序員,選擇職業賽道是一項重要的決策,不僅影響你的職業發展,也影響著你的工作生活。本文將為你介紹如何選擇程序員職業賽道,以及每個方向的特點、挑戰和機遇,幫助你做出明智…

《極客時間 - 左耳聽風》【文章筆記 + 個人思考】

《極客時間 - 左耳聽風》 原文鏈接 :https://time.geekbang.org/column/intro/100002201?tabcatalog 備注:加粗部分為個人思考 01 | 程序員如何用技術變現?(上) 備注:加粗部分為個人思考) 01 | 程序員如何…

Window系統部署Splunk Enterprise并結合內網穿透實現遠程訪問本地服務

文章目錄 前言1. 搭建Splunk Enterprise2. windows 安裝 cpolar3. 創建Splunk Enterprise公網訪問地址4. 遠程訪問Splunk Enterprise服務5. 固定遠程地址 前言 本文主要介紹如何簡單幾步,結合cpolar內網穿透工具實現隨時隨地在任意瀏覽器,遠程訪問在本地…

【24最新版PythonPycharm安裝教程】小白保姆級別安裝教程

今天,我就來教大家一下,如何去安裝Python! 需要博主打包好的一鍵激活版Pycharm&&Python也可掃下方直接獲取 ? 1 了解Python Python是一種面向對象的解釋型計算機程序設計語言,由荷蘭人Guido van Rossum于1989年發明&…

[C++]使用純opencv去部署yolov9的onnx模型

【介紹】 部署 YOLOv9 ONNX 模型在 OpenCV 的 C 環境中涉及一系列步驟。以下是一個簡化的部署方案概述,以及相關的文案。 部署方案概述: 模型準備:首先,你需要確保你有 YOLOv9 的 ONNX 模型文件。這個文件包含了模型的結構和權…

Flutter Gradle下載失敗的解決方案

Flutter Gradle可能會由于網絡原因下載失敗,這個時候我們可以首先下載Gradle,然后再進行配置。具體步驟如下: 第一步:下載對應版本的gradle 可以通過下面地址下載,也可以百度里面搜對應的版本 【極速下載】gradle各版本快速下載地…