07-15 周一 lmdeploy导出迁移因子到量化模型中

news2024/9/21 12:46:56
07-15 周一 lmdeploy导出迁移因子到量化模型中
时间版本修改人描述
2024年7月15日14:57:02V0.1宋全恒新建文档

简介

方案设计

由于norm层的前后导致smoothquant执行量化不好融合,为了降低我事先的难度,所以就不再融合normalization的算子了,因为norm会有变化。因此,在推理执行时,Wv和Wo继续进行量化,Wq, Wk, Wv继续进行量化,但是需要导出迁移前度。

不融合,是norm在前,Q K V在后,但是融合呢?根据这个查询出来的迁移因子,为

但是,

LLama2 模型

image-20240621112016954

 可以看到也是在lmdeploy仓库中的两种方式,即smooth_layers的两个过程。

  • smooth_ln_fcs
  • smooth_fc_fcs
    layer_type = LAYER_TYPE_MAP[type(model).__name__]
    norm_type = NORM_TYPE_MAP[type(model).__name__]
    fc2fcs = FC_FCS_MAP[layer_type]
    norm2fcs = NORM_FCS_MAP[layer_type]

    layers = collect_target_modules(model, layer_type)
    fcs = {}
    for l_name, layer in layers.items():
        name2fc = collect_target_modules(layer, nn.Linear, prefix=l_name)
        fcs.update(name2fc)

    smooth_layers(layers, fc2fcs, norm2fcs, act_scales, -1, device)
  • layers: 长度为32的字典,其中的内容是32层LlamaDecoderLayer
  • fc2fcs: {‘self_attn.v_proj’: [‘self_attn.o_proj’], ‘mlp.up_proj’: [‘mlp.down_proj’]}
  • norm2fc2: {‘input_layernorm’: [‘self_attn.k_proj’, ‘self_attn.q_proj’, ‘self_attn.v_proj’], ‘post_attention_layernorm’: [‘mlp.gate_proj’, ‘mlp.up_proj’]}
  • act_scales: 一共是32 * 7即224个构建块的统计数据,使用的abs_max
  • devce: ‘cuda’

 主要调用了两个函数,smooth_ln_fcssmooth_fc_fcs,对每一层都进行了处理。该函数的主要作用是根据input scales应用权重平滑

{
    'input_layernorm': ['self_attn.k_proj', 'self_attn.q_proj', 'self_attn.v_proj'], 
    'post_attention_layernorm': ['mlp.gate_proj', 'mlp.up_proj']
}
image-20240619172546619

a_scales

    inp_stats = torch.load(work_dir / 'inputs_stats.pth')
    act_scales = inp_stats['absmax']

 保存的是每个模块的输入统计数据。

{'model.layers.0.self_attn.q_proj': tensor([0.1198, 0.0492, 0.0074,  ..., 0.0454, 0.0384, 0.0229],
       dtype=torch.float16), 'model.layers.0.self_attn.k_proj': tensor([0.1198, 0.0492, 0.0074,  ..., 0.0454, 0.0384, 0.0229],
       dtype=torch.float16), 'model.layers.0.self_attn.v_proj': tensor([0.1198, 0.0492, 0.0074,  ..., 0.0454, 0.0384, 0.0229],
       dtype=torch.float16), 'model.layers.0.self_attn.o_proj': tensor([0.0225, 0.0197, 0.0196,  ..., 0.0154, 0.0170, 0.0197],
       dtype=torch.float16), 'model.layers.0.mlp.gate_proj': tensor([0.1963, 0.1783, 0.1750,  ..., 0.1868, 0.1727, 0.1619],
       dtype=torch.float16), 'model.layers.0.mlp.up_proj': tensor([0.1963, 0.1783, 0.1750,  ..., 0.1868, 0.1727, 0.1619],
       dtype=torch.float16), 'model.layers.0.mlp.down_proj': tensor([0.1340, 0.0803, 0.0602,  ..., 0.3337, 0.0664, 0.1180],
       dtype=torch.float16), 'model.layers.1.self_attn.q_proj': tensor([0.3875, 0.3723, 0.3545,  ..., 0.2433, 0.3320, 0.2622],
       dtype=torch.float16), 'model.layers.1.self_attn.k_proj': tensor([0.3875, 0.3723, 0.3545,  ..., 0.2433, 0.3320, 0.2622],
       dtype=torch.float16), 'model.layers.1.self_attn.v_proj': tensor([0.3875, 0.3723, 0.3545,  ..., 0.2433, 0.3320, 0.2622],
       dtype=torch.float16), 'model.layers.1.self_attn.o_proj': tensor([0.2920, 0.3186, 0.1959,  ..., 0.0396, 0.0433, 0.0407],
       dtype=torch.float16), 'model.layers.1.mlp.gate_proj': tensor([0.2852, 0.3372, 0.3159,  ..., 0.3936, 0.2954, 0.3220],
       dtype=torch.float16), 'model.layers.1.mlp.up_proj': tensor([0.2852, 0.3372, 0.3159,  ..., 0.3936, 0.2954, 0.3220],
       dtype=torch.float16), 'model.layers.1.mlp.down_proj': tensor([0.4121, 0.0886, 1.0986,  ..., 0.1576, 0.3171, 1.0928],
       dtype=torch.float16), ...}
image-20240715164644888
act_scales['model.layers.30.self_attn.v_proj'].shape
torch.Size([4096])
layers['model.layers.30'].self_attn.v_proj
Linear(in_features=4096, out_features=4096, bias=False)

由于输入和输出均为4096,所以还看不出来统计是按行进行的还是按列进行的。

layers['model.layers.30'].mlp.down_proj
Linear(in_features=11008, out_features=4096, bias=False)
act_scales['model.layers.30.mlp.down_proj'].shape
torch.Size([11008])

 在给定的 Linear(in_features=11008, out_features=4096, bias=False) 中,权重的形状是 (4096, 11008)

 这是因为对于线性层,权重矩阵的形状通常是 (输出特征数量, 输入特征数量) 。在这里,输出特征数量是 4096 ,输入特征数量是 11008

例如,如果我们有一个简单的线性层 Linear(in_features=2, out_features=3, bias=False) ,那么权重矩阵的形状就是 (3, 2) 。假设输入的数据是 [x1, x2] ,那么通过权重矩阵与输入相乘得到的输出就是 [w11*x1 + w12*x2, w21*x1 + w22*x2, w31*x1 + w32*x2] ,其中 w11, w12, w21, w22, w31, w32 就是权重矩阵中的元素。

根据上面的描述,则统计数据是按照维度跨行进行的,dim=0.

记录运行过程

{
  "metadata": {
    "total_size": 7006265344
  },
  "weight_map": {
    "lm_head.weight": "pytorch_model-00004-of-00004.bin",
    "model.embed_tokens.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.input_layernorm.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.down_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.down_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.gate_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.gate_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.up_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.up_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.post_attention_layernorm.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.k_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.k_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.o_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.o_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.q_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.q_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.v_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.v_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.1.input_layernorm.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.9.self_attn.v_proj.weight": "pytorch_model-00002-of-00004.bin",
    "model.norm.weight": "pytorch_model-00004-of-00004.bin"
  }
}

 layers记录了所有层的信息

'model.layers.0':
LlamaDecoderLayer(
  (self_attn): LlamaSdpaAttention(
    (q_proj): Linear(in_features=4096, out_features=4096, bias=False)
    (k_proj): Linear(in_features=4096, out_features=4096, bias=False)
    (v_proj): Linear(in_features=4096, out_features=4096, bias=False)
    (o_proj): Linear(in_features=4096, out_features=4096, bias=False)
    (rotary_emb): LlamaRotaryEmbedding()
  )
  (mlp): LlamaMLP(
    (gate_proj): Linear(in_features=4096, out_features=11008, bias=False)
    (up_proj): Linear(in_features=4096, out_features=11008, bias=False)
    (down_proj): Linear(in_features=11008, out_features=4096, bias=False)
    (act_fn): SiLU()
  )
  (input_layernorm): LlamaRMSNorm()
  (post_attention_layernorm): LlamaRMSNorm()
)

 即layers是一个字典,其保存了所有的层以及每层的名字。l_name

l_name
'model.layers.0'
{
    'input_layernorm': ['self_attn.k_proj', 'self_attn.q_proj', 'self_attn.v_proj'], 
    'post_attention_layernorm': ['mlp.gate_proj', 'mlp.up_proj']
}

 所以即ln_name依次取input_layernormpost_attention_layernorm

        for ln_name, fc_names in norm2fcs.items():
            a_name = [f'{l_name}.{n}' for n in fc_names][0]
            print(f"a_name: {a_name}")
            ln = layer.get_submodule(ln_name)
            fcs = [layer.get_submodule(n) for n in fc_names]
full_fc_names = [f"{l_name}.{name}" for name in fc_names]
for name in full_fc_names:
    migration_scales[name] = migratrion_scale
fcs[0].weight.shape
torch.Size([11008, 4096])
fcs[0]
Linear(in_features=4096, out_features=11008, bias=False)
scales.shape
torch.Size([4096])

fcs
[Linear(in_features=4...ias=False), Linear(in_features=4...ias=False)]
special variables
function variables
0:
Linear(in_features=4096, out_features=11008, bias=False)
1:
Linear(in_features=4096, out_features=11008, bias=False)
len():
2
concat_w.shape
torch.Size([22016, 4096])

 从上面的输出,输入特征,输出特征对应的weight是反过来的,weight的形状为([out_features, in_features]),并且在计算时,将多个全连接层拼接在一起,而且是按照行拼接的。相当于压在了一起。

act_scales.shape, concat_w.shape, scales.shape
(torch.Size([4096]), torch.Size([12288, 4096]), torch.Size([4096]))

 从上面的输出可以看出,在执行量化时,主要是以列数为准。

过程

 记录迁移尺度向量

 重点关注了ln_fcs,因为需要重新处理layernorm。

 对于fc_fcs,直接融合即可。

错误分析

image-20240717145210912

 vllm程序将使用DefaultModelLoader载入这个模型权重,

 模型结构已经生成

image-20240717145412533

load_weights

 关键是载入权重了

def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]):
        stacked_params_mapping = [
            # (param_name, shard_name, shard_id)
            (".qkv_proj", ".q_proj", "q"),
            (".qkv_proj", ".k_proj", "k"),
            (".qkv_proj", ".v_proj", "v"),
            (".gate_up_proj", ".gate_proj", 0),
            (".gate_up_proj", ".up_proj", 1),
        ]
        params_dict = dict(self.named_parameters())
        for name, loaded_weight in weights:

 上面显示了堆叠的参数映射。

class QKVParallelLinear(ColumnParallelLinear):
    """Linear layers for the attention's QKV transformation.

    Linear layers for the linear transformation of the query, key, and value
    vectors in the attention layer. The weight matrix is concatenated along
    the output dimension. The layer is parallelized along the head dimension.
    When the number of key/value heads is smaller than the number of query
    heads (e.g., multi-query/grouped-query attention), the key/value head may
    be replicated while the query heads are partitioned.

    Args:
        hidden_size: input hidden state size of the transformer.
        head_size: size of each attention head.
        total_num_heads: total number of attention query heads.
        total_num_kv_heads: total number of attention key/value heads. If
                            None, assume total_num_kv_heads = total_num_heads.
        bias: If true, add bias.
        skip_bias_add: This was added to enable performance optimizations where
                       bias can be fused with other element-wise operations. we
                       skip adding bias but instead return it.
        params_dtype: Data type for the parameters.
        quant_config: Quantization configure.
    """

 这主要是input_dim, output_dim不理解导致的问题。

在神经网络和深度学习中,input_dim、output_dim 和 packed_dim 是常见的术语,尤其是在涉及多维数据和张量操作时。下面我将逐一解释这些术语的含义,并使用 PyTorch 演示它们的应用。

input_dim
input_dim 通常表示输入数据的维度或特征数。在神经网络的上下文中,这通常是输入层的大小。例如,在处理图像时,input_dim 可能是每个图像的像素数;在处理文本时,input_dim 可能是词向量的维度。

output_dim
output_dim 通常表示输出数据的维度或特征数。在神经网络的上下文中,这通常是输出层的大小。例如,对于分类任务,output_dim 可能是类别的数量;对于回归任务,output_dim 可能是输出值的数量。

packed_dim
packed_dim 这个术语比较少见,但在某些上下文中可能用来表示压缩或重新排列后的维度。它可能用于描述数据在某种处理后(如批量处理或降维处理)得到的新维度。

 我们来用一个简单的例子演示如何在 PyTorch 中使用这些概念。

假设我们有一个简单的全连接神经网络层(线性层),其输入维度为 input_dim,输出维度为 output_dim。我们会创建一个输入张量(假设有一个批次大小为 batch_size),并通过这个线性层处理。

import torch
import torch.nn as nn

# 定义输入、输出和批次大小
input_dim = 10   # 输入特征数
output_dim = 5   # 输出特征数
batch_size = 4   # 批次大小

# 创建一个全连接层
linear_layer = nn.Linear(input_dim, output_dim)

# 创建一个输入张量(批次大小 x 输入特征数)
input_tensor = torch.randn(batch_size, input_dim)

# 通过线性层处理
output_tensor = linear_layer(input_tensor)

print("Input Tensor Shape:", input_tensor.shape)
print("Output Tensor Shape:", output_tensor.shape)

输入张量 input_tensor:形状为 (batch_size, input_dim),表示一个批次中有 batch_size 个样本,每个样本有 input_dim 个特征。

线性层 linear_layer:输入维度为 input_dim,输出维度为 output_dim,用于将输入特征转换为输出特征。

输出张量 output_tensor:形状为 (batch_size, output_dim),表示一个批次中有 batch_size 个样本,每个样本有 output_dim 个特征。

代码编辑

root@516d350076b6:/mnt/self-define/sunning/lmdeploy/lmdeploy# git diff 63c5da6051c85a13087ac7ef6935a89c05e0a5cf aeda1ac7992e39c86f55de3aeb2be95e60856030
diff --git a/lmdeploy/lite/apis/smooth_quant.py b/lmdeploy/lite/apis/smooth_quant.py
index 3d1bd2b..7b432d5 100644
--- a/lmdeploy/lite/apis/smooth_quant.py
+++ b/lmdeploy/lite/apis/smooth_quant.py
@@ -124,20 +124,18 @@ def smooth_quant(model: str,
         awq_layers(layers, fc2fcs, norm2fcs, act_scales, awq_ratios, -1,
                    device)
     else:
-        migration_scales =  smooth_layers(layers, fc2fcs, norm2fcs, act_scales, -1, device)
+        smooth_layers(layers, fc2fcs, norm2fcs, act_scales, -1, device)
 
     rmsnorms = collect_target_modules(model, norm_type)
 
     for name, linear in fcs.items():
         linear.to(device)
         q_linear = QLinear.from_float(linear)
-        q_linear.migration_scale.copy_(migration_scales[name])
         parent_name, _, child_name = name.rpartition('.')
         parent = model.get_submodule(parent_name)
         setattr(parent, child_name, q_linear)
         linear.to('cpu')
-        
-    #TODO <E9><9C><80><E8><A6><81><E6><B3><A8><E9><87><8A><E6><8E><89><E8><AF><A5><E6><AE><B5><E8><90><BD>
+
     for name, norm in rmsnorms.items():
         norm.to(device)
         q_norm = QRMSNorm.from_float(norm)
diff --git a/lmdeploy/lite/quantization/awq.py b/lmdeploy/lite/quantization/awq.py
index f321177..508a26c 100644
--- a/lmdeploy/lite/quantization/awq.py
+++ b/lmdeploy/lite/quantization/awq.py
@@ -248,7 +248,7 @@ def smooth_layers(layers,
                   group_size=-1,
                   device='cuda'):
     """Apply weight smoothing based on input scales."""
-    migration_scales = {}
+
     for l_name, layer in layers.items():
         layer.to(device)
         for ln_name, fc_names in norm2fcs.items():
@@ -256,24 +256,18 @@ def smooth_layers(layers,
 
             ln = layer.get_submodule(ln_name)
             fcs = [layer.get_submodule(n) for n in fc_names]
-            migration_scale = smooth_ln_fcs(ln, fcs, a_scales[a_name], group_size)
-            full_fc_names = [f"{l_name}.{name}" for name in fc_names]
-            migration_scales.update({name: migration_scale for name in full_fc_names})
+            smooth_ln_fcs(ln, fcs, a_scales[a_name], group_size)
 
         for f_name, fc_names in fc2fcs.items():
             a_name = [f'{l_name}.{n}' for n in fc_names][0]
 
             fc = layer.get_submodule(f_name)
             fcs = [layer.get_submodule(n) for n in fc_names]
-            
-            migration_scale = smooth_fc_fcs(fc, fcs, a_scales[a_name], group_size)
-            full_fc_names = [f"{l_name}.{name}" for name in fc_names]
-            migration_scales.update({name: migration_scale for name in full_fc_names})
+
+            smooth_fc_fcs(fc, fcs, a_scales[a_name], group_size)
 
         layer.to('cpu')
         print(f'{l_name} smooth weight done.')
-    
-    return migration_scales
 
 
 def pseudo_quantize_tensor(w,
diff --git a/lmdeploy/pytorch/models/q_modules.py b/lmdeploy/pytorch/models/q_modules.py
index be3d97f..001fab7 100644
--- a/lmdeploy/pytorch/models/q_modules.py
+++ b/lmdeploy/pytorch/models/q_modules.py
@@ -96,11 +96,6 @@ class QLinear(nn.Module):
         self.register_buffer(
             'scale',
             torch.empty((out_features, 1), device=device, dtype=torch.float32))
-        
-        self.register_buffer(
-            'migration_scale',
-            torch.empty((in_features), device=device, dtype=torch.float32))
-        
         if bias:
             self.register_buffer('bias',
                                  torch.empty(out_features, **factory_kwargs))

 主要是修改的如下的文件:

/lmdeploy/pytorch/models/q_modules.py
/lmdeploy/lite/apis/smooth_quant.py
/lmdeploy/lite/quantization/awq.py

 这样执行之后导出之后,程序会将模型顺利导出到 pytorch_model.bin.index.json

{
  "metadata": {
    "total_size": 7010820096
  },
  "weight_map": {
    "lm_head.weight": "pytorch_model-00004-of-00004.bin",
    "model.embed_tokens.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.input_layernorm.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.down_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.down_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.down_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.gate_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.gate_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.gate_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.up_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.up_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.mlp.up_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.post_attention_layernorm.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.k_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.k_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.k_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.o_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.o_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.o_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.q_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.q_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.q_proj.weight": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.v_proj.migration_scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.v_proj.scale": "pytorch_model-00001-of-00004.bin",
    "model.layers.0.self_attn.v_proj.weight": "pytorch_model-00001-of-00004.bin",
     ...,
     "model.norm.weight": "pytorch_model-00004-of-00004.bin"
  }
}
  

 在llama的模型结构中,单层共有7个线性层,因此,对于每个Linear 会有3个部分,因此,会得到21个成分。

理解量化的过程

论文理论

image-20240718101824373 image-20240718101835526

上图是很清楚的,我们首先统计出对于X,X按列取了最大值,得到一个向量,当然这个绝对值最大值是需要校准的。这个是跨行进行,即第0维。

[2, 16, 2, 9]

然后对于权重,我们求按列的最大值,即第一维度,求取绝对值最大值,得到了最大向量

[2, 1, 2, 1]

 我们采用α=0.5,毕竟这是一个sweet number.

就可以得到迁移向量,s=

[1, 4, 1, 3]

计算w_scale

 观察实际的代码演示,可以主要查看smooth_ln_fcs这个接口

    concat_w = torch.cat([fc.weight for fc in fcs], dim=0)
    w_scales = get_weight_scale(concat_w, group_size)

 从上述代码看,concat_w相当于将权重按照0维度拼接在一起。实现的效果是将张量连接concatenate在一起,把它们端对端地叠起来形成一个更大的张量。我们只需要提供张量列表,并给出沿那个轴连接。

  • 轴0, 按行,形状的第一个元素
  • 按列 轴1, 形状的第二个元素。

 按照轴0进行拼接,则结果的形状为(x.shape(0)+y.shape(0), x.shape(1)),即列的数量保持不变,而行变多,相当于积累了更多的样本数量。

 按照轴1进行拼接,则结果的形状为(x.shape(0), x.shape(1)+y.shape(1)),即行数保持不变,而列数增多。

X = np.arange(12).reshape(3, 4)
Y = np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
np.concatenate([X, Y], axis=0), np.concatenate([X, Y], axis=1)

 从结果可以看出实际效果如下所示:

(array([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [ 2.,  1.,  4.,  3.],
        [ 1.,  2.,  3.,  4.],
        [ 4.,  3.,  2.,  1.]]),
 array([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
        [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
        [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]]))

 所以关键是get_weight_scale函数,获取了权重的scale

@torch.no_grad()
def get_weight_scale(weight, q_group_size=-1):
    org_shape = weight.shape
    if q_group_size > 0:
        weight = weight.view(-1, q_group_size)
    scale = weight.abs() / weight.abs().amax(dim=1, keepdim=True)
    scale = scale.view(org_shape)
    scale = scale.mean(0)
    return scale

 上述函数,对于权重数据进行归一化,然后最后按照行求取了均值。然后就是恢复形状,求出了均值。可以看到scale的形状和input_dime的形状是相同的。

该函数用于计算给定权重参数的权重缩放因子。函数接受两个参数:weight(待计算权重缩放因子的权重参数)和q_group_size(量化组大小,默认为-1)。

  1. 首先,函数获取原始权重参数的形状并保存为org_shape
  2. 如果q_group_size大于0,则将权重参数视图重塑为(-1, q_group_size)
  3. 接下来,函数计算每个元素的绝对值,并将其除以每行的最大绝对值(使用amax函数),得到一个缩放因子的视图。
  4. 然后,将该视图重新调整为原始形状。
  5. 最后,函数计算所有通道的平均缩放因子,并返回该平均值作为权重缩放因子。

 进一步,发散,可以得到nn.Linear 这个Module

在 PyTorch 中,nn.Linear 模块表示一个线性层(也称为全连接层或密集层),用于实现线性变换。其权重张量的输入维度和输出维度与输入特征和输出特征之间有直接关系。

nn.Linear 模块的定义

nn.Linear 的构造函数如下:

torch.nn.Linear(in_features, out_features, bias=True)
  • in_features:输入特征的数量,即输入张量的最后一个维度的大小。
  • out_features:输出特征的数量,即输出张量的最后一个维度的大小。
  • bias:是否使用偏置项。默认值为 True

权重张量的维度

  • 权重张量 weight 的维度是 (out_features, in_features)。这意味着每个输出特征有一组与输入特征相对应的权重。

输入特征和输出特征的关系

  1. 输入特征:输入张量的最后一个维度的大小。
  2. 输出特征:输出张量的最后一个维度的大小。

 关于形状示意图,可以查看下图:

image-20240718143937798

计算过程

对于一个输入张量 x(形状为 (batch_size, in_features)),通过线性层后的输出张量 y 的形状为 (batch_size, out_features)。计算过程如下:

y=x⋅WT+by = x \cdot W^T + by=x⋅WT+b

其中:

  • x 是输入张量,形状为 (batch_size, in_features)
  • W 是权重张量,形状为 (out_features, in_features)
  • b 是偏置项(如果存在),形状为 (out_features)
  • y 是输出张量,形状为 (batch_size, out_features)

该函数的作用是在量化神经网络权重时,为每个权重通道计算一个缩放因子,以保持量化后的权重尽可能接近原始权重。

    for fc in fcs:
        fc.weight.mul_(scales.view(1, -1))
# 修改全连接层(fc)权重
# 使用`scales`张量的元素与`fc.weight`对应列的元素相乘
fc.weight.mul_(scales.view(1, -1))
功能解析:

当scales是一个具有torch.Size([4096])形状的一维张量时:

scales.view(1, -1): 此操作将scales从形状(4096,)转换为(1, 4096)。这样做的目的是为了能够与fc.weight的每一列进行逐元素相乘。假设fc.weight的形状为(N, 4096),其中N是输出特征的数量。

fc.weight.mul_(...): 这一步将scales的每一项与fc.weight的相应列进行乘法运算,即scales中的第一个元素会与fc.weight的第一列相乘,第二个元素与第二列相乘,以此类推。由于mul_是原地操作,fc.weight的值会被直接更新。

关键点:

这种操作常用于神经网络训练中,比如在特定情况下调整权重,如正则化、学习率衰减或应用特定的权重初始化策略。
确保scales的长度与fc.weight的列数相匹配,否则将引发形状不兼容的错误。

总结

 在这个过程中,还是有很多的东西不明白的,很多的torch的API需要深刻的理解和体会,深度学习,也基本上都是逻辑组成的,了解大概之后,就可以上手了,不要有畏难的情绪。不过在这个过程中,真的和熟练的人沟通,放下那个可怜的不好意思,认真的去理解别人头脑中已经理解的东西,才是真正的王道

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1948263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3编程-import.meta.glob实现动态路由(菜单)

import.meta.glob 是vite提供的批量懒加载组件的方法 本地开发环境&#xff1a; const modules import.meta.glob(../views/**/*.vue)这段代码返回的modules是一个Map&#xff1a; key是vue文件的相对路径&#xff0c;值是一个函数&#xff0c;将函数打印出来&#xff0c;如…

【微信小程序实战教程】之微信小程序原生开发详解

微信小程序原生开发详解 微信小程序的更新迭代非常频繁&#xff0c;几乎每个月都会有新版本发布&#xff0c;这就会让初学者感觉到学习的压力和难度。其实&#xff0c;我们小程序的每次版本迭代都是在现有小程序架构基础之上进行更新的&#xff0c;如果想要学好小程序开发技术&…

配置mysql8.0.21版本docker-compose启动容器

1. 总览 2 docker-compose.xml配置 version: 3 services:mysql:image: 192.168.188.131:8000/mysqlrestart: alwaysvolumes:- ./data:/var/lib/mysql- ./my.cnf:/etc/mysql/my.cnf- ./mysql-files:/var/lib/mysql-files- ./log/mysql:/var/log/mysqlenvironment:MYSQL_ROOT_PA…

Shell实现服务自动部署

一、环境 注意&#xff1a; nfs.example.com应该为nfs.exam.com 172.25.250.101-172.25.250.105 共 5 个 IP 地址由servera.exam.com服务器进行提供。 172.25.250.106 由 serverb.exam.com 服务器进行提供。 二、需求 项目需求&#xff1a; 1. 172.25.250.101 主机上的 W…

UEFI DebugLib 介绍

1.我们调试中常用Debug 打印信息&#xff0c;这些会输出到BIOS串口日志中 EFI_STATUSEFIAPIHelloWorld2(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){EFI_STATUS Status;StatusEFI_SUCCESS;gST->ConOut->OutputString(gST->ConOut,L&q…

如何保护您的 WordPress 不被黑?

明月可以说是见到过太多 WordPress 网站被黑的示例了&#xff0c;加上平时明月也会接一些 WordPress 疑难杂症的解决服务订单&#xff0c;所以这方面绝对是专业对口了。作为一个资深 WordPress 博客站长&#xff0c;谁都有被黑过的经历&#xff0c;都是一步步走过来的&#xff…

从零入门AI for Science(AI+化学)#Datawhale AI 夏令营

基于天池平台“第二届世界科学智能大赛 物质科学赛道&#xff1a;催化反应产率预测”使用平台 我的Notebook 魔搭社区 https://modelscope.cn/my/mynotebook/preset 赛事官网 上海科学智能研究院 http://competition.sais.com.cn/competitionDetail/532233/myScore Task1 …

七、SpringBoot日志

1. 得到日志对象 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //打印日志…

【Vue实战教程】之Vue工程化项目详解

Vue工程化项目 随着多年的发展&#xff0c;前端越来越模块化、组件化、工程化&#xff0c;这是前端发展的大趋势。webpack是目前用于构建前端工程化项目的主流工具之一&#xff0c;也正变得越来越重要。本章节我们来详细讲解一下如何使用webpack搭建Vue工程化项目。 1 使用we…

Web渗透-WAF绕过技巧

一、WAF简介 Web应用防护系统&#xff08;也称为&#xff1a;网站应用级入侵防御系统。英文&#xff1a;Web Application Firewall&#xff0c;简称&#xff1a; WAF&#xff09;。利用国际上公认的一种说法&#xff1a;Web应用防火墙是通过执行一系列针对HTTP/HTTPS的安全策略…

叮!2024 龙蜥操作系统大会议题征集正式启动

定啦&#xff01;2024 龙蜥操作系统大会&#xff08;OpenAnolis Conference&#xff0c;以下简称“龙蜥大会”&#xff09;将于 2024 年 8 月 30 日在北京中关村国家自主创新示范区会议中心盛大召开。 2024 龙蜥大会由中关村科学城管委会、海淀区委网信办、中国开源软件推进联…

配置sublime的中的C++编译器(.sublime-build),实现C++20

GCC 4.8: 支持 C11 (部分) GCC 4.9: 支持 C11 和 C14 (部分) GCC 5: 完全支持 C14 GCC 6: 支持 C14 和 C17 (部分) GCC 7: 支持 C17 (大部分) GCC 8: 完全支持 C17&#xff0c;部分支持 C20 GCC 9: 支持更多的 C20 特性 GCC 10: 支持大部分 C20 特性 GCC 11: 更全面地支持 C20 …

uniapp开发精选短视频视频小程序实战笔记20240725,实现顶部轮播图和热门短剧

创建项目 创建项目,叫video_app。 在pages.json里面修改一下标题: 新建search搜索页面和me我的页面。 此时界面预览效果如下: 引入静态资源 主要是static里面的内容,全部复制过来。 配置底部导航栏 pages.json,放到顶层,和全部样式同级: "tabBar&quo…

Java的类加载机制

Java的类加载机制是指将类的字节码文件&#xff08;.class文件&#xff09;加载到JVM中并将其转换为Class对象的过程。这个过程由类加载器&#xff08;ClassLoader&#xff09;完成。Java的类加载机制具有动态性和灵活性&#xff0c;使得Java能够支持动态加载类、实现模块化开发…

4s店客户管理系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;门店管理&#xff0c;车展管理&#xff0c;汽车品牌管理&#xff0c;新闻头条管理&#xff0c;预约试驾管理&#xff0c;我的收藏管理&#xff0c;系统管理 微信端账号功能包括&a…

HTTP请求入参类型解读

HTTP请求入参类型解读 Content-Type 在HTTP请求中&#xff0c;Content-Type请求头用于指示资源的MIME类型&#xff0c;即请求体的媒体类型。它告诉服务器实际发送的数据类型是什么&#xff0c;以便服务器能够正确地解析和处理这些数据。Content-Type可以有多种值&#xff0c;…

13.2 MongoDB

13.2 MongoDB 1. 概述2. docker安装3. SpringBoot整合MongoDB3.1 依赖3.2 配置连接1. 基于`yml`配置2. 基于配置类配置3.3 启动项坑1坑23.4 新增业务1. 实体类映射2. 数据层3. 业务层4. 控制层5. 测试结果3.5 单条记录查询业务1. 数据层2. 业务层3. 控制层4. 断点测试3.6 分页查…

代码随想录算法训练营day6 | 242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1.两数之和

文章目录 哈希表键值 哈希函数哈希冲突拉链法线性探测法 常见的三种哈希结构集合映射C实现std::unordered_setstd::map 小结242.有效的字母异位词思路复习 349. 两个数组的交集使用数组实现哈希表的情况思路使用set实现哈希表的情况 202. 快乐数思路 1.两数之和思路 总结 今天是…

FoundationDB 基本使用

目录 一、FoundationDB介绍 二、安装单机版FoundationDB 2.1 下载安装程序 2.2 安装FoundationDB 2.3 修改配置信息 2.4 管理FoundationDB服务 三、fdbcli的常用命令 3.1连接数据库 3.2退出fdbcli 3.3查看版本 3.4 写模式 3.5写入键值 3.6读取键值 3.7删除键值 …

花几千上万学习Java,真没必要!(二十七)

1、Math类&#xff1a; package mathtest.com; public class MathDemo { public static void main(String[] args) { // 定义圆的半径 double radius 5.0; // 计算并打印圆的周长 double circumference 2 * Math.PI * radius; System.out.printf("圆的周长: %.2f…