【解读Spikingjelly】使用单层全连接SNN识别MNIST

news2024/11/25 2:51:58

原文档:使用单层全连接SNN识别MNIST — spikingjelly alpha 文档

代码地址:完整的代码位于activation_based.examples.lif_fc_mnist.py

GitHub - fangwei123456/spikingjelly: SpikingJelly is an open-source deep learning framework for Spiking Neural Network (SNN) based on PyTorch.

ZhengyuanGao/spikingjelly: 开源脉冲神经网络深度学习框架 - spikingjelly - OpenI - 启智AI开源社区提供普惠算力! (pcl.ac.cn)a

本文补充一些细节代码以解决运行报错问题,并提供可视化代码,解释核心代码作用以辅助SNN初学者快速入门!

目录

1.网络定义

2.主函数

2.1参数设置

2.2主循环

 3.可视化

 3.1准确率

 3.2测试图片与发放脉冲

4.完整代码 

lif_fc_mnist.py(为提高运行速度,迭代次数设置为1)

lif_fc_mnist_test.py


1.网络定义

class SNN(nn.Module):
    def __init__(self, tau):
        super().__init__()

        self.layer = nn.Sequential(
            layer.Flatten(),
            layer.Linear(28 * 28, 10, bias=False),
            neuron.LIFNode(tau=tau, surrogate_function=surrogate.ATan()),
            )

    def forward(self, x: torch.Tensor):
        return self.layer(x)

(1)super:继承父类torch.nn.Module的初始化方法

(2)Sequential:顺序方式连接网络结构,首先将输入展平为一维,定义全连接层,输入格式28*28,输出10个神经元。Neuron.LIFNode将全连接层神经元替换为脉冲神经元,并指定膜时间常数与替代函数(解决不可导问题)

(3)forward:重写前向传播函数,返回网络输出结果

2.主函数

2.1参数设置

(1)使用命令行设置LIF神经网络的超参数

parser = argparse.ArgumentParser(description='LIF MNIST Training')
    parser.add_argument('-T', default=100, type=int, help='simulating time-steps')
    parser.add_argument('-device', default='cuda:0', help='device')
    parser.add_argument('-b', default=64, type=int, help='batch size')
    parser.add_argument('-epochs', default=100, type=int, metavar='N',
                        help='number of total epochs to run')
    parser.add_argument('-j', default=4, type=int, metavar='N',
                        help='number of data loading workers (default: 4)')
# 添加 default='./MNIST' 以解决无下载所需文件夹问题----------------------------------------
    parser.add_argument('-data-dir', type=str, default='./MNIST', help='root dir of MNIST dataset')
# -----------------------------------------------------------------------------------------
    parser.add_argument('-out-dir', type=str, default='./logs', help='root dir for saving logs and checkpoint')
    parser.add_argument('-resume', type =str, help='resume from the checkpoint path')
    parser.add_argument('-amp', action='store_true', help='automatic mixed precision training')
    parser.add_argument('-opt', type=str, choices=['sgd', 'adam'], default='adam', help='use which optimizer. SGD or Adam')
    parser.add_argument('-momentum', default=0.9, type=float, help='momentum for SGD')
    parser.add_argument('-lr', default=1e-3, type=float, help='learning rate')
    parser.add_argument('-tau', default=2.0, type=float, help='parameter tau of LIF neuron')

注:在代码上述标记位置添加  default='./MNIST' 以解决无下载所需文件夹问题

超参数含义如下图所示:

(2) 参数代入:是否自动混合精度训练(PyTorch的自动混合精度(AMP) - 知乎 (zhihu.com))

scaler = None
    if args.amp:
        scaler = amp.GradScaler()

(3)参数代入:优化器类型

optimizer = None
    if args.opt == 'sgd':
        optimizer = torch.optim.SGD(net.parameters(), lr=args.lr, momentum=args.momentum)
    elif args.opt == 'adam':
        optimizer = torch.optim.Adam(net.parameters(), lr=args.lr)
    else:
        raise NotImplementedError(args.opt)

 (4)是否恢复断点训练(if args.resume:从断点处开始继续训练模型)

    if args.resume:
        checkpoint = torch.load(args.resume, map_location='cpu')
        net.load_state_dict(checkpoint['net'])
        optimizer.load_state_dict(checkpoint['optimizer'])
        start_epoch = checkpoint['epoch'] + 1
        max_test_acc = checkpoint['max_test_acc']

 (5)泊松编码

encoder = encoding.PoissonEncoder()

2.2主循环

(1)在主循环之前补充创建两个空数组,用于保存训练过程中的准确率,以便后续绘制曲线

 (2)加载训练数据(测试数据代码大同小异,不另外分析)

        for img, label in train_data_loader:
            optimizer.zero_grad()
            img = img.to(args.device)
            label = label.to(args.device)
            label_onehot = F.one_hot(label, 10).float()
  1. 循环读取训练数据,在每次循环前,清空优化器梯度
  2. 将img、label放置到GPU上训练
  3. 对标签进行独热编码,10个类别(独热编码(One-Hot Encoding) - 知乎 (zhihu.com))

(3)判断是否使用混合精度训练

            if scaler is not None:
                with amp.autocast():
                    out_fr = 0.
                    for t in range(args.T):
                        encoded_img = encoder(img)
                        out_fr += net(encoded_img)
                    out_fr = out_fr / args.T
                    loss = F.mse_loss(out_fr, label_onehot)
                scaler.scale(loss).backward()
                scaler.step(optimizer)
                scaler.update()
            else:
                out_fr = 0.
                for t in range(args.T):
                    encoded_img = encoder(img)
                    out_fr += net(encoded_img)
                out_fr = out_fr / args.T
                loss = F.mse_loss(out_fr, label_onehot)
                loss.backward()
                optimizer.step()

如果使用:
   - 用amp.autocast()包裹前向计算,使其在浮点16位计算
   - 用scaler缩放损失scale(loss)
   - 损失回传
   - 通过scaler更新优化器

如果不使用混合精度:
   - 正常进行前向计算
   - 损失函数计算
   - 反向传播
   - 优化器更新

(4)重置网络

functional.reset_net(net)

SNN中的脉冲神经元在前向传播时会积累状态,比如膜电位、释放的脉冲等。重置可以清空这些状态,使网络回到初始状态。

(5)在下图位置添加对应代码保存.npy文件

 3.可视化

 3.1准确率

 在examples文件夹下创建一个.py文件,用于对结果的可视化

 代码如下:

import numpy as np
import matplotlib.pyplot as plt

test_accs = np.load("./train_accs.npy")
x = []
y = []
maxy = -1
maxx = -1
for t in range(len(test_accs)):
    if test_accs[t] > maxy:
        maxy = test_accs[t]
        maxx = t
    x.append(t)
    y.append(test_accs[t])
plt.plot(x, y)
# plt.plot(test_accs)
plt.xlabel('Iteration')
plt.ylabel('Acc')
plt.title('Train Acc')
plt.annotate(r'(%d,%f)' % (maxx, maxy), xy=(maxx, maxy), xycoords='data', xytext=(+10, +20), fontsize=16,
             arrowprops=dict(arrowstyle='->'), textcoords='offset points')
plt.show()
test_accs = np.load("./test_accs.npy")
x = []
y = []
maxy = -1
maxx = -1
for t in range(len(test_accs)):
    if test_accs[t] > maxy:
        maxy = test_accs[t]
        maxx = t
    x.append(t)
    y.append(test_accs[t])
# plt.plot(x, y)
plt.plot(test_accs)
plt.xlabel('Epoch')
plt.ylabel('Acc')
plt.title('Test Acc')
plt.annotate(r'(%d,%f)' % (maxx, maxy), xy=(maxx, maxy), xycoords='data', xytext=(+10, +20), fontsize=16,
             arrowprops=dict(arrowstyle='->'), textcoords='offset points')
plt.show()

效果:

 

 3.2测试图片与发放脉冲

 添加如下代码至main()函数的末尾:

 img = img.cpu().numpy().reshape(28, 28)
        plt.subplot(221)
        plt.imshow(img)
        plt.subplot(222)
        plt.imshow(img, cmap='gray')
        plt.subplot(223)
        plt.imshow(img, cmap=plt.cm.gray)
        plt.subplot(224)
        plt.imshow(img, cmap=plt.cm.gray_r)
        plt.show()

效果: 

4.完整代码 

lif_fc_mnist.py(为减少运行耗时,迭代次数设置为1)

import os
import time
import argparse
import sys
import datetime

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as data
from torch.cuda import amp
from torch.utils.tensorboard import SummaryWriter
import torchvision
import numpy as np
import matplotlib.pyplot as plt

from spikingjelly.activation_based import neuron, encoding, functional, surrogate, layer


class SNN(nn.Module):
    def __init__(self, tau):
        super().__init__()

        self.layer = nn.Sequential(
            layer.Flatten(),
            layer.Linear(28 * 28, 10, bias=False),
            neuron.LIFNode(tau=tau, surrogate_function=surrogate.ATan()),
            )

    def forward(self, x: torch.Tensor):
        return self.layer(x)

def main():
    '''
    :return: None

    * :ref:`API in English <lif_fc_mnist.main-en>`

    .. _lif_fc_mnist.main-cn:

    使用全连接-LIF的网络结构,进行MNIST识别。\n
    这个函数会初始化网络进行训练,并显示训练过程中在测试集的正确率。

    * :ref:`中文API <lif_fc_mnist.main-cn>`

    .. _lif_fc_mnist.main-en:

    The network with FC-LIF structure for classifying MNIST.\n
    This function initials the network, starts trainingand shows accuracy on test dataset.
    '''
    parser = argparse.ArgumentParser(description='LIF MNIST Training')
    parser.add_argument('-T', default=100, type=int, help='simulating time-steps')
    parser.add_argument('-device', default='cuda:0', help='device')
    parser.add_argument('-b', default=64, type=int, help='batch size')
    # 100
    parser.add_argument('-epochs', default=1, type=int, metavar='N',
                        help='number of total epochs to run')
    parser.add_argument('-j', default=4, type=int, metavar='N',
                        help='number of data loading workers (default: 4)')
    parser.add_argument('-data-dir', type=str, default='./MNIST', help='root dir of MNIST dataset')
    parser.add_argument('-out-dir', type=str, default='./logs', help='root dir for saving logs and checkpoint')
    parser.add_argument('-resume', type =str, help='resume from the checkpoint path')
    parser.add_argument('-amp', action='store_true', help='automatic mixed precision training')
    parser.add_argument('-opt', type=str, choices=['sgd', 'adam'], default='adam', help='use which optimizer. SGD or Adam')
    parser.add_argument('-momentum', default=0.9, type=float, help='momentum for SGD')
    parser.add_argument('-lr', default=1e-3, type=float, help='learning rate')
    parser.add_argument('-tau', default=2.0, type=float, help='parameter tau of LIF neuron')

    args = parser.parse_args()
    print(args)
    net = SNN(tau=args.tau)
    print(net)
    net.to(args.device)

    # 初始化数据加载器
    train_dataset = torchvision.datasets.MNIST(
        root=args.data_dir,
        train=True,
        transform=torchvision.transforms.ToTensor(),
        download=True
    )
    test_dataset = torchvision.datasets.MNIST(
        root=args.data_dir,
        train=False,
        transform=torchvision.transforms.ToTensor(),
        download=True
    )

    train_data_loader = data.DataLoader(
        dataset=train_dataset,
        batch_size=args.b,
        shuffle=True,
        drop_last=True,
        num_workers=args.j,
        pin_memory=True
    )
    test_data_loader = data.DataLoader(
        dataset=test_dataset,
        batch_size=args.b,
        shuffle=False,
        drop_last=False,
        num_workers=args.j,
        pin_memory=True
    )

    scaler = None
    if args.amp:
        scaler = amp.GradScaler()

    start_epoch = 0
    max_test_acc = -1

    optimizer = None
    if args.opt == 'sgd':
        optimizer = torch.optim.SGD(net.parameters(), lr=args.lr, momentum=args.momentum)
    elif args.opt == 'adam':
        optimizer = torch.optim.Adam(net.parameters(), lr=args.lr)
    else:
        raise NotImplementedError(args.opt)

    if args.resume:
        checkpoint = torch.load(args.resume, map_location='cpu')
        net.load_state_dict(checkpoint['net'])
        optimizer.load_state_dict(checkpoint['optimizer'])
        start_epoch = checkpoint['epoch'] + 1
        max_test_acc = checkpoint['max_test_acc']
    
    out_dir = os.path.join(args.out_dir, f'T{args.T}_b{args.b}_{args.opt}_lr{args.lr}')

    if args.amp:
        out_dir += '_amp'

    if not os.path.exists(out_dir):
        os.makedirs(out_dir)
        print(f'Mkdir {out_dir}.')

    with open(os.path.join(out_dir, 'args.txt'), 'w', encoding='utf-8') as args_txt:
        args_txt.write(str(args))

    writer = SummaryWriter(out_dir, purge_step=start_epoch)
    with open(os.path.join(out_dir, 'args.txt'), 'w', encoding='utf-8') as args_txt:
        args_txt.write(str(args))
        args_txt.write('\n')
        args_txt.write(' '.join(sys.argv))

    encoder = encoding.PoissonEncoder()

    # 创建保存数组
    train_accs = []
    test_accs = []

    for epoch in range(start_epoch, args.epochs):
        start_time = time.time()
        net.train()
        train_loss = 0
        train_acc = 0
        train_samples = 0
        for img, label in train_data_loader:
            optimizer.zero_grad()
            img = img.to(args.device)
            label = label.to(args.device)
            label_onehot = F.one_hot(label, 10).float()

            if scaler is not None:
                with amp.autocast():
                    out_fr = 0.
                    for t in range(args.T):
                        encoded_img = encoder(img)
                        out_fr += net(encoded_img)
                    out_fr = out_fr / args.T
                    loss = F.mse_loss(out_fr, label_onehot)
                scaler.scale(loss).backward()
                scaler.step(optimizer)
                scaler.update()
            else:
                out_fr = 0.
                for t in range(args.T):
                    encoded_img = encoder(img)
                    out_fr += net(encoded_img)
                out_fr = out_fr / args.T
                loss = F.mse_loss(out_fr, label_onehot)
                loss.backward()
                optimizer.step()

            train_samples += label.numel()
            train_loss += loss.item() * label.numel()
            train_acc += (out_fr.argmax(1) == label).float().sum().item()

            functional.reset_net(net)

        train_time = time.time()
        train_speed = train_samples / (train_time - start_time)
        train_loss /= train_samples
        train_acc /= train_samples

        writer.add_scalar('train_loss', train_loss, epoch)
        writer.add_scalar('train_acc', train_acc, epoch)

        net.eval()
        test_loss = 0
        test_acc = 0
        test_samples = 0
        with torch.no_grad():
            for img, label in test_data_loader:
                img = img.to(args.device)
                label = label.to(args.device)
                label_onehot = F.one_hot(label, 10).float()
                out_fr = 0.
                for t in range(args.T):
                    encoded_img = encoder(img)
                    out_fr += net(encoded_img)
                out_fr = out_fr / args.T
                loss = F.mse_loss(out_fr, label_onehot)

                test_samples += label.numel()
                test_loss += loss.item() * label.numel()
                test_acc += (out_fr.argmax(1) == label).float().sum().item()
                functional.reset_net(net)
        test_time = time.time()
        test_speed = test_samples / (test_time - train_time)
        test_loss /= test_samples
        test_acc /= test_samples
        writer.add_scalar('test_loss', test_loss, epoch)
        writer.add_scalar('test_acc', test_acc, epoch)

        save_max = False
        if test_acc > max_test_acc:
            max_test_acc = test_acc
            save_max = True

        checkpoint = {
            'net': net.state_dict(),
            'optimizer': optimizer.state_dict(),
            'epoch': epoch,
            'max_test_acc': max_test_acc
        }

        if save_max:
            torch.save(checkpoint, os.path.join(out_dir, 'checkpoint_max.pth'))

        torch.save(checkpoint, os.path.join(out_dir, 'checkpoint_latest.pth'))

        print(args)
        print(out_dir)
        print(f'epoch ={epoch}, train_loss ={train_loss: .4f}, train_acc ={train_acc: .4f}, test_loss ={test_loss: .4f}, test_acc ={test_acc: .4f}, max_test_acc ={max_test_acc: .4f}')
        print(f'train speed ={train_speed: .4f} images/s, test speed ={test_speed: .4f} images/s')
        print(f'escape time = {(datetime.datetime.now() + datetime.timedelta(seconds=(time.time() - start_time) * (args.epochs - epoch))).strftime("%Y-%m-%d %H:%M:%S")}\n')
    #     保存数据至数组
        train_accs = np.append(train_accs, train_acc)
        test_accs = np.append(test_accs, test_acc)
        # print(train_accs)

    # 写入npy
    np.save("./test_accs.npy", test_accs)
    np.save("./train_accs.npy", train_accs)

    # 保存绘图用数据
    net.eval()
    # 注册钩子
    output_layer = net.layer[-1] # 输出层
    output_layer.v_seq = []
    output_layer.s_seq = []
    def save_hook(m, x, y):
        m.v_seq.append(m.v.unsqueeze(0))
        m.s_seq.append(y.unsqueeze(0))

    output_layer.register_forward_hook(save_hook)


    with torch.no_grad():
        img, label = test_dataset[0]
        img = img.to(args.device)
        out_fr = 0.
        for t in range(args.T):
            encoded_img = encoder(img)
            out_fr += net(encoded_img)
        out_spikes_counter_frequency = (out_fr / args.T).cpu().numpy()
        print(f'Firing rate: {out_spikes_counter_frequency}')

        output_layer.v_seq = torch.cat(output_layer.v_seq)
        output_layer.s_seq = torch.cat(output_layer.s_seq)
        v_t_array = output_layer.v_seq.cpu().numpy().squeeze()  # v_t_array[i][j]表示神经元i在j时刻的电压值
        np.save("v_t_array.npy",v_t_array)
        s_t_array = output_layer.s_seq.cpu().numpy().squeeze()  # s_t_array[i][j]表示神经元i在j时刻释放的脉冲,为0或1
        np.save("s_t_array.npy",s_t_array)

        img = img.cpu().numpy().reshape(28, 28)
        plt.subplot(221)
        plt.imshow(img)
        plt.subplot(222)
        plt.imshow(img, cmap='gray')
        plt.subplot(223)
        plt.imshow(img, cmap=plt.cm.gray)
        plt.subplot(224)
        plt.imshow(img, cmap=plt.cm.gray_r)
        plt.show()



if __name__ == '__main__':
    main()

lif_fc_mnist_test.py

import numpy as np
import matplotlib.pyplot as plt

test_accs = np.load("./train_accs.npy")
x = []
y = []
maxy = -1
maxx = -1
for t in range(len(test_accs)):
    if test_accs[t] > maxy:
        maxy = test_accs[t]
        maxx = t
    x.append(t)
    y.append(test_accs[t])
plt.plot(x, y)
# plt.plot(test_accs)
plt.xlabel('Iteration')
plt.ylabel('Acc')
plt.title('Train Acc')
plt.annotate(r'(%d,%f)' % (maxx, maxy), xy=(maxx, maxy), xycoords='data', xytext=(+10, +20), fontsize=16,
             arrowprops=dict(arrowstyle='->'), textcoords='offset points')
plt.show()
test_accs = np.load("./test_accs.npy")
x = []
y = []
maxy = -1
maxx = -1
for t in range(len(test_accs)):
    if test_accs[t] > maxy:
        maxy = test_accs[t]
        maxx = t
    x.append(t)
    y.append(test_accs[t])
# plt.plot(x, y)
plt.plot(test_accs)
plt.xlabel('Epoch')
plt.ylabel('Acc')
plt.title('Test Acc')
plt.annotate(r'(%d,%f)' % (maxx, maxy), xy=(maxx, maxy), xycoords='data', xytext=(+10, +20), fontsize=16,
             arrowprops=dict(arrowstyle='->'), textcoords='offset points')
plt.show()


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

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

相关文章

解决WSL2的docker删除镜像后,磁盘空间不释放问题

1、问题原因 由于WSL2本质上是虚拟机&#xff0c;所以 Windows 会自动创建 vhdx 后缀的虚拟磁盘文件作为存储。这个 vhdx 后缀的虚拟磁盘文件特点是可以自动扩容&#xff0c;但是一般不会自动缩容。一旦有很多文件把它“撑大”&#xff0c;即使把这些文件删除它也不会自动“缩…

go内存管理机制

golang内存管理基本是参考tcmalloc来进行的。go内存管理本质上是一个内存池&#xff0c;只不过内部做了很多优化&#xff1a;自动伸缩内存池大小&#xff0c;合理切割内存块。 基本概念&#xff1a; Page&#xff1a;页&#xff0c;一块 8 K大小的内存空间。Go向操作系统申请和…

Rabbitmq消息不丢失

目录 一、消息不丢失1.消息确认2.消息确认业务封装2.1 发送确认消息测试2.2 消息发送失败&#xff0c;设置重发机制 一、消息不丢失 消息的不丢失&#xff0c;在MQ角度考虑&#xff0c;一般有三种途径&#xff1a; 1&#xff0c;生产者不丢数据 2&#xff0c;MQ服务器不丢数据…

MySQL入门学习教程(二)

上一篇文章讲的是mysql的基本操作&#xff0c;这一篇会有一点难以理解&#xff0c;本节主要内容mysql视图&#xff0c;存储过程&#xff0c;函数&#xff0c;事务&#xff0c;触发器&#xff0c;以及动态执行sql 视图view 视图是一个虚拟表&#xff0c;其内容由查询定义。同真…

在Java中操作Redis(详细-->从环境配置到代码实现)

在Java中操作Redis 文章目录 在Java中操作Redis1、介绍2、Jedis3、Spring Data Redis3.1、对String的操作3.2、对哈希类型数据的操作3.3、对list的操作3.4、对set类型的操作3.5、对 ZSet类型的数据&#xff08;有序集合&#xff09;3.6、通用类型的操作 1、介绍 Redis 的Java客…

开发者必知:.gitignore 文件的魔法,助你管理项目文件如虎添翼!

前言&#xff1a; 在软件开发的世界中&#xff0c;版本控制是一个至关重要的环节。而 Git 作为目前最流行的分布式版本控制系统之一&#xff0c;已经成为开发者不可或缺的工具。然而&#xff0c;在日常的开发过程中&#xff0c;有些文件是不适合被纳入版本控制的&#xff0c;比…

【C++入门】const 成员函数

文章目录 一、基本概念二、经典问题三、使用建议 一、基本概念 const 修饰的成员函数就称作 const 成员函数。 例子&#xff1a; class Date { public:void Display() const{...}private:int _year;int _month;int _day; };事实上&#xff0c;const 成员函数的这个 const 修…

Linux文件系统管理

Linux文件系统管理 磁盘的组成与分区 计算机用于存取文件的硬件是磁盘&#xff0c;磁盘的组成主要有磁盘盘、机械手臂、磁盘读取头与主轴马达所组成&#xff0c; 而数据的写入其实是在磁盘盘上面。磁盘盘上面又可细分出扇区(Sector)与磁道(Track)两种单位&#xff0c; 其中扇区…

YOLOv5入门实践(3)— 手把手教你如何去划分数据集

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。数据集标注完成之后&#xff0c;下一步就是对这些数据集进行划分了。面对繁杂的数据集&#xff0c;如果手动划分的话&#xff0c;不仅麻烦而且不能保持随机性。本节课就给大家介绍一种方法&#xff0c;即使用代码去划分数据…

Express 实战(一):概览

在正式学习 Express 内容之前&#xff0c;我们有必要从大的方面了解一下 Node.js 。 在很长的一段时间里&#xff0c;JavaScript 一门编写浏览器中运行脚本的语言。不过近些年&#xff0c;随着互联网的发展以及技术进步&#xff0c;JavaScript 迎来了一个集中爆发的时代。一个…

思科交换机和路由器使用TFTP备份和还原配置文件

&#xff08;1&#xff09;给交换机配置管理地址&#xff0c;保证交换机与服务器相连通 SW1(config)#int vlan 1 SW1(config-if)#ip add 192.168.1.1 255.255.255.0 SW1(config-if)#no shut SW1#write &#xff08;2&#xff09;备份startup-config到服务器 SW1#copy startup…

【linux教程学习笔记】

目录 一. Linux系统目录结构 ​编辑 二. Linux文件基本属性 1. 文件属性分析 2. 更改文件属性 2.1. chgrp&#xff1a;change group&#xff0c;更改文件所属的组 2. chown&#xff1a;change owner&#xff0c;更改文件所属的用户&#xff0c;也可同时更改文件所属的组…

UG NX二次开发(C#)-CAM-获取刀具类型

文章目录 1、前言2、UG NX中的刀具类型3、获取刀具类型3.1 刀具类型帮助文档1、前言 在UG NX的加工模块,加工刀具是一个必要的因素,其包括了多种类型的类型,有铣刀、钻刀、车刀、磨刀、成型刀等等,而且每种刀具所包含的信息也各不相同。想获取刀具的信息,那就要知道刀具的…

php如何对接伪原创api

在了解伪原创api的各种应用形态之后&#xff0c;我们继续探讨智能写作背后的核心技术。需要说明的是&#xff0c;智能写作和自然语言生成、自然语言理解、知识图谱、多模算法等各类人工智能算法都有紧密的关联&#xff0c;在百度的智能写作实践中&#xff0c;常根据实际需求将多…

RT-Thread Smart 用户态开发体验

背景 RT-Thread Smart 是基于 RT-Thread 操作系统上的混合操作系统&#xff0c;它把应用从内核中独立出来&#xff0c;形成独立的用户态应用程序&#xff0c;并具备独立的地址空间。 自 V5.0.0 起&#xff0c;rt-smart 分支已合并至 master 分支上&#xff0c;下载 rt-thread …

2023年上半年数学建模竞赛题目汇总与难度分析

2023年上半年数学建模竞赛题目汇总与难度分析 ​由于近年来国赛ABC题出题方式漂浮不定&#xff0c;没有太大的定性&#xff0c;目前总体的命题方向为&#xff0c;由之前的单一模型问题变为数据分析评价优化或者预测类题目是B、C题的主要命题方向。为了更好地把握今年命题的主方…

快捷键使用技巧

IDEA生成序列化ID 1 CtrlAlts快捷键打开设置界面 2 选择Editor→Inspections&#xff0c;勾上serialVersionUID 3 每次实现序列化接口&#xff0c;可以鼠标点击类名&#xff0c;AltEnter快捷键导入序列化ID webstorm 快捷键重构 shiftf6 全局替换 通过快捷键CtrlShiftR打…

带扩散器的超快速控制网

一、说明 自从稳定扩散风靡全球以来&#xff0c;人们一直在寻找更好地控制生成过程结果的方法。ControlNet提供了一个最小的界面&#xff0c;允许用户在很大程度上自定义生成过程。使用 ControlNet&#xff0c;用户可以轻松地使用不同的空间上下文&#xff08;如深度图、分割图…

Cpp学习——vector模拟实现

vector简介 在模拟实现vector之前&#xff0c;首先就得知道vector是个啥&#xff1f;vector是个啥呢&#xff1f;vector是一个stl里面的容器&#xff0c;并且是一个模板容器。它就像是一个顺序表模板。还记得顺序表吧&#xff1f;之前我实现的顺序表只能弄整形的数据&#xff0…

深入篇【Linux】学习必备:进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级)

深入篇【Linux】学习必备&#xff1a;进程理解(从底层探究进程概念/进程创建/进程状态/进程优先级&#xff09; 一.进程概念(PCB/task_struct)二.查看进程(top/ps)三.创建进程(fork)四.进程状态(僵尸进程/孤儿进程)五.进程优先级(PRI/NI) 一.进程概念(PCB/task_struct) 1.什么…