三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

反量化:反量化K类(q4_k、q5_k、q6_k)

反量化:反量化K类(q4_k、q5_k、q6_k)

反量化K类.h

// 反量化K类.h —— K 系量化块(q4_k/q5_k/q6_k)的反量化// 用途:把 K 系量化块(每块 256 元素)转为 f32 输出。// 布局(本任务实测确认):// q4_k(144B):d(f16)+dmin(f16)+scales[12]+qs[128];8 组 × 32 元素;x = d·sc·q − dmin·m// q5_k(176B):d+dmin+scales[12]+qh[32]+qs[128];8 组 × 32 元素;q5 含第 5 位// q6_k(210B):ql[128]+qh[64]+scales[16 有符号]+d(f16,块末尾);16 组 × 16 元素;无 dmin#pragmaonce// 引入基础类型(浮点/索引)#include"公共/基础定义.h"// 反量化Q4K块:把一个 q4_k 量化块(256 元素、144 字节)反量化为 f32// 公式:x = (q4 · 组缩放) − dmin·m,组缩放 = d·sc(sc 是 6 位组缩放码)void反量化Q4K块(constuint8_t*,float*输出,size_t 元素数);// 反量化Q5K块:把一个 q5_k 量化块(256 元素、176 字节)反量化为 f32// 公式:x = (q5 · 组缩放) − dmin·m,q5 = 低4位 | (qh 第5位 × 16)void反量化Q5K块(constuint8_t*,float*输出,size_t 元素数);// 反量化Q6K块:把一个 q6_k 量化块(256 元素、210 字节)反量化为 f32// 公式:x = d·sc·(q6 − 32),q6 = 6 位值(低4位 | 高2位×16),sc 是 8 位有符号组缩放void反量化Q6K块(constuint8_t*,float*输出,size_t 元素数);

反量化K类.cpp

// 反量化K类.cpp —— K 系量化块(q4_k/q5_k/q6_k)的反量化实现// 用途:把 K 系量化块(每块 256 元素)转为 f32 输出。// 布局(本任务实测确认,勿凭旧注释/stock 结构):// q4_k(144B):d(f16)+dmin(f16)+scales[12]+qs[128];8 组 × 32 元素// q5_k(176B):d+dmin+scales[12]+qh[32]+qs[128]// q6_k(210B):ql[128]+qh[64]+scales[16 有符号]+d(f16,块末尾);无 dmin#include"内核/反量化/反量化K类.h"#include"内核/反量化/反量化Q8零.h"// 引入错误处理(元素数校验抛运行错误)#include"公共/错误处理.h"// 引入标准头:浮点幂/极限#include<cmath>#include<limits>// 取缩放最小:解出第 j 组(0..7)的 6 位组缩放码与 6 位最小码// 说明:scales[12] 打包 8 个 6 位缩放 + 8 个 6 位最小。j<4 时两组分别存于// scales[j] 与 scales[j+4] 的低 6 位;j>=4 时高 4 位被拆分:// scales[j+4] 低 4 位为缩放低 4 位、高 4 位为最小低 4 位,// scales[j-4] 高 2 位为缩放高 2 位、scales[j] 高 2 位为最小高 2 位staticvoid取缩放最小(intj,constuint8_t*scales,uint8_t&缩放,uint8_t&最小){if(j<4){缩放=scales[j]&63;最小=scales[j+4]&63;}else{缩放=static_cast<uint8_t>((scales[j+4]&0xF)|((scales[j-4]>>6)<<4));最小=static_cast<uint8_t>((scales[j+4]>>4)|((scales[j]>>6)<<4));}}// 反量化Q4K块:把一个 q4_k 量化块(256 元素、144 字节)反量化为 f32// 布局:d(2)+dmin(2)+scales(12)+qs(128)。8 组 × 32 元素,每组一个 6 位缩放码与最小码。// 数据排列:每 64 元素为一个 chunk(32 字节 qs);组 2j 用字节低半字节、组 2j+1 用高半字节// 公式:x = d·sc·q − dmin·mvoid反量化Q4K块(constuint8_t*,float*输出,size_t 元素数){// K 系块固定每块 256 元素(防御:元素数不符直接抛错)if(元素数!=256){抛出运行错误("Q4K块 元素数必须为 256");}// 块头:d(f16) + dmin(f16)const浮点 d=半精度转浮点(static_cast<uint16_t>([0]|([1]<<8)));const浮点 整体最小=半精度转浮点(static_cast<uint16_t>([2]|([3]<<8)));constuint8_t*scales=+4;constuint8_t*qs=+16;// 4 个 chunk,每个 64 元素、32 字节 qs、2 组(缩放)for(intc=0;c<4;++c){uint8_ts0,m0,s1,m1;取缩放最小(c*2+0,scales,s0,m0);取缩放最小(c*2+1,scales,s1,m1);const浮点 d1=d*s0,最小1=整体最小*m0;const浮点 d2=d*s1,最小2=整体最小*m1;constuint8_t*q=qs+c*32;// 组 2c(低半字节):x = d1·q − 最小1for(intl=0;l<32;++l){输出[c*64+l]=d1*(q[l]&0xF)-最小1;}// 组 2c+1(高半字节):x = d2·q − 最小2for(intl=0;l<32;++l){输出[c*64+32+l]=d2*(q[l]>>4)-最小2;}}}// 反量化Q5K块:把一个 q5_k 量化块(256 元素、176 字节)反量化为 f32// 布局:d(2)+dmin(2)+scales(12)+qh(32)+qs(128)。8 组 × 32 元素。// q5 = 低 4 位(qs) | 第 5 位(qh);每 chunk 用 qh[l] 的 u1/u2 位(起始 1/2,每 chunk 左移 2)// 公式:x = d·sc·q5 − dmin·mvoid反量化Q5K块(constuint8_t*,float*输出,size_t 元素数){// K 系块固定每块 256 元素(防御:元素数不符直接抛错)if(元素数!=256){抛出运行错误("Q5K块 元素数必须为 256");}// 块头:d(f16) + dmin(f16)const浮点 d=半精度转浮点(static_cast<uint16_t>([0]|([1]<<8)));const浮点 整体最小=半精度转浮点(static_cast<uint16_t>([2]|([3]<<8)));constuint8_t*scales=+4;constuint8_t*qh=+16;constuint8_t*qs=+48;// 4 个 chunk;u1/u2 是 qh[l] 中对应第 5 位的掩码,每 chunk 左移 2uint8_tu1=1,u2=2;for(intc=0;c<4;++c){uint8_ts0,m0,s1,m1;取缩放最小(c*2+0,scales,s0,m0);取缩放最小(c*2+1,scales,s1,m1);const浮点 d1=d*s0,最小1=整体最小*m0;const浮点 d2=d*s1,最小2=整体最小*m1;constuint8_t*q=qs+c*32;// 组 2c(低半字节 + qh 第 5 位)for(intl=0;l<32;++l){const浮点 q5a=static_cast<浮点>((q[l]&0xF)+((qh[l]&u1)?16:0));输出[c*64+l]=d1*q5a-最小1;}// 组 2c+1(高半字节 + qh 第 5 位)for(intl=0;l<32;++l){const浮点 q5b=static_cast<浮点>((q[l]>>4)+((qh[l]&u2)?16:0));输出[c*64+32+l]=d2*q5b-最小2;}u1=static_cast<uint8_t>(u1<<2);u2=static_cast<uint8_t>(u2<<2);}}// 反量化Q6K块:把一个 q6_k 量化块(256 元素、210 字节)反量化为 f32// 布局:ql(128)+qh(64)+scales(16 有符号)+d(f16,块末尾)。16 组 × 16 元素,无 dmin。// 数据:每 128 元素为一半;q1/q2 用 ql 低半字节、q3/q4 用高半字节;qh 每字节 8 元素各 2 位// 公式:x = d·sc·(q6 − 32),q6 = 6 位值(低4位 | 高2位×16)void反量化Q6K块(constuint8_t*,float*输出,size_t 元素数){// K 系块固定每块 256 元素(防御:元素数不符直接抛错)if(元素数!=256){抛出运行错误("Q6K块 元素数必须为 256");}// d 在块末尾(字节 208~209)const浮点 d=半精度转浮点(static_cast<uint16_t>([208]|([209]<<8)));constuint8_t*ql=;constuint8_t*qh=+128;constint8_t*sc=reinterpret_cast<constint8_t*>(+192);// 2 个 128 元素一半(ql 每半 64 字节、qh 每半 32 字节、scales 每半 8 个)for(intn=0;n<2;++n){for(intl=0;l<32;++l){// is 确定缩放下标所在半组(每 16 元素一组)constintis=l/16;// 四种元素:低/高半字节 × 前/后 32 字节,各取 qh 中 2 位constint8_tq1=static_cast<int8_t>((ql[n*64+l]&0xF)|(((qh[n*32+l]>>0)&3)<<4))-32;constint8_tq2=static_cast<int8_t>((ql[n*64+l+32]&0xF)|(((qh[n*32+l]>>2)&3)<<4))-32;constint8_tq3=static_cast<int8_t>((ql[n*64+l]>>4)|(((qh[n*32+l]>>4)&3)<<4))-32;constint8_tq4=static_cast<int8_t>((ql[n*64+l+32]>>4)|(((qh[n*32+l]>>6)&3)<<4))-32;// 输出按元素序直接下标写入(q1→l+0,q2→l+32,q3→l+64,q4→l+96)输出[n*128+l+0]=d*sc[n*8+is+0]*q1;输出[n*128+l+32]=d*sc[n*8+is+2]*q2;输出[n*128+l+64]=d*sc[n*8+is+4]*q3;输出[n*128+l+96]=d*sc[n*8+is+6]*q4;}}}
← 返回列表