【大模型-驯化】一文教会你bert、qwen、llama等模型对结果进行概率控制问题
本次修炼方法请往下查看
🌈 欢迎莅临我的个人主页 👈这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合,智慧小天地!
🎇 相关内容文档获取 微信公众号
🎇 相关内容视频讲解 B站
🎓 博主简介:AI算法驯化师,混迹多个大厂搜索、推荐、广告、数据分析、数据挖掘岗位 个人申请专利40+,熟练掌握机器、深度学习等各类应用算法原理和项目实战经验。
🔧 技术专长: 在机器学习、搜索、广告、推荐、CV、NLP、多模态、数据分析等算法相关领域有丰富的项目实战经验。已累计为求职、科研、学习等需求提供近千次有偿|无偿定制化服务,助力多位小伙伴在学习、求职、工作上少走弯路、提高效率,近一年好评率100% 。
📝 博客风采: 积极分享关于机器学习、深度学习、数据分析、NLP、PyTorch、Python、Linux、工作、项目总结相关的实用内容。
下滑查看解决方法
🎯 1.问题介绍
大模型的发展使得之前的nlp相关任务:文本分类、实体抽取、文本生成、文本理解等任务都在由bert预训练模型向gpt模型进行过渡,具体的之前用bert进行文本分类的任务都可以通过gpt进行sft来得到微调的最终结果,在通过bert进行文本分类的过程中,我们可以对bert的输出结果softmax进行阈值的分析和选择来确定最终召回和精确率,但是在gpt方式微调的过程中,由于gpt是每次预测未来一个字的概率,因此,和之前的bert分类直接取每个类别的概率的最大值有较大的区别,本次博客教大家如何通过gpt系列模型进行预测,然后通过某个字的概率来调整最终的召回和精确:
💡 2. 问题分析
💡 2. 1 bert模型做分类得出相关的概率值
通过bert进行微调分类效果,通常是通过对bert的最终softmax中某个类别的概率的最大值最为我们最终的结果,因此,如果是通过bert进行分类的话,只需要得到最终一层的softmax的结果的概率值,然后,调整概率值的阈值就可以调整最终的准召,具体的代码如下所示:
import torch
from transformers import XLMRobertaTokenizer, XLMRobertaModel
import pandas as pd
# 指定模型和分词器的路径
model_path = "huggingface/robert"
tokenizer = XLMRobertaTokenizer.from_pretrained(model_path)
model = XLMRobertaModel.from_pretrained(model_path)
# 确定设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# 准备输入数据
def get_result(texts):
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
inputs = {k: v.to(device) for k, v in inputs.items()}
# 获取嵌入
model.eval()
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
# 选择最后一层的隐藏状态
last_hidden_states = outputs.hidden_states[-1]
embeddings = last_hidden_states.mean(dim=1) # 沿序列长度维度取平均
# 转换为DataFrame
embeddings_df = pd.DataFrame(embeddings.cpu().numpy())
return embeddings_df
data=pd.read_excel("./dataset.xlsx")
data = data[['opcode_text','full_graph_content', 'label']]
data['text'] = data.apply(lambda x: str(x['opcode_text'])[:261] + str(x['full_graph_content'])[:261], axis=1)
df = None
for i, (v, label) in enumerate(zip(data['text'].tolist(), data['label'].tolist())):
if i == 0:
df = get_result([v])
df['label'] = label
else:
tmp_df = get_result([v])
tmp_df['label'] = label
df = pd.concat([df, tmp_df])
df.to_csv('all_embedding.csv', sep=',', index=False)
我们最终得到的向量的维度是bert输出的512维的向量维度,如果想要得到softmax对应的每个类别的概率,具体的代码如下所示:
def model_predict():
"""
模型预测
"""
bert_config = BertConfig.from_pretrained('../bert-base-chinese')
model = BertClassifier(bert_config, 2)
# 加载训练好的模型
model.load_state_dict(torch.load('best_model.pkl', map_location=torch.device('cpu')))
model.eval()
tokenizer = BertTokenizer.from_pretrained('../bert-base-chinese')
print('进行预测过程!!!!!')
while True:
text = input('Input: ')
token = tokenizer(text, add_special_tokens=True, padding='max_length', truncation=True, max_length=512)
input_ids = token['input_ids']
attention_mask = token['attention_mask']
token_type_ids = token['token_type_ids']
input_ids = torch.tensor([input_ids], dtype=torch.long)
attention_mask = torch.tensor([attention_mask], dtype=torch.long)
token_type_ids = torch.tensor([token_type_ids], dtype=torch.long)
predicted = model(
input_ids,
attention_mask,
token_type_ids,
)
pred_label = torch.argmax(predicted, dim=1)
print('Label:', pred_label)
有了上述的softamx的输出后,我们可以取最大的概率最为我们最终类别的概率,然后在通过调整概率的阈值来控制最终模型的准召情况
💡 2. 2 gpt模型做分类得出相关的概率值
对于gpt系列的模型,比如qwen、llama等模型,他们的输出是一个token的输出,每次都是输出每个字的概率,这种情况,我们可以根据最终的输出的label进行判断,比如通过qwen进行微调后的分类模型标签为:心情很好,心情很不好,那么这个时候我们可以通过去判断第4个token是好
还是不
来判断最终模型输出的分类是那个,当然这里需要注意的是,gpt对输入进行切词的时候并不是一个字一个字的切分的,而是一个token切分的,因此,可能会有两个字为一个token的情况,因此,可以将gpt的输出token和对应的概率和向量都打印出来
,这个还有一个需要注意的是,gpt的输出是所有词典的token的概率输出,因此是一个很大的向量,具体的代码如下所示:
# A100 14G memory
import numpy as np
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_path = "=qwen2_5-14b-instruct/v4-20241016-163008/checkpoint-1872"
prompt_info = "xxxxxx"
input_query = "xx:啊喂你好师傅外卖到了开门吧\n用户:喂\nxx:好了\n骑手:嗯好嘞\n"
device = "cuda:7"
messages = [
{"role": "system", "content": prompt_info},
{"role": "user", "content": input_query}
]
model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype="auto",
device_map=device
)
tokenizer = AutoTokenizer.from_pretrained(model_path)
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True)
model_inputs = tokenizer([text], return_tensors="pt").to(device)
generated_ids = model.generate(
model_inputs.input_ids,
do_sample=False,
max_new_tokens=512,
return_dict_in_generate=True,
temperature=2,
# output_logits=True
output_scores=True
)
# output_prob(model,generated_ids,model_inputs,tokenizer)
generated_tokens = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids.sequences)
]
for tokens, logits in zip(generated_tokens[0], generated_ids.scores):
print(tokens, tokenizer.decode(tokens), np.max(torch.softmax(logits, dim=1).tolist()[0]),
torch.softmax(logits, dim=1).tolist()[0][tokens.item()])
transition_scores = model.compute_transition_scores(
generated_ids.sequences, generated_ids.scores, normalize_logits=True
)
# print(transition_scores)
response = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
print(response)
print(generated_ids.scores)
print(generated_ids)
# 输出通过概率
# pred = -1
# for idx, score in enumerate(generated_ids.scores):
# prob = torch.softmax(score, dim=1).tolist()[0]
# print(prob)
# for idx_word,p in enumerate(prob):
# if idx==3 and idx_word==67338:
# pred = p
输出结果:
tensor(100855, device='cuda:7') a 1.0
tensor(44934, device='cuda:7') b 1.0
tensor(38342, device='cuda:7') c 0.9999998807907104
tensor(104953, device='cuda:7') de 1.0
tensor(99512, device='cuda:7') f 1.0
tensor(102737, device='cuda:7') g 1.0
tensor(20412, device='cuda:7') h 1.0
tensor(100855, device='cuda:7') i 1.0
tensor(44934, device='cuda:7') j 1.0
tensor(99531, device='cuda:7') kl 1.0
tensor(151645, device='cuda:7') <|im_end|> 1.0
tensor([[ 0.0000e+00, 0.0000e+00, -1.1921e-07, 0.0000e+00, 0.0000e+00,
0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00,
0.0000e+00]], device='cuda:7')
abcdefghijkl