[{"content":"大家可能都有过这种感觉，在某段时间内，自己也没有从事体力劳动，也没有剧烈运动，但是就是感觉身体很累，累起来无论是干什么都没有精气神，只想瘫着刷手机，但是周而复始会越来越累。\n我也有过这种感觉，我当时就在想可能是最近工作太忙，或者生活的琐事太多，那我放松一下不就好了。于是我选择睡一觉、看看剧、玩会手机，但我总感觉对自己帮助不大，自己仍然会处于一种低精力状态。\n在查阅了一些资料后，我把疲分为了三种。\n生理疲劳，主要表现在身体沉、注意力难集中、情绪易波动。\n可能与睡眠不足、长期压力、缺乏运动有关。从生理角度看，它往往意味着神经系统长期处于较高唤醒状态，没有充分恢复。\n认知疲劳，主要表现在执行控制力差、决策难、更倾向于选择及时回报。\n2022年一项研究，当人们在长时间专注工作和集中注意力后，谷氨酸会在外侧前额叶聚集，这就导致我们的判断能力和决策能力下降，从而使得我们倾向于选择短期回报，而不是长期有价值的事情。这就是为什么我们在感觉累的时候，会控制不住自己去刷手机。\n心理疲劳，主要表现在空虚、麻木、想逃避。\n当我们长期处于被安排、被评价、被任务驱动的状态，心理层面可能会出现这种疲劳感。\n拆分之后，我意识到我选择的休息方法并没有在休息，而是我用错了恢复方式。\n刷手机只会让我们加重疲劳感，因为刷手机一般都是刷一些信息流，这种高切换、低参与的刺激只会让我们的神经系统频繁唤醒，体内的多巴胺会一直刺激我们的身体表达想要，我们的大脑也会选择这种低成本高奖励的行为，从而导致恶性循环。\n这些道理我们都懂，我们也清楚在感到疲劳的时候，选择早点入睡、冥想、散步，或者是运动能够有效提升身体的精力，但我们很难选择去做，主要就是因为刚才提到的外侧前额叶积累的谷氨酸导致的。而上述行为都属于启动成本相对较高，延迟奖励，大脑遵循能力效率原则，自然会选择当下轻松的行为。所以这时候需要告诉自己，并不是自己不够自律，而是生物本能导致的，没必要自己责怪自己。\n还有一类人，他们恢复精力的方法与其个体差异有关，比如有的人觉得下班后打会游戏能让自己更加快乐，在第二天能以很好的精神状态迎接工作和学习，有的人下班后喝两杯会感觉很舒服。所以休息也是因人而异的，因为每个人的性格、工作、情绪调节方式都不同，所以恢复精力的方式也不同，对于不同的人来说恢复精力是不同的补偿机制。\n我在这里想构建属于自己的恢复系统。\n原则一：保证生理底层，睡眠、运动、光照。\n睡眠是生物活动一切的根基，在睡眠的时候，大脑会清理白天产生的废物，整理白天创造的记忆等，让我们的身体出在一个稳定、良好的状态。每个人对于睡眠的要求是不同的，成年人基本是在7-9个小时，我更推荐《我们为什么要睡觉》这本书中推荐的方法，按照睡眠周期进行规划自己睡眠，简单来说就是90分钟是一个睡眠周期，尽量不在一个睡眠周期中醒来，我自己尝试过最合适的就是5个睡眠周期，也就是7个半小时。\n运动和光照都是我们刻在基因中的东西，我们的身体设计就是用来奔跑而不是久坐的，现代生活要求我们坐在工位长达8个小时或者更长，非常容易造成各种健康问题，所以坐一会就起来接杯水、溜达溜达，看起来是小事却对身体有很大的帮助，如果能搭配定期的运动那就更好了。\n原则二：减少无意识的刺激，限制信息流、提高使用摩擦。\n由于推荐系统，我们每时每刻都能接触到为自己量身定做的信息推送，只要想看能无止尽的看下去，但是这对我们来说是很大的精力消耗，所以一定要限制自己刷信息流，尽量提高使用摩擦，就是在拿起手机想刷信息的时候，先想一下当下有没有事情要做，一定要刷信息流吗？实在控制不住自己，就把手机放的离自己远一点，比如放到一个小抽屉里，就这一个动作就能让大脑识别到，我还需要多做一步才能看到手机，大脑就会选择放弃。对我来说，限制屏幕使用时间、关闭不必要的通知，以及有意识的卸载信息流app，非常有用。\n原则三：区分舒服和恢复，问自己做完以后是舒服还是更累了。\n在自己疲劳时，当下想做的事情，一定要问问自己做完以后是只有当下舒服了，还是精力恢复了。比如之前提到有的人打会游戏会觉得精力恢复了，那就去打游戏。对于我来说，冥想、阅读这种沉浸式的事情会对第二天精力恢复很有帮助，那我就选择去做这些事情。\n原则四：设计默认行为，而非依赖意志力。\n如果每次都靠意志力去选择不玩手机，强迫自己去读书、冥想、运动，大脑只会更累，可能坚持一段时间后，意志力也不起作用了，又回到了起点，因为大脑会选择低成本、高回报的事情。所以我们需要设计默认行为，《习惯的力量》中讲的习惯回路的三大环节，暗示→惯常行为→奖励，比如手机在手边→解锁刷手机→刺激多巴胺分泌。那我们就可以设计自己行为，比如回到家后的固定流程：把手机放远、换衣服、喝口水。然后选择我们接下来要做的事情，这些事情一定是提前准备好的，陪孩子、读书、写点东西等。这减少了我们的决策成本，这本身就是恢复精力的一部分。当然也不是绝对禁止娱乐的，而是固定时间、固定时长、固定地点，有边界的娱乐。\n真正的休息不是睡一天，或是找一个没人地方追求清净，也不是放纵自己，让当下更爽，而是理解自己消耗了什么，然后补回来。当消耗的是身体，那就想办法恢复身体；当消耗的是注意力，就减少刺激；当消耗的是掌控感，那就创造一点掌控。\n","permalink":"https://kevinchen1994.github.io/posts/%E4%BB%80%E4%B9%88%E6%98%AF%E7%9C%9F%E6%AD%A3%E7%9A%84%E4%BC%91%E6%81%AF/","summary":"分享我对什么是疲劳，以及如何恢复精力的思考。","title":"什么是真正的休息"},{"content":"动机 这个博客很早就部署了，不过之前使用的是一个开源项目Notion-Hugo，这个项目作的很棒，给我提供了一个很好的思路，基于Notion作为CRM，使用Hugo主题进行部署，既满足了我日常使用Notion写作，Hugo漂亮的主题还对我胃口。不过该项目是使用TypeScript写的，在项目运行过程中偶尔会有一些错误出现，我自己调起来又有点麻烦，有时候就索性不管了，一放就好久，到头来还得硬着头皮去调，花了不少时间。后来我就想为什么我不自己开发一个呢，功能也不复杂，完全可以使用Python来开发，应该花不了多少时间，于是就有了这个项目，不过代码基本上都是AI写的，我基本只负责提需求和审查代码。\n开发过程 因为本篇文章拖了很久才写，那时候使用的AI Coding还是GitHub Copilot，不像现在的Cursor这样的AI IDE，能够实现全自动的开发。但是相比以前还是有了10x的效率提升。\n整个开发过程还是很丝滑的，虽然之前也使用了AI Coding，但是没有这次使用这么彻底，这次我想的是完全不自己动手，全凭AI来完成。\n首先是搭建整个项目的框架，我通过chat的方式询问了标准的博客项目的架构是什么样的，然后手动创建了项目目录，接着我让AI开发了与Notion交互的测试代码，测试了下，通过！\n然后是仿照Notion-Hugo项目的方式，实现了从Notion获取数据，然后转成Markdown格式，通过hugo进行部署。甚至很多代码我直接copy Notion-Hugo的TS代码，让AI转成Python，比如不同类型Notion数据的转换。\n项目开发完，基本没有遇到什么bug，遇到了我就把报错信息提供给AI，让AI自己解决。接着部署到github.io就完成了。非常简单丝滑，开发时间也很短，差不多只用了一个下午。\nAI Coing发展过程 接下来我来简单介绍一下AI Coding的发展过程。\n最早的时候我们使用的编辑器都会有提示功能，例如VS Code、JetBrains相关的产品，这类产品基本是基于语法、模板进行代码提示，实现了括号匹配、关键词补充等功能。\n后来发展到使用一些简单的神经网络算法进行代码提示，例如LSTM，但是一直没有火起来，使用体验也没有太大的提升。\n直到LLM发展以后，基于LLM的续写能力，能够实现精准的代码补充，因为你的注释、代码上下文能够为LLM提供充足的信息，让代码预测更加精准，这一时期GitHub Copilot是典型的代表，但是每个月10刀的订阅费，让国内的用户还是望而却步，这时候国内的模型厂商免费提供了大量的插件，例如通义灵码、CodeMars等，我都有使用过。\n随着LLM能力的提升，代码补全已经太简单了，我们可以通过chat的形式给LLM模型发送指令，让其完成相应的代码开发，比如使用Python帮我实现快排、帮我给接口添加标准的日志等，之后LLM会完成响应的代码，我们可以通过复制粘贴的形式，或是直接应用的形式使用代码，但是这种形式仅局限在与一个代码文件，并不能根据整个项目进行实现。\n简单的chat已经不能满足我们的要求了，能不能有更加方便的方法呢？比如我说帮我实现一个web页面，接入天气api，可以查询不同城市日期的天气，这时候Agent的形式就来了，Agent可以帮你构建整个项目，创建好项目的目录结构，分别实现不同的功能。这一时期的代表就是Cursor，我们通过自然语言的交互，可以实现整个项目的搭建开发。这里插一句，Cursor的爆火跟anthropic的Claude-3.5-Sonnet是有直接关系的，Claude-3.5-Sonnet是当时最好的编程模型，没有之一。可见AI Coding的发展是强烈依赖LLM模型的能力的。\n更进一步，除了开发任务，还能进行自动运行、部署、交付，这相当于一个程序员的大部分工作了，Devin做到了，他通过构建一个虚拟环境，赋予LLM足够的权限，实现自动化的工作，Agent的形式我们还需要配合，比如审查代码、点击接受代码、调整需求等。Devin完全是自动化的，我们可以下达指令以后，就去干别的事情了，等着交付就行。Devin一个月500刀的订阅费，你们觉得值不值呢？如果Devin具备高级工程师所具备的能力，我觉得那相当值，但是现在可能还没有达到这种能力，仍然会有出错的可能，所以让子弹再飞一会吧。\n一些思考和感想 随着对AI Coding的使用越来越多，现在我对于工作上的需求，和自己的想法越来越open，特别愿意尝试新技术，比如对于一些不太熟悉的技术栈，我都可以去尝试。因为我本身是算法工程师，所以对于后端开发、前端开发，并没有专业的工程师那么熟悉，但是现在我会主动去使用AI进行搭建web demo，以供产品、运营等同学去进行测试我们的模型，针对自己的一些想法，也会使用AI去做一些小工具来进行验证，还能提供自己的工作、学习效率。因为我总感觉之前我是单打独斗，现在有一个“老师傅”站在我的身后，在我有任何问题的时候，都能耐心的、掰开了、揉碎了讲给我听，还能通过各种例子来帮助我理解。这就是AI带给开发者最好的福利吧。\n对于有开发基础的工程师来说，AI Coding带来的是远超10倍的效率提升，对于没有基础的人来说，是从无到有的提升。所以未来肯定是一个人人都会编程的时代，对于工程师和没有编程基础的人，我给出的建议是，尽可能的多尝试和使用，但是也不能一点编程知识也不学习，还是要学习一些编程基础的，不然无法判断代码的好坏，甚至代码可能都运行不起来。\n当然随着模型能力的提升，对人的要求也是越来越低，比如之前我们需要做详细的Prompt Engineering来让模型遵循我们的指令实现我们的需求，但是现在很少有人特别详细去写Prompt了，因为模型的理解能力变强了，不需要太多的Prompt技巧了，只需要将需求描述清晰，模型就能准确的去实现。\n根据我的经验来说，最好不要一下给模型最终的需求，而是将需求进行拆分，根据最终的目标让模型逐步进行实现。这是因为模型本身上下文是有限制的，模型无法做到一步到位，并且如果是一个很大的需求，模型在实现过程中的某一个环节如果出问题的话，根据错误累积原则，错误会越来越严重，最终可能导致实现的代码与需求不符。\n如果在编程过程中，一个问题总也无法很好的解决，模型可能会一直通过打补丁的方式进行实现，这可能会导致代码库越来越臃肿，以至于我们无法进行维护，甚至导致整个项目瘫痪，所以这时候可能多尝试其他的模型，比如ChatGPT，可以将当前的问题抽象进行描述，看看其他的模型有没有更好的解法，这相当于简化了模型的上下文，让模型只专注于当前遇到的问题，而不是考虑整个代码库进行回答。这样带来的好处是能精准定位问题和解决，不至于让整个代码库进行修改。找到问题解决的思路以后，可能让IDE帮忙进行解决，也可以手动解决，都是可以的，宗旨就是尽量不要一直打补丁。\n还有一些项目可能是使用公司内部的框架，或者是一个多年的老项目，这时候使用AI Coding可能会遇到很多问题，比如AI不了解框架的使用，或是参考代码库内其他的错误的使用方法，导致出错，这种错误很难解决，所以这种情况，最好还是给出清晰的定义，甚至一些好的代码示例，如果框架有文档，也可以交给AI，也就是我们所说的few-shot，让AI去学习正确的使用方法，从而达到比较好的效果。当前目前来看使用AI还是在公开的框架上效果是最好的，因为有充足的训练数据，AI的理解比较到位。\n最后 大家都在谈AGI，我认为编程领域的AGI已经到来了，当然如果你定义AGI是帮我做一个淘宝，AI能够从0开始搭建底层的代码，包括整个项目的部署运营，那肯定不是的。编程领域的AGI是通过使用AI实现效率的大幅提升（10倍以上）、代替部分人员的实际工作，这真的已经到来了。\nAndrej Karpathy提过Vibe Coding，也就是完全沉浸式编程，只给LLM发出指令，等待其运行完成，直到项目完成。这种感觉只有体验过才会觉得有多美妙，这时候我们就是一个创造者，搭配着无所不能的AI，我们可以做任何事！\n我还有一个想法，那就是一定会有一些AI Native Codebase，也就是整个代码库全是由AI来管理的，从代码的编写，bug的修复，代码的上线等各种流程，当这种情况出现的时候，我们就可以宣布AI Coding的AGI彻底到来了！\n","permalink":"https://kevinchen1994.github.io/posts/%E6%9C%AC%E5%8D%9A%E5%AE%A2%E6%98%AF%E5%A6%82%E4%BD%95%E6%9E%84%E5%BB%BA%E7%9A%84feat.-ai-coding/","summary":"通过AI Coding构建了本博客，基于Notion作为CRM，使用Hugo主题进行部署。主要谈了我对于AI Coding的使用和思考。","title":"本博客是如何构建的feat. AI Coding"},{"content":"我们这一代年轻人，甚至更早到改革开放以后出生的人，都会有一种思想钢印，那就是整个社会会一直高速发展、经济会一直向好、个人收入也会逐年增长。当然，这么长一段时间，期间也出现过诸如1997年亚洲金融危机、2008年全球金融危机、2015年股市波动危机这种影响极大的事件，但是我们都挺过来了，且经济持续高速增长。这种思想钢印会影响到我们生活的方方面面，让我们过于”乐观“的对未来充满幻想。\n当然这篇文章不是聊经济，只是最近发生的一些事情让我认识到，增长并不是线性的，可能会受多方面的影响，我在制定计划和对未来预判时，可能会受这种”无限增长假设“所影响。比如我从22年开始跑步，从新手期到入门期，跑步能力能快速的提升，在23年整年的跑量到了1000公里左右，所以我在24年初计划整年要跑2000公里。我做出这种计划并不是随便拍的一个数，当时做了仔细的思考和规划，比如，我23年每次的跑量在10公里左右，每周跑3到4次，每个月在150公里左右，所以在24年可以延续这种跑量，甚至还能再提升单次跑量，周末也可以跑15到20公里长距离，这样算下来2000公里是相当合理的。然而我24年实际的跑量只有770公里，甚至还没有24年跑量多，我复盘了为什么会发生这种情况。\n首先计划是否合理，作为跑步爱好者，每个月150公里左右的跑量是平均水平，但是我忽略了我是一个互联网行业的从业者，和一个4岁孩子的爸爸。这就意味着我的个人时间被极大的压缩了，因为下班时间比较晚，我早已经从夜跑转变成了晨跑，也就是早上6点钟起床，6点半出门，7点半结束训练。孩子幼儿园是需要8点入校的，如果我想送她上学，那就需要在7点50之前洗完澡，还需要帮孩子刷牙和简单的收拾，很明显时间是不够的，所以我缩短了跑步时间，争取在7点10分左右回家，这样能留给我多一点时间，相应的单次的跑量就减少了，同时对我的跑步能力也起不到明显的提升，因为我已经过了入门期，跑40分钟已经在我的舒适区了，只能尽量保证跑步能力不降低。\n再谈执行情况，我当然也意识到了现实，但是我却没有做出相应的改变，或是优化我的时间安排，或是重新制定计划，我只是按部就班的执行，执行效率没有达到计划的要求。并且中途我还伤了一次，这让我有了喘息的机会，倒不如是逃避的机会。在养伤的过程中，我暂停了跑步，恢复以后我跑量越来越少，我以担心再次受伤为由，逃避了增加跑量的想法。因为工作的变动，上班时间提前，所以我再一次压缩了我的跑步时间，最终每次跑量在5公里左右，可想而知，想达到计划的每个月150公里以上是不可能的。\n这其实就是我以”无限增长假设“来制定的计划，因为实际情况失败的例子。我相信也会有很多人，甚至很多公司会遇到这种情况，那如何避免这种情况的发生呢？我觉得有两种解决方案，分别是主动调整和被动接受。\n被动接受，这倒不是消极的意思，不是说反正现实已经这样了，就无所谓了。这并不是解决问题的办法，而是鸵鸟效应。这里的被动是指，认识到增长并不是线性的，是受很多因素影响的，所以允许自己接受波动和调整。不能因为没有完成计划而气馁，相反要鼓励自己，肯定自己已经完成的部分，定期回顾目标和复盘，找到具体的原因进行主动调整。\n主动调整，能够做到这一步就说明你并没有放弃，那目标是否合理，是你最先考虑的问题，如果目标已经超过自己的能力范围了，或者因为实际环境有变，完不成目标了，可以及时进行调整，或者设定灵活的目标，以适应实际的情况。如果目标没问题，是执行层面出现问题了，那就要考虑是自己为什么不能执行了，有可能是偷懒了，有可能是能力达不到，需要想办法解决。\n标题里有瓶颈期这个词，是因为我想到的跑步计划的解决方案里需要解决这个问题。具体来说，我如果要执行2000公里计划，需要做出以下改变：增加单次跑步时长、提高每次跑步效率（配速）、定期回顾目标、严格执行计划。我自己认为我一直无法做出改变，很大一部分原因就是因为到了跑步瓶颈期，那就是日常的简单训练已经不能提升我的跑步能力了，所以对我的积极性有了很大的影响。对我跑步影响最大的是人是黑影儿，他是一个跑步up主，极其严格的自律，他一直传递一个思想那就是跑量决定一切，想要提升自己的能力别无他法，我对此表示认同，只要把这种思想刻在脑子里，就能做出对应的改变，早上再早起15分钟到半个小时，认真对待每一次训练，及时复盘，定能突破瓶颈期。\n最后我再分享最近听到的一期播客，很喜欢。E178 对话艺术家邱志杰：自我不是一座未发现的矿，而是一座待建的塔。播客聊了很多内容，最打动我的就是邱老师传达的，现在的学生太不刻苦了，邱老师是一个相信工作量的人，这点跟黑影儿其实是异曲同工的。无论是邱志杰老师对工作量的坚持，还是黑影儿对跑量的执着，都让我明白，突破瓶颈期需要付出实实在在的努力。叠个甲，我并不是提倡没苦硬吃，而是说如果你相信自己的目标，想做成某件事，那付出是必不可少的，没有不付出就能得到的东西，除了天赋。\n","permalink":"https://kevinchen1994.github.io/posts/%E6%97%A0%E9%99%90%E5%A2%9E%E9%95%BF%E5%81%87%E8%AE%BEfeat.%E7%93%B6%E9%A2%88%E6%9C%9F/","summary":"跑步计划失败的复盘，让我认识到无限增长假设，以及如何应对的问题，希望我的经历，能为你面对瓶颈期提供一些启发。","title":"无限增长假设feat.瓶颈期"},{"content":"动机 微信公众号几乎是中文信息领域质量最高的平台，有很多文章值得细读，那就需要花整块时间进行阅读，并且有的文章在读完后还想收藏起来作为自己的知识库。\n我之前使用过微信浮窗、Omnivore作为read later（稍后读），但是存在几个问题，一是微信浮窗有上限，大概是100篇，如果超过了就不能再加了，虽然稍后读是尽快读完，像仓鼠一样存着文章不读也是一种浪费，但是有这种限制总会让人不爽。二是读完以后没有地方进行保存，即使收藏到微信上，也有可能因为作者把文章删除了，或者某些不可抗性被删除了，就会导致永久丢失。Omnivore可以将文章保存下来，但是Omnivore前段时间刚刚被收购，不提供服务了，虽然开源了源代码，但是自部署也是挺麻烦的一件事，并且他提供的文章保存功能面对微信图片、代码之类的内容总会有一些问题，有可能会丢失一部分数据。\n于是我打算自己写一个小工具来满足我的需求。需求很简单，统一爬取文章按照原有格式进行保存下来，并且支持打标签保存，如果能够加上一些AI的功能那就更好了。\n实现 稍后读功能的使用场景大多数发生在移动端，因为在有电脑的场景下，大概率是有时间和场所进行深度阅读的，所以我想到了使用苹果的「快捷指令」来进行实现，可以读取粘贴板上的文章网址进行保存，并且可以使用iPhone的Action Button快速触发，任务在后台执行，执行完成发送执行结果通知即可。\n具体保存的位置我选择的是Notion，Notion基本是我的第二大脑，我所有的文本相关的数据都存储在Notion中，支持全局搜索和Notion AI，并且Notion API也很好用，支持数据上传和修改，非常方便。\n前段时间黑五，我以很便宜的价格购买了VPS，本来想用来部署博客的，现在正好使用这台机器来部署我的服务。\n第一版本 通过快捷指令中的url访问功能，实现了对文章链接的访问，基于规则识别了文章标题和作者，将其与url一起通过Notion API保存到了我的Notion数据库中。\n这一版只解决了部分数据保存的问题，并没有实现本地化阅读的功能。因为通过快捷指令只能实现文本的内容，文本格式、图片、链接等都无法获取，更不用说按照原有样式保存下来了。\n我使用了两天，基本是在手机上保存链接，在电脑的Notion客户端进行阅读。因为没有本地保存，所以基本看完就删除了，体验并不比之前好多少。\n第二版本 使用Python正式实现了API，通过快捷指令向VPS发送请求，然后在VPS进行爬取文章内容，保存到Notion中。这一步看起来很简单，不就是一个爬虫嘛，实现起来确实花费时间最长的地方。\n我开始认为微信文章不都应该是统一格式的吗，于是决定自己写爬虫去爬取文章内容，只要将内容的HTML标签同步保存下来，再转换成Markdown不就可以了。在实际实现的过程中我才发现文章的HTML会因为文章的排版不同而变化，基础的内容爬取很简单，想要复现文章原本的样式，几乎是不可能的，需要处理太多种情况了。\n我转而想到了开源的方法，去github一通搜索，也没有找到很好的方法。因为工作内容是NLP和LLM，正好看到Jina AI发布了一个小模型，reader-lm-0.5b，可以将html页面转换成Markdown格式，于是我自己部署了一下，发现效果还可以，但是对于我的场景可能不太适用。\n这个模型需要自部署，这对于VPS来说有点不现实，因为本身VPS的配置就不高，即使是0.5b参数的模型部署起来也很困难，再加上一篇文章的HTML长度为一两万很正常，会很影响生成速度。\nLLM进行输出内容与爬虫爬取的内容相比，出错的概率肯定是有的，因为LLM是基于上文进行预测下文的，而我的场景是将文章保存下来，不允许有任何文字的错误，所以这一点基本就把这个方法舍弃了。\n后来我发现Jina AI支持API调用，这个API调用的并不是reader-lm这个模型，而是先提取HTML内容，然后通过正则等其他的方法，将清理后的HTML转换为Markdown，这正好符合我的要求，在保证了内容不出错丢失的前提下，还能以Markdown的形式保存。还有最重要的一点，免费！当然即使是付费，我也愿意支持付费使用。\n我也尝试过微软开源的一个库，markitdown，支持将各种各样的文件转成Markdown格式，我尝试将HTML保存成文件，然后基于这个库进行转换，但是效果不如Jina AI的API效果好。\n还有一个比较麻烦的点，那就是图片，因为微信图片是有版权保护的，通过图片的url上传到Notion会报错，所以只能上传到服务器，或者图片托管平台来获取新的图片地址。\n因为我的VPS本身存储空间不大，并且如果图片全部上传到这里，未来再做服务器迁移的时候，图片的url也会变化，所以VPS并不合适。还可以选择收费阿里云OSS或者AWS的S3云存储，但是我看基本都是500GB起购买，这对于只存储图片的需求来说，太奢侈了。还有其他的方法，像是telegram免费存储，但是可能会因为网络问题导致不稳定，后来我找到了imgur这个平台，这是一个图片分享和托管平台，可以基于imgur的api实现免费的存储，但是存储的图片如果超过6个月不访问会过期，我只需要手动访问或者写一个程序定时访问即可。\nimgurl图片上传有一些限制，那就是图片不能超过特定的尺寸或者大小（10MB），所以我将图片下载下来，检查尺寸和大小，如果超过了，就重新resize一下或者调整一下清晰度，即可上传成功。上传速度还是挺快的，不过有速率的限制，经过测试，如果是一篇图片比较多的文章，将图片全部上传也不会触发速率限制，只要不频繁上传多篇文章即可。\n文本内容和格式准备好以后就好说了，我把这个流程梳理成pipeline，使用ChatGPT和github copilot实现了一下，读取url，获取内容，获取作者和发布时间，在Notion数据库中创建页面并保存内容。基本达到了我的要求，我还在Notion数据库中添加了标签的字段，方便我阅读完后进行打标签梳理。\n最终版本 还记得我之前提到过，如果加上一些AI功能就更好了嘛，作为LLM工程师，这块实现起来还是挺简单的。对于长文，如果有提纲/总结能够帮助我更快的了解文章的整体结构和结论，也可以帮助挑选我关注的部分，节省我的时间。\n目前市面上的开源模型和闭源模型做的都不错，那就选择最便宜的！我选择了qwen2-7b，写好Prompt后，经过测试基本能够满足我的需求。\n使用LLM有一个问题那就是运行时间的问题，作为小工具，运行时间长我是可以接受等待的，但是快捷指令对于API的限制是60s，如果超过的话那就会中断，基于此，我将接口改成了异步的形式，先通过请求获取文章内容，在Notion中创建页面，这时候会提示我数据创建成功，然后服务会在后台执行数据写入、AI总结等流程。\n下面来看一下效果。\n知识库页面，这里我加了一个标语来提醒自己阅读的注意事项。\nAI总结部分\n正文部分\n总结 为自己制作趁手的小工具不仅方便自己使用，更能带来一种成就感，这是之前只有作为程序员才能享受到的快乐，现在这个时代，其实非程序员借助AI的能力也能实现这些功能。所以如果你遇到一些问题，或者有一些很好的想法，可以马上借助AI行动起来，谁知道后边会开什么花，接什么果呢。\n另外我也考虑过将代码开源，但是如果有小伙伴想用的话，前提就是需要自己购买服务器，这可能拦住了大部分人，所以我想以后有机会的话，做一个服务出来，通过我购买的服务器，让想使用这个服务的小伙伴通过配置的方法，实现零代码和零服务器使用服务。\n","permalink":"https://kevinchen1994.github.io/posts/%E6%88%91%E4%B8%BA%E8%87%AA%E5%B7%B1%E5%88%B6%E4%BD%9C%E4%BA%86%E4%B8%80%E4%B8%AA%E9%98%85%E8%AF%BB%E5%B7%A5%E5%85%B7/","summary":"自己制作的read later和知识库管理工具","title":"我为自己制作了一个阅读工具"},{"content":"站在当前这个时间节点（2024年底），大多数人已经认为我们进入了AI时代。最近几年也出现了大量AI+X的创业公司，AI+教育、AI+医疗、AI+陪伴等等，仿佛我们各行各业都已经被AI所渗透，更有甚者说，未来会有多少多少人因为AI而失业，因为那些人的工作能够轻松被AI替代。\n今天我们不讨论AI带来的社会问题，而是从职场人或是个体的视角探讨，在AI时代我们应该如何工作，我的观点是，像老板一样工作。\n老板是如何工作的 老板是如何工作的，肯定不是下场去干苦力，而是思考、规划、决策、任务拆解、任务分配、结果验收。我们作为员工是执行老板安排的任务，这也是我们在公司的价值所在，通过自身的专业技能完成安排的任务。 在AI时代，我们有各种各样的工具可以使用，这些工具就像我们的“员工”。例如，老板安排了一个任务，让你对产品中的新用户进行分析，并完成分析报告。你可以先通过ChatGPT帮你写SQL取出新用户的各种数据，再通过GitHub Copilot来实现数据分析的Python代码，基于数据分析结果，可以使用ChatGPT帮你生成一份报告，甚至还可以使用Kimi、万知等国产大模型生成一份PPT。借助这么多优秀的“员工”，不仅能够大大提高工作效率，还能拓展你的能力边界，使你能够迅速上手以往不熟悉的领域，显著提升个人竞争力。\n使用AI的两个例子 当然，使用这些工具并非简单的发号施令。我们需要清晰地描述任务需求、目标和背景等，也就是写有效的Prompt。你给大模型的信息越丰富，大模型完成任务的效果越好。\n我看到一个反例，一个不懂开发，且不熟悉大模型的人，跟大模型说，帮我实现一个音乐播放器。大模型虽然能够实现一个简单的播放器，但是实现出来的是否是这个人想要的，大概率不是，因为他没有说是播放器客户端，还是网页版，还是app，播放器的功能是什么，长什么样子等等。\n与此相对的正例是，一个不懂开发，但很熟悉大模型的人，察觉到了很多女自拍、化妆的时候需要一个补光器，于是他使用大模型帮助他开发了一款IOS APP，全程几乎没有自己写代码，全是由大模型完成的，他只是把他的需求详细的输入给了大模型，最终这个APP登上了App Store的榜单。这个人是Alchian花生，他在即刻、YouTube、Bilibili都有账号，大家可以去查看。\n这两个例子中的两个人都不懂开发，但是达成的结果完全不一样。就像老板给员工安排工作一样，只有将工作的背景、目标、要求说清楚，员工才能跟老板“对齐”想法，准确的执行老板安排的工作。\n所以在AI时代，我们要发挥我们的特长，并且善于利用AI工具，知道哪些工具擅长做什么事情，就像老板一样，知道手下的员工的长处，并善于利用员工的长处，提高整个公司的工作的效率。这里虽然把员工和工具做了类比，但绝没有贬低的意思。\nAI与教育 因为我是一名父亲，所以最后想简单聊一下AI与教育之间的关系。00后是在移动互联网时代长大的，所以他们天然对移动设备、各种APP很熟悉，上手很快。我们也看到有一些学生沉迷手游、直播等导致学业下降，甚至退学过早的走入了社会。也有一些学生善于利用手机，查找学习资料、使用学习APP，反而提高了自己的成绩。最近几年出生的孩子则是伴随着AI时代成长的。那么，如何让这一代的孩子学会正确使用AI，也是值得探讨的问题。\n我个人的观点是，可以从简单的功能开始使用，给孩子科普AI是什么，如何正确去使用它。我相信会有一些人认为，需要避免让孩子使用AI产品，因为可以导致孩子放弃思考，所有的事情都交给AI去做，包括作业都不自己写。就像我之前在即刻发过动态，说我晚上睡觉之前会用文心一言给孩子讲故事，具体的情节、人物让孩子来制定。就有人评论说，让我赶紧打住，别误了孩子。我理解这些人的想法，就像计算器刚出现的时候，有人也会想不让孩子去使用，因为会让孩子失去计算的能力，但事实上，学会使用计算器的孩子不仅没有失去计算能力，反而能够帮助他们更好的学习数学。\nAI的双刃剑效应 一切事物都有两面，AI好的一面是能够帮我们解决重复性的工作、辅助我们不擅长的工作、提高我们的效率，不好的一面则是让我们丧失思考的能力。就像很多人担心AI有一天会让我们失业、统治人类一样，也有大有人在All in AI。学会正确的使用AI，才是让我们在AI时代保持竞争力的关键。\n","permalink":"https://kevinchen1994.github.io/posts/ai%E6%97%B6%E4%BB%A3%E5%83%8F%E8%80%81%E6%9D%BF%E4%B8%80%E6%A0%B7%E5%B7%A5%E4%BD%9C/","summary":"在AI时代，AI工具就像“员工”一样，我们应该学会像老板一样工作，给“员工”安排好任务，提高整个”公司“的工作效率。","title":"AI时代像老板一样工作"},{"content":"在与他人沟通、共事的过程中，我们总希望能影响对方——无论是让对方接受我们的观点，还是促使对方采取行动。为此，我们会采取多种方法，例如面对面讨论、赠送小礼物、寻求对方上级的支持，甚至通过表达愤怒来传递事件的重要性和我们的关心程度。然而，愤怒是否真的是一种必要且有效的手段呢？\n愤怒是一种选择，而非不可控的情绪 在职场中，我们很少见到有人真正发怒。即便有不满，也多表现为冷淡或沉默。而在家庭环境中，愤怒似乎变得更加频繁，甚至难以控制。人们常说，“把好脾气留给了同事，把坏脾气发泄给了家人。”这恰恰说明，愤怒其实是可以被控制的。试想，如果愤怒不可控，为什么我们能在工作场合中保持理智，却在家中对亲人火冒三丈？\n阿德勒在《被讨厌的勇气》中提到，愤怒并非不可控制的自然反应，而是我们为应对问题所选择的一种手段。愤怒的本质是一种“工具”，常被用来迫使他人服从或试图改变现状。但这种方式并不总是有效，甚至可能适得其反。\n举两个简单的例子：\n如果孩子没有完成作业，父亲可能会非常生气，通过大声呵斥发泄愤怒，希望以此让孩子听话。 如果这位父亲在工作中被上司批评，他是否会对上司发火，像对孩子一样大声回击呢？ 这两个例子清楚地表明，愤怒是可以控制的。我们会根据不同的场景、面对不同的人，选择是否使用愤怒来达到自己的目的。 亲身经历：愤怒的代价 作为一名父亲，前几天，我也做过例子中第一位父亲的事情：通过发怒让孩子服从。当时孩子确实听话了，但他哭得很伤心。我后来意识到，孩子之所以听从我的要求，并不是因为理解了对错，而是因为害怕。那天晚上，我为此感到非常懊悔，孩子脸上恐惧的表情不断在我的脑海中回放。我深刻体会到，为了短暂的服从，我选择了发怒，不仅伤害了孩子，还破坏了我们之间的信任，我非常后悔我做了这么残忍的事情。\n曾经一个YouTube博主跟我讲过孩子的教育最重要的就是“influence without authority”，要建立信任才能影响想法，这比强加的命令更持久，孩子的内驱力也会更强。我当时觉得自己理解了，但在生活中具体去做的时候就把这种思想抛到脑后了。\n愤怒的替代方案：冷静与理性 为此，我跟ChatGPT聊了挺多，我们讨论出来一些实际的方法来应对愤怒。\n意识到愤怒是一种选择。选择愤怒后，自己的很多行为会变得不可控，造成没必要的后果。 将注意力从“愤怒的对象”转移到“目的”，我通过愤怒能解决问题吗？我的目标是什么，是否可以用更理性的方法达到目的。 拒绝情绪接管，暂停，深呼吸，理性的思考。 承担责任，聚焦解决问题。 从亲身经历的一件事，重点讲亲子之间的influence without authority，除此以外，无论在任何场景下都是可以应用这种思想的，重点就在于关注目的是什么，是否必须通过发怒这一种方式来达成目的。 当然influence without authority不仅仅是不发怒，还有很多其他的方面，后续有了更多想法我会继续更新。\n","permalink":"https://kevinchen1994.github.io/posts/influence-without-authority%E6%84%A4%E6%80%92%E6%98%AF%E5%8F%AF%E4%BB%A5%E6%8E%A7%E5%88%B6%E7%9A%84/","summary":"通过亲身经历，结合职场，探讨influence without authority中重要的一点-愤怒是一种可以控制的情绪，而不是解决问题的有效手段。","title":"Influence without authority，愤怒是可以控制的"},{"content":"前言 上一篇文章介绍了关于Agent的一些相关内容，这篇文章来动手实现一个Agent。目前比较流行的Agent开发框架有很多，比如可视化的字节的扣子，还有代码实现的babyagi、AutoGPT，因为我用qwen用的比较多，正好我最近看到qwen也开源了一套框架，qwen-agents，封装了工具、助手和一些规划的方法，使用起来比较方便，那就用这套框架实现一个简单的Agent。\nAgent设计开发 既然是Agent，那就让他做一个LLM不能直接实现的任务，我这边有一个北京的房价表格，我们可以让Agent帮我们分析一下房价，最后生成一个图标来展示每平米的价格。\n因为这个任务需要多步骤才能实现，所以我们可以使用上篇文章中将的ReAct的思路，让模型自己规划任务，根据每一次的执行结果来调整任务，并且可以利用代码助手来帮助他实现目标。\n先配置LLM，这里可以使用阿里的api，也可以使用第三方的api，只要与openAI库兼容就可以。我这里尝试了付费的qwen-max和免费的Qwen2-7B-Instruct。\nllm_cfg = { # 使用 DashScope 提供的模型服务： # \u0026#39;model\u0026#39;: \u0026#39;qwen-max\u0026#39;, # \u0026#39;model_server\u0026#39;: \u0026#39;dashscope\u0026#39;, # \u0026#39;api_key\u0026#39;: \u0026#39;EMPTY\u0026#39;, # 如果这里没有设置 \u0026#39;api_key\u0026#39;，它将读取 `DASHSCOPE_API_KEY` 环境变量。 # 使用与 OpenAI API 兼容的模型服务，例如 vLLM 或 Ollama： \u0026#39;model\u0026#39;: \u0026#39;alibaba/Qwen2-7B-Instruct\u0026#39;, \u0026#39;model_server\u0026#39;: \u0026#39;http://localhost:8000/v1\u0026#39;, # base_url，也称为 api_base \u0026#39;api_key\u0026#39;: \u0026#39;EMPTY\u0026#39;, # （可选） LLM 的超参数： \u0026#39;generate_cfg\u0026#39;: { \u0026#39;top_p\u0026#39;: 0.8 } } 然后去定义我们的Agent，因为框架封装的很方便了，所以几行代码就可以实现。\n可以看到我们只需要配置Agent能够使用的工具，以及Agent的名称，他能做什么就可以了。工具方面我这边只使用了代码解释器，框架还封装了很多其他的工具，比如联网搜索、图片生成（需要api支持）、RAG等。框架也支持自定义工具，可以根据自己的需要实现特定功能的工具。\ndef init_agent_service(): llm_cfg = { } tools = [\u0026#39;code_interpreter\u0026#39;] bot = agents.ReActChat(llm=llm_cfg, name=\u0026#39;house price analyzer\u0026#39;, description=\u0026#39;You are a house price analyzer, you can run code to analyze house price.\u0026#39;, function_list=tools) return bot 然后就可以配置测试的方法了，可以通过命令行进行测试，也可以通过GUI进行测试，框架基于gradio开发了很方便的GUI方法，我就选择GUI来进行测试。\n这里可以提前写好建议的prompt和需要上传的文件，也可以在页面自己手动填写和上传。\ndef app_gui(): bot = init_agent_service() chatbot_config = { \u0026#39;prompt.suggestions\u0026#39;: [{ \u0026#39;text\u0026#39;: \u0026#39;先使用pd.head()查看数据，然后帮我分析昌平区两居室的每平米均价，最后帮我画一个关于昌平区两居室房子面积与价格的折线图\u0026#39;, \u0026#39;files\u0026#39;: [os.path.join(ROOT_RESOURCE, \u0026#39;lianjia.xlsx\u0026#39;)] }] } gui.WebUI(bot, chatbot_config=chatbot_config).run() 最后运行程序就可以了。\nAgent实际效果 这里可以看到他根据我的指令，进行了思考，他需要先加载文件，然后通过pandas来查看数据，然后根据我的要求进行筛序，最后进行计算和画图。\n接着他实现了代码，代码逻辑看上去没有啥问题，有一处小问题，那就是两居室可能不能使用等于2室来表示，而是包含2室，我们看看运行结果怎么样。\n代码运行出错了，因为数据中并没有区域这一列，实际是Region，也没有居室这样的列名，接着他通过pandas查看了列名来确认具体的列名是什么。\n这次的代码没有问题了，字段使用都是正确的，两居室也是使用contains来表示。\n每平米房价跟房屋面积的图画出来了，看上去不错。\n尽管代码报错了，但是最终得到了答案，最后还给出了一些分析，很好的完成了我的任务。\n总结 房价分析Agent很好的完成了我的任务，尽管过程中有出现一些错误，但是经过ReAct的这种观测、思考最终还是顺利的完成了。\n其实实际运行中，Agent并不是一次性就完成了我的任务，中间运行了好多次都是错误的结果，要么是代码写的有问题，要么就是观测的结果与思考有问题，所以想要Agent完全代替人的工作为时尚早。\nAgent相比于LLM来说，确实更加智能，但是其中需要人根据具体的任务去设计Agent，如果是复杂的任务，可能需要多个Agent来协作，这就涉及到了上篇文章讲到的挑战了，复杂的任务通常需要很多步骤才能完成，那LLM的上下文长度就会成为瓶颈。根据这次的经验，LLM通过自然语言作为接口来进行交互难免出错，所以还是需要提升LLM的能力，让其成为一个合格的大脑。\n","permalink":"https://kevinchen1994.github.io/posts/agent--%E6%89%8B%E6%90%93%E4%B8%80%E4%B8%AAagent-feat.-qwen-agent/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e上一篇文章介绍了关于Agent的一些相关内容，这篇文章来动手实现一个Agent。目前比较流行的Agent开发框架有很多，比如可视化的字节的扣子，还有代码实现的babyagi、AutoGPT，因为我用qwen用的比较多，正好我最近看到qwen也开源了一套框架，qwen-agents，封装了工具、助手和一些规划的方法，使用起来比较方便，那就用这套框架实现一个简单的Agent。\u003c/p\u003e\n\u003ch2 id=\"agent设计开发\"\u003eAgent设计开发\u003c/h2\u003e\n\u003cp\u003e既然是Agent，那就让他做一个LLM不能直接实现的任务，我这边有一个北京的房价表格，我们可以让Agent帮我们分析一下房价，最后生成一个图标来展示每平米的价格。\u003c/p\u003e\n\u003cp\u003e因为这个任务需要多步骤才能实现，所以我们可以使用上篇文章中将的ReAct的思路，让模型自己规划任务，根据每一次的执行结果来调整任务，并且可以利用代码助手来帮助他实现目标。\u003c/p\u003e\n\u003cp\u003e先配置LLM，这里可以使用阿里的api，也可以使用第三方的api，只要与openAI库兼容就可以。我这里尝试了付费的qwen-max和免费的Qwen2-7B-Instruct。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ellm_cfg \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# 使用 DashScope 提供的模型服务：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# \u0026#39;model\u0026#39;: \u0026#39;qwen-max\u0026#39;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# \u0026#39;model_server\u0026#39;: \u0026#39;dashscope\u0026#39;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# \u0026#39;api_key\u0026#39;: \u0026#39;EMPTY\u0026#39;,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# 如果这里没有设置 \u0026#39;api_key\u0026#39;，它将读取 `DASHSCOPE_API_KEY` 环境变量。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# 使用与 OpenAI API 兼容的模型服务，例如 vLLM 或 Ollama：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;model\u0026#39;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;alibaba/Qwen2-7B-Instruct\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;model_server\u0026#39;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;http://localhost:8000/v1\u0026#39;\u003c/span\u003e,  \u003cspan style=\"color:#75715e\"\u003e# base_url，也称为 api_base\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;api_key\u0026#39;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;EMPTY\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# （可选） LLM 的超参数：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;generate_cfg\u0026#39;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;top_p\u0026#39;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e然后去定义我们的Agent，因为框架封装的很方便了，所以几行代码就可以实现。\u003c/p\u003e\n\u003cp\u003e可以看到我们只需要配置Agent能够使用的工具，以及Agent的名称，他能做什么就可以了。工具方面我这边只使用了代码解释器，框架还封装了很多其他的工具，比如联网搜索、图片生成（需要api支持）、RAG等。框架也支持自定义工具，可以根据自己的需要实现特定功能的工具。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einit_agent_service\u003c/span\u003e():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    llm_cfg \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tools \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;code_interpreter\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    bot \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e agents\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eReActChat(llm\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ellm_cfg,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                           name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;house price analyzer\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                           description\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;You are a house price analyzer, you can run code to analyze house price.\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                           function_list\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003etools)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e bot\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e然后就可以配置测试的方法了，可以通过命令行进行测试，也可以通过GUI进行测试，框架基于gradio开发了很方便的GUI方法，我就选择GUI来进行测试。\u003c/p\u003e","title":"Agent Ⅱ 手搓一个Agent feat. Qwen-Agent"},{"content":"Overview 随着ChatGPT的流行，我们似乎觉得LLM能够帮我们解决任何问题，但是长时间使用下来，有一些问题LLM并不能很好的解决，比如我想让LLM基于一次考试后的全班试卷帮我统计某个班级的最高分、最低分、平均分，并且针对考试较差的学生提出针对性的建议。目前所有的LLM都无法直接做到，因为首先全班所有的试卷就已经够长了，LLM的上下文长度可能都不够，再就是LLM并不擅长做关于数字的内容，因为数字对于他来说就是token，他有可能并不清楚数字token之间的关系。\n那这个问题怎么解决呢，那就是通过Agent来解决，我们让LLM作为大脑，让其拥有记忆功能来存储试卷内容，还可能让其调用工具，让其学会计算，对于他也不会的知识点，他还可以上网搜索学会了再来辅导学生。这是不是就有人工智能那味儿了。\nAgent能够通过构建LLM与规划、记忆、工具和执行来完成复杂的任务，LLM在其中扮演的就是大脑的角色，通过将复杂任务进行拆解，并且通过调用工具和存储来逐一解决小任务，最终完成复杂的任务。Agent能够大大提升LLM的实用性，让其不仅仅是一个只会对话的模型，而是有了规划和思考的能力。\n下图为Agent的构成的概要。\nMemory 人类的大脑中有多种记忆模式，比如短期记忆，用于存储当下的一些信息，例如我们现在与人对话的上下文，还有长期记忆，用于存储我们的知识体系。Agent也是一样，短期记忆用于存储当前会话的上下文，一般通过模型的上下文窗口来解决，长期记忆用于存储知识库和历史行为，一般通过外部存储来解决。\n短期记忆受模型上下文窗口限制，所以不会太长，随着上下文长度的增长，对于显存的占用也会同步增加，所以这部分我们尽量控制的短一些，如果是超长的上下文，我们也可以将其转换为长期记忆来存储。\n长期记忆其实也就是RAG，我们通过外部向量存储知识库，可以快速检索和访问，因为向量库相对比较便宜，所以我们可以存储大量的知识以供Agent使用，并且向量库的内容我们还可以经常更新，让模型在不调整参数的同时就拥有了比较新的知识。\nPlanning 无反馈规划 对于复杂的任务将其进行拆解能够帮助Agent更好的进行执行。\n对于LLM来说，他并不会主动将任务进行拆解，我们可以通过CoT（Chain of Though）让其进行拆解复杂任务，“let\u0026rsquo;s think step by step”就是一句魔法咒语，在prompt开头添加这句话，能够让模型分步骤解决问题，从而获得更高的准确率。\n在CoT的基础上，还有ToT（Tree of Though）的方法，通过在每一步探索多个可能得路径，形成树状结构，再通过DFS或者BSF来评估每个步骤的有效性。\n还有一种方法是LLM+P，也就是通过外部规划期来进行长期规划，这种方法先将问题转化为PDDL格式，然后利用规划期生成方案，最终将这一方案转换为自然语言。本质上，规划步骤被外包给外部工具，假设特定领域的 PDDL 和合适的规划器可用，这在某些机器人设置中很常见，但在许多其他领域并不常见。\n有反馈规划 如果模型规划有问题也不能很好的解决复杂的问题，所以我们可以让Agent通过反馈来进行规划，以纠正以前的错误，这对于解决复杂的任务有着至关重要的作用，毕竟人类在解决复杂问题时也会通过复盘来迭代自己的方法。姚顺雨大佬的两篇论文就是解决这个问题。\nReAct通过结合特定任务的离散动作与语言描述，实现了LLM中融合推理和执行的能力。离散动作能够与现实环境进行交互，语言米哦啊哈苏则可能促进LLM产生基于自然语言的推理路径。这种策略不仅提高了LLM处理复杂问题的能力，还通过与外部环境直接交互，增强了模型在真实世界中的适应性和灵活性。\n具体的就是通过一下prompt模板，让LLM思考其Though、Action、Observation。\nThought: ... Action: ... Observation: ... ... (Repeated many times) 下图可以看到使用ReAct后的效果，并且对比了只使用推理（reason）和只使用执行（act）的区别。在使用了推理+执行后，模型可以很好的规划这个任务，并且根据每一步的结果来规划下一步需要做什么，最终得到正确的答案。\n传统的强化学习学习大量的训练样本和计算资源来让模型从错误中进行学习，Reflexion提出了一种新的框架，不通过更新模型的权重，而是通过自然语言的反馈来增强模型从错误中学习。\nReflexion有一个标准的RL设置，其中简历模型提供简单的二元奖励，动作空间遵守ReAct中的设置，其中特定于任务的动作空间通过语言进行增强，以实现复杂的推理步骤。每一次执行，执行者（Actor）都会基于观察状态生成文本和动作，执行后，评估者（Evaluator）会对执行者生成的内容计算奖励分数，自我反思模型（Self-reflection）会生成口头自我反思提示以协助执行者进行改进，对于效率低下的规划，因为会存储在长期记忆中，所以会被识别到一直没有完成，这时自我反思模型会考虑将其停止。\nTools 曾几何时，人们普遍认为“使用工具”是人和动物最大的区别，虽然现在人们不这么认为了，但是人类文明发展史很大程度上就是一部人类发明工具帮助人类做事情的历史。如果Agent也能像人类一样使用工具就能够大大提升LLM的能力范围。\n比如LLM对数字不够敏感，可能很多加减法都会做错，但是如果他能够调用计算器，那多复杂的运算也能够准确的计算出来。还有很多工具，像是网页浏览、写代码、画图、调用API等等，都能够有效的提升LLM的能力。\n以下是一些LLM使用工具的方法：\nMRKL（Modular Reasoning, Knowledge and Language）包含一组专家模块，LLM作为路由器将查询路由到最合适的专家模块。\nTALM（Tool Augmented Language Models）和Toolformer都是对LLM进行微调，让其学会使用外部工具API。\nHuggingGPT是一个使用ChatGPT作为任务规划器的框架，根据模型描述选择HuggingFace平台中可用的模型，并根据执行结果总结响应。\nChallenges Agent虽然可以帮我们解决复杂的任务，但是仍然有很多限制和挑战。\nLLM的上下文长度有限，对于复杂的任务，需要记录很多步骤、指令、工具介绍等情况，很容易把上下文窗口占满从而影响Agent的能力。 在planning阶段如果出错，那Agent就很难得到正确的答案，或者走很多弯路，对资源消耗比较大，目前没有完美的解决方案。 自然语言的可靠性，当前的Agent都是依赖自然语言作为LLM与其他组件交流的工具，然而自然语言容易有歧义，文本格式错误也可能导致理解错误，所以成功率并不高。 Summary 从相信LLM无所不能，到高频使用后的去魅，再到Agent解决复杂的问题，让我更加了解了LLM的能力边界，LLM更适合做一个大脑，通过结合不同的模块发挥更大的作用，但是目前来看局限性比较大，很多场景需要人工的设置一些规则才能让Agent有好的表现，所以未来还有很长的路要走，不过我们可以想一想目前哪些任务我们可以交给Agent去做，从而提高我们的效率，让我们更专注于做一些价值更高的事情。\nCitation LLM Powered Autonomous Agents\n姚顺雨-从语言模型到语言智能体（From Language Models to Language Agents）\n","permalink":"https://kevinchen1994.github.io/posts/agent--introduction/","summary":"\u003ch2 id=\"overview\"\u003eOverview\u003c/h2\u003e\n\u003cp\u003e随着ChatGPT的流行，我们似乎觉得LLM能够帮我们解决任何问题，但是长时间使用下来，有一些问题LLM并不能很好的解决，比如我想让LLM基于一次考试后的全班试卷帮我统计某个班级的最高分、最低分、平均分，并且针对考试较差的学生提出针对性的建议。目前所有的LLM都无法直接做到，因为首先全班所有的试卷就已经够长了，LLM的上下文长度可能都不够，再就是LLM并不擅长做关于数字的内容，因为数字对于他来说就是token，他有可能并不清楚数字token之间的关系。\u003c/p\u003e\n\u003cp\u003e那这个问题怎么解决呢，那就是通过Agent来解决，我们让LLM作为大脑，让其拥有记忆功能来存储试卷内容，还可能让其调用工具，让其学会计算，对于他也不会的知识点，他还可以上网搜索学会了再来辅导学生。这是不是就有人工智能那味儿了。\u003c/p\u003e\n\u003cp\u003eAgent能够通过构建LLM与规划、记忆、工具和执行来完成复杂的任务，LLM在其中扮演的就是大脑的角色，通过将复杂任务进行拆解，并且通过调用工具和存储来逐一解决小任务，最终完成复杂的任务。Agent能够大大提升LLM的实用性，让其不仅仅是一个只会对话的模型，而是有了规划和思考的能力。\u003c/p\u003e\n\u003cp\u003e下图为Agent的构成的概要。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_88cd957e-d2df-4ab4-9061-75f0e794111001e26f63-8eec-47b0-99ef-0ddcd7aba4ec.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"memory\"\u003eMemory\u003c/h2\u003e\n\u003cp\u003e人类的大脑中有多种记忆模式，比如短期记忆，用于存储当下的一些信息，例如我们现在与人对话的上下文，还有长期记忆，用于存储我们的知识体系。Agent也是一样，短期记忆用于存储当前会话的上下文，一般通过模型的上下文窗口来解决，长期记忆用于存储知识库和历史行为，一般通过外部存储来解决。\u003c/p\u003e\n\u003cp\u003e短期记忆受模型上下文窗口限制，所以不会太长，随着上下文长度的增长，对于显存的占用也会同步增加，所以这部分我们尽量控制的短一些，如果是超长的上下文，我们也可以将其转换为长期记忆来存储。\u003c/p\u003e\n\u003cp\u003e长期记忆其实也就是RAG，我们通过外部向量存储知识库，可以快速检索和访问，因为向量库相对比较便宜，所以我们可以存储大量的知识以供Agent使用，并且向量库的内容我们还可以经常更新，让模型在不调整参数的同时就拥有了比较新的知识。\u003c/p\u003e\n\u003ch2 id=\"planning\"\u003ePlanning\u003c/h2\u003e\n\u003ch3 id=\"无反馈规划\"\u003e无反馈规划\u003c/h3\u003e\n\u003cp\u003e对于复杂的任务将其进行拆解能够帮助Agent更好的进行执行。\u003c/p\u003e\n\u003cp\u003e对于LLM来说，他并不会主动将任务进行拆解，我们可以通过CoT（Chain of Though）让其进行拆解复杂任务，“let\u0026rsquo;s think step by step”就是一句魔法咒语，在prompt开头添加这句话，能够让模型分步骤解决问题，从而获得更高的准确率。\u003c/p\u003e\n\u003cp\u003e在CoT的基础上，还有ToT（Tree of Though）的方法，通过在每一步探索多个可能得路径，形成树状结构，再通过DFS或者BSF来评估每个步骤的有效性。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_c4759741-9120-4592-b9f4-82e2dd9f16c3caad19c9-e75d-4e81-a654-d8b7605df700.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e还有一种方法是LLM+P，也就是通过外部规划期来进行长期规划，这种方法先将问题转化为PDDL格式，然后利用规划期生成方案，最终将这一方案转换为自然语言。本质上，规划步骤被外包给外部工具，假设特定领域的 PDDL 和合适的规划器可用，这在某些机器人设置中很常见，但在许多其他领域并不常见。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_e989527c-f740-4b26-9e9d-5d90973bee420687d74d-ec28-4124-984b-192ba7e82461.jpg\"\u003e\u003c/p\u003e\n\u003ch3 id=\"有反馈规划\"\u003e有反馈规划\u003c/h3\u003e\n\u003cp\u003e如果模型规划有问题也不能很好的解决复杂的问题，所以我们可以让Agent通过反馈来进行规划，以纠正以前的错误，这对于解决复杂的任务有着至关重要的作用，毕竟人类在解决复杂问题时也会通过复盘来迭代自己的方法。姚顺雨大佬的两篇论文就是解决这个问题。\u003c/p\u003e\n\u003cp\u003eReAct通过结合特定任务的离散动作与语言描述，实现了LLM中融合推理和执行的能力。离散动作能够与现实环境进行交互，语言米哦啊哈苏则可能促进LLM产生基于自然语言的推理路径。这种策略不仅提高了LLM处理复杂问题的能力，还通过与外部环境直接交互，增强了模型在真实世界中的适应性和灵活性。\u003c/p\u003e\n\u003cp\u003e具体的就是通过一下prompt模板，让LLM思考其Though、Action、Observation。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eThought: ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAction: ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eObservation: ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e... (Repeated many times)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e下图可以看到使用ReAct后的效果，并且对比了只使用推理（reason）和只使用执行（act）的区别。在使用了推理+执行后，模型可以很好的规划这个任务，并且根据每一步的结果来规划下一步需要做什么，最终得到正确的答案。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_bf6a889b-de49-447a-9a36-0f5b9ea32822a490c953-a74c-4835-a670-96188bc1f16f.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e传统的强化学习学习大量的训练样本和计算资源来让模型从错误中进行学习，Reflexion提出了一种新的框架，不通过更新模型的权重，而是通过自然语言的反馈来增强模型从错误中学习。\u003c/p\u003e\n\u003cp\u003eReflexion有一个标准的RL设置，其中简历模型提供简单的二元奖励，动作空间遵守ReAct中的设置，其中特定于任务的动作空间通过语言进行增强，以实现复杂的推理步骤。每一次执行，执行者（Actor）都会基于观察状态生成文本和动作，执行后，评估者（Evaluator）会对执行者生成的内容计算奖励分数，自我反思模型（Self-reflection）会生成口头自我反思提示以协助执行者进行改进，对于效率低下的规划，因为会存储在长期记忆中，所以会被识别到一直没有完成，这时自我反思模型会考虑将其停止。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_05698073-909a-4d1b-89bf-2756f5cfcc41076884f4-ee09-41cf-ae0d-6b39bd7e0f27.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"tools\"\u003eTools\u003c/h2\u003e\n\u003cp\u003e曾几何时，人们普遍认为“使用工具”是人和动物最大的区别，虽然现在人们不这么认为了，但是人类文明发展史很大程度上就是一部人类发明工具帮助人类做事情的历史。如果Agent也能像人类一样使用工具就能够大大提升LLM的能力范围。\u003c/p\u003e\n\u003cp\u003e比如LLM对数字不够敏感，可能很多加减法都会做错，但是如果他能够调用计算器，那多复杂的运算也能够准确的计算出来。还有很多工具，像是网页浏览、写代码、画图、调用API等等，都能够有效的提升LLM的能力。\u003c/p\u003e\n\u003cp\u003e以下是一些LLM使用工具的方法：\u003c/p\u003e\n\u003cp\u003eMRKL（Modular Reasoning, Knowledge and Language）包含一组专家模块，LLM作为路由器将查询路由到最合适的专家模块。\u003c/p\u003e\n\u003cp\u003eTALM（Tool Augmented Language Models）和Toolformer都是对LLM进行微调，让其学会使用外部工具API。\u003c/p\u003e\n\u003cp\u003eHuggingGPT是一个使用ChatGPT作为任务规划器的框架，根据模型描述选择HuggingFace平台中可用的模型，并根据执行结果总结响应。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_10baf165-a7b7-4b36-b9cb-3be7f8ce551f56d6065a-8a9f-4353-b13b-18c5ab114ade.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"challenges\"\u003eChallenges\u003c/h2\u003e\n\u003cp\u003eAgent虽然可以帮我们解决复杂的任务，但是仍然有很多限制和挑战。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eLLM的上下文长度有限，对于复杂的任务，需要记录很多步骤、指令、工具介绍等情况，很容易把上下文窗口占满从而影响Agent的能力。\u003c/li\u003e\n\u003cli\u003e在planning阶段如果出错，那Agent就很难得到正确的答案，或者走很多弯路，对资源消耗比较大，目前没有完美的解决方案。\u003c/li\u003e\n\u003cli\u003e自然语言的可靠性，当前的Agent都是依赖自然语言作为LLM与其他组件交流的工具，然而自然语言容易有歧义，文本格式错误也可能导致理解错误，所以成功率并不高。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"summary\"\u003eSummary\u003c/h2\u003e\n\u003cp\u003e从相信LLM无所不能，到高频使用后的去魅，再到Agent解决复杂的问题，让我更加了解了LLM的能力边界，LLM更适合做一个大脑，通过结合不同的模块发挥更大的作用，但是目前来看局限性比较大，很多场景需要人工的设置一些规则才能让Agent有好的表现，所以未来还有很长的路要走，不过我们可以想一想目前哪些任务我们可以交给Agent去做，从而提高我们的效率，让我们更专注于做一些价值更高的事情。\u003c/p\u003e\n\u003ch2 id=\"citation\"\u003eCitation\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://lilianweng.github.io/posts/2023-06-23-agent/\"\u003eLLM Powered Autonomous Agents\u003c/a\u003e\u003c/p\u003e","title":"Agent Ⅰ Introduction"},{"content":"前言 在使用大模型进行推理时我们都会觉得模型的推理速度比较慢，当前也有很多推理加速的方法，像是模型量化、FlashAttention、使用更好的GPU、KVCache等，今天我们就来聊聊KVCache是怎么回事，我会从transformer decoder的推理过程开始，到为什么KVCache可以加速模型推理，以及具体的效果进行介绍。\nTransformer的self-attention机制 目前的大模型都是基于Transformer的decoder架构进行设计的，所以先把著名的self-attention公式抛出来。\n由于大模型使用的是decoder，所以在计算时会mask掉当前token的后边的token，例如当前为token_n，mask矩阵会mask掉n之后的所有token值，目的是防止解码过程中看到答案。\n下图是GPT2的解码过程，给定输入，模型预测下一个token，然后在下一步中使用上一步预测的token作为输入再次进行预测。图来源：https://jalammar.github.io/illustrated-gpt2/\n我们来逐步计算一下解码的过程。\n因为decoder中的self-attention是masked self-attention，所以在计算时需要注意进行mask。\n我们将q和k的计算整理成矩阵，可以得到以下公式。\n展开的话就是这样的一个矩阵。\n对于token_1，Attention_1(Q,K,V)=softmax(\\frac{Q_1K^T_1}{\\sqrt{d_k}})\\vec{V_1}\n对于token_2，Attention_2(Q,K,V)=softmax(\\frac{Q_2K^T_1}{\\sqrt{d_k}})\\vec{V_1}+softmax(\\frac{Q_2K^T_2}{\\sqrt{d_k}})\\vec{V_2}\n对于token_3，Attention_3(Q,K,V)=softmax(\\frac{Q_3K^T_1}{\\sqrt{d_k}})\\vec{V_1}+softmax(\\frac{Q_3K^T_2}{\\sqrt{d_k}})\\vec{V_2}+softmax(\\frac{Q_3K^T_3}{\\sqrt{d_k}})\\vec{V_3}\n可以看到在token_2推理的过程中，K_1、V_1是重复使用的；可以看到在token_3推理的过程中，K_1、V_1、K_2、V_2是重复使用的。\n还可以看出来，每次计算attention只需要使用当前的Q即可，并不需要之前的向量。\n所以我们就可以把之前计算过的K、V缓存起来，这就是今天我们要介绍的KVCache。\nKVCache的作用 现在我们就可以给KVCache进行定义了，在decoder-only架构中，通过缓存解码过程中的K、V，来避免重复计算，从而达到推理加速的效果。\n下图清晰了对比了使用KVCache和不使用KVCache的区别，图来源https://medium.com/@joaolages/kv-caching-explained-276520203249\n实现细节 在huggingface的transformers中，通过modelling_gpt2.py可以看到具体的实现细节。\nquery = self._split_heads(query, self.num_heads, self.head_dim) key = self._split_heads(key, self.num_heads, self.head_dim) value = self._split_heads(value, self.num_heads, self.head_dim) # 通过layer_past来传递上一次计算的key value if layer_past is not None: past_key, past_value = layer_past # 通过拼接来得到最新的key 和 value key = torch.cat((past_key, key), dim=-2) value = torch.cat((past_value, value), dim=-2) if use_cache is True: present = (key, value) else: present = None 在transformers中使用KVCache对比 在Transformers中进行对比开启KVCache后的效果。\nimport numpy as np import time import torch from transformers import AutoModelForCausalLM, AutoTokenizer device = \u0026#34;cuda\u0026#34; if torch.cuda.is_available() else \u0026#34;cpu\u0026#34; tokenizer = AutoTokenizer.from_pretrained(\u0026#34;gpt2\u0026#34;) model = AutoModelForCausalLM.from_pretrained(\u0026#34;gpt2\u0026#34;).to(device) for use_cache in (True, False): times = [] for _ in range(10): # measuring 10 generations start = time.time() model.generate(**tokenizer(\u0026#34;What is KV caching?\u0026#34;, return_tensors=\u0026#34;pt\u0026#34;).to(device), use_cache=use_cache, max_new_tokens=1000) times.append(time.time() - start) print(f\u0026#34;{\u0026#39;with\u0026#39; if use_cache else \u0026#39;without\u0026#39;} KV caching: {round(np.mean(times), 3)} +- {round(np.std(times), 3)} seconds\u0026#34;) 因为我是跑在M1上，所以对比比较明显，开启KVCache后比不开启能快100倍。\nSetting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. with KV caching: 32.101 +- 0.737 seconds Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation. without KV caching: 3516.684 +- 27.119 seconds MHA MQA GQA 目前很多模型，像是LLaMA、Qwen，都使用GQA，其目的也是为了提高模型运行的速度，我们也可以从KVCache的角度去理解这些方法，他们的目的其实就是为了减少KV的个数，从而减少KVCache。\nMHA（Multi-Head Attention），传统的Transformer的attention机制，也就是对query、key、value分别使用单独的头，每个头独立处理输入的不同方面，优点就是独立计算，效果最好，但是计算成本太高。\nMQA（Multi-Query Attention），所有的query、key、value使用一个头，这样就大大降低了计算成本，比MHA快的多，但是会影响模型的效果。\nGQA（Grouped-Query Attention），是MHA和MQA的中间地带。将多个头进行分组，每个组内共享一个key和value，这样每个组的头数较少，所以比MHA快，并且头数不是一个头，所以效果要比MQA要好。\n显存分析 假设输入的序列长度是 𝑚，输出序列长度是 𝑛 ， 𝑏 为数据批次大小， 𝑙 为层数， ℎ 为隐向量维度，以 FP16（2bytes） 来保存，那么 KVCache的峰值显存占用大小为 𝑏(𝑚+𝑛)ℎ∗𝑙∗2∗2=4𝑏𝑙ℎ(𝑚+𝑛) ，第一个 2 代表 K、V，第二个 2 代表 2bytes。可见随着批次大小和长度的增加，KVCache 的显存占用也会快速增大。\n所以KVCache的显存占用跟序列长度是正相关的，现在模型动不动就是4k上下文，甚至上百万上下文，这对GPU的显存占用是很大的，具体解决的方法有以下三个。\n分配一个最大容量的缓冲区，要求提前预知最大的token数量。如果用户的上下文很短的花，这样会很浪费资源。 动态分配缓冲区，先设置固定的容量，超过了就进行扩容处理，但是在在GPU上频繁申请、释放内存的开销是很大的，效率不够高。 不数据拆散，按最小单元存储，使用一个元数据记录每一个数据的位置。这就是大名鼎鼎的PagedAttention，也就是vLLM的主要技术。 单向注意力VS双向注意力 最后我们来聊聊为什么LLM都是使用decoder-only的架构，也就是单向注意力机制。\n在GPT3之前，BERT在NLP领域是绝对的霸主，几乎所有的任务都会使用BERT去做，都能达到SOTA的水平，BERT使用的是Transformer的encoder架构，也就是双向注意力机制。在GPT3之后，我们见识到了大力出奇迹，尤其是ChatGPT爆火以后，我们见识到了decoder-only的架构，也就是单向注意力的威力。那为什么单向注意力加上超大规模的预训练后效果这么好呢？\n苏剑林老师的博客中做了简单的实验，证明了“输入部分的注意力改为双向不会带来收益，Encoder-Decoder架构的优势很可能只是源于参数翻倍。”也就是在同等参数量、同等推理成本下，decoder-only的架构是最优选择。\n还有就是大家都在讨论的低秩问题。在输入部分使用双向注意力机制，输出部分使用单向注意力机制，也就是Prefix LM的做法，直觉上是最优的选择，但是双向注意力机制的低秩问题会带来效果下降。具体的细节大家可以参考苏剑林老师的博客，我就不重复了，实在没有苏剑林老师写的专业。\n顺着这个问题，在知乎上看到了一篇论文，《What Language Model Architecture and Pretraining Objective Work Best for Zero-Shot Generalization?》，这篇论文在50亿参数和1700亿tokens上的预训练任务做了对比实验，得出了以下结论。\n如果大模型制作无监督预训练，那么decoder-only架构+NTP（next token prediction）任务的zero-shot泛化能力最佳。 无监督预训练+multitask finetuning后，encoder-decoder架构+MLM（masked language modeling）任务的zero-shot泛化能力最佳。 所以，在只使用无监督预训练任务的前提下，让模型可以处理开放性问题单向注意力机制是最优的选择。而想要在特定的任务下得到最优可以选择encoder-decoder架构或者encoder架构，使用无监督预训练+finetuning。 题外话 这里简单提一下decoder-only架构的模型是如何处理prompt的，前边我们提到这种架构的模型在输出的时候是通过上一步生成的结果来推理下一个token，那我们输入的prompt是一整句话，模型怎么编码呢？理论上来说也需要通过上一个token预测下一个token，但是因为我们输入的句子是确定的，也就是我们已经知道的prompt的上下文了，所以没必要通过上一个token来预测下一个token了，并且我还需要对其进行编码，那就需要mask矩阵来帮忙了。\n通过上边self-attention的矩阵可以看到，我们可以实现一个下三角的矩阵，来实现当前token看不到后边token的效果。所以在处理prompt时，我们会编码整个句子，然后通过乘上这样的下三角矩阵来实现一个token一个token编码的效果。\n这就实现了prompt并行处理的效果，从openAI的api定价上也能看出来，input的价格是比output的价格要低的，就是因为input可以并行处理，比较节省算力。\n总结 本文算是我的学习笔记，从什么是KVCache，到实现方法及其效果，延伸到单向注意力机制的问题，参考了大量的博客。目的是让自己弄清楚怎么回事，如果能帮助到读者那将是我的荣幸，如果我写的不够清楚，欢迎大家阅读参考文献的原文。\n参考文献 Transformers KV Caching Explained\nLLM Inference Series: 3. KV caching explained\nLLM Inference Series: 4. KV caching, a deeper look\n漫谈注意力机制（五）：自注意力与Transformer\n一文读懂KVCache\n为什么现在的LLM都是Decoder-only的架构？\n为什么现在的LLM都是Decoder only的架构？ - CastellanZhang的回答 - 知乎 https://www.zhihu.com/question/588325646/answer/3002928687\n","permalink":"https://kevinchen1994.github.io/posts/kvcache-feat.-%E5%8D%95%E5%90%91%E6%B3%A8%E6%84%8F%E5%8A%9Bvs%E5%8F%8C%E5%90%91%E6%B3%A8%E6%84%8F%E5%8A%9B/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e在使用大模型进行推理时我们都会觉得模型的推理速度比较慢，当前也有很多推理加速的方法，像是模型量化、FlashAttention、使用更好的GPU、KVCache等，今天我们就来聊聊KVCache是怎么回事，我会从transformer decoder的推理过程开始，到为什么KVCache可以加速模型推理，以及具体的效果进行介绍。\u003c/p\u003e\n\u003ch2 id=\"transformer的self-attention机制\"\u003eTransformer的self-attention机制\u003c/h2\u003e\n\u003cp\u003e目前的大模型都是基于Transformer的decoder架构进行设计的，所以先把著名的self-attention公式抛出来。\u003c/p\u003e\n\u003cp\u003e由于大模型使用的是decoder，所以在计算时会mask掉当前token的后边的token，例如当前为token_n，mask矩阵会mask掉n之后的所有token值，目的是防止解码过程中看到答案。\u003c/p\u003e\n\u003cp\u003e下图是GPT2的解码过程，给定输入，模型预测下一个token，然后在下一步中使用上一步预测的token作为输入再次进行预测。图来源：\u003ca href=\"https://jalammar.github.io/illustrated-gpt2/\"\u003ehttps://jalammar.github.io/illustrated-gpt2/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_4057612f-0c72-4a90-a7ee-7b28e0d726dc56e50bbd-2997-4f89-bfeb-a27377d1c666.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e我们来逐步计算一下解码的过程。\u003c/p\u003e\n\u003cp\u003e因为decoder中的self-attention是masked self-attention，所以在计算时需要注意进行mask。\u003c/p\u003e\n\u003cp\u003e我们将q和k的计算整理成矩阵，可以得到以下公式。\u003c/p\u003e\n\u003cp\u003e展开的话就是这样的一个矩阵。\u003c/p\u003e\n\u003cp\u003e对于token_1，Attention_1(Q,K,V)=softmax(\\frac{Q_1K^T_1}{\\sqrt{d_k}})\\vec{V_1}\u003c/p\u003e\n\u003cp\u003e对于token_2，Attention_2(Q,K,V)=softmax(\\frac{Q_2K^T_1}{\\sqrt{d_k}})\\vec{V_1}+softmax(\\frac{Q_2K^T_2}{\\sqrt{d_k}})\\vec{V_2}\u003c/p\u003e\n\u003cp\u003e对于token_3，Attention_3(Q,K,V)=softmax(\\frac{Q_3K^T_1}{\\sqrt{d_k}})\\vec{V_1}+softmax(\\frac{Q_3K^T_2}{\\sqrt{d_k}})\\vec{V_2}+softmax(\\frac{Q_3K^T_3}{\\sqrt{d_k}})\\vec{V_3}\u003c/p\u003e\n\u003cp\u003e可以看到在token_2推理的过程中，K_1、V_1是重复使用的；可以看到在token_3推理的过程中，K_1、V_1、K_2、V_2是重复使用的。\u003c/p\u003e\n\u003cp\u003e还可以看出来，每次计算attention只需要使用当前的Q即可，并不需要之前的向量。\u003c/p\u003e\n\u003cp\u003e所以我们就可以把之前计算过的K、V缓存起来，这就是今天我们要介绍的KVCache。\u003c/p\u003e\n\u003ch2 id=\"kvcache的作用\"\u003eKVCache的作用\u003c/h2\u003e\n\u003cp\u003e现在我们就可以给KVCache进行定义了，在decoder-only架构中，通过缓存解码过程中的K、V，来避免重复计算，从而达到推理加速的效果。\u003c/p\u003e\n\u003cp\u003e下图清晰了对比了使用KVCache和不使用KVCache的区别，图来源\u003ca href=\"https://medium.com/@joaolages/kv-caching-explained-276520203249\"\u003ehttps://medium.com/@joaolages/kv-caching-explained-276520203249\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_2772e1c2-6780-4c33-a6aa-75a1b091219d9109d308-9ff2-4e32-8ffd-f1fd908c98a1.gif\"\u003e\u003c/p\u003e\n\u003ch2 id=\"实现细节\"\u003e实现细节\u003c/h2\u003e\n\u003cp\u003e在huggingface的transformers中，通过modelling_gpt2.py可以看到具体的实现细节。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        query \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_split_heads(query, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enum_heads, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead_dim)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        key \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_split_heads(key, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enum_heads, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead_dim)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_split_heads(value, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enum_heads, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead_dim)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#75715e\"\u003e# 通过layer_past来传递上一次计算的key value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e layer_past \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            past_key, past_value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e layer_past\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e# 通过拼接来得到最新的key 和 value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            key \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e torch\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecat((past_key, key), dim\u003cspan style=\"color:#f92672\"\u003e=-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e torch\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecat((past_value, value), dim\u003cspan style=\"color:#f92672\"\u003e=-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e use_cache \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            present \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (key, value)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            present \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"在transformers中使用kvcache对比\"\u003e在transformers中使用KVCache对比\u003c/h2\u003e\n\u003cp\u003e在Transformers中进行对比开启KVCache后的效果。\u003c/p\u003e","title":"KVCache feat. 单向注意力VS双向注意力"},{"content":"上周我写了一篇关于有目的的阅读、学习和工作的文章，主要是为了解决低效率的问题。这周，我想谈谈具体的行动问题，即如何动手实践。在这个过程中，我想到了一系列的关键词：把手弄脏、手感、知行合一、输入输出。接下来，我将结合我阅读的一些文章，详细来聊聊我的想法，主要以阅读和写作为例。\n作为一个每天接受大量输入的人，我也有一些输出，比如每周写周记，平时记录灵感和想法在flomo中，或者在个人博客上更新成熟的观点。然而，目前我的输出远远少于输入，这导致了无法形成输入与输出的闭环。有时即使有写作的想法，也迟迟不能动笔，不知道如何开始，或担心被打断等。\n我记得看过一个观点：越分享你的观点，你的观点就越成熟。很多时候，我们害怕自己的观点不够成熟，或羞于表达，就把它们藏在心里，久而久之可能就忘了。如果你把它们分享出来，不仅能帮助你梳理自己的想法，还能通过与他人的讨论不断完善和成熟这些观点。\n写作是让思想更加锐利的工具。即使你脑子里的想法再多，如果不写下来，也难以清晰地梳理。因此，写作是一种将脑海中的网状知识转化为树状结构的方法，能够让你理清思路，使思想更有条理。\n孟岩的文章和播客中提到了费曼学习法，其核心思想是通过教授他人来发现自己理解上的不足。教授的对象最好是对你讲述的知识完全不了解的人，甚至是小朋友，用尽量简单的语言。如果你能让对方理解这些知识，就说明你真的掌握了，否则就是没有完全掌握。同样，如果你能通过文字清晰地表达知识或想法，让大家理解，这也是利用费曼学习法减少知识的缝隙的方式。\n我们看到很多优秀的人，他们的文章和讲话总是能引起共鸣，这其实是经过多次刻意练习的结果。很早之前，我在知乎上看到半佛回答过如何锻炼写作能力的问题。他建议每天在知乎热榜里找到一个问题，写下一个回答，不用在意对错，坚持一段时间，无论什么话题都会有写不完的话。这种方法就是刻意练习。\n辉哥说过，周更比日更更难，月更更不容易。所以，开始写作首先应该是面向自己，把自己当作唯一的读者，谈论任何话题和观点，不用担心别人的看法，这样更容易进入心流状态，轻松写完一篇文章。时机成熟时，再决定是否发表。\n携隐Melody也提到，通过输出倒逼输入，想要保持成长速度，就要多输出。她建议每天写作，控制在800-1000字，这样既不会花费太多时间，也足够表达观点。随着短内容的积累，再去写长篇大论会容易得多。养成每天输出的习惯后，你自然而然就会想接下来我会写什么，你就会主动去学习一些知识来丰富自己的资源库，这也就达到了输出倒逼输入的目的。\n这些写作的方法可以泛化到其他任务，比如学习和工作。如果你掌握的知识与实际操作之间存在gap，就需要动手去实践，把手弄脏，这样才能形成闭环，使知识掌握得更加牢固。\n参考资料 阅读思考行动的闭环\n把自己当成唯一读者\n知识的缝隙\n输出倒逼输入\n","permalink":"https://kevinchen1994.github.io/posts/%E6%8A%8A%E6%89%8B%E5%BC%84%E8%84%8F/","summary":"\u003cp\u003e上周我写了一篇关于有目的的阅读、学习和工作的文章，主要是为了解决低效率的问题。这周，我想谈谈具体的行动问题，即如何动手实践。在这个过程中，我想到了一系列的关键词：把手弄脏、手感、知行合一、输入输出。接下来，我将结合我阅读的一些文章，详细来聊聊我的想法，主要以阅读和写作为例。\u003c/p\u003e\n\u003cp\u003e作为一个每天接受大量输入的人，我也有一些输出，比如每周写周记，平时记录灵感和想法在flomo中，或者在个人博客上更新成熟的观点。然而，目前我的输出远远少于输入，这导致了无法形成输入与输出的闭环。有时即使有写作的想法，也迟迟不能动笔，不知道如何开始，或担心被打断等。\u003c/p\u003e\n\u003cp\u003e我记得看过一个观点：越分享你的观点，你的观点就越成熟。很多时候，我们害怕自己的观点不够成熟，或羞于表达，就把它们藏在心里，久而久之可能就忘了。如果你把它们分享出来，不仅能帮助你梳理自己的想法，还能通过与他人的讨论不断完善和成熟这些观点。\u003c/p\u003e\n\u003cp\u003e写作是让思想更加锐利的工具。即使你脑子里的想法再多，如果不写下来，也难以清晰地梳理。因此，写作是一种将脑海中的网状知识转化为树状结构的方法，能够让你理清思路，使思想更有条理。\u003c/p\u003e\n\u003cp\u003e孟岩的文章和播客中提到了费曼学习法，其核心思想是通过教授他人来发现自己理解上的不足。教授的对象最好是对你讲述的知识完全不了解的人，甚至是小朋友，用尽量简单的语言。如果你能让对方理解这些知识，就说明你真的掌握了，否则就是没有完全掌握。同样，如果你能通过文字清晰地表达知识或想法，让大家理解，这也是利用费曼学习法减少知识的缝隙的方式。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_7ab665a5-a988-4103-89a3-7377906b5f763752746b-2737-41a9-a31f-c9b48bf539ae.png\"\u003e\u003c/p\u003e\n\u003cp\u003e我们看到很多优秀的人，他们的文章和讲话总是能引起共鸣，这其实是经过多次刻意练习的结果。很早之前，我在知乎上看到半佛回答过如何锻炼写作能力的问题。他建议每天在知乎热榜里找到一个问题，写下一个回答，不用在意对错，坚持一段时间，无论什么话题都会有写不完的话。这种方法就是刻意练习。\u003c/p\u003e\n\u003cp\u003e辉哥说过，周更比日更更难，月更更不容易。所以，开始写作首先应该是面向自己，把自己当作唯一的读者，谈论任何话题和观点，不用担心别人的看法，这样更容易进入心流状态，轻松写完一篇文章。时机成熟时，再决定是否发表。\u003c/p\u003e\n\u003cp\u003e携隐Melody也提到，通过输出倒逼输入，想要保持成长速度，就要多输出。她建议每天写作，控制在800-1000字，这样既不会花费太多时间，也足够表达观点。随着短内容的积累，再去写长篇大论会容易得多。养成每天输出的习惯后，你自然而然就会想接下来我会写什么，你就会主动去学习一些知识来丰富自己的资源库，这也就达到了输出倒逼输入的目的。\u003c/p\u003e\n\u003cp\u003e这些写作的方法可以泛化到其他任务，比如学习和工作。如果你掌握的知识与实际操作之间存在gap，就需要动手去实践，把手弄脏，这样才能形成闭环，使知识掌握得更加牢固。\u003c/p\u003e\n\u003ch2 id=\"参考资料\"\u003e参考资料\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://web.okjike.com/originalPost/6647d89b19d274e296bdb175\"\u003e阅读思考行动的闭环\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://mp.weixin.qq.com/s/cP6_0RT9jMpUxy5kpl5s9A\"\u003e把自己当成唯一读者\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://mp.weixin.qq.com/s/Nu16xFYIInSZN5hjW-avrw\"\u003e知识的缝隙\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://web.okjike.com/originalPost/66443da82421ceb48a12a68c\"\u003e输出倒逼输入\u003c/a\u003e\u003c/p\u003e","title":"把手弄脏"},{"content":"前言 这篇文章主要是来讨论在平时的阅读、学习和工作中如何保证高效率且有意义，文章分类我放在了时间管理中，主要原因是高效率的前提是时间管理要做好，否则就是空谈了。时间管理这个类别我应该会持续更新，因为在这方面我感觉自己做的还不够，还有好多要学习的地方，接下来有了新的感悟我会总结下来。\n分析自身 在上一篇文章中我提到了通过『判断信息是否与我有关』和『信息是否接近真相』来过滤信息源，这能大大减少我在无效信息上花费的时间，另外我还会使用日历来进行管理我每天的日程，最近我分析了我这两个月的日程安排，看上去似乎是满满当当，但是确实没有章法，很多事情被我安排的东一榔头西一棒槌，那么我就很难在这些事情上进入心流状态，完成的效果也不尽如人意。\n有一篇文章讲的是『深度阅读为何如此重要』，给了我挺大的启发，文章的主要观点是我们注意力比较分散时，就无法完整的感知这个世界，也很难把充分的注意力集中到困难的事情上，从而难以进行深度思考。不光是阅读，学习和工作应该是一样的道理，只有把全部的注意力集中到当前的事情上，才能进入心流状态，从而高质高效的完成。\n很明显我的日程安排把我的注意力打散了，在按照日程进行执行的时候，通常只是执行且没有目的，所以我需要想办法去解决这个问题。\n目的是什么 之前提过FOMO这种心态，我觉得通过上篇文章中的过滤信息源的方法已经很好的解决了我的FOMO心态，但是仍然不够彻底，那就结合目的来彻底解决。之所以会产生FOMO，无非是你想了解全部的信息，那目的就是获取尽可能多的信息，但是这有意义吗？你掌握的这些信息经过1年、5年或者10年以后基本没有任何价值，甚至在当下来看，也只能作为与人交流的谈资而已。\n所以我们的目的是有问题的，对于资讯类的信息，我们的目的应该是了解即可，在阅读时只需要了解标题就够了，这花不了5秒钟，当然这可能会对注意力造成危害，这个问题后边我们会谈。\n对于非资讯类的信息，这时候我们的目的就是判断有用的信息源，将信息汇总起来，通过我们的判断原则，如果与我有关且信息接近真相，那就把文章收集起来，在专门的阅读时间再读。\n好的，这就是我们在『浏览』信息时需要做的事情了，现在来回答注意力的问题。之所以我这里会使用『浏览』这个词，是因为我们当下的目的并不是深度阅读，而是过滤信息，如果在过滤信息的同时进行深入阅读，很有可能我们的注意力又被打散，导致深度阅读不够深度，那我们的时间就又被浪费了。浏览信息我们尽量在碎片化时间进行，这时候我们的注意力本身就是涣散的，不需要集中精力进行思考，相信长期坚持下来，这套过滤信息的方法我们会掌握熟练，达到一种几乎不用过多的思考就能筛选信息的能力。\n学习和工作是类似的思路，我们的目的就是提前做好准备，让学习和工作的冷启动变的容易，我们使用碎片化时间规划我们的安排，准备学习和工作的资料，为深度学习和工作做好准备。\n带着目的执行任务 在我们整块的时间，我们会进行深度的阅读、学习和工作。这时候我们的目的就会根据具体的任务而变化，比如阅读，我们的目的是尽可能深入的了解一个行业，学习的目的是尽可能多的学习一个算法等等。\n我们还需要对任务进行拆解，因为人的专注力是有限的，你不可能在很短的时间内完成一项重大的任务，那我们将比较大的任务拆解成小任务，也就相当于给每一个小任务设定了一个目标，带着这样的目的去执行任务的时候，我们就不会东一榔头西一棒槌，会很容易的进入心流状态，高效率的完成当前的任务。\n最后一点就是识别到哪些工作是无意义的，课代表的视频说了90%的工作都是无意义的，徐老师还提到，面对海量信息，我们需要培养批判性思维，形成自己的判断框架，以便有效筛选出有价值的信息。可能我们识别哪些任务是否有意义有些困难，那就需要我们尽可能的多思考，我们的目的是什么，《高效能人士的七个习惯》中提到了『以终为始』，就是这个道理。通过最终的目的来判断我们的任务是否有意义，不要为了完成任务而完成任务。\n总结 这篇文章相比于上一篇时间管理的文章，丰富了信息筛选的方法，信息筛选的目的是为了深度阅读做准备。我们弄清了我们的目的以后，在行动时就更能掌控自己的注意力，更快的进入心流的状态，高效的利用时间。\n","permalink":"https://kevinchen1994.github.io/posts/%E5%B8%A6%E6%9C%89%E7%9B%AE%E7%9A%84%E7%9A%84%E9%98%85%E8%AF%BB%E5%AD%A6%E4%B9%A0%E5%B7%A5%E4%BD%9C/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e这篇文章主要是来讨论在平时的阅读、学习和工作中如何保证高效率且有意义，文章分类我放在了时间管理中，主要原因是高效率的前提是时间管理要做好，否则就是空谈了。时间管理这个类别我应该会持续更新，因为在这方面我感觉自己做的还不够，还有好多要学习的地方，接下来有了新的感悟我会总结下来。\u003c/p\u003e\n\u003ch2 id=\"分析自身\"\u003e分析自身\u003c/h2\u003e\n\u003cp\u003e在上一篇文章中我提到了通过『判断信息是否与我有关』和『信息是否接近真相』来过滤信息源，这能大大减少我在无效信息上花费的时间，另外我还会使用日历来进行管理我每天的日程，最近我分析了我这两个月的日程安排，看上去似乎是满满当当，但是确实没有章法，很多事情被我安排的东一榔头西一棒槌，那么我就很难在这些事情上进入心流状态，完成的效果也不尽如人意。\u003c/p\u003e\n\u003cp\u003e有一篇文章讲的是『\u003ca href=\"https://mp.weixin.qq.com/s/vYVfc8uXnENhzYirqOJXUA\"\u003e深度阅读为何如此重要\u003c/a\u003e』，给了我挺大的启发，文章的主要观点是我们注意力比较分散时，就无法完整的感知这个世界，也很难把充分的注意力集中到困难的事情上，从而难以进行深度思考。不光是阅读，学习和工作应该是一样的道理，只有把全部的注意力集中到当前的事情上，才能进入心流状态，从而高质高效的完成。\u003c/p\u003e\n\u003cp\u003e很明显我的日程安排把我的注意力打散了，在按照日程进行执行的时候，通常只是执行且没有目的，所以我需要想办法去解决这个问题。\u003c/p\u003e\n\u003ch2 id=\"目的是什么\"\u003e目的是什么\u003c/h2\u003e\n\u003cp\u003e之前提过FOMO这种心态，我觉得通过上篇文章中的过滤信息源的方法已经很好的解决了我的FOMO心态，但是仍然不够彻底，那就结合目的来彻底解决。之所以会产生FOMO，无非是你想了解全部的信息，那目的就是获取尽可能多的信息，但是这有意义吗？你掌握的这些信息经过1年、5年或者10年以后基本没有任何价值，甚至在当下来看，也只能作为与人交流的谈资而已。\u003c/p\u003e\n\u003cp\u003e所以我们的目的是有问题的，对于资讯类的信息，我们的目的应该是了解即可，在阅读时只需要了解标题就够了，这花不了5秒钟，当然这可能会对注意力造成危害，这个问题后边我们会谈。\u003c/p\u003e\n\u003cp\u003e对于非资讯类的信息，这时候我们的目的就是判断有用的信息源，将信息汇总起来，通过我们的判断原则，如果与我有关且信息接近真相，那就把文章收集起来，在专门的阅读时间再读。\u003c/p\u003e\n\u003cp\u003e好的，这就是我们在『浏览』信息时需要做的事情了，现在来回答注意力的问题。之所以我这里会使用『浏览』这个词，是因为我们当下的目的并不是深度阅读，而是过滤信息，如果在过滤信息的同时进行深入阅读，很有可能我们的注意力又被打散，导致深度阅读不够深度，那我们的时间就又被浪费了。浏览信息我们尽量在碎片化时间进行，这时候我们的注意力本身就是涣散的，不需要集中精力进行思考，相信长期坚持下来，这套过滤信息的方法我们会掌握熟练，达到一种几乎不用过多的思考就能筛选信息的能力。\u003c/p\u003e\n\u003cp\u003e学习和工作是类似的思路，我们的目的就是提前做好准备，让学习和工作的冷启动变的容易，我们使用碎片化时间规划我们的安排，准备学习和工作的资料，为深度学习和工作做好准备。\u003c/p\u003e\n\u003ch2 id=\"带着目的执行任务\"\u003e带着目的执行任务\u003c/h2\u003e\n\u003cp\u003e在我们整块的时间，我们会进行深度的阅读、学习和工作。这时候我们的目的就会根据具体的任务而变化，比如阅读，我们的目的是尽可能深入的了解一个行业，学习的目的是尽可能多的学习一个算法等等。\u003c/p\u003e\n\u003cp\u003e我们还需要对任务进行拆解，因为人的专注力是有限的，你不可能在很短的时间内完成一项重大的任务，那我们将比较大的任务拆解成小任务，也就相当于给每一个小任务设定了一个目标，带着这样的目的去执行任务的时候，我们就不会东一榔头西一棒槌，会很容易的进入心流状态，高效率的完成当前的任务。\u003c/p\u003e\n\u003cp\u003e最后一点就是识别到哪些工作是无意义的，课代表的视频说了90%的工作都是无意义的，徐老师还提到，面对海量信息，我们需要培养批判性思维，形成自己的判断框架，以便有效筛选出有价值的信息。可能我们识别哪些任务是否有意义有些困难，那就需要我们尽可能的多思考，我们的目的是什么，《高效能人士的七个习惯》中提到了『以终为始』，就是这个道理。通过最终的目的来判断我们的任务是否有意义，不要为了完成任务而完成任务。\u003c/p\u003e\n\u003ch2 id=\"总结\"\u003e总结\u003c/h2\u003e\n\u003cp\u003e这篇文章相比于上一篇时间管理的文章，丰富了信息筛选的方法，信息筛选的目的是为了深度阅读做准备。我们弄清了我们的目的以后，在行动时就更能掌控自己的注意力，更快的进入心流的状态，高效的利用时间。\u003c/p\u003e","title":"带有目的的阅读、学习、工作"},{"content":"前言 时间管理是很多人都在谈的一个话题，这里我先泼一盆冷水，那就是时间管理并没有那么重要，精力管理是更重要的一个话题，因为你就算管理了你自己的时间，你这段时间具体能达成什么目标，以及你的工作效率是否高效，是不可控的。所以时间管理只能说让你尽量不浪费时间，具体能达成什么目标需要管理你的精力，让你在这段时间高效的进行工作。\n我自认为是一个比较擅长时间管理的人，我会提前规划自己的时间，不让自己漫无目的的消磨时间。但是我仍然犯了不少错误，导致大量的时间被浪费了。这周读了辉哥一篇文章，我感觉写的挺好的，所以就想结合这篇文章和我实际的经验来聊聊我的时间管理是怎么做的。辉哥的文章在这里，时间分配决定你会成为谁。\n浪费时间的大杀器 什么时候最浪费时间？你疲劳的时候。因为此时你除了想刷视频号、抖音，什么也不想干，包括睡觉。\n我看到这句话的时候，我仿佛被看穿了一样。我经历过几次晚上很累，想早点睡，却一直没有目的的刷手机的场景。虽然我手机里没有抖音/快手这类短视频app，但是并不妨碍我在B站刷一些视频来消磨时间。\n为什么疲劳的时候连睡觉都不想干呢？这明显是与人类生存机制想违背的，我觉得可能就是人类在进化过程中根本没有出现过这么有诱惑力的东西，像是短视频、手机，人类根本没有充足的意志力去抵抗他们，更别提是在你本身已经很疲劳，意志力最薄弱的时候了。\n最近正好在学习冥想，也看了一本关于冥想的书《正念的奇迹》，辉哥也在文章里提到了，累的时候，可能最好的休息就是冥想。所以以后当我很累，又不想睡觉的时候，我会选择去冥想，冥想不仅能提高我身体和头脑的灵活性，也能帮我清除大脑中的废物，这样时间不仅没有浪费，反而对我有帮助。\nFOMO 我一直以来就有一种FOMO（Fear of missing out）的症状，仿佛所有的信息我都需要关注，不然我可能就落后于时代了。我微信关注了很多公众号，所以每天会有有大量的技术文章，在地铁上或者工作间隙我刷到一些文章，觉得标题写的不错，点进去内容貌似还行，我当时没有大块空余的时间，就放到稍后再读了。这种行为我每天可能会有十来次，我一直想找一个大块的时间，专门来好好读这些文章。长年累月下来，积攒了大量的文章，不仅让我很有挫败感，还让我没有动力和勇气去打开他们。\n现实是那些文章真的值得我专门去找时间再读吗？我真的有大块的时间去读吗？可能两个问题都是否定的。虽然有不少优秀的作者会发表一些优秀的文章，值得沉下心来仔细阅读，认真思考，但是大多数文章都是抄来抄去，或者追逐一些热点新闻写一些没有干货的稿子，这种文章真的没必要用专门的时间去阅读，随便浏览一下就好了。\n再就是我每天的工作都需要整块的时间，我很难在专门找出另一个整块的时间去阅读了，通勤的时间我已经分配给读书了，嘈杂的地铁环境和手机的小屏也不适合去读技术类的文章。我必须得适应在10分钟内读一篇技术文章，尝试去理解作者想要表达的意思，并判断其中是否有我值得学习的地方，如果有的话，可以作为我的一个资源库，打好标签收录起来，以后需要的时候方便回顾。\n解决FOMO 辉哥这篇文章里的另一个观点很好的解决了我的FOMO问题，就是我们花费的时间的两个维度，『是否与我有关』和『是否接近真相与本质』，下边的图很直观的体现了，我们应该把时间花费在哪里。\n我们每天会接受到海量的信息，如果大脑全部去处理一遍会把我们珍贵的本来就很少的注意力消耗殆尽，所以我们需要判断出来哪些信息与我有关，并且尽量去考虑这件事的真相与本质，而不是光听、看第三方的观点。\n关于『接近真相与本质』我有一点想说的，通过搜索引擎和推荐系统呈现到我们眼前的信息，太多是二手、三手，甚至是七八手的了，我们需要一种找到信息源头的能力。比如知乎上将BPE算法的文章多得数不清，他们的源头就是一个博主更新在自己的个人技术博客的一篇文章，让人抄了过去，抄的人又被别人抄…所以当你看到这些文章的时候，可能会一头雾水，讲的头头是道，却怎么也不顺畅，当我找到原始文章的时候，那感觉犹如多年的老便秘终于通了一样。这种找到信息源头的能力，可以看文章的参考文献，或善用搜索引擎，知道用什么关键词去搜，或查阅一些权威的出版物，经典书籍、原始论文等。\n为此，我在这周单独找了两个小时，把稍后再读的一大批文章过滤了一遍。把与我相关的文章，并且原创性很高的文章留了下来，其他的全部简单浏览了一遍就过去了。\n总结 你时间的分配决定了你会成为什么样的人，也有这样一句话，你一天怎么过，一辈子就会怎么过。所以在大脑疲倦的时候，就去休息，冥想也好，听音乐也好，睡觉也好，不要想着累了还要卷，提高效率比拉长时间有效。再就是把有限的时间分配到与自己有关且正确的事情上，不要为一些装作“有价值”的无意义事情打扰。\n","permalink":"https://kevinchen1994.github.io/posts/%E6%88%91%E7%9A%84%E6%97%B6%E9%97%B4%E9%83%BD%E6%B5%AA%E8%B4%B9%E5%9C%A8%E5%93%AA%E4%BA%86/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e时间管理是很多人都在谈的一个话题，这里我先泼一盆冷水，那就是时间管理并没有那么重要，精力管理是更重要的一个话题，因为你就算管理了你自己的时间，你这段时间具体能达成什么目标，以及你的工作效率是否高效，是不可控的。所以时间管理只能说让你尽量不浪费时间，具体能达成什么目标需要管理你的精力，让你在这段时间高效的进行工作。\u003c/p\u003e\n\u003cp\u003e我自认为是一个比较擅长时间管理的人，我会提前规划自己的时间，不让自己漫无目的的消磨时间。但是我仍然犯了不少错误，导致大量的时间被浪费了。这周读了辉哥一篇文章，我感觉写的挺好的，所以就想结合这篇文章和我实际的经验来聊聊我的时间管理是怎么做的。辉哥的文章在这里，\u003ca href=\"%E6%97%B6%E9%97%B4%E5%88%86%E9%85%8D%E5%86%B3%E5%AE%9A%E4%BD%A0%E4%BC%9A%E6%88%90%E4%B8%BA%E8%B0%81\"\u003e时间分配决定你会成为谁\u003c/a\u003e。\u003c/p\u003e\n\u003ch2 id=\"浪费时间的大杀器\"\u003e浪费时间的大杀器\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003e什么时候最浪费时间？你疲劳的时候。因为此时你除了想刷视频号、抖音，什么也不想干，包括睡觉。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e我看到这句话的时候，我仿佛被看穿了一样。我经历过几次晚上很累，想早点睡，却一直没有目的的刷手机的场景。虽然我手机里没有抖音/快手这类短视频app，但是并不妨碍我在B站刷一些视频来消磨时间。\u003c/p\u003e\n\u003cp\u003e为什么疲劳的时候连睡觉都不想干呢？这明显是与人类生存机制想违背的，我觉得可能就是人类在进化过程中根本没有出现过这么有诱惑力的东西，像是短视频、手机，人类根本没有充足的意志力去抵抗他们，更别提是在你本身已经很疲劳，意志力最薄弱的时候了。\u003c/p\u003e\n\u003cp\u003e最近正好在学习冥想，也看了一本关于冥想的书《正念的奇迹》，辉哥也在文章里提到了，累的时候，可能最好的休息就是冥想。所以以后当我很累，又不想睡觉的时候，我会选择去冥想，冥想不仅能提高我身体和头脑的灵活性，也能帮我清除大脑中的废物，这样时间不仅没有浪费，反而对我有帮助。\u003c/p\u003e\n\u003ch2 id=\"fomo\"\u003eFOMO\u003c/h2\u003e\n\u003cp\u003e我一直以来就有一种FOMO（Fear of missing out）的症状，仿佛所有的信息我都需要关注，不然我可能就落后于时代了。我微信关注了很多公众号，所以每天会有有大量的技术文章，在地铁上或者工作间隙我刷到一些文章，觉得标题写的不错，点进去内容貌似还行，我当时没有大块空余的时间，就放到稍后再读了。这种行为我每天可能会有十来次，我一直想找一个大块的时间，专门来好好读这些文章。长年累月下来，积攒了大量的文章，不仅让我很有挫败感，还让我没有动力和勇气去打开他们。\u003c/p\u003e\n\u003cp\u003e现实是那些文章真的值得我专门去找时间再读吗？我真的有大块的时间去读吗？可能两个问题都是否定的。虽然有不少优秀的作者会发表一些优秀的文章，值得沉下心来仔细阅读，认真思考，但是大多数文章都是抄来抄去，或者追逐一些热点新闻写一些没有干货的稿子，这种文章真的没必要用专门的时间去阅读，随便浏览一下就好了。\u003c/p\u003e\n\u003cp\u003e再就是我每天的工作都需要整块的时间，我很难在专门找出另一个整块的时间去阅读了，通勤的时间我已经分配给读书了，嘈杂的地铁环境和手机的小屏也不适合去读技术类的文章。我必须得适应在10分钟内读一篇技术文章，尝试去理解作者想要表达的意思，并判断其中是否有我值得学习的地方，如果有的话，可以作为我的一个资源库，打好标签收录起来，以后需要的时候方便回顾。\u003c/p\u003e\n\u003ch2 id=\"解决fomo\"\u003e解决FOMO\u003c/h2\u003e\n\u003cp\u003e辉哥这篇文章里的另一个观点很好的解决了我的FOMO问题，就是我们花费的时间的两个维度，『是否与我有关』和『是否接近真相与本质』，下边的图很直观的体现了，我们应该把时间花费在哪里。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/notion_321b6f2d-3a76-4cc9-97cd-4e06aa25497fb79d541e-3d6c-4d47-845d-92e2a3238b07.png\"\u003e\u003c/p\u003e\n\u003cp\u003e我们每天会接受到海量的信息，如果大脑全部去处理一遍会把我们珍贵的本来就很少的注意力消耗殆尽，所以我们需要判断出来哪些信息与我有关，并且尽量去考虑这件事的真相与本质，而不是光听、看第三方的观点。\u003c/p\u003e\n\u003cp\u003e关于『接近真相与本质』我有一点想说的，通过搜索引擎和推荐系统呈现到我们眼前的信息，太多是二手、三手，甚至是七八手的了，我们需要一种找到信息源头的能力。比如知乎上将BPE算法的文章多得数不清，他们的源头就是一个博主更新在自己的个人技术博客的一篇文章，让人抄了过去，抄的人又被别人抄…所以当你看到这些文章的时候，可能会一头雾水，讲的头头是道，却怎么也不顺畅，当我找到原始文章的时候，那感觉犹如多年的老便秘终于通了一样。这种找到信息源头的能力，可以看文章的参考文献，或善用搜索引擎，知道用什么关键词去搜，或查阅一些权威的出版物，经典书籍、原始论文等。\u003c/p\u003e\n\u003cp\u003e为此，我在这周单独找了两个小时，把稍后再读的一大批文章过滤了一遍。把与我相关的文章，并且原创性很高的文章留了下来，其他的全部简单浏览了一遍就过去了。\u003c/p\u003e\n\u003ch2 id=\"总结\"\u003e总结\u003c/h2\u003e\n\u003cp\u003e你时间的分配决定了你会成为什么样的人，也有这样一句话，你一天怎么过，一辈子就会怎么过。所以在大脑疲倦的时候，就去休息，冥想也好，听音乐也好，睡觉也好，不要想着累了还要卷，提高效率比拉长时间有效。再就是把有限的时间分配到与自己有关且正确的事情上，不要为一些装作“有价值”的无意义事情打扰。\u003c/p\u003e","title":"我的时间都浪费在哪了"},{"content":"前言 RAG这个词听过很多次，我只是了解他大概是做什么的，今天看到一篇文章详细介绍了RAG的流程和基本原理，觉得很不错，就照着写了这篇文章，算是一种记录和知识扫盲吧。\nby the way，感觉我使用的perflexity应该就是使用的这种RAG技术。\nRAG（Retrieval Augmented Generation），检索增强生成为大模型提供了从某些数据源检索到信息，并作为大模型输入的上下文来提供答案的方法。\n简单来说，就是通过向量进行检索到与query相关的信息，并将这些信息输入给大模型，通过Prompt使大模型基于这些进行进行回答。\n基础的RAG技术 将文本分割成块，然后使用基于Transformer decoder的模型将这些块嵌入到向量中，将所有这些向量放入一个索引中，最后为LLM创建一个提示，告诉模型在我们在搜索步骤中找到的上下文中回答用户的查询。 在运行时，我们使用相同的编码器模型将用户的查询向量化，然后对索引执行这个查询向量的搜索，找到前k个结果，从我们的数据库中检索相应的文本块，并将它们作为上下文输入到LLM的提示中。 高级RAG技术 切分和向量化 切分 我们知道一般大模型都有输入长度的限制，所以如何一篇文档很长的话，我们是不能直接将其进行向量化的。另外即使模型的上下文窗口很大，我们将一篇文档切分成段落也比直接将整篇文档进行向量化的效果要好，因为模型最终输入的向量是汇聚了整篇文档的全部信息，向量的维度是固定的，对于一段100个token的段落和10000个token的文档，模型所生成的向量都是一样的大小，但是他们包含的信息是有所的不同的，文本的长度越短，向量所包含的信息越精确。 所以我们尽量将篇幅较大的文档进行切分，切分的策略可以根据大模型的上下文窗口制定，尽量选择比上下文窗口小的长度。除此之外，在切分时要考虑将文档按照段落、句子进行切分，而不是把一句话从中间切开。\n向量化 切分后就需要选择一个模型来讲文本进行向量化，可以选择闭源的openAI的接口，也可以选择开源的模型。这里可以推荐一个开源的仓库，可以用来训练自己的embedding。FlagEmbedding，你也可以去huggingface的榜单选择排名靠前的模型。MTEB leaderboard 搜索索引 向量存储索引 我们得到一批文档的向量后需要进行存储，这样才能够进行检索，无论检索是通过暴力的距离计算还是通过开源的faiss计算，你都需要将向量存储起来。 分层索引 如果你需要从很多文档中检索信息，那对你的索引就有比较高的要求，一个有效的方法就是创建两个索引，一个由摘要组成，一个由文档块组成，并分两步进行检索。首先通过摘要筛选出相关的文档，然后在这些相关文档中检索文档块。 假设性问题和HyDE 还有另外一种方法，那就是让大模型为每个块生成一个问题，并将这些问题嵌入向量中，在运行时针对这个问题向量索引进行查询搜索（在我们的索引中用问题向量替换块向量），然后在检索后路由到原始文档块，并将他们作为上下文提供给大模型。这种方法通过查询与假设性问题之间更高的语义相似性，提高了搜索质量。 还有一种逆向逻辑方法称为HyDE（Hypothetical Document Embeddings），让大模型给定查询生成一个假设性回应，然后使用其向量和查询向量来提高搜索质量。\n上下文丰富化 上下文丰富化试检索更小块以提高搜索质量，但是添加上下文让大模型进行推理。通常有两种做法，通过在检索到的较小块周围的句子扩展上下文，或者将文档递归地分割成包含较小子块的多个较大的父块。\n句子窗口检索 在这个方法中，文档中的乜咯句子都进行向量化，这提高了查询与上下文之间的相似性的准确性。为了在找到最相关的单个句子后更好的进行推理所发现的上下文，我们通过在检索到的句子前后扩展k个句子的上下文窗口，然后将此扩展上下文输入给大模型。\n自动合并检索器（又称父文档检索器） 这里的想法与句子窗口检索非常类似，搜索更精准的信息片段，然后将所述上下文提供给大模型进行推理之前扩展上下文窗口。文档被分割成较小的子块，这些子块引用较大的父块。 在这种方法中，首先在更细粒度的子块上进行搜索，找到与query最相关的块。然后，系统会自动将这些子块与他们所属的父块进行结合。这样就能在回答时，使大模型有更丰富的上下文。\n融合检索或者混合搜索 这个一个相对较老的想法，即从两个世界中各取所长，基于关键字的传统搜索（稀疏检索算法，如tf-idf和bm25）和现代语义或向量搜索，将这两种方法结合在一个检索结果中。这里唯一的技巧是正确组合具有不同相似性得分的检索结果，这个问题通常通过使用倒数排名融合算法来解决，重新排列检索结果以获得最终输出。 重排和过滤 使用上述任何算法得到检索结果后，需要进行过滤、重排或者一些转换来精炼这些结果了。在LlamaIndex中，有多种可用的后处理器，可以根据相似性分数、关键词、元数据过滤结果，或者使用其他模型进行重排，比如LLM、句子转换器交叉编码器、Cohere重排端点，或者基于日期的最新性等元数据——基本上，你能想到的都可以。\n重排和过滤是在将检索到的上下文提供给LLM以获取最终答案之前的最后一步。现在是时候进入更复杂的RAG技术，如查询转换和路由，这两者都涉及到LLM，因此代表了主动性行为——在我们的RAG流程中涉及到一些复杂的逻辑，包括LLM的推理。\n查询转换 查询转换是一系列技术，利用LLM作为推理殷勤来修改用户输入，以提高检索质量。\n有几种不同的方式可以做到这一点，如果查询很复杂，LLM可以将其分解成几个子查询。\n回溯提示 回溯提示是使用LLM生成更一般的query，并检索该query，获得更一般或者高层次的上下文，有助于支撑我们对原始query的回答。 同时，也会对原始query进行检索，两种上下文都会输入给LLM。\n查询重写 查询重写使用LLM重构原始query。 聊天引擎 在构建一个能够针对单个查询多次运行的优秀RAG系统中，下一个重要的环节就是聊天逻辑，这与LLM时代的经典聊天机器人一样，需要考虑对话的上下文。这对于支持后续问题、指代消解或与先前对话上下文相关的任意用户命令是有必要的。可以通过查询压缩技术来解决，同时考虑聊天的上下文和用户的query。\n上下文压缩方法有几种，一种是流行且相对简单的方法，ContextChatEngine。他首先检索与用户query相关的上下文，然后将其连同聊天记录一起发送给LLM，使LLM在生成下一个回答时能够了解之间的上下文。\n另一个更复杂的方法是CondensePlusContextMode，在这种模式中，每次交互都会将聊天历史和最后一条消息压缩成一个新的query，然后这个query会进行检索索引，检索到的上下文连同原始的用户消息一起发送给LLM，生成答案。\n查询路由 查询路由是一个以LLM为驱动的决策步骤，决定针对用户查询接下来要做什么，通常是概括总结、针对某些数据索引执行搜索、或尝试不同的路由，然后将他们的输出合并成一个答案。\n你可能有多个数据来源，查询路由还用于选择索引，或者说选择数据存储位置，以发送用户query。\nLlamaIndex和LangChain都支持查询路由器。\nRAG中的代理 LlamaIndex和LangChain都支持的代理（agents），自从第一个LLM api发布以来，就已经存在了。这个想法是为一个能够进行推理的LLM提供一套工具和一个要完成的任务。这些工具可能包括一些确定性函数，如任何代码函数、外部api或者其他代理，LLM链式调用这些代理。\n我们来看一个多文档代理方案，每个文档初始化一个代理，能够进行文档概要和经典的问答机制，并有一个顶级代理，负责将查询路由到文档代理，并进行最终答案的合成。\n这种方法的好处是能够比较不同的解决方案或实体，缺点就是设计代理中LLM的多次来回迭代，速度比较慢。\n响应合成器 这是任何RAG系统的最后一步，基于我们检索的所有上下文和厨师的query生成文档。最简单的方法是将所有获取到的上下文，连同query一起输入给LLM。但是，除此以外，还有其他更负责的选项，涉及多次LLM调用以优化检索到的上下文并生成更好的答案。\n通过逐块将检索到的上下文发送给LLM来迭代地完善答案。 概括检索到的上下文以适应Prompt。 基于不同的上下文块生成多个答案，然后将他们连接或者概括起来。 编码器和LLM微调 我们可以对负责编码的模型和负责生成答案的LLM进行微调，幸运的是，后者是一个很好的few-shot leaner。\n编码器微调 ranker微调，如果你不信任你的编码器，你可以使用交叉编码器对检索结果进行排序。有点类似BERT中的双句任务，使用分隔符将query与文本块合并起来，相似为1，不相似为0。 LLM微调 评估 RAG系统性能评估有几个框架，它们共享一个理念，即拥有几个独立的指标，如整体答案相关性、答案的根据性、忠实度和检索到的上下文相关性。\nRagas框架使用忠实度和答案相关性作为生成答案质量的指标，以及经典的上下文精确度和召回率用于RAG方案的检索部分。\n在Andrew NG最近发布的精彩短课程《构建和评估高级RAG》中，LlamaIndex和评估框架Truelens建议使用RAG三元组——检索到的上下文与查询的相关性、根据性（LLM答案受提供的上下文支持的程度）以及答案与查询的相关性。\n最关键且最可控的指标是检索到的上下文相关性——基本上上面描述的高级RAG流程的部分以及编码器和排名器微调部分旨在改善这一指标，而第响应合成器和LLM微调则专注于答案相关性和根据性。\n一个相当简单的检索器评估流程的例子可以在这里找到，并且已应用于编码器微调部分。一种更高级的方法不仅考虑命中率，还考虑了平均倒数排名（一个常见的搜索引擎指标）以及生成答案的指标，如忠实度和相关性，这在OpenAI cookbook中有所展示。\nLangChain有一个相当先进的评估框架LangSmith，可以实现自定义评估器，它还监控RAG流程中的运行轨迹，以使你的系统更透明。\n如果你在使用LlamaIndex构建，那么有一个rag_evaluator llama包，提供了一个快速工具，用公共数据集评估你的流程。\n参考文章 Advanced RAG Techniques: an Illustrated Overview\n","permalink":"https://kevinchen1994.github.io/posts/rag%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86%E7%82%B9%E6%89%AB%E7%9B%B2/","summary":"RAG这个词听过很多次，我只是了解他大概是做什么的，今天看到一篇文章详细介绍了RAG的流程和基本原理，觉得很不错，就照着写了这篇文章，算是一种记录和知识扫盲吧。","title":"RAG技术知识点扫盲"},{"content":"2023年我几乎所有的读书都是在通勤的地铁上完成的，时间虽然不算特别长，100个小时左右，但是效率很高，我戴着耳机只需要一头扎进书里就好。这一年我一共读完了24本书，涉及到的类型有小说、散文、政治学、经济学等等，我不挑类型，只要是好书、有人推荐、豆瓣评分不低的离谱、且我看了简介后感兴趣我都会拿来读。\n华罗庚先生说：先把书读厚，再把书读薄。我理解，把书读厚也就是多读书，把书读薄就是吸收书里的思想、精华。我一般会去zlibrary下载想读的书，然后再导入微信读书，在读的过程中会对一些句子划线、写下当时的想法，在我的GitHub上会通过action每天定时跑一个脚本自动把微信读书中的划线句子和笔记同步到Notion的database中，后续非常方便进行回顾和检索。\n我本想将这一年读过的书做一个分类，然后按照类别进行总结的，然而这样免不了会把每一本书都讲一遍，没有太大的必要，于是决定进行一个排名吧，排出这一年的topN，具体N是几，写到多少算多少吧。排名会考虑书的类型/主题等信息，尽量丰富，比如小说已经上榜了，那后续的排名就尽量减少小说的权重。\n⚠️ 以下排名全都是个人主观想法，不代表作者与作品真实的排名。\ntop1 《秋园》 作者：杨本芬 出版时间：2020-6 出版社：北京联合出版社 豆瓣评分：9.0\n《秋园》在2020年出版，获得了当年的豆瓣最佳图书，我是后来才听说，看了看豆瓣简介心里并没有要读的冲动，就搁置了下来。在2023年初，我决定拿出来看看，这一读就一发不可收拾了，完全被杨本芬的笔下的故事吸引了，把她出版的几本书全部看了一遍。\n作者杨本芬并不是一个专业的作者，而是一个八旬的老奶奶，她在退休以后，六十岁才开始写作。\n这本书写的是作者母亲一生的故事，其实也可以理解成这是大多数中国人民经历过的故事，从中体现出来的中国女性的坚毅让我想到我的母亲、奶奶、姥姥。有人说这是女版的《活着》，我刚读完的时候也有这种感觉，作者都是平静的诉说苦难，越是平静，苦难就越深沉。我们更能感受到整个时代中的一粒沙，落到一个人身上都是一座大山。\n“我也感到奇怪：只要提起笔，过去那些日子就涌到笔尖，抢着要被诉说出来。我就像是用笔赶路，重新走了一遍长长的人生。”\ntop2 《男孩、鼹鼠、狐狸和马》 作者：[英]查理·麦克西 出版时间：2021-1 出版社：北京联合出版公司 豆瓣评分：8.7\n这是一本绘本，10分钟就能读完，适合所有人观看。\n“你有没有觉得很奇怪？我们只能看见外面，但几乎所有的事都发生在我们内心。”\n“眼泪掉下来是有原因的，他们是力量，而不代表软弱。”\n“‘你说过最勇敢的话是什么？‘男孩问。‘帮帮我。‘马回答。”\n““当你有半杯水的时，你觉得是有一半空着，还是有一半满了呢？”鼹鼠问。“我会感激我拥有一个杯子。”男孩说。”\n…\n当前这个时代，大家都在关注房子、车子、票子，留给我们内心的时间越来越少，或许你会感到迷茫，或许你会感到孤单，或者你会垂头丧气，但是不要担心，你有很多朋友，这个世界上有很多人爱你，鼓励你。这时候就需要这样的一本书来鼓励我们、温暖我们。\n书里句子在我生活和工作不同的场景给了我好多启发，有人可能觉得是鸡汤，但是我先干为敬。\ntop3 《金钱心理学》 作者：[美] 摩根 · 豪泽尔 出版时间：2023-6 出版社：民主与建设出版社 豆瓣评分：8.3\n投资是否成功取决于你的行为，你的行为跟你的智商是没有关系的，你行为背后可能跟你的认知、成长环境、情绪、经历的事情有关。这本书不是教你怎么投资，而是教你树立正确的金钱观，从而在投资上尽量做出正确的行为。 这本书非常好读，整体思想就是多存钱、利用复利、好好生活。读了一些理财相关的书和文章之类的，就感觉很多投资大师在投资上并什么所谓“不为人知的高超”技巧，而是利用时间+复利获得很好的投资结果，相反他们的时间都花在了阅读提升自己，以及热爱生活，陪伴家人上了。\n书里举的例子也都很有说服力，比如一个名为罗纳德 里德的门卫在离世的时候有八百万美金的资产，他的资产就是他攒下每一分钱，并买入蓝筹股，经过漫长时间的等待，通过复利变成了一笔巨额财富。而高学历、具有金融背景的理查德 福肯斯，工作非常成功，拥有很多财富，却因为挥霍资产，在2008年金融危机破产了。这两个人拥有完全相反的人生，所以你拥有的财富跟你的知识、背景是没有关系的，只与你的行为有关。不过话说回来，你的行为还是基于你的认知去执行的，这也说明了我们需要不断提高我们的认知。\n书里关于财富自由的内容对我启发也是挺大的，“在说自己想成为百万富翁时，大多数人想表达的意思可能是’我希望我有100万可以随便花’，但这和身为百万富翁的真正意义刚好相反。”当你拥有100万后你随便花，花完这100万你还是百万富翁吗？财富自由并不是有钱随便花，而是我可以选择消费或者不消费，这同时也是有钱和富有的区别。\n你在读这本书的内容时可能会想，这些我都知道啊，难道这样就能有钱吗？读完这本书你会知道确实是这样，真的就是这么简单，但是知行合一是最难的。\ntop4 《埃隆马斯克传》 作者：[美]沃尔特·艾萨克森 出版时间： 2023-9 出版社：中信出版社 豆瓣评分：8.6\n这本书应该在2023年热度很高的，无论你是不是科技行业从业者，你都应该知道马斯克这个人，除了他世界首富的头衔，他的特斯拉、SpaceX、Twitter、xAI每一个都话题满满。再加上这本书的作者爱萨克森，也是写传记的大拿，所以2023年很难不去关注这本书。\n由于我本身是科技行业从业者，所以我对马斯克的关注会比较多，之前就读过一本他的传记，《硅谷钢铁侠》，不过这本书没有像《埃隆马斯克传》这样得到马斯克这么多的权限。艾萨克森甚至能够每个月去参加马斯克在不同公司的会议，与马斯克身边亲近的人进行采访，另外在本书出版之前马斯克也是没有看过本书的全部内容的，所以这本书应该是目前了解马斯克最真实一面的比较好的素材了。\n作者在书里这样形容马斯克：“他的胆大包天、他的狂妄自大驱使着他完成那些史诗般的壮举，但是不是因此我们就可以原谅他的鲁莽轻率、出言不逊、横行霸道、冷酷无情？答案当然是否定的。我们可以欣赏一个人的优点，同时指责他的缺点，但我们也要理解这些因素如何相互交织、难以割裂。理解他的人格整体，就要接受其中难以剥离的阴暗面。正如莎士比亚教导我们的，所有英雄都有人格缺陷，有些英雄为缺陷所困，有些英雄以悲剧而终，而那些被我们视为恶棍的角色可能比英雄更加复杂多面。他教导我们，即便性格最为良善之人，他的人格也’由他的缺点所塑造’。”\n并不是说我们看完这本书，我们学习马斯克的行事风格我们就可以成为马斯克，马斯克的成功是不可复制的，他对整个世界中的能源、航空、AI各个方面的贡献是无比巨大的，他的目光着眼与整个人类的未来。为了解决环境污染和能源问题，他推广电动车，那可是十几年前，那时候大家在电动车领域能不能商业化赚钱还没有答案，到现在我国新能源渗透率将近40%，他在其中起到的作用可想而知。他想要人类成功夸行星物种，创办SpaceX，成为第一个发射火箭的民营企业，获得了NASA的订单。\n还有一点就是硅谷，你不得不承认硅谷就是世界创新中心，我们还有比较大的差距，革命尚未成功，同志仍需努力。\ntop5 《习惯的力量》 作者：[美] 查尔斯·都希格 出版时间：2013-3 出版社：中信出版社 豆瓣评分：8.0\n在读这本书之前，我曾经写过我是如何养成习惯的，读完这本书后，我明白了其中的科学道理，那就是习惯的养成是由三点构成的：暗示、惯常行为、奖赏。我们大脑中的这个过程是一个由三步组成的回路。第一步，存在着一个暗示，能让大脑进入某种自动行为模式，并决定使用哪种习惯。第二步，存在一个惯常行为，这可以是身体、思维或情感方面的。第三步则是奖赏，能让你的大脑辨别出是否应该记下这个回路，以备将来之用。\n清楚这三点以后，我们就能根据自己的需要做很多事情了，比如你想养成跑步的习惯，阅读的习惯，只需要给自己设定好暗示就可以，到哪个时间点，或者到哪个地理位置，你就去做这些事情，在做完以后给自己一个奖赏，不需要多久你就会形成这样的习惯回路。甚至你戒烟也可以通过这个回路来解决，你回想一下你什么时候想吸烟，这就是你的暗示，你需要躲避这个暗示，或者在惯常行为中替换掉这个行为。\n杜克大学2006年发布的研究报告表明，人每天有40%的行为并不是真正由决定促成的，而是出于习惯。所以你想要掌控自己的人生，就需要掌控自己的每一天，自己的每一天又是被习惯掌控的，你知道该怎么做了吧。\ntop6 《我们为什么要睡觉》 作者：[英] 马修·沃克 出版年:：2021-3 出版社：北京联合出版公司 豆瓣评分：8.5\n这本书应该是我在比尔盖茨的年度书单里看到的，一直在收藏夹里吃灰，直到我今年早起跑步，我才开始关注我的睡眠状况。读完这本书，我看懂了iOS 中睡眠的图了，并且每天早上起来都会根据图中的信息对自己昨晚的睡眠状况做简单的分析。我之前一直以为深度睡眠越多越好，读完书后才知道不同阶段的睡眠都有其对应的作用。\n我们每天的睡眠都是由激素进行调节的，比如褪黑素开始分泌的时候就是告诉你要睡觉啦，你体内的腺苷会让你产生疲劳感，我们现代人可能会通过摄入咖啡因来阻止腺苷与其受体进行结合，告诉我们的身体“我还不累”，有时候晚上睡不着觉还会主动摄入褪黑素帮助我们睡眠，其实这些行为表面上是在控制我们的身体，实际上是对身体的伤害。\n我们都会关注我们的身体，比如饿了吃东西、身体不舒服就看医生，而我们却忽视了我们的睡眠。一晚糟糕睡眠所造成的身体和精神损害之严重，会让那些因食物或运动缺乏而引起的损害相形见绌。很难想象有任何其他方法（不管是自然的还是医学方面的控制），能够从所有层面为身心健康提供更强有力的补救。\n读完这本书后，我就感到每天晚上的睡眠是多重要，我每次有熬夜的想法的时候，我就会想熬一次夜所造成的伤害，跟我要熬夜所做的事情相比哪个更重要，这通常就会打消我熬夜的念头。我们一生三分之一的时间都在睡觉，所以好好睡觉吧，睡眠是我们进化几百万年得到的上帝的馈赠，请珍惜自己每晚的睡眠，并努力让其更好。\n","permalink":"https://kevinchen1994.github.io/posts/2023%E5%B9%B4%E8%AF%BB%E4%B9%A6%E6%80%BB%E7%BB%93/","summary":"2023年读书个人总结","title":"2023年读书总结"},{"content":"前言 大模型时代模型的参数量越来越大，GPT3的模型参数达到了175B，各大厂商也在不断突破模型参数量的天花板。模型参数量增大显而易见的好处就是模型的能力越来越强，并且模型的参数量达到一定的规模后，模型就会出现涌现能力（Emergent Abilities），而带来的坏处也是很明显的，那就是训练和推理的硬件成本不断增加。\n2023年12月，Mistral AI在开源社区扔了一条磁力链接，引爆了社交网络。Mistral AI基于混合专家模型Mixture of Experts（MoE），证明通过8个7B的模型就能超越LLaMA 2 70B模型的效果，甚至部分超越了GPT 3.5的水平。之前就有人分析过GPT4就是使用8个专家模型组成的专家系统，这给我们带来了很多启发，是否未来大模型未来会朝着这个方向发展呢？\nMixtral 8x7B模型架构 Mixtral 8x7B与LLaMA模型的区别就是在attention计算中将MLP Layer替换成了一个门控层和8个专家模型，通过门控层会给出每个专家层的权重，每个token会选择top2的专家进行计算， 这使得模型训练和推理的速度相比于LLaMA 2 70B会显著提高。\n不过这里有一个误区，那就是模型虽然叫8x7B，但是模型的参数并不是56B，因为在每个层中只有专家层是独立存在的，其他部分如attention是权重共享的，所以模型的参数量在47B左右。\n专家模块细节 因为Mixtral 8x7B模型架构与LLaMA相比只有FFN块不同，所以我们只关注这块的细节。\n我之前看过一个代码解读，他说MoE层是先计算所有专家的输出，然后在选择每个token对应的专家，其实这样的说法是错误的。因为如果每次都计算所有专家的输出，那就不能体现出MoE模型的优势了，8个模型都计算的话，你的计算量是很大的，那么你的耗时也会增加，起不到提速的效果。所以正确的理解是通过门控层来选择专家，然后只有对应的2个专家会进行前向计算，这样就起到了减少计算量和提速的效果。\n首先输入经过attention计算后，经过残差连接、Norm层会输入进行专家层，专家层由门控层和8个专家构成。门控层其实就是一个全连接层，其输出结果再经过softmax函数得到各专家的权重，我们会选择权重排名的top2作为当前token要使用的专家。然后对这两个专家的权重重新进行归一化，得到这两个专家的权重，在前向计算的过程中，top2的专家的输出结果会与其对应的权重进行加权求和，最终我们就能得到整个输入使用不同的专家的结果。\n整个流程是比较简单的，可能容易弄混的地方在于模型的输入是token粒度的，所以我们在计算权重和选择专家的过程中，都是以每个token为视角的。也就是每一个token都会计算专家权重，并选择top2的专家计算前向结果，最终得到的是整个输入的结果。这个过程结合代码看会更加清晰。\n专家模块代码注释 下边的代码是专家模块的实现方法，我对重要部分写上了注释，可以结合注释进行理解。\nclass MixtralSparseMoeBlock(nn.Module): \u0026#34;\u0026#34;\u0026#34; This implementation is strictly equivalent to standard MoE with full capacity (no dropped tokens). It\u0026#39;s faster since it formulates MoE operations in terms of block-sparse operations to accomodate imbalanced assignments of tokens to experts, whereas standard MoE either (1) drop tokens at the cost of reduced performance or (2) set capacity factor to number of experts and thus waste computation and memory on padding. \u0026#34;\u0026#34;\u0026#34; def __init__(self, config): super().__init__() self.hidden_dim = config.hidden_size self.ffn_dim = config.intermediate_size self.num_experts = config.num_local_experts self.top_k = config.num_experts_per_tok # gating self.gate = nn.Linear(self.hidden_dim, self.num_experts, bias=False) self.experts = nn.ModuleList([MixtralBLockSparseTop2MLP(config) for _ in range(self.num_experts)]) def forward(self, hidden_states: torch.Tensor) -\u0026gt; torch.Tensor: \u0026#34;\u0026#34;\u0026#34; \u0026#34;\u0026#34;\u0026#34; # 由attention计算后输出的hidden_states作为输入 batch_size, sequence_length, hidden_dim = hidden_states.shape # 将hidden_states构建成一个二维的形状，用于处理每一个token hidden_states = hidden_states.view(-1, hidden_dim) # router_logits: (batch * sequence_length, n_experts) # 通过门控来生成路由，用来决定每一个token由哪些专家处理 router_logits = self.gate(hidden_states) # 通过softmax计算每一个专家对于每个token的处理权重 routing_weights = F.softmax(router_logits, dim=1, dtype=torch.float) # 选取每个token的前top_k个专家和其对应的权重 selected_experts: (batch * sequence_length, top_k) routing_weights, selected_experts = torch.topk(routing_weights, self.top_k, dim=-1) # 对每一个token对应的专家的权重值进行归一化，使其权重之和为1 routing_weights /= routing_weights.sum(dim=-1, keepdim=True) # we cast back to the input dtype routing_weights = routing_weights.to(hidden_states.dtype) # final_hidden_states用来存储每个token对应的专家结果，初始值为0 final_hidden_states = torch.zeros( (batch_size * sequence_length, hidden_dim), dtype=hidden_states.dtype, device=hidden_states.device ) # One hot encode the selected experts to create an expert mask # this will be used to easily index which expert is going to be sollicitated # 使用one hot编码来代表每个token使用哪些专家 # one hot: (batch * sequence_length, top_k, num_experts) =\u0026gt; expert_mask: (num_experts, top_k, batch * sequence_length) # 这样做的好处就是，用专家的视角，每次遍历只需要遍历每个专家所需要处理的token即可，否则需要遍历每个token使用了哪个专家，前向的次数随着文本的长度线性增加。 expert_mask = torch.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).permute(2, 1, 0) # Loop over all available experts in the model and perform the computation on each expert for expert_idx in range(self.num_experts): expert_layer = self.experts[expert_idx] # idx代表当前专家作为top1需要负责的token索引、作为top2需要负责的token的索引 # top_x代表当前专家负责的token的索引位置。 idx, top_x = torch.where(expert_mask[expert_idx]) # 如果top_x中没有1，则代表当前专家不负责任何token，就跳过这个专家 if top_x.shape[0] == 0: continue # in torch it is faster to index using lists than torch tensors top_x_list = top_x.tolist() idx_list = idx.tolist() # Index the correct hidden states and compute the expert hidden state for # the current expert. We need to make sure to multiply the output hidden # states by `routing_weights` on the corresponding tokens (top-1 and top-2) # 根据索引从输入的隐向量中取得对应的向量，传入到专家模型中进行前向计算 current_state = hidden_states[None, top_x_list].reshape(-1, hidden_dim) current_hidden_states = expert_layer(current_state) * routing_weights[top_x_list, idx_list, None] # However `index_add_` only support torch tensors for indexing so we\u0026#39;ll use # the `top_x` tensor here. # 将当前专家模型的输出写入到预先定义好的final_hidden_states中 final_hidden_states.index_add_(0, top_x, current_hidden_states.to(hidden_states.dtype)) final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, hidden_dim) return final_hidden_states, router_logits 🚀 如果上边的代码看一遍不好理解的话，可以看一下下边的简化版本，结合debug的输出看更容易理解。\n下图中的代码代表了选择专家的简单逻辑，假设当前有10个token和4个专家，每个token选择2个专家。expert_mask输出的结果就是每个专家需要负责的token，我们拆开来看。\n在第一次遍历的过程中，我们遍历的是第一个专家，可以看到他是一个(2, 10)的矩阵，第一行代表了当前专家作为top1负责的token，第二行代表了当前专家作为top2负责的token。\n我们通过torch.where(expert_mask) 来进行解析这个结果，得到的idx 中的0代表了当前专家作为top1需要负责的token，1代表了当前专家作为top2需要负责的token，对应的top_x 则代表了当前专家负责的token的索引位置，将idx和top_x 组合就得到了当前专家作为top1、top2负责的token的索引，例如(0,2)、(0,8)、(0,9)、(1,0)、(1,1)、(1,5)，对应的意思就是当前专家作为top1负责的token索引为2、8、9，当前专家作为top2负责的token索引为0、1、5。\n更多阅读资料 Mistral AI官网对Mixtral 8x7B介绍\nhuggingface 混合专家模型 (MoE) 详解\n这篇文章提出的关于Mixtral 8x7B的几个问题很有意思，比如训练的时候几个专家同时训练、8个专家的贡献度怎么样\n","permalink":"https://kevinchen1994.github.io/posts/mixtral-8x7b-moe%E6%A8%A1%E5%9E%8B%E7%AC%94%E8%AE%B0/","summary":"随着 Mixtral 8x7B 的推出，一种称为混合专家模型 (Mixed Expert Models，简称 MoEs) 的 Transformer 模型在开源人工智能社区引起了广泛关注。","title":"Mixtral 8x7B MoE模型笔记"},{"content":"早上去上班走到小区门口，碰见一个朋友开车出去，他停车问我去哪里，我说朱辛庄地铁站，他说他去回龙观，要不要捎着我，我脑子里第一反应就是我去8号线，回龙观在13号线，不顺路，我就拒绝了。等跟他告别，我突然想到我需要换到13号线去望京西，如果坐他的车就不用换乘了，还不用走着挨冻。\n在去地铁的路上，我脑子就一直在想这个问题，这就是《思考快与慢》中的系统一搞的鬼，明明坐他的车更方便，也更快，我的系统一却没有思考到这里，而是直接做出了拒绝的决策。\n为什么会这样呢？再往深入想，那就是我的性格导致了系统一的决策。我一直不喜欢麻烦别人，麻烦别人我就会觉得不好意思，所以第一反应就是在别人提供帮助的时候直接拒绝。当然在我觉得我需要帮助的时候，我也会主动寻求帮助。\n还有一个原因，那就是我习惯安排好自己的行程，并很不喜欢被人打乱。说严重一点，在我的规划中，如果被人打乱了我可能会不知所措，甚至情绪低落。比如我每天早上会走到地铁站，然后在地铁站看公众号，到了公司班车会看B站视频，每天如此。那天他问我要不要捎我的时候，我脑子里闪过了如果坐他的车，我看公众号的时间就会缩短，而且还会进行一直“无意义”的聊天，这是我不可接受的，我不喜欢被人打乱了计划。\n系统一总是会做出这种短期看上去“更好”的决策，而长期来看，这个决策反而是错误的。\n如果改变这个问题呢？我觉得首先需要改变自己的思想，那就是先让自己改变自己的观念，接受一些生活中的不确定性，这样面对这些不确定性的时候就会启动系统二去进行深入思考，这件事情给我带来的是利还是弊。\n孟岩有一句咒语，“欢迎来到荷兰”。这个是一本书中的故事，讲的是一个人做好了去意大利旅行的计划，买了一大堆旅游指南，学习几句意大利语，结果下飞机的时候，机组人员对他说：欢迎来到荷兰。什么？我要去的是意大利啊，我怎么来到荷兰了？因为飞行计划有变，所以只能停在荷兰，而你只能既来之则安之。在荷兰，他重新购买了旅行指南，发现荷兰有风车、郁金香，并不比自己想象的意大利差。如果，你把生命都浪费在哀叹为什么没有去意大利这件事上，你永远都不能自由的享受荷兰带给你的美好了。\n所以勇于接受一些不确定性，带给你的可能不是一样的体验，甚至是更好的体验。\n第二个方法就是平时多锻炼自己的系统二，思考一些自己平时很少碰到的场景中，系统二会如何决策，那真正面对这个场景时，系统一自然就做出了提前演练的系统二的决策了。\n想到这里，我觉得我在读《思考快与慢》的时候读的太笼统了，很多细节都已经忘了，也没有做太多的笔记，后面我会重读一遍。\n","permalink":"https://kevinchen1994.github.io/posts/%E8%AE%A8%E5%8E%8C%E7%9A%84%E7%B3%BB%E7%BB%9F%E4%B8%80/","summary":"由一件生活中的小事，引发的对大脑中系统一的思考。","title":"讨厌的系统一"},{"content":"看了一些介绍tokenizer算法的文章，感觉大家介绍的时候并不是很系统，甚至有的文章还有一些错误，所以我在这篇文章做了一个汇总，没有个人原创的内容。\nBPE 💡 输入：训练语料； 词表大小V\n1.准备足够大的训练语料，确定期望的subword词表大小； 2.准备基础词表：比如英文中26个字母加上各种符号； 3.基于基础词表将语料中的单词拆分为字符序列并在末尾添加后缀“ \u0026lt;/ w\u0026gt;”；本阶段的subword的粒度是字符。例如单词“ low”的频率为5，那么我们将其改写为“ l o w \u0026lt;/ w\u0026gt;”：5； 4.统计每一个连续字节对的出现频率，选择最高频的字符对合并成新的subword； 5.重复第4步直到达到第1步设定的subword词表大小或下一个最高频的字节对出现频率为1；\n代码参考这篇文章，知乎几乎所有的代码都是来自于这里\nhttps://leimao.github.io/blog/Byte-Pair-Encoding/\n\u0026#39;\u0026#39;\u0026#39; https://leimao.github.io/blog/Byte-Pair-Encoding/ \u0026#39;\u0026#39;\u0026#39; import re, collections def get_vocab(filename): vocab = collections.defaultdict(int) with open(filename, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as fhand: for line in fhand: words = line.strip().split() for word in words: vocab[\u0026#39; \u0026#39;.join(list(word)) + \u0026#39; \u0026lt;/w\u0026gt;\u0026#39;] += 1 return vocab def get_stats(vocab): pairs = collections.defaultdict(int) for word, freq in vocab.items(): symbols = word.split() for i in range(len(symbols)-1): pairs[symbols[i],symbols[i+1]] += freq return pairs def merge_vocab(pair, v_in): v_out = {} bigram = re.escape(\u0026#39; \u0026#39;.join(pair)) p = re.compile(r\u0026#39;(?\u0026lt;!\\S)\u0026#39; + bigram + r\u0026#39;(?!\\S)\u0026#39;) for word in v_in: w_out = p.sub(\u0026#39;\u0026#39;.join(pair), word) v_out[w_out] = v_in[word] return v_out def get_tokens_from_vocab(vocab): tokens_frequencies = collections.defaultdict(int) vocab_tokenization = {} for word, freq in vocab.items(): word_tokens = word.split() for token in word_tokens: tokens_frequencies[token] += freq vocab_tokenization[\u0026#39;\u0026#39;.join(word_tokens)] = word_tokens return tokens_frequencies, vocab_tokenization def measure_token_length(token): if token[-4:] == \u0026#39;\u0026lt;/w\u0026gt;\u0026#39;: return len(token[:-4]) + 1 else: return len(token) def tokenize_word(string, sorted_tokens, unknown_token=\u0026#39;\u0026lt;/u\u0026gt;\u0026#39;): if string == \u0026#39;\u0026#39;: return [] if sorted_tokens == []: return [unknown_token] string_tokens = [] for i in range(len(sorted_tokens)): token = sorted_tokens[i] token_reg = re.escape(token.replace(\u0026#39;.\u0026#39;, \u0026#39;[.]\u0026#39;)) matched_positions = [(m.start(0), m.end(0)) for m in re.finditer(token_reg, string)] if len(matched_positions) == 0: continue substring_end_positions = [matched_position[0] for matched_position in matched_positions] substring_start_position = 0 for substring_end_position in substring_end_positions: substring = string[substring_start_position:substring_end_position] string_tokens += tokenize_word(string=substring, sorted_tokens=sorted_tokens[i+1:], unknown_token=unknown_token) string_tokens += [token] substring_start_position = substring_end_position + len(token) remaining_substring = string[substring_start_position:] string_tokens += tokenize_word(string=remaining_substring, sorted_tokens=sorted_tokens[i+1:], unknown_token=unknown_token) break return string_tokens # vocab = {\u0026#39;l o w \u0026lt;/w\u0026gt;\u0026#39;: 5, \u0026#39;l o w e r \u0026lt;/w\u0026gt;\u0026#39;: 2, \u0026#39;n e w e s t \u0026lt;/w\u0026gt;\u0026#39;: 6, \u0026#39;w i d e s t \u0026lt;/w\u0026gt;\u0026#39;: 3} vocab = get_vocab(\u0026#39;../data/pg16457.txt\u0026#39;) print(\u0026#39;==========\u0026#39;) print(\u0026#39;Tokens Before BPE\u0026#39;) tokens_frequencies, vocab_tokenization = get_tokens_from_vocab(vocab) print(\u0026#39;All tokens: {}\u0026#39;.format(tokens_frequencies.keys())) print(\u0026#39;Number of tokens: {}\u0026#39;.format(len(tokens_frequencies.keys()))) print(\u0026#39;==========\u0026#39;) num_merges = 10000 for i in range(num_merges): pairs = get_stats(vocab) if not pairs: break best = max(pairs, key=pairs.get) vocab = merge_vocab(best, vocab) print(\u0026#39;Iter: {}\u0026#39;.format(i)) print(\u0026#39;Best pair: {}\u0026#39;.format(best)) tokens_frequencies, vocab_tokenization = get_tokens_from_vocab(vocab) print(\u0026#39;All tokens: {}\u0026#39;.format(tokens_frequencies.keys())) print(\u0026#39;Number of tokens: {}\u0026#39;.format(len(tokens_frequencies.keys()))) print(\u0026#39;==========\u0026#39;) # Let\u0026#39;s check how tokenization will be for a known word word_given_known = \u0026#39;mountains\u0026lt;/w\u0026gt;\u0026#39; word_given_unknown = \u0026#39;Ilikeeatingapples!\u0026lt;/w\u0026gt;\u0026#39; sorted_tokens_tuple = sorted(tokens_frequencies.items(), key=lambda item: (measure_token_length(item[0]), item[1]), reverse=True) sorted_tokens = [token for (token, freq) in sorted_tokens_tuple] print(sorted_tokens) word_given = word_given_known print(\u0026#39;Tokenizing word: {}...\u0026#39;.format(word_given)) if word_given in vocab_tokenization: print(\u0026#39;Tokenization of the known word:\u0026#39;) print(vocab_tokenization[word_given]) print(\u0026#39;Tokenization treating the known word as unknown:\u0026#39;) print(tokenize_word(string=word_given, sorted_tokens=sorted_tokens, unknown_token=\u0026#39;\u0026lt;/u\u0026gt;\u0026#39;)) else: print(\u0026#39;Tokenizating of the unknown word:\u0026#39;) print(tokenize_word(string=word_given, sorted_tokens=sorted_tokens, unknown_token=\u0026#39;\u0026lt;/u\u0026gt;\u0026#39;)) word_given = word_given_unknown print(\u0026#39;Tokenizing word: {}...\u0026#39;.format(word_given)) if word_given in vocab_tokenization: print(\u0026#39;Tokenization of the known word:\u0026#39;) print(vocab_tokenization[word_given]) print(\u0026#39;Tokenization treating the known word as unknown:\u0026#39;) print(tokenize_word(string=word_given, sorted_tokens=sorted_tokens, unknown_token=\u0026#39;\u0026lt;/u\u0026gt;\u0026#39;)) else: print(\u0026#39;Tokenizating of the unknown word:\u0026#39;) print(tokenize_word(string=word_given, sorted_tokens=sorted_tokens, unknown_token=\u0026#39;\u0026lt;/u\u0026gt;\u0026#39;)) \u0026#39;\u0026#39;\u0026#39; Tokenizing word: mountains\u0026lt;/w\u0026gt;... Tokenization of the known word: [\u0026#39;mountains\u0026lt;/w\u0026gt;\u0026#39;] Tokenization treating the known word as unknown: [\u0026#39;mountains\u0026lt;/w\u0026gt;\u0026#39;] Tokenizing word: Ilikeeatingapples!\u0026lt;/w\u0026gt;... Tokenizating of the unknown word: [\u0026#39;I\u0026#39;, \u0026#39;like\u0026#39;, \u0026#39;ea\u0026#39;, \u0026#39;ting\u0026#39;, \u0026#39;app\u0026#39;, \u0026#39;l\u0026#39;, \u0026#39;es!\u0026lt;/w\u0026gt;\u0026#39;] \u0026#39;\u0026#39;\u0026#39; BBPE 对于英文来说，使用BPE能够在词表可控的前提下解决OOV的问题，但是对于中文、日文等语言时，稀有文字可能会不必要的占用词表大小。所以BBPE方法是将一本文本的UTF-8编码中的一个字节256位不同编码作为词表的初始化Subword。\n相比ASCII只能覆盖英文中字符，UTF-8编码创建的本身就是为了通用的将世界上不同的语言字符尽可能全部用一套编码进行编号，同时相比UTF-32对于每个字符都采用4位字节（byte）过于冗长。改进的UTF-8编码是一个变长的编码，有1～4个范围的字节(bytes)长度。对于不同语言中字符采用不同长度的字节编码，例如英文字符基本都是1个字节（byte），中文汉字通常需要2～3个字节。\n核心思想是用byte来构建最基础的词表而不是字符。首先将文本按照UTF-8进行编码，每个字符在UTF-8的表示中占据1-4个byte。 在byte序列上再使用BPE算法，进行byte level的相邻合并。编码形式如下。\n在解码阶段，一个byte序列可能解码后不是一个合法的字符序列，这里需要动态规划进行解码，使其能解码出尽可能多的合法字符。所以BBPE虽然解决了中文、日文词表过大的问题，但是解码上还是有一些问题。\nWordPiece 💡 输入：训练语料； 词表大小V\n1.准备足够大的训练语料，确定期望的subword词表大小； 2.准备基础词表：比如英文中26个字母加上各种符号； 3.基于基础词表将语料中的单词拆分为最小单元; 4.基于第3步数据训练语言模型，可以是最简单的unigram语言模型，通过极大似然进行估计即可; 5.从所有可能的subword单元中选择加入语言模型后能最大程度地增加训练数据概率的单元作为新的单元; 6.重复第5步直到达到第2步设定的subword词表大小或概率增量低于某一阈值;\nWordPiece算法跟BPE算法流程基本一致，BPE是基于最高词频来生成Subword，而WordPiece是基于概率来生成Subword。具体来说，有一个词为z，z由x和y组成，对比P(Z)的概率和P(X)+P(Y)的概率，如果P(Z)\u0026gt;P(X)+P(Y)，则合并x和y，因为他们具有最的互信息，也就是在语言模型上具有较强的关联性。\n我们假设句子S=(t_1,t_2,\u0026hellip;,t_n)，其中t_i代表一个子词，且假设各个子词之间是相互独立存在的，那么句子的语言模型似然值等价于所有子词概率的乘积。\n假设把相邻位置的x和y两个子词进行合并，合并后产生的新的字词为z，此时句子S的似然值变化可以表示为：\n从上面的公式可以发现，似然值的变化就是两个子词的互信息。简而言之，WordPiece每次选择合并的两个子词，他们具有最大的互信息值，也就是相关字词在语言模型上具有较强的关联性，他们经常在语料中以相邻的方式同时出现。\nUniLM 💡 输入：训练语料；词表大小V； 保留阈值X；\n1.准备基础词表：初始化一个很大的词表，比如所有字符+高频Ngram，也可以通过BPE算法初始化； 2.针对当前词表，用语言模型（unigram lm)估计每个子词在语料上的概率； 3.计算删除每个Subword后对总loss的影响及Score得分，作为该Subword排序的Score得分； 4.将子词按照Score大小进行排序，保留前X%的Subword；注意，建议单字符的Subword不能被丢弃，以免OOV； 5.重复步骤2到4，直到词表大小减少到设定值；\nUniLM可以看成是WordPiece算法在执行过程中进行反向操作。相比WordPiece借助语言模型选择合并计算概率最大的相邻字符对加入词表中作为新的Subword，UniLM是开始时先构建足够大的词表，之后每一步选择一定比例的计算概率低的Subword从词表中删除。因此过程中比较显著差别是WordPiece算法的词表在生成过程中是从小到大的变化，而UniLM的词表则是从大到小的变化，整个过程根据评估不断删除排序靠后的Subword直到词表大小减少到设定值。\nSentencePiece SentencePiece是Google推出的分词工具，这个包主要是为了多语言模型设计的，使用unicode编码，解决了多语言编码方式不同的问题。\n内置BPE，Unigram，char和word的分词方法 无需预分词，以unicode方式直接编码整个句子，空格会被特殊编码为▁ 相比传统实现进行优化，分词速度速度更快 中文llama模型使用的就是SentencePiece进行扩充中文词表。具体流程为：先使用大量的中文预料进行训练中文tokenizer模型，然后将中文模型与llama tokenizer模型进行合并，合并代码： https://github.com/ymcui/Chinese-LLaMA-Alpaca/blob/main/scripts/merge_tokenizer/merge_tokenizers.py\n参考文章 https://mp.weixin.qq.com/s/Sgz74bSs0saCFhw1GOaMnw\nhttps://zhuanlan.zhihu.com/p/86965595\nhttps://zhuanlan.zhihu.com/p/649030161\nhttps://zhuanlan.zhihu.com/p/651430181\nhttps://huggingface.co/docs/transformers/tokenizer_summary\n","permalink":"https://kevinchen1994.github.io/posts/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%88%86%E8%AF%8D%E7%AE%97%E6%B3%95/","summary":"大模型如何读懂自然语言，需要我们将文本转成机器能读懂的数字，这篇文章介绍了一些从文本到数字的算法。","title":"大模型的分词算法"},{"content":"前言 从小我们就被教育要养成好习惯，好习惯会使我们受益一生。然而，并没有人告诉我们如何才能养成好习惯，隐约记得初中班主任曾经教我们“21天养成习惯法”，并亲自做了实验，具体要养成的习惯是早起还是阅读，我记不清了，总之就是每天都打卡，连着21天，最终结果就是打卡21天。\n养成习惯之前先喜欢上它 从表面上看，很多所谓的好习惯都是违背人性的，都是大家不愿意做的事。之所以被称为好习惯，是因为他对你有好处，比如阅读、运动、健康的饮食等等。\n在想要养成一个习惯之前，我们能不能想清楚：我为什么要养成这个习惯，单纯是因为这个习惯是有收益的吗？如果是这样，并且你并不喜欢这这件事，那么你可能需要每天强迫自己去做这件事，每天都要克服内容的抵触感，这种感觉是不是很难受？\n那么我们是否能够找出在这件事中的一些乐趣点呢？以阅读为例，肯定不是所有的书我都读得下去，因为某些书太过于晦涩难懂，让我无法坚持。那么，我们可不可以先从一些轻松愉快的书籍开始阅读呢？比如选择一些小说，文笔轻松，内容愉快，读起来会很轻松。随着阅读的深入，当你感受到阅读的乐趣时，你可能会慢慢喜欢上这件事，从而能够坚持下去，养成习惯。\n当然，有的人可能认为这种方法是在欺骗自己，用一些容易的事情让自己坚持下去，然后称之为养成了好习惯。但是仔细想想，我们每个人身上的习惯，不都是从一些小事开始并坚持下来的吗？想要坚持的前提就是要去喜欢他，不然做起来太痛苦了，即使坚持了一段时间仍然有很高的概率放弃。我们可以通过一些简单快乐的事情先喜欢上这件事，就比如在阅读时读一些轻松快乐的书籍、在运动中选择自己容易上手并喜欢的运动、在健康饮食中选择自己喜欢的食材，从而坚持下来，养成习惯。\n消除“冷启动” 在携隐的即刻里谈到了读书怎么从一年20本到70本的，主要是因为读70本每天都会读一点，可以完全消除“冷启动”，让每次读书都没有了启动成本。\n结合我这两年阅读的体验，我有很深的感触。2022年我只读了大概10本书，坚持的很艰难，2023年大概是20本书，却很轻松。2022年我会想各种办法让自己去阅读，比如睡前到时间手机app全部不能使用，只能使用微信读书，把实体书放在书桌最显眼的位置上等等，但是这些方法对我的阅读都没有太大作用，所以很艰难的读完了10本书。到了2023年，我几乎没有强迫自己去读书，而是从一本容易读下去的《秋园》开始，读完后，我还觉得意犹未尽，于是又去读了这位作者的其他作品，这时候我已经是主动去找书读了。随着读的越来越多，每次在读完一本书或者打开一本新书时，都充满了激动和期待。每天开始阅读时，都是怀着愉快的心情，根本没有任何负面情绪。就这样，我养成了阅读的习惯，并且越读越多，越读越轻松。\n我是一个很容易有仪式感的人，比如在做某件事之前，我喜欢准备好所有相关的东西，而且必须是有条不紊的。如果发现某件东西没有准备好，那我做这件事的心态可能就没了。其实，这就是冷启动太困难的原因，需要万事俱备才能开始。虽然这样做事情会舒服一些，但是大多数情况下，我们是没办法准备好所有的东西的。而如果每次在做事之前，都要做大量的准备工作，那么我们的意志力可能在准备过程中就已经被耗尽了。\n人是习惯的动物，朝着一个方向前进刚开始很难，后续会越来越容易，但是当我们停下前进的脚步后，再启动就会很困难。因此，想办法消除冷启动的时间和流程，这样能让我们快速进入状态，节省意志力，从而更容易坚持下去，养成习惯。\n越是自然，越容易坚持 我们都应该经历过定闹钟，使用提示事项来提醒我们要去做某件事了，当然如果你的日程安排的很满，这确实是一个很好的办法，但是大多数时候我们是被动去做这件事，甚至是从另外一件事中将我们抽离出来，这就导致了我们的主动性不高，甚至会带有负面情绪，坚持下去也就不那么容易了。\n生物钟的概念大家都听说过，比如起床，我习惯了早上6点多钟起床去跑步，即使那天是休息日，我也会在那个时间醒来，所以我不需要针对早起定好几个闹钟叫我起床。大家可以想办法把要养成的习惯变成自己生活的一部分，就像生物钟一样，完全是主动的、自然的，到时候自己就会去做。\n如何做到自然其实需要你为这件事情做出一定的安排，还是用早起举例子，在我制定了我要6点钟起床跑步的计划后，我就需要计算我的睡眠，一个睡眠周期是1.5个小时，我需要睡够5个睡眠周期，也就是7.5个小时，反推过去就是我需要在晚上10点30分入睡。至于为什么使用睡眠周期来计算睡眠时长，是因为在一个睡眠周期结束后醒来的时候不会太痛苦，对大脑和身体也是有益处的，如果在睡眠周期中间醒来的话，是有可能对大脑造成伤害的，以后有时间我可以专门针对睡眠写一篇文章。现在制定好了睡眠计划，最难的部分是如何做到10点半就上床睡觉，如果一直玩手机的话肯定不想睡觉，同时手机的刺激也会让我难以入睡，所以我尽量减少睡前使用手机的时间。计划开始的时候我需要定闹钟，后来我一般会在闹钟响之前几分钟醒来，逐步做到了无痛早起。\n所以所谓自然就是指通过调整自己的生活状态和计划，让我们的计划融入到我们的生活中去，而不是在一切都不变的前提下强行插入事项，这样只会让你的生活越来越复杂，有时候做减法也是很好的。\n结语 这篇文章主要讨论了我在养成习惯上的一些想法，有一些是有科学依据的，有一些纯粹是个人经验，如果能对你有一些帮助是我最大的荣幸。在这个快节奏的时代，我希望自己能够有时间跟自己对话，在这个过程中，我不仅能够养成好的习惯，也能更好的认识自己，找到内心的平静和安宁。\n","permalink":"https://kevinchen1994.github.io/posts/%E5%A6%82%E4%BD%95%E5%9F%B9%E5%85%BB%E4%B9%A0%E6%83%AF/","summary":"从小我们就被教育要养成好习惯，好习惯会使我们受益一生。然而，并没有人告诉我们如何才能养成好习惯，本文从个人经验的角度去讨论了如何去养成习惯的方法。","title":"如何培养习惯"},{"content":"Fine-Tuning Pre-training后使用有标注的数据对模型进行微调，需要大量的有监督数据，预训练与微调之间存在gap，效果提升不够明显。代表模型EMLO、BERT、GPT1\nPrompt-Tuning系列 通过添加模版的方式，避免引入额外的参数，从而让语言模型能够在Zero-Shot，或者Few-Shot的场景下达到理想的效果。代表模型GPT3\nDiscrete Prompt（离散Prompt） 离散Prompt是指将离散字符与在原始文本进拼接，且在训练中保持不变，这里保持不变是指这些离散字符的词向量（Word Embedding）在训练过程中保持不变。通常情况下，离散法不需要引入任何参数。\nContinuous Prompt（连续Prompt） 连续Prompt是指让模型在训练过程中，根据具体的上下文语义和任务目标对模板参数进行连续可调。因为在离散训练中，模板无法参与模型的训练环境，容易陷入局部最优，而如果将模版变为可以训练的参数，那么不同的样本可以在连续的向量空间中寻找合适的伪标记，同时也可以增强模型的泛化能力。因此，连续法需要引入少量的参数让模型在训练时进行参数更新。\nPrefix Tuning （2021.01） 论文题目：Prefix-Tuning: Optimizing Continuous Prompts for Generation\n论文地址：_https://arxiv.org/pdf/2101.00190.pdf _\n论文源码：https://github.com/XiangLi1999/PrefixTuning\n背景：1. 人工设计的离散Prompt模板成本高，并且模型对模板特别敏感，多一个字少一个字都可能造成较大的变化，并且效果可能不是最优的；2. 传统微调需要针对每一个下游任务单独保存一份模型的权重，训练成本太高。\n基于此，Prefix Tuning提出了固定LM参数，为LM提供可训练的、特定任务的前缀，这样就可以针对不同的任务使用不同的前缀，并且也可以复用LM的参数了。其次，使用连续的Prompt，相比离散的Prompt效果更好。在实际使用中挑选任务相关的prefix与transformer进行组装，实现热插拔。\nprefix tuning可以应用在decoder-only的模型上，也可以应用在encoder-decoder模型上，但主要应用的任务是NLG任务。\nP-tuning （2021.03） 论文题目：GPT Understands, Too\n论文源码：https://github.com/THUDM/P-tuning\n论文地址：https://arxiv.org/pdf/2103.10385.pdf\n背景：人工构建prompt效率低，效果差，想通过自动化的构建模板而不调整模型参数。\n构建连续可微的虚拟token（与prefix-tuning类似），该方法将prompt转换为可以学习的embedding，但仅限于输入层，并没有像prefix-tuning一样在每一层Transformer都添加。\n另外还通过使用MLP+LSTM的方法对prompt embedding进行处理，加速训练。\nPrompt Tuning (2021.09) 论文地址：https://arxiv.org/pdf/2104.08691.pdf\n论文题目：The Power of Scale for Parameter-Efficient Prompt Tuning\n论文源码：https://github.com/google-research/prompt-tuning\n背景：有人提出了自动化在离散的空间中自动搜索prompt的技术，这种方法虽然优于人工设定的prompt，但是跟在连续空间搜索prompt仍有差距。\n固定整个模型参数，对于不同的任务，设定不同的前缀，这些前缀token是可以更新参数的，将不同的任务数据同时输入到模型中进行训练，可以理解prompt tuning是prefix tuning的简化版本。\n实验表明，随着模型参数的增加，prompt tuning的效果越来越好，但在小模型上效果不明显。\nP-tuning-v2 (2022.03) 论文题目：P-Tuning v2: Prompt Tuning Can Be Comparable to Finetuning Universally Across Scales and Tasks\n论文源码：https://github.com/THUDM/P-tuning-v2\n论文地址：https://arxiv.org/pdf/2110.07602.pdf\n背景：为了解决P-tuning和prompt tuning在小模型、跨类任务上效果不佳的问题，作者提出了P-tuning-v2\n相较于P-tuning v1，P-tuning v2将连续提示应用于预训练的每一层，而不仅仅是输入层。P-tuning v2与prefix tuning类似，不同的是prefix tuning应用于NLG任务，而P-tuning v2应用于NLU任务。\n通过增加prompt可调参数量（from 0.01% to 1%~3%），P-tuning v2提高了训练的性能。\nLoRA系列 LoRA（2021.11） 论文题目：LoRA: Low-Rank Adaptation of Large Language Models\n论文源码：https://github.com/microsoft/LoRA\n论文地址：https://arxiv.org/pdf/2106.09685.pdf\n背景：当前PEFT方法中，有增加模型深度导致增加了模型推理时间的，例如Adapter，有训练Prompt，同时减少了模型可用输入的，同时Prompt训练起来也比较难，例如Prompt tuning、Prefix tuning、P-tuning，这些方法的效果都差于full-finetuning。有研究者对语言模型的参数进行研究发现，语言模型虽然参数众多，但是起到作用的还是其中低秩的本质维度（Low instrisic dimension）。\nLora核心思想就是通过低秩分解来模拟参数的改变量，从而以极小的参数量来实现大模型的间接训练，在涉及到矩阵相乘的模块，在原始的PLM旁边增加一个新的通路，通过前后两个矩阵A,B相乘，第一个矩阵A负责降维，第二个矩阵B负责升维，中间层维度为r，从而来模拟所谓的本征秩（intrinsic rank）。\nh=W_0x+ \\triangle W_x=W_0x+BAx\n在训练的时候，LoRA一般只对每层的self-attention进行微调，即对W_q、W_k、W_v、W_o四个映射层进行微调，实验表明同时调整这四个映射层效果是最好的。在推理时，只需要将训练完成的矩阵BA乘积加到原始矩阵W即可，即h=Wx+BAx=(W+BA)x，不会增加额外的计算资源和推理时间。\n对于LoRA的秩取多大，论文中进行了实验，从实验结果来看，在秩极低（r=1）的情况下，对W_q、W_v微调就能获得与高秩相当的性能。\nAdaLoRA（2023.03） 论文题目：Adaptive Budget Allocation for Parameter-Efficient Fine-Tuning\n论文源码：https://github.com/QingruZhang/AdaLoRA\n论文地址：https://arxiv.org/pdf/2106.09685.pdf\n背景：LoRA需要预先指定每个增量矩阵的本征秩 r 相同，在微调预训练模型时，LoRA均匀地分配增量更新的预算到所有预训练权重矩阵上，并忽视了不同权重参数的重要性差异。\n所以AdaLoRA提出了动态调整增量矩阵，对于重要的增量矩阵分配比较大的r，对于不重要的增量矩阵分配比较小的r，防止过拟合，并且节省计算资源。\n以奇异值分解的形式对增量更新进行参数化，并根据重要性指标裁剪掉不重要的奇异值，同时保留奇异向量。由于对一个大矩阵进行精确SVD分解的计算消耗非常大，这种方法通过减少它们的参数预算来加速计算，同时，保留未来恢复的可能性并稳定训练。\n实验结果证明AdaLoRA效果好于LoRA，但是没好太多，个人认为方法是好方法，提升有限。\nQLoRA（2023.05） 论文题目：QLoRA: Efficient Finetuning of Quantized LLMs\n论文源码：https://github.com/artidoro/qlora\n论文地址：https://arxiv.org/pdf/2305.14314.pdf\n背景：量化方法可以显著减少LLM的内存占用，然而只限于在推理阶段，基于此，QLoRA提出了在不降低性能的前提下，微调量化为4bit的LLM。\n具体操作为将预训练模型量化到4bit，并且添加可以学习的低秩适配器权重，这些权重通过量化的反向传播梯度进行优化。QLoRA 有一种低精度存储数据类型（4 bit），还有一种计算数据类型（BFloat16）。实际上，这意味着无论何时使用 QLoRA 权重张量，我们都会将张量反量化为 BFloat16，然后执行 16 位矩阵乘法。QLoRA提出的一些新技术来实现了上述操作，具体为：\n4bit NormalFloat(NF4)：对于正态分布权重而言，一种信息理论上最优的新数据类型，该数据类型对正态分布数据产生比 4 bit整数和 4bit 浮点数更好的实证结果。 双量化：对第一次量化后的那些常量再进行一次量化，减少存储空间。 分页优化器：使用NVIDIA统一内存特性，该特性可以在在GPU偶尔OOM的情况下，进行CPU和GPU之间自动分页到分页的传输，以实现无错误的 GPU 处理。该功能的工作方式类似于 CPU 内存和磁盘之间的常规内存分页。使用此功能为优化器状态（Optimizer）分配分页内存，然后在 GPU 内存不足时将其自动卸载到 CPU 内存，并在优化器更新步骤需要时将其加载回 GPU 内存。 总结 📌 P-tuning v1与Prompt tuning是比较类似的方法\nP-tuning v1和Prompt tuning都是在输入层添加连续的虚拟token；\nP-tuning v1插入虚拟token的位置可以是前缀，也可以插入到中间；\nPrompt tuning是针对不同的任务设定不同的prompt，将prompt与特定任务的数据进行拼接作为输入；\nP-tuning v1将prompt使用MLP+LSTM的方式对prompt进行embedding处理，这里起到的作用的加速训练，Prompt tuning不需要使用MLP进行向量化；\n📌 P-tuning v2与Prefix tuning是比较类似的方法\nPrefix tuning和P-tuning v2都是在transformer的每一层添加虚拟token；\nPrefix tuning应用于NLG任务，而P-tuning v2应用于NLU任务;\nPrefix tuning可以应用在decoder-only模型，也可以应用在encoder-decoder模型，P-tuning v2只能应用在decoder-only模型；\n📌 LoRA系列\nLoRA通过引入低秩的旁路网络（增量矩阵）可获得与全量微调相当的性能，且极大降低训练显存依赖（将GPT-3可调参数减少10000倍，GPU内存需求减少3倍）；LoRA需要预先设置相同增量矩阵的秩，忽略了不同权重参数的重要性差异，AdaLora对这一问题进行改进，通过引入SVD技术，达到对参数矩阵自适应分配秩的效果，从而获得了相较Lora更优的性能；与AdaLoRA思路不同，QLoRA在LoRA基础上，对大模型基座进行4-bit量化，同时引入双量化和分页优化，极大减少训练显存依赖，且效果与16-bit全量微调相当，QLoRA将65B Llama模型的显存需求从大于780G降低到小于48G，AdaLoRA和QLoRA可看作是对LoRA不同的两种改进方式。\n参考文献 https://mp.weixin.qq.com/s/E_0-skD3__w5jLGEJlDpoA\nhttps://mp.weixin.qq.com/s/webUB5j8nNQsthTFQNiqpA\nhttps://mp.weixin.qq.com/s/8A7aLiknSDCBfMuUKg4eiw\nhttps://zhuanlan.zhihu.com/p/636215898\n","permalink":"https://kevinchen1994.github.io/posts/peft/","summary":"对于大模型微调起来不仅需要很多计算资源（GPU），还需要大量的训练时间，PEFT通过高效微调模型，使得我们可以使用单卡去训练比较大的LLM。","title":"PEFT"},{"content":"开发Python一直是在用PyCharm，对VS Code了解不太多，不过在一些视频里见过不少大神是在用VS Code的。PyCharm很强，能满足我所有的需求，有两点我不太满意的点，一是需要续费，或者破解（不支持这种做法），二是有点笨重，不够轻量化。于是我开始尝试免费、开源、轻量化的VS Code，上手还好，基本看一些介绍基本功能都了解的差不多了，可能有些快捷键需要适应一下。VS Code的插件很丰富，使用起来还是很方便的，最重要的是真的轻量级，使用起来就很干净的感觉，同时我把配置云端同步，保证家里的开发环境跟公司的保持一致，这点很棒。\n这篇文章主要记录我使用过程中遇到的一些问题，使用过程中遇到新的问题会及时同步到这里。\n常用快捷键 移动行：option+↑↓\n复制当前行：shift+option+↑↓\n删除当前行：shift+command+k\n代码格式化：shift+option+f（推荐使用ruff插件）\n对import进行排序：shift+optin+o（前提是安装isort插件）\n新建一个窗口（不是新建文件，是新开一个VS Code）：shift+command+n\n导入上一级目录的包 project/ common common_script.py main_script.py utils/ helper_module.py 这种方法会报错，找不到common这个包\n# 如果我想在helper_module.py使用common下的common_script.py from common import common_script 所以需要将上级目录配置到sys.path中\n# 如果我想在helper_module.py使用common下的common_script.py import sys sys.path.append(\u0026#39;..\u0026#39;) from common import common_script 这种方法在任何地方都适用，但是在VS Code中仍会报错，这是因为需要找到根目录的包，VS Code并没有进行配置，需要在settings.json中进行配置，添加。\n} \u0026#34;terminal.integrated.env.osx\u0026#34;: { \u0026#34;PYTHONPATH\u0026#34;: \u0026#34;${workspaceFolder}\u0026#34;, }, \u0026#34;terminal.integrated.env.linux\u0026#34;: { \u0026#34;PYTHONPATH\u0026#34;: \u0026#34;${workspaceFolder}\u0026#34;, }, \u0026#34;terminal.integrated.env.windows\u0026#34;: { \u0026#34;PYTHONPATH\u0026#34;: \u0026#34;${workspaceFolder}\u0026#34;, }, \u0026#34;code-runner.runInTerminal\u0026#34;: true, \u0026#34;python.terminal.executeInFileDir\u0026#34;: true, } 如果是debug模型，文件运行默认是根目录，还是有可能找不到相对路径，那就需要在launch.json中进行配置，将cwd配置当前路径。\n{ \u0026#34;name\u0026#34;: \u0026#34;Python: 当前文件\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;python\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, \u0026#34;program\u0026#34;: \u0026#34;${file}\u0026#34;, \u0026#34;console\u0026#34;: \u0026#34;integratedTerminal\u0026#34;, \u0026#34;stopOnEntry\u0026#34;: true, \u0026#34;cwd\u0026#34;: \u0026#34;${fileDirname}\u0026#34; } 使用参数进行debug 需要在运行-配置中进行配置，配置文件为launch.json\n如果想调试的文件为根目录下的test3.py\nname为你调试配置的名称，理论上来说不需要按照下边这么写，取个名区分的名字就行；\nprogram是要调试的文件，如果只有一个文件的话，默认${file}即可，就可以运行当前文件；\nargs是我们的配置，数据结构为一个list，在里边填上参数名称和参数值；\n{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息，请访问: https://go.microsoft.com/fwlink/?linkid=830387 \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;${workspace}/test3.py\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;python\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, // \u0026#34;program\u0026#34;: \u0026#34;${file}\u0026#34;, \u0026#34;program\u0026#34;: \u0026#34;test3.py\u0026#34;, \u0026#34;console\u0026#34;: \u0026#34;integratedTerminal\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;--device\u0026#34;, \u0026#34;cpu\u0026#34;, \u0026#34;--output_path\u0026#34;, \u0026#34;../test3\u0026#34; ] } ] } 如果要调试的为多个文件，也需要在同一个launch.json中进行配置\n这时候只需要把name定义好，program分别写文件的相对路径即可。想运行哪个文件行，在当前文件窗口界面点击debug选择对应的name就行。\n{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息，请访问: https://go.microsoft.com/fwlink/?linkid=830387 \u0026#34;version\u0026#34;: \u0026#34;0.2.0\u0026#34;, \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;${workspace}/test3.py\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;python\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, // \u0026#34;program\u0026#34;: \u0026#34;${file}\u0026#34;, \u0026#34;program\u0026#34;: \u0026#34;test3.py\u0026#34;, \u0026#34;console\u0026#34;: \u0026#34;integratedTerminal\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;--device\u0026#34;, \u0026#34;cpu\u0026#34;, \u0026#34;--output_path\u0026#34;, \u0026#34;../test3\u0026#34; ] }, { \u0026#34;name\u0026#34;: \u0026#34;${workspace}/test2.py\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;python\u0026#34;, \u0026#34;request\u0026#34;: \u0026#34;launch\u0026#34;, // \u0026#34;program\u0026#34;: \u0026#34;${file}\u0026#34;, \u0026#34;program\u0026#34;: \u0026#34;test2.py\u0026#34;, \u0026#34;console\u0026#34;: \u0026#34;integratedTerminal\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;--device\u0026#34;, \u0026#34;cpu\u0026#34;, \u0026#34;--output_path\u0026#34;, \u0026#34;../test2\u0026#34; ] } ] } 创建新文件后生成文件头 通过首选项中的用户代码片段实现，选择python文件后，在配置文件中添加想要的代码头。\n{ // Place your snippets for python here. Each snippet is defined under a snippet name and has a prefix, body and // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the // same ids are connected. // Example: // \u0026#34;Print to console\u0026#34;: { // \u0026#34;prefix\u0026#34;: \u0026#34;log\u0026#34;, // \u0026#34;body\u0026#34;: [ // \u0026#34;console.log(\u0026#39;$1\u0026#39;);\u0026#34;, // \u0026#34;$2\u0026#34; // ], // \u0026#34;description\u0026#34;: \u0026#34;Log output to console\u0026#34; // } \u0026#34;HEADER\u0026#34;:{\u0026#34;prefix\u0026#34;: \u0026#34;header\u0026#34;, \u0026#34;body\u0026#34;: [ \u0026#34;# -*- coding: utf-8 -*-\u0026#34;, \u0026#34;\u0026#39;\u0026#39;\u0026#39;\u0026#34;, \u0026#34;@File : $TM_FILENAME\u0026#34;, \u0026#34;@Time : $CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND\u0026#34;, \u0026#34;@Author : chenmeng\u0026#34;, \u0026#34;@Desc : None\u0026#34;, \u0026#34;\u0026#39;\u0026#39;\u0026#39;\u0026#34;, \u0026#34;\u0026#34; ], } } 配置好以后，创建新文件后，输入header，会自动生成配置好的文件头。\n自动生成python main方法 通过首选项中的用户代码片段实现，选择python文件后，添加对应配置即可。也可以通过插件实现，不过我配置了代码生成插件，所以不想安装其他的代码提示插件，就选择这种方法了。\n{ // Place your snippets for python here. Each snippet is defined under a snippet name and has a prefix, body and // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the // same ids are connected. // Example: // \u0026#34;Print to console\u0026#34;: { // \u0026#34;prefix\u0026#34;: \u0026#34;log\u0026#34;, // \u0026#34;body\u0026#34;: [ // \u0026#34;console.log(\u0026#39;$1\u0026#39;);\u0026#34;, // \u0026#34;$2\u0026#34; // ], // \u0026#34;description\u0026#34;: \u0026#34;Log output to console\u0026#34; // } \u0026#34;HEADER\u0026#34;:{\u0026#34;prefix\u0026#34;: \u0026#34;header\u0026#34;, \u0026#34;body\u0026#34;: [ \u0026#34;# -*- coding: utf-8 -*-\u0026#34;, \u0026#34;\u0026#39;\u0026#39;\u0026#39;\u0026#34;, \u0026#34;@File : $TM_FILENAME\u0026#34;, \u0026#34;@Time : $CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND\u0026#34;, \u0026#34;@Author : chenmeng\u0026#34;, \u0026#34;@Desc : None\u0026#34;, \u0026#34;\u0026#39;\u0026#39;\u0026#39;\u0026#34; ], }, \u0026#34;PYMAIN\u0026#34;:{ \u0026#34;prefix\u0026#34;: \u0026#34;pymain\u0026#34;, \u0026#34;body\u0026#34;: [ \u0026#34;if __name__ == \u0026#39;__main__\u0026#39;:\u0026#34;, \u0026#34;\u0026#34;, ], \u0026#34;description\u0026#34;: \u0026#34;python–main\u0026#34; } } ","permalink":"https://kevinchen1994.github.io/posts/vs-code%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/","summary":"由PyCharm转到VS Code，记录一些使用方法以及在使用过程中遇到的问题，持续更新。","title":"VS Code使用记录"},{"content":"前言 最近在部门做了一次GPT系列语言模型的分享，这次分享受众很大一部分没有算法背景，所以这篇文章想用讲故事的方式给大家介绍GPT系列语言模型，可以称为无技术技术分享。\n想必大家最近都被ChatGPT刷屏了吧，那为什么ChatGPT这么智能呢？其实最主要的还是因为他的底座模型很强大，所谓RLHF都是为了让底座模型学习到人类的交互方式，让它更好的相应人类的指令。那接下来我会沿着GPT发布的时间顺序分别给大家介绍一下这几个模型。\nTransformer 梦开始的地方 首先我们先简单介绍一下Transformer，因为GPT中的T就是指Transformer，这是大模型梦开始的地方。\n2017年，Google发表了一篇论文《Attention is all you need》，这篇文章主要讲他们设计了一个新的网络结构，以往我们使用的网络结构基本就是卷积神经网络（CNN）、循环神经网络（RNN），而Transformer只使用了self-attention机制，基于seq2seq结构设计了一种新的网络。\n所谓seq2seq结构就是网络具有编码器和解码器，编码器就是将我们的输入进行编码，提取特征，而解码器就是对编码进行解析，输出结果，如果是做中英翻译任务的话，输入就是中文，然后编码器对中文进行编码，解码器会对编码进行解码，输出就是对应的英文。这是整个Transformer的结构图，左边是编码器，右边是解码器。\n什么是 self-attention 呢，翻译过来就是自注意力，自己跟自己算 attention，具体的做法就是将输入转换成三个向量，分别是K、Q、V，然后K与Q进行点乘、缩放，再经过 softmax 以后得到一个权重矩阵，在与V相乘，V拿到的就是加权后的向量，也就是他该关注哪里，不该关注哪里都计算好了。这样的好处就是可以忽略文本之间的距离问题，直接计算全局的特征。举一个简单的例子，一段文本中说：小明是一个好学生，他上课认真听讲，下课好好做作业，然后巴拉巴拉说了一堆，最后说他真值得我们学习。这句话在计算self-attention 的时候，小明这个名字就与最后的他这个字关联性很高，即使他们之间的距离很远，Transformer也关注到了他就是小明，如果使用的是RNN，这么远的距离可能会遗忘掉小明这个名字。\n所谓多头自注意力，就是我们有多对KQV，他们会分别进行attention，之间互不干扰，这样我们就能拿到多种特征，从而提高了模型的特征抽取能力，并且多头自注意力之间还是并行计算的，也就是能够更好的利用 GPU 资源，提高我们的计算效率。\n具体细节大家不需要过多关注，只需要了解Transformer相比于之前的CNN和RNN，不仅特征抽取能力强、还并行度高，还能更好的关注全局信息，减少学到后边忘了前边的这种错误，解决了长距离依赖的问题。\n基于这些能力，使我们训练像GPT和BERT这种大模型成为了可能。\nBERT与GPT1 既生瑜何生亮 在2018年openAI发布了GPT1，采用的方法是使用Transformer-decoder的架构，也就是只使用了Transformer的解码器，可以通过左边的图看出来，GPT1的训练过程是单向的，因为GPT1的训练方法是单字接龙，也就是根据上文预测下文，从这个例子大家可以看到，通过输入的招字，GPT1预测聘字，然后通过招聘预测饿字，以此类推直到预测结束。通过这种训练方式，GPT1能够学习到文本之间的语义知识。一般我们称这种模型为自回归模型，他更适用于生成类的任务。\n紧接着Google发布了BERT，大家对BERT可能或多或少都听说过，我们很多工作都是基于BERT去做的，BERT是基于Transformer-encoder架构设计的，也就是只使用了Transformer的编码器，从图中可以看出来，BERT的训练过程是双向的。因为encoder可以看到输入文本上下文的全部信息，所以需要在训练时mask掉一些字，因此BERT的训练方法为完形填空，让模型根据上下文进行预测mask掉的字。一般我们称这种模型为自编码模型，他在自然语言理解任务上会有不错的效果。\n通过对比我们可以看到两个模型设计是很像的，只不过互为对立面，一个使用的是encoder，一个使用的是decoder。我们再来看一下两个模型的参数，由于GPT1是先发布的，所以BERT完全按照GPT1的参数量进行了设计，都是12层transformer，768维的隐藏层和12个自注意力头。那这两个模型的参数量几乎一致的，他们俩当时都刷新了各大NLP任务的榜单，所以我给这一节的标题起名为既生瑜何生亮。\nGPT1与BERT的使用方法都分为两个阶段，第一阶段都是在大规模无标注语料上进行预训练，也就是pre-training，就是我们刚才提到的单字接龙和完形填空，这阶段的目的就是通过预训练获取无标注语料中的语义知识。这一步Google和openAI都替我们训练好了，我们可以直接下载他们开源的模型，第二阶段是根据不同的下游任务，使用我们自己的有标注的数据进行有监督训练，也就是fine-tuning。这样的做的目的就是利用在预训练阶段模型学习到的语义知识，通过fine-tuning把这部分知识迁移到下游任务中。\n下图为BERT的使用方法，根据不同的任务，在BERT后添加不同的网络层就可以了，在输入部分通过添加特殊标识符[CLS]表示句子的向量，如果是双句任务，通过[SEP]来分割两个句子。例如做分类任务，直接拿到BERT的输出，添加一个分类层就可以；序列标注任务中，我们可以拿到每一个字的输出，在通过分类器进行判断标签即可。\n下图为GPT1的使用方法，使用方法也很类似，通过特殊标识符标识整个句子，双句任务按照特殊标识符进行分割，输入给模型，在拿到模型的输出后，拼接一层线性层就可以拿到预测结果。\n我们来看一下实验结果。\nBERT-base为与GPT1参数规模相同的模型，可以看到在多项NLP任务中，他的成绩都优于GPT1。\nGPT1的效果比之前的方法都要好，但是由于BERT的训练方法为完形填空，所以学习到的语义知识更多，因此在下游任务中的表现都是优于GPT1的。所以在此以后，BERT大火了起来，成为了我们最强的baseline，无论什么任务，我们优先都会去尝试BERT的效果怎么样，我们一度以为BERT才是未来的方向，当然后面的故事大家都清楚了。\nGPT2 无监督学习者 在BERT发布以后，openAI痛定思痛，决定继续深挖自回归模型的潜力，于是在2019年GPT2发布了，论文名称为Language Models are Unsupervised Multitask Learners，语言模型是无监督的多任务学习者。主打的是Zero-Shot learning，所谓Zero-Shot learning就是指在预训练完成后，不需要任何标注的数据，直接对下游任务进行预测。openAI这次想体现的是模型泛化能力强。\n想要做的Zero-Shot，那前提就是模型一定要强，所以openAI加大了参数量和训练数据，参数量提升到了1.5B，也就是15亿的参数，相比于GPT1和BERT提升了13倍多，训练数据也由之前的4GB提升到了40GB。通过实验对比，在Zero-Shot的设定下，GPT2在多个任务上能够吊打绝大部分模型，当然也有一些任务表现没有那么好。\n使用生成式的模型如何进行预测呢？在GPT1中因为使用了微调的方法，所以在输入文本中添加了一些特殊符号，这样模型可以记住这些符号，而在GPT2中，我们直接进行预测，并没有这些特殊符号，所以使用方式为直接写出需要模型做的任务，模型自动识别并输出结果，翻译任务：(translate to french, english text, french text)；阅读理解任务：(answer the question, document, question, answer)。这种方式也称为prompt，也就是提示模型他需要做什么，prompt的目的其实就是为了激发模型的补全能力，我们的prompt作为模型的输入，让模型输出我们想要的东西。后来这种方式成为了大模型主要的使用方法。\nGPT3 大模型的顿悟时刻 实际使用场景下，大家其实都能提供一些标注数据，人类在学习的时候有一些样本学习的也会很好，于是openAI继续深挖，发布了GPT3，论文名称为Language Models are Few-Shot Learners。所谓Few-Shot就是指训练完成后，给我少量的标注数据，但是不进行微调，只是让我看几个例子，我就能帮你做下游任务。\n这次GPT3的参数量到了175B，也就是1750亿参数，相较于GPT2提升了100多倍，训练数据到了570GB。在这也样数据量和参数量下，GPT3展示了什么叫大力出奇迹。除了在很多下游任务上有不错的表现以外，还解锁了其他的能力。\nIn-Context Learning 什么是In-Context Learning呢，简单来说，就是模型在不更新自身参数的情况下，通过在模型输入中带入新任务的描述与少量的样本，就能让模型”学习”到新任务的特征，并且对新任务中的样本产生不错的预测效果。这种能力可以当做是一种小样本学习能力。可以参考下图的例子来理解：在预测阶段，在输入中输入几个任务样例，这样模型就能在不调整模型参数的情况下学习到该任务需要解决什么问题，你再问类似的问题，模型就会输出正确的答案。\n我们可以看一下实验结果，In-Context Learning在大模型上随着标注样本的增多，最多也就几十条，准确率能够稳定提升，这里大模型指千亿参数以上，在小模型上虽然也有提升，但是效果没有大模型效果明显，所以这个能力是大模型独有的，所以这里我们称其为大模型的顿悟时刻。\nchain of thought 我们再来讲一下大模型解锁的另外一个能力，chain of thought，也就是思维链。在一般的prompt中，对于一个复杂或者需要多步计算的问题，如果你直接问模型，他大概率会直接给你一个错误答案，例如左下角这个例子。如果你给出样例，但是没有提供推导过程，模型也很难给出正确答案，例如左上角这个例子，绿色是给了一个样例，然而在继续问一个新的问题的时候，模型直接输出了结果，但是结果错误了。而右上角这个图中展示了，我给了你一个样例，还给了你推导过程，再问你一个新问题的时候，模型不仅能输出解题过程，还能计算正确。这就是思维链的能力，让模型具有了像人类处理复杂问题一样的思维能力。\n甚至还有人提出了一种方法，在问问题的时候，不告诉模型推导过程，只是跟模型说一句，Let’s think step by step，模型就能给出推导过程和正确答案，这妥妥的模型鼓励师了，鼓励模型不要着急，一步一步想，模型就能给出正确的答案。\n能力涌现 In-Context Learning 和 chain of thought 的能力是怎么来的呢？目前学术界也没有确定的答案，现在大家称这种现象为大模型的Emergent Abilities，也就是能力涌现。一般模型的大小和模型的能力是有固定比例的，一般是称线性关系，即模型越大能力越强，但是当模型的规模到了一定的程度以后，像是GPT3、chatGPT这种千亿级别的大模型，模型的能力会有一个质的变化，也就是能力涌现。\n可以通过实验结果图来看一下，横轴为模型的计算量，我们可以理解为模型参数量越大，模型的计算量也就越大，纵轴为模型在不同任务上的表现。随着模型规模变化，模型的能力开始是一点点提升的，有的任务甚至是一条水平线，但是当模型的规模到了一定的程度，模型的能力突然就提升了很多。这也许就是大力出奇迹吧。\nGPT3.5 守得云开见月明 GPT3已经能帮我们做很多事情了，但远没有到chatGPT那么强，我们知道chatGPT是基于GPT3.5进行训练得来的，chatGPT之所以这么强，是因为他有一个强有力的底座模型，也就是GPT的守得云开见月明的时刻。GPT3.5并不单单指一个模型，而是一系列的模型，通过openAI官网我们可以看到，这一系列模型分别擅长不同的任务，有的善于理解代码，有的善于执行人类命令，有的善于聊天。\n目前openAI没有公开GPT3.5具体是多大规模和怎么训练的，不过通过模型擅长的任务，GPT3.5可能不仅仅增大了模型参数量和训练数据量，还加入了代码的训练数据，并且加入了instruction tuning。\n加入代码训练数据我们很好理解，让模型能够读懂代码，能够帮助我们生成代码和修改代码bug，还有一些研究表明在加入了代码训练以后，模型的逻辑推理能力能够有效的增强，原因可能是因为代码都是逻辑比较清晰的，模型通过学习代码，从而增强了自己的逻辑推理能力。\n那什么是instruction tuning呢？我们先来回顾一下BERT、GPT1的使用方法，也就是fine-tuning，针对不同的下游任务，使用不同的标注数据对模型进行微调，此时模型的参数是进行调整的。\n在GPT2和GPT3中，因为模型参数量太大了，一般人也没有那么多的计算资源去优化，也很有可能越训练模型效果越差，所以模型的参数是不允许被修改的，大家是通过prompt也就是提示去命令模型帮助我们解决问题，prompt的目的其实就是为了激发模型的补全能力，我们的prompt作为模型的输入，让模型输出我们想要的东西。\ninstruction tuning直接翻译就是指令学习，是指使用将有标注的数据集使用自然语言描述的方式对模型参数进行微调，可以使用多个不同任务的数据集对模型进行指令学习，这样做的目的不是为了让模型学习到标注数据中的知识，因为在预训练阶段模型已经学习到了充足的知识，这样做是为了让模型能够更好的响应人类的指令，从而能做出正确的反馈。\n当然GPT3.5还有使用到instructGPT的训练方法和强化学习的训练方法，这里我们不做介绍。\nGPT4 更高更快更强 就在3月15号，openAI发布了GPT4，这里我们需要弄清楚GPT4与之前的模型都不一样，因为GPT4不单单指一个模型了，他对标的是chatGPT，是一套系统，相较于chatGPT他更加强大和安全了。\n由于openAI没有公布GPT4具体的参数和训练方法，所以这里我们简单介绍一下GPT4强大在哪里。\n首先是多模态，GPT4支持文本和图片的输入，输出只支持文本，你可以通过输入图片让他解释这张图里都有什么，也可以给他一张搞笑图片让他给你解释为什么好笑，这里体现了GPT4强大的常识知识，也就是他会有类似人类的认知，知道一张搞笑图片搞笑在哪里。\n再就是更强大的编程能力，通过发布会的视频，我们看到了一个例子，演示者通过手绘了一张网页的草图输入给GPT4，GPT4很快就生成了这个网页的源代码，拿过来可以直接进行部署。他还支持你描述一个游戏的玩法，他去给你生成游戏的源代码。\nGPT4支持更长的输入，chatGPT最长是4000个token，当你们之间的对话长度超过这个值，他就会丢失一些上文的信息，从而输出的内容可能会不符合你的要求，GPT4直接升级到了3.2万个token，大约25000个字，所以能更好的支持对轮对话的内容。\nGPT4有了更强大的处理复杂问题的能力，在普通的对话中可能体现不出来GPT4与chatGPT之间的区别，但是在复杂问题上，比如数学问题，或者需要多步推理的问题，GPT4的能力是显著优于chatGPT的。\nopenAI还用GPT4参加了人类的考试，在美国的高考、律师从业资格证都能取得前10%的成绩，而chatGPT参加这些考试的成绩在后10%-30%。\nGPT4的多语言能力也得到了提升，除了英语，在一些小语种中都是优于chatGPT的。这里说一下我个人的之前的一个错误观点，因为在GPT3的训练数据中中文的占比仅为0.1%，我猜测chatGPT中中文的训练数据也不会太多，所以我一直以为在处理非英语问答时，chatGPT内部是不是翻译成了英文再去处理的，后来听了微软亚洲研究院周明博士的一个观点我觉得可能是正确的，他说世界的语言之间其实都是可以对齐的，模型在接受多语言训练数据时，他内部能够对齐不同语言之间的关系，也就是他即使不用翻译也能理解各种语言是什么意思，这可能就是GPT4为什么能很好处理多语言的原因吧。\n最后就是openAI一直强调的，GPT4更加安全可控了，GPT4在去年8月份就训练好了，在今年3月份才发布，用一个比喻来说就是openAI这半年一直在驯服这头能力强大的野兽。他们用了基于人类反馈的强化学习让模型输出更符合人类要求的内容，尽量避免有毒的、性别歧视、种族歧视、黄暴等内容的生成。\n总结 openAI不忘初心 我们总结一下吧，通过下面的表格，我们可以更清晰的看到这些模型之间的区别和他们这几年的发展路线。我们可以看到openAI一直沿着他们最初的想法在做，不断把模型做深做大，用到的数据越来越多，训练资源越来越大，当然他们的效果也越来越好，不断在打破我们的认知，希望我们国内也能早点做出能与之抗行的模型。\n最后请把不忘初心打在公屏上。\n参考文献 [1] Vaswani A , Shazeer N , Parmar N , et al. Attention Is All You Need[J]. 2017.\n[2] Devlin J , Chang M W , Lee K , et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding[J]. 2018.\n[3] Radford A, Narasimhan K, Salimans T, et al. Improving language understanding by generative pre-training[J]. 2018.\n[4] Radford A, Wu J, Child R, et al. Language models are unsupervised multitask learners[J]. OpenAI blog, 2019, 1(8): 9.\n[5] Brown T, Mann B, Ryder N, et al. Language models are few-shot learners[J]. Advances in neural information processing systems, 2020, 33: 1877-1901.\n[6] Kojima T, Gu S S, Reid M, et al. Large language models are zero-shot reasoners[J]. arXiv preprint arXiv:2205.11916, 2022.\n[7] Wei J, Tay Y, Bommasani R, et al. Emergent abilities of large language models[J]. arXiv preprint arXiv:2206.07682, 2022.\n[8] Wei J, Bosma M, Zhao V Y, et al. Finetuned language models are zero-shot learners[J]. arXiv preprint arXiv:2109.01652, 2021.\n[9] openAI GPT4\n[10] Fu, Yao; Peng, Hao and Khot, Tushar. 拆解追溯 GPT-3.5 各项能力的起源. 2022\n","permalink":"https://kevinchen1994.github.io/posts/gpt%E7%B3%BB%E5%88%97%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/","summary":"科普，无技术技术分享，像讲故事一样根据时间线来介绍GPT1到GPT4。","title":"GPT系列语言模型"},{"content":" 👨🏻‍💻自我介绍 👋嗨，我是Kevin，一个自然语言算法算法工程师💻，更是一个热爱生活的编程少年🧑‍💻，INFJ-A。\n坐标北京，互联网行业，喜欢电影 🎬、阅读 📚、跑步 🏃，关注互联网行业🌐、科技行业🚀、新能源汽车行业🚗以及FIRE运动🔥。\n这个博客名称是因为我喜欢《Breaking Bad》(绝命毒师)，主角就叫老白，绰号海森堡，同时他在剧里从事化学制毒，行话cook，所以我就取名为厨师海森堡的厨房，单纯是为了搞怪。\n这个博客我会总结一些技术相关话题，平时的感想，杂七杂八的话题，这里就是我的自留地了。\n如果你对我或者我写的东西感兴趣的话，欢迎交流📮346521888@qq.com，即刻。\n","permalink":"https://kevinchen1994.github.io/about/","summary":"\u003c!-- raw HTML omitted --\u003e\n\u003ch2 id=\"自我介绍\"\u003e👨🏻‍💻自我介绍\u003c/h2\u003e\n\u003cp\u003e👋嗨，我是Kevin，一个自然语言算法算法工程师💻，更是一个热爱生活的编程少年🧑‍💻，INFJ-A。\u003c/p\u003e\n\u003cp\u003e坐标北京，互联网行业，喜欢电影 🎬、阅读 📚、跑步 🏃，关注互联网行业🌐、科技行业🚀、新能源汽车行业🚗以及FIRE运动🔥。\u003c/p\u003e\n\u003cp\u003e这个博客名称是因为我喜欢《Breaking Bad》(绝命毒师)，主角就叫老白，绰号海森堡，同时他在剧里从事化学制毒，行话cook，所以我就取名为厨师海森堡的厨房，单纯是为了搞怪。\u003c/p\u003e\n\u003cp\u003e这个博客我会总结一些技术相关话题，平时的感想，杂七杂八的话题，这里就是我的自留地了。\u003c/p\u003e\n\u003cp\u003e如果你对我或者我写的东西感兴趣的话，欢迎交流📮346521888@qq.com，\u003ca href=\"https://web.okjike.com/u/A4F616A2-54CF-4B8A-BD6E-ECE51D8C02D3\"\u003e即刻\u003c/a\u003e。\u003c/p\u003e","title":"About"}]