news 2026/10/1 10:56:26

基于Django的交通标志识别系统:从PyTorch模型训练到Web部署全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Django的交通标志识别系统:从PyTorch模型训练到Web部署全流程解析

简介:这套基于深度学习的交通标志识别Django项目源码,面向python开发者和深度学习初学者,解决图片及摄像头实时交通标志识别与分类问题。资源共570个文件,包含Python源码、Django模板、前端样式与JavaScript脚本、YOLOv5模型权重与H5模型以及SQL数据库脚本等,压缩包整体约579MB。目前已有292人学习/浏览,适合作为课程设计或毕业设计的基础项目。系统提供后台登录与用户注册界面,支持对实景图片、笔记本电脑摄像头画面进行交通标志检测,并能分类显示对应标志类型;同时针对像素低模糊、较远距离、雾霾、雨天、黑夜等特殊情形进行了专门适配,检测后的图像可保存另存。资源中附带模型训练权重和美化后的前端界面,目录结构清晰,便于二次开发与算法调参,可快速搭建一套完整的智能交通标志识别应用。

1. 交通标志识别项目为什么值得用 Django 包一层再交付

做过毕设或课程设计的人多半见过这种场面:模型在 notebook 里预测单张图片,准确率漂亮得很;一旦要把它做成一个能上传图片、点按钮、看结果的 Web 系统,问题立刻冒出来——要么模型加载报错,要么识别结果错得离谱。这个 python 深度学习交通标志识别系统配合 Django 源码的典型组合,本质上不是“训练一个 CNN”和“写一个网站”两件事,而是一条完整的交付链路:CNN 负责看清图片里的标志,Django 负责把识别能力变成别人能操作的产品。适合手上已经跑过基础图像分类、但没做过模型部署的开发者,也适合需要交付可演示系统的学生。这套方案最值钱的点不在网络多深,而在训练与部署之间的全链路一致性,以及把模型装进 Web 框架时那些容易翻车的细节。

2. 交通标志识别模型选型:ResNet18 是稳妥基线,盲目堆网络深度不划算

2.1 先分清是“分类”还是“检测”,选型思路完全不同

标题里的“交通标志识别”在大多数实际项目里默认做的是多分类:输入一张已经包含标志的图片,输出它属于哪个类别。GTSRB(德国交通标志数据集)是这类任务最常见的基准,共 43 个类别,训练集四万多张图片,标志在图中位置相对居中,学习难度比一般图像分类任务低。很多初次接触深度学习的人在选模型时会直觉性地选择 VGG16,理由往往是“论文里常见、名气大”。但从部署角度看,VGG16 参数量接近 1.38 亿,推理一张图要做的浮点运算量明显偏高,放在 Django 这种同步处理的 Web 服务里,响应时间会被拉长到用户能感知的程度。

ResNet18 只有约 1100 万参数,残差结构让它在加深网络的同时避免了梯度消失,在 GTSRB 这种规模的数据集上足够拟合,准确率能稳定达到 95% 以上。如果你的任务只有 10 类左右的自采数据,甚至可以考虑 MobileNetV3 这种轻量网络,后续想迁移到嵌入式设备也顺理成章。核心逻辑是:交通标志识别是“小物体分类”,目标显著、背景单一,不需要检测模型里那些复杂的区域提议和锚框机制;分类模型的选型优先级是“先保证能收敛、再考虑速度、最后才去追求更高精度”。

此外,如果未来确实需要从一张包含多个标志的场景图里直接找标志,那属于目标检测范畴,需要换成 Faster R-CNN 或 YOLO,并重新准备带标注框的数据集。两者工作量和难度差距极大——检测模型不仅要判断类别,还要回归边界框,调参的复杂度会翻倍。在动手写代码之前,先把这个边界想清楚,能省掉后面大量返工时间。

2.2 训练脚本骨架:ImageNet 预训练微调,还是从头训?

对于 GTSRB 这种与自然图像差异较大的数据集,一个常见的争论是“要不要用 ImageNet 预训练权重”。我的做法是一律先用预训练权重做微调,除非你有明确理由证明数据量足够大或任务域偏移太严重。预训练权重里包含的纹理、边缘、形状组合特征,对交通标志这种线条简洁、颜色饱和的图像同样有迁移价值。从头训练 ResNet18 不是不行,但要花更多时间调学习率和正则化参数,对项目周期不友好。

下面给一个直接能跑的 PyTorch 训练脚本骨架,数据集按 ImageFolder 结构组织。数据目录格式为 data/gtsrb/train/类别目录/图片文件,验证集同理。

# train_resnet18.py from torchvision import models, transforms, datasets from torch import nn, optim from torch.utils.data import DataLoader transform_train = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) transform_val = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) train_data = datasets.ImageFolder('data/gtsrb/train', transform=transform_train) val_data = datasets.ImageFolder('data/gtsrb/val', transform=transform_val) model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) model.fc = nn.Linear(model.fc.in_features, 43) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=1e-4) scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.5) for epoch in range(15): model.train() for xs, ys in DataLoader(train_data, batch_size=32, shuffle=True): optimizer.zero_grad() loss = criterion(model(xs), ys) loss.backward() optimizer.step() model.eval() correct = total = 0 with torch.no_grad(): for xs, ys in DataLoader(val_data, batch_size=64): preds = model(xs).argmax(dim=1) correct += (preds == ys).sum().item() total += ys.size(0) print(f'epoch {epoch} val acc: {correct / total:.4f}') scheduler.step()

逻辑说明:ImageFolder 会按照子目录名生成类别索引,目录名的字典序直接影响类别编号,推理端的类名映射必须使用同一套编号规则,否则会出现训练对的类别、部署时对不上的问题。Resize 到 224×224 是 ResNet 的标准输入尺寸,RandomRotation 和 ColorJitter 针对交通标志拍摄时常见的角度偏移和光照变化做数据增强。StepLR 每 5 个 epoch 把学习率减半,是一种简单有效的收敛策略。CrossEntropyLoss 内部包含 Softmax 和 One-Hot 处理,不需要手动做标签转换。

参数说明:lr 从 1e-4 开始,不要贪快。batch_size=32 需要大约 4GB 以上显存,如果显存不够,降到 16 或 8 也行,但要注意学习率可能需要相应微调。保存模型时统一用 state_dict,方便后续部署时加载。

2.3 三个必调参数:输入尺寸、类别数、置信度阈值

很多人抄到训练脚本后直接套 224 输入尺寸,其实可以想想 224 是不是必须的。GTSRB 不少原始图片本身只有几十像素,强行放大到 224 会引入插值噪声,但网络输入又拒绝小图,所以这属于“没有选择的选择”。如果你的部署目标是 Web 演示,可以试 192 或 160,推理速度更快,准确率下降通常不超过 1%。类别数目必须是数据集中实际类别数,GTSRB 固定为 43,自采数据则要看目录数。

置信度阈值容易被忽略。训练模型输出的是每个类别的概率,界面显示时如果直接把最大概率当作结果,遇到“看不清的图”就会硬猜一个类别,体验很差。常见做法是在推理封装中加一个阈值判断,比如概率低于 0.6 时返回“无法识别”,引导用户换一张更清晰的图片。这个值需要通过验证集统计来定,不是拍脑袋。观测验证集上错误样本的置信度分布,选一个既能覆盖大多数正确样本、又能排除多数错误预测的临界值。

3. 数据准备实操:GTSRB 官方包转换、自采图片不均衡、验证集划分

3.1 把 GTSRB 原始 CSV 标注转成 ImageFolder 结构

torchvision 的 ImageFolder 要求目录结构是 主目录/类别目录/图片文件.jpg,而 GTSRB 官方训练包里的标注信息在 CSV 文件里,图片以 ppm 格式存储,目录命名也不是标准的类别目录。这种情况下不能直接把数据集丢给训练脚本,需要先做一个转换。整个过程不复杂,但要小心路径拼接和目录创建时的细节。

# convert_gtsrb.py import csv import os import shutil from pathlib import Path src = Path('GTSRB/Final_Training/Images') dst_train = Path('data/gtsrb/train') dst_train.mkdir(parents=True, exist_ok=True) with open('GT-final_train.csv', encoding='utf-8') as f: for row in csv.DictReader(f): rel_path = row['Filename'] cls = row['ClassId'] out_dir = dst_train / cls.zfill(2) out_dir.mkdir(parents=True, exist_ok=True) shutil.copy(src / rel_path, out_dir / Path(rel_path).name)

逻辑说明:csv.DictReader 会按行读取并把列名映射成字典键,取 Filename 字段拿到相对路径,取 ClassId 作为类别编号。用 str.zfill(2) 把 0 变成 00、9 变成 09,是为了让目录名的字典序和数字序一致。ImageFolder 内部用 sorted 排序目录名,如果不补零,类别 10 会排在类别 2 前面,类别编号就错位了。shutil.copy 保留原始 ppm 文件,后续 transforms 会统一处理尺寸和格式,这里不做额外压缩。

3.2 自采图片不均衡的解法:WeightedRandomSampler 和增强策略

如果你不满足于只在 GTSRB 上训练,而要加入自己拍的交通标志照片,大概率会遇到样本不均衡:网上能找到的“禁止驶入”图片很多,“限速 80”的图片却很少。模型学到的是多数类的统计特征,少数类基本被忽略,测试时一旦上传这类图片,识别结果几乎必错。常见的解决方法是给每个样本设置采样权重,让少数类有更高概率被抽到。

from torch.utils.data import WeightedRandomSampler import numpy as np targets = [y for _, y in train_data.samples] counts = np.bincount(targets) weights = 1.0 / counts[targets] sampler = WeightedRandomSampler(weights, len(weights), replacement=True) # DataLoader 中替换 shuffle 参数 train_loader = DataLoader(train_data, batch_size=32, sampler=sampler)

参数说明:weights 数组的每个元素对应一个样本,少数类样本的权重更大,被抽中的概率更高。replacement=True 表示允许同一张图在一个 epoch 内重复出现,和简单复制图片相比,这种方式配合 RandomRotation、ColorJitter 等增强,相当于每次采样时图片都有不同的扰动,模型不会死记硬背同一张图。需要注意的是,使用 sampler 后 DataLoader 的 shuffle 参数必须设为 False,两者冲突。

数据增强还可以加入 RandomResizedCrop,模拟不同距离拍摄时标志占画面比例不同的问题。但要控制幅度,比如 scale 范围设成 (0.7, 1.0),避免把标志切出画面。GTSRB 原始图片有些已经很小,增强幅度过大会导致有效信息丢失。

3.3 验证集怎么切才不虚高:按来源切,不按文件随机切

很多人在自采数据上随机按 8:2 切训练集和验证集,结果验证集准确率很高,一上线实际照片就露馅。原因是自采照片通常来自连续拍摄的几段视频,相邻帧之间几乎一模一样,随机切分会把同一场景的相似帧同时分到训练集和验证集,验证集失去评估意义。正确做法是按“来源”切,比如按采集时间或按文件夹整体划分,保证验证集里的图片来自模型从未见过的场景。GTSRB 官方包自带的 train/test 划分基本没问题,但自采数据一定要做这一层处理。

另一个值得注意的点是:GTSRB 的测试集标注格式和训练集不同,如果用 csv 方式读取测试集,别忘了同样做格式转换,并保证类别编号与训练时一致。很多人训练脚本跑得很顺,评估时因为测试集格式不一致,读取错误或类别错位,白白浪费时间。

4. Django 调用 PyTorch 模型:从文件上传到返回识别结果的一整条链路

4.1 最小项目结构:模型代码、推理封装和视图分离

Django 集成深度学习模型,最忌讳的是把模型加载、预处理、推理逻辑全部堆在 views.py 里。代码会变得无法调试。推荐的项目结构是在 app 下建一个 ml 目录,把网络定义和推理封装独立出来。这里给出一个最小可跑结构。

traffic_sign/ manage.py config/ # Django 项目配置 recognition/ # 识别 app views.py urls.py ml/ model.py # 网络定义和类名映射 predictor.py # 预处理 + 推理封装 templates/ recognition/ index.html

标题里带“源码”两个字,读者拿到项目后第一个想知道的就是每个文件负责什么。这种结构让模型训练代码和 Web 代码互相独立,训练时跑出的权重文件 weights.pth 放在 ml 目录下,views.py 只调用 predictor.predict 函数,不关心模型内部结构。后续想换更强的网络,只需要改 model.py 和重新训练权重,Web 层基本不用动。

4.2 推理封装:模型只加载一次,预处理链路要和训练一致

模型加载的位置和时机是部署中最容易被忽视的细节。如果每次请求都执行一次 torch.load,页面的响应时间会达到秒级,而且内存和显存可能被反复吃满。正确的做法是把模型作为模块级变量,进程启动时懒加载一次,后续请求直接复用。同时,预处理必须和训练时的验证集链路完全一致,否则识别性能会断崖式下跌。

# recognition/ml/predictor.py import io import torch from PIL import Image from torchvision import transforms from recognition.ml.model import create_model _device = torch.device('cpu') _model = None _normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) def get_model(): global _model if _model is None: _model = create_model(num_classes=43) state = torch.load('recognition/ml/weights.pth', map_location=_device) _model.load_state_dict(state) _model.eval() return _model def predict(image_bytes: bytes): img = Image.open(io.BytesIO(image_bytes)).convert('RGB') img = img.resize((224, 224)) t = transforms.ToTensor()(img) t = _normalize(t).unsqueeze(0) with torch.no_grad(): logits = get_model()(t) prob = torch.softmax(logits, dim=1)[0] idx = int(prob.argmax()) return idx, float(prob[idx])

逻辑说明:map_location='cpu' 保证了训练时用 GPU、部署机器只有 CPU 的情况下也能正常加载权重。PIL 打开图片后必须 convert('RGB'),因为手机照片可能是 RGBA 模式或灰度图,网络输入要求三通道。推理时用 no_grad() 关闭梯度计算,显著降低显存占用并提高速度。unsqueeze(0) 把形状为 (3, 224, 224) 的张量变成 (1, 3, 224, 224),补上 batch 维。注意推理时的 transforms 里没有 RandomRotation,因为验证和推理阶段都不应该引入随机扰动。

4.3 视图函数与模板渲染:处理上传图片、返回结果

视图函数接收前端上传的图片字节,调用 predictor,把类别编号映射为可读的名称,连同置信度一起渲染到页面。一个关键技术点是使用 base64 内嵌图片,省去单独保存上传文件到 media 目录的步骤,但也意味着每个响应都会放大 1.3 倍左右的体积,适合本地演示,不适合高并发场景。

# recognition/views.py import base64 from django.shortcuts import render from recognition.ml.predictor import predict def index(request): result = None if request.method == 'POST' and request.FILES.get('image'): try: image_bytes = request.FILES['image'].read() idx, conf = predict(image_bytes) result = { 'category': CLASS_NAMES[idx], 'confidence': round(conf * 100, 2), 'image_url': 'data:image/png;base64,' + base64.b64encode(image_bytes).decode() } except Exception: result = {'category': '图片解析失败', 'confidence': 0} return render(request, 'recognition/index.html', {'result': result})

参数说明:request.FILES 只有在表单 enctype="multipart/form-data" 且字段名匹配时才有值,前端模板漏写 enctype 是最常见的问题。base64.b64encode 得到的是 bytes,需要 decode 成字符串才能拼接 URL。整个 try/except 是兜底逻辑,上传损坏文件时返回可读的错误提示而不是 500 白屏。

4.4 大图处理策略:限制最长边,稳定推理耗时

手机相册里的照片通常在 3000×4000 像素左右,直接 resize 到 224 需要做大量插值计算,推理时间会从几十毫秒涨到几百毫秒,而且高分辨率带来的高频细节可能干扰模型判断。常见做法是在预处理时先限制最长边。例如用以下逻辑代替直接的 img.resize:

max_side = 1024 w, h = img.size scale = min(1.0, max_side / max(w, h)) img = img.resize((int(w * scale), int(h * scale))) img = img.resize((224, 224))

这样做的好处是:小图不会被无意义放大,大图先被等比压缩到最长边 1024 以内再做统一缩放,有效控制推理耗时和内存波动。这个细节在实际部署中很实用,用户传一张 5MB 的照片和传一张 50KB 的照片,如果不限制尺寸,响应时间会差距悬殊,体验不一致。

5. 交通标志识别系统从训练到部署的五个典型坑:现象、原因和补救

5.1 训练时准确率 98%,部署后上传图片识别结果全错

现象:在 GTSRB 测试集上验证指标很高,换成自己拍的手机照片后几乎全错。

原因:通常是两类问题叠加。一是模型学到的主要是 GTSRB 图像本身的统计特征,真实场景存在反光、遮挡、视角倾斜等干扰,泛化能力不足;二是预处理链路不一致,比如训练时用了 ColorJitter 和 RandomRotation,部署时的 transforms 里却加了额外的灰度化或忘了归一化。

解决:先检查训练和部署两端的预处理代码是否完全一致,尤其是 Normalize 的均值和标准差。然后逐步把自采图片以增强方式混入训练集,让模型见过更真实的变化。如果自采图片只有几十张,优先用 ColorJitter 和 RandomRotation 生成变体,而不是直接复制原图。

5.2 服务启动后第一张图就报 CUDA Runtime Error

现象:runserver 启动一切正常,上传图片后日志报 CUDA out of memory 或 no kernel image available。

原因:训练机器是 GPU 环境,权重保存时带有 GPU 相关的结构信息,部署机器是纯 CPU 环境,torch.load 默认尝试加载到 CUDA 设备上,自然失败。

解决:加载权重时显式写 map_location=torch.device('cpu')。如果仍然报错,检查保存权重时用的是 torch.save(model.state_dict()) 而不是 torch.save(model)。后者会把整个模型对象连同设备信息一起序列化,部署时更容易出现兼容性问题。统一保存 state_dict 是更稳妥的做法。

5.3 上传图片返回 500,日志提示 cannot identify image file

现象:前端能选图,一提交页面就报错,后台日志显示 PIL 无法打开文件。

原因:Pillow 对不同格式的支持不统一,某些老版本对 WebP 或 BMP 的支持不完整,或者用户上传的文件本身是伪造扩展名的非图片文件。

解决:在 predict 函数里用 try/except 包住 Image.open,捕获异常后返回“图片解析失败”的提示。同时升级 Pillow 到较新版本,并把前端上传控件的 accept 限制为常见的 jpg、png、jpeg 后缀。这个坑在 Windows 本地开发时尤其频繁,因为 Windows 对文件扩展名的大小写不敏感,.JPG 和 .jpg 在部分环境下会导致解析异常。

5.4 验证集准确率不增反降,怎么调参都救不回来

现象:前几个 epoch 正常,后面验证集准确率越训越低,训练集准确率却依旧很高。

原因:过拟合已经发生,但训练脚本没有早停机制,也没有在每轮结束时保留最优权重。很多人训练完直接用最后一个 epoch 的权重,等于把最差的结果用于部署。

解决:每轮评估后记录当前最优验证准确率,并保存对应的权重。训练结束后用最优权重替换权重文件。代码上只需加几行判断:

best_acc = 0 for epoch in range(epochs): # 训练与评估代码省略 if val_acc > best_acc: best_acc = val_acc torch.save(model.state_dict(), 'best.pth')

5.5 页面显示的类别名称是问号或乱码

现象:模板里输出的中文类别名变成 “????” 或者类似 \u4e2d 的原始转义字符。

原因:字符串本身是正常的,问题出在模板文件编码或 Django 响应编码。模板文件如果保存为 ANSI 编码,内含中文时浏览器按 UTF-8 解析就会乱码。

解决:在模板文件顶部加上 {% load static %} 并在 head 里写 meta charset="utf-8",同时确保编辑器保存模板时使用 UTF-8 编码。类名映射如果用 python 字典定义,注意文件也要保存为 UTF-8。这个问题在 Windows 本地开发时非常典型,迁移到 Linux 服务器后通常自动消失。

6. 上线前用混淆矩阵和 Grad-CAM 做验证,比只看准确率更靠谱

准确率只能告诉你“整体对了几成”,不能告诉你“哪些类别经常互相认错”。交通标志里,“限速 40”和“限速 30”、“禁止停车”和“禁止长时间停车”这类外观相近的类别,是混淆的高发区。跑一遍混淆矩阵,能直观看到模型在哪些类对之间犹豫。做法很简单:加载最优权重,遍历验证集,把真实标签和预测标签记录成两个列表,然后用 sklearn 的 confusion_matrix 生成矩阵,再配合 seaborn 画热力图。如果发现固定有几对类别互混严重,优先考虑补充这两个类别的训练数据,而不是盲目加深网络。

Grad-CAM 类激活热力图能告诉你模型“看的是哪里”。对交通标志分类这种任务,理想情况下热力图应该集中在标志中央的图形区域,而不是背景或边缘。如果热力图大面积覆盖背景,说明模型学到了背景相关性,也就是常说的捷径学习,换一个场景就会失效。一个简单的验证方法是:把同一张标志图放在纯白背景和纯黑背景上分别测试,如果置信度波动巨大,说明模型对背景过度敏感,需要回到训练环节加背景扰动增强。

我的个人习惯是,每次训练完第一件事不是看准确率,而是随机挑 20 张验证图,逐张人工检查预测结果和置信度,对那些置信度很高但预测错误的样本特别关注——它们往往就是模型提取特征的盲区。这类样本积累到 30 张左右,就值得把它们加入训练集做二次微调。这套验证流程走一遍,项目能不能演示、敢不敢交给别人试用,心里基本有数了。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 10:55:43

VSCode 调试配置实战:launch.json 与多文件断点排错指南

1. 调试的第一步:搞懂 VSCode 的调试器到底在执行什么很多人装上 VSCode、配好编译器,兴冲冲写了第一行print("Hello World"),然后信心满满地按下 F5,结果屏幕上弹出一个从未见过的launch.json文件,里面一堆…

作者头像 李华
网站建设 2026/10/1 10:55:30

Linux上部署Redis全攻略:从源码编译到Docker主从与调优避坑

在Linux上把Redis跑起来,看着就是个apt install或者解压make的事,但真到了2026年,这事情里的门道其实越来越多。Redis早已不是当年那个只做缓存的KV数据库,数据类型、分布式锁、缓存治理、监控排障一套下来,部署方式的…

作者头像 李华
网站建设 2026/10/1 10:55:28

微服务链路追踪实战:Sleuth+Zipkin从零部署与避坑指南

1. 为什么微服务系统里,一个HTTP请求进来后就“失踪”了?你有没有遇到过这样的场景:用户在前端点了个提交按钮,页面转圈三秒后弹出“系统繁忙”,但后端所有服务的日志里都找不到这条请求的完整踪迹?A服务说…

作者头像 李华
网站建设 2026/10/1 10:54:31

Git 基本使用完全指南:从工作区模型到团队协作避坑

我自己刚开始用 Git 的时候,其实是被吓到的——满屏的 fatal 、 error ,网上搜到的命令又各自为政,仿佛每篇教程都在教一个不同的 Git。后来带过几波新人,又帮同事救过好几次仓库之后,我才慢慢摸清楚一件事&#x…

作者头像 李华
网站建设 2026/10/1 10:53:50

Linux网络编程必会:Wireshark抓包实战与TCP排查技巧

搞Linux网络编程,Wireshark 这个抓包工具是怎么都绕不开的。它强在哪?不是能抓多少包,而是抓完之后你能把 TCP 连接的每一次握手、每一段数据、每一个重传都看得清清楚楚。我自己这些年做 Linux 下的通信程序、排查线上连接问题,几…

作者头像 李华
网站建设 2026/10/1 10:53:49

PyTorch深度学习实战:从环境搭建到CNN模型构建

如果你正打算把深度学习这块骨头啃下来,那PyTorch基本是你绕不开的第一站。这几年不管是逛GitHub、看论文开源代码还是刷技术博客,十有八九都能看到PyTorch的身影。但我也见过太多人卡在第一步:Anaconda装好了,PyTorch也装上了&am…

作者头像 李华