Pytorch对预训练好的VGG16模型进行微调

news2024/9/29 13:25:54

目录

1.数据集准备、预训练模型准备

2.对VGG16模型进行微调

 3.对数据集进行预处理

4.对模型进行训练并可视化训练过程

5.该测试案例的完整代码


对于一个复杂的卷积神经网络来说,通常网络的层数非常大,网络的深度非常深、网络的参数非常多,单单设计一个卷积网络就需要颇费心思,何况网络还需要大量的数据集进行漫长时间的训练,若没有一个好的算力平台也很难迅速训练出模型。可见,从头到尾搭建一个中等规模的卷积神经网络对于我们来说绝非易事。幸运的是PyTorch已经许多预训练好的模型,比如内置了使用ImageNet数据集预训练好的、流行的VGG、AlexNet等深度学习网络,我们可以针对自己的需求,对预训练好的网络进行微调,从而快速完成自己的任务。下面,我们使用基于pytorch提供的预训练好的VGG16,对其进行微调,使用其他的数据集,训练一个图片分类器。

1.数据集准备、预训练模型准备

我们使用来自kaggle数据库中的10类猴子数据集。在该数据集中包含训练数据集和验证数据集,其中训练数据集中每类约140张RGB图像,验证数据集中每类约30张图像。针对该数据集使用VGG16的卷积层和池化层的预训练好的权重,提取数据特征,然后定义新的全连接层,用于图像的分类。

import torch
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score,confusion_matrix,classification_report
import matplotlib.pyplot as plt
import seaborn as sns
import hiddenlayer as hl
import torch.nn as nn
from torch.optim import SGD,Adam
import torch.utils.data as Data
from torchvision import models
from torchvision import transforms
from torchvision.datasets import ImageFolder
#导入预训练好的VGG16
vgg16=models.vgg16(pretrained=True)
vgg=vgg16.features#获取vgg16的特征提取层
for param in vgg.parameters():
    param.requires_grad(False)

在上面的程序中,使用models.vgg16(pretrained=True)导入网络,其中参数pretrained=True表示导入的网络是使用ImageNet数据集预训练好的网络。在得到的VGG16网络中,使用vgg16.features获取VGG16网络的特征提取模块,即前面的卷积核池化层,不包括全连接层。为了提升网络的训练速度,只使用VGG16提取图像的特征,需要将VGG16的特征提取层参数冻结,不更新其权重,通过for循环和param.requires_grad_(False)即可

2.对VGG16模型进行微调

获取VGG16网络的特征提取模块后,需要在VGG16特征提取层之后添加新的全连接层,网络结构定义如下:

class MyVggNet(nn.Module):
    def __init__(self):
        super(MyVggNet, self).__init__()
        #预训练的vgg16特征提取层
        self.vgg=vgg
        #添加新的全连接层
        self.classify=nn.Sequential(
            nn.Linear(25088,512),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(512,256),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(256,10),
            nn.Softmax(dim=1)
        )
    #定义网络的前向传播
    def forward(self,x):
        x=self.vgg(x)
        x=x.view(x.size(0),-1)#多维度的tensor展平成一维
        output=self.classify(x)
        return output
MyVggNet=MyVggNet()
print(MyVggNet)

在上面的程序中,定义了一个卷积神经网络类包含两个大的结构,一个是self.vgg,使用预训练好的VGC16的特征提取,并且其参数的权重已经冻结;另一个是self.classify,由三个全连接层组成。在全连接层中使用ReLU函数作为激活函数,并通过nn.Dropout层防止模型过拟合。输出的网络的详细结构如下: 


MyVggNet(
  (vgg): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU(inplace=True)
    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU(inplace=True)
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (6): ReLU(inplace=True)
    (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): ReLU(inplace=True)
    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU(inplace=True)
    (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (15): ReLU(inplace=True)
    (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (18): ReLU(inplace=True)
    (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (20): ReLU(inplace=True)
    (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (22): ReLU(inplace=True)
    (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (25): ReLU(inplace=True)
    (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (27): ReLU(inplace=True)
    (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (29): ReLU(inplace=True)
    (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (classify): Sequential(
    (0): Linear(in_features=25088, out_features=512, bias=True)
    (1): ReLU()
    (2): Dropout(p=0.5, inplace=False)
    (3): Linear(in_features=512, out_features=256, bias=True)
    (4): ReLU()
    (5): Dropout(p=0.5, inplace=False)
    (6): Linear(in_features=256, out_features=10, bias=True)
    (7): Softmax(dim=1)
  )
)
 

 3.对数据集进行预处理

在定义好网络结构之后,就需要对数据集的数据进行预处理,主要包括定义训练集和测试集的预处理过程。

#对训练集进行预处理
train_data=transforms.Compose([
    transforms.RandomResizedCrop(224),#随机长宽比裁剪为224*224
    transforms.RandomHorizontalFlip(),#依据P=0.5的概率水平翻转
    transforms.ToTensor(),#转化为张量并归一化至[0-1]
    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])#图像标准化处理
])
#对验证集进行预处理
val_data=transforms.Compose([
    transforms.Resize(256),#重置图像分辨率
    transforms.CenterCrop(224),#依据给定的size从中心裁剪
    transforms.ToTensor(),#转化为张量并归一化至[0-1]
    #图像标准化处理
    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
])

值得注意的是:在图像标准化时,我们始终都使用mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225]。这是因为image_mean=[0.485,0.456,0.406]、image_std=[0.229,0.224,0.225]是Imagenet数据集的均值和标准差,使用Imagenet的均值和标准是一种常见的做法。如果我们想在自己的数据集上从头开始训练,我们可以计算新的平均值和标准。否则,建议使用Imagenet的平均值和标准差。是否使用ImageNet的均值和标准差取决于我们的数据:假设数据是“自然场景”的普通照片(人,建筑,动物,不同的照明/角度/背景等等),并且假设数据集和 ImageNet 存在类似的偏差(在类别平衡方面),那么使用 ImageNet 的数据进行规范化就可以了。如果照片是“特殊的”(颜色过滤,对比度调整,不寻常的光线,等等)或“非自然的主题”(医学图像,卫星地图,手绘等) ,那么建议在模型训练之前正确地规范化数据集(计算新的平均值和标准)。

因为该数据集的每类图像都分别保存在一个单独的文件夹中,如下图:

 所以可以使用ImageFolder()函数从文件中读取训练集和验证集,数据读取的程序如下所示:

#读取训练集图像
train_data_dir="./Dataset/10-monkey-species/training"
train_data=ImageFolder(train_data_dir,train_data_transforms)
train_data_loader=Data.DataLoader(train_data,batch_size=32,shuffle=True,num_workers=0)
#读取验证集
val_data_dir="./Dataset/10-monkey-species/validation"
val_data=ImageFolder(val_data_dir,val_data_transforms)
val_data_loader=Data.DataLoader(val_data,batch_size=32,shuffle=True,num_workers=0)
print("训练集样本数:",len(train_data.targets))
print("验证集样本数:",len(val_data.targets))

训练集样本数: 1097
验证集样本数: 272

从输出结果可以发现,训练集有1097个样本,验证集有272个样本。下面我们获取训练集的一个batch图像,然后将获取的32张图像进行可视化,观察数据中图像的内容。

#获取一个batch的数据
for step,(b_x,b_y) in enumerate(train_data_loader):
    if step>0:
        break
mean=np.array([0.485,0.456,0.406])
std=np.array([0.229,0.224,0.225])
plt.figure(figsize=(12,6))
for i in np.arange(len(b_y)):
    plt.subplot(4,8,i+1)
    image=b_x[i,:,:,:].numpy().transpose((1,2,0))
    image=std * image+mean
    image=np.clip(image,0,1)
    plt.imshow(image)
    plt.title(b_y[i].data.numpy())
    plt.axis("off")
plt.subplots_adjust(hspace=0.3)
plt.show()

上面的程序在获取了一个batch图像后,在可视化前,需要将图像每个通道的像素值乘以对应的标准差并加上对应的均值,最后得到的可视化图像如图所示 

4.对模型进行训练并可视化训练过程

为了验证准备好的网络的泛化能力,使用训练集对网络进行训练,使用验证集验证。模型在训练时使用Adam优化算法,损失函数使用nn.CrossEntropyLoss()交叉嫡损失。在训练过程中使用HiddenLayer库可视化网络在训练集和验证集上的表现。

#定义优化器
optimizer=torch.optim.Adam(MyVggNet.parameters(),lr=0.003)
loss_func=nn.CrossEntropyLoss()#使用交叉熵损失函数
if torch.cuda.is_available():
    loss_func=loss_func.cuda()

history1=hl.History()
canvas1=hl.Canvas()
#对模型进行迭代训练
for epoch in range(10):
    train_loss_epoch=0
    val_loss_epoch=0
    train_corrects=0
    val_corrects=0
    #对训练数据的加载器进行迭代计算
    MyVggNet.train()
    for step,(b_x,b_y) in enumerate(train_data_loader):
        if torch.cuda.is_available():
            b_x=b_x.cuda()
            b_y=b_y.cuda()
        #计算每个batch上的损失
        output=MyVggNet(b_x)#CNN在训练batch上的输出
        loss=loss_func(output,b_y)#交叉熵损失函数
        pre_lab=torch.argmax(output,1)
        optimizer.zero_grad()#每个迭代步的梯度初始化为0
        loss.backward()#损失的后向传播,计算梯度
        optimizer.step()#使用梯度进行优化
        train_loss_epoch += loss.item() * b_x.size(0)
        train_corrects +=torch.sum(pre_lab==b_y.data)
    #计算一个epoch上的损失和精度
    train_loss=train_loss_epoch / len(train_data.targets)
    train_acc=train_corrects.double() / len(train_data.targets)
    #计算在验证集上的表现
    MyVggNet.eval()
    for step,(val_x,val_y) in enumerate(val_data_loader):
        if torch.cuda.is_available():
            val_x=val_x.cuda()
            val_y=val_y.cuda()
        output=MyVggNet(val_x)
        loss=loss_func(output,val_y)
        pre_lab=torch.argmax(output,1)
        val_loss_epoch +=loss.item() * val_x.size(0)
        val_corrects+=torch.sum(pre_lab==val_y.data)
    #计算一个epoch上的精度和损失
    val_loss=val_loss_epoch/len(val_data.targets)
    val_acc=val_corrects.double()/len(val_data.targets)
    #保存每个epoch上输出的loss和acc
    history1.log(epoch,train_loss=train_loss,
                 val_loss=val_loss,
                 train_acc=train_acc.item(),
                 val_acc=val_acc.item()
                 )
#可视化网络训练过程
with canvas1:
    canvas1.draw_plot([history1["train_loss"],history1["val_loss"]])
    canvas1.draw_plot([history1["train_acc"],history1["val_acc"]])


 使用上面的程序对模型训练10个epoch,在训练过程中,动态可视化模型的损失函数和识别精度的变化情况如上图所示。网络经过训练后,最终预测结果保持稳定,并且在验证集上的精度高于在训练集上的高度,在验证集上的损失低于在训练集上的损失。

5.该测试案例的完整代码

import torch
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score,confusion_matrix,classification_report
import matplotlib.pyplot as plt
import seaborn as sns
import hiddenlayer as hl
import torch.nn as nn
from torch.optim import SGD,Adam
import torch.utils.data as Data
from torchvision import models
from torchvision import transforms
from torchvision.datasets import ImageFolder
#导入预训练好的VGG16
vgg16=models.vgg16(pretrained=True)
vgg=vgg16.features#获取vgg16的特征提取层
for param in vgg.parameters():
    param.requires_grad_(False)

class MyVggNet(nn.Module):
    def __init__(self):
        super(MyVggNet, self).__init__()
        #预训练的vgg16特征提取层
        self.vgg=vgg
        #添加新的全连接层
        self.classify=nn.Sequential(
            nn.Linear(25088,512),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(512,256),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(256,10),
            nn.Softmax(dim=1)
        )
    #定义网络的前向传播
    def forward(self,x):
        x=self.vgg(x)
        x=x.view(x.size(0),-1)#多维度的tensor展平成一维
        output=self.classify(x)
        return output
MyVggNet=MyVggNet()
if torch.cuda.is_available():
    MyVggNet=MyVggNet.cuda()
print(MyVggNet)
#对训练集进行预处理
train_data_transforms=transforms.Compose([
    transforms.RandomResizedCrop(224),#随机长宽比裁剪为224*224
    transforms.RandomHorizontalFlip(),#依据P=0.5的概率水平翻转
    transforms.ToTensor(),#转化为张量并归一化至[0-1]
    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])#图像标准化处理
])
#对验证集进行预处理
val_data_transforms=transforms.Compose([
    transforms.Resize(256),#重置图像分辨率
    transforms.CenterCrop(224),#依据给定的size从中心裁剪
    transforms.ToTensor(),#转化为张量并归一化至[0-1]
    #图像标准化处理
    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
])
#读取训练集图像
train_data_dir="./Dataset/10-monkey-species/training"
train_data=ImageFolder(train_data_dir,train_data_transforms)
train_data_loader=Data.DataLoader(train_data,batch_size=32,shuffle=True,num_workers=0)
#读取验证集
val_data_dir="./Dataset/10-monkey-species/validation"
val_data=ImageFolder(val_data_dir,val_data_transforms)
val_data_loader=Data.DataLoader(val_data,batch_size=32,shuffle=True,num_workers=0)
print("训练集样本数:",len(train_data.targets))
print("验证集样本数:",len(val_data.targets))

# #获取一个batch的数据
# for step,(b_x,b_y) in enumerate(train_data_loader):
#     if step>0:
#         break
# mean=np.array([0.485,0.456,0.406])
# std=np.array([0.229,0.224,0.225])
# plt.figure(figsize=(12,6))
# for i in np.arange(len(b_y)):
#     plt.subplot(4,8,i+1)
#     image=b_x[i,:,:,:].numpy().transpose((1,2,0))
#     image=std * image+mean
#     image=np.clip(image,0,1)
#     plt.imshow(image)
#     plt.title(b_y[i].data.numpy())
#     plt.axis("off")
# plt.subplots_adjust(hspace=0.3)
# plt.show()

#定义优化器
optimizer=torch.optim.Adam(MyVggNet.parameters(),lr=0.003)
loss_func=nn.CrossEntropyLoss()#使用交叉熵损失函数
if torch.cuda.is_available():
    loss_func=loss_func.cuda()

history1=hl.History()
canvas1=hl.Canvas()
#对模型进行迭代训练
for epoch in range(10):
    train_loss_epoch=0
    val_loss_epoch=0
    train_corrects=0
    val_corrects=0
    #对训练数据的加载器进行迭代计算
    MyVggNet.train()
    for step,(b_x,b_y) in enumerate(train_data_loader):
        if torch.cuda.is_available():
            b_x=b_x.cuda()
            b_y=b_y.cuda()
        #计算每个batch上的损失
        output=MyVggNet(b_x)#CNN在训练batch上的输出
        loss=loss_func(output,b_y)#交叉熵损失函数
        pre_lab=torch.argmax(output,1)
        optimizer.zero_grad()#每个迭代步的梯度初始化为0
        loss.backward()#损失的后向传播,计算梯度
        optimizer.step()#使用梯度进行优化
        train_loss_epoch += loss.item() * b_x.size(0)
        train_corrects +=torch.sum(pre_lab==b_y.data)
    #计算一个epoch上的损失和精度
    train_loss=train_loss_epoch / len(train_data.targets)
    train_acc=train_corrects.double() / len(train_data.targets)
    #计算在验证集上的表现
    MyVggNet.eval()
    for step,(val_x,val_y) in enumerate(val_data_loader):
        if torch.cuda.is_available():
            val_x=val_x.cuda()
            val_y=val_y.cuda()
        output=MyVggNet(val_x)
        loss=loss_func(output,val_y)
        pre_lab=torch.argmax(output,1)
        val_loss_epoch +=loss.item() * val_x.size(0)
        val_corrects+=torch.sum(pre_lab==val_y.data)
    #计算一个epoch上的精度和损失
    val_loss=val_loss_epoch/len(val_data.targets)
    val_acc=val_corrects.double()/len(val_data.targets)
    #保存每个epoch上输出的loss和acc
    history1.log(epoch,train_loss=train_loss,
                 val_loss=val_loss,
                 train_acc=train_acc.item(),
                 val_acc=val_acc.item()
                 )
#可视化网络训练过程
with canvas1:
    canvas1.draw_plot([history1["train_loss"],history1["val_loss"]])
    canvas1.draw_plot([history1["train_acc"],history1["val_acc"]])


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

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

相关文章

中文翻译英语转换器-怎么把wps表格里的英文翻译成中文

对于那些需要频繁地进行中英互译的用户来说,字数限制是一个常见的问题。很多翻译软件经常会限制每次翻译的字数,导致用户翻译工作的效率和质量无法得到保证。如果您正在遭受这些限制,中英互译字数无限的软件将让您的翻译工作变得更加便捷和高…

反射之成员方法

Class类中用于获取成员方法的方法 Method[] getMethods(): 返回所有公共成员方法对象的数组,包括继承 Method[] getDeclaredMethods(): 返回所有成员方法对象的数组,不包括继承 Method getMethod(String name,Class ) …

笔记本硬盘坏了还能恢复数据吗 笔记本硬盘坏了怎么修复

笔记本电脑是经常使用的的学习、工作设备,它的硬盘中储存着大量的数据。一旦笔记本硬盘出现故障,这些数据会无法正常使用,对我们的学习生活产生重大影响。那么,笔记本硬盘坏了还能恢复数据吗,笔记本硬盘坏了怎么修复&a…

如何制作每日工作清单,让你高效完成开发工作

作为开发者,每天都有大量的任务需要完成。有时候,我们可能会感到无所适从,甚至失去动力。那么,如何有效地管理自己的任务和时间呢?在这篇文章中,我们将分享一位国外程序员大佬的亲身经验,介绍他…

【已解决】Field ‘id‘ doesn‘t have a default value 错误的解决办法

介绍 这里是小编成长之路的历程,也是小编的学习之路。希望和各位大佬们一起成长! 以下为小编最喜欢的两句话: 要有最朴素的生活和最遥远的梦想,即使明天天寒地冻,山高水远,路远马亡。 一个人为什么要努力&a…

OpenLdap学习笔记3

1、进入容器: docker exec -it my-openldap-container /bin/bash2、在home目录下创建learn目录: CD /home mkdir learn 3、创建barbara.ldif文件: dn: cnbarbara,dcexample,dcorg objectClass: inetOrgPerson cn: barbara sn: Jensen titl…

优思学院|质量大师的那些名言(一)【质量是免费的】

名言是一种短小精悍、言简意赅的语言表达方式,它们通常包含着深刻的哲理和智慧,可以为我们提供指导和启示。 优思学院会在这个《质量大师的那些名言》系列中让大家透过那些名言,用最简单、直接,和深刻的方法来学习质量和六西格玛…

H2 Database Console未授权访问漏洞复现+利用

1、产品简介 H2是Thomas Mueller提供的一个开源的、纯java实现的关系数据库。H2的主要特点是:非常快,开源,JDBC API;嵌入式和服务器模式;内存数据库;基于浏览器的控制台应用程序。 2、漏洞概述 H2 datab…

virsh 获取虚机IP,网桥ip,brctl,arp使用

第一种方法: allenjettech-WS-C621E-SAGE-Series:~$ sudo virsh domifaddr win10-01Name MAC address Protocol Address -------------------------------------------------------------------------------vnet0 52:54:00:b3:42:28 ipv4 1…

电脑不限时长的录屏软件分享

案例:有没有录屏软件不限时长录制视频? “今天的视频会议特别重要,我想用录屏的方式记录下来。在网上下载了一个录屏软件,录到3分钟的时候,需要解锁高级功能才能继续录制。想问问大家有没有电脑免费不限时长的录屏软件…

四百左右哪款蓝牙耳机比较好?400元价位蓝牙耳机推荐

除了日常通勤以及休息前听歌以外,随着加班变得频繁,工作时也戴起了耳机,由于市面上的耳机种类繁多,因此许多人不知道从而选择,小编发现更多的人是追求性价比,所以整理了一期四百左右性能表现优异的款式给大…

Leetcode 剑指 Offer II 023. 两个链表的第一个重合节点

题目难度: 简单 原题链接 今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定两个单链表的头节点 headA 和 headB ,请找出并返…

vs2022 xp支持情况测试

vs2022默认安装是不支持xp的,生成的可执行文件在xp下报错如图: 1、 若要vs2022支持xp,需要安装xp支持模块,如下图,单个组价,勾选xp支持,右边自动打勾,直接安装即可; 2、工…

2023年第十三届MathorCup高校数学建模挑战赛|C题|电商物流网络包裹应急调运与结构优化问题

【2023 年第十三届 MathorCup 高校数学建模挑战赛】C 题 电商物流网络包裹应急调运与结构优化问题 1 题目 电商物流网络由物流场地(接货仓、分拣中心、营业部等)和物流场地之间的运输线路组成,如图 1 所示。受节假日和“双十一”、“618”等…

QTableView中添加代理控件

目录 1、设置样式文件 2、set base attribute 3、设置model 4、设置表头 5、设置数据 6、添加代理控件 6.1 添加 QSpinBox 代理 6.2 添加 QComboBox 代理 6.3 添加 QPushButton 代理 6.4 添加 CheckBox 代理 6.5 添加 Pixmap 代理 6.6 添加 LineEdit 代理 6…

部署环境从docker swarm迁移到k8s后kie-server的发布方式变化

书接swarm https://cloud.tencent.com/developer/news/475316 swarm的集群部署非常简单,但领导说docker和 docker swarm都不想用 换k8s ok 哦另外, k8s的CRI运行时 也不用docker 而是用containerd 完成. 但发现一个问题 ,k8s没有暂停pod的概念. 同时containerd没有暂停容器的…

Scrum of Scrums规模化敏捷开发管理全流程

Scrum of Scrums是轻量化的规模化敏捷管理模式,Leangoo领歌可以完美支持Scrum of Scrums多团队敏捷管理。 Scrum of Scrums的场景 Scrum of Scrums是指多个敏捷团队共同开发一个大型产品、项目或解决方案。Leangoo提供了多团队场景下的产品路线图规划、需求管理、…

可选择的Elasticsearch好用的可视化客户端工具

前言 常言道:工欲善其事,必先利其器。对于我们开发和测试同学来说,在日常的工作中有一款趁手的工具那真实如虎添翼啊,工作效率可是蹭蹭蹭的往上长,节省下来的时间摸摸鱼该有多好啊。最近我们系统开始使用elasticsearc…

【计算机网络-数据链路层】广域网(WAN)

文章目录1 广域网的概念2 PPP 协议2.1 PPP 帧的格式2.2 PPP 帧的透明传输2.2.1 面向字节的透明传输——字符填充法2.2.2 面向比特的透明传输——零比特填充法2.3 PPP 协议的工作状态1 广域网的概念 广域网(Wide Area Network,WAN)&#xff0…

【python】面向对象编程,这么大一个对象你不要嘛?

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 万物皆是对象,Python当然支持面向对象编程。 类和对象是面向对象编程的两个主要方面,类创建一个新的对象,对象是这个类的实例。 对象可以使用类的变量,属于对象或类的变量…