知识蒸馏—原理+代码实战(Distillation CNN 和 Progressive Distillation Diffusion)

news2024/9/21 12:35:41

文章目录

    • 1. Distillation 基本概念
    • 2. Distillation MNIST CNN分类代码实战
    • 3. Progressive Distillation Diffusion生成代码实战
        • 3.1 Progressive Distillation原理
        • 3.2 v-parameterization
        • 3.2 渐进蒸馏 cifar 代码实战

1. Distillation 基本概念

知识蒸馏被广泛的用于模型压缩迁移学习。开山之作应该是”Distilling the Knowledge in a Neural Network“。这篇文章中,作者的motivation是找到一种方法,把多个模型的知识提炼给单个模型。通常用一个已经训练好的Teacher Model A 去教另一个 Student Model B。通常 Model A 比 Model B更强,在Model A 的引导下,Model B可以比自学 学的更好。

做法:先训练一个teacher网络,然后使用这个teacher网络的输出数据的真实标签训练student网络。知识蒸馏,可以用来将网络从大网络转化成一个小网络,并保留接近于大网络的性能;也可以将多个网络的学到的知识转移到一个网络中,使得单个网络的性能接近emsemble的结果。

如对于如下的图像分类任务:

在这里插入图片描述

  • 传统训练:当没有 Teacher 网络时候,仅仅将 data 经过 Student 网络,在softmax之后,输出概率分布值 q,将 qlabel p 求 cross_entropy loss 就是称为 Hard loss,因为这个p是真实值的one-hot向量,我们希望q和p越接近越好。

  • 知识蒸馏:当有 Teacher 的帮助下的时候,loss来自 Student 和 Teacher 网络。且Teacher 输出的 q' 要经过带温度的Softmax之后(让它更加平滑,思想类似于label smooth)得到 q'' 再与 q 求loss,总loss = Teacher q'' 和 Student q 的 loss + Student q 和 label p 的 loss
    L = α ⋅ H a r d _ L o s s + ( 1 − α ) ⋅ S o f t _ L o s s = α ⋅ C E ( p , q ) + ( 1 − α ) ⋅ C E ( q ′ ′ , q ) L=\alpha\cdot Hard\_Loss+(1-\alpha)\cdot Soft\_Loss=\alpha\cdot CE(p,q) + (1-\alpha)\cdot CE(q'',q) L=αHard_Loss+(1α)Soft_Loss=αCE(p,q)+(1α)CE(q′′,q)在这里插入图片描述

  • SoftMax问题
    普通的Cross Entropy Loss是由NLL LossLogSoftmax组成的:

F.cross_entropy(p,target)) = F.nll_loss(torch.log(torch.softmax(p)), target)

这个 cross_entropy loss 中的 softmax 其实没有那么 soft,输出的概率分布,使得对于正确类别会有一个很高的置信度,而对于其他的类别的概率几乎为0。这样的话,teacher网络学到数据的相似信息(例如数字2和3,7很类似,这种soft label信息)很难传达给student网络。 因此,文章提出了带温度系数T的Softmax:Softmax-T
在这里插入图片描述
这里 q i q_i qi 是student网络学习的对象(soft targets), z i z_i zi 是神经网络softmax前的输出logit。如果将T取1,这个公式就是softmax,根据logit输出各个类别的概率。如果T接近于0,则最大的值会越近1,其它值会接近0,近似于onehot编码。如果T越大,则输出的结果的分布越平缓,相当于平滑的一个作用,起到保留相似信息的作用。如果T等于无穷,就是一个均匀分布。

对比Softmax(上) 和 Softmax-T(下) 对模型预测结果概率分布的可视化:
在这里插入图片描述

最终的蒸馏损失,就是将原本的CE Loss中Soft LossSoftmax换成Softmax_T,得到 KD Loss:
K D _ L o s s = α ⋅ H a r d _ L o s s + ( 1 − α ) ⋅ S o f t _ L o s s = α ⋅ C E ( p , q ) + ( 1 − α ) ⋅ C E ( q ′ ′ , q ) KD\_Loss=\alpha\cdot Hard\_Loss+(1-\alpha)\cdot Soft\_Loss=\alpha\cdot CE(p,q) + (1-\alpha)\cdot CE(q'',q) KD_Loss=αHard_Loss+(1α)Soft_Loss=αCE(p,q)+(1α)CE(q′′,q)
p是真实标签label,q是Student输出,q’'是Teacher输出。

def distillation_loss(y,labels,teacher_scores,temp,alpha):
	soft_loss = nn.KLDivLoss()(F.log_softmax(y/temp, dim=1), F.softmax(teacher_scores/temp,dim=1))
	hard_loss = F.cross_entropy(y,labels)
    return soft_loss *(temp*temp*2.0*alpha) + hard_loss *(1. - alpha)

2. Distillation MNIST CNN分类代码实战

  1. Import libs:
import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from tqdm import tqdm
import torchvision
from torchvision import transforms
  1. Define Teacher CNN Model(big) and Student CNN Model(small):
class TeacherModel(nn.Module):
    def __init__(self, in_channels=1, num_classes=10):
        super(TeacherModel, self).__init__()
        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(784, 1200)
        self.fc2 = nn.Linear(1200, 1200)
        self.fc3 = nn.Linear(1200, num_classes)
        self.dropout = nn.Dropout(p=0.5)

    def forward(self, x):
        x = x.view(-1, 784)
        x = self.relu(self.dropout(self.fc1(x)))
        x = self.relu(self.dropout(self.fc2(x)))
        x = self.fc3(x)
        return x

class StudentModel(nn.Module):
    def __init__(self, in_channels=1, num_classes=10):
        super(StudentModel, self).__init__()
        self.relu = nn.ReLU()
        self.fc1 = nn.Linear(784, 20)
        self.fc2 = nn.Linear(20, 20)
        self.fc3 = nn.Linear(20, num_classes)
        self.dropout = nn.Dropout(p=0.5)

    def forward(self, x):
        x = x.view(-1, 784)
        x = self.relu(self.dropout(self.fc1(x)))
        x = self.relu(self.dropout(self.fc2(x)))
        x = self.fc3(x)
        return x
  1. Function of Train Teacher Model:
def teacher(device, train_loader, test_loader):
    print('--------------teachermodel start--------------')
    model = TeacherModel()
    model = model.to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)

    epochs = 6
    for epoch in range(epochs):
        model.train()

        for data, target in tqdm(train_loader):
            data = data.to(device)
            target = target.to(device)
            preds = model(data)
            loss = criterion(preds, target)

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

        model.eval()
        num_correct = 0
        num_samples = 0

        with torch.no_grad():
            for x, y in test_loader:
                x = x.to(device)
                y = y.to(device)
                preds = model(x)
                predictions = preds.max(1).indices
                num_correct += (predictions.eq(y)).sum().item()
                num_samples += predictions.size(0)
            acc = num_correct / num_samples

        model.train()
        print('Epoch:{}\t Acc:{:.4f}'.format(epoch + 1, acc))
    torch.save(model, 'teacher.pkl')
    print('--------------teachermodel end--------------')
  1. Function of Train Stuent Model independently
def student(device, train_loader, test_loader):
    print('--------------studentmodel start--------------')

    model = StudentModel()
    model = model.to(device)

    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)

    epochs = 3
    for epoch in range(epochs):
        model.train()

        for data, target in tqdm(train_loader):
            data = data.to(device)
            target = target.to(device)
            preds = model(data)
            loss = criterion(preds, target)

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

        model.eval()
        num_correct = 0
        num_samples = 0

        with torch.no_grad():
            for x, y in test_loader:
                x = x.to(device)
                y = y.to(device)
                # print(y)
                preds = model(x)
                #             print(preds)
                predictions = preds.max(1).indices
                # print(predictions)
                num_correct += (predictions.eq(y)).sum().item()
                num_samples += predictions.size(0)
            acc = num_correct / num_samples

        model.train()
        print('Epoch:{}\t Acc:{:.4f}'.format(epoch + 1, acc))
    print('--------------studentmodel prediction end--------------')
  1. Function of Distilling Teacher Model to Student Model:(核心)
def kd(teachermodel, device, train_loader, test_loader):
    print('--------------kdmodel start--------------')

    teachermodel.eval()

    studentmodel = StudentModel()
    studentmodel = studentmodel.to(device)
    studentmodel.train()

    temp = 7    #蒸馏温度
    alpha = 0.3

    hard_loss = nn.CrossEntropyLoss()
    soft_loss = nn.KLDivLoss(reduction='batchmean')

    optimizer = torch.optim.Adam(studentmodel.parameters(), lr=1e-4)

    epochs = 20
    for epoch in range(epochs):
        for data, target in tqdm(train_loader):
            data = data.to(device)
            target = target.to(device)

            with torch.no_grad():
                teacher_preds = teachermodel(data)

            student_preds = studentmodel(data)
            student_loss = hard_loss(student_preds, target) #hard_loss

            distillation_loss = soft_loss(
                F.log_softmax(student_preds / temp, dim=1),
                F.softmax(teacher_preds / temp, dim=1)
            )   #soft_loss

            loss = alpha * student_loss + (1 - alpha) * distillation_loss
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

        studentmodel.eval()
        num_correct = 0
        num_samples = 0

        with torch.no_grad():
            for x, y in test_loader:
                x = x.to(device)
                y = y.to(device)
                preds = studentmodel(x)
                predictions = preds.max(1).indices
                num_correct += (predictions.eq(y)).sum().item()
                num_samples += predictions.size(0)
            acc = num_correct / num_samples

        studentmodel.train()
        print('Epoch:{}\t Acc:{:.4f}'.format(epoch + 1, acc))
    print('--------------kdmodel end--------------')
  1. Main function (load data, implement train function):
if __name__ == '__main__':
    torch.manual_seed(0)

    device = torch.device("cuda" if torch.cuda.is_available else "cpu")
    torch.backends.cudnn.benchmark = True
    #加载数据集
    X_train = torchvision.datasets.MNIST(
        root="dataset/",
        train=True,
        transform=transforms.ToTensor(),
        download=True
    )

    X_test = torchvision.datasets.MNIST(
        root="dataset/",
        train=False,
        transform=transforms.ToTensor(),
        download=True
    )

    train_loader = DataLoader(dataset=X_train, batch_size=32, shuffle=True)
    test_loader = DataLoader(dataset=X_test, batch_size=32, shuffle=False)

    #从头训练教师模型,并预测
    teacher(device, train_loader, test_loader)

   #从头训练学生模型,并预测
    student(device, train_loader, test_loader)

   #知识蒸馏训练学生模型
    model = torch.load('teacher.pkl')
    kd(model, device, train_loader, test_loader)

最终训练结果,对比Teacher MdeolStuent Model without DistillationStuent Model with Distillation的Accuracy:可以看出,①使用Teacher蒸馏训练出的Student,比独立训练的Student更强。②实际场景中,大部分情况下,student本身都是显著弱于teacher的,因此很难超越teacher的表现。

  • Teacher Mdeol:Epoch:3 Acc:0.9689,Epoch:6 Acc:0.9764
  • Stuent Model without Distillation:Epoch:3 Acc:0.8173
  • Stuent Model with Distillation:Epoch:3 Acc:0.8387,Epoch:20 Acc:0.9015

3. Progressive Distillation Diffusion生成代码实战

通过跨步蒸馏减少扩散模型采样步数的方法,主要内容包括:progressive distillationguided diffusion distillationstep distillationData-free DistillationLatent Consistency Models

本节主要讲解渐进式蒸馏 Progressive Distillation: 因为本文提出的 v-parameterization 在后续的Diffusion工作中被广泛的应用,来加快推理速度,如Imagen video, Imagen, Stable Diffusion, Dall E等。

3.1 Progressive Distillation原理

渐进式蒸馏的目标:是将一个步骤很多的Teacher Diffusion蒸馏为一个步骤较少的Student Diffusion,一般通过反复迭代的方式进行。每次迭代,Student企图1步学习Teacher模型2步的结果。每次迭代蒸馏后,Student需要的Sample步数都会比原来少一半,而当前的Student将会变成下一次的Teacher。

在这里插入图片描述
如上图所示,Teacher Diffusion f ( z , η ) f(z,\eta) f(z,η) 通过 4 个确定性步骤将随机噪声 ε 映射到样本 x,Student Diffusion f ( z , θ ) f(z,\theta) f(z,θ) 只需1步即可学习到这种映射关系。

渐进蒸馏方法

  1. 训练Teacher Diffusion:Teacher模型的训练使用标准Diffusion模型训练方法,它的训练Loss函数定义为Noise的 ε 空间中的均方误差
    在这里插入图片描述
    相关变量定义:
    在这里插入图片描述
    注意:除了通过直接预测 x 进行训练(x-parameterization),还可以通过分别预测 x 和 ε(ε-parameterization),再合并为:
    在这里插入图片描述
    ,或者通过预测 v(v-parameterization),再计算 :
    在这里插入图片描述

  2. 渐进蒸馏Student Diffusion:蒸馏前,用Teacher Diffusion的权重初始化Student Diffusion,且模型结构一样。渐进蒸馏Diffusion方法标准Diffusion模型训练方法 的主要区别在于如何确定去噪模型的 Label 值

    • 标准Diffusion训练中:Diffusion去噪的 Label 是DDIM每个step的预定义好的Noise
    • 渐进式蒸馏Diffusion中:Student Diffusion去噪模型需要预测的 Label 是Teacher模型预测的Noise。且Student Diffusion企图用1步的预测Noise 匹配 Teacher Diffusion 2步的预测Noise,即Student Diffusion在 ε 空间的Label是Teacher Diffusion 2步的预测Noise z t ′ ′ z_t^{''} zt′′(ε-parameterization)。再利用 z ˉ t ′ ′ = z t ′ ′ \bar z_t^{''} = z_t^{''} zˉt′′=zt′′还可以变换到 x 空间(x-parameterization):
      在这里插入图片描述
      在这里插入图片描述

总结:传统Diffusion训练渐进蒸馏Diffusion
在这里插入图片描述

3.2 v-parameterization

我们都知道常规的扩散模型都是通过噪声预测来进行去噪的,即 ε-parameterization-prediction,那么什么是速度预测 v-parameterization-prediction,为什么要用速度预测?

与常规的基于噪声预测的扩散模型不同,基于速度预测的扩散模型的输出是速度 v ^ θ \hat v_{\theta} v^θ ,相应的优化目标函数为:
在这里插入图片描述
其中 v 是速度真值,可以从真实样本 x 和噪声 ε 根据噪声级别计算得到:
在这里插入图片描述

由于在扩散模型蒸馏中,v-parameterization 模型往往比 ε-parameterization表现更好,一般将 ε-parameterization 微调为 v-parameterization

在这里插入图片描述

下面将介绍 x, v, z, ε 的关系(结合上图):
在这里插入图片描述
在这里插入图片描述
总结三种 parameterization:
在这里插入图片描述

3.2 渐进蒸馏 cifar 代码实战

参考Colab:diffusion_distillation.ipynb

  1. Download codes and libs, and import libs:
!apt-get -qq install subversion
!svn checkout https://github.com/google-research/google-research/trunk/diffusion_distillation
!pip install -r diffusion_distillation/diffusion_distillation/requirements.txt --quiet

import os
import time
import requests
import functools
import jax
from jax import config
import jax.numpy as jnp
import flax
from matplotlib import pyplot as plt
import numpy as onp
import tensorflow.compat.v2 as tf
tf.enable_v2_behavior()
from diffusion_distillation import diffusion_distillation
  1. configure JAX to use the TPU: JAX是谷歌开源的、可以在CPU、GPU和TPU上运行的numpy,是针对机器学习研究的高性能自微分计算加速框架。
if 'TPU_DRIVER_MODE' not in globals():
  url = 'http://' + os.environ['COLAB_TPU_ADDR'].split(':')[0] + ':8475/requestversion/tpu_driver_nightly'
  resp = requests.post(url)
  time.sleep(5)
  TPU_DRIVER_MODE = 1
config.FLAGS.jax_xla_backend = "tpu_driver"
config.FLAGS.jax_backend_target = "grpc://" + os.environ['COLAB_TPU_ADDR']
print(config.FLAGS.jax_backend_target)
  1. Train a new diffusion model:
# create model
config = diffusion_distillation.config.cifar_base.get_config()
model = diffusion_distillation.model.Model(config)

# init params
state = jax.device_get(model.make_init_state())
state = flax.jax_utils.replicate(state)

# JIT compile training step
train_step = functools.partial(model.step_fn, jax.random.PRNGKey(0), True)
train_step = functools.partial(jax.lax.scan, train_step)  # for substeps
train_step = jax.pmap(train_step, axis_name='batch', donate_argnums=(0,))

# build input pipeline
total_bs = config.train.batch_size
device_bs = total_bs // jax.device_count()
train_ds = model.dataset.get_shuffled_repeated_dataset(
    split='train',
    batch_shape=(
        jax.local_device_count(),  # for pmap
        config.train.substeps,  # for lax.scan over multiple substeps
        device_bs,  # batch size per device
    ),
    local_rng=jax.random.PRNGKey(0),
    augment=True)
train_iter = diffusion_distillation.utils.numpy_iter(train_ds)

# run training
for step in range(10):
  batch = next(train_iter)
  state, metrics = train_step(state, batch)
  metrics = jax.device_get(flax.jax_utils.unreplicate(metrics))
  metrics = jax.tree_map(lambda x: float(x.mean(axis=0)), metrics)
  print(metrics)
  1. Distill a trained diffusion model:(核心)
# create model
config = diffusion_distillation.config.cifar_distill.get_config()
model = diffusion_distillation.model.Model(config)

# load the teacher params
model.load_teacher_state(config.distillation.teacher_checkpoint_path)

# init student state
init_params = diffusion_distillation.utils.copy_pytree(model.teacher_state.ema_params)
optim = model.make_optimizer_def().create(init_params)
state = diffusion_distillation.model.TrainState(
    step=model.teacher_state.step,
    optimizer=optim,
    ema_params=diffusion_distillation.utils.copy_pytree(init_params),
    num_sample_steps=model.teacher_state.num_sample_steps//2)
# build input pipeline
total_bs = config.train.batch_size
device_bs = total_bs // jax.device_count()
train_ds = model.dataset.get_shuffled_repeated_dataset(
    split='train',
    batch_shape=(
        jax.local_device_count(),  # for pmap
        config.train.substeps,  # for lax.scan over multiple substeps
        device_bs,  # batch size per device
    ),
    local_rng=jax.random.PRNGKey(0),
    augment=True)
train_iter = diffusion_distillation.utils.numpy_iter(train_ds)

steps_per_distill_iter = 10  # number of distillation steps per iteration of progressive distillation
end_num_steps = 4  # eventual number of sampling steps we want to use
while state.num_sample_steps >= end_num_steps:

  # compile training step
  train_step = functools.partial(model.step_fn, jax.random.PRNGKey(0), True)
  train_step = functools.partial(jax.lax.scan, train_step)  # for substeps
  train_step = jax.pmap(train_step, axis_name='batch', donate_argnums=(0,))

  # train the student against the teacher model
  print('distilling teacher using %d sampling steps into student using %d steps'
        % (model.teacher_state.num_sample_steps, state.num_sample_steps))
  state = flax.jax_utils.replicate(state)
  for step in range(steps_per_distill_iter):
    batch = next(train_iter)
    state, metrics = train_step(state, batch)
    metrics = jax.device_get(flax.jax_utils.unreplicate(metrics))
    metrics = jax.tree_map(lambda x: float(x.mean(axis=0)), metrics)
    print(metrics)

  # student becomes new teacher for next distillation iteration
  model.teacher_state = jax.device_get(
      flax.jax_utils.unreplicate(state).replace(optimizer=None))

  # reset student optimizer for next distillation iteration
  init_params = diffusion_distillation.utils.copy_pytree(model.teacher_state.ema_params)
  optim = model.make_optimizer_def().create(init_params)
  state = diffusion_distillation.model.TrainState(
      step=model.teacher_state.step,
      optimizer=optim,
      ema_params=diffusion_distillation.utils.copy_pytree(init_params),
      num_sample_steps=model.teacher_state.num_sample_steps//2)
  1. Load a distilled model checkpoint and sample from it:
# list all available distilled checkpoints
!gsutil ls gs://gresearch/diffusion-distillation

# create imagenet model
config = diffusion_distillation.config.imagenet64_base.get_config()
model = diffusion_distillation.model.Model(config)

# load distilled checkpoint for 8 sampling steps
loaded_params = diffusion_distillation.checkpoints.restore_from_path('gs://gresearch/diffusion-distillation/imagenet_8', target=None)['ema_params']

# fix possible flax version errors
ema_params = jax.device_get(model.make_init_state()).ema_params
loaded_params = flax.core.unfreeze(loaded_params)
loaded_params = jax.tree_map(
    lambda x, y: onp.reshape(x, y.shape) if hasattr(y, 'shape') else x,
    loaded_params,
    flax.core.unfreeze(ema_params))
loaded_params = flax.core.freeze(loaded_params)
del ema_params

# sample from the model
imagenet_classes = {'malamute': 249, 'siamese': 284, 'great_white': 2,
                    'speedboat': 814, 'reef': 973, 'sports_car': 817,
                    'race_car': 751, 'model_t': 661, 'truck': 867}
labels = imagenet_classes['truck'] * jnp.ones((4,), dtype=jnp.int32)
samples = model.samples_fn(rng=jax.random.PRNGKey(0), labels=labels, params=loaded_params, num_steps=8)
samples = jax.device_get(samples).astype(onp.uint8)

# visualize samples
padded_samples = onp.pad(samples, ((0,0), (1,1), (1,1), (0,0)), mode='constant', constant_values=255)
nrows = int(onp.sqrt(padded_samples.shape[0]))
ncols = padded_samples.shape[0]//nrows
_, height, width, channels = padded_samples.shape
img_grid = padded_samples.reshape(nrows, ncols, height, width, channels).swapaxes(1,2).reshape(height*nrows, width*ncols, channels)
img = plt.imshow(img_grid)
plt.axis('off')

(-0.5, 131.5, 131.5, -0.5)
在这里插入图片描述

可以看出蒸馏过的Diffusion相较于原始的Diffusion,可以在更少的step下得到不错的生成质量FID。(DDIM 采样器 vs 优化的stochastic随机采样器 vs 蒸馏
在这里插入图片描述

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

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

相关文章

测试用例设计全网最强篇(建议收藏)

本篇从多角度带大家从0开始学习怎么写测试用例,七种方法8个案例(含用例模板);学习目标:测试用例的基本知识以及黑盒测试用例的设计方法。 前言:总体编写策略: 对于测试用例编写来说&#xff0…

C语言——一个数如果恰好等于它的因子之和,这个数就称为“完全数”。

一个数如果恰好等于它的因子之和,这个数就称为“完全数”。例如,6的因子是 1、2、3,而6123。因此6是一个完全数。编程找出 1000 之内的所有完全数。 #include <stdio.h> int main() {int i, j, sum;for (i 1; i < 1000; i) {sum 0; //这一步很重要&#xff0c;每…

SQL注入 - CTF常见题型

文章目录 题型一 &#xff08; 字符型注入 &#xff09;题型二 &#xff08; 整数型注入 &#xff09;题型三 &#xff08; 信息收集SQL注入&#xff09;题型四 &#xff08; 万能密码登录 &#xff09;题型五 &#xff08; 搜索型注入文件读写 &#xff09;题型六 &#xff08…

Softing VisualODX 助力OEM诊断数据开发

ODX 2.2是由ASAM&#xff08;自动化及测量系统标准协会&#xff09;提出的诊断标准&#xff0c;是一种基于XML语言的开放式诊断数据格式&#xff0c;已在国际上得到广泛使用。目前&#xff0c;ODX诊断标准已被国内各大OEM采用&#xff0c;但在ODX数据开发阶段&#xff0c;ODX诊…

Java高级技术(单元测试)

一&#xff0c;概括 二&#xff0c;junit 三&#xff0c;案例 &#xff08;1&#xff09;&#xff0c;实验类 package com.bilibili;public class Name {public static void main(String name) {if (name null){System.out.println("0");return;}System.out.print…

C#文件流FileStream类

目录 一、文件流类 1.FileStream类的常用属性 2.FileStream类的常用方法 3.使用FileStream类操作文件 二、文本文件的写入与读取 1.StreamWriter类 2.StreamReader类 3.示例及源码 三、二进制文件的写入与读取 1.BinaryWriter类 2.BinaryReader类 3.示例源码 数据流…

锂电涂布机设备健康管理:降低运维成本的关键

随着锂电池行业的快速发展&#xff0c;锂电涂布机设备作为关键生产工艺装备&#xff0c;扮演着至关重要的角色。然而&#xff0c;涂布机设备的故障和维护成本对于企业来说是一个不可忽视的挑战。本文将介绍做好锂电涂布机设备的健康管理&#xff0c;降低运维成本的关键措施。 锂…

JAVA的一些便捷性方法(Object)

在IDEA中&#xff0c;如何查看JDK的源码&#xff1f; CTRL B; 常用方法&#xff1a; 1.equals&#xff08;&#xff09; booleanequals(Object obj) 指示其他某个对象是否与此对象“相等”。 与 的比较&#xff1a; &#xff0c;即可判断基本类型&#xff0c;也…

为什么说橘子酒是像红葡萄酒的白葡萄酒?

酿酒师通常会使用不同的葡萄品种来酿橘子酒&#xff0c;这样的葡萄酒味道就会有很大的不同&#xff0c;因此很难为橘子酒定义一个通用的风味特征。其中一些有干花或香草的香味&#xff0c;又或者是核果风味&#xff0c;如杏、油桃或桃子&#xff0c;再或者你可能感觉到坚果和蜂…

Python分享之字符串格式化 (%操作符)

在许多编程语言中都包含有格式化字符串的功能&#xff0c;比如C和Fortran语言中的格式化输入输出。Python中内置有对字符串进行格式化的操作%。 模板 格式化字符串时&#xff0c;Python使用一个字符串作为模板。模板中有格式符&#xff0c;这些格式符为真实值预留位置&#xff…

这个校园门禁考勤技术,还怪高级的咧!

随着科技的不断发展&#xff0c;人脸识别技术在各个领域的应用逐渐成为一种趋势。在企业管理中&#xff0c;人脸考勤系统的引入为传统的考勤方式带来了革命性的变革。 传统的考勤方式存在诸多弊端&#xff0c;例如卡片刷卡易被冒用、指纹考勤不够灵活等问题。而基于三维人脸识别…

【精】A data-driven dynamic repositioning model in bicycle-sharing systems

A data-driven dynamic repositioning model in bicycle-sharing systems 爱思唯尔原文 doi:https://doi.org/10.1016/j.ijpe.2020.107909 article{data2021BRP, address {Univ Cambridge, Inst Mfg, Cambridge CB3 0FS, England}, author {Zhang, Jie and Meng, Meng and W…

Linux Nmap命令解析(Nmap指令)(功能:主机发现、ping扫描、arp扫描、端口扫描、服务版本检测、操作系统识别等)

文章目录 Linux Nmap 命令解析简介Nmap 的核心功能主机发现端口扫描服务版本检测OS 指纹识别&#xff08;操作系统指纹识别&#xff09;脚本扫描 安装 NmapNmap 命令结构Nmap 命令文档英文中文 主机发现Ping 扫描ARP 扫描关于nmap -PR&#xff08;ARP Ping Scan&#xff09;和n…

华为鸿蒙开发(HarmonyOs开发):超详细的:DevEco Studio 的安装和配置 、华为第三方包依赖:SDK软件包的安装、Nodejs的导入配置

2023年11月28日20:00:00 ⚠️⚠️HarmonyOs 开发工具 ⚠️⚠️ ⚠️⚠️DevEco Studio 的安装和配置⚠️⚠️ 文章目录 一、打开鸿蒙开发工具官网二、下载 DevEco Studio三、配置 DevEco Studio四、错误处理 ⚠️⚠️⚠️❤️❤️ 关注了解更多 一、打开鸿蒙开发工具官网 下面…

OSG编程指南<十三>:OSG渲染状态

1、前言 在 OSG 中存在两棵树&#xff0c;即场景树和渲染树。渲染树是一棵以 StateSet 和 RenderLeaf 为节点的树&#xff0c;它可以做到 StateSet 相同的 RenderLeaf 同时渲染而不用切换 OpenGL状态&#xff0c;并且做到尽量少但在多个不同 State 间切换。渲染树在 CullVisito…

基于SSM实现的叮当书城

一、系统架构 前端&#xff1a;jsp | jquery | layui 后端&#xff1a;spring | springmvc | mybatis 环境&#xff1a;jdk1.7以上 | mysql | maven 二、代码与数据库 三、功能介绍 01. 系统首页 02. 商品分类 03. 热销 04. 新品 05. 注册 06. 登录 07. 购物车 08. 后台-首页 …

stm32 TIM

一、TIM简介 TIM&#xff08;Timer&#xff09;定时器定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断。16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时定时器不仅具备基本的定时中断功能&…

什么软件能去水印?分享三款实用去水印工具

什么软件能去水印&#xff1f;去水印你还在担心会损伤画质或处理不干净&#xff1f;今天分享三款好用的图片去水印工具&#xff0c;手机和电脑软件都有&#xff0c;操作简单&#xff0c;去水印速度快&#xff0c;而且去水印后几乎看不水印痕迹&#xff01; 1、水印云 一款图片编…

一天之内“三个离职群都满了”;飞行出租车的时代就此开启?丨 RTE 开发者日报 Vol.94

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

小功能实现(十九)生成shp文件

引入依赖 <!--shp文件相关工具--><dependency><groupId>org.geotools</groupId><artifactId>gt-shapefile</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geo…