文本向量化表示的输出比较
import timeimport 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而是listinputs_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_outputlast_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-ChineseESimCSE-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. 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_outputsentence_embedding = outputs_source.last_hidden_state[:, 0, :] # shape (b_s, 768) 每个文本的CLS 表示 这个是固定的
这里首先得到模型的最后一层隐藏状态的输出last_hidden_state,shape (b_s, max_len, 768)
然后选出CLS这个token的表示将其作为整个句子的表示
1.2 整体代码
import timeimport 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_outputsentence_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_embeddingdef 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_timeprint("消耗时间为: {}秒".format(used_time))
输出结果: 中国
的文本表示如下
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)
这里相当于是把最后一层隐藏状态通过一个全连接层然后在进行输出表示整个句子
2.2 整体代码
import timeimport 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_embeddingdef 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_timeprint("消耗时间为: {}秒".format(used_time))
输出结果: 中国
的文本表示如下
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)
这里是利用last_hidden_state的mean进行表示 但这个表示如果利用批量文本向量化的时候可能会出现问题,因为mean的时候会考虑padding, cls_embedding, 和pool_embedding就不会出现这种情况
3.2 整体代码
import timeimport 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_embeddingdef 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_timeprint("消耗时间为: {}秒".format(used_time))
输出结果: 中国
的文本表示如下
输出结果: 如果输入文本为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
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
import timeimport 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_embeddingif __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_timeprint("消耗时间为: {:.2f}秒".format(used_time))
水平有限 如有问题欢迎指正交流
参考博客: https://blog.csdn.net/ningyanggege/article/details/132206331