-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 195 KB
/
content.json
1
{"meta":{"title":"ANdRoid's BLOG","subtitle":"MaTRix","description":"","author":"Android","url":"https://zade23.github.io","root":"/"},"pages":[{"title":"关于","date":"2024-03-14T03:05:45.153Z","updated":"2024-03-14T03:05:45.153Z","comments":false,"path":"about/index.html","permalink":"https://zade23.github.io/about/index.html","excerpt":"","text":"Android,游戏行业算法学徒,现居广东 见贤思齐焉,见不贤则亦自省"},{"title":"标签","date":"2023-05-18T08:07:04.950Z","updated":"2023-05-18T08:07:04.950Z","comments":false,"path":"tags/index.html","permalink":"https://zade23.github.io/tags/index.html","excerpt":"","text":""},{"title":"Repositories","date":"2023-05-18T03:53:31.018Z","updated":"2023-05-18T03:53:31.018Z","comments":false,"path":"repository/index.html","permalink":"https://zade23.github.io/repository/index.html","excerpt":"","text":""},{"title":"分类","date":"2023-05-18T08:06:17.850Z","updated":"2023-05-18T08:06:17.850Z","comments":false,"path":"categories/index.html","permalink":"https://zade23.github.io/categories/index.html","excerpt":"","text":""}],"posts":[{"title":"【随笔】如何克服拖延症?","slug":"如何克服拖延症?","date":"2024-12-17T03:47:30.000Z","updated":"2024-12-17T07:10:10.479Z","comments":true,"path":"2024/12/17/如何克服拖延症?/","link":"","permalink":"https://zade23.github.io/2024/12/17/%E5%A6%82%E4%BD%95%E5%85%8B%E6%9C%8D%E6%8B%96%E5%BB%B6%E7%97%87%EF%BC%9F/","excerpt":"","text":"为什么会拖延? 处理方法 调动情绪 大佬们的建议 总结 相关参考 为什么会拖延?大部分情况下的拖延因素来源于“畏难情绪”。 通常,被拖延的任务要么是重要的任务,要么是比较困难的任务。面对这样的任务,我们会在大脑中放大这个问题的困难程度,放大的原因在于:不熟悉。 除了拖延症,此前张朝阳还谈起过如何摆脱焦虑的困扰。他介绍称:“在心理学上有一个像物理学里的牛顿定律一样的定律,就是当我们想要去解决焦虑的时候,焦虑就会被放大。所以面对焦虑的时候不要回避,不去解决。而去接受、无视。” 处理方法调动情绪第一步,收集信息。 信息收集的过程是熟悉任务的过程。在收集的同时,和自己进行“对话” “完成这个事是需要这个、需要那个……”“这个跟那个之间有什么关系……” 通过这样的方式,让自己的大脑神经元被充分调动,让自己excited。当情绪调动起来了,一鼓作气的将它完成。 大佬们的建议 不做过度的准备(不需要找个舒服环境再开始工作。先完成,再完美,完善的过程问题会越来越多,最终杀死了灵感和信心) 最难的事情,提前做(难做的事情放在早上先做) 不要中途休息(最好不要有思维的中断,有思路的事情一鼓作气解决) 不要抱有太强的目标感(经历只能参考,不能复制。在过程中见招拆招,遇到问题解决问题) 事情一件一件做(按照难易程度划分,从难往易一件一件解决) 拖延也要有度(有最晚启动时间底线) 总结深度学习领域大牛李沐老师,在上海交通大学进行讲话就提到了:“找到强烈的动机。” 在这里同时分享一句我个人很喜欢的一句话(同样出自李沐老师):“一个很好的创业人或者CEO,他一定是理想主义者兼实践者。” 我的理解是:坚信自己最终的理想是正确的,同时脚踏实地一步一步干出成绩来。 下面的这句话有同样的含义: “尽早确立人生价值观,无论什么情绪来了,都按照价值观坚定的、积极的生活和工作。” ——张朝阳 一句话:找到你内心最想做的事情,追随你的内心。 相关参考 张朝阳谈摆脱拖延的方法:拖延的原因在于不熟悉 杰出系友专访|@BosonAI李沐:找到强烈的动机","categories":[],"tags":[]},{"title":"【DSA】LeetCode_Hot100","slug":"【DSA】LeetCode-Hot100","date":"2024-10-31T02:34:32.000Z","updated":"2024-10-31T03:28:56.597Z","comments":true,"path":"2024/10/31/【DSA】LeetCode-Hot100/","link":"","permalink":"https://zade23.github.io/2024/10/31/%E3%80%90DSA%E3%80%91LeetCode-Hot100/","excerpt":"","text":"","categories":[{"name":"数据结构和算法","slug":"数据结构和算法","permalink":"https://zade23.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"}],"tags":[{"name":"LeetCode","slug":"LeetCode","permalink":"https://zade23.github.io/tags/LeetCode/"}]},{"title":"模型训练库缺失 ImportError: No xformers","slug":"模型训练库缺失-ImportError-No-xformers","date":"2024-07-03T12:03:21.000Z","updated":"2024-07-03T12:08:56.675Z","comments":true,"path":"2024/07/03/模型训练库缺失-ImportError-No-xformers/","link":"","permalink":"https://zade23.github.io/2024/07/03/%E6%A8%A1%E5%9E%8B%E8%AE%AD%E7%BB%83%E5%BA%93%E7%BC%BA%E5%A4%B1-ImportError-No-xformers/","excerpt":"","text":"发现问题借助Lora训练Diffusion模型的时候,出现了报错: ImportError: No xformers 分析问题安装对应版本的Transformer模型即可解决问题,参考:https://github.com/Akegarasu/lora-scripts/issues/422#issuecomment-2127945481 解决问题找到当前运行训练任务的机器,确定对应的版本: 1234import torchpirnt(torch.__version__)>>> 2.3.1 + cu121 从输出定位 torch版本 和 cuda版本,从 https://github.com/facebookresearch/xformers?tab=readme-ov-file#installing-xformers 找 torch + cuda + xformers 三者关系都能对应上的版本。 12# cuda 12.1 versionpip3 install -U xformers --index-url https://download.pytorch.org/whl/cu121 安装成功,问题解决。","categories":[{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"}],"tags":[{"name":"conda","slug":"conda","permalink":"https://zade23.github.io/tags/conda/"},{"name":"environment","slug":"environment","permalink":"https://zade23.github.io/tags/environment/"}]},{"title":"【阅读】Ilya的论文阅读清单","slug":"【阅读】Ilya的论文阅读清单","date":"2024-05-27T06:35:39.000Z","updated":"2024-05-27T07:01:34.542Z","comments":true,"path":"2024/05/27/【阅读】Ilya的论文阅读清单/","link":"","permalink":"https://zade23.github.io/2024/05/27/%E3%80%90%E9%98%85%E8%AF%BB%E3%80%91Ilya%E7%9A%84%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB%E6%B8%85%E5%8D%95/","excerpt":"","text":"背景介绍 主要内容 核心神经网络创新 专业神经网络应用 理论见解和原则性方法 跨学科和概念研究 效率和可扩展性技术 教材和教程 背景介绍Ilya Sutskever 的论文清单:30u30 Ilya Sutskever 是 Hinton 的大弟子,OpenAI的联合创始人兼首席科学家。 以下是他推荐的论文清单,他认为阅读完这些内容之后就可以了解AI领域90%的内容 主要内容核心神经网络创新 Recurrent Neural Network Regularization - Enhancement to LSTM units for better overfitting prevention.递归神经网络正则化 - 增强了 LSTM 单元,以更好地防止过拟合。 Pointer Networks - Novel architecture for solving problems with discrete token outputs. Pointer Networks - 用于解决离散令牌输出问题的新颖架构. Deep Residual Learning for Image Recognition - Improvements for training very deep networks through residual learning. 用于图像识别的深度残差学习 - 改进了通过残差学习训练非常深度的网络。 Identity Mappings in Deep Residual Networks - Enhancements to deep residual networks through identity mappings. 深度残差网络中的身份映射 - 通过身份映射增强深度残差网络。 Neural Turing Machines - Combining neural networks with external memory resources for enhanced algorithmic tasks. 将神经网络与外部内存资源相结合,以增强算法任务。 Attention Is All You Need - Introducing the Transformer architecture solely based on attention mechanisms. Attention Is All You Need - 介绍完全基于注意力机制的 Transformer 架构。 专业神经网络应用 Multi-Scale Context Aggregation by Dilated Convolutions - A convolutional network module for better semantic segmentation. Multi-Scale Context Aggregation by Dilated Convolutions - 一个卷积网络模块,用于更好的语义分割. Neural Machine Translation by Jointly Learning to Align and Translate - A model improving translation by learning to align and translate concurrently. Neural Machine Translation by Joint Learning to Align and Translation - 一种通过学习同时对齐和翻译来改进翻译的模型. Neural Message Passing for Quantum Chemistry - A framework for learning on molecular graphs for quantum chemistry. Neural Message Passing for Quantum Chemistry - 量子化学分子图学习框架. Relational RNNs - Enhancement to standard memory architectures integrating relational reasoning capabilities.Theoretical and Principled Approaches 关系 RNN - 增强了集成关系推理功能的标准内存架构。理论和原则方法 Deep Speech 2: End-to-End Speech Recognition in English and Mandarin - Deep learning system for speech recognition. Deep Speech 2: End-to-End Speech Recognition in English and Mandarin -用于语音识别的深度学习系统. ImageNet Classification with Deep CNNs - Convolutional neural network for classifying large-scale image data. ImageNet Classification with Deep CNNs - 用于对大规模图像数据进行分类的卷积神经网络. Variational Lossy Autoencoder - Combines VAEs and autoregressive models for improved image synthesis. 变分有损自动编码器 - 结合 VAE 和自回归模型以改进图像合成。 A Simple NN Module for Relational Reasoning - A neural module designed to improve relational reasoning in AI tasks. 用于关系推理的简单神经网络模块 - 旨在改进 AI 任务中关系推理的神经模块. 理论见解和原则性方法 Order Matters: Sequence to sequence for sets - Investigating the impact of data order on model performance. Order Matters: Sequence to sequence for sets - 调查数据顺序对模型性能的影响。 Scaling Laws for Neural LMs - Empirical study on the scaling laws of language model performance. Scaling Laws for Neural LMs -语言模型性能的缩放规律的实证研究. A Tutorial Introduction to the Minimum Description Length Principle - Tutorial on the MDL principle in model selection and inference. 最小描述长度原则教程简介 - 模型选择和推理中的MDL原理教程. Keeping Neural Networks Simple by Minimizing the Description Length of the Weights - Method to improve neural network generalization by minimizing weight description length. 通过最小化权重的描述长度来保持神经网络的简单性 - 通过最小化权重描述长度来提高神经网络泛化的方法。 Machine Super Intelligence DissertationMachine Super Intelligence Dissertation - Study on optimal behavior of agents in computable environments. 机器 超级 智能 论文Machine Super Intelligence Dissertation - 研究智能体在可计算环境中的最优行为。 PAGE 434 onwards: Komogrov Complexity - Comprehensive exploration of Kolmogorov complexity, discussing its mathematical foundations and implications for fields like information theory and computational complexity. 第434页起:科莫格罗夫复杂性 - 全面探索柯尔莫戈罗夫复杂性,讨论其数学基础以及对信息论和计算复杂性等领域的影响。 跨学科和概念研究 Quantifying the Rise and Fall of Complexity in Closed Systems: The Coffee Automaton - Study on complexity in closed systems using cellular automata. Quantifying the Rise and Fall of Complexity in Closed Systems: The Coffee Automaton - 使用元胞自动机研究封闭系统中的复杂性. 效率和可扩展性技术 GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism - A method for efficient training of large-scale neural networks. GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism - 一种高效训练大规模神经网络的方法. 教材和教程 CS231n: Convolutional Neural Networks for Visual Recognition - Stanford University course on CNNs for visual recognition. CS231n:用于视觉识别的卷积神经网络 - 斯坦福大学视觉识别 CNN 课程。 The Annotated Transformer - Annotated, line-by-line implementation of the Transformer paper. Code is available here. The Annotated Transformer - Transformer 论文的带注释的逐行实现.代码可在此处获得。 The First Law of Complexodynamics - Blog post discussing the measure of system complexity in computational terms. The First Law of Complexodynamics - 讨论计算术语中系统复杂性度量的博客文章. The Unreasonable Effectiveness of RNNs - Blog post demonstrating the versatility of RNNs. RNN 的不合理有效性 - 展示 RNN 多功能性的博客文章. Understanding LSTM Networks - Blog post providing a detailed explanation of LSTM networks. 了解 LSTM 网络 - 博客文章提供了 LSTM 网络的详细说明。","categories":[],"tags":[]},{"title":"【随笔】关于个人成长的思考","slug":"【随笔】关于个人成长的思考","date":"2024-05-10T03:05:52.000Z","updated":"2024-05-10T07:26:17.273Z","comments":true,"path":"2024/05/10/【随笔】关于个人成长的思考/","link":"","permalink":"https://zade23.github.io/2024/05/10/%E3%80%90%E9%9A%8F%E7%AC%94%E3%80%91%E5%85%B3%E4%BA%8E%E4%B8%AA%E4%BA%BA%E6%88%90%E9%95%BF%E7%9A%84%E6%80%9D%E8%80%83/","excerpt":"","text":"标题相关内容未想好,就先空着吧。 本篇内容是一篇关于学习、成长、阶级跨越的思考…… 起因是这一篇父亲写给女儿的信《阶层跨越》 。 上一次阅读是在2022年,当时也正是因为这篇内容关注了作者。 就在昨天重新整理知乎关注的过程中,重新理解了一下这篇文章,感觉和上一次阅读有了完全不一样的体会。 上一次阅读的时候我还是研三在读,对于工作、领导、责任的感受不深。 现如今已经工作两年,对于之前不理解的内容和当时“感觉已经理解”的内容由有了不一样的体会。 也是。 屁股决定脑袋。 如果以后再看,可能还会有更不同的看法吧。 观点陈述 得益于作者清晰的条理,AI总结的结果也非常清晰。 我通过这些年的经历结合上面提到的论点做出一些说明。 情绪稳定人们常说的情绪稳定是面对打击和挫折时候的情绪。上面作者提到的“正确面对批评和挫折”,同样在今年黄仁勋于斯坦福的讲话中也举例自己年轻时候的端盘子经历以此来鼓励各位学子勇往直前锲而不舍。 我想说点和他们不同的,是我从自己身上找到的问题:乐极生悲 比如对于一些事情过度的期待。如果得不到成果还好,而如果得到了就会沾沾自喜和自己给自己思想放假(“这段时间我真辛苦,休息一下吧”),殊不知很多事情的成功伴随着团队/平台/导师等一些列因素。 这样的后果就是,每一次的小阶段成功之后会有一段时间的“产出真空期”,这是和我对自己设立的阶段目标严重不符的。 正确的做法应该是:理性客观的总结自己在事件和项目中的贡献,分析自己的改变,为什么可以做到这些改变,自己还有哪些不足并应该如何改正…………短暂的整理心情(平复心情)后,重新上路,追求下一个目标。 如范仲淹在《岳阳楼记》中所说:“不以物喜不以己悲” 支持同事和领导在研究生期间,导师给了我一个算法证明相关的课题。 众所周知,证明 + 算法。看似是非常高大上的课题,实则是枯燥乏味且很难获得成就感和反馈的,我很长一段时间都在枯燥的推公式和自我怀疑中度过,不可否认有一大部分的根源来自于能力不足。 在2019-2021年期间,正是AI相关应用爆发的时间段,也眼馋实验室别的同学随便蹭一蹭AI相关就能发文章的情况,就想找导师更换我自己的课题。 结果当然是失败了。 于是就开启了长达两年的纠结过程,做不爱做的课题,并且觉得导师太不通情达理了,自己开始做和课题无关的研究并且在宿舍摆烂(好孩子不要学) 现在想来,是自己在沟通和处理问题方面不成熟。 如果现在让重新和导师沟通,我会这么做:和导师商量一个关于该课题的最低标准(发几区、发几篇),然后表示出自己真正感兴趣的领域,并表面在该领域自己也可以发文章(这是沟通必要条件)。 当时做错的是逃避了沟通,在“负责任”和“支持领导”这两方面都没有成熟思考。 口才和表达再延伸一点,那就是:争取露脸机会 在高校和公司这样的地方,成熟的架构有成百上千号人,大佬们没有办法记住所有人。 最好的办法就是争取露脸机会。 争取到机会之后,勇敢去表现自己的能力和技术水准。 在技术人员中,有很大一部分的人是不太擅长表述的,他们的工程能力非常突出,但是表达的通常比较啰嗦。 前段时间听到过一个观点:人生就是一个巨大的推销过程。 做汇报、述职、做报告,其实就包含了推销自己的能力这个过程。 为什么在AI火爆的那段时间内,讲好故事的公司能够拿到融资,那是因为讲好故事就是一种“夸张”的推销表达。 未来希望自己可以培养的能力:口才 + 表达 + 把握机会的能力 做事能力和思维能力做事能力:业务能力(基本) + 时间管理 + 团队合作(协作和沟通) 思维能力:超出当前层级的视角 + 学习领导能力 多年后重新看这篇文章,最区别的感受是要对于“整体架构”的把控。 原来自己觉得,只用把自己领域做大做强做精尖就可以了。 现在觉得这其实是很困难的,互联网的知识更新换代非常的快,永远学不完这个领域里所有的知识。 比较正确的做法应该是:掌握自己领域内最扎实的能力(业务能力) + 扩展知识的广度。 比如,要设计web端的AI应用,就需要了解前后端的相关技术,是否应该前后端分离,如果涉及到存储,数据库的选型应该是MySQL这种关系型数据库还是NoSQL类的非关系型数据库……对于上下游工作的了解能够让你和上下游更好的协调沟通(如果愿意的话,做个全栈也未尝不可)。 并且,对各方面技术栈的了解对于未来想做管理也是有帮助的。 待补充","categories":[],"tags":[]},{"title":"【Git】在README.md中展示视频的方案","slug":"【Git】在README-md中展示视频的方案","date":"2024-04-26T03:53:42.000Z","updated":"2024-04-26T03:58:20.162Z","comments":true,"path":"2024/04/26/【Git】在README-md中展示视频的方案/","link":"","permalink":"https://zade23.github.io/2024/04/26/%E3%80%90Git%E3%80%91%E5%9C%A8README-md%E4%B8%AD%E5%B1%95%E7%A4%BA%E8%A7%86%E9%A2%91%E7%9A%84%E6%96%B9%E6%A1%88/","excerpt":"","text":"内容介绍 使用说明 github gitlab blog支持的html格式 参考链接 内容介绍在 GitHub 和 GitLab 仓库中,通过在 README.md 中展示视频Demo的方式直观的展示项目的效果。 最后,加上一种HTML形式渲染视频的方案,效果如下: 使用说明github 注意:视频不能超过10m GitHub 上用将视频提交到 issues 中,此时视频文件将上传到GitHub中并获得一个文件链接,这个链接放置在README中可以被直接渲染成视频。 上传视频文件至某个 GitHub 项目的 issues 中 复制链接,放置在README中 gitlabGitLab 可以直接用Markdown图片链接的格式渲染已存放在仓库或 issues 中的视频 用图片链接的格式直接嵌入视频。支持格式为 .mp4、.m4v、.mov、.webm 和 .ogv 修改下面代码中视频文件的相对路径 123Here's a sample video:![Sample Video](img/markdown_video.mp4) 嵌入链接同样可以是已经提到issues中的视频文件链接 blog支持的html格式先采用提issues的方式或者上传至仓库的方式将文件上传。 将下面链接中 src 内的部分改成你的链接即可 1<video src="movie.mp4.mp4" controls="controls" width="500" height="300"></video> 参考链接 极狐文档中心-Markdown-视频 GitHub 仓库的 README 直接播放视频","categories":[],"tags":[{"name":"Git","slug":"Git","permalink":"https://zade23.github.io/tags/Git/"}]},{"title":"【Git】一个流畅的开源贡献流程","slug":"【Git】一个流畅的开源贡献流程","date":"2024-04-25T04:03:15.000Z","updated":"2024-04-28T03:28:58.250Z","comments":true,"path":"2024/04/25/【Git】一个流畅的开源贡献流程/","link":"","permalink":"https://zade23.github.io/2024/04/25/%E3%80%90Git%E3%80%91%E4%B8%80%E4%B8%AA%E6%B5%81%E7%95%85%E7%9A%84%E5%BC%80%E6%BA%90%E8%B4%A1%E7%8C%AE%E6%B5%81%E7%A8%8B/","excerpt":"","text":"由本人的有道云笔记搬运到个人网站,创作日期:2023年3月20日 教练……我想开源 github开源仓库工作流 如果我们想进行开源贡献 Paddle项目 文档贡献指南 第一步:Fork仓库并Clone到本地加载 第二步:Fork仓库并Clone到本地加载 第三步:下载pre-commit工具(下载过就不用再下了) 第四步:单次修改并提交都要运行pre-commit进行单元测试 提交:commit 第五步:push 第六步:review & merge 小插曲:CheckPRTemplate 一定要规范 Gradio项目 参考链接 教练……我想开源一直想参加一个开源项目,丰富自己GitHub账号头衔的同时还能学到一些知识。目前定位到了两个自己非常感兴趣的项目 (国内项目)Paddle:百度飞桨深度学习框架 (国际项目)Gradio:用Python构建机器学习网页APP在项目主页的 Issues 提出自己的问题 github开源仓库工作流 参考《A successful Git branching model》Remote(main/):所有人都共享的代码仓库 如果我们想进行开源贡献 第一步就是建立一个“feature branch”。目前在Local上,main branch 和 feature branch 上面的内容都是一致的。当使用checkout命令后,硬盘会同步git指定branch下所有的源文件(切换就会改变硬盘当下空间内部的文件)如何正确提Issues 在提Issues之前,一定要提前用关键字搜索看一下你想提出的问题是否已经存在。确定不存在后再进行提问 Paddle项目文档贡献指南以最近的一次合并提交为例:本次提交是添加了Tensor文件夹下的十个说明文档。 第一步:Fork仓库并Clone到本地加载12git clone https://github.com/<USERNAME>/docscd docs 第二步:Fork仓库并Clone到本地加载docs 目前使用 Git 流分支模型进行开发,测试,发行和维护。所有的 feature 和 bug fix 的开发工作都应该在一个新的分支上完成,一般从 develop 分支上创建新分支。使用 git checkout -b 创建并切换到新分支。 12git status #查看一下当前分支目录是否cleangit checkout -b <创建并切换到的分支名称> 第三步:下载pre-commit工具(下载过就不用再下了)一种检查模板格式是否匹配项目格式的预提交工具。 12pip install pre-commitpre-commit install 第四步:单次修改并提交都要运行pre-commit进行单元测试 通过指令 git status 进行状态的检查 通过 git add 将内容添加进准备提交的区域 上面的状态空间中显示我 9 次修改中有 1 次进入了“暂存区”,此时就要使用pre-commit指令进行单元测试 上面在运行了pre-commit之后,除了红色以外区域显示是通过的部分。红色部分说明没有通过单元测试,这个报错说明Linux系统和Win系统的换行符不统一导致的格式问题,一般这种情况下pre-commit会自己进行修复,所以重新提交就可以解决 上面在进行了一次性的提交之后,pre-commit一次性将所有的文件进行了检查和修改,之后再进行一次 git add –update 就可以将更新修改后的文件合并至文件的缓存区内。 解决步骤如上 在进行一次提交之后就可以看到所有单元测试已经通过了 在单元测试全部通过之后,可以正式填写提交说明了: 上面我提出的说明是“修改文档”。显示有 9 个文件被修改了, 20 行新增代码, 22 行删减代码。 提交:commit之所以要运行 pre-commit 就是要为后面要执行的commit操作避免提前的冲突和麻烦 1git commit -m "fix/add xxxx" 第五步:push先确保已经同步过原始仓库的最新代码 https://github.com/PaddlePaddle/docs 12345678910111213141516171819202122232425➜ git remoteorigin➜ git remote -vorigin https://github.com/USERNAME/docs (fetch)origin https://github.com/USERNAME/docs (push)# origin 是 clone 的远程仓库的名字,也就是自己用户名下的 Paddle,接下来创建一个原始 Paddle 的仓库远程主机,命名为 upstream➜ git remote add upstream https://github.com/PaddlePaddle/docs➜ git remote>> origin>> upstream-----------------------------------之前提交过了就可以跳到这一步---------------------------------------------# 获取 upstream 的最新代码并更新当前分支# 从 upstream 远程仓库获取最新的代码和提交历史,将本地仓库与远程仓库保持同步。➜ git fetch upstream # 从 upstream 远程仓库 develop 分支中获取最新的代码和提交历史,并将其自动合并到当前本地分支。➜ git pull upstream develop # Push 到远程仓库:也就是将本地修改推送到 GitHub 即: https://github.com/USERNAME/docs# 将本地分支的提交推送到名为origin的远程仓库中的特定分支(<my-pr>)。# 通过执行此命令,您可以将本地分支的修改上传到远程仓库,使得其他协作者可以查看和访问您的提交。➜ git push origin <my-pr(第二步中取的名字)> 第六步:review & merge 我的第一次提交 https://github.com/PaddlePaddle/docs/pull/5747几点小的总结: 尽量把自己在做的过程中不确定的内容进行说明 把疑问的地方进行复现 每一轮的回复都要进行礼貌性的回答 尽量把每一版的修改都进行分点的说明,第一做了XXX,第二做了XXXX…… 小插曲:CheckPRTemplate 一定要规范如果希望 Paddle 能够接受你的提交,至少需要满足以下三点: 单元测试通过 —— Docs-NEW 文档PR格式检测通过 —— CheckPRTemplate 同意签署开源贡献者协议 —— license/cla一般Docs-NEW可能需要比较长的时间进行检测,偶尔也需要排队等待。所以这次我以为 CheckPRTemplat 也是一样需要等,但是等了整整一天时间……最后发现其实PR格式不是特别正确(虽然也有人格式不正确但通过了PR格式检测),最终参考 checkprtemplate 。修改后的格式如下: 修改之后秒过。 Gradio项目一位同学进行的一个非常标准的开源参与流程: 提Issues。在这个过程中以项目管理者角度出发,顺带说明了四点问题: 是否已存在类似Issue。I have searched to see if a similar issue already exists.(我已经搜索过是否已经存在类似问题。) 描述你的问题和想添加的功能。Is your feature request related to a problem? Please describe.(您的功能请求是否与问题相关?请描述。) 提出解决方案。Describe the solution you’d like.(描述您想要的解决方案) 是否有前情提要。Additional context.(附加上下文) 请求合并Merge。在这个过程中以项目管理者角度出发,顺带说明了四点问题: relevant motivation.(相关动机) a summary of the change.(总结你做出的更改) which issue is fixed.(解决了Issue中的哪一个) any additional dependencies that are required for this change.(在本次变更中是否有其他依赖项) 参考链接 git commit之后,想撤销commit 1git reset --soft HEAD^ 视频链接 【如何正确地提github issue?开源项目作者来和你聊聊这个重要技能】 【十分钟学会正确的github工作流,和开源作者们使用同一套流程】 【freeCodeCamp | 如何在 GitHub 提交第一个 pull request】 AIstudio上持久化安装pip包 提前运行 cd ~ && mkdir loca_data && ln -s local_data.local 十分钟学会正确的github工作流,和开源作者们使用同一套流程 一套开源项目和部分企业都在使用的工作流","categories":[{"name":"Git Workflow","slug":"Git-Workflow","permalink":"https://zade23.github.io/categories/Git-Workflow/"}],"tags":[{"name":"Git","slug":"Git","permalink":"https://zade23.github.io/tags/Git/"}]},{"title":"【Unity】破解版安装指北","slug":"【Unity】破解版安装指北","date":"2024-04-22T13:27:41.000Z","updated":"2024-04-22T13:42:52.171Z","comments":true,"path":"2024/04/22/【Unity】破解版安装指北/","link":"","permalink":"https://zade23.github.io/2024/04/22/%E3%80%90Unity%E3%80%91%E7%A0%B4%E8%A7%A3%E7%89%88%E5%AE%89%E8%A3%85%E6%8C%87%E5%8C%97/","excerpt":"","text":"介绍 版本选择 使用说明 踩坑 介绍个人版的Unity安装通过Unity官网一步一步进行即可。今天我们聊的是:如何安装Unity的完整破解版。 如果没有许可的Unity项目在重新登录之后会提示如下界面: 在这个状态下,左右两个选项都无法正常启动项目,那么就需要考虑使用个人版或者破解Unity了。 注意:以下内容仅供用于非营利性的教育和测试目的。 版本选择我当前使用的版本参数如下: Unity:2022.3.9f1 Unity Hub:3.4.2 关于Unity破解版这里用到以下两个用于破解软件的链接: https://github.com/tylearymf/UniHacker https://github.com/agentbillwh/unilic 两个GitHub工程的对应Unity项目的破解关系是: UniHacker → Unity Hubunilic → Unity 正常情况下UniHacker可以同时破解 Unity 和 Unity Hub,由于UniHacker可以破解的Unity版本不能高于2022.2所以我们另外使用unilic破解Unity。 这样破解就可以使用较新的Unity版本了。 使用说明上述链接中的两个工程都是非常详细的工程,按照READ.md内容一步一步来即可,UniHacker更是打包成了一个.exe文件供用户直接破解。 UniHacker的破解结束后有两个选项: 是否登录自己的账号:建议否 是否保持更新:建议否 破解成功后,可打开原始项目。 踩坑 Unity Hub 版本不能太高 如果只破解了Unity而没有破解Unity Hub(或Unity Hub的版本过高,如图中3.7版)则会在打开项目的时候反复弹窗确认登录信息。","categories":[{"name":"Unity","slug":"Unity","permalink":"https://zade23.github.io/categories/Unity/"}],"tags":[{"name":"Game Engine","slug":"Game-Engine","permalink":"https://zade23.github.io/tags/Game-Engine/"}]},{"title":"【NLP】RWKV对比Transformer","slug":"【NLP】RWKV对比Transformer","date":"2024-04-16T03:25:31.000Z","updated":"2024-04-16T09:01:32.603Z","comments":true,"path":"2024/04/16/【NLP】RWKV对比Transformer/","link":"","permalink":"https://zade23.github.io/2024/04/16/%E3%80%90NLP%E3%80%91RWKV%E5%AF%B9%E6%AF%94Transformer/","excerpt":"","text":"模型的精度问题int4? int8? bf16? bf32? float16? float32? 模型参数文件常见的 generation_config.json 文件 下面的内容来自 Baichuan2-7B-Chat 模型生成器参数文件 1234567891011121314{ "pad_token_id": 0, "bos_token_id": 1, "eos_token_id": 2, "user_token_id": 195, "assistant_token_id": 196, "max_new_tokens": 2048, "temperature": 0.3, "top_k": 5, "top_p": 0.85, "repetition_penalty": 1.05, "do_sample": true, "transformers_version": "4.29.2"} 下面的内容来自 RWKV/v5-Eagle-7B-HF 模型生成器参数文件 123456789101112{ "chat_format": "chatml", "eos_token_id": 0, "pad_token_id": 0, "max_window_size": 4096, "max_new_tokens": 4096, "do_sample": true, "top_k": 0, "top_p": 0.1, "repetition_penalty": 1.0, "transformers_version": "4.31.1"} 半精度?全精度? 指令微调? chat模型?HF模型? 微调方式指令微调?全量/半精度/零样本?","categories":[],"tags":[]},{"title":"【NLP】RWKV框架入门","slug":"【NLP】RWKV框架入门","date":"2024-04-15T03:56:48.000Z","updated":"2024-04-25T07:00:53.080Z","comments":true,"path":"2024/04/15/【NLP】RWKV框架入门/","link":"","permalink":"https://zade23.github.io/2024/04/15/%E3%80%90NLP%E3%80%91RWKV%E6%A1%86%E6%9E%B6%E5%85%A5%E9%97%A8/","excerpt":"","text":"全文从以下几个方面展开: RWKV是什么 架构组成 特殊性分析 为什么选型RWKV做大语言基底模型的预研 RWKV和Transformer的差别 结构建模 Self-Attention Multi-Head Attention RWKV结构 效果表现(7B) 结语 RWKV是什么RWKV架构是一种针对分布式存储系统的架构设计,主要关注于提高数据的可靠性、可用性和一致性,同时优化性能和扩展性。 RWKV代表的是Read-Write-Key-Value,这个架构通过分离读(R)和写(W)操作,以及将键(Key)和值(Value)分离,来实现其设计目标。 架构组成 读写分离:RWKV架构中,读操作 和 写操作 是分开处理的。这意味着系统可以针对读操作进行优化,比如通过缓存机制来提高读取速度,而写操作则可以专注于数据的持久化和一致性保证。 键值存储:在这种架构中,键(Key)和值(Value)是分开存储的。键通常包含了定位数据所需的全部信息,而值则是实际的数据内容。这种分离使得系统可以更加灵活地管理数据,比如可以实现键的索引,从而加快查找速度。 数据分区:为了提高系统的扩展性,RWKV架构通常会将数据进行分区。每个分区可以独立地进行读写操作,这样可以在不影响整个系统的情况下,对单个分区进行扩展或维护。 特殊性分析 高性能:通过读写分离,RWKV架构能够针对不同的操作类型进行专门的优化。例如,读操作可以通过增加缓存层来提高响应速度,而写操作则可以通过批量处理和日志优化来提高吞吐量。 高可用性:RWKV架构通过数据分区和副本机制来保证数据的可用性。即使某个分区发生故障,系统也可以通过其他分区或副本来继续提供服务。 一致性保证:在分布式系统中,数据一致性是一个重要的问题。RWKV架构通过写操作的严格控制和一致性协议,比如两阶段提交(2PC)或Paxos、Raft等算法,来确保数据在多个副本之间的一致性。 扩展性:由于数据分区的设计,RWKV架构可以很好地支持水平扩展。当系统需要处理更多的数据或请求时,可以通过增加更多的分区来实现。 灵活性:键值分离的设计使得RWKV架构在数据管理上具有很高的灵活性。可以根据业务需求,对键进行定制化的索引和查询,从而更好地支持复杂的应用场景。 为什么选型RWKV做大语言基底模型的预研因为:便宜好用(这里的便宜是指算力的便宜) 到底有多便宜呢? 上图是RWKV架构模型对比Transformer类架构模型在完成推理任务时的时间复杂度和空间复杂度对比。 通过复杂度可以看到,RWKV架构模型推理的耗时和内存占用随Tokens(可以简单理解为字数)长度呈线性增长关系。而Transformer类模型则呈幂级数增长关系,如果表现在游戏对话任务中,就是和NPC说的话越多,它回复你的时间就会越久,这也很贴合Transformer模型中的重要结构Attention机制的中文翻译——“注意力”。 前些日子看到有文章说华为内部开始研究RWKV架构去服务于他们的大语言模型,于是去其官网上一探究竟,发现 stability.ai(开源AI绘画架构的开发公司)也对RWKV进行了赞助,说明RWKV在架构一定有它的特殊性和优越性,于是决定研究一下。 早在2023年5月RWKV架构问世就已经,在一众Transformer模型架构的LLM领域下,这是一个使用RNN架构进行模型推理的模型。并且在竞争激烈的大语言模型评测中排到了第八名的成绩。 长话短说,如果使用RWKV,最希望它能够解决的问题是:费用问题 在某上线项目的用户信息采集中,我们发现:玩家其实并不那么care游戏中的AI智不智能(更多的是来看看这个AI是不是一个噱头),也并没有对于它不能回答自己的奇怪提问而提出不满(注意:这里得到的结果并不具有泛化性,有许多影响这一结果的因素没有考虑,包括但不限于:游戏玩法和用户年龄段画像等)。那么在学术界上的细扣模型智能面板上提升零点几个百分点的性能表现大战就几乎不需要考虑了,与其牺牲更多的开发成本和模型加载成本得到一个智力刚及格的AI,还要被玩家拷打“爸爸的爸爸叫什么~”,不如创造一个弱智吧吧友级别的AI更有节目效果。 RWKV和Transformer的差别RWKV相较于Transformer: 优点(便宜) 在训练和推理中更少的资源占用(VRAM,CPU, GPU,etc) 对比长上下文的Transformer架构大语言模型,计算要求降低了10到100倍 可以线性缩放到任意长度的上下文(在时间复杂度上Transformer架构的时间复杂度是O(n) ) RWKV通常在这几类语言(例如中文、日语等)上训练的更好,然后才是其他开源的LLM模型 缺点(对提示词的要求高) 对提示词的要求更高,需要用户提出详细描述的问题 在需要回顾的任务上表现较弱,因此需要相应地重新排序提示词的顺序 (正例:“阅读下面的文档,帮我执行XX任务。文档为……” √) (反例(回顾任务):“对于上面一段对话中的内容,帮我执行XX任务。” ×) 以上优缺点对比来自RWKV官网:https://wiki.rwkv.com/#tldr-vs-existing-transformer-models 从缺点上看,RWKV表现出非常经典的“RNN特色”,即:长序列任务训练导致的梯度消失和梯度爆炸问题(根源于反向传播过程中的梯度计算问题)。但是其实没有特别想清楚为什么训练阶段的问题会推广至推理阶段。 看到这里,方便给对于AI领域的专业名词不那么了解的同学讲清楚这个性能侧重问题,我假设衡量大语言模型的性能有以下四个维度:适用领域——多、推理速度——快、表现效果——好、内存占用——省,可绘制的表格如下: 适用领域 推理速度 表现效果 内存占用 Transformer 多(Attention is ALL you need) 慢(O(n2)) 好(或许应该听过Sora是Transformer架构结合Diffusion实现的) 慢(O(n2)) RWKV 较多(RWKV-Runner中可以看到AI绘画和音乐生成等工作) 快(O(n)) 一般(源自于自身结构的脆弱性,在长序列下的表现有时像一个健忘的老人,类似于下面这种表现:input:马冬梅 → output:马冬什么/马什么梅/什么冬梅) 快(O(n)) 这样一个简单的性能矩阵关系,我们应该可以大概定义Transformer和RWKV的应用场景: Transformer侧重于整体性能的表现效果,如果在高并发的场景下使用将非常非常吃服务器性能和配置。如果使用Transformer作为底层模型架构启动AI相关服务,要么提升显卡算力性能,要么对模型进行剪枝蒸馏等一些列模型压缩工作。 RWKV侧重高效和满足基准线,“量大管饱”,在追求“都用上”的场景下,轻量级的高效模型可以很好的弥补Transformer在推理并发上的缺陷,更适合ToC的场景,制作一个弱智吧吧友应该绰绰有余(理论上)。 结构建模Self-Attention一个简单的自注意力机制的实现。 其中涉及到以下几个步骤: 初始化参数:定义需要的查询(Q)、键(K)和值(V)矩阵。 计算注意力权重:通过查询和键的点积来计算注意力得分,使用Softmax激活函数使得分数归一化。 计算输出:使用得到的注意力权重对值进行加权求和。 假设输入是一个小批量的嵌入向量,每个向量维度一致。 下面仅用 Python 语言和 NumPy 库来实现一个基本的自注意力结构。 首先,导入必要的库,其次定义一些参数和初始化权重。接下来是关键的一步,创建一个自注意力层,其中输入、查询、键、值的维度都是相同的。 假设批量大小为一个简单的例子,并处理几个向量。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849# 导入库函数 NumPyimport numpy as np# 利用 NumPy 来写 Softmax 激活函数(也可以使用torch中现成的)def softmax(x): e_x = np.exp(x - np.max(x, axis = -1, keepdims = True)) # Numerical stability return e_x / e_x.sum(axis = -1, keepdims = True)class SelfAttention: # 权重随机初始化 def __init__(self, embed_size): self.embed_size = embed_size # 初始化权重矩阵。简单起见,我们使用**随机初始化** self.query_weights = np.random.rand(embed_size, embed_size) self.key_weights = np.random.rand(embed_size, embed_size) self.value_weights = np.random.rand(embed_size, embed_size) # 前向传播(Forward Propagation) def forward(self, x): # x 的形状是(batch_size, seq_length, embed_size) batch_size, seq_length, embed_size = x.shape # 计算Q,K,V queries = np.dot(x, self.query_weights) keys = np.dot(x, self.key_weights) values = np.dot(x, self.valeu_weights) # 计算**注意力得分** attention_scores = np.dot(queries, key.transpose((0, 2, 1))) / np.sqrt(self.embed_size) attention_probs = softmax(attention_scores) # 将得分结果进行归一化 # 加权求和得到输出 output = np.dot(attention_probs, values) return output# 示例使用embed_size = 4 # 嵌入维度是4seq_length = 3 # 序列长度是3batch_size = 1 # 批大小是1# 创建一个自注意力对象self_attention = SelfAttention(embed_size)# 创建一个随机的输入批量x = np.random.rand(batch_size, seq_length, embed_size)# 运行前向传播output = self_attention.forward(x)print("Output of self-attention:\\n", output) Multi-Head Attention多头注意力结构的目的:显著提高模型的表达能力和注意力的细致度。 多头注意力机制,顾名思义就是多个头的注意力机制,是自注意力机制的扩展。 多头注意力允许模型在不同的表示子控件中并行地捕获信息。 接下来,我们就在上面已经实现的自注意力代码基础上进一步实现这个多头注意力机制。 实现多头注意力机制的几个步骤: 初始化参数:为每个头粉笔初始化查询(Q)、键(K)和值(V)矩阵。 分割输入:将输入向量分割成多个头(Head)。 并行计算:对每个头执行自注意力计算。 合并头:将所有头的输出合并回一个单一的输出。 输出层:可能需要一个额外的线性层(Linear)来整合多头计算的结果。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263# 导入库import numpy as np# 手写Softmaxdef softmax(x): e_x = np.exp(x - np.max(x, axis = -1, keepdims = True)) # Numerical stability return e_x / e_x.sum(axis = -1, keepdims = Ture)class MultiHeadAttention: # 多头的初始化 def __init__(self, embed_size, num_heads): self.embed_size = embed_size self.num_heads = num_heads self.head_dim = embed_size / num_heads assert self.head_dim * num_heads = embed_size, "embed_size nust be devisible by num_heads" # 初始化权重矩阵,每个头都有一组 self.query_weights = np.random.rand(num_heads, self.head_dim, self.head_dim) self.key_weights = np.random.rand(num_heads, self.head_dim, self.head_dim) self.value_weights = np.random.rand(num_heads, self.head_dim, self.head_dim) self.out_weights = np.random.rand(num_heads * self.head_dim, embed_size) def forward(self, x): batch_size, seq_length, embed_size = x.shape # 分割输入 x = x.reshape(batch_size, seq_length, self.num_heads, self.head_dim) # 分别对每个头计算Q、K、V queries = np.dot(x, self.query_weights.transpose((1, 0, 2))) keys = np.dot(x, self.key_weights.transpose((1, 0, 2))) values = np.dot(x, self.value_weights.transpose((1, 0, 2))) # 计算注意力得分 attention_scores = np.einsum('bnhd, bmhd -> bhnm', queries, keys) / np.sqrt(self.head_dim) attention_probs = softmax(attention_scores) # 加权求和得到每个头的输出 out = np.einsum('bhnm, bmhd -> bnhd', attention_probs, values) # 合并所有头的输出 out = out.reshape(batch_size, seq_length, self.num_heads * self.head_dim) # 应用输出层权重 out = np.dot(out, self.out_weights) return out # 示例使用embed_size = 8 # 嵌入维度seq_length = 3 # 序列长度batch_size = 1 # 批量大小num_heads = 2 # 头的数量# 创建一个多头注意力对象multi_head_attention = MultiHeadAttention(embed_size, num_heads)# 创建一个随机的输入批量x = np.random.rand(batch_size, seq_length, embed_size)# 运行前向传播output = multi_head_attention.forward(x)print("Output of multi-head attention:\\n", output) 通过分割输入并在不同的头上并行运算,然后合并结果,它允许模型从多个子空间同时学习信息。 每个头独立地学习输入的不同方面,最后一个输出层将所有头的信息合并起来,形成最终的输出。 RWKV结构下面是用于实现RWKV的类。 组成它的部分是初始化、层归一化、通道融合、时序融合、前向传播。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495class RWKV_RNN(torch.jit.ScriptModule): def __init__(self, args): super().__init__() self.args = args self.eval() # 切换模型至评估模式。运行模型就不会使用Dropout和BatchNormalization w = torch.load(args.MODEL_NAME + '.pth', map_location = 'cpu') for k in w.keys(): if '.time_' in k: w[k] = w[k].squeeze() if '.time_decay' in k: w[k] = -torch.exp(w[k].float) else: w[k] = w[k].float self.w = tpyes.SimpleNamespace() self.w.blocks = {} for k in w.keys(): parts =k.split('.') last = parts.pop() here = welf.w for p in parts: if p.isdigit(): p = int(p) if p not in here: here[p] = types.SimpleNamespace() here = here[p] else: if not hasattr(here, p): setattr(here, p, types.SimpleNamespace()) here = getattr(here, p) setattr(here, last, w[k]) def layer_norm(self, x, w): return F.layer_norm(x, (self.args.n_embd,), weight = w.weight, bias = w.bias) @torch.jit.script_method def channel_mixing(self, x, state, i:int, time_mix_k, timemix_r, kw, vw, rw): xk = x* time_mix_k + state[5*i + 0] * (1 - time_mix_k) xr = x* time_max_r + state[5*i + 0] * (1 - time_mix_r) state[5*i + 0] = x r = torch.sigmoid(rw @ xr) k = torch.square(torch.relu(kw @ xk)) return r *(wv @ k) @torch.jit.script_method def time_mixing(self, x, state, i:int, time_mix_k, time_mix_v, time_mix_r, time_first, time_decay, kw, vw, rw, ow): xk = x * time_mix_k + state[5*i + 1] * (1 - time_mix_k) xv = x * time_mix_v + state[5*i + 1] * (1 - time_mix_v) xr = x * time_mix_r + state[5*i + 1] * (1 - time_mix_r) state[5*i + 1] = x r = torch.sigmoid(rw @ xr) k = kw @ xk v = vw @ xv aa = state[5*i + 2] bb = state[5*i + 3] pp = state[5*i + 4] ww = time_first + k qq = torch.maximum(pp, ww) e1 = torch.exp(pp - qq) e2 = torch.exp(ww - qq) a = e1 * aa + e2 * v b = e1 * bb + e2 wkv = a / b ww = pp + time_decay qq = torch.maximum(ww, k) e1 = torch.exp(ww - qq) e2 = torch.exp(k - qq) state[5*i + 2] = e * aa + e2 * v state[5*i + 3] = e1 * bb + e2 state[5*i + 4] = qq return ow @ (r * wkv) def forward(self, token, state): with torch.no_grad(): if state == None: state = torch.zeros(self.args.n_layer * 5, self.args.n_embd) for i in range(self.args.n_layer): state[5*i + 4] = -1e30 # -infinity x = self.w.emb.weight[token] x = self.layer_norm(x, self.w.blocks[0].ln0) for i in range(self.args.n_layer): att = self.w.blocks[i].att x = x + self.time_mixing(self.layer_norm(x, self.w.blocks[i].ln1), state, i, att.time_mix_k, att.time_mix_v, att.time_mix_r, att.time_first, att.time_decay, att.key.weight, att.value.weight, att.receptance.weight, att.output.weight) ffn = self.w.block[i].ffn x = x + self.channel_mixing(self.layer_norm(x, self.w.blocks[i].ln2), state, i, ffn.time_mix_k, ffn.time_mix_r, ffn.key.weight, ffn.value.weight, ffn.receptance.weight) x = self.w.head.weight @ self.layer_norm(x, self.w.ln_out) return x.float(), state RWKV的结构难点在它的channel_mixing和time_mixing,这是两个类似RNN实现方法的递归模块。 其中,state的状态保存和更新过程是动态规划的实现,这也是在时间复杂度上能达到O(n)的原因。 效果表现(7B)使用 Baichuan2/Baichuan2-7B-Chat 对比 Baichuan2/Baichuan2-7B-Chat. 在推理部分,二者皆使用 编码器-解码器 架构生成文本,最大生成Tokens为500,其余所有参数一致,运行环境一致。 对比结果如下: RWKV Transformer 驱动设备 RTX4090 24G RTX4090 24G 模型加载耗时浮动 较大 较小 模型加载耗时区间(单位:s) [9, 100] [65,75] 模型推理耗时(十次取均值) 10.256s 7.118s 模型推理内存占用 1457Mib 14711Mib 模型推理时内存变化 无变化 随推理过程递增(因此长序列下会OOM) 表现效果 介绍一下声音产生的原理。让我们慢慢来,一步一步分析并做出专业的回答。 Assistant: 声音产生的原理是通过物体振动产生的。当物体振动时,它会产生压缩和膨胀的空气波,这些波会在周围的空气中传播,形成声音。 具体来说,当物体振动时,它会产生压缩和膨胀的空气波。这些波会在周围的空气中传播,形成声音。当这些波到达人的耳朵时,它们会引起耳膜的振动,从而产生听觉信号,最终被大脑解释为声音。 声音的频率和强度取决于物体振动的频率和振幅。高频率的声音通常比低频率的声音更尖锐,而强度则取决于振动的大小和持续时间。 除了物体振动产生声音之外,还有其他一些因素也会影响声音的产生和传播。例如,声音的传播速度取决于介质的密度和粘度,而声音的反射和折射则取决于介质的特性和声音的频率。 总之,声音产生的原理是通过物体振动产生的空气波来传播和传递声音信号。 User: 介绍一下声音产生的原理。让我们慢慢来,一步一步分析并做出专业的回答。 Assistant: 声音产生的原理是通过物体振动产生的空气波来传播和传递声音信号。当物体振动时,它会产生压缩和膨胀的空气波,这些波会在周围的空气中传播,形成声音。 具体来说,当物体振动时,它会产生压缩和膨胀的空气波。这些波会在周围的空气中传播,形成声音。当这些波到达人的耳朵时,它们会引起耳膜的 介绍一下声音产生的原理。让我们慢慢来,一步一步分析并做出专业的回答。 1. 声波的产生:当物体振动时,会带动周围的空气分子一起振动。这种振动以波的形式传播,我们就称之为声波。声波的频率、振幅和方向取决于物体的振动特性以及空气的介质。 2. 声音的传播:声波在空气中以波的形式传播,其速度取决于空气的温度、压力和湿度。在标准大气压下,声波在空气中的传播速度约为340米/秒。声波在传播过程中会受到各种干扰,如吸收、散射和反射等,这会导致声波的衰减和改变方向。 3. 声音的接收:当我们听到声音时,是由于声波经过耳朵的传导作用,使外耳、耳膜、中耳和内耳等结构发生振动。这些振动通过听觉神经传递到大脑,大脑对振动信号进行处理,从而产生声音的感觉。 4. 声音的分类:根据声音的频率范围,我们可以将声音分为低频、中频和高频声音。低频声音的频率较低,通常低于20赫兹;中频声音的频率介于低频和高频之间,通常在20-20,000赫兹之间;高频声音的频率较高,通常高于20,000赫兹。此外,我们还可以根据声音的音调、音色和音量等特征来描述声音。 综上所述,声音是由物体的振动引起的,通过空气传播,经过耳朵的传导作用被感知。声音具有频率、振幅、方向和强度等特征,可以根据这些特征进行分类和描述。 实验过程中RWKV使用手感非常不舒服,在提示词上要求“死板”。 RWKV就像是LLM界的”汇编语言”,该架构能够在450mb参数下进行对话的模型(Transformer架构的模型做不到),但是在同标准的3B/7B模型下效果非常奇怪……要按照它的专属提示词方式才能正常生成。 在同样使用 编码器-解码器 的方式进行文本生成任务时,RWKV 推理耗时并不如 Transformer 模型,这一点与论文和宣传相违背,不知道是否是文本长度不够的问题和实验设计问题。 在生成内容的表现效果上,大量重复和复读的部分(即使已经设置 repetition_penalty 参数),排版效果差,效果不尽人意。 因此,在追求模型效果的架构选择上:Attention is all you need. 结语一个可以在非常轻量化环境下运行的模型,运行内存不到3G(2923Mib),在不追求精度和高并发的任务上有挖掘的空间。 这样一个解决“最低标准”问题的模型,在追求性价比的场景下,需要做比较多的应用场景对比。","categories":[{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"}],"tags":[{"name":"NLP","slug":"NLP","permalink":"https://zade23.github.io/tags/NLP/"}]},{"title":"【NLP】分词器tokenizer","slug":"【NLP】分词器tokenizer","date":"2024-04-15T02:59:34.000Z","updated":"2024-04-15T05:59:15.911Z","comments":true,"path":"2024/04/15/【NLP】分词器tokenizer/","link":"","permalink":"https://zade23.github.io/2024/04/15/%E3%80%90NLP%E3%80%91%E5%88%86%E8%AF%8D%E5%99%A8tokenizer/","excerpt":"","text":"在自然语言处理(NLP)中,”分词器”(Tokenizer)是一个非常基础也非常重要的概念。接下来,我将详细解释分词器的作用和其在NLP中的重要性,以及不同分词器可能产生的影响。 分词器的概念分词器是一种工具,用于将文本分割成更小的单元,通常是单词、短语或有意义的符号。这个过程称为“分词”(Tokenization)。在不同语言和应用中,分词的方法和单位可能有所不同。例如,在英语中,通常按空格和标点符号分割单词,而在中文等没有明显分隔符的语言中,分词变得更加复杂。 分词器的重要性分词是NLP任务的第一步,是后续所有语言处理任务的基础。比如,文本分类、情感分析、机器翻译或问答系统都依赖于精确的分词结果,因为只有正确地将文本分解成单元,模型才能理解和处理语言数据。不准确的分词结果会导致模型无法正确理解文本的意义,从而影响整个NLP系统的性能。 不同分词器对NLP的影响不同的分词器基于不同的规则或模型,可能会在同一文本上产生不同的分词结果。这些结果直接影响到词汇的识别、语法分析和语义理解。 例子:比较不同分词器以英文句子为例:**”Don’t stop believing, hold on to that feeling.”** 基于规则的分词器:可能会将”Don’t”分为”Don”和”‘t”,而”believing,”和”feeling.”则可能因为标点符号的处理不当而分词不准确。 基于机器学习的分词器(如BERT使用的WordPiece):可能会更智能地处理缩写和复合词,将”Don’t”正确地视为一个单元,同时也能更准确地在逗号和句号后进行分词。 这个简单的例子显示了不同分词器在处理特定语言特征时的差异。 一个智能的分词器能更好地理解和保持语言的原意和复杂性,而一个简单的分词器可能会忽略一些细节,从而影响到后续任务的准确性。 在选择分词器时,需要考虑任务的需求、语言的特点以及预期的准确性,选择最合适的分词工具是提升整个NLP系统性能的关键步骤。","categories":[{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"}],"tags":[{"name":"NLP","slug":"NLP","permalink":"https://zade23.github.io/tags/NLP/"}]},{"title":"KMP算法-解决最小重复单元","slug":"KMP算法-解决最小重复单元","date":"2024-03-25T02:17:58.000Z","updated":"2024-03-25T02:18:21.924Z","comments":true,"path":"2024/03/25/KMP算法-解决最小重复单元/","link":"","permalink":"https://zade23.github.io/2024/03/25/KMP%E7%AE%97%E6%B3%95-%E8%A7%A3%E5%86%B3%E6%9C%80%E5%B0%8F%E9%87%8D%E5%A4%8D%E5%8D%95%E5%85%83/","excerpt":"","text":"","categories":[{"name":"数据结构和算法","slug":"数据结构和算法","permalink":"https://zade23.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"},{"name":"C++","slug":"C","permalink":"https://zade23.github.io/tags/C/"}]},{"title":"Transformers_FlashBack","slug":"Transformers-FlashBack","date":"2024-03-21T02:10:26.000Z","updated":"2024-03-21T03:01:16.819Z","comments":true,"path":"2024/03/21/Transformers-FlashBack/","link":"","permalink":"https://zade23.github.io/2024/03/21/Transformers-FlashBack/","excerpt":"","text":"","categories":[{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"}],"tags":[{"name":"Transformers","slug":"Transformers","permalink":"https://zade23.github.io/tags/Transformers/"}]},{"title":"《健壮的Python》读书笔记-Day1","slug":"《健壮的Python》读书笔记-Day1","date":"2024-03-20T13:45:05.000Z","updated":"2024-03-20T13:45:51.762Z","comments":true,"path":"2024/03/20/《健壮的Python》读书笔记-Day1/","link":"","permalink":"https://zade23.github.io/2024/03/20/%E3%80%8A%E5%81%A5%E5%A3%AE%E7%9A%84Python%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-Day1/","excerpt":"","text":"","categories":[{"name":"读书笔记","slug":"读书笔记","permalink":"https://zade23.github.io/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"LLaMA-Factory原理与底层实现","slug":"LLaMA-Factory原理与底层实现","date":"2024-03-19T02:53:47.000Z","updated":"2024-03-20T13:30:21.869Z","comments":true,"path":"2024/03/19/LLaMA-Factory原理与底层实现/","link":"","permalink":"https://zade23.github.io/2024/03/19/LLaMA-Factory%E5%8E%9F%E7%90%86%E4%B8%8E%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0/","excerpt":"","text":"LLaMA-Factory的简要介绍简化大模型训练的工具 从 Pre-train → SFT(监督微调,supervised fine-tuning)→ RLHF(基于人类反馈的强化学习,reinforcement learning human feedback),一步完成。 一张4090完成70B参数的模型微调。 效率: 算子上提高两倍提速和60%的缓存节省。 一键完成。 通过分布式框架,甚至可以实现最多180B的框架模型微调。 目前已经有数十篇ArXiv论文使用了LLaMA-Factory工具作为微调框架。 为什么要使用LLaMA-Factory 如果从头训练一个llama2,模型,从LLaMA论文来看,仅7B参数就需要184320GPU小时,是非常遥不可及的时长。 报错难追踪,因为训练的报错常常是CUDA层面的bug,非常不方便定位。 GaLora微调技术,关于一种全参数的高效训练方法 QLora微调技术,一种Lora训练的量化版本。举例来说,在7B模型下使用2bite的量化微调QLora方法,可以有效的降低到仅仅需要4GB的显存占用。 Lora是在网络的旁边增加一个旁路,将d维度的高秩表征映射到r维的低秩表征,最后再从r维重新映射回d维。由于完整的微调工作是在缩小后的r维上进行的,这样就使得完整的训练中需要修改的参数量大幅度的降低(只用到0.01%的参数,显存占用量减少3倍)。Lora用多了还是有一个明显的缺陷,即对很多任务的拟合依然不是很好。 但是GaLora会是更好的微调方法,因为它具备了全参数微调的能力。 LLaMA-Factory的结构分层从最底层开始介绍: 底层加速层:已适配的硬件有英伟达、华为、海光 软件层和计算架构层:以PyTorch和Hugging Face 为主。DeepSpeed作为分布式多卡加速训练的方案(使用了一种数据并行的方案,平均分配所有参数到多卡环境中),在工程架构实现以后,未来将支持更多的框架。 大模型和数据层:更高层的抽象,比如Flash Attention RoPE Scaling,以及数据上的集成,比如动态集成数据 高效训练层:预训练/继续训练 + 监督微调 + PPO算法 如何优化每一步的加速过程简单回顾一下大模型的训练方式,首先是InstructGPT论文,它使用到的训练方法和GPT是一样,称之为三阶段训练方法: 三个阶段可以分为问答阶段、模型奖励阶段、强化学习训练: 问答阶段:利用人类标记好的数据,进行指令的微调工作。在这一阶段,模型就初具了能够听懂人类指令和完成对话的基本功能。它存在的弊端是,当下的模型依然含有有害内容,而且相对输出的内容过短。 设计模型奖励阶段:这一步主要任务就是收集数据,构造一个已标注过的文本对比数据集。建立一个奖励模型(RM,reward model),该奖励模型可以对每一个给定的问题和这个问题的答案输出一个标量的分数。 强化学习:通过这个得到的标量分数,通过PPO算法去微调问答阶段得到的微调模型(偏好对齐)。 通过以上三步,加上LLaMA-Factory的一些技术,使得这一套训练工作流比原始训练更加高效。 预训练在开始将解SFT之前,首先讲解一下预训练。 预训练是模型训练过程中必要的一个阶段,在有大量无标注的语料情况下,我们希望可以先不进行标注。通过大量的无标注数据的预训练方法,使得模型在我们相关领域的数据上表现的更好(思考为什么?)。 这里我们使用类似GPT3的自回归模型(auto-regressive language model),根据前i-1个tokens来预测第i个token的能力。 在持续训练的过程中,使用传统的因果建模的方式,通过计算交叉熵损失的方式来最小化下面这个对数似然的损失来实现模型的优化。$$\\mathcal{L}_ {\\mathrm{CLM}}=-\\frac{1}{n} \\sum_ {i=1}^{n} \\log P\\left(x_ {i} \\mid x_ {1}, x_ {2}, \\ldots, x_ {i-1} ; \\theta\\right)$$它的优势是:通过无监督的方式来完成一系列优化。 预训练阶段的语料处理这里,处理这种预训练阶段的数据最高效的方法有两种,分别是序列打包方法(sequence packing)和数据的流式加载的(data streaming),下面对二者分别进行介绍。 序列打包方法将不同长度的序列打包在一起,省去padding的过程。如图中的过程,左侧的序列是没有打包过的序列,右边序列是打包过的序列。如果我们假设 batch_size = 1,则处理左边的序列需要4次;处理右边的序列只需要3次。 问:打包在一起的语料之间有干扰怎么办? 答:会有干扰,但是影响极小,带来的训练效率提升却是显著的。 通常情况下,默认会使用 sequence packing 的方法处理序列数据。 数据的流式加载下面的这一种数据流式加载技术,主要是为了极大的洁身CPU的内存。通常,我们预期的阶段需要用到几百GB的内存,在使用了数据的流式加载技术后可以节省巨大的内存占用,同时可以避免在分布式训练上造成超时的问题。 监督微调在经历了一系列优化工作后,监督微调(SFT,Supervised Fine-Tuning)通过问答对的形式进行一个 sequence to sequence 的学习。 这一阶段的方法和预训练阶段的损失是一样的,采用交叉熵损失。$$\\mathcal{L}_ {\\mathrm{SFT}}=-\\frac{1}{n} \\sum_ {i=1}^{n} \\log P\\left(y_{i} \\mid x_ {1}, x_ {2}, \\ldots, x_ {m} ,y_ {1}, y_ {2}, \\ldots, y_ {i-1} ; \\theta\\right)$$在公式中,x代表问答对中的问题,y代表问答对中的回复。在计算损失的过程中,只计算y上面的损失,对于x一般是不计算损失的。 在LLaMA-Factory中,主要就是针对这个多轮对话的数据集去优化sft的loss计算方式。 在多轮对话数据集中,只要计算一次前向传播。 在模型输出的损失函数计算中,将终止标签eos标注添加到标准输出中,这样可以帮助模型学习到在什么位置进行停止。提高5%到10%的运算效率。 奖励模型奖励模型使用 pairwise ranking loss 来计算损失:$$\\mathcal{L}_ {\\mathrm{RM}}=-\\sum_ {(x,y^c,y^r)}\\log\\left(\\sigma(r_ \\theta(x,y^c)-r_ \\theta(x,y^r))\\right)$$其中,yc代表了更好的回复,yr代表了更差的回复,r就是对答案的打分。这个函数希望对好回答的打分高于差回答的打分。 在训练过程中,会将所有的r1、r2、r3的分数一起作为参考求平均值。 在推理过程中,只会参考r3的分数最为推理时候的分数。 思考一个问题:为什么训练的过程中需要将句子中每一个token进行采取然后做分数计算,而不能只计算最后一个token的分数? 答案是:这样会使得模型过拟合。 以上是对奖励模型学习过程中的优化过程:针对最后一个输出token进行打分,用于评估奖励模型效果。 强化学习(PPO学习)PPO学习参考一个多臂老虎机问题(bandit problem)。在大模型的过程中,会对一个Prompt产生一条随机的回复,然后奖励这个模型,并对生成的回复进行打分。PPO模型就是为了希望最大化的奖励这个模型给出的分数。 除了奖励以外,还会对当前这个模型进行KL惩罚以防止PPO对模型的过度优化现象。$$\\mathcal{J}(\\phi)=\\mathbb{E}_ {(\\mathrm{x},\\mathrm{y})\\sim\\mathcal{D}_ {\\pi_\\phi^{\\mathrm{PPO}}}}\\left[r_ \\theta(x,y)-\\beta\\log\\frac{\\pi_\\phi^{\\mathrm{PPO}}(y|x)}{\\pi^{\\mathrm{SFT}}(y|x)}\\right]$$在上面损失函数中,log后分子上的数学建模是PPO模型,分母上的建模是SFT模型。 因此,可以看到,如果想要进行PPO模型的训练,就需要加载三个模型。如果只有一张显卡,就很难把三个模型都运行起来。 模型权重共享的方法在三个模型中,参数的大头在backbone部分,这部分的参数和空间占了整个模型空间的98%左右。如果多次读取模型,最冗余的状态就是backbone的重复加载和卸载。 这里我们引入LoRA适配器的方法,共享backbone部分参数,在训练过程中动态加载LoRA和adapter模块。 PPO算法拥有一些强化学习的通病,就是难收敛。这里可以推荐一个替代的强化学习算法DPO,公式上的差别较小:$$\\mathcal{L}_ {\\mathrm{DPO}}=-\\sum_ {(x,y^c,y^{\\prime})\\sim\\mathcal{D}}\\left[\\log\\sigma\\left(\\beta\\log\\frac{\\pi^{\\mathrm{DPO}}(y^c|x)}{\\pi^{\\mathrm{SFT}}(y^c|x)}-\\beta\\log\\frac{\\pi^{\\mathrm{DPO}}(y^r|x)}{\\pi^{\\mathrm{SFT}}(y^r|x)}\\right)\\right]$$ LLaMA-Factory在应用上的优化在强化学习的两个算法中,DPO收敛更快,但是更容易过拟合;PPO效果好,但是不容易收敛/收敛慢。 在LLaMA-Factory上提出了混合微调梯度的方法。 将sft损失乘一个损失因子γ后,将整体值加到DPO中。由于它是一个常数项,所以并不会增加梯度计算的负担 实现工具调用训练使得大模型具有调用外部工具的能力(LangChain)。 如果咨询大模型:当前时刻某个酒店有多少客房 如果直接让大模型回答相关问题,大模型既不知道最新的知识,也不清楚没见过的专有名词(酒店的名称)。 而通过LangChain等方法,就可以将整个工具链上附带工具化调用的能力,其实就是格式化大模型输出的能力。将模型的输出格式,变成可以随着工具进行传参的格式。 在原有论文的基础上,将传入参数的格式变成json格式传入,这样就可以学习到一些嵌套结构例如列表等多参数的传递。 工具链的学习过程没有特别多的技巧,就是一个多轮对话的sft学习,重要的是对标准的传递格式的学习。 使用Flash Attention加速优化一种计算上无损的加速方式,推理速度提升2~3倍,主要在硬件层实现,其原理是提升了I/O的执行效率。 它的替身效率随上下文长度的提升而显著的提升。 模型量化GPTQ的方式可以将模型的权重量化到4bit的程度 位置编码困惑度是指将模型的输入扩充超过最长tokens的限制,那么模型对于输入几乎会丧失所有的推理能力。 位置编码可以将长序列的困惑度大幅度降低。 这里介绍两种插值方法:线性差值(Linear Scaling)和 NTK-aware 差值。 损失函数的优化在LoRA自身的反向传播过程中,PyTorch的反向传播实现得不够好。 例如,对下面A计算梯度,它是一个m×d维的矩阵,乘m×h维的矩阵,乘h×r维的矩阵。 在PyTorch中,它会先将大矩阵相乘,然后再去乘小矩阵。 而如果先将两个较小的矩阵进行相乘,最后再乘较大的矩阵,计算量是大幅度缩小的。(矩阵的乘法交换律) 那么,通过替换原有的PyTorch中的的梯度计算方法,可以大幅度减少需要计算的浮点运算数量。 计算稳定性上的优化fp16精度下的训练有不稳定的情况,bf16相比前者更加稳定。但是由于精度上的差异,导致二者在训练和运行推理时的兼容性上有一定的问题。 bf16精度训练的模型,可以运行在fp16精度的环境下 fp16精度训练的模型,不可以运行在bf16精度的环境下 如果在LLaMA-Factory的环境上强制运行后者,将会得到一个warning警告。 更好的工具调用和总结能力实例展示了通过相应的数据集在LLaMA-Factory上进行训练后,模型不仅可以正确的理解输入的工具调用指令,还能生成正确对应格式的结果。 Trick继续预训练(continue pre-training)相关 继续预训练最好在base模型上进行(不要用chat版) Scaling law 是非常必要的 参考前人最常用的超参数设置可以事半功倍 需要竟可能大数量级的tokens去继续训练过程 多阶段训练可能会提高模型的能力 词表扩充有可能害了训练结果(除非你需要换小语种) 监督微调(SFT)相关 数据质量很重要(合理使用GPT-4去标注知识) 数据集非常小(只有几百条),最好直接使用chat模型做微调并且保持和数据集模板的对齐 为了保证微调过程中那个,模型原有的能力不会损失太明显,还是需要再微调的过程中加上一些通用领域的知识。 Qlora有时币Lora效果更好(更大幅度的降低参数,带来了更稳定的模型) (一般植入新知识的过程都在继续预训练的过程中进行)如果一定要在监督微调的过程中植入新知识,那么就需要加入“同意不同文(或不同格式)”的几十条数据。例如:在监督微调部分修改模型的自我认知,就需要几百条数据来完成一项知识的植入。 超参数没有最好的,只有实践才知道(实践出真知) 答疑问:Flash Attention没法在V100上用,有什么好的办法? 答:在PyTorch2.0之后,已经实现了替代了FlashAttention的轮子。 问:超参数设置怎么来? 答:参考别的论文。 问:针对不同的response做优化,答案长度不同的情况下,如何做归一化呢? 答:不同长度的情况下,会使用tokens填充的方法,将较少tokens的向量做padding,最终补齐长度和较长的tokens一致。","categories":[{"name":"LLM","slug":"LLM","permalink":"https://zade23.github.io/categories/LLM/"}],"tags":[{"name":"Training","slug":"Training","permalink":"https://zade23.github.io/tags/Training/"}]},{"title":"Python模块封装导入和包的相关知识","slug":"Python模块封装导入和包的相关知识","date":"2024-03-13T06:12:24.000Z","updated":"2024-03-14T06:29:05.733Z","comments":true,"path":"2024/03/13/Python模块封装导入和包的相关知识/","link":"","permalink":"https://zade23.github.io/2024/03/13/Python%E6%A8%A1%E5%9D%97%E5%B0%81%E8%A3%85%E5%AF%BC%E5%85%A5%E5%92%8C%E5%8C%85%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/","excerpt":"","text":"背景介绍 TL;DR 模块 快速理解 导入方式 以脚本方式运行模块 Python文件的编译 使用内置函数dir()查看模型定义的名称 包 从包中导入* 相对导入 多目录中的包 背景介绍作为非科班的程序员,在代码规范和程序思维上是有欠缺的(仅代表我个人)。 这些问题会在合作开发项目中暴露出来(阅读其他成员代码以及在其他成员代码上续写功能这类型的任务中暴露的更加明显),比如:对程序模块的封装、底层架构的了解(底层架构对于阅读代码和理解代码很重要)、Python语言的标准库以及装饰器使用等等…… 2024年的主题就是:“还债”。目标是尽快补齐在程序架构和工程领域的能力。 TL;DR在Python工程中,模块是一个包含Python定义和语句的文件,一般以.py作为后缀。模块中的定义可以导入到其他模块或者主程序(main)中,这样做的的目的是方便程序的维护和复用。 模块的代入:通过import导入模块。模块不会直接把模块自身的函数名称添加到当前命名空间中,而是将模块名称添加到命名空间中。再通过模块名称访问其中的函数,例如:import torch \\ torch.nn.functional() 模块的作用:可执行的语句以及函数定义,用于初始化模块。每个模块都有自己的私有命名空间,它会被用作模块中定义的所有函数的全局命名空间。模块可以导入其他模块,被导入的模块名称会被添加到该模块的全局命名空间。(每个模块都有自己的命名空间,防止与用户的全局变量发生冲突)。 模块功能所做的一切就是为了:代码的复用和方便维护。 总览: 模块快速理解现在有个程序模块名称为fibo.py,通过它的名字大致猜测应该是斐波那契数列的功能实现。 打开这个.py文件,内容如下: 12345678910111213141516# Fibonacci numbers moduledef fib(n): # write Fibonacci series up to n a, b = 0, 1 while a < n: print(a, end=' ') a, b = b, a+b print()def fib2(n): # return Fibonacci series up to n result = [] a, b = 0, 1 while a < n: result.append(a) a, b = b, a+b return result 可以看到该模块中有两个方法,分别是:fib和fib2。 如果我想在该模块中使用这两个函数的功能,可以直接调用函数名称并传入参数即可: 1234>>> fib(1000)0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>> fib2(100)[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] 这时自然而然的诞生一个新的问题,如果在这个函数之外,我依然想使用这两个函数的功能在怎办呢? 先展示结果,最后再讲解细节。 现在,新建一个脚本文件(保证该脚本文件和fibo.py在同一目录下),内容如下: 1234567891011121314>>> import fibo>>> fibo.fib(1000)0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>> fibo.fib2(100)[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]>>> fibo.__name__'fibo'>>> fib = fibo.fib # 如果想要经常使用某个函数功能,可以把它赋值给局部变量>>> fib(500)0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 可以看到,新建的脚本文件中通过import fibo调用了开头写的斐波那契数列功能的模块。 当想要使用模块中的函数方法,仅需要用导入的模块名称加上”.功能函数名称”,就可以实现功能的调用甚至重新命名变量等操作。 导入方式使用import导入包的方式现列出4种,例如: 导入模块中的方法名称 12>>> from fibo import fib, fib2>>> fib(500) 导入模块内定义的所有名称(不包括含_开头的名称,并且不建议使用这种方法导入) 123>>> from fibo import *>>> fib(500)0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 模块名使用as,直接把as后的名称与导入模块绑定 12>>> import fibo as fibo>>> fib.fib(500) 结合from一起使用 12>>> from fibo import fib as fibonacci>>> fibonacci(500) 以脚本方式运行模块通常在命令行执行脚本文件的语句: 1python fibo.py <arguments> 直接运行.py脚本会在我们看不见的地方默认的执行一个事情,即:**把__name__赋值为"__main__"**。 也就是把下列代码添加到了模块的末尾 123if __name__ == "__main__": import sys fib(int(sys.argv[1])) 这样做的含义是,在模块作为”main”文件(脚本)进行执行的时候才会运行。 举个例子: 当模块作为脚本文件执行时(会执行): 12$ python fibo.py 500 1 1 2 3 5 8 13 21 34 当模块被导入到其它模块或主程序时(不会执行): 12>>> import fibo>>> Python文件的编译这部分在官网文档讲解的非常清晰,参考6.1.3. “已编译的” Python 文件 这里附上“Python模块快速加载”的决策细节流程图: 使用内置函数dir()查看模型定义的名称dir()用于查找模块定义的名称,返回值为排序之后的字符串列表。 上实例: dir()含参数时: 12345678910111213141516171819202122232425>>> import fibo, sys>>> dir(fibo)['__name__', 'fib', 'fib2']>>> dir(sys)['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info', 'warnoptions'] dir()不含参数时: 12345>>> a = [1, 2, 3, 4, 5]>>> import fibo>>> fib = fibo.fibdir()['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys'] 包包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。 例如,模块名 A.B 表示名为 A 的包中名为 B 的子模块。 使用modules.func的这种调用方式还有一个好处,就是避免在不同模块中的功能函数命名冲突。 例如:在 NumPy 或 Pillow 等多模块包中很多功能函数命名相同,这样使用np.func或Pillow.func就不必担心彼此的func模块名冲突了。 假设要为统一处理声音文件与声音数据设计一个模块集(“包”)。声音文件的格式很多(通常以扩展名来识别,例如:.wav,.aiff,.au),因此,为了不同文件格式之间的转换,需要创建和维护一个不断增长的模块集合。 为了实现对声音数据的不同处理(例如,混声、添加回声、均衡器功能、创造人工立体声效果),还要编写无穷无尽的模块流。 下面这个分级文件树展示了这个包的架构: 1234567891011121314151617181920212223sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ... 导入包时,Python搜索sys.path里的目录,查找包的子目录。 需要有__init__.py文件才能让Python将包含改文件的目录当做“包”来处理。这样可以防止重名目录如string在无意中屏蔽后续出现在模块搜索路径中的有效模块。 最简单的情况就是,__init__.py可以是一个空文件,但是它也可以执行包的初始化代码或设置__all__变量,这将在稍后详细描述。 一些例子说明: 1234import sound.effects.echo# 加载子模块 sound.effects.echo 必须通过全名来引用sound.effects.echo.echofilter(input, output, delay) 另一种导入子模块的方法: 1234from sound.effects import echo# 加载子模块 echo,并且不必加包前缀echo.echofilter(input, output, delay = 0.7, atten = 4) 还有一种,直接导入所需的函数或变量: 1234from sound.effect.echo import echofilter# 加载子模块 echo,使其函数 echofilter() 直接可用:echofilter(input, output, delay = 0.7, atten = 4) 从包中导入*同理于模块的导入,同样不建议这样做。 一些需要提及的知识点:如果直接使用*进行导入,一般执行的操作为通过包中的__init__.py代码部分的以下__all__中的模块名列表。 1__all__ = ["echo", "surround", "reverse"] 子模块的命名有可能会受到本地定义名称的影响! 模块中的模块如果和环境中已存在的模块重名,则会被本地先定义过的函数名称遮挡。以reverse函数为例: 12345678__all__ = [ "echo", # refers to the 'echo.py' file "surround", # refers to the 'surround.py' file "reverse", # !!! refers to the 'reverse' function now !!!]def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodule return msg[::-1] # in the case of a 'from sound.effects import *' 官方文档中,推荐的做法是:frome package import submodule. 相对导入当包由多个子包构成(如示例中的 sound 包)时,可以使用绝对导入来引用同级包的子模块。 例如,如果 sound.filters.vocoder 模块需要使用 sound.effects 包中的 echo 模块,它可以使用 from sound.effects import echo。 你还可以编写相对导入代码,即使用 from module import name 形式的 import 语句。 这些导入使用前导点号来表示相对导入所涉及的当前包和上级包。 例如对于 surround 模块,可以使用: 123from . import echofrom .. import formatsfrom ..filters import equalizer 注意,相对导入基于当前模块名。 因为主模块名永远是 "__main__" ,所以如果计划将一个模块用作 Python 应用程序的主模块,那么该模块内的导入语句必须始终使用绝对导入。 多目录中的包通过 __path__ 可以传入字符串列表,找到所有 __init__.py坐在目录的位置。该功能不常用,知道就好。 相关参考: 6. 模块 — Python 3.12.2 文档","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"【Git】合作开发中Git工作流程的细节","slug":"【Git】合作开发中Git工作流程的细节","date":"2024-03-11T08:39:05.000Z","updated":"2024-12-17T03:45:00.013Z","comments":true,"path":"2024/03/11/【Git】合作开发中Git工作流程的细节/","link":"","permalink":"https://zade23.github.io/2024/03/11/%E3%80%90Git%E3%80%91%E5%90%88%E4%BD%9C%E5%BC%80%E5%8F%91%E4%B8%ADGit%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E7%9A%84%E7%BB%86%E8%8A%82/","excerpt":"","text":"仓库中各分支的职责 清晰的Commit 记录开发过程中,提交代码仓库的一些经验总结(持续更新……) 仓库中各分支的职责 master分支:master分支是存放发布上线的代码的默认位置。当某个功能经过验证和测试后,将develop分支合并到master分支,并为该版本打上相应的标签(例如x.0.0,表示某个大版本的编号)。master分支应该是稳定且经过充分测试的代码。 develop分支:develop分支是整体开发功能的主要分支。在开发阶段,所有功能的开发工作都应提交到develop分支。在功能开发完成后,通过测试和验证后,将develop分支合并到master分支,使功能正式上线。 扩展: 除了master和develop分支外,还可以存在其他类型的分支,如feature分支、bugfix分支等。这些分支可以用于并行开发不同的功能或修复bug,并在开发完成后合并到develop分支。 开发团队通常采用分支策略,例如Git Flow或GitHub Flow,来管理不同分支之间的合并和发布流程,以确保代码质量和版本控制的有效管理。 主分支(如master或main)通常用于存放稳定的、可发布的代码,而开发分支(如develop)则用于整体功能的开发和集成。 通过使用不同的分支,可以实现并行开发、合理管理代码版本、隔离功能开发和修复等,从而提高团队的协作效率和代码质量。 重要的是,分支之间的合并应该经过适当的测试和验证,以确保代码的稳定性和功能的正确性。 清晰的Commit 使用明确的动词:在提交信息的开头使用明确的动词来描述你的更改。例如,使用 “添加”、”修复”、”更新”、”重构” 等词语,以便其他人可以快速了解你的更改类型。 保持简洁:提交信息应该简洁明了,尽量避免冗长的描述。使用简洁的语句来概括你的更改,并在需要时提供详细信息。 提供相关上下文:除了简洁的概述外,确保提交信息提供足够的上下文信息,以便其他人能够理解你的更改原因和意图。如果有相关的问题、需求或讨论,可以引用相关的编号或链接。 遵循团队或项目规范:根据你所在的团队或项目的规范,使用统一的提交格式和命名约定。这样可以帮助整个团队保持一致的提交信息风格,便于阅读和管理。 避免无意义的提交信息:提交信息应该有实际的意义,避免使用模糊或不相关的描述。确保你的提交信息传达了有用的信息,而不仅仅是表明你进行了一次提交。 使用标准的提交类型:参考常见的提交类型(如前面所示),选择最适合你更改类型的提交类型。这有助于其他人快速了解你的更改类型,并且在版本控制工具中进行过滤和分类。 审查和校对:在提交之前,花一些时间审查和校对你的提交信息。确保拼写正确、语法清晰,并且信息准确传达你的更改。 常见的提交格式实例: 当然,以下是一些常见的提交格式示例: feat: 添加新功能或特性 1feat: 添加用户注册功能 fix: 修复 bug 1fix: 修复登录页面样式错位的 bug docs: 更新文档 1docs: 更新用户手册中的安装说明 style: 代码样式、格式调整 1style: 格式化整个项目的代码风格 refactor: 重构代码,既不修复 bug 也不添加新功能 1refactor: 重构用户管理模块的代码结构 test: 添加或修改测试代码 1test: 添加用户注册页面的单元测试 chore: 构建过程或辅助工具的变动 1chore: 更新构建脚本以支持新的依赖库 这一切的目的是为了让团队其他成员明确该提交所做的工作,同时也让自己回顾代码的时候明确自己的工作内容。","categories":[{"name":"Git Workflow","slug":"Git-Workflow","permalink":"https://zade23.github.io/categories/Git-Workflow/"}],"tags":[{"name":"Git","slug":"Git","permalink":"https://zade23.github.io/tags/Git/"}]},{"title":"更新GitLab仓库SSH","slug":"更新GitLab仓库SSH","date":"2024-03-07T02:09:37.000Z","updated":"2024-03-11T08:41:35.516Z","comments":true,"path":"2024/03/07/更新GitLab仓库SSH/","link":"","permalink":"https://zade23.github.io/2024/03/07/%E6%9B%B4%E6%96%B0GitLab%E4%BB%93%E5%BA%93SSH/","excerpt":"","text":"介绍 问题分析 问题解决 介绍早上开工,在拉取代码仓库的时候,发现SSH过期,于是更新一下SSH为永久,并做一个记录。SSH过期命令行显示如下: 123456789101112$ git pullremote:remote: ========================================================================remote:remote: Your SSH key has expired.remote:remote: ========================================================================remote:fatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists. 问题分析直接将问题送给poe,让AI分析解答,根据分析结果一步步处理即可解决。结果如下: 问题解决 生成新的SSH密码对。ssh-keygen -t rsa -b 4096 -C "[email protected]",该命令将生成一个 4096 位的 RSA 密钥对,替换邮箱地址为你GitLab注册邮箱。 之后会有两次询问你保存位置和输入密码的选项,点击 Enter 键跳过选项即可。显示结果如下: 复制生成的SSH,window使用: 1clip < ~/.ssh/id_rsa.pub 找到GitLab中的SSH Keys一栏,粘贴,保存: 可以拉取仓库了","categories":[{"name":"Git Workflow","slug":"Git-Workflow","permalink":"https://zade23.github.io/categories/Git-Workflow/"}],"tags":[{"name":"Git","slug":"Git","permalink":"https://zade23.github.io/tags/Git/"}]},{"title":"conda环境报错解决:invalid choice: 'activate' ","slug":"conda环境报错解决:invalid-choice-activate","date":"2023-11-22T08:04:41.000Z","updated":"2024-03-14T03:02:38.054Z","comments":true,"path":"2023/11/22/conda环境报错解决:invalid-choice-activate/","link":"","permalink":"https://zade23.github.io/2023/11/22/conda%E7%8E%AF%E5%A2%83%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%EF%BC%9Ainvalid-choice-activate/","excerpt":"","text":"介绍新部署的服务器上进行LLM模型的微调工作,在配置Anaconde环境后输入环境启动命令 conda activate ,出现从来没有见过的报错:anaconda conda: error: argument command: invalid choice when trying to update packages 问题分析环境问题,直接Google。最终在 GitHub 里官方仓库和 Stack Overflow 上找到一致的答复:安装Anaconde后没有进行初始化。 问题解决 终端运行 conda init zsh 之后重启 shell/Terminal (不重启依然报错) 终端运行 conda activate env_name 即可","categories":[{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"}],"tags":[{"name":"conda","slug":"conda","permalink":"https://zade23.github.io/tags/conda/"},{"name":"environment","slug":"environment","permalink":"https://zade23.github.io/tags/environment/"}]},{"title":"玩转Docker学习笔记","slug":"玩转Docker学习笔记","date":"2023-11-07T07:30:56.000Z","updated":"2024-03-11T10:00:59.954Z","comments":true,"path":"2023/11/07/玩转Docker学习笔记/","link":"","permalink":"https://zade23.github.io/2023/11/07/%E7%8E%A9%E8%BD%ACDocker%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/","excerpt":"","text":"镜像相关指令 容器相关指令 网络相关指令 数据卷相关指令 Docker Compose相关指令(使用docker-compose) 通过 DockerFile 构建 Docker Image 镜像相关指令 docker pull <image>:从远程仓库拉取一个镜像或仓库到本地。 docker build -t <tag> .:使用当前目录的Dockerfile构建一个新的镜像。 docker images:列出本地存储的镜像。 docker rmi <image>:删除一个或多个本地的镜像。 docker tag <image> <new_tag>:为镜像添加一个新的标签。 容器相关指令 docker run <image>:创建一个新的容器并运行一个命令。 docker ps:列出当前运行的容器。 docker ps -a:列出所有容器,包括未运行的。 docker stop <container>:停止一个或多个正在运行的容器。 docker start <container>:启动一个或多个已停止运行的容器。 docker restart <container>:重启容器。 docker rm <container>:删除一个或多个容器。 docker logs <container>:获取容器的日志。 docker exec -it <container> <command>:在运行的容器中执行命令,通常用于进入容器。 网络相关指令 docker network ls:列出所有网络。 docker network create <options> <network_name>:创建一个新的网络。 docker network rm <network>:删除一个或多个网络。 docker network connect <network> <container>:连接一个容器到一个网络。 docker network disconnect <network> <container>:断开容器与网络的连接。 数据卷相关指令 docker volume create <name>:创建一个新的卷。 docker volume ls:列出所有卷。 docker volume rm <volume>:删除一个或多个卷。 docker volume inspect <volume>:显示详细的卷信息。 Docker Compose相关指令(使用docker-compose) docker-compose up:在后台启动并运行整个应用。 docker-compose down:停止并移除容器,网络,图像和挂载卷。 docker-compose build:构建或重建服务关联的镜像。 docker-compose logs:查看服务的日志输出。 这些命令代表了Docker操作的基础,但是实际使用中可能还会遇到更复杂的场景和高级特性。建议通过官方文档或其他学习资源深入理解每个命令的用法和选项。 通过 DockerFile 构建 Docker Image一个 Image 是通过一个 DockerFile 定义的,然后使用 docker build 命令构建它。 DockerFile 中的每一条命令的执行结果都会成为 Image 中的一个 Layer。 这里,我们通过 Build 一个镜像,来观察 Image 的分层机制: 123456789101112131415161718192021222324252627# 使用官方的Python运行时环境作为基础镜像FROM python:2.7-slim# 设置工作目录为容器内的/app。如果目录不存在,Docker会自动为你创建这个目录。WORKDIR /app# 将当前目录(Dockerfile所在目录)的内容复制到容器内的/app目录中。# 这意味着你的应用代码和依赖文件(比如requirements.txt)都会被复制进去。COPY . /app# 利用RUN命令执行pip安装命令来安装requirements.txt中列出的所有依赖。# 这里使用了--trusted-host选项来指定可信的PyPI主机,避免SSL证书验证问题。RUN pip install --trusted-host pypi.python.org -r requirements.txt# 使用EXPOSE指令来告诉Docker容器内的应用将会在80端口上监听连接。# 需要注意的是,EXPOSE指令本身不会使容器的端口对外界开放,# 它更多的是一种文档化的作用,真正的端口映射需要在运行容器时通过docker run的-p选项来指定。EXPOSE 80# 使用ENV指令设置一个环境变量。这里定义了一个名为NAME的环境变量,值为World。# 环境变量可以在容器运行时被应用程序读取,用于配置应用行为。ENV NAME World# 使用CMD指令指定容器启动时运行的命令。# 这里的命令是“python app.py”,即使用Python解释器来运行app.py脚本。# CMD的主要作用是指定容器的默认执行命令。如果在docker run命令后面指定了其他命令,CMD指定的命令将被覆盖。CMD ["python", "app.py"] 这份Dockerfile基本上覆盖了构建一个简单Python应用的Docker镜像所需的所有步骤。从选择基础镜像开始,设置工作目录,复制应用代码,安装依赖,到最后指定运行时的命令和暴露的端口,每一步都为镜像的构建提供了必要的指令和配置。 最终的构建结果如下: 1234567891011121314151617181920212223root@rds-k8s-18-svr0:~/xuran/exampleimage# docker build -t hello ./Sending build context to Docker daemon 5.12 kBStep 1/7 : FROM python:2.7-slim ---> 804b0a01ea83Step 2/7 : WORKDIR /app ---> Using cache ---> 6d93c5b91703Step 3/7 : COPY . /app ---> Using cache ---> feddc82d321bStep 4/7 : RUN pip install --trusted-host pypi.python.org -r requirements.txt ---> Using cache ---> 94695df5e14dStep 5/7 : EXPOSE 81 ---> Using cache ---> 43c392d51dffStep 6/7 : ENV NAME World ---> Using cache ---> 78c9a60237c8Step 7/7 : CMD python app.py ---> Using cache ---> a5ccd4e1b15dSuccessfully built a5ccd4e1b15d 下面是对这些信息每一步的详细解释: Sending build context to Docker daemon 5.12 kB这一行表示Docker客户端正在将构建上下文发送给Docker守护进程。构建上下文是指Dockerfile所在目录的内容,Docker会将这些内容打包发送给守护进程。这里的大小是5.12kB,表示你的应用代码和依赖文件等总共大小。 Step 1/7 : FROM python:2.7-slim这是Dockerfile中的第一步,它基于python:2.7-slim这个镜像来构建新的镜像。---> 804b0a01ea83是基础镜像的ID。 Step 2/7 : WORKDIR /app设置工作目录为/app。如果不存在,Docker会自动创建这个目录。---> Using cache ---> 6d93c5b91703表示这一步使用了缓存,6d93c5b91703是这一层的ID。 Step 3/7 : COPY . /app将构建上下文(Dockerfile所在的目录)的内容复制到容器内的/app目录。---> Using cache ---> feddc82d321b表明这一步也使用了缓存。 Step 4/7 : RUN pip install –trusted-host pypi.python.org -r requirements.txt在容器内执行pip install命令,安装requirements.txt文件中列出的Python依赖包。---> Using cache ---> 94695df5e14d说明这一步同样使用了缓存。 Step 5/7 : EXPOSE 81通知Docker容器在运行时将会监听81端口。注意这里与前面提到的Dockerfile中的EXPOSE 80不同,可能是因为Dockerfile被修改了但构建输出没有更新。---> Using cache ---> 43c392d51dff表示使用了缓存。 Step 6/7 : ENV NAME World设置环境变量NAME的值为World。这个环境变量可以在容器运行时被应用程序使用。---> Using cache ---> 78c9a60237c8也显示这一步使用了缓存。 Step 7/7 : CMD [“python”, “app.py”]指定容器启动时默认执行的命令。这里是运行app.py脚本。---> Using cache ---> a5ccd4e1b15d意味着这一步也利用了之前的构建缓存。 Successfully built a5ccd4e1b15d最后,显示了成功构建的镜像ID为a5ccd4e1b15d。 在这个过程中,Using cache表明Docker发现之前的构建步骤与当前的完全一致,因此它复用了之前的结果来加快构建过程。如果你想要强制Docker重新执行每个步骤而不使用缓存,可以在构建时添加--no-cache选项。 参考链接: Docker 101 Tutorial Docker原理(图解+秒懂+史上最全)","categories":[],"tags":[{"name":"Docker","slug":"Docker","permalink":"https://zade23.github.io/tags/Docker/"}]},{"title":"pytorch-tutorial-official","slug":"pytorch-tutorial-official","date":"2023-10-31T08:49:36.000Z","updated":"2024-03-14T03:02:15.673Z","comments":true,"path":"2023/10/31/pytorch-tutorial-official/","link":"","permalink":"https://zade23.github.io/2023/10/31/pytorch-tutorial-official/","excerpt":"","text":"Pytorch 基础 快速入门 处理数据 构建模型 在 PyTorch 中构建神经网络 模型层 Model Layers nn.Flatten nn.Linear nn.ReLU nn.Sequential nn.Softmax 模型的参数 张量 Tensor 初始化张量 直接来自数据 来自另一个 Tensor 使用随机值或常量 张量的属性 张量的操作 类似 NumPy 的索引和切片操作 Tensor 之间的连接 张量的算术运算 单一元素张量 原地操作(不通过返回的方式进行原地修改) Tensor 和 Numpy 二者转换 Tensor2NumPy_array NumPy_array2Tensor 数据集的相关操作 数据加载 数据集迭代和数据可视化 创建自定义的数据集 _init_ _len_ _getitem_ 准备数据并使用 DataLoader 进行训练 遍历 DataLoader 转换 ToTensor() Lambda 转换 (核心内容)自动微分(TORCH.AUTOGRAD) 张量、函数、计算图 梯度计算 禁用梯度追踪 计算图部分扩展阅读 可选阅读:张量梯度 和 雅阁比积(Jacobian_Products) (重要)优化模型参数 先决条件代码 超参数 优化循环 全过程数据跟踪 保存、加载和使用模型 保存/加载模型权重 通过模型的形状参数进行保存/加载 在 PyTorch 中保存和加载常规 Checkpoint 介绍 设置 步骤 1.导入加载数据所需要的库 2.定义和初始化神经网络 3.初始化优化器 4.保存常规检查点 5.加载常规检查点 从 Checkpoint 中加载 nn.Module 的技巧 活用 torch.load(mmap = True) 活用 torch.device("meta") 活用 load_state_dict(assign = True) 结论 Pytorch 基础 https://pytorch.org/tutorials/beginner/basics/intro.html 大多数机器学习工作流: 处理数据 创建模型 优化模型参数 保存训练后模型 通过 Pytorch 基础部分的内容,读者可以完整的走完一整个MachineLearning的工作流,若读者对其中某个环节不理解或感兴趣,针对这些工作流中的每一个环节都有相关的扩展阅读链接。 我们将使用 FashionMNIST 数据集训练一个神经网络,该神经网络预测输入图像是否属于一下类别之一:T恤/上衣、裤子、套头衫、连衣裙、外套、凉鞋、成山、运动鞋、包包、靴子。(是个多分类任务) 快速入门 https://pytorch.org/tutorials/beginner/basics/quickstart_tutorial.html 本节会快速走完一个机器学习多分类的Demo,以此快速了解流程中必要的基本ML相关API。 处理数据Pytorch 中有两个用于处理数据的子库 torch.utils.data.DataLoader 和 torch.utils.data.Dataset.Dataset。顾名思义,Dataset 存储样本及其相应的标签,并将 DataLoader 可迭代对象包装在 Dataset 中。 12345import torchfrom torch import nnfrom torch.utils.data import DataLoaderfrom torchvision import datasetsfrom torchvison.transforms import ToTensor 通过上面的引用(import),我们可以发现:Pytorch 中有非常多的子库,这些子库专注于某一特定的领域,例如: TorchText, TorchVision, 和 TorchAudio, 这些所有子库中都包含相应的数据集。 本次教程中我们使用 TorchVision 数据集。 该 torchvision,.datasets 模块包含 Dataset 来自现实世界中的视觉图像数据,最经典的有:CIFAT,COCO(full list here) 本次教程中我们使用 FashionMNIST 数据集。每个 TorchVison 下的 Dataset 都包含两个参数:transform 和 target_transform 分别用来修改样本与打标签。 123456789101112131415# 从公开数据集中读取训练数据training_data = datasets.FashionMNIST( root = "data", train = True, download = True, transform = ToTensor(),)# 从公开数据集中读取测试数据test_data = datasets.FashionMNIST( root = "data", train = False, download = True, transform = Totensor()) 下图是在 colab 中运行上面程序块的输出结果: 至此为止,通过上面的工作,我们将 Dataset 作为参数传递给了 DataLoader 。同时封装了相关数据集作为一个可迭代的对象,支持自动批处理、采样、洗牌(shuffling)、和多进程的数据加载。 下一步中,我们定义 batch_size = 64 ,即 dataloader 可迭代中的每个元素都将返回一批含有64个特征的标签。 12345678910batch_size = 64# 创建数据读取工具train_dataloader = DataLoader(tarain_data, batch_size = batch_size)test_dataloader = DataLoader(tast_data, batch_size = batch_size)for X, y in dataloader: print(f"Shape of X [N, C, H, W]: {X.shape}") print(f"Shape of y []: {y.shape} {y.dtpye}") break 输出: 12Shape of X [N, C, H, W]: torch.Size([64, 1, 28, 28])Shape of y: torch.Size([64]) torch.int64 关于 loading data in PyTorch 的详细说明 构建模型为了在 Pytorch 中定义神经网络,我们创建一个继承自 nn.Module 的类。 我们通过 __init__ 函数定义神经网络的层,并指明数据如何通过 forward 函数进入神经网络层。 在设备允许的情况下,推荐使用GPU来加速神经网络的运算操作。 代码实现: 123456789101112131415161718192021222324252627282930# 获取用于训练的设备(cpu/gpu/mps)device = ( "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu")print(f"Using {device} device")# 自定义神经网络的模型结构class NeuralNetwork(nn.Module): def __init__(self): super().__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28*28, 512), nn.ReLU(), nn.Linear(512, 512), nn.ReLU(), nn.Linear(512, 10) ) def forward(self, x): x = self.flatten(x) logits = self.linear_relu_stack(x) return logitsmodel = NeuralNetwork().to(device)print(model) 打印结果: 1234567891011Using cpu deviceNeuralNetwork( (flatten): Flatten(start_dim=1, end_dim=-1) (linear_relu_stack): Sequential( (0): Linear(in_features=784, out_features=512, bias=True) (1): ReLU() (2): Linear(in_features=512, out_features=512, bias=True) (3): ReLU() (4): Linear(in_features=512, out_features=10, bias=True) )) 在 PyTorch 中构建神经网络 这一部分是对‘构建模型’部分的一点补充说明,也是 PyTorch 官网教程中的扩展阅读部分 神经网络是由多个对数据进行操作的层/模型组合而成的。torch.nn 命名空间几乎已经提供了构建一个神经网络所需要用到的所有模块。 所有模块都在 PyTorch 下的子块 nn.Module 中提供。 基于这样的结构化嵌套模块,整个神经网络可以自由的进行构建和管理复杂的架构。 在上面的代码块中,我们通过 NeuralNetwork 函数定义了一个神经网络模型 model。 为了使用该模型,我们将输入数据传递给它。这个操作将执行 forward 操作和一些后台操作。 请记住:不要直接使用 model.forward() ! 通过输入操作调用模型,最后将返回一个二维张量,其中 dim = 0 对应于每个类别的 10 个原始预测输出,dim = 1 对应与每个输出的单个值。 我们可以通过 nn.Softmax 模块实例传递对结果预测的概率来进行最终预测概率的判断。 12345X = torch.rand(1, 28, 28, device = device)logits = model(X)pred_probab = nn.Softmax(dim = 1)(logits)y_pred = pred_probab.argmax(1)print(f"Predicted class: {y_pred}") 打印结果: 1Predicted class: tensor([7], device = 'cuda:0') 模型层 Model Layers分解 FashionMNIST 模型中的各层。为了说明这一点,我们通过获取一个包含 3 张大小为 28*28 的小批量图像样本,看看当数据传递到网络时会发生什么。 12input_image = torch.rand(3, 28, 28)print(input_image.size()) 打印输出: 1torch.Size([3, 28, 28]) nn.Flatten初始化 nn.Flatten 层,将每个2D 28*28 图像转换成包含 784 个像素值的连续数组(保持小批量尺寸(dim = 0)) 123flatten = nn.Flatten()flat_image = flatten(input_image)print(flat_image.size()) 打印输出: 1torch.Size([3, 784]) nn.Linear线性层模块通过输入的权重w和偏差值b进行线性变换。 123layer1 = nn.Linear(in_features = 28*28, out_features = 20)hidden1 = layer1(flat_image)print(hidden1.size()) 打印输出: 1torch.Size([3, 20]) nn.ReLU非线性激活函数可以在模型的输入输出之间创建复杂的映射关系。激活函数通过引入非线性的变换帮助神经网络学习各种现象。 在实例模型中,我们在线性层之间使用ReLU激活函数。但还有其他激活函数可以在模型的线性层中间作为激活函数使用,详情参考:激活函数-wiki 123print(f"Before ReLU: {hidden1}\\n\\n")hidden1 = nn.ReLU()(hidden1)print(f"Aftr RelU: {hidden1}") 打印输出: 1234567891011121314151617181920Before ReLU: tensor([[ 0.4158, -0.0130, -0.1144, 0.3960, 0.1476, -0.0690, -0.0269, 0.2690, 0.1353, 0.1975, 0.4484, 0.0753, 0.4455, 0.5321, -0.1692, 0.4504, 0.2476, -0.1787, -0.2754, 0.2462], [ 0.2326, 0.0623, -0.2984, 0.2878, 0.2767, -0.5434, -0.5051, 0.4339, 0.0302, 0.1634, 0.5649, -0.0055, 0.2025, 0.4473, -0.2333, 0.6611, 0.1883, -0.1250, 0.0820, 0.2778], [ 0.3325, 0.2654, 0.1091, 0.0651, 0.3425, -0.3880, -0.0152, 0.2298, 0.3872, 0.0342, 0.8503, 0.0937, 0.1796, 0.5007, -0.1897, 0.4030, 0.1189, -0.3237, 0.2048, 0.4343]], grad_fn=<AddmmBackward0>)After ReLU: tensor([[0.4158, 0.0000, 0.0000, 0.3960, 0.1476, 0.0000, 0.0000, 0.2690, 0.1353, 0.1975, 0.4484, 0.0753, 0.4455, 0.5321, 0.0000, 0.4504, 0.2476, 0.0000, 0.0000, 0.2462], [0.2326, 0.0623, 0.0000, 0.2878, 0.2767, 0.0000, 0.0000, 0.4339, 0.0302, 0.1634, 0.5649, 0.0000, 0.2025, 0.4473, 0.0000, 0.6611, 0.1883, 0.0000, 0.0820, 0.2778], [0.3325, 0.2654, 0.1091, 0.0651, 0.3425, 0.0000, 0.0000, 0.2298, 0.3872, 0.0342, 0.8503, 0.0937, 0.1796, 0.5007, 0.0000, 0.4030, 0.1189, 0.0000, 0.2048, 0.4343]], grad_fn=<ReluBackward0>) nn.Sequentialnn.Sequential是一个有序的模块容器。数据按照定义好的方式顺序的通过当前模块。您可以使用顺序容器来组合一个“快捷网络” ,例如:seq_modules . 12345678seq_modules = nn.Sequential( flatten, layer1, nn.ReLU(), nn.Linear(20, 10))input_image = torch.rand(3, 28, 28)logits = seq_modules(input_image) nn.SoftmaxSoftmax激活函数通常用在最后一个线性层,用来返回对数区间介于 [-infty, infty] 中的原始值,这些值最终被传递给 nn.Softmax 模块。 Softmax 激活函数将对应输出区间范围缩放在 [0, 1] 之间,表示模型对每个类别的预测概率。其中,dim 中所有参数指示值求和应该为 1 。 12softmax = nn.Softmax(dim = 1)pred_probab = softmax(logits) 模型的参数神经网络往往非常的复杂,在整个网络的构建过程中,如果可以便捷的将每个部分表示出来,对于训练过程中的优化和修改相对的权重与偏差等都会有非常大的帮助。 子类化 nn.Module 模块可以帮助我们解决这个问题,该模块会自动跟踪模型对象中定义的所有字段,并使用模型的 parameters() 函数或 named_parameters() 函数方法访问所有参数。 在本次示例中,我们遍历每个参数,并预览它们的所有数值参数。 1234print(f"Model structure: {model}\\n\\n")for name, param in model.named_parameters(): print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \\n") 打印输出结果: 1234567891011121314151617181920212223242526272829Model structure: NeuralNetwork( (flatten): Flatten(start_dim=1, end_dim=-1) (linear_relu_stack): Sequential( (0): Linear(in_features=784, out_features=512, bias=True) (1): ReLU() (2): Linear(in_features=512, out_features=512, bias=True) (3): ReLU() (4): Linear(in_features=512, out_features=10, bias=True) ))Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[ 0.0273, 0.0296, -0.0084, ..., -0.0142, 0.0093, 0.0135], [-0.0188, -0.0354, 0.0187, ..., -0.0106, -0.0001, 0.0115]], device='cuda:0', grad_fn=<SliceBackward0>)Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([-0.0155, -0.0327], device='cuda:0', grad_fn=<SliceBackward0>)Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[ 0.0116, 0.0293, -0.0280, ..., 0.0334, -0.0078, 0.0298], [ 0.0095, 0.0038, 0.0009, ..., -0.0365, -0.0011, -0.0221]], device='cuda:0', grad_fn=<SliceBackward0>)Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([ 0.0148, -0.0256], device='cuda:0', grad_fn=<SliceBackward0>)Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[-0.0147, -0.0229, 0.0180, ..., -0.0013, 0.0177, 0.0070], [-0.0202, -0.0417, -0.0279, ..., -0.0441, 0.0185, -0.0268]], device='cuda:0', grad_fn=<SliceBackward0>)Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([ 0.0070, -0.0411], device='cuda:0', grad_fn=<SliceBackward0>) 张量 Tensor通过一张图初步了解常见的多维空间数据的命名方式(来源:PyTorch in 100 Seconds) Tensor 又称 张量,是一种专门的数据结构,与数组和矩阵非常相似。在 PyTorch 中,我们使用张量对比模型的输入和输出以及模型的参数进行编码。 Tensors 类似于 NumPy 中的ndarrays,不同之处在于张量可以在 GPU 或其他硬件加速器上运行。事实上,张量和 NumPy 数组通常可以共享相同的底层内存,从而消除了复制数据的需要。 张量也针对自动微分进行了优化。 12import torchimport numpy as np 初始化张量一般可以通过如下方式初始化 Tensor: 直接通过数据创建 通过NumPy创建 通过继承另一个Tensor的形状和数据类型 使用随机值或常量 下面分别进行介绍: 直接来自数据张量可以直接从已有的数据中创建,数据类型是自动推断的。 12data = [[1, 2], [3, 4]]x_data = torch.tensor(data) 来自另一个 Tensor新建的张量保留参考张量的部分参数(形状,数据类型),除非用显式的方式直接覆盖。 12345x_ones = torch.ones_like(x_data) # retains the properties of x_dataprint(f"Ones Tensor: \\n {x_ones} \\n")x_rand = torch.rand_like(x_data, dtype = torch.float) # overrides the datatype of x_dataprint(f"Random Tensor: \\n {x_rand} \\n") 打印输出: 1234567Ones Tensor: tensor([[1, 1], [1, 1]])Random Tensor: tensor([[0.8823, 0.9150], [0.3829, 0.9593]]) 使用随机值或常量shape 是张量维度的元组表达式。在下面的函数中,它决定了输出张量的维度: 123456789# 定义 Tensor 的维度shape = (2, 3, )rand_tensor = torch.rand(shape)ones_tensor = torch.ones(shape)zeros_tensor = torch.zeros(shape)print(f"Random Tensor: \\n {rand_tensor} \\n")print(f"Ones_Tensor: \\n {ones_tensor} \\n")print(f"Zeros_Tensor: \\n {zeros_tensor}") 打印输出结果: 1234567891011Random Tensor: tensor([[0.3904, 0.6009, 0.2566], [0.7936, 0.9408, 0.1332]])Ones Tensor: tensor([[1., 1., 1.], [1., 1., 1.]])Zeros Tensor: tensor([[0., 0., 0.], [0., 0., 0.]]) 张量的属性Tensor 的属性描述了它们的形状、数据类型 和 存储它们的设备。 tensor.shape tensor.dtype tensor.device 12345tensor = torch.rand(3, 4)print(f"Shape of tensor: {tensor.shape}")print(f"Dtype of tensor: {tensor.dtype}")print(f"Device of tensor: {tensor.device}") 打印输出: 123Shape of tensor: torch.Size([3, 4])Datatype of tensor: torch.float32Device tensor is stored on: cpu 张量的操作科学计算是深度学习领域的根本!PyTorch提供了 100+ 张量运算操作,包括算术运算、线性代数运算、矩阵运算(转职、索引、切片)、采样等。 PyTorch 中的所有逻辑运算都可以通过GPU进行加速运算 123# 将运行设备选择为 GPU (如果你有的话)if torch.cuda.is_available(): tensor = tensor.to("cuda") 类似 NumPy 的索引和切片操作123456Tensor= torch.ones(4, 4)print(f"First row: {tensor[0]}")print(f"First column: {tensor[:, 0]}")print(f"last column: {tensor[..., -1]}")tensor[:, 1] = 0print(tensor) 打印输出: 1234567First row: tensor([1., 1., 1., 1.])First column: tensor([1., 1., 1., 1])Last column: tensor([1., 1., 1., 1])tensor([[1., 0., 1., 1.], [1., 0., 1., 1.], [1., 0., 1., 1.], [1., 0., 1., 1.]]) Tensor 之间的连接torch.cat 可以用于连接指定维度的张量,拥有同样功能的另一个算子是 torch.stack_ (参考:torch.stack_)。 12t1 = torch.cat([tensor, tensor, tensor], dim = 1)print(t1) 张量的算术运算1234567891011121314# 计算两个张量之间的矩阵乘法。其中,y1, y2, y3 拥有相同的参数值# `tensor.T` 返回张量的转置y1 = tensor @ tensor.Ty2 = tensor.matmul(tensor.T)y3 = torch.rand_like(y1)torch.matmul(tensor, tensor.T, out = y3)# 将张量中的元素逐个相乘。z1, z2, z3 具有相同的值z1 = tensor * tensorz2 = tensor.mul(tensor)z3 = torch.rand_like(tensor)torch.mul(tensor, tensor, out = z3) 打印输出: 1234tensor([[1., 0., 1., 1], [1., 0., 1., 1], [1., 0., 1., 1], [1., 0., 1., 1]]) 单一元素张量如果你有一个单一元素的张量,例如希望通过将张量的所有值聚合为一个值,那么可以使用 item() 将其转换为 python 数值: 123agg = tensor.sum()agg_item = agg.item()print(agg_item, type(agg_item)) 打印输出结果: 112.0 <class 'float'> 原地操作(不通过返回的方式进行原地修改)将结果存储在操作数中的操作成为原地操作。它们由 _ 后缀表示。例如:x.copy() 、 x.t_() ,都将直接修改 x。 123print(f"{tensor} \\n")tensor.add_(5)print(tensor) 打印输出: 123456789tensor([[1., 0., 1., 1.], [1., 0., 1., 1.], [1., 0., 1., 1.], [1., 0., 1., 1.]])tensor([[6., 5., 6., 6.], [6., 5., 6., 6.], [6., 5., 6., 6.], [6., 5., 6., 6.]]) Tensor 和 Numpy 二者转换位于 CPU 位置上的 Numpy 数组与 Tensor 可以共享同一个底层的内容空间,更改一个 tensor 会同时修改另一个 tensor 。 Tensor2NumPy_array1234t = torch.ones(5)print(f"t: {t}")n = t.numpy()print(f"n: {n}") 打印对比结果(t 代表 tensor;n 代表 numpy): 12t: tensor([1., 1., 1., 1., 1.])n: [1. 1. 1. 1. 1.] 下一步,改变 tensor 中的值,同时观察 NumPy 中值的变化: 123t.add_(1)print(f"t: {t}")print(f"n: (n)") 打印对比结果(t 代表 tensor;n 代表 numpy): 12t: tensor([2., 2., 2., 2., 2.])n: [2. 2. 2. 2. 2.] NumPy_array2Tensor12n = np.ones(5)t = torch.from_numpy(n) NumPy 数组中的更改回反映在张量(tensor)中 123np.add(n, 1, out = n)print(f"t: {t}")print(f"n: {n}") 12t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)n: [2. 2. 2. 2. 2.] 数据集的相关操作从代码的架构设计上考虑,无论是出于可读性考虑还是出于代码逻辑的模块化管理考虑,我们都希望数据集代码与模型训练代码分离。 在数据预加载上,PyTorch 提供了两个功能函数 torch.utils.data.DataLoader 和 torch.utils.data.Dataset 分别读取预加载的数据和自己的数据。 Dataset 存储样本和对应的标签,并在 DataLoader 范围内包装成一个可迭代对象 Dataset 以便轻松访问样本。 在 PyTorch 函数库中预先提供好了很多可供预加载的数据集(例如:FashionMNIST),这些数据集借助 torch.utils.data.Dataset 子类化,并实现位于特定数据的函数。它们可用于对模型进行原型设计和基准测试。可以通过如下链接访问:Image Datasets,Text Datasets,Audio Datasets 数据加载下面例子是从 TorchVision 加载 Fashion-MNIST 数据集的示例。Fashion-MNIST 由 60000 个训练样本和 10000 个测试样本组成。每个示例都包含一个 28*28 灰度图像和一个来自 10 个类之一的关联标签。 我们通过如下几个参数,对 FashionMNIST Dataset 数据集进行加载: root 是存储训练/测试数据的根目录 train 指定训练或测试数据集 download = TRUE 允许从互联网上搜索并下载数据集,前提是 root 路径下的数据集文件不存在 transform 和 target_transform 分别执行 标定特征 和 标注转换 12345678910111213141516171819import torchfrom torch.utils.data import Datasetfrom torchvision import datasetsfrom torchvision.transforms import ToTensorimport matplotlib.pyplot as plttraining_data = datasets.FashionMNIST( root = "data", train = True, download = True, transform = ToTensor())test_data = datasets.FashionMNIST( root = "data", train = False, download = True, transform = ToTensor()) 数据集迭代和数据可视化使用 Datasets ,我们可以实现像 Python 中列表那样的手动索引 training_data[index]。 使用 matplotlib 可视化训练数据集中的样本进行展示。 1234567891011121314151617181920212223labels_map = { 0: "T-Shirt", 1: "Trouser", 2: "Pullover", 3: "Dress", 4: "Coat", 5: "Sandal", 6: "Shirt", 7: "Sneaker", 8: "Bag", 9: "Ankle Boot",}figure = plt.figure(figsize = (8, 8))cols, rows = 4, 4for i in range(1, cols * rows + 1): sample_idx = torch.randint(len(training_data), size(1,)).item() image, lable = training_data[sample_idx] figure.add_subplot(rows, cols, i) plt.title(lables_map[label]) plt.axis("off") plt.imshow(img.squeeze(), cmap = "gray")plt.show() # 打印出来的输出为数据集图像示例展示 打印输出: 创建自定义的数据集自定义数据集必须含有三个函数: __init__ __len__ __gititem__ 通过下面这个示例实现了解相关函数的使用方法。 FashionMNIST 图像存储在目录 img_dir,其自身的标签单独存储在CSV文件 annotations_file 中 先看代码块部分: 1234567891011121314151617181920212223import osimport pandas as pdimport torchvision.io as read_imageclass CustomImageDataset(Dataset): def __init__(self, annotations_file, img_dir, transform = None, target_transform = None): self.img_labels = pd.read_csv(annotations_file) self.img_dir = img_dir self.transform = transform self.target_transform = target_transform def __len__(self): return len(self.img_labels) def __getitem__(self): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) image = read_image(img_path) label = self.img_labels.iloc[idx, 1] if self.transform: image = self.transform(image) if self.target_transform: label = self.target_transform(label) return image, label _init_init 作为初始化函数,在实例化 Dataset 对象时运行一次。 我们初始化包含图像、注释文件和两个转换的目录 labels.csv 文件的展示效果如下所示: 1234tshirt1.jpg, 0tshirt2.jpg, 0......ankleboot999.jpg, 9 12345def __init__(self, annotations_file, img_dir, transform = None, target_transform = None): self.img_labels = pd.read_csv(annotations_file) self.img_dir = img_dir self.transform = transform self.target_transform = target_transform _len_len 函数返回数据集中的样本数。 12def __len__(self): return len(self.img_labels) _getitem_getitem 函数从给定索引目录的 idx 处返回数据集中的样本。 根据索引,它识别图像在磁盘上的位置,使用 read_image,从 csv 数据中检索相应的标签self.img_labels,调用它们的转换函数(前提是支持转换),并在元组中返回张量图像和相应的标签。 123456789def __getitem__(self): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) image = read_image(img_path) label = self.img_labels.iloc[idx, 1] if self.transform: image = self.transform(image) if self.target_transform: label = self.target_transform(label) return image, label 准备数据并使用 DataLoader 进行训练检索 Dataset 数据集的特征,并一次标记一个样本。 在训练模型的过程中,我们通常希望通过“小批量”的方式传递样本,在新一轮 epoch 下数据 reshuffle(洗牌) 减少模型过拟合,并使用 Python 中的 multiprocessing 函数来加快数据检索的速度。 DataLoader 是一个可迭代的对象,它通过一个简单的 API 为我们抽象了这种复杂性。 1234from torch.utils.data import DataLoadertrain_dataloader = DataLoader(training_data, batch_size = 64, shuffle = True)test_dataloader = DataLoader(test_data, batch_size = 64, shuffle = True) 遍历 DataLoader通过上面的步骤,我们已经将数据集加载到了 DataLoader 并可以根据我们的需要来遍历该数据集。 后续程序中每一次迭代都会返回一批 train_features 和 train_labels,每批结果中都包含 batch_size = 64 特征和标签。 在上面代码块中,我们指定了 shuffle = True ,在我们遍历了所有批次后,数据会被洗牌(目的是为了更细粒度地控制数据加载顺序,可参考Samplers) 123456789# Display image and label.train_features, train_labels = next(iter(train_dataloader))print(f"Feature batch shape: {train_features.size()}")print(f"Labels batch shape: {train_labels.size()}")img = train_features[0].squeeze()label = train_labels[0]plt.imshow(img, cmap = "gray")plt.show() # 输出数据集中的图片print(f"Label: {label}") 转换数据的格式不总是按照训练机器学习算法所需要的格式出现的,因此我们需要通过转换来对数据进行一系列操作使得其适合于机器学习任务。 所有 TorchVision 数据集都具有两个参数: transform 用于修改标签 target_transform 接受包含转换逻辑的可调用对象 在 torchvision.transforms 中提供了几个开箱即用的转换格式。 FashionMNIST 特征采用 PIL Image 格式,标签为整数。 在训练任务开始前,我们需要将特征处理为归一化之后的张量。 为了进行这些转换,需要使用 ToTensor 和 Lambda 函数方法。 1234567891011import torchfrom torchvision import datasetsfrom torchvision.transforms import ToTensor, Lambdads = datasets.FashionMNIST( root = "data", train = True, download = True, transform = ToTensor(), target_transform = Lambda(lambda y: torch.zero(10, dtype = torch.float).scatter_(0, torch.tensor(y), value = 1))) 打印输出: 123456789101112131415161718192021222324252627282930313233343536373839404142434445Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gzDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to data/FashionMNIST/raw/train-images-idx3-ubyte.gz 0%| | 0/26421880 [00:00<?, ?it/s] 0%| | 65536/26421880 [00:00<01:12, 362364.70it/s] 1%| | 229376/26421880 [00:00<00:38, 680532.51it/s] 3%|2 | 786432/26421880 [00:00<00:11, 2194389.90it/s] 7%|7 | 1933312/26421880 [00:00<00:05, 4185622.75it/s] 17%|#6 | 4423680/26421880 [00:00<00:02, 9599067.02it/s] 25%|##5 | 6717440/26421880 [00:00<00:01, 11175748.57it/s] 34%|###4 | 9109504/26421880 [00:01<00:01, 14174360.51it/s] 44%|####3 | 11567104/26421880 [00:01<00:01, 14358310.56it/s] 53%|#####2 | 13959168/26421880 [00:01<00:00, 16463421.66it/s] 62%|######2 | 16449536/26421880 [00:01<00:00, 15864345.49it/s] 71%|#######1 | 18776064/26421880 [00:01<00:00, 17449238.29it/s] 81%|######## | 21397504/26421880 [00:01<00:00, 16758523.84it/s] 90%|########9 | 23691264/26421880 [00:01<00:00, 18055860.39it/s]100%|##########| 26421880/26421880 [00:01<00:00, 13728491.83it/s]Extracting data/FashionMNIST/raw/train-images-idx3-ubyte.gz to data/FashionMNIST/rawDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gzDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw/train-labels-idx1-ubyte.gz 0%| | 0/29515 [00:00<?, ?it/s]100%|##########| 29515/29515 [00:00<00:00, 327895.25it/s]Extracting data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to data/FashionMNIST/rawDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gzDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz 0%| | 0/4422102 [00:00<?, ?it/s] 1%|1 | 65536/4422102 [00:00<00:11, 363267.41it/s] 5%|5 | 229376/4422102 [00:00<00:06, 683985.17it/s] 19%|#8 | 819200/4422102 [00:00<00:01, 2304448.10it/s] 33%|###3 | 1474560/4422102 [00:00<00:00, 2999709.36it/s] 83%|########2 | 3670016/4422102 [00:00<00:00, 7976134.77it/s]100%|##########| 4422102/4422102 [00:00<00:00, 5985529.02it/s]Extracting data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to data/FashionMNIST/rawDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gzDownloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz 0%| | 0/5148 [00:00<?, ?it/s]100%|##########| 5148/5148 [00:00<00:00, 39473998.16it/s]Extracting data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw ToTensor()ToTensor 将 PIL 图像 或 NumPy ndarray 转换为 FloatTensor. 并在[0., 1.] 范围内缩放图像的像素空间。 Lambda 转换Lambda 函数允许任意用户定义 lambda 函数。在这里,我们定义了一个函数,将整数转换为 one-hot 编码的张量。 Lambda首先创建一个大小为10(我们数据集中的标签数量)的零向量,并调用 scatter_ 函数在索引 y 上分配标签 value = 1。 12target_transform = Lambda(lambda y:torch.zeros( 10, dtype = torch.float).scatter_(dim = 0, index = torch.tensor(y), value = 1)) 阅读延伸:torchvision.transforms API (核心内容)自动微分(TORCH.AUTOGRAD)在训练神经网络时,常用的算法是反向传播。在该算法中,参数(模型权重)根据损失函数相对于给定参数的梯度进行调整。 为了计算这些梯度,PyTorch 有一个内置的用于自动计算微分方程的引擎,称为 torch.autograd . 它支持任何计算图的梯度自动运算。 下面距离一个最简单的单层神经网络,其中包含输入 x、参数 w 和 b,以及一些损失函数。可以在 PyTorch 中按以下方式定义它: 12345678import torchx = torch.ones(5) # input tensory = torch.zeros(3) # expected outputw = torch.randn(5, 3, requires_grad = True)b = torch.randn(3, requires_grad = True)z = torch.matmul(x, w) + bloss = torch.nn.functional.binary_cross_entropy_with_logits(z, y) 张量、函数、计算图上一小节的代码框中实现的损失函数计算流程如下图所示: 在上面网络中,w 和 b 是我们需要优化的参数。因此,我们需要能够计算这些变量损失函数的梯度。 为了做到这些点,我们设置了这些张量的 requires_grad 函数来定义其属性。 设置张量的值有两种方式:一种是在生成张量的时候使用 requires_grad 进行初始化设置;另一种是在后续使用 x.requires_grad_(True) 函数。 用来构造计算图的函数实际上是类 Function 的对象。该对象指导如何计算正向函数,以及如何在反向传播步骤中计算其导数。对向后传播函数的引起存储在张量的属性中的 grad_fn。您可以在PyTorch的官方文档中找到更多信息 Function。 12print(f"Gradient function for z = {z.grad_fn}")print(f"Gradient function for loss = {loss.grad_fn}") 打印输出: 12Gradient function for z = <AddBackward0 object at 0x7d800ac85840>Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x7d800ac85ea0> 梯度计算为了优化神经网络中的参数权重,我们需要计算损失函数相对于参数的导数,即我们需要在固定 x 和 y 值的情况下,求出 $\\frac{\\partial loss}{\\partial w}$ 和 $\\frac{\\partial loss}{\\partial b}$。 为了求出上面的导数,我们需要调用函数 loss.backward(),然后从 w.grad 和 b.grad 中检索权重和偏置的数值。 123loss.backward()print(w.grad)print(b.grad) 打印输出: 123456tensor([[0.3287, 0.0101, 0.0988], [0.3287, 0.0101, 0.0988], [0.3287, 0.0101, 0.0988], [0.3287, 0.0101, 0.0988], [0.3287, 0.0101, 0.0988]])tensor([0.3287, 0.0101, 0.0988]) 可以通过将参数 requires_grad 的 grad 属性设置为 True 来或测计算图的叶节点属性。对于计算图中的所有其他节点,梯度将不可用。 PyTorch考虑性能上的原因,在给定图形上只能调用 backward 调用一次梯度计算。如果我们需要再同一个图上执行多个 backward 调用,我们需要参数传递给 retain_graph = True,再调用 backward 。 禁用梯度追踪默认情况下,所有张量都在 require_grad = True 跟踪其计算历史并支持梯度计算。但是,在某些情况下,只想将模型应用于默写输入数据时,即我们只想通过网络进行前向计算。我们可以通过将计算代码加上 torch.no_grad() 还书,用来停止跟踪计算。 123456z = torch.matmul(x, w) +bprint(z.requires_grad) # Truewith torch.no_grad(): z = torch.matmul(x, w) + bprint(z.requires_grad) # False 另一种方法是在张量上使用 detach() 方法: 123z = torch.matmul(x, w) + bz_det = z.detach()print(z_det.requires_grad) # False 考虑要禁用梯度跟踪的情况: 将神经网络中的某些参数标记为冻结参数 为了优化计算速度,在只进行前向传播时禁用梯度跟踪的选项 计算图部分扩展阅读(留白,前面的区域以后再探索吧~) 可选阅读:张量梯度 和 雅阁比积(Jacobian_Products)(留白,前面的区域以后再探索吧~) (重要)优化模型参数在拥有了模型和数据之后,就可以通过优化数据参数来训练、验证和测试我们的模型了。训练模型是一个带带过程,每次迭代中,模型都会对输出进行*猜测*,计算其猜测中的误差,计算其猜测中的误差(损失),收集误差对参数的导数(上一节中进行的工作),并使用梯度下降这些参数。 更详细的视频讲解,可以参考 backpropagation from 3Blue1Brown 先决条件代码超参数优化循环全过程数据跟踪保存、加载和使用模型保存/加载模型权重通过模型的形状参数进行保存/加载在 PyTorch 中保存和加载常规 Checkpoint介绍设置步骤1.导入加载数据所需要的库2.定义和初始化神经网络3.初始化优化器4.保存常规检查点5.加载常规检查点从 Checkpoint 中加载 nn.Module 的技巧活用 torch.load(mmap = True)活用 torch.device("meta")活用 load_state_dict(assign = True)结论","categories":[{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"}],"tags":[{"name":"PyTorch","slug":"PyTorch","permalink":"https://zade23.github.io/tags/PyTorch/"}]},{"title":"工具网站","slug":"工具网站","date":"2023-10-18T09:04:52.000Z","updated":"2024-03-21T06:41:41.700Z","comments":true,"path":"2023/10/18/工具网站/","link":"","permalink":"https://zade23.github.io/2023/10/18/%E5%B7%A5%E5%85%B7%E7%BD%91%E7%AB%99/","excerpt":"","text":"内容分类 学习资源网站 aigc llm tts rl diffusion huggingface awesome 数学基础 机器学习 深度学习/模型训练 抽象概念可视化 传统算法 汇编语言 电子书 工具网站 在线大语言模型 在线api 数据集 音频处理 视频处理 图像处理 设计 下载工具 格式压缩 内容分类学习资源网站aigc 【关于AIGC的各种精选教程和资源,既适合初学者也适合进阶AI爱好者】github.com/luban-agi/Awesome-AIGC-Tutorials/blob/main/README_zh.md llm 【收集各种垂直领域的大语言模型】Awesome Domain LLM 【关于大型语言模型(LLM)的一切,包括了LLMs的入门、微调方法、多模态模型、稳定扩散、注意力机制优化和数据效率等方面的信息,从nanoGPT到LoRA、QLoRA、RLHF到CLIP等多模态模型】Everything-about-LLMs 【提示工程实例:用实际项目学习提示工程技术,从大型语言模型获得更好的结果,内容涵盖零样本和少样本提示,分隔符,步骤编号,角色提示,思维链(CoT)提示等】 《Prompt Engineering: A Practical Example – Real Python》 tts 【如何在浏览器中处理音视频】Web 音视频系列 【基于华为诺亚方舟实验室Grad-TTS的Grad-SVC】https://github.com/PlayVoice/Grad-SVC 【台大课程《深度学习音乐分析与生成》资料】 github.com/affige/DeepMIR 【大型音频模型相关文献资源列表】 github.com/EmulationAI/awesome-large-audio-models rl 【Generative Agents with Llama2】https://github.com/rlancemartin/generative_agents diffusion 【扩散模型相关论文资源列表,涵盖了文本到视频生成、文本引导视频编辑、个性化视频生成、视频预测等方面】 github.com/ChenHsing/Awesome-Video-Diffusion-Models 【3D Diffusion相关文献列表】 github.com/cwchenwang/awesome-3d-diffusion huggingface 【Hugging Face - Learn】https://huggingface.co/learn awesome 【收集各种生成式 AI 的教程】Awesome AIGC Tutorials 数学基础 【给程序员的线性代数指南】Linear Algebra for programmers 机器学习 【ML Papers Explained:机器学习论文解析】 github.com/dair-ai/ML-Papers-Explained 深度学习/模型训练 【Batched LoRAs:通过同一批次的多个 LoRA 路由推理,最大化 GPU 利用率】Batched LoRAs - batched 抽象概念可视化 【机器学习MachineLearning】www.r2d3.us 【波Waveforms】Let’s Learn About Waveforms 传统算法 【开源算法库Algorithms】The Algorithms 汇编语言 Python 【Python官方文档】Python语法官方文档 【从Python内置函数理解Python】Understanding all of Python, through its builtins 【GraphLearn-for-PyTorch(GLT):PyTorch图学习库,使分布式 GNN 训练和推理变得简单高效】’GraphLearn-for-PyTorch(GLT) A graph learning library for PyTorch that makes distributed GNN training and inference easy and efficient C++ 【C++在线学习】Learn C++ – Skill up with our free tutorials 电子书 (图书馆)【zlibrary】Z-Library – the world’s largest e-book library. Your gateway to knowledge and culture. 【免费书《人工智能:计算Agent基础,(第三版)》】《Artificial Intelligence: Foundations of Computational Agents, 3rd Edition》David L. Poole and Alan K. Mackworth (2023) 【配有动画的数据结构与算法电子书】Hello 算法 工具网站在线大语言模型 百度(文心一言) 抖音(云雀大模型) 智谱AI(GLM大模型) 中科院(紫东太初大模型) 百川智能(百川大模型) ChatGPT Poe 讯飞星火 谷歌bard 阿里通义千问 在线api 【收集各种 AI 工具和资源】AIHub 【免费AI API列表】free-ai-apis 【作文批改:使用GPT4对雅思托福作文判分和批改】https://www.essay.art/ 数据集 【OpenDataLab 为国产大模型提供高质量的开放数据集】OpenDataLab 音频处理 【Whisper:英语音频转成文本的在线工具】Whisper Web 视频处理 【Spikes Studio】https://spikes.studio/ 图像处理 【在线体验-图像分割算法Meta-SegementAnything】Segment Anything 【SDXL在线体验】StableDiffusion XL 体验站 【nutsh:旨在通过人工反馈进行视觉学习的平台,具有用户友好的界面和 API,支持一系列视觉模式、多样化的人工输入方法以及基于人工反馈的学习机制】Nutsh 设计 【中文的图标搜索引擎,作者利用 ChatGPT 翻译了 Iconify 的 18 万个图标名】yesicon 【JupyterCAD - 用于3D几何建模的JupyterLab扩展】https://github.com/QuantStack/jupytercad 【图标搜索引擎收入了10万+的图标】Iconbuddy — 180K+ open source icons 【AI漫画|可选择漫画风格和页面布局】AI Comic Factory - a Hugging Face Space by jbilcke-hf 【Figma在线版】Figma 下载工具 【YouTube视频解析下载 】snapsave.io 【bilibili视频下载】bili.iiilab 格式压缩 【图片压缩软件】Topspeed Image Compressor 在线压缩","categories":[{"name":"杂项","slug":"杂项","permalink":"https://zade23.github.io/categories/%E6%9D%82%E9%A1%B9/"}],"tags":[{"name":"工具网站","slug":"工具网站","permalink":"https://zade23.github.io/tags/%E5%B7%A5%E5%85%B7%E7%BD%91%E7%AB%99/"}]},{"title":"Python元组集合字典","slug":"Python元组集合字典","date":"2023-10-18T08:20:18.000Z","updated":"2024-06-18T09:49:44.600Z","comments":true,"path":"2023/10/18/Python元组集合字典/","link":"","permalink":"https://zade23.github.io/2023/10/18/Python%E5%85%83%E7%BB%84%E9%9B%86%E5%90%88%E5%AD%97%E5%85%B8/","excerpt":"","text":"1. 元组 1.1 元组的初始化 1.2 元组的解包 1.3 元组的其他操作 2. 集合 2.1 集合的初始化 2.2 集合的常用操作 2.3 使用for循环遍历集合 3. 字典 3.1 字典的初始化 3.2 字典的常用操作 3.3 使用for循环遍历字典 4. 作业扩展 1. 元组元组跟 列表 类似,只是不支持动态添加、删除元素,以及不能修改元素。 1.1 元组的初始化元组需要用小括号括起来,中间的元素用逗号隔开。 注意:如果初始化只包含一个元素的元组,需要在该元素后添加逗号。 1234567a = () # 初始化一个空元组b = (1, 2) # 含有2个整数的元组c = 6, "Python", 3.14 # 小括号可以省略,等价于(6, "Python", 3.14)d = (5,) # 注意不能写成(5),(5)表示整数5e = 5, # 等价于(5,)print(a, b, c, d, e) 1.2 元组的解包123t = 12345, 54321, "Hello!" # 初始化一个元组x, y, z = t # 将元组解包,将元组内的三个值按顺序赋值给x、y、zprint(x, y, z) 所以,判断语句中的交换操作,本质上是元组的解包: 12a, b = 3, 4 # 将元组(3, 4)解包,分别赋值给a、ba, b = b, a # 将元组(b, a)解包,分别赋值给a、b 同样地,函数中函数返回多个值,本质上也是返回了一个元组: 1234567def calc(x, y): return x + y, x * y # 等价于 return (x + y, x * y)x, y = 3, 4s, p = calc(x, y) # 将(x + y, x * y)解包,分别赋值给s、pprint(s, p) 1.3 元组的其他操作元组的下标访问元素、循环遍历、切片、加法和乘法运算等操作,都与列表相同。 2. 集合集合是Python中最常用的数据结构之一,用来存储不同元素。注意,集合中的元素是无序的。 2.1 集合的初始化创建集合用花括号或set()函数。注意:创建空集合只能用set(),不能用{},因为{}创建的是空字典,会在下一小节里介绍字典。 集合常见的初始化方式: 123456789101112131415basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} # 会自动去除重复元素print(basket) # 重复的元素已经去除了s = set() # 初始化一个空列表print(s)a = [1, 2, 1, 3, 1]b = set(a) # 将列表转化成集合,一般是为了去重。c = list(b) # 将集合转化回列表print(b, c)x = "abracadabra"a = set(x) # 将字符串中的每个字符存到集合中b = str(a) # 注意,这里并不能将集合转化回原字符串,而是用格式化表示集合中的内容print(a, b) 2.2 集合的常用操作假设a表示一个集合。 12345len(a) 返回集合中包含的元素数量。a.add(x) 在集合中添加一个元素。a.remove(x) 删除集合中的x,如果x不存在,则报异常。a.discard(x) 删除集合中的x,如果x不存在,则不进行任何操作。x in a 判断x是否在a中。 例如: 1234567891011121314a = {1, 2, 3}print(len(a)) # 输出3a.add(4)print(a) # 输出 {1, 2, 3, 4},注意集合中的元素是无序的。a.remove(2)print(a) # 输出 {1, 3, 4}a.remove(5) # 因为5不存在,所以会报异常a.discard(5) # 因为5不存在,所以不进行任何操作print(a) # {1, 3, 4} 2.3 使用for循环遍历集合类似于列表,集合也可以用for ... in ...的形式遍历。例如: 1234a = {1, 2, 3}for x in a: # 循环遍历整个集合 print(x, end=' ') 3. 字典字典是Python中最常用的数据结构之一,用来存储映射关系。注意,字典中的元素是无序的。 不同于列表,字典是以key进行索引的,可以将每个key映射到某个value。key可以是任何不可变类型,常用可以作为key的类型有数字和字符串。列表因为是可变的,所以不能作为key。value可以是任意类型。 3.1 字典的初始化创建字典用花括号或dict()函数。 12345678910tel = {'jack': 4098, 'sape': 4139} # 创建一个字典print(tel) # 输出 {'jack': 4098, 'sape': 4139}a = dict() # 创建一个空字典a[123] = "abc" # 在字典中插入一个key-value对a[456] = "def" # 在字典中插入一个key-value对print(a) # 输出 {123: 'abc', 456: 'def'}b = list(a) # 将字典的关键字转化成列表print(b) # 输出[123, 456] 3.2 字典的常用操作假设a表示一个字典。 len(a):返回字典中的元素对数。 a[x]:获取关键字x对应的值,如果x不存在,会报异常。 a.get(x):获取关键字x对应的值,如果x不存在,会返回None,不会报异常。 a.get(x, y):获取关键字x对应的值,如果x不存在,会返回默认值y,不会报异常。 a[x] = y:在字典中插入一对元素,如果关键字x已存在,则将它之前映射的值覆盖掉。 del a[x]:删除关键字x对应的元素对,如果x不存在,会报异常。 x in a:检查字典中是否存在关键字x。 x not in a:检查字典中是否不存在关键字x。 a.keys():返回字典的所有key。 a.values():返回字典的所有value。 a.items():返回字典的所有由key和value组成的元组。 例如: 1234567891011121314151617181920a = {'abc': 1, 'def': 2, 'python': 3} # 初始化一个字典print(len(a)) # 输出3print(a['def']) # 输出2print(a.get('def')) # 输出2print(a.get('xyz', 5)) # 因为'xyz'不存在,所以输出默认值5a['hello'] = 4 # 插入一对元素 'hello' -> 4print(a) # 输出{'abc': 1, 'def': 2, 'python': 3, 'hello': 4}a['def'] = 5 # 更新'def'映射的值print(a['def']) # 输出5del a['python'] # 删除关键字'python'print(a) # 输出{'abc': 1, 'def': 5, 'hello': 4}print('hello' in a) # 输出Trueprint(a.keys()) # 输出dict_keys(['abc', 'def', 'hello'])print(a.values()) # 输出dict_values([1, 5, 4])print(a.items()) # 输出dict_items([('abc', 1), ('def', 5), ('hello', 4)]) 3.3 使用for循环遍历字典类似于列表,字典也可以用for ... in ...的形式遍历。例如: 1234567891011121314151617a = {'abc': 1, 'def': 2, 'python': 3} # 初始化一个字典for k in a: # 遍历key print(k, end=' ')print() # 输出回车for k in a.keys(): # 遍历key print(k, end=' ')print() # 输出回车for v in a.values(): # 遍历value print(v, end=' ')print() # 输出回车for k, v in a.items(): # 遍历key-value对 print("(%s, %d) " % (k, v), end=' ')print() # 输出回车 4. 作业扩展map()也可以用for ... in ...的形式遍历。例如:for x in map(int, input().split())可以遍历一行内用空格隔开的每个整数。","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"Python字符串","slug":"Python字符串","date":"2023-10-18T08:18:06.000Z","updated":"2024-03-21T02:04:55.828Z","comments":true,"path":"2023/10/18/Python字符串/","link":"","permalink":"https://zade23.github.io/2023/10/18/Python%E5%AD%97%E7%AC%A6%E4%B8%B2/","excerpt":"","text":"1. 字符与整数的联系——ASCII码 2. 字符串常量的写法 3. 表示特殊字符——转义 4. 访问字符串中的每个字符 5. 使用循环语句遍历字符串 6. 字符串的切片操作 7. 字符串的复制 8. 字符串的运算 9. 字符串的常用操作 10. 更复杂的格式化输出 11. 作业题扩展内容 1. 字符与整数的联系——ASCII码每个常用字符都对应一个-128 ~ 127的数字,二者之间可以相互转化。注意:目前负数没有与之对应的字符。 ord()函数可以求一个字符的ASCII码。注意输入是一个字符,而不是字符串。 chr()函数可以将一个ASCII码转化成对应的字符。 12345c = 'a'print(ord(c))a = 66print(chr(a)) 常用ASCII值:'A'- 'Z'是65 ~ 90,'a' - 'z'是97 - 122,0 - 9是48 - 57。 注意:虽然字符可以跟整数相互转化,但在Python中,字符不能参与数值运算,这一点跟C++、Java等语言是不同的。 2. 字符串常量的写法在Python中,字符串既可以用单引号来表示,也可以用双引号来表示,二者完全相同。这一点跟C++、Java等编程语言是不同的,在这些编程语言中,用单引号来表示字符,用双引号来表示字符串。 12345a = "Hello World" # 双引号写法print(a)b = 'Hello World' # 单引号写法print(b) 两个或多个字符串常量并排写,会被自动合并,例如: 12a = "My " "name " "is yxc."print(a) # 输出:My name is yxc. 一个字符串如果包含多行,可以采用"""..."""或者'''...'''的初始化方式,字符串中将自动包含回车字符,例如: 1234a = """Usage: thingy [OPTIONS] -h Display this usage message -H hostname Hostname to connect to"""print(a) 会得到如下输出: 123Usage: thingy [OPTIONS] -h Display this usage message -H hostname Hostname to connect to 3. 表示特殊字符——转义当想在字符串中表示特殊字符时,一般可以在字符前加反斜杠\\。 常见需要转义的字符有: 转义字符 含义 ASCII码(十进制) \\n 回车 10 `` 代表一个反斜杠\\ 92 `“` 表示一个双引号 34 `‘` 表示一个单引号 39 例如: 1print("My name is:\\n\\"yxc!\\"") 会得到如下输出: 12My name is:"yxc!" 另外,如果想输出单引号,也可以用双引号来表示,反之亦然。例如: 12print("My name is 'yxc!'") # 输出:My name is 'yxc!'print('My name is "yxc!"') # 输出:My name is "yxc!" 4. 访问字符串中的每个字符可以通过下标读取字符串中的每个字符,下标从0开始,也可以是负数,负数下标表示的是除以字符串长度的余数对应的位置。 负数下标相当于将字符串首位相接,然后从0往前数。 如果字符串长度是 nn,那么下标只能取 −n∼n−1−n∼n−1 之间的整数,超出范围会报错。 注意:字符串中的每个字符不能修改。 例如: 1234a = "Hello World"print(a[0], ord(a[5])) # 输出H 32a[2] = 'x' # 会报错,字符串不能修改 5. 使用循环语句遍历字符串可以通过下标访问,例如: 1234s = "acwing"for i in range(6): print(s[i], end=' ')print() # 输出回车 可以通过for ... in ...直接遍历,例如: 123for c in "python": print(c, end=' ') # 注意c本身也是字符串类型print() # 输出回车 6. 字符串的切片操作字符串的切片操作会返回一个新字符串。用法: a[begin:end] 会返回包含a[begin], a[begin + 1], ..., a[end - 1]的字符串。 省略begin时,begin的默认值是0。 省略end时,end的默认值是字符串长度。 如果begin或end是负数,表示的是除以字符串长度后的余数。 例如: 1234567a = "ABCDE"print(a[1:4]) # 输出BCDprint(a[1:]) # 输出BCDEprint(a[:4]) # 输出ABCDprint(a[:]) # 输出ABCDEprint(a[-4:-1]) # 等价于print(a[1:4]) 注意:字符串的切片不支持写操作。 例如: 12a = "ABCDE"a[1:4] = "XY" # 会报错,字符串不能修改 7. 字符串的复制跟列表不同,字符串的每次复制操作,都会得到一个全新的字符串。 8. 字符串的运算 字符串的加法可以将两个字符串拼接起来,得到一个新字符串。 字符串乘以一个整数,可以将若干个自身拼接起来,得到一个新字符串。 字符串支持比较运算符,按字典序比较大小。即如果两个字符串相同,则表示相等;否则找到两个字符串从左到右数第一个不一样的字符,哪个字符串的字符的ASCII码小,哪个字符串的字典序就小;另外空字符比任何字符都小。 例如: 12345678910111213a = "Hello "b = "World"c = a + bprint(c) # 输出Hello Worldd = a * 3print(d) # 输出Hello Hello Helloe = a * 3 + "World"print(e) # 输出Hello Hello Hello Worldprint(a <= b) # 按字典序比较大小,输出Trueprint("123" > "22") # 按字典序比较大小,输出False 9. 字符串的常用操作假设s是一个字符串,则: len(s)返回字符串长度。 s.split(sep)返回一个字符串列表。如果给出了sep就按sep分隔;如果没给出,则会按空格分隔,但连续的空格会被视为单个分隔符,而且会忽略首尾的空白字符。 s.strip()将首尾的空白字符删除。 s.replace(old, new)将s中所有的old子串都改成new。 s.find("abc")查询某个子串在s中第一次出现的下标;如果不存在,则返回-1。 s.startswith(prefix)判断prefix是否为s的前缀。 s.endswith(suffix)判断suffix是否为s的后缀。 s.lower()将所有大写字母变成小写。 s.upper()将所有小写字母变成大写。 s.join(a),a是一个字符串列表,这个函数返回将a中的字符用s作为分隔符拼接起来的结果。 注意:返回的所有字符串都是新字符串,原字符串不变。 例如: 123456789101112131415161718s1 = "abc def xyz"print(len(s1)) # 输出11print(s1.split()) # 输出['abc', 'def', 'xyz']s2 = " abc abc "print(s2.strip()) # 输出abc abcprint(s2.replace("abc", "*")) # 输出 * *print(s2.find("abc"), s2.find("xyz")) # 输出2 -1s3 = "Abc deF"print(s3.startswith("Ab")) # 输出Trueprint(s3.endswith("deF")) # 输出Trueprint(s3.lower()) # 输出abc defprint(s3.upper()) # 输出ABC DEFs4 = ", "a = ["aa", "bb", "cc"]print(s4.join(a)) # 输出aa, bb, cc 10. 更复杂的格式化输出当需要用到更复杂的格式化输出时,现查即可。可以参考: 更复杂的输出格式 printf 风格的字符串格式化 11. 作业题扩展内容 作业的评测器会自动忽略每一行的行末空格,所以行末输出多余空格也视为正确。 s.isdigit():当字符串s不是空字符串,且包含的所有字符都是数字时返回True,否则返回False。 a, b = ["abc", "def"]这种写法可以将"abc"赋值给第一个变量a,将"def"赋值给第二个变量b。 s.rfind("abc")查询某个子串在s中最后一次出现的下标;如果不存在,则返回-1。 当不知道读入的具体行数时,可以采用如下方法一次性读取所有行: 1234from sys import stdinfor line in stdin.readlines(): print(line.strip()) # strip()是为了去掉行末的回车","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"经济学——流动性陷阱","slug":"经济学——流动性陷阱","date":"2023-10-16T02:37:26.000Z","updated":"2024-03-21T06:37:38.879Z","comments":true,"path":"2023/10/16/经济学——流动性陷阱/","link":"","permalink":"https://zade23.github.io/2023/10/16/%E7%BB%8F%E6%B5%8E%E5%AD%A6%E2%80%94%E2%80%94%E6%B5%81%E5%8A%A8%E6%80%A7%E9%99%B7%E9%98%B1/","excerpt":"","text":"流动性陷阱——名词解释 白话解释 维基百科解释 换个视角看问题 国家层面 人民层面 流动性陷阱——名词解释白话解释流动性陷阱是一个经济学概念,出现在名义利率已经接近零或非常低,而央行进一步减少利率仍然不能刺激投资和消费的情况。在这种情境下,货币政策的传统工具,如购买国债以增加货币供应量,可能变得无效,因为商业银行和公众可能选择持有现金而不是投资或消费。这是因为他们预期未来的回报率很低,或对未来经济有所担忧。 维基百科解释流动性陷阱(英语:liquidity trap),是英国经济学家凯恩斯提出的概念,又称凯恩斯陷阱。 在凯恩斯主义经济学中,流动性陷阱是指一个当货币政策完全没法刺激经济的情况,无论降低利率抑或增加货币供应量也没有作用。典型的流动性陷阱在当预期经济不景气出现(例如通货紧缩、总需求不足、发生战争),使持有流动资产的人不愿意投资。 换个视角看问题 GPT4的回答 国家层面站在国家的角度: 财政政策:当货币政策变得无效时,国家可以使用财政政策,如增加公共支出、减税或直接向家庭提供资金,以刺激经济。 结构性改革:国家可以采取长期的结构性改革,如改善教育、健康、基础设施,以增加生产力和潜在产出。 消除不确定性:政府可以采取措施减少经济中的不确定性,这可能包括提供更清晰的经济前景、稳定的政策环境等。 合作:与其他国家合作,通过国际贸易和投资来促进增长。 人民层面站在人民的角度: 储蓄与投资:在经济不确定性和低回报期望的背景下,人们可能会倾向于增加储蓄。然而,他们也应考虑多样化投资,可能在国外或不同的资产类别,以获得更好的回报。 消费:尽管流动性陷阱可能导致对未来的担忧,但是长时间的压抑消费可能进一步伤害经济。如果有能力,人民可以考虑适度的消费。 教育与技能提升:在经济低迷的时期,提高自己的技能和教育背景可以帮助人们为未来更好的机会做好准备。 避免过度债务:在低利率环境下,尽管贷款更便宜,但人们应避免过度债务,以确保在经济形势变化时能够应对。 总之,流动性陷阱是一个复杂的经济问题,需要多方面的策略来应对。","categories":[{"name":"杂项","slug":"杂项","permalink":"https://zade23.github.io/categories/%E6%9D%82%E9%A1%B9/"}],"tags":[{"name":"经济学小知识","slug":"经济学小知识","permalink":"https://zade23.github.io/tags/%E7%BB%8F%E6%B5%8E%E5%AD%A6%E5%B0%8F%E7%9F%A5%E8%AF%86/"}]},{"title":"Python函数","slug":"Python函数","date":"2023-10-12T10:22:54.000Z","updated":"2024-06-18T09:49:46.272Z","comments":true,"path":"2023/10/12/Python函数/","link":"","permalink":"https://zade23.github.io/2023/10/12/Python%E5%87%BD%E6%95%B0/","excerpt":"","text":"1. 函数基础 1.1 编写函数 1.2 调用函数 1.3 形参和实参 1.3.1 形参的初始化方式 1.3.2 带默认值的形参 1.3.3 其它参数写法 1.4 变量的作用域 2. 参数传递 2.1 值传递 2.2 引用传递 3.return语句 4.lambda表达式 5. 函数递归 Python中函数的用法非常多,80%的用法不常用,20%的用法常用。大家不要把精力浪费在背完所有用法上,而要把主要精力放到最常用的20%的用法和代码逻辑上,至于另外80%不常用的用法,边用边查就行。 1. 函数基础Python中一个典型的函数定义包括以下部分:关键字def、函数名称、由0个或多个形参组成的列表以及函数体。 1.1 编写函数我们来编写一个求阶乘的函数。例如: 12345def fact(n): res = 1 for i in range(1, n + 1): res *= i return res 函数名称是fact,给它传入一个n,会返回n的阶乘。return语句负责结束函数并返回res的值。 1.2 调用函数123print("我们要计算5的阶乘,答案是:")print(fact(5)) # 输出 120print("计算结束啦!") 函数的调用完成两项工作: 用实参初始化函数对应的形参 将控制权转移给被调用的函数 此时,代码原本的执行顺序被暂时中断,被调函数开始执行。等被调用函数执行完后,再继续执行之前的代码。 1.3 形参和实参实参指调用函数时传入的变量或常量,形参指定义函数时参数列表里的变量。 形参列表可以为空,例如: 1234def f(): print("Hello World")f() # 输出 Hello World 1.3.1 形参的初始化方式调用函数时会用实参去初始化形参,初始化的顺序有两种: 第一种是用位置实参来初始化形参。顾名思义,实参会按位置关系来初始化形参,第一个实参初始化第一个形参,第二个实参初始化第二个形参,依此类推。形参和实参的个数必须匹配。例如: 123456789def f(a, b, c, d): print("a =", a, end=", ") print("b =", b, end=", ") print("c =", c, end=", ") print("d =", d)f(1, True, "Python", 4.2) # 输出 a = 1, b = True, c = Python, d = 4.2f(1, True, "Python", 4.2, 3) # 会报错,因为实参个数多于形参f(1, True, "Python") # 会报错,因为实参个数少于形参 第二种是用关键字实参来初始化形参。此时实参不再按位置关系来初始化形参,而是按变量名初始化。例如: 12# f()的定义如上所述f(b=1, c=True, a="Python", d=4.2) # 输出 a = Python, b = 1, c = True, d = 4.2 两种方式也可以混合使用,但是位置实参一定要放到关键字实参之前。例如: 123# f()的定义如上所述f(1, 2, d="Python", c=4.2) # 输出 a = 1, b = 2, c = 4.2, d = Pythonf(1, b=3, "Python", d=4.2) # 会报错,因为位置实参位于关键字实参后面了。 1.3.2 带默认值的形参形参也可以设置默认值,但所有带默认值的形参必须是最后几个。当某些形参没有被初始化时,这些形参会使用默认值。例如: 123456789def f(a, b, c=3, d="Python"): print("a =", a, end=", ") print("b =", b, end=", ") print("c =", c, end=", ") print("d =", d)f(1, 2) # c和d没有被初始化,采用默认值。输出 a = 1, b = 2, c = 3, d = Pythonf(1, b=2, d="AcWing") # c没有被初始化,采用默认值。输出 a = 1, b = 2, c = 3, d = AcWing 1.3.3 其它参数写法其它参数写法用得不多,想了解的同学可以参考函数定义详解。 1.4 变量的作用域函数内定义的变量为局部变量,只能在函数内部使用。 当需要修改用全局变量时,需要用global关键字在函数内声明全局变量。例如: 12345678910111213x = 1def f(): global x # 在函数内声明全局变量 x = 666 y = 777 print(x, y)f() # 输出 666 777print(x) # 会发现全局变量x也被修改了print(y) # 会报错,因为y是局部变量,函数外无法使用 1.5 嵌套定义函数函数内部也可以定义函数。例如: 12345678def f(): def g(x): # 定义函数g() x += 1 print(x) g(5) # 调用函数g()f() # 输出6 1.6 pass语句当函数定义完但还不想实现时,可以用pass占位符,来避免出现语法错误。例如: 12def f(): pass 2. 参数传递2.1 值传递int、float、bool、字符串等采用值传递。 将实参的初始值拷贝给形参。此时,对形参的改动不会影响实参的初始值。例如: 12345678def f(y): y = 5 print(y)x = 10f(x)print(x) # 会发现x的值没变 2.2 引用传递列表采用引用传递。 将实参的引用传给形参,此时对形参的修改会影响实参的初始值。例如: 12345678def f(b): for i in range(len(b)): b[i] += 1a = [0, 1, 2, 3, 4]f(a)print(a) # 会发现列表a中的每个数加了1 3.return语句return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方,并返回结果。例如: 1234567891011121314def f(x): if x == 1: return # 不写返回值时,会返回None if x == 2: return 3 # 返回一个变量 if x == 3: return 4, 5 # 返回多个变量a = f(1)b = f(2)c, d = f(3)e = f(4) # 没写return时,也会返回Noneprint(a, b, c, d, e) # 输出 None 3 4 5 None 4.lambda表达式lambda关键字可以创建匿名函数,目的是为了简化代码。可以对比下面两种写法,会发现lambda表达式的写法更短一些。 常与sort()函数配合使用,例如: 1234pairs = [[1, "one"], [2, "two"], [3, "three"], [4, "four"]]pairs.sort(key=lambda pair: pair[1]) # 每个元素使用第二个变量比较大小print(pairs) # 输出:[[4, 'four'], [1, 'one'], [3, 'three'], [2, 'two']] 等价于下面的写法: 123456789pairs = [[1, "one"], [2, "two"], [3, "three"], [4, "four"]]def compare(pair): return pair[1]pairs.sort(key=compare) # 每个元素使用第二个变量比较大小print(pairs) # 输出:[[4, 'four'], [1, 'one'], [3, 'three'], [2, 'two']] 5. 函数递归在一个函数内部,也可以调用函数自身。这种写法被称为递归。 写递归函数可以从集合的角度来思考。理解递归函数的执行顺序可以用树的形式来思考。 例如,求解斐波那契数列第 nn 项可以采用如下写法: 1234567def fib(n): if n <= 2: return 1 return fib(n - 1) + fib(n - 2)print(fib(6)) # 输出 8","categories":[],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"github_workflow自动合并主线失败问题","slug":"githun-workflow自动合并主线失败问题","date":"2023-10-08T02:16:53.000Z","updated":"2023-10-08T02:37:06.373Z","comments":true,"path":"2023/10/08/githun-workflow自动合并主线失败问题/","link":"","permalink":"https://zade23.github.io/2023/10/08/githun-workflow%E8%87%AA%E5%8A%A8%E5%90%88%E5%B9%B6%E4%B8%BB%E7%BA%BF%E5%A4%B1%E8%B4%A5%E9%97%AE%E9%A2%98/","excerpt":"","text":"github_workflow自动提交在合并主线时出现的问题介绍看到 GitHub 上的一个好玩的项目,大意就是保持个人主页的提交栏保持尝绿,看起来很有意思,于是学着构建了一个 GitHub_actions_workflow 。仓库是用来每日清晨推送 bing 的当日壁纸的,仓库为:https://github.com/zade23/auto-green 但是部署之后发现 Actions 的 合并提交 动作一直失败,报错如下: 123456789remote: Permission to zade23/auto-green.git denied to github-actions[bot].fatal: unable to access 'https://github.com/zade23/auto-green.git/': The requested URL returned error: 403Error: Invalid exit code: 128 at ChildProcess.<anonymous> (/home/runner/work/_actions/ad-m/github-push-action/master/start.js:30:21) at ChildProcess.emit (node:events:513:28) at maybeClose (node:internal/child_process:1100:16) at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) { code: 128} 问题分析分析主要问题,应试这段 The requested URL returned error: 403 。 最终在 Stack Overflow 上找到了解决办法: Permission denied to github-actions[bot]. The requested URL returned error: 403 问题解决GitHub Actions 中自动提交功能必须在对应仓库下面手动设置允许 bot 合并提交的权限,否则合并动作就会被拒绝。 启动 actions 的 repository - Settings -> Action -> General -> Workflow permissions","categories":[],"tags":[]},{"title":"Python数据结构和算法","slug":"Python数据结构和算法","date":"2023-07-18T07:12:00.000Z","updated":"2024-06-18T09:57:08.945Z","comments":true,"path":"2023/07/18/Python数据结构和算法/","link":"","permalink":"https://zade23.github.io/2023/07/18/Python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/","excerpt":"","text":"列表 列表提供的方法 字符串 字符串提供的方法 集合 Python 集支持的运算 Python 集提供的方法 字典 字典的运算 字典的方法 字典实例 输入和输出 提示字符串 格式化字符串 格式化字符串可用的类型声明 格式化修改符 控制结构 列表解析式 函数 面向对象:定义 类 算法分析相关 复杂度计算 大 O 表示法 类 都是对数据的构成(状态)以及数据能做什么(行为)的描述。由于类的使用者只能看到数据项的状态和行为,因此类与抽象数据类型是相似的、 在面向对象编程范式中,数据项被称作 对象 一个对象就是类的一个实例。 1.内建原子数据结构 python是通过两种内建数据类型实现整数类型和浮点数类型的,相应的python类就是 int 和 float 。 列表列表 是零个或多个指向 Python 数据对象的引用的有序集合,通过在方括号内以逗号分隔的一系列值来表达。空列表就是 []。列表是异构的,这意味着其指向的数据对象不需要都是同一个类,并且这一集合可以被赋值给一个变量。下面的代码段展示了列表含有多个不同的 Python 数据对象。 元组 通常写成由括号包含并且以逗号分隔的一系列值。与序列一样,元组允许之前描述的任一操作。 列表提供的方法 方法名 用法 解释 append alist.append(item) 在列表末尾添加一个新元素 insert alist.insert(i,item) 在列表的第! i 个位置插入一个元素 pop alist.pop() 删除并返回列表中最后一个元素 pop alist.pop(i) 删除并返回列表中第 i 个位置的元素 sort alist.sort() 将列表元素排序 reverse alist.reverse() 将列表元素倒序排列 del del alist[i] 删除列表中第 i 个位置的元素 index alist.index(item) 返回 item 第一次出现时的下标 count alist.count(item) 返回 item 在列表中出现的次数 remove alist.remove(item) 从列表中移除第一次出现的 item 对应输出: 123456789101112131415161718192021222324252627282930313233343536>>> myList[1024, 3, True, 6.5]>>> myList.append(False)>>> myList[1024, 3, True, 6.5, False]>>> myList.insert(2,4.5)>>> myList[1024, 3, 4.5, True, 6.5, False]>>> myList.pop()False>>> myList[1024, 3, 4.5, True, 6.5]>>> myList.pop(1)3>>> myList[1024, 4.5, True, 6.5]>>> myList.pop(2)True>>> myList[1024, 4.5, 6.5]>>> myList.sort()>>> myList[4.5, 6.5, 1024]>>> myList.reverse()>>> myList[1024, 6.5, 4.5]>>> myList.count(6.5)1>>> myList.index(4.5)2>>> myList.remove(6.5)>>> myList[1024, 4.5]>>> del myList[0]>>> myList[4.5] 字符串字符串是零个或多个字母、数字和其他符号的有序集合。这些字母、数字和其他符号被称为 字符。常量字符串值通过引号(单引号或者双引号均可)与标识符进行区分。 123456789>>> "David"'David'>>> myName = "David">>> myName[3]'i'>>> myName*2'DavidDavid'>>> len(myName)5 字符串提供的方法 方法名 用法 解释 center astring.center(w) 返回一个字符串,原字符串居中,使用空格填充新字符串,使其长度为 w count astring.count(item) 返回 item 出现的次数 ljust astring.ljust(w) 返回一个字符串,将原字符串靠左放置并填充空格至长度 w rjust astring.rjust(w) 返回一个字符串,将原字符串靠右放置并填充空格至长度 w lower astring.lower() 返回均为小写字母的字符串 upper astring.upper() 返回均为大写字母的字符串 find astring.find(item) 返回 item 第一次出现时的下标 split astring.split(schar) 在 schar 位置将字符串分割成子串,不填则默认分割空格和换行符 列表和字符串的区别: 列表有 可修改性 ,字符串没有 集合集(set)是由零个或多个不可修改的 Python 数据对象组成的无序集合。集不允许重复元素,并且写成由花括号包含、以逗号分隔的一系列值。空集由 set() 来表示。集是异构的,并且可以通过下面的方法赋给变量。 Python 集支持的运算 运算名 运算符 解释 成员 in 询问集中是否有某元素 长度 len 获取集的元素个数 | aset | otherset 返回一个包含 aset 与 otherset 所有元素的新集 & aset & otherset 返回一个包含 aset 与 otherset 共有元素的新集 - aset - otherset 返回一个集,其中包含只出现在 aset 中的元素 <= aset <= otherset 询问 aset 中的所有元素是否都在 otherset 中 Python 集提供的方法 方法名 用法 解释 union aset.union(otherset) 返回一个包含 aset 和 otherset 所有元素的集 intersection aset.intersection(otherset) 返回一个仅包含两个集共有元素的集 difference aset.difference(otherset) 返回一个集,其中仅包含只出现在 aset 中的元素 issubset aset.issubset(otherset) 询问 aset 是否为 otherset 的子集 add aset.add(item) 向 aset 添加一个元素 remove aset.remove(item) 将 item 从 aset 中移除 pop aset.pop() 随机移除 aset 中的一个元素 clear aset.clear() 清除 aset 中的所有元素 12345678910111213141516171819202122232425262728293031>>> mySet{False, 4.5, 3, 6, 'cat'}>>> yourSet = {99,3,100}>>> mySet.union(yourSet){False, 4.5, 3, 100, 6, 'cat', 99}>>> mySet | yourSet{False, 4.5, 3, 100, 6, 'cat', 99}>>> mySet.intersection(yourSet){3}>>> mySet & yourSet{3}>>> mySet.difference(yourSet){False, 4.5, 6, 'cat'}>>> mySet - yourSet{False, 4.5, 6, 'cat'}>>> {3,100}.issubset(yourSet)True>>> {3,100}<=yourSetTrue>>> mySet.add("house")>>> mySet{False, 4.5, 3, 6, 'house', 'cat'}>>> mySet.remove(4.5)>>> mySet{False, 3, 6, 'house', 'cat'}>>> mySet.pop()False>>> mySet{3, 6, 'house', 'cat'}>>> mySet.clear()>>> mySet 字典字典 是无序结构,由相关的元素对构成,其中每对元素都由一个键和一个值组成。这种键–值对通常写成键:值的形式。字典由花括号包含的一系列以逗号分隔的键–值对表达,如下所示。 12>>> capitals = {'Iowa':'DesMoines','Wisconsin':'Madison'}>>> capitals{'Wisconsin':'Madison', 'Iowa':'DesMoines'} 可以通过键访问其对应的值,也可以向字典添加新的键–值对。访问字典的语法与访问序列的语法十分相似,只不过是使用键来访问,而不是下标。添加新值也类似。 1234567>>> capitals['Iowa'] = 'DesMoines'>>> capitals['Utah'] = 'SaltLakeCity'>>> capitals{'Utah':'SaltLakeCity', 'Wisconsin':'Madison', 'Iowa':'DesMoines'}>>> capitals['California']='Sacramento'>>> capitals{'Utah':'SaltLakeCity', 'Wisconsin':'Madison', 'Iowa':'DesMoines', 'California':'Sacramento'}>>> len(capitals)>>> 4 需要谨记,字典并不是根据键来进行有序维护的。第一个添加的键–值对('Utah':'SaltLakeCity')被放在了字典的第一位,第二个添加的键–值对('California':'Sacramento')则被放在了最后。 字典的运算keys、values 和 items 方法均会返回包含相应值的对象。可以使用 list 函数将字典转换成列表。 运算名 运算符 解释 [] myDict[k] 返回与 k 相关联的值,如果没有则报错 in key in adict 如果 key 在字典中,返回 True,否则返回 False del del adict[key] 从字典中删除 key 的键–值对 字典的方法get 方法有两种版本。如果键没有出现在字典中,get 会返回 None。然而,第二个可选参数可以返回特定值。 方法名 用法 解释 keys adict.keys() 返回包含字典中所有键的 dict_keys 对象 values adict.values() 返回包含字典中所有值的 dict_values 对象 items adict.items() 返回包含字典中所有键–值对的 dict_items 对象 get adict.get(k) 返回 k 对应的值,如果没有则返回 None get adict.get(k, alt) 返回 k 对应的值,如果没有则返回 alt 字典实例12345678910111213141516171819>>> phoneext={'david':1410, 'brad':1137}>>> phoneext{'brad':1137, 'david':1410}>>> phoneext.keys()dict_keys(['brad', 'david'])>>> list(phoneext.keys())['brad', 'david']>>> phoneext.values()dict_values([1137, 1410])>>> list(phoneext.values())[1137, 1410]>>> phoneext.items()dict_items([('brad', 1137), ('david', 1410)])>>> list(phoneext.items())[('brad', 1137), ('david', 1410)]>>> phoneext.get("kent")>>> phoneext.get("kent", "NO ENTRY")'NO ENTRY'>>> 输入和输出程序经常要和用户进行交互。 目前的大多数程序使用对话框作为要求用户提供某种输入的方式。尽管 Python 确实有方法来创建这样的对话框,但是可以利用更简单的函数。Python 提供了一个函数,它使得我们可以要求用户输入数据并且返回一个字符串的引用。这个函数就是 input。 提示字符串input 函数接受一个字符串作为参数。由于该字符串包含有用的文本来提示用户输入,因此它经常被称为 提示字符串。举例来说,可以像下面这样调用 input。 不论用户在提示字符串后面输入什么内容,都会被存储在 aName 变量中。使用 input 函数,可以非常简便地写出程序,让用户输入数据,然后再对这些数据进行进一步处理。例如,在下面的两条语句中,第一条要求用户输入姓名,第二条则打印出对输入字符串进行一些简单处理后的结果。 12345aName = input("Please enter your name ")print("Your name in all capitals is ", aName.upper(), "and has length", len(aName)) 需要注意的是,input 函数返回的值是一个字符串,它包含用户在提示字符串后面输入的所有字符。如果需要将这个字符串转换成其他类型,必须明确地提供类型转换。在下面的语句中,用户输入的字符串被转换成了浮点数,以便于后续的算术处理。 123sradius = input("Please enter the radius of the circle ")radius = float(sradius)diameter = 2 * radius 格式化字符串print 函数为输出 Python 程序的值提供了一种非常简便的方法。它接受零个或者多个参数,并且将单个空格作为默认分隔符来显示结果。通过设置 sep 这一实际参数可以改变分隔符。此外,每一次打印都默认以换行符结尾。这一行为可以通过设置实际参数 end 来更改。下面是一些例子。 12345678>>> print("Hello")Hello>>> print("Hello","World")Hello World>>> print("Hello","World", sep="***")Hello***World>>> print("Hello","World", end="***")Hello World*** Python 提供了另一种叫作 格式化字符串 的方式。格式化字符串是一个模板,其中包含保持不变的单词或空格,以及之后插入的变量的占位符。例如,下面的语句包含 is 和 years old.,但是名字和年龄会根据运行时变量的值而发生改变。 1print(aName, "is", age, "years old.") 使用格式化字符串,可以将上面的语句重写成下面的语句。 1print("%s is %d years old." % (aName, age)) 格式化字符串可用的类型声明% 是字符串运算符,被称作 格式化运算符。 表达式的左边部分是模板(也叫格式化字符串),右边部分则是一系列用于格式化字符串的值。 格式化字符串可以包含一个或者多个转换声明。转换字符告诉格式化运算符,什么类型的值会被插入到字符串中的相应位置。 在上面的例子中,%s 声明了一个字符串,%d 则声明了一个整数。其他可能的类型声明还包括 i、u、f、e、g、c 和 %。 字符 输出格式 d、i 整数 u 无符号整数 f m.dddd 格式的浮点数 e m.dddde+/-xx 格式的浮点数 E m.ddddE+/-xx 格式的浮点数 g 对指数小于-4 或者大于 5 的使用 %e,否则使用 %f c 单个字符 s 字符串,或者任意可以通过 str 函数转换成字符串的 Python 数据对象 % 插入一个常量 % 符号 格式化修改符 修改符 例子 解释 数字 %20d 将值放在 20 个字符宽的区域中 - %-20d 将值放在 20 个字符宽的区域中,并且左对齐 + %+20d 将值放在 20 个字符宽的区域中,并且右对齐 0 %020d 将值放在 20 个字符宽的区域中,并在前面补上 0 . %20.2f 将值放在 20 个字符宽的区域中,并且保留小数点后 2 位 (name) %(name)d 从字典中获取 name 键对应的值 1234567891011>>> price = 24>>> item = "banana">>> print("The %s costs %d cents" % (item,price))The banana costs 24 cents>>> print("The %+10s costs %5.2f cents" % (item,price))The banana costs 24.00 cents>>> print("The %+10s costs %10.2f cents" % (item,price))The banana costs 24.00 cents>>> itemdict = {"item":"banana","cost":24}>>> print("The %(item)s costs %(cost)7.1f cents" % itemdict)The banana costs 24.0 cents 控制结构Python提供的标准的标准控制语句有 while 语句以及 for 语句。 1234567891011>>> counter = 1>>> while counter <= 5:... print("Hello, world")... counter = counter + 1...Hello, worldHello, worldHello, worldHello, worldHello, world 看下面这个例子。 1while counter <= 10 and not done: 迭代语句只有在上面两个条件都满足的情况下才会被执行。变量 counter 的值需要小于或等于 10,并且变量 done 的值需要为 False(not False 就是 True),因此 True and True 的最后结果才是 True。 for在遍历每一个成员上非常的方便。例如: 12345678>>> for item in [1,3,6,2,5]:... print(item)...13625 列表解析式列表可以通过使用迭代结构和分支结构来创建。这种方式被称为 列表解析式。通过列表解析式,可以根据一些处理和分支标准轻松创建列表。 举例来说,如果想创建一个包含前 10 个完全平方数的列表,可以使用以下的 for 语句。 12345>>> sqlist = []>>> for x in range(1,11): sqlist.append(x*x)>>> sqlist[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 进阶:使用列表解析式,只需一行代码即可创建完成。 123>>> sqlist = [x*x for x in range(1,11)]>>> sqlist[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 进阶:变量 x 会依次取由 for 语句指定的 1 到 10 为值。之后,计算 x*x 的值并将结果添加到正在构建的列表中。列表解析式也允许添加一个分支语句来控制添加到列表中的元素。 123>>> sqlist = [x*x for x in range(1,11) if x%2 != 0]>>> sqlist[1, 9, 25, 49, 81] 例子:(力扣的原题:输出列表中非元音的字母并以大写形式返回) 12>>>[ch.upper() for ch in 'comprehension' if ch not in 'aeiou']['C', 'M', 'P', 'R', 'H', 'N', 'S', 'N'] 函数下面定义的简单函数会返回传入值的平方。 1234567>>> def square(n):... return n**2...>>> square(3)9>>> square(square(3))81 同样的,我们可以自己定义一个平方根函数 squareroot() 通过牛顿迭代法求解平方根 1234561. def squareroot(n):2. root = n/2 #initial guess will be 1/2 of n3. for k in range(20):4. root = (1/2)*(root + (n / root))5.6. return root 接下来,模拟调用这个函数 1234>>> squareroot(9)3.0>>> squareroot(4563)67.549981495186216 面向对象:定义 类算法分析相关复杂度计算大 O 表示法 如果可以通过观察循环结构和算法的方式快速判断出复杂度就算出师 异序词排序问题 1234567891011121314151617181920212223def anagramSolution1(s1, s2): alist = list(s2) pos1 = 0 StillOK = True while pos1 < len(s1) and stillOK: pos2 = 0 found = False while pos2 < len(alist) and not found: if s1[pos1] == alist[pos2]: found = True else: pos2 += 1 if found: alist[pos2] = None else: stillOK = False pos1 += 1 return stillOK 123456789101112131415161718def anagramSolution2(s1, s2): alist1 = list(s1) alist2 = list(s2) # 这里需要记住,常用的几类排序时间复杂度在 O(n^2) 或 O(nlogn) alist1.sort() alist2.sort() pos = 0 matches = True while pos < len(s1) and matches: if alist1[pos] == alist2[pos]: pos += 1 else: matches = False return matches 123456789101112131415161718192021def anagramSolution3(s1, s2): c1 = [0] * n c2 = [0] * n for i in range(len(s1)): pos = ord(s1[i]) - ord('a') c1[pos] += 1 for i in range(len(s2)): pos = ord(s2[i] - ord('a')) c2[pos] += 1 j = 0 stillOk = True while j < 26 and stillOK: if c1[j] == c2[j]: j += 1 else: stillOK = False return stillOk 第四个例子是最快的。因为没有循环嵌套,只有O(n)的复杂度。倘若需要考虑关于空间上的需求,那么第四个例子新开了额外的空间用于存储计数器,这种方式就是常说的:空间换时间的方法。","categories":[{"name":"数据结构和算法","slug":"数据结构和算法","permalink":"https://zade23.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"完备区间判断下的二分查找万能模板","slug":"完备区间判断下的二分查找万能模板","date":"2023-07-07T06:57:15.000Z","updated":"2024-06-18T10:03:01.022Z","comments":true,"path":"2023/07/07/完备区间判断下的二分查找万能模板/","link":"","permalink":"https://zade23.github.io/2023/07/07/%E5%AE%8C%E5%A4%87%E5%8C%BA%E9%97%B4%E5%88%A4%E6%96%AD%E4%B8%8B%E7%9A%84%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E4%B8%87%E8%83%BD%E6%A8%A1%E6%9D%BF/","excerpt":"","text":"场景 二分模板 整数二分 C++ Python 浮点数二分 场景单调数列/数组 一定可以用二分;但如果非单调的情况,也有可能存在能够使用二分的情况 二分模板整数二分C++123456789101112131415161718192021222324252627#include <iostream>// 整数二分的模板bool check(int x) {/*......*/} // 检查x是否满足某种性质/* 目前发现整数二分的所有情况都可以被下面的两个板子涵盖*/// 2.1.1 区间被划分成 [l, mid] 和 [mid + 1, r]int bsearch_1(int l, int r) { while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } return l}// 2.1.2 区间被划分成 [l, mid - 1] 和 [mid, r]int bsearch_2(int l, int r) { while (l < r) { int mid = l + r + 1 >> 1; if (chedk(mid)) l = mid; else r = mid - 1; }} Python1234567891011121314151617181920212223242526# 二分查找def check(x): # 判断x是否满足某种性质 if x > 0: return True else: return False# 2.1.1 区间被划分成 [l, mid] 和 [mid + 1, r]def bsearch_2(l, r): while l < r: mid = l + r >> 1 if (check(mid)): r = mid else: l = mid + 1 return l# 2.1.2 区间被划分成 [l, mid - 1] 和 [mid, r]def bsearch_1(l, r): while l < r: mid = l + r + 1 >> 1 if (check(mid)): l = mid else: r = mid - 1 return l 浮点数二分只有C++要考虑,高贵的Python不需要考虑。 1234567891011#include <iostream>double bsearch_3(double l, double r) { const double eps = 1e-6; while (r - l > eps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l;}","categories":[{"name":"数据结构和算法","slug":"数据结构和算法","permalink":"https://zade23.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"},{"name":"C++","slug":"C","permalink":"https://zade23.github.io/tags/C/"}]},{"title":"Python条件判断的优先级","slug":"Python条件判断的优先级","date":"2023-07-06T12:53:13.000Z","updated":"2024-06-18T10:01:11.723Z","comments":true,"path":"2023/07/06/Python条件判断的优先级/","link":"","permalink":"https://zade23.github.io/2023/07/06/Python%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD%E7%9A%84%E4%BC%98%E5%85%88%E7%BA%A7/","excerpt":"","text":"背景 代码部分 问题代码 正确代码 具体分析 背景做LeetCode的时候经常能够发现很多基本功不扎实的问题。这一次是在做一道简单题时候忽视的条件判断优先级的问题 题目连接:https://leetcode.cn/problems/count-the-number-of-vowel-strings-in-range 代码部分 问题代码12345678910class Solution: def vowelStrings(self, words: List[str], left: int, right: int) -> int: words = words[left : right + 1] n = len(words) print(n) res = 0 for i in range(n): if words[i][0] == 'a' or words[i][0] == 'e' or words[i][0] == 'i' or words[i][0] == 'o' or words[i][0] == 'u' and words[i][-1] == 'a' or words[i][-1] == 'e' or words[i][-1] == 'i' or words[i][-1] == 'o' or words[i][-1] == 'u': res += 1 return res 正确代码1234567891011class Solution: def vowelStrings(self, words: List[str], left: int, right: int) -> int: words = words[left : right + 1] n = len(words) print(n) res = 0 for i in range(n): if words[i][0] == 'a' or words[i][0] == 'e' or words[i][0] == 'i' or words[i][0] == 'o' or words[i][0] == 'u': if words[i][-1] == 'a' or words[i][-1] == 'e' or words[i][-1] == 'i' or words[i][-1] == 'o' or words[i][-1] == 'u': res += 1 return res 二者的差别就在如何衔接 or 和 and 之间的关系上。 具体分析分析错误代码的bug: 在第二段错误代码中,条件判断部分存在问题。以下是有问题的代码片段: 12if words[i][0] == 'a' or words[i][0] == 'e' or words[i][0] == 'i' or words[i][0] == 'o' or words[i][0] == 'u' and words[i][-1] == 'a' or words[i][-1] == 'e' or words[i][-1] == 'i' or words[i][-1] == 'o' or words[i][-1] == 'u': res += 1 问题出在逻辑运算符的优先级上。在Python中,and 运算符的优先级高于 or 运算符,因此该条件判断实际上被解释为: 12if words[i][0] == 'a' or words[i][0] == 'e' or words[i][0] == 'i' or words[i][0] == 'o' or (words[i][0] == 'u' and words[i][-1] == 'a') or words[i][-1] == 'e' or words[i][-1] == 'i' or words[i][-1] == 'o' or words[i][-1] == 'u': res += 1 由于 or 运算符的短路特性,只要 words[i][0] 的首字母为元音字母之一,整个条件判断就会被认为是True,导致res增加。而第二个部分 words[i][-1] == 'a'、words[i][-1] == 'e'等都是单独的条件,不会影响整个条件判断的结果。 修正该bug的方法是使用括号明确指定条件的分组,确保逻辑关系正确。以下是修正后的代码: 12if (words[i][0] == 'a' or words[i][0] == 'e' or words[i][0] == 'i' or words[i][0] == 'o' or words[i][0] == 'u') and (words[i][-1] == 'a' or words[i][-1] == 'e' or words[i][-1] == 'i' or words[i][-1] == 'o' or words[i][-1] == 'u'): res += 1 这样修改后,条件判断会首先检查首字母是否为元音字母,然后再检查末尾字母是否为元音字母,两个条件都满足时才会增加res的值。","categories":[{"name":"数据结构和算法","slug":"数据结构和算法","permalink":"https://zade23.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"}]},{"title":"Hello World","slug":"hello-world","date":"2023-05-16T09:45:58.000Z","updated":"2024-03-20T13:42:07.174Z","comments":false,"path":"2023/05/16/hello-world/","link":"","permalink":"https://zade23.github.io/2023/05/16/hello-world/","excerpt":"","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1hexo new "My New Post" More info: Writing Run server1hexo server More info: Server Generate static files1hexo generate More info: Generating Deploy to remote sites1hexo deploy More info: Deployment 如果公式出现渲染失败的情况,请将公式中下划线后面加上空格。","categories":[{"name":"whatever","slug":"whatever","permalink":"https://zade23.github.io/categories/whatever/"}],"tags":[{"name":"Hexo","slug":"Hexo","permalink":"https://zade23.github.io/tags/Hexo/"}]}],"categories":[{"name":"数据结构和算法","slug":"数据结构和算法","permalink":"https://zade23.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"},{"name":"Deeplearning","slug":"Deeplearning","permalink":"https://zade23.github.io/categories/Deeplearning/"},{"name":"Git Workflow","slug":"Git-Workflow","permalink":"https://zade23.github.io/categories/Git-Workflow/"},{"name":"Unity","slug":"Unity","permalink":"https://zade23.github.io/categories/Unity/"},{"name":"读书笔记","slug":"读书笔记","permalink":"https://zade23.github.io/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"},{"name":"LLM","slug":"LLM","permalink":"https://zade23.github.io/categories/LLM/"},{"name":"杂项","slug":"杂项","permalink":"https://zade23.github.io/categories/%E6%9D%82%E9%A1%B9/"},{"name":"whatever","slug":"whatever","permalink":"https://zade23.github.io/categories/whatever/"}],"tags":[{"name":"LeetCode","slug":"LeetCode","permalink":"https://zade23.github.io/tags/LeetCode/"},{"name":"conda","slug":"conda","permalink":"https://zade23.github.io/tags/conda/"},{"name":"environment","slug":"environment","permalink":"https://zade23.github.io/tags/environment/"},{"name":"Git","slug":"Git","permalink":"https://zade23.github.io/tags/Git/"},{"name":"Game Engine","slug":"Game-Engine","permalink":"https://zade23.github.io/tags/Game-Engine/"},{"name":"NLP","slug":"NLP","permalink":"https://zade23.github.io/tags/NLP/"},{"name":"Python","slug":"Python","permalink":"https://zade23.github.io/tags/Python/"},{"name":"C++","slug":"C","permalink":"https://zade23.github.io/tags/C/"},{"name":"Transformers","slug":"Transformers","permalink":"https://zade23.github.io/tags/Transformers/"},{"name":"Training","slug":"Training","permalink":"https://zade23.github.io/tags/Training/"},{"name":"Docker","slug":"Docker","permalink":"https://zade23.github.io/tags/Docker/"},{"name":"PyTorch","slug":"PyTorch","permalink":"https://zade23.github.io/tags/PyTorch/"},{"name":"工具网站","slug":"工具网站","permalink":"https://zade23.github.io/tags/%E5%B7%A5%E5%85%B7%E7%BD%91%E7%AB%99/"},{"name":"经济学小知识","slug":"经济学小知识","permalink":"https://zade23.github.io/tags/%E7%BB%8F%E6%B5%8E%E5%AD%A6%E5%B0%8F%E7%9F%A5%E8%AF%86/"},{"name":"Hexo","slug":"Hexo","permalink":"https://zade23.github.io/tags/Hexo/"}]}