Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
BuxianChen committed Nov 14, 2024
1 parent f146ebb commit 642e3ef
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 19 deletions.
73 changes: 73 additions & 0 deletions _drafts/2023-08-15-streamlit-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,76 @@ print("end", rerun_id)
参考资料:

- 博客: [part-1](https://blog.streamlit.io/streamlit-authenticator-part-1-adding-an-authentication-component-to-your-app/), [part-2](https://blog.streamlit.io/streamlit-authenticator-part-2-adding-advanced-features-to-your-authentication-component/)


## 示例

### 常见错误: `session_state` 无法被修改

```python
import streamlit as st
from uuid import uuid4

RUN_ID = str(uuid4())
print(f"RERUN {RUN_ID}: {st.session_state}")
st.text_input("text", key="text")
button = st.button("save")
if button:
print(f"点击按钮 {RUN_ID}: {st.session_state}")
st.session_state["text"] = ""
print(f"FINISH_RUN {RUN_ID}: {st.session_state}")
```

将上述代码运行起来后, 与前端交互, 先在文本输入框里输入, 一切正常, 点击按钮, 则执行报错, 后端日志如下

```
# 第一次 rerun
RERUN bd8c35e4-60bc-4a62-b741-c1503ee37b86: {}
FINISH_RUN bd8c35e4-60bc-4a62-b741-c1503ee37b86: {'text': ''}
# 输入文本后触发 rerun
RERUN cfd7307c-6bd1-4368-b155-a9867f0d2d15: {'text': '12'}
FINISH_RUN cfd7307c-6bd1-4368-b155-a9867f0d2d15: {'text': '12'}
# 点击按钮后触发 rerun
RERUN 17935c8e-78af-4fa3-b28a-f807a571b21d: {'text': '12'}
点击按钮 17935c8e-78af-4fa3-b28a-f807a571b21d: {'text': '12'}
streamlit.errors.StreamlitAPIException: `st.session_state.text` cannot be modified after the widget with key `text` is instantiated.
```

原因是 streamlit 在组件被渲染了之后就不允许修改: 在我们点击按钮时, streamlit 会记录组件的现状, 并重新运行脚本, 当我们进入 if 分支后, 尝试对组件的值重新修改, 就会引发错误.

**修正方式**

```python
import streamlit as st
from uuid import uuid4

RUN_ID = str(uuid4())
print(f"RERUN {RUN_ID}: {st.session_state}")

def button_callback():
print(f"点击按钮 {RUN_ID}: {st.session_state}")
st.session_state["text"] = ""

st.text_input("text", key="text")
button = st.button("save", on_click=button_callback)
print(f"FINISH_RUN {RUN_ID}: {st.session_state}")
```

后端日志

```
# 第一次 rerun
RERUN 274c2950-a3ad-4a15-b992-7291958d3cfa: {}
FINISH_RUN 274c2950-a3ad-4a15-b992-7291958d3cfa: {'text': ''}
# 输入文本后触发 rerun
RERUN 1e371487-47cb-4daf-8e04-cfef94b09800: {'text': '12'}
FINISH_RUN 1e371487-47cb-4daf-8e04-cfef94b09800: {'text': '12'}
# 点击按钮后, 注意: 在 callback 里, RUN_ID 的值还是上一次的值, 但 rerun 之后, RUN_ID 被更新了
点击按钮 1e371487-47cb-4daf-8e04-cfef94b09800: {'text': '12'}
RERUN c62c4d41-213d-4d9f-aa6d-d37597360f3d: {'text': ''}
FINISH_RUN c62c4d41-213d-4d9f-aa6d-d37597360f3d: {'text': ''}
```
69 changes: 50 additions & 19 deletions _drafts/2024-10-09-gguf.md
Original file line number Diff line number Diff line change
Expand Up @@ -1146,25 +1146,25 @@ def my_dequant(blocks):
b48_64_3 = b_3[48:64]

result = np.array([
c[0] * ((b0_16_0<<4)+a0_16_low-32),
c[1] * ((b16_32_0<<4)+a16_32_low-32),
c[2] * ((b0_16_1<<4)+a32_48_low-32),
c[3] * ((b16_32_1<<4)+a48_64_low-32),
c[0] * ((b0_16_0<<4)+a0_16_low-32).view(np.int8),
c[1] * ((b16_32_0<<4)+a16_32_low-32).view(np.int8),
c[2] * ((b0_16_1<<4)+a32_48_low-32).view(np.int8),
c[3] * ((b16_32_1<<4)+a48_64_low-32).view(np.int8),

c[4] * ((b0_16_2<<4)+a0_16_high-32),
c[5] * ((b16_32_2<<4)+a16_32_high-32),
c[6] * ((b0_16_3<<4)+a32_48_high-32),
c[7] * ((b16_32_3<<4)+a48_64_high-32),

c[8] * ((b32_48_0<<4)+a64_80_low-32),
c[9] * ((b48_64_0<<4)+a80_96_low-32),
c[10] * ((b32_48_1<<4)+a96_112_low-32),
c[11] * ((b48_64_1<<4)+a112_128_low-32),

c[12] * ((b32_48_2<<4)+a64_80_high-32),
c[13] * ((b48_64_2<<4)+a80_96_high-32),
c[14] * ((b32_48_3<<4)+a96_112_high-32),
c[15] * ((b48_64_3<<4)+a112_128_high-32),
c[4] * ((b0_16_2<<4)+a0_16_high-32).view(np.int8),
c[5] * ((b16_32_2<<4)+a16_32_high-32).view(np.int8),
c[6] * ((b0_16_3<<4)+a32_48_high-32).view(np.int8),
c[7] * ((b16_32_3<<4)+a48_64_high-32).view(np.int8),

c[8] * ((b32_48_0<<4)+a64_80_low-32).view(np.int8),
c[9] * ((b48_64_0<<4)+a80_96_low-32).view(np.int8),
c[10] * ((b32_48_1<<4)+a96_112_low-32).view(np.int8),
c[11] * ((b48_64_1<<4)+a112_128_low-32).view(np.int8),

c[12] * ((b32_48_2<<4)+a64_80_high-32).view(np.int8),
c[13] * ((b48_64_2<<4)+a80_96_high-32).view(np.int8),
c[14] * ((b32_48_3<<4)+a96_112_high-32).view(np.int8),
c[15] * ((b48_64_3<<4)+a112_128_high-32).view(np.int8),
]) * d

return result
Expand Down Expand Up @@ -1246,7 +1246,38 @@ float make_qx_quants(int n, int nmax, const float * x, int8_t * L, int rmse_type
mkdir build && cd build && cmake ../ && cmake --build . && ./bin/learn
```

ps: 计划给 llama.cpp 提 PR, 增加 Q6_K 的量化算法, 需要对齐 C 的实现
任务 2:

`ggml/src/ggml-quants.h` 中, 有如下关于 `q6_K` 的声明

```c
void quantize_row_q6_K_ref(const float * GGML_RESTRICT x, block_q6_K * GGML_RESTRICT y, int64_t k);

void quantize_row_q6_K(const float * GGML_RESTRICT x, void * GGML_RESTRICT y, int64_t k);

void dequantize_row_q6_K(const block_q6_K * GGML_RESTRICT x, float * GGML_RESTRICT y, int64_t k);

size_t quantize_q6_K(const float * GGML_RESTRICT src, void * GGML_RESTRICT dst, int64_t nrows, int64_t n_per_row, const float * imatrix);
```
而在 `ggml/src/ggml-quants.c` 中, 除上面以外, 还包含:
```c
static void quantize_row_q6_K_impl(const float * restrict x, block_q6_K * restrict y, int64_t n_per_row, const float * quant_weights);
```

quantize 相关的 4 个函数调用关系如下:

- `quantize_q6_K` 条件调用 `quantize_row_q6_K_ref``quantize_row_q6_K_impl`
- `quantize_row_q6_K` 调用 `quantize_row_q6_K_ref`
- `quantize_row_q6_K_ref` 不调用其他
- `quantize_row_q6_K_impl` 不调用其他

从函数入参来看 `quantize_q6_K``quantize_row_q6_K` 的参数是基本类型的指针, 而 `quantize_row_q6_K_ref``quantize_row_q6_K_impl` 包含了自定义数据结构 `block_q6_K`



任务 4

原始的 [PR](https://github.com/ggerganov/llama.cpp/commit/4134999e01f31256b15342b41c4de9e2477c4a6c#diff-ce8ce4f4e23f467e525c2192d310d1530528facef89eafeedef4cb5574097d65R230), 对齐的验证方法如下(TODO:有些不优雅: `make libglmm.so`):

Expand Down
84 changes: 84 additions & 0 deletions _drafts/2024-11-14-numeric-representation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
layout: post
title: "(P0) 数值表示"
date: 2024-11-14 13:00:04 +0800
labels: [ieee754]
---


## 动机、参考资料、涉及内容

数值表示(整数,浮点数,大/小端序,字节对齐), 一些 Python/C++/Numpy 中关于数值类型的常用手段

## IEEE 754

IEEE 754 表示法的描述参见 CSAPP, 简要描述如下:

浮点数的表示方法为:$V=(-1)^s\times M \times 2^E$, 其中 $M$ 表示 $[0, 2-\epsilon]$ 中的一个数, $E$ 表示一个整数, $s$ 表示 0(正数) 或 1(负数)。

以 float32 为例, 具体的编码方式为:

- 第 1 位表示符号
- 接下来的 $k=8$ 位 $exp = e_7e_6...e_0$ 表示指数 $E$ (exponent)
- 最后的 $n=23$ 位 $frac = f_{22}f_{21}f_0$ 表示系数 $M$ (significand)

具体的编码方式如下, 分为 3 类情形:

**1. Normalized values**

当指数位不为全0或全1时,属于此类。这种情况下,$E=exp-Bias$,$M=1+0.f_{23}f_{22}...f_{0}$。其中 $Bias=2^{k-1}-1=2^7-1=127$。

**2. Denormalized values**

当指数位全为0时,属于此类。这种情况下,$E=1-Bias=-126$, $M=0.f_{23}f_{22}...f_{0}$。

这类编码方式主要有两个目的:一是可以表示出0:+0.0被编码为全0, -0.0被编码为第一位是1,其余位全为0;二是可以表示数十分接近0的数字

**3. Special values**

当指数位全为1时,属于此类。当 $frac$ 全为 0 时,代表 $-\inf$ (如果符号位为1) 或 $+\inf$ (如果符号位为1);如果 $frac$ 不全为0,则代表 $NaN$

**总结**

从表示范围来看,三类值如下分布(一个数的正数表示与负数表示只相差符号位)

正数:
- +0.0 (denormalized): $s=0$, $exp=00000000$, $frac=00...00$
- 最小正数 (denormalized): $\epsilon=2^{-2^{k-1}+2-n}=2^{-126-23}$, $s=0$, $exp=00000000$, $frac=00...01$
- ... (denormalized)
- 最大 denormalized 正数 (denormalized): $2^{-126} - \epsilon$, $s=0$, $exp=00000000$, $frac=11...11$
- 最小 normalized 正数 (normalized): $2^{-2^{k-1}+2}=2^{-126}$, $s=0$, $exp=00000001$, $frac=00...00$
- ... (normalized)
- 最大正数 (normalized): $(2-2^{-n})\times2^{(2^{k-1}-1)}=(2-2^{-23}) \times 2^{127}$, $s=0$, $exp=11111110$, $frac=11...11$
- 正无穷 (special): $s=0$, $exp=11111111$, $frac=00...00$
- NaN: $s=0$, $exp=11111111$, $frac\neq 00...00$

这样我们知道:

- fp64: $k=11$, $n=52$, 最大正数为 $2^{1024}-2^{971}$, 最小正数为 $2^{-1074}$
- fp32: $k=8$, $n=23$, 最大正数为 $2^{128}-2^{104}$, 最小正数为 $2^{-149}$
- fp16: $k=5$, $n=10$, 最大正数为 $2^{16}-2^3=65536-32=65504$, 最小正数为 $2^{-24}$

例子:
```
1的表示: (1+0.0)*2^0: 0 01111111 000...000
2的表示: (1+0.0)*2^1: 0 10000000 000...000
5/2的表示: (1+1/4)*2^1: 0 10000000 010...000
```

## np.frombuffer

```python
np.frombuffer(b"\x00\x00\x80\x3f\x00\x00\x20\x40", np.float32) # [1.0, 2.5]

# 每4位为一组:
# \x00: 00000000, \x00: 00000000, \x80: 10000000, \x3f: 00111111
# 倒序拼接:
# 00111111 10000000 00000000 00000000
# 然后解码为fp32: 1.0

# \x00: 00000000, \x00: 00000000, \x20: 00100000, \x40: 01000000
# 倒序拼接:
# 01000000 00100000 00000000 00000000
# 然后解码为fp32: 2.5
```

0 comments on commit 642e3ef

Please sign in to comment.