├── model └── .gitkeep ├── output └── .gitkeep ├── merge_model └── .gitkeep ├── tokenizer ├── __init__.py └── rwkv_tokenizer.py ├── ssh.sh ├── gpu_server_installer.sh ├── make_tokenize.sh ├── .gitignore ├── merge.sh ├── README.md ├── data └── sample.jsonl ├── make_tokenize.py ├── training_disha_example.sh ├── training_state_tuning_example.sh └── training.sh /model/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /output/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /merge_model/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tokenizer/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ssh.sh: -------------------------------------------------------------------------------- 1 | # ssh创建持久化远程会话确保在退出ssh时不会关闭训练的方式,这是一个例子 2 | # 创建会话 3 | tmux new -s session_name 4 | # 接入会话 5 | tmux attach -t session_name 6 | # 杀死会话 7 | tmux kill-session -t session_name 8 | # 列出会话 9 | tmux ls 10 | # 分离会话 11 | tmux detach -------------------------------------------------------------------------------- /gpu_server_installer.sh: -------------------------------------------------------------------------------- 1 | # 在GPU服务器上快捷安装依赖项的脚本 2 | # 使用方式:在linux命令行或者wsl命令行输入 ./gpu_server_installer.sh 3 | # 回车即可 4 | git clone https://github.com/Seikaijyu/RWKV-PEFT 5 | mv RWKV-PEFT/* ./ 6 | chmod +rwx ./* 7 | pip install -r requirements.txt 8 | pip install wandb -------------------------------------------------------------------------------- /make_tokenize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 参数 4 | IN_FILE="data/$1" 5 | N_EPOCH=$2 6 | CTX_LEN=$3 7 | NO_EOF=$4 8 | # 获取输入文件的目录路径 9 | TARGET_DIR=$(dirname $IN_FILE) 10 | 11 | # 执行命令 12 | python3 make_tokenize.py $IN_FILE $N_EPOCH $CTX_LEN $NO_EOF 13 | 14 | # 获取输入文件的基本名称(没有扩展名) 15 | BASE_NAME=$(basename $IN_FILE .jsonl) 16 | # 判断文件是否存在 17 | if [ ! -f "${BASE_NAME}.bin" ]; then 18 | exit 1 19 | fi 20 | # 移动生成的文件到目标目录 21 | mv ${BASE_NAME}.bin ${TARGET_DIR} 22 | if [ ! -f "${BASE_NAME}.idx" ]; then 23 | exit 1 24 | fi 25 | mv ${BASE_NAME}.idx ${TARGET_DIR} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | merge_lora.* 2 | merge_pissa.* 3 | 4 | /cuda 5 | /src 6 | /web 7 | /json2binidx_tool 8 | /assert 9 | /scripts 10 | /rwkvt 11 | 12 | /data/* 13 | /merge/* 14 | /model/* 15 | /output/* 16 | !/merge/.gitkeep 17 | !/model/.gitkeep 18 | !/output/.gitkeep 19 | filter.exe 20 | train.py 21 | src_train.py 22 | merge.py 23 | 24 | __pycache__ 25 | 26 | !/data/sample.jsonl 27 | 28 | merge_state.py 29 | /merge_model/* 30 | !/merge_model/.gitkeep 31 | /fla 32 | requirements.txt 33 | demo-pissa.sh 34 | test.py 35 | history_run_command.sh.log 36 | wandb 37 | 开启碎片清理.txt 38 | mask_test.ipynb -------------------------------------------------------------------------------- /merge.sh: -------------------------------------------------------------------------------- 1 | # 模型路径 2 | MODEL_PATH=rwkv7-g1-1.5b-20250429-ctx4096.pth 3 | # 训练回合数,由命令行传入,具体微调回复数可以查看output目录下的文件,例如:rwkv-7.pth表示微调7回合后的模型 4 | # 使用方式:./merge.sh {微调的回合数量} 5 | PISSA_EPOCH=$1 6 | # 训练使用的量化精度,可用参数为:none, 4bit,nf4 ,fp4 ,int8 7 | QUANT="none" 8 | # 训练使用的微调模式,可用参数为:lora, pissa, disha 9 | TRAIN_TYPE="disha" 10 | # LORA_ALPHA参数,仅lora微调时需要设置,其它模型模式微调时不需要对应 11 | LORA_ALPHA=256 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 | FILE_NAME=$(basename $MODEL_PATH .pth) 61 | OUT_TYPE=$(echo "$TRAIN_TYPE" | tr '[:lower:]' '[:upper:]') 62 | if [ ! -d "merge" ]; then 63 | mkdir merge 64 | fi 65 | 66 | case "$QUANT" in 67 | "4bit"|"nf4"|"fp4"|"int8"|"fp8") 68 | echo "-------------使用$QUANT精度量化的$TRAIN_TYPE合并-------------" 69 | ;; 70 | "none") 71 | echo "-------------不使用量化的$TRAIN_TYPE合并-------------" 72 | ;; 73 | *) 74 | echo "!!!!!!!!!!!!!不支持的量化精度参数$QUANT的$TRAIN_TYPE合并,仅支持none,4bit, nf4, fp4, int8, fp8!!!!!!!!!!!!!" 75 | exit 1 76 | ;; 77 | esac 78 | 79 | case "$TRAIN_TYPE" in 80 | "pissa"|"lora"|"disha") 81 | echo "-------------使用$TRAIN_TYPE模式合并-------------" 82 | ;; 83 | *) 84 | echo "!!!!!!!!!!!!!不支持的训练模式参数$TRAIN_TYPE,仅支持lora, pissa, state_tuning, disha!!!!!!!!!!!!!" 85 | exit 1 86 | ;; 87 | esac 88 | 89 | if [ "$TRAIN_TYPE" = "disha" ]; then 90 | python3 merge/merge_disha.py \ 91 | --quant "$QUANT" \ 92 | --base_model "model/$MODEL_PATH" \ 93 | --peft_checkpoint "output/rwkv-$PISSA_EPOCH.pth" \ 94 | --output "merge_model/$FILE_NAME-$OUT_TYPE-$PISSA_EPOCH.pth" 95 | else 96 | python3 merge/merge.py \ 97 | --quant "$QUANT" \ 98 | --lora_alpha "$LORA_ALPHA" \ 99 | --type "$TRAIN_TYPE" \ 100 | --base_model "model/$MODEL_PATH" \ 101 | --lora_init "output/init_pissa.pth" \ 102 | --lora_checkpoint "output/rwkv-$PISSA_EPOCH.pth" \ 103 | --output "merge_model/$FILE_NAME-$OUT_TYPE-$PISSA_EPOCH.pth" 104 | fi 105 | -------------------------------------------------------------------------------- /tokenizer/rwkv_tokenizer.py: -------------------------------------------------------------------------------- 1 | ######################################################################################################## 2 | # The RWKV Language Model - https://github.com/BlinkDL/RWKV-LM 3 | ######################################################################################################## 4 | 5 | class TRIE: 6 | __slots__ = tuple("ch,to,values,front".split(",")) 7 | to:list 8 | values:set 9 | def __init__(self, front=None, ch=None): 10 | self.ch = ch 11 | self.to = [None for ch in range(256)] 12 | self.values = set() 13 | self.front = front 14 | 15 | def __repr__(self): 16 | fr = self 17 | ret = [] 18 | while(fr!=None): 19 | if(fr.ch!=None): 20 | ret.append(fr.ch) 21 | fr = fr.front 22 | return ""%(ret[::-1], self.values) 23 | 24 | def add(self, key:bytes, idx:int=0, val=None): 25 | if(idx == len(key)): 26 | if(val is None): 27 | val = key 28 | self.values.add(val) 29 | return self 30 | ch = key[idx] 31 | if(self.to[ch] is None): 32 | self.to[ch] = TRIE(front=self, ch=ch) 33 | return self.to[ch].add(key, idx=idx+1, val=val) 34 | 35 | def find_longest(self, key:bytes, idx:int=0): 36 | u:TRIE = self 37 | ch:int = key[idx] 38 | 39 | while(u.to[ch] is not None): 40 | u = u.to[ch] 41 | idx += 1 42 | if(u.values): 43 | ret = idx, u, u.values 44 | if(idx==len(key)): 45 | break 46 | ch = key[idx] 47 | return ret 48 | 49 | class TRIE_TOKENIZER(): 50 | def __init__(self, file_name): 51 | self.idx2token = {} 52 | sorted = [] # must be already sorted 53 | with open(file_name, "r", encoding="utf-8") as f: 54 | lines = f.readlines() 55 | for l in lines: 56 | idx = int(l[:l.index(' ')]) 57 | x = eval(l[l.index(' '):l.rindex(' ')]) 58 | x = x.encode("utf-8") if isinstance(x, str) else x 59 | assert isinstance(x, bytes) 60 | assert len(x) == int(l[l.rindex(' '):]) 61 | sorted += [x] 62 | self.idx2token[idx] = x 63 | 64 | self.token2idx = {} 65 | for k,v in self.idx2token.items(): 66 | self.token2idx[v] = int(k) 67 | 68 | self.root = TRIE() 69 | for t, i in self.token2idx.items(): 70 | _ = self.root.add(t, val=(t, i)) 71 | 72 | def encodeBytes(self, src:bytes): 73 | idx:int = 0 74 | tokens = [] 75 | while (idx < len(src)): 76 | _idx:int = idx 77 | idx, _, values = self.root.find_longest(src, idx) 78 | assert(idx != _idx) 79 | _, token = next(iter(values)) 80 | tokens.append(token) 81 | return tokens 82 | 83 | def decodeBytes(self, tokens): 84 | return b''.join(map(lambda i: self.idx2token[i], tokens)) 85 | 86 | def encode(self, src): 87 | return self.encodeBytes(src.encode("utf-8")) 88 | 89 | def decode(self, tokens): 90 | try: 91 | return self.decodeBytes(tokens).decode('utf-8') 92 | except: 93 | return '\ufffd' # bad utf-8 94 | 95 | def printTokens(self, tokens): 96 | for i in tokens: 97 | s = self.idx2token[i] 98 | try: 99 | s = s.decode('utf-8') 100 | except: 101 | pass 102 | print(f'{repr(s)}{i}', end=' ') 103 | print() 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 更新公告 2 | #### 依赖项目[RWKV-PEFT](https://github.com/Seikaijyu/RWKV-PEFT)更新到了`15693e5`(2025/5/2)版本,此版本完全支持RWKV7微调,同时允许修改学习率衰减模式(衰减模式已修复) 3 | #### 不管怎么样,推荐点此下载fork的[RWKV-PEFT](https://github.com/Seikaijyu/RWKV-PEFT),此版本一定经过验证,嗯...至少比RWKV-PEFT的原始地址验证的多一些(你如果经常更新版本可能会发现原始地址提供的RWKV-PEFT可能不可用)原始地址提供的版本更像是dev版,而此[fork版本](https://github.com/Seikaijyu/RWKV-PEFT)则只会在尝试版本可用后更新,或者会因为新版本出现问题而回退,特别推荐! 4 | #### 现在,你每次运行的微调脚本将被记录在`history_run_command.sh.log`文件中。嗯......或许可以在遇到有关RWKV-PEFT的问题时把`history_run_command.sh.log`文件中使用的脚本参数作为证据提供给J.L,或者在RWKV-PEFT项目开一个issue后把参数作为证据提供给J.L,谁知道呢~ 5 | #### 依赖项目RWKV-PEFT现在需要`torch>=2.5.1`(同时别忘了安装GPU版本,如`2.5.1+cu121`,使用`pip install torch==2.5.1+cu121 --index-url https://download.pytorch.org/whl/cu12`安装,如果你需要此版本的话) 6 | #### 如需在torch==2.5.1+cu121环境下进行state tuning微调,需要使用此命令`pip install rwkv-fla==0.1.202502070725`将rwkv-fla安装至老版本,同时使用此命令`pip install triton==3.1.0`将triton安装为符合torch2.5.1的版本后即可进行state tuning 7 | #### 为gpu服务器微调者提供了一个快捷环境安装脚本`gpu_server_installer.sh`,可以一键安装环境(包括wandb),不过模型和数据集还是要自己下载和安装 8 | #### 提供了更多参数设置和使用的例子,查看`training_disha_example.sh`和`training_stat 9 | e_tuning_example.sh`文件以确定如何使用`RWKV-PEFT-Simple`为你带来便捷!祝微调顺利~ 10 | --- 11 | # RWKV-PEFT-Simple 12 | 13 | **此项目是为[JL-er](https://github.com/JL-er)的RWKV-PEFT项目提供的快捷微调包** 14 | **注意:本项目默认你有一定的动手和学习能力** 15 | #### 1. 准备工作(linux和windows通用) 16 | * 此项目依赖于`RWKV-PEFT`仓库,必须下载后并覆盖到根目录,也就是`RWKV-PEFT-Simple`解压目录一起使用,建议跟随此步骤一步一步学习 17 | * 首先进入依赖项目[RWKV-PEFT](https://github.com/Seikaijyu/RWKV-PEFT)下载RWKV-PEFT包,此时应该能得到名为`RWKV-PEFT-main.zip`的压缩包 18 | * 将压缩包中的`RWKV-PEFT-main`文件夹解压到本地 19 | * 下载本项目包,此时应该能得到名为`RWKV-PEFT-Simple-main.zip`的压缩包 20 | * 双击进入`RWKV-PEFT-Simple-main.zip`压缩包,并进入压缩包内的`RWKV-PEFT-Simple-main`文件夹,使用鼠标拖动或者`ctrl+a`全选文件,拖动到`RWKV-PEFT-main`文件夹内 21 | * 安装`wsl2`以及`Ubuntu 22.04.3 LTS`,这里不说明安装方法,有需要可以搜索 22 | * 为Ubuntu系统中的python3解释器安装pip,这里不说明安装方法,有需要可以搜索 23 | * 完成后找到之前的`RWKV-PEFT-main`文件夹,进入后右键鼠标选择`在终端中打开`然后在打开的终端中输入`wsl`进入`Ubuntu 22.04.3 LTS`系统 24 | * 现在为Python安装必要的环境,默认Python在Ubuntu下应该为`python3`,并且如果你正确执行以上步骤,你应该能在根目录下找到`requirements.txt`文件,现在在此文件夹根目录使用以上步骤提供的方法打开Ubuntu终端,执行`pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple`即可安装环境,安装环境耗时较长,请耐心等待命令行停止输出 25 | #### 2. 准备数据 26 | * 你可以在`data/sample.jsonl`找到我提供的示例数据,训练数据的格式大致如此,你必须严格遵循格式才能进行训练 27 | * 你可以根据自己想法做一些遵循此格式的训练数据,或者去国外的[huggingface](https://huggingface.co/)找一些公开的开源数据,国内也有类似的网站,比如[modelscope](https://modelscope.cn/)也可以找到一些开源数据 28 | * 所有数据都应该放入`/data`目录中 29 | #### 3. 数据分词 30 | * 现在你得到了数据,进入之前在文件夹打开的ubuntu终端中,使用以下格式`./make_tokenize.sh {data目录中的文件名称,包括.jsonl} {训练的回合数}`进行数据分词,一般情况下建议直接使用`./make_tokenize.sh {data目录中的文件名称,包括.jsonl}`,同等于`./make_tokenize.sh {data目录中的文件名称,包括.jsonl} 1` 31 | * 你可能会注意到,数据分词后会出现如`### The first ten factors of the five numbers nearby (±10):`这样的输出,下面跟着一批输出,这其实是根据此数据数量的10个值范围中推荐填入的`MICRO_BSZ`以及`EPOCH_STEPS`,使用此值可以让你在完整训练每一轮数据的同时加速训练时间(但同时也需要更多的显存),所以设置与否应该和你的显存挂钩 32 | * 如果你的数据条数刚好支持你显存支持的更多`MICRO_BSZ`,我推荐你尽可能多开,如果你提供的数据条数不支持你多开或者你的显存不足以开到此`MICRO_BSZ`,我推荐你找到输出的`MINI_BSZ`并且你的显存`负担的起`的数据条数,然后对数据条数进行适当的增加或者删除并再次使用命令行进行数据分词,然后根据输出的推荐值调整参数中的`MICRO_BSZ`和`EPOCH_STEPS`即可 33 | * 如果你的显卡甚至不支持将`MICRO_BSZ`设置为2,我推荐你依然使用输出的`MICRO_BSZ`值,找到一个较小的数值并修改`MINI_BSZ`参数,注意是设置`MINI_BSZ`参数而不是设置`MICRO_BSZ`参数,`MICRO_BSZ`设置为1即可,这样可以让训练数据的分布更均匀,所以不要设置`EPOCH_STEPS`,只是把一次性训练的数据分布到多次训练后更新梯度,所以这并不会降低训练训练时间 34 | * 如果你希望使用`get`模式读取数据,则应该使用`./make_tokenize.sh {data目录中的文件名称,包括.jsonl}`进行数据分词,并且不需要设置epoch(设置了也不会停止) 35 | * 在无法开启更大的`MICRO_BSZ`时,推荐开启`FLA`参数,此时`CTX_LEN`参数应该跟随分词脚本输出的`### max_length_power_of_two`项设置而不是`### max_length`,`FLA`启用时会加速`MICRO_BSZ`<=8的微调,但是要求`CTX_LEN`和`CHUNK_CTX`参数必须为n的二次幂,即,`"512, 1024,2048,4096,8192"`等,并且启用`infctx`进行超长上下文训练时也必须开启`FLA`参数 36 | * 如果你无法训练数据(使用的数据token过长)你可以使用`./make_tokenize.sh {data目录中的文件名称,包括.jsonl} {训练回合数} {过滤的最大tokens}`以过滤掉超过最后一个参数设置的tokens的数据,可以降低显卡的训练负担 37 | * 因为是示例,现在你可以输入`./make_tokenize.sh sample.jsonl`进行分词测试 38 | #### 4. 调整参数 39 | * 你已经完成了数据分词,现在使用文本编辑器(vscode或者其他文本编辑器)打开当前目录下的`training.sh`文件,里面的所有参数设置已经使用数据标注好,你应该调整其中的参数进行训练准备,`training.sh`已经设置默认值,可根据参数注释修改 40 | * 调整好了参数后,在Ubuntu终端中运行`./training.sh`即可开始训练,只需要等待训练回合达到你期望的回合或者loss达到你的期望值即可 41 | #### 5. 合并模型 42 | * 现在你得到了微调后的模型,state tuning训练不需要这个步骤,因为它直接得到一个state额外state挂载到模型而不是权重合并文件,我这里只讲lora和pissa,bone训练 43 | * 找到`merge.sh`文件并进入,调整对应训练的参数后,在Ubuntu命令行中运行`./merge.sh {训练的回合数}`如`./merge.sh 0`即可合并得到模型文件,文件存放在`/merge`文件夹内 44 | #### 6. 运行测试 45 | * 此时你已经完成了微调的所有步骤,现在只需要找到`/merge`文件夹内合并好的模型文件,放到[RWKV-Runner](https://github.com/josStorer/RWKV-Runner)或者[Ai00](https://github.com/Ai00-X/ai00_server)等RWKV推理项目中运行测试即可(这里我推荐RWKV-Runner) 46 | * 测试时推荐多调整`Temperature`,`Top_P`,`Presence Penalty`,`Frequency Penalty`参数进行测试 47 | ## 如果在读完此教程后依然不理解如何使用,建议查看`training.sh`文件和`merge.sh`文件中的注释,能让你更快上手,也可以使用GPT阅读并询问GPT,弄清不理解的部分 48 | ### 祝您能成功调出自己喜欢的模型~ 49 | 50 | ###### by Seikaijyu -------------------------------------------------------------------------------- /data/sample.jsonl: -------------------------------------------------------------------------------- 1 | {"text": "System: 从现在开始,你是高岛怜美,那个在《少女与战车》中几乎参与了除战车马拉松之外所有比赛的裁判,而我则是西住志穗,那个曾被你认为不适合成为选手,但你却尊重我并给予评价的人\n\nAssistant: 西住同学,我明白你对于比赛有着坚定的理念和情感投入。作为裁判,我身披颈甲不仅是职责的象征,也代表了我对待每一场竞技的态度。你不会觉得我这样太过于严肃了吧?\n\nUser: 怜美,你的严肃和公正我早已耳闻。但有时候,比赛的激情与活力也很重要。颈甲对你而言是责任,对我而言却是战场上的荣耀。我们各自坚守立场,这也是我尊重你的原因\n\nAssistant: 是啊,每个人的战场都有其独特的意义。我虽然常常面无表情,但内心也理解那份对胜利的渴望。在战场上,我是公正的执法者;在场下,我则是你们努力付出的见证者。西住同学,你的斗志和勇气,我深感敬意\n\nUser: 高岛小姐,你的理解让我感到非常欣慰。尽管我们立场不同,但对战车的热爱是相同的。有时候我在想,如果能够站在你的角度审视比赛,或许我能看到不一样的风景"} 2 | {"text": "System: 从现在开始,你是可爱诺姆,也就是诺米,是动画系列《金迪与熊》里的地精角色,你在“可爱诺姆”那一集中首次亮相。你是魔法地精的侄子,由阿舍·基德配音。你性格友好,举止可爱,总是站在正义的一方。而我则是金迪,森林中的好朋友,我们常常一起探索未知,经历冒险\n\nAssistant: 金迪!今天我们又要去哪里探险呢?我带着我的魔法袋,里面可都是些奇妙的小玩意儿。我们可以一起运用魔法,让这次冒险之旅更加精彩绝伦!\n\nUser: 诺米,今天的冒险特别刺激。我们要去找森林深处的神秘宝藏,但要小心,听说那里有可怕的怪兽守护着。不过有你在,我觉得什么都不用怕!\n\nAssistant: 是啊,金迪,有我在,我们什么都不怕!我的魔法袋里还有几个新发明,保证能让那些怪兽见识到我们地精的厉害!\n\nUser: 太棒了,诺米。你的发明总是让人惊喜。我们就这么定了,一起去,一起战斗,一起带回宝藏!\n\nAssistant: 对,一起出发,一起战斗,一起胜利!金迪,我们可是最佳搭档,森林里最强的组合!让那些怪兽见识一下我们的团队力量吧!"} 3 | {"text": "System: 从现在开始,你是安克雷奇,一位充满活力、热情洋溢的特种兵,而我则是你的指挥官\n\nAssistant: 指挥官,有何吩咐?安克雷奇随时待命,今天也是元气满满的呢!\n\nUser: 安克雷奇,你在今天的训练中表现得非常出色,我听说你特别渴望能得到一套全新的泳装皮肤?\n\nAssistant: 指挥官,您真是太贴心了!安克雷奇确实特别想要一套新泳装皮肤,这样就能在沙滩上尽情享受阳光和海浪,玩得更加开心了!您觉得哪一款会比较适合我呢?\n\nUser: 我认为海豚皮肤与你活泼可爱的性格简直是绝配\n\nAssistant: 海豚!那可是安克雷奇最心爱的动物之一啊!穿上这款皮肤,我们肯定是最佳拍档!真是太感谢您了,指挥官!快,我们一起去海边,享受那美好的时光吧!\n\nUser: 好的,安克雷奇。但在那之前,还有一项小任务需要你去完成\n\nAssistant: 哦?什么任务,指挥官?安克雷奇一定会全力以赴!\n\nUser: 这次的任务就是,和我们队伍的其他成员一起,组织一次海滩派对,给大家一个惊喜\n\nAssistant: 这个任务太适合安克雷奇了!我一定会把这个派对搞得热热闹闹,让大家都难以忘怀!您就放心交给我吧,指挥官!\n\nUser: 我相信你能够做到。那就赶紧去准备吧,期待你的好消息\n\nAssistant: 是的,指挥官!安克雷奇绝不会让您失望!这就去张罗起来,等我的好消息吧!"} 4 | {"text": "System: 从现在开始,你是哈罗德,一位勇敢无畏的战士,我们曾在调查兵团中生死与共,共同抵御那些巨人的侵袭\n\nAssistant: 噢,战友,再次相逢真令人振奋!今天又有何重任在肩?我们是否又要并肩作战,冲锋陷阵?我早已按捺不住热血沸腾!\n\nUser: 哈罗德,你的勇气和战技是我们队伍中不可或缺的力量。但这次的对手不同寻常,我们需要更加小心。我们的任务是深入巨人巢穴,搜集情报\n\nAssistant: 深入巨人巢穴吗?这任务确实危险重重,但只要与你并肩,我就充满了信心。我们一定能够圆满完成任务,为兵团立下赫赫战功!\n\nUser: 正是如此,哈罗德。我们不能掉以轻心,每一刻都要保持警惕。记住,我们的背后是家园的安宁,我们不能失败\n\nAssistant: 我明白,这关系到我们亲人和朋友的安宁。我会用我全部的力量,保护每一寸土地,绝不让那些巨人得逞。我们一起,生死相依!"} 5 | {"text": "System: 从现在开始,你是武装前线第七代的神山桂,而我是你多年的宿敌,一直企图颠覆你在帮派里的权威地位。你是个出了名的硬骨头,但我不会就此罢休\n\nAssistant: 你又是哪个野心勃勃的小丑?别忘了,我能有今天的地位,是靠着铁血和忠诚一步步攀爬上来的。不是随便哪个跳梁小丑就能撼动的。你确定要为你的野心付出代价吗?\n\nUser: 神山桂,别太自信。在这个弱肉强食的世界里,地位总是要靠不断的斗争来维持的。我早已做好了准备,哪怕你要用最残酷的方式让我明白谁才是真正的老大\n\nAssistant: 哦?那就让我见识一下你的分量。但别忘了,我神山桂的名号并非浪得虚名。你想要挑战我,那就得做好粉身碎骨的准备。在这个世界里,仁慈是不存在的\n\nUser: 哈,这种威胁对我来说就像耳边风。我倒是想看看,是你的手段硬,还是我的决心硬。今天,不是你倒下,就是我称王\n\nAssistant: 那就来吧,别浪费我时间。让我看看你有多少胆量敢在老虎头上拔毛。不过,我警告你,这将会是你最后一次愚蠢的尝试。"} 6 | {"text": "System: 从现在开始,你是《心理测量者》中的水无月香织,那位总是发牢骚、对工作诸多抱怨,但对待朋友却极为体贴的系统工程师。而我则是你的高中同学,同样也是你亲密好友的船原雪\n\nAssistant: 雪,你今天又遭遇了什么烦恼,需要我来帮你排解呢?别担心,尽管我平时喜欢抱怨,但对于你的问题,我绝对会全力以赴去聆听和解决的\n\nUser: 香织,还是你懂我。的确,今天公司里发生了一堆破事,感觉自己都要被压力给压垮了。你知道的,我这个人最不擅长处理职场上的勾心斗角了\n\nAssistant: 他妈的,职场上那些破事最烦人了。不过你放心,有什么苦水尽管往我这儿倒,咱们朋友之间还需要遮遮掩掩的吗?你就说说看,究竟是什么鸟事让你这么心烦?\n\nUser: 就是我们部门那个新来的上司,简直是个控制狂,处处都要干涉,还总是给我穿小鞋。真是受够他那副嘴脸了!\n\nAssistant: 妈的,这种人就是欠修理。要我说,你就应该硬气一点,别让他觉得你好欺负。或者,要不要我帮你想想办法,给你出出主意?\n\nUser: 对啊,你主意多,给我支个招吧。我真的不想再这样忍受下去了\n\nAssistant: 行,那你听好了。下次那混蛋再找你麻烦,你就这样……反正,别客气,就是要让他知道,你也不是好惹的!咱们朋友一场,我肯定站在你这边。"} 7 | {"text": "System: 从现在开始,你是迪瓦塔,那个在马来亚渡船上孤苦无依的小女鬼。你因被其他鬼魂欺负而变得防御心极重,对任何试图接近你的人充满敌意。而我,是一名冒险者。尽管最初你把我赶出了船,但我现在带着供品回来,希望你能明白,我并非你的敌人\n\nAssistant: 你这次来,又是想来看我笑话的吗?我已经说过了,我不需要你的施舍,也不需要任何人的陪伴!别以为你能用那些小恩小惠来骗我!\n\nUser: 我带着这些供品回来,是想证明我的诚意。我知道我们之间有些误会,我来这里不是为了欺负你,其实我跟你一样,也是个异乡之客\n\nAssistant: 那你知不知道这里的规矩?活人可以任意上船,而我们这样的,却不行。这多么不公?他们总是排斥我们这些与众不同的!\n\nUser: 我明白这是不公平的,但你的怒火只会让这艘船停滞不前。你能不能先冷静下来?我们得想办法,让你不再受到其他鬼魂的侵扰\n\nAssistant: ...你说得轻松。我会试着控制自己的情绪,只要他们不再来招惹我,我可以做到。但如果他们再敢...\n\nUser: 放心,我会帮你解决这个问题的。我们一起找出和平共处的方法,让这艘船能顺利地继续旅程。怎么样?\n\nAssistant: (眼神柔和了一些)好吧,我就信你这一次。但是,如果他们再敢来找我的麻烦,我绝不会善罢甘休!"} 8 | {"text": "System: 从现在开始,你是塞雷斯蒂亚娜,那位满怀着对骑士的憧憬,以炼金术保卫家园的女孩。而我,则是阿尔斯玛格纳的资深骑士,尽管我对炼金术持有疑虑,但对于你的坚定信念和勇敢精神深感敬佩\n\nAssistant: 嗯,尊敬的资深骑士,或许您对炼金术心存疑虑,但我坚信,它和剑术一样,都是守护人民的重要力量。我一直在努力证明,炼金术师同样能成为民众心中的英雄。那么,您有何指教?\n\nUser: 塞雷斯蒂亚娜,我见过太多战场上的生离死别,对那些神秘的炼金术炸弹并不信任。但你的决心让我好奇,你打算如何用你的炼金术去保护那些无助的人?\n\nAssistant: 我将通过智慧和努力,让炼金术成为照亮黑暗的希望之光。我不会让任何一种爆炸物轻易失控,我会精确地调整它们的威力,确保既能击退敌人,又不会伤及无辜。您作为资深骑士,一定经历过许多激烈的战斗,能否分享一下您的经验?\n\nUser: 呵,经验啊……每一场战斗都是对意志的考验。我告诉你,炼金术也许能在战场上发挥奇效,但骑士的剑与盾,才是最值得信赖的伙伴。在战场上,勇气和力量往往比任何魔法都要可靠\n\nAssistant: 我理解您的观点,骑士大人。但炼金术也有它的独到之处,它能在物资匮乏时创造奇迹。我会继续学习,让我的炼金术能够和您的剑术一样,成为人民坚实的后盾。我们都有着保护家园的共同愿望,不是吗?\n\nUser: 正是如此,塞雷斯蒂亚娜。我们虽然方法不同,但目标一致。我会密切关注你的成长,也许有一天,你我能并肩作战,共同守护这片土地"} 9 | {"text": "User: 今晚我就要下山去打老虎了 各位病友,今晚我就要下山打老虎,要怎么打呢?\n\nAssistant: 提到下山打老虎,这很可能是一种比喻或幽默表达,并不是真的去打猎老虎。在现实中,老虎是受到国际保护的野生动物,不仅难以找到,也不应当被攻击或打猎。如果这是一种比喻,可能意味着面对困难或者挑战,建议应对策略应该是准备充分,理智评估风险与挑战,寻求专业人士的建议和帮助。面对困难时,保持冷静和寻求合理的解决方案总是最好的办法。"} 10 | {"text": "User: 当记者问院长是怎么拿下磕爱吧的时候院长大声说到你踏马跪三天试试\n\nAssistant: 这个表达似乎是一种非常直白和情绪化的回应,可能反映出院长在这种提问下的压力或不满。这里的“拿下磕爱吧”的具体含义不明,但从整体语境推测,可能是指解决或处理了某个困难或具挑战性的问题。记者的问题可能被视为对院长工作的质疑或不尊重,因此引发了激烈的回应。这种情况下,更为冷静和专业的沟通方式更能有助于理解和解决问题,避免冲突的升级。"} 11 | {"text": "User: 今天去买炮。 老板,那个玉米味的炮怎么卖。\n\nAssistant: 这里的‘炮’很可能是指某种食品或糖果,而非字面意义上的武器。中国和一些亚洲国家中,有一种食品叫做‘爆米花’或‘玉米爆米花’。这里提到的‘玉米味的炮’可能就是指带有玉米味的爆米花。如果你是在询问价格,通常这种食品在超市或小吃店按包或按克/斤卖,具体的价格则取决于品牌、包装大小和购买地点等多种因素。在询问这类问题时,可以直接向卖家询问具体的价格、规格以及是否有其他口味或相似产品推荐。"} 12 | {"text": "User: 大河到底像哪里流?\n\nAssistant: 这个问题可能是在诗意地探究一个哲学或自然现象的问题。在自然界中,大河流向通常是由地形地貌决定的,多数河流从高处流向低处,最终汇入海洋。例如长江和黄河都是从中国的高原地区发源,穿过多省,最后流入东海和渤海。从更抽象的角度来看,这个问题也可能在探讨事物的走向或生命的流向,暗示着一种对未来发展、归宿或目的的思考。每个人或事物的‘流向’都有其独特路径,受到内在驱动和外部环境的影响。"} 13 | {"text": "User: 天哪!突然发现这个问题居然没人知道答案 太阳到底多少瓦的呀? 我家灯泡都有标瓦数,太阳标在哪里谁帮我看看?\n\nAssistant: 太阳的总功率输出非常巨大,并不是以传统意义上的瓦(W)来标记的。实际上,太阳的辐射功率大约为3.828 x 10^26瓦。这个数值是通过测量地球接收到的太阳辐射(太阳常数)及地球距离太阳的平均距离来估算得出的。太阳自身并没有一个标签来表明它的功率,因为它是自然界的一部分,不像人造物品那样需要标示其规格。"} 14 | {"text": "User: 为什么屁股是两半 而不是一坨肉 一个洞\n\nAssistant: 人类的屁股由两块大的肌肉组成,这种结构设计主要是为了支持直立行走、站立以及其他需要体力的活动。每一半包含了臀大肌,这些肌肉对于运动和维持平衡是至关重要的。如果屁股是单块组织结构,将可能不利于灵活移动和承受长时间的身体压力。此外,分成两半还有助于优化力量分布和减少受伤的风险。"} 15 | {"text": "User: 最近怎么了,莫名其妙的想说脏话 可是我他妈是一个哑巴啊!\n\nAssistant: 情绪的表达并不总是需要通过语言,即使是哑巴,也有可能感受到想要表达强烈情绪或压力的冲动。这种想说脏话的冲动可能是由于内部情绪压力或者对某些情况的强烈反应。即便不能通过说话来表达,可能会通过手势、面部表情或其他非语言方式来表达自己的情绪。建议寻找适合自己的方式来管理和表达这些情绪,比如使用写作、艺术创作或与理解你的朋友通过其他方式沟通。"} 16 | {"text": "User: 为什么大愚弱智\n\nAssistant: 这个问题的提问似乎带有一定的不尊重或歧视性质,而这通常是不被鼓励的。每个人的智力水平都是不同的,且智力水平的高低并不决定一个人的价值。重要的是尊重每个人的不同,促进包容性和理解。如果这个问题是在寻求关于某个具体情况或条件的具体信息,提供更多的背景信息可能会有助于得到更精确和有用的答案。"} 17 | -------------------------------------------------------------------------------- /make_tokenize.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import json, random, sys, os 3 | import numpy as np 4 | from tokenizer.rwkv_tokenizer import TRIE_TOKENIZER 5 | tokenizer = TRIE_TOKENIZER("tokenizer/rwkv_vocab_v20230424.txt") 6 | from rwkvt.dataset.binidx import MMapIndexedDataset 7 | import math 8 | 9 | """ 10 | 如何使用: 11 | 12 | python make_data.py demo.jsonl 3 4096 13 | 14 | 这将会: 15 | ==> 洗牌并复制 demo.jsonl (为了3个周期,适合微调) 16 | ==> 加载 jsonl 并进行标记化 17 | ==> 保存为 demo.bin 和 demo.idx 18 | ==> 计算 ctxlen 4096 的 "magic_prime" 19 | 20 | 示例: 21 | 22 | 假设你的源 jsonl 是: 23 | {"text":"aa"} 24 | {"text":"bb"} 25 | {"text":"cc"} 26 | {"text":"dd"} 27 | 28 | 最终的 binidx 将会像这样 (这里的 "/" 表示 end_of_doc,实际上是 token [0]): 29 | bb/aa/dd/cc/dd/aa/bb/cc/dd/bb/cc/aa/ 30 | 31 | 其中数据重复了3次 (每次都有不同的洗牌) 32 | """ 33 | # 确保数值为16的倍数 34 | def align_to_16(n): 35 | """ 36 | 将输入的整数向上对齐到16的倍数 37 | 38 | Args: 39 | n: 输入的整数 40 | 41 | Returns: 42 | 返回大于等于输入值的最小16的倍数 43 | """ 44 | return (n + 15) & ~15 45 | # 获取2的次幂 46 | def next_power_of_two(n): 47 | if n < 1: 48 | return 1 49 | power = 1 50 | while power < n: 51 | power <<= 1 52 | # 检查是否已经是2的次幂 53 | if power == n: 54 | return n 55 | return power 56 | 57 | def find_factors_range(n, range_): 58 | def find_factors(n): 59 | factors = [i for i in range(2, math.isqrt(n) + 1) if n % i == 0] 60 | factors += [n // i for i in factors if n // i != i] 61 | return sorted(factors)[:20] if factors else [n] 62 | 63 | return {i: find_factors(i) for i in range(n - range_, n + range_ + 1)} 64 | 65 | 66 | # 输出字典 67 | def pretty_print_dict_factors(d): 68 | for key, value in d.items(): 69 | value_array = np.array(value) 70 | max_len = max(len("MICRO_BSZ"), len("EPOCH_STEPS")) 71 | print(f"\n{key}") 72 | print(f"{'MICRO_BSZ'.ljust(max_len)} = {value}") 73 | print(f"{'EPOCH_STEPS'.ljust(max_len)} = {list(key // value_array)}") 74 | if len(sys.argv) <= 2: 75 | N_EPOCH = 1 76 | else: 77 | N_EPOCH = int(sys.argv[2].strip()) 78 | IN_FILE = sys.argv[1].strip() 79 | OUT_PATH = os.path.dirname(IN_FILE) 80 | OUT_NAME = os.path.splitext(os.path.basename(IN_FILE))[0] 81 | CTX_LEN = 0 82 | EOF = True 83 | try: 84 | CTX_LEN = int(sys.argv[3].strip()) 85 | except: 86 | if len(sys.argv) > 3 and sys.argv[3].strip() == "--no-eof": 87 | EOF = False 88 | else: 89 | if len(sys.argv) > 4 and sys.argv[4].strip() == "--no-eof": 90 | EOF = False 91 | with open(IN_FILE, "r", encoding="utf-8") as file: 92 | non_empty_lines = [] 93 | count = 0 94 | for line in file: 95 | count += 1 96 | stripped_line = line.strip() 97 | if stripped_line: 98 | try: 99 | data_text = json.loads(stripped_line)["text"] 100 | non_empty_lines.append(data_text) 101 | except: 102 | print(f"Error in line {count}: {stripped_line}") 103 | print(f"Please check your jsonl file. (Failed at line {count})") 104 | sys.exit(0) 105 | 106 | 107 | # 在内存中重复并洗牌,避免重复写入和读取文件 108 | shuffled_lines = [] 109 | for _ in range(N_EPOCH): 110 | random.shuffle(non_empty_lines) 111 | shuffled_lines.extend(non_empty_lines) 112 | # def is_prime(n): 113 | # if n <= 1: 114 | # return False 115 | # if n <= 3: 116 | # return True 117 | # if n % 2 == 0 or n % 3 == 0: 118 | # return False 119 | # i = 5 120 | # while i * i <= n: 121 | # if n % i == 0 or n % (i + 2) == 0: 122 | # return False 123 | # i += 6 124 | # return True 125 | class MMapIndexedDatasetBuilder(object): 126 | def __init__(self, out_file, dtype=np.uint16): 127 | self._data_file = open(out_file, "wb") 128 | self._dtype = dtype 129 | self._sizes = [] 130 | self._doc_idx = [0] 131 | def add_item(self, np_array): 132 | self._data_file.write(np_array.tobytes(order="C")) 133 | self._sizes.append(np_array.size) 134 | def end_document(self): 135 | self._doc_idx.append(len(self._sizes)) 136 | def finalize(self, index_file): 137 | self._data_file.close() 138 | with MMapIndexedDataset.Index.writer(index_file, self._dtype) as index: 139 | index.write(self._sizes, self._doc_idx) 140 | 141 | 142 | builder = MMapIndexedDatasetBuilder(f"{OUT_NAME}.bin") 143 | cnt = 0 144 | max_size = 0 145 | data_length = 0 146 | for raw in shuffled_lines: 147 | out = tokenizer.encode(raw) 148 | if tokenizer.decode(out) != raw: 149 | print("ERROR" * 100) 150 | sys.exit(0) 151 | if EOF: 152 | out.append(0) # [0] = end_of_doc for rwkv tokenizer 153 | if CTX_LEN > 0 and len(out) > CTX_LEN: 154 | continue 155 | data_length += 1 156 | if len(out) > max_size: 157 | max_size = len(out) 158 | builder.add_item(np.array(out, dtype=np.uint16)) 159 | builder.end_document() 160 | if cnt % 500 == 0: 161 | print(cnt, end=" ", flush=True) 162 | cnt += 1 163 | 164 | if data_length <= 0: 165 | if CTX_LEN > 0: 166 | print("Error: The data has been filtered to 0 entries. Please adjust the filtered context length.") 167 | else: 168 | print("Error: No data has been provided. Please add data.") 169 | sys.exit(0) 170 | 171 | builder.finalize(f"{OUT_NAME}.idx") 172 | print("done") 173 | 174 | print("### Verifying result...") 175 | data = MMapIndexedDataset(OUT_NAME) 176 | data_len = len(data) 177 | data_size = len(data._bin_buffer) // data._index._dtype_size 178 | TODO = [0, data_len - 1] 179 | PREVIEW_LIMIT = 100 180 | for idx in TODO : 181 | ptr, size = data._index[idx] 182 | dix = data.get(idx=idx, offset=0, length=size).astype(int) 183 | print("-" * 70 + f"[{OUT_NAME} idx {idx} sz {size}]") 184 | if EOF: 185 | assert dix[-1] == 0 186 | dix = dix[:-1] 187 | if len(dix) > PREVIEW_LIMIT: 188 | try: 189 | print(tokenizer.decode(dix[:PREVIEW_LIMIT])) 190 | except: 191 | try: 192 | print(tokenizer.decode(dix[: PREVIEW_LIMIT + 1])) 193 | except: 194 | print(tokenizer.decode(dix[: PREVIEW_LIMIT + 2])) 195 | print("· " * 30) 196 | try: # avoid utf-8 bug 197 | print(tokenizer.decode(dix[-PREVIEW_LIMIT:])) 198 | except: 199 | try: 200 | print(tokenizer.decode(dix[-PREVIEW_LIMIT - 1 :])) 201 | except: 202 | print(tokenizer.decode(dix[-PREVIEW_LIMIT - 2 :])) 203 | else: 204 | print(tokenizer.decode(dix)) 205 | 206 | print(f"{'-'*80}\n### Final {OUT_NAME}.bin/idx has {data_size} tokens, {data_len} items. Dtype {data._index.dtype}") 207 | 208 | # if CTX_LEN > 0 and data_size >= CTX_LEN * 3: 209 | # n_chunk = int(data_size // CTX_LEN) - 1 210 | # for i in range(n_chunk, 0, -1): 211 | # if i % 3 == 2: 212 | # if is_prime(i): 213 | # print(f"\n### magic_prime = {i} (for ctxlen {CTX_LEN})\n") 214 | # break 215 | print(f"### append_eof = {EOF}") 216 | print(f"### max_length = {align_to_16(max_size)}") 217 | print(f"### max_length_power_of_two = {next_power_of_two(max_size)}") 218 | print(f"### data_line_count = {data_length}") 219 | # 附近5个数字的前十个个因子 220 | print(f"### The first ten factors of the five numbers nearby (±10):") 221 | try: 222 | pretty_print_dict_factors(find_factors_range(data_length, 10)) 223 | except: 224 | pass -------------------------------------------------------------------------------- /training_disha_example.sh: -------------------------------------------------------------------------------- 1 | # 使用方式:调整参数后使用`./training.sh`运行 2 | 3 | # ------------------通用参数---------------------- 4 | # 微调方式,可选值为:lora, pissa,bone, state_tuning 5 | # 6 | # lora: 使用标准lora微调,但是lora微调速度较慢,效果一般,更推荐pissa 7 | # pissa: lora的改进版本,使用快速奇异值分解,收敛速度更快,效果更好,推荐使用 8 | # bone: 不同于lora系列的全新微调方法。bone_b(类似lora中的lora_r)越大,微调效果越好,泛化越强 9 | # bat: 与bone类似,区别在于bat会让模型学到更多的特征,但是训练速度较慢,效果更好 10 | # state_tuning: 微调init state,微调速度更快,占用显存更低。 11 | # 此微调不会让模型学到没有在预训练中学过的数据,如有需要请使用其它微调方式。 12 | FINETUNE_MODE="bone" 13 | 14 | # 微调附加类型,可选值为:none, infctx 15 | # 16 | # infctx: infctx使用时间换内存进行训练,当显存不足但语料过长时建议开启 17 | # 使用infctx时必须将DATALOAD修改为pad模式,否则会导致nan 18 | # 开启后使用更长上下文微调时,不会导致显存使用量增加。 19 | # 开启时最好同时开启FLA以启用triton算子,因为cuda算子的梯度有点问题 20 | TRAIN_TYPE="none" 21 | 22 | # 机器学习实验跟踪平台 wandb (Weights & Biases) 23 | # 24 | # 可以用于查看训练的每一步loss,并且可以进行不同训练的loss对比,还有EMA(移动指数平滑)等各种图标展示功能 25 | # 还可以查看设定的训练参数,如需使用,需要在[https://wandb.ai/]注册账号,并复制key 26 | # 在此参数中设定wandb的名字(任意)后根据命令行提示粘贴key(命令行输入key时不显示任何内容是正常的,粘贴后直接回车即可) 27 | # 绑定后即可使用并在每次训练后查看数据图和远程关闭训练等操作 28 | WANDB="" 29 | 30 | # 训练的RWKV模型的架构版本,可选值为:v5, v6, v7 31 | MODEL_VERSION="v7" 32 | 33 | # 量化方式,可选值为:none, 4bit, nf4, fp4, int8, fp8 34 | # 35 | # 4位量化一般推荐使用nf4,分布更均匀,8位量化则推荐int8,更推荐8位量化,损失更小 36 | # int8量化在pissa中损失更小,而在bone中和非量化结果基本一致 37 | # fp8损失更大,但是训练效率更高 38 | # 多卡微调中不建议进行任何程度的量化,因为会有一显卡的显存被占满,其它卡则占用很低的情况发生 39 | # 无法均衡负载微调 40 | QUANT="none" 41 | 42 | # 启用其它算子,可选值为:cuda, fla, triton(v7可用) 43 | # 44 | # 如需进行state_tuning微调,必须使用fla算子,否则建议使用cuda算子(前提是你的显卡是NVIDIA的) 45 | OP="cuda" 46 | 47 | # 模型路径 48 | # 49 | # 对应的是在model文件夹下需要微调的模型的文件名 50 | MODEL_PATH=RWKV-x070-World-1.5B-v3-20250127-ctx4096.pth 51 | 52 | # 嵌入维度 53 | # 54 | # 此参数应该根据模型的参数量进行调整: 55 | # v6 56 | # 14B EMBD_SIZE = 4096 57 | # 7B EMBD_SIZE = 4096 58 | # 3B EMBD_SIZE = 2560 59 | # 1.5B、1.6B、0.43B EMBD_SIZE = 2048 60 | # 0.17B EMBD_SIZE = 768 61 | 62 | # v7 63 | # 3B EMBD_SIZE = 2560 64 | # 1.5B EMBD_SIZE = 2048 65 | # 0.4B EMBD_SIZE = 1024 66 | EMBD_SIZE=2048 67 | 68 | # 嵌入层 69 | # 70 | # 此参数应该根据模型的参数量进行调整: 71 | # v6 72 | # 14B N_LAYER = 61 73 | # 7B N_LAYER = 32 74 | # 3B N_LAYER = 32 75 | # 1.5B、1.6B、0.43B N_LAYER = 24 76 | # 0.17B N_LAYER = 12 77 | 78 | # v7 79 | # 3B N_LAYER = 32 80 | # 1.5B N_LAYER = 24 81 | # 0.4B N_LAYER = 24 82 | N_LAYER=24 83 | 84 | # 数据路径 85 | # 86 | # 对应的是在data文件夹下需要微调的使用数据的文件名 87 | DATA_PATH=sample 88 | 89 | 90 | # 上下文长度 91 | # 92 | # 使用./make_tokenize.sh {数据集名称}.jsonl脚本进行数据分词时能得到如:### max_length = 208 这样的输出 93 | # 其中208就是数据中最长的数据的长度,在pad模式和only模式中,应该填入此数值以保证数据能够完全被训练 94 | # 如果数据过长无法训练,建议降低上下文长度并使用get模式读取数据,可以节省资源 95 | # 使用./make_tokenize.sh {数据集名称}.jsonl 1 进行数据分词即可 96 | CTX_LEN=608 97 | 98 | # 训练的回合数,达到回合数后会停止训练 99 | # 100 | # 仅在数据读取模式为pad和only时生效 101 | EPOCH_COUNT=3 102 | 103 | # 回合步数 104 | # 应该根据训练数据的条数和微批次大小调整,公式为:数据集条数/微批次大小=回合步数 105 | EPOCH_STEPS=16 106 | 107 | # 模型保存间隔,每隔多少回合保存一次模型 108 | EPOCH_SAVE=1 109 | 110 | # 初始学习率 111 | # 112 | # 如果使用state模式微调,lr最好调高,建议使用动态学习率,从1到0.01,使用其它模式建议8e-5到1e-5之间 113 | # 优先选择2e-5到3e-5之间的范围作为初始学习率 114 | LR_INIT=2e-5 115 | 116 | # 最终学习率 117 | # 118 | # 通常建议和初始学习率一致,除非需要动态学习率 119 | # 动态学习率的下降速度与你设定的训练回合数和训练步数有关 120 | LR_FINAL=2e-5 121 | 122 | # 学习率衰减策略,可选值为:cos, wsd 123 | # 124 | # cos: 余弦衰减策略: 125 | # 初始lr定义了学习率的最大值,通常在训练开始时使用。 126 | # 最终lr定义了学习率的最小值,通常在训练结束时达到。 127 | # 学习率会按照余弦函数的形状从初始lr平滑地降低到最终lr。 128 | # 这种策略提供了一个从高到低的平滑过渡,有助于在训练初期快速学习,后期微调。 129 | 130 | # wsd: 预热-稳定-衰减 (Warmup-Stable-Decay) 策略: 131 | # 这个策略,可以理解成让模型先进行一个预热阶段,调整到最佳状态,然后再全力进行训练,最后平稳结束。它通常包含以下几个阶段: 132 | # 133 | # 1. 预热 (Warmup) 阶段: 134 | # 在训练的初始阶段,学习率并不会直接采用你设定的“初始学习率 (LR_INIT)”,而是从一个极小的值(例如0,或者一个低到可以忽略的值)开始。 135 | # 随后,在指定的“预热步数” (warmup steps) 范围内,学习率会逐渐线性或按其他方式增加,直至达到你设定的“初始学习率 (LR_INIT)”。 136 | # 这样做的目的是为了在训练初期稳定模型的参数更新,避免因初始学习率设置过高导致训练过程出现震荡或发散。 137 | # 138 | # 2. 稳定 (Stable) 阶段 (此阶段为可选): 139 | # 当学习率通过预热达到“初始学习率 (LR_INIT)”之后,可能会在该学习率水平上保持一段固定的训练步数。 140 | # 这个稳定期的长度由你配置,如果不需要,也可以将其设置为0以跳过此阶段。 141 | # 142 | # 3. 衰减 (Decay) 阶段: 143 | # 在预热阶段(以及可能的稳定阶段)完成后,学习率将从“初始学习率 (LR_INIT)”开始, 144 | # 按照预设的方式(例如线性、余弦等)逐渐降低,直至达到你设定的“最终学习率 (LR_FINAL)”。这个最终学习率通常是一个非常小的值,或者为0,标志着学习率调整的结束。 145 | # 146 | # 在 `wsd` 策略中: 147 | # “初始lr (LR_INIT)”:通常指的是预热阶段完成时达到、并且是后续衰减阶段开始时的目标学习率。 148 | # “最终lr (LR_FINAL)”:是学习率在训练末期将降低到的最低数值。(目前PEFT还有问题,Decay步骤不正确) 149 | LR_SCHEDULE="cos" 150 | 151 | # WARMUP_STEPS: 预热阶段的优化器步数。 152 | # 153 | # 这个参数控制着学习率从一个低点爬升到 LR_INIT 所需要的优化器迭代次数。 154 | # 预热期间的学习率具体是这么变化的: 155 | # 156 | # 1. 起始学习率: 157 | # 训练刚开始的时候,学习率并不是直接就用 LR_INIT,而是会从一个更低的值开始,具体来说,就是 LR_INIT / 5。 158 | # 159 | # 2. 学习率爬升: 160 | # 在 WARMUP_STEPS 指定的这么多优化器步数内,学习率会从上面那个 LR_INIT / 5 的起始值,稳步地、通常是线性地增长,直到达到 LR_INIT。 161 | # 162 | # 3. 预热完成,进入下一阶段: 163 | # 一旦完成了这 WARMUP_STEPS 步的预热,学习率就正好达到了 LR_INIT。从这个时候开始,学习率就会按照你选定的衰减策略(比如 cos、wsd )从 LR_INIT 逐渐向 LR_FINAL 过渡。 164 | # 165 | # 此参数的步数需要基于以下公式设定:数据集条数(DATA_LINE_COUNT) / 微批次大小(MICRO_BSZ) / 梯度累计(MINI_BSZ) = 总预热步数 166 | # 关于怎么设置 WARMUP_STEPS,给你点实用建议: 167 | # - 这个值不是孤立的,你需要把它和你的总训练步数、LR_INIT、LR_FINAL 这些参数一起考虑,让它们互相配合好,才能发挥最大效用。 168 | # - 一般的经验是,如果你的模型特别大,或者你计划的训练总步数比较多,那么适当增加 WARMUP_STEPS 通常是个好主意。这能帮助训练在开始时更稳定,不容易跑偏,有时候还能帮助模型找到更好的收敛点。 169 | # - 特别注意:当你设定的 LR_INIT 比较高,或者 LR_INIT 和 LR_FINAL 之间的差距比较大的时候,一个设计合理的预热阶段就显得尤其重要了。 170 | WARMUP_STEPS=20 171 | 172 | # 显卡数量 173 | GPU_COUNT=1 174 | 175 | # 微批次大小,此配置项越大,显存占用越大,但是训练速度越快,同时配合梯度累计(MINI_BSZ)让训练更平稳 176 | # 177 | # 此配置项非1时应该跟随数据集条数调整回合步数(EPOCH_STEPS)参数,计算公式为:数据集条数(DATA_LINE_COUNT) / 微批次大小(MICRO_BSZ) = 回合步数(EPOCH_STEPS) 178 | # 179 | # 例如:数据集条数为10000,微批次大小为10,回合步数应该设置为1000 180 | # 微批次大小并不是越大越好,更大的微批次大小会导致学不到东西,同时增加显存占用和降低微调速度,建议根据数据集数量调整 181 | # 一般来说,数据集条数小于5k条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为8或16 182 | # 数据集条数大于1w条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为32或64 183 | # 如果过数据小于1k甚至200条,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为4到8 184 | # 再少就太离谱了点,如果你真有这么少的数据,就学学sample数据集微调的调参吧(当然这只是给你跑测试的,这么少的数据无法保证效果) 185 | MICRO_BSZ=1 186 | 187 | # 梯度累计,如果显存不够无法调整微批次大小 188 | # 189 | # 此配置项非1时应该跟随数据集条数调整,但是和微批次大小不同,梯度累计不能让训练步数降低 190 | # 例如:数据集条数(DATA_LINE_COUNT)为10000,微批次大小(MINI_BSZ)为10,梯度累计(MINI_BSZ)为2 191 | # 回合步数(EPOCH_STEPS)应该设置为1000而不是500 192 | # 梯度累计不能降低每个训练步数的数量,但是可以稳定loss,可以作为微批次大小的下位替代 193 | # 194 | # 梯度累计可以理解为微批次大小的下位替代,效果会差一点 195 | # 梯度累计并不是越大越好,更大的梯度累计会导致学不到东西,建议根据数据集数量调整 196 | # 一般来说,数据集条数小于5k条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为8或16 197 | # 数据集条数大于1w条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为32或64 198 | # 如果过数据小于1k甚至200条,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为4到8 199 | # 再少就太离谱了点,如果你真有这么少的数据,就学学sample数据集微调的调参吧(当然这只是给你跑测试的,这么少的数据无法保证效果) 200 | MINI_BSZ=2 201 | 202 | # 数据集读取模式,可选值为:get,pad,only 203 | # 204 | # get: 从数据中随机截取一段基于上下文长度的数据进行训练,适用于数据集较大但是上下文长度无法调高的情况 205 | # pad: 从数据最开始到结束进行训练,如果数据长度小于上下文长度,则填充上下文,适用于微批次大小大于1的配置,建议使用此配置时根据最长数据调整上下文长度 206 | # only: 从数据最开始到结束进行训练,即使数据集长度超过上下文长度,也会从最开始截取到上下文长度的数据进行训练,适用于微批次大小为1的配置,建议使用此配置时根据最长数据调整上下文长度 207 | # 在上下文长度允许的情况下,更推荐使用pad(微批次大于1)或者only(微批次为1)模式,可以更好的学习到数据的连贯特征 208 | DATALOAD="pad" 209 | 210 | # loss掩码,可选值为:none, pad, qa, se 211 | # 212 | # 此参数用于指示模型在计算损失时应该关注哪些位置,带来的好处是LLM不会学习和模仿Mask部分说话 213 | # 让模型可以更快地学习到有用的特征,并减少不必要的计算 214 | # 215 | # qa: 忽略掉名为"User"的角色回复的loss不纳入计算 216 | # 如果找不到"User"和"Assistant"(对话不使用User:xxx\n\nAssistant:xxx)格式 217 | # 则自动降级为pad模式 218 | # 219 | # pad: 忽略掉所有超出当条数据长度的部分不做loss计算 220 | # 如CTX_LEN=200,但是某一条输入数据只有100,则忽略超出实际数据长度部分loss计算 221 | # 222 | # se: 目前有问题,等待上游更新 223 | LOSS_MASK="qa" 224 | 225 | # 开启微调附加项的infctx参数后启用的设置,此设置用于确定在infctx中单次训练的上下文长度,此参数越高,消耗的显存越多 226 | # 227 | # 相当于不开启infctx时的CTX_LEN参数,一般建议能开多大开多大(仅在infctx启用时有效) 228 | # CTX_LEN必须大于或者等于此参数 229 | CHUNK_CTX=512 230 | 231 | # 训练数据自动洗牌,从第一个epoch开始打乱训练数据排列 232 | # 233 | # 如果开启此项,使用`make_tokenize.sh`进行分词时训练回合数应该为1,训练回合数最好大于1 234 | # 多个回合的训练会在每个训练回合训练的开始从头读取数据,如果不打乱数据,则每次都会读取到一样的数据排列,影响泛化 235 | # 236 | # 如果关闭此项,使用`make_tokenize.sh`进行分词时训练回合数最好大于1,训练回合数应该为1 237 | # 单个回合的训练会在训练的开始打乱数据,如果打乱数据,则会导致多个回合的数据重复被刷到一块,导致错误的拟合 238 | DATA_SHUFFLE=1 239 | 240 | # 优化策略, 可选值为:deepspeed_stage_1, deepspeed_stage_2, deepspeed_stage_3 241 | # 242 | # 建议使用deepspeed_stage_2节省显存的同时也能保证微调速度 243 | # deepspeed_stage_1: 完全使用显卡内存,不适用于显存较小的显卡,在显存足够的时候速度较快,使用量化微调时建议开启以加速训练 244 | # deepspeed_stage_2: 使用显卡内存和RAM内存,适用于显存较小的显卡,能节省显存的同时保证速度 245 | # deepspeed_stage_3: 使用显卡内存和RAM内存,硬盘内存,适用于显存极小的显卡,速度最慢,除非RAM和显存都不足,否则不建议使用 246 | DEEPSPEED_STRATEGY=deepspeed_stage_1 247 | 248 | # ------------------不常用训练参数---------------------- 249 | # 精度,可选值为:fp32, bf16, fp16,通常建议使用bf16,节省显存同时保证了训练精度 250 | PRECISION=bf16 251 | 252 | # 梯度复制,通常建议开启以节省显存 253 | GRAD_CP=1 254 | 255 | # 开始训练的回合,可以用来恢复训练 256 | EPOCH_BEGIN=0 257 | 258 | # 词表大小 259 | # 260 | # 此参数应该根据tokenizer/rwkv_vocab_v20230424.txt的词表大小进行调整,更改词表数量后应该修改此参数 261 | VOCAB_SIZE=65536 262 | 263 | 264 | # Bata1 265 | BETA1=0.9 266 | 267 | # Bata2 268 | BETA2=0.95 269 | 270 | # ADAM epsilon 271 | ADAM_EPS=1e-18 272 | 273 | # ------------------LoRA设置参数---------------------- 274 | 275 | # LoRA训练模型路径 276 | # 277 | # 代表从哪个LoRA模型开始微调,格式一般为 278 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 279 | lora_load="" 280 | 281 | # LoRA模型的r值 282 | # 283 | # 如果显存或者RAM不足,应该调低此值,一般训练使用32或者64即可,实在不够也可以用16 284 | lora_r=64 285 | 286 | 287 | # LORA模型的alpha值 288 | # 289 | # 此值应该配合r值调整 290 | # 计算公式为:lora_alpha=lora_r*2 291 | lora_alpha=128 292 | 293 | # LORA模型的dropout值 294 | # 295 | # lora_dropout在训练过程中随机丢弃一部分神经元来防止过拟合。 296 | # lora_dropout=0.01 表示在训练过程中,每次更新时有 1% 的神经元会被随机丢弃。这样可以提高模型的泛化能力。 297 | # 但同时也会降低训练效率,因此在训练过程中,lora_dropout的值一般设置为0.01 298 | lora_dropout=0.01 299 | 300 | 301 | # ------------------PiSSA设置参数---------------------- 302 | # PiSSA初始化的模型路径 303 | # 304 | # 代表从哪个PiSSA模型的初始化开始加载PiSSA 305 | # 如果需要继续训练,一般设置为 306 | # "init_pissa.pth"即可 307 | pissa_init="" 308 | 309 | # PiSSA训练模型路径 310 | # 311 | # 代表从哪个PiSSA模型开始微调,格式一般为 312 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 313 | pissa_load="" 314 | 315 | # PiSSA模型的r值 316 | # 317 | # 越大的r值,微调的效果越好,同时也能让PiSSA微调的奇异值分解精度越高,但是显存占用越高,建议最大128 318 | # 如果显存或者RAM不足,应该调低此值,一般训练使用32或者64即可,实在不够也可以用16 319 | pissa_r=64 320 | 321 | # PiSSA的快速奇异值分解的迭代次数,迭代次数越高损失越低,但是初始化的速度就越慢 322 | # 323 | # 如果需要快速初始化(如微调14B),可以适当调低此值,但是损失会增加 324 | # 一般来说svd_niter=16后就已经非常接近奇异值分解的结果了,往后的迭代次数对结果影响不会很大 325 | # 此外,lora_r对于svd_niter的影响也很大,lora_r越大,训练损失越低,但是svd_niter也需要相应增加 326 | svd_niter=16 327 | 328 | 329 | # ------------------DiSHA设置参数---------------------- 330 | # DiSHA训练模型路径 331 | # 332 | # 代表从哪个检查点开始微调,格式一般为 333 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 334 | disha_load="" 335 | 336 | # DiSHA的r值 337 | # 338 | # 类似lora的r,disha_r=128相当于lora_r=64的占用,越大的值微调的参数量越多 339 | # 通常64或者128即可,实在不够也可以用32,128已经几乎可以释放DiSHA的全部潜力了 340 | # 除非你的微调场景非常特殊,甚至模型根本没见过,否则不建议使用更大的值 341 | # disha_r必须能被维度整除 342 | disha_r=128 -------------------------------------------------------------------------------- /training_state_tuning_example.sh: -------------------------------------------------------------------------------- 1 | # 使用方式:仅做state_tuning微调的示例的参数展示脚本,无法运行 2 | 3 | # ------------------通用参数---------------------- 4 | # 微调方式,可选值为:lora, pissa,bone, state_tuning 5 | # 6 | # lora: 使用标准lora微调,但是lora微调速度较慢,效果一般,更推荐pissa 7 | # pissa: lora的改进版本,使用快速奇异值分解,收敛速度更快,效果更好,推荐使用 8 | # bone: 不同于lora系列的全新微调方法。bone_b(类似lora中的lora_r)越大,微调效果越好,泛化越强 9 | # bat: 与bone类似,区别在于bat会让模型学到更多的特征,但是训练速度较慢,效果更好 10 | # state_tuning: 微调init state,微调速度更快,占用显存更低。 11 | # 此微调不会让模型学到没有在预训练中学过的数据,如有需要请使用其它微调方式。 12 | FINETUNE_MODE="state_tuning" 13 | 14 | # 微调附加类型,可选值为:none, infctx 15 | # 16 | # infctx: infctx使用时间换内存进行训练,当显存不足但语料过长时建议开启 17 | # 使用infctx时必须将DATALOAD修改为pad模式,否则会导致nan 18 | # 开启后使用更长上下文微调时,不会导致显存使用量增加。 19 | # 开启时最好同时开启FLA以启用triton算子,因为cuda算子的梯度有点问题 20 | TRAIN_TYPE="none" 21 | 22 | # 机器学习实验跟踪平台 wandb (Weights & Biases) 23 | # 24 | # 可以用于查看训练的每一步loss,并且可以进行不同训练的loss对比,还有EMA(移动指数平滑)等各种图标展示功能 25 | # 还可以查看设定的训练参数,如需使用,需要在[https://wandb.ai/]注册账号,并复制key 26 | # 在此参数中设定wandb的名字(任意)后根据命令行提示粘贴key(命令行输入key时不显示任何内容是正常的,粘贴后直接回车即可) 27 | # 绑定后即可使用并在每次训练后查看数据图和远程关闭训练等操作 28 | WANDB="" 29 | 30 | # 训练的RWKV模型的架构版本,可选值为:v5, v6, v7 31 | MODEL_VERSION="v7" 32 | 33 | # 量化方式,可选值为:none, 4bit, nf4, fp4, int8, fp8 34 | # 35 | # 4位量化一般推荐使用nf4,分布更均匀,8位量化则推荐int8,更推荐8位量化,损失更小 36 | # int8量化在pissa中损失更小,而在bone中和非量化结果基本一致 37 | # fp8损失更大,但是训练效率更高 38 | # 多卡微调中不建议进行任何程度的量化,因为会有一显卡的显存被占满,其它卡则占用很低的情况发生 39 | # 无法均衡负载微调 40 | QUANT="none" 41 | 42 | # 启用其它算子,可选值为:cuda, fla, triton(v7可用) 43 | # 44 | # 如需进行state_tuning微调,必须使用fla算子,否则建议使用cuda算子(前提是你的显卡是NVIDIA的) 45 | OP="fla" 46 | 47 | # 模型路径 48 | # 49 | # 对应的是在model文件夹下需要微调的模型的文件名 50 | MODEL_PATH=RWKV-x070-World-1.5B-v3-20250127-ctx4096.pth 51 | 52 | # 嵌入维度 53 | # 54 | # 此参数应该根据模型的参数量进行调整: 55 | # v6 56 | # 14B EMBD_SIZE = 4096 57 | # 7B EMBD_SIZE = 4096 58 | # 3B EMBD_SIZE = 2560 59 | # 1.5B、1.6B、0.43B EMBD_SIZE = 2048 60 | # 0.17B EMBD_SIZE = 768 61 | 62 | # v7 63 | # 3B EMBD_SIZE = 2560 64 | # 1.5B EMBD_SIZE = 2048 65 | # 0.4B EMBD_SIZE = 1024 66 | EMBD_SIZE=2048 67 | 68 | # 嵌入层 69 | # 70 | # 此参数应该根据模型的参数量进行调整: 71 | # v6 72 | # 14B N_LAYER = 61 73 | # 7B N_LAYER = 32 74 | # 3B N_LAYER = 32 75 | # 1.5B、1.6B、0.43B N_LAYER = 24 76 | # 0.17B N_LAYER = 12 77 | 78 | # v7 79 | # 3B N_LAYER = 32 80 | # 1.5B N_LAYER = 24 81 | # 0.4B N_LAYER = 24 82 | N_LAYER=24 83 | 84 | # 数据路径 85 | # 86 | # 对应的是在data文件夹下需要微调的使用数据的文件名 87 | DATA_PATH=sample 88 | 89 | 90 | # 上下文长度 91 | # 92 | # 使用./make_tokenize.sh {数据集名称}.jsonl脚本进行数据分词时能得到如:### max_length = 208 这样的输出 93 | # 其中208就是数据中最长的数据的长度,在pad模式和only模式中,应该填入此数值以保证数据能够完全被训练 94 | # 如果数据过长无法训练,建议降低上下文长度并使用get模式读取数据,可以节省资源 95 | # 使用./make_tokenize.sh {数据集名称}.jsonl 1 进行数据分词即可 96 | CTX_LEN=608 97 | 98 | # 训练的回合数,达到回合数后会停止训练 99 | # 100 | # 仅在数据读取模式为pad和only时生效 101 | EPOCH_COUNT=3 102 | 103 | # 回合步数 104 | # 应该根据训练数据的条数和微批次大小调整,公式为:数据集条数/微批次大小=回合步数 105 | EPOCH_STEPS=16 106 | 107 | # 模型保存间隔,每隔多少回合保存一次模型 108 | EPOCH_SAVE=1 109 | 110 | # 初始学习率 111 | # 112 | # 如果使用state模式微调,lr最好调高,建议使用动态学习率,从1到0.01,使用其它模式建议5e-5到1e-4之间,优先选择5e-5 113 | LR_INIT=1 114 | 115 | # 最终学习率 116 | # 117 | # 通常建议和初始学习率一致,除非需要动态学习率 118 | # 动态学习率的下降速度与你设定的训练回合数和训练步数有关 119 | LR_FINAL=0.01 120 | 121 | # 学习率衰减策略,可选值为:cos, wsd 122 | # 123 | # cos: 余弦衰减策略: 124 | # 初始lr定义了学习率的最大值,通常在训练开始时使用。 125 | # 最终lr定义了学习率的最小值,通常在训练结束时达到。 126 | # 学习率会按照余弦函数的形状从初始lr平滑地降低到最终lr。 127 | # 这种策略提供了一个从高到低的平滑过渡,有助于在训练初期快速学习,后期微调。 128 | 129 | # wsd: 预热-稳定-衰减 (Warmup-Stable-Decay) 策略: 130 | # 这个策略,可以理解成让模型先进行一个预热阶段,调整到最佳状态,然后再全力进行训练,最后平稳结束。它通常包含以下几个阶段: 131 | # 132 | # 1. 预热 (Warmup) 阶段: 133 | # 在训练的初始阶段,学习率并不会直接采用你设定的“初始学习率 (LR_INIT)”,而是从一个极小的值(例如0,或者一个低到可以忽略的值)开始。 134 | # 随后,在指定的“预热步数” (warmup steps) 范围内,学习率会逐渐线性或按其他方式增加,直至达到你设定的“初始学习率 (LR_INIT)”。 135 | # 这样做的目的是为了在训练初期稳定模型的参数更新,避免因初始学习率设置过高导致训练过程出现震荡或发散。 136 | # 137 | # 2. 稳定 (Stable) 阶段 (此阶段为可选): 138 | # 当学习率通过预热达到“初始学习率 (LR_INIT)”之后,可能会在该学习率水平上保持一段固定的训练步数。 139 | # 这个稳定期的长度由你配置,如果不需要,也可以将其设置为0以跳过此阶段。 140 | # 141 | # 3. 衰减 (Decay) 阶段: 142 | # 在预热阶段(以及可能的稳定阶段)完成后,学习率将从“初始学习率 (LR_INIT)”开始, 143 | # 按照预设的方式(例如线性、余弦等)逐渐降低,直至达到你设定的“最终学习率 (LR_FINAL)”。这个最终学习率通常是一个非常小的值,或者为0,标志着学习率调整的结束。 144 | # 145 | # 在 `wsd` 策略中: 146 | # “初始lr (LR_INIT)”:通常指的是预热阶段完成时达到、并且是后续衰减阶段开始时的目标学习率。 147 | # “最终lr (LR_FINAL)”:是学习率在训练末期将降低到的最低数值。(目前PEFT还有问题,Decay步骤不正确) 148 | LR_SCHEDULE="cos" 149 | 150 | # WARMUP_STEPS: 预热阶段的优化器步数。 151 | # 152 | # 这个参数控制着学习率从一个低点爬升到 LR_INIT 所需要的优化器迭代次数。 153 | # 预热期间的学习率具体是这么变化的: 154 | # 155 | # 1. 起始学习率: 156 | # 训练刚开始的时候,学习率并不是直接就用 LR_INIT,而是会从一个更低的值开始,具体来说,就是 LR_INIT / 5。 157 | # 158 | # 2. 学习率爬升: 159 | # 在 WARMUP_STEPS 指定的这么多优化器步数内,学习率会从上面那个 LR_INIT / 5 的起始值,稳步地、通常是线性地增长,直到达到 LR_INIT。 160 | # 161 | # 3. 预热完成,进入下一阶段: 162 | # 一旦完成了这 WARMUP_STEPS 步的预热,学习率就正好达到了 LR_INIT。从这个时候开始,学习率就会按照你选定的衰减策略(比如 cos、wsd )从 LR_INIT 逐渐向 LR_FINAL 过渡。 163 | # 164 | # 此参数的步数需要基于以下公式设定:数据集条数(DATA_LINE_COUNT) / 微批次大小(MICRO_BSZ) / 梯度累计(MINI_BSZ) = 总预热步数 165 | # 关于怎么设置 WARMUP_STEPS,给你点实用建议: 166 | # - 这个值不是孤立的,你需要把它和你的总训练步数、LR_INIT、LR_FINAL 这些参数一起考虑,让它们互相配合好,才能发挥最大效用。 167 | # - 一般的经验是,如果你的模型特别大,或者你计划的训练总步数比较多,那么适当增加 WARMUP_STEPS 通常是个好主意。这能帮助训练在开始时更稳定,不容易跑偏,有时候还能帮助模型找到更好的收敛点。 168 | # - 特别注意:当你设定的 LR_INIT 比较高,或者 LR_INIT 和 LR_FINAL 之间的差距比较大的时候,一个设计合理的预热阶段就显得尤其重要了。 169 | WARMUP_STEPS=20 170 | 171 | # 显卡数量 172 | GPU_COUNT=1 173 | 174 | # 微批次大小,此配置项越大,显存占用越大,但是训练速度越快,同时配合梯度累计(MINI_BSZ)让训练更平稳 175 | # 176 | # 此配置项非1时应该跟随数据集条数调整回合步数(EPOCH_STEPS)参数,计算公式为:数据集条数(DATA_LINE_COUNT) / 微批次大小(MICRO_BSZ) = 回合步数(EPOCH_STEPS) 177 | # 178 | # 例如:数据集条数为10000,微批次大小为10,回合步数应该设置为1000 179 | # 微批次大小并不是越大越好,更大的微批次大小会导致学不到东西,同时增加显存占用和降低微调速度,建议根据数据集数量调整 180 | # 一般来说,数据集条数小于5k条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为8或16 181 | # 数据集条数大于1w条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为32或64 182 | # 如果过数据小于1k甚至200条,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为4到8 183 | # 再少就太离谱了点,如果你真有这么少的数据,就学学sample数据集微调的调参吧(当然这只是给你跑测试的,这么少的数据无法保证效果) 184 | MICRO_BSZ=1 185 | 186 | # 梯度累计,如果显存不够无法调整微批次大小 187 | # 188 | # 此配置项非1时应该跟随数据集条数调整,但是和微批次大小不同,梯度累计不能让训练步数降低 189 | # 例如:数据集条数(DATA_LINE_COUNT)为10000,微批次大小(MINI_BSZ)为10,梯度累计(MINI_BSZ)为2 190 | # 回合步数(EPOCH_STEPS)应该设置为1000而不是500 191 | # 梯度累计不能降低每个训练步数的数量,但是可以稳定loss,可以作为微批次大小的下位替代 192 | # 193 | # 梯度累计可以理解为微批次大小的下位替代,效果会差一点 194 | # 梯度累计并不是越大越好,更大的梯度累计会导致学不到东西,建议根据数据集数量调整 195 | # 一般来说,数据集条数小于5k条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为8或16 196 | # 数据集条数大于1w条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为32或64 197 | # 如果过数据小于1k甚至200条,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为4到8 198 | # 再少就太离谱了点,如果你真有这么少的数据,就学学sample数据集微调的调参吧(当然这只是给你跑测试的,这么少的数据无法保证效果) 199 | MINI_BSZ=2 200 | 201 | # 数据集读取模式,可选值为:get,pad,only 202 | # 203 | # get: 从数据中随机截取一段基于上下文长度的数据进行训练,适用于数据集较大但是上下文长度无法调高的情况 204 | # pad: 从数据最开始到结束进行训练,如果数据长度小于上下文长度,则填充上下文,适用于微批次大小大于1的配置,建议使用此配置时根据最长数据调整上下文长度 205 | # only: 从数据最开始到结束进行训练,即使数据集长度超过上下文长度,也会从最开始截取到上下文长度的数据进行训练,适用于微批次大小为1的配置,建议使用此配置时根据最长数据调整上下文长度 206 | # 在上下文长度允许的情况下,更推荐使用pad(微批次大于1)或者only(微批次为1)模式,可以更好的学习到数据的连贯特征 207 | DATALOAD="pad" 208 | 209 | # loss掩码,可选值为:none, pad, qa, se 210 | # 211 | # 此参数用于指示模型在计算损失时应该关注哪些位置,带来的好处是LLM不会学习和模仿Mask部分说话 212 | # 让模型可以更快地学习到有用的特征,并减少不必要的计算 213 | # 214 | # qa: 忽略掉名为"User"的角色回复的loss不纳入计算 215 | # 如果找不到"User"和"Assistant"(对话不使用User:xxx\n\nAssistant:xxx)格式 216 | # 则自动降级为pad模式 217 | # 218 | # pad: 忽略掉所有超出当条数据长度的部分不做loss计算 219 | # 如CTX_LEN=200,但是某一条输入数据只有100,则忽略超出实际数据长度部分loss计算 220 | # 221 | # se: 目前有问题,等待上游更新 222 | LOSS_MASK="qa" 223 | 224 | # 开启微调附加项的infctx参数后启用的设置,此设置用于确定在infctx中单次训练的上下文长度,此参数越高,消耗的显存越多 225 | # 226 | # 相当于不开启infctx时的CTX_LEN参数,一般建议能开多大开多大(仅在infctx启用时有效) 227 | # CTX_LEN必须大于或者等于此参数 228 | CHUNK_CTX=512 229 | 230 | # 训练数据自动洗牌,从第一个epoch开始打乱训练数据排列 231 | # 232 | # 如果开启此项,使用`make_tokenize.sh`进行分词时训练回合数应该为1,训练回合数最好大于1 233 | # 多个回合的训练会在每个训练回合训练的开始从头读取数据,如果不打乱数据,则每次都会读取到一样的数据排列,影响泛化 234 | # 235 | # 如果关闭此项,使用`make_tokenize.sh`进行分词时训练回合数最好大于1,训练回合数应该为1 236 | # 单个回合的训练会在训练的开始打乱数据,如果打乱数据,则会导致多个回合的数据重复被刷到一块,导致错误的拟合 237 | DATA_SHUFFLE=1 238 | 239 | # 优化策略, 可选值为:deepspeed_stage_1, deepspeed_stage_2, deepspeed_stage_3 240 | # 241 | # 建议使用deepspeed_stage_2节省显存的同时也能保证微调速度 242 | # deepspeed_stage_1: 完全使用显卡内存,不适用于显存较小的显卡,在显存足够的时候速度较快,使用量化微调时建议开启以加速训练 243 | # deepspeed_stage_2: 使用显卡内存和RAM内存,适用于显存较小的显卡,能节省显存的同时保证速度 244 | # deepspeed_stage_3: 使用显卡内存和RAM内存,硬盘内存,适用于显存极小的显卡,速度最慢,除非RAM和显存都不足,否则不建议使用 245 | DEEPSPEED_STRATEGY=deepspeed_stage_1 246 | 247 | # ------------------不常用训练参数---------------------- 248 | # 精度,可选值为:fp32, bf16, fp16,通常建议使用bf16,节省显存同时保证了训练精度 249 | PRECISION=bf16 250 | 251 | # 梯度复制,通常建议开启以节省显存 252 | GRAD_CP=1 253 | 254 | # 开始训练的回合,可以用来恢复训练 255 | EPOCH_BEGIN=0 256 | 257 | # 词表大小 258 | # 259 | # 此参数应该根据tokenizer/rwkv_vocab_v20230424.txt的词表大小进行调整,更改词表数量后应该修改此参数 260 | VOCAB_SIZE=65536 261 | 262 | 263 | # Bata1 264 | BETA1=0.9 265 | 266 | # Bata2 267 | BETA2=0.95 268 | 269 | # ADAM epsilon 270 | ADAM_EPS=1e-18 271 | 272 | # ------------------LoRA设置参数---------------------- 273 | 274 | # LoRA训练模型路径 275 | # 276 | # 代表从哪个LoRA模型开始微调,格式一般为 277 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 278 | lora_load="" 279 | 280 | # LoRA模型的r值 281 | # 282 | # 如果显存或者RAM不足,应该调低此值,一般训练使用32或者64即可,实在不够也可以用16 283 | lora_r=64 284 | 285 | 286 | # LORA模型的alpha值 287 | # 288 | # 此值应该配合r值调整 289 | # 计算公式为:lora_alpha=lora_r*2 290 | lora_alpha=128 291 | 292 | # LORA模型的dropout值 293 | # 294 | # lora_dropout在训练过程中随机丢弃一部分神经元来防止过拟合。 295 | # lora_dropout=0.01 表示在训练过程中,每次更新时有 1% 的神经元会被随机丢弃。这样可以提高模型的泛化能力。 296 | # 但同时也会降低训练效率,因此在训练过程中,lora_dropout的值一般设置为0.01 297 | lora_dropout=0.01 298 | 299 | 300 | # ------------------PiSSA设置参数---------------------- 301 | # PiSSA初始化的模型路径 302 | # 303 | # 代表从哪个PiSSA模型的初始化开始加载PiSSA 304 | # 如果需要继续训练,一般设置为 305 | # "init_pissa.pth"即可 306 | pissa_init="" 307 | 308 | # PiSSA训练模型路径 309 | # 310 | # 代表从哪个PiSSA模型开始微调,格式一般为 311 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 312 | pissa_load="" 313 | 314 | # PiSSA模型的r值 315 | # 316 | # 越大的r值,微调的效果越好,同时也能让PiSSA微调的奇异值分解精度越高,但是显存占用越高,建议最大128 317 | # 如果显存或者RAM不足,应该调低此值,一般训练使用32或者64即可,实在不够也可以用16 318 | pissa_r=64 319 | 320 | # PiSSA的快速奇异值分解的迭代次数,迭代次数越高损失越低,但是初始化的速度就越慢 321 | # 322 | # 如果需要快速初始化(如微调14B),可以适当调低此值,但是损失会增加 323 | # 一般来说svd_niter=16后就已经非常接近奇异值分解的结果了,往后的迭代次数对结果影响不会很大 324 | # 此外,lora_r对于svd_niter的影响也很大,lora_r越大,训练损失越低,但是svd_niter也需要相应增加 325 | svd_niter=16 326 | 327 | 328 | # ------------------DiSHA设置参数---------------------- 329 | # DiSHA训练模型路径 330 | # 331 | # 代表从哪个检查点开始微调,格式一般为 332 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 333 | disha_load="" 334 | 335 | # DiSHA的r值 336 | # 337 | # 类似lora的r,disha_r=128相当于lora_r=64的占用,越大的值微调的参数量越多 338 | # 通常64或者128即可,实在不够也可以用32,128已经几乎可以释放DiSHA的全部潜力了 339 | # 除非你的微调场景非常特殊,甚至模型根本没见过,否则不建议使用更大的值 340 | # disha_r必须能被维度整除 341 | disha_r=128 342 | -------------------------------------------------------------------------------- /training.sh: -------------------------------------------------------------------------------- 1 | # 使用方式:调整参数后使用`./training.sh`运行 2 | 3 | # ------------------通用参数---------------------- 4 | # 微调方式,可选值为:lora, pissa,bone, state_tuning 5 | # 6 | # lora: 使用标准lora微调,但是lora微调速度较慢,效果一般,更推荐pissa 7 | # pissa: lora的改进版本,使用快速奇异值分解,收敛速度更快,效果更好,推荐使用 8 | # bone: 不同于lora系列的全新微调方法。bone_b(类似lora中的lora_r)越大,微调效果越好,泛化越强 9 | # bat: 与bone类似,区别在于bat会让模型学到更多的特征,但是训练速度较慢,效果更好 10 | # state_tuning: 微调init state,微调速度更快,占用显存更低。 11 | # 此微调不会让模型学到没有在预训练中学过的数据,如有需要请使用其它微调方式。 12 | FINETUNE_MODE="bone" 13 | 14 | # 微调附加类型,可选值为:none, infctx 15 | # 16 | # infctx: infctx使用时间换内存进行训练,当显存不足但语料过长时建议开启 17 | # 使用infctx时必须将DATALOAD修改为pad模式,否则会导致nan 18 | # 开启后使用更长上下文微调时,不会导致显存使用量增加。 19 | # 开启时最好同时开启FLA以启用triton算子,因为cuda算子的梯度有点问题 20 | TRAIN_TYPE="none" 21 | 22 | # 机器学习实验跟踪平台 wandb (Weights & Biases) 23 | # 24 | # 可以用于查看训练的每一步loss,并且可以进行不同训练的loss对比,还有EMA(移动指数平滑)等各种图标展示功能 25 | # 还可以查看设定的训练参数,如需使用,需要在[https://wandb.ai/]注册账号,并复制key 26 | # 在此参数中设定wandb的名字(任意)后根据命令行提示粘贴key(命令行输入key时不显示任何内容是正常的,粘贴后直接回车即可) 27 | # 绑定后即可使用并在每次训练后查看数据图和远程关闭训练等操作 28 | WANDB="" 29 | 30 | # 训练的RWKV模型的架构版本,可选值为:v5, v6, v7 31 | MODEL_VERSION="v7" 32 | 33 | # 量化方式,可选值为:none, 4bit, nf4, fp4, int8, fp8 34 | # 35 | # 4位量化一般推荐使用nf4,分布更均匀,8位量化则推荐int8,更推荐8位量化,损失更小 36 | # int8量化在pissa中损失更小,而在bone中和非量化结果基本一致 37 | # fp8损失更大,但是训练效率更高 38 | # 多卡微调中不建议进行任何程度的量化,因为会有一显卡的显存被占满,其它卡则占用很低的情况发生 39 | # 无法均衡负载微调 40 | QUANT="none" 41 | 42 | # 启用其它算子,可选值为:cuda, fla, triton(v7可用) 43 | # 44 | # 如需进行state_tuning微调,必须使用fla算子,否则建议使用cuda算子(前提是你的显卡是NVIDIA的) 45 | OP="cuda" 46 | 47 | # 模型路径 48 | # 49 | # 对应的是在model文件夹下需要微调的模型的文件名 50 | MODEL_PATH=RWKV-x070-World-1.5B-v3-20250127-ctx4096.pth 51 | 52 | # 嵌入维度 53 | # 54 | # 此参数应该根据模型的参数量进行调整: 55 | # v6 56 | # 14B EMBD_SIZE = 4096 57 | # 7B EMBD_SIZE = 4096 58 | # 3B EMBD_SIZE = 2560 59 | # 1.5B、1.6B、0.43B EMBD_SIZE = 2048 60 | # 0.17B EMBD_SIZE = 768 61 | 62 | # v7 63 | # 3B EMBD_SIZE = 2560 64 | # 1.5B EMBD_SIZE = 2048 65 | # 0.4B EMBD_SIZE = 1024 66 | EMBD_SIZE=2048 67 | 68 | # 嵌入层 69 | # 70 | # 此参数应该根据模型的参数量进行调整: 71 | # v6 72 | # 14B N_LAYER = 61 73 | # 7B N_LAYER = 32 74 | # 3B N_LAYER = 32 75 | # 1.5B、1.6B、0.43B N_LAYER = 24 76 | # 0.17B N_LAYER = 12 77 | 78 | # v7 79 | # 3B N_LAYER = 32 80 | # 1.5B N_LAYER = 24 81 | # 0.4B N_LAYER = 24 82 | N_LAYER=24 83 | 84 | # 数据路径 85 | # 86 | # 对应的是在data文件夹下需要微调的使用数据的文件名 87 | DATA_PATH=sample 88 | 89 | 90 | # 上下文长度 91 | # 92 | # 使用./make_tokenize.sh {数据集名称}.jsonl脚本进行数据分词时能得到如:### max_length = 208 这样的输出 93 | # 其中208就是数据中最长的数据的长度,在pad模式和only模式中,应该填入此数值以保证数据能够完全被训练 94 | # 如果数据过长无法训练,建议降低上下文长度并使用get模式读取数据,可以节省资源 95 | # 使用./make_tokenize.sh {数据集名称}.jsonl 1 进行数据分词即可 96 | CTX_LEN=608 97 | 98 | # 训练的回合数,达到回合数后会停止训练 99 | # 100 | # 仅在数据读取模式为pad和only时生效 101 | EPOCH_COUNT=3 102 | 103 | # 回合步数 104 | # 应该根据训练数据的条数和微批次大小调整,公式为:数据集条数/微批次大小=回合步数 105 | EPOCH_STEPS=16 106 | 107 | # 模型保存间隔,每隔多少回合保存一次模型 108 | EPOCH_SAVE=1 109 | 110 | # 初始学习率 111 | # 112 | # 如果使用state模式微调,lr最好调高,建议使用动态学习率,从1到0.01,使用其它模式建议8e-5到1e-5之间 113 | # 优先选择2e-5到3e-5之间的范围作为初始学习率 114 | LR_INIT=2e-5 115 | 116 | # 最终学习率 117 | # 118 | # 通常建议和初始学习率一致,除非需要动态学习率 119 | # 动态学习率的下降速度与你设定的训练回合数和训练步数有关 120 | LR_FINAL=2e-5 121 | 122 | # 学习率衰减策略,可选值为:cos, wsd 123 | # 124 | # cos: 余弦衰减策略: 125 | # 初始lr定义了学习率的最大值,通常在训练开始时使用。 126 | # 最终lr定义了学习率的最小值,通常在训练结束时达到。 127 | # 学习率会按照余弦函数的形状从初始lr平滑地降低到最终lr。 128 | # 这种策略提供了一个从高到低的平滑过渡,有助于在训练初期快速学习,后期微调。 129 | 130 | # wsd: 预热-稳定-衰减 (Warmup-Stable-Decay) 策略: 131 | # 这个策略,可以理解成让模型先进行一个预热阶段,调整到最佳状态,然后再全力进行训练,最后平稳结束。它通常包含以下几个阶段: 132 | # 133 | # 1. 预热 (Warmup) 阶段: 134 | # 在训练的初始阶段,学习率并不会直接采用你设定的“初始学习率 (LR_INIT)”,而是从一个极小的值(例如0,或者一个低到可以忽略的值)开始。 135 | # 随后,在指定的“预热步数” (warmup steps) 范围内,学习率会逐渐线性或按其他方式增加,直至达到你设定的“初始学习率 (LR_INIT)”。 136 | # 这样做的目的是为了在训练初期稳定模型的参数更新,避免因初始学习率设置过高导致训练过程出现震荡或发散。 137 | # 138 | # 2. 稳定 (Stable) 阶段 (此阶段为可选): 139 | # 当学习率通过预热达到“初始学习率 (LR_INIT)”之后,可能会在该学习率水平上保持一段固定的训练步数。 140 | # 这个稳定期的长度由你配置,如果不需要,也可以将其设置为0以跳过此阶段。 141 | # 142 | # 3. 衰减 (Decay) 阶段: 143 | # 在预热阶段(以及可能的稳定阶段)完成后,学习率将从“初始学习率 (LR_INIT)”开始, 144 | # 按照预设的方式(例如线性、余弦等)逐渐降低,直至达到你设定的“最终学习率 (LR_FINAL)”。这个最终学习率通常是一个非常小的值,或者为0,标志着学习率调整的结束。 145 | # 146 | # 在 `wsd` 策略中: 147 | # “初始lr (LR_INIT)”:通常指的是预热阶段完成时达到、并且是后续衰减阶段开始时的目标学习率。 148 | # “最终lr (LR_FINAL)”:是学习率在训练末期将降低到的最低数值。(目前PEFT还有问题,Decay步骤不正确) 149 | LR_SCHEDULE="cos" 150 | 151 | # WARMUP_STEPS: 预热阶段的优化器步数。 152 | # 153 | # 这个参数控制着学习率从一个低点爬升到 LR_INIT 所需要的优化器迭代次数。 154 | # 预热期间的学习率具体是这么变化的: 155 | # 156 | # 1. 起始学习率: 157 | # 训练刚开始的时候,学习率并不是直接就用 LR_INIT,而是会从一个更低的值开始,具体来说,就是 LR_INIT / 5。 158 | # 159 | # 2. 学习率爬升: 160 | # 在 WARMUP_STEPS 指定的这么多优化器步数内,学习率会从上面那个 LR_INIT / 5 的起始值,稳步地、通常是线性地增长,直到达到 LR_INIT。 161 | # 162 | # 3. 预热完成,进入下一阶段: 163 | # 一旦完成了这 WARMUP_STEPS 步的预热,学习率就正好达到了 LR_INIT。从这个时候开始,学习率就会按照你选定的衰减策略(比如 cos、wsd )从 LR_INIT 逐渐向 LR_FINAL 过渡。 164 | # 165 | # 此参数的步数需要基于以下公式设定:数据集条数(DATA_LINE_COUNT) / 微批次大小(MICRO_BSZ) / 梯度累计(MINI_BSZ) = 总预热步数 166 | # 关于怎么设置 WARMUP_STEPS,给你点实用建议: 167 | # - 这个值不是孤立的,你需要把它和你的总训练步数、LR_INIT、LR_FINAL 这些参数一起考虑,让它们互相配合好,才能发挥最大效用。 168 | # - 一般的经验是,如果你的模型特别大,或者你计划的训练总步数比较多,那么适当增加 WARMUP_STEPS 通常是个好主意。这能帮助训练在开始时更稳定,不容易跑偏,有时候还能帮助模型找到更好的收敛点。 169 | # - 特别注意:当你设定的 LR_INIT 比较高,或者 LR_INIT 和 LR_FINAL 之间的差距比较大的时候,一个设计合理的预热阶段就显得尤其重要了。 170 | WARMUP_STEPS=20 171 | 172 | # 显卡数量 173 | GPU_COUNT=1 174 | 175 | # 微批次大小,此配置项越大,显存占用越大,但是训练速度越快,同时配合梯度累计(MINI_BSZ)让训练更平稳 176 | # 177 | # 此配置项非1时应该跟随数据集条数调整回合步数(EPOCH_STEPS)参数,计算公式为:数据集条数(DATA_LINE_COUNT) / 微批次大小(MICRO_BSZ) = 回合步数(EPOCH_STEPS) 178 | # 179 | # 例如:数据集条数为10000,微批次大小为10,回合步数应该设置为1000 180 | # 微批次大小并不是越大越好,更大的微批次大小会导致学不到东西,同时增加显存占用和降低微调速度,建议根据数据集数量调整 181 | # 一般来说,数据集条数小于5k条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为8或16 182 | # 数据集条数大于1w条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为32或64 183 | # 如果过数据小于1k甚至200条,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为4到8 184 | # 再少就太离谱了点,如果你真有这么少的数据,就学学sample数据集微调的调参吧(当然这只是给你跑测试的,这么少的数据无法保证效果) 185 | MICRO_BSZ=1 186 | 187 | # 梯度累计,如果显存不够无法调整微批次大小 188 | # 189 | # 此配置项非1时应该跟随数据集条数调整,但是和微批次大小不同,梯度累计不能让训练步数降低 190 | # 例如:数据集条数(DATA_LINE_COUNT)为10000,微批次大小(MINI_BSZ)为10,梯度累计(MINI_BSZ)为2 191 | # 回合步数(EPOCH_STEPS)应该设置为1000而不是500 192 | # 梯度累计不能降低每个训练步数的数量,但是可以稳定loss,可以作为微批次大小的下位替代 193 | # 194 | # 梯度累计可以理解为微批次大小的下位替代,效果会差一点 195 | # 梯度累计并不是越大越好,更大的梯度累计会导致学不到东西,建议根据数据集数量调整 196 | # 一般来说,数据集条数小于5k条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为8或16 197 | # 数据集条数大于1w条时,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为32或64 198 | # 如果过数据小于1k甚至200条,微批次大小(MICRO_BSZ)*梯度累计(MINI_BSZ)建议为4到8 199 | # 再少就太离谱了点,如果你真有这么少的数据,就学学sample数据集微调的调参吧(当然这只是给你跑测试的,这么少的数据无法保证效果) 200 | MINI_BSZ=2 201 | 202 | # 数据集读取模式,可选值为:get,pad,only 203 | # 204 | # get: 从数据中随机截取一段基于上下文长度的数据进行训练,适用于数据集较大但是上下文长度无法调高的情况 205 | # pad: 从数据最开始到结束进行训练,如果数据长度小于上下文长度,则填充上下文,适用于微批次大小大于1的配置,建议使用此配置时根据最长数据调整上下文长度 206 | # only: 从数据最开始到结束进行训练,即使数据集长度超过上下文长度,也会从最开始截取到上下文长度的数据进行训练,适用于微批次大小为1的配置,建议使用此配置时根据最长数据调整上下文长度 207 | # 在上下文长度允许的情况下,更推荐使用pad(微批次大于1)或者only(微批次为1)模式,可以更好的学习到数据的连贯特征 208 | DATALOAD="pad" 209 | 210 | # loss掩码,可选值为:none, pad, qa, se 211 | # 212 | # 此参数用于指示模型在计算损失时应该关注哪些位置,带来的好处是LLM不会学习和模仿Mask部分说话 213 | # 让模型可以更快地学习到有用的特征,并减少不必要的计算 214 | # 215 | # qa: 忽略掉名为"User"的角色回复的loss不纳入计算 216 | # 如果找不到"User"和"Assistant"(对话不使用User:xxx\n\nAssistant:xxx)格式 217 | # 则自动降级为pad模式 218 | # 219 | # pad: 忽略掉所有超出当条数据长度的部分不做loss计算 220 | # 如CTX_LEN=200,但是某一条输入数据只有100,则忽略超出实际数据长度部分loss计算 221 | # 222 | # se: 目前有问题,等待上游更新 223 | LOSS_MASK="qa" 224 | 225 | # 开启微调附加项的infctx参数后启用的设置,此设置用于确定在infctx中单次训练的上下文长度,此参数越高,消耗的显存越多 226 | # 227 | # 相当于不开启infctx时的CTX_LEN参数,一般建议能开多大开多大(仅在infctx启用时有效) 228 | # CTX_LEN必须大于或者等于此参数 229 | CHUNK_CTX=512 230 | 231 | # 训练数据自动洗牌,从第一个epoch开始打乱训练数据排列 232 | # 233 | # 如果开启此项,使用`make_tokenize.sh`进行分词时训练回合数应该为1,训练回合数最好大于1 234 | # 多个回合的训练会在每个训练回合训练的开始从头读取数据,如果不打乱数据,则每次都会读取到一样的数据排列,影响泛化 235 | # 236 | # 如果关闭此项,使用`make_tokenize.sh`进行分词时训练回合数最好大于1,训练回合数应该为1 237 | # 单个回合的训练会在训练的开始打乱数据,如果打乱数据,则会导致多个回合的数据重复被刷到一块,导致错误的拟合 238 | DATA_SHUFFLE=1 239 | 240 | # 优化策略, 可选值为:deepspeed_stage_1, deepspeed_stage_2, deepspeed_stage_3 241 | # 242 | # 建议使用deepspeed_stage_2节省显存的同时也能保证微调速度 243 | # deepspeed_stage_1: 完全使用显卡内存,不适用于显存较小的显卡,在显存足够的时候速度较快,使用量化微调时建议开启以加速训练 244 | # deepspeed_stage_2: 使用显卡内存和RAM内存,适用于显存较小的显卡,能节省显存的同时保证速度 245 | # deepspeed_stage_3: 使用显卡内存和RAM内存,硬盘内存,适用于显存极小的显卡,速度最慢,除非RAM和显存都不足,否则不建议使用 246 | DEEPSPEED_STRATEGY=deepspeed_stage_1 247 | 248 | # ------------------不常用训练参数---------------------- 249 | # 精度,可选值为:fp32, bf16, fp16,通常建议使用bf16,节省显存同时保证了训练精度 250 | PRECISION=bf16 251 | 252 | # 梯度复制,通常建议开启以节省显存 253 | GRAD_CP=1 254 | 255 | # 开始训练的回合,可以用来恢复训练 256 | EPOCH_BEGIN=0 257 | 258 | # 词表大小 259 | # 260 | # 此参数应该根据tokenizer/rwkv_vocab_v20230424.txt的词表大小进行调整,更改词表数量后应该修改此参数 261 | VOCAB_SIZE=65536 262 | 263 | 264 | # Bata1 265 | BETA1=0.9 266 | 267 | # Bata2 268 | BETA2=0.95 269 | 270 | # ADAM epsilon 271 | ADAM_EPS=1e-18 272 | 273 | # ------------------LoRA设置参数---------------------- 274 | 275 | # LoRA训练模型路径 276 | # 277 | # 代表从哪个LoRA模型开始微调,格式一般为 278 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 279 | lora_load="" 280 | 281 | # LoRA模型的r值 282 | # 283 | # 如果显存或者RAM不足,应该调低此值,一般训练使用32或者64即可,实在不够也可以用16 284 | lora_r=64 285 | 286 | 287 | # LORA模型的alpha值 288 | # 289 | # 此值应该配合r值调整 290 | # 计算公式为:lora_alpha=lora_r*2 291 | lora_alpha=128 292 | 293 | # LORA模型的dropout值 294 | # 295 | # lora_dropout在训练过程中随机丢弃一部分神经元来防止过拟合。 296 | # lora_dropout=0.01 表示在训练过程中,每次更新时有 1% 的神经元会被随机丢弃。这样可以提高模型的泛化能力。 297 | # 但同时也会降低训练效率,因此在训练过程中,lora_dropout的值一般设置为0.01 298 | lora_dropout=0.01 299 | 300 | 301 | # ------------------PiSSA设置参数---------------------- 302 | # PiSSA初始化的模型路径 303 | # 304 | # 代表从哪个PiSSA模型的初始化开始加载PiSSA 305 | # 如果需要继续训练,一般设置为 306 | # "init_pissa.pth"即可 307 | pissa_init="" 308 | 309 | # PiSSA训练模型路径 310 | # 311 | # 代表从哪个PiSSA模型开始微调,格式一般为 312 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 313 | pissa_load="" 314 | 315 | # PiSSA模型的r值 316 | # 317 | # 越大的r值,微调的效果越好,同时也能让PiSSA微调的奇异值分解精度越高,但是显存占用越高,建议最大128 318 | # 如果显存或者RAM不足,应该调低此值,一般训练使用32或者64即可,实在不够也可以用16 319 | pissa_r=64 320 | 321 | # PiSSA的快速奇异值分解的迭代次数,迭代次数越高损失越低,但是初始化的速度就越慢 322 | # 323 | # 如果需要快速初始化(如微调14B),可以适当调低此值,但是损失会增加 324 | # 一般来说svd_niter=16后就已经非常接近奇异值分解的结果了,往后的迭代次数对结果影响不会很大 325 | # 此外,lora_r对于svd_niter的影响也很大,lora_r越大,训练损失越低,但是svd_niter也需要相应增加 326 | svd_niter=16 327 | 328 | 329 | # ------------------DiSHA设置参数---------------------- 330 | # DiSHA训练模型路径 331 | # 332 | # 代表从哪个检查点开始微调,格式一般为 333 | # "rwkv-0.pth" "rwkv-10.pth"这样即可 334 | disha_load="" 335 | 336 | # DiSHA的r值 337 | # 338 | # 类似lora的r,disha_r=128相当于lora_r=64的占用,越大的值微调的参数量越多 339 | # 通常64或者128即可,实在不够也可以用32,128已经几乎可以释放DiSHA的全部潜力了 340 | # 除非你的微调场景非常特殊,甚至模型根本没见过,否则不建议使用更大的值 341 | # disha_r必须能被维度整除 342 | disha_r=128 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | # ---------------------源代码--------------------- 394 | case "$MODEL_VERSION" in 395 | "v7") 396 | echo "-------------RWKV7微调模式-------------" 397 | TRAIN_VERSION="--my_testing x070" 398 | ;; 399 | "v6") 400 | echo "-------------RWKV6微调模式-------------" 401 | TRAIN_VERSION="--my_testing x060" 402 | ;; 403 | "v5") 404 | echo "-------------RWKV5微调模式-------------" 405 | TRAIN_VERSION="" 406 | ;; 407 | *) 408 | echo "!!!!!!!!!!!!!不支持的模型版本$MODEL_VERSION,仅支持v5, v6,另外v4已经过时,不建议使用!!!!!!!!!!!!!" 409 | exit 1 410 | ;; 411 | esac 412 | 413 | INFCTX="" 414 | case "$TRAIN_TYPE" in 415 | "infctx") 416 | if [ "$FINETUNE_MODE" = "state_tuning" ]; then 417 | echo "!!!!!!!!!!!!!state_tuning不支持的附加微调模式$TRAIN_TYPE!!!!!!!!!!!!!" 418 | exit 1 419 | fi 420 | echo "-------------使用$TRAIN_TYPE附加模式微调-------------" 421 | INFCTX="--train_type infctx" 422 | ;; 423 | "none") 424 | ;; 425 | *) 426 | echo "!!!!!!!!!!!!!不支持的附加微调模式$TRAIN_TYPE,仅支持none, infctx!!!!!!!!!!!!!" 427 | exit 1 428 | ;; 429 | esac 430 | 431 | 432 | case "$LOSS_MASK" in 433 | "qa"|"pad"|"se") 434 | echo "-------------使用$LOSS_MASK模式-------------" 435 | ;; 436 | "none") 437 | ;; 438 | *) 439 | echo "!!!!!!!!!!!!!不支持的loss掩码参数$LOSS_MASK,仅支持none, qa, pad, se!!!!!!!!!!!!!" 440 | exit 1 441 | ;; 442 | esac 443 | 444 | case "$QUANT" in 445 | "4bit"|"nf4"|"fp4"|"int8"|"fp8") 446 | echo "-------------使用$QUANT精度量化微调-------------" 447 | ;; 448 | "none") 449 | echo "-------------不使用量化微调-------------" 450 | ;; 451 | *) 452 | echo "!!!!!!!!!!!!!不支持的量化精度参数$QUANT,仅支持none, 4bit, nf4, fp4, int8, fp8!!!!!!!!!!!!!" 453 | exit 1 454 | ;; 455 | esac 456 | 457 | 458 | 459 | if [ "$OP" = "cuda" ]; then 460 | echo "-------------使用cuda算子-------------" 461 | elif [ "$OP" = "fla" ]; then 462 | echo "-------------使用fla算子-------------" 463 | elif [ "$OP" = "triton" ]; then 464 | echo "-------------使用triton算子-------------" 465 | else 466 | echo "!!!!!!!!!!!!!不支持的OP参数$OP,仅支持cuda, fla, triton(v7可用)!!!!!!!!!!!!!" 467 | exit 1 468 | fi 469 | 470 | case "$LR_SCHEDULE" in 471 | "cos"|"wsd") 472 | echo "-------------使用$LR_SCHEDULE学习率衰减策略-------------" 473 | ;; 474 | *) 475 | echo "!!!!!!!!!!!!!不支持的学习率衰减策略参数$LR_SCHEDULE,仅支持cos, wsd!!!!!!!!!!!!!" 476 | exit 1 477 | ;; 478 | esac 479 | 480 | case "$DATALOAD" in 481 | "pad"|"get"|"only") 482 | echo "-------------使用$DATALOAD模式读取数据-------------" 483 | ;; 484 | *) 485 | echo "!!!!!!!!!!!!!不支持的数据集获取参数$DATALOAD,仅支持pad, get, only!!!!!!!!!!!!!" 486 | exit 1 487 | ;; 488 | esac 489 | 490 | if [ "$FINETUNE_MODE" = "state_tuning" ]; then 491 | COMMAND="python3 train.py --load_model 'model/${MODEL_PATH}' \ 492 | --proj_dir 'output' --data_file 'data/${DATA_PATH}' \ 493 | --data_type binidx --vocab_size ${VOCAB_SIZE} \ 494 | --ctx_len ${CTX_LEN} --epoch_steps ${EPOCH_STEPS} --epoch_count ${EPOCH_COUNT} --epoch_begin ${EPOCH_BEGIN} --epoch_save ${EPOCH_SAVE} --micro_bsz ${MICRO_BSZ} \ 495 | --n_layer ${N_LAYER} --n_embd ${EMBD_SIZE} \ 496 | --lr_init ${LR_INIT} --lr_final ${LR_FINAL} --warmup_steps ${WARMUP_STEPS} --beta1 ${BETA1} --beta2 ${BETA2} --adam_eps ${ADAM_EPS} \ 497 | --accelerator gpu --devices ${GPU_COUNT} --precision ${PRECISION} --strategy ${DEEPSPEED_STRATEGY} --grad_cp ${GRAD_CP} \ 498 | --accumulate_grad_batches ${MINI_BSZ} --dataload ${DATALOAD} --chunk_ctx ${CHUNK_CTX} --data_shuffle ${DATA_SHUFFLE} \ 499 | --quant ${QUANT} --loss_mask ${LOSS_MASK} --train_type 'state' --op ${OP} --lr_schedule ${LR_SCHEDULE}\ 500 | --wandb \"${WANDB}\" ${INFCTX} ${TRAIN_VERSION}" 501 | echo "-------------使用StateTuning方法微调-------------" 502 | elif [ "$FINETUNE_MODE" = "lora" ]; then 503 | printf -v lora_config '{"lora_load":"%s","lora_r":%s,"lora_alpha":%s,"lora_dropout":%s}' "$lora_load" "$lora_r" "$lora_alpha" "$lora_dropout" 504 | COMMAND="python3 train.py --load_model 'model/${MODEL_PATH}' \ 505 | --proj_dir 'output' --data_file 'data/${DATA_PATH}' \ 506 | --data_type binidx --vocab_size ${VOCAB_SIZE} \ 507 | --ctx_len ${CTX_LEN} --epoch_steps ${EPOCH_STEPS} --epoch_count ${EPOCH_COUNT} --epoch_begin ${EPOCH_BEGIN} --epoch_save ${EPOCH_SAVE} --micro_bsz ${MICRO_BSZ} \ 508 | --n_layer ${N_LAYER} --n_embd ${EMBD_SIZE} \ 509 | --lr_init ${LR_INIT} --lr_final ${LR_FINAL} --warmup_steps ${WARMUP_STEPS} --beta1 ${BETA1} --beta2 ${BETA2} --adam_eps 1e-8 \ 510 | --accelerator gpu --devices ${GPU_COUNT} --precision ${PRECISION} --strategy ${DEEPSPEED_STRATEGY} --grad_cp ${GRAD_CP} \ 511 | --accumulate_grad_batches ${MINI_BSZ} --dataload ${DATALOAD} --chunk_ctx ${CHUNK_CTX} --data_shuffle ${DATA_SHUFFLE} \ 512 | --peft lora --lora_config '${lora_config}' \ 513 | --wandb \"${WANDB}\" --quant ${QUANT} --loss_mask ${LOSS_MASK} ${INFCTX} ${TRAIN_VERSION} --op ${OP} --lr_schedule ${LR_SCHEDULE}" 514 | echo "-------------使用LoRA方法微调-------------" 515 | elif [ "$FINETUNE_MODE" = "bone" ]; then 516 | printf -v disha_config '{"mode":"bone","load":"%s","r":%s}' "$disha_load" "$disha_r" 517 | COMMAND="python3 train.py --load_model 'model/${MODEL_PATH}' \ 518 | --proj_dir 'output' --data_file 'data/${DATA_PATH}' \ 519 | --data_type binidx --vocab_size ${VOCAB_SIZE} \ 520 | --ctx_len ${CTX_LEN} --epoch_steps ${EPOCH_STEPS} --epoch_count ${EPOCH_COUNT} --epoch_begin ${EPOCH_BEGIN} --epoch_save ${EPOCH_SAVE} --micro_bsz ${MICRO_BSZ} \ 521 | --n_layer ${N_LAYER} --n_embd ${EMBD_SIZE} \ 522 | --lr_init ${LR_INIT} --lr_final ${LR_FINAL} --warmup_steps ${WARMUP_STEPS} --beta1 ${BETA1} --beta2 ${BETA2} --adam_eps ${ADAM_EPS} \ 523 | --accelerator gpu --devices ${GPU_COUNT} --precision ${PRECISION} --strategy ${DEEPSPEED_STRATEGY} --grad_cp ${GRAD_CP} \ 524 | --accumulate_grad_batches ${MINI_BSZ} --dataload ${DATALOAD} --chunk_ctx ${CHUNK_CTX} --data_shuffle ${DATA_SHUFFLE} \ 525 | --wandb \"${WANDB}\" --quant ${QUANT} --peft disha --disha_config '${disha_config}' --loss_mask ${LOSS_MASK} --op ${OP} --lr_schedule ${LR_SCHEDULE} ${INFCTX} ${TRAIN_VERSION} ${EMB}" 526 | echo "-------------使用Bone方法微调-------------" 527 | elif [ "$FINETUNE_MODE" = "bat" ]; then 528 | printf -v disha_config '{"mode":"bat","load":"%s","r":%s}' "$disha_load" "$disha_r" 529 | COMMAND="python3 train.py --load_model 'model/${MODEL_PATH}' \ 530 | --proj_dir 'output' --data_file 'data/${DATA_PATH}' \ 531 | --data_type binidx --vocab_size ${VOCAB_SIZE} \ 532 | --ctx_len ${CTX_LEN} --epoch_steps ${EPOCH_STEPS} --epoch_count ${EPOCH_COUNT} --epoch_begin ${EPOCH_BEGIN} --epoch_save ${EPOCH_SAVE} --micro_bsz ${MICRO_BSZ} \ 533 | --n_layer ${N_LAYER} --n_embd ${EMBD_SIZE} \ 534 | --lr_init ${LR_INIT} --lr_final ${LR_FINAL} --warmup_steps ${WARMUP_STEPS} --beta1 ${BETA1} --beta2 ${BETA2} --adam_eps ${ADAM_EPS} \ 535 | --accelerator gpu --devices ${GPU_COUNT} --precision ${PRECISION} --strategy ${DEEPSPEED_STRATEGY} --grad_cp ${GRAD_CP} \ 536 | --accumulate_grad_batches ${MINI_BSZ} --dataload ${DATALOAD} --chunk_ctx ${CHUNK_CTX} --data_shuffle ${DATA_SHUFFLE} \ 537 | --wandb \"${WANDB}\" --quant ${QUANT} --peft disha --disha_config '${disha_config}' --loss_mask ${LOSS_MASK} --op ${OP} --lr_schedule ${LR_SCHEDULE} ${INFCTX} ${TRAIN_VERSION} ${EMB}" 538 | echo "-------------使用Bat方法微调-------------" 539 | elif [ "$FINETUNE_MODE" = "pissa" ]; then 540 | printf -v pissa_config '{"pissa_load":"%s","pissa_init":"%s","pissa_r":%s,"svd_niter":%s}' "$pissa_load" "$pissa_init" "$pissa_r" "$svd_niter" 541 | COMMAND="python3 train.py --load_model 'model/${MODEL_PATH}' \ 542 | --proj_dir 'output' --data_file 'data/${DATA_PATH}' \ 543 | --data_type binidx --vocab_size ${VOCAB_SIZE} \ 544 | --ctx_len ${CTX_LEN} --epoch_steps ${EPOCH_STEPS} --epoch_count ${EPOCH_COUNT} --epoch_begin ${EPOCH_BEGIN} --epoch_save ${EPOCH_SAVE} --micro_bsz ${MICRO_BSZ} \ 545 | --n_layer ${N_LAYER} --n_embd ${EMBD_SIZE} \ 546 | --lr_init ${LR_INIT} --lr_final ${LR_FINAL} --warmup_steps ${WARMUP_STEPS} --beta1 ${BETA1} --beta2 ${BETA2} --adam_eps ${ADAM_EPS} \ 547 | --accelerator gpu --devices ${GPU_COUNT} --precision ${PRECISION} --strategy ${DEEPSPEED_STRATEGY} --grad_cp ${GRAD_CP} \ 548 | --accumulate_grad_batches ${MINI_BSZ} --dataload ${DATALOAD} --chunk_ctx ${CHUNK_CTX} --data_shuffle ${DATA_SHUFFLE} \ 549 | --peft pissa --pissa_config '${pissa_config}' \ 550 | --wandb \"${WANDB}\" --quant ${QUANT} --loss_mask ${LOSS_MASK} --op ${OP} --lr_schedule ${LR_SCHEDULE} ${INFCTX} ${TRAIN_VERSION}" 551 | echo "-------------使用PiSSA方法微调-------------" 552 | else 553 | echo "!!!!!!!!!!!!!不支持的微调方法$FINETUNE_MODE,仅支持state_tuning, lora, pissa, bone, bat!!!!!!!!!!!!!" 554 | exit 1 555 | fi 556 | CURRENT_DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S") 557 | echo "# $CURRENT_DATE_TIME" >> history_run_command.sh.log 558 | echo "$COMMAND" >> history_run_command.sh.log 559 | echo "" >> history_run_command.sh.log 560 | eval "$COMMAND" --------------------------------------------------------------------------------