每日一句:Stray birds of summer come to my window to sing and fly away away. And yellow leaves of autumn, which have no songs , flutter and fall there with a sign.

研究背景与动机

通过重复输入的视角检查模型的上下文能力,定量分析输入在生成中扮演的角色,实验性证明token共现强化的存在。这一规则会在上下文共现的基础上,加强token的关系。

主要工作与贡献

实验概况:
image.png
展示了重复性输入下,逻辑能力会受到影响,模型会陷入虚假的连接中错过关键信息。
通过多种定量分析,分析token强化对于结果的双面影响。

工作缺陷与未来展望

从侧面证明了词的生成共现性会极大的影响模型的输出,并且也说明了为什么测试中会出现重复性结果。

参考文献

  1. https://arxiv.org/pdf/2310.00297.pdf

每日一句: I cannot choose the best. The best chooses me.

研究背景与动机

核心是希望能够在有限的资源下能够扩充LLM的窗口。分别通过稀疏Attention(shift short Attention)和对模型的嵌入层一起微调实现微调逼近全参的效果。
类似于直接将计算量降低+LoRA+Embedding+PI扩充窗口

主要工作与贡献

分别从数据和策略两方面实现上下文窗口的扩充。

  1. 数据侧:缺少公开长文本对话数据,此前的一些数据都是“next-token-generation”的方式的非对话语料;重新收集高质量的长文本问答语料
  2. 策略:从注意力着手修改-shift short attention偏置短注意力;用稀疏attention代替稠密attention

image.png

  1. LoRA训练基础上增加了对Embedding layer和Normalization layer的微调,实现达到和全参数调整的效果(解决LoRA无法在文本长度迁移取得良好效果的问题)

参考文献

  1. 贾佳亚团队开源全球首个70B长文本大语言模型,读论文看小说直接ProMax
  2. https://browse.arxiv.org/pdf/2309.12307.pdf

研究背景与动机

现有的一些增强推理和理解的方案中,CoT和自矫正方案是比较常用的。但在没有外部反馈的情况下,LLM 难以修正其回复,因为模型完全依赖内部表示来生成回复,这使得克服固有的能力限制变得困难。

该文章提出EoT框架,实现在问题解答过程中的跨模型的交互,从而解决这一问题。

主要工作与贡献

概括

  • 提出EoT框架
  • 提出并研究不同的沟通范式
  • 实验结果证明其有效

EoT框架

image-20231215170108276

作者提出的思维交流(Exchange-of-Thought,EoT),通过促进跨模型交流,实现对外部反馈的整合。该方法充分利用了 LLM 的通信能力,鼓励参与模型之间理性和答案的共享,从而建立起一个协同思考和分析的环境。

四种沟通范式

作者提出了四种通信范式,以确定模型之间的适当对应关系。如图所示,有记忆、报告、中继和辩论的通信范式,分别对应总线、星型、环形和树状网络拓扑。

image-20231215170221060

这四种通信范式分别是:

  1. 记忆(Memory): 采用总线拓扑,其中一个中央节点将信息传递给所有其他节点。在 EoT 中,每个模型在推理过程中生成的信息被集中存储,其他模型可以随时访问。
  2. 报告(Report): 采用星型拓扑,其中一个中心节点(中心模型)收集所有其他节点的信息。每个模型向中心模型报告其生成的推理和答案,由中心模型进行综合。
  3. 中继(Relay): 采用环形拓扑,信息通过相邻的节点依次传递。在 EoT 中,模型之间以循环的方式传递推理信息,允许信息在模型之间传递和融合。
  4. 辩论(Debate): 采用树状拓扑,一个中心节点与多个子节点相连。在 EoT 中,中心模型与其他模型展开辩论,各自提出不同的推理和答案,最终达成共识或者根据多数意见选择答案。

对应的不同沟通方式的通信量都有不同,并为了避免不必要的通信,本文也给出了终止通信的标准:一致输出与多数共识。

置信度评估

通过多轮生成的结果是否一致,判断模型输出的信息是否可靠,保护解决过程中免受错误信息的干扰。

未来展望

在 CoT 的基础上,作者引入思维交流(EoT)框架,实现了不同模型之间的跨模型交流,从而使推理性能得到显著提升。

EoT的四种通信范式不仅在各种推理任务中展现出卓越的性能,而且相较于其他方法更节约计算成本。通过对外部反馈的整合,EoT 提供了一种新颖而高效的方式,使模型在解决复杂问题时能够更全面、深入地进行推理。

实验证明,EoT 在常识推理和符号推理任务上表现出更好的性能,充分发挥了多模型协作的潜力。我们的研究也强调了模型多样性的重要性,通过整合不同模型的优势,取得了比单一模型更出色的效果。

工作会偏工程应用,更像是以多个模型去实现这样的任务,并不是在模型基础上优化实现的,存在一定的局限性。

参考文献

  1. https://mp.weixin.qq.com/s/hxZ5SnVcQd2zRSYDSsZOuQ
  2. https://arxiv.org/pdf/2312.01823.pdf

TCP与UDP

都同属于TCP/IP协议簇。

UDP TCP
是否连接 无连接 面向连接
是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输(数据顺序和正确性),使用流量控制和拥塞控制
连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信
传输方式 面向报文 面向字节流
首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节
适用场景 适用于实时应用,例如视频会议、直播 适用于要求可靠传输的应用,例如文件传输

TCP

TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。

TCP三次握手

  1. A->B,发送一个含有同步序列号标志位数据,请求建立连接–希望通信、你可以用哪个序列号作为起始数据段回应
  2. B->A,ACK+SYN响应–收到请求,你可以用哪个序列号作为起始数据段回应
  3. A->B,ACK–确认已收到主机B 的数据段

特点:没有应用层的数据 ,SYN这个标志位只有在TCP建立连接时才会被置1 ,握手完成后SYN标志位被置0。

TCP四次挥手

  1. A提出停止TCP连接的请求,将控制位FIN置一
  2. B收到FIN确认这一方向上的TCP连接将关闭,将ACK置1,
  3. B提出反方向的关闭请求,将FIN置1
  4. A请求进行确认,将ACK置1,双方向的关闭结束.

连接机制原因

  1. 为什么握手要三次,挥手却要四次呢?

    那是因为握手的时候并没有数据传输,所以服务端的 SYN 和 ACK 报文可以一起发送,但是挥手的时候有数据在传输,所以 ACK 和 FIN 报文不能同时发送,需要分两步,所以会比握手多一步。

  2. 为什么要三次握手

    三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的

    只有经过三次握手才能确认双发的收发功能都正常,缺一不可:

    第一次握手(客户端发送 SYN 报文给服务器,服务器接收该报文):客户端什么都不能确认;服务器确认了对方发送正常,自己接收正常

    第二次握手(服务器响应 SYN 报文给客户端,客户端接收该报文):客户端确认了:自己发送、接收正常,对方发送、接收正常;服务器确认了:对方发送正常,自己接收正常

    第三次握手(客户端发送 ACK 报文给服务器):客户端确认了:自己发送、接收正常,对方发送、接收正常; 服务器确认了:自己发送、接收正常,对方发送、接收正常

  3. 为什么要四次挥手

由于 TCP 的半关闭(half-close)特性,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

  1. 实现数据可靠传输,为什么刚好需要三次握手呢?如果两次握手,行不行?

    两次握手:

    • A发送同步信号SYN+A的初始序列号
    • B发送同步信号SYN+B的初始序列号+B的ACK序列号

    两次握手会产生一个问题,B没办法知道A是不是已经接收了自己的同步信号。一旦这个同步信号丢了,A和B就B的初始序列号将无法达成一致。

    显然,两次握手是不可取的。

    那么四次握手又如何呢?

    • A发送同步信号SYN+A的初始序列号
    • B确认收到A的同步信号,并记录A的ISN到本地,命名B的ACK序列号
    • B发送同步信号SYN+B的初始序列号
    • A确认收到B的同步信号,并记录B的ISN到本地,命名A的ACK序列号

    显然,并不需要四个步骤,2和3和可以合并,提高连接的速度和效率。

    TCP协议需考虑到可靠性和传输效率,明白了这一点,我们也就明白了为什么只能是三次握手,而不是两次或者四次了。

UDP

UDP 在传送数据之前不需要先建立连接,远程主机在收到 UDP 报文后,不需要给出任何确认。

Refs

  1. TCP和UDP的区别 (zhihu.com)
  2. [TCP 和 UDP 是什麼:簡單的說明 | NordVPN](https://nordvpn.com/zh-tw/blog/tcp-udp-bijiao/#:~:text=TCP 有錯誤檢查和,服務,例如FTP 檔案傳輸。)
  3. 关于TCP为什么三次握手和四次挥手,满分回答在此 - 知乎 (zhihu.com)
  4. TCP为什么是三次握手?两次、四次握手不行吗?-tcp三次握手 (51cto.com)

通信协议

二叉树

相关概念

  • 满二叉树:所有的度(就是节点)都是 2,就称为满二叉树。

  • 完全二叉树:从左向右边,的节点没有间断,即为完全二叉树。(有右节点,肯定有左节点,有左节点,不一定有右节点)

  • 非完全二叉树:有间断了,就是非完全二叉树。

性质

第i层节点:2(i-1)

层次为k的二叉树,最多有 $2^{k-1}$个节点

对任何一颗二叉树,如果其叶子节点数为 n0,度为 2 的节点数为 n2, 则有 n0 = n2 + 1

哈希表

定义

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。

本质

本质就是数组,具体实现中,分别可以采用:

  1. 数组+链表
  2. 数组+二叉树

后一种结构主要用于解决哈希冲突

哈希内核

哈希函数/散列函数

大白话散列函数:

例如,查找人名比较快的方法是按照首字母排序,再查找。

对应到哈希表中,直接拿到当前首字母(值地址)的方法就是散列函数。

  • 常见的哈希函数

一个哈希函数的好不好,取决于以下三点

  1. 哈希函数的定义域必须包括需要存储的全部关键码,而如果哈希表允许有m个地址时,其值域必须在0 到m-1之间
  2. 哈希函数计算出来的地址能均匀分布在整个空间中
  3. 哈希函数应该比较简单

eg:除留余数法、直接定制法、平方取中法

哈希冲突

解决哈希冲突主要有两个方案:闭散列开散列

  • 闭散列

    也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那 么可以把key存放到冲突位置中的“下一个” 空位置中去

    闭散列中主要处理方法有 线性探测二次探测

    线性探测:下一个空的位置;伪删除;当前数据达到负载因子这个阈值时,就需要将当前表进行扩容;

    二次探测:通过当前位置的哈希冲突次数的平方查找新的位置;

  • 开散列

    链地址法

哈希表中存储的是链表的头结点。具有相同的哈希地址会存放在同一链表中,每个链表中的元素都具有相同的哈希地址。

LSH

References

  1. 来吧!一文彻底搞定哈希表!
  2. 数据结构 5分钟带你搞定哈希表(建议收藏)!!!

最小堆与最大堆

堆树的定义

堆树的定义如下:

(1)堆树是一颗完全二叉树

(2)堆树中某个节点的值总是不大于或不小于其孩子节点的值

(3)堆树中每个节点的子树都是堆树

当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。

最大堆

最小堆

堆树操作

  1. 构造最大堆
    思路:将每个叶子节点视为一个堆,再将每个叶子节点与其父节点一起构造成包含一个更多节点的堆
    实现:首先需要找到最后一个节点的父节点,从这个节点开始构造最大堆;直到该节点前面所有分支节点都处理完毕

  2. 插入节点
    思路:在堆的最后添加一个节点,然后沿着堆树上升。

  3. 删除节点
    思路:将堆树的最后的节点提到根结点,然后删除最大值,然后再把新的根节点放到合适的位置。

堆排序

相当于是堆树操作中的删除节点的操作。

1、创建一个最大(小)堆H;

2、把堆首和堆尾元素互换;

3、把堆的大小减1,重新构造一个最大(小)堆;

4、重复步骤2、3,直到堆的大小减少为1。

References

  1. 堆树(最大堆、最小堆)详解
  2. 经典排序算法(7)——堆排序算法详解

集合

is 与 == 的区别

is 用于判断两个变量引用对象是否为同一个, == 用于判断引用变量的值是否相等。

a is b 相当于id(a)==id(b),id() 能够获取对象的内存地址。 如果a=10;b=a; 则此时a 和b 的内存地址一样的; 但当a=[1,2,3]; 另b=a[:] 时,虽然a 和b 的值一样,但内存地址不一样。

finally

无论try语句中是否抛出异常,finally中的语句一定会被执行。

list方法的计算复杂度

Operation Big-O Efficiency
index [] O(1)
index assignment O(1)
append O(1)
pop() O(1)
pop(i) O(n)
insert(i,item) O(n)
del operator O(n)
iteration O(n)
contains (in) O(n)
get slice [x:y] O(k)
del slice O(n)
set slice O(n+k)
reverse O(n)
concatenate O(k)
sort O(n log n)
multiply O(nk)

装饰器

装饰器定义:装饰器便于代码复用, 将函数作为参数传给装饰器函数, 拓展原来函数功能的一种函数。

装饰器作用:装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能(增强函数功能但是又不修改原函数, 抽离函数中与函数本身无关的功能进行复用)。

Python的装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数。
这样我们可以在不改变被装饰函数的代码的情况下给被装饰函数或程序添加新的功能。
Python的装饰器广泛应用于缓存、权限校验、性能测试(比如统计一段程序的运行时间)和插入日志等应用场景。
有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码,增加一个函数的重用性。

用装饰器实现程序的计时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time

def time_it(func):
def inner():
start = time.time()
func()
end = time.time()
print('用时:{}秒'.format(end-start))
return inner

@time_it
def func1():
time.sleep(2)
print("Func1 is running.")

if __name__ == '__main__':
func1()

迭代器

迭代器定义:迭代器(Iterator)是访问集合内元素的一种方式,提供了一种遍历序列对象的方法。一个类(对象)只要含有__iter__、__next__两个方法,就将其称为迭代器。

迭代器作用:迭代器最核心的功能就是可以通过__next__方法的调用来返回下一个值。而这个值不是从已有的数据中读取的,而是通过程序按照一定的规则生成的。这也就意味着我们可以不再依赖一个现存的数据集合来存放数据,而是边用边生成,这样的好处就是可以节省大量的内存空间。

生成器

生成器定义:一边循环一边计算的机制,称为生成器(generator)。生成器(generator)也是一种迭代器,在每次迭代时返回一个值,直到抛出 StopIteration 异常。

生成器作用:列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

Refs

  1. 【Python系列】为啥老问装饰器、迭代器、生成器?-腾讯云开发者社区-腾讯云 (tencent.com)

传参

不可变对象 :int,string,float,tuple – 可理解为C中,该参数为值传递
可变对象 :list,dictionary – 可理解为C中,该参数为指针传递

对于不可变对象作为函数参数,相当于C系语言的值传递;
对于可变对象作为函数参数,相当于C系语言的引用传递,但其实不完全是引用传递,会先构造一个新的引用,更像是赋值引用。

Refs

  1. (1 封私信 / 59 条消息) Python 的函数是怎么传递参数的? - 知乎 (zhihu.com)

Python数组和列表有什么区别?

Python中的数组和列表具有相同的存储数据方式。但是,数组只能包含单个数据类型元素,而列表可以包含任何数据类型元素。

为什么使用*args,**kwargs?

当我们不确定将多少个参数传递给函数,或者我们想要将存储的列表或参数元组传递给函数时,我们使用*args。当我们不知道将多少关键字参数传递给函数时使用**kwargs,或者它可以用于将字典的值作为关键字参数传递。标识符args和kwargs是一个约定。

如何在Python中删除文件?

要在Python中删除文件,您需要导入OS模块。之后,您需要使用os.remove()函数。

如何在Python中实现多线程?

Python有一个多线程库,但是用多线程来加速代码的效果并不是那么的好,

Python有一个名为Global Interpreter Lock(GIL)的结构。GIL确保每次只能执行一个“线程”。一个线程获取GIL执行相关操作,然后将GIL传递到下一个线程。

虽然看起来程序被多线程并行执行,但它们实际上只是轮流使用相同的CPU核心。

所有这些GIL传递都增加了执行的开销。这意味着多线程并不能让程序运行的更快。

全局解释锁

适当的多线程能够提高运行效率,但在python中并不如此。

GIL功能:在 CPython 解释器中执行的每一个 Python线程,都会先锁住自己,以阻止别的线程执行。

存在原因:古老单核的调度机制

因此并行的过程在python中是通过线程交替执行模拟并行的线程。

CPython 中还有另一个机制,叫做间隔式检查(check_interval),意思是 CPython 解释器会去轮询检查线程 GIL 的锁住情况,每隔一段时间,Python 解释器就会强制当前线程去释放 GIL,这样别的线程才能有执行的机会。

GIL不意味着线程安全:
因为间隔式检查这种抢占机制,线程有可能也会被打断

避免GIL影响:

  1. 在以IO操作为主的IO密集型应用中,相比进程操作,线程操作更加轻量级,线程之间的通讯复杂度更低,建议使用多线程。
  2. 如果是计算密集型的应用,尽量使用多进程或者协程来代替多线程。

参考:

  1. Python GIL全局解释器锁详解(深度剖析) (biancheng.net)
  2. 深入理解Python中的GIL(全局解释器锁)。 - 知乎 (zhihu.com)

  1. 虚函数与纯虚函数

虚函数,在类成员方法的声明(不是定义)语句前加“virtual”, 如 virtual void func()

纯虚函数,在虚函数后加“=0”,如 virtual void func()=0

对于虚函数,子类可以(也可以不)重新定义基类的虚函数,该行为称之为复写Override。

对于纯虚函数,子类必须提供纯虚函数的个性化实现。

  1. 多态

多态(polymorphism)是面向对象编程语言的一大特点,而虚函数是实现多态的机制。其核心理念就是通过基类访问派生类定义的函数。

onnx框架

统一模型表示形式
ONNX 运行时是一种用于将 ONNX 模型部署到生产环境的高性能推理引擎。 它针对云和 Edge 进行了优化,适用于 Linux、Windows 和 Mac。 它使用 C++ 编写,还包含 C、Python、C#、Java 和 Javascript (Node.js) API,可在各种环境中使用。

ONNX 文件不仅仅存储了神经网络模型的权重,同时也存储了模型的结构信息以及网络中每一层的输入输出和一些其它的辅助信息。

refs

http://giantpandacv.com/project/%E9%83%A8%E7%BD%B2%E4%BC%98%E5%8C%96/AI%20%E9%83%A8%E7%BD%B2%E5%8F%8A%E5%85%B6%E5%AE%83%E4%BC%98%E5%8C%96%E7%AE%97%E6%B3%95/ONNX%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

DPO

Direct Preference Optimization: Your Language Model is Secretly a Reward Model

核心思路:通过监督学习的方式实现RLHF对齐人类偏好的效果。
概括实现:使用奖励函数和最优策略的映射,实现约束奖励最大化问题的效果,通过单阶段策略训练优化不再需要拟合RM的训练阶段,可以直接微调对齐人类偏好

具体实现:
基于之前的RL微调的一些工作我们可以知道对应的带约束的优化问题的设置:

img

转换可以得到最优解:
img

那么其对应的奖励函数可以转换成以下形式:
img

结合奖励模型的训练方式,是增大不同答案的差异性,将其引入之后,新的偏好模型可以得到是以下的形式,其中$\sigma$是sigmoid函数:
img

基于以上推导,即可得到DPO的目标函数,其中包含了RLHF的相关过程:

img

Refs

  1. DPO——RLHF 的替代之《Direct Preference Optimization: Your Language Model is Secretly a Reward Model》论文阅读 - 知乎 (zhihu.com)
0%