当前位置: 首页 > news >正文

【深度学习实验】卷积神经网络(八):使用深度残差神经网络ResNet完成图片多分类任务

目录

一、实验介绍

二、实验环境

1. 配置虚拟环境

2. 库版本介绍

三、实验内容

0. 导入必要的工具包

1. 构建数据集(CIFAR10Dataset)

 a. read_csv_labels()

b. CIFAR10Dataset

 2. 构建模型(FeedForward)

3.整合训练、评估、预测过程(Runner)

4. __main__

5. 代码整合


一、实验介绍

        本实验实现了实现深度残差神经网络ResNet,并基于此完成图像分类任务。

        残差网络(ResNet)是一种深度神经网络架构,用于解决深层网络训练过程中的梯度消失和梯度爆炸问题。通过引入残差连接(residual connection)来构建网络层与层之间的跳跃连接,使得网络可以更好地优化深层结构。

        残差网络的一个重要应用是在图像识别任务中,特别是在深度卷积神经网络(CNN)中。通过使用残差模块,可以构建非常深的网络,例如ResNet,其在ILSVRC 2015图像分类挑战赛中取得了非常出色的成绩。

        在ResNet中,每个残差块由一个或多个卷积层组成,其中包含了跳跃连接。跳跃连接将输入直接添加到残差块的输出中,从而使得网络可以学习残差函数,即残差块只需学习将输入的变化部分映射到输出,而不需要学习完整的映射关系。这种设计有助于减轻梯度消失问题,使得网络可以更深地进行训练。

二、实验环境

        本系列实验使用了PyTorch深度学习框架,相关操作如下:

1. 配置虚拟环境

conda create -n DL python=3.7 
conda activate DL
pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
conda install matplotlib
 conda install scikit-learn

2. 库版本介绍

软件包本实验版本目前最新版
matplotlib3.5.33.8.0
numpy1.21.61.26.0
python3.7.16
scikit-learn0.22.11.3.0
torch1.8.1+cu1022.0.1
torchaudio0.8.12.0.2
torchvision0.9.1+cu1020.15.2

三、实验内容

0. 导入必要的工具包

import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torchvision.io import read_image

1. 构建数据集(CIFAR10Dataset)

        CIFAR10数据集共有60000个样本,每个样本都是一张32*32像素的RGB图像(彩色图像),每个RGB图像又必定分为3个通道(R通道、G通道、B通道)。CIFAR10中有10类物体,标签值分别按照0~9来区分,他们分别是飞机( airplane )、汽车( automobile )、鸟( bird )、猫( cat )、鹿( deer )、狗( dog )、青蛙( frog )、马( horse )、船( ship )和卡车( truck )。为减小运行时间,本实验选取其中1000张作为训练集。

数据集链接:

CIFAR-10 and CIFAR-100 datasets (toronto.edu)icon-default.png?t=N7T8http://www.cs.toronto.edu/~kriz/cifar.html

 a. read_csv_labels()

        从CSV文件中读取标签信息并返回一个标签字典。

def read_csv_labels(fname):"""读取fname来给标签字典返回一个文件名"""with open(fname, 'r') as f:# 跳过文件头行(列名)lines = f.readlines()[1:]tokens = [l.rstrip().split(',') for l in lines]return dict(((name, label) for name, label in tokens))
  •  使用open函数打开指定文件名的CSV文件,并将文件对象赋值给变量f。这里使用'r'参数以只读模式打开文件。

  • 使用文件对象的readlines()方法读取文件的所有行,并将结果存储在名为lines的列表中。通过切片操作[1:],跳过了文件的第一行(列名),将剩余的行存储在lines列表中。

  • 列表推导式(list comprehension):对lines列表中的每一行进行处理。对于每一行,使用rstrip()方法去除行末尾的换行符,并使用split(',')方法将行按逗号分割为多个标记。最终,将所有行的标记组成的子列表存储在tokens列表中。

  • 使用字典推导式(dictionary comprehension)将tokens列表中的子列表转换为字典。对于tokens中的每个子列表,将子列表的第一个元素作为键(name),第二个元素作为值(label),最终返回一个包含这些键值对的字典。

b. CIFAR10Dataset

class CIFAR10Dataset(Dataset):def __init__(self, folder_path, fname):self.labels = read_csv_labels(os.path.join(folder_path, fname))self.folder_path = os.path.join(folder_path, 'train')def __len__(self):return len(self.labels)def __getitem__(self, idx):img = read_image(self.folder_path + '/' + str(idx + 1) + '.png')label = self.labels[str(idx + 1)]return img, torch.tensor(int(label))
  • 构造函数:

    • 接受两个参数

      • folder_path表示数据集所在的文件夹路径

      • fname表示包含标签信息的文件名。

    • 调用read_csv_labels函数,传递folder_pathfname作为参数,以读取CSV文件中的标签信息,并将返回的标签字典存储在self.labels变量中。

    • 通过拼接folder_path和字符串'train'来构建数据集的文件夹路径,将结果存储在self.folder_path变量中。

  • def __len__(self)

    • 这是CIFAR10Dataset类的方法,用于返回数据集的长度,即样本的数量。

  • def __getitem__(self, idx): 这是CIFAR10Dataset类的方法,用于根据给定的索引idx获取数据集中的一个样本。它首先根据索引idx构建图像文件的路径,并调用read_image函数来读取图像数据,将结果存储在img变量中。然后,它通过将索引转换为字符串,并使用该字符串作为键来从self.labels字典中获取相应的标签,将结果存储在label变量中。最后,它返回一个元组,包含图像数据和经过torch.tensor转换的标签。

 2. 构建模型(FeedForward)

        参考前文:

【深度学习实验】卷积神经网络(七):实现深度残差神经网络ResNet-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/133705834

3.整合训练、评估、预测过程(Runner)

        参考前文:

【深度学习实验】前馈神经网络(九):整合训练、评估、预测过程(Runner)_QomolangmaH的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/133219448?spm=1001.2014.3001.5501

4. __main__

if __name__ == '__main__':batch_size = 20# 构建训练集train_data = CIFAR10Dataset('cifar10_tiny', 'trainLabels.csv')train_iter = DataLoader(train_data, batch_size=batch_size)# 构建测试集test_data = CIFAR10Dataset('cifar10_tiny', 'trainLabels.csv')test_iter = DataLoader(test_data, batch_size=batch_size)# 模型训练num_classes = 10# 定义模型model = ResNet(num_classes)# 定义损失函数loss_fn = F.cross_entropy# 定义优化器optimizer = torch.optim.SGD(model.parameters(), lr=0.1)runner = Runner(model, optimizer, loss_fn, metric=None)runner.train(train_iter, num_epochs=10, save_path='chapter_5')# 模型预测runner.load_model('chapter_5.pth')x, label = next(iter(test_iter))predict = torch.argmax(runner.predict(x.float()), dim=1)print('predict:', predict)print('  label:', label)

5. 代码整合

# 导入必要的工具包
import torch
from torch import nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torchvision.io import read_image#  残差连接, 输入和输出的维度有时是相同的, 有时是不同的, 所以需要 use_1x1conv来判断是否需要
class Residual(nn.Module):def __init__(self, input_channels, num_channels, use_1x1conv=False, strides=1):super().__init__()self.conv1 = nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1, stride=strides)self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1)if use_1x1conv:self.conv3 = nn.Conv2d(input_channels, num_channels, kernel_size=1, stride=strides)else:self.conv3 = None# 批量归一化层,将会在第7章讲到self.bn1 = nn.BatchNorm2d(num_channels)self.bn2 = nn.BatchNorm2d(num_channels)def forward(self, X):Y = F.relu(self.bn1(self.conv1(X)))Y = self.bn2(self.conv2(Y))if self.conv3:X = self.conv3(X)Y += Xreturn F.relu(Y)# 残差网络是由几个不同的残差块组成的
def resnet_block(input_channels, num_channels, num_residuals, first_block=False):blk = []for i in range(num_residuals):if i == 0 and not first_block:blk.append(Residual(input_channels, num_channels,use_1x1conv=True, strides=2))else:blk.append(Residual(num_channels, num_channels))return blkclass ResNet(nn.Module):def __init__(self, num_classes):super().__init__()self.b1 = nn.Sequential(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))self.b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))self.b3 = nn.Sequential(*resnet_block(64, 128, 2))self.b4 = nn.Sequential(*resnet_block(128, 256, 2))self.b5 = nn.Sequential(*resnet_block(256, 512, 2))self.head = nn.Sequential(nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(512, num_classes))def forward(self, x):net = nn.Sequential(self.b1, self.b2, self.b3, self.b4, self.b5, self.head)return net(x)import osdef read_csv_labels(fname):"""读取fname来给标签字典返回一个文件名"""with open(fname, 'r') as f:# 跳过文件头行(列名)lines = f.readlines()[1:]tokens = [l.rstrip().split(',') for l in lines]return dict(((name, label) for name, label in tokens))class CIFAR10Dataset(Dataset):def __init__(self, folder_path, fname):self.labels = read_csv_labels(os.path.join(folder_path, fname))self.folder_path = os.path.join(folder_path, 'train')def __len__(self):return len(self.labels)def __getitem__(self, idx):img = read_image(self.folder_path + '/' + str(idx + 1) + '.png')label = self.labels[str(idx + 1)]return img, torch.tensor(int(label))class Runner(object):def __init__(self, model, optimizer, loss_fn, metric=None):self.model = modelself.optimizer = optimizerself.loss_fn = loss_fn# 用于计算评价指标self.metric = metric# 记录训练过程中的评价指标变化self.dev_scores = []# 记录训练过程中的损失变化self.train_epoch_losses = []self.dev_losses = []# 记录全局最优评价指标self.best_score = 0# 模型训练阶段def train(self, train_loader, dev_loader=None, **kwargs):# 将模型设置为训练模式,此时模型的参数会被更新self.model.train()num_epochs = kwargs.get('num_epochs', 0)log_steps = kwargs.get('log_steps', 100)save_path = kwargs.get('save_path', 'best_model.pth')eval_steps = kwargs.get('eval_steps', 0)# 运行的step数,不等于epoch数global_step = 0if eval_steps:if dev_loader is None:raise RuntimeError('Error: dev_loader can not be None!')if self.metric is None:raise RuntimeError('Error: Metric can not be None')# 遍历训练的轮数for epoch in range(num_epochs):total_loss = 0# 遍历数据集for step, data in enumerate(train_loader):x, y = datalogits = self.model(x.float())loss = self.loss_fn(logits, y.long())total_loss += lossif step % log_steps == 0:print(f'loss:{loss.item():.5f}')loss.backward()self.optimizer.step()self.optimizer.zero_grad()# 每隔一定轮次进行一次验证,由eval_steps参数控制,可以采用不同的验证判断条件if eval_steps != 0:if (epoch + 1) % eval_steps == 0:dev_score, dev_loss = self.evaluate(dev_loader, global_step=global_step)print(f'[Evalute] dev score:{dev_score:.5f}, dev loss:{dev_loss:.5f}')if dev_score > self.best_score:self.save_model(f'model_{epoch + 1}.pth')print(f'[Evaluate]best accuracy performance has been updated: {self.best_score:.5f}-->{dev_score:.5f}')self.best_score = dev_score# 验证过程结束后,请记住将模型调回训练模式self.model.train()global_step += 1# 保存当前轮次训练损失的累计值train_loss = (total_loss / len(train_loader)).item()self.train_epoch_losses.append((global_step, train_loss))self.save_model(f'{save_path}.pth')print('[Train] Train done')# 模型评价阶段def evaluate(self, dev_loader, **kwargs):assert self.metric is not None# 将模型设置为验证模式,此模式下,模型的参数不会更新self.model.eval()global_step = kwargs.get('global_step', -1)total_loss = 0self.metric.reset()for batch_id, data in enumerate(dev_loader):x, y = datalogits = self.model(x.float())loss = self.loss_fn(logits, y.long()).item()total_loss += lossself.metric.update(logits, y)dev_loss = (total_loss / len(dev_loader))self.dev_losses.append((global_step, dev_loss))dev_score = self.metric.accumulate()self.dev_scores.append(dev_score)return dev_score, dev_loss# 模型预测阶段,def predict(self, x, **kwargs):self.model.eval()logits = self.model(x)return logits# 保存模型的参数def save_model(self, save_path):torch.save(self.model.state_dict(), save_path)# 读取模型的参数def load_model(self, model_path):self.model.load_state_dict(torch.load(model_path, map_location=torch.device('cpu')))if __name__ == '__main__':batch_size = 20# 构建训练集train_data = CIFAR10Dataset('cifar10_tiny', 'trainLabels.csv')train_iter = DataLoader(train_data, batch_size=batch_size)# 构建测试集test_data = CIFAR10Dataset('cifar10_tiny', 'trainLabels.csv')test_iter = DataLoader(test_data, batch_size=batch_size)# 模型训练num_classes = 10# 定义模型model = ResNet(num_classes)# 定义损失函数loss_fn = F.cross_entropy# 定义优化器optimizer = torch.optim.SGD(model.parameters(), lr=0.1)runner = Runner(model, optimizer, loss_fn, metric=None)runner.train(train_iter, num_epochs=15, save_path='chapter_5')# 模型预测runner.load_model('chapter_5.pth')x, label = next(iter(test_iter))predict = torch.argmax(runner.predict(x.float()), dim=1)print('predict:', predict)print('  label:', label)

相关文章:

【深度学习实验】卷积神经网络(八):使用深度残差神经网络ResNet完成图片多分类任务

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 构建数据集(CIFAR10Dataset) a. read_csv_labels() b. CIFAR10Dataset 2. 构建模型(FeedForward&#x…...

HarmonyOS学习 -- ArkTS开发语言入门

文章目录 一、编程语言介绍二、TypeScript基础类型1. 布尔值2. 数字3. 字符串4. 数组5. 元组6. 枚举7. unknown8. void9. null 和 undefined10. 联合类型 三、TypeScript基础知识条件语句if语句switch语句 函数定义有名函数和匿名函数可选参数剩余参数箭头函数 类1. 类的定义2.…...

早安心语|不委屈不将就,让生活充满仪式感

1、让自己的生活多一种可能,给自己的未来多一份惊喜,人生所有的机会和惊喜,都是在你全力以赴的道路上遇到的。 2、推开自己喜欢的人叫成长,留住自己喜欢的人叫本事,总有人嫌你不够好,也有人觉得你哪都好&am…...

[Python进阶] 操纵键盘:pyuserinput

6.3 操纵键盘:pyuserinput 6.3.1 说明 在安装pyuserinput库时会自动安装PyMouse和PyKeyboard库。前者主要用来操作鼠标,包括鼠标的点击、移动等。后者主要用来操作键盘,包括键盘按键的按下、弹起等。这两个库还可以同时对鼠标和键盘的事件进…...

解析Moonbeam的安全性、互操作性和市场竞争力

Moonbeam依托Polkadot Substrate框架构建,用Rust程序设计语言创建的智能合约区块链平台,在继承Polkadot安全性的基础上为项目提供以太坊虚拟机(EVM)的兼容性和原生的跨链互操作性优势。Moonbeam的EVM兼容性表示开发者无需学习Subs…...

RPA是什么?怎么成为RPA高手?

RPA(Robotic Process Automation,机器人流程自动化)是一种技术,通过软件机器人模拟人类在计算机上执行重复性任务,从而提高生产力、减少错误并降低成本。RPA 可以广泛应用于金融、医疗、制造、零售等多个行业&#xff…...

Apache Shiro 漏洞复现

文章目录 Apache Shiro 漏洞复现1. Apache Shiro 1.2.4 反序列化漏洞1.1 漏洞描述1.2 漏洞原理1.3 漏洞复现1.3.1 环境启动 1.4 漏洞利用1.5 修复方案 Apache Shiro 漏洞复现 链接地址:Vulhub - Docker-Compose file for vulnerability environment 1. Apache Shi…...

炒现货白银的最佳时间

天时地利人和是我们进行现货白银投资最关键的因素。天时是指我们因时而动,在适合的时机出击。地利,就是我们对市场的定位,对自己入场的定位有清晰的了解,并且这些位置对我们有利。人和就是指投资者的状态很好,对如何进…...

C# OpenVINO 人脸识别

效果 耗时 Preprocess: 1.41ms Infer: 4.38ms Postprocess: 0.03ms Total: 5.82ms 项目 代码 using OpenCvSharp; using Sdcb.OpenVINO; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Text; using Syste…...

ESP32-WROOM-32无法进入下载模式进行程序上传的问题

结论 先说结论,ESP32-WROOM-32无法进入下载模式通过串口进行程序上传,可能是GPIO2引脚没有通过下拉电阻拉低,导致无法进入正确的启动模式。 启动模式 ESP32启动时会打印rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) 复位源rs…...

尚硅谷Flink(一)

目录 ☄️前置工作 fenfa脚本 🌋概述 ☄️Flink是什么 ☄️特点(多nb) ☄️应用场景(不用看) ☄️分层API 🌋配环境 ☄️wordcount ☄️WcDemoUnboundStreaming 🌋集群部署 ☄️集…...

C++ 设计模式 —— 桥接模式

C 设计模式 —— 桥接模式 0. 引用连接 本文主要的思路和代码,来自于对以下连接的学习和实现: 桥接模式 1. 引言 1.1 什么是桥接模式? 桥接模式的定义桥接模式的作用 桥接模式,顾名思义,就像是一座连接两岸的桥…...

微信怎么删除好友?非常简单,2个方法!

随着生活和工作的节奏加快,这也导致我们微信里的联系人变得越来越多。有时候,我们可能只是需要给对方转钱、发送照片或者是一些其他理由。 而这部分“好友”可能除了这次交流后再也没有别的联系了,那么这时候大家可能会想把他们删除。那么微…...

小谈设计模式(25)—职责链模式

小谈设计模式(25)—职责链模式 专栏介绍专栏地址专栏介绍 职责链模式分析角色分析抽象处理者(Handler)具体处理者(ConcreteHandler)客户端(Client) 优缺点分析优点123 缺点12 应用场…...

Python- JSON-RPC创建一个远程过程调用

我们使用JSON-RPC创建一个远程过程调用的例子,我们将使用jsonrpcserver库和Flask框架创建一个后端服务,并使用jsonrpcclient作为客户端。这个例子将包括: 一个计算服务,提供加、减、乘、除四个方法。错误处理:除数为零…...

Linux中scp命令复制文件

scp命令是在Linux中用于在本地主机和远程主机之间进行安全传输文件的命令。下面是使用scp命令的语法: scp [参数] [来源路径] [目标路径]参数: -r:递归复制整个目录。-P:指定远程主机的端口。-p:保留原文件的修改时间…...

Interlay采用Moonbeam路由流动性,为波卡发展更多流动性

波卡上的首选多链开发平台Moonbeam宣布Interlay现在支持由Carrier赋能的Moonbeam路由流动性。Carrier是一个功能强大的token和NFT跨链桥,支持超过12个网络。Interlay是波卡上的一条平行链,与HydraDX一起通过Wormhole、Moonbeam和Carrier为波卡生态挖掘流…...

Jetson Orin NX 开发指南(9): Pixhawk 6X 飞控固件的烧写与 QGroundControl 参数配置

一、前言 由于 Jetson Orin NX 常被用作自主无人机机载电脑,其往往需要与烧写了 PX4 固件的飞控进行通信,飞控的烧写与配置往往会遇到很多问题,因此本文将介绍时下最款的 Pixhawk 系列飞控 Pixhawk 6X,做一个固件烧写和参数配置的…...

Redis(四)多级缓存

文章目录 一、传统缓存存在的问题二、多级缓存方案三、JVM进程缓存案例演示: 四、Lua语法入门Lua语言入门 五、多级缓存(一)安装OpenResty(二)OpenResty入门(三)请求参数处理(四&…...

网站安全防护

1.确保环境安全例如近期LNMP投毒事件 2.nginx/php隐藏版本号/关闭报错显示行 3.限制3306端口/phpMyAdmin尽量不用 4.修改指纹例如X-Powered-By/Cookie 5.上线前后进行主机漏扫网站漏扫 6.系统安装杀毒ClamAV 7.更改后台管理入口 8.安装雷池WAF防护 9.网站使用https协议 10.后台…...

Scrcpy:重新定义安卓设备跨平台交互体验

Scrcpy:重新定义安卓设备跨平台交互体验 【免费下载链接】scrcpy Display and control your Android device 项目地址: https://gitcode.com/gh_mirrors/sc/scrcpy 一、跨设备交互的现实困境:发现问题本质 在数字化办公与移动开发的日常场景中&a…...

PCL (Matlab)拟合椭球

一、椭球点云数学模型二、PCL生成点云int main() {// 生成椭球点云 噪声pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);// 椭球参数float a 2.0f; // x轴float b 1.5f; // y轴float c 1.0f; // z轴int N 20000;// 随机数…...

如何在5分钟内开始使用Ivy Wallet:新手入门教程

如何在5分钟内开始使用Ivy Wallet&#xff1a;新手入门教程 【免费下载链接】ivy-wallet Ivy Wallet is an open-source money manager app for android that you can either build or download from Google Play. 项目地址: https://gitcode.com/gh_mirrors/iv/ivy-wallet …...

Mid-70激光雷达与相机无目标标定:从环境搭建到实战避坑

1. 为什么选择Ubuntu 16.04进行Mid-70标定 最近在给Livox Mid-70激光雷达做相机标定时&#xff0c;我踩了个大坑——在Ubuntu 22.04上折腾了整整两天都没搞定环境配置。后来才发现问题出在版本兼容性上&#xff1a;ROS Kinetic、Ceres 1.14.x和Eigen 3.2.92这几个关键组件在新系…...

HARMONYOS应用实例258:反比例函数图像

反比例函数图像 功能:绘制双曲线,点击图像上的点显示坐标,验证 xy=kxy=kxy=k 的恒等关系。 应用功能: 绘制反比例函数双曲线图像 y = k/x 可调节k值,范围从1到20...

Ceph存储集群搭建:如何选择RAID卡模式(HBA vs IT vs non-RAID)

Ceph存储集群搭建&#xff1a;RAID卡模式选择与性能优化实战指南 在构建企业级Ceph存储集群时&#xff0c;硬件配置的每一个细节都可能成为性能瓶颈或稳定性隐患。其中&#xff0c;RAID控制器的工作模式选择——HBA、IT与non-RAID之间的差异&#xff0c;往往被许多初次部署Ceph…...

告别格式枷锁:ncmdumpGUI让音乐自由播放变得触手可及

告别格式枷锁&#xff1a;ncmdumpGUI让音乐自由播放变得触手可及 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换&#xff0c;Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 开篇痛点直击&#xff1a;那些被NCM格式困住的…...

嵌入式AI新篇章:Qwen3-ASR-0.6B在边缘计算设备上的部署与优化

嵌入式AI新篇章&#xff1a;Qwen3-ASR-0.6B在边缘计算设备上的部署与优化 1. 引言&#xff1a;当语音识别遇见边缘计算 想象一下&#xff0c;你对着一个巴掌大的智能音箱说话&#xff0c;它几乎在你话音落下的瞬间就理解了你的意思&#xff0c;并且完全不需要连接云端。或者&…...

Qwen3-0.6B应用案例:如何用它快速生成文案和邮件回复

Qwen3-0.6B应用案例&#xff1a;如何用它快速生成文案和邮件回复 1. 引言&#xff1a;轻量级AI写作助手 在日常工作中&#xff0c;我们经常需要处理大量文字工作&#xff1a;撰写产品介绍、回复客户邮件、编写营销文案等。这些任务虽然不复杂&#xff0c;但耗时耗力。Qwen3-0…...

Linux网络命名空间实战:5分钟搞定veth pair跨命名空间通信

Linux网络命名空间实战&#xff1a;5分钟搭建隔离通信环境 在云计算和容器化技术蓬勃发展的今天&#xff0c;Linux网络命名空间已经成为系统管理员和开发者的必备技能。想象一下&#xff0c;当你需要在单台物理机上同时运行多个需要独立网络环境的服务时&#xff0c;传统方式可…...