深度学习笔记: 最详尽估算送达时间系统设计

欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家!

估算送达时间

1. 问题陈述

构建一个模型来估算在给定订单详情、市场条件和交通状况下的总送达时间。

为了简化,我们不考虑在此练习中对订单进行分批处理(在餐馆将多个订单合并处理)。

在构建估算总送达时间的模型时,“市场条件”指的是可能影响送餐过程的各种外部因素。一些市场条件的例子包括:

星期几:送达时间可能在工作日和周末之间有所不同。

一天中的时间:送达时间可能会受到高峰用餐时间(例如午餐或晚餐时间)或非高峰时间的影响。

天气状况:不利的天气如雨、雪或极端高温可能会延缓送达时间。

特殊事件:如体育比赛、音乐会或节日等当地事件可能会导致交通拥堵和送达时间延长。

假期:由于订单量增加和交通模式变化,假期期间的送达时间可能会更长。

促销和折扣:特殊促销或折扣期间,订单量增加可能会影响送达时间。

送达时间计算

DeliveryTime = PickupTime + PointtoPointTime + DropoffTime \text{DeliveryTime} = \text{PickupTime} + \text{PointtoPointTime} + \text{DropoffTime} DeliveryTime=PickupTime+PointtoPointTime+DropoffTime

2. 指标设计和要求

指标

离线指标: 使用均方根误差(RMSE)
RMSE = ∑ k = 1 n ( predict − y ) 2 n \text{RMSE} = \sqrt{\frac{\sum\limits_{k=1}^{n} (\text{predict} - y)^2}{n}} RMSE=nk=1n(predicty)2
其中,

  • n n n 是样本总数,
  • predict \text{predict} predict 是估算的等待时间,
  • y y y 是实际等待时间。

在线指标: 使用 A/B 测试并监控 RMSE、客户参与度、客户留存率等。

RMSE 是回归问题中常用的指标,包括估算送达时间。RMSE 衡量预测值与实际值之间误差的平均大小。通过取 MSE 的平方根,RMSE 将误差转换回目标变量的原始单位。这意味着 RMSE 与送达时间(分钟)具有相同的单位,更易于解释。例如,2.9 分钟的 RMSE 直接告诉我们,预测值平均偏差约为 2.9 分钟。

A/B 测试:将用户分为两组,一组使用新的送达时间估算模型,另一组使用现有模型。比较这些组之间的结果。

客户参与度:衡量客户与应用程序的互动,例如检查送达时间、订单频率和应用使用时长。

客户留存率:跟踪客户是否继续使用服务,这表明对送达体验的满意度。

要求

训练
  • 在训练期间,我们需要处理大量数据。为此,训练管道应具有高吞吐量。为实现此目的,数据可以组织在 Parquet 文件中。
  • 模型应每隔几小时重新训练一次。送达操作处于动态环境中,受许多外部因素影响:交通、天气状况等。因此,模型必须学习并适应新环境。例如,在比赛日,某些地区的交通状况会变差。如果没有重新训练的模型,当前模型将持续低估送达时间。调度器负责每天多次重新训练模型。
  • 平衡高估和低估。为此,每天多次重新训练以适应市场动态和交通状况。

高吞吐量:在机器学习中,“高吞吐量”指的是快速高效地处理大量数据的能力。

为实现高吞吐量,训练管道应优化速度和效率。这涉及使用允许快速读写数据的数据存储和处理技术。因此我们可以考虑使用 Parquet 文件 :Apache Parquet 是一种专为高效数据处理而设计的列式存储文件格式。

以下是使用 Parquet 文件帮助实现高吞吐量的原因:

Parquet 以列式格式存储数据,这意味着某个列的所有值都存储在一起。这与行式格式(如 CSV)形成对比,其中每行的数据存储在一起。对于许多机器学习任务,操作是在整个列上进行的(例如计算平均值、数据标准化)。列式存储使这些操作更高效。

Parquet 文件支持多种压缩算法(例如 Snappy、Gzip),减少了磁盘上的数据大小。
速度:较小的文件大小意味着需要从磁盘读取到内存的数据较少,加快了数据加载时间。

推理
  • 对于每次送达,系统需要尽可能频繁地进行实时估算。为了简单起见,我们假设每次送达需要进行 30 次预测。
  • 近实时更新:任何状态变化都需要尽快通过模型评分,即餐馆开始准备餐食,司机开始驾车前往客户。
  • 每当送达有变化时,模型运行新估算并向客户发送更新。
  • 捕获近实时聚合统计数据,即特征管道从多个源(Kafka、数据库)聚合数据以减少延迟。
  • 延迟为 100ms 至 200ms

特征管道是一个系统,它从不同来源收集原始数据,处理并将其转化为机器学习模型用于预测的特征。

总结

类型期望目标
指标优化为低 RMSE。估算时间应小于 10-15 分钟。如果我们高估,客户下单的可能性较低。低估可能会导致客户不满。
训练高吞吐量,能够每天多次重新训练
推理延迟为 100ms 至 200ms

3. 模型

特征工程

特征特征工程描述
订单特征小计、菜系
商品特征价格和类型
订单类型团体、餐饮
商户详情
商店ID商店嵌入
实时特征订单数量、骑手数量、交通状况、旅行时间估算
时间特征一天中的时间(午餐/晚餐)、星期几、周末、假日
历史汇总过去X周的平均送达时间:商店/城市/市场/一天中的时间
相似度平均停车时间、历史时间的方差
经纬度测量订单送达时间(到消费者)与餐馆之间的预计驾驶时间

订单特征:从订单详情中提取的特征。例如,订单的总成本(小计)和菜系类型(如意大利菜、中国菜)。这些特征可能会影响送达时间,因为不同菜系的准备时间可能不同。小计 是所有商品价格的总和。不包括税费、配送费或小费。

商品特征:从订单中的单个商品中提取的特征。商品价格和类型(如饮料、主菜)可以影响整体准备和包装时间。

订单类型:标识订单类型。与单个订单相比,团体订单和餐饮服务的准备和送达时间通常更长。

商户详情:关于商户(餐馆)的详细信息,如他们的历史表现、准备速度和受欢迎程度。这些因素会影响送达时间。

商店ID:商店的数值表示(嵌入),捕捉各种特征和历史表现。这有助于模型理解不同商店之间的差异。

实时特征:捕捉实时条件的特征,如当前正在处理的订单数量、可用的送餐员数量、当前交通状况和旅行时间估算。这些对准确预测实时送达时间至关重要。

时间特征:指示订单下单时间的时间特征。送达时间可能会根据是否在高峰时间(午餐或晚餐)、周末或假日期间显著变化。

历史汇总:汇总的历史数据,如特定商店、城市、市场和一天中某些时间段的过去几周的平均送达时间。这有助于根据过去的表现预测未来的送达时间。

相似度:捕捉条件相似性的特征,如送达地点的平均停车时间和历史送达时间的方差。这些可能表明潜在的延迟。

经纬度:测量从餐馆到送达地点的距离和预计驾驶时间的地理空间特征。这对于准确估算旅行时间至关重要。

训练数据

我们可以使用过去6个月的历史送达数据作为训练数据。历史送达数据包括送达数据和实际总送达时间、商店数据、订单数据、客户数据、位置和停车数据。

模型

梯度提升决策树
  • 梯度提升决策树示例

  • 梯度提升决策树的工作原理

  1. 计算基线:给定历史送达数据,模型首先计算平均送达时间。此值将用作基线。

  2. 测量残差:模型测量预测值与实际送达时间之间的残差(误差)。

    Error = Actual Delivery Time − Estimated Delivery Time \text{Error} = \text{Actual Delivery Time} - \text{Estimated Delivery Time} Error=Actual Delivery TimeEstimated Delivery Time

  3. 构建决策树:构建决策树以预测残差。每个叶子节点将包含对残差值的预测。

  4. 使用所有树进行预测:使用所有树进行预测。使用以下公式构建送达时间预测值:

    Estimated Delivery Time = Average Delivery Time + learning rate × residuals \text{Estimated Delivery Time} = \text{Average Delivery Time} + \text{learning rate} \times \text{residuals} Estimated Delivery Time=Average Delivery Time+learning rate×residuals

  5. 计算新残差:给定新的估算送达时间,计算新的残差。使用这些值在步骤3中构建新的决策树。

  6. 重复:重复步骤3-5,直到达到超参数中定义的迭代次数。

梯度提升决策树(GBDT) 中,目标是最小化预测值与实际值之间的差异。这些差异称为残差。通过构建决策树来预测残差,模型关注于先前预测所犯的错误。其思想是通过后续迭代纠正这些错误。

RMSE 优化问题

优化RMSE的一个问题是,它对低估预测和高估预测的惩罚是相同的。考虑下表。两个模型都使用提升决策树。

实际值模型1预测值模型1平方误差模型2预测值模型2平方误差
3034162616
35374334

虽然模型1和模型2有相同的RMSE误差,但模型1高估了送达时间,这会阻止客户下单。模型2低估了送达时间,可能会导致客户不满。

4. 计算与估算

假设

为简化,我们可以做以下假设:

  • 每月有200万活跃用户,总共有2000万用户,30万家餐馆,20万名司机送餐。
  • 平均每年有2000万次送达。

数据量

  • 1个月内,我们收集了200万次送达的数据。每次送达相关特征大约有500字节。

  • 总大小: 500 bytes × 2 × 1 0 6 = 1 0 9 bytes = 1 Gigabytes 500 \text{ bytes} \times 2 \times 10^6 = 10^9 \text{ bytes} = 1 \text{ Gigabytes} 500 bytes×2×106=109 bytes=1 Gigabytes

规模

  • 支持2000万用户。

5. 系统设计

  • 特征存储:提供快速查找以实现低延迟。具有高可用性的任何键值存储的特征存储是一个不错的选择,如 Amazon DynamoDB。
  • 特征管道:从 Kafka 读取数据,进行转换并汇总近实时统计数据。然后将它们存储在特征存储中。
  • 数据库:送达订单数据库存储历史订单和送达数据。数据准备是一个从数据库创建训练数据的过程。我们可以将训练数据存储在云存储中,例如 S3。
  • 我们有三个服务:状态服务、通知服务和估算送达时间服务。前两个服务处理实时更新,估算送达时间服务使用我们的机器学习模型来估算送达时间。
  • 我们有一个调度器,负责协调每天多次重新训练模型。训练后,我们将模型存储在模型存储中。

让我们检查系统的流程:

  1. 用户请求估算送达时间

  1. 估算送达时间服务返回时间估算给应用服务器。应用服务器将时间估算返回给用户

  • 主要有三种类型的用户:消费者/用户、送餐员和餐馆。

  • 用户流程

    • 用户访问主页,检查他们的食物订单,并请求应用服务器估算送达时间。
    • 应用服务器将请求发送到估算送达时间服务。
    • 估算送达时间服务从模型存储中加载最新的机器学习模型,并从特征存储中获取所有特征值。然后使用机器学习模型预测送达时间并将结果返回给应用服务器。
  • 餐馆/送餐员流程

    • 当餐馆有进展时,例如开始制作菜肴或包装食物,他们会将状态发送给状态服务。
    • 状态服务更新订单状态。此事件通常会在队列服务(如 Kafka)中更新,因此其他服务可以订阅并相应地获取更新。
    • 通知服务订阅消息队列(如 Kafka),并实时接收最新的订单状态。

6. 设计扩展

  • 我们扩展服务以处理大量每秒请求。我们还使用负载均衡器在应用服务器之间平衡负载。
  • 我们利用流处理系统如 Kafka 来处理通知以及模型预测。一旦我们的机器学习模型完成预测,它会将预测结果发送到 Kafka,以便其他服务可以立即获取通知。

7. 后续问题

问题答案
使用 StoreID 嵌入作为特征的缺点是什么?我们需要评估使用 StoreID 嵌入在处理新店方面是否有效。
我们需要多久重新训练一次模型?这取决于,我们需要有基础设施来监控在线指标。当在线指标下降时,我们可能需要触发模型重新训练。

缺点:处理新店:使用 StoreID 嵌入的一个重大挑战是处理没有历史数据的新店。由于新店的嵌入未定义,模型可能无法很好地处理这些新店。

8. 总结

  • 我们学习了如何使用梯度提升决策树将估算送达时间表述为一个机器学习问题。
  • 我们学习了如何收集和使用数据来训练模型。
  • 我们学习了如何使用 Kafka 处理日志和模型预测以实现近实时预测。

附录

离线指标示例

假设我们有5个送达订单,实际送达时间和预测送达时间如下(单位:分钟):

送达订单实际时间 (y)预测时间 (predict)
13032
22528
34036
43538
52022

首先,计算平方差:
( 32 − 30 ) 2 = 4 (32 - 30)^2 = 4 (3230)2=4
( 28 − 25 ) 2 = 9 (28 - 25)^2 = 9 (2825)2=9
( 36 − 40 ) 2 = 16 (36 - 40)^2 = 16 (3640)2=16
( 38 − 35 ) 2 = 9 (38 - 35)^2 = 9 (3835)2=9
( 22 − 20 ) 2 = 4 (22 - 20)^2 = 4 (2220)2=4

将这些平方差相加:
4 + 9 + 16 + 9 + 4 = 42 4 + 9 + 16 + 9 + 4 = 42 4+9+16+9+4=42

除以样本数 (n = 5) 并取平方根:
RMSE = 42 5 = 8.4 ≈ 2.9 \text{RMSE} = \sqrt{\frac{42}{5}} = \sqrt{8.4} \approx 2.9 RMSE=542 =8.4 2.9

因此,此示例中的RMSE约为2.9分钟。

梯度提升决策树的示例解释

让我们通过一个具体示例来逐步解释梯度提升决策树(GBDT)的工作原理。假设我们有以下历史送达数据(单位:分钟):

送达订单实际时间 (y)特征 (X)
13010
2258
34012
43511
5207

为了简化,假设只有一个特征 ( X ) 代表一些关于订单和交通状况的汇总信息。

第一步:计算基线

首先,我们计算所有历史数据的平均送达时间。此平均值作为所有送达的初始预测。

平均送达时间 = 30 + 25 + 40 + 35 + 20 5 = 30 分钟 \text{平均送达时间} = \frac{30 + 25 + 40 + 35 + 20}{5} = 30 \text{ 分钟} 平均送达时间=530+25+40+35+20=30 分钟

所以,我们对所有送达的初始预测为30分钟。

第二步:测量残差

接下来,我们计算实际送达时间和初始预测之间的残差(误差)。

送达订单实际时间 (y)初始预测残差 (误差)
130300
22530-5
3403010
435305
52030-10
第三步:构建决策树

我们使用特征 ( X ) 构建决策树来预测残差。

特征 (X)残差 (误差)
100
8-5
1210
115
7-10

假设决策树如下所示:

   X <= 9.5/      \
-7.5     X <= 11.5/       \7.5       5

此树将数据划分为不同区域,并预测每个区域的平均残差。

第四步:使用所有树进行预测

使用学习率调整预测值。假设学习率为0.1。更新后的预测值为:

新预测 = 初始预测 + 学习率 × 残差预测 \text{新预测} = \text{初始预测} + \text{学习率} \times \text{残差预测} 新预测=初始预测+学习率×残差预测

让我们计算更新后的预测值:

送达订单初始预测残差预测新预测
1307.5 (对 X > 9.5 X > 9.5 X>9.5)30 + 0.1 * 7.5 = 30.75
230-7.5 (对 X ≤ 9.5 X \leq 9.5 X9.5)30 + 0.1 * -7.5 = 29.25
3307.5 (对 X > 11.5 X > 11.5 X>11.5)30 + 0.1 * 7.5 = 30.75
4307.5 (对 9.5 < X ≤ 11.5 9.5 < X \leq 11.5 9.5<X11.5)30 + 0.1 * 7.5 = 30.75
530-7.5 (对 X ≤ 9.5 X \leq 9.5 X9.5)30 + 0.1 * -7.5 = 29.25
第五步:计算新残差

使用更新后的预测值计算新残差。

送达订单实际时间 (y)新预测新残差 (误差)
13030.7530 - 30.75 = -0.75
22529.2525 - 29.25 = -4.25
34030.7540 - 30.75 = 9.25
43530.7535 - 30.75 = 4.25
52029.2520 - 29.25 = -9.25
第六步:重复

我们根据超参数定义的迭代次数重复步骤3-5。每次迭代都会构建一个新的决策树来预测新残差并相应地调整预测值。

通过迭代地构建决策树来预测残差并调整预测值,GBDT模型逐步提高其估算送达时间的准确性。学习率确保每棵树仅做出小幅调整,以避免过拟合。

参考文献:

  • 来自educative的机器学习系统设计

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

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

相关文章

python-jupyter notebook安装教程

&#x1f308;所属专栏&#xff1a;【python】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的…

Java内存模型,堆、栈和方法区的区别

Java内存管理是Java虚拟机&#xff08;JVM&#xff09;技术的核心之一。了解Java内存管理对于提高程序性能、解决内存泄漏和优化资源利用至关重要。 一、Java内存模型&#xff08;Java Memory Model, JMM&#xff09; Java内存模型描述了Java程序中变量&#xff08;包括实例字…

5.1 Python 函数的参数类型

1. 实参与形参 形参: 函数定义阶段, 括号内定义的参数名(变量名), 形参的表现形式只有一种就是参数命. 实参: 函数调用阶段, 括号内传入的参数值(变量值), 实参的表现形式有很多种(核心: 可以引用到值).两者之间的关系: 函数调用阶段 --> 实参的值绑定给形参名. 函数调用完…

GraphQL(9):Spring Boot集成Graphql简单实例

1 安装插件 我这边使用的是IDEA&#xff0c;需要先按照Graphql插件&#xff0c;步骤如下&#xff1a; &#xff08;1&#xff09;打开插件管理 在IDEA中&#xff0c;打开主菜单&#xff0c;选择 "File" -> "Settings" (或者使用快捷键 Ctrl Alt S …

什么是快乐?

什么是快乐&#xff1f; What is Happiness? 1. 快乐不是追求外在的物质&#xff0c;而是内心的平静与满足。当我们学会感恩&#xff0c;懂得珍惜眼前的一切&#xff0c;心中自然会充满喜悦。快乐并非来自拥有更多&#xff0c;而是感受到已经拥有的足够。每一天都怀抱感激之情…

最新情侣飞行棋高阶羞羞版,解锁私密版情侣小游戏,文末有福利!

今天要跟大家聊聊一种特别有意思的游戏——情侣飞行棋羞羞版。别急着脸红&#xff0c;这可是专为情侣设计的游戏&#xff0c;让你们在轻松愉快的氛围中&#xff0c;增进了解&#xff0c;加深感情。 谈恋爱&#xff0c;不就是两个人在一起&#xff0c;做些有趣的事情吗&#xf…

【INTEL(ALTERA)】Quartus® 软件 Pin Planner 中 Agilex™ 5 FPGA的 HSIO 库可以选择 1.8V VCCIO?

目录 说明 解决方法 说明 由于 Quartus Prime Pro Edition 软件版本 24.1 存在一个问题&#xff0c;Quartus 软件 Pin Planner 中的 I/O 组属性 GUI 允许用户选择 1.8V 作为 HSIO 银行位置的 VCCIO。HSIO bank 支持的有效 VCCIO 电压仅为 1.0V、1.05V、1.1V、1.2V 和 1.3V。…

【SpringBoot + Vue 尚庭公寓实战】地区信息管理接口实现(九)

【SpringBoot Vue 尚庭公寓实战】地区信息管理接口实现&#xff08;九&#xff09; 文章目录 【SpringBoot Vue 尚庭公寓实战】地区信息管理接口实现&#xff08;九&#xff09;1、业务说明2、数据逻辑模型3、接口实现3.1、查询省份信息列表3.2、根据省份ID查询城市信息列表3…

Http协议JSON格式

1. 计算机网络 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 思考:计算机网络…

基于matlab提取一维数组中非nan的数据

一、使用逻辑索引 使用逻辑索引来选择数组中所有非NaN的元素。逻辑索引是与原数组同型的逻辑数组&#xff0c;true对应的位置将会被选中。 % 假设a是一维数组 a [1, 2, NaN, 4, NaN, 6];% 使用逻辑索引提取非NaN元素 non_nan_elements a(~isnan(a)); 二、使用isnan函数和fi…

程序猿大战Python——容器——知识补充

字典遍历方法 目标&#xff1a;了解遍历字典的遍历方法。 当要遍历字典的元素内容&#xff0c;即获取字典的键、值。 常用方法&#xff1a; 函数名含义keys()以列表的形式&#xff0c;返回一个字典所有的键。values()以列表的形式&#xff0c;返回一个字典所有的值。items()返…

光学雨量监测站:科技赋能,精准监测降水过程

TH-YJ3随着科技的不断进步&#xff0c;光学雨量监测站作为一种先进的降水监测设备&#xff0c;正逐渐在气象、水文、农业等领域发挥重要作用。光学雨量监测站以其高精度、高可靠性、实时性强的特点&#xff0c;为降水数据的收集和分析提供了强有力的支持&#xff0c;为相关领域…

深入解析B树:数据结构、存储结构与算法优势

一、引言 在计算机科学中&#xff0c;数据结构和算法是核心内容。它们的选择和应用直接影响程序的效率和性能。B树&#xff08;B-Tree&#xff09;作为一种自平衡的多叉树数据结构&#xff0c;广泛应用于数据库和文件系统中。本文将详细介绍B树的数据结构模型、存储结构&#…

IDEA 配置方法模板无法获取到参数值和返回值(methodParameters()、methodReturnType()获取不到值)

问题现象&#xff1a; 我在 review 同事代码时候&#xff0c;发现方法上有注释&#xff0c;但是注释上又没有方法参数和返回值&#xff0c;这不是IDEA 配置了方法模板就可以自动生成的嘛&#xff0c;我出于好奇去问了下该同事是怎么回事&#xff0c;该同事有点不好意思的说我配…

Linux---系统的初步学习【 项目二 管理Linux文件和目录】

项目二 管理Linux文件和目录 2.1项目知识准备 ​ 文件是存储在计算机上的数据集合。在Windows系统中&#xff0c;我们理解的文件可以是文本文档、图片、程序、音乐、视频等。在Linux中&#xff0c;一切皆文件&#xff0c;也就是除了Windows中所理解的文件&#xff0c;目录、字…

【测试】软件测试方案—实际项目直接套用(Word原件)

1. 引言 1.1. 编写目的 1.2. 项目背景 1.3. 读者对象 1.4. 参考资料 1.5. 术语与缩略语 2. 测试策略 2.1. 测试完成标准 2.2. 测试类型 2.2.1. 功能测试 2.2.2. 性能测试 2.2.3. 安全性与访问控制测试 2.3. 测试工具 3. 测试技术 4. 测试资源 4.1. 人员安排 4.2. 测试环境 4.2.…

2024 年最新使用 Node 搭建QQ开放平台官方 QQ 频道机器人详细教程(更新中)

注册 QQ 开放平台账号 QQ 开放平台是腾讯应用综合开放类平台&#xff0c;包含 QQ 机器人、QQ 小程序、QQ 小游戏 等集成化管理&#xff0c;也就是说你注册了QQ 开放平台&#xff0c;你开发 QQ 机器人还是 QQ 小程序都是在这个平台进行部署上线和管理。 如何注册 QQ 开放平台账…

小熊家政帮day22-day23 订单系统优化(订单状态机、练习分库分表、索引、订单缓存)

目录 1 状态机1.1 状态机介绍1.1.1 当前存在的问题1.1.2 使用状态机解决问题 1.2 实现订单状态机1.2.1 编写订单状态机1.2.1.1 依赖引入1.2.1.2 订单状态枚举类1.2.1.3 状态变更事件枚举类1.2.1.4 定义订单快照类1.2.1.5 定义事件变更动作类1.2.1.5 定义订单状态机类1.2.1.6 状…

明天二战六级

明天二战六级&#xff0c;各位程序员们&#xff0c;加油

HTML静态网页成品作业(HTML+CSS)—— 明星吴磊介绍网页(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…