第J1周:ResNet50算法(Tensorflow版)
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
目标
具体实现
(一)环境
语言环境:Python 3.10
编 译 器: PyCharm
框 架: TensorFlow
(二)具体步骤
import numpy as np
import tensorflow as tf
from tensorflow.python.data import AUTOTUNE # 设置GPU
# 获取当前系统中所有可用的物理GPU设备
gpus = tf.config.list_physical_devices("GPU") # 如果系统中存在GPU设备
if gpus: # 设置第一个GPU设备的内存增长模式为动态增长,以避免一次性占用所有显存 tf.config.experimental.set_memory_growth(gpus[0], True) # 设置当前可见的GPU设备为第一个GPU,确保程序仅使用该GPU进行计算 tf.config.set_visible_devices([gpus[0]], "GPU") # 导入数据
import matplotlib.pyplot as plt
import os, PIL, pathlib
from tensorflow import keras
from tensorflow.keras import layers, models # 设置matplotlib的字体为SimHei,以支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
# 设置matplotlib的负号显示为正常符号,避免显示为方块
plt.rcParams['axes.unicode_minus'] = False # 定义数据目录路径
data_dir = "./data/bird_photos"
# 将路径转换为pathlib.Path对象,方便后续操作
data_dir = pathlib.Path(data_dir) # 使用glob方法获取所有子目录下的jpg文件,并计算其数量
image_count = len(list(data_dir.glob('*/*.jpg')))
# 打印图片数量
print("图片数量:",image_count) # 数据预处理
# 定义批量大小和图像尺寸
batch_size = 8
img_height = 224
img_width = 224 # 使用 `tf.keras.preprocessing.image_dataset_from_directory` 从指定目录加载训练数据集
# 参数说明:
# - data_dir: 包含图像数据的目录路径
# - validation_split: 用于验证集的数据比例,此处为20%
# - subset: 指定加载的数据子集,此处为训练集
# - seed: 随机种子,确保数据分割的可重复性
# - image_size: 图像将被调整到的尺寸,此处为224x224
# - batch_size: 每个批次的图像数量,此处为8
train_ds = tf.keras.preprocessing.image_dataset_from_directory( data_dir, validation_split=0.2, subset="training", seed=123, image_size=(img_height, img_width), batch_size=batch_size) # 使用 `tf.keras.preprocessing.image_dataset_from_directory` 从指定目录加载验证数据集
# 参数说明与训练集相同,但 `subset` 参数指定为验证集
val_ds = tf.keras.preprocessing.image_dataset_from_directory( data_dir, validation_split=0.2, subset="validation", seed=123, image_size=(img_height, img_width), batch_size=batch_size) # 从训练数据集中获取类别名称
class_names = train_ds.class_names # 打印类别名称
print("类别:", class_names) # 可视化数据
# 可视化训练数据集中的部分图像及其对应的标签
# 该代码块创建一个大小为10x5的图形窗口,并在窗口中展示训练数据集中的前8张图像及其标签。 plt.figure(figsize=(10, 5)) # 创建一个大小为10x5的图形窗口
plt.suptitle("训练数据集可视化") # 设置图形的标题为"训练数据集可视化" # 从训练数据集中取出一批数据(images和labels),并展示其中的前8张图像
for images, labels in train_ds.take(1): for i in range(8): ax = plt.subplot(2, 4, i+1) # 在2行4列的网格中创建第i+1个子图 plt.imshow(images[i].numpy().astype("uint8")) # 显示第i张图像,并将其转换为uint8类型 plt.title(class_names[labels[i]]) # 设置子图的标题为对应的类别名称 plt.axis("off") # 关闭子图的坐标轴显示 # 检查数据
"""
遍历训练数据集中的批次,并打印图像批次和标签批次的形状。 该代码片段从训练数据集 `train_ds` 中获取一个批次的数据,并打印该批次中图像和标签的形状。
`train_ds` 是一个可迭代对象,通常包含图像和标签的批次数据。 代码执行流程:
1. 从 `train_ds` 中获取一个批次的图像和标签。
2. 打印图像批次的形状。
3. 打印标签批次的形状。
4. 使用 `break` 语句提前退出循环,仅处理第一个批次。
"""
for image_batch, labels_batch in train_ds: # 打印图像批次的形状,通常为 (batch_size, height, width, channels) print(image_batch.shape) # 打印标签批次的形状,通常为 (batch_size,) print(labels_batch.shape) # 仅处理第一个批次后退出循环 break # 配置数据集
# 设置自动调优参数,用于优化数据加载和预处理性能
AUTOTUNE = tf.data.AUTOTUNE # 对训练数据集进行优化处理:
# 1. `cache()`: 将数据集缓存到内存或磁盘,避免在每个epoch重复加载数据,提高训练效率。
# 2. `shuffle(1000)`: 对数据集进行随机打乱,缓冲区大小为1000,确保训练数据的随机性。
# 3. `prefetch(buffer_size=AUTOTUNE)`: 使用自动调优的缓冲区大小,预取数据以重叠数据加载和模型训练,提高整体性能。
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE) # 对验证数据集进行优化处理:
# 1. `cache()`: 将数据集缓存到内存或磁盘,避免在每个epoch重复加载数据,提高验证效率。
# 2. `prefetch(buffer_size=AUTOTUNE)`: 使用自动调优的缓冲区大小,预取数据以重叠数据加载和模型验证,提高整体性能。
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE) # 构建ResNet50网络
from keras import layers
from keras.layers import Input, Activation, BatchNormalization, Flatten
from keras.layers import Dense, Conv2D,MaxPooling2D,ZeroPadding2D,AveragePooling2D
from keras.models import Model def identity_block(input_tensor, kernel_size, filters, stage, block): """ 实现ResNet中的恒等块(Identity Block)。 参数: - input_tensor: 输入张量,形状为 (batch_size, height, width, channels)。 - kernel_size: 卷积核的大小,通常为 (3, 3)。 - filters: 三个整数的列表,表示三个卷积层的滤波器数量。 - stage: 当前块的阶段标识,用于命名。 - block: 当前块的标识,用于命名。 返回: - 输出张量,形状与输入张量相同。 """ filters1, filters2, filters3 = filters name_base= str(stage) + block + '_identify_block_' # 第一个卷积层,1x1卷积,用于降维 x = Conv2D(filters1, (1,1), name = name_base + 'conv1')(input_tensor) x = BatchNormalization(name = name_base + 'bn1')(x) x = Activation('relu', name = name_base + 'relu1')(x) # 第二个卷积层,使用指定的kernel_size进行卷积 x = Conv2D(filters2, kernel_size, padding = 'same', name = name_base + 'conv2')(x) x = BatchNormalization(name = name_base + 'bn2')(x) x = Activation('relu', name = name_base + 'relu2')(x) # 第三个卷积层,1x1卷积,用于升维 x = Conv2D(filters3, (1,1), name = name_base + 'conv3')(x) x = BatchNormalization(name = name_base + 'bn3')(x) # 将输入张量与卷积结果相加,实现残差连接 x = layers.add([x, input_tensor], name = name_base + 'add') x = Activation('relu', name = name_base + 'relu4')(x) return x def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2,2)): """ 实现ResNet中的卷积块(Convolutional Block)。 参数: - input_tensor: 输入张量,形状为 (batch_size, height, width, channels)。 - kernel_size: 卷积核的大小,通常为 (3, 3)。 - filters: 三个整数的列表,表示三个卷积层的滤波器数量。 - stage: 当前块的阶段标识,用于命名。 - block: 当前块的标识,用于命名。 - strides: 卷积步幅,默认为 (2, 2)。 返回: - 输出张量,形状与输入张量相同。 """ filters1, filters2, filters3 = filters res_name_base = str(stage) + block + '_conv_block_res_' name_base = str(stage) + block + '_conv_block_' # 第一个卷积层,1x1卷积,用于降维 x = Conv2D(filters1, (1,1), strides = strides, name = name_base + 'conv1')(input_tensor) x = BatchNormalization(name = name_base + 'bn1')(x) x = Activation('relu', name = name_base + 'relu1')(x) # 第二个卷积层,使用指定的kernel_size进行卷积 x = Conv2D(filters2, kernel_size, padding = 'same', name = name_base + 'conv2')(x) x = BatchNormalization(name = name_base + 'bn2')(x) x = Activation('relu', name = name_base + 'relu2')(x) # 第三个卷积层,1x1卷积,用于升维 x = Conv2D(filters3, (1,1), name = name_base + 'conv3')(x) x = BatchNormalization(name = name_base + 'bn3')(x) # 对输入张量进行1x1卷积,以匹配输出张量的维度 shortcut = Conv2D(filters3, (1,1), strides = strides, name = res_name_base + 'conv1')(input_tensor) shortcut = BatchNormalization(name = res_name_base + 'bn1')(shortcut) # 将卷积结果与输入张量相加,实现残差连接 x = layers.add([x, shortcut], name = res_name_base + 'add') x = Activation('relu', name = res_name_base + 'relu4')(x) return x def ResNet50(input_shape=[224, 224, 3], classes=1000): """ 构建ResNet50模型。 参数: - input_shape: 输入图像的形状,默认为 [224, 224, 3]。 - classes: 分类任务的类别数,默认为 1000。 返回: - 构建好的ResNet50模型。 """ img_input = Input(shape=input_shape) x = ZeroPadding2D((3,3))(img_input) # 初始卷积层 x = Conv2D(64, (7,7), strides = (2,2), name = 'conv1')(x) x = BatchNormalization(name = 'bn_conv1')(x) x = Activation('relu')(x) x = MaxPooling2D((3,3), strides = (2,2))(x) # 第一阶段:包含一个卷积块和两个恒等块 x = conv_block(x, 3, [64,64,256], stage=2, block='a', strides=(1,1)) x = identity_block(x, 3, [64,64,256], stage=2, block='b') x = identity_block(x, 3, [64,64,256], stage=2, block='c') # 第二阶段:包含一个卷积块和三个恒等块 x = conv_block(x, 3, [128,128,512], stage=3, block='a') x = identity_block(x, 3, [128,128,512], stage=3, block='b') x = identity_block(x, 3, [128,128,512], stage=3, block='c') x = identity_block(x, 3, [128,128,512], stage=3, block='d') # 第三阶段:包含一个卷积块和五个恒等块 x = conv_block(x, 3, [256,256,1024], stage=4, block='a') x = identity_block(x, 3, [256,256,1024], stage=4, block='b') x = identity_block(x, 3, [256,256,1024], stage=4, block='c') x = identity_block(x, 3, [256,256,1024], stage=4, block='d') x = identity_block(x, 3, [256,256,1024], stage=4, block='e') x = identity_block(x, 3, [256,256,1024], stage=4, block='f') # 第四阶段:包含一个卷积块和两个恒等块 x = conv_block(x, 3, [512,512,2048], stage=5, block='a') x = identity_block(x, 3, [512,512,2048], stage=5, block='b') x = identity_block(x, 3, [512,512,2048], stage=5, block='c') # 全局平均池化层和全连接层 x = AveragePooling2D((7,7), name = 'avg_pool')(x) x = Flatten()(x) x = Dense(classes, activation='softmax', name='fc1000')(x) model = Model(img_input, x, name='resnet50') # 加载预训练权重 model.load_weights('./models/resnet50_weights_tf_dim_ordering_tf_kernels.h5') return model # 初始化一个ResNet50模型实例
# 参数说明:
# - input_shape: 输入图像的形状,格式为[height, width, channels],此处为[224, 224, 3],表示224x224像素的RGB图像
# - classes: 分类任务的类别数量,此处为class_names列表的长度,表示模型将输出对应类别的概率
model = ResNet50() # 打印模型的摘要信息,包括每一层的名称、输出形状和参数数量
model.summary() model.compile( # 使用Adam优化器,学习率初始值为0.001 optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), # 设置损失函数为交叉熵损失函数 loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), # 设置性能指标列表,将在模型训练时监控列表中的指标 metrics=['accuracy']
) # 训练模型并记录训练过程中的历史数据
#
# 参数:
# train_ds: 训练数据集,通常是一个tf.data.Dataset对象,包含训练数据。
# validation_data: 验证数据集,通常是一个tf.data.Dataset对象,用于在训练过程中评估模型性能。
# epochs: 训练的轮数,即模型将遍历整个训练数据集的次数。
#
# 返回值:
# history: 一个History对象,包含训练过程中的损失和评估指标的历史记录。 epochs = 10
history = model.fit( train_ds, validation_data=val_ds, epochs=epochs
) # 评估模型
# 该代码块用于绘制模型训练过程中的准确率和损失曲线,以便可视化模型在训练集和验证集上的表现。 # 从训练历史记录中提取训练集和验证集的准确率及损失值
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss'] # 生成一个范围,表示训练的轮数(epochs)
epochs_range = range(epochs) # 创建一个大小为12x4的图形窗口
plt.figure(figsize=(12, 4)) # 在图形窗口的第一个子图中绘制训练集和验证集的准确率曲线
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right') # 添加图例,位置在右下角
plt.title('Training and Validation Accuracy') # 设置子图标题 # 在图形窗口的第二个子图中绘制训练集和验证集的损失曲线
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right') # 添加图例,位置在右上角
plt.title('Training and Validation Loss') # 设置子图标题 # 显示绘制的图形
plt.show() # 预测
# 该函数用于展示验证数据集中的图片,并使用训练好的模型对图片进行预测,显示预测结果。
# 函数的主要步骤包括:
# 1. 创建一个大小为10x5的图形窗口。
# 2. 设置图形的总标题为“图片预测”。
# 3. 从验证数据集中取出一批图片和标签。
# 4. 对每张图片进行预测,并在子图中显示图片和预测结果。
# 5. 关闭子图的坐标轴显示。 plt.figure(figsize=(10, 5)) # 创建一个大小为10x5的图形窗口
plt.suptitle("图片预测") # 设置图形的总标题为“图片预测” # 从验证数据集中取出一批图片和标签
for images, labels in val_ds.take(1): # 遍历前8张图片,并在子图中显示图片和预测结果 for i in range(8): ax = plt.subplot(2, 4, i+1) # 创建2行4列的子图,并选择第i+1个子图 plt.imshow(images[i].numpy().astype("uint8")) # 显示第i张图片 # 对图片进行预测 img_array = tf.expand_dims(images[i], 0) # 扩展图片的维度以适应模型输入 predictions = model.predict(img_array) # 使用模型进行预测 # 在子图标题中显示预测结果 plt.title(class_names[np.argmax(predictions)]) plt.axis("off") # 关闭子图的坐标轴显示
相关文章:

第J1周:ResNet50算法(Tensorflow版)
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 目标 具体实现 (一)环境 语言环境:Python 3.10 编 译 器: PyCharm 框 架: TensorFlow (二)具体…...
炸裂函数explode
在 Apache Hive 中,"炸裂函数"通常指的是将复杂数据类型(如数组或映射)拆分成多行的函数。Hive 提供了几个内置函数来实现这种操作,其中最常用的是 explode 函数。 1. explode 函数 explode 函数用于将数组或映射类型…...

计算机视觉(opencv-python)之图像预处理基本操作(待补充)
图像预处理是计算机视觉任务中的关键步骤,它通过对原始图像进行处理,以提高后续图像分析、特征提取和识别的准确性。 示例图片 目录 常见图像处理方法 灰度化处理 法一 法二 说明 切片截取部分图像数据 cv2.cvtColor() 颜色空间转换 cv2.split(…...
数据结构秘籍(四) 堆 (详细包含用途、分类、存储、操作等)
1 引言 什么是堆? 堆是一种满足以下条件的树:(树这一篇可以参考我的文章数据结构秘籍(三)树 (含二叉树的分类、存储和定义)-CSDN博客) 堆中的每一个结点值都大于等于(…...

前端正则表达式完全指南:从入门到实战
文章目录 第一章:正则表达式基础概念1.1 什么是正则表达式1.2 正则表达式工作原理1.3 基础示例演示 第二章:正则表达式核心语法2.1 元字符大全表2.2 量词系统详解2.3 字符集合与排除 第三章:前端常用正则模式3.1 表单验证类3.1.1 邮箱验证3.1…...

【SRC实战】小游戏漏洞强制挑战
小游戏业务分析: 1、挑战成功加分,失败减分,存在段位机制,段位影响榜单排名 2、随机推荐挑战对象,随着等级升高不再推荐低等级玩家 3、玩家等级需要培养,培养需要道具,道具需要看广告/完成任务/付费 4、…...

细说 Java 集合之 Map
前言:本文基于JDK8 一、HashMap 1.1、hash方法 hash方法是map中的基石,后续很多操作都依赖hash方法; 下面是 jdk 7 中 hash方法,注意hashSeed 这个扰动因子,该值随机,所以同一个 key 每次调用hash方法后…...
【vue-echarts】——05.柱状图
文章目录 一、柱状图基本设置1.实现代码2.结果展示二、柱状图效果实现11.代码实现2.结果展示三、柱状图效果实现21.代码实现2.结果展示一、柱状图基本设置 柱状图:一种图表类型,因为构成是由一根一根类似柱子的数据条组合而成的坐标平面,所以命名为柱状 图。主要是用来反应对…...

【C】链式二叉树算法题1 -- 单值二叉树
leetcode链接https://leetcode.cn/problems/univalued-binary-tree/description/ 1 题目描述 如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时,才返回 true;否则返回 false。 示例 1࿱…...

C++11——智能指针和function库
目录 一、智能指针 1. std::unique_ptr(独占所有权指针) 2. std::shared_ptr(共享所有权指针) 3. std::weak_ptr(弱引用指针) 关键区别总结 最佳实践 基本用法 可封装的对象类型 核心特性 示例代码…...

[操作系统] 文件的软链接和硬链接
文章目录 引言硬链接(Hard Link)什么是硬链接?硬链接的特性硬链接的用途 软链接(Symbolic Link)什么是软链接?软链接的特性软链接的用途 软硬链接对比文件的时间戳实际应用示例使用硬链接节省备份空间用软链…...

RabbitMQ面试题及原理
RabbitMQ使用场景: 异步发送(验证码、短信、邮件…)MYSQL和Redis, ES之间的数据同步分布式事务削峰填谷 1. 消息可靠性(不丢失) 消息丢失场景: RabbitMQ-如何保证消息不丟失? 开启生产者确…...
SpringBoot中Get请求和POST请求接收参数详解
1、Get请求 1.1 方法形参接收参数 这种方式一般适用参数比较少的情况,并且前后端参数名称必须保持一致 RestController RequestMapping(“/user”) Slf4j public class DemoController { GetMapping("/query") public void getStudent(String name,Strin…...

分布式日志和责任链路
目录 日志问题 责任链问题 分布式日志 GrayLog简介 部署安装 收集日志 配置Inputs 集成微服务 日志回收策略 搜索语法 搜索语法 自定义展示字段 日志统计仪表盘 创建仪表盘 链路追踪 APM 什么是APM 原理 技术选型 Skywalking简介 部署安装 微服务探针 整合…...

h5 IOS端渐变的兼容问题 渐变实现弧形效果
IOS端使用渐变的时候有兼容问题 以下是问题效果,图中黑色部分期望的效果应该是白色的。但是ios端是下面的样子…… 安卓pc 支持: background-image: radial-gradient(circle 40rpx at 100% 0, #f3630c 40rpx, rgb(255, 255, 255) 50%);安卓pc ios支持…...

哈希算法--猜数字游戏
1.题目要求 输入两个位数相同的数,判断对应位置的数字是否相等,返回两个数。第一个数是数字和位置完全猜对的数字个数,第二个数是数字大小猜对但位置不对的数字个数 2.逐步编程 2.1 定义函数 def g(secret,guess):sec_dic{}gue_dic{}# 定义…...

idea生成自定义Maven原型(archetype)项目工程模板
一、什么是Maven原型(Maven archetype) 引自官网的介绍如下: Maven原型插件官网地址 这里采用DeepSeek助手翻译如下: Maven 原型 什么是原型? 简而言之,原型是一个 Maven 项目模板工具包。原型被定义为一…...

Redis面试常见问题——使用场景问题
目录 Redis面试常见问题 如果发生了缓存穿透、击穿、雪崩,该如何解决? 缓存穿透 什么是布隆过滤器? 缓存击穿 缓存雪崩 双写一致性(redis做为缓存,mysql的数据如何与redis进行同步呢?) …...
样式和ui(待更新)
element-plus 先在项目下执行安装语句执行按需导入的命令按照官方文档修改vitest.json sass样式定制 npm -i sass -D在项目下准备定制的样式文件 styles/element/index.scss(!注意这里是.scss文件在vitest.json 修改配置文件 Components({resolvers: [ElementPlusResolver(…...

大摩闭门会:250228 学习总结报告
如果图片分辨率不足,可右键图片在新标签打开图片或者下载末尾源文件进行查看 本文只是针对视频做相应学术记录,进行学习讨论使用...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
Easy Excel
Easy Excel 一、依赖引入二、基本使用1. 定义实体类(导入/导出共用)2. 写 Excel3. 读 Excel 三、常用注解说明(完整列表)四、进阶:自定义转换器(Converter) 其它自定义转换器没生效 Easy Excel在…...

Android Framework预装traceroute执行文件到system/bin下
文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数(使用 ICMP Echo 请求)-T 参数(使用 TCP SYN 包) 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11,在/s…...