前端RAG:使用Transformers.js手搓纯网页版RAG(二)- 基于qwen1.5-0.5B

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 本文继续探讨了RAG的后半部分,通过在浏览器中运行qwen1.5-0.5B模型实现了增强搜索全流程。然而,由于浏览器与模型性能限制,该方案更适合研究、离线及高隐私场景。文章提供了完整的前端代码,让读者能够动手尝试。此外,详细介绍了代码框架、知识库准备、模型初始化及问答实现等步骤,并展示了实际运行效果。受限于当前技术,除非在离线或高隐私环境下,网页大模型的应用仍需进一步优化。

书接上文,本文完了RAG的后半部分,在浏览器运行qwen1.5-0.5B实现了增强搜索全流程。但受限于浏览器和模型性能,仅适合于研究、离线和高隐私场景,但对前端小伙伴来说大模型也不是那么遥不可及了,附带全部代码,动手试试吧! 纯前端,不适用第三方接口

1 准备工作

1.1 前置知识

1.2页面代码框架

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>网页端侧增强搜索</title>
</head>
<body>
  <div id="app">
    <div>
      <input type="text" id="question" />
      <button id="search">提问</button>
    </div>
    <div id="info"></div>
  </div>
  <script type="module">
    import {
      pipeline,
      env,
      cos_sim,
    } from "https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2/dist/transformers.min.js";
    env.remoteHost = "https://hf-mirror.com";
    // 后续代码位置
  </script>
</body>
</html>

image.gif

1.3 chrom/edge浏览器

目前测试firefox模型缓存有问题,建议用这两个,首次加载模型需要点时间,后续就不需要了,记住刷新时按F5不要清空缓存了。

2 搜索代码实现

2.1 准备好知识库和初始化向量库

前一篇文章已经介绍了相关内容,本文知识库有些不一样,因为是需要给大模型去生成回答,而不是直接给出答案,所以合并在了一起。

const knowledges = [
      "问:洛基在征服地球的尝试中使用了什么神秘的物体?\n答:六角宝",
      "问:复仇者联盟的哪两名成员创造了奥创?\n答:托尼·斯塔克(钢铁侠)和布鲁斯·班纳(绿巨人浩克)。",
      "问:灭霸如何实现了他在宇宙中消灭一半生命的计划?\n答:通过使用六颗无限宝石",
      "问:复仇者联盟用什么方法扭转了灭霸的行动?\n通过时间旅行收集宝石。",
      "问:复仇者联盟的哪位成员牺牲了自己来打败灭霸?\n答:托尼·斯塔克(钢铁侠)",
    ];
    const verctorStore = [];

image.gif

2.2 定义打印输出和参数

topK控制送给大模型处理的最匹配的知识数量上下,越多的知识条数prompt越大会导致处理用时越长,一般三个最匹配的知识就差不多够用了,尤其是在网页中运行时

const infoEl = document.getElementById("info");
    const print = text => infoEl.innerHTML = text;
    const knowEl = document.getElementById("knowEl");
    const topK = 3;

image.gif

2.3 准备好嵌入和生成模型

嵌入使用 bge-base ,回答生成使用qwen1.5-0.5B

const embedPipe = pipeline("feature-extraction", "Xenova/bge-base-zh-v1.5", {
      progress_callback: (d) => {
        infoEl.innerHTML = `embed:${JSON.stringify(d)}`;
      },
    });
    const chatPipe = pipeline('text-generation', 'Xenova/Qwen1.5-0.5B-Chat', {
      progress_callback: (d) => {
        infoEl.innerHTML = `chat:${JSON.stringify(d)}`;
      },
    });

image.gif

2.4 定义向量库数据初始方法

这个不多赘述,和前一篇的类似

const buildVector = async () => {
      if (!verctorStore.size) {
        const embedding = await embedPipe;
        print(`构建向量库`)
        const output = await embedding(knowledges, {
          pooling: "mean",
          normalize: true,
        });
        knowledges.forEach((q, i) => {
          verctorStore[i] = output[i];
        });
      }
    };

image.gif

2.5 定义问答主方法

这里也不赘述过多,和上一篇不同之处在于:根据score从大到小排序,选出topK传入生成方法

const search = async () => {
      const start = Date.now()
      const embedding = await embedPipe;
      const question = document.getElementById("question").value;
      const [qVector] = await embedding([question], {
        pooling: "mean",
        normalize: true,
      });
      await buildVector();
      const scores = verctorStore.map((q, i) => {
        return {
          score: cos_sim(qVector.data, verctorStore[i].data),
          knowledge: knowledges[i],
          index: i,
        };
      });
      scores.sort((a, b) => b.score - a.score);
      const picks = scores.slice(0, topK)
      const docs = picks.map(e => e.knowledge)
      const answer = await generateAnswer(question, docs.join('\n'))
      print(answer + `(用时:${Date.now()- start}ms)`)
    };
    document.querySelector("#search").onclick = search;

image.gif

3 生成代码实现 - G

这一部分主要介绍generateAnser的实现

3.1 定义prompt

这部分自己测试时可多调整下,prompt定义的越好效果越好

const prompt =
        `请根据【上下文】回答【问题】,当得不到较为准确的答案时,必须回答我不知道。
  【上下文】
  ${context}
  【问题】
  ${question}
  请给出你的答案:
  `

image.gif

3.2 构建消息和输入

const messages = [
        { role: 'system', content: '你是一个分析助手,根据上下文回答问题。必须生成更人性化的答案。' },
        { role: 'user', content: prompt }
      ]
      console.log(messages)
      // 生成cha
      const text = generator.tokenizer.apply_chat_template(messages, {
        tokenize: false,
        add_generation_prompt: true,
      });
      console.log(text)

image.gif

3.3 等待回答返回首个答案

print(`思考中...`)
      const output = await generator(text, {
        max_new_tokens: 128,
        do_sample: false,
        return_full_text: false,
      });
      console.log(output)
      return output[0].generated_text;

image.gif

4 运行测试

4.1 等待模型加载就绪

嵌入和千问整体有接近1G的数据下载,需要稍微等待下,直到看到下图所示结果

image.gif 编辑

4.2 输入提问

我的问题是“他是怎么实现计划的”,点击提问

4.3 检查控制台输出的prompt

可以看到匹配到的三个答案和问题

<|im_start|>system
你是一个分析助手,根据上下文回答问题。必须生成更人性化的答案。<|im_end|>
<|im_start|>user
请根据【上下文】回答【问题】,当得不到较为准确的答案时,必须回答我不知道。
  【上下文】
  问:灭霸如何实现了他在宇宙中消灭一半生命的计划?
答:通过使用六颗无限宝石
问:复仇者联盟用什么方法扭转了灭霸的行动?
通过时间旅行收集宝石。
问:洛基在征服地球的尝试中使用了什么神秘的物体?
答:六角宝
  【问题】
  他是怎么实现计划的
  请给出你的答案:
  <|im_end|>
<|im_start|>assistant

image.gif

4.4 等待回复

耗时25s,有点长,但考虑到这是可以离线在端侧运行的非gpu版本,用于做一些后台任务还是可以的,结果如下 image.gif 编辑

5 总结

5.1 qwen1.5-0.5B比预期效果好

结果比续期要好一些,因为比较新的web版本大模型就找到qwen1.5-0.5B的,后续有时间我会出一期试试llama3.2-1B,但整个过程会比较长 - 因为还涉及到模型迁移

5.2 除非离线和高隐私环境网页大模型暂不适用

受限于网页性能和WebGPU的支持在transformer.js处于实验性阶段,生成用时比较久,除非是离线环境,以及对隐私要求比较高的情况下,目前的响应速度还是比较慢的

最近眼睛肿了,今天就一篇吧,剩下时间休息了,明天又得上班 ~ 啊啊啊

相关文章
|
11天前
|
前端开发 JavaScript API
(前端3D模型开发)网页三维CAD中加载和保存STEP模型
本文介绍了如何使用`mxcad3d`库在网页上实现STEP格式三维模型的导入与导出。首先,通过官方教程搭建基本项目环境,了解核心对象如MxCAD3DObject、Mx3dDbDocument等的使用方法。接着,编写了加载和保存STEP模型的具体代码,包括HTML界面设计和TypeScript逻辑实现。最后,通过运行项目验证功能,展示了从模型加载到保存的全过程。此外,`mxcad3d`还支持多种其他格式的三维模型文件操作。
|
29天前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
26天前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
1月前
|
XML 前端开发 JavaScript
前端大神揭秘:如何让你的网页秒变炫酷,让用户欲罢不能的5大绝招!
前端开发不仅是技术活,更是艺术创作。本文揭秘五大前端开发技巧,包括合理运用CSS动画、SVG图形、现代JavaScript框架、优化网页性能及注重细节设计,助你打造炫酷网页,提升用户体验。
72 30
|
29天前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
41 5
|
27天前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
66 1
|
1月前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
36 4
|
1月前
|
存储 编解码 前端开发
惊!前端新手也能秒懂的高级技巧,轻松提升网页颜值与性能!
本文针对前端新手,介绍了三个简单易学的高级技巧,帮助提升网页的颜值和性能。包括使用CSS框架快速美化网页、优化图片资源加快加载速度,以及利用ARIA属性和媒体查询提高网页的可访问性和响应性。示例代码清晰,适合初学者上手实践。
35 3
|
1月前
|
存储 前端开发 搜索推荐
(前端直接编辑CAD)网页CAD二次开发中线型表的使用方法
在DWG数据库中,线型样式存储在线型样式表 `McDbLinetypeTable` 中,每个线型表记录对象 `McDbLinetypeTableRecord` 对应一种线型样式。本文介绍了如何获取、添加、遍历、删除和修改线型样式,并提供了绘制不同线型的示例代码,包括虚线、点划线和带文字的线型。通过在线示例demo,用户可以实践修改CAD图纸中的实体线型及其样式。
|
1月前
|
搜索推荐 前端开发 开发者
前端开发的必修课:如何让你的网页在搜索引擎中脱颖而出?
【10月更文挑战第31天】前端开发不仅是构建网页与用户间桥梁的关键,还需注重搜索引擎优化(SEO)。优化网页结构、合理使用关键词、提升加载速度及整合社交媒体等技巧,能帮助网页在搜索引擎中脱颖而出,吸引更多用户。
36 5
下一篇
DataWorks