第95步 深度学习图像目标检测:Faster R-CNN建模
基于WIN10的64位系统演示
一、写在前面
本期开始,我们学习深度学习图像目标检测系列。
深度学习图像目标检测是计算机视觉领域的一个重要子领域,它的核心目标是利用深度学习模型来识别并定位图像中的特定目标。这些目标可以是物体、人、动物或其他可识别的实体。与传统的图像分类任务不同,目标检测不仅要确定图像中存在哪些类别的目标,还要确定它们的确切位置和尺寸。这通常是通过在图像上绘制一个或多个边界框来实现的,这些边界框精确地标出了目标的位置和范围。
二、Faster R-CNN简介
Faster R-CNN 是一种流行的深度学习图像目标检测算法,由 Shaoqing Ren, Kaiming He, Ross Girshick 和 Jian Sun 在 2015 年提出。它是 R-CNN 系列模型中的一个重要里程碑,因为它提高了检测速度,同时保持了高精度。以下是 Faster R-CNN 的主要特点和组件:
(1)区域提议网络 (RPN):
Faster R-CNN 的核心创新是引入了一个叫做区域提议网络 (RPN) 的组件。RPN 能够在卷积特征图上直接生成目标的边界框提议,这大大减少了提议的计算时间。RPN 使用了一组固定大小和比例的锚框(anchors),对每一个锚框预测偏移量和目标存在的概率。
(2)共享卷积特征:
与其前任 Fast R-CNN 不同,Faster R-CNN 的 RPN 和最终的目标检测都共享相同的卷积特征。这意味着图像只需要进行一次前向传播,从而大大提高了计算效率。
(3)ROI Pooling:
一旦得到了区域提议,Faster R-CNN 使用 ROI (Region of Interest) Pooling 技术来从每个提议中提取固定大小的特征。这确保无论提议的大小如何,都可以输入到一个固定大小的全连接网络中进行分类和边界框回归。
(4)双任务损失:
RPN 被训练为一个双任务问题:分类(目标 vs. 非目标)和边界框回归。这种双任务损失结构确保了 RPN 在生成提议时既考虑了准确性也考虑了定位。
总之,Faster R-CNN 通过引入区域提议网络和共享卷积特征,大大提高了目标检测的速度和精度,为后续的研究和应用打下了坚实的基础。
三、数据源
来源于公共数据,文件设置如下:


大概的任务就是:用一个框框标记出MTB的位置。
四、Faster R-CNN实战
直接上代码:
import os
import random
import torch
import torchvision
from torchvision.models.detection import fasterrcnn_resnet50_fpn
from torchvision.transforms import functional as F
from PIL import Image
from torch.utils.data import DataLoader
import xml.etree.ElementTree as ET
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
import matplotlib.pyplot as plt
from torchvision import transforms
import albumentations as A
from albumentations.pytorch import ToTensorV2
import numpy as np# Function to parse XML annotations
def parse_xml(xml_path):tree = ET.parse(xml_path)root = tree.getroot()boxes = []for obj in root.findall("object"):bndbox = obj.find("bndbox")xmin = int(bndbox.find("xmin").text)ymin = int(bndbox.find("ymin").text)xmax = int(bndbox.find("xmax").text)ymax = int(bndbox.find("ymax").text)# Check if the bounding box is validif xmin < xmax and ymin < ymax:boxes.append((xmin, ymin, xmax, ymax))else:print(f"Warning: Ignored invalid box in {xml_path} - ({xmin}, {ymin}, {xmax}, {ymax})")return boxes# Function to split data into training and validation sets
def split_data(image_dir, split_ratio=0.8):all_images = [f for f in os.listdir(image_dir) if f.endswith(".jpg")]random.shuffle(all_images)split_idx = int(len(all_images) * split_ratio)train_images = all_images[:split_idx]val_images = all_images[split_idx:]return train_images, val_images# Dataset class for the Tuberculosis dataset
class TuberculosisDataset(torch.utils.data.Dataset):def __init__(self, image_dir, annotation_dir, image_list, transform=None):self.image_dir = image_dirself.annotation_dir = annotation_dirself.image_list = image_listself.transform = transformdef __len__(self):return len(self.image_list)def __getitem__(self, idx):image_path = os.path.join(self.image_dir, self.image_list[idx])image = Image.open(image_path).convert("RGB")xml_path = os.path.join(self.annotation_dir, self.image_list[idx].replace(".jpg", ".xml"))boxes = parse_xml(xml_path)# Check for empty bounding boxes and return Noneif len(boxes) == 0:return Noneboxes = torch.as_tensor(boxes, dtype=torch.float32)labels = torch.ones((len(boxes),), dtype=torch.int64)iscrowd = torch.zeros((len(boxes),), dtype=torch.int64)target = {}target["boxes"] = boxestarget["labels"] = labelstarget["image_id"] = torch.tensor([idx])target["iscrowd"] = iscrowd# Apply transformationsif self.transform:image = self.transform(image)return image, target# Define the transformations using torchvision
data_transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(), # Convert PIL image to tensortorchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # Normalize the images
])# Adjusting the DataLoader collate function to handle None values
def collate_fn(batch):batch = list(filter(lambda x: x is not None, batch))return tuple(zip(*batch))# Function to get the Mask R-CNN model
def get_model(num_classes):model = fasterrcnn_resnet50_fpn(pretrained=True)in_features = model.roi_heads.box_predictor.cls_score.in_featuresmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)return model# Function to save the model
def save_model(model, path="mmaskrcnn_mtb.pth", save_full_model=False):if save_full_model:torch.save(model, path)else:torch.save(model.state_dict(), path)print(f"Model saved to {path}")# Function to compute Intersection over Union
def compute_iou(boxA, boxB):xA = max(boxA[0], boxB[0])yA = max(boxA[1], boxB[1])xB = min(boxA[2], boxB[2])yB = min(boxA[3], boxB[3])interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)iou = interArea / float(boxAArea + boxBArea - interArea)return iou# Adjusting the DataLoader collate function to handle None values and entirely empty batches
def collate_fn(batch):batch = list(filter(lambda x: x is not None, batch))if len(batch) == 0:# Return placeholder batch if entirely emptyreturn [torch.zeros(1, 3, 224, 224)], [{}]return tuple(zip(*batch))#Training function with modifications for collecting IoU and loss
def train_model(model, train_loader, optimizer, device, num_epochs=10):model.train()model.to(device)loss_values = []iou_values = []for epoch in range(num_epochs):epoch_loss = 0.0total_ious = 0num_boxes = 0for images, targets in train_loader:# Skip batches with placeholder dataif len(targets) == 1 and not targets[0]:continue# Skip batches with empty targetsif any(len(target["boxes"]) == 0 for target in targets):continueimages = [image.to(device) for image in images]targets = [{k: v.to(device) for k, v in t.items()} for t in targets]loss_dict = model(images, targets)losses = sum(loss for loss in loss_dict.values())optimizer.zero_grad()losses.backward()optimizer.step()epoch_loss += losses.item()# Compute IoU for evaluationwith torch.no_grad():model.eval()predictions = model(images)for i, prediction in enumerate(predictions):pred_boxes = prediction["boxes"].cpu().numpy()true_boxes = targets[i]["boxes"].cpu().numpy()for pred_box in pred_boxes:for true_box in true_boxes:iou = compute_iou(pred_box, true_box)total_ious += iounum_boxes += 1model.train()avg_loss = epoch_loss / len(train_loader)avg_iou = total_ious / num_boxesloss_values.append(avg_loss)iou_values.append(avg_iou)print(f"Epoch {epoch+1}/{num_epochs} Loss: {avg_loss} Avg IoU: {avg_iou}")# Plotting loss and IoU valuesplt.figure(figsize=(12, 5))plt.subplot(1, 2, 1)plt.plot(loss_values, label="Training Loss")plt.title("Training Loss across Epochs")plt.xlabel("Epochs")plt.ylabel("Loss")plt.subplot(1, 2, 2)plt.plot(iou_values, label="IoU")plt.title("IoU across Epochs")plt.xlabel("Epochs")plt.ylabel("IoU")plt.show()# Save model after trainingsave_model(model)# Validation function
def validate_model(model, val_loader, device):model.eval()model.to(device)with torch.no_grad():for images, targets in val_loader:images = [image.to(device) for image in images]targets = [{k: v.to(device) for k, v in t.items()} for t in targets]model(images)# Paths to your data
image_dir = "tuberculosis-phonecamera"
annotation_dir = "tuberculosis-phonecamera"# Split data
train_images, val_images = split_data(image_dir)# Create datasets and dataloaders
train_dataset = TuberculosisDataset(image_dir, annotation_dir, train_images, transform=data_transform)
val_dataset = TuberculosisDataset(image_dir, annotation_dir, val_images, transform=data_transform)# Updated DataLoader with new collate function
train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True, collate_fn=collate_fn)
val_loader = DataLoader(val_dataset, batch_size=4, shuffle=False, collate_fn=collate_fn)# Model and optimizer
model = get_model(2)
optimizer = torch.optim.SGD(model.parameters(), lr=0.005, momentum=0.9, weight_decay=0.0005)# Train and validate
train_model(model, train_loader, optimizer, device="cuda", num_epochs=100)
validate_model(model, val_loader, device="cuda")#######################################Print Metrics######################################
def calculate_metrics(predictions, ground_truths, iou_threshold=0.5):TP = 0 # True PositivesFP = 0 # False PositivesFN = 0 # False Negativestotal_iou = 0 # to calculate mean IoUfor pred, gt in zip(predictions, ground_truths):pred_boxes = pred["boxes"].cpu().numpy()gt_boxes = gt["boxes"].cpu().numpy()# Match predicted boxes to ground truth boxesfor pred_box in pred_boxes:max_iou = 0matched = Falsefor gt_box in gt_boxes:iou = compute_iou(pred_box, gt_box)if iou > max_iou:max_iou = iouif iou > iou_threshold:matched = Truetotal_iou += max_iouif matched:TP += 1else:FP += 1FN += len(gt_boxes) - TPprecision = TP / (TP + FP) if (TP + FP) != 0 else 0recall = TP / (TP + FN) if (TP + FN) != 0 else 0f1_score = (2 * precision * recall) / (precision + recall) if (precision + recall) != 0 else 0mean_iou = total_iou / (TP + FP)return precision, recall, f1_score, mean_ioudef evaluate_model(model, dataloader, device):model.eval()model.to(device)all_predictions = []all_ground_truths = []with torch.no_grad():for images, targets in dataloader:images = [image.to(device) for image in images]predictions = model(images)all_predictions.extend(predictions)all_ground_truths.extend(targets)precision, recall, f1_score, mean_iou = calculate_metrics(all_predictions, all_ground_truths)return precision, recall, f1_score, mean_ioutrain_precision, train_recall, train_f1, train_iou = evaluate_model(model, train_loader, "cuda")
val_precision, val_recall, val_f1, val_iou = evaluate_model(model, val_loader, "cuda")print("Training Set Metrics:")
print(f"Precision: {train_precision:.4f}, Recall: {train_recall:.4f}, F1 Score: {train_f1:.4f}, Mean IoU: {train_iou:.4f}")print("\nValidation Set Metrics:")
print(f"Precision: {val_precision:.4f}, Recall: {val_recall:.4f}, F1 Score: {val_f1:.4f}, Mean IoU: {val_iou:.4f}")#sheet
header = "| Metric | Training Set | Validation Set |"
divider = "+----------+--------------+----------------+"train_metrics = f"| Precision | {train_precision:.4f} | {val_precision:.4f} |"
recall_metrics = f"| Recall | {train_recall:.4f} | {val_recall:.4f} |"
f1_metrics = f"| F1 Score | {train_f1:.4f} | {val_f1:.4f} |"
iou_metrics = f"| Mean IoU | {train_iou:.4f} | {val_iou:.4f} |"print(header)
print(divider)
print(train_metrics)
print(recall_metrics)
print(f1_metrics)
print(iou_metrics)
print(divider)#######################################Train Set######################################
import numpy as np
import matplotlib.pyplot as pltdef plot_predictions_on_image(model, dataset, device, title):# Select a random image from the datasetidx = np.random.randint(50, len(dataset))image, target = dataset[idx]img_tensor = image.clone().detach().to(device).unsqueeze(0)# Use the model to make predictionsmodel.eval()with torch.no_grad():prediction = model(img_tensor)# Inverse normalization for visualizationinv_normalize = transforms.Normalize(mean=[-0.485/0.229, -0.456/0.224, -0.406/0.225],std=[1/0.229, 1/0.224, 1/0.225])image = inv_normalize(image)image = torch.clamp(image, 0, 1)image = F.to_pil_image(image)# Plot the image with ground truth boxesplt.figure(figsize=(10, 6))plt.title(title + " with Ground Truth Boxes")plt.imshow(image)ax = plt.gca()# Draw the ground truth boxes in bluefor box in target["boxes"]:rect = plt.Rectangle((box[0], box[1]), box[2]-box[0], box[3]-box[1],fill=False, color='blue', linewidth=2)ax.add_patch(rect)plt.show()# Plot the image with predicted boxesplt.figure(figsize=(10, 6))plt.title(title + " with Predicted Boxes")plt.imshow(image)ax = plt.gca()# Draw the predicted boxes in redfor box in prediction[0]["boxes"].cpu():rect = plt.Rectangle((box[0], box[1]), box[2]-box[0], box[3]-box[1],fill=False, color='red', linewidth=2)ax.add_patch(rect)plt.show()# Call the function for a random image from the train dataset
plot_predictions_on_image(model, train_dataset, "cuda", "Selected from Training Set")#######################################Val Set####################################### Call the function for a random image from the validation dataset
plot_predictions_on_image(model, val_dataset, "cuda", "Selected from Validation Set")
不解读了,给出GPT的咒语参考:
咒语:我有一批数据,存在“tuberculosis-phonecamera”文件夹中,包括两部分:
一部分是MTB的痰涂片抗酸染色图片,为jpg格式,命名为“tuberculosis-phone-0001.jpg”、“tuberculosis-phone-0002.jpg”等;
一部分是MTB的痰涂片抗酸染色图片对应的注释文件,主要内容是标注MTB的痰涂片抗酸染色图片中MTB的具体位置,是若干个红色框,为XML格式,命名为“tuberculosis-phone-0001.xml”、“tuberculosis-phone-0002.xml”等,我上传一个xml文件给你做例子;
我需要基于上面的数据,使用pytorch建立一个Mask R-CNN目标识别模型,去识别MTB的痰涂片抗酸染色图片中的MTB,并使用红色框标注出来。数据需要随机分为训练集(80%)和验证集(20%)。
看看结果:
(1)loss曲线图:

(2)性能指标:

(3)训练的图片测试结果:


(4)验证集的图片测试结果:


五、写在后面
直接使用预训练模型,而且模型并没有调参。但是训练集的准确率还是挺高的,验证集就差点意思了。需要更高的性能,还得认真研究如何调参。
相关文章:
第95步 深度学习图像目标检测:Faster R-CNN建模
基于WIN10的64位系统演示 一、写在前面 本期开始,我们学习深度学习图像目标检测系列。 深度学习图像目标检测是计算机视觉领域的一个重要子领域,它的核心目标是利用深度学习模型来识别并定位图像中的特定目标。这些目标可以是物体、人、动物或其他可识…...
设计模式—里氏替换原则
1.概念 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影…...
PyTorch包
进入PyTorch的官网: pytorch GitHub 点击GitHub: 进入PyTorch的主目录: 进入Vision reference: detection: 这就是我们在训练过程中会使用到的文件了:...
22、什么是中间件和权限拦截中间件实操
新建中间件 middleware\auth.js // 定义权限判断中间件,中间件的第一个参数是context export default ({store, redirect}) > {console.log("中间件被调用")// if (!store || !store.state.userinfo) {// redirect("/")// } }页面使用…...
vue.config.js
proxy代理 proxy选项用于配置开发服务器的代理。下面是proxy的全部属性: 1. target (String | Object | Function): 指定要代理的目标主机的URL。可以是一个字符串,也可以是一个对象或函数,用于动态返回目标URL。 2. forward (Boolean): 控…...
80C51单片机----数据传送类指令
目录 一.一般传送指令,即mov指令 1.16位传送(仅1条) 2.8位传送 (1)目的字节为A(累加器) (2)目的字节为Rn(工作寄存器) (3)目的字节为direct…...
【Golang】使用泛型对数组进行去重
背景: 要求写一个方法,返回去重后的数组。数组的类型可能是int64,也可能是string,或是其他类型。 如果区分类型的话,每增加一个新的类型都需要重新写一个方法。 示例代码: //对int64数组进行去重 func DeD…...
Ps:画笔工具的基本操作
画笔工具 Brush Tool是 Ps 中最常用的工具,广泛地用于绘画与修饰工作。 虽然多数操作可在画笔工具的工具选项栏中选择执行,但是如果能记住相应的快捷键可大大提高工作效率。 熟练掌握画笔工具的操作对于使用其他工具也非常有益,因为 Ps 中许多…...
【Apache Doris】一键实现万表MySQL整库同步 | 快速体验
【Apache Doris】一键实现万表MySQL整库同步 | 快速体验) 一、 环境信息1.1 硬件信息1.2 软件信息 二、 流程介绍三、 前提概要3.1 安装部署3.2 JAR包准备3.2.1 数据源3.2.2 目标源 3.3 脚本模版 四、快速体验五、常见问题5.1 Mysql通信异常5.2 MySQL无Key同步异常5…...
35.逻辑运算符
目录 一.什么是逻辑运算符 二.C语言中的逻辑运算符 三.逻辑表达式 三.视频教程 一.什么是逻辑运算符 同时对俩个或者俩个以上的表达式进行判断的运算符叫做逻辑运算符。 举例:比如去网吧上网,只有年满十八周岁并且带身份证才可以上网。在C语言中如果…...
ASP.NET Core 启用CORS
浏览器的安全阻止一个域的本地页面请求另外不同域的本地页面,这个限制叫同源策略,这个安全特性用来阻止恶意站点从别的网站读取数据 例如假如我有一个页面叫A.html https://foo.example/A.html 现在页面A.html有一个ajax代码尝试读取B.html的HTML的源…...
io.lettuce.core.RedisCommandExecutionException
io.lettuce.core.RedisCommandExecutionException: ERR invalid password ERR invalid password-CSDN博客 io.lettuce.core.RedisCommandExecutionException /** Copyright 2011-2022 the original author or authors.** Licensed under the Apache License, Version 2.0 (the…...
vue3 导出数据为 excel 文件
文章目录 安装插件封装组件 -- Export2Excel.js多表封装界面使用 -- 数据处理成二维数组更多 菜鸟最近做了一个需求,就是需要上传表单并识别,然后识别出来的内容要可以修改,然后想的就是识别内容变成 form 表单,所以并没有使用 Sp…...
PyQt6简介
锋哥原创的PyQt6视频教程: 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计12条视频,包括:2024版 PyQt6 Python桌面开发 视频教程(无废话版…...
某60区块链安全之未初始化的存储指针实战二学习记录
系列文章目录 文章目录 系列文章目录未初始化的存储指针实战二实验目的实验环境实验工具实验原理实验内容实验过程EXP利用 未初始化的存储指针实战二 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约未初始化的存储指针漏洞 找到合约漏洞进行分析并形成利用 实验…...
软件工程第十二周
软件作坊、软件危机、软件过程控制、重型控制、敏捷、DevOps 这些术语概括了软件开发历史和实践中的几个重要概念和阶段。让我们逐一解析它们: 软件作坊(Software Craftsmanship):这是软件开发的早期模式,强调个人技能…...
electron 问题记录
23年11月24 electron项目npm install 卡在一个地方不动 原因:主要是 install electron 会卡住 解决方法: # 先解决install electron卡死 npm install -g cnpm --registryhttps://registry.npmmirror.com cnpm install electron# 然后下载其他依赖 np…...
nodejs+vue+python+PHP+微信小程序-留学信息查询系统的设计与实现-安卓-计算机毕业设计
1、用户模块: 1)登录:用户注册登录账号。 2)留学查询模块:查询学校的入学申请条件、申请日期、政策变动等。 3)院校排名:查询国外各院校的实力排名。 4)测试功能:通过入学…...
JWT和Token之间的区别
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:每天一个知识点 ✨特色专栏:…...
UserRole
Qt::UserRole 是 Qt::ItemDataRole 枚举中的一个成员,用于表示自定义数据角色(Data Role)的起始值。 在 Qt 中,Qt::ItemDataRole 枚举用于标识项(Item)中不同类型的数据。这些数据角色包括 Qt::DisplayRol…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
