人工智能算法工程师(中级)课程21-深度学习中各种优化器算法的应用与实践、代码详解

news2024/11/28 22:39:13

大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(中级)课程21-深度学习中各种优化器算法的应用与实践、代码详解。本文将介绍PyTorch框架下的几种优化器,展示如何使用PyTorch中的优化器,我们将使用MNIST数据集和一个简单的多层感知器(MLP)模型。本文仅用于演示不同优化器的用法,实际应用中可能需要调整超参数以获得最佳性能。
在这里插入图片描述

文章目录

    • 一、PyTorch框架下的算法优化器
      • 1. SGD (随机梯度下降)
      • 2. ASGD (平均随机梯度下降)
      • 3. Rprop (弹性反向传播)
      • 4. Adagrad (自适应梯度)
      • 5. Adadelta (自适应学习率和动量)
      • 6. RMSprop (均方根传播)
      • 7.Adam
      • 概率论与数理统计中的矩估计介绍
    • 二、PyTorch实现算法优化器的代码
      • 多优化器代码实现
      • 运行结果

一、PyTorch框架下的算法优化器

1. SGD (随机梯度下降)

SGD,即随机梯度下降,是机器学习中不可或缺的伙伴,它巧妙地通过单个样本或一小批样本计算梯度来更新模型参数,就像一位精明的向导,在数据的大海中引领算法逐步靠近最优解,既节省了计算资源又加速了学习过程,尤其在大数据集上展现出了非凡的魅力。
数学表达式:
θ t + 1 = θ t − η ∇ J ( θ t ) \theta_{t+1} = \theta_t - \eta \nabla J(\theta_t) θt+1=θtηJ(θt)
其中, θ \theta θ 是参数, η \eta η 是学习率, ∇ J ( θ t ) \nabla J(\theta_t) J(θt) 是损失函数 J J J 关于参数 θ \theta θ 的梯度。
在这里插入图片描述

2. ASGD (平均随机梯度下降)

ASGD 是 SGD 的一位智慧升级版同伴,它通过计算参数的历史移动平均值来指导学习路径,就像是在不断积累经验的老练航海家,即便面对波涛汹涌的数据海洋也能稳健航行,有效提高了优化过程的稳定性和收敛速度,尤其适合在复杂多变的模型训练旅程中指引方向。
数学表达式:
θ t + 1 = θ t − η ∇ J ( θ t ) θ ˉ t + 1 = ( 1 − λ ) θ ˉ t + λ θ t + 1 \theta_{t+1} = \theta_t - \eta \nabla J(\theta_t) \\ \bar{\theta}_{t+1} = (1 - \lambda) \bar{\theta}_t + \lambda \theta_{t+1} θt+1=θtηJ(θt)θˉt+1=(1λ)θˉt+λθt+1

其中, θ ˉ \bar{\theta} θˉ 是参数的移动平均值, λ \lambda λ 是平滑因子。

3. Rprop (弹性反向传播)

Rprop 是一种别具匠心的优化算法,它不依赖于梯度的大小,而是聚焦于梯度的方向变化,就像是一位技艺高超的导航员,在参数调整的旅途中只关注前进的方向而忽略速度,这种独特的方法使得它能够在复杂的优化地形中找到更高效的路径,特别适合那些对学习率敏感的任务,为模型训练带来了新的可能性。
数学表达式:
Δ θ t + 1 = { Δ θ t ⋅ η + if  ∇ J ( θ t ) ⋅ ∇ J ( θ t − 1 ) > 0 Δ θ t ⋅ η − if  ∇ J ( θ t ) ⋅ ∇ J ( θ t − 1 ) < 0 Δ θ t otherwise θ t + 1 = θ t − sign ( ∇ J ( θ t ) ) ⋅ Δ θ t + 1 \Delta \theta_{t+1} = \begin{cases} \Delta \theta_t \cdot \eta^+ & \text{if } \nabla J(\theta_t) \cdot \nabla J(\theta_{t-1}) > 0 \\ \Delta \theta_t \cdot \eta^- & \text{if } \nabla J(\theta_t) \cdot \nabla J(\theta_{t-1}) < 0 \\ \Delta \theta_t & \text{otherwise} \end{cases} \\ \theta_{t+1} = \theta_t - \text{sign}(\nabla J(\theta_t)) \cdot \Delta \theta_{t+1} Δθt+1= Δθtη+ΔθtηΔθtif J(θt)J(θt1)>0if J(θt)J(θt1)<0otherwiseθt+1=θtsign(J(θt))Δθt+1

其中, Δ θ \Delta \theta Δθ 是参数更新的大小, η + \eta^+ η+ η − \eta^- η 是增加和减少更新大小的因子。

4. Adagrad (自适应梯度)

Adagrad 是一种充满智慧的优化算法,它像是一位细心的园丁,根据不同参数的历史梯度精心调整学习率,确保每个参数都能以最适合自己的步伐向着最优解迈进。这种方式特别适用于稀疏数据的情况,能够让模型训练更加高效且稳定,特别是在面对复杂多变的学习任务时,Adagrad 能够展现出其独特的魅力。
数学表达式:
G t = G t − 1 + ∇ J ( θ t ) 2 θ t + 1 = θ t − η G t + ϵ ∇ J ( θ t ) G_t = G_{t-1} + \nabla J(\theta_t)^2 \\ \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_t + \epsilon}} \nabla J(\theta_t) Gt=Gt1+J(θt)2θt+1=θtGt+ϵ ηJ(θt)

其中, G t G_t Gt 是历史梯度的累积平方和, ϵ \epsilon ϵ 是平滑项。

5. Adadelta (自适应学习率和动量)

Adadelta 是 Adagrad 的一位聪明后代,它巧妙地运用动量项来更新参数,就像是一位无需教练指导的运动员,能自我调整步伐大小,无需预先设定学习率。这种方式使得模型训练更加灵活高效,尤其在长距离赛跑般的深度学习任务中,Adadelta 能够帮助模型稳步向前,避免因学习率设置不当而导致的训练停滞或震荡。
数学表达式:
E [ g 2 ] t = ρ E [ g 2 ] t − 1 + ( 1 − ρ ) ∇ J ( θ t ) 2 Δ θ t = − E [ Δ θ 2 ] t − 1 + ϵ E [ g 2 ] t + ϵ ∇ J ( θ t ) E [ Δ θ 2 ] t = ρ E [ Δ θ 2 ] t − 1 + ( 1 − ρ ) Δ θ t 2 θ t + 1 = θ t + Δ θ t E[g^2]_t = \rho E[g^2]_{t-1} + (1 - \rho) \nabla J(\theta_t)^2 \\ \Delta \theta_t = - \frac{\sqrt{E[\Delta \theta^2]_{t-1} + \epsilon}}{\sqrt{E[g^2]_t + \epsilon}} \nabla J(\theta_t) \\ E[\Delta \theta^2]_t = \rho E[\Delta \theta^2]_{t-1} + (1 - \rho) \Delta \theta_t^2 \\ \theta_{t+1} = \theta_t + \Delta \theta_t E[g2]t=ρE[g2]t1+(1ρ)J(θt)2Δθt=E[g2]t+ϵ E[Δθ2]t1+ϵ J(θt)E[Δθ2]t=ρE[Δθ2]t1+(1ρ)Δθt2θt+1=θt+Δθt

其中, E [ g 2 ] E[g^2] E[g2] E [ Δ θ 2 ] E[\Delta \theta^2] E[Δθ2] 分别是梯度平方和和参数更新平方和的指数移动平均值, ρ \rho ρ 是动量因子。

6. RMSprop (均方根传播)

RMSProp(Root Mean Square Propagation)优化算法是一种自适应学习率的优化算法,主要用于深度学习中。以下是RMSProp优化算法的数学公式:

首先,计算梯度的平方的指数加权移动平均值:

v t = β v t − 1 + ( 1 − β ) g t 2 v_t = \beta v_{t-1} + (1 - \beta) g_t^2 vt=βvt1+(1β)gt2

其中, v t v_t vt 是时间步 t 的平方梯度指数加权移动平均值, g t 2 g_t^2 gt2 是当前时间步梯度的元素平方, β \beta β 是梯度的指数衰减率。

然后,更新模型参数:

θ t + 1 = θ t − η v t + ϵ g t \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{v_t} + \epsilon} g_t θt+1=θtvt +ϵηgt

其中, θ t \theta_t θt 是时间步 t 的模型参数, η \eta η 是学习率, ϵ \epsilon ϵ 是为了数值稳定性而添加的小常数。

以上描述了RMSProp优化算法中计算梯度平方指数加权移动平均值和更新模型参数的过程。RMSProp算法通过对梯度进行平方指数加权移动平均来自适应地调整学习率,从而在训练过程中更好地优化模型。

7.Adam

Adam 算法,这位融合了动量梯度下降与 RMSprop 精髓的优化大师,犹如一位技艺高超的调音师,通过计算梯度的一阶矩和二阶矩来动态调整每个参数的学习率。它既能捕捉到历史梯度的方向感,又能根据当前梯度的波动进行微调,使得模型训练过程既稳定又高效,尤其在处理大规模数据和复杂模型时,Adam 能够带领模型快速而准确地找到最优解。
以下是Adam优化算法的数学表达式:

首先,计算一阶矩估计(即梯度的指数移动平均值):

m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t mt=β1mt1+(1β1)gt

其中, m t m_t mt 是时间步 t 的一阶矩估计, g t g_t gt 是当前时间步的梯度, β 1 \beta_1 β1 是动量的指数衰减率。

其次,计算二阶矩估计(即梯度的平方的指数移动平均值):

v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 vt=β2vt1+(1β2)gt2

其中, v t v_t vt 是时间步 t 的二阶矩估计, g t 2 g_t^2 gt2 是当前时间步梯度的元素平方, β 2 \beta_2 β2 是梯度的指数衰减率。

接着,校正一阶矩的偏差:

m ^ t = m t 1 − β 1 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t} m^t=1β1tmt

v ^ t = v t 1 − β 2 t \hat{v}_t = \frac{v_t}{1 - \beta_2^t} v^t=1β2tvt

然后,更新模型参数:

θ t + 1 = θ t − η v ^ t + ϵ m ^ t \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t θt+1=θtv^t +ϵηm^t

其中, θ t \theta_t θt 是时间步 t 的模型参数, η \eta η 是学习率, ϵ \epsilon ϵ 是为了数值稳定性而添加的小常数。

以上描述了Adam优化算法中用于更新梯度估计、计算动量和RMSProp的过程,并最终利用它们来更新模型参数的方法。

概率论与数理统计中的矩估计介绍

在优化算法中,一阶矩估计和二阶矩估计是指对梯度的统计特征进行估计的过程,涉及了概率论与数理统计的知识。我来详细解释一下:

一阶矩估计通常表示对随机变量的期望值的估计,也可以理解为均值的估计。在优化算法中,一阶矩估计可以用来估计梯度的平均值,在Adam和RMSProp等算法中起到了动量的作用。动量可以帮助优化算法在参数更新时更平稳地前进,避免陷入局部极小值点。一阶矩估计可以通过指数加权移动平均的方式来计算,从而更好地反映梯度的变化趋势。

二阶矩估计则通常表示对随机变量的方差的估计。在优化算法中,二阶矩估计可以用来估计梯度的方差或者标准差,如在RMSProp算法中所使用的。通过估计梯度的方差,我们可以更好地了解梯度的变化范围,并且利用这个信息来自适应地调整学习率,以提高训练的效率和稳定性。

概率论与数理统计为我们提供了对随机变量的期望、方差等统计特征的概念和计算方法,优化算法中的一阶矩估计和二阶矩估计正是借鉴了这些概念和方法,使得优化算法能够更好地利用梯度的统计信息来指导参数更新的过程,从而提高模型的训练效果。

二、PyTorch实现算法优化器的代码

多优化器代码实现

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 定义一个简单的多层感知器
class MLP(nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 28 * 28)
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 准备数据
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器列表
optimizers = [
    optim.SGD,
    optim.ASGD,
    optim.Rprop,
    optim.Adagrad,
    optim.Adadelta,
    optim.RMSprop,
    optim.Adam
]

# 训练函数
def train(optimizer_class, model, dataloader, criterion, epochs=3):
    optimizer = optimizer_class(model.parameters(), lr=0.01)
    model.train()
    for epoch in range(epochs):
        running_loss = 0.0
        correct = 0
        total = 0
        for batch_idx, (data, target) in enumerate(dataloader):
            optimizer.zero_grad()
            output = model(data)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()

            running_loss += loss.item()
            _, predicted = torch.max(output.data, 1)
            total += target.size(0)
            correct += (predicted == target).sum().item()

        print(f'Optimizer: {optimizer_class.__name__}, Epoch: {epoch + 1}, Loss: {running_loss / len(dataloader)}, Accuracy: {correct / total * 100}%')

# 使用不同的优化器训练模型
for optimizer_class in optimizers:
    model = MLP()
    train(optimizer_class, model, train_loader, criterion)

运行结果

Optimizer: SGD, Epoch: 1, Loss: 0.8009015738265093, Accuracy: 79.87333333333333%
Optimizer: SGD, Epoch: 2, Loss: 0.31090657713253106, Accuracy: 91.07333333333332%
Optimizer: SGD, Epoch: 3, Loss: 0.2509216960471894, Accuracy: 92.69833333333334%
Optimizer: ASGD, Epoch: 1, Loss: 0.8227703367659787, Accuracy: 79.11333333333333%
Optimizer: ASGD, Epoch: 2, Loss: 0.3227304352451362, Accuracy: 90.68833333333333%
Optimizer: ASGD, Epoch: 3, Loss: 0.2698148043155035, Accuracy: 92.145%
Optimizer: Rprop, Epoch: 1, Loss: 8.706047950292637, Accuracy: 85.69333333333333%
Optimizer: Rprop, Epoch: 2, Loss: 16.184261398441567, Accuracy: 85.75166666666667%
Optimizer: Rprop, Epoch: 3, Loss: 15.855906286521126, Accuracy: 85.99166666666666%
Optimizer: Adagrad, Epoch: 1, Loss: 0.24328371752172645, Accuracy: 92.56166666666667%
Optimizer: Adagrad, Epoch: 2, Loss: 0.12497247865737311, Accuracy: 96.25333333333333%
Optimizer: Adagrad, Epoch: 3, Loss: 0.09774033319570426, Accuracy: 97.06666666666666%
Optimizer: Adadelta, Epoch: 1, Loss: 1.3385312659526938, Accuracy: 69.485%
Optimizer: Adadelta, Epoch: 2, Loss: 0.5202090000229349, Accuracy: 86.955%
Optimizer: Adadelta, Epoch: 3, Loss: 0.39094064729427225, Accuracy: 89.41666666666667%
Optimizer: RMSprop, Epoch: 1, Loss: 0.6654755138456504, Accuracy: 88.81666666666666%
Optimizer: RMSprop, Epoch: 2, Loss: 0.23642293871569037, Accuracy: 93.51833333333333%
Optimizer: RMSprop, Epoch: 3, Loss: 0.20657251488222783, Accuracy: 94.41833333333334%
Optimizer: Adam, Epoch: 1, Loss: 0.2741849403957457, Accuracy: 91.88833333333334%
Optimizer: Adam, Epoch: 2, Loss: 0.18909314711804567, Accuracy: 94.86833333333334%
Optimizer: Adam, Epoch: 3, Loss: 0.1710762643500535, Accuracy: 95.42166666666667%

以上代码将为每个优化器运行3个训练周期,并打印损失值和准确率。我们可以看到针对这个任务Adagrad优化器表现较好,在实际应用中,我们可能需要运行更多的训练周期并调整学习率等超参数以获得最佳性能。

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

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

相关文章

云游戏畅玩黑神话悟空:使用 NVIDIA 4090 体验极致画质

​ 黑神话悟空 爽啦&#xff01;没有好配置又想玩《黑神话&#xff1a;悟空》的朋友们都爽啦&#xff01;自己没有好的 GPU&#xff0c;体验《黑神话&#xff1a;悟空》时画质不好玩的不舒心&#xff1f;厚德云来帮你解决问题&#xff01;厚德云上线了《黑神话&#xff1a;悟空…

机器学习第五十三周周报 MAG

文章目录 week53 MAG摘要Abstract1. 题目2. Abstract3. 预测标准3.1 问题提出3.2 数据预处理3.3 模型架构MAG3.4 时域故障模式识别3.5 故障检测器 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.4 实验结果4.5 结果分析 5. 结论小结参考文献 week53 MAG 摘要 本周阅读…

【ASPLOS2024】RECom:通过编译器技术加速推荐模型推理,论文中选并获得荣誉奖项!

2024年5月&#xff0c;关于推荐模型自动编译优化的论文《RECom: A Compiler Approach to Accelerate Recommendation Model Inference with Massive Embedding Columns》在系统领域顶会ASPLOS 2024上中选并进行了展示&#xff0c;并被授予了Distinguished Artifact Award 荣誉&…

基于springboot的招聘系统的设计与实现

TOC springboot614基于springboot的招聘系统的设计与实现--论文 研究背景 近年来&#xff0c;由于计算机技术和互联网技术的快速发展&#xff0c;使得所有企事业单位内部都是数字化、信息化、无纸化的发展趋势&#xff0c;随着趋势的发展&#xff0c;各种决策系统、辅助系统…

springboot安全在线学习平台---附源码131019

摘 要 这是采用springboot技术&#xff0c;SQL Server数据库实现的一个基于网络的在线学习系统。系统具有用户登录和注册、普通用户、课程分类、课程、购买表、课程上传等功能。在线学习是通过提供电子课件&#xff0c;让学生可以进行在线学习&#xff0c;并支持电子课件下载的…

windows主机查询url请求来自哪里发起的

最近使用fiddler抓包&#xff0c;看到一直有http://conna.gj.qq.com:47873 的请求&#xff0c; 对此进行溯源&#xff0c;确定是不是被攻击了。 在dos里查询端口进程&#xff1a;netstat -ano | findstr :47873 查到来自8020的进程id 查看此进程应用&#xff0c;发现竟然是…

postgresql 集群文档

https://www.cnblogs.com/Alicebat/p/14148933.html [命令] Pacemaker 命令 pcs cluster &#xff08;管理节点&#xff09; – Eternal Center PostgreSQL实战之物理复制和逻辑复制&#xff08;五&#xff09;_postgresql 流复制和物理复制-CSDN博客 https://jingyan.baidu…

UE5.4 - 下载和安装

一. 简介 虚幻引擎&#xff08;Unreal Engine&#xff09;是由 Epic Games 公司推出的一款功能强大的游戏开发引擎。它于 1998 年推出第一代&#xff0c;其口号是 “全球最开放、最先进的实时 3D 创作工具”。 虚幻引擎被广泛应用于游戏产业&#xff0c;创作出了众多知名的 3…

企业级WEB应用服务器TOMCAT从讲解到实战一篇解决

一 .WEB技术 1.1 HTTP协议和B/S 结构 操作系统有进程子系统&#xff0c;使用多进程就可以充分利用硬件资源。进程中可以多个线程&#xff0c;每一个线程可以 被CPU调度执行&#xff0c;这样就可以让程序并行的执行。这样一台主机就可以作为一个服务器为多个客户端提 供计算服…

教程:postman的平替hoppscotch,又叫postwoman,hoppscotch的docker-compose安装过程

目录 1. 背景2. 前期准备2.1 准备docker-compose文件&#xff0c;两个版本&#xff0c;一个3合1&#xff0c;一个分开2.1.1 3合1版本&#xff08;推荐&#xff09;2.1.2 独立版本 2.2 准备安装nginx-proxy-manager&#xff08;可选&#xff09;2.2 准备.env文件2.2.1 默认ip的.…

爬虫案例5——爬取东方财富网的港股数据

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正 任务&#xff1a;从东方财富网中爬取港股的代码&#xff0c;名称&#xff0c;最近价格&#xff0c;涨跌幅&#xff0c;今开&#xff0c;最高等数据 目标网页地址&#xff1a;https://data.eastmone…

科技大厂对AI的垄断

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

探索HTML中的“iframe”标签——WEB开发系列12

现代网页开发中&#xff0c;​​<iframe>​​ 标签是一个非常重要的工具。允许我们在一个网页中嵌入另一个网页&#xff0c;对于展示外部内容、应用嵌套或实现复杂的布局设计都非常有用。来一起探讨如何使用 ​​iframe​​ 标签&#xff0c;包括设置高度和宽度、移除边框…

记录 升级到Gradle 8.4.2 遇到的坑

序言 最近将Gradle从4.0.1 升级到 8.4.2 遇到了很多坑&#xff0c;记录下来帮助有缘人 升级gradle 修改根目录的build.gradle dependencies {classpath com.android.tools.build:gradle:8.4.2classpath org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0}修改gradle-wrapper.…

【html+css 绚丽Loading】000011 三元轮回珠

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

【TS】函数重载的作用

前言 当调用一个函数时&#xff0c;通常会提示有多种调用方式&#xff0c;各种调用方式的参数类型、参数数量、返回值等均可能不同。本文通过自己封装的message作为案例模拟一下。 实现 自己封装的message组件&#xff0c;有以下7种调用方式 message({mode: "mode"…

QT-计算器

QT-计算器 一、演示效果二、关键程序三、下载链接 一、演示效果 二、关键程序 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui…

分布式事务:基本概念

文章目录 一、基础概念1、什么是事务2、本地事务3 、分布式事务4、分布式事务产生的场景 二、分布式事务基础理论1、CAP理论&#xff08;1&#xff09;理解CAP&#xff08;2&#xff09;CAP组合方式&#xff08;3&#xff09;总结 2、BASE理论 三、分布式事务解决方案之2PC&…

【Java日志系列】Logback日志框架

目录 前言 一、Logback简介 二、Logback组件 三、快速入门 四、配置文件的使用 1. 配置文件中的标签 1.1 logger标签 1.2 root标签 1.3 appender标签 1.4 filter标签 1.5 encoder标签 1.6 property标签 2. 常见的Appender 2.1 ConsoleAppender 2.2 FileAppender…

瓜子二手车源码开发

瓜子二手车作为国内知名的二手车交易平台&#xff0c;其开发需求与功能架构主要围绕提升用户体验、保障交易安全、优化交易流程以及提供全面服务等方面展开。以下是对瓜子二手车开发需求与功能架构的详细分析 一、开发需求 用户需求&#xff1a; 便捷性&#xff1a;用户希望能…