LLM - LLaMA-2 获取文本向量并计算 Cos 相似度

打印 上一主题 下一主题

主题 1782|帖子 1782|积分 5346

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x

目次
一.弁言
二.获取文本向量
1.hidden_states 与 last_hidden_states
◆ hidden_states
◆ last_hidden_states 
2.LLaMA-2 获取 hidden_states
◆ model config 
◆ get Embedding
三.获取向量 Cos 相似度
1.向量选择
2.Cos 相似度
3.BERT-whitening 特征白化
4.评估指标对比
四.总结



一.弁言

前面提到了两种基于统计的呆板翻译评估方法: Rouge 与 BLEU,二者通过统计概率计算 N-Gram 的准确率与召回率,在呆板翻译这种回复相对固定的场景该方法可以作为肯定参考,但在当前大模子更加多样性的场景以及发散的回复的环境下,Rouge 与 BLEU 有时候并不能更好的描述文本之间的相似度,下面我们尝试从 LLM 大模子提取文本的 Embedding 并进行向量相似度计算。

二.获取文本向量

1.hidden_states 与 last_hidden_states

根据 LLM 模子类型的差异,有的 Model 提供 hidden_states 方法,比方 LLaMA-2-13B,有的模子提供 last_hidden_states 方法,比方 GPT-2。查找模子对应方法 API 可以在 Transformer 官网。
 hidden_states


hidden_states 类型为 typing.Optional[typing.Tuple[torch.FloatTensor]],其提供一个 Tuple[Tensor] 分别记录了每层的输出,完整的表明在参数下方: 

模子在每一层输出处的隐藏状态加上可选的初始嵌入输出。这里我们可以通过打印模子 Layer 和索引从而获取 hidden_states 中隐层的输出。

◆ last_hidden_states 


一些传统的模子比方 GPT-2,另有当下一些的新模子比方 ChatGLM2 都有 last_hidden_states 的 API,可以直接获取最后一层的 Embedding 输出,而如果利用 hidden_states 则只需要通过 [-1] 索引即可获得 last_hidden_states,相比来如前者更全面后者更方便。

2.LLaMA-2 获取 hidden_states

model config 

  1.     config_kwargs = {
  2.         "trust_remote_code": True,
  3.         "cache_dir": None,
  4.         "revision": 'main',
  5.         "use_auth_token": None,
  6.         "output_hidden_states": True
  7.     }
  8.     config = AutoConfig.from_pretrained(ori_model_path, **config_kwargs)
  9.     llama_model = AutoModelForCausalLM.from_pretrained(
  10.         ori_model_path,
  11.         config=config,
  12.         torch_dtype=torch.float16,
  13.         low_cpu_mem_usage=True,
  14.         trust_remote_code=True,
  15.         revision='main'
  16.     )
复制代码
 根据 CausalLMOutputWithPast hidden_states 参数的提示,我们只需要在模子 config 中添加:
  1. "output_hidden_states": True
复制代码

get Embedding

  1. def get_embeddings(result, llm_tokenizer, model, args):
  2.     fw = open(args.output, 'w', encoding='utf-8')
  3.     for qa in result:
  4.         q = qa[0]
  5.         a = qa[1]
  6.         # 对输出文本进行 tokenize 和编码
  7.         tokens = llm_tokenizer.encode_plus(a, add_special_tokens=True, padding='max_length', truncation=True,
  8.                                            max_length=128, return_tensors='pt')
  9.         input_ids = tokens["input_ids"]
  10.         attention_mask = tokens['attention_mask']
  11.         # 获取文本 Embedding
  12.         with torch.no_grad():
  13.             outputs = model(input_ids=input_ids.cuda(), attention_mask=attention_mask)
  14.             embedding = list(outputs.hidden_states)
  15.             last_hidden_states = embedding[-1].cpu().numpy()
  16.             first_hidden_states = embedding[0].cpu().numpy()
  17.             last_hidden_states = np.squeeze(last_hidden_states)
  18.             first_hidden_states = np.squeeze(first_hidden_states)
  19.             fisrt_larst_avg_status = np.mean(first_hidden_states + last_hidden_states, axis=0)
  20.         log = "%s\t%s\t%s\n" % (q, a, toString(fisrt_larst_avg_status))
  21.         fw.write(log)
  22.     fw.close()
复制代码
predict  猜测       ➔  将 model 基于 Question generate 得到的 Answer 存入 result
encode 编码       ➔  对 Answer 进行编码获取对应 Token 与 input_ids、attention_mask
output 模子输出  ➔  直接调用 model 进行输出,有的也可以调用 model.transform 方法进行输出
hidden_states     ➔  outputs.hidden_states 获取各隐层输出
最后获取的向量需要先 cpu 然后再转为 numpy 数组,一般的做法是接纳 mean 获得句子的均匀表征。

三.获取向量 Cos 相似度

1.向量选择

在 BERT-flow 的论文中,如果不加任何后处理本事,那么基于 BERT 抽取句向量的最好 Pooling 方法是 BERT 的第一层与最后一层的全部 token 向量的均匀,即 fisrt-larst-avg,对应 hidden_state 的 0 和 -1 索引,以是后面的相似度计算我们都以 fisrt-larst-avg 为基准来评估 Embedding 相似度。
  1. # 获取文本 Embedding
  2. with torch.no_grad():
  3.     outputs = model(input_ids=input_ids.cuda(), attention_mask=attention_mask)
  4.     embedding = list(outputs.hidden_states)
  5.     last_hidden_states = embedding[-1].cpu().numpy()
  6.     first_hidden_states = embedding[0].cpu().numpy()
  7.     last_hidden_states = np.squeeze(last_hidden_states)
  8.     first_hidden_states = np.squeeze(first_hidden_states)
  9.     fisrt_larst_avg_status = np.mean(first_hidden_states + last_hidden_states, axis=0)
复制代码

2.Cos 相似度

  1. # 计算 Cos 相似度
  2. def compute_cosine(a_vec, b_vec):
  3.     norms1 = np.linalg.norm(a_vec, axis=1)
  4.     norms2 = np.linalg.norm(b_vec, axis=1)
  5.     dot_products = np.sum(a_vec * b_vec, axis=1)
  6.     cos_similarities = dot_products / (norms1 * norms2)
  7.     return cos_similarities
复制代码
a_vec 为猜测文本转化得到的 Embedding,b_vec 为人工标注正样本文本转化得到的 Embedding,通过计算二者相似度,评估猜测文本与人工文本的相似程度。

3.BERT-whitening 特征白化

苏神在 BERT-whitening 一文中提出了一种基于 PCA 降维的无监视 Embedding 评估方式,Bert-whitening 又叫特征白化,其思路与 PCA 降维类似,意在对 SVD 分解后的主成分矩阵取前 λ 个特征向量构造特征值矩阵,提取向量中的关键信息,使输出向量矩阵每个维度均值为零,协方差矩阵为单元阵,λ 个特征值也对应前 λ 个主成分。其算法逻辑如下:

 下面我们调用 Sklearn 的 PCA 库简单实现下:
  1. from sklearn.decomposition import PCA
  2. from sklearn.preprocessing import normalize
  3.     # 取出句子的平均表示 -> 使用 PCA 降维 -> 白化处理
  4.     concatenate = np.concatenate((answer_vector, predict_vector))
  5.     pca = PCA(n_components=2048)
  6.     pca.fit(concatenate)
  7.     ans_white_vec = pca.transform(answer_vector)
  8.     ans_norm_vec = normalize(ans_white_vec)
  9.     pre_white_vec = pca.transform(predict_vector)
  10.     pre_norm_vec = normalize(pre_white_vec)
  11.     pca_cos_similarities = compute_cosine(ans_norm_vec, pre_norm_vec)
复制代码
answec_vector 和 predict_vector 均通过 first_and_last 方法从 hidden_states 中获取,n_components 即 top_k 的选择,以 LLaMA-2 为例,原始得到的向量维度为 5120,原文中也有利用 n_components = 256 实行。

4.评估指标对比

在同一份尺度答案的基础上,博主分别利用 LLaMA-33B、LLaMA-2-13B、Aplace-2-13B 进行了向量相似度的指标评估,肉眼看结果  LLaMA-33B > LLaMA-2-13B > Aplace-2-13B,实际的指标也基本保持了这个趋势:

一方面 LLaMA-33B 依附参数量和 Embedding 维度的上风保持了各项指标的领先,其次回归到这些相似度评估指标身上,Embedding 层得到的 First Layer 其实是 Token 的表征,而 Last Layer 得到的是 Next Token 的分布,不管基于 COS 还是 PCA,这些指标还是没有脱离 Rouge、Bleu 的统计概率,只不外是更加抽象的文本相似。想要做到更深层次的语义理解,可以需要有监视样本训练后的 LLM 去评估,不外最少现在有了除 Rouge、Bleu 外的指标了。 

四.总结

博主接纳 1500+ 样本分别利用 cos、pca 和 self_pca [自己实现 SVD 与特征矩阵] 三种方法对向量相似度进行评估,n_components 设为 1024:

可以看到 SVD 处理后得到的 W 和 mu 的 shape,通过下述操纵可完成向量的降维:
  1. vecs = (vecs + bias).dot(kernel)
复制代码
最终得到的结果 Cosine 与 PCA 降维的相似度差距较大,由于自然语言天生的样本没有严格意义的正样本,上面计算接纳的参考文本也是人工标注,有肯定的不确定性,以是基于差异的度量,我们也可以统计分析,定一个 threshold,认为大于该 threshold 的输入样本为可用。
Tips:
  1. what is your name? => 'what' 'is' 'your' 'name' '?'
  2. 你的名字是什么? => '你' '的' '名' '字' '是' '什' '么' '?'
复制代码
以上面的语句为例,如果是英文分词可以将每个词分解开,但是中文的词肯定程度上需要用到词组,固然 Rouge 可以计算 N-Gram 也可以覆盖一部分词组,但是团体意思还是有毛病,以是有兴趣的同砚也可以尝试将逐字分词修改为 jieba 分词等,检察指标是否有变革:
  1. 你的名字是什么? => '你' '的' '名字' '是' '什么' '?'
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

汕尾海湾

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表