YOLOv11改进 | 主干/Backbone篇 | 目标检测网络EfficientNetV1均衡缩放网络改进特征提取层 (适配yolov11全系列N、S、M、L、X)

一、本文介绍

这次给大家带来的改进机制是 EfficientNetV1主干 ,用其替换我们YOLOv11的 特征提取网络 ,其主要思想是通过均衡地缩放网络的深度、宽度和分辨率,以提高卷积 神经网络 的性能。这种方法采用了一个简单但有效的复合系数,统一调整所有维度。经过我的实验该主干网络确实能够涨点在大中小三种物体检测上, 同时该主干网络提供多种版本 ,大家可以在 源代码 中进行修改版本的使用。 本文通过介绍其主要框架原理,然后教大家如何添加该网络结构到网络模型中。

(本文内容可根据yolov11的N、S、M、L、X进行二次缩放,轻量化更上一层)。


目录

一、本文介绍

二、EfficientNetV1的框架原理

三、EfficientNetV1的核心代码

四、手把手教你添加EfficientNetV1机制

4.1 修改一

4.2 修改二

4.3 修改三

4.4 修改四

4.5 修改五

4.6 修改六

4.7 修改七

4.8 修改八

注意!!! 额外的修改!

打印计算量问题解决方案

注意事项!!!

五、EfficientNetV1的yaml文件

5.1 EfficientNetV1的yaml文件版本

5.2 训练文件

六、成功运行记录

七、本文总M结


二、EfficientNetV1的框架原理

官方论文地址: 官方论文地址点击即可跳转

官方代码地址: 官方代码地址点击即可跳转


EfficientNetV1的主要思想是通过均衡地缩放网络的深度、宽度和 分辨率 ,以提高卷积神经网络的性能。这种方法采用了一个简单但有效的复合系数,统一调整所有维度。EfficientNet在多个方面优于现有的ConvNets,特别是在ImageNet数据集上,EfficientNet-B7模型在保持较小的大小和更快的推理速度的同时,达到了84.3%的顶级准确率。此外,EfficientNet还在CIFAR-100和Flowers等其他数据集上展示了出色的 迁移学习 性能,参数数量大大减少。

总结: EfficientNetV1的主要创新为 提出了一种新的模型缩放方法,该方法使用一个复合系数来统一地缩放网络的深度、宽度和分辨率,实现更均衡的网络扩展

​这张图展示了EfficientNet提出的模型缩放方法。图中(a)表示基线网络,而图(b)-(d)表示传统的缩放方法,只增加网络的一个维度:宽度、深度或分辨率。图(e)展示了EfficientNet的创新之处,即复合缩放方法,它使用固定比例同时均匀地缩放网络的所有三个维度。


三、EfficientNetV1的核心代码

  1. import re
  2. import math
  3. import collections
  4. from functools import partial
  5. import torch
  6. from torch import nn
  7. from torch.nn import functional as F
  8. from torch.utils import model_zoo
  9. # Parameters for the entire model (stem, all blocks, and head)
  10. GlobalParams = collections.namedtuple('GlobalParams', [
  11. 'width_coefficient', 'depth_coefficient', 'image_size', 'dropout_rate',
  12. 'num_classes', 'batch_norm_momentum', 'batch_norm_epsilon',
  13. 'drop_connect_rate', 'depth_divisor', 'min_depth', 'include_top'])
  14. # Parameters for an individual model block
  15. BlockArgs = collections.namedtuple('BlockArgs', [
  16. 'num_repeat', 'kernel_size', 'stride', 'expand_ratio',
  17. 'input_filters', 'output_filters', 'se_ratio', 'id_skip'])
  18. # Set GlobalParams and BlockArgs's defaults
  19. GlobalParams.__new__.__defaults__ = (None,) * len(GlobalParams._fields)
  20. BlockArgs.__new__.__defaults__ = (None,) * len(BlockArgs._fields)
  21. # Swish activation function
  22. if hasattr(nn, 'SiLU'):
  23. Swish = nn.SiLU
  24. else:
  25. # For compatibility with old PyTorch versions
  26. class Swish(nn.Module):
  27. def forward(self, x):
  28. return x * torch.sigmoid(x)
  29. # A memory-efficient implementation of Swish function
  30. class SwishImplementation(torch.autograd.Function):
  31. @staticmethod
  32. def forward(ctx, i):
  33. result = i * torch.sigmoid(i)
  34. ctx.save_for_backward(i)
  35. return result
  36. @staticmethod
  37. def backward(ctx, grad_output):
  38. i = ctx.saved_tensors[0]
  39. sigmoid_i = torch.sigmoid(i)
  40. return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i)))
  41. class MemoryEfficientSwish(nn.Module):
  42. def forward(self, x):
  43. return SwishImplementation.apply(x)
  44. def round_filters(filters, global_params):
  45. """Calculate and round number of filters based on width multiplier.
  46. Use width_coefficient, depth_divisor and min_depth of global_params.
  47. Args:
  48. filters (int): Filters number to be calculated.
  49. global_params (namedtuple): Global params of the model.
  50. Returns:
  51. new_filters: New filters number after calculating.
  52. """
  53. multiplier = global_params.width_coefficient
  54. if not multiplier:
  55. return filters
  56. # TODO: modify the params names.
  57. # maybe the names (width_divisor,min_width)
  58. # are more suitable than (depth_divisor,min_depth).
  59. divisor = global_params.depth_divisor
  60. min_depth = global_params.min_depth
  61. filters *= multiplier
  62. min_depth = min_depth or divisor # pay attention to this line when using min_depth
  63. # follow the formula transferred from official TensorFlow implementation
  64. new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor)
  65. if new_filters < 0.9 * filters: # prevent rounding by more than 10%
  66. new_filters += divisor
  67. return int(new_filters)
  68. def round_repeats(repeats, global_params):
  69. """Calculate module's repeat number of a block based on depth multiplier.
  70. Use depth_coefficient of global_params.
  71. Args:
  72. repeats (int): num_repeat to be calculated.
  73. global_params (namedtuple): Global params of the model.
  74. Returns:
  75. new repeat: New repeat number after calculating.
  76. """
  77. multiplier = global_params.depth_coefficient
  78. if not multiplier:
  79. return repeats
  80. # follow the formula transferred from official TensorFlow implementation
  81. return int(math.ceil(multiplier * repeats))
  82. def drop_connect(inputs, p, training):
  83. """Drop connect.
  84. Args:
  85. input (tensor: BCWH): Input of this structure.
  86. p (float: 0.0~1.0): Probability of drop connection.
  87. training (bool): The running mode.
  88. Returns:
  89. output: Output after drop connection.
  90. """
  91. assert 0 <= p <= 1, 'p must be in range of [0,1]'
  92. if not training:
  93. return inputs
  94. batch_size = inputs.shape[0]
  95. keep_prob = 1 - p
  96. # generate binary_tensor mask according to probability (p for 0, 1-p for 1)
  97. random_tensor = keep_prob
  98. random_tensor += torch.rand([batch_size, 1, 1, 1], dtype=inputs.dtype, device=inputs.device)
  99. binary_tensor = torch.floor(random_tensor)
  100. output = inputs / keep_prob * binary_tensor
  101. return output
  102. def get_width_and_height_from_size(x):
  103. """Obtain height and width from x.
  104. Args:
  105. x (int, tuple or list): Data size.
  106. Returns:
  107. size: A tuple or list (H,W).
  108. """
  109. if isinstance(x, int):
  110. return x, x
  111. if isinstance(x, list) or isinstance(x, tuple):
  112. return x
  113. else:
  114. raise TypeError()
  115. def calculate_output_image_size(input_image_size, stride):
  116. """Calculates the output image size when using Conv2dSamePadding with a stride.
  117. Necessary for static padding. Thanks to mannatsingh for pointing this out.
  118. Args:
  119. input_image_size (int, tuple or list): Size of input image.
  120. stride (int, tuple or list): Conv2d operation's stride.
  121. Returns:
  122. output_image_size: A list [H,W].
  123. """
  124. if input_image_size is None:
  125. return None
  126. image_height, image_width = get_width_and_height_from_size(input_image_size)
  127. stride = stride if isinstance(stride, int) else stride[0]
  128. image_height = int(math.ceil(image_height / stride))
  129. image_width = int(math.ceil(image_width / stride))
  130. return [image_height, image_width]
  131. # Note:
  132. # The following 'SamePadding' functions make output size equal ceil(input size/stride).
  133. # Only when stride equals 1, can the output size be the same as input size.
  134. # Don't be confused by their function names ! ! !
  135. def get_same_padding_conv2d(image_size=None):
  136. """Chooses static padding if you have specified an image size, and dynamic padding otherwise.
  137. Static padding is necessary for ONNX exporting of models.
  138. Args:
  139. image_size (int or tuple): Size of the image.
  140. Returns:
  141. Conv2dDynamicSamePadding or Conv2dStaticSamePadding.
  142. """
  143. if image_size is None:
  144. return Conv2dDynamicSamePadding
  145. else:
  146. return partial(Conv2dStaticSamePadding, image_size=image_size)
  147. class Conv2dDynamicSamePadding(nn.Conv2d):
  148. """2D Convolutions like TensorFlow, for a dynamic image size.
  149. The padding is operated in forward function by calculating dynamically.
  150. """
  151. # Tips for 'SAME' mode padding.
  152. # Given the following:
  153. # i: width or height
  154. # s: stride
  155. # k: kernel size
  156. # d: dilation
  157. # p: padding
  158. # Output after Conv2d:
  159. # o = floor((i+p-((k-1)*d+1))/s+1)
  160. # If o equals i, i = floor((i+p-((k-1)*d+1))/s+1),
  161. # => p = (i-1)*s+((k-1)*d+1)-i
  162. def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, groups=1, bias=True):
  163. super().__init__(in_channels, out_channels, kernel_size, stride, 0, dilation, groups, bias)
  164. self.stride = self.stride if len(self.stride) == 2 else [self.stride[0]] * 2
  165. def forward(self, x):
  166. ih, iw = x.size()[-2:]
  167. kh, kw = self.weight.size()[-2:]
  168. sh, sw = self.stride
  169. oh, ow = math.ceil(ih / sh), math.ceil(iw / sw) # change the output size according to stride ! ! !
  170. pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)
  171. pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)
  172. if pad_h > 0 or pad_w > 0:
  173. x = F.pad(x, [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2])
  174. return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups)
  175. class Conv2dStaticSamePadding(nn.Conv2d):
  176. """2D Convolutions like TensorFlow's 'SAME' mode, with the given input image size.
  177. The padding mudule is calculated in construction function, then used in forward.
  178. """
  179. # With the same calculation as Conv2dDynamicSamePadding
  180. def __init__(self, in_channels, out_channels, kernel_size, stride=1, image_size=None, **kwargs):
  181. super().__init__(in_channels, out_channels, kernel_size, stride, **kwargs)
  182. self.stride = self.stride if len(self.stride) == 2 else [self.stride[0]] * 2
  183. # Calculate padding based on image size and save it
  184. assert image_size is not None
  185. ih, iw = (image_size, image_size) if isinstance(image_size, int) else image_size
  186. kh, kw = self.weight.size()[-2:]
  187. sh, sw = self.stride
  188. oh, ow = math.ceil(ih / sh), math.ceil(iw / sw)
  189. pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)
  190. pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)
  191. if pad_h > 0 or pad_w > 0:
  192. self.static_padding = nn.ZeroPad2d((pad_w // 2, pad_w - pad_w // 2,
  193. pad_h // 2, pad_h - pad_h // 2))
  194. else:
  195. self.static_padding = nn.Identity()
  196. def forward(self, x):
  197. x = self.static_padding(x)
  198. x = F.conv2d(x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups)
  199. return x
  200. def get_same_padding_maxPool2d(image_size=None):
  201. """Chooses static padding if you have specified an image size, and dynamic padding otherwise.
  202. Static padding is necessary for ONNX exporting of models.
  203. Args:
  204. image_size (int or tuple): Size of the image.
  205. Returns:
  206. MaxPool2dDynamicSamePadding or MaxPool2dStaticSamePadding.
  207. """
  208. if image_size is None:
  209. return MaxPool2dDynamicSamePadding
  210. else:
  211. return partial(MaxPool2dStaticSamePadding, image_size=image_size)
  212. class MaxPool2dDynamicSamePadding(nn.MaxPool2d):
  213. """2D MaxPooling like TensorFlow's 'SAME' mode, with a dynamic image size.
  214. The padding is operated in forward function by calculating dynamically.
  215. """
  216. def __init__(self, kernel_size, stride, padding=0, dilation=1, return_indices=False, ceil_mode=False):
  217. super().__init__(kernel_size, stride, padding, dilation, return_indices, ceil_mode)
  218. self.stride = [self.stride] * 2 if isinstance(self.stride, int) else self.stride
  219. self.kernel_size = [self.kernel_size] * 2 if isinstance(self.kernel_size, int) else self.kernel_size
  220. self.dilation = [self.dilation] * 2 if isinstance(self.dilation, int) else self.dilation
  221. def forward(self, x):
  222. ih, iw = x.size()[-2:]
  223. kh, kw = self.kernel_size
  224. sh, sw = self.stride
  225. oh, ow = math.ceil(ih / sh), math.ceil(iw / sw)
  226. pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)
  227. pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)
  228. if pad_h > 0 or pad_w > 0:
  229. x = F.pad(x, [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2])
  230. return F.max_pool2d(x, self.kernel_size, self.stride, self.padding,
  231. self.dilation, self.ceil_mode, self.return_indices)
  232. class MaxPool2dStaticSamePadding(nn.MaxPool2d):
  233. """2D MaxPooling like TensorFlow's 'SAME' mode, with the given input image size.
  234. The padding mudule is calculated in construction function, then used in forward.
  235. """
  236. def __init__(self, kernel_size, stride, image_size=None, **kwargs):
  237. super().__init__(kernel_size, stride, **kwargs)
  238. self.stride = [self.stride] * 2 if isinstance(self.stride, int) else self.stride
  239. self.kernel_size = [self.kernel_size] * 2 if isinstance(self.kernel_size, int) else self.kernel_size
  240. self.dilation = [self.dilation] * 2 if isinstance(self.dilation, int) else self.dilation
  241. # Calculate padding based on image size and save it
  242. assert image_size is not None
  243. ih, iw = (image_size, image_size) if isinstance(image_size, int) else image_size
  244. kh, kw = self.kernel_size
  245. sh, sw = self.stride
  246. oh, ow = math.ceil(ih / sh), math.ceil(iw / sw)
  247. pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)
  248. pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)
  249. if pad_h > 0 or pad_w > 0:
  250. self.static_padding = nn.ZeroPad2d((pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2))
  251. else:
  252. self.static_padding = nn.Identity()
  253. def forward(self, x):
  254. x = self.static_padding(x)
  255. x = F.max_pool2d(x, self.kernel_size, self.stride, self.padding,
  256. self.dilation, self.ceil_mode, self.return_indices)
  257. return x
  258. ################################################################################
  259. # Helper functions for loading model params
  260. ################################################################################
  261. # BlockDecoder: A Class for encoding and decoding BlockArgs
  262. # efficientnet_params: A function to query compound coefficient
  263. # get_model_params and efficientnet:
  264. # Functions to get BlockArgs and GlobalParams for efficientnet
  265. # url_map and url_map_advprop: Dicts of url_map for pretrained weights
  266. # load_pretrained_weights: A function to load pretrained weights
  267. class BlockDecoder(object):
  268. """Block Decoder for readability,
  269. straight from the official TensorFlow repository.
  270. """
  271. @staticmethod
  272. def _decode_block_string(block_string):
  273. """Get a block through a string notation of arguments.
  274. Args:
  275. block_string (str): A string notation of arguments.
  276. Examples: 'r1_k3_s11_e1_i32_o16_se0.25_noskip'.
  277. Returns:
  278. BlockArgs: The namedtuple defined at the top of this file.
  279. """
  280. assert isinstance(block_string, str)
  281. ops = block_string.split('_')
  282. options = {}
  283. for op in ops:
  284. splits = re.split(r'(\d.*)', op)
  285. if len(splits) >= 2:
  286. key, value = splits[:2]
  287. options[key] = value
  288. # Check stride
  289. assert (('s' in options and len(options['s']) == 1) or
  290. (len(options['s']) == 2 and options['s'][0] == options['s'][1]))
  291. return BlockArgs(
  292. num_repeat=int(options['r']),
  293. kernel_size=int(options['k']),
  294. stride=[int(options['s'][0])],
  295. expand_ratio=int(options['e']),
  296. input_filters=int(options['i']),
  297. output_filters=int(options['o']),
  298. se_ratio=float(options['se']) if 'se' in options else None,
  299. id_skip=('noskip' not in block_string))
  300. @staticmethod
  301. def _encode_block_string(block):
  302. """Encode a block to a string.
  303. Args:
  304. block (namedtuple): A BlockArgs type argument.
  305. Returns:
  306. block_string: A String form of BlockArgs.
  307. """
  308. args = [
  309. 'r%d' % block.num_repeat,
  310. 'k%d' % block.kernel_size,
  311. 's%d%d' % (block.strides[0], block.strides[1]),
  312. 'e%s' % block.expand_ratio,
  313. 'i%d' % block.input_filters,
  314. 'o%d' % block.output_filters
  315. ]
  316. if 0 < block.se_ratio <= 1:
  317. args.append('se%s' % block.se_ratio)
  318. if block.id_skip is False:
  319. args.append('noskip')
  320. return '_'.join(args)
  321. @staticmethod
  322. def decode(string_list):
  323. """Decode a list of string notations to specify blocks inside the network.
  324. Args:
  325. string_list (list[str]): A list of strings, each string is a notation of block.
  326. Returns:
  327. blocks_args: A list of BlockArgs namedtuples of block args.
  328. """
  329. assert isinstance(string_list, list)
  330. blocks_args = []
  331. for block_string in string_list:
  332. blocks_args.append(BlockDecoder._decode_block_string(block_string))
  333. return blocks_args
  334. @staticmethod
  335. def encode(blocks_args):
  336. """Encode a list of BlockArgs to a list of strings.
  337. Args:
  338. blocks_args (list[namedtuples]): A list of BlockArgs namedtuples of block args.
  339. Returns:
  340. block_strings: A list of strings, each string is a notation of block.
  341. """
  342. block_strings = []
  343. for block in blocks_args:
  344. block_strings.append(BlockDecoder._encode_block_string(block))
  345. return block_strings
  346. def efficientnet_params(model_name):
  347. """Map EfficientNet model name to parameter coefficients.
  348. Args:
  349. model_name (str): Model name to be queried.
  350. Returns:
  351. params_dict[model_name]: A (width,depth,res,dropout) tuple.
  352. """
  353. params_dict = {
  354. # Coefficients: width,depth,res,dropout
  355. 'efficientnet-b0': (1.0, 1.0, 224, 0.2),
  356. 'efficientnet-b1': (1.0, 1.1, 240, 0.2),
  357. 'efficientnet-b2': (1.1, 1.2, 260, 0.3),
  358. 'efficientnet-b3': (1.2, 1.4, 300, 0.3),
  359. 'efficientnet-b4': (1.4, 1.8, 380, 0.4),
  360. 'efficientnet-b5': (1.6, 2.2, 456, 0.4),
  361. 'efficientnet-b6': (1.8, 2.6, 528, 0.5),
  362. 'efficientnet-b7': (2.0, 3.1, 600, 0.5),
  363. 'efficientnet-b8': (2.2, 3.6, 672, 0.5),
  364. 'efficientnet-l2': (4.3, 5.3, 800, 0.5),
  365. }
  366. return params_dict[model_name]
  367. def efficientnet(width_coefficient=None, depth_coefficient=None, image_size=None,
  368. dropout_rate=0.2, drop_connect_rate=0.2, num_classes=1000, include_top=True):
  369. """Create BlockArgs and GlobalParams for efficientnet model.
  370. Args:
  371. width_coefficient (float)
  372. depth_coefficient (float)
  373. image_size (int)
  374. dropout_rate (float)
  375. drop_connect_rate (float)
  376. num_classes (int)
  377. Meaning as the name suggests.
  378. Returns:
  379. blocks_args, global_params.
  380. """
  381. # Blocks args for the whole model(efficientnet-b0 by default)
  382. # It will be modified in the construction of EfficientNet Class according to model
  383. blocks_args = [
  384. 'r1_k3_s11_e1_i32_o16_se0.25',
  385. 'r2_k3_s22_e6_i16_o24_se0.25',
  386. 'r2_k5_s22_e6_i24_o40_se0.25',
  387. 'r3_k3_s22_e6_i40_o80_se0.25',
  388. 'r3_k5_s11_e6_i80_o112_se0.25',
  389. 'r4_k5_s22_e6_i112_o192_se0.25',
  390. 'r1_k3_s11_e6_i192_o320_se0.25',
  391. ]
  392. blocks_args = BlockDecoder.decode(blocks_args)
  393. global_params = GlobalParams(
  394. width_coefficient=width_coefficient,
  395. depth_coefficient=depth_coefficient,
  396. image_size=image_size,
  397. dropout_rate=dropout_rate,
  398. num_classes=num_classes,
  399. batch_norm_momentum=0.99,
  400. batch_norm_epsilon=1e-3,
  401. drop_connect_rate=drop_connect_rate,
  402. depth_divisor=8,
  403. min_depth=None,
  404. include_top=include_top,
  405. )
  406. return blocks_args, global_params
  407. def get_model_params(model_name, override_params):
  408. """Get the block args and global params for a given model name.
  409. Args:
  410. model_name (str): Model's name.
  411. override_params (dict): A dict to modify global_params.
  412. Returns:
  413. blocks_args, global_params
  414. """
  415. if model_name.startswith('efficientnet'):
  416. w, d, s, p = efficientnet_params(model_name)
  417. # note: all models have drop connect rate = 0.2
  418. blocks_args, global_params = efficientnet(
  419. width_coefficient=w, depth_coefficient=d, dropout_rate=p, image_size=s)
  420. else:
  421. raise NotImplementedError('model name is not pre-defined: {}'.format(model_name))
  422. if override_params:
  423. # ValueError will be raised here if override_params has fields not included in global_params.
  424. global_params = global_params._replace(**override_params)
  425. return blocks_args, global_params
  426. # train with Standard methods
  427. # check more details in paper(EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks)
  428. url_map = {
  429. 'efficientnet-b0': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b0-355c32eb.pth',
  430. 'efficientnet-b1': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b1-f1951068.pth',
  431. 'efficientnet-b2': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b2-8bb594d6.pth',
  432. 'efficientnet-b3': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b3-5fb5a3c3.pth',
  433. 'efficientnet-b4': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b4-6ed6700e.pth',
  434. 'efficientnet-b5': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b5-b6417697.pth',
  435. 'efficientnet-b6': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b6-c76e70fd.pth',
  436. 'efficientnet-b7': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b7-dcc49843.pth',
  437. }
  438. # train with Adversarial Examples(AdvProp)
  439. # check more details in paper(Adversarial Examples Improve Image Recognition)
  440. url_map_advprop = {
  441. 'efficientnet-b0': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b0-b64d5a18.pth',
  442. 'efficientnet-b1': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b1-0f3ce85a.pth',
  443. 'efficientnet-b2': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b2-6e9d97e5.pth',
  444. 'efficientnet-b3': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b3-cdd7c0f4.pth',
  445. 'efficientnet-b4': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b4-44fb3a87.pth',
  446. 'efficientnet-b5': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b5-86493f6b.pth',
  447. 'efficientnet-b6': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b6-ac80338e.pth',
  448. 'efficientnet-b7': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b7-4652b6dd.pth',
  449. 'efficientnet-b8': 'https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/adv-efficientnet-b8-22a8fe65.pth',
  450. }
  451. # TODO: add the petrained weights url map of 'efficientnet-l2'
  452. def load_pretrained_weights(model, model_name, weights_path=None, load_fc=True, advprop=False, verbose=True):
  453. """Loads pretrained weights from weights path or download using url.
  454. Args:
  455. model (Module): The whole model of efficientnet.
  456. model_name (str): Model name of efficientnet.
  457. weights_path (None or str):
  458. str: path to pretrained weights file on the local disk.
  459. None: use pretrained weights downloaded from the Internet.
  460. load_fc (bool): Whether to load pretrained weights for fc layer at the end of the model.
  461. advprop (bool): Whether to load pretrained weights
  462. trained with advprop (valid when weights_path is None).
  463. """
  464. if isinstance(weights_path, str):
  465. state_dict = torch.load(weights_path)
  466. else:
  467. # AutoAugment or Advprop (different preprocessing)
  468. url_map_ = url_map_advprop if advprop else url_map
  469. state_dict = model_zoo.load_url(url_map_[model_name])
  470. if load_fc:
  471. ret = model.load_state_dict(state_dict, strict=False)
  472. assert not ret.missing_keys, 'Missing keys when loading pretrained weights: {}'.format(ret.missing_keys)
  473. else:
  474. state_dict.pop('_fc.weight')
  475. state_dict.pop('_fc.bias')
  476. ret = model.load_state_dict(state_dict, strict=False)
  477. assert set(ret.missing_keys) == set(
  478. ['_fc.weight', '_fc.bias']), 'Missing keys when loading pretrained weights: {}'.format(ret.missing_keys)
  479. assert not ret.unexpected_keys, 'Missing keys when loading pretrained weights: {}'.format(ret.unexpected_keys)
  480. if verbose:
  481. print('Loaded pretrained weights for {}'.format(model_name))
  482. VALID_MODELS = (
  483. 'efficientnet-b0', 'efficientnet-b1', 'efficientnet-b2', 'efficientnet-b3',
  484. 'efficientnet-b4', 'efficientnet-b5', 'efficientnet-b6', 'efficientnet-b7',
  485. 'efficientnet-b8',
  486. # Support the construction of 'efficientnet-l2' without pretrained weights
  487. 'efficientnet-l2'
  488. )
  489. class MBConvBlock(nn.Module):
  490. """Mobile Inverted Residual Bottleneck Block.
  491. Args:
  492. block_args (namedtuple): BlockArgs, defined in utils.py.
  493. global_params (namedtuple): GlobalParam, defined in utils.py.
  494. image_size (tuple or list): [image_height, image_width].
  495. References:
  496. [1] https://arxiv.org/abs/1704.04861 (MobileNet v1)
  497. [2] https://arxiv.org/abs/1801.04381 (MobileNet v2)
  498. [3] https://arxiv.org/abs/1905.02244 (MobileNet v3)
  499. """
  500. def __init__(self, block_args, global_params, image_size=None):
  501. super().__init__()
  502. self._block_args = block_args
  503. self._bn_mom = 1 - global_params.batch_norm_momentum # pytorch's difference from tensorflow
  504. self._bn_eps = global_params.batch_norm_epsilon
  505. self.has_se = (self._block_args.se_ratio is not None) and (0 < self._block_args.se_ratio <= 1)
  506. self.id_skip = block_args.id_skip # whether to use skip connection and drop connect
  507. # Expansion phase (Inverted Bottleneck)
  508. inp = self._block_args.input_filters # number of input channels
  509. oup = self._block_args.input_filters * self._block_args.expand_ratio # number of output channels
  510. if self._block_args.expand_ratio != 1:
  511. Conv2d = get_same_padding_conv2d(image_size=image_size)
  512. self._expand_conv = Conv2d(in_channels=inp, out_channels=oup, kernel_size=1, bias=False)
  513. self._bn0 = nn.BatchNorm2d(num_features=oup, momentum=self._bn_mom, eps=self._bn_eps)
  514. # image_size = calculate_output_image_size(image_size, 1) <-- this wouldn't modify image_size
  515. # Depthwise convolution phase
  516. k = self._block_args.kernel_size
  517. s = self._block_args.stride
  518. Conv2d = get_same_padding_conv2d(image_size=image_size)
  519. self._depthwise_conv = Conv2d(
  520. in_channels=oup, out_channels=oup, groups=oup, # groups makes it depthwise
  521. kernel_size=k, stride=s, bias=False)
  522. self._bn1 = nn.BatchNorm2d(num_features=oup, momentum=self._bn_mom, eps=self._bn_eps)
  523. image_size = calculate_output_image_size(image_size, s)
  524. # Squeeze and Excitation layer, if desired
  525. if self.has_se:
  526. Conv2d = get_same_padding_conv2d(image_size=(1, 1))
  527. num_squeezed_channels = max(1, int(self._block_args.input_filters * self._block_args.se_ratio))
  528. self._se_reduce = Conv2d(in_channels=oup, out_channels=num_squeezed_channels, kernel_size=1)
  529. self._se_expand = Conv2d(in_channels=num_squeezed_channels, out_channels=oup, kernel_size=1)
  530. # Pointwise convolution phase
  531. final_oup = self._block_args.output_filters
  532. Conv2d = get_same_padding_conv2d(image_size=image_size)
  533. self._project_conv = Conv2d(in_channels=oup, out_channels=final_oup, kernel_size=1, bias=False)
  534. self._bn2 = nn.BatchNorm2d(num_features=final_oup, momentum=self._bn_mom, eps=self._bn_eps)
  535. self._swish = MemoryEfficientSwish()
  536. def forward(self, inputs, drop_connect_rate=None):
  537. """MBConvBlock's forward function.
  538. Args:
  539. inputs (tensor): Input tensor.
  540. drop_connect_rate (bool): Drop connect rate (float, between 0 and 1).
  541. Returns:
  542. Output of this block after processing.
  543. """
  544. # Expansion and Depthwise Convolution
  545. x = inputs
  546. if self._block_args.expand_ratio != 1:
  547. x = self._expand_conv(inputs)
  548. x = self._bn0(x)
  549. x = self._swish(x)
  550. x = self._depthwise_conv(x)
  551. x = self._bn1(x)
  552. x = self._swish(x)
  553. # Squeeze and Excitation
  554. if self.has_se:
  555. x_squeezed = F.adaptive_avg_pool2d(x, 1)
  556. x_squeezed = self._se_reduce(x_squeezed)
  557. x_squeezed = self._swish(x_squeezed)
  558. x_squeezed = self._se_expand(x_squeezed)
  559. x = torch.sigmoid(x_squeezed) * x
  560. # Pointwise Convolution
  561. x = self._project_conv(x)
  562. x = self._bn2(x)
  563. # Skip connection and drop connect
  564. input_filters, output_filters = self._block_args.input_filters, self._block_args.output_filters
  565. if self.id_skip and self._block_args.stride == 1 and input_filters == output_filters:
  566. # The combination of skip connection and drop connect brings about stochastic depth.
  567. if drop_connect_rate:
  568. x = drop_connect(x, p=drop_connect_rate, training=self.training)
  569. x = x + inputs # skip connection
  570. return x
  571. def set_swish(self, memory_efficient=True):
  572. """Sets swish function as memory efficient (for training) or standard (for export).
  573. Args:
  574. memory_efficient (bool): Whether to use memory-efficient version of swish.
  575. """
  576. self._swish = MemoryEfficientSwish() if memory_efficient else Swish()
  577. class EfficientNet(nn.Module):
  578. def __init__(self,blocks_args=None, global_params=None, factor=0.5, depth_factor=0.5):
  579. super().__init__()
  580. assert isinstance(blocks_args, list), 'blocks_args should be a list'
  581. assert len(blocks_args) > 0, 'block args must be greater than 0'
  582. new_blocks_args = []
  583. for block in blocks_args:
  584. new_block = BlockArgs(
  585. num_repeat=max(1, int(block.num_repeat * depth_factor)),
  586. kernel_size=block.kernel_size,
  587. stride=block.stride,
  588. expand_ratio=block.expand_ratio,
  589. input_filters=int(block.input_filters * factor),
  590. output_filters=int(block.output_filters * factor),
  591. se_ratio=block.se_ratio,
  592. id_skip=block.id_skip
  593. )
  594. new_blocks_args.append(new_block)
  595. channel = int(32 * factor)
  596. # 将新的列表替换掉原来的 blocks_args
  597. blocks_args = new_blocks_args
  598. self._global_params = global_params
  599. self._blocks_args = blocks_args
  600. # Batch norm parameters
  601. bn_mom = 1 - self._global_params.batch_norm_momentum
  602. bn_eps = self._global_params.batch_norm_epsilon
  603. # Get stem static or dynamic convolution depending on image size
  604. image_size = global_params.image_size
  605. Conv2d = get_same_padding_conv2d(image_size=image_size)
  606. # Stem
  607. in_channels = 3 # rgb
  608. out_channels = round_filters(channel, self._global_params) # number of output channels
  609. self._conv_stem = Conv2d(in_channels, out_channels, kernel_size=3, stride=2, bias=False)
  610. self._bn0 = nn.BatchNorm2d(num_features=out_channels, momentum=bn_mom, eps=bn_eps)
  611. image_size = calculate_output_image_size(image_size, 2)
  612. # Build blocks
  613. self._blocks = nn.ModuleList([])
  614. for block_args in self._blocks_args:
  615. # Update block input and output filters based on depth multiplier.
  616. block_args = block_args._replace(
  617. input_filters=round_filters(block_args.input_filters, self._global_params),
  618. output_filters=round_filters(block_args.output_filters, self._global_params),
  619. num_repeat=round_repeats(block_args.num_repeat, self._global_params)
  620. )
  621. # The first block needs to take care of stride and filter size increase.
  622. self._blocks.append(MBConvBlock(block_args, self._global_params, image_size=image_size))
  623. image_size = calculate_output_image_size(image_size, block_args.stride)
  624. if block_args.num_repeat > 1: # modify block_args to keep same output size
  625. block_args = block_args._replace(input_filters=block_args.output_filters, stride=1)
  626. for _ in range(block_args.num_repeat - 1):
  627. self._blocks.append(MBConvBlock(block_args, self._global_params, image_size=image_size))
  628. # image_size = calculate_output_image_size(image_size, block_args.stride) # stride = 1
  629. # Head
  630. in_channels = block_args.output_filters # output of final block
  631. out_channels = round_filters(1280, self._global_params)
  632. Conv2d = get_same_padding_conv2d(image_size=image_size)
  633. self._conv_head = Conv2d(in_channels, out_channels, kernel_size=1, bias=False)
  634. self._bn1 = nn.BatchNorm2d(num_features=out_channels, momentum=bn_mom, eps=bn_eps)
  635. # Final linear layer
  636. self._avg_pooling = nn.AdaptiveAvgPool2d(1)
  637. if self._global_params.include_top:
  638. self._dropout = nn.Dropout(self._global_params.dropout_rate)
  639. self._fc = nn.Linear(out_channels, self._global_params.num_classes)
  640. # set activation to memory efficient swish by default
  641. self._swish = MemoryEfficientSwish()
  642. self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
  643. def set_swish(self, memory_efficient=True):
  644. """Sets swish function as memory efficient (for training) or standard (for export).
  645. Args:
  646. memory_efficient (bool): Whether to use memory-efficient version of swish.
  647. """
  648. self._swish = MemoryEfficientSwish() if memory_efficient else Swish()
  649. for block in self._blocks:
  650. block.set_swish(memory_efficient)
  651. def extract_endpoints(self, inputs):
  652. # """Use convolution layer to extract features
  653. # from reduction levels i in [1, 2, 3, 4, 5].
  654. #
  655. # Args:
  656. # inputs (tensor): Input tensor.
  657. #
  658. # Returns:
  659. # Dictionary of last intermediate features
  660. # with reduction levels i in [1, 2, 3, 4, 5].
  661. # Example:
  662. # >>> import torch
  663. # >>> from efficientnet.model import EfficientNet
  664. # >>> inputs = torch.rand(1, 3, 224, 224)
  665. # >>> model = EfficientNet.from_pretrained('efficientnet-b0')
  666. # >>> endpoints = model.extract_endpoints(inputs)
  667. # >>> print(endpoints['reduction_1'].shape) # torch.Size([1, 16, 112, 112])
  668. # >>> print(endpoints['reduction_2'].shape) # torch.Size([1, 24, 56, 56])
  669. # >>> print(endpoints['reduction_3'].shape) # torch.Size([1, 40, 28, 28])
  670. # >>> print(endpoints['reduction_4'].shape) # torch.Size([1, 112, 14, 14])
  671. # >>> print(endpoints['reduction_5'].shape) # torch.Size([1, 320, 7, 7])
  672. # >>> print(endpoints['reduction_6'].shape) # torch.Size([1, 1280, 7, 7])
  673. # """
  674. endpoints = dict()
  675. # Stem
  676. x = self._swish(self._bn0(self._conv_stem(inputs)))
  677. prev_x = x
  678. # Blocks
  679. for idx, block in enumerate(self._blocks):
  680. drop_connect_rate = self._global_params.drop_connect_rate
  681. if drop_connect_rate:
  682. drop_connect_rate *= float(idx) / len(self._blocks) # scale drop connect_rate
  683. x = block(x, drop_connect_rate=drop_connect_rate)
  684. if prev_x.size(2) > x.size(2):
  685. endpoints['reduction_{}'.format(len(endpoints) + 1)] = prev_x
  686. elif idx == len(self._blocks) - 1:
  687. endpoints['reduction_{}'.format(len(endpoints) + 1)] = x
  688. prev_x = x
  689. # Head
  690. x = self._swish(self._bn1(self._conv_head(x)))
  691. endpoints['reduction_{}'.format(len(endpoints) + 1)] = x
  692. return endpoints
  693. def forward(self, inputs):
  694. """use convolution layer to extract feature .
  695. Args:
  696. inputs (tensor): Input tensor.
  697. Returns:
  698. Output of the final convolution
  699. layer in the efficientnet model.
  700. """
  701. # Stem
  702. x = self._swish(self._bn0(self._conv_stem(inputs)))
  703. unique_tensors = {}
  704. # Blocks
  705. for idx, block in enumerate(self._blocks):
  706. drop_connect_rate = self._global_params.drop_connect_rate
  707. if drop_connect_rate:
  708. drop_connect_rate *= float(idx) / len(self._blocks) # scale drop connect_rate
  709. x = block(x, drop_connect_rate=drop_connect_rate)
  710. width, height = x.shape[2], x.shape[3]
  711. unique_tensors[(width, height)] = x
  712. result_list = list(unique_tensors.values())[-4:]
  713. # Head
  714. return result_list
  715. @classmethod
  716. def from_name(cls, model_name, in_channels=3, **override_params):
  717. """Create an efficientnet model according to name.
  718. Args:
  719. model_name (str): Name for efficientnet.
  720. in_channels (int): Input data's channel number.
  721. override_params (other key word params):
  722. Params to override model's global_params.
  723. Optional key:
  724. 'width_coefficient', 'depth_coefficient',
  725. 'image_size', 'dropout_rate',
  726. 'num_classes', 'batch_norm_momentum',
  727. 'batch_norm_epsilon', 'drop_connect_rate',
  728. 'depth_divisor', 'min_depth'
  729. Returns:
  730. An efficientnet model.
  731. """
  732. cls._check_model_name_is_valid(model_name)
  733. blocks_args, global_params = get_model_params(model_name, override_params)
  734. model = cls(blocks_args, global_params)
  735. model._change_in_channels(in_channels)
  736. return model
  737. @classmethod
  738. def from_pretrained(cls, model_name, weights_path=None, advprop=False,
  739. in_channels=3, num_classes=1000, **override_params):
  740. """Create an efficientnet model according to name.
  741. Args:
  742. model_name (str): Name for efficientnet.
  743. weights_path (None or str):
  744. str: path to pretrained weights file on the local disk.
  745. None: use pretrained weights downloaded from the Internet.
  746. advprop (bool):
  747. Whether to load pretrained weights
  748. trained with advprop (valid when weights_path is None).
  749. in_channels (int): Input data's channel number.
  750. num_classes (int):
  751. Number of categories for classification.
  752. It controls the output size for final linear layer.
  753. override_params (other key word params):
  754. Params to override model's global_params.
  755. Optional key:
  756. 'width_coefficient', 'depth_coefficient',
  757. 'image_size', 'dropout_rate',
  758. 'batch_norm_momentum',
  759. 'batch_norm_epsilon', 'drop_connect_rate',
  760. 'depth_divisor', 'min_depth'
  761. Returns:
  762. A pretrained efficientnet model.
  763. """
  764. model = cls.from_name(model_name, num_classes=num_classes, **override_params)
  765. load_pretrained_weights(model, model_name, weights_path=weights_path,
  766. load_fc=(num_classes == 1000), advprop=advprop)
  767. model._change_in_channels(in_channels)
  768. return model
  769. @classmethod
  770. def get_image_size(cls, model_name):
  771. """Get the input image size for a given efficientnet model.
  772. Args:
  773. model_name (str): Name for efficientnet.
  774. Returns:
  775. Input image size (resolution).
  776. """
  777. cls._check_model_name_is_valid(model_name)
  778. _, _, res, _ = efficientnet_params(model_name)
  779. return res
  780. @classmethod
  781. def _check_model_name_is_valid(cls, model_name):
  782. """Validates model name.
  783. Args:
  784. model_name (str): Name for efficientnet.
  785. Returns:
  786. bool: Is a valid name or not.
  787. """
  788. if model_name not in VALID_MODELS:
  789. raise ValueError('model_name should be one of: ' + ', '.join(VALID_MODELS))
  790. def _change_in_channels(self, in_channels):
  791. """Adjust model's first convolution layer to in_channels, if in_channels not equals 3.
  792. Args:
  793. in_channels (int): Input data's channel number.
  794. """
  795. if in_channels != 3:
  796. Conv2d = get_same_padding_conv2d(image_size=self._global_params.image_size)
  797. out_channels = round_filters(32, self._global_params)
  798. self._conv_stem = Conv2d(in_channels, out_channels, kernel_size=3, stride=2, bias=False)
  799. def efficient(model_name='efficientnet-b0', factor=0.25, depth_factor=0.5, pretrained=False):
  800. if pretrained:
  801. model = EfficientNet.from_pretrained('{}'.format(model_name, factor=factor, depth_factor=depth_factor))
  802. else:
  803. model = EfficientNet.from_name('{}'.format(model_name, factor=factor, depth_factor=depth_factor))
  804. return model
  805. if __name__ == "__main__":
  806. # VALID_MODELS = (
  807. # 'efficientnet-b0', 'efficientnet-b1', 'efficientnet-b2', 'efficientnet-b3',
  808. # 'efficientnet-b4', 'efficientnet-b5', 'efficientnet-b6', 'efficientnet-b7',
  809. # 'efficientnet-b8',
  810. # # Support the construction of 'efficientnet-l2' without pretrained weights
  811. # 'efficientnet-l2'
  812. # )
  813. # Generating Sample image
  814. image_size = (1, 3, 640, 640)
  815. image = torch.rand(*image_size)
  816. # Model
  817. model = efficient('efficientnet-b0', factor=0.25, depth_factor=0.5)
  818. out = model(image)
  819. print(len(out))


四、手把手教你添加EfficientNetV1机制

4.1 修改一

第一步还是建立文件,我们找到如下ultralytics/nn文件夹下建立一个目录名字呢就是'Addmodules'文件夹( !然后在其内部建立一个新的py文件将核心代码复制粘贴进去即可


4.2 修改二

第二步我们在该目录下创建一个新的py文件名字为'__init__.py'( ,然后在其内部导入我们的检测头如下图所示。


4.3 修改三

第三步我门中到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( !


4.4 修改四

添加如下两行代码!!!


4.5 修改五

找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是 函数 名。

  1. elif m in {自行添加对应的模型即可,下面都是一样的}:
  2. m = m(*args)
  3. c2 = m.width_list # 返回通道列表
  4. backbone = True


4.6 修改六

下面的两个红框内都是需要改动的。

  1. if isinstance(c2, list):
  2. m_ = m
  3. m_.backbone = True
  4. else:
  5. m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module
  6. t = str(m)[8:-2].replace('__main__.', '') # module type
  7. m.np = sum(x.numel() for x in m_.parameters()) # number params
  8. m_.i, m_.f, m_.type = i + 4 if backbone else i, f, t # attach index, 'from' index, type


4.7 修改七

如下的也需要修改,全部按照我的来。

代码如下把原先的代码替换了即可。

  1. if verbose:
  2. LOGGER.info(f'{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f} {t:<45}{str(args):<30}') # print
  3. save.extend(x % (i + 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
  4. layers.append(m_)
  5. if i == 0:
  6. ch = []
  7. if isinstance(c2, list):
  8. ch.extend(c2)
  9. if len(c2) != 5:
  10. ch.insert(0, 0)
  11. else:
  12. ch.append(c2)


4.8 修改八

修改八和前面的都不太一样,需要修改前向传播中的一个部分, 已经离开了parse_model方法了。

可以在图片中开代码行数,没有离开task.py文件都是同一个文件。 同时这个部分有好几个前向传播都很相似,大家不要看错了, 是70多行左右的!!!,同时我后面提供了代码,大家直接复制粘贴即可,有时间我针对这里会出一个视频。

​​

代码如下->

  1. def _predict_once(self, x, profile=False, visualize=False, embed=None):
  2. """
  3. Perform a forward pass through the network.
  4. Args:
  5. x (torch.Tensor): The input tensor to the model.
  6. profile (bool): Print the computation time of each layer if True, defaults to False.
  7. visualize (bool): Save the feature maps of the model if True, defaults to False.
  8. embed (list, optional): A list of feature vectors/embeddings to return.
  9. Returns:
  10. (torch.Tensor): The last output of the model.
  11. """
  12. y, dt, embeddings = [], [], [] # outputs
  13. for m in self.model:
  14. if m.f != -1: # if not from previous layer
  15. x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
  16. if profile:
  17. self._profile_one_layer(m, x, dt)
  18. if hasattr(m, 'backbone'):
  19. x = m(x)
  20. if len(x) != 5: # 0 - 5
  21. x.insert(0, None)
  22. for index, i in enumerate(x):
  23. if index in self.save:
  24. y.append(i)
  25. else:
  26. y.append(None)
  27. x = x[-1] # 最后一个输出传给下一层
  28. else:
  29. x = m(x) # run
  30. y.append(x if m.i in self.save else None) # save output
  31. if visualize:
  32. feature_visualization(x, m.type, m.i, save_dir=visualize)
  33. if embed and m.i in embed:
  34. embeddings.append(nn.functional.adaptive_avg_pool2d(x, (1, 1)).squeeze(-1).squeeze(-1)) # flatten
  35. if m.i == max(embed):
  36. return torch.unbind(torch.cat(embeddings, 1), dim=0)
  37. return x

到这里就完成了修改部分,但是这里面细节很多,大家千万要注意不要替换多余的代码,导致报错,也不要拉下任何一部,都会导致运行失败,而且报错很难排查!!!很难排查!!!


注意!!! 额外的修改!

关注我的其实都知道,我大部分的修改都是一样的,这个网络需要额外的修改一步,就是s一个参数,将下面的s改为640!!!即可完美运行!!


打印计算量问题解决方案

我们找到如下文件'ultralytics/utils/torch_utils.py'按照如下的图片进行修改,否则容易打印不出来计算量。


注意事项!!!

如果大家在验证的时候报错形状不匹配的错误可以固定验证集的图片尺寸,方法如下 ->

找到下面这个文件ultralytics/ models /yolo/detect/train.py然后其中有一个类是DetectionTrainer class中的build_dataset函数中的一个参数rect=mode == 'val'改为rect=False


五、EfficientNetV1的yaml文件

复制如下yaml文件进行运行!!!


5.1 EfficientNetV1 的yaml文件版本

此版本训练信息:YOLO11-EfficientNetV1 summary: 337 layers, 3,515,595 parameters, 3,515,579 gradients, 3.7 GFLOPs

使用说明:# 下面 [-1, 1, efficientnet-b0}, [efficientnet-b0, 0.25,0.5]] 参数位置的0.25是通道放缩的系数, YOLOv11N是0.25 YOLOv11S是0.5 YOLOv11M是1. YOLOv11l是1 YOLOv11是1.5大家根据自己训练的YOLO版本设定即可.
#  0.5对应的是模型的深度系数
#  efficientnet-b0为模型的版本

# 本文支持版本有

#     'efficientnet-b0', 'efficientnet-b1', 'efficientnet-b2', 'efficientnet-b3',
#     'efficientnet-b4', 'efficientnet-b5', 'efficientnet-b6', 'efficientnet-b7',
#     'efficientnet-b8', 'efficientnet-l2'
  1. # Ultralytics YOLO 🚀, AGPL-3.0 license
  2. # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
  3. # Parameters
  4. nc: 80 # number of classes
  5. scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
  6. # [depth, width, max_channels]
  7. n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  8. s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  9. m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  10. l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  11. x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
  12. # 下面 [-1, 1, efficientnet-b0}, [efficientnet-b0, 0.250.5]] 参数位置的0.25是通道放缩的系数, YOLOv11N是0.25 YOLOv11S是0.5 YOLOv11M是1. YOLOv11l是1 YOLOv111.5大家根据自己训练的YOLO版本设定即可.
  13. # 0.5对应的是模型的深度系数
  14. # efficientnet-b0为模型的版本
  15. # 支持的版本:
  16. # 'efficientnet-b0', 'efficientnet-b1', 'efficientnet-b2', 'efficientnet-b3',
  17. # 'efficientnet-b4', 'efficientnet-b5', 'efficientnet-b6', 'efficientnet-b7',
  18. # 'efficientnet-b8', 'efficientnet-l2'
  19. # YOLO11n backbone
  20. backbone:
  21. # [from, repeats, module, args]
  22. - [-1, 1, efficient, [efficientnet-b0, 0.25,0.5]] # 0-4 P1/2
  23. - [-1, 1, SPPF, [1024, 5]] # 5
  24. - [-1, 2, C2PSA, [1024]] # 6
  25. # YOLO11n head
  26. head:
  27. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  28. - [[-1, 3], 1, Concat, [1]] # cat backbone P4
  29. - [-1, 2, C3k2, [512, False]] # 9
  30. - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  31. - [[-1, 2], 1, Concat, [1]] # cat backbone P3
  32. - [-1, 2, C3k2, [256, False]] # 12 (P3/8-small)
  33. - [-1, 1, Conv, [256, 3, 2]]
  34. - [[-1, 9], 1, Concat, [1]] # cat head P4
  35. - [-1, 2, C3k2, [512, False]] # 15 (P4/16-medium)
  36. - [-1, 1, Conv, [512, 3, 2]]
  37. - [[-1, 6], 1, Concat, [1]] # cat head P5
  38. - [-1, 2, C3k2, [1024, True]] # 18 (P5/32-large)
  39. - [[12, 15, 18], 1, Detect, [nc]] # Detect(P3, P4, P5)


5.2 训练文件

  1. import warnings
  2. warnings.filterwarnings('ignore')
  3. from ultralytics import YOLO
  4. if __name__ == '__main__':
  5. model = YOLO('ultralytics/cfg/models/v8/yolov8-C2f-FasterBlock.yaml')
  6. # model.load('yolov8n.pt') # loading pretrain weights
  7. model.train(data=r'替换数据集yaml文件地址',
  8. # 如果大家任务是其它的'ultralytics/cfg/default.yaml'找到这里修改task可以改成detect, segment, classify, pose
  9. cache=False,
  10. imgsz=640,
  11. epochs=150,
  12. single_cls=False, # 是否是单类别检测
  13. batch=4,
  14. close_mosaic=10,
  15. workers=0,
  16. device='0',
  17. optimizer='SGD', # using SGD
  18. # resume='', # 如过想续训就设置last.pt的地址
  19. amp=False, # 如果出现训练损失为Nan可以关闭amp
  20. project='runs/train',
  21. name='exp',
  22. )


六、成功运行记录

下面是成功运行的截图,已经完成了有1个epochs的训练,图片太大截不全第2个epochs,这里改完之后打印出了点问题,但是不影响任何功能,后期我找时间修复一下这个问题。

​​


七、本文总M结

到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充, 目前本专栏免费阅读(暂时,大家尽早关注不迷路~) ,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

​​