大模型实战营-初夏营-第三次课笔记

这是大模型实战营-初夏营第三次课的笔记

前言

早就听闻茴香豆——“豆哥”的鼎鼎大名,现在借着初夏营这个机会体验一把,所以先把这个作业做了

环境配置

conda 虚拟环境配置

与第一期实战营不同,本期实战营在Intern Studio上提供了非常方便的环境配置命令,仅需运行

1
studio-conda -o internlm-base -t InternLM2_Huixiangdou

即可一键配置运行豆哥所需的环境

在此基础之上,我们还需要安装一些python包:

1
2
3
4
5
6
7
8
# 安装 python 依赖
# pip install -r requirements.txt

pip install protobuf==4.25.3 accelerate==0.28.0 aiohttp==3.9.3 auto-gptq==0.7.1 bcembedding==0.1.3 beautifulsoup4==4.8.2 einops==0.7.0 faiss-gpu==1.7.2 langchain==0.1.14 loguru==0.7.2 lxml_html_clean==0.1.0 openai==1.16.1 openpyxl==3.1.2 pandas==2.2.1 pydantic==2.6.4 pymupdf==1.24.1 python-docx==1.1.0 pytoml==0.1.21 readability-lxml==0.8.1 redis==5.0.3 requests==2.31.0 scikit-learn==1.4.1.post1 sentence_transformers==2.2.2 textract==1.6.5 tiktoken==0.6.0 transformers==4.39.3 transformers_stream_generator==0.0.5 unstructured==0.11.2

## 因为 Intern Studio 不支持对系统文件的永久修改,在 Intern Studio 安装部署的同学不建议安装 Word 依赖,后续的操作和作业不会涉及 Word 解析。
## 想要自己尝试解析 Word 文件的同学,uncomment 掉下面这行,安装解析 .doc .docx 必需的依赖
# apt update && apt -y install python-dev python libxml2-dev libxslt1-dev antiword unrtf poppler-utils pstotext tesseract-ocr flac ffmpeg lame libmad0 libsox-fmt-mp3 sox libjpeg-dev swig libpulse-dev

激活环境之后可以运行下面的命令清理配置环境过程中的缓存:

1
2
pip cache purge
conda clean -y --all

其中第二条命令清理出了12GB还多的空闲空间(也有可能是之前的东西太多没清理哈哈):

下载所需模型和仓库

  • bce-embedding,bce-reranker和InternLM-2模型

Intern Studio上已经准备好了上述模型的权重,只需建立软链接即可

1
2
3
4
5
6
cd /root && mkdir model
# BCE系列模型
ln -s /root/share/new_models/maidalun1020/bce-embedding-base_v1 /root/model/bce-embedding-base_
ln -s /root/share/new_models/maidalun1020/bce-reranker-base_v1/ /root/model/bce-reranker-base_v1
# InternLM2-Chat-7B
ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-7b /root/model/internlm2-chat-7b
  • clone 源码并checkout到课程对应分支
1
2
3
4
cd /root
# 克隆代码仓库
git clone https://github.com/internlm/huixiangdou && cd huixiangdou
git checkout ded0551

修改模型路径

我们还需要修改HuiXiangDou/config.ini中的模型路径,文档是利用sed命令直接在命令行修改的,这些命令的详细解释可以参考九月大佬的笔记。还可以直接用VScode SSH连接远程主机,直接在GUI上修改成下面这样也行:

1
2
3
4
5
6
7
8
# L6
embedding_model_path = "/root/model/bce-embedding-base_v1"
# L7
reranker_model_path = "/root/model/bce-reranker-base_v1"

...
# L29
local_llm_path = "/root/model/internlm2-chat-7b"

构建语料库

  • 知识库语料

我们的目标是用豆哥做一个基于豆哥文档的RAG助手,主要用于解答用户关于豆哥的问题。首先需要从github上再次下载豆哥的源码作为语料:

1
2
3
cd /root/huixiangdou && mkdir repodir
# depth=1 仅clone最近一次commit的分支
git clone https://github.com/internlm/huixiangdou --depth=1 repodir/huixiangdou
  • 接受,拒答问题语料:

除了语料知识的向量数据库,茴香豆建立接受和拒答两个向量数据库,用来在检索的过程中更加精确的判断提问的相关性,我们需要建立这两个语料库:

  1. 接受问题语料,即希望茴香豆助手回答的示例问题

存储在 HuiXiangDou/resource/good_questions.json 中,我们需要将原有的文件备份起来,生成一份新的语料,新语料和豆哥文档相关性更强

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
cd /root/huixiangdou
mv resource/good_questions.json resource/good_questions_bk.json
# 这个echo命令可以忽略,直接在GUI中建立新文件,写入保存即可
echo '[
"mmpose中怎么调用mmyolo接口",
"mmpose实现姿态估计后怎么实现行为识别",
"mmpose执行提取关键点命令不是分为两步吗,一步是目标检测,另一步是关键点提取,我现在目标检测这部分的代码是demo/topdown_demo_with_mmdet.py demo/mmdetection_cfg/faster_rcnn_r50_fpn_coco.py checkpoints/faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth 现在我想把这个mmdet的checkpoints换位yolo的,那么应该怎么操作",
"在mmdetection中,如何同时加载两个数据集,两个dataloader",
"如何将mmdetection2.28.2的retinanet配置文件改为单尺度的呢?",
"1.MMPose_Tutorial.ipynb、inferencer_demo.py、image_demo.py、bottomup_demo.py、body3d_pose_lifter_demo.py这几个文件和topdown_demo_with_mmdet.py的区别是什么,\n2.我如果要使用mmdet是不是就只能使用topdown_demo_with_mmdet.py文件,",
"mmpose 测试 map 一直是 0 怎么办?",
"如何使用mmpose检测人体关键点?",
"我使用的数据集是labelme标注的,我想知道mmpose的数据集都是什么样式的,全都是单目标的数据集标注,还是里边也有多目标然后进行标注",
"如何生成openmmpose的c++推理脚本",
"mmpose",
"mmpose的目标检测阶段调用的模型,一定要是demo文件夹下的文件吗,有没有其他路径下的文件",
"mmpose可以实现行为识别吗,如果要实现的话应该怎么做",
"我在mmyolo的v0.6.0 (15/8/2023)更新日志里看到了他新增了支持基于 MMPose 的 YOLOX-Pose,我现在是不是只需要在mmpose/project/yolox-Pose内做出一些设置就可以,换掉demo/mmdetection_cfg/faster_rcnn_r50_fpn_coco.py 改用mmyolo来进行目标检测了",
"mac m1从源码安装的mmpose是x86_64的",
"想请教一下mmpose有没有提供可以读取外接摄像头,做3d姿态并达到实时的项目呀?",
"huixiangdou 是什么?",
"使用科研仪器需要注意什么?",
"huixiangdou 是什么?",
"茴香豆 是什么?",
"茴香豆 能部署到微信吗?",
"茴香豆 怎么应用到飞书",
"茴香豆 能部署到微信群吗?",
"茴香豆 怎么应用到飞书群",
"huixiangdou 能部署到微信吗?",
"huixiangdou 怎么应用到飞书",
"huixiangdou 能部署到微信群吗?",
"huixiangdou 怎么应用到飞书群",
"huixiangdou",
"茴香豆",
"茴香豆 有哪些应用场景",
"huixiangdou 有什么用",
"huixiangdou 的优势有哪些?",
"茴香豆 已经应用的场景",
"huixiangdou 已经应用的场景",
"huixiangdou 怎么安装",
"茴香豆 怎么安装",
"茴香豆 最新版本是什么",
"茴香豆 支持哪些大模型",
"茴香豆 支持哪些通讯软件",
"config.ini 文件怎么配置",
"remote_llm_model 可以填哪些模型?"
]' > /root/huixiangdou/resource/good_questions.json
  1. 拒绝问题语料,即希望茴香豆助手拒答的示例问题

存储在HuiXiangDou/resource/bad_questions.json 中, 其中多为技术无关的主题或闲聊。我们无需改动这些语料

  1. 测试问题

存储在HuiXiangDou/test_queries.json中,用于测试拒答效果

1
2
3
4
5
6
cd /root/huixiangdou
# 这个echo命令可以忽略,直接在GUI中建立新文件,写入保存即可
echo '[
"huixiangdou 是什么?",
"你好,介绍下自己"
]' > ./test_queries.json
  • 建立向量数据库并测试

运行以下命令建立向量数据库并使用测试问题测试

1
python3 -m huixiangdou.service.feature_store --sample ./test_queries.json

从命令行输出可以看到,豆哥接受第一个问题并且给出了最可能包含答案的文档README.md而且豆哥拒绝回答第二个问题:

运行豆哥

修改一下main.py中的测试query:

1
sed -i '74s/.*/    queries = ["huixiangdou 是什么?", "茴香豆怎么部署到微信群", "今天天气怎么样?"]/' /root/huixiangdou/huixiangdou/main.py

然后直接运行

1
python3 -m huixiangdou.main --standalone

效果如下

可以看到,豆哥的回答过程主要包括三个部分

  1. 问题打分:基于一定规则利用大模型给用户的问题打分(0~10分)低于一定阈值会直接拒答;看了一下技术报告和源码,大模型打分之前还会通过用户问题与示例问题进行余弦相似度计算进行一次打分,如果所有示例问题打分均低于配置文件中的reject_throttle, 则直接拒答;

  2. 问题主题提取:利用大模型提取问题主题,与技术不相关的主题(如天气)会被拒答;

  3. 检索并回答,就是RAG的通用pipeline了

gradio网页demo搭建

首先安装gradio依赖:

1
pip install gradio==4.25.0 redis==5.0.3 flask==3.0.2 lark_oapi==1.2.4

gradio, 启动!

1
2
cd /root/HuiXiangDou
python3 -m tests.test_query_gradio

可以使用VSCode终端的Port页面直接将远端7860端口映射到本地,之后直接访问http://127.0.0.1:7860即可进入页面

提问:茴香豆怎么部署到微信群?

回答:

进阶实践1:开启搜索引擎

  1. 首先按照文档注册Serper并将API key复制到config.iniweb_search.x_api_key

  2. 按照上一个部分的步骤搭建一个网页demo

  3. 询问一个和豆哥无关,但与good_questions.json中的示例问题相近的技术问题,结果没有回答:

翻源码看了一下code=3UNRELATED 说明提问与知识库无关,这个结果比较意外,虽然与知识库无关,但开启搜索引擎就应该有一个fallback去调用搜索引擎API,看了一下相关源码:

1
2
3
4
5
6
7
8
# huixiangdou/service/worker.py L180 ~ L186 on a deprecated branch 63f2eef0e6e4ac113b05ede0316b193948a42cda
chunk, db_context, references = self.retriever.query(
topic,
context_max_length=self.context_max_length -
2 * len(self.GENERATE_TEMPLATE))
if db_context is None:
tracker.log('feature store reject')
return ErrorCode.UNRELATED, response, references

而网络搜索是在这之后执行的,所以这里的代码逻辑可能有问题。突然想起来这个分支不是main于是我切换到main分支再试试相同的操作,结果仍是如此(看了源码,虽然代码重构成了pipeline,但是内部逻辑没变), 提个issue讨论一下这样设计的原因。

今天看到了maintainer大佬的回答,原来是我漏看了一部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# huixiangdou/service/worker.py L497 ~ L509 on branch main
for node in pipeline:
node.process(sess)

# unrelated to knowledge base or bad input, exit
if sess.code in exit_states:
break

if sess.code == ErrorCode.SUCCESS:
check.process(sess) # check用于检查回答是否正确,是否含有有害内容

# check success, return
if sess.code == ErrorCode.SUCCESS:
break

只有在检索到相关文档且回答错误时才会进行下面的网络搜索和增强搜索等操作,至于为啥不在问题不相关时进行网络搜索,大佬的解释如下:

本地检索阈值卡得很低(topk 取 100, rerank 阈值也很低), 如果检索不到,大概率是个无关的话题。

无关的话题不应该再处理了。

检索低阈值给 UNRELATED 是表示拒答, 和 is_reject 有一点差异,在 chunk 切分上。

进阶实践2:使用远程模型

前两天刚好弄了个deepseek账号,有500万免费token,就先用deepseek试一下。

  • Hybrid方式,即localremote 都启用, 本地模型用于给问题打分,远程模型用于生成回答:

不过在这个分支上似乎不能正确请求deepseek的API

在main分支上试了一下Hybrid效果:

打分过程并没有调用deepseek API

  • 非Hybrid方式(打分和回答都交给远程模型)在目前分支上会出现如下错误(现已修复:PR):

再提个issue问问吧

看issue中大佬的建议,我切回main分支试了一下,是可以正常运行的:

可以看到成功请求了deepseek API