model_get.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import os
  2. import torch
  3. choice_dict = {
  4. 'LeNet': 'model_prepare(args).LeNet()',
  5. 'Alexnet': 'model_prepare(args).Alexnet()',
  6. 'VGG19': 'model_prepare(args).VGG19()',
  7. 'VGG16': 'model_prepare(args).VGG16()',
  8. 'GoogleNet': 'model_prepare(args).GoogleNet()',
  9. 'resnet': 'model_prepare(args).resnet()'
  10. }
  11. def model_get(args):
  12. if args.weight and os.path.exists(args.weight): # 优先加载已有模型继续训练
  13. model_dict = torch.load(args.weight, map_location='cpu')
  14. else: # 新建模型
  15. if args.prune: # 模型剪枝
  16. model_dict = torch.load(args.prune_weight, map_location='cpu')
  17. model = model_dict['model']
  18. model = prune(args, model)
  19. else:
  20. model = eval(choice_dict[args.model])
  21. model_dict = {}
  22. model_dict['model'] = model
  23. model_dict['epoch_finished'] = 0 # 已训练的轮数
  24. model_dict['optimizer_state_dict'] = None # 学习率参数
  25. model_dict['ema_updates'] = 0 # ema参数
  26. model_dict['standard'] = 0 # 评价指标
  27. return model_dict
  28. def prune(args, model):
  29. # 记录BN层权重
  30. # Debugging output
  31. BatchNorm2d_weight = []
  32. for module in model.modules():
  33. if isinstance(module, torch.nn.BatchNorm2d):
  34. BatchNorm2d_weight.append(module.weight.data.clone())
  35. BatchNorm2d_weight_abs = torch.cat(BatchNorm2d_weight, dim=0).abs()
  36. weight_len = len(BatchNorm2d_weight)
  37. # 记录权重与BN层编号的关系
  38. BatchNorm2d_id = []
  39. for i in range(weight_len):
  40. BatchNorm2d_id.extend([i for _ in range(len(BatchNorm2d_weight[i]))])
  41. id_all = torch.tensor(BatchNorm2d_id)
  42. # 筛选
  43. value, index = torch.sort(BatchNorm2d_weight_abs, dim=0, descending=True)
  44. boundary = int(len(index) * args.prune_ratio)
  45. prune_index = index[0:boundary] # 保留参数的下标
  46. prune_index, _ = torch.sort(prune_index, dim=0, descending=False)
  47. prune_id = id_all[prune_index]
  48. # 将保留参数的下标放到每层中
  49. index_list = [[] for _ in range(weight_len)]
  50. for i in range(len(prune_index)):
  51. index_list[prune_id[i]].append(prune_index[i])
  52. # 将每层保留参数的下标换算成相对下标
  53. record_len = 0
  54. for i in range(weight_len):
  55. index_list[i] = torch.tensor(index_list[i])
  56. index_list[i] -= record_len
  57. if len(index_list[i]) == 0: # 存在整层都被减去的情况,至少保留一层
  58. index_list[i] = torch.argmax(BatchNorm2d_weight[i], dim=0).unsqueeze(0)
  59. record_len += len(BatchNorm2d_weight[i])
  60. # 创建剪枝后的模型
  61. args.prune_num = [len(_) for _ in index_list]
  62. prune_model = eval(choice_dict[args.model])
  63. # BN层权重赋值和部分conv权重赋值
  64. index = 0
  65. for module, prune_module in zip(model.modules(), prune_model.modules()):
  66. if isinstance(module, torch.nn.Conv2d): # 更新部分Conv2d层权重
  67. print(f"处理 Conv2d 层,索引:{index},权重形状:{module.weight.data.shape}")
  68. if index > 0 and index - 1 < len(index_list):
  69. # 打印 index_list 状态
  70. print(f"当前层前一层索引列表(index_list[{index - 1}]):{index_list[index - 1]}")
  71. # 检查是否索引越界
  72. if index_list[index - 1].max().item() < module.weight.data.shape[1]: # 检查最大索引是否小于输入通道数
  73. weight = module.weight.data.clone()
  74. if index < len(index_list):
  75. weight = weight[:, index_list[index - 1], :, :]
  76. if prune_module.weight.data.shape == weight.shape:
  77. prune_module.weight.data = weight
  78. else:
  79. print("索引越界,跳过当前层的处理")
  80. elif index == 0:
  81. weight = module.weight.data.clone()[index_list[index]]
  82. if prune_module.weight.data.shape == weight.shape:
  83. prune_module.weight.data = weight
  84. if isinstance(module, torch.nn.BatchNorm2d):
  85. print(f"更新 BatchNorm2d 层,索引:{index},权重形状:{module.weight.data.shape}")
  86. if index < len(index_list) and len(index_list[index]) > 0:
  87. expected_size = module.weight.data.size(0)
  88. actual_size = len(index_list[index])
  89. print(f"期望的大小:{expected_size}, 实际保留的大小:{actual_size}")
  90. if actual_size == expected_size:
  91. prune_module.weight.data = module.weight.data.clone()[index_list[index]]
  92. prune_module.bias.data = module.bias.data.clone()[index_list[index]]
  93. prune_module.running_mean = module.running_mean.clone()[index_list[index]]
  94. prune_module.running_var = module.running_var.clone()[index_list[index]]
  95. else:
  96. print("警告: 剪枝后的大小与期望的 BatchNorm2d 层大小不匹配")
  97. index += 1
  98. return prune_model
  99. class model_prepare:
  100. def __init__(self, args):
  101. self.args = args
  102. def LeNet(self):
  103. from model.LeNet import LeNet
  104. model = LeNet(self.args.input_channels, self.args.output_num, self.args.input_size)
  105. return model
  106. def Alexnet(self):
  107. from model.Alexnet import Alexnet
  108. model = Alexnet(self.args.input_channels, self.args.output_num, self.args.input_size)
  109. return model
  110. def VGG19(self):
  111. from model.VGG19 import VGG19
  112. model = VGG19()
  113. return model
  114. def VGG16(self):
  115. from model.VGG19 import VGG16
  116. model = VGG16(self.args.input_size)
  117. return model
  118. def GoogleNet(self):
  119. from model.GoogleNet import GoogLeNet
  120. model = GoogLeNet(self.args.input_channels, self.args.output_num)
  121. return model
  122. def resnet(self):
  123. from model.resnet import ResNet18
  124. model = ResNet18(self.args.input_channels, self.args.output_num)
  125. return model