Pytorch | 对比Pytorch中的十种优化器:基于CIFAR10上的ResNet分类器

Pytorch | 对比Pytorch中的十种优化器:基于CIFAR10上的ResNet分类器

  • CIFAR10数据集
  • ResNet
    • 提出背景
    • 网络结构特点
    • 工作原理
    • 优势
  • 代码实现分析
    • utils.py
    • main.py
      • 导入必要的库
      • 设备选择与数据预处理定义
      • 加载训练集和测试集
      • 主函数部分
        • 训练部分
        • 测试部分
  • 结果
    • 10种优化器对应的训练损失下降曲线
      • Adadelta
      • Adagrad
      • Adam
      • Adamax
      • AdamW
      • NAdam
      • RMSprop
      • Rprop
      • SGD
      • SparseAdam
    • 测试结果
  • 代码汇总
    • utils.py
    • main.py

上篇文章中实现了十种优化算法:Python | 从零实现10种优化算法并比较
这篇文章我们用Pytorch上不同的优化器在CIFAR10数据集上训练ResNet模型,比较不同优化器的效果。

CIFAR10数据集

CIFAR-10数据集是由加拿大高级研究所(CIFAR)收集整理的用于图像识别研究的常用数据集,基本信息如下:

  • 数据规模:该数据集包含60,000张彩色图像,分为10个不同的类别,每个类别有6,000张图像。通常将其中50,000张作为训练集,用于模型的训练;10,000张作为测试集,用于评估模型的性能。
  • 图像尺寸:所有图像的尺寸均为32×32像素,这相对较小的尺寸使得模型在处理该数据集时能够相对快速地进行训练和推理,但也增加了图像分类的难度。
  • 类别内容:涵盖了飞机(plane)、汽车(car)、鸟(bird)、猫(cat)、鹿(deer)、狗(dog)、青蛙(frog)、马(horse)、船(ship)、卡车(truck)这10个不同的类别,这些类别都是现实世界中常见的物体,具有一定的代表性。

下面是一些示例样本:
在这里插入图片描述

ResNet

ResNet(Residual Network)即残差网络,是由微软研究院的何恺明等人在2015年提出的一种深度卷积神经网络架构,在图像识别等计算机视觉任务中取得了巨大成功。

提出背景

随着神经网络深度的增加,出现了梯度消失/爆炸以及网络退化等问题,导致训练难度增大,精度饱和甚至下降。ResNet通过引入残差连接(shortcut connection)有效地解决了这些问题,使得训练极深的网络成为可能。

网络结构特点

  • 残差块(Residual Block):这是ResNet的核心结构。它由多个卷积层组成,并且在卷积层之间引入了shortcut connection。一个基本的残差块包含两个3×3卷积层,中间有一个ReLU激活函数,其输入可以直接跳过这两个卷积层与输出相加,这种结构使得网络能够学习到残差函数,即输入与输出之间的差异,而不是直接学习输出本身。
    在这里插入图片描述

  • 多种层数的网络结构:ResNet有多种不同层数的架构,如ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152等,其中数字表示网络的层数。层数越深,模型的表示能力越强,但计算成本也越高,训练难度也相应增大。
    在这里插入图片描述

  • 瓶颈结构(Bottleneck):在较深的ResNet架构如ResNet-50及以上中,使用了瓶颈结构来减少计算量。它由1×1、3×3和1×1三个卷积层组成,1×1卷积层用于降低输入特征图的通道数,3×3卷积层进行主要的特征提取,最后1×1卷积层用于恢复通道数。

工作原理

在正向传播时,输入特征图通过残差块中的卷积层进行特征提取,得到输出特征图。然后将输入特征图与输出特征图相加,得到最终的输出。如果残差块中的卷积层没有学到有用的特征,那么它们的输出接近于零,此时最终的输出就近似等于输入,即网络可以学习到恒等映射。在反向传播时,由于shortcut connection的存在,梯度可以直接通过捷径传播到较早的层,避免了梯度消失或爆炸的问题,使得网络能够更容易地训练深层网络。

优势

  • 有效解决梯度消失和退化问题:使得训练非常深的网络成为可能,能够提取更高级的图像特征,从而提高了模型的准确性和泛化能力。
  • 降低模型训练难度:残差连接使得网络在训练过程中更容易收敛,减少了对超参数调整的依赖,提高了训练效率。
  • 模型具有很强的可扩展性:可以通过增加残差块的数量来构建更深的网络,以适应不同的任务和数据集。

代码实现分析

utils.py

该文件中我们预先为调用不同的优化器做好准备,对于不同的优化器参数,为了方便,这里只设置学习率,其余参数可根据需要自己设置。

import torch
import torch.optim as optimdef get_optimizer(optimizer_name, model_parameters, lr=0.001, **kwargs):"""根据传入的优化器名称返回对应的优化器实例。参数:- optimizer_name: 优化器名称,如 "Adadelta", "Adagrad" 等。- model_parameters: 模型的可训练参数,通常通过 model.parameters() 获取。- lr: 学习率,默认值为 0.001。- **kwargs: 其他特定优化器需要的额外参数。返回:- optimizer: 对应的优化器实例。"""if optimizer_name == "Adadelta":return optim.Adadelta(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Adagrad":return optim.Adagrad(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Adam":return optim.Adam(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Adamax":return optim.Adamax(model_parameters, lr=lr, **kwargs)elif optimizer_name == "AdamW":return optim.AdamW(model_parameters, lr=lr, **kwargs)elif optimizer_name == "NAdam":return optim.NAdam(model_parameters, lr=lr, **kwargs)elif optimizer_name == "RMSprop":return optim.RMSprop(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Rprop":return optim.Rprop(model_parameters, lr=lr, **kwargs)elif optimizer_name == "SGD":return optim.SGD(model_parameters, lr=lr, **kwargs)elif optimizer_name == "SparseAdam":return optim.SparseAdam(model_parameters, lr=lr, **kwargs)else:raise ValueError(f"不支持的优化器名称: {optimizer_name}")

main.py

本文这里使用Pytorch自带的ResNet18模型。

以下是对上述代码的分块讲解:

导入必要的库

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
from utils import get_optimizerimport warnings
warnings.filterwarnings("ignore")import sslssl._create_default_https_context = ssl._create_unverified_context
  • from utils import get_optimizer 从前面的 utils 模块中导入 get_optimizer 函数,用于调用不同的优化器;
  • warnings.filterwarnings("ignore") 用于忽略一些可能出现的警告信息,让代码运行时输出更简洁;
  • ssl 相关的代码是为了解决在下载数据集时可能出现的SSL验证问题,通过创建一个默认的不验证SSL证书的上下文来允许数据正常下载。

设备选择与数据预处理定义

# 检查cuda是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 定义数据预处理操作,将图像转换为张量并进行归一化
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])
  • 检查 gpu(cuda) 是否可用;
  • transforms.Normalize 对图像张量进行归一化操作,传入的两个元组分别表示每个通道的均值和标准差

加载训练集和测试集

# 加载训练集,设置batch_size等参数
# 这里batch_size设为128,可按需调整
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,shuffle=True, num_workers=2)# 加载测试集
testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=128,shuffle=False, num_workers=2)

这部分代码用于加载CIFAR-10数据集,它是一个常用的图像分类数据集,包含10个不同类别的图像。

对于训练集:

  • torchvision.datasets.CIFAR10 函数用于创建训练集对象 trainset,其中 root='./data' 指定了数据集下载和保存的根目录(如果不存在会自动下载到该目录下),train=True 表示加载的是训练集部分,download=True 表示如果数据集不存在则自动下载,transform=transform 表示应用前面定义好的数据预处理操作。
  • torch.utils.data.DataLoader 用于将数据集 trainset 包装成一个可迭代的数据加载器 trainloader,设置 batch_size=128 意味着每次迭代会返回一个包含128张图像及其对应标签的批次数据,shuffle=True 会在每个训练轮次开始时打乱数据顺序,num_workers=2 表示使用2个子进程来并行加载数据,加快数据读取速度。

对于测试集:

  • 同样使用 torchvision.datasets.CIFAR10 函数创建 testset,不过 train=False 表示加载的是测试集部分,其他参数作用和训练集加载时类似。
  • 再通过 torch.utils.data.DataLoader 创建 testloader,只是 shuffle=False 因为测试集一般不需要打乱顺序。

主函数部分

训练部分

训练部分实现了使用10种优化器训练,共训练10个epoch,并记录损失下降情况以可视化,具体可参考下面代码和其中注释:

# 定义类别标签,CIFAR-10有10个类别
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
if __name__ == "__main__":# 使用不同的优化器optimizer_names = ["Adadelta", "Adagrad", "Adam", "Adamax", "AdamW", "NAdam", "RMSProp", "RProp", "SGD", "SparseAdam"]for optimizer_name in optimizer_names:print(f"========= Optimizer: {optimizer_name} ==========")# 使用PyTorch自带的ResNet18模型,修改全连接层输出维度为10(对应CIFAR-10的类别数)model = torchvision.models.resnet18(pretrained=False)num_ftrs = model.fc.in_featuresmodel.fc = nn.Linear(num_ftrs, 10)model = model.to(device)# 定义交叉熵损失函数,常用于分类任务criterion = nn.CrossEntropyLoss()# 定义优化器optimizer = get_optimizer(optimizer_name, model.parameters(), lr=0.001)# 训练轮数,设为10轮,可根据实际情况更改num_epochs = 10loss_history = []for epoch in range(num_epochs):running_loss = 0.0for i, data in enumerate(trainloader, 0):# 获取输入数据和标签inputs, labels = datainputs = inputs.to(device)labels = labels.to(device)# 梯度清零optimizer.zero_grad()# 前向传播 + 计算损失outputs = model(inputs)loss = criterion(outputs, labels)# 反向传播并更新权重loss.backward()optimizer.step()running_loss += loss.item()loss_history.append(loss.item())if i % 100 == 99:    # 每100个小批次打印一次平均损失print(f'Epoch: {epoch + 1}  Batch: {i + 1}  loss: {running_loss / 100}')running_loss = 0.0# 绘制并保存损失下降曲线plt.plot(loss_history)plt.xlabel('Iteration')plt.ylabel('Loss')plt.title(f'Loss Curve by {optimizer_name}')plt.savefig(f'results\\loss_curve_{optimizer_name}.png')  # 保存图像为 loss_curve.png 文件,可根据需求修改文件名和路径plt.close()
测试部分

测试部分使用已经训练好的模型对测试集进行预测,具体参考下面代码:

    correct = 0total = 0with torch.no_grad():for data in testloader:images, labels = dataimages = images.to(device)labels = labels.to(device)outputs = model(images)_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()print(f'Optimizer: {optimizer_name} -- Accuracy of the network on the 10000 test images: {100 * correct / total}%')

结果

10种优化器对应的训练损失下降曲线

Adadelta

在这里插入图片描述

Adagrad

在这里插入图片描述

Adam

在这里插入图片描述

Adamax

在这里插入图片描述

AdamW

在这里插入图片描述

NAdam

在这里插入图片描述

RMSprop

在这里插入图片描述

Rprop

在这里插入图片描述

SGD

SparseAdam

本文训练到这里时,报错:RuntimeError: SparseAdam does not support dense gradients, please consider Adam instead .
原因: SparseAdam 优化器主要是设计用于处理稀疏梯度的场景,也就是梯度张量中大部分元素为零的情况(比如在处理稀疏的文本数据表示等情况时)。而在当前的代码应用场景中,很可能模型计算得到的梯度是密集的(即梯度张量中元素大多是非零值),这就导致 SparseAdam 优化器无法正常处理这样的梯度,进而抛出这个错误提示,建议你考虑使用 Adam 优化器来替代 SparseAdam 优化器。
----因此这里不再给出SparseAdam的优化结果----

测试结果

在这里插入图片描述
从测试结果来看,刨除 SparseAdamAdam 优化器的训练效果最优,Adadelta 优化器的训练效果最差.

代码汇总

项目结构:

|--data
|--results
|--utils.py
|--main.py

utils.py

import torch
import torch.optim as optimdef get_optimizer(optimizer_name, model_parameters, lr=0.001, **kwargs):"""根据传入的优化器名称返回对应的优化器实例。参数:- optimizer_name: 优化器名称,如 "Adadelta", "Adagrad" 等。- model_parameters: 模型的可训练参数,通常通过 model.parameters() 获取。- lr: 学习率,默认值为 0.001。- **kwargs: 其他特定优化器需要的额外参数。返回:- optimizer: 对应的优化器实例。"""if optimizer_name == "Adadelta":return optim.Adadelta(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Adagrad":return optim.Adagrad(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Adam":return optim.Adam(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Adamax":return optim.Adamax(model_parameters, lr=lr, **kwargs)elif optimizer_name == "AdamW":return optim.AdamW(model_parameters, lr=lr, **kwargs)elif optimizer_name == "NAdam":return optim.NAdam(model_parameters, lr=lr, **kwargs)elif optimizer_name == "RMSprop":return optim.RMSprop(model_parameters, lr=lr, **kwargs)elif optimizer_name == "Rprop":return optim.Rprop(model_parameters, lr=lr, **kwargs)elif optimizer_name == "SGD":return optim.SGD(model_parameters, lr=lr, **kwargs)elif optimizer_name == "SparseAdam":return optim.SparseAdam(model_parameters, lr=lr, **kwargs)else:raise ValueError(f"不支持的优化器名称: {optimizer_name}")

main.py

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
from utils import get_optimizerimport warnings
warnings.filterwarnings("ignore")import sslssl._create_default_https_context = ssl._create_unverified_context# 检查cuda是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定义数据预处理操作,将图像转换为张量并进行归一化
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载训练集,设置batch_size等参数
# 这里batch_size设为128,可按需调整
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,shuffle=True, num_workers=4)# 加载测试集
testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=128,shuffle=False, num_workers=4)# 定义类别标签,CIFAR-10有10个类别
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')if __name__ == "__main__":# 使用不同的优化器optimizer_names = ["Adadelta", "Adagrad", "Adam", "Adamax", "AdamW", "NAdam", "RMSprop", "Rprop", "SGD", "SparseAdam"]for optimizer_name in optimizer_names:print(f"========= Optimizer: {optimizer_name} ==========")# 使用PyTorch自带的ResNet18模型,修改全连接层输出维度为10(对应CIFAR-10的类别数)model = torchvision.models.resnet18(pretrained=False)num_ftrs = model.fc.in_featuresmodel.fc = nn.Linear(num_ftrs, 10)model = model.to(device)# 定义交叉熵损失函数,常用于分类任务criterion = nn.CrossEntropyLoss()# 定义优化器optimizer = get_optimizer(optimizer_name, model.parameters(), lr=0.001)# 训练轮数,设为10轮,可根据实际情况更改num_epochs = 10loss_history = []for epoch in range(num_epochs):running_loss = 0.0for i, data in enumerate(trainloader, 0):# 获取输入数据和标签inputs, labels = datainputs = inputs.to(device)labels = labels.to(device)# 梯度清零optimizer.zero_grad()# 前向传播 + 计算损失outputs = model(inputs)loss = criterion(outputs, labels)# 反向传播并更新权重loss.backward()optimizer.step()running_loss += loss.item()loss_history.append(loss.item())if i % 100 == 99:    # 每100个小批次打印一次平均损失# print(f'Epoch: {epoch + 1}  Batch: {i + 1}  loss: {running_loss / 100}')running_loss = 0.0# 绘制并保存损失下降曲线plt.plot(loss_history)plt.xlabel('Iteration')plt.ylabel('Loss')plt.title(f'Loss Curve by {optimizer_name}')plt.savefig(f'results\\loss_curve_{optimizer_name}.png')  # 保存图像为 loss_curve.png 文件,可根据需求修改文件名和路径plt.close()correct = 0total = 0with torch.no_grad():for data in testloader:images, labels = dataimages = images.to(device)labels = labels.to(device)outputs = model(images)_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()print(f'Optimizer: {optimizer_name} -- Accuracy of the network on the 10000 test images: {100 * correct / total}%')

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

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

相关文章

Linux系统操作03|chmod、vim

上文: Linux系统操作02|基本命令-CSDN博客 目录 六、chmod:给文件设置权限 1、字母法 2、数字法(用的最多) 七、vim:代码编写和文本编辑 1、启动和退出 1️⃣启动 2️⃣退出 2、vim基本操作 六、chmod&#x…

【漏洞复现】eking管理易Html5Upload接口存在任意文件上传漏洞

🏘️个人主页: 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞👍+收藏💗支持一下哦 【漏洞复现】eking管理易Html5Upload接口存在任意文件上传漏洞 一、漏洞概述1.1漏洞简介1.2组件描述1.3漏洞描述二、漏洞复现2.1 应用协议2.2 环境搭建2.3 漏洞复现三、…

徐州数字孪生工业互联网可视化技术,赋能新型工业化智能制造工厂

#徐州数字孪生工业互联网#在当下智能制造的热潮之下,徐州作为中国制造业的重要基地,正积极拥抱数字化转型,通过数字孪生工业互联网可视化技术,赋能新型工业化智能制造工厂,引领制造业向更高效、更智能、更绿色的方向发…

C# 探险之旅:第二十四节 - 类型class基础,一场“类”似的奇妙冒险

嘿,勇敢的探险家们!欢迎来到C#王国的“类”似奇妙冒险!今天,我们要深入探索一个神秘而强大的领域——class(类)。想象一下,class就像C#世界里的一块魔法土地,每块土地上都能孕育出独…

解决MyBatis在 Oracle 中使用 IN 语句不能超过 1000 问题

在 Oracle 数据库中,IN 语句常用于查询某个字段是否属于一组特定的值。对于大多数开发者而言,IN 是一种简单直观的查询方式,能够提升开发效率,避免过多的 OR 语句。然而,许多人在使用 IN 语句时可能遇到一个问题&#…

(五)机器学习 - 数据分布

数据分布(Data Distribution)是指数据在不同值或值区间内的分布情况,它描述了数据点在整个数据集中是如何分散或集中的。数据分布可以通过多种方式来分析和表示,包括图形和数值方法。 常见的数据分布特征和描述数据分布的方法&…

C# 探险之旅:第二十三节 - 字符(char):字符小精灵的独舞

嘿,探险家们!欢迎再次踏上C#王国的奇妙旅程。这一节里,我们要深入探索一个非常基础但又极其重要的角色——字符(char)。想象一下,你正在参加一场由单个字母和数字组成的精灵舞会,每个精灵都代表着一个独特的字符。让我…

【JAVA】Java项目实战—分布式微服务项目:分布式文件存储系统

Java分布式微服务项目:分布式文件存储系统 一、背景介绍 随着互联网的快速发展和用户数据量的激增,软件开发中,传统的单体应用架构逐渐无法满足高并发、高可用和可扩展的需求。微服务架构应运而生,它将一个大型应用拆分为多个小…

苹果电脑可以安装windows操作系统吗?Mac OS X/OS X/macOS傻傻分不清?macOS系统的Java支持?什么是macOS的五大API法王?

苹果电脑可以安装windows操作系统吗? 先抛开虚拟机安装,苹果电脑可以安装Windows操作系统。苹果公司提供了一个名为Boot Camp的软件,它允许用户在Mac电脑上安装Windows操作系统。通过Boot Camp,用户可以在启动电脑时选择是要进入macOS还是Wi…

基于stm32的红外测温系统设计(论文+源码)

1总体方案设计 本课题为基于STM32的红外测温系统设计,在此将系统架构设计如图3.1所示, 整个系统包括STM32F103单片机,红外测温模块MLX90614,显示模块OLED12864,蜂鸣器以及按键等构成,在功能上,…

网易游戏分享游戏场景中MongoDB运行和分析实践

在游戏行业中,数据库的稳定和性能直接影响了游戏质量和用户满意度。在竞争激烈的游戏市场中,一个优秀的数据库产品无疑能为游戏的开发和后期的运营奠定良好的基础。伴随着MongoDB在不同类型游戏场景中的应用越来越广泛,许多知名的游戏公司都在…

【杭州电商商城系统开发建设】

杭州电商商城系统开发建设是一项综合性的工程,它涉及到多个方面的内容。以下是对杭州电商商城系统开发建设的详细分析: 需求分析:深入了解用户需求,包括用户群体特征、购物习惯、支付偏好等,为系统设计提供基础。明确…

排序算法(5):归并排序

问题 排序 [30, 24, 5, 58, 18, 36, 12, 42, 39] 归并排序 归并排序采用分治法,将序列分成若干子序列,每个子序列有序后再合并成有序的完整序列。 在数组排序中,如果只有一个数,那么它本身就是有序的。如果有两个数&#xff0…

前端将base64转pdf页面预览

前端将base64转pdf页面预览 <embed :src"pdfList" width"100%" height"100%" type"application/pdf" />pdfList.value data:application/pdf;base64,${res}//后端传jpg或pdf格式可直接 :src“返回内容”显示

MySQL 主从复制与高可用架构

一、MySQL 主从复制概述 &#xff08;一&#xff09;定义与作用 MySQL 主从复制是一种允许在多个 MySQL 数据库服务器之间进行数据同步的技术。简单来说&#xff0c;就是可以把数据从一个 MySQL 服务器&#xff08;主服务器、主节点&#xff09;复制到一个或多个从节点&#…

JSSIP的使用及问题(webRTC,WebSockets)

简介 项目中有一个需要拨打电话的功能&#xff0c;要求实时的进行音频接听&#xff0c;并且可以在电话接听或者挂断等情况下做出相应的操作。jssip作为一个强大的实现实时通信的javascript库&#xff0c;这不门当户对了嘛。 jssip&#xff08;官网&#xff1a; JsSIP - the J…

DP3复现代码运行逻辑全流程(六)—— gen_demonstration_adroit.sh 演示生成与可视化

用于生成演示、培训和评估的脚本都在 Scripts/ 文件夹中 DP3 通过 gen_demonstration 生成演示&#xff0c;即训练数据&#xff0c;例如: bash scripts/gen_demonstration_adroit.sh hammer 这将在 Adroit 环境中生成锤子任务的演示。数据将自动保存在 3D-Diffusion-Policy/…

Python常用字符串排序●sorted()函数--一行语句简洁实现

在Python等编程中&#xff0c;时常会用到字符串排序。 今天在这里只讲讲最常用的Python字符串排序。 同时&#xff0c;只讲sorted()函数方法。 给定一个字符串列表&#xff1a; sl [共和国, 中国, 中华人民共和国, 大中华, 人民共和国]。 第一种排序方法是不使用任何参数…

知从科技总经理受邀参加上海临港新片区商会“湖畔TECS”技术分享沙龙(第五期)

11月26日&#xff0c;上海知从科技有限公司创始人陈荣波先生受邀出席临港新片区商会 “湖畔TECS”技术分享沙龙&#xff08;第五期&#xff09;活动&#xff0c;并在活动上为参会嘉宾们做了主题分享。本次活动由临港新片区商会主办&#xff0c;智能网联汽车创新联盟协办&#x…

【MySQL数据库】Ubuntu下的mysql

目录 1&#xff0c;安装mysql数据库 2&#xff0c;mysql默认安装路径 3&#xff0c;my.cnf配置文件 4&#xff0c;mysql运用的相关指令及说明 5&#xff0c;数据库、表的备份和恢复 mysql是一套给我们提供数据存取的&#xff0c;更加有利于管理数据的服务的网络程序。下面…