《Pytorch深度学习和图神经网络(卷 2)》学习笔记——第一章

news2024/10/5 20:18:28

学习基于如下书籍,仅供自己学习,用来记录回顾,非教程。

<PyTorch深度学习和图神经网络(卷2)——开发应用>一书配套代码:
https://github.com/aianaconda/pytorch-GNN-2nd-
百度网盘链接:https://pan.baidu.com/s/1dnq5IbFjjdekAR54HLb9Pg
提取码:k7vi
压缩包密码:dszn

图片分类模型

2012年起,在ILSVRC竞赛中获得冠军的模型如下
2012年:AlexNet
2013年:OverFeat
2014年:GoogLeNet、VGG(亚军)
2015年:ResNet
2016年:Trimps-Soushen、ResNeXt(亚军)
2017年:SENet
之后又有很多性能更加出色的如PNASNet、DenseNet、EfficientNet。

Inception系列模型

它主要是解决深层网络中3个问题:
训练数据有限,参数太多,容易过拟合。
网络越大,计算复杂度越大,难以应用。
网络越深,梯度越往后传,越容易消失(梯度弥散),难以优化模型。

多分支结构

原始的Inception模型采用1X1、3X3、5X5卷积和3X3最大池化,增加了网络的宽度,增强了网络对不同尺寸的适应性。

全局均值池化

代替最后的全连接输出层,如1000个分类,最后一层的特征图要1000个,可以直接得出分类。

Inception V1模型

在大卷积核前和池化后加入1X1卷积,起到降低特征图厚度的作用。

Inception V2模型

融入当时的主流技术,加入了BN层和梯度截断技术,借鉴了VGG模型,用两个3X3代替一个5X5,降低了参数量,提高了运算速度。

Inception V3模型

将3X3卷积分解成1XN和NX1,让卷积核分解更小,基于线性代数的原理。
假设256个特征输入,256个特征输出,Inception层只能执行3X3卷积,也就是要完成256X256X3X3(589824)次乘积累加运算。假设现在要减少进行卷积运算的特征数量,将其变为64个(256/4),先进行256到64的1X1的卷积,然后在所有Inception层的分支上进行64次卷积,最后使用一个64到256的特征的1X1卷积。现在有256X64X1X1+64X64X3X3+64X256X1X1=69632次计算量,提高了运算速度。
实际测试中,在前几层效果不好,但对特征图大小为12到20的中间层效果明显

Inception V4模型

结合ResNet模型,加入了残差连接。

Inception-ResNet V2模型

在网络复杂度相近的情况下,该模型略优于V4,残差提高网络准确率,而不会增加计算量的作用。
通过将3个带有残差连接的Inception模型和一个Inception V4模型组合,就可以在ImageNet上得到3.08%的错误率。

ResNet模型

模型层数加深,梯度在多层传播时会越来越小,直到消失,层数越多训练误差会越来越大。ResNet解决深层无法训练的问题,借鉴了高速网络模型的思想,在前馈上加一个直接连接,直连可以保留梯度。作用是将网络串行改成了并行,V4结合残差原理不用残差连接就可以达到ResNet V2等同的效果。

DenseNet模型

2017年被提出,是密集连接的卷积神经网络,每个层都会接受前面所有层的作为输入。主要优势如下:
可以直达最后的误差信号,减轻梯度消失问题
拼接特征图实现短路连接,实现特征重用,每个层独有的特征图比较小
前面的特征图直接传给后面,可以充分利用不同层级的特征
不如就是可能耗费很多GPU显存

稠密块

其中只含有两个卷积层,分别为1X1,3X3。每个稠密块是L个全连接组成,不同稠密块之间没有。

PNASNet模型

使用了残差结构和多分支卷积技术,同时还添加了深度可分离卷积和空洞卷积的处理。

组卷积

了解深度可分离卷积要了解组卷积。组卷积对输入数据先分组,能增强卷积核之间对角相关性,减少训练参数,不容易过拟合,类似于正则效果。

深度可分离卷积

Xception模型是Inception系列模型的统称,主要目的是将通道相关性和平面空间维度相关性进行解耦,使通道关系和平面空间关系上的卷积操作相互独立,达到更好的效果。

空洞卷积

也称为扩张卷积,在不做池化操作而导致损失信息的情况下,加大了卷积的感受野,让每个卷积输出都包含更大的范围。

EfficientNet模型

是谷歌公司通过机器搜索得到的模型,构建步骤如下。
使用强化学习算法实现的MnasNet模型生成基准模型EfficientNet-B0
采用复合缩放,在预先设定的内存和计算量大小的限制条件下,对EfficientNet-B0模型的深度、宽度(特征图通道数)、图片尺寸这3个维度同时进行缩放,这3个维度的缩放比例由网络搜索得到,最终输出了EfficientNet模型。

MBConv

内部由多个MBConv卷积块实现的,也用了类似残差连接的结构,在短连接部分使用了SE模块,并且将常用的ReLU激活函数换成了Swish激活函数,另外使用了Drop Connect层来代替传统的Dropout层(丢弃隐藏层输入而不是输出)。

实例:使用预训练模型识别图片内容

这里用models模块创建模型,然后载入下载好的参数。

model = models.resnet18()
model.load_state_dict(torch.load(r"E:\desktop\Home_Code\pytorch\2-chapter1\some\resnet18-5c106cde.pth")) 	#true 代表下载
model = model.eval()

记得要进行model.eval()

from PIL import Image  						#引入基础库
import matplotlib.pyplot as plt
import json
import numpy as np

import torch								#引入PyTorch库
import torch.nn.functional as F
from torchvision import models, transforms #引入torchvision库

model = models.resnet18()
model.load_state_dict(torch.load(r"E:\desktop\Home_Code\pytorch\2-chapter1\some\resnet18-5c106cde.pth")) 	#true 代表下载
model = model.eval()

labels_path = r'pytorch\2-chapter1\some\imagenet_class_index.json'  #处理英文标签
with open(labels_path) as json_data:
    idx_to_labels = json.load(json_data)

def getone(onestr):
    return onestr.replace(',',' ')
with open(r'pytorch\2-chapter1\some\中文标签.csv','r+') as f: 		#处理中文标签				
    zh_labels =list( map(getone,list(f))  )
    print(len(zh_labels),type(zh_labels),zh_labels[:5]) #显示输出中文标签

transform = transforms.Compose([			#对图片尺寸预处理
 transforms.Resize(256),
 transforms.CenterCrop(224),
 transforms.ToTensor(),
 transforms.Normalize(						#对图片归一化预处理
     mean=[0.485, 0.456, 0.406],
     std=[0.229, 0.224, 0.225]
         )
])

def preimg(img):                              		#定义图片预处理函数
    if img.mode=='RGBA':                    		#兼容RGBA图片
        ch = 4 
        print('ch',ch)
        a = np.asarray(img)[:,:,:3] 
        img = Image.fromarray(a)
    return img

im =preimg( Image.open(r'pytorch\2-chapter1\some\book.png') )				#打开图片
transformed_img = transform(im)					#调整图片尺寸

inputimg = transformed_img.unsqueeze(0)			#增加批次维度

output = model(inputimg)						#输入模型
output = F.softmax(output, dim=1)				#获取结果

# 从预测结果中取出前3名
prediction_score, pred_label_idx = torch.topk(output, 3)
prediction_score = prediction_score.detach().numpy()[0] #获取结果概率
pred_label_idx = pred_label_idx.detach().numpy()[0]		 #获取结果的标签id

predicted_label = idx_to_labels[str(pred_label_idx[0])][1]#取出标签名称
predicted_label_zh = zh_labels[pred_label_idx[0] + 1 ] #取出中文标签名称
print(' 预测结果:', predicted_label,predicted_label_zh, 
        '预测分数:', prediction_score[0])

#可视化处理,创建一个1行2列的子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 8))
fig.sca(ax1)  				#设置第一个轴是ax1
ax1.imshow(im)  				#第一个子图显示原始要预测的图片

#设置第二个子图为预测的结果,按概率取前3名
barlist = ax2.bar(range(3), [i for i in prediction_score])
barlist[0].set_color('g')  		#颜色设置为绿色

#预测结果前3名的柱状图
plt.sca(ax2)
plt.ylim([0, 1.1])

#竖直显示Top3的标签
plt.xticks(range(3), [idx_to_labels[str(i)][1][:15] for i in pred_label_idx ], rotation='vertical')
fig.subplots_adjust(bottom=0.2)  	#调整第二个子图的位置
plt.show()  							#显示图像 

预测结果: book_jacket 防尘罩 书皮
预测分数: 0.27850005
成功识别出书皮,不过可信度不是很高
在这里插入图片描述
试试别的
在这里插入图片描述
在这里插入图片描述

迁移学习

把在一个任务上训练完成的模型进行简单的修改,再用另一个任务的数据继续训练,使之能够完成新的任务。
如在ImageNet数据集上训练过的ResNet模型,原任务是用来图片分类的,可以对它进行修改,使之用在目标定位任务上。
迁移学习是机器学习的分支,按照学习方式可以分为基于样本的迁移、基于特征的迁移、基于模型的迁移、基于关系的迁移。
初衷是节省人工标注样本的时间,让模型通过一个已有的标记数据领域向未标记数据领域进行迁移,从而训练出适用于该领域的模型。好处如下:
对于本身数据集很小(几千张图片)的情况,从头开始训练几千万个参数的大型神经网络模型不现实,越大的模型数据量需求越大,过拟合无法避免。如果还想用大型模型的超强特征提取能力,只能靠微调已经训练好的模型。
可以降低训练成本,用导出特征向量的方法,后期训练成本非常低。
前人训练的模型大概率比你自己从零训练要强大,没必要重复造轮子。

与微调的关系没有严格的区分,作者的理解,微调是迁移学习的后期,是它的一部分,一个技巧。

实例:使用迁移学习识别多种鸟类

import glob
import os
import numpy as np#引入基础库
from PIL import Image
import matplotlib.pyplot as plt       		#plt 用于显示图片

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.utils.data import Dataset,DataLoader

import torchvision
import torchvision.models as models
from torchvision.transforms import ToPILImage
import torchvision.transforms as transforms


def load_dir(directory,labstart=0):#获取所有directory中的所有图片和标签
    #返回path指定的文件夹所包含的文件或文件夹的名字列表
    strlabels = os.listdir(directory)
    #对标签进行排序,以便训练和验证按照相同的顺序进行
    strlabels.sort()
    #创建文件标签列表
    file_labels = []
    for i,label in enumerate(strlabels):
#        print(label)
        jpg_names = glob.glob(os.path.join(directory, label, "*.jpg"))
#        print(jpg_names)
        #加入列表
        file_labels.extend(zip( jpg_names,[i+labstart]*len(jpg_names))  )

    return file_labels,strlabels


def load_data(dataset_path):      #定义函数加载文件名称和标签
    sub_dir= sorted(os.listdir(dataset_path) )#跳过子文件夹
    start =1 #none:0
    tfile_labels,tstrlabels=[],['none']
    for i in sub_dir:
        directory = os.path.join(dataset_path, i)
        if os.path.isdir(directory )==False: #只处理目录中的数据
            print(directory)
            continue
        file_labels,strlabels = load_dir(directory ,labstart = start )
        tfile_labels.extend(file_labels)
        tstrlabels.extend(strlabels)
        start  = len(strlabels)
    #理解为解压缩,把数据路径和标签解压缩出来
    filenames, labels=zip(*tfile_labels)
    return filenames, labels,tstrlabels



def default_loader(path):
    return Image.open(path).convert('RGB')

class OwnDataset(Dataset):
    def __init__(self,img_dir, labels, indexlist= None, transform=transforms.ToTensor(),
                 loader=default_loader,cache=True):
        self.labels = labels
        self.img_dir = img_dir
        self.transform = transform
        self.loader=loader
        self.cache = cache 								#缓存标志
        if indexlist is None:
            self.indexlist = list(range(len(self.img_dir)))
        else:
            self.indexlist = indexlist
        self.data = [None] * len(self.indexlist) 		#存放样本图片
    
    def __getitem__(self, idx):
        if self.data[idx] is None:						#第一次加载
            data = self.loader(self.img_dir[self.indexlist[idx]])
            if self.transform:
                data = self.transform(data)
        else:
            data = self.data[idx]
        if self.cache: 									#保存到缓存里
            self.data[idx] = data
        return data,  self.labels[self.indexlist[idx]]

    def __len__(self):
        return len(self.indexlist)


data_transform = {                      #定义数据的预处理方法
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}
def Reduction_img(tensor,mean,std):#还原图片
    dtype = tensor.dtype
    mean = torch.as_tensor(mean, dtype=dtype, device=tensor.device)
    std = torch.as_tensor(std, dtype=dtype, device=tensor.device)
    tensor.mul_(std[:, None, None]).add_(mean[:, None, None])#扩展维度后计算

    
dataset_path = r'pytorch/2-chapter1/some2/'
filenames, labels,classes = load_data(dataset_path)   

#打乱数组顺序
np.random.seed(0)
label_shuffle_index = np.random.permutation( len(labels)  ) 
label_train_num = (len(labels)//10) *8 
train_list =  label_shuffle_index[0:label_train_num]  
test_list =   label_shuffle_index[label_train_num: ] 


train_dataset=OwnDataset(filenames, labels,train_list,data_transform['train'])
val_dataset=OwnDataset(filenames, labels,test_list,data_transform['val'])


train_loader =DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)
val_loader=DataLoader(dataset=val_dataset, batch_size=32, shuffle=False)


#
sample = iter(train_loader)
images, labels = sample.__next__()
print('样本形状:',np.shape(images))
print('标签个数:',len(classes))

mulimgs = torchvision.utils.make_grid(images[:10],nrow=10)
Reduction_img(mulimgs,[0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
_img= ToPILImage()( mulimgs )
plt.axis('off')
plt.imshow(_img)
plt.show()
print(','.join('%5s' % classes[labels[j]] for j in range(len(images[:10]))))


############################################

#指定设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)


def get_ResNet(classes,pretrained=True,loadfile = None):
    ResNet=models.resnet101(pretrained)# 这里自动下载官方的预训练模型
    if loadfile!= None:
        ResNet.load_state_dict(torch.load( loadfile))	#加载本地模型 
        
    # 将所有的参数层进行冻结
    for param in ResNet.parameters():
        param.requires_grad = False
    # 这里打印下全连接层的信息
    print(ResNet.fc)
    x = ResNet.fc.in_features #获取到fc层的输入
    ResNet.fc = nn.Linear(x, len(classes)) # 定义一个新的FC层
    print(ResNet.fc) # 最后再打印一下新的模型
    return ResNet

ResNet=get_ResNet(classes)
ResNet.to(device)

criterion = nn.CrossEntropyLoss()
#指定新加的fc层的学习率
optimizer = torch.optim.Adam([ {'params':ResNet.fc.parameters()}], lr=0.001)


def train(model,device, train_loader, epoch,optimizer):
    model.train()
    allloss = []
    for batch_idx, data in enumerate(train_loader):
        x,y= data
        x=x.to(device)
        y=y.to(device)
        optimizer.zero_grad()
        y_hat= model(x)
        loss = criterion(y_hat, y)
        loss.backward()
        allloss.append(loss.item())
        optimizer.step()
    print ('Train Epoch: {}\t Loss: {:.6f}'.format(epoch,np.mean(allloss)  ))

def test(model, device, val_loader):
    model.eval()
    test_loss = []
    correct = []
    with torch.no_grad():
        for i,data in enumerate(val_loader):          
            x,y= data
            x=x.to(device)
            y=y.to(device)
            y_hat = model(x)
            test_loss.append( criterion(y_hat, y).item()) # sum up batch loss
            pred = y_hat.max(1, keepdim=True)[1] # get the index of the max log-probability
            correct.append( pred.eq(y.view_as(pred)).sum().item()/pred.shape[0] )
    print('\nTest set——{}: Average loss: {:.4f}, Accuracy: ({:.0f}%)\n'.format(
         len(correct),np.mean(test_loss), np.mean(correct)*100 ))
if __name__ == '__main__':

    firstmodepth = './finetuneRes101_1.pth'

    
    if os.path.exists(firstmodepth) ==False:
        print("_____训练最后一层________")  
        for epoch in range(1, 2):
            train(ResNet,device, train_loader,epoch,optimizer )
            test(ResNet, device, val_loader )
        # 保存模型
        torch.save(ResNet.state_dict(), firstmodepth)       
    
    
    
    secondmodepth = './finetuneRes101_2.pth'
    optimizer2=optim.SGD(ResNet.parameters(),lr=0.001,momentum=0.9)
    exp_lr_scheduler = lr_scheduler.StepLR(optimizer2, step_size=2, gamma=0.9)
    
    for param in ResNet.parameters():
        param.requires_grad = True
    
    if os.path.exists(secondmodepth) :
        ResNet.load_state_dict(torch.load( secondmodepth))	#加载本地模型
    else:
        ResNet.load_state_dict(torch.load(firstmodepth))	#加载本地模型    
    print("_____全部训练________")    
    for epoch in range(1, 100):
        train(ResNet,device, train_loader,epoch,optimizer2 )
        if optimizer2.state_dict()['param_groups'][0]['lr']>0.00001:
            exp_lr_scheduler.step()
            print("___lr:" ,optimizer2.state_dict()['param_groups'][0]['lr'] )
        
        test(ResNet, device, val_loader )    
    # 保存模型
    torch.save(ResNet.state_dict(), secondmodepth)  

训练太慢了,而且输出结果与书籍不符合,标签个数就不一样,应该为201个,我是6个。应该用CUB-200训练,而不是压缩包里的6类数据。这个锐龙显卡不知道怎么加速,就不训练了。

随机数据增强

目前效果最好的Efficient系列模型中,B7版本中就是使用了随机数据增强,RandAugment比自动数据增强(AutoAugment)更简单好用,后者有30多个参数,前者将其简化为2个,图片的N次变换,和每次变换的强度M,取值为0到10。

分类模型中常用的三种损失函数

BCELoss,用于单标签二分类,或者多标签二分类,一个样本可以有多个分类不互斥,输出为(Batch,C),Batch样本数量,C是类别数量。每个C值代表属于一类标签的概率。
BCEWithLogitsLoss,同上,但对网络输出结果进行了一次Sigmoid。
CrossEntropyLoss,用于多类别分类,每个C是互斥的,相互关联的,求每个C的Softmax。

样本均衡

训练样本不均衡时,可以用过采样,欠采样,数据增强等手段来避免过拟合。
采样器类Sample中有一类派生的权重采样类WeightedRandomSampler,能够在加载数据时进行随机顺序采样。
DataLoader类中使用了采样器Sampler类就不能使用shuffle参数

从深度卷积模型中提取视觉特征

前面的实例通过替换预训练模型输出层的方式,实现对其他图片的分类任务,这种迁移学习本质上是借助了预训练模型对图片处理后的视觉特征,这在深度学习任务中起到了很大的作用。如目标检测、语义分割,甚至是图像与文本的混合处理模型等,迁移学习只是其中一种。
有两种方式可以实现视觉特征的提取:钩子函数、重组结构

钩子函数

重组结构

ResNet2=nn.Sequential(*list(ResNet.children( ))[:-1])

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

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

相关文章

【有功功率、无功功率】可再生能源配电馈线的鲁棒经济调度研究[IEEE13节点](Matlab代码实现)

&#x1f4a5;1 概述 "有功功率和无功功率" 是与电力系统中能量传输和功率控制相关的两个重要概念。 有功功率&#xff08;Active Power&#xff09;是指电力系统中传输和消耗能量的功率&#xff0c;也被称为实功功率。它负责提供电力系统中的实际电能需求&#xf…

Python使用select模块/asyncio库实现轮询机制

一、轮询机制概念 在操作系统中&#xff0c;用户态轮询机制是一种等待系统中某个资源就绪的方式&#xff0c;它通常用于非阻塞式I/O操作。这种机制允许用户进程在等待I/O操作完成时继续执行其他任务&#xff0c;而不是一直阻塞等待。用户进程可以使用系统调用将I/O操作请求提交…

数学分析:换元详解

这一端文章没有写详细的证明。意思是说n维空间下的k个向量围成的多面体的体积&#xff0c;都可以用公式(3)进行计算。详细证明过程参考&#xff1a;行列式的一种推广 - 知乎 这里简述下过程&#xff1a; 首先要把这n个m维向量进行格拉姆斯密特正交化&#xff0c;得到正交后的…

解决appium-doctor报gst-launch-1.0.exe and/or gst-inspect-1.0.exe cannot be found

一、下载gst-launch-1.0.exe and gst-inspect-1.0.exe 下载地址&#xff1a;Download GStreamer runtime installer 和 development installer 两个应用程序都要下载并安装 二、运行安装 下载好后点击安装会弹出如下界面&#xff0c;点击“更多信息”展开&#xff0c;点击“仍然…

C语言实现计算器简单混合运算

计算器的实现看似简单&#xff0c;其实并不简单。 要求完成功能&#xff1a; 1.实现 - * / 简单运算&#xff1b; 2.可以实现这几个运算符的综合&#xff08;混合&#xff09;运算&#xff1b; 注意&#xff1a;该计算器混合运算中不包含太复杂的运算符&#xff0c;如()&am…

星戈瑞 CY3-Dextran的合成方法和表征

CY3-Dextran是一种荧光染料&#xff0c;可用于细胞标记和显微镜观察。它具有很强的荧光信号和稳定性&#xff0c;可以用于研究细胞生物学和分子生物学。 CY3-Dextran的合成方法涉及将CY3染料与葡聚糖进行共价结合。以下是一种常用的合成方法&#xff1a; 【合成方法】&#xf…

3.1例子---登录窗口1

3.1例子—登录窗口1 这一次效果图是这样的&#xff1a; 界面创建 # welcome image canvas tk.Canvas(window, height200, width500)#创建画布 image_file tk.PhotoImage(filewelcome.gif)#加载图片文件 image canvas.create_image(0,0, anchornw, imageimage_file)#将图…

MySQL数据库第九课--------join连接四件套------不错的哦哦哦

作者前言 欢迎小可爱们前来借鉴我的gtiee秦老大大 (qin-laoda) - Gitee.com ____________________________________________________________________ 目录 SQL查询语句 限定输出 limit 连接查询 join 内连接 左连接 右连接 外连接 ____________________________________…

xxx.indexOf is not a function报错

注意&#xff1a;xxx 如果是数字、布尔、对象&#xff0c;然而indexOf用于查找字符串或数组中的元素&#xff0c;所以会报错。

Qt5.15.2安装

解释一下 Qt 的版本号 比如 5.15.2 是完整的 Qt 版本号&#xff0c;第一个数字 5 是大版本号&#xff08;major&#xff09;&#xff0c;第二个数字 15 是小版本号&#xff08;minor&#xff09;&#xff0c;第三个数字 2 是补丁号&#xff08;patch&#xff09;。 只要前面两个…

Python异步编程框架Tornado使用方法

Tornado简介 Python异步编程框架Tornado是一个轻量级的Web框架和异步网络库&#xff0c;它能够处理大量并发连接和请求&#xff0c;非常适合高并发的网络应用和实时应用。 Tornado基本概念&#xff1a; 协程&#xff1a;Tornado采用协程并发模型&#xff0c;可以让单线程同时…

Vue第三篇:最简单的vue购物车示例

本文参考&#xff1a;Vue Cli&#xff08;脚手架&#xff09;实现购物车小案例 - - php中文网博客 效果图&#xff1a; 编写流程&#xff1a; 1、首先通过vue/cli创建工程 vue create totalprice 2、改写App.vue代码如下&#xff1a; <template><div><div v…

017 - STM32学习笔记 - SPI读写FLASH(二)

016 - STM32学习笔记 - SPI访问Flash&#xff08;二&#xff09; 上节内容学习了通过SPI读取FLASH的JEDEC_ID&#xff0c;在flash资料的指令表中&#xff0c;还看到有很多指令可以使用&#xff0c;这节继续学习使用其他指令&#xff0c;程序模板采用上节的模板。 为了方便起…

uni-app中a标签下载文件跳转后左上角默认返回键无法继续返回

1.首先使用的是onBackPress //跟onShow同级别 onBackPress(option){ uni.switchTab({ url:/pages/....... return true }) }发现其在uni默认头部中使用是可以的 但是h5使用了"navigationStyle":"custom"后手机默认的返回并不可以&#xff0c; 2.经过查询…

autok3s k3d rancher研究

参考 功能介绍 | Rancher文档AutoK3s 是用于简化 K3s 集群管理的轻量级工具&#xff0c;您可以使用 AutoK3s 在任何地方运行 K3s 服务。http://docs.rancher.cn/docs/k3s/autok3s/_index 什么是 AutoK3s k3s是经过完全认证的 Kubernetes 产品&#xff0c;在某些情况下可以替…

Centos 7 使用国内镜像源更新内核

内核选择参考 此博文 &#xff1a;https://blog.csdn.net/alwaysbefine/article/details/108931626 elrepo官网介绍的内核升级方式为&#xff1a; 一、按文档执行引入 elrepo库&#xff1b; # 1、引入公钥 rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org# 2、安…

数据中心水浸事件,该如何找回安全?

数据中心是现代企业和组织中不可或缺的基础设施&#xff0c;承载着大量的敏感数据和关键业务运作。然而&#xff0c;水浸事件可能成为数据中心的巨大威胁&#xff0c;可能导致设备故障、数据丢失以及业务中断&#xff0c;给组织带来严重的损失和风险。 因此&#xff0c;为了保护…

机器学习(十六):决策树

全文共18000余字&#xff0c;预计阅读时间约36~60分钟 | 满满干货&#xff0c;建议收藏&#xff01; 一、介绍 树模型是目前机器学习领域最为重要的模型之一&#xff0c;同时它也是集成学习中最常用的基础分类器。 与线性回归、逻辑回归等算法不同&#xff0c;树模型并不只是…

力扣707设计链表

你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则还需要属性 prev 以指示链表中的上一个节点…

Spring Security OAuth2.0(5):Spring Security工作原理

文章目录 工作原理结构总览 认证流程授权流程AuthenticationProviderUserDetailsServicePasswordEncoder如何使用BCryptPasswordEncoder 授权流程授权流程授权决策 工作原理 结构总览 \qquad Spring Security 所解决的问题就是安全访问控制&#xff0c;而安全访问控制功能其实…