Read_Bert_Code

GitHub
665 152 中等 1 次阅读 1个月前语言模型开发框架
AI 解读 由 AI 自动生成,仅供参考

Read_Bert_Code 是一个专为深度学习爱好者打造的 BERT 源码精读项目。它基于 PyTorch 框架,以文本分类任务为具体场景,将复杂的 BERT 模型代码拆解为可运行、可调试的逐步教程。

许多开发者在使用 BERT 时往往只调用现成库,对其内部架构知之甚少,这在需要进行模型压缩或定制化改进的生产环境中会成为瓶颈。Read_Bert_Code 正是为了解决这一痛点而生。它不仅仅提供了代码,更通过详细的断点调试指南,带领用户从主函数入口开始,一步步解析命令行参数处理、设备配置(CPU/GPU/分布式)、数据处理器加载等核心流程。项目还包含了将谷歌官方 TensorFlow 预训练模型转换为 PyTorch 格式的完整脚本,并准备了精简版的中文数据集,让用户能零门槛启动本地调试。

该项目特别适合希望深入理解 Transformer 架构原理的 AI 研究人员、算法工程师以及计算机专业的学生。如果你不满足于“黑盒”式调用,而是想亲手摸清 BERT 的每一行代码逻辑,甚至为后续学习 VIT、SwinTRM 等衍生模型打下坚实基础,那么 Read_Bert_Code 将是你不可多得的实战向导。通过跟随项目的节奏,你将在真实的代码运行中,彻底掌握 BERT 的运作机制。

使用场景

某算法工程师接到任务,需将庞大的 BERT 中文预训练模型压缩以适应移动端部署,但在尝试剪枝和量化时,因对底层架构理解不深而频频受阻。

没有 Read_Bert_Code 时

  • 源码如黑盒:面对成千上万行的 PyTorch 实现代码,难以定位注意力机制和前馈网络的具体交互逻辑,只能凭印象猜测。
  • 调试无从下手:试图修改模型结构时,不清楚数据在 run_classifier.py 中的流转细节,导致断点设置盲目,排查效率极低。
  • 环境配置繁琐:手动下载谷歌预训练模型并转换为 PyTorch 格式时,常因路径错误或参数不匹配导致加载失败,浪费大量时间在环境搭建上。
  • 理论脱离实践:虽然读过 Transformer 论文,但无法将抽象的公式与具体的代码行对应起来,导致压缩策略缺乏理论支撑,容易破坏模型性能。

使用 Read_Bert_Code 后

  • 逐行透视架构:借助仓库中详细的步骤解读和断点调试指南,清晰看到了从输入嵌入到输出分类的完整数据流向,彻底搞懂了内部结构。
  • 精准定位修改点:跟随作者对 main 函数及参数解析的拆解,快速锁定了需要调整的核心代码块,安全地实施了层剪枝操作。
  • 开箱即用流程:直接复用仓库提供的模型转换脚本和数据集准备方案,几分钟内就完成了从 TensorFlow 到 PyTorch 的模型迁移与环境验证。
  • 知行合一优化:通过代码与 PPT 的对照学习,将理论知识映射到具体实现,制定了科学的量化方案,在保持准确率的同时成功缩小了模型体积。

Read_Bert_Code 将晦涩的 BERT 源码转化为可视化的学习路径,让开发者从“盲目调参”进阶为“掌控内核”,极大降低了大模型落地应用的门槛。

运行环境要求

操作系统
  • Linux
  • macOS
  • Windows
GPU
  • 非必需
  • 支持 CPU 或 GPU 运行
  • 若使用 GPU,代码自动检测可用设备 (torch.cuda.is_available),未指定具体型号、显存大小或 CUDA 版本要求
内存

未说明

依赖
notes1. 需手动下载谷歌中文预训练模型 (chinese_L-12_H-768_A-12.zip) 并使用脚本转换为 PyTorch 格式 (.bin)。2. 数据集使用 Tnews,README 中作者仅截取部分数据 (训练/测试/开发各 1k) 用于代码解读,非完整数据集。3. 推荐使用 PyCharm 导入项目进行断点调试以学习代码细节。4. 运行命令中包含多 GPU 和分布式训练参数配置,但默认配置可适应单机 CPU 环境。
python未说明
pytorch (torch)
transformers (隐含,用于 BertModel 等类)
numpy (隐含,用于数据处理)
Read_Bert_Code hero image

快速开始

注解: Transformer、VIT、SwinTRM、BERT 模型从零解读的代码和PPT我移动到了这个仓库:

https://github.com/DA-southampton/TRM_tutorial

Bert源代码解读-以BERT文本分类代码为例子

Bert在生产环境的应用需要进行压缩,这就要求对Bert结构很了解,这个仓库会一步步解读Bert源代码(pytorch版本)。

代码和数据介绍

首先 对代码来说,借鉴的是这个仓库

我直接把代码clone过来,放到了本仓库,重新命名为bert_read_step_to_step。

我会使用这个代码,一步步运行bert关于文本分类的代码,然后同时记录下各种细节包括自己实现的情况。

运行之前,需要做两个事情。

准备预训练模型

一个是预训练模型的准备,我使用的是谷歌的中文预训练模型:chinese_L-12_H-768_A-12.zip,模型有点大,我就不上传了,如果本地不存在,就点击这里直接下载,或者直接命令行运行

wget https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip

预训练模型下载下来之后,进行解压,然后将tf模型转为对应的pytorch版本即可。对应代码如下:

export BERT_BASE_DIR=/path/to/bert/chinese_L-12_H-768_A-12

python convert_tf_checkpoint_to_pytorch.py \
  --tf_checkpoint_path $BERT_BASE_DIR/bert_model.ckpt \
  --bert_config_file $BERT_BASE_DIR/bert_config.json \
  --pytorch_dump_path $BERT_BASE_DIR/pytorch_model.bin

转化成功之后,将模型放入到仓库对应位置:

Read_Bert_Code/bert_read_step_to_step/prev_trained_model/

并重新命名为:

 bert-base-chinese

准备文本分类训练数据

第二个事情就是准备训练数据,这里我准备做一个文本分类任务,使用的是Tnews数据集,这个数据集来源是这里,分为训练,测试和开发集,我已经上传到了仓库中,具体位置在

Read_Bert_Code/bert_read_step_to_step/chineseGLUEdatasets/tnews

需要注意的一点是,因为我只是为了了解内部代码情况,所以准确度不是在我的考虑范围之内,所以我只是取其中的一部分数据,其中训练数据使用1k,测试数据使用1k,开发数据1k。

准备就绪,使用pycharm导入项目,准备调试,我的调试文件是 run_classifier.py文件,对应的参数为

--model_type=bert --model_name_or_path=prev_trained_model/bert-base-chinese --task_name="tnews" --do_train --do_eval --do_lower_case --data_dir=./chineseGLUEdatasets/tnews --max_seq_length=128 --per_gpu_train_batch_size=16 --per_gpu_eval_batch_size=16 --learning_rate=2e-5 --num_train_epochs=4.0 --logging_steps=100 --save_steps=100 --output_dir=./outputs/tnews_output/ --overwrite_output_dir

然后对run_classifier.py 进行调试,我会在下面是调试的细节

1.main函数进入

首先是主函数位置打入断点,位置在这里,然后进入看一下主函数的情况

##主函数打上断点
if __name__ == "__main__":
    main()##主函数进入

2.解析命令行参数

这里这里就是在解析命令行参数,是常规操作,主要是什么模型名称,模型地址,是否进行测试等等。比较简单,直接过就可以了。

3.判断一些情况

这里这里是一些常规的判断:

判断是否存在输出文件夹

判断是否需要远程debug

判断单机cpu训练还是单机多gpu训练,还是多机分布式gpu训练,这个有两个参数进行控制

一个是args.local_rank == -1 or args.no_cuda ,没理解错误的话,简单来讲,-1代表CPU或者是单机多GPU训练,

具体可以看代码如下:

if args.local_rank == -1 or args.no_cuda:
    device = torch.device("cuda" if torch.cuda.is_available() and not args.no_cuda else "cpu")
    args.n_gpu = torch.cuda.device_count()
else:  # Initializes the distributed backend which will take care of sychronizing nodes/GPUs
    torch.cuda.set_device(args.local_rank)
    device = torch.device("cuda", args.local_rank)
    torch.distributed.init_process_group(backend='nccl')
    args.n_gpu = 1

4.获取任务对应Processor

获取任务对应的相应processor,这个对应的函数就是需要我们自己去定义的处理我们自己输入文件的函数,位置在这里,代码如下:

processor = processors[args.task_name]()

这里我们使用的是,这个结果返回的是一个类,我们使用的是如下的类:

TnewsProcessor(DataProcessor)

具体代码位置在这里,

4.1 TnewsProcessor

仔细分析一下TnewsProcessor,首先继承自DataProcessor

点击此处打开折叠代码
## DataProcessor在整个项目的位置:processors.utils.DataProcessor
class DataProcessor(object):
    def get_train_examples(self, data_dir):
        raise NotImplementedError()

    def get_dev_examples(self, data_dir):
        raise NotImplementedError()

    def get_labels(self):
        raise NotImplementedError()

    @classmethod
    def _read_tsv(cls, input_file, quotechar=None):
        with open(input_file, "r", encoding="utf-8-sig") as f:
            reader = csv.reader(f, delimiter="\t", quotechar=quotechar)
            lines = []
            for line in reader:
                lines.append(line)
            return lines

    @classmethod
    def _read_txt(cls, input_file):
        """Reads a tab separated value file."""
        with open(input_file, "r") as f:
            reader = f.readlines()
            lines = []
            for line in reader:
                lines.append(line.strip().split("_!_"))
            return lines

然后它自己包含五个函数,分别是读取训练集,开发集数据,获取返回label,制作bert需要的格式的数据

接下来看一下 TnewsProcessor代码格式:

点击此处打开折叠代码
class TnewsProcessor(DataProcessor):

    def get_train_examples(self, data_dir):
        """See base class."""
        return self._create_examples(
            self._read_txt(os.path.join(data_dir, "toutiao_category_train.txt")), "train")

    def get_dev_examples(self, data_dir):
        """See base class."""
        return self._create_examples(
            self._read_txt(os.path.join(data_dir, "toutiao_category_dev.txt")), "dev")

    def get_test_examples(self, data_dir):
        """See base class."""
        return self._create_examples(
            self._read_txt(os.path.join(data_dir, "toutiao_category_test.txt")), "test")

    def get_labels(self):
        """See base class."""
        labels = []
        for i in range(17):
            if i == 5 or i == 11:
                continue
            labels.append(str(100 + i))
        return labels

    def _create_examples(self, lines, set_type):
        """Creates examples for the training and dev sets."""
        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s-%s" % (set_type, i)
            text_a = line[3]
            if set_type == 'test':
                label = '0'
            else:
                label = line[1]
            examples.append(
                InputExample(guid=guid, text_a=text_a, text_b=None, label=label))
        return examples

这里有一点需要提醒大家,如果说我们使用自己的训练数据,有两个方法,第一个就是把数据格式变化成和我们测试用例一样的数据,第二个就是我们在这里更改源代码,去读取我们自己的数据格式

5.加载预训练模型

代码比较简单,就是调用预训练模型,不详细介绍了

点击此处打开折叠代码
config_class, model_class, tokenizer_class = MODEL_CLASSES[args.model_type]
config = config_class.from_pretrained(args.config_name if args.config_name else args.model_name_or_path, num_labels=num_labels, finetuning_task=args.task_name)
tokenizer = tokenizer_class.from_pretrained(args.tokenizer_name if args.tokenizer_name else args.model_name_or_path, do_lower_case=args.do_lower_case)
model = model_class.from_pretrained(args.model_name_or_path, from_tf=bool('.ckpt' in args.model_name_or_path), config=config)

6.训练模型-也是最重要的部分

训练模型,从主函数这里看就是两个步骤,一个是加载需要的数据集,一个是进行训练,代码位置在这里。大概代码就是这样:

train_dataset = load_and_cache_examples(args, args.task_name, tokenizer, data_type='train')
global_step, tr_loss = train(args, train_dataset, model, tokenizer)

两个函数,我们一个个看:

6.1 加载训练集

我们先看一下第一个函数,load_and_cache_examples 就是加载训练数据集,代码位置在这里。大概看一下这个代码,核心操作有三个。

第一个核心操作,位置在这里,代码如下:

examples = processor.get_train_examples(args.data_dir)

这个代码是为了利用processor读取训练集,很简单。

这里得到的example大概是这样的(这个返回形式在上面看processor的时候很清楚的展示了):

guid='train-0'
label='104'
text_a='今天股票形式不怎么样啊'
text_b=None

第二个核心操作是convert_examples_to_features讲数据进行转化,也很简单。

代码位置在这里。代码如下:

features = convert_examples_to_features(examples,tokenizer,label_list=label_list,max_length=args.max_seq_length,output_mode=output_mode,pad_on_left=bool(args.model_type in ['xlnet']),                                                pad_token=tokenizer.convert_tokens_to_ids([tokenizer.pad_token])[0],
pad_token_segment_id=4 if args.model_type in ['xlnet'] else 0,

我们进入这个函数看一看里面究竟是咋回事,位置在:

processors.glue.glue_convert_examples_to_features

做了一个标签的映射,'100'->0 '101'->1...

接着获取输入文本的序列化表达:input_ids, token_type_ids;形式大概如此:

'input_ids'=[101, 5500, 4873, 704, 4638, 4960, 4788, 2501, 2578, 102]

'token_type_ids'=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

获取attention mask:attention_mask = [1 if mask_padding_with_zero else 0] * len(input_ids)

结果形式如下:[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

计算出当前长度,获取pading长度,比如我们现在长度是10,那么需要补到128,pad就需要118个0.

这个时候,我们的input_ids 就变成了上面的列表后面加上128个0.然后我们的attention_mask就变成了上面的形式加上118个0,因为补长的并不是我们的第二个句子,我们压根没第二个句子,所以token_type_ids是总共128个0

每操作一个数据之后,我们需要做的是

features.append(InputFeatures(input_ids=input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids,
label=label,
input_len=input_len))##长度为原始长度,这里应该是10,不是128

InputFeatures 在这里就是将转化之后的特征存储到一个新的变量中

在将所有原始数据进行特征转化之后,我们得到了features列表,然后将其中的元素转化为tensor形式,随后

第三个是将转化之后的新数据tensor化,然后使用TensorDataset构造最终的数据集并返回,

dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_lens,all_labels)

6.2 训练模型-Train函数

我们来看第二个函数,就是train的操作。

6.2.1 常规操作

首先都是一些常规操作。

对数据随机采样:RandomSampler

DataLoader读取数据

计算总共训练步数(梯度累计),warm_up 参数设定,优化器,是否fp16等等

然后一个batch一个batch进行训练就好了。 这里最核心的代码就是下面的把数据和参数送入到模型中去:

outputs = model(**inputs)

我们是在进行一个文本分类的demo操作,使用的是Bert中对应的 BertForSequenceClassification 这个类。

我们直接进入这个类看一下里面函数究竟是啥情况。

6.2.2 Bert分类模型:BertForSequenceClassification

主要代码代码如下:

点击此处打开折叠代码
##reference: transformers.modeling_bert.BertForSequenceClassification 
class BertForSequenceClassification(BertPreTrainedModel):
    def __init__(self, config):
				...
        ...
        self.bert = BertModel(config)
        self.dropout = nn.Dropout(config.hidden_dropout_prob)
        self.classifier = nn.Linear(config.hidden_size, self.config.num_labels)

    def forward(self, input_ids, attention_mask=None, token_type_ids=None,position_ids=None, head_mask=None, labels=None):
        outputs = self.bert(input_ids,
                            attention_mask=attention_mask,
                            token_type_ids=token_type_ids,
                            position_ids=position_ids, 
                            head_mask=head_mask)
        ##zida注解:注意看init中,定义了self.bert就是BertModel,所以我们需要的就是看一看BertModel中数据怎么进入的
        pooled_output = outputs[1]
        pooled_output = self.dropout(pooled_output)
				...
        ...
        return outputs  # (loss), logits, (hidden_states), (attentions)

这个类最核心的有两个部分,第一个部分就是使用了 BertModel 获取Bert的原始输出,然后使用 cls的输出继续做后续的分类操作。比较重要的是 BertModel,我们直接进入看 BertModel 这个类的内部情况。代码如下:

然后我们看一下BertModel这个模型究竟是怎么样的

6.2.1.1 BertModel

代码如下:

点击此处打开折叠代码
class BertModel(BertPreTrainedModel):
    def __init__(self, config):
        super().__init__(config)
        self.embeddings = BertEmbeddings(config)
        self.encoder = BertEncoder(config)
        self.pooler = BertPooler(config)

    def forward(self, input_ids, attention_mask=None, token_type_ids=None, position_ids=None, head_mask=None):
        # Embedding layer
        embedding_output = self.embeddings(input_ids, token_type_ids, position_ids, head_mask)
        # Encoder
        encoder_outputs = self.encoder(embedding_output, attention_mask)
        # Pooler
        pooled_output = self.pooler(encoder_outputs)
        return embedding_output, encoder_outputs, pooled_output

BertModel 是整个Bert架构的核心组件,负责处理输入文本的嵌入、编码以及池化操作。它 состоит из трех основных частей:

  1. Embedding Layer: Здесь происходит преобразование входного текста в векторное представление с использованием специальных слоев, таких как токен-эмбеддинги, позиционные эмбеддинги и типовые эмбеддинги.

  2. Encoder: Это серия трансформерных блоков, которые обрабатывают эмбеддинги, извлекая из них семантическую информацию. Каждый блок содержит механизмы внимания и полностью соединенные слои для глубокой обработки данных.

  3. Pooler: После прохождения через все блоки кодера, данные поступают в пуллер, который создает финальное резюме всей информации, используя специальный слой для сводной обработки.

Этот процесс позволяет модели эффективно улавливать контекст и взаимосвязи между словами в тексте, что делает Берт чрезвычайно мощным инструментом для различных задач NLP.

参考:transformers.modeling_bert.BertModel

class BertModel(BertPreTrainedModel): def init(self, config):

    self.embeddings = BertEmbeddings(config)
    self.encoder = BertEncoder(config)
    self.pooler = BertPooler(config)
			...
def forward(self, input_ids, attention_mask=None, token_type_ids=None,position_ids=None, head_mask=None):
			...
    ### 第一部分,对 attention_mask 进行操作,并对输入做embedding
    extended_attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
    extended_attention_mask = extended_attention_mask.to(dtype=next(self.parameters()).dtype) # fp16 compatibility
    extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0
    embedding_output = self.embeddings(input_ids, position_ids=position_ids, token_type_ids=token_type_ids)
    ### 第二部分 进入 encoder 进行编码
    encoder_outputs = self.encoder(embedding_output,
                                   extended_attention_mask,
                                   head_mask=head_mask)
			...
    return outputs  

</details>

对于BertModel ,我们可以把它分成两个部分,第一个部分是对 attention_mask 进行操作,并对输入做embedding,第二个部分是进入encoder进行编码,这里的encoder使用的是BertEncoder。我们直接进去看一下

###### 6.2.1.1.1 BertEncoder

代码如下:

```python
##reference:transformers.modeling_bert.BertEncoder
class BertEncoder(nn.Module):
    def __init__(self, config):
        super(BertEncoder, self).__init__()
        self.output_attentions = config.output_attentions
        self.output_hidden_states = config.output_hidden_states
        self.layer = nn.ModuleList([BertLayer(config) for _ in range(config.num_hidden_layers)])

    def forward(self, hidden_states, attention_mask=None, head_mask=None):
        all_hidden_states = ()
        all_attentions = ()
        for i, layer_module in enumerate(self.layer):
            if self.output_hidden_states:
                all_hidden_states = all_hidden_states + (hidden_states,)

            layer_outputs = layer_module(hidden_states, attention_mask, head_mask[i])
            hidden_states = layer_outputs[0]

            if self.output_attentions:
                all_attentions = all_attentions + (layer_outputs[1],)

        # Add last layer
        if self.output_hidden_states:
            all_hidden_states = all_hidden_states + (hidden_states,)

        outputs = (hidden_states,)
        if self.output_hidden_states:
            outputs = outputs + (all_hidden_states,)
        if self.output_attentions:
            outputs = outputs + (all_attentions,)
        return outputs  # last-layer hidden state, (all hidden states), (all attentions)

      

有一个BertEncoder小细节,就是如果output_hidden_states为True,会把每一层的结果都输出,也包含词向量,所以如果十二层的话,输出是是13层,第一层为word-embedding结果,每层结果都是[batchsize,seqlength,Hidden_size](除了第一层,[batchsize,seqlength,embedding_size])

当然embedding_size在维度上是和隐层维度一样的。

还有一点需要注意的就是,我们需要在这里看到一个细节,就是我们是可以做head_mask,这个head_mask我记得有个论文是在做哪个head对结果的影响,这个好像能够实现。

BertEncoder 中间最重要的是BertLayer

  • BertLayer

BertLayer分为两个操作,BertAttention和BertIntermediate。BertAttention分为BertSelfAttention和BertSelfOutput。我们一个个来看

    • BertAttention
      • BertSelfAttention
def forward(self, hidden_states, attention_mask=None, head_mask=None):
  ## 接受参数如上
  mixed_query_layer = self.query(hidden_states) ## 生成query [16,32,768],16是batch_size,32是这个batch中每个句子的长度,768是维度
  mixed_key_layer = self.key(hidden_states)
  mixed_value_layer = self.value(hidden_states)

  query_layer = self.transpose_for_scores(mixed_query_layer)## 将上面生成的query进行维度转化,现在维度: [16,12,32,64]:[Batch_size,Num_head,Seq_len,每个头维度]
  key_layer = self.transpose_for_scores(mixed_key_layer)
  value_layer = self.transpose_for_scores(mixed_value_layer)

  # Take the dot product between "query" and "key" to get the raw attention scores.
  attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2))
  ## 上面操作之后 attention_scores 维度为torch.Size([16, 12, 32, 32])
  attention_scores = attention_scores / math.sqrt(self.attention_head_size)
  if attention_mask is not None:
  # Apply the attention mask is (precomputed for all layers in BertModel forward() function)
  attention_scores = attention_scores + attention_mask
  ## 这里直接就是相加了,pad的部分直接为非常大的负值,下面softmax的时候,直接就为接近0

  # Normalize the attention scores to probabilities.
  attention_probs = nn.Softmax(dim=-1)(attention_scores)

  # This is actually dropping out entire tokens to attend to, which might
  # seem a bit unusual, but is taken from the original Transformer paper.
  attention_probs = self.dropout(attention_probs)##维度torch.Size([16, 12, 32, 32])

  # Mask heads if we want to
  if head_mask is not None:
  	attention_probs = attention_probs * head_mask

  context_layer = torch.matmul(attention_probs, value_layer)##维度torch.Size([16, 12, 32, 64])

  context_layer = context_layer.permute(0, 2, 1, 3).contiguous()##维度torch.Size([16, 32, 12, 64])
  new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)## new_context_layer_shape:torch.Size([16, 32, 768])
  context_layer = context_layer.view(*new_context_layer_shape)
##维度变成torch.Size([16, 32, 768])
  outputs = (context_layer, attention_probs) if self.output_attentions else (context_layer,)
  return outputs

这个时候 BertSelfAttention 返回结果维度为 torch.Size([16, 32, 768]),这个结果作为BertSelfOutput的输入

      • BertSelfOutput
class BertSelfOutput(nn.Module):
    def __init__(self, config):
        super(BertSelfOutput, self).__init__()
        self.dense = nn.Linear(config.hidden_size, config.hidden_size)
        ## 做了一个linear 维度没变
        self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps)
        self.dropout = nn.Dropout(config.hidden_dropout_prob)

    def forward(self, hidden_states, input_tensor):
        hidden_states = self.dense(hidden_states)
        hidden_states = self.dropout(hidden_states)
        hidden_states = self.LayerNorm(hidden_states + input_tensor)
        return hidden_states

上面两个函数BertSelfAttention 和BertSelfOutput之后,返回attention的结果,接下来仍然是BertLayer的下一个操作:BertIntermediate

    • BertIntermediate

这个函数比较简单,经过一个Linear,经过一个Gelu激活函数

输入结果维度为 torch.Size([16, 32, 3072])

这个结果 接下来进入 BertOutput 这个模型

    • BertOutput

也比较简单,Liner+BertLayerNorm+Dropout,输出结果维度为 torch.Size([16, 32, 768])

BertOutput的输出结果返回给BertEncoder 类

BertEncoder 结果返回 BertModel 类 作为 encoder_outputs,维度大小 torch.Size([16, 32, 768])

BertModel的返回为 outputs = (sequence_output, pooled_output,) + encoder_outputs[1:]

sequence_output:torch.Size([16, 32, 768])

pooled_output:torch.Size([16, 768]) 是cls的输出经过一个pool层(其实就是linear维度不变+tanh)的输出

outputs返回给BertForSequenceClassification,也就是对pooled_output 做分类

常见问题

相似工具推荐

stable-diffusion-webui

stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。

162.1k|★★★☆☆|今天
开发框架图像Agent

everything-claude-code

everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上

139k|★★☆☆☆|今天
开发框架Agent语言模型

ComfyUI

ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。

107.7k|★★☆☆☆|2天前
开发框架图像Agent

NextChat

NextChat 是一款轻量且极速的 AI 助手,旨在为用户提供流畅、跨平台的大模型交互体验。它完美解决了用户在多设备间切换时难以保持对话连续性,以及面对众多 AI 模型不知如何统一管理的痛点。无论是日常办公、学习辅助还是创意激发,NextChat 都能让用户随时随地通过网页、iOS、Android、Windows、MacOS 或 Linux 端无缝接入智能服务。 这款工具非常适合普通用户、学生、职场人士以及需要私有化部署的企业团队使用。对于开发者而言,它也提供了便捷的自托管方案,支持一键部署到 Vercel 或 Zeabur 等平台。 NextChat 的核心亮点在于其广泛的模型兼容性,原生支持 Claude、DeepSeek、GPT-4 及 Gemini Pro 等主流大模型,让用户在一个界面即可自由切换不同 AI 能力。此外,它还率先支持 MCP(Model Context Protocol)协议,增强了上下文处理能力。针对企业用户,NextChat 提供专业版解决方案,具备品牌定制、细粒度权限控制、内部知识库整合及安全审计等功能,满足公司对数据隐私和个性化管理的高标准要求。

87.6k|★★☆☆☆|今天
开发框架语言模型

ML-For-Beginners

ML-For-Beginners 是由微软推出的一套系统化机器学习入门课程,旨在帮助零基础用户轻松掌握经典机器学习知识。这套课程将学习路径规划为 12 周,包含 26 节精炼课程和 52 道配套测验,内容涵盖从基础概念到实际应用的完整流程,有效解决了初学者面对庞大知识体系时无从下手、缺乏结构化指导的痛点。 无论是希望转型的开发者、需要补充算法背景的研究人员,还是对人工智能充满好奇的普通爱好者,都能从中受益。课程不仅提供了清晰的理论讲解,还强调动手实践,让用户在循序渐进中建立扎实的技能基础。其独特的亮点在于强大的多语言支持,通过自动化机制提供了包括简体中文在内的 50 多种语言版本,极大地降低了全球不同背景用户的学习门槛。此外,项目采用开源协作模式,社区活跃且内容持续更新,确保学习者能获取前沿且准确的技术资讯。如果你正寻找一条清晰、友好且专业的机器学习入门之路,ML-For-Beginners 将是理想的起点。

85k|★★☆☆☆|今天
图像数据工具视频

ragflow

RAGFlow 是一款领先的开源检索增强生成(RAG)引擎,旨在为大语言模型构建更精准、可靠的上下文层。它巧妙地将前沿的 RAG 技术与智能体(Agent)能力相结合,不仅支持从各类文档中高效提取知识,还能让模型基于这些知识进行逻辑推理和任务执行。 在大模型应用中,幻觉问题和知识滞后是常见痛点。RAGFlow 通过深度解析复杂文档结构(如表格、图表及混合排版),显著提升了信息检索的准确度,从而有效减少模型“胡编乱造”的现象,确保回答既有据可依又具备时效性。其内置的智能体机制更进一步,使系统不仅能回答问题,还能自主规划步骤解决复杂问题。 这款工具特别适合开发者、企业技术团队以及 AI 研究人员使用。无论是希望快速搭建私有知识库问答系统,还是致力于探索大模型在垂直领域落地的创新者,都能从中受益。RAGFlow 提供了可视化的工作流编排界面和灵活的 API 接口,既降低了非算法背景用户的上手门槛,也满足了专业开发者对系统深度定制的需求。作为基于 Apache 2.0 协议开源的项目,它正成为连接通用大模型与行业专有知识之间的重要桥梁。

77.1k|★★★☆☆|昨天
Agent图像开发框架