<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>V2beach&#39;s Blog</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://blog.v2beach.cn/"/>
  <updated>2025-06-18T08:24:22.744Z</updated>
  <id>http://blog.v2beach.cn/</id>
  
  <author>
    <name>V2beach</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>TOEFL速通</title>
    <link href="http://blog.v2beach.cn/2025/03/25/TOEFL/"/>
    <id>http://blog.v2beach.cn/2025/03/25/TOEFL/</id>
    <published>2025-03-25T07:38:30.000Z</published>
    <updated>2025-06-18T08:24:22.744Z</updated>
    
    <content type="html"><![CDATA[<p>Test of English as a Foreign Language.  </p><a id="more"></a><h1 id="Terms"><a href="#Terms" class="headerlink" title="Terms"></a><strong>Terms</strong></h1><h2 id="ETS"><a href="#ETS" class="headerlink" title="ETS"></a><strong>ETS</strong></h2><p>Educational Testing Service，美国教育测验服务社。<br>他们主办，负责出题、提供考试软件、评分等，中国由国家教育部考试中心（NEEA, National Education Examinations Authority）承办，组织考试。</p><h2 id="iBT"><a href="#iBT" class="headerlink" title="iBT"></a><strong>iBT</strong></h2><p>Internet Based Test，2005年推出的在线考试形式。</p><blockquote><p>The TOEFL iBT test measures a test taker’s ability to combine listening, reading, speaking and writing skills where it matters most — <strong>in the classroom.</strong></p></blockquote><p>听、说、读、写分别30分，总分120，考试过程中可做笔记，主要考察的是考生是否能应付学校生活中的语言实际需求。</p><p>刚出时4小时，之后改成3小时，上回我第一次裸考托福（Test Date: 2023年9月9日）之前刚改成2小时。</p><hr><h3 id="读-R"><a href="#读-R" class="headerlink" title="读/R"></a><strong>读/R</strong></h3><p>两篇。</p><p>几条经验：</p><ol><li>实在不在状态读不进去的题，先读一下每段第一句话提状态；<ul><li>每段在草稿上记第一句话；</li><li>当前段题目可能反映主旨；</li><li>6选3直接对应主旨段落。</li></ul></li><li>6选3大概率是按文章结构，每部分一个总结句；</li><li>文章结构有以下几种：</li></ol><div align=center><img src="/pics/TOEFL/结构1.png" loading="lazy" alt="分类，攻击性" width="66%" height="66%"></div><div align=center><img src="/pics/TOEFL/结构2.png" loading="lazy" alt="对比，工业革命" width="66%" height="66%"></div><div align=center><img src="/pics/TOEFL/结构3.png" loading="lazy" alt="提问求解，鲸类起源" width="66%" height="66%"></div><div align=center><img src="/pics/TOEFL/结构4.png" loading="lazy" alt="因果，沙漠化" width="66%" height="66%"></div><p>不过我立刻发现李文勍这个结构分类很局限，比如49-2和50-2这两篇历史文章，都是按时间顺序写的，给它归类为什么总分、总分总都很牵强，很片面。</p><p>49-2: 5个段落，按照<strong>时间顺序</strong>组织，“发明前→发明→发展→影响”。</p><ol><li>公元前200～300讲到1150再到15世纪纸张演变，从莎草、牛羊皮到中国<strong>造纸术</strong>传入欧洲，便宜纸张开始普及，主要讲的是<strong>印刷术</strong>，纸是物质基础；</li><li>两个德国金匠发明活字印刷，有3个优点，另外印刷Gutenberg圣经；</li><li>德国的印刷产业化，旧的手抄本提供印刷机做不到的奢侈品；</li><li>国际竞争，德国意大利字体；</li><li>广泛传播，对欧洲和世界的影响。</li></ol><p>50-2: 这篇可以被认为是总分，但2到5段也是按<strong>时间顺序</strong>组织的，“”。</p><ol><li>巴西从葡萄牙独立没有血腥革命，有两次失败的conspiracies，总结是如果不是发生了一些意外事件，可能会更晚独立；</li><li>然后按时间顺序开始讲到底怎么独立的：法国入侵葡萄牙导致葡萄牙王室逃到（这里用flight表示逃亡）里约热内卢Rio de Janeiro，葡萄牙开启若昂 Joan 国王 devise 的双君主时代 dual monarchy（这人真牛，法国入侵让权，巴西独立又让权），若昂人在 Rio de Janeiro，巴西由此开始开放和发展经济，但是也因此开始依赖英国，这里有个没见过的说法 <strong>“substitution of X for Y” 用 X 取代 Y</strong>；</li><li>巴西的发展导致🇧🇷和🇵🇹精英的怨恨和冲突加剧；</li><li>1820年葡萄牙革命导致巴西跟宗主国 mother country 决裂，葡萄牙要求若昂回国，结束了短暂的双君主制，但他把他儿子留在了巴西，让他儿子领导巴西独立并成为国王；</li><li>Pedro佩德罗找准时机，巴西独立。</li></ol><p>这个故事很有趣，借此我还查了一下巴西的殖民历史，1492年西班牙先登陆了南美，占领大部分南美地盘，1500年葡萄牙登陆巴西，把西班牙没殖民到的地方殖民了。<br>当时南美土著有五六千万人，殖民者来后一百年，他们死了90%。<br>但“大灭绝”（The Great Dying）并不主要因为屠杀和饥荒，而是因为<strong>原住民对天花、麻疹、流感等欧洲疾病没有免疫力</strong>，所以大规模死亡，<strong>巴西现在土著只剩1%，在亚马逊的原住民保留地。</strong></p><div align=center><img src="/pics/TOEFL/阅读每道题时间限制.png" loading="lazy" alt="题型" width="66%" height="66%"></div><p>另外有几个早期的经验：</p><ol><li>做每一篇都计时，18min 一篇；</li><li>每天都要背核心单词和学科分类单词；</li><li>每1～2道题对应一个段落，看题目问的啥确定段落主旨；</li><li>抓主谓宾“谁对谁做了什么”和“动作的结果/影响”就能快速拆解句子；</li><li>后期要尽量往12min一篇努力。</li></ol><hr><h3 id="听-L"><a href="#听-L" class="headerlink" title="听/L"></a><strong>听/L</strong></h3><p>2个对话，3段演讲。</p><p>托福听力给淇姐和<a href="https://www.cc98.org/topic/6137412">cc98速通哥</a>的感觉都是，大结构比细节更重要，可以忽略小细节，且不需要推理，都是直来直去的，以听懂一篇文章的逻辑和主要意思为日常练习重点。<br>根据我的<a href="https://www.cc98.org/topic/5984440">cc98资料姐</a>，用<a href="https://www.bilibili.com/video/BV1LW411v7Z6">清华李文勍</a>的mooc：</p><ol><li>想拿高分conversation一定不能错，先从简单的conversation练起，都是美国教授/服务人员帮助学生的场景；</li><li>精听是唯一搞定托福听力的捷径，我还有9天精听时间。</li><li>conversation和lecture都是有明显层次的：<ul><li>conversation 5 道题，基本上按顺序有 5 个层次，尤其注意比较的地方。</li><li>lecture 6 道题，也会有师生对话，无论是师生问答还是老师自问自答，很可能都会考到问答总结。</li></ul></li></ol><p>结尾考研专注度的问题我容易听不见，必须从头到尾保持专注，cs231n一节课一俩小时我都上下来了，这5分钟都坚持不住的话，上鸡毛？</p><p>首的问题解决了，我现在总听不到尾。</p><p>有质疑实验的问题大概率会考，他们是怎么质疑的？不能复现？不能证明假设？还得练…</p><p>每篇听力做这两件事：</p><ol><li>精听后，把原文拿出来，找Question出现的位置；</li><li>画出如下文章结构图（跟阅读一样，可能分不出cases，case可能在很开头也可能到结尾才说）：</li></ol><div align=center><img src="/pics/TOEFL/conversation层次.png" loading="lazy" alt="conv" width="66%" height="66%"></div><div align=center><img src="/pics/TOEFL/lecture层次.png" loading="lazy" alt="lecture" width="66%" height="66%"></div><hr><h3 id="说-S"><a href="#说-S" class="headerlink" title="说/S"></a><strong>说/S</strong></h3><p>1独立，2一篇学生proposal或学校announcement+学生讨论听力，3一个学术名词，阅读是定义，听力是professor的例子解释，需要压缩这个故事切题，4一个lecture听力，有两个分支实例，把实例复述好就行。<br>自信，调动情绪，对分影响很大。</p><h4 id="TASK1语料"><a href="#TASK1语料" class="headerlink" title="TASK1语料"></a><strong>TASK1语料</strong></h4><p>泛用理由（每一个都准备例子补全细节，如果时间不够想到第二个关键词，也可以只说一个理由但说一堆细节），后面独立写作有些也许也能用——<br>如果题目对我来说如果比较难选，可以先说一个Yeah, I mean…比如63先说要有discipline/regulation，再套我真正想套的下面的观点。</p><ol><li><strong>开拓眼界</strong>（广度）67，66，40（选一个国家或文化这种题，得改改？）<ul><li>不用broaden my horizons。</li><li>用“Increase my exposure to different sides/perspectives”。</li><li>xxx (Engaging in discussions/cooperation with others) would increase my exposure to different perspectives, <strong>which helps me see the issue from all sides</strong> and leads to more informed views.</li></ul></li><li><strong>加深理解</strong>（深度）<ul><li>dig deeper into the topic</li><li>By xxx, I can dig deeper into the topic, <strong>which helps me to see the issue from all sides</strong> and leads to a deeper understanding.</li></ul></li><li><strong>感情/关系</strong>（故人）69，63，61=58（宿舍选择）<ul><li>strengthen our relationship.</li><li>create lasting memories</li><li>time flies by but our bond keeps strong becasue of (Activity)</li></ul></li><li><strong>交友/人脉</strong>（新人）跟1.开拓眼界接近，66，61=58（宿舍选择）<ul><li>meet new people and make friends</li><li>discover life possibilities and ways of living</li><li>share resources</li><li>study and career advice</li><li>release stress, encourage me during tough times</li></ul></li><li><strong>金钱</strong>（赚钱、省钱）68(disagree)，65(agree with 钱=成功，然后说even though some other people will argue that happiness…, but as a student(承认片面，然后陈述自己的片面观点))，60，59(实习赚钱)<ul><li>(1)财务角度很重要</li><li>(2)现状（预算紧张）</li><li>(3)通过某行为改善经济状况</li><li>(4)具体用途/投资方向</li><li>个人：<ul><li>(1) It makes more sense financially.</li><li>(2) As a student on a tight budget, I need to be very careful with my spending.</li><li>(3) If I could earn some extra money / save money by …, this would help me <strong>stretch my budget</strong> much further.</li><li>(4) cover basic needs, afford housing costs, pay for emergencies.</li></ul></li><li>学校：<ul><li>(1) It makes more sense financially.</li><li>(2) Educational institutions constantly face financial pressures and need to <strong>stretch their budgets.</strong></li><li>(3) xxx. This additional income stream / cost savings would really strengthen the university’s budget.</li><li>(4) upgrade technology, expand library resources, and improve campus facilities.</li></ul></li></ul></li><li><strong>借鉴反思</strong>（犯错、历史、名人）57犯错<ul><li>(1)反思学习的重要性</li><li>(2)避免重复错误</li><li>(3)汲取正面经验</li><li>(1)<strong>This(Making mistakes(57 from quick decisions)) gives us/me a really good chance to (think about … )/ (learn from what happened before.)</strong></li><li>(2) not to repeat the same mistakes</li><li>(3) draw valuable lessons from</li></ul></li><li><strong>电子设备</strong>（电子设备）64，62(别多想，就硬套缺点)<ul><li>(1)健康问题</li><li>(2)社交能力下降</li><li>(3)分心/效率问题</li><li>(1) really worried about health issues that come with too much screen time. (have a bad impact on, 或者直接 harm your health)</li><li>(2) <strong>On top of that,</strong> everyone’s just stuck indoors, losing their social skills, and dealing with sleep problems and eye strain. </li><li>(3) <strong>On top of that,</strong> You just can’t focus properly when you’re constantly getting distracted by notifications and apps.</li></ul></li><li><strong>放松&amp;减压</strong>（任意能往上靠的）63(老师对学生友善chargin students’ mental battery, focus on the classroom with way more energy)，61<ul><li>(1) 情绪调节</li><li>(2) 恢复学习和工作动力</li><li>(1) <strong>It’s like hitting an emotional reset button - you go from stressed to chill in no time</strong></li><li>(2) <strong>It’s like charging your mental battery - you come back with way more energy.</strong></li></ul></li><li><strong>聊天话题</strong>（任意能往上靠的）<ul><li>(1)缓解社交焦虑</li><li>(2)建立社交连接</li><li>(1) You’ve got something in your back pocket when the chat gets awkward, you never have to panic about what to say next.</li><li>(2) Shared topics are like social magnets - they pull the right people toward you.</li></ul></li><li><strong>能力</strong>（任意能往上靠的）67，64，57，56<ul><li>X(activity) provides the perfect opportunity to help people cultivate Y(quality),</li><li>which would play an important role in their future. </li><li>For example, … 跟小站试听一样举例，when … I did X(activity), then when I encounter … I found a way to deal with … 或者 deal with … pretty easily.（可以缩短只说未来有这个能力可能会怎样）</li></ul></li></ol><h4 id="模版"><a href="#模版" class="headerlink" title="模版"></a><strong>模版</strong></h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">task1:观点+理由+例子（时间够说两个，时间不够把一个说完整）</span><br><span class="line">task2:</span><br><span class="line">     The student proposed to... because...(理由太复杂就省略)</span><br><span class="line">     The university plans to...because...</span><br><span class="line">     阅读一定要把两个reasons看明白，看明白之后能记原文就记，方便直接复述不再组织语言；</span><br><span class="line">     The man&#x2F;woman likes&#x2F;doesn&#39;t like...First off...Besides...</span><br><span class="line">     听力部分一定要跟阅读两个点一一对应，每个原因也都是主题+理由，必须记下来主题，理由没记下来可以用其他句子填进去。</span><br><span class="line">task3:</span><br><span class="line">     Well, the reading passage talks about... which means... </span><br><span class="line">     定义句一定要记清楚；</span><br><span class="line">     The professor expands it with the example of ...</span><br><span class="line">     开始讲故事，用When等词串联时间节点，把故事大致讲清楚；</span><br><span class="line">     example里会有跟“concept”无关的细节！比如聊“comfort zone”，professor的朋友在工作一段时间后放弃了尝试dream job的机会，那example里他在当前工作怎么一开始not good at，后来又如何good at这都不用讲。</span><br><span class="line">task4:</span><br><span class="line">     The professor talks about.主题</span><br><span class="line">     尽量简短,忽视背景信息的讲解，前几十秒理解主题能简要复述就行；</span><br><span class="line">     One strategy&#x2F;... is ...</span><br><span class="line">     前30秒，比如salesman策略这个题，一句话说明白 is pointing out special features of product，之后一直讲例子；</span><br><span class="line">     Another strategy&#x2F;... is ...</span><br><span class="line">     后30秒，一句话说明白 is demonstrating the product，之后主讲例子，顾客担心 portability，就演示怎么transport。</span><br></pre></td></tr></table></figure><p>关于task4我引用<a href="https://www.xiaohongshu.com/explore/61eeb3ff00000000210384eb?xsec_token=ABKxozPIwpYjCewz6bHqwkGMJ17D_UKx5o3LapihGdrqs=&amp;xsec_source=pc_user">不爱酸苦辣的笔记</a>：</p><div align=center><img src="/pics/TOEFL/不爱酸苦辣.png" loading="lazy" alt="技巧性最强的task4" width="66%" height="66%"></div><hr><h3 id="写-W"><a href="#写-W" class="headerlink" title="写/W"></a><strong>写/W</strong></h3><p>1篇综合写作（读+听+写），1篇独立写作，类似口语task1。</p><p>独立写作最大的技巧是<strong>多看范文</strong>，综合写作跟口语task2不一样，口语task2 lecture可能赞成可能不赞成 passage，但综合写作一定不赞成，而且开篇和三个点的结构固定一一对应。听读篇幅都是300字，最好也要写到300字（它的150～225要求已经是从2006年沿用至今）。<br>比例大概是复述passage：复述lecture1:～1.5，详尽写听力是怎么反对阅读的，听力部分占分多，阅读只占20%，听力40%，之间联系20%。</p><p>范文：<a href="https://www.toeflresources.com">https://www.toeflresources.com</a> 。</p><h4 id="integrated"><a href="#integrated" class="headerlink" title="integrated"></a><strong>integrated</strong></h4><p>根据Vince和cc98总结一套我自己的模版：</p><p>The reading <strong>discusses</strong> [Reading’s General Stance/Topic]. In the lecture, however, the speaker <strong>challenges</strong> this <strong>perspective</strong>, <strong>contending</strong> that [Lecture’s General Counter-Argument].</p><p>Firstly, the reading <strong>asserts</strong> that [R1], <strong>positing</strong> that [R1-explanation/details]. <strong>In contrast,</strong> the lecture <strong>disputes</strong> this <strong>assertion</strong>, <strong>maintaining</strong> that [L1].</p><p>Secondly, the reading <strong>suggests</strong> that [R2], further <strong>illustrating</strong> that [R2-explanation/details]. <strong>Nevertheless,</strong> the lecture <strong>refutes</strong> this <strong>suggestion</strong>, <strong>arguing</strong> that [L2].</p><p>Thirdly, the reading <strong>proposes</strong> that [R3], <strong>citing</strong> that [R3-explanation/details]. <strong>However,</strong> the lecture <strong>counters</strong> this <strong>proposal</strong>, <strong>emphasizing</strong> that [L3].</p><ol><li>指代“文章”只用“reading”，指代“音频”只用“lecture”（long serious speech）；</li><li>几种总结topic的说法：<ol><li>假说：“theories regarding …”；（72，71，69，68(negatives底片拍摄者)，67…）</li><li>解决问题：“ways”；（75，73…）</li><li>分析利弊：“advantages”；（70，66…）</li><li>或者可能上面有一些我看漏的，是只有一个theory，提出三个论据来佐证？</li></ol></li><li>according to gemini，thirdly比finally要好。</li></ol><p>一定要切题，跟高考语文一样，首要是别跑题。</p><p>阅读的部分少写，听力的部分多写，我阅读部分填充太多了。可以用阅读的部分反着说充实听力内容，还欠练。</p><h4 id="academic"><a href="#academic" class="headerlink" title="academic"></a><strong>academic</strong></h4><ol><li>不要有口语化表达，there’re, be going to(改成will)这种；</li><li>其他很多错误记在本子上了，就是得多写多错多改正…</li></ol><div align=center><img src="/pics/TOEFL/总览.png" loading="lazy" alt="" width="66%" height="66%"></div><p>学术写作/独立写作也来套模版。  </p><p>先来八道题看看都长什么样：</p><ol><li>社会科学：<ol><li>labor shortages 劳动力短缺后，企业要竞争 compete with one another，企业要怎样 attractive to potential employees.？<ol><li>同学A:location</li><li>同学B:food</li><li>不限选项，任答原因</li></ol></li><li>develope deeper, closer relationships with a select few 和 increase the number of friends，哪个更好？<ol><li>同学A:朋友多好</li><li>同学B:朋友少但亲密好</li><li>二选一</li></ol></li><li>foundational knowledge数理化还是nurture creativity艺术课对孩子更好？<ol><li>同学A:数理化好</li><li>同学B:画画、音乐好</li><li>二选一</li></ol></li><li><strong>我艹？！我5-25考的原题</strong>居然在<a href="https://www.doufu100.cn/#/writingReport/10908/exercise/report/0/-1/null">这里面</a>？！<ol><li>说的是一个full-time position给two part-time workers做，所谓job-sharing，你觉得好不好？</li><li>同学A:不好，容易乱套，每天谁干什么搞不清楚；同学B:很好，时间上很方便，可以空余时间发展hobbies和take classes。</li><li>二选一</li></ol></li></ol></li><li>人文科学<ol><li>Should schools limit the teaching of fictional texts in favor of teaching more practical, nonfiction texts? 不要教诗、小说、虚拟故事。<ol><li>同学A:应该对自己负责，学习天下大事成为良好市民。</li><li>同学B:虽然读书没有直接影响他，但是他觉得读书好。</li><li>二选一</li></ol></li><li>set a minimum age for when children can have a mobile phone，给孩子多大自由？给孩子拥有手机设置年龄限制？<ol><li>同学A:应该设置，10岁以下都没准备好</li><li>同学B:不设置，小孩需要手机联系父母，比如紧急情况</li><li>二选一</li></ol></li><li>celebrities还是ordinary ppl来宣传商品？宣传商品是 promote a product，用的是晋升这个词。<ol><li>同学A:普通人，比如衣服广告，只有卖家秀不够，要有买家秀</li><li>同学B:名人，吸引attention就是最好策略</li><li>二选一</li></ol></li></ol></li><li>自然科学<ol><li>How much can individuals help solve the water pollution problem compared with large institutions such as corporations and governments? 相比企业和政府，个人多大程度上能解决水污染问题？<ol><li>同学A:个人影响很小，大头是工厂。</li><li>同学B:个人很关键，是 the first step，我们自己不丢有害化学元素且清理身边的水道，这很 inspiring，或者用社交媒体呼吁别人。</li><li>二选一</li></ol></li></ol></li></ol><p>基本都是二选一，只需要套模版，改上题干、题里学生的观点、自己的理由和例子就行了。  </p><ol><li>让步认可：[同学] <strong>presents a compelling argument in favor of</strong> [同学的观点].</li><li>让步理由：(不可否认) <strong>Admittedly</strong>, [同学的观点] <strong>is an excellent means to</strong> [同学观点的好处].</li><li>转折观点：<strong>However, considering</strong> [插入初步宽泛理由], <strong>I am inclined to prioritize</strong> [我的观点].</li><li>语料理由：<strong>In my opinion,</strong> [我的观点] <strong>can</strong> [初步理由].</li><li>详细解释：这个初步理由太抽象，用从句+ing状语之类的复杂句式，具体解释理由，<ol><li>比如初步理由是：increase exposure to different sides</li><li>可以具体化为：<ul><li>New supportive friends, </li><li>as potential resources, </li><li>not only will give me a great way to exchange study and career advices, </li><li>but also can help me discover life possibilities and ways of living.</li></ul></li></ol></li><li>论据细节：用记叙文六要素，比如人物+原因+发展+结果，编一个靠谱的。<ul><li><strong>For example, both faculty and students in our Computer Science Department …</strong></li></ul></li><li>总结观点：<strong>Therefore, I firmly believe that</strong> [我的观点] <strong>will</strong> [理由].</li></ol><div align=center><img src="/pics/TOEFL/论据细节.png" loading="lazy" alt="" width="66%" height="66%"></div><p><a href="#task1语料">独立口语的语料</a>其实还是能用的，再补充几个：</p><ol><li>效率(time/efficiency)<ol><li>节约时间：saves a significant amount of time / complete tasks more quickly</li><li>资源利用：utilized to their fullest potential / efficient allocation of resources</li><li>简化复杂性：simplifies complex operation / reduce unnecessary complexity</li><li>enhance efficiency, save time</li></ol></li><li>安全<ol><li>防护作用：robust layer of protection</li><li>降低负面可能：minimize exposure to danger/harm</li><li>身心健康：physical and mental well-being</li><li>ensure safety, prevent accidents/injuries/harms</li></ol></li><li>交流<ol><li>建立联系：build strong(er) connection / lead to deeper bonds</li><li>解决问题：resolve problems, misunderstandings</li><li>分享思想：sharing ideas, perspectives, insights</li><li>facilitate effective communication, enhance collaboration, promote information exchange</li></ol></li></ol><p>（可能会用到的词——跨学科：interdisciplinary / multidisciplinary / cross-disciplinary，注意discipline既是纪律，又是<strong>学科</strong>，学科别用subjects，用<strong>disciplines</strong>；实习 I intern at xxx company）</p><hr><h2 id="历年真题"><a href="#历年真题" class="headerlink" title="历年真题"></a><strong>历年真题</strong></h2><p>正在服役的题库里泄露出来的真题。可能包括一些退役的但是没有成为TPO？</p><h2 id="OG"><a href="#OG" class="headerlink" title="OG"></a><strong>OG</strong></h2><p>The Official Guide to the TOEFL Test，<a href="https://hawallieltblog.wordpress.com/wp-content/uploads/2011/04/the-official-guide-to-the-toefl-ibt.pdf">官方出的一本教程。</a></p><h2 id="机经"><a href="#机经" class="headerlink" title="机经"></a><strong>机经</strong></h2><p>考生对考试的回忆。口语第1写作第2一般比较准确？</p><h2 id="TPO-official"><a href="#TPO-official" class="headerlink" title="TPO/official"></a><strong>TPO/official</strong></h2><p>ETS卖的官方练习题，退役的原题，可以参考出题思路，题型、话题、难度可能都跟真实有差距。</p><p>托福小站macOS版里有54套题，从新东方新航道买TPO？最新都到 80 了。</p><p>做英文题的过程确实能学到东西，比如我花两年读的这篇小站54，The Commercialization of Lumber。<br>band saw 带锯，以及原来因为运输困难，美国五大湖附近的地方砍木头都要等冬天路上湖上结冰了才开始大规模工作，如果暖冬干冬就gg。sleigh雪橇用来运输。<br>1h做41道题，我的阅读速度真是需要提升，平常查东西看到长篇文章都是直接跳过找关键词的。<br>54R得24分，跟2023-9-9（用英文玩了一整年游戏，连托福考几道题考多长时间都不知道就去裸考59）差不多水平啊。这不够，4个24才96，每个部分的目标都要是27～29才能稳过95。</p><p>可以说从22年到25年3月25号，我是花了3年才第一次正经做TPO，而且只有54R没有L/S/W。</p><p>看了一周经验贴，刷了5套tpo，看了一些课，20天准备时间紧张就紧张在，前半段都在预热，都在熟悉这门考试（虽然我第二天就考88）。<br>不过好在跑上实验了，考前再跑好剩下的stage3(CoT+instructions)和conditional mattergen就有数据了，有实验结果就能写得完论文。</p><p>6-5:</p><ol><li>TPO套号与难度无关；</li><li>听力要刷的学术场景不是按高频，而是按自己的错频，学术高频参考<a href="https://www.bilibili.com/video/BV15L4y1Y7Tw">cc98推荐的林强课程</a>：<ol><li>生物出现频率最高，每套题都有？动物植物微生物；</li><li>艺术其次；</li><li>天文地质（不是地理）。</li></ol></li><li>每套题的难度=文章难度+题目难度，是方差很小的，难度的判断标准只有错题量而非认为自己听懂与否，错了题就说明自己在这套题的考察方面有不足。</li><li>用小站APP<a href="#听力练习顺序">按照学术类别练题</a>，这么练4天到周日，剩下5天再刷套题；</li><li>今天用TPO56～71的16套Speaking Task 1优化了一下我<a href="#task1">上面10个话题模板</a>（其中两三个在我的习惯里用不到）；</li><li>日常保持：200学术词，S/W，10道词汇题，<a href="https://numblr.io/">number听力</a>溜缝儿。</li><li>不来北街咖啡这天就算白给，就算放假。（然而6-9开始放弃北街了）另外，感冒了且久坐后骶尾旧病复发了，希望不要影响考试。</li></ol><p>托福100好处是题全、webapp、2小时题量，但有很多typos和一堆错误答案，最严重的是TPO74 W1综合写作reading直接给错了，没有第一段，第三段是其他文章里的。</p><h3 id="听读学术主题频率"><a href="#听读学术主题频率" class="headerlink" title="听读学术主题频率"></a><strong>听读学术主题频率</strong></h3><p>问了chatgpt、gemini、claude三个llm，得到的top5只有后几个顺序稍有区别，平均排序——</p><h4 id="听力学术主题"><a href="#听力学术主题" class="headerlink" title="听力学术主题"></a><strong>听力学术主题</strong></h4><div class="table-container"><table><thead><tr><th>排名</th><th>学术主题</th><th>平均排名</th></tr></thead><tbody><tr><td>1</td><td>生物学 (Biology)</td><td>1.0</td></tr><tr><td>2</td><td>艺术史/文学/音乐 (Art History/Literature/Music)</td><td>2.7</td></tr><tr><td>3</td><td>地质学/天文学/气象 (Geology/Astronomy/Meteorology)</td><td>3.3</td></tr><tr><td>4</td><td>心理学/社会学/人类学 (Psychology/Sociology/Anthropology)</td><td>4.0</td></tr><tr><td>5</td><td>历史/考古学 (History/Archaeology)</td><td>5.0</td></tr></tbody></table></div><h4 id="阅读学术主题"><a href="#阅读学术主题" class="headerlink" title="阅读学术主题"></a><strong>阅读学术主题</strong></h4><div class="table-container"><table><thead><tr><th>排名</th><th>学术主题</th><th>平均排名</th></tr></thead><tbody><tr><td>1</td><td>生物学 (Biology)</td><td>1.0</td></tr><tr><td>2</td><td>历史学 (History)</td><td>2.0</td></tr><tr><td>3</td><td>地质学/天文学 (Geology/Astronomy)</td><td>3.0</td></tr><tr><td>4</td><td>考古学/人类学 (Archaeology/Anthropology)</td><td>4.0</td></tr><tr><td>5</td><td>艺术史/文学/音乐 (Art History/Literature/Music)</td><td>4.7</td></tr></tbody></table></div><h3 id="听力练习顺序"><a href="#听力练习顺序" class="headerlink" title="听力练习顺序"></a><strong>听力练习顺序</strong></h3><p>小编似乎认为天文地质更难？还是说这类题大家错得多？</p><ul><li>生物：<ul><li>45-lec2(又是态度题，又是结尾专注力不够导致漏听的细节，我错误率最高的绝对是态度题，全文和对应答案句子被我摘出来了，发现确实每个学生的提问都意味着深入，也很可能是Question位置；可以学一学关键词？), </li><li><strong>56-lec2</strong>(主旨和态度题错了，主旨题能错就说明没听懂，拉出来原文对应question句子精听；<strong>态度题又是出现在lecture结尾</strong>，但错误原因是纯没听懂，compel是强迫，compelling是有说服力的，选项里的incline to是倾向于，我想成了decline。另外，Q4关于尾巴我也没听到，排除对的。)<ul><li>“—there’s no beak, the tail is long and bony(bone的adj.) the way a reptile’s is, rather than short and stubby like a modern bird’s.” 一些鸟、鱼的结构图我贴在<a href="#学科专业词汇">学术词汇</a>里。</li></ul></li></ul></li><li>文艺：<ul><li><strong>50-lec3</strong>(错了个细节题5选3。一方面北街好吵，另一方面书名人名实在听不懂，这篇所有题我都没谱！注意强调重要性的词比如it’s <strong>really</strong> a <strong>key</strong> text because it’s <strong>one of</strong> the <strong>original</strong> realist work.)<ol><li>引言： 从浪漫主义引出现实主义，给出定义和时间范围。</li><li>时代背景： 解释现实主义兴起的社会、政治和思想基础。</li><li>创作特点： 详细阐述现实主义的文学手法（真实性、客观性、人物中心等）。</li><li>代表作家： 介绍两位重要现实主义作家及其代表作。</li><li>总结： 强调其历史地位和对美国文学的影响。</li></ol></li><li>51-lec3(错了一个双选细节，本来选对了，又改错了，水彩画完给出版社继续画的部分没听到；听时想到了，人名多的题所有人名都在纸上用缩写记出来，比如JJA。这个题toefl100答案又错了(AB)，我选的是AB，正确是AD，实际上B虽然确实JJA会追踪鸟但跟他书里的illustration无关。另外，Q3批评他到底是artist还是naturalist这个我也没听到，蒙对的，<strong>critic(注意不是批评，是批评家、评论家，v. criticize, n. criticism)这类题我错误率很高!</strong>), </li><li>52-2</li></ul></li><li>天文地质：<ul><li>49-2, 55-5, 57-3, 68-3, 69-2, 71-4</li></ul></li><li>对话：<ul><li>52-1(全对，但是有细节没听懂，比如one-on-one), </li><li>68-1(错了双选细节题，<strong>exam period是老美的期末周</strong>，不是这门考试的考试时间)<br>之前做过的懒得贴了。</li></ul></li></ul><h4 id="6-9～6-13模考"><a href="#6-9～6-13模考" class="headerlink" title="6-9～6-13模考"></a><strong>6-9～6-13模考</strong></h4><ol><li>自己模考TPO46<ol><li>conv1错在没听懂 put on 是举办的意思，put on a whole range of other activities 举办一系列活动，a range of 是一系列；</li><li>lec1错在没仔细听movie相关内容，切换话题的时候一定要注意，聊着聊着ants/birds突然聊到movie就要警钟猛鸣！这里说的是电影用计算机图形学渲染，用的就是swarm intelligence的3rules生成鸟群；</li><li>lec2没错，但是从2nd theory(婴儿更多地看到抱他的父母左脸)到3nd theory(从用日光到灯光打光)的过渡我没听明白—— <strong>Exactly … Alright, what about the way the artist’s studio is organized</strong>, specifically the light source? 用what about开讲一个新理论。——由于多数画家右撇子，光从左边来比较方便画。</li><li>conv2这种文艺历史题确实是我的弱点，没搞清她到底要写啥，查了才知道是学期论文term paper，其实就是期末大论文，错的这道题是没懂教授的要求：“<strong>But</strong> remember, I’ll be grading your paper based on the details you include. <strong>And at some point in your paper, you’ll want to</strong> focus on a particular event of the Revolution, like maybe the storming of the Bastille prison?” 说的是你论文里必须要写一个特定历史事件，不要 too broad for this assignment。</li><li>lec3根本没听到法国政府的事，这一段制作过程根本没必要听懂 “The recipe was difficult. The stone had to be ground finely, not easy to do with a rock, then mixed with melted wax, resins and oils, wrapped in a cloth and knitted like bread dough.”，但这段没听懂直接影响到我后面两道题根本没心情听，无论如何能听懂多少听多少，全程保持专注理解整体讲了个什么事就行了。</li><li>lec4，主要错在这个题了——没听懂整个课在讲什么。重听都没发现是在讲要不要取消penny nickel，最后才讲的是铜的其他用途。<ul><li>1美分 (Penny / Cent)</li><li>5美分 (Nickel)</li><li>10美分 (Dime)</li><li>25美分 (Quarter)</li><li>50美分 (Half Dollar)</li><li>1美元硬币 (Dollar Coin)</li><li>材质：copper, zinc, nickel</li><li>经验1: 所有的<strong>But，Even though，Also</strong>，都是非常重要的过渡词。</li></ul></li></ol></li><li>toefl100真题2做得非常非常差，这个垃圾网站的音频也一直在卡顿<ol><li>conv1本来想选A，但实在太奇怪了，attend a lecture还要被“invited”？not open不应该是不被允许吗？所以选了“她觉得自己不是graduate或faculty水平不够”，其实这个说法不符合这个人性格。</li><li>lec1错三道，心态崩了，考古题材，Roman Villas，听完才发现是三方面1.避暑胜地/皇帝个人表达；2.有个皇帝在villa别墅里实践建筑艺术；3.villa还是农场、工厂和渔场。两个实验那种题确实可能最后才分岔，但这道题是一开始就开始进第1/3个分支了。<ul><li>经验2: <strong>first这种词听好了</strong>，But it was only through… that… (通过…做了…)，而且<strong>又是But</strong>过渡到了experiment they did第二点，<strong>Also</strong>过渡到了第三点。</li><li>证据题：So by piecing together this Information about the environment around the villa, as well as the size of the aqueduct. We can pretty safely assume that dye equations, estate was the site of a textile factory, one that probably employed thousands of people. 这之前说了河里的 sulfur 用来做纺织工厂 textile 染料。</li></ul></li><li>lec2错三道，听不懂turbine，也听不懂两种turbine分别是什么，按理说可以看到图的，这个网站没有图，而且托福100的真题音频很卡，但也有个教训是要习惯各种语言习惯，这个男的连音超多。But one feature that makes any model of vertical access turbine preferable to a propeller turbine is its ability to function in a wide range of wind conditions. And it also operates in turbulent gusty winds. Storm force winds. Propeller turbines just can’t efficiently handle winds on these two opposite ends of the spectrum.</li><li>conv2错两道，主要问题是没听懂大的逻辑，重听前脑袋里没有这个谱：“教授说去解剖anatomy不是analogy(类似)博物馆要看lung肺的模型，然后交流了两个呼吸系统器官，分别负责交换气体和呼吸气体“，就这么简单，脑袋里有这个谱就很好判断。She confused terms related to the respiratory system.</li><li>lec3全对，我也要自己制作皮影戏玩！</li></ol></li><li>TPO69<ol><li>conv1 没错，借CD</li><li>lec1 错得最多，<ol><li>Q2一上来就说了，hydrothermal vents, … , it’s like a geysers, except it’s on the ocean floor. 这种<strong>对比关系 distinction, different, contrast, resemble, similar, like, likewise，一定要关注！</strong> 后面其实还提了一遍，A hydrothermal vent is essentially this same thing, but the water is emitted out of cracks or, or fractures in the ocean floor.，same thing, BUT，我其实听到了这句，但以为是成因而不是地点区别，没理解…</li><li>Q3也是一上来就有，要<strong>注意这些强调词比如 significance, 各种比较级最高级和程度词，first original这些</strong>：But, the vents are also enormous <strong>significance</strong> for us. Beacause … chemistry …</li></ol></li><li>lec2 重听题错了，<ol><li>错因：听不懂 enought lecturing, I can get pretty passionate to …这个是在自嘲，我只能通过他的声音判断，但这个读题人没体现出情绪。</li><li>enough … 够了！注意一下</li></ol></li><li>conv2 没听到这个地方，说简爱takes place in England，另一个人主要关注的是Jamaica，意思是让学生思考得更深点。</li><li>lec3 重听题又错了，<ol><li>错因：没听过“blurry”这个词，虽然知道是“模糊”但是听到音频信号反应不过来：“And here’s the thing, the crab’s visual field is even blurrier <strong>in</strong> the center upon the sky than it is <strong>on</strong> the edges.” 螃蟹中间视野比边缘更模糊。</li><li>而且这句话是在重听题的后面，没有出现在重听题题干里，误导性强，要求当时听原文听懂了这个逻辑。</li></ol></li></ol></li></ol><h3 id="阅读练习顺序"><a href="#阅读练习顺序" class="headerlink" title="阅读练习顺序"></a><strong>阅读练习顺序</strong></h3><p>文科题对我来说好像更难懂？不管是听力的文艺还是阅读的历史。</p><ul><li>生物：<ul><li>之前做过：54-2</li><li>30-1(跟着mooc做的，太简单，但确实感觉到有意识提取主干能提升阅读速度), </li><li>42-1(我勒个豆，不知道是不是因为看完mooc悟了, 第一次全对, 六选三也对了（经验是弄清段落的总分结构，三句话一定能完整总结全文）), </li><li>67(只有手机上有，没法做), </li><li>45-1(生态题，只错一个6选3，因为关键句没看懂。)</li></ul></li><li>历史：<ul><li>之前做过：75-2</li><li>49-2(词汇题（理解错了，原文说的是“paper”这个词contracted from“papyrus”，原来如此）和6选3错了，这6选3看答案都没懂D为什么对，原来是<strong>答案错了</strong>，toefl100和小站的6个选项顺序都错了，D是在paragraph3，是个细节——印刷厂是欧洲最早的工业first true industry), <ul><li>标题都没看懂，Movable Type就是活字印刷术（每个字提前刻好拼成文章），movable可移动的，type是活字、文字、typeface是字体font，跟刻在木板上的雕版印刷（block printing）相对。</li></ul></li><li>50-2(错了一个修辞目的题，原因是 <strong>position 有立场的意思，the position taken by courtiers 大臣的立场被我错误理解成了篡位</strong>；原文逻辑理得不够顺，脑子里没有整个故事，在<a href="#读r">上面Reading攻略部分</a>我把这个精彩的故事梳理了一下。精力不集中的时候做得就达不到要求，这两篇都花了21分钟多，虽然原tpo是20分钟的上限而不是18，但还是亟需提高抓句子主干的速度), </li><li>51-1, 71-2</li></ul></li><li>天文地质：<ul><li>42-2, 52-1</li></ul></li></ul><h4 id="6-9～6-13模考-1"><a href="#6-9～6-13模考-1" class="headerlink" title="6-9～6-13模考"></a><strong>6-9～6-13模考</strong></h4><ol><li>自己模考TPO46错了俩词汇题和一个6选3…<ol><li>virtue在暗黑地牢里明明学过是美德的意思，now and then是有时…我以为是总是。跟from time to time一样，表达的是这时那时，时有发生。</li><li>6选3完全没看懂第二篇哪里说“集权”了，原来就在第四段倒数第二句说有更高的税就能更集权，我只看到了最后一句说商人跟当权者合作。</li></ol></li><li>toefl100真题1，两篇都只错了6选3，第一篇我觉得答案也有问题（事实是gemini说我的是对的，答案是错的，所以我这套做了29分），第二篇几乎没看懂大气层的一堆术语和化学元素术语：<ol><li>The atmosphere is composed of troposphere, stratosphere, mesosphere, ionosphere and exosphere. 图贴在<a href="#学科专业词汇">下面 term 部分</a>，其中troposphere对流层是天气层，飞机如果没有航线冲突倾向于在更平稳的stratosphere平流层飞。</li><li>sulfur dioxide是二氧化硫，ozone臭氧</li></ol></li><li>TPO69<ol><li>第一篇错了个插句子题，这篇做慌了，很少错这类题；</li><li>第二篇错了三个，第一个因为typo，east给doufu100写成了eat，真是个垃圾平台；第二个没道理，纯犯畜；第三个问作者为什么提到大海龟和大鸟，是想说他们补充了mammals的这个生态位的缺失，<strong>完全因为看不懂niche（只知道是壁龛），不知道是生态位。</strong></li></ol></li></ol><h3 id="学科专业词汇"><a href="#学科专业词汇" class="headerlink" title="学科专业词汇"></a><strong>学科专业词汇</strong></h3><p>地球：</p><div align=center><img src="/pics/TOEFL/earth.jpg" loading="lazy" alt="" width="66%" height="66%"></div><p>atmosphere大气层：</p><div align=center><img src="/pics/TOEFL/atmosphere.png" loading="lazy" alt="" width="66%" height="66%"></div><p>太阳系（没有Pluto冥王星，不是行星，只有8个）：</p><div align=center><img src="/pics/TOEFL/MicrosoftTeams-image-22.jpg.webp" loading="lazy" alt="" width="66%" height="66%"></div><p>grain（Cereal是Grain的子集，指禾本科(1.长得像野草的作物们；2.果皮和种子紧贴在一起（颖果），苹果我们吃的是内外果皮，里面的核就是种子)，尤其在美国前者通常指麦片）：</p><div align=center><img src="/pics/TOEFL/cereals.png" loading="lazy" alt="" width="66%" height="66%"></div><ol><li>Ear of wheat (麦穗): 特指小麦植株上，包含许多麦粒的部分。这是一个具体的植物结构。</li><li>Wheat (小麦): 一种重要的谷物，广泛用于制作面粉，进而制作面包、面条、饼干等。含有麸质。</li><li>Rye (黑麦): 另一种谷物，与小麦类似，但通常在较冷、贫瘠的土壤中生长。用于制作黑麦面包、威士忌等。含有麸质。</li><li>Rice (大米/稻谷): 亚洲地区最重要的主食之一，有多种类型（如白米、糙米、糯米等）。不含麸质。</li><li>Barley (大麦): 一种古老的谷物，常用于制作啤酒、麦芽糖，也可用于煮粥、炖汤。含有麸质。</li><li>Oats (燕麦): 一种营养丰富的谷物，常用于制作燕麦片、燕麦粥。通常被认为是无麸质的，但可能会在加工过程中与含麸质谷物交叉污染。</li><li>Maize/Corn (玉米): 在美洲地区非常重要的谷物，用途广泛，可以作为蔬菜直接食用，也可以加工成玉米粉、玉米淀粉、玉米油等。不含麸质。</li><li>Millet (小米/黍): 一类小粒谷物的总称，包括多种种类。在亚洲和非洲一些地区是重要的主食。不含麸质。</li></ol><p>高粱是Sorghum，我没记得我吃过高粱？</p><p>前20 periodic table：（hydrate是水合物，carbohydrate碳水(carbon没有n)，dioxide是二氧化物，carbon dioxide是二氧化碳）</p><div align=center><img src="/pics/TOEFL/what-are-the-first-20-elements-608820-FINAL-5b758ab446e0fb002c67279a.png" loading="lazy" alt="" width="66%" height="66%"></div><p>植物：<a href="https://www.cannagardening.com/articles/basic-structure-plants">这篇文章</a>里各种图都有，都画得很精美，我只借用一张。</p><div align=center><img src="/pics/TOEFL/plants.jpg.webp" loading="lazy" alt="" width="66%" height="66%"></div><p>鹅：</p><div align=center><img src="/pics/TOEFL/054-morphology-of-a-goos_orig.jpg" loading="lazy" alt="" width="66%" height="66%"></div><p>鱼：</p><div align=center><img src="/pics/TOEFL/The-general-morphological-structure-of-a-fish-Retrieved-from.png" loading="lazy" alt="" width="66%" height="66%"></div><p>atom: nucleus原子核的复数是Nuclei</p><div align=center><img src="/pics/TOEFL/image-5c57789265fda.png" loading="lazy" alt="" width="66%" height="66%"></div><h3 id="所有学术“学科”词"><a href="#所有学术“学科”词" class="headerlink" title="所有学术“学科”词"></a><strong>所有学术“学科”词</strong></h3><h4 id="一、以-ology-结尾（表示“……学”）"><a href="#一、以-ology-结尾（表示“……学”）" class="headerlink" title="一、以 -ology 结尾（表示“……学”）"></a>一、以 <code>-ology</code> 结尾（表示“……学”）</h4><div class="table-container"><table><thead><tr><th>学科名称</th><th>中文</th></tr></thead><tbody><tr><td>biology</td><td>生物学</td></tr><tr><td><strong>geology</strong></td><td><strong>地质学</strong></td></tr><tr><td>psychology</td><td>心理学</td></tr><tr><td>sociology</td><td>社会学</td></tr><tr><td><strong>anthropology</strong></td><td><strong>人类学</strong></td></tr><tr><td>ecology</td><td>生态学</td></tr><tr><td>archaeology</td><td>考古学</td></tr><tr><td>zoology</td><td>动物学</td></tr><tr><td><strong>paleontology</strong></td><td><strong>古生物学</strong></td></tr><tr><td>climatology</td><td>气候学</td></tr><tr><td>physiology</td><td>生理学</td></tr><tr><td>neurology</td><td>神经学</td></tr><tr><td>theology</td><td>神学</td></tr><tr><td>toxicology</td><td>毒理学</td></tr><tr><td>pharmacology</td><td>药理学</td></tr><tr><td>metrology</td><td>度量学</td></tr><tr><td><strong>ornithology (-ist)</strong></td><td><strong>鸟类学 (家)</strong></td></tr></tbody></table></div><hr><h4 id="二、以-ics-结尾（表示“……学”）"><a href="#二、以-ics-结尾（表示“……学”）" class="headerlink" title="二、以 -ics 结尾（表示“……学”）"></a>二、以 <code>-ics</code> 结尾（表示“……学”）</h4><div class="table-container"><table><thead><tr><th>学科名称</th><th>中文</th></tr></thead><tbody><tr><td>physics</td><td>物理学</td></tr><tr><td>economics</td><td>经济学</td></tr><tr><td>linguistics</td><td>语言学</td></tr><tr><td>mathematics</td><td>数学</td></tr><tr><td>statistics</td><td>统计学</td></tr><tr><td><strong>acoustics</strong></td><td><strong>声学</strong></td></tr><tr><td>mechanics</td><td>力学</td></tr><tr><td>genetics</td><td>遗传学</td></tr><tr><td>robotics</td><td>机器人学</td></tr><tr><td>optics</td><td>光学</td></tr><tr><td><strong>aesthetics</strong></td><td><strong>美学</strong></td></tr><tr><td>politics</td><td>政治学</td></tr><tr><td><strong>ethics</strong></td><td><strong>伦理学</strong></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>形容词</th><th>中文</th></tr></thead><tbody><tr><td>economical</td><td>节约的（注意区别 economic）</td></tr><tr><td>economic</td><td>经济的</td></tr><tr><td>acoustic</td><td>声学的，指的是声音的物理上振动传播</td></tr><tr><td>aural</td><td>听觉的，是进入人耳的声音</td></tr><tr><td>oral</td><td>口头的，aural长得还真像这</td></tr></tbody></table></div><hr><h4 id="三、以-nomy-graphy-metry-try-结尾"><a href="#三、以-nomy-graphy-metry-try-结尾" class="headerlink" title="三、以 -nomy / -graphy / -metry / -try 结尾"></a>三、以 <code>-nomy</code> / <code>-graphy</code> / <code>-metry</code> / <code>-try</code> 结尾</h4><div class="table-container"><table><thead><tr><th>学科名称</th><th>中文</th></tr></thead><tbody><tr><td><strong>astronomy</strong></td><td><strong>天文学</strong></td></tr><tr><td><strong>agronomy</strong></td><td><strong>农学</strong></td></tr><tr><td>taxonomy</td><td>分类学</td></tr><tr><td><strong>geography</strong></td><td><strong>地理学</strong></td></tr><tr><td>cartography</td><td>制图学</td></tr><tr><td><strong>geometry</strong></td><td><strong>几何学</strong></td></tr><tr><td>trigonometry</td><td>三角学</td></tr><tr><td>chemistry</td><td>化学</td></tr><tr><td><strong>anatomy</strong></td><td><strong>解剖学</strong></td></tr></tbody></table></div><hr><h4 id="四、以-y-其他后缀结尾的常见学科"><a href="#四、以-y-其他后缀结尾的常见学科" class="headerlink" title="四、以 -y / 其他后缀结尾的常见学科"></a>四、以 <code>-y</code> / 其他后缀结尾的常见学科</h4><div class="table-container"><table><thead><tr><th>学科名称</th><th>中文</th></tr></thead><tbody><tr><td>history</td><td>历史</td></tr><tr><td>philosophy</td><td>哲学</td></tr><tr><td>pedagogy</td><td>教育学（教学法）</td></tr><tr><td>metallurgy</td><td>冶金学</td></tr><tr><td>logic</td><td>逻辑学</td></tr><tr><td>law</td><td>法学</td></tr><tr><td><strong>literature</strong></td><td><strong>文学</strong></td></tr></tbody></table></div><h1 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a><strong>参考文献</strong></h1><ol><li>cc98，托福帖子太多了，随便挑了几篇有眼缘的作为主要参考，去年充了2000块校内vpn，现在偷着乐。</li><li>未来一个月的 3 次托福考试（2025-5-18 21:21）。想起来当时盲目自信，且甘愿拖延到最后才开始准备的心态，不断重复同一个错误。 这一个月考不出 95 分托福，没法免修就修不满学分，毕业继续延期，工作也要丢，不成功便成仁；</li><li>我还欠 100 顿酒的苏小淇，距离她给我写那篇攻略已经过去了 900 天，直到现在（2025-5-18 21:23）我才发现这考经写得这么用心，直到现在我才发现她写的歌这么用心，2022年到年底真的快被傻逼组、不擅长的工作、积攒了一年半的悲痛情绪和傻逼疫情压垮了，但这不是伤害别人的理由（然后在2025-6-6 20:11发现原来她的攻略有部分是copy的，不爱酸苦辣的写作tips跟她的完全一样，但不爱酸苦辣是2022年初发的小红书，她是年底给我的攻略，昨天看听/读mooc就感觉有些地方相似…）；</li><li>文章未完待续。</li></ol><h1 id="6-13"><a href="#6-13" class="headerlink" title="6-13"></a><strong>6-13</strong></h1><p>考前一天，RL模考共53，最后的任务是熟悉用肿的手写铅笔字和打字。</p><p>Reading 27，错的是：</p><ol><li>第一篇第八题，两个相近的选项没有稍微再多仔细分辨一下；</li><li>第一篇第九题，居然不是插入句子，题目要true我以为是wrong/false逻辑看反了；</li><li>第二篇第七题，mastery只知道大概是精通，有掌控的意思。虽然这两篇的6选3都对了，但这篇错主要还是有些句子没读明白导致思绪乱飞。</li></ol><p>Listening 26，错的是：</p><ol><li>conv1和conv2各错了一个双选，都是只听到一半细节。<ol><li>Though 这个关键词也要注意！！！</li><li>口音听不明白，最后两句没听清是建议他先忙学业，推迟工作。But听到了，ought to念太快根本听不出来，以为是 you know how to 而不是 you ought to。</li></ol></li><li>lec1和lec2没错，lec3错了两个，这个老太太就是conv2那个声优，这个实验根本没听明白，最后一道题真的千万别放松警惕，这个时候可能已经听累了，但深呼吸找回状态：<ol><li>deception deceive 欺骗这俩词没听明白。另外！语速放缓的时候仔细听，是重点。后面重新出现了一次欺骗：low-ranking 猴子会有 false alarm call 阻止新成员进入，false alarm 听成了 fossil。。。</li><li>这个 false alarm 包括后面的男生的提问（即解释），说 know there’s no leopard but believe the others would think there’s a leopard 这里面的 no 也没听到，但凡听明白其中之一这俩题都能做对。</li></ol></li></ol><p>早睡早起，明上午再看看作文素材和学科单词，就这样了。</p><h1 id="6-15"><a href="#6-15" class="headerlink" title="6-15"></a><strong>6-15</strong></h1><p>大概率是93～94分了，R27L22，通宵一整晚没睡着，S和W的状态不可能比L强太多，cc98上大家普遍是42～45，我需要46才能到95分。<br>不能怪女友了，我TPO本来就只有25分的平均水平，状态不好掉几分很正常。<br>未来一周全部拿来听TPO听力，从第一套开始往后逐句听懂，备考下次6-21。</p><p>结果TPO25听的第一个conv1就是关于毕业问题的，哪壶不开提哪壶（苦笑。</p><h1 id="youtube资源练听力"><a href="#youtube资源练听力" class="headerlink" title="youtube资源练听力"></a><strong>youtube资源练听力</strong></h1><p>only take notes about what you understand</p><p>notes → big picture, don’t write down any words you hear</p><p>很多时候at the end才理解main idea是正常的，don’t get stuck on that</p><p>上一秒还在听听力，<a href="https://www.youtube.com/watch?v=TDPDtrLxT-c">tst prep 这一套</a>错了十几个，几乎都要对6-21绝望，下一秒查分99，哈哈哈哈哈哈哈！给自己做了两三天心里建设，看来是用不上了。</p><div align=center><img src="/pics/TOEFL/Screenshot 2025-06-17 at 17.58.22.png" loading="lazy" alt="不要hold不要复审"></div><p>考场很舒适，这次东6机房居然一共只有六七个人所以很安静，隔板很高空间很独立。<br>可完全集中不了注意力，第一篇的时候看两句就开始涣散，<br>但我绝对不能失败。</p><p>本来以为SW也就42～46，因为cc98上速通贴大多都是这个水平，我也本以为会只差一分考94免修失败。<br>没想到sw考出个50分！模版真好用啊，那么拉的精神状态都超常拿分了。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Test of English as a Foreign Language.  &lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="英语免修" scheme="http://blog.v2beach.cn/tags/%E8%8B%B1%E8%AF%AD%E5%85%8D%E4%BF%AE/"/>
    
      <category term="RUN" scheme="http://blog.v2beach.cn/tags/RUN/"/>
    
  </entry>
  
  <entry>
    <title>SSL certificate</title>
    <link href="http://blog.v2beach.cn/2025/03/09/SSL-certificate/"/>
    <id>http://blog.v2beach.cn/2025/03/09/SSL-certificate/</id>
    <published>2025-03-09T14:02:09.000Z</published>
    <updated>2025-03-18T08:29:01.059Z</updated>
    
    <content type="html"><![CDATA[<p>Secure Sockets Layer（安全套接字层）技术上已经被弃用，只是习惯上仍如此称呼，现在用的技术是更高效有效的Transport Layer Security（TLS，传输层安全）。本文除了分析协议全过程外还涉及证书申请和证书内容、加密算法和哈希算法。</p><a id="more"></a><h1 id="协议总览"><a href="#协议总览" class="headerlink" title="协议总览"></a><strong>协议总览</strong></h1><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[客户端] ------&gt; [服务器]</span><br><span class="line">   Client Hello  ------&gt;  Server Hello + 证书</span><br><span class="line">   验证证书</span><br><span class="line">   生成 Pre-Master Secret</span><br><span class="line">   使用公钥加密  ------&gt;  私钥解密 + 会话密钥生成</span><br><span class="line">   [加密数据传输]</span><br></pre></td></tr></table></figure><h1 id="握手阶段"><a href="#握手阶段" class="headerlink" title="握手阶段"></a><strong>握手阶段</strong></h1><p>SSL/TLS Handshake</p><h2 id="1-客户端Hello"><a href="#1-客户端Hello" class="headerlink" title="1. 客户端Hello"></a><strong>1. 客户端Hello</strong></h2><p>client向server发送一个Hello消息，包括：</p><ul><li>支持的SSL/TLS版本</li><li>支持的加密算法（AES，chacha？，RSA，DES？等  gcm呢？</li><li>随机数client random用于生成密钥</li><li>其他扩展信息如SNI（Server Name Indication，服务器名称指示），如果一个ip上有多个服务可以用SNI指定域名</li></ul><h2 id="2-服务器Hello"><a href="#2-服务器Hello" class="headerlink" title="2. 服务器Hello"></a><strong>2. 服务器Hello</strong></h2><p>server向client回复一个Hello消息，包括：</p><ul><li>确认使用的SSL/TLS版本</li><li>确认的加密算法</li><li>随机数server random用于生成密钥</li><li>服务器的SSL/TLS证书（certificate）</li></ul><h2 id="3-客户端对服务器的证书验证"><a href="#3-客户端对服务器的证书验证" class="headerlink" title="3. 客户端对服务器的证书验证"></a><strong>3. 客户端对服务器的证书验证</strong></h2><p>Certificate Verification :</p><ul><li>检查证书是否由受客户端信任的CA签发</li><li>检查证书是否已过期</li><li>检查证书的域名与目标服务器是否匹配</li></ul><p>所以证书实际上只有在一台设备第一次访问服务器时会用到，验证过公钥可靠性之后，客户端保存下来公钥，只要公钥没被server改变也没被client删掉，证书就不再参与加密过程了。</p><h3 id="证书内容"><a href="#证书内容" class="headerlink" title="证书内容"></a><strong>证书内容</strong></h3><p>.pem, .crt文件里的<code>-----BEGIN CERTIFICATE-----, ...,  -----END CERTIFICATE-----</code>看起来是乱码通常是 Base64 编码的 ASN.1（二进制）数据，二进制易于存储传输。<br>根据 X.509 标准，用<code>openssl x509 -in example.crt -text -noout</code>命令解码后证书中包含以下信息：</p><ol><li>版本号（Version）：证书使用的 X.509 版本（如 v3）。</li><li>序列号（Serial Number）：证书的唯一标识号。</li><li>签名算法（Signature Algorithm）：用于签名的算法（如 RSA）。</li><li>颁发机构（Issuer）：签发证书的 CA（证书颁发机构）。</li><li>有效期（Validity）：证书的起始日期和过期日期。</li><li>主体（Subject）：证书所属实体（如网站域名）。</li><li>公钥信息（Public Key）：包含公钥及其算法。</li><li>扩展字段（Extensions）：包括用途等。</li><li>签名（Signature）：由 CA 用其私钥生成，用于验证证书的真实性。</li></ol><p>按适用范围分类有三种：</p><ol><li>单域名证书：保护单个域名，如 <code>example.com</code>，不适用于 <code>sub.example.com</code>；</li><li>多域名证书（SAN 证书，Subject Alternative Name）：允许多个不同域名共享一个证书，如 <code>a.com</code>、<code>b.net</code>、<code>c.org</code>；</li><li>泛域名证书（Wildcard Certificate）：适用于主域名及所有子域名，例如 <code>*.example.com</code> 可用于：<code>www.example.com</code>、<code>mail.example.com</code>、<code>blog.example.com</code>，但不支持二级以上子域名（如 a.b.example.com）。</li></ol><h3 id="ACME协议"><a href="#ACME协议" class="headerlink" title="ACME协议"></a><strong>ACME协议</strong></h3><p>ACME协议的工具有：</p><ul><li>Certbot（Let’s Encrypt 官方推荐）</li><li>acme.sh（轻量级 Shell 脚本实现）</li><li>LEGO（Go 语言实现）</li></ul><p><a href="https://letsencrypt.org/getting-started/#">Let’s Encrypt的官网</a>是不能申请证书的，完全通过 ACME client 操作，官方提供的是 certbot。</p><p>ACME（Automatic Certificate Management Environment，自动证书管理环境）是一种用于自动化管理TLS/SSL证书的协议，由Let’s Encrypt和Internet Security Research Group (ISRG) 开发并由 IETF（Internet Engineering Task Force）标准化，定义在 <a href="https://datatracker.ietf.org/doc/html/rfc8555">RFC 8555</a>。</p><p>这里选用 <a href="https://github.com/acmesh-official/acme.sh">acme.sh</a> ，因为我希望尽量轻量而且一步到位完全自动，想要在我阿里云 CDN + OSS 的架构上自动申请证书要先回忆一下我到底是怎么搭建博客的。</p><ol><li>首先hexo生成的是静态网站，这个static不是说没有Javascript的动态交互，而是说没有数据库操作，服务端只提供html等资料，部署在OSS（Object Storage Service）就跟存在网盘或者图床上的资源差不多性质。</li><li>然后我用CDN（Content delivery network）提升访问速度，以<a href="wiki.v2beach.cn">本网站的 wiki</a> 为例，<ul><li>阿里云 CDN 服务的 Edge Servers 域名是 <code>wiki.v2beach.cn.w.kunlunaq.com</code>，wiki 本身通过阿里云 DNS 的 CNAME（域名→域名）映射到这个域名，这个域名再映射到下图所示的多个边缘服务器。</li><li><strong>如果 CDN 上没有缓存</strong>，会进行回源访问 <code>v2beach.github.io:443</code> 这个 GitHub Pages 源站，如果是 <code>blog.v2beach.cn</code> 的 CDN <code>blog.v2beach.cn.w.kunlunca.com</code> 上缓存不命中则是回源到 <code>blog-v2beach-cn.oss-cn-chengdu.aliyuncs.com:443</code> 这个 oss 原址上，试图将静态资源缓存到 CDN。</li></ul></li></ol><p><img src="/pics/how-does-a-CDN-work.jpg" alt=""></p><p>所以实际上我需要的是将申请到的证书再上传到阿里云证书管理服务，并部署到 CDN 上。<br><img src="/pics/Screenshot 2025-03-17 at 21.24.50.png" alt=""></p><p>好在七八年前的<a href="https://github.com/acmesh-official/acme.sh/issues/1461">这个 issue</a> 里有人已经写过了 <a href="https://github.com/acmesh-official/acme.sh/blob/master/deploy/ali_cdn.sh">deploy/ali_cdn</a>：<br><strong>安装阶段 acme.sh 做了这些操作</strong>：</p><ol><li>Create and copy acme.sh to your home dir (<code>$HOME</code>): <code>~/.acme.sh/</code>. All certs will be placed in this folder too.</li><li>Create alias for: <code>acme.sh=~/.acme.sh/acme.sh</code>.</li><li>Create daily cron job to check and renew the certs if needed.<br><strong>然后就可以 issue certs（签发证书）了</strong>，根据 <a href="https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert">How to issue a cert</a> 和<a href="https://github.com/acmesh-official/acme.sh/wiki/dnsapi#11-use-aliyun-domain-api-to-automatically-issue-cert">在阿里云 DNS 添加 txt 记录从而自动签发的 api</a></li></ol><p>这两步阿里云 api 要申请权限。</p><p><img src="/pics/Screenshot 2025-03-17 at 22.12.35.png" alt=""></p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">./acme.sh --issue --dns dns_ali -d <span class="string">'v2beach.cn'</span> -d <span class="string">'*.v2beach.cn'</span></span><br></pre></td></tr></table></figure><p>为主域名和子域名一块申请泛域名证书。<br><strong>之后在阿里云 CDN 部署证书</strong>，根据 <a href="https://github.com/acmesh-official/acme.sh/wiki/deployhooks#34-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun">deplothooks 文档</a>，</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line"><span class="built_in">export</span> DEPLOY_ALI_CDN_DOMAIN=<span class="string">"blog.v2beach.cn wiki.v2beach.cn"</span></span><br><span class="line">./acme.sh --deploy -d <span class="string">'v2beach.cn'</span> --deploy-hook ali_cdn</span><br></pre></td></tr></table></figure><p>这里部署的域名是被加速的域名，而不是 Edge Servers CNAME 域名。<br><img src="/pics/Screenshot 2025-03-17 at 22.33.18.png" alt="完成，希望自动配置的 cron 任务顺利更新"></p><p>原来 cron 定时任务这个单词跟 chrono ark 是同一个词源，都是来自希腊语的时间。</p><p>本来想用 Docker 管理，但离开商汤后两年没用了，手生没办法还是用 git 操作了。</p><h3 id="Let’s-Encrypt证书内容"><a href="#Let’s-Encrypt证书内容" class="headerlink" title="Let’s Encrypt证书内容"></a><strong>Let’s Encrypt证书内容</strong></h3><p>证书签发过程中除了<a href="#证书内容">上面证书内容</a>里说的<strong>.pem, .crt之外还会有.key文件（即私钥），公钥已经包含在证书文件里了。</strong><br>这个密钥是在 server hello 里跟 client 传证书时数字签名用的。</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">[Mon Mar 17 22:14:34 CST 2025] Your cert is <span class="keyword">in</span>: /Users/v2beach/.acme.sh/v2beach.cn_ecc/v2beach.cn.cer</span><br><span class="line">[Mon Mar 17 22:14:34 CST 2025] Your cert key is <span class="keyword">in</span>: /Users/v2beach/.acme.sh/v2beach.cn_ecc/v2beach.cn.key</span><br><span class="line">[Mon Mar 17 22:14:34 CST 2025] The intermediate CA cert is <span class="keyword">in</span>: /Users/v2beach/.acme.sh/v2beach.cn_ecc/ca.cer</span><br><span class="line">[Mon Mar 17 22:14:35 CST 2025] And the full-chain cert is <span class="keyword">in</span>: /Users/v2beach/.acme.sh/v2beach.cn_ecc/fullchain.cer</span><br></pre></td></tr></table></figure><p><strong>每个证书都包含证书链，证书链里每一个证书（Root CA，Intermediate CA）都要被验证通过，否则证书验证失败。</strong></p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[服务器证书]  &lt;-- 由 [中间 CA] 签发</span><br><span class="line">[中间 CA]  &lt;-- 由 [根 CA] 签发</span><br><span class="line">[根 CA]  &lt;-- 由 操作系统&#x2F;浏览器 预置信任</span><br></pre></td></tr></table></figure><p>根 CA 采用分层架构是为了极大降低根 CA 私钥使用频率，中间 CA 被攻破的情况下会受中间人攻击的服务器大大减少。</p><ul><li>根 CA 只用于签发中间 CA 证书，签发频率极低（可能几年才用一次）。</li><li>根 CA 私钥存储在离线环境（HSM 设备），并且通常处于冷存储，无法通过网络访问，极大降低了攻击面。即使根 CA 被攻破，攻击者仍然需要物理访问HSM 设备（Hardware Security Module，硬件安全模块是一种专门用于存储和保护加密密钥的硬件设备），否则无法使用私钥进行签发。</li><li>相比之下，如果根 CA 直接签发每一个终端证书，私钥必须频繁使用，暴露在在线环境的风险更大。</li></ul><p><code>openssl x509 -in /Users/v2beach/.acme.sh/v2beach.cn_ecc/v2beach.cn.cer -noout -text</code> 翻译二进制证书，基本符合<a href="#证书内容">上面证书内容部分</a>列举的内容。</p><h2 id="4-生成各种密钥"><a href="#4-生成各种密钥" class="headerlink" title="4. 生成各种密钥"></a><strong>4. 生成各种密钥</strong></h2><ol><li>随机生成预主密钥 (Pre-Master Secret)；</li><li>用Pre-Master Secret和client random以及server random共同生成主密钥 (Master Secret)；</li><li>用Master Secret派生出一系列会话密钥 (Session Key)。</li></ol><p>生成Session Key本身的过程用<strong>非对称加密</strong>传输随机数密钥，这个Session Key进而用于数据传输的<strong>对称加密</strong>。</p><p><strong>为什么费劲两轮加密？第一次非对称加密是想办法在不直接传输密钥的情况下让客户端和服务器得到相同密钥，因为如果直接传输约定的密钥，只需截获这个信息和加密算法（通过任何渠道——截获Hello，穷举，从密钥反推都可以得到加密算法），第三方破译信息甚至伪装成客户端或服务器也就轻而易举了。</strong>之后的对称加密则是在确保密钥不泄漏的情况下进行安全的数据传输了。</p><h3 id="如果用RSA传输Pre-Master-Secret"><a href="#如果用RSA传输Pre-Master-Secret" class="headerlink" title="如果用RSA传输Pre-Master Secret"></a><strong>如果用RSA传输Pre-Master Secret</strong></h3><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Client: [客户端随机生成 48 Bytes Pre-Master Secret] → [使用服务器公钥（Server Hello中获得）加密] → [发送给服务器]</span><br><span class="line">Server: [用私钥解密] → [获得 Pre-Master Secret]</span><br></pre></td></tr></table></figure><p>很自然地提问，为什么SSL/TLS中不角色互换，让服务端随机生成PMS，而客户端保存私钥提供公钥？</p><ol><li>客户端私钥容易丢失；</li><li>RSA私钥解密计算开销大（$c^d \mod n$），pc或许可以承受，移动端就加重设备负担；</li><li>chatgpt说会加大MITM风险（Man-In-The-Middle，中间人攻击）？可是中间人攻击应该是从server Hello就拦截掉了，向client伪造自己是server。向server伪造自己是client，按我的理解MITM攻击跟谁存私钥关系不大。</li></ol><p>算法细节总结在<a href="#rivestshamiradleman">下文Rivest–Shamir–Adleman</a>。</p><p>举例来说，假如p=2，q=3，公钥指数e=5，计算只需要这三个数字。</p><ol><li><script type="math/tex">n = 2 \times 3=6</script>；</li><li><script type="math/tex">\varphi(6) = (2 - 1) \times (3 - 1)=2</script>，跟6互质的有1和5；</li><li>选择互质的e=5，<script type="math/tex">\gcd(5, \varphi(6)) = 1</script>；</li><li>寻找这样一个d，<script type="math/tex">5 \times d \equiv 1 \mod 2</script>，这个私钥指数可以是…, -1, 1, 3, 5, 7, …，任何一个都可以解密，但一般选最小正整数这里即1，减少运算量；</li><li>公钥即（6, 5），私钥即（6, 1）。</li></ol><p>假设要加密的明文M=3（M一般要设定0到n之间，否则会解密出错，所以p和q一般设置很大），加密过程：$C=3^5 \mod 6=3$；解密过程：$M=3^1 \mod 6=3$。<br>发现n过小的时候C=M，所以n甚至会设置为2048位的大数。<br>经验1.<script type="math/tex">0<M<n</script>；2.<script type="math/tex">n>10^{1024}</script>。</p><p>涉及参数分别是$p, q，n，\phi(n)，e\rightarrow d$，其中，n是要公开（不加密，明文）发送给客户端的，e是选定的跟n组成公钥一起公开，最后密文C是公开的，其他所有数字都是保密的。</p><h3 id="如果用ECDH生成Pre-Master-Secret"><a href="#如果用ECDH生成Pre-Master-Secret" class="headerlink" title="如果用ECDH生成Pre-Master Secret"></a><strong>如果用ECDH生成Pre-Master Secret</strong></h3><p>双方各自生成一个私钥（私密）和一个公钥（可公开）。<br>双方交换各自的公钥，用对方的公钥和自己的私钥，分别用椭圆曲线和一个基点（不是焦点，是曲线上的点）计算出相同的 Pre-Master Secret。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Client: [生成私钥 a] → [计算公钥 A &#x3D; g^a mod p] → [发送 A]</span><br><span class="line">Server: [生成私钥 b] → [计算公钥 B &#x3D; g^b mod p] → [发送 B]</span><br><span class="line">双方计算：Pre-Master Secret &#x3D; B^a mod p &#x3D; A^b mod p</span><br></pre></td></tr></table></figure></p><h3 id="PRF生成Master-Secret"><a href="#PRF生成Master-Secret" class="headerlink" title="PRF生成Master Secret"></a><strong>PRF生成Master Secret</strong></h3><p>基于一段数字PMS生成另一段随机数字MS，但要求得知seed（Key）的情况下就能得到生成数字MS。</p><p>PRF (Pseudo-Random Function，伪随机函数) 在 TLS1.2 和 SSL 里用的是 MAC 和 HMAC，在 TLS1.3 里用的是安全优化后的 HKDF（HMAC-based Key Derivation Function）。</p><p>以HMAC(Hash-based Message Authentication Code)为例：</p><script type="math/tex; mode=display">\text{HMAC}(Key, Message) = Hash((Key \oplus \text{opad}) \parallel Hash((Key \oplus \text{ipad}) \parallel Message))</script><ul><li><strong>$ \oplus $</strong> ：按位异或 (XOR) 运算  </li><li><strong>$ \parallel $</strong> ：表示字符串连接 (Concatenation)  </li><li><strong><code>pad</code></strong> ：填充常量（值为 0x…，与哈希块大小相同）  </li></ul><p>PRF(Secret, Label, Seed)=P_hash(Secret, Label + Seed)</p><ul><li>Secret：Pre-Master Secret 或 Master Secret</li><li>Label：一个ASCII字符串标签，表示当前正在生成的密钥类型（如 “master secret”、”key expansion” 等）</li><li>Seed：由客户端和服务器在 TLS 握手时交换的两个随机数 (ClientHello.random + ServerHello.random)</li><li>P_hash()：基于 HMAC 的递归扩展函数</li></ul><p>总之就是按某种神秘复杂固定算法，能得到一个一一对应的map，即（Message，Key）对应哈希得到的唯一值。关于哈希算法之前一直没有细想过，为了不影响整体观感我在<a href="#Hash">下文整理一些Hash算法</a>。</p><h3 id="PRF生成Session-Key"><a href="#PRF生成Session-Key" class="headerlink" title="PRF生成Session Key"></a><strong>PRF生成Session Key</strong></h3><p>用上述算法生成连接过程中SSL/TLS所需其他加密密钥。</p><h2 id="5-握手结束"><a href="#5-握手结束" class="headerlink" title="5. 握手结束"></a><strong>5. 握手结束</strong></h2><p>双方使用会话密钥对“握手完成（Handshake Finished）”消息加密并发送，以确认握手成功。<br>根据<a href="https://www.ietf.org/rfc/rfc2246.txt">RFC2246</a>：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">finished_label</span><br><span class="line">           For Finished messages sent by the client, the string &quot;client</span><br><span class="line">           finished&quot;. For Finished messages sent by the server, the</span><br><span class="line">           string &quot;server finished&quot;.</span><br></pre></td></tr></table></figure><p>之后与握手结束过程相同继续进行对称加密的数据交换。</p><h1 id="加密算法"><a href="#加密算法" class="headerlink" title="加密算法"></a><strong>加密算法</strong></h1><h2 id="本质"><a href="#本质" class="headerlink" title="本质"></a><strong>本质</strong></h2><ol><li>将转为数字（比如直接转成定长的ASCII码拼起来）的信息，结合另一段数字（密钥），用具备某种数学性质的算法转换成一段乱码。</li><li>对于对称加密（Symmetric Encryption）来说相对简单，只需要一个互逆的数学变换$f$，满足：<script type="math/tex">C=f(K,P), P=f^{-1}(K,C)</script><ul><li>其中K是密钥key，P是明文plaintext，C是密文ciphertext。比如最简单的例子，加减（$f=+, f^{-1}=-$）乘除（$f=\times , f^{-1}=\div $），或者异或XOR（$f=f^{-1}=\oplus $，因为$A\oplus A=0, A\oplus 0=A$）。</li></ul></li><li>非对称加密（Asymmetric Encryption）则需要一个“单向陷门函数（One-way Trapdoor Function）”，说人话就是正向好计算，逆向如果没有密码就非常难算：<script type="math/tex">C=f(K_{public},P), P=g(K_{private},C)</script><ul><li>单向函数是已知x和f很容易计算f(x)，但已知f和f(x)很难反推出x。</li><li>陷门函数是用一个数学后门Key，能很容易用f和f(x, key)反推出x。</li><li>f 就是易计算但难逆的数学函数（如大整数分解、椭圆曲线问题）。</li></ul></li></ol><p>比如在RSA中的单向陷门函数是$f(x) = x^e \mod n$，陷门trapdoor就是用通过欧拉函数得到的解密指数（私钥指数）d 求解$g(x)=y^d \mod n$。注意，对称算法是可逆函数$f, f^{-1}$，非对称算法不是f的可逆函数而是另一个相关函数g。</p><h2 id="非对称"><a href="#非对称" class="headerlink" title="非对称"></a><strong>非对称</strong></h2><p>以SSL/TLS为例（服务端存私钥）的应用：</p><ol><li>加密数据，client用server的公钥a加密b得到c并传输，server用私钥d解密c得到信息b；<ul><li>加密任何单向数据，SSL/TLS里client加密发送Pre Master Secret用于生成后续密钥。</li></ul></li><li>数字签名，server用私钥d加密b得到c并传输，client用server的公钥a解密c得到信息b，一方面能验证身份真实性（因为公钥a能解开就证明是公钥a对应server发送的信息），另一方面数字签名都会附加用SHA-256等哈希函数把信息b映射成e，用于客户端用同样的SHA-256映射信息b为f，通过e==f验证数据完整性。<ul><li>SSL/TLS里“服务器Hello”内包含的证书就需要数字签名，一开始的server Hello是没加密的，通过验证身份能避免伪造server。</li><li>邮件需要数字签名验证发件人身份和邮件内容完整，但是邮件是隐私，需要额外加密邮件内容。</li><li>软件需要数字签名验证制作者身份和软件内容完整，软件是公开内容一般不需要加密安装包等。</li></ul></li></ol><p>RSA的可逆性让它可被用做上述<strong>两种用途</strong>，client用server的公钥加密传输预主密钥，server用私钥数字签名证书传给client，只用一套公钥私钥，即key1加密key2可以解密，key2加密key1也可以解密。</p><p>那么一定存在其他加密算法只能key1加密key2解密，反之不行。</p><p>比如<strong>ElGamal</strong>分别有加密和数字签名两种用法，一样是在SSL/TLS中server存私钥这个例子，但一套密钥只能用于加密传输或数字签名<strong>一种用途</strong>，若用于加密传输就是client用server的公钥加密server收到后解密，若用于数字签名就是server用私钥加密client收到证书后解密验证。公钥若用来加密就不能反过来用同对私钥加密。</p><p>因为算法不可逆，反过来用密钥key2加密会导致key1无法解密（得用个key3解密）。</p><h3 id="Rivest–Shamir–Adleman"><a href="#Rivest–Shamir–Adleman" class="headerlink" title="Rivest–Shamir–Adleman"></a><strong>Rivest–Shamir–Adleman</strong></h3><p>这里R、S、A分别代表创造算法的三个人。</p><p>数学原理：</p><ol><li>选择两个大素数 $p$ 和 $q$，计算它们的乘积：<script type="math/tex">n = p \times q</script>；</li><li>计算 $n$ 的欧拉函数（在数论中，对正整数n，欧拉函数φ(n)是小于或等于n的正整数中与n互质的数的数目。例如φ(8) = 4，因为1,3,5,7均和8互质。）：<script type="math/tex">\varphi(n) = (p - 1) \times (q - 1)</script>；</li><li>选择一个公钥指数 $e$，通常选择一个小的整数，使得 $e$ 与 $\varphi(n)$ 互质：<script type="math/tex">\gcd(e, \varphi(n)) = 1</script>；</li><li>计算私钥指数 $d$，满足：<script type="math/tex">e \times d \equiv 1 \mod \varphi(n)</script>，即求解：<script type="math/tex">d≡e^{−1} mod φ(N)</script>；</li><li>这样就得到公钥：$(n, e)$ 和私钥：$(n, d)$。</li></ol><p>加密过程：$encrypted = message^e mod n$；<br>解密过程：$message = encrypted^d mod n$。</p><h2 id="对称"><a href="#对称" class="headerlink" title="对称"></a><strong>对称</strong></h2><h3 id="AES"><a href="#AES" class="headerlink" title="AES"></a><strong>AES</strong></h3><p>Advanced Encryption Standard，高级加密标准是一种分组加密算法，工作方式如下：</p><p>输入：固定长度的明文<strong>分组</strong>（128 位，即 16 字节）和密钥（128、192 或 256 位）。<br>加密过程：<br>经过多轮<strong>字节替换（SubBytes）、行移位（ShiftRows）、列混合（MixColumns）和轮密钥加（AddRoundKey）</strong>操作。<br>轮数取决于密钥长度：<br>AES-128：10 轮<br>AES-192：12 轮<br>AES-256：14 轮<br>其中，MixColumns 在最后一轮中被省略。<br>输出：加密后的密文（同样是 128 位）。</p><p>justmysocks-ss的gcm（Galois/Counter Mode）只是AES增加功能的特殊模式。</p><h3 id="ChaCha20"><a href="#ChaCha20" class="headerlink" title="ChaCha20"></a><strong>ChaCha20</strong></h3><p>ChaCha20 是一种流加密算法，基于Hash函数（MD5，SHA）和异或操作，不同于 AES 的分组加密。</p><p>输入：<br>256 位密钥（32 字节）<br>96 位随机数（Nonce）（12 字节）<br>计数器（32 位，避免重复）<br>加密过程：<br>以 512 位（64 字节） 为单位生成伪随机密钥流。<br>明文和密钥流按位异或（XOR）。<br>通过 20 轮<strong>四分之一轮函数（Quarter Round）</strong>变换密钥流，确保扩散性。<br>输出：和明文长度相同的密文。</p><h1 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a><strong>Hash</strong></h1><p>Hash 之所以叫 Hash 就是打碎、混合原本信息，转为定长值，用于超快的 O(1) 数组索引读操作，或达到保证数据完整安全的效果。</p><p>SHA-1/SHA-256, MD5 都是注重安全性的哈希算法，不只要求Key和Value一一对应，而且要求算法不可逆，即已知算法本身和Value，非常难计算回Key。使用时比如将密码转为 MD5，数据库里存的只有 MD5 所以不怕泄漏，密码明文传到服务器计算 MD5 进而跟数据库匹配的过程由 HTTPS 保证安全。</p><p>而平常我用的Hash Table，如C++的STL，map是红黑树，unordered_map是FNV-1a/MurmurHash等低安全性可逆的高效算法，python dict早期也用FNV-1a，现用SipHash（未进行fact check）。</p><h2 id="MD5"><a href="#MD5" class="headerlink" title="MD5"></a><strong>MD5</strong></h2><p>Message Digest Algorithm 5，字面意思是消息摘要，可以把任何信息转换成128-bit（16 字节）。<br>哈希算法的4个步骤：</p><ol><li>用填充（Padding）等方式，将数据的二进制形式长度补齐到 ≡ 0 mod 512 整除，然后把数据拆分成一堆 512 bits 的 blocks。</li><li>初始化 MD5 缓冲区（都是从右边低字节开始存的，即<strong>小端序 Little Endian</strong>）：<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">A &#x3D; 0x67452301，小端序01234567</span><br><span class="line">B &#x3D; 0xefcdab89，跟A连着的89abcdef，设置成这个随机初始值纯粹是看起来舒服</span><br><span class="line">C &#x3D; 0x98badcfe，是B的倒序fedcba98</span><br><span class="line">D &#x3D; 0x10325476，是A的倒序76543210</span><br></pre></td></tr></table></figure></li><li><p><strong>每个 512-bit block 计算时都被分成 16 份 32-bit 子块</strong>：</p><ul><li>对 16 个子块共做 4 轮循环，64 次运算；</li><li>每次运算的公式为 <code>A = B + ((A + F(B,C,D) + M[i] + T[i]) &lt;&lt;&lt; s)</code>，意思是运算时只有寄存器 A 被写了，运算完后（A，B，C，D）寄存器值会右移为（D，Anew，B，C）；</li><li>T 是 64 个 32-bit 常数，M 是 16 份子块，但 4 轮中每轮的索引顺序 i 会变，比如第一轮是<code>0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15</code>，第二轮变成<code>1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12</code>，s 是每次左移的位数，一切只是为了增加计算复杂度；</li><li>每轮的操作以下面 4 次运算为例（即整个运算的 1 / 16 部分）：<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">A &#x3D; B + ((A + F(B,C,D) + M[0] + T[0]) &lt;&lt;&lt; s)</span><br><span class="line">D &#x3D; A + ((D + F(A,B,C) + M[1] + T[1]) &lt;&lt;&lt; s)</span><br><span class="line">C &#x3D; D + ((C + F(D,A,B) + M[2] + T[2]) &lt;&lt;&lt; s)</span><br><span class="line">B &#x3D; C + ((B + F(C,D,A) + M[3] + T[3]) &lt;&lt;&lt; s)</span><br></pre></td></tr></table></figure></li><li>公式中的 F 在四轮中分别是 F、G、H、I 四个非线性函数，分别是：</li></ul><p>| 轮数      | 函数 | 计算公式                                      |<br>|—————|———|—————————————————————|<br>| 0 - 15   | <strong>F</strong> | $ F(B, C, D) = (B \land C) \lor (\neg B \land D) $ |<br>| 16 - 31  | <strong>G</strong> | $ G(B, C, D) = (B \land D) \lor (C \land \neg D) $ |<br>| 32 - 47  | <strong>H</strong> | $ H(B, C, D) = B \oplus C \oplus D $ |<br>| 48 - 63  | <strong>I</strong> | $ I(B, C, D) = C \oplus (B \lor \neg D) $ |</p><ul><li>这样经过 4 轮 64 次基于原本信息 M 的运算，得到的（A，B，C，D）四个 32-bit 量再累加到初始值上：<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">A &#x3D; A + 初始 A</span><br><span class="line">B &#x3D; B + 初始 B</span><br><span class="line">C &#x3D; C + 初始 C</span><br><span class="line">D &#x3D; D + 初始 D</span><br></pre></td></tr></table></figure></li><li>每一个 512-bit block 都以上一个 block 的（A，B，C，D）为初始值计算，最终得到的（A，B，C，D）4 个 32-bit 变量加起来就是 128-bit MD5。</li></ul></li></ol><p><img src="/pics/md5-no-2fix-02-scaled.jpg" alt="图中的K就是公式中的T"></p><h2 id="FNV-1"><a href="#FNV-1" class="headerlink" title="FNV-1"></a><strong>FNV-1</strong></h2><p>Fowler–Noll–Vo 分别代表创造算法的三个人。</p><p><a href="http://www.isthe.com/chongo/tech/comp/fnv/">http://www.isthe.com/chongo/tech/comp/fnv/</a></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">hash = offset_basis</span><br><span class="line"><span class="keyword">for</span> each octet_of_data to be hashed</span><br><span class="line"> hash = hash * FNV_prime</span><br><span class="line"> hash = hash xor octet_of_data</span><br><span class="line"><span class="keyword">return</span> hash</span><br></pre></td></tr></table></figure><p>这里的 octet 就是 byte。<br>发现其实用的哈希算法非常简单，就是用一个值 hash 乘一个 FNV 质数，之后用这个 hash 跟被哈希信息的每一个字节做异或 XOR，因为 octet_of_data只有 8 位，hash 这个值一般大于 8 位，所以 octet_of_data 在高位补 0，异或操作也不会影响 hash 高于 8 位的部分，相当于之后一直是数据在跟自身异或。</p><p>FNV-1a 只是交换了乘 FNV 质数和 XOR 的顺序。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">hash = offset_basis</span><br><span class="line"><span class="keyword">for</span> each octet_of_data to be hashed</span><br><span class="line"> hash = hash xor octet_of_data</span><br><span class="line"> hash = hash * FNV_prime</span><br><span class="line"><span class="keyword">return</span> hash</span><br></pre></td></tr></table></figure><p>用了这么久的 hash table library 们居然只是这么大道至简的算法就能解决存储碰撞，让我有些意外。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Secure Sockets Layer（安全套接字层）技术上已经被弃用，只是习惯上仍如此称呼，现在用的技术是更高效有效的Transport Layer Security（TLS，传输层安全）。本文除了分析协议全过程外还涉及证书申请和证书内容、加密算法和哈希算法。&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="Encryption" scheme="http://blog.v2beach.cn/tags/Encryption/"/>
    
      <category term="Hash" scheme="http://blog.v2beach.cn/tags/Hash/"/>
    
      <category term="Certificate" scheme="http://blog.v2beach.cn/tags/Certificate/"/>
    
  </entry>
  
  <entry>
    <title>水电（主要是电⚡️）</title>
    <link href="http://blog.v2beach.cn/2025/02/01/plumbing-electrical/"/>
    <id>http://blog.v2beach.cn/2025/02/01/plumbing-electrical/</id>
    <published>2025-02-01T12:27:10.000Z</published>
    <updated>2025-03-18T08:25:25.780Z</updated>
    
    <content type="html"><![CDATA[<p>这篇文章是过年给家里修东西感到知识匮乏的大脑复健，一些基本知识在摆脱噩梦般的高中后已经忘记，一些必要的生存知识在成人后又没能及时学习。以后有新东西再回来补充。</p><a id="more"></a><h1 id="中学电路知识"><a href="#中学电路知识" class="headerlink" title="中学电路知识"></a><strong>中学电路知识</strong></h1><p>P=UI, U=W/q, U=IR, P=I²R. (按中学习惯电压还是用U而不是V)</p><h2 id="电流Electric-Current"><a href="#电流Electric-Current" class="headerlink" title="电流Electric Current"></a><strong>电流Electric Current</strong></h2><p>电流是单位时间横截面通过的电子数量。</p><h2 id="电压Voltage"><a href="#电压Voltage" class="headerlink" title="电压Voltage"></a><strong>电压Voltage</strong></h2><p>电压是电场对单个电子平均做的功，或者制造的电势能。</p><h2 id="功Work"><a href="#功Work" class="headerlink" title="功Work"></a><strong>功Work</strong></h2><p>功work是能量energy的转移或转化，有回路的时候就是做功，公式U=W/q适用，没回路的时候W=0不适用公式，仍然存在电压是因为电势能差的存在。</p><h2 id="势能Potential-Energy"><a href="#势能Potential-Energy" class="headerlink" title="势能Potential Energy"></a><strong>势能Potential Energy</strong></h2><p>港台叫位能，物体在一个力场的位置上固然存在的能量，比如宏观的重力势能$U=mgh$，弹性势能胡克定律$U=\frac{1}{2}kx^2$，微观的库伦力电势能$U=\frac{kq_1q_2}{r^2}$。</p><h2 id="功率Power"><a href="#功率Power" class="headerlink" title="功率Power"></a><strong>功率Power</strong></h2><p>微观理解电压U=W/q乘电流I=单位时间横截面通过的电子数量，乘起来就是单位时间内通过横截面的总的能量，总的功，即功率。表示每个电子在单位时间内获得并传递的能量work/energy总和。</p><h2 id="电对人的伤害"><a href="#电对人的伤害" class="headerlink" title="电对人的伤害"></a><strong>电对人的伤害</strong></h2><ol><li>神经信号是电信号，外部施加强电流会导致神经、心脏、肌肉功能失效，比如肌肉痉挛（即强烈收缩contraction），比如心跳失常，比如脑功能失常；</li><li>导致烧伤，焦耳热 P=I²R；</li><li><p>体内物质因为强电发生电解。</p></li><li><p>如果I大U小（current大voltage小）：比如车载电池是 500A 电流 12V 电压，但是对人体危害不大，因为 500A 存在于汽车电路，人触碰到相当于是增加了并联电路，汽车电路都是导线自由电子多，人高电阻实际电流会很微弱，按人体最低的估计电阻1000Ω算，车载电池只够在人体产生12mA电流，可能刚够造成肌肉痉挛（类比小腿抽筋）。只有高电压（构造高电势差）才能制造高电流，遵循欧姆定律 U=IR，而不是P=UI。</p></li><li>如果U大I小（voltage大current小）：即我触电的电源到我的身体和地面之间有很大电势差，电流小是因为回路内自由电子少，人体电阻（&gt;1000Ω）如皮肤被击穿电流增大，电阻会随之下降。</li></ol><p>对人体来说不可能出现人体内通过 500A 电流 12V 电压这种情况，因为人体的电阻远远远大于 12/500，电压足够大才一定会导致大电流。</p><h2 id="交流电"><a href="#交流电" class="headerlink" title="交流电"></a><strong>交流电</strong></h2><p>传输电力实际上不是“运输电子”，而是通过各种途径传输能量，包括电子间相互作用力导致的动能传输，更主要的是依赖于量子力学效应的能级跃迁、电子-声子相互作用等。传输电力可以简单理解成电源通过推动电子把能量传到另一边，交流电是通过电子振荡传输能量，就可以理解成是电源来回推拉电子的过程。</p><p>家用交流电是-311V～311V的三角函数，50Hz即1秒钟内按这样的周期变化 50 轮，三角函数的有效值是311V/根2=220，即传输的功率等价于220V直流电。</p><h2 id="发电和用电"><a href="#发电和用电" class="headerlink" title="发电和用电"></a><strong>发电和用电</strong></h2><p>发电是动能（火力发电也是通过热能用水蒸汽推动蒸汽轮机）<strong>切割磁场线（导体穿过磁场发生磁通量变化制造感应电动势）</strong>转化成电能的过程，用电则是再转化为光能或动能、热能等。</p><p>比如马达，电机是靠电周围产生的磁场带动磁铁运动；<br>电能转化成光能有很多种情况：</p><ol><li><strong>早期阴极射线管（CRT，Cathode Ray Tube）</strong>是通过电子流让荧光材料发光；</li><li><strong>液晶显示（LCD，Liquid Crystal Display）</strong>是这五个中的特殊情况，电压改变液晶排列只控制光的透过性，<strong>真正发光的是背光源（Backlight）</strong> LED，早期的背光源也是荧光粉（汞蒸汽被通电后释放紫外线（UV），紫外线激发不同的荧光粉发出不同颜色的光；（电脑屏幕，我的老NS屏幕用的TN和IPS都是一种LCD，我手里可能只有mbp m1 2020和iPhone 12是LED屏）</li><li><strong>发光二极管（LED，Light-Emitting Diode）</strong>是半导体发光器件，其发光原理是电致发光（Electroluminescence），施加正向电压后PN结（PN中间的过渡层）中的电子跟空穴结合，电子从高能态跃迁到低能态，释放出光子（光），不同制造二极管的材料会发出不同的光，GaN（氮化镓）发出蓝光、GaAs（砷化镓）发出红光、InGaN（铟镓氮）+荧光粉产生白光（LCD 背光常用）；</li><li><strong>有机发光二极管（OLED，Organic LED）</strong>即PN结换成有机材料。</li><li><strong>等离子显示（Plasma Display Panel, PDP）</strong>跟LCD中早期背光源相似，不过不是汞蒸汽，是氖气和氙气通电后形成等离子体发出紫外线（UV），紫外线激发荧光粉发光。</li></ol><h3 id="荧光效应"><a href="#荧光效应" class="headerlink" title="荧光效应"></a><strong>荧光效应</strong></h3><p>荧光材料里含有稀土离子（如Eu²⁺、Eu³⁺、Tb³⁺），这些金属离子在吸收特定波长的光（通常是紫外光或短波长可见光）后电子会吸收能量，从基态跃迁到激发态。<br>电子在激发态不稳定，会通过无辐射跃迁（能量以热的形式释放）回到较低的激发态，释放可见光。</p><p>荧光效应简单来说就是一些<strong>稀土离子</strong>（rare-earth element，REE，是元素周期表中第3族钪、钇和镧系元素共17种金属化学元素的合称，电子特性和磁性质在现代工业应用广泛无法被取代）<strong>吸收高能光变为不稳定态又释放出可见光</strong>的过程。</p><h3 id="法拉第电磁感应"><a href="#法拉第电磁感应" class="headerlink" title="法拉第电磁感应"></a><strong>法拉第电磁感应</strong></h3><p>磁生电感生电动势公式：</p><script type="math/tex; mode=display">\mathcal{E} = -\frac{d\Phi}{dt}</script><p>其中：</p><ul><li><script type="math/tex">\mathcal{E}</script>：感应电动势（单位：伏特 V）</li><li><script type="math/tex">\Phi</script>：磁通量（单位：韦伯 Wb）</li><li><script type="math/tex">t</script>：时间（单位：秒 s）</li></ul><script type="math/tex; mode=display">\Phi = \int_S \mathbf{B} \cdot d\mathbf{A}</script><p>其中：</p><ul><li><script type="math/tex">\mathbf{B}</script>：磁感应强度（单位：特斯拉 T）</li><li><script type="math/tex">d\mathbf{A}</script>：面积微元（单位：平方米 <script type="math/tex">m^2</script>）</li></ul><p>闭合回路中的感应电动势等于穿过回路的磁通量随时间的变化率，并取负号（楞次定律），即感应电流的方向总是反抗磁通量的变化。</p><h3 id="安培定理"><a href="#安培定理" class="headerlink" title="安培定理"></a><strong>安培定理</strong></h3><p><img src="/pics/1382d8ba00f9eecbe00563a76ac1eef8.jpg" alt=""></p><h3 id="变压器微观解释"><a href="#变压器微观解释" class="headerlink" title="变压器微观解释"></a><strong>变压器微观解释</strong></h3><p>升压时：更多电子的“动能”集中在更少的电子上（电压升高，电流减少）。通过增多线圈匝数，每个电子获得的能量（电压）更高；但导体内的电子总数（电流）减少。<br>降压时：电子的“动能”分散到更多的电子上（电压降低，电流增加）。</p><p>调整电压的公式是<script type="math/tex">V_2 = V_1 \times \frac{N_2}{N_1}</script>，V1是输入电压，N1是初级线圈匝数，V2是输出电压，N2是次级线圈匝数。<br>如果输入电压未知大小，可能是220V，可能是100V，假如输出都需要10V，解决方案：</p><ol><li>首先假如N1是2200匝，按公式计算N2的10V端就需要100匝。如果输入电源只有两种可能，可以加一个抽头（Tap）在N1的1000匝处接出来一个点，如果输入是220V就用完整2200匝，如果输入是100V就接到抽头上用其中1000匝；</li><li>上面方法很老了，现在较大功率的电器用高频变压器/开关模式电源 (Switch Mode Power Supply)，手柄里则用稳压器，比如线性稳压器 (Linear Regulator) 是用一个可调节电阻消耗多余电压，或者用开关稳压器 (Switching Regulator) 用MOSFET、控制芯片和电容电感等控制输出电压。</li></ol><h2 id="电池"><a href="#电池" class="headerlink" title="电池"></a><strong>电池</strong></h2><p>顺带着分析下新的xbox series手柄电池充放电原理，虽然已经把我7年前的xbox 360修好了。</p><h3 id="碱性电池"><a href="#碱性电池" class="headerlink" title="碱性电池"></a><strong>碱性电池</strong></h3><p>刚发现新手柄里的5号电池是不可充电的（not rechargeable），给的type-c口是直接从电源连到电路上的（通过<a href="#变压器微观解释">变压器</a>调整电压和整流电路把交流变直流）。</p><p>碱性电池的化学体系基于<strong>锌-二氧化锰（Zn-MnO₂）</strong>，电解液通常是氢氧化钾（KOH）。反应<strong>不可逆</strong>，所以不能充电。  </p><p>放电时：</p><p>负极（阳极，氧化反应）：</p><script type="math/tex; mode=display">\text{Zn} + 2\text{OH}^- \rightarrow \text{ZnO} + H_2O + 2e^-</script><p>正极（阴极，还原反应）：</p><script type="math/tex; mode=display">2\text{MnO}_2 + 2H_2O + 2e^- \rightarrow 2\text{MnOOH} + 2\text{OH}^-</script><p>总反应：</p><script type="math/tex; mode=display">\text{Zn} + 2\text{MnO}_2 + H_2O \rightarrow \text{ZnO} + 2\text{MnOOH}</script><h3 id="铅酸电池"><a href="#铅酸电池" class="headerlink" title="铅酸电池"></a><strong>铅酸电池</strong></h3><p>汽车电瓶用这种。</p><p>铅酸电池使用<strong>Pb（铅）</strong>作为负极，<strong>PbO₂（氧化铅）</strong>作为正极，电解液是<strong>硫酸（H₂SO₄）</strong>。其充放电过程<strong>可逆</strong>。  </p><p>放电时（供应电能）：</p><p>负极（阳极，氧化反应）：</p><script type="math/tex; mode=display">\text{Pb} + \text{SO}_4^{2-} \rightarrow \text{PbSO}_4 + 2e^-</script><p>正极（阴极，还原反应）：</p><script type="math/tex; mode=display">\text{PbO}_2 + 4H^+ + \text{SO}_4^{2-} + 2e^- \rightarrow \text{PbSO}_4 + 2H_2O</script><p>总反应：</p><script type="math/tex; mode=display">\text{Pb} + \text{PbO}_2 + 2H_2SO_4 \rightarrow 2\text{PbSO}_4 + 2H_2O</script><p>充电时（吸收电能）：</p><script type="math/tex; mode=display">2\text{PbSO}_4 + 2H_2O \rightarrow \text{Pb} + \text{PbO}_2 + 2H_2SO_4</script><h3 id="锂离子电池"><a href="#锂离子电池" class="headerlink" title="锂离子电池"></a><strong>锂离子电池</strong></h3><p>锂电池的正极材料可以是<strong>LiCoO₂（钴酸锂）、LiFePO₄（磷酸铁锂）等</strong>，负极通常是<strong>石墨（C）</strong>。其反应也<strong>可逆</strong>，充放电时锂离子在正负极之间来回移动。  </p><p>放电时（供应电能）：</p><p>负极（阳极，氧化反应）：</p><script type="math/tex; mode=display">\text{LiC}_6 \rightarrow \text{C}_6 + \text{Li}^+ + e^-</script><p>正极（阴极，还原反应）：</p><script type="math/tex; mode=display">\text{Li}_{1-x}\text{CoO}_2 + \text{Li}^+ + e^- \rightarrow \text{LiCoO}_2</script><p>总反应：</p><script type="math/tex; mode=display">\text{LiC}_6 + \text{Li}_{1-x}\text{CoO}_2 \rightarrow \text{C}_6 + \text{LiCoO}_2</script><p>充电时（吸收电能）：</p><script type="math/tex; mode=display">\text{C}_6 + \text{LiCoO}_2 \rightarrow \text{LiC}_6 + \text{Li}_{1-x}\text{CoO}_2</script><p>接下来的内容就是房屋水电，<a href="https://www.bilibili.com/video/BV1XT421k7M2">b站有个自装修up有水电合集</a>，<a href="https://www.youtube.com/results?search_query=electric+and+plumbing+guide">youtube也有很多教程</a>。</p><h1 id="电灯走线，开关结构"><a href="#电灯走线，开关结构" class="headerlink" title="电灯走线，开关结构"></a><strong>电灯走线，开关结构</strong></h1><p><img src="/pics/一开双控开关and二开双控开关（单双控接线）.jpg" alt="开关是接在火线的断点上，电器上自己接零线构成回路"></p><p><img src="/pics/How-Electricity-Reaches-Your-Home-1.jpg" alt=""></p><p><img src="/pics/Home-Electrical-Wiring-Layout.jpg" alt=""></p><p>amp是Ampere安培，以电路5的16A（最大安全工作电流，额定电流）为例，额定功率是 P=220V×16A=3520W。</p><h1 id="PVC-PPR"><a href="#PVC-PPR" class="headerlink" title="PVC/PPR"></a><strong>PVC/PPR</strong></h1><p>PVC（Polyvinyl Chloride，聚氯乙烯），硬度较高，但韧性较差。含有氯元素，燃烧时可能释放氯化氢气体，有毒，寿命短，不耐高温。胶水连接。</p><p>PPR（Polypropylene Random Copolymer，无规共聚聚丙烯），聚丙烯共聚而成，耐高温、耐腐蚀，无毒无味。热熔连接。</p><p>谈起这个区别是因为一开始误会奶奶家阳台的给水管是 PVC，实际都是PPR，但是是外径 20 mm，壁厚很薄（～3mm）的内壁铝塑管，十几二十年过去风化严重，现已淘汰，给奶奶用热熔枪换上的是更厚的 PPR。</p><h1 id="牢骚"><a href="#牢骚" class="headerlink" title="牢骚"></a><strong>牢骚</strong></h1><p>年初五，过年七天累，年在走完倪家庵这四世亲戚后算过完了。<br>难以置信，距离2025-2-2动笔这篇文章，已经过去1313天了。<br>一回家就会被无尽的回忆吞噬，清冷早晨从南窗森林传进屋里的四声杜鹃，北窗外没改西关分校和高楼之前，平房远处是铁路公路的景象，一瞬间就能把我拉回过去，老家属院被废物政府拆了，这栋06年春节搬进的房子就承载了我对过去的所有记忆。<br>如果一切一如我前二十二年人生那样顺利，这些记忆只是跟家人茶余饭后的笑谈，现在却是历经三年依然能不断淹没我的巨大痛苦。<br>是的，沉浸在过去是毫无价值的，我的痛苦在这充满灾难的世界不值一提。<br>但我还是多么想多么想付出生命付出一切，只要能永远留在那个梦里，回到那些听着四声杜鹃、看着老家属院坡上那轮巨大朝阳的早晨，冲到家人身边，拥抱爸和年轻的奶奶，告诉他们那是我一生中最好的时光。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;这篇文章是过年给家里修东西感到知识匮乏的大脑复健，一些基本知识在摆脱噩梦般的高中后已经忘记，一些必要的生存知识在成人后又没能及时学习。以后有新东西再回来补充。&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="electricity" scheme="http://blog.v2beach.cn/tags/electricity/"/>
    
      <category term="plumbing" scheme="http://blog.v2beach.cn/tags/plumbing/"/>
    
      <category term="battery" scheme="http://blog.v2beach.cn/tags/battery/"/>
    
  </entry>
  
  <entry>
    <title>torch.nn.parallel</title>
    <link href="http://blog.v2beach.cn/2025/01/17/torch-nn-parallel/"/>
    <id>http://blog.v2beach.cn/2025/01/17/torch-nn-parallel/</id>
    <published>2025-01-16T16:17:43.000Z</published>
    <updated>2025-05-15T06:06:20.059Z</updated>
    
    <content type="html"><![CDATA[<p>Data parallelism is a way to process multiple data batches across multiple devices simultaneously to achieve better performance. In PyTorch, the <a href="https://pytorch.org/docs/stable/data.html#torch.utils.data.distributed.DistributedSampler">DistributedSampler</a> ensures each device gets a non-overlapping input batch. The model is replicated on all the devices; each replica calculates gradients and simultaneously synchronizes with the others using the <a href="https://tech.preferred.jp/en/blog/technologies-behind-distributed-deep-learning-allreduce/">ring all-reduce algorithm</a>(梯度在設備間環狀傳遞、求和、更新).</p><a id="more"></a><h1 id="DataParallel-DP-vs-Distributed-Data-Parallel-DDP"><a href="#DataParallel-DP-vs-Distributed-Data-Parallel-DDP" class="headerlink" title="DataParallel(DP) vs. Distributed Data Parallel(DDP)"></a><strong>DataParallel(DP) vs. Distributed Data Parallel(DDP)</strong></h1><div class="table-container"><table><thead><tr><th>DataParallel</th><th>DistributedDataParallel</th></tr></thead><tbody><tr><td>More overhead; model is replicated and destroyed at each forward pass</td><td>Model is replicated only once</td></tr><tr><td>Only supports single-node parallelism</td><td>Supports scaling to multiple machines</td></tr><tr><td>Slower; uses multithreading on a single process and runs into Global Interpreter Lock (GIL) contention</td><td>Faster (no GIL contention) because it uses multiprocessing</td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>特性</th><th>Multithreading (多线程)</th><th>Multiprocessing (多进程)</th></tr></thead><tbody><tr><td><strong>适用场景</strong></td><td>I/O密集型任务（如文件操作、网络请求等）</td><td>CPU密集型任务（如科学计算、数据处理等）</td></tr><tr><td><strong>执行方式</strong></td><td>线程共享同一内存空间，多线程在同一进程内执行</td><td>每个进程拥有独立内存空间，多个进程并行执行</td></tr><tr><td><strong>性能</strong></td><td>由于 GIL，不能真正并行执行 CPU 密集型任务</td><td>能充分利用多核 CPU，适合 CPU 密集型任务</td></tr><tr><td><strong>内存开销</strong></td><td>内存共享，开销较小</td><td>每个进程拥有独立内存，内存消耗较大</td></tr><tr><td><strong>创建和销毁开销</strong></td><td>线程较轻量，创建和销毁开销较小</td><td>进程较重，创建和销毁的开销较大</td></tr><tr><td><strong>进程/线程间通信</strong></td><td>线程间共享内存，通信高效但可能存在线程安全问题</td><td>进程间需要使用 IPC（如队列、管道），通信较复杂</td></tr><tr><td><strong>调试难度</strong></td><td>难度较大，尤其是在多线程竞争和死锁问题上</td><td>相对较简单，进程独立，错误隔离更好</td></tr></tbody></table></div><p><img src="/pics/v2-674f0d37fca4fac1bd2df28a2b78e633_720w.webp" alt="並發vs.並行"></p><h1 id="Ring-Allreduce"><a href="#Ring-Allreduce" class="headerlink" title="Ring-Allreduce"></a><strong>Ring-Allreduce</strong></h1><p><strong>每个设备上都会存完整的model用于前向计算，但是会把batch拆成batch_size除以设备数的batches，然后反向传播得到的梯度dW靠All Reduce传递计算dW的平均值来更新model weights。</strong></p><p><a href="https://andrew.gibiansky.com/blog/machine-learning/baidu-allreduce/">https://andrew.gibiansky.com/blog/machine-learning/baidu-allreduce/</a><br>上面這邊博客比<a href="https://pytorch.org/tutorials/beginner/ddp_series_multigpu.html">PyTorch DDP官方教程</a>裡提供的<a href="https://tech.preferred.jp/en/blog/technologies-behind-distributed-deep-learning-allreduce/">這篇</a>講得清楚很多。</p><h2 id="算法名"><a href="#算法名" class="headerlink" title="算法名"></a><strong>算法名</strong></h2><p>所謂Ring是指將GPU構成邏輯環，每個GPU只跟兩個GPUs連接，一個left neighbour一個right neighbour，chunk只會從左邊接收發給右邊。</p><p>所謂Allreduce是指相比1個reducer而言，所有的GPUs都作為聚合者reducer。</p><p><img src="/pics/ringallreduce/master-slave-gpus.png" alt="Array求和操作GPU應遠快過CPU，所以如果5個GPUs會這樣分配"></p><p><img src="/pics/ringallreduce/ring-gpus.png" alt="所有GPUs都參與聚合操作"></p><h2 id="算法"><a href="#算法" class="headerlink" title="算法"></a><strong>算法</strong></h2><p>Ring-Allreduce以計算一個求和任務為例（exactly what we need in backprop），有Scatter Reduce-Allgather兩個階段：</p><p><img src="/pics/ringallreduce/array-partition.png" alt="Partitioning of an array into N chunks"></p><ol><li>首先假如有5個GPUs，所有GPU上的array就都被分成5個chunks。</li><li>Scatter Reduce 階段每個第 N 個 GPU 都會發生 N-1 個 iterations，每次迭代的操作是求和：<ol><li>1st iteration 發送第 n 個 chunk，接收第 n-1 個 chunk；</li><li>之後的所有 iterations 裡都是發送剛接收到的 chunk。</li></ol></li><li>Allgather 階段每個第 N 個 GPU 也都會發生 N-1 個 iterations，每次迭代只是用接收的 chunk 覆蓋原本 chunk：<ol><li>1st iteration 發送第 n+1 個 chunk，接收第 n 個 chunk；</li><li>之後的所有 iterations 裡都是發送剛接收到的 chunk。（同上）</li></ol></li></ol><div class="table-container"><table><thead><tr><th>Scatter-reduce data transfers (iteration 1)</th><th>Scatter-reduce data transfers (iteration 2)</th><th>Scatter-reduce data transfers (iteration 3)</th><th>Scatter-reduce data transfers (iteration 4)</th><th>Final state after all scatter-reduce transfers</th></tr></thead><tbody><tr><td><img src="/pics/ringallreduce/scatter-reduce-iteration-1.png" alt=""></td><td><img src="/pics/ringallreduce/scatter-reduce-iteration-2.png" alt=""></td><td><img src="/pics/ringallreduce/scatter-reduce-iteration-3.png" alt=""></td><td><img src="/pics/ringallreduce/scatter-reduce-iteration-4.png" alt=""></td><td><img src="/pics/ringallreduce/scatter-reduce-iteration-done.png" alt=""></td></tr></tbody></table></div><p>每個 GPU 上有一個求和結束的 chunk，每個 GPU 都作為 reducer 完成了工作。</p><div class="table-container"><table><thead><tr><th>Allgather data transfers (iteration 1)</th><th>Allgather data transfers (iteration 2)</th><th>Allgather data transfers (iteration 3)</th><th>Allgather data transfers (iteration 4)</th><th>Final state after all allgather transfers</th></tr></thead><tbody><tr><td><img src="/pics/ringallreduce/allgather-iteration-1.png" alt=""></td><td><img src="/pics/ringallreduce/allgather-iteration-2.png" alt=""></td><td><img src="/pics/ringallreduce/allgather-iteration-3.png" alt=""></td><td><img src="/pics/ringallreduce/allgather-iteration-4.png" alt=""></td><td><img src="/pics/ringallreduce/allgather-iteration-done.png" alt=""></td></tr></tbody></table></div><p><strong>Bandwidth vs. Latency：</strong><br>Bandwidth帶寬是每秒通過A點的數據量；<br>Latency延遲是一個數據從A點到B點需要的時間。</p><p>帶寬遠大於延遲時 Ring-Allreduce 這種與 GPU 數量無關的算法會很快，上面第一種方法因為壓力會給到帶寬（reducer要挨個給每個GPU返回幾個G的梯度數據更新參數）所以耗時跟 GPU 數量是線性增長關係。</p><h2 id="MapReduce的reduce"><a href="#MapReduce的reduce" class="headerlink" title="MapReduce的reduce"></a><strong>MapReduce的reduce</strong></h2><p>reduce算法中reduce的意思不是減少，而是“合併”（併是動詞，並是其他副詞連詞詞性）、“聚合”。</p><p>假如用MapReduce算法做統計文本的單詞頻率這個任務，以“hello world hello”為例，有Map-Shuffle-Reduce三個階段：</p><ol><li>Map：文本分割成單詞，構造鍵值對。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">(&quot;hello&quot;, 1)</span><br><span class="line">(&quot;world&quot;, 1)</span><br><span class="line">(&quot;hello&quot;, 1)</span><br></pre></td></tr></table></figure></li><li>Shuffle：Map的輸出會經過Shuffle把Key一樣的值聚集到一起。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">(&quot;hello&quot;, [1, 1])</span><br><span class="line">(&quot;world&quot;, [1])</span><br></pre></td></tr></table></figure></li><li>Reduce：對Shuffle的結果聚合，這個任務是求和，有的任務是統計Key出現的次數，有的是求Value最大最小值，有的是把Value合併成一個數據結構。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">(&quot;hello&quot;, 2)</span><br><span class="line">(&quot;world&quot;, 1)</span><br></pre></td></tr></table></figure></li></ol><h1 id="DDP"><a href="#DDP" class="headerlink" title="DDP"></a><strong>DDP</strong></h1><ol><li>構建 Process Group（進程組，<a href="#dataparalleldp-vs-distributed-data-parallelddp">DDP是multiprocessing，DP是multithreading</a>），其中backend有两种，用于支持多设备之间通信操作比如广播(Broadcast)聚合(Reduce)和<a href="#ring-allreduce">All-Reduce</a>等，NCCL全称是NVIDIA Collective Communications Library针对多GPU场景，Gloo是Facebook AI Research开发的针对CPU和GPU混合场景。<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ddp_setup</span><span class="params">(rank: int, world_size: int)</span>:</span></span><br><span class="line">   <span class="string">"""</span></span><br><span class="line"><span class="string">   Args:</span></span><br><span class="line"><span class="string">       rank: Unique identifier of each process</span></span><br><span class="line"><span class="string">      world_size: Total number of processes</span></span><br><span class="line"><span class="string">   """</span></span><br><span class="line">   os.environ[<span class="string">"MASTER_ADDR"</span>] = <span class="string">"localhost"</span></span><br><span class="line">   os.environ[<span class="string">"MASTER_PORT"</span>] = <span class="string">"12355"</span></span><br><span class="line">   torch.cuda.set_device(rank)  <span class="comment"># sets the default GPU for each process.</span></span><br><span class="line">   <span class="comment"># 分佈式進程組 distributed process group 包含mp.spawn出來的所有能交流和同步的進程</span></span><br><span class="line">   torch.distributed.init_process_group(backend=<span class="string">"nccl"</span>, rank=rank, world_size=world_size)  <span class="comment"># 默認用 TCP 協議初始化 distributed process group，backend 的選擇上，GPU分佈式訓練用NCCL，CPU分佈式訓練用Gloo</span></span><br></pre></td></tr></table></figure></li><li>構建 DDP model。<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">self.model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[gpu_id])</span><br></pre></td></tr></table></figure></li><li>Distributing Input Data（分發輸入數據）。<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">train_data = torch.utils.data.DataLoader(</span><br><span class="line">    dataset=train_dataset,</span><br><span class="line">    batch_size=<span class="number">32</span>,</span><br><span class="line">    shuffle=<span class="literal">False</span>,  <span class="comment"># We don't shuffle</span></span><br><span class="line">    sampler=torch.utils.data.distributed.DistributedSampler(train_dataset), <span class="comment"># 為所有 distributed process 隨機採樣不重複的數據，GPUs/Processe 之間不重複，共有 32 * nprocs 個有效 samples，如果用 torch.distributed，DistributedSampler 會自動獲取 rank 所以不用顯式指定？</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_run_epoch</span><span class="params">(self, epoch)</span>:</span></span><br><span class="line">    b_sz = len(next(iter(self.train_data))[<span class="number">0</span>])</span><br><span class="line">    self.train_data.sampler.set_epoch(epoch)   <span class="comment"># 每個 epoch 重新打亂一次，否則每個 epoch 隨機抽樣順序一樣每個 process 可能拿到同樣的數據</span></span><br><span class="line">    <span class="keyword">for</span> source, targets <span class="keyword">in</span> self.train_data:</span><br><span class="line">      ...</span><br><span class="line">      self._run_batch(source, targets)</span><br></pre></td></tr></table></figure></li><li>保存模型 checkpoints。<figure class="highlight diff"><table><tr><td class="code"><pre><span class="line"><span class="deletion">- ckp = self.model.state_dict()</span></span><br><span class="line"><span class="addition">+ ckp = self.model.module.state_dict()</span></span><br><span class="line">...</span><br><span class="line">...</span><br><span class="line"><span class="deletion">- if epoch % self.save_every == 0:</span></span><br><span class="line"><span class="addition">+ if self.gpu_id == 0 and epoch % self.save_every == 0:  # 只有rank0主進程保存ckp，因為根據上面的ring-allreduce算法每個gpu上的參數相同，集合調用 Collective calls 要在這之前是什麼意思？</span></span><br><span class="line">  self._save_checkpoint(epoch)</span><br></pre></td></tr></table></figure></li><li>運行分佈式訓練（distributed training），<a href="https://pytorch.org/docs/stable/multiprocessing.html#spawning-subprocesses">torch.multiprocessing</a>是對原生multiprocessing封裝的wrapper。<figure class="highlight diff"><table><tr><td class="code"><pre><span class="line"><span class="deletion">- def main(device, total_epochs, save_every):</span></span><br><span class="line"><span class="addition">+ def main(rank, world_size, total_epochs, save_every):</span></span><br><span class="line"><span class="addition">+  ddp_setup(rank, world_size)</span></span><br><span class="line">   dataset, model, optimizer = load_train_objs()</span><br><span class="line">   train_data = prepare_dataloader(dataset, batch_size=32)</span><br><span class="line"><span class="deletion">-  trainer = Trainer(model, train_data, optimizer, device, save_every)</span></span><br><span class="line"><span class="addition">+  trainer = Trainer(model, train_data, optimizer, rank, save_every)  # 原來是 device，現在是 rank</span></span><br><span class="line">   trainer.train(total_epochs)</span><br><span class="line"><span class="addition">+  torch.distributed.destroy_process_group()  # 結束訓練要釋放 process group</span></span><br><span class="line"></span><br><span class="line">if __name__ == "__main__":</span><br><span class="line">   import sys</span><br><span class="line">   total_epochs = int(sys.argv[1])</span><br><span class="line">   save_every = int(sys.argv[2])</span><br><span class="line"><span class="deletion">-  device = 0      # shorthand for cuda:0</span></span><br><span class="line"><span class="deletion">-  main(device, total_epochs, save_every)</span></span><br><span class="line"><span class="addition">+  world_size = torch.cuda.device_count()  # world_size 不一定等於 gpus 數？</span></span><br><span class="line"><span class="addition">+  torch.multiprocessing.spawn(main, args=(world_size, total_epochs, save_every,), nprocs=world_size)  # mp.spawn 調用時會由 DDP 自動分配 rank 給 process</span></span><br></pre></td></tr></table></figure></li></ol><p>计算模型显存占用量？</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Data parallelism is a way to process multiple data batches across multiple devices simultaneously to achieve better performance. In PyTorch, the &lt;a href=&quot;https://pytorch.org/docs/stable/data.html#torch.utils.data.distributed.DistributedSampler&quot;&gt;DistributedSampler&lt;/a&gt; ensures each device gets a non-overlapping input batch. The model is replicated on all the devices; each replica calculates gradients and simultaneously synchronizes with the others using the &lt;a href=&quot;https://tech.preferred.jp/en/blog/technologies-behind-distributed-deep-learning-allreduce/&quot;&gt;ring all-reduce algorithm&lt;/a&gt;(梯度在設備間環狀傳遞、求和、更新).&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="Distributed Data Parallel" scheme="http://blog.v2beach.cn/tags/Distributed-Data-Parallel/"/>
    
  </entry>
  
  <entry>
    <title>conda</title>
    <link href="http://blog.v2beach.cn/2025/01/15/conda/"/>
    <id>http://blog.v2beach.cn/2025/01/15/conda/</id>
    <published>2025-01-15T14:30:32.000Z</published>
    <updated>2025-03-19T12:02:37.926Z</updated>
    
    <content type="html"><![CDATA[<div class="table-container"><table><thead><tr><th><strong>水蚺 (Anaconda)</strong></th><th><strong>蟒蛇 (Python)</strong></th></tr></thead><tbody><tr><td>常待在河流、沼泽等水域中。</td><td>生活在森林、草原或岩石地区。</td></tr><tr><td>更重更粗壮，适应水中的捕猎。</td><td>更长但较轻，灵活性更高。</td></tr><tr><td>卵胎生（卵在母体内孵化后直接产下幼蛇）</td><td>卵生（产卵，并在巢中孵化）</td></tr></tbody></table></div><a id="more"></a><p>界門綱目科屬種：</p><p>動物界 Animalia、脊索動物門 Chordata、爬蟲綱 Reptilia、有鱗目 Squamata、蛇亞目 Serpentes、蚺科 Boidae 和蚺亞科 Boinae、水蚺屬 Eunectes<br>動物界 Animalia、脊索動物門 Chordata、爬蟲綱 Reptilia、有鱗目 Squamata、蛇亞目 Serpentes、蟒蛇科 Pythonidae 和蟒亞科 Pythoninae、蟒屬 Python</p><p>蚺是水裡最大的蛇，蟒是陸地最大的蛇，這讓我突然想到為什麼水生動物比陸地動物更大？最大陸生動物是10噸重7米長的非洲象，最大的水生動物是200噸重33米長的藍鯨。</p><p>也許是因為浮力拖舉著讓他們移動受地心引力限制更小，能長大就往大了長了。</p><p>人為什麼不能是現在體型的兩倍？如果是因為引力，大不了腿骨密度和肌肉（現在70%）相較於上肢佔比更高些？如果是因為能量，那人類群體少一半也是可以的，也許是因為人是用腦子改變自然適應自己的生物，多一個人比大一倍的自己要好一些吧。或許正是差的那些浮力提高了一部分動物的體型上限。</p><p>因為2021年剛買M1時conda還不適配湊合用的是pyenv &amp; virtualenv &amp; miniforge，其難用程度和其他庫的不兼容程度直接導致一直到2021年7月都放棄寫python（記得當時還嘗試過為某些庫編譯apple silicon版本）。後來在vivo和網易都用的high level infra，最後在商湯用的是clusters和每次創建都需要dockerfile新建的機器，或方便或不方便，總之一直沒怎麼用過conda，上次用conda可以追溯到2020年及之前的本科時代。</p><p><img src="/pics/1708649697-how-conda-miniconda-and-anaconda-differ-from-one-another.jpeg.avif" alt=""></p><h1 id="conda-vs-pyenv-amp-virtualenv"><a href="#conda-vs-pyenv-amp-virtualenv" class="headerlink" title="conda vs. pyenv &amp; virtualenv"></a><strong>conda vs. pyenv &amp; virtualenv</strong></h1><p>pyenv是python版本管理，virtualenv是與之配合的環境（主要是庫的版本，次要是python版本）管理，按理說是能做到跟conda一樣隔離python環境的效果。</p><p>pip是package manager，virtualenv是environment manager，conda也是二者之和。</p><p>所以conda=pyenv(python version manager) + pip(package manager) + virtualenv(environment manager)。</p><div class="table-container"><table><thead><tr><th><strong>Task</strong></th><th><strong>Conda</strong></th><th><strong>Pip</strong></th><th><strong>Virtualenv</strong></th></tr></thead><tbody><tr><td><strong>Install a package</strong></td><td><code>conda install $PACKAGE_NAME</code></td><td><code>pip install $PACKAGE_NAME</code></td><td><code>X</code></td></tr><tr><td><strong>Update a package</strong></td><td><code>conda update --name $ENVIRONMENT_NAME $PACKAGE_NAME</code></td><td><code>pip install --upgrade $PACKAGE_NAME</code></td><td><code>X</code></td></tr><tr><td><strong>Update package manager</strong></td><td><code>conda update conda</code></td><td>Linux/macOS: <code>pip install -U pip</code><br>Win: <code>python -m pip install -U pip</code></td><td><code>X</code></td></tr><tr><td><strong>Uninstall a package</strong></td><td><code>conda remove --name $ENVIRONMENT_NAME $PACKAGE_NAME</code></td><td><code>pip uninstall $PACKAGE_NAME</code></td><td><code>X</code></td></tr><tr><td><strong>Create an environment</strong></td><td><code>conda create --name $ENVIRONMENT_NAME python</code></td><td><code>X</code></td><td><code>pyenv virtualenv &lt;python_version&gt; &lt;env_name&gt;</code></td></tr><tr><td><strong>Activate an environment</strong></td><td><code>conda activate $ENVIRONMENT_NAME</code></td><td><code>X</code></td><td><code>pyenv activate &lt;env_name&gt;</code></td></tr><tr><td><strong>Deactivate an environment</strong></td><td><code>conda deactivate</code></td><td><code>X</code></td><td><code>pyenv deactivate</code></td></tr><tr><td><strong>Search available packages</strong></td><td><code>conda search $SEARCH_TERM</code></td><td><code>pip search $SEARCH_TERM</code></td><td><code>X</code></td></tr><tr><td><strong>Install package from specific source</strong></td><td><code>conda install --channel $URL $PACKAGE_NAME</code></td><td><code>pip install --index-url $URL $PACKAGE_NAME</code></td><td><code>X</code></td></tr><tr><td><strong>List installed packages</strong></td><td><code>conda list --name $ENVIRONMENT_NAME</code></td><td><code>pip list</code></td><td><code>X</code></td></tr><tr><td><strong>Create requirements file</strong></td><td><code>conda list --export</code></td><td><code>pip freeze</code></td><td><code>X</code></td></tr><tr><td><strong>List all environments</strong></td><td><code>conda info --envs</code></td><td><code>X</code></td><td><code>pyenv virtualenvs</code> or <code>lsvirtualenv</code></td></tr><tr><td><strong>Install other package manager</strong></td><td><code>conda install pip</code></td><td><code>pip install conda</code></td><td><code>X</code></td></tr><tr><td><strong>Install Python</strong></td><td><code>conda install python=x.x</code></td><td><code>X</code></td><td><code>X</code></td></tr><tr><td><strong>Update Python</strong></td><td><code>conda update python*</code></td><td><code>X</code></td><td><code>X</code></td></tr><tr><td><strong>刪除虛擬環境</strong></td><td><code>conda env remove --name &lt;env_name&gt;</code></td><td><code>rm -rf &lt;env_path&gt;  # Linux/macOS</code></td><td><code>pyenv uninstall &lt;env_name&gt;</code></td></tr></tbody></table></div><p>conda update python會把Python 2.x更新到最新的2.latest，把Python 3.x更新到3.latest。</p><h2 id="Library-vs-Package-vs-Module"><a href="#Library-vs-Package-vs-Module" class="headerlink" title="Library vs. Package vs. Module"></a><strong>Library vs. Package vs. Module</strong></h2><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mylibrary&#x2F;</span><br><span class="line">    __init__.py</span><br><span class="line">    module1.py</span><br><span class="line">    module2.py</span><br><span class="line">    subpackage&#x2F;</span><br><span class="line">        __init__.py</span><br><span class="line">        submodule1.py</span><br></pre></td></tr></table></figure><ol><li>一个library可能包含一个或多个包，也可能只包含一个模块。它的目的是提供某些功能，使开发者不必重新发明轮子。</li><li>一个package是包含<code>__init__.py</code>文件的目录，<code>__init__.py</code>文件使得这个目录被Python识别为一个包。</li><li>module是Python代码的一个文件，通常以.py结尾。它可以包含变量、函数、类和可执行代码。模块是最小的代码单元，可以被单独导入使用。</li></ol><p>mylibrary, NumPy, Matplotlib 这些都同时是库也是包，怎么叫都行。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># import 引入一个模块或包，通常是整个模块或包。</span></span><br><span class="line"><span class="keyword">import</span> math  <span class="comment"># 引入模块 math (C实现的数学库)</span></span><br><span class="line"><span class="keyword">import</span> numpy  <span class="comment"># 引入包 numpy</span></span><br><span class="line"><span class="comment"># from 从一个模块或包中引入特定的功能（如函数、类、变量都可以）。</span></span><br><span class="line"><span class="keyword">from</span> math <span class="keyword">import</span> pi  <span class="comment"># 从模块 math 中引入变量 pi</span></span><br><span class="line"><span class="keyword">from</span> math <span class="keyword">import</span> sqrt  <span class="comment"># 从模块 math 中引入函数 sqrt</span></span><br><span class="line"><span class="keyword">from</span> numpy <span class="keyword">import</span> linalg  <span class="comment"># 从包 numpy 中引入子包 linalg</span></span><br></pre></td></tr></table></figure><h2 id="venv-vs-virtualenv"><a href="#venv-vs-virtualenv" class="headerlink" title="venv vs. virtualenv"></a><strong>venv vs. virtualenv</strong></h2><p>venv是Python3内置的标准库，用法是python3 -m venv。<br>我2021年用的是pyenv里带的插件pyenv-virtualenv，不是标准库，用法就如上面的表格。</p><h2 id="PyPI"><a href="#PyPI" class="headerlink" title="PyPI"></a><strong>PyPI</strong></h2><p>PyPI是一個Python官方的包倉庫，直接pip install package就會<a href="https://pypi.python.org/pypi">搜索PyPI</a>，官網把library/package又叫作project，除了可以從PyPI下載，還可以：</p><ol><li>在本地pip install，</li><li>任意服務器pip install UR，</li><li>或者用git repo，</li><li>或者公有/私有鏡像pip install —index-url 或 -i，</li><li>或者conda倉庫conda install。</li></ol><h2 id="environment-yml和requirement-txt"><a href="#environment-yml和requirement-txt" class="headerlink" title="environment.yml和requirement.txt"></a><strong>environment.yml和requirement.txt</strong></h2><p>dockerfile、makefile、environment.yml都是軟件（docker、make、conda）自定義的語法，用Go、C、PyYAML解析。</p><div class="table-container"><table><thead><tr><th><strong>功能</strong></th><th><strong><code>environment.yml</code> (用于 <code>conda</code>)</strong></th><th><strong><code>requirements.txt</code> (用于 <code>pip</code>)</strong></th></tr></thead><tbody><tr><td><strong>定义</strong></td><td>定义一个完整的 Conda 环境，包括 Python 版本、依赖包等</td><td>列出所有 Python 包及其版本</td></tr><tr><td><strong>创建环境命令</strong></td><td><code>conda env create -f environment.yml</code></td><td><code>pip install -r requirements.txt</code></td></tr><tr><td><strong>激活环境命令</strong></td><td><code>conda activate myenv</code></td><td><code>conda activate myenv</code> 或者 <code>python -m venv env</code> (虚拟环境)</td></tr><tr><td><strong>导出当前环境</strong></td><td><code>conda env export &gt; environment.yml</code></td><td><code>pip freeze &gt; requirements.txt</code></td></tr><tr><td><strong>支持的包管理工具</strong></td><td><code>conda</code> 和 <code>pip</code>（通过 <code>pip</code> 部分）</td><td><code>pip</code></td></tr><tr><td><strong>支持 Python 包以外的依赖</strong></td><td>可以列出任何类型的依赖（包括非 Python 包）</td><td>仅限 Python 包，无法列出非 Python 包</td></tr><tr><td><strong>指定包的版本</strong></td><td><code>- numpy=1.21.0</code></td><td><code>numpy==1.21.0</code></td></tr><tr><td><strong>安装命令</strong></td><td><code>conda env create -f environment.yml</code></td><td><code>pip install -r requirements.txt</code></td></tr><tr><td><strong>更新环境</strong></td><td><code>conda env update -f environment.yml</code></td><td><code>pip install --upgrade -r requirements.txt</code></td></tr><tr><td><strong>删除环境</strong></td><td><code>conda env remove -n myenv</code></td><td>見上面的表“刪除虛擬環境”</td></tr><tr><td><strong>手动转换</strong></td><td>可以手动从 <code>requirements.txt</code> 转换为 <code>environment.yml</code></td><td>可以通过 <code>conda list --export</code> 转换为 <code>requirements.txt</code></td></tr></tbody></table></div><p>conda裡用pip：<br>默认通过 Conda 通道安装，支持通过 <code>pip</code> 安装包，都會安裝到當前環境的site-packages裡，pip只會安裝在當前環境裡，在yaml文件裡會在dependencies:裡多一個- pip:- xxx。</p><p><code>pip install -r requirements.txt</code>就相當於把requirements.txt的內容展開按順序放在install之後，如果<a href="#pypi">希望安裝PyPI之外的資源</a>，類似<code>conda env create -f environment.yml</code>這樣指定channel的話，可以直接在<code>pip install -r requirements.txt</code>之後添加-i，或者把-i放進txt首行也行。</p><p>conda的channels：</p><ol><li>default通道就是<a href="https://anaconda.org/anaconda/repo">官方倉庫</a>。</li><li><a href="https://conda-forge.org">conda-forge</a>是最主要的第三方倉庫。</li><li><code>conda config --show channels</code>看配置了哪些channels。</li><li><code>conda config --add channels conda-forge</code>添加channel。</li><li><code>conda config --set channel_priority strict</code>可以指定搜索順序，比如把conda-forge設置在default之前獲取最新更新。</li></ol><p><img src="/pics/conda-channels.jpeg" alt=""></p><h2 id="PyTorch"><a href="#PyTorch" class="headerlink" title="PyTorch"></a><strong>PyTorch</strong></h2><p><a href="https://pytorch.org/get-started/locally/">https://pytorch.org/get-started/locally/</a><br>pytorch pytorch-cpu pytorch-gpu pytorch-cuda真JB乱套，组里服务器是cuda12.4，最后我只留下了从上面链接里下的pytorch和pytorch-cuda。<br>等跑完導電就把這個打包新的environment.yml上傳到github，不然總亂套。</p><h2 id="vscode-ssh-remote開發時跳轉函數定義"><a href="#vscode-ssh-remote開發時跳轉函數定義" class="headerlink" title="vscode ssh remote開發時跳轉函數定義"></a><strong>vscode ssh remote開發時跳轉函數定義</strong></h2><p>在服務器上沒法按著command跳轉到模塊裡，<a href="https://stackoverflow.com/questions/70785500/how-can-i-jump-to-function-definitions-using-vs-code-in-ssh-remote-development">查了下</a>想起來vscode的extensions需要在服務器上再裝一遍，裝完Python插件果然立刻就好了。<br>有些找不到位置無法訪問但是確實裝了的庫選對解釋器位置就好了（shift+command+p 重新选择python解释器）。<br><img src="/pics/Screenshot 2025-01-16 at 20.59.51.png" alt=""></p><h1 id="24號回家的火車上再讀cosmos"><a href="#24號回家的火車上再讀cosmos" class="headerlink" title="24號回家的火車上再讀cosmos"></a>24號回家的火車上再讀cosmos</h1><p>The known is finite, the unknown infinite; intellectually we stand on an islet in the midst of an illimitable ocean of inexplicability. Our business in every generation is to reclaim a little more land, to add something to the extent and the solidity of our possessions. ——Thomas Huxley, 1887</p><p>Excerpt From Cosmos, Carl Sagen</p><p>看残酷天堂群有人说到退休储蓄pretax 401k match &gt; &gt; roth IRA &gt; pretax/roth 401k &gt; backdoor IRA &gt; personal brokeage account，中国的从朱镕基开始的由社会保障体系取代单位负责退休金是怎样的机制？以前的单位负责分非货币化的房和退休金相比如今确实是两个世界。<br>浦東是朱鎔基被鄧小平提拔之前在上海跟江澤民共事時開發的。<br>學而不思欠擬合，思而不學過擬合。</p><h1 id="BUGs"><a href="#BUGs" class="headerlink" title="BUGs"></a><strong>BUGs</strong></h1><ul><li>出现两个环境前缀。<figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">(molgen) (base) liuhao@zju0:~/MolGen$ conda deactivate </span><br><span class="line">(base) (base) liuhao@zju0:~/MolGen$ conda deactivate</span><br><span class="line">liuhao@zju0:~/MolGen$ conda activate molgen</span><br><span class="line">(molgen) liuhao@zju0:~/MolGen$</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;水蚺 (Anaconda)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;蟒蛇 (Python)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;常待在河流、沼泽等水域中。&lt;/td&gt;
&lt;td&gt;生活在森林、草原或岩石地区。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;更重更粗壮，适应水中的捕猎。&lt;/td&gt;
&lt;td&gt;更长但较轻，灵活性更高。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;卵胎生（卵在母体内孵化后直接产下幼蛇）&lt;/td&gt;
&lt;td&gt;卵生（产卵，并在巢中孵化）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="pyenv" scheme="http://blog.v2beach.cn/tags/pyenv/"/>
    
      <category term="virtualenv" scheme="http://blog.v2beach.cn/tags/virtualenv/"/>
    
      <category term="python" scheme="http://blog.v2beach.cn/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>RL Algos</title>
    <link href="http://blog.v2beach.cn/2024/12/23/RL-Algos/"/>
    <id>http://blog.v2beach.cn/2024/12/23/RL-Algos/</id>
    <published>2024-12-23T09:52:28.000Z</published>
    <updated>2024-12-30T13:09:27.978Z</updated>
    
    <content type="html"><![CDATA[<p>put on your helmet, and keep going.</p><a id="more"></a><p>最高人民检察院关于全面深化检察改革、进一步加强新时代检察工作的意见<br>发布时间：2024年12月05日<br>二、健全坚持党对检察工作绝对领导制度体系<br><strong>3.坚持和加强党中央集中统一领导。</strong>持续擦亮坚定拥护“两个确立”、坚决做到“两个维护”的鲜明政治底色，切实做到检察工作方向由党指引、检察工作原则由党确定、检察工作决策由党统领。严格落实“第一议题”制度，完善习近平总书记重要指示批示和党中央重大决策部署在检察机关落实机制。严格执行请示报告制度。<br><strong>4.持续加强党的创新理论武装。</strong>学深悟透习近平新时代中国特色社会主义思想，学思践悟习近平法治思想，健全以学铸魂、以学增智、以学正风、以学促干长效机制。加强理想信念教育和职业道德教育，分级分类开展检察人员政治轮训。<br><strong>5.完善检察机关党的建设制度机制。</strong>坚持以政治建设为统领，增强检察机关各级党组织政治功能和组织功能。做深做实政治素质考察，深化检察系统内巡视和政治督察。完善检察机关党员教育管理、作用发挥机制，推进党建和业务深度融合。加强新时代检察文化建设，培育“忠诚、为民、担当、公正、廉洁”的新时代检察精神，优化检察英模人物选树学习机制，让求真务实、担当实干成为检察人员的鲜明履职特征。<br><strong>6.健全检察机关意识形态工作机制。</strong>全面落实意识形态工作责任制，坚决捍卫意识形态安全。坚持敢于斗争、善于斗争，坚决抵制西方“宪政”、“三权鼎立”、“司法独立”等错误观点。常态化开展检察人员现实思想调查分析，落实谈心谈话制度。</p><p>这是中国人的强化学习。</p><script type="math/tex; mode=display">\text{reward} =\begin{cases}\text{1}, & \text{sing praise} \\\text{Uniform}(-0x3f, -1), & \text{cross red lines} \\0, & \text{otherwise}\end{cases}</script><hr><p>其他人的强化学习：</p><p>公权力法无授权不可为；私权法无禁止皆可为。德国基本法 <strong>Basic Law for the Federal Republic of Germany</strong>,<br>Article 20: [Constitutional principles – Right of resistance]<br>(3) The legislature shall be bound by the constitutional order, the executive and the judiciary by law and justice.<br>(4) All Germans shall have the right to resist any person seeking to abolish this constitutional order if no other remedy is available.<br>Article 2: [Personal freedoms]<br>(1) Every person shall have the right to free development of his personality insofar as he does not violate the rights of others or offend against the constitutional order or the moral law.<br>(2) Every person shall have the right to life and physical integrity. Freedom of the person shall be inviolable. These rights may be interfered with only pursuant to a law.</p><p>公民没有自证清白的义务。谁主张，谁举证。美国宪法第五修正案 The <strong>Fifth Amendment</strong> to the <strong>United States Constitution</strong> 关于 Self-Incrimination :<br>No person shall be held to answer for a capital, or otherwise infamous crime, unless on a presentment or indictment of a Grand Jury, except in cases arising in the land or naval forces, or in the Militia, when in actual service in time of War or public danger; nor shall any person be subject for the same offence to be twice put in jeopardy of life or limb; nor shall be compelled in any criminal case to be a witness against himself, nor be deprived of life, liberty, or property, without due process of law; nor shall private property be taken for public use, without just compensation.</p><h1 id="DQN"><a href="#DQN" class="headerlink" title="DQN"></a><strong>DQN</strong></h1><p><a href="https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html">Pytorch DQN Tutorial</a>, <a href="https://github.com/V2beach/RL-Atari/blob/main/_downloads/9da0471a9eeb2351a488cd4b44fc6bbf/reinforcement_q_learning.ipynb">code</a>.</p><h1 id="A2C"><a href="#A2C" class="headerlink" title="A2C"></a><strong>A2C</strong></h1><p><a href="https://hrl.boyuai.com/chapter/2/策略梯度算法/">https://hrl.boyuai.com/chapter/2/策略梯度算法/</a><br><a href="https://github.com/V2beach/RL-Atari/blob/main/Actor-Critic.md">A2C</a>, <a href="https://github.com/V2beach/RL-Atari/blob/main/a2c.py">code</a>.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;put on your helmet, and keep going.&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="Reinforcement Learning" scheme="http://blog.v2beach.cn/tags/Reinforcement-Learning/"/>
    
  </entry>
  
  <entry>
    <title>RL Atari</title>
    <link href="http://blog.v2beach.cn/2024/12/22/RL-Atari/"/>
    <id>http://blog.v2beach.cn/2024/12/22/RL-Atari/</id>
    <published>2024-12-22T05:11:22.000Z</published>
    <updated>2025-03-19T08:55:14.968Z</updated>
    
    <content type="html"><![CDATA[<p>Gym 是 OpenAI 做的 RL tasks 模拟库 (虽然 Play Atari w/ Deep RL 等都是 Deepmind 发的)，比如 <a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#What-is-Reinforcement-Learning">CartPole, Robot Locomotion, Atari 2600</a> 都有模拟。除了 Python 外在 C++ 也实现了此 API。<br>其中 ALE (Arcade-Learning-Environment) 是 Atari 2600 part，stable-baselines3 在这个模拟库的逻辑之上实现 RL 经典算法。</p><a id="more"></a><h1 id="Gymnasium"><a href="#Gymnasium" class="headerlink" title="Gymnasium"></a><strong>Gymnasium</strong></h1><p><a href="https://gymnasium.farama.org/">https://gymnasium.farama.org/</a></p><p>為所有單 Agent RL Environments 提供 API，並實現了一些常用環境。</p><h2 id="Env"><a href="#Env" class="headerlink" title="Env"></a><strong>Env</strong></h2><p><code>Env</code> 是 Gym 的核心，是個實現了 Markov 決策過程的類，能生成 initial state、給定 action 转移到下一个 state、可视化 environment。<code>Wrapper</code> 用来修改 environment，即修改 agent observations, rewards, actions。</p><p>使用 Gym 有四个关键函数 <code>make()</code>, <code>Env.reset()</code>, <code>Env.step()</code>, <code>Env.render()</code>。</p><h3 id="初始化环境"><a href="#初始化环境" class="headerlink" title="初始化环境"></a><strong>初始化环境</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line">env = gym.make(<span class="string">'CartPole-v1'</span>)</span><br></pre></td></tr></table></figure><p><code>make()</code> 返回一个可交互的 <code>Env</code> 对象，<code>pprint_registry()</code> 查看所有可以创建的环境。</p><p>可以指定 environment 内部定义的 <code>render_mode</code>，可以为 None, 字符串比如 human, rgb_array, rgb_array_list 等，以 <code>rgb_array</code> 和 <code>rgb_array_list</code> 为例：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">frame = env.render(render_mode=<span class="string">'rgb_array'</span>)  <span class="comment"># 返回单个图像帧 (3 维 numpy 数组)</span></span><br><span class="line">frames = env.render(render_mode=<span class="string">'rgb_array_list'</span>)  <span class="comment"># 从环境开始到当前 time step 的所有图像，[frame_1, frame_2, ..., frame_n]，每个 frame 都是 3d numpy array</span></span><br></pre></td></tr></table></figure><p>用 register 初始化的话， 可以用<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">gym.register(id=<span class="string">"namespace/mandatory name-optional version"</span>, entry_point=<span class="class"><span class="keyword">class</span>)</span></span><br></pre></td></tr></table></figure><br>最好用 register 更灵活的扩展 <code>register_envs</code>，比如 <code>gym.register(ale_py)</code> 这种语法在 register 里不行。</p><p><code>gymnasium.pprint_registry()</code> 能打印所有注册的环境，注册过才可以用 <code>gymnasium.make()</code> 创建，<code>gymnasium.make_vec(, num_envs=n)</code> 可以生成 n 个并行的相同环境。</p><h3 id="环境交互"><a href="#环境交互" class="headerlink" title="环境交互"></a><strong>环境交互</strong></h3><p><img src="/pics/The-general-agent-environment-interaction-loop-Based-on-the-current-state-the-agent.ppm.png" width="50%" height="50%"/></p><p><img src="/pics/AE_loop.png" width="50%" height="50%"/></p><p>参考上面经典的 “agent-environment loop”，Agent 会收到一个环境的 observation，然后选择一个 action，environment 用它来决定 reward 和下一个 observation。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">!pip install swig <span class="string">"gymnasium[box2d]"</span></span><br><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"></span><br><span class="line">env = gym.make(<span class="string">"CartPole-v1"</span>, render_mode=<span class="string">"human"</span>)</span><br><span class="line">observation, info = env.reset()  <span class="comment"># reset(seed, options), 特定 environment 可以输入一个 dict 选择初始化的方式，reset 即得到 first observation</span></span><br><span class="line"></span><br><span class="line">episode_over = <span class="literal">False</span>  <span class="comment"># 结束循环 flag</span></span><br><span class="line"><span class="keyword">while</span> <span class="keyword">not</span> episode_over:</span><br><span class="line">    action = env.action_space.sample()  <span class="comment"># agent policy that uses the observation and info，随机从 action space 选一个</span></span><br><span class="line">    observation, reward, terminated, truncated, info = env.step(action)  <span class="comment"># 执行指定 action，一次 action-observation 的 exchange (给 Env 一个 action 交易到一个新 state / observation) 是一个 timestep</span></span><br><span class="line"></span><br><span class="line">    episode_over = terminated <span class="keyword">or</span> truncated  <span class="comment"># terminated 是环境终止，比如 robot 掉进 lava 这种，truncated 用于在一定 timesteps 之后终止 Env</span></span><br><span class="line"></span><br><span class="line">env.close()  <span class="comment"># 有时不希望 close Env 就用 reset 重新开始</span></span><br></pre></td></tr></table></figure><h3 id="动作和状态空间"><a href="#动作和状态空间" class="headerlink" title="动作和状态空间"></a><strong>动作和状态空间</strong></h3><p><strong>任何 environment 都指定了有效的 actions 和 observations</strong>，存在 attributes <code>action_space</code> 和 <code>observation_space</code> 里，有助于理解环境期望的输入输出。</p><p><code>Env.action_space</code> 和 <code>Env.observation_space</code> 都属于 <code>Space</code> 类的 instances，主要有两个函数 <code>Space.contains()</code>和 <code>Space.sample()</code>。</p><p>Gym 支持很多种数据结构的 spaces：</p><ul><li>Box</li><li>Discrete</li><li>MultiBinary</li><li>MultiDiscrete</li><li>Text</li><li>Dict</li><li>Tuple</li><li>Graph</li><li>Sequence</li></ul><h3 id="修改环境"><a href="#修改环境" class="headerlink" title="修改环境"></a><strong>修改环境</strong></h3><p>Wrappers 可以修改 environment 而不改变其代码，大多数用 <code>gymnasium.make()</code> 产生的环境都是默认被 <code>TimeLimit</code> (定义一个 truncated timestep)、<code>OrderEnforcing</code> (reset 之前 step 或 render 会报错)、<code>PassiveEnvChecker</code> (检查 reset 是否返回符合规范的初始状态 obs 和信息 info，检查 step 是否返回格式正确的 <code>(obs, reward, done(terminated, truncated), info)</code>，检查 render (可选) 是否是合理的可视化)。</p><p>其中 PassiveEnvChecker 这层 wrapper 可以在 make 时通过 <code>, disable_env_checker=True</code> 去掉。</p><p>除此之外比如 FlattenObservation，</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">from</span> gymnasium.wrappers <span class="keyword">import</span> FlattenObservation</span><br><span class="line">env = gym.make(<span class="string">"CarRacing-v3"</span>)</span><br><span class="line">env.observation_space.shape</span><br><span class="line">(<span class="number">96</span>, <span class="number">96</span>, <span class="number">3</span>)</span><br><span class="line">wrapped_env = FlattenObservation(env)</span><br><span class="line">wrapped_env.observation_space.shape</span><br><span class="line">(<span class="number">27648</span>,)</span><br><span class="line">wrapped_env</span><br><span class="line">&lt;FlattenObservation&lt;TimeLimit&lt;OrderEnforcing&lt;PassiveEnvChecker&lt;CarRacing&lt;CarRacing-v3&gt;&gt;&gt;&gt;&gt;&gt;</span><br><span class="line">wrapped_env.unwrapped</span><br><span class="line">&lt;gymnasium.envs.box2d.car_racing.CarRacing object at <span class="number">0x7f04efcb8850</span>&gt;</span><br></pre></td></tr></table></figure><p>unwrapped 可以得到最里面的 base environment。</p><p><a href="https://gymnasium.farama.org/api/wrappers/misc_wrappers/#gymnasium.wrappers.AtariPreprocessing">AtariPreprocessing</a> 也是个 warpper，会完成 Noop Reset (返回执行过随机次 no-op 的 initial state, default max 30 no-ops), Frame Skipping (4 by default), Resize to a square image (210x180 to 84x84 by default), Grayscale observation (默认转灰度图), Grayscale new axis (保留 channel 维度为 1，默认不保留) 等。</p><h3 id="自定义环境"><a href="#自定义环境" class="headerlink" title="自定义环境"></a><strong>自定义环境</strong></h3><ul><li>为 <code>class CustomEnv(gym.Env)</code> 实现 <code>__init__</code> (包括定义 agent / target location 和 actions)</li><li>构建 observations</li><li>实现<code>reset</code>, <code>step</code></li><li>包装 wrappers</li><li>register / make</li></ul><h2 id="ALE"><a href="#ALE" class="headerlink" title="ALE"></a><strong>ALE</strong></h2><p><a href="http://ale.farama.org/">http://ale.farama.org/</a></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">rom_file = <span class="string">"Breakout.bin"</span>  <span class="comment"># WARNING: Possibly unsupported ROM: mismatched MD5.</span></span><br><span class="line"><span class="keyword">from</span> ale_py <span class="keyword">import</span> ALEInterface, roms</span><br><span class="line">ale = ALEInterface()</span><br><span class="line">ale.loadROM(roms.get_rom_path(<span class="string">"breakout"</span>))  <span class="comment"># /Users/v2beach/.pyenv/versions/miniforge3-4.10/lib/python3.9/site-packages/ale_py/roms/breakout.bin</span></span><br><span class="line">print(ale.getLegalActionSet())  <span class="comment"># [&lt;Action.NOOP: 0&gt;, &lt;Action.FIRE: 1&gt;, &lt;Action.UP: 2&gt;, &lt;Action.RIGHT: 3&gt;, &lt;Action.LEFT: 4&gt;, &lt;Action.DOWN: 5&gt;, &lt;Action.UPRIGHT: 6&gt;, &lt;Action.UPLEFT: 7&gt;, &lt;Action.DOWNRIGHT: 8&gt;, &lt;Action.DOWNLEFT: 9&gt;, &lt;Action.UPFIRE: 10&gt;, &lt;Action.RIGHTFIRE: 11&gt;, &lt;Action.LEFTFIRE: 12&gt;, &lt;Action.DOWNFIRE: 13&gt;, &lt;Action.UPRIGHTFIRE: 14&gt;, &lt;Action.UPLEFTFIRE: 15&gt;, &lt;Action.DOWNRIGHTFIRE: 16&gt;, &lt;Action.DOWNLEFTFIRE: 17&gt;]</span></span><br><span class="line">ale.act(<span class="number">0</span>)</span><br><span class="line">rgb_image = ale.getScreenRGB()</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">plt.imshow(rgb_image)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p>可以这样每次执行一个动作都用 ALE Python interface 保存 screenshots。</p><h3 id="用-Gym"><a href="#用-Gym" class="headerlink" title="用 Gym"></a><strong>用 Gym</strong></h3><p>用原始 API 比较方便，不用学两套 function，</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">import</span> ale_py</span><br><span class="line">gym.register_envs(ale_py)</span><br><span class="line">env = gym.make_vec(<span class="string">"ALE/Breakout-v5"</span>, num_envs=<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者make_vec_env，跟 register 一样是过时版本，但可能会遇到</span></span><br><span class="line">make_vec_env(<span class="string">"ALE/Breakout-v5"</span>, n_envs=<span class="number">4</span>, vec_env_cls=stable_baselines3.common.vec_env.DummyVecEnv)</span><br></pre></td></tr></table></figure><p>这个 env 其实就是对上面 ALEInterface() 的 wrapper 封装，用下面的方法可以直接操作 base env ALEInterface：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">ale_interface = env.unwrapped.ale</span><br><span class="line">current_state = ale_interface.cloneSystemState()</span><br><span class="line"></span><br><span class="line">print(ale_interface.getAvailableModes())  <span class="comment"># [0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44]</span></span><br><span class="line">print(ale_interface.getAvailableDifficulties())  <span class="comment"># [0, 1]</span></span><br><span class="line">print(current_state.getCurrentMode())</span><br><span class="line">print(current_state.getDifficulty())</span><br><span class="line"></span><br><span class="line">ale_interface.setMode(ale_interface.getAvailableModes()[<span class="number">3</span>])</span><br><span class="line">ale_interface.setDifficulty(ale_interface.getAvailableDifficulties()[<span class="number">1</span>])  <span class="comment"># 但修改了模式和难度游戏内没发现变化</span></span><br><span class="line"></span><br><span class="line">ale_interface = env.unwrapped.ale</span><br><span class="line">current_state = ale_interface.cloneSystemState()</span><br><span class="line">print(current_state.getCurrentMode())</span><br><span class="line">print(current_state.getDifficulty())</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># /Users/v2beach/.pyenv/versions/miniforge3-4.10/lib/python3.9/site-packages/ale_py/ 除了 roms/breakout.bin 就是 env.py</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">render_mode: str =&gt; One of &#123; 'human', 'rgb_array' &#125;.</span></span><br><span class="line"><span class="string">    If `human` we'll interactively display the screen and enable game sounds. This will lock emulation to the ROMs specified FPS</span></span><br><span class="line"><span class="string">    If `rgb_array` we'll return the `rgb` key in step metadata with the current environment RGB frame.</span></span><br><span class="line"><span class="string">"""</span></span><br></pre></td></tr></table></figure><p>可以在训练的时候用 rgb array，推理 render 的时候用 human。</p><h3 id="Frame-Skipping-and-Preprocessing"><a href="#Frame-Skipping-and-Preprocessing" class="headerlink" title="Frame Skipping and Preprocessing"></a><strong>Frame Skipping and Preprocessing</strong></h3><p><a href="https://danieltakeshi.github.io/2016/11/25/frame-skipping-and-preprocessing-for-deep-q-networks-on-atari-2600-games/">这篇文章</a>写得相当好。</p><h2 id="Training-an-Agent"><a href="#Training-an-Agent" class="headerlink" title="Training an Agent"></a><strong>Training an Agent</strong></h2><p><a href="https://gymnasium.farama.org/introduction/train_agent/">https://gymnasium.farama.org/introduction/train_agent/</a></p><h3 id="Stable-Baseline-3"><a href="#Stable-Baseline-3" class="headerlink" title="Stable Baseline 3"></a><strong>Stable Baseline 3</strong></h3><p><code>pip install &quot;stable-baselines3[extra]&quot;</code></p><p>直接看代码，反正 Gym ALE Stable-baseline 的 doc 也是直接贴代码。</p><p><a href="https://github.com/DLR-RM/rl-baselines3-zoo/blob/506bb7aa40e9d90e997580a369f2e9bf64abe594/benchmark.md?plain=1#L31">pre-trained zoo</a> 里 breakout 模型都训了 10M 个 timesteps，作者是<a href="https://github.com/DLR-RM/rl-trained-agents">这个</a>，打砖块代码分别存在 <a href="https://github.com/V2beach/RL-Atari/blob/main/stable-baselines3/test.py">1</a>, <a href="https://github.com/V2beach/RL-Atari/blob/main/stable-baselines3/breakout.py">2</a>, <a href="https://github.com/V2beach/RL-Atari/blob/main/stable-baselines3/breakout_vec.py">3</a> 里面。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">import</span> ale_py  <span class="comment"># 这是 Env 类</span></span><br><span class="line">gym.register_envs(ale_py)</span><br><span class="line"></span><br><span class="line"><span class="comment"># env = gym.make_vec('ALE/Breakout-v5', num_envs=4, frameskip=1, render_mode="human")</span></span><br><span class="line"><span class="comment"># env = gym.wrappers.AtariPreprocessing(env)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> gymnasium.wrappers <span class="keyword">import</span> AtariPreprocessing</span><br><span class="line"><span class="keyword">from</span> stable_baselines3.common.vec_env <span class="keyword">import</span> DummyVecEnv, SubprocVecEnv</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">make_env</span><span class="params">()</span>:</span></span><br><span class="line">    env = gym.make(<span class="string">'ALE/Breakout-v5'</span>, frameskip=<span class="number">1</span>, render_mode=<span class="string">"human"</span>)</span><br><span class="line">    env = AtariPreprocessing(env)</span><br><span class="line">    env = gym.wrappers.FrameStackObservation(env, stack_size=<span class="number">4</span>)</span><br><span class="line">    <span class="keyword">return</span> env</span><br><span class="line">env = DummyVecEnv([make_env <span class="keyword">for</span> _ <span class="keyword">in</span> range(<span class="number">4</span>)])</span><br><span class="line"></span><br><span class="line"><span class="comment"># print(type(env.observation_space), env.observation_space.shape, type(env.action_space), env.action_space)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> stable_baselines3 <span class="keyword">import</span> DQN</span><br><span class="line"><span class="comment"># model = DQN('CnnPolicy', env, verbose=1)</span></span><br><span class="line"><span class="comment"># model.learn(total_timesteps=10_000_000, progress_bar=True)</span></span><br><span class="line"><span class="comment"># model.save("xxx-BreakoutNoFrameskip-v4")</span></span><br><span class="line">model = DQN.load(<span class="string">"dqn-BreakoutNoFrameskip-v4"</span>,  env=env, custom_objects=&#123;</span><br><span class="line">    <span class="string">"optimize_memory_usage"</span>: <span class="literal">False</span>,</span><br><span class="line">    <span class="string">"handle_timeout_termination"</span>: <span class="literal">False</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment"># env = model.get_env()  # 这里需要保证跟 model 的环境一致，用非 vec 的写法可以生成 model 但其实跟训练环境不一致</span></span><br><span class="line">obs = env.reset()</span><br><span class="line">done = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="keyword">not</span> done:</span><br><span class="line">    actions, _states = model.predict(obs, deterministic=<span class="literal">True</span>)</span><br><span class="line">    obs, rewards, dones, info = env.step(actions)</span><br><span class="line">    env.render(<span class="string">"human"</span>)  <span class="comment"># img = env.render(mode='rgb_array') if rgb_array, 而且确保在初始化环境时正确配置了渲染模式, make(,render_mode=)</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Gym 是 OpenAI 做的 RL tasks 模拟库 (虽然 Play Atari w/ Deep RL 等都是 Deepmind 发的)，比如 &lt;a href=&quot;https://blog.v2beach.cn/2024/09/24/cs231n-1/#What-is-Reinforcement-Learning&quot;&gt;CartPole, Robot Locomotion, Atari 2600&lt;/a&gt; 都有模拟。除了 Python 外在 C++ 也实现了此 API。&lt;br&gt;其中 ALE (Arcade-Learning-Environment) 是 Atari 2600 part，stable-baselines3 在这个模拟库的逻辑之上实现 RL 经典算法。&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="Reinforcement Learning" scheme="http://blog.v2beach.cn/tags/Reinforcement-Learning/"/>
    
  </entry>
  
  <entry>
    <title>paper Molecule generation using transformers and policy gradient reinforcement learning</title>
    <link href="http://blog.v2beach.cn/2024/12/18/paper-Molecule-generation-using-transformers-and-policy-gradient-reinforcement-learning/"/>
    <id>http://blog.v2beach.cn/2024/12/18/paper-Molecule-generation-using-transformers-and-policy-gradient-reinforcement-learning/</id>
    <published>2024-12-18T07:07:44.000Z</published>
    <updated>2025-01-16T16:22:06.414Z</updated>
    
    <content type="html"><![CDATA[<p>两年没看论文，实在难以专注，写笔记更有劲。</p><p>文章里的大部分术语都能在<a href="https://blog.v2beach.cn/2024/12/14/Molecule/">cs231n筆記</a>里找到解释。</p><a id="more"></a><p>药物发现里一个主要的挑战是，设计具有所需属性的药物。化学空间里有成药潜力的分子有 10^23 到 10^60 个，人类已合成过的分子量级是 10^8 。研发一种新药的平均开销 1～2 billion 美元，在 100 亿人民币上下，平均研发时间是 13 年。</p><p><strong>新药研发开销</strong>：</p><ol><li>药物发现阶段 (Discovery Phase)<ul><li>靶点筛选、化合物筛选、药理研究等；</li><li>成本占研发费用 10%～20%。</li></ul></li><li>临床前研究 (Preclinical Phase)<ul><li>动物实验、药物代谢研究、毒性试验等；</li><li>需要 1 到 3 年时间，成本在几千万美元到 1 亿美元之间。</li></ul></li><li>临床试验阶段 (Clinical Trials)<ul><li>Phase I: 一期关注药物安全性，开销几百万到几千万美元在少数健康志愿者中进行；</li><li>Phase II: 二期评估有效性和进一步安全性，开销几千万美元在少数患者中测试；</li><li>Phase III: 三期是整个研发过程中最贵的阶段，要在成千上万患者中进一步证明有效性和安全性。</li></ul></li><li>审批和上市阶段 (Approval and Launch Phase)<ul><li>监管机构审批，提交大量的临床数据和文献，需要大量行政支持。<ul><li>美国食品药物监管局 FDA (Food and Drug Administration) , 欧盟附属机构欧洲药品管理局 EMA (European Medicines Agency), 国家药品监督管理局 NMPA (National Medical Products Administration)</li></ul></li><li>上市后市场推广和营销，需要上亿美元。</li></ul></li><li>研发失败<ul><li>药物研发的成功率非常低，只有1/5,000到1/10,000的候选化合物最终能够成功上市。</li><li>平均研发十三年和平均开销 1～2 billion 美元里面，有很大一部分项目在临床前和临床阶段失败。</li></ul></li></ol><p>传统上，化学家和药理学家通过他们的直觉和专业知识来识别新分子。尽管用 Lipinski’s RO5 可以减少可能成药的分子数量，但搜索空间还是过大。为了进一步减小搜索空间，人们开始使用 <strong>high-throughput screening (HTS)</strong> 高通量筛选，但任务仍然难以完成。现在，计算方法可以用于减小药物搜索空间和缩短研发新药的时间。</p><p><strong>高通量筛选</strong>：</p><ol><li>研究人员准备化合物库 (Compound Library) 或药物库 (Drug Library)<ul><li>化学合成<ul><li>可以通过多步合成，每步通过化学反应加上一个新的基团。</li><li>现代化学合成可以用自动化的反应装置，短时间合成大量化合物。</li></ul></li><li>天然产物，即从植物、微生物、海洋生物等中，提取分离。</li><li>生物制药，从动物、人体提取。</li><li>药物衍生再设计，改造现有药物，通过增删改基团可以改变分子的性质。</li><li>基因工程微生物可以被设计成发酵过程中生产不同化学物质。</li><li>以上过程也可以跟计算机辅助药物设计 (CADD) 结合，用分子对接、分子动力学筛选化合物。</li></ul></li><li>实验筛选<ul><li>在微孔板（通常是96孔、384孔、1536孔板等）的每个孔中放入一个化合物，</li><li>测量其与目标的相互作用，例如酶的活性变化、细胞生长或死亡、荧光信号变化等。</li></ul></li><li>数据分析</li></ol><h1 id="property-generation"><a href="#property-generation" class="headerlink" title="property generation"></a><strong>property generation</strong></h1><p>3 个<strong>数据集</strong>：</p><ul><li>Moses 1.6M</li><li>Zinc 250K</li><li>GDB13 rand 1M</li></ul><p>7 个 <strong>baseline</strong>：</p><ul><li>GCPN: graphs</li><li>JTVAE: graphs</li><li>MolGPT: SMILES<ul><li>MolT5?效果如何</li></ul></li><li>MolGAN: graphs</li><li>MolDQN: graphs, Q-learning</li><li>GraphDF: flow</li><li>LSTM+RL</li></ul><p>用这些数据的开源代码，在 QED 上优化，用 baseline 在各自论文里描述的 reward function。<br>Hardware: one TITAN RTX GPU, 60GB RAM, and an eight-core CPU.<br>模型<strong>超参</strong>：</p><ul><li>4 masked transformer decoder layers</li><li>8 attention heads</li><li>1024d fc</li><li>512d embedding</li><li>3 epochs gpt</li><li>100 steps with each step averaging 500 molecules in RL stage</li><li>REINFORCE algorithm discount factor 0.99, max seq size 150 chars</li><li>character-level tokenization</li></ul><p><img src="/pics/molecule/taiga.png" alt=""></p><p>其中 <strong>reward function</strong>：</p><script type="math/tex; mode=display">R(s_T) = \begin{cases} 10 \times \text{QED}(s_T), & \text{if } s_T \text{ is a valid molecule} \\0, & \text{otherwise}\end{cases}</script><p><strong>Metrics</strong>：</p><ul><li>validity, rdkit 能生成 molecule 对象就是 1，否则 0；</li><li>novelty, 训练集没出现的生成数据；</li><li>diversity, 去重，这三个指标筛选后的再参与下面两个指标的比较。</li><li>QED, <a href="https://blog.v2beach.cn/2024/12/14/Molecule/#QED">博客里聊过</a>，这里用 8 个性质 (molecular weight, AlogP, hydrogen bond donors, hydrogen donor acceptors, molecular polar surface area, rotatable bonds, aromatic rings, structural alerts) 得到 0～1 结果；</li><li>SAS, 大于 6 难以合成。</li></ul><h1 id="property-optimization"><a href="#property-optimization" class="headerlink" title="property optimization"></a><strong>property optimization</strong></h1><p>2 个<strong>数据集</strong>：</p><ul><li>ChEMBL IC50，移除了只有范围数据的分子，只用治疗阿尔茨海默病的潜在靶点 BACE 1 这个 β-分泌酶1 (Beta-secretase 1) 的数据，从10164 条里筛选出了 9331 个样本。</li><li>400 个分子来自 anti-cancer activity dataset like FDA approval, clinical trials, DrugBank, etc</li></ul><p>他们用 Chemprop 而不是 RDKit 预测分子的 anti-cancer 性质数据，用 batch-size 50 训练了 30 epochs, lr 1e-4, ensemble size 2.<br>然后对 ChEMBL 数据用 exp(pIC50/3) 为奖励函数，对抗癌数据用 Chemprop 预测的 0～1 probability 结果作为奖励函数。</p><p>得到的结果有问题：</p><ol><li>3 个数据集的 Baseline 跟上面 Taiga stage 1 的数据对不上；</li><li>用 pIC50 作为奖励函数，模型评估指标也是 pIC50，这种 IC50 亲和力表现提高 20% 的提升是不是不太公平？</li></ol><p>之后还有很多试验，包括 tokenizer 和 w/o w/ RL 的 Validity(↑) Divesity(↑) Novelty(↑) QED(↑) SAS(↓) 结果，但不知为何后者没有 SAS 结果。</p><p><a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#Markov-Decision-Processes">MDP: M = (S, A, P, R, γ )</a>，这里我感觉文章里的表达逻辑混乱，梳理一下：</p><ul><li>状态空间 $S = \{s_i\}$，$0 &lt; i \leq t$，SMILES 串的长度 $t \leq T$ ，其中 $T$ 表示在模型生成 [EOS] 标记或达到最大长度时的终止状态；而 $s_0$ 是初始状态，它是一个空字符串。<ul><li>每一个 $s$ 都被表示为一个向量？向量里每一个字符都来自 $\{0, \dots, n\}$ 的 SMILES 字符集。</li></ul></li><li>动作空间 $A = \{a_i\}$，每个 $a_i$ 都来自 $\{0, \dots, n\}$ 的 SMILES vocabulary。</li><li>转移概率 P，由于状态空间和动作空间仅由字符组成，因此转移概率简单地为 $p(s_{t+1} | s_t, a_t) = 1$，因为添加字符是确定性的。</li><li>奖励函数 R，定义所有中间状态的奖励为零，即 $R(s_t) = 0$。$R(s_T) = f(s_T)$ 函数应用于最终生成的分子，$\gamma$ 是折扣因子。</li></ul><p>具体做法是 stage 2 每一个 token 作为 action，最后如果符合要求（QED, anti-cancer）则 reward = 1，前面每一步都是 0。<br>为什么不合并为 1 个 stage？<br>大概因为刚开始训练时模型生成的 SMILES 太随机，符合要求的几率太低，都是无效样本。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;两年没看论文，实在难以专注，写笔记更有劲。&lt;/p&gt;
&lt;p&gt;文章里的大部分术语都能在&lt;a href=&quot;https://blog.v2beach.cn/2024/12/14/Molecule/&quot;&gt;cs231n筆記&lt;/a&gt;里找到解释。&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="AI4science" scheme="http://blog.v2beach.cn/tags/AI4science/"/>
    
      <category term="molecule" scheme="http://blog.v2beach.cn/tags/molecule/"/>
    
  </entry>
  
  <entry>
    <title>分子生成</title>
    <link href="http://blog.v2beach.cn/2024/12/14/Molecule/"/>
    <id>http://blog.v2beach.cn/2024/12/14/Molecule/</id>
    <published>2024-12-14T03:40:07.000Z</published>
    <updated>2025-03-19T06:17:23.657Z</updated>
    
    <content type="html"><![CDATA[<p>在文獻裡 Molecular Generation 和 Molecule Generation 這倆說法各占一半。</p><a id="more"></a><h1 id="Atom-vs-Molecule"><a href="#Atom-vs-Molecule" class="headerlink" title="Atom vs. Molecule"></a><strong>Atom vs. Molecule</strong></h1><div align=center><img src="/pics/molecule/difference-atom-molecule_27c5571306.jpg" loading="lazy" alt="" width="50%" height="50%"></div><p>這裡的原子軌道其實是錯的，</p><div align=center><img src="/pics/molecule/electron_clouid-580x396.jpg" loading="lazy" alt="" width="50%" height="50%"></div><p>電子不是以確定的軌道運動的，是以波動的形式分布在原子核周圍，無法被精確定位，它的分布區域叫<strong>電子雲 (Electron Cloud)</strong> ，電子雲是一個概率分佈。</p><p>基態到激發態電子的能級變高，離原子核就越遠，改變軌道後會產生不同形狀的電子雲。</p><div align=center><img src="/pics/molecule/C12.png" loading="lazy" alt="" width="50%" height="50%"></div><p>比如碳 Carbon C 的基態電子分布是1s² 2s² 2p²，其中第二能層有兩種軌道， p 軌道離原子核比 s 軌道更遠，所以能量更高，吸收能量到激發態時可能電子會先從 s 軌道跳到 p 軌道，這裡的吸收能量通常是光子能量，不同波長的光子對應不同的能量。激發態的 C 可能變成 1s² 2s¹ 2p³ / 1s² 2s² 2p¹ 3s¹ / 1s² 2s² 2p¹ 3p¹ 等。</p><div align=center><img src="/pics/molecule/Borh-and-Shrodinger-Model.jpeg" loading="lazy" alt="" width="50%" height="50%"></div><p>分子裡的電子跟原子類似，電子只能佔據有特定能量的分子軌道，分子軌道是各個原子的原子軌道通過量子力學的線性組合，化學鍵（共價鍵、離子鍵、金屬鍵）是原子的電子雲重疊的結果，是通過量子力學中的波動函數互動產生的量子效應。</p><ul><li><strong>共價鍵 (Covalent Bond)</strong> 是兩個原子共享電子，比如乙炔的 C≡C 碳碳三重鍵和碳氫單鍵都是共價鍵。比如這裡的 C-H 碳氫單鍵是由 C 的 sp 混合軌道和 H 的 1s 軌道重疊所形成的較強 σ 鍵。</li><li><strong>離子鍵 (Ionic Bond)</strong> 是一個原子將<strong>電子完全轉移</strong>到另一個原子，從而形成帶正電的陽離子和帶負電的陰離子。比如氯化鈉是 Na 原子將一個電子轉移給 Cl 原子形成 Na⁺陽離子和 Cl⁻陰離子。通常是活潑的金屬原子和非金屬原子形成，HCl是共價鍵。</li><li><strong>金属键 (Metallic Bond)</strong> 是金屬元素間共享自由移動的電子，形成<strong>電子海 (Electron Sea)</strong>，自由地在整個金屬結構中移動。比如鋼鐵 Fe 中的鐵原子由金屬鍵結合，自由電子（“束縛電子”或“導電電子”）在金屬原子間流動，並且在金屬原子周圍形成一種電場，使得正離子（金屬原子核）保持緊密排列並且相互吸引。</li></ul><div align=center><img src="/pics/molecule/Ionic-vs-covalent-bonds.jpg" loading="lazy" alt="" width="50%" height="50%"></div><h2 id="物質"><a href="#物質" class="headerlink" title="物質"></a><strong>物質</strong></h2><p><strong>物質（Substance）</strong>是指具有一定質量和體積的物體，它由原子、分子或離子等基本單位組成。物質不都是分子，分子只是原子通過共價鍵結合成的粒子，比如 NaCl 是離子化合物。</p><ul><li><strong>元素 (Elements)</strong> 是由相同類型的原子組成的純物質，無法分解成更簡單的物質。</li><li><strong>化合物 (Compounds)</strong> 是由兩種或更多種不同化学元素通過化學鍵結合而成的物質，具有固定的成分比例，且不同於其組成元素的性質。</li><li><strong>混合物 (Mixtures)</strong> 是由兩種或多種不同物質（元素或化合物）物理結合而成，成分比例可以變化，且各成分保持其原有性質。</li></ul><h3 id="有机物"><a href="#有机物" class="headerlink" title="有机物"></a><strong>有机物</strong></h3><p>有机物 / 有机化合物 (organic compound/chemical) 是分子中包含碳-氢或碳-碳共价键的化合物，通常等价于烃，常有碳碳键。</p><p>在<a href="https://wiki.v2beach.cn/Tech/cholesterol.html">去年的 wiki</a> 里有过相关学习。</p><h1 id="分子間作用力"><a href="#分子間作用力" class="headerlink" title="分子間作用力"></a><strong>分子間作用力</strong></h1><p>原子與原子之間的作用力，我們稱之為『化學鍵』(離子鍵、共價鍵、金屬鍵)；而分子與分子間的作用力 (Intermolecular force : IMF; also secondary force)，有『凡得瓦力(van der Waals forces)』、『氫鍵(Hydrogen Bonding)』以及 Ion-dipole forces / Ion-ion forces。</p><h2 id="極性"><a href="#極性" class="headerlink" title="極性"></a><strong>極性</strong></h2><p>極性 (Polarity) 是指<strong>分子內部電荷分布不對稱</strong>，導致分子呈現某種程度的正負極性。所以離子一般都有極性。<br>共價鍵也會有極性，比如水分子 (H₂O)，O 比 H 更具電負性，所以水分子中氧原子帶負電，氫原子帶正電。極性分子因此具有了<strong>偶極 (dipole)</strong>，偶指的是分子內部有兩個電極（正、負）。</p><h2 id="範德華力"><a href="#範德華力" class="headerlink" title="範德華力"></a><strong>範德華力</strong></h2><p>是三種 Dipole-dipole force/interaction 偶極-偶極力。</p><h3 id="相似相容-like-dissolves-like"><a href="#相似相容-like-dissolves-like" class="headerlink" title="相似相容 (like dissolves like)"></a><strong>相似相容 (like dissolves like)</strong></h3><p>溶解的本質是溶劑 (Solvent) 和溶質 (Solute) 之間的作用力大於溶劑分子之間、溶質分子之間的作用力，以結合溶質分子和溶劑分子。</p><p>溶解分為四種情況：</p><ul><li>極性溶劑，極性溶質；<ul><li>極性分子內部有偶極，極性分子之間偶極可以正負互相吸引，即普通的<strong>偶極-偶極力</strong>。</li><li>極性離子跟極性分子會產生 <a href="#離子-偶極力">離子-偶極力</a>。</li></ul></li><li>非極性溶劑，非極性溶質；<ul><li>是另一種範德華力，非極性文子沒有永久偶極，但有<strong>瞬時偶極（伦敦色散力）</strong>。</li></ul></li><li>極性溶劑，非極性溶質；<ul><li>溶解是一个克服能量障碍的过程。</li><li>極性溶液裡，溶劑分子之間的 偶極-偶極力 很強，溶質分子的瞬時偶極跟溶劑分子之間的 伦敦色散力 不足以破壞溶劑分子之間的力。</li></ul></li><li>非極性溶劑，極性溶質，跟極性溶液非極性溶質一樣的道理。</li></ul><p>一般來說溶劑都是 共價鍵 化合物，也有離子溶劑存在，如熔融鹽 (如 NaCl )。</p><h2 id="離子-偶極力"><a href="#離子-偶極力" class="headerlink" title="離子-偶極力"></a><strong>離子-偶極力</strong></h2><p>Ion-dipole Forces</p><p>這是帶電的離子與極性分子之間的作用力。例如，在水溶液中，帶正電的離子（如Na⁺）會與水分子的負極性（氧端）相互作用，形成離子-偶極力。</p><h2 id="離子-離子力"><a href="#離子-離子力" class="headerlink" title="離子-離子力"></a><strong>離子-離子力</strong></h2><p>Ion-ion Forces</p><p>帶電離子之間的 靜電引力（庫倫力）$F = k_e \frac{|q_1 q_2|}{r^2}$，這個分子間作用力跟原子間的離子鍵一樣是因為靜電力粒子間相互吸引。</p><h2 id="氫鍵"><a href="#氫鍵" class="headerlink" title="氫鍵"></a><strong>氫鍵</strong></h2><p>Hydrogen Bonding</p><p>氫鍵是一種特殊的分子間作用力，發生在<strong>氫原子</strong>與某些具有<strong>高電負性的原子（如氟、氧或氮）</strong>之間。氫鍵比一般的凡得瓦力強，並且在水（H₂O）、氟化氫（HF）、氨（NH₃）等分子中起著重要作用。</p><p>例如，在水分子中，氫原子與氧原子之間形成氫鍵，使得水的熔點和沸點比許多其他小分子要高。</p><h1 id="結構和性質對應關係"><a href="#結構和性質對應關係" class="headerlink" title="結構和性質對應關係"></a><strong>結構和性質對應關係</strong></h1><div class="table-container"><table><thead><tr><th>特性</th><th>共價鍵（Covalent Bond）</th><th>離子鍵（Ionic Bond）</th><th>金屬鍵（Metallic Bond）</th></tr></thead><tbody><tr><td><strong>導電性</strong></td><td>通常是非導電的，因為它們不容易離解成自由的離子。</td><td>通常是良好的導電體，當它們溶解在水中或熔化時，能形成自由移動的離子，導電性顯著。</td><td>自由電子可以在金屬內自由移動，使金屬能夠良好導電。</td></tr><tr><td><strong>熔點和沸點</strong></td><td>通常較低，因分子之間的相互作用較弱。</td><td>通常較高，因為強大的靜電吸引力需要較多的能量來克服。</td><td>通常較高，金屬鍵的強度較大，需要較大能量來克服金屬原子間的吸引力。</td></tr><tr><td><strong>溶解性</strong></td><td>分子极性较强或能形成氢键，就能溶于水，否則溶於非極性溶劑。</td><td>通常容易溶解在水等極性溶劑中，形成帶電的離子。</td><td>大多數金屬不易溶解於水或一般溶劑中，但可以在其他金屬中形成合金。</td></tr><tr><td><strong>結構</strong></td><td>通常具有分子結構，分子之間的相互作用較弱（如氫鍵）。</td><td>通常形成晶體結構，由離子組成，離子之間通過靜電吸引力結合。</td><td>具有金屬晶格結構，原子通過金屬鍵保持在一起。</td></tr><tr><td><strong>延展性和可塑性</strong></td><td>通常較脆，容易破裂。</td><td>通常較脆，受外力作用時容易斷裂。</td><td>具有良好的延展性和可塑性，原子能在外力作用下滑動而不破裂。</td></tr><tr><td><strong>光澤</strong></td><td>通常無光澤，部分共價化合物可能呈現不同的顏色。</td><td>通常無光澤，但在某些情況下可能具有光澤（例如鹽晶體）。</td><td>金屬具有光澤，金屬表面能反射光線，產生金屬光澤。</td></tr></tbody></table></div><h2 id="一些性質參數"><a href="#一些性質參數" class="headerlink" title="一些性質參數"></a><strong>一些性質參數</strong></h2><h3 id="LogP（疏水度）"><a href="#LogP（疏水度）" class="headerlink" title="LogP（疏水度）"></a><strong>LogP（疏水度）</strong></h3><p>P 是分子在 辛醇（octanol）和水之间的<strong>分配系数 (Partition</strong> Coefficient)。(octanol-water partition coefficient)</p><p>$P = \frac{[\text{分子在辛醇中的浓度}]}{[\text{分子在水中的浓度}]}$</p><p>$\text{LogP} = \log_{10}(P)$</p><ul><li>LogP &gt; 0 表示親脂，因為在辛醇裡濃度比水中大。</li><li>LogP &lt; 0 表示親水，因為在辛醇裡濃度比水中小。</li><li><p>LogP = 0，親水性 = 疏水性。</p></li><li><p>药物设计：LogP 影响药物的 吸收、分布和渗透性，通常要求 LogP 值适中（例如 0-5）。</p></li><li>Lipinski’s Rule of Five 中，LogP &lt; 5 是成药性的重要规则之一。</li><li>苯（C6H6）：LogP ≈ 2.1（疏水性较强）；乙醇（CH3CH2OH）：LogP ≈ -0.2（亲水性）。</li></ul><h3 id="LogS-親水度"><a href="#LogS-親水度" class="headerlink" title="LogS (親水度)"></a><strong>LogS (親水度)</strong></h3><p>S 是分子在<strong>水中的溶解度 (Solubility)</strong>。</p><p>$S = 分子在水中的饱和溶解度(mol/L)$</p><ul><li>药物开发：良好的溶解性（高 LogS）对药物吸收和生物利用度至关重要。</li><li>乙醇：LogS ≈ 1.6（溶解性高）。</li><li>阿司匹林：LogS ≈ -2.0（溶解性较低，需要助溶策略）。</li></ul><h3 id="MW"><a href="#MW" class="headerlink" title="MW"></a><strong>MW</strong></h3><p>Molecular Weight: 分子质量，单位道尔顿 (dalton, Da) 是碳 12 (质子和中子数都是 6 ) 质量的 $\frac{1}{12}$ 即 $= 1.661×10^{-27} kg$。 </p><p>比如在药物设计里，过大分子可能难以通过细胞膜。</p><h3 id="TPSA"><a href="#TPSA" class="headerlink" title="TPSA"></a><strong>TPSA</strong></h3><p>Topological Polar Surface Area: 分子上所有<a href="#極性">极性原子</a>的表面积，常用于评估分子的透过生物膜的能力。</p><h3 id="Hydrogen-Bond-Donors-and-Acceptors-RO5"><a href="#Hydrogen-Bond-Donors-and-Acceptors-RO5" class="headerlink" title="Hydrogen Bond Donors and Acceptors, RO5"></a><strong>Hydrogen Bond Donors and Acceptors, RO5</strong></h3><p>氢键供体和受体的数量，影响分子的生物活性。</p><p>Lipinski’s rule of five:</p><ul><li>No more than <strong>5 hydrogen bond donors</strong> (the total number of nitrogen–hydrogen and oxygen–hydrogen bonds)</li><li>No more than <strong>10 hydrogen bond acceptors</strong> (all nitrogen or oxygen atoms)</li><li>A molecular mass less than <strong><a href="#mw">500 daltons</a></strong></li><li>A calculated octanol-water partition coefficient <strong>(<a href="#logp疏水度">Clog P</a>) that does not exceed 5</strong></li></ul><p>藥物設計的五個基本規則，rule of five(RO5)，包括上面說到的logP、分子質量、氫鍵供受體。</p><h3 id="導電性"><a href="#導電性" class="headerlink" title="導電性"></a><strong>導電性</strong></h3><ol><li><strong>HOMO-LUMO Gap</strong> (Highest Occupied Molecular Orbital - Lowest Unoccupied Molecular Orbital Gap)：<ul><li>HOMO-LUMO间隙是指分子的最高占据分子轨道（HOMO）与最低空分子轨道（LUMO）之间的能量差。</li><li>homo-lumo gap <strong>越小</strong>，分子中的电子越容易从HOMO跃迁到LUMO，从而激发出电荷载体，这有助于电子的流动，提高导电性。</li><li>金属材料（如金、银）的HOMO-LUMO间隙几乎为零，意味着电子能在材料中自由流动，因此它们具有非常好的导电性。</li><li>有机半导体材料（如有机太阳能电池中的材料）通常具有适中的HOMO-LUMO间隙，使其在特定条件下能够导电。</li><li>绝缘材料（如陶瓷或某些高分子材料）具有较大的HOMO-LUMO间隙，因此它们几乎不导电。</li></ul></li><li><strong>Electron Affinity</strong>：<ul><li>電子親和力表示分子或材料获取一个电子的能力。通常以电子伏特（eV）为单位。</li><li><strong>親和力越高，材料的导电性越好。</strong></li><li>有机半导体材料（如聚合物电池或有机光伏材料）通常具有较高的电子亲和力，允许电子在电场作用下迅速移动，从而提高导电性。</li></ul></li><li><strong>Charge Mobility</strong>：<ul><li>電荷遷移率表示电荷载体（如电子或空穴）在电场作用下的迁移速度。</li><li><strong>迁移率越高，材料的导电性越好。</strong></li></ul></li><li><strong>Polarizability</strong>：<ul><li>极化性是指材料在外电场作用下电子云的变形程度，极化性高的材料能更好地响应外电场。</li><li><strong>高极化性的材料能更好地调节其电子云，增加电子流动性</strong>。</li><li>太陽能電池的有機材料聚苯胺（PANI）和聚吡咯（PPy）有很高極化性。</li></ul></li><li><strong>Ionization Energy</strong>：<ul><li>电离能是将分子中的一个电子从其最低能级移除所需要的能量。</li><li><strong>低电离能的材料更容易释放电子</strong>，釋放電子後材料會表現強導電性。</li><li>电金属如铜和银有較低的电离能。</li></ul></li><li><strong>Electron Density</strong>：<ul><li>分子的<strong>电子密度高，电子就容易在分子内自由移动。</strong></li><li>导电金属如铜和银有非常高的电子密度。</li></ul></li></ol><h3 id="pKa-pKb"><a href="#pKa-pKb" class="headerlink" title="pKa, pKb"></a><strong>pKa, pKb</strong></h3><p>pKa (Acid dissociation constant): 用来描述酸在水溶液中的离解程度，是分子酸性或碱性强弱的一个重要参数。</p><script type="math/tex; mode=display">\text{pKa} = -\log_{10}(\text{Ka})</script><script type="math/tex; mode=display">\text{HA} \rightleftharpoons \text{H}^+ + \text{A}^-</script><script type="math/tex; mode=display">\text{Ka} = \frac{[\text{H}^+][\text{A}^-]}{[\text{HA}]}</script><p>强酸：$pKa$ 值较小，酸容易离解；弱酸：$pKa$ 值较大，酸不容易离解。</p><p>用 pKa 能得到 pKb (Base Dissociation Constant, Kb)。</p><script type="math/tex; mode=display">\text{pKa} + \text{pKb} = 14</script><h3 id="pK"><a href="#pK" class="headerlink" title="pK"></a><strong>pK</strong></h3><p>pK 是亲和力值的负对数，即 pK = -log(亲和力)。</p><h3 id="亲和力"><a href="#亲和力" class="headerlink" title="亲和力"></a><strong>亲和力</strong></h3><p>什么是配体——<br><strong>配体 (ligand)</strong> 可以理解为一种“钥匙”，它能够与蛋白质或其他大分子上的“锁”（即<strong>靶标, target</strong>）相结合。这个“锁”通常是蛋白质表面上的一个特定部位，也叫做<strong>结合位点</strong>。当配体（钥匙）与锁结合时，它<strong>会改变锁的功能或启动某些生物反应。</strong><br>比如<strong>药物分子</strong>就是配体，它们<strong>通过结合到病菌或人体内某些蛋白质上，来调节这些蛋白质的活性，从而达到治疗疾病的效果。</strong>配体不仅仅是药物，也可以是体内的<strong>激素、神经递质</strong>等分子，它们都在调节身体各种功能时发挥着重要作用。</p><div align=center><img src="/pics/molecule/640px-Agonists_v2.png" loading="lazy" alt="红线是对受体亲和性高的配体，绿线是对受体亲和性低的配体" width="50%" height="50%"></div><p>AutoDock 中的“Dock”即所谓 <strong>Molecular Docking 分子对接</strong>是指预测 / 模拟配体小分子如何跟靶标大分子结合，预测结合模式、结合强度以及生物学效果，比如模拟阿司匹林分子在COX酶的结合位点中的各种可能的构象，评估每种构象的结合强度。</p><p>分子之间 / 抗原抗体亲和力<strong>检测方法</strong>有——</p><ul><li>动力学方法<ul><li>表面等离子共振 (SPR), </li><li>生物膜干涉 (BLI), </li></ul></li><li>热力学方法<ul><li>等温滴定量热法 (ITC)</li><li>差示扫描量热仪 (DSC)</li></ul></li><li>饱和浓度法<ul><li>ELISA</li></ul></li></ul><p><strong>指标</strong>有——</p><ul><li>IC50 半抑制浓度(或称 IC₅₀ 为半抑制率，半最大抑制浓度)<ul><li>描述一个化合物抑制目标（如酶或受体）功能的浓度。表示实验中抑制作用达到50%的化合物浓度。</li><li>单位是 μM 或 nM 表示药物的浓度。</li><li>IC50 低于 100 nM 被认为有高亲和力，低于 10 nM 意味着分子对目标亲和力非常强，高于 100 nM 甚至超过 1 μM 表示亲和力弱。</li></ul></li><li>pIC50=-log(IC50)，<strong>p 只有在化学里表示 negative logarithm</strong>，比如 pH = -log₁₀([H⁺] mol/L)。</li><li>EC50</li><li>Ki<ul><li>抑制常数（inhibition constant），反应抑制剂对靶标的抑制强度，越小越强。</li><li>$Ki=\frac{IC50}{1+\frac{[S]}{Km}}$ S是底物浓度；</li><li>$Ki=\frac{IC50}{1+\frac{[A]}{EC50}}$ A是受体激动剂浓度</li></ul></li><li>Kd、KD 一回事<ul><li>解离常数（dissociation constant）</li></ul></li><li>Ka<ul><li>结合常数（association constant）</li><li>Ka=1/Kd</li></ul></li><li>Km<ul><li>米氏常数（Michaelis-Menten constant）</li><li>酶本身的一种特征参数，酶促反应达到最大反应速度一半时底物S的浓度。</li></ul></li><li>Kon<ul><li>结合速率常数（association rate constant）</li><li>代表分子间结合时的快慢，单位为$M^{-1}∙S^{-1}$。</li></ul></li><li>Koff<ul><li>解离速率常数（dissociation rate constant）</li><li>代表分子间解离时的快慢，单位为$S^{-1}$。</li></ul></li></ul><h1 id="分子生成"><a href="#分子生成" class="headerlink" title="分子生成"></a><strong>分子生成</strong></h1><ol><li>Target-Agnostic Generation<ul><li>目标无关生成，類似 vanilla vae 和 gan 都是不加控制的、没有特定目标的生成。</li></ul></li><li>Target-Aware Generation<ul><li>目标相关生成，在训练时把目标信息作为输入，或在奖励函数中引入目标信息，類似 conditional vae/gan，允許用類別標籤、圖像、文本描述控制生成。</li></ul></li><li>Conformation Generation<ul><li>构象生成，考慮分子三維結構（即構象），這對性質也至關重要，构象生成不仅需要生成分子的二维连接图，还需要考虑分子的空间排列（如旋转、弯曲等）。</li></ul></li></ol><h2 id="Data"><a href="#Data" class="headerlink" title="Data"></a><strong>Data</strong></h2><p>雖然說<a href="#物質">物質</a>（元素或化合物）不都是分子，但分子生成的數據是廣義的分子，比如離子化合物和通常以晶體結構存在的純金屬也算。</p><ul><li><a href="#1d2d-datasets">1D 分子表示方法</a>，除了化学计量式 Stoichiometric formula 如水（H₂O）二氧化碳（CO₂）之外有以下几种，<ol><li><a href="#smiles">SMILES</a><ul><li>以及各种变种，比如规范化 canonical SMILES，通过把 SMILES 展开为图 graph 结构，然后字典排序 (Lexicographic Order)，从最小原子以唯一的遍历顺序便利环和分支结构，确保每个分子有唯一的 SMILES 表示。</li></ul></li><li><a href="#inchi">InChI</a></li><li><a href="#morgan-fingerprints">Morgan Fingerprints</a></li></ol></li><li><p>2D，由于数据比较少，直接在这列举，</p><ul><li><p>Pixels (像素)</p><ul><li><p><a href="https://huggingface.co/datasets/ds4sd/USPTO-30K">USPTO-30K</a>，10K 行数据，300M；</p><div align=center><img src="/pics/molecule/US20180250283A1-20180906-C00024.jpg" loading="lazy" alt="" width="33%" height="33%"></div></li><li><p><a href="https://zenodo.org/records/6456306">DECIMER</a>，手绘分子图，5K 张</p><div align=center><img src="/pics/molecule/13321_2022_620_Fig1_HTML.png.webp" loading="lazy" alt="" width="66%" height="66%"></div></li><li><p><a href="https://data.niaid.nih.gov/resources?id=zenodo_5356499">Molecule OCR Real images Dataset</a>，分子截图，300 张</p></li></ul></li></ul></li><li><a href="#3d-datasets">3D datasets</a><ul><li>Voxels (体素，体积元素)</li></ul></li></ul><p>这些表示方法的唯一性和可翻转性见<a href="#appendix-representations-comparison">附录</a>。</p><h3 id="SMILES"><a href="#SMILES" class="headerlink" title="SMILES"></a><strong>SMILES</strong></h3><p>Simplified Molecular Input Line Entry System</p><div class="table-container"><table><thead><tr><th>语法元素</th><th>描述</th><th>示例</th></tr></thead><tbody><tr><td><strong>原子</strong></td><td>元素符号表示原子</td><td><code>C</code> (碳), <code>O</code> (氧), <code>N</code> (氮)</td></tr><tr><td><strong>单键</strong></td><td>默认连接方式，无需显示</td><td><code>C-C</code>（乙烷）</td></tr><tr><td><strong>双键</strong></td><td>用 <code>=</code> 表示</td><td><code>C=C</code>（乙烯）</td></tr><tr><td><strong>三键</strong></td><td>用 <code>#</code> 表示</td><td><code>C#C</code>（乙炔）</td></tr><tr><td><strong>环结构</strong></td><td>使用数字表示环中的连接位置</td><td><code>C1CCCCC1</code>（环己烷）</td></tr><tr><td><strong>分支</strong></td><td>使用括号表示分支结构</td><td><code>CC(C)C</code>（异丙烷）</td></tr><tr><td><strong>芳香环</strong></td><td>小写字母表示芳香性环结构</td><td><code>c1ccccc1</code>（苯）</td></tr><tr><td><strong>电荷</strong></td><td>使用 <code>+</code> 或 <code>-</code> 表示电荷</td><td><code>[Na+]</code>（钠离子）</td></tr><tr><td><strong>氢原子</strong></td><td>默认省略，若显示可通过 <code>H</code> 或 <code>H#</code> 表示</td><td><code>CH4</code>（甲烷）</td></tr><tr><td><strong>立体化学</strong></td><td>用 <code>@</code> 或 <code>@@</code> 表示立体化学配置</td><td><code>C[C@H](O)C</code>（立体异构）</td></tr><tr><td><strong>官能团</strong></td><td>使用合适的符号表示，如羧基 <code>C(=O)O</code></td><td><code>CC(=O)O</code>（乙酸）</td></tr><tr><td><strong>多分子</strong></td><td>用 <code>.</code> 表示多个分子</td><td><code>C1CC1.C1CC1</code>（两个环己烷分子）</td></tr><tr><td><strong>键连接</strong></td><td>用 <code>-</code> 或 <code>=</code> 连接多个分子</td><td><code>C-C</code>（乙烷）</td></tr><tr><td><strong>分子中的其他原子</strong></td><td>省略不常见的原子和其氢原子，如 <code>O</code>、<code>N</code> 可不标明氢</td><td><code>C=O</code>（醛基）</td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>例子</th><th>SMILES</th><th>描述</th></tr></thead><tbody><tr><td><strong>甲烷</strong></td><td><code>CH4</code></td><td>四氢化碳</td></tr><tr><td><strong>乙烯</strong></td><td><code>C=C</code></td><td>两个碳原子用双键连接</td></tr><tr><td><strong>环己烷</strong></td><td><code>C1CCCCC1</code></td><td>一个六碳的环结构</td></tr><tr><td><strong>苯</strong></td><td><code>c1ccccc1</code></td><td>芳香环结构（苯）</td></tr><tr><td><strong>乙酸</strong></td><td><code>CC(=O)O</code></td><td>醋酸</td></tr><tr><td><strong>甲醇</strong></td><td><code>CO</code></td><td>一个碳与氧连结的分子</td></tr><tr><td><strong>氯化钠</strong></td><td><code>[Na+].[Cl-]</code></td><td>两个离子：钠和氯</td></tr><tr><td><strong>异丙烷</strong></td><td><code>CC(C)C</code></td><td>由甲基取代的丙烷</td></tr></tbody></table></div><h3 id="InChI"><a href="#InChI" class="headerlink" title="InChI"></a><strong>InChI</strong></h3><ul><li><a href="https://github.com/IUPAC-InChI/InChI">International Chemical Identifier 生成</a></li><li><a href="https://iupac-inchi.github.io/InChI-Web-Demo/">web demo</a></li></ul><p>比如乙醇 (Ethanol)：<br>InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3</p><div align=center><img src="/pics/molecule/a477cdf40019fca7d2f885b88e040b631fe421e4.svg" loading="lazy" alt="" ></div><ul><li>Main layer (always present)<ol><li><strong>1</strong>：版本號，目前是 version 1；</li><li><strong>S</strong>：standard，如果是 non-standard 就是 InChI=1/C2H6O/c1-2-3/h3H,2H2,1H3 略過 S；</li><li>分子式 (無前綴)<ul><li><strong>C2H6O</strong>：由2个碳原子（C）、6个氢原子（H）和1个氧原子（O）组成；</li></ul></li><li>分子的化学键 (前綴 “c” )<ul><li><strong>c1-2-3</strong>：atom 1 与 atom 2 之间有一个单键，atom 2 与 atom 3 之间也有一个单键；</li><li>再举两个例子，C2H6 乙烷：InChI=1S/C2H6/c1-2/h1-2H3；</li><li>InChI=1S/C2H4/c1-2/h1-2H2 乙烯和 InChI=1S/C2H2/c1-2/h1-2H 乙炔，化学键层都是 c1-2，是通过后面每个 atom 上 H 的数量体现二键 h1-2H2 三键 h1-2H。</li></ul></li><li>氢的排列 (前綴 “h” )<ul><li><strong>h3H,2H2,1H3</strong>：atom 3 (氧) 上有一个 H，atom 2 上有 2 个 H，atom 1 上有 3 个 H。</li><li>再举两个例子，C3H6 环丙烷：InChI=1S/C3H6/c1-2-3-1/h1-3H2，这里的 1-3H2 表示 C1 C2 C3 都有 2 个 H；</li><li>C3H8 丙烷：InChI=1S/C3H8/c1-3-2/h3H2,1-2H3，对比上面 c1-2-3-1 和 h3H2,1-2H3。</li></ul></li></ol></li><li>其他层不会在所有 InChI 里出现，比如电荷层 “q” charge sublayer，质子层 “p” proton sublayer，也都是为了唯一标识一个分子而存在，见下图。</li></ul><div align=center><img src="/pics/molecule/InChIKEY-1024x364.png" loading="lazy" alt="" width="70%" height="70%"></div><div align=center><img src="/pics/molecule/Figure2-1024x478.jpg" loading="lazy" alt="" width="70%" height="70%"></div><h3 id="Morgan-Fingerprints"><a href="#Morgan-Fingerprints" class="headerlink" title="Morgan Fingerprints"></a><strong>Morgan Fingerprints</strong></h3><p>以乙醇为例，</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">  H H</span><br><span class="line">  | |</span><br><span class="line">H—C—C—OH</span><br><span class="line">  | |</span><br><span class="line">  H H</span><br></pre></td></tr></table></figure><p>大致计算步骤是：</p><ol><li>给 C1, C2, O, H1, H2, H3, H4, H5, H6 定义初始哈希值表示类型和邻域。<ul><li>C1 邻域：C2、H1、H2、H3；</li><li>C2 邻域：C1、H4、H5、OH；</li><li>O 邻域：C2、H6。</li></ul></li><li><p>扩展邻域，把更多原子加入原先邻域。</p><div align=center><img src="/pics/molecule/ecfp_iterations.png" loading="lazy" alt="扩展邻域对选定原子的影响"></div></li><li><p>扩展 iterations 中的所有邻域都生成对应哈希值。</p><div align=center><img src="/pics/molecule/ecfp_generation.png" loading="lazy" alt="邻域生成哈希值"></div></li><li><p>对应填充 fingerprint，</p><div align=center><img src="/pics/molecule/ecfp_folding.png" loading="lazy" alt="生成 fixed-length bit string"></div><ul><li>每个哈希值对应一个二进制位，即指纹中的每一位 1 对应存在此邻域，0 对应不存在此邻域；</li><li>fingerprint 是固定长度的二进制码，如 1024 / 2048 等。</li></ul></li></ol><p>实际应用时会用 Morgan 原子邻域拓扑结构指纹算法的其他众多变种：</p><ol><li>ECFP (Extended Connectivity Fingerprints)<ul><li>FCFP (Functional Connectivity Fingerprints)</li></ul></li><li>MACCS Keys</li><li>PubChem Fingerprints<br>…</li></ol><h3 id="1D-2D-datasets"><a href="#1D-2D-datasets" class="headerlink" title="1D/2D datasets"></a><strong>1D/2D datasets</strong></h3><p>分别列举每个数据集的量级、主要构成、一条样本。</p><ol><li><strong>GDB</strong><ul><li><a href="https://gdb.unibe.ch/downloads/">Generated Database</a></li><li>GDB-11 enumerates small organic molecules up to 11 atoms of C, N, O and F following simple chemical stability and synthetic feasibility rules.</li><li>GDB-13 enumerates small organic molecules up to 13 atoms of C, N, O, S and Cl following simple chemical stability and synthetic feasibility rules. 977 468 314 structures</li><li><a href="http://pubs.acs.org/doi/abs/10.1021/ci300415d?ref=gdb.unibe.ch">GDB-17</a> enumerates 166.4 billion molecules of up to 17 atoms of C, N, O, S, and halogens</li><li>GDB-13 和 GDB-17 的前五條數據如下所示：<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">S1C&#x3D;CC&#x3D;C1</span><br><span class="line">O1C&#x3D;CC&#x3D;C1</span><br><span class="line">C1CCCC1</span><br><span class="line">N1C&#x3D;CC&#x3D;C1</span><br><span class="line">S1C&#x3D;CC&#x3D;N1</span><br><span class="line">BrC1&#x3D;C2C3&#x3D;C4C(CC3CCC2&#x3D;O)C(&#x3D;N)NC4&#x3D;N1</span><br><span class="line">BrC1&#x3D;C2C3C4CCC(C4)C3C(&#x3D;N)OC2&#x3D;NC&#x3D;C1</span><br><span class="line">BrC1&#x3D;C2C3C4CCC(O4)C3(OC2&#x3D;NC&#x3D;C1)C#C</span><br><span class="line">BrC1&#x3D;C2C3C4CNC(C4)(C#N)C3OC2&#x3D;NC&#x3D;C1</span><br><span class="line">BrC1&#x3D;C2C3&#x3D;C4C(OC(&#x3D;O)C4&#x3D;CC2&#x3D;O)&#x3D;CC3&#x3D;NO1</span><br></pre></td></tr></table></figure></li></ul></li><li><p><strong>QM9</strong></p><ul><li>全稱 <a href="http://quantum-machine.org/datasets/">Quantum Machine</a>，<a href="https://www.nature.com/articles/sdata201422">paper</a>。</li><li>是 133885 个来自 GDB-17 用简单化学稳定性和易合成规则生成的，最多 9 个 CONF 原子（不包括 H ）构成的有机物数据集。</li><li><p><a href="https://figshare.com/collections/_/978904">數據來源</a>，sgdb9nsd_000001.xyz 第一条数据如下所示：</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">5</span><br><span class="line">gdb 1157.7118157.70997157.706990.13.21-0.38770.11710.504835.36410.044749-40.47893-40.476062-40.475117-40.4985976.469</span><br><span class="line">C-0.0126981359 1.0858041578 0.0080009958-0.535689</span><br><span class="line">H 0.002150416-0.0060313176 0.0019761204 0.133921</span><br><span class="line">H 1.0117308433 1.4637511618 0.0002765748 0.133922</span><br><span class="line">H-0.540815069 1.4475266138-0.8766437152 0.133923</span><br><span class="line">H-0.5238136345 1.4379326443 0.9063972942 0.133923</span><br><span class="line">1341.3071341.32841341.3651562.67311562.74533038.32053151.60343151.67883151.7078</span><br><span class="line">CC</span><br><span class="line">InChI&#x3D;1S&#x2F;CH4&#x2F;h1H4InChI&#x3D;1S&#x2F;CH4&#x2F;h1H4</span><br></pre></td></tr></table></figure><div align=center><img src="/pics/molecule/Screenshot 2024-12-15 at 18.01.33.png" loading="lazy" alt="数据解释" width="50%" height="50%"></div><div align=center><img src="/pics/molecule/Screenshot 2024-12-15 at 18.01.49.png" loading="lazy" alt="第二行解釋" width="50%" height="50%"></div></li><li><p>还有另一种形式，包含能量和<a href="https://github.com/chainer/chainer-chemistry/blob/master/examples/qm9/qm9_dataset_exploration.ipynb">距离矩阵</a> / <a href="https://github.com/bigdata-ustc/QM9nano4USTC">邻接矩阵</a>，在 <a href="https://github.com/chainer/chainer-chemistry/blob/master/examples/qm9/qm9_dataset_exploration.ipynb">chainer 框架</a>里有这个数据集。</p></li></ul></li><li><p><strong>ZINC-20</strong></p><ul><li><p><a href="https://zinc20.docking.org/tranches/home/">鋅-20</a>，通过<a href="#logp疏水度">疏水度 logP</a> 和<a href="#mw">分子质量 MW</a> 划分 Tranches 提供数据下载。</p><div align=center><img src="/pics/molecule/Screenshot 2024-12-16 at 00.38.07.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="/pics/molecule/Screenshot 2024-12-16 at 00.38.47.png" loading="lazy" alt="" width="50%" height="50%"></div></li><li><p>數據支持很多格式，包括 1D SMILES，2D 以 tab 间隔的 txt，3D 的 DOCK37.db2, AutoDock.pdbqt, Mol2.mol2, SDF.sdf。</p></li><li>除了从官网手动下载外，用 curl 或 wget 以 mol2 格式为例 tranches 的下载方式：<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">curl --remote-time --fail --create-dirs -o AA&#x2F;AAML&#x2F;AAAAML.xaa.mol2.gz http:&#x2F;&#x2F;files.docking.org&#x2F;3D&#x2F;AA&#x2F;AAML&#x2F;AAAAML.xaa.mol2.gz</span><br><span class="line">curl --remote-time --fail --create-dirs -o AA&#x2F;AAMM&#x2F;AAAAMM.xaa.mol2.gz http:&#x2F;&#x2F;files.docking.org&#x2F;3D&#x2F;AA&#x2F;AAMM&#x2F;AAAAMM.xaa.mol2.gz</span><br><span class="line">curl --remote-time --fail --create-dirs -o AA&#x2F;AAMN&#x2F;AAAAMN.xaa.mol2.gz http:&#x2F;&#x2F;files.docking.org&#x2F;3D&#x2F;AA&#x2F;AAMN&#x2F;AAAAMN.xaa.mol2.gz</span><br></pre></td></tr></table></figure></li><li>1D 和 2D 是 SMILES + ZINC ID + 一些性质参数，3D 会包含原子坐标、分子间作用力和电性等数据，以 AutoDock 格式为例：<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">MODEL        1</span><br><span class="line">REMARK  Name &#x3D; ZINC000004228267</span><br><span class="line">REMARK                            x       y       z     vdW  Elec       q    Type</span><br><span class="line">REMARK                         _______ _______ _______ _____ _____    ______ ____</span><br><span class="line">ROOT</span><br><span class="line">ATOM      1  N   UNK A  0        2.396  -0.224   1.517  0.00  0.00    -0.740 N </span><br><span class="line">ATOM      2  H   UNK A  0        2.098   0.687   1.372  0.00  0.00    +0.400 HD</span><br><span class="line">ENDROOT</span><br><span class="line">BRANCH   1   3</span><br><span class="line">ATOM      3  C   UNK A  0        3.745  -0.489   1.727  0.00  0.00    +0.230 A</span><br></pre></td></tr></table></figure></li></ul></li><li><strong>ChEMBL</strong><ul><li><a href="https://www.ebi.ac.uk/chembl/">ChEMBL</a> or ChEMBLdb是一个手动整理的化学数据库，包含具有药物诱导性质的生物活性分子。</li><li>chEMBL 中的 EMBL 是 European Molecular Biology Laboratory，跟 ZINC 一样有很多种数据，如 2.5M compounds, 92.1k documents, 37.2k activities。</li><li>可用属性有38个条目，实际在<a href="https://www.ebi.ac.uk/chembl/explore/compounds/">可下载的官网化合物</a>csv里有36条 ↓ ，<a href="#appendix-chembl">表格见附录</a>。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">&quot;ChEMBL ID&quot;;&quot;Name&quot;;&quot;Synonyms&quot;;&quot;Type&quot;;&quot;Max Phase&quot;;&quot;Molecular Weight&quot;;&quot;Targets&quot;;&quot;Bioactivities&quot;;&quot;AlogP&quot;;&quot;Polar Surface Area&quot;;&quot;HBA&quot;;&quot;HBD&quot;;&quot;#RO5 Violations&quot;;&quot;#Rotatable Bonds&quot;;&quot;Passes Ro3&quot;;&quot;QED Weighted&quot;;&quot;CX Acidic pKa&quot;;&quot;CX Basic pKa&quot;;&quot;CX LogP&quot;;&quot;CX LogD&quot;;&quot;Aromatic Rings&quot;;&quot;Structure Type&quot;;&quot;Inorganic Flag&quot;;&quot;Heavy Atoms&quot;;&quot;HBA (Lipinski)&quot;;&quot;HBD (Lipinski)&quot;;&quot;#RO5 Violations (Lipinski)&quot;;&quot;Molecular Weight (Monoisotopic)&quot;;&quot;Np Likeness Score&quot;;&quot;Molecular Species&quot;;&quot;Molecular Formula&quot;;&quot;Smiles&quot;;&quot;Inchi Key&quot;;&quot;Inchi&quot;;&quot;Withdrawn Flag&quot;;&quot;Orphan&quot;</span><br><span class="line"></span><br><span class="line">CHEMBL1807862;&quot;&quot;;&quot;&quot;;&quot;Small molecule&quot;;&quot;&quot;;&quot;279.72&quot;;&quot;1&quot;;&quot;1&quot;;&quot;3.84&quot;;&quot;22.00&quot;;&quot;3&quot;;&quot;0&quot;;&quot;0&quot;;&quot;1&quot;;&quot;N&quot;;&quot;0.66&quot;;&quot;None&quot;;&quot;None&quot;;&quot;3.99&quot;;&quot;3.99&quot;;&quot;3&quot;;&quot;MOL&quot;;&quot;-1&quot;;&quot;18&quot;;&quot;2&quot;;&quot;0&quot;;&quot;0&quot;;&quot;278.9921&quot;;&quot;-1.36&quot;;&quot;None&quot;;&quot;C13H7ClFNOS&quot;;&quot;O&#x3D;c1c2cccc(F)c2sn1-c1ccc(Cl)cc1&quot;;&quot;HENRYDPMUODAEA-UHFFFAOYSA-N&quot;;&quot;InChI&#x3D;1S&#x2F;C13H7ClFNOS&#x2F;c14-8-4-6-9(7-5-8)16-13(17)10-2-1-3-11(15)12(10)18-16&#x2F;h1-7H&quot;;&quot;False&quot;;&quot;-1&quot;</span><br></pre></td></tr></table></figure></li><li>在 <a href="https://www.ebi.ac.uk/chembl/explore/assays/">assays</a> 里有 1.7 M 条分子的很短的 description 可以用于 captioning，比如 <a href="https://www.ebi.ac.uk/chembl/explore/assay/CHEMBL959390">959390</a> 这个分子的描述是，“Cytotoxicity against human CCRF-CEM cells by SRB assay” 根据 SRB 分析此物质对人类 CCRF-CEM 有细胞毒性。</li></ul></li><li><strong>CEPDB</strong><ul><li><a href="https://www.molecularspace.org/explore/">The Clean Energy Project Database</a>, 2.3 million molecular motifs, derived from 150 million density functional theory calculations.</li><li>是 CEP <a href="https://www.matter.toronto.edu/basic-content-page/data-download">Harvard Clean Energy Project</a> 的一部分，2012 和 2013 版共15.14 GB（压缩后，解压的 cepdb_2012-10-24.sql 有 61 GB，建表），geometry 三维坐标数据共 18.54 GB。</li><li>关注能量相关属性，除了 HOMO-LUMO gap 之外，还有 Photovoltaic  performance parameters 光伏表现参数 PCE: the power conversion efficiency Voc: the open-circuit voltage Jsc: the short-circuit current density。<figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Table structure for table `data_calcqcset1`</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> <span class="keyword">IF</span> <span class="keyword">EXISTS</span> <span class="string">`data_calcqcset1`</span>;</span><br><span class="line"><span class="keyword">SET</span> @saved_cs_client     = @@character_set_client;</span><br><span class="line"><span class="keyword">SET</span> character_set_client = utf8;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`data_calcqcset1`</span> (</span><br><span class="line">  <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT,</span><br><span class="line">  <span class="string">`mol_geom_id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`calc_id_str`</span> <span class="built_in">varchar</span>(<span class="number">250</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`calc_tbz_str`</span> <span class="built_in">varchar</span>(<span class="number">250</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`calc_archive_subdir_path`</span> <span class="built_in">varchar</span>(<span class="number">250</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`modelchem_str`</span> <span class="built_in">varchar</span>(<span class="number">100</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_total`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_homo_alpha`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_lumo_alpha`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_gap_alpha`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_homo_beta`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_lumo_beta`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_gap_beta`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`e_gap_min`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`dipmom_total`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`s2_val`</span> <span class="keyword">double</span> <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (<span class="string">`id`</span>),</span><br><span class="line">  <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> <span class="string">`calc_id_str`</span> (<span class="string">`calc_id_str`</span>),</span><br><span class="line">  <span class="keyword">KEY</span> <span class="string">`data_calcqcset1_2174315b`</span> (<span class="string">`mol_geom_id`</span>),</span><br><span class="line">  <span class="keyword">CONSTRAINT</span> <span class="string">`mol_geom_id_refs_id_9f7beeea`</span> <span class="keyword">FOREIGN</span> <span class="keyword">KEY</span> (<span class="string">`mol_geom_id`</span>) <span class="keyword">REFERENCES</span> <span class="string">`data_molgeom`</span> (<span class="string">`id`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> AUTO_INCREMENT=<span class="number">113757770</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8 <span class="keyword">COLLATE</span>=utf8_bin;</span><br><span class="line"><span class="keyword">SET</span> character_set_client = @saved_cs_client;</span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Dumping data for table `data_calcqcset1`</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- ORDER BY:  `id`</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">LOCK</span> <span class="keyword">TABLES</span> <span class="string">`data_calcqcset1`</span> WRITE;</span><br><span class="line"><span class="comment">/*!40000 ALTER TABLE `data_calcqcset1` DISABLE KEYS */</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> <span class="string">`data_calcqcset1`</span> <span class="keyword">VALUES</span> (<span class="number">1</span>,<span class="number">16855</span>,<span class="string">'A.16.C13H8N2S.49.noopt.bp86.sto6g.n.sp'</span>,<span class="string">''</span>,<span class="string">''</span>,<span class="string">'MM//BP86/STO-6G'</span>,<span class="number">-1004.9549135703</span>,<span class="number">-0.101</span>,<span class="number">0.019</span>,<span class="number">0.12</span>,<span class="number">-0.101</span>,<span class="number">0.019</span>,<span class="number">0.12</span>,<span class="number">0.12</span>,<span class="number">3.9718</span>,<span class="literal">NULL</span>),(<span class="number">2</span>,<span class="number">16855</span>,<span class="string">'A.16.C    13H8N2S.49.noopt.bp86.svp.n.sp'</span>,<span class="string">''</span>,<span class="string">''</span>,<span class="string">'MM//BP86/SVP'</span>,<span class="number">-1007.3925734934</span>,<span class="number">-0.189</span>,<span class="number">-0.073</span>,<span class="number">0.116</span>,<span class="number">-0.189</span>,<span class="number">-0.073</span>,<span class="number">0.116</span>,<span class="number">0.116</span>,<span class="number">2.9927</span>,<span class="literal">NULL</span>),(<span class="number">3</span>,<span class="number">15011810</span>,<span class="string">'A.16.C13H8N2S.49.bp86.svp.n.bp86.svp.n.sp'</span>,<span class="string">''</span>,<span class="string">''</span>,<span class="string">'BP86/S    VP//BP86/SVP'</span>,<span class="number">-1007.4106491085</span>,<span class="number">-0.189</span>,<span class="number">-0.076</span>,<span class="number">0.113</span>,<span class="number">-0.189</span>,<span class="number">-0.076</span>,<span class="number">0.113</span>,<span class="number">0.113</span>,<span class="number">3.3101</span>,<span class="literal">NULL</span>), <span class="comment">-- vim 读大文件真好用，1000 行的 id 在 500 万左右，不知总量多少。</span></span><br></pre></td></tr></table></figure></li></ul></li><li><strong>MOSES</strong><ul><li><a href="https://github.com/molecularsets/moses">Molecular Sets</a> 来自 ZINC Clean Leads，分子量范围为 250 到 350 Da，旋转键数量不超过7个，XlogP小于或等于3.5。</li><li>移除了含有带电原子或除了碳（C）、氮（N）、硫（S）、氧（O）、氟（F）、氯（Cl）、溴（Br）、氢（H）之外的原子，或环长度超过8个原子的分子。分子还通过了药物化学筛选（MCFs）和PAINS筛选。</li><li><a href="https://media.githubusercontent.com/media/molecularsets/moses/master/data/dataset_v1.csv">该数据集</a>包含1,936,962个分子结构。为了进行实验，我们将数据集分为训练集、测试集和骨架测试集，分别包含约160万个、17.6万个和17.6万个分子。</li><li>所谓 <strong>Bemis-Murcko 骨架</strong>是去掉官能团等，去除分支，保留环包括芳香环饱和环等，保留连接键作为化学骨架的主链，提取出基础框架；</li><li>所谓<strong>先导分子 (Lead Compound)</strong> 是药物发现中初步筛选后，对目标生物靶标（如酶、受体或其他分子）具有活性并且潜力较高的化学物质，比如阿司匹林 (Aspirin) 的先导分子是天然化合物水杨酸（Salicylic acid）。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">SMILES,SPLIT</span><br><span class="line">CCCS(&#x3D;O)c1ccc2[nH]c(&#x3D;NC(&#x3D;O)OC)[nH]c2c1,train</span><br><span class="line">CC(C)(C)C(&#x3D;O)C(Oc1ccc(Cl)cc1)n1ccnc1,train</span><br><span class="line">CC1C2CCC(C2)C1CN(CCO)C(&#x3D;O)c1ccc(Cl)cc1,test</span><br><span class="line">Cc1c(Cl)cccc1Nc1ncccc1C(&#x3D;O)OCC(O)CO,train</span><br><span class="line">Cn1cnc2c1c(&#x3D;O)n(CC(O)CO)c(&#x3D;O)n2C,train</span><br><span class="line">CC1Oc2ccc(Cl)cc2N(CC(O)CO)C1&#x3D;O,train</span><br><span class="line">O&#x3D;C(C1CCCCC1)N1CC(&#x3D;O)N2CCCc3ccccc3C2C1,test_scaffolds</span><br></pre></td></tr></table></figure></li></ul></li></ol><div align=center><img src="/pics/molecule/pipeline.png" loading="lazy" alt="" width="66%" height="66%"></div><h3 id="DFT"><a href="#DFT" class="headerlink" title="DFT"></a><strong>DFT</strong></h3><p><a href="#atom-vs-molecule">开始</a>提到过，原子结合成分子实际上是电子云重叠，原子轨道相互结合的结果。</p><p>简单说 DFT (密度泛函理论, Density Functional Theory) 生成分子的原理是通过求解电子密度来得到分子的稳定状态，并进一步预测其化学和物理性质。 </p><ul><li>DFT 的目标是找到分子总能量最小化的稳定状态，即找到基态的电子密度，</li><li>“泛函”是一个把电子密度映射到能量上的函数，根据电子密度告诉我们分子总能量。</li></ul><p>大概跟 nn 的优化类似，比如——</p><ul><li>随机初始化一个 input (电子云密度)，</li><li>weights (泛函) 是确定的 Kohn-Sham 方程，这个方程不需要优化参数，只需要迭代，</li><li>目标是通过改变 input 的原子坐标，改变 input 的电子云密度，以最小化 loss (分子能量)。</li></ul><p>泛函就是函数的函数，普通的函数输入是 scalar 输出是 scalar，泛函输入是函数输出是 scalar。比如有一个温度分布 $f(x)$，泛函 $F(f)$ 可能是温度分布的总和、平均值、能量等，或比如通过积分来压缩函数信息 $F(f) = \iint_{D} f(x, y) \, dx \, dy$</p><h3 id="3D-datasets"><a href="#3D-datasets" class="headerlink" title="3D datasets"></a><strong>3D datasets</strong></h3><ol><li><strong>GEOM</strong><ul><li><a href="https://github.com/learningmatter-mit/geom?tab=readme-ov-file">Geometric Ensemble Of Molecules</a>，数据在 <a href="https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/JNGTDF">Harvard Dataverse</a> 里。</li><li>37 M 分子构象，450 K 个分子，用能量和统计量标注，其中 317 K 是理化生实验性数据，133 K 来自 <a href="#1d2d-datasets">QM9</a>。</li><li>SMILES和标注之外有一个 pickle 序列化数据，用 pickle 读取后是跟上面 ZINC 一样的原子坐标数据。</li><li>上面是 summary.json，</li><li>下面是同一个分子的 pickle，包括 39 个构象 conformers。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">&quot;COCC1CCN(C(C)C(&#x3D;O)NC(Cc2cc(F)cc(F)c2)C(O)C[NH2+]Cc2cccc(OC)c2)C1&#x3D;O&quot;: &#123;</span><br><span class="line">    &quot;pickle_path&quot;: &quot;rd_mols&#x2F;AAAGFCUDAXPDAA-WPSXGZRWNA-O.pickle&quot;,</span><br><span class="line">    &quot;datasets&quot;: [</span><br><span class="line">        &quot;bace&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;bace&quot;: &#123;</span><br><span class="line">        &quot;class&quot;: 0.0</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;charge&quot;: 1,</span><br><span class="line">    &quot;temperature&quot;: 298.15,</span><br><span class="line">    &quot;totalconfs&quot;: 34,</span><br><span class="line">    &quot;uniqueconfs&quot;: 34,</span><br><span class="line">    &quot;ensembleGsolv&quot;: 0.0,</span><br><span class="line">    &quot;ensembleenergy&quot;: 0.8293786453390717,</span><br><span class="line">    &quot;ensembleGmRRHO&quot;: 0.5259982,</span><br><span class="line">    &quot;ensemblefreeenergy&quot;: 0.647714789834022,</span><br><span class="line">    &quot;poplowestpct&quot;: 14.64,</span><br><span class="line">    &quot;lowestenergy&quot;: -1790.2116692,</span><br><span class="line">    &quot;lowestfreeenergy&quot;: -1789.6853815,</span><br><span class="line">    &quot;lowestGsolv&quot;: 0.0</span><br><span class="line">&#125;,</span><br><span class="line"></span><br><span class="line">&#123;&#39;conformers&#39;: </span><br><span class="line">[</span><br><span class="line">&#123;&#39;rd_mol&#39;: &lt;rdkit.Chem.rdchem.Mol object at 0x102a21b30&gt;, &#39;geom_id&#39;: 191755154, &#39;ExTB&#39;: -114.5215816, &#39;Gtot&#39;: -1789.685381829991, &#39;homo&#39;: -5.81134502543315, &#39;lumo&#39;: -1.49017707949315, &#39;part&#39;: 2, &#39;Gsolv&#39;: 0.0, &#39;GmRRHO&#39;: 0.5262877, &#39;confnum&#39;: 39, &#39;imgfreq&#39;: 0, &#39;deltaExTB&#39;: 0.14966089649667502, &#39;deltaGtot&#39;: 0.0, &#39;gcpenergy&#39;: 0.050944951, &#39;scfenergy&#39;: -1790.2238525, &#39;multipoles&#39;: [&#123;&#39;X&#39;: 13.320834477108601, &#39;Y&#39;: -5.436236001799401, &#39;Z&#39;: 12.805190419428502&#125;], &#39;totalenergy&#39;: -1790.211669529991, &#39;atomiccharges&#39;: &#123;&#39;chelpg&#39;: [-0.384107, -0.128012, -0.119892, 0.012918, -0.348897, 0.003971, -0.099417, 0.22832, -0.620608, 0.464031, -0.579857, -0.666551, 0.70218, -0.565307, 0.53173, -0.728704, 0.576836, -0.237119, -0.586949, 0.514835, -0.22303, -0.617046, -0.074913, -0.669473, -0.294498, 0.163227, -0.393489, 0.336125, -0.430043, -0.002394, -0.447307, 0.493919, -0.34994, -0.154772, -0.445046, 0.392509, -0.615911, 0.159517, 0.160476, 0.140683, 0.122743, 0.090592, 0.102362, 0.133326, 0.147082, 0.09422, 0.042913, 0.092729, 0.196191, 0.19269, 0.163939, 0.310529, 0.010566, 0.157477, 0.168965, 0.326704, 0.271008, 0.257133, 0.159122, 0.483004, 0.180651, 0.190366, 0.273248, 0.224669, 0.177406, 0.191023, 0.198102, 0.156906, 0.210417, 0.148076, 0.113059, 0.098193, 0.216593], &#39;lowdin&#39;: [-0.079183, -0.816131, 0.038287, -0.116671, -0.224488, 8.8e-05, -0.797974, 0.092744, -0.351816, 0.376512, -0.54649, -0.585718, 0.070386, -0.217474, -0.058601, -0.163355, 0.217519, -0.288981, -0.154311, 0.222021, -0.292465, -0.16142, 0.1198, -0.589261, 0.000245, -0.584898, 0.008839, -0.083127, -0.178494, -0.153136, -0.139364, 0.175758, -0.687728, -0.054253, -0.150924, 0.354333, -0.509984, 0.168522, 0.178438, 0.16578, 0.181807, 0.180614, 0.195278, 0.172587, 0.177462, 0.186995, 0.183336, 0.23472, 0.176017, 0.175266, 0.170432, 0.304486, 0.220894, 0.188031, 0.195249, 0.199422, 0.206502, 0.198593, 0.199265, 0.290755, 0.214907, 0.211322, 0.310346, 0.315253, 0.210142, 0.209117, 0.184257, 0.181314, 0.191326, 0.186433, 0.174786, 0.173853, 0.196207], &#39;mulliken&#39;: [-0.33693, -0.256748, -0.294745, 0.234326, -0.463492, -0.224222, 0.12083, -0.216166, -0.549971, 0.283058, -0.52329, 0.065622, -0.378607, -0.446257, 0.217793, -0.3163, 0.346635, -0.236717, -0.380578, 0.37968, -0.23705, -0.359167, -0.00936, -0.466132, -0.31224, -0.241289, -0.253706, 0.05608, -0.211705, -0.163229, -0.306699, 0.310531, -0.209426, -0.339675, -0.286686, 0.051327, -0.478489, 0.156809, 0.167621, 0.16549, 0.164922, 0.168774, 0.190009, 0.183647, 0.179029, 0.167752, 0.170588, 0.231494, 0.179978, 0.18388, 0.181521, 0.294058, 0.246738, 0.200581, 0.20499, 0.192331, 0.205063, 0.190918, 0.193903, 0.348514, 0.217127, 0.207198, 0.379381, 0.408151, 0.203561, 0.214364, 0.17676, 0.17631, 0.183222, 0.173553, 0.171895, 0.171776, 0.181088]&#125;, &#39;boltzmannweight&#39;: 0.1463940268992422, &#39;dispersionenergy&#39;: -0.038761983, &#39;electricdipolemomentnorm&#39;: 19.260586561982183, &#39;deltaGxTB&#39;: None&#125;, </span><br><span class="line">&#123;&#39;rd_mol&#39;:,&#39;geom_id&#39;:,&#39;ExTB&#39;:,&#39;Gtot&#39;:,&#39;homo&#39;:,&#39;lumo&#39;:,&#39;part&#39;:,&#39;Gsolv&#39;:,&#39;GmRRHO&#39;:,&#39;confnum&#39;:,&#39;imgfreq&#39;:,&#39;deltaExTB&#39;:,&#39;deltaGtot&#39;:,&#39;gcpenergy&#39;:,&#39;scfenergy&#39;:,&#39;multipoles&#39;&#39;: [&#123;&#39;X&#39;:,&#39;Y&#39;:,&#39;Z&#39;:&#125;],&#39;totalenergy&#39;:,&#39;atomiccharges&#39;: &#123;&#39;chelpg&#39;: [], &#39;lowdin&#39;: [], &#39;mulliken&#39;: []&#125;, &#39;boltzmannweight&#39;:,&#39;dispersionenergy&#39;:,&#39;electricdipolemomentnorm&#39;:,&#39;deltaGxTB&#39;:&#125;, </span><br><span class="line">···</span><br><span class="line">], </span><br><span class="line">&#39;smiles&#39;: &#39;COCC1CCN(C(C)C(&#x3D;O)NC(Cc2cc(F)cc(F)c2)C(O)C[NH2+]Cc2cccc(OC)c2)C1&#x3D;O&#39;, &#39;datasets&#39;: [&#39;bace&#39;], &#39;bace&#39;: &#123;&#39;class&#39;: 0.0&#125;, &#39;charge&#39;: 1, &#39;temperature&#39;: 298.15, &#39;totalconfs&#39;: 38, &#39;uniqueconfs&#39;: 38, &#39;ensembleGsolv&#39;: 0.0, &#39;ensembleenergy&#39;: 0.8294144900204856, &#39;ensembleGmRRHO&#39;: 0.5259981729924965, &#39;ensemblefreeenergy&#39;: 0.6477336869578462, &#39;poplowestpct&#39;: 14.63940268992422, &#39;lowestenergy&#39;: -1790.211669529991, &#39;lowestfreeenergy&#39;: -1789.685381829991, &#39;lowestGsolv&#39;: 0.0&#125;</span><br></pre></td></tr></table></figure></li></ul></li><li><strong>ISO17</strong><ul><li>isomers 同分异构体-17 同样来自 QM9 数据集，数据在 <a href="http://quantum-machine.org/datasets/">Quantum Machine 官网</a>，</li><li>是固定原子组合 C7O2H10 不同排列的有效化学结构，有 129 个分子，每个包含 5000 种构象几何 geometries 、能量 energies 、作用力 forces 数据。</li><li>“分子动力学轨迹的时间分辨率为1飞秒。” 数据是 .db 格式需要 SQL 操作，可以用 <code>from ase.db import connect</code> 读写，<code>with connect(path_to_db) as conn:</code>，数据包含 total energy 和 atomic forces 列。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Atoms(symbols&#x3D;&#39;C3OC4OH10&#39;, pbc&#x3D;False, cell&#x3D;[1.0, 1.0, 1.0])</span><br><span class="line">-11506.376711958</span><br><span class="line">[[0.0, 0.0, 0.0], ...]</span><br></pre></td></tr></table></figure></li></ul></li><li><p><strong>Molecule3D</strong></p><ul><li><p>4M 个从 <a href="#dft">density functional theory (DFT)</a> 得到的分子，还提供了<a href="https://github.com/divelab/MoleculeX/tree/molx/Molecule3D">数据预处理、分割、训练和评价代码</a>。数据在 <a href="https://drive.google.com/drive/u/0/folders/1y-EyoDYMvWZwClc2uvXrM4_hQBtM85BI">Google Drive</a>，<a href="https://paperswithcode.com/dataset/molecule3d">paperswithcode</a> 有这个 benchmark，先 3D 结构预测后性质预测。</p><div align=center><img src="/pics/molecule/24e8cbf5-5d1e-4fcf-a17d-1745d2c6ddf9.png" loading="lazy" alt="" width="66%" height="66%"></div></li><li><p>下面 sdf 格式样本里第一行的 PUBCHEM 000000004 表示这个数据来自 <a href="https://pubchem.ncbi.nlm.nih.gov">PubChem</a>，<a href="https://pubchem.ncbi.nlm.nih.gov/compound/4">ID 第 4</a>，B3LYP 是一种 DFT 方法，6-31G(d) 是用于计算分子轨道的函数，GAMESS 是几何优化软件，最后表示用 OpenBabel 转化数据格式；</p></li><li>第二行的 14 13 表示这个分子有 14 个原子，13 条键，V2000 是个可以用 OpenBabel 处理的数据格式；</li><li>之后首先是 14 个原子在当前 Å 尺度 resolution 下的坐标；</li><li>之后是 13 个键的连接关系，  1  2  1 表示原子 1 和 原子 2 中有个 1 键，如果是双键就是 1 2 2；</li><li>最后 M END 表示这个分子结束，和 dollar 结束符。</li><li>sdf 格式数据之外有 properties.csv，列对应关系见下文例子或<a href="#appendix-molecule3d">附录</a>。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUBCHEM 000000004 B3LYP 6-31G(d) gamess geometry optimization</span><br><span class="line"> OpenBabel05211418503D</span><br><span class="line"></span><br><span class="line"> 14 13  0  0  1  0  0  0  0  0999 V2000</span><br><span class="line">    0.8709   -0.0593   -0.0932 C   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.3923   -0.0939    0.0366 C   0  0  1  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.8675   -0.1496    1.4905 C   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    4.3253   -0.0567    1.5320 N   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.9524   -1.1865   -0.7083 O   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    0.4511    0.8328    0.3857 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    0.4145   -0.9383    0.3830 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    0.5813   -0.0547   -1.1486 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.8285    0.7976   -0.4266 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.4511   -1.0709    1.9489 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.4412    0.6964    2.0457 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    4.7035   -0.7783    0.9198 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    4.6675   -0.2503    2.4708 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">    2.5111   -1.9958   -0.4005 H   0  0  0  0  0  0  0  0  0  0  0  0</span><br><span class="line">  1  2  1  0  0  0  0</span><br><span class="line">  1  7  1  0  0  0  0</span><br><span class="line">  1  6  1  0  0  0  0</span><br><span class="line">  2  9  1  6  0  0  0</span><br><span class="line">  2  3  1  0  0  0  0</span><br><span class="line">  3  4  1  0  0  0  0</span><br><span class="line">  3 10  1  0  0  0  0</span><br><span class="line">  3 11  1  0  0  0  0</span><br><span class="line">  4 13  1  0  0  0  0</span><br><span class="line">  5 14  1  0  0  0  0</span><br><span class="line">  5  2  1  0  0  0  0</span><br><span class="line">  8  1  1  0  0  0  0</span><br><span class="line"> 12  4  1  0  0  0  0</span><br><span class="line">M  END</span><br><span class="line">$$$$</span><br><span class="line"></span><br><span class="line">cid,dipole x,dipole y,dipole z,homo,lumo,homolumogap,scf energy</span><br><span class="line">000000000,4.404345,3.934717,-1.135217,-6.17698440635,-2.097997787355,4.078986618995,-20690.251813900697</span><br><span class="line">000000004,-1.452589,-1.389503,1.434038,-6.1878689603699994,1.8476530448950002,8.035522005265,-6794.53585995115</span><br></pre></td></tr></table></figure></li></ul></li><li><strong>CrossDocked2020</strong><ul><li>数据也在 <a href="https://github.com/gnina/models/tree/master/data/CrossDocked2020">GitHub</a> 可以获取，90 G 左右。</li><li>label = 1 对应 <a href="#rmsd">RMSD（根均方偏差, 见下文 metrics）</a>和晶体构象的偏差 ≤ 2， label = 0 是 RMSD &gt; 2；</li><li>接下来的性质参数是 <a href="#pk">pK 值</a>，若<a href="#亲和力">配体亲和力数据</a>未知，则<a href="#pk">pK 值</a>为 0；</li><li>receptor 和 ligand 就是<a href="#pk">蛋白质受体和配体分子</a>，对应一条数据；</li><li><a href="#rmsd">RMSD to crystal</a> 列在 DenseNet 里要移除掉。<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">&lt;label&gt; &lt;pK&gt; &lt;RMSD to crystal&gt; &lt;Receptor&gt; &lt;Ligand&gt; # &lt;Autodock Vina score&gt;</span><br><span class="line"></span><br><span class="line">1 0.0000 1.0247 1A02_HUMAN_25_199_pep_0&#x2F;2gj6_D_rec_0.gninatypes 1A02_HUMAN_25_199_pep_0&#x2F;2gj6_D_rec_2gj6_3ib_lig_tt_min_0.gninatypes #-3.5844</span><br></pre></td></tr></table></figure></li></ul></li><li><strong>PDB (scPDB, Binding MOAD)</strong><ul><li><a href="https://www.rcsb.org/docs/general-help/organization-of-3d-structures-in-the-protein-data-bank">Protein Data Bank</a>，<a href="https://www.rcsb.org/pages/about-us/index">官网</a>说是生物学和医学领域的第一个</li><li>PDB 有超过1TB的蛋白质、DNA和RNA的结构数据，且每年增长接近10%。</li><li>类似 NCBI 的 GenBank，这些数据集包罗万象，任何格式任何形式的几乎任何蛋白质和基因数据都能找到。</li></ul></li><li><strong>DUD-E</strong><ul><li>DUD (A Directory of Useful Decoys)，decoys 是诱饵，跟真正具有好性质的目标化合物很像，但并不具有某些特性，DUD 2,950 种活性化合物，针对 40 个靶标，每个活性化合物有 36 个“诱饵”，它们的 <a href="#mw">MW</a> 和 <a href="#logp疏水度">logP</a> 相似，分子结构不同。</li><li>DUD-E 是 DUD 的 Enhanced 和重建版本<ul><li>有 22,886 个活性化合物，和它们<a href="#亲和力">亲和</a>的 102 个靶标，平均每个目标靶标有 224 个<a href="#亲和力">配体 (ligands)</a>；</li><li>活性化合物每个有 50 个诱饵 decoys，有相似的物理化学属性但是不同的 2-D topology 结构。</li></ul></li><li><a href="https://dude.docking.org/targets">DUD-E 把活性化合物数据按 102 个靶标分开</a>，比如 AA2AR 这个靶标 (target)，<ul><li>首先对应<a href="https://www.rcsb.org/structure/3eml">一个 PDB 条目</a> 和对应物质量等；</li><li>然后对应几个活性化合物的数据文件，ism 里包括 SMILES 和 ChEMBL id 如下所示，以及 mol2 / sdf 格式的 3D 坐标数据；</li><li>最后有一个 receptor.pdb 文件描述靶标 (target) 分子原子构成。</li></ul></li></ul></li></ol><div class="table-container"><table><thead><tr><th>#</th><th>Target Name</th><th>PDB</th><th>Description</th><th>Method</th><th>Substances</th><th>Clustered</th></tr></thead><tbody><tr><td>1</td><td>AA2AR</td><td><a href="https://www.rcsb.org/structure/3eml">3eml</a></td><td>Adenosine A2a receptor</td><td>By Hand</td><td>3057</td><td>482</td></tr><tr><td>2</td><td>ABL1</td><td>2hzi</td><td>Tyrosine-protein kinase ABL</td><td>Auto</td><td>409</td><td>182</td></tr></tbody></table></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">AA2AR actives_final.ism: (对应有一个decoys_final.ism存诱饵分子)</span><br><span class="line">C#CCOc3nc(c1ccccc1)nc4sc2CCCc2c34 630004 CHEMBL1087078</span><br><span class="line">OC[C@H]3OC(n1cnc2c(NCCS(O)(&#x3D;O)&#x3D;O)ncnc12)[C@H](O)[C@@H]3O 607385 CHEMBL610435</span><br><span class="line">CCNC(&#x3D;O)[C@H]4OC(n2cnc3c(N)nc(C#Cc1ccc(CCC(O)&#x3D;O)cc1)nc23)[C@H](O)[C@@H]4O 605505 CHEMBL605065</span><br><span class="line">···</span><br><span class="line"></span><br><span class="line">AA2AR receptor.pdb:</span><br><span class="line">ATOM      1  N   ILE     3     -30.582 -20.763  57.829</span><br><span class="line">ATOM      2  CA  ILE     3     -29.314 -20.499  57.159</span><br><span class="line">ATOM      3  C   ILE     3     -28.389 -19.651  58.027</span><br><span class="line">···</span><br></pre></td></tr></table></figure><div align=center><img src="/pics/molecule/Screenshot 2024-12-16 at 18.07.39.png" loading="lazy" alt="dude.docking.org" width="50%" height="50%"></div><h2 id="Metrics"><a href="#Metrics" class="headerlink" title="Metrics"></a><strong>Metrics</strong></h2><ol><li>这些既可以作为模型的评价指标，也可以作为 reward function 为模型引入 reinforcement learning 思想，作为 guide gradient。<ul><li>比如一种常用的 reward function：<script type="math/tex; mode=display">J(S) = \log P(S) - SA(S) - \text{RingPenalty}(S)</script></li><li>加上penalty是：<script type="math/tex; mode=display">R(S) = \begin{cases} J(S) \cdot \frac{1}{1 + |J(S)|} & \text{for valid SMILES} \\-1.0 & \text{for invalid SMILES} \end{cases}</script></li></ul></li><li>分子的定量性质、特征，还会被称为分子描述符 (Descriptors)，比如 <a href="#synthesizability-sascore">RDKit 的 SAScore</a> 函数名就是 rdMolDescriptors。</li></ol><h3 id="Stability"><a href="#Stability" class="headerlink" title="Stability"></a><strong>Stability</strong></h3><ul><li><p>Atom Stability（原子稳定性）<br><strong>定义</strong>：原子稳定性原子是否跟其他原子形成稳定的化学键，避免连接到不稳定的键上。  </p></li><li><p>Molecule Stability（分子<strong>稳定性</strong>）<br><strong>定义</strong>：分子稳定性衡量整个分子在环境中的化学稳定性，评估分子是否容易分解、氧化或发生其他不希望的化学反应，从而确保分子在目标环境中能够长时间存在，尤其是药物分子需要在体内保持活性。  </p></li></ul><p>有人用 spearman’s ρ 衡量 stability。<br>$\rho = 1 - \frac{6 \sum_{i=1}^{n} d_i^2}{n(n^2 - 1)}$<br>$ d_i $ 是每对预测标签和真实标签之间的排名差异，$n$ 是样本数量，</p><ul><li>$\rho$：Spearman’s ρ，表示排名相关系数，取值范围从 -1 到 1。<ul><li>$\rho = 1$ 表示完全正相关（排名完全一致）。</li><li>$\rho = -1$ 表示完全负相关（排名完全相反）。</li><li>$\rho = 0$ 表示无相关（排名无序）。</li></ul></li></ul><p><a href="#references">reference 2</a> 的 5.4 3D Molecule Evaluation 这句话是错的：<br>当一个原子与其他原子连接的键数与该原子的价电子数相匹配时，该原子是稳定的；当分子中所有原子都稳定时，整个分子也是稳定的。<br>可以参照下面 Validity 计算 Stability，我认为这两个指标可以一起算，就算原子化学键跟原子价匹配也未必稳定。</p><h3 id="Validity"><a href="#Validity" class="headerlink" title="Validity"></a><strong>Validity</strong></h3><p><strong>有效性</strong>:</p><ul><li>基本的化学结构规则，寻找不合理的键；<ul><li>C=C=C (C₃H₄)，虽然不违反化合价规则，但只能是不稳定的短暂中间体；</li><li>C-C≡N 也是。</li></ul></li><li>每个原子的键数是否符合其化学价 / 化合价 valence。</li></ul><p>$\text{Validity} = \frac{\text{ # valid molecules}}{\text{ # generated molecules}}$</p><p>可以用 rdkit.Chem.Descriptors / rdMolDescriptors 计算。</p><h3 id="Uniqueness"><a href="#Uniqueness" class="headerlink" title="Uniqueness"></a><strong>Uniqueness</strong></h3><p><strong>唯一性</strong>：希望生成的分子少重复。</p><p>$\text{Uniqueness} = \frac{\text{ # unique molecules}}{\text{ # generated molecules}}$</p><script type="math/tex; mode=display">R_{\text{uniqueness}} = \frac{| \text{set}(G) |}{| G |}</script><ul><li>$R_{\text{uniqueness}}$ 表示分子生成集合 $G$ 中的唯一性评分。</li><li>$| \text{set}(G) |$ 是集合 $G$ 中唯一分子的数量（去除重复分子）。</li><li>$| G |$ 是生成的分子总数。  </li></ul><h3 id="Novelty"><a href="#Novelty" class="headerlink" title="Novelty"></a><strong>Novelty</strong></h3><p><strong>新颖性</strong>：希望生成训练集里不存在的分子。</p><p>$\text{Novelty} = \frac{\text{ # novel molecules}}{\text{ # generated molecules}}$</p><script type="math/tex; mode=display">R_{\text{novelty}} = \begin{cases} 1 & \text{if } x \notin \text{Training Set} \\0.3 & \text{if } x \in \text{Training Set}\end{cases}</script><ul><li>$R_{\text{novelty}}$ 是新颖性评分。</li><li>$x$ 表示生成的分子。</li><li>如果生成的分子 $x$ 不在训练集 $\text{Training Set}$ 中，则赋值为 1，表示生成了一个全新的分子。</li><li>如果 $x$ 在训练集中，则赋值为 0.3，表示该分子并不新颖，可能是已知的分子。  </li></ul><p>或者用 $r_{\text{novel}} = 1 - \frac{|G \cap T|}{|T|}$ ，生成的新分子也不在 T 测试集里的比例，衡量 novelty。</p><h3 id="QED"><a href="#QED" class="headerlink" title="QED"></a><strong>QED</strong></h3><p>Quantitative Estimation of Drug-likeness, <strong>药物相似性定量估计</strong>，表示其作为药物候选分子的潜力。</p><p>Bickerton et al., 2012 的计算步骤如下：</p><ol><li>数据集<ul><li>SMILES 或 InChI 格式药物分子化学结构；</li><li>生物活性，通常用 <a href="#亲和力">IC50 或 EC50</a>；</li></ul></li><li>用 RDKit 或 Open Babel 计算<a href="#metrics">分子描述符</a><ul><li>芳香环数（Aromatic Rings）：表示分子中芳香环的数量。芳香环的数量可能与分子的稳定性和相互作用特性相关。</li><li>重原子数（Heavy Atoms）：通常指除了氢以外的元素，反映了分子的复杂性。</li><li>环系数（Ring Count）：分子中环结构的数量。环结构增加了分子的稳定性和生物活性。</li><li><a href="#tpsa">PSA</a></li><li><a href="#hydrogen-bond-donors-and-acceptors-ro5">Lipinski’s Rule of Five</a></li></ul></li><li>模型训练交叉验证<ul><li>用传统模型，比如 Linear Regression / SVM, SVR / Random Forest</li><li>loss, 模型评估用 <a href="#RMSD">MSE</a>，决定系数（R²）</li></ul></li></ol><p><strong>R² 决定系数</strong> (R-squared, R2) ，是衡量回归模型拟合效果的一个指标，0～1 越大越好。</p><script type="math/tex; mode=display">R² = 1 - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{n} (y_i - \bar{y})^2}</script><h3 id="AutoDock-Vina"><a href="#AutoDock-Vina" class="headerlink" title="AutoDock Vina"></a><strong>AutoDock Vina</strong></h3><p><strong>定义</strong>：AutoDock Vina是分子对接工具，用于评估分子与靶标蛋白质之间的结合亲和力，优化分子构象与受体结合能，以帮助选择结合能力最强的分子。<br><strong>公式</strong>：通过评估结合模式的能量（静电、范德瓦尔斯力、疏水效应、分子旋转自由度等）预测分子之间的结合亲和力：</p><script type="math/tex; mode=display">E_{\text{affinity}} = E_{\text{vdW}} + E_{\text{elec}} + E_{\text{hydrophobic}} + E_{\text{torsion}}</script><ul><li>$E_{\text{affinity}}$ 是分子与靶标蛋白质结合的总亲和能量，越低表示分子之间亲和力越强。</li><li>$E_{\text{vdW}}$ 是范德瓦尔斯力，反映分子之间的非键合相互作用。</li><li>$E_{\text{elec}}$ 是静电力，表示带电分子之间的相互作用。</li><li>$E_{\text{hydrophobic}}$ 是疏水效应，考虑分子中的疏水基团与水分子之间的相互作用。</li><li>$E_{\text{torsion}}$ 是分子旋转自由度的能量，反映分子旋转自由度对结合的影响。  </li></ul><h3 id="High-Affinity-Percentage"><a href="#High-Affinity-Percentage" class="headerlink" title="High Affinity Percentage"></a><strong>High Affinity Percentage</strong></h3><p><strong>定义</strong>：高亲和力百分比衡量生成的分子中具有较高结合亲和力（低结合能）分子所占的比例，用于评估生成分子是否具有良好的药物结合能力。<br><strong>公式</strong>：</p><script type="math/tex; mode=display">\text{High Affinity Percentage} = \frac{\text{Number of high affinity molecules}}{\text{Total number of generated molecules}} \times 100</script><ul><li>$\text{Number of high affinity molecules}$ 是具有高亲和力（低结合能）的分子数量。比如用 <a href="#亲和力">IC50</a> 的阈值可以设为 &lt; 100 nM 为高亲和力，用 <a href="#autodock-vina">AutoDock Vina</a> 的阈值可以设为 &lt; -7 kcal/mol 结合能是高亲和力。</li><li>$\text{Total number of generated molecules}$ 是生成的分子总数。  </li></ul><h3 id="Synthesizability-SAScore"><a href="#Synthesizability-SAScore" class="headerlink" title="Synthesizability, SAScore"></a><strong>Synthesizability, SAScore</strong></h3><p><strong>定义</strong>：<strong>Synthetic Accessibility Score</strong> 评估分子的合成难易度，计算多个结构特征（如键类型、环系统等）来量化分子在实际合成过程中可能遇到的难度。  </p><p>在 RDKit 中，Synthetic Accessibility score（简称 SA score）是通过特定算法评估分子合成难度的。该评分系统由 <a href="https://jcheminf.biomedcentral.com/articles/10.1186/1758-2946-1-8">Ertl et al. (2009)</a> 提出，</p><p>原始文章：</p><script type="math/tex; mode=display">SAScore = fragmentScore - ComplexityPenalty</script><script type="math/tex; mode=display">ringComplexityScore = log(nRingBridgeAtoms + 1) + log(nSpiroAtoms + 1)</script><script type="math/tex; mode=display">stereoCom plexityScore = log(nStereoCenters + 1)</script><script type="math/tex; mode=display">macrocyclePenalty = log(nMacrocycles + 1)</script><script type="math/tex; mode=display">sizePenalty = natoms**1.005 - natoms</script><ul><li>$ nRingBridgeAtoms $: 环桥原子数量。</li><li>$ nSpiroAtoms $: 螺环原子数量。</li><li>$ nStereoCenters $: 立体中心的数量。</li><li>$ nMacrocycles $: 宏环数量。</li><li>$ natoms $: 分子中原子的总数。</li></ul><p>SAScore 结果通常在 -4（最差）到 2.5（最佳）之间，被乘 -1 (越高越难以合成) 后缩放至1到10之间，以提供一个更易于解释的值。</p><p>现在 RDKit 是通过 nn 实现 fragmentScore：</p><script type="math/tex; mode=display">SAScore_{fragmentScore} = w_1 \times f_1 + w_2 \times f_2 + \cdots + w_n \times f_n</script><p>其中：</p><ul><li>$f_1, f_2, \dots, f_n$ 是描述分子结构的特征（如分子量、旋转自由度、拓扑指数等），这些特征与分子活性有一定相关性。</li><li>$w_1, w_2, \dots, w_n$ 是对应特征的权重，通常通过回归分析或者机器学习方法来确定。</li></ul><p>除了会考虑环结构和官能团、拓扑结构这些 penalty 之外，实现时还可能参考合成路径数据库 (Synthetic Route Database) 检查是否有已知的合成方法。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> rdkit <span class="keyword">import</span> Chem</span><br><span class="line"><span class="keyword">from</span> rdkit.Chem <span class="keyword">import</span> rdMolDescriptors</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建一个分子对象</span></span><br><span class="line">smiles = <span class="string">"CCO"</span>  <span class="comment"># 例如乙醇</span></span><br><span class="line">mol = Chem.MolFromSmiles(smiles)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算 SA score</span></span><br><span class="line">sa_score = rdMolDescriptors.CalcSyntheticAccessibility(mol)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"Synthetic Accessibility Score: <span class="subst">&#123;sa_score&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><h3 id="Diversity"><a href="#Diversity" class="headerlink" title="Diversity"></a><strong>Diversity</strong></h3><p>基本思路是用数据的两两相似度衡量多样性，相似度均值越低多样性越高，文章中有两种大差不差的算法，但 <a href="#references">reference</a> 1 的 3.2.1 Diversity &amp; novelty Eqn. 26 公式都写错了，所以取 reference 2 equation 16 的写法。</p><script type="math/tex; mode=display">\text{Int} = 1 - \frac{1}{|Z_{\text{gen}}|(|Z_{\text{gen}}| - 1)} \sum_{Z_1, Z_2 \in Z_{\text{gen}}, Z_1 \neq Z_2} \text{sim}(Z_1, Z_2)</script><script type="math/tex; mode=display">\text{Ext} = 1 - \frac{1}{|Z_{\text{gen}}| |Z_{\text{train}}|} \sum_{Z_1, Z_2 \in Z_{\text{gen}}, Z_2 \in Z_{\text{train}}} \text{sim}(Z_1, Z_2)</script><p>如果多样性过高，可以通过在 reward 里增加 penalty 限制。</p><script type="math/tex; mode=display">R_{\text{diversity mismatch}} = - | r_{\text{generated diversity}} - r_{\text{training diversity}} |</script><h3 id="Coverage"><a href="#Coverage" class="headerlink" title="Coverage"></a><strong>Coverage</strong></h3><script type="math/tex; mode=display">COV(Sg, Sr) = \frac{1}{|Sr|} \left| \{ R \in Sr \mid \text{RMSD}(R, \hat{R}) < \delta, \hat{R} \in Sg \} \right|</script><p>其中，$S_g$ 和 $S_r$ 分别表示生成的和参考的构象，$delta$ 是给定的RMSD阈值。</p><h3 id="Matching"><a href="#Matching" class="headerlink" title="Matching"></a><strong>Matching</strong></h3><p><strong>定义</strong>：匹配度度量生成的分子与目标分子或参考数据库中分子之间的相似性，通过计算分子指纹的相似度来评估。<br><strong>公式</strong>：</p><script type="math/tex; mode=display">r_{\text{SNN}} = \frac{1}{| G |} \sum_{x_G \in G} \max_{x_D \in D} D(x_G, x_D)</script><ul><li>$r_{\text{SNN}}$ 是匹配度评分。</li><li>$G$ 是生成的分子集，$D$ 是参考数据库或目标分子集。</li><li>$D(x_G, x_D)$ 是分子 $x_G$ 与 $x_D$ 之间的相似度。  </li></ul><script type="math/tex; mode=display">MAT(Sg, Sr) = \frac{1}{|Sr|} \sum_{R \in Sr} \min_{\hat{R} \in Sg} \text{RMSD}(R, \hat{R})</script><p>其中，$S_g$ 和 $S_r$ 分别表示生成的和参考的构象。</p><p>1D2D ↓</p><h3 id="KL散度"><a href="#KL散度" class="headerlink" title="KL散度"></a><strong>KL散度</strong></h3><p>Kullback–Leibler Divergence (KLD)，</p><script type="math/tex; mode=display">D_{\text{KL}}(P \parallel Q) = \sum_{i} P(i) \log \frac{Q(i)}{P(i)}</script><p>KL散度是非对称的，</p><script type="math/tex; mode=display">D_{\text{KL}}(P \parallel Q) \neq D_{\text{KL}}(Q \parallel P)</script><p>举个简单例子，$P=[0.4,0.6], Q=[0.5,0.5]$：</p><script type="math/tex; mode=display">\begin{aligned}D_{\text{KL}}(P \parallel Q) &= P(1) \log \frac{Q(1)}{P(1)} + P(2) \log \frac{Q(2)}{P(2)} \\&= 0.4 \log \frac{0.5}{0.4} + 0.6 \log \frac{0.5}{0.6} \\&= 0.4 \log 0.8 + 0.6 \log 1.2 \\&\approx 0.4 \times (-0.09691) + 0.6 \times 0.18232 \\&\approx 0.070628\end{aligned}</script><h3 id="MMD"><a href="#MMD" class="headerlink" title="MMD"></a><strong>MMD</strong></h3><p>Maximum Mean Discrepancy (MMD, 最大均值差异),<br>假设有两个样本集 $X = \{x_1, x_2, \dots, x_n\}$ 和 $Y = \{y_1, y_2, \dots, y_m\}$，分别来自两个分布 $P$ 和 $Q$。</p><p>MMD的计算公式为：</p><script type="math/tex; mode=display">\text{MMD}^2(P, Q) = \mathbb{E}_{x, x' \sim P}[k(x, x')] + \mathbb{E}_{y, y' \sim Q}[k(y, y')] - 2 \mathbb{E}_{x \sim P, y \sim Q}[k(x, y)]</script><p>其中，$k(x, y)$ 是核函数，通常选择高斯核（RBF核）来度量数据点之间的相似度。核函数的选择影响了MMD的表现和计算效率。<br>MMD依赖于一个核函数 $k(x, y)$ 来计算样本点间的相似性。常用的核函数包括：</p><ul><li><strong>高斯核</strong>：<script type="math/tex; mode=display">k(x, y) = \exp\left(-\frac{\|x - y\|^2}{2\sigma^2}\right)</script></li><li><strong>线性核</strong>：<script type="math/tex; mode=display">k(x, y) = x^T y</script>用样本相似度衡量两个分布相似度的方式跟 KL 散度有些类似，但观察公式可以发现 MMD 不需要显式计算分布。<br>由于 <a href="#kl散度">KL散度</a> 计算的例子是离散分布，所以这里举一个连续正态分布的例子说明这一点：</li><li><strong>分布P</strong>：一个标准正态分布 $ P \sim \mathcal{N}(0, 1) $。</li><li><strong>分布Q</strong>：另一个正态分布 $ Q \sim \mathcal{N}(1, 1) $。</li></ul><p>两个分布的差异是均值不同，一个均值为0，一个均值为1。</p><p>对于一维正态分布 $ P \sim \mathcal{N}(0, 1) $ 和 $ Q \sim \mathcal{N}(1, 1) $，我们有：</p><ul><li>$ p(x) = \frac{1}{\sqrt{2\pi}} \exp\left(-\frac{x^2}{2}\right) $</li><li>$ q(x) = \frac{1}{\sqrt{2\pi}} \exp\left(-\frac{(x-1)^2}{2}\right) $</li></ul><p>KL 散度公式：</p><script type="math/tex; mode=display">D_{\text{KL}}(P \parallel Q) = \int_{-\infty}^{\infty} \frac{1}{\sqrt{2\pi}} \exp\left(-\frac{x^2}{2}\right) \log \left( \frac{\frac{1}{\sqrt{2\pi}} \exp\left(-\frac{x^2}{2}\right)}{\frac{1}{\sqrt{2\pi}} \exp\left(-\frac{(x-1)^2}{2}\right)} \right) dx</script><p>需要确切知道 $ p(x) $ 和 $ q(x) $ 的 PDF 概率密度函数。而用 MMD 只需要从 $ P $ 和 $ Q $ 中各自抽取样本集合 $ X = \{x_1, x_2, …, x_n\} $ 和 $ Y = \{y_1, y_2, …, y_m\} $ 代入 MMD 公式：</p><script type="math/tex; mode=display">\text{MMD}^2(P, Q) = \frac{1}{n^2} \sum_{i,j} k(x_i, x_j) + \frac{1}{m^2} \sum_{i,j} k(y_i, y_j) - \frac{2}{nm} \sum_{i,j} k(x_i, y_j)</script><p>代入核函数：</p><script type="math/tex; mode=display">k(x, y) = \exp\left(-\frac{(x - y)^2}{2\sigma^2}\right)</script><p>并不需要显式得到分布 PDF。</p><h3 id="FCD"><a href="#FCD" class="headerlink" title="FCD"></a><strong>FCD</strong></h3><p>Fréchet 距离本身是计算两个路径 / 曲线的相似度，比如两个人分别在两个不同的城市出发，各自沿着某条路线行进，目标是同时到达各自的目的地，Fréchet 距离就是两个路径的“最小同时距离”，也就是“最小的最大距离”，可以想像路径越相似 Fréchet 距离值越小。</p><p>而 <strong>Fréchet ChemNet Distance (FCD)</strong> 可以衡量两个分布的距离，通常就是两个分子集合，Fréchet 距离例子里的“路径”对应分子的特征，FCD 的分子特征是 ChemNet 倒数第二层的激活值，进而计算均值 $\mu$ 和协方差 $\Sigma$ 来衡量距离。</p><script type="math/tex; mode=display">D_{\text{FCD}} = \|\mu_1 - \mu_2\|_2^2 + \text{Tr}(\Sigma_1 + \Sigma_2 - 2(\Sigma_1^{1/2} \Sigma_2 \Sigma_1^{1/2})^{1/2})</script><ul><li>$\mu_1, \mu_2$ 是两个分布的均值向量。</li><li>$\Sigma_1, \Sigma_2$ 是这两个分布的协方差矩阵。</li><li>Tr表示矩阵的迹（即矩阵对角线元素的和）。</li></ul><p>其中协方差公式：</p><script type="math/tex; mode=display">\text{Cov}(X, Y) = \frac{1}{n} \sum_{i=1}^{n} (X_i - \mu_X)(Y_i - \mu_Y)</script><p>3D ↓</p><h3 id="RMSD"><a href="#RMSD" class="headerlink" title="RMSD"></a><strong>RMSD</strong></h3><p>Root Mean Square Deviation，根均方偏差是一种衡量两个分子结构（通常是晶体构象和预测构象）之间差异的常用指标。它通过计算两组原子坐标之间的差异来量化结构的相似性。<br>RMSD 的值越小，表示两个结构越相似。</p><ol><li>对齐两组结构：首先，需要对齐（superimpose）两个结构，以消除旋转和平移的影响，通常使用最小二乘法（least squares）来对齐。</li><li>计算坐标差异：对于每对对应的原子，计算它们在三维空间中的位置差异（通常使用欧几里得距离）。</li><li>求平方和：将每对原子之间的差异平方后加和。</li><li>求平均值：取所有差异平方和的平均值。</li><li>开平方：最后，取平均值的平方根，得到 RMSD。</li></ol><p>公式如下：</p><script type="math/tex; mode=display">RMSD = \sqrt{\frac{1}{N} \sum_{i=1}^{N} (r_i - p_i)^2}</script><p>其中：</p><ul><li>$N$ 是比较的原子对数。</li><li>$r_i$ 是真实晶体结构中第 $i$ 个原子的坐标。</li><li>$p_i$ 是预测结构中第 $i$ 个原子的坐标。</li></ul><p><a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#回歸vs-分類-Classification-vs-Regression">其实就是 MSE 的形式。</a></p><h3 id="MAE"><a href="#MAE" class="headerlink" title="MAE"></a><strong>MAE</strong></h3><p>同样地，有 L2 损失就有 L1 损失，MAE (Mean Absolute Error, 平均绝对误差)。</p><h2 id="Models"><a href="#Models" class="headerlink" title="Models"></a><strong>Models</strong></h2><p>输入输出都可以是<a href="#data">所有类型的分子表示</a>，模型目标是按<a href="#metrics">指标</a>生成分子。</p><ul><li><strong><a href="#https://blog.v2beach.cn/2024/09/24/cs231n-1/#Lecture-13">GAN</a></strong></li><li><strong><a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#Lecture-13">VAE</a></strong></li><li><strong><a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#Lecture-14">RL</a></strong></li><li><strong><a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#Lecture-17-Attention-and-Transformers">transformers</a></strong></li><li><strong><a href="https://distill.pub/2021/gnn-intro/">GNN</a></strong></li><li><strong><a href="https://www.youtube.com/watch?v=ifCDXFdeaaM&amp;list=PLJV_el3uVTsNi7PgekEUFsyVllAJXRsP-&amp;index=4">Diffusion</a></strong></li><li><strong><a href="https://www.youtube.com/watch?v=uXY18nzdSsM">Flow-Based</a></strong></li></ul><p>transformer + rl = <a href="https://www.nature.com/articles/s41598-023-35648-w">Molecule generation using transformers and policy gradient reinforcement learning</a></p><h1 id="Molecule-Captioning"><a href="#Molecule-Captioning" class="headerlink" title="Molecule Captioning"></a><strong>Molecule Captioning</strong></h1><p>输入可以是<a href="#data">所有类型的分子表示</a>，输出是分子对应的自然语言描述。</p><ul><li><strong><a href="https://blog.v2beach.cn/2024/09/24/cs231n-1/#Lecture-18-Self-Supervised-Learning">contrastive learning</a></strong></li><li><strong>cogview, dalle, ofa</strong></li><li><strong>lstm</strong></li></ul><h1 id="References"><a href="#References" class="headerlink" title="References"></a><strong>References</strong></h1><p>三篇綜述。</p><ol><li>Deep learning for molecular design - a review of the state of the art, 2019, Daniel C. Elton, et al.</li><li>MolGenSurvey: A Systematic Survey in Machine Learning Models for Molecule Design, 2022, Yuanqi Du, et al.</li><li>A Survey of Generative AI for de novo Drug Design: New Frontiers in Molecule and Protein Generation, 2024, Xiangru Tang, et al.</li></ol><h1 id="Appendix"><a href="#Appendix" class="headerlink" title="Appendix"></a><strong>Appendix</strong></h1><h2 id="Appendix-Representations-Comparison"><a href="#Appendix-Representations-Comparison" class="headerlink" title="Appendix-Representations-Comparison"></a><strong>Appendix-Representations-Comparison</strong></h2><script type="math/tex; mode=display">\begin{array}{|c|c|c|c|}\hline & \text{method} & \text{unique?} & \text{invertible?} \\\hline\text{3D↓} & \text{raw voxels} & \times & \checkmark \\\hline & \text{smoothed voxels} & \times & \checkmark \\\hline & \text{tensor field networks} & \times & \times \\\hline\text{2D graph↓} & \text{SMILES} & \times & \checkmark \\\hline & \text{canonical SMILES} & \checkmark & \checkmark \\\hline & \text{InChI} & \checkmark & \checkmark \\\hline & \text{MACCS keys} & \checkmark & \times \\\hline & \text{tensors} & \times & \checkmark \\\hline & \text{Chemception images} & \checkmark & \checkmark \\\hline & \text{fingerprinting} & \checkmark & \times \\\hline\end{array}</script><h2 id="Appendix-QM9"><a href="#Appendix-QM9" class="headerlink" title="Appendix-QM9"></a><strong>Appendix-QM9</strong></h2><div class="table-container"><table><thead><tr><th><strong>行号</strong></th><th><strong>内容</strong></th></tr></thead><tbody><tr><td>1</td><td>Number of atoms $n_a$</td></tr><tr><td>2</td><td>Scalar properties (see Table 3)</td></tr><tr><td>3,…,$na+2$</td><td>Element type, coordinate (x, y, z, in Å), Mulliken partial charges (in e) on atoms</td></tr><tr><td>$n_{a+3}$</td><td>Harmonic vibrational frequencies $3n_a−5$ or $3n_a-6$, in $cm^{-1}$</td></tr><tr><td>$n_{a+4}$</td><td>SMILES strings from GDB-17 and from B3LYP relaxation</td></tr><tr><td>$n_{a+5}$</td><td>InChI strings for Corina and B3LYP geometries</td></tr></tbody></table></div><p>$n_a$=number of atoms.</p><p>Å (埃, Angstrom), $10^{-10}$ 米，0.1 纳米。会用来描述分子坐标的尺度，或者叫分辨率如 <a href="https://www.rcsb.org/structure/3eml">Resolution: 2.60 Å</a>。</p><div class="table-container"><table><thead><tr><th><strong>No.</strong></th><th><strong>Property</strong></th><th><strong>Unit</strong></th><th><strong>Description</strong></th></tr></thead><tbody><tr><td>1</td><td>tag</td><td>—</td><td>‘gdb9’ string to facilitate extraction</td></tr><tr><td>2</td><td>i</td><td>—</td><td>Consecutive, 1-based integer identifier</td></tr><tr><td>3</td><td>A</td><td>GHz</td><td>Rotational constant</td></tr><tr><td>4</td><td>B</td><td>GHz</td><td>Rotational constant</td></tr><tr><td>5</td><td>C</td><td>GHz</td><td>Rotational constant</td></tr><tr><td>6</td><td>μ</td><td>D</td><td>Dipole moment</td></tr><tr><td>7</td><td>α</td><td>$a^3_0$</td><td>Isotropic polarizability</td></tr><tr><td>8</td><td>$ϵ_{HOMO}$</td><td>Ha</td><td>Energy of HOMO</td></tr><tr><td>9</td><td>$ϵ_{LUMO}$</td><td>Ha</td><td>Energy of LUMO</td></tr><tr><td>10</td><td>$ϵ_{gap}$</td><td>Ha</td><td>Gap ($ϵ_{LUMO} - ϵ_{HOMO}$)</td></tr><tr><td>11</td><td>$⟨R^2⟩$</td><td>$a^2_0$</td><td>Electronic spatial extent</td></tr><tr><td>12</td><td>zpve</td><td>Ha</td><td>Zero point vibrational energy</td></tr><tr><td>13</td><td>$U_0$</td><td>Ha</td><td>Internal energy at 0 K</td></tr><tr><td>14</td><td>$U$</td><td>Ha</td><td>Internal energy at 298.15 K</td></tr><tr><td>15</td><td>$H$</td><td>Ha</td><td>Enthalpy at 298.15 K</td></tr><tr><td>16</td><td>$G$</td><td>Ha</td><td>Free energy at 298.15 K</td></tr><tr><td>17</td><td>$C_v$</td><td>$\frac{cal}{molK}$</td><td>Heat capacity at 298.15 K</td></tr></tbody></table></div><p>Properties are stored in the order given by the first column.</p><h2 id="Appendix-ChEMBL"><a href="#Appendix-ChEMBL" class="headerlink" title="Appendix-ChEMBL"></a><strong>Appendix-ChEMBL</strong></h2><p>properties available</p><div class="table-container"><table><thead><tr><th><strong>字段名称</strong></th><th><strong>描述</strong></th><th><strong>数据类型</strong></th></tr></thead><tbody><tr><td><strong>molecule_chembl_id</strong></td><td>ChEMBL ID</td><td>字符串</td></tr><tr><td><strong>pref_name</strong></td><td>Name</td><td>字符串</td></tr><tr><td><strong>molecule_synonyms</strong></td><td>Synonyms</td><td>对象</td></tr><tr><td><strong>molecule_type</strong></td><td>Type</td><td>字符串</td></tr><tr><td><strong>max_phase</strong></td><td>Max Phase</td><td>字符串</td></tr><tr><td><strong>molecule_properties.full_mwt</strong></td><td>Molecular Weight</td><td>双精度浮点数</td></tr><tr><td><strong>_metadata.related_targets.count</strong></td><td>Targets</td><td>整数</td></tr><tr><td><strong>_metadata.related_activities.count</strong></td><td>Bioactivities</td><td>整数</td></tr><tr><td><strong>molecule_properties.alogp</strong></td><td>AlogP</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.psa</strong></td><td>Polar Surface Area</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.hba</strong></td><td>HBA</td><td>整数</td></tr><tr><td><strong>molecule_properties.hbd</strong></td><td>HBD</td><td>整数</td></tr><tr><td><strong>molecule_properties.num_ro5_violations</strong></td><td>#RO5 Violations</td><td>整数</td></tr><tr><td><strong>molecule_properties.rtb</strong></td><td>#Rotatable Bonds</td><td>整数</td></tr><tr><td><strong>molecule_properties.ro3_pass</strong></td><td>Passes Ro3</td><td>字符串</td></tr><tr><td><strong>molecule_properties.qed_weighted</strong></td><td>QED Weighted</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.cx_most_apka</strong></td><td>CX Acidic pKa</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.cx_most_bpka</strong></td><td>CX Basic pKa</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.cx_logp</strong></td><td>CX LogP</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.cx_logd</strong></td><td>CX LogD</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.aromatic_rings</strong></td><td>Aromatic Rings</td><td>整数</td></tr><tr><td><strong>structure_type</strong></td><td>Structure Type</td><td>字符串</td></tr><tr><td><strong>inorganic_flag</strong></td><td>Inorganic Flag</td><td>整数</td></tr><tr><td><strong>molecule_properties.heavy_atoms</strong></td><td>Heavy Atoms</td><td>整数</td></tr><tr><td><strong>molecule_properties.hba_lipinski</strong></td><td>HBA (Lipinski)</td><td>整数</td></tr><tr><td><strong>molecule_properties.hbd_lipinski</strong></td><td>HBD (Lipinski)</td><td>整数</td></tr><tr><td><strong>molecule_properties.num_lipinski_ro5_violations</strong></td><td>#RO5 Violations (Lipinski)</td><td>整数</td></tr><tr><td><strong>molecule_properties.mw_monoisotopic</strong></td><td>Molecular Weight (Monoisotopic)</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.np_likeness_score</strong></td><td>Np Likeness Score</td><td>双精度浮点数</td></tr><tr><td><strong>molecule_properties.molecular_species</strong></td><td>Molecular Species</td><td>字符串</td></tr><tr><td><strong>molecule_properties.full_molformula</strong></td><td>Molecular Formula</td><td>字符串</td></tr><tr><td><strong>molecule_structures.canonical_smiles</strong></td><td>Smiles</td><td>字符串</td></tr><tr><td><strong>molecule_structures.standard_inchi_key</strong></td><td>Inchi Key</td><td>字符串</td></tr><tr><td><strong>molecule_structures.standard_inchi</strong></td><td>Inchi</td><td>字符串</td></tr><tr><td><strong>withdrawn_flag</strong></td><td>Withdrawn Flag</td><td>布尔值</td></tr><tr><td><strong>orphan</strong></td><td>Orphan</td><td>整数</td></tr><tr><td><strong>_metadata.compound_records.compound_key</strong></td><td>Records Key</td><td>字符串</td></tr><tr><td><strong>_metadata.compound_records.compound_name</strong></td><td>Records Name</td><td>字符串</td></tr></tbody></table></div><h2 id="Appendix-Molecule3D"><a href="#Appendix-Molecule3D" class="headerlink" title="Appendix-Molecule3D"></a><strong>Appendix-Molecule3D</strong></h2><div class="table-container"><table><thead><tr><th>Target ID Number</th><th>Property</th></tr></thead><tbody><tr><td>0</td><td>Dipole x</td></tr><tr><td>1</td><td>Dipole y</td></tr><tr><td>2</td><td>Dipole z</td></tr><tr><td>3</td><td>HOMO</td></tr><tr><td>4</td><td>LUMO</td></tr><tr><td>5</td><td>HOMO-LUMO Gap</td></tr><tr><td>6</td><td>SCF Energy</td></tr></tbody></table></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在文獻裡 Molecular Generation 和 Molecule Generation 這倆說法各占一半。&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="AI4science" scheme="http://blog.v2beach.cn/tags/AI4science/"/>
    
      <category term="molecule" scheme="http://blog.v2beach.cn/tags/molecule/"/>
    
  </entry>
  
  <entry>
    <title>chess</title>
    <link href="http://blog.v2beach.cn/2024/11/16/chess/"/>
    <id>http://blog.v2beach.cn/2024/11/16/chess/</id>
    <published>2024-11-16T06:46:31.000Z</published>
    <updated>2025-03-18T10:15:09.869Z</updated>
    
    <content type="html"><![CDATA[<p>當頭炮純粹出於我禮貌的開場，屏風馬神華內斂才能以柔克剛。<br>第二十六著炮五進四只是在試探性衡量，三十三著車二平七的出手你恐怕就暗箭難防。<br>這一場不流血的仗只有自尊會受傷，成王敗寇的鐵則跟現實沒啥兩樣，<br>提著鳥籠的老頭站在一旁拍我肩膀，我猜他想的應該和我一樣。</p><a id="more"></a><div align=center><iframe allow="autoplay *; encrypted-media *;" frameborder="0" height="200" style="width:100%;max-width:660px;overflow:hidden;background:transparent;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/cn/album/%E5%B0%86%E5%86%9B/536114662?i=536115198"></iframe></div><p>去年大概也是這個時候開始玩西洋棋，一直到今年春天臀傷和腳傷之前都在下，回家時老被奶奶吐槽一會兒沒說話就又開一把。一晃半年過去，又開始當臭棋簍子，沒時間<a href="https://wiki.v2beach.cn/Tech/chess.html">廢話</a>了，還是那倆號，還是那倆開局。</p><p>西西里和后翼棄兵之於我，曾像<a href="https://wiki.v2beach.cn/Log/2024-9-end.html">dota2和lol裡的人馬</a>，是我的底裤也是轮椅。</p><h1 id="開放-vs-封閉"><a href="#開放-vs-封閉" class="headerlink" title="開放 vs. 封閉"></a>開放 vs. 封閉</h1><div class="table-container"><table><thead><tr><th><strong>特徵</strong></th><th><strong>開放開局open</strong></th><th><strong>封閉開局closed</strong></th><th><strong>半開放開局semi-open</strong></th><th><strong>半封閉開局semi-closed</strong></th><th><strong>非正規開局irregular</strong></th></tr></thead><tbody><tr><td><strong>白方第一步</strong></td><td>1.e4</td><td>1.d4</td><td>1.e4</td><td>1.d4</td><td>非 1.e4 或 1.d4</td></tr><tr><td><strong>黑方回應</strong></td><td>1…e5</td><td>1…d5</td><td>不走 1…e5（如 1…c5）</td><td>不走 1…d5（如 1…Nf6）</td><td>任意非主流回應</td></tr><tr><td><strong>中心結構</strong></td><td>中心空曠，兵交換</td><td>中心堵塞，兵對峙</td><td>中心不對稱，兵靈活</td><td>中心偏封閉，策略漸進</td><td></td></tr><tr><td><strong>常見佈局</strong></td><td>西班牙開局(the Spanish game, 1.e4 e5 2 Nf3 Nc6 3 Bb5)</td><td>后翼棄兵(1.d4 d5 2. c4)</td><td>西西里防禦(1.e4 c5)、法蘭西防禦(1.e4 e6)</td><td>印度防禦(1.d4 Nf6)</td><td>安德森開局爬蟲結構(1.a3 1…e5 2.h3 d5, Creepy Crawly Formation)威爾開局螃蟹變例(1.a4 1…e5 2.h4, Crab Variation)</td></tr></tbody></table></div><h1 id="Sicilian-Defense"><a href="#Sicilian-Defense" class="headerlink" title="Sicilian Defense"></a>Sicilian Defense</h1><p>Mandy老師：<a href="https://www.youtube.com/watch?v=E6BdM_Q9XvM">https://www.youtube.com/watch?v=E6BdM_Q9XvM</a><br>龍式：<a href="https://www.youtube.com/watch?v=ejh_NGiMkcU">https://www.youtube.com/watch?v=ejh_NGiMkcU</a><br>Najdorf納依道夫變著：<a href="https://www.youtube.com/watch?v=kvDwHYHA-LQ">https://www.youtube.com/watch?v=kvDwHYHA-LQ</a></p><h1 id="Queen’s-Gambit"><a href="#Queen’s-Gambit" class="headerlink" title="Queen’s Gambit"></a>Queen’s Gambit</h1><p>Rey老師：<a href="https://www.youtube.com/watch?v=VS0Um7EEmoQ">https://www.youtube.com/watch?v=VS0Um7EEmoQ</a> , <a href="https://www.youtube.com/watch?v=MG_OvTqNQGA">https://www.youtube.com/watch?v=MG_OvTqNQGA</a> .<br>Mandy老师：<a href="https://www.youtube.com/watch?v=yGAW9BGmqaI">https://www.youtube.com/watch?v=yGAW9BGmqaI</a></p><h1 id="中國象棋"><a href="#中國象棋" class="headerlink" title="中國象棋"></a>中國象棋</h1><p><img src="/pics/chess/Xiangqi_board.svg.png" width="50%" height="50%"><br><img src="/pics/chess/220px-Xiangqiboard.png" alt=""></p><h2 id="中炮局"><a href="#中炮局" class="headerlink" title="中炮局"></a>中炮局</h2><p>中炮局也稱為當頭砲，象棋術語。是指開局的第一步是炮二平五或是炮八平五的開局。<br>中炮局是中國象棋最多最雜的佈局。</p><h2 id="四大開局"><a href="#四大開局" class="headerlink" title="四大開局"></a>四大開局</h2><p>都是一般用於後手應對當頭炮局的。</p><h3 id="順炮"><a href="#順炮" class="headerlink" title="順炮"></a>順炮</h3><ol><li>炮二平五 炮８平５</li></ol><p>就是倆中炮了。</p><h3 id="屏風馬"><a href="#屏風馬" class="headerlink" title="屏風馬"></a>屏風馬</h3><ol><li>炮二平五 馬８進７</li><li>馬二進三<h3 id="反宮馬"><a href="#反宮馬" class="headerlink" title="反宮馬"></a>反宮馬</h3></li><li>炮二平五 馬２進３</li><li>馬二進三 炮８平６</li><li>車一平二 馬８進７<h3 id="單提馬"><a href="#單提馬" class="headerlink" title="單提馬"></a>單提馬</h3></li><li>炮二平五 馬２進３</li><li>馬二進三 馬８進９</li></ol><h2 id="體會"><a href="#體會" class="headerlink" title="體會"></a>體會</h2><p>就從學習開局這點來說，中國象棋比西洋棋資料匱乏程度，或者說獲取難度大太多了。連張正兒八經的棋盤棋子圖都很難找，更別說棋譜了。<br>真不是不想玩，有心無力而已。</p><h1 id="TBD"><a href="#TBD" class="headerlink" title="TBD"></a>TBD</h1><p>學到新鮮玩意再更新。</p><audio controls>    <source src="/audio/想念.mp3" type="audio/mp3" alt="想念">    Your browser does not support the audio element.</audio>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;當頭炮純粹出於我禮貌的開場，屏風馬神華內斂才能以柔克剛。&lt;br&gt;第二十六著炮五進四只是在試探性衡量，三十三著車二平七的出手你恐怕就暗箭難防。&lt;br&gt;這一場不流血的仗只有自尊會受傷，成王敗寇的鐵則跟現實沒啥兩樣，&lt;br&gt;提著鳥籠的老頭站在一旁拍我肩膀，我猜他想的應該和我一樣。&lt;/p&gt;
    
    </summary>
    
    
      <category term="gamer" scheme="http://blog.v2beach.cn/categories/gamer/"/>
    
    
      <category term="BeInvisible" scheme="http://blog.v2beach.cn/tags/BeInvisible/"/>
    
      <category term="chess" scheme="http://blog.v2beach.cn/tags/chess/"/>
    
  </entry>
  
  <entry>
    <title>Elo Rating System和Probability Theory Basics</title>
    <link href="http://blog.v2beach.cn/2024/11/05/Elo-Rating-System%E5%92%8CProbability-Theory-Basics/"/>
    <id>http://blog.v2beach.cn/2024/11/05/Elo-Rating-System%E5%92%8CProbability-Theory-Basics/</id>
    <published>2024-11-05T07:43:20.000Z</published>
    <updated>2025-01-16T08:01:31.796Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/pics/ArpadElo.jpg" alt=""></p><a id="more"></a><h1 id="Elo-Rating-System"><a href="#Elo-Rating-System" class="headerlink" title="Elo Rating System"></a><strong>Elo Rating System</strong></h1><p><a href="https://en.wikipedia.org/wiki/Elo_rating_system">https://en.wikipedia.org/wiki/Elo_rating_system</a></p><p>The Elo rating system is a method for calculating the relative skill levels of players in zero-sum games such as chess or esports. It is named after its creator Arpad Elo, a Hungarian-American physics professor. (Educated in public primary and secondary schools, he earned his BS (22歲) and MS (25歲) degrees, <strong>in Physics</strong>, from the University of Chicago.)</p><p>西洋棋的elo rating在<a href="https://ratings.fide.com/top_lists.phtml">這裡</a>，我的<a href="https://www.dotabuff.com/players/443839701/matches">dota</a>和<a href="https://www.chess.com/member/dopedashenone">chess</a>在這裡。</p><p>Pairwise comparisons form the basis of the Elo rating methodology.</p><p><a href="https://github.com/V2beach/books/blob/main/1978-elo-theratingofchessplayerspastandpresent.pdf">https://github.com/V2beach/books/blob/main/1978-elo-theratingofchessplayerspastandpresent.pdf</a></p><div align=center><img src="https://wiki.v2beach.cn/assets/Pairwise+Comparison+Matrix+Manual+Spreadsheet+Method+Calculator+Example.png" loading="lazy" alt="pairwise comparison" width="50%" height="50%"></div><p>Elo’s central assumption was that the chess performance of each player in each game is a normally distributed random variable. Although a player might perform significantly better or worse from one game to the next, Elo assumed that the mean value of the performances of any given player changes only slowly over time. Elo thought of a player’s true skill as the mean of that player’s performance random variable.</p><p>A further assumption is necessary because chess performance in the above sense is still not measurable. One cannot look at a sequence of moves and derive a number to represent that player’s skill. Performance can only be inferred from wins, draws, and losses. Therefore, a player who wins a game is assumed to have performed at a higher level than the opponent for that game. Conversely, a losing player is assumed to have performed at a lower level. If the game ends in a draw, the two players are assumed to have performed at nearly the same level.</p><p><a href="https://en.wikipedia.org/wiki/Elo_rating_system#Practical_issues">實踐中存在很多問題。</a></p><h2 id="Mathematical-details"><a href="#Mathematical-details" class="headerlink" title="Mathematical details"></a><strong>Mathematical details</strong></h2><p>如果玩家A的rating是RA，玩家B的當前分數是RB，玩家A預測分數的公式（using the <a href="https://en.wikipedia.org/wiki/Logistic_curve">logistic curve</a> with <a href="https://en.wikipedia.org/wiki/Common_logarithm">base 10</a>）是<br>$E_{\mathrm{A}}=\frac{1}{1+10^{\left(R_{\mathrm{B}}-R_{\mathrm{A}}\right) / 400}} .$<br>相似地，B的預期分數是<br>$E_{\mathrm{B}}=\frac{1}{1+10^{\left(R_{\mathrm{A}}-R_{\mathrm{B}}\right) / 400}} .$<br>也可以表示為<br>$E_A=\frac{Q_A}{Q_A+Q_B}$<br>和<br>$E_B=\frac{Q_B}{Q_A+Q_B},$<br>其中$Q_A=10^{R_A/400}$$Q_B=10^{R_B/400}$。這個logistic函數的曲線如下，</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-04 at 22.57.34.png" loading="lazy" alt="player's expected score function graph" width="50%" height="50%"></div><p>其實就是用分差把預期分數約束到0到1之間，其中勝利是1，失敗是0。</p><p>假設玩家A（當前分數$R_A$）的預期分數是$E_A$但是實際得分是$S_A$，玩家更新分數的公式是<br>$R_{\mathrm{A}}^{\prime}=R_{\mathrm{A}}+K \cdot\left(S_{\mathrm{A}}-E_{\mathrm{A}}\right).$</p><p>K-factor set at K=16  for masters and K=32 for weaker players.</p><p>Moreover, online judge sites are also using Elo rating system or its derivatives. For example, Topcoder is using a modified version based on normal distribution, while Codeforces is using another version based on logistic distribution.</p><p>Formal derivation for win/loss games<br>The above expressions can be now formally derived by exploiting the link between the Elo rating and the stochastic gradient update in the logistic regression.有的用隨機梯度更新分數</p><h2 id="Logistic-Sigmoid"><a href="#Logistic-Sigmoid" class="headerlink" title="Logistic/Sigmoid"></a><strong>Logistic/Sigmoid</strong></h2><p>A <b>logistic function</b> or <b>logistic curve</b> is a common S-shaped curve (<a href="https://en.wikipedia.org/wiki/Sigmoid_function" title="Sigmoid function">sigmoid curve</a>) with the equation.</p>  <p>$f(x)=\frac{L}{1+e^{-k\left(x-x_0\right)}}$</p><p>where</p><ul><li>L is the carrying capacity, the supremum of the values of the function;</li><li>k is the logistic growth rate, the steepness of the curve; and</li><li>x0 is the x value of the function’s midpoint.<br>The logistic function has domain the real numbers, the limit as x→−∞ is 0, and the limit as x→+∞ is L.</li></ul><p><img src="https://wiki.v2beach.cn/assets/Logistic-curve.svg.png" width="50%" height="50%" loading="lazy" alt="Standard logistic function where L=1,k=1,x0=0."></p><blockquote><p>sup(X)是取上限函数，inf(X) 是取下限函数。 sup是supremum的简写，意思是：上确界，最小上界。 inf是infimum的简写，意思是：下确界，最大下界。<br><img src="https://wiki.v2beach.cn/assets/Minimum-maximum-infimum-and-supremum-of-example-functions.ppm.png" width="50%" height="50%"></p></blockquote><p>A <strong><em>sigmoid function</em></strong> refers specifically to a function whose graph follows the logistic function. It is defined by the formula:</p><p>$\sigma(x)=\frac{1}{1+e^{-x}}=\frac{e^x}{1+e^x}=1-\sigma(-x) .$</p><p><img src="https://blog.v2beach.cn/pics/cs231n/sigmoid.png" width="50%" height="50%" loading="lazy" alt="Some sigmoid functions compared. In the drawing all functions are normalized in such a way that their slope at the origin is 1."></p><p>The logistic function was introduced in a series of three papers by Pierre François <strong>Verhulst</strong> between 1838 and 1847, who devised it as <strong>a model of population growth by adjusting the exponential growth model</strong>。</p><p>Exponential Growth：</p><style>    .legend-color{        display: inline-block;        min-width: 1.25em;        height: 1.25em;        line-height: 1.25;        margin: 1px 0;        text-align: center;        border: 1px solid black    }</style><p><img src="https://wiki.v2beach.cn/assets/Exponential.svg.png" width="50%" height="50%" loading="lazy" alt="The graph illustrates how exponential growth (green) surpasses both linear (red) and cubic (blue) growth."/></p><div class="legend"><span class="legend-color mw-no-invert" style="background-color:red; color:black;">&nbsp;</span>&nbsp;Linear growth</div><div class="legend"><span class="legend-color mw-no-invert" style="background-color:blue; color:white;">&nbsp;</span>&nbsp;<a href="https://en.wikipedia.org/wiki/Polynomial" title="Polynomial">Cubic growth</a></div><div class="legend"><span class="legend-color mw-no-invert" style="background-color:green; color:white;">&nbsp;</span>&nbsp;Exponential growth</div><p>這logistic函數發明初衷是用於優化人口增長函數，在統計學和機器學習的應用只是它茫茫多應用的其中之一而已。<br>所有圖像長得像logsitic函數的都是sigmoid函數(S-shaped curve)。</p><p>Logistic functions are used in several roles in statistics. For example, they are the cumulative distribution function of the logistic family of distributions, and they are, a bit simplified, used to model the chance a chess player has to beat their opponent in the Elo rating system. More specific examples now follow.</p><h2 id="Logistic-regression"><a href="#Logistic-regression" class="headerlink" title="Logistic regression"></a><strong>Logistic regression</strong></h2><p>Logistic functions are used in logistic regression to model how the probability p of an event may be affected by one or more explanatory variables: an example would be to have the model</p><p>$p = f(a + bx),$</p><p>where x is the explanatory variable, a and b are model parameters to be fitted, and f is the standard logistic function.</p><p>Logistic regression and other <strong>log-linear models</strong> are also commonly used in machine learning. A generalisation of the logistic function to multiple inputs is the softmax activation function, used in multinomial logistic regression.</p><h2 id="Log-linear"><a href="#Log-linear" class="headerlink" title="Log-linear"></a><strong>Log-linear</strong></h2><p>先複習一下，線性回歸是$y_i(x)=\beta_0+\beta_1x_i$，</p><div align=center><img src="https://wiki.v2beach.cn/assets/1*F8xLapWs4stL93NEO5NPeQ.webp" loading="lazy" alt="graph" width="50%" height="50%"></div><p>把數據$(x_i, y_i)$代入之後，用最小平方法(Least Square)計算loss function目標/損失函數，<br>${Loss}\left({\beta}_0, {\beta}_1\right)=\sum_{i=1}^n\left(y_i-\hat{y}_i\right)^2=\sum_{i=1}^n\left(y_i-\left({\beta}_0+{\beta}_1 x_i\right)\right)^2$<br>之後更新beta修正函數擬合數據。</p><p>而log-linear就是把a+bx結果代入logistic或者其他sigmoid函數，好處是比如對分類任務來說，这个Sigmoid Function可以将线性的值，映射到[0-1]范围中。如果映射结果小于0.5，则认为是负的样本，如果是大于0.5，则认为是正的样本。</p><h2 id="採用logistic分布和gaussian-normal分布的區別？"><a href="#採用logistic分布和gaussian-normal分布的區別？" class="headerlink" title="採用logistic分布和gaussian/normal分布的區別？"></a><strong>採用logistic分布和gaussian/normal分布的區別？</strong></h2><p>A workable rating system, fully developed from basic theory, includes certain principal components:</p><ol><li>Rating scale</li><li>Performance distribution function</li><li>Percentage expectancy function</li><li>Performance rating formula</li><li>Continuous rating formula</li><li>Appropriate numerical coefficients and ancillary formulae<br>—— 8.12 <a href="https://gwern.net/doc/statistics/order/comparison/1978-elo-theratingofchessplayerspastandpresent.pdf">The Rating of Chessplayers, Past &amp; Present, Second Edition, Arpad E. Elo</a></li></ol><p>區別就是預期分數（player’s expected score）的計算公式，上面用的logistic function(base10)其實就是logistic distribution的CDF，可以發現如下圖所示跟normal distribution的CDF很像。</p><ul><li>In probability theory and statistics, the logistic distribution is a continuous probability distribution.</li><li><strong>Its cumulative distribution function is the logistic function, which appears in logistic regression and feedforward neural networks.</strong></li><li>It resembles the normal distribution in shape but has heavier tails (higher kurtosis).</li></ul><p>logistic distribution其實就是尾部更長的normal distribution了。</p><table class="infobox infobox-table ib-prob-dist"><tbody><tr><td colspan="4">Logistic distribution</td><td colspan="4">Normal distribution</td></tr><tr><td colspan="4" class="infobox-image"><div class="ib-prob-dist-image">Probability density function</div><span typeof="mw:File"><a href="/wiki/File:Logisticpdfunction.svg" class="mw-file-description" title="Standard logistic PDF"><img loading="lazy" alt="Standard logistic PDF" src="https://wiki.v2beach.cn/assets/Logisticpdfunction.svg.png" decoding="async" width="320" height="256" class="mw-file-element" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/6/66/Logisticpdfunction.svg/480px-Logisticpdfunction.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/6/66/Logisticpdfunction.svg/640px-Logisticpdfunction.svg.png 2x" data-file-width="450" data-file-height="360"></a></span></td><td colspan="4" class="infobox-image"><div class="ib-prob-dist-image">Probability density function</div><span class="skin-invert-image" typeof="mw:File"><a href="/wiki/File:Normal_Distribution_PDF.svg" class="mw-file-description"><img src="https://wiki.v2beach.cn/assets/Normal_Distribution_PDF.svg.png" decoding="async" width="400" height="256" class="mw-file-element" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/7/74/Normal_Distribution_PDF.svg/600px-Normal_Distribution_PDF.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/7/74/Normal_Distribution_PDF.svg/800px-Normal_Distribution_PDF.svg.png 2x" data-file-width="720" data-file-height="460"></a></span><div class="infobox-caption">The red curve is the <i>standard normal distribution</i>.</div></td></tr><tr><td colspan="4" class="infobox-image"><div class="ib-prob-dist-image">Cumulative distribution function</div><span typeof="mw:File"><a href="/wiki/File:Logistic_cdf.svg" class="mw-file-description" title="Standard logistic CDF"><img loading="lazy" alt="Standard logistic CDF" src="https://wiki.v2beach.cn/assets/Logistic_cdf.svg.png" decoding="async" width="320" height="256" class="mw-file-element" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Logistic_cdf.svg/480px-Logistic_cdf.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Logistic_cdf.svg/640px-Logistic_cdf.svg.png 2x" data-file-width="450" data-file-height="360"></a></span></td><td colspan="4" class="infobox-image"><div class="ib-prob-dist-image">Cumulative distribution function</div><span class="skin-invert-image" typeof="mw:File"><a href="/wiki/File:Normal_Distribution_CDF.svg" class="mw-file-description"><img src="https://wiki.v2beach.cn/assets/Normal_Distribution_CDF.svg.png" decoding="async" width="400" height="256" class="mw-file-element" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Normal_Distribution_CDF.svg/600px-Normal_Distribution_CDF.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Normal_Distribution_CDF.svg/800px-Normal_Distribution_CDF.svg.png 2x" data-file-width="720" data-file-height="460"></a></span></td></tr><tr><th scope="row" class="infobox-label">Notation</th><td colspan="3">LogisticDistribution(μ,s)</td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mrow class="MJX-TeXAtom-ORD">          <mrow class="MJX-TeXAtom-ORD">            <mi class="MJX-tex-caligraphic" mathvariant="script">N</mi>          </mrow>        </mrow>        <mo stretchy="false">(</mo>        <mi>μ<!-- μ --></mi>        <mo>,</mo>        <msup>          <mi>σ<!-- σ --></mi>          <mrow class="MJX-TeXAtom-ORD">            <mn>2</mn>          </mrow>        </msup>        <mo stretchy="false">)</mo>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/863304aaa42a945f2f07d79facc3d2eebc845ce7" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; margin-left: -0.062ex; width:8.966ex; height:3.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Statistical_parameter" title="Statistical parameter">Parameters</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>        <mo>,</mo>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/1e7e1ef161a49a22b500d63307460ad92eeb6a16" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:2.049ex; height:2.176ex;" loading="lazy" alt=""></span> <a href="/wiki/Location_parameter" title="Location parameter">location</a> (<a href="/wiki/Real_number" title="Real number">real</a>)<br><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>s</mi>        <mo>&gt;</mo>        <mn>0</mn>        <mo>,</mo>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/c383e7faf9b1add044f091319507fccc0f8a310f" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.671ex; width:5.998ex; height:2.509ex;" loading="lazy" alt=""></span> <a href="/wiki/Scale_parameter" title="Scale parameter">scale</a> (real)</td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>        <mo>∈<!-- ∈ --></mo>        <mrow class="MJX-TeXAtom-ORD">          <mi mathvariant="double-struck">R</mi>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/a9a48f0e84328dc53dec2ad301bb321c00dcf422" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:5.92ex; height:2.676ex;" loading="lazy" alt=""></span> = <a href="/wiki/Mean" title="Mean">mean</a> (<a href="/wiki/Location_parameter" title="Location parameter">location</a>)<br><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <msup>          <mi>σ<!-- σ --></mi>          <mrow class="MJX-TeXAtom-ORD">            <mn>2</mn>          </mrow>        </msup>        <mo>∈<!-- ∈ --></mo>        <msub>          <mrow class="MJX-TeXAtom-ORD">            <mi mathvariant="double-struck">R</mi>          </mrow>          <mrow class="MJX-TeXAtom-ORD">            <mo>&gt;</mo>            <mn>0</mn>          </mrow>        </msub>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/5b81059aaa2db87802908711c7527c0e1d65fc06" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.671ex; width:9.236ex; height:3.009ex;" loading="lazy" alt=""></span> = <a href="/wiki/Variance" title="Variance">variance</a> (squared <a href="/wiki/Scale_parameter" title="Scale parameter">scale</a>)</td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Support_(mathematics)" title="Support (mathematics)">Support</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>x</mi>        <mo>∈<!-- ∈ --></mo>        <mo stretchy="false">(</mo>        <mo>−<!-- − --></mo>        <mi mathvariant="normal">∞<!-- ∞ --></mi>        <mo>,</mo>        <mi mathvariant="normal">∞<!-- ∞ --></mi>        <mo stretchy="false">)</mo>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/d7aea9be5e96822459afc5c7d9f911a586290dc5" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:13.469ex; height:2.843ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>x</mi>        <mo>∈<!-- ∈ --></mo>        <mrow class="MJX-TeXAtom-ORD">          <mi mathvariant="double-struck">R</mi>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/a9c6d458566aec47a7259762034790c8981aefab" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.338ex; width:5.848ex; height:2.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Probability_density_function" title="Probability density function">PDF</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mrow class="MJX-TeXAtom-ORD">          <mfrac>            <msup>              <mi>e</mi>              <mrow class="MJX-TeXAtom-ORD">                <mo>−<!-- − --></mo>                <mo stretchy="false">(</mo>                <mi>x</mi>                <mo>−<!-- − --></mo>                <mi>μ<!-- μ --></mi>                <mo stretchy="false">)</mo>                <mrow class="MJX-TeXAtom-ORD">                  <mo>/</mo>                </mrow>                <mi>s</mi>              </mrow>            </msup>            <mrow>              <mi>s</mi>              <msup>                <mrow>                  <mo>(</mo>                  <mrow>                    <mn>1</mn>                    <mo>+</mo>                    <msup>                      <mi>e</mi>                      <mrow class="MJX-TeXAtom-ORD">                        <mo>−<!-- − --></mo>                        <mo stretchy="false">(</mo>                        <mi>x</mi>                        <mo>−<!-- − --></mo>                        <mi>μ<!-- μ --></mi>                        <mo stretchy="false">)</mo>                        <mrow class="MJX-TeXAtom-ORD">                          <mo>/</mo>                        </mrow>                        <mi>s</mi>                      </mrow>                    </msup>                  </mrow>                  <mo>)</mo>                </mrow>                <mrow class="MJX-TeXAtom-ORD">                  <mn>2</mn>                </mrow>              </msup>            </mrow>          </mfrac>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9d42088b22c0c7577eb7a68d827061870368d054" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -3.505ex; width:17.79ex; height:7.343ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mrow class="MJX-TeXAtom-ORD">          <mfrac>            <mn>1</mn>            <msqrt>              <mn>2</mn>              <mi>π<!-- π --></mi>              <msup>                <mi>σ<!-- σ --></mi>                <mrow class="MJX-TeXAtom-ORD">                  <mn>2</mn>                </mrow>              </msup>            </msqrt>          </mfrac>        </mrow>        <msup>          <mi>e</mi>          <mrow class="MJX-TeXAtom-ORD">            <mo>−<!-- − --></mo>            <mrow class="MJX-TeXAtom-ORD">              <mfrac>                <mrow>                  <mo stretchy="false">(</mo>                  <mi>x</mi>                  <mo>−<!-- − --></mo>                  <mi>μ<!-- μ --></mi>                  <msup>                    <mo stretchy="false">)</mo>                    <mrow class="MJX-TeXAtom-ORD">                      <mn>2</mn>                    </mrow>                  </msup>                </mrow>                <mrow>                  <mn>2</mn>                  <msup>                    <mi>σ<!-- σ --></mi>                    <mrow class="MJX-TeXAtom-ORD">                      <mn>2</mn>                    </mrow>                  </msup>                </mrow>              </mfrac>            </mrow>          </mrow>        </msup>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/2ce7e315b02666699e0cd8ea5fb1a3e0c287cd9d" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -2.838ex; width:15.527ex; height:7.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Cumulative_distribution_function" title="Cumulative distribution function">CDF</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mrow class="MJX-TeXAtom-ORD">          <mfrac>            <mn>1</mn>            <mrow>              <mn>1</mn>              <mo>+</mo>              <msup>                <mi>e</mi>                <mrow class="MJX-TeXAtom-ORD">                  <mo>−<!-- − --></mo>                  <mo stretchy="false">(</mo>                  <mi>x</mi>                  <mo>−<!-- − --></mo>                  <mi>μ<!-- μ --></mi>                  <mo stretchy="false">)</mo>                  <mrow class="MJX-TeXAtom-ORD">                    <mo>/</mo>                  </mrow>                  <mi>s</mi>                </mrow>              </msup>            </mrow>          </mfrac>        </mrow>        <mo>=</mo>        <mrow class="MJX-TeXAtom-ORD">          <mfrac>            <mrow>              <mn>1</mn>              <mo>+</mo>              <mi>tanh</mi>              <mo>⁡<!-- ⁡ --></mo>              <mrow class="MJX-TeXAtom-ORD">                <mfrac>                  <mrow>                    <mi>x</mi>                    <mo>−<!-- − --></mo>                    <mi>μ<!-- μ --></mi>                  </mrow>                  <mrow>                    <mn>2</mn>                    <mi>s</mi>                  </mrow>                </mfrac>              </mrow>            </mrow>            <mn>2</mn>          </mfrac>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/841f3507b4810bd25d4211115d9d1720edfa96eb" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -2.338ex; width:30.538ex; height:7.176ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi mathvariant="normal">Φ<!-- Φ --></mi>        <mrow>          <mo>(</mo>          <mrow class="MJX-TeXAtom-ORD">            <mfrac>              <mrow>                <mi>x</mi>                <mo>−<!-- − --></mo>                <mi>μ<!-- μ --></mi>              </mrow>              <mi>σ<!-- σ --></mi>            </mfrac>          </mrow>          <mo>)</mo>        </mrow>        <mo>=</mo>        <mrow class="MJX-TeXAtom-ORD">          <mfrac>            <mn>1</mn>            <mn>2</mn>          </mfrac>        </mrow>        <mrow>          <mo>[</mo>          <mrow>            <mn>1</mn>            <mo>+</mo>            <mi>erf</mi>            <mo>⁡<!-- ⁡ --></mo>            <mrow>              <mo>(</mo>              <mrow class="MJX-TeXAtom-ORD">                <mfrac>                  <mrow>                    <mi>x</mi>                    <mo>−<!-- − --></mo>                    <mi>μ<!-- μ --></mi>                  </mrow>                  <mrow>                    <mi>σ<!-- σ --></mi>                    <mrow class="MJX-TeXAtom-ORD">                      <msqrt>                        <mn>2</mn>                      </msqrt>                    </mrow>                  </mrow>                </mfrac>              </mrow>              <mo>)</mo>            </mrow>          </mrow>          <mo>]</mo>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/c0fed43e25966344745178c406f04b15d0fa3783" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -2.838ex; width:36.321ex; height:6.509ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Quantile_function" title="Quantile function">Quantile</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>        <mo>+</mo>        <mi>s</mi>        <mi>log</mi>        <mo>⁡<!-- ⁡ --></mo>        <mrow>          <mo>(</mo>          <mrow class="MJX-TeXAtom-ORD">            <mfrac>              <mi>p</mi>              <mrow>                <mn>1</mn>                <mo>−<!-- − --></mo>                <mi>p</mi>              </mrow>            </mfrac>          </mrow>          <mo>)</mo>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/30fdc436e07663fa4f425651b2d506f86c740dc2" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -2.505ex; width:18.121ex; height:6.176ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>        <mo>+</mo>        <mi>σ<!-- σ --></mi>        <mrow class="MJX-TeXAtom-ORD">          <msqrt>            <mn>2</mn>          </msqrt>        </mrow>        <msup>          <mi>erf</mi>          <mrow class="MJX-TeXAtom-ORD">            <mo>−<!-- − --></mo>            <mn>1</mn>          </mrow>        </msup>        <mo>⁡<!-- ⁡ --></mo>        <mo stretchy="false">(</mo>        <mn>2</mn>        <mi>p</mi>        <mo>−<!-- − --></mo>        <mn>1</mn>        <mo stretchy="false">)</mo>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/510cf4025ba141645f21c7f06866867648d5bc21" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:22.19ex; height:3.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Expected_value" title="Expected value">Mean</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9fd47b2a39f7a7856952afec1f1db72c67af6161" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:1.402ex; height:2.176ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9fd47b2a39f7a7856952afec1f1db72c67af6161" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:1.402ex; height:2.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Median" title="Median">Median</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9fd47b2a39f7a7856952afec1f1db72c67af6161" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:1.402ex; height:2.176ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9fd47b2a39f7a7856952afec1f1db72c67af6161" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:1.402ex; height:2.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Mode_(statistics)" title="Mode (statistics)">Mode</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9fd47b2a39f7a7856952afec1f1db72c67af6161" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:1.402ex; height:2.176ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mi>μ<!-- μ --></mi>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/9fd47b2a39f7a7856952afec1f1db72c67af6161" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.838ex; width:1.402ex; height:2.176ex;" loading="lazy" alt=""></span></td></tr><tr><th scope="row" class="infobox-label"><a href="/wiki/Variance" title="Variance">Variance</a></th><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <mrow class="MJX-TeXAtom-ORD">          <mfrac>            <mrow>              <msup>                <mi>s</mi>                <mrow class="MJX-TeXAtom-ORD">                  <mn>2</mn>                </mrow>              </msup>              <msup>                <mi>π<!-- π --></mi>                <mrow class="MJX-TeXAtom-ORD">                  <mn>2</mn>                </mrow>              </msup>            </mrow>            <mn>3</mn>          </mfrac>        </mrow>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/f9e1141f49e6711939a3757111a9dc5576990db0" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -1.838ex; width:5.369ex; height:5.676ex;" loading="lazy" alt=""></span></td><td colspan="3" class="infobox-data"><span class="mwe-math-element"><span class="mwe-math-mathml-inline mwe-math-mathml-a11y" style="display: none;"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="">  <semantics>    <mrow class="MJX-TeXAtom-ORD">      <mstyle displaystyle="true" scriptlevel="0">        <msup>          <mi>σ<!-- σ --></mi>          <mrow class="MJX-TeXAtom-ORD">            <mn>2</mn>          </mrow>        </msup>      </mstyle>    </mrow>    <annotation encoding="application/x-tex"></annotation>  </semantics></math></span><img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/53a5c55e536acf250c1d3e0f754be5692b843ef5" class="mwe-math-fallback-image-inline mw-invert skin-invert" aria-hidden="true" style="vertical-align: -0.338ex; width:2.385ex; height:2.676ex;" loading="lazy" alt=""></span></td></tr></tbody></table><h1 id="Probability-theory-basics機率基礎"><a href="#Probability-theory-basics機率基礎" class="headerlink" title="Probability theory basics機率基礎"></a><strong>Probability theory basics機率基礎</strong></h1><p><a href="https://www.youtube.com/watch?v=GwSEguqJj6U&amp;list=PLtvno3VRDR_jMAJcNY1n4pnP5kXtPOmVk">https://www.youtube.com/watch?v=GwSEguqJj6U&amp;list=PLtvno3VRDR_jMAJcNY1n4pnP5kXtPOmVk</a><br>台大電機 Prof. 葉丙成 機率與統計 2013，這個講得超好。</p><p><strong>為什麼我們要研究機率？</strong><br><strong>因為我們對這世界了解太少，這世界的運作有很多仍是未知的。——prof.benson</strong></p><p>術語：</p><ul><li>Experiment實驗(procedures實驗步驟、model機率模型、observations結果觀察，三個部分任何一個改變，都會變為新的實驗)，</li><li>Outcome結果，</li><li>Sample Space樣本空間，若是S={o1,…,on}n個outcome的集合</li><li>Event事件（實驗結果的敘述，因此是Outcome的集合，或者是Sample Space的子集），</li><li>Event Space事件空間，則為\{\{o1},…,{on},{o1,o2},{o1,o3},…,{on-1,on},{o1,o2,o3},…,{o1,o2,…,on},{}}一共2^n個事件元素的集合</li><li>Probability機率（是個函數，P(Event)，Probability is a function of Event(Set)，Domain定義域就是Event Space，Range值域是[0,1]） </li><li>Set Theory（因為Probability is a function of Set，所以要了解Set）術語：<ul><li>Element, Set, Subset, Universal Set全集, Empty Set, Intersection交集, Union并集/聯集, Complement補集, Difference差集(X - Y), Disjoint不相交(X, Y disjoint if X∩Y=∅), Mutually Exclusive互斥（兩個set是disjoint，一堆集合兩兩不相交就是互斥）</li><li>De Morgan’s Law:<br><img src="https://wiki.v2beach.cn/assets/1280px-Demorganlaws.svg.png" width="50%" height="50%"></li></ul></li></ul><blockquote><p>為什麼事件空間是2^n？一次實驗裡兩個結果，三個結果怎麼可能同時發生？<br>剛開始這個疑問就是沒搞清結果和事件的區別，一個事件可能包含多個結果。（37分鐘有例子）</p></blockquote><h2 id="Axioms-ˈaksɪəm"><a href="#Axioms-ˈaksɪəm" class="headerlink" title="Axioms /ˈaksɪəm/"></a><strong>Axioms /ˈaksɪəm/</strong></h2><p>從<a href="https://www.youtube.com/watch?v=ee-k1VBdzNU&amp;list=PLtvno3VRDR_jMAJcNY1n4pnP5kXtPOmVk&amp;index=2">這部影片</a>第一次了解一個理論的公理，中國大陸的教學真差。<br>近代數學常從數條公理推導出整套理論，線性代數有8(9?)條公理(<a href="https://www.math.ucla.edu/~tao/resource/general/121.1.00s/vector_axioms.html">9 axioms of linear algebra/real vector spaces</a>):</p><ul><li>Additive axioms.  For every x,y,z in X, we have<ul><li>x+y = y+x.</li><li>(x+y)+z = x+(y+z).</li><li>0+x = x+0 = x.</li><li>(-x) + x = x + (-x) = 0.</li></ul></li><li>Multiplicative axioms.  For every x in X and real numbers c,d, we have<ul><li>0x = 0</li><li>1x = x</li><li>(cd)x = c(dx)</li></ul></li><li>Distributive axioms.  For every x,y in X and real numbers c,d, we have<ul><li>c(x+y) = cx + cy.</li><li>(c+d)x = cx +dx.<br>整套理論只基於幾條公理的好處是，對於一個新問題只需要確定滿足這些公理（其實沒有定義“+”要怎麼做，是general的，套別的case其實不一定滿足），就能適用其他所有定理。<br>公理不用證明，axioms是理性直覺覺得是對的東西。只從這幾條廢話推導出所有定理確實牛逼。</li></ul></li></ul><p>Axioms of Probability(機率三公理):</p><ul><li>Axiom 1: For any event A, $P(A) \geq 0$</li><li>Axiom 2: Probability of the sample space S is $P(S)=1$</li><li>Axiom 3: If A1,A2,A3,⋯ are disjoint events, then $P(A_1 \cup A_2 \cup A_3 \cdots)=P(A_1)+P(A_2)+P(A_3)+\cdots$</li></ul><p>axiom3的disjoint即互斥。</p><h2 id="性質-定理"><a href="#性質-定理" class="headerlink" title="性質(定理)"></a><strong>性質(定理)</strong></h2><ul><li>P(A) = P(A-B) + P(A∩B)  </li><li>P(A∪B) = P(A) + P(B) - P(A∩B)  </li><li>切麵包定理：若 $C_1, C_2, \cdots, C_n$互斥，且$C_1 \cup C_2 \cup \cdots \cup C_n = S$，則对任何事件 A：$P(A) = P(A \cap C_1) + P(A \cap C_2) + \cdots + (A \cap C_n)$</li><li>若A⊂B則P(A)≤P(B)</li><li>Boole’s inequality/Union Bound布爾不等式：對任何事件A1, A2, …, An，$\mathbb{P}\left(\bigcup_i A_i\right)\leq \sum_i\mathbb P(A_i)$</li><li>Bonferroni’s inequality</li></ul><h2 id="conditional-probability條件機率-貝氏定理"><a href="#conditional-probability條件機率-貝氏定理" class="headerlink" title="conditional probability條件機率(貝氏定理)"></a><strong>conditional probability條件機率(貝氏定理)</strong></h2><p>P ( X | Y ), Y是觀察到的已發生的事件，X是關心的未發生的事件。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 13.53.26.png" loading="lazy" alt="原本實驗的樣本空間若是S即P(S)=1，新的樣本空間就變成了Y即P(Y|Y)=1，這個豎線｜讀作“given”比如P(X|Y)讀作P of X given Y" width="50%" height="50%"></div><p><strong>tex公式裡∪是\\cup，∩是\\cap，一個杯子一個帽子很形象很有趣。</strong></p><p>$P(X|Y) = \frac{P(X\cap Y)}{P(Y)}$</p><p>P ( X | Y )可以理解成只是樣本空間S變成了Y而已。  </p><p><strong>若事件C1, C2 . . . Cn互斥且S = C1 ∪ C2 ∪ . . . . . ∪ Cn，則對任意事件A，</strong></p><ul><li>Law of Total Probability(切麵包)，$\begin{array}{l} P(A) =\sum\limits_{i=1}^{n} P(C_i)P(A| C_i)\end{array}$。Ex：阿宅 vs. 可愛店員：店員對阿宅笑否，受店的生意影響很大。已知滿座機率，可愛店員對阿宅笑的機率？</li><li>Bayes’ Rule, $ P(C_j|A)=\frac{P(A|C_j)P(C_j)}{\sum\limits_{i=1}^{n} P(A | C_i) P(C_i)}$，之前算的都是P of A given C，貝葉斯是解決計算P of C given A。Ex：一日，老闆見可愛店員笑，請問在此情況下，當日生意滿座之機率為何？  <div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 15.00.44.png" loading="lazy" alt="proof" width="50%" height="50%"></div></li></ul><h2 id="Independence-獨立性"><a href="#Independence-獨立性" class="headerlink" title="Independence(獨立性)"></a><strong>Independence(獨立性)</strong></h2><p>若滿足P(A∩B)=P(A)P(B)則A、B兩事件為獨立事件。<br>跟互斥對比：A∩B=∅不可能事件即P(A∩B)=0。</p><p>可以理解成兩個實驗的樣本空間沒有交集？</p><p>Prof. Benson立刻就講到了<a href="https://www.youtube.com/watch?v=GQvEk8Rsyd4&amp;list=PLtvno3VRDR_jMAJcNY1n4pnP5kXtPOmVk&amp;index=3&amp;t=222s">更好的定義</a>，若滿足P(A|B) = P(A)則A、B兩事件為獨立事件。</p><p>根據上面的理解“P ( X | Y )可以理解成只是樣本空間S變成了Y而已”，即Y的樣本空間跟X完全沒有關係，比如X的樣本空間是{o1, o2, …, on}，Y的樣本空間是{p1, p2, …, pn}，事件Y可能是{p1, p2, p3}，完全不影響這個世界裡事件X發生與否，這裡的Y實際上是{p1, p2, p3, o1, o2, …, on}。</p><p>非獨立事件的conditional probability是這樣的：假如實驗是投一個6面骰子一次，樣本空間是{1,2,3,4,5,6}，X事件是扔出{1,5,6}，Y事件是扔出大數{4,5,6}，那X｜Y的樣本空間就變成了{4,5,6}即X只可能是扔出了大數中的某個數，o1∩Y=∅，X就變成{5,6}，P(X｜Y)則=P({5,6})/P({4,5,6})=2/3。</p><h2 id="Graph-Scheme"><a href="#Graph-Scheme" class="headerlink" title="Graph Scheme"></a><strong>Graph Scheme</strong></h2><p><a href="https://www.youtube.com/watch?v=GQvEk8Rsyd4&amp;list=PLtvno3VRDR_jMAJcNY1n4pnP5kXtPOmVk&amp;index=3&amp;t=1307s">實驗分解。</a></p><h2 id="counting-method"><a href="#counting-method" class="headerlink" title="counting method"></a><strong>counting method</strong></h2><p>為什麼機率需要算排列組合，需要counting？<br>因為<strong>古典機率常假設每個outcome發生機率相同，</strong>所以計算某個事件的機率，等同於計算這個事件包含多少個outcome，像<a href="#probability-theory-basics機率基礎">上面</a>計算事件空間那樣，機率（事件）=outcome數/事件空間元素數。所以機率問題等於counting問題。</p><p>counting前的判斷：</p><ul><li>distinguishable? 可區分？</li><li>w/wo replacement? 有放回？</li><li>order matters or not? 關心順序嗎？<strong>if order matters then Permutation (排列問題) else Combination (組合問題)</strong></li></ul><h3 id="Permutation-order-matters"><a href="#Permutation-order-matters" class="headerlink" title="Permutation(order matters)"></a><strong>Permutation(order matters)</strong></h3><ul><li>w/o replacement<div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 16.28.23.png" loading="lazy" alt="若有n異物，從中依序取出K物，共有多少種結果？" width="50%" height="50%"></div></li><li>w/ replacement<div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 16.31.09.png" loading="lazy" alt="若有n異物，從中選取一物，選完後放回。依序選取k次，共有多少種結果？" width="50%" height="50%"></div></li></ul><h3 id="Combination-order-doesn’t-matter"><a href="#Combination-order-doesn’t-matter" class="headerlink" title="Combination(order doesn’t matter)"></a><strong>Combination(order doesn’t matter)</strong></h3><p>除以k!就是因為order doesn’t matter，要把order的排列除掉，也就是k個items的所有排列方式k!種（比如3個items有6種排列方式=3!）。</p><p>數學裡叫n choose k，n取k。</p><p>$C(n, k) = \binom{n}{k} = C_n^k = \frac{n!}{k!(n-k)!}$</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 18.38.19.png" loading="lazy" alt="二項式定理裡的係數就是這個所以C(n, k)也叫binomial coefficient" width="50%" height="50%"></div><h3 id="Multinomial"><a href="#Multinomial" class="headerlink" title="Multinomial"></a><strong>Multinomial</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 18.47.16.png" loading="lazy" alt="跟二項式係數類似" width="50%" height="50%"></div><h2 id="Random-Variable隨機變數"><a href="#Random-Variable隨機變數" class="headerlink" title="Random Variable隨機變數"></a><strong>Random Variable隨機變數</strong></h2><p>就是把outcome數字化的方式，讓推導更數學、更簡明。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 19.21.49.png" loading="lazy" alt="本質是一個mapping，是個函數，定義域是樣本空間值域是實數。" width="50%" height="50%"></div><ul><li>Discrete R.V. (離散隨機變數)</li><li>Continuous R.V. (連續隨機變數)<div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 20.55.38.png" loading="lazy" alt="隨機變數" width="50%" height="50%"></div></li></ul><p><strong>隨機變數的函數</strong>也是隨機變數，因為outcome的函數的函數也是outcome的函數。</p><h2 id="CDF-Cumulative-Distribution-Function"><a href="#CDF-Cumulative-Distribution-Function" class="headerlink" title="CDF (Cumulative Distribution Function)"></a><strong>CDF (Cumulative Distribution Function)</strong></h2><p>累計分佈函數$F_X(x) = P(X \le x)$。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 21.40.43.png" loading="lazy" alt="最有用的用途：計算X落在某範圍內的機率" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 21.53.48.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 22.21.28.png" loading="lazy" alt="discrete r.v.函數曲線是階梯式的，所以X的區間有沒有等於包不包含邊界區別很大" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 22.42.03.png" loading="lazy" alt="continuous r.v. P(x)=0" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 22.48.48.png" loading="lazy" alt="性質" width="50%" height="50%"></div><h2 id="PMF-Probability-Mass-Function"><a href="#PMF-Probability-Mass-Function" class="headerlink" title="PMF (Probability Mass Function)"></a><strong>PMF (Probability Mass Function)</strong></h2><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 22.57.17.png" loading="lazy" alt="一旦知道discrete r.v.的PMF，就完全掌握" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 23.16.30.png" loading="lazy" alt="對一個discrete random variable來說PMF和CDF可以互相推得" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-10-31 at 23.58.51.png" loading="lazy" alt="" width="50%" height="50%"></div><p>兩個函數一個是X=x(PMF)，一個是X&lt;=x(CDF)就是這個區別。<br>分布是把很多相似實驗總結成規律。</p><h2 id="Discrete-Probability-Distributions"><a href="#Discrete-Probability-Distributions" class="headerlink" title="Discrete Probability Distributions"></a><strong>Discrete Probability Distributions</strong></h2><h3 id="Bernoulli-Distribution"><a href="#Bernoulli-Distribution" class="headerlink" title="Bernoulli Distribution"></a><strong>Bernoulli Distribution</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 18.50.27.png" loading="lazy" alt="" width="50%" height="50%"></div><p>只care成功失敗，一次實驗，非1即0。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.15.44.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.19.18.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Binomial-Distribution"><a href="#Binomial-Distribution" class="headerlink" title="Binomial Distribution"></a><strong>Binomial Distribution</strong></h3><p>二項分布。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.22.22.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.27.03.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.28.59.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.31.08.png" loading="lazy" alt="" width="50%" height="50%"></div><p>n=1時退化成白努力分布。</p><h3 id="Uniform-Distribution"><a href="#Uniform-Distribution" class="headerlink" title="Uniform Distribution"></a><strong>Uniform Distribution</strong></h3><p>均勻分布。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 21.32.42.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 22.45.12.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 22.47.04.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Geometric-Distribution"><a href="#Geometric-Distribution" class="headerlink" title="Geometric Distribution"></a><strong>Geometric Distribution</strong></h3><p>几何分布。</p><p>$P_X(x)$等比級數，即幾何級數</p><p>$S_n=\frac{a_1\left(1-r^n\right)}{1-r}, r \neq 1$</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 22.54.08.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 22.59.08.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.17.12.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.21.49.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Pascal-Distribution"><a href="#Pascal-Distribution" class="headerlink" title="Pascal Distribution"></a><strong>Pascal Distribution</strong></h3><p>k=1就退化成Geometric分布。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.25.45.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.34.44.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.44.20.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.48.35.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Poisson-Distribution"><a href="#Poisson-Distribution" class="headerlink" title="Poisson Distribution"></a><strong>Poisson Distribution</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-01 at 23.51.42.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 00.12.09.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 00.18.37.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 00.19.01.png" loading="lazy" alt="" width="50%" height="50%"></div><h2 id="Probability-Density-Function"><a href="#Probability-Density-Function" class="headerlink" title="Probability Density Function"></a><strong>Probability Density Function</strong></h2><p>PDF就是CDF的微分！</p><p>積分constant不用擔心，可以通過-無窮=0等邊界條件唯一確定。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 14.42.37.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.12.29.png" loading="lazy" alt="why mass和density，這張圖一目了然，continuous不能有PMF，就是顧名思義“質量”和“密度”的關係" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.18.20.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.23.02.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.26.34.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.28.21.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.28.56.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.29.17.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.33.07.png" loading="lazy" alt="](https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.31.32.png) ![" width="50%" height="50%"></div><h3 id="Uniform-Distribution-1"><a href="#Uniform-Distribution-1" class="headerlink" title="Uniform Distribution"></a><strong>Uniform Distribution</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.41.03.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.43.14.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Exponential-Distribution"><a href="#Exponential-Distribution" class="headerlink" title="Exponential Distribution"></a><strong>Exponential Distribution</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.48.27.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.50.39.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Gamma-Erlang-Distribution"><a href="#Gamma-Erlang-Distribution" class="headerlink" title="Gamma / Erlang Distribution"></a><strong>Gamma / Erlang Distribution</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.58.38.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.59.04.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 15.59.35.png" loading="lazy" alt="" width="50%" height="50%"></div><h2 id="Normal-Distribution"><a href="#Normal-Distribution" class="headerlink" title="Normal Distribution"></a><strong>Normal Distribution</strong></h2><p>終於來到elo的重點。</p><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 16.01.02.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 16.02.15.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 16.03.52.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-02 at 16.05.08.png" loading="lazy" alt="" width="50%" height="50%"></div><p>比如gaussian，為什麼單獨對standard normal建表？因為：。。。</p><p><a href="https://en.wikipedia.org/wiki/Normal_distribution">https://en.wikipedia.org/wiki/Normal_distribution</a></p><svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="24.077ex" height="7.176ex" style="vertical-align: -2.838ex;" viewBox="0 -1867.7 10366.4 3089.6" role="img" focusable="false" aria-labelledby="MathJax-SVG-1-Title"><title id="MathJax-SVG-1-Title"></title><defs aria-hidden="true"><path stroke-width="1" id="E1-MJMATHI-66" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"/><path stroke-width="1" id="E1-MJMAIN-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/><path stroke-width="1" id="E1-MJMATHI-78" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/><path stroke-width="1" id="E1-MJMAIN-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/><path stroke-width="1" id="E1-MJMAIN-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/><path stroke-width="1" id="E1-MJMAIN-31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/><path stroke-width="1" id="E1-MJMAIN-32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"/><path stroke-width="1" id="E1-MJMATHI-3C0" d="M132 -11Q98 -11 98 22V33L111 61Q186 219 220 334L228 358H196Q158 358 142 355T103 336Q92 329 81 318T62 297T53 285Q51 284 38 284Q19 284 19 294Q19 300 38 329T93 391T164 429Q171 431 389 431Q549 431 553 430Q573 423 573 402Q573 371 541 360Q535 358 472 358H408L405 341Q393 269 393 222Q393 170 402 129T421 65T431 37Q431 20 417 5T381 -10Q370 -10 363 -7T347 17T331 77Q330 86 330 121Q330 170 339 226T357 318T367 358H269L268 354Q268 351 249 275T206 114T175 17Q164 -11 132 -11Z"/><path stroke-width="1" id="E1-MJMATHI-3C3" d="M184 -11Q116 -11 74 34T31 147Q31 247 104 333T274 430Q275 431 414 431H552Q553 430 555 429T559 427T562 425T565 422T567 420T569 416T570 412T571 407T572 401Q572 357 507 357Q500 357 490 357T476 358H416L421 348Q439 310 439 263Q439 153 359 71T184 -11ZM361 278Q361 358 276 358Q152 358 115 184Q114 180 114 178Q106 141 106 117Q106 67 131 47T188 26Q242 26 287 73Q316 103 334 153T356 233T361 278Z"/><path stroke-width="1" id="E1-MJMAIN-221A" d="M95 178Q89 178 81 186T72 200T103 230T169 280T207 309Q209 311 212 311H213Q219 311 227 294T281 177Q300 134 312 108L397 -77Q398 -77 501 136T707 565T814 786Q820 800 834 800Q841 800 846 794T853 782V776L620 293L385 -193Q381 -200 366 -200Q357 -200 354 -197Q352 -195 256 15L160 225L144 214Q129 202 113 190T95 178Z"/><path stroke-width="1" id="E1-MJMATHI-65" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/><path stroke-width="1" id="E1-MJMAIN-2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/><path stroke-width="1" id="E1-MJMATHI-3BC" d="M58 -216Q44 -216 34 -208T23 -186Q23 -176 96 116T173 414Q186 442 219 442Q231 441 239 435T249 423T251 413Q251 401 220 279T187 142Q185 131 185 107V99Q185 26 252 26Q261 26 270 27T287 31T302 38T315 45T327 55T338 65T348 77T356 88T365 100L372 110L408 253Q444 395 448 404Q461 431 491 431Q504 431 512 424T523 412T525 402L449 84Q448 79 448 68Q448 43 455 35T476 26Q485 27 496 35Q517 55 537 131Q543 151 547 152Q549 153 557 153H561Q580 153 580 144Q580 138 575 117T555 63T523 13Q510 0 491 -8Q483 -10 467 -10Q446 -10 429 -4T402 11T385 29T376 44T374 51L368 45Q362 39 350 30T324 12T288 -4T246 -11Q199 -11 153 12L129 -85Q108 -167 104 -180T92 -202Q76 -216 58 -216Z"/><path stroke-width="1" id="E1-MJMAIN-2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"/></defs><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)" aria-hidden="true"> <use xlink:href="#E1-MJMATHI-66" x="0" y="0"/> <use xlink:href="#E1-MJMAIN-28" x="550" y="0"/> <use xlink:href="#E1-MJMATHI-78" x="940" y="0"/> <use xlink:href="#E1-MJMAIN-29" x="1512" y="0"/> <use xlink:href="#E1-MJMAIN-3D" x="2179" y="0"/><g transform="translate(3236,0)"><g transform="translate(120,0)"><rect stroke="none" width="3054" height="60" x="0" y="220"/> <use xlink:href="#E1-MJMAIN-31" x="1276" y="676"/><g transform="translate(60,-961)"> <use xlink:href="#E1-MJMAIN-221A" x="0" y="80"/><rect stroke="none" width="2100" height="60" x="833" y="821"/><g transform="translate(833,0)"> <use xlink:href="#E1-MJMAIN-32" x="0" y="0"/> <use xlink:href="#E1-MJMATHI-3C0" x="500" y="0"/><g transform="translate(1074,0)"> <use xlink:href="#E1-MJMATHI-3C3" x="0" y="0"/> <use transform="scale(0.707)" xlink:href="#E1-MJMAIN-32" x="810" y="408"/></g></g></g></g></g><g transform="translate(6530,0)"> <use xlink:href="#E1-MJMATHI-65" x="0" y="0"/><g transform="translate(466,681)"> <use transform="scale(0.707)" xlink:href="#E1-MJMAIN-2212" x="0" y="0"/><g transform="translate(550,0)"><g transform="translate(120,0)"><rect stroke="none" width="2033" height="60" x="0" y="146"/><g transform="translate(60,515)"> <use transform="scale(0.574)" xlink:href="#E1-MJMAIN-28" x="0" y="0"/> <use transform="scale(0.574)" xlink:href="#E1-MJMATHI-78" x="389" y="0"/> <use transform="scale(0.574)" xlink:href="#E1-MJMAIN-2212" x="962" y="0"/> <use transform="scale(0.574)" xlink:href="#E1-MJMATHI-3BC" x="1740" y="0"/><g transform="translate(1345,0)"> <use transform="scale(0.574)" xlink:href="#E1-MJMAIN-29" x="0" y="0"/> <use transform="scale(0.574)" xlink:href="#E1-MJMAIN-32" x="389" y="364"/></g></g><g transform="translate(536,-567)"> <use transform="scale(0.574)" xlink:href="#E1-MJMAIN-32" x="0" y="0"/><g transform="translate(287,0)"> <use transform="scale(0.574)" xlink:href="#E1-MJMATHI-3C3" x="0" y="0"/> <use transform="scale(0.574)" xlink:href="#E1-MJMAIN-32" x="572" y="288"/></g></g></g></g></g></g> <use xlink:href="#E1-MJMAIN-2E" x="10087" y="0"/></g></svg><h2 id="PDF-PMF-vs-CDF"><a href="#PDF-PMF-vs-CDF" class="headerlink" title="PDF/PMF vs. CDF"></a><strong>PDF/PMF vs. CDF</strong></h2><p>關於PDF vs. PMF上面（why mass和density，這張圖一目了然，continuous不能有PMF，就是顧名思義“質量”和“密度”的關係）寫過了。</p><p>PDF/PMF 就是 P(x = …)；<br>CDF 就是P(… &lt; x &lt; …)。</p><p>也是一目了然。</p><h2 id="Central-Limit-Theorem-中央極限定理"><a href="#Central-Limit-Theorem-中央極限定理" class="headerlink" title="Central Limit Theorem(中央極限定理)"></a><strong>Central Limit Theorem(中央極限定理)</strong></h2><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 13.46.25.png" loading="lazy" alt="convolution" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.39.14.png" loading="lazy" alt="uniform分布的獨立事件convolution完變成了gaussian" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.40.21.png" loading="lazy" alt="exponential分布的獨立事件convolution完變成了gaussian" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.43.13.png" loading="lazy" alt="laplacian分布也是一樣，可以知道continuous r.v.都是一樣" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.44.05.png" loading="lazy" alt="多個discrete隨機變數之和也是" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.44.49.png" loading="lazy" alt="不只uniform，geometric也是一樣" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.52.56.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="應用"><a href="#應用" class="headerlink" title="應用"></a><strong>應用</strong></h3><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 15.55.30.png" loading="lazy" alt="牛逼" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-03 at 16.16.35.png" loading="lazy" alt="牛逼" width="50%" height="50%"></div><p>為什麼mean是probability，variance是probability(1- probability)，因為Bernoulli的X非1即0，所以expectation是1*probability + 0*(1- probability)，$Var(X)=E(X^2)-E(X)^2 = probability - probability^2$。</p><h2 id="失憶性"><a href="#失憶性" class="headerlink" title="失憶性"></a><strong>失憶性</strong></h2><div align=center><img src="https://wiki.v2beach.cn/assets/IMG_0608.jpg" loading="lazy" alt="條件機率的概率分佈計算方式" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-04 at 00.25.03.png" loading="lazy" alt="0.8^(x-1)*0.2還是geometric distribution" width="50%" height="50%"></div><div align=center><img src="https://wiki.v2beach.cn/assets/Screenshot 2024-11-04 at 00.26.13.png" loading="lazy" alt="機率很重要的common sense" width="50%" height="50%"></div><h2 id="似然likelihood"><a href="#似然likelihood" class="headerlink" title="似然likelihood"></a><strong>似然likelihood</strong></h2><ul><li>概率（Probability） 是指给定某个已知的模型和参数时，观测数据出现的可能性。<ul><li>已知模型和参数，计算数据出现的概率。</li></ul></li><li>似然（Likelihood） 则是指给定观测数据的情况下，某个特定模型参数出现的可能性。<ul><li>已知数据，计算特定模型参数的可能性。</li></ul></li></ul><p>似然（Likelihood） 通常指的是一个函数，用来衡量某个特定参数值在给定数据集下生成这些数据的可能性。<strong>它并不是概率，虽然其形式和概率类似</strong>，但有所不同。具体来说，似然是<strong>根据已知数据和模型参数来计算某个参数值“合理性”的度量。</strong></p><p>在统计学中，<strong>我们有一个统计模型，其中包含一些我们想要估计的未知参数。</strong>我们通过观测到的数据来推断这些参数的值。<br>似然函数（Likelihood Function）是指给定某个参数值后，生成观测数据的概率（或可能性）。我们通过似然函数来评估不同参数值的合理性。</p><h2 id="先驗？後驗？"><a href="#先驗？後驗？" class="headerlink" title="先驗？後驗？"></a><strong>先驗？後驗？</strong></h2><p>貝葉斯定理：<br>$P(\theta \mid D) = \frac{P(D \mid \theta) P(\theta)}{P(D)}$，<br><strong>其中：</strong></p><p>$P(\theta \mid D)$ 是后验概率(Posterior Probability)，即在观察到数据 $D$ 后，参数 $\theta$ 的概率分布。</p><blockquote><p>在贝叶斯框架下，后验（posterior）是我们通过已有的数据（如症状、检查结果等）对某个假设或未知参数（如是否患病）的认知的更新。</p></blockquote><p>$P(D \mid \theta)$ 是似然函数，即在给定参数 $\theta$ 的条件下，观察到数据 $D$ 的概率。</p><p>$P(\theta)$ 是先验概率(Prior Probability)，即在没有观察到数据时，关于参数 $\theta$ 的先验知识。</p><blockquote><p>先验（Prior）是指我们在没有观察到数据之前，对某个参数或假设的信念或假设。也就是说，先验是我们根据以前的知识、经验或理论对某个未知量（如参数、假设等）做出的初步估计。</p></blockquote><p>$P(D)$ 是证据或标准化常数，确保后验概率的总和为1。</p><p><strong>举个例子：</strong></p><p>假设你在诊断一个病人是否患有某种疾病，$\theta$ 代表病人是否患病（例如 $\theta = 1$ 代表患病，$\theta = 0$ 代表不患病）。你有以下信息：</p><p>先验 $P(\theta)$：表示你在没有任何诊断数据时，认为病人患病的概率。</p><p>似然 $P(D \mid \theta)$：表示在给定病人确实患病或未患病的情况下，得到某种诊断结果 $D$ 的概率。</p><p>后验 $P(\theta \mid D)$：表示在获取到诊断结果 $D$ 后，病人患病的更新概率。</p><p>應用場景是我們經常需要已知 $P(\theta \mid D)$ 求 $P(D \mid \theta)$（或反之），那麼假如我們還知道 $P(\theta)$ 就可以用貝葉斯公式求解，其中 $P(D)$ 根據<a href="#conditional-probability條件機率貝氏定理">上面條件機率中的Law of Total Probability(切麵包)</a>可以通過 $P(D) = =\sum\limits_{i=1}^{n} P(\theta_i)P(D \mid \theta_i)$ 求解。</p><p>同樣用上面的例子，“$P(D)$”是診斷結果的機率，可以通過無數個獨立的病人是否患病 $P(\theta_i)$ 乘基於此的診斷結果 $P(D \mid \theta_i)$ 之和求得。</p><h2 id="用似然解釋Neural-Nets"><a href="#用似然解釋Neural-Nets" class="headerlink" title="用似然解釋Neural Nets"></a><strong>用似然解釋Neural Nets</strong></h2><p>監督學習模型和無監督學習模型都可以看作是一個似然函數。</p><ol><li>似然函數與預測的關係：<ul><li>对于分类任务，假设我们有 $C$ 个类别，并且通过神经网络得到了每个类别的概率 $p(y_i = c | x_i; \theta)$，其中 $c$ 是类别标签，$y_i$ 是样本 $i$ 的真实标签。</li><li>这个概率分布表示了模型对每个类别的“信心”。假设真实标签是 $y_i = c$，那么我们关心的是模型预测该标签的概率 $p(y_i = c | x_i; \theta)$。</li></ul></li><li>模型目標轉為<strong>最大/極大化似然函數（maximum likelihood estimation，简作MLE）</strong>：<ul><li>对于所有训练数据点，似然函数 $L(\theta)$ 是所有预测的概率的乘积：<script type="math/tex; mode=display"> L(\theta) = \prod_{i=1}^{N} p(y_i | x_i; \theta)</script></li><li>这个似然函数表示了在给定所有训练数据的条件下，模型参数 $\theta$ 下观测到实际标签的“概率”或“可能性”。</li><li>模型的目標就是找到 $\theta$ 讓模型預測到正確類別 $y_i$ 的概率 $p(y_i)$ 最大化。</li></ul></li><li>原來的乘積轉為對數：<ul><li>为了简化计算和优化，通常使用对数似然（log-likelihood），这将乘积转化为和（因為我們並不關心 $L$ 的絕對值而只希望越大越好）：<script type="math/tex; mode=display">\log L(\theta) = \sum_{i=1}^{N} \log p(y_i | x_i; \theta)</script><ul><li>对数似然的作用是将概率分布的乘法转化为对数的加法，便于优化（尤其是在梯度下降中）。</li></ul></li></ul></li><li><strong>负对数似然（NLL）</strong>：<ul><li><strong>由于我们通常使用最大化对数似然来训练模型，但在优化过程中更常见的是最小化损失函数。</strong>因此，我们使用负对数似然（negative log-likelihood）作为损失函数：<script type="math/tex; mode=display">\text{NLL}(\theta) = -\sum_{i=1}^{N} \log p(y_i | x_i; \theta)</script></li></ul></li><li>其實交叉熵softmax或者MSE(L2) Loss都可以看作是一種NLL Loss：<ul><li>当我们假设数据点 $y_i$ 是从一个高斯（正态）分布中采样的，且预测值 $\hat{y}_i$ 也是一个高斯分布的均值时，NLL 会转化为 MSE。<ul><li>假设我们有一组样本 $(x_i, y_i)$，我们的目标是最小化 NLL，使得模型的预测概率最大化。如果我们假设数据点 $y_i$ 服从一个高斯分布，均值为 $\hat{y}_i$，方差为 $\sigma^2$，那么似然函数为：<script type="math/tex; mode=display">p(y_i | \hat{y}_i, \sigma^2) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp\left( -\frac{(y_i - \hat{y}_i)^2}{2 \sigma^2} \right)</script></li><li>NLL：<script type="math/tex; mode=display">\text{NLL} = - \sum_{i=1}^{N} \log p(y_i | \hat{y}_i, \sigma^2) = \sum_{i=1}^{N} \frac{(y_i - \hat{y}_i)^2}{2 \sigma^2} + \frac{N}{2} \log(2 \pi \sigma^2)</script></li><li>可以看到，这里有两部分：一部分是 $(y_i - \hat{y}_i)^2$，另一部分是常数项。我们通常关心的是与参数优化相关的部分，因此常数项可以忽略不计，剩下的就是一个类似于均方误差的项：<script type="math/tex; mode=display">\text{NLL} \propto \sum_{i=1}^{N} (y_i - \hat{y}_i)^2</script></li></ul></li><li>在二分类问题中，我们假设每个样本的标签 $y_i$ 是从 Bernoulli 分布中采样的，模型预测的是一个表示为类别 1 的概率 $p(y_i = 1 | x_i; \theta)$。假设输出概率是 $\hat{y}_i$，那么负对数似然（NLL）为：<script type="math/tex; mode=display">  \text{NLL} = - \sum_{i=1}^{N} \left[ y_i \log \hat{y}_i + (1 - y_i) \log (1 - \hat{y}_i) \right]</script><ul><li>这实际上就是 $\textbf{二分类交叉熵（Binary Cross-Entropy）}$ 损失函数。因此，二分类交叉熵与 NLL 是等价的，它表示了模型预测概率与真实标签之间的差异。</li></ul></li></ul></li></ol><p>有标签数据（监督学习）：似然函数通常是</p><script type="math/tex; mode=display">L(\theta \mid X, Y) = P(Y \mid X, \theta)</script><p>我們希望最大化给定输入 $X$ 时，输出 $Y$ 的条件概率。</p><p>无标签数据（无监督学习）：似然函数是</p><script type="math/tex; mode=display">L(\theta \mid X) = P(X \mid \theta)</script><p>我們希望最大化给定数据 $X$ 时，原分布数据出现的概率。</p><h2 id="用貝葉斯解釋Neural-Nets"><a href="#用貝葉斯解釋Neural-Nets" class="headerlink" title="用貝葉斯解釋Neural Nets"></a><strong>用貝葉斯解釋Neural Nets</strong></h2><h3 id="1-贝叶斯公式"><a href="#1-贝叶斯公式" class="headerlink" title="1. 贝叶斯公式"></a><strong>1. 贝叶斯公式</strong></h3><p>根据贝叶斯定理，后验概率分布可以表示为：</p><script type="math/tex; mode=display">P(\theta \mid X, Y) = \frac{P(Y \mid X, \theta) P(\theta)}{P(Y \mid X)}</script><p>其中：</p><ul><li>$ P(\theta \mid X, Y) $ 是后验分布，表示在观测数据 $ X $ 和标签 $ Y $ 后，参数 $ \theta $ 的分布。</li><li>$ P(Y \mid X, \theta) $ 是<strong>似然函数</strong>，表示在给定参数 $ \theta $ 和输入 $ X $ 时，标签 $ Y $ 出现的概率。</li><li>$ P(\theta) $ 是<strong>先验分布</strong>，表示我们对参数 $ \theta $ 的先验信念。</li><li>$ P(Y \mid X) $ 是<strong>边际似然</strong>或证据：</li></ul><script type="math/tex; mode=display">P(Y \mid X) = \int P(Y \mid X, \theta) P(\theta) d\theta</script><h3 id="2-最大化后验概率MAP"><a href="#2-最大化后验概率MAP" class="headerlink" title="2. 最大化后验概率MAP"></a><strong>2. 最大化后验概率MAP</strong></h3><p><strong>即最大後驗概率估計(Maximum A Posteriori estimation, MAP)。A posteriori is from Latin ā posteriōrī, which means literally, “from what is later.”</strong></p><p>为了找到最可能的参数值 $ \theta $，我们希望最大化后验分布 $ P(\theta \mid X, Y) $：</p><script type="math/tex; mode=display">\hat{\theta}_{\text{MAP}} = \arg\max_{\theta} P(\theta \mid X, Y)</script><p>由于分母 $ P(Y \mid X) $ 与参数 $ \theta $ 无关，是一个常数，因此可以忽略。这样，最大化后验概率等价于最大化分子：</p><script type="math/tex; mode=display">\hat{\theta}_{\text{MAP}} = \arg\max_{\theta} \left[ P(Y \mid X, \theta) P(\theta) \right]</script><h3 id="3-对数变换"><a href="#3-对数变换" class="headerlink" title="3. 对数变换"></a><strong>3. 对数变换</strong></h3><p>为了简化计算，我们对目标函数取对数。由于对数函数是单调递增的，最大化对数与最大化原函数等价：</p><script type="math/tex; mode=display">\log P(\theta \mid X, Y) = \log \left[ P(Y \mid X, \theta) P(\theta) \right]</script><p>利用对数的乘法法则，这可以拆分为两部分：</p><script type="math/tex; mode=display">\log P(\theta \mid X, Y) = \log P(Y \mid X, \theta) + \log P(\theta)</script><h3 id="4-最小化负对数损失"><a href="#4-最小化负对数损失" class="headerlink" title="4. 最小化负对数损失"></a><strong>4. 最小化负对数损失</strong></h3><p>在优化中，我们通常通过<strong>最小化</strong>损失函数来实现参数优化。因此，我们可以将上述公式转化为<strong>最小化负对数后验概率</strong>：</p><script type="math/tex; mode=display">\hat{\theta}_{\text{MAP}} = \arg\min_{\theta} \left[ -\log P(Y \mid X, \theta) - \log P(\theta) \right]</script><p>这就得到了 <strong>MAP 损失函数</strong>：</p><script type="math/tex; mode=display">\text{MAP loss} = -\log P(Y \mid X, \theta) - \log P(\theta)</script><ul><li>第一项 $ -\log P(Y \mid X, \theta) $ 是<strong>负对数似然（NLL）</strong>，衡量模型预测的准确性。</li><li>第二项 $ -\log P(\theta) $ 是<strong>负对数先验</strong>，对参数进行正则化，鼓励模型参数满足先验分布。</li></ul><h3 id="5-特殊情况：最大似然估计（MLE）"><a href="#5-特殊情况：最大似然估计（MLE）" class="headerlink" title="5. 特殊情况：最大似然估计（MLE）"></a><strong>5. 特殊情况：最大似然估计（MLE）</strong></h3><p>当先验 $ P(\theta) $ 是均匀分布时，先验项 $ \log P(\theta) $ 是一个常数。此时，MAP 退化为最大似然估计（MLE）：</p><script type="math/tex; mode=display">\hat{\theta}_{\text{MLE}} = \arg\min_{\theta} \left[ -\log P(Y \mid X, \theta) \right]</script><p>即，最大似然估计只关注似然，不考虑先验。</p><hr><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h3><ul><li><strong>MAP</strong> 最大化的是后验概率 $ P(\theta \mid X, Y) $，通过结合似然和先验来优化参数。</li><li><strong>MLE</strong> 是 MAP 的一种特殊情况，当先验均匀时，MAP 退化为 MLE，只最大化似然。</li></ul><h2 id="頻率學派vs-貝葉斯"><a href="#頻率學派vs-貝葉斯" class="headerlink" title="頻率學派vs.貝葉斯"></a><strong>頻率學派vs.貝葉斯</strong></h2><p><a href="https://www.zhangzhenhu.com/glm/source/贝叶斯估计/content.html">這篇文章</a>講頻率學派和貝葉斯學派的區別講得還不錯，這裡面的共軛先驗跟我在LDA數學八卦裡學的學的Beta-Binomial共軛還不太是一回事。</p><div class="table-container"><table><thead><tr><th><strong>推导步骤</strong></th><th><strong>最大似然估计（MLE）</strong></th><th><strong>最大后验估计（MAP）</strong></th></tr></thead><tbody><tr><td><strong>目标</strong></td><td>最大化数据的似然函数，即找到最可能的参数值 $ \theta $。</td><td>最大化后验分布 $ P(\theta \mid X, Y) $，结合了似然和先验。</td></tr><tr><td><strong>后验分布 / 似然</strong></td><td>无先验，仅依赖似然：</td><td>依赖似然和先验：</td></tr><tr><td></td><td>$ P(\theta \mid X, Y) \propto P(Y \mid X, \theta) $</td><td>$ P(\theta \mid X, Y) \propto P(Y \mid X, \theta) P(\theta) $</td></tr><tr><td><strong>贝叶斯公式</strong></td><td>$ P(\theta \mid X, Y) = \frac{P(Y \mid X, \theta) P(\theta)}{P(Y)} $</td><td>$ P(\theta \mid X, Y) = \frac{P(Y \mid X, \theta) P(\theta)}{P(Y \mid X)} $</td></tr><tr><td><strong>推导目标</strong></td><td>最大化似然函数（$ P(Y \mid X, \theta) $）</td><td>最大化后验分布（结合似然和先验）</td></tr><tr><td><strong>取对数后</strong></td><td>对似然函数取对数得到对数似然：</td><td>对后验分布取对数得到对数后验：</td></tr><tr><td></td><td>$ \log P(Y \mid X, \theta) $</td><td>$ \log P(Y \mid X, \theta) + \log P(\theta) $</td></tr><tr><td><strong>损失函数</strong></td><td>负对数似然（Negative Log-Likelihood, NLL）损失函数：</td><td>MAP损失函数：</td></tr><tr><td></td><td>$ \text{NLL}_{\text{MLE}} = -\log P(Y \mid X, \theta) $</td><td>$ \text{MAP loss} = -\log P(Y \mid X, \theta) - \log P(\theta) $</td></tr><tr><td><strong>优化目标</strong></td><td>最小化负对数似然（NLL）：</td><td>最小化MAP损失函数：</td></tr><tr><td></td><td>$ \hat{\theta}_{\text{MLE}} = \arg\min_{\theta} \left[ -\log P(Y \mid X, \theta) \right] $</td><td>$ \hat{\theta}_{\text{MAP}} = \arg\min_{\theta} \left[ -\log P(Y \mid X, \theta) - \log P(\theta) \right] $</td></tr><tr><td><strong>是否包含先验</strong></td><td>不包含先验。</td><td>包含先验 $ P(\theta) $，通过 $ -\log P(\theta) $ 正则化参数。</td></tr><tr><td><strong>在数据稀缺时的行为</strong></td><td>在数据稀缺时，MLE 可能会过拟合，因为它仅关注最大化似然，没有考虑任何先验。</td><td>在数据稀缺时，MAP 通过引入先验能够稳定估计，避免过拟合。</td></tr><tr><td><strong>特殊情况</strong></td><td>当先验 $ P(\theta) $ 是均匀分布时，MAP 退化为 MLE。</td><td>当先验是均匀分布时，MAP 退化为最大似然估计（MLE）。</td></tr></tbody></table></div><h1 id="dota-mmr"><a href="#dota-mmr" class="headerlink" title="dota mmr"></a><strong>dota mmr</strong></h1><p><a href="https://dota2.fandom.com/wiki/Matchmaking">https://dota2.fandom.com/wiki/Matchmaking</a><br><a href="https://dota2.fandom.com/wiki/Matchmaking_Rating">https://dota2.fandom.com/wiki/Matchmaking_Rating</a></p><p>Valve has stated that matchmaking tries to fulfil several criteria:</p><ul><li>The teams are balanced. (Each team has a 50% chance to win. It’s based on Elo rating.)</li><li>The discrepancy in skill between the most and least skilled player in the match is minimized.</li><li>The highest skill Radiant player should be close to the same skill as the highest skill Dire player.</li><li>The discrepancy between experience (measured by the number of games played) between the least experienced player and the most experienced player is minimized.</li><li>Each team contains about the same number of parties.</li><li>Five-player parties can only be matched against other five-player parties.</li><li>Players’ language preferences contains a common language.</li><li>Wait times shouldn’t be too long.</li></ul><h2 id="elo-hell"><a href="#elo-hell" class="headerlink" title="elo hell"></a><strong>elo hell</strong></h2><p>Elo hell (also known as MMR hell) is a video gaming term used in MOBAs and other multiplayer online games with competitive modes. It refers to portions of the matchmaking ranking spectrum where individual matches are of poor quality, and are often determined by factors <strong>such as poor team coordination</strong> which are perceived to be <strong>outside the individual player’s control.</strong> This ostensibly makes it difficult for skilled players to “climb” up the matchmaking ranking (and out of Elo hell), due to the difficulty of consistently winning games under these conditions. Its existence in various games has been debated, and <strong>some game developers have called it an illusion caused by cognitive bias.</strong></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/pics/ArpadElo.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="Elo" scheme="http://blog.v2beach.cn/tags/Elo/"/>
    
      <category term="Matchmaking" scheme="http://blog.v2beach.cn/tags/Matchmaking/"/>
    
      <category term="Probabiltiy" scheme="http://blog.v2beach.cn/tags/Probabiltiy/"/>
    
  </entry>
  
  <entry>
    <title>cs231n</title>
    <link href="http://blog.v2beach.cn/2024/09/24/cs231n-1/"/>
    <id>http://blog.v2beach.cn/2024/09/24/cs231n-1/</id>
    <published>2024-09-24T08:38:31.000Z</published>
    <updated>2025-06-11T10:47:14.513Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/pics/cs231n/life paths.jpeg" alt="it&#39;s never too late"></p><a id="more"></a><ul><li><a href="#Python-Keywords">Python Keywords</a><ul><li><a href="#assert">assert</a><ul><li><a href="#in-dictionary">in dictionary</a></li><li><a href="#in-python">in python</a></li></ul></li><li><a href="#yield">yield</a><ul><li><a href="#in-dictionary-1">in dictionary</a></li><li><a href="#in-python-1">in python</a></li><li><a href="#generator-what">generator? what?</a></li><li><a href="#generator-expression">generator expression</a></li><li><a href="#yield写斐波那契数列">yield写斐波那契数列</a></li></ul></li><li><a href="#numpy-dot">numpy dot</a><ul><li><a href="#範例">範例</a></li><li><a href="#如果明確定義行或列向量">如果明確定義行或列向量</a></li></ul></li><li><a href="#lambda">lambda</a><ul><li><a href="#基本语法">基本语法</a></li><li><a href="#示例">示例</a></li><li><a href="#优缺点">优缺点</a></li></ul></li><li><a href="#广播broadcast">广播broadcast</a></li><li><a href="#random">random</a></li><li><a href="#星號">*</a></li><li><a href="#pad">pad</a></li><li><a href="#array改變形狀">array改變形狀</a><ul><li><a href="#transpose和moveaxis">transpose和moveaxis</a></li><li><a href="#reshape">reshape</a></li></ul></li><li><a href="#array擴展">array擴展</a></li><li><a href="#concatenation拼接">concatenation拼接</a></li><li><a href="#torchvision-transforms">torchvision.transforms</a><ul><li><a href="#Compose">Compose()</a></li><li><a href="#Normalize">Normalize()</a></li></ul></li><li><a href="#torch-nn-Sequential">torch.nn.Sequential()</a></li><li><a href="#DataLoader">DataLoader</a><ul><li><a href="#PIL-Pillow">PIL/Pillow</a></li><li><a href="#為什麼需要DataLoader？">為什麼需要DataLoader？</a></li></ul></li><li><a href="#PyTorch-vs-Numpy">PyTorch vs. Numpy</a></li><li><a href="#requires-grad和Pytorch裡的computational-graph">requires_grad和Pytorch裡的computational graph</a><ul><li><a href="#關於torch-no-grad">關於torch.no_grad()</a></li></ul></li><li><a href="#傳參：可變對象-vs-不可變對象">傳參：可變對象 vs. 不可變對象</a></li><li><a href="#model-eval">model.eval()</a></li><li><a href="#零碎pytorch">零碎pytorch</a></li><li><a href="#keepdim-True">keepdim=True</a></li><li><a href="#使用預訓練resent">使用預訓練resent</a></li></ul></li><li><a href="#數學">數學</a><ul><li><a href="#矩阵计算加速">矩阵计算加速</a><ul><li><a href="#1-L2距离矩阵">1. L2距离矩阵</a></li><li><a href="#2-余弦相似度矩阵">2. 余弦相似度矩阵</a></li><li><a href="#3-矩阵-Frobenius-范数的平方">3. 矩阵 Frobenius 范数的平方</a></li><li><a href="#4-交叉熵损失的高效计算">4. 交叉熵损失的高效计算</a></li><li><a href="#5-矩阵的平方和（双循环的优化）">5. 矩阵的平方和（双循环的优化）</a></li><li><a href="#6-矩阵乘法优化（维度分解）">6. 矩阵乘法优化（维度分解）</a></li></ul></li><li><a href="#矩陣求導，要轉置">矩陣求導，要轉置</a></li><li><a href="#商的求导法则">商的求导法则</a></li><li><a href="#幂函数的求导公式">幂函数的求导公式</a></li><li><a href="#正則項的偏導">正則項的偏導</a></li><li><a href="#Adam優化版">Adam優化版</a></li><li><a href="#对数的商的性质">对数的商的性质</a></li><li><a href="#BN的梯度">BN的梯度</a><ul><li><a href="#1-Batch-Normalization-BN-层的梯度计算">1. Batch Normalization (BN) 层的梯度计算</a></li><li><a href="#2-Dropout-层的梯度计算">2. Dropout 层的梯度计算</a></li></ul></li><li><a href="#chain-rule-多元複合函數求導法則">chain rule 多元複合函數求導法則</a></li><li><a href="#一個變量的導數和他本身維度通常一致">一個變量的導數和他本身維度通常一致</a><ul><li><a href="#1-标量对矩阵的导数">1. 标量对矩阵的导数</a></li><li><a href="#2-矩阵对标量的导数">2. 矩阵对标量的导数</a></li><li><a href="#3-矩阵对矩阵的导数">3. 矩阵对矩阵的导数</a></li></ul></li></ul></li><li><a href="#Notes">Notes</a><ul><li><a href="#Lecture-1">Lecture 1</a></li><li><a href="#Lecture-2">Lecture 2</a></li><li><a href="#Lecture-3">Lecture 3</a><ul><li><a href="#chatgpt給的一個計算梯度的例子">chatgpt給的一個計算梯度的例子</a></li><li><a href="#示例函数">示例函数</a></li><li><a href="#1-计算梯度">1. 计算梯度</a></li><li><a href="#2-梯度的意义">2. 梯度的意义</a></li><li><a href="#3-如何找到梯度最大的方向">3. 如何找到梯度最大的方向</a></li></ul></li><li><a href="#Lecture-4-計算圖-local-gradient-and-chain-rule">Lecture 4 計算圖(local gradient and chain rule)</a><ul><li><a href="#Jacobian-矩阵计算示例">Jacobian 矩阵计算示例</a></li></ul></li><li><a href="#Lecture-5">Lecture 5</a><ul><li><a href="#CNN的卷積">CNN的卷積</a></li><li><a href="#信號處理的卷積計算signal-processing-convolution">信號處理的卷積計算signal processing convolution</a></li><li><a href="#deconvolution反卷積">deconvolution反卷積</a></li><li><a href="#右圖為什麼downsample太快不好？那為什麼還要pooling？現在不缺算力追求越多參數越好是不是不再需要太多downsample了？">右圖為什麼downsample太快不好？那為什麼還要pooling？現在不缺算力追求越多參數越好是不是不再需要太多downsample了？</a></li><li><a href="#Pooling-层的-stride-是否一般设置为没有-overlap？">Pooling 层的 stride 是否一般设置为没有 overlap？</a></li><li><a href="#為什麼max-pooling比average-pooling更commonly-used？">為什麼max pooling比average pooling更commonly used？</a></li></ul></li><li><a href="#Lecture-6">Lecture 6</a><ul><li><a href="#Activation-Functions-non-linearity-为什么输入要正负样本均衡？">Activation Functions(non-linearity) 为什么输入要正负样本均衡？</a></li><li><a href="#Data-Preprocessing">Data Preprocessing</a></li><li><a href="#Weight-Initialization">Weight Initialization</a></li><li><a href="#Batch-Normalization">Batch Normalization</a></li><li><a href="#Babysitting-the-Learning-Process">Babysitting the Learning Process</a></li><li><a href="#Hyperparameter-Optimization">Hyperparameter Optimization</a></li></ul></li><li><a href="#Lecture-7">Lecture 7</a><ul><li><a href="#Fancier-optimization">Fancier optimization</a></li><li><a href="#Regularization">Regularization</a></li><li><a href="#Transfer-Learning">Transfer Learning</a></li></ul></li><li><a href="#Lecture-8">Lecture 8</a><ul><li><a href="#CPU-vs-GPU">CPU vs GPU</a></li><li><a href="#Deep-Learning-Frameworks">Deep Learning Frameworks</a></li></ul></li><li><a href="#Lecture-9">Lecture 9</a><ul><li><a href="#LeNet-5">LeNet-5</a></li><li><a href="#AlexNet">AlexNet</a></li><li><a href="#ZFNet">ZFNet</a></li><li><a href="#VGG">VGG</a></li><li><a href="#GoogLeNet">GoogLeNet</a></li><li><a href="#ResNet">ResNet</a></li><li><a href="#Other-Architectures">Other Architectures</a></li></ul></li><li><a href="#Lecture-10">Lecture 10</a><ul><li><a href="#循環nn-vs-递归nn">循環nn vs. 递归nn</a></li><li><a href="#vanilla-RNN">vanilla RNN</a></li><li><a href="#attention">attention</a></li><li><a href="#Multilayers-RNN">Multilayers RNN</a></li><li><a href="#LSTM">LSTM</a></li><li><a href="#激活函數non-linearity">激活函數non-linearity</a></li></ul></li><li><a href="#Lecture-11">Lecture 11</a><ul><li><a href="#Semantic-Segmentation">Semantic Segmentation</a></li><li><a href="#Classification-Localization">Classification + Localization</a></li><li><a href="#回歸vs-分類-Classification-vs-Regression">回歸vs.分類 (Classification vs. Regression)</a></li><li><a href="#Object-Detection">Object Detection</a></li><li><a href="#Instance-Segmentation">Instance Segmentation</a></li></ul></li><li><a href="#Lecture-12">Lecture 12</a><ul><li><a href="#Weights-Visualization">Weights Visualization</a></li><li><a href="#Last-Layer-Visualization">Last Layer Visualization</a></li><li><a href="#Activations-Visualization">Activations Visualization</a></li></ul></li><li><a href="#Lecture-13">Lecture 13</a><ul><li><a href="#Unsupervised-Learning">Unsupervised Learning</a></li><li><a href="#Generative-Models">Generative Models</a></li><li><a href="#PixelRNN-CNN">PixelRNN/CNN</a></li><li><a href="#Auto-Encoders">Auto-Encoders</a></li><li><a href="#Variational-Auto-Encoders">Variational Auto-Encoders</a></li><li><a href="#Generative-Adversarial-Networks">Generative Adversarial Networks</a></li></ul></li><li><a href="#Lecture-14">Lecture 14</a><ul><li><a href="#What-is-Reinforcement-Learning">What is Reinforcement Learning?</a></li><li><a href="#Markov-Decision-Processes">Markov Decision Processes</a></li><li><a href="#Q-Learning">Q-Learning</a></li></ul></li><li><a href="#傳統-Q-learning">傳統 Q-learning</a></li><li><a href="#DQN-Deep-Q-Learning">DQN, Deep Q-Learning</a><ul><li><a href="#Policy">Policy</a></li><li><a href="#方法1-reward-和執行次數算-weighted-sum">方法1: reward 和執行次數算 weighted sum</a></li><li><a href="#更常用方法2-优势函数">更常用方法2: 优势函数</a></li></ul></li><li><a href="#Continue-Lecture-14-Reinforcement-Learning">Continue Lecture 14 Reinforcement Learning</a><ul><li><a href="#Policy-Gradients">Policy Gradients</a></li><li><a href="#REINFORCE-algorithm">REINFORCE algorithm</a></li></ul></li><li><a href="#Lecture-15">Lecture 15</a><ul><li><a href="#Algorithms-for-Efficient-Inference">Algorithms for Efficient Inference</a></li><li><a href="#Hardware-for-Efficient-Inference">Hardware for Efficient Inference</a></li><li><a href="#Algorithms-for-Efficient-Training">Algorithms for Efficient Training</a></li><li><a href="#Hardware-for-Efficient-Training">Hardware for Efficient Training</a></li></ul></li><li><a href="#Lecture-16">Lecture 16</a><ul><li><a href="#What-are-adversarial-examples">What are adversarial examples?</a></li><li><a href="#Why-do-they-happen">Why do they happen?</a></li><li><a href="#在高維度裡為什麼隨機找一個random-vector跟reference-vector很可能是正交的？有點反直覺？">在高維度裡為什麼隨機找一個random vector跟reference vector很可能是正交的？有點反直覺？</a></li><li><a href="#What-are-the-defenses">What are the defenses?</a></li></ul></li><li><a href="#Lecture-17-Attention-and-Transformers">Lecture 17 Attention and Transformers</a><ul><li><a href="#seq2seq-with-vanilla-rnn-or-lstm">seq2seq with vanilla rnn or lstm</a></li><li><a href="#Attention">Attention</a></li><li><a href="#Visual-Attention">Visual Attention</a></li><li><a href="#理解論文title">理解論文title</a></li><li><a href="#Abstract-Attention-Layer-Self-Attention">Abstract Attention Layer, Self Attention</a></li><li><a href="#1-Positional-Encoding">1. Positional Encoding</a></li><li><a href="#2-Masked-Self-Attention">2. Masked Self-Attention</a></li><li><a href="#3-Multihead-Self-Attention">3. Multihead Self-Attention</a></li><li><a href="#Layer-Example">Layer Example</a></li><li><a href="#Comparison">Comparison</a></li></ul></li><li><a href="#Lecture-18-Self-Supervised-Learning">Lecture 18 Self-Supervised Learning</a><ul><li><a href="#Confusion-Matrix">Confusion Matrix</a></li><li><a href="#Pretext-tasks-from-image-transformations">Pretext tasks from image transformations</a></li><li><a href="#Contrastive-representation-learning">Contrastive representation learning</a></li><li><a href="#SimCLR-and-MOCO">SimCLR and MOCO</a></li><li><a href="#CLIP">CLIP</a></li><li><a href="#CPC">CPC</a></li><li><a href="#Bert-GPT-T5">Bert/GPT/T5</a></li></ul></li><li><a href="#降維">降維</a><ul><li><a href="#PCA-t-SNE">PCA, t-SNE</a></li></ul></li><li><a href="#Lecture-19-3D-Vision">Lecture 19 3D Vision</a></li></ul></li><li><a href="#Assignments">Assignments</a><ul><li><a href="#CIFAR-10-open-ended-challenge">CIFAR-10 open-ended challenge</a><ul><li><a href="#Idea-0">Idea 0</a></li><li><a href="#Idea-1">Idea 1</a></li><li><a href="#Idea-2">Idea 2</a></li></ul></li><li><a href="#COCO">COCO</a></li></ul></li><li><a href="#Colab">Colab</a><ul><li><a href="#Colab-Pro">Colab Pro</a></li></ul></li><li><a href="#Regular-Expression">Regular Expression</a><ul><li><a href="#加粗标题">加粗标题</a></li><li><a href="#图片缩放">图片缩放</a></li><li><a href="#wiki圖片改assets路徑">wiki圖片改assets路徑</a></li></ul></li><li><a href="#小知识">小知识</a></li><li><a href="#常遇見bug一覽">常遇見bug一覽</a></li></ul><h1 id="Python-Keywords"><a href="#Python-Keywords" class="headerlink" title="Python Keywords"></a><strong>Python Keywords</strong></h1><h2 id="assert"><a href="#assert" class="headerlink" title="assert"></a><strong>assert</strong></h2><h3 id="in-dictionary"><a href="#in-dictionary" class="headerlink" title="in dictionary"></a><strong>in dictionary</strong></h3><p>verb</p><ol><li>to say that something is certainly true断言</li><li>to do something to show that you have power主张<ul><li>assert your authority/right</li></ul></li></ol><h3 id="in-python"><a href="#in-python" class="headerlink" title="in python"></a><strong>in python</strong></h3><h4 id="definition"><a href="#definition" class="headerlink" title="definition"></a><strong>definition</strong></h4><p>The assert keyword is used when debugging code.<br>用于调试代码。<br>The assert keyword lets you test if a condition in your code returns True, if not, the program will raise an AssertionError.<br>如果assert后的条件判断为真，则无事发生继续运行；为假则报错AssertionError。</p><h4 id="usage"><a href="#usage" class="headerlink" title="usage"></a><strong>usage</strong></h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = <span class="number">9527</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">assert</span> x == <span class="number">9527</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">assert</span> x == <span class="number">1551</span></span><br><span class="line">Traceback (most recent call last):</span><br><span class="line">  File <span class="string">"&lt;stdin&gt;"</span>, line <span class="number">1</span>, <span class="keyword">in</span> &lt;module&gt;</span><br><span class="line">AssertionError</span><br></pre></td></tr></table></figure><h2 id="yield"><a href="#yield" class="headerlink" title="yield"></a><strong>yield</strong></h2><h3 id="in-dictionary-1"><a href="#in-dictionary-1" class="headerlink" title="in dictionary"></a><strong>in dictionary</strong></h3><p>verb</p><ol><li>produce产生<ul><li>The process yields oil for industrial use.</li><li>Early radio equipment yielded poor sound quality.</li><li>The experiments yielded some surprising results.</li></ul></li><li>give up放弃<ul><li>Despite renewed pressure to give up the occupied territory, they will not yield.</li></ul></li><li>bend/break弯折<ul><li>His legs began to yield under the sheer weight of his body.</li></ul></li><li>stop让路<ul><li>If you’re going downhill, you need to yield to bikers going uphill. <ul><li>Phrasal verb: yield to something</li></ul></li></ul></li></ol><h3 id="in-python-1"><a href="#in-python-1" class="headerlink" title="in python"></a><strong>in python</strong></h3><h4 id="definition-1"><a href="#definition-1" class="headerlink" title="definition"></a><strong>definition</strong></h4><p>The yield keyword is used to return a list of values from a function.<br>用来返回函数值。<br>Unlike the return keyword which stops further execution of the function, the yield keyword continues to the end of the function.<br>跟return相比，yield返回值后，函数会继续运行。<br>When you call a function with yield keyword(s), the return value will be a list of values, one for each yield.<br>无论yield几次（哪怕只有一次），都会以generator的形式返回。</p><h4 id="usage-1"><a href="#usage-1" class="headerlink" title="usage"></a><strong>usage</strong></h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">test1</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">yield</span> <span class="number">9527</span></span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(type(test1()))</span><br><span class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">generator</span>'&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt; <span class="title">print</span><span class="params">(test1<span class="params">()</span>)</span> # 如果是<span class="title">return</span> 9527這裡當然是直接打印9527</span></span><br><span class="line"><span class="class">&lt;<span class="title">generator</span> <span class="title">object</span> <span class="title">test1</span> <span class="title">at</span> 0<span class="title">x1047d7350</span>&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt; <span class="title">for</span> <span class="title">i</span> <span class="title">in</span> <span class="title">test1</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(i)</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="number">9527</span></span><br></pre></td></tr></table></figure><p>可以用for遍历，或以迭代器的方式遍历<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">test2</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">yield</span> <span class="number">1</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">yield</span> <span class="number">2</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">yield</span> <span class="number">3</span></span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = test2() <span class="comment">#a generator object</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(next(x))</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(next(x))</span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(next(x))</span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> i <span class="keyword">in</span> test2():</span><br><span class="line"><span class="meta">... </span>    print(i)</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure></p><h3 id="generator-what"><a href="#generator-what" class="headerlink" title="generator? what?"></a><strong>generator? what?</strong></h3><h4 id="iterator-first"><a href="#iterator-first" class="headerlink" title="iterator first"></a><strong>iterator first</strong></h4><p><a href="https://docs.python.org/3/glossary.html#term-iterator">https://docs.python.org/3/glossary.html#term-iterator</a><br>iterator is an object representing a stream of data.<br>迭代器是个数据流对象。<br>Repeated calls to the iterator’s <code>__next__()</code> method (or passing it to the <a href="https://docs.python.org/3/library/functions.html">built-in function</a> <code>next()</code>) return successive items in the stream. When no more data are available a <code>StopIteration</code> exception is raised instead.<br>迭代器是這樣一種數據流對象：调用它的“__next__()”方法或用<a href="https://docs.python.org/3/library/functions.html">python内置函数</a>“next(iterator)”可以得到它这个流的后继数据，没数据了就抛出StopIteration异常。<br>根据這裡迭代器的定义，我們就可以自造迭代器类。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">MyNumbers</span>:</span></span><br><span class="line"><span class="meta">... </span>  <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span>    self.a = <span class="number">1</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span> self</span><br><span class="line"><span class="meta">... </span>  <span class="function"><span class="keyword">def</span> <span class="title">__next__</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span>    x = self.a</span><br><span class="line"><span class="meta">... </span>    self.a += <span class="number">1</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span> x</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>myclass = MyNumbers()</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>myiter = iter(myclass)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(next(myiter))</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(next(myiter))</span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(next(myiter))</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><br>这里的iter()也是个<a href="https://docs.python.org/3/library/functions.html">内置函数</a>，iter(object)或iter(object, sentinel)，接收一個對象返回一個iterator對象。<br>Without a second argument, object must be a collection object which supports the iterable protocol (the <code>__iter__()</code> method), or it must support the sequence protocol (the <code>__getitem__()</code> method with integer arguments starting at 0).<br>如果没有sentinel哨兵参数，這裡的object就必须实现__iter__或__getitem__方法。</p><h4 id="now-generator"><a href="#now-generator" class="headerlink" title="now generator"></a><strong>now generator</strong></h4><p><a href="https://docs.python.org/3/glossary.html#term-generator">https://docs.python.org/3/glossary.html#term-generator</a><br>A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.<br>“generator function”是个包含yield的函数，返回结果是个可以被for循环或<a href="https://docs.python.org/3/library/functions.html">python内置函数</a>next()遍历的iterator。</p><p>Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.<br>“generator”可能指代函数或迭代器，所以最好说全“生成器函数”或“生成器迭代器”而不只是说“生成器”。</p><p>Generator iterator is an object created by a generator function.<br>Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation).<br>每个yield都会暂时中止处理，记住位置执行状态（包括局部变量和待处理的try语句）。当生成器迭代器恢复时，它会从上次中断的地方继续执行（与每次调用时都从头开始的函数不同）。</p><p><strong>即，调用包含yield的函数会返回一个iterator，每次调用next(iterator)的时候函数就会运行到下一个yield处，python会记录这个yield的位置状态，下次next(iterator)从这里开始运行函数</strong></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">test</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">yield</span></span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(type(test()))</span><br><span class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">generator</span>'&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt; <span class="title">print</span><span class="params">(type<span class="params">(test)</span>)</span></span></span><br><span class="line"><span class="class">&lt;<span class="title">class</span> '<span class="title">function</span>'&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt; <span class="title">def</span> <span class="title">test</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span></span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(type(test()))</span><br><span class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">NoneType</span>'&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt;</span></span><br></pre></td></tr></table></figure><p>根据上述官方文档，这里的<code>&lt;class &#39;generator&#39;&gt;</code>实际上是个”generator iterator”。这里的<code>&lt;class &#39;function&#39;&gt;</code>实际上是个”generator function”。</p><h3 id="generator-expression"><a href="#generator-expression" class="headerlink" title="generator expression"></a><strong>generator expression</strong></h3><p>以前常用的下面这种写法也是个generator，()括号里面写一个循环（必须有括号）。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = (i*i <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">10</span>))</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(type(x))</span><br><span class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">generator</span>'&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt; <span class="title">for</span> <span class="title">i</span> <span class="title">in</span> <span class="title">x</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(i)</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="number">0</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">4</span></span><br><span class="line"><span class="number">9</span></span><br><span class="line"><span class="number">16</span></span><br><span class="line"><span class="number">25</span></span><br><span class="line"><span class="number">36</span></span><br><span class="line"><span class="number">49</span></span><br><span class="line"><span class="number">64</span></span><br><span class="line"><span class="number">81</span></span><br></pre></td></tr></table></figure><br>实际上相当于下面这种写法。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">genEx</span><span class="params">(num)</span>:</span></span><br><span class="line"><span class="meta">... </span>    i = <span class="number">0</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">while</span> i &lt; num:</span><br><span class="line"><span class="meta">... </span>            <span class="keyword">yield</span> i * i</span><br><span class="line"><span class="meta">... </span>            i = i + <span class="number">1</span></span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = genEx(<span class="number">10</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> i <span class="keyword">in</span> x:</span><br><span class="line"><span class="meta">... </span>    print(i)</span><br></pre></td></tr></table></figure><br>在上面这种只需要print结果的情况里，其实yield可以直接替换成print。<br>但很多时候不是需要打印而是需要取用这个值，如果把(i * i)存入其他容器也可实现相同功能。yield为这种情况提供了方便。</p><h3 id="yield写斐波那契数列"><a href="#yield写斐波那契数列" class="headerlink" title="yield写斐波那契数列"></a><strong>yield写斐波那契数列</strong></h3><p>The Fibonacci sequence is the series of numbers where each number is the sum of the two preceding numbers. For example, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, …<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">fib</span><span class="params">(limit)</span>:</span> <span class="comment"># a generator function</span></span><br><span class="line"><span class="meta">... </span>    a, b = <span class="number">0</span>, <span class="number">1</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">while</span> (b &lt; limit):</span><br><span class="line"><span class="meta">... </span>            <span class="keyword">yield</span> b</span><br><span class="line"><span class="meta">... </span>            a, b = b, a + b</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = fib(<span class="number">200</span>) <span class="comment"># a generator iterator</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> i <span class="keyword">in</span> x:</span><br><span class="line"><span class="meta">... </span>    print(i)</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">5</span></span><br><span class="line"><span class="number">8</span></span><br><span class="line"><span class="number">13</span></span><br><span class="line"><span class="number">21</span></span><br><span class="line"><span class="number">34</span></span><br><span class="line"><span class="number">55</span></span><br><span class="line"><span class="number">89</span></span><br><span class="line"><span class="number">144</span></span><br><span class="line">&gt;&gt;&gt;</span><br></pre></td></tr></table></figure></p><h2 id="numpy-dot"><a href="#numpy-dot" class="headerlink" title="numpy dot"></a><strong>numpy dot</strong></h2><p>矩陣點乘，但向量其實是特殊處理的：<br>在數學上，點乘需要一個行向量和一個列向量：</p><script type="math/tex; mode=display">\text{點乘} = \mathbf{v}^\top \cdot \mathbf{u} \quad \text{或} \quad \mathbf{u}^\top \cdot \mathbf{v}</script><p>但 NumPy 的 1D 陣列已隱含了這個行為。只要兩個 1D 陣列長度相等，它會自動將一個看作「行」，另一個看作「列」，執行點乘運算。因此，不需要手動轉置。</p><h3 id="範例"><a href="#範例" class="headerlink" title="範例"></a><strong>範例</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定義兩個向量</span></span><br><span class="line">v1 = np.array([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])  <span class="comment"># 一維向量</span></span><br><span class="line">v2 = np.array([<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>])  <span class="comment"># 一維向量</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 點乘</span></span><br><span class="line">dot_product = np.dot(v1, v2)</span><br><span class="line">print(<span class="string">"點乘結果:"</span>, dot_product)  <span class="comment"># 結果: 32</span></span><br></pre></td></tr></table></figure><h3 id="如果明確定義行或列向量"><a href="#如果明確定義行或列向量" class="headerlink" title="如果明確定義行或列向量"></a><strong>如果明確定義行或列向量</strong></h3><p>如果需要明確區分行向量或列向量，可以使用 2D 陣列來表示：</p><p>列向量：<code>v_col = np.array([[1], [2], [3]])</code>（形狀是 3×1）<br>行向量：<code>v_row = np.array([[1, 2, 3]])</code>（形狀是 1×3）<br>這時候計算點乘就需要遵守矩陣乘法的規則。</p><p>範例：行與列向量操作<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">v_row = np.array([[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]])  <span class="comment"># 行向量</span></span><br><span class="line">v_col = np.array([[<span class="number">4</span>], [<span class="number">5</span>], [<span class="number">6</span>]])  <span class="comment"># 列向量</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 行向量乘列向量 (結果是標量)</span></span><br><span class="line">result = np.dot(v_row, v_col)</span><br><span class="line">print(<span class="string">"行向量 * 列向量的結果:"</span>)</span><br><span class="line">print(result)  <span class="comment"># 結果是 [[32]]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 列向量乘行向量 (結果是矩陣)</span></span><br><span class="line">result2 = np.dot(v_col, v_row)</span><br><span class="line">print(<span class="string">"列向量 * 行向量的結果:"</span>)</span><br><span class="line">print(result2)  <span class="comment"># 結果是 3x3 矩陣</span></span><br></pre></td></tr></table></figure><br>輸出：<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">行向量 * 列向量的結果:</span><br><span class="line">[[<span class="number">32</span>]]</span><br><span class="line">列向量 * 行向量的結果:</span><br><span class="line">[[ <span class="number">4</span>  <span class="number">8</span> <span class="number">12</span>]</span><br><span class="line"> [ <span class="number">5</span> <span class="number">10</span> <span class="number">15</span>]</span><br><span class="line"> [ <span class="number">6</span> <span class="number">12</span> <span class="number">18</span>]]</span><br></pre></td></tr></table></figure><br>numpy裡想取一行是array[i]，想取一列是array[:,j]。<br>chatgpt輔助寫代碼實在是太好用了，語法這方面絕對的百科全書。</p><h2 id="lambda"><a href="#lambda" class="headerlink" title="lambda"></a><strong>lambda</strong></h2><p><code>lambda</code> 是 Python 中用来创建匿名函数（没有名字的函数）的一种表达式。它非常简洁，但功能强大，适合用在一些简单的场景。</p><h3 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a><strong>基本语法</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">lambda</span> 参数<span class="number">1</span>, 参数<span class="number">2</span>, ...: 表达式</span><br></pre></td></tr></table></figure><ul><li>参数：可以有多个参数，类似于常规函数定义。</li><li>表达式：只能包含一个表达式，表达式的计算结果即为返回值。</li></ul><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a><strong>示例</strong></h3><ol><li>简单的用法<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 普通函数定义</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add</span><span class="params">(x, y)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> x + y</span><br><span class="line"></span><br><span class="line"><span class="comment"># lambda表达式</span></span><br><span class="line">add_lambda = <span class="keyword">lambda</span> x, y: x + y</span><br><span class="line"></span><br><span class="line">print(add(<span class="number">3</span>, <span class="number">5</span>))         <span class="comment"># 输出 8</span></span><br><span class="line">print(add_lambda(<span class="number">3</span>, <span class="number">5</span>))  <span class="comment"># 输出 8</span></span><br></pre></td></tr></table></figure></li><li>作为内联函数<br>常用在需要短小函数的场景，例如 map()、filter()、sorted() 等。<ul><li>跟map()配合<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">nums = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]</span><br><span class="line">squared = map(<span class="keyword">lambda</span> x: x ** <span class="number">2</span>, nums)</span><br><span class="line">print(list(squared))  <span class="comment"># 输出 [1, 4, 9, 16]</span></span><br></pre></td></tr></table></figure></li><li>与filter()配合<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">nums = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line">even = filter(<span class="keyword">lambda</span> x: x % <span class="number">2</span> == <span class="number">0</span>, nums)</span><br><span class="line">print(list(even))  <span class="comment"># 输出 [2, 4]</span></span><br></pre></td></tr></table></figure></li><li>跟sorted()配合<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">words = [<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"cherry"</span>]</span><br><span class="line">sorted_words = sorted(words, key=<span class="keyword">lambda</span> x: len(x))</span><br><span class="line">print(sorted_words)  <span class="comment"># 输出 ['apple', 'cherry', 'banana']</span></span><br></pre></td></tr></table></figure></li></ul></li><li>作为函数参数<br>可以直接将 lambda 函数传递给其他函数作为参数。<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">apply_function</span><span class="params">(func, value)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> func(value)</span><br><span class="line"></span><br><span class="line">result = apply_function(<span class="keyword">lambda</span> x: x * <span class="number">2</span>, <span class="number">10</span>)</span><br><span class="line">print(result)  <span class="comment"># 输出 20</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a><strong>优缺点</strong></h3><p>优点：</p><ul><li>简洁，适合定义简单逻辑。可以轻松传递小函数。</li></ul><p>缺点：</p><ul><li>只能写单一表达式，功能有限。</li><li>可读性较差，复杂逻辑建议使用 def 定义常规函数。</li></ul><p>使用建议：</p><ul><li>当逻辑非常简单且不会重复使用时，用 lambda。</li><li>复杂逻辑或需要调试时，使用 def 明确定义函数。</li></ul><h2 id="广播broadcast"><a href="#广播broadcast" class="headerlink" title="广播broadcast"></a><strong>广播broadcast</strong></h2><div class="table-container"><table><thead><tr><th><strong>A.shape</strong></th><th><strong>B.shape</strong></th><th><strong>Resulting Shape</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td>(3, 4)</td><td>(3, 4)</td><td>(3, 4)</td><td>相同形状，逐元素操作</td></tr><tr><td>(3, 4)</td><td>(1, 4)</td><td>(3, 4)</td><td>B 的第 0 维度广播为 3，变成(3, 4)</td></tr><tr><td>(3, 4)</td><td>(4,)</td><td>(3, 4)</td><td>B 增加维度后广播为 (1, 4)，再把第 0 维广播为(3, 4)</td></tr><tr><td>(3, 1, 4)</td><td>(1, 2, 4)</td><td>(3, 2, 4)</td><td>高维度逐维广播</td></tr><tr><td>(3, 4)</td><td>()</td><td>(3, 4)</td><td>标量广播为 (3, 4)</td></tr><tr><td>(3, 4)</td><td>(2, 4)</td><td><strong>Error</strong></td><td>形状不兼容，无法广播</td></tr></tbody></table></div><p>广播其实就是把两个矩阵补成一样大小。</p><p>注意⚠️：</p><ul><li>广播的结果只是虚拟扩展，并不真的复制数据，因此它的内存和计算效率都很高。</li><li><p>如果不确定广播的结果，可以使用 np.broadcast_shapes 或 np.broadcast_to 来检查或预览。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 检查广播后形状</span></span><br><span class="line">np.broadcast_shapes((<span class="number">3</span>, <span class="number">4</span>), (<span class="number">1</span>, <span class="number">4</span>))  <span class="comment"># 输出 (3, 4)</span></span><br></pre></td></tr></table></figure></li><li><p>标量：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">A = np.ones((<span class="number">3</span>, <span class="number">4</span>))</span><br><span class="line">B = <span class="number">2</span>  <span class="comment"># 标量</span></span><br><span class="line">C = A + B  <span class="comment"># B 会广播为 (3, 4) 进行相加</span></span><br></pre></td></tr></table></figure></li><li>高维度例如(3, 1, 4)和(1, 2, 4)<ul><li>第 0 维：3 和 1 -&gt; 广播为 3。</li><li>第 1 维：1 和 2 -&gt; 广播为 2。</li><li>第 2 维：4 和 4 -&gt; 保持不变。</li><li>最终广播结果为 (3, 2, 4)<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">A = np.ones((<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>))</span><br><span class="line">B = np.ones((<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>))</span><br><span class="line">C = A + B  <span class="comment"># 广播为 (3, 2, 4)</span></span><br></pre></td></tr></table></figure></li></ul></li></ul><h2 id="random"><a href="#random" class="headerlink" title="random"></a><strong>random</strong></h2><div class="table-container"><table><thead><tr><th><strong>特性</strong></th><th><strong><code>np.random.rand</code></strong></th><th><strong><code>np.random.randn</code></strong></th></tr></thead><tbody><tr><td><strong>生成分布</strong></td><td>均勻分布（Uniform distribution）</td><td>標準正態分布（Standard normal distribution, 均值 0，方差 1）</td></tr><tr><td><strong>數值範圍</strong></td><td>$[0, 1)$</td><td>理論上為 $(-\infty, +\infty)$，但集中於約 $[-3, 3]$</td></tr><tr><td><strong>輸出數值類型</strong></td><td>浮點數（float）</td><td>浮點數（float）</td></tr><tr><td><strong>用途</strong></td><td>模擬隨機比例、概率等均勻分布的數據</td><td>模擬中心為 0 且有波動的隨機數據，適用於神經網絡初始化等場景</td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th><strong>場景</strong></th><th><strong><code>np.random.rand</code></strong> 用法</th><th><strong><code>np.random.randn</code></strong> 用法</th></tr></thead><tbody><tr><td><strong>模擬隨機概率</strong></td><td>用於生成隨機概率數據，例如蒙特卡洛模擬</td><td>不適用</td></tr><tr><td><strong>數據增強</strong></td><td>隨機調整數據（添加隨機偏差）</td><td>添加噪聲，用於模型訓練的數據增強</td></tr><tr><td><strong>神經網絡初始化</strong></td><td>初始化介於 $[0, 1)$ 的權重</td><td>初始化符合正態分布的權重，中心為 0，標準差為 1</td></tr><tr><td><strong>隨機測試</strong></td><td>在 $[0, 1)$ 中隨機生成測試數據</td><td>模擬符合正態分布的隨機測試數據</td></tr></tbody></table></div><p>numpy/<br>├── random/                     # numpy.random 模組<br>│   ├── Generator               # 現代化的隨機數生成器 (建議用)<br>│   ├── RandomState             # 傳統的隨機數生成接口<br>│   ├── rand()                  # 均勻分布隨機數 (RandomState 方法)<br>│   ├── randn()                 # 標準正態分布隨機數 (RandomState 方法)<br>│   ├── randint()               # 整數隨機數生成<br>│   ├── normal()                # 通用正態分布隨機數生成<br>│   ├── uniform()               # 通用均勻分布隨機數生成<br>│   ├── seed()                  # 設置隨機種子<br>│   ├── …</p><div class="table-container"><table><thead><tr><th><strong>方法</strong></th><th><strong>功能</strong></th></tr></thead><tbody><tr><td><code>np.random.rand</code></td><td>生成均勻分布的隨機數，範圍為 $[0, 1)$。</td></tr><tr><td><code>np.random.randn</code></td><td>生成標準正態分布（均值為 0，標準差為 1）的隨機數。</td></tr><tr><td><code>np.random.randint</code></td><td>生成隨機整數，可以指定範圍和大小。</td></tr><tr><td><code>np.random.uniform</code></td><td>生成均勻分布的隨機數，可以指定範圍，例如 $[a, b)$。</td></tr><tr><td><code>np.random.normal</code></td><td>生成正態分布的隨機數，可以指定均值和標準差。</td></tr><tr><td><code>np.random.choice</code></td><td>從給定的序列中隨機選取元素，可以指定抽樣數量和是否放回。</td></tr><tr><td><code>np.random.shuffle</code></td><td>就地打亂給定的數組（改變原數組順序）。</td></tr><tr><td><code>np.random.permutation</code></td><td>返回給定數組的隨機排列（不改變原數組）。</td></tr><tr><td><code>np.random.seed</code></td><td>設置隨機數生成器的種子，用於保證實驗的可重現性。</td></tr><tr><td><code>np.random.beta</code></td><td>生成 Beta 分布隨機數。</td></tr><tr><td><code>np.random.binomial</code></td><td>生成二項分布隨機數。</td></tr><tr><td><code>np.random.poisson</code></td><td>生成泊松分布隨機數。</td></tr><tr><td><code>np.random.exponential</code></td><td>生成指數分布隨機數。</td></tr></tbody></table></div><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">seed = <span class="number">231</span> <span class="comment"># 如果不設定 seed，兩個 random 每次的值就都不一樣，設定了就永遠一樣</span></span><br><span class="line">torch.manual_seed(seed)</span><br><span class="line">np.random.seed(seed)</span><br><span class="line">torch.randn((<span class="number">3</span>, <span class="number">5</span>)) <span class="comment"># 接受 tuple，返回 torch.Tensor</span></span><br><span class="line">np.random.randn(<span class="number">3</span>, <span class="number">5</span>) <span class="comment"># 接受任意多個 int 維度，返回 numpy.ndarray</span></span><br></pre></td></tr></table></figure><p>生成所需區間的隨機數，比如<code>[a, b)</code>：$(b - a)random + a$<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">(b - a) * np.random.rand() + a</span><br><span class="line">(b - a) * torch.rand(<span class="number">1</span>, <span class="number">1</span>) + a</span><br></pre></td></tr></table></figure></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">rand_range</span><span class="params">(<span class="keyword">double</span> a, <span class="keyword">double</span> b)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">double</span> random_value = (<span class="keyword">double</span>)rand() / RAND_MAX;</span><br><span class="line">    <span class="keyword">return</span> (b - a) * random_value + a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="星號"><a href="#星號" class="headerlink" title="星號*"></a><strong>星號*</strong></h2><p>np.random.rand(x.shape)是不行的，np.random.rand(x.shape[0], x.shape[1], x.shape[2])這樣可以，或者用*。<br>np.random.rand(*x.shape)，在這裡，<em> 是一個<em>*解包運算符（unpacking operator）</em></em>，它將 x.shape 元組的每個元素作為獨立參數傳入。</p><p>* 是 Python 的解包運算符，主要用來將 可迭代對象（如列表、元組）展開為單獨的元素。<br>在函數調用中，* 可以用來將列表或元組中的元素作為多個獨立的參數傳入函數。</p><p><code>torch.rand(*size, *, generator=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False) → Tensor</code></p><h2 id="pad"><a href="#pad" class="headerlink" title="pad"></a><strong>pad</strong></h2><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">numpy.pad(array, pad_width, mode=<span class="string">'constant'</span>, **kwargs)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>np.pad([[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>],[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]],pad_width=<span class="number">1</span>, mode=<span class="string">'constant'</span>, constant_values=<span class="number">0</span>) <span class="comment"># cnn padding</span></span><br><span class="line">array([[<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">       [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">0</span>],</span><br><span class="line">       [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">0</span>],</span><br><span class="line">       [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>]])</span><br></pre></td></tr></table></figure><div class="table-container"><table><thead><tr><th><strong>参数</strong></th><th><strong>说明</strong></th><th><strong>示例</strong></th></tr></thead><tbody><tr><td><code>array</code></td><td>要填充的数组，支持任意维度</td><td><code>np.array([1, 2, 3])</code></td></tr><tr><td><code>pad_width</code></td><td>填充宽度，可以是整数、元组或元组列表</td><td><code>2</code> （所有维度前后各填充 2 个）；<code>((1, 1), (2, 2))</code>（每维单独指定）</td></tr><tr><td><code>mode</code></td><td>填充模式，常用模式如下：</td><td></td></tr><tr><td><code>&#39;constant&#39;</code></td><td>填充固定值，默认值为 0，可通过 <code>constant_values</code> 指定</td><td><code>np.pad([1, 2], 2, mode=&#39;constant&#39;, constant_values=9)</code> -&gt; <code>[9, 9, 1, 2, 9, 9]</code></td></tr><tr><td><code>&#39;edge&#39;</code></td><td>用边界值填充</td><td><code>np.pad([1, 2], 2, mode=&#39;edge&#39;)</code> -&gt; <code>[1, 1, 1, 2, 2, 2]</code></td></tr><tr><td><code>&#39;symmetric&#39;</code></td><td>对称填充（包括边界）</td><td><code>np.pad([1, 2], 2, mode=&#39;symmetric&#39;)</code> -&gt; <code>[2, 1, 1, 2, 1, 1, 2]</code></td></tr><tr><td><code>&#39;reflect&#39;</code></td><td>镜像填充（不包括边界）</td><td><code>np.pad([1, 2], 2, mode=&#39;reflect&#39;)</code> -&gt; <code>[2, 1, 2, 1, 2, 1]</code></td></tr><tr><td><code>&#39;wrap&#39;</code></td><td>环绕填充</td><td><code>np.pad([1, 2, 3], 2, mode=&#39;wrap&#39;)</code> -&gt; <code>[3, 1, 2, 3, 1, 2, 3, 1]</code></td></tr><tr><td><strong>其他参数</strong></td><td><strong>根据模式指定额外参数</strong></td><td></td></tr><tr><td><code>constant_values</code></td><td>在 <code>constant</code> 模式下指定填充值</td><td><code>np.pad([1, 2], 2, mode=&#39;constant&#39;, constant_values=5)</code> -&gt; <code>[5, 5, 1, 2, 5, 5]</code></td></tr></tbody></table></div><h2 id="array改變形狀"><a href="#array改變形狀" class="headerlink" title="array改變形狀"></a><strong>array改變形狀</strong></h2><h3 id="transpose和moveaxis"><a href="#transpose和moveaxis" class="headerlink" title="transpose和moveaxis"></a><strong>transpose和moveaxis</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>N, C, H, W = <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = <span class="number">4</span> * np.random.randn(N, C, H, W) + <span class="number">10</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>np.array_equal(x.transpose(<span class="number">1</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>), np.moveaxis(x, <span class="number">0</span>, <span class="number">1</span>))</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><p>本质上，moveaxis 是 transpose 的封装，简化了多维轴变换的操作，但效果和 transpose 相同。</p><h3 id="reshape"><a href="#reshape" class="headerlink" title="reshape"></a><strong>reshape</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>np.array_equal(x.transpose(<span class="number">1</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>), x.reshape(C, N, H, W))</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p>reshape：改变形状，不改变数据的顺序。它不能实现轴的重新排列效果。<br>transpose：调整轴的顺序，可能导致数据的逻辑顺序发生变化。<br>moveaxis：更灵活的轴变换，与 transpose 作用相同。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建一个 2x3x4 的三维数组</span></span><br><span class="line">x = np.arange(<span class="number">24</span>).reshape(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">print(<span class="string">"原始数组 (shape: 2x3x4):"</span>)</span><br><span class="line">print(x)</span><br><span class="line">[[[ <span class="number">0</span>  <span class="number">1</span>  <span class="number">2</span>  <span class="number">3</span>]</span><br><span class="line">  [ <span class="number">4</span>  <span class="number">5</span>  <span class="number">6</span>  <span class="number">7</span>]</span><br><span class="line">  [ <span class="number">8</span>  <span class="number">9</span> <span class="number">10</span> <span class="number">11</span>]]</span><br><span class="line"></span><br><span class="line"> [[<span class="number">12</span> <span class="number">13</span> <span class="number">14</span> <span class="number">15</span>]</span><br><span class="line">  [<span class="number">16</span> <span class="number">17</span> <span class="number">18</span> <span class="number">19</span>]</span><br><span class="line">  [<span class="number">20</span> <span class="number">21</span> <span class="number">22</span> <span class="number">23</span>]]]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 transpose：交换第一轴和第二轴 (1, 0, 2)</span></span><br><span class="line">x_transposed = x.transpose(<span class="number">1</span>, <span class="number">0</span>, <span class="number">2</span>)</span><br><span class="line">print(<span class="string">"\n使用 transpose 后 (shape: 3x2x4):"</span>)</span><br><span class="line">print(x_transposed)</span><br><span class="line">[[[ <span class="number">0</span>  <span class="number">1</span>  <span class="number">2</span>  <span class="number">3</span>]</span><br><span class="line">  [<span class="number">12</span> <span class="number">13</span> <span class="number">14</span> <span class="number">15</span>]]</span><br><span class="line"></span><br><span class="line"> [[ <span class="number">4</span>  <span class="number">5</span>  <span class="number">6</span>  <span class="number">7</span>]</span><br><span class="line">  [<span class="number">16</span> <span class="number">17</span> <span class="number">18</span> <span class="number">19</span>]]</span><br><span class="line"></span><br><span class="line"> [[ <span class="number">8</span>  <span class="number">9</span> <span class="number">10</span> <span class="number">11</span>]</span><br><span class="line">  [<span class="number">20</span> <span class="number">21</span> <span class="number">22</span> <span class="number">23</span>]]]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 reshape：直接改成 3x2x4</span></span><br><span class="line">x_reshaped = x.reshape(<span class="number">3</span>, <span class="number">2</span>, <span class="number">4</span>)</span><br><span class="line">print(<span class="string">"\n使用 reshape 后 (shape: 3x2x4):"</span>)</span><br><span class="line">print(x_reshaped)</span><br><span class="line">[[[ <span class="number">0</span>  <span class="number">1</span>  <span class="number">2</span>  <span class="number">3</span>]</span><br><span class="line">  [ <span class="number">4</span>  <span class="number">5</span>  <span class="number">6</span>  <span class="number">7</span>]]</span><br><span class="line"></span><br><span class="line"> [[ <span class="number">8</span>  <span class="number">9</span> <span class="number">10</span> <span class="number">11</span>]</span><br><span class="line">  [<span class="number">12</span> <span class="number">13</span> <span class="number">14</span> <span class="number">15</span>]]</span><br><span class="line"></span><br><span class="line"> [[<span class="number">16</span> <span class="number">17</span> <span class="number">18</span> <span class="number">19</span>]</span><br><span class="line">  [<span class="number">20</span> <span class="number">21</span> <span class="number">22</span> <span class="number">23</span>]]]</span><br></pre></td></tr></table></figure><h2 id="array擴展"><a href="#array擴展" class="headerlink" title="array擴展"></a><strong>array擴展</strong></h2><p>np.tile(A, reps) 用于重复数组，将输入数组沿指定维度扩展成更大的数组。</p><ul><li>A：输入数组。</li><li>reps：整数或元组，表示每个维度重复的次数。<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1維重複3次</span></span><br><span class="line">print(np.tile([<span class="number">1</span>, <span class="number">2</span>], <span class="number">3</span>))  <span class="comment"># [1 2 1 2 1 2] </span></span><br><span class="line"></span><br><span class="line">print(np.tile([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]], (<span class="number">2</span>, <span class="number">3</span>))) <span class="comment"># 1維（行）重複兩次，2維（列）重複三次</span></span><br><span class="line"><span class="comment"># [[1 2 1 2 1 2]</span></span><br><span class="line"><span class="comment">#  [3 4 3 4 3 4]</span></span><br><span class="line"><span class="comment">#  [1 2 1 2 1 2]</span></span><br><span class="line"><span class="comment">#  [3 4 3 4 3 4]]</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="concatenation拼接"><a href="#concatenation拼接" class="headerlink" title="concatenation拼接"></a><strong>concatenation拼接</strong></h2><p>使用 + 操作符（對於列表）或 numpy.concatenate() 函數（對於數組）。</p><p>list直接相+。</p><p>torch裡跟numpy的操作是幾乎一樣的，所以很方便。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 創建兩個2D數組</span></span><br><span class="line">arr1 = np.array([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]]) <span class="comment"># torch.tensor([[1, 2], [3, 4]])</span></span><br><span class="line">arr2 = np.array([[<span class="number">5</span>, <span class="number">6</span>], [<span class="number">7</span>, <span class="number">8</span>]]) <span class="comment"># torch.tensor([[5, 6], [7, 8]])</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在垂直方向（axis=0）拼接</span></span><br><span class="line">result_vertical = np.concatenate([arr1, arr2], axis=<span class="number">0</span>) <span class="comment"># torch.cat((tensor1, tensor2), dim=0)</span></span><br><span class="line">print(result_vertical)</span><br><span class="line"><span class="comment"># Output:</span></span><br><span class="line"><span class="comment"># [[1 2]</span></span><br><span class="line"><span class="comment">#  [3 4]</span></span><br><span class="line"><span class="comment">#  [5 6]</span></span><br><span class="line"><span class="comment">#  [7 8]]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在水平方向（axis=1）拼接</span></span><br><span class="line">result_horizontal = np.concatenate([arr1, arr2], axis=<span class="number">1</span>) <span class="comment"># torch.cat((tensor1, tensor2), dim=1)</span></span><br><span class="line">print(result_horizontal)</span><br><span class="line"><span class="comment"># Output:</span></span><br><span class="line"><span class="comment"># [[1 2 5 6]</span></span><br><span class="line"><span class="comment">#  [3 4 7 8]]</span></span><br></pre></td></tr></table></figure><h2 id="torchvision-transforms"><a href="#torchvision-transforms" class="headerlink" title="torchvision.transforms"></a><strong>torchvision.transforms</strong></h2><p><strong>One of our former instructors, Justin Johnson, made an excellent <a href="https://github.com/jcjohnson/pytorch-examples">tutorial</a> for PyTorch.</strong><br><strong>You can also find the detailed <a href="http://pytorch.org/docs/stable/index.html">API doc</a> here. If you have other questions that are not addressed by the API docs, the <a href="https://discuss.pytorch.org/">PyTorch forum</a> is a much better place to ask than StackOverflow.</strong></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torchvision.transforms <span class="keyword">as</span> T</span><br><span class="line">transform = T.Compose([</span><br><span class="line">                T.ToTensor(),</span><br><span class="line">                T.Normalize((<span class="number">0.4914</span>, <span class="number">0.4822</span>, <span class="number">0.4465</span>), (<span class="number">0.2023</span>, <span class="number">0.1994</span>, <span class="number">0.2010</span>)) <span class="comment"># hardcoded mean and std</span></span><br><span class="line">            ])</span><br></pre></td></tr></table></figure><h3 id="Compose"><a href="#Compose" class="headerlink" title="Compose()"></a><strong>Compose()</strong></h3><p>將多個圖像預處理操作組合成一個序列，就是打包預處理。</p><p>比如其中第一個T.ToTensor()就是把圖像轉成Tensor數據類型。另外還可以做Resize和CenterCrop之類的操作。查上面的libraries/torchvision文檔。</p><h3 id="Normalize"><a href="#Normalize" class="headerlink" title="Normalize()"></a><strong>Normalize()</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">torchvision.transforms.Normalize(mean, std)</span><br></pre></td></tr></table></figure><ul><li>mean：是一个列表或元组，包含每个通道的均值（例如，对于 RGB 图像，可以是 <code>[R_mean, G_mean, B_mean]</code>）。</li><li>std：是一个列表或元组，包含每个通道的标准差（例如，对于 RGB 图像，可以是 <code>[R_std, G_std, B_std]</code>）。</li></ul><p>每個通道分別減去所有圖像的統計量。</p><h2 id="torch-nn-Sequential"><a href="#torch-nn-Sequential" class="headerlink" title="torch.nn.Sequential()"></a><strong>torch.nn.Sequential()</strong></h2><p>跟上面的Compose()很像就一塊說了。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">model = torch.nn.Sequential(</span><br><span class="line">    torch.nn.Conv2d(in_channels=<span class="number">3</span>, out_channels=<span class="number">64</span>, kernel_size=<span class="number">3</span>),</span><br><span class="line">    torch.nn.ReLU(),</span><br><span class="line">    torch.nn.MaxPool2d(kernel_size=<span class="number">2</span>),</span><br><span class="line">    torch.nn.Linear(<span class="number">64</span>, <span class="number">10</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>這個是打包網絡層方便前後向計算。</p><h2 id="DataLoader"><a href="#DataLoader" class="headerlink" title="DataLoader"></a><strong>DataLoader</strong></h2><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> sampler</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> torchvision.datasets <span class="keyword">as</span> dset</span><br><span class="line"></span><br><span class="line">NUM_TRAIN = <span class="number">49000</span></span><br><span class="line"></span><br><span class="line">cifar10_train = dset.CIFAR10(<span class="string">'./cs231n/datasets'</span>, train=<span class="literal">True</span>, download=<span class="literal">True</span>, transform=transform)</span><br><span class="line">loader_train = DataLoader(cifar10_train, batch_size=<span class="number">64</span>, sampler=sampler.SubsetRandomSampler(range(NUM_TRAIN)))</span><br><span class="line"></span><br><span class="line">cifar10_val = dset.CIFAR10(<span class="string">'./cs231n/datasets'</span>, train=<span class="literal">True</span>, download=<span class="literal">True</span>, transform=transform)</span><br><span class="line">loader_val = DataLoader(cifar10_val, batch_size=<span class="number">64</span>, sampler=sampler.SubsetRandomSampler(range(NUM_TRAIN, <span class="number">50000</span>)))</span><br><span class="line"></span><br><span class="line">cifar10_test = dset.CIFAR10(<span class="string">'./cs231n/datasets'</span>, train=<span class="literal">False</span>, download=<span class="literal">True</span>, transform=transform)</span><br><span class="line">loader_test = DataLoader(cifar10_test, batch_size=<span class="number">64</span>)</span><br></pre></td></tr></table></figure><p>torchvision.datasets.CIFAR10加載數據集：</p><ul><li>root時存儲路徑，如果沒有且download=True會自動下載，</li><li>train=True加載訓練集（data_batch_1/2/3/4/5），False加載測試集（test_batch）</li><li>transform就是預處理</li></ul><p>這裡如果不 ToTensor() 返回的不是 Python 列表 (List) ，也不是numpy.ndarray，是 PIL.Image.Image 對象。</p><ul><li>数据转换 是在 Dataset 对象创建时完成的（通过 transform）。</li><li>数据加载和批处理 是在 DataLoader 中进行的。</li></ul><h3 id="PIL-Pillow"><a href="#PIL-Pillow" class="headerlink" title="PIL/Pillow"></a><strong>PIL/Pillow</strong></h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="comment"># 打开一个图像文件</span></span><br><span class="line">image = Image.open(<span class="string">'image.jpg'</span>)</span><br><span class="line"><span class="comment"># 显示图像</span></span><br><span class="line">image.show()</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> ImageDraw</span><br><span class="line"><span class="comment"># 创建一个可编辑的图像对象</span></span><br><span class="line">draw = ImageDraw.Draw(image)</span><br><span class="line"><span class="comment"># 在图像上绘制文本</span></span><br><span class="line">draw.text((<span class="number">50</span>, <span class="number">50</span>), <span class="string">"Hello, World!"</span>, fill=<span class="string">"black"</span>)</span><br><span class="line"><span class="comment"># 图像保存</span></span><br><span class="line">image.save(<span class="string">'new_image.jpg'</span>)</span><br></pre></td></tr></table></figure><p>PIL 是 Python Imaging Library，Initial release 是1995年，Pillow 是 2010 年 forked 這個 PIL repository 並支持 Python 3.x 的繼任者。</p><h3 id="為什麼需要DataLoader？"><a href="#為什麼需要DataLoader？" class="headerlink" title="為什麼需要DataLoader？"></a><strong>為什麼需要DataLoader？</strong></h3><p>上面用 torchvision.datasets 用 PIL 讀取數據並轉為 Tensor之後，用DataLoader轉為torch.utils.data.dataloader.DataLoader。</p><p>這裡不用 sampler 用 shuffle=True 的話是打亂整個數據集，跟 SubsetRandomSampler 也差不多，都是<strong>不放回的隨機抽樣</strong>。全部輸入一遍就是一個epoch，一個minibatch是一個iteration。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">loader_train = DataLoader(cifar10_train, batch_size=<span class="number">64</span>, sampler=sampler.SubsetRandomSampler(range(NUM_TRAIN)))</span><br><span class="line"><span class="keyword">for</span> data, target <span class="keyword">in</span> loader_train:</span><br><span class="line">    print(data.device)  <span class="comment"># 检查每个批次数据的设备，返回“cpu”</span></span><br><span class="line">    <span class="keyword">break</span>  <span class="comment"># 只查看第一个批次</span></span><br></pre></td></tr></table></figure><br>其中，cifar10_train 是 Tensor 原數據集，batch_size 是每個 minibatch 的大小，sampler 是採樣 minibatch 的方法，這裡是從索引 0 到 49000 隨機取 64 個。</p><ol><li>提前分好 minibatches 比較方便，assignment 1/2 裡的 /cs231n/solver 都是在 <code>_step()</code> 裡用 <code>batch_mask = np.random.choice(num_train, self.batch_size)</code> 現取；</li><li>DataLoader 負責分批把數據加載到內存，節省 CPU Memory；</li><li>支持多線程並行加載數據，用 <code>DataLoader(..., num_workers=4)</code> 可以加速讀數據；</li></ol><h2 id="PyTorch-vs-Numpy"><a href="#PyTorch-vs-Numpy" class="headerlink" title="PyTorch vs. Numpy"></a><strong>PyTorch vs. Numpy</strong></h2><p>在<a href="#deep-learning-frameworks">Lecture 8</a>討論過Pytorch相比Tensorflow除了一個Dynamic vs. Static的優勢之外，還有就是類似numpy所以上手快。</p><p>所以在這整理一點兒用到過的等價操作，以供查閱，以防混淆。</p><div class="table-container"><table><thead><tr><th>Pytorch</th><th>Numpy</th><th></th></tr></thead><tbody><tr><td>torch.Tensor</td><td>numpy.ndarray</td><td>相似數據類型</td></tr><tr><td>Tensor.view()</td><td>ndarray.reshape() / numpy.reshape()</td><td>數據結構內置</td></tr><tr><td>torch.arange()</td><td>range()</td><td>前者生成Tensor後者生成list</td></tr><tr><td>Tensor.matmul()</td><td>ndarray.dot()</td><td>矩陣乘 Matrix Multiplication，<strong>python 3.5後可以用 @</strong>，相當於 matmul 和 dot 的語法糖。对于高维张量：执行广播规则，然后对最后两个维度进行矩阵乘法。</td></tr><tr><td>Tensor.dot()</td><td>ndarray.dot()</td><td>只能接受兩個一維向量計算內積</td></tr><tr><td>Tensor.mm()</td><td>ndarray.dot()</td><td>只能接受兩個二維矩陣算矩陣乘法，但因為底層實現比matmul()簡單所以大概更快</td></tr><tr><td>*</td><td>*</td><td>廣播逐元素乘 broadcast elementwise</td></tr><tr><td>torch.rand / torch.randn</td><td>numpy.random.rand / numpy.random.randn</td><td><a href="#random">討論過numpy</a>，torch用法跟numpy有點細微區別在下面補充一下吧</td></tr><tr><td>s.gather(1, y.view(-1, 1)).squeeze()</td><td>s[np.arange(N), y]</td><td>Tensor[range(len(y)), y]也可以啊…好像不需要gather這個語法了</td></tr><tr><td>torch.tensor(), torch.Tensor()</td><td>np.array() np.asarray()</td><td>轉類型</td></tr><tr><td>torch.cat((A,B),dim=0)</td><td>np.concatenate((A,B),axis=0)</td><td>拼接</td></tr><tr><td>Tensor.permute()</td><td><a href="#transpose和moveaxis">numpy.transpose()</a></td><td>轉置，之前還總結過跟moveaxis的區別</td></tr></tbody></table></div><p>這次能一個月做完也多虧了 chatgpt，2022 年末在商湯時 chatgpt 突然爆火，當時如果能用熟的話也不至於那麼菜。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">seed = <span class="number">231</span> <span class="comment"># 如果不設定 seed，兩個 random 每次的值就都不一樣，設定了就永遠一樣</span></span><br><span class="line">torch.manual_seed(seed)</span><br><span class="line">np.random.seed(seed)</span><br><span class="line">torch.randn((<span class="number">3</span>, <span class="number">5</span>)) <span class="comment"># 接受 tuple，返回 torch.Tensor</span></span><br><span class="line">np.random.randn(<span class="number">3</span>, <span class="number">5</span>) <span class="comment"># 接受任意多個 int 維度，返回 numpy.ndarray</span></span><br></pre></td></tr></table></figure><h2 id="requires-grad和Pytorch裡的computational-graph"><a href="#requires-grad和Pytorch裡的computational-graph" class="headerlink" title="requires_grad和Pytorch裡的computational graph"></a><strong>requires_grad和Pytorch裡的computational graph</strong></h2><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">w = torch.randn(shape, device=device, dtype=dtype) * np.sqrt(<span class="number">2.</span> / fan_in)</span><br><span class="line">w.requires_grad = <span class="literal">True</span></span><br><span class="line"><span class="comment"># tensor([[0.6324]], device='cuda:0', requires_grad=True)</span></span><br><span class="line">print(w.grad_fn)</span><br><span class="line"><span class="comment"># None</span></span><br></pre></td></tr></table></figure><p>初始化 weights 的 requires_grad 應該用這種寫法，如果寫到torch.randn裡是錯誤的。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">w = torch.randn(shape, device=device, dtype=dtype, requires_grad=<span class="literal">True</span>) * np.sqrt(<span class="number">2.</span> / fan_in)</span><br><span class="line"><span class="comment"># tensor([[0.6324]], device='cuda:0', grad_fn=&lt;MulBackward0&gt;)</span></span><br></pre></td></tr></table></figure></p><p>當輸出顯示<code>requires_grad=True</code>的時候表示 w 在計算圖裡沒有反向傳播的 gradient function，<code>grad_fn=&lt;MulBackward0&gt;</code>表示一個計算圖的節點。<br>這裡的 grad_fn (gradient function) 是一個<strong>指向反向傳播函數的引用</strong>，MulBackward0 表示這個反向傳播的 node 用逐元素乘 gradient function，除此之外還有 MatMulBackward (矩陣乘) / AddBackward (加) / DivBackward (除) 等。（其中 0 不知道代表什麼，測試過重複乘並不會變成 1。）</p><p><code>* np.sqrt(2. / fan_in)</code>這個操作相當於在 計算圖 裡加入了一個 乘法 節點（torch.autograd），反向傳播的時候會錯誤地給 dw 再乘上這個初始化操作<code>* np.sqrt(2. / fan_in)</code>。</p><h3 id="關於torch-no-grad"><a href="#關於torch-no-grad" class="headerlink" title="關於torch.no_grad()"></a><strong>關於torch.no_grad()</strong></h3><p>首先捋一遍pytorch梯度更新過程：</p><ol><li><code>output = model(x)</code>前向傳播，生成計算圖；</li><li><code>loss = torch.nn.CrossEntropyLoss(output, label)</code>計算損失；</li><li><code>loss.backward</code>計算所有requires_grad=True的節點構成的計算圖，用每一個node的grad_fn計算梯度，存到參數Tensor的.grad屬性(attribute)裡面；</li><li>更新參數（nn.Module / nn.Sequential）： <figure class="highlight python"><table><tr><td class="code"><pre><span class="line">optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  <span class="comment"># 定義優化器</span></span><br><span class="line">optimizer.step()  <span class="comment"># 用每個參數的.grad更新</span></span><br><span class="line">optimizer.zero_grad()  <span class="comment"># 清空.grad梯度</span></span><br></pre></td></tr></table></figure> 或者像assignment裡的barebone一樣手動操作： <figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> w <span class="keyword">in</span> params:</span><br><span class="line">            w -= learning_rate * w.grad</span><br><span class="line">            <span class="comment"># Manually zero the gradients after running the backward pass</span></span><br><span class="line">            w.grad.zero_()</span><br></pre></td></tr></table></figure></li></ol><p>在不需要反向傳播的時候就需要 torch.no_grad() 阻止 <code>output = model(x)</code> 生成計算圖，以節省推理時的內存和時間。</p><div class="table-container"><table><thead><tr><th>API</th><th>Flexibility</th><th>Convenience</th></tr></thead><tbody><tr><td>Barebone</td><td>High</td><td>Low</td></tr><tr><td><code>nn.Module</code></td><td>High</td><td>Medium</td></tr><tr><td><code>nn.Sequential</code></td><td>Low</td><td>High</td></tr></tbody></table></div><p>Barebone:<br><code>output = torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) → Tensor</code></p><p>nn.Module:<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">conv = torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=<span class="number">1</span>, padding=<span class="number">0</span>, dilation=<span class="number">1</span>, groups=<span class="number">1</span>, bias=<span class="literal">True</span>, padding_mode=<span class="string">'zeros'</span>, device=<span class="literal">None</span>, dtype=<span class="literal">None</span>)</span><br><span class="line">output = conv(input)</span><br></pre></td></tr></table></figure></p><p>除了 optimizer 有區別，用 nn.Module/nn.Sequential 這樣的寫法不用在外部定義繁瑣的 weights 再傳進去。</p><h2 id="傳參：可變對象-vs-不可變對象"><a href="#傳參：可變對象-vs-不可變對象" class="headerlink" title="傳參：可變對象 vs. 不可變對象"></a><strong>傳參：可變對象 vs. 不可變對象</strong></h2><p>不像 C 裡面傳參只能通過傳變量指針的方式 <code>void fn(int *var)</code> 在函數內部修改變量，python 有兩種對象：</p><ol><li>可變對象 (Mutable objects): list, dict 或是被訓練的 torch.nn.Module 都是可變對象，在函數內修改後函數外也可以看到變化；</li><li>不可變對象 (Immutable objects): int, float, tuple, str 跟 C 裡一樣，函數裡使用的時候其實是創造了一個新對象，函數內修改不會影響到函數外的變量本身。<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">fn</span><span class="params">(aaa)</span>:</span></span><br><span class="line"><span class="meta">... </span>    aaa.append(<span class="number">1</span>)</span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>bbb = [<span class="number">0</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>fn(bbb)  <span class="comment"># a list is a mutable object</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(bbb)</span><br><span class="line">[<span class="number">0</span>, <span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">fn</span><span class="params">(ccc)</span>:</span></span><br><span class="line"><span class="meta">... </span>    ccc += <span class="number">1</span></span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span></span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>ddd = <span class="number">0</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>fn(ddd)  <span class="comment"># an integer is an immutable object</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(ddd)</span><br><span class="line"><span class="number">0</span></span><br></pre></td></tr></table></figure></li></ol><p>所以訓練完 torch.nn.Module 子類 對象 model 之後是不需要返回的，函數內已經改變了 model 的 parameters 為優化後結果。<br>這裡的 Module 父類除了 forward() 之外還有 parameters() 可以返回模型中所有可學習的參數。</p><h2 id="model-eval"><a href="#model-eval" class="headerlink" title="model.eval()"></a><strong>model.eval()</strong></h2><p>model.eval() 是为了将模型设置为评估模式，主要影响 Dropout 和 BatchNorm 层。</p><ul><li>在评估模式下，Dropout 层不会丢弃神经元，而是使用所有神经元的输出；</li><li>BatchNorm 层会使用训练时计算的全局均值和方差。</li></ul><p>訓練時要調到 model.train() 模式。</p><h2 id="零碎pytorch"><a href="#零碎pytorch" class="headerlink" title="零碎pytorch"></a><strong>零碎pytorch</strong></h2><p>torch.flip和transforms.RandomHorizontalFlip 和 transforms.RandomVerticalFlip有兩種flip。</p><p><code>transforms.RandomApply([operation], p=0.5)</code>可以讓 torchvision.transforms 在某個概率下執行，不知道ToTensor()這種和nn.Linear()可不可以生效。</p><p>grayscale灰度圖像轉換公式：Y=0.2989×R+0.5870×G+0.1140×B，<br>transforms.Grayscale(num_output_channels=1)，設置成 3 的話 3 個通道都是一樣的灰度值。ass裡是設成 3 了。</p><p>cosine similarity又叫 normalized dot product 歸一化點積。</p><p>torch.masked_select(input, mask, *, out=None) 應用mask後會把mask=True的元素平鋪成一個 1 維 tensor 返回。</p><p>torchvision.utils.make_grid(images) 把多張圖拼成一個大圖。</p><h2 id="keepdim-True"><a href="#keepdim-True" class="headerlink" title="keepdim=True"></a><strong>keepdim=True</strong></h2><ul><li>sum()</li><li>mean()</li><li>std()</li><li>max()</li><li>min()</li><li>prod()</li><li>argmax()</li><li>argmin()</li></ul><p>以上這些函數都可以 keepdim，比如對 N, D 維度的 tensor x 做 x.sum(dim=1) 結果是 torch.Size([10]) 而 x.sum(dim=1, keepdim=True) 結果是 torch.Size([10, 1]) 。</p><p>最好想清楚廣播或者後續計算需不需要保持維度一致，<a href="https://drive.google.com/file/d/1SmUfq0mftlXVCXd6CNBp53PZ5wQPhB_C/view?usp=share_link">ass3的simclr的contrastive_loss.py</a>裡面sim_positive_pairs就因為這個結果錯了。</p><h2 id="使用預訓練resent"><a href="#使用預訓練resent" class="headerlink" title="使用預訓練resent"></a><strong>使用預訓練resent</strong></h2><p>用simclr的代碼做例子。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.nn.functional <span class="keyword">as</span> F</span><br><span class="line"><span class="keyword">from</span> torchvision.models.resnet <span class="keyword">import</span> resnet50</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Model</span><span class="params">(nn.Module)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, feature_dim=<span class="number">128</span>)</span>:</span></span><br><span class="line">        super(Model, self).__init__()</span><br><span class="line"></span><br><span class="line">        self.f = []  <span class="comment"># list</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 這裡創建的 resnet50() 只是個 nn.Module 的對象，</span></span><br><span class="line">        <span class="comment"># resnet50(pretrained=True) 就是加載參數，這裡只是隨機初始化的模型，即那些 FP32 的 weights 們都是垃圾</span></span><br><span class="line">        <span class="keyword">for</span> name, module <span class="keyword">in</span> resnet50().named_children():  <span class="comment"># named_children()遍歷所有子層</span></span><br><span class="line">            <span class="keyword">if</span> name == <span class="string">'conv1'</span>:  <span class="comment"># 重寫conv1</span></span><br><span class="line">                module = nn.Conv2d(<span class="number">3</span>, <span class="number">64</span>, kernel_size=<span class="number">3</span>, stride=<span class="number">1</span>, padding=<span class="number">1</span>, bias=<span class="literal">False</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> isinstance(module, nn.Linear) <span class="keyword">and</span> <span class="keyword">not</span> isinstance(module, nn.MaxPool2d):  <span class="comment"># 丟掉 linear 和 maxpooling 層</span></span><br><span class="line">                self.f.append(module)</span><br><span class="line">        <span class="comment"># encoder</span></span><br><span class="line">        self.f = nn.Sequential(*self.f)  <span class="comment"># 用改寫的 resnet50 構建新 Model</span></span><br><span class="line">        <span class="comment"># projection head</span></span><br><span class="line">        self.g = nn.Sequential(nn.Linear(<span class="number">2048</span>, <span class="number">512</span>, bias=<span class="literal">False</span>), nn.BatchNorm1d(<span class="number">512</span>),</span><br><span class="line">                               nn.ReLU(inplace=<span class="literal">True</span>), nn.Linear(<span class="number">512</span>, feature_dim, bias=<span class="literal">True</span>))  <span class="comment"># 降維 2048 到 512 到 128</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">forward</span><span class="params">(self, x)</span>:</span></span><br><span class="line">        x = self.f(x)</span><br><span class="line">        feature = torch.flatten(x, start_dim=<span class="number">1</span>)</span><br><span class="line">        out = self.g(feature)</span><br><span class="line">        <span class="keyword">return</span> F.normalize(feature, dim=<span class="number">-1</span>), F.normalize(out, dim=<span class="number">-1</span>)  <span class="comment"># 下游任務用的特徵 h 和投影後的自監督訓練特徵 z</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Classifier</span><span class="params">(nn.Module)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, num_class)</span>:</span></span><br><span class="line">        super(Classifier, self).__init__()</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Encoder.</span></span><br><span class="line">        self.f = Model().f  <span class="comment"># 用上述模型接一個 2048 維 FC 作為 classifier，另外注意上個 class 裡的 self.f 這裡還是叫作 self.f</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># Classifier.</span></span><br><span class="line">        self.fc = nn.Linear(<span class="number">2048</span>, num_class, bias=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">forward</span><span class="params">(self, x)</span>:</span></span><br><span class="line">        x = self.f(x)</span><br><span class="line">        feature = torch.flatten(x, start_dim=<span class="number">1</span>)</span><br><span class="line">        out = self.fc(feature)</span><br><span class="line">        <span class="keyword">return</span> out</span><br><span class="line"></span><br><span class="line">model = Classifier(num_class=len(train_data.classes))</span><br><span class="line"><span class="comment"># 這裡把預訓練好的 FP32 weights 覆蓋到上面改寫的 model 裡面。</span></span><br><span class="line"><span class="comment"># load_state_dict 會根據 torch.load 的權重字典 state_dict 裡的名稱（比如conv1）查找 model 中的對應層，只要名稱匹配就開始覆蓋，</span></span><br><span class="line"><span class="comment"># 如果 state_dict 和 model 的層名稱或架構不能完全匹配，若設 strict=False 就只匹配能匹配上的，若設 strict=True 則報錯，</span></span><br><span class="line"><span class="comment"># 如果名稱匹配，可比如conv1中weights參數量對不上，那無論如何都會報錯。</span></span><br><span class="line"><span class="comment">#  strict 默認值是 True，model.to 是把參數移動到 cpu 或 cuda。</span></span><br><span class="line">model.load_state_dict(torch.load(pretrained_path, map_location=<span class="string">'cpu'</span>), strict=<span class="literal">False</span>)</span><br><span class="line">model = model.to(device)</span><br><span class="line"><span class="keyword">for</span> param <span class="keyword">in</span> model.f.parameters():  <span class="comment"># ⚠️注意：這裡只把 self.f 裡的參數 freeze 了，所以 finetune 的時候其他自訂義的部分能被更新，但是 conv1 既沒加載參數又沒被訓練好像完全沒用？</span></span><br><span class="line">    param.requires_grad = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> thop <span class="keyword">import</span> profile, clever_format</span><br><span class="line">flops, params = profile(model, inputs=(torch.randn(<span class="number">1</span>, <span class="number">3</span>, <span class="number">32</span>, <span class="number">32</span>).to(device),))  <span class="comment"># 可以計算模型的浮點運算量 FLOPs 和參數量，通過用 randn 的隨機輸入做一次 forward pass</span></span><br><span class="line">flops, params = clever_format([flops, params])  <span class="comment"># 把輸出格式化為易讀的格式，比如 M 轉為百萬。</span></span><br><span class="line">print(<span class="string">'# Model Params: &#123;&#125; FLOPs: &#123;&#125;'</span>.format(params, flops))</span><br><span class="line">optimizer = optim.Adam(model.fc.parameters(), lr=<span class="number">1e-3</span>, weight_decay=<span class="number">1e-6</span>)  <span class="comment"># lr 1e-3 l2 1e-6 不用調參結果就很好了</span></span><br><span class="line">pretrain_results = &#123;<span class="string">'train_loss'</span>: [], <span class="string">'train_acc@1'</span>: [], <span class="string">'train_acc@5'</span>: [], <span class="string">'test_loss'</span>: [], <span class="string">'test_acc@1'</span>: [], <span class="string">'test_acc@5'</span>: []&#125;</span><br></pre></td></tr></table></figure><p>用 pytorch 真的很簡單。</p><h1 id="數學"><a href="#數學" class="headerlink" title="數學"></a><strong>數學</strong></h1><h2 id="矩阵计算加速"><a href="#矩阵计算加速" class="headerlink" title="矩阵计算加速"></a><strong>矩阵计算加速</strong></h2><p>反直觉的简化公式。<br>这些公式的反直觉点在于：</p><ul><li>避免直接操作整个矩阵或高维数据，通过预处理（如点积、模长）简化计算。</li><li>利用代数性质减少重复计算，如平方展开、trace 操作。</li><li>通过广播机制和维度扩展，实现高效并行计算。</li></ul><h3 id="1-L2距离矩阵"><a href="#1-L2距离矩阵" class="headerlink" title="1. L2距离矩阵"></a><strong>1. L2距离矩阵</strong></h3><p>L2 中的 “L” 是 “norm” 的缩写来源，表示范数（norm）的符号约定。“L” 最早来源于法语数学文献中用于 Lebesgue 空间（$L^p$-spaces）的符号，它现在广泛用于表示函数和向量范数。</p><p><strong>问题</strong>：给定两个矩阵 $A \in \mathbb{R}^{m \times n}$ 和 $B \in \mathbb{R}^{p \times n}$，计算两组向量之间的欧几里得距离矩阵 $D$，形状为 $(m, p)$。<br>公式：</p><script type="math/tex; mode=display">D[i, j] = \sqrt{\| A[i, :] - B[j, :] \|^2}</script><p>简化：</p><script type="math/tex; mode=display">D = \sqrt{\| A \|^2 + \| B \|^2 - 2 A \cdot B^T}</script><p><strong>解释</strong>：</p><ul><li>利用 $A \cdot B^T$ 点积高效计算向量间的关系。</li></ul><p>KNN计算(500,3072)和(5000,3072)得到(500,5000)的两两图片距离矩阵时，有两种做法：</p><p><strong>第一种：</strong></p><h4 id="I-直接通过广播计算一个-500-3072-5000-3072-得到-500-5000-的矩阵？"><a href="#I-直接通过广播计算一个-500-3072-5000-3072-得到-500-5000-的矩阵？" class="headerlink" title="I. 直接通过广播计算一个 $(500, 3072) - (5000, 3072)$ 得到 $(500, 5000)$ 的矩阵？"></a><strong>I. 直接通过广播计算一个 $(500, 3072) - (5000, 3072)$ 得到 $(500, 5000)$ 的矩阵？</strong></h4><p>我们需要计算一个矩阵 $\mathbf{C} \in \mathbb{R}^{500 \times 5000}$，其中每个元素为：</p><script type="math/tex; mode=display">C[i, j] = \|\mathbf{A}[i, :] - \mathbf{B}[j, :]\|^2</script><p>或展开为：</p><script type="math/tex; mode=display">C[i, j] = \sum_k \left(\mathbf{A}[i, k] - \mathbf{B}[j, k]\right)^2</script><hr><h4 id="直接广播计算的步骤"><a href="#直接广播计算的步骤" class="headerlink" title="直接广播计算的步骤"></a><strong>直接广播计算的步骤</strong></h4><ol><li><p><strong>输入矩阵形状</strong></p><ul><li>$\mathbf{A} \in \mathbb{R}^{500 \times 3072}$</li><li>$\mathbf{B} \in \mathbb{R}^{5000 \times 3072}$</li></ul></li><li><p><strong>扩展维度</strong></p><ul><li>将 $\mathbf{A}$ 扩展为 $(500, 1, 3072)$：  <script type="math/tex; mode=display">\mathbf{A}_{\text{expanded}} = \mathbf{A}[:, \text{np.newaxis}, :]</script></li><li>将 $\mathbf{B}$ 扩展为 $(1, 5000, 3072)$：  <script type="math/tex; mode=display">\mathbf{B}_{\text{expanded}} = \mathbf{B}[\text{np.newaxis}, :, :]</script></li></ul></li><li><p><strong>广播相减</strong></p><ul><li>利用广播机制直接相减，结果是形状为 $(500, 5000, 3072)$ 的 3D 矩阵：<script type="math/tex; mode=display">\mathbf{D} = \mathbf{A}_{\text{expanded}} - \mathbf{B}_{\text{expanded}}</script></li></ul></li><li><p><strong>计算平方差</strong></p><ul><li>对最后一个维度（即 3072）计算平方和，得到形状为 $(500, 5000)$ 的矩阵：<script type="math/tex; mode=display">\mathbf{C} = \sum_{k=1}^{3072} \mathbf{D}[:, :, k]^2</script></li></ul></li></ol><hr><h4 id="完整代码实现"><a href="#完整代码实现" class="headerlink" title="完整代码实现"></a><strong>完整代码实现</strong></h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例输入</span></span><br><span class="line">A = np.random.rand(<span class="number">500</span>, <span class="number">3072</span>)  <span class="comment"># (500, 3072)</span></span><br><span class="line">B = np.random.rand(<span class="number">5000</span>, <span class="number">3072</span>)  <span class="comment"># (5000, 3072)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 扩展维度</span></span><br><span class="line">A_expanded = A[:, np.newaxis, :]  <span class="comment"># (500, 1, 3072)</span></span><br><span class="line">B_expanded = B[np.newaxis, :, :]  <span class="comment"># (1, 5000, 3072)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 广播减法并计算平方差</span></span><br><span class="line">C = np.sum((A_expanded - B_expanded) ** <span class="number">2</span>, axis=<span class="number">2</span>)  <span class="comment"># (500, 5000)</span></span><br><span class="line"></span><br><span class="line">print(C.shape)  <span class="comment"># 输出: (500, 5000)</span></span><br></pre></td></tr></table></figure><hr><h4 id="问题"><a href="#问题" class="headerlink" title="问题"></a><strong>问题</strong></h4><p>“直接广播可能导致内存不足”的原因是 <strong>广播机制会在内存中创建一个扩展后的高维临时数组</strong>。</p><p>对于形状为 $ (500, 3072) $ 的矩阵 $ A $ 和 $ (5000, 3072) $ 的矩阵 $ B $：</p><ol><li>扩展后它们会形成 $ (500, 1, 3072) $ 和 $ (1, 5000, 3072) $。</li><li><strong>相减后生成一个 $ (500, 5000, 3072) $ 的 3D 数组</strong>。</li></ol><p>这个 3D 数组需要存储 $ 500 \times 5000 \times 3072 $ 个浮点数，占用约 <strong>76 GB</strong>（若每个浮点数占 8 字节）。<br>对于大规模数据，内存无法承受，因此直接广播操作会报错或导致性能问题。</p><p>使用范数分解公式避免构造高维中间数组，可以极大节省内存。</p><hr><p><strong>第二种：</strong></p><h4 id="II-优化方法：使用范数分解公式"><a href="#II-优化方法：使用范数分解公式" class="headerlink" title="II. 优化方法：使用范数分解公式"></a><strong>II. 优化方法：使用范数分解公式</strong></h4><p>由于直接广播会创建一个巨大的 $(500, 5000, 3072)$ 的临时数组，可能导致内存不足，我们可以利用范数分解公式优化计算。</p><p>公式展开为：</p><script type="math/tex; mode=display">C[i, j] = \|\mathbf{A}[i, :]\|^2 + \|\mathbf{B}[j, :]\|^2 - 2 \cdot \mathbf{A}[i, :] \cdot \mathbf{B}[j, :]^T</script><hr><h4 id="优化后的步骤"><a href="#优化后的步骤" class="headerlink" title="优化后的步骤"></a><strong>优化后的步骤</strong></h4><ol><li><p>预先计算每行向量的平方和：</p><ul><li>对于 $\mathbf{A}$，计算 $|\mathbf{A}[i, :]|^2$：<script type="math/tex; mode=display">\mathbf{A}_{\text{norm}} = \sum_{k=1}^{3072} \mathbf{A}[:, k]^2 \quad \text{(shape: } (500, 1)\text{)}</script></li><li>对于 $\mathbf{B}$，计算 $|\mathbf{B}[j, :]|^2$：<script type="math/tex; mode=display">\mathbf{B}_{\text{norm}} = \sum_{k=1}^{3072} \mathbf{B}[:, k]^2 \quad \text{(shape: } (1, 5000)\text{)}</script></li></ul></li><li><p>计算点积：</p><script type="math/tex; mode=display">\mathbf{D} = \mathbf{A} \cdot \mathbf{B}^T \quad \text{(shape: } (500, 5000)\text{)}</script></li><li><p>合并结果：</p><script type="math/tex; mode=display">\mathbf{C} = \mathbf{A}_{\text{norm}} + \mathbf{B}_{\text{norm}} - 2 \cdot \mathbf{D}</script></li></ol><hr><h4 id="优化代码实现"><a href="#优化代码实现" class="headerlink" title="优化代码实现"></a><strong>优化代码实现</strong></h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 计算每行向量的平方和</span></span><br><span class="line">A_norm = np.sum(A ** <span class="number">2</span>, axis=<span class="number">1</span>).reshape(<span class="number">-1</span>, <span class="number">1</span>)  <span class="comment"># (500, 1)</span></span><br><span class="line">B_norm = np.sum(B ** <span class="number">2</span>, axis=<span class="number">1</span>).reshape(<span class="number">1</span>, <span class="number">-1</span>)  <span class="comment"># (1, 5000)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算点积</span></span><br><span class="line">dot_product = A @ B.T  <span class="comment"># (500, 5000)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 利用范数分解公式</span></span><br><span class="line">C = A_norm + B_norm - <span class="number">2</span> * dot_product</span><br><span class="line"></span><br><span class="line">print(C.shape)  <span class="comment"># 输出: (500, 5000)</span></span><br></pre></td></tr></table></figure><hr><h4 id="两种方法对比"><a href="#两种方法对比" class="headerlink" title="两种方法对比"></a><strong>两种方法对比</strong></h4><div class="table-container"><table><thead><tr><th>方法</th><th>内存占用</th><th>计算速度</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>直接广播相减</strong></td><td>高（需要创建 3D 数组）</td><td>简单实现</td><td>小规模矩阵</td></tr><tr><td><strong>范数分解公式</strong></td><td>低（避免创建 3D 数组）</td><td>更高效</td><td>大规模矩阵</td></tr></tbody></table></div><h3 id="2-余弦相似度矩阵"><a href="#2-余弦相似度矩阵" class="headerlink" title="2. 余弦相似度矩阵"></a><strong>2. 余弦相似度矩阵</strong></h3><p><strong>问题</strong>：给定矩阵 $A$ 和 $B$，计算两组向量的余弦相似度矩阵：</p><script type="math/tex; mode=display">\text{CosSim}[i, j] = \frac{A[i, :] \cdot B[j, :]^T}{\| A[i, :] \| \cdot \| B[j, :] \|}</script><p>简化：</p><script type="math/tex; mode=display">\text{CosSim} = \frac{A \cdot B^T}{\sqrt{\text{row\_norm}(A)} \cdot \sqrt{\text{row\_norm}(B)}^T}</script><p><strong>解释</strong>：</p><ul><li>先计算点积 $A \cdot B^T$。</li><li>再将每行的模长预计算为一个列向量，最后进行广播除法。</li></ul><h3 id="3-矩阵-Frobenius-范数的平方"><a href="#3-矩阵-Frobenius-范数的平方" class="headerlink" title="3. 矩阵 Frobenius 范数的平方"></a><strong>3. 矩阵 Frobenius 范数的平方</strong></h3><p>Frobenius 范数：</p><script type="math/tex; mode=display">\| A \|_F^2 = \sum_{i,j} A_{ij}^2</script><p>如果需要计算两个矩阵 $A$ 和 $B$ 之间的差的 Frobenius 范数：</p><script type="math/tex; mode=display">\| A - B \|_F^2 = \| A \|_F^2 + \| B \|_F^2 - 2 \cdot \text{trace}(A^T B)</script><p><strong>解释</strong>：</p><ul><li>通过展开 $| A - B |_F^2$，简化计算，避免构造 $A - B$。</li></ul><h3 id="4-交叉熵损失的高效计算"><a href="#4-交叉熵损失的高效计算" class="headerlink" title="4. 交叉熵损失的高效计算"></a><strong>4. 交叉熵损失的高效计算</strong></h3><p>交叉熵损失公式：</p><script type="math/tex; mode=display">L = - \sum_{i} y_i \log(\hat{y}_i)</script><p>如果 $y$ 是 one-hot 编码的标签，交叉熵可以简化为直接取预测的正确类别的概率对数：</p><script type="math/tex; mode=display">L = - \log(\hat{y}_{\text{true}})</script><p><strong>解释</strong>：</p><ul><li>不需要计算 $y$ 和 $\hat{y}$ 的逐元素乘积，只需取索引值。</li></ul><h3 id="5-矩阵的平方和（双循环的优化）"><a href="#5-矩阵的平方和（双循环的优化）" class="headerlink" title="5. 矩阵的平方和（双循环的优化）"></a><strong>5. 矩阵的平方和（双循环的优化）</strong></h3><p>直接计算矩阵 $A \in \mathbb{R}^{m \times n}$ 中每对行向量的平方和：</p><script type="math/tex; mode=display">S[i, j] = \sum_{k} (A[i, k] + A[j, k])^2</script><p>可以化简为：</p><script type="math/tex; mode=display">S[i, j] = \| A[i, :] \|^2 + \| A[j, :] \|^2 + 2 A[i, :] \cdot A[j, :]^T</script><p><strong>解释</strong>：</p><ul><li>避免显式计算所有对的组合，通过提前计算模长和点积直接构造。</li></ul><h3 id="6-矩阵乘法优化（维度分解）"><a href="#6-矩阵乘法优化（维度分解）" class="headerlink" title="6. 矩阵乘法优化（维度分解）"></a><strong>6. 矩阵乘法优化（维度分解）</strong></h3><p>如果需要计算 $C = A^T A$，其中 $A \in \mathbb{R}^{m \times n}$，可通过分块减少乘法次数：</p><ul><li>普通算法时间复杂度为 $O(m^2 n)$。</li><li>利用分块或对称性质，可以优化为 $O(m^2)$。</li></ul><h2 id="矩陣求導，要轉置"><a href="#矩陣求導，要轉置" class="headerlink" title="矩陣求導，要轉置"></a><strong>矩陣求導，要轉置</strong></h2><p>矩阵C=AB那C对A的导数是B的转置，C对B的导数是A的转置。</p><p>假设 $C = A \cdot B$，则矩阵求导结果如下：</p><ol><li><p><strong>$C$ 对 $A$ 的导数</strong>：  </p><script type="math/tex; mode=display">\frac{\partial C}{\partial A} = B^T</script><p>因为 $A$ 的每个元素对 $C$ 的每个元素的贡献是通过 $B$ 的列影响的。</p></li><li><p><strong>$C$ 对 $B$ 的导数</strong>：  </p><script type="math/tex; mode=display">\frac{\partial C}{\partial B} = A^T</script><p>因为 $B$ 的每个元素对 $C$ 的每个元素的贡献是通过 $A$ 的行影响的。</p></li></ol><h3 id="例子验证"><a href="#例子验证" class="headerlink" title="例子验证"></a>例子验证</h3><p>假设：</p><script type="math/tex; mode=display">A = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}, \quadB = \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix}</script><p>计算 $C = A \cdot B$：</p><script type="math/tex; mode=display">C = \begin{bmatrix} 1 \cdot 5 + 2 \cdot 7 & 1 \cdot 6 + 2 \cdot 8 \\ 3 \cdot 5 + 4 \cdot 7 & 3 \cdot 6 + 4 \cdot 8 \end{bmatrix}= \begin{bmatrix} 19 & 22 \\ 43 & 50 \end{bmatrix}</script><ol><li><p>对 $A$ 求导（结果为 $B^T$）：</p><script type="math/tex; mode=display">B^T = \begin{bmatrix} 5 & 7 \\ 6 & 8 \end{bmatrix}</script></li><li><p>对 $B$ 求导（结果为 $A^T$）：(在矩陣乘法裡，A的1和2，只會受到B的5和7影響；對A的3和4來說只有B的6和8會影響結果，所以正好轉置過來)</p><script type="math/tex; mode=display">A^T = \begin{bmatrix} 1 & 3 \\ 2 & 4 \end{bmatrix}</script></li></ol><h2 id="商的求导法则"><a href="#商的求导法则" class="headerlink" title="商的求导法则"></a><strong>商的求导法则</strong></h2><p>如果函数为 $\frac{u(x)}{v(x)}$，其导数公式是：</p><script type="math/tex; mode=display">\frac{d}{dx} \left(\frac{u}{v}\right) = \frac{u'v - uv'}{v^2}.</script><h3 id="例子-1-frac-x-x-1-的导数"><a href="#例子-1-frac-x-x-1-的导数" class="headerlink" title="例子 1: $ \frac{x}{x+1} $ 的导数"></a><strong>例子 1: $ \frac{x}{x+1} $ 的导数</strong></h3><p>设 $u = x$，$v = x+1$：  </p><ul><li>$u’ = 1$，$v’ = 1$。</li></ul><p>代入公式：</p><script type="math/tex; mode=display">\frac{d}{dx} \left(\frac{x}{x+1}\right) = \frac{(1)(x+1) - (x)(1)}{(x+1)^2} = \frac{x+1-x}{(x+1)^2} = \frac{1}{(x+1)^2}.</script><h3 id="例子-2-frac-x-2-x-2-的导数"><a href="#例子-2-frac-x-2-x-2-的导数" class="headerlink" title="例子 2: $ \frac{x^2}{x+2} $ 的导数"></a><strong>例子 2: $ \frac{x^2}{x+2} $ 的导数</strong></h3><p>设 $u = x^2$，$v = x+2$：  </p><ul><li>$u’ = 2x$，$v’ = 1$。</li></ul><p>代入公式：</p><script type="math/tex; mode=display">\frac{d}{dx} \left(\frac{x^2}{x+2}\right) = \frac{(2x)(x+2) - (x^2)(1)}{(x+2)^2}.</script><p>化简：</p><script type="math/tex; mode=display">\frac{d}{dx} \left(\frac{x^2}{x+2}\right) = \frac{2x^2 + 4x - x^2}{(x+2)^2} = \frac{x^2 + 4x}{(x+2)^2}.</script><h2 id="幂函数的求导公式"><a href="#幂函数的求导公式" class="headerlink" title="幂函数的求导公式"></a><strong>幂函数的求导公式</strong></h2><p>如果 $f(x) = x^n$，对 $x$ 求导，我们直接应用幂函数的求导公式：</p><script type="math/tex; mode=display">\frac{d}{dx}[x^n] = n x^{n-1}.</script><p>这是基本的导数公式，其中：</p><ul><li>$n$ 是常数，</li><li>$x^{n-1}$ 是 $x$ 的幂次降低1后的表达式。</li></ul><p>例如：</p><ol><li>若 $f(x) = x^3$，则 $f’(x) = 3x^2$；</li><li>若 $f(x) = x^{-2}$，则 $f’(x) = -2x^{-3}$；</li><li>若 $f(x) = x^{1/2}$，则 $f’(x) = \frac{1}{2}x^{-1/2} = \frac{1}{2\sqrt{x}}$。</li></ol><h2 id="正則項的偏導"><a href="#正則項的偏導" class="headerlink" title="正則項的偏導"></a><strong>正則項的偏導</strong></h2><p>对于正则化项：<br>$<br>\text{Regularization} = \text{reg} \cdot \left(|W_1|_F^2 + |W_2|_F^2\right)<br>$<br>计算对各参数的偏导：</p><ol><li><p><strong>对 $W_2$：</strong><br>$<br>\frac{\partial \text{Regularization}}{\partial W_2} = 2 \cdot \text{reg} \cdot W_2<br>$</p></li><li><p><strong>对 $b_2$：</strong><br>$<br>\frac{\partial \text{Regularization}}{\partial b_2} = 0<br>$<br>因为正则化不包含偏置。</p></li><li><p><strong>对 $W_1$：</strong><br>$<br>\frac{\partial \text{Regularization}}{\partial W_1} = 2 \cdot \text{reg} \cdot W_1<br>$</p></li><li><p><strong>对 $b_1$：</strong><br>$<br>\frac{\partial \text{Regularization}}{\partial b_1} = 0<br>$</p></li></ol><h3 id="总结："><a href="#总结：" class="headerlink" title="总结："></a>总结：</h3><ul><li>正则化项仅对权重 $W_1$ 和 $W_2$ 起作用，偏导为 $2 \cdot \text{reg} \cdot W$。  </li><li>偏置 $b_1$ 和 $b_2$ 的偏导为零。</li><li>對 $dx$ 的偏導也是0，梯度往前傳遞是靠 $dx$ 。</li></ul><p>b1不是input_dim，b2不是hidden_dim:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">self.params[<span class="string">'W1'</span>] = np.random.normal(loc=<span class="number">0.0</span>, scale=weight_scale, size=(input_dim, hidden_dim))</span><br><span class="line">self.params[<span class="string">'b1'</span>] = np.zeros(hidden_dim)</span><br><span class="line">self.params[<span class="string">'W2'</span>] = np.random.normal(loc=<span class="number">0.0</span>, scale=weight_scale, size=(hidden_dim, num_classes))</span><br><span class="line">self.params[<span class="string">'b2'</span>] = np.zeros(num_classes)</span><br></pre></td></tr></table></figure><h2 id="Adam優化版"><a href="#Adam優化版" class="headerlink" title="Adam優化版"></a><strong>Adam優化版</strong></h2><p>如果你看到的 Adam 公式版本中没有显式的 $t$（时间步），那可能是省略了 $t$ 的具体表示形式。但实际上，Adam 的完整公式中 $t$ 隐含在动量更新和偏差校正因子中。以下是 Adam 优化器的完整公式：</p><ol><li><p><strong>一阶动量（梯度的指数加权平均）</strong>：</p><script type="math/tex; mode=display">m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t</script></li><li><p><strong>二阶动量（梯度平方的指数加权平均）</strong>：</p><script type="math/tex; mode=display">v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2</script></li><li><p><strong>偏差校正</strong>（修正初期估计的偏向零问题）：</p><script type="math/tex; mode=display">\hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t}</script></li><li><p><strong>参数更新</strong>：</p><script type="math/tex; mode=display">\theta_{t+1} = \theta_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}</script></li></ol><p>这里的 $t$ 是显式表示每个时间步的迭代次数，用于偏差校正因子的计算。在实际实现中，时间步通常由代码中的循环或计数器隐式管理，因此部分文档可能会省略 $t$ 的具体表示。</p><p>如果你看到的公式中没有偏差校正因子的部分，可能是简化版本，忽略了初期动量估计偏差的影响。这种版本常用于直观解释或早期实现中。</p><p>beta ** t，t越大m_hat就越大，防止<a href="#5-adam">一開始過小的問題</a>。</p><h2 id="对数的商的性质"><a href="#对数的商的性质" class="headerlink" title="对数的商的性质"></a><strong>对数的商的性质</strong></h2><script type="math/tex; mode=display">\log\left(\frac{a}{b}\right) = \log a - \log b</script><p>其中 $a &gt; 0, b &gt; 0$，对数的底可以是任意正数（通常是 10 或 $e$，但不能是 1）。</p><h2 id="BN的梯度"><a href="#BN的梯度" class="headerlink" title="BN的梯度"></a><strong>BN的梯度</strong></h2><h3 id="1-Batch-Normalization-BN-层的梯度计算"><a href="#1-Batch-Normalization-BN-层的梯度计算" class="headerlink" title="1. Batch Normalization (BN) 层的梯度计算"></a>1. <strong>Batch Normalization (BN) 层的梯度计算</strong></h3><p>Batch Normalization 的反向传播较为复杂，因为它涉及多个步骤的链式法则计算。下面是完整推导过程和实现思路：</p><h4 id="BN-前向传播公式"><a href="#BN-前向传播公式" class="headerlink" title="BN 前向传播公式"></a><strong>BN 前向传播公式</strong></h4><p>给定输入 $x_i$：</p><ol><li>计算均值和方差：<script type="math/tex; mode=display">\mu = \frac{1}{m} \sum_{i=1}^m x_i, \quad \sigma^2 = \frac{1}{m} \sum_{i=1}^m (x_i - \mu)^2</script></li><li>标准化：<script type="math/tex; mode=display">\hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}}</script></li><li>应用缩放和平移（可学习参数 $\gamma$ 和 $\beta$）：<script type="math/tex; mode=display">y_i = \gamma \hat{x}_i + \beta</script></li></ol><hr><h4 id="BN-反向传播公式"><a href="#BN-反向传播公式" class="headerlink" title="BN 反向传播公式"></a><strong>BN 反向传播公式</strong></h4><div align=center><img src="/pics/cs231n/batchnorm_graph.png" loading="lazy" alt="computational graph" width="50%" height="50%"></div><h5 id="1-梯度传播关系"><a href="#1-梯度传播关系" class="headerlink" title="1. 梯度传播关系"></a><strong>1. 梯度传播关系</strong></h5><p>BN 的输出 $y_i$ 的梯度通过链式法则回传：</p><script type="math/tex; mode=display">\frac{\partial L}{\partial x_i} = \frac{\partial L}{\partial y_i} \cdot \frac{\partial y_i}{\partial x_i}</script><p>因此，关键在于计算 $\frac{\partial y_i}{\partial x_i}$。</p><h5 id="2-分步骤求解"><a href="#2-分步骤求解" class="headerlink" title="2. 分步骤求解"></a><strong>2. 分步骤求解</strong></h5><ul><li><p><strong>对 $\gamma$ 和 $\beta$ 的梯度：</strong></p><script type="math/tex; mode=display">\frac{\partial L}{\partial \gamma} = \sum_{i=1}^m \frac{\partial L}{\partial y_i} \cdot \hat{x}_i, \quad \frac{\partial L}{\partial \beta} = \sum_{i=1}^m \frac{\partial L}{\partial y_i}</script></li><li><p><strong>对 $\hat{x}_i$ 的梯度：</strong></p><script type="math/tex; mode=display">\frac{\partial L}{\partial \hat{x}_i} = \frac{\partial L}{\partial y_i} \cdot \gamma</script></li></ul><h5 id="3-對-x-i-的梯度需要-frac-partial-mu-partial-x-i-frac-partial-v-partial-x-i-frac-partial-sigma-partial-v-frac-partial-Y-partial-sigma-and-frac-partial-Y-partial-mu"><a href="#3-對-x-i-的梯度需要-frac-partial-mu-partial-x-i-frac-partial-v-partial-x-i-frac-partial-sigma-partial-v-frac-partial-Y-partial-sigma-and-frac-partial-Y-partial-mu" class="headerlink" title="3. 對 $ x_i $ 的梯度需要$\frac{\partial \mu}{\partial x_i}, \frac{\partial v}{\partial x_i}, \frac{\partial \sigma}{\partial v}, \frac{\partial Y}{\partial \sigma}$, and $\frac{\partial Y}{\partial \mu}$"></a><strong>3. 對 $ x_i $ 的梯度需要$\frac{\partial \mu}{\partial x_i}, \frac{\partial v}{\partial x_i}, \frac{\partial \sigma}{\partial v}, \frac{\partial Y}{\partial \sigma}$, and $\frac{\partial Y}{\partial \mu}$</strong></h5><p>計算圖裡的var和mu那條關係可以忽略，可以不走那邊。</p><p>$\frac{\partial Y}{\partial \sigma}$, $\frac{\partial Y}{\partial \mu}$其實算的是$\gamma \cdot \frac{\partial \hat{x_i}}{\partial \sigma}$, $\gamma \cdot \frac{\partial \hat{x_i}}{\partial \mu}$。</p><p>公式提要：<script type="math/tex">\begin{align}& \mu=\frac{1}{N}\sum_{k=1}^N x_k  &  v=\frac{1}{N}\sum_{k=1}^N (x_k-\mu)^2 \notag\\& \sigma=\sqrt{v+\epsilon}         &  y_i=\frac{x_i-\mu}{\sigma} \notag\end{align}</script></p><ol><li><p>mean對x的導數：<br>$\text{mean} = \frac{1}{n} \sum_{i=1}^n x_i$，对 $x_k$ 求导时：</p><script type="math/tex; mode=display">\frac{\partial \text{mean}}{\partial x_k} = \frac{1}{n}</script></li><li><p>方差的導數$\frac{\partial v}{\partial x_i}$：</p><script type="math/tex; mode=display">\begin{equation}\frac\partial{\partial x_i}\frac 1N\sum_k^N (x_k-\mu)^2\notag\end{equation}</script><script type="math/tex; mode=display">\begin{equation}= \frac 1N\sum_k^N \frac\partial{\partial x_i}(x_k-\mu)^2\notag\end{equation}</script><p><a href="https://math.stackexchange.com/a/2887894">擴展求和，</a></p><script type="math/tex; mode=display">\begin{equation}= \frac 1N \Biggl[\frac\partial{\partial x_i}(x_0-\mu)^2 + \cdots + \frac\partial{\partial x_i}(x_i-\mu)^2 + \cdots + \frac\partial{\partial x_i}(x_N-\mu)^2\Biggl]\notag\end{equation}</script><p>發現除了$\frac\partial{\partial x_i}, (x_i-\mu)^2=2(x_i - \mu)$都是0，所以</p><script type="math/tex; mode=display">\begin{equation}= \frac 2N (x_i - \mu)\notag, \end{equation}</script><p>或者把mean也展開就是</p><script type="math/tex; mode=display">\begin{equation}=\dfrac{2}{N^2}\left[Nx_i-\sum_kx_k\right].\notag\end{equation}</script></li><li><p>standard deviation標準差對variance方差的導數很好求，就是幂函数的求导公式，$\sigma=\sqrt{v+\epsilon}$導數是$\sigma’=\frac{1}{2\sqrt{v+\epsilon}} = \frac{1}{2\sigma}$。</p></li></ol><blockquote><p>chatgpt說：計算$\frac{x-mean}{std}$對mean和std的偏導時，雖然mean和std都是依賴於x的統計特性，但算一個偏導可以把另一個看作常數：</p><ol><li>$\gamma \cdot (\frac{\partial \hat{x_i}}{\partial \sigma} = \frac{-1}{std})$</li><li>$\gamma \cdot (\frac{\partial \hat{x_i}}{\partial \mu} = -\frac{x - mean}{std^2} = -\frac{x_i-\mu}{std^2})$ 他媽的發現錯誤原因是這倆求反了，下面也代入反了，一下午打水漂。chatgpt說的是對的，計算後證明這倆反過來算對了。<script type="math/tex; mode=display">= \gamma \cdot \frac{-1}{std} \cdot \frac{1}{2\sqrt{v+\epsilon}} \cdot \frac 2N (x_i - \mu) + \gamma \cdot  -\frac{x_i - \mu}{std^2} \cdot \frac1N</script><script type="math/tex; mode=display">= -\gamma(\frac{x_i - \mu}{N\cdot std\cdot\sqrt{v+\epsilon}} + \frac{x_i-\mu}{N \cdot std^2})</script><script type="math/tex; mode=display">= - \frac{2\gamma \cdot (x_i - \mu)}{N \cdot variance}</script>但事實證明不對，而且沒理解好鏈式法則，沒有加上直接項。正確的理解在<a href="#chain-rule-多元複合函數求導法則">下面</a>，按<a href="#chain-rule-多元複合函數求導法則">下面</a>的公式，這裡其實改寫成$L = f(\Phi, \Psi, \Omega), \Phi = a(x) = x, \Psi = b(x) = mean, \Omega = c(x) = std, f(\Phi, \Psi, \Omega) = \frac{\Phi - \Psi}\Omega$。參考<a href="https://kevinzakka.github.io/2016/09/14/batch_normalization/">https://kevinzakka.github.io/2016/09/14/batch_normalization/</a> 1.沒有把另外一個統計量當作常數；2.對$\mu$和$\sigma$的任何求導，根據<a href="#一個變量的導數和他本身維度通常一致">下文一個變量的導數和他本身維度通常一致</a>，結果應該是個scalar或者vector，所以要求和，我的計算都漏了求和！重新算：</li></ol></blockquote><p>$\frac{\partial Y}{\partial x_i} = \frac{\partial Y}{\partial \hat{x_i}}\frac{\partial \hat{x_i}}{\partial x_i} + \frac{\partial Y}{\partial \sigma}\frac{\partial \sigma}{\partial v}\frac{\partial v}{\partial x_i} + \frac{\partial Y}{\partial \mu}\frac{\partial \mu}{\partial x_i}$ 還差標題的后倆：</p><ol><li>$\frac{\partial Y}{\partial \sigma} = \gamma \cdot \sum_{i=1}^N upstream \cdot \frac{\partial \hat{x_i}}{\partial \sigma}$ 中，<script type="math/tex; mode=display">\frac{\partial \hat{x_i}}{\partial \sigma} = -\frac{x_i-\mu}{\sigma^2}</script></li><li><p>$\frac{\partial v}{\partial \mu}$：</p><ol><li><p>方差公式：</p><script type="math/tex; mode=display">\sigma^2 = \frac{1}{N} \sum_{i=1}^N (x_i - \mu)^2</script></li><li><p>对 $\mu$ 求导数：</p><script type="math/tex; mode=display">\frac{\partial \sigma^2}{\partial \mu} = \frac{\partial}{\partial \mu} \left( \frac{1}{N} \sum_{i=1}^N (x_i - \mu)^2 \right)</script></li><li><p>将求导运算移入求和符号中：</p><script type="math/tex; mode=display">\frac{\partial \sigma^2}{\partial \mu} = \frac{1}{N} \sum_{i=1}^N \frac{\partial}{\partial \mu} (x_i - \mu)^2</script></li><li><p>求单项的导数：</p><script type="math/tex; mode=display">\frac{\partial}{\partial \mu} (x_i - \mu)^2 = 2 (x_i - \mu) \cdot \frac{\partial}{\partial \mu} (x_i - \mu)</script><p>注意到 $\frac{\partial}{\partial \mu} (x_i - \mu) = -1$，代入得：</p><script type="math/tex; mode=display">\frac{\partial}{\partial \mu} (x_i - \mu)^2 = -2 (x_i - \mu)</script></li><li><p>将结果代入整体公式：</p><script type="math/tex; mode=display">\frac{\partial \sigma^2}{\partial \mu} = \frac{1}{N} \sum_{i=1}^N -2 (x_i - \mu)</script></li><li><p>提取常数项 $-2$：</p><script type="math/tex; mode=display">\frac{\partial \sigma^2}{\partial \mu} = -\frac{2}{N} \sum_{i=1}^N (x_i - \mu)</script></li><li><p>根据均值的定义，$\sum_{i=1}^N (x_i - \mu) = 0$，因为均值是数据的平衡点。</p></li></ol></li><li><p>$\frac{\partial Y}{\partial \mu} = \gamma \cdot \sum_{i=1}^N upstream \cdot \frac{\partial \hat{x_i}}{\partial \mu}$ 中，方差和標準差sigma其實也是mean的函數$v=\frac{1}{N}\sum_{k=1}^N (x_k-\mu)^2$，所以根據<a href="#chain-rule-多元複合函數求導法則">復合函數鏈式法則</a>：</p><script type="math/tex; mode=display">\frac{\partial \hat{x_i}}{\partial \mu} = \frac{\partial \hat{x_i}}{\partial \mu} + \frac{\partial \hat{x_i}}{\partial \sigma}\frac{\partial \sigma}{\partial v}\frac{\partial v}{\partial \mu}\\= -\frac{1}{std}+(-\frac{x-\mu}{\sigma^2})\frac{1}{2\sqrt{v+\epsilon}}\cdot0\\= -\frac{1}{std}</script></li></ol><p>最後，$\frac{\partial \hat{x_i}}{\partial x_i} = \frac{1}{\sigma}$。</p><p>最終，</p><script type="math/tex; mode=display">\frac{\partial Y}{\partial x_i}</script><script type="math/tex; mode=display">=\frac{\partial Y}{\partial \hat{x_i}}\frac{\partial \hat{x_i}}{\partial x_i} + \frac{\partial Y}{\partial \sigma}\frac{\partial \sigma}{\partial v}\frac{\partial v}{\partial x_i} + \frac{\partial Y}{\partial \mu}\frac{\partial \mu}{\partial x_i}\\</script><script type="math/tex; mode=display">= \gamma \cdot upstream \cdot \frac{1}{\sigma} +  \gamma \cdot \sum_{j=1}^N upstream \cdot -\frac{x_j - \mu}{\sigma^2} \cdot \frac{1}{2\sigma} \cdot \frac 2N (x_i - \mu) + \gamma \cdot \sum_{j=1}^N upstream \cdot \frac{-1}{\sigma}  \cdot \frac1N</script><p>化簡：</p><script type="math/tex; mode=display">= \frac{\gamma}{\sigma} \cdot upstream - \frac\gamma\sigma \cdot \frac 1N \cdot \sum_{j=1}^N upstream \cdot \frac{x_j - \mu}{\sigma} \cdot \frac{x_i - \mu}{\sigma} - \frac\gamma\sigma \cdot \frac1N \cdot \sum_{j=1}^N upstream</script><script type="math/tex; mode=display">= \frac\gamma\sigma(upstream-\frac{\hat{x_i}}{N}\cdot\sum_{j=1}^N upstream\cdot\hat{x_j}-\frac1N \cdot \sum_{j=1}^N upstream)</script><p>其中的$\gamma \cdot upstream$其實可以合成為$\hat{upstream}$，測試的speedup是3.69x。<strong>另外layernorm就把所有的axis=0改成axis=1即可。</strong><br><a href="https://github.com/V2beach/books/blob/main/Batch%20Normalization.pdf">論文裡求過偏導了，但算起來比較慢。</a>這樣用計算圖鏈式法則一步到位，雖然手算慢，但電腦運算快。<br>Note that the batch normalization paper suggests a different test-time behavior: they compute sample mean and variance for each feature using a large number of training images rather than using a running average. For this implementation we have chosen to use running averages instead since they do not require an additional estimation step; the torch7 implementation of batch normalization also uses running averages.<br>掙扎了大半天時間梯度終於算對了，感謝<a href="https://kevinzakka.github.io/2016/09/14/batch_normalization/">UCB哥的推導</a>，最後發現了兩個問題，1.我居然忘記算upstream（dout）了，2.求和的對象太關鍵了，求和的範圍是$\mu$，$\sigma$的偏導範圍決定的。這種公式問chatgpt它還是算不明白的，雖然它的結果通常沒錯。<br>四年前倒在這，直到現在還是不能像<a href="https://blog.csdn.net/SpicyCoder/article/details/97796858">他</a>和<a href="https://mahanfathi.github.io/CS231/">他</a>們一樣，推導不出來<a href="https://github.com/V2beach/cs231n/blob/main/assignment2/cs231n/layers.py">batchnorm_backward_alt</a>就放棄跳過，死磕，不死磕心裡永遠有個結。</p><p><img src="/pics/cs231n/Screenshot 2024-11-22 at 13.06.27.png" alt="通過！"></p><hr><h3 id="2-Dropout-层的梯度计算"><a href="#2-Dropout-层的梯度计算" class="headerlink" title="2. Dropout 层的梯度计算"></a>2. <strong>Dropout 层的梯度计算</strong></h3><h4 id="Dropout-前向传播"><a href="#Dropout-前向传播" class="headerlink" title="Dropout 前向传播"></a><strong>Dropout 前向传播</strong></h4><p>在训练时，Dropout 随机将部分神经元的输出置为 0，以减少过拟合：</p><ol><li>生成掩码（mask），通常是一个与输入相同形状的矩阵，元素是 0 或 1：<script type="math/tex; mode=display">\text{mask} = \text{Bernoulli}(p)</script>其中 $p$ 是保留概率。</li><li>应用掩码：<script type="math/tex; mode=display">y = x \cdot \text{mask}</script></li></ol><p>在测试时，通常不使用 Dropout，但会将输出按比例缩放 $y = x \cdot p$。</p><hr><h4 id="Dropout-反向传播"><a href="#Dropout-反向传播" class="headerlink" title="Dropout 反向传播"></a><strong>Dropout 反向传播</strong></h4><p>Dropout 的反向传播非常简单，因为它的梯度只需考虑 mask 的影响：</p><script type="math/tex; mode=display">\frac{\partial L}{\partial x_i} = \frac{\partial L}{\partial y_i} \cdot \text{mask}_i</script><h2 id="chain-rule-多元複合函數求導法則"><a href="#chain-rule-多元複合函數求導法則" class="headerlink" title="chain rule 多元複合函數求導法則"></a><strong>chain rule 多元複合函數求導法則</strong></h2><p>我们需要计算 $ z = f(x, y) $ 对 $ t $ 的偏导数，其中 $ f(x, y) = \frac{x - y}{y} $，并且 $ x = g(t) = t^2 $，$ y = h(t) = 2t $。</p><p>z = f(x, y)其實就=t/2 - 1，導數是1/2，這個例子對理解BN的梯度computational graph非常有幫助。</p><p>因為上面的 $Y = \gamma \cdot \frac{x_i - \mu}{\sigma} + \beta$ 就可以改寫成$L = f(\Phi, \Psi, \Omega), \Phi = a(x) = x, \Psi = b(x) = mean, \Omega = c(x) = std, f(\Phi, \Psi, \Omega) = \frac{\Phi - \Psi}\Omega$。</p><h3 id="步骤-1：应用链式法则"><a href="#步骤-1：应用链式法则" class="headerlink" title="步骤 1：应用链式法则"></a>步骤 1：应用链式法则</h3><p>根据链式法则：</p><script type="math/tex; mode=display">\frac{dz}{dt} = \frac{\partial z}{\partial x} \cdot \frac{dx}{dt} + \frac{\partial z}{\partial y} \cdot \frac{dy}{dt}</script><p>首先，我们需要计算各个部分的偏导数和导数。</p><hr><h3 id="步骤-2：计算-f-x-y-frac-x-y-y-对-x-和-y-的偏导数"><a href="#步骤-2：计算-f-x-y-frac-x-y-y-对-x-和-y-的偏导数" class="headerlink" title="步骤 2：计算 $ f(x, y) = \frac{x - y}{y} $ 对 $ x $ 和 $ y $ 的偏导数"></a>步骤 2：计算 $ f(x, y) = \frac{x - y}{y} $ 对 $ x $ 和 $ y $ 的偏导数</h3><h4 id="对-x-的偏导数："><a href="#对-x-的偏导数：" class="headerlink" title="对 $ x $ 的偏导数："></a>对 $ x $ 的偏导数：</h4><script type="math/tex; mode=display">f(x, y) = \frac{x - y}{y}</script><p>对 $ x $ 求偏导数，视 $ y $ 为常数：</p><script type="math/tex; mode=display">\frac{\partial f}{\partial x} = \frac{1}{y}</script><h4 id="对-y-的偏导数："><a href="#对-y-的偏导数：" class="headerlink" title="对 $ y $ 的偏导数："></a>对 $ y $ 的偏导数：</h4><script type="math/tex; mode=display">\frac{\partial f}{\partial y} = \frac{\partial}{\partial y} \left( \frac{x - y}{y} \right)</script><p>可以用商法则（商的求导法则）来求导：</p><script type="math/tex; mode=display">\frac{\partial f}{\partial y} = \frac{y \cdot (-1) - (x - y) \cdot 1}{y^2} = \frac{-y - (x - y)}{y^2} = \frac{-x}{y^2}</script><p>所以我们得到了：</p><script type="math/tex; mode=display">\frac{\partial f}{\partial x} = \frac{1}{y}, \quad \frac{\partial f}{\partial y} = \frac{-x}{y^2}</script><hr><h3 id="步骤-3：计算-x-g-t-t-2-和-y-h-t-2t-对-t-的导数"><a href="#步骤-3：计算-x-g-t-t-2-和-y-h-t-2t-对-t-的导数" class="headerlink" title="步骤 3：计算 $ x = g(t) = t^2 $ 和 $ y = h(t) = 2t $ 对 $ t $ 的导数"></a>步骤 3：计算 $ x = g(t) = t^2 $ 和 $ y = h(t) = 2t $ 对 $ t $ 的导数</h3><ol><li><p><strong>$ x = t^2 $ 对 $ t $ 的导数</strong>：</p><script type="math/tex; mode=display">\frac{dx}{dt} = 2t</script></li><li><p><strong>$ y = 2t $ 对 $ t $ 的导数</strong>：</p><script type="math/tex; mode=display">\frac{dy}{dt} = 2</script></li></ol><hr><h3 id="步骤-4：将各部分代入链式法则"><a href="#步骤-4：将各部分代入链式法则" class="headerlink" title="步骤 4：将各部分代入链式法则"></a>步骤 4：将各部分代入链式法则</h3><p>将以上结果代入链式法则公式中：</p><script type="math/tex; mode=display">\frac{dz}{dt} = \frac{\partial f}{\partial x} \cdot \frac{dx}{dt} + \frac{\partial f}{\partial y} \cdot \frac{dy}{dt}</script><p>代入具体的表达式：</p><script type="math/tex; mode=display">\frac{dz}{dt} = \left( \frac{1}{y} \right) \cdot (2t) + \left( \frac{-x}{y^2} \right) \cdot 2</script><p>将 $ x = t^2 $ 和 $ y = 2t $ 代入：</p><script type="math/tex; mode=display">\frac{dz}{dt} = \frac{1}{2t} \cdot (2t) + \frac{-t^2}{(2t)^2} \cdot 2</script><p>简化：</p><script type="math/tex; mode=display">\frac{dz}{dt} = 1 + \frac{-t^2}{4t^2} \cdot 2</script><script type="math/tex; mode=display">\frac{dz}{dt} = 1 - \frac{2}{4} = 1 - \frac{1}{2}</script><script type="math/tex; mode=display">\frac{dz}{dt} = \frac{1}{2}</script><hr><h3 id="最终答案："><a href="#最终答案：" class="headerlink" title="最终答案："></a>最终答案：</h3><script type="math/tex; mode=display">\frac{dz}{dt} = \frac{1}{2}</script><h3 id="总结：-1"><a href="#总结：-1" class="headerlink" title="总结："></a>总结：</h3><p>通过链式法则，我们计算得到了 $ z $ 对 $ t $ 的偏导数为 $ \frac{1}{2} $。</p><h2 id="一個變量的導數和他本身維度通常一致"><a href="#一個變量的導數和他本身維度通常一致" class="headerlink" title="一個變量的導數和他本身維度通常一致"></a><strong>一個變量的導數和他本身維度通常一致</strong></h2><h3 id="1-标量对矩阵的导数"><a href="#1-标量对矩阵的导数" class="headerlink" title="1. 标量对矩阵的导数"></a><strong>1. 标量对矩阵的导数</strong></h3><p>如果标量 $f$ 是矩阵 $\mathbf{X}$ 的函数，那么 $\frac{\partial f}{\partial \mathbf{X}}$ 的结果是一个与 $\mathbf{X}$ 维度相同的矩阵。例如：</p><script type="math/tex; mode=display">f = \mathbf{a}^T \mathbf{X} \mathbf{b},</script><p>其中 $\mathbf{X}$ 是 $n \times m$ 的矩阵，$\mathbf{a}$ 是 $n$-维向量，$\mathbf{b}$ 是 $m$-维向量。那么：</p><script type="math/tex; mode=display">\frac{\partial f}{\partial \mathbf{X}} = \mathbf{a} \mathbf{b}^T,</script><p>它与 $\mathbf{X}$ 的维度 $n \times m$ 相同。</p><hr><h3 id="2-矩阵对标量的导数"><a href="#2-矩阵对标量的导数" class="headerlink" title="2. 矩阵对标量的导数"></a><strong>2. 矩阵对标量的导数</strong></h3><p>如果矩阵 $\mathbf{X}$ 是标量 $f$ 的导函数（例如梯度），我们也可以得到一个与 $\mathbf{X}$ 维度相同的矩阵：</p><script type="math/tex; mode=display">\frac{\partial \mathbf{X}}{\partial f} \to \text{(同维矩阵)}</script><hr><h3 id="3-矩阵对矩阵的导数"><a href="#3-矩阵对矩阵的导数" class="headerlink" title="3. 矩阵对矩阵的导数"></a><strong>3. 矩阵对矩阵的导数</strong></h3><p>如果需要计算一个矩阵对另一个矩阵的导数，例如 $\frac{\partial \mathbf{A}}{\partial \mathbf{B}}$，那么结果通常是一个四维张量。这是因为：</p><script type="math/tex; mode=display">\frac{\partial \mathbf{A}}{\partial \mathbf{B}} = \text{一个描述 \(\mathbf{A}_{ij}\) 对 \(\mathbf{B}_{kl}\) 的偏导数的结构}.</script><p>如果 $\mathbf{A}$ 和 $\mathbf{B}$ 的维度分别是 $n \times m$ 和 $p \times q$，则结果是一个 $n \times m \times p \times q$ 的张量。</p><p>例如，对于 $\mathbf{A} = \mathbf{B}^2$，结果张量会考虑所有元素的依赖关系。</p><hr><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h3><ul><li><strong>标量对矩阵的导数</strong>，结果与矩阵维度相同。</li><li><strong>矩阵对标量的导数</strong>，结果也与矩阵维度相同。</li><li><strong>矩阵对矩阵的导数</strong>，结果通常是一个四维张量，与原矩阵的维度不同。</li></ul><h1 id="Notes"><a href="#Notes" class="headerlink" title="Notes"></a><strong>Notes</strong></h1><p><a href="https://cs231n.stanford.edu/slides/2017/">https://cs231n.stanford.edu/slides/2017/</a></p><p><a href="https://cs231n.stanford.edu/slides/2024/">https://cs231n.stanford.edu/slides/2024/</a> 今年的也大差不差，lecture6都是講Batch Normalization，甚至很多圖都一樣。</p><p><a href="https://cs231n.github.io">https://cs231n.github.io</a> notes在這（其實用slides裡的圖片這篇筆記的圖片會很小，但懶得挨個扒，截圖堆成超大圖片屎山了。）</p><p>2018，2019 年也都是 Justin Johnson 和 Serena Yeung，slides 完全沒變，不知為啥 Justin Johnson 從 2016 上這門課四年不說， 2019 年又去 Michigan 講了一遍；2020，2021 年是 Ranjay Krishna, Danfei Xu ，<a href="https://cs231n.stanford.edu/slides/2021/lecture_13.pdf">2021年有了 Self-Supervised Learning Lecture 13</a>，而且雖然 <a href="https://www.youtube.com/watch?v=YAgjfMR9R_M&amp;list=PL5-TkQAfAZFbzxjBHtzdVCWE0Zbhomg7r&amp;index=13">2019 年就講了 self-attention 和 transformer</a>，<a href="https://cs231n.github.io/assignments2021/assignment3/#q2-image-captioning-with-transformers-20-points">2021 年才加入了 assignment</a>；2022 年是 Jiajun Wu, Ruohan Gao；2023 年是 Yunzhu Li, Ruohan Gao，<a href="https://cs231n.stanford.edu/slides/2023/lecture_15.pdf">Google Deepmind 去做了 Guest Lecture，講了 VAE 和  2022 年我上班時火遍地球的 Diffusion Models</a>；2024 年是 Ehsan Adeli, Zane Durante，內容幾乎沒變。</p><h2 id="Lecture-1"><a href="#Lecture-1" class="headerlink" title="Lecture 1"></a><strong>Lecture 1</strong></h2><p>From PASCAL Visual Object Challenge(20 object categories) to ImageNet (Deng, Dong, Socher, Li, Li, &amp; Fei-Fei 2009). 李飞飞他们花了3年， 从互联网上下billions of images，然后用WorldNet这个词典把图像组织起来(which has tens of thousands of classes)，然后用Amazon Mechanical Turk平台排序、清洗、标注图像。然后开放了Large Scale Visual Recognition Challenge，“输入是image，输出是label，用前五个labels是否正确来作为指标”作为benchmark评价进展。2012年CNN(from 1998 LeCun to recognize handwriting digits and addresses for the post office)把error rate从26降到了16，从此一发不可收。</p><blockquote><p>WordNet是一个由普林斯顿大学认识科学实验室在心理学教授乔治·A·米勒的指导下建立和维护的英语字典。开发工作从1985年开始，从此以后该项目接受了超过300万美元的资助（主要来源于对机器翻译有兴趣的政府机构）。<br>WordNet根据词条的意义将它们分组，每一个具有相同意义的字条组称为一个synset（同义词集合）。WordNet为每一个synset提供了简短，概要的定义，并记录不同synset之间的语义关系。</p></blockquote><p><a href="http://wordnetweb.princeton.edu/perl/webwn">http://wordnetweb.princeton.edu/perl/webwn</a></p><div align=center><img src="/pics/cs231n/Screenshot 2024-09-25 at 20.14.16.png" loading="lazy" alt="world net" width="50%" height="50%"></div><div align=center><img src="/pics/cs231n/Screenshot 2024-09-25 at 19.09.08.png" loading="lazy" alt="视觉处理信息的阶段，对 解构vision数据是很有启发的" width="50%" height="50%"></div><p>The primary focus of this class is Image Classification (A core task in Computer Vision ), the setup is that <strong>your algorithm looks at an image, and then picks from among some fixed set of categories to classify that image.</strong> <strong><em>This might seem like somewhat of restrictive or artificial setup, but it’s actually quite general. This problem can be applied in many different settings both in industry and academia and many different places.</em></strong> But in this course we are also gonna talk about several other visual recognition problems that build upon many of the <strong>tools that we develop for the purpose of image classification</strong>, such as, <strong>object detection</strong> and <strong>image captioning</strong>.<br>这也是我后来一直存在的疑问，当时没仔细看错过了这个关键问题。AI的最终目标比如图像分类这个任务从来不是能识别一切图像，因为视觉信息这个二维量本身就是局限的，通用AI(AGI)的目标是能做出人类所有智能行为的AI，所以超越人类水平已经足够了。<br>这个技术的价值是能应用到所有场景里，比如在饭店判断菜品的任务里，能超过人的准确率就已经可以接受了。<br>回顾在商汤做的visual grounding任务，输入“这个垃圾桶是满的”，视觉定位模型找到图像中的垃圾桶并输出“是”“否”满。其实完全能分割成目标检测+图像分类任务，只是因为对这些基础技术太不熟练导致思路模糊，只能跟着团队用OFA人云亦云亦步亦趋。随便搭一个两步的模型对从事这项工作的人来说应该是最基础的事，基础不牢地动山摇。（不过当时团队是侧重于做一个能快速适应新场景的模型，经过几十张甚至0张图片的训练就能检测新任务比如“电动车有没有超载”，但一样也该入职时迅速搭一个两步模型上手任务。）<br>(关于Justin Johnson这句话的语法，from among的among强调范围，是对的；some set的some表示不确定，set是对的，sets大概也可以)<br>另外，AI的飞速发展一直让过去的我焦虑，这种焦虑让我不愿意学习当前的sota技术，因为未来某一天就突然会被革新。可现在回头看，雖然沒有新的課堂錄像，但通過課程官網可以看出<strong>stanford的cs231n内容几乎从未改变</strong>，这足以说明这些老玩意还是新积木的基石。</p><h2 id="Lecture-2"><a href="#Lecture-2" class="headerlink" title="Lecture 2"></a><strong>Lecture 2</strong></h2><p>Numpy efficiently implements vectorized operations. It’s should be practiced a lot in assignment 1.<br>就识别猫这个问题来说，通过找到所有edges然后根据猫的特征逐一匹配（耳朵眼睛尾巴，大致间距和位置）大概也可以做到，而且这样的代码完全可解释。而现在neural nets的data driven，根本不可能讲清楚哪个部分识别了哪个特征，能把握的只有梯度变化规律。</p><p>knn也是data-driven的，只不过不从training data里获得知识，只是简单记住train set，来一个新数据就跟所有train set数据比较，跟哪个类别的某k个图<strong>最相似</strong>那就属于哪个类别。如何计算两个图（向量）的距离<a href="https://blog.v2beach.cn/2020/11/15/cs231n/#向量范数度量图片差异">(Distance Metric)四年前</a>已经总结过了，可以用L1曼哈顿也可以用L2欧式，对于新问题用什么距离试试哪个好就行了。训练O(1)，推理O(N)，正好反了。<br>只要记住KNN的full form——K Nearest Neighbors就能想起来这个dumb算法了。</p><blockquote><p>metric不是metrix，metric is a system for measuring something是指标的意思；metrix才是矩阵。</p></blockquote><p>模型在train set上训练完后，在validation set上找最优hyperparameters（不靠训练得到的都算，包括距离计算L1还是L2），最后临论文deadline，只在test set上跑一次only once，得到的数就是最终写在报告、论文里的数据，以保证没有作弊。严格分离验证集和测试集对确保做研究没有不诚实、不公平很重要。</p><div class="table-container"><table><thead><tr><th>validation set for tuning hyperparameters</th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-09-26 at 00.25.17.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-07 at 17.16.07.png" alt=""></td></tr></tbody></table></div><p>另外idea4是交叉验证，神经网络实验的验证集validation set一般不用cross validation，在不同分割的训练集上训练好几遍开销太大了，只适用于较小的数据集。</p><div align=center><img src="/pics/cs231n/Screenshot 2024-09-27 at 17.11.10.png" loading="lazy" alt="linear classification" width="50%" height="50%"></div><p>linear classification是最簡單的parametric model（即上面的f(x,W)也有時weights/paramters會寫成theta θ而不是W），用Xtrain训练出W参数，inference过程就可以抛弃训练数据只用x和W计算结果。<br>最简单的x和W组合方式就是二者相乘。<br>其中對bias的簡單理解是，数据集里的猫比狗多，那猫类别的bias值可能就比狗高。可以通过增加一个常数项1特征，把b算作W的一部分。<br>模型做的通常是在给定幾個类别里打分的工作，而不是凭空产生一个相关类别，人脑大概也是这么工作的——看到一张猫的图片，在语言系统的无数个词条里关联到“猫”。<br>在很多模型裡linear classification就作為最後一層，承擔把所有的計算匯總到一起給上面10個類別打分的工作。<br>可以把linear classification的Wx这个計算过程看成是template matching，這在我<a href="https://blog.v2beach.cn/2020/11/15/cs231n/#Template-matching">之前的筆記</a>也寫過一次，weights的每一行可以看作是一個被strech成一維的模板，圖片跟模板越像的話，在這一類的分數就會越高。<br>這是因為這兩個vector的點積就是點積相似度，點積越大可以認為就越相似，因為圖片的每個像素都是個xyz-axis都在0~255內的向量，所以可以把這個點積相似度放在r=255的球裡來想，或許有幫助。</p><script type="math/tex; mode=display">\mathbf{A} \cdot \mathbf{B} = \|\mathbf{A}\| \|\mathbf{B}\| \cos(\theta)</script><p><img src="/pics/cs231n/This-is-an-unit-sphere-in-3D-cartesian-coordinate-system-a-is-the-angle-between-normal-n.gif.png" alt="unit sphere in 3d coordinate"></p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-07 at 16.46.20.png" loading="lazy" alt="template matching viewpoint linear classification" width="50%" height="50%"></div><p>在neural network等其他複雜模型裡，不會只限制模型每個類別只學一個模板，可能因此才變得更準確。<br>这种可视化方式可以学来，用在神经网络里可视化一下参数。不过我之前也可視化過比如capsule，能看出參數對應圖像的一些特徵，但不像linear这么明显。</p><div class="table-container"><table><thead><tr><th>站在比數據高一點兒維度的角度看問題</th><th>單純linear decision boundaries顯然解決不了的問題</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-07 at 17.05.48.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-07 at 17.06.09.png" alt=""></td></tr></tbody></table></div><p>之後的內容可以理解成：</p><ul><li>Loss function (quantifying what it means to have a “good” W)</li><li>Optimization (start with random W and find a W that minimizes the loss)</li><li>ConvNets! (tweak the functional form of f)</li></ul><p>Problems:</p><ul><li>KNN:(never used)<ol><li>Very slow at test time.</li><li>Distance metrics on pixels are not informative.</li><li>Curse of dimensionality.</li></ol></li></ul><p><img src="/pics/cs231n/Screenshot 2024-11-11 at 20.38.36.png" alt="knn前幾名label出現次數同樣多怎麼辦？怎麼決定分到哪個類？"></p><h2 id="Lecture-3"><a href="#Lecture-3" class="headerlink" title="Lecture 3"></a><strong>Lecture 3</strong></h2><div align=center><img src="/pics/cs231n/Screenshot 2024-11-08 at 16.36.45.png" loading="lazy" alt="multiclass svm loss" width="50%" height="50%"></div><p>想一些相關問題有助於理解：</p><ol><li>選margin為1還是其他數字無所謂，只是一個arbitrary choice；</li><li>車的分數稍微改變一下不影響loss仍然為0；</li><li>就像hinge曲線一樣，loss最小值為1，最大值為infinity；</li><li>讓W為全0，即s≈0，那麼loss應該是num of classes - 1，<strong>這個可以用於debug</strong>；</li><li>讓j可以=y_i的話，結果是loss-1，其實都一樣但是讓loss最小值為0而不是“1”比較make sense；</li><li>計算loss不用sum，用mean也沒區別，因為不關心loss絕對值；</li><li>如果用上式loss的平方，可以代表我們更不能容忍大的錯誤，因為錯誤較大的類別會直接指數倍放大；</li><li>如果loss=0，這時的W是唯一的嗎？當然不是，2W的loss也是0。</li></ol><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">L_i_vectorized</span><span class="params">(x, y, W)</span> :</span></span><br><span class="line">    scores = W.dot(x)</span><br><span class="line">    margins = np.maximum(<span class="number">0</span>, scores - scores[y] + <span class="number">1</span>)</span><br><span class="line">    margins[y] = <span class="number">0</span></span><br><span class="line">    loss_i = np. sum(margins)</span><br><span class="line">    <span class="keyword">return</span> loss_i</span><br></pre></td></tr></table></figure><p>相比想矩陣，用一條數據vector的例子分析example code比較簡單。</p><div class="table-container"><table><thead><tr><th>Occam’s Razor: “Among competing hypotheses, the simplest is the best” William of Ockham, 1285 - 1347</th><th>發現regularization是“W”的函數，是讓W越簡單越好，比如讓高次冪特徵係數為0</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 15.59.12.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-10 at 16.38.01.png" alt=""></td></tr></tbody></table></div><p>之前學cs231n就是卡在batch normalization附近了，這一曠就是四年。總之還是沒理解清防止過擬合加的正則項到底是個什麼。需要找點最簡單的例子，具體分析。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 20.12.37.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 20.13.12.png" alt=""></td></tr></tbody></table></div><p>想一些相關問題有助於理解：</p><ol><li>min:-log(1)=0 max:-log(0)=infinity，但這兩個值都是取不到的，因為如果softmax函數=1，錯誤類別都需要是負無窮才能e^sj為0，如果softmax=0，正確類別分數需要是負無窮e^sy才能為0;</li><li>如果W=0，s全≈0，L_i就=-log(1/C(num of classes))=log(C)；這個也<strong>可以用來debug</strong>，跟上面SVM一樣！</li><li>對比一下再來想問題3：</li></ol><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 21.03.27.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 21.05.58.png" alt=""></td></tr></tbody></table></div><p>如果是右圖這種情況，修改一點已經分類正確的分數，svm loss仍然是0不會變，而softmax會改變，因為svm的目標是只要正確分類的score跟錯誤分類差距大過margin就可以了，而softmax會盡可能地把錯誤分數拉向-inf，把正確分數拉向inf。</p><ul><li>svm和softmax都只是為了最優化f(x)=Wx+b的損失函數而已，他們的函數形狀不重要，f(x)=Wx+B仍是線性函數</li></ul><p>關於optimizer他的解釋很有意思，解析解analytic solution像是你在山頂（高度就是loss值）magically teleport到了最低點，但當prediction function f，loss function包括regularizer都很複雜就只能尋求數值解（iterative methods that start some solution and gradually imporve by time，梯度下降，即求導按負導數方向找低點，高中數學，pretty easy actually）。</p><ol><li>Strategy #1: A first very bad idea solution: Random search</li><li><p>Strategy #2: Follow the slope</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-11 at 21.21.44.png" loading="lazy" alt="gradient偏微分" width="50%" height="50%"></div><ol><li><p>一種計算gradient的想法是——，但是這方法太蠢太慢了，Numerical gradient: approximate :(, slow :(, easy to write :)</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-11 at 21.35.05.png" loading="lazy" alt="" width="50%" height="50%"></div></li><li><p>Analytic gradient: exact :), fast :), error-prone :(，直接用微積分得到dW迭代loss</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-11 at 21.37.58.png" loading="lazy" alt="" width="50%" height="50%"></div></li></ol></li></ol><p>In practice: Always use analytic gradient, but check implementation with numerical gradient. This is called a gradient check.（finding derivative is error-prone求導是容易出錯的）</p><hr><h3 id="chatgpt給的一個計算梯度的例子"><a href="#chatgpt給的一個計算梯度的例子" class="headerlink" title="chatgpt給的一個計算梯度的例子"></a><strong>chatgpt給的一個計算梯度的例子</strong></h3><h3 id="示例函数"><a href="#示例函数" class="headerlink" title="示例函数"></a><strong>示例函数</strong></h3><p>假设我们有一个二维函数：</p><script type="math/tex; mode=display">f(x, y) = x^2 + y^2</script><p>这个函数是一个标准的二次函数，描述的是一个圆形等高线的抛物面。我们的目标是计算其梯度，并理解梯度的含义。</p><h3 id="1-计算梯度"><a href="#1-计算梯度" class="headerlink" title="1. 计算梯度"></a><strong>1. 计算梯度</strong></h3><p>首先，梯度是指函数在不同方向上变化的速率，它由偏导数组成。在二维情况下，梯度可以写成一个向量：</p><script type="math/tex; mode=display">\nabla f(x, y) = \left( \frac{\partial f}{\partial x}, \frac{\partial f}{\partial y} \right)</script><h4 id="求偏导数："><a href="#求偏导数：" class="headerlink" title="求偏导数："></a><strong>求偏导数：</strong></h4><p>计算 $f(x, y) = x^2 + y^2$ 对 $x$ 和 $y$ 的偏导数：</p><ol><li><p>对 $x$ 的偏导数：</p><script type="math/tex; mode=display">\frac{\partial f}{\partial x} = 2x</script></li><li><p>对 $y$ 的偏导数：</p><script type="math/tex; mode=display">\frac{\partial f}{\partial y} = 2y</script></li></ol><p>因此，梯度 $\nabla f(x, y)$ 为：</p><script type="math/tex; mode=display">\nabla f(x, y) = (2x, 2y)</script><h3 id="2-梯度的意义"><a href="#2-梯度的意义" class="headerlink" title="2. 梯度的意义"></a><strong>2. 梯度的意义</strong></h3><p>在几何上，梯度是函数在该点变化最快的方向。具体来说，梯度的方向就是函数值增长最快的方向，而梯度的负方向（即 $-\nabla f$）则是函数值下降最快的方向。</p><h4 id="示例：在点-1-1-处的梯度"><a href="#示例：在点-1-1-处的梯度" class="headerlink" title="示例：在点 $(1, 1)$ 处的梯度"></a><strong>示例：在点 $(1, 1)$ 处的梯度</strong></h4><p>假设我们在点 $(1, 1)$ 计算梯度：</p><script type="math/tex; mode=display">\nabla f(1, 1) = (2 \cdot 1, 2 \cdot 1) = (2, 2)</script><p>这表明，在点 $(1, 1)$ 处，函数 $f(x, y)$ 增长最快的方向是向量 $(2, 2)$ 的方向。这个方向对应于45度方向，即沿着 $x$ 轴和 $y$ 轴方向各增加1的方向。</p><h3 id="3-如何找到梯度最大的方向"><a href="#3-如何找到梯度最大的方向" class="headerlink" title="3. 如何找到梯度最大的方向"></a><strong>3. 如何找到梯度最大的方向</strong></h3><p>梯度本身就表示函数变化最快的方向。它的方向指向函数值增长最快的方向，而它的大小（即梯度的模）则表示变化的速率。</p><p>在我们的例子中，点 $(1, 1)$ 的梯度是 $(2, 2)$，它的模为：</p><script type="math/tex; mode=display">\|\nabla f(1, 1)\| = \sqrt{2^2 + 2^2} = \sqrt{8} = 2\sqrt{2}</script><p><strong>总结：</strong></p><ol><li>在任意点 $(x, y)$ 处，函数 $f(x, y) = x^2 + y^2$ 增长最快的方向是沿着梯度方向 $(2x, 2y)$。</li><li>梯度的大小表示函数在该方向上增长的速率。</li><li>如果我们想沿着函数值降低的方向移动，那么应当沿着梯度的负方向移动。<hr></li></ol><p>所以gradient descent的代碼也很簡單：<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># **Vanilla Gradient Descent**</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    weights_grad = evaluate_gradient(loss_fun, data, weights)</span><br><span class="line">    weights += - step_size * weights_grad <span class="comment"># perform parameter update</span></span><br></pre></td></tr></table></figure></p><p>其中loss_fun就可以想像成上面的f(x,y,z)=x^2+y^2+z^2，data就是x,y，weights就是z。</p><p>learning rate(step size)是最先要確定的超參數，model size和regularization strength都是其次的。</p><p>fancier update rules than gradient descent:<br>momentum, adam，就是不同的利用梯度信息的方式。</p><p>比如稍微改進vanilla為stochastic（SGD隨機梯度下降是隨機選一個，這裡是變種Mini-batch Gradient Descent）：</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-11 at 22.15.07.png" loading="lazy" alt="N可以是幾百萬，更新一次W算下個loss要等半輩子，主要是不一定一次到位，可能0.5N就到位了，或者要1.5個N才到位，所以用minibatch" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>features</th><th>color histogram</th><th>edge histogram</th><th>like codebook(OFA啥的也有這個概念來著？)</th><th>現在跟以前先提取特徵的兩步法也沒什麼區別，包括cnn，包括transformer，只是變成隱性地提取這些特徵，做成end to end端到端的黑箱</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 22.21.17.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 22.21.33.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 22.21.47.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 22.22.35.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-11 at 22.30.39.png" alt=""></td></tr></tbody></table></div><h2 id="Lecture-4-計算圖-local-gradient-and-chain-rule"><a href="#Lecture-4-計算圖-local-gradient-and-chain-rule" class="headerlink" title="Lecture 4 計算圖(local gradient and chain rule)"></a><strong>Lecture 4 計算圖(local gradient and chain rule)</strong></h2><div class="table-container"><table><thead><tr><th>computational graph chain rule</th><th>每個node只需要關注它周圍的nodes</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 00.59.26.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 01.10.23.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 01.20.00.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 01.24.07.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>把幾個基本二元運算合成一個計算模塊，比如sigmoid，和它的導數</th><th>這裡算導數用的不是x，是f(x)，由此可知backprop時node的forward pass的前後值都可能用到</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 01.26.35.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 01.52.04.png" alt=""></td></tr></tbody></table></div><p>任何時候比如做assignments時，如果求梯度遇到困難，畫一個計算圖用chain rule就都可以迎刃而解。</p><div class="table-container"><table><thead><tr><th>patterns</th><th>pattern</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 12.12.42.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 12.28.22.png" alt=""></td></tr></tbody></table></div><p>$\frac{df}{dx} = \sum_i \frac{df}{dq_i} \cdot \frac{dq_i}{dx}.$</p><p>懶得畫圖了，如果x給多個q當了自變量，多個q又給同一個f當了自變量，那就得把導數加起來，隨便想個computational graph的例子就不抽象了。</p><p><hr><br>Jacobian矩陣很簡單，就是每個輸出對應每個輸入的偏導——</p><h3 id="Jacobian-矩阵计算示例"><a href="#Jacobian-矩阵计算示例" class="headerlink" title="Jacobian 矩阵计算示例"></a><strong>Jacobian 矩阵计算示例</strong></h3><p>Jacobian 矩阵在多元函数中用于描述一个向量值函数的偏导数。假设我们有一个向量值函数 $ \mathbf{F}(\mathbf{x}) $，其中 $ \mathbf{F} : \mathbb{R}^n \to \mathbb{R}^m $，输入向量 $ \mathbf{x} \in \mathbb{R}^n $，输出向量 $ \mathbf{F}(\mathbf{x}) \in \mathbb{R}^m $。</p><p>对于每一个 $ x_i $ 和 $ F_j $，Jacobian 矩阵的元素是 $ \frac{\partial F_j}{\partial x_i} $。整个矩阵的形式是：</p><script type="math/tex; mode=display">J = \begin{bmatrix}\frac{\partial F_1}{\partial x_1} & \frac{\partial F_1}{\partial x_2} & \cdots & \frac{\partial F_1}{\partial x_n} \\\frac{\partial F_2}{\partial x_1} & \frac{\partial F_2}{\partial x_2} & \cdots & \frac{\partial F_2}{\partial x_n} \\\vdots & \vdots & \ddots & \vdots \\\frac{\partial F_m}{\partial x_1} & \frac{\partial F_m}{\partial x_2} & \cdots & \frac{\partial F_m}{\partial x_n} \\\end{bmatrix}</script><h3 id="示例-1"><a href="#示例-1" class="headerlink" title="示例"></a><strong>示例</strong></h3><p>假设我们有函数（這裡的x和y當然可以改成x1和x2）：</p><script type="math/tex; mode=display">F_1(x, y) = x^2 + y^2</script><script type="math/tex; mode=display">F_2(x, y) = e^x + \sin(y)</script><p>我们希望计算 $ F(x, y) = \begin{bmatrix} F_1(x, y) \\ F_2(x, y) \end{bmatrix} $ 的 Jacobian 矩阵。</p><h4 id="第一步：计算偏导数"><a href="#第一步：计算偏导数" class="headerlink" title="第一步：计算偏导数"></a><strong>第一步：计算偏导数</strong></h4><ol><li><p>对 $ F_1(x, y) = x^2 + y^2 $：</p><ul><li>$ \frac{\partial F_1}{\partial x} = 2x $</li><li>$ \frac{\partial F_1}{\partial y} = 2y $</li></ul></li><li><p>对 $ F_2(x, y) = e^x + \sin(y) $：</p><ul><li>$ \frac{\partial F_2}{\partial x} = e^x $</li><li>$ \frac{\partial F_2}{\partial y} = \cos(y) $</li></ul></li></ol><h4 id="第二步：构造-Jacobian-矩阵"><a href="#第二步：构造-Jacobian-矩阵" class="headerlink" title="第二步：构造 Jacobian 矩阵"></a><strong>第二步：构造 Jacobian 矩阵</strong></h4><p>将上述结果代入矩阵中：</p><script type="math/tex; mode=display">J = \begin{bmatrix}\frac{\partial F_1}{\partial x} & \frac{\partial F_1}{\partial y} \\\frac{\partial F_2}{\partial x} & \frac{\partial F_2}{\partial y} \\\end{bmatrix} = \begin{bmatrix}2x & 2y \\e^x & \cos(y) \\\end{bmatrix}</script><h4 id="第三步：在特定点的-Jacobian-矩阵"><a href="#第三步：在特定点的-Jacobian-矩阵" class="headerlink" title="第三步：在特定点的 Jacobian 矩阵"></a><strong>第三步：在特定点的 Jacobian 矩阵</strong></h4><p>如果我们希望在特定点，比如 $ (x, y) = (1, 0) $ 处求得 Jacobian 矩阵，则代入此点的值：</p><script type="math/tex; mode=display">J(1, 0) = \begin{bmatrix}2 \cdot 1 & 2 \cdot 0 \\e^1 & \cos(0) \\\end{bmatrix} = \begin{bmatrix}2 & 0 \\e & 1 \\\end{bmatrix}</script><h3 id="结果"><a href="#结果" class="headerlink" title="结果"></a><strong>结果</strong></h3><p>因此，函数 $ F(x, y) $ 在点 $ (1, 0) $ 处的 Jacobian 矩阵为：</p><script type="math/tex; mode=display">J(1, 0) = \begin{bmatrix} 2 & 0 \\ e & 1 \end{bmatrix}</script><p>这个矩阵描述了函数 $ F(x, y) $ 在点 $ (1, 0) $ 处的变化率和方向。</p><hr><div class="table-container"><table><thead><tr><th>because each element of your gradient is quntifying how much element is contributing/affecting your final output</th><th></th><th>下面偽代碼的forward和backward</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 15.55.05.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 16.01.56.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 16.09.31.png" alt=""></td></tr></tbody></table></div><p>關於<strong>這個1（indicator function）</strong>：<br>这个符号 $1_{k=i}$ 表示一个<strong>指示函数（或称为指示符号）</strong>，其作用是判断条件 $k = i$ 是否成立：</p><ul><li>当 $k = i$ 时，$1_{k=i} = 1$</li><li>当 $k \neq i$ 时，$1_{k=i} = 0$</li></ul><p>因此，如果写成 $1_{k=i} x_j$，则表示仅当 $k = i$ 时，值为 $x_j$；如果 $k \neq i$，则值为 0。</p><p>所以就可以簡單寫一下計算圖的rough pseudo code：<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ComputationalGraph</span><span class="params">(object)</span>:</span></span><br><span class="line">    <span class="comment">#...</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">forward</span><span class="params">(inputs)</span> :</span></span><br><span class="line">        <span class="comment"># 1. [pass inputs to input gates...]</span></span><br><span class="line">        <span class="comment"># 2. forward the computational graph:</span></span><br><span class="line">        <span class="keyword">for</span> gate <span class="keyword">in</span> self.graph.nodes_topologically_sorted():</span><br><span class="line">            gate.forward()</span><br><span class="line">        <span class="keyword">return</span> loss <span class="comment"># the final gate in the graph outputs the loss</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">backward</span><span class="params">()</span>:</span></span><br><span class="line">        <span class="keyword">for</span> gate <span class="keyword">in</span> reversed(self.graph.nodes_topologically_sorted()):</span><br><span class="line">            gate.backward() <span class="comment"># little piece of backprop (chain rule applied)</span></span><br><span class="line">        <span class="keyword">return</span> inputs_gradients</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MultiplyGate</span><span class="params">(object)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">forward</span> <span class="params">(x,y)</span> :</span></span><br><span class="line">        z = x*y</span><br><span class="line">        self.x = x <span class="comment"># must keep these around!</span></span><br><span class="line">        self.y = y</span><br><span class="line">        <span class="keyword">return</span> z</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">backward</span><span class="params">(dz)</span>:</span></span><br><span class="line">        dx = self.y * dz <span class="comment"># [dz/dx * dL/dz]</span></span><br><span class="line">        dy = self.x * dz <span class="comment"># [dz/dy * dL/dz]</span></span><br><span class="line">        <span class="keyword">return</span> [dx, dy]</span><br></pre></td></tr></table></figure></p><p><a href="https://github.com/BVLC/caffe/tree/master/src/caffe/layers">caffe layers的代碼</a>在這節課之後就沒變過。比如下圖這個<a href="https://github.com/BVLC/caffe/blob/master/src/caffe/layers/sigmoid_layer.cpp">sigmoid_layer</a>。</p><div class="table-container"><table><thead><tr><th>topdiff是上游梯度</th><th>寫之前先畫computational graph</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 16.15.42.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 16.16.33.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>if you just stack linear layers on top of each other, they’re just gonna collapse to a single linear function (w/o max here)</th><th>這裡其實我一直有個疑問，如果X裡有x1 x1x2 x1x2x3這種高階特徵，那豈不是不再線性了？這個問題其實有點蠢，因為線性關係是指x1和f(x1)、x1x2和f(x1x2)、x1x2x3和f(x1x2x3)之間是線性/直線，而不是x1x2/x1x2x3跟f(x1)</th><th>用之前template matching的觀點可以認為W1學到了更多templates（維度變大了），而W2是模板的weights，h是tempaltes得分，h之前會做non-linear（這裡是max），最後得分就是templates的weighted sum</th><th>3層3-layer就是再套一次f = Wз max(0, W2 max(0, W1x))，代碼在下面</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 16.19.54.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 18.36.31.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 20.37.09.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 20.48.21.png" alt=""></td></tr></tbody></table></div><p>上面這個3-layer的代碼就像這樣：<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># **forward-pass of a 3-layer neural network:**</span></span><br><span class="line">f = <span class="keyword">lambda</span> x: <span class="number">1.0</span>/(<span class="number">1.0</span> + np.exp(-x)) <span class="comment"># activation function (use sigmoid)</span></span><br><span class="line">x = np. random. randn(<span class="number">3</span>, <span class="number">1</span>) <span class="comment"># random input vector of three numbers (3x1)</span></span><br><span class="line">h1 = f(np.dot(Wl, x) + bl) <span class="comment"># calculate first hidden layer activations (4x1)</span></span><br><span class="line">h2 = f(np.dot(W2, h1) + b2) <span class="comment"># calculate second hidden layer activations (4x1)</span></span><br><span class="line">out = np.dot(W3, h2) + b3 <span class="comment"># output neuron (1x1)</span></span><br></pre></td></tr></table></figure></p><p>關於polynomial regression，</p><blockquote><p>多项式回归模型本身是非线性的，因为它涉及到自变量的高次方，能够拟合非线性数据关系。然而，多项式回归模型仍然可以被视为线性模型的一种，原因在于模型参数的估计和模型预测都可以通过线性回归的方法实现。</p><p>具体来说，在多项式回归模型中，自变量的高次方可以视为新的特征，将其添加到原始特征中，从而将非线性问题转化为线性问题。然后，使用线性回归模型估计模型参数（即新特征的系数），并使用线性回归模型进行预测。</p><p>因此，多项式回归模型被称为线性模型的扩展，它可以用于拟合非线性数据关系，并且可以使用线性回归的方法进行参数估计和预测。</p></blockquote><div class="table-container"><table><thead><tr><th>Serena說relu是最接近實際神經的activation function</th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 20.45.59.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 20.48.08.png" alt=""></td></tr></tbody></table></div><h2 id="Lecture-5"><a href="#Lecture-5" class="headerlink" title="Lecture 5"></a><strong>Lecture 5</strong></h2><div class="table-container"><table><thead><tr><th>1986 backprop</th><th>1998 first example backprop/gradient based cnn</th><th>2012 Alex Krizhevsky</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 21.56.02.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 22.02.40.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 22.04.44.png" alt=""></td></tr></tbody></table></div><h3 id="CNN的卷積"><a href="#CNN的卷積" class="headerlink" title="CNN的卷積"></a><strong>CNN的卷積</strong></h3><div align=center><img src="/pics/cs231n/Screenshot 2024-11-12 at 22.43.37.png" loading="lazy" alt="實際寫代碼的時候是strech out這個filter和image(或activation map)，因為dot product是一樣的結果" width="50%" height="50%"></div><p>each filter is looking for a specific type of template or concept of the input volume.</p><p>有個問題問得很好，what’s the intuition for increasing the depth each time. 每一層讓filter更多activation map更深（3、6、…）是實踐裡大家發現的更好的設定而已，cnn有很多design choice，比如filter size啥的。</p><div align=center><img src="/pics/cs231n/2D_Convolution_Animation.gif" loading="lazy" alt="filter也可以說是receptive field" width="50%" height="50%"></div><hr><h3 id="信號處理的卷積計算signal-processing-convolution"><a href="#信號處理的卷積計算signal-processing-convolution" class="headerlink" title="信號處理的卷積計算signal processing convolution"></a><strong>信號處理的卷積計算signal processing convolution</strong></h3><p>假设两个简单的离散信号 $f(t)$ 和 $g(t)$，它们的取值如下：</p><script type="math/tex; mode=display">f(t) = \{1, 2, 3\} \quad \text{for} \quad t = 0, 1, 2</script><script type="math/tex; mode=display">g(t) = \{4, 5\} \quad \text{for} \quad t = 0, 1</script><h4 id="步骤-1：反转信号-g-t"><a href="#步骤-1：反转信号-g-t" class="headerlink" title="步骤 1：反转信号 $g(t)$"></a><strong>步骤 1：反转信号 $g(t)$</strong></h4><p>首先将 $g(t)$ 反转，得到 $g(-t)$：</p><script type="math/tex; mode=display">g(-t) = \{5, 4\}</script><p>然後就可以想像是這個反轉的 g 在 f 上滑動了。</p><h4 id="步骤-2：滑动并计算乘积"><a href="#步骤-2：滑动并计算乘积" class="headerlink" title="步骤 2：滑动并计算乘积"></a><strong>步骤 2：滑动并计算乘积</strong></h4><script type="math/tex; mode=display">y(t) = (f * g)(t) = \sum_{\tau} f(\tau) \cdot g(t - \tau)</script><h4 id="当-t-0-时："><a href="#当-t-0-时：" class="headerlink" title="当 t = 0 时："></a><strong>当 t = 0 时：</strong></h4><script type="math/tex; mode=display">y(0) = f(0) \cdot g(0) = 1 \cdot 4 = 4</script><h4 id="当-t-1-时："><a href="#当-t-1-时：" class="headerlink" title="当 t = 1 时："></a><strong>当 t = 1 时：</strong></h4><script type="math/tex; mode=display">y(1) = f(0) \cdot g(1) + f(1) \cdot g(0) = 1 \cdot 5 + 2 \cdot 4 = 5 + 8 = 13</script><h4 id="当-t-2-时："><a href="#当-t-2-时：" class="headerlink" title="当 t = 2 时："></a><strong>当 t = 2 时：</strong></h4><script type="math/tex; mode=display">y(2) = f(0) \cdot g(2) + f(1) \cdot g(1) + f(2) \cdot g(0)</script><p>由于 $g(t)$ 在 $t = 2$ 时没有定义，所以 $g(2) = 0$，于是：</p><script type="math/tex; mode=display">y(2) = 1 \cdot 0 + 2 \cdot 5 + 3 \cdot 4 = 0 + 10 + 12 = 22</script><h4 id="当-t-3-时："><a href="#当-t-3-时：" class="headerlink" title="当 t = 3 时："></a><strong>当 t = 3 时：</strong></h4><script type="math/tex; mode=display">y(3) = f(1) \cdot g(2) + f(2) \cdot g(1)</script><p>同样，$g(2) = 0$，因此：</p><script type="math/tex; mode=display">y(3) = 2 \cdot 0 + 3 \cdot 5 = 0 + 15 = 15</script><h4 id="当-t-4-时："><a href="#当-t-4-时：" class="headerlink" title="当 t = 4 时："></a><strong>当 t = 4 时：</strong></h4><script type="math/tex; mode=display">y(4) = (f * g)(4) = f(2) \cdot g(2) = 3 \cdot 0 = 0</script><h4 id="最终卷积结果"><a href="#最终卷积结果" class="headerlink" title="最终卷积结果"></a><strong>最终卷积结果</strong></h4><p>因此，卷积的结果 $(f * g)(t)$ 为：</p><script type="math/tex; mode=display">y(t) = (f * g)(t) = \{ 4, 13, 22, 15, 0 \}</script><p>其中卷积结果的最大时间点是由 $f(t)$ 和 $g(t)$ 的最远时间点组合而成。具体来说，卷积的结果最大会到达：</p><script type="math/tex; mode=display">t_{\text{max}} = (\text{last time of } f(t)) + (\text{last time of } g(t))</script><p>例如，假设 $f(t)$ 的最后一个有效时间点是 $t = 2$，而 $g(t)$ 的最后一个有效时间点是 $t = 1$，则：</p><script type="math/tex; mode=display">t_{\text{max}} = 2 + 1 = 3</script><p>这样，卷积的最大时间点 $t_{\text{max}}$ 为 3。</p><ul><li>跟cnn的卷積一樣的是：都要想像成滑動操作，信號卷積是二維的兩條線滑動；</li><li>不一樣的是：cnn不需要反轉，而(f *g)(t)裡後面這個函數要先在時間軸上反轉。</li></ul><p><img src="/pics/cs231n/Comparison_convolution_correlation.svg.png" alt="卷積、互相關和自相關的圖示比較。假定f高度是1.0，在5個不同點上的值，用在每個點下面的陰影面積來指示。"></p><h3 id="deconvolution反卷積"><a href="#deconvolution反卷積" class="headerlink" title="deconvolution反卷積"></a><strong>deconvolution反卷積</strong></h3><p>上面的 $f(t) = [1, 2, 3]$ 是<strong>原始信號</strong>（可能是圖像像素值、音頻信號幅度），$g(t) = [4, 5]$ 叫做<strong>模糊核</strong>（比如是一個濾波器、噪聲源），得到的線性無填充的<strong>卷積結果</strong> $y(t) = [4, 13, 22, 15, 0]$ 。</p><p>反卷積就是通過模糊核 $g(t)$ 和卷積結果 $y(t)$ 恢復原始信號 $f(t)$ 的過程，可以用傅里叶变换或卷积定理，在实际应用中，反卷积操作通常需要一些数值优化算法来求解，因为恢复信号可能涉及更复杂的卷积核和信号。</p><ul><li>卷积：信号与某个模糊核进行卷积，导致信号被模糊处理。</li><li>反卷积：给定模糊后的信号和模糊核，通过数学方法恢复出原始信号。</li></ul><p>用<a href="https://www.ee-diary.com/2023/05/online-convolution-calculator.html">這個網站</a>可以在線算信號卷積。</p><hr><p>這節課原來後面講了如何可視化模型學到的特徵。(basically each element of these grids is showing what in the input would look like that basically maximizes the activation of the neuron. So in a sense, what is the neuron looking for?)</p><hr><div class="table-container"><table><thead><tr><th>不這樣設置stride</th><th>formula，但其實豎直方向上也可以有stride，ouput size又是另外的formula</th><th>padding(zero,mirror,and so on)能保持activation size，input和ouput一樣size，但是stride&gt;1的作用呢？是避免边缘信息丢失</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 23.25.05.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 23.26.36.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 23.34.32.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>example2</th><th>1x1 conv filter是可行的</th><th>torch的一個conv</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 23.40.34.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 23.46.44.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-12 at 23.54.05.png" alt=""></td></tr></tbody></table></div><p>stride越大downsampling就越多，參數越少模型越小，包括所有參數一直到FC連接的值也會變少。（tradeoff）</p><div class="table-container"><table><thead><tr><th>pooling，不影響depth</th><th>stride設大就是downsampling，跟pooling效果是一樣的</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 00.09.07.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 00.07.26.png" alt=""></td></tr></tbody></table></div><h3 id="右圖為什麼downsample太快不好？那為什麼還要pooling？現在不缺算力追求越多參數越好是不是不再需要太多downsample了？"><a href="#右圖為什麼downsample太快不好？那為什麼還要pooling？現在不缺算力追求越多參數越好是不是不再需要太多downsample了？" class="headerlink" title="右圖為什麼downsample太快不好？那為什麼還要pooling？現在不缺算力追求越多參數越好是不是不再需要太多downsample了？"></a><strong>右圖為什麼downsample太快不好？那為什麼還要pooling？現在不缺算力追求越多參數越好是不是不再需要太多downsample了？</strong></h3><p>這個問題可以再問chatgpt，回答得挺好，1.前幾層下採樣太快會丟失很多信息；2.pooling減少計算量即使現在不缺算力還是需要，否則計算量迅速膨脹徒增開銷，用結果導向的思路來說不影響效果的情況下downsampling總是好的；3.pooling或者說設置較大stride、現代架構其他捕捉全局關係的方式，除了减少特征图尺寸減少參數量外，也能抑制過擬合。是一種regularization。</p><hr><div align=center><img src="/pics/cs231n/Screenshot 2024-11-13 at 00.16.28.png" loading="lazy" alt="pooling也有filter size、stride，但這裡有overlap了呀，3x3的stride2，不是說沒有overlap嗎？" width="50%" height="50%"></div><h3 id="Pooling-层的-stride-是否一般设置为没有-overlap？"><a href="#Pooling-层的-stride-是否一般设置为没有-overlap？" class="headerlink" title="Pooling 层的 stride 是否一般设置为没有 overlap？"></a><strong>Pooling 层的 stride 是否一般设置为没有 overlap？</strong></h3><p>在卷积神经网络（CNN）中，<strong>Pooling 层的 stride</strong> 是否设置为没有 overlap（即步长等于池化窗口大小）通常取决于网络设计的需求。一般来说，<strong>Pooling 层的 stride</strong> <strong>通常设置为没有 overlap</strong>，即步长与池化窗口大小相同。具体解释如下：</p><ol><li><p><strong>没有重叠（No Overlap）</strong>：</p><ul><li><strong>Stride = Pooling窗口大小</strong>：这是最常见的设置，意味着池化操作的步长与池化窗口的大小相同，从而池化窗口之间没有重叠。 </li><li>例如，在 <strong>2x2</strong> 的池化窗口下，<strong>stride = 2</strong> 会导致每次池化窗口滑动两个像素，池化窗口之间没有任何重叠区域。</li></ul><p><strong>优点</strong>：</p><ul><li><strong>计算效率更高</strong>：没有重叠的池化可以减少特征图的尺寸，降低计算复杂度。</li><li><strong>避免冗余计算</strong>：每次池化操作处理的区域都是唯一的，因此没有重复计算的部分。</li></ul></li><li><p><strong>有重叠（Overlap）</strong>：</p><ul><li><strong>Stride &lt; Pooling窗口大小</strong>：在某些情况下，池化窗口之间会有重叠。例如，使用 <strong>2x2</strong> 的池化窗口和 <strong>stride = 1</strong>，池化窗口会覆盖有重叠的区域。</li></ul><p><strong>优点</strong>：</p><ul><li><strong>保留更多细节</strong>：有重叠的池化可能会保留更多的特征，尤其是在需要更精细特征提取时。</li></ul><p><strong>缺点</strong>：</p><ul><li><strong>计算量更大</strong>：重叠池化会增加计算开销，导致计算效率较低。</li></ul></li><li><p><strong>结论</strong>：</p><ul><li>在大多数情况下，<strong>Pooling 层的 stride</strong> 被设置为没有重叠，尤其是在经典的卷积神经网络（如 LeNet、VGG）中。这样可以高效地减少特征图的尺寸，并降低计算量。</li><li>在某些特殊任务中（例如，需要更细致特征提取的任务），也可以选择有重叠的池化。</li></ul></li></ol><hr><h3 id="為什麼max-pooling比average-pooling更commonly-used？"><a href="#為什麼max-pooling比average-pooling更commonly-used？" class="headerlink" title="為什麼max pooling比average pooling更commonly used？"></a><strong>為什麼max pooling比average pooling更commonly used？</strong></h3><ol><li>实践效果更好。在大量的实际实验中，使用 Max Pooling 的 CNN 通常比使用 Average Pooling 的 CNN 效果更好，尤其是在分类和目标检测任务中。这也是 Max Pooling 被更广泛应用的原因之一。</li><li>更好地保留关键信息。Max Pooling 选择每个池化窗口中的最大值，从而保留了特征图中的最显著特征。这种方式在提取边缘、纹理等重要特征时非常有效。相比之下，Average Pooling 是取平均值，可能会平滑掉重要的边缘信息，导致信息丢失。</li><li>具有更强的平移不变性。Max Pooling 能更好地处理位置的微小变化（平移不变性），因为它仅保留每个窗口的最大值，对其他不显著的特征不敏感。这在处理图像时有利于使网络对物体位置的微小变动更加鲁棒。</li></ol><div align=center><img src="/pics/cs231n/Screenshot 2024-11-13 at 00.24.11.png" loading="lazy" alt="每一個小圖都是activation maps，最後pooling層只剩一堆像素，因為已經是最高級最抽象的特徵（like 物体的局部区域、图案、场景、动作、语义信息" width="50%" height="50%"></div><p>訓練例子：<a href="https://cs.stanford.edu/people/karpathy/convnetjs/demo/cifar10.html">https://cs.stanford.edu/people/karpathy/convnetjs/demo/cifar10.html</a></p><p>通常只有第一層activation maps能被解釋，越高層越難解釋。</p><ul><li>There is a trend towards smaller filters and deeper architectures</li><li>There is a trend towards getting rid of POOL/FC layers (just CONV)</li><li>Typical architectures look like:<br>  <strong>[(CONV-RELU)*N-POOL?]*M-(FC-RELU)*K,SOFTMAX</strong><br>  where N is usually up to ~5, M is large, 0 &lt;= K &lt;= 2. </li><li>but recent(2017) advances such as ResNet/GoogLeNet challenge this paradigm</li></ul><h2 id="Lecture-6"><a href="#Lecture-6" class="headerlink" title="Lecture 6"></a><strong>Lecture 6</strong></h2><ul><li>Activation Functions(non-linearity)</li><li>Data Preprocessing</li><li>Weight Initialization</li><li>Batch Normalization</li><li>Babysitting the Learning Process</li><li>Hyperparameter Optimization</li></ul><h3 id="Activation-Functions-non-linearity-为什么输入要正负样本均衡？"><a href="#Activation-Functions-non-linearity-为什么输入要正负样本均衡？" class="headerlink" title="Activation Functions(non-linearity) 为什么输入要正负样本均衡？"></a><strong>Activation Functions(non-linearity) 为什么输入要正负样本均衡？</strong></h3><p>gradient on w這個說法指的是$\frac{\partial L}{\partial w}\ or\ \nabla_w L$。（或者df/dw）</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-12 at 20.48.08.png" loading="lazy" alt="" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>backprop反向傳播回來的value可以光看曲線不用公式，平緩的地方導數為0（forward pass就看函數曲線本身，backward就看曲線變化，高中數學），(sigmoid gate, a node in computational graph)</th><th>講得太好了這裡</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 17.09.58.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 16.10.56.png" alt=""></td></tr></tbody></table></div><ol><li>sigmoid<ul><li>3 problems:<ol><li>Saturated neurons “kill” the gradients:（注意這裡說的是激活函數這個node的gradient，只有一個自變量）<ul><li>x = -10, sigmoid(x) = 0, sigmoid’(x) = (1-0)0 = 0</li><li>x = 0, sigmoid(x) = 1/2, sigmoid’(x) = (1-1/2)1/2 = 1/4</li><li>x = 10, sigmoid(x) = 1, sigmoid’(x) = (1-1)1 = 0</li></ul></li><li>Sigmoid outputs are not zero-centered: 如下图（這裡說的是激活函數之前score function node，有W和X倆自變量）<ul><li>如果一个神经元的输入X全是正的，gradients on w即dL/dw = dL/df * df/dw（其中df/dw=x）就全是正的或者负的，就会导致graident只向全正和全负更新，最优化W的过程就会很低效。</li><li>这也是为什么x最好positive和negative均衡，因为如果输入数据X本身就是全正的，那不需要sigmoid激活函数压缩就已经会导致图里的zig zag path问题了。</li><li>而sigmoid激活函数就是这样一个会把输入全部拉到0～1的玩意。</li></ul></li><li>exp() is a bit computationally expensive</li></ol></li></ul></li><li>tanh(x)解决了zero-centered的问题，但还是存在梯度消失问题，而且计算开销比sigmoid更大因为有更多exp运算。$\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}$</li><li>ReLU(Rectified Linear Unit), f(x) = max(0, x)<ul><li>pros:（上面的饱和函数、计算开销問題都解決了）<ul><li>Does not saturate (in region).</li><li>Very computationally efficient </li><li>Converges much faster than sigmoid/tanh in practice (e.g. 6x faster) 收斂更快</li><li>Actually more biologically plausible than sigmoid</li></ul></li><li>2012 Alex Krizhevsky第一次使用</li><li>cons:<ul><li>zero-centered問題還是存在</li><li>dead relu<ul><li>Dead ReLU 问题是指在使用 ReLU (Rectified Linear Unit) 激活函数时，某些神经元可能在训练过程中始终输出零，这些神经元被称为“死神经元”。这种情况通常发生在 ReLU 的输入为负值时，ReLU 会将其输出设置为零。长期处于这种状态的神经元无法进行有效的训练或更新，从而导致 死 ReLU 问题。</li><li>虽然 Dead ReLU 问题是存在的，但它在实际应用中通常并不严重，</li></ul><ol><li>神经网络中往往有足够的神经元来弥补死神经元的影响。</li><li>死神经元不会影响其他神经元的梯度传播。</li><li>使用 改进版 ReLU 激活函数（如 Leaky ReLU 或 PReLU）可以有效避免死神经元问题。</li></ol></li></ul></li></ul></li><li>Leaky ReLU / PReLU<ul><li>Leaky ReLU</li><li>f(x) = max(0.01x, x)通常是0.01</li><li>will not “die”.</li><li>PReLU(Parametric ReLU)</li><li>f(x) = max(αx, x)</li><li>α是通過backprop/gradient descent訓練得到的，可是α如果學成負數，包括下面的ELU激活函數，都可能讓輸出全部變成正數，跟sigmoid一樣的問題</li><li>α是不是每個neuron都不一樣？Serena也記不清了。chatgpt說是每個都獨立，沒有全局alpha，那這樣的話α學成負數就不是什麼問題了，大概會是偶然現象</li></ul></li><li>ELU(Exponential Linear Units)<ul><li><script type="math/tex; mode=display">f(x) = \begin{cases}       x, & \text{if } x \geq 0 \\      \alpha (e^x - 1), & \text{if } x < 0       \end{cases}</script></li><li>pros<ul><li>All benefits of RelU</li><li>Closer to zero mean outputs</li><li>Negative saturation regime compared with Leaky ReLU adds some robustness to noise</li></ul></li><li>cons<ul><li>exp() computation</li></ul></li></ul></li><li>maxout neuron<ul><li>$f(x) = \max \left( W_1^T x + b_1, W_2^T x + b_2, \dots, W_k^T x + b_k \right)$</li><li>Does not have the basic form of dot product -&gt; nonlinearity</li><li>pros:<ul><li>Generalizes ReLU and Leaky ReLU</li><li>Linear Regime!(線性狀態（線性區間）) Does not saturate! Does not die!</li></ul></li><li>cons:<ul><li>doubles the number of parameters/neuron</li></ul></li></ul></li></ol><div align=center><img src="/pics/cs231n/Screenshot 2024-11-13 at 17.50.40.png" loading="lazy" alt="TLDR，現在用什麼activate？transformer用的啥來著？好像也不是很關鍵了" width="50%" height="50%"></div><p>怎麼理解“activate”激活？這節課原來後面講了如何可視化模型學到的特徵。(basically each element of these grids is showing what in the input would look like that basically maximizes the activation of the neuron. So in a sense, what is the neuron looking for?上面這段看看</p><h3 id="Data-Preprocessing"><a href="#Data-Preprocessing" class="headerlink" title="Data Preprocessing"></a><strong>Data Preprocessing</strong></h3><div class="table-container"><table><thead><tr><th>zero-mean就是因為<a href="#Activation-Functions-non-linearity-为什么输入要正负样本均衡？">上面</a>的原因，也有別的PCA(decorrelate)和Whitening</th><th>圖片說是一般不用normalize，只減去均值，減所有channels和per-channel結果上沒區別</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 17.52.35.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 17.59.54.png" alt=""></td></tr></tbody></table></div><p>注意，trainning set上做的preprocess，test set也要做。注意是訓練階段得到的均值，而不是测试数据本身的均值。</p><p>上面的兩種方法：</p><ol><li>计算训练集中的所有图像的像素值的均值，得到一个 全局均值图像，然後所有图像的每个像素都减去训练集中的所有图像在该像素位置的均值；</li><li>RGB三個通道分別算均值，這倆方法可以取足夠量的數據算一個值，只要能代表總體分布就行不用完全全部數據。</li></ol><h3 id="Weight-Initialization"><a href="#Weight-Initialization" class="headerlink" title="Weight Initialization"></a><strong>Weight Initialization</strong></h3><div align=center><img src="/pics/cs231n/Screenshot 2024-11-13 at 18.17.47.png" loading="lazy" alt="所有的neurons都會變一樣，都會有一樣的梯度，也會一樣地更新。" width="50%" height="50%"></div><ul><li>First idea: Small random numbers (gaussian with zero mean and 1e-2 standard deviation)<ul><li>W = 0.01 * np. random. rand (D,H)</li><li>Works ~okay for small networks, but problems with deeper networks.</li></ul></li></ul><div class="table-container"><table><thead><tr><th>First Idea activation layer visualization, 結果為右</th><th>這裡的gradients跟前面一樣也是dLoss/dW=X，這裡的激活層值全為0，所以梯度完全沒有更新</th><th>不用0.01而x100倍用1.0的初始化W的話，因為用的是tanh，到了激活函數的飽和區間，tanh飽和區間斜率幾乎是0，所以梯度也會消失</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 19.27.18.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture6-46.jpeg" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture6-47.jpeg" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>xavier initialization用tanh，因為tanh在0處斜率很大，所以是梯度很有效的區域</th><th>用relu就梯度消失了，這裡雖然上面的mean也是0，但是relu在0處沒有斜率，沒有梯度，所以神經元也是deactivated</th><th>用He初始化就變成我們想看到的分布，下面分布圖一樣是因為x拉太長了，上面的mean均值在0.5，是relu很有效的斜率gradient為1的線性區域</th><th>2017年仍是個活躍研究領域，不知道現在如何？</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture6-48.jpeg" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture6-49.jpeg" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture6-51.jpeg" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-13 at 20.36.55.png" alt=""></td></tr></tbody></table></div><h3 id="Batch-Normalization"><a href="#Batch-Normalization" class="headerlink" title="Batch Normalization"></a><strong>Batch Normalization</strong></h3><p>We wants to keep activations in a gaussian range, like above, so use BN.<br>關於BN我的思考：</p><ol><li><p>四年前就assginment卡在这没继续了，这次可好好搞明白。其实这部分跟上面找比较好的初始化W，以使每一层输入的分布都在神经元的有效区间内是一样的道理，有效区间就是tanh的0附近，relu的正数线性区间，斜率=1，即能让梯度反向传递回去。</p></li><li><p>BN有幾個核心目的，（去他媽媽的减小内部协变量偏移(Internal Covariate Shift)）i.加速训练过程；ii.正则化作用（因為引入了小噪聲，噪聲指讓數據分布改變了？Serena的說法是，之前activation neuron只會考慮一個input但現在的input都是被整個batch的其他特徵影響過的，但總之增強泛化不是BN的主要目的）；iii.通過下面的#3.可以缓解梯度消失和梯度爆炸。</p></li><li><p>那麼只要搞懂為什麼normalization能加快收斂，就可以了——</p><ul><li><a href="https://stats.stackexchange.com/a/437848">https://stats.stackexchange.com/a/437848</a> In Machine learning, how does normalization help in convergence of gradient descent?</li><li>考慮$f(w)=w_1^2 + 25w_2^2$，通過觀察可以發現有global minimum $w=[0,0]^\top$。這個函數的梯度是<script type="math/tex">\nabla f(w)=\begin{bmatrix}2w_1 \\50w_2\end{bmatrix}</script>，假如learning rate 𝛼=0.035，$w^{(0)}=[0.5, 0.5]^\top,$ 梯度更新公式是$w^{(1)} =w^{(0)}-\alpha \nabla f\left(w^{(0)}\right)，$<img src="/pics/cs231n/lA6xQ.png" alt="">，df/dw永遠會朝向w2的方向，可以想像w^2和25w^2這兩個曲線，變化速率相差太大（係數就是X），但經過normalization後，減去mean(12)除以σ = √<span style="border-top: solid;">Σ(x-x̄)<sup>2</sup> / N</span> = √<span style="border-top: solid;">144</span> = <b>12</b>，可以標準化為$f(w)=-w_1^2 + w_2^2$，大概這麼理解下（這個分布只有兩個數據x1=1,x2=25所以感覺很怪），normalize後梯度變化會更朝向global minimum而不再zig zag。</li></ul></li><li><p>batch normalization會根據不同的激活函數學習不同的參數嗎？比如對relu來說，希望輸入都在正數區間避免dead relu，比如tanh希望輸入不要到飽和區間。還是說batch normalization總是盡可能接近mean=0，deviation=1的標準正態分佈？</p><ul><li>不一定？</li><li>chatgpt說BN總是希望輸入/特徵的分布盡可能接近標準正態，只不過normalization之後，1.可以避免太多負值導致太多dead relu；2.可以避免太多值在tanh的-1和1飽和區間裡。這樣來看也還是很好的。</li><li>但Serena說通過學習γ和β參數，雖然BN本身是想讓分布變成標準正態分佈，但也給模型靈活度，可以主動不讓tanh飽和，或者搞出全正數分布不讓dead relu出現。</li></ul></li></ol><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 00.52.27.png" loading="lazy" alt="在extreme情況下才會learn identity mapping，實際上這樣效果不好，模型的特徵不會從normal distribution在收斂過程中變回去散亂的分布" width="50%" height="50%"></div><ol><li><p>激活層/隱藏層的歸一化是輸入的歸一化，不是輸出的。</p></li><li><p>數據的歸一跟激活函數的歸一化/標準化是一樣的目的和道理，實際上就是因為數據normalization/standardization效果很好，那麼為什麼不把每一層的輸入值都歸一化/標準化？</p></li><li><p>BN 允许激活值的分布在训练过程中变化，并不强制要求它们是标准正态分布。尽管初始阶段会做标准化处理，但在学习过程中，BN 可以调整输出的均值和方差，导致输出分布变得更加灵活。</p><ul><li>通过学习 γ 和 β 参数，BN 可能会产生一些不完全符合标准正态分布的分布，包括可能的偏移或变化，如全正分布等。</li></ul></li><li><p>課程進行到1小時時同學問了我想問的問題，強迫features變成gaussian分布不會丟失信息讓數據失真嗎？CNN裡希望保持空間信息所以預處理不用normalization，對特徵圖activation maps其實不會失真，說只是shifting（- mean）和scaling（/ standard deviation）。</p></li></ol><p><strong>在测试时（Testing Phase）</strong>：</p><ul><li>在测试阶段，BN不再使用测试数据来计算均值和方差。相反，它会使用训练过程中通过各个mini-batch累计的全局均值和方差，每一层使用单独的全局均值和方差。</li><li>这样做是因为测试集的batch大小通常较小，直接计算时可能不稳定，且我们希望测试时模型的行为尽可能和训练时一致。</li></ul><h3 id="Babysitting-the-Learning-Process"><a href="#Babysitting-the-Learning-Process" class="headerlink" title="Babysitting the Learning Process"></a><strong>Babysitting the Learning Process</strong></h3><div class="table-container"><table><thead><tr><th>增強正則的時候，期望是loss變大（加了一項），debugging策略get✔。</th><th></th><th>take the first 20 examples from CIFAR-10;turn off regularization (reg = 0.0);use simple vanilla ‘sgd’，用最簡單的設定確保模型能過擬合很少的樣本，讓loss趨近於0，debugging strategy get✔</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.22.13.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.26.00.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.30.03.png" alt=""></td></tr></tbody></table></div><p>然後用小的正則來找合適的learning rate，最重要的超參，如果loss/cost變化太慢一般來說是learning rate太小了；loss變成nan就是越界了，learning rate太大，loss變成inf？</p><h3 id="Hyperparameter-Optimization"><a href="#Hyperparameter-Optimization" class="headerlink" title="Hyperparameter Optimization"></a><strong>Hyperparameter Optimization</strong></h3><p>Cross-validation strategy，<br>先粗定大範圍，然後仔細尋找（coarse to fine search）。</p><p>justin在2017年會盡量避免一次搜索2到4個以上超參數，複雜度畢竟指數級增長。</p><p>Tip for detecting explosions in the solver:<br>If the cost is ever &gt; 3 * original cost, break out early</p><div class="table-container"><table><thead><tr><th>沒懂為什麼用10的uniform冪為範圍</th><th>隨機的更好？</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.46.14.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.47.15.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>之前OFA和學姐做的另一個忘了名字的模型我整天就調適這玩意</th><th>CV就是這麼枯燥，我當時做小樣本都快做瘋了，小樣本那玩意太多實驗了（找ppt</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.48.31.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.49.35.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>觀察曲線調參</th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.50.35.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.51.29.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 01.52.41.png" alt=""></td></tr></tbody></table></div><h4 id="Track-the-ratio-of-weight-updates-weight-magnitudes"><a href="#Track-the-ratio-of-weight-updates-weight-magnitudes" class="headerlink" title="Track the ratio of weight updates / weight magnitudes"></a><strong>Track the ratio of weight updates / weight magnitudes</strong></h4><p>“<strong>Track the ratio of weight updates / weight magnitudes</strong>” 简单来说，指的是计算在每次训练迭代中，<strong>权重更新的大小</strong>与<strong>权重的当前大小</strong>之间的比例。</p><h4 id="计算方式："><a href="#计算方式：" class="headerlink" title="计算方式："></a><strong>计算方式：</strong></h4><ol><li><p><strong>权重更新</strong>（Weight Updates）：计算在一次迭代中，权重的更新量，通常表示为：</p><script type="math/tex; mode=display">\Delta w = -\eta \cdot \nabla_w L(w)</script><p>其中，$\eta$ 是学习率，$\nabla_w L(w)$ 是权重关于损失函数的梯度。</p></li><li><p><strong>权重的大小</strong>（Weight Magnitudes）：权重向量的模长，通常使用 L2 范数计算：</p><script type="math/tex; mode=display">\|w\|_2 = \sqrt{w_1^2 + w_2^2 + \dots + w_n^2}</script></li><li><p><strong>权重更新与权重大小的比率</strong>（Ratio of weight updates to weight magnitudes）：</p><script type="math/tex; mode=display">\text{Ratio} = \frac{\|\Delta w\|_2}{\|w\|_2}</script></li></ol><h4 id="目的："><a href="#目的：" class="headerlink" title="目的："></a><strong>目的：</strong></h4><ul><li><p><strong>衡量更新幅度与权重大小的关系</strong>：这个比率可以反映梯度更新的幅度相对于权重当前大小的比例。</p><ul><li>比率太大：可能表明学习率过高，导致梯度更新过大，容易导致训练不稳定。</li><li>比率太小：可能表明学习率过低，更新幅度过小，训练过程过于缓慢。</li></ul></li><li><p><strong>调节训练过程</strong>：通过监控这个比率，可以帮助调整学习率，以确保训练过程既不太剧烈，也不太缓慢，保持合理的收敛速度。</p></li></ul><p>“<strong>Track the ratio of weight updates / weight magnitudes</strong>” 通过计算每次权重更新的大小与权重大小之间的比率，帮助评估训练是否稳定、学习率是否合适。</p><h2 id="Lecture-7"><a href="#Lecture-7" class="headerlink" title="Lecture 7"></a><strong>Lecture 7</strong></h2><p>改變了一個超參後，其他超參的最優值會改變嗎？比如改變model size，learning rate的最優解不太會變，如果已經找到一個較好的範圍了，就算最壞改變也只是讓模型更慢收斂到global optimal而已，尤其是用了比較好的optimization策略。</p><ul><li>Fancier optimization</li><li>Regularization</li><li>Transfer Learning（回想起2021年剛來，就瘋狂讓我學遷移學習，現在都沒人在提了）</li></ul><h3 id="Fancier-optimization"><a href="#Fancier-optimization" class="headerlink" title="Fancier optimization"></a><strong>Fancier optimization</strong></h3><p>注⚠️：公式裡的x實際上是w。</p><div class="table-container"><table><thead><tr><th>local minima versus saddle point</th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/local minima versus saddle point.png" alt=""></td><td><img src="/pics/cs231n/14.7.3.png" alt=""></td></tr></tbody></table></div><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 19.21.19.png" loading="lazy" alt="鞍點最好的例子，這裡梯度是0，各個方向都沒有變化率" width="50%" height="50%"></div><p>SGD Problems：</p><div class="table-container"><table><thead><tr><th>1.跟上面zig zag例子一樣，這個問題不光希望通過BN數據/特徵解決，也希望通過優化算法解決</th><th>2.local minima和saddle point，雖然一維看起來local minima比saddle point更嚴重，但比如說在100- dimension的空間裡，每一個方向都上升造成local minima是很罕見的（任何一個方向下降就不是local minima$f(x_0) \leq f(x), \quad \forall x \in 邻域U$），而且saddle point周圍斜率過低訓練過慢也是很麻煩的問題</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 17.28.55.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 17.31.34.png" alt=""></td></tr></tbody></table></div><p>3.因為mini-batch SGD每次只用一小部分，反應不了真實分佈，而且有很多噪音，所以訓練過程會很曲折meander，但其實full batch vanilla gradient descent還是有這些問題。</p><h4 id="1-Momentum-SGD"><a href="#1-Momentum-SGD" class="headerlink" title="1. Momentum SGD"></a><strong>1. Momentum SGD</strong></h4><p>一些物理術語：</p><ol><li>velocity（速度，=speed速率+方向）；</li><li>Kinetic Energy（動能）；$KE=\frac{1}{2}mv^2$</li><li>Momentum（動量）；p=mv，碰撞中的動量守恆：在兩物體碰撞過程中，雖然動能可能不守恆（如在非彈性碰撞中），但動量總是守恆的。</li><li>Kinetic Friction（動摩擦）；</li><li>Static Friction（靜摩擦）。</li></ol><p>動量和慣性的關係：<br>假設有兩個物體，質量分別為 1 kg 和 10 kg，兩者都以相同的速度 5 m/s 移動。</p><ul><li><p>輕物體的動量：</p><script type="math/tex; mode=display">p = mv = 1 \, \text{kg} \times 5 \, \text{m/s} = 5 \, \text{kg·m/s}</script></li><li><p>重物體的動量：</p><script type="math/tex; mode=display">p = mv = 10 \, \text{kg} \times 5 \, \text{m/s} = 50 \, \text{kg·m/s}</script></li></ul><p><strong>儘管兩者的速度相同，但重物體的動量要比輕物體大，這意味著在相同的外力作用下，重物體的運動狀態更難改變，這就是慣性強的表現。</strong></p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 20.16.48.png" loading="lazy" alt="用velocity vector取代gradient vector，這個intuition直覺上是想保持初始速度不那麼輕易改變（動量/慣性）" width="50%" height="50%"></div><p>velocity一般就初始化為0。</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 20.31.25.png" loading="lazy" alt="不光是下面zigzag的情況緩解，saddle point和local minima也可以因為有原“速度”無視gradient<=0的區域衝過去" width="50%" height="50%"></div><p>有個同學的問題問到了我的疑惑，如果因為這個動量，gradient衝過了一個narrow/sharp很窄的最優點怎麼辦？Justin Johnson的解釋是，我們還是希望要一個flat平台的最優點，更robust，就算很窄的那個是最優點，我們不一定想要。</p><h4 id="2-Nesterov-Momentum"><a href="#2-Nesterov-Momentum" class="headerlink" title="2. Nesterov Momentum"></a><strong>2. Nesterov Momentum</strong></h4><p>上面稍微變體。</p><div class="table-container"><table><thead><tr><th></th><th>公式看著有點頭疼，其實就是永遠在更新完gradient的點上計算新gradient，而不是在更新前計算gradient。現在計算有問題，希望loss和gradient一起計算，於是有了下面的版本。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 20.35.29.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.04.12.png" alt=""></td></tr></tbody></table></div><h4 id="3-Ada-Grad"><a href="#3-Ada-Grad" class="headerlink" title="3. Ada Grad"></a><strong>3. Ada Grad</strong></h4><p>類似於每次除以之前所有gradients的總和，目的是為了平均過大和過小的斜率/變化率/梯度以解決上面的zig-zag問題，但導致每次的梯度更新會越來越少。</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.16.54.png" loading="lazy" alt="" width="50%" height="50%"></div><h4 id="4-RMSProp"><a href="#4-RMSProp" class="headerlink" title="4. RMSProp"></a><strong>4. RMSProp</strong></h4><p>上面的slight variation，這個分母會自己變小。</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.18.18.png" loading="lazy" alt="decay rate like 0.9 0.99" width="50%" height="50%"></div><h4 id="5-Adam"><a href="#5-Adam" class="headerlink" title="5. Adam"></a><strong>5. Adam</strong></h4><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.24.48.png" loading="lazy" alt="積木A加B，beta還是0.9的decay rate，因此first moment和second moment剛開始會很小" width="50%" height="50%"></div><p>所以剛開始更新的時候底下分母很小，如果first moment不幸也很小的話可能導致更新幅度很大，讓模型更難收斂。要知道這不是因為loss函數斜率很大，只是因為adam 特性。</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.34.15.png" loading="lazy" alt="改進" width="50%" height="50%"></div><h4 id="6-Learning-Rate"><a href="#6-Learning-Rate" class="headerlink" title="6. Learning Rate"></a><strong>6. Learning Rate</strong></h4><div class="table-container"><table><thead><tr><th>decay over time</th><th>一般SGD Momentum用lr decay，而且最好先選一個lr，再用cross-validation選一個好的decay</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.39.10.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.41.26.png" alt=""></td></tr></tbody></table></div><h4 id="7-Second-order-Optimization"><a href="#7-Second-order-Optimization" class="headerlink" title="7. Second-order Optimization"></a><strong>7. Second-order Optimization</strong></h4><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.52.30.png" loading="lazy" alt="上面的都是求一階導數，這裡用二階導數second derivative" width="50%" height="50%"></div><p><strong>好像沒見有人用過，這個倒是可以拿來當畢業論文的創新點。XD</strong></p><div class="table-container"><table><thead><tr><th>考慮拿來水論文</th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.55.11.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.54.44.png" alt=""></td></tr></tbody></table></div><h4 id="8-Test-Error"><a href="#8-Test-Error" class="headerlink" title="8. Test Error"></a><strong>8. Test Error</strong></h4><div class="table-container"><table><thead><tr><th>如果在Training Dataset已經做得足夠好了，怎麼減小train error和test error之間的gap？記得嗎？test set是最後啟用的，我們只能用val set測試</th><th>用不同的隨機初始化訓練十個模型，平均他們的預測結果，在比賽裡還是有用的</th><th>或者可以在模型訓練的不同階段，把weights拿出來幾個snapshots當不同模型，自己跟不同時間的自己ensemble，2017當周的ICLR</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.10.15.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 21.58.52.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.08.48.png" alt=""></td></tr></tbody></table></div><p>ensemble的模型可以用不同的超參數。</p><p>如果不訓練多個模型來提升泛化能力，單模型改善表現可以使用正則。</p><h3 id="Regularization"><a href="#Regularization" class="headerlink" title="Regularization"></a><strong>Regularization</strong></h3><h4 id="1-Dropout"><a href="#1-Dropout" class="headerlink" title="1. Dropout"></a><strong>1. Dropout</strong></h4><p>在 dropout 技術中，將神經網絡中的一部分神經元的輸出設為 0，而不是將其輸入設為 0。</p><div class="table-container"><table><thead><tr><th>這種看起來就是totally messing with nets的行為怎麼就好用了？</th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.40.05.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.40.41.png" alt=""></td></tr></tbody></table></div><p>反正就是好用。</p><div class="table-container"><table><thead><tr><th>test做法</th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.43.42.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.44.05.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.45.11.png" alt=""></td></tr></tbody></table></div><p>dropout當然會導致training更久，因為w更新得更少了？</p><p>有點像都是引入了噪聲，有時bn就夠了，但dropout相當於給regularization加了個係數（變0的probability）更靈活了。</p><h4 id="2-Data-Augmentation"><a href="#2-Data-Augmentation" class="headerlink" title="2. Data Augmentation"></a><strong>2. Data Augmentation</strong></h4><p>training加入stochasticity和noise的都可以看作regularization。</p><div class="table-container"><table><thead><tr><th>pattern裡test time的marginalization是說“边缘化”，即再把噪聲給忽略掉</th><th>在網絡設計上加入這些“創新點”（別人的積木）可以當做大論文和專利的</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.50.22.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.51.55.png" alt=""></td></tr></tbody></table></div><p>每次用多少個正則手段？BN很多情況已經夠了（加速擬合、防止過擬合），應該每次感覺模型過擬合了才加入新的策略。</p><h3 id="Transfer-Learning"><a href="#Transfer-Learning" class="headerlink" title="Transfer Learning"></a><strong>Transfer Learning</strong></h3><p>Off the shelf和off the wall到底啥意思？<br>这两个短语的含义是通过比喻或隐喻演变而来的，跟它们字面上的意思有些联系。让我分别解释一下为什么会有这样的意思：</p><ol><li><p><strong>Off the shelf</strong><br>这个短语最初的字面意思是从货架上拿下来的物品。我们都知道商店里货架上的商品是直接可供购买的，顾客不需要等待生产或定制。所以，“off the shelf” 引申为“现成的”或“不需要定制的”。这个表达强调的是可以直接得到、无需额外处理的意思。</p><p>例子：在一些领域，像软件、硬件、书籍等商品，有现成的商品直接从货架上取下，可以即刻使用，因此常常被形容为“off the shelf”。也可以用来描述任何“常规、标准化”的东西。</p></li><li><p><strong>Off the wall</strong><br>这个短语的起源比较有趣。它通常解释为某物“从墙上弹出来”，在字面上听起来像是某物离开了正常的轨迹，变得不受控制或不合常规。墙是一个常见的边界或框架，东西从墙上弹出来就意味着它突破了这个框架或界限。</p><p>在这个意义上，“off the wall” 形容的是“跳脱框架、非常规、不拘一格”的事物。因此，原本它用来形容某事或某人行为怪异、出乎意料或充满创意，后来这个含义得到了广泛应用。</p><p>例子：如果你看到一个人的行为或想法非常独特，无法用传统的方式理解，或者说他的艺术作品完全不符合常见的审美标准，就可以用“off the wall” 来形容它的独特性或非常规性。</p></li></ol><p>这些表达通常是从具体的、可观察的现象逐渐引申出更抽象的意义。</p><div align=center><img src="/pics/cs231n/Screenshot 2024-11-14 at 22.56.14.png" loading="lazy" alt="" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>當時的CNN剛開始pretrain-finetune，已經不train from scratch了。</th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-11-15 at 14.59.54.png" alt=""></td><td><img src="/pics/cs231n/Screenshot 2024-11-14 at 23.02.26.png" alt=""></td></tr></tbody></table></div><p>optimization- training loss<br>regularization-test performance<br>transfer learning-less data</p><p>剛發現原來invisibility:true/false這個已經沒用了，得-BeInvisible，都給忘了。另外priority一定要有，不然推薦文章排序會bug。</p><h2 id="Lecture-8"><a href="#Lecture-8" class="headerlink" title="Lecture 8"></a><strong>Lecture 8</strong></h2><ul><li>CPU vs GPU</li><li>Deep Learning Frameworks<ul><li>Caffe / Caffe2</li><li>Theano / TensorFlow</li><li>Torch / PyTorch</li></ul></li></ul><h3 id="CPU-vs-GPU"><a href="#CPU-vs-GPU" class="headerlink" title="CPU vs GPU"></a><strong>CPU vs GPU</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>對應的行和列完全可以分別在不同的gpu core上同時運算，包括cnn的不同filter等都是parallelized任務</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8-2.png" loading="lazy" alt="" width="50%" height="50%"></div><p>沒學會的cuda課，王麓涵好像是在做OpenCL系的矩陣算法優化來著，來浙大最遺憾的就是抄那門課作業糊弄及格（代碼之前又被我從郵件下回來了），在雲大最後悔的是編譯原理抄作業過關，有趣的是都是github上的校友repo，再其次就是OS課的大作業自己沒做出來抄了一段代碼。</p><p>Justin Johnson說是他從沒為自己的研究寫過cuda代碼。And <strong>cuDNN much faster than “unoptimized” CUDA.</strong></p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8-3.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Deep-Learning-Frameworks"><a href="#Deep-Learning-Frameworks" class="headerlink" title="Deep Learning Frameworks"></a><strong>Deep Learning Frameworks</strong></h3><p>Torch(NYU / Facebook)→PyTorch(Facebook)，2017年時Justin Johnson最常用的就是Torch和Pytorch，現在也仍然是吧。</p><p>Caffe(UC Berkeley)→Caffe2(Facebook)<br>Theano(U Montreal)→TensorFlow(Google)</p><p>自己寫from scratch的（不是stretch伸展“Scratch”的原意是體育比賽地上畫的線）積木代碼雖然很爽，但是相比之下有三個不足：</p><p>The point of deep learning frameworks:<br>(1) Easily build big computational graphs<br>(2) Easily compute gradients in computational graphs<br>(3) Run it all efficiently on GPU (wrap cuNN, cuBLAS, etc)</p><p>無論是寫代碼效率還是運行效率都讓人不得不用第一代發源於academia第二代發展於industry的現成框架。</p><div class="table-container"><table><thead><tr><th>Numpy is definitely CPU only. And it’s a pain to have to compute your own gradient.</th><th>GOAL:  looks like numpy, but on GPU</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture8-4.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture8-5.png" alt=""></td></tr></tbody></table></div><h4 id="Tensorflow"><a href="#Tensorflow" class="headerlink" title="Tensorflow"></a><strong>Tensorflow</strong></h4><p>沒有allocate memory，只是在graph裡加入這些nodes，所謂“placeholders”。</p><p>在CPU memory和GPU memory之間copy數據(weights/gradients)是開銷很大的，會成為bottleneck，定義成variable而不是placeholder可以避免這個問題：</p><blockquote><p>Change w1 and w2 from placeholder (fed on each call) to Variable (persists in the graph between calls. Add assign operations to update w1 and w2 as part of the graph! Run graph once to initialize w1 and w2 first, then run many times to train.)</p></blockquote><p>為什麼X和y不也放進graph裡？因為比如mini-batches裡的數據每個iteration會變，沒法提前存。</p><p>updates只是為了更新w加入的一個依賴於w的變量，因為想計算updates就要先更新前置w nodes，tf.group將多個操作聲明依賴關係，使他們作為一個整體運行，updates只是返回None。</p><p>相比一點一點group需要的參數，用tf的optimizer一兩行就夠了，內部實現跟上面一樣。</p><p>Justin Johnson也只知道placeholder是graph之外的，variable存在在graph裡面，並不清楚內部實現。</p><p>顯式定義的loss也可以用tf的函數代替。</p><p>最後可以把initial也都用簡便函數代替。除了這些還有很多高層庫能用，比如Keras就是基於tensorflow開發的（剛知道，也可以用tensorflow前身theano當後端），因為computational graph（比如ass1 ass2裡的）是很底層的實現，思考neural nets時一般會用比較高層的視角，用高層抽象的庫對開發效率是很有幫助的。在keras裡就變成了model.compile做back propagation(backward)工作，model.fit取代train()。Sklearn就不像keras是某個框架的API，也不是只面向深度學習的，是基於NumPy、SciPy 和 Cython的獨立<strong>机器学习</strong>库 。</p><p>前後對比：</p><div class="table-container"><table><thead><tr><th>代替前</th><th>代替後</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture8-12.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture8-13.png" alt=""></td></tr></tbody></table></div><h4 id="Pytorch"><a href="#Pytorch" class="headerlink" title="Pytorch"></a><strong>Pytorch</strong></h4><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8-6.png" loading="lazy" alt="ndarray(n-dimensional array)" width="50%" height="50%"></div><p>相比tensorflow顯式更改cpu:0和gpu:0，pytorch是改數據類型——dtype = torch.FloatTensor。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture8-10.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture8-11.png" alt=""></td></tr></tbody></table></div><p>torch沒有很多高層庫的選擇，只有nn.Modules。</p><p>tensorflow會先定義一個graph，然後train()時運行多次這個graph；而pytorch是每次前向forward pass裡都創造一個新的graph？？這樣能讓代碼更clean。</p><hr><h5 id="Autograd"><a href="#Autograd" class="headerlink" title="Autograd"></a><strong>Autograd</strong></h5><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8-17.png" loading="lazy" alt="" width="50%" height="50%"></div><hr><div class="table-container"><table><thead><tr><th>而且可以定義New Autograd Functions，就跟assignment1和2裡的layers/layer_utils一樣。</th><th>optimizer.step()更新所有參數，tf裡是seesion.run()</th><th>也可以Define new nnModules，跟assignment2裡classifier的fcnet和cnn非常相似。Define forward pass using child modules and autograd ops on Variables. No need to define backward - autograd will handle it.</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture8-16.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture8-15.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture8-14.png" alt=""></td></tr></tbody></table></div><p><code>torch.clamp(input, min=None, max=None, *, out=None)</code>會把tensor(就是gpu上的np.array)最小值截斷到min，最大值截斷到max，不改變形狀。</p><p>dataloader是方便數據分割操作的玩意兒，需要定義自己的數據class，有minibatching, shuffling, multithreading等。例子等ass2寫的時候再理解吧</p><p>visdom Somewhat similar to TensorBoard: add logging to your code, then visualized in a browser Can’t visualize computational graph structure (yet?)（Justin Johnson沒用過）</p><p>Direct ancestor of PyTorch (they share a lot of C backend)<br>Written in Lua, not Python？我去居然是lua。2016年Justin Johnson上課還用torch，2017的pytorch是很新的東西。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture8-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>static優勢是serialization序列化，部署時比較容易？<br>dynamic優勢：1.conditional，pytorch要做的跟我在numpy裡做過的一樣，但tf就要用tf.cond提前在graph裡加nodes/ops，倒是可以做到一樣的能力，就是麻煩；2.loop也是，循環也需要在graph裡顯式定義出nodes，可以看出tensorflow幾乎創造了自己的計算圖的programming language。Justin Johnson覺得學著用新語言很麻煩，用torch更舒適。</p><p>Recurrent networks 、Recursive networks，cs224n的Dynamic Graph Applications例子，比如對數Tree Data Structure的數據處理，torch可以就用普通的python做法，tensorflow就究極麻煩</p><h4 id="Caffe"><a href="#Caffe" class="headerlink" title="Caffe"></a><strong>Caffe</strong></h4><ul><li>Core written in C++</li><li>Has Python and MATLAB bindings</li><li>Good for training or finetuning feedforward classification models</li><li>Often no need to write code!</li><li>Not used as much in research anymore, still popular for deploying</li></ul><p>Caffe: Training / Finetuning<br>No need to write code!</p><ol><li>Convert data (run a script)</li><li>Define net (edit prototxt)</li><li>Define solver (edit prototxt)</li><li>Train (with pretrained weights) (run a script)</li></ol><p>上這課的時候一個周前剛Facebook發布了caffe2。</p><ul><li>Static graphs, somewhat similar to TensorFlow</li><li>Core written in C++</li><li>Nice Python interface</li><li>Can train model in Python, then serialize and deploy without Python</li><li>Works on iOS / Android,</li></ul><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture8-8.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture8-9.png" alt=""></td></tr></tbody></table></div><h2 id="Lecture-9"><a href="#Lecture-9" class="headerlink" title="Lecture 9"></a><strong>Lecture 9</strong></h2><p>Case Stydies:</p><ul><li>LeNet-5</li><li>AlexNet (2012 ImageNet Large Scale Visual Recognition Challenge (ILSVRC) Winner)</li><li>VGG (ILSVRC’14 2nd in classification, 1st in localization)</li><li>GoogLeNet (ILSVRC’14 classification winner)</li><li>ResNet (ILSVRC’15 winner，也在COCO’15數據集上的各比賽裡都明顯優於其他模型)</li></ul><p>這些模型都是ILSVRC比賽的冠軍模型，空缺的2013年的ZFNet只是增加了AlexNet的參數量。</p><p><a href="#lecture-1">李飛飛的ImageNet</a>只在2010年到2017年之間舉辦了這個ILSVRC比賽，因為2017年之後ImageNet分類任務的錯誤率太低，已經沒有再在這個任務上提升的必要了。</p><p>ILSVRC的成功促進了深度學習技術在計算機視覺領域的爆炸性發展，但隨著技術的進步和研究焦點的轉移，比賽完成了其歷史使命而逐漸退出舞台。</p><h3 id="LeNet-5"><a href="#LeNet-5" class="headerlink" title="LeNet-5"></a><strong>LeNet-5</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture9.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="AlexNet"><a href="#AlexNet" class="headerlink" title="AlexNet"></a><strong>AlexNet</strong></h3><div class="table-container"><table><thead><tr><th>有些細節沒有說明(simplified)，比如開始的224變成227(Serena說有些funny pattern)</th><th>因為當時GTX 580顯存只有3G放不下所有參數，所以分成兩半訓練</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture9-1.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-2.png" alt=""></td></tr></tbody></table></div><h3 id="ZFNet"><a href="#ZFNet" class="headerlink" title="ZFNet"></a><strong>ZFNet</strong></h3><div class="table-container"><table><thead><tr><th>參數變了，但idea一致</th><th>之後就是filter變小，layers變多的趨勢</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture9-3.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-4.png" alt=""></td></tr></tbody></table></div><h3 id="VGG"><a href="#VGG" class="headerlink" title="VGG"></a><strong>VGG</strong></h3><p>感受野（Receptive Field）指的是卷積神經網絡（CNN）中某個輸出單元對輸入圖像的感知範圍。簡單來說，它代表了輸出特徵圖上某個像素或神經元，能夠從原始輸入圖像中“看到”的區域大小。<br>感受野大小 就是這個像素值所受影響的輸入區域的範圍。例如：<br>如果感受野大小是 3×3，這意味著輸出特徵圖中某個像素對應於輸入圖像的 3×3 區域。</p><div class="table-container"><table><thead><tr><th>比如三層的activation maps裡，layer3上的一個點（一個像素）是由layer2的3x3範圍計算得到的，layer2的3x3是layer1上的5x5計算得到的。</th><th>因此7x7的一個filter可以由 3 個3x3的filter代替。所以從2014年的VGG之後都是上面所說的“filter變小，layers變多的趨勢”。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/receptive-field-in-convolutional-networks.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-5.png" alt=""></td></tr></tbody></table></div><p>$3 * 3^2C^2 = 27C^2 &lt; 7^2C^2 = 49C^2$，3 個 3x3 filters代替 7x7 filters，這個替換的參數量是原來的55%。</p><div class="table-container"><table><thead><tr><th>不是所有參數都要一直保存在顯存裡，梯度是圖裡Total Memory的2倍，無論是Hidden Layer/Activation Map還是Weights/Filters都是以浮點數存儲，FP32（32位浮點數，4字節）</th><th>VGG-16就是加FC一共16層，VGG-19多3層CONV，圖裡不知為何跟實際不太一樣，vgg應該是block1和block2有2層conv，vgg16後面3個blocks都是3層conv，vgg19後面3個blocks都是4層conv。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture9-6.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-7.png" alt=""></td></tr></tbody></table></div><p>再詳細記錄一下數量計算，這個很有用：</p><ol><li>存在顯存裡的數據：<ul><li>模型參數Weights and Biases，每次前向和反向傳播都要用到；</li><li>梯度Gradients，反向傳播結束後可以釋放，前向完再佔用顯存；</li><li>激活值Activations（數據集dataset是在CPU Memory或磁盤裡），<ul><li>哪些層有激活層？卷積層（Conv Layers），全連接層（FC Layers），所以這些層計算梯度需要考慮activation function；</li><li>哪些層沒有？批量歸一化層（BatchNorm Layers），Dropout之類的；</li><li>哪些激活函數需要存激活值？<ul><li>$\text{Sigmoid:} \quad \sigma’(x) = \sigma(x) \cdot (1 - \sigma(x))$</li><li>$\text{Tanh:} \quad \frac{\partial \tanh(x)}{\partial x} = 1 - \tanh^2(x)$</li></ul></li><li>哪些不需要存激活值，需要存激活函數的輸入而不是輸出，或者存別的？<ul><li>ReLU可以存activation的inputs而不是ouputs，或只存一個mask，輸入是正數的梯度是dout * 1，負數或0的梯度是dout * 0，這很像池化層（Pooling Layers），也是dout * MASK；</li><li>Leaky ReLU/PReLU也差不多，只需要存一個MASK，區別是負數或0的梯度是dout * alpha；</li></ul></li><li>除了ReLU/dropout這種存個MASK可以節省顯存之外，還可以用反向重計算（Recompute Activations）<ul><li>如果顯存非常有限，可以在反向傳播時重新計算前向傳播的激活值，而不是在顯存中保存它們。這叫 梯度檢查點（Gradient Checkpointing），以計算時間換取存儲空間。</li></ul></li></ul></li></ul></li><li>顯存使用 = 參數+激活數據+參數梯度(+激活梯度)<ul><li>前向顯存 = 參數 + 激活數據</li><li>反向顯存 = 參數 + 激活數據 + 參數梯度</li><li>激活梯度包括$dx$和prelu裡的$d\alpha$，后者幾乎可以忽略，dx不需要存儲，因為它是逐層即時計算並傳遞的。</li><li>兩倍關係：反向傳播的顯存需求 = 正向傳播需求 + 參數梯度需求，大約是兩倍，在transformer這種參數量一定大於激活數據的模型裡，反向對正向是大於兩倍關係的。</li><li>混合精度訓練（Mixed Precision Training）： 使用 16-bit 浮點數（FP16）存儲激活數據和梯度，顯著減少Memory占用；</li><li>模型壓縮： 减少参数数量，通过剪枝等方法减少存储需求。改數據類型也可以用在參數上，能成倍減少開銷。</li></ul></li><li>上面的Weights and Biases、Activations、Gradients都是以浮點數存儲，一般是FP32（32位浮點數，4字節），即實際佔用顯存量就是數據量 x 4bytes。</li></ol><p>為什麼用Softmax沒人用SVM loss？因為就是效果好。</p><ul><li>模型集成提分：Use ensembles for best results</li><li>FC7學到的特徵可以用於其他任務：FC7 features generalize well to other tasks</li></ul><h3 id="GoogLeNet"><a href="#GoogLeNet" class="headerlink" title="GoogLeNet"></a><strong>GoogLeNet</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture9-8.png" loading="lazy" alt="" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>這裡通過padding強行讓output size都是28x28，而且pooling意味著下一個inception module的輸出只會更深層，開銷爆炸。</th><th>用1x1的conv減depth雖然會有一點information loss但是能降低超多開銷。 其他dimensionality reduction也是可以用的。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture9-9.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-10.png" alt=""></td></tr></tbody></table></div><p>前面加兩個fc-loss（auxiliary outputs，training時3個loss不是分多次的只會有一次back prop，Serena也忘了inference時是三個loss取平均還是只用其中一個），更多地算前幾層梯度更新前幾層的參數，說是有更好的效果，說是有時梯度信息會在超長網絡裡丟失，這樣也可以緩解問題。</p><h3 id="ResNet"><a href="#ResNet" class="headerlink" title="ResNet"></a><strong>ResNet</strong></h3><p>何恺明閃亮登場。</p><p>既然趨勢是網絡越深效果越好，那可以直接狂堆層數嗎？這樣做其實會讓效果變差。而且是training/test error都高，所以並不是過擬合導致的。</p><p>一種假設是<strong>網絡越深越難優化</strong>，不然深層模型至少應該跟淺層一樣好，因為只需要copy淺層剩下的層讓input和ouput一致就行了。</p><p>那麼就讓模型學x的residual(x的殘餘)，模型完全可以把f(x)學成0達到上面說的效果，也許這樣會更簡單一點。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture9-16.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-15.png" alt=""></td></tr></tbody></table></div><p>實現細節：</p><div class="table-container"><table><thead><tr><th>丟掉VGG的兩層FC之後，參數量大大減少，記得上面VGG那裡計算參數量時幾乎全部都來自FC</th><th></th><th>跟GoogLeNet一樣可以用1x1的conv減少channel depth</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture9-11.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-12.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture9-13.png" alt=""></td></tr></tbody></table></div><p>H(x) = f(x) + x裡，f(x) 和 x 的concatenation如果維度不同就無法逐元素相加，所以：</p><h4 id="1-維度本來一致時"><a href="#1-維度本來一致時" class="headerlink" title="1. 維度本來一致時"></a><strong>1. 維度本來一致時</strong></h4><p>如果 $x$ 和 $f(x)$ 的通道數和空間尺寸一致，直接相加即可：</p><script type="math/tex; mode=display">H(x) = f(x) + x</script><ul><li>條件：$x$ 和 $f(x)$ 的形狀為 $[N, C, H, W]$，其中 $N$ 是 batch size，$C$ 是通道數，$H$ 和 $W$ 是特徵圖的高和寬。</li></ul><h4 id="2-通道數不一致時"><a href="#2-通道數不一致時" class="headerlink" title="2. 通道數不一致時"></a><strong>2. 通道數不一致時</strong></h4><p>當 $f(x)$ 改變了通道數時，需要對 $x$ 進行通道數匹配。</p><p><strong>解決方法</strong>：使用 $1 \times 1$ 卷積進行投影，將 $x$ 的通道數從 $C_{\text{in}}$ 變換為 $C_{\text{out}}$：</p><script type="math/tex; mode=display">x' = W_{\text{proj}} \ast x</script><ul><li>$W_{\text{proj}}$ 是 $1 \times 1$ 卷積核。</li><li>$x’$ 和 $f(x)$ 的形狀一致。</li></ul><h4 id="3-空間尺寸不一致時"><a href="#3-空間尺寸不一致時" class="headerlink" title="3. 空間尺寸不一致時"></a><strong>3. 空間尺寸不一致時</strong></h4><p>當步幅 $s &gt; 1$ 導致 $f(x)$ 的空間尺寸縮小（例如從 $[H, W]$ 變為 $[H/s, W/s]$），需要對 $x$ 進行下採樣。</p><p><strong>解決方法</strong>：使用帶步幅 $s$ 的 $1 \times 1$ 卷積，同時調整通道數和空間尺寸：</p><script type="math/tex; mode=display">x' = W_{\text{proj}} \ast x \quad (\text{stride}=s)</script><ul><li>$x’$ 的形狀與 $f(x)$ 保持一致。</li></ul><p>總之都是用 1x1 的conv修改維度。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture9-14.png" loading="lazy" alt="圓的大小代表顯存開銷（參數量）" width="50%" height="50%"></div><h3 id="Other-Architectures"><a href="#Other-Architectures" class="headerlink" title="Other Architectures"></a><strong>Other Architectures</strong></h3><ul><li>Network in Network (NiN)</li><li><a href="https://arxiv.org/pdf/1608.06993">DenseNet</a>，原來是中國人做的，根據<a href="https://github.com/mantasu/cs231n/blob/master/assignment2/PyTorch.ipynb">這個solution</a>試試看吧。</li><li>SqueezeNet</li></ul><p>沒細看，又開始急了，趕緊RNN。</p><p>MLP(Multi-Layer Perceptron) = 多層 FC 層(Fully Connected Layer) + 激活函數 + 正則。</p><h2 id="Lecture-10"><a href="#Lecture-10" class="headerlink" title="Lecture 10"></a><strong>Lecture 10</strong></h2><p>JJ為什麼說AlexNet是9層？chatgpt：<br>AlexNet 是一個經典的卷積神經網絡，最初提出時被認為是 8 層深，但有時也被描述為 9 層。這取決於如何計算層的數量。總層數 = 卷積層（5 層）+ 全連接層（3 層）+ Softmax概率輸出層（有時算有時不算）。</p><p>2014年還沒出BN，所以VGG和GoogleNet訓練得很吃力，不太能收斂。VGG是先訓練了個11層的，後續往裡加成16/19層，JJ說GoogLeNet的兩個ugly hacks只是用來幫助收斂的。</p><p>residual也會讓梯度更好反向傳播，因為有一條x的直通路線，完全不用擔心梯度消失（爆炸還是會的吧？）。<br>管理梯度流在任何模型裡都很重要，包括RNN。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture10.png" loading="lazy" alt="Lecture9最後這些奇形怪狀的網絡，都可以把每條線看作為了讓梯度更好回流的手段" width="50%" height="50%"></div><p>recurrent nn是Justin Johnson’s favourite topic.</p><h3 id="循環nn-vs-递归nn"><a href="#循環nn-vs-递归nn" class="headerlink" title="循環nn vs. 递归nn"></a><strong>循環nn vs. 递归nn</strong></h3><div align=center><img src="/pics/cs231n/text4155-1-.png" loading="lazy" alt="recurrent nn(RNN,線性鏈式結構)是recursive(RecNN,樹結構)的特例" width="37%" height="37%"></div><h3 id="vanilla-RNN"><a href="#vanilla-RNN" class="headerlink" title="vanilla RNN"></a><strong>vanilla RNN</strong></h3><p>又叫做Simple RNN或Elman RNN。</p><p>每層用的W都是一樣的，更新 $\frac{\partial L}{\partial W}$ 的梯度則為每個time step的梯度之和：</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture10-1.png" loading="lazy" alt="簡潔的公式" width="50%" height="50%"></div><p>computational graph:</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture10-2.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10-3.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10-4.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10-5.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10-6.png" alt=""></td></tr></tbody></table></div><h4 id="最大概率argmax-vs-採樣sample-vanilla"><a href="#最大概率argmax-vs-採樣sample-vanilla" class="headerlink" title="最大概率argmax vs. 採樣sample, vanilla"></a><strong>最大概率argmax vs. 採樣sample, vanilla</strong></h4><div align=center><img src="/pics/cs231n/cs231n_2017_lecture10-7.png" loading="lazy" alt="這個例子用sample只是因為這裡如果取argmax結果出不來“hello”，實際上有時rnn在inference階段會使用sample增加模型輸出的多樣性，迫使它產生豐富答案。" width="50%" height="50%"></div><p>sampling是根據每個詞（輸出）的概率分佈進行隨機選擇，不是均勻分布的隨機採樣，哪個詞softmax後概率大被選中的概率就大。生成更多元、創造性或不確定的輸出，提高結果的真實感。</p><p>test time不用softmax用one-hot的原因：1.如果train/test給模型的東西不一致通常會輸出garbage；2.假如vocabulary是以萬計的詞表，用softmax這種dense vector會增加計算開銷，會傾向於用one-hot這種sparse vector。</p><p>這群同學問的問題都太好了。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture10-8.png" loading="lazy" alt="像上面說的，訓練時每次算梯度都要加起每一個step的loss，然後對每一個step求dw求和" width="50%" height="50%"></div><p>為降低開銷使用截斷反向傳播<strong>Truncated Backpropagation Through Time (TBPTT)</strong>，其實類似於我們不做full batch的gradient descent而選擇用mini-batches的SGD，這裡只用幾百個time steps計算一次梯度。</p><p>vanilla rnn就講到這，剩下的JJ就讓自己看min-char-rnn的代碼了。</p><h3 id="attention"><a href="#attention" class="headerlink" title="attention"></a><strong>attention</strong></h3><div class="table-container"><table><thead><tr><th>image captioning用CNN+RNN時，有很多組合的方式，比如把image information直接乘Weights加到第一個time step上。</th><th>training階段在每個caption的最後都加一個<code>&lt;end&gt;</code>token(開頭加<code>&lt;start&gt;</code>)，所以模型test階段會傾向於在最後採樣結束標誌。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture10-10.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10-9.png" alt=""></td></tr></tbody></table></div><p>可以用Microsoft Coco數據集從頭到尾訓練這樣一個CNN+RNN，也可以固定CNN最後的FC層，訓練RNN的時候捎帶更新FC。</p><div class="table-container"><table><thead><tr><th>JJ還講到了第一篇用到hard attention（不可導，后面強化學習還要講）的文章Show, Attend and Tell: Neural Image Caption Generation with Visual Attention, ICML 2015</th><th>第一個在神經網絡訓練裡用soft attention的是Neural Machine Translation by Jointly Learning to Align and Translate, ICLR 2015</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture10-11.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10-12.png" alt=""></td></tr></tbody></table></div><p>VQA也在用attention機制，image和word vectors的結合最常用的就是直接concatenation。</p><h3 id="Multilayers-RNN"><a href="#Multilayers-RNN" class="headerlink" title="Multilayers RNN"></a><strong>Multilayers RNN</strong></h3><p>多層也一般就三四五層，RNN沒有很深層的，LSTM也一樣。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture10lstm-3.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="LSTM"><a href="#LSTM" class="headerlink" title="LSTM"></a><strong>LSTM</strong></h3><p>因為向前面time step傳遞梯度時，$dh$ 會不停地乘 $dtanh$ (即 $1-tanh^2$ )和 $W^T$ ，這個值是不變的（截斷back prop也要很多time steps才update params），</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture10lstm.png" loading="lazy" alt="" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>如果這個 $dtanh \cdot W^T$ 值大於1，傳遞到最開始的 $dh_0$ 就很可能梯度爆炸，</th><th>小於1就很可能梯度消失。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture10lstm-1.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10lstm-2.png" alt=""></td></tr></tbody></table></div><p>解決方案是，太大就直接clamp截斷，為了應對太小的問題則需要其他RNN架構（LSTM同時能解決exploding和vanishing，已經是1997年的論文了，Long Short-Term Memory, Neural Computation, 1997, Sepp Hochreiter, Jürgen Schmidhuber (German)）。</p><div class="table-container"><table><thead><tr><th>公式裡的 <script type="math/tex">\begin{pmatrix}h_{t-1} \\ x_t\end{pmatrix}</script> 是指concatenation之後的vector，LSTM相比vanilla RNN除了hidden state $h_t$ 之外還需要maintain一個cell state $c_t$，$c_t$ 是個幾乎只在cell內部作用的內部量</th><th>LSTM同樣是用這個stack的向量 ((h+x), 1) 乘一個大的W (4h, (h+x))，結果直接切分成4個(h, 1)維向量，然後分別通過3個sigmoid和1個tanh激活函數得到4個跟hidden state（h）維度相同的門gates（i f o g）。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture10lstm-4.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10lstm-5.png" alt=""></td></tr></tbody></table></div><p>理解下右邊右下角公式：</p><ul><li>f是被sigmoid拉到0～1的值，跟cell state相乘可以理解成是否遺忘上一個時間的狀態，0為遺忘1為記住；</li><li>i也是0～1，用於決定本個LSTM cell的信息是否被寫入cell state；</li><li>g是-1～1的值，中文是“候選記憶單元”，是待寫入cell state的信息；<ul><li>所以 $c_t$ 的更新規則為，除了決定是否記住/忘記上個cell state，每個time step還會加一個(-1, 1)之間的值；</li><li>所以其實 $c_t$ 類似於一個time step的計數器，每個time step ±1；</li><li>在計算完cell state之後，就用 $c_t$ 來計算真正暴露在cell外的外部量，hidden state；</li></ul></li><li>o是被sigmoid壓縮到0～1的值，$c_t$ 通過 tanh 被 squashed 壓縮到 (-1, 1)之間與 $o$ 相乘，所以 $o$ 用來決定是否把 $c_t$ 用於外部計算。</li></ul><p>一個最直接的疑問可能是：這樣被壓縮到0到1之間的 $x_t$ 和 $h_t$ 不會造成信息損失嗎？——其實是不會的，不管那些“平滑變換”之類的說法，vanilla RNN本身和FC和CNN都是會用所謂的non-linearity把值 squash 到 0～1 或 -1～1 之間。<br>在本節最下面再討論一次激活函數non-linearity的問題。 </p><div class="table-container"><table><thead><tr><th>上面的加法只會直接傳遞upstream，乘法會讓upstream逐元素乘forget gate f，1.這樣傳遞梯度只會有逐元素乘，优於vanilla RNN的 $dtanh \cdot W^T$的矩陣乘；2.這樣每個time step的梯度反向傳播不會不斷地反覆地乘一個W，而會是乘不同的0～1之間的f，這樣不容易梯度消失？</th><th>cell state這條通過additive和element wise multiplicative interactions傳遞梯度的highway跟resnet很像</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture10lstm-6.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture10lstm-7.png" alt=""></td></tr></tbody></table></div><p>關於上面JJ說的的兩個好處我滿滿都是疑問：</p><ol><li>為什麼只有逐元素乘？因為更新W是通過i f o g四個門更新的，這些正向都是逐元素乘操作，所以反向梯度也是逐元素乘；</li><li>但為什麼逐元素乘好過矩陣乘？不知道是說訓練效率還是模型效果；</li><li>為什麼反覆乘一個f不會導致梯度消失？f在0～1之間，就算一個位置一直是0.99經過100個time steps也會變成0.366！<ul><li>說是乘W是固定一直乘一個，f是動態的，所以好；</li><li>說是傳遞cell state的梯度不經過tanh只經過加法操作（f i g都沒有tanh，只有o有，但是它們本身的sigmoid不會影響穩定性嗎？）梯度傳遞更穩定。</li><li>1:08:41原來JJ講到了！我太喜歡他們了——<ul><li>說是確實會有這種問題，所以在實踐中大家可能會把forget gate的biases都初始化為正數，所以在訓練剛開始時forget gate會一直接近1保證乾淨的梯度流，</li><li>確實仍然會存在vanishing的問題，但是會比vanilla RNN好非常多，就是因為1. forget gate是變化的，而且2. 是元素乘不是矩陣乘。</li></ul></li></ul></li></ol><p><strong>總之我們關心的只有W和dW</strong>，原本的RNN的 $\frac{\partial L}{\partial W}$ 只會通過h傳導而存在不停乘 $W^T$ 的問題，現在LSTM的 $\frac{\partial L}{\partial W}$ 同時通過 c 和 h 傳導，而 c 的梯度相比 h 有以上那些好處，所以回傳的梯度會更乾淨更穩定。</p><p><strong>Takeaway</strong>：<br>後來二十多年的研究裡並沒有效果全面超過LSTM或GRU的，可能某一個任務強但是其他任務弱。<br>可能直接用LSTM或GRU不會有什麼神奇的效果，但是通過添加 加法additive connections 和 乘法門multiplicative gates 來讓梯度流更合理地反向傳播的思路往往很有用。</p><h3 id="激活函數non-linearity"><a href="#激活函數non-linearity" class="headerlink" title="激活函數non-linearity"></a><strong>激活函數non-linearity</strong></h3><p>sigmoid、tanh、relu的目的是对数据进行平滑变换，不會丟棄信息，通过将输入映射到一个新的范围，使其在更合理的范围内流动，而不会出现过大的数值波动（避免梯度爆炸）或数值消失（避免梯度消失）。<br>它們基本都保留了輸入信號的大小和方向，而且能允许网络捕捉复杂的非线性关系。<br>在上面<a href="#结果">Lecture 4</a>那也想過相關問題，“這裡其實我一直有個疑問，如果X裡有x1 x1x2 x1x2x3這種高階特徵，那豈不是不再線性了？這個問題其實有點蠢，因為線性關係是指x1和f(x1)、x1x2和f(x1x2)、x1x2x3和f(x1x2x3)之間是線性/直線，而不是x1x2/x1x2x3跟f(x1)”。</p><p>Lecture 10 Summary</p><ul><li>RNNs allow a lot of flexibility in architecture design</li><li>Vanilla RNNs are simple but don’t work very well</li><li>Common to use LSTM or GRU: their additive interactions improve gradient flow</li><li>Backward flow of gradients in RNN can explode or vanish. Exploding is controlled with gradient clipping. Vanishing is controlled with additive interactions (LSTM)</li><li>Better/simpler architectures are a hot topic of current research</li><li>Better understanding (both theoretical and empirical) is needed.</li></ul><p>RNN本身效果太差，用就用LSTM，雖然equations看起來crazy，但是make sense，因為優化了backprop gradient flow。</p><h2 id="Lecture-11"><a href="#Lecture-11" class="headerlink" title="Lecture 11"></a><strong>Lecture 11</strong></h2><p><a href="https://bingcheng.openmc.cn/HyperQuest/">HyperQuest</a>它們開發完了，對他們同學有extra credit的調參遊戲，<strong>有空</strong>(when?)跟上面之前幾個<a href="http://vision.stanford.edu/teaching/cs231n-demos/linear-classify/">demo</a>一起玩一玩。</p><p>之前的主題都是image classification，這節課講detection/segmentation/localization和其他tasks。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture11.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Semantic-Segmentation"><a href="#Semantic-Segmentation" class="headerlink" title="Semantic Segmentation"></a><strong>Semantic Segmentation</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture11-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>語義分割任務目標是給每一個像素分一個label，這裡跟圖像分類一樣，數據集裡我們關心的類別是10個100個1000個提前確定的類。<br>右邊不區分兩頭牛都打上cow是語義分割的一個短板，instance segmentation實例分割會解決這個問題。</p><p>所以這類任務的數據很昂貴，需要用軟件手動畫邊界。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture11-3.png" loading="lazy" alt="sliding window一個像素一個像素分類開銷太大，所以需要端到端的一步到位CNN；但是如果一直保持同樣大小的activation map開銷也太大，所以要先降維再升維" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>對應降維的pooling層，升維時是unpooling/upsampling，即復原MASK其他的位置都補0。</th><th>對應降維的conv層，升維時還是用filter走stride，只不過把conv的內積變成逐元素乘。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture11-4.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture11-5.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>用矩陣乘表示convolution操作的話，現在的升維convolution就是輸入transpose一下，這裡是stride=1的演示</th><th>stride = 2</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture11-6.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture11-7.png" alt=""></td></tr></tbody></table></div><p>transpose convolution真是個好主意，叫做”transpose”也真的很聰明，但其實也就是玩維度遊戲，剩下的一切交給optimizer罷了。不要叫deconvolution，因為反卷積不是這個操作。我在<a href="#信號處理的卷積計算signal-processing-convolution">上面cnn convolution部分討論原初的signal processing convolution的地方</a>補充了反卷積的概念，以及改掉了一開始chatgpt的錯誤例子。</p><p>用的都是很熟悉的模塊，但是稍加修改立刻就能應用到其他任務上，這super cool也super 爽。我的論文可以學這裡把模型遷移到其他任務的思路，兵來將擋水來土掩，包括diffusion和transformer之外用上面的LSTM機制會看起來很屌。</p><h3 id="Classification-Localization"><a href="#Classification-Localization" class="headerlink" title="Classification + Localization"></a><strong>Classification + Localization</strong></h3><p>這個任務跟目標檢測object detection的區別是，Classification + Localization 分类 + 定位任務事先假設只有 1 個目標或者exact多個目標只生成exact 1 個bounding box邊界框。</p><div class="table-container"><table><thead><tr><th>如果這個multitask loss不是直接加起來，而是用一個weight做weighted sum，那麼就需要一個loss之外的metric衡量performance，因為一般我們調超參都是以loss為指標，換幾個hyper parameters看loss怎麼變化，但這個weighting parameter是會直接影響loss值的</th><th>這個任務可以應用到其他地方，比如Human Pose Estimation人体姿态估计。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture11-2.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture11-8.png" alt=""></td></tr></tbody></table></div><ul><li>把兩條路合成一個可行嗎？未必，這裡只是basic setup。</li><li>那如果不把兩個loss合成一個multitask loss而是分辨訓練兩個FC呢？JJ說在Transfer Learning時Fine Tune Jointly（聯合訓練）永遠效果都好過分開tune，因為features的mismatch，但是也有一個trick是分開訓練完都converge收斂了，再合在一起jointly訓練一下，這樣也是可以的。</li></ul><p>這裡同學的一個問題很好：為什麼這裡JJ說Human Pose Estimation用“regression loss”，指的是什麼？<br>JJ說他指的就是cross-entropy(softmax)或者SVM loss之外的loss，比如L2和L1 loss，如果你希望輸出是離散的（如classification）就用softmax loss（classification loss），如果希望輸出是連續的（比如這裡）那就用L2（regression loss）。</p><p>單獨討論一下回歸問題和分類問題吧。</p><h3 id="回歸vs-分類-Classification-vs-Regression"><a href="#回歸vs-分類-Classification-vs-Regression" class="headerlink" title="回歸vs.分類 (Classification vs. Regression)"></a><strong>回歸vs.分類 (Classification vs. Regression)</strong></h3><p>簡單來說，</p><ul><li>回归是<strong>預測連續值</strong>，使用MSE (Mean Squared Error, 均方误差) 损失函数。</li><li><p>分类是<strong>預測離散值</strong>，把數據分配到離散類別，使用交叉熵损失函数。</p><ul><li>MSE确实可以用于分类任务，但它并没有交叉熵损失那么直观且有效，尤其在处理概率和梯度更新方面。</li><li>交叉熵更适合分类任务，它是专门为概率分布设计的，能够更好地处理类别之间的关系，并且有更稳定和有意义的梯度。</li></ul></li><li><p>假设我们有一个二分类问题，类别0和类别1。</p></li><li>模型的输出为0.7，表示模型认为它属于类别1的概率为0.7。</li><li>真实标签是类别0（即标签为0）。</li></ul><p>$\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2$</p><p>MSE = (0 - 0.7)² = 0.49</p><p>$\text{Cross-Entropy Loss} = -\left( y \log(\hat{y}) + (1 - y) \log(1 - \hat{y}) \right) = -(0 \cdot \log(0.7) + (1 - 0) \cdot \log(1 - 0.7)) = - \log(0.3) \approx 1.204 $</p><p>可以發現，</p><ol><li>交叉熵的loss更大，即對錯誤的懲罰更大，錯得越離譜用log就大得越多；</li><li>還是因為這個對數，對數曲線是越接近0時越陡，根據上面的公式模型預測越錯誤log的自變量就越接近0，梯度就越大，模型更新得就越快。</li></ol><p>反過來看回歸的例子：</p><ul><li>假如我們在預測房價，一個數據的真實房價是3.5，預測值是3。</li></ul><p>$\text{MSE} = (3.5 - 3.0)^2 = (0.5)^2 = 0.25$</p><p>但是用交叉熵根本沒法處理，因為這裡沒有類別，假如想強行使用交叉熵得發揮想像力，比如把連續值離散化為類別，房價離散化為區間：</p><ul><li>类别1：( [3.0, 3.5) )</li><li>类别2：( [3.5, 4.0) )</li></ul><p>那 $y = 類別2$，$\hat{y} = 類別1$，而且這裡錯誤類別的預測值也得假設，<br>這裡預測值是3.0，就得假設 $\hat{y}_{類別1} = 0.99$，假設 $\hat{y}_{類別2} = 0.01$ 這樣。</p><p>雖然能算，但是幾乎沒法算。</p><p>結論：用同一個模型可能既可以處理regression又可以處理classification，但是需要構造不同數據 且 需要改loss function。</p><h3 id="Object-Detection"><a href="#Object-Detection" class="headerlink" title="Object Detection"></a><strong>Object Detection</strong></h3><p>這個領域的歷史和研究足夠講一個學期，是非常meaty的問題。</p><p>任務描述：</p><ol><li>同樣是some fixed set of categories；</li><li>given image：每次上述categories裡的一個出現在image裡的時候， 我們希望得到一個bounding box和它的類別label。</li></ol><p>跟Classification + Localization不同的是每個image都可能有不同數量的outputs。上課時的數據集多年來相對常用的是PASCAL VOC，但因為15年以後mAP一直80%以上，很多人因為這個數據集的任務太簡單而轉用其他數據集了。</p><div class="table-container"><table><thead><tr><th>把目標檢測想成是regression回歸任務有些麻煩（輸出連續值坐標），</th><th>classification比較好（離散值），用熟悉的滑動窗口sliding window，但問題是需要滑動的位置太多了！</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture11-9.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture11-10.png" alt=""></td></tr></tbody></table></div><p>這裡的類別在categories之外會加入一個additional category: background。</p><div class="table-container"><table><thead><tr><th>針對sliding window滑動位置太多的問題，自然想到事先選定一些窗口，即Region Proposals。左下角是一些region proposal算法(Selective Search/EdgeBoxes)，都是在CPU上完成的。</th><th>基於這些算法，優化版的sliding window出現：R-CNN</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture11-11.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture11-12.png" alt=""></td></tr></tbody></table></div><p>R-代表region，region-based cnn。</p><ul><li>也是個multi-task loss，svm判斷label，然後會有個Bbox reg的correction項，把region proposals出來的bounding box再修正一點，svm預測的是離散標籤所以是classification，預測的Bbox reg是連續值所以是regression。</li><li>這裡提取的RoI的aspect ratio寬高比？大小如何影響結果？JJ不確定相關實驗結果。</li><li>RoI可以是非rectangles嗎？說是在其他任務比如instance segmentation裡region proposals可能不是矩形。</li><li>R-CNN裡region proposals不是learned的，是固定的傳統的算法，但後面會有基於這個改動的算法——Fast和Faster用另一個CNN(RPN)學到。</li><li>RoI裡沒有objects的也是都會輸出background。</li></ul><p>這類數據的獲得構建也有論文，比如如果只有一些圖片有bounding box數據？如果數據有噪聲（like誤定位誤分類？），這些問題我的論文也可能遇到。</p><p>這部分寫作業不知道2017年和2024年哪邊有，最好還是實現一下，看過很多次了包括<a href="https://zh.d2l.ai/chapter_computer-vision/index.html#">前些年看李沐老闆上課時讓實現這塊兒讓我負責講這塊兒slide</a>到現在還是不懂。</p><p>實在是太慢了，訓練在cpu上，要花 80 多小時，要很多disk，而預測一個圖片要花將近 1 分鐘。</p><h4 id="Fast-Faster-R-CNN"><a href="#Fast-Faster-R-CNN" class="headerlink" title="Fast/Faster R-CNN"></a><strong>Fast/Faster R-CNN</strong></h4><div class="table-container"><table><thead><tr><th>Fast R-CNN裡region proposals的做法是，不直接對圖片取region，而是對CNN處理後的feature map取region。</th><th>訓練仍然是multi-task loss</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture11-13.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture11-14.png" alt=""></td></tr></tbody></table></div><div align=center><img src="/pics/cs231n/cs231n_2017_lecture11-15.png" loading="lazy" alt="Faster R-CNN" width="50%" height="50%"></div><ul><li>讓bottleneck的region proposals直接由RPN網絡提供。不再被這一步算法限制速度（Fast R-CNN雖然是在feature map上提取但還是慢）</li><li>Fast R-CNN和Faster 都有一步 RoI Pooling 層，vanilla R-CNN是手工裁剪 region 為相同大小 RoI 給 CNN，不是 Pooling。</li></ul><p>JJ說是文章裡有提到，如果region proposals的network跟classification的network分開呢？實驗結果是區別不大所以選擇計算開銷低的只用 1 個sharing network。<br>還有個同學問到了關鍵問題——怎麼設計的region proposals這部分的loss？JJ說基本思路是看region跟ground truth bounding box的重合率，重合高過threshold就positive反之negative，但是這些都是黑魔法超參數很hairy。loss是個二分類的loss，會在模型生成的一堆regions裡面分類。。</p><p>或者不分成兩個階段（region proposal + classification），而是<strong>一個模型一步到位</strong>。</p><h4 id="YOLO-SSD"><a href="#YOLO-SSD" class="headerlink" title="YOLO/SSD"></a><strong>YOLO/SSD</strong></h4><div align=center><img src="/pics/cs231n/cs231n_2017_lecture11-16.png" loading="lazy" alt="" width="50%" height="50%"></div><p>沒有region proposals，首先把input分割成 7 x 7 grid cells，每個 grid cell 以中心為 anchor 創造 B (&gt;=3) 個 Base Boxes，對每個 base boxes直接預測 (dx, dy, dh, dw, confidence)，和類別分數。</p><p>Base Box（基础框）Anchor Boxes（锚框）<br>grid cell實踐裡一般會超過3個，confidence是bounding box裡存在object的confidence。</p><h3 id="Instance-Segmentation"><a href="#Instance-Segmentation" class="headerlink" title="Instance Segmentation"></a><strong>Instance Segmentation</strong></h3><p>任務描述：類似object detection + semantic segmentation，不只想要bounding box，需要objects的segmentation mask。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture11-17.png" loading="lazy" alt="MASK R-CNN" width="50%" height="50%"></div><p>用MSCOCO (200,000 images, 80 categories, 5 or 6 instances per img)。</p><h2 id="Lecture-12"><a href="#Lecture-12" class="headerlink" title="Lecture 12"></a><strong>Lecture 12</strong></h2><p>整個visualizing和interpretation都是不希望neural nets對人類只是數字很好看的黑箱，不能解釋如何做出的classification decision就似乎無法被信任，不像隨便什麼linear regression、random forest、decision tree都能直觀理解weights而被解釋。</p><h3 id="Weights-Visualization"><a href="#Weights-Visualization" class="headerlink" title="Weights Visualization"></a><strong>Weights Visualization</strong></h3><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12.png" loading="lazy" alt="" ></div></td><td><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-1.png" loading="lazy" alt="" ></div></td></tr></tbody></table></div><p>這些Visualized Filters是彩色因為都是first layer的，數據是3 channel的第一層conv filter當然是3 channel的。N, C, H, W或者F, C, H, W四個維度，cnn那節或者在上面套路<a href="#array改變形狀">改變array形狀</a>時實現過的。</p><h3 id="Last-Layer-Visualization"><a href="#Last-Layer-Visualization" class="headerlink" title="Last Layer Visualization"></a><strong>Last Layer Visualization</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-2.png" loading="lazy" alt="" width="50%" height="50%"></div><p>尋找最後的特徵向量的nearest neighbours，發現確實學到了一些語義特徵，比如在圖像最左側的大象和最右側的大象是nearest neighbour（對原images用L2或者L1算KNN是不太會得到這麼好的結果的），在普通的supervised learning裡比如cross entropy loss並沒有要求這些feature相近，這只是個happy的發現，但是contrastive loss和triplet loss是顯式地要求同類features相近。看到這兒終於想起來三年前的種種往事，下回了久違的keynote，看了第一次組會拖了一個月的slides，老闆</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-3.png" loading="lazy" alt="" width="50%" height="50%"></div><p>dimensionality reduction也可以visualize what’s going on in the last layer，用PCA把高維數據降為2維就能可視化了。t-SNE (t-distributed stochastic neighbour embeddings) 是個更好的非線性算法，這裡直接聚類了m-nest dataset的數據，也可以聚類最後一維的features，這裡的做法是用t-SNE處理這個4096-d的feature得到一個x,y坐標，然後用feature的原圖代入進去。</p><h3 id="Activations-Visualization"><a href="#Activations-Visualization" class="headerlink" title="Activations Visualization"></a><strong>Activations Visualization</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-4.png" loading="lazy" alt="" width="50%" height="50%"></div><p>visualize weights可能不是很interpretable，但是activation map有可能比較interpretable。黑的是沒有active的激活函數，（不是dead relu，dead relu是說整個訓練都接收負值的）這裡所謂的activation map是每個conv filter輸出的大的3維數據的activation volume的一個切面(1, H, W)（我剛意識到輸出不是四維的嗎？喔N是數據量，展示的只是1個數據的模型）</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-5.png" loading="lazy" alt="" width="50%" height="50%"></div><p>activating patches也是個思路，這裡每一行都是conv5的激活層的某一個neuron？這裡的neuron就是比如relu的輸出值scalar。還是某一個channel的neurons？因為一個channel的neuron都是由同一個weight的filter產生的，比如第一行的這個neuron可能在找淺色包裹的深色圓形物體。因為深層的一個像素receptive field比較大所以能相較edge檢測更大的物體。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-6.png" loading="lazy" alt="" width="50%" height="50%"></div><p>occlusion這個實驗思路是，用一個occluded patch (用image mean遮擋的區域)滑過圖片每個區域，在每個地方用模型生成一個目標分類的probability，比如elephant，把滑過每個地方的分類到elephant的概率合起來畫成一張heatmap熱力圖，基本思路是如果遮擋住某個部分讓分類概率大幅變化，那麼這個部分就對分類決定很重要。really import to classification decision</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>saliency map在2017年的assignment3裡，思路是直接對輸入的每個pixel計算gradient，其實一般我們不會算第一個dx，居然發現效果很不錯，同學提問JJ說確實可以用來做semantic segmentation problem。</p><p>guided back propagation能更好體現哪些真的在影響neuron的scores，JJ說想看細節得去讀paper。（跳過這個idea）</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>到目前為止都是給網絡固定的圖片，求解哪一部分哪個neuron最感興趣，但是也可以求解in general，使用gradient ascent，fix the weights, synthesize the image to try to maximize the scores of intermediate neurons or classees。</p><p>這裡也要加regularization，可能只是生成的圖片的L2 norm，不加reg的話可能看起來像random noise？然後初始化一個image，喂給訓練好的模型，計算感興趣的class/neuron wrt image的梯度，然後往梯度方向(argmax)而不是梯度反方向(argmin)改變圖像，生成的圖片很有意思。顏色很亂是因為可視化很tricky，因為我們要把gradient ascent的結果限制在0～255之間。</p><p>Fooling Image/Adversial就可以基於這個做法，把初始化圖片換成一個隨機圖片比如elephant，然後選擇一個隨機類別比如koala bear，然後用這個gradient ascent讓image maximize the class，最後可以生成人眼看起來一樣（居然沒變，difference也基本看上去只是noise）但是會分類為koala bear的圖片，即欺騙圖像。最後一節課lecture 16 Ian Goodfellow (這哥們兒姓“好人”？)會細講，這個也在作業裡。</p><p>DeepDream這個公式怎麼來的，56分JJ簡單算了一下沒聽懂。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture12-9.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Feature Inversion用到了Total Variation regularizer(encourages spatial smoothness)，用cnn的越深層relu生成的圖片越抽象可能說明模型越希望保留更多語義信息？也是2017 ass3的作業，2024大概沒有，所以估計要把2017的Q3拿來做一做。</p><p>Style Transfer裡Texture Synthesis材質生成是個很老的CG問題，用一小塊圖片生成一大塊材質，傳統方法比如可以用nearest neighbour copy材質圖片。</p><p>後面十分鐘講Style Transfer和好看的圖片的內容就沒聽了，就聽到這裡outer product算gram matrix，聽不懂但是應該暫時不做2017年ass3的Q4，所以先不看，等做完2024年的ass3再做。</p><h2 id="Lecture-13"><a href="#Lecture-13" class="headerlink" title="Lecture 13"></a><strong>Lecture 13</strong></h2><ul><li>Unsupervised Learning</li><li>Generative Models<ul><li>PixeIRNN and PixelCNN</li><li>Variational Autoencoders (VAE)</li><li>Generative Adversarial Networks (GAN)</li></ul></li></ul><h3 id="Unsupervised-Learning"><a href="#Unsupervised-Learning" class="headerlink" title="Unsupervised Learning"></a><strong>Unsupervised Learning</strong></h3><div align=center><img src="/pics/cs231n/lecture_13.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Generative-Models"><a href="#Generative-Models" class="headerlink" title="Generative Models"></a><strong>Generative Models</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13.png" loading="lazy" alt="" width="50%" height="50%"></div><p>生成模型的目標就是通過 $p_data$ 生成盡可能与 $p_data$ 相似的 $p_model$。即解決density estimation問題（無監督學習核心問題）。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Density Estimation 密度估計，指的是通过已知数据点来估计一个数据分布的<a href="https://blog.v2beach.cn/2024/11/05/Elo-Rating-System和Probability-Theory-Basics/#Probability-Density-Function">概率密度函数（PDF）</a>。换句话说，密度估计旨在描述一个未知的分布，通常是通过从有限的观测数据中估计一个连续变量的分布形态。</p><h3 id="PixelRNN-CNN"><a href="#PixelRNN-CNN" class="headerlink" title="PixelRNN/CNN"></a><strong>PixelRNN/CNN</strong></h3><p>tractable Density (容易獲得的？)</p><div align=center><img src="/pics/cs231n/lecture_13-1.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="/pics/cs231n/lecture_13-2.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13-2.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13-3.png" loading="lazy" alt="" width="50%" height="50%"></div><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13-4.png" loading="lazy" alt="" width="50%" height="50%"></div><p>回顧<a href="/2024/11/05/Elo-Rating-System和Probability-Theory-Basics/#似然likelihood">似然likelihood</a>，無監督學習中，尤其是generative models 生成模型們直接通过最大化似然来进行训练。</p><p>算法的label是training data本身，就是後續的像素，目標是最大化“訓練數據被生成”的似然。</p><h3 id="Auto-Encoders"><a href="#Auto-Encoders" class="headerlink" title="Auto-Encoders"></a><strong>Auto-Encoders</strong></h3><div class="table-container"><table><thead><tr><th>需要一個latent space z，而且因為我們希望z是最meaningful的features，z的維度要小於x也就是要先做dimensionality reduction。encoder和decoder可以用各種網絡構造</th><th>然後就出現了曾經我最熟悉的reconstruction loss也就是training data跟重建的自己做L2 loss，AE是通過重建自己的training data來學習z的。（另外熟悉的就是contrastive loss）</th><th>我們可以利用學習到的 z 隱藏空間，在新的任務上 finetune</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture13-5.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture13-6.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture13-7.png" alt=""></td></tr></tbody></table></div><p><strong>AE自编码器可以重建数据，并且能够学习特征以初始化监督学习模型。</strong><br><strong>但我们无法从自编码器生成新图像，因为我们不知道 $z$ 的空间，只是知道怎麼把 x 轉化到 z 空間。</strong></p><p><strong>VAE能让自编码器成为一个生成模型。</strong>VAE 會讓 z 輸出一個 $\mu$ 和 $\sigma$ 確定一個近似的正態分佈，然後可以在這個正態分佈裡採樣新樣本。</p><h3 id="Variational-Auto-Encoders"><a href="#Variational-Auto-Encoders" class="headerlink" title="Variational Auto-Encoders"></a><strong>Variational Auto-Encoders</strong></h3><p>approximate Density</p><p>PixelCNN 定義的是 tractable 的公式，likelihood 裡的參數都是易得的：</p><script type="math/tex; mode=display">p_\theta(x)=\prod_{i=1}^n p_\theta\left(x_i \mid x_1, \ldots, x_{i-1}\right)</script><p>VAEs 是定義了一個依賴於 latent z 的不能直接優化的公式，只是推導下面這個 likelihood 的 lower bound，然後轉而優化這個下界：</p><script type="math/tex; mode=display">p_\theta(x)=\int p_\theta(z) p_\theta(x \mid z) d z</script><p>雖然在<a href="/2024/11/05/Elo-Rating-System和Probability-Theory-Basics/#似然likelihood">Elo裡</a>詳細又學了一遍，但 ELBO (evidence lower bound) 或 variational lower bound 還是很難理解。</p><div align=center><img src="/pics/cs231n/fig4.svg" loading="lazy" alt="" width="50%" height="50%"></div><p><a href="https://yunfanj.com/blog/2021/01/11/ELBO.html">https://yunfanj.com/blog/2021/01/11/ELBO.html</a> 這篇文章講得非常細緻，$p_\theta(x \mid z)$ 就是上面的 w (weights)，核心思路是用多個Gaussian（或其他分佈）逼近目標分布。</p><p>总之先记住这个结论吧：</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture13-16.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture13-17.png" alt=""></td></tr></tbody></table></div><p>AE和VAE區別，KL散度作為正則</p><h3 id="Generative-Adversarial-Networks"><a href="#Generative-Adversarial-Networks" class="headerlink" title="Generative Adversarial Networks"></a><strong>Generative Adversarial Networks</strong></h3><p>生成對抗網路。</p><p>總之生成任務都是試圖在高維的training data這個分布裡sample採樣，採樣出一些新的samples，這個分布是沒法直接得到的，AE/VAE是學習一個latent space z，通過“變分推斷”近似数据的真实分布。GAN是先從簡單分布比如Gaussian採樣，然後目標是變化這個分布(transformation)得到這個複雜分布。用Neural Network來學習這個“transformation”。VAE的neural network是被用來表示ELBO lowerbound的兩個似然？開始的思路是啥？其實這個學transformation的過程跟VAE變化prior的做法很像吧？</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>沃草，就是guest lecture 16 的lecturer Ian Goodfellow發的NIPS 2014 Generative Adversarial Nets。（他已經30w+引用了，guest lecture 15講硬件/加速的韓松也是個神人，6w+引用）<br>理解不能了，看到這裡還是一個似然，還是找幾個例子先想明白似然貝葉斯和nn的關係吧。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture13-10.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture13-9.png" alt=""></td></tr></tbody></table></div><p>main idea是，我們訓練一個可以區分真假圖片的discriminator，如果generator可以生成出騙過discriminator的圖片，那麼可以說明generative model就已經足夠好了，這倆網絡是train jointly用一個minimax objective function。</p><p>GAN裡raining data的“real images”的label都是1，而generator生成的fake images的label都是0（unsupervised）。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture13-11.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture13-12.png" alt=""></td></tr></tbody></table></div><p>Again，gradient ascent和gradient descent的唯一區別就是求出來梯度之後，前者加梯度後者減梯度。</p><p>但是看右下角的圖，$D(G)$ 越接近1（discriminator越把fake圖當成真的）即generator生成得越好的時候，$log(1-x)$ 曲線是越陡的，也就是說梯度是更大的，參數的變化是更快的，這是我們不希望的，我們希望 $D(G)$ 越接近0的時候參數更新越快，很巧妙的是可以把objective改成-log(x)（注意不是log(-x)，<a href="https://www.desmos.com/calculator?lang=zh-TW">這倆曲線前者是一四象限後者是二三象限</a>）就有了這種好的 $D(G)$ 接近 0 時陡 $D(G)$ 接近 1 時緩的曲線，即gradient decent $-log(D(G))$，等價於gradient ascent $log(D(G))$。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture13-13.png" loading="lazy" alt="" width="50%" height="50%"></div><p>訓練算法是，每個training iteration都先訓練k次discriminator，然後再訓練generator。這裡的k設成1還是大於1，直到2024年的slide還是跟2017年一樣，作為一個現調的超參沒人在乎了大概。</p><p>訓完就把discriminator丟掉用generator生成。這裡2014年Ian Goodfellow訓GAN的neural nets都是FC來的，但後來2016年Alex Radford用CNN訓了一套，效果變好了很多。</p><p>在早期GAN裡，是完全隨機地生成樣本，後來才有conditional cGAN允許用類別標籤、圖像、文本描述控制生成。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture13-14.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture13-15.png" alt=""></td></tr></tbody></table></div><p>在latent space裡对隨機的z插值會有平滑的變化，是個有趣的實驗。對z做各種有趣的事看到圖像的合理變化能說明z有一定的可解釋性。</p><p>一個有趣的2-player IDEA，一個上口的名字 GAN，效果好就是大家爭相發展的硬道理。新的工作其實也只是修改一點兒loss function之類的工作，assignment 3裡2017和2024一直都有GAN（和RNN/LSTM）。</p><p>VAE相比GAN有inference queries：<br>大概就是VAE有個中間階段，encoder會生成一個latent space的mean和variance顯式得到分布，“decoder”採樣的過程就是明確的推理過程，可以比較方便地操作潛在變量；<br>而GAN沒有顯式推理過程，只用generator通過隨機噪聲生成樣本。</p><p>ass裡的兩個loss：BCE就是Binary Cross-Entropy Loss二分類交叉熵和Least Squares Loss (最小二乘损失)，<a href="https://drive.google.com/file/d/1eS4SoGy5vWK0I42xYeCyuTmQFW-2u7oi/view?usp=share_link">GAN原來同時可以用回歸和分類的損失。</a></p><h2 id="Lecture-14"><a href="#Lecture-14" class="headerlink" title="Lecture 14"></a><strong>Lecture 14</strong></h2><div align=center><img src="/pics/cs231n/cs231n_2017_lecture14-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>強化學習原來也是馬爾科夫過程，死去的LDA記憶又開始復蘇。</p><ul><li>What is Reinforcement Learning?</li><li>Markov Decision Processes</li><li>Q-Learning</li><li>Policy Gradients</li></ul><h3 id="What-is-Reinforcement-Learning"><a href="#What-is-Reinforcement-Learning" class="headerlink" title="What is Reinforcement Learning?"></a><strong>What is Reinforcement Learning?</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture14.png" loading="lazy" alt="" width="50%" height="50%"></div><p>整個流程：</p><ul><li>Environment給Agent一個state</li><li>loop:<ul><li>Agent給Environment一個action</li><li>Environment給Agent一個reward和next state</li></ul></li><li>until Environment給Agent一個terminal state</li></ul><p>cases:</p><ul><li>Cart-Pole Problem（推车摆问题）是一个经典的控制问题，通常用于强化学习（Reinforcement Learning, RL）和机器人学的研究中。这个问题描述了如何控制一个安装在推车上的摆杆（或倒立摆）保持直立的状态。它是一个经典的物理控制问题，具有挑战性，特别是在处理实时控制和系统稳定性时。</li><li>Robot Locomotion（机器人运动学）優化運動方式、機器人穩定性。</li><li>Games，比如 Go 圍棋、比如最早的遊戲公司 Atari。</li></ul><h3 id="Markov-Decision-Processes"><a href="#Markov-Decision-Processes" class="headerlink" title="Markov Decision Processes"></a><strong>Markov Decision Processes</strong></h3><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture14-2.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture14-3.png" alt=""></td></tr></tbody></table></div><p>A Simple MDP: Grid World這個例子裡，reward被設為negative，就意味著到達終點越久cumulative reward累積獎勵就越小，policy pi的optimal應該是maximize the sum of rewards。</p><p><strong>马尔可夫决策过程（MDP）</strong>是一个数学模型，又叫随机动态规划 (stochastic dynamic program), 通过五个元素 (或者4-tuple只有前四个)（状态、动作、转移概率、奖励函数、策略 / 折扣因子）M = (S, A, P, R, π / γ ) 描述智能体在环境中如何做出决策，以最大化长期奖励，其数学形式最早在 1950s 由 Richard Bellman 提出的动态规划理论奠定，并在 ecology, economics, healthcare, telecommunications and reinforcement learning 中广泛应用。</p><h3 id="Q-Learning"><a href="#Q-Learning" class="headerlink" title="Q-Learning"></a><strong>Q-Learning</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture14-4.png" loading="lazy" alt="" width="50%" height="50%"></div><ol><li><p><strong>Value Function（价值函数）</strong></p><p> Value Function $V(s)$ 用于表示在状态 $s$ 下，智能体能期望获得的累积奖励。它通常定义为从某一状态出发，按照某个策略 $ \pi $ 行动所能获得的期望回报。</p><script type="math/tex; mode=display"> V(s) = \mathbb{E} \left[ \sum_{t=0}^{\infty} \gamma^t r_t \mid s_0 = s \right]</script><p> <strong>其中：</strong></p><ul><li>$V(s)$: 在状态 $s$ 下的价值函数，表示从状态 $s$ 开始，按照某策略最大化累积奖励的期望值。</li><li>$\mathbb{E}$: 期望操作符，表示对未来奖励进行期望计算。</li><li>$\sum_{t=0}^{\infty}$: 求和符号，表示从时间步 $t=0$ 到无穷远的求和。</li><li>$\gamma$: 折扣因子，$0 \leq \gamma &lt; 1$，用于降低未来奖励的重要性。</li><li>$r_t$: 时间步 $t$ 时的奖励。</li><li>$s_0$: 初始状态，表示从状态 $s$ 开始进行决策。</li></ul></li><li><p><strong>Q-Value Function（Q值函数）</strong></p><p> Q值函数 $Q(s, a)$ 用于表示在某个状态 $s$ 下，采取某个动作 $a$ 后能获得的期望累积奖励。Q值函数是强化学习中非常关键的函数，常用于评估特定的动作选择。</p><script type="math/tex; mode=display"> Q(s, a) = \mathbb{E} \left[ \sum_{t=0}^{\infty} \gamma^t r_t \mid s_0 = s, a_0 = a \right]</script><p> <strong>其中：</strong></p><ul><li>$Q(s, a)$: 在状态 $s$ 下，采取动作 $a$ 后的 Q 值，表示从状态 $s$ 出发并采取动作 $a$ 后能期望获得的累积奖励。</li><li>$\mathbb{E}$: 期望操作符，表示对未来奖励进行期望计算。</li><li>$\sum_{t=0}^{\infty}$: 求和符号，表示从时间步 $t=0$ 到无穷远的求和。</li><li>$\gamma$: 折扣因子，$0 \leq \gamma &lt; 1$，用于降低未来奖励的重要性。</li><li>$r_t$: 时间步 $t$ 时的奖励。</li><li>$s_0$: 初始状态，表示从状态 $s$ 开始进行决策。</li><li>$a_0$: 初始动作，表示在状态 $s$ 下采取的动作。</li></ul></li></ol><div class="table-container"><table><thead><tr><th>Bellman equation 是 loss function</th><th>用 nn 作為函數逼近來估計所有 state-action pair 的 Q(s,a)</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture14-5.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture14-6.png" alt=""></td></tr></tbody></table></div><p>但是這樣一個個<strong>試錯</strong>的做法not scalable，面對大規模數據沒有擴展性，計算開銷太大，<br>而一旦出現一個很複雜的function比如Q(s,a)就是這樣一個function，我們想估計，就可以用neural nets作為函數逼近器（function approximator）來用數值方法逼近這個函數。</p><p>$Q(s,a;\theta) ～ Q^*(s, a)$</p><p>網絡的輸入就是 s 和 a，輸出就是Q(s, a)，思路很直接，但是就像2019年亞太杯一樣，問題是：</p><ol><li>loss function</li><li>model structure</li><li>實現後找問題</li></ol><p>這裡倆問題，1.reward怎麼來？人為設定的，比如Atari的打磚塊遊戲breakout，打掉一個磚塊+1，球掉了-1，或者可以設置成0；2.貼過來pseudo代碼？跟上面公式不太一樣？ </p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture14-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Input: state $s_t$<br>訓一個atari像google那樣？為什麼nn能有這麼高效率算出來Q？</p><h2 id="傳統-Q-learning"><a href="#傳統-Q-learning" class="headerlink" title="傳統 Q-learning"></a><strong>傳統 Q-learning</strong></h2><ul><li><p><strong>Q-Learning 更新公式</strong></p><p>  Q-learning 是一种无模型的强化学习算法，即Agent不需要知道环境的具体模型（即状态转移概率state transition probability和奖励函数reward function），只通过与环境的交互来逐渐学习最优策略。通过与环境的交互，不断更新 Q-值函数。其核心更新公式如下：</p><script type="math/tex; mode=display">  Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ r_{t+1} + \gamma \max_a Q(s_{t+1}, a) - Q(s_t, a_t) \right]</script><p>  <strong>其中：</strong></p><ul><li>$Q(s_t, a_t)$: 当前时刻 $t$，在状态 $s_t$ 下采取动作 $a_t$ 的 Q 值，表示预期的累积奖励。</li><li>$\alpha$: 学习率，决定了每次更新的幅度，$0 \leq \alpha \leq 1$。</li><li>$r_{t+1}$: 在时刻 $t+1$ 获取的奖励。</li><li>$\gamma$: 折扣因子，$0 \leq \gamma &lt; 1$，用于降低未来奖励的重要性。</li><li>$\max_a Q(s_{t+1}, a)$: 在下一个状态 $s_{t+1}$ 中，所有可能的动作 $a$ 中，具有最大 Q 值的动作对应的 Q 值。<ul><li>a是“最大Q值的動作”。</li></ul></li><li>$s_t$: 当前时刻 $t$ 的状态。</li><li>$a_t$: 当前时刻 $t$ 选择的动作。</li></ul></li></ul><p>在訓練階段是有時隨機地隨意地、有時用maxQ轉移狀態探索環境來更新Q矩陣，<br>在決策階段就用Q值貪心地選擇最優策略 $a^*(s) = \arg\max_a Q(s, a)$。</p><p>直觀地理解Q-Learning更新公式的Intuition：</p><ul><li>$Q(s_t, a_t)$ 表示的是從 $s_t$ 這個狀態通過 $a_t$ 這個行動轉移到下個狀態的價值、回報；</li><li>因為是在學習，首先更新這個值就要加上原來的學習經驗 $Q(s_t, a_t)$（沒有經驗初始化為0）；</li><li>$\alpha$ 就相當於學習率，不用管；</li><li>$\left[ r_{t+1} + \gamma \max_a Q(s_{t+1}, a) - Q(s_t, a_t) \right]$ 是更新的部分，包含 $r_{t+1}$、$\gamma \max_a Q(s_{t+1}, a)$、$- Q(s_t, a_t)$；</li><li>$r_{t+1}$ <strong>即時獎勵</strong>是 $Q(s_t, a_t)$ 轉移狀態探測環境後實際獲得的獎勵，這是環境給的；</li><li>$\max_a Q(s_{t+1}, a)$ 是<strong>未來獎勵的預期</strong>，這一步的價值不光包括立刻獲得的獎勵，還要往下一步看看最大能獲得多少獎勵，即多看一步的貪心算法，$\gamma$ 是給未來預期打個折扣；</li><li>$- Q(s_t, a_t)$ 是為了避免過度更新減掉的，因為 $r_{t+1} + \gamma \max_a Q(s_{t+1}, a)$ 是实际获得的即时奖励 + 未来可能的回报，加太多不減點兒會導致Q值起飛數值不穩定。</li></ul><p><strong>更新公式的核心是时序差分（Temporal Difference, TD）方法，實際是蒙特卡洛方法和動態規劃思想的結合。</strong>又叫Bellman Error又叫Temporal Difference，有空再研究推導，Serena這裡公式一筆帶過了算是課外內容。</p><p>傳統 Q-learning 是維護一個 Q(s, a) 表（所謂時序差分裡面動態規劃思想的部分），用 $Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ r_{t+1} + \gamma \max_a Q(s_{t+1}, a) - Q(s_t, a_t) \right]$ 公式更新表（所謂時序差分裡面蒙特卡洛思想的部分）。</p><p>而 DQN 是輸入 s 直接由 nn 回歸得到 Q 值。</p><div align=center><img src="/pics/cs231n/1714758048691.png" loading="lazy" alt="" width="50%" height="50%"></div><p>↑ 傳統 vs. Deep ↑</p><h2 id="DQN-Deep-Q-Learning"><a href="#DQN-Deep-Q-Learning" class="headerlink" title="DQN, Deep Q-Learning"></a><strong>DQN, Deep Q-Learning</strong></h2><p>輸入：環境 Environment 的狀態 State $s_t$ ，可以是一個向量（比如迷宮用FC）、圖像（比如Atari games用Conv）或其他特徵。</p><p>輸出：一個向量 vector，表示所有可能動作 Action 對應的 Q-value。</p><p>網絡架構跟其他任務沒什麼兩樣。相當於把填表工作交給數值逼近高手神經網絡。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture14-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Loss function: $L_i\left(\theta_i\right)=\mathbb{E}_{s, a \sim \rho(\cdot)}\left[\left(y_i-Q\left(s, a ; \theta_i\right)\right)^2\right]$ where $\quad y_i=\mathbb{E}_{s^{\prime} \sim \mathcal{E}}\left[r+\gamma \max _{a^{\prime}} Q\left(s^{\prime}, a^{\prime} ; \theta_{i-1}\right) \mid s, a\right]$</p><ol><li>這個 loss 是 Bellman 誤差 (Bellman error) 最小化，最小化當前 Q 函數 $Q(s, a; \theta_i)$ 和目標值 $y_i$ 。</li><li>$y_i$ 代表的是當前 Q 函數即 $Q(…; \theta_{i-1})$ 在下一個狀態 $s’$ 中最理想的 rewards 最大的可能性；<ul><li>可以發現 loss 裡的 $y_i$ 基本就是更新公式 $Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ r_{t+1} + \gamma \max_a Q(s_{t+1}, a) - Q(s_t, a_t) \right]$；</li><li>這個是理想值，用一個神經網絡縮小它跟 $Q(s, a; \theta_i)$ 的差距以學到更好的 Q(s, a)；</li><li>$y_i$ 是不斷改變隨 Q(s, a) 更新的！</li></ul></li><li>$Q(s, a; \theta_i)$ 和 $Q(…; \theta_{i-1})$ 都是由神經網絡預測。</li></ol><h3 id="Policy"><a href="#Policy" class="headerlink" title="Policy"></a><strong>Policy</strong></h3><p>這個 loss 公式裡有兩個沒說明的點，</p><ol><li>$\rho(\cdot)$ 是個分布函數，是個行為策略 (Behavior Policy，通常寫作 $\rho$ )，所謂行為策略就是訓練時用的策略，是被優化的目標，最終目標叫做目標策略 (Target Policy，通常寫作 $\pi$ )，行為策略和目標策略又可以分為以下幾種：<ul><li>策略 policy 就是<strong>在某state下應該執行哪個action</strong>，比如在一個 action 只有上下左右的迷宮裡用均勻分布隨機策略，$\rho(s, a_1) = 0.25, \quad \rho(s, a_2) = 0.25, \quad \rho(s, a_3) = 0.25, \quad \rho(s, a_4) = 0.25$ 或 $\pi(s, a_1) = 0.25, \quad \pi(s, a_2) = 0.25, \quad \pi(s, a_3) = 0.25, \quad \pi(s, a_4) = 0.25$，每種。</li><li><strong>确定性策略（Deterministic Policy）</strong>：s下就執行a，$\pi(s) = a$，一個狀態就對應一個行為；</li><li><strong>随机策略（Stochastic Policy）</strong>：策略給一個概率分佈，$\pi(a \mid s) = P(a \mid s)$ 狀態s下執行動作a0的概率是0.8，執行動作a1的概率是0.2。</li><li><strong>貪婪策略 (Greedy Policy)</strong>：$ \pi_{\text{greedy}}(a|s)$ 代表狀態 s 下選擇動作 a 的概率，即只會選擇讓 Q(s, a) 最大的 a’。<script type="math/tex; mode=display">  \pi_{\text{greedy}}(a|s) =   \begin{cases}   1 & \text{if } a = \arg\max_{a'} Q(s, a) \\  0 & \text{otherwise}  \end{cases}</script></li><li><strong>ε-贪婪策略（ε-greedy Policy）</strong>：假設 ϵ 設置為 10%，意味著有 10% 時間裡會進行探索。<script type="math/tex; mode=display">  \pi_{\epsilon\text{-greedy}}(a|s) =   \begin{cases}   1 - \epsilon & \text{if } a = \arg\max_{a'} Q(s, a) \\  \frac{\epsilon}{|\mathcal{A}|} & \text{if } a \neq \arg\max_{a'} Q(s, a)  \end{cases}</script>  意思是有 90% 機率選擇讓 Q(s, a) 最大的 a’，剩下的所有動作 a 都有均等概率被執行。</li><li>注意⚠️，上<strong>面公式裡的 $\pi$ 都可以改成 $\rho$</strong>，也就是說這些策略既可以作為訓練用，又可以用來實際推理用；</li><li>如果一個算法裡 $\pi$ 和 $\rho$ 選擇的策略一樣，比如都是貪婪策略，這種就叫 on-policy 強化學習；</li><li>如果 $\pi$ 和 $\rho$ 不一樣，比如 Q-learning，訓練時用ε-贪婪策略保持探索性，推理時用貪婪策略每一步都選收益 Q(s, a) 最大的 a，這就叫 off-policy 強化學習。</li><li>最後，↓ <strong>經驗回放（Experience Replay）</strong> ↓，壓軸策略。</li></ul></li><li>$\mathcal{E}$ 是經驗回放策略維護的 <strong>經驗回放池（Replay Buffer）</strong>，是 Agent 和 Environment 交互的所有經驗，這麼講有點抽象，其實就是<strong>統計</strong>每個 state 選了什麼 action 對應了什麼 reward，舉個例子：<ul><li>假設現在狀態空間是 5x5 的網格，Agent 從 (0, 0) 開始移動，終點是 (4, 4) 。</li><li>假如已經交互了一百次，現在又回到了 (0, 0) 每次的經驗是下面這樣——</li></ul></li></ol><div class="table-container"><table><thead><tr><th>Episode</th><th>状态 (S)</th><th>动作 (A)</th><th>奖励 (R)</th></tr></thead><tbody><tr><td>1</td><td>(0,0)</td><td>向右</td><td>+1</td></tr><tr><td>2</td><td>(0,0)</td><td>向右</td><td>+1</td></tr><tr><td>3</td><td>(0,0)</td><td>向下</td><td>+2</td></tr><tr><td>4</td><td>(1,0)</td><td>向右</td><td>+1</td></tr><tr><td>5</td><td>(1,0)</td><td>向下</td><td>+2</td></tr><tr><td>6</td><td>(1,1)</td><td>向右</td><td>+1</td></tr><tr><td>7</td><td>(1,1)</td><td>向右</td><td>+1</td></tr><tr><td>8</td><td>(2,1)</td><td>向下</td><td>+2</td></tr><tr><td>9</td><td>(2,2)</td><td>向右</td><td>+1</td></tr><tr><td>10</td><td>(3,2)</td><td>向右</td><td>+1</td></tr><tr><td>…</td><td>…</td><td>…</td><td>…</td></tr></tbody></table></div><p>那麼現在在(0, 0)，假如：</p><ul><li>在 <code>(0, 0)</code> 状态下，“向右”动作执行了 30 次，<strong>平均奖励为 +1</strong>。</li><li>在 <code>(0, 0)</code> 状态下，“向下”动作执行了 20 次，<strong>平均奖励为 +2</strong>。</li></ul><p>在经验分布策略中，Agent 会根据每个动作的历史表现（即其平均奖励）来调整选择该 Action 的概率。通常，代理会根据每个动作的<strong>优势（奖励）</strong>和<strong>执行次数</strong>来评估选择概率。</p><h3 id="方法1-reward-和執行次數算-weighted-sum"><a href="#方法1-reward-和執行次數算-weighted-sum" class="headerlink" title="方法1: reward 和執行次數算 weighted sum"></a><strong>方法1: reward 和執行次數算 weighted sum</strong></h3><ol><li><p><strong>计算每个动作的总奖励</strong>：</p><ul><li>对于“向右”动作，执行了 30 次，平均奖励 +1，因此总奖励是：<br>$30 \times 1 = 30$</li><li>对于“向下”动作，执行了 20 次，平均奖励 +2，因此总奖励是：<br>$20 \times 2 = 40$</li></ul></li><li><p><strong>计算每个动作的选择概率</strong>：</p><ul><li>总奖励：$30 + 40 = 70$（即两种动作的总奖励）。</li><li>“向右”的选择概率是：<br>$P(\text{向右}) = \frac{30}{70} = 0.4286$</li><li>“向下”的选择概率是：<br>$P(\text{向下}) = \frac{40}{70} = 0.5714$</li></ul></li></ol><h3 id="更常用方法2-优势函数"><a href="#更常用方法2-优势函数" class="headerlink" title="更常用方法2: 优势函数"></a><strong>更常用方法2: 优势函数</strong></h3><ol><li>advantage function：<ul><li>算基準獎勵，假設是總獎勵平均值 $\frac{30 \times 1 + 20 \times 2}{30 + 20} = \frac{30 + 40}{50} = 1.4$，</li><li>算優勢 advantage = 平均獎勵 - 基準獎勵。</li><li>優勢：$A(\text{向右}) = 1 - 1.4 = -0.4$，$A(\text{向下}) = 2 - 1.4 = +0.6$。</li></ul></li><li>softmax $P(\text{动作}) = \frac{e^{A(\text{动作})}}{\sum_{\text{动作}} e^{A(\text{动作})}}$<script type="math/tex; mode=display"> P(\text{向右}) = \frac{0.6703}{2.4924} \approx 0.268</script><script type="math/tex; mode=display"> P(\text{向下}) = \frac{1.8221}{2.4924} \approx 0.732</script></li></ol><div align=center><img src="/pics/cs231n/ReinforcementLearning.png" loading="lazy" alt="" width="50%" height="50%"></div><p><strong>每種策略生成的 Q-value function Q(s, a) 是不同的，Q值依賴於 Agent 所使用的策略。</strong></p><div align=center><img src="/pics/cs231n/1*7YaeVSDiv9kg7B69GxbTWA.png" loading="lazy" alt="" width="50%" height="50%"></div><p>↑ 根據模型輸出的 Q(s, a) 和採用的 policy 公式，生成一個概率分佈選擇 action。↑</p><h2 id="Continue-Lecture-14-Reinforcement-Learning"><a href="#Continue-Lecture-14-Reinforcement-Learning" class="headerlink" title="Continue Lecture 14 Reinforcement Learning"></a><strong>Continue Lecture 14 Reinforcement Learning</strong></h2><h3 id="Policy-Gradients"><a href="#Policy-Gradients" class="headerlink" title="Policy Gradients"></a><strong>Policy Gradients</strong></h3><p>問題是Q，這個value of state action pair會非常非常非常大，比如圖裡的高維問題。<br>Q很大（情況很多）但是決策一般比較簡單，所以是否可以從collection of polices裡直接學到一個policy？（這裡的 policy 就是<a href="#policy">上面 Q-learning 討論的</a>）</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture14-9.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這個公式跟 <a href="#q-learning">Value Function</a> 一模一樣，只把 given $s_0$ 改為 given $\pi_\theta$。</p><p>代表算法 ↓</p><h3 id="REINFORCE-algorithm"><a href="#REINFORCE-algorithm" class="headerlink" title="REINFORCE algorithm"></a><strong>REINFORCE algorithm</strong></h3><div class="table-container"><table><thead><tr><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture14-10.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture14-11.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture14-12.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture14-13.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture14-14.png" alt=""></td></tr></tbody></table></div><script type="math/tex; mode=display">\begin{aligned}J(\theta) &= \mathbb{E}\left[\sum_{t \geq 0} \gamma^t r_t \mid \pi_\theta\right] \\          &= \mathbb{E}_{\tau \sim p(\tau ; \theta)}\left[r(\tau)\right] \\          &= \int_{\tau} r(\tau) p(\tau ; \theta) \, d\tau\end{aligned}</script><ol><li>每個 $\pi_\theta$ 都有很多可能路徑 trajectory $\tau$，期望展開，就是每個軌跡的 reward 和軌跡概率分佈的積分。</li><li>$\nabla_\theta J(\theta) = \int_{\tau} r(\tau) \nabla_\theta p(\tau; \theta) \, d\tau$ 被積函數光滑可微時，梯度和積分就可以互換，根據某些法則。<ul><li>trick 是 log(x) 導數 = 1/x;</li><li>$\begin{aligned} \nabla_\theta J(\theta) &amp; =\int_\tau\left(r(\tau) \nabla_\theta \log p(\tau ; \theta)\right) p(\tau ; \theta) \mathrm{d} \tau \\ &amp; =\mathbb{E}_{\tau \sim p(\tau ; \theta)}\left[r(\tau) \nabla_\theta \log p(\tau ; \theta)\right]\end{aligned}$</li><li>這個公式還是期望展開成積分，反過來積分倒回期望。</li><li>直接計算這個期望可能非常困難，因為我們無法遍歷所有可能的路徑 $\tau$。但根據大數法則，我們知道，如果我們從分佈 $p(\tau; \theta)$ 中抽取足夠多的樣本 $\tau_1, \tau_2, \dots, \tau_N$，那麼這個期望可以通過樣本的平均來近似：<script type="math/tex; mode=display">\nabla_\theta J(\theta) \approx \frac{1}{N} \sum_{i=1}^{N} r(\tau_i) \nabla_\theta \log p(\tau_i ; \theta)</script>這就是蒙特卡洛方法的核心。它依賴於從分佈 $p(\tau; \theta)$ 中抽取的隨機樣本來近似整個期望值。我們通過多次採樣從策略分佈中獲取不同的路徑 $\tau_i$，對每個樣本計算其相應的回報 $r(\tau_i)$ 和策略梯度 $\nabla_\theta \log p(\tau_i; \theta)$，然後求取平均來估計期望。</li></ul></li><li>transition 就是 $(s_t, a_t, r_t, s_{t+1})$ 四元組，這裡的技巧就是log累乘變累加。</li><li>最終求出梯度的直覺就是，$reward(\tau)$ 越大，$\pi(a_t|s_t)$ 越變大，即在 $s_t$ 下根據 <a href="#policy">policy</a> 採用 $a_t$ 動作的概率就直接變大。</li><li>路徑裡每個行動的短期 reward 方差很大，比如隨機性影響，明明很好的一步但是天氣很差，明明很差的一步但是對手很弱，所以要關注長期影響，<script type="math/tex; mode=display">\nabla_\theta J(\theta) \approx \sum_{t \geq 0} r(\tau) \nabla_\theta \log \pi_\theta\left(a_t \mid s_t\right)</script>改成不不只關注時間 t 的當下回報 $r_t(\tau)$，關注從當前時間步開始未來的累積回報，<script type="math/tex; mode=display">\nabla_\theta J(\theta) \approx \sum_{t>0}\left(\sum_{t^{\prime}>t} r_{t^{\prime}}\right) \nabla_\theta \log \pi_\theta\left(a_t \mid s_t\right)</script>但是很容意想到，如果 reward 都是正的，idea 1 和 2 會不停地把 action 們的 probability 拉高，所以可以減一個目前為止的平均值，作為 baseline。</li></ol><div class="table-container"><table><thead><tr><th></th><th>vanilla</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/ReinforcementLearning-1.png" alt=""></td><td><img src="/pics/cs231n/ReinforcementLearning-2.png" alt=""></td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>然後發現這倆值<a href="#q-learning">正好就是 Q 和 V</a></th><th>這個新算法正好就是<a href="#policy">experience replay裡的advantage</a></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/ReinforcementLearning-3.png" alt=""></td><td><img src="/pics/cs231n/ReinforcementLearning-4.png" alt=""></td></tr></tbody></table></div><p>在八方城那個出租屋裡，老闆半夜十一點半給我打電話說別學LDA學點有用的，沒想到強化學習也需要蒙特卡洛採樣，模擬估計值（狀態價值，動作價值）。好吧發現記錯了，這裡不是 Markov chain Monte Carlo MCMC 。</p><p>正好是这节课的前一天柯洁输了AlphaGO第一场比赛。</p><p>发现 <a href="https://stable-baselines3.readthedocs.io">stable-baseline3</a> 这个<a href="https://github.com/DLR-RM/stable-baselines3">宝藏 repo</a>，直接跑个 Atari Game 玩玩看。</p><h2 id="Lecture-15"><a href="#Lecture-15" class="headerlink" title="Lecture 15"></a><strong>Lecture 15</strong></h2><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15.png" loading="lazy" alt="" width="50%" height="50%"></div><p>最耗電的是內存操作。</p><p>pJ 是 皮焦耳（picojoule）能量单位的缩写。它是焦耳（J，joule）的子单位。1 pJ = 10⁻¹² J。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-2.png" loading="lazy" alt="" width="50%" height="50%"></div><p>各種數據類型。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-3.png" loading="lazy" alt="" width="50%" height="50%"></div><p>浮點乘是最占芯片空間的操作，DRAM讀取是最耗電的。<br>RAM 随机存取存储器：<br>SRAM (Static RAM) 使用一组反向的晶体管来存储每一位数据（通常是 4-6 个晶体管），常用於 Cache。<br>DRAM (Dynamic RAM) 使用一个晶体管和一个电容器来存储每一位数据。数据存储在电容中，但由于电容会逐渐放电，因此 DRAM 必须定期刷新（每几毫秒刷新一次）以保持数据。</p><h3 id="Algorithms-for-Efficient-Inference"><a href="#Algorithms-for-Efficient-Inference" class="headerlink" title="Algorithms for Efficient Inference"></a><strong>Algorithms for Efficient Inference</strong></h3><ul><li><ol><li>Pruning</li></ol></li><li><ol><li>Weight Sharing</li></ol></li><li><ol><li>Quantization</li></ol></li><li><ol><li>Low Rank Approximation</li></ol></li><li><ol><li>Binary / Ternary Net</li></ol></li><li><ol><li>Winograd Transformation</li></ol></li></ul><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-11.png" loading="lazy" alt="" width="50%" height="50%"></div><p>之前cnn architectures部分提到過可以先用1x1 conv減少input維度可以大大節省運算開銷。</p><h4 id="Pruning"><a href="#Pruning" class="headerlink" title="Pruning"></a><strong>Pruning</strong></h4><ul><li>Weight Pruning</li><li>Neuron Pruning</li></ul><p>方法就是直接把 weight 或者 activation 置 0。</p><ol><li>根據 weight 絕對值大小，低於 threshold 的置0。</li><li>根據 neuron 被激活比例，即有多少是在activation function的有效區間裡。</li><li>根據梯度。</li></ol><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture15-4.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture15-5.png" alt=""></td></tr></tbody></table></div><h4 id="Weight-Sharing"><a href="#Weight-Sharing" class="headerlink" title="Weight Sharing"></a><strong>Weight Sharing</strong></h4><p>把所有 weights 都取整，只存索引。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture15-6.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture15-15.png" alt=""></td></tr></tbody></table></div><h4 id="Quantization"><a href="#Quantization" class="headerlink" title="Quantization"></a><strong>Quantization</strong></h4><p>就是把小的浮點數擴大（線性縮放scale）到整數。</p><script type="math/tex; mode=display">\text{scale} = \frac{\text{max_int} - \text{min_int}}{\text{max_float} - \text{min_float}}</script><script type="math/tex; mode=display">x_{\text{quantized}} = \text{round}(x_{\text{float}} \times \text{scale}) + \text{min_int}</script><ul><li>weight quantization</li><li>activation quantization<br>權重和激活值量化是一樣的方法。舉個例子：</li></ul><ol><li><p>权重量化</p><p> 假设某一层的权重矩阵是浮动的：</p><script type="math/tex; mode=display">W = [0.1, 0.5, -0.2, 1.0]</script><p> 你可以使用 <strong>线性量化</strong> 进行转化，即定义一个量化比例（scale），例如，假设我们选择将权重范围映射到 8 位整数范围 [0, 255]。假设最大值为 1.0，最小值为 -1.0，量化尺度（scale）为：</p><script type="math/tex; mode=display">\text{scale} = \frac{255}{2} = 127.5</script><p> 将权重乘以量化比例，得到量化后的整数值：</p><script type="math/tex; mode=display">\text{quantized } W = [\text{round}(0.1 \times 127.5), \text{round}(0.5 \times 127.5), \text{round}(-0.2 \times 127.5), \text{round}(1.0 \times 127.5)]</script><p> 计算结果为：</p><script type="math/tex; mode=display">\text{quantized } W = [13, 64, -25, 127]</script><p> 然后，你可以用这些整数权重值进行神经网络的推理。</p></li><li><p>激活量化</p><p> 假设神经网络的某一层的激活输出为：</p><script type="math/tex; mode=display">\text{activation} = [0.2, 0.8, -0.3, 0.9]</script><p> 你可以对这些激活值进行量化，将它们映射到一个整数范围，比如 [0, 255]，并进行类似的操作来获得量化后的激活值。</p><p> 假设最大值为 1.0，最小值为 -1.0，量化尺度（scale）为 127.5，那么激活的量化过程是：</p><script type="math/tex; mode=display">\text{quantized activation} = [\text{round}(0.2 \times 127.5), \text{round}(0.8 \times 127.5), \text{round}(-0.3 \times 127.5), \text{round}(0.9 \times 127.5)]</script><p> 计算结果为：</p><script type="math/tex; mode=display">\text{quantized activation} = [26, 102, -38, 114]</script></li></ol><p>擴大後再 round 捨掉的精度會小兩三個量級。</p><p>這也是TPU為什麼能用INT8的原因。既節省空間又提高速度。</p><h4 id="Low-Rank-Approximation"><a href="#Low-Rank-Approximation" class="headerlink" title="Low Rank Approximation"></a><strong>Low Rank Approximation</strong></h4><p>低秩近似，用奇异值分解（SVD）之類的矩陣分解技術把卷積核分解。</p><h4 id="Binary-Ternary-Net"><a href="#Binary-Ternary-Net" class="headerlink" title="Binary / Ternary Net"></a><strong>Binary / Ternary Net</strong></h4><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>大道至簡，直接給所有weight簡化成2個或3個值。</p><h4 id="Winograd-Transformation"><a href="#Winograd-Transformation" class="headerlink" title="Winograd Transformation"></a><strong>Winograd Transformation</strong></h4><p>通過Winograd算法能減少卷積裡的乘法次數。</p><h3 id="Hardware-for-Efficient-Inference"><a href="#Hardware-for-Efficient-Inference" class="headerlink" title="Hardware for Efficient Inference"></a><strong>Hardware for Efficient Inference</strong></h3><p>a common goal: minimize memory access</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>ASIC 是应用特定集成电路（Application-Specific Integrated Circuit）。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>TPU就是一個ASIC。</p><p>核心是一個 MMU (Matrix Multiply Unit)，這個芯片可以做到每個 clock circle 運算 65536 次矩陣乘或加。乘 700 MHz ，throughput 就高達每秒 92 T 次矩陣乘操作。</p><h3 id="Algorithms-for-Efficient-Training"><a href="#Algorithms-for-Efficient-Training" class="headerlink" title="Algorithms for Efficient Training"></a><strong>Algorithms for Efficient Training</strong></h3><ul><li><ol><li>Parallelization</li></ol></li><li><ol><li>Mixed Precision with FP16 and FP32</li></ol></li><li><ol><li>Model Distillation</li></ol></li><li><ol><li>DSD: Dense-Sparse-Dense Training</li></ol></li></ul><h4 id="Parallelization"><a href="#Parallelization" class="headerlink" title="Parallelization"></a><strong>Parallelization</strong></h4><div align=center><img src="/pics/cs231n/Historical-microprocessor-trends-78.png" loading="lazy" alt="Historical microprocessor trends" width="50%" height="50%"></div><p>單核性能已經到頂了，因為 1.  晶體管已經很難再縮小幾納米，就決定了clock circle很難再縮短；2. 再提高頻率會增大功耗導致過熱和電源問題。</p><p>所以要尋求並行化。</p><ol><li><p>Data Parallel，即分布式計算的做法，用一個 master server 和一堆 slave servers 用分布的數據計算前向後向；</p> <div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-9.png" loading="lazy" alt="" width="50%" height="50%"></div></li><li><p>Model Parallel，類似 LSTM 計算門的做法，把 weight 矩陣切開在多個 core 上計算。</p> <div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-10.png" loading="lazy" alt="" width="50%" height="50%"></div></li></ol><h4 id="Mixed-Precision-with-FP16-and-FP32"><a href="#Mixed-Precision-with-FP16-and-FP32" class="headerlink" title="Mixed Precision with FP16 and FP32"></a><strong>Mixed Precision with FP16 and FP32</strong></h4><p>FP: Floating Point</p><p>混合精度訓練只有在更新梯度的時候用 FP32，因為累加會有精度累积，如果用 FP16 會損失更多精度，乘法無所謂。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/cs231n_2017_lecture15-12.png" alt=""></td><td><img src="/pics/cs231n/cs231n_2017_lecture15-13.png" alt=""></td></tr></tbody></table></div><h4 id="Model-Distillation"><a href="#Model-Distillation" class="headerlink" title="Model Distillation"></a><strong>Model Distillation</strong></h4><p>用教師模型 ensemble 即加權平均的 outputs 作為學生模型的 labels，即所謂軟標籤（Soft Targets），学生模型不仅学习从标注数据中获得的“硬标签”（例如“猫”），还学习教师模型提供的“软标签”。软标签提供了更多的细节信息，例如哪些类别可能较为相似，这有助于学生模型更好地学习复杂的决策边界。</p><p>學生模型的 y 就是教師模型的預測概率分佈，隱含了類別間的相對關係。</p><p>損失函數用 KL散度，衡量學生和教師模型的預測分布的差異。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-14.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Hardware-for-Efficient-Training"><a href="#Hardware-for-Efficient-Training" class="headerlink" title="Hardware for Efficient Training"></a><strong>Hardware for Efficient Training</strong></h3><p>computation和memory bandwidth是兩個bottleneck，每個GPU/TPU/EIE都是要看這倆參數。</p><p>專門面向deep learning的Volta GV100 (2017)，有tensor flops/tensor core。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture15-16.png" loading="lazy" alt="" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th>Tesla Product</th><th>Tesla K40</th><th>Tesla M40</th><th>Tesla P100</th><th>Tesla V100</th></tr></thead><tbody><tr><td><strong>GPU</strong></td><td>GK110 (Kepler)</td><td>GM200 (Maxwell)</td><td>GP100 (Pascal)</td><td>GV100 (Volta)</td></tr><tr><td><strong>GPU Boost Clock</strong></td><td>810/875 MHz</td><td>1114 MHz</td><td>1480 MHz</td><td>1455 MHz</td></tr><tr><td><strong>Peak FP32 TFLOP/s</strong></td><td>5.04</td><td>6.8</td><td>10.6</td><td>15</td></tr><tr><td><strong>Peak Tensor Core TFLOP/s</strong></td><td>-</td><td>-</td><td>-</td><td>120</td></tr><tr><td><strong>Memory Interface</strong></td><td>384-bit GDDR5</td><td>384-bit GDDR5</td><td>4096-bit HBM2</td><td>4096-bit HBM2</td></tr><tr><td><strong>Memory Size</strong></td><td>Up to 12 GB</td><td>Up to 24 GB</td><td>16 GB</td><td>16 GB</td></tr><tr><td><strong>TDP</strong></td><td>235 Watts</td><td>250 Watts</td><td>300 Watts</td><td>300 Watts</td></tr><tr><td><strong>Transistors</strong></td><td>7.1 billion</td><td>8 billion</td><td>15.3 billion</td><td>21.1 billion</td></tr><tr><td><strong>GPU Die Size</strong></td><td>551 mm²</td><td>601 mm²</td><td>610 mm²</td><td>815 mm²</td></tr><tr><td><strong>Manufacturing Process</strong></td><td>28 nm</td><td>28 nm</td><td>16 nm FinFET+</td><td>12 nm FFN</td></tr></tbody></table></div><h2 id="Lecture-16"><a href="#Lecture-16" class="headerlink" title="Lecture 16"></a><strong>Lecture 16</strong></h2><ul><li>What are adversarial examples?</li><li>Why do they happen?</li><li>How can they be used to compromise machine learning systems?</li><li>What are the defenses?</li><li>How to use adversarial examples to improve machine learning, even when there is no adversary</li></ul><h3 id="What-are-adversarial-examples"><a href="#What-are-adversarial-examples" class="headerlink" title="What are adversarial examples?"></a><strong>What are adversarial examples?</strong></h3><p>You’re probably used to following the gradient on the parameters of a model.<br>You can use the back propagation algorithm to compute the gradient on the input image using exactly the same procedure that you would use to compute the gradient on the parameters.</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Ian第一次实现时不敢相信是正确的代码，以为是bug，所以特地把输入和输出拿出来用numpy算difference。<br>這裡的做法是之前<a href="#lecture-12">Lecture 12</a>裡講的用錯誤類別對input image做gradient ascent，可以做到fool spam欺騙模型，人眼看還是兩個大熊貓，模型區分不了，而模型能區分人眼區分不了的 Alaska Husky 和 Siberian Husky。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-12.png" loading="lazy" alt="" width="50%" height="50%"></div><h3 id="Why-do-they-happen"><a href="#Why-do-they-happen" class="headerlink" title="Why do they happen?"></a><strong>Why do they happen?</strong></h3><p>but then because this is a very complicated function and it has just way more parameters than it actually needs to represent the training task, it throws little blobs of probability mass around the rest of space randomly.</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>他們首先認為這是overfitting導致的，但是如果真的是過擬合導致的，那按理說這個 model 是個 unique bad luck，如果重新 fit 一遍或者 fit 個略微不同的模型，應該會出現其他 random different adversarial examples，<br>但是事實是很多不同的模型都會 misclassify the same adversarial examples，甚至是分到同樣的類裡，而且發現 adversarial example 減 original example，得到的 offset vector 有相同的方向，把這個加到任何 clean example 上都會得到 adversarial example。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-2.png" loading="lazy" alt="" width="50%" height="50%"></div><p>所以或許是欠擬合導致的。</p><p>Modern deep nets are very piecewise linear<br>一個很神奇的思路：input和output是piecewise linear function的關係。。但是每層的weights matrices是乘在一起的所以是非常non-linear的，這部分導致訓練很難，好像確實是，input並沒有乘自己所以是一次關係</p><p>But the mapping from the input to the output is much more linear and predictable, and it means that optimization problems that aim to optimize the input to the model are much easier than optimization problems that aim to optimize the parameters.</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-3.png" loading="lazy" alt="" width="50%" height="50%"></div><p>本來WX就是線性的，這裡展示的 non-linearity有效區間也幾乎都是線性的。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-4.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Nearly Linear Response 這個slide裡，實驗是選了一個白車紅底圖，選了一個這張圖將travel through space的方向，然後 $\epsilon$ 乘這個方向，加在input image上， $\epsilon = 0$ 就是原圖。<br>左邊的是 $\epsilon$ 從 -30 到 +30 的animation，黃框的是分類正確的。<br>右邊的是這個 deep convolutional rectified linear unit model (CNN with ReLU) 輸出的 logits。</p><p>可以看到 car 在這裡是 automobile， $\epsilon = 0$ 時logits最大。而改變 $\epsilon$ 時變化最大的是 plot 裡的 brown curve，frog 類，正確的 automobile 分類在這個方向上移動時遠不如 frog 變得快。<br>所以這個方向也許就是跟蛙類相關的方向。<br>就像“Adversarial Examples from Excessive Linearity” 認為不是過擬合反而是欠擬合時，說的那樣的位置/方向，linear model 有很高的 confidence 給角落裡沒見過樣本的位置。</p><p>Adversarial examples存在就是因為模型非常線性，上面CNN的曲線也印證了這一點，logits對input線性變化的反應也是線性的。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-5.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Small inter-class distances 這裡也說明，即使用一個比平常用的 reg 7 小的 L2 norm 3.96，也可能造成肉眼上非常大的變化，所以其實人眼不可見的變化也許是會讓 input go really far 並且乘 coefficient W 的時候在這個方向上變得很大。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-6.png" loading="lazy" alt="" width="50%" height="50%"></div><p>The Fast Gradient Sign Method 就基於這種 neural nets 其實非常 linear 的觀察，認為梯度方向就是一個生成上面所說的這種 adversarial example 最快的方向，根據 Taylor series approximation 泰勒展開得到上面的公式，基於此推導出底下更新出 adversarial example 的公式。</p><p>Ian說人們很難找到真的好過fast gradient sign（gradient ascent這樣）的構造方式，Nicholas Carlini’s attack 也值得嘗試。The Fast Gradient Sign Method 攻擊沒有 special defenses 的 regular neural nets 有 99% 的成功率。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Maps of Adversarial and Random Cross-Sections 他們做的這個“橫截面” decision boundary 圖是另一個 evidence。FGSM 就是上面這個簡稱。</p><p>每個cell對應一個 CIFAR-10 的樣本，白色的是正確分類，彩色是其他錯誤分類，x axis 方向是樣本沿 FGSM 的梯度方向改變，y axis 方向是 orthogonal 梯度正交的方向。<br>可以看到 adversarial examples 的區分是個 linear subspace，不像上面的圖裡假設的那樣 adversarial examples 只是一堆個例 blob，因此只要用一個跟 gradient 點積較大的方向就可以構造 adversarial examples 了。<br>最後還有個加噪聲方向的對比圖，用隨機噪聲其實會讓模型要麼完全分類錯，要麼完全分類對。這是因為在高維空間裡，隨機向量幾乎一定和選定的參考向量（random noise的方向）正交。</p><h3 id="在高維度裡為什麼隨機找一個random-vector跟reference-vector很可能是正交的？有點反直覺？"><a href="#在高維度裡為什麼隨機找一個random-vector跟reference-vector很可能是正交的？有點反直覺？" class="headerlink" title="在高維度裡為什麼隨機找一個random vector跟reference vector很可能是正交的？有點反直覺？"></a><strong>在高維度裡為什麼隨機找一個random vector跟reference vector很可能是正交的？有點反直覺？</strong></h3><p><strong>Reference vector（参考向量）</strong>是一个在某个空间或上下文中用于描述或标定其他向量的基准向量。具体来说，参考向量通常用于对比、计算、映射或表示其他向量的方向、大小等特征。</p><p>在高维空间中，<strong>参考向量</strong>的定义可以根据问题的背景有所不同。比如在一些机器学习或信号处理中，参考向量可以用来表示某种特征，或者作为某种算法的“标准”，其他的向量可以相对于参考向量进行比较。</p><h4 id="为什么随机选择一个向量与参考向量正交的可能性很高？"><a href="#为什么随机选择一个向量与参考向量正交的可能性很高？" class="headerlink" title="为什么随机选择一个向量与参考向量正交的可能性很高？"></a><strong>为什么随机选择一个向量与参考向量正交的可能性很高？</strong></h4><p>这个问题的反直觉性主要来源于高维空间中的几何性质。具体来说，<strong>高维空间中的向量之间的相互关系</strong>与我们在低维空间（如二维或三维空间）中的直观理解非常不同。在高维空间中，随机选择的向量与某个固定向量（如参考向量）正交的概率实际上是非常高的，原因可以从以下几个方面理解：</p><h5 id="1-高维空间的几何特性："><a href="#1-高维空间的几何特性：" class="headerlink" title="1. 高维空间的几何特性："></a>1. <strong>高维空间的几何特性</strong>：</h5><p>   在二维或三维空间中，随机选择的向量与某个固定向量正交的概率是零，因为正交的向量只可能在一个特定的方向上（例如，在二维空间中，只有与参考向量垂直的那条直线上的向量才会正交）。然而，在高维空间中，正交向量的集合（即与参考向量垂直的所有向量）所形成的子空间的维度远大于低维空间。</p><ul><li>在高维空间中，给定一个参考向量，它所确定的“正交子空间”的维度非常高。假设我们在 $n$-维空间中，参考向量 $\mathbf{v}$ 所确定的正交子空间是一个 $n-1$ 维的子空间。</li><li>任何一个随机选择的向量，如果它位于这个 $n-1$ 维的正交子空间中，那么它就会与参考向量正交。</li></ul><h5 id="2-随机向量的分布："><a href="#2-随机向量的分布：" class="headerlink" title="2. 随机向量的分布："></a>2. <strong>随机向量的分布</strong>：</h5><p>   在高维空间中，随机选择的向量往往“几乎”是正交的。随机选择的向量可以看作是从一个单位球体（即所有向量的长度为1的集合）中随机选择的。如果我们取一个随机向量，它与参考向量的夹角大多数情况下会非常接近90度（即正交），这是因为高维空间中的向量几乎是均匀分布的，且任何两个向量之间的夹角都有很大的可能性接近于 $90^\circ$。</p><h5 id="3-高维空间中的内积接近于零："><a href="#3-高维空间中的内积接近于零：" class="headerlink" title="3. 高维空间中的内积接近于零："></a>3. <strong>高维空间中的内积接近于零</strong>：</h5><p>   在高维空间中，<strong>内积的期望值趋近于零</strong>。如果你随机选择一个 $n$-维向量，并且将它与一个固定的参考向量做内积，由于高维空间的“稀疏性”，这个内积值通常接近零，这意味着随机向量和参考向量几乎是正交的。</p><h5 id="4-高维空间的“球面集中性”："><a href="#4-高维空间的“球面集中性”：" class="headerlink" title="4. 高维空间的“球面集中性”："></a>4. <strong>高维空间的“球面集中性”</strong>：</h5><p>   随机向量在高维空间中的分布有一个有趣的现象，称为<strong>球面集中性</strong>。随着维度的增加，大多数随机向量的方向都趋于均匀分布。对于一个随机选择的向量，它与任何固定参考向量的夹角都会趋向于均匀分布，且<strong>大部分时间夹角都接近于90度</strong>，这使得它们在高维空间中呈现“正交”的特性。</p><h4 id="直观的理解："><a href="#直观的理解：" class="headerlink" title="直观的理解："></a><strong>直观的理解：</strong></h4><ul><li>在二维或三维空间中，两个向量要正交，它们必须满足某种特定的几何关系（90度或說垂直），因此它们在几何上是“特殊”的。</li><li>然而，在高维空间中，大多数向量之间的关系都趋向于“随机”。当你随机选择一个向量时，几乎可以认为它在一个高维的“球面”上，它与参考向量几乎是随机的，且有非常高的概率接近正交。</li></ul><h5 id="数学上的说明："><a href="#数学上的说明：" class="headerlink" title="数学上的说明："></a><strong>数学上的说明：</strong></h5><ul><li>假设参考向量是 $\mathbf{v}$，随机向量是 $\mathbf{r}$，它们的内积 $\mathbf{v} \cdot \mathbf{r}$ 表示它们之间的相似度。随着空间维度的增大，内积的期望值趋近于零，因为大多数向量几乎处于与参考向量正交的状态。</li></ul><h4 id="总结：-2"><a href="#总结：-2" class="headerlink" title="总结："></a><strong>总结：</strong></h4><p>在低维空间中，我们直观地认为两个向量之间的关系是比较特殊的（例如正交），但在高维空间中，随机选择的向量与参考向量正交的概率非常高，这是由于高维空间中正交子空间的维度很大，以及高维空间中向量之间的内积趋向于零，导致随机向量与参考向量几乎是正交的现象。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這個Stanford的工作是在MNIST（28 x 28 = 784 維，手寫數字單通道）上做的，有25個跟 gradient 點積大的方向，也就是說一個 attacker 有 10% 的機率隨機用噪聲騙過模型。</p><p>很像 Clever Hans 這匹誤以為會 arithmetic 的馬，model 學到的 “linear” function 只會在跟 training set 分布類似的 test set上有好效果，如果 shift a little 比如在這 25 個 adversarial direction 上變化就沒有泛化能力了。<br><a href="https://www.youtube.com/watch?v=r2jm0nRJZdI">adversarial FGSM 也可以讓很難失敗的 RL 算法失敗。</a></p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-9.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這個分辨 7 和 3 的任務裡，weights 應該長得像它倆的區別（因為會同時學習 7 和 3 的關鍵特徵，這也正是它倆的區別）。<br>他當時發現 RBEs behave more intuitively，但是回傳梯度總是變0，他沒能訓練成功很深的RBF，如果有人有更好的優化方法也許能解決adversarial問題，不知現在如何。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-10.png" loading="lazy" alt="" width="50%" height="50%"></div><p>不同的模型能轉化！</p><ol><li>attacker 可以在目標任務數據集上訓一個模型找 adversarial examples 騙過目標模型。</li><li>目標任務數據集獲得不到，但是能訪問目標網絡的話，可以給它一些輸入，得到一些結果，然後用這來訓練自己的模型。</li></ol><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-11.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這個圖裡 ensemble 了除了每行第一個外的其他四個模型，后面被 ensemble 的模型不可能分類正確，沒被 ensemble 的模型正確率也很低。</p><p>甚至有個 malGAN 繞過 malware detectors 恶意软件（Malicious Software）。</p><h3 id="What-are-the-defenses"><a href="#What-are-the-defenses" class="headerlink" title="What are the defenses?"></a><strong>What are the defenses?</strong></h3><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-13.png" loading="lazy" alt="" width="50%" height="50%"></div><p>Ian 說就算你讓模型訓練應對了一種defense，但因為是 non stationary 的，它會對另一種 attack 變得 vulnerable。</p><div align=center><img src="/pics/cs231n/cs231n_2017_lecture16-14.png" loading="lazy" alt="" width="50%" height="50%"></div><p>我一直想問的問題，如果訓練的時候加入攻擊樣本呢？——Training on Adversarial Examples</p><p>adversarial training 是 data hungry 的，每次更新參數都要生成新的數據，也很合理，畢竟每次更新參數時才能拿到 dLoss over dX。</p><p>QA階段：他的思路是，如果一個現象出現次數超過 80% 就會放進 slide 裡並試圖解釋它，比如上面的 weird things 比如上面的 frog class。<br>關於上面說的數據總維度和 adversarial 維度的關係（比如 MNIST 的實驗裡是 25 over 784 大約 3%），他也沒有具體概念，他只在兩個數據集上跑過，因為當時是在 OpenAI 離職和去 Google 之間的時間，所以只在筆記本的 CPU 上跑了這個代碼，沒有 GPU。</p><p>來讀研三年半只有商湯的半年真的感覺自己在做科研。</p><p>Stanford和Berkeley都專門有GAN的課，<a href="https://deepgenerativemodels.github.io">CS236</a>和<a href="https://sites.google.com/view/berkeley-cs294-158-sp19/home">CS294-158</a>。</p><h2 id="Lecture-17-Attention-and-Transformers"><a href="#Lecture-17-Attention-and-Transformers" class="headerlink" title="Lecture 17 Attention and Transformers"></a><strong>Lecture 17 Attention and Transformers</strong></h2><p><a href="https://www.youtube.com/watch?v=YAgjfMR9R_M&amp;list=PL5-TkQAfAZFbzxjBHtzdVCWE0Zbhomg7r&amp;index=13">2024的Lecture 8，或者2019的Lecture13。</a></p><h3 id="seq2seq-with-vanilla-rnn-or-lstm"><a href="#seq2seq-with-vanilla-rnn-or-lstm" class="headerlink" title="seq2seq with vanilla rnn or lstm"></a><strong>seq2seq with vanilla rnn or lstm</strong></h3><p>回顧上面 <a href="#vanilla-rnn">Lecture 10 提到的多種 rnn 結構/任務的 computational graph 裡</a>，其中 sequence to sequence 任務是 many to one + one to many 的形式，上面只是推理時的結構，這裡用seq2seq任務舉例，先細化訓練時的做法。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這裡的 context vector c 就是 $h_t$ 最後一個 hidden state，$s_0$ 就是類似 $h_0$ 或 $c_0$ 的初始化 decoder 裡的 hidden state。（這裡cs231n看圖的意思是s0不用ht初始化，JJ講的Michigan Online是說s0也要用ht初始化，见图）訓練時不一樣的是會給 decoder 每一個 time step 都輸入 ground truth token。</p><ul><li>訓練時 <strong>teacher forcing</strong>：就算預測是錯的也會用 correct token 作為下個 time step 的輸入，強迫 decoder 用 ground truth 訓練；</li><li>推理時 Test-time 最多只給 decoder 一個 <code>&lt;START&gt;</code>，也是 sample 到 <code>&lt;STOP&gt;</code> 或者 <code>&lt;END&gt;</code> 為止。</li></ul><p>但是像圖裡說的，這裡有個問題是如果 Encoder 的 time step 太長，比如翻譯任務輸入一個 1000 詞的文本，只用“ c ”無法很好地承載/傳遞所有的信息，<strong>這個 c 就是所謂的 Bottleneck 瓶頸</strong>。</p><p>vanilla 或 LSTM 無法處理長序列的問題在於 c 這個 context 向量不能承載所有上下文信息，一個解決方案是<strong>在解碼時直接使用輸入序列的原始信息</strong>。</p><h3 id="Attention"><a href="#Attention" class="headerlink" title="Attention"></a><strong>Attention</strong></h3><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-1.png" loading="lazy" alt="" width="50%" height="50%"></div><p>encoder還是一樣的，生成一個 $s_0$，但不直接輸入給 decoder 了，先用一個 alignment function（一個 FC） 把 Initial Decoder State $s_0$ 跟每一個 Encoder 的 Hidden State $h_{1/2/3/4…}$ 結合起來。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-2.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這個 Alignment Scores 的 Intuition 是：<strong>告訴我們在生成當前 Decoder 的 Hidden State $s_{1/2/3/4…}$時，Decoder 更希望關注哪些輸入。</strong>這個其實已經足夠了，但因為都是些 arbitrary real number，所以<strong>用 Softmax 整理成概率分佈</strong>，這個概率分佈就叫 Attention Weigths。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-3.png" loading="lazy" alt="" width="50%" height="50%"></div><p>有了“Attention <strong>Weights</strong>”之後，像上面說的，我們希望的是“<strong>在解碼時直接使用輸入序列的原始信息</strong>”解決 Context Vector 承載能力太低的問題，所以就用 Weigths 跟每個 Time Step / 輸入 的 Hidden State 求 加權和 作為 Decoder 新的 Context Vector $c_{1/2/3/4…}$。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-4.png" loading="lazy" alt="" width="50%" height="50%"></div><p>以機器翻譯為例，這樣做的直觀解釋是，比如要翻譯“我們是”為“we are”，decoder在預測 “we” “are” 時只需要關注 input sequence 裡的 “我”和“們”，而且這些不是提前設定的，是可導的、模型自己學到的，模型自己通過 jointly optimize 決定它更想關注哪部分。</p><div class="table-container"><table><thead><tr><th>一樣地，decoder 的第二個 time step 就用剛生成的 $s_1$ 計算新的 $e_{2x}, a_{2x}$ 和 weighted sum 即 context vector $c_2$</th><th>這時進行下一個 forward pass，用 c2 和剛預測的 $y_1$ 生成新的 hidden state $s_2$和$y_2$</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/498_FA2019_lecture13-5.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-6.png" alt="應該是漏了個箭頭我補上了"></td></tr></tbody></table></div><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這樣就得到了用 Attention 解決 Context Vector 的 Bottlenecking 的新 RNN。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>驗證模型是否真的學習到了 Attention 也很簡單，只需要把上面的 probability distribution 即 Attention Weigths 拿出來比一比就可以了。</p><h3 id="Visual-Attention"><a href="#Visual-Attention" class="headerlink" title="Visual Attention"></a><strong>Visual Attention</strong></h3><div class="table-container"><table><thead><tr><th>區別是 probability distribution 從 1D 變成了 2D</th><th>幾乎一模一樣的操作，$h_{i,j}$是個vector，$e_{t,i,j}$是個vector</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/498_FA2019_lecture13-9.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-10.png" alt=""></td></tr></tbody></table></div><p>跟前面 input 是個 sequence 的區別是這裡的 input 一直是這個 feature map，只不過不同的 time step 關注不同的位置。</p><p>這裡跟<a href="#attention">2017年講的attention</a>很像，就是跟<a href="#attention-1">新的 rnn attention idea</a> 結合了一下。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-11.png" loading="lazy" alt="" width="50%" height="50%"></div><p>原來晶狀體的英文跟透鏡/鏡頭一樣，都是Lens，Retina視網膜中間的Fovea中央凹是最清晰的視覺，看得到最清楚的細節和顏色，越邊緣部位越不敏銳（visual acuity視覺銳度 或 sensitivity敏感度下降）。</p><p>而且原來每個人的眼睛都會有扫视运动（<strong>saccades</strong>），就算在凝視一個地方也會有微扫视（microsaccades），眼睛會每秒跳很多次以應對Fovea這種機制讓Fovea接收更多信息。</p><h3 id="理解論文title"><a href="#理解論文title" class="headerlink" title="理解論文title"></a><strong>理解論文title</strong></h3><p>X, Attend, and Y<br>“Show, attend, and tell” (Xu et al, ICML 2015) 比如這第一篇attention文章，意思就是給模型show一個數據，讓它attend關注某個區域，然後tell通過生成words，告訴你模型看到了什麼。<br>Look at image, attend to image regions, produce question<br>“Ask, attend, and answer” (Xu and Saenko, ECCV 2016)<br>“Show, ask, attend, and answer” (Kazemi and Elqursh, 2017)<br>Read text of question, attend to image regions, produce answer<br>“Listen, attend, and spell” (Chan et al, ICASSP 2016)<br>Process raw audio, attend to audio regions while producing text<br>“Listen, attend, and walk” (Mei et al, AAAI 2016)<br>Process text, attend to text regions, output navigation commands<br>“Show, attend, and interact” (Qureshi et al, ICRA 2017)<br>Process image, attend to image regions, output robot control commands<br>“Show, attend, and read” (Li et al, AAAI 2019)<br>Process image, attend to image regions, output text</p><p>總之如果我們想用一個類型的數據來生成另一個類型，都可以考慮用attention mechanism，比如這裡的machine translation和image captioning。</p><p>然後就是generalization，有了個很好的mechanism之後，想要把它應用到所有任務。</p><h3 id="Abstract-Attention-Layer-Self-Attention"><a href="#Abstract-Attention-Layer-Self-Attention" class="headerlink" title="Abstract Attention Layer, Self Attention"></a><strong>Abstract Attention Layer, Self Attention</strong></h3><p>把這個模式推廣。這裡 generalization 的目的不是再用回之前的 RNN，只是因為效果好所以想推廣到所有地方成為一個類似於 FC / Conv 的新的 Layer。</p><p>對比下面兩種表達方式：</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/498_FA2019_lecture13-4.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-12.png" alt=""></td></tr></tbody></table></div><ul><li>Query vector 對應 Decoder 的 each time step 生成下一個 c 用的 Hidden State vector $s_{1/2/3/4…}$；</li><li>Input vectors 對應 Encoder 的所有 Hidden State vectors $h_{1/2/3/4…}$，有 N 個 inputs；</li><li>之前 intuition 是 alignment 的函數在這裡叫 similarity ；<ul><li>這個改進可以參考machine translation和image captioning和上面的 “Visualize Attention Weights”，decoder 的 output 跟 input 的區域基本上會有一一對應的關係，可以說就是<strong>尋找高維語義空間裡相似的部分</strong>；</li><li>也許沒有一一對應關係的任務需要用回 nn 作為 function approximator，或者針對任務定義 similarities function 。</li></ul></li><li>e (similarities) 就是之前的 alignment scores ；</li><li>Attention Weigths 沒變；</li><li>Output vector 對應 $c_{1/2/3/4…}$。</li></ul><p>改進思路：</p><ol><li>既然是計算 similarities，可以把 $f_{att}$ 這個 Fully-connected net 變成簡單的 dot product 點積相似度；<ul><li>改進版 scaled dot product，除以 $q$ 和 $X_i$ 的維度 $D_Q$ 的 square root ，總之就是防止 similarities 太大；</li></ul></li><li>不讓每一個 $s_t$ 和 $c_t$ 依賴於 $s_{t-1}$ 了，一次用所有 $s_t$ 即所有 Query vectors 矩陣乘 Input Vectors，Simultaneously 。</li><li>因為 Input vectors <strong>同時用來計算 similarities 和後面的 weighted sum</strong> ，所以可以把它分為 K 和 V，Intuition 是跟 BN 加兩個學習參數一樣用來增加 flexibility：<ul><li>model 計算 similarities 本來是用 FC 計算，換作點積之後可能減少了靈活性，用一個可學習的 $Weight_K$ 再給它增加回來；</li><li>model 利用 inputs 可能不只是 weighted sum 一種方式，就再加一個 $Weight_V$ 。比如用 Query 搜索時，我們不一定只希望知道 Query 本身，還希望知道跟 Query 相關的其他發散，有 flexibility 就可能給我們返回其他沒有出現在 Query 裡但我們同樣想知道的事。</li><li>最後/最上面的計算方法是 $Y_1 = V_1 \cdot A_{1,1} + V_2 \cdot A_{1,2} + V_3 \cdot A_{1,3}$。</li></ul></li></ol><div class="table-container"><table><thead><tr><th>1. 點積</th><th>2. Query 擴展</th><th>3. X 轉化爲 K 和 V 分別用在兩個不同計算裡</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/498_FA2019_lecture13-13.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-14.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-15.png" alt=""></td></tr></tbody></table></div><p>上面說的 “不讓每一個 $s_t$ 和 $c_t$ 依賴於 $s_{t-1}$ 了” 本來就有點奇怪，因為 Hidden State 按理說是 Sequential 的，self-attention 就是讓 Query 直接由 Inputs X 生成，用再一個新的 $Weight_Q$，我自認為的 Intuition 是：本來 teacher forcing 時 Decoder 裡所有的 Input 就都是 Groud Truth，不如直接用跟 Y 對應的 X 生成 Query（一開始 RNN 裡的 Hidden State）。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-16.png" loading="lazy" alt="" width="50%" height="50%"></div><p>然後就得到了 super generalized layer，（Again，這裡 generalization 的目的不是再用回之前的 RNN，只是因為效果好所以想推廣到所有地方成為一個類似於 FC / Conv 的新的 Layer）</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-17.png" loading="lazy" alt="" width="50%" height="50%"></div><p>但是這裡其實 layer 感受不到 inputs 的順序，交換 inputs 對輸出沒有影響，只是改變了輸出順序，因為這種計算基本是在讓 input 自己跟自己對比得到結果，<strong>有幾種方式（variatns）能讓模型有位置信息：</strong></p><h3 id="1-Positional-Encoding"><a href="#1-Positional-Encoding" class="headerlink" title="1. Positional Encoding"></a><strong>1. Positional Encoding</strong></h3><p>可以加入 positional encoding。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-18.png" loading="lazy" alt="" width="50%" height="50%"></div><p>ass 裡加入的方式是用 sin 和 cos 函數映射到 -1 到 1 之間，生成的看起來都是沒有邏輯的隨機數，但說是因為對坐標 i 和 j 引入週期性變化的步長和幅度有助於學習位置關係。</p><p>直接加 i 和 j 好像也沒有很好的辦法？</p><h3 id="2-Masked-Self-Attention"><a href="#2-Masked-Self-Attention" class="headerlink" title="2. Masked Self-Attention"></a><strong>2. Masked Self-Attention</strong></h3><p>Masked Self-Attention Layer 回到了之前的 RNN attention，又“讓每一個 $s_t$ 和 $c_t$ 依賴於 $s_{t-1}$”，<strong>不過牛在可以並行計算</strong>，下一個位置的 input 只能利用 the past inputs 的信息，置 $-\inf$ 可以讓 attention weights 為 0，即被 Masked。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-19.png" loading="lazy" alt="" width="50%" height="50%"></div><p>在 language modeling tasks 裡很常用，因為能識別語序。</p><h3 id="3-Multihead-Self-Attention"><a href="#3-Multihead-Self-Attention" class="headerlink" title="3. Multihead Self-Attention"></a><strong>3. Multihead Self-Attention</strong></h3><p>步驟是，把 $X_1$ 分成 H 份，把 $X_2$  $X_3$ 都分成 H 份，分別用 H 個 self-attention layer 計算 Y，最後把 H 個結果 concatenate 到一起得到最終的 Y。</p><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-21.png" loading="lazy" alt="" width="50%" height="50%"></div><p>2 個 Hyper Parameters，Query 和 Key 的維度 $D_Q$ 和 #Heads 。多頭也很常用。</p><p>Question：Dv怎麼設置？</p><h3 id="Layer-Example"><a href="#Layer-Example" class="headerlink" title="Layer Example"></a><strong>Layer Example</strong></h3><div align=center><img src="/pics/cs231n/498_FA2019_lecture13-20.png" loading="lazy" alt="" width="50%" height="50%"></div><p>這裡 Attention Weights 的 Intuition 是，Input 的每一個位置有多關注 (attend) 其他 Input 的所有位置。這樣跟 Values 相乘計算的 Y 依賴於 Input 所有位置。</p><p>跟 Conv 的卷積和 FC 的純粹 Weighted Sum 都是有點類似的思路，但是完全不一樣的計算方法, a very different type of computation。</p><p>這就是一個完全不同的層，比如我們可以 Conv x N — Self-Attention x N — Conv x N — FC x N 這樣搭新的積木。</p><h3 id="Comparison"><a href="#Comparison" class="headerlink" title="Comparison"></a><strong>Comparison</strong></h3><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/498_FA2019_lecture13-22.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-23.png" alt=""></td></tr></tbody></table></div><p>self-attention 的需要很多 memory 存參數的問題，對現在的 GPU 不太是大問題。</p><p>對 sequence 問題來說，Attention is all you need，不需要考慮 LSTM 和 CNN。2017 年這篇文章用 self-attention layer 設計了 Transformer Block，</p><div class="table-container"><table><thead><tr><th>因為這些independency，所以transformer的擴展性和并行性都特別好——</th><th>scalability和parallelism性質好是相輔相成的，因為不像lstm一樣依賴序列順序計算，所以並行性好，矩陣在 GPU 的並行高效運算提高了效率，也就讓模型可以擴展到更大模型和更長序列。</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/498_FA2019_lecture13-25.png" alt=""></td><td><img src="/pics/cs231n/498_FA2019_lecture13-24.png" alt=""></td></tr></tbody></table></div><ol><li>Layer Norm 做的是 normalize each of the output vectors from self-attention independently，效果很好；</li><li>每一個 MLP 也是會 independently 處理每個 output vector；</li><li>residual 讓梯度流更 clean 的做法仍然很好用，MLP 之後也是一樣加個 Layer Norm。</li></ol><p>在已經忘記 Transformer 結構的前提下，<a href="#cifar-10-open-ended-challenge">我做的之前 ass2 的實驗</a>，batch norm的好結果跟後來模型的選擇也確實是不謀而合。</p><p>從 Transformer 開始，CV裡常用的Pretrain-Finetune模式也開始大規模發生在NLP領域，2017年發表了Transformer，2018年各大AI Lab都開始competing訓練bigger and bigger and bigger transformer models，Layers、Width（Dq）、Heads都大幅增加。Data雖然是40G但是對text來說已經很多很多了。</p><p>這裡的公式好像寫錯了？Key和Value應該是同樣維度，而Query是不同維度？Aij的求和方式也不太對，但是代碼裡其實沒有顯式地求和。</p><p>Google  Facebook  OpenAI  Nvidia Megatron  變形金剛到霸天虎？OpenAI當時的文本生成就做得很好。</p><h2 id="Lecture-18-Self-Supervised-Learning"><a href="#Lecture-18-Self-Supervised-Learning" class="headerlink" title="Lecture 18 Self-Supervised Learning"></a><strong>Lecture 18 Self-Supervised Learning</strong></h2><p><a href="https://cs231n.stanford.edu/slides/2021/lecture_13.pdf">2021的Lecture 13。</a></p><p>剛寫完 GANs 先用 Generative 任務跟 Self-supervised做個對比：</p><ul><li>兩者目標都是<strong>從沒有 人工標注的數據 (data without manual label annotation) 中學習</strong>。</li><li>區別：<ul><li>generative learning 目標是學習到 $p_{data}(x)$ 這個數據分佈，比如 VAE 就是直接學習正態分佈的 mean 和 variance 來採樣足夠真實的生成數據。</li><li>self-supervised learning 目標是通過解決 <strong>“pretext” tasks</strong> 來產生足夠應付 <strong>下游任務(downstream tasks)</strong> 的 features；<ul><li>training 的時候實際上還是用 supervised leanring objectives，還是用 classification 或 regression 任務；</li><li>這些“預訓練任務/預文本任務” pretext tasks 的 <strong>labels 都是自動生成的</strong>。</li></ul></li></ul></li></ul><div align=center><img src="/pics/cs231n/Fig-Left-Drawing-of-a-dollar-bill-from-memory-Right-Drawing-subsequently-made-with-a.ppm.png" loading="lazy" alt="On the left, a person's sketch of a dollar bill from memory. On the right, the same person's sketch with access to a reference. This indicates that humans only remember and can perform recognition with only a small handful of salient aspects of data and have substantial detail invariance." width="50%" height="50%"></div><p>就像這個經典例子，證明人類只通過記住一些顯著要素 salient aspects 來認知世界，</p><ul><li>根本不需要學習到能畫出 / generate pixel-level details 的地步，</li><li>學習 high-level semantic features with pretext tasks 就足夠了。</li></ul><div class="table-container"><table><thead><tr><th>通過下游任務的表現來評價自監督學習</th><th>這門課只講了CV的pretext</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-1.png" alt=""></td><td><img src="/pics/cs231n/lecture_13-2021.png" alt=""></td></tr></tbody></table></div><p>The reason SSL methods have seen a surge in popularity is because the learnt model continues to perform well on other datasets as well i.e. new datasets on which the model was not trained on!</p><h3 id="Confusion-Matrix"><a href="#Confusion-Matrix" class="headerlink" title="Confusion Matrix"></a><strong>Confusion Matrix</strong></h3><p>Confusion Matrix 混淆矩陣，裡面包含很多很多種 metrics 衡量任務表現的指標，拿這個問chatgpt的gpt4幾次它就算錯幾次。</p><p>假設現在做貓狗分類任務，🐱：30只，🐶：70只。</p><p>預測結果：</p><ul><li>識別出🐱20只，其中18只是🐱，2只識別錯了其實是🐶；</li><li>識別出🐶80只，其中68只是🐶，12只識別錯了其實是🐱；</li></ul><p>假設🐱是正例，🐶是負例：</p><ul><li>True Positive: 這裡所有的 True 和 False 都是模型預測正確與否，即是否把🐱分類為🐱把🐶分類為🐶，Positive 和 Negative 是模型預測的類別。</li><li>True Positive: 18只識別正確的🐱；</li><li>False Postive：2只識別錯誤的🐶；</li><li>True Negative：68只識別正確的🐶；</li><li>False Negative：12只識別錯誤的🐱。</li></ul><p>最需要記住的其實就是前面這個布爾值是“<strong>評估</strong>”模型是否正確，後面這個正負量是模型“<strong>推理的結果</strong>”。</p><p>然後其他所有的 Measures 找一個基準點就好理解，用最熟悉的“準確率”Accuracy作為參照物：</p><ul><li>ACC = (TP + TN) / (P + N)，準確率，分子是兩個 T，分母是所有樣本，也就是所有數據裡模型預測對的比例，模型最直觀的能力。上面的例子就是 86%。</li><li>Recall，有的地方叫Sensitivity敏感性，= TPR = TP / (TP + FN) = 分類正確的🐱 18 / 所有真正的🐱 30 = 60%。有時我們只關心能不能把貓貓🐱全給找出來，所以<strong>只對正例🐱算的acc準確率</strong>就是召回率。<ul><li>癌症推斷場景是很好的例子，我們希望所有的癌症陽性患者都被檢測出來，寧錯殺不放過；</li><li>商湯的垃圾桶滿溢做了半年過去兩年我現在才理解，我們關心的是所有滿的垃圾桶都要報警防止污染環境，不那麼關心有沒滿的垃圾桶被預測為滿的讓人空跑一場（反正半滿也能先清一下）。</li></ul></li><li>Precision，有時跟acc會混叫準確度，其實該叫精度，Positive Predictive Value, PPV = TP / (TP + FP) = 分類正確的🐱 18 / 所有被分類為🐱的🐱和🐶 20 = 90%。<ul><li>還是上面那個場景，在滿足了高召回率recall之後，我們也希望precision高，即希望不要把健康人分類為癌症患者。</li></ul></li><li>Specificity, SPC = TN / (FP + TN) = 分類正確的🐶 68 / 所有真正🐶 70 = 97%。<ul><li>這個跟recall類似，就是關心把所有🐶給找出來；</li><li>也可理解成跟precision一樣關注假陽性（FP）儘量少。</li></ul></li><li>F1 score = 2TP / (2TP + FP + FN)<ul><li>$F1 = \frac{2 \times \text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}}$</li><li>是精度和召回率的調和平均數，調和平均數就是倒數的平均數：<ul><li>$F1 = \frac{2}{\frac{1}{\text{Precision}} + \frac{1}{\text{Recall}}}$</li></ul></li><li>Intuition就是希望在recall和precision之間找個平衡，既希望把所有的🐱找出來，又希望不要把🐱錯分為🐶；既希望不論對錯把所有疑似癌症患者找出來，又希望別把太多健康人錯診，一個平衡量。</li></ul></li><li>F1這種“平衡”為什麼不能直接用ACC？因為 ACC 的分子有 TN 🐶，F1 只有 TP 🐱。<ul><li>癌症篩查裡，正負樣本不平衡，癌症人數遠遠少於正常人數，<strong>模型會通過正確預測健康人群 (TN) 提高準確率</strong>，ACC也會變大。</li></ul></li><li>AUC-ROC, Area Under Curve Receiver Operating Characteristic，評估二分類模型，反正值域 <code>[0, 1]</code> 越大越好。<ul><li>ROC適用於均衡或不均衡的數據集分類；</li><li>Precision-Recall 更適用於不均衡數據集，這個曲線可能是各種樣子，precision和recall也可能同時為1。</li></ul></li></ul><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/precision-recall.png" alt=""></td><td><img src="/pics/cs231n/evaluation-measures-for-precision-recall.png.webp" alt=""></td></tr></tbody></table></div><ul><li>IoU（Intersection over Union）：用于目标检测和分割任务，衡量预测框和真实框之间的重叠度。<ul><li>$\text{IoU} = \frac{\text{预测区域} \cap \text{真实区域}}{\text{预测区域} \cup \text{真实区域}}$</li></ul></li><li>% mAP（Mean Average Precision）平均精度，mAP 评估的是检测或分类模型的精度。<ol><li>AP是算上面說的 Precision-Recall 曲線下面積 PR-AUC；</li><li>mAP是求不管正負例雞鴨鵝狗例的，所有類別AP均值。</li></ol></li><li>% mIoU（Mean Intersection over Union）平均交并比，mIoU 评估的是分割模型的质量。<ol><li>對每個類別計算IoU；</li><li>計算所有類別平均IoU。</li></ol></li></ul><p>Agenda:</p><ul><li>Pretext tasks from image transformations<ul><li>Rotation, inpainting, rearrangement, coloring</li></ul></li><li>Contrastive representation learning<ul><li>Intuition and formulation</li><li>Instance contrastive learning: SimCLR and MOCO</li><li>Sequence contrastive learning: CPC</li></ul></li></ul><h3 id="Pretext-tasks-from-image-transformations"><a href="#Pretext-tasks-from-image-transformations" class="headerlink" title="Pretext tasks from image transformations"></a><strong>Pretext tasks from image transformations</strong></h3><p>現在的驗證碼，不是讓人旋轉圖形，就是讓人拼圖，或者讓人組合打亂的字，再不然就是既旋轉又拼圖又排列組合。跟這些pretext tasks一毛一樣。</p><div align=center><img src="/pics/cs231n/lecture_13-2021-2.png" loading="lazy" alt="" width="50%" height="50%"></div><h4 id="rotations"><a href="#rotations" class="headerlink" title="rotations"></a><strong>rotations</strong></h4><p>Hypothesis: 一個模型只有具備視覺常識，即知道沒擾動的物體應該是什麼樣子，才能正確識別物體的旋轉。</p><p>可以不當作 regression 判斷旋轉度數，而是只用 4 種旋轉方式。</p><div align=center><img src="/pics/cs231n/lecture_13-2021-3.png" loading="lazy" alt="" width="50%" height="50%"></div><p>做法是在 CIFAR10 整個數據集上自監督訓練，然後下游任務比如 classification 就 freeze 前兩層 conv1 conv2，只用一個 CIFAR10 的子集 fine tune 後兩層 conv3 linear。</p><p>最終大規模跑的實驗是用 AlexNet 在整個 ImageNet 自監督訓練，在 Pascal Voc 2007 上 finetune 了 classifcation(%mAP)、detection(%mAP)、segmentation(%mIoU) 三個任務。</p><h4 id="inpainting"><a href="#inpainting" class="headerlink" title="inpainting"></a><strong>inpainting</strong></h4><p>圖像填充 / 圖像修復。</p><div align=center><img src="/pics/cs231n/lecture_13-2021-6.png" loading="lazy" alt="" width="50%" height="50%"></div><script type="math/tex; mode=display">L_{\text{total}} = L_{\text{reconstruction}} + \lambda L_{\text{adversarial}}</script><script type="math/tex; mode=display">\begin{aligned}& L(x)=L_{\text {recon }}(x)+L_{a d v}(x) \\& L_{\text {recon }}(x)=\left\|M *\left(x-F_\theta((1-M) * x)\right)\right\|_2^2 \\& \left.L_{a d v}=\max _D \mathbb{E}[\log (D(x))]+\log (1-D(F((1-M) * x)))\right]\end{aligned}</script><p>Adversarial loss between “real” images and inpainted images，先用重建損失訓練generator，然後用對抗損失的半部分加強這個loss $L_{\text{adversarial}} = - \log D(\hat{I})$ 。</p><h4 id="rearrangement"><a href="#rearrangement" class="headerlink" title="rearrangement"></a><strong>rearrangement</strong></h4><div class="table-container"><table><thead><tr><th>判斷patch的相對位置，predict relative patch locations</th><th>jigsaw拼圖，solving jigsaw puzzles</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-4.png" alt=""></td><td><img src="/pics/cs231n/lecture_13-2021-5.png" alt=""></td></tr></tbody></table></div><p>9個數permutation排列不是一共9!=362,880種嗎？</p><h4 id="coloring"><a href="#coloring" class="headerlink" title="coloring"></a><strong>coloring</strong></h4><div align=center><img src="/pics/cs231n/lecture_13-2021-7.png" loading="lazy" alt="" width="50%" height="50%"></div><p>通過一個channel預測其他channel的值，最後concatenate到一起。</p><h3 id="Contrastive-representation-learning"><a href="#Contrastive-representation-learning" class="headerlink" title="Contrastive representation learning"></a><strong>Contrastive representation learning</strong></h3><p>上面所有pretext tasks都會導致學習到的特徵綁定在一種任務上，下面這種pretext task可以集合他們所有的perturbation的優勢。（perturb 擾動和 permute 排列別混了）</p><div align=center><img src="/pics/cs231n/lecture_13-2021-8.png" loading="lazy" alt="" width="50%" height="50%"></div><p>希望所有被擾動的樣本都互相attract，非同一類的樣本都互相排斥repel，可以得到這個pretext task的目標：</p><script type="math/tex; mode=display">\operatorname{score}\left(f(x), f\left(x^{+}\right)\right)>>\operatorname{score}\left(f(x), f\left(x^{-}\right)\right)</script><div align=center><img src="/pics/cs231n/lecture_13-2021-9.png" loading="lazy" alt="意思是希望從 N 個 samples 裡給那個 positive sample 給正例打高分" width="50%" height="50%"></div><p>綠色的分子和分母代表正例和參照圖的pair，紅色的分母代表其他所有負例和參照圖的pair，這合起來就是一個 <strong>Cross entropy loss for a N-way softmax classifier!</strong></p><p>代入普通的多分類交叉熵損失，這裡的正負例對應各類別的分數，<strong>這個Loss的意思就是試圖從 N 個 samples 裡找到那個正樣本 positive sample。</strong></p><div align=center><img src="/pics/cs231n/lecture_13-2021-10.png" loading="lazy" alt="" width="50%" height="50%"></div><ul><li>叫信息對比損失InfoNCE Loss是因為基於一個概率模型估計方法Noise Contrastive Estimation (NCE, 2010的paper)。</li><li>這裡的MI是Mutual Information互信息，即經過訓練後增強的圖片和原圖在特徵空間的距離，我們希望這個值即兩者在表示空間的距離盡可能近。</li><li>公式的直觀意思是 infoNCE 是 MI - log(負樣本數) 的下界：<ul><li>改成 $\text{MI}[f(x), f(x^+)] \geq \log(N) - L$ 好理解點；</li><li>增大負樣本 N 數可以讓 增強圖 和 原圖 在特徵空間距離下界更高，即更近、更好。</li></ul></li></ul><h3 id="SimCLR-and-MOCO"><a href="#SimCLR-and-MOCO" class="headerlink" title="SimCLR and MOCO"></a><strong>SimCLR and MOCO</strong></h3><p>Instance contrastive learning</p><p>SimCLR就是設定s為cosine similarity？</p><div class="table-container"><table><thead><tr><th>SimCLR, A Simple Framework for Contrastive Learning of Visual Representations, Google</th><th>所有的data augmentation</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-11.png" alt=""></td><td><img src="/pics/cs231n/lecture_13-2021-12.png" alt=""></td></tr></tbody></table></div><p>我們其實只需要 f 變換之後的特徵 h 用於下游任務，但還是多變換了一次 g，因為這個線性/非線性的 projection head 提升了特徵學習的效果。</p><ul><li><a href="https://arxiv.org/pdf/2002.05709">paper</a>的4.2. A nonlinear projection head improves the representation quality of the layer before it和B.4. Understanding The Non-Linear Projection Head講了加這個 投影頭 的好處。</li></ul><div class="table-container"><table><thead><tr><th>projection head</th><th>projection</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-17.png" alt=""></td><td><img src="/pics/cs231n/3d-2d-projection.jpg" alt=""></td></tr></tbody></table></div><ul><li>為什麼叫projection head，我理解就是個降維操作，類似<a href="#pca-t-sne">PCA和t-SNE</a>，投影本身就是一種降維，這些都在<a href="#降維">降維</a>裡復習，<strong>而且 f(x) 是個很深的網絡 like resnet，g(h) 只是個簡單的MLP，</strong>又因為在其他網絡頂上，所以叫投影頭；</li></ul><div class="table-container"><table><thead><tr><th>pseudo code</th><th>assignment3最後一個作業實現方式跟 paper 不一樣，這個公式更容易寫代碼</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-13.png" alt=""></td><td><img src="/pics/cs231n/lecture_13-2021-14.png" alt=""></td></tr></tbody></table></div><p>寫 ass 的時候能感覺到，<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">sim_matrix = compute_sim_matrix(out)  <span class="comment"># [2*N, 2*N]</span></span><br><span class="line">sim_vector = sim_positive_pairs(out_left, out_right)  <span class="comment"># (N, 1)</span></span><br></pre></td></tr></table></figure></p><p>這裡有個問題，根據上面的 $\text{MI}[f(x), f(x^+)] \geq \log(N) - L$ 可以知道，負樣本越多特徵學得越好，<br><a href="https://arxiv.org/pdf/2002.05709">simclr 文章</a>中提到，</p><blockquote><p>To keep it simple, we do not train the model with a memory bank (Wu et al., 2018; He et al., 2019). Instead, we vary the training batch size N from 256 to 8192. A batch size of 8192 gives us 16382 negative examples per positive pair from both augmentation views. Training with large batch size may be unstable when using standard SGD/Momentum with linear learning rate scaling (Goyal et al., 2017). To stabilize the training, we use the LARS optimizer (You et al., 2017) for all batch sizes. We train our model with Cloud TPUs, using 32 to 128 cores depending on the batch size.2<br>As the loss, we use NT-Xent, optimized using LARS with learning rate of 4.8 (= 0.3 ×BatchSize/256) and weight decay of 10−6.</p></blockquote><p>最後選擇的是 We train at batch size 4096 for 100 epochs. ，縮小 batch size 會讓 performance 變差。<br>一次計算完 batch (e.g. 4096 張) 裡所有元素的 sim_matrix 開銷很大，2 個 augmentation 之後變成 8192 張，每個 minibatch 算 sim_matrix 都需要 8192x8192=67108864 ops。</p><p>而 <a href="https://arxiv.org/pdf/1911.05722">moco 文章</a>中提到，他們在 ImageNet 1K 的 ～1.28M imgs 1000 classes 和 Instagram 的 ～940M imgs ～1500 hashtags 數據集上的選擇是，</p><blockquote><p>Training. We use SGD as our optimizer. The SGD weight decay is 0.0001 and the SGD momentum is 0.9. For IN-1M, we use a mini-batch size of 256 (N in Algorithm 1) in 8 GPUs, and an initial learning rate of 0.03. We train for 200 epochs with the learning rate multiplied by 0.1 at 120 and 160 epochs [61], taking∼53 hours training ResNet-50. For IG-1B, we use a mini-batch size of 1024 in 64 GPUs, and a learning rate of 0.12 which is exponentially decayed by 0.9×after every 62.5k iterations (64M images). We train for 1.25M iterations (∼1.4 epochs of IG-1B), taking∼6 days for ResNet-50.</p></blockquote><p>對 IG-1B 選用 batch size 1024，假如跟 simclr 一樣負樣本 size 也是 4096 (queue size K)，那麼根據下面 pseudocode，</p><ul><li>首先這裡不是計算 4096x2=8192 個負樣本，query 也是 1024 而不是 2048；</li><li><p>那麼 sim_matrix 總操作就是 query(batch size)*queue(K)=1024x4096=4194304，每個batch小了一個數量級；</p></li><li><p>跟 simclr 一樣，比如 batch size 256，ref img 是 🐱，<strong>就算剩下 255 imgs 裡有 🐱 也會被當作負樣本</strong>，這個似乎是 self-supervised 無法解決的問題？</p></li></ul><p>但是如果 batch size 都選用 4096，K 選用遠大於 batch size 的 65536，那麼其實一個 batch / iteration / epoch 的計算量 moco 是大於 simclr 的，我認為 moco 的主要優勢在於，</p><ol><li>這其實是 memory bank 的優勢，每個 batch 接觸到的負樣本比正樣本多很多，從比例上看更多操作量分配到 repel 負樣本上了，更少 attract 正樣本，從結果上看效果更好；</li><li>一個 batch 的正樣本和負樣本都來自 momentum 更新的 f_k，ref img 本身是生成自 f_q，前期更新速度慢，訓練穩定，負樣本特徵空間的噪聲小：$\theta_{\text{momentum}} \leftarrow m \cdot \theta_{\text{momentum}} + (1 - m) \cdot \theta_{\text{model}}$, theta 設置如下有消融實驗。</li></ol><div align=center><img src="/pics/cs231n/Screenshot 2024-12-12 at 14.12.07.png" loading="lazy" alt="" width="50%" height="50%"></div><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/cs231n/Screenshot 2024-12-12 at 14.21.11.png" alt=""></td><td><img src="/pics/cs231n/MoCo.png" alt=""></td></tr></tbody></table></div><div align=center><img src="/pics/cs231n/Screenshot 2024-12-12 at 14.23.13.png" loading="lazy" alt="" width="50%" height="50%"></div><p>moco 對比 memory bank：</p><ol><li>對比學習負例越多久越難（正例越多也越難吧），兩者思路都是增加負例，都是解耦 decouple 了 batchsize 和 number of negatives；</li><li>memory bank 是把 f_k 計算過的負樣本 feature 全都存起來作為負例，<ul><li>f_k 不變會導致跟 f_q 特徵空間差別太大學到的東西很差，本來希望 🐱 repel 🐶 這種負例，如果特徵空間不一樣 🐶 可能變成了外星 🐱 即 paper 裡講的 consistency ；</li></ul></li><li>用 queue 解決內存和計算限制的問題，盡可能多但不是用全部負樣本；用動量更新 f_k 解決了希望負樣本多的情況下兩側 encoder alignment 的問題。</li></ol><blockquote><p>The memory bank mechanism can support a larger dictionary size. But it is 2.6% worse than MoCo. This is inline with our hypothesis: the keys in the memory bank are from very different encoders all over the past epoch and they are not consistent.</p></blockquote><div class="table-container"><table><thead><tr><th>MoCo, Momentum Contrastive Learning (v1居然還是何愷明做的)</th><th>pseudo code</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-15.png" alt=""></td><td><img src="/pics/cs231n/lecture_13-2021-16.png" alt=""></td></tr></tbody></table></div><div align=center><img src="/pics/cs231n/moco.jpg" loading="lazy" alt="黑色是previous batches的負樣本，白色是current batch的負樣本。" width="50%" height="50%"></div><p>實驗量很大，主要貢獻感覺是工程上的。</p><p>想理解模型還是看代碼最快，code 缺實現細節再從論文裡摳。</p><h3 id="CLIP"><a href="#CLIP" class="headerlink" title="CLIP"></a><strong>CLIP</strong></h3><div class="table-container"><table><thead><tr><th>text vs. img</th><th>numpy like pseudocode</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/lecture_13-2021-18.png" alt=""></td><td><img src="/pics/cs231n/CLIP-algo.png" alt=""></td></tr></tbody></table></div><p>把爬下來圖文配對數據處理一下，每一段文本對應一個圖片，整段文本編碼成 1 個向量表示，是整個句子的 semantic embedding，跟圖片做對比損失。</p><h3 id="CPC"><a href="#CPC" class="headerlink" title="CPC"></a><strong>CPC</strong></h3><p>Sequence contrastive learning</p><h3 id="Bert-GPT-T5"><a href="#Bert-GPT-T5" class="headerlink" title="Bert/GPT/T5"></a><strong>Bert/GPT/T5</strong></h3><p>雖然都基於transformer，但pretext task和工程實現差別很大。</p><h4 id="1-BERT-Bidirectional-Encoder-Representations-from-Transformers"><a href="#1-BERT-Bidirectional-Encoder-Representations-from-Transformers" class="headerlink" title="1. BERT (Bidirectional Encoder Representations from Transformers)"></a>1. <strong>BERT (Bidirectional Encoder Representations from Transformers)</strong></h4><ul><li><p><strong>Pretext Task</strong>: <strong>Masked Language Modeling (MLM)</strong> 和 <strong>Next Sentence Prediction (NSP)</strong>。</p><ul><li><p><strong>Masked Language Modeling (MLM)</strong>: BERT 的核心任务是随机遮蔽输入文本中的一些词（即使用 <code>[MASK]</code> 标记代替某些词），然后让模型根据上下文预测被遮蔽的词。这个任务使得 BERT 学习到输入序列中每个词的上下文信息。由于它是双向的，BERT 会同时利用左侧和右侧的上下文来做出预测。</p></li><li><p><strong>Next Sentence Prediction (NSP)</strong>: 这个任务用于帮助 BERT 学习句子间的关系。给定一对句子，模型需要预测第二个句子是否是第一个句子的后续句子。这个任务有助于捕捉句子间的语义关系，尤其对于问答和自然语言推理任务（如SQuAD）很有帮助。</p></li></ul></li></ul><h4 id="2-GPT-Generative-Pre-trained-Transformer"><a href="#2-GPT-Generative-Pre-trained-Transformer" class="headerlink" title="2. GPT (Generative Pre-trained Transformer)"></a>2. <strong>GPT (Generative Pre-trained Transformer)</strong></h4><ul><li><p><strong>Pretext Task</strong>: <strong>Causal Language Modeling (Autoregressive Language Modeling)</strong>。</p><p>GPT 的预训练任务是 <strong>因果语言建模</strong>，也叫 <strong>自回归语言建模</strong>。在这种设置中，GPT 的目标是预测序列中每个词的下一个词。具体来说，给定输入文本的前面一部分，模型学习如何生成文本的下一部分。GPT 模型是单向的（从左到右），只利用前面的上下文来预测下一个词。这种任务让 GPT 能够学习文本生成和语言建模的能力。</p></li></ul><h4 id="3-T5-Text-to-Text-Transfer-Transformer"><a href="#3-T5-Text-to-Text-Transfer-Transformer" class="headerlink" title="3. T5 (Text-to-Text Transfer Transformer)"></a>3. <strong>T5 (Text-to-Text Transfer Transformer)</strong></h4><ul><li><p><strong>Pretext Task</strong>: <strong>Text-to-Text Transformation (denoising autoencoding)</strong>。</p><p>T5 的预训练任务可以看作是 <strong>去噪自编码</strong>（Denoising Autoencoding）。具体来说，T5 将所有任务统一为文本到文本的格式。例如，在预训练时，T5 会将输入文本进行部分遮蔽，然后要求模型生成被遮蔽的部分（类似于 BERT 的 MLM，但 T5 是整体作为文本输入，输入和输出都是文本）。通过这种方式，T5 学习到文本的生成与理解，并能够处理多种任务（例如翻译、摘要、问答等）。</p><p>T5 的一个关键特点是，它采用了统一的输入和输出形式——所有任务都被视为“文本到文本”的问题。因此，T5 的预训练任务包括了多种形式的任务，如填空、翻译、摘要等。</p></li></ul><h4 id="总结：-3"><a href="#总结：-3" class="headerlink" title="总结："></a><strong>总结</strong>：</h4><ul><li><strong>BERT</strong>：使用 <strong>Masked Language Modeling (MLM)</strong> 和 <strong>Next Sentence Prediction (NSP)</strong>。</li><li><strong>GPT</strong>：使用 <strong>Causal Language Modeling (自回归语言建模)</strong>。</li><li><strong>T5</strong>：使用 <strong>Text-to-Text Transformation (去噪自编码)</strong>，通过将输入文本进行变换来训练模型。</li></ul><div class="table-container"><table><thead><tr><th><strong>模型</strong></th><th><strong>使用的 Transformer 部分</strong></th><th><strong>双向/单向</strong></th><th><strong>Pretext Task</strong></th><th><strong>主要特点</strong></th><th><strong>據chatgpt說的擅長任務</strong></th></tr></thead><tbody><tr><td><strong>BERT</strong></td><td>编码器（Encoder）</td><td>双向（Bidirectional） — <code>I love playing soccer on the weekend.</code> 用 I love playing 和 on the weekend 預測 soccer</td><td>Masked Language Modeling (MLM)，Next Sentence Prediction (NSP)</td><td>只使用编码器，双向理解上下文，适用于理解任务。</td><td>理解任务：文本分类、命名实体识别、情感分析、自然语言推理等</td></tr><tr><td><strong>GPT</strong></td><td>解码器（Decoder）</td><td>单向（Unidirectional） — <code>I love playing</code> → <code>soccer</code> → <code>on</code> → <code>the</code> → <code>weekend</code></td><td>Causal Language Modeling</td><td>只使用解码器，自回归模型，生成文本。</td><td>生成任务：文本生成、对话生成、文章续写等</td></tr><tr><td><strong>T5</strong></td><td>编码器 + 解码器（Encoder-Decoder）</td><td>双向/单向（Bidirectional/Unidirectional） — 依任务而定?</td><td>Text-to-Text (Denoising Autoencoding)</td><td>结合编码器和解码器，统一文本到文本任务，适用于多任务。</td><td>多任务：翻译、摘要、问答、文本分类等</td></tr></tbody></table></div><h2 id="降維"><a href="#降維" class="headerlink" title="降維"></a><strong>降維</strong></h2><p>dimensionality reduction</p><p>Autoencoder 和 conv / pooling 都算降維，conv 不光能用大 stride 減小 H, W 維度，還能用 1x1 減少 channel 數。</p><h3 id="PCA-t-SNE"><a href="#PCA-t-SNE" class="headerlink" title="PCA, t-SNE"></a><strong>PCA, t-SNE</strong></h3><p>跟投影的區別？目标：投影通常是为了将数据映射到一个子空间，并且不一定考虑减少信息损失，除非该投影是最佳的（例如，主成分分析（PCA）中的投影）。</p><div class="table-container"><table><thead><tr><th><strong>特性</strong></th><th><strong>PCA</strong></th><th><strong>t-SNE</strong></th></tr></thead><tbody><tr><td><strong>降维类型</strong></td><td>线性降维</td><td>非线性降维</td></tr><tr><td><strong>保留的信息</strong></td><td>保留全局结构（数据的方差）</td><td>保留局部结构（相似数据点的关系）</td></tr><tr><td><strong>适用情况</strong></td><td>适用于线性可分的数据集</td><td>适用于非线性、复杂的数据集</td></tr><tr><td><strong>可解释性</strong></td><td>可以通过主成分解释数据的方差</td><td>不容易解释低维嵌入的含义</td></tr><tr><td><strong>可视化效果</strong></td><td>更适合全局结构的可视化</td><td>更适合展示局部结构和聚类效果</td></tr><tr><td><strong>计算复杂度</strong></td><td>相对较快，适用于大规模数据集</td><td>计算时间较长，尤其对于大数据集</td></tr><tr><td><strong>PyTorch 使用方法</strong></td><td><code>torch.pca_lowrank()</code> 或通过自定义实现特征值分解</td><td><a href="https://github.com/CannyLab/tsne-cuda">from tsnecuda import TSNE</a> <code>TSNE(n_components=2, perplexity=15, learning_rate=10).fit_transform(X)</code>, TSNE創建對象fit_transform實現降維</td></tr><tr><td><strong>Numpy 使用方法</strong></td><td><code>np.linalg.svd()</code> 或 <code>np.linalg.eig()</code></td><td>使用 <code>sklearn.manifold.TSNE()</code>，Numpy 主要用于数据操作</td></tr><tr><td><strong>关键步骤</strong></td><td>1. 标准化数据（均值为0，方差为1）。<br>2. 计算协方差矩阵。<br>3. 计算协方差矩阵的特征值和特征向量。<br>4. 选择最重要的主成分并将数据投影到这些主成分上。</td><td>1. 计算高维空间中每对点之间的相似性（通常使用高斯分布）。<br>2. 在低维空间中随机初始化点的位置。<br>3. 通过优化算法调整低维点的位置，使得相似点在低维空间中靠得更近。<br>4. 使用 t-分布（Student’s t-distribution）来计算低维空间中的相似性，并减少拥挤现象。</td></tr></tbody></table></div><h2 id="Lecture-19-3D-Vision"><a href="#Lecture-19-3D-Vision" class="headerlink" title="Lecture 19 3D Vision"></a><strong>Lecture 19 3D Vision</strong></h2><p><a href="https://www.youtube.com/watch?v=S1_nCdLUQQ8&amp;list=PL5-TkQAfAZFbzxjBHtzdVCWE0Zbhomg7r&amp;index=18">不全看，對分子生成有啟發。</a></p><h1 id="Assignments"><a href="#Assignments" class="headerlink" title="Assignments"></a><strong>Assignments</strong></h1><p><a href="https://cs231n.stanford.edu/assignments.html">https://cs231n.stanford.edu/assignments.html</a><br>assignment 1 和 2 都是做了 2024 版的，3 在這之外會額外做 2017 版的 Visualization 等。<br>大部分內容（代碼/理論）上面和四年前的博客都已經寫過了，這裡是殘羹剩飯。</p><h2 id="CIFAR-10-open-ended-challenge"><a href="#CIFAR-10-open-ended-challenge" class="headerlink" title="CIFAR-10 open-ended challenge"></a><strong>CIFAR-10 open-ended challenge</strong></h2><p>Assignment 2 PyTorch.ipynb 2024 version Part V</p><h3 id="Idea-0"><a href="#Idea-0" class="headerlink" title="Idea 0"></a><strong>Idea 0</strong></h3><ul><li>Convolutional layer (with bias) with 32 5x5 filters, with zero-padding of 2 (N x 32 x 32 x 32)</li><li>ReLU</li><li>Convolutional layer (with bias) with 16 3x3 filters, with zero-padding of 1 (N x 16 x 32 x 32)</li><li>ReLU</li><li>Fully-connected layer (with bias) to compute scores for 10 classes</li></ul><p>已經 1 個 epoch 就達到了接近 60% 的 accuracy，第一個目標是作業要求的 10 epochs 70%。</p><h3 id="Idea-1"><a href="#Idea-1" class="headerlink" title="Idea 1"></a><strong>Idea 1</strong></h3><p>簡單地增加模型深度，從 3 layer 變成 4 layer，用 batchnorm 輔助擬合加速收斂。學 AlexNet 先變深後變淺，還是保證維度一樣。</p><ul><li>Convolutional layer (with bias) with 64 5x5 filters, with zero-padding of 2, stride=1 (32x32)</li><li>Batchnorm</li><li>ReLU</li><li>Convolutional layer (with bias) with 32 5x5 filters, with zero-padding of 2, stride=1 (32x32)</li><li>Batchnorm</li><li>ReLU</li><li>Convolutional layer (with bias) with 16 3x3 filters, with zero-padding of 1, stride=1 (32x32)</li><li>Batchnorm</li><li>ReLU</li><li><p>Fully-connected layer (with bias) to compute scores for 10 classes</p></li><li><p>沒加 batchnorm 只加一層 64 x 3 x 5 x 5 conv 的時候，10 個 epochs 後 acc 無法超過 10%，也許是模型變深導致優化難度變大，如果只是因為其他超參沒跟著一起變，不至於差到這個程度；</p><ul><li>有的時候不能擬合，有時可以</li></ul></li><li>沒加一層 conv，只加 batchnorm 也導致 acc 變成 10%，可能是因為 BN 潛在引入噪聲導致的正則能力讓小模型沒有能力學習了。<ul><li>比上面只加深不 batchnorm 的情況收斂機率稍微高一些，但就算收斂了也比原先 3 層沒有 batchnorm 時效果差。</li></ul></li><li>結論是在 3 layer 基礎上加深網絡，且用 batchnorm 輔助收斂，即同時加一層 64 x 3 x 5 x 5 conv 和 batchnorm 的 acc 提高 5% 左右，從不到 60% 變到了 65%。</li></ul><p>這裡發現沒圖真不好判斷過擬合等情況，用 pytorch 跟以前一樣存一下 acc_train/val_list 然後畫個曲線：</p><p>用了更深層，3-layer to 4-layer，用了更多參數，32 channels to 64 channels，確實有提升，</p><div class="table-container"><table><thead><tr><th>原始的 3 層，只加 batchnorm</th><th>用 4 層，第一層 conv 32 channels</th><th>第一層 64 效果好點有限</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/3 layer, batchnorm.png" alt=""></td><td><img src="/pics/cs231n/4 layer, batchnorm, 32 channels.png" alt=""></td><td><img src="/pics/cs231n/4 layer, batchnorm, 64 channels.png" alt=""></td></tr></tbody></table></div><h3 id="Idea-2"><a href="#Idea-2" class="headerlink" title="Idea 2"></a><strong>Idea 2</strong></h3><p>發現 train_acc 相比 val_acc 高太多，過擬合嚴重，所以加dropout，如果達不到 70% 的小目標就繼續改架構，如果接近就換 Adam 和 parametric ReLU。</p><p>Block0:<br>Batchnorm - ReLU - Dropout (p=0.2) p 是丟棄概率</p><ul><li>Convolutional layer (with bias) with 64 5x5 filters, with zero-padding of 2, stride=1 (32x32)</li><li>Block0</li><li>Convolutional layer (with bias) with 32 5x5 filters, with zero-padding of 2, stride=1 (32x32)</li><li>Block0</li><li>Convolutional layer (with bias) with 16 3x3 filters, with zero-padding of 1, stride=1 (32x32)</li><li>Block0</li><li>Fully-connected layer (with bias) to compute scores for 10 classes</li></ul><p>0.2時還真到70%了，沃草，成就感拉滿，設置到 0.5 試試，0.5 雖然過擬合減輕了，但實際 acc 變差。</p><div class="table-container"><table><thead><tr><th>加 dropout 正則就達到目標了</th><th>調大正則調多參數效果不太好</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/dropout 0.2.png" alt=""></td><td><img src="/pics/cs231n/128, 0.5.png" alt=""></td></tr></tbody></table></div><p>按計劃，換 prelu 和 adam 1 個 epoch 裡 learning rate 1e-3 比 1e-2 好很多，所以就用 1e-3 懶得調參了。</p><div class="table-container"><table><thead><tr><th>dropout 0.2, prelu, adam 1e-3</th><th>dropout 調大 0.5 效果不好，所以一直用 0.2</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/dropout 0.2, prelu, adam 1e-3.png" alt=""></td><td><img src="/pics/cs231n/dropout 0.5.png" alt=""></td></tr></tbody></table></div><p>跑完 10 個 epochs 確實又好了一點兒，跟之前自己 from scratch 寫時的經驗情況差不多，換這些技術都有效果提升，至少穩定在 70 以上了，但還是過擬合嚴重，dropout 再次設置到 0.5 還是效果不好，改回 0.2 dropout，發現沒加 L2 正則，用優化器內置的 weight_decay 加 L2 正則：</p><div class="table-container"><table><thead><tr><th>1e-3 L2正則</th><th>1e-4 L2正則</th></tr></thead><tbody><tr><td><img src="/pics/cs231n/weight_decay 1e-3.png" alt=""></td><td><img src="/pics/cs231n/weight_decay 1e-4.png" alt=""></td></tr></tbody></table></div><p>先upsampling然後maxpool？圖太小效果未必好，最終結果 acc 是 69.99%…懶得弄了。</p><p>Checking accuracy on test set<br>Got 6999 / 10000 correct (69.99)<br>0.6999</p><p>直接轉實現 resnet、densenet、googlenet、squeeze net 和 visualization。算了暫時不在CIFAR-10上訓了，還是得ImageNet。</p><h2 id="COCO"><a href="#COCO" class="headerlink" title="COCO"></a><strong>COCO</strong></h2><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">train_captions &lt;<span class="class"><span class="keyword">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">400135</span>, <span class="number">17</span>)</span> <span class="title">int32</span></span></span><br><span class="line"><span class="class"><span class="title">train_image_idxs</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">400135</span>,)</span> <span class="title">int32</span></span></span><br><span class="line"><span class="class"><span class="title">val_captions</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">195954</span>, <span class="number">17</span>)</span> <span class="title">int32</span></span></span><br><span class="line"><span class="class"><span class="title">val_image_idxs</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">195954</span>,)</span> <span class="title">int32</span></span></span><br><span class="line"><span class="class"><span class="title">train_features</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">82783</span>, <span class="number">512</span>)</span> <span class="title">float32</span></span></span><br><span class="line"><span class="class"><span class="title">val_features</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">40504</span>, <span class="number">512</span>)</span> <span class="title">float32</span></span></span><br><span class="line"><span class="class"><span class="title">idx_to_word</span> &lt;<span class="title">class</span> '<span class="title">list</span>'&gt; 1004</span></span><br><span class="line"><span class="class"><span class="title">word_to_idx</span> &lt;<span class="title">class</span> '<span class="title">dict</span>'&gt; 1004</span></span><br><span class="line"><span class="class"><span class="title">train_urls</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">82783</span>,)</span> &lt;<span class="title">U63</span></span></span><br><span class="line"><span class="class"><span class="title">val_urls</span> &lt;<span class="title">class</span> '<span class="title">numpy</span>.<span class="title">ndarray</span>'&gt; <span class="params">(<span class="number">40504</span>,)</span> &lt;<span class="title">U63</span></span></span><br></pre></td></tr></table></figure><p>2014 版一共 82783 個 training images 和 40504 個 validation images，原數據共 20 GB，是由 Amazon Mechanical Turk 這個众包平台的全世界工人 Turkers 標注的。<br>400135 是總的 captions 數，82783 一般每個圖 5 個 captions，有的圖不到 5 個所以小於 82783 x 5 = 431915。<br>每個 caption 會用 <code>NULL</code> 填充到最長的 17 words，即每個 caption 文本長度是 17。chatgpt 的 max length 是 2048 個 token。<br><code>idx_to_word</code> 是 <code>&#39;&lt;NULL&gt;&#39;, &#39;&lt;START&gt;&#39;, &#39;&lt;END&gt;&#39;, &#39;&lt;UNK&gt;&#39;, &#39;a&#39;</code> 索引對應 word 的 list，UNK 是 vocabulary 裡沒有的詞的代替 token UNKNOWN。<br><code>word_to_idx</code> 就是反過來對應索引的 dict。<br><code>&lt;U63</code>是 Unicode 字符串，且最多包含 63 个字符。比如 <code>train_urls[0]</code> 是 <code>http://farm4.staticflickr.com/3153/2970773875_164f0c0b83_z.jpg</code> 這個圖片。</p><h1 id="Colab"><a href="#Colab" class="headerlink" title="Colab"></a><strong>Colab</strong></h1><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># **This mounts your Google Drive to the Colab VM.**</span></span><br><span class="line"><span class="keyword">from</span> google.colab <span class="keyword">import</span> drive</span><br><span class="line">drive.mount(<span class="string">'/content/drive'</span>)</span><br></pre></td></tr></table></figure><p>2022在商汤时跟着卢博用熟了jupyter发现非常方便，只是有的操作会很卡。虽然一年半过去，如今试图回忆往昔只剩脑袋空空，不过再打开colab操作起来还是很舒适自然的。本科时觉得colab跟我全权掌控的小云服务器相比有各种不便，读研实习尝试过vivo网易各个公司的infra后发现各家都是大同小异，就像去哪都要适应新的开发环境一样，colab只是把适应对象从公司的gpu clusters换成google cloud而已。<br>再看当时跑的cogview和OFA，其实都只是无脑地跑inference。我一直说深度学习是经验科学无迹可寻，只是因为2020、2021年这些assignments里的代码基础我没玩熟练。普通的computer science对我来说其实就像machine learning一样，计算机的底层系统是怎样运行的我也不甚清楚，一句print(“Hello, World”)背后怎么变成字节码？字节码怎么由cpython的c语言逐条执行/解释/转换成汇编指令？汇编指令又怎么变成机器码？机器码又怎么被cpu处理？<br>这其实跟理解反向传播的计算图一样，没必要每写一个def function():都想清楚cpython在干嘛，没必要每写一个net都想透梯度怎么七十二变，但做哪个任务比如image classification那他的pipeline一定得玩熟，网络的每一个模块是什么作用一定得玩熟。当然可以借助工具，今天我记不起“assert”、“yield”的作用了，记不起Softmax、Batch Normalization的作用了，当然可以查资料，但这都建立在能很快搭一个积木，供尝试Softmax、Batch Normalization的基础上，如果一个最简单任务的pipeline我都没法熟练随手搭建，何谈尝试陌生模块？就好像今天记不起yield的作用，我就必须会写def function():、知道怎么写type()、for-loop才能测试。</p><h2 id="Colab-Pro"><a href="#Colab-Pro" class="headerlink" title="Colab Pro"></a><strong>Colab Pro</strong></h2><p>【一个对话框下，不同gpu每小时消耗计算单元的数量】（具体以系统自动计算为准）：<br>T4(大约2个每小时)<br>A100(大约11.7个每小时)<br>L4（大约4.8个每小时）<br>V100(大约6个每小时)<br>TPU(大约2个每小时)</p><p>CPU 0.07h</p><p>You are not subscribed.<br>Available: 99.99 compute units<br>Usage rate: approximately 0 per hour<br>You have 0 active sessions.</p><p>淘宝用泰铢 47.9 块买了 9.9$ 加 tax 快 80 人民币的 100 个计算资源（比 Colab Pro 稍微划算一些），不知道为什么这么便宜。总之还是比网费便宜多了，而且能督促自己一次写完代码。</p><p>能用 V100 和 A100 已经跟在商汤时的资源类似了，在那边我也只是有两张卡和 cluster 能用而已。可能因為充的錢不夠後來發現只能用 A100 不能用 V100。</p><p>跑GAN的最後一個效果最好但很深的網絡DCGAN(Deeply Convolutional GANs)時，用2到4核（最多超線程8核）的CPU跑時，等了好幾分鐘跑不出250個iteration，一換上最便宜的2560核T4，相同的時間直接整個把Jupyter都跑完了。也是Pytorch底層優化寫得好吧。</p><p>上面跑 assignment 找 better cnn architecture 時兩年來頭一次用上了在商湯時跟大家學到的東西，調架構調參——畫圖——分析實驗結果，感慨。</p><h1 id="Regular-Expression"><a href="#Regular-Expression" class="headerlink" title="Regular Expression"></a><strong>Regular Expression</strong></h1><p>写博客写代码时经常会用regex正则批量修改文本，所以在这里记录一些常用的以免重复造轮子。</p><h2 id="加粗标题"><a href="#加粗标题" class="headerlink" title="加粗标题"></a><strong>加粗标题</strong></h2><p>以此为例，第一行为被替换原文本，第二行为替换后文本。<br><code>^(#+) ((?!\*)(.*))</code><br><code>$1 **$2**</code></p><ol><li>先用<a href="https://wiki.v2beach.cn/Tech/RegularExpression.html">(pattern)</a>匹配markdown标题井号；</li><li>然后是个<a href="https://www.regular-expressions.info/lookaround.html">negative lookhead</a><code>(?!)</code>匹配井号后没有星号的文本，这里用转义符escape character反斜杠。</li></ol><h2 id="图片缩放"><a href="#图片缩放" class="headerlink" title="图片缩放"></a><strong>图片缩放</strong></h2><p>markdown图片语法不方便改大小位置，改成html语法的img比较方便操作，写markdown的时候每个都手敲img又太费劲不如<code>![]()</code>。<br><code>!\[(.*)\]\((.*)\)</code><br><code>&lt;img src=&quot;$2&quot; loading=&quot;lazy&quot; alt=&quot;$1&quot; width=&quot;50%&quot; height=&quot;50%&quot;&gt;</code><br>or<br><code>&lt;div align=center&gt;&lt;img src=&quot;$2&quot; loading=&quot;lazy&quot; alt=&quot;$1&quot; width=&quot;50%&quot; height=&quot;50%&quot;&gt;&lt;/div&gt;</code></p><h2 id="wiki圖片改assets路徑"><a href="#wiki圖片改assets路徑" class="headerlink" title="wiki圖片改assets路徑"></a><strong>wiki圖片改assets路徑</strong></h2><p><code>!\[(.*)\]\(((?!/assets/).*)\)</code><br><code>![$1](/assets/$2)</code></p><h2 id="大小写转换"><a href="#大小写转换" class="headerlink" title="大小写转换"></a><strong>大小写转换</strong></h2><p>小写全部换大写：<br><code>[a-z]</code><br><code>\U$0</code></p><h1 id="小知识"><a href="#小知识" class="headerlink" title="小知识"></a><strong>小知识</strong></h1><ol><li>et al(and others in Latin)跟at all读音就是一样滴。</li><li>“Vanilla”常被视为“基本”或“经典”的代表，因为它是最初的冰淇淋和甜品口味之一，且易于与其他味道搭配。这种简单而纯粹的特性使它成为许多食品的基准，从而赋予其“最初”或“标准”的象征意义。</li><li>他们用的也是google slide(macos)。</li><li>hexo deploy 報錯，<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[Deployer error] 部署异常[ 文件路径 : &#x2F;Users&#x2F;v2beach&#x2F;code&#x2F;blog&#x2F;public&#x2F;pics&#x2F;cs231n&#x2F;Screenshot 2024-11-14 at 01.48.31.png,对象键 : pics&#x2F;cs231n&#x2F;Screenshot 2024-11-14 at 01.48.31.png] </span><br><span class="line"></span><br><span class="line">Error [ResponseTimeoutError]: Response timeout for 60000ms, please increase the timeout or use multipartDownload.</span><br><span class="line">    at Client.requestError (&#x2F;Users&#x2F;v2beach&#x2F;code&#x2F;blog&#x2F;node_modules&#x2F;ali-oss&#x2F;lib&#x2F;client.js:311:11)</span><br><span class="line">    at Client.request (&#x2F;Users&#x2F;v2beach&#x2F;code&#x2F;blog&#x2F;node_modules&#x2F;ali-oss&#x2F;lib&#x2F;client.js:207:22)</span><br><span class="line">    at runMicrotasks (&lt;anonymous&gt;)</span><br><span class="line">    at processTicksAndRejections (internal&#x2F;process&#x2F;task_queues.js:97:5)</span><br></pre></td></tr></table></figure>解決方案，在blog/node_modules/<strong>hexo-deployer-ali-oss/index.js</strong>裡修改timeout時間。<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> result = <span class="keyword">await</span> client.put(objectKey, localPath, &#123;<span class="attr">timeout</span>: <span class="number">120000</span>&#125;);</span><br></pre></td></tr></table></figure></li><li>hexo d -g通過Node.js上傳到ali OSS會越傳越慢，最後超過默認60s timeout，嘗試multipartUpload還是超時，沒有嘗試流式上傳，只能修改timout到2分鐘。<br>hexo在config.yml裡註冊新配置：<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">hexo.extend.deployer.register(<span class="string">'ali-oss'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">args</span>)</span>&#123;</span><br></pre></td></tr></table></figure>图太多不能再塞了，打开这篇文章得加载个大几分钟。</li><li>chatgpt公式prompt：<code>里面的\[\]和\(\)都换成dollar符号</code>。</li><li>x～p(x) 讀作 x follows the distribution p of x，或者 x follows p of x。</li></ol><h1 id="常遇見bug一覽"><a href="#常遇見bug一覽" class="headerlink" title="常遇見bug一覽"></a><strong>常遇見bug一覽</strong></h1><h3 id="1-求梯度dW寫成dw或者反之，非常難查出來。"><a href="#1-求梯度dW寫成dw或者反之，非常難查出來。" class="headerlink" title="1. 求梯度dW寫成dw或者反之，非常難查出來。"></a><strong>1. 求梯度dW寫成dw或者反之，非常難查出來。</strong></h3><h3 id="2-np-max和np-maximum不一樣。"><a href="#2-np-max和np-maximum不一樣。" class="headerlink" title="2. np.max和np.maximum不一樣。"></a><strong>2. np.max和np.maximum不一樣。</strong></h3><div class="table-container"><table><thead><tr><th><strong>功能</strong></th><th><strong><code>np.max</code></strong></th><th><strong><code>np.maximum</code></strong></th></tr></thead><tbody><tr><td><strong>作用</strong></td><td>计算数组的全局最大值或按指定轴计算最大值</td><td>逐元素比较两个输入，返回对应元素的最大值</td></tr><tr><td><strong>输入</strong></td><td>单个数组，可选 <code>axis</code> 参数</td><td>两个数组或一个数组和一个标量</td></tr><tr><td><strong>输出</strong></td><td>标量（全局最大值）或数组（按轴最大值）</td><td>与输入形状相同的数组</td></tr><tr><td><strong>广播支持</strong></td><td>不支持广播</td><td>支持广播</td></tr></tbody></table></div><h3 id="3-總算知道數據穩定啥意思了，比如-zero和exp溢出，所以BN和數值穩定版softmax或者其他穩定技巧才重要。"><a href="#3-總算知道數據穩定啥意思了，比如-zero和exp溢出，所以BN和數值穩定版softmax或者其他穩定技巧才重要。" class="headerlink" title="3. 總算知道數據穩定啥意思了，比如/zero和exp溢出，所以BN和數值穩定版softmax或者其他穩定技巧才重要。"></a><strong>3. 總算知道數據穩定啥意思了，比如/zero和exp溢出，所以BN和數值穩定版softmax或者其他穩定技巧才重要。</strong></h3><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">(Iteration 1 &#x2F; 125) loss: 7.856643</span><br><span class="line">(Epoch 0 &#x2F; 25) train acc: 0.260000; val_acc: 0.184000</span><br><span class="line">&#x2F;content&#x2F;drive&#x2F;MyDrive&#x2F;cs231n&#x2F;assignment2&#x2F;cs231n&#x2F;layers.py:147: RuntimeWarning: overflow encountered in exp</span><br><span class="line">  scores &#x3D; np.exp(x)</span><br><span class="line">&#x2F;content&#x2F;drive&#x2F;MyDrive&#x2F;cs231n&#x2F;assignment2&#x2F;cs231n&#x2F;layers.py:152: RuntimeWarning: invalid value encountered in divide</span><br><span class="line">  p &#x3D; scores &#x2F; sum_exp[:, np.newaxis]</span><br><span class="line">(Epoch 1 &#x2F; 25) train acc: 0.102000; val_acc: 0.087000</span><br><span class="line">(Epoch 2 &#x2F; 25) train acc: 0.102000; val_acc: 0.087000</span><br><span class="line">·</span><br><span class="line">·</span><br><span class="line">·</span><br><span class="line">(Iteration 101 &#x2F; 125) loss: nan</span><br></pre></td></tr></table></figure><p>梯度爆炸。為什麼這裡loss不顯示Inf而顯示nan？因為exp產生的infinity在後續任何計算——比如inf-inf或0*inf，都是無效操作結果為nan。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>print(np.inf-np.inf, <span class="number">0</span>*np.inf, <span class="number">1</span>*np.inf)</span><br><span class="line">nan nan inf</span><br></pre></td></tr></table></figure></p><div class="table-container"><table><thead><tr><th>特性</th><th>NaN</th><th>Inf</th></tr></thead><tbody><tr><td><strong>含義</strong></td><td>無效或未定義值</td><td>超出數值範圍的值</td></tr><tr><td><strong>來源</strong></td><td>$0/0$, $\sqrt{-1}$</td><td>$1/0$, $10^{309}$</td></tr><tr><td><strong>比較行為</strong></td><td>與任何數值比較均為 $False$</td><td>可以比較大小</td></tr><tr><td><strong>檢測方法</strong></td><td><code>np.isnan()</code></td><td><code>np.isinf()</code></td></tr></tbody></table></div><p>直接将 $ \log\left(\frac{\text{up}}{\text{down}}\right) $ 改写为 $ \log(\text{up}) - \log(\text{down}) $ <strong>不会缓解数值问题</strong>，反而可能引入新的风险。</p><p>从数值稳定性的角度，$ \log\left(\frac{\text{up}}{\text{down}}\right) $ 中的分数计算可以通过硬件或软件优化，避免极端情况下的下溢问题。</p><p>而将其分开为 $ \log(\text{up}) - \log(\text{down}) $ 时：</p><ul><li>$ \log(\text{up}) $ 和 $ \log(\text{down}) $ 会单独计算，可能分别触发下溢或溢出。</li><li><p>两个独立的结果再相减，可能引入额外的浮点数误差。</p></li><li><p><strong>加 epsilon</strong></p><ul><li>在求对数前，为分子和分母添加一个小值 $ \epsilon $，避免 0 的出现：<script type="math/tex; mode=display">\log\left(\frac{\text{up}}{\text{down}}\right) \approx \log\left(\frac{\text{up} + \epsilon}{\text{down} + \epsilon}\right)</script></li><li>通常，$ \epsilon $ 的值为 $ 10^{-12} $ 或 $ 10^{-8} $。</li></ul></li><li>最常用<strong>數值穩定版本的 softmax</strong>：<script type="math/tex; mode=display">\text{softmax}(x_i) = \frac{e^{x_i - x_{\text{max}}}}{\sum_{j} e^{x_j - x_{\text{max}}}}</script>在dropout用500個數據過擬合500層的實驗裡，上面的梯度爆炸問題靠<strong>+1e-8沒能解決</strong>，因為不是divided by 0，是exp大數，就算沒溢出也會梯度爆炸導致w起飛，必須用數值穩定的softmax減掉xmax。</li></ul><p>weight scale？和ass2的其他問題</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>N, C, H, W = <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>x = <span class="number">4</span> * np.random.randn(N, C, H, W) + <span class="number">10</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>np.array_equal(x.transpose(<span class="number">1</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>), np.moveaxis(x, <span class="number">0</span>, <span class="number">1</span>))</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><h3 id="4-參數順序"><a href="#4-參數順序" class="headerlink" title="4. 參數順序"></a><strong>4. 參數順序</strong></h3><p>首先，有默認值的參數 (default argument) 必須在沒有默認值的參數 (non-default argument) 之後。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train</span><span class="params">(model, optimizer, epochs=<span class="number">1</span>, train_acc, val_acc)</span>:</span></span><br><span class="line">SyntaxError: non-default argument follows default argument</span><br></pre></td></tr></table></figure><br>其次，關鍵字參數 (positional argument) 必須出現在位置參數 (keyword argument) 之後。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">best_model = train(model, optimizer, epochs=<span class="number">10</span>, train_acc, val_acc)</span><br><span class="line">SyntaxError: positional argument follows keyword argument</span><br></pre></td></tr></table></figure></p><h3 id="5-路徑最好不要有"><a href="#5-路徑最好不要有" class="headerlink" title="5. 路徑最好不要有%"></a><strong>5. 路徑最好不要有%</strong></h3><p>圖片路徑加百分號會跟 %20（空格）之類的衝突，報錯URIError: URI malformed。</p><h3 id="6-RuntimeError-a-leaf-Variable-that-requires-grad-is-being-used-in-an-in-place-operation"><a href="#6-RuntimeError-a-leaf-Variable-that-requires-grad-is-being-used-in-an-in-place-operation" class="headerlink" title="6. RuntimeError: a leaf Variable that requires grad is being used in an in-place operation."></a><strong>6. RuntimeError: a leaf Variable that requires grad is being used in an in-place operation.</strong></h3><p>copy_<br>= +<br>+=?</p><h3 id="7-markdown-裡-lt-div-gt-lt-div-gt-前後要空行，否則會渲染錯誤。"><a href="#7-markdown-裡-lt-div-gt-lt-div-gt-前後要空行，否則會渲染錯誤。" class="headerlink" title="7. markdown 裡 &lt;div&gt;&lt;/div&gt; 前後要空行，否則會渲染錯誤。"></a><strong>7. markdown 裡 <code>&lt;div&gt;&lt;/div&gt;</code> 前後要空行，否則會渲染錯誤。</strong></h3><h3 id="8-Masked-self-attention，mask要加在alignment-scores變成-inf，只把attention-weights置0不行，而且要看明白mask-0代表遮掩還是mask-True代表遮掩。"><a href="#8-Masked-self-attention，mask要加在alignment-scores變成-inf，只把attention-weights置0不行，而且要看明白mask-0代表遮掩還是mask-True代表遮掩。" class="headerlink" title="8. Masked self-attention，mask要加在alignment scores變成-inf，只把attention weights置0不行，而且要看明白mask=0代表遮掩還是mask=True代表遮掩。"></a><strong>8. Masked self-attention，mask要加在alignment scores變成-inf，只把attention weights置0不行，而且要看明白mask=0代表遮掩還是mask=True代表遮掩。</strong></h3><p>這次完成得這麼順利：</p><ol><li>放棄本地配環境，用 colab 或者公司 infra 就是爽，實習得到的經驗；</li><li>chatgpt 查語法比查 document 方便多了；</li><li>不再追求逐行看懂，全寫完才回頭搞懂一部分內容。</li></ol><p>三年前要不是買了 m1，繼續用原來的 windows 可能早就完成了，奈何當時環境兼容性差到幾乎任何一個庫都不能順利安裝。<br>沒過多久就遇到直到現在都難以承受的變故，又碰到個極品老闆，組裡三個博士全部鬧掰換導師。<br>當時靠自己實在無法重拾信心，這一年多虧朋友、女友。時也，命也。</p><p>一旦国家机器被用来维护特定阶级的利益，它就会成为腐败的工具，即便这个阶级声称是无产阶级。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/pics/cs231n/life paths.jpeg&quot; alt=&quot;it&amp;#39;s never too late&quot;&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="Neural Network" scheme="http://blog.v2beach.cn/tags/Neural-Network/"/>
    
      <category term="Python" scheme="http://blog.v2beach.cn/tags/Python/"/>
    
      <category term="Math" scheme="http://blog.v2beach.cn/tags/Math/"/>
    
  </entry>
  
  <entry>
    <title>磁存储</title>
    <link href="http://blog.v2beach.cn/2024/01/27/%E7%A3%81%E5%AD%98%E5%82%A8/"/>
    <id>http://blog.v2beach.cn/2024/01/27/%E7%A3%81%E5%AD%98%E5%82%A8/</id>
    <published>2024-01-26T19:45:41.000Z</published>
    <updated>2025-03-18T10:17:55.208Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://wiki.v2beach.cn/assets/HardDiskAnatomy.jpg" alt=""><br><a id="more"></a></p><h1 id="Magnetic-Storage"><a href="#Magnetic-Storage" class="headerlink" title="Magnetic Storage"></a>Magnetic Storage</h1><ul><li><a href="https://cs.stanford.edu/people/nick/how-hard-drive-works">Hard Disk Drive</a></li><li><a href="http://hyperphysics.phy-astr.gsu.edu/hbase/Audio/tape2.html">cassette tape磁带</a><br><img src="https://wiki.v2beach.cn/assets/tape.jpg" alt=""><br><img src="https://wiki.v2beach.cn/assets/tape9.gif" alt=""></li><li><a href="https://en.wikipedia.org/wiki/Floppy_disk">floppy disk软盘</a><br><img src="https://wiki.v2beach.cn/assets/Floppy_disk_2009_G1.jpg" alt=""><br><img src="https://wiki.v2beach.cn/assets/2f75a076f2dca4d1f88402212a821850.png" alt=""></li></ul><p>本文主要讨论HDD硬盘驱动器，其他的磁性存储原理均相仿。</p><h1 id="物理学原理"><a href="#物理学原理" class="headerlink" title="物理学原理"></a>物理学原理</h1><ul><li><p>1831年法拉第电磁感应定律。</p></li><li><p>电动势方向，楞次定律：电路上所诱导出的电动势的方向，总是使得它所驱动的电流，会阻碍原先产生它（即电动势）的磁通量之变化。</p></li><li><p>电生磁方向，安培（右手螺旋）定律，</p></li></ul><p><img src="https://wiki.v2beach.cn/assets/Electromagnetism.svg.png" alt="Ampère&#39;s circuital law"></p><p><img src="https://wiki.v2beach.cn/assets/amplaw2.gif" alt="螺线管solenoic的磁感线"></p><p>螺线管（或其他方式）产生的<strong>磁场能改变磁性材料</strong>中的<a href="https://en.wikipedia.org/wiki/Magnetic_domain">磁畴（magnetic domain）</a>。</p><p><img src="https://wiki.v2beach.cn/assets/IMG_20211122_192346.jpg" alt="magnetic domain"></p><p><img src="https://wiki.v2beach.cn/assets/mdexample.png" alt="magnetic domains example"></p><h1 id="结构"><a href="#结构" class="headerlink" title="结构"></a>结构</h1><p><img src="https://wiki.v2beach.cn/assets/Hard_drive-en.svg.png" alt="hdd components"></p><h2 id="盘片（Platters）"><a href="#盘片（Platters）" class="headerlink" title="盘片（Platters）"></a>盘片（Platters）</h2><p>A typical HDD design consists of a spindle that holds flat circular disks, called platters, which hold the recorded data. The platters are made from a non-magnetic material, usually aluminum alloy, glass, or ceramic. They are coated with a shallow layer of magnetic material typically 10–20 nm in depth, with an outer layer of carbon for protection.</p><p>A strand of human DNA  is 2.5 nanometers in diameter. Most proteins are about 10 nanometers wide, and a typical virus is about 100 nanometers wide. A bacterium is about 1000 nanometers. Human cells, such as red blood cells, are about 10,000 nanometers across. PM2.5 = matter that has a diameter of 2.5 micrometres or smaller.</p><p>盘片是用铝合金、玻璃或陶瓷这种非磁性物质制作的，上面会覆盖10～20nm的磁性物质（iron(III) oxide一般用矫顽力大的永磁物质Fe2O3氧化铁），还有一层碳用于保护。</p><h2 id="磁道（Track）"><a href="#磁道（Track）" class="headerlink" title="磁道（Track）"></a>磁道（Track）</h2><p>当磁盘旋转时，磁头若保持在一个位置上，则每个磁头都会在磁盘表面划出一个圆环(annulus /ˈanjʊləs/)轨迹即磁道。</p><h2 id="柱面（Cylinder）"><a href="#柱面（Cylinder）" class="headerlink" title="柱面（Cylinder）"></a>柱面（Cylinder）</h2><p>如果是多个盘片构成的盘组，各盘面同一磁道构成的就是柱面。</p><p><img src="https://wiki.v2beach.cn/assets/65b606ed69b641868b9570ac52640241.png" alt="schematic of the hard drive geometry"></p><h2 id="扇区（Sector）"><a href="#扇区（Sector）" class="headerlink" title="扇区（Sector）"></a>扇区（Sector）</h2><p>这部分英文来自<a href="https://en.wikipedia.org/wiki/Disk_sector">维基百科</a>和<a href="https://superuser.com/questions/1218075/how-does-a-hard-drive-know-where-the-data-starts">其他资料的汇总</a>。</p><blockquote><p>In computer disk storage, a sector is a subdivision of a track on a magnetic disk or optical disc. For most disks, each sector stores a fixed amount of user-accessible data, traditionally 512 bytes for hard disk drives (HDDs) and 2048 bytes for CD-ROMs and DVD-ROMs. Newer HDDs and SSDs use 4096-byte (4 KiB) sectors, which are known as the Advanced Format (AF).</p></blockquote><p>在计算机disk存储里，一个扇区是磁盘或光盘上一个磁道的小分区。对大多数disks来说，每个扇区存储固定量的数据，传统上hdd是512字节，CD和DVD是2048字节。新的hdd和ssd都用4K字节的扇区，即advanced format。</p><blockquote><p>The sector is the minimum storage unit of a hard drive. Most disk partitioning schemes are designed to have files occupy an integral number of sectors regardless of the file’s actual size. Files that do not fill a whole sector will have the remainder of their last sector filled with zeroes.</p></blockquote><p>扇区是硬盘的最小存储单位，大多数磁盘的分区体系都被设计为不管文件的实际大小是多少，都让文件占据整数个分区。占不到一个扇区的那个零头（比如300Bytes）就把扇区剩下的部分用0填充。</p><blockquote><p>Geometrically, the word sector means a portion of a disk between a center, two radii and a corresponding arc, which is shaped like a slice of a pie. Thus, the disk sector refers to the intersection of a track and geometrical sector.</p></blockquote><p>几何学上所谓sector直译是扇形，指的是由盘的圆心、两个半径边、相应圆弧组成的部分（下图B）。因此磁盘里指的sector扇区实际上是几何学扇形和磁道track的交集（下图C）。</p><p><img src="https://wiki.v2beach.cn/assets/1920px-Disk-structure2.svg.png" alt="Disk structures:(A) Track(B) Geometrical sector(C) Disk sector(D) Cluster(物理相邻的若干个扇区组成一个簇cù)"></p><blockquote><p>In modern disk drives, each physical sector is made up of two basic parts, the sector header area (typically called “ID”) and the data area. The sector header contains information used by the drive and controller; this information includes sync bytes(that identifies the start of the sector, they merely indicate the start of the record.), address identification(sector number so the drive can know when it has found the start of a sector, and which sector it is), flaw flag and error detection and correction information. The header may also include an alternate address to be used if the data area is undependable. The address identification is used to ensure that the mechanics of the drive have positioned the read/write head over the correct location. The data area contains the sync bytes, user data and an error-correcting code (ECC) that is used to check and possibly correct errors that may have been introduced into the data.</p></blockquote><p>现代磁盘驱动器的每个物理扇区都有两个基本部分，分别是扇区头和数据（见下图）。扇区头包括sync区（这块只用来指示扇区的开始）、地址ID（扇区号，通过这前两块，驱动器就能知道什么时候找到了一个扇区的起点及这块扇区是哪一块）、错误标记、<a href="https://blog.v2beach.cn/2023/10/21/FreeBSD趟坑记/#Redundancy-check-Checksum-Parity-bit-Check-bit">错误检测校正信息（见我之前的文章）</a>。扇区头可能包含一个替代地址以防数据区不可用。address identification地址ID是确保驱动器将读写头定位到了正确的区域。数据区包含sync bytes、用户数据和<a href="(https://blog.v2beach.cn/2023/10/21/FreeBSD趟坑记/#Redundancy-check-Checksum-Parity-bit-Check-bit">ecc码（见我之前的文章）</a>)。</p><p><img src="https://wiki.v2beach.cn/assets/Advanced_format_4Kib_HDD_sector.svg-1.png" alt="disk sector format"></p><h3 id="实际的扇区到底他妈长什么样？"><a href="#实际的扇区到底他妈长什么样？" class="headerlink" title="实际的扇区到底他妈长什么样？"></a>实际的扇区到底他妈长什么样？</h3><p>上面的资料就能回答一个困扰我很久的问题，actuator到底是怎么让arm区分不同扇区的？并不是物理上有分隔，确实是囫囵铺了一整层永磁物质氧化铁，而是有sync（不知是否会跟data区冲突）标志。找某个扇区（MBR之类的）就是遍历然后找那个address ID符合的。</p><p>扇区间物理上没有任何分隔，只有逻辑上的分隔。</p><p><a href="https://www.open.edu/openlearn/digital-computing/introducing-computing-and-it/content-section-5.3">还有一点要注意的是，</a>上面Disk structures图里的实际上是老磁盘驱动器的样子，盘片是个圆，那么越靠外的磁道或扇区就比中心的越大，这意味着靠外的空间利用率就更差（因为逻辑上每个扇区都只能存512B或4KB）。现代的每个扇区在物理上也是一样大小，so they each store the <strong>same number of bits per unit area</strong>per单位区域存的数据量就一样了（如下图）。</p><p><img src="https://wiki.v2beach.cn/assets/variable_sectors_per_track.png" alt="variable sectors per track示意"></p><p><img src="https://wiki.v2beach.cn/assets/Aufnahme_einzelner_Magnetisierungen_gespeicherter_Bits_auf_einem_Festplatten-Platter..jpg" alt="Recording of single magnetisations of bits on a 200 MB HDD-platter (recording made visible using CMOS-MagView)"></p><p>关于上面的问题，<a href="https://qr.ae/pKB4x8">有个有趣的观点是</a>磁盘变慢的原因之一是靠中心的磁道被占用，读写头要遍历的距离更长了。</p><p><img src="https://wiki.v2beach.cn/assets/%E9%80%BB%E8%BE%91%E5%88%86%E5%8C%BA.png" alt="逻辑分区"></p><h2 id="磁头（Heads）"><a href="#磁头（Heads）" class="headerlink" title="磁头（Heads）"></a>磁头（Heads）</h2><p>每个盘片都有两面，因此也会相对应每盘片有2个磁头。</p><p>執行器（英語：Actuators）又稱為促動器、致動器、操動件、執行機構、驅動器或驅動件，是一種將能源轉換成機械動能的裝置，並可藉由執行器來控制驅使物體進行各種預定動作。</p><p>In modern hard drives, the amount of space between the head and rotating platter at normal operating speed is typically less than 5 nanometers… this gap is also referred to as the flying height.</p><p>磁头跟盘片距离5nm，参照上面的DNA是2.5nm。</p><p><img src="https://wiki.v2beach.cn/assets/Kopftraeger_WD2500JS-00MHB0.jpg" alt="Head stack with an actuator coil on the left and read/write heads on the right"></p><h1 id="读写技术"><a href="#读写技术" class="headerlink" title="读写技术"></a>读写技术</h1><blockquote><p>A modern HDD records data by magnetizing a thin film of ferromagnetic material on both sides of a disk. Sequential changes in the direction of magnetization represent binary data bits. The data is read from the disk by detecting the transitions in magnetization. User data is encoded using an encoding scheme, such as <a href="#run-length-encoding"><strong>run-length limited</strong> encoding</a>, which determines how the data is represented by the magnetic transitions.</p></blockquote><p>一个现代硬盘通过磁化盘片两面上的一小片铁磁性（强磁性，铁是强磁的）材料。用<strong>磁性方向的序列变化</strong>代表二进制位。读数据就是检测磁性的转变。用户数据用RLE等编码，这也决定了数据怎样被磁性的转变表达。</p><p><img src="https://wiki.v2beach.cn/assets/1*utiFV3HsGX-dPoxoNCvXLg.webp" alt="Read/Write Head and a magnetic storage(tape, HHD, etc)"></p><h2 id="写数据"><a href="#写数据" class="headerlink" title="写数据"></a>写数据</h2><h3 id="Longitudinal-Magnetic-Recording"><a href="#Longitudinal-Magnetic-Recording" class="headerlink" title="Longitudinal Magnetic Recording"></a>Longitudinal Magnetic Recording</h3><p>根据<a href="#物理学原理">上文物理原理</a>，写数据的原理即用螺线管电生磁改变磁性材料的极性，</p><blockquote><p>Reversing the current would polarize the magnetic material in the opposite direction.</p></blockquote><p>通过改变电流方向以让盘片的磁性物质改为不同的极性，一种极性作为0，相反的极性就是1。</p><p><img src="https://wiki.v2beach.cn/assets/LMR.gif" alt="LMR"> </p><p>磁头就是个U型电磁铁，磁性最强处就是U型两个开口末端。</p><h3 id="Perpendicular-Magnetic-Recording"><a href="#Perpendicular-Magnetic-Recording" class="headerlink" title="Perpendicular Magnetic Recording"></a>Perpendicular Magnetic Recording</h3><p><img src="https://wiki.v2beach.cn/assets/PMR.gif" alt="PMR"> </p><p>上面是LMR纵向磁性记录，PMR垂直磁性记录，只是改变了方向（扇区的极性从横变纵）。</p><h3 id="读数据"><a href="#读数据" class="headerlink" title="读数据"></a>读数据</h3><p>上文写数据图中左边的黑色柱子是读取器，用来探测磁极性变化。</p><p>根据<a href="#物理学原理">上文物理原理</a>，导体周围在磁通量变化时会产生感应电动势，闭合回路内进而会出现感生电流。因此实际上感应电路在穿过扇区本身时不会有太大电流变化，只有在经过扇区交界时，磁场方向反转，磁通量产生较大变化，进而有电流变化（见下图，只有reverse时是有效读数据，中间的grain自然推得是0）。</p><p><img src="https://wiki.v2beach.cn/assets/MagneticMedia.svg.png" alt="Magnetic cross section(截面) &amp; frequency modulation(频率调制) encoded binary data"></p><p>有外围电路与读取器相连，用来过滤噪音和放大感生电流。</p><p><img src="https://wiki.v2beach.cn/assets/1*a6HtiPWJIU_3vUYEW63Flg.webp" alt="Perpendicular Recording"></p><p>其实在扇区内而不是交界处，根据高电平还是低电平读取数据也是可以的，这部分<a href="https://blog.csdn.net/lengye7/article/details/122445057">这篇文章</a>讲数字信号处理讲得多一点比如Non-return-to-zero方法。</p><h2 id="run-length-encoding"><a href="#run-length-encoding" class="headerlink" title="run-length encoding"></a>run-length encoding</h2><p><a href="#读写技术">上文</a>提到的编码手段中文是游程编码/运行长度编码，缩写RLE。</p><blockquote><p>是一种与资料性质无关的无损数据压缩技术，基于“使用变动长度的码来取代连续重复出现的原始资料”来实现压缩。</p></blockquote><p>伪代码，<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">input: AAABCCBCCCCAA</span><br><span class="line"></span><br><span class="line">Q &#x3D; input(0)</span><br><span class="line">for i&#x3D;1:size (input)</span><br><span class="line"> if(Q &#x3D; input(i))</span><br><span class="line">    計數器+1</span><br><span class="line"> else</span><br><span class="line">   output的前項&#x3D;計數器的值, output的下一項&#x3D;Q值,</span><br><span class="line">   Q換成input(i)，計數器值換成0</span><br><span class="line"> end</span><br><span class="line">end</span><br><span class="line"></span><br><span class="line">output: 3A 1B 2C 1B 4C 2A(空格不存在)</span><br></pre></td></tr></table></figure></p><h1 id="寻道技术"><a href="#寻道技术" class="headerlink" title="寻道技术"></a>寻道技术</h1><h2 id="before-1980s-CHS"><a href="#before-1980s-CHS" class="headerlink" title="before 1980s, CHS"></a>before 1980s, CHS</h2><p>Cylinder-Head-Sector，顾名思义，按柱面、磁头、扇区这个三维坐标定位扇区。</p><p>Head可以确定是disk中的哪一个盘片platter；Cylinder和Head可以共同确定是盘片上的哪一个磁道track；最后用Sector可以确定被定位的是哪一个数据块。This data block is an arc of (360/n) degrees, where n is the number of sectors in the track.</p><blockquote><p>CHS addresses were exposed, instead of simple linear addresses (going from 0 to the total block count on disk - 1), because early hard drives didn’t come with an embedded disk controller, that would hide the physical layout. A separate generic controller card was used, so that the operating system had to know the exact physical “geometry” of the specific drive attached to the controller, to correctly address data blocks. The traditional limits were 512 bytes/sector × 63 sectors/track × 255 heads (tracks/cylinder) × 1024 cylinders, resulting in a limit of 8032.5 MiB for the total capacity of a disk.</p></blockquote><p>CHS的地址是暴露的（直球的），而不是简单的线性从0到盘片上的扇区数-1，因为早期硬盘没有能向操作系统隐藏磁盘物理结构的内置的disk控制器。那时用的是分开的通用控制卡，导致操作系统必须要精确地知道连在控制器上的盘的物理位置，才能正确定位数据块。传统上限是512 bytes/sector × 63 sectors/track × 255 heads (tracks/cylinder) × 1024 cylinders = 8032.5 MiB。</p><h2 id="1980s-1990s-CHS"><a href="#1980s-1990s-CHS" class="headerlink" title="1980s ~ 1990s, CHS"></a>1980s ~ 1990s, CHS</h2><blockquote><p>As the geometry became more complicated (for example, with the introduction of zone bit recording) and drive sizes grew over time, the CHS addressing method became restrictive. Since the late 1980s, hard drives began shipping with an embedded disk controller that had good knowledge of the physical geometry. These logical CHS values would be translated by the controller, thus CHS addressing no longer corresponded to any physical attributes of the drive.</p></blockquote><p>随着分区变得更复杂和磁盘容量增长，CHS定位就变得更局限。从1980年代末期开始开始内置磁盘控制器，其中有Head/Cylinder/Sector这些实际物理位置的信息。控制器会把物理地址翻译成逻辑地址（范围物理地址(0~c,0~h,0~s)→逻辑地址(0~c*h*s)），CHS定位为就再也不需要任何磁盘的相关物理属性。</p><blockquote><p><strong>For a single or double sided floppy disk track is the common term; and for more than two heads cylinder is the common term.</strong></p><p>以下来自<a href="https://farseerfc.me/zhs/history-of-chs-addressing.html">https://farseerfc.me/zhs/history-of-chs-addressing.html</a></p><p>在 IBM PC 上，驱动软盘和硬盘的是 CPU 执行位于主板 BIOS (Basic Input/Output System) 中的程序，具体来说操作系统（比如DOS）和应用程序调用 INT 13H 中断，通过 AH=02H/03H 选择读/写操作，BIOS 在中断表中注册的 13H 中断处理程序执行在 CPU 上完成读写请求。调用 INT 13H 读写扇区的时候，CPU 先通过 INT 13H AH=0CH 控制硬盘的磁头臂旋转到特定柱面上，然后选定具体磁头，让磁头保持在磁道上读数据， 通过忙轮训的方式等待要读写的扇区旋转到磁头下方，从而读到所需扇区的数据。在 DOS 之后的操作系统， 比如早期的 Windows 和 Linux 和 BSD 能以覆盖中断程序入口表的方式提供升级版本的这些操作替代 BIOS 的程序。</p></blockquote><h2 id="after-1990s-LBA"><a href="#after-1990s-LBA" class="headerlink" title="after 1990s, LBA"></a>after 1990s, LBA</h2><p>Logical Block Addressing</p><script type="math/tex; mode=display">\begin{equation*}LBA 地址 = ( C \times 磁头数 + H ) \times 扇区数 + ( S - 1 )\end{equation*}</script><p><img src="https://wiki.v2beach.cn/assets/5e0d27483f7332b7e98523ad8bc0a300c566f85d.svg" alt="CHS tuple mapped to LBA"></p><p>where A is the LBA address, Nheads is the number of heads on the disk, Nsectors is the maximum number of sectors per track, and (c, h, s) is the CHS address.</p><p>For example, for geometry 1020 16 63 of a disk with 1028160 sectors, CHS 3 2 1 is LBA 3150 = ((3 × 16) + 2) × 63 + (1 – 1);</p><blockquote><p>By the mid 1990s, hard drive interfaces replaced the CHS scheme with logical block addressing (LBA), but many tools for manipulating the master boot record (MBR) partition table still aligned partitions to cylinder boundaries; thus, artifacts of CHS addressing were still seen in partitioning software by the late 2000s.</p></blockquote><p>1990年代中期，LBA取代了CHS定位，但很多操作master boot record (MBR)分区表的工具还在用CHS，所以知道2000年代末期还可以见到。</p><blockquote><p>In the early 2010s, the disk size limitations imposed by MBR became problematic and the GUID Partition Table (GPT) was designed as a replacement; modern computers using UEFI firmware without MBR support no longer use any notions from CHS addressing.</p></blockquote><p>2010年代早期，由于MBR对磁盘容量的限制，其被GUID Partition Table (GPT)取代，现代使用UEFI固件但不支持MBR的电脑都不再使用CHS定位的任何概念。</p><p><img src="https://wiki.v2beach.cn/assets/v2-c1f894dca846db8b305c71a07adbe75b_1440w.webp" alt="对应关系"></p><h2 id="relative-sector-addressing"><a href="#relative-sector-addressing" class="headerlink" title="relative sector addressing"></a>relative sector addressing</h2><p>used in DOS</p><h1 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h1><p><img src="https://wiki.v2beach.cn/assets/2560px-Perpendicular_Recording_Diagram.svg.png" alt="Longitudinal recording (standard) &amp; perpendicular recording diagram"></p><p>CMR（Conventional Magnetic Recording，傳統磁性記錄）包括LMR（Longitudinal Magnetic Recording，縱向磁記錄）和PMR（Perpendicular Magnetic Recording，垂直磁記錄）</p><p>PMR提升了LMR的存储密度，SMR（Shingled Magnetic Recording，疊瓦式磁記錄）可以进一步提升存储密度。</p><p>参见上文<a href="#写数据">这里</a>和<a href="#读数据">那里</a>的图，写头总是比读头要大一些（均已经缩小至物理极限），这就导致磁道tracks之间总有一条是利用不到的，参见<a href="https://hk.xfastest.com/2715/hdd-technology-intro/">下两图</a></p><p><img src="https://wiki.v2beach.cn/assets/SMR2.png" alt="SMR diagram"></p><p><img src="https://wiki.v2beach.cn/assets/figure-4-smr-band-structure-550x166.jpg" alt="傳統磁軌間距"></p><p>SMR的做法是像我<a href="http://wiki.v2beach.cn/Hybrid/ChineseArchitecture.html#屋顶装饰">之前文章里写到的仰合瓦屋顶</a>那样把磁道像板瓦一样堆叠起来，</p><p><img src="https://wiki.v2beach.cn/assets/figure-3-writer-overlap-trimmed-smr-tracks-550x154.jpg" alt="SMR技術磁軌間距"></p><p>让板瓦（磁道）没被覆盖到的部分刚好等于读头的宽度。</p><p><img src="https://wiki.v2beach.cn/assets/figure-2-smr-writes-track-spacing-550x213.jpg" alt="寫入器與磁軌重疊"></p><p>不过这样的缺点也明显很严重——数据只能循序(sequential)写入，不能随机(random)写入，否则会覆盖其他数据；而且如果要修改某块数据，就需要一直修改整片磁道。折中方案是把连续的磁道组成Band/Zone，区域的最后一条按PMR写入不做重叠，读写数据都以这个Band/Zone为单位（见下图）。</p><p><img src="https://wiki.v2beach.cn/assets/figure-1-smr-conventional-writes-tracks-550x172.jpg" alt="SMR頻帶結構"></p><p>由于修改数据的开销太高，SMR比较适合数据归档之类长期不变的用途。</p><p><img src="https://wiki.v2beach.cn/assets/HDD_Startup_and_Shutdown.gif" alt="HDD Startup and Shutdown.gif"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://wiki.v2beach.cn/assets/HardDiskAnatomy.jpg&quot; alt=&quot;&quot;&gt;&lt;br&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="硬盘" scheme="http://blog.v2beach.cn/tags/%E7%A1%AC%E7%9B%98/"/>
    
      <category term="软盘" scheme="http://blog.v2beach.cn/tags/%E8%BD%AF%E7%9B%98/"/>
    
      <category term="磁带" scheme="http://blog.v2beach.cn/tags/%E7%A3%81%E5%B8%A6/"/>
    
  </entry>
  
  <entry>
    <title>中国建筑</title>
    <link href="http://blog.v2beach.cn/2023/11/21/%E4%B8%AD%E5%9B%BD%E5%BB%BA%E7%AD%91/"/>
    <id>http://blog.v2beach.cn/2023/11/21/%E4%B8%AD%E5%9B%BD%E5%BB%BA%E7%AD%91/</id>
    <published>2023-11-20T19:45:41.000Z</published>
    <updated>2025-03-18T10:17:29.167Z</updated>
    
    <content type="html"><![CDATA[<p>世界上的<a href="https://zh.wikipedia.org/zh-cn/建筑风格">建筑风格</a>太多了，只简单总结一下我见过的中式建筑。<br><a id="more"></a></p><h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><p>最近在看偶然从PT站下载的<a href="https://tv.cctv.com/v/vs3/VIDA1355574411487644.html">《梁思成和林徽因》</a>，之前的OS和AI老师，现在在研究计算艺术史的<a href="http://www.saggas.shisu.edu.cn/ArticleDetail/ArticleDetail?ArticleId=19a4246b-5b4d-4e28-890a-00f229eacbd0">张彬彬老师</a>最近在朋友圈也提到了<a href="https://zh.wikipedia.org/zh-cn/营造法式">《营造法式》</a>，加上最近打算去从七月就说想去的黄山和黄山脚下的<a href="#中国建筑派系风格">徽派小镇</a>玩，似乎不得不了解一点建筑艺术。</p><h1 id="中国建筑"><a href="#中国建筑" class="headerlink" title="中国建筑"></a>中国建筑</h1><p>这篇文章仍以维基百科为主要参考，维基百科上有两个条目，<a href="https://zh.wikipedia.org/zh-cn/中国传统建筑">中国传统建筑</a>和<a href="https://zh.wikipedia.org/zh-cn/中國建築">中国建筑</a>。</p><p>由于幅员辽阔，各处的<strong>气候、人文、地质</strong>等条件各不相同，而形成了中国各具特色的建筑风格。尤其<strong>民居形式</strong>更为丰富多彩。如南方的干阑式建筑、西北的窑洞建筑、游牧民族的毡包建筑、北方的四合院建筑等等。传统中国建筑更加塑造了整个东亚的建筑体系，朝鲜、越南、蒙古、琉球、日本等汉字文化圈地区均受中国傅统文化影响。</p><h1 id="中国传统建筑"><a href="#中国传统建筑" class="headerlink" title="中国传统建筑"></a>中国传统建筑</h1><p>相对于西方古建筑的砖石结构体系来说，中国古建筑是独立的机构体系，其最大的特点有四（出自梁思成《中国建筑史》）：（读到这莫名想起了1666年伦敦的那把大火）</p><ol><li>以木结构体系为主。砖石结构多用于塔式建筑。金属建筑以铜为主，著名的铜建筑实例有北京颐和园宝云阁、湖北武当山金殿和昆明太和宫金殿。</li><li>中国木结构体系历来采用构架制的结构原理：以四根立柱，上加横梁、竖枋而构成“间”，一般建筑由奇数间构成，如三、五、七、九间。等级越高，紫禁城太和殿为十一开间，是现存最高等级的木构古建筑。</li><li>斗栱是中国木架建结构中的关键部件，其作用是在柱子上伸出悬臂梁承托出檐部分的重量。</li><li>特异的外部轮廓：多层台基，色彩鲜艳的曲线坡面屋顶，院落式的建筑群，展现广阔空。两千多年前汉墓砖画上已经有院落建筑的表现，及至明清最宏大的建筑群——紫禁城，也采用的复杂的围合形式。</li></ol><p>关于中国古建结构，这篇<a href="http://sanyamuseum.com/a/3/2022/0606/965.html">三亚博物馆的常识科普</a>写得最好（很多内容同样源自梁思成的《中国建筑史》），实际上大多中国传统建筑风格都是以此为基础的。</p><h2 id="构架"><a href="#构架" class="headerlink" title="构架"></a>构架</h2><p>以立柱四根，上施梁、枋（按开间方向连贯两柱间的横木为<strong>梁</strong>；按进深方向连贯两柱间的横木为<strong>枋</strong>），牵制而成为一“间”。</p><p>梁可数层重叠称“梁架”。每层缩短如梯级，逐级增高称“举折”，左右两梁端，每级上承长<strong><a href="#檩、桁">榑</a></strong>，直至最上为脊榑，故可有五榑，七榑至十一榑不等，视梁架之层数而定。每两榑之间，密布栉篦并列之<strong>椽</strong>，构成斜坡屋顶之骨干；上加望板，始覆以<strong>瓦葺(qì, 茅草)</strong>。四柱间之位置称<strong>“间”</strong>。</p><p>此种构架制之特点，在使建筑物上部之一切荷载均由构架负担；承重者为其立柱与其梁枋，不借力于高墙厚壁之垒砌。建筑物中所有墙壁，无论其为砖石或为木板，均为“隔断墙”（Curtain Wall），非负重之部分。</p><p>是故门窗之分配毫不受墙壁之限制，而<strong>墙壁之设施，亦仅视分隔之需要。</strong>欧洲建筑中，<strong>唯现代之钢架及钢筋混凝土之构架</strong>在原则上与此木质之构架建筑相同。所异者材料及科学程度之不同耳。谚语“墙倒屋不塌”也正是这种构架制的真实写照。</p><p><img src="https://wiki.v2beach.cn/assets/164130H40-0.jpg" alt="构架示意图"></p><h3 id="开间（面宽）和进深"><a href="#开间（面宽）和进深" class="headerlink" title="开间（面宽）和进深"></a>开间（面宽）和进深</h3><p>开间是房间的主采光面成为<strong>开间（面宽）</strong>，与其垂直的称为<strong>进深</strong>（由门口向屋里延伸的深度），是指住宅的实际长度。</p><p><img src="https://wiki.v2beach.cn/assets/hd.webp" alt="开间和进深"></p><p>因此对于坐北朝南的古代建筑，梁是东西向的横木（起主要稳定和承重作用），枋是南北向的横木（辅助承重稳定、起连接作用）。</p><h2 id="三分"><a href="#三分" class="headerlink" title="三分"></a>三分</h2><p>其中官式建筑屋顶体型硕大、出挑深远是建筑造型中最重要的部分。</p><p><img src="https://wiki.v2beach.cn/assets/1641303T2-1.jpg" alt="三分"></p><h2 id="屋顶"><a href="#屋顶" class="headerlink" title="屋顶"></a>屋顶</h2><p>除了实用，主要考虑等级礼制，庑wǔ殿顶、歇山顶、悬山顶、硬山顶各有其使用的规则。等级从高到低依次为：重檐庑殿顶&gt;重檐歇山顶&gt;单檐庑殿顶&gt;单檐歇山顶&gt;悬山顶&gt;硬山顶。</p><p><img src="https://wiki.v2beach.cn/assets/1641302b5-2.jpg" alt="屋顶制式"></p><p><img src="https://wiki.v2beach.cn/assets/1641304613-3.jpg" alt="例子"></p><h2 id="屋脊"><a href="#屋脊" class="headerlink" title="屋脊"></a>屋脊</h2><p>屋顶两坡面相交隆起之处，一般用<strong>瓦条和砖垒砌而成</strong>。最初是一种<strong>防漏措施</strong>，后演变成优美的曲线轮廓和活泼的屋顶装饰。<strong>屋脊的位置不同</strong>，有不同的名称：正脊、垂脊、戗脊、博脊。</p><p><img src="https://wiki.v2beach.cn/assets/Jim_zek.png" alt="重檐歇山顶"></p><h2 id="屋顶装饰"><a href="#屋顶装饰" class="headerlink" title="屋顶装饰"></a>屋顶装饰</h2><p>以前常看到的正脊上翘起来的角叫<strong>吻兽</strong>。</p><p><img src="https://wiki.v2beach.cn/assets/1641304195-21.jpg" alt="吻兽"></p><p>明代的这个玄鉴楼也能很好地示意。</p><p><img src="https://wiki.v2beach.cn/assets/164130La-26.jpg" alt="玄鉴楼"></p><p>另一个比较重要的是<strong>瓦作</strong>。</p><p><img src="https://wiki.v2beach.cn/assets/1641302534-27.jpg" alt="瓦作"><br><img src="https://wiki.v2beach.cn/assets/1641304936-28.jpg" alt="瓦作"></p><p><strong>板瓦</strong>两侧称为<strong>瓦翅</strong>，瓦翅向上铺在屋顶为仰板瓦，其上可覆筒瓦，构成<strong>筒瓦屋顶</strong>（上2图，筒瓦一般以粘土为材料，一般用于大型庙宇、宫殿）；也可覆瓦翅向下的板瓦，构成<strong>仰合瓦屋顶</strong>（下2图，下也有板瓦示意图）。</p><p><img src="https://wiki.v2beach.cn/assets/仰合瓦.jpeg" alt="仰合瓦"><br><img src="https://wiki.v2beach.cn/assets/屋瓦分解.jpg" alt="屋瓦分解"><br><img src="https://wiki.v2beach.cn/assets/1641305405-30.jpg" alt="板瓦示意图"></p><p>筒瓦前面的<strong>瓦当</strong>是屋檐最前端的一片圆型挡片瓦，是用以装饰美化和蔽护建筑物檐头的建筑附件。</p><p>板瓦每一列形成一条排水沟为<strong>“一陇”</strong>，每陇最下一块带有如意头状者叫做<strong>“滴水”</strong>。 </p><h2 id="斗拱"><a href="#斗拱" class="headerlink" title="斗拱"></a>斗拱</h2><p>斗拱在宋代被称为铺作，它是中国木构架建筑中最富有特色的结构构件，因其重要的作用，故而成为我国建筑学会的会徽，梁思成曾是领导人。<br><img src="https://wiki.v2beach.cn/assets/ARCHITECTURAL SOCIETY OF CHINA.jpg" alt="会徽"></p><p>斗拱位于<strong>立柱和横梁交接处</strong>（见上图），由水平放置的方形<strong>斗</strong>、<strong>升</strong>和矩形的<strong>栱</strong>以及斜置的<strong>昂</strong>组成，是建筑<strong>屋顶和屋身立面</strong>上的过渡。</p><p><img src="https://wiki.v2beach.cn/assets/16413051Y-32.jpg" alt="斗拱主要构件"></p><p>斗拱在古建筑中起着十分重要的作用，主要有四个方面：</p><p>一是它位于柱与梁之间，由屋面和上层构架传下来的<strong>荷载，要通过斗拱传给柱子，再由柱传到基础</strong>，因此，它起着承上启下，传递荷载的作用。</p><p>二是它向外出挑，可把最外层的<strong>桁、檩</strong>挑出一定距离，使建筑物出檐更远，造形更优美、壮观。</p><p>三是它构造精巧，是很好的装饰性构件。</p><p>四是这种结构和现代梁柱框架结构极为类似。遇有强烈地震时，采用<strong>榫卯结合</strong>的空间结构虽会“松动”却不致“散架”，消耗地震传来的能量，使整个房屋的地震荷载大为降低，起到抗震作用。</p><p><img src="https://wiki.v2beach.cn/assets/1641302922-33-2.jpg" alt="斗拱主要构件"></p><p>华拱四跳——</p><p><img src="https://wiki.v2beach.cn/assets/1641301112-34.jpg" alt="多组斗拱组合层迭"></p><h2 id="抬梁式、穿斗式、井干式、干阑式四种木结构"><a href="#抬梁式、穿斗式、井干式、干阑式四种木结构" class="headerlink" title="抬梁式、穿斗式、井干式、干阑式四种木结构"></a>抬梁式、穿斗式、井干式、干阑式四种木结构</h2><p>其中抬梁式和穿斗式结构最常见，这二者的区别主要是柱子有没有用梁承托檩子，因此在<a href="#梁架">下文</a>梁架的部分讨论。</p><p>井干式特点是建筑结构<strong>完全由木条十字交叉叠积，用材之间由半刻榫卯相合，形成“井”字平面。</strong></p><p><img src="https://wiki.v2beach.cn/assets/井干式.jpg" alt="井干式结构示意图"></p><p>干阑式就是干栏屋、高脚屋、吊脚楼、棚屋，“编竹苫茅为两重，上以自处，下居鸡豚，谓之麻栏”，通常是木头，竹子所构屋梁，并用茅草盖顶遮蔽的住屋，也有柱桩顶端设轭木，较牢固的干栏式建筑。其主要特色是<strong>将其楼板垫高，以楼梯上下住所。</strong></p><p>干栏式民居除了透气凉爽外，也有避免瘴气，潮湿，淹水，并防止虫蛇进入和抗震的功能，另外，在架设上也较为简易。</p><p><img src="https://wiki.v2beach.cn/assets/Indonesia_Aceh.jpg" alt="亚齐人的传统住屋"></p><p><img src="https://wiki.v2beach.cn/assets/Puente_en_zona_de_palafitos_en_Nueva_Venecia-Sitionuevo-Magdalena-Colombia.jpg" alt="哥伦比亚圣玛尔塔大沼泽湖"></p><p><img src="https://wiki.v2beach.cn/assets/Fragaria_washington.JPG" alt="美国华盛顿州Fragaria普吉特海湾岸边"></p><h2 id="柱梁枋檩椽"><a href="#柱梁枋檩椽" class="headerlink" title="柱梁枋檩椽"></a>柱梁枋檩椽</h2><p>柱(column)、梁(栿fú, beam)、枋(fāng)、檩(lǐn, 桁héng, purlin)、椽(chuán, rafter)</p><p>构架是由柱、梁、枋、檩等构成的一种木制建筑结构，是木构架建筑物的承重部分，同时又是木建筑比例尺度和形体外观的重要决定因素。属“大木作”，大木作又称大木匠，是指木建筑的营造工艺。</p><p>这里简单记录一些我感兴趣的结构。<br><img src="https://wiki.v2beach.cn/assets/164130KQ-42.jpg" alt="结构示意图"><br><img src="https://wiki.v2beach.cn/assets/164130J00-43.jpg" alt="结构示意图"></p><h3 id="柱"><a href="#柱" class="headerlink" title="柱"></a>柱</h3><p>建筑中的垂直主结构件，承托在它上方物件的重量，比如屋檐的重量，是建筑的<strong>承重部分</strong>。此外还有一些其他较小的柱，这些短柱不是置于地基之上，而是<strong>置于梁架之上</strong>，承托上方物件的重量，再把这<strong>重量透过梁架，传递至主柱</strong>之上，例如脊瓜柱或蜀柱。古代柱子多为木造，亦有石柱。为防水、防潮，在主柱与地基间，建有柱础，并<strong>在木柱的柱础之上，垫以石櫍(zhì, 泛指器物的足)。</strong></p><p><img src="https://wiki.v2beach.cn/assets/1641306451-40.jpg" alt="柱阵图"></p><ul><li>檐柱也称廊柱，建筑物檐下最外一列支撑屋檐的柱子，也叫外柱。</li><li><p>瓜柱是立于大梁上用来支承上面梁架的短柱，有的称为“童柱”。其高度大于其直径。</p><ul><li><p>蜀柱（“脊瓜柱、侏儒柱”）是在屋脊部位的三架梁上，用来支撑脊檩的短柱。</p><p><img src="https://wiki.v2beach.cn/assets/1641302334-41.jpg" alt="瓜柱、蜀柱"></p></li></ul></li><li>驼峰是宋式建筑名词，系用在两层梁之间的垫块，配合斗拱承托<strong>梁栿</strong>的构件，因起外形似骆驼之背，故名之。</li><li><p>柱础：俗又称磉盘，或柱础石。古代为使落地屋柱<strong>不受潮</strong>，在柱脚下添了一块石墩，使柱脚与地坪隔离，起到绝对的防潮作用；同时，又是承受屋柱<strong>压力的垫基石</strong>，柱下的基础，凡是木架结构的房屋，可谓柱柱皆有，缺一不可。</p><p>  <img src="https://wiki.v2beach.cn/assets/1641301327-44.jpg" alt="柱础"></p></li><li><p>柱櫍：或称柱珠，它是柱身与柱础的过渡部分，因为柱子多为木制，水分易顺着竖向的木纹上升而影响木柱的耐久质量，“櫍”的纹理为横向平置，可有效<strong>防止水分顺纹上升，起到保护柱身的作用</strong>。“櫍”一般呈扁鼓形，材质有石、木两种。</p><p>  <img src="https://wiki.v2beach.cn/assets/1641305563-45.jpg" alt="柱櫍"></p></li></ul><h3 id="梁架"><a href="#梁架" class="headerlink" title="梁架"></a>梁架</h3><p>中国传统木结构建筑中的一种骨架。一般在柱间上部用梁和矮柱重迭装成，用以支撑屋顶檩条。梁也叫做“柁”，宋则称之为“栿” ，是古建筑的主要木作构件，多指按开间（开间：柱间的距离）方向连贯两柱间的横木，是房屋中承受重量的水平大木。截面可以是圆形的，也可以是矩形。在宋式建筑中，显露在外，一眼能看见的梁，称为明栿，被天花板遮蔽，无法看见的梁，称为草栿。</p><p><a href="#抬梁式、穿斗式、井干式、干阑式四种木结构">上文</a>已经讨论过井干式和干阑式两种木结构，这里讨论抬梁式和穿斗式。</p><p>相邻屋架间，在各层梁的两端和最上层梁中间小柱（脊瓜柱）上架檩，檩间架椽，构成双坡顶房屋的空间骨架。屋面重量通过椽、檩、梁、柱传到基础（有斗拱时，通过斗拱传到柱上）。这种木结构建筑形式，在我国北方地区比较常见，室内少柱甚至无柱，空间大，所以更为皇家建筑所选，在宫殿、庙宇、寺院等大型建筑中普遍采用，是我国木构架建筑的代表。因为是在立柱上架梁，且梁上又抬梁，这种梁架被称为“抬梁式”屋架，或“叠梁式”屋架。</p><p><img src="https://wiki.v2beach.cn/assets/1641301Z7-52.jpg" alt="抬梁式和穿斗式"></p><p><img src="https://wiki.v2beach.cn/assets/w700d1q75cms-2.jpg" alt="抬梁式"></p><h3 id="枋、雀替"><a href="#枋、雀替" class="headerlink" title="枋、雀替"></a>枋、雀替</h3><p>枋是较小于梁的辅材，也是主要的木作构件，截面为<strong>矩形</strong>，是方柱形的横木。枋的位置不同，称谓也不同。</p><p><img src="https://wiki.v2beach.cn/assets/1641303392-58.jpg" alt="枋"><br><img src="https://wiki.v2beach.cn/assets/16413013A-59.jpg" alt="枋"></p><hr><p>雀替又叫插角，又叫“角替”，在宋代叫做“绰幕枋”，指置于梁枋下与立柱相交的短木，可以缩短梁枋得净跨距离，防止梁枋与立柱之间角度变形。</p><p><img src="https://wiki.v2beach.cn/assets/太和门雀替.JPG" alt="故宫太和门清式雀替"></p><h3 id="檩、桁"><a href="#檩、桁" class="headerlink" title="檩、桁"></a>檩、桁</h3><p>檩和桁指的是同一个部分，也称“桁条”、“檩条”、“檩子” ，在宋式建筑中称“榑(不知道怎么念，百度百科说bó音时义为斗拱…)”。</p><p>实在是有些难以区分，下图是比较清楚的。<br><img src="https://wiki.v2beach.cn/assets/Wood_structure.png" alt="木建筑侧面简图"></p><p>上面这张图里的枋是没有跟梁正交的，这说明不仅跟梁正交的那根横木叫枋，两柱之间<a href="#开间（面宽）和进深">起连接作用</a>的横木也可以叫枋。可以看下图，一目了然。</p><p><img src="https://wiki.v2beach.cn/assets/f8f01ef3a7b2470f9f8b93dd323a011e.jpeg" alt="枋！"></p><p>关于梁、枋、檩、椽的区分，</p><p>桁和檩是一个意思，桁就是檩，桁（檩）大多是<strong>圆木</strong>，<strong>平行于屋脊</strong>，而且最高处的桁（檩）上面盖上瓦就成为<strong>屋脊</strong>；</p><p><strong>搭在桁（檩）上面的是椽子</strong>，椽子的作用是将<strong>瓦的重量传递给桁（檩）</strong>。<strong>椽子有圆有方，垂直于屋脊</strong>；</p><p>桁（檩）下面是一条垫板，<strong>垫板下面就是枋</strong>了，所以<strong>枋的作用是承托桁（檩）</strong>。<strong>多数（多数！）</strong>枋也是平行于屋脊的，<strong>枋和梁都是方的</strong>；</p><p>梁和枋的<strong>作用虽然相近</strong>，但有不同，梁和枋相互垂直，<strong>都架于柱上</strong>，平行于屋脊的托着桁的是枋，垂直于屋脊的就是梁。</p><h3 id="椽"><a href="#椽" class="headerlink" title="椽"></a>椽</h3><p>也叫椽子、椽条。中国古代木构建筑的屋顶都有挑出的屋檐，目的是保护檐口下的木构架及夯土墙少受雨淋。屋檐的主要构件就是椽子，它密集地排列于檩上，并与檩成正交，是支撑屋顶盖材料的圆形木条，其功能是承受屋顶的望板和瓦等材料。</p><p>它按屋面坡度铺砌，所以和地面形成角度，而不是水平的。<br><img src="https://wiki.v2beach.cn/assets/1641301355-64.jpg" alt="椽"></p><h3 id="叉手"><a href="#叉手" class="headerlink" title="叉手"></a>叉手</h3><p>支撑在蜀柱两侧的木构件。<a href="#柱梁枋檩椽">上面</a>部分图里有这个结构。</p><h1 id="中国建筑类型"><a href="#中国建筑类型" class="headerlink" title="中国建筑类型"></a>中国建筑类型</h1><p>以下列了八类。</p><ul><li>宫殿</li></ul><p><img src="https://wiki.v2beach.cn/assets/Forbidden_city_07.jpg" alt="紫禁城"></p><ul><li>城池，《礼记·礼运》记载：“城郭沟池以为固。”因此，城池包括了城墙和护城河，其中“城”指的是城墙及城墙上的门楼、角楼等，“池”指的是护城河。与欧洲国家比较起来，东亚的城郭规模一般较大。</li></ul><p><img src="https://wiki.v2beach.cn/assets/Dongilporu.JPG" alt="带顶炮台"></p><ul><li>民居，下文关于中国建筑风格的讨论均为民居</li><li>园林，园林意指在人工建筑出来的环境中模拟自然景物，东方园林与西亚、欧洲园林并称为世界三大造园系统，主要分为皇家园林、私家园林、寺庙园林。下面是在苏州平江路和平江河（宋元的时候，苏州又名平江）、艺圃等苏州园林，摄于2022年8月20日。</li></ul><p><img src="https://wiki.v2beach.cn/assets/IMG_6220 Large.jpeg" alt="艺圃"><br><img src="https://wiki.v2beach.cn/assets/2022-08-20_12-39-45_IMG_6181 Large.jpeg" alt="平江路"></p><ul><li>桥梁</li><li>石窟，起源于古埃及中王国时期的岩窟墓，在新王国时期演变为石窟，在公元前5至4世纪，经波斯传入印度，后通过中亚传入中国。石窟艺术作品包括佛教故事、神话传说、历史人物、民间传说等，比如莫高窟（即敦煌石窟，亦即千佛洞）。</li></ul><p><img src="https://wiki.v2beach.cn/assets/Dunhuang_Mogao_Ku_2013.12.31_12-30-18.jpg" alt="敦煌莫高窟"></p><ul><li>古塔，楼阁？是中国的传统建筑楼阁与印度的窣堵坡结合的产物。楼阁为多层建筑物，多建于园林中风景优美的地方，或水陆交通枢纽。最著名楼阁有黄鹤楼、岳阳楼、滕王阁、鹳雀楼，合称四大名楼。</li></ul><p><img src="https://wiki.v2beach.cn/assets/1280px-Yellow_Crane_Tower_in_20060430.jpg" alt="黄鹤楼"><br><img src="https://wiki.v2beach.cn/assets/Giant_Wild_Goose_Pagoda.jpg" alt="大雁塔"></p><p>塔本就是给<strong>佛教僧侶用作埋骨</strong>之用，除了上面的<strong>楼阁式塔</strong>还有很多种类，比如<strong>北海公园的白塔是覆钵式塔</strong>（摄于2019年7月17日）。</p><p><img src="https://wiki.v2beach.cn/assets/IMG_20190717_111624.jpg" alt="北海白塔"></p><ul><li>牌坊，简称坊，类似于牌楼，是中国传统建筑中非常重要的一种建筑类型，牌坊不同于牌楼，在立柱和横板上没有斗拱及屋顶结构的称为牌坊，而在立柱和横板上有楼的称为牌楼。下图光华复旦牌坊是1782年<strong>乾隆(ᡥᡠᠩ ᠯᡳ)年间存放四库全书的文澜阁</strong>竣工时立的，<strong>2021年9月6日</strong>摄于<strong>西湖孤山公园</strong>。</li></ul><p><img src="https://wiki.v2beach.cn/assets/IMG_20210906_133423.jpg" alt="西湖"></p><h1 id="中国建筑派系（风格）"><a href="#中国建筑派系（风格）" class="headerlink" title="中国建筑派系（风格）"></a>中国建筑派系（风格）</h1><p>以下列了十类。</p><ul><li>客派，比如土楼（即客家围楼），福建土楼是一种在福建省巨型围绕式建筑，它利用不加工的生土来作为承重墙，外形又像把空旷的内地包围起来，因此用“围”字或“土”形容这种建筑。其功能是四代同堂、居防合一（以抵御倭寇和山贼为目的），总数约三千余座。</li></ul><p><img src="https://wiki.v2beach.cn/assets/Snail_pit_tulou.jpg" alt="田螺坑土楼群"><br><img src="https://wiki.v2beach.cn/assets/Zhenchenglou_4_rings.JPG" alt="四环承启楼"></p><ul><li>皖派，徽派就是其中一支。</li></ul><p><img src="https://wiki.v2beach.cn/assets/w700d1q75cms.jpg" alt="徽派建筑"></p><ul><li>闽派，客家跟闽南有一定区别，这可能是维基区分开这俩风格的主要原因（实际上闽南也有土楼）。燕尾脊在闽南和台湾很常见。燕尾脊指的是具有向上弯曲的脊形的屋顶，形状像燕子的尾巴。燕尾可以是单层或双层的。这一特征起源于16世纪（明朝）。当时，福建人正在与东南亚、台湾、琉球和日本做生意。</li></ul><p><img src="https://wiki.v2beach.cn/assets/Singang_Fengtian_Temple_20081012.jpg" alt="奉天宫妈祖庙"></p><ul><li>粤派（广府民居、岭南园林，和江南园林、四川园林等并列为中国园林的主要风格之一），广府民居里有镬huò耳屋，其建筑特点是瓦顶建龙船脊和山墙筑镬耳顶，用于压顶挡风，一般是砖木结构。</li></ul><p><img src="https://wiki.v2beach.cn/assets/FSWongFeihungMusium.jpg" alt="佛山黄飞鸿纪念馆"></p><ul><li>京派，中国北方建筑的典型。</li></ul><p><img src="https://wiki.v2beach.cn/assets/Xsgl2.jpg" alt="西施故里"></p><ul><li>苏派（苏州园林）</li></ul><p><img src="https://wiki.v2beach.cn/assets/2022-08-20_13-41-51_IMG_6213 Large.jpeg" alt="艺圃"><br><img src="https://wiki.v2beach.cn/assets/IMG_6247 Large.jpeg" alt="路边小河"></p><ul><li>晋派，晋派只是一个泛称，不仅指山西一带，还包括陕西、甘肃、宁夏及青海部分地区。在这些地区中以山西的建筑风格最为成熟，故统称为晋派建筑。晋派建筑总体可分为“山西城市建筑”和“陕北周边的窑洞建筑”两大类。窑洞是黄土高原上的陕西和山西建筑，土窑建筑直接在黄土形成的崖壁上挖孔形成居室。多数在内部加盖砖或石墙，以防止土层倒塌。</li></ul><p><img src="https://wiki.v2beach.cn/assets/ec295d146fbd495b8a6427cd2b5a46af.png" alt="正常建筑"><br><img src="https://wiki.v2beach.cn/assets/8dda7e320d43ae58521609ec7036f63c.jpg" alt="窑洞"></p><ul><li><p>据说还有川派，是云贵川地区少数民族风格，如竹楼鼓楼吊脚楼。这类建筑在上文<a href="#抬梁式穿斗式井干式干阑式四种木结构">干阑式建筑</a>里讨论过了。</p></li><li><p>栱圆形建筑，包括蒙古包和上文的福建土楼。</p></li></ul><p><img src="https://wiki.v2beach.cn/assets/Gurvger.jpg" alt="蒙古包"></p><ul><li>船屋和艇户，中国疍dàn家人是以船为家的渔民。由于他们生活在船艇上，他们的脚与生活在陆地上的人略有差别，士大夫则雅称之为“艇户”。</li></ul><p><img src="https://wiki.v2beach.cn/assets/24RIVERLIFE-1-master1050.jpg" alt="疍家人，sea gypsies"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;世界上的&lt;a href=&quot;https://zh.wikipedia.org/zh-cn/建筑风格&quot;&gt;建筑风格&lt;/a&gt;太多了，只简单总结一下我见过的中式建筑。&lt;br&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="传统建筑" scheme="http://blog.v2beach.cn/tags/%E4%BC%A0%E7%BB%9F%E5%BB%BA%E7%AD%91/"/>
    
  </entry>
  
  <entry>
    <title>橄榄球(rugby/football,soccer)和亚运片段</title>
    <link href="http://blog.v2beach.cn/2023/11/13/%E6%A9%84%E6%A6%84%E7%90%83-rugby-football-soccer-%E5%92%8C%E4%BA%9A%E8%BF%90%E7%89%87%E6%AE%B5/"/>
    <id>http://blog.v2beach.cn/2023/11/13/%E6%A9%84%E6%A6%84%E7%90%83-rugby-football-soccer-%E5%92%8C%E4%BA%9A%E8%BF%90%E7%89%87%E6%AE%B5/</id>
    <published>2023-11-13T15:31:44.000Z</published>
    <updated>2023-11-15T13:06:37.337Z</updated>
    
    <content type="html"><![CDATA[<p>亚运会去看了足球、曲棍球、棒球、拳击、残运举重、残运击剑六个项目，都相当有趣。</p><p>最有趣的是棒球，看完亚运比赛之后就一直追完了MLB的季后赛。今天来了解下一直感兴趣的橄榄球的历史和规则。</p><a id="more"></a><p><img src="/pics/19thAsianGames/IMG_1834 Large.jpeg" alt="足球"> <img src="/pics/19thAsianGames/IMG_1864 Large.jpeg" alt="足球"> <img src="/pics/19thAsianGames/IMG_1927 Large.jpeg" alt="曲棍球"> <img src="/pics/19thAsianGames/IMG_2085 Large.jpeg" alt="拳击"> <img src="/pics/19thAsianGames/IMG_2650 Large.jpeg" alt="举重"> <img src="/pics/19thAsianGames/IMG_2725 Large.jpeg" alt="轮椅击剑"> <img src="/pics/19thAsianGames/IMG_2822 Large.jpeg" alt="棒球"></p><p>亚运会全靠女友才抢到了这么些票，有这么一次在我跟前办这么多大型比赛的机会是非常难得的，这俩月看得超爽。</p><p>文本基本是直接从维基百科摘抄来的，但整合成了只有我感兴趣的部分。</p><p>总的来说，英式足球 (association football) 和英式橄榄球 (rugby football) 都脱胎于中世纪足球，只是橄榄球规则确立时间比现代英式足球规则早导致中文语境下的足球只能取 association 的“soc”变成soccer，实际上橄榄球也并不都像美式那样对抗激烈，比如源自爱尔兰足球的澳式橄榄球仍以踢球射门为重。</p><p>在了解的过程中我还学了板球的规则，慢慢发觉实际上一类运动（包括麻将等）从一个国家诞生，传播到各个文化中生根发芽，任何历史的偶然都会让同一类运动发展出往不同方向倾斜的规则，只是经济最发达的国家的规则最广为流传、受众最多，但就像<a href="https://www.sportsv.net/articles/65531">台湾的一篇板球棒球对比文章</a>里提到的，</p><blockquote><p>為什麼？為什麼板球和棒球不一樣？很簡單，因為都是遊戲，不同遊戲自有不同規則，畢竟三振、四壞、先發9棒、一壘在右邊，這些也不是什麼絕對「正常」的事，任何一個歷史的偶然，都會讓規則改變。每個人喜歡觀賞某種運動的原因，可能都不太一樣；如果你喜歡大量得分，對棒球比分太低有不滿，那麼得分通常比籃球還高的板球，可能就是一個不錯的選擇。</p></blockquote><p>这些最广为流传的规则绝对不是「理所当然」的，如果你觉得美式橄榄球不对劲，去看英式澳式也不错。</p><h1 id="Rugby-Football"><a href="#Rugby-Football" class="headerlink" title="Rugby Football"></a>Rugby Football</h1><p>橄榄球（Rugby）又称拉格比足球（Rugby football），源自其发源地拉格比公学（<strong>Rugby School</strong>，是位于<strong>英格兰中部沃里克郡拉格比镇</strong>上的一间男女兼收寄宿学校，也是英格兰最古老的公学之一，成立于1567年），香港简称为“榄球”，通常专指联合式橄榄球（Rugby Union football）或英式橄榄球，是一种使用椭圆形球进行奔跑推进得分的团体球类运动，是最早出现的橄榄球类运动。</p><p>“橄榄球”的中文译名是因其使用橄榄形（椭圆形）用球而得名，可以泛指一切使用椭圆形球的球类运动，在英语中并无类似称呼。</p><p>英式橄榄球和英式足球一样起源于英国，都是衍生自欧洲古代民间传统足球的一种团队运动。上述“欧洲古代传统足球”中的“足球”一词是泛指足球类运动，并不是指大中华地区和非英语国家认知中“足球”的英式足球。<strong>橄榄球和英式足球等其他足球类运动有共同的起源，都源自19世纪英格兰公立中学之间发展出的公校足球。</strong>而由英格兰北部拉格比公学自创的“拉格比规则”在1845年确立，<strong>时间上比英国足协确立现代英式足球规则的时间更早</strong>，以至于原版的英式足球不得不创造一个返璞词“<strong>协会足球”（association football，简称soccer！）</strong>加以区分。</p><p>传统的联合会式橄榄球，竞赛双方各派15名球员上场，比赛场地为长方形草地，球场两端各有一“H”型球门。比赛目的是持球冲到对方球门线后方以球触地得分（达阵 Try），触地得分后可再取得一次罚球射门机会补加分数；另亦可以在比赛中踢球越过球门横杆上方而得分。当比赛时间结束时，以得分较多者获胜。比赛过程中，防守一方可擒抱攻方持球员以阻止其进攻，但擒抱方式和位置均有所限制，以避免选手受伤。</p><p>近年来流行的7人制橄榄球，是橄榄球运动的一种变化，其场地、规则与传统15人制橄榄球大致相同，但人数少、比赛节奏快、平均得分高，普遍受到欢迎，重要性日增，为世界运动会的正式竞技项目。</p><p>除此之外，尚有许多运动是由橄榄球和足球衍生发展出来，包括同样使用橄榄形球的联盟式橄榄球（Rugby League）、美式橄榄球（American football）和加拿大式橄榄球（Canadian football，跟美式很像）。澳式橄榄球（Australian rules football）和流行于爱尔兰的盖尔式足球（Gaelic football），发展相对较独立。盖尔式足球虽然使用圆形球，但与英式橄榄球运动有几分相似，有时会被拿来作比较。</p><h1 id="Australian-football"><a href="#Australian-football" class="headerlink" title="Australian football"></a>Australian football</h1><p><img src="https://wiki.v2beach.cn/assets/Tyrone_Blanket_Defence.jpg" alt="Ireland, Gaelic football"><br><img src="https://wiki.v2beach.cn/assets/Stoppage_in_an_AFL_game.jpg" alt="Asutrilian football"><br><img src="https://wiki.v2beach.cn/assets/Aussie_rules_kicking.jpg" alt="踢球进门柱间是基本得分方法"><br><img src="https://wiki.v2beach.cn/assets/1024px-AFL_stadium.svg.png" alt="球场示意图"><br>关于澳式起源，目前“爱尔兰起源说”说服力最强，当年因为马铃薯饥荒导致许多爱尔兰人离开故土谋求生计，加上淘金潮，维多利亚州是爱尔兰移民的一个主要聚居区，澳式橄榄球有爱尔兰血统并不稀奇。</p><p>而维多利亚州在当时并没有英式橄榄球运动流行的记录，一向球风粗野的英式橄榄球也不太可能被用来为被誉为“绅士”的板球手们提供冬季锻炼，加上英式橄榄球以跑阵为主的得分方式和澳式橄榄球依赖射门得分有着本质上的不同。</p><h1 id="American-football"><a href="#American-football" class="headerlink" title="American football"></a>American football</h1><p>美式橄榄球（英语：American football，在美国则只称为“football”），另称美式足球，是在美国流行的一种由英式橄榄球衍生而来的竞技体育运动。</p><p>美式橄榄球与加拿大式橄榄球十分相像，两者常常被一起并称为烤盘（Gridiron，比赛场地划记着许多线，像烤盘）足球。美式橄榄球比赛的目的是要把球带到对手的“端区”得分，主要用持球或传球两种方式。得分方法有多种，包括持球越过底线，传球给在端区内的队友，或把球踢过两枝门柱中间射门。比赛时间完时得分较多的一队胜出。由于球赛中球员往往会与对方有激烈的身体冲撞，因此需穿护具及头盔出赛。</p><p>美式橄榄球首先由哈佛大学学生以一种名叫“Ballown”的运动开始，这个运动的目的是要带着球跑过对手。在美国内战结束后美式橄榄球开始在大学中广泛流行。罗格斯学院和普林斯顿大学在1869年打了史上第一场大学美式橄榄球赛。这个运动最初跟英式足球相近，后来被人们改良至比较接近英式橄榄球。自1880年代起，被称为“美式橄榄球之父”的耶鲁大学教练沃尔特·坎普则把规则进一步改变，包括采用攻防线（line of scrimmage）和记档距离的比赛方式。1905年仍然有18位球员于比赛中受伤死亡。1906年又增加向前传球的规则，攻防线之间设置中立区（neutral zone），以及每队都要有至少6名球员于攻防线列阵，最重要的改革是首推将向前传球（抛传）合法化，使得真正意义上的美式橄榄球开始成形。</p><p>美式橄榄球与英式橄榄球的比赛用球明显分别在于美式橄榄球用球上有白色缝线，以利于球员抓球及传球。而英式橄榄球则体型较大并且没有任何缝线。</p><h2 id="Rules"><a href="#Rules" class="headerlink" title="Rules"></a>Rules</h2><h3 id="Teams-and-Positions"><a href="#Teams-and-Positions" class="headerlink" title="Teams and Positions"></a>Teams and Positions</h3><p>A football game is played between two teams of 11 players each. Teams may substitute any number of their players between downs. Individual players in a football game must be designated with a uniform number between 1 and 99.</p><p><img src="https://wiki.v2beach.cn/assets/American_Football_Positions.svg.png" alt="roles, positions"></p><p>除了offensive unit进攻球员和defensive unit防守球员还有特勤组，special teams unit。</p><h3 id="Scoring"><a href="#Scoring" class="headerlink" title="Scoring"></a>Scoring</h3><p>The <strong>touchdown (TD, 达阵6分)</strong>, worth six points, is the most valuable scoring play in American football. A touchdown is scored when a live ball is advanced into, caught in, or recovered in the opposing team’s end zone.</p><p>The scoring team then attempts a try or conversion (附加得分), more commonly known as the <strong>point(s)-after-touchdown (PAT)</strong>, which is a single scoring opportunity. A PAT is most commonly attempted from the two- or three-yard line, depending on the level of play. If a PAT is scored by a place kick or drop kick through the goal posts, it is worth one point, typically called the <strong>extra point. (踢进1分)</strong> If it is scored by what would normally be a touchdown it is worth two points, typically called the <strong>two-point conversion. (触地2分)</strong></p><p><img src="https://wiki.v2beach.cn/assets/football-two-point-conversion.png" alt="两分转换"></p><p>A <strong>field goal (FG, 射门3分)</strong>, worth three points, is scored when the ball is place kicked (定点球, 一个人按着) or drop kicked (落地球, 球扔地上弹起来踢, rugby比较多而美式改尖后不好踢) through the uprights and over the crossbars of the defense’s goalposts. </p><p>After a PAT attempt or successful field goal, the scoring team must kick the ball off to the other team.</p><p>A <strong>safety (安全分，安防，自杀球2分)</strong> is scored when the ball carrier is tackled in his own end zone. Safeties are worth two points, which are awarded to the defense. In addition, the team that conceded the safety must kick the ball to the scoring team via a <a href="#kicking">free kick</a>.</p><h3 id="Field-and-equipment"><a href="#Field-and-equipment" class="headerlink" title="Field and equipment"></a>Field and equipment</h3><p>Football games are played on a rectangular field that measures 120 yards (110 m) long and $53\frac1{3}$ yards (48.8 m) wide.</p><p>end line底线, sideline边线, yard line分码线, hash marks码标, goal line得分线, end zone底线区/端区, goalposts/uprights球门柱.</p><p><img src="https://wiki.v2beach.cn/assets/football-field-diagram.png" alt="football-field-diagram"></p><p>Weighted <strong>pylons</strong> (达阵柱) are placed the sidelines on the inside corner of the intersections with the goal lines and end lines. </p><p><img src="https://wiki.v2beach.cn/assets/football-diagram1.gif" alt="football-diagram"></p><h3 id="Duration-and-time-stoppages"><a href="#Duration-and-time-stoppages" class="headerlink" title="Duration and time stoppages"></a>Duration and time stoppages</h3><p>Football games last for a total of 60 minutes and are divided into two halves of 30 minutes and four quarters (节) of 15 minutes.</p><p>If a down is in progress when a quarter ends, play continues until the down is completed.</p><p>Games last longer than their defined length due to play stoppages—the average NFL game lasts slightly over <strong>three hours.</strong></p><p>如果四节比赛后，双方同分，比赛将会延长15分钟，采用先得分者获胜的规则，而且无需再经过追加得分而直接结束比赛。NFL常规赛比赛若出现加时赛，若一队先通过射门得3分，则比赛不会马上结束，比分落后的一方仍有一次进攻机会，若再次打平则比赛继续。加时赛结束后比分打平，则比赛按平局计算。而在NFL季后赛中若出现加时赛，则需要决出胜负为止。大学比赛的延长赛规定更为繁复与不同。</p><h3 id="Advancing-the-ball-and-downs"><a href="#Advancing-the-ball-and-downs" class="headerlink" title="Advancing the ball and downs"></a>Advancing the ball and downs</h3><p>There are two main ways the offense can advance the ball: running and passing. In a typical play, the <strong>center (中锋)</strong> passes the ball backwards and between their legs to the <strong>quarterback (四分卫)</strong>in a process known as the <strong>snap (发球)</strong>. The quarterback then either hands the ball off to a <strong>running back (跑卫)</strong>, throws the ball, or runs with it. The play ends when the player with the ball is tackled or goes out-of-bounds or a pass hits the ground without a player having caught it. A forward pass can be legally attempted only if the passer is behind the line of scrimmage; only one forward pass can be attempted per down. <strong>As in rugby, players can also pass the ball backwards (向后传球) at any point during a play.</strong> In the NFL, a down also ends immediately if the runner’s helmet comes off.</p><p>The offense is given a series of four plays, known as downs. If the offense advances ten or more yards in the four downs, they are awarded a new set of four downs.  (进攻有四档, 须推进&gt;=10 yards)<strong>If they fail to advance ten yards, possession of the football is turned over to the defense. (turnover)</strong> In most situations, if the offense reaches their fourth down they will punt (弃踢, 方法是进攻队员（特勤组）把球扔下并在球落地之前将其踢向远处。通常进攻一方如果在前三档都未能成功前进十码，而该处位置又超过可以射门的距离，为免在攻守交换后让对方有机会可以在该处开始进攻，便会在第四档时使用弃踢大脚解围。) the ball to the other team, which forces them to begin their drive from farther down the field; if they are in field goal range (射门范围), they might attempt to score a field goal instead.</p><p><strong>down (档, 即被对方拦截放倒一次)</strong><br>The offense must advance at least ten yards in four downs or plays; if they fail, they turn over the football to the defense, but if they succeed, they are given a new set of four downs to continue the drive.</p><p>除了punt(弃踢)，还有interception(拦截，抄截，防守方接到进攻方的传球)和fumble(掉球，进攻方掉了球被防守方先捡到)也是常见的攻防转换情况。</p><h3 id="Kicking"><a href="#Kicking" class="headerlink" title="Kicking"></a>Kicking</h3><p>There are two categories of kicks in football: scrimmage kicks, which can be executed by the offensive team on any down from behind or on the line of scrimmage (攻防线), and free kicks. The free kicks are the kickoff (开球。<strong>On a kickoff, the ball is placed at the 35-yard line; On a safety kick, the kicking team kicks the ball from their own 20-yard line.</strong>), which starts the first and third quarters and overtime and follows a try attempt or a successful field goal; the safety kick follows a safety (安防之后的安防踢不能用球座(tee), 需要扶球手, 也可以选safety punt).</p><p>They (after safety) can punt, drop kick or place kick the ball, but a tee (球座) may not be used in professional play. Any member of the receiving team may catch or advance the ball. The ball may be recovered by the kicking team once it has gone at least ten yards and has touched the ground or has been touched by any member of the receiving team.</p><blockquote><p><a href="https://tdl100.com/thread-23658-1-1.html">https://tdl100.com/thread-23658-1-1.html</a> 这里有讨论safety为什么大多数时候选punt而不是kick。<br>Kick的确能踢更远，但是滞空时间短，所以回攻手能在踢球队队员阻拦自己之前跑出更多的码数；<br>而punt虽然距离短一些，但高度更高，滞空时间长，踢球组能更快赶到球所在位置，所以回攻手可能跑不了几步就会被拦下来。平均的净码数(=踢的码数-回攻码数)，punt比kick要多5~10码。</p></blockquote><p>下面这块其实我没搞懂，<a href="https://www.tdl100.com/thread-34646-1-1.html">https://www.tdl100.com/thread-34646-1-1.html</a> 看达阵联盟论坛里也有很多人不明白。</p><p>The three types of scrimmage kicks are place kicks, drop kicks, and punts. Only place kicks and drop kicks can score points. The place kick is the standard method used to score points, because the pointy shape of the football makes it difficult to reliably drop kick. Once the ball has been kicked from a scrimmage kick, it can be advanced by the kicking team only if it is caught or recovered behind the line of scrimmage. If it is touched or recovered by the kicking team beyond this line, it becomes dead at the spot where it was touched. The kicking team is prohibited from interfering with the receiver’s opportunity to catch the ball. The receiving team has the option of signaling for a fair catch, which prohibits the defense from blocking into or tackling the receiver. The play ends as soon as the ball is caught and the ball may not be advanced.</p><h3 id="Officials-and-fouls"><a href="#Officials-and-fouls" class="headerlink" title="Officials and fouls"></a>Officials and fouls</h3><p>裁判们，The back judge, The head linesman/down judge, The side judge, The line judge, The field judge, The center judge.</p><p>就打算写到这了，剩下的要多看才清楚，为了方便自己查阅（有时候不方便科学上网）还是把常见犯规放在这里。</p><p><strong>进攻方提前移动（False start）</strong>：除了在开球线平行移动的一名球员外，进攻方队员在开球前移动。退后5码。</p><p><strong>越位（Offside）</strong>：在开球前队员越过了球的位置。退后5码。相似的犯规有：在开球前接触对方队员、越入中立区。</p><p><strong>阻挡（Holding）</strong>：一名队员不公平地用拉球衣，勾人或铲人的方式妨碍对方有机会的阻截手或接球手。如果进攻方犯规或攻防转换中，退后10码（若是发生在距离攻方的端区20码内，则罚退后的码数为距离端区码数的一半；若进攻方在自己的端区，则被判安全球，防守方得2分）。如果防守方犯规，退后5码和进攻方自动获得新的四次进攻机会。</p><p><strong>干扰接球（Pass interference）</strong>：当球传出后，防守队员推、勾、拉或击倒进攻方有机会接到传球的队员；或接球队员用同样的方式对付防守方队员以避免被对方截球。干扰接球的规则只针对攻守双方在接到球之前有效，一旦他们其中一方接到球后，对方可以拦截或阻止得球方控制球。目前在NFL的规则是，若是防守方干扰接球，则进攻方在犯规处自动获得新的四档进攻（如果犯规发生在端区，那么球将被放在1码线开始进攻）；若是进攻方干扰接球，则会被罚退后10码。在攻防线之上及之后不会吹罚防守方干扰接球犯规。</p><p><strong>非法接触（Illegal  contact）</strong>：防守球员在四分卫持球并且待在口袋时，接球员超过5码之后，对接球员产生明显体身体接触。防守方退后5码和进攻方自动获得新的四次进攻机会。</p><p><strong>个人犯规（Personal foul）</strong>：处罚防守方，退后十五码且对方自动获得四次进攻。原因包六个项目：粗暴冲击传球者、粗暴冲击踢球员、粗暴冲击置球员（攻方射门中把皮球直立予踢球者射门的人员）、拉扯对方面罩、弯身用头盔拦截对方、飞身在阻挡对方射门后畜意跳压在对方身上（但不会被计算在犯规，若守方已在己方的一码线）。</p><p><strong>用头盔拦截进攻球员（Targeting）</strong>：属于一种个人犯规，防守方球员故意用头盔拦截进攻球员，且撞击位置于进攻球员颈部以上。防守方退后15码，造成判罚的球员在经过影像重播确定后必须离场。</p><p><strong>拖延比赛（Delay of game）</strong>：处罚四分卫，后退五码处分，二十五秒或四十秒发球计时器数至零时仍未发球。</p><p><strong>非法向前传球（Illegal forward pass）</strong>：攻方在攻防线之前的位置向前传球；后退五码处分，并且会消耗一档。</p><p><strong>进攻方非法阵型（Illegal formation）</strong>：攻防线上少于七人列阵；合法接球员没有在阵型的最左侧和最右侧列阵</p><p><strong>场上人数过多（Too many men on the field）</strong>：任何一方在发球时超过十一人在场上均会被罚，后退五码。（注意：任何一方在交锋上少于十人都是合法；在2007年季赛中华盛顿红皮队为了纪念在屋中被劫杀的后卫尚•泰勒（Sean Taylor），特意在他死后的那周作客布法罗比尔队的比赛上，防守以十人列阵。）</p><p><strong>非法变阵易位（Illegal shift）</strong>：进攻方的球员可以在交锋发球前移位，但只限列阵于攻防线以后的球员，并且每次只限一人，攻方要在队友停下来后才可以进行易位，其他球员或者非法球员易位会变被处分五码；防守方则不在此限。</p><p><strong>非法接球员前冲（Ineligible receiver downfield）</strong>：攻方的合法接球员是指站于锋线最外的两员及所有卫员（发球线以后的球员）；攻方在传球攻击时有上述球员以外的球员越过发球线都会被视为犯规，后退五码。</p><p><strong>违反体育精神（Unsportsmanlike conduct）</strong>：后退15码处分，处罚的原因有很多种，会由主裁判决定。常见的有:蓄意撞击或言语上辱骂裁判、故意延长得分后的庆祝、连续使用暂停冻结对方踢球员等（守方连续在踢球员踢球前叫两次暂停，以冻结对方并增加心理压力）。同一名球员在无蓄意撞击、辱骂裁判的前提下，被判第2次就必须离场。</p><p><strong>蓄意触地(Intentional grounding)</strong>:处罚四分卫，后退10码，由于蓄意的将球砸向没有合法接球员的地面，用以暂停时间来不断获得新攻势的机会，被裁判发现后予以处罚。</p><h4 id="特殊情况"><a href="#特殊情况" class="headerlink" title="特殊情况"></a><strong>特殊情况</strong></h4><p>若犯规被罚的码数越过第一次进攻目标线，亦同样自行获得新的进攻权（例如：攻方在“2攻3码”时，守方有球员越位，攻方可以获得新的四次进攻）。若攻守双方被罚的后退的码线位于端区，球会放在2码线开始；若攻守方在已方的2码线内犯规，无论是多少码数的处罚，罚的码数均会变成跟端区的一半距离，直至球的一部分进入端区，对手可获得安全分（2009年超级碗，匹兹堡钢人队正好被处罚了一个相同情况的安全分）。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;亚运会去看了足球、曲棍球、棒球、拳击、残运举重、残运击剑六个项目，都相当有趣。&lt;/p&gt;
&lt;p&gt;最有趣的是棒球，看完亚运比赛之后就一直追完了MLB的季后赛。今天来了解下一直感兴趣的橄榄球的历史和规则。&lt;/p&gt;
    
    </summary>
    
    
      <category term="gamer" scheme="http://blog.v2beach.cn/categories/gamer/"/>
    
    
      <category term="亚运会" scheme="http://blog.v2beach.cn/tags/%E4%BA%9A%E8%BF%90%E4%BC%9A/"/>
    
      <category term="19thAsianGames" scheme="http://blog.v2beach.cn/tags/19thAsianGames/"/>
    
      <category term="橄榄球" scheme="http://blog.v2beach.cn/tags/%E6%A9%84%E6%A6%84%E7%90%83/"/>
    
      <category term="足球类运动" scheme="http://blog.v2beach.cn/tags/%E8%B6%B3%E7%90%83%E7%B1%BB%E8%BF%90%E5%8A%A8/"/>
    
      <category term="football" scheme="http://blog.v2beach.cn/tags/football/"/>
    
  </entry>
  
  <entry>
    <title>FreeBSD趟坑记</title>
    <link href="http://blog.v2beach.cn/2023/10/21/FreeBSD%E8%B6%9F%E5%9D%91%E8%AE%B0/"/>
    <id>http://blog.v2beach.cn/2023/10/21/FreeBSD%E8%B6%9F%E5%9D%91%E8%AE%B0/</id>
    <published>2023-10-20T19:14:42.000Z</published>
    <updated>2025-03-18T10:15:44.049Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/pics/NAS/FreeBSD-logo.png"></img><br><a href="https://www.freebsd.org">FreeBSD的官方文档和社区</a>的完善程度是所有操作系统里首屈一指的水平，随便对比一下Linux的发行版<a href="https://docs.centos.org/en-US/docs/">CentOS</a>、<a href="https://help.ubuntu.com">Ubuntu</a>、<a href="https://archlinux.org">Arch Linux</a>，<a href="https://discussions.apple.com/community/mac/macbook-pro">macOS</a>，<a href="https://answers.microsoft.com/en-us/">windows</a>，高下立判。唯二缺点是用户少导致软件生态不如Linux（但有兼容层），且其中文资料比较匮乏，因此在这儿记录一下我使用时遇到的问题。</p><p>文章中也包含了我对RAID（主要是RAID用到的信息编码和群论）和UPnP、SMB等通用技术的理解。</p><a id="more"></a><h1 id="zfs-ufs"><a href="#zfs-ufs" class="headerlink" title="zfs, ufs"></a>zfs, ufs</h1><h2 id="Zettabyte-File-System"><a href="#Zettabyte-File-System" class="headerlink" title="Zettabyte File System"></a>Zettabyte File System</h2><p>Sun开发的世界上第一个128位文件系统，理论极限是64位文件系统的10^19倍。<br>zpool是个很有意思的存储管理工具，存储设备都加到zpool里，另外zfs原生支持软RAID-Z。<br>轻度使用其实ufs就够了，所以除了ssd系统盘另外的盘我都用的ufs。关于zfs只了解了这么多。</p><h2 id="Unix-File-System"><a href="#Unix-File-System" class="headerlink" title="Unix File System"></a>Unix File System</h2><p>FreeBSD的原生文件系统就是UFS(1969)，FreeBSD7之后才支持ZFS(2005)。<br>除此之外也支持Linux的EXT(Extended file system, 1992)和微软的NTFS(New Technology FileSystem, 1993)等。<br>跟EXT差不多，有RAID卡不需要软RAID所以基本用的都是顺手的UFS，之后再深度体验ZFS。</p><hr><h1 id="GROUP"><a href="#GROUP" class="headerlink" title="GROUP"></a>GROUP</h1><p>相比Linux的权限管理，FreeBSD的组是个对系统管理员比较友好的东西，基本就两种操作方式<br>1.直接用root修改/etc/group操作groups。<br>2.用<a href="https://man.freebsd.org/cgi/man.cgi?pw(8">pw(8)</a>)，pw groupmod wheel -m myuser在某个组里添加某个用户myusesr，或者pw usermod myuser -G wheel,operator把myuser添加进多个组（-g单个组）里。<br><a href="https://man.freebsd.org/cgi/man.cgi?query=groups&amp;sektion=1">groups(1)</a>可以直接查看某个用户所在的组，比如groups root，<br>我的用户在wheel operator video这几个组里。</p><hr><h1 id="ports-pkg"><a href="#ports-pkg" class="headerlink" title="ports, pkg"></a>ports, pkg</h1><p>在FreeBSD上安装第三方软件一般三种方式，1.FreeBSD Ports 套件（从源代码安装）；2.packages（从预编译的二进制版本安装）；3.从网上搜资源自己编译安装。<br>绝大多数时候pkg install就够用了，两者各有优势，<br>Package Benefits</p><ul><li>A compressed package tarball is typically smaller than the compressed tarball containing the source code for the application.</li><li>Packages do not require compilation time. For large applications, such as Firefox, KDE Plasma, or GNOME, this can be important on a slow system.</li><li>Packages do not require any understanding of the process involved in compiling software on FreeBSD.</li></ul><p>Port Benefits</p><ul><li>Packages are normally compiled with conservative options because they have to run on the maximum number of systems. By compiling from the port, one can change the compilation options.</li><li>Some applications have compile-time options relating to which features are installed. For example, NGINX® can be configured with a wide variety of different built-in options.</li><li>In some cases, multiple packages will exist for the same application to specify certain settings. For example, NGINX® is available as a nginx package and a nginx-lite package, depending on whether or not Xorg is installed. Creating multiple packages rapidly becomes impossible if an application has more than one or two different compile-time options.</li><li>The licensing conditions of some software forbid binary distribution. Such software must be distributed as source code which must be compiled by the end-user.</li><li>Some people do not trust binary distributions or prefer to read through source code in order to look for potential problems.</li><li>Source code is needed in order to apply custom patches.</li></ul><p>大体上package就是默认推荐安装，port是自定义安装，可以自己修改源代码。</p><h2 id="packages"><a href="#packages" class="headerlink" title="packages"></a>packages</h2><p>关于pkg，以<a href="https://www.freshports.org/net/wireshark/">wireshark</a>为例常用的命令有几个，<br>pkg search wireshark 搜索，<br>pkg install wireshark 安装，<br>pkg remove wireshark 删除，<br>关于<a href="https://forums.freebsd.org/threads/pkgng-upgrade-specific-package.46424/">更新软件</a>，<br><a href="https://man.freebsd.org/cgi/man.cgi?query=pkg-upgrade">pkg upgrade</a> 会更新所有的包，但关于做过自定义配置的package你可能并不想全部更新，比如wireshark遇到<a href="#wireshark">问题</a>我用upgrade解决后，v2ray回到原先版本又报panic，另外qbt升级后不再继承web-ui要单独运行nox。这时，可以使用<a href="https://man.freebsd.org/cgi/man.cgi?query=pkg-lock">pkg-lock(8)</a>，<br>pkg lock -a 锁定全部packages，这时被锁定的不会受reinstallation, modification or deletion影响，然后<br>pkg unlock wireshark 解锁需要更新的几个特定packages，再运行upgrade就可以对特定的软件用pkg更新了。</p><p>btw，<a href="https://www.freebsd.org/cgi/man.cgi?query=pkg-remove&amp;sektion=8&amp;manpath=freebsd-release-ports">pkg-remove(8)</a> and <a href="https://www.freebsd.org/cgi/man.cgi?query=pkg-delete&amp;sektion=8&amp;manpath=freebsd-release-ports">pkg-delete(8)</a> <a href="https://forums.freebsd.org/threads/completely-removing-a-package.79531/">are the same thing (remove is an alias for delete).</a></p><h2 id="ports"><a href="#ports" class="headerlink" title="ports"></a>ports</h2><p>ports安装软件的基本流程，<br>cd /usr/ports/net/wireshark 到需要安装的软件的源代码路径，<br>make install clean 一步完成 make、 make install 和 make clean 这三个分开的步骤的工作。<br>ports使用<a href="https://man.freebsd.org/cgi/man.cgi?query=fetch&amp;sektion=1&amp;format=html">fetch(1)</a>下载，设置代理也是对fetch配置环境变量，详细的<a href="https://man.freebsd.org/cgi/man.cgi?query=fetch&amp;sektion=3&amp;format=html">fetch(3)</a>。<br>顺带一提，FreeBSD里下载某个文件也是fetch url即可。</p><p>注意，尽量别用make deinstall/reinstall，我在解决<a href="#wireshark">下面</a>wireshark那个动态库的问题时，因为操作自己下的新版本误用了deinstall/reinstall，把很多包都误卸载了，我推测是因为这个命令会卸载Makefile里的所有包，包括依赖，pkg remove就只会卸载指定包。</p><blockquote><p>Yeah, “make deinstall” uses the information in the port Makefile and pkg-plist to see what to remove. If the version in the Makefile is not identical to the installed version, files can be left behind or accidentally deleted.</p><p>“pkg_delete” uses the information in /var/db/pkg, which corresponds to what’s actually on the disk.</p><p>“make deinstall” and “make reinstall” should really only be used for testing. “pkg_delete” and “make install” are better, more reliable tools.<br><a href="https://forums.freebsd.org/threads/make-deinstall-reinstall.14220/">phoenix</a>也提到除了测试时尽量不要用。</p></blockquote><hr><h1 id="xorg-gui"><a href="#xorg-gui" class="headerlink" title="xorg, gui"></a>xorg, gui</h1><p>跟随官方教程如果遇到xrandr的<strong>Can’t open display</strong>错误，需要先xinit，DISPLAY变量会由xinit设置(:0)，或者多尝试一下setenv DISPLAY，尽量不要Xorg -configure创建一个新的xorg.conf，xorg可以自动检测大部分参数。<br><a href="https://book.bsdcn.org/di-4-zhang-zhuo-mian-an-zhuang/di-4.4-jie-an-zhuang-mate.html#安装与配置">pkg install mate</a><br>如果没有自动启动gui的话，在～下创建.xinitrc输入exec mate-session，之后就可以用xinit从cli进入gui（或者直接用exec mate-session）。</p><p>中文乱码的话可以不用下文泉驿的字体，直接在/usr/ports的Chinese里make就能解决。<br>MATE(马黛)的效果可以看<a href="#ipv6">下面</a>ddns的那张图。</p><hr><h1 id="mount-new-disk"><a href="#mount-new-disk" class="headerlink" title="mount new disk"></a>mount new disk</h1><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># geom part list</span></span><br><span class="line">root@nas:/ <span class="meta"># gpart create -s GPT ada0</span></span><br><span class="line">The partition scheme <span class="keyword">is</span> created, <span class="keyword">and</span> <span class="keyword">then</span> a <span class="built_in">single</span> partition <span class="keyword">is</span> added. <span class="keyword">To</span> improve performance <span class="keyword">on</span> newer disks <span class="keyword">with</span> larger hardware block sizes, the partition <span class="keyword">is</span> aligned <span class="keyword">to</span> one megabyte boundaries:</span><br><span class="line">root@nas:/ <span class="meta"># gpart add -t freebsd-ufs -a 1M ada0</span></span><br><span class="line">Depending <span class="keyword">on</span> use, several smaller partitions may be desired. See gpart(<span class="number">8</span>) <span class="keyword">for</span> options <span class="keyword">to</span> create partitions smaller than a whole disk.</span><br><span class="line">The disk partition information can be viewed <span class="keyword">with</span> gpart show:</span><br><span class="line">root@nas:/home/V2beach/Desktop <span class="meta"># gpart show ada0</span></span><br><span class="line">=&gt;       <span class="number">40</span>  <span class="number">976773088</span>  ada0  GPT  (<span class="number">466</span>G)</span><br><span class="line">         <span class="number">40</span>       <span class="number">2008</span>        - free -  (<span class="number">1.0</span>M)</span><br><span class="line">       <span class="number">2048</span>  <span class="number">976771072</span>     <span class="number">1</span>  freebsd-ufs  (<span class="number">466</span>G)</span><br><span class="line">  <span class="number">976773120</span>          <span class="number">8</span>        - free -  (<span class="number">4.0</span>K)</span><br><span class="line">A file system <span class="keyword">is</span> created <span class="keyword">in</span> the <span class="keyword">new</span> partition <span class="keyword">on</span> the <span class="keyword">new</span> disk:</span><br><span class="line">root@nas:/ <span class="meta"># newfs -U /dev/ada0p1</span></span><br><span class="line">/dev/ada0p1: <span class="number">476939.0</span>MB (<span class="number">976771072</span> sectors) block size <span class="number">32768</span>, fragment size <span class="number">4096</span></span><br><span class="line"><span class="keyword">using</span> <span class="number">763</span> cylinder groups <span class="keyword">of</span> <span class="number">625.22</span>MB, <span class="number">20007</span> blks, <span class="number">80128</span> inodes.</span><br><span class="line"><span class="keyword">with</span> soft updates</span><br><span class="line">super-block backups (<span class="keyword">for</span> fsck_ffs -b <span class="meta">#) at:</span></span><br><span class="line"> <span class="number">192</span>, <span class="number">1280640</span>, <span class="number">2561088</span>, <span class="number">3841536</span>, <span class="number">5121984</span>, <span class="number">6402432</span>, <span class="number">7682880</span>, <span class="number">8963328</span>, <span class="number">10243776</span>, ...</span><br><span class="line"> An empty directory <span class="keyword">is</span> created <span class="keyword">as</span> a mountpoint, a location <span class="keyword">for</span> mounting the <span class="keyword">new</span> disk <span class="keyword">in</span> the original disk’s file system:</span><br><span class="line">root@nas:/ <span class="meta"># mkdir /newdisk</span></span><br><span class="line">root@nas:/ <span class="meta"># mount /dev/ada0p1 /newdisk</span></span><br><span class="line">root@nas:/ <span class="meta"># chmod 777 /newdisk</span></span><br></pre></td></tr></table></figure><p>以上是按照<a href="https://docs.freebsd.org/en/books/handbook/disks/的做法。">https://docs.freebsd.org/en/books/handbook/disks/的做法。</a><br>但是这样挂载的硬盘在重启后就不在挂载点上了，而是跑到了/media下面，想一直mount在mount point上就要手动写入fstab，<br><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># vim /etc/fstab</span></span><br><span class="line"><span class="meta"># Device                Mountpoint      FStype  Options         Dump    Pass#</span></span><br><span class="line">/dev/gpt/efiboot0               /boot/efi       msdosfs rw              <span class="number">2</span>       <span class="number">2</span></span><br><span class="line">/dev/nvd0p3             none    swap    sw              <span class="number">0</span>       <span class="number">0</span></span><br><span class="line">/dev/ada0p1     /newdisk        ufs     rw      <span class="number">2</span>       <span class="number">2</span></span><br><span class="line">/dev/mfid0p1    /RAID_LSI9271_ST4000VX015       ufs     rw      <span class="number">2</span>       <span class="number">2</span></span><br></pre></td></tr></table></figure><br>这里一定要是rw，sw会出错，Dump和Pass类型都无所谓。<br>umount -l和umount -f可以unmount busy device。</p><hr><h1 id="proxy"><a href="#proxy" class="headerlink" title="proxy"></a>proxy</h1><p>直接pkg install v2ray，<br>在/etc/rc.conf中加入v2ray_enable=”YES”之后service v2ray start即可。<br>但注意，直接使用会报版本相关的错，<br><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># service v2ray start</span></span><br><span class="line">Starting v2ray.</span><br><span class="line">root@nas:/ <span class="meta"># panic: qtls.ClientHelloInfo doesn't match</span></span><br><span class="line"></span><br><span class="line">goroutine <span class="number">1</span> [running]:</span><br><span class="line">github.com/marten-seemann/qtls-go1<span class="number">-15.</span>init<span class="number">.0</span>()</span><br><span class="line">github.com/marten-seemann/qtls-go1<span class="number">-15</span>/unsafe.go:<span class="number">20</span> +<span class="number">0x132</span></span><br></pre></td></tr></table></figure><br>这时需要把<a href="https://github.com/V2beach/v2ray-freebsd-64"><strong>老版本</strong></a>v2ray和v2ctl转移到/usr/local/bin/v2ray，修改一下/usr/local/etc/v2ray/config.json之后就能正常运行。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">root@nas:&#x2F; # find &#x2F; -name v2ray</span><br><span class="line">&#x2F;usr&#x2F;local&#x2F;bin&#x2F;v2ray</span><br><span class="line">&#x2F;usr&#x2F;local&#x2F;etc&#x2F;v2ray&#x2F;config.json</span><br></pre></td></tr></table></figure></p><p>上传FreeBSD的v2ray时不小心把macOS的v2ray删掉了，下了个<a href="https://github.com/v2ray/dist/blob/master/v2ray-macos-arm64-v8a.zip">新的</a>改了MEOW.sh（sed36&amp;新版本v2ray要跟run才运行）之后报文件损坏的错误，xattr -cr即可修复。</p><p>note: socks port 1081; http port 8001.</p><hr><h1 id="dlna"><a href="#dlna" class="headerlink" title="dlna"></a>dlna</h1><h2 id="server"><a href="#server" class="headerlink" title="server"></a>server</h2><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># pkg install minidlna</span></span><br><span class="line">root@nas:/ <span class="meta"># vim /usr/local/etc/minidlna.conf</span></span><br><span class="line">[minidlna.conf](https://man.freebsd.org/cgi/man.cgi?query=minidlna.conf)</span><br><span class="line">基本只需要修改media_dir=/newdisk</span><br><span class="line">echo <span class="comment">'minidlna_enable="YES"' &gt;&gt; /etc/rc.conf</span></span><br><span class="line">service minidlna start/rescan</span><br></pre></td></tr></table></figure><p>Some facts:</p><ul><li>uPNP protocol uses UDP 1900</li><li>miniDLNA uses TCP 8200 for status web page</li><li>miniDLNA on FreeBSD:<ul><li>service is minidlna</li><li>process is minidlnad</li><li>configuration is /usr/local/etc/minidlna.conf</li><li>log file (default) is /var/log/minidlna.log</li><li>db directory (default) is /var/db/minidlna/</li></ul></li><li>minidlnad -R to rescan</li><li>VLC can be used as DLNA client</li></ul><h2 id="client"><a href="#client" class="headerlink" title="client"></a>client</h2><p>macOS和windows都是下载VLC等支持dlna的播放器，在UPnP里可以搜索到同局域网的dlna服务器。<br><a href="https://www.google.com/search?client=safari&amp;rls=en&amp;q=macos%E7%9A%84SMB%E5%8D%8F%E8%AE%AE%E5%A4%AA%E6%85%A2&amp;ie=UTF-8&amp;oe=UTF-8">macOS上SMB协议速度比较慢</a>，之后可能需要<a href="https://www.twbsd.org/cht/book/ch21.htm">尝试NFS</a>，<a href="#nfs">尝试了</a>。</p><h2 id="DLNA协议集"><a href="#DLNA协议集" class="headerlink" title="DLNA协议集"></a>DLNA协议集</h2><p>DLNA(Digital Living Network Alliance 数字生活联盟)，根据<a href="https://spirespark.com/dlna/guidelines">spirespark文档</a>，协议集包括以下部分，</p><div class="table-container"><table><thead><tr><th>Functional Components</th><th>Technology Ingredients</th></tr></thead><tbody><tr><td>Connectivity</td><td>Ethernet, 802.11 (including Wi-Fi Direct), MoCA, HD-PLC, HomePlug-AV, HPNA and Bluetooth</td></tr><tr><td>Networking</td><td>IPv4 and IPV6 Suite</td></tr><tr><td>Device Discovery and Control</td><td>UPnP* Device Architecture</td></tr><tr><td>Media Management and Control</td><td>UPnP AV, EnergyManagement, DeviceManagement, and Printer</td></tr><tr><td>Media Formats</td><td>Required and Optional Format Profiles</td></tr><tr><td>Media Transport</td><td>HTTP (Mandatory) , HTTP Adaptive Delivery (DASH) and RTP</td></tr><tr><td>Remote User Interfaces</td><td>CEA-2014-A , HTML5</td></tr><tr><td>Device Profiles</td><td>CVP-NA-1, CVP-EU-1, CVP-2</td></tr></tbody></table></div><p>这么看不太直观，对比OSI或者TCP/IP模型就能看的比较清楚，<br><img src="/pics/NAS/dlna.png" alt="DLNA architecture" /><br>数据链路层802系列协议，网络层IP协议，传输层的TCP和UDP分别支持下面图上的应用层HTTP/UPnP协议。<br>其中核心是UPnP（Universal Plug and Play 通用即插即用），实现了DLNA的设备发现、控制等基本功能。</p><h2 id="UPnP协议集"><a href="#UPnP协议集" class="headerlink" title="UPnP协议集"></a>UPnP协议集</h2><p><img src="/pics/NAS/HowUPnPworksUPnPProtocolStack.jpg" alt="UPnP architecture" /><br>UPnP协议集又包含三个核心协议，SSDP（Simple Service Discovery Protocol 简单服务发现协议）、SOAP（Simple Object Access Protocol 简单对象访问协议）、GENA（Generic Event Notification Architecture 一般事件通知架构）。<br>根据<a href="https://en.wikipedia.org/wiki/Universal_Plug_and_Play">维基百科</a>的解释（概括得很好的几段我直接搬过来），</p><blockquote><p>UPnP assumes the network runs IP and then leverages HTTP, on top of IP, in order to provide device/service description, actions, data transfer and event notification. Device search requests and advertisements are supported by running HTTP on top of UDP (port 1900) using multicast (known as HTTPMU). Responses to search requests are also sent over UDP, but are instead sent using unicast (known as HTTPU).<br>UPnP uses UDP due to its lower overhead in not requiring confirmation of received data and retransmission of corrupt packets. UPnP uses UDP port 1900 and all used TCP ports are derived from the SSDP alive and response messages.</p></blockquote><p>UPnP假定网络运行着Internet Protocol并且利用IP之上的HTTP，提供设备或服务的描述、活动、数据交换和事件通知。设备发现请求由HTTPMU实现（UDP1900端口上的广播HTTP请求）；响应由HTTPU（Hypertext Transfer or Transport Protocol over UDP）实现，是单播的，也用UDP。<br>UPnP用UDP是因为开销较低（不需要确认收到数据且不需要重传损坏的包）。UPnP用UDP1900端口，所有用到的TCP端口都源于SSDP活跃的响应消息。</p><blockquote><p>Conceptually, UPnP extends plug and play—a technology for dynamically attaching devices directly to a computer—to zero-configuration networking for residential and SOHO wireless networks.</p></blockquote><p>概念上UPnP把PnP协议（把设备动态直连计算机的技术，指在電腦加上一個新的外部裝置時，能自動偵測與配置硬體資源（如I/O地址、DMA、IRQ），而不需要重新組態或手動安裝驅動程式。USB、PCIE、SATA/SAS、DVI/HDMI、Thunderbolt都是目前常用的PnP接口）扩展到<strong>简单场景的</strong>零配置<strong>无线</strong>网络。 </p><blockquote><p>UPnP的Control Points（CPs控制点）使用UPnP控制Controlled Devices（CDs被控制设备），Dynamic Host Configuration Protocol (DHCP) and Domain Name System (DNS) servers are optional and are only used if they are available on the network. DHCP和DNS服务器都是可选的。</p></blockquote><p>UPnP的作用过程如下，</p><blockquote><p><strong>Addressing</strong><br>The foundation for UPnP networking is IP addressing. Each device must implement a DHCP client and search for a DHCP server when the device is first connected to the network. If no DHCP server is available, the device must assign itself an address. The process by which a UPnP device assigns itself an address is known within the UPnP Device Architecture as AutoIP. If during the DHCP transaction, the device obtains a domain name, for example, through a DNS server or via DNS forwarding, the device should use that name in subsequent network operations; otherwise, the device should use its IP address.</p></blockquote><p>1.寻址，UPnP网络的基础是IP寻址，每个设备都必须实现一个DHCP客户端，并在第一次加入网络时寻找一个DHCP服务器，如果没有可用的DHCP服务器，这个设备必须给自己分配一个地址。这个UPnP设备给自己分配地址的过程叫<strong>AutoIP</strong>。如果在一个DHCP事务（transaction）中，设备获得了一个域名（比如通过一个DNS服务器或者通过DNS转发），这个设备应该在后续的网络操作中使用这个名字，否则设备应使用它的IP地址。</p><blockquote><p><strong>Discovery</strong><br>Once a device has established an IP address, the next step in UPnP networking is discovery. The UPnP discovery protocol is known as the Simple Service Discovery Protocol (SSDP). When a device is added to the network, SSDP allows that device to advertise its services to control points on the network. This is achieved by sending SSDP alive messages. When a control point is added to the network, SSDP allows that control point to actively search for devices of interest on the network or listen passively to the SSDP alive messages of devices. The fundamental exchange is a discovery message containing a few essential specifics about the device or one of its services, for example, its type, identifier, and a pointer (network location) to more detailed information.</p></blockquote><p>2.发现，一个设备建立了IP地址之后，在UPnP网络里的下一步是发现。UPnP发现协议是SSDP（简单服务发现协议）。当一个设备加入网络时，SSDP允许设备向网络上的控制点通过发送SSDP活动消息以通知他的服务。当一个控制点加入网络时，SSDP允许控制点主动寻找网络上的设备或者被动监听网络上设备的SSDP活动消息。基本交换是一个包含一些关于设备或服务的重要细节的发现消息，比如它的类型、标识符或者一个指针（网络位置）指向更多细节信息。</p><blockquote><p><strong>Description</strong><br>After a control point has discovered a device, the control point still knows very little about the device. For the control point to learn more about the device and its capabilities, or to interact with the device, the control point must retrieve the device’s description from the location (URL) provided by the device in the discovery message. The UPnP Device Description is expressed in XML and includes vendor-specific manufacturer information like the model name and number, serial number, manufacturer name, (presentation) URLs to vendor-specific web sites, etc. The description also includes a list of any embedded services. For each service, the Device Description document lists the URLs for control, eventing and service description. Each service description includes a list of the commands, or actions, to which the service responds, and parameters, or arguments, for each action; the description for a service also includes a list of variables; these variables model the state of the service at run time, and are described in terms of their data type, range, and event characteristics.</p></blockquote><p>3.描述，在一个控制点发现了一个设备之后，控制点仍不了解设备，因此控制点要检索发现消息里提供的位置（或URL）里面的描述。<strong>UPnP设备描述</strong>是XML格式的，包含运营商特定的制造商信息，还包含一系列嵌入式服务。对于每个服务，设备描述又都会进一步提供控制、事件和服务描述的URL。每个服务描述包含一系列命令和变量。</p><blockquote><p><strong>Control</strong><br>Having retrieved a description of the device, the control point can send actions to a device’s service. To do this, a control point sends a suitable control message to the control URL for the service (provided in the device description). Control messages are also expressed in XML using the Simple Object Access Protocol (SOAP). Much like function calls, the service returns any action-specific values in response to the control message.</p></blockquote><p>4.控制，检索了设备描述之后，控制点会发送一些行动给一个设备的服务。控制点会向（设备描述里提供的）控制URL发送一个恰当的控制消息，控制消息用的是SOAP（简单对象访问协议），也是XML格式。服务在响应控制消息时会返回任何行动特定的值，很像函数调用。</p><blockquote><p><strong>Event Notification</strong><br>Another capability of UPnP networking is event notification, or eventing. The event notification protocol defined in the UPnP Device Architecture is known as General Event Notification Architecture (GENA). A UPnP description for a service includes a list of actions the service responds to and a list of variables that model the state of the service at run time. The service publishes updates when these variables change, and a control point may subscribe to receive this information. The service publishes updates by sending event messages. Event messages contain the names of one or more state variables and the current value of those variables. These messages are also expressed in XML.</p></blockquote><p>5.事件通知，UPnP网络的另一个能力是事件通知，事件通知协议是GENA（一般事件通知架构）。通过XML格式的事件消息，UPnP网络里的控制点可以随时获得设备上服务的运行时状态变量。</p><blockquote><p><strong>Presentation</strong><br>The final step in UPnP networking is presentation. If a device has a URL for presentation, then the control point can retrieve a page from this URL, load the page into a web browser, and depending on the capabilities of the page, allow a user to control the device and/or view device status.</p></blockquote><p>6.展示，最后一步是如果一个设备有一个展示URL，控制点会检索这个URL的页面，把页面加载到浏览器中并允许用户通过页面控制设备或者只是查看设备的状态（DLNA只允许通过默认8200端口查看状态）。</p><blockquote><p><strong>NAT traversal</strong><br>One solution for NAT traversal, called the Internet Gateway Device Protocol (IGD Protocol), is implemented via UPnP. Many routers and firewalls expose themselves as Internet Gateway Devices, allowing any local UPnP control point to perform a variety of actions, including retrieving the external IP address of the device, enumerating existing port mappings, and adding or removing port mappings. By adding a port mapping, a UPnP controller behind the IGD can enable traversal of the IGD from an external address to an internal client.</p></blockquote><p>一种NAT穿透的方法叫IGDP（网关设备协议）通过UPnP实现。很多路由器和防火墙会对网关设备暴露他们自己，这就让本地UPnP控制点能做很多事，包括检索一个设备的公网IP，列举它现存的端口映射，并且增删端口映射。通过增加一个端口映射，一个网关设备背后的UPnP控制者（IGD作为Controlled Device）可以让外网的网关设备穿透到内部设备。</p><hr><h1 id="samba"><a href="#samba" class="headerlink" title="samba"></a>samba</h1><h2 id="server-1"><a href="#server-1" class="headerlink" title="server"></a>server</h2><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># pkg install samba</span></span><br><span class="line">root@nas:/ <span class="meta"># vim /usr/local/etc/smb4.conf</span></span><br></pre></td></tr></table></figure><figure class="highlight javascript"><figcaption><span>/usr/local/etc/smb4.conf</span></figcaption><table><tr><td class="code"><pre><span class="line">[global]</span><br><span class="line">workgroup          = WORKGROUP</span><br><span class="line">netbios name       = freebsd-test</span><br><span class="line">server string      = samba</span><br><span class="line">security           = user</span><br><span class="line">wins support       = yes</span><br><span class="line">passdb backend     = tdbsam</span><br><span class="line"></span><br><span class="line"># Following is the data directory you want to share.</span><br><span class="line"></span><br><span class="line">[SMBshare]</span><br><span class="line">  path = <span class="regexp">/newdisk</span></span><br><span class="line"><span class="regexp">  valid users = V2beach</span></span><br><span class="line"><span class="regexp">  public = no</span></span><br><span class="line"><span class="regexp">  writable  = yes</span></span><br><span class="line"><span class="regexp">  browsable  = yes</span></span><br><span class="line"><span class="regexp">  read only = no</span></span><br><span class="line"><span class="regexp">  guest ok = no</span></span><br><span class="line"><span class="regexp">  create mask = 0755</span></span><br><span class="line"><span class="regexp">  directory mask = 0755</span></span><br></pre></td></tr></table></figure><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/home/V2beach/Desktop <span class="meta"># pdbedit -a -u V2beach</span></span><br><span class="line"><span class="keyword">new</span> password:</span><br><span class="line">retype <span class="keyword">new</span> password:</span><br><span class="line">Unix username:        V2beach</span><br><span class="line">NT username:          </span><br><span class="line">Account Flags:        [U          ]</span><br><span class="line">User SID:             S<span class="number">-1</span><span class="number">-5</span><span class="number">-21</span><span class="number">-1459039941</span><span class="number">-1455980450</span><span class="number">-4236412286</span><span class="number">-1000</span></span><br><span class="line">Primary <span class="keyword">Group</span> SID:    S<span class="number">-1</span><span class="number">-5</span><span class="number">-21</span><span class="number">-1459039941</span><span class="number">-1455980450</span><span class="number">-4236412286</span><span class="number">-513</span></span><br><span class="line">Full Name:            Hulk Leo</span><br><span class="line">Home Directory:       \\FREEBSD-TEST\v2beach</span><br><span class="line">HomeDir Drive:        </span><br><span class="line">Logon Script:         </span><br><span class="line">Profile Path:         \\FREEBSD-TEST\v2beach\profile</span><br><span class="line">Domain:               FREEBSD-TEST</span><br><span class="line">Account desc:         </span><br><span class="line">Workstations:         </span><br><span class="line">Munged dial:          </span><br><span class="line">Logon time:           <span class="number">0</span></span><br><span class="line">Logoff time:          Wed, <span class="number">06</span> Feb <span class="number">2036</span> <span class="number">23</span>:<span class="number">06</span>:<span class="number">39</span> CST</span><br><span class="line">Kickoff time:         Wed, <span class="number">06</span> Feb <span class="number">2036</span> <span class="number">23</span>:<span class="number">06</span>:<span class="number">39</span> CST</span><br><span class="line">Password last <span class="keyword">set</span>:    Mon, <span class="number">25</span> Sep <span class="number">2023</span> <span class="number">01</span>:<span class="number">34</span>:<span class="number">38</span> CST</span><br><span class="line">Password can change:  Mon, <span class="number">25</span> Sep <span class="number">2023</span> <span class="number">01</span>:<span class="number">34</span>:<span class="number">38</span> CST</span><br><span class="line">Password must change: never</span><br><span class="line">Last bad password   : <span class="number">0</span></span><br><span class="line">Bad password count  : <span class="number">0</span></span><br><span class="line">Logon hours         : FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF</span><br><span class="line">root@nas:/home/V2beach/Desktop <span class="meta">#</span></span><br></pre></td></tr></table></figure><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta">#  vim /etc/rc.conf</span></span><br><span class="line">samba_enable=<span class="string">"YES "</span></span><br><span class="line">samba_server_enable=<span class="string">"YES"</span></span><br><span class="line">root@nas:/ <span class="meta"># service samba_server start</span></span><br><span class="line">root@nas:/ <span class="meta"># service samba_server status</span></span><br></pre></td></tr></table></figure><h2 id="client-1"><a href="#client-1" class="headerlink" title="client"></a>client</h2><h3 id="windows"><a href="#windows" class="headerlink" title="windows"></a>windows</h3><p><img src="/pics/NAS/windowservice.png" alt="开服务"/><br>开服务之后在文件窗口输\\192.168.x.x(NAS的IP地址)\SMBshare。</p><h3 id="macos"><a href="#macos" class="headerlink" title="macos"></a>macos</h3><p>finder里connect to server，连smb://192.168.x.x/SMBshare。</p><h2 id="SMB协议"><a href="#SMB协议" class="headerlink" title="SMB协议"></a>SMB协议</h2><p>Server Message Block（服务器消息块）总的来说跟DLNA相比使用上的区别有，</p><p>1.DLNA只支持AV（Audio/Video）文件共享，而SMB是任何文件的共享，可以说对“Network Attached Storage网络附加存储”来说类SMB的服务是刚需；<br>2.DLNA看视频比较流畅，SMB有时会卡；<br>3.DLNA需要支持协议的播放器（VLC），比如potplayer就不支持，而SMB在绝大多数设备上都原生支持。</p><blockquote><p>Server Message Block (SMB) enables file sharing, printer sharing, network browsing, and inter-process communication (through named pipes) over a computer network. SMB serves as the basis for Microsoft’s Distributed File System implementation.<br>SMB relies on the TCP and IP protocols for transport. This combination allows file sharing over complex, interconnected networks, including the public Internet. The SMB server component uses TCP port 445. SMB originally operated on NetBIOS over IEEE 802.2 - NetBIOS Frames or NBF - and over IPX/SPX, and later on NetBIOS over TCP/IP (NetBT), but Microsoft has since deprecated these protocols. On NetBT, the server component uses three TCP or UDP ports: 137 (NETBIOS Name Service), 138 (NETBIOS Datagram Service), and 139 (NETBIOS Session Service).</p></blockquote><p>SMB支持网络上的文件分享、打印机共享、网络浏览和进程内通信（用命名管道），SMB是微软分布式文件系统实现的基础。<br>SMB基于TCP/IP协议实现传输，这让SMB能进行复杂的、网络间（包括公网）的文件共享。SMB服务器组件使用TCP445端口，SMB最初在基于IEEE802.2的NetBIOS（NBF）之上实现，之后基于TCP/IP（NetBT），但微软此后放弃了这些SMB协议。NetBT实现中服务器使用UDP/137（NetBIOS 名称服务）、UDP/138（NetBIOS 数据报服务）、TCP/139（NetBIOS 会话服务）。</p><blockquote><p><a href="https://www.quora.com/What-is-the-difference-between-NetBIOS-and-SMB">The NetBIOS API and the SMB protocol are generally used together as follows:</a></p><ul><li>An SMB client will use the NetBIOS API to send an SMB command to an SMB server, and to listen for replies from the SMB server.</li><li>An SMB server will use the NetBIOS API to listen for SMB commands from SMB clients, and to send replies to the SMB client.</li></ul></blockquote><p>一个SMB客户端会通过NetBIOS API向SMB服务器发送SMB命令， 并且监听SMB服务器的响应；一个SMB服务器会通过NetBIOS API监听SMB客户端的SMB命令，并且向SMB发送响应。<br>但两者实际上是相互独立的，SMB可以用别的API实现设备间交流，比如用Berkeley Sockets API；NetBIOS这个接口也可以用于实现别的协议。</p><hr><h1 id="NFS"><a href="#NFS" class="headerlink" title="NFS"></a>NFS</h1><p>NFS也（还记得ZFS吗）由Sun开发，论文<a href="http://www.cs.yale.edu/homes/aspnes/pinewiki/attachments/NetworkFileSystems/nsf-design.pdf">Design and Implementation of the Sun Network Filesystem(Sandberg, 1985)</a>。<br><img src="/pics/NAS/4-Figure1-1.png" alt="NFS diagram"><br>里面提到的Unix 4.2系统指的是BSD4.2，NFS(Network File System)基本上是基于RPC(Remote Procedure Call, 远程过程调用)实现的，另外XDR(External Data Representation, 外部数据表示法)指的是一种标准数据序列化格式，在OSI表示层中实现。<br><img src="/pics/NAS/Unix_history-simple.svg" alt="BSD4.2"></p><h2 id="RPC协议"><a href="#RPC协议" class="headerlink" title="RPC协议"></a>RPC协议</h2><blockquote><ol><li>客户端调用客户端stub（client stub）。这个调用是在本地，并将调用参数push到栈（stack）中。</li><li>客户端stub（client stub）将这些参数包装，并通过系统调用发送到服务端机器。打包的过程叫 marshalling。（常见方式：XML、JSON、二进制编码）</li><li>客户端本地操作系统发送信息至服务器。（可通过自定义TCP协议或HTTP传输）</li><li>服务器系统将信息传送至服务端stub（server stub）。</li><li>服务端stub（server stub）解析信息。该过程叫 unmarshalling。</li><li>服务端stub（server stub）调用程序，并通过类似的方式返回给客户端。</li></ol><p>所谓marshal（集结、结集、编码、编组、编集、安整、数据打包、列集）就是把一个对象的内存变成适合存储和发送的数据格式，类似于序列化，比如用JAXB把一个学生对象转成XML文件。<br>unmarshal解集，即逆过程。<br>序列化一个对象意味着把它的状态转化为字节流，使这个字节流能反向转化为该对象的一个副本。<br>术语“marshal”被Python标准库认为与“序列化”同义。但与Java相关的RFC 2713不认为二者是同义：<br>“marshal”一个对象意味着记录下它的状态与codebase(s)在这种方式，以便当这个marshal对象在被”unmarshalled”时，可以获得最初代码的一个副本，可能会自动装入这个对象的类定义。可以marshal任何能被序列化或远程（即实现java.rmi.Remote接口）。Marshalling类似序列化，除了marshalling也记录codebases。Marshalling不同于序列化是marshalling特别处理远程对象。<br>——Schema for Representing Java(tm) Objects in an LDAP Directory (RFC 2713)</p></blockquote><p><img src="/pics/NAS/operating-system-remote-procedure-call-1.png" alt=""><br><img src="/pics/NAS/what-is-rpc-in-operating-system4.png" alt=""></p><blockquote><p>远程过程调用是一个<strong>分布式计算的客户端-服务器（Client/Server）的例子</strong>，它简单而又广受欢迎。<br>远程过程调用总是由客户端对服务器发出一个执行若干过程请求，并用客户端提供的参数。<br>执行结果将返回给客户端。</p></blockquote><h2 id="server-2"><a href="#server-2" class="headerlink" title="server"></a>server</h2><figure class="highlight vb"><figcaption><span>/etc/rc.conf</span></figcaption><table><tr><td class="code"><pre><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># rm -rf .Trash-1001/</span></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># vim /etc/rc.conf</span></span><br><span class="line"></span><br><span class="line">hostname=<span class="string">"nas.v2beach.cn"</span></span><br><span class="line">ifconfig_em0_ipv6=<span class="string">"inet6 accept_rtadv"</span></span><br><span class="line">create_args_wlan0=<span class="string">"country CN regdomain ETSI"</span></span><br><span class="line">local_unbound_enable=<span class="string">"YES"</span></span><br><span class="line">sshd_enable=<span class="string">"YES"</span></span><br><span class="line">moused_enable=<span class="string">"YES"</span></span><br><span class="line">ntpdate_enable=<span class="string">"YES"</span></span><br><span class="line">ntpd_enable=<span class="string">"YES"</span></span><br><span class="line">powerd_enable=<span class="string">"YES"</span></span><br><span class="line"><span class="meta"># Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable</span></span><br><span class="line">dumpdev=<span class="string">"AUTO"</span></span><br><span class="line">zfs_enable=<span class="string">"YES"</span></span><br><span class="line">kld_list=<span class="string">"i915kms"</span></span><br><span class="line">dbus_enable=<span class="string">"YES"</span></span><br><span class="line">lightdm_enable=<span class="string">"YES"</span></span><br><span class="line">v2ray_enable=<span class="string">"YES"</span></span><br><span class="line">minidlna_enable=<span class="string">"YES"</span></span><br><span class="line">samba_server_enable=<span class="string">"YES"</span></span><br><span class="line">jail_enable=<span class="string">"YES"</span></span><br><span class="line"><span class="meta"># NFS configuration</span></span><br><span class="line">rpcbind_enable=<span class="string">"YES"</span></span><br><span class="line">nfs_server_enable=<span class="string">"YES"</span></span><br><span class="line">nfs_server_flags=<span class="string">"-u -t -n 4"</span></span><br><span class="line">mountd_enable=<span class="string">"YES"</span></span><br><span class="line">mountd_flags=<span class="string">"-r"</span></span><br><span class="line"><span class="string">"/etc/rc.conf"</span> <span class="number">27</span>L, <span class="number">642</span>B written</span><br><span class="line"></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># vim /etc/exports</span></span><br><span class="line"></span><br><span class="line">/RAID_LSI9271_ST4000VX015 -mapall=V2beach -network <span class="number">192.168</span><span class="number">.2</span><span class="number">.0</span> -mask <span class="number">255.255</span><span class="number">.255</span><span class="number">.0</span>                                                              </span><br><span class="line"><span class="string">"/etc/exports"</span> [<span class="keyword">New</span>] <span class="number">1</span>L, <span class="number">83</span>B written</span><br><span class="line"></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># rpcbind </span></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># nfsd -u -t -n 4</span></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># mountd -r</span></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># showmount -e 192.168.2.x</span></span><br><span class="line">Exports list <span class="keyword">on</span> <span class="number">192.168</span><span class="number">.2</span>.x:</span><br><span class="line">/RAID_LSI9271_ST4000VX015          <span class="number">192.168</span><span class="number">.2</span><span class="number">.0</span></span><br><span class="line">root@nas:/RAID_LSI9271_ST4000VX015 <span class="meta"># cat /var/run/mountd.pid </span></span><br></pre></td></tr></table></figure><h2 id="MacOS-client"><a href="#MacOS-client" class="headerlink" title="MacOS client"></a>MacOS client</h2><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">sudo mount -o nolocks,resvport,locallocks -t nfs 192.168.2.x:&#x2F;RAID_LSI9271_ST4000VX015 &#x2F;Users&#x2F;v2beach&#x2F;nas</span><br></pre></td></tr></table></figure><p>-o后用<a href="https://man.freebsd.org/cgi/man.cgi?mount(8">其他的参数</a>)也可以把nas挂载到macOS上，但跟smb速度相仿，不过似乎相对稳定，用SMB传数据断了好几次，不过也可能是路由器出问题重启了（因为断传的时候网也断了）。</p><hr><h1 id="iOS文件备份，sync"><a href="#iOS文件备份，sync" class="headerlink" title="iOS文件备份，sync"></a>iOS文件备份，sync</h1><p>从照片里通过smb存储到nas太慢，改smb的配置后变更慢了，只好尝试了两个软件。<br>resilio sync是个P2P的网盘，潜力挺大但是跟我自动备份照片的主需求不太匹配；<br>photo sync相当好用，一方面速度很快而且支持的连接设备和功能都很全，另一方面我很喜欢跟iOS照片一样按天分类的功能。</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/IMG_2859.PNG" alt="resilio"/></td><td><img src="/pics/NAS/IMG_2860.PNG" alt="photosync smb"/></td><td><img alt="photosync day-wise categorize" src="/pics/NAS/IMG_2861.PNG"/></td></tr></tbody></table></div><h1 id="Android文件备份，sync"><a href="#Android文件备份，sync" class="headerlink" title="Android文件备份，sync"></a>Android文件备份，sync</h1><p>用photosync一直报rename和null之类的错，<br>看了半天v2ex选用了syncthing，也是个P2P的文件传输软件，FreeBSD里可以直接用pkg install syncthing安装，Android如果没有Google Play可以<a href="https://github.com/syncthing/syncthing-android">从GitHub获取</a>，相当好用。</p><div class="table-container"><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/FreeBSDsyncthing.png" alt="FreeBSD"></td><td><img src="/pics/NAS/AndroidSyncthing.JPG" alt="Android"></td></tr></tbody></table></div><hr><h1 id="相册-album-gallery"><a href="#相册-album-gallery" class="headerlink" title="相册, album, gallery"></a>相册, album, gallery</h1><p>测试了很多软件，其中三个比较好用，geeqie可以批量操作图片，GImageView是相当轻量的图片管理，但没有我想要的按天分类，<br>最后找到了digiKam，比较重量级，不过比较符合我的需求。<br><img src="/pics/NAS/geeqie.png" alt="geeqie"><br><img src="/pics/NAS/gimageview.png" alt="gimageview"><br><img src="/pics/NAS/digikam.png" alt="digkam"></p><hr><h1 id="ipv6"><a href="#ipv6" class="headerlink" title="ipv6"></a>ipv6</h1><p>只要是作为“网络附加存储”存在，就总有远程连接的需求，虽然只在内网使用是完全可以的。<br>我没有ipv4的公网ip，如果想远程访问nas还可以用ddns，但是我觉得很冗杂，其实之前我就用过向日葵和parsec，体验挺一般。<br><img src="/pics/NAS/Huawei ddns.png" alt="dynamic dns"/><br>自然而然地我选择尝试给nas配一个ipv6，看到的教程里都需要光猫改桥接后用支持ipv6的路由器分配地址，所以我先鼓捣起了光猫。<br><img src="/pics/NAS/IMG_2270 Large.jpeg" alt="光猫"/><br>一开始死活找不到复杂配置的入口，直接访问网关ip就只能访问到简化设置，后来才发现是在8080端口，但useradmin的权限也相当有限，大部分功能都配置不了，而管理员用户的密码在联网的一瞬间就变成动态分配了，这时没别的办法，只能重置光猫用默认密码进设置（telecomadmin nE7jA%5m），改桥接并开启telnet（Hw9aH%6c），但没用。<br>1.桥接需要路由器pppoe拨号，运营商不给宽带账户密码；<br><img src="/pics/NAS/Screen Shot 2023-10-26 at 10.09.10 PM.png" alt="modem"/><br>2.telnet是阉割版，几乎任何命令都用不了，用华为的enable软件使能失败，也就是说我不能通过cli获得管理员密码，每次我想设置光猫都要先捅屁股重置它。<br>但后来不知道是因为我改了一下出租屋的组网（把我房间的路由器直连到了光猫上，而不是再连另一个路由器），<br><img src="/pics/NAS/modem speed!.png" alt="网速起飞"/><br>还是我在路由器里改了ipv6的DHCP并关了防火墙，nas分配到了有效的ipv6，访问外网也可以用ipv6了，但向内的流量并不能通，局域网下是可以的。<br><img src="/pics/NAS/Screen Shot 2023-10-26 at 6.13.46 PM.png" alt="80端口服务"/><br>v2ex有个很<a href="https://www.v2ex.com/t/814699">类似的问题</a>，目前我还没解决。</p><hr><h1 id="jail"><a href="#jail" class="headerlink" title="jail"></a>jail</h1><h2 id="thick-jail-classic"><a href="#thick-jail-classic" class="headerlink" title="thick jail (classic)"></a>thick jail (classic)</h2><blockquote><p>a jail only needs a hostname, a root directory, an IP address, and a userland.</p></blockquote><p>jail需要一个名字，一个根目录，一个IP地址和一个用户区。</p><h3 id="IP-address"><a href="#IP-address" class="headerlink" title="IP address"></a>IP address</h3><p>jail有三种组网方式，1.Host Networking Mode (IP Sharing)跟宿主用同一个IP，2.Virtual Networks (VNET)获得独立网络栈，3.The netgraph system分别定义jail和宿主间、jails之间怎么网络交流，一般来说inherit就够用了。</p><h3 id="userland-root-directory"><a href="#userland-root-directory" class="headerlink" title="userland, root directory"></a>userland, root directory</h3><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># setenv D /usr/jail/vim-adventure</span></span><br><span class="line">root@nas:/ <span class="meta"># mkdir -p $D </span></span><br><span class="line">root@nas:/ <span class="meta"># cd /usr/src</span></span><br><span class="line">root@nas:/ <span class="meta"># make buildworld </span></span><br><span class="line">--------------------------------------------------------------</span><br><span class="line">&gt;&gt;&gt; World build completed <span class="keyword">on</span> Wed Sep <span class="number">27</span> <span class="number">14</span>:<span class="number">21</span>:<span class="number">17</span> CST <span class="number">2023</span></span><br><span class="line">&gt;&gt;&gt; World built <span class="keyword">in</span> <span class="number">14389</span> seconds, ncpu: <span class="number">4</span></span><br><span class="line">--------------------------------------------------------------</span><br><span class="line">root@nas:/ <span class="meta"># make installworld DESTDIR=$D </span></span><br><span class="line">root@nas:/ <span class="meta"># make distribution DESTDIR=$D </span></span><br><span class="line">root@nas:/ <span class="meta"># mount -t devfs devfs $D/dev</span></span><br></pre></td></tr></table></figure><p>或者直接下载base，<br><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">fetch https://download.freebsd.org/ftp/releases/amd64/amd64/13.2-RELEASE/base.txz -o /usr/<span class="built_in">local</span>/jails/media/13.2-RELEASE-base.txz</span><br><span class="line">tar -xvf /usr/<span class="built_in">local</span>/jails/media/13.2-RELEASE-base.txz -C /usr/jail/vim-adventure/</span><br></pre></td></tr></table></figure></p><h3 id="hostname-configuration"><a href="#hostname-configuration" class="headerlink" title="hostname, configuration"></a>hostname, configuration</h3><p>/etc/jail.conf或者/etc/jail.conf.d/，<br>比如下面我这个配置，<br><figure class="highlight properties"><figcaption><span>/etc/jail.conf</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="meta">vim-adventure</span> <span class="string">&#123; </span></span><br><span class="line"><span class="comment">  # STARTUP/LOGGING</span></span><br><span class="line">  <span class="meta">exec.start</span> = <span class="string">"/bin/sh /etc/rc"; </span></span><br><span class="line">  <span class="meta">exec.stop</span> = <span class="string">"/bin/sh /etc/rc.shutdown"; </span></span><br><span class="line">  <span class="meta">exec.consolelog</span> = <span class="string">"/var/log/jail_console_$&#123;name&#125;.log"; </span></span><br><span class="line"></span><br><span class="line"><span class="comment">  # PERMISSIONS</span></span><br><span class="line">  <span class="meta">allow.raw_sockets;</span> <span class="string"></span></span><br><span class="line">  <span class="meta">exec.clean;</span> <span class="string"></span></span><br><span class="line">  <span class="meta">mount.devfs;</span> <span class="string"></span></span><br><span class="line"></span><br><span class="line"><span class="comment">  # HOSTNAME/PATH</span></span><br><span class="line">  <span class="meta">host.hostname</span> = <span class="string">"$&#123;name&#125;"; </span></span><br><span class="line">  <span class="attr">path</span> = <span class="string">"/usr/jail/$&#123;name&#125;"; </span></span><br><span class="line"></span><br><span class="line"><span class="comment">  # NETWORK</span></span><br><span class="line">  <span class="attr">ip4</span> = <span class="string">inherit; </span></span><br><span class="line">  <span class="attr">ip6</span> = <span class="string">inherit;</span></span><br><span class="line">  <span class="attr">interface</span> = <span class="string">em0; </span></span><br><span class="line"><span class="attr">&#125;</span></span><br></pre></td></tr></table></figure><br>echo jail_enable=”YES” &gt;&gt; /etc/rc.conf<br>service jail start<br><strong>jls查看现有jails，jexec 1 tcsh进入编号为1的jail。</strong></p><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># jexec 1 tcsh</span></span><br><span class="line">root@nas:/ <span class="meta"># node bin/www.js</span></span><br><span class="line">  Listening <span class="keyword">on</span> port <span class="number">80</span></span><br></pre></td></tr></table></figure><p>写这篇文章之前没多久，github上vim-adventures的逆向代码就被删除了，我本地的代码等过阵子录视频的时候传到站里。（2023-11-18）</p><h2 id="thin-jail"><a href="#thin-jail" class="headerlink" title="thin jail"></a>thin jail</h2><p>thin jail更轻量，官方文档给了几种实现方式，<br>1.<a href="https://docs.freebsd.org/en/books/handbook/jails/#creating-thin-jail-openzfs-snapshots">Creating a Thin Jail Using OpenZFS Snapshots</a>;<br>2.<a href="https://docs.freebsd.org/en/books/handbook/jails/#creating-thin-jail-nullfs">Creating a Thin Jail Using NullFS</a>;<br>3.<a href="https://docs.freebsd.org/en/books/handbook/jails/#creating-vnet-jail">Creating a VNET Jail</a>;<br>4.<a href="https://docs.freebsd.org/en/books/handbook/jails/#creating-linux-jail">Creating a Linux Jail</a>.<br>等试过哪个之后就回来补充哪个。</p><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p>jail基本上是chroot的加强版，<a href="https://docs.freebsd.org/en/books/arch-handbook/jail/">官方文档</a>讲得挺详细了，有时间再把我的理解整理出来。<br>最热门的容器技术docker在FreeBSD上的原生实现目前还有很多问题，但通过Linux二进制兼容层似乎可以运行。最初的FreeBSD版docker就是依赖 zfs与jail的。</p><hr><h1 id="wireshark"><a href="#wireshark" class="headerlink" title="wireshark"></a>wireshark</h1><p>ld-elf.so.1 Undefined symbol _ZN11QToolButton13checkStateSetEv@Qt_5<br>根据<a href="https://forums.freebsd.org/threads/virtualbox-symbol-error.90572/">forum</a>，可以直接更新pkg upgrade解决问题。</p><hr><h1 id="Private-Tracker"><a href="#Private-Tracker" class="headerlink" title="Private Tracker"></a>Private Tracker</h1><h2 id="qbittorrent"><a href="#qbittorrent" class="headerlink" title="qbittorrent"></a>qbittorrent</h2><p>qbt直接pkg install就能用了，我本来用的是4.5.4，但由于<a href="#packages">上面的问题</a>，pkg upgrade完变成4.5.5，gui里没有了web-ui选项，想用webui要手动启动nox。（直接命令行qbittorrent-nox）<br>cli配置方法<a href="https://forum.qbittorrent.org/viewtopic.php?t=3358">~/.config/qBittorrent/qBittorrent.conf</a></p><h2 id="ptpp"><a href="#ptpp" class="headerlink" title="ptpp"></a>ptpp</h2><p>虽然下架了，issue里提供的FireFox安装<a href="https://github.com/pt-plugins/PT-Plugin-Plus/wiki/install-from-amo">途径</a>仍有效，<br>简单来说就是去<a href="https://addons.mozilla.org/en-US/developers/">开发者中心</a>提交一个新add-on，选on your own提交离线的插件，不用提交源代码，很快就能过审，之后直接把xpi文件拖进浏览器就行了。</p><h2 id="iyuu"><a href="#iyuu" class="headerlink" title="iyuu"></a>iyuu</h2><p>从<a href="https://github.com/ledccn/IYUUAutoReseed">iyuu的repo</a>下载源码之后简单弄下环境和配置文件就能运行了，<br><figure class="highlight vb"><figcaption><span>command line interface</span></figcaption><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># fetch https://github.com/ledccn/IYUUAutoReseed/archive/refs/heads/master.zip</span></span><br><span class="line">root@nas:/ <span class="meta"># unzip master.zip</span></span><br><span class="line">root@nas:/ <span class="meta"># pkg install php</span></span><br><span class="line">root@nas:/ <span class="meta"># vim config/config.php</span></span><br><span class="line">root@nas:/ <span class="meta"># php iyuu.php</span></span><br></pre></td></tr></table></figure><br>即可自动辅种，相比ptpp的挨个手动辅种还是很方便的。但注意两点，<br>1.iyuu需要申请作者网站的api才能跟各个pt站种子的hash对比；<br>2.config里必须配置一个合作站点，否则跑不通，我随手申请了个HD Dolby的账户。<br><img src="/pics/NAS/iyuu_autoreseed.png" alt="iyuu log"/></p><h2 id="ISO-BDMV"><a href="#ISO-BDMV" class="headerlink" title="ISO(BDMV)"></a>ISO(BDMV)</h2><p>FreeBSD不支持windows打包的ISO，dlna也读不到这些文件，其实BDMV在FreeBSD上本来就不能整体播放，我一般是在STREAM里找最大的m2ts文件播放，问题是跟常用字幕对不上。<br><strong>解决方法</strong>是windows里通过SMB协议直接把BDMV和ISO拖到播放器里播放。<br><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/ <span class="meta"># mdconfig -a -t vnode -f some.iso (or -u 0)</span></span><br><span class="line">md0</span><br><span class="line">root@nas:/ <span class="meta"># mount -t cd9660 /dev/md0 /mnt</span></span><br><span class="line">root@nas:/ <span class="meta"># man tar | less +/9660</span></span><br><span class="line">root@nas:/ <span class="meta"># tar -tf/-xf file.iso</span></span><br><span class="line">root@nas:/ <span class="meta"># file - &lt; /dev/md0</span></span><br><span class="line">/dev/stdin: data</span><br><span class="line">root@nas:/ <span class="meta"># file -s /dev/md0</span></span><br><span class="line">/dev/md0: data</span><br></pre></td></tr></table></figure><br>ISO的正常挂载方式是上面这样，但对这些PT下的蓝光行不通。<br>vlc, mplayer都打不开文件，这些ISO都不是cd9660文件系统，也不是别的常见DVD文件系统，所以FreeBSD不支持直接转换，MKVToolNix和Handbrake能转换，但是要么特别慢要么转完巨大。<br>这个就像我朋友开发HIS看的这个上古PB视频一样，<br><img src="/pics/NAS/strange format.png" alt="KHCS"/><br>这至少是个windows XP的视频，是个被淘汰的视频格式，现在的系统里死活打不开，格式工厂都不会转，FreeBSD打不开这个奇怪FS的ISO就很像这个问题，FreeBSD上很少有人看这种windows上BDMV打包的ISO电影了吧。</p><h2 id="PTGen-Differential"><a href="#PTGen-Differential" class="headerlink" title="PTGen, Differential"></a>PTGen, Differential</h2><p><a href="https://ptgen.rhilip.info">PTGen</a>是个为PT网站上传资源提供格式化简介生成的接口，由<a href="https://github.com/BFDZ/PT-Gen">他</a>基于这位<a href="https://blog.rhilip.info/archives/1260/">Rhilip</a>的PT-help开发（也是PTPP和AutoSeed的开发者），这哥们居然也用的是Vultr。<br><a href="https://github.com/LeiShi1313/Differential">Differential</a>是个更简化操作的工具，是位狗家的兄弟写的。</p><blockquote><p>差速器是汽车上的一种能使左、右轮胎以不同转速转动的结构。使用同样的动力输入，差速器能够输出不同的转速。就如同这个工具之于PT资源，差速器帮你使用同一份资源，输出不同PT站点需要的发种数据。</p></blockquote><p>主要是需要从<a href="#ports">ports</a>装<a href="https://www.freshports.org/lang/mono">mono</a>。<br><a href="/assets/mono">连下载带编译</a>需要装大几个小时（光pillow就个把小时），从官网fetch和building过程都太慢，相比预编译的packages这ports自己编译慢得离谱了。<br>所以还是选择package了，因为差速器dft用的是python，还要下python和python package manager。<br><figure class="highlight vb"><table><tr><td class="code"><pre><span class="line">root@nas:/usr/ports/lang/mono <span class="meta"># make install clean (太慢)</span></span><br><span class="line">root@nas:/usr/ports/lang/mono <span class="meta"># make all-depends-list (太多)</span></span><br><span class="line">root@nas:/ <span class="meta"># pkg install mono ffmpeg mediainfo</span></span><br><span class="line">root@nas:/ <span class="meta"># pkg install python</span></span><br><span class="line">The following <span class="number">2</span> package(s) will be affected (<span class="keyword">of</span> <span class="number">0</span> checked):</span><br><span class="line"><span class="keyword">New</span> packages <span class="keyword">to</span> be INSTALLED:</span><br><span class="line">            python: <span class="number">3.9</span>_3,<span class="number">2</span></span><br><span class="line">            python3: <span class="number">3</span>_3</span><br><span class="line">Number <span class="keyword">of</span> packages <span class="keyword">to</span> be installed: <span class="number">2</span></span><br><span class="line"><span class="number">2</span> KiB <span class="keyword">to</span> be downloaded.</span><br><span class="line">root@nas:/ <span class="meta"># pkg install py39-pip</span></span><br><span class="line">root@nas:/ <span class="meta"># pip install --no-build-isolation Differential --proxy http://127.0.0.1:8001</span></span><br></pre></td></tr></table></figure><br>另外有几个问题，</p><ol><li>setuptools报错，明明pip list里有但检索不到，因此需要—no-build-isolation强制不装依赖。</li><li>我用的图床是用Chevereto搭的imgbb。</li><li>rhilip的ptgen要绕过cloudflare检测，ptgen.lgto.workers.dev 则只能通过浏览器访问，也许是关闭了ICMP之类的服务，因此要在differential/plugins/base.py(/usr/local/lib/python3.9/site-packages)里（config.ini也能改）把ptgen_url改成second_ptgen_url: str的这个<a href="https://ptgen.caosen.com">https://ptgen.caosen.com</a> ，也就是手动调一下顺序（第一个报错并不会自动尝试第二个ptgen_url），或者另找一个/自己在cloudflare搭一个改上。</li><li>同样的问题也出现在短网址服务上，网站用requests连不通lg.to/new，use_short_url改成false。<a href="/assets/differential_error">这里</a>是报错示例，config.ini我放在桌面上了。</li><li>配完之后发现并不能自动填充，需要自行安装easy_upload和auto_feed_js，我选择的是<a href="https://github.com/techmovie/easy-upload">easy_upload</a>，需要装<a href="https://www.tampermonkey.net">Tampermonkey</a>以应用<a href="https://greasyfork.org/zh-CN/scripts/423199-easyupload-pt一键转种">Typescript脚本</a>，配好后目前是挺好用的。</li><li>最后提一个我在FreeBSD上老犯的错，因为在CLI里我通常偷懒用root，这时运行dft是在root权限下启动firefox，会发现跟普通用户下的同一个软件不一样（插件和cookie之类的都不见了），这是因为不同用户的同一个application有不同的设置，V2beach@nas:~ % firefox或dft就好了。</li></ol><div class="table-container"><table><thead><tr><th>问题5</th><th>配好后</th><th>问题6</th></tr></thead><tbody><tr><td><img src="/pics/NAS/fail.png" alt="upload failed"></td><td><img src="/pics/NAS/easy_upload.png" alt="easy_upload"></td><td><img src="/pics/NAS/firefoxissue.png" alt="firefoxissue %U!"></td></tr></tbody></table></div><hr><h1 id="GEOM-RAID"><a href="#GEOM-RAID" class="headerlink" title="GEOM, RAID"></a>GEOM, RAID</h1><p>GEOM在<a href="https://docs.freebsd.org/en/books/handbook/geom">handbook</a>里叫模块化磁盘变换框架，FreeBSD里不用ZFS的话其实还可以用GEOM组RAID，所以在这提一嘴。<br>GEOM还直接支持用<a href="https://man.freebsd.org/cgi/man.cgi?query=ggated&amp;sektion=8&amp;format=html">ggated(8)</a><a href="https://docs.freebsd.org/zh-cn/books/handbook/geom/#geom-ggate">远程使用设备</a>，不知相比SMB和DLNA效果如何，有待尝试。<br>然后分别记录一下各种RAID（Redundant Array of Independent Disks 容错式磁盘阵列），</p><h2 id="RAID0-1"><a href="#RAID0-1" class="headerlink" title="RAID0, 1"></a>RAID0, 1</h2><p><img src="/pics/NAS/striping.png" alt="RAID0, stripe"/><br>RAID0是把数据平均分散到各设备上，数据并行读写，是效率最高的RAID，也没有数据冗余，但正因如此安全性也是最差的，一个硬盘坏了可能所有数据就都不再可用了。<br>RAID1是镜像mirror，直接复制一份数据，我目前用的就是RAID1，硬RAID的配置很简单，在<a href="https://blog.v2beach.cn/2023/10/19/NAS装机实录/">之前</a>有提过。<br>RAID2用到了汉明码（Hamming Code ECC），海明码是一种ECC（Error Correction Code <a href="https://zh.wikipedia.org/zh-cn/错误检测与纠正">纠错码</a>），在此之前我想先回顾一下计算机网络里学过的冗余校验（Redundancy check）。</p><h2 id="Redundancy-check-Checksum-Parity-bit-Check-bit"><a href="#Redundancy-check-Checksum-Parity-bit-Check-bit" class="headerlink" title="Redundancy check, Checksum, Parity bit/Check bit"></a>Redundancy check, Checksum, Parity bit/Check bit</h2><h3 id="Redundancy-check-冗余校验"><a href="#Redundancy-check-冗余校验" class="headerlink" title="Redundancy check(冗余校验)"></a>Redundancy check(冗余校验)</h3><p>冗余校验是一段数据中用于<strong>错误检测</strong>Error Detection和<strong>错误校正</strong>Error Correction的附加数据。<br>校验和是种最简单的冗余校验方式，CRC(cyclic redundancy check)循环冗余校验是经典的冗余校验方式。<br>以下内容主要基于维基百科，英文的比中文的要完善很多。</p><h3 id="Checksum-校验和"><a href="#Checksum-校验和" class="headerlink" title="Checksum(校验和)"></a>Checksum(校验和)</h3><p>包括校验码，校验位等，其中校验码（Check digit）通常是一组数字的最后一位，由前面的数字通过某种运算得出，用以检验该组数字的正确性。比如中华人民共和国的身份证号码，一共十八位，第十八位的计算方式是，<br>1.计算17位数字各位数字与对应的加权因子的乘积S；<br>2.计算$\frac{S}{11}$的余数，$T=S\ mod\ 11$；<br>3.计算$\frac{12-T}{11}$的余数R，如果R=10，校验码为字母“X”；如果R≠10，校验码为数字“R”。</p><p><p><b>General topic</b></p></p><ul><li><a href="/wiki/Algorithm" title="Algorithm">Algorithm</a></li><li><a href="/wiki/Check_digit" title="Check digit">Check digit</a></li><li><a href="/wiki/Damm_algorithm" title="Damm algorithm">Damm algorithm</a></li><li><a href="/wiki/Data_rot" class="mw-redirect" title="Data rot">Data rot</a></li><li><a href="/wiki/File_verification" title="File verification">File verification</a></li><li><a href="/wiki/Fletcher%27s_checksum" title="Fletcher&#39;s checksum">Fletcher's checksum</a></li><li><a href="/wiki/Frame_check_sequence" title="Frame check sequence">Frame check sequence</a></li><li><a href="/wiki/Cksum" title="Cksum">cksum</a></li><li><a href="/wiki/Md5sum" title="Md5sum">md5sum</a></li><li><a href="/wiki/Sha1sum" title="Sha1sum">sha1sum</a></li><li><a href="/wiki/Parchive" title="Parchive">Parchive</a></li><li><a href="/wiki/Sum_(Unix)" title="Sum (Unix)">Sum (Unix)</a></li><li><a href="/wiki/SYSV_checksum" title="SYSV checksum">SYSV checksum</a></li><li><a href="/wiki/BSD_checksum" title="BSD checksum">BSD checksum</a></li><li><a href="/wiki/XxHash" class="mw-redirect" title="XxHash">xxHash</a></li></ul><p><b>Error correction</b></p><ul><li><a href="/wiki/Hamming_code" title="Hamming code">Hamming code</a></li><li><a href="/wiki/Reed%E2%80%93Solomon_error_correction" title="Reed–Solomon error correction">Reed–Solomon error correction</a></li><li><a href="/wiki/IPv4_header_checksum" class="mw-redirect" title="IPv4 header checksum">IPv4 header checksum</a></li></ul><p><b>Hash functions</b></p><ul><li><a href="/wiki/List_of_hash_functions" title="List of hash functions">List of hash functions</a></li><li><a href="/wiki/Luhn_algorithm" title="Luhn algorithm">Luhn algorithm</a></li><li><a href="/wiki/Parity_bit" title="Parity bit">Parity bit</a></li><li><a href="/wiki/Rolling_checksum" class="mw-redirect" title="Rolling checksum">Rolling checksum</a></li><li><a href="/wiki/Verhoeff_algorithm" title="Verhoeff algorithm">Verhoeff algorithm</a></li></ul><p><b>File systems</b></p><ul><li><a href="/wiki/ZFS" title="ZFS">ZFS</a>&#160;– a file system that performs automatic file integrity checking using checksums</li></ul><p><b>Related concepts</b></p><ul><li><a href="/wiki/Isopsephy" title="Isopsephy">Isopsephy</a></li><li><a href="/wiki/Gematria" title="Gematria">Gematria</a></li><li><a href="/wiki/File_fixity" title="File fixity">File fixity</a></li></ul><h3 id="Parity-bit-奇偶校验-Check-bit-校验位"><a href="#Parity-bit-奇偶校验-Check-bit-校验位" class="headerlink" title="Parity bit(奇偶校验)/Check bit(校验位)"></a>Parity bit(奇偶校验)/Check bit(校验位)</h3><p>奇偶校验位Parity bit是校验和Checksum的一种，也是最简单的错误检测码Error Detecting Code。<br>维基百科里有个<a href="https://en.wikipedia.org/wiki/Parity_bit">例子</a>，</p><table>    <tr>        <td rowspan="2">7位数据（1的个数）</td>        <td colspan="2">带有校验位的字节</td>    </tr>    <tr>        <td>偶核對位元</td>        <td>奇核對位元</td>    </tr>    <tr>        <td>0000000（0）</td>        <td>0000000<b>0</b></td>        <td>0000000<b>1</b></td>    </tr>    <tr>        <td>1010001（3）</td>        <td>1010001<b>1</b></td>        <td>1010001<b>0</b></td>    </tr>    <tr>        <td>1101001（4）</td>        <td>1101001<b>0</b></td>        <td>1101001<b>1</b></td>    </tr>    <tr>        <td>1111111（7）</td>        <td>1111111<b>1</b></td>        <td>1111111<b>0</b></td>    </tr></table><blockquote><p>In mathematics parity can refer to the evenness or oddness of an integer, which, when written in its binary form, can be determined just by examining only its least significant bit.</p></blockquote><p>在数学里parity指的是一个整数的奇偶性，当一个数被写成二进制格式时可以仅看它的least significant bit最低有效位（LSB，就是权值2^0最后那位，相对应的是M(most)SB最高有效位，就是有符号二进制采用反码或补码形式的负数里，最高的表示正负的那位，1是负数0是正数）。</p><blockquote><p>The parity bit ensures that the total number of 1-bits in the string is even or odd. Accordingly, there are two variants of parity bits: even parity bit and odd parity bit. In the case of even parity, for a given set of bits, the bits whose value is 1 are counted. In the case of even parity, for a given set of bits, the bits whose value is 1 are counted. If that count is odd, the parity bit value is set to 1, making the total count of occurrences of 1s in the whole set (including the parity bit) an even number. If the count of 1s in a given set of bits is already even, the parity bit’s value is 0. In the case of odd parity, the coding is reversed. Even parity is a special case of a cyclic redundancy check (CRC), where the 1-bit CRC is generated by the polynomial x+1.</p></blockquote><p>奇偶校验位确保整个二进制数的<strong>“1”</strong>有偶数个或者奇数个。因此有两种变体：偶数校验位和奇数校验位。偶数校验位是如果二进制数原本有奇数个1，那就在LSB上补一个1让整个二进制数的1有偶数个，如果原本就有偶数个1，那只需要补一个0，保持偶数个；奇数校验位反之。<br>偶数校验位是一种循环冗余校验CRC，一个特例，通过多项式 x + 1 得到1位CRC。</p><blockquote><p>For example, suppose two drives in a three-drive RAID 5 array contained the following data:</p></blockquote><p>比方说现在有个三磁盘组的RAID5，</p><p>Drive 1:    01101101<br>Drive 2:    11010100<br>01101101 XOR 11010100 = 10111001</p><p>所以，</p><p>Drive 3: 10111001</p><p>第三个磁盘存的是前两个磁盘的异或值10111001，诶？异或跟奇偶校验位是怎么扯上关系的呢？</p><blockquote><p>Parity bits are written at the rate of one parity bit per n bits, where n is the number of disks in the array.<br>奇偶校验位按每n位（n是磁盘的个数）一个的速度被写入。</p></blockquote><p>实际上磁盘1和磁盘2按位对应计算偶数校验位（even parity bit）的结果就是磁盘3存入的XOR异或值。再多一些磁盘组RAID5也是一样的偶数校验位值。</p><blockquote><p>In electronics, transcoding data with parity can be very efficient, as XOR gates output what is equivalent to a check bit that creates an even parity, and XOR logic design easily scales to any number of inputs. XOR and AND structures comprise the bulk of most integrated circuitry.</p></blockquote><p>在电子元件里，因为异或门XORgate的输出相当于偶数校验位，所以计算是非常高效的，并且XOR可以轻易地扩展到所有输入上，XOR异或门和AND与门构成了大多数集成电路的大部分。</p><p>RAID里奇偶校验不光做错误检测，还可以错误校正，比如RAID5的磁盘最多可以坏一个，剩下的盘可以推导出坏盘的数据，还以上面的三盘为例，</p><p>Drive 1:    01101101<br>Drive 2:    11010100<br>Drive 3: 10111001</p><p>如果盘1坏了，那Drive2 XOR Drive3 = 11010100 XOR 10111001 =<br>01101101 = Drive1，可以复原盘1数据。</p><p>即$A\oplus{B}=C\Rightarrow{A}\oplus{C}=B$这里简单展开一下。</p><h3 id="operational-rule-of-Boo-lean-algebra，布尔运算律"><a href="#operational-rule-of-Boo-lean-algebra，布尔运算律" class="headerlink" title="operational rule of Boo-lean algebra，布尔运算律"></a>operational rule of Boo-lean algebra，布尔运算律</h3><div class="table-container"><table><thead><tr><th>Y</th><th>B = 0</th><th>B = 1</th></tr></thead><tbody><tr><td>A = 0</td><td>0</td><td>1</td></tr><tr><td>A = 1</td><td>1</td><td>0</td></tr></tbody></table></div><p>Y = A’ · B + A · B’ = A XOR B<br>XOR (/ˌɛks ˈɔːr/, /ˌɛks ˈɔː/, /ˈksɔːr/ or /ˈksɔː/), EOR, EXOR,  $\oplus$, $\nleftrightarrow$, $\not \equiv $. Exclusive or.</p><p>distributive law proof，(A+B)·C=A·C+B·C和A+B·C=(A+B)·(A+C)，布尔代数分配律，<br><img src="/pics/NAS/1.PNG" alt="X(Y +Z) = XY + XZ"/><br><img src="/pics/NAS/2.PNG" alt="X + YZ = (X + Y)(X + Z)"/><br>Unfortunately, the only proof of a distributive law in Boolean algebra is that very law written down, since it is <a href="https://www.quora.com/What-is-the-algebraic-proof-of-distributive-law-in-boolean-algebra#:~:text=Unfortunately%2C%20the%20only%20proof%20of,be%20proven%20through%20another%20law.">an independent axiom of BA which cannot be proven through another law.</a></p><p>根据恒等律：X ⊕ 0 = X，归零律：X ⊕ X = 0，和反演律：(A · B)’ = A’ + B’；(A + B)’ = A’ · B’（数学归纳法），得到XOR的结合律。</p><script type="math/tex; mode=display">\begin{align}(A \oplus B) \oplus C&= (A' · B + A · B') \oplus C \tag{truth table}\\&= (A' · B + A · B')' · C + (A' · B + A · B') · C ' \tag{truth table}\\&= ((A' · B)' · (A · B')')· C + A' · B · C ' + A · B' · C ' \tag{De Morgan's laws}\\&= ((A + B') · (A' + B))· C + A' · B · C ' + A · B' · C ' \tag{De Morgan's laws}\\&= (A · B + A' · B') · C + A' · B · C ' + A · B' · C ' \tag{distributivity}\\&= A · B · C + A' · B' · C + A' · B · C ' + A · B' · C ' \tag{distributivity}\\&\Rightarrow \tag{same}\\A \oplus (B \oplus C)&= A · B · C + A' · B' · C + A' · B · C ' + A · B' · C ' \tag{same}\\&= (A \oplus B) \oplus C \tag{i.e. Associative Law}\end{align}</script><p>所以$A \oplus C = A \oplus (A \oplus B) = (A \oplus A) \oplus B = 0 \oplus B = B$，就得到了<a href="#Parity-bit-奇偶校验-Check-bit-校验位">上面</a>的公式。<br>本来我是觉得证分配律需要用吸收律就把证明都写了下，结果写完发现没球关系，也懒得删了就放这吧。</p><p>absorption law proof，A·(A+B)=A（对偶定律，A+A·B=A）布尔代数吸收律，<br>看了一下很多证明都依赖于下面的distributivity，分配律的证明又依赖于吸收律，证了个寂寞，所以我比较喜欢<a href="https://cs.stackexchange.com/a/84660">这位的思路</a>，用min和max定义AND和OR。</p><script type="math/tex; mode=display">\begin{align*}x \land y &= x \cdot y = \min(x,y)\\x \lor y &= x + y = \max(x,y)\end{align*}</script><p>与操作就是求0和1中的较小值，或操作就是求0和1中的较大值，所以$A·(A+B)$就变成了$min(A,max(A,B))$，<br>当A小于B时，当A等于B时，当A大于B时都如下所示，</p><script type="math/tex; mode=display">\begin{align*}\min(A, \color{red}{\max(A,B)}) &= \min(A, \color{red}{B})\\ &= \min(A, B) = A\\\end{align*}</script><p>两个变量比较好分类讨论，三个变量就有点麻烦了。</p><h2 id="Hamming-Code"><a href="#Hamming-Code" class="headerlink" title="Hamming Code"></a>Hamming Code</h2><p>汉明码基本上是加强版奇偶校验码，设计是十分巧妙的。</p><blockquote><p>By contrast, the simple parity code cannot correct errors, and can detect only an odd number of bits in error.</p></blockquote><p>对比下，简单的奇偶校验不能校正错误，而且只能检测奇数个位的错误。<br>汉明码会分别对数据里不同的位分组，计算多次奇偶校验码，让每一位由多个校验码控制，这样虽然增加了redundancy但大大降低了检测不出错误的概率，而且由于能定位到错的是哪一位，就有了错误校正的能力。<br>$2^r-r-1$长度的数据上会附加长度为r的汉明码。</p><p>以7位二进制数据1011001为例，需要4位（$2^4-4-1=7$）汉明码，选择偶校验，<br><img src="/pics/NAS/hamming0.jpg" alt=""><br>二进制第一位是1的由R1存储校验码，第3、5、7、9、11位由于有4个1，R1=3⊕5⊕7⊕9⊕11=0，<br><img src="/pics/NAS/hamming1.jpg" alt=""><br>二进制第二位是1的由R2存储校验码，R2=3⊕6⊕7⊕10⊕11=1，<br><img src="/pics/NAS/hamming2.jpg" alt=""><br>二进制第三位是1的由R4($2^{3-1}$)存储校验码，R4=1，<br><img src="/pics/NAS/hamming3.jpeg.png" alt=""><br>二进制第四位是1的由R8($2^{4-1}$)存储校验码，R8=9⊕10⊕11=0，<br><img src="/pics/NAS/hamming4.jpg" alt=""><br>最后得到1011001被偶校验汉明编码的数据1010 1001 110。上面的校验位和数据位对应情况可以直接看下表，来自<a href="https://en.wikipedia.org/wiki/Hamming_code">wikipedia</a>。</p><table style="text-align:center;"><tbody><tr><th colspan="2"><span style="color:#888">Bit position</span></th><th><span style="color:#888">1</span></th><th><span style="color:#888">2</span></th><th><span style="color:#888">3</span></th><th><span style="color:#888">4</span></th><th><span style="color:#888">5</span></th><th><span style="color:#888">6</span></th><th><span style="color:#888">7</span></th><th><span style="color:#888">8</span></th><th><span style="color:#888">9</span></th><th><span style="color:#888">10</span></th><th><span style="color:#888">11</span></th><th><span style="color:#888">12</span></th><th><span style="color:#888">13</span></th><th><span style="color:#888">14</span></th><th><span style="color:#888">15</span></th><th><span style="color:#888">16</span></th><th><span style="color:#888">17</span></th><th><span style="color:#888">18</span></th><th><span style="color:#888">19</span></th><th><span style="color:#888">20</span></th><td rowspan="7">&#8230;</td></tr><tr><th colspan="2">Encoded data bits</th><th style="background-color: #90FF90;">p1</th><th style="background-color: #90FF90;">p2</th><th>d1</th><th style="background-color: #90FF90;">p4</th><th>d2</th><th>d3</th><th>d4</th><th style="background-color: #90FF90;">p8</th><th>d5</th><th>d6</th><th>d7</th><th>d8</th><th>d9</th><th>d10</th><th>d11</th><th style="background-color: #90FF90;">p16</th><th>d12</th><th>d13</th><th>d14</th><th>d15</th></tr><tr><th rowspan="5">Parity<br />bit<br />coverage</th><th style="background-color: #90FF90;">p1</th><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td></tr><tr><th style="background-color: #90FF90;">p2</th><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td></tr><tr><th style="background-color: #90FF90;">p4</th><td></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td></tr><tr><th style="background-color: #90FF90;">p8</th><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td></td><td></td><td></td><td></td><td></td></tr><tr><th style="background-color: #90FF90;">p16</th><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td><td data-sort-value="Yes" style="background: #DFD; vertical-align: middle; text-align: center;" class="table-yes2"><span typeof="mw:File"><span title="Yes">✔️</span></span></td></tr></tbody></table><h2 id="群论Galois-Field-i-e-Finite-Field-域"><a href="#群论Galois-Field-i-e-Finite-Field-域" class="headerlink" title="群论Galois Field (i.e. Finite Field)域"></a>群论Galois Field (i.e. Finite Field)域</h2><p>上面提到过，偶数校验位实际是通过多项式 x + 1 得到1位CRC的循环冗余校验，那在探索CRC之前必须学一下GF有限域。<br>在学习的时候遇到了一些困难，首先是定义和性质，看得我云里雾里，分别看了<a href="https://zh.wikipedia.org/zh-cn/有限域">维基百科</a>、<a href="https://zhuanlan.zhihu.com/p/161411524">知乎</a>和这篇文章的翻译出处Channel Codes: Classical and Modern（之后找个地方上传一下这些资源）之后，仍然没有搞清楚定义和性质的推导，不过好在对<a href="https://zh.wikipedia.org/zh-cn/有限域算术">有限域的运算</a>理解了一些，这已经足够大概理解CRC了。</p><p>以下定义将直接摘抄自Channel Codes: Classical and Modern，这本书是美籍华人Adjunct Professor<a href="https://faculty.engineering.ucdavis.edu/lin/biography/">林舒</a>和William E. Ryan的著作（2009年）。</p><h3 id="Field"><a href="#Field" class="headerlink" title="Field"></a>Field</h3><p>一个基本概念——</p><blockquote><p>Definition 2.2. Let S be a set with a binary operation ∗. The operation is said to be commutative if, given any two elements, a and b, in S, the following equality holds:<br>a∗b = b∗a. (2.2)<br>We also say that ∗ satisfies the commutative law, if (2.2) holds.</p></blockquote><p>如果一个二元操作∗（不是乘法，任意的二元操作）在S上对任意元素都满足a∗b = b∗a，我们就说二元操作∗是可交换的，也说∗满足交换律。</p><blockquote><p><strong>2.3 Fields</strong><br>In this section, we introduce an algebraic system with two binary operations, called a field. Fields with finite numbers of elements, called finite fields, play an important role in developing algebraic coding theory and constructing error-correction codes that can be efficiently encoded and decoded. Well-known and widely used error-correction codes in communication and storage systems based on finite fields are BCH (Bose–Chaudhuri–Hocquenghem) codes and RS (Reed–Solomon) codes, which were introduced at the very beginning of the 1960s. Most recently, finite fields have successfully been used for constructing low-density parity-check (LDPC) codes that perform close to the Shannon limit with iterative decoding.</p></blockquote><p>在信道编码的2.3节中，在介绍完2.1集合和二元运算(Sets and Binary Operations)、2.2群(Groups)之后，开始讲解有两个二元运算的代数系统——域。包含有限元素的域叫做有限域（galois），有限域在代数编码理论的发展中、在构建能高效编解码的错误校正编码中扮演了重要角色。在通信和存储系统中，基于有限域的广为人知的错误校正码有BCH码、RS码，都是在六十年代早期被发明的。最近，有限域被成功地用在构建通过迭代解码、性能接近香农极限的低密度奇偶检测（LDPC）码上。</p><blockquote><p>2.3.1 Definitions and Basic Concepts<br>Definition 2.8. Let F be a set of elements on which two binary operations, called addition ‘‘+’’ and multiplication ‘‘·,’’ are defined. F is a field under these two operations, addition and multiplication, if the following conditions (or axioms) are satisfied.</p><ol><li>F is a commutative group under addition +. The identity element with respect to addition + is called the zero element (or additive identity) of F and is denoted by 0.</li><li>The set F\\{0} of nonzero elements of F forms a commutative group under multiplication ·. The identity element with respect to multiplication · is called the unit element (or multiplicative identity) of F and is denoted by 1.</li><li>For any three elements a, b, and c in F, $a·(b+c) = a·b+a·c$ The equality is called the distributive law, i.e., multiplication is distributive over addition.</li></ol></blockquote><p>定义和基本概念。设F是一个两个二元运算（加和乘）上的集合。在满足以下情况（或公理）时这个F是加和乘运算下的域，</p><ol><li>F是个加法的交换群。与加法相关的<a href="https://zh.wikipedia.org/zh-cn/單位元">恒等元（identity element）</a>被称为F的零元或<a href="https://zh.wikipedia.org/zh-cn/加法單位元">加法单位元</a>，用0表示。</li><li>集合F\\{0}（the set that contains the elements in X but not in Y is called the difference between X and Y and is denoted by X\\Y）即没有0元素的F构成了乘法的交换群。乘法相关的恒等元叫做单位元或乘法单位元，用1表示。</li><li>对于F内的任意三个元素a，b，c，$a·(b+c) = a·b+a·c$满足分配律，乘法可以分配到加法上。</li></ol><blockquote><p>From the definition, we see that a field consists of two groups with respect to two operations, addition and multiplication. The group with respect to addition is called the additive group of the field, and the group with respect to multiplication is called the multiplicative group of the field. Since each group must contain an identity element, a field must contain at least two elements. Later, we will show that a field with two elements does exist. In a field, the additive inverse of an element a is denoted by ‘‘−a,’’ and the multiplicative inverse of a is denoted by ‘‘a−1,’’ provided that a ̸= 0. From the additive and multiplicative inverses of ele- ments of a field, two other operations, namely subtraction ‘‘−’’ and division ‘‘÷,’’ can be defined.<br><strong>A field is simply an algebraic system in which we can perform addition, subtraction, multiplication, and division without leaving the field.</strong></p></blockquote><p>根据定义，可以发现一个域包含两个关于二元操作的群，加法和乘法。关于加法的叫做域的加法群，关于乘法的叫域的乘法群。因为每个群必须包含一个恒等元（单位元），所以一个域至少包含两个元素{0, 1}。之后会证明两个元素的域确实存在。在域中，加法的a反过来记作“-a”，乘法的a反过来记作“a-1”（a均不等于0），从这两个相反（逆运算）可以得到subtraction ‘‘−’’ 减和division ‘‘÷” 除操作。$a − b \triangleq a + (−b)$，很明显a-a=0；$a ÷ b \triangleq a · (b−1)$，很明显a÷a=1。<br><strong>域就是可以在域中的任意元素上做加减乘除运算，结果仍在域内的代数系统。</strong></p><p><img src="/pics/NAS/definition.png" alt="definition"/></p><blockquote><p>Definition 2.9. The number of elements in a field is called the order of the field. A field with finite order is called a finite field.<br>Definition 2.10. Let F be a field. A subset K of F that is itself a field under the operations of F is called a subfield of F. In this context, F is called an extension field of K. If K ̸= F, we say that K is a proper subfield of F. A field containing no proper subfields is called a prime field.<br>Definition 2.11. Let F be a field and 1 be its unit element (or multiplicative identity). The characteristic of F is defined as the smallest positive integer λ such that $\sum_{i=1}^\lambda 1=\underbrace{1+1+\cdots+1}_\lambda=0$ where the summation represents repeated applications of addition + of the field. If no such λ exists, F is said to have zero characteristic, i.e., λ = 0, and F is an infinite field.<br>Theorem 2.4. The characteristic λ of a finite field is a prime.</p></blockquote><p>定义，域里元素的数量叫做域的<strong>阶</strong>order，阶数有限的域叫有限域。<br>定义，设F是一个域，F的子集K如果是满足F的操作的域那就叫F的子域。如果K不等于F，K就是一个恰当的子域。一个不包含任何恰当子域的域就叫素数域。<br>定义，设F是一个域而且1是乘法单位元，F的<strong>特征</strong>characteristic被定义为满足公式 $\sum_{i=1}^\lambda 1=\underbrace{1+1+\cdots+1}_\lambda=0$ 的最小正整数λ，其中的累加是重复域F里的加法操作。如果不存在这样的λ，F被称作有0特征，即λ=0且F是一个无线域。<br>定理，有限域的特征λ是一个素数。</p><p>构造扩展域（extenstion field）所需的不可约多项式（Irreducible Polynomial），本原多项式（Primitive Polynomial），还有群（Groups）论的概念之后再细讲。<br>重要的是下面的这几段，关于有限域和有限域上的多项式——</p><h3 id="Finite-Fields"><a href="#Finite-Fields" class="headerlink" title="Finite Fields"></a>Finite Fields</h3><blockquote><p><strong>2.3.2 Finite Fields</strong><br>The rational-, real- and complex-number fields are infinite fields. In error-control coding, both theory and practice, we mainly use finite fields.<br>Let p be a prime number. In Section 2.2.2, we have shown that the set of inte- gers, {0, 1, . . ., p − 1}, forms a commutative group under modulo-p addition $\boxplus$. We have also shown that the set of nonzero elements, {1, 2, . . ., p − 1}, forms a commutative group under modulo-p multiplication $\boxdot$. Following the definitions of modulo-p addition and multiplication and the fact that real-number multiplication is distributive over real number addition, we can readily show that the modulo-p multiplication is distributive over modulo-p addition. Therefore, the set of integers, {0, 1, . . ., p − 1}, forms a finite field GF(p) of order p under modulo-p addition and multiplication, where 0 and 1 are the zero and unit elements of the field, respectively. The following sums of the unit element 1 give all the elements of the field:<br>$1, \quad \sum_{i=1}^2 1=2, \quad \sum_{i=1}^3 1=1 \boxplus 1 \boxplus 1=3, \quad \ldots, \quad \sum_{i=1}^{p-1} 1=p-1, \quad \sum_{i=1}^p 1=0$<br>Since p is the smallest positive integer such that $\sum_{i=1}^p 1=0$, the characteristic of the field is p. The field contains no proper subfield and hence is a prime field.</p></blockquote><p>有理数、实数和复数域都是无限域。在错误控制编码的理论和实践中，我们常用的都是<strong>有限域</strong>。<br>设p是一个素数（不是素数的待会儿会讨论到），在群的小节里这本书讨论过，</p><ol><li>整数集{0, 1, . . ., p − 1}构成模p加法（modulo-p addition $\boxplus$）的交换群，</li><li>还有无零元素集合{1, 2, . . ., p − 1}构成模p乘法（modulo-p multiplication $\boxdot$）交换群。</li><li>模p就是运算后对p取模，比如modulo-7的$1 \boxplus 9 = 10\ mod\ 7 = 3$。根据模运算定义和实数中乘法满足对加法的分配律的基本事实，可以证明模p乘法对模p加法也满足分配率。</li></ol><p>三个条件都满足了，这个{0, 1, . . ., p − 1}就构成了阶数为p的有限域GF(p)，0和1分别是域上的加法单位元和乘法单位元。<br>接下来的累加就是域里的所有元素：<br>$1, \quad \sum_{i=1}^2 1=2, \quad \sum_{i=1}^3 1=1 \boxplus 1 \boxplus 1=3, \quad \ldots, \quad \sum_{i=1}^{p-1} 1=p-1, \quad \sum_{i=1}^p 1=0$，<br>因为p是最小的满足$\sum_{i=1}^p 1=0$的正整数，因此p是这个域的特征（即λ=p）。这个域不包含任何恰当的子域因此是一个素数域。</p><p>需要记住特征就是要用来模运算的数，阶数是域的元素数。</p><blockquote><p>Example 2.8. Consider the special case for which p = 2. For this case, the set {0, 1} under modulo-2 addition and multiplication as given by Tables 2.6 and 2.7 forms a field of two elements, denoted by GF(2).<br>Note that {1} forms the multiplicative group of GF(2), a group with only one element. The two elements of GF(2) are simply the additive and multiplicative identities of the two groups of GF(2). The additive inverses of 0 and 1 are themselves. This implies that 1 − 1 = 1 + 1. Hence, over GF(2), subtraction and addition are the same. The multiplicative inverse of 1 is itself. GF(2) is commonly called a binary field, the simplest finite field. GF(2) plays an important role in coding theory and is most commonly used as the alphabet of code symbols for error-correction codes.</p></blockquote><h4 id="GF-2"><a href="#GF-2" class="headerlink" title="GF(2)"></a>GF(2)</h4><p>关于<strong>CRC要用到的GF(2)</strong>，正好书里有个例子。<br>p=2的时候，在modulo-2加和乘操作下的集合{0,1}结果如下，</p><script type="math/tex; mode=display">\begin{equation}\begin{array}{lll}\hline \hline \boxplus & 0 & 1 \\\hline 0 & 0 & 1 \\1 & 1 & 0 \\\hline \hline\tag{modulo-2 plus}\end{array}\end{equation}</script><script type="math/tex; mode=display">\begin{equation}\begin{array}{lll}\hline \hline \boxdot & 0 & 1 \\\hline 0 & 0 & 0 \\1 & 0 & 1 \\\hline \hline\tag{modulo-2 mutiplication}\end{array}\end{equation}</script><p>构成了一个包含两个元素的域，记为GF(2)。<br>{1}构成了GF(2)的乘法群，只有一个元素的群。GF(2)的两个元素就是GF(2)的两个群的加法单位元和乘法单位元。0和1的加法就是减法的结果，如1-1=1+1。因此，在GF(2)上的加法addition和减法subtraction操作是一样的，1的乘法的相反（逆运算）也是它自己，1乘1等于1除1。GF(2)通常被叫做二元域，也是最简单的有限域。GF(2)在编码理论里扮演了重要角色，并最常被用于ECC的编码符号字母表。<br>同理可以算出来在GF(5)上的modulo-5运算结果，两个对称矩阵。这里原书写错了。</p><script type="math/tex; mode=display">\begin{equation}\begin{array}{llllll}\hline \hline \boxplus & 0 & 1 & 2 & 3 & 4 \\\hline 0 & 0 & 1 & 2 & 3 & 4 \\1 & 1 & 2 & 3 & 4 & 0 \\2 & 2 & 3 & 4 & 0 & 1 \\3 & 3 & 4 & 0 & 1 & 2 \\4 & 4 & 0 & 1 & 2 & 3 \\\hline \hline\tag{modulo-5 addition}\end{array}\end{equation}</script><script type="math/tex; mode=display">\begin{equation}\begin{array}{llllll}\hline \hline \boxdot & 0 & 1 & 2 & 3 & 4 \\\hline 0 & 0 & 0 & 0 & 0 & 0 \\1 & 0 & 1 & 2 & 3 & 4 \\2 & 0 & 2 & 4 & 1 & 3 \\3 & 0 & 3 & 1 & 4 & 2 \\4 & 0 & 4 & 3 & 2 & 1 \\\hline \hline\tag{modulo-5 multiplication}\end{array}\end{equation}</script><p>现在讨论一下阶数不是素数的域。</p><blockquote><p>We have shown that, for every prime number p, there exists a prime field GF(p). For any positive integer m, it is possible to construct a Galois field $GF(p^m)$ with $p^m$ elements based on the prime field GF(p) and a root of a special irreducible polynomial with coefficients from GF(p). This will be shown in a later section. The Galois field $GF(p^m)$ contains GF(p) as a subfield and is an extension field of GF(p). A very important feature of a finite field is that its order must be a power of a prime. That is to say that the order of any finite field is a power of a prime.</p></blockquote><p>对素数p，都有一个素数域GF(p)。对任何正整数m，都可以构建一个基于素数域GF(p)的包含$p^m$个元素的有限域$GF(p^m)$，和一个由GF(p)构成系数的特殊<strong>不可约多项式的根</strong>。（之后的章节才继续讨论，这个我在本文也不展开了。）有限域$GF(p^m)$包含子域GF(p)，是一个GF(p)的扩展域。有限域一个很重要的特性是<strong>阶数一定要是素数的幂</strong>，任何有限域的阶数都是素数的幂。</p><blockquote><p>Definition 2.12. Let a be a nonzero element of a finite field GF(q). The smallest positive integer n such that $a^n = 1$ is called the order of the nonzero field element a.<br>Theorem 2.5. Let a be a nonzero element of order n in a finite field GF(q). Then the powers of a, $a^n=1, a, a^2, \ldots, a^{n-1}$ form a cyclic subgroup of the multiplicative group of GF(q).<br>Theorem 2.6. Let a be a nonzero element of a finite field GF(q). Then $a^{q−1} = 1$.<br>Theorem 2.7. Let n be the order of a nonzero element a in GF(q). Then n divides q − 1.<br>Definition 2.13. A nonzero element a of a finite field GF(q) is called a primitive element if its order is q − 1. That is, $a^{q−1} = 1,a,a2, …,a^{q−2}$ form all the nonzero elements of GF(q).</p></blockquote><p>定义，设a是有限域GF(q)的非零元素，能使$a^n = 1$的最小的正整数n叫做非零域元素a的阶。<br>定理，设a是阶为n的有限域GF(q)上的非零元素，a的幂构成了GF(q)乘法群的一个循环子群。<br>定理，设a是有限域GF(q)的非零元素，则$a^{q−1} = 1$。<br>定律，设n是有限域GF(q)上非零元素a的阶，那么n能整除q-1即(q-1) mod n = 0。比如GF(5)上2的阶是4，$2^4=16\ mod\ 5 = 1$，4 mod 4 = 0。证明都在书上，我就不贴了。<br>定义，有限域GF(q)的非零元素a如果阶是q - 1则被称作<strong>本原元</strong>。即$a^{q−1} = 1,a,a2, …,a^{q−2}$构成了GF(q)的所有非零元素。</p><h3 id="Polynomials-over-Finite-Fields"><a href="#Polynomials-over-Finite-Fields" class="headerlink" title="Polynomials over Finite Fields"></a>Polynomials over Finite Fields</h3><blockquote><p><strong>2.5 Polynomials over Finite Fields</strong><br>In this section, we consider polynomials with coefficients from a finite field GF(q), which are used in error-control coding theory. A polynomial with one variable (or indeterminate) X over GF(q) is an expression of the following form:<br>$a(X) = a_0 + a_1X + …, a_nX^{n}$<br>where n is a non-negative integer and the coefficients $a_i$, 0 ≤ i ≤ n, are elements of GF(q). The degree of a polynomial is defined as the largest power of X with a nonzero coefficient. For the polynomial a(X) above, if $a_n \neq 0$, its degree is n; if $a_n = 0$, its degree is less than n. The degree of a polynomial $a(X) = a_0$ with only the constant term is zero. We use the notation deg(a(X)) to denote the degree of polynomial a(X). A polynomial is called monic if the coefficient of the highest power of X is 1 (the unit element of GF(q)). The polynomial whose coefficients are all equal to zero is called the zero polynomial and denoted by 0.</p></blockquote><p>在这里我们只考虑在错误控制编码理论中使用的，使用有限域GF(q)的元素作为系数的多项式。一个GF(q)上的只有一个变量X的多项式形式如$a(X) = a_0 + a_1X + …, a_nX^{n}$，n是非负整数，系数$a_i$是GF(q)的元素。多项式的阶被定义为非零系数的X的最大的幂次（不太喜欢把域的阶order和多项式的阶degree写成一样，所以之后都用英文）。对于前面的多项式a(X)，如果$a_n \neq 0$，degree就是n，如果$a_n = 0$，degree就小于n。只有一个常数项的多项式$aX = a_0$的degree是0。我们使用deg(a(X))来表示a(X)的degree。当X最高幂次是1时多项式被称为首一多项式（即GF(q)的乘法单位元），如果多项式的所有系数都是0那就叫0多项式并且用0表示。</p><p>接下来分别说一下<strong>多项式的四则运算</strong>。The arithmetic of polynomials is governed by familiar rules, such as addition, subtraction, multiplication, and division.</p><p>对于这样两个多项式，$a(X)=a_{0}+a_{1} X+\cdots+a_{n} X^{n}, \quad b(X)=b_{0}+b_{1} X+\cdots+b_{m} X^{m}$——</p><h4 id="Addition"><a href="#Addition" class="headerlink" title="Addition"></a><strong>Addition</strong></h4><script type="math/tex; mode=display">\begin{aligned}a(X)+b(X)= & \left(a_{0}+b_{0}\right)+\left(a_{1}+b_{1}\right) X+\cdots+\left(a_{m}+b_{m}\right) X^{m} \\& +\left(a_{m+1}+0\right) X^{m+1}+\cdots+\left(a_{n}+0\right) X^{n}\end{aligned}</script><h4 id="Multiplication"><a href="#Multiplication" class="headerlink" title="Multiplication"></a><strong>Multiplication</strong></h4><p>$a(X) \cdot b(X)=c_{0}+c_{1} X+\cdots+c_{k} X^{k}+\cdots+c_{n+m} X^{n+m}$<br>$c_{k}=\sum_{\substack{i+j=k, 0 \leq i \leq n, 0 \leq j \leq m}} a_{i} \cdot b_{j}$</p><hr><p>we can readily verify that the polynomials over $\operatorname{GF}(q)$ satisfy the following conditions.</p><ol><li>Associative laws. For any three polynomials $a(X), b(X)$, and $c(X)$ over $\mathrm{GF}(q)$,</li></ol><script type="math/tex; mode=display">\begin{aligned}a(X)+[b(X)+c(X)] & =[a(X)+b(X)]+c(X) \\a(X) \cdot[b(X) \cdot c(X)] & =[a(X) \cdot b(X)] \cdot c(X)\end{aligned}</script><ol><li>Commutative laws. For any two polynomials $a(X)$ and $b(X)$ over $\operatorname{GF}(q)$,</li></ol><script type="math/tex; mode=display">\begin{aligned}a(X)+b(X) & =b(X)+a(X) \\a(X) \cdot b(X) & =b(X) \cdot a(X)\end{aligned}</script><ol><li>Distributive law. For any three polynomials $a(X), b(X)$, and $c(X)$ over $\mathrm{GF}(q)$,</li></ol><script type="math/tex; mode=display">a(X) \cdot[b(X)+c(X)]=a(X) \cdot b(X)+a(X) \cdot c(X)</script><p>In algebra, the set of polynomials over $\operatorname{GF}(q)$ under polynomial addition and multiplication defined above is called a <strong>polynomial ring</strong> over $\mathrm{GF}(q)$.</p><hr><h4 id="Subtraction"><a href="#Subtraction" class="headerlink" title="Subtraction"></a><strong>Subtraction</strong></h4><p>Subtraction of $b(X)$ from $a(X)$ is carried out as follows:</p><script type="math/tex; mode=display">\begin{aligned}a(X)-b(X)= & \left(a_{0}-b_{0}\right)+\left(a_{1}-b_{1}\right) X+\cdots+\left(a_{m}-b_{m}\right) X^{m} \\& +\left(a_{m+1}-0\right) X^{m+1}+\cdots+\left(a_{n}-0\right) X^{n}\end{aligned}</script><p>where $a_{i}-b_{i}=a_{i}+\left(-b_{i}\right)$ is carried out in $\operatorname{GF}(q)$ and $-b_{i}$ is the additive inverse of $b_{i}$.</p><h4 id="Division"><a href="#Division" class="headerlink" title="Division"></a><strong>Division</strong></h4><blockquote><p>Definition 2.19. A set $\mathcal{R}$ with two binary operations, called addition + and multiplication $\cdot$, is called a ring, if the following axioms are satisfied.</p><ol><li>$\mathcal{R}$ is a commutative group with respect to + .</li><li>Multiplication “.” is associative, i.e., $(a \cdot b) \cdot c=a \cdot(b \cdot c)$ for all $a, b, c \in \mathcal{R}$.</li><li>The distributive laws hold: for $a, b, c \in \mathcal{R}, a \cdot(b+c)=a \cdot b+a \cdot c$ and $(b+c) \cdot a=b \cdot a+b \cdot a$.</li></ol><p>The set of all polynomials over $\operatorname{GF}(q)$ defined above satisfies all three axioms of a ring. Therefore, it is a ring of polynomials. The set of all integers under real addition and multiplication is a ring. For any positive integer $m \geq 2$, the set $\mathcal{R}=$ $\{0,1, \ldots, m-1\}$ under modulo- $m$ addition and multiplication forms a ring (the proof is left as an exercise). In Section 2.3, it was shown that, if $m$ is a prime, $\mathcal{R}=$ $\{0,1, \ldots, m-1\}$ is a prime field under modulo- $m$ addition and multiplication.</p></blockquote><p><strong>定义</strong>，如果一个有两个二元运算加+和乘·的集合$\mathcal{R}$叫做一个环Ring，要以下几个公理。</p><ol><li>$\mathcal{R}$是一个加法的交换群。</li><li>集合的任意元素间的乘法满足结合律。</li><li>集合的任意元素间的运算满足分配率。</li></ol><p>GF(q)的所有多项式的集合满足以上三个环的功力，因此GF(q)的所有多项式是一个多项式环。所有实数整数的加法和乘法是一个环。对于任意的正整数$m \geq 2$，在modulo-m加法和乘法下的集合$\mathcal{R}= \{0,1, \ldots, m-1\}$构成一个环。在上面讨论过，如果m是一个素数，$\mathcal{R}= \{0,1, \ldots, m-1\}$就是一个modulo-m加法和乘法运算下的素数域。</p><p>（这里简单提一下，在抽象代数里最先学群论，因为群的概念最大，环次之，域最小，一个环一定是一个群，一个域一定是一个环，所谓环可能指的是域里的运算始终指回域本身的这种现象，知乎有很多有趣的解释比如ring其实指团伙。）</p><blockquote><p>Let $a(X)$ and $b(X)$ be two polynomials over $\operatorname{GF}(q)$. Suppose that $b(X)$ is not the zero polynomial, i.e., $b(X) \neq 0$. When $a(X)$ is divided by $b(X)$, we obtain a unique pair of polynomials over $\mathrm{GF}(q), q(X)$ and $r(X)$, such that</p><p><script type="math/tex">a(X)=q(X) \cdot b(X)+r(X)</script>where $0 \leq \operatorname{deg}(r(X))&lt;\operatorname{deg}(b(X))$ and the polynomials $q(X)$ and $r(X)$ are called the quotient and remainder, respectively. This is known as Euclid’s division algorithm. The quotient $q(x)$ and remainder $r(X)$ can be obtained by ordinary long division of polynomials. If $r(X)=0$, we say that $a(X)$ is divisible by $b(X)$ (or $b(X)$ divides $a(X))$.</p></blockquote><p>$a(X)=q(X) \cdot b(X)+r(X)$其中$0 \leq \operatorname{deg}(r(X))，&lt;\operatorname{deg}(b(X))$，多项式$q(X)$和$r(X)$分别被称为商和余，即欧几里得除法，$q(X)$和$r(X)$可以由多项式的长除算得。如果$r(X)$，我们就说$b(X)$整除$a(X)$，即$a(X)$ mod $b(X)$ = 0。<br>除法比较复杂所以演算一个书上的例子。</p><blockquote><p>Example 2.13. Let $a(x)=3+4 X+X^{4}+2 X^{5}$ and $b(X)=1+3 X^{2}$. Suppose we divide $a(X)$ by $b(X)$. Using long division,<br><img src="/pics/NAS/Screen Shot 2023-10-30 at 5.46.09 PM.png" alt="example 2.13"/></p></blockquote><p>定理和性质就不再讨论了，理解CRC需要的知识现在都具备了。</p><h2 id="Cyclic-Redundancy-Check"><a href="#Cyclic-Redundancy-Check" class="headerlink" title="Cyclic Redundancy Check"></a>Cyclic Redundancy Check</h2><h3 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h3><blockquote><p>A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to digital data. Blocks of data entering these systems get a short check value attached, based on the remainder of a polynomial division of their contents.<br>CRCs are so called because the check (data verification) value is a redundancy (it expands the message without adding information) and the algorithm is based on cyclic codes. CRCs are popular because they are simple to implement in binary hardware, easy to analyze mathematically, and particularly good at detecting common errors caused by noise in transmission channels. Because the check value has a fixed length, the function that generates it is occasionally used as a hash function.</p></blockquote><p>循环冗余校验是一个通常用于数字的网络和存储设备的错误校验码，来检测偶然的数据变化。根据某个多项式和数据内容做除法的余数，进入这些系统的数据块会被附加一个短的校验值。<br>循环冗余校验这个名字来自于，1.这个附加的校验值是冗余的（扩增了消息但没有增加信息）；2.算法基于循环码。CRCs由于易于在二进制硬件上实现、易于从数学上分析并且在检测常见的、由传输通道中的噪声造成的错误时尤其有效而被广泛使用。由于校验值是固定长度的，生成校验值的函数偶尔被用作哈希函数。</p><blockquote><p>Specification of a CRC code requires definition of a so-called generator polynomial. This polynomial becomes the divisor in a polynomial long division, which takes the message as the dividend and in which the quotient is discarded and the remainder becomes the result. The important caveat is that the polynomial coefficients are calculated according to the arithmetic of a finite field, so the addition operation can always be performed bitwise-parallel (there is no carry between digits).<br>In practice, all commonly used CRCs employ the finite field of two elements, GF(2). The two elements are usually called 0 and 1, comfortably matching computer architecture.</p></blockquote><p>CRC码需要定义一个生成多项式。这个多项式作为<a href="#division">多项式长除</a>的除数，消息本身作为被除数，商被舍弃而余数作为结果。一个重要的警告是，<strong>多项式系数</strong>是根据有限域的运算方式计算的，因此<strong>加法运算始终可以按位并行执行（数字之间没有进位，因为进位的要被模运算消除掉）</strong>。<br>在实践中所有常用的循环冗余检测都基于两个元素的有限域<a href="#gf2">GF(2)</a>，这两个元素通常是0和1，跟计算机架构很舒服地匹配了。</p><blockquote><p>CRCs are specifically designed to protect against common types of errors on communication channels, where they can provide quick and reasonable assurance of the integrity of messages delivered. However, they are not suitable for protecting against intentional alteration of data.<br>an attacker can edit a message and recompute the CRC without the substitution being detected.<br>unlike cryptographic hash functions, CRC is an easily reversible function, which makes it unsuitable for use in digital signatures.</p></blockquote><p>CRC是为了保护常见的通信通道的错误被特别设计的，可以快速地确保消息传递的完整。但是CRC对于有意修改的数据是不适用的。攻击者可以修改一个消息并且重新计算CRC（知道固定的多项式）并不会被检测到替换。<br>CRC不像密码学哈希函数，CRC很容易翻过来计算，所以不能用于数字签名。</p><h3 id="Computation-modulo-2-division-模二除法"><a href="#Computation-modulo-2-division-模二除法" class="headerlink" title="Computation, modulo-2 division(模二除法)"></a>Computation, modulo-2 division(模二除法)</h3><h4 id="为什么用多项式算CRC？"><a href="#为什么用多项式算CRC？" class="headerlink" title="为什么用多项式算CRC？"></a>为什么用多项式算CRC？</h4><p>这是个重要的问题。<br>关于为什么用多项式算余数而不是直接用二进制数计算，一种解答是“多项式表示循环冗余校验过程比较方便”，我的理解是，</p><ol><li>用多项式可以直接根据x的power幂次判断1分别处在第几位；</li><li>打个比方，现在要计算101 mod 11(modulo-2 division)，<br>2.1 GF(2)上只有{0,1}这两个单位元，不存在101和11这两个元素，所以如果你要计算101模二除以11，实际上跟GF(2)没什么关系。而$x^2+1$和$x+1$这两个多项式可以在GF(2)上计算，因为x取{0,1}实际上用modulo-2 addition和modulo-2 multiplication结果还是在{0,1}里；<br>2.2 为什么非要用模二除？模二除不需要退位，因此可以用XOR计算，相当高效。但实际上模二除跟正常除法的商和余数都是没关系的。<a href="https://stackoverflow.com/a/50178761">A polynomial over GF(2) can be represented as a string of bits. An integer can be represented as a string of bits. There the similarity ends. They are two entirely different beasts. </a>这个通过下面那段（来自<a href="https://asecuritysite.com/comms/mod_div">asecuritysite</a>的）代码计算101和11的例子就一目了然了。</li></ol><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Binary form:101  divided by  11</span><br><span class="line">Decimal form:5  divided by  3</span><br><span class="line"></span><br><span class="line">x**2+1</span><br><span class="line">x+1</span><br><span class="line"></span><br><span class="line">Divide operation:</span><br><span class="line"></span><br><span class="line">Result is11</span><br><span class="line">Remainder is0</span><br><span class="line">Working is</span><br><span class="line"></span><br><span class="line"> 11 </span><br><span class="line">--- </span><br><span class="line">101</span><br><span class="line">11</span><br><span class="line">--</span><br><span class="line"> 11</span><br><span class="line"> 11</span><br><span class="line"> --</span><br><span class="line">  0</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> struct</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line">val1=<span class="string">'1010'</span></span><br><span class="line">val2=<span class="string">'110'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (len(sys.argv)&gt;<span class="number">1</span>):</span><br><span class="line">  val1=str(sys.argv[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (len(sys.argv)&gt;<span class="number">2</span>):</span><br><span class="line">  val2=str(sys.argv[<span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">showpoly</span><span class="params">(a)</span>:</span></span><br><span class="line">  str1 = <span class="string">""</span></span><br><span class="line">  nobits = len(a)</span><br><span class="line">  <span class="keyword">for</span> x <span class="keyword">in</span> range (<span class="number">0</span>,nobits<span class="number">-2</span>):</span><br><span class="line">    <span class="keyword">if</span> (a[x] == <span class="string">'1'</span>):</span><br><span class="line">      <span class="keyword">if</span> (len(str1)==<span class="number">0</span>):</span><br><span class="line">        str1 +=<span class="string">"x**"</span>+str(nobits-x<span class="number">-1</span>)</span><br><span class="line">      <span class="keyword">else</span>: </span><br><span class="line">        str1 +=<span class="string">"+x**"</span>+str(nobits-x<span class="number">-1</span>)</span><br><span class="line">  <span class="keyword">if</span> (a[nobits<span class="number">-2</span>] == <span class="string">'1'</span>):</span><br><span class="line">    <span class="keyword">if</span> (len(str1)==<span class="number">0</span>):</span><br><span class="line">      str1 +=<span class="string">"x"</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">      str1 +=<span class="string">"+x"</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (a[nobits<span class="number">-1</span>] == <span class="string">'1'</span>):</span><br><span class="line">    str1 +=<span class="string">"+1"</span></span><br><span class="line">  print(str1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">toList</span><span class="params">(x)</span>:</span></span><br><span class="line">  l = []</span><br><span class="line">  <span class="keyword">for</span> i <span class="keyword">in</span> range (<span class="number">0</span>,len(x)):</span><br><span class="line">    l.append(int(x[i]))</span><br><span class="line">  <span class="keyword">return</span> (l)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">toString</span><span class="params">(x)</span>:</span></span><br><span class="line">  str1 =<span class="string">""</span></span><br><span class="line">  <span class="keyword">for</span> i <span class="keyword">in</span> range (<span class="number">0</span>,len(x)):</span><br><span class="line">    str1+=str(x[i])</span><br><span class="line">  <span class="keyword">return</span> (str1)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">divide</span><span class="params">(val1,val2)</span>:</span></span><br><span class="line">  a = toList(val1)</span><br><span class="line">  b = toList(val2)</span><br><span class="line">  working=<span class="string">""</span></span><br><span class="line">  res=<span class="string">""</span></span><br><span class="line">  <span class="keyword">while</span> len(b) &lt;= len(a) <span class="keyword">and</span> a:</span><br><span class="line">    <span class="keyword">if</span> (a[<span class="number">0</span>] == <span class="number">1</span>):</span><br><span class="line">      <span class="keyword">del</span> a[<span class="number">0</span>]</span><br><span class="line">      <span class="keyword">for</span> j <span class="keyword">in</span> range(len(b)<span class="number">-1</span>):</span><br><span class="line">        a[j] ^= b[j+<span class="number">1</span>]</span><br><span class="line">      <span class="keyword">if</span> (len(a)&gt;<span class="number">0</span>):</span><br><span class="line">        working +=toString(a)</span><br><span class="line">        res+= <span class="string">"1"</span></span><br><span class="line">      <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">del</span> a[<span class="number">0</span>]</span><br><span class="line">        working +=toString(a)</span><br><span class="line">        res+=<span class="string">"0"</span></span><br><span class="line">  print(<span class="string">"Result is\t"</span>,res)</span><br><span class="line">  print(<span class="string">"Remainder is\t"</span>,toString(a))</span><br><span class="line">  <span class="keyword">return</span> </span><br><span class="line"></span><br><span class="line">print(<span class="string">"Binary form:\t"</span>,val1,<span class="string">" divided by "</span>,val2)</span><br><span class="line">print(<span class="string">"Decimal form:\t"</span>,int(val1,<span class="number">2</span>),<span class="string">" divided by "</span>,int(val2,<span class="number">2</span>))</span><br><span class="line">print(<span class="string">""</span>)</span><br><span class="line"></span><br><span class="line">showpoly(val1)</span><br><span class="line">showpoly(val2)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"\nDivide operation:\n"</span>)</span><br><span class="line"></span><br><span class="line">divide(val1,val2)</span><br></pre></td></tr></table></figure><p>所以用多项式计算CRC。<br>11不可以在GF(2)而x+1可以在GF(2)对吗？</p><h4 id="Example"><a href="#Example" class="headerlink" title="Example"></a><a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check">Example</a></h4><blockquote><p>To compute an n-bit binary CRC, line the bits representing the input in a row, and position the (n + 1)-bit pattern representing the CRC’s divisor (called a “polynomial”) underneath the left end of the row.<br>In this example, we shall encode 14 bits of message with a 3-bit CRC, with a polynomial x^3 + x + 1. The polynomial is written in binary as the coefficients; a 3rd-degree polynomial has 4 coefficients (1x^3 + 0x^2 + 1x + 1). In this case, the coefficients are 1, 0, 1 and 1. The result of the calculation is 3 bits long, which is why it is called a 3-bit CRC. However, you need 4 bits to explicitly state the polynomial.</p></blockquote><p>要计算n位CRC（CRC-n-XXX），把比特表示列在一行里，并把n+1位的CRC除数多项式放在下一行的最左侧。<br>在例子里，我们用3位CRC编码14位消息（11010011101100），用的是$x^3 + x + 1$。多项式系数用二进制表示，一个3阶多项式有4个系数$(1x^3 + 0x^2 + 1x + 1)$。在例子里系数分别是1, 0, 1, 1。计算的结果是3位长度的，但是需要4位来明确地表示多项式。</p><blockquote><p>This is first padded with zeros corresponding to the bit length n of the CRC. This is done so that the resulting code word is in systematic form. Here is the first calculation for computing a 3-bit CRC:</p></blockquote><p>所谓的模二除法其实跟<a href="#gf2">上文</a>的模二加法模二乘法一个意思，在长除的时候模二除法不需要借位（退位减），因此可以直接用XOR计算。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">11010011101100 000 &lt;--- input right padded by 3 bits</span><br><span class="line">1011               &lt;--- divisor (4 bits) &#x3D; x³ + x + 1</span><br><span class="line">------------------</span><br><span class="line">01100011101100 000 &lt;--- result</span><br></pre></td></tr></table></figure><blockquote><p>The algorithm acts on the bits directly above the divisor in each step. The result for that iteration is the bitwise XOR of the polynomial divisor with the bits above it. The bits not above the divisor are simply copied directly below for that step. The divisor is then shifted right to align with the highest remaining 1 bit in the input, and the process is repeated until the divisor reaches the right-hand end of the input row. Here is the entire calculation:</p></blockquote><p>这种算法每一步都作用于除数正上方的位。迭代的结果是多项式除数与上面对应位的按位异或。不在除数上方的位直接复制到下方。然后<strong>将除数右移以与输入中剩余的最高 “1” 位对齐</strong>，并重复该过程，直到除数到达输入行的右端。 这是整个计算过程：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">11010011101100 000 &lt;--- input right padded by 3 bits</span><br><span class="line">1011               &lt;--- divisor</span><br><span class="line">01100011101100 000 &lt;--- result (note the first four bits are the XOR with the divisor beneath, the rest of the bits are unchanged)</span><br><span class="line"> 1011              &lt;--- divisor ...</span><br><span class="line">00111011101100 000</span><br><span class="line">  1011</span><br><span class="line">00010111101100 000</span><br><span class="line">   1011</span><br><span class="line">00000001101100 000 &lt;--- note that the divisor moves over to align with the next 1 in the dividend (since quotient for that step was zero)</span><br><span class="line">       1011             (in other words, it doesn&#39;t necessarily move one bit per iteration)</span><br><span class="line">00000000110100 000</span><br><span class="line">        1011</span><br><span class="line">00000000011000 000</span><br><span class="line">         1011</span><br><span class="line">00000000001110 000</span><br><span class="line">          1011</span><br><span class="line">00000000000101 000</span><br><span class="line">           101 1</span><br><span class="line">-----------------</span><br><span class="line">00000000000000 100 &lt;--- remainder (3 bits).  Division algorithm stops here as dividend is equal to zero.</span><br></pre></td></tr></table></figure></p><blockquote><p>Since the leftmost divisor bit zeroed every input bit it touched, when this process ends the only bits in the input row that can be nonzero are the n bits at the right-hand end of the row. These n bits are the remainder of the division step, and will also be the value of the CRC function (unless the chosen CRC specification calls for some postprocessing).<br>The validity of a received message can easily be verified by performing the above calculation again, this time with the check value added instead of zeroes. The remainder should equal zero if there are no detectable errors.</p></blockquote><p>因为最左边的除数位会让任何碰到的输入位变成0，当它处理结束时仅剩的1就是这行最右侧的n位CRC校验值了。这n位就是模二除的余数，也是CRC方程的值（除非选择的CRC需要做后处理），<br>收到的消息的有效性可以通过简单地重复上面的计算过程验证，这次附加的n位不是0，是之前计算的余数。如果没有可检测的错误，这次计算的余数应该等于0，像下面的演示一样。<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">11010011101100 100 &lt;--- input with check value</span><br><span class="line">1011               &lt;--- divisor</span><br><span class="line">01100011101100 100 &lt;--- result</span><br><span class="line"> 1011              &lt;--- divisor ...</span><br><span class="line">00111011101100 100</span><br><span class="line"></span><br><span class="line">......</span><br><span class="line"></span><br><span class="line">00000000001110 100</span><br><span class="line">          1011</span><br><span class="line">00000000000101 100</span><br><span class="line">           101 1</span><br><span class="line">------------------</span><br><span class="line">00000000000000 000 &lt;--- remainder</span><br></pre></td></tr></table></figure></p><h2 id="RAID2-3-4-5-6"><a href="#RAID2-3-4-5-6" class="headerlink" title="RAID2, 3, 4, 5, 6"></a>RAID2, 3, 4, 5, 6</h2><p>经过上面这一大堆啰嗦的前置知识，终于可以搞懂RAID2了——<br><img src="/pics/NAS/RAIDa.png" alt=""><br><img src="/pics/NAS/RAIDb.png" alt=""><br><img src="/pics/NAS/RAIDc.png" alt=""><br><img src="/pics/NAS/RAIDd.png" alt=""><br>RAID3使用上面提到的奇偶校验代替了汉明码，所以只需要一个校验盘做错误校正和检测。<br>RAID4跟RAID3几乎相同，只是RAID4是按数据块而不是像RAID3一样按位访问。<br>RAID5和RAID-Z1类似，相比RAID3的单独一个校验盘，RAID5将校验位分散到了各个盘上。<br><img src="/pics/NAS/raid5-vs-3.png" alt=""><br>RAID6和RAID-Z2类似，相比RAID5只是多了一位校验位，双重奇偶校验。</p><hr><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><p><a href="https://en.wikipedia.org">https://en.wikipedia.org</a><br><a href="https://forums.freebsd.org">https://forums.freebsd.org</a><br><a href="https://docs.freebsd.org/en/books/handbook/">https://docs.freebsd.org/en/books/handbook/</a><br><a href="https://docs.freebsd.org/zh-cn/books/handbook/">https://docs.freebsd.org/zh-cn/books/handbook/</a><br><a href="https://man.freebsd.org/cgi/man.cgi">https://man.freebsd.org/cgi/man.cgi</a><br><a href="https://docs.freebsd.org/en/books/arch-handbook/">https://docs.freebsd.org/en/books/arch-handbook/</a><br><a href="https://book.bsdcn.org">https://book.bsdcn.org</a><br>Channel Codes</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/pics/NAS/FreeBSD-logo.png&quot;&gt;&lt;/img&gt;&lt;br&gt;&lt;a href=&quot;https://www.freebsd.org&quot;&gt;FreeBSD的官方文档和社区&lt;/a&gt;的完善程度是所有操作系统里首屈一指的水平，随便对比一下Linux的发行版&lt;a href=&quot;https://docs.centos.org/en-US/docs/&quot;&gt;CentOS&lt;/a&gt;、&lt;a href=&quot;https://help.ubuntu.com&quot;&gt;Ubuntu&lt;/a&gt;、&lt;a href=&quot;https://archlinux.org&quot;&gt;Arch Linux&lt;/a&gt;，&lt;a href=&quot;https://discussions.apple.com/community/mac/macbook-pro&quot;&gt;macOS&lt;/a&gt;，&lt;a href=&quot;https://answers.microsoft.com/en-us/&quot;&gt;windows&lt;/a&gt;，高下立判。唯二缺点是用户少导致软件生态不如Linux（但有兼容层），且其中文资料比较匮乏，因此在这儿记录一下我使用时遇到的问题。&lt;/p&gt;
&lt;p&gt;文章中也包含了我对RAID（主要是RAID用到的信息编码和群论）和UPnP、SMB等通用技术的理解。&lt;/p&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="RAID" scheme="http://blog.v2beach.cn/tags/RAID/"/>
    
      <category term="ECC" scheme="http://blog.v2beach.cn/tags/ECC/"/>
    
      <category term="NAS" scheme="http://blog.v2beach.cn/tags/NAS/"/>
    
      <category term="FreeBSD" scheme="http://blog.v2beach.cn/tags/FreeBSD/"/>
    
      <category term="Network Protocols" scheme="http://blog.v2beach.cn/tags/Network-Protocols/"/>
    
  </entry>
  
  <entry>
    <title>NAS装机实录</title>
    <link href="http://blog.v2beach.cn/2023/10/19/NAS%E8%A3%85%E6%9C%BA%E5%AE%9E%E5%BD%95/"/>
    <id>http://blog.v2beach.cn/2023/10/19/NAS%E8%A3%85%E6%9C%BA%E5%AE%9E%E5%BD%95/</id>
    <published>2023-10-19T13:56:58.000Z</published>
    <updated>2025-03-18T10:17:01.548Z</updated>
    
    <content type="html"><![CDATA[<table style="word-break: break-word; border-color:white;" border="1">    <tbody>        <tr>            <td>Case</td>            <td>HP Gen7 (Proliant Microserver)</td>            <td>209</td>        </tr>        <tr>            <td>Matherboard</td>            <td>ASRock B460M-ITX/ac (ALC887, WiFi5, BT4.2)</td>            <td>465</td>        </tr>        <tr>            <td>CPU</td>            <td>Intel Pentium G6605 (2cores, 4threads, 4.30GHz, TDP58W, UHD Graphics 630)</td>            <td>410</td>        </tr>        <tr>            <td>CPU cooler</td>            <td>ID Cooling IS-30 (graphene) 热管喷石墨烯</td>            <td>110</td>        </tr>        <tr>            <td>Memory</td>            <td>金百达(KingBank) 银爵8G * 2 3200MHz C14低时序</td>            <td>209</td>        </tr>        <tr>            <td>Power Supply Unit(PSU)</td>            <td>益横Enhance 7025B FLEX 1U 250W 80plus(铜牌)</td>            <td>263</td>        </tr>        <tr>            <td rowspan="3">Storage</td>            <td>Seagate(希捷) ST4000VX015 CMR 4TB * 2</td>            <td>452 + 462</td>        </tr>        <tr>            <td>梵想S500Pro 256GB NVMe SSD M.2/PCIe, 某杂牌(舰灵)M.2 2280 SSD散热</td>            <td>114</td>        </tr>        <tr>            <td>Western Digital(西数)WD5000LPLX黑盘</td>            <td>68</td>        </tr>        <tr>            <td>RAID</td>            <td>LSI MegaRAID 9271-8i 6Gb/s 2208 1GBcache LSI49571-03电池</td>            <td>170</td>        </tr>        <tr>            <td></td>            <td>USB2.0扩展板, 半高PCI挡板</td>            <td>15 + 6.5</td>        </tr>    </tbody></table><a id="more"></a><p>其实组台NAS是早在七夕给自己挑的礼物（当然被送的部分只有电源），组好也已是一个多月之前的事，到现在都接近农历九月初七了。这次可以算是第一次真正自己装机，Gen7的装机之旅又格外坎坷，所以还是有些记录的价值。</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/IMG_1406.JPG" alt="星际蜗牛"></img></td><td><img src="/pics/NAS/IMG_1419.JPG" alt="Gen7下单"></img></td><td><img src="/pics/NAS/IMG_1420.JPG" alt="Gen7背面"></img></td></tr></tbody></table></div><p>其实本来已经下单了星际蜗牛，但鬼使神差地退单选用了便宜100块也好看些的DIY切割版惠普Gen7。</p><p><img src="/pics/NAS/IMG_1563.JPG" alt="刚开始实际上只买了图上这些元件"></img><br>硬件到齐之后组装起来一次点亮，舒服，<br>但是有三个麻烦问题，<br>问题1.CPU温度上升巨快，每次开机进BIOS一分钟不到就从50度上升到99度。<br>问题2.chassis fan机箱风扇转速特别快，而且BIOS改过频率后重启设置会失效。<br>问题3.Gen7四个硬盘架连出来的是根SAS口的数据线，由于我没买机箱店家自带的SAS2SATA转接板，所以只能把4T盘放在光驱位单独连主板，我并没这么做。</p><p>解1.CPU硅脂跟散热器没碰上，涂厚了一层就好了，头一次见这种硅脂涂少CPU完全碰不到散热管的。</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/IMG_1680.JPG" alt="更新BIOS"></img></td><td><img src="/pics/NAS/IMG_1681.JPG" alt="Fan Tuning"></img></td><td><img src="/pics/NAS/IMG_1682.JPG" alt="风扇设置"></img></td></tr></tbody></table></div><p>解2.更新BIOS，B460M-ITXac(L1.51)ROM，之后点Fan Tuning自动设置一下转速就好了。</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/LSI9271-8i.JPG" alt="卡"></img></td><td><img src="/pics/NAS/IMG_1582.JPG" alt="装系统前在BIOS组"></img></td><td><img src="/pics/NAS/IMG_1585.JPG" alt="参数"></img></td></tr></tbody></table></div><p>解3.因为我本来就有组RAID的打算，所以买了卡商推荐的故障率较低、性价比也合适的LSI9271，还另买了一块同型号4T盘。<br>卡到了之后用上面图里的参数在BIOS顺利组好了RAID1。</p><p>然后装FreeBSD，这时我犯了个蠢，我分别下载了13.2Release和Stable几个版本，用win32diskimager刻录到U盘里发现都引导不了系统安装，我当时以为是在硬RAID上装系统的bug，就买了个100块的杂牌SSD，但最后用Get-FileHash看了下SHA256校验码才发现是镜像下载时出了错（大概是因为连外网资源连接不稳定，好几个镜像全出错了）。算是买了个教训，之后每次装系统的第一件事都是对比checksum。</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/IMG_1636.JPG" alt="花里胡哨的散热"></img></td><td><img src="/pics/NAS/IMG_1638.JPG" alt="把SSD贴纸扯掉"></img></td><td><img src="/pics/NAS/IMG_1640.JPG" alt="系统OK"></img></td></tr></tbody></table></div><p>言而总之，从屋子里扯了5m的网线（事实证明用有线是对的，板上的网卡总是检测不到无线信号）后，总算装好了FreeBSD 13.2Release。但把主板装进机箱后，莫名其妙识别不出RAID卡了，<br>这里要先提一下这个机箱的两个毛病，</p><div class="table-container"><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/pics/NAS/IMG_1661.JPG" alt="卖家秀"></img></td><td><img src="/pics/NAS/IMG_1673.JPG" alt="可以看到底板凸出来一些"></img></td><td><img src="/pics/NAS/IMG_1678.JPG" alt="卡扣都怼掉了，用热熔胶能粘上，但不如就这样凑合用，卡扣少点还好装底板，店家的转接板和底板都不给单卖的"></img></td></tr></tbody></table></div><p>毛病1.主板架跟机箱底板固定的螺丝孔一个大一个小，一边铜柱能穿过螺丝孔另一边穿不过，而且铜柱上面的螺丝不像上面卖家秀的蓝螺丝那样是手扭的，导致如果想拆主板，就要先拆挡着主板架的底板；<br>毛病2.底板好拆还好，这机箱的底板拆装很麻烦，刚开始我没注意底板推不到底，后来大力出奇迹，直接把卡扣全怼烂了。</p><p>拜这俩毛病所赐，这台pc维护起来着实费劲。装好后我实在懒得拆出来排查不识卡的问题，拔插多次以各种姿势检查了RAID卡没有接触不良后，我想了关于9271识别不到的几种可能性，<br>（刚开始我并没有意识到BIOS都识别不到RAID卡了，我本以为是组RAID跟装系统的先后或者系统所在硬盘的位置问题导致我在BSD里找不到盘，所以我重装了几遍BSD和OpenMediaVault）<br>可能1.驱动问题；<br>可能2.盘的问题；<br>可能3.怼机箱底板的时候把卡或主板PCIe槽怼坏了，比如掉电容、断线之类的。</p><p>排查1.mpi3mr,mfi,mrsas，把FreeBSD上的LSI驱动都打了一遍之后，确定了FreeBSD自带的<a href="https://man.freebsd.org/cgi/man.cgi?query=mrsas">mrsas(4)</a>本来就支持9271，一天白干。<br>排查2.花不到50块买了块2.5寸500GB小黑盘，结果BIOS能识别到FreeBSD识别不到。最后在BIOS看到盘的参数controller type : AHCI, controller interface : SATA，把SATA模式从Intel RST RAID改到AHCI，FreeBSD里终于找到ahci0（修好后raid是mfi0）<br>camcontrol看到ada0，pciconf -lv看得到sata口了，geom disk list陈列得最清楚。<br><img src="/pics/NAS/IMG_1767.JPG" alt="切模式"/><br>排查3.首先我退了卡换了一张新的9271，其实在新的到货之前退货的老板就跟我说了卡是好的，当时心里就说完蛋了，这下要换PCIe槽了，手头没有显卡的我正打算第二天找个地方借张显卡看能不能识别到x16（现在想想其实用SSD试试也行？）。<br>这时新卡到了，结果新卡也是一样的问题，不装进机箱就识别得到RAID装进机箱就识别不到RAID，但新卡有个好处是金手指更厚，插进去更紧。拆来拆去有一次在机箱里卡也被识别到，我挠着头终于想到，机箱PCI挡板比卡高一截，导致只要主板怼到底就一定会让RAID卡翘起来，把RAID卡上的固定板拆下来才稳定识卡。<br>我去问机箱老板才知道，原来TM是机箱切割魔改之后主板架下沉了，最费周章的环节终于拨云见雾。<br><img src="/pics/NAS/IMG_1771.JPG" alt="完工"></img></p>]]></content>
    
    <summary type="html">
    
      &lt;table style=&quot;word-break: break-word; border-color:white;&quot; border=&quot;1&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Case&lt;/td&gt;
            &lt;td&gt;HP Gen7 (Proliant Microserver)&lt;/td&gt;
            &lt;td&gt;209&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Matherboard&lt;/td&gt;
            &lt;td&gt;ASRock B460M-ITX/ac (ALC887, WiFi5, BT4.2)&lt;/td&gt;
            &lt;td&gt;465&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;CPU&lt;/td&gt;
            &lt;td&gt;Intel Pentium G6605 (2cores, 4threads, 4.30GHz, TDP58W, UHD Graphics 630)&lt;/td&gt;
            &lt;td&gt;410&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;CPU cooler&lt;/td&gt;
            &lt;td&gt;ID Cooling IS-30 (graphene) 热管喷石墨烯&lt;/td&gt;
            &lt;td&gt;110&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Memory&lt;/td&gt;
            &lt;td&gt;金百达(KingBank) 银爵8G * 2 3200MHz C14低时序&lt;/td&gt;
            &lt;td&gt;209&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Power Supply Unit(PSU)&lt;/td&gt;
            &lt;td&gt;益横Enhance 7025B FLEX 1U 250W 80plus(铜牌)&lt;/td&gt;
            &lt;td&gt;263&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td rowspan=&quot;3&quot;&gt;Storage&lt;/td&gt;
            &lt;td&gt;Seagate(希捷) ST4000VX015 CMR 4TB * 2&lt;/td&gt;
            &lt;td&gt;452 + 462&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;梵想S500Pro 256GB NVMe SSD M.2/PCIe, 某杂牌(舰灵)M.2 2280 SSD散热&lt;/td&gt;
            &lt;td&gt;114&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Western Digital(西数)WD5000LPLX黑盘&lt;/td&gt;
            &lt;td&gt;68&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;RAID&lt;/td&gt;
            &lt;td&gt;LSI MegaRAID 9271-8i 6Gb/s 2208 1GBcache LSI49571-03电池&lt;/td&gt;
            &lt;td&gt;170&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;USB2.0扩展板, 半高PCI挡板&lt;/td&gt;
            &lt;td&gt;15 + 6.5&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
    
    </summary>
    
    
      <category term="technology" scheme="http://blog.v2beach.cn/categories/technology/"/>
    
    
      <category term="PC Building" scheme="http://blog.v2beach.cn/tags/PC-Building/"/>
    
      <category term="RAID" scheme="http://blog.v2beach.cn/tags/RAID/"/>
    
      <category term="Network Attached Storage" scheme="http://blog.v2beach.cn/tags/Network-Attached-Storage/"/>
    
  </entry>
  
  <entry>
    <title>Happy Birthday to Myself</title>
    <link href="http://blog.v2beach.cn/2023/05/30/Happy-Birthday-to-Myself/"/>
    <id>http://blog.v2beach.cn/2023/05/30/Happy-Birthday-to-Myself/</id>
    <published>2023-05-29T16:35:56.000Z</published>
    <updated>2023-10-31T11:21:46.203Z</updated>
    
    <content type="html"><![CDATA[<p>祝自己生日快樂。:D</p><a id="more"></a><h3 id="一"><a href="#一" class="headerlink" title="一"></a><strong>一</strong></h3><p>今天是我的24歲陰曆生日。一個小時前一時興起買了個蛋糕，一口氣吃完了。<br><img src="/pics/24th-Birthday/IMG_8243.jpg" width="50%"></img></p><h3 id="二"><a href="#二" class="headerlink" title="二"></a><strong>二</strong></h3><p>剛健身完回到公寓，就想坐下寫點東西。<br>從五一給哥們當完伴郎，回杭州後就幾乎一直在小區健身器材用自重瞎練，實際上效果很差。近期因為瘋玩遊戲，作息又開始接近Chairman Mao（好在很規律——日落而作，日出而息），每天都是凌晨健身，甚至有天凌晨三點多下樓健身。<br>總之這兩三個月的成果是這樣：<br><img src="/pics/24th-Birthday/IMG_8246.jpg" width="50%"></img><br>拍得很爛，圖裡的我看上去有些不對稱，相比一年前稀爛的狀態判若兩豬，但近來過於懈怠，去年花了九個月“逃學”工作、賺錢，今年希望能花九個月健身。目前背闊、肩、胸、大腿、小腿都相當弱，生日之後要重新打起精神去健身房。</p><h3 id="三"><a href="#三" class="headerlink" title="三"></a><strong>三</strong></h3><p>這個月瘋玩了些什麼遊戲呢？<br>不談Darkest Dungeon，British Isles相關的正在前一篇文章裡更新，再除了Only Up!和Strange Horticulture就基本是一众恐怖遊戲了——SOMA、Layers of Fear、Silent Hill 2。<br>恐怖遊戲是悲劇最好的載體，從SOMA的存在主義末路；到LOF裡，鋼琴家女主不幸殘疾、視女主為繆斯的畫家男主崩潰酗酒、對家人語言暴力、餘生直至鬼魂都癡迷於畫出心中完美女人而放棄其他一切的家庭悲劇。<br>最讓我觸動的是寂靜嶺，Mary得絕症放棄希望後不斷遭受精神折磨的James，被父親強暴、被母親嫌棄鄙視的Angela，因為外貌一生被人欺凌導致心理疾病、以虐殺動物和人為樂的Eddie，以及故事裡其他每一個人。<br>這部22年前的遊戲不只是用出色的演出讓我共情（Mary的那封信極盡愛的糾結，既不想讓愛著的人看到醜陋的自己、想不再拖累他讓他過自己的生活，又害怕自己孤獨地死去、想盡可能地跟他多呆每一分鐘，這極深情的自相矛盾和糾結很難不讓人動容），更因為小鎮被濃霧籠罩，怪物遍佈、聲嘶力竭地吼叫，在這裡我時刻要面對死亡威脅，始終身處在黑暗壓抑的環境裡，邁向Lake View Hotel的每一步都艱難漫長，這種代入感，對把玩家的情緒拉向故事的角色是尤為關鍵的。<br>另外，寂靜嶺2的音樂大概是恐怖遊戲的巔峰，Promise我從十年前聽到了現在。</p><h3 id="四"><a href="#四" class="headerlink" title="四"></a><strong>四</strong></h3><p>5月26號是我的陽曆生日，是玩著Only Up!度過的，今天不想打遊戲。<br>去年23歲生日大概是跟同事下館子度過的，回想起來幾段實習經歷，似乎也沒有印象裡那麼難熬了。<br>在vivo的時候忙著刷題、趕LDA進度，中途還花了一個月天天開車跑華為去濱江水項目，那時的熬夜、焦慮是因為想找更好的公司、自視甚高。後來的後來在商湯，忙著大規模跑實驗整理實驗結果、逃班、整日整日地上班打三國殺、通宵玩環世界、逃避一切壓力，那時的熬夜、焦慮是因為自卑於同事都比自己強和對科研不感興趣的痛苦。就像最近跟在濟南的那個姊姊聊的那樣，回憶是真的會美化經歷，看之前的微博，當時覺得幾乎熬不過去的坎，熬過去的現在居然可以輕蔑地嘲笑當時的自己。<br><img src="/pics/24th-Birthday/IMG_6449.jpg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/IMG_D18498D0E508-1.jpeg" width="50%"></img><br>但哪有什麼熬不過去的坎呢？從之前想起他的悲傷就像快要讓人溺死的一波一波連續不斷的海浪，到現在只有每週一兩次在夢裡看到那個幻象，偶爾呼吸困難。去年那段時間也真是要感謝小姑和Miss許，但近來都不再聯絡了。<br>這樣說起來其實最快樂的日子就是在網易吃食堂，在公司裡尋找夢幻西遊、永劫無間、我的世界遊戲組，找網易雲音樂和伏羲，尤其是跟同事打萬智牌，這大概是我最棒的桌遊經歷之一，無論如何，感謝網易，感謝所有在工作中幫助過我的前輩。<br><img src="/pics/24th-Birthday/IMG_5947.jpg" width="50%"></img><br>照片和聲音，一下就能把人拖回某一段時空（代碼也可以？），雖僅僅過去不到半年，上海的經歷已如隔世，難以想像一月中旬離開上海的前夕我還在地鐵被媽搞到心態失控。是啊，三五個月就已經很久了吧，已經能改變很多事情了吧。僅僅距離那一切三五個月的2021年的12月份，我在做什麼。<br>該死的疫情，該死的防控。都是不能忘記的憤怒。</p><h3 id="五"><a href="#五" class="headerlink" title="五"></a><strong>五</strong></h3><p>約好出來吃飯的朋友因為復感染Coronavirus-19沒法一起慶生了，<del>今天的計畫是</del>，今天沒有計畫，不過反正中午燒烤、晚上燒烤，杭州的摩天輪在妳媽蕭山區36km，性價比比較高的獵鷹射擊在下沙20+km，但杭州樂園和獵鷹距離也有20+km… 說實話300～500人民幣在美國夠買上千發了吧，在國內只能射20發手槍+10發氣槍鉛彈。算了今天去西湖轉轉吧，約煜謙明天去打靶，今晚去夜店！健身放假一天，遊戲放假一天，英語放假一天。<br>說起來，昨天跟王阿姨打電話，聽到她說她身邊特別多的出國案例，也提到一個直到申請季沒考出托福，因為國內疫情關考場不得已去東南亞考試的案例，似乎是跟我很像的拖延，包括她女兒多倫多芝大回國實習的履歷，心裡咯噔一下。我是真的該全力考托考G了。<br>But anyway, I should enjoy my life, abstain from being that anxious anymore, everything is gonna be alright, that’s the most important thing I learned last year.</p><h3 id="去年生日"><a href="#去年生日" class="headerlink" title="去年生日"></a><strong>去年生日</strong></h3><p>補個下館子的照片，可惜沒合照，商湯的合照（離職太急太想逃忘記保存了D:）得記得求推薦信的時候跟同事要一下。<br><img src="/pics/24th-Birthday/IMG_5071.jpg" width="50%"></img><br>找到了去年生日2022年5月12日寫的東西。<br>凌晨，<br>——窒息。<br>——<br>“<br>我已经很老了，这也代表我活得比许多我认识的、爱的人久。我失去过好朋友、阿公阿嬷、我妈、熟人，还有许许多多的人。我希望我能说我已经习惯了失去，但我从来不曾习惯，也不想要习惯。<br>每当我爱的人去世的时候，我的心就像被撕裂了一道伤口。但我不想变得不在乎，也不想草草带过。那道伤疤就像是我对他的爱的证明。<br>伤疤越深，对他的爱就越深。这道伤疤就像是我人生的见证；也是即使我曾经受过伤，还是能深深爱一个人的证明；证明我能继续生活，继续去爱。愈合后的皮肤会变得更强壮，只有那些不明白的人，才会觉得这道伤疤是丑陋的。<br>至于悲伤呢，就像是一波波的海浪。当一开始船沉没的时候，你会觉得自己好像快要溺死。周遭漂浮的残骸不断会提醒你，往日的美好已经不在，你唯一能做的只有努力不要溺死。<br>你会找到某片残骸，紧紧抓住不放，也许是某个物质上的东西，也许是某段快乐的回忆、也许是某个和你一起漂浮在汪洋中的人。在这段时间里，你只能努力漂着，努力活着。<br>一开始，海浪感觉会有好几公尺高，无情地将你淹没。海浪会不断袭来，让你连喘气的机会都没有，你只能努力地撑着。<br>过一阵子后，也许是几周、也许是几个月，你会发现海浪虽然还是那么高，但间隔变长了；虽然还是会让你痛得刻骨铭心，但在下一波海浪袭来前的间隔，你能够好好喘口气、好好地处理事情。<br>你不知道什么会触发你的感伤，也许是某张照片、某首歌、某个路口、某杯咖啡的味道，任何事情都有可能让你难过，但在每波悲伤巨浪的间隔，你可以好好生活。<br>一段时间过后（每个人需要的时间不同），你会发现海浪好像没有那么可怕了，虽然还是能够伤害到你，但间隔变得更长了，而且现在你能够大概知道海浪来临的时间，也许是你们的纪念日、对方的生日、圣诞节，或是某个特别的节日，大部分的时候你知道海浪会在何时袭来，而你也能够提前准备自己的心情。<br>当海浪冲刷过后，即使你全身湿透，呛了几口水，还紧紧抓着某片小小的残骸，但你知道自己能撑过去。<br>某人曾告诉我，这一波波的海浪永远不会停止，而且不知为何，最后你也不希望这些海浪消失。<br>你已经学会怎样去面对这些浪潮，下一波海浪依然会向你袭来，而你一样能撑过去。<br>如果你像我一样幸运，活得那么久的话，你身上会有许许多多爱的伤疤，也会经历过一场场的“船难”。<br>”<br>无论发生什么，今年的十二月都会准时到来。<br>痛苦就对了，生活只有一个明确的方向，就是死亡。</p><p>下午兩點，<br>——有她和兄弟的祝福已经完全够了，真开心啊，<br>虽然最近的事都让我觉得窒息，但今天好满足。待会儿跟组里人去吃饭，不需要他们知道我生日，他俩和奶奶知道已经足够了。 ​​​</p><p>想起上一個春夏之交跟殘酷群群友吃飯，那整頓飯一句話都說不利索，去年一整年我的心理狀況都岌岌可危。<br>將來看到那些、這些，再覺得矯情，也別忘了當時經歷了多少心理困境，甚至畢業開題在導師那邊都是拿抑鬱搪塞過去的。<br>希望24歲後的，年過兩輪的我，今後一直都有能力擺脫那種煎熬。</p><h3 id="今年生日！"><a href="#今年生日！" class="headerlink" title="今年生日！"></a><strong>今年生日！</strong></h3><p>琢磨了半天刺激的项目，结果生日当天啥也没干，第二天跟煜谦约了射击俱乐部，儿童节跟新认识的妹妹约了天空之城，跟朱约了唱K和洁宝粤江南。这个周甚至这整个五月到六月初的生活都快乐得离谱。总之，生日当天去了灵隐寺和永福禅寺，在飞来峰景区里——<br><img src="/pics/24th-Birthday/灵隐寺.jpg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/洞.jpeg" width="50%"></img><br><img src="/pics/24th-Birthday/地图.jpeg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/天王.jpeg" width="50%"></img><br><img src="/pics/24th-Birthday/再次出现的天王，看到了阎王.jpeg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/大雄宝殿.jpeg" width="50%"></img><br><img src="/pics/24th-Birthday/五百罗汉.jpeg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/永福寺.jpeg" width="50%"></img><br><img src="/pics/24th-Birthday/绣球.PNG" width="50%">&lt;/img&gt;<br>从玉泉去曙光路酒吧走错路了，居然在青芝坞里发现了第二家朴墅，看来生意太好了南门方圆一公里开两家。<br><img src="/pics/24th-Birthday/外接电梯.jpg" width="50%"></img><br>朴墅上次体验很好，问粥道和旅行者酒吧也同样体验很棒。中午吃的是大概是家里价格两倍的中规中矩的羊排。<br><img src="/pics/24th-Birthday/朴墅.jpg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/饭馆.jpg" width="50%"></img><br><img src="/pics/24th-Birthday/曙光路.jpeg" width="50%">&lt;/img&gt;<br><img src="/pics/24th-Birthday/浙计.jpg" width="50%"></img><br><img src="/pics/24th-Birthday/羊排.jpg" width="50%">&lt;/img&gt;<br>知乎推荐的酒吧真不错，上次去酒吧是我18岁时在丽江的闹吧，这个清吧的酒和歌手都很棒。<br><img src="/pics/24th-Birthday/Blue Hawaii.jpg" width="50%"></img></p><p><video src="/pics/24th-Birthday/三十岁的女人.mp4" type='video/mp4' controls='controls'  width='50%' height='50%' style="max-width: 100%; display: block; margin-left: auto; margin-right: auto;"></video><br>不过生日第二天吃得更好，美蛙自助吃爽了，多谢煜谦的优惠券。<br><img src="/pics/24th-Birthday/爽吃十六七只牛蛙.jpg" width="50%"></img><br>碰到了个90年大一的大叔，他们作为89第二年的新生，经历了跟现在军校一样的三个月军训包括实弹射击。</p><p><video src="/pics/24th-Birthday/Beretta87.mp4" type='video/mp4' controls='controls'  width='75%' height='75%' style="max-width: 100%; display: block; margin-left: auto; margin-right: auto;"></video></p><p><video src="/pics/24th-Birthday/archer.mp4" type='video/mp4' controls='controls'  width='25%' height='25%' style="max-width: 100%; display: block; margin-left: auto; margin-right: auto;"></video><br>两三周前的保龄球，</p><p><video src="/pics/24th-Birthday/bowling.mp4" type='video/mp4' controls='controls'  width='25%' height='25%' style="max-width: 100%; display: block; margin-left: auto; margin-right: auto;"></video><br>再贴一张四个周前的廉价西装伴郎吧。（等我找个大一帅照来对比下）<br><img src="/pics/24th-Birthday/婚礼.jpg" width="50%"></img><br>找帅照找得绷不住了，算了，今天给博客歌单加新歌发现我听的太多都是李志的，加不了，也算了，直接从60来首加到了100多，有些需要vip的都算了，asoul和向晚的歌狂加，跟朱去唱了K之后才想起来这些尘封一年的经典。</p><p>补个图，<br>写完不列颠历史～<br>自己调酒。<br>playlist时好时坏，6月6号凌晨发现又加载不出来了，得自己搞个js的api，别人的都不靠谱，另外把jsdelivr的代码下载到本地比较好。<br>另外梯子7月8号到期，记得续一下。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;祝自己生日快樂。:D&lt;/p&gt;
    
    </summary>
    
    
      <category term="gamer" scheme="http://blog.v2beach.cn/categories/gamer/"/>
    
    
      <category term="Birthday" scheme="http://blog.v2beach.cn/tags/Birthday/"/>
    
      <category term="Silent Hill" scheme="http://blog.v2beach.cn/tags/Silent-Hill/"/>
    
      <category term="Workout" scheme="http://blog.v2beach.cn/tags/Workout/"/>
    
  </entry>
  
  <entry>
    <title>Timeline of the British Isles</title>
    <link href="http://blog.v2beach.cn/2023/05/21/Timeline-of-the-British-Isles/"/>
    <id>http://blog.v2beach.cn/2023/05/21/Timeline-of-the-British-Isles/</id>
    <published>2023-05-21T08:48:29.000Z</published>
    <updated>2023-09-01T03:50:46.630Z</updated>
    
    <content type="html"><![CDATA[<p>and some roman history<br><a id="more"></a></p><p><style><br>    .aCanvas{<br>        display: inline-block;<br>        min-width: 1.25em;<br>        height: 1.25em;<br>        line-height: 1.25;<br>        margin: 1px 0;<br>        text-align: center;<br>        border: 1px solid black;<br>    }</style><br>BC: Before Christ<br>AD: Anno Domini<br>bya: billion years ago<br>BCE: Before Common Era<br>CE: Common Era<br>c. : circa, (often preceding a date) approximately<br>pertaining to<br>phonetic symbols? of dialect?<br>archaeological periods?(<a href="https://en.wikipedia.org/wiki/List_of_archaeological_periods">https://en.wikipedia.org/wiki/List_of_archaeological_periods</a>)<br>technology eras?<br>Most dates of periods are just proximation, I tidied up those dates and found that even wikipedia itself couldn’t align ‘em well. For example, the lasting dates of Western European Chalcolithic is 2200BC(which is the end of Neolithic)~x in <a href="https://en.wikipedia.org/wiki/Neolithic">https://en.wikipedia.org/wiki/Neolithic</a>. But in <a href="https://en.wikipedia.org/wiki/List_of_archaeological_periods">https://en.wikipedia.org/wiki/List_of_archaeological_periods</a>, copper age seems to become a subperiod of new stone age, and the end of Neolithic becomes 2000BC(200 years later). So whether Chalcolithic is actually a subset of broader Neolithic or not, I’ll just take copper age as a transition between new stone age and bronze age, regardless of the date, and most other dates of periods. (<a href="https://en.wikipedia.org/wiki/Iron_Age_Europe">https://en.wikipedia.org/wiki/Iron_Age_Europe</a> the end of Western European Iron Age is 1 AD but <a href="https://en.wikipedia.org/wiki/List_of_archaeological_periods">https://en.wikipedia.org/wiki/List_of_archaeological_periods</a> here is 1 BC…)<br><img src="/pics/TimelineOfBritish/Terminology-British-Isles-United-Kingdom-Ireland-Great.jpg">Terminology-British-Isles-United-Kingdom-Ireland-Great</img><br>The British Isles are a group of islands off the northwestern coast of Europe. The largest of these islands are Britain and Ireland. (Smaller ones include the Isle of Wight.) In the Middle Ages, the name Britain was also applied to a small part of France now known as Brittany. As a result, Great Britain came into use to refer specifically to the island. However, that name had no official significance until 1707, when the island’s rival kingdoms of England and Scotland were united as the Kingdom of Great Britain.<br><a href="https://www.britannica.com/story/whats-the-difference-between-great-britain-and-the-united-kingdom">https://www.britannica.com/story/whats-the-difference-between-great-britain-and-the-united-kingdom</a><br>最后要检查一遍文章里的Britain, Great Britain和British. 时态？</p><p><table style="word-break: break-word;">    <thead>        <tr>            <th width="10%"></th>            <th width="10%"></th>            <th width="10%"></th>            <th width="35%"></th>            <th width="35%"></th>        </tr>    </thead>    <tbody>        <tr>            <td>Date</td>            <td>Eon/States/People</td>            <td>Subperiod</td>            <td>Relevant Events</td>            <td>Note</td>        </tr>        <tr>            <td> – 4.5 bya</td>            <td>Hadean</td>            <td></td>            <td>The Earth is formed out of debris, there's no life, temperatures are extremely hot, with frequent volcanic activity and hellish-looking environments. And the moon is formed.</td>            <td>Hence the eon's name comes from Hades, who is the god of the dead and the king of the underworld, in the ancient Greek religion and myth.</td>        </tr>        <tr>            <td>4 bya – 538.8 mya</td>            <td></td>            <td></td>            <td>The first form of life emerges, continents exist.</td>            <td>The 1st turining point of the evolution of Earth. “evolution” is noun of evolve, not evolvement.</td>        </tr>        <tr>            <td>538.8 mya – present</td>            <td>Phanerozoic</td>            <td></td>            <td>Complex life dominate the ocean in a process known as the Cambrian Explosion(寒武纪生命爆发) and expands to land. Speaking of landmass, supercontinent forms and dissolves.</td>            <td>Phanerozoic means “visible life”, the etymology of which is Greek words “phaneros”(visable) and “zoe”(life) and “-ic”(of or pertaining to).</td>        </tr>        <tr>            <td>66 mya</td>            <td>End of Cretaceous(白垩纪)</td>            <td></td>            <td>Mammals became dominant on Earth after the extinction of the dinosaurs, caused by a asteroid/comet/meteor striking the Earth.</td>            <td>Asteroids are rocky, comets are icy, and meteors are much smaller and are the shooting stars that you see up in the sky. – NASA            It's just a coincidence that "steroid" and "asteroid" look alike. They have the shared suffix "-oid," which means "to be like" (humanoid for instance). However, "aster-" and "ster" are unrelated. "Aster" is Greek for "star," so an asteroid is "star-like." The "ster-" in steroid comes from "sterol," which is a type of alcohol organic molecule, whose name can be traced back to the Ancient Greek "steros," meaning solid. – mdgraller from reddit</td>        </tr>        <tr>            <td rowspan="4">3.3 mya – 2000 BC</td>            <td rowspan="4">Stone Age</td>            <td>x hundred thousand years ago (x00, 000)</td>            <td>Modern humans originated in Africa, evovled from Homo erectus(upright human in Latin)</td>            <td>The 2nd turning point of the Earth, human emerges. Humans don't evolve by natural selection anymore, but by changing the environment to suit their genes.(? Isn't the evolution of brain a kind of natural selection) Homo: 人属, not a gay.</td>        </tr>        <tr>            <td>Paleolithic (pre c. 10000 BCE)            The last Ice Age or Glacial            Mesolithic (c. 10000 – 4500 BCE).</td>            <td>By the Mesolithic, Homo sapiens, or modern humans, were the only hominid species to still survive in the British Isles.            At the end of the old stone age, agriculture revolution's eve, the population(x million) increases dozens of times compared the beginning of the old stone age(a hundred thousand). Stone age ended with the advent of metalworking. Though some simple metalworking of malleable metals, particularly the use of gold and copper for purposes of ornamentation, was known in the Stone Age, it is the melting and smelting of copper that marks the end of the Stone Age.</td>            <td>Prehistory predates recorded history, i.e. "before we had written records". History is the study of the past using written records , it's also the record itself. About 2.5 million years ago, writing was developed. archaeology/ˌɑː(r)kiˈɒlədʒi/,  https://en.wikipedia.org/wiki/Prehistory 定义变了？three-age system全部属于prehistory? Prehistory, also known as pre-literary history,[1] is the period of human history between the first known use of stone tools by hominins c. 3.3 million years ago and the beginning of recorded history with the invention of writing systems. 没变，意思是西欧的prehistory史前期结束于公元前1年？</td>        </tr>        <tr>            <td>Neolithic(c. 4500 – 2500 BC)</td>            <td>Neolithic Revolution is comprised of the introduction of farming, domestication of animals, and change from a hunter-gatherer lifestyle to one of settlement.</td>            <td>The etymology of Neolithic is "neo-"(English prefix, new) and "lithos"(Greek root, stone) and "-ic"(English suffix, of/pertaining to), i.e. New Stone Age. The term 'Neolithic' was coined by Sir John Lubbock in 1865 as a refinement of the three-age system.</td>        </tr>        <tr>            <td>Iberian, Stonehenge</td>            <td>About 2000 years B.C. pre-celtic people had already settled in Great Britain. These were called the Iberians. Probably the came from Spain, which was also called the Iberian peninsula.</td>            <td><img src="/pics/TimelineOfBritish/EuropeanPeninsulas.jpg">European Peninsulas.</img>            <img src="/pics/TimelineOfBritish/Stonehenge2007_07_30.jpg">Stone Henge</img></td>        </tr>        <tr>            <td>Transition</td>            <td>Copper Age</td>            <td></td>            <td>The Copper Age is an archaeological period characterized by regular human manipulation of copper, but prior to the discovery of bronze alloys.</td>            <td>Copper Age, Eneolithic(Latin), Chalcolithic(Greek) are synonyms.</td>        </tr>        <tr>            <td>c. 2500 – 600 BC, Iberian?</td>            <td>Bronze Age</td>            <td></td>            <td>The Bronze Age is the second principal period of the three-age system proposed in 1836 by Christian Jürgensen Thomsen for classifying and studying ancient societies and history. It is also considered the second phase, of three, in the Metal Ages.</td>            <td>While copper is pure metal(Cu from Latin: cuprum 29), brass and bronze are copper alloys (brass is a combination of copper and zinc; bronze is a combination of copper and tin.)<img src="/pics/TimelineOfBritish/210525-bronze-brass-copper 2.jpg"></img></td>        </tr>        <tr>            <td rowspan="2">c. 700 BC – 55 BC or AD 43, Celtic</td>            <td rowspan="2">Iron Age</td>            <td></td>            <td>Celtic conquers the Iberian, Iberian is/are? called ancient Briton, a part of Celtics are called Britons. (the origin of "europe" and "british"? england is named after "anglos" I know that).            The "Iron Age" is defined by archaeological convention. It begins locally when the production of iron or steel has advanced to the point where iron tools and weapons replace their bronze equivalents in common use. The Iron Age is taken to end, also by convention, with the beginning of the historiographical record. This usually does not represent a clear break in the archaeological record. Just some convention, such as Roman conquests of Central and Western Europe, c. 800 AD the beginning of Viking Age as the end of Germanic Iron Age of Scandinavia.</td>            <td>In China, written history started before iron-working arrived, so the term is infrequently used. https://en.wikipedia.org/wiki/List_of_archaeological_periods It's interesting. And, does British prehistory includes bronze and iron? https://en.wikipedia.org/wiki/History_of_Europe, check it out.             Latest prehistoric technology in the Near East – cultures in the Near East achieved the development of writing first, during their Bronze Age.            Latest prehistoric technology in the rest of the Old World: Europe, India, and China reached Iron Age technological development before the introduction of writing there. 中国最早的文字甲骨文出现于商朝（前16世纪到前11世纪）。The oldest word in China, Oracle, emerges in Shang Dynasty(16 c. BC – 11 c. BC)</td>        </tr>        <tr>            <td>Celtic</td>            <td>According to <a href="https://www.britainexpress.com/History/Celtic_Britain.htm#:~:text=Celtic%20Britain%20(The%20Iron%20Age%20%2D%20600%20BC%20%2D%2050%20AD)&text=Who%20were%20they%3F,itself%20throughout%20the%20British%20Isles.">britain express</a>, Celts gradually infiltrated Britain over the course of the centuries between about 500 and 100 B.C. There was probably never an organized Celtic invasion; for one thing, the Celts were so fragmented and given to fighting among themselves that the idea of a concerted invasion would have been ludicrous.            The Celts were a group of peoples loosely tied by similar language, religion, and cultural expression. They were not centrally governed, and quite as happy to fight each other as any non-Celt. They were warriors, living for the glories of battle and plunder. They were also the people who brought iron working to the British Isles.<br>            The basic unit of Celtic life was the clan, a sort of extended family.Clans were bound together very loosely with other clans into tribes, each of which had its own social structure and customs, and poibly its own local gods.</td>            <td>以上是prehistory, prehistory分为Britain和Ireland<img src="/pics/TimelineOfBritish/早期人类的迁移.jpeg">早期人类迁移</img></td>        </tr>        <tr>            <td rowspan="10">c. 55 BCE or AD 43 – 407 CE, Mixture, Complex</td>            <td rowspan="10">Roman, Classical period, Classical antiquity?</td>            <td>first landing, as the beginning of classical antiquity</td>            <td>Roman general and future dictator Gaius Julius Caesar first landed in Britain on August 26th, 55 BC. Then 54 BC he launched another invasion of the British Isles, didn't result in a full Roman occupation either.</td>            <td>About Ancient Rome? https://en.wikipedia.org/wiki/History_of_Europe , I'm more interesting about that during this period.</td>        </tr>        <tr>            <td>AD 43, actual conquering</td>            <td>In AD 43, southern Britain became part of the Roman Empire, and Romans took the Thames estuary Londinium(London) as gathering place. On Nero's accession Roman Britain extended as far north as Lindum (Lincoln). Paulinus, the conqueror of Mauretania (modern-day Algeria and Morocco), then became governor of Britain.</td>            <td></td>        </tr>        <tr>            <td>AD 58</td>            <td>Rome controls much of south-eastern Britain, north and west are remain beyond its grasp.            Roman army is bogged down on the border of modern-day Wales.</td>            <td></td>        </tr>        <tr>            <td>AD 60, Butchery</td>            <td>Roman army arrived in North Wales, arrived on the island of Anglesey, to kill Druids, to destroy Britons' culture. Paulinus penned up the last resistance and massacred the last of the druids and burnt their sacred groves, 'cause druids, religion leaders of Celtic society, can unify people.            After that, Britain is transformed, new roads are built, they still exist. Lincoln, York, Chester, Bath, London, those cities are built at that time. Roman arena comes.</td>            <td><img src="/pics/TimelineOfBritish/Romans_murdering_Druids_and_burning_their_groves_cropped.jpg">Romans murdering Druids and burning their groves cropped. </img>Most written records are from Tacitus.</td>        </tr>        <tr>            <td>AD 122, Hadrian's Wall</td>            <td>Romans have been sealed off from the rest of Roman Britain by a feat of engineering 75 miles long, in places 6 meters high, from coast to coast, with a fort every mile. Hadrian's Wall was built in AD 122 for King of Ancient Rome Hadrian to fight against Celtic from northern Great Britain. In hadrian's words, they wanna "separate Romans from the barbarians" to the north. They are Picts, "Painting People"(tattoos), who lived in Scotland.</td>            <td><img src="/pics/TimelineOfBritish/Roman_Empire_-_Britannia_(125_AD).svg.png">Province of Britain in the Roman Empire in 125 AD</img><img src="/pics/TimelineOfBritish/Hadrians_Wall_from_Housesteads1.jpg"></img></td>        </tr>        <tr>            <td>AD 367, Barbarians from Scotland, Ireland and Germany co-ordinate their attacks and launch raids on Roman Britain.</td>            <td>For 300 years, the people of today's Scotland have remained free. They are a ferocious force. Scotland i.e. North Britain, Scots i.e. Picts, fight Rome till the end.<br>            And there're Goths, the Goths were a nomadic Germanic people who fought against Roman rule in the late 300s and early 400s A.D., helping to bring about the downfall of the Roman Empire, which had controlled much of Europe for centuries.</td>            <td>Visigoth was the name given to the western tribes of Goths, while those in the east were referred to as Ostrogoths. Ancestors of the Visigoths mounted a successful invasion of the Roman Empire, beginning in 376, and ultimately defeated them in the Battle of Adrianople in 378 A.D.</td>        </tr>        <tr>            <td>AD 376, From what is now Germany, the Visigoths storm Rome.</td>            <td>Visigoth was the name given to the western tribes of Goths, while those in the east were referred to as Ostrogoths. Ancestors of the Visigoths mounted a successful invasion of the Roman Empire, beginning in 376, and ultimately defeated them in the Battle of Adrianople in 378 A.D.</td>            <td>The Goths were a nomadic Germanic people who fought against Roman rule in the late 300s and early 400s A.D., helping to bring about the downfall of the Roman Empire, which had controlled much of Europe for centuries.</td>        </tr>        <tr>            <td>Christianity</td>            <td>Constantine converted to Christianity in 312 at the Battle of Milvian Bridge, where he fought against Western Emperor Maxentius to take his place on the throne. Christianity was a mere cult, Constantine's conversion turned Rome into a Christian empire. As the Romans withdraw, the Christianity they introduced to these shores survives.<br>            5th century, Irish pirates come to Britain for slaves. Patric is dragged to Ireland. Patric preaches in the local language to normal Irish Celts! He's called Saint Patric, He succeeds in establishing religion where the Romans failed.</td>            <td>From 406 AD, no new coins are sent to Britain. 410 AD, Rome can no longer police the country. Local warlords set themselves up. It's the death of Romano-Britain.</td>        </tr>        <tr>            <td>retreat/withdraw of military</td>            <td>For the first time in 800 years, Rome falls. Britain, too, is in chaos. Ceramics grind to a halt. From 406 AD, no new coins are sent to Britain. 410 AD, Rome can no longer police the country. Local warlords set themselves up. It's the death of Romano-Britain. 410 Roman army retreated. As the Roman occupation of Britain was coming to an end, Constantine III withdrew the remains of the army in reaction to the Germanic invasion of Gaul with the Crossing of the Rhine in December 406.</td>            <td><img src="/pics/TimelineOfBritish/End.of.Roman.rule.in.Britain.jpg"></img></td>        </tr>        <tr>            <td>c. AD 407 – AD 597, Local Warlords? total wikipedia</td>            <td>Sub-Roman Britain is the period of late antiquity in Great Britain between the end of Roman rule and the Anglo-Saxon settlement. The term was originally used to describe archaeological remains found in 5th- and 6th-century AD sites that hinted at the decay of locally made wares from a previous higher standard under the Roman Empire. It is now used to describe the period that commenced with the recall of Roman troops to Gaul by Constantine III in 407 and to have concluded with the Battle of Deorham in 577.<br>            The period of sub-Roman Britain traditionally covers the history of the area which subsequently became England from the end of Roman imperial rule, traditionally dated to be in 410, to the arrival of Saint Augustine in 597. The date taken for the end of this period is arbitrary in that the sub-Roman culture continued in northern England, and longer in western England like Wales.</td>            <td>The term "post-Roman Britain" is also used for the period, mainly in non-archaeological contexts. Popular (and some academic) works use a range of more dramatic names for the period: the Dark Ages, the Brythonic Age(Briton人的), the Age of Tyrants(暴君), or the Age of Arthur(亚瑟王).<img src="/pics/TimelineOfBritish/Britain.circa.540.jpg"></img></td>        </tr>        <tr>            <td rowspan="11">c. AD 597 – AD 1485</td>            <td rowspan="11">Medieval, mixture</td>            <td>Anglo-Saxon England (597–1066)</td>            <td>Anglo-Saxon England or Early Medieval England, existing from the 5th to the 11th centuries from the end of Roman Britain until the Norman conquest in 1066, consisted of various Anglo-Saxon kingdoms until 927, when it was united as the Kingdom of England by King Æthelstan (r. 927–939). It became part of the short-lived North Sea Empire of Cnut the Great, a personal union between England, Denmark and Norway in the 11th century.<br>            The Anglo-Saxons migrated to England from mainland northwestern Europe after the Roman Empire abandoned Britain at the beginning of the fifth century. Anglo-Saxon history thus begins during the period of sub-Roman Britain following the end of Roman control, and traces the establishment of Anglo-Saxon kingdoms in the 5th and 6th centuries (conventionally identified as seven main kingdoms: <b>Northumbria, Mercia, East Anglia, Essex, Kent, Sussex, and Wessex</b>); their Christianisation during the 7th century; the threat of Viking invasions and Danish settlers; the gradual unification of England under the Wessex hegemony during the 9th and 10th centuries; and ending with the Norman conquest of England by William the Conqueror in 1066. Anglo-Saxon identity survived beyond the Norman conquest, came to be known as Englishry under Norman rule, and through social and cultural integration with Celts, Danes and Normans became the modern English people.<br>            Before Æthelstan,             </td>            <td>Throughout this article Anglo-Saxon is used for Saxon, Angle, Jute or Frisian unless it is specific to a point being made; "Anglo-Saxon" is used when the culture is meant as opposed to any ethnicity. ??? What does this annotation mean? Need understand the "Historical Context", the point is to organize the whole history in my mind but not this stupid article.<img src="/pics/TimelineOfBritish/Invasions_of_the_Roman_Empire_1.png"></img><img src="/pics/TimelineOfBritish/1280px-Britain_peoples_circa_600.svg.png">after settlement</img><img src="/pics/TimelineOfBritish/heptarchy_by_c00lfr0g_d6dcfp2.png"></img></td>        </tr>        <tr>            <td>Heptarchy(597–829) and Unification</td>            <td>By 600, a new order was developing, of kingdoms and sub-Kingdoms. The medieval historian Henry of Huntingdon conceived the idea of the Heptarchy, which consisted of the seven principal Anglo-Saxon kingdoms (Heptarchy literal translation from the Greek: hept – seven; archy – rule).<br>            By convention, the Heptarchy period lasted from the end of Roman rule in Britain in the 5th century, <b>until most of the Anglo-Saxon kingdoms came under the overlordship of Egbert of Wessex in 829</b>. This approximately 400-year period of European history is often referred to as the Early Middle Ages or, more controversially, as the Dark Ages. Although heptarchy suggests the existence of seven kingdoms, the term is just used as a label of convenience and does not imply the existence of a clear-cut or stable group of seven kingdoms. The number of kingdoms and sub-kingdoms fluctuated rapidly during this period as competing kings contended for supremacy.            Egbert of Wessex briefly unified, but split again after his death.<br>            From 874 to 879 the western half of Mercia was ruled by Ceowulf II, who was succeeded by Æthelred as Lord of the Mercians. Alfred the Great of Wessex styled himself King of the Anglo-Saxons from about 886. In 886/887 Æthelred married Alfred's daughter Æthelflæd. On Alfred's death in 899, his son Edward the Elder succeeded him.<br>            When Æthelred died in 911, Æthelflæd succeeded him as "Lady of the Mercians", and in the 910s she and her brother Edward recovered East Anglia and eastern Mercia from Viking rule. Edward and his successors expanded Alfred's network of fortified burhs, a key element of their strategy, enabling them to go on the offensive. When Edward died in 924 he ruled all England south of the Humber. His son, Æthelstan, annexed Northumbria in 927 and thus became the first king of all England.<br>            Cnut (/kəˈnjuːt/; Old English: Cnut cyning; c. 990 – 12 November 1035, born in Denmark!), also known as Cnut the Great and Canute, was King of England from 1016, King of Denmark from 1018, and King of Norway from 1028 until his death in 1035. The three kingdoms united under Cnut's rule are referred to together as the North Sea Empire.</td>            <td><img src="/pics/TimelineOfBritish/1280px-England_878.svg.png"></img> <img src="/pics/TimelineOfBritish/1920px-Cnut_lands.svg.png"></img></td>        </tr>        <tr>            <td>Norman Conquest (1066)</td>            <td>The Norman Conquest (or the Conquest) was the 11th-century invasion and occupation of England by an army made up of thousands of Norman, Breton, Flemish, and French troops, all led by the Duke of Normandy, later styled William the Conqueror.</img></td>            <td>            Christmas Day, 1066, great monument of Anglo-Saxon. Foreigner new king, French, to be crowned in Westminster Abbey. Walliam. Battle of Hastings. Normans. French? the Harrying of the North. 北方掠夺战？rebellions. and brutal slaughter. Normans build a lot of strongholds. estates owned by 4000 Saxon lords are divided between 200 Norman barons.<img src="/pics/TimelineOfBritish/Norman-conquest-1066.svg.png"><img src="/pics/TimelineOfBritish/English_monarchy_family_tree.png"></img></td>        </tr>        <tr>            <td>AD 1085</td>            <td>1085, commissions the most ambitious census in Europe. Cement Norman ownership. allow Walliam to tax his population as never before. Domesday Book, the book of judgement末日审判书。200million words, in Latin, in foreign language. It records 8 million acres of cultivated land.            Of the 6 richest people in the whole of British history, 4 are Norman lords. Walliam, the Bastard Duke of Normandy, has become the conqueror, most powerful monarch.            And they built a lot of huge cathedrals.</td>            <td></td>        </tr>        <tr>            <td>AD 1096, crusades</td>            <td>1096,Payne Peverel is preparing for war, finally he become a baron.Normans join Crusades, enrich themselves with booty, carve out for themselves fiefdoms and estates.1099, the first attack on the holy city Jerusalem. Thousands of Muslim and Jewish inhabitants are slaughtered. It continues for 200 years. Funded by harsh levies, peasants provide for the war, high tax makes the people's livelihood difficult.</td>            <td>The Crusades were a series of religious wars initiated, supported, and sometimes directed by the Christian Latin Church in the medieval period.<img src="/pics/TimelineOfBritish/Crusade.jpg"></img></td>        </tr>        <tr>            <td>AD 1315, mistreated British under Normans' reign</td>            <td>1315, farming catastrophe, harvests are hard, one of the worst famines.1317, poachers poaching in Norman royal forests. all common men must be practiced bowman. Robin Hood, heroes. a metaphor for fairness.</td>            <td><img src="/pics/TimelineOfBritish/Robin_Hood_Memorial.jpg">Statue of Robin Hood near Nottingham Castle by James Woodford, 1951</img></td>        </tr>        <tr>            <td>AD 1348, The Black Death</td>            <td>1348, black rats from the continent. a merchant ship from France with deadly cargo.The fleas they carry are agents of death for a disease.<br>            The Black Death. Blood clots can block capillaries in fingertips and toes. The gangrenous flesh gives the plague the name Black Death.<br>            Bubonic plague becomes pneumonic plague then.腺鼠疫变成肺鼠疫。<br>            Village after village lose up to half their population, the disease spreads, across the sea to Ireland.<br>            Like a nuclear holocaust.</td>            <td><img src="/pics/TimelineOfBritish/Acral_gangrene_due_to_plague.jpg">Acral gangrene due to plague</img> <img src="/pics/TimelineOfBritish/The_Triumph_of_Death_by_Pieter_Bruegel_the_Elder.jpg">Pieter Bruegel's The Triumph of Death reflects the social upheaval and terror that followed the plague, which devastated medieval Europe.</img>  <img src="/pics/TimelineOfBritish/1346-1353_spread_of_the_Black_Death_in_Europe_map.svg.png"></img> </td>        </tr>        <tr>            <td>After plague, The Peasants' Revolt, End of Serfdom</td>            <td>Half the population may be dead, but the survivors of the plague will gain new opportunities.Vacant land is taken up by the plague's survivors.<br>            1381, life is changing. With a shortage of labor, the wages of people have risen substantially. (substance = essence) It's the beginning of a more modern relationship between employer and employed man or woman.<br>            But lords are threatened. They try to fix wages at pre-plague levels. Even to pass laws telling peasants what to eat and wear, and a harsh new poll tax. <br>            Fobbing in Essex, people refuse to pay.<br>            1383, 5.30, Brentwood, poll tax. peasant revolution, Brentwood's rebellion. It sparks the first great popular uprising in British history. 陈胜吴广？The peasants' revolt. Their aim, an end to serfdom.农奴制终结<br>            With in a fortnight (fourteen nights OLD ENGLISH), the Essex rebels are joined by others from Kent to march on London. Then from London, revolt spreads to towns and villages across the country. It sets in progress the first steps to a modern woking life.<br>            Within two generations, serfdom has all but collapsed. But within a month, the rebel leaders are rounded up and executed. 30 are from Essex (5 from Fobbing). <br>            <b>The Peasants' Revolt</b> sets England apart. A land of personal freedoms.</td>            <td></td>        </tr>        <tr>            <td>AD 1415</td>            <td>Agincourt, France, 1415, 10.25, England is at war. The latest offensive in a century of conflict with France. 英法百年战争中英国的最后一次进攻。（两次百年战争Second Hundred Years' War，1689年-1815年）The English face twice their number on the battlefield.            The french force is 15,000 strong. Over half are knights and noblemen. More than three-quarters of the English army are archers. The English longbow.            No enemy soldier will face a comparable rate of fire for over 400 years??? Just archer???            14th century, finally writes the common man into the history books. not just dictated by royalty, nobility and clergy.                England wins the latest offensive.            England is transformed! But this same free spirit will trigger a century of religious mayhem and civil war.</td>            <td></td>        </tr>        <tr>            <td>Scotland in the Middle Ages (400–900–1286–1513)</td>            <td>Scotland was divided into a series of kingdoms in the early Middle Ages, i.e. between the end of Roman authority in southern and central Britain from around 400 CE and the rise of the kingdom of Alba in 900 CE. Of these, the four most important to emerge were the Picts, the Gaels of Dál Riata, the Britons of Alt Clut, and the Anglian kingdom of Bernicia. After the arrival of the Vikings in the late 8th century, Scandinavian rulers and colonies were established on the islands and along parts of the coasts. In the 9th century, the House of Alpin combined the lands of the Scots and Picts to form a single kingdom which constituted the basis of the Kingdom of Scotland.</td>            <td>The Picts were a group of peoples who lived in Britain north of the Forth–Clyde isthmus in the Pre-Viking, Early Middle Ages. https://en.wikipedia.org/wiki/Picts</td>        </tr>        <tr>            <td>Wales in the Middle Ages (411-1542)</td>            <td></td>            <td><img src="/pics/TimelineOfBritish/Britain_in_AD500.jpg"></td>        </tr>        <tr>            <td rowspan="16">c. AD 1485 – c. 1800 CE</td>            <td rowspan="16">Post-medieval, Early Morden?</td>            <td></td>            <td>Some scholars date the beginning of Early Modern Britain to the end of the Wars of the Roses and the crowning of Henry Tudor in 1485 after his victory at the battle of Bosworth Field. Henry VII's largely peaceful reign ended decades of civil war and brought the peace and stability to England needed for art and commerce to thrive. A major war on English soil would not occur again until the English Civil War of the 17th century. The Wars of the Roses claimed an estimated 105,000 dead.</td>            <td>Norman诺曼-Plantagenet金雀花(勇敢的心)-Tudor(都铎)-stuart斯图亚特(议会分为支持国王的托利党和反对国王的辉格党)-Hanover汉诺威-Saxe-coburg and Gotha, Windsor温莎王朝<img src="/pics/TimelineOfBritish/English_monarchy_family_tree.png"></img></td>        </tr>        <tr>            <td>Tudor period (1485–1603)</td>            <td>In England and Wales, the Tudor period occurred between 1485 and 1603, and included the Elizabethan period during the reign of Elizabeth I (1558–1603). The Tudor period coincides with the dynasty of the House of Tudor in England, which began with the reign of Henry VII. Historian John Guy (1988) argued that "England was economically healthier, more expansive, and more optimistic under the Tudors" than at any time since the Roman occupation.<br>            1539, Glastonbury Abbey. Henry the VIII's relationship with the church has broken down, he pushes England closer to war with Catholic Europe.            In the past 300 years, England has changed beyond recognition. But a third of all land remains owned by the Church.            Now Henry has broken with the Pope, he wants it, it made England a target for all catholic countries in Europe.            1539, 11.15, abbot was hanged.            In four years Henry takes control of all 8000 monasteries in England, adding into the royal coffers by billions in today's money.            What will transform Henry's reign, is an invention. printing press. Paper from China and the Middle East, made from water and shredded rags. Before the printing press came to England, a single scribe could take over a year to copy out a book by hand. But now, merchant Caxton's printing press can produce hundreds of books in weeks.            Compare it to the Internet.            Suddenly, they have access to printed news and information! There's a case for saying it was the most important invention. This is the first time??? the English language comes to the printed page.            maybe more important than the Internet, it allowed to disseminate ideas. Revolutionary!            1539, one book explodes onto the scene! The first official English language Bible. with no Christ or Papal, but Henry, push a step closer to war with Catholic Europe.            Cast Cannons!            The improvement operation is paid for by a wealthy parson, William Levett. On a Sunday he preaches from Henry's Bible, during the week he bankrolls weapons research.XD            This was an innovation that would set the mark in gunnery for the next three centuries.            It turns England into one of the biggest arms manufacturers in Europe and helps secure borders.</td>            <td><img src="/pics/TimelineOfBritish/1920px-Tudor_Rose.svg.png"></img></td>        </tr>        <tr>            <td>Tudor period (1485–1603), Tudor conquest of Ireland</td>            <td></td>            <td></td>        </tr>        <tr>            <td>Tudor period (1485–1603), English Renaissance</td>            <td>The English Renaissance can be hard to date precisely, but for most scholars, it begins with the rise of the Tudor Dynasty (1485–1603) and reaches its cultural summit during the 45-year reign of the final Tudor monarch, the charismatic Elizabeth I (1558–1603).</td>            <td></td>        </tr>        <tr>            <td>Tudor period (1485–1603), Elizabethan era (1558–1603)</td>            <td>When Elizabeth 1 is on the throne, the European powers are focusing their attention on getting wealth from beyond the ocean.South America is being colonized and looted by Spain, England's great rival.Silver and gold have transformed Catholic Spain into a superpower. It's difficult to conceive the scale of the Spanish empire, covering much of South America, with vast quantities of gold travelling to Spain and enriching the country. So, the Spanish Empire is vastly bigger and more important than little England.England looks enviously on.1579, 3.1, the Pacific Ocean, England wanna have its share of the Spaniards' new wealth.Golden Hind vs Cacafuego, Golden Hind(the first English ship to(to??) sail into the Pacific) captain privateer Francis Drake, secretly backed by Queen Elizabeth 1.They have 15 cannons! It's one of the biggest heists in history. And they're building one of the greatest fighting machines the world ever seen, the Royal Navy.Jealous of Spain's riches, England's new power at sea (Royal Navy) begins a radical transformation of the nation, and kick-starts its overseas adventures.</td>            <td><img src="/pics/TimelineOfBritish/English_Ships_and_the_Spanish_Armada,_August_1588_RMG_BHC0262.jpg">The Spanish Armada fighting the English navy at the Battle of Gravelines in 1588.</img></td>        </tr>        <tr>            <td>First British Empire(1583-1783)</td>            <td>English overseas possessions (1583–1707)Americas, Africa and the slave tradeRivalry with other European empiresScottish attempt to expand overseas"First" British Empire (1707–1783)Loss of the Thirteen American Colonies1769, the South Pacific, Lieutenant James Cook, sail off the edge of the map, to go in search of the fabled southern continent.After six months at sea, he found it. For 50,000 years(全球通史p19, from Asia), they are the first visitors.He claims the land for Britain, calling it New South Wales.In this age of imperial expansion, Australia, it will add three million square miles to the British Empire(760万平方公里).Cook writes, "The great quantity of plants collected in this place occasion my giving it the name Botany Bay." Sydney.(???没看懂grammar)King George III makes him director of the new Kew Gardens. Amongst the 30,000 specimens he brings home are 1,400 plants never seen before in Britain.His triumph inspires imitators, people commission plant hunters to find and bring plants back.</td>            <td></td>        </tr>        <tr>            <td>Jacobean era (1567–1625)</td>            <td>King James VI of Scotland has inherited the English throne from his mother's cousin, Elizabeth. James is the first monarch to rule both Scotland and England.He's both James VI and I. His son is Charles I(1600-11-19~1649-1-30), the King of England, Scotland and Ireland(1625-3-27~1649).Printing, Protestantism, and the spread of education have turned the English into one of the most literate people in the world.Over 70 percent of Londoners can read.Opposition to the King and his government begins to spread across the British Isles. Personal freedoms were hard fought for.</td>            <td>The Jacobean era succeeds the Elizabethan era and precedes the Caroline era. <img src="/pics/TimelineOfBritish/1024px-James_I_of_England_by_Daniel_Mytens.jpg">King James VI and I by Mijtens (1621)</img></td>        </tr>        <tr>            <td>Union of the Crowns (1603)</td>            <td>King James devised new coats of arms and a uniform coinage.<img src="/pics/TimelineOfBritish/Royal_Coat_of_Arms_of_the_United_Kingdom_(HM_Government)_(2022).svg.png"></img></td>            <td><img src="/pics/TimelineOfBritish/Screen Shot 2023-08-30 at 12.39.57 AM.png"></img></td>        </tr>        <tr>            <td>Caroline era (1625–1642)</td>            <td>King James VI of Scotland has inherited the English throne from his mother's cousin, Elizabeth. James is the first monarch to rule both Scotland and England.He's both James VI and I. His son is Charles I(1600-11-19~1649-1-30), the King of England, Scotland and Ireland(1625-3-27~1649).Printing, Protestantism, and the spread of education have turned the English into one of the most literate people in the world.Over 70 percent of Londoners can read.Opposition to the King and his government begins to spread across the British Isles. Personal freedoms were hard fought for.1640, newly elected member for Cambridge enters Parliament, Oliver Cromwell. He, puritan farmer, leads Britain through the most turbulent era in its history. In Cromwell's time, the King is free to call and dismiss MPs when he sees fit. But this sitting(can it be 'his sitting'?) will change it. It's now Parliament's opportunity to ensure that it can never happen again.the Palace of Westminster has been the heart of lawmaking and taxation for centuries.Cromwell demanded the release of John Lilburne.1642, 1.4, Charles intends to arrest the rebel MPs in(why not at) the House of Commons. But they have been tipped off and escaped. He's caught in a situation that his own Parliament are defying him. That's the break between the monarch and the Parliament, and there begins the English Civil War.Then Charles abandons London, London becomes a Parliament stronghold.It became a civil war within communities, where neighbors might find themselves on opposite sides of the conflict.This is a conflict which is fundamental to the future of Britain, it can only now be resolved by war. It's a hinge on which the British history turns.1500 men are dead, both sides claim victory, stalemate.</td>            <td>The Caroline era is the period in English and Scottish history named for the 24-year reign of Charles I (1625–1649). The term is derived from Carolus, the Latin for Charles. The Caroline era followed the Jacobean era, the reign of Charles's father James I & VI (1603–1625), overlapped with the English Civil War (1642–1651), and was followed by the English Interregnum until The Restoration in 1660. It should not be confused with the Carolean era which refers to the reign of Charles I's son King Charles II.<img src="/pics/TimelineOfBritish/1920px-The_execution_of_King_Charles_I_from_NPG.jpg"></img><img src="/pics/TimelineOfBritish/Charles_Landseer_-_The_Eve_of_the_Battle_of_Edge_Hill,_1642_cropped.jpg">King Charles I, in blue sash, holding a council of war before the Battle of Edgehill in 1642; the cavalry commander Prince Rupert is seated left.</img></td>        </tr>        <tr>            <td>English Civil War (1642–1651)</td>            <td>Parliament created the New Model Army. Then they beats the King, the King surrenders and is put to death for treason.1649.1, Oliver Cromwell signs the King's death warrant. This is the moment when it is decided the Britain will not become an absolute monarchy, Britain will become a constitutional monarchy.英国最后一个试图君主专制的King，之后就是君主立宪的时代。</td>            <td><img src="/pics/TimelineOfBritish/Battle_of_Naseby.jpg">The Battle of Naseby, 14 June 1645; Parliamentarian victory marked the decisive turning point in the English Civil War.</img></td>        </tr>        <tr>            <td>English Interregnum (1651–1660)空位期, associate with Korea??</td>            <td>The Interregnum was the period between the execution of Charles I on 30 January 1649 and the arrival of his son Charles II in London on 29 May 1660 which marked the start of the Restoration. During the Interregnum, England was under various forms of republican government (see Commonwealth of England; this article describes other facets of the Interregnum).</td>            <td></td>        </tr>        <tr>            <td>Restoration (1660)</td>            <td>London, 1666, 9.2, after midnight. A fire, the worst inferno in London until the Blitz(德国闪电战). It's a disaster that will destroy London. All on fire, and flaming at once.On the third day, whole streets are blown up to create firebreaks(炸毁，防火障). The fire swallows St. Paul.Four-fifths of the old city gone, 80,000 homeless. A city 1,600 years in the making lost in four days. But a new capital rises, the city reinvents itself, in stone this time.First new London, then Newcastle, Edinburgh and Dublin. At London's heart, one of Britain's greatest buildings, the new St. Paul's Cathedral. It takes 36 years to build. It remains the tallest structure in London until 1962.</td>            <td>Architecture establishes a nation, and makes a people love their native country.That's what buildings are for, they're statements, "Look how important we are, look what we can achieve."An inspiration. The St. Paul is a mind-blowing building.</td>        </tr>        <tr>            <td>Glorious Revolution (1688)</td>            <td>James II replaced as king by his daughter Mary II and her husband William III???</td>            <td></td>        </tr>         <tr>            <td>Kingdom of Great Britain (1707–1800)</td>            <td>Commerce is booming in England's oldest colony, Ireland.Dublin is the second largest city of the Empire.The penal laws(刑法) would not allow any Irishman to own property or land. It was an extraordinary repressive regime.(reign???)There are also laws to stop Catholics from holding public office(担任公职), joining the army and even owning a fine horse or a gun. 压迫爱尔兰人和天主教徒。Britain decides to reform then.Britain was in the middle of a century of war with France. So, the ban on Irish Catholics is lifted and thousands quickly sign up.Maybe it's because of the oppression an repression压迫和约束 that Irish retained its individuality.1768, James Arkwright, spinning machine, Spinning Jenny. It's the greatest change in working life since humans invented farming?The livelihood of Britain's 200,000 weavers is under threat. 1812, 4.24, handloom weavers risk their lives to destroy a cotton mill. The rioters wreck all of the mill's 170 looms.Sparked a revolution.The government sends a small army, 12,000 troops, to crush the uprising. The girls are released, due, it was said, to their tender sex??????They cannot resist the power of the state or advance of the machine.In terms of industrial potential, coal has the same significance in the early 19c as oil does today.By 1830, Britain is producing four-fifths of all the coal sold anywhere in the world. From coal, you can make steam.Steam Power! 3,500 miles of rail track have been laid in just 25 years.A third are Scots, A third are Irish, dig the canals, build the roads and the railways, Navvies.1845.12, the tunnel is complete.Cook strips of raw rubber in molten sulphur for hours. 天然橡胶条在炽热的硫磺中煮几小时Rubber is a testimony to the way in which Britain is an imperial economic power.Between 1830 and 1850, more patents are filed for new inventions than in the previous two centuries."the workshop of the world"The Hooligan Nature, The Savage Nature.</td>            <td><img src="/pics/TimelineOfBritish/Flag_of_Great_Britain_(1707–1800).svg.png"></img>Flag of Great Britain (1707–1801)</td>        </tr>         <tr>            <td>Second British Empire (1783–1815)</td>            <td>40% of the jobs in Bristol and Liverpool depend upon the slave trade.1772.2, Somerset was arrested.The legal wrangling stretches out over six months.1772.5, Somerset was discharged.It's the beginning of the end of slavery.The slave trade is finally banned in 1807, by 1838, nearly all slaves in British Empire are free. 到底该现在时态还是过去时态？This Empire now stretches over a million square miles. Britain has the greatest merchant fleet on earth and controls valuable trade routes all over the world.But Britain's supremacy of the seas is under threat. A new power is sweeping Europe. France prepares to invade Britain, led by their emperor, Napoleon Bonaparte.拿破仑波拿巴1805,10.21, Cape Trafalgar, off the coast of Spain（特拉法加角，西班牙海岸附近）, a British warship sails into the mother of all sea battles. France and Spain have joined forces to smash(联合打击？) Britain's control of the seas.海上霸权Britain's feet is outnumbered(again?), but has Victory.Captain of Victory is Nelson. And Nelson's plan was working.Napoleon's fleet is is decimated, Nelson is triumphed.In the end, the British Navy destroys 18 ships, over half the enemy's fleet. Nelson loses NONE.He won Trafalgar.The Royal Navy commands the ocean for the next 100 years, it's a vitally important war.Control of world trade generates huge profits that finances the world's first industrial revolution.</td>            <td><img src="/pics/TimelineOfBritish/BritishEmpire1815.png">The British Empire at the end of the Napoleonic Wars in 1815</img><img src="/pics/TimelineOfBritish/Flag_of_the_United_Kingdom.svg.png">Flag of the United Kingdom (1801–present)</img><img src="/pics/TimelineOfBritish/The_British_Empire_5.png">The British Empire at its largest?</img></td>        </tr>         <tr>            <td>Georgian era (1714–1837)</td>            <td>A quarter of all Londoners now earn their living from the docks.Scotsman William Paterson founds the Bank of England(1694). It issues its first banknotes.(the earliest of the British?)随着七世纪更大规模的商业贸易兴起，开始出现更方便携带的纸币，11世纪中国四川出现了最早的纸币交子，13世纪马可波罗向欧洲介绍他旅途中见到的纸币，欧洲最早现代纸币出现于1661年，斯德哥尔摩银行。Modern insurance is invented to underwrite dangerous voyages. And a new way to make money, stocks and shares. 债券和股票Britain has a brand new institution, the coffee house. It's "the" place to meet, get the news of the day and make deal.Investing!投资（股票）The hottest shares of all are the South Sea Company. South Sea stock selling out!!South Sea stock selling out!!!It has a monopoly on trade with South America and promises over 100% returns. A get-rich-quick scheme anyone would take.Joining thousands of investors is Sir Issac Newton.在数千投资者中有一位是艾萨克牛顿爵士！He leads a scientific revolution that's sweeping Europe. Reason and science replace superstition and fear.理智和科学取代了迷信和恐惧。He buys 1,000 pounds of South Sea Stock.The entire country is caught up in South Sea mania. The company is now valued at 300 million, more than all the gold in Britain. The first financial bubble in British history.By late summer, the price falters , and tumbles. People panic and rush to sell. By the end of the year, the stock is worthless.These speculations are a game of musical chairs.投机买卖就像抢凳子游戏。You're fine if you're sitting down when the music stops, but if you're still standing, then it's going to mean ruin.Bank collapse.Newton is caught out, "I can calculate the motion for heavenly bodies, but not for the madness of man."Why does South Sea take on more than half the government's debt, paying it off with South Sea shares, and seals this deal with bribes.为了不让政府管他？The government launches a huge clean-up operation. Bank of England takes over the national debt, the Chancellor is sent to the Tower. The South Sea Bubble of 1720.Within a year, trade once again revives Britain's economy. It's the birth of boom-or-bust of capitalism. But beneath the new world is a terrible industry. Britain's most valuable cargo--African Slaves.During 18c, Britain becomes the world's largest slave trader, transporting over 2.5 million abducted Africans to work on our sugar, tobacco and cotton plantations.Slavery transforms Liverpool from a sleepy port of 5000 to a prosperous city of 78,000.Somerset, a young boy, is sold 15 pounds. 1:1000(then:now)The other biggest industry is sex trade. In London, the sex industry is worth almost as much as London's entire overseas trade.Catalogues are published to rate and review prostitutes. 草，异世界风俗娘评鉴指南。Contemporary reports show, one in five women in London is a sex worker.Less than a hundred years ago, under Oliver Cromwell, even adultery(通奸) was punishable by death.Gonson, moral crusader, v.s. Moll, procuress.She won?</td>            <td>The Georgian era was a period in British history from 1714 to c. 1830–1837, named after the Hanoverian kings George I, George II, George III and George IV. The subperiod that is the Regency era is defined by the regency of George IV as Prince of Wales during the illness of his father George III.</td>        </tr>        <tr>            <td rowspan="7">1801-- present</td>            <td rowspan="7">Industrial revolutions/Modern, the United Kingdom</td>            <td></td>            <td>United Kingdom of Great Britain and Ireland (1801–1922), United Kingdom of Great Britain and Northern Ireland (1922–), History of the Republic of Ireland (1922–) Irish Civil War</td>            <td>https://en.wikipedia.org/wiki/History_of_the_British_Isles#Periods</td>        </tr>        <tr>            <td>United Kingdom of Great Britain and Ireland (1801–1922), Britain's Imperial Century (1815–1914)</td>            <td>The empire on which the sun never sets.</td>            <td><img src="/pics/TimelineOfBritish/The_British_Empire_5.png">The British Empire at its largest?</img></td>        </tr>        <tr>            <td>United Kingdom of Great Britain and Ireland (1801–1922), Victorian era (1837–1901)</td>            <td>Regency (1811–1820)Big Ben projected Victorian power, is a symbol of the reach of Britain, the monument standing at the centre of this global empire.In the 1850s, Imperial Russia's push south threatens Britain's trade interests. To assert its authority, the British government sends an army to the Crimean Peninsula.It will be the first war of the industrial age.(草，鸦片战争在人家眼里只是仗势欺人"We're a bit of trouble"，根本不是战争)1854, 9, British troops have one greatest advantage over their enemies, the rifled musket. Lead bullet "mini ball".The thin red line of British infantrymen is one product of this world's first industrialized war.不就是一字排开吗ironclad warships, railways, exploding mines. and the telegraph. 电报！On the battlefield. The pioneering work of Mary Seacole and Florence Nightingale.南丁格尔 Raise the standards of hygiene in hospitals back home???Nursing is part of the great Victorian cult of improvement.During the 1870s, The rapidly expanding British population is running short of meat. New Zealand has 11 million sheep. Coal-powered freezer.Refrigeration makes more people eat well.The empire on which the sun never sets.1851, London. In the reign of Queen Victoria, The Great Exhibition万国工业博览会，或者世界博览会（第一次世博会）. 6 million visitors come to see 100,000 exhibits. Housed in Crystal Palace, constructed wit 290,000 panes of glasses, it's 6 times bigger than Saint Paul's Cathedral. 上海我跟妈去的那个艺术宫就是2010中国世博馆？On 1851, 5.1, at The Great Exhibition, George Jennings, a Hampshire plumber, opens Britain's first-ever public flushing loos. From this moment, the expression "To spend a penny" is said to enter the English language.花一个便士=上公厕Flushing Loos!George Jennings!1851 is also a tipping point in world history, Britain is the first place on Earth where more people live not in the countryside, but in the cities. 人口暴增, London 1 million to 6.5 million. six-and-a-half million.cholera, slums. 霍乱英国人也是用井水。well pumpCholera is the most feared disease in Britain since the Black Death 500 years ago.It was very unclear to people how diseases spread.John Snow discovered that cholera was spread by water.Four years later, Britain begins the most radical(radical again) cleanup in its history.London's sewers下水道 masterminded by engineer Joseph Bazalgette, use 380 million bricks and take 16 years to complete, stretching 1,300 miles under the city.Britain never suffers a cholera epidemic(plague?) again.Reservoirs(蓄水池) are built all over the country for clean drinking water (pure, uncontaminated未经污染的). In 1881, the first stone damn(石坝) is built in Powys to supply Liverpool. 19c, One-third of London's inhabitants live without sufficient food or shelter, including Samuel Holmes.Huge criminal underclass. knee-jerk, police.Australia was a Britain's prison. By the end of the 19th century, over 160,000 British convicts are deported to Australia. 15,000 are children under 16.Oliver Twist雾都孤儿。Charles Dickens1885.7, the first piece of serious investigative Victorian journalism, Stead's article, "The Maiden Tribute to Modern Babylon", 5 pounds can buy a child prostitute.Society was beginning to think that it needed to have a collective responsibility for those of its members who were not able to take care of themselves.集体责任Victoria virtue. By 1900, there are 22,000 charities in Britain慈善机构。Leisure Revolution. Workers get Saturday afternoon off and their first bank holidays.Over 500 football clubs open.In 1878, at Bramall Lane football pitch in Sheffield. Alternative to gas. John Tasker, erected arc lamps (illuminators used in lighthouses), set up a steam engine to power a dynamo generating electricity for the four arc lamps to illuminate the pitch.He goes on to build Sheffield's first power station and electricity network. Sheffield F.C. 1857. Sheffield United F.C. 1889Dim gas lamps are replaced with bright electric lighting, in 1879, Newcastle sees the first street illuminated. Cities start to transform from dark, grimy, sooty places to light, clean, safer environments.By the end of Victoria's reign, Britain is transformed, the late 19c.In the next century, Britain Empire will wane, two world wars will shatter the country.A new nation will be forged.Edwardian period (1901–1910)</td>            <td>1918-7-1, a factory in Chilwell, Nottinghamshire.诺丁汉郡的村子Britain is at war with German, a battle of industry.One of the worst disasters on Britain's home front.The efforts of the Great War derailed British industry.Britain was broke, was in debt to America, and had its industry in disarray.Before the Great War, only one in ten families owned their own home. By 1939, cheap mortgages(房贷) mean that this number has tripled.Fleming discovers penicillin青霉素(a mould).Antibiotics, Penicillin revolutionizes the way infection and disease are treated across the world.From the Britons taking on the Romans, to the Anglo-Saxons taking on the Normans. There is a collective determination.</td>        </tr>        <tr>            <td>United Kingdom of Great Britain and Ireland (1801–1922), Britain in World War I (1914–1918)</td>            <td>The United Kingdom was a leading Allied Power during the First World War of 1914–1918. They fought against the Central Powers, mainly Germany. The armed forces were greatly expanded and reorganised—the war marked the founding of the Royal Air Force. The highly controversial introduction, in January 1916, of conscription for the first time in British history followed the raising of one of the largest all-volunteer armies in history, known as Kitchener's Army, of more than 2,000,000 men.  The outbreak of war was a socially unifying event. Enthusiasm was widespread in 1914, and was similar to that across Europe.</td>            <td><img src="/pics/TimelineOfBritish/Daddy,_what_did_You_do_in_the_Great_War?.jpg"></img>First World War recruiting poster, playing on the guilt of those who did not volunteer<img src="/pics/TimelineOfBritish/Stanford’s_Geographical_Establishment,_What_Germany_Wants_1917_Cornell_CUL_PJM_1199_01.jpg">A world map showing territory that "Germany Wants" by Edward Stanford. 1917. Close reading of the quoted material shows that the map is misleading: it implies that Germany plans to annex all the territory in red, but this is only the case for a small fraction of it.</img></td>        </tr>        <tr>            <td>United Kingdom of Great Britain and Ireland (1801–1922), Irish War of Independence (1919-1921)</td>            <td>The Irish War of Independence (Irish: Cogadh na Saoirse) or Anglo-Irish War was a guerrilla war fought in Ireland from 1919 to 1921 between the Irish Republican Army (IRA, the army of the Irish Republic) and British forces: the British Army, along with the quasi-military Royal Irish Constabulary (RIC) and its paramilitary forces the Auxiliaries and Ulster Special Constabulary (USC). It was part of the Irish revolutionary period.</td>            <td><img src="/pics/TimelineOfBritish/1918_United_Kingdom_general_election_(Ireland)_map_-_winning_party_vote_share_by_constituency.svg.png">Result of the 1918 UK general election in Ireland</img></td>        </tr>        <tr>            <td>United Kingdom of Great Britain and Northern Ireland (1922–)</td>            <td>Britain in World War II (1939–1945)<br>            History of the United Kingdom (1945–present)<br><br>            The military history of the United Kingdom in World War II covers the Second World War against the Axis powers, starting on 3 September 1939 with the declaration of war by the United Kingdom and France, followed by the UK's Dominions, Crown colonies and protectorates on Nazi Germany in response to the invasion of Poland by Germany. There was little, however, the Anglo-French alliance could do or did do to help Poland. The Phoney War culminated in April 1940 with the German invasion of Denmark and Norway. Winston Churchill became prime minister and head of a coalition government in May 1940. The defeat of other European countries followed – Belgium, the Netherlands, Luxembourg and France – alongside the British Expeditionary Force which led to the Dunkirk evacuation in June 1940.<br><br>            From the Britons taking on the Romans, to the Anglo-Saxons taking on the Normans. There is a collective determination.<br>            1940, 12.29, Hitler air raid.<br>            The cathedral had survived, as if by a miracle, while all around was consumed.<br>            1953, 6.2, A royal coronation of the 27-year-old Queen Elizabeth II. <br>            Live on black-and-white TV for the first time.<br>            The whole nation sees the coronation. ("whole nations" is plural, We don't use "the" with plural nouns. It can be used with singular or plural.)</td>            <td><img src="/pics/TimelineOfBritish/Back_Them_Up!_Art.IWMPST16866.jpg"></img></td>        </tr>        <tr>            <td>History of the Republic of Ireland (1922–), Irish civil war</td>            <td>The Irish Civil War (Irish: Cogadh Cathartha na hÉireann; 28 June 1922 – 24 May 1923) was a conflict that followed the Irish War of Independence and accompanied the establishment of the Irish Free State, an entity independent from the United Kingdom but within the British Empire.<br>            The civil war was waged between the Provisional Government of Ireland and the anti-Treaty Irish Republican Army (1922–1969) (IRA) over the Anglo-Irish Treaty. The Provisional Government (which became the Free State in December 1922) supported the terms of the treaty, while the anti-Treaty opposition saw it as a betrayal of the Irish Republic that had been proclaimed during the Easter Rising of 1916. Many of the combatants had fought together against the British in the Irish Republican Army (1919–1922) during the War of Independence, and had divided after that conflict ended and the treaty negotiations began.</td>            <td></td>        </tr>    </tbody></table><br>格式不行，考虑放到wiki里吧，用gitbook。<br>Just organize the main history events, main cities, main persons.<br><a href="https://en.wikipedia.org/wiki/History_of_the_British_Isles#Timeline_history_of_the_British_Isles">https://en.wikipedia.org/wiki/History_of_the_British_Isles#Timeline_history_of_the_British_Isles</a>, the timeline is so helpful, I’m so confused about all those names and their fucking relations.</p><p>ROME!<br>Ancient Rome, 753BC ~ 476/480AD?<br><img src="/pics/TimelineOfBritish/Roman_Republic_Empire_map.gif">&lt;/img&gt;<br><span class="aCanvas" style="background-color:#a64; color:black;"> </span>Roman Repbulic<br><span class="aCanvas" style="background-color:#a6a; color:black;"> </span>Romean Empire<br><span class="aCanvas" style="background-color:#48a; color:black;"> </span>Western Roman Empire<br><span class="aCanvas" style="background-color:#bc4; color:black;"> </span>Eastern Roman Empire<br>Holy Rome Empire, 800/962 ~ 1806<br><img src="/pics/TimelineOfBritish/Banner_of_the_Holy_Roman_Emperor_with_haloes_(1400-1806).svg.png"></img><br>Imperial Banner(c. 1430–1806)<br><img src="/pics/TimelineOfBritish/800px-Coat_of_Arms_of_the_Holy_Roman_Emperor_(c.1433-c.1450).svg.png">&lt;/img&gt;<br>Coat of arms(15th century design)<br><img src="/pics/TimelineOfBritish/Quaternion_Eagle_by_Jost_de_Negker.jpg"></img><br>The double-headed eagle with coats of arms of individual states, the symbol of the Holy Roman Empire (painting from 1510)<br><img src="/pics/TimelineOfBritish/Golden_Bull_of_1356.png">&lt;/img&gt;<br>The Holy Roman Empire when the Golden Bull of 1356 was signed<br><img src="/pics/TimelineOfBritish/Map_of_the_Holy_Roman_Empire,_1789_en.png"></img><br>The Empire on the eve of the French Revolution, 1789</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;and some roman history&lt;br&gt;
    
    </summary>
    
    
      <category term="gamer" scheme="http://blog.v2beach.cn/categories/gamer/"/>
    
    
      <category term="History" scheme="http://blog.v2beach.cn/tags/History/"/>
    
      <category term="Europa Universalis" scheme="http://blog.v2beach.cn/tags/Europa-Universalis/"/>
    
  </entry>
  
  <entry>
    <title>We All Die Alone</title>
    <link href="http://blog.v2beach.cn/2022/12/31/We-All-Die-Alone/"/>
    <id>http://blog.v2beach.cn/2022/12/31/We-All-Die-Alone/</id>
    <published>2022-12-31T14:19:27.838Z</published>
    <updated>2023-10-29T07:04:47.077Z</updated>
    
    <content type="html"><![CDATA[<p>“不做俗人，哪儿会知道这般乐趣？家破人亡，平了头每日荷锄，却自有真人生在里面，识到了，即是幸，即是福。衣食是本，自有人类，就是每日在忙这个。可囿在其中，终于还不太像人。”<br>倦意渐渐上来，就拥了幕布，沉沉睡去。<br>——«棋王»(阿城)</p><a id="more"></a><h1 id="年终"><a href="#年终" class="headerlink" title="年终"></a>年终</h1><h2 id="这两年来"><a href="#这两年来" class="headerlink" title="这两年来"></a>这两年来</h2><p>的一些片段</p><h2 id="一些决定"><a href="#一些决定" class="headerlink" title="一些决定"></a>一些决定</h2><p>辞职</p><h1 id="围棋"><a href="#围棋" class="headerlink" title="围棋"></a>围棋</h1><h2 id="glossary"><a href="#glossary" class="headerlink" title="glossary"></a>glossary</h2><h3 id="一些概念和手筋"><a href="#一些概念和手筋" class="headerlink" title="一些概念和手筋"></a>一些概念和手筋</h3><blockquote><p>手筋：日文汉字，局部对弈里的妙着zhāo。<br>诘棋：棋局残局，围棋的死活题就是诘棋。</p></blockquote><p>这俩术语跟将棋通用的。<br><strong>气</strong>；<strong>连/粘</strong>（zhān用子连接起两部分）；<strong>断</strong>（用子隔开两部分）；<strong>打吃</strong>（下子导致某部分子只剩一气）；<strong>提</strong>；<br><strong>长</strong>（cháng，紧连着自己的子延长一子）；<strong>立</strong>（顺着自己的棋向下、向边线“长”一子）；<br><strong>拆</strong>（跟原有的子同一直线，隔一路下一子叫拆一/一间拆，间隔五路为限都叫拆）；<strong>跳/关</strong>（跟“拆一”一样，跟原棋子同一直线上隔一路下一子）；<strong>飞</strong>（不在同一直线，在原有的子“日”字对角下一子叫“小飞”，“目”字对角下一子叫“大飞”）；<strong>尖/小尖</strong>（在原有棋子的“口”字，紧挨的对角下一子，没有大尖）；<strong>尖顶</strong>（紧挨着对方的子在己方子的“尖”位置下子）；<br><strong>扳</strong>（两路棋贴着的时候，斜向在对方直线上拦一个子）；<strong>曲/拐</strong>（也是两方子贴着，连着己方的子在对方直线上下子以包围）；<br><strong>夹</strong>（两子夹对方一个子在中间，不一定同一直线）；<strong>虎</strong>（三子构成“品”字状，中间位置叫“虎口”）；<strong>刺</strong>（在对方的断点或还其他还没连接的位置下一子）；<br><strong>双打</strong>；<strong>双</strong>（就是双关，两个单关连在一块，连着的两个拆一）；<strong>冲</strong>（紧挨着自己的子，在对方“关”的中间空隙下子）；<strong>肩冲</strong>（在对方子高一路的“尖”位下子）；<strong>挖</strong>（也是在对方“关”的中间空隙下子，但不紧挨着自己的子）；<br><strong>压</strong>（紧挨着对方的子在高位下子，把对方的子压在低位）；<strong>镇</strong>（在对方的子高路“拆一”的位置，也就是中腹方向的“拆一”位置下子）；<br><strong>靠/搭</strong>（有己方子“策应”的情况下贴着对方下子，妈的什么算策应）；<strong>碰</strong>（紧挨着对方棋子下子，一般是孤立的子）；<br><strong>挡</strong>（对方侵入自己的地域或防止对方棋子冲出包围时，在对方的直线上下子拦住）；<strong>拦/拦逼</strong>（跟挡的区别在于不紧挨）；<strong>退</strong>（紧挨己方跟对方接触的子，向己方子方向“长”一子）；<strong>并</strong>（跟己方的子并排，但不跟对方接触下子）；<strong>掖</strong>（一种“断”吧，什么鬼东西）；<br><strong>渡</strong>（三路一下在对方棋子低路下一子连接自己两部分被隔离的棋）；<strong>点</strong>（在对方缺陷或要害下子）；<strong>打入</strong>（在对方控制区下子）；<strong>打N还一</strong>；<strong>跨</strong>（己方“小飞”切断对方“小飞”）；<br><strong>扑</strong>（在对方“虎口”下子，迫使对方吃子自紧一气，对杀中用来紧气和破眼）；<strong>爬</strong>（在对方的压迫下，沿着棋盘二线或一线直线行棋）；<strong>封</strong>（阻止对方往中腹发展）；<br><strong>顶</strong>（一种碰？）；<strong>贴</strong>（似乎是紧随对方贴着走直线下子）。</p><h3 id="术语小结"><a href="#术语小结" class="headerlink" title="术语小结"></a><strong>术语小结</strong></h3><p>总之这些所谓的手筋的组合，就跟学语法和词源学构成语感一样，构成只可意会的“棋感”。<br><a href="https://tromp.github.io/go/legal.html">https://tromp.github.io/go/legal.html</a><br>19x19围棋棋局的所有可能性是$3^{361}$，梦溪笔谈里就算过，每个位置有三种可能（黑、白、空），而考虑落子顺序的话，可能性总量会上升到$361!$，假设某个模型能在规定时间内遍历完所有棋局，那对弈实际上可以简化成「计算每一步能导致胜利结局最多的落子位置」这种古典概率吧。<br>“手割”（日文汉字，解剖，一次增加一黑一白，或是减少一黑一白，而达到其他已知的定式或两分型「围棋对局的一个阶段后双方得失大致相等」，以分析棋的好坏）分析每一着棋效（围棋是个围地的游戏，目的不在吃子，棋效代表每一着能扩张的地盘），就是根据已知的棋局经验总结胜率更高的下法。<br>现在我们学的棋理似乎大都是日本人的工作，起源在我们却没有成体系的理论工作，我觉得也正因此围棋一直以来都是个曲高和寡的游戏。</p><h3 id="一些杀棋-进攻-技巧"><a href="#一些杀棋-进攻-技巧" class="headerlink" title="一些杀棋(进攻)技巧"></a>一些杀棋(进攻)技巧</h3><ul><li>征子/扭羊头——征子方向上如果有对方子就容易征子不利，会产生一堆断点。</li><li>扑——逼对方在吃自己子的位置下子丢气，反扑来杀更多子。看了几个棋谱一般“扑”是起到“断”的作用，无论是紧气杀子还是破眼。</li><li>接不归——一般跟扑对应，棋子被打吃、来不及连出去续气的状态。</li><li>夹——看棋谱一般是●○●/○●○这种紧连着的夹。</li><li>枷——下面这种对气超过3口的棋子，很难直接枷吃掉。这种应该算跳枷，除此之外还有尖枷、飞枷、宽枷——《围棋入门》枷。<br><img src="/pics/围棋/枷.png" alt="枷"></li><li>门吃——对方的子被包围，出口的气两边各一个子像门一样拦住打吃。</li><li>抱吃——像是抱起来的打吃…</li><li>断——</li></ul><p>这两天疯狂熬夜，盲目地焦虑快把自己逼疯了。快绷断了。</p><p><a href="https://x-wei.github.io/GT-summery.html">https://x-wei.github.io/GT-summery.html</a><br>巴黎圣日耳曼现在阵容好牛啊。。FIFA21里还是拜仁慕尼黑最强的。</p><h1 id="痛苦上瘾？"><a href="#痛苦上瘾？" class="headerlink" title="痛苦上瘾？"></a>痛苦上瘾？</h1><h2 id=""><a href="#" class="headerlink" title=" "></a> </h2>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;“不做俗人，哪儿会知道这般乐趣？家破人亡，平了头每日荷锄，却自有真人生在里面，识到了，即是幸，即是福。衣食是本，自有人类，就是每日在忙这个。可囿在其中，终于还不太像人。”&lt;br&gt;倦意渐渐上来，就拥了幕布，沉沉睡去。&lt;br&gt;——«棋王»(阿城)&lt;/p&gt;
    
    </summary>
    
    
      <category term="gamer" scheme="http://blog.v2beach.cn/categories/gamer/"/>
    
    
      <category term="nonsense" scheme="http://blog.v2beach.cn/tags/nonsense/"/>
    
      <category term="围棋" scheme="http://blog.v2beach.cn/tags/%E5%9B%B4%E6%A3%8B/"/>
    
  </entry>
  
</feed>
