news 2026/9/1 6:26:26

MobileNetV3核心组件逐模块拆解与PyTorch实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MobileNetV3核心组件逐模块拆解与PyTorch实战

简介:面向深度学习从业者与研究人员,MobileNetV3 完整 PyTorch 实现资源包聚焦轻量级网络在计算机视觉中的应用,涵盖模型搭建、预训练权重、训练日志、推理测试与效率评估等环节,适合用于学习 MobileNetV3 架构细节并快速开展图像分类实验。资源包为 zip 格式,共 24 个文件、约 58.72MB,包含 4 个 .pth 预训练模型文件、4 个 .log 训练过程日志、3 个 .py 脚本、3 张 .jpeg 测试图片、2 个 .txt 文本文件,以及若干项目配置与说明文件。其中 mobilenetv3.py 与 run_pred_mobilenet.py 分别负责网络定义和推理入口,flops.py 可辅助计算模型计算量;imagenet_classes.txt 提供分类标签,日志文件记录 300/450 轮训练曲线,便于分析训练效果。资源附带猫、狗、鸟等真实测试图片,下载后即可加载权重体验分类推理。目前已有 657 人浏览学习,对于希望深入理解轻量级网络实现、需要可直接运行的基准代码的读者来说,具备较高的参考价值。

1. 从MobileNetV1到V3:轻量网络的进化逻辑

1.1 深度可分离卷积:V1打的底子

要真正理解MobileNetV3,不能跳过它的前两代。2017年MobileNetV1提出时,核心就一句话:把标准卷积拆成两步走。标准卷积在提取特征时,既要考虑通道间的关系,又要考虑空间位置的关系,计算量是 H×W×C_in×C_out×K×K。而深度可分离卷积把这个过程拆成了深度卷积(Depthwise Convolution)和逐点卷积(Pointwise Convolution),深度卷积只在每个通道内部做K×K的卷积核滑动,逐点卷积再用1×1卷积把通道信息融合起来。直观感受一下,标准卷积96个单位算完的事,拆开后只需要十几个单位就能完成,计算量大约下降到原来的八分之一到九分之一,具体取决于卷积核大小和通道数。

MobileNetV1用两个超参数控制网络的胖瘦和输入分辨率:宽度乘子α和分辨率乘子ρ。α负责把每层的通道数按比例缩放(典型值0.25、0.5、0.75、1.0),ρ负责缩放输入图像尺寸。这套组合拳让MobileNetV1可以按照不同硬件算力快速裁剪出合适的子网络,从高算力GPU一路覆盖到嵌入式MCU。

1.2 V2的线性瓶颈与倒残差

V2的改进点从MobileNetV1的实测结果中来:深度卷积本身不改变通道数,如果通道很窄,经过ReLU激活后特征信息会被大量冲掉。研究者在实验中发现,低维特征图经过ReLU之后,很多通道直接变成了死区,信息不可恢复。于是V2引入了倒残差结构(Inverted Residual Block):先通过1×1卷积把低维特征升维到高维(一般是6倍),在高维空间做深度卷积提取特征,再通过线性变换降维回低维,这个降维操作不再接ReLU,而是纯线性映射。

这就是著名的Linear Bottleneck。为什么升维?因为ReLU在高维空间造成的特征损失相对小,信息不容易被破坏。为什么最后不加ReLU?因为降维后特征已经很紧凑了,再经过非线性激活可能破坏已经提取好的信息。V2的瓶颈结构从视觉上看是一个两头小、中间大的纺锤形,而传统的残差块是个两头大、中间小的沙漏形,所以叫倒残差。这个结构直接奠定了V3的基本骨架,V3的几个核心组件全都是围绕这个骨架做优化。

1.3 V3做了什么:NAS搜索、NetAdapt与手工微调

V3的诞生背后是神经网络架构搜索(NAS)的成熟。V3的论文名字里就带着“Searching for MobileNetV3”,它的block配置不是人手工拍脑袋排的,而是通过NAS自动搜索出来的。具体分两个阶段:先用平台感知的NAS在计算量约束下搜索网络的整体结构(每个block用多少个通道、多少层、用不用SE模块),再用NetAdapt算法对搜索出来的结构精细化调整每层的卷积核数量。最后作者在V2的代码框架上做了几处人工干预,重点就是两个:引入h-swish激活函数和SE通道注意力模块。

需要强调一点,V3并不是纯粹让NAS黑箱跑出来的,作者做了大量统计分析,比如发现搜索出来的网络在网络的浅层普遍倾向使用传统卷积而不是深度卷积,因为浅层往往只有几个通道,深度卷积收益不明显;比如SE模块放在某些靠后的层收益更大。这些人工微调和分析,才是V3在精度和速度上能够同时全面超越V2的关键。所以读V3时千万别只背配置表,要理解每一层为什么放在那里,这样你自己写代码、改结构的时候才有依据。

2. MobileNetV3的核心组件逐模块拆解

2.1 h-swish激活函数:低开销版的Swish

Swish激活函数是Google量子团队在2017年提出的,公式是 x·sigmoid(x)。实测发现Swish在深层模型上精度优于ReLU,但sigmoid的计算开销在移动端不可接受——它涉及指数、除法和加法等浮点运算,在手机上跑要比ReLU贵一大截。MobileNetV3的解决办法是“硬”版本,用ReLU6来近似sigmoid,公式如下:

[ h\text{-}swish(x) = x \cdot \frac{ReLU6(x+3)}{6} ]

因为ReLU6本身只是clip操作,运算只有一次加法和一次乘法,比sigmoid便宜得多。实测下来h-swish和原版Swish的精度差距在零点几个百分点以内,但速度优势明显,在骁龙系列等移动处理器上可以节省约20%的激活层耗时。作者的测试结果显示,h-swish放在网络后半段效果更好,放在浅层反而可能产生负面影响——这也是为什么V3只在网络的第7层开始才使用h-swish,浅层仍然保持ReLU。这点在后续实现中要特别注意,不是全网络无脑换激活函数。

在PyTorch里,h-swish实现起来非常简洁,而且PyTorch的ReLU6本身有CUDA上的高效实现,逐行代码如下:

class HSwish(nn.Module): """h-swish激活函数: x * relu6(x+3) / 6 相比原版swish, 用relu6近似sigmoid, 避免指数运算, 移动端友好 """ def __init__(self, inplace: bool = True): super(HSwish, self).__init__() self.inplace = inplace def forward(self, x: torch.Tensor) -> torch.Tensor: return x * F.relu6(x + 3.0, inplace=self.inplace) / 6.0

2.2 SE注意力模块:让网络学会“挑重点”

SE模块是SENet提出的通道注意力机制,做法不复杂:对特征图的每个通道做全局平均池化,得到一个长度为C的向量,然后经过两个全连接层,第一层降维(通常降到C的1/4),第二层升维回C,经过sigmoid得到一个0到1之间的逐通道权重,最后把这个权重乘回原特征图。直觉上可以理解为:网络自己判断每个通道的重要程度,有用的通道放大,没用的通道压小。

在轻量网络中加SE,作者一开始是有顾虑的——两个全连接层虽然参数量不大,但运算开销是实打实的。特别是SE模块通常放在Bneck块内部,会打断原有的流水线。不过V3的实验结果表明,在合适的层加入SE之后,精度提升明显大于那点速度损失。另外还有一个细节:SE模块在V3中使用的降维比例不是统一的1/4,作者在部分层用了1/8,这部分在代码里要根据配置灵活处理。标准SE实现如下:

class SqueezeExcitation(nn.Module): """SE通道注意力模块 参数: in_channels: 输入通道数 se_ratio: 隐藏层降维比例, 默认0.25 """ def __init__(self, in_channels: int, se_ratio: float = 0.25): super().__init__() hidden_channels = max(1, int(in_channels * se_ratio)) # 全局平均池化: 聚合空间信息 self.pool = nn.AdaptiveAvgPool2d(1) # 两个1x1卷积等价于两个全连接层, 1x1卷积可以保持特征图的空间维度, 效率更高 self.fc1 = nn.Conv2d(in_channels, hidden_channels, kernel_size=1, bias=True) self.fc2 = nn.Conv2d(hidden_channels, in_channels, kernel_size=1, bias=True) def forward(self, x: torch.Tensor) -> torch.Tensor: b, c, _, _ = x.size() # 空间压缩 -> 全连接降维 -> ReLU -> 全连接升维 -> sigmoid加权 out = self.pool(x).view(b, c, 1, 1) out = self.fc1(out) out = F.relu(out, inplace=True) out = self.fc2(out) out = F.sigmoid(out) # 权重范围[0,1] return x * out.expand_as(x)

2.3 Bneck瓶颈残差块的完整结构

Bneck是MobileNetV3的基本构成单元,它把前面提到的所有组件缝合在一起。一个完整Bneck块的结构从输入到输出依次是:

  1. 1×1扩展卷积(只有当扩展比例大于1时才存在),把低维输入升到expanded_channel维,接BN和激活函数;
  2. 3×3或5×5深度卷积,接BN和激活函数,步长可为1或2,步长为2时用于下采样;
  3. SE模块(按配置决定是否加),在深度卷积之后、逐点卷积之前插入;
  4. 1×1逐点卷积,把高维特征压回输出通道数,接BN,不加激活函数;
  5. 残差连接:步长为1且输入输出通道相等时,把原始输入和输出相加。

为什么要按这个顺序排列?深度卷积之后紧接SE,是因为SE是在空间特征已经充分提取之后重新校准通道权重,效果最好。如果把SE放到扩展卷积之后,输入特征被放大到高维空间,SE需要处理的通道数多,但空间信息还没经过深度卷积提取,注意力校准的意义不大。V2的经验告诉我们在逐点卷积之后不加激活函数,所以Bneck的最后一层保持线性输出。

代码实现如下:

class Bneck(nn.Module): """MobileNetV3的瓶颈残差块 参数: in_channels: 输入通道数 out_channels: 输出通道数 kernel_size: 深度卷积核大小, 3或5 stride: 深度卷积步长, 1或2 expand_ratio: 扩展比例, 决定了中间升维后的通道数 se_ratio: SE模块降维比例, 0表示不添加SE act_layer: 激活函数类型, 'relu'或'hs'表示h-swish use_hs: 是否使用h-swish激活 """ def __init__(self, in_channels, out_channels, kernel_size, stride, expand_ratio, se_ratio=0.25, use_hs=False): super().__init__() self.stride = stride hidden_channels = in_channels * expand_ratio self.use_shortcut = (stride == 1 and in_channels == out_channels) # 当expand_ratio为1时, 跳过扩展卷积, 直接进入深度卷积 if expand_ratio != 1: self.conv_expand = nn.Conv2d(in_channels, hidden_channels, kernel_size=1, bias=False) self.bn_expand = nn.BatchNorm2d(hidden_channels) # 深度卷积 self.conv_depthwise = nn.Conv2d(hidden_channels, hidden_channels, kernel_size=kernel_size, stride=stride, padding=kernel_size // 2, groups=hidden_channels, bias=False) self.bn_depthwise = nn.BatchNorm2d(hidden_channels) # SE模块, 当se_ratio不为None时启用 self.se = None if se_ratio is not None and se_ratio > 0: self.se = SqueezeExcitation(hidden_channels, se_ratio) # 逐点压缩卷积 self.conv_project = nn.Conv2d(hidden_channels, out_channels, kernel_size=1, bias=False) self.bn_project = nn.BatchNorm2d(out_channels) # 激活函数选择 self.act_layer = HSwish() if use_hs else nn.ReLU(inplace=True) def forward(self, x): identity = x if hasattr(self, 'conv_expand'): x = self.bn_expand(self.conv_expand(x)) x = self.act_layer(x) x = self.bn_depthwise(self.conv_depthwise(x)) x = self.act_layer(x) if self.se is not None: x = self.se(x) x = self.bn_project(self.conv_project(x)) if self.use_shortcut: x = x + identity return x

3. PyTorch代码实现与逐行解析

3.1 完整网络模型实现

MobileNetV3在论文中给出了Large和Small两个版本配置,分别面向高算力和低算力场景。我把配置文件直接用Python列表形式写出来,每组参数对应一个Bneck块。相比用字典定义再解析的方式,直接用列表更直观,可读性更好,也方便逐个层检查。配置格式定义为(扩展比例, 输出通道数, 注意力配置, 深度卷积核大小, 步长),用se0.25表示SE的降维比率,None表示不添加SE,激活函数单独用列表控制。网络前面必须加一个标准的3×3卷积层做初始下采样(stride=2),中间所有下采样都通过Bneck块内部的stride=2深度卷积完成,最后接全局平均池化和分类头。

核心配置和主干代码:

def _make_divisible(v, divisor=8, min_value=None): """将通道数向上取整到divisor的倍数, 保证硬件友好性 移动端处理器对8的倍数通道数有更好的内存对齐效率 """ if min_value is None: min_value = divisor new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) # 确保向下取整后不低于原值的90%, 防止通道数被过度缩减 if new_v < 0.9 * v: new_v += divisor return new_v class MobileNetV3(nn.Module): """MobileNetV3网络主干 参数: cfg: 配置块列表, 见下方Large配置定义 num_classes: 分类类别数 width_mult: 全局宽度乘子, 用于通道数缩放 """ def __init__(self, cfg, num_classes=1000, width_mult=1.0): super().__init__() # 初始卷积层, 输入3通道 -> 16通道, stride=2下采样 in_channels = _make_divisible(16 * width_mult) self.conv_stem = nn.Conv2d(3, in_channels, kernel_size=3, stride=2, padding=1, bias=False) self.bn_stem = nn.BatchNorm2d(in_channels) self.act_stem = HSwish() # 根据配置逐层堆叠Bneck块 layers = [] for layer_cfg in cfg: expand_ratio, out_channels, se_ratio, kernel_size, stride = layer_cfg out_channels = _make_divisible(out_channels * width_mult) layers.append(Bneck( in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, expand_ratio=expand_ratio, se_ratio=se_ratio, use_hs=(in_channels >= 40) # 论文: h-swish仅用于通道数>=40的层 )) in_channels = out_channels self.blocks = nn.Sequential(*layers) # 最后一层扩展卷积, 提升特征维度再分类 last_conv_out = _make_divisible(960 * width_mult) self.last_conv = nn.Conv2d(in_channels, last_conv_out, kernel_size=1, bias=False) self.last_conv_bn = nn.BatchNorm2d(last_conv_out) self.last_conv_act = HSwish() self.classifier = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Dropout(0.2), nn.Linear(last_conv_out, num_classes), ) self._initialize_weights() def forward(self, x): x = self.act_stem(self.bn_stem(self.conv_stem(x))) x = self.blocks(x) x = self.last_conv_act(self.last_conv_bn(self.last_conv(x))) x = self.classifier(x) return x def _initialize_weights(self): """轻量网络对初始化敏感, 采用官方推荐的初始化策略""" for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') if m.bias is not None: nn.init.zeros_(m.bias) elif isinstance(m, nn.BatchNorm2d): nn.init.ones_(m.weight) nn.init.zeros_(m.bias) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.zeros_(m.bias)

3.2 Large与Small配置表

论文中的配置表我这里直接保留原始数据,但需要注意的是,第一行16表示的是stem卷积的输出通道,不是标准Bneck配置,在代码里已经单独处理了权重初始化了。原始配置中的注意力模块列我用se0.25形式表示,方便代码解析。

def mobilenetv3_large(num_classes=1000, width_mult=1.0): """MobileNetV3-Large, 适合算力相对充裕的场景, 比如旗舰手机或Jetson系列""" cfg = [ # expand_ratio, out_channels, se_ratio, kernel_size, stride (1, 16, None, 3, 1), (4, 24, None, 3, 2), (3, 24, None, 3, 1), (3, 40, 'se0.25', 5, 2), (3, 40, 'se0.25', 5, 1), (3, 40, 'se0.25', 5, 1), (6, 80, None, 3, 2), (2.5, 80, None, 3, 1), (2.3, 80, None, 3, 1), (2.3, 80, None, 3, 1), (6, 112, 'se0.25', 3, 1), (6, 112, 'se0.25', 3, 1), (6, 160, 'se0.25', 5, 2), # 注意: 最后的stride=2下采样, 全局池化前的小分辨率 (6, 160, 'se0.25', 5, 1), (6, 160, 'se0.25', 5, 1), ] return MobileNetV3(cfg, num_classes, width_mult)

有一个坑要提醒:原始论文配置表中,最后一层Bneck的stride是2,意味着在网络很深的位置还有一个下采样,这样到分类层时特征图大小为7×7(输入224)。印象中很多GitHub开源实现把这一层的stride改为1,因为大部分分类任务在7×7全局池化和在14×14全局池化上的精度差异极小,而后者参数更少。如果要和论文数据对齐,建议保留stride=2。

def mobilenetv3_small(num_classes=1000, width_mult=1.0): """MobileNetV3-Small, 适合低算力场景, 如MCU或低端嵌入式设备""" cfg = [ (1, 16, 'se0.25', 3, 2), (4.5, 24, None, 3, 2), (3.67, 24, None, 3, 1), (4, 40, 'se0.25', 5, 2), (6, 40, 'se0.25', 5, 1), (6, 40, 'se0.25', 5, 1), (3, 48, 'se0.25', 5, 1), (3, 48, 'se0.25', 5, 1), (6, 96, 'se0.25', 5, 2), (6, 96, 'se0.25', 5, 1), (6, 96, 'se0.25', 5, 1), ] return MobileNetV3(cfg, num_classes, width_mult)

3.3 前向传播细节与参数量统计

搭建完成后最好立刻做一次参数验证,确认模型结构合理。用下面的代码可以快速算出参数量和理论FLOPs,并打印每一层的输出张量尺寸,方便排查维度问题:

import torch from torchinfo import summary model = mobilenetv3_large(num_classes=1000) model.eval() summary(model, input_size=(1, 3, 224, 224))

MobileNetV3-Large在width_mult=1.0时,参数量大约548万(5.48M),FLOPs约为2.2G。可以看到相比于ResNet50的25.5M参数和4.1G FLOPs,计算量压缩得非常明显。实际测试中,如果只跑分类且不要求Top-5精度到99%的级别,这类轻量模型完全可以在树莓派4上实时运行。

宽度乘子width_mult是调参利器,想进一步压缩模型时可以直接把width_mult设为0.75或0.5。实测在不少小型分类任务上,0.5倍宽度的V3-Small仍然有可用的精度,参数量直接降到130万以下,非常适合在STM32MP1之类的主控上跑。

4. 训练、迁移学习与部署经验

4.1 训练配置与迁移学习要点

MNV3的PyTorch实现拿来即用,但训练时有不少细节容易踩坑。官方在ImageNet上训练时采用RMSProp优化器(初始学习率0.1,权重衰减1e-5),但在自己的小数据集上,直接用AdamW配合余弦退火学习率更省心。如果你要迁移到自己的分类数据集,建议加载PyTorch官方预训练权重,然后把分类头替换成自己任务的类别数,只微调分类头训练几个epoch,等loss收敛后再解冻全部层用更小的学习率(1e-4左右)微调整个网络。

import torch.nn as nn from torchvision.models import mobilenet_v3_large # 加载官方预训练权重 model = mobilenet_v3_large(weights='MobileNet_V3_Large_Weights.DEFAULT') # 替换分类头为自己的任务, 例如生物分类5类 num_classes = 5 model.classifier[3] = nn.Linear(model.classifier[3].in_features, num_classes) # 冻结特征提取层, 先只训练分类器 for param in model.features.parameters(): param.requires_grad = False # 只更新分类头和新增层 optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-3)

有一个值得留意的现象:MobileNetV3的BN层在迁移学习时默认统计的是当前batch的均值和方差,不会受冻结影响。但如果冻结了特征层还套用BN的预训练running_mean,实际batch size又特别小,反而容易导致训练不稳定。建议冻结阶段把BN层的track_running_stats保持原样,不做额外修改,因为PyTorch在requires_grad=False时仍然维护BN统计量,不会引发问题。

4.2 不同输入分辨率下SE模块的性能表现

SE模块的性能跟输入分辨率强相关。在224×224输入下,SE的全局平均池化把空间信息压缩到1×1,各通道的注意力权重实际上是整张图的全局统计。当分辨率降低到96×96甚至64×64时,SE在高分辨率下学到的通道重要性分布可能不再适用——因为空间信息变少,通道间的语义差异也变化了。我的实测经验是:如果训练和推理分辨率不一致,建议重新微调SE模块所在的层,单独设置较大的学习率。另一个有效做法是直接在低分辨率训练整个网络,因为MNV3在低分辨率下训练收敛速度明显更快,而精度损失在多数任务中只有1-3个百分点。

5. 踩坑记录与调试技巧实录

5.1 常见问题速查表

下面是我在复现MobileNetV3过程中实际遇到并解决过的问题,整理成表格方便对照排查:

问题现象可能原因解决方案
训练时loss不下降分类头初始化不当分类头用正态分布(mean=0, std=0.01)初始化,别直接用默认的均匀分布
验证精度比训练低很多Dropout比例在超大模型上不合适低数据量任务把Dropout从0.2降到0.1或0.05
推理速度比MobileNetV2还慢h-swish和SE在CPU上未做算子融合部署时改用C++ LibTorch或ONNX Runtime,PyTorch动态图在移动端有额外开销
显存占用比预期高BN层在推理模式下仍保留训练缓冲区model.eval()后调用torch.no_grad(),并检查是否有model.train()残留
微调loss不收敛学习率设置过高预训练权重微调时初始学习率不超过1e-4,全局微调用1e-4~5e-4
不同batch size下精度波动大BN对batch size敏感小batch训练时把BN默认的momentum从0.1减小到0.01

5.2 我最想提醒的几个细节

第一,_make_divisible函数一定不要省。MNV3配置表里的通道数看起来都是整齐的数字,但在width_mult不是1的时候,直接取整会导致硬件利用率下降。比如width_mult=0.5时,40×0.5=20,而20不是8的倍数,在有些边缘设备的NEON指令集中会产生额外的padding计算。加上这个约束函数,模型可能多0.1%的参数量,但推理速度能提升3-5%。

第二,MNV3的SE模块放在深度卷积之后和逐点卷积之前,这个顺序非常讲究。踩过一次坑,把SE放到了逐点卷积之后,精度掉了一个多点,参数量完全相同。原因在2.3小节说过:逐点卷积是特征压缩过程,压缩后的通道是“浓缩信息”,此时再按通道加权就晚了。

第三,如果你要在自己的数据集上做蒸馏或者对比实验,建议直接用PyTorch官方torchvision提供的版本,因为它和timm等第三方实现的性能基本一致,但接口更规范、依赖更少。而Google的官方老仓库对应的是TensorFlow实现,两者的BN策略和dropout位置有细微差别。

6. 模型扩展思路与个人实测心得

MobileNetV3的基础分类版本是很好的特征提取骨干,实际项目里我更常用它来做目标检测的Backbone。把classifierlast_conv层去掉,把blocks输出的特征图接到YOLO或SSD的检测头上,速度和精度综合表现都很可靠。SSD-Lite的检测头配合MNV3-Large作为骨干,在Jetson Nano上跑640×640输入能到30FPS左右,这个组合在轻量检测场景里非常经典。

另一个思路是替换classifier为多层感知机做多标签分类。MNV3最后的特征图是7×7×160(Large版),全局池化后是160维向量,可以在上面堆两个全连接层(160→64→类别数),效果比直接单层分类头好一点,开销可忽略。

最后分享一个我个人的经验:MobileNetV3在ImageNet的Top-1精度比MobileNetV2高大约3-4个百分点(官方数据是75.2% vs 72.0%,均不加额外增强),但在一些小型数据集上优势并没有这么大,差距往往只有1-2个点。原因很简单,小数据集本身类别和样本量有限,注意力模块能学到的通道关系不够丰富。所以如果你的任务数据量特别小,优先级也许不是直接上MNV3,而是先考虑数据增强和正则化。反过来,如果数据量大且类别多,MNV3的SE模块优势就体现出来了。用不用SE、用多少层的SE,可以结合你实际硬件的时间和功耗预算,通过消融实验来决策。

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

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

下载即用!DeepSeek Harness 有桌面客户端了!

大家好&#xff0c;我是 Java陈序员。 最近的 DeepSeek Harness 可以说是十分火热&#xff0c;很多人都想安装体验一下。但是 DeepSeek Harness 依赖 Node.js 环境&#xff0c;需要手动安装 Node.js 环境、敲命令行启动。 对于一些不是很懂技术的普通用户来说&#xff0c;安装过…

作者头像 李华
网站建设 2026/9/1 6:24:53

Matlab实现元胞自动机模拟城市增长

简介&#xff1a;基于元胞自动机的Matlab城市增长模拟项目&#xff0c;适合城乡规划、地理信息科学等方向的师生与研究人员。项目以艾哈迈达巴德地区为案例&#xff0c;通过CA模型模拟未开发土地向住宅、商业、工业等用地的演变&#xff0c;并依据人口迁移、交通便利度等规则迭…

作者头像 李华
网站建设 2026/9/1 6:24:16

OpenAI断供Cursor?开发者如何应对AI编程工具链的模型依赖风险

OpenAI 对 Cursor 断供这件事&#xff0c;今天在不少技术群里都刷屏了。先说我的判断&#xff1a;如果消息属实&#xff0c;影响最大的是那些把 Cursor 当主力开发工具、并且所有 Agent 任务都跑在 OpenAI 模型上的人&#xff1b;如果你只是偶尔用 Cursor 补全代码&#xff0c;…

作者头像 李华
网站建设 2026/9/1 6:23:48

频谱仪基础

以N9918A为例的学习1.外观信息1.频率-26.5GHz最大工作频率26.5GHz&#xff08;频谱仪模式起始工作频率5kHz&#xff09;2.RF Input信号输入端口 27dBm&#xff1a;功率上限0.5W&#xff08;在50Ω系统下&#xff0c;射频交流信号&#xff0c;最大允许 5V 有效值&#xff09; 换…

作者头像 李华
网站建设 2026/9/1 6:21:15

m3u8视频处理全攻略:从HLS协议到ffmpeg转码与前端播放

1. 这篇文章真正要解决的问题最近在整理在线医学课程资料时&#xff0c;经常遇到一种很特殊的视频文件——后缀是.m3u8。比如这一期“博纳格大讲堂No_128《骨增量手术中骨代替材料和屏障膜的选择》”&#xff0c;文件名看起来就是一个普通的技术讲座视频&#xff0c;但你会发现…

作者头像 李华
网站建设 2026/9/1 6:20:15

深度学习基础-1-FFN前馈神经网络

FFN前馈神经网络 一、MCP单层感知机 1、实现流程 输入信号、加权求和、非线性激活&#xff08;、逻辑回归/softmax回归&#xff09;输出 2、特点 a二值化的线性模型&#xff08;模拟单个神经细胞只有抑制和兴奋两种状态&#xff09; **b网络参数&#xff08;权重W和偏置b&#…

作者头像 李华