导盲赛事第二弹: 数据集与训练策略

本文围绕智能导盲机器狗比赛的数据集与训练策略展开。数据集方面,介绍了train与val数据融合的方法及代码,还提及伪标签训练(半监督学习)的入门、进阶和创新版方式。训练策略上,针对PaddleDetection,给出了显卡数量、batchsize、优化器选择等方面的建议,并提供了含droppath的ConvNeXt代码实例。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

导盲赛事第二弹: 数据集与训练策略 - 创想鸟

0、简介

一只导盲犬能够给盲人带来许多生活上的便利,但是导盲犬的培训周期长,费用高昂,因此,不是所有盲人能够拥有导盲犬,如果有机器狗代替导盲犬,将极大的造福盲人,此项比赛为智能导盲机器狗比赛,通过比赛来考评智能导盲机器狗的智能感知能力及综合运动性能,要求智能四足仿生机器人沿布置好的城市人行道场景走完全程并完成指定任务。

其实说起来高大上,当我们一句看到具体的任务的时候就会发现,其实就是一个非常简单的目标检测任务在出塞中,赛事组提供五种不同的目标让你去进行识别,但是为了服务于现实场景, 其在模型大小 以及检测速度等方面均提出了要求模型大小限制在200兆以内检测速度要求不低于20FPS。

目前赛事已经快接近尾声了,相信大家应该已经确定自己的路线了,而且网络的改进应该也差不多了,接下来要选择的应该就是在数据集与训练策越方面开始各显神通了,那么现在就让我们一起在数据集与训练策略方面下下功夫吧

上集指路:导盲赛道思路分享

1、数据集

1.1 train val 数据融合

有些比赛是直接把一堆数据给你然后让你自己划分train与val,去进行训练,那么通常情况下选手会先划分train与val然后去进行训练然后一点点改进网络,当val达到最高值的时候,也就是确定好了网络架构,那么一般选手就会将val与train再融合到一起再去跑一遍最后最好的网络然后去提交,而本赛事中赛事组已经帮你划好了train与val然后用coco数据集给你确定下来了,这样你就免去了第一步,并且保证了train与val的分布均匀,使用coco格式对于你先期确定网络架构有很大帮助,但是如果你对数据处理比较薄弱,那么在将train与val合并的时候就会出问题,但是别担心我这里已经帮你做好了这个工作。

你只需要运行下面的代码就可以得到一个包含train与val所有数据的文件夹了

In [ ]

## 解压文件夹!tar -zxvf data/data137625/WisdomGuide.tar.gz## 安装所需第三方库!pip install lxml!pip install pycocotools#coco2vocfrom pycocotools.coco import COCO import  os, cv2, shutilfrom lxml import etree, objectifyfrom tqdm import tqdmfrom PIL import Imageimport numpy as npimport timeimport jsondef cover_copy(src,dst):    '''    src和dst都必须是文件,该函数是执行覆盖操作    '''    if os.path.exists(dst):        os.remove(dst)        shutil.copy(src,dst)    else:        shutil.copy(src,dst)def coco2voc(basedir='VOCdevkit/COCO_VOC',sourcedir='WisdomGuide'):    """    basedir:用来存放转换后数据和标注文件    sourcedir:用来指定原始COCO数据集的存放位置    """    img_savepath= os.path.join(basedir,'JPEGImages')    ann_savepath=os.path.join(basedir,'Annotations')    main_path = os.path.join(basedir,"annotations")    for p in [basedir,img_savepath,ann_savepath,main_path]:        if os.path.exists(p):            shutil.rmtree(p)            os.makedirs(p)        else:            os.makedirs(p)        datasets = ['train','val']    # datasets = ['val2017']    for dataset in datasets:        start = time.time()        print(f"start {dataset}")        no_ann=[] #用来存放没有标注数据的图片的id,并将这些图片复制到results文件夹中        not_rgb=[] #是灰度图,同样将其保存        annfile = 'instance_{}.json'.format(dataset)        annpath=os.path.join(sourcedir,'annotations',annfile)                print('loading annotations into memory...')        tic = time.time()        with open(annpath, 'r') as f:            dataset_ann = json.load(f)        assert type(            dataset_ann        ) == dict, 'annotation file format {} not supported'.format(            type(dataset))        print('Done (t={:0.2f}s)'.format(time.time() - tic))                coco = COCO(annpath)        classes = dict()        for cat in coco.dataset['categories']:            classes[cat['id']] = cat['name']        imgIds = coco.getImgIds()        # imgIds=imgIds[0:1000]#测试用,抽取10张图片,看下存储效果        for imgId in tqdm(imgIds):            img = coco.loadImgs(imgId)[0]               filename = img['file_name']            filepath=os.path.join(sourcedir,dataset,filename)            annIds = coco.getAnnIds(imgIds=img['id'],  iscrowd=None)            anns = coco.loadAnns(annIds)                        if not len(anns):                # print(f"{dataset}:{imgId}该文件没有标注信息,将其复制到{dataset}_noann_result中,以使查看")                no_ann.append(imgId)                result_path = os.path.join(sourcedir,dataset+"_noann_result")                dest_path = os.path.join(result_path,filename)                if not os.path.exists(result_path):                    os.makedirs(result_path)                cover_copy(filepath,dest_path)                continue #如果没有标注信息,则把没有标注信息的图片移动到相关结果文件 noann_result中,来进行查看 ,然后返回做下一张图            #有标注信息,接着往下走,获取标注信息            objs = []            for ann in anns:                name = classes[ann['category_id']]                if 'bbox' in ann:                    # print('bbox in ann',imgId)                    bbox = ann['bbox']                    xmin = (int)(bbox[0])                    ymin = (int)(bbox[1])                    xmax = (int)(bbox[2] + bbox[0])                    ymax = (int)(bbox[3] + bbox[1])                    obj = [name, 1.0, xmin, ymin, xmax, ymax]                    #标错框在这里                    if not(xmin-xmax==0 or ymin-ymax==0):                        objs.append(obj)                 else:                    print(f"{dataset}:{imgId}bbox在标注文件中不存在")# 单张图有多个标注框,某个类别没有框                               annopath = os.path.join(ann_savepath,filename[:-3] + "xml") #生成的xml文件保存路径            dst_path = os.path.join(img_savepath,filename)                       im = Image.open(filepath)            image = np.array(im).astype(np.uint8)            if im.mode != "RGB":             # if img.shape[-1] != 3:                                                # print(f"{dataset}:{imgId}该文件非rgb图,其复制到{dataset}_notrgb_result中,以使查看")                # print(f"img.shape{image.shape} and img.mode{im.mode}")                not_rgb.append(imgId)                result_path = os.path.join(sourcedir,dataset+"_notrgb_result")                dest_path = os.path.join(result_path,filename)                if not os.path.exists(result_path):                    os.makedirs(result_path)                cover_copy(filepath,dest_path) #复制到notrgb_result来方便查看                                im=im.convert('RGB')                image = np.array(im).astype(np.uint8)                im.save(dst_path,quality=95)#图片经过转换后,放到我们需要的位置片                im.close()            else:                                cover_copy(filepath, dst_path)#把原始图像复制到目标文件夹            E = objectify.ElementMaker(annotate=False)            anno_tree = E.annotation(                E.folder('VOC'),                E.filename(filename),                E.source(                    E.database('COCO'),                    E.annotation('VOC'),                    E.image('COCO')                ),                E.size(                    E.width(image.shape[1]),                    E.height(image.shape[0]),                    E.depth(image.shape[2])                ),                E.segmented(0)            )            for obj in objs:                E2 = objectify.ElementMaker(annotate=False)                anno_tree2 = E2.object(                    E.name(obj[0]),                    E.pose(),                    E.truncated("0"),                    E.difficult(0),                    E.bndbox(                        E.xmin(obj[2]),                        E.ymin(obj[3]),                        E.xmax(obj[4]),                        E.ymax(obj[5])                    )                )                anno_tree.append(anno_tree2)            etree.ElementTree(anno_tree).write(annopath, pretty_print=True)        print(f"{dataset}该数据集有{len(no_ann)}/{len(imgIds)}张图片没有instance标注信息,已经这些图片复制到{dataset}_noann_result中以使进行查看")        print(f"{dataset}该数据集有{len(not_rgb)}/{len(imgIds)}张图片是非RGB图像,已经这些图片复制到{dataset}_notrgb_result中以使进行查看")        duriation = time.time()-start        print(f"数据集{dataset}处理完成用时{round(duriation/60,2)}分")#run coco2voccoco2voc()#voc2coco!cd voc2coco && python voc2coco.py ../VOCdevkit/COCO_VOC/Annotations ../VOCdevkit/COCO_VOC/COCO.json!mkdir COCO_all!mv VOCdevkit/COCO_VOC/JPEGImages COCO_all/!mv VOCdevkit/COCO_VOC/COCO.json COCO_all/

   

1.2 伪标签训练

一些比赛不会像导盲赛事一样不向你提供test数据集让你把网络提交上去,而是会选择把test数据集的照片给你,让你预测完了把预测结果交上去,那么这个时候伪标签就有用了,简单来说就是将预测的结果给当成标注信息再和赛事提供的原数据一起训练一般来说会上升百分之0.几个点,但是有的赛事不允许伪标签。 我看了一眼咱们这个比赛规则并没有限制这个。但是很遗憾啦,这个赛事并没有给你提供test照片呀

但是我相信有些财大气粗,底蕴雄厚的学校肯定积累过这些数据,但是其中可能很多都没有标注,一个个标注也不太现实,那么你就可以尝试使用伪标签训练的形式进行训练,而这种训练方式也有一个比较高大上的名字半监督学习

这里引用知乎的文章简单介绍一下半监督学习的方式

来源伪标签(Pseudo-Labelling)——锋利的匕首

入门版1. 使用标记数据训练有监督模型M2. 使用有监督模型M对无标签数据进行预测,得出预测概率P3. 通过预测概率P筛选高置信度样本4. 使用有标记数据以及伪标签数据训练新模型M’

       

导盲赛事第二弹: 数据集与训练策略 - 创想鸟        

进阶版1. 使用标记数据训练有监督模型M2. 使用有监督模型M对无标签数据进行预测,得出预测概率P3. 通过预测概率P筛选高置信度样本4. 使用有标记数据以及伪标签数据训练新模型M’5. 将M替换为M’,重复以上步骤直至模型效果不出现提升

       

导盲赛事第二弹: 数据集与训练策略 - 创想鸟        

创新版1. 使用标记数据训练有监督模型M2. 使用有监督模型M对无标签数据进行预测,得出预测概率P3. 将模型损失函数改为Loss = loss(labeled_data) + alpha*loss(unlabeled_data)4. 使用有标记数据以及伪标签数据训练新模型M’

       

导盲赛事第二弹: 数据集与训练策略 - 创想鸟        

2、 训练策略

相信大部分人使用的都是PaddleDetection,那么在训练策略方面有些事情你可能需要注意一下

看看你有几个卡 PaddleDetection的配置文件是以八卡为基准进行配置的,因此如果你只有一个显卡的话最好将学习率也变为原来的1/8,并且如果你的batsize又缩小了的话那么建议你也把学习率同步缩小卡不是越多越好 可以看到如果是训练COCO数据集的话一般是使用8卡v100起步,但是那是因为coco数据集太大了!,你像是咱们这个数据集我两张1080ti 每张卡batchsize设置为8,每个epoch也才333个iter!目前我的实验下来如果是使用4卡v100的效果是不如两张1080ti的。batchsize不是越大越好 目前ai studio已经上线了a100,然后我就震惊了因为我的网络只有100m大小,然后我的分辨率是416×416,我在a100上最后直接把batchsize改到了48,太离谱了!但是当我花了7个小时去训练后,我平静了,a100确实可以大幅缩短训练时间,但是最终精度反而相比较两张1080ti低了0.5个点。因此我建议你最高也就把batchsize放到32吧。别一味地放大batchsize了。优化器的选择 PaddleDetection中目前主流的两个优化器分别为 Momentum以及AdamW,其中Momentum适合于普通的卷积网络,而AdamW适合一些比较难以训练的网络,或者说如果你相信奇迹并且你是炼丹大师而且相信能抖出一个奇迹那么Momentum就比较适合你,但是如果你是一个比较求稳想找一个比较合适的点就行那么AdamW就比较适合你。惩戒方法选择 为了防止网络过拟合一般会在训练策略的时候加一些措施比如droppath等,但是droppath需要在网络中加相关代码,一会我再给大家放一个实例,这里聊得是PaddleDetection在配置文件中一般的做法,一般如果你用Momentum那么会使用L2范式进行规范,如果你用的AdamW则会使用clip_grad_by_norm进行规范。

下面我就把我的一个训练策略开放给大家做一个参考

epoch: 400LearningRate:  base_lr: 0.00025  schedulers:    - !CosineDecay      max_epochs: 500    - !LinearWarmup      start_factor: 0.      epochs: 5OptimizerBuilder:  clip_grad_by_norm: 0.1  regularizer: false  optimizer:    type: AdamW    weight_decay: 0.0001

   

2.1 droppath实例 使用ConvNeXt进行droppath操作

In [ ]

# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##    http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# Code was based on https://github.com/facebookresearch/ConvNeXt# import math# from numbers import Integral# import paddle# import paddle.nn as nn# import paddle.nn.functional as F# from ppdet.core.workspace import register, serializable# from paddle.regularizer import L2Decay# from paddle.nn.initializer import Uniform# from paddle import ParamAttr# from paddle.nn.initializer import Constant# from paddle.vision.ops import DeformConv2D# from .name_adapter import NameAdapter# from ppdet.modeling.shape_spec import ShapeSpecimport paddleimport paddle.nn as nnimport paddle.nn.functional as F# __all__ = ['ConvNeXt']trunc_normal_ = nn.initializer.TruncatedNormal(std=0.02)zeros_ = nn.initializer.Constant(value=0.0)ones_ = nn.initializer.Constant(value=1.0)class Identity(nn.Layer):    def __init__(self):        super().__init__()    def forward(self, x):        return xdef drop_path(x, drop_prob=0.0, training=False):    if drop_prob == 0.0 or not training:        return x    keep_prob = paddle.to_tensor(1 - drop_prob)    shape = (paddle.shape(x)[0], ) + (1, ) * (x.ndim - 1)    random_tensor = keep_prob + paddle.rand(shape, dtype=x.dtype)    random_tensor = paddle.floor(random_tensor)  # binarize    output = x.divide(keep_prob) * random_tensor    return outputclass DropPath(nn.Layer):    def __init__(self, drop_prob=None):        super(DropPath, self).__init__()        self.drop_prob = drop_prob    def forward(self, x):        return drop_path(x, self.drop_prob, self.training)class Block(nn.Layer):    """ ConvNeXt Block. There are two equivalent implementations:    (1) DwConv -> LayerNorm (channels_first) -> 1x1 Conv -> GELU -> 1x1 Conv; all in (N, C, H, W)    (2) DwConv -> Permute to (N, H, W, C); LayerNorm (channels_last) -> Linear -> GELU -> Linear; Permute back    Args:        dim (int): Number of input channels.        drop_path (float): Stochastic depth rate. Default: 0.0        layer_scale_init_value (float): Init value for Layer Scale. Default: 1e-6.    """    def __init__(self, dim, drop_path=0., layer_scale_init_value=1e-6):        super().__init__()        self.dwconv = nn.Conv2D(dim, dim, kernel_size=7, padding=3,                                groups=dim)  # depthwise conv        self.norm = LayerNorm(dim, epsilon=1e-6)        self.pwconv1 = nn.Linear(            dim, 4 * dim)  # pointwise/1x1 convs, implemented with linear layers        self.act = nn.GELU()        self.pwconv2 = nn.Linear(4 * dim, dim)        self.gamma = paddle.create_parameter(            shape=[dim],            dtype='float32',            default_initializer=nn.initializer.Constant(                value=layer_scale_init_value)        ) if layer_scale_init_value > 0 else None        self.drop_path = DropPath(drop_path) if drop_path > 0. else Identity()    def forward(self, x):        input = x        x = self.dwconv(x)        x = x.transpose([0, 2, 3, 1])  # (N, C, H, W) -> (N, H, W, C)        x = self.norm(x)        x = self.pwconv1(x)        x = self.act(x)        x = self.pwconv2(x)        if self.gamma is not None:            x = self.gamma * x        x = x.transpose([0, 3, 1, 2])  # (N, H, W, C) -> (N, C, H, W)        x = input + self.drop_path(x)        return xclass LayerNorm(nn.Layer):    """ LayerNorm that supports two data formats: channels_last (default) or channels_first.    The ordering of the dimensions in the inputs. channels_last corresponds to inputs with    shape (batch_size, height, width, channels) while channels_first corresponds to inputs    with shape (batch_size, channels, height, width).    """    def __init__(self,                 normalized_shape,                 epsilon=1e-6,                 data_format="channels_last"):        super().__init__()        self.weight = paddle.create_parameter(shape=[normalized_shape],                                              dtype='float32',                                              default_initializer=ones_)        self.bias = paddle.create_parameter(shape=[normalized_shape],                                            dtype='float32',                                            default_initializer=zeros_)        self.epsilon = epsilon        self.data_format = data_format        if self.data_format not in ["channels_last", "channels_first"]:            raise NotImplementedError        self.normalized_shape = (normalized_shape, )    def forward(self, x):        if self.data_format == "channels_last":            return F.layer_norm(x, self.normalized_shape, self.weight,                                self.bias, self.epsilon)        elif self.data_format == "channels_first":            u = x.mean(1, keepdim=True)            s = (x - u).pow(2).mean(1, keepdim=True)            x = (x - u) / paddle.sqrt(s + self.epsilon)            x = self.weight[:, None, None] * x + self.bias[:, None, None]            return x# @register# @serializableclass ConvNeXt(nn.Layer):    """ ConvNeXt        A Paddle impl of : `A ConvNet for the 2020s`  -          https://arxiv.org/pdf/2201.03545.pdf    Args:        in_chans (int): Number of input image channels. Default: 3        depths (tuple(int)): Number of blocks at each stage. Default: [3, 3, 9, 3]        dims (int): Feature dimension at each stage. Default: [96, 192, 384, 768]        drop_path_rate (float): Stochastic depth rate. Default: 0.        layer_scale_init_value (float): Init value for Layer Scale. Default: 1e-6.        head_init_scale (float): Init scaling value for classifier weights and biases. Default: 1.    """    def __init__(        self,        in_chans=3,        out_channals=[1, 2, 3],        depths=[3, 3, 9, 3],        dims=[96, 192, 384, 768],        drop_path_rate=0.,        layer_scale_init_value=1e-6,    ):        super().__init__()        self._out_strides = [4, 8, 16, 32]        self.dims = dims        self.out_channals = out_channals        self.downsample_layers = nn.LayerList(        )  # stem and 3 intermediate downsampling conv layers        stem = nn.Sequential(            nn.Conv2D(in_chans, dims[0], kernel_size=4, stride=4),            LayerNorm(dims[0], epsilon=1e-6, data_format="channels_first"))        self.downsample_layers.append(stem)        for i in range(3):            downsample_layer = nn.Sequential(                LayerNorm(dims[i], epsilon=1e-6, data_format="channels_first"),                nn.Conv2D(dims[i], dims[i + 1], kernel_size=2, stride=2),            )            self.downsample_layers.append(downsample_layer)        self.stages = nn.LayerList(        )  # 4 feature resolution stages, each consisting of multiple residual blocks        dp_rates = [            x.item() for x in paddle.linspace(0, drop_path_rate, sum(depths))        ]        cur = 0        for i in range(4):            stage = nn.Sequential(*[                Block(dim=dims[i],                      drop_path=dp_rates[cur + j],                      layer_scale_init_value=layer_scale_init_value)                for j in range(depths[i])            ])            self.stages.append(stage)            cur += depths[i]        self.norm = nn.LayerNorm(dims[-1], epsilon=1e-6)  # final norm layer        self.apply(self._init_weights)    def _init_weights(self, m):        if isinstance(m, (nn.Conv2D, nn.Linear)):            trunc_normal_(m.weight)            zeros_(m.bias)    # @property    def out_shape(self):        return [            ShapeSpec(                channels=self.dims[i], stride=self._out_strides[i])            for i in self.out_channals        ]    def forward(self, x):        x = x        outs = []        for i in range(4):            x = self.downsample_layers[i](x)            x = self.stages[i](x)            if i in self.out_channals:                outs.append(x)        return outsif __name__ == "__main__":    model = ConvNeXt()    paddle.summary(model, (1, 3, 640, 640))

   

以上就是导盲赛事第二弹: 数据集与训练策略的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/53377.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 09:36:18
下一篇 2025年11月9日 09:40:08

相关推荐

  • 自定义函数处理事件和回调

    在软件开发中,自定义函数可用于处理事件和回调,定制程序的行为。首先,使用def创建函数,定义处理事件时的行为。然后,通过函数或方法将自定义函数与事件关联。例如,可以使用button.clicked.connect(handle_button_click)将handle_button_click()函…

    2025年12月12日
    000
  • PHP 函数在 DevOps 流程中的自动化实践

    php 函数在 devops 流程中提供了自动化实践:安装 composer,一个 php 依赖管理器,以简化库安装和更新。使用 php 开发 devops 工具来创建项目脚本,用于通过命令行执行任务。使用 composer 运行脚本,简化了脚本执行过程。利用 php 函数(如 exec())来自动…

    2025年12月12日
    000
  • PHP 函数与 Python 函数比较

    php和python函数在声明(function、def)、类型提示(php 8.0、注解)、返回值(return)、可变参数(php …、python *)方面存在差异。php函数sum_list和python函数sum_list均能计算整数列表的和。 PHP 函数与 Python 函…

    2025年12月12日
    000
  • 探索 Matrix:将类似 JavaScript 的异步引入 PHP

    您是一位羡慕 javascript 异步/等待范例的简单性的 php 开发人员吗?您是否希望能够同样轻松地在 php 中管理异步任务?别再犹豫了!今天,我们将深入研究 matrix,这是一个尖端的 php 库,它为 php 生态系统带来了类似 javascript 的异步操作的强大功能。 什么是矩阵…

    2025年12月12日
    000
  • PHP 函数与其他语言函数对比

    php 函数与其他语言函数存在差异,包括语法(例如 php 使用圆括号,而 javascript 使用花括号)、参数传递方式(php 通过引用传递,而 javascript 通过值传递)、返回值类型(php 可以返回数字、字符串或数组,而 javascript 可以返回数字、字符串、布尔值或对象)。…

    2025年12月12日
    000
  • ph函数安全威胁情报分析与应用

    ph 函数库提供安全威胁情报分析和应用功能,支持集成威胁情报馈送。实战案例包括从 virustotal 提取恶意域名,并利用 ph 函数检测恶意 url。高级功能包括归因分析、态势感知和沙箱集成,可提升威胁情报能力并优化响应措施。 PH 函数:安全威胁情报分析与应用 概述 PH 函数是 Python…

    2025年12月12日
    000
  • php和java、python等语言的函数对比

    php、java 和 python 都支持函数,用于封装特定任务。php 函数以 function 关键字开头,java 函数(方法)定义在类中,python 函数以 def 关键字开头。三个语言的实战用例类似,以计算两个数之和为例,php 使用独立函数,java 定义在类中,python 使用缩进…

    2025年12月12日
    000
  • ph函数安全问题最佳实践与专家建议

    ph 函数处理用户输入时存在安全风险。最佳实践包括验证输入是否为数字、使用过滤器清除特殊字符、使用类型强制转换强制转换为 float。此外,专家建议使用经过参数化的查询、避免使用 eval() 函数、禁用远程文件包含。 pH 函数安全问题最佳实践与专家建议 简介 PHP 函数在处理输入数据时可能会存…

    2025年12月12日
    000
  • PHP 函数与 Python 函数的区别

    php 和 python 函数的区别在于:1. 语法不同;2. 传参方式不同(php 按值,python 按引用);3. 返回值不同(php 返回 null,python 返回 none)。 PHP 函数与 Python 函数的区别 PHP 和 Python 都是广泛使用的编程语言,它们都支持函数的…

    2025年12月12日
    000
  • python和PHP的函数之间差异有哪些

    python 和 php 中函数的主要差异包括语法、参数分隔符、类型提示、默认参数、返回值数量等,具体如下:语法:python 使用 def 关键字定义函数,php 使用 function 关键字。参数分隔符:python 参数以空格分隔,php 参数以逗号分隔。类型提示:python 支持类型提示…

    2025年12月12日
    000
  • ExcelMapper:简化 PHP 项目中的 Excel 数据导入

    在 php 应用程序中管理从 excel 文件导入的数据通常是一个繁琐的过程。无论您是构建 crm、库存系统还是任何数据驱动的应用程序,处理具有各种结构和格式的 excel 文件都是常见要求。为了简化此过程,我很高兴推出 excelmapper — 一个 php 库,旨在简化 excel 数据到 p…

    2025年12月12日
    000
  • php平台有哪些

    PHP平台指包含PHP解释器和附加组件的套件,用于构建Web应用程序。主要平台包括XAMPP(跨平台)、WAMP(Windows)、LAMP(跨平台)、MEAN(全栈JavaScript)、Laravel(PHP框架)和Symfony(PHP框架)。选择合适平台时,需考虑操作系统兼容性、项目规模、开…

    2025年12月12日
    000
  • PHP框架社区如何促进职业发展和人脉拓展?

    php框架社区为php开发者提供职业成长和人脉拓展的机会。通过知识共享(论坛、研讨会)、代码贡献(开源框架)和项目指导(资深开发者),社区促进职业发展。人脉拓展渠道包括会议、社交媒体和在线论坛,开发者可以与其他开发者、专家和潜在雇主建立联系。实战案例证明了社区参与如何提升技术栈和推动职业转型,为未来…

    2025年12月12日
    000
  • 被斜线切割的区域

    959。被斜线切割的区域 中 主题: 数组、哈希表、深度优先搜索、广度优先搜索、并集查找、矩阵 n x n 网格由 1 x 1 方格组成,其中每个 1 x 1 方格由 ‘/’、” 或空格 ‘ ‘ 组成。这些字符将正方形划分为连续的区域。 给…

    2025年12月12日
    000
  • 云社区对PHP框架社区支持的影响

    云社区为 php 框架社区提供了通过协作、知识共享、支持和创新来蓬勃发展的平台:协作和知识共享:云社区提供平台供开发者连接、分享知识和讨论最佳实践。支持和指导:社区提供文档、教程和支持论坛,帮助开发者入门并获得经验丰富的支持。创新和发展:共享想法、实验和协作促进云社区内 php 框架的进步。 云社区…

    2025年12月12日
    000
  • 使用 PHP 框架进行性能优化的最佳实践

    对于构建高性能 php 应用程序,以下是在 php 框架中实现性能优化的最佳实践:选择轻量级框架,例如 laravel 或 symfony。缓存和优化查询,使用 redis 或 memcached 等缓存机制,并利用 orm、索引和预编译语句优化查询。使用异步编程,例如 guzzle 或 swool…

    2025年12月12日
    000
  • PHP framework社区的情感支持分析

    使用自然语言处理对 php 框架社区论坛数据的情感分析揭示了以下见解:社区情绪总体呈积极态势,平均情感极性得分为 0.1。与技术相关帖子的情感极性得分高于与社区相关帖子的得分。来自社区成员的回复显示出更高的情感极性,表明成员在提供支持方面非常积极。 PHP 框架社区的情感分析:使用自然语言处理量化支…

    2025年12月12日
    000
  • PHP框架的不同社区支持水平有哪些?

    php 框架的社区支持水平至关重要。不同框架的社区支持水平差异很大,涵盖文档、论坛、聊天社区和社区规模等方面。以 laravel 为例,它拥有庞大且活跃的社区,提供迅速的帮助和持续的更新,并提供企业级支持。因此,在选择 php 框架时,应考虑其社区支持水平,以确保快速获得帮助和保持框架的最新状态。 …

    2025年12月12日
    000
  • 使用 honeystone/context 构建多租户应用程序

    不要与 laravel 的新上下文库混淆,该包可用于构建多上下文多租户应用程序。大多数多租户库本质上都有一个“租户”上下文,因此如果您需要多个上下文,事情可能会变得有点麻烦。这个新包解决了这个问题。 让我们看一个例子好吗? 示例项目 对于我们的示例应用程序,我们将拥有一个组织成团队的全球用户群,每个…

    2025年12月12日
    000
  • PHP框架社区在不同地区或国家有何差异?

    摘要:php框架社区存在区域差异,主要体现在以下几个方面:1. 文化差异:亚太地区:协作、礼貌欧洲:严谨、专业性美洲:创新、创业精神2. 技术偏好:亚洲:laravel、codeigniter欧洲:symfony、zend framework美洲:cakephp、yii3. 社区参与:亚太地区:st…

    2025年12月12日
    000

发表回复

登录后才能评论
关注微信