基于MNIST数据集的手写数字识别(CNN)

目录

一,模型训练

1.1 数据集介绍

1.2 CNN模型层结构

1.3 定义CNN模型

1.4 神经网络的前向传播过程

1.5 数据预处理

1.6 加载数据

1.7 初始化

1.8 模型训练过程

1.9 保存模型

二,模型测试

2.1 定义与训练时相同的CNN模型架构

2.2 图像的预处理

2.3 预测

三,测试

3.1 测试方法

3.2 测试结果

四 ,总结

五,完整代码

5.1 模型训练部分代码

5.2 模型测试部分代码


本实验直观地体现了CNN对比全连接对于图像处理的优势

全连接网络实现MNIST数字识别实验如下链接:

基于MNIST数据集的手写数字识别(简单全连接网络)-CSDN博客


一,模型训练

1.1 数据集介绍

        MNIST 数据集由 60,000 张图像构成的训练集和 10,000 张图像组成的测试集构成,其中的图像均为 28×28 像素的灰度图,涵盖 0 - 9 这 10 个阿拉伯数字,且数字书写风格、大小、位置多样。它源于美国国家标准与技术研究所(NIST)的数据集,经过归一化和中心化处理。MNIST 数据集是图像识别研究领域的经典数据集,常用于开发和评估图像识别算法与模型,也是机器学习课程中常用的教学案例,许多高性能卷积神经网络模型在该数据集测试集上准确率可达 99% 以上,充分展现出其在机器学习领域的重要价值和广泛应用。

1.2 CNN模型层结构

1.3 定义CNN模型

def __init__(self):super(CNN, self).__init__()  # 调用父类的初始化方法self.conv1 = nn.Conv2d(1, 32, kernel_size=3)  # 定义第一个卷积层,输入通道1,输出通道32,卷积核大小3x3self.conv2 = nn.Conv2d(32, 64, kernel_size=3)  # 定义第二个卷积层,输入通道32,输出通道64,卷积核大小3x3self.pool = nn.MaxPool2d(2, 2)  # 定义最大池化层,池化核大小2x2self.dropout1 = nn.Dropout2d(0.25)  # 定义第一个Dropout层,随机丢弃25%的神经元self.fc1 = nn.Linear(64 * 12 * 12, 128)  # 定义第一个全连接层,输入维度64*12*12,输出维度128self.dropout2 = nn.Dropout(0.5)  # 定义第二个Dropout层,随机丢弃50%的神经元self.fc2 = nn.Linear(128, 10)  # 定义输出层,输入维度128,输出维度10(对应10个数字类别)

        定义了一个用于手写数字识别的卷积神经网络(CNN)架构,专为 MNIST 等单通道图像分类任务设计。网络包含两个卷积层(Conv1 和 Conv2)进行特征提取,每个卷积层后接 ReLU 激活函数和最大池化层(MaxPool2d)进行下采样,逐步将 28×28 的输入图像转换为更高层次的抽象特征。为防止过拟合,在卷积层后添加了 Dropout2d (0.25),在全连接层前使用 Dropout (0.5) 增强模型泛化能力。特征提取完成后,通过两次全连接层(FC1 和 FC2)将卷积输出的多维特征映射到 10 个类别,最终输出对应 0-9 数字的分类得分。

1.4 神经网络的前向传播过程

    def forward(self, x):# 第一层卷积+ReLU激活x = torch.relu(self.conv1(x))# 第二层卷积+ReLU激活+池化x = self.pool(torch.relu(self.conv2(x)))# 应用Dropoutx = self.dropout1(x)# 将多维张量展平为一维向量(64*12*12)x = x.view(-1, 64 * 12 * 12)# 第一个全连接层+ReLU激活x = torch.relu(self.fc1(x))# 应用Dropoutx = self.dropout2(x)# 输出层,得到未归一化的预测分数x = self.fc2(x)return x

1.5 数据预处理

transform = transforms.Compose([transforms.ToTensor(),  # 将图像转换为张量transforms.Normalize((0.1307,), (0.3081,))  # 使用MNIST数据集的均值和标准差进行归一化
])

1.6 加载数据

train_dataset = datasets.MNIST('data', train=True, download=True, transform=transform)  # 加载MNIST训练数据集
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)  # 创建数据加载器,批次大小为64,打乱数据

1.7 初始化

model = CNN()  # 创建CNN模型实例
criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # 定义Adam优化器,学习率为0.001

1.8 模型训练过程

def train(epochs):model.train()  # 设置模型为训练模式for epoch in range(epochs):  # 进行指定轮数的训练running_loss = 0.0  # 初始化本轮的损失累加器for batch_idx, (data, target) in enumerate(train_loader):  # 遍历数据加载器中的每个批次optimizer.zero_grad()  # 梯度清零output = model(data)  # 前向传播,计算模型输出loss = criterion(output, target)  # 计算损失loss.backward()  # 反向传播,计算梯度optimizer.step()  # 更新模型参数running_loss += loss.item()  # 累加当前批次的损失if batch_idx % 100 == 0:  # 每100个批次打印一次损失print(f'Epoch {epoch + 1}, Batch {batch_idx}, Loss: {loss.item():.6f}')print(f'Epoch {epoch + 1} completed, Average Loss: {running_loss / len(train_loader):.6f}')  # 打印本轮平均损失

1.9 保存模型

if __name__ == '__main__':train(epochs=5)  # 调用训练函数,训练5轮torch.save(model.state_dict(),'mnist_cnn_model.pth')  # 保存模型的参数print("模型已保存为: mnist_cnn_model.pth")  # 打印保存模型的信息

二,模型测试

2.1 定义与训练时相同的CNN模型架构

class CNN(nn.Module):def __init__(self):# 调用父类初始化方法super(CNN, self).__init__()# 第一个卷积层:输入1通道(灰度图),输出32通道,卷积核3x3self.conv1 = nn.Conv2d(1, 32, kernel_size=3)# 第二个卷积层:输入32通道,输出64通道,卷积核3x3self.conv2 = nn.Conv2d(32, 64, kernel_size=3)# 最大池化层:核大小2x2,步长2self.pool = nn.MaxPool2d(2, 2)# Dropout层:训练时随机丢弃25%的神经元,防止过拟合self.dropout1 = nn.Dropout2d(0.25)# 第一个全连接层:输入维度64*12*12,输出128self.fc1 = nn.Linear(64 * 12 * 12, 128)# Dropout层:训练时随机丢弃50%的神经元self.dropout2 = nn.Dropout(0.5)# 输出层:输入128,输出10个类别(对应0-9数字)self.fc2 = nn.Linear(128, 10)def forward(self, x):# 第一层卷积+ReLU激活x = torch.relu(self.conv1(x))# 第二层卷积+ReLU激活+池化x = self.pool(torch.relu(self.conv2(x)))# 应用Dropoutx = self.dropout1(x)# 将多维张量展平为一维向量(64*12*12)x = x.view(-1, 64 * 12 * 12)# 第一个全连接层+ReLU激活x = torch.relu(self.fc1(x))# 应用Dropoutx = self.dropout2(x)# 输出层,得到未归一化的预测分数x = self.fc2(x)return x

2.2 图像的预处理

def preprocess_image(image_path):"""预处理自定义图像,使其符合模型输入要求"""# 打开图像并转换为灰度图(单通道)image = Image.open(image_path).convert('L')# 调整图像大小为28x28像素(如果不是)if image.size != (28, 28):image = image.resize((28, 28), Image.Resampling.LANCZOS)# 将PIL图像转换为numpy数组以便处理img_array = np.array(image)# 预处理:二值化和颜色反转# MNIST数据集中数字为白色(255),背景为黑色(0)if img_array.mean() > 127:  # 如果平均像素值大于127,说明可能是黑底白字img_array = 255 - img_array  # 颜色反转# 将numpy数组转换为PyTorch张量并添加批次维度img_tensor = transforms.ToTensor()(img_array).unsqueeze(0)# 使用MNIST数据集的均值和标准差进行归一化img_tensor = transforms.Normalize((0.1307,), (0.3081,))(img_tensor)return image, img_tensor  # 返回原始图像和处理后的张量

2.3 预测

def predict_digit(image_path):"""预测自定义图像中的数字"""# 创建模型实例model = CNN()# 加载预训练模型权重model.load_state_dict(torch.load('mnist_cnn_model.pth'))# 设置模型为评估模式(关闭Dropout等训练特有的层)model.eval()# 预处理输入图像original_img, img_tensor = preprocess_image(image_path)# 预测过程,不计算梯度以提高效率with torch.no_grad():# 前向传播,得到模型输出output = model(img_tensor)# 应用softmax将输出转换为概率分布probabilities = torch.softmax(output, dim=1)# 获取最高概率及其对应的数字类别confidence, predicted = torch.max(probabilities, 1)

三,测试

3.1 测试方法

如上文代码所示,我这里用的测试图片是自己定义图片,使用电脑自带的paint绘图软件,设置画布为28*28像素,黑底白字,手动写入一个字进行预测

3.2 测试结果

        预测5的置信度为99.94%

        预测0的置信度为99.99%

        预测7的置信度为92.33%(尽管这个“7”写的很不好但是并不影响预测结果)


四 ,总结

        卷积神经网络(CNN)在图像分类中相比全连接网络(FNN)具有显著优势:通过局部连接权重共享机制,CNN 大幅减少参数量,避免全连接网络因输入维度高导致的参数爆炸问题,计算效率更高且不易过拟合;CNN 通过卷积核逐层提取图像的局部特征(如边缘、纹理),结合池化层的平移不变性,能自动学习从低级到高级的层级化语义特征,而全连接网络将图像展平为向量,完全忽略像素空间关系,需依赖人工特征或大量数据学习;此外,CNN 的卷积结构天然具备正则化效果,对数据量需求更低,训练速度更快,且通过可视化卷积核和特征图可直观解释其对图像模式的捕捉过程,而全连接网络的特征表示缺乏可解释性。


五,完整代码

5.1 模型训练部分代码

import torch  # 导入PyTorch库,用于深度学习
import torch.nn as nn  # 导入PyTorch的神经网络模块
import torch.optim as optim  # 导入PyTorch的优化器模块
from torchvision import datasets, transforms  # 从torchvision导入数据集和数据变换模块
from torch.utils.data import DataLoader  # 导入数据加载器模块# 定义CNN模型
class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()  # 调用父类的初始化方法self.conv1 = nn.Conv2d(1, 32, kernel_size=3)  # 定义第一个卷积层,输入通道1,输出通道32,卷积核大小3x3self.conv2 = nn.Conv2d(32, 64, kernel_size=3)  # 定义第二个卷积层,输入通道32,输出通道64,卷积核大小3x3self.pool = nn.MaxPool2d(2, 2)  # 定义最大池化层,池化核大小2x2self.dropout1 = nn.Dropout2d(0.25)  # 定义第一个Dropout层,随机丢弃25%的神经元self.fc1 = nn.Linear(64 * 12 * 12, 128)  # 定义第一个全连接层,输入维度64*12*12,输出维度128self.dropout2 = nn.Dropout(0.5)  # 定义第二个Dropout层,随机丢弃50%的神经元self.fc2 = nn.Linear(128, 10)  # 定义输出层,输入维度128,输出维度10(对应10个数字类别)def forward(self, x):x = torch.relu(self.conv1(x))  # 对第一个卷积层的输出应用ReLU激活函数x = self.pool(torch.relu(self.conv2(x)))  # 对第二个卷积层的输出应用ReLU激活函数,然后进行最大池化x = self.dropout1(x)  # 应用第一个Dropout层x = x.view(-1, 64 * 12 * 12)  # 将张量展平为一维向量,-1表示自动推断批次维度x = torch.relu(self.fc1(x))  # 对第一个全连接层的输出应用ReLU激活函数x = self.dropout2(x)  # 应用第二个Dropout层x = self.fc2(x)  # 通过输出层return x  # 返回模型的输出# 数据预处理
transform = transforms.Compose([transforms.ToTensor(),  # 将图像转换为张量transforms.Normalize((0.1307,), (0.3081,))  # 使用MNIST数据集的均值和标准差进行归一化
])# 加载数据
train_dataset = datasets.MNIST('data', train=True, download=True, transform=transform)  # 加载MNIST训练数据集
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)  # 创建数据加载器,批次大小为64,打乱数据# 初始化模型、损失函数和优化器
model = CNN()  # 创建CNN模型实例
criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # 定义Adam优化器,学习率为0.001# 训练模型
def train(epochs):model.train()  # 设置模型为训练模式for epoch in range(epochs):  # 进行指定轮数的训练running_loss = 0.0  # 初始化本轮的损失累加器for batch_idx, (data, target) in enumerate(train_loader):  # 遍历数据加载器中的每个批次optimizer.zero_grad()  # 梯度清零output = model(data)  # 前向传播,计算模型输出loss = criterion(output, target)  # 计算损失loss.backward()  # 反向传播,计算梯度optimizer.step()  # 更新模型参数running_loss += loss.item()  # 累加当前批次的损失if batch_idx % 100 == 0:  # 每100个批次打印一次损失print(f'Epoch {epoch + 1}, Batch {batch_idx}, Loss: {loss.item():.6f}')print(f'Epoch {epoch + 1} completed, Average Loss: {running_loss / len(train_loader):.6f}')  # 打印本轮平均损失# 执行训练并保存模型
if __name__ == '__main__':train(epochs=5)  # 调用训练函数,训练5轮torch.save(model.state_dict(),'mnist_cnn_model.pth')  # 保存模型的参数print("模型已保存为: mnist_cnn_model.pth")  # 打印保存模型的信息

5.2 模型测试部分代码

# 导入PyTorch深度学习框架及其神经网络模块
import torch
import torch.nn as nn
# 导入torchvision的图像变换工具
from torchvision import transforms
# 导入PIL库用于图像处理
from PIL import Image
# 导入matplotlib用于可视化
import matplotlib.pyplot as plt
# 导入numpy用于数值计算
import numpy as np
# 导入os模块用于文件和路径操作
import os# 设置matplotlib的字体,确保中文正常显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'WenQuanYi Micro Hei', 'Heiti TC']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题# 定义与训练时相同的CNN模型架构
class CNN(nn.Module):def __init__(self):# 调用父类初始化方法super(CNN, self).__init__()# 第一个卷积层:输入1通道(灰度图),输出32通道,卷积核3x3self.conv1 = nn.Conv2d(1, 32, kernel_size=3)# 第二个卷积层:输入32通道,输出64通道,卷积核3x3self.conv2 = nn.Conv2d(32, 64, kernel_size=3)# 最大池化层:核大小2x2,步长2self.pool = nn.MaxPool2d(2, 2)# Dropout层:训练时随机丢弃25%的神经元,防止过拟合self.dropout1 = nn.Dropout2d(0.25)# 第一个全连接层:输入维度64*12*12,输出128self.fc1 = nn.Linear(64 * 12 * 12, 128)# Dropout层:训练时随机丢弃50%的神经元self.dropout2 = nn.Dropout(0.5)# 输出层:输入128,输出10个类别(对应0-9数字)self.fc2 = nn.Linear(128, 10)def forward(self, x):# 第一层卷积+ReLU激活x = torch.relu(self.conv1(x))# 第二层卷积+ReLU激活+池化x = self.pool(torch.relu(self.conv2(x)))# 应用Dropoutx = self.dropout1(x)# 将多维张量展平为一维向量(64*12*12)x = x.view(-1, 64 * 12 * 12)# 第一个全连接层+ReLU激活x = torch.relu(self.fc1(x))# 应用Dropoutx = self.dropout2(x)# 输出层,得到未归一化的预测分数x = self.fc2(x)return xdef preprocess_image(image_path):"""预处理自定义图像,使其符合模型输入要求"""# 打开图像并转换为灰度图(单通道)image = Image.open(image_path).convert('L')# 调整图像大小为28x28像素(如果不是)if image.size != (28, 28):image = image.resize((28, 28), Image.Resampling.LANCZOS)# 将PIL图像转换为numpy数组以便处理img_array = np.array(image)# 预处理:二值化和颜色反转# MNIST数据集中数字为白色(255),背景为黑色(0)if img_array.mean() > 127:  # 如果平均像素值大于127,说明可能是黑底白字img_array = 255 - img_array  # 颜色反转# 将numpy数组转换为PyTorch张量并添加批次维度img_tensor = transforms.ToTensor()(img_array).unsqueeze(0)# 使用MNIST数据集的均值和标准差进行归一化img_tensor = transforms.Normalize((0.1307,), (0.3081,))(img_tensor)return image, img_tensor  # 返回原始图像和处理后的张量def predict_digit(image_path):"""预测自定义图像中的数字"""# 创建模型实例model = CNN()# 加载预训练模型权重model.load_state_dict(torch.load('mnist_cnn_model.pth'))# 设置模型为评估模式(关闭Dropout等训练特有的层)model.eval()# 预处理输入图像original_img, img_tensor = preprocess_image(image_path)# 预测过程,不计算梯度以提高效率with torch.no_grad():# 前向传播,得到模型输出output = model(img_tensor)# 应用softmax将输出转换为概率分布probabilities = torch.softmax(output, dim=1)# 获取最高概率及其对应的数字类别confidence, predicted = torch.max(probabilities, 1)# 创建可视化窗口plt.figure(figsize=(12, 4))# 子图1:显示原始输入图像plt.subplot(1, 3, 1)plt.imshow(original_img, cmap='gray')plt.title('原始图像')plt.axis('off')  # 关闭坐标轴显示# 子图2:显示模型实际输入(归一化后的图像)plt.subplot(1, 3, 2)plt.imshow(img_tensor[0][0], cmap='gray')plt.title('模型输入')plt.axis('off')# 子图3:显示预测结果和置信度条形图plt.subplot(1, 3, 3)plt.bar(range(10), probabilities[0].numpy())plt.xticks(range(10))  # 设置x轴刻度为0-9plt.title(f'预测结果: {predicted.item()} (置信度: {confidence.item() * 100:.2f}%)')plt.xlabel('数字')plt.ylabel('概率')# 自动调整子图布局plt.tight_layout()# 显示图像plt.show()# 返回预测结果和置信度return predicted.item(), confidence.item() * 100if __name__ == '__main__':# 指定要测试的图像路径,请替换为实际路径image_path = r"C:\Users\10532\Desktop\Study\test\Untitled.png"# 检查文件是否存在if not os.path.exists(image_path):print(f"错误:文件 '{image_path}' 不存在")else:# 执行预测digit, confidence = predict_digit(image_path)print(f"预测结果: {digit},置信度: {confidence:.2f}%")

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

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

相关文章

centos中postfix的作用

/usr/libexec/postfix/master 是 Postfix 邮件服务器的主进程,qmgr 和 pickup 是 Postfix 的子进程。这些进程本身是正常的,但如果你怀疑服务器被用于钓鱼活动,需要进一步检查 Postfix 的配置和日志,确保它没有被滥用。 1. 检查 P…

蓝牙耳机什么牌子好?倍思值得冲不?

最近总被问“蓝牙耳机什么牌子好”,作为踩过无数坑的资深耳机党,必须安利刚入手的倍思M2s Pro主动降噪蓝牙耳机!降噪、音质、颜值全都在线,性价比直接拉满。 -52dB降噪,通勤摸鱼神器 第一次开降噪就被惊到&#xff01…

游戏引擎学习第285天:“Traversables 的事务性占用”

回顾并为当天的工作做准备 我们有一个关于玩家移动的概念,玩家可以在点之间移动,而且当这些点移动时,玩家会随之移动。现在这个部分基本上已经在工作了。我们本来想实现的一个功能是:当玩家移动到某个点时,这个点能“…

java中的包机制

包机制 为了更好地组织类,java提供了包机制,用于区分类名的命名空间 包语句的语法格式为 package pkg1[. pkg2[. pkg3...]]一般利用公司域名倒置作为包名 : 公司域名:www.baidu.com 包名:com.baidu.www 为了能够…

python打卡DAY22

##注入所需库 import pandas as pd import seaborn as sns import matplotlib.pyplot as plt import random import numpy as np import time import shap # from sklearn.svm import SVC #支持向量机分类器 # # from sklearn.neighbors import KNeighborsClassifier …

CodeBuddy 开发 JSON 可视化工具实录:JsonVision 的诞生之旅

我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 🧭 项目起点:一个灵光一现的念头 在日常的前端开发中,我时常需要调试复杂的…

Redis学习专题(一)配置和持久化

目录 一.配置Redis 1.配置application.properties 2. 配置Config 3.测试连接redis 二、Redis持久化 持久化方案 RDB: 1、RDB基础认识 1、具体流程如下: 3、小结: 3、Fork&Copy-On-Write 4、RDB的配置 5、默认快照的配置 6、…

[ctfshow web入门] web77

信息收集 上一题的读取flag方式不能用了,使用后的回显是:could not find driver 解题 同样的查目录方法 cvar_export(scandir("glob:///*"));die();cforeach(new DirectoryIterator("glob:///*") as $a){echo($a->__toString…

每日算法刷题Day8 5.16:leetcode定长滑动窗口4道题,用时1h

5. 2379.得到k个黑块的最少涂色次数(简单) 2379. 得到 K 个黑块的最少涂色次数 - 力扣(LeetCode) 思想 1.返回至少出现 一次 连续 k 个黑色块的 最少 操作次数 2.还是定长k,统计量就是把白色变成黑色的操作次数,无需记录当前有…

很啰嗦,再次总结 DOM

DOM (文档对象模型) 详解 一、DOM 基础概念 1. 定义与作用 DOM(Document Object Model)即文档对象模型,是一种用于 HTML 和 XML 文档的编程接口。它将文档解析为一个由节点和对象组成的树状结构,允许程序和脚本动态访问、修改文…

ES6 (ECMAScript 2015) 详解

文章目录 一、ES6简介1.1 什么是ES6?1.2 为什么要学习ES6?1.3 浏览器支持情况 二、let和const关键字2.1 let关键字2.2 const关键字2.3 var、let和const的选择 三、箭头函数3.1 基本语法3.2 箭头函数的特点3.3 何时使用箭头函数 四、模板字符串4.1 基本语…

LeetCode 746 使用最小花费爬楼梯

当然可以!LeetCode 746 是一道经典的动态规划入门题,我来用 C 为你详细解释。 题目描述 给定一个整数数组 cost,其中每个元素 cost[i] 表示从第 i 个台阶向上爬需要支付的费用。一旦支付费用,你可以选择向上爬 1 步 或 2 步。 你…

6.1.1图的基本概念

基本概念 图: 顶点集边集 顶点集:所有顶点的集合,不能为空(因为图是顶点集和边集组成,其中一个顶点集不能为空,则图肯定不为空) 边集:所有边的集合,边是由顶点集中的2…

WeakAuras Lua Script [TOC BOSS 5 - Anub‘arak ]

WeakAuras Lua Script [TOC BOSS 5 - Anubarak ] 阿努巴拉克 - 小强中虫范围 插件 !WA:2!DE1B0Xrvv8UmuRmIqZwiaXQmgKycwsYUPjPLZPTz3nBYULKnBNDtlYP6o)7T7mMzNz6BMnnBefBqGacIUOsXIkSIki)rCbLkIhLi6h8t3to6h9G2dXt4R9d(rR33mt2MyepQ75KSV3BUZ9FV7VF37g54rDvgU)yX7)GrRgvlQ2Y…

【C/C++】深度探索c++对象模型_笔记

1. 对象内存布局 (1) 普通类(无虚函数) 成员变量排列:按声明顺序存储,但编译器会根据内存对齐规则插入填充字节(padding)。class Simple {char a; // 1字节(偏移0)int b; …

湖北理元理律师事务所:债务优化中的双维支持实践解析

在债务压力与生活质量失衡的社会议题下,法律服务机构的功能边界正在从单一的法律咨询向复合型支持延伸。湖北理元理律师事务所通过“法律心理”双维服务模式,探索债务优化与生活保障的平衡路径,其方法论或为行业提供实践参考。 法律框架&…

Python uv包管理器使用指南:从入门到精通

Python uv包管理器使用指南:从入门到精通 作为一名Python开发者,你是否曾经为虚拟环境管理和依赖包安装而头疼?今天我要向大家介绍一个强大的工具——uv包管理器,它将彻底改变你的Python开发体验。 什么是uv包管理器&#xff1f…

Windows系统安全加固

掌握的加固点: 用户系统检查 口令策略检查 日志审计检查 安全选项检查 信息保护检查 2.2.1 用户系统检查 #检查系统版本内核 判断依据:无 检查方式:命令 msinfo32 dxdiag查看 #检查Administrator账号是否停用 判断依据:禁…

小蜗牛拨号助手用户使用手册

一、软件简介 小蜗牛拨号助手是一款便捷实用的拨号辅助工具,能自动识别剪贴板中的电话号码,支持快速拨号操作。最小化或关闭窗口后,程序将在系统后台运行,还可设置开机自启,方便随时使用,提升拨号效率。 …

c/c++消息队列库RabbitMQ的使用

RabbitMQ C 消息队列组件设计与实现文档 1. 引言 1.1. RabbitMQ 简介 RabbitMQ 是一个开源的消息代理软件(也称为面向消息的中间件),它实现了高级消息队列协议(AMQP)。RabbitMQ 服务器是用 Erlang 语言编写的&#…