python基于轻量级卷积神经网络模型ShuffleNetv2开发构建辣椒病虫害图像识别系统

news2024/11/24 17:30:21

轻量级识别模型在我们前面的博文中已经有过很多实践了,感兴趣的话可以自行移步阅读:

《移动端轻量级模型开发谁更胜一筹,efficientnet、mobilenetv2、mobilenetv3、ghostnet、mnasnet、shufflenetv2驾驶危险行为识别模型对比开发测试》

《基于Pytorch框架的轻量级卷积神经网络垃圾分类识别系统》

《基于轻量级卷积神经网络模型实践Fruits360果蔬识别——自主构建CNN模型、轻量化改造设计lenet、alexnet、vgg16、vgg19和mobilenet共六种CNN模型实验对比分析》

《探索轻量级模型性能上限,基于GhostNet模型开发构建多商品细粒度图像识别系统》

《基于轻量级神经网络GhostNet开发构建的200种鸟类细粒度识别分析系统》

《基于MobileNet的轻量级卷积神经网络实现玉米螟虫不同阶段识别分析》

《python开发构建轻量级卷积神经网络模型实现手写甲骨文识别系统》

《基于轻量级模型GHoshNet开发构建眼球眼疾识别分析系统,构建全方位多层次参数对比分析实验》

本文的核心思想是像基于ShuffleNetv2来开发构建辣椒病虫害识别系统,首先看下实例效果:

本文选择的是轻量级网络中非常强大的ShuffleNetv2,ShuffleNetv2网络是一种用于图像识别和分类任务的卷积神经网络结构,它旨在在保持准确性的同时尽可能地减少模型的计算和参数量。下面是对ShuffleNetv2网络构建原理的详细介绍,并针对性地分析其优点和缺点:

构建原理:

  1. 基本单元:ShuffleNetv2网络的基本单元是由深度可分离卷积和通道重排组成的特殊模块。其中深度可分离卷积用于减少计算量和参数数量,而通道重排则用于交换和重组特征通道。

  2. 分组卷积:ShuffleNetv2将卷积操作分组,使得每个分组内部进行特征提取,然后再将特征通道进行重排和整合,从而实现了计算效率的提升。

  3. 网络结构:ShuffleNetv2网络结构由多个堆叠的ShuffleNetv2块组成,每个块内部包含了多个基本单元,通过合理的设计实现了高效的信息流动和特征提取。

优点:

  1. 轻量级高效:ShuffleNetv2相比传统的卷积神经网络具有更少的参数量和计算量,适合于移动设备上的部署和实时推理。

  2. 准确性:尽管模型规模小,但ShuffleNetv2在保持较高的准确性的同时,实现了高效的模型推理。

  3. 结构灵活:ShuffleNetv2网络结构可以通过调整基本单元的个数和网络深度来适应不同的计算资源和任务需求。

缺点:

  1. 学习能力有限:由于参数数量较少,ShuffleNetv2网络可能在复杂场景和大规模数据上的表示学习能力相对较弱,可能无法取得更高的准确性。

  2. 训练复杂度:尽管ShuffleNetv2网络在推理时很高效,但在训练过程中相比一些大型网络结构可能需要更多的迭代才能取得好的效果,训练复杂度较大。

总的来说,ShuffleNetv2网络在轻量级和高效方面具有明显的优势,适用于移动设备和资源受限的场景,但在一些大规模和复杂任务上可能会存在一定的限制。

这里给出基于Keras和pytorch对应的代码实现,首先是keras实现,如下所示:

#!usr/bin/env python
# encoding:utf-8
from __future__ import division



import os
import numpy as np
from keras.utils import plot_model
from keras.applications.imagenet_utils import _obtain_input_shape
from keras.engine.topology import get_source_inputs
from keras.layers import *
from keras.models import Model
import keras.backend as K
from utils import block


def ShuffleNetV2(
    include_top=True,
    input_tensor=None,
    scale_factor=1.0,
    pooling="max",
    input_shape=(224, 224, 3),
    load_model=None,
    num_shuffle_units=[3, 7, 3],
    bottleneck_ratio=1,
    classes=1000,
):
    if K.backend() != "tensorflow":
        raise RuntimeError("Only tensorflow supported for now")
    name = "ShuffleNetV2_{}_{}_{}".format(
        scale_factor, bottleneck_ratio, "".join([str(x) for x in num_shuffle_units])
    )
    input_shape = _obtain_input_shape(
        input_shape,
        default_size=224,
        min_size=28,
        require_flatten=include_top,
        data_format=K.image_data_format(),
    )
    out_dim_stage_two = {0.5: 48, 1: 116, 1.5: 176, 2: 244}

    if pooling not in ["max", "avg"]:
        raise ValueError("Invalid value for pooling")
    if not (float(scale_factor) * 4).is_integer():
        raise ValueError("Invalid value for scale_factor, should be x over 4")
    exp = np.insert(
        np.arange(len(num_shuffle_units), dtype=np.float32), 0, 0
    )  
    # [0., 0., 1., 2.]
    out_channels_in_stage = 2**exp
    out_channels_in_stage *= out_dim_stage_two[
        bottleneck_ratio
    ]  
    #  calculate output channels for each stage
    out_channels_in_stage[0] = 24  
    # first stage has always 24 output channels
    out_channels_in_stage *= scale_factor
    out_channels_in_stage = out_channels_in_stage.astype(int)
    if input_tensor is None:
        img_input = Input(shape=input_shape)
    else:
        if not K.is_keras_tensor(input_tensor):
            img_input = Input(tensor=input_tensor, shape=input_shape)
        else:
            img_input = input_tensor
    # create shufflenet architecture
    x = Conv2D(
        filters=out_channels_in_stage[0],
        kernel_size=(3, 3),
        padding="same",
        use_bias=False,
        strides=(2, 2),
        activation="relu",
        name="conv1",
    )(img_input)
    x = MaxPool2D(pool_size=(3, 3), strides=(2, 2), padding="same", name="maxpool1")(x)
    # create stages containing shufflenet units beginning at stage 2
    for stage in range(len(num_shuffle_units)):
        repeat = num_shuffle_units[stage]
        x = block(
            x,
            out_channels_in_stage,
            repeat=repeat,
            bottleneck_ratio=bottleneck_ratio,
            stage=stage + 2,
        )
    if bottleneck_ratio < 2:
        k = 1024
    else:
        k = 2048
    x = Conv2D(
        k,
        kernel_size=1,
        padding="same",
        strides=1,
        name="1x1conv5_out",
        activation="relu",
    )(x)
    if pooling == "avg":
        x = GlobalAveragePooling2D(name="global_avg_pool")(x)
    elif pooling == "max":
        x = GlobalMaxPooling2D(name="global_max_pool")(x)
    if include_top:
        x = Dense(classes, name="fc")(x)
        x = Activation("softmax", name="softmax")(x)
    if input_tensor:
        inputs = get_source_inputs(input_tensor)
    else:
        inputs = img_input
    model = Model(inputs, x, name=name)
    if load_model:
        model.load_weights("", by_name=True)
    return model

接下来是PyTorch的实现:

#!usr/bin/env python
# encoding:utf-8
from __future__ import division



import os
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import init
from collections import OrderedDict



def _make_divisible(v, divisor, min_value=None):
    """
    This function is taken from the original tf repo.
    It ensures that all layers have a channel number that is divisible by 8
    It can be seen here:
    https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
    :param v:
    :param divisor:
    :param min_value:
    :return:
    """
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    # Make sure that round down does not go down by more than 10%.
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v


def channel_shuffle(x, groups):
    batchsize, num_channels, height, width = x.data.size()
    assert num_channels % groups == 0
    channels_per_group = num_channels // groups
    # reshape
    x = x.view(batchsize, groups, channels_per_group, height, width)
    # transpose
    # - contiguous() required if transpose() is used before view().
    #   See https://github.com/pytorch/pytorch/issues/764
    x = torch.transpose(x, 1, 2).contiguous()
    # flatten
    x = x.view(batchsize, -1, height, width)
    return x


class SELayer(nn.Module):
    def __init__(self, channel, reduction=16):
        super(SELayer, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(channel, channel // reduction),
            nn.ReLU(inplace=True),
            nn.Linear(channel // reduction, channel),
            nn.Sigmoid(),
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y


class BasicUnit(nn.Module):
    def __init__(
        self,
        inplanes,
        outplanes,
        c_tag=0.5,
        activation=nn.ReLU,
        SE=False,
        residual=False,
        groups=2,
    ):
        super(BasicUnit, self).__init__()
        self.left_part = round(c_tag * inplanes)
        self.right_part_in = inplanes - self.left_part
        self.right_part_out = outplanes - self.left_part
        self.conv1 = nn.Conv2d(
            self.right_part_in, self.right_part_out, kernel_size=1, bias=False
        )
        self.bn1 = nn.BatchNorm2d(self.right_part_out)
        self.conv2 = nn.Conv2d(
            self.right_part_out,
            self.right_part_out,
            kernel_size=3,
            padding=1,
            bias=False,
            groups=self.right_part_out,
        )
        self.bn2 = nn.BatchNorm2d(self.right_part_out)
        self.conv3 = nn.Conv2d(
            self.right_part_out, self.right_part_out, kernel_size=1, bias=False
        )
        self.bn3 = nn.BatchNorm2d(self.right_part_out)
        self.activation = activation(inplace=True)
        self.inplanes = inplanes
        self.outplanes = outplanes
        self.residual = residual
        self.groups = groups
        self.SE = SE
        if self.SE:
            self.SELayer = SELayer(self.right_part_out, 2)  # TODO

    def forward(self, x):
        left = x[:, : self.left_part, :, :]
        right = x[:, self.left_part :, :, :]
        out = self.conv1(right)
        out = self.bn1(out)
        out = self.activation(out)
        out = self.conv2(out)
        out = self.bn2(out)
        out = self.conv3(out)
        out = self.bn3(out)
        out = self.activation(out)
        if self.SE:
            out = self.SELayer(out)
        if self.residual and self.inplanes == self.outplanes:
            out += right
        return channel_shuffle(torch.cat((left, out), 1), self.groups)


class DownsampleUnit(nn.Module):
    def __init__(self, inplanes, c_tag=0.5, activation=nn.ReLU, groups=2):
        super(DownsampleUnit, self).__init__()
        self.conv1r = nn.Conv2d(inplanes, inplanes, kernel_size=1, bias=False)
        self.bn1r = nn.BatchNorm2d(inplanes)
        self.conv2r = nn.Conv2d(
            inplanes,
            inplanes,
            kernel_size=3,
            stride=2,
            padding=1,
            bias=False,
            groups=inplanes,
        )
        self.bn2r = nn.BatchNorm2d(inplanes)
        self.conv3r = nn.Conv2d(inplanes, inplanes, kernel_size=1, bias=False)
        self.bn3r = nn.BatchNorm2d(inplanes)
        self.conv1l = nn.Conv2d(
            inplanes,
            inplanes,
            kernel_size=3,
            stride=2,
            padding=1,
            bias=False,
            groups=inplanes,
        )
        self.bn1l = nn.BatchNorm2d(inplanes)
        self.conv2l = nn.Conv2d(inplanes, inplanes, kernel_size=1, bias=False)
        self.bn2l = nn.BatchNorm2d(inplanes)
        self.activation = activation(inplace=True)
        self.groups = groups
        self.inplanes = inplanes

    def forward(self, x):
        out_r = self.conv1r(x)
        out_r = self.bn1r(out_r)
        out_r = self.activation(out_r)
        out_r = self.conv2r(out_r)
        out_r = self.bn2r(out_r)
        out_r = self.conv3r(out_r)
        out_r = self.bn3r(out_r)
        out_r = self.activation(out_r)
        out_l = self.conv1l(x)
        out_l = self.bn1l(out_l)
        out_l = self.conv2l(out_l)
        out_l = self.bn2l(out_l)
        out_l = self.activation(out_l)
        return channel_shuffle(torch.cat((out_r, out_l), 1), self.groups)


class ShuffleNetV2(nn.Module):
    """
    ShuffleNetV2 implementation
    """

    def __init__(
        self,
        scale=1.0,
        in_channels=3,
        c_tag=0.5,
        num_classes=1000,
        activation=nn.ReLU,
        SE=False,
        residual=False,
        groups=2,
    ):
        """
        ShuffleNetV2 constructor
        :param scale:
        :param in_channels:
        :param c_tag:
        :param num_classes:
        :param activation:
        :param SE:
        :param residual:
        :param groups:
        """

        super(ShuffleNetV2, self).__init__()

        self.scale = scale
        self.c_tag = c_tag
        self.residual = residual
        self.SE = SE
        self.groups = groups
        self.activation_type = activation
        self.activation = activation(inplace=True)
        self.num_classes = num_classes
        self.num_of_channels = {
            0.5: [24, 48, 96, 192, 1024],
            1: [24, 116, 232, 464, 1024],
            1.5: [24, 176, 352, 704, 1024],
            2: [24, 244, 488, 976, 2048],
        }
        self.c = [_make_divisible(chan, groups) for chan in self.num_of_channels[scale]]
        self.n = [3, 8, 3]  # TODO: should be [3,7,3]
        self.conv1 = nn.Conv2d(
            in_channels, self.c[0], kernel_size=3, bias=False, stride=2, padding=1
        )
        self.bn1 = nn.BatchNorm2d(self.c[0])
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2)
        self.shuffles = self._make_shuffles()
        self.conv_last = nn.Conv2d(self.c[-2], self.c[-1], kernel_size=1, bias=False)
        self.bn_last = nn.BatchNorm2d(self.c[-1])
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Linear(self.c[-1], self.num_classes)
        self.init_params()

    def init_params(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, mode="fan_out")
                if m.bias is not None:
                    init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                init.normal_(m.weight, std=0.001)
                if m.bias is not None:
                    init.constant_(m.bias, 0)

    def _make_stage(self, inplanes, outplanes, n, stage):
        modules = OrderedDict()
        stage_name = "ShuffleUnit{}".format(stage)
        # First module is the only one utilizing stride
        first_module = DownsampleUnit(
            inplanes=inplanes,
            activation=self.activation_type,
            c_tag=self.c_tag,
            groups=self.groups,
        )
        modules["DownsampleUnit"] = first_module
        second_module = BasicUnit(
            inplanes=inplanes * 2,
            outplanes=outplanes,
            activation=self.activation_type,
            c_tag=self.c_tag,
            SE=self.SE,
            residual=self.residual,
            groups=self.groups,
        )
        modules[stage_name + "_{}".format(0)] = second_module
        # add more LinearBottleneck depending on number of repeats
        for i in range(n - 1):
            name = stage_name + "_{}".format(i + 1)
            module = BasicUnit(
                inplanes=outplanes,
                outplanes=outplanes,
                activation=self.activation_type,
                c_tag=self.c_tag,
                SE=self.SE,
                residual=self.residual,
                groups=self.groups,
            )
            modules[name] = module
        return nn.Sequential(modules)

    def _make_shuffles(self):
        modules = OrderedDict()
        stage_name = "ShuffleConvs"

        for i in range(len(self.c) - 2):
            name = stage_name + "_{}".format(i)
            module = self._make_stage(
                inplanes=self.c[i], outplanes=self.c[i + 1], n=self.n[i], stage=i
            )
            modules[name] = module
        return nn.Sequential(modules)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.activation(x)
        x = self.maxpool(x)
        x = self.shuffles(x)
        x = self.conv_last(x)
        x = self.bn_last(x)
        x = self.activation(x)
        # average pooling layer
        x = self.avgpool(x)
        # flatten for input to fully-connected layer
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return F.log_softmax(x, dim=1)


可以拿到自己的项目中直接集成使用。

接下来看下数据集:

一共包含11种对应的疾病,如下:

炭疽病
疫病
用药不当
病毒病
温度不适
螨虫
细菌性病害
根腐病
缺素
脐腐病
蓟马

数据分布可视化如下所示:

默认100次的迭代训练,执行结束后来看下结果详情:
【loss曲线】

【acc曲线】

【混淆矩阵】

开发专用的系统界面实现可视化推理实例如下所示:

感兴趣的话都可以自行动手实践一下。

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

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

相关文章

DHCP Host Name

文章目录 前言DHCP OptionOption (12) Host Namednsmasq 前言 打开路由器页面&#xff0c;看到下面连接的设备&#xff0c;有的显示设备名称 Tmall-Genie、ESP-C37CE8&#xff0c;而有的直接显示 MAC 地址 D2:B0:XX:XX:XX:XX。 这个名称是哪里来的呢&#xff1f; 这就是我们今…

Excel 动态拼接表头实现导出

public class Column {//单元格内容private String content;//字段名称&#xff0c;用户导出表格时反射调用private String fieldName;//这个单元格的集合private List<Column> listTpamscolumn new ArrayList<Column>();int totalRow;int totalCol;int row;//exc…

用AI在抖音直播做姓氏头像的全新玩法,详细分析制作教程

前段时间在圈子里给大家分享了用AI写艺术字做小红书账号案例玩法&#xff0c;同学们都比较热衷学习。纷纷动手实践。 事实上用AI艺术字变现玩法还有许多。 例如上周末在星球给圈友们分享的一个AI艺术字直播的抖音账号&#xff0c;直播内容形式很简单&#xff0c;就是展现用AI…

机器学习实验六:聚类

系列文章目录 机器学习实验一&#xff1a;线性回归机器学习实验二&#xff1a;决策树模型机器学习实验三&#xff1a;支持向量机模型机器学习实验四&#xff1a;贝叶斯分类器机器学习实验五&#xff1a;集成学习机器学习实验六&#xff1a;聚类 文章目录 系列文章目录一、实验…

SpectralGPT: Spectral Foundation Model 论文翻译3

遥感领域的通用大模型 2023.11.13在CVPR发表 原文地址&#xff1a;[2311.07113] SpectralGPT: Spectral Foundation Model (arxiv.org) E.消融研究 在预训练阶段&#xff0c;我们对可能影响下游任务表现的各种因素进行了全面研究。这些因素包括掩蔽比、ViT patch大小、数据规…

代码随想录第二十五天(一刷C语言)|递增子序列全排列全排列II

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 组合和排列问题是在树形结构的叶子节点上收集结果&#xff0c;而子集问题就是取树上所有节点的结果。 一、递增子序列 思路&#xff1a;参考carl文档 已经是递增序列故而不用排序&#xff…

STL源码分析之allocate

空间配置函数allocate //空间配置函数的内部实现原理 //allocate()函数&#xff0c;首先判断区块大小&#xff0c;大于128bytes就调用第一级配置器&#xff0c;小于128bytes就检查对应的free list. //如果free list之内有可用的区块&#xff0c;就直接拿来用&#xff0c;如果没…

Axure RP免费版:详细信息抢先知道

Axure RP收费吗&#xff1f; 是的&#xff0c;AxureRP是一种收费的原型设计工具。它提供了两种选择&#xff1a;免费试用版和付费版。免费试用版可免费使用30天&#xff0c;功能与付费版相同&#xff0c;但导出时会有Axure水印&#xff0c;文件无法保存。付费版分为Pro版和Tea…

实现了一个简单的卡通渲染效果

介绍 简单参考下实现了基本卡通着色渲染效果&#xff1a; 主要包含了描边和内部色块 开始构建了一个场景用于展示光线的变化&#xff0c;并放置了一个角色。 npr_1 接下去加入描边的效果&#xff0c;可以感觉到人物轮廓变明显了。 npr_2 然后再加入了内部的色块变化并调小了点…

Android集成科大讯飞语音识别与语音唤醒简易封装

一、语音唤醒部分 1、首先在科大讯飞官网注册开发者账号 控制台-讯飞开放平台 2、配置唤醒词然后下载sdk 3、选择对应功能下载 4、语音唤醒lib包全部复制到工程目录下 5、把语音唤醒词文件复制到工程的assets目录 6、复制对应权限到AndroidManifest.xml中 <uses-permissio…

同旺科技 USB TO RS-485 定制款适配器--- 拆解(二)

内附链接 1、USB TO RS-485 定制款适配器 ● 支持USB 2.0/3.0接口&#xff0c;并兼容USB 1.1接口&#xff1b; ● 支持USB总线供电&#xff1b; ● 支持Windows系统驱动&#xff0c;包含WIN10 / WIN11系统32 / 64位&#xff1b; ● 支持Windows RT、Linux、Mac OS X、Windo…

【开源】基于Vue+SpringBoot的固始鹅块销售系统

项目编号&#xff1a; S 060 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S060&#xff0c;文末获取源码。} 项目编号&#xff1a;S060&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 鹅块类型模块2.3 固…

CentOS关闭 swap分区

临时关闭swap分区: swapoff -a # 永久关闭swap分区: sed -ri s/.*swap.*/#&/ /etc/fstab 或者 vim /etc/fstab free -m

《opencv实用探索·八》图像模糊之均值滤波、高斯滤波的简单理解

1、前言 什么是噪声&#xff1f; 该像素与周围像素的差别非常大&#xff0c;导致从视觉上就能看出该像素无法与周围像素组成可识别的图像信息&#xff0c;降低了整个图像的质量。这种“格格不入”的像素就被称为图像的噪声。如果图像中的噪声都是随机的纯黑像素或者纯白像素&am…

批量免费AI写作工具,批量免费AI写作软件

人工智能&#xff08;AI&#xff09;的应用在各个领域不断创新。面对繁重的写作任务,我们应该怎么完成&#xff1f;本文将专心分享批量免费AI写作的方法、工具以及选择时需要注意的事项。 批量免费AI写作的方法 利用开源AI模型 一种常见的批量免费AI写作方法是利用开源的AI模…

Unity中动态合批

文章目录 前言一、动态合批的规则1、材质相同是合批的前提&#xff0c;但是如果是材质实例的话&#xff0c;则一样无法合批。2、支持不同网格的合批3、动态合批需要网格支持的顶点条件二、我们导入一个模型并且制作一个Shader&#xff0c;来测试动态合批1、我们选择模型的 Mesh…

企业级SQL开发:如何审核发布到生产环境的SQL性能

自从上世纪 70 年代数据库开始普及以来&#xff0c;DBA 们就不停地遭遇各种各样的数据库管理难题&#xff0c;其中最为显著的&#xff0c;可能就是日常的开发任务中&#xff0c;研发人员们对于核心库进行变更带来的一系列风险。由于针对数据库的数据变更是一项非常常见的任务&a…

老师如何管理课堂纪律?

1 .与学生建立良好的师生关系&#xff0c;加强沟通。建立互相尊重的关系&#xff0c;让学生感受到老师的理解和关心 2. 制定合理的课堂规则和纪律&#xff0c;让学生了解规则的重要性和必要性&#xff0c;并遵守规则。 3. 在课堂上保持秩序&#xff0c;避免嘈杂的行为和讨论。…

MAMBA介绍:一种新的可能超过Transformer的AI架构

有人说&#xff0c;“理解了人类的语言&#xff0c;就理解了世界”。一直以来&#xff0c;人工智能领域的学者和工程师们都试图让机器学习人类的语言和说话方式&#xff0c;但进展始终不大。因为人类的语言太复杂&#xff0c;太多样&#xff0c;而组成它背后的机制&#xff0c;…

如何使用Python核对文件夹内的文件

说明&#xff1a;日常工作中&#xff0c;我们经常会遇到这样的场景&#xff1a;核对A、B文件夹中文件的差异&#xff0c;找出A、B文件夹中不同部分的文件&#xff1b; 本文介绍如何使用Python来实现&#xff1b; 第一步&#xff1a;获取文件清单 首先&#xff0c;我们要获取…