0

0

基于efficientDet在jetson NX上实现目标检测

P粉084495128

P粉084495128

发布时间:2025-07-31 11:05:36

|

713人浏览过

|

来源于php中文网

原创

本文讲述在jetson nx部署efficientdet的过程。先介绍efficientnet的复合模型扩张方法,通过平衡深度、宽度和分辨率提升性能;提及efficientdet的bifpn结构。接着说明将动态图转为静态图并导出,测试静态图性能,最后阐述在jetson nx配置环境及部署流程,其期望fps为4。

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

基于efficientdet在jetson nx上实现目标检测 - php中文网

前言

听闻jetson NX的算力很强,前几天部署了ppyolo并跑出了接近100的FPS,于是这几天尝试部署一下efficient。基于efficientDet在jetson NX上实现目标检测 - php中文网基于efficientDet在jetson NX上实现目标检测 - php中文网基于efficientDet在jetson NX上实现目标检测 - php中文网        

引用

paddle在jetson NX的配置

引用了这位大佬的efficientnet

知乎

efficient net

背景

卷积神经网络扩大规模以获得更好的精度,比如可以提高网络深度(depth)、网络宽度(width)和输入图像分辨率 (resolution)大小。但是通过人工去调整 depth, width, resolution 的放大或缩小的很困难的,在计算量受限时有放大哪个缩小哪个,这些都是很难去确定的,换句话说,这样的组合空间太大,人力无法穷举。基于上述背景,该论文提出了一种新的模型缩放方法,它使用一个简单而高效的复合系数来从depth, width, resolution 三个维度放大网络,不会像传统的方法那样任意缩放网络的维度,基于神经结构搜索技术可以获得最优的一组参数(复合系数)。从下图可看出,EfficientNet不仅比别的网络快很多,而且精度也更高。基于efficientDet在jetson NX上实现目标检测 - php中文网        

简介

EfficientNet从三个维度均扩大了,但是扩大多少,就是通过作者提出来的复合模型扩张方法结合神经结构搜索技术获得的。基于efficientDet在jetson NX上实现目标检测 - php中文网        

复合模型扩张方法

实现最优化目标。 优化目标就是在资源有限的情况下,要最大化 Accuracy, 优化目标的公式表达如下: 基于efficientDet在jetson NX上实现目标检测 - php中文网 作者发现,更大的网络具有更大的宽度、深度或分辨率,往往可以获得更高的精度,但精度增益在达到80%后会迅速饱和,这表明了只对单一维度进行扩张的局限性,实验结果如下图:基于efficientDet在jetson NX上实现目标检测 - php中文网        

作者指出,模型扩张的各个维度之间并不是完全独立的,比如说,对于更大的分辨率图像,应该使用更深、更宽的网络,这就意味着需要平衡各个扩张维度,而不是在单一维度张扩张。

所以本文提出了复合扩张方法,这也是文章核心的地方。 基于efficientDet在jetson NX上实现目标检测 - php中文网        

先固定公式中的一个参数,然后求别参数,从而得到基础的d0网络,然后固定其他参数,根据基础网络扩展d1以上的网络

网络结构

基于efficientDet在jetson NX上实现目标检测 - php中文网        

efficientDet

文章动机

1、如何高效的进行多尺度特征融合(efficient multi-scale feature fusion):提到多尺度融合,在融合不同的输入特征时,以往的研究(FPN以及一些对FPN的改进工作)大多只是没有区别的将特征相加;然而,由于这些不同的输入特征具有不同的分辨率,我们观察到它们对融合输出特征的贡献往往是不平等的,为了解决这一问题,作者提出了一种简单而高效的加权(类似与attention)双向特征金字塔网络(BiFPN),它引入可学习的权值来学习不同输入特征的重要性,同时反复应用自顶向下和自下而上的多尺度特征融合。

2、如何对模型进行扩张(参考上文 EfficientNet ,同时考虑depth、width和resolution)

作者基于EfficientNet, 提出对检测器的backbone等网络进行模型缩放,并且结合提出的BiFPN提出了新的检测器家族,叫做EfficientDet。本文提出的检测器的主要遵循one-stage的设计思想,通过优化网络结构,可以达到更高的效率和精度。

基于efficientDet在jetson NX上实现目标检测 - php中文网        

BiFPN结构

如下图所示,BiFPN在图e的基础上增加了shortcut,这些都是在现有的一些工作的基础上添砖加瓦。基于efficientDet在jetson NX上实现目标检测 - php中文网        

伪softmax

作者提出了快速的限制方法 基于efficientDet在jetson NX上实现目标检测 - php中文网        

efficientDet静态图导出

之前的写好的是动态图模式的复现,我们首先要将其转换成静态图模式

In [2]
%cd EfficientDet/
       
/home/aistudio/EfficientDet
       

安装两个库

银河易创
银河易创

一站式AIGC创作平台,集成GPT-3.5、GPT-4、文心一言等对话模型、Midjourney、DallE等绘画工具、AI音乐、AI视频和AI PPT等功能!

下载
In [ ]
!pip install pycocotools webcolors
   
In [ ]
# 先检查动态图能不能跑通!python b.py
   

接下来导出静态图

注意,自定义算子的apply无法在静态图里面使用,因此如果要导出静态图,必须要先设置好自定义算子在静态静态图的运动模式

#自定义算子class SwishImplementation(PyLayer):    @staticmethod
    def forward(ctx, i):
        result = i * F.sigmoid(i)
        ctx.save_for_backward(i)        return result    @staticmethod
    def backward(ctx, grad_output):
        i = ctx.saved_tensor()[0]
        sigmoid_i = F.sigmoid(i)        return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i)))
       
# 动态图模式class MemoryEfficientSwish(nn.Layer):
    def forward(self, x):
        return SwishImplementation.apply(x)
       
# 静态图的自定义算子的运用class Swish(nn.Layer):
    def forward(self, x):
        return x * F.sigmoid(x)
   
In [ ]
# 导出静态图import paddleimport cv2import numpy as npfrom backbone import EfficientDetBackbonefrom efficientdet.utils import BBoxTransform, ClipBoxesfrom utils.utils import invert_affine, postprocess, preprocess_videoimport os

compound_coef = 0# 更改compound可以选择efficientDet的版本force_input_size = None  # set None to use default sizethreshold = 0.2iou_threshold = 0.2obj_list = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',            'fire hydrant', '', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',            'cow', 'elephant', 'bear', 'zebra', 'giraffe', '', 'backpack', 'umbrella', '', '', 'handbag', 'tie',            'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove',            'skateboard', 'surfboard', 'tennis racket', 'bottle', '', 'wine glass', 'cup', 'fork', 'knife', 'spoon',            'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut',            'cake', 'chair', 'couch', 'potted plant', 'bed', '', 'dining table', '', '', 'toilet', '', 'tv',            'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink',            'refrigerator', '', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier',            'toothbrush']# tf bilinear interpolation is different from any other's, just make doinput_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536, 1536]
input_size = input_sizes[compound_coef] if force_input_size is None else force_input_size# load modelmodel = EfficientDetBackbone(compound_coef=compound_coef, num_classes=len(obj_list)
,onnx_export=True)
model.set_state_dict(paddle.load(f'weights/efficientdet-d{compound_coef}.pdparams'))
model.eval()
input_spec = paddle.static.InputSpec([1, 3, 512, 512], 'float32', 'image')# input_spec = paddle.static.InputSpec([None, 3, 512, 512], 'float32', 'image')# 填写底下那句,到后面None会被表示成-1,这样如果动态图里面有-1就不行了model = paddle.jit.to_static(model, input_spec=[input_spec])
paddle.jit.save(model, 'inference_models/efficientDet')# paddle.onnx.export(model, "inference", input_spec=[input_spec],opset_version=11)# 打印保存的模型文件名print(os.listdir('inference_models'))
   

注意在设置模型输入类型时,如果动态图模式里,维度大小表示时使用了-1.则输入类型可能不能有-1

模型可视化

点击项目右边的数据可视化,然后选择模型文件为inference_models/efficientDet.pdmodel,然后进入可视化即可

这个模型有点大!

测试静态图

In [ ]
# Core Author: Zylo117# Script's Author: winter2897 """
Simple Inference Script of EfficientDet-Pytorch for detecting objects on webcam
"""import timeimport paddleimport cv2import numpy as npfrom efficientdet.utils import BBoxTransform, ClipBoxesfrom utils.utils import invert_affine, postprocess, preprocess_video# Video's pathvideo_src = 'video.mp4'  # set int to use webcam, set str to read from a video filecompound_coef = 0force_input_size = None  # set None to use default sizethreshold = 0.2iou_threshold = 0.2obj_list = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',            'fire hydrant', '', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',            'cow', 'elephant', 'bear', 'zebra', 'giraffe', '', 'backpack', 'umbrella', '', '', 'handbag', 'tie',            'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove',            'skateboard', 'surfboard', 'tennis racket', 'bottle', '', 'wine glass', 'cup', 'fork', 'knife', 'spoon',            'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut',            'cake', 'chair', 'couch', 'potted plant', 'bed', '', 'dining table', '', '', 'toilet', '', 'tv',            'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink',            'refrigerator', '', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier',            'toothbrush']# tf bilinear interpolation is different from any other's, just make doinput_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536, 1536]
input_size = input_sizes[compound_coef] if force_input_size is None else force_input_sizedef display(preds, imgs):
    for i in range(len(imgs)):        if len(preds[i]['rois']) == 0:            return imgs[i]        for j in range(len(preds[i]['rois'])):            # print("x1 y1 x2 y2",preds[i]['rois'][j])
            (x1, y1, x2, y2) = preds[i]['rois'][j].astype(np.int)
            
            cv2.rectangle(imgs[i], (x1, y1), (x2, y2), (255, 255, 0), 2)
            obj = obj_list[preds[i]['class_ids'][j]]
            score = float(preds[i]['scores'][j])
            cv2.putText(imgs[i], '{}, {:.3f}'.format(obj, score),
                        (x1, y1 + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                        (255, 255, 0), 1)        
        return imgs[i]# BoxregressBoxes = BBoxTransform()
clipBoxes = ClipBoxes()# Video capturecap = cv2.VideoCapture(video_src)
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
fps = cap.get(cv2.CAP_PROP_FPS)
width, height = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
outvideo = cv2.VideoWriter('result.mp4', fourcc, fps, (width, height))

model = paddle.jit.load('inference_models/efficientDet')# predictor=predict_config("inference_models/u2netp.pdmodel","inference_models/u2netp.pdiparams")while True:
    ret, frame = cap.read()    if not ret:        break
    # print("frame shape is",frame.shape)
    # frame preprocessing
    ori_imgs, framed_imgs, framed_metas = preprocess_video(frame, max_size=input_size)

    x = paddle.stack([paddle.to_tensor(fi) for fi in framed_imgs], 0)

    x = x.astype(paddle.float32).transpose([0, 3, 1, 2])    # model predict
    with paddle.no_grad():        
        # y=x.numpy().astype(np.float32)
        # l=predict(predictor,[y])
        time_start = time.time()
        l=model(x)        print('Time Cost:{}'.format(time.time()-time_start) , "s")
        regression=paddle.to_tensor(l[-3]).astype(paddle.float32)
        classification=paddle.to_tensor(l[-2]).astype(paddle.float32)
        anchors=paddle.to_tensor(l[-1]).astype(paddle.float32)       # print(l)
        out = postprocess(x,
                        anchors, regression, classification,
                        regressBoxes, clipBoxes,
                        threshold, iou_threshold)    # result
    out = invert_affine(framed_metas, out)
    img_show = display(out, ori_imgs)    # show frame by frame
    # cv2.imshow('frame',img_show)
    outvideo.write(img_show)    if cv2.waitKey(1) & 0xFF == ord('q'): 
        breakcap.release()
cv2.destroyAllWindows()
   

cpu版的aistudio推理较慢,GPU版大约在25帧左右

部署到jetson NX

jetson NX上配置paddleGPU的环境

具体参考我之前写的 paddle在jetson NX的配置

efficientDet的部署

把efficientdet,utils和inference_models三个文件夹下载下来(里面包含了模型和目标检测所用的所有工具) 然后在板子上用以下代码即可运行efficientDet。 或者使用work文件下,我已经打包好了的

In [ ]
import paddleimport cv2import numpy as npfrom efficientdet.utils import BBoxTransform, ClipBoxesfrom utils.utils import invert_affine, postprocess, preprocess_video# Video's pathvideo_src = 'video.mp4'  # set int to use webcam, set str to read from a video filecompound_coef = 0force_input_size = None  # set None to use default sizethreshold = 0.2iou_threshold = 0.2obj_list = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',            'fire hydrant', '', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',            'cow', 'elephant', 'bear', 'zebra', 'giraffe', '', 'backpack', 'umbrella', '', '', 'handbag', 'tie',            'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove',            'skateboard', 'surfboard', 'tennis racket', 'bottle', '', 'wine glass', 'cup', 'fork', 'knife', 'spoon',            'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut',            'cake', 'chair', 'couch', 'potted plant', 'bed', '', 'dining table', '', '', 'toilet', '', 'tv',            'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink',            'refrigerator', '', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier',            'toothbrush']# tf bilinear interpolation is different from any other's, just make doinput_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536, 1536]
input_size = input_sizes[compound_coef] if force_input_size is None else force_input_size# function for displaydef gstreamer_pipeline(
    capture_width=1280,
    capture_height=720,
    display_width=1280,
    display_height=720,
    framerate=60,
    flip_method=0,):
    return (        "nvarguscamerasrc ! "
        "video/x-raw(memory:NVMM), "
        "width=(int)%d, height=(int)%d, "
        "format=(string)NV12, framerate=(fraction)%d/1 ! "
        "nvvidconv flip-method=%d ! "
        "video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! "
        "videoconvert ! "
        "video/x-raw, format=(string)BGR ! appsink"
        % (
            capture_width,
            capture_height,
            framerate,
            flip_method,
            display_width,
            display_height,
        )
    ) 

    



def display(preds, imgs):
    for i in range(len(imgs)):        if len(preds[i]['rois']) == 0:            return imgs[i]        for j in range(len(preds[i]['rois'])):            # print("x1 y1 x2 y2",preds[i]['rois'][j])
            (x1, y1, x2, y2) = preds[i]['rois'][j].astype(np.int)
            
            cv2.rectangle(imgs[i], (x1, y1), (x2, y2), (255, 255, 0), 2)
            obj = obj_list[preds[i]['class_ids'][j]]
            score = float(preds[i]['scores'][j])
            cv2.putText(imgs[i], '{}, {:.3f}'.format(obj, score),
                        (x1, y1 + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                        (255, 255, 0), 1)        
        return imgs[i]# BoxregressBoxes = BBoxTransform()
clipBoxes = ClipBoxes()# Video capturecap = cv2.VideoCapture(gstreamer_pipeline(flip_method=0), cv2.CAP_GSTREAMER)# cap = cv2.VideoCapture(video_src)fps = cap.get(cv2.CAP_PROP_FPS)
width, height = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

model = paddle.jit.load('inference_models/efficientDet')while True:
    ret, frame = cap.read()    if not ret:        break
    # print("frame shape is",frame.shape)
    # frame preprocessing
    ori_imgs, framed_imgs, framed_metas = preprocess_video(frame, max_size=input_size)

    x = paddle.stack([paddle.to_tensor(fi) for fi in framed_imgs], 0)

    x = x.astype(paddle.float32).transpose([0, 3, 1, 2])    # model predict
    with paddle.no_grad():        
        # y=x.numpy().astype(np.float32)
        # l=predict(predictor,[y])
        l=model(x)
        regression=paddle.to_tensor(l[-3]).astype(paddle.float32)
        classification=paddle.to_tensor(l[-2]).astype(paddle.float32)
        anchors=paddle.to_tensor(l[-1]).astype(paddle.float32)        # print(l)
        out = postprocess(x,
                        anchors, regression, classification,
                        regressBoxes, clipBoxes,
                        threshold, iou_threshold)    # result
    out = invert_affine(framed_metas, out)
    img_show = display(out, ori_imgs)    # show frame by frame
    cv2.imshow('frame',img_show)    
    if cv2.waitKey(1) & 0xFF == ord('q'): 
        breakcap.release()
cv2.destroyAllWindows()
   
代码解释

期望FPS为4(很慢) YOLOv4在NX上的FPS也差不多

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
好用的视频编辑软件推荐
好用的视频编辑软件推荐

好用的视频编辑软件:1. Final Cut Pro X:适合Mac用户,专业级,配置要求高。2. iMovie:苹果设备自带,适合初学者。3. Adobe Premiere Pro:跨平台,功能强大,适合专业用户。4. DaVinci Resolve:专业调色软件,配置要求高。5. 爱剪辑:适合Windows初学者,功能丰富。6. 威力导演:适合Windows中级用户,支持360度视频编辑。

237

2025.04.15

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

22

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

48

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

93

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

216

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

413

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

143

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

221

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

31

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 4.9万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号