<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://zsq259.github.io</id>
    <title>hastin&apos;s blog</title>
    <updated>2026-04-05T13:30:30.182Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://zsq259.github.io"/>
    <link rel="self" href="https://zsq259.github.io/atom.xml"/>
    <subtitle>逸一时，误一世</subtitle>
    <logo>https://zsq259.github.io/images/avatar.png</logo>
    <icon>https://zsq259.github.io/favicon.ico</icon>
    <rights>All rights reserved 2026, hastin&apos;s blog</rights>
    <entry>
        <title type="html"><![CDATA[毕业小结]]></title>
        <id>https://zsq259.github.io/post/bi-ye-xiao-jie/</id>
        <link href="https://zsq259.github.io/post/bi-ye-xiao-jie/">
        </link>
        <updated>2026-04-05T13:27:14.000Z</updated>
        <summary type="html"><![CDATA[<p>路漫漫，且行且问</p>
]]></summary>
        <content type="html"><![CDATA[<p>路漫漫，且行且问</p>
<!--more -->
<h2 id="1">1</h2>
<p>虽然之前每次写学期小结和自己的年度小结的时候，经常会写着写着就发现写了大几千字了。但是这一次写毕业小结还真不知道如何下笔。可能是四年的跨度已经足够大，而那些早期的记忆已经有些模糊。但到了这个时刻，也确实值得花一个下午，坐下来好好回顾一下自己的大学生涯，给自己做一个总结了。</p>
<p>刚接触到ACM班是在高考出分后不久。那时候分数还算高，但也没有到稳上清北的地步，但也还是怀抱着一些志向，想在大学继续冲刺。而在高中培养的对于知识本身感兴趣的理念，也让我自己在接触ACM班之前，本来就希望走上科研的道路。此时和俞老师的面试的机会则是更进一步地推动了这样的想法，也让我获得了进入ACM班，享受这样的资源的机会。</p>
<p>而在进入大学之后，首先接触的还是上课学知识，但我对于知识和研究的态度却发生过很多波折。从学习上来讲，我感受到明显的问题就是节奏变快了很多。前面高中的很多时间都会一遍遍地反复去练许多题，给足了训练的轮次和数据量，但是大学之后，首先是节奏变快了，其次还需要自己安排时间。不行高中那样强制排好的时间确实是带来了不小的挑战。刚开学时的疫情更是雪上加霜，网课让课程的参与感更是下降了一大截。而这一切叠加起来之后，我也确实没能很好的应对过去。大一上的成绩还算勉强，大一下就惨遭 qualify 了。在这个过程中，我的意志也在一点点的钝化。具体来说，知识的难度和学习的节奏都变大以后，我在逐渐失去对知识本身的兴趣，变得只是为了完成任务。而后面面对一些观念的冲击则让我开始有了一点开摆的想法，仿佛不做的那么好，也是可以接受的。再更进一步地，我的观念有点异化成了，不那么感兴趣的事，就当完成任务就行了。所以对学的知识没那么感兴趣了，加上这样的观念，就让我的学习变得没那么主动。最后也是导致了在 qualify 的时候和俞老师争了一下。但也多亏有这样的流程，给了我再次反思的机会。在 qualify 之后，我回去也反思了很久。我意识到之前我是认为不感兴趣的就直接放弃了，但是其实应该还是要考虑如何让自己去变得感兴趣起来，去培养这方面的意识。所以后面的学习我还是在往这方面的意识去靠。但可惜的是，我似乎一直都只有一点淡淡的情感，却一直难以把握那种强烈的追求的感觉，也正因这样，导致后续的我成绩虽然有所进步，但学习的状态还是没有达到最理想的样子。对此还是感觉比较遗憾。</p>
<p>另外从另一方面来讲，我一直有一个心态，就是比我厉害的人太多了。但我这是一种比俞老师说的“归零”的心态程度更极端的想法。我感觉比较健康的理解应该是不要因为一开始做不好而过于沮丧，而是接受自己一开始的不完美，但还是坚持努力成长。但在我的潜意识里，这变成了我一直就是比他们差，这是“正常”的。但这就导致我会丧失很多追求的动力。我应该也经常看到所谓的“成长型思维与固定型思维”的说法，大概也是前面的说的那两种感觉。在以前，我确实觉得成长型思维很好，非常合理，理所应当的就应该是这样。但在很后来，某个时刻我才猛然发现，其实我自己一直都是固定性思维。我内心深处依旧害怕着自己会表现得“不够好”。但我却只是表面上表现的像“能接受自己的失败”，以为这样就够了，来否定后者所描述的“害怕犯错”的特征，但我的内心依旧是消极的。我可能会承认努力是可以提升能力的，但我没有努力的意愿。我不是不相信努力能改变结果，但我却没有这样的追求。总的来说，我只是做到了“接受自己的失败”的层次，却没有做到“再努力做到自己的最好”的地步。</p>
<p>当然，还有一个很直接的想法就是，如果我能有一个愿景，有一个未来的理想，那我可以朝着这个理想去努力。但我实践起来却发现还是难以做到。我一直觉得这个理想离我来说非常的遥远，以至于遥远到难以为我提供直接的动力。当然，我理应讲其拆解成更容易实现的目标。但这样却又出现了一个矛盾：这个拆出来的目标，要么依旧遥远，要么离我很近时，目标本身离原来的理想又过于遥远了，以至于又难以和理想建立联系。所以到最后，我在通过愿景和理想提供动力的尝试的结果还是不太理想。</p>
<p>在学习这方面还有一个遗憾，就是有一些看上去的水课和通识课，就没有太认真听，比如学术写作，近现代史纲要什么的。但其实包括后来与同学们聊天，都还是觉得老师们挺有特色，也比较有水平。可惜当时只想着在课上多做一点作业。但其实是很亏的，做作业也不差上课那点时间，而且课上做了之后，不上课的时候我也不够自律。加上前面所说的完成任务的态度，就会导致在没课的时候，如果又没有任务，反而无所事事消磨时间去了。而那些课的内容，虽然看上去相对没那么“重要”，但应该还是挺有意思的，而且可能还能塑造我对事物的一些看法。其实相当于调节我大脑的参数了。可惜我当初刻板印象还是太重了，不愿意放下心来老实坐那好好听讲。</p>
<p>这样看下来的话，我这学习的态度和状态还是非常糟糕，而且各个层面相互叠加，像前面提到的兴趣的问题，追求的心态的问题，以及课上课后的行动，导致回过头看就感觉很遗憾，什么都没有做好。而且讲实话，其实我算是一直在这个问题上翻车，就是努力不起来，或者说就是太懒了。比如说这个兴趣的问题，我可能也尝试了许多方法，比如说先让自己投入起来，写一些笔记，保持在课上的专注等等。但是稍微假设哪次没这么做，然后就开摆了。包括读论文，一开始也在尝试做点笔记，深入理解一下，结果做了一篇之后就没下文了。总结一下就是太容易滑坡了，建立一个好的习惯还是太难了，稍微没有维护就直接翻车。而再结合我那“没有想做到很好的目标”的心态，导致我确实还是一直是得过且过的心态。我也看到身边有的同学真的非常拼命的想把事情做好，其实我也挺羡慕他们这样的心态，虽然他们自己也说这样非常痛苦，但终究是我没有体会过的，而且确实对自己的能力和结果能有很大的帮助。</p>
<p>但话是这么说，这学习的过程里面让我觉得最有意思的还是写一些编程大作业的感觉，确实是给我一种在用代码搭建自己的作品的感觉。这种一点点去设计，实现和调试的过程确实令我沉浸其中。也因此，我暑假 PPCA 和编译器的出勤时间应该是很高的，应该几乎是最早到的之一。另外就是有一些大作业也是班上最早通过的，还是能带来一些成就感。可惜到了后面 LLM 发展起来之后，就很少再自己一点点手写代码了，它的能力和效率确实是越来越高，但也由此带来了一些弊端。比如说到了大二后期和大三那会，相关课程的代码就基本上是用 LLM 写的。虽然说是也会去了解一下大概是怎样的逻辑，怎样的结构，但还是没能做到像自己一行行敲出来这样吃透。比如说像正向传播反向传播这种函数，我就从来没有自己写过。导致的结果可能就是我对于，比如说 AI 的一些知识只停留在了概念上，但印象就又更浅一些了。当然，根据我的印象，哪怕是实践了课堂上的作业之类的代码，其与前沿的研究实践也还是有较大的差距。我觉得这样还是有些可惜的，毕竟我们前两年确实通过各种大作业，编译器等练习了不少编程能力，但我并没有在自己的研究中很好的结合起来。而且现在 agent 出来，这样的问题应该会更加加重。agent 现在确实非常方便，叫他干什么事基本上都能很好的做好。但对人来说，“为什么”做就更加重要了。现在属于是必须要知道为什么做。才能更好地指导 agent 来做，那未来对科学家的能力的需求可能就会更加不一样。但在培养相关能力的过程中，agent 却又可能</p>
<p>而到了大二下，开始实验室轮转以后，我们的重心从上课开始往科研方向转变。我也才开始懵懂地了解这个领域。在最后选择导师，以及研究方向时，也曾非常犹豫。但可能和大部分犹豫的前途之类的不一样，我只是在考虑自己现在，和未来，到底想要做什么。可惜我的成绩没能让我去成当时最想去的导师那里，于是也走上了现在搞 NLP 的道路。具体的经历其实也在前面实验室实践的小结，和学期小结等各个地方都写了。总的来讲，我觉得其实延续了前面的习惯，就也还是在做，但是比起那些很拼的同学来说还是少了很多。虽然我的成果看起来也不少了，但确实没有中的，每篇的质量也还是没有达到我理想的水平。</p>
<p>说到这里，也可以总结一下我做的工作。我觉得 LLM 时代入科研，特别是 LLM 本身这行还是不太容易的。要深刻的了解最底层的东西也没那么容易，加上我之前学习的基础也不是特别好，所以刚开始的时候只是在做一些比较外围的事情，比如建立 benchmark，简单训练模型等等。后面了解的一些相关工作，其实也是围绕比较外围的东西来设计，比如说人为设计一些 agent 的工作流，设计一些基于 LLM 的模块这些。而我最开始做 benchmark 是觉得做这个上手比较容易，但后面发现其实很难讲好故事，讲清楚做这个的动机。但我最近的工作的出发点就确实还是有所不同，是我想做某个问题，发现确实没有相关的评测基准，因此才想先自己做一个。但难度也还是在于，我自己确实想做这个问题，但也要说明白这个问题是真的值得做。</p>
<p>但我觉得比较遗憾的还是，我没有去做到很底层的，比如说去深刻的了解一个网络结构，或者训练的方法，具体是如何设计的，为什么要这样设计。以及能不能自己提出一些新的方法，去实践一下。这里就又回应到之前的学习习惯和写代码的问题了。由于我研究的问题又确实不涉及那些，我平时可能主要就想着去推研究进度了，也没有去了解那些方面的欲望和习惯，现在想来也还是很遗憾。以至于我和别人聊天的时候，对方对这方面侃侃而谈，从这里我也能感受到对方确实是有很浓厚的兴趣，可我只能比较被动性的回应。</p>
<p>最后说的申请这方面，我只能说，外界的原因和我自己的原因肯定是都有的。一方面我确实在暑研在一路过来非常倒霉，另一方面我也觉得自己一直以来做的不是很好。这二者结合起来，造就了我现在这样悲惨的情况，只有一个勉强还行的保底。如果说我没那么倒霉，没有被中途“背刺”，那可能和其他同学走一样的流程，最后也可以拿一个 return offer。但可惜没如果。而且在那之后我的处理也不是很好。导师的选择上，选了一个方向不那么一致的，像尝试一下改变，但后续工作的时候又遭遇进度问题，只能做回原来的方向，导致也不太匹配。确实就只是能勉强做一些成果出来了，也难以令我自己满意。</p>
<p>但其实，即使在写这些的时候，我也还有一个问题：我到底是按照什么标准在评价自己？比如说我前面说这么大一堆，我觉得我自己做的很不好，但这好与不好到底该如何评定？我成绩很不理想所以就很不好吗？我最后申请的结果不好所以就很不好吗？我自己想要的到底是什么，这个评价到底由谁来给。即使我说让我自己来评价自己，但我又要如何评价自己，我参考的依然是他人给我的标准，是那些被默认、公认的维度。但果真需要这样吗？一定是要申请到好学校，或者弄很有影响力的研究，做出一番大事，赚很多钱吗？只能说我们班的理念是这样的，而且我们占了这么多资源，理应是该这样的。但对于我自己来说，总感觉在这一整套标准之外，还缺了一点说不清的东西。我觉得拿这些来评价也没有什么问题，我也确实可以参考这些来评价自己。但我希望我不只是直接拿这些标准来作为我的目标和规范。我更在意的，可能是这些标准背后真正重要的东西，比如它们为什么会被认为是“好”，它们对应的能力、状态或者意义究竟是什么。也许这些不是一时就能想清楚的，而是需要在过程中慢慢体会，而这一点本身，也需要我去认真对待、用心去做。</p>
<p>那么，如果能重新来一次的话，我可能需要先接受一件事：动力的问题未必能被真正解决，它很可能一直都是不稳定的。与其期待自己始终有足够的热情，不如换一个更实际的做事方式——只要开始做一件事情，就尽量把其中至少一部分真正做透，而不是停留在完成任务的程度。之前在一些编程大作业中，我其实体验过这种从设计到实现的过程，也更容易进入状态。如果能重来，我应该会更主动地去保留这种过程，而不是在后面逐渐转向以完成任务为主。像 LLM 这样的工具，确实可以帮我完成很多事情，但如果关键的部分没有自己走一遍，就很容易停留在“知道大概在做什么”，却没有真正掌握。因此如果重来，我可能会给自己加一些约束，比如对一些基础或核心模块，至少手动实现一次，哪怕是简化版本，也比完全依赖生成要更有意义。再就是比较重要的暑研，之前确实没有意识到暑研的影响会如此大。而且当时也不是一些具体的失误，而是确实是通过一次变化就完全陷入了被动。如果能重来一次的话，我肯定需要留好足够的后路和应对方案，而不是在一个老师中途出问题后就有点慌乱，导致后面一连串失利。总之，对我来说，可能更重要的是在过程中不再轻易退回到“做到差不多就可以”的状态，而是至少在一部分事情上，尝试把它真正做完、做好。</p>
<p>给 5 年后的自己：希望你这些年没有被工具所代替，而是至少成为一个能把这些不断变化的工具用起来、并真正做出点东西的人。</p>
<p>给 10 年后的自己：三十而立，这个时候的你，应该已经过了最不确定的阶段了吧。也许很多事情都已经慢慢定下来了，只是希望你没有在一路往前走的过程中，把自己弄得太累，也没有把原本在意的一些东西一点点丢掉。</p>
<p>给 20 年后的自己：四十而不惑，那时候的你，大概已经过着一种比较稳定的生活了吧。也不去想你是不是有多成功，只希望你回头看现在的时候，不会觉得这段时间是可以随便带过的，也还能坦然地面对自己当时的选择。</p>
<h2 id="2">2</h2>
<p>在写完上面的内容之后，我突然意识到，我也可以把这些给LLM，看看它们的评价。这样我可以得到一些新的反馈，还能直接进行一些迭代，也可以写一点小结的小结。</p>
<p>LLM 提了一个很有意思的说法，它提出了一个主线：“在高水平环境中，没有真正进入主动成长状态，并逐渐失去驱动力。“相当于把我的问题的几层串了起来。另外由于整体是回忆着心路历程写的，所以它们会觉得结构上可以再优化一下，一些深层心理的篇幅可以更多一些。但我可能也还是需要进一步的沉淀和消化，所以有一种点到为止的感觉。</p>
<p>总的来看，感觉我这小结写的还是非常惆怅的，这毕竟也与我现在的心态有关。由于申请结果不理想，确实一直处在一个比较忧郁的状态。但更深的惆怅可能来自另一件事：我写了这么多，却发现自己对很多问题还是没有答案。不知道动力该从哪里来，不知道评价标准该是什么，不知道那些&quot;原本在意的东西&quot;到底是什么。这些问题在写的过程中一个个浮出来，但没有一个真正被解开。而我的小结也没有写成&quot;虽然走了弯路，但我成长了很多&quot;的励志故事。可能在我不知不觉的地方其实也还是有一些成长的吧。只能说，现在还算是尽力在剖析自己了，但是一些悬而未决的问题也确实还只是留在那里。但至少也算是在诚实的面对了吧，也不算是简单的就喊一下口号。至少说明这些问题我还在认真对待，没有随便给自己一个答案敷衍过去。可能确实是需要接受这样的一个现实，有些问题确实是很难直接有一个解决方案的，但也不是说就摆了，而是要接受它一直伴随着自己，但同时也是不断尝试着与其抗争。就像我提到的成长型人格那样，既然我现在意识到了，那在自己想退缩的时候，也还是可以多推一推自己了。</p>
<h3 id="3">3</h3>
<p>写了这么多下来才发现完全沉浸在自己的心路历程里了，都没怎么提“携手”，那刚好单独开一章来写写。我觉得还是很有必要回顾一下的。ACM 班确实是具有很强，很独特的凝聚力。我在上大学之前听到的说法就是高中交的就是最后的朋友了，上了大学就没有这么多交集了。而我和其他地方的同学朋友聊天，他们告诉我的行政班的感觉也都是基本相互不认识的感觉。相对之下，我们班一路下来确实是给人很不一样的感觉，也带来了很多优势。一方面，学长带来的传承，这种助教的形式，一级级下来对于课程内容的迭代，都不断在优化我们的体验。另一方面，同学之间的相互帮助也建立起了深厚的友谊，也让我交到了很多很好的朋友。那些奋力冲刺大作业的时刻，校园漫步时的讨论，都令我难忘。他们的真诚帮助也常常令我非常感动，在我生病时陪我去医院，在我申请和面试需要帮助时给我细心的指导。前两章写了很多遗憾、困惑、那些没能做好的事。但回头看，这四年里有些东西是确实留下来了的，这些人就是其中最重要的一部分。在最难的时候有人在旁边，这件事本身，可能比很多事情都更难得。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[2024 年度总结]]></title>
        <id>https://zsq259.github.io/post/2024年度总结/</id>
        <link href="https://zsq259.github.io/post/2024年度总结/">
        </link>
        <updated>2025-01-28T12:34:14.000Z</updated>
        <summary type="html"><![CDATA[<p>本来写了上半年总结的，感觉内容应该会少一些。没想到这下半年不算长的时间内，或许事情的量不算多，但也够我好好想一想了。</p>
]]></summary>
        <content type="html"><![CDATA[<p>本来写了上半年总结的，感觉内容应该会少一些。没想到这下半年不算长的时间内，或许事情的量不算多，但也够我好好想一想了。</p>
<!--more-->
<h3 id="学业">学业</h3>
<p>这学期分的比较明确的两个部分就是课程和实验室了，也可以先分开写写。</p>
<h4 id="课程">课程</h4>
<p>本期的课程压力应该不算大，感觉真正要学的就数据科学基础和计算机视觉，在线算法算是不知道选啥之后凑数的<s>反正也学不懂</s>。在这学期刚开始的时候，也还算非常有动力，在认真听且做笔记<s>承接上半年总结</s>，结果到了后面又开始摆了。其中数据科学基础是感觉讲课节奏太不适应了，而我的课后的行动力还是非常差，基本上就不会去回头看课程内的东西，导致感觉也没学啥。然后计算机视觉也是一样，后面上课的时候也开始干自己的事情摸鱼去了。现在回想一下，应该是有一次线上上课没认真听，然后习惯垮掉以后，后面听也听不懂就开始滚雪球了。但我觉得我应该是需要去学习一下这个课程的，也是值得我听的，所以我一直想再看一遍回放。讲实话也不是没时间，但就是一直没有去行动，导致一直又在摆和在拖。这个毛病后面还会再说。总之，课程内的东西最后还是落得了和以前一样的下场：几乎什么都没学到。</p>
<h4 id="实验室">实验室</h4>
<p>鉴于我已经写过了一篇实验室实践的经历和感想，这里不再说太多。但自从寒假回来后，我的状态可以说是非常糟糕，一直没有在工作的状态。明明知道情况其实比较紧急了，却就是提不起劲来。</p>
<p>总的来说，感觉我现在学习的状态非常糟糕。我一直希望我是真的为了学习本身而去学习，但现实却是我并不能那么享受这一过程。可能别人会去逼自己逼的足够狠，但我又狠不下心来，所以一直在以一种无所事事的状态逃避现实。其实我感觉无论是我课程的老师，还是实验室带我的博士生，他们都是很好的人，对待科研的态度都很认真，且都是很本质的在做事情，而我却在慢慢变得麻木和漫不经心。借着写这个的机会，也该审视一下我的内心，好好想想我的状态了。可能最根本上，我其实内心非常空虚，对什么都提不起兴趣，光靠着一点点才能走到现在。而看到一些同学们非常有兴致地讨论一些学术问题而我却插不上话的时候，我就经常感觉到和他们的差距。不管怎么说，他们的表现确实是很有兴趣的样子。或许我应该行动起来让我去真正感兴趣上一些方向吧。而说完动机之后，在执行方面我表现的也很差，最大的一个问题就是我不能长期保持专注，经常学一点不长的时间后就去摆一会，结果一摆又摆很久，时间利用率很低。这实际上一方面说明我不够能沉浸于在做的事情上，另一方面就是对自己狠不下心来。其实这些问题估计都说了几年了，但就是一直改不掉，或者说没有真正想过去改。那么感觉如果要真正行动起来的话，还是得要有一个让我真正想行动的动力。但对于现在几乎什么都不在乎了的我来说，这真的存在吗？</p>
<p>回顾一下我最本质的理想，可能是为了创造一个在人孤独时能够给予陪伴的对象，算是从我的实际情况出发的吧哈哈。但随着学习的不断深入，面对一些更复杂的现实时，就会感觉自己当初的一些想法非常天真，并且随之而来的是一些畏难的情绪。虽然理想很美好，但现实的路却很难走，这时我会感到畏惧，开始怀疑自己，而存在的一点兴趣也似乎被打散。而我现在什么都不在乎的心态，也许也是一个自我保护的机制，是对压力和焦虑的一种回避反应。可能我内心深处还是害怕着面对失败的现实，或者是那种做了也做不到的无力感。这点不只是学习，其实我生活的其他方面也存在着这样的现象。或许是为了给自己叠甲，但到最后却看不清自己真实的内心了。但即使这样，现实中还是有很多问题需要面对，比如升学。所以这样下去只会加深焦虑，说到底，还是要面对现实了。有时候我也会怀念我高三时的学习状态，那时感觉是我最接近为了学习而学习的状态。但现在已经没有这样的外部环境条件，只能靠我自己行动起来。至少，现在我迈出了开始反思的这一步了。那么之后的时间还是要把握住这一点动力，虽然可能也并不本质，但至少能让我再动起来，去寻找补充燃料的办法。总之，该找回那种学习的本质的感觉了。</p>
<h3 id="情感">情感</h3>
<p>本来如果一直等的话，这部分就直接一笔带过了。但在 2025 年 1 月 1 日还是发生了意外：发现微信也被拉黑了。这下就必须去问一下了，最终得到的答复就是分了就应该断干净，关系已经结束了，不要再联系对方了。既然这样，我的这段故事就彻底画上了句号。</p>
<p>当然，脑中不免还是会有许多思绪，其中最大的应该还是遗憾吧，正如五月天《突然好想你》中的那几句话一样。有可能她已经有新的幸福生活了，也有可能她带着遗憾向现实妥协了，但不管怎么样，她选择了告别过去，那我也不必再妄加揣测了。只是事后回想一下，我们的关系确实是 debuff 拉满。本身异地就难，人家家里还管得那么严，再加上我们都没什么经历，很多事情在当时其实都不明白，造成了很多的不好的影响。考虑到我们班的异地的已经全都分了，可能确实是非常难的吧。</p>
<p>但既然这段故事已经彻底结束了，自己的心态就该转变了。之前的我的心态可能还想着怀念和挽回过去，但现在只能向前看了。恰好有经历相似的同学，也聊过很多。我觉得理想的情况应该是能够坦诚地面对这一切吧，才叫所谓的放下。并不是说去忘记这些事情，或者说逃避，恰恰相反，这些经历也会变成自己的一部分，伴随着自己走下去。只是说，可能各方面会受到一些影响罢了。也许未来再经历类似的事情的时候，能够有更成熟的心态去思考和处理了；对于一些承诺方面，就会更加冷静和慎重了，可能不会轻易给出了，也不太会轻易相信了吧<s>太美的承诺因为太年轻</s>。而看向未来的话，我虽然还是会有这方面的希望，但一方面，能不能遇到合适的人还得看运气；另一方面，我觉得自己也还不够优秀，尚未准备好。所以现在还是先慢慢做好自己吧。而她当时说她无任何恋爱的意愿和可能了，但我还是祝她未来的旅程能更幸福丰盛。</p>
<p>现在想来，感觉我各方面确实有一个共同点，在上面两个方面也都有体现。或许是害怕自己受伤，所以选择了麻痹自己。在学业上害怕失败，害怕努力尝试，便变得好像没什么追求；在情感上害怕自己的付出白给，便经常有不安全感，以最大的恶意揣测别人，却忘记了去真正在乎对方的感受。虽然说是内心的防御机制，但回头来反思的话，感觉还是不好。并且这并不能真正的帮我抵御伤害。可能当时那一刻内心不会有太大的情感波动，但后面还是得去慢慢的面对现实，也会去焦虑，会忍不住想。就像是获得了一下的减伤，但后面还是会慢慢扣血。所以说，还是得要能够诚实地面对自己的内心才行。虽说现在有些心理上的机制已经比较固定了，但还是可以针对地注意一下。</p>
<h3 id="娱乐">娱乐</h3>
<p>仔细一想，这期好像都没怎么集中玩游戏，时间都零零散散地消磨掉了。</p>
<p>但是在某一个晚上，由于当时二游都没什么活，然后当时一个晚上 kupi 在玩，我看着也没忍住，于是自此开始打农了。</p>
<p>另外现充生活是去了五月天的演唱会，也算是长了见识了。现场的氛围确实和平时听歌不一样，毕竟是在偌大的一个体育场里面。而唱到《突然好想你》那几句的时候也没忍住眼睛湿了。另外就是没听过的歌的话，个人觉得参与感确实会少点。听过的熟练度拉的很高了，出前奏就知道是什么，但没听过的说实话坐那么远也听不太清楚。所以这次算是见了一下世面，那之后还要去演唱会的话应该只会考虑某个人的了。</p>
<h3 id="总结">总结</h3>
<p>翻了一下好像之前几篇都在最后立了点 flag，但说实话做的也不是很好，过去的失败确实让人越来越害怕下决心了。所以还是不放什么豪言壮语了，先行其言而后从之吧。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[2024 年上半年总结]]></title>
        <id>https://zsq259.github.io/post/2024上半年总结/</id>
        <link href="https://zsq259.github.io/post/2024上半年总结/">
        </link>
        <updated>2024-09-14T07:58:49.000Z</updated>
        <summary type="html"><![CDATA[<p>为什么突然写一个上半年的呢，因为这段时间确实经历了很多事情，也带来了很多新的思考。兴致来了就开始写了。</p>
]]></summary>
        <content type="html"><![CDATA[<p>为什么突然写一个上半年的呢，因为这段时间确实经历了很多事情，也带来了很多新的思考。兴致来了就开始写了。</p>
<!--more-->
<h2 id="2024-年上半年总结">2024 年上半年总结</h2>
<p>虽然说是上半年，实际上是九月份写的，也包括了暑假的内容。</p>
<h3 id="学业">学业</h3>
<p>首先是学业方面，这学期刚开始的时候可能又是经典上头，想要好好开始搞一手。结果真正面对作业的时候又开摆了。就算这期的核心在机器学习和大语言模型上吧，但几个大作业都实在不能令人满意，还是投入的太少了。另外也感觉越发依赖 ChatGPT 了，但是时不时问一句就要在那等一会，而且要是犯唐还要再调教很久，其实很拉低效率，可能有时候还是要回归传统搜索引擎。对于完成大作业本身，对我的感觉还是太乱了，以及没有在投入工作的感觉。虽然东拼西凑最后也搞出来了点东西，但可以说几乎没有创新性。另外大语言模型最后一个大作业其实我几乎没怎么参与，实际上也少了很多学习的机会。另外还是那个毛病，我现在搞科研感觉还是自己动手太少了，总是停留在一些抽象的层级上面，但面对代码又不怎么下得去手，有了 GPT 后更是如此。</p>
<p>另外几门学科其实也学得非常糟糕，主要还是没有真正去学的感觉，比如说预习上课复习什么的。虽然形式上没有翘过课，但概率论听不懂的时候也经常开摆，系统更是常常启动。所以说完全没有真正在学的样子。理论上，看了其他同学的学习方法，如果真正要学的话，还是得完善这一套学习的流程，以及好好做笔记。</p>
<p>而进了暑假主要有三件事情：PPCA 助教，PKU 暑期学校，以及实验室实践。其中 PPCA 助教是前两天突然被拉过去的，当时好像挺有责任感的样子，但后面来看感觉做的事情也很少（虽然也不知道其他组的情况）。以及那段时间人还在 PKU 参加暑期学校，没能提供线下的陪伴。助教的工作大概就这么多了，可能最主要工作的是找到了一个项目（笑）（还是水米建议的）。另外就是暑期学校了，虽然去之前开玩笑说就是去当旅游的，但实际上也没怎么旅游成。这里先讲学习的方面：去了那里之后面对其他学校的大佬还是有很大的压迫感，尤其是知道和 IOI 选手金牌坐一辆车的时候。每天基本上就是半天一个专题的讲座，倒是让我看到了许多之前都没听过的研究方向（虽然我本来也见得少），算是开开眼界吧。另外全英文授课也带来了不小的压力，经常听着听着就不知道在讲什么了。有一些老师更是语速飙得飞快，当然也有一些老师讲的非常清楚，还是挺好的。但课余的时间又基本上在摸鱼了，自制力不够导致的。另外社交方面，虽然说是多交朋友，但社恐还是根本不知道怎么主动和别人交流。另外三次企业参观也没有怎么听明白。</p>
<p>感觉上面一些问题还是自己专业知识积累太少导致的。如果是闲谈一些杂七杂八的我可能能说很多，但一到这种正式的场合就不知道能说什么，想必也是肚子里没什么东西导致的。这点还是需要日后多加积累，比如说在实验室搞自己的科研。说到实验室，在起初我还信心满满的觉得暑假里我能一路 dash，结果一方面忙着忙那的又被打散了注意力，另一方面遇到一点问题就卡住开始颓废。回想起来个人的状态还是太烂了。暑假本来说打算投一篇的结果最后也没有做出来。现在各方面的问题还是很大，看论文做实验什么的感觉还是没什么经验，和新手一样。现在换了一个方向做，希望能重新开始，扎实一点去做吧。</p>
<p>上面说了这么多，其实我感觉学业上，我最本质的问题还是我并没有什么驱动力，那种一定要做到的事情的感觉，一直是得过且过的心态。也可能是在乎的东西太少了吧。我自己一直想追求最本质的动机，因此觉得成绩和排名什么的不过是前进路途上顺路获得的东西，而不是目标本身，所以虽然也很重要但并不那么在意。但我的问题是我现在并没有找到更本质的想要去追求的东西，或者说，虽然可能有，但又感觉太过遥远而并不能带来什么驱动力。游戏里的阶段性目标都会设计成玩家触手可及，稍微再尝试一点就能达成的样子，但看来我并没有为自己设计好这样的阶段。</p>
<p>当然，之后的日子肯定也不能一直颓废下去，所以说还是要增加自己的行动力。关于目标什么的可能是一个遥远的追寻过程，但现在，我计划减少自己无所事事的时间，当进入这样的状态的时候马上去找事情做，或许能提高一些行动力。当然也还是要看日后的执行情况。</p>
<h3 id="情感">情感</h3>
<p>最终还是决定再想想这些事情。在今年 3 月 18 日，hst 迎来了他的至暗时刻。在多重 debuff 的加持下终于走向了分开的结局。说实话，当时那个状态，在异地的情况下本身体验并不好，且已经有一些不安全感了，再加上对方当时的状态已经表示很累了，感觉确实不适合再继续下去。因此虽然我也经常想要是当时再多留一下会怎么样，但也还是没有因那个决定后悔过。但自那之后，我几乎没有一天不在想这些事情。只能说可能有些方面，确实在一起的时候完全没有考虑到，只有在分开后才会去想到。关于这方面的思考其实在另一篇里也写了很多了，这里就不再重复。最核心的应该还是，由于家庭方面的原因，对于亲情这一块的缺失使得我希望寻找一种亲密感和归属感，但彼时的我并没有意识到，我多数情况下属于是希望用自己的付出来获得回报，也正因此，当觉得没有获得回报时，就会因为付出的失衡而陷入内耗。不仅如此，甚至还会因为对回报的期待而过于要求对方，甚至指责对方。现在看来可能并不算是健康的状态了。另外，当两个人之间安全感充足的时候其实也需要注意，如果是有稳定的安全感时，可能就会稍微纵容自己对对方的态度和方式，不那么注意自己的举止，从而伤害对方。回想起来曾经也是发生过的，但当时完全没有这样的意识。总的来说，分开之后确实让我意识到了很多事情，是当初在一起时完全没有注意到，但其实很重要的。但可惜亲身学习的成本太高了，现在已经落得了这样的境地。</p>
<p>而在分开后，暂时地有过一段联系，再往后则是逐渐在各个平台被删除好友。我其中并不能理解这样的动机，也在心里妄自揣测过许多种可能。只能说，看到自己的好友被删除的时候还是会非常伤心的。而在 8 月 12 日，我也再次发送邮件表明自己的思考和心意，只是到现在依旧没有回复。</p>
<p>至于为什么我一直放不下这个事情呢，一方面是我确实有对这种事情的渴望，这种亲密关系之类的。另一方面，当我回忆起过往的时候，发现我可能还是猜疑了太多，以至于没有对对方有足够的信任，但回过头来看才发现，我还是应该相信对方的。总的来说留下了许多遗憾。除此之外，随着我认识到了各种各样的人之后，对方在我心里的含金量还是一直在上升的。我之前也有很多没做好的地方希望能够弥补。所以我一直留着念想，但我也完全不知道对方如今的态度。只能说，怎样的结局我都还是能够面对，但心中的感触也还是会有所不同。但既然我有这样的想法，那在想到所有的可能性后，不妨再相信一下，对方也和我一样会认真地对待这种事情吧。</p>
<p>感觉我现在对于这种事情已经变得非常理想化了，简单来说就是追求纯粹的人的情感，希望这种事情主要由两个人相互被对方这个人本身所吸引的感情所推动。虽然这在现在现实中看上去已经遥不可及了，人们的追求发生了变化，人与人相互了解的成本也变得很高。所以这也仅是我个人的一点理想，至少现在还能在坚持一下。对于我来说，尽量需要做的也就是做好我自己了，剩下的就交给运气了。</p>
<h3 id="娱乐">娱乐</h3>
<p>可能是赚的米变多了吧，这半年来的娱乐活动也多了不少。</p>
<p>还是先从游戏说起，粥原崩三个基本上就那样，主要取决于抽卡，欧就玩非就不玩。</p>
<p>然后通关了老头环本体和 dlc，转近战法师后交互感和成就感高了很多，技术应该也提升了不少。</p>
<p>黑猴也玩了两章了，感觉还不错。</p>
<p>此外线下活动多了不少，可能算是现充点了？</p>
<p>五一期间去了音律联觉，星铁land 和明日方舟嘉年华，暑假又去了原神 fes。可惜由于不那么知情导致 tho 的 staff 没当成。</p>
<p>之后是旅游：暑假刚开始就和群友一起去了台州一圈玩了几天，也算是开启了浙江的地图。另外去北京那边也算是出去玩了一天，可惜没有多去一些经典的景点。另外马上就又要去一趟南京了。曾经在空间和朋友圈看别人感觉一暑假出去了好多次，现在自己倒好像也变成这样了。</p>
<h3 id="终">终</h3>
<p>大概也算是把对我来说影响最大的两个方面稍微梳理了一下吧，其实现在要做的也挺明确的，就是好好过好自己的生活。学业方面也要加把劲来点紧迫感了，另外养成一下想颓废的时候就去干点正事的习惯，让生活过得充实一点吧，实际上该做的事情还是有很多的，以及前面没提到的学习语言的事情也要加油了。所以总的来说，也希望自己能成为一个更好的人，有一天能够真正问心无愧就好了。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[2023年度总结]]></title>
        <id>https://zsq259.github.io/post/2023年度总结/</id>
        <link href="https://zsq259.github.io/post/2023年度总结/">
        </link>
        <updated>2024-02-10T07:58:49.000Z</updated>
        <summary type="html"><![CDATA[<p>其实好早之前就准备写了的，结果大年初一这个点才更，可以看出实在是太摆了。</p>
]]></summary>
        <content type="html"><![CDATA[<p>其实好早之前就准备写了的，结果大年初一这个点才更，可以看出实在是太摆了。</p>
<!-- more -->
<h3 id="流水账">流水账</h3>
<p>一月份疫情回来开摆，处理一些破事。</p>
<p><s>似乎会和去年年度总结有重合的部分毕竟结束是到农历新年</s></p>
<p>受某些事情影响，寒假也是没有坚持过几天正经计划。（不过本来就摆，只能说心更烦了）</p>
<p>然后开学，开启了比较迷茫的第二学期。主要在于感觉学的差不多就可以了，不知道为什么要学那么好。后面再细讲。</p>
<p>错峰中招被送进大智居隔离。期间开启了假面骑士坑。</p>
<p>第一次去漫展。但明日方舟 only。</p>
<p>整个学期间好像就挺平常地过去了。</p>
<p>五一假第一次线下 dating。</p>
<p>暑假小学期，快乐编译器。</p>
<p>去了 tho。</p>
<p>长达两周的假期时间，然而还要写编译器。开摆。</p>
<p>和又一次线下。</p>
<p>开学，成为了学长和助教。</p>
<p>国庆节第三次线下。</p>
<p>装机，能打游戏了。老头环启动。</p>
<p>去图书馆和自习室的时间变多了。</p>
<p>养成了喝奶茶和吃夜宵的坏习惯。</p>
<p>2023 结束，之后是 2024 到农历新年。</p>
<p>寒假彻底开摆，啥事没干。</p>
<p>去广州旅游。</p>
<p>某些极其不愉快的事情。</p>
<h3 id="学业">学业</h3>
<p>正如前面所说，大一下的时候人还是有点迷茫，主要是觉得对一些科目不感兴趣，就觉得没有必要学那么好。</p>
<p>然后就在 qua 的时候被 yyu diss 了。</p>
<p>细想一下的话，有几点还是有些道理的：不感兴趣不代表不能学好，以及现在不感兴趣的可能在未来对于感兴趣的东西会很重要，比如学最优化的时候就感觉数分基础泰拉了。</p>
<p>然后是 ppca，还是比较有干劲的。<s>也贡献了许多更博</s>。</p>
<p><s>但分组后感觉大部分时间都在摸鱼</s></p>
<p>作为唯一的 AI 组，研究 codemate(x)，研究爬虫(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msqrt><mrow></mrow></msqrt></mrow><annotation encoding="application/x-tex">\sqrt{}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.04em;vertical-align:-0.2395em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8005em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"></span></span><span style="top:-2.7605em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg width='400em' height='1.08em' viewBox='0 0 400000 1080' preserveAspectRatio='xMinYMin slice'><path d='M95,702c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,
-10,-9.5,-14c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54c44.2,-33.3,65.8,
-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10s173,378,173,378c0.7,0,
35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429c69,-144,104.5,-217.7,106.5,
-221c5.3,-9.3,12,-14,20,-14H400000v40H845.2724s-225.272,467,-225.272,467
s-235,486,-235,486c-2.7,4.7,-9,7,-19,7c-6,0,-10,-1,-12,-3s-194,-422,-194,-422
s-65,47,-65,47z M834 80H400000v40H845z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2395em;"><span></span></span></span></span></span></span></span></span>)。</p>
<p>感觉后面搞神经网络的时候也没有能系统地学点东西，而是随便搓了点代码，就去瞎调参了。</p>
<p>这样看来其实感觉收获不是很大。</p>
<p>然后就是编译器，第一次写这么大的一个项目。</p>
<p>一开始也是完全上手不能，只能先参考 kupi 的照葫芦画瓢一波。但在了解了以后就可以自己手搓了。</p>
<p>虽然最后的成果不太理想，但这个过程也还是收获了很多。</p>
<p>另外达成成就：六周全勤奖无迟到。</p>
<p>在下半年进入大二后，学业压力又增大了。（三门数学课带来的自信）</p>
<p>但是动力也比上半年更足了，出去自习搞学的次数也变多了。</p>
<p>但感觉效率和时间利用率还是太低了，到最后也没有一科敢说是学的很好，完全掌握的。</p>
<p>但是物理完全放弃了，结果期末直接背刺。</p>
<p>另外对于助教工作，也是体会到了作为助教是怎样的感受，从另一个视角了解了我们大一上程序设计课的方方面面。但最惭愧和难受的一件事就是期末机考的出题，自己还是水平不够，加上选题时的失策，导致最后题目难度失去控制，也让人体会到了有时候方向错了，付出再多努力也成效不大。</p>
<p>而这期最后我大概意识到了一件事情，我的行动力其实还是不太行，有些畏手畏脚，总觉得“这个东西我肯定学不懂”，“这个题我自己肯定做不出来”，也算是“给自己设限”了。但有时候，其实没必要去在乎能做到怎样的程度，或者一定要能获得怎样的能察觉到的收获。只管去做，某一刻可能就会发现，之前想都不敢想的事情，也能轻易做到了。</p>
<p>这样看来上面两段似乎还有点矛盾，有时候就是怕搞错了方向才犹豫不敢往前的。这样的话可能关键就在选择了，选择的时候慎重，在选完后就放手去做。但好像实现起来的话还是会很困难。</p>
<h3 id="娱乐">娱乐</h3>
<p>原神从 3.4 开始应该就直接裂开了，剧情抽象地图无聊抽卡还老歪，就没玩了。不过后面进枫丹之后又反转了一波，至少主线剧情起来了。所以现在属于版本更新后就玩玩的情况。</p>
<p>而今年崩铁开服了，也是整了个号随便玩玩，没想到还是终极无敌至尊欧皇。但游戏本身感觉还是原神那一套，到最后天天刷，就也没正经玩，每次版本更新了上号肝一肝抽个卡。最近 2.0 的主线剧情还可以，但是抽卡非了，所以以后更不会正经玩了。（过年玩的三款游戏只有一个给我歪了，是谁呢）</p>
<p>另外在 steam 上也玩了不少游戏，装完机之后电脑的配置也终于足够了，于是今年后半年的主要游戏就是老头环。作为魂系游戏难度和我以前玩的那些高了不少，游戏性也是没得讲。不过现在感觉还没找到太多注入感情的感觉，不过也不急，慢慢玩吧。</p>
<p>令人印象深刻的另一部作品是 kupi 推荐的 to the moon，主要是剧情和音乐，使我水元素充盈，也在 steam 上正经发表了第一篇评测。</p>
<p>但是今年就着实没玩几部 gal 了，应该只玩了亚托莉，樱之诗也还是推不动（咕咕咕咕咕咕咕）。</p>
<p>此外，入坑了假面骑士，看完了 kabuto, decade, w, fourze, wizard, drive, ex-aid, build, zio, 01, saber, revice, geats, 在追 gotchard。<s>这样数起来确实看了好多</s></p>
<p>比较上头的是买了许多假面骑士玩具。</p>
<p>另外，线下终于第一次去了漫展，然后发现没钱买东西。不过后面的互动环节还是乐了一把。</p>
<p>但在 tho 好像就只是逛了半天，也买不起啥。鉴于更衣室比较贵，可惜没出成 cos。互动也没怎么看。</p>
<p>第二天的 live 则是站了 7h，鉴于我对东方社团也不是很熟悉，也没有体验过这种 live 的氛围，所以大概也没有怎么融入进去吧。不过多少也算是一些特别的体验了。</p>
<p>而在年末受邀去了一趟广州，主要是冲着明日方舟的联动博物馆去的，也逛了一些景点，吃了一些特色菜。但其实令我享受的，是出行本身而已。<s>重量级的是过去的通宵硬座和回来的 10h 无座</s></p>
<h2 id="碎念">碎念</h2>
<p>感觉自己越来越怠于思考了，有些时候一些值得去细想的东西选择了放到一边。</p>
<p>而这个年度总结，本来可以在元旦节写的，当时决定了过农历年写，但放假回来后又一直鸽了。直到今天，正月初一才匆匆写完。有点赶 ddl 的味道了。</p>
<p>回想去年几年，由于分班和升学，每年都能接触到一批新的同学，这样看来今年还是头一回更加稳定一些的一年。而身边和线上的好友间的关系倒也依旧稳固。没有像以往那样失联，倒应该也算是进步了。</p>
<p>转眼间迈入大学也已经一年半了，相当于半个高中的时间，有点越来越快的感觉。但不得不说，依旧没有找到一种学习和生活的感觉，就觉得时间只是平淡地流过，自己也讲不出有多少变化。但未来的方向将越来越清晰，为了自己所希望的，也应该提起精神来。或许，我还是差了点下定决心的勇气。</p>
<p>总之，希望自己能在新的一年里能真正享受生活的点点滴滴，能成为自己真正所希望成为的样子。也祝愿所有的人们，新年快乐。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[十月月度总结]]></title>
        <id>https://zsq259.github.io/post/十月月度总结/</id>
        <link href="https://zsq259.github.io/post/十月月度总结/">
        </link>
        <updated>2023-11-01T09:28:07.000Z</updated>
        <summary type="html"><![CDATA[<p><s>在 kupi 的 诱劝下写了这篇</s></p>
]]></summary>
        <content type="html"><![CDATA[<p><s>在 kupi 的 诱劝下写了这篇</s></p>
<!-- more -->
<p>在国庆节放完假后回来后，感觉其实十月份也就是做三次物理实验的轮回。不过回想一下，又觉得似乎还有些漫长。遂小回顾一下。</p>
<h3 id="国庆节放假">国庆节放假</h3>
<p>在家开摆，有点无聊。期间就出去 date 了一次。</p>
<p>家里的事情还是很令人头疼。</p>
<h3 id="学习">学习</h3>
<p>至少出去自习的次数增加了不少。<s>开始约卷</s></p>
<p>不过感觉效率提升也不是很大，经常感觉在图书馆坐半天但是也没干啥事。</p>
<p>目前还是有一种被赶着走的感觉，没有一点主动的想法。想必长远来看会寄。</p>
<p>物理课彻底摆了，但时间并没有很好的利用上。</p>
<p>物理，最优化，系统，这三门课的东西现在脑子里是一团糊（物理是啥都没有）</p>
<p>最优化吃了数分基础不行的亏，也没有一点系统的认知。</p>
<p>这里不得不提到上期我的困惑，虽说在 qua 的时候没有得到很好的解答，但现在我自己明白了打基础的重要性。之前脑子里就是一点东西都没有的感觉，现在学新东西的时候，就难以更加深入。所以就重要性而言的话，之前的我还是低估了。但现在希望能把这一块给补回来，至少是把最优化给学明白得。毕竟以后还得再用上的。这大概也需要更加强的时间管理和自律。</p>
<p>另外，感觉对事物的兴趣已经越来越淡了。看着别人学子讲坛讲一些自己喜欢的东西，可以讲得很深的时候也还是比较感慨，感觉自己没有哪方面是了解足够深入的。</p>
<p>不过上课不让自己玩手机了。</p>
<h3 id="工作">工作</h3>
<p><s>双倍的工作，双倍的工资</s></p>
<p>说实话感觉打工带来的额外的压力其实并不大。boyu 那边和以前没什么区别。</p>
<p>今年当上了下一届程序设计的助教，看着现在的小朋友就会想到当年的我们。</p>
<p>不是自己负责的部分的时候，感觉几乎没什么事情。</p>
<p>（精力充沛且实力强大的 dark 教授是另一回事）</p>
<p><s>而到了自己负责的大作业的时候，好像也没什么事</s></p>
<p>主要是没小朋友来答疑或者交流，也就时不时看看作业页面。</p>
<p>不过后面还会负责小作业和机考以及期末机考出题，好日子还在后头呢。</p>
<h3 id="经济">经济</h3>
<p>为了买 saber 大套，在某些方面的自律倒是还可以，比如每天记账。</p>
<p>有了泡沫般的工资后，花钱倒是大手大脚了一些，具体体现在买玩具上。</p>
<p>不过11月要考虑装机了。</p>
<h3 id="生活">生活</h3>
<p>似乎一个月没怎么额外的运动。</p>
<h3 id="展望">展望</h3>
<p>还是需要更努力一些。主要体现在时间管理上面。很多时候还是坐那刷刷手机啥的一个半天就过去了，事后又觉得挺后悔的，但当时就是感觉没有干劲。这样的状态还是不太行。所以说这方面还是得加把劲吧，估计得调整状态。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[bert学习实录]]></title>
        <id>https://zsq259.github.io/post/bert学习实录/</id>
        <link href="https://zsq259.github.io/post/bert学习实录/">
        </link>
        <updated>2023-07-27T03:32:53.000Z</updated>
        <summary type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>在上上周，我们的分类器实现主要是基于机器学习库 <code>sklearn</code>，使用的也都是已经封装好了的类。在这周，我们将探索深度学习与神经网络，利用 bert 预训练模型并手动实现下游模型，来实现我们的分类器。</p>
]]></summary>
        <content type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>在上上周，我们的分类器实现主要是基于机器学习库 <code>sklearn</code>，使用的也都是已经封装好了的类。在这周，我们将探索深度学习与神经网络，利用 bert 预训练模型并手动实现下游模型，来实现我们的分类器。</p>
<!--more-->
<p>参考：</p>
<ul>
<li><a href="https://www.bilibili.com/video/BV1a44y1H7Jc/">https://www.bilibili.com/video/BV1a44y1H7Jc/</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/364933751">https://zhuanlan.zhihu.com/p/364933751</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/431796984">https://zhuanlan.zhihu.com/p/431796984</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/493424507">https://zhuanlan.zhihu.com/p/493424507</a></li>
</ul>
<h2 id="step1">step1</h2>
<p>在 <a href="https://www.bilibili.com/video/BV1a44y1H7Jc/">https://www.bilibili.com/video/BV1a44y1H7Jc/</a> 中，我们可以跟着视频提供的代码实现一个训练一轮并测试的分类器。视频已经说的比较清楚了，这里再回顾一下：我们首先用 <code>bert-base-chinese</code> 将文本处理成 token，再将 token 传入我们的下游模型进行训练。而我们的下游模型是基于预训练模型实现的，在视频中也是选取的 <code>bert-base-chinese</code> 作为预训练模型。而在训练一轮之后，我们发现我们的结果非常不理想，测试集上的预测准确率只有 70% 不到。因此，我们需要进一步优化。</p>
<h2 id="step2">step2</h2>
<p>首先感谢 jpp 同学指出训练应该不止一轮，而要反复多轮次地训练。经过实践，在进行多轮训练，也就是把训练集多次喂给模型后，在测试集上的预测准确率确实有所提升。而这时一个整体上的方法。在此基础上，还有以下参数可以调整（也是我有所尝试的）：</p>
<ul>
<li>
<p>预训练模型的选择</p>
<ul>
<li><code>bert-base-chinese</code></li>
<li><code>algolet/bert-large-chinese</code></li>
<li><code>allenai/longformer-base-4096</code></li>
</ul>
</li>
<li>
<p>下游模型中的神经网络层</p>
</li>
<li>
<p>batch_size</p>
<ul>
<li>16</li>
<li>32</li>
</ul>
</li>
<li>
<p>max_length（预训练模型接受一句话的最大长度）</p>
<ul>
<li>512</li>
<li>1024（仅<code>algolet/bert-large-chinese</code> 可用）</li>
<li>2048（仅<code>algolet/bert-large-chinese</code> 可用）</li>
</ul>
<p>预训练模型限制了能接受的最大的长度，而我们有不少数据都是超过这个长度的，所以实际上模型接受的只是句子的一部分。而默认情况下，则是从句首截取指定长度。这也是一开始遇到的一个问题。而可能的解决办法有三种：</p>
<ul class="contains-task-list">
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox" id="task-item-877610"><label class="task-list-item-label" for="task-item-877610"> 随机在句子中选取一段。</label></li>
<li class="task-list-item"><input class="task-list-item-checkbox" disabled="" type="checkbox" id="task-item-9490108"><label class="task-list-item-label" for="task-item-9490108"> 使用滑动窗口将句子拆成若干个句子。</label></li>
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox" id="task-item-4139176"><label class="task-list-item-label" for="task-item-4139176"> 调整模型使其能接受更长的句子，或使用其他模型。</label></li>
</ul>
</li>
<li>
<p>requires_grad（是否梯度回传，即是否修改预训练模型的参数，也就是微调 bert）</p>
</li>
<li>
<p>learning_rate（梯度下降时的学习率）</p>
<ul>
<li>1e-3</li>
<li>5e-5</li>
<li>5e-6</li>
</ul>
</li>
<li>
<p>weight_decay（减少过拟合的可能，设置过大可能导致欠拟合）</p>
<ul>
<li>1e-5</li>
</ul>
</li>
</ul>
<p>接下来就是对于每种参数测试了。由于算力原因，小编只能使用 Kaggle 和 Google colab 云端平台来运行代码。而迭代次数设置为了 100，所以一份代码的运行时间也非常久。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[python 爬虫进阶]]></title>
        <id>https://zsq259.github.io/post/python爬虫进阶/</id>
        <link href="https://zsq259.github.io/post/python爬虫进阶/">
        </link>
        <updated>2023-07-18T02:43:53.000Z</updated>
        <summary type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>在上上周我们了解了 python 爬虫的基本操作，这次就让小编带大家来了解更多的爬虫吧！</p>
]]></summary>
        <content type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>在上上周我们了解了 python 爬虫的基本操作，这次就让小编带大家来了解更多的爬虫吧！</p>
<!--more-->
<p>这次我们的目标也是两个：</p>
<ul>
<li>爬取 CSDN问答区精华版块的所有问答</li>
<li>爬取 stackoverflow 的数据</li>
</ul>
<h2 id="csdn精华">CSDN精华</h2>
<p><a href="https://ask.csdn.net/channel/1005?rewardType&amp;stateType=0&amp;sortBy=1&amp;quick=6&amp;essenceType=1&amp;tagName=essence">我们本次要爬取的网页</a></p>
<p>简单观察网页，我们会发现，随着页面往下滑动，会出现更多进入具体问答页面的链接。在控制台中的 <code>Elements</code> 项中，也能观察到出现了更多的类似于 <code>&lt;div class=&quot;question-content-wrapper&quot; data-v-09010672=&quot;&quot; data-v-2e2ddf27=&quot;&quot;&gt;&lt;/div&gt;</code> 的标签。但当我们进入 <code>network</code>  项中，却发现并没有能够得到相关的数据。因此，这和上次下滑出现更多页面的分 <code>p</code> 发包的原理不同，这些链接是通过页面的滑动，用 js 动态渲染出来的，和上次的答案部分一样。</p>
<p>当然，我们的爬虫技艺也不会止步于此。既然能在 <code>Elements</code> 中看到，那想必还是有办法爬取的。之前我们爬虫利用的原理是发送请求后从返回的响应中找信息。那能不能让爬虫直接像我们平时使用浏览器一样获得最后渲染出的页面呢？</p>
<p>自然是可以的。这次我们使用的原理就是在爬虫中模拟一个浏览器，通过浏览器打开页面，来直接得到页面中显示的所有数据。而这些自然也是有很多造好了的轮子。一个非常经典的便是 <code>selenuim</code> 。可以参考<a href="https://www.selenium.dev/zh-cn/documentation/webdriver/getting_started/">入门指南</a>。但小编这里用的不是这个，而是另一个更加新一点的，叫做 <code>playwright</code>。学习资料参考 <a href="https://cuiqingcai.com/36045.html">这篇文章</a>。同时<a href="https://playwright.dev/python/docs/api/class-playwright">这里是 api 文档</a>。</p>
<p>于是，我们可以在模拟的浏览器中打开刚刚的 CSDN 精华 的网页了。并且，阅读 api 文档后，我们发现可以用 <code>page.mouse.wheel()</code> 函数来模拟鼠标滚轮。接下来，只需要一直滑动到页面底端爬取所有进入具体问答页面的链接就行了。</p>
<p>在此过程中，有两种选择：一种在一边滚轮的同时一边进入得到的链接并爬去具体信息；另一种则是先滚到底部，爬取所有的链接并存入文件中。接下来我们只需要在那个文件中读入所有链接就能进行后续的爬取。在写爬虫时，调试也是经常需要遇到的，所有我觉得这里第二种更优，毕竟鼠标滚轮滚完一遍后，就不需要再进行这样的操作了。否则，我们反复运行程序调试，每次都要一遍滚一边爬，还是比较麻烦。</p>
<p>在获得所有链接后，接下来就是进入链接爬取问答详情了。由于我们已经有了模拟浏览器的手段，动态渲染的答案对我们来说也已经不算问题。但当我们尝试用之前的 <code>asyncio</code> 进行并发爬取时，至少小编的电脑是直接炸了。原来当我们把所有链接加入任务后，如果没有限制，爬下来的链接数量是 3000 左右，则相当于有 3000 个任务并发。于是一种处理的手段是利用信号量（Semaphore）来限制并发的数量。这自然是可行的。</p>
<h2 id="更多并发的方式">更多并发的方式</h2>
<p>不过，除了协程，还有其他并发的手段，那就是多线程和多进程。</p>
<h3 id="多线程">多线程</h3>
<p>参考</p>
<ul>
<li><a href="https://www.runoob.com/python/python-multithreading.html">https://www.runoob.com/python/python-multithreading.html</a></li>
<li><a href="https://www.zhihu.com/question/263977050/answer/2138607545">python爬虫如何利用多线程？ - 刘早起的回答 - 知乎</a></li>
</ul>
<h3 id="多进程">多进程</h3>
<p>参考</p>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/86805649">python爬虫效率提升——多进程 - ziyangwong的文章 - 知乎</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/340657122">在Python中优雅地用多进程 - 曾伊言的文章 - 知乎</a></li>
</ul>
<p>学会了这些手段后，我们的爬虫优化也能变得更加多样。</p>
<h2 id="stackoverflow">stackoverflow</h2>
<p>这个网站的爬取将更加艰难，因为它设置了一些反爬虫的机制。</p>
<p>但我们还是先把爬虫的大体思路梳理一下。进入网站后，由于这是全英文的，所有需要先翻译我们要搜索的关键词。接下来，搜索关键词，我们会要进行一步<strong>人机验证</strong>。之后的感觉就和上上周的 CSDN 一样了。我们获得进入详情页面的链接，再在详情页获取具体的问答信息即可。在这里我们选择下方的第一个回答作为答案。</p>
<p>但问题是，我们的爬虫无法完成人机验证。这也就意味着无法进入搜索关键词后显示所有具体链接的页面。此时似乎陷入了僵局。但在多次访问和搜索后，发现在进行一次人机验证后的一段时间内，搜索将不需要进行人机验证。而更具体地，不需要进行验证的时间为五分钟。所以一个简单的想法就是我们在浏览器中手动点一下人机验证，然后让爬虫爬五分钟，再手动点一下，以此循环。但在大量数据面前，这对我们而言似乎不太友好。</p>
<p>于是，我们希望能提升爬虫的速度，让它在五分钟内能爬尽量多的数据。但当我们上了高并行的爬虫后，我们发现没过多久爬虫就收不到响应了。进入浏览器再人工查看网页，会有一个提示，说我们的 ip 在同一时间发送了太多请求，这是不正常的，所以把我们的 ip 封了。</p>
<p>这个问题理论上可以通过 ip 池随机代理来解决，但是处理起来比较困难。而经过探索，我们发现，只有搜索关键词后才会跳转人机验证，而进入具体问答页面的链接是不会跳人机验证的。再联系上文 CSDN 精华的处理方式，便能得到一个简单点的想法。首先我们以人工辅助人机验证的方式来为爬虫获取五分钟时间，在这五分钟内只把跳转问答详情页的链接爬下来并存在文件中。获取所有链接后，我们的爬虫便能以一个合理的速度，慢慢地，不被封 ip 地爬取所有的详情问答了。</p>
<h2 id="scrapy-框架">scrapy 框架</h2>
<p><code>scrapy</code> 是一个实现的框架。参考：</p>
<ul>
<li><a href="https://segmentfault.com/blog/rui0908">https://segmentfault.com/blog/rui0908</a></li>
<li><a href="https://blog.csdn.net/cainiao_python/article/details/119224134">https://blog.csdn.net/cainiao_python/article/details/119224134</a></li>
</ul>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[文本的向量化和分类器的训练]]></title>
        <id>https://zsq259.github.io/post/文本的向量化和分类器的训练/</id>
        <link href="https://zsq259.github.io/post/文本的向量化和分类器的训练/">
        </link>
        <updated>2023-07-15T14:17:53.000Z</updated>
        <summary type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>在上周，我们爬取了 CSDN 和 Wikipedia 上的部分数据。那么在这周，我们将对数据进行处理，并用于训练我们的分类器。本文将介绍如何进一步对爬下来存储为 <code>.jsonl</code> 文件的数据进行处理，以及后续的随机森林算法来训练我们的分类器。</p>
]]></summary>
        <content type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>在上周，我们爬取了 CSDN 和 Wikipedia 上的部分数据。那么在这周，我们将对数据进行处理，并用于训练我们的分类器。本文将介绍如何进一步对爬下来存储为 <code>.jsonl</code> 文件的数据进行处理，以及后续的随机森林算法来训练我们的分类器。</p>
<!--more-->
<h2 id="文本的向量化">文本的向量化</h2>
<p>在自然语言处理等领域，一个最基本的问题就是，如何让计算机“认识”对我们而言十分日常十分熟悉的语言。虽然我们能够很自如的运用，但计算机可是看不懂一点。在我们的数据中的体现就是问题和回答等，这些直接给计算机的话，显然是不行的。因此，要将语言喂给计算机，我们肯定是要对其进行一些处理。</p>
<p><a href="https://www.bilibili.com/video/BV1vS4y1N7mo">这个视频</a> 对从 one-hot 到 word2vec 都有介绍，并且给出了许多可以探索的学习资料。这些部分在此就不再赘述。在视频中是用的 python 的 <code>gensim</code> 库，这自然不失为一种选择。</p>
<p>而另一种 word2vec 的选择是 <code>TfidfVectorizer</code>。<a href="https://blog.csdn.net/blmoistawinde/article/details/80816179">这篇文章</a>对其进行了简单的介绍，从用 <code>jieba</code> 分词到一些参数的含义都已经说明了，这里也就不展开讲述。</p>
<p>当然，可供选择的模型还有很多，但作者并没有一一尝试了。但总之，通过 word2vec，我们将数据中的若干个句子转化为了计算机可以运算的向量，来进行接下来的训练。</p>
<h2 id="分类器的训练">分类器的训练</h2>
<p>让我们回顾一下，我们最终的任务究竟是要做什么？</p>
<p>实际上，到这一步，我才理清我们的任务的关系。对于我们爬下来的问答对，自然是有好的问题和回答，也有不好的问题和回答。而我们最终其实是要实现一个分类器，能够用于判断一对问答是“好的”还是“坏的”。这其实就归类到了机器学习中的分类算法了。<a href="https://www.bilibili.com/video/BV164411b7dx/">这个视频</a>的 p1 到 p4 可以让我们初步了解一点机器学习。而具体训练时，我们需要先喂给模型一些训练集，给它一些问答对，告诉它“这些是好的”，“这些是坏的”。之后它将通过算法学习分类，并对我们给出的测试集进行预测。当然，我们自己手中也是有测试集的实际“答案”的，因此可以对模型的预测进行评价打分。自然，我们是希望它的得分越高越好。</p>
<p>而模型究竟是如何进行学习和分类的呢？这就是各种算法大展神通的时候了。本次我们采用的是随机森林算法。它的前置知识决策树在<a href="https://www.bilibili.com/video/BV1T24y1L7ik/">这个视频</a>中有介绍，而随机森林算法在<a href="https://www.bilibili.com/video/BV12s4y1N7Bp/">这个视频</a>中同样也已经介绍了，这里就不再展开讲。</p>
<p>而具体到代码实现上面，<code>sklearn</code>已经给我们造好了轮子。我们只需使用其中的 <code>RandomForestClassifier</code> 类便能实现随机森林算法。它的 <code>fit()</code> 函数能够接受我们的数据集 (X, y)，在这里 X 代表我们的问答对，y 表示我们对问答对的标注（好坏）。而 <code>predict()</code> 函数则能接受问答对的数据，并给出预测。再通过 <code>accuracy_score()</code> 函数传入预测的结果和实际的好坏标注，便能给出模型的得分，为 0~1 之间的一个实数。</p>
<p>或许上文讲的还是非常抽象，可以参考<a href="https://github.com/zsq259/PPCA_codemate/blob/main/classifier/test1.py">我的代码</a>。</p>
<p>至此，我们的分类器训练就基本可以宣布结束了。</p>
<h2 id="一些问题">一些问题</h2>
<h3 id="多个特征的处理">多个特征的处理</h3>
<p>细心的读者可能会发现，对于究竟是如何把向量化后的数据丢给<code>RandomForestClassifier</code>的这件事情，我讲的非常含糊。事实的确如此。让我们回顾一下分类算法，我们的数据应该是有若干个特征，落到我们的问答对中应该就至少是 <code>Question</code> 和 <code>Answer</code>（在实际操作时发现<code>Knowledge_Point</code> 和 <code>Tag</code> 对于效果并没有什么影响）。这也就意味着我们的数据集至少得有两个特征。但是我目前还没有找到在向量化后实现多个特征的方法。那么经过与同学间的交流，我发现同学们都是采用的讲 <code>Question</code> 和 <code>Answer</code> 拼成一个句子，然后当做一个向量来进行后续操作的。那么与多个特征相比究竟效果如何呢？这依旧有待进一步的探索。</p>
<h3 id="代码的处理">代码的处理</h3>
<p>在问题和回答中，都可能出现代码块。而代码和我们平时使用的语言又有所不同。比如同样效果的代码变量名称可能差别很大，也可能递归和循环看上去完全不同，但实际上是一样的效果。以上其实是我的猜测。但在我们的处理中，我们是直接无视了代码相对的特殊性，直接一股脑处理的。但一种可能更好的方案则是将其处理为一个特殊的 token，再进行训练。但很遗憾，对于给定的数据，如何将代码从文本中分离出来（即知道哪段是代码）就难倒了我们。因此到目前对于这个问题我们还没有应对的办法。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[python 爬虫实录]]></title>
        <id>https://zsq259.github.io/post/python爬虫实录/</id>
        <link href="https://zsq259.github.io/post/python爬虫实录/">
        </link>
        <updated>2023-07-08T08:36:53.000Z</updated>
        <summary type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>爬虫这个词想必我们都不陌生，但它究竟是如何实现的？就让小编带大家看看吧！</p>
]]></summary>
        <content type="html"><![CDATA[<p><a href="https://github.com/zsq259/PPCA_codemate">本项目仓库</a></p>
<p>爬虫这个词想必我们都不陌生，但它究竟是如何实现的？就让小编带大家看看吧！</p>
<!-- more -->
<h2 id="网络请求">网络请求</h2>
<p>当我们访问一个网页的时候，实际上是浏览器向对应网址解析后的 ip 的服务器发送了一些请求，然后通过获取的回复来构建出的页面。</p>
<h2 id="网页构成">网页构成</h2>
<p>了解一些 html，css，js 相关知识即可</p>
<p>而对于爬虫而言，还要了解一下 dom tree 和 xpath 等等，正则表达式也可</p>
<p>这样一来，我们的爬虫的思路就清晰了。我们可以在 python 程序中也向服务器发送请求，再根据响应来寻找我们需要的数据即可。而寻找数据则需要先人工分析对应网站的页面的 html 结构，找到需要的数据所在的位置。因此实际上爬虫大部分算是体力活。</p>
<h2 id="实战">实战</h2>
<h3 id="csdn">CSDN</h3>
<p>由于我们要爬取的是 QA 问答对的形式，并且为了保证质量，所以我们进入 CSDN 搜索关键字后，点击下方的<code>问答</code>并选中<code>已采纳</code>。接下来我们可以看到下面列出了若干个问题和最佳答案。我们点开控制台，进入 Network 项并四处翻找，可能会看到一个 search? q开头的包。进入 response 项，我们便能看到服务器给我们的响应了。可以看到这是一个 json 文件，是一个字典。再在里面翻找，我们会发现在<code>result_vos</code> 这项（也是一个字典）中有我们需要的更多的信息，比如单个问答的 url。复制打开后，我们会发现我们进入了单个问答的网页。因此，我们的思路就逐渐明确了。首先进入搜索后的页面得到刚刚这个包，再在响应中找到每个问答的网页，再进去继续获取具体的信息。</p>
<p>在 python 程序中如果我们发送和刚刚这个包一样的请求，得到的响应便也是我们看到的那样。在哪里看发送的请求长啥样呢？我们进入刚刚那个包的 Headers 项便能看到了。一点进去，在 General 出就有一个 Request URL，这个便是我们需要发送的请求。</p>
<p>接下来，我们再在单个问答的具体网页中获取问题和答案。再次进入控制台，在 Elements 项中找到点击标题，问题，答案等，可以发现它们在 html 中的位置。再在 Network 项中找到一串数字开头的包，发现其响应中就是我们的页面。我们在程序中获取这个响应，并以此构建 dom tree。此时就要用到 xpath 相关的知识了。我们刚刚在 Elements 页面探索了一番后，可以写出对应的 xpath，比如</p>
<pre><code>title = tree.xpath(&quot;//section[@class='title-box']/h1/text()&quot;)[0]
question = tree.xpath('//section[@class=&quot;question_show_box&quot;]//div[@class=&quot;md_content_show&quot;]//text()')
answer = tree.xpath('//section[@div=&quot;@class=answer_box&quot;]//div[@class=&quot;md_content_show&quot;]//text()')
</code></pre>
<p>就是标题，问题和答案描述的 xpath。于是，对于这一个问答网页，我们应该就能通过程序获取对应的问题和答案了。python，启动！</p>
<p>这里值得补充的一点是，在控制台的 Element 项中右键点击某个元素其实可以直接复制对应的 xpath，不过它是从根一个个节点一路下来的，可能会比较繁琐，而且遇到 tbody 这种还会出错，具体原因后面细说。</p>
<p>当我们启动程序后，我们大概会发现，title 和 question 确实都被爬下来了，但是 answer 却啥都没有。是我们的 xpath 写错了吗？检查几遍后发现没有。这时事情便变得奇怪起来。</p>
<p>让我们回顾一下我们刚刚爬虫的过程，这时细心的读者可能会发现，在第一次进入搜索到的页面的时候，我们是在 Network 的 response 中寻找需要的数据的，但是第二次却直接在 Elements 里面去找了。实际上这是作者在写爬虫的时候犯的一个错误。那么当我们尝试在第二次的具体问答页面的 response 中寻找时，我们会发现，问题和标题的确没什么区别，但是答案部分却不见了。而当我们尝试 <code>Ctrl+F</code>搜索答案中的某些字时，会发现它们是被套在一个 <code>script</code> 块内的函数中的，这意味着它是被动态渲染出来的。这样一来，我们便不能直接在收到的包中找到它。</p>
<p>此时便出现了僵局，一种通用的解决方法是使用 selenuim 或者类似的库，去模拟一个浏览器出来，先渲染一波，再在生成的页面去找。但对于 CSDN，还有一种更简单的方法。</p>
<p>我们回忆起，在刚搜到关键词，显示许多问答的页面时，每个问答的最佳答案是已经显示出来了的。此时，当我们回过头再去翻一翻那个页面获得的包，会惊讶地发现，在 <code>result_vos</code>对应的字典内，有关键字 <code>answer</code> 已经对应了最佳回答的全部文本。也就是说，我们可以在这个页面就获得答案。但可惜的是，这个页面的问题描述是显示不全的，所以依旧需要进入具体页面去爬取问题的标题和具体描述。</p>
<p>最后再总结一下我们爬取 CSDN 的思路：首先进入 CSDN 搜索关键词，进入到显示许多问答的页面，在这个页面获得每个问答的答案和显示单个问答的页面的 url，再进入到单个问答的页面获得问题描述。对于我们的程序，则是通过发送两次请求完成。</p>
<p>而我们要爬取的关键字自然不止一个。仔细观察搜索之后的页面的 url，可以发现在 <code>q=</code>后面的就是我们的关键字。于是我们可以先列出要爬的关键字列表，再用 python 的 <code>.format</code> 替换 url 中 <code>q=</code> 后面的部分。</p>
<p>最后是一个小小的细节：在第一次进入的页面中，一个收到的包里的问答是不全的。具体来说，当我们下拉网页并一边观察 Network 项中的 <code>search?q=</code> 开头的包，我们会发现这样的包会不断增加。也就是说，网页中显示的问答变多，实际上是发送了更多的请求获得了更多的包，以包含更多的问答。那怎么把这些包全部爬下来呢？我们选中不同的包，在 Payload 项中可以看到，它们的区别在于 <code>p=</code> 后面的数字不同。因此，对于一个关键词，我们改变 <code>p=</code>后面的数字，多发一些请求，就能把包都收到了。至此，我们爬取 CSDN 的过程就结束了。</p>
<h3 id="wikipedia">wikipedia</h3>
<p>首先，我们进入 <a href="https://zh.wikipedia.org/wiki/">https://zh.wikipedia.org/wiki/</a> 页面，在搜索框输入关键字跳转。多搜几个关键字后，可以发现情况有两种：一种是输入关键字后直接进入了一个具体的词条的页面，比如搜索 <code>斐波那契数</code>；另一种则是进入的页面中列举了许多词条，需要我们进一步点击进入对应词条的页面，比如 <code>大O表示法</code>。对于第一种情况，我们直接进一步处理即可；而对于第二种，我目前采取的策略则是选择进入搜索出来的第一个词条再进一步处理。找到并进入第一个词条的方法则比较简单，分析网页结构再发送一次请求即可，想必在爬完 CSDN 后这已经不成问题。当然，这样的缺陷是可能第一个词条实际上和我们要搜索的东西毫不相关，也可能后面有更多的的词条的相关性更大。这则一方面是我们搜索的关键字的问题，另一方面，我们也可以考虑选择进入更多的词条再进一步处理（先把数据爬下来再说）。</p>
<p>而进入一个词条的页面后， wikipedia 上本没有问答的形式，因此我们需要手动将其设置为问答的形式。具体来说，比如我们来到了 <a href="https://zh.wikipedia.org/wiki/%E7%B4%A0%E6%80%A7%E6%B5%8B%E8%AF%95">素性测试</a> 的页面，那么可以将“什么是素性测试？”作为问题，对应的介绍作为答案。通过观察，可以发现 wikipedia 的页面由若干级标题构成，有一个页面的大标题（h1），和各部分的小标题 （h2,h3）等。对应标题下方则是具体介绍。那么我们的问题可以设计成类似于“什么是h1的h2的h3？”的形式，再在相邻的标题间寻找答案即可。</p>
<p>本以为事情将会非常简单地解决，可没想到，wikipedia 的页面远比我想象的复杂<s>nt</s>。最初，一个简单的思路是把各级标题的位置找到，这很好办，寻找 <code>@class='mw-headline'</code>即可。然后把两个标题之间的文本爬下来作为答案。一个来自于 lpr 同学的类似的思路则是利用 wikipedia 标题旁边的 <code>编辑</code> 字来找，并且可以通过其链接跳转到的页面获取文本，也非常方便。</p>
<p>但当我打开<a href="https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0">斐波那契数</a>的页面的时候，这个方法便出现了问题。原因是在 h3 标题<code>初等代数解法</code>下的各个步骤中，还用到了 h4 标题来表示步骤中的每一步。但实际上，问题到 <code>初等代数解法</code> 应该就已经需要作为一个最小的单位了。也就是说，按照上面的方法，我们会把 h4 标题<code>首先构建等比数列</code>单独作为一个问题爬下来，但实际上它应该是 <code>初等代数解法</code>中的一步。至此，一个问题是如何将标题区分开来，它究竟是一个问题，还是只是某一个步骤？</p>
<p>通过标题的等级来区分的办法并行不通。比如 <a href="https://zh.wikipedia.org/wiki/%E5%A0%86%E6%A0%88">堆栈</a> 的页面中，h4 依旧是作为一个独立的问题存在的。此时似乎陷入了僵局。但当我们对比 <a href="https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0">斐波那契数</a> 和  <a href="https://zh.wikipedia.org/wiki/%E5%A0%86%E6%A0%88">堆栈</a> 的页面时，或许会观察到，它们的最上方的<code>目录</code>的显示似乎有区别。斐波那契数的目录到 <code>初等代数解法</code> 后就是最后一级了，并没有包含 h4 的标题，而堆栈的目录中则是也列出了那些 h4 标题。此时，便自然能得出根据目录来寻找问题的想法。而在控制台模式中，仔细观察后，会发现目录框可以通过 <code>div[@id='toc']</code>获得。而在堆栈的页面中，它的上一级是 <code>div[@class=&quot;mw-parser-output&quot;]</code>，但在斐波那契数的页面中，二者之间还夹了一个 <code>div[@class=&quot;toclimit-3&quot;]</code>。根据字面意思，有理由怀疑这个标签是用来限制目录大小的。而将其中的 <code>3</code> 改成 <code>4</code> 后，果然本来没有显示的 h4 标题也显示在目录中了。至此，通过这个标签的有无和其中的数字，我们便可以获得目录中的所有关键词，也就是一个问题的最小单位。</p>
<p>此时还有一个小细节：如果我们获取的是目录中的文本内容的话，依旧会得到 <a href="https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0">斐波那契数</a>页面的制裁。它的目录中有一个 <code>模n的周期性</code>的关键字，而在 html 的中，却变成了<code>模&lt;i&gt;n&lt;/i&gt;的周期性</code>。因此使用 text() 的话，则会被拆成三个，这也不好区分了。此时再一次陷入僵局。再次观察目录的成分后，发现上方还有一个 <code>&lt;a href=&quot;#模n的週期性&quot;&gt;</code>。在 <code>href</code> 属性中这个词是完整的。因此，我们获取这里的词即可，而这写成 xpath 就是<code>//div[@id='toc']/ul/li/a/@href</code>。当然，在后面的处理中还要去掉最前面的 <code>#</code>。</p>
<p>在将我们需要的标题的关键词都找到后，便可以去寻找对应的答案了。这里我的想法是，找到下一个和它同级的标题，再把中间的文本都爬下来作为答案。这样做的一个好处是，对于 h2 ，它的回答便能包括它的介绍里面的所有 h3 等更低级的标题。当然，这里要特判一下处于最后的情况，比如一个 h2 下的最后一个 h3 ，它后面可能是另一个 h2，此时就不能找下一个 h3 了，而是下面的 h2，或者直接到了页面的结尾。</p>
<p>本以为对 wikipedia 的爬取就到此为止了，然而，在爬取的过程中，却又出现了一些神奇的页面。某个页面的 h2 标题下紧接着的是 h4 而非 h3，因此判断标题的时候不能直接判断相邻的等级，而需要考虑所有的。另外一个特殊情况是，在 <a href="https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%BC%AF%E8%B7%9D%E9%9B%A2">编辑距离</a> 页面中，压根没有目录。这时就要使用最开始的方法直接把所有标题爬下来处理了。</p>
<h2 id="并行爬虫">并行爬虫</h2>
<p>参考 <a href="https://cuiqingcai.com/202271.html">https://cuiqingcai.com/202271.html</a></p>
<p>在我们之前的爬虫过程中，会发现，搜索许多关键字的话，爬虫的运行时间将非常久。有没有优化的方法呢？自然是有的。我们的爬虫在发送请求后，需要等待服务器的回复。而在我们的朴素爬虫中，我们只能在那干等。而并行爬虫的基本原理就是在等待响应的时候去做别的事情，比如发送其他的请求。这样，如果一个网站必须在 5 秒后返回响应，那么我们 10 个请求本来需要等待 50 秒，但并行化后可以直接发出 10 个请求，在等待 5 秒后，所有的请求都得到了响应。</p>
<p>而这一点可以基于 python 的 asyncio 库通过协程实现。上面的参考链接中其实已经讲的很清楚了，这里就不再赘述。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[从 0 开始的 RIEC-V 模拟器]]></title>
        <id>https://zsq259.github.io/post/从0开始的RISC-V模拟器/</id>
        <link href="https://zsq259.github.io/post/从0开始的RISC-V模拟器/">
        </link>
        <updated>2023-06-20T07:44:09.000Z</updated>
        <summary type="html"><![CDATA[<p>本项目仓库地址：<a href="https://github.com/zsq259/RISC-V">https://github.com/zsq259/RISC-V</a>。</p>
]]></summary>
        <content type="html"><![CDATA[<p>本项目仓库地址：<a href="https://github.com/zsq259/RISC-V">https://github.com/zsq259/RISC-V</a>。</p>
<!-- more -->
<h2 id="一级流水">一级流水</h2>
<p>RISC-V 模拟器是一个用 c++ 模拟 cpu 来实现 RV32I 指令集的 PPCA 大作业。本文将从 0 开始介绍作者在 PPCA 期间对此的实现。</p>
<p>这个作业，以我目前的认识来说，就是用 c++ 代码去模拟一个 cpu，接受一系列的指令（输入数据）然后大模拟，再返回数据。</p>
<p>由于模拟的是 cpu 的硬件，所以首先需要了解 cpu 的硬件架构，可以参考 <a href="https://www.bilibili.com/video/BV1EW411u7th">计算机科学速成课</a> 的第五集到第九集。也可以参考<a href="https://shimo.im/docs/PJAUY30F1uYksv0h/read">速成课的速通笔记</a>。</p>
<p>然后就可以去看 RV32I 的指令集了（反正我是这个顺序）。这里感谢 crm 已经整理了部分需要的指令集**<a href="https://mirage-cast-408.notion.site/RISC-5a61df89b3484c62b8a981f9cf211e1d">这里</a><strong>，同时也可以在线查找</strong><a href="https://msyksphinz-self.github.io/riscv-isadoc/html/rvi.html#lw">这里</a>**。</p>
<p>当然，一开始看到这个表的时候我是一脸懵的，完全看不懂，所以这里解释一下：</p>
<figure data-type="image" tabindex="1"><img src="https://zsq259.github.io/post-images/RS32I-1.png" alt="RS32I" loading="lazy"></figure>
<p>首先这张图表面了六大类指令的格式。最上方的数字是32位二进制数的位置。 opcode 表示指令的大类，rd 表示 目标寄存器，funct3 和 funct7 表示在大类里面细分时的依据，rs1 和 rs2 则是作为参数的寄存器，imm 表示立即数，imm[4:1] 表示32位二进制数的这几位所对应的是立即数的第4位到第1位。是的，可以发现立即数被拆成了好几个部分，相当于还要在解码的时候再拼起来。我尝试搜索了其原因，但是以我现在的知识无法理解。</p>
<figure data-type="image" tabindex="2"><img src="https://zsq259.github.io/post-images/RV32I.png" alt="" loading="lazy"></figure>
<p>然后这张图就是所有指令的参数了。同时类似于 imm[12|10:5] 则是表示立即数的 12 位以及 10 位到 5 位，没错还是奇怪的拆开。</p>
<p>而对于每个指令的具体操作，上面给出的参考链接里已经有了一部分解释。而似乎更全的解释在 RISC-V 手册的附录可以查找。</p>
<p>以及，更普遍地能看到的一种描述指令的方式其实是，比如 <code>add x1 x2 x3</code>，这里 <code>rd</code> 是最前面的，<code>rs1</code> 和 <code>rs2</code> 是后面的两个。似乎默认的都是这样写的，我也不清楚为什么。</p>
<p>在了解指令后，让我们把目光放到输入数据上（面向数据（雾））：</p>
<pre><code>#include &quot;io.inc&quot;

int main() {
  printInt(177);
  return judgeResult; // 94
}

</code></pre>
<p>这是 <code>sample.c</code>，是编译之前的源代码。~~实际上我们并不用管它。~~最后 return 后的注释则是我们需要输出的结果。</p>
<pre><code>@00000000
37 01 02 00 EF 10 00 04 13 05 F0 0F B7 06 03 00 
23 82 A6 00 6F F0 9F FF 
@00001000
37 17 00 00 83 27 C7 06 33 45 F5 00 13 05 D5 0A 
23 26 A7 06 67 80 00 00 83 47 05 00 63 82 07 02 
37 17 00 00 83 26 C7 06 B3 C7 D7 00 93 87 97 20 
23 26 F7 06 13 05 15 00 83 47 05 00 E3 94 07 FE 
67 80 00 00 13 01 01 FF 23 26 11 00 13 05 10 0B 
EF F0 1F FB B7 17 00 00 03 A5 C7 06 83 20 C1 00 
13 01 01 01 67 80 00 00 
@00001068
FD 00 00 00 
</code></pre>
<p>这是下发的 <code>sample.data</code>。而在我们实现的时候，我们是先直接把它全部读入到我们模拟的内存中。<code>@00000000</code>意味着内存中的地址。后面的十六进制数则是指令。每次读四个，前面读进来的其实是二进制数的低位，然后再解码成指令。</p>
<pre><code>
./test/test.om:     file format elf32-littleriscv


Disassembly of section .rom:

00000000 &lt;.rom&gt;:
   0:	00020137          	lui	sp,0x20
   4:	040010ef          	jal	ra,1044 &lt;main&gt;
   8:	0ff00513          	li	a0,255
   c:	000306b7          	lui	a3,0x30
  10:	00a68223          	sb	a0,4(a3) # 30004 &lt;__heap_start+0x2e004&gt;
  14:	ff9ff06f          	j	c &lt;printInt-0xff4&gt;

Disassembly of section .text:

00001000 &lt;printInt&gt;:
    1000:	00001737          	lui	a4,0x1
    1004:	06c72783          	lw	a5,108(a4) # 106c &lt;__bss_end&gt;
    1008:	00f54533          	xor	a0,a0,a5
    100c:	0ad50513          	addi	a0,a0,173
    1010:	06a72623          	sw	a0,108(a4)
    1014:	00008067          	ret

00001018 &lt;printStr&gt;:
    1018:	00054783          	lbu	a5,0(a0)
    101c:	02078263          	beqz	a5,1040 &lt;printStr+0x28&gt;
    1020:	00001737          	lui	a4,0x1
    1024:	06c72683          	lw	a3,108(a4) # 106c &lt;__bss_end&gt;
    1028:	00d7c7b3          	xor	a5,a5,a3
    102c:	20978793          	addi	a5,a5,521
    1030:	06f72623          	sw	a5,108(a4)
    1034:	00150513          	addi	a0,a0,1
    1038:	00054783          	lbu	a5,0(a0)
    103c:	fe0794e3          	bnez	a5,1024 &lt;printStr+0xc&gt;
    1040:	00008067          	ret

00001044 &lt;main&gt;:
    1044:	ff010113          	addi	sp,sp,-16 # 1fff0 &lt;__heap_start+0x1dff0&gt;
    1048:	00112623          	sw	ra,12(sp)
    104c:	0b100513          	li	a0,177
    1050:	fb1ff0ef          	jal	ra,1000 &lt;printInt&gt;
    1054:	000017b7          	lui	a5,0x1
    1058:	06c7a503          	lw	a0,108(a5) # 106c &lt;__bss_end&gt;
    105c:	00c12083          	lw	ra,12(sp)
    1060:	01010113          	addi	sp,sp,16
    1064:	00008067          	ret

Disassembly of section .srodata:

00001068 &lt;Mod&gt;:
    1068:	00fd                	addi	ra,ra,31
	...

Disassembly of section .sbss:

0000106c &lt;judgeResult&gt;:
    106c:	0000                	unimp
	...

Disassembly of section .comment:

00000000 &lt;.comment&gt;:
   0:	3a434347          	fmsub.d	ft6,ft6,ft4,ft7,rmm
   4:	2820                	fld	fs0,80(s0)
   6:	29554e47          	fmsub.s	ft8,fa0,fs5,ft5,rmm
   a:	3820                	fld	fs0,112(s0)
   c:	332e                	fld	ft6,232(sp)
   e:	302e                	fld	ft0,232(sp)
	...

</code></pre>
<p>这是 <code>sample.dump</code>，可以说是对上面输入的解释，第一列代表内存的位置，后面可以清楚的看到对应的指令和参数。当然有一些细节部分我也没有深入追究。</p>
<p>细心的读者可能会发现，在 <code>.dump</code> 文件中，关于指令的名称和操作数似乎有一些令人迷惑的地方。首先，这是因为有一些指令实际上是等价于另外一些指令的。比如说，<code>beqz</code> 这个指令在表中就没有出现过，而它就等价于  <code>beq</code> 的一个寄存器为 <code>x0</code>，再与另一个寄存器的值比较。<code>bnez</code> 也是同理。除此之外，还有 <code>li</code>，<code>ret</code>等，具体也可以参考 RISC-V 手册。其次，一个小小的细节是 <code>.dump</code> 文件中的操作数使用的是寄存器的别名，比如说 <code>sp</code>，<code>a0</code>等等，关于这个也可以去看 <a href="https://mirage-cast-408.notion.site/RISC-5a61df89b3484c62b8a981f9cf211e1d">crm的笔记</a>。</p>
<p>当我们掌握所有的指令后，我们就可以动手写一个一级流水的笨蛋模拟器了。用 c++ 模拟各个硬件，处理每个指令即可。当然，还有一些小小的细节值得注意（我自己写的时候遇到的）：</p>
<ul>
<li>对于那些会改变 pc 的指令，如果触发了 pc 的跳转，那么就不会再自动 pc+4 了。</li>
<li><code>x0</code> 寄存器的值永远都是 0，哪怕有指令试图将其改变也是无效的。</li>
<li>注意小端序和大端序的区分。</li>
</ul>
<p>到此，至少本人已经用一级流水能跑模拟器了。（虽说模拟地并不完全<s>并且第一次提交的时候忘记删debug输出导致stdout挤爆了然后oj炸了</s>）</p>
<p>当然，写一个一级流水的模拟器并不是必要的，并且对于接下来马上提到的 Tomasulo 算法而言似乎并不能很好地进行代码复用。（或许还是可以作为辅助调试的方法吧（雾））</p>
<h2 id="tomasulo">Tomasulo</h2>
<p>当然，光有笨蛋的一级流水可是不行的呐，毕竟有些情况下性能会比较低。比如说以下情况：</p>
<pre><code>add x1 x2 x3
sub x10 x1 x4
add x5 x6 x7
</code></pre>
<p>那么当第一行的指令在执行的时候，理论上第三行的指令是完全不受影响的，但是还是得老实等在那，被卡住了。所以，之后要了解的 Tomasulo 算法便是能够乱序执行以提高效率的手段。</p>
<p>在进一步了解 Tomasulo 之前，让我们回顾一个概念：时钟周期。回头看一下我们的一级流水，我们会发现其实它并没有太与时钟周期挂钩，因为一直都是读入指令-&gt;解析指令-&gt;处理指令来驱动的。但是 Tomasulo 则不然，其后将提到的各个元件和乱序执行等将会<s>以及作业要求</s>提高我们对于时钟周期的要求。因此在处理之前把这个问题理清楚是有必要的。</p>
<h3 id="时钟周期">时钟周期</h3>
<p>我们知道，cpu 里实际上是以时钟来驱动运行的。在一个时钟内，各个元件都并行地进行“一步”操作。当然，在我们这个作业中也不需要更加细致地深入了解其物理原理和硬件实现。但是由于 c++ 模拟只能串行不能并行，所以对于时钟的模拟显得比较抽象<s>nitian</s>。具体来说，我们设一个变量 clock 代表时钟，然后每过一个周期就 clock++ <s>已经开始抽象了</s>。那么在我们手动增加 clock 的间隔中，便可以认为其中运行的程序都是在一个周期内并行的。这也意味着在一个时钟周期内其中运行的元件可以以任意顺序执行。</p>
<p>要实现这一点的话，一个简单而泥潭的方法是对于每个通用寄存器（可以理解为所有东西）都存一个当前的状态和下一时刻的状态，然后再在每个周期末将“下一时刻”更新至“当前时刻”。</p>
<p>而对于一个时钟周期内具体能干什么事情，则是一个更加抽象<s>nitian</s>的问题。 我目前了解到的也比较模糊。目前有（有些涉及到具体的元件目前还没讲）：</p>
<ul>
<li>fetch （取指令）需一个周期</li>
<li>decode （解码）需一个周期</li>
<li>issue (发射) 可以和 decode 在一个周期一起进行也可以分开</li>
<li>在 reservation station 里，一个周期内能执行的指令取决于 ALU 的个数（如果ALU足够多可以全执行）</li>
</ul>
<h3 id="思想">思想</h3>
<p>让我们回到 Tomasulo 上来，在上面的例子中，第三行的 <code>add</code> 明显是可以在第一行的指令执行的同时执行的（如果有多个 ALU 并行的话），但是第二行的 <code>sub</code> 必须等第一行执行完后才能执行，否则会有 RAW（read after write） 问题。那么这里就体现了一些依赖关系，某些指令必须在一些指令之后执行。而这个关系能够比较自然地让我们联想到拓扑序。实际上，Tomasulo 采取的也是类似的方法：记录每个指令所依赖的指令，待其依赖的指令执行完毕，操作数准备好之后再执行。</p>
<p>同时，Tomasulo 还有一个概念：寄存器重命名。这是一种类似于缓存的思想。在前面的一级流水中，我们是在处理指令的时候，直接在寄存器上操作。但是，当一条指令的操作数所需的寄存器没有被占用时，我们可以直接将操作数取出来并存放起来，这样这条指令就与其操作数所需的寄存器没有关系了。在之后的 Reservation station 部分，我们将详细讨论这一部分。</p>
<h3 id="架构">架构</h3>
<p>我的 Tomasulo 的架构参考的是 Computer Architecture:A Quantitative Approach（计算机体系结构：量化研究方法）第 3.4 章到第 3.6 章的内容，并加上个人的理解<s>魔改</s>。以下是架构图：</p>
<p>接下来将对各个元件进行介绍。</p>
<h4 id="instruction-unit"><strong>Instruction unit</strong></h4>
<p>这个部分从内存取出指令（fetch），进行解码（decode）再发射（issue）到 RoB。<s>没什么好说的</s></p>
<h4 id="reservation-station保留站-rs"><strong>Reservation Station（保留站 RS）</strong></h4>
<p>前面提到了 Tomasulo 的思想是根据依赖来执行指令和寄存器重命名。在保留站中我们将看到它具体的工作。首先，（我的）保留站的一个元素有以下内容：</p>
<ul>
<li>busy：在执行 or 执行完毕</li>
<li>op：指令类型</li>
<li>vj：第一个操作数</li>
<li>vk：第二个操作数</li>
<li>qj：第一个操作数所在的寄存器依赖的指令在 RoB （之后会提到）中的编号，如果为 0 说明无依赖可以执行</li>
<li>qk：第二个操作数所在的寄存器依赖的指令在 RoB 中的编号（同 qj）</li>
<li>dest：这条指令在 RoB 中对应的位置</li>
</ul>
<p>而 RS 实际上是一个类似于表格的形式：</p>
<table>
<thead>
<tr>
<th>busy</th>
<th>op</th>
<th>vj</th>
<th>vk</th>
<th>qj</th>
<th>qk</th>
<th>dest</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p><s>由于没有例子我就不往里面填东西了</s></p>
<p>而在这些元素中，值得关注的就是 vj, vk 和 qj, qk。首先看到 vj, vk，它们就相当于寄存器重命名，把本来在寄存器中的操作数存在了 RS 中。这样一来，这条指令在读完操作数后就与寄存器没有关系了。而 qj, qk   则维护了指令操作数所在的寄存器的依赖关系。当 qj, qk 均为 0 的时候，代表这条指令已经没有依赖，操作数准备完毕，可以执行了。反之，则意味着对应的寄存器在前面还有指令尚未执行完毕，因此需要等待。而此时 qj, qk 则就设置为 占用对应寄存器的指令在 RoB 中的编号。而什么时候获得这个值呢？我们将在后面 RoB 的部分提到。</p>
<h4 id="register-file"><strong>Register File</strong></h4>
<p>这个也被称为 qi，记录的是某一寄存器目前是被 RoB 中的哪个指令占用的，能够使 RS 获得 qj, qk。</p>
<h4 id="load-and-store-buffer"><strong>Load and Store Buffer</strong></h4>
<p>如果熟悉指令的话，我们会发现有两类指令<code>load</code>和<code>store</code>的特点是要依靠内存操作。这就不像寄存器一样可以记录依赖了，毕竟内存那么大嘛。而且读写内存也是连续的几个字节。所以对于这两类指令，一种可能的做法是，使这两种指令顺序执行。稍微优化一点的话，可以发现如果 <code>load</code>指令前没有 <code>store</code> 指令的话就可以执行了，而 <code>store</code> 指令前面必须没有 <code>load</code> 或 <code>store</code> 指令才能执行。基于这种特性，我们能够提出一种更加简单的架构。<s>但是我摆了所以如果有机会再改或者介绍</s></p>
<h4 id="reorder-buffer重排序缓冲区-rob"><strong>Reorder Buffer（重排序缓冲区 RoB）</strong></h4>
<p>可以先思考一下，光有前面的结构，是不是也能跑了？答案是是的。</p>
<p>但是存在一个问题，当遇到条件跳转指令的时候，由于乱序执行，必须等条件跳转指令所需的寄存器依赖消除后，才能继续计算应该跳转的分支。所以在此之前 fetch 和 decode 都会停止。这自然是不太优雅的。因此一个可能的手段是<strong>分支预测</strong>。预测的方式将在后面介绍。但是分支预测又会带来一个问题：预测错误之后的补救。如果每条指令都是直接修改寄存器的值的话，在预测错误后执行的那些指令带来的影响将难以消除。因此，我们有了 RoB。正如其名字，它将乱序执行后的指令重新按照发射的顺序排序。它使用了循环队列的结构，也就意味着先进先出。在指令发射后，它就进入 RoB。在执行完后，指令并不直接修改寄存器，而是将指令需要修改的寄存器编号（或内存位置）（dest）和对应的值（value）记录在 RoB 中。而每个周期检测队首的指令是否已经执行完毕，如果执行完毕的话，就按照记录修改寄存器或内存，这一过程也称为提交（commit）。这样一来，乱序执行完毕后的结果将按照顺序来修改寄存器。</p>
<p>而在上文提到的寄存器依赖中，当新发射的指令依赖的寄存器被某个已经执行完毕的指令占用，却还没有在队首被提交的话，那么这时就直接从 RoB 中获取所需的操作数即可。</p>
<h4 id="分支预测器"><strong>分支预测器</strong></h4>
<p>可以参考<a href="https://zh.wikipedia.org/wiki/%E5%88%86%E6%94%AF%E9%A0%90%E6%B8%AC%E5%99%A8">wikipedia上的分支预测器介绍</a>。</p>
<p>我的实现是，对 pc 取6位进行 hash （3到8位），再对 hash 的值使用四级自适应预测器，再在里面套一层二位饱和预测器。具体的实现方式可以看 wiki，也比较简单，这里就不详细展开。</p>
<h3 id="一些细节">一些细节</h3>
<p>对于 pc 指针的移动是一个需要注意的地方，稍有不慎， fetch 和 decode 就会乱掉。这里稍微讨论一下我的实现。</p>
<p>首先，需要更改 pc 的值的指令有 B 型指令，J 型指令和 jail 指令。同时，正常情况下 pc 会自动 +4。那么同样地，在不正常的情况下，如指令发射失败，也是需要注意的地方。以及，在第一个周期，只有 fetch 指令运行而没有 decode 也是需要注意的地方。</p>
<p>对于 B 和 J 型指令，我们在解码之后立即能得到 pc 需要跳转的位置（B 型通过预测），但同一周期 fetch 到的指令则是无效的。因此在下一个周期 decode 的时候处理的指令也是无效的，需要跳过。</p>
<p>而对于 jail ，pc 之后要跳转的位置是难以预测的<s>所以就不预测了</s>。因此，在 jail commit 之前，fetch 和 decode 都需要停掉，一直等到计算出需要跳转的位置为止。</p>
<p>对于发射失败而言，下一个周期需要继续发射这条指令。更优化的方法是采取 instruction queue 和 多发射的手段来解决这个问题，但是那需要更深入的理解<s>摆了</s>，因此这里讨论的是一种比较粗暴的方法。</p>
<p>在简单分析了这几种情况之后，就可以开始具体的讨论了。首先，在正常情况下，pc 会自动 +4 的操作似乎有两种选择，一种是在 fetch 之后，另一种则是在 decode 之后。由于第一个周期没有 decode ，因此我选择的是第一种。（事实上我不清楚真正的架构是如何实现的）</p>
<p>现在来看，对于 B 和 J ，似乎没有停掉 fetch 的必要。下个周期的 fetch 能够直接从修改后的 pc 处读入。但是下个周期的 decode 则是需要中断，因为指令失效了。对于 jalr 则是整个都中断了。对于发射失败的话，下个周期 decode 则是继续发射这个周期的指令，而下个周期的 fetch 就要停掉。但是注意到，这个周期的 fetch 已经读入下个周期需要 decode 的指令了，这个指令则需要延后<s>在我这里就是失效了</s>。而由于要求硬件并行，也就是 c++模拟时各个函数可以乱序执行，那则是有可能在 decode 之后才执行 fetch ，所以并不能直接在 decode 里面直接改下一个周期的指令，而是需要置一个 tag 表示下个周期的指令应该修改。</p>
<h2 id="感谢">感谢</h2>
<p>感谢 <a href="https://darksharpness.github.io/">DarkShrapness</a> ，<a href="https://www.wankupi.top/">Wankupi</a> 等人的帮助，对于我对这个作业的理解和思考提供了很大的帮助。</p>
]]></content>
    </entry>
</feed>