diff --git a/Experiments/configs/microstories/config.json b/Experiments/configs/microstories/config.json new file mode 100644 index 0000000..4c637fc --- /dev/null +++ b/Experiments/configs/microstories/config.json @@ -0,0 +1,30 @@ +{ + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "initializer_range": 0.02, + "aux_loss_alpha": 0.01, + "hidden_act": "silu", + "tie_word_embeddings": true, + "dropout": 0.0, + "flash_attn": true, + "hidden_size": 768, + "intermediate_size": 2048, + "model_type": "llama", + "multiple_of": 512, + "num_attention_heads": 16, + "num_key_value_heads": 4, + "num_hidden_layers": 16, + "max_position_embeddings": 512, + "n_routed_experts": 4, + "n_shared_experts": true, + "rms_norm_eps": 1e-5, + "norm_topk_prob": true, + "num_experts_per_tok": 2, + "scoring_func": "softmax", + "seq_aux": true, + "torch_dtype": "float32", + "transformers_version": "4.44.0", + "use_moe": false, + "vocab_size": 6400 +} diff --git a/Journey/Day09/Day09.ipynb b/Journey/Day09/Day09.ipynb index 2fa2a0c..e83b7ad 100644 --- a/Journey/Day09/Day09.ipynb +++ b/Journey/Day09/Day09.ipynb @@ -86,51 +86,57 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from litgpt import LLM\n", - "from litgpt.prompts import MicroStories\n", + "from litgpt.prompts import Phi2\n", "\n", - "ms = MicroStories()\n", - "llm = LLM.load(model=\"../../Experiments/Output/sft/microstories/mask_prompt_5e-4/final\")" + "prompt_style = Phi2()\n", + "llm = LLM.load(\n", + " model=\"../../Experiments/Output/sft/microstories_v2/bf16_mixed_5e-4/step-001000\"\n", + ")" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "从前,有一个大草地。它大得让人惊叹,因为有一群喜欢玩沙子。孩子们会围着草滚来滚去,用铲子在草地上挖沙。\n", - "一天,一个叫莉莉的小女孩决定去玩那个大草地。她拿起铲子开始挖沙。她挖呀挖,直到她发现了一块大石头。她把石头给她的朋友们看,并问能不能用它来挖。\n", - "她的朋友们也挖沙子,但他们挖不出来。然后,他们又开始挖那块石头。突然,莉莉听到了一声巨响。她把沙子挖出来,看到她的朋友们在玩一个玩具。她想起了自己的铲子,微笑着。她知道可以用它来挖沙。\n", - "从那以后,莉莉就爱上了她的大草地和她的铲子。她总是用那块大铲子,确保她的玩具保持安全。每次找到新玩意儿,她都会把它拿出来,用它来挖沙。\n", + "从前,有一只叫汤姆的胖猫。汤姆有一个最好的朋友,一只叫杰瑞的小老鼠。他们喜欢一起玩耍和分享。汤姆和杰瑞是最好的朋友。 \n", + "一天,汤姆和杰瑞在玩一个大红球。他们玩得可开心了。然后,汤姆看到地上有个开关。他想看看它有什么作用。于是,他按下了开关,发生了意想不到的事情。 \n", + "汤姆和杰瑞开始慌张。他们不知道该怎么办。突然,一只叫杰瑞的小老鼠从开关里跑了出来。汤姆和杰瑞看到彼此的反应,开心极了。他们笑着拥抱在一起。从那天起,汤姆和杰瑞就一直是最好的朋友。 \n", + "总结一下:一只胖猫汤姆和一只杰瑞一起玩开关,变成了一个有趣的发现。\n", "----------------------------------------------------------------------------------------------------\n", - "从前,有一个小男孩。小男孩,他喜欢玩玩具。他喜欢玩玩具,也喜欢和妈妈和爸爸一起玩。 \n", - "一天,他看到一只狗。那只狗非常活泼,也想玩耍。他觉得和狗一起玩一定很有趣。 \n", - "那只狗的爪子一碰到小水,竟然开始在地上倒水!小男孩非常开心,他捡起水,开始在水面上摇晃。他玩得非常开心,没多久就该把水倒回水里了。小男孩把水倒在地上,狗也跟着他跑。他们玩得特别开心,小男孩说:“谢谢你,小狗,帮了我!” \n", - "小狗用摇晃的水瓶摇晃着,小男孩玩得不亦乐乎。他们玩了很长时间,回家的时候,小男孩和狗都很累。他们都带着灿烂的笑容,小男孩带着他的新朋友小狗,开心地回家。\n", + "从前,有一个小男孩叫詹姆斯。他喜欢在草地上玩。一天,他在草地上看到一把铲子,决定捡起来。他开始挖土,挖了很久,突然看到有什么东西藏在土里。 \n", + "詹姆斯非常好奇,于是开始用铲子挖。他挖呀挖,终于找到了什么东西。他不停地挖,直到看到一把大武器! \n", + "詹姆斯既惊讶又害怕,但他还是勇敢地把武器捡了起来。他把武器给妈妈看,妈妈非常为他骄傲。 \n", + "“詹姆斯,你在草地上找到了什么啊?”妈妈问。 \n", + "詹姆斯举起武器,说:“我不知道,但我很勇敢。” \n", + "妈妈微笑着说:“这武器看起来挺棘,詹姆斯。也许它是用来挖东西的啾。” \n", + "詹姆斯点头上挂着闪闪发光的铲子,\n", "----------------------------------------------------------------------------------------------------\n", - "从前,有一只小蚂蚁。蚂蚁在花园里玩耍,发现了一颗好吃的苹果。他吃了苹果,开始感到快乐。\n", - "但随后,蚂蚁看到花园里有一只大狗。狗看到小蚂蚁后,开始追它。\n", - "小蚂蚁感到很害怕。它不知道该怎么办。然后,小蚂蚁听到了他的声音。\n", - "他找到了一些种子,吃了。他感到安全。\n", - "蚂蚁很高兴,感到很满足。他不再害怕狗,继续在花园里享受吃草。\n", - "\n", - "故事结束了。\n", + "从前,有个小女孩叫莉莉。她喜欢在家里蹦蹦跳跳,玩得不亦乐乎。一天,她在厨房里发现了一个大圆形的水果。它是圆圆的,红红的。莉莉以为那是个球,但她不知道这是什么。 \n", + "莉莉的妈妈看到她在玩水果,心里有些担心。她告诉莉莉那是苹果,不是球。莉莉虽然不太明白,但还是听妈妈的话,又回去玩她的玩具。 \n", + "这次,莉莉的妈妈把水果切好了。她给莉莉一片红苹果。莉莉高兴得不得了,因为她喜欢苹果!她吃了苹果,感到很饱。从那天起,莉莉知道了这个水果是什么——是苹果! \n", + "小结:莉莉喜欢在厨房里的圆形水果,但她的妈给她切了一片,以后给她尝。\n", "----------------------------------------------------------------------------------------------------\n", - "从前,有一个小男孩,他感到非常伤心。他想要一些东西来让他的感到快乐,但不知道该怎么做。他看到云朵,他试着想一些特别的东西,但不知道是什么。他环顾四周,看到天空中的星星,感到很快乐。他知道自己想要一些特别的东西。\n", - "\n", - "他决定试着让那闪烁的星星消失。他拿起他最喜欢的玩具,一个拼图块。他开始拼这个拼图,拼了很久。他拼了很久,直到拼图拼得整幅画。\n", - "\n", - "天空中飘着白云。它们又黑又亮。他感到很开心。他不再感到伤心了,因为他知道闪烁的星星是特别的。\n", - "\n", - "小男孩微笑着,心里充满了快乐。然后他知道自己做了一件特别的事情。\n", + "从前,有一只小蚂蚁。蚂蚁小得可爱极了。它在花园里四处走动,寻找食物。突然,蚂蚁看到了一个大红苹果。它想咬一口苹果,但苹果太大了,蚂蚁够不着。 \n", + "这时,蚂蚁看见了一只大棕蚂蚁。“请问,蚂蚁,您能帮我一下吗?”蚂蚁说。“当然,我可以帮你咬一口苹果。”棕色蚂蚁回答。 \n", + "棕色蚂蚁用刀把苹果切成了两半。蚂蚁非常高兴,谢谢了朋友们。从那天起,蚂蚁们就开始互相分享食物了。 \n", + "总结一下:蚂蚁们在花园里发现了一个大红苹果,但它太大,无法自己吃掉。棕色蚂蚂蚂蚁把苹果切开,发现里面有苹果,大家都很开心。\n", + "----------------------------------------------------------------------------------------------------\n", + "从前,有一个小女孩叫莉莉。她喜欢在外面玩耍,仰望天空中美丽的蓬松白云。一天,她看到一朵大白云,看起来像一朵白云。她指着它说:“看,妈妈,一朵云!” \n", + "她的妈妈说:“那朵云真漂亮,莉莉。它是由天空中飘的一小堆云组成的。” \n", + "莉莉微笑着说:“我喜欢看云朵在天空中飘动。它们看起来像动物和植物。” \n", + "她的妈妈回答:“是的,它们看起来确实像动物和植物。也许它们都是云,但看起来像我们,不是很美。” \n", + "莉莉点头说:“是的,它们很美。我喜欢看云朵在天空中飘动。” \n", + "她的妈妈妈妈妈妈妈妈:“我也是,莉莉。天空中总是那么美丽。”\n", "----------------------------------------------------------------------------------------------------\n" ] } @@ -138,30 +144,25 @@ "source": [ "test_cases = [\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"词汇:铲子,草地\\n\",\n", + " \"instruction\": \"词汇:铲子,草地\\n\",\n", " },\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"特点:转折\\n\",\n", + " \"instruction\": \"特点:转折\\n\",\n", " },\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"摘要:一只小蚂蚁在花园里寻找吃的,最后找到了一个苹果。\\n\",\n", + " \"instruction\": \"摘要:一只小蚂蚁在花园里寻找吃的,最后找到了一个苹果。\\n\",\n", " },\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"随机句子:天空中飘着几朵白云。\\n\",\n", + " \"instruction\": \"随机句子:天空中飘着几朵白云。\\n\",\n", " },\n", "]\n", - "prompts = [\n", - " ms.apply(prompt=case[\"instruction\"], input=case[\"input\"]) for case in test_cases\n", - "]\n", + "prompts = [prompt_style.apply(prompt=case[\"instruction\"]) for case in test_cases]\n", + "prompts.insert(0, \"汤姆和杰瑞是一对好朋友\")\n", "for prompt in prompts:\n", " text = llm.generate(\n", " prompt=prompt,\n", " max_new_tokens=300,\n", - " temperature=0.8,\n", + " temperature=0.2,\n", " top_p=0.9,\n", " top_k=30,\n", " )\n", @@ -190,24 +191,27 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "一天,一个名叫蒂姆的男孩去了公园。他看到一个标志。标志上写着:“今天今天我们要去外面用铲子玩。”蒂姆非常高兴,因为他喜欢铲子。他觉得挖个大洞会很有趣。 \n", - "蒂姆开始用铲子挖土。他挖呀挖,挖出了一个大洞。沙子让他感到很累,但他在地下埋了虫子和石头。他非常喜欢这个公园,想要找到更多可以玩的东西。 \n", - "然后,突然发生了意外的事情。一个大盒子从公园的盖子开了它开进了公园。蒂姆感到非常惊讶。他打开盒子,发现里面有一只小虫子。虫子没有死,它只是想和蒂姆一起玩。他们在公园里一起玩得非常开心。\n", + "从前,有一个聪明的女孩叫露西。她喜欢在草地上玩耍。每天,她都会在草地上跑、跳、穿梭。 \n", + "一天,露西在草地上看到一把锋利的铲子。她捡起来开始玩。她像船一样在地上划过来。真是太好玩了! \n", + "露西的朋友汤姆来找她玩。他看到她在玩铲子,也想来试试。他们都假装在草地上划过来。他们笑着玩得特别开心。 \n", + "突然,露西踩到了一个坎子,铲子飞了起来!汤姆和露西都很惊讶,他们不知道那把铲子怎么会到那里的。他们在草地上飞快地跑着,铲子帮助他们找到更多的故事来探索。 \n", + "总结一下:露西在草地上发现了一把锹,她在草地上用它当作船,她和她的朋友汤姐姐姐在一起玩耍。\n", "----------------------------------------------------------------------------------------------------\n", - "从前,有一只小蚂蚁住在一个大花园里。这个花园很大,有很多草和树。小蚂蚁喜欢到处跑和探索。 \n", - "一天,小蚂蚁想要找到一些美味的食物。于是,小蚂蚁开始寻找吃的。小蚂蚁在土里挖呀挖,直到找到了一些苦涩的苹果。苹果太美味了! \n", - "小蚂蚁高兴得跑回去给妈妈看。妈妈也喜欢这些苹果。他们坐在花园里,一起吃苹果。小蚂蚁为自己找到了吃的而感到无比自豪。\n", + "从前有一只小蚂蚁,它是周围最小的蚂蚁。每天,这只小蚂蚁都在寻找食物。一天,它突然想到一个主意。它决定好好铺一块碗,里面放着一些食物,让其他蚂蚁也能吃。 \n", + "小蚂蚁小心翼翼地把食物倒入碗中。然后,它开始四处张望。它看到了许多美味的苹果片,真是太甜了!它忍不住咬了一口苹果,这是它吃过的最好吃的苹果。 \n", + "小蚂蚁非常高兴,开始带着碗回家。在路上,它注意到附近有几只蚂蚁。每只蚂蚁都有自己吃的食物。小蚂蚁真自豪华,心里满是这么多食物。 \n", + "当小蚂蚁回到家时,它决定好好地把食物放在自己的碗里。这样,它就可以和其他蚂蚂蚁一起分享。现在,所有的蚂蚁都能吃到食物,大家都很开心。\n", "----------------------------------------------------------------------------------------------------\n", - "从前,有一只小蚂蚁住在一个大花园里。这个花园里有许多蚂蚁在花园里努力工作,挖土。一天,这只小蚂蚁饿得不行。 \n", - "突然,小蚂蚁看到一朵朵下有一片闪亮的大白云。小蚂蚁心里想:“那是什么东西?”小蚂蚁继续行进,发现那是一棵苹果树。小蚂蚁吃了苹果,感到很开心。 \n", - "过了一会儿,小蚂蚁回到了小花园。小蚂蚁为找到这么好的一个家感到自豪。小蚂蚁四处游荡,给所有蚂蚁朋友看了这棵树。他们都微笑着,玩得特别开心。完。\n", + "从前,有一只小蚂蚁住在花园里。蚂蚁喜欢花园里的草地。一天,蚂蚁在花园里散步时,看见了一棵大树。树上结满了苹果。蚂蚁非常高兴,爬上了树。 \n", + "当蚂蚁到达树顶时,它看到一个苹果。那是一个鲜艳的苹果,看起来真好吃。但就在他准备咬一口时,他听到了一声响亮的声音。那是树枝上的一只声音。树枝枝枝折断了,蚂蚁掉到了地上。 \n", + "蚂蚁很害怕,但它没事。它爬回了家,吃了其他食物。结束。\n", "----------------------------------------------------------------------------------------------------\n" ] } @@ -215,26 +219,21 @@ "source": [ "test_cases = [\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"词汇:铲子,草地\\n特点:转折\\n\",\n", + " \"instruction\": \"词汇:铲子,草地\\n特点:转折\\n\",\n", " },\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"词汇:铲子,草地\\n摘要:一只小蚂蚁在花园里寻找吃的,最后找到了一个苹果。\\n\",\n", + " \"instruction\": \"词汇:铲子,草地\\n摘要:一只小蚂蚁在花园里寻找吃的,最后找到了一个苹果。\\n\",\n", " },\n", " {\n", - " \"instruction\": \"请用给定的约束生成故事\",\n", - " \"input\": \"词汇:铲子,草地\\n随机句子:天空中飘着几朵白云。\\n摘要:一只小蚂蚁在花园里寻找吃的,最后找到了一个苹果。\\n\",\n", + " \"instruction\": \"词汇:铲子,草地\\n随机句子:天空中飘着几朵白云。\\n摘要:一只小蚂蚁在花园里寻找吃的,最后找到了一个苹果。\\n\",\n", " },\n", "]\n", - "prompts = [\n", - " ms.apply(prompt=case[\"instruction\"], input=case[\"input\"]) for case in test_cases\n", - "]\n", + "prompts = [prompt_style.apply(prompt=case[\"instruction\"]) for case in test_cases]\n", "for prompt in prompts:\n", " text = llm.generate(\n", " prompt=prompt,\n", " max_new_tokens=300,\n", - " temperature=0.8,\n", + " temperature=0.9,\n", " top_p=1,\n", " top_k=30,\n", " )\n", diff --git a/Journey/Day11/generate_dpo_data.py b/Journey/Day11/generate_dpo_data.py index 9be0c2e..4a0aeec 100644 --- a/Journey/Day11/generate_dpo_data.py +++ b/Journey/Day11/generate_dpo_data.py @@ -65,6 +65,7 @@ async def main(concurrency, test_mode): try: with open("dpo_cache.json", "r", encoding="utf-8") as f: cache = json.load(f) + logger.info(f"加载缓存: {len(cache)}条") except FileNotFoundError: cache = {} diff --git a/Journey/Day12/Day12.ipynb b/Journey/Day12/Day12.ipynb new file mode 100644 index 0000000..2f34634 --- /dev/null +++ b/Journey/Day12/Day12.ipynb @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "之前说准备好`DPO`数据就着手开始自己`DIY`一下训练的实现了(因为`litgpt`库暂时还没有集成相关实现)。\n", + "虽说是`DIY`但是纯从零手撸的成本还是有点高的,因此还是打算参考已有的实现来弄,比如:\n", + "- [eric-mitchell/direct-preference-optimization: Reference implementation for DPO (Direct Preference Optimization)](https://github.com/eric-mitchell/direct-preference-optimization)\n", + "- [huggingface的trl库里的实现](https://github.com/huggingface/trl/blob/main/trl/trainer/dpo_trainer.py)\n", + "\n", + "`transformers`库毕竟是大主流,因此还是优先考虑基于`transformers`库的`API`来实现。\n", + "\n", + "那么面临的第一个问题就是需要将`litgpt`框架训练的模型转换为`huggingface`库的模型格式。\n", + "\n", + "而在这个过程中我遇到了一个**意想不到的坑**,直接给我整**破防**了。\n", + "\n", + "大家听我慢慢道来。\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## litgpt模型转换到huggingface format\n", + "`litgpt`框架本身考虑得很周全,它从简化模型实现的角度完全基于`torch`实现的[model.py](https://github.com/Lightning-AI/litgpt/blob/main/litgpt/model.py)\n", + "\n", + "同时也提供了将`checkpoint`转换为`huggingface`模型格式的`API`:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# ! litgpt convert_from_litgpt input_checkpoint_dir output_dir" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "这里会得到一个`model.pth`文件,为了后续加载方便,大家可以直接改名为`pytorch_model.bin`。\n", + "\n", + "另外需要注意的是`transformers`库依赖`config.json`文件,如果大家的模型架构选择的是使用`litgpt`框架已经支持的`huggingface`上的模型,那么可以直接去下载;但如果是自己定义的模型架构,那么就需要大家自己动手来写这个`config.json`文件了。\n", + "\n", + "> 后面我会写一个简单的脚本,基于`litgpt`框架的`config.yaml`文件来生成`config.json`文件,可能在一些场景下能具备通用性。\n", + "\n", + "\n", + "## transformers库模型加载\n", + " \n", + "模型的转换还是比较简单且顺利的,但是按照[litgpt的convert文档](https://github.com/Lightning-AI/litgpt/blob/main/tutorials/convert_lit_models.md),通过`transformers`库加载转换后的模型文件的时候,问题来了。\n", + "\n", + "我的到了一个意想不到的报错,追溯到的是`transformers`库里对模型参数尺寸检查的这段代码:\n", + "\n", + "![](https://erxuanyi-1257355350.cos.ap-beijing.myqcloud.com/image.png)\n", + "\n", + "**它强制要求了`embedding`的向量尺寸必须正好等于注意力头数`n_heads`和注意力头尺寸`head_size`的乘积**。\n", + "\n", + "原因是他们不知出于什么考虑,在对`attention`的`kqv`转换矩阵实现的时候,做了一个简化,大家看下图里我用红框标出的地方。\n", + "\n", + "![](https://erxuanyi-1257355350.cos.ap-beijing.myqcloud.com/imag_4.png)\n", + "\n", + "\n", + "如果不理解的话,再对比看下面这张图应当就能理解了,是`Karpathy`大神的`nanoGPT`里同样模块的实现。\n", + "\n", + "![](https://erxuanyi-1257355350.cos.ap-beijing.myqcloud.com/image_3.png)\n", + "\n", + "但凡看过原始论文里的公式推导就会知道,`hidden_size = n_heads * head_size`这个要求是**完全没有必要**的。\n", + "\n", + "`W_k/W_q/W_v`矩阵理论上可以将`hidden_size`投影到任何维度上去,只要最后再通过`W_o`的线性层映射回`hidden_size`即可。\n", + "\n", + "而`transformers`库的实现里将这个任何维度简化为了`n_heads * head_size`,这对我这种需要从自定义的模型架构转换过来的场景造成了**毁灭性打击**🤦‍♂️。\n", + "\n", + "因为我当时**拍脑袋**定的`0.044b`参数模型里,`embedding`维度选的`768`,`n_heads`是`6`,而`head_size`定了个`48`,导致这里没有办法加载了。\n", + "\n", + "我当然可以通过改源码的方式来弥补这个问题,但是如果我希望我的模型能够被更多使用`transformers`库的人使用,这个方式就不合适了。\n", + "\n", + "最保险的方式是按照它这个**不合理的**要求来调整我的模型架构,从而得到一个满足`hidden_size = n_heads * head_size`的模型。\n", + "\n", + "## 教训总结\n", + "\n", + "\n", + "没办法,我最终选择了重新预训练我的故事模型;坑虽然踩了,但是也得到了一些收获。\n", + "\n", + "大部分人一般情况下主要是基于`huggingface`上已有的模型架构来训练,我这类**自定义模型架构**的情况相对少见,因此踩坑踩得有点狠。\n", + "\n", + "即便是`transformers`库这样的大主流,也难免会有一些设计不合理的地方。\n", + "\n", + "也提醒我,对开源库的使用,如果时间精力允许,还是要**多花点功夫看看源码**,理解其背后的设计思想,这样在遇到问题的时候才能从更本质的地方找到解决方案。\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> 一个更让我欲哭无泪的事实:\n", + "> \n", + "> 就在我的新架构模型快完成预训练的时候,\n", + "> 我发现这个不合理的逻辑其实在[这个PR](https://github.com/huggingface/transformers/pull/32857)里得到了修复。\n", + "> \n", + "> 在`transformers`库的最新版本里已经没有这个问题了。\n", + "> \n", + "> 合着是我更新库不够积极呗?🤷‍♂️" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "bigmodel", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Journey/Day12/Day12.md b/Journey/Day12/Day12.md new file mode 100644 index 0000000..0cfd796 --- /dev/null +++ b/Journey/Day12/Day12.md @@ -0,0 +1,85 @@ +之前说准备好`DPO`数据就着手开始自己`DIY`一下训练的实现了(因为`litgpt`库暂时还没有集成相关实现)。 +虽说是`DIY`但是纯从零手撸的成本还是有点高的,因此还是打算参考已有的实现来弄,比如: +- [eric-mitchell/direct-preference-optimization: Reference implementation for DPO (Direct Preference Optimization)](https://github.com/eric-mitchell/direct-preference-optimization) +- [huggingface的trl库里的实现](https://github.com/huggingface/trl/blob/main/trl/trainer/dpo_trainer.py) + +`transformers`库毕竟是大主流,因此还是优先考虑基于`transformers`库的`API`来实现。 + +那么面临的第一个问题就是需要将`litgpt`框架训练的模型转换为`huggingface`库的模型格式。 + +而在这个过程中我遇到了一个**意想不到的坑**,直接给我整**破防**了。 + +大家听我慢慢道来。 + + + +## litgpt模型转换到huggingface format +`litgpt`框架本身考虑得很周全,它从简化模型实现的角度完全基于`torch`实现的[model.py](https://github.com/Lightning-AI/litgpt/blob/main/litgpt/model.py) + +同时也提供了将`checkpoint`转换为`huggingface`模型格式的`API`: + + +```python +# ! litgpt convert_from_litgpt input_checkpoint_dir output_dir +``` + +这里会得到一个`model.pth`文件,为了后续加载方便,大家可以直接改名为`pytorch_model.bin`。 + +另外需要注意的是`transformers`库依赖`config.json`文件,如果大家的模型架构选择的是使用`litgpt`框架已经支持的`huggingface`上的模型,那么可以直接去下载;但如果是自己定义的模型架构,那么就需要大家自己动手来写这个`config.json`文件了。 + +> 后面我会写一个简单的脚本,基于`litgpt`框架的`config.yaml`文件来生成`config.json`文件,可能在一些场景下能具备通用性。 + + +## transformers库模型加载 + +模型的转换还是比较简单且顺利的,但是按照[litgpt的convert文档](https://github.com/Lightning-AI/litgpt/blob/main/tutorials/convert_lit_models.md),通过`transformers`库加载转换后的模型文件的时候,问题来了。 + +我的到了一个意想不到的报错,追溯到的是`transformers`库里对模型参数尺寸检查的这段代码: + +![](https://erxuanyi-1257355350.cos.ap-beijing.myqcloud.com/image.png) + +**它强制要求了`embedding`的向量尺寸必须正好等于注意力头数`n_heads`和注意力头尺寸`head_size`的乘积**。 + +原因是他们不知出于什么考虑,在对`attention`的`kqv`转换矩阵实现的时候,做了一个简化,大家看下图里我用红框标出的地方。 + +![](https://erxuanyi-1257355350.cos.ap-beijing.myqcloud.com/imag_4.png) + + +如果不理解的话,再对比看下面这张图应当就能理解了,是`Karpathy`大神的`nanoGPT`里同样模块的实现。 + +![](https://erxuanyi-1257355350.cos.ap-beijing.myqcloud.com/image_3.png) + +但凡看过原始论文里的公式推导就会知道,`hidden_size = n_heads * head_size`这个要求是**完全没有必要**的。 + +`W_k/W_q/W_v`矩阵理论上可以将`hidden_size`投影到任何维度上去,只要最后再通过`W_o`的线性层映射回`hidden_size`即可。 + +而`transformers`库的实现里将这个任何维度简化为了`n_heads * head_size`,这对我这种需要从自定义的模型架构转换过来的场景造成了**毁灭性打击**🤦‍♂️。 + +因为我当时**拍脑袋**定的`0.044b`参数模型里,`embedding`维度选的`768`,`n_heads`是`6`,而`head_size`定了个`48`,导致这里没有办法加载了。 + +我当然可以通过改源码的方式来弥补这个问题,但是如果我希望我的模型能够被更多使用`transformers`库的人使用,这个方式就不合适了。 + +最保险的方式是按照它这个**不合理的**要求来调整我的模型架构,从而得到一个满足`hidden_size = n_heads * head_size`的模型。 + +## 教训总结 + + +没办法,我最终选择了重新预训练我的故事模型;坑虽然踩了,但是也得到了一些收获。 + +大部分人一般情况下主要是基于`huggingface`上已有的模型架构来训练,我这类**自定义模型架构**的情况相对少见,因此踩坑踩得有点狠。 + +即便是`transformers`库这样的大主流,也难免会有一些设计不合理的地方。 + +也提醒我,对开源库的使用,如果时间精力允许,还是要**多花点功夫看看源码**,理解其背后的设计思想,这样在遇到问题的时候才能从更本质的地方找到解决方案。 + + +> 一个更让我欲哭无泪的事实: +> +> 就在我的新架构模型快完成预训练的时候, +> 我发现这个不合理的逻辑其实在[这个PR](https://github.com/huggingface/transformers/pull/32857)里得到了修复。 +> +> 在`transformers`库的最新版本里已经没有这个问题了。 +> +> 合着是我更新库不够积极呗?🤷‍♂️ + +