三、计算机视觉_03LeNet5及手势识别案例

1 LeNet-5基本介绍

LeNet-5是一种经典的卷积神经网络(CNN)架构,由Yann LeCun在1998年提出,用于手写数字识别,LeNet-5是卷积神经网络的开创性工作之一,它引入了卷积层、池化层和全连接层的组合,为现代深度学习模型奠定了基础

LeNet-5网络结构包括以下几个关键层:

  1. 卷积层(Convolutional Layer):使用多个卷积核提取图像特征
  2. 池化层(Pooling Layer):通常使用最大池化(Max Pooling)或平均池化(Average Pooling)来降低特征图的空间维度
  3. 全连接层(Fully Connected Layer):将特征展平铺并连接到输出层进行分类

LeNet-5的具体网络结构如下:

  • 输入层:32x32像素的灰度图像
  • 卷积层C1:使用6个5x5的卷积核,输出6个28x28的特征图
  • 池化层S2:2x2的最大池化,输出6个14x14的特征图
  • 卷积层C3:使用16个5x5的卷积核,输出16个10x10的特征图
  • 池化层S4:2x2的最大池化,输出16个5x5的特征图
  • 全连接层C5:将16个5x5的特征图展平,连接到120维向量
  • 全连接层F6:120维全连接到84维向量
  • 输出层:84维全连接到10维,代表10个类别

全连接的图像展平处理:

  • 常规图像:[N, C, H, W]
  • 图像展平:[N, C * H * W]
  • 本质是改变shape,不会改变像素值,也不会改变数据本身的信息;只是为了对口型、为了科学计算、为了矩阵相乘

2 LeNet-5网络搭建

(1)复杂一点的写法(易于理解)

# 引入pytorch和nn神经网络
import torch
import torch.nn as nnclass LeNet5(nn.Module):"""自定义一个LeNet5神经网络"""def __init__(self, in_channels=1, ):"""初始化"""# super(LeNet5, self).__init__()这行代码的作用是调用Model类的父类(即nn.Module)的__init__()方法super(LeNet5, self).__init__()# 第一层转换(卷积)self.conv1 = nn.Conv2d(in_channels=in_channels,out_channels=6,kernel_size=5, # stride=1, w-k+1=28, w=32-->k=5stride=1,padding=0)# 第二层转换(亚采样)self.mp1 = nn.MaxPool2d(kernel_size=2, # stride=2, 28/k=w, w=14-->k=2stride=2,padding=0)# 第三层转换(卷积)self.conv2 = nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5, # stride=1, w-k+1=10, w=14-->k=5stride=1,padding=0)# 第四层转换(亚采样)self.mp2 = nn.MaxPool2d(kernel_size=2, # stride=2, 10/k=w, w=5-->k=2stride=2,padding=0)# 第五层转换(全连接)# 1、展平(常规图像:[N, C, H, W],展平:[N, C * H * W])# 数据处理的金标准:按维度理解和处理数据,画图和打印是行不通的,高纬度和大量的数据画图和打印会令人自闭!# 展平只是改变shape,不会改变像素值,不会改变数据本身的信息!# 之所以改变,是为了对口型,为了科学计算,为了矩阵相乘!self.flatten = nn.Flatten(start_dim=1,    # 第一个维度开始end_dim=-1    # 最后一个维度结束)# 2、全连接self.fc1 = nn.Linear(in_features=400,    # 上一个图的c*h*w=16*5*5=400out_features=120    # 输出是图中标注的120)self.fc2 = nn.Linear(in_features=120,out_features=84)self.fc3 = nn.Linear(in_features=84,out_features=n_classes)def forward(self, X):"""前向传播"""print(X.shape)X = self.conv1(X)print(X.shape)X = self.mp1(X)print(X.shape)X = self.conv2(X)print(X.shape)X = self.mp2(X)print(X.shape)X = self.flatten(X)print(X.shape)X = self.fc1(X)print(X.shape)X = self.fc2(X)print(X.shape)X = self.fc3(X)print(X.shape)return X

(2)简化后的写法

# 引入pytorch和nn神经网络
import torch
import torch.nn as nn# 简化的写法
class LeNet5(nn.Module):"""自定义一个神经网络"""def __init__(self, in_channels=1, n_classes=10):"""初始化"""super(LeNet5, self).__init__()# 1. 特征抽取self.feature_extractor = nn.Sequential(nn.Conv2d(in_channels=in_channels, out_channels=6, kernel_size=5,stride=1,padding=0),nn.MaxPool2d(kernel_size=2, stride=2,padding=0),nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5,stride=1,padding=0),nn.MaxPool2d(kernel_size=2, stride=2,padding=0))# 2. 分类输出self.classifier = nn.Sequential(nn.Flatten(start_dim=1, end_dim=-1),nn.Linear(in_features=400, out_features=120),nn.Linear(in_features=120, out_features=84),nn.Linear(in_features=84, out_features=n_classes))def forward(self, x):"""前向传播"""# 1. 先做特征抽取x = self.feature_extractor(x)# 2. 再做分类回归x = self.classifier(x)return x

(3)效果测试

# 实例化对象
m1 = LeNet5(in_channels=1)
# 创建一个随机X对象
X = torch.randn(2, 1, 32, 32)
# 获得预测结果
y_pred = m1(X)
# 查看预测结果的形状
y_pred.shape
# 查看预测结果的内容
y_pred

3 基于LeNet-5的手势识别案例

3.1 原始数据读取

人工智能的数据集很可能很大,要一次性把所有数据都读进内存是不现实的,因此,在本案例中,并不是把所有图像全部读进内存,而是先把所有图像的路径类别归纳和梳理出来,然后分批次读取

  • img_path
  • img_label
"""尝试读取 train 
"""
import os
train_root = os.path.join("gesture", "train")
# 图片路径的列表
train_paths = []
# 图片对应的数字列表
train_labels = []for label in os.listdir(train_root):label_root = os.path.join(train_root, label)for file in os.listdir(label_root):file_path = os.path.join(label_root, file)print(file_path)print(label)train_paths.append(file_path)train_labels.append(label)
"""尝试读取 test (同理)
"""
import os
test_root = os.path.join("gesture", "test")
test_paths = []
test_labels = []for label in os.listdir(test_root):label_root = os.path.join(test_root, label)for file in os.listdir(label_root):file_path = os.path.join(label_root, file)test_paths.append(file_path)test_labels.append(label)

读取数据之后,我们可以构建标签字典

# 构建 标签字典 label dict
labels = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
label2idx = {label: idx for idx, label in enumerate(labels)}
idx2label = {idx: label for label, idx in label2idx.items()}# 打印输出
print(label2idx)
print(idx2label)

3.2 数据的批量化打包

批量化打包数据主要包括以下两个步骤:

  • 继承 Dataset,自定义一个数据集
  • 实例化 DataLoader
# Dataset 类是一个抽象类,它定义了数据集的接口,如果要自定义一个数据集类,则首先需要继承Dataset
from torch.utils.data import Dataset
# DataLoader 类是一个迭代器,它封装了 Dataset 对象,并提供了批量加载数据、打乱数据、多进程加载等功能
from torch.utils.data import DataLoaderfrom PIL import Image# torchvision 是一个与 PyTorch 深度学习框架配套使用的库,专门用于处理图像和视频数据。它提供了许多工具和预构建的模块,旨在简化计算机视觉任务中的数据处理、模型构建和训练流程
# transforms 是 torchvision 中的一个模块,它提供了许多实用的图像转换函数(与深度学习模型架构Transformer不是一个东西,不要弄混了)
from torchvision import transforms
import torch
class GestureDataset(Dataset):"""自定义手势识别数据集"""def __init__(self, X, y):"""初始化"""self.X = Xself.y = ydef __getitem__(self, idx):"""实现:按下标来索引一个样本__getitem__ 是 python 中的一个魔术方法(也称为特殊方法或双下划线方法),它在对象被用于下标操作时自动调用这个方法允许对象模拟序列(如列表、元组)或映射(如字典)的行为,使得对象可以通过索引或键来访问其元素这个方法在 Dataset 类中有一个特定的约定,它只接受一个参数,即索引参数"""# 获取图像路径img_path = self.X[idx]# 读取图像img = Image.open(fp=img_path)# 统一大小img = img.resize((32, 32))# 转张量 [C, H, W]——>归一化# transforms.ToTensor() 是一个预处理操作,它将 PIL 图像或 NumPy 数组转换为 torch.Tensor 对象# 这个转换包括将图像的像素值从范围 [0, 255] 缩放到 [0.0, 1.0]img = transforms.ToTensor()(img)# [0, 1]减去0.5,除以0.5,得到[-1, 1]——>标准化img = transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])(img)# 读取标签img_label = self.y[idx]# 标签转 idimg_idx = label2idx.get(img_label)# 转张量label = torch.tensor(data=img_idx, dtype=torch.long)return img, labeldef __len__(self):"""返回该数据集的样本个数"""return len(self.X)
# 训练集加载器
# 获取训练数据集
train_dataset = GestureDataset(X=train_paths, y=train_labels)
# shuffle=True表示每个epoch开始时都随机打乱样本,batch_size=16表示每个批次将包含16个样本
train_dataloader = DataLoader(dataset=train_dataset, shuffle=True, batch_size=16)
# 测试集加载器(同上)
test_dataset = GestureDataset(X=test_paths, y=test_labels)
test_dataloader = DataLoader(dataset=test_dataset, shuffle=False, batch_size=32)
# 打印看看测试数据集的效果
for X, y in test_dataloader:print(X.shape)print(y.shape)break

3.3 模型搭建

# 引入pytorch和nn神经网络
import torch
import torch.nn as nn# 简化的写法
class LeNet5(nn.Module):"""自定义一个神经网络"""def __init__(self, in_channels=1, n_classes=10):"""初始化"""super(LeNet5, self).__init__()# 1. 特征抽取self.feature_extractor = nn.Sequential(nn.Conv2d(in_channels=in_channels, out_channels=6, kernel_size=5,stride=1,padding=0),nn.MaxPool2d(kernel_size=2, stride=2,padding=0),nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5,stride=1,padding=0),nn.MaxPool2d(kernel_size=2, stride=2,padding=0))# 2. 分类输出self.classifier = nn.Sequential(nn.Flatten(start_dim=1, end_dim=-1),nn.Linear(in_features=400, out_features=120),nn.Linear(in_features=120, out_features=84),nn.Linear(in_features=84, out_features=n_classes))def forward(self, x):"""前向传播"""# 1. 先做特征抽取x = self.feature_extractor(x)# 2. 再做分类回归x = self.classifier(x)return x

3.4 模型训练

# 设置训练轮次
epochs = 50
# 设备
device = "cuda" if torch.cuda.is_available() else "cpu"
# 实例化模型
model = Model()
model.to(device=device)
# 优化器
# 创建一个 Adam 优化器实例,Adam 是一种自适应学习率优化算法,它结合了 RMSprop 和 Momentum 两种优化算法的优点。它通常表现良好,并且对许多不同的问题都适用
# params=model.parameters() 用于指定了优化器将要优化的参数,参数值model.parameters() 是一个生成器,它返回模型中所有需要梯度更新的参数(即权重和偏置)
# lr=1e-3:设置优化器的学习率,即每次参数更新的步长为 0.001
optimizer = torch.optim.Adam(params=model.parameters(), lr=1e-3)
# 损失函数
# 创建一个交叉熵损失函数的实例,交叉熵损失函数是分类问题中最常用的损失函数之一,特别是当输出是互斥类别的概率分布时
# 实例化nn中交叉熵损失的计算类,创建损失函数对象
loss_fn = nn.CrossEntropyLoss()
# 准确率计算
def get_acc(data_loader):accs = []# model.eval() 用于将模型设置为评估(evaluation)模式。这个方法在模型的训练和评估过程中扮演着重要的角色,尤其是在使用某些特定层(如 Dropout 和 Batch Normalization)时model.eval()with torch.no_grad():for X, y in data_loader:X = X.to(device=device)y = y.to(device=device)y_pred = model(X)# 获取预测结果中概率最高的类别,argmax(dim=-1) 返回最后一个维度(即概率分布)中最大值的索引y_pred = y_pred.argmax(dim=-1)acc = (y_pred == y).to(torch.float32).mean().item()accs.append(acc)# ndigits=5表示保留5位小数,round表示四舍五入final_acc = round(number=sum(accs) / len(accs), ndigits=5)return final_acc
import time# 训练过程
def train():# 训练集准确率列表train_accs = []# 测试集准确率列表test_accs = []# 当前准确率cur_test_acc = 0# 初始准确率train_acc = get_acc(data_loader=train_dataloader)test_acc = get_acc(data_loader=test_dataloader)train_accs.append(train_acc)test_accs.append(test_acc)print(f"训练之前的准确率:train_acc: {train_acc},test_acc: {test_acc}")# 每一轮次for epoch in range(epochs):# 模型设置为 train 模式model.train()# 每一轮的开始时间start_train = time.time()for X, y in train_dataloader:# 0, 数据搬家X = X.to(device=device)y = y.to(device=device)# 1,正向传播y_pred = model(X)# 2,计算损失loss = loss_fn(y_pred, y)# 3,反向传播loss.backward()# 4,优化一步optimizer.step()# 5,清空梯度optimizer.zero_grad() # 每一轮的结束时间stop_train = time.time()# 本轮处理之后的准确率train_acc = get_acc(data_loader=train_dataloader)test_acc = get_acc(data_loader=test_dataloader)train_accs.append(train_acc)test_accs.append(test_acc)# 保存模型if cur_test_acc < test_acc:cur_test_acc = test_acc# 保存更好的模型torch.save(obj=model.state_dict(), f="lenet_best.pt")# 保存最好的模型torch.save(obj=model.state_dict(), f="lenet_last.pt") # 日志监控print(f"""当前为第 {epoch + 1} 轮:""")print(f"""训练准确率 (train_acc)\t测试准确率 (test_acc)\t运行时间 (elapsed_time)""")# 在python的字符串格式化中,:<width用于指定字符串的最小宽度print(f"""{train_acc:<18}\t{test_acc:<17}\t{round(number=stop_train - start_train, ndigits=3)}秒""")return train_accs, test_accs

调用训练方法,获得优化后的最佳模型(lenet_last.pt)

train_accs, test_accs = train()

3.5 Streamlit页面实现

Step1: 将【3.3 模型搭建】中的代码,复制粘贴到一个名为models.py的python文件中进行保存

Step2: 将【3.4 模型训练中获得的】lenet_last.pt文件,与models.py放到同级目录中

Step3: 新建一个modelApp.py的文件,与上面两个文件处于同级目录,复制粘贴以下内容

import streamlit as st
import torch
import os
from PIL import Image
from models import LeNet5
from torchvision import transformsdef predict(img_path, model, device):# 判断图像是否存在if not os.path.exists(img_path):raise FileNotFoundError("文件已丢失")else:# 读取图像img = Image.open(fp=img_path)# 预处理及转张量img = img.resize((32, 32))img = transforms.ToTensor()(img)img = transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])(img)# 添加维度# 上面的维度是[height, width, channels],需要增加一个维度,使其变成[batch_size, channels, height, width]# 填0,即代表从第0个位置,给这个张量添加一个维度(1),也就是batch_size=1img = img.unsqueeze(0)# 数据搬家img = img.to(device=device)# 将模型设为评估模式model.eval()# 无梯度环境with torch.no_grad():# 正向传播y_pred = model(img)# 解析结果y_pred = y_pred.argmax(dim=-1).item()# 返回结果return y_predif __name__ == "__main__":# 显示当前设备是GPU设备还是CPUdevice = "cuda" if torch.cuda.is_available() else "cpu"st.write(f"当前设备是:{device}")# 加载模型m1 = LeNet5()m1.to(device=device)# 加载之前训练好的权重m1.load_state_dict(state_dict=torch.load(f="lenet_best.pt", map_location=device), strict=False)# 上传一张图片uploaded_img = st.file_uploader("请上传一张图片", type=["png", "jpg", "jpeg"])# 将上传的图像文件保存到临时文件if uploaded_img is not None:with open(file="temp_img.jpg", mode="wb") as f:f.write(uploaded_img.getvalue())img_path = "temp_img.jpg"if img_path:# 加载训练好的lenet_best.pt模型pred = predict(img_path=img_path, model=m1, device=device)st.write(f"经预测,此图片中对应的手势是:{pred}")# 显示上传好的图片img = Image.open(fp=img_path)st.image(image=img, caption="上传的图片", use_column_width=True)

Step4: 使用以下命令运行modelApp.py,通过streamlit页面进行使用

streamlit run modelApp.py

效果示例:

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

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

相关文章

【论文模型复现】深度学习、地质流体识别、交叉学科融合?什么情况,让我们来看看

文献&#xff1a;蓝茜茜,张逸伦,康志宏.基于深度学习的复杂储层流体性质测井识别——以车排子油田某井区为例[J].科学技术与工程,2020,20(29):11923-11930. 本文目录 一、前言二、文献阅读-基于深度学习的复杂储层流体性质测井识别2.1 摘要2.2 当前研究不足2.3 本文创新2.4 论文…

Uni-APP+Vue3+鸿蒙 开发菜鸟流程

参考文档 文档中心 运行和发行 | uni-app官网 AppGallery Connect DCloud开发者中心 环境要求 Vue3jdk 17 Java Downloads | Oracle 中国 【鸿蒙开发工具内置jdk17&#xff0c;本地不使用17会报jdk版本不一致问题】 开发工具 HBuilderDevEco Studio【目前只下载这一个就…

Unity-Editor扩展Odin + 自定义EditorWindow记录

没有上下文&#xff0c;可能你不知道这是什么&#xff08;关于Odin Inspector) 在写一个 Odin 插件的完整文章&#xff0c;卡了三天&#xff0c;之后会放出 使用Unity的人之中 1/10 可能会使用Editor扩展&#xff0c;而这之中的又1/10的 人可能会用Odin这个Editor的附加扩展 -…

FIFO系列 - FIFO使用中需要注意的若干问题

FIFO使用中需要注意的若干问题 文章目录 FIFO使用中需要注意的若干问题前言场景1:包数据FIFO设计之冗余法场景2、FIFO数据传输之流控总结前言 场景1:包数据FIFO设计之冗余法 场景:类似图像、文字等码流数据是不需要重复被访问的,因此使用FIFO进行缓存(如果需要被存储,一…

计算机毕业设计 | springboot+vue大学城水电管理系统 校园学校物业水电管理(附源码+文档)

1&#xff0c;绪论 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理大学城水电管理系统的相关信息成…

5-对象的访问权限

对象的访问权限知识点 对象的分类 在数据库中&#xff0c;数据库的表、索引、视图、缺省值、规则、触发器等等、都可以被称为数据库对象&#xff0c;其中对象主要分为两类 1、模式(schema)对象&#xff1a;模式对象可以理解为一个存储目录、包含视图、索引、数据类型、函数和…

药方新解:Spring Boot中药实验管理系统设计

3系统分析 3.1可行性分析 通过对本中药实验管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本中药实验管理系统采用SSM框架&#xff0c;JAVA作为开发语…

动态规划-完全背包问题——279.完全平方数

1.题目解析 题目来源 279.完全平方数——力扣 测试用例 2.算法原理 1.状态表示 完全背包问题通常都是使用一个二维数组来表示其状态&#xff0c;这里是 dp[i][j]&#xff1a;在[1,i]区间选择平方数&#xff0c;当此时已选择平方数的总和完全等于j时所选择的最小平方数个数 …

二叉树的层序遍历

一、题目 给定一个二叉树&#xff0c;返回该二叉树层序遍历的结果&#xff0c;&#xff08;从左到右&#xff0c;一层一层地遍历&#xff09; 例如&#xff1a; 给定的二叉树是{3,9,20,null,null,15,7}, 该二叉树层序遍历的结果是 [[3],[9,20],[15,7]] 二、解决方案 2.0 树…

模型训练过程的显存占用实测

依赖项说明 pip install nvitop pip install timm pip install peft后续的显存占用数据截图&#xff0c;均基于nvitop命令实现 1、模型显存占用说明 1.1 理论占用值 在 一文讲明白大模型显存占用&#xff08;只考虑单卡&#xff09;与大模型显存占用分析都对模型训练过程中…

后端分层解耦

引入 在上篇所举的例子中&#xff0c;我们将所有的代码均放在HelloControl方法之中&#xff0c;这样会导致代码的复用性、可读性较差&#xff0c;难以维护。因此我们需 三层架构 在之前的代码中&#xff0c;代码大体可以分为三部分&#xff1a;数据访问、数据逻辑处理、响应数…

AIGC 入门全攻略:开启智能创作新时代

一、AIGC 初印象 AIGC&#xff0c;即人工智能生成内容&#xff0c;是继专业生产内容&#xff08;PGC&#xff09;、用户生产内容&#xff08;UGC&#xff09;之后的新型内容创作方式。它涵盖了文本生成、图像与视频创作、音频生成等多个领域&#xff0c;正在以惊人的速度改变着…

约克VRF地暖中央空调,让你舒适过冬

想要冬季过得舒服&#xff0c;采暖必须要到位&#xff01;对于没有集中供暖的南方地区来说&#xff0c;冬季室内阴冷刺骨。 选购地暖中央空调时&#xff0c;强效制热的能力必不可少&#xff0c;让我们可以享受温暖的室内温度&#xff0c;有效减少室内忽冷忽热的温度变化。 约克…

基于Java Springboot宠物领养救助平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

使用原生 OpenTelemetry 解锁各种可能性:优先考虑可靠性,而不是专有限制

作者&#xff1a;来自 Elastic Bahubali Shetti•Miguel Luna Elastic 现在支持使用 OTel Operator 在 Kubernetes 上部署和管理 Elastic Distributions of OpenTelemetry (EDOT)。SRE 现在可以访问开箱即用的配置和仪表板&#xff0c;这些配置和仪表板旨在通过 Elastic Observ…

基于python Django的boss直聘数据采集与分析预测系统,爬虫可以在线采集,实时动态显示爬取数据,预测基于技能匹配的预测模型

本系统是基于Python Django框架构建的“Boss直聘”数据采集与分析预测系统&#xff0c;旨在通过技能匹配的方式对招聘信息进行分析与预测&#xff0c;帮助求职者根据自身技能找到最合适的职位&#xff0c;同时为招聘方提供更精准的候选人推荐。系统的核心预测模型基于职位需求技…

安装 python-pcl 遇到的问题

安装python-pcl 成功安装错误尝试尝试一尝试二尝试三 本人环境 Ubuntu 22.04.4LTS ros2-humble cpython 3.0.11 python 3.10.12 libpcl-dev 1.12.1dfsg-3build1 pcl-tools 1.12.1dfsg-3build1 代码摘抄来源&#xff1a;Breadcrumbsouster-ros-extras/scripts/ros2_pcl_filters.…

【C++进阶篇】——string类的使用

文章目录 前言&#xff1a;1. string的介绍2. string类对象的常见构造3. string类对象的容量操作4. string类对象的访问5. 迭代器6. string类对象的修改操作7. string类对象的字符串运算8.string类成员函数9.string类非成员函数10.string类常量成员 前言&#xff1a; std::str…

vmware虚拟机给创建的centos扩展磁盘步骤

1.先看看原来的磁盘信息&#xff0c;目前磁盘是20g的&#xff0c;重点关注红色箭头指向的地方&#xff0c;一个17g 可用11g&#xff0c;接下来要对其进行扩展 df -h2.关闭当前虚拟机&#xff0c;先进行磁盘扩展&#xff0c;目前我扩展到了50g。 3.重新开启虚拟机&#xff0c;…

开源物业管理系统助力智能社区提升服务效率与用户体验

内容概要 开源物业管理系统是一种灵活、智能的解决方案&#xff0c;专为社区物业管理而生。随着智能社区的发展&#xff0c;这种系统变得越来越重要。它不仅帮助物业管理者高效地处理日常事务&#xff0c;还提升了居民的生活体验。在这个日新月异的时代&#xff0c;开源物业管…