Slimming剪枝方法

news2024/11/18 20:00:59

本文参考:5-剪枝后模型参数赋值_哔哩哔哩_bilibiliz

 https://github.com/foolwood/pytorch-slimming

一、模型剪枝理论说明

论文:Learning Efficient Convolutional Networks through Network Slimming

(1)卷积后得到多个特征图(channel=64, 128, 256…),这些图不一定都重要,所以量化计算特征图的重要性

(2)训练模型的时候需要加入一些策略,让权重参数有明显的大小之分,从而筛选重要的特征图

Channel scaling factors里面的数值为特征图的打分,直观理解为分值大的特征图需要保留,分值小的特征图可以去掉。

二、计算特征图重要性

Network slimming ,利用BN层中的缩放因子Ƴ

BN的理论支持:

,使得数据为(0,1)正态分布。

整体感觉是一个归一化操作,但是BN中需要额外引入两个可训练的参数:Ƴ和β

BatchNorm的本质:

(1)BN要做的就是把越来越偏离的分布给拉回来

(2)再重新规范化到均值为0方差为1的标准正态分布

(3)这样能够使得激活函数在数值层面更敏感,训练更快。

(4)产生的问题:经过BN之后,把数值分布强制在了非线性函数的线性区域中

针对第(3)点解释:

在激活函数中,两边处于饱和区域不敏感,接近于0位置非饱和处于敏感区域。

针对第(4)点解释:

BN将数据强制压缩到中间红色区域的线性部分,F(x)只做仿射变化,F=sigmoid,多个仿射变化的叠加仍然是仿射变化,添加再多隐藏层与单层神经网络是等价的。

所以,BN需要保证一些非线性,对规范后的结果再进行变化

添加两个参数后重新训练:

,这两个参数是网络训练过程中得到的,而不是超参给的。

该公式相当于BN的逆变换,

相当于对正态分布进行一些改变,拉动一下,变一下形状,做适当的还原。

Ƴ值越大越重要,那么该特征图调整的幅度越大,说明该特征图越重要

三、让特征图重要度两极分化更明显

使用L1正则化对参数进行稀疏操作。

L1求导后为:sign(Θ),相当于稳定前进,都为1,最后学成0了

L2求导后为:Θ,相当于越来越慢,很多参数都接近0,平滑。

论文核心:

四、剪枝流程

使用到的vgg模型架构:

import torch
import torch.nn as nn
import math
from torch.autograd import Variable

class vgg(nn.Module):
    def __init__(self, dataset='cifar10', init_weights=True, cfg=None):
        super(vgg, self).__init__()
        if cfg is None:
            cfg = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512]
        self.feature = self.make_layers(cfg, True)

        if dataset == 'cifar10':
            num_classes = 10
        elif dataset == 'cifar100':
            num_classes = 100
        self.classifier = nn.Linear(cfg[-1], num_classes)
        if init_weights:
            self._initialize_weights()


    def make_layers(self, cfg, batch_norm=False):
        layers = []
        in_channels = 3
        for v in cfg:
            if v == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1, bias=False)
                if batch_norm:
                    layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
                else:
                    layers += [conv2d, nn.ReLU(inplace=True)]
                in_channels = v
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.feature(x)
        x = nn.AvgPool2d(2)(x)
        x = x.view(x.size(0), -1)
        y = self.classifier(x)
        return y

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(0.5)
                m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                m.weight.data.normal_(0, 0.01)
                m.bias.data.zero_()

if __name__ == '__main__':
    net = vgg()
    x = Variable(torch.FloatTensor(16, 3, 40, 40))
    y = net(x)
    print(y.data.shape)

1、原始模型训练:

(1)BN的L1稀疏正则化:使用次梯度下降法,对BN层的权重进行再调整

(2)训练完成后主要保存原始模型的参数信息

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
from vgg import vgg
import shutil
from tqdm import tqdm

learning_rate = 0.1
momentum = 0.9
weight_decay = 1e-4
epochs = 3
log_interval = 100
batch_size = 100
sparsity_regularization = True
scale_sparse_rate = 0.0001

checkpoint_model_path = 'checkpoint,pth.tar'
best_model_path = 'model_best.pth.tar'

train_loader = torch.utils.data.DataLoader(
    datasets.CIFAR10('D:\\ai_data\\cifar10', train=True, download=True,
                     transform=transforms.Compose([
                         transforms.Pad(4),
                         transforms.RandomCrop(32),
                         transforms.RandomHorizontalFlip(),
                         transforms.ToTensor(),
                         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
                     ])),
    batch_size=batch_size, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.CIFAR10('D:\\ai_data\\cifar10', train=False, transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])),
    batch_size=batch_size, shuffle=True)

model = vgg()
model.cuda()
optimizer = optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum, weight_decay=weight_decay)

def train(epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(tqdm(train_loader)):
        data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)
        loss.backward()
        if sparsity_regularization:
            updateBN()
        optimizer.step()
        if batch_idx % log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.1f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))

def test():
    model.eval()
    test_loss = 0
    correct = 0
    for data, target in tqdm(test_loader):
        data , target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        test_loss += F.cross_entropy(output, target, size_average=False).item()
        pred = output.data.max(1, keepdim=True)[1]
        correct += pred.eq(target.data.view_as(pred)).cpu().sum()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.1f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
    return correct / float(len(test_loader.dataset))

def save_checkpoint(state, is_best, filename=checkpoint_model_path):
    torch.save(state, filename)
    if is_best:
        shutil.copyfile(filename, best_model_path)

def updateBN():
    for m in model.modules():
        if isinstance(m, nn.BatchNorm2d):
            m.weight.grad.data.add_(scale_sparse_rate * torch.sign(m.weight.data)) # L1,使用次梯度下降

best_prec = 0
for epoch in range(epochs):
    train(epoch)
    prec = test()
    is_best = prec > best_prec
    best_prec = max(prec, best_prec)
    save_checkpoint({
        'epoch': epoch + 1,
        'state_dict': model.state_dict(),
        'best_prec': best_prec,
        'optimizer': optimizer.state_dict()
    }, is_best)

2、模型剪枝

(1)剪枝过程主要分为两部分:第一部分是计算mask,第二部分是根据mask调整各层的shape

(2)BN层通道数:Conv -> BN -> ReLU -> MaxPool--à Linear,所以BN的输入维度对应Conv的输出通道数

(3)BN层总通道数:将所有BN层的通道数进行汇总

(4)BN层剪枝百分位:取总通道数的百分位得到具体的float值,大于该值的通道对应的mask置为1,否则对应的mask置为0

(5)改变权重weight:BN层抽取mask为1的通道数的值,该操作会改变BN的shape,从而上下游操作中的Conv和Linear也需要被动做出调整,对Maxpool和ReLu的通道数无影响

(6)Conv层的参数为[out_channels, in_channels, kernel_size1, kernel_size2],所以需要调整两次,先对in_channels进行调整,再对out_channels进行调整。Conv初始输入为RGB的3通道。

假如计算出的保留通道数信息为:

[48, 60, 115, 118, 175, 163, 141, 130, 259, 267, 258, 249, 225, 212, 234, 97]

Conv的输入输出变为:

In shape: 3 Out shape:48

In shape: 48 Out shape:60

In shape: 60 Out shape:115

In shape: 115 Out shape:118

……

In shape: 234 Out shape:97

(7)保存模型时,一方面把有用的参数信息保存了下来,同时剪枝后的最新的模型结构参数也保存了,方便后续再训练时构建新的模型结构

import os
import torch
import torch.nn as nn
from torch.autograd import Variable
from torchvision import datasets, transforms
from vgg import vgg
import numpy as np
from tqdm import tqdm

percent = 0.5
batch_size = 100
raw_model_path = 'model_best.pth.tar'
save_model_path = 'prune_model.pth.tar'

model = vgg()
model.cuda()
if os.path.isfile(raw_model_path):
    print("==> loading checkpoint '{}'".format(raw_model_path))
    checkpoint = torch.load(raw_model_path)
    start_epoch = checkpoint['epoch']
    best_prec = checkpoint['best_prec']
    model.load_state_dict(checkpoint['state_dict'])
    print("==> loaded checkpoint '{}'(epoch {}) Prec:{:f}".format(raw_model_path, start_epoch, best_prec) )
print(model)

total = 0
for m in model.modules():
    if isinstance(m, nn.BatchNorm2d):
        total += m.weight.data.shape[0]

bn = torch.zeros(total)
index = 0
for m in model.modules():
    if isinstance(m, nn.BatchNorm2d):
        size = m.weight.data.shape[0]
        bn[index : index + size] = m.weight.data.abs().clone()
        index += size

y, i = torch.sort(bn)
thre_index = int(total * percent)
thre = y[thre_index]

pruned = 0
cfg = []
cfg_mask = []
for k, m in enumerate(model.modules()):
    if isinstance(m, nn.BatchNorm2d):
        weight_copy = m.weight.data.clone()
        mask = weight_copy.abs().gt(thre).float().cuda()
        pruned += mask.shape[0] - torch.sum(mask)
        m.weight.data.mul_(mask)
        m.bias.data.mul_(mask)
        cfg.append(int(torch.sum(mask)))
        cfg_mask.append(mask.clone())
        print('layer index: {:d} \t total channel: {:d} \t remaining channel: {:d}'.format(k, mask.shape[0], int(torch.sum(mask))))
    elif isinstance(m, nn.MaxPool2d):
        cfg.append('M')
pruned_ratio = pruned / total

print('pruned_ratio: {},Pre-processing Successful!'.format(pruned_ratio))

# simple test model after Pre-processing prune(simple set BN scales to zeros)
def test():
    test_loader = torch.utils.data.DataLoader(
        datasets.CIFAR10('D:\\ai_data\\cifar10', train=False, transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
        ])),
        batch_size=batch_size, shuffle=True)
    model.eval()

    correct = 0
    for data, target in tqdm(test_loader):
        data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        pred = output.data.max(1, keepdim=True)[1]
        correct += pred.eq(target.data.view_as(pred)).cpu().sum()

    print('\nTest set: Accuracy: {}/{} ({:.1f}%)\n'.format(
        correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset)))

test()

# make real prune
print(cfg)
new_model = vgg(cfg=cfg)
new_model.cuda()

layer_id_in_cfg = 0  # cfg中的层数索引
start_mask = torch.ones(3)
end_mask = cfg_mask[layer_id_in_cfg]
for [m0,  m1] in zip(model.modules(), new_model.modules()):
    if isinstance(m0, nn.BatchNorm2d):
        idx1 = np.squeeze(np.argwhere(np.asarray(end_mask.cpu().numpy())))
        m1.weight.data = m0.weight.data[idx1].clone()
        m1.bias.data = m0.bias.data[idx1].clone()
        m1.running_mean = m0.running_mean[idx1].clone()
        m1.running_var = m0.running_var[idx1].clone()
        layer_id_in_cfg += 1
        start_mask = end_mask.clone()
        if layer_id_in_cfg < len(cfg_mask):  # do not change in Final FC
            end_mask = cfg_mask[layer_id_in_cfg]
    elif isinstance(m0, nn.Conv2d):
        idx0 = np.squeeze(np.argwhere(np.asarray(start_mask.cpu().numpy())))
        idx1 = np.squeeze(np.argwhere(np.asarray(end_mask.cpu().numpy())))
        print('In shape: {:d} Out shape:{:d}'.format(idx0.shape[0], idx1.shape[0]))
        w = m0.weight.data[:, idx0, :, :].clone()
        w = w[idx1, :, :, :].clone()
        m1.weight.data = w.clone()
    elif isinstance(m0, nn.Linear):
        idx0 = np.squeeze(np.argwhere(np.asarray(start_mask.cpu().numpy())))
        m1.weight.data = m0.weight.data[:, idx0].clone()

torch.save({'cfg': cfg, 'state_dict': new_model.state_dict()}, save_model_path)
print(new_model)
model = new_model
test()


3、再训练

剪枝后保存的模型参数相当于训练过程中的一个checkpoint,根据新的模型结构,在此checkpoint的基础上再进行训练,直到得到满意的指标。

import torch
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
from vgg import vgg
import shutil
from tqdm import tqdm

learning_rate = 0.1
momentum = 0.9
weight_decay = 1e-4
epochs = 3
log_interval = 100
batch_size = 100
sparsity_regularization = True
scale_sparse_rate = 0.0001

prune_model_path = 'prune_model.pth.tar'
prune_checkpoint_path = 'pruned_checkpoint.pth.tar'
prune_best_model_path = 'pruned_model_best.pth.tar'


train_loader = torch.utils.data.DataLoader(
    datasets.CIFAR10('D:\\ai_data\\cifar10', train=True, download=True,
                     transform=transforms.Compose([
                         transforms.Pad(4),
                         transforms.RandomCrop(32),
                         transforms.RandomHorizontalFlip(),
                         transforms.ToTensor(),
                         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
                     ])),
    batch_size=batch_size, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.CIFAR10('D:\\ai_data\\cifar10', train=False, transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])),
    batch_size=batch_size, shuffle=True)

checkpoint = torch.load(prune_model_path)
model = vgg(cfg=checkpoint['cfg'])
model.cuda()
model.load_state_dict(checkpoint['state_dict'])

optimizer = optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum, weight_decay=weight_decay)

def train(epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(tqdm(train_loader)):
        data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.1f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))

def test():
    model.eval()
    test_loss = 0
    correct = 0
    for data, target in tqdm(test_loader):
        data , target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        test_loss += F.cross_entropy(output, target, size_average=False).item()
        pred = output.data.max(1, keepdim=True)[1]
        correct += pred.eq(target.data.view_as(pred)).cpu().sum()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.1f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
    return correct / float(len(test_loader.dataset))

def save_checkpoint(state, is_best, filename=prune_checkpoint_path):
    torch.save(state, filename)
    if is_best:
        shutil.copyfile(filename, prune_best_model_path)

best_prec = 0
for epoch in range(epochs):
    train(epoch)
    prec = test()
    is_best = prec > best_prec
    best_prec = max(prec, best_prec)
    save_checkpoint({
        'epoch': epoch + 1,
        'state_dict': model.state_dict(),
        'best_prec': best_prec,
        'optimizer': optimizer.state_dict()
    }, is_best)

4、原始模型和剪枝后模型比较:

在cifar10上通过vgg模型分别迭代3次。

原始模型为156M,准确率为70%左右

剪枝后模型为36M,准确率为76%左右

备注:最好是原始模型达到顶峰时再剪枝,此时再比较剪枝前后的准确率影响。

 

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

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

相关文章

通过逻辑回归和感知器算法对乳腺癌数据集breastCancer和鸢尾花数据集iris进行线性分类

逻辑回归和感知器算法进行线性分类 代码使用了LogisticRegression和Perceptron两种分类方法 # 使用LogisticRegreeion分类器学习和测试 lr LogisticRegression() lr.fit(X_train_scaler, y_train) y_pred_lr lr.predict(X_test_scaler)#定义感知机 perceptron Perceptron(…

N3-PEG-ALD,Azide-PEG-Aldehyde,醛基-聚乙二醇-叠氮

1、名称 英文&#xff1a;N3-PEG-ALD&#xff0c;Azide-PEG-Aldehyde 中文&#xff1a;叠氮-聚乙二醇-醛基 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Aldehyde / Acetal PEG Azide PEG 4、分子量&#xff1a;可定制&#xff0c;5000 N3-PEG-ALD、10000 叠氮-PEG…

用html做一个漂亮的网站【茶文化12页】期末网页制作 HTML+CSS网页设计实例 企业文化网站制作

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

【仿牛客网笔记】项目进阶,构建安全高效的企业服务——Spring Security

https://spring.io/projects/spring-security 认证判断用户有没有登录。 授权 是访问有没有访问的权限 Spring MVC 的核心组件是DispatcherServlet&#xff0c;所有的组件都是交给DispatcherServlet处理&#xff0c;然后将请求分发给控制器&#xff0c;具体由某个控制器控制请求…

如何在微信上制作小程序?【制作小程序的方式】

很多人想知道如何在微信上制作小程序&#xff0c;毕竟小程序现在已经成为了我们每天都会使用到的轻应用。在微信上制作小程序之前&#xff0c;要先了解自己想要做出什么的小程序&#xff0c;才能下手开展小程序制作。那么下面就介绍三种如何在微信上制作小程序的方式。 1、懂编…

彻底搞懂SwaggerKnife4j使用方法

&#xff08;一&#xff09;、准备 1、SpringBoot项目 说明&#xff1a;前后端分离前提下&#xff0c;前端是一个独立项目&#xff0c;因此该SpringBoot无需添加Thymeleaf依赖&#xff01; 2、vo类 Employee类&#xff1a; package com.soft.vo;import java.util.Date;publ…

Hadoop与Spark的使用,HBase分布式数据库安装及操作实验

docker pull harisekhon/hbase:1.3 docker run -d --name hbase001 -p 16010:16010 harisekhon/hbase:1.3 进入环境 docker exec -it hbase001 bash hbase shell 按照一个特定的值来查找 hbase(main):003:0> scan students,{FILTER>"ValueFilter(,binary:20202…

Matplotlib教程一

文章目录一.简介二.Mapltlib的简单使用2.1 Pylot的API2.2 Pylab模块2.3 matplotlib简单绘图三.Mapltlib进阶使用3.1 面向对象思想画图3.2 Figure类3.3 Axes类3.4 画布上创建多个子图3.5 图中添加网格3.6 设置图片轴线3.7 保存图片一.简介 matplotlib是最初由John D.Hunter于20…

Shell脚本编程(一) —— 变量定义(用户自定义变量、位置变量、预定义变量、环境变量)

目录 1、用户自定义变量 2、位置变量 3、预定义变量 4、环境变量&#xff08;一次性全局变量&#xff09; 5、永久全局变量&#xff08;重启以后依然存在的变量&#xff09; (1) 本地变量文件 (2) 全局变量文件 1、用户自定义变量 定义变量&#xff1a; 变量名value …

九、propTypes验证

目标 理解类型验证的必要性 灵活掌握类型验证的使用 知识点 在给react组件传属性的的时候&#xff0c;我们可以定义属性的类型&#xff0c;此时我们需要下载prop-types这个库。 安装prop-types npm install prop-types -S引入和使用 import PropTypes from prop-types;cla…

winform语言切换C#设计笔记(八)

一、修改当前区域性 string languageName“zh-CN”; Thread.CurrentThread.CurrentUICulture new CultureInfo(languageName); 二、定义语言切换类Mullanguage或方法如下&#xff1a; private static Dictionary<string, ResourceManager> ResManagerDic new Dictionar…

【ESP32_8266_WiFi (十五)】ESP8266 OTA 操作说明

文章目录ESP8266 OTA 操作说明1 通过数据线上传初始示例程序2 通过Arduino IDE正确选择OTA端口3 认证并上传程序4 OTA的局限性ESP8266 OTA 操作说明 所谓OTA&#xff0c;就是Over-The-Air的缩写。有人将其翻译为“空中下载”&#xff0c;也有翻译为“隔空传输”。无论如何翻译…

Java高并发编程卷二(二) 锁

文章目录三 锁3.1 偏向锁3.1.1 为啥需要偏向锁&#xff1f;3.1.2 原理3.1.3 解释3.1.4 案例3.1.5 撤销与膨胀3.1.5.1 撤销3.1.5.2 膨胀3.2 轻量级锁3.2.1 为啥需要轻量级锁&#xff1f;3.2.2 原理3.2.3 案例3.2.4 分类3.2.5 膨胀3.3 重量级锁3.3.1 原理3.3.2 对象监视器详细介绍…

zookeeper学习笔记

zookeeper学习笔记1.Zookeeper概念2.Zookeeper命令操作2.1数据模型2.1.1数据结构2.1.2节点类型2.2服务端命令2.3客户端命令-基本命令2.4客户端命令-高级点命令3.Zookeeper JavaAPI操作3.1Cutor介绍3.2Cutor API常用操作-增删改查3.2.1建立连接3.2.2创建节点3.2.3删除节点3.2.4修…

js 回到顶部逻辑实现和elementUI源码解析

回到顶部 大家或多或少都会遇到“回到顶部”这样的需求&#xff0c;在此分享这个技术点以及可能遇到的问题。再分析element源码。 回到顶部代码实现 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta http-…

基于PHP+MySQL的个人博客系统毕设

随着时代和网络的发展,人们越来越希望通过多种模式来展示自己。于是个人博客就出现了,它可以更好的让人们来记录自己的工作和学习方式。博客不仅仅可以让自己抒发个人感情,还可以展示自己真实的生活,从而建立起一种友好的交友平台。 PHP个人博客系统毕设系统分为前台和后台两部…

Python每日一练 02

Python每日一练 02 文章目录Python每日一练 02一、对象二、对象属性三、赋值一、对象 Python中所有数字、序列、集合、映射、类、实例、异常、模块、类与类的实例、函数、方法、布尔值、空值等都被称为对象。 二、对象属性 每个对象都有3个基本属性&#xff1a; 类型(type)…

Flink-源算子Source(获取数据源)的使用

5.1 整体介绍 获取执行环境读取数据源定义基于数据的转换操作定义计算结果的输出位置触发程序执行 5.2 创建集成环境 5.2.1 获取执行环境 批处理getExecutionEnvironment 提交命令行设置 bin/flink run -Dexecution.runtime-modeBATCH ...代码 StreamExecutionEnvironme…

pip Command Not Found – Mac 和 Linux 错误被解决

使用Python时&#xff0c;可能需要安装和使用某些软件包。有一个命令可用于’pip‘ 使用pip&#xff0c;您可以安装、升级和卸载各种Python包。在本文中&#xff0c;您将学习如何使用它&#xff0c;以及如何处理pip错误。 如何使用 pip Pip是一个可以在Linux或Mac命令行上使用…

HTTP(http+抓包Fiddler+协议格式+请求+响应)

目录 &#x1f984;1. 了解HTTP &#x1f984;2. 抓包 &#x1f984;3. http协议格式 &#x1f432;3.1 完整的HTTP请求格式 &#x1f432;3.2 完整的HTTP响应的格式 HTTP请求 &#x1f984;4. 认识URL &#x1f984;5. http中的"方法" &#x1f432;5.1…