背景说明
在很多传统场景下,文本的泛化能力往往的评价一个产品是否优异的标准和软实力。例如各种产品的语音泛化能力。在传统的方案往往是采取不断添加预设关键词的方式来增加泛化能力,然鹅很明显,泛化能力存在一个上限,对于没有提前预设的命令,将不会收到任何反馈。
近几年大模型浩浩荡荡进发,大模型对于各个行业的赋能将不可阻挡。很多嵌入式的系统虽然因为各种各样的能力(性能受限等)暂时没办法直接搭载大模型能力(步子大了容易扯着蛋),但是把大模型的一部分功能拆下来使用,便可以给产品进行极大的赋能。
采用大模型文本嵌入的方案,将一句话映射为一个向量,通过向量之间余弦相似度比对的方案可以很好的完善泛化能力。至此,泛化是基于文本理解的泛化,而非增加预设词。
算法流程
步骤如下
- 进入M3E官网下载基础文件和基础模型 链接:魔搭社区 (modelscope.cn)
- 本地新建一个DataBase.txt 的文件,用以保存基础的关键命令,格式:每一个命令一行。
- 新建代码,代码已经附上
代码如下:
from sentence_transformers import SentenceTransformer
import numpy as np
def cosine_similarity(vec1, vec2):
# 确保输入是numpy数组
vec1 = np.array(vec1)
vec2 = np.array(vec2)
# 计算点积
dot_product = np.dot(vec1, vec2)
# 计算范数(长度)
norm_a = np.linalg.norm(vec1)
norm_b = np.linalg.norm(vec2)
# 防止除以零
if norm_a == 0 or norm_b == 0:
return 0
# 计算余弦相似度
cosine_similarity = dot_product / (norm_a * norm_b)
return cosine_similarity
#加载文件并构建embedding
def load_document_and_get_embeddings(document_path):
# 加载文档
with open(document_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
# 初始化用于存储二元组的列表
embeddings_list1 = []
embeddings_list2 = []
# 对文档的每一行进行处理
for line in lines:
print("正在处理" + line)
# 去除行尾的换行符
line = line.strip()
# 如果行不为空,则获取其嵌入
if line:
# 使用get_text_embeddings函数获取嵌入
embeddings = model.encode([line])
# 构建二元组并添加到列表中
embeddings_list1.append((line, embeddings))
embeddings_list2.append(embeddings.tolist())
return embeddings_list1, embeddings_list2
def PCAshow(data_transformed):
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
colors = ['r', 'g', 'b', 'y']
for i in range(len(data_transformed)):
ax.scatter(data_transformed[i, 0], data_transformed[i, 1],
data_transformed[i, 2], c=colors[labels[i]])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
if __name__ == "__main__":
print("初始化模型ing……")
model = SentenceTransformer('m3e-base')
print("模型加载完毕ing……")
#本地文件加载嵌入
embeddings_list, embeddings_list2 = load_document_and_get_embeddings("DataBase.txt")
print("数据文本初始化完毕")
while True:
your_input = input("请输入:")
yourEmbeddings = model.encode([your_input])
similarities = []
for idx, (sentence, embedding) in enumerate(embeddings_list):
similarity = cosine_similarity(embedding[0], yourEmbeddings[0])
similarities.append([similarity, sentence])
sorted_similarities = sorted(similarities, reverse=True)
print(sorted_similarities[0:3])
t1.join()
改进策略
对于上述方案还需要对于当前场景进行微调,否则将会出现啼笑皆非的现象!
例如:新命令“开始打扫马桶” 将会被分类为 “打扫厕所”,而且相似度还很高,因为在宏观世界看来“马桶”和“厕所”的关联度很高,然鹅在扫地机器人场景来看“打扫马桶”“和打扫厕所”是完全不同的两个命令,“打扫马桶”怎么说都不能直接执行“打扫厕所”的执行命令 。
这就需要对模型本身进行微调
微调方案一:模型微调训练
采用uniem进行嵌入模型的微调
微调方式如下,说明很友好,纯新手也能很快入手。
uniem/examples/finetune.ipynb at main · wangyuxinwhy/uniem · GitHub
微调方案二:负语义词增设关键词
当然也有一个更笨的方法,把一些关联性太强的词语(但是我们不希望他们泛化到同一个命令中),都加入预设的关键词中,那么在运行过程中,把这些词统一处理即可,例如回复“暂时不支持此功能哦”