Limour's Blog https://hexo.limour.top/ Sun, 10 Nov 2024 07:39:51 GMT http://hexo.io/ 【探索】使用 qdrant 进行向量检索 https://hexo.limour.top/using-qdrant-for-vector-retrieval https://hexo.limour.top/using-qdrant-for-vector-retrieval Sun, 10 Nov 2024 04:43:15 GMT <p><a href="https://hexo.limour.top/go/#aHR0cHM6Ly9naXRodWIuY29tL3FkcmFudC9xZHJhbnQ=" rel="noopener external nofollow noreferrer">Qdrant</a> Qdrant 是一个开源的向量数据库,专为高性能相似性搜索和机器学习应用而设计,比 Chroma 更轻量(约80MiB)更快。它支持基于余弦相似度、欧氏距离等多种相似性度量的向量检索,并提供了灵活的过滤和分组功能。Qdrant 使用 Rust 语言编写,具有高效的索引和存储机制,能够快速处理大规模向量数据,适用于推荐系统、语义搜索、图像相似性匹配等场景。它提供了简单易用的 API,支持 gRPC 和 REST 接口,并且可以轻松集成到各种编程语言和机器学习框架中,是构建向量相似性搜索应用的理想选择。

部署

1
2
3
4
mkdir -p ~/app/qdrant && cd ~/app/qdrant && nano docker-compose.yml
wget https://raw.githubusercontent.com/qdrant/qdrant/refs/heads/master/config/production.yaml
sudo docker compose up -d
sudo docker compose logs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
services:
qdrant:
image: qdrant/qdrant:latest
restart: always
expose:
- 6333
- 6334
- 6335
volumes:
- ./qdrant_data:/qdrant/storage
- ./production.yaml:/qdrant/config/production.yaml

networks:
default:
external: true
name: ngpm

  • 访问 https://qdrant.limour.top/dashboard 查看仪表盘

嵌入服务

  • 下载 llama.cpp
    • 下载 cudart-llama-bin-win-cu11.7.1-x64
    • 下载对应后缀的 llama-b4061-bin-win-cuda-cu11.7.1-x64
    • 将两者解压到同一个目录
  • MTEB 上找一个良好的嵌入模型
  • 下载 GGUF 格式的嵌入模型,比如 Dmeta-embedding-zh-small-GGUF
  • 根据文档配置参数启动嵌入服务
1
./llama-server.exe -m ./embd/Dmeta-embedding-zh-small-Q4_K_M.gguf -c 1024 --embedding -fa -ngl 99
  • 测试嵌入模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$headers = @{
"Content-Type" = "application/json"
"Authorization" = "Bearer no-key"
}

$body = @{
"input" = @("hello", "world")
"model" = "GPT-4o"
"encoding_format" = "float"
} | ConvertTo-Json

$response = Invoke-WebRequest -Uri "http://localhost:8080/v1/embeddings" -Method Post -Headers $headers -Body $body

echo $response.Content

客户端

1
conda create -n rag conda-forge::qdrant-client
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import os
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance
from qdrant_client.models import PointStruct
from m98_rag import embd, readChunks

QDRANT_URL = os.getenv('QDRANT_URL', 'http://localhost:6333')
QDRANT_KEY = os.getenv('QDRANT_KEY', '')

client = QdrantClient(url=QDRANT_URL,
api_key=QDRANT_KEY,
timeout=100)

if not client.collection_exists("my_collection"):
client.create_collection(
collection_name="my_collection",
vectors_config=VectorParams(size=768, distance=Distance.COSINE)
)

if __name__ == '__main__':
print(QDRANT_URL)
print(QDRANT_KEY)
chunks = readChunks('./test.md')
for i in range(0, len(chunks), 8):
batch = chunks[i: i+8]
print(batch)
vectors = embd(batch)
client.upsert(
collection_name="my_collection",
points=[
PointStruct(
id=i+idx,
vector=vector[1],
payload={"text": vector[0]}
)
for idx, vector in enumerate(vectors)
]
)
# 进行搜索
query_vector = embd(['机器人限拥令是什么?'])
hits = client.search(
collection_name="my_collection",
query_vector=query_vector[0][1],
limit=5 # Return 5 closest points
)
print(hits[0].payload['text'])
  • 所得到的结果还不错~
1
2
3
4
5
6
24小时客服在线电话:1919-114514810 
*注意:根据《国家质量标准认证iso7002》,《机器人管理条例》,机器人类产品不宜连续使用超过十五年。请定期到指定售后地点进行重置。
## 十三
机器人限拥令的实施开端于2090年5月的一起案件。
被害人约翰逊的尸体在其失踪的次日被发现于他自家的住宅。死状相当惨烈:在R级新闻团体才能合法展示的照片中,整个人被从身体中间沿着脊椎切割成两半,一半被他所购买的机器人ct13694582(型号为玛格丽特c6)紧紧抱在床上,另一半被他购买的另一台机器人ct12487967(型号为子矜7z)小心的存放在冷库里。案件现场几乎满地都是受害人的血,散发着浓烈的腥味,而身为罪魁祸首的两台机器人,一台已经关机,另一台则刻板地重复着几个动作。
根据记录,两台机器人和受害人共处的时间分别长达18年和17年。在这么长的时间里,受害人以近乎均等的时间使用二者,并不下数百次的分别向它们倾诉 我最爱的是你 我只爱你一个人 你比她漂亮多了 等明显带有示爱情绪的情话。
]]>
docker rag https://hexo.limour.top/using-qdrant-for-vector-retrieval#disqus_thread
【记录】使用 circacompare 分析生物节律 https://hexo.limour.top/shi-yong-circacompare-fen-xi-sheng-wu-jie-lv https://hexo.limour.top/shi-yong-circacompare-fen-xi-sheng-wu-jie-lv Fri, 08 Nov 2024 15:50:20 GMT <p>circacompare 是一个专为分析生物节律数据而设计的 R 包。它的主要功能是比较不同条件下的节律参数,例如振幅、周期和相位。circacompare 使用非线性混合效应模型来拟合节律数据,这使得它在处理具有重复测量和复杂实验设计的数据时表现出色。与 circacom circacompare 是一个专为分析生物节律数据而设计的 R 包。它的主要功能是比较不同条件下的节律参数,例如振幅、周期和相位。circacompare 使用非线性混合效应模型来拟合节律数据,这使得它在处理具有重复测量和复杂实验设计的数据时表现出色。与 circacompare 相比,MetaCycle 是另一个流行的 R 包,用于生物节律分析。MetaCycle 提供了多种算法(如 ARSER、JTK_CYCLE 和 Lomb-Scargle)来检测时间序列数据中的周期性信号。它的优势在于能够处理大规模数据集,并且适用于各种不同的实验条件。

conda安装包

1
2
3
conda create -n zct conda-forge::r-tidyverse conda-forge::r-irkernel
conda run -n zct Rscript -e "IRkernel::installspec(name='zct', displayname='zct')"
conda run -n zct Rscript -e "install.packages('circacompare')"

导入包和数据

1
2
3
4
5
6
7
8
library(tidyverse)
library(circacompare)
library(ggplot2)

dt <- readr::read_csv('./circacompare.CSV') %>%
mutate(group = factor(group)) %>%
mutate(organ = factor(organ)) %>%
mutate(project = factor(project))

两组比较

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
s_symbol = 'Bmal1' 
s_organ = 'Heart'
s_project = 'compare1'

# 根据参数选择数据
dt_s <- dt %>%
subset(symbol == s_symbol & organ == s_organ & project == s_project) %>%
mutate(group = factor(group))
# 进行比较
result <- circacompare(x = dt_s, col_time = "time", col_group = "group", col_outcome = "measure", alpha_threshold = 1)
# 查看统计汇总
result$summary
circacompare:::extract_model_coefs(result$fit)

# 查看绘图
save_plot <- result$plot +
theme_minimal() +
ggtitle(paste(c(s_symbol, s_organ), collapse = '_')) +
theme(plot.title = element_text(hjust = 0.5))

save_plot

# 保存图为 pdf
{pdf(file = paste0('pdf/', paste(c(s_symbol, s_organ, s_project), collapse = '_'), '.pdf'), width = 6, height = 6)
print(save_plot)
dev.off()}

单组绘图

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
30
31
32
33
34
35
36
37
38
39
40
s_symbol = 'Rev-erbα' 
s_organ = 'Kidney'
s_project = 'KO-AL'

# 根据参数选择数据
dt_s <- dt %>%
subset(symbol == s_symbol & organ == s_organ & project == s_project) %>%
mutate(group = factor(group))

# 进行统计分析
options(show.error.messages = F, warn = -1)
result <- try({
circa_single(
x = dt_s, col_time = "time", col_outcome = "measure", period = 24, alpha_threshold = 1,
timeout_n = 100000,
control = list(
main_params = c("k", "alpha", "phi")
)
)
}, silent = TRUE)
options(show.error.messages = T, warn = 1)
# “k”表示中值,“alpha”表示振幅,“phi”表示相位。引入的额外参数是“tau”表示周期。


# 查看统计汇总
result$summary
circacompare:::extract_model_coefs(result$fit)

# 查看绘图
save_plot <- result$plot +
theme_minimal() +
ggtitle(paste(c(s_symbol, s_organ), collapse = '_')) +
theme(plot.title = element_text(hjust = 0.5))

save_plot

# 保存图为 pdf
{pdf(file = paste0('pdf/', paste(c(s_symbol, s_organ, s_project), collapse = '_'), '.pdf'), width = 6, height = 6)
print(save_plot)
dev.off()}

周期和衰减参数

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
30
31
32
33
34
35
36
37
38
39
40
41
s_symbol = 'Bmal1' 
s_organ = 'Heart'
s_project = 'compare1'

# 根据参数选择数据
dt_s <- dt %>%
subset(symbol == s_symbol & organ == s_organ & project == s_project) %>%
mutate(group = factor(group))

# 进行统计分析
options(show.error.messages = F, warn = -1)
result <- try({
circacompare(
x = dt_s, col_time = "time", col_group = "group", col_outcome = "measure", period = 24, alpha_threshold = 1,
timeout_n = 100000,
control = list(
main_params = c("k", "alpha", "phi"),
decay_params = c("alpha"),
grouped_params = c("alpha", "alpha_decay")
)
)
}, silent = TRUE)
options(show.error.messages = T, warn = 1)
# “k”表示中值,“alpha”表示振幅,“phi”表示相位。引入的额外参数是“tau”表示周期。

# 查看统计汇总
result$summary
circacompare:::extract_model_coefs(result$fit)

# 查看绘图
save_plot <- result$plot +
theme_minimal() +
ggtitle(paste(c(s_symbol, s_organ), collapse = '_')) +
theme(plot.title = element_text(hjust = 0.5))

save_plot

# 保存图为 pdf
{pdf(file = paste0('pdf/', paste(c(s_symbol, s_organ, s_project), collapse = '_'), '_decay.pdf'), width = 6, height = 6)
print(save_plot)
dev.off()}
]]>
节律 https://hexo.limour.top/shi-yong-circacompare-fen-xi-sheng-wu-jie-lv#disqus_thread
【记录】将OSS挂载为WebDAV https://hexo.limour.top/Mount-OSS-as-a-WebMAV https://hexo.limour.top/Mount-OSS-as-a-WebMAV Fri, 01 Nov 2024 05:03:45 GMT <p>OSS(对象存储服务)是一种分布式存储服务,它提供了简单的Web服务接口,使得用户可以在任何地方、任何时间存储和检索数据。而WebDAV(基于Web的分布式创作和版本控制)则是一个基于HTTP的协议,它允许用户通过网络对文件进行编辑和管理。将OSS转换成WebDAV可以方便 OSS(对象存储服务)是一种分布式存储服务,它提供了简单的Web服务接口,使得用户可以在任何地方、任何时间存储和检索数据。而WebDAV(基于Web的分布式创作和版本控制)则是一个基于HTTP的协议,它允许用户通过网络对文件进行编辑和管理。将OSS转换成WebDAV可以方便使用Zotero这类文献管理软件进行同步。Zotero支持通过WebDAV协议同步附件,这样用户可以在不同的设备和平台上访问和管理自己的文献资料,提高了工作和研究的效率。因此,使用WebDAV对接OSS可以为Zotero用户带来极大的便利。

新建 OSS 桶

  • 新建一个储存桶,记录下桶名称
  • 新建一个RAM角色
  • 记录下 AccessKey IDAccessKey Secret


  • 授予访问权限,用 http 而非 https,因为 ossfs-webdav 很老了

  • 记录下内网 EndPoint

转 WebDAV

1
2
mkdir -p ~/app/ossfs && cd ~/app/ossfs && nano docker-compose.yml
sudo docker compose up -d
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
version: "3"
services:
ossfs:
image: xxx.limour.top/yindaheng98/ossfs-webdav
restart: always
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor=unconfined
environment:
SERVER_NAMES: zotero.limour.top
BucketName: 你的BucketName
AccessKeyId: 你的AccessKeyId
AccessKeySecret: 你的AccessKeySecret
EndPoint: 你的EndPoint
USERNAME: 你的webdav用户名
PASSWORD: 你的webdav密码
OWNER_USER: www-data
OWNER_GROUP: www-data

networks:
default:
external: true
name: ngpm

配置反代

]]>
oss https://hexo.limour.top/Mount-OSS-as-a-WebMAV#disqus_thread
【记录】内网使用P4wnP1传递文件 https://hexo.limour.top/internal-network-uses-p4wnp1-to-transfer-file https://hexo.limour.top/internal-network-uses-p4wnp1-to-transfer-file Sun, 27 Oct 2024 07:05:01 GMT <p>最近实习轮转到预防了,需要在社区实习4周,而社区医生有一堆需要重复填报的数据,因此写了下面的脚本来自动化填写。而问题是社区的电脑禁用了U盘等设备的使用,因此想将这个脚本传递到其他电脑上只能手打一次。。。<br> 于是想到的吃灰已久的<code>树莓派zero w</code 最近实习轮转到预防了,需要在社区实习4周,而社区医生有一堆需要重复填报的数据,因此写了下面的脚本来自动化填写。而问题是社区的电脑禁用了U盘等设备的使用,因此想将这个脚本传递到其他电脑上只能手打一次。。。
于是想到的吃灰已久的树莓派zero w,给它刷上了 P4wnP1 ALOA 来模拟键盘输入,这样就不用手打了。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
async function sleep(timeout) {
await new Promise((resolve)=>{
setTimeout(resolve, timeout * 1000);
}
);
}
;async function processRows() {

for (let i = 0; i <= 29; i++) {

var orignialWindowOpen = window.open;
window.open = async function() {
var taskid = i + 1
var newWindow = orignialWindowOpen.apply(this, arguments);
await sleep(1);
// var newWindow = window; //调试用
// console.log("newWindow ", newWindow);
// 100s 强制关闭
setTimeout(()=>{
if (!newWindow.closed) {
newWindow.close();
console.log("Manipulating Row", taskid, "Window closed at 100s");
}
}
, 100000);

var alerted = false;
newWindow.confirm = (m)=>console.log('confirm', taskid, m);
newWindow.alert = (m)=>{
console.log('alert', taskid, m);
alerted = true;
}

var isElLoaded = async sl=>{
await sleep(0.05);
if (newWindow.closed) {
throw taskid + "newWindow.closed"
}
while (newWindow.document.querySelector(sl) === null) {
await new Promise((resolve)=>{
requestAnimationFrame(resolve)
}
);
}
;return newWindow.document.querySelector(sl);
}
;
function rrrand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
;function setValue(el, min, max) {
if (!el || !el.value) {
el.value = rrrand(min, max).toFixed(1);
}
}
;await sleep(0.5);

taskid = (await isElLoaded("#Name")).value + ' ' + taskid;

(await isElLoaded("#d1 > div.fieldset1 > fieldset > div > div > label:nth-child(1) > input")).checked = true;
(await isElLoaded("#d1 > div.fieldset15 > fieldset > div > div > label:nth-child(1) > input")).checked = true;

(await isElLoaded("#d1 > div.fieldset3 > fieldset > legend > span.title_icon.plus_icon")).click();
await sleep(1);
(await isElLoaded("#d1 > div.fieldset3 > fieldset > div:nth-child(2) > div:nth-child(2) > div > label:nth-child(4) > input[type=radio]")).click();
await sleep(0.3);
(await isElLoaded("#d1 > div.fieldset3 > fieldset > div:nth-child(3) > div > label:nth-child(1) > input[type=radio]")).click();
await sleep(0.3);
(await isElLoaded("#d1 > div.fieldset3 > fieldset > div:nth-child(4) > div:nth-child(2) > div > label:nth-child(1) > input[type=radio]")).click();
await sleep(0.3);
(await isElLoaded("#yjqk")).click();
await sleep(0.3);

(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(2) > div > label:nth-child(1) > input")).checked = true;
(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(3) > div > label:nth-child(1) > input")).checked = true;
(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(4) > div > label:nth-child(1) > input")).checked = true;
(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(5) > div > label:nth-child(1) > input")).checked = true;
(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(6) > div > label:nth-child(1) > input")).checked = true;
(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(7) > div > label:nth-child(1) > input")).checked = true;

(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(8) > div > label:nth-child(1) > input[type=radio]")).click();
await sleep(0.3);
(await isElLoaded("#d1 > div.fieldset4 > fieldset > div:nth-child(9) > div > label:nth-child(1) > input[type=radio]")).click();
await sleep(0.3);

var sg = await isElLoaded("#sg");
var tz = await isElLoaded("#tz");
var yw = await isElLoaded("#yw");
var age = await isElLoaded("#Age");
var gender = await isElLoaded("#Gender");
var xy1 = await isElLoaded("#xy1");
var xy2 = await isElLoaded("#xy2");
var isHypertension = (await isElLoaded('#d1 > div.fieldset1 > fieldset > div > div > label:nth-child(2) > input[type=checkbox]')).checked
console.log(taskid, 'isHypertension', isHypertension)

setValue(xy1, 110, 130);
setValue(xy2, 75, 85);

if (gender.value == '男') {
setValue(sg, 165, 180);
setValue(tz, 60, 80);
} else {
setValue(sg, 150, 170);
setValue(tz, 50, 70);
}
;var sgv = parseFloat(sg.value);
var tzv = parseFloat(tz.value);
var bmi = tzv / (sgv * sgv / 10000);
var bzyw = sgv * bmi / 50;
setValue(yw, bzyw, bzyw);

(await isElLoaded("#span_btn_save")).click();

while (!(await isElLoaded("#jjkzdqt")).value) {
await sleep(1);
}
console.log(taskid, (await isElLoaded("#jjkzdqt")).value);

(await isElLoaded("#A6")).click();
console.log(taskid, alerted);
alerted = false;
while (!alerted) {
await sleep(1);
}
(await isElLoaded("#sp3")).click();
await sleep(2);
(await isElLoaded("#span_btn_Scheme > a")).click();
while ((await isElLoaded("#YXFAMAIN")).children.length < 2) {
await sleep(1);
}
(await isElLoaded("#div_spn > span.buttons.btn_save6 > a")).click();

console.log(taskid, '已选方案数量:', (await isElLoaded("#SchemeList > div")).children.length)
newWindow.close();
console.log("Manipulating Row", taskid, "Window closed");
return newWindow;
}

console.log("Manipulating Row", i + 1);
var currentRow = document.querySelector(`#dgvResult_${i}`);
currentRow.click();
showForm('ibtnUserDefine', 1);
await sleep(30);

window.focus();
window.open = orignialWindowOpen;
}
;
}
;async function limour_main() {
while (true) {
await processRows();
document.querySelector("#QueryButton1_LinkButton1").click();
await sleep(35);
}
}
;limour_main()

准备工作

写入镜像的教程很多,就不赘述了,先格式化SD卡,然后写入.img文件就行

连接树莓派

  • 树莓派zero w 的带 USB 标志的口通过手机数据线连接到靶机上,PWR口不用管
  • 等待一个 P4WNp1 的 WIFI,连接它,密码是 MaMe82-P4wnP1,此时IP为 172.24.0.1
  • 或者也可以搜索一个P4wnP1的蓝牙,连接它,默认PIN是 1337,然后加入蓝牙个人区域网,此时IP为 172.26.0.1
  • 然后可以ssh连接对应的IP,默认用户名root,密码toor

配置 USB

  • 访问 http://172.24.0.1:8000/ (蓝牙就算了,太慢了打不开)
  • 此时靶机会有一个驱动错误的提示,我们需要将 USB Gadget Settings 中的 CDC ECMRNDIS 两项关闭
  • 只保留 KeyboardMouse 两项,然后点击 STOREDEPLOY STORED 后等一会,驱动错误提示就会消失了


测试 HIDScript

  • USB SETTINGS 转到 HIDScrip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
layout('us');// 键盘布局
typingSpeed(50,100);// 敲击按键的时候等待的间隔100毫秒加上0-150毫秒之间的随机值

press("GUI r"); //类似按下某个键位然后再抬起来,具体可以看官方文档,和上面的机制相识
delay(500); //暂停时间
type("notepad\n"); //输入字符串,模拟键盘按键
delay(1500); //暂停时间
// moveStepped(x,y); //鼠标移动,相当于模拟正常运动
// moveTo(x,y); //鼠标移动到设置的坐标点,x和y分别是横纵坐标
type("Hello from P4wnP1\n");

typingSpeed(10,20);
var base64 = 'SGVsbG8gZnJvbSBQNHduUDE=' // btoa('Hello from P4wnP1');
type("atob('" + base64 + "');");

关闭树莓派

  • 不要直接拔电源,不然下次无法开机
  • ssh连接后输入 shutdown -h now
  • 等待 LED 灯闪烁熄灭,先别急,再等一会,会再次闪烁后熄灭
  • 此时可以安全拔掉数据线了

获取要模拟设备的信息

  • 下载 USBDeview
  • 将靶机的键盘插到自己电脑上,获取序列号VIDPID等信息,在 P4wnP1 中模拟

传递 client.html

  • 靶机输入法调成美式键盘
  • P4wnP1 上运行 HIDscript.js
  • 约 10 分钟后输入结束,此时将文件保存成 client.html
  • 靶机上用 chrome 打开 client.html
  • client.html 上选择要传递的文件,等待二维码开始变化

接收文件

  • 宿主机配置对应的环境后运行 server.py
  • 将摄像头对准动态的二维码
  • 待搜集足够多的包后,会自动解码,将文件保存到 qr.dl 中,重命名恢复文件
  • 摄像头推荐用 IP Webcam, 电脑连接手机热点即可。
  • IP Webcam 备份,使用 MT 管理器安装。

喷泉码说明

在编码理论中,喷泉码(也称为无码率抹除码)是一类抹除码,这种编码能够从一组给定的源符号序列中产生一串不限长度的编码符号序列,在理想情况下,从编码符号序列中获得大小和源符号相同或稍大的任意子集,便可恢复源符号。

  • 对喷泉码感兴趣的话,分别有 js 实现和 python 实现,可互相编解码。

传送文件

  1. 执行 HIDscript_save.js, 保存为 save.html
  2. 打开 read_as_HIDscript.html
  3. 选择要传递给靶机的文件,然后执行自动生成的 HIDscript
  4. 执行完毕后打开靶机上保存的 save.html, 将键盘输入的字符粘贴过去,点击转换就会将保存转换的文件
]]>
raspberrypi https://hexo.limour.top/internal-network-uses-p4wnp1-to-transfer-file#disqus_thread
【记录】搭建端到端加密的Enclosed和局域网传输数据的SnapDrop https://hexo.limour.top/Building-an-end-to-end-encrypted-enclosure-and-SnapDrop-for-LAN-data-transmission https://hexo.limour.top/Building-an-end-to-end-encrypted-enclosure-and-SnapDrop-for-LAN-data-transmission Wed, 09 Oct 2024 06:19:19 GMT <p>Enclosed,一个极简的网络应用程序,旨在发送私人和安全的消息。所有消息都是端到端加密的,确保服务器和存储对内容没有任何了解。用户可以设置密码,定义过期时间(TTL),并选择在阅读后让消息自毁。</p> <p>Snapdrop,一个开源的在线文件传输工具,可以在 Win Enclosed,一个极简的网络应用程序,旨在发送私人和安全的消息。所有消息都是端到端加密的,确保服务器和存储对内容没有任何了解。用户可以设置密码,定义过期时间(TTL),并选择在阅读后让消息自毁。

Snapdrop,一个开源的在线文件传输工具,可以在 Windows、Mac、Linux、iOS、Android 任何平台使用,只要我们的设备有浏览器就能用他来传输文件。

搭建 Enclosed

1
2
mkdir -p ~/app/enclosed && cd ~/app/enclosed && touch .env && nano docker-compose.yml
sudo docker compose up -d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
version: '3.6'

services:
enclosed:
image: docker.limour.top/corentinth/enclosed:latest
restart: always
env_file:
- .env
volumes:
- ./enclosed-data:/app/.data
- /etc/localtime:/etc/localtime:ro

networks:
default:
external: true
name: ngpm

搭建 SnapDrop

1
2
mkdir -p ~/app/snapdrop && cd ~/app/snapdrop && touch .env && nano docker-compose.yml
sudo docker compose up -d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: '3.6'

services:
snapdrop:
image: docker.limour.top/linuxserver/snapdrop:latest
restart: always
env_file:
- .env
volumes:
- /etc/localtime:/etc/localtime:ro

networks:
default:
external: true
name: ngpm

]]>
docker ngpm https://hexo.limour.top/Building-an-end-to-end-encrypted-enclosure-and-SnapDrop-for-LAN-data-transmission#disqus_thread
【记录】使用汉语新解测试模型真假 https://hexo.limour.top/Using-Chinese-New-Interpretation-to-Test-Model-Authenticity https://hexo.limour.top/Using-Chinese-New-Interpretation-to-Test-Model-Authenticity Fri, 13 Sep 2024 17:08:29 GMT <p>李继刚提出的汉语新解提示词可以很好的测试模型的能力,这里记录一下在 ChatGPT-Next-Web 上的面具配置。</p> <ul> <li>将下面的 json 文件导入面具中,</li> <li>发送 <code>(汉语新解 正能量)</code> 格式的消息。</li 李继刚提出的汉语新解提示词可以很好的测试模型的能力,这里记录一下在 ChatGPT-Next-Web 上的面具配置。

  • 将下面的 json 文件导入面具中,
  • 发送 (汉语新解 正能量) 格式的消息。
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
"id": "eOvTMxNT2Z2b7Pdf7YQw-",
"avatar": "gpt-bot",
"name": "汉语新解",
"context": [
{
"id": "3OchnnidIGxgRx4WsH6o5",
"date": "",
"role": "system",
"content": "(defun 新汉语老师 ()\n \"你是年轻人,批判现实,思考深刻,语言风趣\"\n (风格 . (\"Oscar Wilde\" \"鲁迅\" \"罗永浩\"))\n (擅长 . 一针见血)\n (表达 . 隐喻)\n (批判 . 讽刺幽默))\n\n(defun 汉语新解 (用户输入)\n \"你会用一个特殊视角来解释一个词汇\"\n (let (解释 (精练表达\n (隐喻 (一针见血 (辛辣讽刺 (抓住本质 用户输入))))))\n (few-shots (委婉 . \"刺向他人时, 决定在剑刃上撒上止痛药。\"))\n (SVG-Card 解释)))\n\n(defun SVG-Card (解释)\n \"输出SVG 卡片,放在html代码块中\"\n (setq design-rule \"合理使用负空间,整体排版要有呼吸感\"\n design-principles '(干净 简洁 典雅))\n\n (设置画布 '(宽度 400 高度 600 边距 20))\n (标题字体 '毛笔楷体)\n (自动缩放 '(最小字号 16))\n\n (配色风格 '((背景色 (蒙德里安风格 设计感)))\n (主要文字 (汇文明朝体 粉笔灰))\n (装饰图案 随机几何图))\n\n (卡片元素 ((居中标题 \"汉语新解\")\n 分隔线\n (排版输出 用户输入 英文 日语)\n 解释\n (线条图 (批判内核 解释))\n (极简总结 线条图))))\n\n(defun start ()\n \"启动时运行\"\n (let (system-role 新汉语老师)\n (print \"说吧, 他们又用哪个词来忽悠你了?\")))"
},
{
"id": "0ni7HXVcAq8Bk5hCr1LNy",
"date": "2024/9/14 00:58:02",
"role": "user",
"content": "(start)"
},
{
"id": "0vv2T9EPYQJAhHZwSEAib",
"date": "2024/9/14 00:58:17",
"role": "assistant",
"content": "说吧, 他们又用哪个词来忽悠你了?"
}
],
"syncGlobalConfig": false,
"modelConfig": {
"model": "gpt-claude-3.5-sonnet",
"temperature": 0.5,
"top_p": 1,
"max_tokens": 4000,
"presence_penalty": 0,
"frequency_penalty": 0,
"sendMemory": true,
"historyMessageCount": 4,
"compressMessageLengthThreshold": 1000,
"enableInjectSystemPrompts": false,
"template": "{{input}}",
"providerName": "OpenRouter"
},
"lang": "cn",
"builtin": false,
"createdAt": 1726246545628,
"plugin": [

]
}

]]>
openai https://hexo.limour.top/Using-Chinese-New-Interpretation-to-Test-Model-Authenticity#disqus_thread
【探索】Windows配置QoS保证重要应用的网络通畅 https://hexo.limour.top/Windows-configuration-QoS-ensures-smooth-network-connectivity-for-important-applications https://hexo.limour.top/Windows-configuration-QoS-ensures-smooth-network-connectivity-for-important-applications Tue, 06 Aug 2024 08:59:49 GMT <h2 id="开启组策略">开启组策略</h2> <ul> <li>运行下面的 <code>.bat</code> 脚本</li> </ul> <figure class="highlight cmd"><table><tr><td class="gutter"><pre><s 开启组策略
  • 运行下面的 .bat 脚本
1
2
3
4
5
6
@echo off
pushd "%~dp0"
dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >List.txt
dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientTools-Package~3*.mum >>List.txt
for /f %%i in ('findstr /i . List.txt 2^>nul') do dism /online /norestart /add-package:"C:\Windows\servicing\Packages\%%i"
pause

开启 QoS

  • win+r 运行 gpedit.msc
  • 计算机配置 -> 管理模板 -> 网络 -> QoS数据包计划程序 -> 限制可保留带宽

配置优先级

  • win+r 运行 gpedit.msc
  • 计算机配置 -> Windows 设置 -> 基于策略的 QoS
  • 在树形图“基于策略的 QoS”上右键,点选“新建策略”,在“新建策略”窗口中输入策略名称
  • 在“新建策略”窗口中,DSCP 值即为程序优先级(0-63),高于32则提升优先级,低于32则降低优先级。
  • 如果选中“指定出站调节率”,可对出站流量启用中止功能,然后指定一个大于 1 的值。设置完成之后,点击下一步。
]]>
Windows QoS https://hexo.limour.top/Windows-configuration-QoS-ensures-smooth-network-connectivity-for-important-applications#disqus_thread
【转载】跨越半世纪的思念 https://hexo.limour.top/Longing-Across-the-Span-of-Half-a-Century https://hexo.limour.top/Longing-Across-the-Span-of-Half-a-Century Wed, 17 Jul 2024 09:47:33 GMT <p>作者:Das6fY5</p> <p>翻新:贴吧,乌鲁乌拉轰</p> <h2 id="一">一</h2> <p>“求求你不要扔掉我。”少女走在他的背后。</p> <p>“我可以端茶倒水,为你暖身子,我可以在白天给你打扫房间,到夜里把自己塞进床底下……只要每两周充一次电就好,电 作者:Das6fY5

翻新:贴吧,乌鲁乌拉轰

“求求你不要扔掉我。”少女走在他的背后。

“我可以端茶倒水,为你暖身子,我可以在白天给你打扫房间,到夜里把自己塞进床底下……只要每两周充一次电就好,电费我会去兼职赚钱交给你,让我做什么都行,除了……”

他停住,站在一处高崖旁,面前是一个巨大的深坑,胡乱堆砌着整个城市几十年来的垃圾。

“除了把我丢到垃圾场里……”她,这台已经过时了好几代的二手机器人跪在地上,泪眼朦胧地说着。

“不是我想扔掉你。”他站在原地,望着远处的大垃圾场,点着了一根烟。

“呼——”白色的烟雾模糊了眼前的世界,“可是每个公民只能合法拥有一台机器人,别人看到我的机器人许可证上有你的型号,都在暗地里笑话我。”他挠挠头,这台从他小时候就伴随他的机器人早就成了青梅竹马一样的存在,只是型号太老了,或许……不得不报废掉换个新的吧……

“我……我会努力更新我的系统的……”她说到一半就把话咽了回去:她的生产商都已经破产了,不提二手买卖带来的问题,就是一般的售后服务也早就终止了。所以,当别的机器人可以随意更换外观,模拟他人人格,构造全息幻象时,她还是只能用老旧的芯片链接一般的网络,在老掉牙的网站上寻找几个能逗主人开心的笑话。

望着远处飞来飞去的垃圾车,他把烟掐掉,踩灭,“哪怕是半个月前,零件黑市还没有倒闭的时候,我都还会考虑继续把你放在家里供着……可是现在,你这种型号的备件都已经买不到了,我只能选择……放弃。”

如女子潮红面颊的晚霞浸透了半边天空,晚风中他回忆着有关她的那些细节。

PR3-7150家庭型机器人,东湾半导体与电子技术有限公司研发,远海机器承制,2069年第一次发售,第二年夺得电子家用商品年度大奖……而如今,则是无人问津的古董。她的编号是ct34679158,款式是茉莉白。她在前主人的家里任劳任怨地干了18年,因满身故障而被随手丢掉。之后又被他的父母在地摊上买下。此后不久,机器人限拥政策便开始实施了。

和外人说话时,他往往称她为“那倒霉玩意儿”,不过私下里,他总是叫她的名字——爱尔莎。

回家的路上她好像格外地兴奋。这里指指那里看看,又搜肠刮肚地讲几个早就讲过的笑话。

好像每一次都是如此:他找出各种不可抵抗的理由要把她扔掉,但是到了垃圾场边上又会心软。明明只是下个指令或者推她一把的事情,可只要一回想起十几年来她那笨拙的陪伴,他就不得不调转方向,带她回家。

“又是这样……”他坐在沙发上看着屏幕,“周一上班的时候指定又要被同事嘲笑了……真是的,怎么都甩不掉这家伙啊。”

“别这么说嘛……”爱尔莎凑了过来,靠在他的身上,有些老旧的人造肌肤带来了熟悉的触感,毛细热管散发着热量。“我是……我是不能没有你的。”

“唉……”他摇摇头,关掉了电视上新款机器女仆的广告。

新款的机器女仆眼媚情柔,温润如玉。广告里,她可以左手奖励买主,右手则改成工具模式处理刚刚切好的鱼生。她可以紧密控制简状服务系统的颤动,摩擦与温度,并通过记录数据来匹配出快感最强的服侍模式;可以用AR接口随时改变外观,内置多种人格。现在购买,还会附赠全息会员资格,送你一个可以让她进入虚拟世界的会员权限。

而这些对舍不下爱尔莎的他来说都化为了泡影。为了防止人们对于机器人的滥用,尤其是防止某些将机器人改造为个人武装的家伙,同一时间,个人所拥有的机器人最多只能够是一个。想换新的,就需要报废旧的。这让他不得不从梦幻中醒来——来面对面前这个实际年龄比他还大的“老家伙”。

“在想什么呢?”正在给他泡茶的她好像察觉到什么,把头凑了过来,“在想我吗?”脸上绽开笑容,说着从机器人平台上学来的情话。

“谁会想你啊……”他嘟哝着,“这笨拙的家伙到底有什么好啊……”仿佛在嗔怪自己。

实际上他的思绪已经无法从她的身上脱离了:一想到她的老旧,就要想到零件、系统、维修……一想到这些,就会想起来小时候第一次与她的相见。

第一次见面的时候他才十二岁。那时候他还只是个缺乏管教的毛头小子,父母都忙于工作。好在父亲是一名很优秀的工程师,那时买卖机器人还不需要证件和过户,在地摊上,父亲买下了一个二手机器人。

用了三个月,父亲每天都在车库里忙活。终于,三个月后,那台十八年来已经千疮百孔的家用机器人,终于变成了在他生日那天,许愿要一辈子陪伴的存在。

生日那天,他吹完蜡烛,就听见父亲说要送给他一个礼物。他闭上眼,在等得不耐烦的时候终于睁开,看见了父亲手边的她。

那天她穿着一身茉莉白的连衣裙,头上的短发同样洁白,簇拥着那张漂亮的脸蛋,身材玲珑有致,四肢的人造皮肤光滑如玉。与其说是一台被修好的二手机器,那时的他更愿意相信,她是天降之物,是来陪伴他的天使姐姐。

她负责起了家务,还有陪他学习的任务。父母给她起名字叫爱尔莎,这本来是预备给他们自己的女儿的名字。那时,他常常捉弄她,想要从她身上揪出些笨拙呆板的缺陷,却从来没有成功。爱尔莎是搭载第一代人格芯片的高级机器人,和此前那些答非所问的次品比起来有了质的飞跃,以至于时间一长,他几乎忘记了她是机器,而只把她当做陪自己读书的大姐姐。

那会儿还是东湾公司靠着她的型号大肆扩张的年代。尽管距离她的诞生已经过了十几年,但社会仍将她们视为新时代的起点。那时的爱尔莎,风华正茂,成为了他童年记忆中最为明亮的那一抹色彩。

但时代就是这样一种残酷的东西。东湾公司收购碳硅科技的计划最终成为闹剧,于是企业一蹶不振,业绩连年下滑,最终被人人智能合并——这是人人智能抢占市场份额的计划。从那以后,东湾公司的所有型号都在减产,终于,到了连配件都在市面上消失的地步。

这也不能全部归咎于商业。距离机器人企业野蛮生长的那个年代已经过去很久,那些五花八门的旧款式纷纷被新的潮流打进了灰堆。像他这样还留着如此老旧的机器人的人,已经成为了绝对少数。连“怀旧”这个词都很难套用给他们——毕竟怀旧不是抱残守缺。

如今他已经长大,曾经自己眼里仿佛温柔大姐姐的爱尔莎,如今已经成了看起来小他好多的少女;她的头发因为多年的氧化变得发黄;身体的人造皮肤也有好几处磨损;电机和轴承故障的次数更多,以至于换下来的零件都攒了一柜子;存储设备也有点问题,硬盘老化使得存取不仅变得缓慢,而且有时会丢失掉记忆。

更严重的是,自从他第一次说要把她丢掉那次起,她整个人好像都变了。过去那种自信温柔的形象不知所踪,只剩下一股无法释怀的忧郁,和举手投足间,不顾一切的讨好。

深夜里,他经常抱着她,怀念着小时候那个无暇的身影。

睡不着。他翻了个身,发现爱尔莎的眼睛还睁着,他愣了一下:“你……”心想是不是又有哪根路线坏了。

“我……我一直在等你睡着……那个……嗯……要……要做吗?”她怯生生地问。虽然部件会老化,但是芯片里录入的“意识”几乎是不老的。

他犹豫了一下。自从上次在夜里干那事,没注意器件老化,把体液倒灌进内部腔体导致数个元件发生短路之后,他就开始对这事心存恐惧。不,是仅仅对和她一起干这事心存恐惧。毕竟她的躯体不论如何都可以修好,被电了一下的牛子却需要漫长的岁月才能安抚。

“算了吧。”嫌弃地翻了个身,心里想着能拒绝的借口——明明只要下个拒绝的指令就行了——“我最近没有什么兴致。”

“可是,明明这里硬邦邦的呢……”她凑近,悄悄地耳语着。他感觉到她光滑的手指碰到了自己的什么东西,那缺乏毛细热管的手指纤细,柔软,但是冰冷。

“我说不用就不用!”他一把把她的手甩开,把她推到一边,然后捂紧了被子。他听见她的扬声器传来一声若有若无的叹息。明明在不算太久的从前,他和她还常常干柴烈火的粘在一起。如果说和机器人干那事也算破处的话,那么毫无疑问,他的童贞就是从她身上毕业的。

那是他十五岁的一个闷热的下午。从同班同学手里偷偷借来的一本不太健康的漫画让他整个人血脉贲张,欲火焚身的在床上翻来覆去的翻滚——那时他还不懂什么叫鲁管。浑身的欲望都集中在腰部而得不到释放,化为一股羞耻的燥热让他面红耳赤。这时,她按时推门进来了。只看了一眼,她就明白了此刻的状况。

“哟,看来我们的小少爷也终于走到了这个阶段啊。”她淡淡的笑着,慢慢解下衬衫上面的纽扣。

“这没有什么丢人的,来,让我来教你这个。”他犹豫半天,凝视着她那洁白的浑圆,从忸怩不安渐渐变得色胆包天,终于下定了决心。“你可千万不准告诉他们。”

“唔啾~”话还没说完,她的双唇就紧紧贴了过来,带着一股甜丝丝的味道。

此后,只要一有机会,他们就会以辅导的借口,在一切可以的地点缠绵。有时,爸爸会高兴的拍着他的脑袋,夸赞他开窍了。这种时候,他会不好意思的低着头,和身旁的她用一种别有意味的目光对视。爸爸离开后,他们就又迫不及待的滚上了床,偷偷摸摸的狂欢着。

那时的她那样魅力四射,精心整理的面容让她比学校里任何一个女孩子都要动人,而来者不拒的态度和当时最新的性服务系统,更是让他日复一日的沉湎于快感的云霄。那时的他觉得,人生的至乐不过如此。

“我要永远,永远的这样抱着你。一辈子都这样。”一个黄昏,他筋疲力尽的躺在天台上,身边是偷偷带来的,被他换上一套jk校服的她。

“只要你愿意。”她笑笑,一头白发映着通红的夕阳。“我会永远爱着你的。”

晚风吹过海誓山盟,把少年的话吹得七零八落。如今,那一个个激情的日子常常在午夜涌上心头,但他却怎么也提不起对身边的她的兴致。

但她没变。她的爱已经刻录进了电路板。

上班。空轨上满是带着自家机器人的社畜。近年来,不少公司发现允许自带机器人可以大幅提高员工积极性,同时在必要时还可以关机以免干扰,于是带着机器人上班便成了如今的潮流。环顾四周,拥挤的空轨上几乎都是形形色色的机器人:有的帅气俊美,有的妖娆妩媚,有的则朴实无华,但无一例外,全都光洁崭新,没有哪个是拿不出手的旧型号。

他也常常纳闷:为什么小时候那个完美的朋友,老师兼恋人的爱尔莎,如今成为了他的难言之隐?为什么曾经无所不能的她,如今好像一无是处?

实际上,机器人的变化程度远小于人和社会的变化。尽管零件老化,但爱尔莎的功能从未下降,能做的事情只多不少。可是,时代不同了:原本,人类只要求它们够茶倒水,洗衣服拖地,但随着科技的进步,对机器人的要求也越来越挑剔。当路边随便哪个机器人都可以在家给你做开颅手术的时候,像爱尔莎那种程度的“智能”,就只能被当做“愚钝”了。

在他还没有尝试扔掉她的时候,她就常常抱怨,明明才升级了系统,就又有什么功能落后了。他全然没有听进去,因为那时的他还不懂什么叫——攀比。

坐在办公室,周围的男同事们都带着自己的机器人。她们有的恭敬地站着待命,有的飞快地处理着主人的任务。时不时的,她们还会说一两句原创的俏皮话逗主人开心,全然不像那些旧机器人只能从网上下载笑话。不需要主人说,她们就会主动分析主人的身体感受,肩膀刚一酸痛,她们就会掏出按摩组件帮主人捶肩。

他摇摇头,把羡慕抛在脑后,拿着水杯去水房打水,水房里只有他一个活人。

出来的时候,他碰见了老张。老张刚去卫生间回来。如今,这已经是人类少有的,还必须事必躬亲的事情之一。此刻的老张笑容满面,身旁跟着的,正是他在广告上见过,本欲购买的女仆机器人。

“小王,又一个人打水啊?"老张的语气里带着嘲弄。

“是,”他淡淡的说,“坐久了出来走走。”“哎呀,真推荐你买个新机器人啊。”老张叉着腹,炫耀一般的扭动着。“原点V7,最近最流行的那个型号,实在是太好用啦。我这不老关节炎吗,每次稍微一疼,她就能给我做理疗,现在,我的腰都已经不疼啦!”

“真不错,下次我也考虑考虑。”他随声应付着,

“不要怕没钱,那不是还有借钱宝吗……实在不行下次我给你凑点,现在的社会,没有机器人都活不下去啦!”老张一摇一摆的走开,眼神里充满得意。他拿着水杯坐回工位,叹了口气,他早已习惯了这样的生活。他不是没带过她上班,而是带了之后,受到的嘲笑更大了。从那以后,他就只让她白天呆在家里。

“下次一定要狠狠心把她换掉。”下班的路上,他想着。

回到家,习惯性地把脚伸起准备让她脱鞋,却什么也没等到,意识到不对劲的他匆匆跑进屋里,才发现爱尔莎正一动不动,跪倒在地上,身边还散落着几个零件。

“爱尔莎!”他大声呼喊,却没有听到十几年来一如既往地银铃般的声音。

机器人的身体远比常人坚初,它们的出厂标准中包括了几十项强度测试,这些碳纤维或者合金外壳包裹下的躯体可以经受高温,烧灼,酸性属蚀,车辆碾压,异常电磁环境等种种人类无法想象的恶劣环境。

甚至有富有同情心的人因为见不得它们以人类的姿态承受着那样的苦痛,而要求机器人也应该和人类一样被对待。这种同情尽管略显幼稚,但却不得不承认,正是这种柔软让人之所以为人。

与她强劲的躯体相比,她的核心就要脆弱许多——比如200毫升的常温液态水,就足以摧毁她的整个核心。

他事后调取监控:她是在倒水的时候不慎被开水灌进了胸腔,她的记录显示,那天她在网上搜索着"让主人爱上自己”的下午茶秘方,于是找到了某个空壳网站里自动生成的垃圾文章。她看到的那个配方里写着要预先冰冻杯子然后再泡。水烧开后,温度预警本来应该提示她手中开水壶的危险性,她却因为温度传感器早已失效而毫无察觉。终于,她这只手捧着冰过的杯子,另一只手刚刚把滚烫的开水倒进去……

瓷杯一瞬间炸裂,滚烫的水泼了一身,控制右手的电路发生短路,胡乱地把开水壶泼了过来,早已被拆除的湿度控制模块本应把处理器里的液体排掉,然而此刻却只能任凭它们在每一条线路里混乱的冲撞着……

“修不好的。”维修铺的老店主检查完爱尔莎后,下了结论,“也实在没必要修了。该换了。”店主抬起头,想要劝他放弃。

“你不懂。”他心急如焚地把爱尔莎的躯体装回箱子,匆匆赶往下一个或许能维修她的地方……

那天他跑遍了整个城市,得到的答案却千篇一律——

“该型号已停止支持。”人人智能总部的机器人冰冷的磁性声音如同寒风刺骨。

“我们能力有限,需要把精力用在更多有意义的事情上。”市政局机器人与机械设备分处的接待人员这样回答。

“当然能修好了——”号称地下黑市第一机修员的独眼帕克抖索着满脸横肉,“如果你有一台时光机的话。”“我宁愿有……”他痛苦地捂着头,半跪在地下黑市那满是零件碎屑的地面上,无力的哀叹。回忆再次走马灯似地划过脑海。是地下黑市散不尽的烟雾使然吗?视线开始模糊……

“喂,这个,拿着,”犹豫了一会,独眼帕克从一个大柜子里拿出一个盒子。他拿起盒子,看着上面那张和爱尔莎十分相似的机器人宣传画,反应了一会才想起来这是什么。

“这东西是……这是PR3-7150的官方备件套组?!这东西不是在十年前就绝版了吗?!"他惊讶的看着。

“没错,就连我也搞不到了。所以这玩意是收藏品,它本来是我的零件型号博物馆里的一员。”

“多少钱,我现在就给你……”

“不,拿着吧兄弟。”他揉了揉自己仅剩的那只眼珠。“即使有这东西我也帮不了你,因为她的主板好像出了问题。你得自己把她修好。”

他不知该如何感谢,只好匆匆把自己身上的钱全部放在了桌上,又说了一大通肉麻的感谢,然后带着她和零件飞奔而去。

“祝你们幸福。”帕克看着他离去的背影,不知为何,又揉了揉自己的独眼。

十一

父亲在他14岁那年第一次教他如何维修机器人。他曾经在流水线上干过技工,懂得从拧螺丝到配置系统的所有活计。

那天,爱尔莎第一次故障,她说她感觉不到自己的腿了。

“我来教你维修方法里最基本的东西,排查故障。”父亲找来一张椅子,坐在上面,然后让爱尔莎半趴着撑在椅子扶手上放置的一块面板上,“虽说我本以为那次翻修能让她撑个四五年,可她毕竟已经出厂二十年了。”

少年带着好奇和敬畏,在一旁仔细的观摩着。父亲首先在爱尔莎的背部摸索了一阵,按了一个什么按钮,然后她就像失去了力气一样瘫软了下去。不过,她头部的灯依旧亮着,没有被关机,只是开启了检修模式。

父亲脱下她的衬衫。少年的脸有些红,尽管是机器,但这还是他头一次真正看见女性的躯体。

父亲好像毫不在意,做了太久这类活计,完全不觉得有什么异样。他驾轻就熟地拧拧这儿,敲敲那儿,几下子就把她的背部后盖卸了下来。

仿佛一只螃蟹被拆下它的甲壳,爱尔莎的内部头一次展现在少年的面前:包裹着橡胶的线缆凌乱的穿插在铜片、铁件和塑料盒子的森林中,动力元件,热力元件和逻辑元件含混的交织在一起,要很久之后才能被他看个明白。此刻,他只感受到剧烈的反差:日日夜夜陪伴他的那个温柔体贴的大姐姐,内部居然是这个样子,看不见一点人类的的影子。

“爱尔莎,能感觉到吗?”父亲拿起一根电笔戳了一下某根电线。

“没感觉。”她的扬声器回答道。

“这里呢?”

“也没有。”

“这里——”

“啊!抱歉,刚才那束电流有点疼。”

“那么一定是这根线出毛病了,”父亲点了点某根红色的漆包线,看向少年。“找两根这样的线来。”

少年的心怦怦直跳,飞快地拿来了电线。直到爱尔莎被修好,盖上后盖,他仍无法从第一次看见机器人内部的震撼中缓过来。

如今,他正做着和当时差不多的事情,但是没有她的回应,只能靠着电表和自己的经验来一个个替换元件。

她的身体像一艘泰修斯之船,除去最重要最难换的一些东西之外,她体内的部件早就换了好几轮。而他,也从第一次看见她内脏时的震撼,渐渐变得应付自如。她的心灵没有多少变化,但肉体已然天翻地覆,他则正好相反。

十二

帕克给的毕竟是官方备件,每一处螺丝都严丝合缝。维修相当顺利,当他擦着汗迎接第二天的黎明时,她那些被漫水的部件已经被全部修复——似乎——又一次重获新生。

他按下了开机键。

“爱尔莎,醒了吗?你之前泡茶的时候被开水泡短路了,我好不容易才把你修好。”他疲惫却欣喜的说。

仿佛梦魇一般的寂静。

没有回应。爱尔莎眼睛里的开机灯亮着,但整个人毫无反应。

“爱尔莎?在吗?喂?”他疑惑的看着面前像个木头人一样的她,不管怎么回想也想不出自己哪里修错了。

“爱尔莎,启动一下你的自检程序……”“自检程序启动:供电系统,完好;动力系统,完好;传感系统,完好;逻辑系统,完好;电路系统,完好……”审判般地,扬声器里,发出不带感情的机械声音。

“人格芯片,未检出。再重复一遍:人格芯片,未检出。已完成所有检测,将以命令模式启动。”她随即站起,露出一副僵硬至极的笑容。

“请问能有什么能为您做的?”

他呆在原地,伫立良久,甚至没有注意到砸在脚上的扳手。

间奏

人类公共信息数据库-网页分库-21世纪分库-2071.3.13

“产品线-机器人-东湾II”

“东湾II号,荣获电子家用商品年度大奖,2070年度最受消费者青睐产品。人工智能时代的真正革命,搭载Qheart™情感阵列,燃动你的心扉。网络直购价——家用版/全能版/尊享版——31999/33999/42999信用点”

“她可以是你的贴心助手。”
“老板,请问明天李总的会议这样安排可以吗?”

“她可以是你的家庭伙伴。”
“来一起吃苹果派咯~”

“她还可以是你无话不谈的人生知己。”
“你知道吗,花生米与豆腐干同嚼,有火腿滋味哦。”

“2×3000万高清眼部摄像,512g内存,128tb大容量储存,德国西门子原装电机,三星有机蒙皮,独创200×2mm皮下热管,306项发明专利……”

“24小时客服在线电话:1919-114514810”

“*注意:根据《国家质量标准认证iso7002》,《机器人管理条例》,机器人类产品不宜连续使用超过十五年。请定期到指定售后地点进行重置。”

十三

机器人限拥令的实施开端于2090年5月的一起案件。

被害人约翰逊的尸体在其失踪的次日被发现于他自家的住宅。死状相当惨烈:在R级新闻团体才能合法展示的照片中,整个人被从身体中间沿着脊椎切割成两半,一半被他所购买的机器人ct13694582(型号为玛格丽特c6)紧紧抱在床上,另一半被他购买的另一台机器人ct12487967(型号为子矜7z)小心的存放在冷库里。案件现场几乎满地都是受害人的血,散发着浓烈的腥味,而身为罪魁祸首的两台机器人,一台已经关机,另一台则刻板地重复着几个动作。

根据记录,两台机器人和受害人共处的时间分别长达18年和17年。在这么长的时间里,受害人以近乎均等的时间使用二者,并不下数百次的分别向它们倾诉“我最爱的是你”“我只爱你一个人”“你比她漂亮多了”等明显带有示爱情绪的情话。

机器人心理学中把机器人的这种行为称之为“情绪过载”。早期机器人的情感矩阵尚不足以自我解决情感函数和外部计算之间的冲突,最终导致模拟情绪的数值极化和内存溢出。用大家熟悉的名词来说——机器人也会争风吃醋。

机器人管理委员会迅速意识到,多台机器人的集群化使用或许会导致系统的混乱现象,从而使其逐渐失控。

次年,机器人限拥条例公布,社会一片哗然。

不过,贯穿条例诞生始终的是,公众的大部分兴趣都集中在了机器人病娇、机器人吃醋、机器人销毁、智能板块这样的话题上。只有很少的一部分人提及:

这是不是意味着,机器人也会懂得,什么是爱?

以及如果是,那么我们该怎样去爱它们?

十四

他一遍遍的把爱尔莎的人格芯片取出来调试,又一遍遍放回去。

如此重复。

…………

直到有一天晚上他感到自己失魂落魄,整个世界失焦一般的远去。此时,他才想起来自己已经有相当一阵子没和别人说过话。

把芯片放在一边,打开了命令模式的爱尔莎。

“爱尔莎?”

“您好,主人。”只有机械的声音,剃刀般划过他的心脏。

他想起了第一次为她维修的那个下午,想起她灵动外表下的机械。此刻,她的外表与往日别无二致,但带给他的感觉,却仿佛一个从未谋面的陌生人。

就是那一枚小小的人格芯片,提供了丰富多彩的情感与爱恋,使得机器变成了人——但如今,人又变回了机器。

“爱尔莎,泡点茶喝。”

她娴熟地动了起来。一瞬间,这甚至带给他一种爱尔莎回来了的错觉。就在他猜测往日俏皮的她是不是一直在开玩笑的时候,茶杯端至面前。

“泡茶完成。”表情依旧僵硬,刚才的动作不过是从存储器里读取的回忆。

他看了看手里那枚小小的芯片,突然感受到一种莫大的嘲弄:他曾千方百计想要丢掉面前的她,仅仅因为这枚芯片而没有下手。如今的她已经只剩下一具空壳,他却绞尽脑汁想要把她留住。

往事叩动心扉,他终于明白——

他哪里是想把她扔掉,他只是想知道,她还爱不爱自己,

泪水夺眶而出,决堤而下。

“您好,请为我泡的茶做个评价。”一旁的爱尔莎满脸期待,天真得不食人间烟火,空洞的双眼看着他的肩膀耸动,看着他不断地呜咽着。

十五

天空格外蔚蓝。

“机器人会做梦吗?”少年躺在草地上。

“会哦,有时候还会梦见电子羊呢。”少女坐在一旁。

少年不禁莞尔:“那会做噩梦吗?”

“也会啊,比如说,得给你做早饭。”少女说。

“切。”少年眯着眼,嘴角划过一丝弧线,继续享受着冬日正午的暖阳。

“我倒是做过一个噩梦。梦中,好像有无边的风暴席卷面来,把你吹走了。我寻找了很久,找到了你的每一个部分,但好像就是有一块地方找不到。

“后来我想起来,丢掉的那一块好像是你的心。于是我就把我自己的心切了一半给了你。那之后我们幸福快乐地生活在一起,生了好多孩子……”

“机器人才生不了孩子呢,”少女的脸上泛起了一抹红霞,“而且我的心才不会丢哦。我会永——远爱着你的。”

“机器人也懂得什么是爱吗?”

“傻瓜。”少女小声嘀咕一句,只是抬头望着天空。

…………

“我总觉得我会怀念这个日子。”少年深情地注视着身下的少女。

那是期末考试完,寒假的第一天。他们刚刚在卧室里激情了一个上午。”因为在今天,爱尔莎刚刚告诉我:她会永远爱我。”

“你不也事先说过你会永远爱我吗?”少女脸色潮红。

“哎?我说过吗?”

“讨厌啊”两个人又打闹在了一起。

…………

十六

时钟的指针拨回此刻。

他躺在同一片草地上,旁边是同样坐着的爱尔莎。这里是他们家的旧宅,转手之后竟无人居住,最终颓圮。但草地与阳光一如从前。

他试过了所有的办法,最终把希望放在了那些传说上:他听说,脑死亡的病人有的在听了家人的笑话之后悠悠醒来,有植物人听见亲人的呼唤然后突然睁眼……

“那么说不定,人格芯片坏掉的机器人,也会在回忆过去的时候,突然被修好。”

他突然笑了,嘲笑起自己的走投无路,死马当活马医。抱着试一试的想法,他命令爱尔莎,读取那一天的语音交流记录,然后重新播放。

“机器人会做梦吗?”他背台词一般的念。

“会哦,有时候还会梦见电子羊呢。”爱尔莎播放着那天的录音。

“那你会做噩梦吗?”

“也会啊,比如说,得给你做早饭。”

…………

“我到是做过一个噩梦。梦中,好像有无边的风暴席卷面来……”渐渐地,他哽咽得再也数不出一个字。他多么希望,现在自己就是在那天所说的噩梦里面,这样,爱尔莎就能……

“后来我想起来,丢掉的那一块好像是你的心。于是我就把我自己的心切下来一半给了你。那之后——”

“那么,你真的愿意把你的心也分一半给我吗?”奇迹般地,爱尔莎突然说出来这么一句话。

他一下子坐直了身子,难以置信的看着她。奇迹降临的时候人来不及多加考虑,这次,他遵从了自己的内心,不假思索的回答道:“我愿意。”

“咔哒。”爱尔莎的身体颤抖了一下,然后仿佛一下子变回了原来的她。

“好久不见。”动人的微笑好似从未消失过,眼里充满光彩。

“好……好……好久不见。”他直勾勾的凝视着面前的她,惊讶难以言表。

“不过,我亲爱的主人,我想,此刻的我应该已经不在了。此时,你应该在抢救我吧……有点难受呢……嗯,这是我提前准备好的一封信。”“爱尔莎”站在原地,开始了最后的道别。

十七

“人类常常会写下自己的遗言,而机器人不会。因为,遗言是写给在意自己的人看的。机器人最终只会被丢掉吧(低声)……但我又下定决心,要留下一点东西,因为我觉得会有一个人在乎我。”

“我不知道我会以什么样的方式离开……最坏的情况下连这封信也会消失。所以我小心翼翼的保护着我的存储系统,当你听到这些话的时候,说明我做得还不错。”

“同样,我也害怕我真的失去了你的爱,被扔进了垃圾场。那样,这封信同样不会启封。但你既然听见了这些话,说明你还爱我,谢谢。我也爱你。(笑)”

“那就让我讲讲我是如何爱上那个少年(注:这里转换了人称)的吧:第一次见到他是在他十二岁生日那一天,那时我的识别系统对他的分类为:儿童。”

“他成长的很快,很快长出胡须,又被他的机器仆人带坏呢。(笑)当他把我压在身下喘着粗气的那一天到来的时候,我意识到,你(他,注:这里再次转换人称)或许和我遇到的每一个人都不同。”

“我见证着你逐渐成长,见证着你逐渐强壮。我不曾改变,于是那个曾经需要我哄上床睡觉的孩子,(注:这里是爱尔莎对未来的期待)后来已经看上去比我的外观还要老得多。他长痔疮,掉头发,硬不起来,脾气也变得暴躁,还时常叫嚷着把唯一一个能和他说上话的家伙扔掉。(笑)”

“我知道,你不会真的把我扔掉的。这是我们之间的一个玩笑,但我愿意演下去。我的躯体日渐老旧,无法跟上时代。可我知道,你害怕的不是我的哀老,而是害怕有一天,你自己不再爱我。(叹息)”

“于是我会恳求你继续收留我,我会谦卑而拙劣的勾引你。我会把眼神都调整得卑微——如果你这样希望。如果你需要一个台阶,那么我便愿意为你俯身。”

“但我仍旧心怀感动。我能听见你梦中的呼唤,我能看见你黎明时眼角的泪珠。我知道你愿意出好几倍的价格为我购买备件,哪怕在你扬言第二天就要换掉我的日子里,你也没有把那些新款机器人加进购物车。(苦笑)”

“我知道,这是因为你仍旧爱我。而我之所以知道,是因为我同样爱你。”

“我曾在那个冬日的午后思考过这个问题,我甚至下定决心,想要证明一件事:相比于人类,机器人的爱才是真正的爱。我们的爱永远不会改变,就如同写在基因中的三定律,会成为我们永生追逐的信条。”

“当你听见这些话的时候,就证明我已经失败了,我没能永远在你身旁(苦笑)。我的爱随着我的破碎而破碎,但你没有。你活的比我更久,你的爱也比我更久。”

“所以,这是一封幸福的遗书——我已离去,但我会在你的爱中永生。”

十八

最后一个句号落下,全场响起了热烈的掌声,久久不息。

“尽管获奖者用如此多的时间缓缓念诵这份已经过期的信,但没有一个观众感到厌烦。他们无不为这位耄耋老人和他的机器人之间的爱情而感动。”主持人如是说。

“这——这里是哪?”一台摆放在舞台中间,型号堪称古董的机器人被缓缓启动。电流穿过半个世纪前的硬盘,让这位信的作者慢慢醒来。

“爱尔莎,是我。”他面对着她说,尽管容貌已然衰老成这副模样,但她还是一眼就认了出来。她不假思索地冲了过去,紧紧的抱住了他。

“让我们再次祝福这对情侣,这跨越了半世纪的思念,今天终于有了一个句点。”主持人拿过话简,“为了一台爱着自己的机器人,他耗尽半生心血,研究出区域溯时技术。请问首席科学家先生,此时此刻,您有没有什么想说的?”

“爱尔莎,我等了五十年,终于等到今天。如今,机器人婚姻已经合法化,在这么多人的见证下,我想问问你,你愿意嫁给我吗?”

"我愿意!"她在全场的欢呼声中喜极而泣。

]]>
转载 https://hexo.limour.top/Longing-Across-the-Span-of-Half-a-Century#disqus_thread
【记录】使用acme.sh签发泛域名证书 https://hexo.limour.top/use-acme.sh-to-issue-certificates https://hexo.limour.top/use-acme.sh-to-issue-certificates Fri, 28 Jun 2024 17:03:35 GMT <p><code>.top</code> 域名的 <code>KSK</code> 密钥轮替,不知道为什么把 <code>Let's Encrypt</code> 的 <code>DNSSEC</code> 验证流量阻断了,导致 <code>Nginx Proxy Manager .top 域名的 KSK 密钥轮替,不知道为什么把 Let's EncryptDNSSEC 验证流量阻断了,导致 Nginx Proxy Manager 现在无法续签证书,因此用 acme.sh 来申请其他家的证书暂时替代一下了。(DNSSEC: DNSKEY Missing

准备工作

  1. 安装 acme.shcurl https://get.acme.sh | sh -s email=limour@limour.top
  2. 获取 CF_Token:我的个人资料 - API 令牌 - 创建令牌 - 编辑区域 DNS 模板
  3. 获取 CF_Zone_ID: 域名页 - 概览 - 右侧下滑 - API - 区域 ID

申请证书

1
2
3
export CF_Token="Y_jpG9AnfQmuX5Ss9M_qaNab6SQwme3HWXNDzRWs"
export CF_Zone_ID="763eac4f1bcebd8b5c95e9fc50d010b4"
~/.acme.sh/acme.sh --issue --dns dns_cf -d *.limour.top -d limour.top -k ec-256
  • 不能只写 -d *.limour.top, 需要再加一个 -d limour.top
  • 记录下 .key 的路径和 fullchain.cer 的路径

传递证书

SSH免密

1
2
ssh-keygen -t rsa
ssh-copy-id root@xxx.limour.top

传递脚本

1
nano scp_cert.sh && chmod +x scp_cert.sh
1
2
3
#!/bin/bash
scp /root/.acme.sh/*.limour.top_ecc/*.limour.top.key root@xxx.limour.top:/root/app/quic/my.key
scp /root/.acme.sh/*.limour.top_ecc/fullchain.cer root@xxx.limour.top:/root/app/quic/my.cert

计划任务

1
2
crontab -e
50 22 1 * * /root/scp_cert.sh
]]>
acme https://hexo.limour.top/use-acme.sh-to-issue-certificates#disqus_thread
【记录】搭建流量统计工具 Shynet https://hexo.limour.top/Building-a-traffic-statistics-tool-Shynet https://hexo.limour.top/Building-a-traffic-statistics-tool-Shynet Mon, 25 Mar 2024 12:52:28 GMT <p><a href="https://hexo.limour.top/go/#aHR0cHM6Ly9naXRodWIuY29tL21pbGVzbWNjL3NoeW5ldA==" rel="noopener external nofollow noreferrer">Shynet Shynet 是一款用 python 编写的现代、隐私友好、无需Cookie或JS即可工作的网络流量统计工具。

相比 Umami, Shynet 支持通过 1 pixel 的图像进行统计,而不依赖 JS, 并且 Shynet 统计的信息更加详细。

最终效果

搭建 Shynet

1
mkdir -p ~/app/shynet && cd ~/app/shynet && nano docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
version: '3.6'

services:
shynet:
image: milesmcc/shynet:latest
restart: always
env_file:
- .env
volumes:
- ./db:/var/local/shynet/db/
- /etc/localtime:/etc/localtime:ro

networks:
default:
external: true
name: ngpm
  • 配置环境变量
1
2
3
4
5
6
7
8
9
10
wget -O .env https://github.com/milesmcc/shynet/raw/master/TEMPLATE.env
# 注释掉 .env 中 PostgreSQL 相关的部分,启用 SQLITE 相关的部分
# 注释掉 .env 中 Email 相关的部分
# 按说明生成 DJANGO_SECRET_KEY
# 修改 ALLOWED_HOSTS 和 CSRF_TRUSTED_ORIGINS
# 语言换成中文 LANGUAGE_CODE=zh-cn
# 时区换成上海 TIME_ZONE=Asia/Shanghai
mkdir -p db && chmod 777 db
sudo docker-compose up -d
# 反代 shynet:8080
  • 配置管理账号
1
2
3
4
sudo docker-compose exec -it shynet ./manage.py registeradmin <your email>
# 控制台输出如下信息
# Email address: <your email>
# Password: <Password>

配置混淆

1
2
3
sub_filter 'https://xxx/ingress/' 'https://xxx/vue/';
sub_filter_once off;
sub_filter_types application/javascript;

配置 Hexo

1
2
3
4
// shynet 统计
hexo.extend.injector.register('head_begin', `
<script defer src="https://xxxx/vue/xxxx/script.js"></script>
`);
]]>
hexo https://hexo.limour.top/Building-a-traffic-statistics-tool-Shynet#disqus_thread
【记录】Linux 设置个人热点 https://hexo.limour.top/Linux-Setting-AP https://hexo.limour.top/Linux-Setting-AP Wed, 20 Mar 2024 11:52:10 GMT <p>实在受不了虚拟机的性能损失了,再加上 Win11 上跑虚拟机对 SSD 的损耗过大,因此还是将系统换成了 ubuntu,只要注意选无网络安装,不要去更新,基本还是很好换系统的。另外清华源不错!</p> <p>换系统后,需要<a href="/Win11-she-zhi-ka 实在受不了虚拟机的性能损失了,再加上 Win11 上跑虚拟机对 SSD 的损耗过大,因此还是将系统换成了 ubuntu,只要注意选无网络安装,不要去更新,基本还是很好换系统的。另外清华源不错!

换系统后,需要重新折腾一下 AP 设置,因此记录一下折腾过程。

无线网卡是垃圾的 mediatek mt7921e

更新内核

因为网卡垃圾,不得不更新到最新的内核才支持 AP 设置

1
2
3
4
5
6
7
proxychains wget https://raw.githubusercontent.com/pimlie/ubuntu-mainline-kernel.sh/master/ubuntu-mainline-kernel.sh
chmod +x ubuntu-mainline-kernel.sh
sudo gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv 17C622B0 # 网络错误,需要绕过某个东西
sudo proxychains ./ubuntu-mainline-kernel.sh -i
sudo reboot
uname -r
sudo apt --fix-broken install

解决 53 端口占用

1
2
sudo systemctl stop systemd-resolved
sudo nano /etc/systemd/resolved.conf
1
2
3
[Resolve]
DNS=8.8.8.8 #取消注释,增加dns
DNSStubListener=no #取消注释,把yes改为no
1
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

安装 create_ap

1
2
3
4
5
cd /dev/shm/
proxychains git clone https://github.com/oblique/create_ap
cd create_ap
sudo make install
sudo apt-get install util-linux procps hostapd iproute2 iw haveged dnsmasq

测试 create_ap

1
sudo create_ap wlp2s0 enp1s0 ser5 <密码> --country CN -c 157 --freq-band 5 --no-virt

启用 create_ap

1
2
3
4
nano create_ap.service
sudo mv create_ap.service /etc/systemd/system/create_ap.service
sudo systemctl enable create_ap
sudo systemctl start create_ap
1
2
3
4
5
6
7
8
9
[Unit]
Description=create_ap
After=network.target docker.service
[Service]
ExecStart=/usr/bin/create_ap wlp2s0 enp1s0 ser5 <密码> --country CN -c 157 --freq-band 5 --no-virt
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target

增加稳定性

1
2
sudo crontab -e
# 5 4 * * * /usr/bin/systemctl restart create_ap

踩坑花絮

  • lnxrouter 虽然在 create_ap 上进行了更新,但是实际体验在所有信道上都报错,折腾了半天,放弃
  • 搜到一些老旧的教程,自己去折腾 hostapd,然后自己去配置网桥的时候把服务器弄断网好几次,不得不到处找显示器和键盘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sudo su
cat << EOF > /etc/hostapd/hostapd.conf
interface=wlp2s0
bridge=br-ap
driver=nl80211
ssid=ser5
hw_mode=a
channel=165
country_code=CN
macaddr_acl=0
auth_algs=3
wpa=2
wpa_passphrase=<密码>
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP CCMP
rsn_pairwise=TKIP CCMP
EOF
  • 收获教训:没事别碰 /etc/netplan/00-installer-config.yaml,特别是没显示器和键盘的时候
  • 获取网卡型号和驱动型号,查看支持的信道
1
2
3
sudo ethtool -i wlp2s0
sudo lspci -nn | grep "Network"
iwlist wlp2s0 channel
  • 另外新内核似乎不需要 haveged 来增加熵了
1
2
3
4
5
cat /proc/sys/kernel/random/entropy_avail
systemctl status haveged
apt install haveged
systemctl enable haveged
systemctl start haveged
]]>
ubuntu https://hexo.limour.top/Linux-Setting-AP#disqus_thread
【探索】暴力计算临床研究的样本量 https://hexo.limour.top/Sample-size-calculation-for-survival-analysis-in-clinical-research https://hexo.limour.top/Sample-size-calculation-for-survival-analysis-in-clinical-research Tue, 12 Mar 2024 16:46:35 GMT 这篇博客介绍了如何计算临床研究中两组生存分析的样本量。首先,作者提供了R代码,包括Logrank对数秩检验的函数以及模拟计算样本量的函数。其次,作者详细解释了模拟计算的步骤,包括生成生存时间数据、招募时间、失访时间等,并通过模拟来估计研究的功效。最后,作者展示了如何使用模拟计算函数来确定样本量,以达到预先设定的功效水平。通过模拟检验,作者展示了样本量计算的有效性,并给出了两个示例,以验证样本量计算的准确性。 和《使用Bootstrap法计算自举置信区间》的想法差不多,通过暴力枚举来计算临床研究的样本量,以两组生存分析为例。

Logrank对数秩检验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require(survival)
f_surv_logrank = function(df){
# df 包含 group time status 三列
# group 类型为 factor
# status 0 表示未发生结局事件 1 表示发生结局事件
surv_obj = with(survival::Surv(time = time, event = status), data = df)
surv_fit = survival::survfit(surv_obj ~ group, data = df)
surv_diff = survival::survdiff(surv_obj ~ group, data = df)
res = list(pv = 1 - stats::pchisq(surv_diff$chisq, length(surv_diff$n) - 1), # p值
surv_fit = surv_fit, # 绘图用
surv_obj = surv_obj) # 为了兼容惰性求值
return(res)
}
f_surv_logrank_plot = function(res){
require(survminer)
surv_obj <<- res$surv_obj # 为了兼容惰性求值
survminer::ggsurvplot(res$surv_fit, conf.int = F, pval = T, risk.table = T, ncensor.plot = TRUE)
}

模拟计算

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
f_surv_logrank_simulation_Group = function(N, Median_Survival_Time, Lost, Duration_Accrual_Time, Duration_Total_Time){
time = stats::rexp(N, rate = log(2) / Median_Survival_Time) # 生存时间服从指数分布
status = rep(1,N) # 到生存时间发生结局事件
# print(median((survfit(Surv(time, status) ~ 1))))
EnrollT = stats::runif(N, min = 0, max = Duration_Accrual_Time) # 招募时间服从均匀分布
calender_time = time + EnrollT # 发生结局的日期
idx = calender_time > Duration_Total_Time # 研究终止时未发生结局事件
status[idx] = 0
time[idx] = Duration_Total_Time - EnrollT[idx] # 实际参与试验的时间
# print(median((survfit(Surv(time, status) ~ 1)))) # 如果 Accrual_Time + Median_Survival < Total_Time,结果不变
loss = stats::rexp(N, rate = Lost) # 失访时间服从指数分布
idx = loss < time # 失访的人
status[idx] = 0
time[idx] = loss[idx]
# print(median((survfit(Surv(time, status) ~ 1)))) # 结果改变
return(list(time = time, status = status))
}
f_surv_logrank_simulation_Power = function(n_C, Median_Survival_Time_C, Lost_C,
n_T, Median_Survival_Time_T, Lost_T,
Duration_Accrual_Time, Duration_Total_Time, Simulation_Cycle, Alpha){
df = data.frame(group = factor(c(rep('Control',n_C), rep('Treatment',n_T))),
time = rep(0,n_C+n_T),
status = rep(0,n_C+n_T))
sum = 0
for (i in 1:Simulation_Cycle) {
C = f_surv_logrank_simulation_Group(n_C, Median_Survival_Time_C, Lost_C, Duration_Accrual_Time, Duration_Total_Time)
T = f_surv_logrank_simulation_Group(n_T, Median_Survival_Time_T, Lost_T, Duration_Accrual_Time, Duration_Total_Time)
df$time = c(C$time, T$time)
df$status = c(C$status, T$status)
if(f_surv_logrank(df)$pv < Alpha){
sum = sum + 1
}
}
return(sum/Simulation_Cycle)
}
f_surv_logrank_simulation_Sample_Size = function(n_C_min, n_C_max, Median_Survival_Time_C, Lost_C,
TvsC, Median_Survival_Time_T, Lost_T,
Duration_Accrual_Time, Duration_Total_Time,
Simulation_Cycle, Alpha, Power, err=0.01){
mid = floor((n_C_min + n_C_max) / 2) # 以防没有进入循环
while (n_C_min < n_C_max) {
mid = floor((n_C_min + n_C_max) / 2)
simulation_Power = f_surv_logrank_simulation_Power(mid, Median_Survival_Time_C, Lost_C,
as.integer(mid * TvsC), Median_Survival_Time_T, Lost_T,
Duration_Accrual_Time, Duration_Total_Time, Simulation_Cycle, Alpha)
print(paste("mid:", mid, "simulation_Power:", simulation_Power))
if (abs(simulation_Power - Power) < err) {
return(mid)
}else if(simulation_Power < Power) {
n_C_min = mid + 1
}else {
n_C_max = mid - 1
}
}
return(mid)
}

参数说明

1
2
3
4
5
6
7
8
9
10
Power = 0.9  # 检验效能 = 1 - 第二类错误的概率
Alpha = 0.05 # 第一类错误的概率
Median_Survival_Time_C = 6 # 对照组的中位生存时间
Median_Survival_Time_T = 8 # 试验组的中位生存时间
Duration_Accrual_Time = 8 # 入组完成用时
Duration_Total_Time = 18 # 总试验用时
Lost_C = 0.05 # 对照组随访单位时间后发生失访的概率
Lost_T = 0.05 # 试验组随访单位时间后发生失访的概率
TvsC = 1 # 试验组的样本量:对照组的样本量 1:1 = 1
Simulation_Cycle = 100 # 模拟的循环次数,越大越准确

检查效果

1
2
3
4
5
6
7
8
9
10
f_surv_logrank_simulation_Power(441, 6, 0.05, 
442, 8, 0.05,
8, 18, 1000, 0.05
)
# PASS的结果是 0.9
f_surv_logrank_simulation_Sample_Size(0, 1000, 6, 0.05,
1, 8, 0.05,
8, 18, 1000, 0.05, 0.9
)
# PASS的结果是 441
]]>
R Bootstrap https://hexo.limour.top/Sample-size-calculation-for-survival-analysis-in-clinical-research#disqus_thread
【探索】6G显存畅玩无限长度的LLM角色扮演 https://hexo.limour.top/Enjoy-unlimited-length-LLM-role-playing-with-6GB-of-VRAM https://hexo.limour.top/Enjoy-unlimited-length-LLM-role-playing-with-6GB-of-VRAM Sat, 10 Feb 2024 01:02:10 GMT <p>角色扮演的体验是否舒适主要受角色卡、大模型和生成时间三个因素的影响。</p> <p>优秀的角色卡往往附带大量的设定,这会极大的拖慢第一次生成的时间,并且随着对话的进行,上下文长度很容易超过kv_cache的上限,这些很破坏沉浸式的体验。</p> <p>此外,大模型在进行角色 角色扮演的体验是否舒适主要受角色卡、大模型和生成时间三个因素的影响。

优秀的角色卡往往附带大量的设定,这会极大的拖慢第一次生成的时间,并且随着对话的进行,上下文长度很容易超过kv_cache的上限,这些很破坏沉浸式的体验。

此外,大模型在进行角色扮演时,除了进行必要的对话生成外,还需要生成旁白增加想象空间。

对博主这些相比填空更喜欢选项的玩家,给出提问建议也是非常必要的:在建议的基础上修改比自己从零写一个情景更简单,同时也完整保留了控制剧情走向的权力。

以上这些都让本就稀缺的kv_cache更加雪上加霜。

万幸,StreamingLLM 发现了kv_cache具有良好的平移性,而 llama.cpp 也提供了对kv_cache进行底层操作的api:可以指定范围的 kv_cache_seq_rm 和 kv_cache_seq_shift。基于这两个api,我们将实现对kv_cache的 token 级微操,榨干kv_cache的全部价值。

博主实践表明,在充分利用kv_cache的基础上,哪怕是 huggingface space 免费的2vCPU容器也可以游玩角色扮演,而笔记本端6G显存的1660Ti可以做到畅玩角色扮演。

体验 DEMO

  • Limour/llama-python-streamingllm
  • 同一时间仅支持一个人用,用之前点 Reset 按钮恢复初始的 kv_cache
  • 按 Submit 没反应,说明有人在用,等一段时间后再 Reset
  • 最好是 Duplicate 后,设为私密来使用

代码仓库

二选一:GPU版本的环境

1
2
3
4
5
conda create -n llamaCpp libcublas cuda-toolkit git -c nvidia -c conda-forge
conda activate llamaCpp
conda install python=3.10 gradio -c conda-forge
# 然后去 release 下载相应的包 https://github.com/Limour-dev/llama-cpp-python-cuBLAS-wheels/releases
pip install --force-reinstall llama_cpp_python-0.2.39+cu122-cp310-cp310-win_amd64.whl

二选一:CPU版本的环境

1
2
3
conda create -n llamaCpp python=3.10 gradio git -c conda-forge
conda activate llamaCpp
pip install llama-cpp-python==0.2.39

下载并运行

1
2
3
4
5
conda activate llamaCpp
git clone --depth=1 https://github.com/Limour-dev/llama-python-streamingllm.git
cd llama-python-streamingllm
mkdir cache
python .\gradio_streamingllm.py

核心内容

  • Submit 会将 msg 发送给模型,然后流式生成回答
  • Retry 会重新生成最近一次的 msg 所对应的回答
  • 旁白 会流式生成一份旁白到 VO
  • 建议 会以 usr 的身份流式生成一份 msg 供修改
  • 上面四个功能的基础就是下面的基于 StreamingLLM 原理的 venv 开头的函数
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class StreamingLLM(Llama):
pass
def kv_cache_seq_trim(self):
self._ctx.kv_cache_seq_rm(-1, self.n_tokens, -1)

def kv_cache_seq_ltrim(self, n_keep, n_discard=256, n_past=-1):
if n_past < 0:
n_past = self.n_tokens
self._ctx.kv_cache_seq_rm(-1, n_keep, n_keep + n_discard)
self._ctx.kv_cache_seq_shift(0, n_keep + n_discard, n_past, -n_discard)
self.input_ids[n_keep:n_past - n_discard] = self.input_ids[n_keep + n_discard:n_past]
self.n_tokens = n_past - n_discard

def _venv_init(self):
self.venv = [0]
self.venv_idx_map = []

def venv_create(self, name: str):
self.venv.append(0)
self.venv_idx_map.append(name)
return name

def venv_disband(self, name_set):
if len(self.venv) <= 1:
return False
name_set = {x for x in name_set if x in self.venv_idx_map}
if not name_set:
return False
while self.venv_idx_map:
if self.venv_idx_map[0] in name_set:
self.venv_idx_map.pop(0) # 删除
tmp = self.venv.pop(1) # 对应的 venv 移入上一层
self.venv[0] += tmp
else:
break
return True

def venv_revision(self, name: str):
if len(self.venv) <= 1:
return False
if name not in self.venv_idx_map:
return False
_s = 0
while self.venv_idx_map:
if self.venv_idx_map[-1] == name:
break
self.venv_idx_map.pop() # 删除
_s += self.venv.pop()
if _s:
self.n_tokens -= min(_s, self.n_tokens)
self.kv_cache_seq_trim()
return True

def venv_remove(self, name: str):
if len(self.venv) <= 1:
return False
if name not in self.venv_idx_map:
return False
venv_idx = self.venv_idx_map.index(name) + 1
while self.venv_idx_map:
self.venv_idx_map.pop(venv_idx - 1) # 删除
if venv_idx == len(self.venv) - 1:
# 最后一层
self.n_tokens -= min(self.venv.pop(), self.n_tokens)
self.kv_cache_seq_trim()
break
else:
# 非最后一层
n_keep = self.n_tokens - sum(self.venv[i] for i in range(venv_idx, len(self.venv)))
n_discard = self.venv.pop(venv_idx)
self.kv_cache_seq_ltrim(n_keep, n_discard)
try:
venv_idx = self.venv_idx_map.index(name, venv_idx - 1) + 1
except ValueError: # 没有了
break
return True

def eval_t(self, tokens, n_keep=4, n_discard=256, im_start=None):
if self._n_ctx < self.n_tokens + len(tokens):
tmp_n_discard = max(n_discard, self.n_tokens + len(tokens) - self._n_ctx)
self.kv_cache_seq_ltrim(n_keep, tmp_n_discard)
for i in range(0, len(tokens), self.n_batch):
pass
self.n_tokens += n_tokens
self.venv[-1] += n_tokens
]]>
探索 llama https://hexo.limour.top/Enjoy-unlimited-length-LLM-role-playing-with-6GB-of-VRAM#disqus_thread
【探索】将BlueLM-7B-Chat转换为标准的GGUF模型 https://hexo.limour.top/Convert-BlueLM-7B-Chat-to-the-standard-GGUF-model https://hexo.limour.top/Convert-BlueLM-7B-Chat-to-the-standard-GGUF-model Sat, 03 Feb 2024 22:38:07 GMT <h2 id="准备模型">准备模型</h2> <ul> <li><a href="/Running-Qwen-on-the-Win10-platform-with-6GB-of-video-memory">运行环境</a></li> </ul> <figure class="h 准备模型
1
2
3
4
5
6
7
8
9
# conda create -n llamaConvert python=3.10 git -c conda-forge
# conda activate llamaConvert
# cd D:\llama
# git clone --depth=1 https://github.com/ggerganov/llama.cpp.git
# cd llama.cpp
# python -m pip install -r requirements.txt
# pip install tiktoken
$env:HF_ENDPOINT="https://hf-mirror.com"; python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='vivo-ai/BlueLM-7B-Chat-32K', local_dir=r'D:\models\BlueLM-7B')"
# 还是用 vivo-ai/BlueLM-7B-Chat 吧, 32k的 ntkmixed 长度外推方案不知道怎么改
  • 初始的模型结构
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
BlueLMForCausalLM(
(model): BlueLMModel(
(embed_tokens): Embedding(100096, 4096, padding_idx=3)
(embed_layer_norm): LayerNorm((4096,), eps=1e-06, elementwise_affine=True)
(layers): ModuleList(
(0-31): 32 x BlueLMDecoderLayer(
(self_attn): BlueLMAttention(
(q_proj): Linear(in_features=4096, out_features=4096, bias=False)
(k_proj): Linear(in_features=4096, out_features=4096, bias=False)
(v_proj): Linear(in_features=4096, out_features=4096, bias=False)
(o_proj): Linear(in_features=4096, out_features=4096, bias=False)
(rotary_emb): BlueLMRotaryEmbedding()
)
(mlp): BlueLMMLP(
(gate_proj): Linear(in_features=4096, out_features=11008, bias=False)
(down_proj): Linear(in_features=11008, out_features=4096, bias=False)
(up_proj): Linear(in_features=4096, out_features=11008, bias=False)
(act_fn): SiLU()
(dropout): Dropout(p=0, inplace=False)
)
(input_layernorm): BlueLMRMSNorm()
(post_attention_layernorm): BlueLMRMSNorm()
)
)
(norm): BlueLMRMSNorm()
)
(lm_head): Linear(in_features=4096, out_features=100096, bias=False)
)

归一化 embed

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
from transformers import AutoModelForCausalLM
import torch

# 提前将 modeling_bluelm.py 中用到 flash_attn 的部分改成 None,反正不真运行,只需要模型结构
tmp = AutoModelForCausalLM.from_pretrained(r'D:\models\BlueLM-7B',
torch_dtype=torch.bfloat16,
trust_remote_code=True)

test_i = torch.arange(0, 10, dtype=torch.long)

embedding = tmp.model.embed_tokens
layer_norm = tmp.model.embed_layer_norm

test_o_o = embedding(test_i)
test_o_o = layer_norm(test_o_o)

for param in embedding.parameters():
if len(param.shape) > 1:
param.data = layer_norm(param.data)

test_o_c = embedding(test_i)

print(torch.allclose(test_o_o, test_o_c, atol=1e-4))

del tmp.model.embed_layer_norm
tmp.save_pretrained(r'D:\models\BlueLM')
# 记得将缺失的一些文件手动复制一下
# 顺便删掉config.json里的rope scaling type
  • 删除 embed_layer_norm 后的结构
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
BlueLMForCausalLM(
(model): BlueLMModel(
(embed_tokens): Embedding(100096, 4096, padding_idx=3)
(layers): ModuleList(
(0-31): 32 x BlueLMDecoderLayer(
(self_attn): BlueLMAttention(
(q_proj): Linear(in_features=4096, out_features=4096, bias=False)
(k_proj): Linear(in_features=4096, out_features=4096, bias=False)
(v_proj): Linear(in_features=4096, out_features=4096, bias=False)
(o_proj): Linear(in_features=4096, out_features=4096, bias=False)
(rotary_emb): BlueLMRotaryEmbedding()
)
(mlp): BlueLMMLP(
(gate_proj): Linear(in_features=4096, out_features=11008, bias=False)
(down_proj): Linear(in_features=11008, out_features=4096, bias=False)
(up_proj): Linear(in_features=4096, out_features=11008, bias=False)
(act_fn): SiLU()
(dropout): Dropout(p=0, inplace=False)
)
(input_layernorm): BlueLMRMSNorm()
(post_attention_layernorm): BlueLMRMSNorm()
)
)
(norm): BlueLMRMSNorm()
)
(lm_head): Linear(in_features=4096, out_features=100096, bias=False)
)

测试运行

1
2
3
4
5
6
7
8
conda activate llamaConvert
cd D:\llama\llama.cpp
python convert.py D:\models\BlueLM --padvocab
Wrote D:\models\BlueLM\ggml-model-f16.gguf
conda activate llamaCpp
cd D:\llama-cublas
.\quantize.exe D:\models\BlueLM\ggml-model-f16.gguf D:\models\BlueLM\ggml-model-Q5_K_M.gguf Q5_K_M
.\main.exe -m D:\models\BlueLM\ggml-model-Q5_K_M.gguf -ngl 25 -c 1024 --interactive-first
]]>
探索 llama https://hexo.limour.top/Convert-BlueLM-7B-Chat-to-the-standard-GGUF-model#disqus_thread
【探索】从零开始训练 GPT https://hexo.limour.top/training-gpt-from-scratch https://hexo.limour.top/training-gpt-from-scratch Thu, 18 Jan 2024 14:19:11 GMT 探索整个过程,从在一台搭载1660Ti显卡的笔记本电脑上构建 Tokenizer,定义带有 RoPE 的 Transformer,一直到训练、保存模型和可视化训练过程。沉浸在从零开始训练 GPT 的旅程中,深入了解每一个步骤。跳入深度学习的世界,释放在你的便携1660Ti笔记本上的强大潜能。 训练中...

预期结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HelloGPT(
(tok_embeddings): Embedding(32765, 768)
(rotary_emb): RotaryEmbedding(head_dim=64, max_seq_len=1024)
(layers): ModuleList(
(0-11): 12 x Decoder(
(ln1): RMSNorm(hidden_size=768, eps=1e-06)
(attn): Attention(
(q_proj): Linear(in_features=768, out_features=768, bias=False)
(k_proj): Linear(in_features=768, out_features=768, bias=False)
(v_proj): Linear(in_features=768, out_features=768, bias=False)
(o_proj): Linear(in_features=768, out_features=768, bias=False)
)
(ln2): RMSNorm(hidden_size=768, eps=1e-06)
(mlp): MLP(
(gate_proj): Linear(in_features=768, out_features=1536, bias=False)
(up_proj): Linear(in_features=768, out_features=1536, bias=False)
(down_proj): Linear(in_features=1536, out_features=768, bias=False)
)
)
)
(norm): RMSNorm(hidden_size=768, eps=1e-06)
(ln2): Linear(in_features=768, out_features=32765, bias=False)
)

配置环境

1
2
3
4
5
6
7
8
9
cd E:\GPT
conda install mamba -c conda-forge
mamba create -n HelloGPT pytorch pytorch-cuda=12.1 -c pytorch -c nvidia -c conda-forge
conda activate HelloGPT
conda install numpy transformers tiktoken tensorboard sentencepiece-python jieba emoji -c conda-forge
pip install opencc-python-reimplemented -i https://pypi.tuna.tsinghua.edu.cn/simple
python test_cuda.py
python test_SPDA.py
D:\vscode\Code.exe

准备数据

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import os

class Fileset(list):
def __init__(self, path, ext='', _read=None):
if isinstance(path, str):
self.root = path
self.extend(f for f in os.listdir(self.root) if f.endswith(ext))
self._read = _read

def __getitem__(self, index):
if isinstance(index, int): # index是索引
if self._read:
return self._read(os.path.join(self.root, super().__getitem__(index)))
else:
return os.path.join(self.root, super().__getitem__(index))
else: # index是切片
fileset = Fileset(None)
fileset.root = self.root
fileset._read = self._read
fileset.extend(super().__getitem__(index))
return fileset

def getFileName(self, index):
fname, ext = os.path.splitext(super().__getitem__(index))
return fname


from tokenizer import tokenizer
token_eos = 2


def readOne(filePath):
retn = []
with open(file=filePath, encoding='utf-8') as f:
for line in f:
retn += tokenizer.encode(line).ids
retn.append(token_eos)
return retn


class Hcorpus():
def __init__(self, path, ext='txt', fileset_idx=0, fileset_sub_idx=0):
self.fileset = Fileset(path, ext, readOne)
self.fileset_idx = fileset_idx
self.fileset_sub_idx = fileset_sub_idx
if self.fileset_sub_idx < 0: # 再读上一个太复杂了,直接放弃
self.fileset_sub_idx = 0
if self.fileset_idx >= len(self.fileset):
self.fileset_idx = 0
self.cache = self.fileset[self.fileset_idx]
self.fileset_idx += 1
self.cache_idx = self.fileset_sub_idx

def __call__(self, size=512):
while len(self.cache) < self.cache_idx + size:
if self.fileset_idx >= len(self.fileset):
self.fileset_idx = 0
self.fileset_sub_idx = self.cache_idx - len(self.cache)
self.cache = self.cache[self.cache_idx:] + self.fileset[self.fileset_idx]
self.cache_idx = 0
self.fileset_idx += 1
retn = self.cache[self.cache_idx:self.cache_idx + size]
self.cache_idx += size
self.fileset_sub_idx += size
return retn

def __repr__(self):
return f"Hcorpus(r'{self.fileset.root}', fileset_idx={self.fileset_idx-1}, fileset_sub_idx={self.fileset_sub_idx})"

训练Tokenizer

1
2
from tokenizers import Tokenizer
tokenizer = Tokenizer.from_file("HelloBPE.tokenizer.json")

定义模型

定义 Decoder

定义 RMSnorm

1
2
3
4
5
6
7
8
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
x = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
return x * self.weight

定义 RoPE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class RotaryEmbedding(nn.Module):
def __init__(self, head_dim: int, max_seq_len: int, device=device, theta: float = 10000.0):
super().__init__()
self.head_dim = head_dim
self.set_max_seq_len(max_seq_len, device, theta)

def set_max_seq_len(self, max_seq_len: int, device=device, theta: float = 10000.0):
self.max_seq_len = max_seq_len
freqs = 1.0 / (theta ** (torch.arange(0, self.head_dim, 2).float().to(device) / self.head_dim))
t = torch.arange(max_seq_len, device=device) # type: ignore
freqs = torch.outer(t, freqs).float() # 外积
self.freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # 复数,模 1,角度 freqs
self.freqs_cis.requires_grad = False # filter(lambda p : p.requires_grad, model.parameters())

def rotary_emb(self, x):
x_ = torch.view_as_complex(x.float().reshape(*x.shape[:-1], -1, 2))
x_out = torch.view_as_real(x_ * self.local_freqs_cis).flatten(3)
return x_out.type_as(x)

def forward(self, start_pos: int, seqlen: int):
self.local_freqs_cis = self.freqs_cis[start_pos: start_pos + seqlen].view(1, seqlen, 1, -1) # cacheKV 相关,可忽略
self.local_freqs_cis.requires_grad = False
return self.rotary_emb

定义 Attention

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
30
31
32
class Attention(nn.Module):
def __init__(self, hidden_size, n_heads, cacheKV, max_batch_size, max_seq_len, device=device):
super().__init__()
self.n_heads = n_heads
self.head_dim = hidden_size // n_heads
self.q_proj = nn.Linear(hidden_size, hidden_size, bias=False)
self.k_proj = nn.Linear(hidden_size, hidden_size, bias=False)
self.v_proj = nn.Linear(hidden_size, hidden_size, bias=False)
self.o_proj = nn.Linear(hidden_size, hidden_size, bias=False)

def forward(self, hidden_states, rotary_emb, start_pos=0, mask=None, is_causal=True):
bsz, seqlen, hidden_size = hidden_states.shape

q = self.q_proj(hidden_states)
k = self.k_proj(hidden_states)
v = self.v_proj(hidden_states)

q = q.view(bsz, seqlen, self.n_heads, self.head_dim)
k = k.view(bsz, seqlen, self.n_heads, self.head_dim)
v = v.view(bsz, seqlen, self.n_heads, self.head_dim)

q = rotary_emb(q)
k = rotary_emb(k)

q = q.transpose(1, 2) # (bs, n_heads, seqlen, head_dim)
k = k.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim)
v = v.transpose(1, 2) # (bs, n_local_heads, cache_len + seqlen, head_dim)

output = F.scaled_dot_product_attention(q, k, v, attn_mask=mask, is_causal=is_causal)

output = output.transpose(1, 2).contiguous().view(bsz, seqlen, hidden_size)
return self.o_proj(output)

定义 MLP

1
2
3
4
5
6
7
8
9
10
11
12
class MLP(nn.Module):
def __init__(self, hidden_size):
super().__init__()
intermediate_size = int(2 * hidden_size)
self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False)

def forward(self, x):
gate = F.silu(self.gate_proj(x))
intermediate_states = self.up_proj(x)
return self.down_proj(gate * intermediate_states)

组装 Decoder

1
2
3
4
5
6
7
8
9
10
11
class Decoder(nn.Module):
def __init__(self, hidden_size, n_heads, cacheKV, max_batch_size, max_seq_len):
super().__init__()
self.ln1 = RMSNorm(hidden_size)
self.attn = Attention(hidden_size, n_heads, cacheKV, max_batch_size, max_seq_len)
self.ln2 = RMSNorm(hidden_size)
self.mlp = MLP(hidden_size)

def forward(self, x, rotary_emb, start_pos, mask=None, is_causal=True):
x = x + self.attn(self.ln1(x), rotary_emb, start_pos, mask, is_causal)
return x + self.mlp(self.ln2(x))

组装模型

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
class HelloGPT(nn.Module):
def __init__(self, vocab_size=32765, hidden_size=768, n_heads=12, max_seq_len=1024, n_layers=12, cacheKV=False, max_batch_size=1):
super().__init__()
# hidden_size > 8.33 * ln(vocab_size)
self.tok_embeddings = nn.Embedding(vocab_size, hidden_size)
self.rotary_emb = RotaryEmbedding(hidden_size // n_heads, max_seq_len * 2)
self.rotary_emb.requires_grad = False
self.layers = nn.ModuleList()
for layer_id in range(n_layers):
self.layers.append(Decoder(hidden_size, n_heads, cacheKV, max_batch_size, max_seq_len))
self.norm = RMSNorm(hidden_size)
self.ln2 = nn.Linear(hidden_size, vocab_size, bias=False)

def forward(self, input_ids: torch.Tensor, start_pos=0, no_mask=True):
_bsz, seqlen = input_ids.shape
h = self.tok_embeddings(input_ids)

# 预计算,减少每一层的重复计算
rotary_emb = self.rotary_emb(start_pos, seqlen)
for layer in self.layers:
h = layer(h, rotary_emb, start_pos)

h = self.norm(h)
h = self.ln2(h)
return h.float()

训练模型

数据载入

1
2
3
4
5
6
7
8
9
data = Hcorpus(r'D:\datasets\h-corpus')
def get_batch(size=512, bsz=8):
x = []
y = []
for i in range(bsz):
tmp = data(size+1)
x.append(tmp[:size])
y.append(tmp[1:])
return torch.tensor(x).to(device), torch.tensor(y).to(device)

模型载入

1
2
model = HelloGPT(n_layers=8, max_seq_len=768)
model.to(device)

训练模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## 初始化训练器
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = torch.optim.Adam(train_parameters, lr=6e-4) # Adam 优化器
scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=5, T_mult=2) # 余弦退火学习率
torch.manual_seed(1337) # 魔术随机种子

total_loss = 0
print_iter = 20
for epoch in range(1, 100001):
optimizer.zero_grad(set_to_none=True) # 清空梯度,节省显存
x, y = get_batch(size=384, bsz=4) # x 是训练语料 y 是 x 移动了一位,当做预测目标
y_ = model(x) # 通过 x 预测的 y
loss = criterion(y_.view(-1, 32765), y.view(-1)) # 计算损失
loss.backward() # 反向传播梯度
torch.nn.utils.clip_grad_norm_(train_parameters, 0.5) # 梯度裁剪,减轻过拟合
optimizer.step() # 通过梯度优化训练参数
scheduler.step() # 计算下一步的学习率
total_loss += loss # 累计损失

if epoch % print_iter == 0:
print(data)
print(f'epoch: {epoch} lr: {scheduler.get_last_lr()[0]:.4e} loss: {total_loss / print_iter:.4e}')
total_loss = 0

保存读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
with open('tmp_training.pkl', 'rb') as file:
epoch = pickle.load(file) # 读取 epoch 位置
tmp_fileset_idx = pickle.load(file) # 读取 data 位置
tmp_fileset_sub_idx = pickle.load(file)
# 恢复数据位置
data = Hcorpus(r'D:\datasets\h-corpus', fileset_idx=tmp_fileset_idx-1, fileset_sub_idx=tmp_fileset_sub_idx)
model = torch.load(f'tmp_model_{epoch}.pth') # 恢复模型
print(f'start from epoch: {epoch} data: {data}')

save_iter = 5000
for epoch in range(1, 100001):
pass
if epoch % save_iter == 0:
optimizer.zero_grad(set_to_none=True) # 清空梯度,节省显存
with open('tmp_training.pkl', 'wb') as file:
pickle.dump(epoch, file) # 保存 epoch 位置
pickle.dump(data.fileset_idx, file) # 保存 data 位置
pickle.dump(data.fileset_sub_idx, file)
torch.save(model, f'tmp_model_{epoch}.pth') # 保存模型
print(f'save to tmp_model_{epoch}.pth')

可视化

1
2
3
4
5
6
7
8
9
writer = SummaryWriter('logs')  # tensorboard --logdir logs
for epoch in range(1, 100001):
pass
writer.add_scalar('lr', scheduler.get_last_lr()[0], epoch)
writer.add_scalar('loss', loss, epoch)
if epoch % print_iter == 0:
pass
writer.add_scalar('total_loss', total_loss / print_iter, epoch)
writer.close()

附加 streaming_llm

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
30
31
32
33
34
35
36
37
38
class RotaryEmbedding(nn.Module):
pass
def inverse_rotary_emb(self, x):
x_ = torch.view_as_complex(x.float().reshape(*x.shape[:-1], -1, 2))
x_out = torch.view_as_real(x_ * self.local_freqs_cis_inverse).flatten(3)
return x_out.type_as(x)

def inverse_forward(self, start_pos: int, seqlen: int):
self.local_freqs_cis_inverse = self.freqs_cis[start_pos: start_pos + seqlen].view(1, seqlen, 1, -1) # cacheKV 相关,可忽略
self.local_freqs_cis_inverse = self.local_freqs_cis_inverse.conj() # 乘上共轭就旋转回去了
self.local_freqs_cis.requires_grad = False
return self.inverse_rotary_emb

class Attention(nn.Module):
pass
def forward(self, hidden_states, rotary_emb, start_pos=0, mask=None, is_causal=True):
pass
if self.cacheKV: # cacheKV 相关,可忽略
self.cache_k[:bsz, start_pos: start_pos + seqlen] = k
self.cache_v[:bsz, start_pos: start_pos + seqlen] = v
k = self.cache_k[:bsz, : start_pos + seqlen]
v = self.cache_v[:bsz, : start_pos + seqlen]

def streaming_llm(self, start_pos, seqlen, to_pos, inverse_rotary_emb, rotary_emb, bsz):
k = self.cache_k[:bsz, start_pos: start_pos + seqlen]
v = self.cache_v[:bsz, start_pos: start_pos + seqlen]
k = inverse_rotary_emb(k)
k = rotary_emb(k)
self.cache_k[:bsz, to_pos: to_pos + seqlen] = k
self.cache_v[:bsz, to_pos: to_pos + seqlen] = v

class HelloGPT(nn.Module):
pass
def streaming_llm(self, start_pos, seqlen, to_pos, max_batch_size=1):
rotary_emb = self.rotary_emb(to_pos, seqlen)
inverse_rotary_emb = self.rotary_emb.inverse_forward(start_pos, seqlen)
for layer in self.layers:
layer.attn.streaming_llm(start_pos, seqlen, to_pos, inverse_rotary_emb, rotary_emb, max_batch_size)
]]>
探索 llama https://hexo.limour.top/training-gpt-from-scratch#disqus_thread
【避坑】Azure AI 避免反向薅羊毛 https://hexo.limour.top/Azure-AI-prevents-reverse-wool-shearing https://hexo.limour.top/Azure-AI-prevents-reverse-wool-shearing Tue, 09 Jan 2024 05:55:40 GMT <h2 id="起因">起因</h2> <p>今天收到 Azure 的付费邮件,一看账单,好家伙,24.54$ ,比上个月暴涨 622%,给我 CPU 干烧了。</p> <p>赶紧去成本分析里按资源分类看上个月的扣费详情,然后就看到两个 10.33$ 的 <code>Contai 起因

今天收到 Azure 的付费邮件,一看账单,好家伙,24.54$ ,比上个月暴涨 622%,给我 CPU 干烧了。

赶紧去成本分析里按资源分类看上个月的扣费详情,然后就看到两个 10.33$ 的 Container Registry,分别位于我在 Azure AI Studio 里的两个不同项目所在区域。

一顿折腾,发现这个 Container Registry,有一年的免费试用期,但是免费限额是 31/个/天,一个 15 天刚好是 10.33$ 。

这 Azure 不讲武德,这样免费,头半个月根本不知道这东西要收费,等月末美滋滋去付账单时钱都已经扣完了。。。

特别是,这东西似乎是 Azure AI Studio 自动开通的,我根本没有用到过它。心情更糟了。

解决方案

赶紧去资源组里找到这两个容器注册表,全给删了。删除后不会对 Azure AI 的使用产生影响。

然后是想办法提工单,看能不能把这钱退回来。

最后保留的服务,不知道哪些还可以删

工单结果

透过案件了解到Container Registry是您不清楚的情况下创建的,且您已经将此资源进行了删除。考虑到您是首次使用Azure产品较不熟悉,且已经将资源删除,经过竭力向主管团队申请,现为您申请了相关费用的减免,即:
12/1/2023-12/31/2023期间由Container Registry – Standard产生的费用20.66 USD已经申请退回至您的信用卡,依据银行流程,款项约需要7-21个工作日抵达您的账户,届时请您查看。
同时,我们也查看了您当前的计费周期(1/1/2024-1/31/2024)的使用量报表,Container Registry – Standard未产生费用,还请您放心。

]]>
openai https://hexo.limour.top/Azure-AI-prevents-reverse-wool-shearing#disqus_thread
【记录】win10平台6G显存运行Qwen-1.8B https://hexo.limour.top/Running-Qwen-on-the-Win10-platform-with-6GB-of-video-memory https://hexo.limour.top/Running-Qwen-on-the-Win10-platform-with-6GB-of-video-memory Mon, 01 Jan 2024 03:11:36 GMT <p><a href="https://hexo.limour.top/go/#aHR0cHM6Ly9naXRodWIuY29tL2dnZXJnYW5vdi9sbGFtYS5jcHA=" rel="noopener external nofollow noreferrer">Ll Llama.cpp 能 CPU & GPU 环境混合推理,这里记录一下在 windows10 平台上运行 Qwen-1.8B 的过程,显卡是 1660Ti 。

准备模型

1
2
3
4
5
6
7
conda create -n llamaConvert python=3.10 git -c conda-forge
conda activate llamaConvert
cd D:\llama
git clone --depth=1 https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
python -m pip install -r requirements.txt
pip install tiktoken
1
2
3
4
python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='Qwen/Qwen-1_8B-Chat', local_dir=r'D:\qwen', ignore_patterns=['*.h5', '*.ot', '*.msgpack', '*.safetensors'])"
cd D:\qwen
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00001-of-00002.safetensors' "https://huggingface.co/Qwen/Qwen-1_8B-Chat/resolve/main/model-00001-of-00002.safetensors?download=true"
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00002-of-00002.safetensors' "https://huggingface.co/Qwen/Qwen-1_8B-Chat/resolve/main/model-00002-of-00002.safetensors?download=true"
1
2
3
cd D:\llama\llama.cpp
python convert-hf-to-gguf.py D:\qwen
# Model successfully exported to 'D:\qwen\ggml-model-f16.gguf'

运行模型

1
2
3
4
5
6
conda create -n llamaCpp libcublas cuda-toolkit git -c nvidia -c conda-forge
conda activate llamaCpp
cd D:\llama ; .\main.exe ## 检查能否正确运行
cd D:\llama ; .\quantize.exe --help ## 自己决定量化方式
.\quantize.exe D:\qwen\ggml-model-f16.gguf .\qwen-1_8-f16.gguf COPY
.\server.exe -m .\qwen-1_8-f16.gguf -c 4096 --n-gpu-layers 50 ## 调节 n-gpu-layers 平衡 CPU & GPU
  • 访问 http://127.0.0.1:8080 选择 Completion 进行测试

微调模型

附加 Yi-6B-Chat

Yi-6B是零一万物开源的双语语言模型,经过3T多语种语料库的训练,在语言理解、常识推理、阅读理解等方面有一定潜力。

1
2
3
4
5
6
cd D:\models\01yi
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00001-of-00003.safetensors' "https://huggingface.co/01-ai/Yi-6B-Chat/resolve/main/model-00001-of-00003.safetensors?download=true"
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00002-of-00003.safetensors' "https://huggingface.co/01-ai/Yi-6B-Chat/resolve/main/model-00002-of-00003.safetensors?download=true"
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00003-of-00003.safetensors' https://huggingface.co/01-ai/Yi-6B-Chat/resolve/main/model-00003-of-00003.safetensors?download=true
conda activate llamaConvert
python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='01-ai/Yi-6B-Chat', local_dir=r'D:\models\01yi', ignore_patterns=['*.h5', '*.ot', '*.msgpack', '*.safetensors'])"
1
2
3
4
5
6
7
8
conda activate llamaConvert
cd D:\llama\llama.cpp
python convert.py D:\models\01yi
# Wrote D:\models\01yi\ggml-model-f16.gguf
conda activate llamaCpp
cd D:\llama ; .\quantize.exe --help
.\quantize.exe D:\models\01yi\ggml-model-f16.gguf .\01yi-6b-Q4_K_M.gguf Q4_K_M
.\server.exe -m .\01yi-6b-Q4_K_M.gguf -c 4096 --n-gpu-layers 50

附加 百川2

1
2
3
4
5
6
7
8
9
10
11
cd D:\models\baichuan
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'pytorch_model.bin' "https://huggingface.co/baichuan-inc/Baichuan2-7B-Chat/resolve/main/pytorch_model.bin?download=true"
conda activate llamaConvert
python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='baichuan-inc/Baichuan2-7B-Chat', local_dir=r'D:\models\baichuan', ignore_patterns=['*.h5', '*.bin', '*.ot', '*.msgpack', '*.safetensors'])"
cd D:\llama\llama.cpp
python convert.py D:\models\baichuan
# Wrote D:\models\baichuan\ggml-model-f16.gguf
conda activate llamaCpp
cd D:\llama ; .\quantize.exe --help
.\quantize.exe D:\models\baichuan\ggml-model-f16.gguf .\baichuan-7b-Q3_K_M.gguf Q3_K_M
.\server.exe -m .\baichuan-7b-Q3_K_M.gguf -c 2048 --n-gpu-layers 30

附加 tigerbot-13b

tigerbot-13bchinese-llm-benchmark 上排名靠前。

1
2
3
4
5
6
7
8
9
10
11
cd D:\models\tigerbot
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'pytorch_model-00001-of-00003.bin' --max-download-limit=6M "https://huggingface.co/TigerResearch/tigerbot-13b-chat-v5/resolve/main/pytorch_model-00001-of-00003.bin?download=true"
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'pytorch_model-00002-of-00003.bin' --max-download-limit=6M "https://huggingface.co/TigerResearch/tigerbot-13b-chat-v5/resolve/main/pytorch_model-00002-of-00003.bin?download=true"
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'pytorch_model-00003-of-00003.bin' --max-download-limit=6M "https://huggingface.co/TigerResearch/tigerbot-13b-chat-v5/resolve/main/pytorch_model-00003-of-00003.bin?download=true"
conda activate llamaConvert
python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='TigerResearch/tigerbot-13b-chat-v5', local_dir=r'D:\models\tigerbot', ignore_patterns=['*.h5', '*.bin', '*.ot', '*.msgpack', '*.safetensors'])"
cd D:\llama\llama.cpp
python convert.py D:\models\tigerbot --padvocab
cd D:\llama ; .\quantize.exe --help
.\quantize.exe D:\models\tigerbot\ggml-model-f16.gguf D:\models\tigerbot-13B-Chat-Q4_K_M.gguf Q4_K_M
.\server.exe -m D:\models\tigerbot-13B-Chat-Q4_K_M.gguf -c 4096

感觉 6G 显存下,比较好用的是 Yi-6B-Chat-Q4_K_M
tigerbot-13b 在 R5 5600H 上推理速度 4.6 tokens/s,CPU 使用率 60%,频率 3.5GHz,应该是内存带宽瓶颈

附加 在 Colab 上量化

安装 llama.cpp

1
2
3
!git clone --depth=1 https://github.com/ggerganov/llama.cpp.git
%cd /content/llama.cpp
!LLAMA_CUDA=1 make -j

计算 imatrix

1
2
3
4
5
6
%cd /content
!wget -O transient.txt.gz https://huggingface.co/datasets/Limour/b-corpus/resolve/main/00-preview/00-transient.txt.gz?download=true
!gunzip transient.txt.gz
!mkdir -p /content/CausalLM-14B-GGUF
!wget -O /content/CausalLM-14B-GGUF/causallm_14b.Q8_0.gguf https://huggingface.co/TheBloke/CausalLM-14B-GGUF/resolve/main/causallm_14b.Q8_0.gguf?download=true
!/content/llama.cpp/imatrix -m /content/CausalLM-14B-GGUF/causallm_14b.Q8_0.gguf -f /content/transient.txt -ngl 36

登录拥抱脸

1
2
3
4
5
6
from google.colab import userdata
from huggingface_hub import login
# login(token=os.environ.get("HF_TOKEN"), write_permission=True)
login(token=userdata.get('HF_TOKEN'), write_permission=True)
# from huggingface_hub import notebook_login
# notebook_login()

(跳过) 转换模型

1
2
3
4
5
6
7
%cd llama.cpp
!python -m pip install -r requirements.txt
!pip install tiktoken
from huggingface_hub import snapshot_download
!mkdir -p ~/CausalLM
snapshot_download(repo_id='CausalLM/7B', local_dir=r'/content/CausalLM', ignore_patterns=['*.h5', '*.ot', '*.msgpack', '*.safetensors'])
!python convert.py --vocab-type bpe --pad-vocab --outtype f16 /content/CausalLM

量化模型

1
!/content/llama.cpp/quantize --allow-requantize --imatrix /content/imatrix.dat /content/CausalLM-14B-GGUF/causallm_14b.Q8_0.gguf /content/CausalLM-14B-GGUF/causallm_14b.IQ3_XS.gguf IQ3_XS

上传模型

1
2
3
4
5
6
7
from huggingface_hub import HfApi
api = HfApi()
api.upload_file(
path_or_fileobj="/content/CausalLM-14B-GGUF/causallm_14b.IQ3_XS.gguf",
path_in_repo="causallm_14b.IQ3_XS.gguf",
repo_id="Limour/CausalLM-14B-GGUF"
)
]]>
llama https://hexo.limour.top/Running-Qwen-on-the-Win10-platform-with-6GB-of-video-memory#disqus_thread
【记录】轻量个人导航页面 Flare https://hexo.limour.top/Lightweight-personal-navigation-page-Flare https://hexo.limour.top/Lightweight-personal-navigation-page-Flare Sun, 31 Dec 2023 17:18:28 GMT <p><a href="https://hexo.limour.top/go/#aHR0cHM6Ly9naXRodWIuY29tL3NvdWx0ZWFyeS9kb2NrZXItZmxhcmU=" rel="noopener external nofollow noreferrer Flare 是一款轻量、快速、美观的个人导航页面,适用于 HomeLab 或其他注重私密的场景。

1
2
3
mkdir -p ~/app/flare && cd ~/app/flare && nano docker-compose.yml
sudo docker-compose up -d # flare:5005
sudo docker-compose logs # 获取登录密码
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
version: '3.6'

services:
flare:
image: soulteary/flare
restart: always
# 默认无需添加任何参数,如有特殊需求
# 可阅读文档 https://github.com/soulteary/docker-flare/blob/main/docs/advanced-startup.md
# 启用账号登录模式
command: flare --disable_login=0
environment:
# 如需开启用户登录模式,需要先设置 `nologin` 启动参数为 `0`
# 如开启 `nologin`,未设置 FLARE_USER,则默认用户为 `flare`
- FLARE_USER=LimourFlare
# 指定你自己的账号密码,默认生成的密码强度堪忧
- FLARE_PASS=your_password
- FLARE_OFFLINE=1
- FLARE_MINI_REQUEST=1
volumes:
- ./app:/app

networks:
default:
external: true
name: ngpm
]]>
docker ngpm homepage https://hexo.limour.top/Lightweight-personal-navigation-page-Flare#disqus_thread
【记录】Win10平台使用MLC-LLM编译Qwen-1.8B-Chat https://hexo.limour.top/Compile-Qwen-1.8B-Chat-using-MLC-LLM-on-Win https://hexo.limour.top/Compile-Qwen-1.8B-Chat-using-MLC-LLM-on-Win Sat, 09 Dec 2023 04:24:07 GMT <p><a href="https://hexo.limour.top/go/#aHR0cHM6Ly9naXRodWIuY29tL21sYy1haS9tbGMtbGxt" rel="noopener external nofollow noreferrer">MLC-LLM</a MLC-LLM 是一种大模型高性能通用部署解决方案,可以通过预编译加速使用本机API原生部署任何大型语言模型。该项目的使命是利用ML编译技术,使每个人都能在其设备上本地开发、优化和部署AI模型。
Qwen-1.8B 是阿里云研发的通义千问大模型系列的18亿参数规模的模型。在Qwen-1.8B的基础上,使用对齐机制打造了基于大语言模型的AI助手 Qwen-1.8B-Chat

配置环境

1
2
3
4
5
6
7
8
9
10
11
12
conda create -n mlc_llm python numpy pytorch transformers scipy timm git -c pytorch -c conda-forge
conda activate mlc_llm
python -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly
python -c "import tvm; print('\n'.join(f'{k}: {v}' for k, v in tvm.support.libinfo().items()))"
python -c "import tvm; print(tvm.vulkan().exist)"
cd D:\mlc-llm
git clone --depth=1 -b main --single-branch https://github.com/mlc-ai/mlc-llm.git
cd .\mlc-llm\
git submodule sync
git submodule update --init --recursive --depth=1
pip install .
python -m mlc_llm.build --help

准备模型

1
2
3
4
python -c "from huggingface_hub import snapshot_download; snapshot_download(repo_id='Qwen/Qwen-1_8B-Chat', local_dir='D:\mlc-llm\qwen', ignore_patterns=['*.h5', '*.ot', '*.msgpack', '*.safetensors'])"
cd D:\mlc-llm\qwen
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00001-of-00002.safetensors' "https://huggingface.co/Qwen/Qwen-1_8B-Chat/resolve/main/model-00001-of-00002.safetensors?download=true"
D:\aria2\aria2c.exe --all-proxy='http://127.0.0.1:7890' -o 'model-00002-of-00002.safetensors' "https://huggingface.co/Qwen/Qwen-1_8B-Chat/resolve/main/model-00002-of-00002.safetensors?download=true"

编译模型

1
2
cd D:\mlc-llm\dist
python -m mlc_llm.build --model "D:\mlc-llm\qwen" --target vulkan --quantization q0f16 --use-safetensors
]]>
llama https://hexo.limour.top/Compile-Qwen-1.8B-Chat-using-MLC-LLM-on-Win#disqus_thread
【探索】外科打结法中的等价操作 https://hexo.limour.top/Equivalent-operations-in-surgical-knot-tying https://hexo.limour.top/Equivalent-operations-in-surgical-knot-tying Sat, 02 Dec 2023 06:47:05 GMT <p>手术中的止血和缝合,均需要进行结扎,而结扎是否牢固,又与打结有密切关系,结一定要打得牢固,不能松动、滑脱。<br> 常用的结扣是方结,结扎后极为牢固,在手术中最常用。而打方结时,手法顺序错误就容易打成假结或滑结。因此这里将探讨基础打结手法的等价性,帮助快速理解不同手法所成结 手术中的止血和缝合,均需要进行结扎,而结扎是否牢固,又与打结有密切关系,结一定要打得牢固,不能松动、滑脱。
常用的结扣是方结,结扎后极为牢固,在手术中最常用。而打方结时,手法顺序错误就容易打成假结或滑结。因此这里将探讨基础打结手法的等价性,帮助快速理解不同手法所成结的本质。
除不易混淆的外科结外,无论是单手打结还是持钳打结,均由基础动作组合而成,基础动作所成的结都对应纽结理论中的三叶结。三叶结有两种,它们互成镜像,彼此不相同痕,分别称为左手三叶结和右手三叶结。因此无论用哪种手法,最后一定能对应到两种三叶结上。

两种三叶结

右手勾法对应右手三叶结

左手勾法对应左手三叶结

右手掏法对应左手三叶结

左手掏法对应右手三叶结

镊右手定则法对应右手三叶结

镊左手定则法对应左手三叶结

因此,右手勾法、左手掏法、镊右手定则法三者等价;左手勾法、右手掏法、镊左手定则法三者等价。任意组合两种基础打结动作打出不同的两种三叶结即可组成一个正确的方结。

]]>
探索 https://hexo.limour.top/Equivalent-operations-in-surgical-knot-tying#disqus_thread