AWQ: Activation-Aware Weight Quantization For On-Device LLM Compression AND Acceleration
AWQ: Activation-Aware Weight Quantization For On-Device LLM Compression AND Acceleration 研究方向: On-device LLM 两个主要的内容是 Memory size: 内存容量(模型参数能不能放到设备里面) Memory bandwidth(内存带宽) : 即使模型装得下,能否快速的读取权重,进行计算 要理解这个问题,首先要理解到量化的一个核心 压缩率不等于加速比 除此之外,作者的一个核心观察是 Weights are not equally important Protecting only 1% of salient weights can greatly reduce quatization error 作者把重要的权重称为 salient weights, 显著重要的权重 实验中,作者发现只保护一小部分这样的权重就能够大幅度优化量化后的模型表现。然而问题同时也来了,怎么判断那些权重重要? 当然,权重的重要程度是和 Activation相关的,而不能只关注weight本身的...
RustDiary Day 5 - struct and array
元组 元组是多种类型组合到一起形成的,因此它是复合类型,元组的长度是固定的,元组的顺序也是固定的。 123fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1);} 变量tpu被绑定了一个元组值 (500,6.4,1), 该元组的类型是(i32, f64, u8) 元组使用括号将多个类型组合到一起。 用模型匹配解构元组 1234567fn main() { let tup = (500, 6.4, 1); let (x,y,z) = tup; println!("The value of y is: {}", y);} 上述代码首先创建一个元组,然后帮他绑定到tup上面,然后用 let( x,y,z) = tup来完成模式匹配。。 用 . 来访问元组 模式匹配可以让我们一次性的把元组中的值取出来,但是如果只是想要访问特定的元素,那么模式匹配就略显繁琐,对此,Rust提供了.的访问方式: 12345678910fn main(){ le...
Rust Diary Day 5-> Linked List
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162// 填空,让代码运行use crate::List::*;enum List { // Cons: 链表中包含有值的节点,节点是元组类型,第一个元素是节点的值,第二个元素是指向下一个节点的指针 Cons(u32, Box<List>), // Nil: 链表中的最后一个节点,用于说明链表的结束 Nil,}// 为枚举实现一些方法impl List { // 创建空的链表 fn new() -> List { // 因为没有节点,所以直接返回 Nil 节点 // 枚举成员 Nil 的类型是 List Nil } // 在老的链表前面新增一个节点,并返回新的链表 fn prepend(self,...
RustDiary -Day4 : Tuple, Struct
元组 元组是多种类型组合到一起形成的,因此它是复合类型,元组的长度是固定的,元组的顺序也是固定的。 123fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1);} 变量tpu被绑定了一个元组值 (500,6.4,1), 该元组的类型是(i32, f64, u8) 元组使用括号将多个类型组合到一起。 用模型匹配解构元组 1234567fn main() { let tup = (500, 6.4, 1); let (x,y,z) = tup; println!("The value of y is: {}", y);} 上述代码首先创建一个元组,然后帮他绑定到tup上面,然后用 let( x,y,z) = tup来完成模式匹配。。 用 . 来访问元组 模式匹配可以让我们一次性的把元组中的值取出来,但是如果只是想要访问特定的元素,那么模式匹配就略显繁琐,对此,Rust提供了.的访问方式: 12345678910fn main(){ le...
RustDiary Day3 ——String
复合类型 复合类型是由其他类型组合而成的,最典型的就是结构体struct和枚举enum,例如平面上的一个点point(x,y),由两个数值类型的值 x, y 组合而来。 看一段代码: 12345678910111213141516171819202122#![allow(unused_variables)]type File = String;fn open(f: &mut File) -> bool { true}fn close(f: &mut File) { true}#[allow(dead_code)]fn read(f: &mut File, save_to: &mut Vec<u8>) -> !{ unimplemented!()}fn main() { let mut f1 = File::from("f1.txt"); open(&mut f1); close(&mut f1);}...
Target Regeneration Shrinks the Gains of Draft-Training Objectives in Speculative Decoding
Target Regeneration Shrinks the Gains of Draft-Training Objectives in Speculative Decoding 我们看到新的Loss function 相比于 Cross-Entropy显著提高了Acceptance , 但是这个增益究竟是来自Loss本身,还是因为训练数据原本就不符合target的输出分布? Abstract 核心问题: External Responses 与 Target Responses 不一致 假设我们的训练数据来自于另外一个模型,例如GPT-4 , 而现在希望为Qwen3-8B 训练一个Drafter,那么训练集中的某个位置真实token可以看作是 yt, 而Qwen3-8B在某个位置上最可能生成的数据和y_t不一样。 假如我们直接使用普通的CE,其实是在要求Draft模仿外部数据的Token,但是SD要匹配的是Target的输出。 这个时候使用KD 就是很合理的了,训练drafter 匹配 完整target的token distribution, 看起来就能获得明显受益。...
Orca: The beginning of Morden LLM serving.
Orca: The beginning of Morden LLM serving. 总的来说,ORCA最重要的历史贡献,是在Transformer自回归推理中系统化提出Iteration levelscheduling, 迭代级调度和Selective Batching(选择性批处理), 前者是后来通常所说的Continuos batching的重要基础。 Orca改变的是推理引擎最基本的执行单位,这项设计今天仍然适用于单卡和双卡推理。 Abstract 论文的摘要很直接。 首先,传统的Request level batching不适合自回归生成。例如现在我们有三个请求 A 需要生成100 tokens B 需要 20 tokens C 在A \ B 执行10轮后到达。 2022年那会,传统的Request Batching会把A \ B 作为固定Batch交给Execution Engine,等B返回之后,还得等待A运行,而C则要等待一整个Batch才能加入。 所以Orca自然不会放过这个现象,Orca让Scheduler在每次model iteration结束之后,重新...
Rust Diary DAY2 —— Ownership and reference
所有权和借用 Rust的内存安全性是其最为显著的特征。在以往,内存安全几乎都是通过GC的方式实现,但是GC会引来性能、内存占用的问题。 Rust因此采用了所有权系统。 所有权 所有的程序都得和计算机内存打交道,如何从内存中申请空间来存放程序的运行内容,如何在不需要的时候释放这些空间,成了重中之重。 在语言的演变中,产生了三种流派 GC: 程序运行的时候不断寻找不再使用的内存,eg: java go 手动管理: 程序员自己去申请和释放内存,eg: C C++ 所有权系统: 编译器在编译期就能知道内存的使用情况,eg: Rust 例子 123456int* foo(){ int a ; a = 100; char *c = "xyz"; return &a;} a和c都是局部变量,但是局部变量a存在栈中,离开作用域之后,a申请的栈上内存都会被回收,从而造成了悬空指针的问题。 变量c是常量字符串,存储在常量区域, "xyz" 只有在整个程序结束后才能回收这片内存。 所以内存安全问题,一直都是程序员很头痛的问...
RustDiary 3 Sentence and function
语句和表达式 Rust的函数体是由一系列语句组成,最后一个表达式来返回值,例如: 12345fn add_with_extra(x:i32, y:i32) -> i32{ let x = x+ 1; let y = y + 5; x + y;} 语句会执行一些操作但是不会返回一个值,而表达式在球之后会返回一个值。 上面两行是语句,最后一行是表达式,返回值是 x+y 的结果。 rust中,基于语句和表达式的方式是非常重要的 语句 123let a = 8;let b:Vec<f64> = Vec::new();let (a,c) = ("hi", false); 以上都是语句,他们完成一个具体的操作,但是没有返回值,因此是语句 由于let是语句,因此不能将let的返回值赋值给别的值 12//error examplelet b = (let a = 8); 表达式 表达式会进行求值,然后返回一个值,例如 5+6 表达式可以成为语句中的一部分,例如let y = 6 中, 6就是一个表达式。它在求值后返回一个值 6 调用...
RustDiary——2 类型
基本类型 Rust 的每个值都有其确切的主句类型,总的来说可以分为两类: 基本类型和复合类型。 基本类型意味着他们往往是一个最小化原子类型,无法解构为其他类,一般来说由以下的值组成: 有符号整数 i8 i16 i32 i64 i128 isize 无符号整数 u8 u16 u32 u64 u128 usize 浮点数 f32 f64 字符串切片 &str 布尔类型 bool 字符类型 char 表示单个Unicode字符,存储为4个字节 单元类型 () 也叫做unit类型,表示没有值的类型,类似于C语言中的void类型。 类型推导和类型标注 rust是静态类型语言,编译器必须在编译期知道所有变量的类型。 Rust编译器很聪明,可以根据变量的值和上下文中的食用方式来自动推导出变量的类型。 1let guess = "42".parse().expect("Not a number!"); 这段代码的目的是对字符串42进行解析,而编译器在这里无法推导出我们想要的类型。因此,我们要给编译器guess变量一个显式的类型标注: let ...









