我的学习笔记

土猛的员外

Java21新特性——ZGC、虚拟线程和结构化并发

前两天同事和我说现在可以回来看看Java了,Java17可能更新的还不多,但是Java21这次释放了一大波新特性,会是接下来五六年的一个新起点,至少这次Java21支持到2026年9月。于是我抽了点时间看了一下Java21,确实有很多新特性,总结其中几个,做个收藏。

下面就先记录三种主要特性吧:

  • 新的垃圾收集器——Generational ZGC;
  • Java的“协程”——Virtual Threads;
  • 结构化并发——Structured Concurrency。

Garbage Collection – Level Up Coding

一.Generational ZGC:新的垃圾收集器

垃圾收集”的概念本质上是关于自动内存管理的。每次创建新的数据结构时,运行时都需要内存来完成。如果我们不再需要一个对象或留下一个堆栈框架,并且它的变量超出了作用域,就必须有人清理相关的内存,即“垃圾”,否则我们将很快了解Java的OutOfMemoryError

垃圾收集(GC)的缺点是需要时间来清理和重新安排内存,这会引入了一定的运行时开销,因为GC运行的实际发生时间点通常是不确定的,并且不是手动触发的。特别是在多线程上扩展的高吞吐量、大型内存消耗的应用程序可能会遭受长时间的“GC暂停”,GC暂停的另外一个更可怕的名字是“世界停止”。但是Java之所以能和C/C++抢夺市场,很重要的一点就是Java程序员不需要手动去回收变量的内存占用,GC在这一方面贡献巨大。

1.如何选择GC

GC算法主要关注三个指标:

  • 吞吐量:在长时间内未使用GC的总时间的百分比。
  • 延迟:应用程序的整体响应性,受GC暂停的影响。
  • Footprint:应用对内存的需求,比如内存使用100M/s、GC速度80M/s,10s内存进行一次回收,那内存剩下的200M称为峰值。

与许多问题一样,你不可能针对所有问题进行优化,所以每个GC都需要在它们之间找到一个平衡。以下是一些场景及其匹配GC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 垃圾回收器 | 使用场景
====================|==========================================
* Serial | * 小数据集 (最大~100 MB max)
| * 有限的资源 (e.g., single core)
| * 低暂停时间
--------------------|------------------------------------------
* Parallel | * 多核系统的峰值性能
| * 高计算负载
| * 大于1秒的暂停是可以接受的
--------------------|------------------------------------------
* G1 | * 响应时间超过吞吐量
* CMS | * 大堆heap
| * 暂停时间小于1秒
--------------------|------------------------------------------
* Shenandoah | * 最小化暂停时间
| * 可预测的延迟
--------------------|------------------------------------------
* ZGC | * 响应时间是高优先级的,和/或
| * 非常大的堆heap
--------------------|------------------------------------------
* Epsilon GC | * 性能测试和故障排除

每种方法都有自己的优缺点,这在很大程度上取决于应用程序的需求和可用资源。

Generational ZGC在表里面的选择情况已经说的比较清楚了。

2.世代特点

“世代假设”是一种观察结果,即年轻的对象比老的对象更有可能“消失”,至少在大多数情况下是这样。这就是为什么根据对象的年龄不同处理它们是有益的。改进年轻对象的收集需要更少的资源并产生更多的内存。

即使不按世代处理,ZGC在GC暂停时间上也有很大的改进,至少在有足够的可用资源的情况下,它回收内存的速度比并发线程消耗内存的速度要快。但是,所有对象都将被存储,而不考虑它们的年龄,并且在GC运行时必须检查所有对象。

在Java 21中,ZGC将堆划分为两个逻辑代:一个用于最近分配的对象,另一个用于分配长期对象。GC可以专注于更频繁地收集更年轻、更有希望的对象,而无需增加暂停时间,将它们保持在1毫秒以下。

分代ZGC可以带来的好处:

  • 减低分配失速的风险
  • 降低所需的堆开销
  • 降低GC对CPU的影响

与不分代的ZGC相比,所有这些优点都不会显著降低吞吐量。此外,不需要自己配置代的大小、使用的线程或年龄限制。

3.如何使用

在典型的Java方式中,新的ZGC不会在可用时立即强加给我们。相反,它将与它的非代际前身一起提供。你可以用java 参数来配置:

1
2
3
4
5
# Enable ZGC (defaults to non-generational)
$ java -XX:+UseZGC

# Use Generational ZGC
$ java -XX:+UseZGC -XX:+ZGenerational

请注意,一代ZGC应该随着时间的推移取代它的前身,并成为默认的ZGC。在这一点上,你可以用拮抗参数来关闭它,通过将+(加)替换为-(减/破折号):

1
2
# Do not use Generational ZGC
$ java -XX:+UseZGC -XX:-ZGenerational

它还计划在以后的版本中完全删除非分代ZGC。

Java Virtual Threads — Easy introduction | by Ram Lakshmanan | Medium

2.Virtual Threads——Java自己的“协程”

Java的多线程并发编程一直是比较难学的,像我这样的老程序员,在设计多线程的时候,还要自己“心算”一下会不会出问题,每次都需要非常谨慎。但后来GoLang的协程出现,你会非常羡慕为什么人家可以用的如此丝滑。这不,Java自己的“协程”也来了,它就是虚拟线程——Virtual Threads。

其实虚拟线程在JDK19或JDK20中就开始出现,只是在Java21中变得更加成熟和自然。

1.虚拟线程和协程的异同点

虚拟线程是基于协程的线程,与其他语言中的协程有相似之处,但也有一些不同之处。虚拟线程附属于主线程,如果主线程被销毁,虚拟线程将不再存在。

以下是一些相似点与区别:

相似点:

  • 虚拟线程(Virtual Threads)和协程都是轻量级的,它们的创建和销毁开销比传统的操作系统线程要小。
  • Virtual Threads和协程都可以通过挂起和恢复在线程之间切换,从而避免线程上下文切换的开销。
  • Virtual Threads和协程都可以异步和非阻塞的方式处理任务,提高应用程序的性能和响应能力。

区别:

  • Virtual Threads在JVM级别实现,而协程在语言级别实现。因此,虚拟线程的实现可以与任何支持JVM的语言一起使用,而协程的实现需要特定的编程语言支持。
  • Virtual Threads是基于线程的协程实现,所以他们可以使用线程相关的api,如ThreadLocalLockSemaphore。协程不依赖于线程,通常需要特定的异步编程框架和api。
  • Virtual Threads的调度由JVM管理,协程的调度由编程语言或异步编程框架管理。因此,Virtual Threads可以更好地与其他线程协作,而协程则更适合处理异步任务。

2.虚拟线程的优势

一般来说,虚拟线程是一种新的线程类型,它可以提高应用程序的性能和资源利用率,同时还可以使用传统的线程相关api。虚拟线程与协程有许多相似之处,但也有一些不同之处。

Virtual Threads确实可以使多线程编程更容易、更高效。与传统的操作系统线程相比,创建和销毁虚拟线程的开销更小,线程上下文切换的开销也更小,因此可以大大降低多线程编程中的资源消耗和性能瓶颈。

使用Virtual Threads,开发人员可以像编写传统线程代码一样编写代码,而不必担心线程的数量和调度,因为JVM将自动管理虚拟线程的数量和调度。此外,虚拟线程还支持传统的线程相关api,如ThreadLocalLockSemaphore,这使得开发人员更容易将传统线程代码迁移到虚拟线程中。

虚拟线程的引入使多线程编程更高效、更简单、更安全,允许开发人员更多地关注业务逻辑,而不必过多关注底层线程管理。

3.如何使用虚拟线程

首先,声明一个线程类,实现from Runnable,并实现run方法。

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleThread implements Runnable{

@Override
public void run() {
System.out.println("name:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

然后,您可以使用这个线程类并启动线程。

1
2
Thread thread = new Thread(new SimpleThread());
thread.start();

拥有虚拟线程后,如何实现它?

1
Thread.ofPlatform().name("thread-test").start(new SimpleThread());

下面是使用虚拟线程的几种方法。

1. 直接启动一个虚拟线程

1
Thread thread = Thread.startVirtualThread(new SimpleThread());

2. 使用ofVirtual(),构建器模式启动虚拟线程,您可以设置线程名称,优先级,异常处理和其他配置

1
2
3
4
5
6
7
8
9
10
11
Thread.ofVirtual()
.name("thread-test")
.start(new SimpleThread());
// Or
Thread thread = Thread.ofVirtual()
.name("thread-test")
.uncaughtExceptionHandler((t, e) -> {
System.out.println(t.getName() + e.getMessage());
})
.unstarted(new SimpleThread());
thread.start();

3. 使用Factory创建线程

1
2
3
4
ThreadFactory factory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(new SimpleThread());
thread.setName("thread-test");
thread.start();

4. 使用Executor

1
2
3
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
Future<?> submit = executorService.submit(new SimpleThread());
Object o = submit.get();

image-20230926164502116

3.Structured Concurrency——结构化并发

结构化并发是一种编程范例,旨在通过提供结构化且易于遵循的方法来简化并发编程。使用结构化并发,开发人员可以创建更容易理解和调试的并发代码,并且更不容易出现竞争条件和其他与并发相关的错误。在结构化并发中,所有并发代码都被结构化为定义良好的工作单元,称为任务。任务以结构化的方式创建、执行和完成,并且任务的执行总是保证在其父任务完成之前完成。

1.结构化并发优势

结构化并发可以使多线程编程更容易、更可靠。在传统的多线程编程中,线程的启动、执行和终止都是由开发人员手动管理的,因此容易出现线程泄漏、死锁和异常处理不当等问题。

使用结构化并发,开发人员可以更自然地组织并发任务,使任务之间的依赖关系更清晰,代码逻辑更简洁。结构化并发还提供了一些异常处理机制,以便更好地管理并发任务中的异常,避免异常导致的程序崩溃或数据不一致。

此外,结构化并发还可以通过限制并发任务的数量和优先级来防止资源竞争和短缺。这些特性使开发人员更容易实现高效可靠的并发程序,而无需过多关注底层线程管理。

2.使用方法

想想下面的场景。假设您有三个任务要同时执行。只要其中任何一个任务完成并返回结果,就可以直接使用该结果,而其他两个任务可以停止。例如,天气服务通过三个通道获取天气条件,只要有一个通道返回它。

当然,在这种情况下,在Java 8下应该做的事情也是可能的。

1
2
3
List<Future<String>> futures = executor.invokeAll(tasks);

String result = executor.invokeAny(tasks);

使用ExecutorService的invokeAll和invokeAny实现,但是会有一些额外的工作。获得第一个结果后,需要手动关闭另一个线程。

在JDK21中,它可以通过结构化编程实现。

ShutdownOnSuccess捕获第一个结果并关闭任务范围以中断未完成的线程并唤醒调用线程。

在这种情况下,任何子任务的结果都可以直接获得,而无需等待其他未完成任务的结果。

它定义了获取第一个结果或在所有子任务失败时抛出异常的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws IOException {
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
Future<String> res1 = scope.fork(() -> runTask(1));
Future<String> res2 = scope.fork(() -> runTask(2));
Future<String> res3 = scope.fork(() -> runTask(3));
scope.join();
System.out.println("scope:" + scope.result());
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}

public static String runTask(int i) throws InterruptedException {
Thread.sleep(1000);
long l = new Random().nextLong();
String s = String.valueOf(l);
System.out.println(i + "task:" + s);
return s;
}

ShutdownOnFailure

执行多个任务,只要其中一个失败(发生异常或抛出其他活动异常),停止其他未完成的任务,并使用作用域。未能捕获并抛出异常。

如果所有任务都没问题,则使用Feature.get()*Feature.resultnow()来获取结果。

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
public static void main(String[] args) throws IOException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> res1 = scope.fork(() -> runTaskWithException(1));
Future<String> res2 = scope.fork(() -> runTaskWithException(2));
Future<String> res3 = scope.fork(() -> runTaskWithException(3));
scope.join();
scope.throwIfFailed(Exception::new);

String s = res1.resultNow();
System.out.println(s);
String result = Stream.of(res1, res2,res3)
.map(Future::resultNow)
.collect(Collectors.joining());
System.out.println("result:" + result);
} catch (Exception e) {
e.printStackTrace();
}
}

public static String runTaskWithException(int i) throws InterruptedException {
Thread.sleep(1000);
long l = new Random().nextLong(3);
if (l == 0) {
throw new InterruptedException();
}
String s = String.valueOf(l);
System.out.println(i + "task:" + s);
return s;
}

其他

Java21还有很多新特性,比如Scoped Values、String Templates、Switch Pattern Matching、Sequenced Collections、Record Patterns等,后面有时间在写,哈。


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



大模型主流应用RAG的介绍——从架构到技术细节

本文主要内容:

  • 大语言模型(LLM)在实际应用中存在的问题;
  • 什么是RAG——检索增强生成;
  • RAG架构解析
  • RAG技术架构的细节展示

写在前面

如果你问我现在基于LLM(大语言模型,本文有时候也会讲该名词描述为“大模型”)最火热的应用是什么,那我必须主推检索增强生成(RAG,Retrieval Augmented Generation)。RAG最初是为了解决LLM的各类问题的(后面会提到)产生的,但后面大家发现在现阶段的很多企业痛点上,使用RAG好像是更好的解决方案。就像我之前的文章《关于LLM的一些声音总结》提到的一样,企业发现其实自己要的好像是一个更好地搜索,根本不关心是不是大模型。于是,RAG被越来越多提到,包括开源的ChatPDF,也是RAG的一个经典应用。

但是我相信很多去实践RAG的人已经发现了一个情况,就是RAG入门很简单,但要真正达到企业应用的要求很难。而且RAG组成中的各类组件、流程和AI技术都过于复杂,很多人不知道如何去下手优化。

所以本文我们就来聊聊RAG,以及关于RAG优化的一些看法。

LLM的问题

首先我们还是要承认,RAG不管多厉害,它还是基于LLM的,脱离了LLM,RAG会脱离“人味儿”。

但是在今年年初的那一波大模型潮里面,我们发现如果仅仅依靠LLM,会有很多限制阻碍着我们前进,以下三点是最主要的:

  • 幻觉问题:大模型的底层原理是基于概率,所以它有时候会一本正经胡说八道,比如我们问大模型的Chat(问答系统),“XXX博物院下周一开门吗?”我相信这样的问题你不能联系问,因为大模型会有一定的几率告诉你开门。而如果游客真的在下周一去了XXX博物院,那估计就要失望了,如果这个Chat还是博物院官方提供的,那事情最终会演变成一通12345的投诉电话。所以在很多需要非常精确的场景,仅仅依赖GPT的这种生成式回答是很不严谨的,而且看很难消除——目前常见的解决方案是前置一个BERT,或者预置大量prompt做优化。
  • 新鲜度问题:规模越大(参数越多、tokens越多),大模型训练的成本越高。类似OpenAI的ChatGPT3.5,目前的数据新鲜度依然保留在2021年,对于之后的事情就不知道了。而且对于一些高时效性的事情,大模型更加无能为力,比如帮我看看今天晚上有什么电影值得去看?这种任务是需要去淘票票、猫眼等网站先去获取最新电影信息的,大模型本身无法完成这个任务。
  • 数据安全:OpenAI已经遭到过几次隐私数据的投诉,而对于企业来说,如果把自己的经营数据、合同文件等机密文件和数据上传到互联网上的大模型,那想想都可怕。如果企业人员想提一个类似这样的问题:“帮我看看3月份XX部门的销售环比数据与哪些兄弟部门的增长是密切相关的?”,这需要打穿企业内部的很多数据。既要保证安全,又要借助AI能力,那么最好的方式就是把数据全部放在本地,企业数据的业务计算全部在本地完成。而在线的大模型仅仅完成一个归纳的功能,甚至,LLM都可以完全本地化部署。

其实问题还有很多,包括tokens的限制,虽然这个长期来看不是问题,各LLM供应商的tokens数量限制肯定会越来越大。但是,费用也许就是另外一个需要考虑的问题了。

在解决这些问题的方法上,目前RAG走的比较前面。有些朋友应该已经了解过LangChain,它是架在LLM之上的一个应用框架,帮助人们快速开发基于LLM的应用。它的很多功能其实也属于RAG范畴。

什么是RAG

为RAG铺垫了这么多,下面我们来看看什么是RAG。

从开头的介绍里面大家已经可以看到,RAG——Retrieval Augmented Generation,检索增强生成。它的主要作用是生成(最终的答案),但是它先做了对现有文档的检索,而不是任由LLM来发挥。下面我提供讲一个浅显的例子来说明一下RAG:

假设一个工程师需要从厚厚的《业务操作手册》中找到相关的业务知识来帮助他完成工作,那么他有三种方式可以使用:

  • 最原始:他可以去翻阅这么厚厚的《业务操作手册》,或者用去查询这么《业务操作手册》的电子版,然后认真阅读掌握操作方法。当然,如果他碰到的业务知识比较复杂,他就需要自己去综合这么书上面的多个章节的内容,并融会贯通;
  • 借助问答机器人:他也可以直接去咨询问答机器人(chatbot),机器人也会把相应的知识吐给你。但是它可能有两个麻烦,一是它类似于FAQ,一问一答,还是需要自己去组合所有的回答内容;二是这个机器人需要前期大量的预训练知识库,需要专业的工程师去一条一条(其实是一个个知识条目,包括答案和多个相似问法),工作量极大,不太适合大面试推广使用;
  • RAG:RAG的操作方式就是我们可以直接把这个《业务操作手册》的电子版上传到系统,系统在几分钟之内就可以把这篇“巨著”变成索引,供刚才那位工程师咨询。而且RAG给的答案会去综合正本手册的多个相关知识点,并用“专家”一样的口吻来给你答案:“要解决这个问题,你需要先解决两个前提,有三种方法来解决。下面我们一步步来看怎么做….”。

好了,我们现在知道RAG是个什么玩意儿了。你有没有发现,它其实会把之前的FAQ问答给取代掉,但是它能做的远远不止这些,它还是很多应用的中间件。我们自己的一个面向文博场馆的产品就是基于RAG的,大概占了整个系统的1/3比重。另外,RAG不仅仅面向文本,它还可以面向语音、视频和图像等多模态场景,只要可以embedding的内容就可以,当然这些我们这里就不多介绍了。

RAG架构

下面我们来了解一下RAG,它有非常多的组件,但是我们可以化繁为简。我喜欢把RAG——Retrieval Augmented Generation理解为Retrieval And Generation,也就是检索与生成,在加上一个数据向量和索引的工作,我们对RAG就可以总概方式地理解为“索引、检索和生成”。

以下就是RAG的主要组成,依次是数据提取——embedding(向量化)——创建索引——检索——自动排序(Rerank)——LLM归纳生成。当然这里少了使用环节,我们暂时先忽略用户提问的环节。

1

RAG技术细节概览

在技术细节上,我们还可以分成更细的组成。

2

一、数据索引

  • 数据提取

    • 数据清洗:包括数据Loader,提取PDF、word、markdown以及数据库和API等;
    • 数据处理:包括数据格式处理,不可识别内容的剔除,压缩和格式化等;
    • 元数据提取:提取文件名、时间、章节title、图片alt等信息,非常关键。
  • 分块(Chunking)

    • 固定大小的分块方式:一般是256/512个tokens,取决于embedding模型的情况。但是这种方式的弊端是会损失很多语义,比如“我们今天晚上应该去吃个大餐庆祝一下”,很有可能就会被分在两个chunk里面——“我们今天晚上应该”、“去吃个大餐庆祝一下”。这样对于检索是非常不友好的,解决方法是增加冗余量,比如512tokens的,实际保存480tokens,一头一尾去保存相邻的chunk头尾的tokens内容;
    • 基于意图的分块方式:
      • 句分割:最简单的是通过句号和换行来做切分。当然也有通过专业的意图包来切分的,常用的意图包有基于NLP的NLTK和spaCy;
      • 递归分割:通过分治的方法,用递归切分到最小单元的一种方式;
      • 特殊分割:还有很多不常见的,用于特殊场景,这里就不提了。
    • 影响分块策略的因素:
      • 取决于你的索引类型,包括文本类型和长度,文章和微博推文的分块方式就会很不同;
      • 取决于你的模型类型:你使用什么LLM也会有不同,因为ChatGLM、ChatGPT和Claude.ai等的tokens限制长度不一样,会影响你分块的尺寸;
      • 取决于问答的文本的长度和复杂度:最好问答的文本长度和你分块的尺寸差不多,这样会对检索效率更友好;
      • 应用类型:你的RAG的应用是检索、问答和摘要等,都会对分块策略有不同的影响。
  • 向量化(embedding):这是将文本、图像、音频和视频等转化为向量矩阵的过程,也就是变成计算机可以理解的格式,embedding模型的好坏会直接影响到后面检索的质量,特别是相关度。关于embedding大家可以看我之前的一篇文章《大模型应用中大部分人真正需要去关心的核心——Embedding》,一般我们现在可以选择的embedding模型有这些:

    • BGE:这是国人开发的中文embedding模型,在HuggingFace的MTEB(海量文本Embedding基准)上排名前2,实力强劲;
    • M3E:也是国人开发的中文embedding模型,我们之前用的就是这个模型,总体来说也算可以,这个还看大家的使用场景,也许你的场景会比我们更加适用;
    • 通义千问的embedding模型:因为是1500+维的模型,所以我们在国庆节后准备用用看;
    • Text-embedding-ada-002:这是OpenAI的embedding模型,1536维,我感觉上应该是目前最好的模型,但是它在MTEB上排名好像只有第六,但是国内应该也不太能用,所以我们就放弃了;
    • 自己训练embedding模型:这是最酷的了,我过几天会专门写一篇如何训练embedding模型的文章,没有关注我的可以先关注,哈。当然,训练是基于一个既有embedding模型的,一般我们有希望让它在原来的基础上提升3%-10%的性能。

二、检索环节(Retriever)

检索环节技术含量依然很高,而且对于我们目前来说,还有一两项工作正在进行中。

检索优化一般分为下面五部分工作:

  • 元数据过滤:当我们把索引分成许多chunks的时候,检索效率会成为问题。这时候,如果可以通过元数据先进行过滤,就会大大提升效率和相关度。比如,我们问“帮我整理一下XX部门今年5月份的所有合同中,包含XX设备采购的合同有哪些?”。这时候,如果有元数据,我们就可以去搜索“XX部门+2023年5月”的相关数据,检索量一下子就可能变成了全局的万分之一;

  • 图关系检索:如果可以将很多实体变成node,把它们之间的关系变成relation,就可以利用知识之间的关系做更准确的回答。特别是针对一些多跳问题,利用图数据索引会让检索的相关度变得更高;

  • 检索技术:前面说的是一些前置的预处理的方法,检索的主要方式还是这几种:

    • 相似度检索:前面我已经写过那篇文章《大模型应用中大部分人真正需要去关心的核心——Embedding》种有提到六种相似度算法,包括欧氏距离、曼哈顿距离、余弦等,后面我还会再专门写一篇这方面的文章,可以关注我,yeah;
    • 关键词检索:这是很传统的检索方式,但是有时候也很重要。刚才我们说的元数据过滤是一种,还有一种就是先把chunk做摘要,再通过关键词检索找到可能相关的chunk,增加检索效率。据说Claude.ai也是这么做的;
    • SQL检索:这就更加传统了,但是对于一些本地化的企业应用来说,SQL查询是必不可少的一步,比如我前面提到的销售数据,就需要先做SQL检索。
    • 其他:检索技术还有很多,后面用到再慢慢说吧。
  • 重排序(Rerank):很多时候我们的检索结果并不理想,原因是chunks在系统内数量很多,我们检索的维度不一定是最优的,一次检索的结果可能就会在相关度上面没有那么理想。这时候我们需要有一些策略来对检索的结果做重排序,比如使用planB重排序,或者把组合相关度、匹配度等因素做一些重新调整,得到更符合我们业务场景的排序。因为在这一步之后,我们就会把结果送给LLM进行最终处理了,所以这一部分的结果很重要。这里面还会有一个内部的判断器来评审相关度,触发重排序。

  • 查询轮换:这是查询检索的一种方式,一般会有几种方式:

    • 子查询:可以在不同的场景中使用各种查询策略,比如可以使用LlamaIndex等框架提供的查询器,采用树查询(从叶子结点,一步步查询,合并),采用向量查询,或者最原始的顺序查询chunks等;
    • HyDE:这是一种抄作业的方式,生成相似的,或者更标准的prompt模板。

    三、生成(Gen)

    这一部反而是我比较疏忽的,因为有大量的现成框架可以使用,而且,这一步真正发挥巨大作用的是LLM。

    这里面我们使用的框架有Langchain和LlamaIndex,而且我们因为有之前的AI产品积累,所以还有一套完整的Java框架可以使用,所以这一块我没有太多研究。唯一非常关注的就是Prompt工程,我们团队内部,这一部分的工作是交给了原来AI产品的知识库运营团队来做的,他们原来做的更多是BERT相关的知识库预训练,应该说工作内容还是比较匹配的。

    在Prompt里面其实还是有很多决定因素的,这和大家对所处行业的knowhow有关。比如在文旅行业,你要知道游客或者观众一般会怎么提问,真正需要得到的是什么内容。这些我就不多说了,各行业不太一样,但是大家可以翻看我之前写的关于prompt的文章《一文讲清楚实用Prompt工程》、《高级prompt工程讲解》。

    其他的关于ReAct等内容,本文就先不涉及了,后面再专门写一篇文章。

总结

LLM这一波,催生的技术真的很多,每一个环节,要真正做好,符合企业应用,都可以让我们研究好长一段时间,并需要不断去实践,才能打磨出精品。但是如果打磨出精品,你肯定可以摘取新技术带来的果实。技术人员在很多公司地位不高?嗯,大模型这一波,我已经感觉到变化的发生了——了解大模型和不了解大模型的人,在AI的业务上的理解上,有某些角度来看,他们的区别真的就现代人类和史前人类一样巨大。

本文可能还是属于大模型应用——RAG的一个大纲式的技术普及文章,这里面的很多技术细节,我会在后面一篇篇专门来写,欢迎大家关注“土猛的员外”。


Update: 2024-01-26

我们的TorchV Bot产品目前已经开始试用了,详情可以点击:https://www.luxiangdong.com/2024/01/25/lanuch-1
目前只接受企业用户试用,需要您填写一些信息,必要信息如下:

邮箱: 用来接收地址和账号
如何称呼您:
所服务的公司:
您的职位:

当然,如果您可以告诉我们您的使用场景,我们将更加感激!
对了,可以发送到yuanwai@mengjia.net
另外,也可以直接加我微信(lxdhdgss)联系我。


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



最详细的文本分块(Chunking)方法,直接影响LLM应用效果

RAG是一个考验技术的工作

这两周发的文章大模型偏多,但如果你有阅读过前面的文章,你会发现我其实不是为了说大模型而说大模型(有点绕),我更多的是在写怎么解决企业应用中真正的难题。这个真正难题我在前面的文章中《一些LLM的声音总结》中提到过,基于大模型的企业应用中很大一部分需求就是RAG——检索增强生成。

1

这个流程依然无法描述RAG的复杂性

RAG涉及的内容其实广泛,包括Embedding、分词分块、检索召回(相似度匹配)、chat系统、ReAct和Prompt优化等,最后还有与LLM的交互,整个过程技术复杂度很高。如果你用的LLM非常好,反而大模型这一块是你最不需要关心的。而这些环节里面我们每个都没达到1(比如0.9、0.7…),那么最终的结果可能是这些小数点的乘积。如果我们每个环节都可以做到>1.0,那么最终的结果会比上一个结果高出很多。

今天我们来聊聊分块,很重要的一个环节(没有哪个环节不重要),但它也许是我们容易做到高质量的一个环节。

什么是分块?

在构建RAG这类基于LLM的应用程序中,分块(chunking)是将大块文本分解成小段的过程。当我们使用LLM embedding内容时,这是一项必要的技术,可以帮助我们优化从向量数据库被召回的内容的准确性。在本文中,我们将探讨它是否以及如何帮助提高RAG应用程序的效率和准确性。

6

Pinecone是领先的向量数据库供应商

在向量数据库(如:Pinecone)中索引的任何内容都需要首先Embedding。分块的主要原因是尽量减少我们Embedding内容的噪音

例如,在语义搜索中,我们索引一个文档语料库,每个文档包含一个特定主题的有价值的信息。通过使用有效的分块策略,我们可以确保搜索结果准确地捕获用户查询的需求本质。如果我们的块太小或太大,可能会导致不精确的搜索结果或错过展示相关内容的机会。根据经验,如果文本块尽量是语义独立的,也就是没有对上下文很强的依赖,这样子对语言模型来说是最易于理解的。因此,为语料库中的文档找到最佳块大小对于确保搜索结果的准确性和相关性至关重要

另一个例子是会话Agent。我们使用embedding的块为基于知识库的会话agent构建上下文,该知识库将代理置于可信信息中。在这种情况下,对分块策略做出正确的选择很重要,原因有两个:

  • 首先,它将决定上下文是否与我们的prompt相关。
  • 其次,考虑到我们可以为每个请求发送的tokens数量的限制,它将决定我们是否能够在将检索到的文本合并到prompt中发送到大模型(如OpenAI)。

在某些情况下,比如使用具有32k上下文窗口的GPT-4时,拟合块可能不是问题。尽管如此,当我们使用非常大的块时,我们需要注意,因为这可能会对我们从向量数据库获得的结果的相关性产生不利影响。

在这篇文章中,我们将探讨几种分块方法,并讨论在选择分块大小和方法时应该考虑的权衡。最后,我们将给出一些建议,以确定适合您的应用程序的最佳块大小和方法。

Embedding长短内容

当我们在嵌入内容(也就是embedding)时,我们可以根据内容是短(如句子)还是长(如段落或整个文档)来预测不同的行为。

当嵌入一个句子时,生成的向量集中在句子的特定含义上。当与其他句子Embedding进行比较时,自然会在这个层次上进行比较。这也意味着Embedding可能会错过在段落或文档中找到的更广泛的上下文信息。

当嵌入一个完整的段落或文档时,Embedding过程既要考虑整个上下文,也要考虑文本中句子和短语之间的关系。这可以产生更全面的向量表示,从而捕获文本的更广泛的含义和主题。另一方面,较大的输入文本大小可能会引入噪声或淡化单个句子或短语的重要性,从而在查询索引时更难以找到精确的匹配。

查询的长度也会影响Embeddings之间的关系。较短的查询,如单个句子或短语,将专注于细节,可能更适合与句子级Embedding进行匹配。跨越多个句子或段落的较长的查询可能更适合段落或文档级别的Embedding,因为它可能会寻找更广泛的上下文或主题。

索引也可能是非同质的,并且包含“不同”大小的块的Embedding。这可能会在查询结果相关性方面带来挑战,但也可能产生一些积极的后果。一方面,由于长内容和短内容的语义表示不一致,查询结果的相关性可能会波动。另一方面,非同构索引可能捕获更大范围的上下文和信息,因为不同的块大小表示文本中的不同粒度级别。这可以更灵活地容纳不同类型的查询。

分块需要考虑的因素

在确定最佳分块策略时,有几个因素会对我们的选择起到至关重要的影响。以下是一些事实我们需要首先记在心里:

  1. 被索引内容的性质是什么? 这可能差别会很大,是处理较长的文档(如文章或书籍),还是处理较短的内容(如微博或即时消息)?答案将决定哪种模型更适合您的目标,从而决定应用哪种分块策略。

  2. 您使用的是哪种Embedding模型,它在多大的块大小上表现最佳?例如,sentence-transformer[1]模型在单个句子上工作得很好,但像text- embedt-ada -002[2]这样的模型在包含256或512个tokens的块上表现得更好。

  3. 你对用户查询的长度和复杂性有什么期望?用户输入的问题文本是简短而具体的还是冗长而复杂的?这也直接影响到我们选择分组内容的方式,以便在嵌入查询和嵌入文本块之间有更紧密的相关性。

  4. 如何在您的特定应用程序中使用检索结果? 例如,它们是否用于语义搜索问答摘要其他目的?例如,和你底层连接的LLM是有直接关系的,LLM的tokens限制会让你不得不考虑分块的大小。

没有最好的分块策略,只有适合的分块策略,为了确保查询结果更加准确,有时候我们甚至需要选择性的使用几种不同的策略。

分块的方法

分块有不同的方法,每种方法都可能适用于不同的情况。通过检查每种方法的优点和缺点,我们的目标是确定应用它们的正确场景。

固定大小分块

这是最常见、最直接的分块方法:

我们只需决定块中的tokens的数量,以及它们之间是否应该有任何重叠。一般来说,我们会在块之间保持一些重叠,以确保语义上下文不会在块之间丢失。在大多数情况下,固定大小的分块将是最佳方式。与其他形式的分块相比,固定大小的分块在计算上更加经济且易于使用,因为它在分块过程中不需要使用任何NLP库。

下面是一个使用LangChain执行固定大小块处理的示例:

1
2
3
4
5
6
7
8
text = "..." # your text
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
separator = "\n\n",
chunk_size = 256,
chunk_overlap = 20
)
docs = text_splitter.create_documents([text])

Content-Aware:基于内容意图分块

这是一系列方法的组合,利用我们正在分块的内容的性质,并对其应用更复杂的分块。下面是一些例子:

句分割——Sentence splitting

正如我们之前提到的,许多模型都针对Embedding句子级内容进行了优化。当然,我们会使用句子分块,有几种方法和工具可以做到这一点,包括:

  • Naive splitting: 最幼稚的方法是用句号(。)和“换行”来分割句子。虽然这可能是快速和简单的,但这种方法不会考虑到所有可能的边缘情况。这里有一个非常简单的例子:
1
2
text = "..." # 你的文本
docs = text.split(".")
  • NLTK[3]: 自然语言工具包(NLTK)是一个流行的Python库,用于处理自然语言数据。它提供了一个句子标记器,可以将文本分成句子,帮助创建更有意义的分块。例如,要将NLTK与LangChain一起使用,您可以这样做:
1
2
3
4
text = "..." # 你的文本
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter()
docs = text_splitter.split_text(text)
  • spaCy[4]: spaCy是另一个用于NLP任务的强大Python库。它提供了一个复杂的句子分割功能,可以有效地将文本分成单独的句子,从而在生成的块中更好地保存上下文。例如,要将space与LangChain一起使用,您可以这样做:
1
2
3
4
text = "..." # 你的文本
from langchain.text_splitter import SpacyTextSplitter
text_splitter = SpaCyTextSplitter()
docs = text_splitter.split_text(text)

3

spacy-llm package

递归分割

递归分块使用一组分隔符以分层和迭代的方式将输入文本分成更小的块。如果分割文本开始的时候没有产生所需大小或结构的块,那么这个方法会使用不同的分隔符或标准对生成的块递归调用,直到获得所需的块大小或结构。这意味着虽然这些块的大小并不完全相同,但它们仍然会逼近差不多的大小。

这里有一个例子,如何配合LangChain使用递归分块:

1
2
3
4
5
6
7
8
9
text = "..." # 你的文本
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
# 设置一个非常小的块大小。
chunk_size = 256,
chunk_overlap = 20
)

docs = text_splitter.create_documents([text])

专门的分块

Markdown和LaTeX是您可能遇到的结构化和格式化内容的两个例子。在这些情况下,可以使用专门的分块方法在分块过程中保留内容的原始结构。

4-2

本文就是用markdown写作的
  • Markdown: Markdown是一种轻量级的标记语言,通常用于格式化文本。通过识别Markdown语法(例如,标题、列表和代码块),您可以根据其结构和层次结构智能地划分内容,从而生成语义更连贯的块。例如:
1
2
3
4
5
from langchain.text_splitter import MarkdownTextSplitter
markdown_text = "..."

markdown_splitter = MarkdownTextSplitter(chunk_size=100, chunk_overlap=0)
docs = markdown_splitter.create_documents([markdown_text])
  • LaTex: LaTeX是一种文档准备系统和标记语言,通常用于学术论文和技术文档。通过解析LaTeX命令和环境,您可以创建尊重内容逻辑组织的块(例如,节、子节和方程),从而产生更准确和上下文相关的结果。例如:
1
2
3
4
from langchain.text_splitter import LatexTextSplitter
latex_text = "..."
latex_splitter = LatexTextSplitter(chunk_size=100, chunk_overlap=0)
docs = latex_splitter.create_documents([latex_text])

确定应用程序的最佳块大小

如果常见的分块方法(如固定分块)不容易适用于您的用例,这里有一些指针可以帮助您找到最佳的块大小。

  • 清洗数据 :在确定应用程序的最佳块大小之前,您需要首先预处理清洗数据以确保质量。例如,如果您的数据是从web爬取的,您可能需要删除HTML标记或特定的元素,保证文本的“纯洁”,减少文本的噪音。
  • 选择一个范围的块大小 :一旦你的数据被预处理,下一步是选择一个范围的潜在块大小进行测试。如前所述,选择时应考虑内容的性质(例如短文本还是长文档)、将要使用的Embedding模型及其功能(如token限制)。目标是在保留上下文和保持准确性之间找到平衡。从探索各种块大小开始,包括较小的块(例如,128或256个tokens)用于捕获更细粒度的语义信息,较大的块(例如,512或1024个tokens)用于保留更多上下文。
  • 评估每个块大小的性能 :很傻,但是很稳重的一种方式。为了测试不同大小的块,您可以把不同大小的块进行标记。使用可以覆盖你的业务场景效果的数据集,为要测试的各个大小的块创建Embedding,并将它们保存下来。然后,你可以运行一系列查询来评估质量,并比较不同块大小的性能。这是一个反复测试的过程,在这个过程中,针对不同的查询测试不同的块大小,直到找到最佳的块大小。

结论

好了,说了这么多,最后我要说的结论你可能会失望,那就是我们自己也还没有找到最佳的分块方式,哈哈。但是对于不同的业务场景,我们现在比刚开始做RAG应用的时候会有经验很多了。

引用:

1.sentence-transformer:https://huggingface.co/sentence-transformers

2.text- embedt-ada -002:https://openai.com/blog/new-and-improved-embedding-model

3.NLTK:https://www.nltk.org/

4.spaCy:https://spacy.io/

5.Chunking Strategies for LLM Applications:https://www.pinecone.io/learn/chunking-strategies/


Update: 2024-01-26

我们的TorchV Bot产品目前已经开始试用了,详情可以点击:https://www.luxiangdong.com/2024/01/25/lanuch-1
目前只接受企业用户试用,需要您填写一些信息,必要信息如下:

邮箱: 用来接收地址和账号
如何称呼您:
所服务的公司:
您的职位:

当然,如果您可以告诉我们您的使用场景,我们将更加感激!
对了,可以发送到yuanwai@mengjia.net
另外,也可以直接加我微信(lxdhdgss)联系我。


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



Embedding——从入门到生产使用

搜索功能已经深入到我们的日常生活中,我们常说“Google一下就知道了”,用户已经开始期望几乎每个应用程序和网站都提供某种类型的搜索功能。随着有效搜索变得越来越相关(双关语),寻找新的方法和体系结构来改进搜索结果对于架构师和开发人员来说至关重要。从基础开始,这篇博文将描述Redis中利用深度学习模型创建的向量Embeddings的人工智能搜索功能。

向量Embedding

什么是向量Embedding?简单地说,向量Embedding是可以表示许多类型数据的数字列表。

向量Embedding非常灵活。音频、视频、文本和图像都可以表示为矢量Embedding。这种特性使得向量Embedding成为数据科学家工具箱中的瑞士军刀。

Embedding creation process

从不同类型的数据创建向量Embedding的过程:音频,文本,视频。

为了解释为什么Embedding提供了这样的实用程序,让我们看一下以前处理文本数据(如表格数据中的分类值)的方法。数据科学家有时会使用one-hot编码等方法将分类特征转换为数值。这些编码将为每种类型的类别创建一个列。值为1表示项目属于该列指定的类别。相反,值为0表示项目不属于该类别。

例如,考虑书籍类型:“小说”、“非小说”和“传记”。每一种体裁都可以编码成一个热向量,然而,这样的向量会非常稀疏,因为书籍通常只属于两个体裁。下图显示了这种编码是如何工作的。注意这里0的数量是1的两倍。对于像图书类型这样的类别,随着更多的类型被添加到数据集中,这种稀疏性将会呈指数级恶化。

稀疏性会给ML模型带来挑战。对于每一种新的类型,编码表示的大小都会增长,因此数据集的计算成本会变得很高。

One-hot encoding example

One-hot(独热)编码示例

对于图书类型,或者任何具有相对较少类别的分类数据,我们可以使用简单的one-hot编码,但是,对于整个英语语言呢?对于这种规模的语料库,这种编码方法将变得不切实际。

进入向量Embedding

向量Embedding呈现固定大小的表示,不随数据中观测值的数量而增长。由模型创建的结果向量,通常是384个浮点值,比其他编码方法(如one-hot编码)的表示密度要高得多。这意味着在更少的字节中存在更多的信息,因此在计算上的利用成本更低。正如您稍后将读到的,这些密集表示可以用于多种目的,例如反向图像搜索、聊天机器人、问答和推荐系统。

创建向量Embedding

为了理解向量Embedding是如何创建的,对现代深度学习模型的简要介绍是有帮助的。

机器学习模型不使用非结构化数据。为了使模型能够理解文本或图像,我们必须将它们转换为数字表示。在机器学习之前,这样的表示通常是通过Feature Engineering“手工”创建的。

随着深度学习的出现,复杂数据中的非线性特征交互是由模型学习而不是人工设计的。当一个输入遍历深度学习模型时,该输入数据的新表示将以不同的形状和大小创建。每一层通常关注输入的不同方面。深度学习的这一方面,从输入中“自动”生成特征表示,构成了如何创建向量Embedding的基础。

例如,考虑在ImageNet数据集上训练的著名的ResNet模型。ResNet是一种卷积神经网络(CNN),通常用于与图像相关的任务。在这种情况下,ResNet被训练来预测图像中的对象属于1000个类中的哪一个。

在训练过程中,ResNet将捕获图像中存在的特征信息,通过将图像传递给多个卷积层、池化层和完全连接层。这些图层将捕获边缘、线条和角等特征,并将它们分组到传递给下一个图层的“桶”中。由于CNN的空间不变特性,无论边缘或直线出现在图像的哪个位置,这些特征都将始终映射到相同的桶。这些层将通过模型的层变得越来越小,直到一个由1000个浮点值组成的完全连接的层作为输出。每个值代表1000个类中的1个。该值越高,图像中的对象属于该类的概率就越大。

Diagram of a simple Convolutional Neural Network (CNN)

简单卷积神经网络(CNN)示意图

ResNet和其他类似的图像分类模型回答了这个问题:“这个图像中是什么类型的对象?”然而,这些分类在回答诸如“哪些图像与此图像相似?”之类的prompts时用处不大。对于这个问题,我们需要一起比较图像。尽管没有专门针对这项任务进行训练,ResNet仍然很有用,因为它可以捕获图像的密集表示。

简单地说,CNN和其他类似的模型学习有用的数据表示,以执行图像分类等任务。当输入通过模型的各个层时,可以提取这些表示。被提取的层,也称为潜在空间,通常是靠近模型输出的层。在上图中,这可能是包含768或500个隐藏单位的图层。提取的层或潜在空间提供了一个密集的表示,其中包含有关当前特征的信息,这对于视觉相似性搜索等任务在计算上是可行的。

这是向量Embedding。

存在大量的预训练模型,可以很容易地用于创建向量Embedding。Huggingface Model Hub (https://huggingface.co/models)包含许多模型,可以为不同类型的数据创建Embedding。例如,[all-MiniLM-L6-v2模型](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)是在线托管和运行的,不需要专业知识或安装。

sentence_transformers这样的包,也来自HuggingFace,为语义相似度搜索、视觉搜索等任务提供了易于使用的模型。要使用这些模型创建Embeddings,只需要几行Python代码:

1
2
3
4
5
6
7
8
9
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

sentences = [
"That is a very happy Person",
"That is a Happy Dog",
"Today is a sunny day"
]
embeddings = model.encode(sentences)

语义相似度搜索的向量Embedding

语义相似搜索是将文本片段进行比较,以找出包含最相似含义的文本的过程。虽然这对普通人来说似乎很容易,但语言是相当复杂的。将非结构化文本数据提炼成机器学习模型可以理解的格式一直是许多自然语言处理研究人员的研究主题。

向量Embeddings为任何人提供了一种执行语义相似搜索的方法,而不仅仅是NLP研究人员或数据科学家。它们提供了一种有意义的、计算效率高的数字表示,可以通过预先训练的模型“开箱即用”来创建。下面是一个语义相似度的例子,它概述了用上面所示的sentence_transformers库创建的向量Embedding。

让我们看看下面的句子:

  • “That is a happy dog(那是一只快乐的狗)”
  • “That is a very happy person(那是一个非常幸福的人)”
  • “Today is a sunny day(今天是个晴天)”

这些句子中的每一个都可以转换成向量Embedding。下面是一个简化的表示,突出显示了这些示例句子在二维向量空间中相对于彼此的位置。这对于从视觉上衡量我们的Embedding如何有效地表示文本的语义意义非常有用。下文将详细介绍。

A simplified plot of vector embeddings projected into 2 dimensions.

向量Embeddings投影到二维的简化图

假设我们要将这些句子与“那是一个快乐的人”进行比较。首先,我们为查询语句创建向量Embedding。

1
2
3
4
5
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

# create the vector embedding for the query
query_embedding = model.encode("That is a happy person")

接下来,我们需要比较查询向量Embedding和数据集中的向量Embedding之间的距离。

有很多方法可以计算向量之间的距离。当涉及到语义搜索时,每种方法都有自己的优点和缺点,但我们将在另一篇文章中讨论。下面显示了一些常见的距离度量。

5-1

用于计算向量相似度的距离度量。

在这个例子中,我们将使用余弦相似度来度量两个向量的内积空间之间的距离。

Formula for Cosine Similarity

余弦相似度公式

在Python中,这看起来像

1
2
def cosine_similarity(a, b):
return np.dot(a, b)/(norm(a)*norm(b))

在我们的查询向量和上图中的其他三个向量之间运行这个计算,我们可以确定句子之间的相似程度。

2D plot showing the cosine similarity between the vector embeddings created from our sentences earlier

2D图显示了之前从我们的句子中创建的向量Embeddings之间的余弦相似性

你可能已经猜到,“That is a very happy person(那是一个非常幸福的人)”和“That is a happy person(那是一个幸福的人)”是最相似的句子。这个例子只捕获了向量Embeddings的许多可能用例中的一个:语义相似搜索

下面列出了运行整个示例的Python代码

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
import numpy as np

from numpy.linalg import norm
from sentence_transformers import SentenceTransformer

# Define the model we want to use (it'll download itself)
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

sentences = [
"That is a very happy person",
"That is a happy dog",
"Today is a sunny day"
]

# vector embeddings created from dataset
embeddings = model.encode(sentences)

# query vector embedding
query_embedding = model.encode("That is a happy person")

# define our distance metric
def cosine_similarity(a, b):
return np.dot(a, b)/(norm(a)*norm(b))

# run semantic similarity search
print("Query: That is a happy person")
for e, s in zip(embeddings, sentences):
print(s, " -> similarity score = ",
cosine_similarity(e, query_embedding))

在安装NumPysentence_transformers之后,运行这个脚本应该会得到以下计算结果

1
2
3
4
5
>>> Query: That is a happy person

>>> That is a very happy person -> similarity score = 0.94291496
>>> That is a happy dog -> similarity score = 0.69457746
>>> Today is a sunny day -> similarity score = 0.25687605

该脚本的结果应该与所选模型在HuggingFace inference API上看到的结果一致。

HuggingFace inference API similarity results

HuggingFace推理API相似度结果

向量Embedding搜索在生产环境中的使用

开发和生产是两个不同的东西,在学习了更多之后,你可能会开始问这样的问题:

  • 我把这些向量存储在哪里?
  • API应该是什么样子?
  • 如何将其与过滤等传统搜索功能结合起来?

幸运的是,开发Redis的好人们决定为你找出这些问题,并将向量相似搜索(VSS)功能构建到现有的reresearch模块中。这基本上把Redis变成了一个低延迟的向量数据库。

Redis as a vector database

Redis是一个矢量数据库

VSS功能是作为reresearch模块的新功能而构建的。它允许开发人员像在Redis散列中存储任何其他字段一样轻松地存储向量。它提供了在大型向量空间中执行低延迟搜索所需的高级索引和搜索功能,通常分布在许多机器上的向量从数万到数亿不等。

Redis现在支持两种类型的向量索引:

  1. Flat
  2. 分级可导航小世界(HNSW)

以及3个距离度量:

  1. LP —— 欧几里得距离
  2. IP —— 内积
  3. cos —— 余弦相似度(如上图所示)

下面是一个使用redis -py在向量被加载到Redis后创建索引的例子。

1
2
3
4
5
6
7
8
9
10
11
12
from redis import Redis
from redis.commands.search.field import VectorField, TagField

def create_flat_index(redis_conn: Redis, number_of_vectors: int, distance_metric: str='COSINE'):

image_field = VectorField("img_vector","FLAT", {
"TYPE": "FLOAT32",
"DIM": 512,
"DISTANCE_METRIC": distance_metric,
"INITIAL_CAP": number_of_vectors,
"BLOCK_SIZE": number_of_vectors})
redis_conn.ft().create_index([image_field])

索引只需要创建一次,当新的哈希值存储在Redis中时,索引会自动重新索引。在将向量加载到Redis中并创建索引之后,就可以为各种基于相似性的搜索任务形成和执行查询。

索引只需要创建一次,当新的哈希值存储在Redis中时,索引会自动重新索引。在将向量加载到Redis中并创建索引之后,就可以为各种基于相似性的搜索任务形成和执行查询。

更好的是,所有现有的reresearch功能,如文本、标签和基于地理的搜索,都可以与VSS功能协同工作。这被称为“混合查询”。对于混合查询,传统的搜索功能可以用作矢量搜索的预过滤器,这可以帮助限制搜索空间。

上面的索引创建函数(create_flat_index)可以很容易地通过添加新字段(如redis-py中的TagFieldTextField)来支持混合查询。

Redis VSS演示

最近,我构建了一个web应用程序来探索这些功能。Fashion Product Finder利用了Redis中新的VSS功能,以及我最喜欢的Redis生态系统的其他部分,如redis-om-python。您可以访问应用程序在这里

注册使用该应用程序后,您将看到如下所示的页面。

Fashion Product Finder application

时尚产品查找应用程序,使用由Redis提供的向量相似度搜索

要通过文本表示查询类似的产品,请找到您喜欢的产品并单击by text。同样,要通过视觉向量搜索查询,请单击产品上的by Image按钮。

可以为产品的性别和类别设置混合搜索属性,以便在执行矢量搜索时,返回的项目将通过这些标记进行过滤。下面是选择右下角黑色手表时的视觉向量搜索示例。

Fashion Product Finder search results

通过图片查询类似手表后的搜索结果为黑色G-Shock手表。

这个演示是一种探索Redis VSS功能的有趣方式,然而,这并不是应用程序中使用的Redis生态系统的唯一组件。事实上,Redis是这个应用程序使用的唯一数据库,用RedisJSON存储产品元数据,用RediSearch存储矢量数据。

您可以查看整个代码库在这里。如果你觉得有用,请点赞和分享!

有关Redis和reresearch模块中VSS的更多信息,您可以查看以下资源:

参考文档


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



不开玩笑——在家部署1800亿参数的Falcon大模型

0

Falcon的发布机构TII官网

前几天Falcon-180B大模型发布,在Hugging Face的所有LLM(大语言模型)里面排名第一,应该也是迄今为止参数量最大的开源预训练模型了。就性能来说,我看很多国外的专家都在给他背书,说仅次于GPT-4,和PaLM2相当,也就是说性能已经超过了大部分常用的ChatGPT3.5(GPT-3.5)了。开始我是不相信的,但是马上一想,这是TII(阿联酋阿布扎比技术创新研究所)发布的,我信了,因为他们是一群有“钞”能力的人。

很快,我们吕总就开始问我了,搞不搞。我一想到1800亿参数,那还不得上百张A100啊,不可能的,直接拒绝了。但是意识到可以自己武断了,于是再去好好查看了他们的网站,果然是我唐突了。官方说推理的话,400GB的显存就可以了,也就是6张A100-80GB就可以了(实际需要大概8张A100),大概也就是84万元(实际需要112万元)人民币(不考虑其他设备成本的话)。

好吧,这个“也就”用的有点飘,我感觉到了。

说实话180B的大模型,目前还没有太多中文benchmark出来,所以,我还是很想去尝试一下,看看效果的。

于是,我找到了一篇文章:《Falcon 180B: Can It Run on Your Computer?》,可能大多数人不一定能打开原文,那么可以看我下面的一些转述。

注:本文中180B中的B代表billion,也就是十亿,所以180B=1800亿。

2023年5月,阿布扎比技术创新研究所(TII)已经发布了两种预训练大语言模型:Falcon-7b和Falcon-40b,以及它们的聊天版本(Chat)。这两个模型表现出了非常好的性能,并曾经在OpenLLM排行榜上排名第一。

TII发布的第三个大模型就是Falcon-180B,一个1800亿个参数模型。它比Llama2-70b多2.5x参数,比Falcon-40b多4.5x参数。

关于Falcon-180B的一些基本情况(1)

  • 预训练3.5万亿tokens(2)
  • 使用Apache 2.0 license分发
  • 容量大小为360GB
  • 在OpenLLM排行榜(3)上排名第一(截至2023年9月11日)

img

OpenLLM排行榜截图(2023年9月11日)

还有一个聊天(Chat)版本。这些模型可以在Hugging Face中心找到:

Falcon-180B是完全免费和最先进的。但它也是一个巨大的模型

那么,它能在你的电脑上运行吗?

除非你的计算机已经准备好进行非常密集的计算,否则它无法开箱即用地运行Falcon180B。您将需要升级您的计算机并使用该模型的量化版本。

接下来,我们来看看如何在消费类硬件上运行Falcon-180B。我们将看到,在家用计算机上运行一个1800亿个参数的模型是可行的。我还讨论了几种有助于减少硬件需求的技术。

在电脑上加载Falcon180B所需的条件

您需要知道的第一件事是,Falcon180B有1800亿个参数,存储形式为bfloat16。一个bfloat16参数在内存中占2个字节。

当你加载一个模型时,标准Pytorch管道是这样工作的:

  1. 创建一个空模型:180B个参数* 2字节= 360 GB
  2. 在内存中加载其权重:180B个参数* 2字节= 360 GB
  3. 在步骤1创建的空模型中加载步骤2加载的权重
  4. 将步骤3得到的模型移动到设备上进行推理,例如GPU

步骤1和步骤2消耗内存。总的来说,您需要720 GB的可用内存。这可以是CPU RAM,但为了快速推理,您可能希望使用GPU,例如,9块A100(80GB)VRAM刚好是720GB,但实际上最好是12块A100。

无论是CPU RAM还是VRAM,这都是很大的内存。幸运的是,目前其实有一些方法可以“曲线”完成。

在HuggingFace,Falcon-180B使用安全防护器格式(safetensors format)分发。与标准Pytorch格式相比,这种格式有几个优点。它(几乎)没有复制(注:不需要有一个巨大或等量内存区域来做中间缓存),所以模型直接加载到第1步创建的空模型中。它节省了很多内存。

关于safetensors

safetensors 节省内存,而且它也使模型更安全运行,因为没有任意代码可以存储在这种格式。Safetensors模型的加载速度也快得多。当您从Hugging Face下载模型时,请使用此格式而不是“.bin”格式,以便更快、更安全、更节省内存。

尽管看起来我们跳过了第2步,但仍然会有一些内存开销。TII在Hugging Face的模型介绍上写着400GB的内存可以工作。这仍然很大,但比使用标准Pytorch格式少了220gb。

我们需要一个400 GB的设备,例如,5个A100(80G)的GPU VRAM,但这个结果离“消费级”家用电脑配置还很远。

在多个存储设备上分布式Falcon180B

你可能没有一个400 GB的内存设备,但如果你把以下所有设备的内存组合起来,你的电脑可能有超过400 GB的内存:

  • GPU VRAM:如果你有NVIDIA RTX 3090或4090,那已经是24GB了。
  • CPU RAM:大多数现代计算机至少有12GB的CPU内存,CPU的扩展也非常便宜。
  • 硬盘(或SSD):SSD一般都可以有几个TB存储空间,而且是可以用SWAP做虚拟内存的。请注意,如果您计划使用SSD(固态硬盘)运行LLM,那么它将比原来的机械硬盘驱动器速度快得多,但是仅仅是高铁比汽车快的意思,和飞机、火箭差距依然很大。

为了利用可用的设备,我们可以拆分Falcon-180B,以便它按照以下优先顺序使用设备的最大可用内存:GPU, CPU RAM和SSD。

这很容易通过Accelerate上的device_map架构来实现。

Device_map将模型的整个层放在您拥有的不同设备上。

img

device_map架构示意图

如果您想查看device_map使用的一些示例,请参考这篇文章

Device_map对于解决CUDA内存不足问题来说确实是一种很好的方法,但如果你打算在消费级硬件上使用Falcon180B,那还远远不够理想。即使是拥有24 GB VRAM的GPU(比如RTX 4090)和32 GB CPU RAM的高端配置,它也依然还需要动用几百GB的硬盘空间。

这里有两个原因:

  • 硬盘驱动器/SSD总体来说比GPU VRAM、CPU RAM慢得多。从硬盘装载和运行Falcon180B需要很长时间。
  • 消费类硬盘和固态硬盘不是为这种密集计算使用设计和测试的。如果模型的许多部分被装载到硬盘驱动器上,系统将不得不在推理期间多次访问和读取模型的大量分割部分。这是长时间的大量读取操作。如果您连续数天进行推理,例如生成一些合成数据集,这可能会损坏您的硬盘,或者至少会显著降低其预期寿命。

为了避免过度使用硬盘,我们最好的方式就是去除硬盘方案,那么剩下来的解决方案实在不多了:

  • 增加一个GPU:大多数高端主板可以容纳两个RTX 3090/4090。它会给你48 GB的VRAM。
  • 扩展CPU RAM:大多数主板有4个插槽可用于CPU RAM套件。4x128GB CPU内存的套件有出售,但不容易找到,而且仍然很贵。 注意:您的操作系统可以支持的CPU RAM总量也有限制。Windows 10是2tb。如果你使用的是一个老版操作系统,你应该在购买更多的内存之前看看它的文档
  • 量化版Falcon180B和扩展CPU RAM

Falcon180B的量化版是减少内存消耗的最佳方案之一。

使用量化版Falcon180B减少RAM尺寸

现在的常见做法是将非常大的语言模型量化到较低的精度。GPTQ(4)和bitsandbytes nf4(5)是将LLM量化到4位精度的两种常用方法。

Falcon180B在使用bfloat16的情况下,我们看到它的尺寸是360 GB。

一旦量化到NF4精度,它只需要90GB(1800亿个参数* 0.5字节)。我们可以用100GB内存(90GB +一些内存开销)加载NF4的Falcon-180B。

如果你有24 GB的VRAM,你“只”需要75 GB的CPU RAM。这仍然是很多,但比加载原始模型便宜得多,并且在推理期间不会需要和硬盘上的模型层做交换。注意:您仍然需要100 GB的可用硬盘空间来存储模型

你甚至不需要GPU。使用128 GB的CPU RAM,您可以仅使用CPU进行推理。

量化本身是非常昂贵的。值得庆幸的是,我们已经可以在网上找到量化的版本。TheBloke:https://huggingface.co/TheBloke 发布了使用GPTQ制作的4位版本:

注:也有3位模型可作为模型的“分支”。按照4位型号卡的说明来获取它们

虽然模型的精度降低了,但根据Hugging Face的实验,模型的性能仍然相似这一点太棒了!!!)。

GPTQ模型的推理速度很快,您可以使用LoRA适配器对它们进行微调,可以在这里查看如何微调使用GPTQ量化的Llama 2(6)

虽然可以对GPTQ模型进行微调,但我不推荐这样做。使用QLoRA进行微调具有类似的内存消耗,但由于使用nf4进行了更好的量化,因此产生了更好的模型,如QLoRA论文(7)所示。

最后

总而言之,您需要量化版本(4-bit Falcon180B)和100GB内存才能在高配置的家用计算机上运行Falcon-180B。

对于快速推理微调,你需要使用到GPU。RTX 4090(或RTX 3090 24GB,更实惠,但速度更慢)将足以加载量化模型的1/4。如果你的电脑机箱有足够的空间,你甚至可以放两张RTX卡。

如果你想在一个只有CPU的配置上运行Falcon-180b,也就是说,没有任何GPU,那你只把放弃微调的想法,它会慢到你怀疑人生,嗯,推理也同样会很慢。如果要在纯CPU的机器上运行,那llama.cpp(C++重写的llama)是个不错的选择。我们只能期待Falcon180B也有这样的版本,那在存内存的环境下运行Falcon180B才有可能。

我目前家里电脑的配置是RTX 4090(24GB) + 32GB内存,如果再买一块64GB的内存,好像就可以跑起来了哈哈。64GB的内存大概1400元,虽然有些舍不得,但感觉有点心动。

引用:

1.Falcon-180B模型介绍:https://huggingface.co/tiiuae/falcon-180B

2.RefinedWeb:https://huggingface.co/datasets/tiiuae/falcon-refinedweb

3.OpenLLM排行榜:https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard

4.GPTQ:https://arxiv.org/abs/2210.17323

5.bitsandbytes nf4:https://arxiv.org/abs/2305.14314

6.微调使用GPTQ量化的Llama 2:https://kaitchup.substack.com/p/quantize-and-fine-tune-llms-with?source=post_page-----c3f3fb1611a9--------------------------------

7.QLoRA论文:https://arxiv.org/abs/2305.14314


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



使用frp轻松搞定ssh家里的服务器

起因

前些天听我邻居介绍了ngrok可以做内网穿透,这样就可以把我家里的那台带了RTX4090的PC用起来。于是我折腾了一通,发现ngrok配置是简单的,但用它自己的server来做转发,网络太慢了,于是就放弃了。昨晚在Github搜索ngrok的替代品时,发现有人整理了一个非常完整的tunneling工具列表frp赫然列在第一位。

list

图1:tunneling工具列表(部分)

frp的优势

相比于ngrok,这次的frp更符合我的需求。frp的一些特性优势:

  • 自定义服务端:可以使用自己的云服务器做服务端,速度有保障(确实很快);
  • 类型支持丰富:包括http/https/ssh/ssh2/dns/socket/ftp/xtcp等类型;
  • 文档出色:有中文文档,如果你有高级需求,那文档还是很重要的。

也许ngrok也有这些功能,但我没有耐心继续找下去了,frp更符合我。

frp安装

frp安装其实比较简单。

在介绍安装之前,先介绍一下我的PC配置,你们可以按自己的情况下载和安装:

  • 本地服务器:i7/RTX4090/32G/2TB,Ubuntu Server 22.04.3 LTS,对应的就是你自己家里的电脑;
  • 云服务器:阿里云,轻应用服务器(612元/年,买了一年多了),CentOS,3M带宽。

因为都是linux系统,所以我下载的是frp_0.51.3_linux_amd64.tar.gz,大家可以在这里下载

如果是公众号文章,会无法直接点击跳转,您可以点击左下角的“阅读原文”,原文是我的blog,那里的链接可以直接点击。

我们可以把这个文件分别传到本地服务器和云服务器,然后用tar -xvf frp_0.51.3_linux_amd64.tar.gz把文件解开,我们可以发现里面的文件如下:

frp_folder

图2:frp目录下的文件
  • frpc:对应的是你家里的电脑;
  • frps:对应的是云服务器,公网上的;
  • xxxx.ini:这是配置文件,你要做的主要配置仅仅在frpc.inifrps.ini这两个文件里面。

其实解压好之后,安装其实就算完成了,对于存放的目录其实没太多要求。

frp配置

配置可能是这里面最复杂的了,但是总体上也非常简单,只要你照着我下面的步骤做。

【服务器端】

先来看服务端,在我这里就是那台有公网IP的阿里云服务器。

进入到frp的目录,输入以下命令打开frps.ini:

1
sudo vim frps.ini

在这里,我们以配置ssh为例,你可以先看一下官方文档

guanf

图3:frps的服务端和客户端配置

教程已经非常清楚了,我无需再详细解读,这里我贴一下我的配置,以便大家可以对照看看如何修改。

1
2
3
[common]
bind_port = 7000
vhost_http_port = 8080

这里如果大家只是配置ssh的话,只需要上面的bind_port = 7000,注意=两边的空格。下面的vhost_http_port是为了放开家里服务器的web服务的。

然后用命令:

1
sudo ./frps -c ./frps.ini

启动服务端的服务。

注意:一般是先启动服务端(也就是公网上的),再启动本地的。

【注册成系统服务】

目前的命令在我们的终端关闭之后就会结束,所以如果你要持续运行,可以将它注册成系统服务。官方也提供了很详细的文档,使用systemd,很简单,在服务端和本地端都可以用,我就不再赘述了。

【本地配置】

好,接下来我们来配置本地这一端,也就是你家里的PC。

本地端的配置是在你自己的电脑上(怕大家弄错了,多啰嗦一下),修改的是frpc.ini,对,结尾是c,不是s

官方的配置说明已经在图3展示了,你也可以直接进入官方文档进行查看。

我贴一下我的frpc.ini配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[common]
server_addr = 121.xxx.xx.175
server_port = 7000

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000


[web]
type = http
local_port = 80
custom_domains = www.luxxxxxxxxxxxxxong.com

我不仅仅是ssh,还配置了一个web。

隐去一些具体信息。

然后,启动你的本地PC上的服务:

1
sudo ./frpc -c ./frpc.ini

好了,你试试看。在其他电脑上用下面的命令试试ssh:

1
ssh -oPort=6000 username@x.x.x.x

x.x.x.x是你的公网服务器IP地址。

【无法访问的原因】

如果你的测试过程有问题,那一多半的可能是你的云服务器对外端口未开放。你配置里面涉及的端口,如6000/7000/8080,都需要在阿里云服务器上开通。

一般在“控制台”–>“轻量应用服务器/ECS云服务器”–>“防火墙/安全组规则”。

端口

图4:云服务器的出入站规则

结束

OK,我想你应该和我一样可以开心地跑起来了,速度还可以!

如果你有具体的问题,可以邮件咨询我:lxdhzzj@gmail.com

下面是我的ssh附图:

ssh6000


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



关于LLM(大语言模型)的一些声音

这篇文章是最近听到的一些播客(如《此话当真》),以及之前读到的一些文章,当然还有自己在公司产品中的一些实践中产生的想法归纳。

这波LLM,巨头和创业者/小公司其实处在同一起跑线,但是小公司/创业者真的没必要去做LLM,还是应该聚焦在基于AI的应用上,需要去做错位竞争。

主要原因有二:

  • 这次LLM是在第一时间给市场看到了最好的产品——ChatGPT,其他竞争对手和OpenAI都还存在代差。一些国内外的Benchmark大家看看就得了,只要他们声称自己的新模型超过GPT-3.5,接近GPT-4.0,那在全领域来比较多半还差距很大,所以不要全信。
  • 后面大模型的竞争首先会成为卡的竞争,也就是钱。对于大部分企业来说,无法获得如此多的资金,而且去All in一个可能还是未知数的方向。大可在基于LLM的应用上多发力,比如没有什么垂直大模型,只有垂直应用。

数据还是壁垒吗?

那必须是!

但开放数据已经变得不值一提了,大家都可以获取,哪怕所谓的垂直LLM,如果你的训练数据是可以公开获取的,那壁垒也不高。比如司法LLM、医药LLM等行业。

什么样的数据才是壁垒?

那些不属于你所有,但与你共生的数据才是最重要的数据。比如你帮客户建立AI服务,中间产生的很多数据,你可以触碰和分析,但是不能做它途使用的数据。这部分数据以后应该是最多的,而且符合数据安全的相关条例。我们现在产品中的数据也属于这一类。

这部分数据,在企业应用中大多数是:1.内部协同数据;2.企业的客户使用数据。在个人层面,这部分数据更多是设备端数据。

LLM在未来的一种变形是做的事情更专注、更小型化,比如可以装载在设备上,做边缘计算,这些LLM可以帮助人们的生活更加简单。

RAG vs Fine-tuning

Fine-tuning(微调)是用一定量的数据集对LLM进行局部参数的调整,以期望LLM更加理解我们的业务逻辑,有更好的zero-shot能力。

RAG(检索增强生成)的做法是把企业内部的文档数据先进行embedding,借助检索先获得大致的知识范围答案,再结合prompt给到LLM,让LLM生成最终的答案。

说实话两种方式都不简单,但是Fine-tuning 的成本似乎更大一些。所以目前有一种趋势就是更倾向RAG方式,毕竟对于客户本身来说,操作空间会更大,他们可以通过管理文档来调整最终的检索和问答能力。所以目前有一些预测已经旗帜鲜明地认为fine-tuning的需求一定会下降。

传统程序员怎么办?

首先我觉得没有什么可以惊慌的,如果你原来的工作就很有价值,以后依然会很有价值。

AI算法工程师很重要,但是没有重要到离谱的程度,甚至现在很多招聘其实对AI算法工程师的热度是在下降的(Q3与Q2相比)。因为大厂拿到牌照了,加上Llama2、ChatGLM2等开源商用授权的发布,让很多公司稍稍从恐慌中心里有底了一些。

做过基于LLM产品的人现在大概都知道了,一个产品/平台,最终组成部分,80%是产品工程,20%是创新技术(国内的话,创新技术有10%其实已经很优秀)。所以,对于传统程序员和产品经理,一方面拥抱AI,另外一方面真正要支棱起一个AI应用,更大的工程量还是要靠你们。

对于AI应用的需求

在和B端客户交流中发现,65%的需求都是信息的检索、汇总和再生成,其实更像上面提到的RAG。

另外第二大需求(约占20%)是流程自动化、决策辅助,BI等需求上AI驱动的应用升级。

文生图和代码生成的需求在B端其实很低,和我们之前预期的差距很大。

很多大客户对于结果的可靠性要求非常高,很多项目都已经进入POC阶段了,因为达不到想要的可靠性(哪怕是再降低一些)而被否决。

甚至有些客户后面直接就想通了,我要的就是一个更好的搜索而已,但是LLM好像做不到。到最后,大家都冷静下来了,客户知道自己要的是一个解决方案,解决他业务中的痛点,而不是一个大模型。

客户一般都知道或者用过最好的(ChatGPT),所以他们的需求会天马行空,然后接触到国内基于LLM的产品之后,感觉落差巨大。

运营兜底

但是基于LLM的产品也不是就没救了,别这么悲观。现在碰到的问题是:我们把LLM神化了,感觉什么都可以通过LLM一劳永逸的解决了,但实际真不是这样的。

我们回想过往所有产品,精准度都是很高的,我们怎么做到的呢?运营啊!加人力成本啊!

到目前为止,要有非常好的精准度,依然必须要有运营兜底。基于生成式的LLM在幻想方面是个黑盒,不要说我们,OpenAI也没办法完全控制。

运营介入的方法简单说有三类:

1.prompt优化,当然不排斥针对某些行业和客户的“穷举”优化;

2.反馈迭代:获得使用中用户的反馈,进行微调,或者补充知识;

3.BERT前置:对于一些需要非常谨慎的服务内容,还是需要用预训练语料去完成,只要覆盖了需要这些高精度需求,其他的可以用GPT托底。

AI产品经理的核心素质

国内LLM的能力其实大家都差不多,所以对于基于LLM的业务最后拼的就是运营和反馈机制建立。

  • 运营:就是我上面一节说的这个意思,我们原来小知在行业里面占据领先地位也是这个做法;

  • 反馈:必须是建立一种可以持续获得反馈的机制。Midjourney生成四张图,每次让人选择一张(一般情况下会认为是更符合客户需求的),这种方式就是一种非常好的反馈机制。

所以,作为AI产品经理,最最重要的就是这两件事

  • 认清现实:知道我们完全靠机器是不太可能做好所有的事情的,然后如何用运营来弥补这种缺失,达到最好的交付效果;
  • 反馈飞轮:如何在交互上做出一个良好的反馈机制,让“模型-应用-反馈-优化”这个飞轮可以更加快速运转起来。

如果没有这两方面的考虑,都不能称之为一个合格的AI产品经理。

再说说运营能力,也可以作为一种标准服务提供给企业,或者运营也可以是一种AI浪潮下的企业员工培训,让企业员工如何适应AI产品来提高生产力。

避免过早优化

整体来说,中文LLM的能力还非常差,很多时候都体会不到真正LLM的“魔力”。

但是我对于中文LLM的发展还是非常乐观的,所以我们在做基于LLM产品的时候,可以考虑一年之后我们可以拿到一个类似GPT4差不多水平的LLM。

言外之意,即使在现在,业务优先,AI在后

应用与LLM的切割,做好架构,先做好自己的应用,做到随时切换各个不同的LLM。

或者,我们现在做业务,也可以不要一个猛子扎进去,可以有意与LLM有一个边界。

PLUS:现在做的三款产品

一、2019年就开始做的AI助手产品,在颐和园、长隆、良渚、普陀山等140多家景区有应用,原来是纯BERT的,现在加了GPT兜底;

二、基于RAG的产品,在行政服务中心、招商等部门都有一定的应用;

三、文博行业产品,在客户端,给原来纯用声音听的功能做了可视化升级,在后台用大模型做了内容的快速处理与合成,比传统内容制作效率提升很多。非直接生成,那质量非常不可控。

好了,就写到这里!


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



Transformer架构的整体指南

介绍

Transformer模型于2017年发明,并首次在开创性论文“Attention is All You Need”(Vaswani等人,2017)中提出,它对深度学习做出了革命性的贡献,可以说是对整个计算机科学的贡献。作为一个神经机器翻译的工具,它已经被证明是深远的,扩展了它的适用性,超越了自然语言处理(NLP),巩固了它作为一个通用的、通用的神经网络架构的地位。

在这个全面的指南中,我们将剖析Transformer模型的核心,深入探索从其注意机制到编码器-解码器( encoder-decoder)结构的每个关键组件。我们不会停留在基础层面,我们将遍历利用transformer功能的大型语言模型,深入研究它们独特的设计属性和功能。进一步扩大视野,我们将探索transformer模型在NLP之外的应用,并探讨这一有影响力的架构当前的挑战和潜在的未来方向。此外,还将为那些有兴趣进一步探索的人提供一个精心策划的开源实现列表和补充资源。

transformer

图0:我们将在本文中深入探讨的Transformer体系结构。改编自(Vaswani et al. 2017)。

Transformers出现之前的神经网络

Transformer神经体系结构的设计者对寻找一种可以用于序列到序列建模的体系结构很感兴趣。这并不是说没有现有的序列建模体系结构,只是它们有很多缺点。还有哪些类型的神经网络可以用于序列建模?它们的缺点是什么?让我们在激励Transformers的过程中寻找这些问题的答案。

多层感知器(MLP)

让我们从多层感知器(MLP)开始,这是一种经典的神经网络方法。MLP本身并不是超级强大,但你会发现它们几乎集成在任何其他架构中(令人惊讶的是,甚至在transformer中)。MLP基本上是一个线性层序列或完全连接的层。

2mlps

图1:多层感知器(MLP)。

在AI社区找到各种模式的最佳架构之前,MLP长期以来一直用于建模不同类型的数据,但有一点是肯定的,它们不适合序列建模。由于它们的前馈设计,它们不能保持序列中信息的顺序。当数据的顺序丢失时,序列数据就失去了意义。因此,MLP不能保持信息的顺序使其不适合序列建模。此外,MLP需要许多参数,这是神经网络可能具有的另一个不希望的属性。

卷积神经网络

卷积神经网络(CNN 或ConvNets)是一类神经网络架构,以处理图像和其他模式(如文本和视频)而闻名。

3convnets

图2:用于文本理解的卷积神经网络(X。Zhang and LeCun 2015)。

迄今为止,卷积神经网络在小尺度和大尺度视觉识别方面取得了成功,但在序列建模方面并不十分成功。由于它们的局部性(计算被捆绑在输入数据的局部部分),它们很容易并行化(对GPU很好),它们需要许多层来处理长期依赖关系。与固定长度的图像相反,大多数顺序数据具有可变长度,这是ConvNets或MLP都无法处理的。

循环神经网络

与MLP或ConvNets不同,递归神经网络(RNN)的设计考虑了序列。RNN在其设计中具有反馈回路,这是其模拟序列数据能力的关键因素。RNN的另一个令人满意的特性是它们可以处理可变长度的数据。

RNN的连接方式存在一些基本问题。首先,由于它们的顺序设计,它们可能对长期序列不稳定。

img

图3:循环神经网络(RNN)。

循环网络有很多变体。其中一个著名的版本是长短期记忆(LSTM)。LSTM可以处理长期序列。它们有一个细胞状态(下图中的水平直线)和门,它们都平滑信息流。

img

图4:长短期记忆(LSTM)

LSTM的另一个稍微有效的版本是门循环单元(GRUs)。LSTM对于基本的序列建模问题非常有效,但它们的应用范围仍然有限。正如我们之前所说,它们不能被平行化,也就是说它们不能被缩放。此外,即使它们能够保持信息的顺序,它们也无法推断出它们正在处理的数据的全局上下文。背景很重要。以机器翻译为例(这个任务基本上给了我们transformer),被翻译的句子的上下文和顺序一样重要。

我们所做的基本上就是激励Transformers。到目前为止,我们已经看到,先前的神经网络要么不适合序列建模,要么不可并行化,要么不稳定,要么受上下文长度的限制,这些都是序列神经结构的主要特征。

现在我们有了正确的背景知识,让我们深入了解transformer架构。

Transformer架构

Transformer是一种神经网络架构,可以处理文本、音频、视频和图像等顺序数据(作为图像补丁的序列)。Transformer不使用任何循环层或卷积层。它的基础层叫做Attention(注意力)。它还包含其他基本层,如全连接层、规范化层[主要是LayerNorm](Ba, Kiros, and Hinton 2016)、Embedding层和位置编码层。在下一节中,我们将看到这些层的作用。

6transformer

图5:Transformer体系结构。改编自(Vaswani et al. 2017)。

正如我们在开头提到的,transformer最初是为机器翻译引入的,这是一项需要处理两个序列(输入和输出都是序列)的任务。因此,transformer模型有两个部分:处理输入的encoder和生成输出的解码器。关于encoder、解码器和其他层的更多信息将在下面讨论。

Encoder(编码器)

编码器是transformer结构的主要模块之一,位于输入序列的输入端。编码器将输入序列转换为压缩表示。在原始的transformer架构中,encoder重复了6次(这取决于架构的整体尺寸,可以更改)。每个encoder块有3个主要层,即多头注意(MHA),层规范和mlp(或根据论文的前馈)。

多头注意和mlp在transformer论文中被称为子层。子层之间有层归一化,层之间有dropout和残差连接(各层的正确流程见图)。

如前所述,encoder层数为6层。encoder层的数量越多,模型就越大,模型就越有可能捕获输入序列的全局上下文,从而产生更好的任务泛化。

Decoder(解码器)

解码器几乎与encoder相同,除了在encoder的输出上操作额外的多头注意。解码器的目标是将encoder输出与目标序列融合,并进行预测(或预测下一个token)。

在解码器中占用目标序列的注意力被屏蔽,以防止当前(正在处理的)token关注目标序列中的后续tokens。如果解码器可以访问完整的目标序列,这基本上是作弊,并且可能导致模型不能泛化到训练数据之外。

解码器通常也与encoder重复相同的次数。在原transformer中,解码器块的数量也是6块。

Attention(注意力)

注意力到底是什么?

注意力是transformer结构的基本要素。从本质上讲,注意是一种机制,它可以让神经网络更多地关注输入数据中包含有意义信息的部分,而较少关注输入的其余部分。

早在transformer架构引入之前,注意力机制就被用于各种任务中。注意力的概念最早出现在神经机器翻译(NMT)方法中,该方法使用注意力来寻找输入句子中最相关信息集中的[位置集](Bahdanau, Cho, and Bengio 2014)。因为他们基于注意力的NMT可以联合或同时对齐和翻译,所以它比以前的方法表现得更好。如下图所示,该网络能够在翻译的句子中找到正确的单词顺序,这是之前的神经机器翻译方法难以实现的壮举。

7attention

图6:神经机器学习翻译中的源句和目标句对齐(Bahdanau, Cho, and Bengio 2014)。x轴和y轴分别表示源句和翻译句。每个像素表示源(输入)token与其对应的目标token的关注权重。对角线上的注意表示单词按相应的顺序排列(例如:对->L ‘ accord sur la)。注意可以找出正确的词序(例如:European Economic Area ->区域(欧洲)。

上面的图片是怎么回事?你能发现什么吗?在翻译的句子中,只要在目的语中有意义,单词的顺序就会被颠倒。因此,在翻译一个句子时,注意不仅可以赋予模型正确翻译句子的能力,而且还可以根据目标语言的上下文按照正确的顺序进行翻译。简而言之,在将一种语言翻译成另一种语言时,注意力可以识别和保存语境。

另一个使用注意力的早期工作是在神经图像字幕中发现的(Xu et al. 2015)。在这项工作中,作者使用卷积神经网络进行特征提取,并使用带有注意力机制的rnn来生成与输入图像最一致的标题。下图(取自论文)显示了模型大致关注的地方。

8attention-cap

图7:用神经字幕模型生成字幕。白色区域显示了模型在生成标题“一个女人在公园里扔飞盘”时聚焦的位置。图片来自Xu et al. 2015)。

在全局层面上,在图像字幕模型中集成注意机制有助于模型在生成字幕时关注输入图像中有意义的部分。

9attention-cap2

图8:模型可以在生成标题时关注关键对象。图片取自Xu et al. 2015)。

我们上面使用的两个例子都证明了注意力的有效性。注意力确实是一种神奇的机制,它允许神经网络专注于包含有意义信息的部分输入数据,而较少关注其余输入数据。

现在我们了解了注意力,让我们看看transformer体系结构中注意力函数的输入:查询、键和值。

注意力功能:Query, Key, Value(查询、键、值)

直观地说,注意力实际上是“关注输入数据中最重要的部分”。从技术上讲,注意力测量两个向量之间的“相似度”,并返回“加权相似度分数”。一个标准的注意力函数有三个主要输入:查询、键和值向量。在分解注意力函数之前,让我们试着理解键、值和查询的含义。

查询、键和值是搜索引擎和数据库系统中常用的术语。为了理解这些术语,让我们举一个简单的例子:1假设你正在搜索ArXiv上基于注意力的论文。理想情况下,您将在搜索框中输入查询。在内部,ArXiv可以通过一组预定义的键来组织论文。在ArXiv给出你所要求的论文之前,它会将你的查询与那些预定义的密钥集进行比较,并返回与查询和密钥对应最匹配的论文。Values仅指数据库中的所有论文。作为免责声明,我们使用这个示例来理解查询、键和值在搜索和数据库系统上下文中的含义。这并不是试图展示ArXiv系统是如何工作的。

img

图9:ArXiv论文搜索系统中的查询、键和值示例。

对查询、键和值有了这样直观的理解之后,让我们转向注意力函数的数学表示。

11

由上式可知,Q、K、V分别为查询矩阵、键矩阵、值矩阵。我们计算查询和键的点积,并将乘积除以一个比例因子dk的平方根。比例因子用于避免QK^T^的大值会导致小梯度的情况。然后,我们用softmax将点积归一化为概率分布(这基本上给了我们加权和),并通过将其与值相乘,我们得到加权值。

img

图10:点积注意力的图形表示。图改编自(Vaswani, 2017)

上面描述的注意力被称为缩放点积注意力,一种改进的点积注意力(Luong, Pham, and Manning 2015)。还有其他类型的注意力,如添加性注意力(Bahdanau, Cho, and Bengio 2014),基于内容的注意力(Graves, Wayne, and Danihelka 2014),基于位置的注意力(Bahdanau, Cho, and Bengio 2014)和一般注意力(Luong, Pham,and Manning 2015)。每一种注意力类型都可以应用于全局(整个输入数据),因此是全局注意力,也可以应用于局部(输入数据的子部分),因此是局部注意力。

你可能听说过transformer是平行的,你可能想知道它是从哪里来的。变压器并联源于注意函数。如果查询、键和值都是矩阵,则可以在两个主要的矩阵乘法中执行注意力,因此不涉及循环或任何循环操作。gpu的计算注意力更快。对于更大的模型(数以十亿计的参数)和大量的训练数据(数以十亿/万亿计的tokens),注意力可能是昂贵的,因为每个token都要参加其他tokens,这需要二次时间复杂度

注意

如果查询、键和值来自同一来源,则应用于它们的关注称为自关注。如果它们来自不同的来源,我们说***交叉注意。

多头注意力

我们在上面描述的是一个单一的注意层。在实践中,只有一个注意层通常不会得到好的结果。相反,人们倾向于并行计算多个注意层,并将结果串联起来。简而言之,这就是多头注意力。多头注意力基本上是在线性投影的QKV向量上计算的多个独立注意力。在下面的多头注意图中,将连接的注意值线性投影到模型维度。

img

图11:多头注意力。图改编自(Vaswani, 2017)

正如transformer架构的设计者所解释的那样,并行计算多个关注点允许模型“共同关注来自不同位置的不同表示子空间的信息”。*(Vaswani et al. 2017)。关于多头注意力的一个令人惊讶的事情是,它不会增加总体计算成本,因为每个头像的维度是头像数量的十分之一(i。E、底座transformer的磁头是整体模型尺寸的8倍(即512倍)。因此,如果模型(文中dmodel)的维数为512,则多头注意中的头像数为8,则每个头像为512/8=64。

在ConvNets中,多头注意力可以被视为深度可分离卷积(Chollet 2017)。深度可分离卷积是一种特殊类型的卷积,它将输入张量分成多个通道,在每个通道上独立操作,连接各个输出,并将结果提供给点卷积(1x1卷积相当于线性投影)。

MLPs

MLPs或多层感知器2是encoder和解码器中的两个子层之一。Transformer中的MLPs由两个线性层组成,中间有ReLU激活,它们独立且相同地应用于每个位置。

img

图12:transformer中的多层感知器(MLP)。

Embeddings和位置编码层

transformer架构包含两个Embedding层:一个在encoder处用于处理输入或源序列,另一个在解码器处用于处理目标或输出序列。这些Embedding层将输入或输出tokens转换为固定大小的密集向量,本质上是将序列中的每个token映射到特定的密集向量。利用Embeddings是语言建模的标准实践,因为它们提供了语义深度。有了这些嵌入的token向量,那些具有相似语义的向量倾向于在同一方向上对齐3

基本transformer中的Embeddings尺寸为512(这是整个模型的尺寸)。作为旁注,transformer架构在整个网络中保持相同的尺寸,基本模型为512。这就是之前所说的dmodel模型。

位置编码在Transformer模型中的encoder和解码器的初始阶段都是不可或缺的组件。它们用于保持序列中tokens的顺序。有人可能会质疑这些位置Embeddings的必要性。这源于注意力机制固有的排列不变性,因此修改tokens的顺序不会改变输出加权值4。因此,注意机制本身缺乏对token顺序的意识。由于transformer架构不包含任何其他递归方法,因此引入位置编码以使模型具有序列中tokens的位置感知。实质上,如果没有位置编码,Transformer确实会表现出排列不变性。然而,这样的设计对于序列顺序具有重要意义的任务来说是不够的,就像大多数NLP任务一样。

为了在序列中编码位置信息,transformer的设计者使用了不同频率的正弦函数。他们还尝试了习得的位置Embeddings,但结果并没有什么不同。

残留连接,层规范化,和Dropout

残差连接是神经网络设计的核心,也是现代深度学习中流行的成分之一。自从深度残差网络在计算机视觉中证明了可观的性能(He et al. 2016)以来,残差连接已被用于几乎大多数神经网络,不仅用于视觉,还用于其他模式。事实上,现在几乎不可能看到一个不使用剩余连接的神经网络模型。残差连接缓解了梯度不稳定问题,有助于模型更快收敛。

transformer的作者之一Ashish Vaswani曾经说过,“在其他信息中,剩余连接将位置信息传递到更高层。”看看下面的图片!

img

图13:剩余连接将信号传递到更高层,从而改善了transformer模型的训练。第一幅图像的平滑对角线(带有残差)显示了残差连接的有效性。图片由Ashish Vaswani在CS224N拍摄。

层归一化(Ba, Kiros, and Hinton 2016)也是现代神经网络中最常用的归一化技术之一。层归一化通过用层均值和方差对层的激活进行归一化,显著减少了训练时间。与批归一化(Ioffe and Szegedy 2015)不同,批归一化是用在小批上计算的均值和方差对每一层进行归一化,而层归一化只是用每个激活的均值和方差对每一层进行归一化。层规范化在训练和测试阶段保持相似的行为,不像批规范化在这两个阶段表现出不同的行为。

有两种方法可以在transformer体系结构中实现层规范化。第一种方法称为后层规范化(Post- ln),其中层规范化放置在残馀块之间(或在每个子层之后(多头注意和mlp),但在加法之后)。第二种选择称为预层规范化(Pre- ln),其中层规范化放置在剩余块内的每个子层之前。标准transformer架构使用Post-LN,但在训练原始transformer的更新代码库5中,发现它是Pre-LN。纸张和代码之间的不匹配使得很难追溯到初始transformer中层规范化的实际位置,但从提交历史来看,似乎后来使用了Pre-LN。作者本可以更新这篇论文,但他们可能并不介意,因为没有人知道这篇论文会成为神经网络设计领域有影响力的参考论文之一。

img

图14:层后归一化(Post- ln)和层前归一化(Pre- ln)

因此,层归一化应该在哪里并不明确,这是一个积极的研究问题。最近一项关于Pre-LN和Post-LN影响的研究(Xiong et al. 2020)表明,在多头注意力和mlp之前进行层归一化(Pre-LN)可以提高训练效果,并且比在多头注意力和mlp之后进行层归一化收敛得快得多。该研究还声称,使用Pre-LN,你不需要聪明地选择学习率调度器,因为Pre-LN有更好的初始化。前ln和后ln都不是完美的。最近的另一项研究引入了ResDual(Xie et al. 2023),通过引入层归一化的附加残差连接,基本上缓解了ln前和ln后的问题。

应该在哪里放置图层规范化仍然是一个问题,但这应该不是一个问题。正如许多人注意到的那样,transformer似乎是一种通用架构。在语言建模、视觉识别和多模态学习方面,原始的香草transformer(有一些调整,如yes LN)仍然是大多数新颖作品的背后,并且有数百万的作品声称可以改进transformer。因此,我们的目标应该是保持这种架构的普遍性。我们将在文章末尾的高效Transformers中看到更多这方面的内容。

在我们结束本节之前,让我们讨论一下transformer架构中的dropout(Srivastava et al. 2014)。层规范化可以作为一种副作用的正则化器,但您仍然需要其他形式的网络正则化来处理过拟合。Dropout应用于每个子层的输出(在加法和归一化之前)。它还适用于encoder和解码器堆栈中的Embeddings和位置编码之和。有关训练transformer中使用的其他正则化技术和其他训练细节,请查看论文以获取更多信息。

线性和Softmax图层

解码器后的线性层获取解码激活并将其投影到词汇表的大小。这个线性层基本上会产生对数。softmax层将获取这些逻辑并将它们转换为下一个令牌概率。下一个预测的token基本上是softmax输出的argmax。

可视化的关注

注意力可以从输入序列中捕获整体上下文,这通常会提高模型的性能。通过将注意力可视化,我们可以看到输入序列的哪些部分对模型的输出有显著影响。这有助于我们更好地理解Transformer神经网络的内部工作原理。

img

图15:使用ExBert可视化注意力。

上图描绘了GPT-2第8^th^层的注意力头(Radford et al. 2019)。从图中可以清楚地看出,即使在transformer的早期层中,大多数tokens也是相互关联的。

随着时间的推移,许多可视化注意力的工具已经发展起来,以帮助深度学习社区理解transformer模型内部的情况。其中最著名的工具是BertViz(Vig 2019) 6。我们用来进行上述可视化的ExBert也是一个优秀而简单的工具,用于可视化大多数基于transformer的模型(如GPT-2和BERT)的注意力(Devlin et al. 2019)。

注意力的利弊

注意机制导致了序列建模和其他可以作为序列框架的模式的重大转变。与其他序列网络(如循环网络和1D卷积)相比,注意力网络具有许多优势。下面简要讨论这些问题:

  • 长期依赖:传统的循环神经网络(rnn),包括长短期记忆(LSTM)和门控循环单元(GRU)等变体,容易出现长期依赖的问题,即模型保留信息的能力随着时间的推移而减弱。注意机制允许模型直接访问输入序列中的任何点,从而保留整个上下文,从而帮助缓解了这个问题。
  • 并行化:与需要顺序计算的rnn不同,基于注意力的模型,如transformer架构,可以并行处理输入序列中的所有tokens。这使得它们的计算效率更高,并且可以更好地与当前的硬件加速器配合使用。
  • 可解释性:注意提供一定程度的可解释性,因为它突出了模型认为对产生特定输出最重要的输入部分。“注意图”可以帮助我们理解模型在“思考”什么。
  • 全局上下文:在卷积神经网络(cnn)中,接受野通常是局部的,取决于核大小,可能导致更广泛的上下文丢失。然而,需要注意的是,每个输出token都可以考虑输入序列中每个token的信息,从而保留全局上下文。
  • 提高性能:基于注意力的模型,特别是那些利用transformer架构的模型,在许多NLP任务中取得了最先进的性能,优于RNN和CNN同行。他们还推动了计算机视觉、语音识别、机器人、多模式学习等其他模式的发展……

在下面的图中,我们总结了基于注意力的模型与其他深度神经网络架构的特性。

img

图16:注意力与其他循环网络架构的对比。变压器几乎具有神经网络的所有优良特性。卷积神经网络类似于transformer,但需要很多层才能实现远程依赖。

尽管它们提供了许多优势,但就像生活中的其他事情一样,注意力机制也面临着相当大的挑战。例如,在几种类型的注意力中,内存消耗和计算成本都可以随序列长度呈二次增长。已经提出了各种策略,例如稀疏注意力或局部注意力,以缓解这些问题,但其中大多数在实践中很少使用(Tay等人,2020)。

虽然Transformers在训练过程中提供了并行化的优势,但推理过程的本质可能仍然需要顺序方法,这取决于特定的任务。由于它们的自回归性质,Transformers每次产生一个token输出,继续这个迭代过程,直到完全产生所需的输出序列。

此外,虽然注意力提供了一定程度的可解释性,但它远非完美。尽管它提供了对模型功能的一些见解,但完全基于注意力图来完全破译复杂的模型,至少可以说,是一项艰巨的任务,如果不是几乎不可能的话。

大型语言转换模型

大语言模型的演变

大型语言模型(大语言模型)已经彻底改变了人类与机器学习系统的互动。自然语言接口,如ChatGPTBard,由健壮的大语言模型提供支持。这些模型为实时执行自然语言下游任务或通过zero-shot学习铺平了道路。在过去,这样的任务需要收集下游或特定任务的数据集。

在这些大语言模型的核心,它基本上是一个transformer模型,我们看到这里和那里有一点调整。在本节中,我们将深入研究大型语言模型的压缩演化。此外,我们将探索垂直大语言模型的发展,专门为特定应用设计和微调。

变压器基本模型有65M个参数,但从那以后,语言模型变得越来越大(以数十亿计),因此被称为大型语言模型。下面是流行的大型语言模型的快速概述。

img

图17:流行的大语言模型概述。对于编码器-解码器模型,层是堆叠的编码器/解码器或两者的数量,宽度是模型的维度,头是多头注意中注意层的数量,参数是参数的数量。注意,GPT-2测试中出现正面的次数并不确切。

大多数大型语言模型(大语言模型)的训练过程遵循大致相似的模式。在最初的预训练阶段,大语言模型会接触到大量经过整理的文本数据,这些数据来自书籍、文章、代码片段和网站等各种各样的材料。这个庞大的数据集对于模型获得对世界的全面理解至关重要,使它们能够创建丰富的表示并生成与上下文相关的响应。公众对大语言模型在各个领域的表现抱有很高的期望。为了满足这些期望,预训练数据必须包含广泛的主题和学科([J]。Yang et al. 2023

大语言模型的实际训练以无监督的方式进行,特别关注自我监督学习(SSL)。这种方法消除了对标记数据的需求,考虑到几乎不可能对整个在线内容进行标记,这是一个至关重要的特性。

img

图18:大型语言模型的典型训练工作流。大语言模型通常在大型未标记数据集上进行训练。之后,它们可以通过prompt工程直接使用,也可以在专门任务中进一步微调。

然而,基于未标记数据的训练模型需要巧妙地实现训练目标,因为没有可参考的基础真理。因此,大多数大语言模型使用下一个令牌预测(NTP)作为常见的训练目标。本质上,大语言模型被教导准确地预测序列中的下一个token,逐渐增强他们的理解和生成能力。另一个常用的训练目标是掩码语言建模(MLM)。蒙面语言模型被训练来预测序列中的蒙面token。BERT推广了这一目标(Devlin et al. 2019)。

在预训练阶段之后,模型可以通过zero-shot学习或少射击学习等技术来生成文本。在zero-shot学习中,提示模型执行任务(或回答给定的问题),而不需要演示如何完成任务。在几次学习中,在要求模型执行任务之前,会给模型一些如何完成任务的演示。zero-shot学习和Few-shot学习是情境学习是指大语言模型使用语义先验知识生成连贯文本的能力(Jerry Wei等人,2023),而不需要任何参数更新(akyrek等人,2023)。提示大型语言模型(也称为prompt工程)本身是一个相对较新的领域,还有其他prompt工程技术,如思维链(CoT)(Jason Wei, Nye, and Liang 2022)。

情境学习倾向于擅长那些被认为简单的任务,但不擅长那些无法在prompts中轻易描述的任务。复杂的任务需要的不仅仅是巧妙的prompts。用Karpathy的话来说,“达到顶级性能(在复杂任务上)将包括微调,特别是在具有明确定义的具体任务的应用程序中,可以收集大量数据并在其上“实践”。7。因此,为了让大语言模型在数学、医学、科学领域(如化学)等专业任务上获得良好的表现,人们通常会在下游数据集上对大语言模型进行微调。我们将在垂直大语言模型一节中看到这方面的例子。

既然我们已经简要介绍了大型语言模型(Large Language Models, llm),现在是时候研究一些最流行的大语言模型了,特别关注它们的设计选择:它们是作为编码器、解码器,还是采用编码器-解码器的组合架构。

Encoder, Decoder, Encoder-decoder大语言模型

标准的transformer模型有编码器-解码器,这与它要执行的任务有关,这是机器翻译,你必须处理输入句子和目标翻译。自transformer以来,AI研究界针对不同的任务提出了不同的架构变体。根据不同的任务,一些transformer模型保持编码器-解码器结构,一些只使用解码器或只使用encoder。让我们从后者开始。

仅仅Encoder的LLMs

只有编码器的大语言模型使用标准transformer模型的encoder部分。只有编码器的大语言模型通常用于NLP判别任务,如文本分类和情感分析。

BERT(Devlin et al. 2019)是最流行的纯编码语言模型之一。BERT是最早的工作之一,它表明您可以在大型未标记文本数据集上预训练transformer(encoder),并通过附加的任务特定头部在各种下游任务上微调相同的架构。BERT的预训练目标是掩模语言建模(MLM)和下一句预测(NSP)8。通过屏蔽语言建模,我们屏蔽了给定百分比(论文中提到的15%)的输入tokens,目标是预测被屏蔽的tokens。在下一个句子预测中,对于组成输入序列的两个句子对,目标是随机预测两个句子的顺序是否正确。

img

图19:BERT中的掩码语言建模(MLM)。在图中所示的句子示例中,训练BERT的目标是预测被屏蔽词“network”。在下一个句子预测目标中,工作流程大致相同,但我们不是预测被屏蔽的tokens,而是预测由SEPtoken分隔的两个句子对是否顺序正确。

BERT是一项真正革命性的技术,它在无所不在的NLP下游任务上改进了SOTA。它也启发了其他用于NLP预训练的高效双向架构,如RoBERTa(Y。Liu et al. 2019](https://deeprevision.github.io/posts/001-transformer/#ref-liu2019roberta))代表鲁棒优化的BERT方法。RoBERTa介绍的主要设计选择之一是不使用下一个句子预测目标。

仅仅Decoder的LLMs

纯解码器大语言模型基于标准transformer的解码器部分。在transformer架构中,解码器与encoder非常相似,只是解码器中的自关注被屏蔽,以防止模型在生成当前token时查看后续tokens。

解码器大语言模型使用下一个token预测目标进行训练9。因此,它们一次只能生成一个token或自回归。总的来说,解码器模型用于生成任务。

最流行的解码器模型是GPT(生成预训练变压器)模型家族,最著名的是GPT-3(Brown等人。2020)和GPT-4(OpenAI 2023)。GPT-3和GPT-4是早期GPT模型的直接放大(Radford et al. 2018)。与任何其他大型语言模型一样,GPT模型是在大量未标记数据(数十亿到数万亿个tokens)上进行训练的。由于大规模的预训练和合适的训练目标,GPT模型开发了令人印象深刻的上下文学习能力,它们可以执行一系列NLP下游任务,而无需梯度更新或特定于任务的微调(Brown等人。2020)。事实上,GPT模型只需在zero-shot或Few-shot设置中提示模型,就可以执行文本分类、摘要、问题回答等任务10。这种卓越的情境学习能力通常被称为大型语言模型的“涌现能力”(Jason Wei等人,2022)。

GPT模型并不是唯一基于解码器的模型。事实上,大多数著名的大语言模型都是解码器。例如PaLM(Chowdhery等人。2022)、BLOOM(Le Scao等人。2022)、Chinchilla(Hoffmann等人。2022)、Llama(Touvron等人。2023)等等。

Encoder-Decoder LLMs

编码器-解码器大语言模型看起来像标准transformer。它们通常用于需要处理两个序列的任务中。(输入和目标都是序列)如机器翻译。与我们所见过的其他模型风格相比,编码器-解码器风格并没有被广泛使用。这类最著名的模型是T5(rafael et al. 2019)、BART(Lewis et al. 2019)、UL2(Tay et al. 2022)、FlanT5(Chung et al. 2022)、mT5(Xue et . 2021)等…

编码器-解码器风格也用于多模态学习,最著名的是视觉语言预训练(VLP)。工作原理类似SimVLM([Z]。Wang等。2021](https://deeprevision.github.io/posts/001-transformer/#ref-wang2021simvlm))和pal -X([X。Chen等人2023)使用encoder来学习图像和文本的联合表示,使用解码器来生成输出。

Vertical(垂直) LLMs

我们上面概述的大多数大语言模型通常被称为基础或前沿大语言模型。基础模型通常在具有自我监督的大量数据上进行训练,并且可以对其进行微调,以适应广泛的下游任务(Bommasani et al. 2022)。

垂直大语言模型是一类适用于特定应用的大语言模型。基础大语言模型可以推广到情感分析等简单任务,但它们在复杂任务或需要领域专业知识的任务上表现不佳。例如,基础LLM不太可能在医学问答任务中表现良好,因为它没有医学专业知识。更多的例子:基础LLM不太可能在法律问题回答任务中表现良好,因为它没有法律专业知识。在金融、物理、化学等其他领域也是如此……垂直大语言模型就是为了解决这个问题而设计的。他们在一个特定的领域受过训练,他们可以很好地完成需要该领域专业知识的任务。基础模型的目标是成为通才,但大多数时候,我们关心的是能把一件事做得很好的模型。

最近的垂直大语言模型的例子包括MedPaLM(Singhal et al. 2022)和Med-PaLM 2, ClinicalGPT(G。Wang等。2023), FinGPT([H.]Yang, Liu, and Wang 2023), bloomberg pt (Wu et al. 2023), Galactica(Taylor et al. 2022), Minerva(Lewkowycz et al. 2022)等。

img

图20:大语言模型拓扑。改编自[J]Yang et al. 2023

超越NLP的Transformers:视觉和其他模式

Transformer被引入到自然语言处理(NLP)领域,更准确地说,是用于神经机器翻译。在大多数NLP任务中,Transformers的表现很快就超过了先前的神经网络,并迅速扩展到其他模式。在本节中,我们将简要讨论Transformers在视觉识别和其他模式中的出现。

视觉识别是最早受到Transformers显著影响的模式之一。在很长一段时间里,卷积神经网络是视觉识别领域的最新技术。因此,问为什么研究人员关心卷积神经网络的替代品是一个关键问题。卷积神经网络的主要缺点是其空间归纳偏差11

transformer在图像处理中的最早应用之一是image transformer (Parmar et al. 2018),它将图像生成作为一个自回归问题,类似于文本生成。Image Transformer是一个应用于一系列像素的标准transformer,经过训练可以自回归地生成这些像素,直到创建完整的图像。这是一个很棒的想法,但事实证明,图像通常具有很大的分辨率,因此,对256x256的图像应用自关注是不可行的。有几部作品试图将transformer应用于图像域,但第一批成功的作品之一是视觉变压器(Dosovitskiy等人,2021),它将transformerencoder应用于一系列图像补丁。利用图像拼接思想克服了自关注的计算复杂性,标志着变换向计算机视觉领域的扩展迈出了重要的一步。

正如我们之前看到的,NLP中Transformers成功的巨大贡献是对大量未标记数据的无监督预训练。Vision transformer的成功也归功于数以百万计的训练图像,JFT-300M(C。Sun et al. 2017),尽管后来的MAE(He et al. 2021)和(Steiner et al. 2021)在经典的计算机视觉基准(如ImageNet)上取得了相当不错的性能。MAE是一种编码器-解码器自监督模型,它遵循BERT预训练的目标,预测随机掩码补丁,而BERT则探索巧妙的增强和正则化来训练ViT。ViT已被用作许多有影响力的论文的主干,如CLIP(Radford et al. 2021), DALLE•2(Ramesh et al. 2022),Stable Diffusion(Rombach et al. 2022),以及其他最近在视觉语言模型方面的研究。除了ViT支持视觉和语言的联合建模之外,它还被卷积神经网络增强,以在计算机视觉下游任务中获得这两个世界。ConvNets和Vision Transformer拓扑的著名作品有DETR(Carion et al. 2020)、PatchConvNet(Touvron et al. 2021)、MobileViT(Mehta and Rastegari 2022)等。

当涉及到人机交互时,视觉和语言是最重要的两种形式,所以大多数结合Transformers的作品都是在语言、视觉或视觉语言学习中进行的,这并不奇怪。也就是说,Transformers已用于其他模式,如强化学习(L。Chen et al. 2021](https://deeprevision.github.io/posts/001-transformer/#ref-chen2021decision))、机器人技术(Brohan et al. 2022)、机器猫(Bousmalis et al. 2023)和语音识别(Radford et al. 2022)。最后,像Gato(Reed et al. 2022)和ImageBind(Girdhar et al. 2023)这样的作品在几乎所有模式的建模方面都走得更远。

Transformer已经确立了自己作为通用架构的地位,最近跨不同模式的工作证明了这一点,但仍然存在挑战。

Transformer:当前的挑战和未来的方向

高效Transformers

Transformer在语言、视觉、机器人和强化学习等各种模式上都表现出了显著的性能。Transformer神经网络体系结构具有一系列特征,使其成为适合这些领域的体系结构:它具有表现力,与当前的优化技术配合得很好,并且可以并行化。从这些特点来看,我们可以说transformer是一种高效的架构。然而,由于transformer自注意的二次时间和内存复杂度,其效率带来了巨大的计算成本。transformer的计算需求限制了其可扩展性及其在智能手机和微控制器等低预算设备中的应用。

在开发和部署机器学习系统时,模型效率是一个重要的考虑因素,因为模型在推理过程中的表现会影响用户体验(Dehghani et al. 2022)。已经有无数的transformer模型声称可以提高transformer架构的效率(内存占用和计算成本)(这些模型通常被称为“xformers”),但这些模型通常倾向于针对一个特定的基准或设备。大多数声称可以降低自关注的二次时间和内存复杂度的新型*** **模型都比普通transformer慢得多,在实践中很少使用,也不具备原始transformer的通用性(Tay et al. 2020)。

正如(Tay et al. 2020)在“高效Transformers”的调查中很好地指出的那样,理想的变形金刚应该减少自我关注的二次时间复杂度,但应该保持普遍性,并在所有任务和模式中表现良好。它也不应该为了内存而牺牲速度,不应该是硬设计的,应该保持优雅和简单。*更多信息,我建议您阅读[高效Transformers]的调查报告(https://arxiv.org/abs/2009.06732)。

img 图21:高效Transformers的分类。图片来自Tay et al. 2020)。

几乎所有改进的transformer模型都计算了注意力的近似值,以降低成本。与这些方法相反,实际上有一种注意力可以精确地计算出标准注意力值,但速度更快。这种方法就是FlashAttention(Dao et al. 2022),我们将在高层次上讨论它。

FlashAttention是快速和内存效率的算法,计算准确的注意力。FlashAttention比标准attention快2-4倍。它通过使用两种主要技术实现了计算效率的巨大提高:平铺和重新计算。平铺发生在向前传递中,涉及将注意力(K键和V值)的大矩阵分成块。FlashAttention不是在整个矩阵上计算注意力,而是在块上计算注意力,并将结果块连接起来,从而节省了大量内存。重新计算发生在向后传递,它基本上意味着重新计算注意力矩阵,而不是向前存储它。FlashAttention的想法归结为提高内存,而不是减少计算,因为现代gpu具有高理论FLOPs(Floaping Point Operations,意味着你想要最大化),但有限的内存12(means在内存中的任何节省都可以提高训练速度)。HBM(高带宽内存)通常很大,但它并不比片上SRAM(静态随机存取内存)快,因此,对块(K和V)的计算发生在SRAM中(因为它更快),但所有完整矩阵都存储在HBM中(因为它很大)。这种高层次的解释可能过于简单化,因为FlashAttention是在GPU层面实现的(使用CUDA软件),这实际上是IO感知的原因,但希望这能解释这个快速算法中发生的事情。

下图显示了GPU, FlashAttention算法的内存层次结构,以及GPT-2注意与FlashAttention的每个中间步骤所花费的时间(毫秒)。理想情况下,我们希望大量的计算由矩阵乘法(matmul)操作来完成,但令人惊讶的是,dropout、softmax和mask(i)。(例如,GPT-2是解码器模型)最终会占用GPT-2的整个运行时间,因为它们是在完整矩阵上计算的。Matmuls比那些其他操作花费更少的运行时间,因为gpu的设计正是为了快速处理矩阵乘法(它们具有非常高的理论FLOPs,最大化FLOPs的使用并不会减少运行时间)。通过使用平铺和重新计算技术,FlashAttention的计算时间明显低于标准注意,如下图所示。

img

图22:GPT-2注意与FlashAttention的内存层次、FlashAttention算法和运行时。

FlashAttention集成在PyTorch 2.0,Hugging FaceTransformers,微软的DeepSpeed, MosaicML作曲家库和许多其他库中。您可以在论文中了解更多FlashAttention,或观看核心作者的此视频发布博客。在撰写本节时,FlashAttention2(Dao 2023)也发布了,它甚至比FlashAttention版本1要快几个数量级。FlashAttention2通过并行化序列长度维度而不是批大小和注意头的数量来提高并行性,并拆分Q(查询)矩阵而不是K和v。这篇博文(https://crfm.stanford.edu/2023/07/17/flash2.html)很好地解释了FlashAttention2给张量表带来了什么。

具有有效长上下文的Transformers

处理长上下文长度是Transformer大型模型研究的主要活跃领域之一。由于注意力的二次时间和记忆复杂性的直接后果,transformer不能处理长上下文窗口。研究扩展transformer架构上下文窗口的技术是一件很重要的事情,因为上下文窗口决定了在推理期间transformer内存中可以容纳的信息量。像长对话、总结长文档和执行长期计划这样的任务可能需要支持长上下文窗口的模型。Chen et al. 2023

已经有很多关于上下文窗口和扩展它们的文章,例如S.Sun etal.2021,但我想强调最近的一篇论文,该论文围绕长上下文提出了显著的发现。最近的语言模型(基于transformer)可以接受较长的上下文,但不清楚长上下文是否真的有帮助。如图[N]所示。F. Liu et al. 2023,语言模型的性能随着输入上下文长度的增加而下降。因此,即使对于具有扩展上下文长度的模型,它们的性能对于更长的输入上下文仍然会下降。此外,该工作还发现,当相关信息被放置在输入上下文的开头或结尾时,语言模型表现良好,而当相关信息被放置在中间时,语言模型显著下降,这表明语言模型是u形推理器。

上面强调的研究结果很有吸引力,并提供了广泛的含义,可以适用于微调数据集的设计和上下文学习,但重要的是要注意,如果“transformer模型如何在长上下文窗口上执行”是一个活跃的研究领域,那么这些都不是既定的理解。我们希望未来的transformer模型能够在长输入序列上运行,同时无论放置的相关信息如何,都能表现良好。这实际上是大型语言模型的圣杯。

img 图23:当相关信息位于输入上下文的开头或结尾时,语言模型(基于transformer)往往表现良好(左图),而对于较长的上下文(右图),它们的性能会下降。图表取自[N]。刘峰等。2023

Multimodal Transformer

神经网络设计的一个主要目标是构建一个单一的通用模型,该模型可以有效地处理多个模态,而无需特定于模态的编码器或预处理。实际上,transformer模型已经被广泛应用于各种领域,包括文本、图像、机器人和语音。然而,创造一种真正通用的transformer的目标仍然是一个挑战,这种变压器在所有模式下都能同样有效地工作,而不需要进行特定的调整。这一挑战来自数据类型和transformer模型本身的固有差异和复杂性,它们经常需要特定于模式的修改。

例如,处理文本、图像和语音的过程由于其各自的特征而具有独特的考虑。Transformers在数据可以被框架为一系列tokens的场景中表现出色,然而,将特定模态转置到这样一个序列的方法在不同的模态之间显着不同。因此,挑战在于设计一种能够以相当的效率统一地从所有数据类型中提取有价值的见解的单一体系结构。

这种架构的实现将标志着多模态学习领域的巨大进步,为可以在不同类型的数据之间无缝转换的模型铺平了道路,并有可能开启多模态表示学习的新探索途径。

目前几乎所有的多模态学习技术都是为每个模态使用单独的标记器和encoder,其中大多数也是为视觉语言学习而设计的。本节不会深入讨论当前基于Transformers的多模式方法的细节,但我们为有兴趣深入研究的人提供了示例:Flamingo(视觉语言)(Alayrac等人,2022), Gato(Reed等人,2022), ImageBind(Girdhar等人,2023), OFA(P。Wang et al. 2022), Unified-IO(Lu et al. 2022), Meta-Transformer([Y。Zhang et al. 2023)等。

请注意

几乎所有的transformer挑战都源于其极端的计算和内存要求。像FlashAttention这样真正高效的Transformers可能会缓解这些挑战。

Transformer的开源实现

最初的transformer模型是在Tensor2Tensor库中实现的13,但最近已经弃用了。Tensor2Tensor的继承者是基于JAX的Trax 14

有许多transformer模型体系结构的开源实现。让我们简要讨论一下最流行的三种实现。HuggingFace Transformer库(Wolf etal . 2020)可以说是最流行的Transformers实现之一。该库简化了NLP(和视觉)下游任务的推理管道,并可用于训练或微调基于变压器的模型。HuggingFace Transformer库易于使用,它很干净,并且有一个庞大的开发人员和贡献者社区。Andrej Karpathy的minGPTnanoGPT也是开源和研究社区中流行的实现。此外,x-transformers提供了各种transformer模型的简明和实验实现,通常来自新的研究论文。

最后,你不太可能需要从头开始实现transformer模型或它的一部分,因为现代深度学习框架,如PyTorchKeras和JAX(Via flex)提供了可以轻松导入的层的实现,就像导入卷积或线性层一样。

补充的资源

本文对现有的围绕transformer神经网络体系结构理解的知识库做出了贡献。因此,不强调一些关于transformer体系结构的宝贵资源是疏忽的,我们将在下面简要地提供:

  • 注释的Transformer:这是最好的和实用的资源之一。它提供了transformer体系结构的逐行实现和完全可用的代码。原始版本由Sasha Rush撰写,最新版本由Austin Huang和他的同事撰写。

  • Andrej Karpathy的《让我们从头开始构建GPT》:这可以说是关于transformer实现的最佳资源,最值得注意的是GPT(生成预训练transformer)。Karpathy从零开始构建和训练整个GPT,并在此过程中为每一步提供了一个像样的解释。这里有一个讲座视频附带的代码库(nanoGPT)

  • 斯坦福CS25:Transformers联合V2旨在研究Transformers是如何工作的,以及它们如何应用于从NLP, CV,生物学到机器人等不同领域。本课程包含来自研究人员的精彩演讲。课程最新版本的入门课程深入研究了transformer架构,由Karpathy教授,他对神经网络的复杂性有着深刻的理解。

  • Transformers的形式化算法提供了各种transformer架构的数学概述和形式化算法。

  • Transformer分类法提供了transformer模型的优秀文献综述,自标准transformer开始以来的架构变化,后预培训技术和3种培训技术。

  • The Illustrated Transformer是一篇出色的博客文章,它将transformer模型分解并直观地解释了每个部分。

  • Lilian Weng的Transformer和注意力系列博客也提供了对transformer和注意力机制的深刻理解。一个值得注意的例子是Transformer家族版(there也是这个博客的第二版)和[注意?(https://lilianweng.github.io/posts/2018-06-24-attention/)。

  • 关注是你所需要的视频由Yannic Kilcher讲解论文,解释所有相关概念和相关作品。

  • Transformer模型:介绍和目录也是另一个值得一提的资源。它提供了一个不错的流行transformer型号目录。

结论

Transformer神经网络体系结构在深度学习和计算机科学领域的重要性不容忽视。最初为神经机器翻译引入的transformer模型已经发展成为一种通用的通用架构,展示了超越自然语言处理的令人印象深刻的性能。

在本文中,我们深入研究了transformer的核心机制及其基本组件——它的encoder和解码器结构、注意机制、多头注意、mlp、Embedding、位置编码层等等。我们已经探讨了自我关注的几个好处,以及潜在的缺点。此外,通过研究注意力的可视化,我们对Transformers如何聚焦于输入序列的不同部分以产生输出有了更深入的了解。

Transformers是最近风靡全球的大型语言模型(大语言模型)的核心。我们已经看到了大语言模型及其不同设计风格的演变,以及Transformers在NLP之外的应用。我们还讨论了他们当前面临的挑战,包括对更高效模型的需求和对上下文窗口的有效使用。这些挑战为未来的研究和改进提供了令人兴奋的机会。

随着深度学习领域的不断发展,transformer架构仍然是现代机器学习系统的基础构建块。transformer架构有许多变化,但无论Transformers的未来如何,有一件事是肯定的-注意力是你所需要的。保持好奇,不断学习,时刻关注!

参考文献

Akyürek, Ekin, Dale Schuurmans, Jacob Andreas, Tengyu Ma, and Denny Zhou. 2023. “What Learning Algorithm Is in-Context Learning? Investigations with Linear Models.” arXiv Preprint arXiv:2211.15661.

Alayrac, Jean-Baptiste, Jeff Donahue, Pauline Luc, Antoine Miech, Iain Barr, Yana Hasson, Karel Lenc, et al. 2022. “Flamingo: A Visual Language Model for Few-Shot Learning.” arXiv Preprint arXiv:2204.14198.

Ba, Jimmy Lei, Jamie Ryan Kiros, and Geoffrey E Hinton. 2016. “Layer Normalization.” arXiv Preprint arXiv:1607.06450.

Bahdanau, Dzmitry, Kyunghyun Cho, and Yoshua Bengio. 2014. “Neural Machine Translation by Jointly Learning to Align and Translate.” arXiv Preprint arXiv:1409.0473.

Bommasani, Rishi, Drew A Hudson, Ehsan Adeli, Russ Altman, Simran Arora, Sydney von Arx, Michael S Bernstein, et al. 2022. “On the Opportunities and Risks of Foundation Models.” arXiv Preprint arXiv:2108.07258.

Bousmalis, Konstantinos, Giulia Vezzani, Dushyant Rao, Coline Devin, Alex X Lee, Maria Bauza, Todor Davchev, et al. 2023. “RoboCat: A Self-Improving Foundation Agent for Robotic Manipulation.” arXiv Preprint arXiv:2306.11706.

Brohan, Anthony, Noah Brown, Justice Carbajal, Yevgen Chebotar, Joseph Dabis, Chelsea Finn, Keerthana Gopalakrishnan, et al. 2022. “RT-1: Robotics Transformer for Real-World Control at Scale.” arXiv Preprint arXiv:2212.06817.

Brown, Tom B, Benjamin Mann, Nick Ryder, Melanie Subbiah, Jared Kaplan, Prafulla Dhariwal, Arvind Neelakantan, et al. 2020. “Language Models Are Few-Shot Learners.” arXiv Preprint arXiv:2005.14165.

Carion, Nicolas, Francisco Massa, Gabriel Synnaeve, Nicolas Usunier, Alexander Kirillov, and Sergey Zagoruyko. 2020. “End-to-End Object Detection with Transformers.” arXiv Preprint arXiv:2005.12872.

Chen, Lili, Kevin Lu, Aravind Rajeswaran, Kimin Lee, Aditya Grover, Michael Laskin, Pieter Abbeel, Aravind Srinivas, and Igor Mordatch. 2021. “Decision Transformer: Reinforcement Learning via Sequence Modeling.” arXiv Preprint arXiv:2106.01345.

Chen, Shouyuan, Sherman Wong, Liangjian Chen, and Yuandong Tian. 2023. “Extending Context Window of Large Language Models via Positional Interpolation.” arXiv Preprint arXiv:2306.15595.

Chen, Xi, Josip Djolonga, Piotr Padlewski, Basil Mustafa, Soravit Changpinyo, Jialin Wu, Carlos Riquelme Ruiz, et al. 2023. “PaLI-x: On Scaling up a Multilingual Vision and Language Model.” arXiv Preprint arXiv:2305.18565.

Chollet, François. 2017. “Xception: Deep Learning with Depthwise Separable Convolutions.” arXiv Preprint arXiv:1610.02357.

Chowdhery, Aakanksha, Sharan Narang, Jacob Devlin, Bosma Maarten, Mishra Gaurav, Roberts Adam, Barham Paul, et al. 2022. “PaLM: Scaling Language Modeling with Pathways.” arXiv Preprint arXiv:2204.02311.

Chung, Hyung Won, Le Hou, Shayne Longpre, Barret Zoph, Yi Tay, William Fedus, Yunxuan Li, et al. 2022. “Scaling Instruction-Finetuned Language Models.” arXiv Preprint arXiv:2210.11416.

Dao, Tri. 2023. “FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning.” arXiv Preprint arXiv:2307.08691.

Dao, Tri, Daniel Y. Fu, Stefano Ermon, Atri Rudra, and Christopher Ré. 2022. “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness.” arXiv Preprint arXiv:2205.14135.

Dehghani, Mostafa, Anurag Arnab, Lucas Beyer, Ashish Vaswani, and Yi Tay. 2022. “The Efficiency Misnomer.” arXiv Preprint arXiv:2110.12894.

Devlin, Jacob, Ming-Wei Chang, Kenton Lee, and Kristina Toutanova. 2019. “BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding.” In Proceedings of the 2019 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies, Volume 1 (Long and Short Papers), 4171–86.

Dosovitskiy, Alexey, Lucas Beyer, Alexander Kolesnikov, Dirk Weissenborn, Xiaohua Zhai, Thomas Unterthiner, Mostafa Dehghani, et al. 2021. “An Image Is Worth 16x16 Words: Transformers for Image Recognition at Scale.” In International Conference on Learning Representations.

Girdhar, Rohit, Alaaeldin El-Nouby, Zhuang Liu, Mannat Singh, Kalyan Vasudev Alwala, Armand Joulin, and Ishan Misra. 2023. “ImageBind: One Embedding Space to Bind Them All.” arXiv Preprint arXiv:2305.05665.

Graves, Alex, Greg Wayne, and Ivo Danihelka. 2014. “Neural Turing Machines.” arXiv Preprint arXiv:1410.5401.

He, Kaiming, Xinlei Chen, Saining Xie, Yanghao Li, Piotr Dollár, and Ross Girshick. 2021. “Masked Autoencoders Are Scalable Vision Learners.” arXiv Preprint arXiv:2111.06377.

He, Kaiming, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. 2016. “Deep Residual Learning for Image Recognition.” In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, 770–78.

Hoffmann, Jordan, Sebastian Borgeaud, Arthur Mensch, Elena Buchatskaya, Trevor Cai, Eliza Rutherford, Diego de Las Casas, et al. 2022. “Training Compute-Optimal Large Language Models.” arXiv Preprint arXiv:2203.15556.

Ioffe, Sergey, and Christian Szegedy. 2015. “Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift.” In International Conference on Machine Learning, 448–56.

Le Scao, Teven, Angela Fan, Christopher Akiki, Pavlick Ellie, Ilić Suzana, Hesslow Daniel, Castagné Roman, et al. 2022. “BLOOM: A 176B-Parameter Open-Access Multilingual Language Model.” arXiv Preprint arXiv:2211.05100.

Lewis, Mike, Yinhan Liu, Naman Goyal, Marjan Ghazvininejad, Abdelrahman Mohamed, Omer Levy, Veselin Stoyanov, and Luke Zettlemoyer. 2019. “BART: Denoising Sequence-to-Sequence Pre-Training for Natural Language Generation, Translation, and Comprehension.” arXiv Preprint arXiv:1910.13461.

Lewkowycz, Aitor, Anders Andreassen, David Dohan, Ethan Dyer, Henryk Michalewski, Vinay Ramasesh, Ambrose Slone, et al. 2022. “Solving Quantitative Reasoning Problems with Language Models.” arXiv Preprint arXiv:2206.14858.

Liu, Nelson F., Kevin Lin, John Hewitt, Ashwin Paranjape, Michele Bevilacqua, Fabio Petroni, and Percy Liang. 2023. “Lost in the Middle: How Language Models Use Long Contexts.” arXiv Preprint arXiv:2307.03172.

Liu, Yinhan, Myle Ott, Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike Lewis, Luke Zettlemoyer, and Veselin Stoyanov. 2019. “RoBERTa: A Robustly Optimized BERT Pretraining Approach.” arXiv Preprint arXiv:1907.11692.

Lu, Jiasen, Christopher Clark, Rowan Zellers, Roozbeh Mottaghi, and Aniruddha Kembhavi. 2022. “Unified-IO: A Unified Model for Vision, Language, and Multi-Modal Tasks.” arXiv Preprint arXiv:2206.08916.

Luong, Minh-Thang, Hieu Pham, and Christopher D Manning. 2015. “Effective Approaches to Attention-Based Neural Machine Translation.” arXiv Preprint arXiv:1508.04025.

Mehta, Sachin, and Mohammad Rastegari. 2022. “MobileViT: Light-Weight, General-Purpose, and Mobile-Friendly Vision Transformer.” arXiv Preprint arXiv:2110.02178.

OpenAI. 2023. “GPT-4 Technical Report.” arXiv Preprint arXiv:2303.08774.

Parmar, Niki, Ashish Vaswani, Jakob Uszkoreit, Łukasz Kaiser, Noam Shazeer, Alexander Ku, and Dustin Tran. 2018. “Image Transformer.” In Proceedings of the 35th International Conference on Machine Learning, 4055–64.

Radford, Alec, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal, Girish Sastry, et al. 2021. “Learning Transferable Visual Models from Natural Language Supervision.” In International Conference on Machine Learning, 8748–63.

Radford, Alec, Jong Wook Kim, Tao Xu, Greg Brockman, Christine McLeavey, and Ilya Sutskever. 2022. “Robust Speech Recognition via Large-Scale Weak Supervision.” arXiv Preprint arXiv:2212.04356.

Radford, Alec, Karthik Narasimhan, Tim Salimans, and Ilya Sutskever. 2018. “Improving Language Understanding by Generative Pre-Training.”

Radford, Alec, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei, and Ilya Sutskever. 2019. “Language Models Are Unsupervised Multitask Learners.” OpenAI Blog 1 (8).

Raffel, Colin, Noam Shazeer, Adam Roberts, Katherine Lee, Sharan Narang, Michael Matena, Yanqi Zhou, Wei Li, and Peter J Liu. 2019. “Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer.” arXiv Preprint arXiv:1910.10683.

Ramesh, Aditya, Prafulla Dhariwal, Alex Nichol, Casey Chu, and Mark Chen. 2022. “Hierarchical Text-Conditional Image Generation with CLIP Latents.” arXiv Preprint arXiv:2204.06125.

Reed, Scott, Konrad Zolna, Emilio Parisotto, Sergio Gomez Colmenarejo, Alexander Novikov, Gabriel Barth-Maron, Mai Gimenez, et al. 2022. “A Generalist Agent.” arXiv Preprint arXiv:2205.06175.

Rombach, Robin, Andreas Blattmann, Dominik Lorenz, Patrick Esser, and Björn Ommer. 2022. “High-Resolution Image Synthesis with Latent Diffusion Models.” In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, 10684–95.

Singhal, Karan, Shekoofeh Azizi, Tao Tu, S Sara Mahdavi, Jason Wei, Hyung Won Chung, Nathan Scales, et al. 2022. “Large Language Models Encode Clinical Knowledge.” arXiv Preprint arXiv:2212.13138.

Srivastava, Nitish, Geoffrey Hinton, Alex Krizhevsky, Ilya Sutskever, and Ruslan Salakhutdinov. 2014. “Dropout: A Simple Way to Prevent Neural Networks from Overfitting.” Journal of Machine Learning Research 15 (56): 1929–58.

Steiner, Andreas, Alexander Kolesnikov, Xiaohua Zhai, Ross Wightman, Jakob Uszkoreit, and Lucas Beyer. 2021. “How to Train Your ViT? Data, Augmentation, and Regularization in Vision Transformers.” arXiv Preprint arXiv:2106.10270.

Sun, Chen, Abhinav Shrivastava, Saurabh Singh, and Abhinav Gupta. 2017. “Revisiting Unreasonable Effectiveness of Data in Deep Learning Era.” In Proceedings of the IEEE International Conference on Computer Vision, 843–52.

Sun, Simeng, Kalpesh Krishna, Andrew Mattarella-Micke, and Mohit Iyyer. 2021. “Do Long-Range Language Models Actually Use Long-Range Context?” In Proceedings of the 2021 Conference on Empirical Methods in Natural Language Processing (EMNLP), 807–22. Online; Punta Cana, Dominican Republic: Association for Computational Linguistics. https://doi.org/10.18653/v1/2021.emnlp-main.62.

Tay, Yi, Mostafa Dehghani, Dara Bahri, and Donald Metzler. 2020. “Efficient Transformers: A Survey.” arXiv Preprint arXiv:2009.06732.

Tay, Yi, Mostafa Dehghani, Vinh Q Tran, Xavier Garcia, Jason Wei, Xuezhi Wang, Hyung Won Chung, et al. 2022. “UL2: Unifying Language Learning Paradigms.” arXiv Preprint arXiv:2205.05131.

Taylor, Ross, Marcin Kardas, Guillem Cucurull, Thomas Scialom, Anthony Hartshorn, Elvis Saravia, Andrew Poulton, Viktor Kerkez, and Robert Stojnic. 2022. “Galactica: A Large Language Model for Science.” arXiv Preprint arXiv:2211.09085.

Touvron, Hugo, Matthieu Cord, Alaaeldin El-Nouby, Piotr Bojanowski, Armand Joulin, Gabriel Synnaeve, and Hervé Jégou. 2021. “Augmenting Convolutional Networks with Attention-Based Aggregation.” arXiv Preprint arXiv:2112.13692.

Touvron, Hugo, Thibaut Lavril, Gautier Izacard, Xavier Martinet, Marie-Anne Lachaux, Timothée Lacroix, Baptiste Rozière, et al. 2023. “LLaMA: Open and Efficient Foundation Language Models.” arXiv Preprint arXiv:2302.13971.

Vaswani, Ashish, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez, Lukasz Kaiser, and Illia Polosukhin. 2017. “Attention Is All You Need.” arXiv Preprint arXiv:1706.03762.

Vig, Jesse. 2019. “A Multiscale Visualization of Attention in the Transformer Model.” In Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics: System Demonstrations, 37–42.

Wang, Guangyu, Guoxing Yang, Zongxin Du, Longjun Fan, and Xiaohu Li. 2023. “ClinicalGPT: Large Language Models Finetuned with Diverse Medical Data and Comprehensive Evaluation.” arXiv Preprint arXiv:2306.09968.

Wang, Peng, An Yang, Rui Men, Junyang Lin, Shuai Bai, Zhikang Li, Jianxin Ma, Chang Zhou, Jingren Zhou, and Hongxia Yang. 2022. “OFA: Unifying Architectures, Tasks, and Modalities Through a Simple Sequence-to-Sequence Learning Framework.” In Proceedings of the 39th International Conference on Machine Learning, 23318–40. PMLR.

Wang, Zirui, Jiahui Yu, Adams Wei Yu, Zihang Dai, Yulia Tsvetkov, and Yuan Cao. 2021. “SimVLM: Simple Visual Language Model Pretraining with Weak Supervision.” arXiv Preprint arXiv:2108.10904.

Wei, Jason, Max Nye, and Percy Liang. 2022. “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.” arXiv Preprint arXiv:2201.11903.

Wei, Jason, Yi Tay, Rishi Bommasani, Colin Raffel, Barret Zoph, Sebastian Borgeaud, Dani Yogatama, et al. 2022. “Emergent Abilities of Large Language Models.” arXiv Preprint arXiv:2206.07682.

Wei, Jerry, Jason Wei, Yi Tay, Dustin Tran, Albert Webson, Yifeng Lu, Xinyun Chen, et al. 2023. “Larger Language Models Do in-Context Learning Differently.” arXiv Preprint arXiv:2303.03846.

Wolf, Thomas, Lysandre Debut, Victor Sanh, Julien Chaumond, Clement Delangue, Anthony Moi, Pierric Cistac, et al. 2020. “Transformers: State-of-the-Art Natural Language Processing.” In Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing: System Demonstrations, 38–45. Online: Association for Computational Linguistics. https://doi.org/10.18653/v1/2020.emnlp-demos.6.

Wu, Shijie, Ozan Irsoy, Steven Lu, Vadim Dabravolski, Mark Dredze, Sebastian Gehrmann, Prabhanjan Kambadur, David Rosenberg, and Gideon Mann. 2023. “BloombergGPT: A Large Language Model for Finance.” arXiv Preprint arXiv:2303.17564.

Xie, Shufang, Huishuai Zhang, Junliang Guo, Xu Tan, Jiang Bian, Hany Hassan Awadalla, Arul Menezes, Tao Qin, and Rui Yan. 2023. “ResiDual: Transformer with Dual Residual Connections.” arXiv Preprint arXiv:2304.14802.

Xiong, Ruibin, Yunchang Yang, Di He, Kai Zheng, Shuxin Zheng, Chen Xing, Huishuai Zhang, Yanyan Lan, Liwei Wang, and Tie-Yan Liu. 2020. “On Layer Normalization in the Transformer Architecture.” In International Conference on Machine Learning, 10524–33.

Xu, Kelvin, Jimmy Ba, Ryan Kiros, Kyunghyun Cho, Aaron Courville, Ruslan Salakhutdinov, Richard S Zemel, and Yoshua Bengio. 2015. “Show, Attend and Tell: Neural Image Caption Generation with Visual Attention.” In International Conference on Machine Learning, 2048–57.

Xue, Linting, Noah Constant, Adam Roberts, Mihir Kale, Rami Al-Rfou, Aditya Siddhant, Aditya Barua, and Colin Raffel. 2021. “mT5: A Massively Multilingual Pre-Trained Text-to-Text Transformer.” In Proceedings of the 2021 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies, 483–98.

Yang, Hongyang, Xiao-Yang Liu, and Christina Dan Wang. 2023. “FinGPT: Open-Source Financial Large Language Models.” arXiv Preprint arXiv:2306.06031.

Yang, Jingfeng, Hongye Jin, Ruixiang Tang, Xiaotian Han, Qizhang Feng, Haoming Jiang, Bing Yin, and Xia Hu. 2023. “Harnessing the Power of LLMs in Practice: A Survey on ChatGPT and Beyond.” arXiv Preprint arXiv:2304.13712.

Zhang, Xiang, and Yann LeCun. 2015. “Text Understanding from Scratch.” arXiv Preprint arXiv:1502.01710.

Zhang, Yiyuan, Kaixiong Gong, Kaipeng Zhang, Hongsheng Li, Yu Qiao, Wanli Ouyang, and Xiangyu Yue. 2023. “Meta-Transformer: A Unified Framework for Multimodal Learning.” arXiv Preprint arXiv:2307.10802.

Footnotes

  1. Example adapted from Deep Learning with Python by Francois Chollet↩︎
  2. In the transformer paper, MLPs are what referred to as feed-forward networks(FFNs). I find the terminology of FFNs confusing sometime. MLPs are feed-forward networks but not the other way around.↩︎
  3. If you want to see how embeddings look like and how words with same semantic meaning tend to be closer to each other, you can play with Embedding Projector↩︎
  4. The core operation in attention is the dot product between query and keys, which, being a summation operation, is permutation invariant↩︎
  5. Hat tip to Sebastian Raschka for sharing this in his newsletter↩︎
  6. BertViz be accessed at https://github.com/jessevig/bertviz[↩︎](https://deeprevision.github.io/posts/001-transformer/#fnref6)
  7. Karpathy said that in a Twitter thread. Available here: https://twitter.com/karpathy/status/1655994367033884672?s=20[↩︎](https://deeprevision.github.io/posts/001-transformer/#fnref7)
  8. Next sentence prediction in BERT and next token prediction in standard transformer are different. The idea is roughly similar, but the former is usually for discriminative modelling while the later is for auto-regressive generative modelling↩︎
  9. Next token prediction in decoder LLMs is different to next sentence prediction in BERT. The former operates on token level while the later operates on sentence level↩︎
  10. It’s fair to say that GPT-3 popularized prompt engineering.↩︎
  11. The inductive biases in ConvNets are the results of their translation invariance. Convolution itself is translation equivariance(changing the position of pixels changes the output) but pooling which is often used after convolution is translation invariant(changing the position of pixels doesn’t change the output) and this make the overall ConvNets translation invariant architecture↩︎
  12. GPU main memory is called HBM which stands for High Bandwidth Memory↩︎
  13. Available at https://github.com/tensorflow/tensor2tensor[↩︎](https://deeprevision.github.io/posts/001-transformer/#fnref13)
  14. Available at https://github.com/google/trax[↩︎](https://deeprevision.github.io/posts/001-transformer/#fnref14)

原文:The Transformer Blueprint: A Holistic Guide to the Transformer Neural Network Architecture


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



如何使用LLM与任何pdf和图像文件聊天

介绍

如此多有价值的信息被困在PDF和图像文件中。幸运的是,我们拥有强大的大脑,能够处理这些文件以找到特定的信息,这实际上很棒。

但是,我们当中有多少人在内心深处不希望有一个工具可以回答关于给定文档的任何问题?

这就是本文的全部目的。我将逐步解释如何构建一个可以与任何pdf和图像文件聊天的系统。

项目的一般工作流程

对正在构建的系统的主要组件有一个清晰的理解总是好的。让我们开始吧。

截屏2023-09-09 23.47.50

整个聊天系统的端到端工作流程
  • 首先,用户提交待处理的文档,文档可以是PDF格式,也可以是图像格式。
  • 第二个模块用于检测文件的格式,以便应用相关的内容提取功能。
  • 然后使用Data Splitter模块将文档的内容分割成多个块。
  • 这些块最终使用Chunk Transformer转换成Embeddings,然后将它们存储在矢量存储中。
  • 在流程结束时,用户的查询用于查找包含该查询答案的相关块,并将结果作为JSON返回给用户。

1. 检测文档类型

对于每个输入文档,将根据其类型(无论是PDF还是image)应用特定的处理。

这可以通过辅助函数detect_document_type 与内置Python模块中的guess 函数相结合来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def detect_document_type(document_path):

guess_file = guess(document_path)
file_type = ""
image_types = ['jpg', 'jpeg', 'png', 'gif']

if(guess_file.extension.lower() == "pdf"):
file_type = "pdf"

elif(guess_file.extension.lower() in image_types):
file_type = "image"

else:
file_type = "unkown"

return file_type

现在我们可以在两种类型的文档上测试这个函数:

  • transformer_paper.pdf是Transformers研究论文来自Arxiv
  • zoumana_article_information.png是图像文档,包含有关我在Medium上覆盖的主要主题的信息。
1
2
3
4
5
research_paper_path = "./data/transformer_paper.pdf"
article_information_path = "./data/zoumana_article_information.png"

print(f"Research Paper Type: {detect_document_type(research_paper_path)}")
print(f"Article Information Document Type: {detect_document_type(article_information_path)}")

输出:

img

成功检测到的文件类型(图片按作者)

这两种文件类型都被detect_document_type函数成功检测到。

2. 根据文档类型提取内容

langchain库提供了不同的模块来提取给定类型文档的内容。

  • UnstructuredImageLoader提取图像内容。
  • UnstructuredFileLoader提取任何pdf和Txt文件的内容。

我们可以将这些模块与上面的detect_document_type 函数结合起来实现文本提取逻辑。

这些模块可用于在extract_file_content函数中实现端到端的文本提取逻辑。

让我们看看他们的行动吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from langchain.document_loaders.image import UnstructuredImageLoader
from langchain.document_loaders import UnstructuredFileLoader

def extract_file_content(file_path):

file_type = detect_document_type(file_path)

if(file_type == "pdf"):
loader = UnstructuredFileLoader(file_path)

elif(file_type == "image"):
loader = UnstructuredImageLoader(file_path)

documents = loader.load()
documents_content = '\n'.join(doc.page_content for doc in documents)

return documents_content

现在,让我们打印每个文件内容的前400个字符。

1
2
3
4
5
6
7
8
research_paper_content = extract_file_content(research_paper_path)
article_information_content = extract_file_content(article_information_path)

nb_characters = 400

print(f"First {nb_characters} Characters of the Paper: \n{research_paper_content[:nb_characters]} ...")
print("---"*5)
print(f"First {nb_characters} Characters of Article Information Document :\n {research_paper_content[:nb_characters]} ...")

输出:

上述文件的前400个字符如下:

  • 研究论文内容以Provided proper attribution is provided开始,以Jacod Uszkoreit* Google research usz@google.com结束。
  • 图像文档的内容以This document provides a quick summary开始,并以Data Science section covers basic to advance concepts结束。

img

《Transformers》论文和文章信息文档的前400个字符

3. Chat实现

输入文档被分割成块,然后在实现问答逻辑之前为每个块创建Embedding。

a. 文档组块

块表示较大文本的较小部分。这个过程对于确保一段内容以尽可能少的噪音表示,使其具有语义相关性至关重要。

可以应用多种分块策略。

例如,我们有NLTKTextSplitterSpacyTextSplitter,、RecursiveCharacterTextSplitterCharacterTextSplitter等等。

每一种策略都有其优缺点。

本文的主要重点是CharacterTextSplitter,它基于\n\n从输入文档中创建块,并通过其字符数测量每个块的长度(length_function)。

1
2
3
4
5
6
text_splitter = CharacterTextSplitter(        
separator = "\n\n",
chunk_size = 1000,
chunk_overlap = 200,
length_function = len,
)

chunk_size告诉我们希望每个块中最多有1000个字符,较小的值将产生更多的块,而较大的值将产生更少的块。

需要注意的是,选择chunk_size的方式会影响整体结果。因此,一个好的方法是尝试不同的值,并选择更适合自己用例的值。

此外,chunk_overlap意味着我们希望在连续块之间最多有200个重叠字符。

例如,假设我们有一个包含文本Chat with your documents using LLMs的文档,并希望使用Chunk Size = 10Chunk overlap = 5来应用分块。

这个过程如下图所示:

img

文档分块说明

我们可以看到,对于35个字符(包括空格)的输入文档,我们最终得到了总共7个块。

但是,我们为什么要首先使用这些重叠呢?

包括这些重叠, CharacterTextSplitter确保在块之间维护底层上下文,这在处理长段文档时特别有用。

chunk_size类似,chunk_overlap没有固定值。需要测试不同的值,以选择效果更好的值。

现在,让我们看看它们在我们的场景中的应用:

1
2
3
4
5
research_paper_chunks = text_splitter.split_text(research_paper_content)
article_information_chunks = text_splitter.split_text(article_information_content)

print(f"# Chunks in Research Paper: {len(research_paper_chunks)}")
print(f"# Chunks in Article Document: {len(article_information_chunks)}")

输出:

img

每个文档中的块数

对于像研究论文这样的大型文档,我们有更多的块(51),而一页的文章文档只有2个。

b. 创建块的Embeddings

我们可以使用OpenAIEmbeddings模块,它默认使用text-embedding-ada-002 模型来创建块的Embedding。

而不是使用text-embedding-ada-002可以使用不同的模型(例如:gpt-3.5-turbo-0301),通过改变以下参数:

  • model = “ gpt-3.5-turbo-0301
  • deployment = “<DEPLOYMENT-NAME>“,对应于模型部署期间给出的名称。默认值也是text- embeddings -ada-002

为了简单起见,我们将在本教程中坚持使用默认参数的值。但在此之前,我们需要获得OpenAI凭据,下面的文章提供了所有步骤。

1
2
3
4
5
from langchain.embeddings.openai import OpenAIEmbeddings
import os

os.environ["OPENAI_API_KEY"] = "<YOUR_KEY>"
embeddings = OpenAIEmbeddings()

c. 创建文档搜索

为了获得给定查询的答案,我们需要创建一个向量存储,用于查找与该查询最接近的匹配块。

这样的矢量存储可以使用FAISS模块中的from_texts函数来创建,该函数有两个主要参数:text_splitterEmbeddings,这两个参数都是前面定义的。

1
2
3
4
5
from langchain.vectorstores import FAISS

def get_doc_search(text_splitter):

return FAISS.from_texts(text_splitter, embeddings)

通过在研究论文块上运行get_doc_search,我们可以看到结果是vectorstores。如果我们使用article_information_chunks,结果将是相同的。

1
2
doc_search_paper = get_doc_search(research_paper_chunks)
print(doc_search_paper)

输出:

img

研究论文的矢量存储

d. 开始和你的文件聊天

祝贺你走了这么远!

chat_with_file函数用于通过组合上述所有函数以及similarity_search函数来实现聊天的端到端逻辑。

最后一个函数接受两个形参:

  • 我们想要聊天的文件,还有
  • 用户提供的查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from langchain.llms import OpenAI
from langchain.chains.question_answering import load_qa_chain
chain = load_qa_chain(OpenAI(), chain_type = "map_rerank",
return_intermediate_steps=True)

def chat_with_file(file_path, query):

file_content = extract_file_content(file_path)
text_splitter = text_splitter.split_text(file_content)

document_search = get_doc_search(text_splitter)
documents = document_search.similarity_search(query)

results = chain({
"input_documents":documents,
"question": query
},
return_only_outputs=True)
answers = results['intermediate_steps'][0]

return answers

让我们退一步来正确理解上面代码块中发生的事情。

  • load_qa_chain提供了一个接口,用于在一组文档上执行问答。在这个特定的情况下,我们使用默认的OpenAI GPT-3大型语言模型。
  • chain_typemap_rerank。通过这样做,load_qa_chain函数根据链给出的置信度分数返回答案。还有其他的chain_type可以使用,比如map_reducestuffrefine等等。每个人都有自己的优点和缺点。
  • 通过设置return_intermediate_steps=True,我们可以访问元数据,如上述置信度评分。

它的输出是一个包含两个键的字典:查询的答案和信心得分

我们终于可以和我们的文件聊天了,从图像文档开始:

  • 与图像文档聊天

为了与图像文档进行对话,我们提供了文档的路径,以及我们希望模型回答的问题。

1
2
3
4
5
6
7
8
query = "What is the document about"

results = chat_with_file(article_information_path, query)

answer = results["answer"]
confidence_score = results["score"]

print(f"Answer: {answer}\n\nConfidence Score: {confidence_score}")

输出:

img

对图片文档查询的结果

该模型对其响应有100%的信心。通过查看下面原始文档的第一段,我们可以看到模型响应确实是正确的。

img

原文章图片文档前两段

最有趣的部分之一是,它提供了文档中涵盖的主要主题的简要摘要(统计、模型评估指标、SQL查询等)。

  • 与PDF文件聊天

PDF文件的处理过程与上一节中的过程类似。

1
2
3
4
5
6
7
8
query = "Why is the self-attention approach used in this document?"

results = chat_with_file(research_paper_path, query)

answer = results["answer"]
confidence_score = results["score"]

print(f"Answer: {answer}\n\nConfidence Score: {confidence_score}")

输出:

我们再一次从模型中得到了100%的置信度。这个问题的答案看起来非常正确!

img

查询PDF文件的结果

在这两种情况下,该模型都能在几秒钟内做出类似人类的反应。让一个人完成同样的过程需要几分钟,甚至几个小时,这取决于文档的长度。

本文的Github地址

原文:How to Chat With Any PDFs and Image Files Using Large Language Models — With Code


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!



Midjourney中标点符号的使用指南

这篇文章研究的是标点符号在prompt中的作用。

Midjourney的prompt可以包含标点符号。我们经常使用它们来制作prompt,而不需要太多思考。毕竟,这是官方的Midjourney用户指南声明:

使用逗号,括号和连字符来帮助组织你的想法,但请记住,MidjourneyBot不会可靠地解释它们。

对于许多用户来说,“不能可靠地解释它们”意味着“不用麻烦了”。然而,标点符号对你的prompt几乎没有影响,这是真的吗?如果他们这样做了怎么办?如果它们有微妙的影响,你可以利用它们来调整你的prompt?

到目前为止我们对标点符号的了解

在我们研究标点符号是否重要之前,让我们来看看Midjourney机器人可以可靠理解的五个主要标点符号:

  1. 双连字符--用于--ar 3:2
  2. 双冒号::用于multiprompt(可以表示::两边字符的权重)和输入weights
  3. 花括号{}表示排列prompt
  4. 反斜杠\用于转义排列中的逗号。例如,/imagine prompt:a photo of {a bird\, cat, a dog\, fish}将创建两个prompts:/imagine prompt:a photo of a bird\, cat/imagine prompt:a photo of a dog, fish
  5. 空格实际上就是分隔单词或tokens的字符

另外两个标点符号是正斜杠 / 和冒号: ——但它们是在命令中使用,而不是在prompt中。

根据Midjourney常见问题解答,上述标点符号列表之外的所有其他标点符号都是“有趣的噪音”。

“有趣的噪音”表明某些事情显然正在发生,即使还没有设计出实验来揭示它。在对prompt进行故障排除时,有必要尝试一下有趣的噪音,因为它可能是打开你想要的东西的钥匙。

Midjourney的编程不支持理解逗号或连字符,但建议在prompts中使用它们,因为人类会使用它们,而且它们在数据集中可能具有很强的相关性。

到目前为止,我们已经讨论了我们所知道的标点符号。现在,让我们来看看这个“有趣的噪音”。

以下是我们将探讨的4个问题:

  1. 如果我们用标点符号超载prompt符会怎样?
  2. 何时使用连字符,何时不使用?
  3. 如果我们用不同的标点符号来区分概念或想法有关系吗?
  4. 机器人能理解由标点符号组成的表情符号吗?

方法

  • 至少重新提示3次
  • 小心使用 /shorten
  • 使用CLIP标记器
  • 使用MidjourneyV5.2进行测试

每个prompt将被重新提示至少三次,以确保一致性和可重复性。结果预计会受到随机化的影响,但目标是在再次提示后找到类似的东西或效果。

/shorten用于测试标点符号对prompt的影响。它用于检测由标点符号引起的单词“相对影响值”的变化。相对影响力的值表示token(或单词)对生成的输出是否具有更大的影响力或影响。

不幸的是, /shorten仍然是一项正在进行的工作,需要更可靠地精确定位生成图像所需的单词。例如,一些重要的单词被 /缩短划掉(划掉),但需要生成所需的图像。

但是,我们仍然可以使用它来验证prompt是否发生了变化,因为单词的相对影响值可能由于标点符号而发生了变化。

CLIP Tokenizer用于确定标点符号是否影响在prompt符中创建的tokens的数量。

Midjourney机器人会在读取prompt时将单词分解成称为“tokens”的片段。然后将tokens与训练数据进行比较以生成图像。

tokens的数量通常等于或超过单词的数量。更多的tokens并不总是好的,因为Midjourney V5.2中的prompt符最多只能容纳大约。60个tokens。

好的,让我们从现在开始寻找新的东西。

(1) 如果我们用标点符号超载prompt符会怎样?

感叹号用于表达强烈的情感。星号*符号强调消息传递应用程序中的单词或短语。如果我们把它们都塞进一个prompt中呢?对形象有影响吗?

Prompt 1 — /imagine prompt: a dog and a cat

Prompt 2 — /imagine prompt: a dog and a !!!!!**cat**!!!!

Prompt 3 — /imagine prompt: a cat and a !!!!!**dog**!!!!

Prompt 1 在没有标点符号时,为图像的外观建立基线。它创建了5个tokens。生成的图像描绘了一只没有表情或情感的狗和一只猫。

  • /shorten 分析只是忽略了猫作为重要关键字,这是令人沮丧的。
  • 为了演示标点符号的效果,我修改了prompt符,以包括短语“一张照片”-生成的图像与没有短语的图像相同(未显示图像)。据报道,狗的相对影响值为1,猫的相对影响值为0.6。

Prompt 2 用感叹号和星号填充了prompt。它生成了10个tokens,是prompt1的两倍!动物们现在表现出兴奋的迹象。令人惊讶的是,虽然“猫”这个词被标点符号包围,但狗比猫更兴奋。但摇了几下之后,这只猫也表现出兴奋的迹象。

  • 因为在prompt中没有使用其他词语来描述情感,很明显,标点符号改变了受试者的行为举止
  • 情绪的表现是随机的。偶尔,网格中的大多数图像都描绘了情感。其他时候,每个网格只有一个图像具有情感表达。

Prompt 3和Prompt 2是一样的,除了猫的位置和狗的位置互换了。因为Prompt 2中的狗狗同样兴奋,所以很难说位置的改变是否会让狗狗更开心。Prompt 3生成10个tokens,与Prompt 2相同。

  • 看到 /shorten如何识别感叹号(!!!!!cat)作为重要的tokens而忽略星号是很有趣的。Cat现在的相对值(0.46)低于没有标点符号的prompt符。

img

img

(上) /imagine prompt: a dog and a cat (下) /imagine prompt: a *dog and a !!!!!**cat**!!!!*

img

img

img

/shorten 分析结果. (上) /imagine prompt: a dog and a cat (中) /imagine prompt: a photo of a dog and a cat (下) /imagine prompt: *a photo of a dog and a !!!!!**cat**!!!!*

目前还不清楚我们是否可以针对一个主题,并在其周围使用多个标点符号来让主题“做某事”。

将prompt改为使用更少的感叹号对动物的表达几乎没有影响(图片未显示)。例如:/imagine prompt:a dog and a !cat!

不管周围有没有标点符号,狗狗们都很兴奋。(我还想在第一个图像网格中突出显示图4的6腿猫!)

img

img

(上) /imagine prompt: *a dog and a !!!!!**cat**!!!!* (下) /imagine prompt: *a cat and a !!!!!**dog**!!!!*

另一件需要注意的事情是,在单词周围加上星号将使它在Discord中斜体化。例如,/imagine prompt:dog将斜体/强调dog这个词。然而,不管有没有星号,都没有任何特殊效果。

摘要:标点符号会影响prompt中单词的相对影响力,产生的tokens数量,甚至受试者的行为举止。

(2) 什么时候用连字符,什么时候不用?

连字符是我最喜欢的连接单词的方便工具之一,它可以增加单词之间的联系和影响力。“影响力”指的是这些词在生成的图像中被表达或出现的能力。

Prompt 4 — /imagine prompt: a photo of an **elephant-like** **monster**

Prompt 5 — /imagine prompt: a photo of an **elephant like monster**

假设我们正在寻找具有某些怪物特征的大象,prompt 4的“elephant-like”链接短语比prompt5的结果更好。

根据“/shorten”分析的结果,“monster” token在prompt4中的影响较小,使主题看起来更像大象。

当prompt5中“monster” token的相对影响值增加时,我们得到一个具有大象特征的怪物。

prompt 4生成8个tokens,而prompt 5生成7个。

img

img

(上) /imagine prompt: a photo of an elephant-like monster (下) /imagine prompt: a photo of an elephant like monster

img

img

/shorten分析结果. (上) /imagine prompt: a photo of an elephant-like monster (下) /imagine prompt: a photo of an elephant like monster

什么时候使用连字符

Midjourney中的许多颜色使用连字符。在Midjourney V5 alpha中记录的一种公认的遗留颜色是带有连字符的“olive-green”。

Prompt 6 — /imagine prompt: a photo of a box **olive green**

Prompt 7 — /imagine prompt: a photo of a box **olive-green**

Prompt 8 — /imagine prompt: a photo of a box**, olive-green**

prompt8产生了最好的结果,假设我们正在寻找一个与橄榄无关的绿色盒子。它用逗号把盒子的概念和颜色分开。

prompt6有问题。如果没有连字符,机器人就会生成带有橄榄色和绿色方框的图像。如果这是你要找的,去掉连字符。

prompt7与prompt8非常相似。然而,它偶尔会生成一些绿色盒子和橄榄的图像。如果这就是你所需要的,那么就不需要加逗号了。

prompts6到8分别产生了7、8和9个tokens。标点符号增加了tokens的数量。

img

img

img

(上) /imagine prompt: a photo of a box olive green (中) /imagine prompt: a photo of a box olive-green (下) /imagine prompt: a photo of a box, olive-green

总结:连字符是用来连接单词,以增加他们的联想和影响力。它影响prompt中单词的相对重要性和生成的tokens的数量。

在对prompt进行故障处理时,请考虑添加或删除连字符,并判断生成的结果是否符合要求。

(3) 如果我们用不同的标点符号来区分概念或想法有关系吗?

Midjourney唯一的“官方”概念分隔符是双冒号::。这意味着机器人必须独立考虑概念或想法,而不是将它们结合起来。

双冒号在多提示符中被广泛使用。如果你是多提示和多提示的新手,看看这些故事:Midjourney:多提示的温和指南Midjourney滑块方法:如何通过多提示微调图像

还有其他很少讨论的概念分隔符。为了简单起见,我们将集中讨论逗号和管道符号。(注意:我将在本文中使用管道符号作为“标点符号”,尽管thesaurus.com说它不是。)

`/shorten据说与多提示符不兼容,但我发现它对短的多提示符有些作用。

Prompt 9 — /imagine prompt: a photo of a **honey bee**

Prompt 10 — /imagine prompt: a photo of a **honey, bee**

Prompt 11 — /imagine prompt: a photo of a **honey | bee**

Prompt 12 — /imagine prompt: a photo of a **honey:: bee::**

假设我们只是在寻找蜜蜂(不是蜂蜜),prompt9(没有标点符号)显然是赢家,表明蜜蜂在努力工作。

令人惊讶的是,逗号(prompt10)巧妙地将单词honey和bee分开。网格中至少有一张图像描绘了蜂巢上的蜜蜂。

  • 我曾经认为逗号对概念分离没有任何影响,但这是不正确的。它确实有微妙的影响。这个新信息使我想知道是否应该删除或添加逗号以排除将来的prompts。

双冒号(prompt12)将蜂蜜和蜜蜂的概念分开。它在合成中生成了蜜蜂和蜂蜜/蜂巢的独立图像。分离的概念是显而易见的。(注意:末尾的最后一个双冒号是可选的,但这是我喜欢的编写多提示符的风格。)

Pipe是一个发现,它证明了它可以分离概念,尽管比双冒号更弱、更模糊。

/shorten分析结果表明,标点符号改变了tokens的相对影响值。它支持这样一种看法,即逗号会微妙地影响概念分离,因为其影响值与没有逗号的prompt相似。

如前所述,生成的tokens数量随着使用标点符号的增加而增加。

从整体上看,标点符号的“概念分离强度”由高到低依次为:逗号(最弱)→管道→双冒号(最强)

img

img

(上) /imagine prompt: a photo of a honey bee (下) /imagine prompt: a photo of a honey, bee

img

img

(上) /imagine prompt: a photo of a honey | bee (下) /imagine prompt: a photo of a honey:: bee::

img

img

/shorten分析结果 (上) /imagine prompt: a photo of a honey bee (下) /imagine prompt: a photo of a honey, bee

img

img

/shorten 分析结果. (上) /imagine prompt: a photo of a honey | bee (下) /imagine prompt: a photo of a honey:: bee::

再次确认

考虑到Midjourney中概念分离的重要性,我将调查扩展到另一个主题上——海狮。

Prompt 13 — /imagine prompt: a photo of a **sea lion**

Prompt 14 — /imagine prompt: a photo of a **sea, lion**

Prompt 15 — /imagine prompt: a photo of a **sea | lion**

Prompt 16 — /imagine prompt: a photo of a **sea:: lion::**

实验结果与以往的蜜蜂实验结果一致。除了 / shorten的结果更难以解释。目前还不清楚为什么逗号会对相对影响值产生如此重大的影响。

img

img

(上) /imagine prompt: a photo of a sea lion (下) /imagine prompt: a photo of a sea, lion

img

img

(上) /imagine prompt: a photo of a sea | lion (下) /imagine prompt: a photo of a sea:: lion::

img

img

Results of /shorten analysis. (Top/Left) /imagine prompt: a photo of a sea lion (Bottom/Right) /imagine prompt: a photo of a sea, lion

img

img

/shorten分析结果. (Top/Left) /imagine prompt: a photo of a sea | lion (Bottom/Right) /imagine prompt: a photo of a sea:: lion::

我还测试了使用连字符和加号+的效果。这里没有显示生成的图像,因为没有什么特别的期望。

不出所料,连字符很好地连接了这些想法。加号对概念分离的影响可以忽略不计,但改变了/shorten 分析中的相对影响值。

摘要:最强的概念分隔符是一个双冒号,后跟一个管道和一个逗号。逗号在分隔prompt中的概念方面有微妙的作用。该管道可能具有用于分离概念的新应用程序,作为多提示符的替代方案。尚不清楚该管道是否受控制多提示符的相同规则的约束。

img

img

/shorten 分析结果. (Top/Left) /imagine prompt: a photo of a sea-lion (Bottom/ Right) /imagine prompt: a photo of a sea + lion

(4) 机器人能理解由标点符号组成的表情符号吗?

最后,让我们看看机器人是否能理解一些完全由标点符号(没有单词)组成的表情符号。

Prompt 17 — /imagine prompt: {:-), :-(, ;-), :-/, :-O, X-(}

以下是经过测试的简单表情符号列表:

:-)
:-(
;-)
:-/
:-O
X-(

你猜怎么着?机器人只能理解 :-( 悲伤的表情符号。其他的表情符号都不起作用。

嘿,Midjourney机器人,你现在不开心吗?是因为我们给你太多工作要做吗?

Images of sad people.

/imagine prompt: :-(

原文:Midjourney: The Ultimate Guide to Punctuation


TorchV AI支持试用!

如您有大模型应用方面的企业需求,欢迎咨询!