• 文本向量化


    文本向量化表示的输出比较

    import time
    
    import torch
    from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModel
    
    
    # simcse相似度分数
    def get_model_output(model, tokenizer, text_str):
        """
        验证文本向量化表示的输出
        :param model:       模型的名称
        :param tokenizer:   tokenizer
        :param text_str:    文本内容 可以只一个str, 也可以是一个str_list
        :return:            返回该文本向量表示形式
        """
        # 返回的类似一个字典类型
        # padding 表示填充max_len  CLS + text + [SEP] return_tensors="pt" 返回pytorch类型的张量
        # 如果不加这个return_tensors="pt"参数 input_ids等key的value就不是tensor而是list
        inputs_text = tokenizer(text_str, return_tensors="pt",  padding=True)
        print_inputs_source(**inputs_text)
    
        with torch.no_grad():
            outputs_source = model(**inputs_text)
            last_hidden_states = outputs_source.last_hidden_state                   # shape (b_s, max_len, 768)
            # 一般我们会使用的三个模型输出 建议使用 pooling_output
            last_cls_embedding = outputs_source.last_hidden_state[:, 0, :]          # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
            last_hidden_states_mean = outputs_source.last_hidden_state.mean(dim=1)  # shape (b_s, 768) 这个表示会有问题 对(b_s, max_len, 768) 进行平均池化得到的
            pooling_output = outputs_source.pooler_output                           # shape (b_s, 768) 每个文本的CLS之后在进入一次池化(全连接层)得到pool_output   这个表示也是固定的
            last_hidden_states_pooling = model.pooler(last_hidden_states)
            print("last_hidden_states shape {}".format(last_hidden_states.shape))
            print("last_CLS_embedding shape {}".format(last_cls_embedding.shape))
            print("last_hidden_states_pool shape {}".format(last_hidden_states_pooling.shape))
            print("last_hidden_states_mean shape {}".format(last_hidden_states_mean.shape))
            print("pool_output shape {}".format(pooling_output.shape))
            print("equal last_pool and pool_output: {}".format(torch.equal(pooling_output, last_hidden_states_pooling)))
    
            print("last_hidden_states embedding {}".format(last_hidden_states))
            print("last_hidden_states mead {}".format(last_hidden_states_mean))
            print("last_cls_embedding embedding {}".format(last_cls_embedding))
            print("pool_output {}".format(pooling_output))
    
    
    def print_inputs_source(input_ids, token_type_ids, attention_mask):
        """
        打印模型tokenizer之后的内容
        :param input_ids:
        :param token_type_ids:
        :param attention_mask:
        :return:
        """
        print("input_ids: {}".format(input_ids))
        print("token_type_ids: {}".format(token_type_ids))
        print("attention_mask: {}".format(attention_mask))
    
    
    
    if __name__ == '__main__':
        """字典的拆包"""
        # "D:\program\pretrained_model\ESimCSE-ext-chinese-bert-wwm"
        """
        Erlangshen-SimCSE-110M-Chinese
        ESimCSE-ext-chinese-bert-wwm        这里的tokenier_config文件用的是sup-simcse的
        sup-simcse-bert-base-uncased
        "bert-base-uncased"
        """
        start_run_time = time.time()
        prefix_path = "D:/program/pretrained_model/"        # 本地路径前缀
        model_path = prefix_path + "bert-base-uncased"
        model = AutoModel.from_pretrained(model_path)
        tokenizer = AutoTokenizer.from_pretrained(model_path)
        text_str = "中国"
        text_list = ["中国", "今天是个好日子"]
        get_model_output(model, tokenizer, text_list)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    1. CLS表示整个文本

    outputs_source.hidden_states[-1][:, 0, :]这行代码是用来从模型的输出中提取最后一层的所有单词的第一个位置(通常是 [CLS] 标记)的隐藏状态。

    1.1 关键代码:

            outputs_source = model(**inputs_text) 
            last_hidden_states = outputs_source.last_hidden_state                   # shape (b_s, max_len, 768)
    pooling_output
            sentence_embedding = outputs_source.last_hidden_state[:, 0, :]          # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
    
    • 1
    • 2
    • 3
    • 4

    这里首先得到模型的最后一层隐藏状态的输出last_hidden_state,shape (b_s, max_len, 768)
    然后选出CLS这个token的表示将其作为整个句子的表示

    1.2 整体代码

    import time
    
    import torch
    from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModel
    
    
    # simcse相似度分数
    def get_model_output(model, tokenizer, text_str):
        """
        验证文本向量化表示的输出
        :param model:       模型的名称
        :param tokenizer:   tokenizer
        :param text_str:    文本内容 可以只一个str, 也可以是一个str_list
        :return:            返回该文本向量表示形式
        """
        # 返回的类似一个字典类型
        # padding 表示填充max_len  CLS + text + [SEP] return_tensors="pt" 返回pytorch类型的张量
        inputs_text = tokenizer(text_str, return_tensors="pt",  padding=True)
        # print_inputs_source(**inputs_text)
    
        with torch.no_grad():
            outputs_source = model(**inputs_text)
            last_hidden_states = outputs_source.last_hidden_state                   # shape (b_s, max_len, 768)
            # 一般我们会使用的三个模型输出 建议使用 pooling_output
            sentence_embedding = outputs_source.last_hidden_state[:, 0, :]          # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
    
        print("last_hidden_state shape: {}".format(last_hidden_states.shape))
        print("cls_embedding shape: {}".format(sentence_embedding.shape))
        print("last_cls_embedding: {}".format(sentence_embedding))
        return sentence_embedding
    
    
    def print_inputs_source(input_ids, token_type_ids, attention_mask):
        """
        打印模型tokenizer之后的内容  CLS + text + SEP
        :param input_ids:
        :param token_type_ids:
        :param attention_mask:
        :return:
        """
        print("input_ids: {}".format(input_ids))
        print("token_type_ids: {}".format(token_type_ids))
        print("attention_mask: {}".format(attention_mask))
    
    
    if __name__ == '__main__':
        start_run_time = time.time()
        prefix_path = "D:/program/pretrained_model/"        # 本地路径前缀
        model_path = prefix_path + "bert-base-uncased"
        model = AutoModel.from_pretrained(model_path)
        tokenizer = AutoTokenizer.from_pretrained(model_path)
        text_str = "中国"
        text_list = ["中国", "今天是个好日子"]
        get_model_output(model, tokenizer, text_str)
    
        end_run_time = time.time()
        used_time = end_run_time-start_run_time
        print("消耗时间为: {}秒".format(used_time))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    输出结果: 中国 的文本表示如下
    在这里插入图片描述

    2. pooler_output表示整个文本

    outputs_source.pooler_output 表示从模型的输出中提取的池化器输出(pooler output)。

    在BERT模型中,经过最后一层的所有隐藏状态被经过一些池化操作,得到一个被称为"pooler output"的表示。这个表示通常被用于下游任务的输入,因为它是整个句子/序列的一个紧凑的表示。

    所以,outputs_source.pooler_output 就是源文本(或输入文本)通过池化器得到的表示。

    2.1 关键代码

            outputs_source = model(**inputs_text)
            sentence_embedding = outputs_source.pooler_output                   # shape (b_s, max_len, 768)
    
    • 1
    • 2

    这里相当于是把最后一层隐藏状态通过一个全连接层然后在进行输出表示整个句子

    2.2 整体代码

    import time
    
    import torch
    from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModel
    
    
    # simcse相似度分数
    def get_model_output(model, tokenizer, text_str):
        """
        验证文本向量化表示的输出
        :param model:       模型的名称
        :param tokenizer:   tokenizer
        :param text_str:    文本内容 可以只一个str, 也可以是一个str_list
        :return:            返回该文本向量表示形式
        """
        # 返回的类似一个字典类型
        # padding 表示填充max_len  CLS + text + [SEP] return_tensors="pt" 返回pytorch类型的张量
        inputs_text = tokenizer(text_str, return_tensors="pt",  padding=True)
        # print_inputs_source(**inputs_text)
    
        with torch.no_grad():
            outputs_source = model(**inputs_text)
            sentence_embedding = outputs_source.pooler_output                   # shape (b_s, max_len, 768)
            # 一般我们会使用的三个模型输出 建议使用 pooling_output
            # sentence_embedding = outputs_source.last_hidden_state[:, 0, :]          # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
    
        print("pooling_embedding shape: {}".format(sentence_embedding.shape))
        print("pooling_embedding: {}".format(sentence_embedding))
        return sentence_embedding
    
    
    def print_inputs_source(input_ids, token_type_ids, attention_mask):
        """
        打印模型tokenizer之后的内容  CLS + text + SEP
        :param input_ids:
        :param token_type_ids:
        :param attention_mask:
        :return:
        """
        print("input_ids: {}".format(input_ids))
        print("token_type_ids: {}".format(token_type_ids))
        print("attention_mask: {}".format(attention_mask))
    
    
    if __name__ == '__main__':
        start_run_time = time.time()
        prefix_path = "D:/program/pretrained_model/"        # 本地路径前缀
        model_path = prefix_path + "bert-base-uncased"
        model = AutoModel.from_pretrained(model_path)
        tokenizer = AutoTokenizer.from_pretrained(model_path)
        text_str = "中国"
        text_list = ["中国", "今天是个好日子"]
        get_model_output(model, tokenizer, text_str)
    
        end_run_time = time.time()
        used_time = end_run_time-start_run_time
        print("消耗时间为: {}秒".format(used_time))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    输出结果: 中国 的文本表示如下
    在这里插入图片描述

    3. 利用最后隐藏状态的mean进行表示

    3.1 关键代码

            outputs_source = model(**inputs_text)
            sentence_embedding = outputs_source.last_hidden_state.mean(dim=1)         # shape (b_s, max_len, 768)
    
    • 1
    • 2

    这里是利用last_hidden_state的mean进行表示 但这个表示如果利用批量文本向量化的时候可能会出现问题,因为mean的时候会考虑padding, cls_embedding, 和pool_embedding就不会出现这种情况

    3.2 整体代码

    import time
    
    import torch
    from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModel
    
    
    # simcse相似度分数
    def get_model_output(model, tokenizer, text_str):
        """
        验证文本向量化表示的输出
        :param model:       模型的名称
        :param tokenizer:   tokenizer
        :param text_str:    文本内容 可以只一个str, 也可以是一个str_list
        :return:            返回该文本向量表示形式
        """
        # 返回的类似一个字典类型
        # padding 表示填充max_len  CLS + text + [SEP] return_tensors="pt" 返回pytorch类型的张量
        inputs_text = tokenizer(text_str, return_tensors="pt",  padding=True)
        # print_inputs_source(**inputs_text)
    
        with torch.no_grad():
            outputs_source = model(**inputs_text)
            sentence_embedding = outputs_source.last_hidden_state.mean(dim=1)         # shape (b_s, max_len, 768)
            # 一般我们会使用的三个模型输出 建议使用 pooling_output
            # sentence_embedding = outputs_source.last_hidden_state[:, 0, :]          # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
    
        print("last_mean_embedding shape: {}".format(sentence_embedding.shape))
        print("last_mean_embedding: {}".format(sentence_embedding))
        return sentence_embedding
    
    
    def print_inputs_source(input_ids, token_type_ids, attention_mask):
        """
        打印模型tokenizer之后的内容  CLS + text + SEP
        :param input_ids:
        :param token_type_ids:
        :param attention_mask:
        :return:
        """
        print("input_ids: {}".format(input_ids))
        print("token_type_ids: {}".format(token_type_ids))
        print("attention_mask: {}".format(attention_mask))
    
    
    if __name__ == '__main__':
        start_run_time = time.time()
        prefix_path = "D:/program/pretrained_model/"        # 本地路径前缀
        model_path = prefix_path + "bert-base-uncased"
        model = AutoModel.from_pretrained(model_path)
        tokenizer = AutoTokenizer.from_pretrained(model_path)
        text_str = "中国"
        text_list = ["中国", "今天是个好日子"]
        get_model_output(model, tokenizer, text_str)
    
        end_run_time = time.time()
        used_time = end_run_time-start_run_time
        print("消耗时间为: {}秒".format(used_time))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    输出结果: 中国 的文本表示如下
    在这里插入图片描述

    输出结果: 如果输入文本为text_list(批量向量化)那么输出结果如下
    在这里插入图片描述

    这里可以发现中国的文本表示变化了 说明padding填充影响了mean的结果
    所以这种方法慎重使用

    4. 总结:

    4.1 三种表示方法

            model(**inputs_text).last_hidden_state.mean(dim=1)
            model(**inputs_text).last_hidden_state[:, 0, :]
            model(**inputs_text).pooler_output
    
    • 1
    • 2
    • 3

    4.2 last_hidden_state 和 pooler_output的区别以及转化

    参考博客: https://blog.csdn.net/ningyanggege/article/details/132206331

    last_hidden_states = model(**inputs_text).last_hidden_state 
    model.pooler(last_hidden_state) == model(**inputs_text).pooler_output
    
    • 1
    • 2
    import time
    
    import torch
    from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModel
    
    
    # simcse相似度分数
    def get_model_output(model, tokenizer, text_str):
        """
        验证文本向量化表示的输出
        :param model:       模型的名称
        :param tokenizer:   tokenizer
        :param text_str:    文本内容 可以只一个str, 也可以是一个str_list
        :return:            返回该文本向量表示形式
        """
        # 返回的类似一个字典类型
        # padding 表示填充max_len  CLS + text + [SEP] return_tensors="pt" 返回pytorch类型的张量
        inputs_text = tokenizer(text_str, return_tensors="pt",  padding=True)
        # print_inputs_source(**inputs_text)
    
        with torch.no_grad():
            outputs_source = model(**inputs_text)
            pooling_embedding = outputs_source.pooler_output                   # shape (b_s, max_len, 768)
    
            last_pool_embedding = model.pooler(outputs_source.last_hidden_state)
    
            # 一般我们会使用的三个模型输出 建议使用 pooling_output
            # sentence_embedding = outputs_source.last_hidden_state[:, 0, :]          # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
    
        print("valid equals {}".format(torch.equal(pooling_embedding, last_pool_embedding)))
    
        return pooling_embedding
    
    
    if __name__ == '__main__':
        start_run_time = time.time()
        prefix_path = "D:/program/pretrained_model/"        # 本地路径前缀
        model_path = prefix_path + "bert-base-uncased"
        model = AutoModel.from_pretrained(model_path)
        tokenizer = AutoTokenizer.from_pretrained(model_path)
        text_str = "中国"
        text_list = ["中国", "今天是个好日子"]
        get_model_output(model, tokenizer, text_str)
    
        end_run_time = time.time()
        used_time = end_run_time-start_run_time
        print("消耗时间为: {:.2f}秒".format(used_time))
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    水平有限 如有问题欢迎指正交流
    参考博客: https://blog.csdn.net/ningyanggege/article/details/132206331

  • 相关阅读:
    【Excel高阶技巧】条件函数、查找函数、字符串相关函数、公式审核、保护工作表/工作簿、模拟分析、宏
    fastjson框架漏洞复现
    UE4碰撞射线检测2
    七天学会C语言-第四天(数组)
    c++ I/O流
    日志监控系统 loki 配置文件详解
    ISTQB- TTA大纲
    JS(javascript)面试题 7点一次过 => 必会之八股文
    一文彻底搞懂前端ES6模块化
    Python文件操作(05):Excel操作
  • 原文地址:https://blog.csdn.net/qq_45056135/article/details/134493432