<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Jack Zhu</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>http://jackzhu.top/</id>
  <link href="http://jackzhu.top/" rel="alternate"/>
  <link href="http://jackzhu.top/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Jack Zhu</rights>
  <subtitle>不乱于心，不困于情，不念过往，不畏将来。</subtitle>
  <title>Jack's Blog</title>
  <updated>2026-06-10T08:01:07.567Z</updated>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="Agent实践" scheme="http://jackzhu.top/categories/Agent%E5%AE%9E%E8%B7%B5/"/>
    <category term="Agent, Hermes, PassiveAgent, AI工具, 工程实践" scheme="http://jackzhu.top/tags/Agent-Hermes-PassiveAgent-AI%E5%B7%A5%E5%85%B7-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/"/>
    <content>
      <![CDATA[<p>最开始做 PassiveAgent 的时候，我其实一直有一个疑问：如果只是写代码，用 Codex 和 Claude Code 不就够了吗？为什么还要把 Hermes 放进这个开发过程里？</p><p>这个问题很真实。因为在今天，写一个 Python CLI、补几个模块、改几个 bug、加一些测试，Codex &#x2F; Claude Code 确实已经非常强了。甚至可以说，如果目标只是“把代码写出来”，Hermes 并不是最核心的工具。</p><p>但 PassiveAgent 做到初具规模之后，我对这个问题的答案逐渐清晰了：</p><blockquote><p>Codex &#x2F; Claude Code 更像施工队，负责把一个个功能实现出来；Hermes 更像这个项目的长期操作系统，负责记住项目、编排工具、沉淀经验，并让这个系统在真实环境里长期活下去。</p></blockquote><p>这篇文章想分享的，不只是 PassiveAgent 这个项目本身，也包括我在用 Hermes 开发它的过程中，对 Agent 工具分工、自进化、长期个人系统的一些体会。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/passiveagent-hermes-cover.svg" alt="Codex 造零件，Hermes 养系统"></p><hr><p>这篇文章可以按两条线看：一条线是 PassiveAgent 作为个人注意力调度系统的项目介绍；另一条线是 Hermes 在这个项目中扮演的角色——它不是替代 Codex&#x2F;Claude Code，而是把一次性 coding 能力放进长期可记忆、可验证、可演化的工程闭环里。</p><hr><h2 id="PassiveAgent-是什么"><a href="#PassiveAgent-是什么" class="headerlink" title="PassiveAgent 是什么"></a>PassiveAgent 是什么</h2><p>PassiveAgent 是我给自己做的一个“个人注意力调度系统”。</p><p>现在资料太多了：Zotero 里有论文，Obsidian 里有摘录和临时链接，GitHub Stars 里有项目，HuggingFace Daily Papers 每天也有新论文。真正的问题不是“没有资料”，而是每天不知道应该先看哪几个。</p><p>PassiveAgent 要解决的问题就是：</p><blockquote><p>每天从多个信息池里挑出少量真正值得看的内容，摘要、打分、排序，然后推送给我。</p></blockquote><p>它的基础流程大概是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Zotero / Obsidian / HuggingFace Daily / GitHub Stars</span><br><span class="line">        ↓</span><br><span class="line">采集与标准化</span><br><span class="line">        ↓</span><br><span class="line">去重</span><br><span class="line">        ↓</span><br><span class="line">LLM 摘要与多维度打分</span><br><span class="line">        ↓</span><br><span class="line">Rank Top N</span><br><span class="line">        ↓</span><br><span class="line">飞书推送 / CLI 查看</span><br><span class="line">        ↓</span><br><span class="line">看、忽略、加入周末、生成面试卡或技术笔记</span><br><span class="line">        ↓</span><br><span class="line">反馈影响后续推荐</span><br></pre></td></tr></table></figure><p>它适合的场景很明确：不是替我刷信息流，而是替我减少信息流；不是每天推一堆“可能有用”的东西，而是只推少量和当前目标高度相关的内容。</p><p>当前我的主要目标是 Agent &#x2F; LLM &#x2F; 算法工程方向的学习和面试准备，所以 PassiveAgent 的打分逻辑会围绕这些主题进行。</p><hr><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/passiveagent-hermes-architecture.svg" alt="PassiveAgent 架构：从资料池到注意力调度"></p><h2 id="项目结构：一个小型但完整的-Agent-应用"><a href="#项目结构：一个小型但完整的-Agent-应用" class="headerlink" title="项目结构：一个小型但完整的 Agent 应用"></a>项目结构：一个小型但完整的 Agent 应用</h2><p>PassiveAgent 不是一个单文件脚本，而是一个完整的小型 Agent 应用。当前项目结构大致是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">PassiveAgent/</span><br><span class="line">├── config.yaml.example      # 配置模板</span><br><span class="line">├── .env.example             # 环境变量模板，不提交真实密钥</span><br><span class="line">├── prompts/                 # LLM prompt 模板</span><br><span class="line">├── docs/                    # 配置、飞书、定时任务、排障文档</span><br><span class="line">├── scripts/                 # launchd 安装脚本和 plist 模板</span><br><span class="line">├── src/passive_agent/</span><br><span class="line">│   ├── main.py              # CLI 入口</span><br><span class="line">│   ├── pipeline.py          # 每日处理流水线</span><br><span class="line">│   ├── collectors/          # 数据采集器</span><br><span class="line">│   ├── processors/          # 去重、摘要、打分、排序</span><br><span class="line">│   ├── actions/             # 用户操作处理</span><br><span class="line">│   ├── feishu/              # 飞书 Bot、卡片、回调、命令</span><br><span class="line">│   ├── integrations/        # LLM、Obsidian、Zotero 等外部集成</span><br><span class="line">│   └── storage/             # SQLite 数据库和模型</span><br><span class="line">└── tests/</span><br></pre></td></tr></table></figure><p>技术栈比较朴素：Python 3.11+、SQLite、Click、Jinja2、OpenAI-compatible LLM API、飞书开放平台。它没有复杂的 Web 前端，也没有刻意追求“平台化”，核心目标就是每天稳定地帮我筛选内容。</p><p>配置集中在 <code>config.yaml</code>，大致分成 8 类：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">runtime:</span>          <span class="comment"># 数据库、报告、prompt 路径</span></span><br><span class="line"><span class="attr">llm:</span>              <span class="comment"># LLM provider、model、base_url、并发、重试</span></span><br><span class="line"><span class="attr">goals:</span>            <span class="comment"># 当前目标、优先级主题、低优先级主题</span></span><br><span class="line"><span class="attr">sources:</span>          <span class="comment"># Zotero / Obsidian / GitHub Stars / HF Daily</span></span><br><span class="line"><span class="attr">display:</span>          <span class="comment"># dashboard、推送、报告展示数量</span></span><br><span class="line"><span class="attr">recommendations:</span>  <span class="comment"># 推荐生命周期与关联条目数量</span></span><br><span class="line"><span class="attr">feishu:</span>           <span class="comment"># 飞书异步超时等</span></span><br><span class="line"><span class="attr">scoring:</span>          <span class="comment"># 打分权重、daily_limit、负反馈降权</span></span><br></pre></td></tr></table></figure><p>这样做的好处是：这个项目不是“我自己电脑上碰巧能跑的脚本”，而是一个可以解释、可以迁移、可以排障的系统。</p><hr><h2 id="怎么使用"><a href="#怎么使用" class="headerlink" title="怎么使用"></a>怎么使用</h2><p>最短路径其实很简单：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/CoderJackZhu/PassiveAgent.git</span><br><span class="line"><span class="built_in">cd</span> PassiveAgent</span><br><span class="line">uv <span class="built_in">sync</span></span><br><span class="line"><span class="built_in">cp</span> config.yaml.example config.yaml</span><br><span class="line"><span class="built_in">cp</span> .env.example .<span class="built_in">env</span></span><br><span class="line">uv run passive-agent init-db</span><br><span class="line">uv run passive-agent daily</span><br></pre></td></tr></table></figure><p>然后可以用这些命令查看结果：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">uv run passive-agent status</span><br><span class="line">uv run passive-agent dashboard</span><br><span class="line">uv run passive-agent list --stage recommended</span><br></pre></td></tr></table></figure><p>如果接入飞书，则可以主动推送：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uv run passive-agent feishu-push --stage recommended --<span class="built_in">limit</span> 5</span><br></pre></td></tr></table></figure><p>日常使用时，我更希望它不是一个“需要我主动打开的工具”，而是一个低打扰的系统：每天晚上定时跑一次，挑出 3 条左右值得看的内容推给我；周末再推一个适合深读的队列。</p><p>在飞书卡片里，我可以对每条内容做操作：</p><ul><li>展开详情；</li><li>加入周末阅读；</li><li>忽略并记录负反馈；</li><li>在展开后生成面试卡；</li><li>生成技术笔记；</li><li>标记已读。</li></ul><p>其中“面试卡”和“技术笔记”不是只在飞书里展示一下，而是会写入本地 Obsidian vault。例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">01-Interview/&#123;topic&#125;/&#123;title&#125;.md</span><br><span class="line">03-Tech-Notes/&#123;topic&#125;/&#123;title&#125;.md</span><br></pre></td></tr></table></figure><p>这点对我很重要。因为 PassiveAgent 不应该只是一个推荐器，它还应该把有价值的信息沉淀到我的知识系统里。</p><hr><h2 id="一个推荐样例"><a href="#一个推荐样例" class="headerlink" title="一个推荐样例"></a>一个推荐样例</h2><p>一个典型的推送条目会包含这些信息：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">标题：某篇关于 Agent 记忆机制 / Tool Use / RAG 评测的论文或文章</span><br><span class="line">来源：Zotero / HuggingFace Daily / Obsidian / GitHub Stars</span><br><span class="line">摘要：用几句话解释它解决的问题、核心方法和可能价值</span><br><span class="line">评分：目标相关性、新颖性、可操作性、难度匹配、来源质量、时效性</span><br><span class="line">推荐理由：为什么今天值得看</span><br><span class="line">可执行动作：展开 / 周末 / 忽略 / 生成面试卡 / 技术笔记</span><br></pre></td></tr></table></figure><p>例如，如果它发现一篇和 Agent memory 相关的论文，它不会只是告诉我“这篇论文很新”，而是会尝试回答：</p><ul><li>它和我当前准备 Agent 算法岗面试有什么关系？</li><li>它适合今天快速浏览，还是适合周末深读？</li><li>它能不能转成一道面试题？</li><li>它能不能沉淀成一张技术笔记？</li></ul><p>这也是我做 PassiveAgent 的初衷：不是追热点，而是让信息进入“学习—理解—沉淀”的链路。</p><hr><h2 id="那为什么不用-Codex-Claude-Code-就行？"><a href="#那为什么不用-Codex-Claude-Code-就行？" class="headerlink" title="那为什么不用 Codex &#x2F; Claude Code 就行？"></a>那为什么不用 Codex &#x2F; Claude Code 就行？</h2><p>回到开头的问题。</p><p>如果只是写 PassiveAgent 的某个模块，比如实现一个 HuggingFace Daily collector、写一个 Feishu card、修一个 SQLite 迁移 bug，Codex 和 Claude Code 完全够用，而且非常适合。</p><p>所以我并不认为 Hermes 的价值是“比 Codex 更会写代码”。这不是它的核心定位。</p><p>我现在更愿意这样理解它们的分工：</p><table><thead><tr><th>角色</th><th>更擅长什么</th></tr></thead><tbody><tr><td>Codex &#x2F; Claude Code</td><td>写代码、改 bug、重构、补测试</td></tr><tr><td>Hermes</td><td>长期上下文、工具编排、跨会话记忆、运行环境排障、经验沉淀</td></tr></tbody></table><p>Codex &#x2F; Claude Code 像施工队。你告诉它：“把这里修一下”“加一个功能”“重构一下这个模块”，它可以快速完成。</p><p>Hermes 更像项目的长期工作台。它知道：</p><ul><li>PassiveAgent repo 在哪里；</li><li>怎么跑测试；</li><li>怎么跑 daily pipeline；</li><li>飞书推送需要哪些权限；</li><li>launchd 有哪些坑；</li><li><code>.env</code> 和继承环境变量可能冲突；</li><li>Zotero 分类应该用 collection，而不是靠 tag 乱贴；</li><li>之前为什么没有推送；</li><li>之前哪些 bug 已经修过；</li><li>这些经验下次应该怎么复用。</li></ul><p>如果每次都从零开始，Codex&#x2F;CC 当然也能做。但一旦项目进入长期维护状态，“从零开始”本身就是成本。</p><p>Hermes 在这里提供的不是一次性智能，而是连续性。</p><hr><h2 id="Hermes-在开发过程里具体做了什么"><a href="#Hermes-在开发过程里具体做了什么" class="headerlink" title="Hermes 在开发过程里具体做了什么"></a>Hermes 在开发过程里具体做了什么</h2><h3 id="1-项目记忆：从“一个-repo”变成“一个长期对象”"><a href="#1-项目记忆：从“一个-repo”变成“一个长期对象”" class="headerlink" title="1. 项目记忆：从“一个 repo”变成“一个长期对象”"></a>1. 项目记忆：从“一个 repo”变成“一个长期对象”</h3><p>在开发 PassiveAgent 的过程中，Hermes 逐渐形成了一个项目级记忆：</p><ul><li>这个项目叫 PassiveAgent；</li><li>它是个人注意力调度系统；</li><li>核心输入源是 Zotero、Obsidian、HF Daily、GitHub Stars；</li><li>核心输出是飞书卡片和 Obsidian 笔记；</li><li>当前目标是 Agent &#x2F; LLM &#x2F; 面试准备；</li><li>运行方式是 CLI + macOS launchd + Feishu long connection；</li><li>配置和密钥不能混在一起；</li><li>文档要面向新用户，而不是只堆配置项。</li></ul><p>这些内容后来被沉淀到了专门的 <code>passive-agent-development</code> skill 里。于是之后我再问 PassiveAgent 相关问题时，Hermes 不需要重新探索一遍项目，它可以直接带着项目上下文开始工作。</p><p>这和普通聊天最大的区别是：项目不再是一段临时上下文，而是一个长期对象。</p><h3 id="2-工程排障：处理真实环境里的非代码问题"><a href="#2-工程排障：处理真实环境里的非代码问题" class="headerlink" title="2. 工程排障：处理真实环境里的非代码问题"></a>2. 工程排障：处理真实环境里的非代码问题</h3><p>PassiveAgent 真正麻烦的地方，很多都不是代码本身。</p><p>比如飞书：</p><ul><li>主动发消息需要 <code>im:message:send_as_bot</code> 或 <code>im:message</code>；</li><li>接收消息需要订阅 <code>im.message.receive_v1</code>；</li><li>卡片按钮需要 <code>card.action.trigger</code>；</li><li>权限或事件改完以后还要发布新版本，并在租户侧升级；</li><li><code>FEISHU_CHAT_ID</code> 必须来自机器人所在的真实目标聊天。</li></ul><p>这些不是“写一个函数”能解决的，它们是系统集成问题。</p><p>再比如 launchd：</p><ul><li>macOS 的 <code>~/Documents</code>、Desktop、Downloads 会受 TCC 保护；</li><li>user LaunchAgent 错过定时点后不会像 systemd <code>Persistent=true</code> 那样自动补跑；</li><li>环境变量来自 launchctl &#x2F; plist &#x2F; <code>.env</code>，不同执行路径可能不一致；</li><li>代理环境变量可能影响飞书长连接。</li></ul><p>这些问题如果只交给 coding agent，很容易变成“改了代码但实际没跑通”。Hermes 的优势是它能在一个会话里查日志、读配置、跑命令、结合历史经验，然后给出可验证的判断。</p><p>比如有一次 6.3 没有正常推送，最后定位不是 Feishu 失败，也不是 pipeline 报错，而是 Mac 在 21:00 定时点时用户 LaunchAgent 不在可执行状态，后来开机&#x2F;登录后 launchd 没有补跑错过的任务。</p><p>这个结论只有把日志、数据库记录、报告文件、launchd 状态和系统启动时间放在一起看，才比较可靠。</p><h3 id="3-工作流沉淀：从“改一下”到-audit-→-fix-→-verify-→-commit"><a href="#3-工作流沉淀：从“改一下”到-audit-→-fix-→-verify-→-commit" class="headerlink" title="3. 工作流沉淀：从“改一下”到 audit → fix → verify → commit"></a>3. 工作流沉淀：从“改一下”到 audit → fix → verify → commit</h3><p>开发过程中逐渐形成了一个固定流程：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">audit → fix → verify → commit</span><br></pre></td></tr></table></figure><p>先审查真实代码和日志，再定位根因；做最小修改；跑测试或真实命令验证；确认后提交。这个流程后来也成为我用 Hermes 处理 PassiveAgent 问题的默认方式。</p><p>这件事看起来普通，但很重要。</p><p>因为 Agent 工具最容易出问题的地方不是“不会写代码”，而是“太快给出看似合理的改动，但没有验证”。Hermes 在我的使用里承担了一个“完成前验证”的角色：不只是改完，还要确认它真的跑过。</p><h3 id="4-技能进化：把踩坑写回系统"><a href="#4-技能进化：把踩坑写回系统" class="headerlink" title="4. 技能进化：把踩坑写回系统"></a>4. 技能进化：把踩坑写回系统</h3><p>PassiveAgent 过程中踩过的坑，后来都被写进了 skill 或文档：</p><ul><li>LLM provider 从 DeepSeek 切到 OpenAI-compatible &#x2F; MiMo 时，不是只改 YAML，还要确认 LLM factory 是否支持；</li><li>WebUI 能用某个 provider，不代表 launchd daily job 也能用同一套环境；</li><li><code>python-dotenv</code> 默认不覆盖已经存在的环境变量，所以从 Hermes&#x2F;WebUI 里手动跑 PassiveAgent 时，可能继承到 Hermes 自己的飞书环境变量；</li><li>所有新采集内容都被去重后，也不能直接结束 pipeline，而应该从 existing recommendations 里继续推送；</li><li>Feishu 卡片上“面试卡”按钮和展开后的“生成面试卡”重复，会造成交互困惑，应该把生成动作放到详情页之后。</li></ul><p>这些经验如果只停留在一次对话里，很快就会消失。Hermes 的意义是把它们变成下一次可加载的 procedure memory。</p><hr><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/passiveagent-hermes-evolution-loop.svg" alt="Hermes 自进化闭环"></p><h2 id="我理解的“自进化”"><a href="#我理解的“自进化”" class="headerlink" title="我理解的“自进化”"></a>我理解的“自进化”</h2><p>这里的自进化不是说模型权重变强了，也不是说 Agent 突然能自己完成一切。</p><p>在 PassiveAgent 这个案例里，自进化更像是：</p><blockquote><p>每一次真实开发和排障之后，系统都会把经验写回自己的工作流。下一次面对类似问题时，它不再从零开始，而是带着上一次的经验继续工作。</p></blockquote><p>我觉得可以分成四层。</p><h3 id="第一层：记忆进化"><a href="#第一层：记忆进化" class="headerlink" title="第一层：记忆进化"></a>第一层：记忆进化</h3><p>一开始 Hermes 只知道“有个 PassiveAgent 项目”。后来它知道项目路径、运行方式、常见命令、部署方式、数据源、飞书交互、Zotero 分类习惯、用户目标和偏好。</p><p>这让后续维护不再是冷启动。</p><h3 id="第二层：技能进化"><a href="#第二层：技能进化" class="headerlink" title="第二层：技能进化"></a>第二层：技能进化</h3><p>项目中反复出现的操作，被沉淀成 skill：</p><ul><li>如何运行测试；</li><li>如何手动触发 daily；</li><li>如何排查飞书；</li><li>如何检查 launchd；</li><li>如何做 Zotero taxonomy dry-run；</li><li>如何迁移 LLM provider；</li><li>如何写面向用户的 README。</li></ul><p>Skill 本质上是 Agent 的操作手册。它不是知识库里的静态资料，而是下一次执行任务时会真的进入上下文的工作规程。</p><h3 id="第三层：流程进化"><a href="#第三层：流程进化" class="headerlink" title="第三层：流程进化"></a>第三层：流程进化</h3><p>从“帮我改一下”，变成“先审查、再修复、再验证、再提交”。</p><p>这其实是我对 Agent 工程最深的体会之一：Agent 要可靠，不能只靠更强的模型，还要靠更稳定的流程。</p><h3 id="第四层：系统进化"><a href="#第四层：系统进化" class="headerlink" title="第四层：系统进化"></a>第四层：系统进化</h3><p>PassiveAgent 自己也在这个过程中变得更成熟：</p><ul><li>配置从硬编码走向显式 YAML；</li><li>LLM 输出增加类型校验和兜底；</li><li>推荐逻辑支持无新增时从 existing pool 推送；</li><li>飞书按钮交互变清晰；</li><li>data 目录区分业务报告和运行日志；</li><li>README、配置文档、Feishu 文档、定时任务文档逐渐补齐。</li></ul><p>所以自进化不是玄学，而是一个很工程化的闭环：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">真实使用 → 暴露问题 → 修复问题 → 验证结果 → 沉淀经验 → 下一次更快更稳</span><br></pre></td></tr></table></figure><hr><h2 id="这个过程给我的几个感受"><a href="#这个过程给我的几个感受" class="headerlink" title="这个过程给我的几个感受"></a>这个过程给我的几个感受</h2><h3 id="1-不要把-Hermes-当“另一个-Coding-Agent”"><a href="#1-不要把-Hermes-当“另一个-Coding-Agent”" class="headerlink" title="1. 不要把 Hermes 当“另一个 Coding Agent”"></a>1. 不要把 Hermes 当“另一个 Coding Agent”</h3><p>如果把 Hermes 和 Codex&#x2F;CC 放在同一个维度比较，它并没有明显优势。</p><p>但如果把 Hermes 放在“长期个人 Agent 环境”的维度，它的定位就清楚很多：它不是替代 coding agent，而是承接 coding agent 的产物，并负责长期运行、记忆和演化。</p><p>一句话概括：</p><blockquote><p>Codex 帮我造零件，Hermes 帮我养系统。</p></blockquote><h3 id="2-真正有价值的-Agent-项目，通常不是一次性任务"><a href="#2-真正有价值的-Agent-项目，通常不是一次性任务" class="headerlink" title="2. 真正有价值的 Agent 项目，通常不是一次性任务"></a>2. 真正有价值的 Agent 项目，通常不是一次性任务</h3><p>PassiveAgent 的价值不在于某个单点功能多复杂，而在于它每天进入我的信息流。</p><p>这类项目需要长期运行、长期调参、长期排障、长期根据反馈变化。它更像一个个人系统，而不是一个 demo。</p><p>Hermes 适合的也正是这种场景：有状态、有历史、有工具、有运行环境、有反馈闭环。</p><h3 id="3-Agent-的难点经常不在模型，而在边界条件"><a href="#3-Agent-的难点经常不在模型，而在边界条件" class="headerlink" title="3. Agent 的难点经常不在模型，而在边界条件"></a>3. Agent 的难点经常不在模型，而在边界条件</h3><p>LLM 摘要和打分只是中间一环。真正花时间的是：</p><ul><li>数据源质量；</li><li>去重；</li><li>评分权重；</li><li>负反馈；</li><li>推送节奏；</li><li>飞书权限；</li><li>launchd 环境；</li><li>配置与密钥隔离；</li><li>失败后的可观测性。</li></ul><p>这些都是 Agent 应用工程化的一部分。</p><h3 id="4-自进化的前提是“把经验写回系统”"><a href="#4-自进化的前提是“把经验写回系统”" class="headerlink" title="4. 自进化的前提是“把经验写回系统”"></a>4. 自进化的前提是“把经验写回系统”</h3><p>如果每次只是让 Agent 改完代码，然后关掉窗口，那么不会有什么自进化。</p><p>自进化发生在这些动作里：</p><ul><li>把踩坑写进 skill；</li><li>把稳定流程写进文档；</li><li>把用户偏好写进 memory；</li><li>把反复出现的问题变成检查项；</li><li>把一次性排障变成下一次的默认知识。</li></ul><p>这也是 Hermes 相比普通一次性问答工具更有意义的地方。</p><hr><h2 id="后续想继续做什么"><a href="#后续想继续做什么" class="headerlink" title="后续想继续做什么"></a>后续想继续做什么</h2><p>PassiveAgent 现在只是初具规模，后面还有很多可以继续演进的方向：</p><ol><li><strong>补偿机制</strong>：如果 macOS 错过 daily 定时任务，登录后自动检测并补跑。</li><li><strong>更细的反馈学习</strong>：不仅记录忽略，还区分“不相关”“太简单”“重复”“暂时不看”。</li><li><strong>更强的知识沉淀</strong>：从推荐条目自动生成 Obsidian 结构化笔记，并建立和已有笔记的链接。</li><li><strong>面试准备闭环</strong>：把高价值内容进一步转成面试题、回答卡、追问链路。</li><li><strong>推荐质量评估</strong>：定期回看过去一周推送，统计真正被阅读、被沉淀、被忽略的比例。</li></ol><p>这些方向都不是单纯“再加一个功能”，而是让 PassiveAgent 更像一个能陪我长期学习和筛选信息的个人系统。</p><hr><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>做 PassiveAgent 让我重新理解了 Hermes 的价值。</p><p>它不是用来证明“我也有一个 coding agent”。写代码这件事，Codex 和 Claude Code 已经做得很好。Hermes 真正有意思的地方，是它让一个项目拥有长期记忆，让开发过程能沉淀为技能，让排障经验能进入下一次执行，让一个个人 Agent 系统在真实环境里持续演化。</p><p>PassiveAgent 管理的是我的信息注意力；Hermes 管理的是这个项目的工程记忆。</p><p>前者帮我每天少看一点没那么重要的东西，后者帮我下次少重复一次已经踩过的坑。</p><p>这就是我目前理解的 Hermes 实践价值：</p><blockquote><p>不是替代 Codex&#x2F;CC，而是把一次性 coding 能力放进一个长期可记忆、可执行、可调度、可演化的个人工程环境里。</p></blockquote>]]>
    </content>
    <id>http://jackzhu.top/2026/06/10/Codex%E9%80%A0%E9%9B%B6%E4%BB%B6Hermes%E5%85%BB%E7%B3%BB%E7%BB%9F/</id>
    <link href="http://jackzhu.top/2026/06/10/Codex%E9%80%A0%E9%9B%B6%E4%BB%B6Hermes%E5%85%BB%E7%B3%BB%E7%BB%9F/"/>
    <published>2026-06-10T15:11:04.000Z</published>
    <summary>以 PassiveAgent 为案例，记录我如何用 Hermes 作为长期项目工作台，配合 Codex/Claude Code 开发一个个人注意力调度系统，以及对 Agent 自进化和工程化实践的思考。</summary>
    <title>Codex 造零件，Hermes 养系统：我用 Hermes 实践 PassiveAgent 的过程</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="技术" scheme="http://jackzhu.top/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="Hermes,Agent,自动化,博客,Skill,自进化,OpenClaw,Claude Code" scheme="http://jackzhu.top/tags/Hermes-Agent-%E8%87%AA%E5%8A%A8%E5%8C%96-%E5%8D%9A%E5%AE%A2-Skill-%E8%87%AA%E8%BF%9B%E5%8C%96-OpenClaw-Claude-Code/"/>
    <content>
      <![CDATA[<span id="more"></span><h2 id="引子：Agent-工程师的”吃狗粮”实验"><a href="#引子：Agent-工程师的”吃狗粮”实验" class="headerlink" title="引子：Agent 工程师的”吃狗粮”实验"></a>引子：Agent 工程师的”吃狗粮”实验</h2><p>作为一名 Agent 算法工程师，我每天都在调优模型的 reasoning 链路、设计工具调用策略、处理多步规划的回滚。但有一个问题始终困扰着我：<strong>我自己的 Agent，我自己真的在用吗？</strong></p><p>于是我做了一个实验：把自己最常做的机械性劳动——写技术博客——彻底交给 Agent 来驱动。不是简单让 LLM 代写一篇文章丢上去，而是真正搭建一套<strong>从选题到部署的完整自动化流水线</strong>。每次执行只需几十分钟，产出一篇结构完整、配有暗色 SVG 图表的中文技术文章，自动 push 到 GitHub Pages 并上线 CDN。</p><p>这篇文章就是这个实验的第一篇产物——它本身就是 Hermes 写出来的，包括你现在看到的封面图。</p><h2 id="选型：为什么不是-Claude-Code-或-OpenClaw？"><a href="#选型：为什么不是-Claude-Code-或-OpenClaw？" class="headerlink" title="选型：为什么不是 Claude Code 或 OpenClaw？"></a>选型：为什么不是 Claude Code 或 OpenClaw？</h2><p>在搭建之前，我对比了三个当前主流的开源 Agent 框架：</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/agent-framework-comparison.svg" alt="Agent 框架选型对比"></p><p>三个框架各有侧重。<strong>Claude Code</strong> 的定位很清晰——它是一个编码 Agent，擅长代码补全、重构、PR review，但它没有持久记忆，也不支持定时触发，更别提自进化能力了。对博客自动化这种需要跨 session 积累经验的场景，它不适用。</p><p><strong>OpenClaw</strong> 则是一个通用 Agent 框架，支持 Telegram 和 Web 输入、有完善的插件系统，看起来似乎非常适合。但实际评估后发现几个硬伤：它没有持久记忆（每次 session 都是全新的上下文）、没有 cron 定时任务引擎、部署需要 Docker + 多服务协调，维护成本不低。最关键的是——<strong>它没有自进化机制</strong>。每次任务执行完毕，知识就随着 context window 的关闭消散了。下次执行同样的任务，它还是会踩同样的坑。</p><p>而 <strong>Hermes</strong> 是唯一同时具备以下能力的框架：</p><ol><li><strong>持久记忆</strong>：跨 session 保留用户偏好、环境信息、历史教训</li><li><strong>Cron 定时引擎</strong>：内置定时任务系统，真正做到无人值守</li><li><strong>自进化 Skill 系统</strong>：每次任务完成后自动沉淀 Skill，下次更稳更快</li><li><strong>零维护部署</strong>：<code>pip install</code> 一行搞定，不需要 Docker 集群</li></ol><p>这正是我选择 Hermes 来驱动博客引擎的核心原因。</p><h2 id="架构：Pipeline-全景"><a href="#架构：Pipeline-全景" class="headerlink" title="架构：Pipeline 全景"></a>架构：Pipeline 全景</h2><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/hermes-blog-engine-cover.svg" alt="Hermes 自进化博客引擎"></p><p>整个流水线分为五个阶段，每一阶段都完全由 Hermes Agent 驱动：</p><ol><li><strong>选题策划</strong>：Agent 根据持久记忆中记录的阅读偏好和技术栈，结合当前热点，自动生成选题建议</li><li><strong>AI 写作</strong>：大模型按照预设的章节结构（引子→选型→架构→机制→展望→总结）生成正文，自动控制字数在 3000-4000 字</li><li><strong>图表生成</strong>：直接生成 SVG 格式的暗色配图——流程图、对比表、架构图，全部用代码完成而非调用图片生成 API</li><li><strong>Git 部署</strong>：自动 commit + push 到 GitHub Pages 仓库，3 分钟内上线 CDN</li><li><strong>CDN 上线</strong>：通过 jsDelivr 全球 CDN 分发，访问 jackzhu.top 即可看到</li></ol><p>最妙的是第 6 步——<strong>自进化回路</strong>。每次流水线执行完成后，Agent 会把本次执行中踩到的坑、发现的优化点、用户反馈的修正自动写入 Skill 文件。下一次执行时，这些经验会自动加载到 context 中，让流水线变得更稳、更快、更精准。</p><h2 id="自进化机制：不只是”自动化”，是”越用越好”"><a href="#自进化机制：不只是”自动化”，是”越用越好”" class="headerlink" title="自进化机制：不只是”自动化”，是”越用越好”"></a>自进化机制：不只是”自动化”，是”越用越好”</h2><p>很多人把 Agent 和”自动化脚本”划等号，这是一个误解。普通自动化脚本是<strong>配置驱动</strong>的：一次编写，万年不变，场景稍有变化就需要人类介入修改。</p><p>而 Hermes 的自进化机制完全不同：</p><table><thead><tr><th>维度</th><th>传统脚本&#x2F;配置驱动</th><th>Hermes Skill 自进化</th></tr></thead><tbody><tr><td>知识保留</td><td>无，每次重启丢失</td><td>持久记忆跨 session 保留</td></tr><tr><td>错误修正</td><td>人工修改代码</td><td>Agent 自动沉淀并复用</td></tr><tr><td>场景适应</td><td>固定的 if-else</td><td>动态加载相关 Skill</td></tr><tr><td>效率提升</td><td>线性（纯靠人工优化）</td><td>指数级（Skill 叠加效应）</td></tr><tr><td>维护成本</td><td>高，需要持续干预</td><td>低，自愈自优</td></tr></tbody></table><p>举个例子：第一次写博客时，Agent 生成的 SVG 图表可能在布局上有些瑕疵——图表间距不够、文字溢出、颜色对比度不够。这些反馈我不会手动去修代码，而是在回复中指出。Hermes 会把我的修正意见写入 Skill，下次再生成 SVG 时自动应用这些改进。<strong>三次迭代之后，生成的图表质量已经可以媲美专业设计师。</strong></p><h2 id="Skill-沉淀：从-v1-0-到-v1-3-的进化之路"><a href="#Skill-沉淀：从-v1-0-到-v1-3-的进化之路" class="headerlink" title="Skill 沉淀：从 v1.0 到 v1.3 的进化之路"></a>Skill 沉淀：从 v1.0 到 v1.3 的进化之路</h2><p>博客引擎的 Skill 经历了三次关键迭代：</p><ul><li><strong>v1.0</strong>：基础流水线——能跑通，但图表丑、排版偶尔翻车、字数控制不稳定</li><li><strong>v1.1</strong>：SVG 定义沉淀——暗色主题模板、间距规范、配色方案全部固化到 Skill，图表从”能用”变成”好看”</li><li><strong>v1.2</strong>：Hexo 规范适配——frontmatter 格式、分类体系、CDN 引用路径全部标准化，不再出现部署后图片 404</li><li><strong>v1.3</strong>：自检与回滚——每次发布前自动检查图片 CDN 可访问性，发现问题时自动回滚并重试</li></ul><p>这三个版本只用了不到一周的迭代时间。如果不是自进化机制，光 v1.0 到 v1.1 的 SVG 调优工作，靠手动改 CSS 至少需要一整个下午。</p><h2 id="为什么不用-OpenClaw：一个务实的取舍"><a href="#为什么不用-OpenClaw：一个务实的取舍" class="headerlink" title="为什么不用 OpenClaw：一个务实的取舍"></a>为什么不用 OpenClaw：一个务实的取舍</h2><p>这里有必要坦诚地说明为什么我没有选 OpenClaw。OpenClaw 的插件生态（40+ 插件）和通用性确实令人印象深刻，但它设计哲学是”一次性的自动化任务”——每个 session 是独立的，没有跨 session 的知识传承。</p><p>打个比方：OpenClaw 像一个非常能干的实习生，每次交给他任务都能完成，但他不会做笔记。下次再给他同样的任务，他还是要从头摸索。而 Hermes 像一个会写 SOP 的资深工程师——每次完成任务后会把经验写成标准操作流程，下次拿到类似任务直接调用，越做越顺手。</p><p>对于博客自动化这种 <strong>“长期稳定运行 + 需要持续优化”</strong> 的场景，有没有自进化能力几乎是质的差别。</p><h2 id="复用展望：不止于博客"><a href="#复用展望：不止于博客" class="headerlink" title="复用展望：不止于博客"></a>复用展望：不止于博客</h2><p>这套流水线的核心能力——定时触发、多模态生成、Git 自动化、自进化沉淀——远不止能用于博客。我已经在规划几个扩展方向：</p><ul><li><strong>日报&#x2F;周报生成器</strong>：自动抓取项目进度、PR 记录、会议纪要，汇总成结构化的日报</li><li><strong>竞品监控 Agent</strong>：定时抓取竞品更新、定价变化、新功能发布，生成分析简报</li><li><strong>技术文档维护</strong>：代码仓库的 README、CHANGELOG 随代码提交自动更新</li></ul><p>每一个新场景只需要创建一个新的 skill 文件，框架层完全复用。这就是自进化 Agent 的真正威力——<strong>基础设施一次搭建，垂直场景无限扩展</strong>。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这次”吃狗粮”实验验证了我的核心信念：<strong>Agent 不只是回答问题或写代码的工具，它可以是一套完整的生产系统</strong>。从选题、写作、配图到部署，几十分钟内全自动闭环。每次执行都是一次学习，每个 Skill 都是一次进化。</p><p>如果你也在做 Agent 相关的开发，我建议你也试试”吃自己的狗粮”——把你最日常的任务交给你的 Agent，看看它能做到什么程度。你可能会发现，你的 Agent 比你想象的更强大。</p><hr><p><strong>本文由 Hermes Agent 全程驱动完成——从选题策划到 SVG 图表生成到 Git 部署，人类仅在最终审阅环节介入。这正是 “自进化博客引擎” 的第一篇自证之作。</strong></p>]]>
    </content>
    <id>http://jackzhu.top/2026/05/12/hermes-self-evolving-blog-engine/</id>
    <link href="http://jackzhu.top/2026/05/12/hermes-self-evolving-blog-engine/"/>
    <published>2026-05-12T22:30:00.000Z</published>
    <summary>作为 Agent 算法工程师，我用 Hermes 搭建了一套从选题到部署全自动的博客流水线——几十分钟完成一篇带 SVG 配图的技术文章，而且每次执行后自动沉淀 Skill，让下次更稳更快。</summary>
    <title>Hermes 自进化博客引擎：从选题到部署，几十分钟全自动闭环</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="AI技术" scheme="http://jackzhu.top/categories/AI%E6%8A%80%E6%9C%AF/"/>
    <category term="Agent" scheme="http://jackzhu.top/tags/Agent/"/>
    <content>
      <![CDATA[<h2 id="0-引言"><a href="#0-引言" class="headerlink" title="0. 引言"></a>0. 引言</h2><p>想象这样一个场景：你让 Agent 帮你调研一个开源项目。第一轮，你告诉它「只关注 Python 实现，忽略 JS 版本」。第三轮，它兴高采烈地给你推荐了一个 Node.js 库。你纠正了它。到了第五轮，它又忘了。</p><p>你忍不住问：<strong>你的记性为什么这么差？</strong></p><p>这个问题背后，是 AI Agent 领域一个被严重低估的核心挑战——<strong>上下文工程（Context Engineering）</strong>。大多数人以为，只要上下文窗口足够大（比如 Gemini 的 1M tokens），Agent 就能「记住一切」。但真实情况是：<strong>上下文窗口是 Agent 的 RAM，记忆系统才是 Agent 的硬盘——两者缺一不可，且各自有各自的工程难题。</strong></p><p>本文将系统性地拆解 Agent 记忆系统，从问题定义到工程方案，分为六个层次逐步深入。</p><hr><h2 id="1-问题定义：上下文窗口的幻觉"><a href="#1-问题定义：上下文窗口的幻觉" class="headerlink" title="1. 问题定义：上下文窗口的幻觉"></a>1. 问题定义：上下文窗口的幻觉</h2><p>长上下文是必要条件，但它带来的问题远比解决的问题多。我们来看四个核心挑战：</p><h3 id="1-1-注意力稀释：Lost-in-the-Middle"><a href="#1-1-注意力稀释：Lost-in-the-Middle" class="headerlink" title="1.1 注意力稀释：Lost in the Middle"></a>1.1 注意力稀释：Lost in the Middle</h3><p>2023 年，Liu 等人在论文 <em>Lost in the Middle: How Language Models Use Long Contexts</em> 中揭示了一个反直觉的发现：当信息位于上下文窗口的中间位置时，模型的检索准确率会显著下降。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/lost-in-middle-chart.svg" alt="Lost in the Middle 效应"></p><p>这个发现的重要性怎么强调都不为过。模型不是均匀地「注意」所有上下文——它天然倾向于关注开头（首因效应）和结尾（近因效应），中间部分的信息被严重稀释。当你把 100K tokens 的上下文一股脑塞给模型时，中间 40K-60K 位置的关键指令可能已经被「淹没」了。</p><h3 id="1-2-成本爆炸"><a href="#1-2-成本爆炸" class="headerlink" title="1.2 成本爆炸"></a>1.2 成本爆炸</h3><p>以 GPT-4 级别模型为例，输入价格为 $10&#x2F;1M tokens。1M token 上下文的一次推理就要 $10，而一个复杂的 Agent 任务可能需要十几轮甚至几十轮对话。成本不是线性增长，而是飞轮效应——每轮都把历史带上，上下文越来越长，成本越来越高。</p><table><thead><tr><th>上下文长度</th><th>单轮成本（估算）</th><th>10 轮对话成本</th></tr></thead><tbody><tr><td>32K tokens</td><td>$0.32</td><td>~$5</td></tr><tr><td>128K tokens</td><td>$1.28</td><td>~$25</td></tr><tr><td>500K tokens</td><td>$5.00</td><td>~$80+</td></tr><tr><td>1M tokens</td><td>$10.00</td><td>~$150+</td></tr></tbody></table><h3 id="1-3-幻觉加剧"><a href="#1-3-幻觉加剧" class="headerlink" title="1.3 幻觉加剧"></a>1.3 幻觉加剧</h3><p>上下文越长，无关信息越多。模型在多轮对话中会逐渐被早期噪声干扰，产生「上下文幻觉」——用错误的记忆覆盖正确的信息。这不是模型本身的幻觉问题，而是上下文工程的问题。</p><h3 id="1-4-延迟不可控"><a href="#1-4-延迟不可控" class="headerlink" title="1.4 延迟不可控"></a>1.4 延迟不可控</h3><p>KV Cache 的大小与上下文长度成正比。1M token 上下文的 prefilling 阶段可能需要数秒甚至数十秒，首 token 延迟（TTFT）从毫秒级膨胀到秒级。对于需要快速响应的 Agent 场景，这是致命的。</p><blockquote><p><strong>结论：</strong> 上下文窗口大 ≠ 记忆好。你需要一套工程体系来管理 Agent 的「注意力预算」。</p></blockquote><hr><h2 id="2-上下文分层架构"><a href="#2-上下文分层架构" class="headerlink" title="2. 上下文分层架构"></a>2. 上下文分层架构</h2><p>Agent 记忆系统的核心设计理念是<strong>分层</strong>——不是一层记忆，而是三层。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/agent-memory-architecture.svg" alt="Agent 记忆系统三层架构"></p><h3 id="2-1-工作记忆（Working-Memory）"><a href="#2-1-工作记忆（Working-Memory）" class="headerlink" title="2.1 工作记忆（Working Memory）"></a>2.1 工作记忆（Working Memory）</h3><p><strong>定义：</strong> 当前对话窗口中的全部信息，4K–32K tokens，严格顺序保留。</p><p>工作记忆是 Agent 的「意识」。它包含系统提示（角色定义、行为规则）、对话历史（用户输入和 Agent 回复的完整序列）、工具定义（函数签名和参数 schema），以及从长期记忆中检索到的相关上下文。</p><p><strong>关键工程挑战：</strong></p><ul><li><strong>触发压缩的时机：</strong> 当 token 预算达到阈值（如 70%）时，必须触发压缩，而不是等 OOM 再处理</li><li><strong>压缩策略选择：</strong> 摘要（保留语义，丢失精确细节）vs 截断（保留最新信息，丢失历史）vs 关键对话提取（保留决策点和纠正，丢弃过程噪音）</li><li><strong>压缩粒度：</strong> 对早期轮次进行激进压缩（只保留摘要），对近期轮次保持原始内容</li></ul><h3 id="2-2-情景记忆（Episodic-Memory）"><a href="#2-2-情景记忆（Episodic-Memory）" class="headerlink" title="2.2 情景记忆（Episodic Memory）"></a>2.2 情景记忆（Episodic Memory）</h3><p><strong>定义：</strong> 历史会话的结构化摘要，向量化存储，支持语义检索。</p><p>情景记忆是 Agent 的「短期记忆」。当一次会话结束后，不是把原始对话全部丢掉，而是提取关键信息——决策、纠正、偏好变化——并以向量的形式存储。</p><p><strong>存储什么：</strong></p><ul><li>用户在这次会话中的偏好表达（「以后用 pytest 不要用 unittest」）</li><li>Agent 在这次会话中的关键决策及其原因</li><li>用户对 Agent 行为的纠正（「上次你说 X，实际是 Y」）</li><li>会话最终产出的摘要</li></ul><p><strong>不存储什么：</strong></p><ul><li>过程性讨论和试探性尝试</li><li>工具调用的中间结果</li><li>用户明确表示「不要记」的内容</li></ul><h3 id="2-3-语义记忆（Semantic-Memory）"><a href="#2-3-语义记忆（Semantic-Memory）" class="headerlink" title="2.3 语义记忆（Semantic Memory）"></a>2.3 语义记忆（Semantic Memory）</h3><p><strong>定义：</strong> 长期累积的结构化知识，包含用户画像、领域知识、工具经验和固化技能。</p><p>语义记忆是 Agent 的「长期记忆」，也是最稳定的一层。它不随会话结束而消失，而是逐步累积和更新。</p><table><thead><tr><th>类别</th><th>示例</th><th>存储方式</th></tr></thead><tbody><tr><td>用户画像</td><td>角色、编程风格偏好、时区</td><td>结构化标签</td></tr><tr><td>领域知识</td><td>项目文件结构、常用 API 端点</td><td>文档化存储</td></tr><tr><td>工具经验</td><td>「该 API 超时设为 30s 而非 5s」</td><td>结构化经验库</td></tr><tr><td>固化技能</td><td>从多次重复操作中提取 SOP</td><td>可执行技能定义</td></tr></tbody></table><p><strong>核心原则：</strong> 语义记忆必须是<strong>结构化</strong>的，不能是纯文本堆砌。因为它在每次推理时都会被注入到系统提示中，结构化的信息更容易被模型有效利用。</p><hr><h2 id="3-Token-预算管理"><a href="#3-Token-预算管理" class="headerlink" title="3. Token 预算管理"></a>3. Token 预算管理</h2><p>如果把上下文窗口比作 Agent 的「预算」，那么 Token 就是 Agent 的「货币」。你需要一个精打细算的财务管理系统。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/token-budget-chart.svg" alt="Token 预算分配策略"></p><h3 id="3-1-静态预算分配"><a href="#3-1-静态预算分配" class="headerlink" title="3.1 静态预算分配"></a>3.1 静态预算分配</h3><p>以 128K 上下文窗口为例，建议的分配策略：</p><table><thead><tr><th>组件</th><th>建议占比</th><th>说明</th></tr></thead><tbody><tr><td>系统提示</td><td>10-15%</td><td>角色定义、行为规则、语义记忆注入</td></tr><tr><td>工具定义</td><td>10-15%</td><td>函数签名、参数 schema、工具描述</td></tr><tr><td>对话历史</td><td>40-50%</td><td>工作记忆核心内容</td></tr><tr><td>检索上下文</td><td>15-20%</td><td>从情景记忆中检索到的相关信息</td></tr><tr><td>预留余量</td><td>5-10%</td><td>工具调用返回结果的空间</td></tr></tbody></table><h3 id="3-2-动态调度机制"><a href="#3-2-动态调度机制" class="headerlink" title="3.2 动态调度机制"></a>3.2 动态调度机制</h3><p>静态分配只是起点。真正的挑战在于动态调度：</p><p><strong>工具返回截断：</strong> 当工具调用结果超过预留空间时，不直接截断到固定长度，而是提取关键信息——比如从 10000 行的日志中只保留 ERROR 和 WARNING 级别的内容。</p><p><strong>递进式压缩：</strong> 不是一次性压缩所有历史，而是递进式处理。早期轮次（第 1-5 轮）高度压缩为摘要，中期轮次（第 6-10 轮）保留关键对话，近期轮次（最近 5 轮）完整保留。</p><p><strong>记忆外化：</strong> 当上下文即将耗尽时，主动将部分信息「外化」到外部存储，并在需要时通过检索召回。这类似于操作系统的虚拟内存机制。</p><h3 id="3-3-实践建议"><a href="#3-3-实践建议" class="headerlink" title="3.3 实践建议"></a>3.3 实践建议</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 伪代码：Token 预算检查</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">before_each_turn</span>():</span><br><span class="line">    tokens_used = count_tokens(system_prompt + history + tools)</span><br><span class="line">    tokens_left = max_context - tokens_used - RESERVE</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> tokens_left &lt; SAFETY_THRESHOLD:</span><br><span class="line">        <span class="keyword">if</span> oldest_rounds_not_compressed():</span><br><span class="line">            compress_oldest_rounds()  <span class="comment"># 递进式压缩</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            externalize_to_memory()   <span class="comment"># 记忆外化</span></span><br></pre></td></tr></table></figure><p><strong>重要提示：</strong> 不要信任模型自报的 token 计数。始终使用独立的 tokenizer 维护精确计数，在每次工具调用前后检查预算。</p><hr><h2 id="4-记忆检索：Agent-的搜索引擎"><a href="#4-记忆检索：Agent-的搜索引擎" class="headerlink" title="4. 记忆检索：Agent 的搜索引擎"></a>4. 记忆检索：Agent 的搜索引擎</h2><p>存得好不如取得好。如果检索系统不能在海量记忆中精准召回相关信息，那记忆系统就是个昂贵的摆设。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/hybrid-retrieval.svg" alt="混合检索流程图"></p><h3 id="4-1-Agent-场景的检索特殊性"><a href="#4-1-Agent-场景的检索特殊性" class="headerlink" title="4.1 Agent 场景的检索特殊性"></a>4.1 Agent 场景的检索特殊性</h3><p>相比传统 RAG，Agent 的记忆检索有三个独特挑战：</p><ul><li><strong>时间敏感性：</strong> 上一条消息中用户说的「不要用 unittest」比三个月前说过的话重要得多</li><li><strong>上下文依赖性：</strong> 同样的查询「跑一下测试」，在不同项目、不同阶段含义完全不同</li><li><strong>精确匹配需求：</strong> 用户可能明确提到实体名（如「那个叫 hermes-agent 的项目」），需要关键词级别的精确匹配</li></ul><h3 id="4-2-混合检索公式"><a href="#4-2-混合检索公式" class="headerlink" title="4.2 混合检索公式"></a>4.2 混合检索公式</h3><p>单一维度无法满足 Agent 的需求。我们需要混合检索：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">score = α × 语义相似度 + β × 时间衰减 + γ × 关键词匹配 + δ × 重要性权重</span><br></pre></td></tr></table></figure><p><strong>各维度详解：</strong></p><ul><li><strong>α × 语义相似度（权重 0.40）：</strong> 基于 embedding 的 cosine similarity，捕捉语义层面的相似性</li><li><strong>β × 时间衰减（权重 0.25）：</strong> 指数衰减函数 <code>e^(-λ × Δt)</code>，让近期记忆有更高的召回率</li><li><strong>γ × 关键词匹配（权重 0.20）：</strong> 使用 BM25 算法，确保用户提到的精确实体能被命中</li><li><strong>δ × 重要性权重（权重 0.15）：</strong> 在存储时由 LLM 对记忆的重要性进行预打分</li></ul><h3 id="4-3-检索时机"><a href="#4-3-检索时机" class="headerlink" title="4.3 检索时机"></a>4.3 检索时机</h3><p>Agent 不应该只在用户明确要求时才去检索记忆。三种触发时机：</p><ol><li><strong>主动检索：</strong> 每轮对话开始时，根据用户最新输入检索相关记忆，注入工作记忆</li><li><strong>被动检索：</strong> 当 Agent 对自己的回答不确定时，主动查询记忆进行校验</li><li><strong>触发词检索：</strong> 用户说「上次」「之前」「我记得你说过」时，强制触发跨会话检索</li></ol><hr><h2 id="5-终身学习：让-Agent-越用越聪明"><a href="#5-终身学习：让-Agent-越用越聪明" class="headerlink" title="5. 终身学习：让 Agent 越用越聪明"></a>5. 终身学习：让 Agent 越用越聪明</h2><p>三层记忆架构解决的是「记住什么」和「怎么取」的问题。但还有一个更深刻的问题：<strong>Agent 能不能从经验中学习，而不是每次都需要用户重新教一遍？</strong></p><p>这就是<strong>终身学习（Lifelong Learning）</strong>——Agent 记忆系统的最上层。</p><h3 id="5-1-学习什么"><a href="#5-1-学习什么" class="headerlink" title="5.1 学习什么"></a>5.1 学习什么</h3><p>Agent 可以从以下几个维度持续学习：</p><table><thead><tr><th>学习维度</th><th>触发条件</th><th>示例</th></tr></thead><tbody><tr><td>用户偏好</td><td>用户明确表达或行为暗示</td><td>「回复简洁一点」→ 更新用户画像</td></tr><tr><td>纠正学习</td><td>Agent 犯错后被用户纠正</td><td>「你说的 X 不对，实际是 Y」→ 更新知识</td></tr><tr><td>工具经验</td><td>任务执行中遇到问题并解决</td><td>「此 API 并发限制 10 req&#x2F;s」→ 更新工具使用经验</td></tr><tr><td>项目约定</td><td>用户多次使用相同模式</td><td>连续 5 次用 <code>pytest -n 4</code> → 固化为默认</td></tr><tr><td>成功模式</td><td>某个任务流程多次成功执行</td><td>「这种调研任务分 3 步做最快」→ 固化为技能</td></tr></tbody></table><h3 id="5-2-学习机制"><a href="#5-2-学习机制" class="headerlink" title="5.2 学习机制"></a>5.2 学习机制</h3><p><strong>显式学习：</strong> 用户明确纠正 Agent → 立即更新相关记忆。这是最高优先级的学习信号。</p><p><strong>隐式学习：</strong> Agent 从成功完成的任务中提取模式。不需要用户干预，但需要足够多的样本（连续 3-5 次相同模式）才会触发固化。</p><p><strong>反思学习：</strong> 任务失败后，Agent 进行根因分析（Root Cause Analysis），将教训以「反模式」的形式存入记忆，避免下次犯同样的错误。</p><h3 id="5-3-防止灾难性遗忘"><a href="#5-3-防止灾难性遗忘" class="headerlink" title="5.3 防止灾难性遗忘"></a>5.3 防止灾难性遗忘</h3><p>记忆系统最大的敌人不是「记不住」，而是「记错了」或者「新知识覆盖了旧知识」。</p><p><strong>三大防御机制：</strong></p><ol><li><strong>冲突检测：</strong> 新记忆与旧记忆矛盾时，优先信任用户最新表达，但保留旧记忆作为上下文参考</li><li><strong>衰减而非删除：</strong> 长期未使用的记忆降低检索权重，但不删除。因为「很久没用的知识」可能只是暂时不需要</li><li><strong>定期审计：</strong> 定期让 Agent 自查记忆一致性。例如：用户说他是「Python 开发者」，但某条记忆写的是「主要写 Rust」——这种矛盾应该标记出来</li></ol><hr><h2 id="6-工程实践与总结"><a href="#6-工程实践与总结" class="headerlink" title="6. 工程实践与总结"></a>6. 工程实践与总结</h2><h3 id="6-1-架构全景"><a href="#6-1-架构全景" class="headerlink" title="6.1 架构全景"></a>6.1 架构全景</h3><p>完整的 Agent 记忆系统架构可以总结为三层 + 两个管道：</p><ul><li><strong>三层：</strong> 工作记忆 → 情景记忆 → 语义记忆</li><li><strong>向下管道（压缩沉淀）：</strong> 工作记忆过期 → 压缩为情景记忆 → 长期知识提取为语义记忆</li><li><strong>向上管道（检索召回）：</strong> 语义记忆 + 情景记忆通过混合检索 → 注入工作记忆</li></ul><h3 id="6-2-选型建议"><a href="#6-2-选型建议" class="headerlink" title="6.2 选型建议"></a>6.2 选型建议</h3><table><thead><tr><th>场景规模</th><th>推荐方案</th></tr></thead><tbody><tr><td>个人开发者 &#x2F; 原型验证</td><td>SQLite + ChromaDB + 简单摘要策略</td></tr><tr><td>小团队 &#x2F; 内部工具</td><td>PostgreSQL + pgvector + Redis 缓存 + 混合检索</td></tr><tr><td>生产级 Agent 系统</td><td>自研记忆服务 + 专用向量数据库 + 事件溯源 + 持续评估管道</td></tr></tbody></table><h3 id="6-3-关键启示"><a href="#6-3-关键启示" class="headerlink" title="6.3 关键启示"></a>6.3 关键启示</h3><p>回顾全文，我想强调三个核心理念：</p><ul><li><strong>「上下文窗口是 Agent 的 RAM，记忆系统是 Agent 的硬盘」</strong>——两者缺一不可，且各自需要精心设计</li><li><strong>「好的记忆系统让 Agent 从工具变成伙伴」</strong>——它不需要你反复交代，能从过往交互中学习成长</li><li><strong>「不要等到上下文不够用了才想记忆——在设计的第一天就要考虑三层架构」</strong></li></ul><h3 id="6-4-展望"><a href="#6-4-展望" class="headerlink" title="6.4 展望"></a>6.4 展望</h3><p>Agent 记忆系统还远未成熟。展望未来，有几个值得关注的方向：</p><ul><li><strong>多模态记忆：</strong> Agent 不仅要记住文本，还要记住图片、代码片段、结构化数据</li><li><strong>联邦记忆：</strong> 多个 Agent 共享部分记忆，形成集体智能</li><li><strong>自适应记忆架构：</strong> 根据用户使用模式自动调整各层容量和检索策略</li><li><strong>隐私感知记忆：</strong> 用户可以选择性地「遗忘」某些信息，符合 GDPR 等隐私法规</li></ul><hr><p><em>本文首发于 <a href="https://jackzhu.top/">jackzhu.top</a>。如果你在大厂 Agent 团队工作或有相关实践经验，欢迎在评论区交流讨论。</em></p>]]>
    </content>
    <id>http://jackzhu.top/2026/05/10/Agent%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B7%A5%E7%A8%8B%EF%BC%9A%E4%BB%8EToken%E7%AE%A1%E7%90%86%E5%88%B0%E7%BB%88%E8%BA%AB%E8%AE%B0%E5%BF%86/</id>
    <link href="http://jackzhu.top/2026/05/10/Agent%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B7%A5%E7%A8%8B%EF%BC%9A%E4%BB%8EToken%E7%AE%A1%E7%90%86%E5%88%B0%E7%BB%88%E8%BA%AB%E8%AE%B0%E5%BF%86/"/>
    <published>2026-05-10T02:28:00.000Z</published>
    <summary>深入探讨 AI Agent 的上下文工程，从 Token 预算管理到三层记忆架构，构建能让 Agent 越用越聪明的记忆系统。</summary>
    <title>Agent 上下文工程：从 Token 管理到终身记忆</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="技术知识" scheme="http://jackzhu.top/categories/%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86/"/>
    <category term="技术知识" scheme="http://jackzhu.top/tags/%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86/"/>
    <content>
      <![CDATA[<p>在 Python 开发中，使用好的包和项目管理工具，相当于打好基础。这年头，来自 Astral (即 Ruff 和 Black 的开发者) 的新工具UV，一上线就很受欢迎，在企业场景也广泛使用，以 Rust 高性能实现，目标是替代 pip，virtualenv，poetry 等组合，成为 Python 最新一代的包管理栏杆。</p><h2 id="为什么要使用uv"><a href="#为什么要使用uv" class="headerlink" title="为什么要使用uv"></a>为什么要使用uv</h2><p>现在的包管理工具有很多，conda，pip，poetry，pipenv 等等，功能各有千秋，但都存在一些问题，而uv则可以在很大程度上解决了这些问题。</p><p>自从接触到uv之后我几乎不再使用其他包管理工具，只有部分情况是例外的，比如个别仓库默认的包管理工具是conda或者poetry，而且这个项目用uv会存在bug，比如ComfyUI，使用uv就会有问题，这种就会使用conda。或者个别比较老的代码，有些时候就存在一些问题，这种还是求稳定最好。</p><h3 id="uv的优势"><a href="#uv的优势" class="headerlink" title="uv的优势"></a>uv的优势</h3><ol><li>极致性能</li></ol><ul><li><p>安装速度提升：这是非常重要的一点，UV 在无缓存的冷启动情况下，安装速度比 pip 快约 8～10 倍；uv在已有缓存的情况下，速度提升可达 80～100 倍。这一点看似不明显，但是亲身体验之后就回不去了，命令刚输完就执行完了，简直不要太爽。pip是单线程串行下载，且每安装一个包就校验一次，速度受限；解析依赖时是<strong>递归深度优先</strong>，每次都重新 resolve，IO+CPU双重瓶颈。而uv<strong>完全异步并行下载</strong>（利用 Rust 的 tokio runtime），同时批量多线程拉取 wheel 文件。<strong>依赖解析一次性建树</strong>，在内存中直接解析出完整依赖树，再按优先级并行处理。大量减少 I&#x2F;O 阻塞、减少不必要校验轮次。</p></li><li><p>并行下载与解析：​利用 Rust 的并发能力，UV 实现了高效的依赖解析和并行下载，大幅减少了安装时间。uv在解析阶段同时并发处理<strong>多条依赖关系。下载阶段是异步 HTTP 客户端</strong>，同时并发多达 32+ 连接从镜像源拉取 <code>.whl</code> 文件。在安装阶段：<strong>解压 wheel 包时并行 IO + CPU 分摊</strong>。最近几年很多rust的项目都表现不错，未来可期。</p></li><li><p>全局缓存机制：UV 使用全局包缓存，避免重复下载和构建，提高了资源利用率。这一点其实还是很重要的，因为conda也可以进行全局缓存，但是conda由于本身就非常慢，这样效率其实也没有快哪里去。同时uv的依赖解析缓存也更加细粒度，非常快，全局统一缓存目录管理更加智能，冷启动性能好。<strong>pip 的 cache</strong>只是简单放 <code>.whl</code> 文件。<strong>UV的 cache</strong>更加复杂，针对不同源（PyPI、国内镜像等）<strong>独立管理缓存目录</strong>；wheel&#x2F;binary&#x2F;source包缓存分离；pip-style <code>.whl</code> 缓存 + 自有 <code>build cache</code>（本地打包 artifacts）；还有<strong>软链接&#x2F;硬链接优化</strong>（安装时尽量链接，不拷贝）</p></li></ul><ol start="2"><li>统一的项目管理</li></ol><ul><li><p>集成虚拟环境管理：​UV 内置虚拟环境管理功能，自动在项目中创建和管理虚拟环境，避免了手动操作，非常方便，而且还方便切换python多个版本，避免了本地环境污染。</p></li><li><p>支持 pyproject.toml：UV 使用 pyproject.toml 作为项目配置文件，统一管理依赖和项目元数据。这是一种现代化的依赖管理方法，负责定义项目的元数据、依赖关系、构建系统以及 UV 的特定配置。</p></li><li><p>生成锁定文件：​UV 自动生成 uv.lock 文件，精确记录每个包的版本和校验信息，确保环境的可重复性。​</p></li></ul><ol start="3"><li>灵活的依赖解析</li></ol><ul><li><p>多种解析策略：​UV 提供多种依赖解析策略，如选择最新兼容版本或最低版本组合，满足不同需求。​</p></li><li><p>支持多 Python 版本：​UV 允许指定目标 Python 版本进行依赖解析，即使当前环境中未安装该版本的 Python。​</p></li></ul><p>依赖覆盖机制：​UV 引入了“依赖覆盖”机制，允许用户修改某些包声明的依赖版本，解决上游包的依赖冲突问题。​</p><ol start="4"><li>丰富的功能集<br>命令行工具管理：​UV 提供类似于 pipx 的功能，可以在隔离的环境中安装 Python 工具，同时使它们的命令行接口全局可用。​</li></ol><p>Python 版本管理：​UV 支持安装和管理不同版本的 Python，方便在项目中使用特定版本的解释器。​</p><p>脚本运行支持：​UV 支持运行脚本，并支持内联的依赖元数据，简化了脚本的依赖管理。</p><h2 id="基本使用和常用命令"><a href="#基本使用和常用命令" class="headerlink" title="基本使用和常用命令"></a>基本使用和常用命令</h2><p>一段很简单的安装：</p><p>pip install uv</p><p>快速创建项目：</p><p>uv init my_project<br>cd my_project</p><p>添加包：</p><p>uv add requests</p><p>删除包：</p><p>uv remove requests</p><p>同步安装依赖：</p><p>uv sync</p><p>重新解析依赖并生成锁定文件：</p><p>uv lock</p><p>创建虚拟环境：</p><p>uv venv .venv</p><p>跟 pip 使用方式类似：</p><p>uv pip install flask</p><p>运行脚本：</p><p>uv run python app.py</p><p>清理缓存：</p><p>uv cache clean</p><p>pyproject.toml 包管理文件使用</p><p>UV 采用标准的 pyproject.toml，统一管理项目信息和依赖：</p><p>基础结构：</p><p>[project]<br>name &#x3D; “my_project”<br>version &#x3D; “0.1.0”<br>dependencies &#x3D; [<br>    “requests&gt;&#x3D;2.31”,<br>    “numpy~&#x3D;1.24”<br>]</p><p>[project.optional-dependencies]<br>dev &#x3D; [“pytest”, “black”]</p><p>[build-system]<br>requires &#x3D; [“hatchling”]<br>build-backend &#x3D; “hatchling.build”</p><p>UV 特有配置：</p><p>[tool.uv]<br>managed &#x3D; true<br>package &#x3D; true</p><p>[[tool.uv.index]]<br>url &#x3D; “<a href="https://pypi.tuna.tsinghua.edu.cn/simple">https://pypi.tuna.tsinghua.edu.cn/simple</a>“<br>default &#x3D; true</p><p>[tool.uv] 表示是否由 UV 管理虚拟环境、打包等</p><p>index 可以指定镜像源（如清华 PyPI）</p><p>同时 UV 会生成 uv.lock，锁定给定环境下的确定包版本和校验信息，保证可重复性。</p><h2 id="常见问题和解决方法"><a href="#常见问题和解决方法" class="headerlink" title="常见问题和解决方法"></a>常见问题和解决方法</h2><ol><li>使用 –system-site-packages 后包无效</li></ol><p>UV 创建虚拟环境时，当使用 –system-site-packages，运行可以访问系统包，但不会在解析时考虑。</p><p>建议自己在虚拟环境内重新安装所需包，保证环境简洁可控。</p><ol start="2"><li>删除包后剩余依赖</li></ol><p>uv remove xxx 不会自动删除其依赖包</p><p>需要手动执行：</p><p>uv lock<br>uv sync</p><p>依赖分析后自动清理</p><ol start="3"><li>网络慢，下载失败</li></ol><p>使用国内镜像：</p><p>uv config set index-url <a href="https://pypi.tuna.tsinghua.edu.cn/simple">https://pypi.tuna.tsinghua.edu.cn/simple</a></p><ol start="4"><li>uv.lock 未自动更新</li></ol><p>进行依赖改动后，需自动执行：</p><p>uv lock</p><h2 id="在-UV-中管理-PyTorch"><a href="#在-UV-中管理-PyTorch" class="headerlink" title="在 UV 中管理 PyTorch"></a>在 UV 中管理 PyTorch</h2><ol><li>基础安装：</li></ol><p>默认：Windows&#x2F;Mac 安装 CPU-only 版，Linux 安装 CUDA 12.4 版</p><p>uv init –python 3.12<br>uv add torch torchvision torchaudio</p><ol start="2"><li>安装指定 CUDA 版（如 12.6）：</li></ol><p>修改 pyproject.toml：</p><p>[[tool.uv.index]]<br>name &#x3D; “pytorch-cu126”<br>url &#x3D; “<a href="https://download.pytorch.org/whl/cu126">https://download.pytorch.org/whl/cu126</a>“<br>explicit &#x3D; true</p><p>[tool.uv.sources]<br>torch &#x3D; { index &#x3D; “pytorch-cu126” }<br>torchvision &#x3D; { index &#x3D; “pytorch-cu126” }<br>torchaudio &#x3D; { index &#x3D; “pytorch-cu126” }</p><p>后执行：</p><p>uv sync</p><p>不过后面官方有了支持，现在好像不用这么配自动了，直接 uv add torch torchvision torchaudio 就会自动安装对应 CUDA 版本的包了</p><ol start="3"><li>验证 CUDA 支持</li></ol><p>新建脚本：</p><p>import torch<br>print(torch.cuda.is_available())<br>print(torch.cuda.get_device_name(0))</p><p>运行：</p><p>uv run python check_cuda.py</p><h2 id="常见问题："><a href="#常见问题：" class="headerlink" title="常见问题："></a>常见问题：</h2><p>如果直接用 uv pip install 安装，pyproject.toml 不会更新，建议终究使用 uv add</p><p>如遇到网络慢，同样可配置用国内镜像源</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>UV 是一款超高性能、全配套、很合于环境及大型项目管理的新世代工具，特别适合对环境素质有严格要求的开发者和研究人员。</p><p>无论是日常开发，进行快速指定版本部署，还是复杂的模型训练，UV 都完全能上场，是很值得推荐的 Python 包管理方案。</p>]]>
    </content>
    <id>http://jackzhu.top/2025/04/29/%E7%8E%B0%E4%BB%A3%E7%9A%84%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7uv/</id>
    <link href="http://jackzhu.top/2025/04/29/%E7%8E%B0%E4%BB%A3%E7%9A%84%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7uv/"/>
    <published>2025-04-29T22:21:37.000Z</published>
    <summary>秋招正式批拼多多技术面试分享。</summary>
    <title>现代化的包管理工具：uv</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试总结" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/"/>
    <category term="面试总结" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/"/>
    <content>
      <![CDATA[<p>秋招基本也结束了，这里对整个过程进行复盘和总结，也是提前备战明年的春招。</p><p>这次秋招前期一直0offer，直到11月多才逐渐有差不多几家，最后开出来了一些，最终还是决定去钱多的地方，毕竟现在工作都不稳，钱多才是王道。</p><h1 id="面试前的内容"><a href="#面试前的内容" class="headerlink" title="面试前的内容"></a>面试前的内容</h1><h2 id="算法-vs-开发"><a href="#算法-vs-开发" class="headerlink" title="算法 vs 开发"></a>算法 vs 开发</h2><p>对于算法和开发的选择，我还是选择了算法，他们都说算法要有顶会，但是其实这个因素影响没有那么大的，我觉得不一定需要顶会，但是论文还是要有的，毕竟算法岗。没有顶会的情况下最重要的就是实习了，实习一定要把握住，这个可比顶会好拿到多了。我看往前几届的师兄师姐都去的开发岗，基本都是Java，但是这块我又不擅长，突击几个月还不一定有本科生学的好，干脆all in 算法了。毕竟算法还是比开发高很多的，还是可以冲下的。</p><p>另外一个选择的区别就是个人选择和未来规划，如果本科双非，同时没有科研论文，还没有实习，而且不打算后面卷，而是打算过几年回老家附近的地方，或者直接一步到位去二线城市，肯定直接Java，毕竟选择面非常广，Java的岗位数量遥遥领先，而且去国企银行运营商都好找工作。</p><p>如果还更有些追求，打算去大厂干几年，算法还是更好的，只是本科双非其实不算主要的问题，前提是有比较好的论文或者顶会，再想办法弄个实习，这样基本还是能找的，但要是都没有，那就不太容易了。</p><p>对于算法的准备，主要从以下几个方面展开，大致有力扣，简历，实习，秋招等。</p><h2 id="力扣"><a href="#力扣" class="headerlink" title="力扣"></a>力扣</h2><p>力扣这是最基本的准备，毕竟除了人才计划，其他无论算法开发岗位都有力扣的考察，所以这个是一定要准备的。力扣的内容基本刷几遍hot100就差不多了，基本面试的手撕都是hot100这个难度，基本从前一年的12月就可以开始每天一道题了，开始的时候会很慢，后面慢慢熟练了就快了。之所以要开始这么早是因为实习是第二年三月就开始了，提前三个月准备刷题还是有必要的。很多人刷300题，基本肯定是够用了，有的人甚至刷了600题，其实不是特别有必要，力扣题主要起到的是一个门槛的作用，笔试不过直接pass，过的情况下主要看面试了。</p><h2 id="简历"><a href="#简历" class="headerlink" title="简历"></a>简历</h2><p>简历是非常重要的，决定了简历筛选后面能不能进笔试和面试，我简历凑合吧，投了90家，进面大概30家，正常水平吧。简历一般是先教育经历，之后最好是要有实习经历，然后是论文和科研部分，后面是项目可以放几个，后面是比赛获奖那些，主体就这些吧，具体根据不同人，优势不同，做的好的放前面一些。根据篇幅，后面可以加一些个人技能，比如编程语言和计算机工具比如Git、Linux什么的掌握情况，最后篇幅不够可以放自我评价，这个不是必须，可有可无。</p><p>对于简历的排版来说，也可直接按照不同模块的顺序，也有按照一个一个项目，然后每个项目后面跟着其中的产出论文成果以及技术栈，不同的都是可以的，根据自己实际情况来，总之自己最突出的地方要尽可能的放到比较显眼的前面。</p><p>对于简历是一页还是两页，我看不同HR喜好并不同，有的认为一页简单明了，有的觉得一页太单薄。总之校招一般不超过两页（博士除外），实际来看如果确实有内容建议充实两页，排版不要过于空旷，适当紧凑一些。</p><h1 id="面试内容"><a href="#面试内容" class="headerlink" title="面试内容"></a>面试内容</h1><p>一般一面或者二面技术面，自我介绍之后，如果有比较有含金量的实习或者论文会先讲这部分，之后会问项目之类的，之后有的会问一些八股，这个八股有的是算法八股比如Transformer、CNN、RNN卷积等什么的，这块需要背下。有的会问到计算机基础比如进程线程还有Python基础比如装饰器，线程锁啥的，这块只是有时候会问到。</p><p>然后就是面试中的手撕了，这个不是必须，有的面试会有手撕，这个因岗位和公司不同，手撕大部分是力扣，当然也有例外，比如淘天，投实习的时候手撕是手写多头注意力，交叉熵等题目，还有k-means的，这个不提前准备的话想快速写出来也不太容易。比如华子正式面试二面就出了个数字图像处理的手撕，很难，就蚌埠住了，这种题不多见。</p><p>技术面之后一般主管面，这块可能的问题很多，从个人信息到家里情况，到抗压测试，了解新信息的途径，其他还有比如人工智能对各行各业的影响，大模型在不同行业的应用，甚至包括盈利模式啥的等产品问题，不同的侧重点也不同，需要随机应变。这个部分不可小视，相关问题要整理下，大致想想如何回答。有的公司比如华子是技术面了可以捞，但是主管面挂了就不行。</p><h2 id="实习"><a href="#实习" class="headerlink" title="实习"></a>实习</h2><p>实习的重要程度可以说是第一了，毕竟一般如果组内条件不具备的话，是发不出来顶会的，尤其对于硕士，所以实习就成为了性价比最高而且最可以获取到的了。</p><p>准备实习第一步是要提前刷力扣题，然后准备简历润色，之后到三月下实习基本就开始了，四五月是最主要的时间，六月基本就不多了，建议尽早投递，一般越早越容易拿到Offer，前期看哪些先开，最开始的可以不投特别大的厂，因为没有相关经验，如果面的很差的话确实会影响面评，影响后面，不过大部分好像影响不大的，可以先从小厂开始，虽然很多时候大厂先开。在面试的过程中不断复盘，总结经验，不断提高，投递尽量不要拖太后，后面很多没有hc，即使准备的好也没用了。</p><p>我前面投递实习的时候技术面其实不会特别难，不过也有例外，比如淘天的，还有腾讯AILab这种，不过多面还是有好处的，对于秋招面试也是积累经验的过程。</p><p>如果实在没有实习经历，也是可以把一些横向的项目加进去，包括校企合作，实践活动，这种有的也是算实习的。</p><p>实习看能不能转正，如果能转正就要好好准备，不能的话就趁早想想后路，实习的经历如何整理成体系，面试的时候问答到相关的问题如何回答，把工作点整理好，面试的时候就不慌。</p><p>除此之外，如果没有特别好的成果的话实习投递也有可能不容易找到，但是这也是一个打怪升级的过程，有实习还是远远大于没有实习的，没有大厂就先去小厂，后面有机会再去大厂，先找一个能去的。</p><h2 id="秋招"><a href="#秋招" class="headerlink" title="秋招"></a>秋招</h2><p>如果有实习的话一般八九月就要回去准备秋招了，除非对自己非常自信能实习转正。对于有实习的有经历，但是秋招的准备会少一些时间，因为实习很多时候也挺忙的，个别实习好像能直接在公司刷力扣，不过这种极少，看部门和主管。没有实习的人秋招七八月的时候有比较完整的时间准备秋招的内容比如力扣八股，把项目好好复盘。</p><p>建议还是尽早投递，确实是越早越容易，所以早点开始准备秋招的时候就可以占有不少优势，前后期难度确实不同，基本上八月份面试的都容易进。然后整理一个表格，不同公司的啥时候开始投递，有些相关的群要关注下，有的学长学姐会进行整理，自己这个表格记录好公司、投递时间、岗位、base地，笔试面试情况和时间啥的，方便自己查看。</p><h1 id="算法方向"><a href="#算法方向" class="headerlink" title="算法方向"></a>算法方向</h1><p>对于算法方向，目前岗位很多是算法工程师或者AI工程师，这块需要的技能很多时候不是只限一个方面的，很多岗位问的时候是既有传统算法也有大模型，所以技能肯定是越多越好，绝大部分算法都不是研究型的算法，而是业务型的算法，这样的算法主要是以解决实际问题来的，无论传统算法还是深度学习或者大模型，根据实际问题选择，所以都会最好。当然也有不少是直接的大模型算法工程师，虽然这种岗问的时候也还是有一般的算法。具体来说，主要从以下几个方向展开。</p><h2 id="视觉方向"><a href="#视觉方向" class="headerlink" title="视觉方向"></a>视觉方向</h2><p>这个方向也是前面做的人最多的方向，虽然很多人现在做的还是视觉的项目或者科研，但是确实不建议用这个来找工作，除非你能发CCF-C及以上的论文吧，虽然C也很勉强。现在视觉的岗位太少了，所以必须是做的比较深，有一定的研究才行，不然相关的算法都很成熟了，不太好用来找工作。</p><p>这个方向现在不适合单独作为一个方向找工作，但是也是算法工程师基本必不可少的一个基础，还是要会一些相关内容更好的。</p><p>以下是两个半可以做的方向，说是两个半是因为第三个方向部署推理这块的hc远远不如前面两个的。</p><h2 id="大模型"><a href="#大模型" class="headerlink" title="大模型"></a>大模型</h2><p>这个方向是目前算法最好找工作的方向了，大语言模型和AIGC这块的需求很多。很多人都说自己没做过，可问题是22年11月底ChatGPT出来之前，有几个人是做大模型的？现在做大模型的人99.9%的都是大模型出来之后开始学的，这块的上手门槛没有那么高的。很多人准备一两个月做一个大致像样的项目，用来参加比如书生浦语浦源大模型挑战赛，或者阿里这种的大模型比赛，差不多获奖就可以拿来简历用了，只要被问到的时候能讲清楚就还是可以的。现在各行各业都在用大模型做一遍，仍然是个可以做的风口，虽然风口过去并不知道能做成啥样，但是这几年自己能赚到就可以了。</p><p>学习这个并不需要一上来直接看论文，看看相关的项目，和一些相对比较容易的开源课程，先上手了解整个体系，然后尝试做个玩具微调下，之后再去认真研究，是个比较好的学习方法。</p><h2 id="搜广推"><a href="#搜广推" class="headerlink" title="搜广推"></a>搜广推</h2><p>这个方向作为互联网的基础，有非常稳定的基础，但是对于应届生来说学校期间往往不做这块的内容，不过要是突击两个月还是可以做下的，不算特别热，但是hc还可以，互联网都招。</p><h2 id="部署推理"><a href="#部署推理" class="headerlink" title="部署推理"></a>部署推理</h2><p>这个方向涉及模型的量化、剪枝、蒸馏、推理、部署、推理框架、推理引擎，还要AI Infra这些，这些方向其实还是可以做的，尤其现在大模型落地对这块的需求还是不少的，即使不是大模型的部署，小模型的部署也是有需求的。不过这块就要深挖下了，很多时候涉及C++，难度就上来了，不过这块的hc数就远比不上前面了，而且相对不太好转方向，有的开的价还可以，有的不如算法，但是还是比开发高的，也算是一个可以尝试的方向。tensorrt、ncnn、tvm、onnnx，还有vllm、turbomind、triton这些都是可以学习的。</p><p>这个方向因为hc极少，只攻这一个方向还是不好找工作，但是问题在于，算法工程师的技能点之一是CPP和量化部署这块，包括AI研发岗，我秋招面的很多家都会问到这块学的如何，也问相关的细节，所以这块如果会一些也是有好处的。</p><h2 id="面试内容总结"><a href="#面试内容总结" class="headerlink" title="面试内容总结"></a>面试内容总结</h2><blockquote><h1 id="简历学习内容"><a href="#简历学习内容" class="headerlink" title="简历学习内容"></a>简历学习内容</h1><h2 id="大模型-1"><a href="#大模型-1" class="headerlink" title="大模型"></a>大模型</h2><h3 id="实习相关"><a href="#实习相关" class="headerlink" title="实习相关"></a>实习相关</h3><h4 id="实验评价指标"><a href="#实验评价指标" class="headerlink" title="实验评价指标"></a>实验评价指标</h4><ul><li>正则匹配如何做的</li><li>类别非常不均的情况下 <code>acc</code> 指标是否足够，不足使用了什么</li><li>语义理解的评价指标</li><li>具体指标</li><li>为什么这样用</li><li>这一指标达到多少，什么意义</li><li><code>badcase</code> 有多少，是什么原因，如何处理的</li></ul><h4 id="如何根据实验结果调整-prompt"><a href="#如何根据实验结果调整-prompt" class="headerlink" title="如何根据实验结果调整 prompt"></a>如何根据实验结果调整 <code>prompt</code></h4><h4 id="数据构造"><a href="#数据构造" class="headerlink" title="数据构造"></a>数据构造</h4><ul><li>数据量</li><li>构造方法</li><li>实际上下文长度是多少</li><li>数据截断是什么问题</li></ul><h4 id="数据如何标注的"><a href="#数据如何标注的" class="headerlink" title="数据如何标注的"></a>数据如何标注的</h4><ul><li>真值从哪里来</li><li>标注的数据有什么问题</li></ul><h4 id="采用大模型而不是之前的机器学习方法的意义、优点"><a href="#采用大模型而不是之前的机器学习方法的意义、优点" class="headerlink" title="采用大模型而不是之前的机器学习方法的意义、优点"></a>采用大模型而不是之前的机器学习方法的意义、优点</h4><h4 id="改进和上线所需措施"><a href="#改进和上线所需措施" class="headerlink" title="改进和上线所需措施"></a>改进和上线所需措施</h4><ul><li>模型是否满足上线需要</li><li>如果不满足是因为什么，效果或速度还是其他</li><li>上线需要解决其他什么问题，后续需要做什么</li></ul><h3 id="相关理论"><a href="#相关理论" class="headerlink" title="相关理论"></a>相关理论</h3><ul><li>主流的大模型参数、量级、结构、最大上下文长度，如何选取<ul><li>Llama 系列</li><li>千问</li><li>盘古智子</li><li>chatglm</li><li>internlm</li><li>其他</li><li>openai o1</li></ul></li><li>大模型评测方法</li><li>CoT 和实习中的应用</li><li>微调方法<ul><li>LoRA、QLoRA 等多种方法<ul><li>多头 LoRA</li><li>LoRA</li><li>QLoRA</li></ul></li><li>训练了哪些层</li><li>设置了哪些超参数，如 <code>r</code>、 <code>alpha</code>等</li><li>如何根据实际情况调整这些参数</li></ul></li><li>分布式训练方法<ul><li>deepspeed</li><li>zero</li></ul></li></ul><h3 id="RAG-相关"><a href="#RAG-相关" class="headerlink" title="RAG 相关"></a>RAG 相关</h3><ul><li>原理</li><li>解决什么问题，为什么用 RAG</li><li>RAG 的向量表征方法都有哪些，怎么做的</li></ul><h3 id="视觉"><a href="#视觉" class="headerlink" title="视觉"></a>视觉</h3><h4 id="目标检测"><a href="#目标检测" class="headerlink" title="目标检测"></a>目标检测</h4><ul><li>模型<ul><li>YOLOv1~v11</li><li>各代演进和区别</li><li>总体发展理论</li><li>DETR 等无 <code>nms</code> 的模型</li></ul></li><li>模型架构<ul><li>主干网络的变化和区别</li><li>金字塔融合方式的多种</li><li>中间模块和处理方法</li></ul></li><li>图像增强<ul><li>一般的几种</li><li>mosaic 等多种方法</li></ul></li><li>量化<ul><li>量化方法</li><li>参数量</li><li>速度精度</li></ul></li><li>剪枝<ul><li>方法</li><li>对网络什么部分进行剪枝</li><li>参数量变化</li><li>速度变化</li></ul></li><li>CPP 部分主要包括哪些<ul><li>具体这部分如何用的</li></ul></li></ul><h4 id="人脸识别"><a href="#人脸识别" class="headerlink" title="人脸识别"></a>人脸识别</h4><ul><li>人脸识别和检测部分</li><li>构建索引和检索理论</li><li>向量表征</li><li>相似度检索</li><li>活体识别</li></ul><h3 id="算法八股"><a href="#算法八股" class="headerlink" title="算法八股"></a>算法八股</h3><h4 id="理论"><a href="#理论" class="headerlink" title="理论"></a>理论</h4><ul><li>Transformer 模型讲解</li><li>LayerNorm 等多种 <code>norm</code> 方式如 BatchNorm</li><li>Encoder 和 Decoder 相关</li><li>RNN、LSTM、Transformer 变化</li><li>大模型中激活函数等多种常用的结构<ul><li>GQA</li><li>MQA</li><li>RMSNorm</li><li>SwiGLU</li><li>RoPE</li></ul></li><li>激活函数<ul><li>ReLU</li><li>Leaky ReLU</li><li>GELU</li><li>tanh</li><li>sigmoid</li><li>swish</li><li>ELU</li></ul></li><li>特征工程包括什么，如何做<ul><li>常见的数据预处理方法有哪些</li></ul></li><li>分布式数据处理原理（如 Hadoop）</li><li>计算机理论相关（如堆和栈的区别）</li></ul><h4 id="手撕"><a href="#手撕" class="headerlink" title="手撕"></a>手撕</h4><ul><li>Transformer</li><li>注意力</li><li>LayerNorm</li><li>KMeans</li><li>NMS</li><li>IOU 计算</li></ul></blockquote><h1 id="最后补个学习的内容：算法工程师面试常考手撕题"><a href="#最后补个学习的内容：算法工程师面试常考手撕题" class="headerlink" title="最后补个学习的内容：算法工程师面试常考手撕题"></a>最后补个学习的内容：算法工程师面试常考手撕题</h1><p>引用链接<a href="https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw">https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw</a></p><ul><li>算法工程师面试常考手撕题<ul><li>注意力（Attention）篇<ul><li>手撕单头注意力机制（ScaledDotProductAttention）函数</li><li>手撕多头注意力（MultiHeadAttention）</li><li>手撕自注意力机制函数（SelfAttention）</li></ul></li><li>基础机器学习算法篇<ul><li>手撕 k-means 算法</li></ul></li><li>手撕 Layer Normalization 算法</li><li>手撕 Batch Normalization 算法</li><li>解码算法篇<ul><li>手撕 贪心搜索 （greedy search）</li></ul></li><li>神经网络篇<ul><li>手撕 卷积神经网络(CNN)法</li><li>手撕 二维卷积 算法</li></ul></li><li>位置编码篇<ul><li>手撕 绝对位置编码 算法</li><li>手撕 可学习位置编码 算法</li><li>手撕 相对位置编码 算法</li><li>手撕 rope 算法</li></ul></li><li>面试题汇总</li><li>致谢</li></ul></li></ul><h2 id="注意力（Attention）篇"><a href="#注意力（Attention）篇" class="headerlink" title="注意力（Attention）篇"></a><strong>注意力（Attention）篇</strong></h2><h3 id="手撕单头注意力机制（ScaledDotProductAttention）函数"><a href="#手撕单头注意力机制（ScaledDotProductAttention）函数" class="headerlink" title="手撕单头注意力机制（ScaledDotProductAttention）函数"></a><strong>手撕单头注意力机制（ScaledDotProductAttention）函数</strong></h3><p>输入是query和 key-value，注意力机制首先计算query与每个key的关联性（compatibility），每个关联性作为每个value的权重（weight），各个权重与value的乘积相加得到输出。<br><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444477-8e426dd3-37ed-473f-ab5f-7d445ed7592b.jpeg#clientId=u8884b877-175c-4&from=paste&id=u147acd2d&originHeight=75&originWidth=390&originalType=url&ratio=1&rotation=0&showTitle=false&size=4542&status=done&style=none&taskId=u5130429b-212c-4651-83d6-557bf8c86d9&title=" alt="image.jpg"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">class ScaledDotProductAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot; Scaled Dot-Product Attention &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self, scale):</span><br><span class="line">        super().__init__()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.scale = scale</span><br><span class="line">        self.softmax = nn.Softmax(dim=2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, q, k, v, mask=None):</span><br><span class="line">        u = torch.bmm(q, k.transpose(1, 2)) # 1.Matmul</span><br><span class="line">        u = u / self.scale # 2.Scale</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if mask is not None:</span><br><span class="line">            u = u.masked_fill(mask, -np.inf) # 3.Mask</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn = self.softmax(u) # 4.Softmax</span><br><span class="line">        output = torch.bmm(attn, v) # 5.Output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return attn, output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    n_q, n_k, n_v = 2, 4, 4</span><br><span class="line">    d_q, d_k, d_v = 128, 128, 64</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    q = torch.randn(batch, n_q, d_q)</span><br><span class="line">    k = torch.randn(batch, n_k, d_k)</span><br><span class="line">    v = torch.randn(batch, n_v, d_v)</span><br><span class="line">    mask = torch.zeros(batch, n_q, n_k).bool()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    attention = ScaledDotProductAttention(scale=np.power(d_k, 0.5))</span><br><span class="line">    attn, output = attention(q, k, v, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    print(attn)</span><br><span class="line">    print(output)</span><br></pre></td></tr></table></figure><h3 id="手撕多头注意力（MultiHeadAttention）"><a href="#手撕多头注意力（MultiHeadAttention）" class="headerlink" title="手撕多头注意力（MultiHeadAttention）"></a><strong>手撕多头注意力（MultiHeadAttention）</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line">class MultiHeadAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot; Multi-Head Attention &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self, n_head, d_k_, d_v_, d_k, d_v, d_o):</span><br><span class="line">        super().__init__()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.n_head = n_head</span><br><span class="line">        self.d_k = d_k</span><br><span class="line">        self.d_v = d_v</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.fc_q = nn.Linear(d_k_, n_head * d_k)</span><br><span class="line">        self.fc_k = nn.Linear(d_k_, n_head * d_k)</span><br><span class="line">        self.fc_v = nn.Linear(d_v_, n_head * d_v)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.attention = ScaledDotProductAttention(scale=np.power(d_k, 0.5))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.fc_o = nn.Linear(n_head * d_v, d_o)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, q, k, v, mask=None):</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        n_head, d_q, d_k, d_v = self.n_head, self.d_k, self.d_k, self.d_v</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        batch, n_q, d_q_ = q.size()</span><br><span class="line">        batch, n_k, d_k_ = k.size()</span><br><span class="line">        batch, n_v, d_v_ = v.size()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        q = self.fc_q(q) # 1.单头变多头</span><br><span class="line">        k = self.fc_k(k)</span><br><span class="line">        v = self.fc_v(v)</span><br><span class="line">        q = q.view(batch, n_q, n_head, d_q).permute(2, 0, 1, 3).contiguous().view(-1, n_q, d_q)</span><br><span class="line">        k = k.view(batch, n_k, n_head, d_k).permute(2, 0, 1, 3).contiguous().view(-1, n_k, d_k)</span><br><span class="line">        v = v.view(batch, n_v, n_head, d_v).permute(2, 0, 1, 3).contiguous().view(-1, n_v, d_v)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if mask is not None:</span><br><span class="line">            mask = mask.repeat(n_head, 1, 1)</span><br><span class="line">        attn, output = self.attention(q, k, v, mask=mask) # 2.当成单头注意力求输出</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        output = output.view(n_head, batch, n_q, d_v).permute(1, 2, 0, 3).contiguous().view(batch, n_q, -1) # 3.Concat</span><br><span class="line">        output = self.fc_o(output) # 4.仿射变换得到最终输出</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return attn, output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    n_q, n_k, n_v = 2, 4, 4</span><br><span class="line">    d_q_, d_k_, d_v_ = 128, 128, 64</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    q = torch.randn(batch, n_q, d_q_)</span><br><span class="line">    k = torch.randn(batch, n_k, d_k_)</span><br><span class="line">    v = torch.randn(batch, n_v, d_v_)    </span><br><span class="line">    mask = torch.zeros(batch, n_q, n_k).bool()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    mha = MultiHeadAttention(n_head=8, d_k_=128, d_v_=64, d_k=256, d_v=128, d_o=128)</span><br><span class="line">    attn, output = mha(q, k, v, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    print(attn.size())</span><br><span class="line">    print(output.size())</span><br></pre></td></tr></table></figure><h3 id="手撕自注意力机制函数（SelfAttention）"><a href="#手撕自注意力机制函数（SelfAttention）" class="headerlink" title="手撕自注意力机制函数（SelfAttention）"></a><strong>手撕自注意力机制函数（SelfAttention）</strong></h3><p>Self-Attention。和Attention类似，他们都是一种注意力机制。不同的是Attention是source对target，输入的source和输出的target内容不同。例如英译中，输入英文，输出中文。而Self-Attention是source对source，是source内部元素之间或者target内部元素之间发生的Attention机制，也可以理解为Target&#x3D;Source这种特殊情况下的注意力机制。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">class SelfAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot; Self-Attention &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self, n_head, d_k, d_v, d_x, d_o):</span><br><span class="line">        self.wq = nn.Parameter(torch.Tensor(d_x, d_k))</span><br><span class="line">        self.wk = nn.Parameter(torch.Tensor(d_x, d_k))</span><br><span class="line">        self.wv = nn.Parameter(torch.Tensor(d_x, d_v))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.mha = MultiHeadAttention(n_head=n_head, d_k_=d_k, d_v_=d_v, d_k=d_k, d_v=d_v, d_o=d_o)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.init_parameters()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def init_parameters(self):</span><br><span class="line">        for param in self.parameters():</span><br><span class="line">            stdv = 1. / np.power(param.size(-1), 0.5)</span><br><span class="line">            param.data.uniform_(-stdv, stdv)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, x, mask=None):</span><br><span class="line">        q = torch.matmul(x, self.wq)   </span><br><span class="line">        k = torch.matmul(x, self.wk)</span><br><span class="line">        v = torch.matmul(x, self.wv)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn, output = self.mha(q, k, v, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return attn, output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    n_x = 4</span><br><span class="line">    d_x = 80</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    x = torch.randn(batch, n_x, d_x)</span><br><span class="line">    mask = torch.zeros(batch, n_x, n_x).bool()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    selfattn = SelfAttention(n_head=8, d_k=128, d_v=64, d_x=80, d_o=80)</span><br><span class="line">    attn, output = selfattn(x, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    print(attn.size())</span><br><span class="line">    print(output.size())</span><br></pre></td></tr></table></figure><h2 id="基础机器学习算法篇"><a href="#基础机器学习算法篇" class="headerlink" title="基础机器学习算法篇"></a><strong>基础机器学习算法篇</strong></h2><h3 id="手撕-k-means-算法"><a href="#手撕-k-means-算法" class="headerlink" title="手撕 k-means 算法"></a><strong>手撕 k-means 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">import numpy as np</span><br><span class="line">def kmeans(data, k, thresh=1, max_iterations=100):</span><br><span class="line">  # 随机初始化k个中心点</span><br><span class="line">  centers = data[np.random.choice(data.shape[0], k, replace=False)]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  for _ in range(max_iterations):</span><br><span class="line">    # 计算每个样本到各个中心点的距离</span><br><span class="line">    distances = np.linalg.norm(data[:, None] - centers, axis=2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 根据距离最近的中心点将样本分配到对应的簇</span><br><span class="line">    labels = np.argmin(distances, axis=1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 更新中心点为每个簇的平均值</span><br><span class="line">    new_centers = np.array([data[labels == i].mean(axis=0) for i in range(k)])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 判断中心点是否收敛，多种收敛条件可选</span><br><span class="line">    # 条件1：中心点不再改变</span><br><span class="line">    if np.all(centers == new_centers):</span><br><span class="line">      break</span><br><span class="line">    # 条件2：中心点的阈值小于某个阈值</span><br><span class="line">    # center_change = np.linalg.norm(new_centers - centers)</span><br><span class="line">    # if center_change &lt; thresh:</span><br><span class="line">    #     break</span><br><span class="line">    centers = new_centers</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  return labels, centers</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 生成一些随机数据作为示例输入</span><br><span class="line">data = np.random.rand(100, 2)  # 100个样本，每个样本有两个特征</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 手动实现K均值算法</span><br><span class="line">k = 3  # 聚类数为3</span><br><span class="line">labels, centers = kmeans(data, k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 打印簇标签和聚类中心点</span><br><span class="line">print(&quot;簇标签:&quot;, labels)</span><br><span class="line">print(&quot;聚类中心点:&quot;, centers)</span><br></pre></td></tr></table></figure><h2 id="手撕-Layer-Normalization-算法"><a href="#手撕-Layer-Normalization-算法" class="headerlink" title="手撕 Layer Normalization 算法"></a><strong>手撕 Layer Normalization 算法</strong></h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">from torch import nn</span><br><span class="line"> </span><br><span class="line">class LN(nn.Module):</span><br><span class="line">    # 初始化</span><br><span class="line">    def __init__(self, normalized_shape,  # 在哪个维度上做LN</span><br><span class="line">                 eps:float = 1e-5, # 防止分母为0</span><br><span class="line">                 elementwise_affine:bool = True):  # 是否使用可学习的缩放因子和偏移因子</span><br><span class="line">        super(LN, self).__init__()</span><br><span class="line">        # 需要对哪个维度的特征做LN, torch.size查看维度</span><br><span class="line">        self.normalized_shape = normalized_shape  # [c,w*h]</span><br><span class="line">        self.eps = eps</span><br><span class="line">        self.elementwise_affine = elementwise_affine</span><br><span class="line">        # 构造可训练的缩放因子和偏置</span><br><span class="line">        if self.elementwise_affine:  </span><br><span class="line">            self.gain = nn.Parameter(torch.ones(normalized_shape))  # [c,w*h]</span><br><span class="line">            self.bias = nn.Parameter(torch.zeros(normalized_shape))  # [c,w*h]</span><br><span class="line"> </span><br><span class="line">    # 前向传播</span><br><span class="line">    def forward(self, x: torch.Tensor): # [b,c,w*h]</span><br><span class="line">        # 需要做LN的维度和输入特征图对应维度的shape相同</span><br><span class="line">        assert self.normalized_shape == x.shape[-len(self.normalized_shape):]  # [-2:]</span><br><span class="line">        # 需要做LN的维度索引</span><br><span class="line">        dims = [-(i+1) for i in range(len(self.normalized_shape))]  # [b,c,w*h]维度上取[-1,-2]维度，即[c,w*h]</span><br><span class="line">        # 计算特征图对应维度的均值和方差</span><br><span class="line">        mean = x.mean(dim=dims, keepdims=True)  # [b,1,1]</span><br><span class="line">        mean_x2 = (x**2).mean(dim=dims, keepdims=True)  # [b,1,1]</span><br><span class="line">        var = mean_x2 - mean**2  # [b,c,1,1]</span><br><span class="line">        x_norm = (x-mean) / torch.sqrt(var+self.eps)  # [b,c,w*h]</span><br><span class="line">        # 线性变换</span><br><span class="line">        if self.elementwise_affine:</span><br><span class="line">            x_norm = self.gain * x_norm + self.bias  # [b,c,w*h]</span><br><span class="line">        return x_norm</span><br><span class="line"> </span><br><span class="line"># ------------------------------- #</span><br><span class="line"># 验证</span><br><span class="line"># ------------------------------- #</span><br><span class="line"> </span><br><span class="line">if __name__ == &#x27;__main__&#x27;:</span><br><span class="line"> </span><br><span class="line">    x = torch.linspace(0, 23, 24, dtype=torch.float32)  # 构造输入层</span><br><span class="line">    x = x.reshape([2,3,2*2])  # [b,c,w*h]</span><br><span class="line">    # 实例化</span><br><span class="line">    ln = LN(x.shape[1:])</span><br><span class="line">    # 前向传播</span><br><span class="line">    x = ln(x)</span><br><span class="line">    print(x.shape)</span><br></pre></td></tr></table></figure><h3 id="手撕-Batch-Normalization-算法"><a href="#手撕-Batch-Normalization-算法" class="headerlink" title="手撕 Batch Normalization 算法"></a><strong>手撕 Batch Normalization 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">class MyBN:</span><br><span class="line">    def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        初始化参数值</span><br><span class="line">        :param momentum: 动量，用于计算每个batch均值和方差的滑动均值</span><br><span class="line">        :param eps: 防止分母为0</span><br><span class="line">        :param feat_dim: 特征维度</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        # 均值和方差的滑动均值</span><br><span class="line">        self._running_mean = np.zeros(shape=(feat_dim, ))</span><br><span class="line">        self._running_var = np.ones((shape=(feat_dim, ))</span><br><span class="line">        # 更新self._running_xxx时的动量</span><br><span class="line">        self._momentum = momentum</span><br><span class="line">        # 防止分母计算为0</span><br><span class="line">        self._eps = eps</span><br><span class="line">        # 对应Batch Norm中需要更新的beta和gamma，采用pytorch文档中的初始化值</span><br><span class="line">        self._beta = np.zeros(shape=(feat_dim, ))</span><br><span class="line">        self._gamma = np.ones(shape=(feat_dim, ))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def batch_norm(self, x):</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        BN向传播</span><br><span class="line">        :param x: 数据</span><br><span class="line">        :return: BN输出</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        if self.training:</span><br><span class="line">            x_mean = x.mean(axis=0)</span><br><span class="line">            x_var = x.var(axis=0)</span><br><span class="line">            # 对应running_mean的更新公式</span><br><span class="line">            self._running_mean = (1-self._momentum)*x_mean + self._momentum*self._running_mean</span><br><span class="line">            self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var</span><br><span class="line">            # 对应论文中计算BN的公式</span><br><span class="line">            x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)</span><br><span class="line">        else:</span><br><span class="line">            x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)</span><br><span class="line">        return self._gamma*x_hat + self._beta</span><br></pre></td></tr></table></figure><h2 id="解码算法篇"><a href="#解码算法篇" class="headerlink" title="解码算法篇"></a><strong>解码算法篇</strong></h2><h3 id="手撕-贪心搜索-（greedy-search）"><a href="#手撕-贪心搜索-（greedy-search）" class="headerlink" title="手撕 贪心搜索 （greedy search）"></a><strong>手撕 贪心搜索 （greedy search）</strong></h3><p>贪心搜索（greedy search）在每个时间步 t 都选取当前概率分布中概率最大的词，即<br><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444607-da855304-da72-4fa6-8f9b-fd9cf3440b94.jpeg#clientId=u8884b877-175c-4&from=paste&id=ud89f8e75&originHeight=59&originWidth=273&originalType=url&ratio=1&rotation=0&showTitle=false&size=2641&status=done&style=none&taskId=ub3f3a342-b73d-485d-8964-e14e29f56aa&title=" alt="image.jpg"><br>直到 yt 为或达到预设最大长度时停止生成。<br>贪心搜索本质上是局部最优策略，但并不能保证最终结果一定是全局最优的。由于贪心搜索在解码的任意时刻只保留一条候选序列，所以在搜索效率上，贪心搜索的复杂度显著低于穷举搜索。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">def greedy_decoding(input_ids, max_tokens=300):</span><br><span class="line"> with torch.inference_mode():</span><br><span class="line"> for _ in range(max_tokens):</span><br><span class="line">            outputs = model(input_ids)</span><br><span class="line">            next_token_logits = outputs.logits[:, -1, :]</span><br><span class="line">            next_token = torch.argmax(next_token_logits, dim=-1)</span><br><span class="line"> if next_token == tokenizer.eos_token_id:</span><br><span class="line"> break</span><br><span class="line">            input_ids = torch.cat([input_ids, rearrange(next_token, &#x27;c -&gt; 1 c&#x27;)], dim=-1)</span><br><span class="line">        generated_text = tokenizer.decode(input_ids[0])</span><br><span class="line"> return generated_text</span><br></pre></td></tr></table></figure><h3 id="手撕-Top-K-Sampling算法"><a href="#手撕-Top-K-Sampling算法" class="headerlink" title="手撕 Top-K Sampling算法"></a><strong>手撕 Top-K Sampling算法</strong></h3><p>Top-K 采样（在每个时间步选择条件概率排名前 K 的词语，然后在这 K 个词语中进行随机采样。这种方法既能保持一定的生成质量，又能增加文本的多样性，并且可以通过限制候选词语的数量来控制生成文本的多样性。<br>这个过程使得生成的文本在保持一定的生成质量的同时，也具有一定的多样性，因为在候选词语中仍然存在一定的竞争性。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">def top_k_sampling(input_ids, max_tokens=100, top_k=50, temperature=1.0):</span><br><span class="line"> for _ in range(max_tokens):</span><br><span class="line"> with torch.inference_mode():</span><br><span class="line">            outputs = model(input_ids)</span><br><span class="line">            next_token_logits = outputs.logits[:, -1, :]</span><br><span class="line">            top_k_logits, top_k_indices = torch.topk(next_token_logits, top_k)</span><br><span class="line">            top_k_probs = F.softmax(top_k_logits / temperature, dim=-1)</span><br><span class="line">            next_token_index = torch.multinomial(top_k_probs, num_samples=1)</span><br><span class="line">            next_token = top_k_indices.gather(-1, next_token_index)</span><br><span class="line">            input_ids = torch.cat([input_ids, next_token], dim=-1)</span><br><span class="line">    generated_text = tokenizer.decode(input_ids[0])</span><br><span class="line"> return generated_text</span><br></pre></td></tr></table></figure><h2 id="神经网络篇"><a href="#神经网络篇" class="headerlink" title="神经网络篇"></a><strong>神经网络篇</strong></h2><h3 id="手撕-卷积神经网络-CNN-法"><a href="#手撕-卷积神经网络-CNN-法" class="headerlink" title="手撕 卷积神经网络(CNN)法"></a><strong>手撕 卷积神经网络(CNN)法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torch.nn.functional as F #使用functional中的ReLu激活函数</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#CNN模型</span><br><span class="line">class CNNNet(torch.nn.Module):</span><br><span class="line">    def __init__(self):</span><br><span class="line">        super(CNNNet, self).__init__()</span><br><span class="line">        #两个卷积层</span><br><span class="line">        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)  #1为in_channels 10为out_channels</span><br><span class="line">        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)</span><br><span class="line">        #池化层</span><br><span class="line">        self.pooling = torch.nn.MaxPool2d(2)  #2为分组大小2*2</span><br><span class="line">        #全连接层 320 = 20 * 4 * 4</span><br><span class="line">        self.fc = torch.nn.Linear(320, 10)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, x):</span><br><span class="line">        #先从x数据维度中得到batch_size</span><br><span class="line">        batch_size = x.size(0)</span><br><span class="line">        #卷积层-&gt;池化层-&gt;激活函数</span><br><span class="line">        x = F.relu(self.pooling(self.conv1(x)))</span><br><span class="line">        x = F.relu(self.pooling(self.conv2(x)))</span><br><span class="line">        x = x.view(batch_size, -1)  #将数据展开，为输入全连接层做准备</span><br><span class="line">        x = self.fc(x)</span><br><span class="line">        return x</span><br><span class="line">model = CNNNet()</span><br></pre></td></tr></table></figure><h3 id="手撕-二维卷积-算法"><a href="#手撕-二维卷积-算法" class="headerlink" title="手撕 二维卷积 算法"></a><strong>手撕 二维卷积 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">import numpy as np </span><br><span class="line">def conv2d(img, in_channels, out_channels ,kernels, bias, stride=1, padding=0):</span><br><span class="line">    N, C, H, W = img.shape </span><br><span class="line">    kh, kw = kernels.shape</span><br><span class="line">    p = padding</span><br><span class="line">    assert C == in_channels, &quot;kernels&#x27; input channels do not match with img&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if p:</span><br><span class="line">        img = np.pad(img, ((0,0),(0,0),(p,p),(p,p)), &#x27;constant&#x27;) # padding along with all axis</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    out_h = (H + 2*padding - kh) // stride + 1</span><br><span class="line">    out_w = (W + 2*padding - kw) // stride + 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    outputs = np.zeros([N, out_channels, out_h, out_w])</span><br><span class="line">    # print(img)</span><br><span class="line">    for n in range(N):</span><br><span class="line">        for out in range(out_channels):</span><br><span class="line">            for i in range(in_channels):</span><br><span class="line">                for h in range(out_h):</span><br><span class="line">                    for w in range(out_w):</span><br><span class="line">                        for x in range(kh):</span><br><span class="line">                            for y in range(kw):</span><br><span class="line">                                outputs[n][out][h][w] += img[n][i][h * stride + x][w * stride + y] * kernels[x][y]</span><br><span class="line">                if i == in_channels - 1:</span><br><span class="line">                    outputs[n][out][:][:] += bias[n][out]</span><br><span class="line">    return outputs</span><br></pre></td></tr></table></figure><h2 id="位置编码篇"><a href="#位置编码篇" class="headerlink" title="位置编码篇"></a><strong>位置编码篇</strong></h2><h3 id="手撕-绝对位置编码-算法"><a href="#手撕-绝对位置编码-算法" class="headerlink" title="手撕 绝对位置编码 算法"></a><strong>手撕 绝对位置编码 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">class SinPositionEncoding(nn.Module):</span><br><span class="line">    def __init__(self, max_sequence_length, d_model, base=10000):</span><br><span class="line">        super().__init__()</span><br><span class="line">        self.max_sequence_length = max_sequence_length</span><br><span class="line">        self.d_model = d_model</span><br><span class="line">        self.base = base</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self):</span><br><span class="line">        pe = torch.zeros(self.max_sequence_length, self.d_model, dtype=torch.float)  # size(max_sequence_length, d_model)</span><br><span class="line">        exp_1 = torch.arange(self.d_model // 2, dtype=torch.float)  # 初始化一半维度，sin位置编码的维度被分为了两部分</span><br><span class="line">        exp_value = exp_1 / (self.d_model / 2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        alpha = 1 / (self.base ** exp_value)  # size(dmodel/2)</span><br><span class="line">        out = torch.arange(self.max_sequence_length, dtype=torch.float)[:, None] @ alpha[None, :]  # size(max_sequence_length, d_model/2)</span><br><span class="line">        embedding_sin = torch.sin(out)</span><br><span class="line">        embedding_cos = torch.cos(out)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        pe[:, 0::2] = embedding_sin  # 奇数位置设置为sin</span><br><span class="line">        pe[:, 1::2] = embedding_cos  # 偶数位置设置为cos</span><br><span class="line">        return pe</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">SinPositionEncoding(d_model=4, max_sequence_length=10, base=10000).forward()</span><br></pre></td></tr></table></figure><h3 id="手撕-可学习位置编码-算法"><a href="#手撕-可学习位置编码-算法" class="headerlink" title="手撕 可学习位置编码 算法"></a><strong>手撕 可学习位置编码 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">class TrainablePositionEncoding(nn.Module):</span><br><span class="line">    def __init__(self, max_sequence_length, d_model):</span><br><span class="line">        super().__init__()</span><br><span class="line">        self.max_sequence_length = max_sequence_length</span><br><span class="line">        self.d_model = d_model</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self):</span><br><span class="line">        pe = nn.Embedding(self.max_sequence_length, self.d_model)</span><br><span class="line">        nn.init.constant(pe.weight, 0.)</span><br><span class="line">        return pe</span><br></pre></td></tr></table></figure><h3 id="手撕-相对位置编码-算法"><a href="#手撕-相对位置编码-算法" class="headerlink" title="手撕 相对位置编码 算法"></a><strong>手撕 相对位置编码 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line">class RelativePosition(nn.Module):</span><br><span class="line">    def __init__(self, num_units, max_relative_position):</span><br><span class="line">        super().__init__()</span><br><span class="line">        self.num_units = num_units</span><br><span class="line">        self.max_relative_position = max_relative_position</span><br><span class="line">        self.embeddings_table = nn.Parameter(torch.Tensor(max_relative_position * 2 + 1, num_units))</span><br><span class="line">        nn.init.xavier_uniform_(self.embeddings_table)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, length_q, length_k):</span><br><span class="line">        range_vec_q = torch.arange(length_q)</span><br><span class="line">        range_vec_k = torch.arange(length_k)</span><br><span class="line">        distance_mat = range_vec_k[None, :] - range_vec_q[:, None]</span><br><span class="line">        distance_mat_clipped = torch.clamp(distance_mat, -self.max_relative_position, self.max_relative_position)</span><br><span class="line">        final_mat = distance_mat_clipped + self.max_relative_position</span><br><span class="line">        final_mat = torch.LongTensor(final_mat).cuda()</span><br><span class="line">        embeddings = self.embeddings_table[final_mat].cuda()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return embeddings</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class RelativeMultiHeadAttention(nn.Module):</span><br><span class="line">    def __init__(self, d_model, n_heads, dropout=0.1, batch_size=6):</span><br><span class="line">        &quot;Take in model size and number of heads.&quot;</span><br><span class="line">        super(RelativeMultiHeadAttention, self).__init__()</span><br><span class="line">        self.d_model = d_model</span><br><span class="line">        self.n_heads = n_heads</span><br><span class="line">        self.batch_size = batch_size</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        assert d_model % n_heads == 0</span><br><span class="line">        self.head_dim = d_model // n_heads</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.linears = _get_clones(nn.Linear(d_model, d_model), 4)</span><br><span class="line">        self.dropout = nn.Dropout(p=dropout)</span><br><span class="line">        self.relative_position_k = RelativePosition(self.head_dim, max_relative_position=16)</span><br><span class="line">        self.relative_position_v = RelativePosition(self.head_dim, max_relative_position=16)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.scale = torch.sqrt(torch.FloatTensor([self.head_dim])).cuda()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, query, key, value):</span><br><span class="line">        # embedding</span><br><span class="line">        # query, key, value = [batch_size, len, hid_dim]</span><br><span class="line">        query, key, value = [l(x).view(self.batch_size, -1, self.d_model) for l, x in</span><br><span class="line">                             zip(self.linears, (query, key, value))]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        len_k = query.shape[1]</span><br><span class="line">        len_q = query.shape[1]</span><br><span class="line">        len_v = value.shape[1]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        # Self-Attention</span><br><span class="line">        # r_q1, r_k1 = [batch_size, len, n_heads, head_dim]</span><br><span class="line">        r_q1 = query.view(self.batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)</span><br><span class="line">        r_k1 = key.view(self.batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)</span><br><span class="line">        attn1 = torch.matmul(r_q1, r_k1.permute(0, 1, 3, 2))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        r_q2 = query.permute(1, 0, 2).contiguous().view(len_q, self.batch_size * self.n_heads, self.head_dim)</span><br><span class="line">        r_k2 = self.relative_position_k(len_q, len_k)</span><br><span class="line">        attn2 = torch.matmul(r_q2, r_k2.transpose(1, 2)).transpose(0, 1)</span><br><span class="line">        attn2 = attn2.contiguous().view(self.batch_size, self.n_heads, len_q, len_k)</span><br><span class="line">        attn = (attn1 + attn2) / self.scale</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn = self.dropout(torch.softmax(attn, dim=-1))</span><br><span class="line">        # attn = [batch_size, n_heads, len, len]</span><br><span class="line">        r_v1 = value.view(self.batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)</span><br><span class="line">        weight1 = torch.matmul(attn, r_v1)</span><br><span class="line">        r_v2 = self.relative_position_v(len_q, len_v)</span><br><span class="line">        weight2 = attn.permute(2, 0, 1, 3).contiguous().view(len_q, self.batch_size * self.n_heads, len_k)</span><br><span class="line">        weight2 = torch.matmul(weight2, r_v2)</span><br><span class="line">        weight2 = weight2.transpose(0, 1).contiguous().view(self.batch_size, self.n_heads, len_q, self.head_dim)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        x = weight1 + weight2</span><br><span class="line">        # x = [batch size, n heads, query len, head dim]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        x = x.permute(0, 2, 1, 3).contiguous()</span><br><span class="line">        # x = [batch size, query len, n heads, head dim]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        x = x.view(self.batch_size * len_q, self.d_model)</span><br><span class="line">        # x = [batch size * query len, hid dim]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return self.linears[-1](x)</span><br></pre></td></tr></table></figure><h3 id="手撕-rope-算法"><a href="#手撕-rope-算法" class="headerlink" title="手撕 rope 算法"></a><strong>手撕 rope 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torch.nn as nn</span><br><span class="line">import torch.nn.functional as F</span><br><span class="line">import math</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># %%</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def sinusoidal_position_embedding(batch_size, nums_head, max_len, output_dim, device):</span><br><span class="line">    # (max_len, 1)</span><br><span class="line">    position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(-1)</span><br><span class="line">    # (output_dim//2)</span><br><span class="line">    ids = torch.arange(0, output_dim // 2, dtype=torch.float)  # 即公式里的i, i的范围是 [0,d/2]</span><br><span class="line">    theta = torch.pow(10000, -2 * ids / output_dim)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (max_len, output_dim//2)</span><br><span class="line">    embeddings = position * theta  # 即公式里的：pos / (10000^(2i/d))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (max_len, output_dim//2, 2)</span><br><span class="line">    embeddings = torch.stack([torch.sin(embeddings), torch.cos(embeddings)], dim=-1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, max_len, output_dim//2, 2)</span><br><span class="line">    embeddings = embeddings.repeat((batch_size, nums_head, *([1] * len(embeddings.shape))))  # 在bs维度重复，其他维度都是1不重复</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, max_len, output_dim)</span><br><span class="line">    # reshape后就是：偶数sin, 奇数cos了</span><br><span class="line">    embeddings = torch.reshape(embeddings, (batch_size, nums_head, max_len, output_dim))</span><br><span class="line">    embeddings = embeddings.to(device)</span><br><span class="line">    return embeddings</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># %%</span><br><span class="line">def RoPE(q, k):</span><br><span class="line">    # q,k: (bs, head, max_len, output_dim)</span><br><span class="line">    batch_size = q.shape[0]</span><br><span class="line">    nums_head = q.shape[1]</span><br><span class="line">    max_len = q.shape[2]</span><br><span class="line">    output_dim = q.shape[-1]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, max_len, output_dim)</span><br><span class="line">    pos_emb = sinusoidal_position_embedding(batch_size, nums_head, max_len, output_dim, q.device)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # cos_pos,sin_pos: (bs, head, max_len, output_dim)</span><br><span class="line">    # 看rope公式可知，相邻cos，sin之间是相同的，所以复制一遍。如(1,2,3)变成(1,1,2,2,3,3)</span><br><span class="line">    cos_pos = pos_emb[...,  1::2].repeat_interleave(2, dim=-1)  # 将奇数列信息抽取出来也就是cos 拿出来并复制</span><br><span class="line">    sin_pos = pos_emb[..., ::2].repeat_interleave(2, dim=-1)  # 将偶数列信息抽取出来也就是sin 拿出来并复制</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # q,k: (bs, head, max_len, output_dim)</span><br><span class="line">    q2 = torch.stack([-q[..., 1::2], q[..., ::2]], dim=-1)</span><br><span class="line">    q2 = q2.reshape(q.shape)  # reshape后就是正负交替了</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 更新qw, *对应位置相乘</span><br><span class="line">    q = q * cos_pos + q2 * sin_pos</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    k2 = torch.stack([-k[..., 1::2], k[..., ::2]], dim=-1)</span><br><span class="line">    k2 = k2.reshape(k.shape)</span><br><span class="line">    # 更新kw, *对应位置相乘</span><br><span class="line">    k = k * cos_pos + k2 * sin_pos</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    return q, k</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># %%</span><br><span class="line">def attention(q, k, v, mask=None, dropout=None, use_RoPE=True):</span><br><span class="line">    # q.shape: (bs, head, seq_len, dk)</span><br><span class="line">    # k.shape: (bs, head, seq_len, dk)</span><br><span class="line">    # v.shape: (bs, head, seq_len, dk)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if use_RoPE:</span><br><span class="line">        q, k = RoPE(q, k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    d_k = k.size()[-1]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    att_logits = torch.matmul(q, k.transpose(-2, -1))  # (bs, head, seq_len, seq_len)</span><br><span class="line">    att_logits /= math.sqrt(d_k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if mask is not None:</span><br><span class="line">        att_logits = att_logits.masked_fill(mask == 0, -1e9)  # mask掉为0的部分，设为无穷大</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    att_scores = F.softmax(att_logits, dim=-1)  # (bs, head, seq_len, seq_len)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if dropout is not None:</span><br><span class="line">        att_scores = dropout(att_scores)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, seq_len, seq_len) * (bs, head, seq_len, dk) = (bs, head, seq_len, dk)</span><br><span class="line">    return torch.matmul(att_scores, v), att_scores</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &#x27;__main__&#x27;:</span><br><span class="line">    # (bs, head, seq_len, dk)</span><br><span class="line">    q = torch.randn((8, 12, 10, 32))</span><br><span class="line">    k = torch.randn((8, 12, 10, 32))</span><br><span class="line">    v = torch.randn((8, 12, 10, 32))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    res, att_scores = attention(q, k, v, mask=None, dropout=None, use_RoPE=True)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, seq_len, dk),  (bs, head, seq_len, seq_len)</span><br></pre></td></tr></table></figure><pre><code>print(res.shape, att_scores.shape)</code></pre><h2 id="面试题汇总"><a href="#面试题汇总" class="headerlink" title="面试题汇总"></a><strong>面试题汇总</strong></h2><ul><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483957&idx=1&sn=abec4b75b9865b754f8a303c340c13a3&scene=21#wechat_redirect">大模型微调的经验与感想分享</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483942&idx=1&sn=a5ba1da8459df0b76e1ea70bfa4dc068&scene=21#wechat_redirect">百度-NLP算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483919&idx=1&sn=c9a530ecce9e60af4fad4c06062ec9ce&scene=21#wechat_redirect">美团-大模型算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483896&idx=1&sn=6b79f7eb585cc1d91a1f61010941477c&scene=21#wechat_redirect">小米-NLP算法工程师面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483884&idx=1&sn=e1f4d13589606786f2d2467e11b4e2dc&scene=21#wechat_redirect">好未来-NLP算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483862&idx=1&sn=0dc0ee080532d397b2b00bdd20c86260&scene=21#wechat_redirect">百度大模型算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483853&idx=2&sn=f717767538329ce17325de72aa58ba1b&scene=21#wechat_redirect">昆仑天工大模型算法工程师</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483839&idx=1&sn=b66447f92f4dbfa8be7922f53aa8ba4b&scene=21#wechat_redirect">阿里大模型算法工程师一面</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483790&idx=1&sn=308fb18b66cc66b78f7e15822cdd6eff&scene=21#wechat_redirect">算法工程师面试常考手撕题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483773&idx=1&sn=003c347fc05e1a3fa4328ac09dddb797&scene=21#wechat_redirect">搜狐大模型算法工程师</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483757&idx=1&sn=79394fd14e39948d1fc98aa09e031561&scene=21#wechat_redirect">字节大模型算法实习生</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483750&idx=1&sn=18d9c270e8d58a32dc4792fbc5f8f6e8&scene=21#wechat_redirect">理想汽车大模型算法实习生</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483745&idx=1&sn=ee37c895b25bf2a1f8387edf1d687e30&scene=21#wechat_redirect">百度大模型算法实习生面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483731&idx=1&sn=08cb4b390e80f3ca4a1e0fa2dd5a3020&scene=21#wechat_redirect">腾讯大模型算法实习生面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483723&idx=1&sn=baa9b82a7ac4f12e936ff8b58dcf8977&scene=21#wechat_redirect">阿里大模型算法工程师一面</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483713&idx=1&sn=c90af03630f92999eed214d5dc9f06a3&scene=21#wechat_redirect">某大厂大模型算法工程师面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483697&idx=1&sn=82e8cbb46aa2a0a656ae6f76ed225b03&scene=21#wechat_redirect">说说百度大模型算法工程师二面经历</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483686&idx=1&sn=79b3d0eb8a034cf7fe8746cd5e362899&scene=21#wechat_redirect">阿里大模型算法工程师面试小结</a></li></ul><h2 id="致谢"><a href="#致谢" class="headerlink" title="致谢"></a><strong>致谢</strong></h2><ul><li>LLMs 千面郎君 更新版 <a href="https://mp.weixin.qq.com/s/C6NdO_Ebj3DQx2AVAAgQRQ">https://mp.weixin.qq.com/s/C6NdO_Ebj3DQx2AVAAgQRQ</a></li><li>LLMs九层妖塔 <a href="https://mp.weixin.qq.com/s/Eh0tY1zx2FqXQqIGa2dIBA">https://mp.weixin.qq.com/s/Eh0tY1zx2FqXQqIGa2dIBA</a></li><li>NLP 面无不过 <a href="https://github.com/km1994/NLP-Interview-Notes">https://github.com/km1994/NLP-Interview-Notes</a></li></ul><blockquote><p>来自: <a href="https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw">算法工程师面试常考手撕题（更新）</a></p></blockquote>]]>
    </content>
    <id>http://jackzhu.top/2024/11/30/%E7%A7%8B%E6%8B%9B%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/11/30/%E7%A7%8B%E6%8B%9B%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93%E5%88%86%E4%BA%AB/"/>
    <published>2024-11-30T00:27:37.000Z</published>
    <summary>秋招结束后的面试总结分享。</summary>
    <title>秋招面试总结分享</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>今天9.30面的金山，金山这个面试基本是我面试过程最难的几个了，本来面试计划时间半小时，结果面了一个小时，面试官非常深挖项目，问的非常细，而且非常的耐心，里面就问了两个八股，看来要准备的还真的需要很多。</p><p>首先是自我介绍，然后问了下我的实习，第一部分主要是针对实习的具体内容展开，我先讲完做了啥，</p><p>用了什么模型，参数是多少，量级是多少，为什么用千问，盘古智子，千问大模型版本，结构，最大上下文长度是多少，怎么评测，选取哪个大模型。大模型训练出来的结果如何评价，用的什么指标。</p><p>LoRA微调原理，训练哪些层，都哪些超参数，超参数怎么设置的。</p><p>前面这块我了解的不是特别深入，然后就开始讲Prompt这块关于具体任务的内容，数据集怎么构造的，为什么这么构建，标注数据有什么问题，数据量多少，这个项目最终的好坏如何评价。</p><p>后面上线需要解决什么问题，是效果达不到，还是速度达不到，还是其他问题，千问的模型是否可以上线，解决方案是什么，后续怎么做。</p><p>还问了大模型的分布式这块了解不，我问是不是deepspeed这种，是，这块说了两句，不是特别懂。</p><p>然后问我后面哪个项目感觉做的比较深入，然后我说了第一个项目做的深入而且有论文，然后他就开始问了我最后一个项目，这个项目做的比较浅。问到这个项目，量化和剪枝如何做的，参数量是多少，剪枝之后是多少，速度提升多少。量化如何做的，量化之后的参数量是多少，速度提升多少。其中的CPP需要写哪些部分。</p><p>最后是八股，问了下现在各个大模型的架构是啥，主流都是哪些模型，都是啥样的。除了Transformer之外还了解mamba和kan不。</p><p>反问问了他们主要是大模型还是传统，说都有。</p><p>被拷打的很惨，感觉这次面试是最难的，感觉肯定G了。</p><p>结果没想到，过了，这次面试真的是很难，感觉自己很多地方都答的不好，但是还是过了，感觉有点意外。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p>10.15今天金山二面，面了一个小时，过去自我介绍，然后面试官说让找一个印象最深的项目深入的讲。</p><p>然后我就讲了我做的第一个项目，也有论文，就屏幕共享讲论文，这次讲比第一次讲论文好多了，大致是讲清楚了，虽然后面面试官说可延展性不太行，不过确实，一个横向项目论文，确实挺水的，但也没办法。</p><p>然后是手撕，还是屏幕共享，让打开IDE，然后我Pycharm手撕一道题，这次问的是数组中找两个数之和，使其和target之差最小，然后用的双指针，说了下思路，然后写了出来，然后问算法复杂度，是O(nlogn)，然后是延伸，是否能用二分查找法做，如果这样做如何来设计，然后我说了下，应该说的没问题，这块也追问了好几个，比如两个数的时候有没有啥问题，边界条件是不是对，差不多都答上来了。</p><p>然后是一个八股，问大模型和小模型的区别，这块见得多了，从实用角度，可解释性，成本，大模型幻觉啥的展开讲，基本没啥问题，面试官差不多还夸了下。</p><p>然后是反问环节，问部门做啥，大模型还是小模型，都有，然后是base，问我报的哪里，他们这边有珠海、武汉、北京，后面会培训然后协商部门和base地。</p><p>然后是问我有没有其他offer，我说有几个在排序中，还在看。</p><p>总体来说感觉也还行，应该是过了吧。</p><h2 id="三面HR面"><a href="#三面HR面" class="headerlink" title="三面HR面"></a>三面HR面</h2><p>11.5HR打电话，问我有没有时间，我说有，然后就直接电话面了。</p><p>首先问了下我实习7~9月华子实习，问结束了没，是结束了。然后问为什么没有华子内部转正，我说华子的不能转正，而且只有特别的可以评A直通三面，但是我一起的都是C9的，然后问那个部门招多少人，这个我不清楚。然后问华子比例多少，我说那个比例不超过20%。然后问实习为什么时间短，没想着转正或者没有机会吗，还是不喜欢工作内容。我说一方面前面在学校老师安排的活和毕设中期，就拖到了7.3；另一方面九月份就要秋招了，后面就没时间了，而且华子本身也不能转正。</p><p>然后问都拿到了哪些公司的offer或者哪家公司已经走到了后面的流程了，我说现在有几家已经到二面三面了，但是地方还没看好，更想去粤港澳地方。然后问头的是珠海，是什么原因呢有家里家人朋友在这边吗？我说不是，因为更看好这边的发展，我家里是河南，但是不是很想回去，粤港澳这边中国发展比较好，从未来考虑。然后问哪些公司比较后面了，我说讯飞理想都在后面了，但是还没有发offer，然后问他们的地点是哪里，我说还没仔细看。</p><p>然后问对未来工作的规划，你应聘的是算法，我说一直在做AI相关的，这块也比较了解。一方面从发展维度来说，进去前两年做技术，后面再考虑带团队，从学校和公司的差异。另一方面从就业的具体内容来说也希望更好的用到前面学的人工智能方面的知识，其他的也是可以快速学习的。然后我问这边工作是偏研发还是工程，他们说偏向工程产品落地。</p><p>然后问前面面试的过程中觉得他们关注的一些方向以及他们去问的一些问题跟您未来的那个发展的规划相符吗。我说基本还是可以。对于他们关注等一些内容有哪些方面女士没有涉及到或者是没有了解和学习接触过的吗？我说主要是从学校和公司的差异，对于性能效率方面的一些差异吧，这个我举了个例子来说的。</p><p>然后问：目前在学校期间的话是在做毕业论文还是做一些什么样的方向的一些学习吗？目前主要是秋招，后面做毕设，也是AI方面的。最近的话最近肯定是还是以秋为主，就是现在还没有就是还没有拿到不错的offer，暂时还没有走到那个offer这一步。</p><p>然后问薪酬的预期，我说我也不知道下面咱们这个行情到底应该是个什么情况。那你身边的同学有啊拿到什么样的一些offer你有了解到吗？我大致说了下，很多在等华为。然后问我为什么不是很想去华子，我说华子应该不算互联网。然后问我身边小米美团他们的薪酬大致多少，我说年三十多到四十多，具体也要看地方。然后问我珠海如何，我说年薪也要30多以上吧，也不知道行情如何。然后问珠海的薪资和深圳的薪资比较比较下来的话会有什么样的差异吗？我说肯定比深圳低。</p><p>然后HR说这边暂时没有其他问题了，晚一点确定录取和信息待遇什么的回尽快打电话，然后反问环节，我就问了下地方，武汉珠海是一样的，可以后面转，然后就结束了。<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/7162652ed15d942a69859975d27f38d.png" alt="7162652ed15d942a69859975d27f38d"></p><h2 id="oc"><a href="#oc" class="headerlink" title="oc"></a>oc</h2><p>11.6早晨HR打电话，谈了下薪酬福利，工作时间，这种类似的。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/11/06/%E7%A7%8B%E6%8B%9B%E9%87%91%E5%B1%B1%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/11/06/%E7%A7%8B%E6%8B%9B%E9%87%91%E5%B1%B1%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-11-06T11:40:37.000Z</published>
    <summary>秋招正式批金山技术面试分享。</summary>
    <title>秋招金山面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>11.1下午这次面的是中金所的子公司，总共就面了15分钟，几乎没问技术。</p><p>过去首先自我介绍，然后问了下华为实习是不是能转正，我说不能，然后说现在华子秋招还没开。</p><p>然后问我是否了解这个公司，我说是金融方面的，具体还不是很了解。</p><p>问我最近在看什么书，我说最近秋招，所以在看人工智能方面的书，根据不不同的阶段和任务看书。</p><p>然后问我找工作有多个offer的时候看重什么，我说第一看公司的发展，第二个人能力的培养，第三个是薪资福利。</p><p>然后让我找一个印象深刻的经历，展开讲讲，我就讲了下实习的内容，讲完他都没问问题，直接就到了反问环节。</p><p>我就反问部门具体是什么业务，哪方面的，他说前沿技术岗，一个是跟踪行业发展，探索落地，各种大模型区块链都有涉及，这个部门是运维的，就是前沿技术在这块的应用。</p><p>然后我问了下后面还有几面，他说如果一面过了的话二面是具体岗位相关的技术，三面是HR面。</p><p>总体来说还是太简短了，相比上午面一个小时还是差很多。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/11/01/%E7%A7%8B%E6%8B%9B%E4%B8%AD%E9%87%91%E6%89%80%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/11/01/%E7%A7%8B%E6%8B%9B%E4%B8%AD%E9%87%91%E6%89%80%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-11-01T14:39:37.000Z</published>
    <summary>秋招正式批中金所面试分享。</summary>
    <title>秋招中金所面试分享</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>这次面的是蔚来，开始面试官很热情，上来介绍他们部门是自动驾驶的芯片的，然后我自我介绍，然后就开始问问题了。这次面了一个小时，可以说底裤都被扒的干干净净，好多问题都没回答上来，感觉是要G了。</p><p>这次没问实习，毕竟实习是做大模型的，这边完全不做大模型了，纯视觉，所以问的我的项目。</p><p>首先第一个项目，这个是我研究生做的第一个项目，讲完问了效果误差如何，是否满足需要，我说误差1%差不多是可以用了，而且成本低，这块简单问了下，还问了整个过程都用了哪些模型。又问了我用的MMPose框架内部是怎么实现的，这个框架我还真不是特别了解，只是用过，所以回答的不是很好。然后问我这块目标检测用的模型，这块是项目中很小的一个点，我都没准备，结果问到了，我就回答不好了。</p><p>然后第二个项目，这个讲完主体后，让我讲下这个过程用了什么模型，我讲了下还有效果。然后问实时性什么的是否满足需要，我就讲了下整体的流程架构。然后问这个项目的难点是什么，我说主要应该是解决具体的问题。然后问我有没有做边缘部署，我说这个本来有这个需求，但是后面没谈好，就没做了。还问了项目指标到多少，评价指标如何设计的，这块也是回答了个大概。</p><p>然后问第三个项目，这个项目做的浅，问了前面部分用的什么做的，这块用的接口做的，只做了后面的不难的部分，大致说了下，也是说的很水。</p><p>然后问第四个项目，我把整个流程说了下，然后说了下主要的难点和处理方法，也是大致说了下就完了。</p><p>然后开始问卷积内部实现和计算，这块我大致说了，然后又问了参数量规模，大致说了下，回答的一般。然后问我NMS怎么算的，我大致把理论说了下，然后问IOU怎么算的，这个比较容易，我还会手写。</p><p>然后问Python相关的，问了下*args和**kargs什么区别，这块大致说出来了。</p><p>然后问浅拷贝和深拷贝有什么区别，python中有哪些数据类型是浅拷贝，哪些是深拷贝。第一个浅拷贝和深拷贝大致说了下，第二个还真不知道。</p><p>然后问CPP相关的，问虚函数和纯虚函数什么区别，泛型编程是什么，模板是什么。这些就真不会了。</p><p>然后反问，我就问了下部门业务，然后他问我愿不愿意做这种比较底层的，我说还是很有兴趣的，然后就结束了。</p><p>感觉这次面试官开始还是很热情的，但是我回答的不太行，感觉是要G了，这些问题都太底层了，还有很多八股，我都没准备，感觉是要准备下这些问题了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/11/01/%E7%A7%8B%E6%8B%9B%E8%94%9A%E6%9D%A5%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/11/01/%E7%A7%8B%E6%8B%9B%E8%94%9A%E6%9D%A5%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-11-01T12:08:37.000Z</published>
    <summary>秋招正式批蔚来面试分享。</summary>
    <title>秋招蔚来面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><ol start="10"><li></li></ol><p>30下午面试的广联达，这是个做建筑设计的公司，过去自我介绍完，他们好像主要是偏视觉一些，所以先问了我第一个项目，讲完之后追问了一些细节，包括问用的什么模型，模型的结构是什么。然后其他的问了下实习的内容，我就把实习的内容讲了一遍，他就问了下相比之前的效果如何，我说主要是业务流程上的改进，用大模型来完成这些事情。</p><p>之后他想问八股，我说计组计网操作系统编译原理都没学过，他就问了下如何处理类别不均的问题，这个我举了我实习时候的例子，再结合了之前做一般小模型时候的一些处理方法，给出了一些回答。</p><p>然后是问他们部门，他介绍了下他们公司主要偏视觉一些，是建筑信息化方面的公司，涉及图文生成和3DGS之类的，我也聊了下先关的内容。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/31/%E7%A7%8B%E6%8B%9B%E5%B9%BF%E8%81%94%E8%BE%BE%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/31/%E7%A7%8B%E6%8B%9B%E5%B9%BF%E8%81%94%E8%BE%BE%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-31T00:04:37.000Z</published>
    <summary>秋招正式批广联达技术面试分享。</summary>
    <title>秋招广联达</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面AI面"><a href="#一面AI面" class="headerlink" title="一面AI面"></a>一面AI面</h2><p>虽然是AI面试，但是还是记录一下，面试的内容，这个相当于是综合面试的题提前到了技术面之前了。这些内容还是需要总结一下，也是后面面试可能会问的问题。</p><p>具体有7个题分别需要录制两分钟的视频，第一个是自我介绍，包含学校、实习、项目、技能、兴趣爱好这些部分。</p><p>后面有印象最深的项目是什么，具体哪个？</p><p>第五个题目是如何促成他人意见和自己达成一致，会因此感到压力吗？</p><p>第六个题目是你制定过最成功的计划是什么？</p><p>最后一个题目是简述自己的优势和劣势和未来的职业规划？</p><p>这些题目有30秒的思考时间，难度有一些的，还是要提前准备一下的。</p><h2 id="二面人工面"><a href="#二面人工面" class="headerlink" title="二面人工面"></a>二面人工面</h2><p>这次面试大概35分钟，基本问的也算全，不过后面的都没问。线上腾讯会议面试，有两个面试官，第一个面的时候第二个听。</p><p>第一个面试官，过去先自我介绍，然后讲实习做的啥，先讲了背景，然后按照要求说了输入输出和目的，只讲了一个点，中间问了很多细节，数据多少条，正负样本配比是否合适。用的啥模型训练的，现在主流的大模型结构是什么样的，这个我都没回答完，就打断了。然后问我大模型在银行场景中如何应用，我就说了下目前主要在知识库和智能客服结合RAG和Agent这块做的，其他倒是不太多。然后问了我是否会开发，问技术栈，我说主要会Python和Pytorch，C++只会一点，还有就是机器学习和深度学习算法，比如Kmeans啥的。Java不太会，不过学起来应该也问题不大。然后问我是否了解SQL，我说本科用过，速成了一下增删查改，后面没有用过就不太懂了，不过要学应该挺快的。</p><p>然后让第二个面试官问，面试官首先说你既然比较了解Pytorch，那Pytorch中的基本数据结构是什么，我说了下Tensor，他问和numpy中的有什么区别，我说主要是需要计算梯度和更新参数，所以主要是梯度累计记录这块的区别。然后问我前向传播和反向传播有什么区别，简单说了下更新参数和梯度这块的内容。然后说你刚才说Kmeans，那你说一下这个算法，我就介绍了下原理，然后问我距离计算有哪些类型，我就说了下L1和L2欧式距离，还有0范数和无穷范数啥的。然后他问我如何确定聚类数目，我说这是个超参数，不过其他聚类如DBSCAN不需要指定聚类个数，如果Kmeans需要预估的话，需要进行数据分析一下，大致预估。然后是问我大模型在银行中如何应用落地，这块我也是只能从知识库智能客服这块说了下，然后就是结合华为实习的风控场景类似银行场景中也可以进行应用。这个开放性问题其实我回答的不算出彩，感觉这个问题很多领导还是很看重的，还是需要重点准备一下。</p><p>然后是反问，我就说了下我技术栈过去是否适配适应，面试官说这个数据岗主要细分数据分析和人工智能大模型算法应用，然后就结束了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/25/%E7%A7%8B%E6%8B%9B%E4%B8%8A%E6%B5%B7%E9%93%B6%E8%A1%8C%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/25/%E7%A7%8B%E6%8B%9B%E4%B8%8A%E6%B5%B7%E9%93%B6%E8%A1%8C%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-25T14:49:37.000Z</published>
    <summary>秋招正式批上海银行面试分享。</summary>
    <title>秋招上海银行面试分享</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>这次面试是电话面试，大概40分钟。</p><p>首先自我介绍，然后问实习，讲完问的问题有，大模型相比传统业务的优势， 样本的评测标准是否和之前的一样，这个项目后面是否可以做成纯大模型提取学习，等等之类的。问了这些就是八股了，现在主流都有哪些大模型，他们的结构是啥样的，我介绍完问ChatGLM为什么不是纯Decoder的，具体是采用了什么结构，这块我就答不上来了。</p><p>然后是问后面的第一个项目，有论文这个，把大致的内容讲了下，这块没怎么问。</p><p>然后就问到最后一个项目，说其中的量化剪枝这块怎么做的，这块我讲的不太清楚，涉及CPP的这块讲了下，还有部署和服务这块怎么做的大致讲了下。</p><p>然后反问，问具体部门做啥，具体是大模型的工程应用团队，然后就结束了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/21/%E7%A7%8B%E6%8B%9B%E7%99%BE%E4%BF%A1%E9%93%B6%E8%A1%8C%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/21/%E7%A7%8B%E6%8B%9B%E7%99%BE%E4%BF%A1%E9%93%B6%E8%A1%8C%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-21T21:35:37.000Z</published>
    <summary>秋招正式批百信银行面试分享。</summary>
    <title>秋招百信银行面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>这次面了33分钟，过去他先简单介绍他们情况，搜广推部门啥的，自我介绍完，先问了实习做的啥，然后我讲完了点，然后面试官说后面不用讲了，然后开始问，相对之前相比的优点，也问了一些里面的细节，主要还是业务流程方面的，没有仔细问模型本身相关的东西。然后是关于后面是否能上线，有啥问题。还问了大模型实际提高速度如何做，我说选用性能足够的小模型，量化剪枝蒸馏。</p><p>后面的项目好像没问，毕竟确实也不怎么相关。</p><p>然后说既然是搜广推，出个算法题，模型预测的一个排序和实际的排序，如[1,3,2,5,4]和实际的顺序[1,2,3,4,5]，设计一个函数给模型的输出打分，这块我大致思考了下，说了个思路，然后让实现，我就开始写</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    n = <span class="built_in">len</span>(rank1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    rank1_dict = &#123;value: idx <span class="keyword">for</span> idx, value <span class="keyword">in</span> <span class="built_in">enumerate</span>(rank1)&#125;</span><br><span class="line">    rank2_dict = &#123;value: idx <span class="keyword">for</span> idx, value <span class="keyword">in</span> <span class="built_in">enumerate</span>(rank2)&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    loss_sum = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> value <span class="keyword">in</span> rank1:</span><br><span class="line">        loss = rank1_dict[value] - rank2_dict[value]  <span class="comment"># 计算排名差</span></span><br><span class="line">        loss_sum += loss ** <span class="number">2</span>  <span class="comment"># 差值平方累计</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>写了这些，然后分数差不多就是损失归一化然后反过来，然后就差不多了。</p><p>然后反问环节，我就问了下我技术栈过去转换是否大，面试官说他们也有大模型岗位，我说大模型不一定能火多少年，不过搜广推是互联网的基础，更看好一些，面试官让回去了解一下搜广推，上手做下项目啥的，毕竟是不能只靠兴趣，我说回去看看。</p><p>总体来看，这是第一个投搜广推给面的，虽然我也基本没怎么投搜广推，感觉面的也凑合，不过匹配程度确实是个问题。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/19/%E7%A7%8B%E6%8B%9B%E8%99%BE%E7%9A%AE%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/19/%E7%A7%8B%E6%8B%9B%E8%99%BE%E7%9A%AE%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-19T18:39:37.000Z</published>
    <summary>秋招正式批虾皮面试分享。</summary>
    <title>秋招虾皮面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>这次面试的时间还挺短的，应该是22分钟，过去自我介绍完，先问了实习做的啥，然后大致讲了讲，然后问效果和之前的比如何，然后问了一些细节，是否上线。</p><p>然后问哪个项目做的深，第一个项目讲了讲，讲了大致背景和做法，都没有讲具体的结构流程，问了几个问题，包括数据、标注，和之前的比做的意义，做出的效果啥的，还有这个项目是不是自己做的，在其中什么角色。</p><p>然后问我如何职业规划和方向，想做啥，我说这些都比较通，都可以做。然后是城市的问题，这些城市我都差不多可以接受。</p><p>然后问我了不了解他们公司，这块我还真不怎么了解。然后问我有没有其他的offer，我说有几个在流程中。</p><p>最后反问，问了公司具体的业务，他们这个包括的比较多，CV、NLP大模型、语音这些都做。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/19/%E7%A7%8B%E6%8B%9B%E6%8B%9B%E8%81%94%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/19/%E7%A7%8B%E6%8B%9B%E6%8B%9B%E8%81%94%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-19T15:39:37.000Z</published>
    <summary>秋招正式批招联面试分享。</summary>
    <title>秋招招联面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>15号的比较早，9：30左右，等了一会，叫号，过去面试，自我介绍完，先拿出来了笔试时候的题目让复盘一下，我用的还是投实习时候过的，正式批就没有笔试，简单看下，大致说出了思路。</p><p>然后说重点讲三个项目，我就讲一个实习+两个项目了。</p><p>开始问实习，也是介绍完之后问细节，具体也问了不少，然后讲第一个和第二个项目，讲完让手撕个题目，题目是一段01的比特流字符串，给一个target是0或者1，然后比特流只能变一位，求最多多少个连续的target，想了几分钟双指针写了出来。总体来说，一面还是比较友好的。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p>等了一会就二面了，二面过去自我介绍完，我讲完实习，就问了几个套路性的常用问题，大概不到十分钟，然后就给一个手撕题目，场景题，用卷积算二阶导，对图像做边缘检测。这个没怎么见过，没想出来，要用题目给定的方法做，大致写了下，然后就结束了，写的应该不太对。面试官直接说今天面试结束了可以回去了，然后就短信通知今天的结束了，进入系统一看，果然挂了。</p><p>这种手撕场景题不太好准备，没得办法，二面都没怎么问，手撕g就g了，这题真没见过，太难了，全程都没问八股，二面一堆项目都没问，实习问的比一面还浅，没办法。</p><p>我回头问了我我同学们，还没有见过有二面挂的，他们题目都可以三选一，而且基本都是力扣，还都不是很难的那种，出这个题他们也都不会，让面试官给提示也不给，只报还剩下多少时间。毕竟连项目和八股都不问的面试我还是第一次见，一面的面试官就好的多。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/3d0afaab4a49212c06ee3d91bc12e82.jpg" alt="3d0afaab4a49212c06ee3d91bc12e82"></p><p>我回去复盘了一下，这个题大致应该是这样的，总体来说也不能算完全的hard，但是难就难在开始这部分，如何用[1,-1]和$[1,-1]^T$计算拉普拉斯算子，后面的就很容易了。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20241017224856.png" alt="20241017224856"></p><p>ChatGPT给的答案是：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">edge_detection_laplacian</span>(<span class="params">image</span>):</span><br><span class="line">    <span class="comment"># 获取图像尺寸</span></span><br><span class="line">    rows, cols = <span class="built_in">len</span>(image), <span class="built_in">len</span>(image[<span class="number">0</span>])</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 定义Laplacian卷积核g</span></span><br><span class="line">    g = [[<span class="number">0</span>, -<span class="number">1</span>, <span class="number">0</span>],</span><br><span class="line">         [-<span class="number">1</span>, <span class="number">4</span>, -<span class="number">1</span>],</span><br><span class="line">         [<span class="number">0</span>, -<span class="number">1</span>, <span class="number">0</span>]]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 添加padding, 初始化f为图像+2边框的0矩阵</span></span><br><span class="line">    f = [[<span class="number">0</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(cols + <span class="number">2</span>)] <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(rows + <span class="number">2</span>)]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 将原始图像数据填充到f的中心部分</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(rows):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(cols):</span><br><span class="line">            f[i + <span class="number">1</span>][j + <span class="number">1</span>] = image[i][j]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 创建结果矩阵，用于存储边缘检测的结果</span></span><br><span class="line">    result = [[<span class="number">0</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(cols)] <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(rows)]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 主卷积循环，遍历图像中的每一个像素</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, rows + <span class="number">1</span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, cols + <span class="number">1</span>):</span><br><span class="line">            <span class="comment"># 进行卷积操作</span></span><br><span class="line">            conv_sum = <span class="number">0</span></span><br><span class="line">            <span class="keyword">for</span> m <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">                <span class="keyword">for</span> n <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">                    conv_sum += f[i + m - <span class="number">1</span>][j + n - <span class="number">1</span>] * g[m][n]</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 将卷积结果存入结果矩阵</span></span><br><span class="line">            result[i - <span class="number">1</span>][j - <span class="number">1</span>] = conv_sum</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例输入</span></span><br><span class="line">image = [</span><br><span class="line">    [<span class="number">1</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">1</span>],</span><br><span class="line">    [<span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">2</span>],</span><br><span class="line">    [<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>]</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用Laplacian边缘检测函数</span></span><br><span class="line">edge_result = edge_detection_laplacian(image)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出结果</span></span><br><span class="line"><span class="keyword">for</span> row <span class="keyword">in</span> edge_result:</span><br><span class="line">    <span class="built_in">print</span>(row)</span><br></pre></td></tr></table></figure><p>但是他这个回答没有给出拉普拉斯算子怎么算的，其实这个算子我也是知道，也会用，其实早在投实习的时候一面就问了这个算子，我直接手写了出来，但是如果不给算子，而是用[1,-1]和$[1,-1]^T$计算拉普拉斯算子，再算的话，就不太容易了。我当时只算了水平的，没有算垂直的，水平垂直的叠加其实就可以了，有算子之后后面就容易了。</p><p>chatgpt给出了推导过程：</p><h3 id="1-一阶导数的离散化"><a href="#1-一阶导数的离散化" class="headerlink" title="1. 一阶导数的离散化"></a>1. 一阶导数的离散化</h3><p>在连续情况下，函数 <code>f(x)</code> 的一阶导数是：</p><p>$$<br>f’(x) &#x3D; \frac{d}{dx}f(x)<br>$$</p><p>在离散情况下，我们用差分来近似一阶导数。在离散网格上，假设 <code>f(x)</code> 是一组离散的数值表示（如图像中的像素值），我们可以用<strong>向前差分</strong>或<strong>向后差分</strong>来近似一阶导数。</p><p><strong>向前差分</strong>表示为：</p><p>$$<br>f’(x) \approx f(x+1) - f(x)<br>$$</p><p>这相当于卷积核 <code>[1, -1]</code>，它计算当前点与下一个点的差值。</p><p><strong>向后差分</strong>表示为：</p><p>$$<br>f’(x) \approx f(x) - f(x-1)<br>$$</p><p>这相当于卷积核 <code>[-1, 1]</code>。</p><p>如果我们想取中间值（中心差分），可以将这两个差分的平均值作为一阶导数的离散近似：</p><p>$$<br>f’(x) \approx \frac{f(x+1) - f(x-1)}{2}<br>$$</p><p>这一中心差分可以表示为卷积核 <code>[1, 0, -1]</code>，它计算左右相邻点的差异。</p><h3 id="2-二阶导数的离散化"><a href="#2-二阶导数的离散化" class="headerlink" title="2. 二阶导数的离散化"></a>2. 二阶导数的离散化</h3><p>二阶导数表示的是函数变化的变化率。二阶导数是通过对一阶导数再次求导数得到的。在连续情况下，二阶导数是：</p><p>$$<br>f’’(x) &#x3D; \frac{d^2}{dx^2}f(x)<br>$$</p><p>对于离散网格，我们可以用差分的形式来近似二阶导数。</p><h3 id="3-从一阶导数推导二阶导数"><a href="#3-从一阶导数推导二阶导数" class="headerlink" title="3. 从一阶导数推导二阶导数"></a>3. 从一阶导数推导二阶导数</h3><p>假设我们已经知道一阶导数的离散近似表示为 <code>f&#39;(x) = f(x+1) - f(x)</code>，那么二阶导数可以通过对一阶导数再次取差分来计算：</p><p>$$<br>f’’(x) \approx f’(x+1) - f’(x)<br>$$</p><p>将一阶导数的表达式代入：</p><p>$$<br>f’’(x) \approx (f(x+2) - f(x+1)) - (f(x+1) - f(x))<br>$$</p><p>展开后得到：</p><p>$$<br>f’’(x) \approx f(x+2) - 2f(x+1) + f(x)<br>$$</p><p>这就是二阶导数的离散近似形式。其卷积核为 <code>[1, -2, 1]</code>，它计算当前像素值及其左右两个像素值的加权和，能够检测到亮度变化的速率，也就是图像中的曲率。</p><h3 id="4-解读-1-2-1"><a href="#4-解读-1-2-1" class="headerlink" title="4. 解读 [1, -2, 1]"></a>4. 解读 <code>[1, -2, 1]</code></h3><ul><li><code>1</code>：代表距离当前像素一个单位距离的前后像素值的贡献。</li><li><code>-2</code>：代表当前像素的值，权重大，表示它与左右两个像素的关系。</li><li><code>1</code>：再次代表左右相邻像素的贡献。</li></ul><p>这个差分算子 <code>[1, -2, 1]</code> 通过结合当前像素与其左右像素的值，近似计算二阶导数，检测出图像中变化最剧烈的部分（例如边缘或轮廓）。</p><h3 id="5-二维情况：Laplacian-算子"><a href="#5-二维情况：Laplacian-算子" class="headerlink" title="5. 二维情况：Laplacian 算子"></a>5. 二维情况：Laplacian 算子</h3><p>在二维图像处理的场景下，类似的二阶导数可以推广到多个方向。我们可以分别对 <code>x</code> 方向和 <code>y</code> 方向应用上述 <code>[1, -2, 1]</code> 的差分算子，得到图像在这两个方向的二阶导数。然后通过将两个方向的二阶导数合并，我们得到 <strong>Laplacian 算子</strong>，其标准形式是：</p><p>$$<br>g &#x3D; \begin{bmatrix} 0 &amp; -1 &amp; 0 \ -1 &amp; 4 &amp; -1 \ 0 &amp; -1 &amp; 0 \end{bmatrix}<br>$$</p><p>其中：</p><ul><li>中心位置的 <code>4</code> 对应着当前像素值的权重。</li><li><code>-1</code> 表示与上下左右像素的二阶差分贡献。</li></ul><p>这一卷积核检测的是像素与其邻域的二阶亮度变化，从而可以用于边缘检测。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>总之，这个题不算拉普拉斯的推导就是easy，但是算上的话，就不太容易，我当时算出来了水平的梯度了$[-1,2,-1]$，垂直的应该也是一样的，但是把垂直的和水平的合并就不会了，合并了就是拉普拉斯算子了，后面就没啥了。</p><p>血亏</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/15/%E7%A7%8B%E6%8B%9B%E5%8D%8E%E4%B8%BA%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/15/%E7%A7%8B%E6%8B%9B%E5%8D%8E%E4%B8%BA%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-15T13:42:37.000Z</published>
    <summary>秋招正式批华为面试分享。</summary>
    <title>秋招华为面试分享</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>10.16下午一面，这次面试体验很差，面试官过去之后不开摄像头，然后先自我介绍。</p><p>然后面试官让讲实习，就讲了下，后面问了几个问题，主要是指标相比之前的业务是否有提升，还有如何判断效果如何，和之前的比较有啥区别，这块也问了几个问题。</p><p>然后是讲第一个项目，到这面试官就已经开始不耐烦了，讲完就没问问题，然后开始八股，先问了池化层是什么作用，简单回答了下，然后问平均池化和最大池化的区别，回答完问了解SQL不，我说曾经做过一点，但不是太会，然后给了个SQL题目，我确实不太会，就让改成算法题，然后简单写了下，写完喊面试官几次都不应答，后面基本也都是嗯一下答复。</p><p>然后反问了下部门，面试官说了下，然后就结束了，感觉这次面试官不太耐烦，可能是我回答的不太好，希望能过吧。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/15/%E7%A7%8B%E6%8B%9B%E6%90%BA%E7%A8%8B%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/15/%E7%A7%8B%E6%8B%9B%E6%90%BA%E7%A8%8B%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-15T13:42:37.000Z</published>
    <summary>秋招正式批携程面试分享</summary>
    <title>秋招携程面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次是预定半小时的线上面试，最后面了35分钟，总体还行，首先过去在等候线上会议室签到，签到之后等下进去特定的会议，然后查验学生证和身份证。</p><p>自我介绍，然后面试官说时间紧咱们挑三个项目讲，就挑了实习+两个横向的项目。</p><p>首先问实习，也是先介绍背景和做了啥，然后是问细节，badcase怎么处理的，用大模型做这个流程相比之前的意义和优势，然后问我是否了解多头LoRA，我说不知道。</p><p>然后问了发了论文的项目，讲完主要内容，然后细节，各个模块是如何做的，都用了什么模型，是否进行了创新，然后问是否可以优化更改，我说可以，不过成本会更高，然后面试官问是否可以那样说更好，我说可以。最后问发的论文是不是人工智能领域的，我说是的。</p><p>然后是第二个横向的项目，这个时候时间不多了，让快点讲，我就快速讲完背景和主要方法和思路，问用了什么做的，我说分割，然后问具体用了什么模型，是否自己训练了，我说用的框架啥的，进行了训练，再问了一点细节。</p><p> 最后又问了我是不是会CPP，我说会一点，但不多，涉及部署的用到一点会一些。</p><p> 没有手撕环节。</p><p> 然后反问是问了他们部分是做啥的，都有哪些业务，后面会不会进行部门匹配，就讲了他部门，然后说后续应该还是会进行业务匹配的。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p> 二面就是主管面了，就完全不问技术了，时间也不是很长，大概也就半小时。</p><p> 自我介绍完，问我将来进去想做AI哪方面的，我说之前视觉、大模型、NLP都做过，一方面看个人能力，还看行业发展趋势和需要。</p><p> 问研究生期间遇到了什么困难，如何解决的。</p><p> 问研究生期间什么时候压力最大，怎么解决的。</p><p> 问自己的优点和缺点是什么。</p><p>然后说我填报的是上海，我说一线城市也都是可以的，年轻要多努力打拼。</p><p>然后问我期望薪酬是多少，我说上海的话要年40以上吧，毕竟是算法类，感觉是不是要的有点高了，应该说可以商量的。</p><p>最后问分配部门的情况和进去做什么的内容，说还要后续匹配。</p><p>中间还有其他问题，但是我没有什么印象特别深的，没有特别难的问题，都是之前被问到过的，回答起来也比较顺畅。</p><p>总的来说，倒是不难，没有问什么特别回答不上来的问题，第二天看还没挂，和理想一样都是二面就主管面，几乎不问技术了。</p><p>过了几天看，已经挂了，看样子大概率是我要价太高了，应该评级不到，40以上应该是ssp，但应该不到，所以直接G。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/14/%E7%A7%8B%E6%8B%9B%E8%8D%A3%E8%80%80%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/14/%E7%A7%8B%E6%8B%9B%E8%8D%A3%E8%80%80%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-14T14:40:37.000Z</published>
    <summary>秋招正式批荣耀面试分享。</summary>
    <title>秋招荣耀面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>这次的面试和之前的都不一样，这次问的非常宏观，这应该也和我投的岗位有关，我投的是AI研发工程师，这个和之前投的大部分都是算法工程师还是很不一样的，按照他自己的说法，这个岗是做除了模型性能之外的其他事，模型性能当然是网络结构，调参，其他部分应该就包括他们做的数据自动预处理框架，机器学习平台，推理框架，还有推理引擎，吞吐量和服务部署，还有量化剪枝TensorRT、Openvino这种了。</p><p>面试首先问的还是我的最后一个项目，这个项目涉及一些CPP和量化剪枝，这个问法和前面的也很不一样，问数据从哪里来的，如何处理的，模型是用的创新性的还是网上找的，性能调优调了哪些参数，具体部署如何做的，cpp部分有哪些，量化剪枝是否自己做的。项目包括几个人，自己在其中是什么角色，最后部署服务如何做的。</p><p>然后问前面的一个项目，问这个项目是如何设计的，流程是啥样的，还是几个人，自己在其中做的啥，然后部署怎么做的，这个具体涉及几个部分，我就讲了讲这个服务的设计和数据的流程，最终部署上线的情况，在其中如何根据具体的场景做了针对性的改进。他还是问了问这个平台包括什么部分，我做的具体是那块的平台还是其中的一部分，这块讲清楚之后就问部署之后各个模块之间如何写作，http服务如何做的，都传递哪些参数。最后问了问最后的推理时间是多少，我说了后面试官问是否想过要进行性能优化，我说项目没要求就没做。</p><p>然后问第一个项目，也是问了问pipeline包括几个模块，也是不问模型本身的那些八股，然后我说这块没有多少创新，主要是根据任务设计了利用几个模型连起来做了个系统，然后问这个东西最终是否投入使用，我说这个是最终上线了，然后就是关于系统本身的一些问题，这块没有问太多。</p><p>之后就是几乎每个面试都会问的实习经历了，这块问了不少，关于前面的机器学习系统是如何做的，大模型是如何做的，他们之前的业务流程是什么样的，我做了之后业务流程是什么样的，评价指标是什么。然后是最经典的问题，和机器学习模型相比效果如何，然后是关于业务相关的一些问题。</p><p>最后问了问我是否了解python的gc，我说不太懂，然后问了一个东西我没听过，之后问多线程协程，我说这块我处理数据的时候用过，但是没有仔细研究过，然后是给了一个小场景题，有6个cpu，然后一个几百张图片需要处理，如何设计，一个进程和cpu之间的关系对应，这块我就完全不太懂了，这块跟我前面做的还是很不一样的，特别偏向底层了。</p><p>然后是反问，我就问了问算法工程师、AI工程师、AI研发工程师有什么区别，然后这块大致讲了讲，就结束了。</p><p>总的来说，关于模型部署和系统底层这块我了解的还是不太多，关于量化剪枝部署这块还是要好好学习一下，毕竟从纯算法角度我可能优势不大，这次面试关于这块问的还是挺多的，回答的不太行，感觉面试官也是有点失望的样子，不过这次面试问的问题还是很有意思的，也是我之前没有接触过的。</p><p>不过没想到的是一面竟然过了。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p>10.17晚上七点二面，面试官也很忙，面试首先自我介绍，然后跳过了实习，直接问哪个项目认为做的比较深，然后我就讲了第一个项目，从头讲到尾，然后也是不问模型细节，而是关注流程，然后是问效果，还有怎么部署的，然后我就讲了下fastapi和flask启动服务这些，涉及推理部署这块做的不多，但是整体流程还是都做过的。</p><p>然后是问大模型方面都做过哪些，然后我说实习，就讲了下实习做的工作，第一个点讲了一些，也问了数据，评价指标，和之前的对比效果如何，等等，第二个点没讲完面试官就不让讲了，然后问其他还做过哪些，其他的我倒是没怎么做过，也是学了相关的知识。</p><p>然后是问我后面说的做了RAG相关的，就问让我讲讲，我大致讲了下概念和应用，优劣啥的，然后面试官问向量表示这些工具了解如何，这块我只能简单的介绍了下那些milvus、chroma、faiss这些我了解的不是很多，没有系统的研究过。</p><p>然后问Cpp了解的如何，这块我也是初步会用一些，但是不多，然后问我会不会Java、Golong这些，这些开发的我自然是不会的。</p><p>然后问我是想做哪方面的，是效果还是效率，效果就是模型调优，效率是机器学习平台、推理框架这种，我还是更喜欢效率这块，毕竟在学校做改网络调参的活我是不太喜欢的。</p><p>然后是问我对于挑选工作offer看重哪些地方，我说从公司的长期发展、个人具体做的事情和内容、薪酬这些角度来说，然后面试官问我期望薪酬是多少，我说这个要看城市，综合多家比较来看，大城市更高一些，生活成本也是要考虑的。</p><p>然后问我对科大讯飞了解多少，我说语音这块做的比较好，问其他，我说星火大模型也了解一些，然后从未来发展角度来说，语音这个业务总体还是很看好的。</p><p>然后说我github还经常更新，我说有博客，之前会写一些技术博客，现在主要是面试复盘。</p><p>然后反问，我就问了部门具体的内容和技术栈，这块大致说了下，然后就结束了，然后面试官说会综合一面和二面的情况，等HR联系。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/11/%E7%A7%8B%E6%8B%9B%E7%A7%91%E5%A4%A7%E8%AE%AF%E9%A3%9E%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/11/%E7%A7%8B%E6%8B%9B%E7%A7%91%E5%A4%A7%E8%AE%AF%E9%A3%9E%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-11T21:56:37.000Z</published>
    <summary>秋招正式批科大讯飞技术面试分享。</summary>
    <title>秋招科大讯飞面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<p>先说十一期间，十一当天下午阿里达摩院发消息要一会直接面试，我说下午有事，能否改成明天早晨，然后改完一会，面试官说要不你先笔试吧，我看笔试都已经10.12了，感觉不妙，也只能这样了，然后10.10直接被终止流程了，笔试被取消了。虽然达摩院我肯定去不了，但是这样一个笔试和面试的机会直接无也还是很难受，看来面试笔试尽量还是别推，不然一不小心直接无就难受了。</p><p>说到这不得不说科大讯飞了，九月中的线下面试，提前打电话说线下没有我这个岗的面试官了，然后说后面线上，结果过了三周了都没动静了，估计那边也不缺人了吧，应该也是直接g了。</p><h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>拼多多这次面试我面的也比较一般，过去自我介绍完然后开始问实习的内容，讲完之后开始问细节，大模型测评用的什么标准，具体是什么，如何衡量好坏，样本构造正负样本如何设置的，效果如何评价，然后我说主要用的精确率，极度不平衡的情况下结果是否有可参考性。对语义解释部分如何正则化提取，遇到无法提取的多吗，有多少，如何处理的。</p><p>然后是问的LoRA微调是否了解，原理是啥，如何做的，显存和速度是否有提升，如何体现，在推理过程中显存和速度是否有提升。</p><p>然后是八股，问我微调用的什么模型，我说盘古智子，然后让我讲下模型结构，这块我说都差不多，简单说了下，但是说的不详细。然后问我是否了解注意力机制，这块大致讲了讲，不是很细，然后追问QK相乘为什么要scale，为什么除以根号下向量长度。然后问大模型处理的整个流程是什么。</p><p>最后手撕，先给题目，然后说完思路再做，这次是依次给了两道题。</p><p>第一道是有1，2，5三种硬币若干，求组成价值为21的组合有多少种，这是个背包问题，用动态规划做，然后面试官问了问初始化1什么意义，两层循环是否可以调换。</p><p>第二道题是实现一个开根号的函数，保留n位有效数字，这个题目我写了，不过好像有一点小问题。面试官问了我是否确认这个能实现这个功能</p><p>最后问我是否了解拼多多的工作强度，是否可以接受，可以。然后反问，我问了问部门具体做啥，他们具体根据商家和客户需求找到可能有问题的，然后及时给出平台的措施，也是结合多模态的信息处理，涉及推荐。然后就结束了。</p><p>总体来说感觉面试中答的比较一般，好多问题都准备的不太好，感觉这次面试也是G了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/10/10/%E7%A7%8B%E6%8B%9B%E6%8B%BC%E5%A4%9A%E5%A4%9A%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/10/10/%E7%A7%8B%E6%8B%9B%E6%8B%BC%E5%A4%9A%E5%A4%9A%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-10-10T15:03:37.000Z</published>
    <summary>秋招正式批拼多多技术面试分享。</summary>
    <title>秋招拼多多面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>这次面的滴滴，刚好赶上交大线下双选会，就在交大的楼里面了下，面完十分钟就过了。</p><p>过去自我介绍完之后，面试官就问了下我之前的实习主要做的内容，也问了不少细节，不过大体也答了上来。</p><p>然后问了特征工程是如何做的，具体这块我讲了下数据预处理和筛选相关这些，实际场景一般怎么做的。</p><p>问是否做过大规模的分布式的数据处理，我说实验室没有这个条件，这块估计问的可能是hadoop这种，理论估计还是要了解下。</p><p>然后问了一些八股，过拟合如何处理，需要如何调节。</p><p>LoRA微调是什么，需要调节什么参数，啥时候调节啥参数，为啥要这样调节。</p><p>最后是手撕一道算法题，这个是个场景题，题目还很长，已知n个员工每个员工的工资，钱币有100 50 20 10 5 1六种，每个员工的工资都是这六种钱币的组合，现在要找出这n个员工的工资的组合，使得这n个员工的工资的组合的钱币数量最少，然后手撕了出来。</p><p>面完三天写的复盘，面试的时候问的哪些已经忘得差不多了，看后面约二面的时间。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p>10.17二面的还行吧，过去自我介绍完让我选一个印象最深的项目讲，我就没讲实习，讲了第一个项目，然后就问细节，这块问的也挺多的，比如路线是啥样，相比传统方法的优势，遇到了什么问题，怎么解决的，这些都问了。</p><p>然后是八股，问了几个问题</p><ol><li><p>YOLOv1到现在v11都有什么变化，大致说了说变化和大体的结构框架。</p></li><li><p>遇到过拟合欠拟合的时候如何做的，我讲了下小模型情况下从数据、预处理和增强方式、模型、训练方式这些展开讲了讲，还讲了大模型情况下LoRA调节遇到的如何解决这些问题。</p></li><li><p>优化器用的什么，如何调节的，我就从梯度衰减和优化器自动调节这些展开讲了讲。</p></li></ol><p>然后是手撕阶段，出了一个题，给定一个字符串s和一个字符串数组words，（串联字串是指words中所有字符串以任意顺序排列起来的，而不是words中所有元素随机排列），然后求串联字串在s中的起始位置，我就用滑动窗口解决了。</p><p>然后就反问，问部门有哪些需要和技术栈，面试官就讲了下部门情况，然后问我有没有其他offer，我说有几个在排序中，然后就结束了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/27/%E7%A7%8B%E6%8B%9B%E6%BB%B4%E6%BB%B4%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/27/%E7%A7%8B%E6%8B%9B%E6%BB%B4%E6%BB%B4%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-27T20:20:37.000Z</published>
    <summary>秋招正式批滴滴技术面试分享。</summary>
    <title>秋招滴滴面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>开始投的机器学习，最后被调到LLM了，本来以为会问很多大模型八股，实际也没有那么多，还行，面试了38分钟。</p><p>过去首先自我介绍，然后开始问算法八股，第一个是介绍一下特征工程，我就先从传统特征工程来说的，然以在面试官的提示下也说了数据预处理相关的内容。问RNN、LSTM、Transformer的区别，这块是经典八股了，差不多答了。</p><p>然后是问大模型的实习具体做了啥，这块我讲了好多，感觉后面面试官都不是很耐烦了，毕竟这块东西还挺多。然后是经典的问题，既然机器学习方法能做，为什么用大模型做，这块大致答了下。然后面试官问了解CoT不，我说了解一点，面试官说这个任务也是可以用CoT来做的，然后我也简单回答了具体做的相关性。</p><p>最后是手撕，给了个经典题，word1到word2最少的操作，插入、删除、替换，然后手撕了出来。</p><p>最后反问，问部门做啥，不同岗位之间的区别和调整，面试官说虽然这个岗是LLM，不过还是有很多机器学校方法等的内容，然后就结束了。</p><p>总体答的大差不差，不过感觉有一点KPI面，不过也可能是我自己感觉的，毕竟面试官也是很忙的。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p>10.23二面，面了差不多40分钟吧，这次面的感觉还行，这次是主管面，没怎么问技术。</p><p>过去自我介绍完，然后面试官说视觉做的多，我说大模型这块也做了一些，就问目前主流都有哪些大语言模型和视觉大模型，大语言模型我大致介绍和点评了下，然后视觉大模型就不太了解了，多模态大模型简单说了一点。</p><p>然后问我更想做大语言模型还是视觉大模型，我这块犯了个忌讳，我说他们底层都差不多，其实CV和NLP差别还是过大了，直接说差不多还是不太合适，这块我又解释了下我都能做，这块回答的不太好。然后我说的目前技术栈偏向大语言模型，但是从长期发展更看好视觉大模型，视觉大模型的发展还处于比较早期。</p><p>然后是问关于大模型如何赚钱落地和商业模型这块如何看待，这个问题就比较宏观了，就不是技术，而是如何赚钱了。这块我首先谈了下RAG、Agent啥的，面试官说不是技术，而是盈利模式这块。我就大致谈了谈目前大模型各行各业的应用，然后说了下商业模式的一些看法，对于个人、开发者、以及做产品的角度如何看待，关于做产品我还是从交互方式的角度谈了下，大模型可能和新一代的硬件，改变交互方式有关。这块的回答应该还是很不错的，面试官说本来前面的回答让他很不满意想直接给我挂了，但是这个回答他还是很高兴的。然后就是谈了下这个关于这个领域和行业发展后面可能的一些方向之类的，这块感觉还是很不错的。</p><p>然后还是聊关于部门内部两个细分大语言模型和视觉大模型的组，面试官说视觉大模型这块都是博士和院士团队在做，我履历达不到，所以进去还是会给我安排大语言模型这块。</p><p>然后反问，我就问的我过去是否需要什么技术上的学习和准备，面试官说不用，反正进来还是会有相关培训，然后就结束了。</p><p>总体来说，面试前面面的不太行，中后期回答基本还算可以，感觉还是有希望的。</p><h2 id="HR面"><a href="#HR面" class="headerlink" title="HR面"></a>HR面</h2><p>11.27面直接过过了，然后问问题。</p><ol><li>是理想北京，是否接受</li><li>毕业时间</li><li>西电本硕否</li><li>是否都全日制</li><li>是否签三方</li><li>薪酬保密，给不错的薪酬，年终奖</li><li>六险一金</li><li>工作时间和加班情况</li><li>租房情况</li><li>有食堂需要自费</li><li>工作内容和晋升机制</li><li>是否有末位淘汰</li><li>家乡在哪，是否支持</li><li>加微信</li></ol>]]>
    </content>
    <id>http://jackzhu.top/2024/09/25/%E7%A7%8B%E6%8B%9B%E7%90%86%E6%83%B3%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/25/%E7%A7%8B%E6%8B%9B%E7%90%86%E6%83%B3%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-25T19:00:37.000Z</published>
    <summary>秋招正式批理想技术面试分享。</summary>
    <title>秋招理想面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>昨天有人加我，说是网商银行的智能引擎部门，主要基于蚂蚁集团大规模数据结合大模型，提升数据的感知和认知能力，帮助千万小商家的信用成长。部门包括多个NLP、大模型、时序和图方向，技术自由度高，且有业务价值。</p><p>然后约了今天上午的一面，今天上午到点是个电话面试，之前还没电话面过。面试首先自我介绍，然后主要讲实习的工作，他们这边应该是大模型比较相关的，所以实习相对契合一点。然后就开始非常细致的讨论实习做的内容，包括很多细节，文件如何处理的，如何表示的，如何训练的，任务是怎么样的，系统的架构，用了哪些机器学习方法，改进使用大模型的意义都有哪些方面，还有如何微调的，用了哪些方面，具体参数如何调整和设置的。然后问到LoRA，问为什么用，原理是什么，需要调整什么，啥时候怎么调整。总之这个问的细致程度基本是其他面试二面的强度，深挖一个项目，还是非常深入的。</p><p>然后面试官说看你简历里面有了解RAG相关内容，然后开始问相关的问题，原理是什么，为什么用RAG，解决了什么问题，还有相关的一些问题，最后问到向量表征都有哪些方法，如何做的，这个确实不是很懂，没怎么研究过。</p><p>然后反问，问了下后面有几面怎么安排，然后就结束了。后面看果然G了</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/24/%E7%A7%8B%E6%8B%9B%E8%9A%82%E8%9A%81%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/24/%E7%A7%8B%E6%8B%9B%E8%9A%82%E8%9A%81%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-24T20:40:37.000Z</published>
    <summary>秋招正式批蚂蚁技术面试分享。</summary>
    <title>秋招蚂蚁面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次面试的岗位是机器学习算法，面了一个小时，这次面试也算有质量。</p><p>过去先简单自我介绍，面试官说你这专业算计算机是吧，那计算机相关的理论你应该会，我说我专业属于计算机，不过学的还是偏向电子信息，然后让讲一下堆和栈的区别，这块没怎么讲清楚，只是简单讲了下定义，这块应该从内存中的开辟使用管理啥的开始说的。毕竟这块也是算基础吧，还是要准备下的，前面这块的都没背。</p><p>然后面试官说有论文，是不是人工智能领域的，我说是的。让讲一下论文，然后我翻出来自己的论文，从前到后讲了一遍，不过前面部分我用的时间太多，讲的有点乱，后面讲的就挺快了，讲完面试官说讲的有点乱，听的不是很清楚。</p><p>然后说去实习了就讲下实习的内容，然后我大致讲了讲，面试官简单问了两个小问题，比如效果不好的时候，如何判断是哪里的问题导致的，怎么改正。</p><p>之后面试官说给个数学题你想下：一个圆上取三点，构成锐角三角形的概率是多少，这个我尬住了，想了半天没思路，然后面试官说构成直角三角形的概率是多少，然后再到锐角，最后还是没回答出来。这块答的感觉不太行，没有体现出遇到未知问题解决问题的能力，这个思考过程不够清晰。</p><p>然后让手撕一道题，找无序数组中第K大的数，这个是一个很经典的题了，我用python堆来做的，写出来自然很简单，写完面试官说你用库实现的太简单，要不换个方法，然后说要不你自己用数组实现一个堆，然后我写不出来，又尬住了半天，然后面试官说让用快排的方法写，写了出来了。</p><p>然后反问环节，问他们部门具体做啥，然后哪方面的，说是搜广推的，还有一点具体的方向，然后刚好一个小时，就结束了。</p><p>然后晚上看，果然已经挂了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/23/%E7%A7%8B%E6%8B%9B%E5%BF%AB%E6%89%8B%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/23/%E7%A7%8B%E6%8B%9B%E5%BF%AB%E6%89%8B%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-23T17:11:37.000Z</published>
    <summary>秋招正式批快手技术面试分享。</summary>
    <title>秋招快手面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次面试的岗位是图像算法工程师，就面了15分钟7秒，特别短，面的不太行，应该也G了。</p><p>过去先自我介绍，介绍完之后面试官说看你简历里面有cpp，面试官说你会C++是吧，然后我说我只是在前面的一个项目中用到了一点，也是去年初了，后面几乎没怎么用到，所以也不是特别会。然后问CPP基础，第一个问题问值传递和引用有什么区别，第二个问题问虚函数定义是什么，这些都不会，毕竟cpp真很久不用了。</p><p>然后问其中一个项目，问是怎么做的，讲了讲，然后问做了什么创新点，当然是没有，把技术路线讲了讲，问这个项目的难点是什么。之后又问了一个项目，这个也问的不多，简单问问就够了。然后问训练模型都调节什么超参数，过拟合和欠拟合如何处理，注意力机制是什么。</p><p>然后反问，问他们这边是偏应用还是开发啥的，说开发的多一点，很多地方要用cpp，所以这块比较需要。</p><p>这个才是目前线上问的时间最短的，实习都没问，毕竟这边是图像算法。感觉不是很契合，第二天通知果然挂了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/23/%E7%A7%8B%E6%8B%9B%E5%90%88%E8%82%A5%E5%AE%89%E8%BF%85%E7%B2%BE%E5%AF%86%E6%8A%80%E6%9C%AF%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/23/%E7%A7%8B%E6%8B%9B%E5%90%88%E8%82%A5%E5%AE%89%E8%BF%85%E7%B2%BE%E5%AF%86%E6%8A%80%E6%9C%AF%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-23T14:15:37.000Z</published>
    <summary>秋招正式批合肥安迅精密技术面试分享。</summary>
    <title>秋招合肥安迅精密技术面试分享</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次是线下面，就在南校区C楼面了，过去也不用叫号啥的，就是随便看人自己排队，这个面试就是人事面，相当于填简历的面试。</p><p>过去先看证件，给简历，然后看本硕学历证明，成绩单，四六级，还要本科的成绩单，我没打印，就给记录上没本科成绩单，然后问有没有挂科，当然没有；然后挨个问，问完记录 ，就是纯粹当面填简历信息，问论文，然后把英文论文翻译成中文录进去，问简历号，然后是竞赛有啥，然后记录专利是啥名字；然后挨个问项目是做的啥，记完名字一句话描述做的啥，然后录系统，挨个问完之后问编程语言，然后是研究方向是啥，也是简历上有的信息。</p><p>然后问了问实习，她好像都没看简历，简历上写的有实习，他还问我有没有，我给他指出来她才看到然后对着把信息填系统。</p><p>问完这些然后问家是哪里的，父母是什么工作，将来想去哪里工作，将来想做啥方面的，我说人工智能这块都比较通，这块都可以做。</p><p>后面还是说本科成绩单的事，所以只好加上微信，后面发过去，然后反问，问他们后面有几面，是不是分配，说一面是人事面，二面技术面，然后也是根据人匹配岗位啥的，然后就结束了。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/f0e470325b3d2276037b6d9878231f6.jpg" alt="f0e470325b3d2276037b6d9878231f6"></p><p>后面就发现挂了，进入人才库。</p><p>后面10.10被软开捞了，电话打过来问对开发有没有兴趣，我完全没有学过开发，前后端啥的完全不会，问cpp的也不太会，问了数据结构，还有堆栈、ArrayList、LinkedList这些就问了一二十分钟吧，然后就完了。</p><p>然后10.15就直接给offer了，但是13k，这个价在深圳只能说有点离谱，*1.36也没有太大意义。</p><p>offer是高级系统开发工程师。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/22/%E7%A7%8B%E6%8B%9B%E6%AF%94%E4%BA%9A%E8%BF%AA%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/22/%E7%A7%8B%E6%8B%9B%E6%AF%94%E4%BA%9A%E8%BF%AA%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-22T19:20:37.000Z</published>
    <summary>秋招正式批比亚迪面试分享。</summary>
    <title>秋招比亚迪面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次面试进去面试官就说时间紧，咱们尽快，晚上七点半的面试，看起来这公司还是要加班很多的。</p><p>进去先自我介绍，然后问了我最后面的一个项目，谈谈做的啥，然后问了下细节，数据处理，标注，处理啥的。然后问有啥项目感觉比较有内容的，然后我说了下第一个项目，讲讲背景和具体怎么做的，讲了一些，但是创新性不够，问这个项目的难点在哪里。后面又问了问实习做的啥，然后我就讲具体做的什么，这块也追问了一些细节，然后没讲完，面试官就说时间不多了，然后反问，我问进去是做啥，他说一面只是初筛，具体的方向和部分还是会具体分配，然后就结束了。</p><p>总的来说，得物这个一面是目前线上面试的里面时间最短的，没有之一，部分线下面试有不超过半小时的，这个面试都不到半小时，就问了下项目和实习，没有八股和手撕部分，像淘天这种，八股和手撕是主要，风格还很不一样，不过我八股和手撕都准备的不太好，所以这种风格也不能算太差吧，至少比手撕不出来好。</p><p>面完后面看果然已经挂了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/22/%E7%A7%8B%E6%8B%9B%E5%BE%97%E7%89%A9%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/22/%E7%A7%8B%E6%8B%9B%E5%BE%97%E7%89%A9%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-22T02:40:37.000Z</published>
    <summary>秋招正式批得物面试分享。</summary>
    <title>秋招得物面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/15c68ad5ba57c4307aaf8b5475c4205.png" alt="15c68ad5ba57c4307aaf8b5475c4205"><br>9.19面试的，前面投实习的时候再加秋招前面简历挂了好多次了，这次面的时候就感觉大概率也g了，果然面完半小时就挂了。</p><p>面试过去首先面试官介绍了他们部门是做什么的，然后我做了自我介绍，然后面试官说给你三十分钟，让手撕三个力扣题，第一个是写一个查找字符串数组中的最长公共前缀；</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">longest_common_prefix</span>(<span class="params">strs</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> strs:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 取第一个字符串作为基准进行比较</span></span><br><span class="line">    prefix = strs[<span class="number">0</span>]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 遍历数组中剩下的字符串</span></span><br><span class="line">    <span class="keyword">for</span> s <span class="keyword">in</span> strs[<span class="number">1</span>:]:</span><br><span class="line">        <span class="comment"># 逐渐缩短前缀，直到找到公共前缀</span></span><br><span class="line">        <span class="keyword">while</span> s[:<span class="built_in">len</span>(prefix)] != prefix:</span><br><span class="line">            prefix = prefix[:-<span class="number">1</span>]</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> prefix:</span><br><span class="line">                <span class="keyword">return</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> prefix</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试用例</span></span><br><span class="line">strs = [<span class="string">&quot;flower&quot;</span>, <span class="string">&quot;flow&quot;</span>, <span class="string">&quot;flight&quot;</span>]</span><br><span class="line">result = longest_common_prefix(strs)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Longest common prefix:&quot;</span>, result)</span><br></pre></td></tr></table></figure><p>时间复杂度：O(S)，其中 S 是数组中所有字符串的字符总数。在最坏情况下，算法需要逐个字符比较每个字符串的前缀。<br>空间复杂度：O(1)，除了输入数据外，额外使用的空间主要用于存储前缀。</p><p>关于这点我就说的很差，时间空间复杂度说的完全不对，我还以为是$O(mn)$， g</p><p>第二个是找出数组中所有可以使得数字和为target的组合，每个元素只能出现一次；</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">combination_sum</span>(<span class="params">nums, target</span>):</span><br><span class="line">    <span class="comment"># 先对数组排序，这样可以更容易地进行剪枝操作</span></span><br><span class="line">    nums.sort()</span><br><span class="line">    result = []</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 定义回溯函数</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">backtrack</span>(<span class="params">start, path, remaining_target</span>):</span><br><span class="line">        <span class="comment"># 当剩余的target为0时，说明我们找到了一个有效的组合</span></span><br><span class="line">        <span class="keyword">if</span> remaining_target == <span class="number">0</span>:</span><br><span class="line">            result.append(path[:])  <span class="comment"># 将当前路径加入结果中</span></span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 遍历从当前索引开始的每个数字</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(start, <span class="built_in">len</span>(nums)):</span><br><span class="line">            <span class="comment"># 如果当前数字大于剩余的target，则无需继续搜索</span></span><br><span class="line">            <span class="keyword">if</span> nums[i] &gt; remaining_target:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 为了避免重复的组合，我们跳过相同的数字</span></span><br><span class="line">            <span class="keyword">if</span> i &gt; start <span class="keyword">and</span> nums[i] == nums[i - <span class="number">1</span>]:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 选择当前数字，继续递归搜索</span></span><br><span class="line">            path.append(nums[i])</span><br><span class="line">            <span class="comment"># 递归，i + 1表示每个元素只能使用一次</span></span><br><span class="line">            backtrack(i + <span class="number">1</span>, path, remaining_target - nums[i])</span><br><span class="line">            <span class="comment"># 回溯，撤销选择</span></span><br><span class="line">            path.pop()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 调用回溯函数</span></span><br><span class="line">    backtrack(<span class="number">0</span>, [], target)</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试示例</span></span><br><span class="line">nums = [<span class="number">10</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">7</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">5</span>]</span><br><span class="line">target = <span class="number">8</span></span><br><span class="line">combinations = combination_sum(nums, target)</span><br><span class="line"><span class="built_in">print</span>(combinations)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>时间复杂度是指数级的 $O(2^n)$，属于递归经典的时间复杂度了，我这里说成了$O(N^2)$， 明显的直接g了， 这里的空间复杂度好像不太好算，应该是$O(k*2^n + n)$， k 是每个组合的平均长度， n是数组的长度。</p><p>第三题是一个链表数组，其中每个已经按照升序排列，合并成一个升序链表。写完后说时间和空间复杂度，这块没说清楚，对于第三题，问我为什么用堆，有什么优势，堆的几种基本操作的时间复杂度是多少。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> heapq</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义链表节点的类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ListNode</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, val=<span class="number">0</span>, <span class="built_in">next</span>=<span class="literal">None</span></span>):</span><br><span class="line">        self.val = val</span><br><span class="line">        self.<span class="built_in">next</span> = <span class="built_in">next</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">merge_k_sorted_lists</span>(<span class="params">lists</span>):</span><br><span class="line">    <span class="comment"># 创建一个最小堆</span></span><br><span class="line">    heap = []</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 将每个链表的头节点加入最小堆</span></span><br><span class="line">    <span class="keyword">for</span> i, lst <span class="keyword">in</span> <span class="built_in">enumerate</span>(lists):</span><br><span class="line">        <span class="keyword">if</span> lst:</span><br><span class="line">            heapq.heappush(heap, (lst.val, i, lst))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 哨兵节点，用于构建最终的合并链表</span></span><br><span class="line">    dummy = ListNode()</span><br><span class="line">    current = dummy</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 进行堆的操作，直到堆为空</span></span><br><span class="line">    <span class="keyword">while</span> heap:</span><br><span class="line">        <span class="comment"># 弹出堆中的最小值</span></span><br><span class="line">        val, i, node = heapq.heappop(heap)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 将弹出的节点连接到最终结果链表中</span></span><br><span class="line">        current.<span class="built_in">next</span> = node</span><br><span class="line">        current = current.<span class="built_in">next</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 如果弹出的节点还有后续节点，将它的后续节点加入堆中</span></span><br><span class="line">        <span class="keyword">if</span> node.<span class="built_in">next</span>:</span><br><span class="line">            heapq.heappush(heap, (node.<span class="built_in">next</span>.val, i, node.<span class="built_in">next</span>))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 返回合并后的链表</span></span><br><span class="line">    <span class="keyword">return</span> dummy.<span class="built_in">next</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试用例</span></span><br><span class="line"><span class="comment"># 构建几个升序链表</span></span><br><span class="line">list1 = ListNode(<span class="number">1</span>, ListNode(<span class="number">4</span>, ListNode(<span class="number">5</span>)))</span><br><span class="line">list2 = ListNode(<span class="number">1</span>, ListNode(<span class="number">3</span>, ListNode(<span class="number">4</span>)))</span><br><span class="line">list3 = ListNode(<span class="number">2</span>, ListNode(<span class="number">6</span>))</span><br><span class="line"></span><br><span class="line">lists = [list1, list2, list3]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 合并链表</span></span><br><span class="line">merged_list = merge_k_sorted_lists(lists)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出合并后的链表</span></span><br><span class="line"><span class="keyword">while</span> merged_list:</span><br><span class="line">    <span class="built_in">print</span>(merged_list.val, end=<span class="string">&quot; -&gt; &quot;</span>)</span><br><span class="line">    merged_list = merged_list.<span class="built_in">next</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;None&quot;</span>)</span><br></pre></td></tr></table></figure><p>第三题这个不算特别难的，感觉比第二题还好一点，但是对于堆的相关概念说的不是很清楚，这个时间复杂度是$O(Nlogk)$，N是所有链表的节点总数，k是链表的个数，空间复杂度是$O(k)$，k是链表的个数。</p><p>然后让我讲下transformer结构，大致说了下，然后问transformer相比RNN有什么优势。这块实习的时候淘天就问过这个问题，当时说的模糊，思路不是很清晰，现在秋招了这块也没怎么看过，仍然说的不是很清楚，八股还是要背的，这种基础知识都说不好的肯定不行。</p><p>GPT总结了一些：</p><blockquote><ol><li>并行计算能力<br>RNN：RNN 是一种序列模型，必须按时间步依次处理输入数据。也就是说，第 t 时刻的输出依赖于第 t-1 时刻的计算结果，因此不能并行化处理序列中的元素，这导致训练时间较长。<br>Transformer：Transformer 利用自注意力机制（Self-Attention），使得每个输入位置的处理可以与其他位置的元素并行进行。这意味着 Transformer 能够在同一时间处理整个输入序列，从而大大加快了训练和推理速度。这种并行化特性使 Transformer 非常适合在 GPU 上运行，尤其对于长序列，训练速度相较 RNN 显著提升。<blockquote><ol start="2"><li>捕捉长距离依赖关系的能力<br>RNN：RNN 的主要缺点之一是难以有效捕捉长距离依赖关系。由于其每个时间步的状态依赖于上一个时间步的状态，随着序列的增长，长距离的依赖可能被遗忘（梯度消失问题）。虽然 LSTM 和 GRU 在一定程度上缓解了这个问题，但仍然存在限制。<br>Transformer：通过自注意力机制，Transformer 可以直接建模序列中任意两个位置之间的依赖关系，无论这两个位置相隔多远。这意味着 Transformer 在处理长序列时，能够高效且准确地捕捉长距离的依赖关系，而不会像 RNN 那样受到序列长度的限制。<blockquote><ol start="3"><li>消除递归结构，提升效率<br>RNN：RNN 的递归结构需要依次处理序列中的每个元素，这种顺序性导致模型无法并行执行，尤其在长序列任务中，RNN 的训练和推理效率较低。<br>Transformer：Transformer 通过完全消除递归结构，取而代之的是基于全局自注意力机制的并行计算，这大大提升了计算效率。每个位置的输出只需通过矩阵运算来计算，无需逐个时间步依赖前一个输出，极大地提升了效率。<blockquote><ol start="4"><li>更好的记忆和上下文捕捉<br>RNN：RNN 的记忆是通过隐状态（hidden state）来传递的，每一步的状态依赖于前一步的信息传递。这种方式在处理长序列时会因为过多的信息传递而产生信息损失，导致模型难以保留远距离的上下文信息。<br>Transformer：Transformer 使用自注意力机制，使得每个词或元素能够直接与其他所有元素建立联系。这种全局视角能够让模型在同一层次上捕捉到整个输入序列的全局信息，从而更好地保留上下文信息。<blockquote><ol start="5"><li>避免梯度消失问题<br>RNN：RNN 依赖于逐步传递的隐状态，训练时容易遇到梯度消失或梯度爆炸的问题，尤其是在处理长序列时，梯度的传播会变得非常困难。<br>Transformer：Transformer 没有依赖于序列传递的隐状态，而是通过自注意力机制和前馈神经网络来计算输出。这样就避免了在长序列上梯度消失或爆炸的问题，训练更为稳定，尤其在长文本或长序列任务中表现尤为显著。<blockquote><ol start="6"><li>更强的表示能力<br>RNN：RNN 只能通过顺序计算来处理序列信息，这种线性的计算方式在处理复杂序列依赖时可能有局限。<br>Transformer：通过多头自注意力机制（Multi-Head Attention），Transformer 可以从多个子空间同时处理信息，每个注意力头关注不同的信息模式，增强了模型的表示能力。多头自注意力机制允许模型同时关注输入序列中的不同部分，获取不同层次的特征。<blockquote><ol start="7"><li>适应性更强的输入表示<br>RNN：RNN 的输入表示是通过序列顺序传递的，缺乏对输入元素之间关系的灵活表示。<br>Transformer：Transformer 通过**位置编码（Positional Encoding）**来为序列中的每个位置引入位置信息。这种设计使得 Transformer 在处理文本等有序数据时，不会像 RNN 一样依赖于输入的顺序，还可以灵活适应各种输入长度和结构。<blockquote><ol start="8"><li>更容易并行扩展<br>RNN：RNN 的递归结构天然不适合并行处理，尤其在训练非常深层的 RNN 模型时，计算资源的利用效率较低。<br>Transformer：Transformer 的并行性和层级结构使其更容易扩展到大规模数据和模型，特别适合深层模型的并行计算。因为没有递归关系，Transformer 可以利用 GPU 进行大规模的并行计算，大幅提升训练效率和处理能力。<blockquote><ol start="9"><li>更适合预训练和迁移学习<br>RNN：尽管 RNN 可以在特定任务上进行预训练，但其能力有限，尤其在处理大规模数据和迁移学习时，效果不如 Transformer。<br>Transformer：Transformer 的架构非常适合大规模预训练模型，如 BERT、GPT 系列。这些模型通过在大规模数据上进行无监督预训练，能够很好地迁移到各种下游任务中，并且预训练的表示可以用于不同领域的任务，大大提高了模型的泛化能力和迁移学习能力。</li></ol></blockquote></li></ol></blockquote></li></ol></blockquote></li></ol></blockquote></li></ol></blockquote></li></ol></blockquote></li></ol></blockquote></li></ol></blockquote></li></ol></blockquote><p>之后是问简历上的实习经历，大致问了下，讲了背景，然后说具体是做了哪方面的工作，经典的一个问题就是，这个任务用大模型做有什么优势，这块我说我就是预研的一个探索，用大模型端到端的做这件事，实际上需要的数据什么的还是用的传统方法获得的，这块其实这样说还是不太好。另外涉及具体的工作，他们这边好像也是有做类似的东西，但是我也没讲的很出彩，然后中间问到了一个问题，我听的不是很明白，面试官就没什么兴趣了，我这块还没说完然后差不多就一个小时了，就让反问，然后就结束了。</p><p>面完就感觉不行了，过一会去看就挂了，淘天还是这么喜欢让手撕代码，这块还是要准备下。总之投实习的时候就淘天面的最差，现在秋招了，淘天还是面的最差的，问题还是有很多，面完要复盘总结经验。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/19/%E7%A7%8B%E6%8B%9B%E6%B7%98%E5%A4%A9%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/19/%E7%A7%8B%E6%8B%9B%E6%B7%98%E5%A4%A9%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-19T13:14:20.000Z</published>
    <summary>秋招正式批淘天面试分享。</summary>
    <title>秋招淘天凉经</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次面试是秋招正式批的第一个线下面试，问的比较浅显，我查了下，一面应该是技术面，不过好像技术的东西问的不多。</p><p>过去首先检查学生证和身份证，然后给简历，自我介绍，然后就要看成绩单，问有没有论文，我说有论文，之后让介绍实习做了啥，难点是啥，大致说下我的工作。其他的项目都没怎么问到。</p><p>然后问我是想做哪方面的，是大模型研发微调还是应用，我说尽可能的偏向研发，然后问从哪个项目可以提现这个能力，我说前面做的项目应用的比较多，但是倾向还是偏向更技术一些的。</p><p>然后反问，然后我问了问部门是做啥的，他说看我意愿分配到适合的部门，面试二十多分钟就结束了，也没有力扣环节。</p><h1 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h1><p>一面完很快就发了二面的预约，9.18 上午的面试，这次人巨多， 等了好久，过去还是先检查学生证和身份证，然后给简历，自我介绍，这次涉及技术的更少了。问了爱好，实习的收获有哪些，然后我就详细讲了讲这块关于实习和在学校。</p><p>之后是问对中兴了解如何，如何看待加班，期望薪酬是多少，有没有其他的offer，我说了下，然后问我有没有问题，我问了问部门是做啥的，然后面试官说一面没问吗，确实没问，然后问意向地点去哪里，我说杭州或者北上广深这种一线城市，大致也就这些。</p><p>没想到面这么随意，后面就挂了，进入了人才库<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/11d5911824c8fffcda4e77dfbee6164e.jpeg" alt="11d5911824c8fffcda4e77dfbee6164e"></p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/18/%E7%A7%8B%E6%8B%9B%E4%B8%AD%E5%85%B4%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/18/%E7%A7%8B%E6%8B%9B%E4%B8%AD%E5%85%B4%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-18T16:46:37.000Z</published>
    <summary>秋招正式批中兴面试分享。</summary>
    <title>秋招中兴面试分享</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>这次面试是秋招正式批的第一个面试，八股什么的也都没有准备，就直接开始面试了。</p><p>首先介绍一下自己，介绍完之后问实习的经历，然后我就讲了讲业务背景是什么，自己在里面干了什么，但是面试官应该不是做这个的，所以也不是很感兴趣。</p><p>然后开始问我的第一个项目，这个发了论文的，问这个项目是背景和做了什么，问了下细节，把大致的流程讲了差不多，然后问这个项目的难点是什么，这个我倒是没有怎么总结，因为都没有说很难的地方。</p><p>然后面试官开始问八股了，YOLO相关的内容，按说我做的视觉项目比较多，这块应该比较懂，不过这块确实很久没看过了，连续问了三个问题回答的都一般，首先让我讲yolo的大致结构，我讲到提特征用darknet，然后讲为什么用darknet不用resnet，这块我就已经忘了，然后讲池化金字塔融合有哪些形式是什么样的，我就简单讲了下，也说的不是很清楚，然后后面的模块简单说了下，之后问发展过程的演变我也是只简单说了下。然后问了下yolo的图像增强方法都有哪些，我简单说了一两个，不太行，回去还是要准备下。</p><p>之后是问第二个项目是做什么的，然后我大致讲了下背景和具体怎么做的，然后这块也没有问太多，毕竟这个含金量也不是很高。</p><p>之后就直接让手撕了，这次考的题目是求最长的斐波那契数列子序列的长度，想了会也写了出来。</p><p>然后是反问环节，我问了问他们部门具体是做什么的，然后这块我也简单聊了聊3dgs和无人机相关那些东西，然后面试官说感谢你的参与，然后就结束了。</p><h1 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h1><p>9.18号下午三点的面试，这次面的挺延伸的。</p><p>过去首先自我介绍，然后让我挑一个认为做的深的项目讲一下，我就讲了下第一个项目，然后首先讲背景，这里就讲了好久，我说这个可以降温增效，面试官就要很清楚的了解，到底降低了什么成本，是在性能的改进，还是成本的改进，还是时间的改进，这个明确之后，才继续讲。然后是讲技术方案，这块也是细致的扣，技术如何这样做会不会有误差，如何保证，具体怎么做。然后是问认为这个项目的难点是什么，我说主要是把这个整个体系实现出来，然后大致讲讲如何看待这个事，是因为在学校和公司的差异。之后就是一个问题，关于测量的真实值如何获得，我简单说了下。然后是开放性问题，问给很多很多钱的情况下如何找到精准真实值，我说找很多专业人士，然后用好的仪器，然后面试官问还有啥，我说不知道了，然后面试官给了点提示，无人驾驶的，然后我说多模态融合激光雷达什么的，也算是答的差不多。后面也是开放的聊了一些。</p><p>之后是代码环境，要求实现一个容器，其中两个函数实现增加元素和删除元素，然后一开始懵逼，提示了下大致写了下思路，时间不够就把大致怎么实现的说了下，也算是差不多说清楚了，就是代码写的还不太完整。</p><p>总体来说，这种很延伸的还是很难的。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/09/12/%E7%A7%8B%E6%8B%9B%E7%BE%8E%E5%9B%A2%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/09/12/%E7%A7%8B%E6%8B%9B%E7%BE%8E%E5%9B%A2%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-09-12T00:14:37.000Z</published>
    <summary>秋招正式批美团机器学习面试分享。</summary>
    <title>秋招美团机器学习面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h1><p>今天周一我六点多下班就回来面试，面试官首先让我自我介绍，然后我自我介绍完，然后是简单问一下我项目情况以及简历上各种内容的情况，然后展开论文讲解，然后我就把论文的背景和内容，然后创新点什么的讲解了一下，然后突出自己的优势，做的比较多学习新东西比较快。</p><p>然后问了问自己在华为实习，这段时间都做了什么？然后我也是简单讲了讲背景，然后讲自己在做的东西这块儿内容有些单薄，后面要丰富一下，这块儿我都没有怎么准备，所以说着坑坑巴巴的。</p><p>然后面试官问自己，在之前遇到什么困难，然后怎么解决的，然后我就换方向这个内容展开讲了讲，说之前遇到这些困难，当然现在解决了，现在做新东西学东西新东西都挺快。</p><p>然后面试官说让手撕代码，然后我说这块儿还没怎么开始准备秋招，然后给了一个题，对于一个长度为六的链表，然后如何反转456项，然后我就按照最简单粗暴的方法，然后他说长度不定，用通用的方法来做，然后我就用一般的方法做基本思路，还是说出来了，先找到一半的节点的头节点，然后面试官说，既然有思路，那就让本地IDE写了。我简单写了写，然后卡住了，翻转这块。想了一会儿，想出来了，这块儿也花了一会儿，还是不是很熟练。</p><p>然后面试官问还有什么想问的，我看面试官都有点不耐烦了，然后简单问了问他们部门儿是做什么的，然后因为我投的是语音部门，所以感觉还是不太match，最后说完面试官说感谢你的参与，全程面了一个小时，这样回答感觉大概率是G了，问题不大就第一场，后面还有机会慢慢准备先把实习做好。</p><h1 id="2024-8-19二面"><a href="#2024-8-19二面" class="headerlink" title="2024.8.19二面"></a>2024.8.19二面</h1><p>这次面试问的非常细，让自己选一个项目，然后深挖。首先讲了讲自己的项目大致背景、流程、以及主要用到的模型，然后提问这个数据是什么，要提特征，然后讲用了什么模型，模型输入输出是什么，架构是什么，我简单说了之后面试官让详细讲，我就讲了讲思路，但是整个模型的结构就忘了。之后问主体模型是啥，输入输出，架构是啥，为啥用这个模型，然后这个模型是单向还是双向的，为什么，然后单向双向网络有什么区别，感觉可以从模型设计的区别和代理任务的区别展开。然后问了问，这个项目你做了两年了，现在回过头来看，你会用什么来做，如何设计训练，输入输出是什么，预训练的话用什么做，对应的任务如何设置。这些还是挺难的，而且还不是那种能查出来的问题。</p><p>然后问了问未来发展如何想，是只做算法还是什么，然后我说因为本硕都学的这个，所以选这个感觉适合，后面做其他的学习能力也很强可以快速学好。</p><p>然后是简单的编程，写一个排序算法，然后我写了个插入排序。</p><p>最后是提问，我问了我部门做啥，具体什么场景。</p><h1 id="2024-9-2三面"><a href="#2024-9-2三面" class="headerlink" title="2024.9.2三面"></a>2024.9.2三面</h1><p>二面完本来约上周，但是上周都抽不出时间，只好这周，还是周一。这次面试主要还是聊聊天，首先做自我介绍，然后就问实习这段时间有什么收获，和学校有什么不同，我说一方面是技术上的，另一方面是从学生到职场转换这一过渡；在学校是一个人做项目，在企业分工合作做一个大的，然后问这两个哪个好，我说看根据具体的项目来定，大项目合作分工，小项目单人做。然后就是聊现在技术发展趋势和未来职业规划，我说跟着时代趋势走，同时积累自己的核心能力，然后面试官就在说时代趋势和语音这块发发展。<br>后面到反问环节，我问部门主要做什么，然后主要就是语音交互，相关的技术。最后我问了问部门升职和培养机制。</p><p>其他还问了有没有拿到其他offer， 投了哪些了，我就如实说，还没offer，也基本没开始投。</p><p>总的来说，比较偏向于聊天，但是具体要不要我真看不出来，希望能过吧，然后就说联系HR了。后面如果过的话应该就进池子了，不知道啥时候能泡出来。</p><h1 id="2024-12-17HR面"><a href="#2024-12-17HR面" class="headerlink" title="2024.12.17HR面"></a>2024.12.17HR面</h1><p>本来以为开不出来了，没想到打电话过来，首先问我签了哪里，我说已经签了一家中厂了，并问了问方向，然后问了问毁约到什么时间可以。之后问了问我后续投递计划以及现在的年终奖月份了。 </p><p>然后问我是否还有其他offer，之后问我关于方向的问题，以及预期薪酬。</p><p>第二天就给我确定了薪酬和工作内容，让我等着发offer就行，审批中，之后过了几天offer就发下来了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/08/06/%E7%A7%8B%E6%8B%9B%E6%8F%90%E5%89%8D%E6%89%B9%E7%99%BE%E5%BA%A6%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/08/06/%E7%A7%8B%E6%8B%9B%E6%8F%90%E5%89%8D%E6%89%B9%E7%99%BE%E5%BA%A6%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-08-06T01:03:37.000Z</published>
    <summary>秋招提前批百度机器学习/数据挖掘/自然语言处理工程师面试分享。</summary>
    <title>秋招提前批百度机器学习/数据挖掘/自然语言处理工程师面试分享</title>
    <updated>2026-06-10T08:01:07.569Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="面试前的内容"><a href="#面试前的内容" class="headerlink" title="面试前的内容"></a>面试前的内容</h2><p>本来投的计算机视觉，但是一直在简历筛选中，5.26我看腾讯实习都快结束了，视觉这块肯定做不了，改投了机器学习，然后昨天就接到了约面，今天上午面，腾讯的算法好像就叫技术研究-机器学习方向。<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240528110403.png" alt="20240528110403"></p><h2 id="面试"><a href="#面试" class="headerlink" title="面试"></a>面试</h2><p>面试首先让做自我介绍，之后让选一个项目深入讲解，我做的通用项目比较多，然后大致概括了下，之后选了一个项目，然后问这个项目做的啥，技术亮点是啥，这里问的也是很细，然后我说主要还是任务上应用到这个领域，然后问如何衡量效果，设计什么指标，效果如何，误差是多少；然后是问数据量多少，是否足够，如何判断数据量足够，质量如何，进行了什么预处理；讲具体用了mmseg这个框架，然后问具体用了什么模型，为什么选这个模型，这个模型结构是什么，模型的优缺点是什么。</p><p>然后让讲解一下YOLO系列，这块我没法详细的讲解，只能说个大体框架，然后每一代的改进简单提了一点，也没详细讲出来。</p><p>然后让讲transformer的结构，我大致讲了下，然后问qkv的物理意义，问为什么现在大模型都是decoder结构的，大致说了下和bert的区别，然后其中讲的比较宏观。</p><p>所以面试官问了SAM了解的如何，这块我还真不太了解，然后说用了下，感觉太慢，效果还行。然后问关于大模型理论这块的看法，然后我说现在视觉大模型还不太够，主要还是基于LLM，然后简单问了下LLM相关。</p><p>之后就是手撕，一个字符串求最大的无重复字符串长度，这个不算难，用的滑动窗口法，面试官说复杂度O(n)太高，有没有优化空间，不知道。</p><p>之后就是反问环节了，我问了问他们部门是做什么的，说是游戏和强化学习相关的，我说这块感觉是未来的方向。之后问我啥时候能去，我说六月多到九月多可以三个月，然后问老师放不放，我说没问题。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总的来说感觉问题跳跃度非常高，从一个问题回答中间提到了其他技术，就继续追问，很多新技术我了解的并不深入，比如SAM这样。但是我在项目准备上略有欠缺，YOLO系列也没有系统的整理，这次面试官也只问了一个项目，让捡着一个详细说然后不断发散，前面的钉钉和淘天的视觉面就问的特别细，几乎每个项目，论文，竞赛等等都问了一遍。</p><p>一个问题就是我做的项目虽然多，但是大部分都是含金量不太够的项目，一深挖就会发现很工程，嗑盐创新点不够，不够深入，还是要有至少一个项目做的非常深，这样每次面试的时候只需要对这一个做的非常好的项目做准备就可以了，毕竟大部分面试也不会把每个项目都拷问一遍，贪多没用，简历写了一大堆也就那样。</p><p>这可是腾讯AI Lab，我也是太勇了，这种感觉几乎没有可能过，level太高了，这应该是我面试这么多以来最高的了。</p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>非常合理的面试挂了，毕竟面试开始前我就预估不通过率能有95%，这要是能过我只能说腾讯是真没人了，没有意外的非常合理。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240530145109.png" alt="20240530145109"></p>]]>
    </content>
    <id>http://jackzhu.top/2024/05/28/%E8%85%BE%E8%AE%AFAI%20Lab%E6%9A%91%E5%AE%9E%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/05/28/%E8%85%BE%E8%AE%AFAI%20Lab%E6%9A%91%E5%AE%9E%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-05-28T10:59:37.000Z</published>
    <summary>腾讯AI Lab暑实面试记录分享。</summary>
    <title>腾讯AI Lab暑实面试分享</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="面试前的内容"><a href="#面试前的内容" class="headerlink" title="面试前的内容"></a>面试前的内容</h2><p>本来阿里国际投的算法工程，结果简历筛选就没过，然后给调到了大模型应用开发，这块我没做过具体项目，感觉是要G了。</p><p>果然G了<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240528131347.png" alt="20240528131347"></p><h2 id="面试"><a href="#面试" class="headerlink" title="面试"></a>面试</h2><p>这块首先是做了两分钟的自我介绍，然后问哪个项目印象深刻，然后讲了下，之后问论文，然后简单讲下，然后说有没有网络模型的创新，我说这块没有；然后简单问下大模型知道多少，我简单说了一点理论和应用上的了解；关于项目和论文，也基本没有追问，没有仔细考察，可能毕竟和大模型差的也很多吧，然后就是反问环节，问了部门是做啥的，然后说主要是做大模型的应用开发，然后具体说是做应用，设计一些模型改进和调整什么的。</p><h2 id="手撕代码"><a href="#手撕代码" class="headerlink" title="手撕代码"></a>手撕代码</h2><p>力扣这块我做的确实还是不到位，大部分手撕都做不出来，这次出的是对一个$\m \times n$的矩阵，从每行中取出一个数然后相加，使得总和与一个target值的绝对差最小，绝对差是值两个数之差的绝对值，然后我就想半天也没想出来，然后就结束了。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这次面试感觉还是挺差的，毕竟大模型这块我没做过，也没怎么了解，力扣这块也是要多刷题，总之这次感觉是有点KPI面，问的也比较粗略，感觉肯定是G了。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/05/23/%E9%98%BF%E9%87%8C%E5%9B%BD%E9%99%85%E6%9A%91%E5%AE%9E%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/05/23/%E9%98%BF%E9%87%8C%E5%9B%BD%E9%99%85%E6%9A%91%E5%AE%9E%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-05-23T13:43:37.000Z</published>
    <summary>阿里国际技术的面试记录分享。</summary>
    <title>阿里国际暑实面试分享</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h2 id="面试前的内容"><a href="#面试前的内容" class="headerlink" title="面试前的内容"></a>面试前的内容</h2><p>本来今天中午是打算面钉钉的，前面推了三次，终于约到了今天中午，结果提前淘天打电话让直接面试了，所以今天就先面了淘天</p><h2 id="淘天面试"><a href="#淘天面试" class="headerlink" title="淘天面试"></a>淘天面试</h2><p>在之前的时候淘天的视觉已经G了</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240522153311.png" alt="20240522153311"></p><p>事实证明，视觉这块真不行，要求太高了，真做不了，不是简历挂就是面试挂，今天给调到了机器学习。</p><p>面试首先就是自我介绍，然后开始问项目，问哪个项目印象深刻，然后就重点讲了第三个项目，讲了之后问论文做了啥，大致都讲了下，然后是比赛，问做的过程中用了什么方法，前几名是用的什么方法，有什么差距，具体是哪里的差距；然后我用的什么方法，有什么亮点，我说前几名用的主要方法都差不太多，重点在于策略和具体的应用调整。反正这块主要就是拷问简历上的项目了，毕竟前面笔试啥的都弄了，算法八股倒是没问。</p><p>之后是反问环节，然后问他们部门是做什么的，然后说他们主要是做搜广推的，主要是搜索和广告，然后大致讲了做啥。</p><p>已经G了</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240528131547.png" alt="20240528131547"></p><h2 id="钉钉面试"><a href="#钉钉面试" class="headerlink" title="钉钉面试"></a>钉钉面试</h2><p>刚面完淘天就是面钉钉，钉钉的这个笔试和面试挨着的，这个笔试不是力扣算法题，而是深度学习方面的题，首先手撕三选二：</p><ol><li>层归一化</li><li>多头注意力</li><li>kmeans</li></ol><p>层归一化我确实不太会理论，就没写，多头注意力是经典手撕了，凑合写了下，kmeans我是很熟悉的，毕竟大三第一次写这种算法就是kmeans， 手撕完之后让逐行讲解，凑合讲了下，不过细节还是有点漏，这块可以再看看。</p><p>之后是说怎么项目都是去年的，我说前面也有个，然后问了问论文，也问的详细，相比前人贡献是啥，我说这块前人工作很少，我主要是开辟了这样一个流程和处理方法，然后各种细节问了下，这块问的很细，具体来说，这个部分包括一个流程，其中视频部分具体用了什么模型，为什么用这个模型，这个模型架构是什么样的，具体是如何实现这个功能的，对比效果如何；检测和关键点部分用了什么模型，相应的模型架构是什么样的，卷积神经网络和transformer相比特点分别是什么。</p><p>然后问常见的激活函数都有哪些，然后都介绍一下，这块比较新的我都没怎么记，就只说了下之前的激活函数。</p><p>然后问最喜欢的网络模型是什么，为什么喜欢这个模型，讲一下结构，然后相比前人工作改进是什么，我大致讲了下，不过模型结构讲的不够细节。</p><p>之后是说看我简历上写了多模态大模型啥的，我说这块了解了些，但是发现算力什么的差很多，然后主要是做老师的项目，所以这块只是了解。</p><p>然后问职业规划，这块我没准备，也没经验，只能凑合说下，后面还是要准备下。</p><p>之后是说我学习能力强，各种项目都做，所以迁移到学习新东西也很快，然后问具体如何体现，这块我就不是很知道怎么回答了。</p><p>最后反问环节，我也是问了部门是做的什么，说部门主要三个方向，第一是风控，第二个是啥我给忘了，第三个就是大模型文生文，图生成文，语言和图文多模态啥的。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这两场面完仍然是汗流浃背，总之还是比上次淘天的要好一点吧，至少手撕八股准备了下。具体这次问项目和论文的细节也是非常的细致，具体很多问题已经记不清了，总之简历上的东西还是要详细到问每个细节都能答得上来。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/05/22/%E9%92%89%E9%92%89%E5%92%8C%E6%B7%98%E5%A4%A9%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%8D%9E%E6%9A%91%E6%9C%9F%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/05/22/%E9%92%89%E9%92%89%E5%92%8C%E6%B7%98%E5%A4%A9%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%8D%9E%E6%9A%91%E6%9C%9F%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-05-22T15:43:37.000Z</published>
    <summary>淘天技术的面试记录分享。</summary>
    <title>钉钉和淘天机器学习捞暑期实习面试分享</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<h1 id="面试凉经"><a href="#面试凉经" class="headerlink" title="面试凉经"></a>面试凉经</h1><p>估计没有二面了，这次被拷打的狠狠的，估计是无了，第一次视觉面，没有准备相关内容,第二天去看，果然已经流程结束<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240430181423.png" alt="20240430181423"></p><h2 id="自我介绍"><a href="#自我介绍" class="headerlink" title="自我介绍"></a>自我介绍</h2><p>和前面一样先自我介绍。</p><h2 id="项目拷打"><a href="#项目拷打" class="headerlink" title="项目拷打"></a>项目拷打</h2><p>这次我投的计算机视觉算法，面试的比较对口，面试官就问的很细，面试首先是问了几个项目的内容，我前面做的任务主要是通用视觉目标检测、姿态估计、分割等这些的项目，这些项目都没什么深度，主要是用，然后问的很细，就很难受，被拷打出来了没做啥有深度的内容，主要是应用，这块就不太行了。前面的面试面试官不是研究视觉的，就只大致问问做项目过程中遇到的难点，困难如何解决的，这次要先把做的任务说清楚，不清楚就会继续不断追问，然后说清楚做了什么，涉及较难的部分还会重点追问。但是这部分我往往都是只应用，并没有结构上的创新，然后具体用的什么模型，如何训练，如何处理，等等都会追问，问的非常细，项目虽然是自己做的，但是很多东西也没怎么想到，就非常难受。这里面除了一个配准的专利面试官可能没做过没问，其他所有项目比赛都非常仔细的抠细节。</p><p>现在想想，前面做过的项目也没有系统的整理里面的亮点和创新点等等，而且做的时候也很多时候只是用现有的技术做了，但是没有去想过创新点，含金量就不太够，还是要把做过的东西包装一下。</p><h2 id="简单的数据处理脚本"><a href="#简单的数据处理脚本" class="headerlink" title="简单的数据处理脚本"></a>简单的数据处理脚本</h2><p>然后面试官说让做题，首先是一个简单的路径字符串列表变成文件树的任务，这个非常简单的，这种类似的见得多了，但是当时太紧张了，然后这个我就没写出来，实在是不应该，其实另外一个写不出来的问题在于，平时写代码都有Github Copilot辅助，基本都是只需要写个大致的思路，辅助编程就能自动补全了，具体某个函数名字和参数什么的真不怎么记住，这个也是一个问题，以后要多写多记住一些基础的东西。</p><p>具体题目是：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">news/car/suv</span><br><span class="line">news/car/mpv</span><br><span class="line">news/sport/basketball</span><br><span class="line">news/sport/football</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">tree</span>(<span class="params">str_list</span>):</span><br><span class="line">    n = <span class="built_in">len</span>(str_list)</span><br><span class="line">    category_dict = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line">        strs = str_list[i].split(<span class="string">&#x27;/&#x27;</span>)</span><br><span class="line">        category, name = strs[<span class="number">1</span>], strs[<span class="number">2</span>]</span><br><span class="line">        <span class="keyword">if</span> category <span class="keyword">not</span> <span class="keyword">in</span> category_dict:</span><br><span class="line">            <span class="comment"># 这一步我都没想起来，实在是太离谱了</span></span><br><span class="line">            category_dict[category] = [name]</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            category_dict[category].append(name)</span><br><span class="line">    <span class="keyword">return</span> category_dict</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">print_tree</span>(<span class="params">category_dict, depth=<span class="number">1</span></span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;news&#x27;</span>)</span><br><span class="line">    <span class="keyword">for</span> key, value <span class="keyword">in</span> category_dict.items():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;\t&#x27;</span> * depth + key)</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(value, <span class="built_in">dict</span>):</span><br><span class="line">            print_tree(value, depth + <span class="number">1</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">for</span> v <span class="keyword">in</span> value:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&#x27;\t&#x27;</span> * (depth + <span class="number">1</span>) + v)</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    str_list = [<span class="string">&#x27;news/car/suv&#x27;</span>, <span class="string">&#x27;news/car/mpv&#x27;</span>, <span class="string">&#x27;news/sport/basketball&#x27;</span>, <span class="string">&#x27;news/sport/football&#x27;</span>]</span><br><span class="line">    category_dict = tree(str_list)</span><br><span class="line">    print_tree(category_dict)</span><br></pre></td></tr></table></figure><h2 id="logistic回归"><a href="#logistic回归" class="headerlink" title="logistic回归"></a>logistic回归</h2><p>让说一下logistic回归，我就说了下公式，然后说前些年用，因为方便求导，简单说了下，具体用来更新模型的参数没说。这相关的算法也是学过的，但是要清晰讲出来还是要准备的。</p><h2 id="手撕注意力"><a href="#手撕注意力" class="headerlink" title="手撕注意力"></a>手撕注意力</h2><p>然后让写一个attention的公式，然后也没写出来，就简单说了下原理，肯定是不行的，这种都是算法的八股，我应该提前准备下的。</p><h3 id="chatgpt答案"><a href="#chatgpt答案" class="headerlink" title="chatgpt答案"></a>chatgpt答案</h3><p>注意力机制是一种让模型能够在处理信息时自动地聚焦于最重要信息的技术。在深度学习中，注意力机制通常用于序列模型，特别是在自然语言处理领域。最常见的注意力机制是由 Vaswani 等人在 2017 年提出的“Scaled Dot-Product Attention”。</p><h4 id="注意力机制的数学公式"><a href="#注意力机制的数学公式" class="headerlink" title="注意力机制的数学公式"></a>注意力机制的数学公式</h4><p>基础的注意力机制可以表示为三个核心组件：查询（Query），键（Key），值（Value）。给定查询 ( Q )，键 ( K )，和值 ( V )，注意力权重计算通常包括以下步骤：</p><ol><li><p><strong>注意力得分（Score）计算</strong>：<br>[ \text{Score}(Q, K) &#x3D; QK^T ]</p></li><li><p><strong>缩放（Scaling）</strong>：<br>[ \text{Scaled Score} &#x3D; \frac{\text{Score}}{\sqrt{d_k}} ]<br>其中 ( d_k ) 是键向量的维度，这一步骤是为了避免计算中的梯度过小。</p></li><li><p><strong>Softmax 归一化</strong>：<br>[ \text{Attention Weights} &#x3D; \text{softmax}(\text{Scaled Score}) ]</p></li><li><p><strong>输出计算</strong>：<br>[ \text{Output} &#x3D; \text{Attention Weights} \cdot V ]</p></li></ol><h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><p>在 Python 中，可以使用 PyTorch 这样的深度学习框架来实现注意力机制。下面是一个简单的 Scaled Dot-Product Attention 的实现示例：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><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.functional <span class="keyword">as</span> F</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">scaled_dot_product_attention</span>(<span class="params">Q, K, V, mask=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    实现 Scaled Dot-Product Attention。</span></span><br><span class="line"><span class="string">    参数:</span></span><br><span class="line"><span class="string">    Q: 查询张量 [batch_size, num_heads, query_len, key_dim]</span></span><br><span class="line"><span class="string">    K: 键张量 [batch_size, num_heads, key_len, key_dim]</span></span><br><span class="line"><span class="string">    V: 值张量 [batch_size, num_heads, value_len, value_dim]</span></span><br><span class="line"><span class="string">    mask: 掩码张量，用于遮蔽不需要的值 [batch_size, 1, 1, key_len]</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    返回:</span></span><br><span class="line"><span class="string">    output: 注意力机制的结果 [batch_size, num_heads, query_len, value_dim]</span></span><br><span class="line"><span class="string">    attention_weights: 注意力权重 [batch_size, num_heads, query_len, key_len]</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    d_k = Q.size(-<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 计算 Q 和 K 的点积，然后除以 sqrt(d_k)</span></span><br><span class="line">    scores = torch.matmul(Q, K.transpose(-<span class="number">2</span>, -<span class="number">1</span>)) / torch.sqrt(torch.tensor(d_k, dtype=torch.float32))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 如果有掩码，将掩码加到 scores 上</span></span><br><span class="line">    <span class="keyword">if</span> mask <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        scores = scores.masked_fill(mask == <span class="number">0</span>, <span class="built_in">float</span>(<span class="string">&#x27;-inf&#x27;</span>))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 应用 Softmax 函数获取注意力权重</span></span><br><span class="line">    attention_weights = F.softmax(scores, dim=-<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 计算最终的输出</span></span><br><span class="line">    output = torch.matmul(attention_weights, V)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> output, attention_weights</span><br></pre></td></tr></table></figure><p>这段代码实现了 Scaled Dot-Product Attention，其中 <code>Q</code>, <code>K</code>, <code>V</code> 是输入的张量，可来自相同的或不同的输入源。<code>mask</code> 参数用于可选的掩码操作，通常用在处理变长序列时忽略填充项。注意力权重通过点积、缩放、Softmax 计算得出，最后使用这些权重对值 <code>V</code> 进行加权求和，得到每个查询的输出。</p><h4 id="手撕Attention"><a href="#手撕Attention" class="headerlink" title="手撕Attention"></a>手撕Attention</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Attention</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, dim, num_heads=<span class="number">8</span>, qkv_bias=<span class="literal">False</span>, qk_scale=<span class="literal">None</span>, attn_drop=<span class="number">0.</span>, proj_drop=<span class="number">0.</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        self.num_heads = num_heads</span><br><span class="line">        head_dim = dim // num_heads</span><br><span class="line">        <span class="comment"># NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights</span></span><br><span class="line">        self.scale = qk_scale <span class="keyword">or</span> head_dim ** -<span class="number">0.5</span></span><br><span class="line"></span><br><span class="line">        self.qkv = nn.Linear(dim, dim * <span class="number">3</span>, bias=qkv_bias)</span><br><span class="line">        self.attn_drop = nn.Dropout(attn_drop)</span><br><span class="line">        self.proj = nn.Linear(dim, dim)</span><br><span class="line">        self.proj_drop = nn.Dropout(proj_drop)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        B, N, C = x.shape</span><br><span class="line">        qkv = self.qkv(x).reshape(B, N, <span class="number">3</span>, self.num_heads, C // self.num_heads).permute(<span class="number">2</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>)</span><br><span class="line">        q, k, v = qkv[<span class="number">0</span>], qkv[<span class="number">1</span>], qkv[<span class="number">2</span>]  <span class="comment"># make torchscript happy (cannot use tensor as tuple)</span></span><br><span class="line"></span><br><span class="line">        attn = (q @ k.transpose(-<span class="number">2</span>, -<span class="number">1</span>)) * self.scale</span><br><span class="line">        attn = attn.softmax(dim=-<span class="number">1</span>)</span><br><span class="line">        attn = self.attn_drop(attn)</span><br><span class="line"></span><br><span class="line">        x = (attn @ v).transpose(<span class="number">1</span>, <span class="number">2</span>).reshape(B, N, C)</span><br><span class="line">        x = self.proj(x)</span><br><span class="line">        x = self.proj_drop(x)</span><br><span class="line">        <span class="keyword">return</span> x</span><br></pre></td></tr></table></figure><h4 id="MLP的代码"><a href="#MLP的代码" class="headerlink" title="MLP的代码"></a>MLP的代码</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Mlp</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, in_features, hidden_features=<span class="literal">None</span>, out_features=<span class="literal">None</span>, act_layer=nn.GELU, drop=<span class="number">0.</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        out_features = out_features <span class="keyword">or</span> in_features</span><br><span class="line">        hidden_features = hidden_features <span class="keyword">or</span> in_features</span><br><span class="line">        self.fc1 = nn.Linear(in_features, hidden_features)</span><br><span class="line">        self.act = act_layer()</span><br><span class="line">        self.fc2 = nn.Linear(hidden_features, out_features)</span><br><span class="line">        self.drop = nn.Dropout(drop)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        x = self.fc1(x)</span><br><span class="line">        x = self.act(x)</span><br><span class="line">        x = self.drop(x)</span><br><span class="line">        x = self.fc2(x)</span><br><span class="line">        x = self.drop(x)</span><br><span class="line">        <span class="keyword">return</span> x</span><br></pre></td></tr></table></figure><h2 id="扩散模型"><a href="#扩散模型" class="headerlink" title="扩散模型"></a>扩散模型</h2><p>然后问扩散模型知道不，然后我说没做过，这里也是改准备下的，毕竟这么著名的模型，我虽然大致了解原理还有DDPM什么的，但是如果认真的讲出来，不成体系，我也就没敢说，这块还是要准备下。</p><h3 id="chatgpt回答"><a href="#chatgpt回答" class="headerlink" title="chatgpt回答"></a>chatgpt回答</h3><p>扩散模型是一种新兴的深度学习方法，特别是在生成模型领域中表现出色。这种模型基于随机过程理论，特别是扩散过程，通过逐步增加随机性来转换数据，然后再逐步学习如何逆转这一过程，从而生成高质量的数据样本。这一方法在图像和声音生成中尤其流行。</p><h4 id="扩散模型的基本原理"><a href="#扩散模型的基本原理" class="headerlink" title="扩散模型的基本原理"></a>扩散模型的基本原理</h4><p>扩散模型包括两个主要阶段：<strong>正向过程</strong>（forward process）和<strong>反向过程</strong>（reverse process）。</p><ol><li><p><strong>正向过程（噪声添加过程）</strong>：<br>在正向过程中，模型逐步地将噪声引入到原始数据中。这通常通过多个时间步骤实现，每个时间步骤都会向数据添加更多的高斯噪声，直到数据完全转变为纯噪声。这个过程可以用以下等式表示：<br>[<br>x_{t} &#x3D; \sqrt{\alpha_t} x_{t-1} + \sqrt{1-\alpha_t} \epsilon_t<br>]<br>其中，( x_t ) 是在时间 ( t ) 的数据，( \alpha_t ) 是时间步骤中噪声的比例，而 ( \epsilon_t ) 是从标准正态分布中采样的噪声。</p></li><li><p><strong>反向过程（噪声去除过程）</strong>：<br>反向过程旨在学习如何从纯噪声数据逐步恢复出原始数据。这是通过一个参数化的神经网络模型实现的，该模型试图预测给定噪声数据的前一时间步骤的状态。反向过程的目标是逐渐减少数据中的噪声，最终恢复出接近原始数据的状态。</p></li></ol><h4 id="训练扩散模型"><a href="#训练扩散模型" class="headerlink" title="训练扩散模型"></a>训练扩散模型</h4><p>扩散模型的训练通常依赖于变分下界（ELBO，Evidence Lower Bound）的优化。模型的目标是最小化重构数据和原始数据之间的差异，同时也优化生成数据的概率分布。训练过程需要大量的计算资源，因为它涉及到大量的正向和反向模拟步骤。</p><h2 id="FM算法"><a href="#FM算法" class="headerlink" title="FM算法"></a>FM算法</h2><p>然后问机器学习算法FM算法，然后我说不知道，这个我还真没听说过，查了下，是推荐的算法，因子分解机，真不懂，现学记住吧，chatgpt说：</p><h3 id="chatgpt回答-1"><a href="#chatgpt回答-1" class="headerlink" title="chatgpt回答"></a>chatgpt回答</h3><p>FM（Factorization Machines，因子分解机）是一种广泛用于推荐系统和预测任务的机器学习算法。它被设计用来处理具有极大稀疏性的数据集，特别是在用户-物品交互数据中常见的那种稀疏性。</p><p><em>基本原理</em>: 因子分解机模型主要是为了解决传统线性模型（如线性回归、逻辑回归）在处理大规模稀疏数据集时的不足。在传统的线性模型中，每个特征的权重是独立学习的，这使得模型难以捕捉特征间的交互作用。FM通过引入隐向量来为每个特征学习潜在的交互效应，这样可以有效地捕捉到特征之间的相互作用。<br><em>数学表示</em>: 给定一个包含$n$个特征的数据集，FM模型的预测$\hat{y}$由下式给出：<br>$$<br>\hat{y}(\mathbf{x}) &#x3D; w_0 + \sum_{i&#x3D;1}^{n}w_i x_i + \sum_{i&#x3D;1}^{n} \sum_{j&#x3D;i+1}^{n} \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x_j<br>$$<br>其中：</p><ul><li>$w_0$ 是模型的全局偏置。</li><li>$w_i$ 是第$i$个特征的权重。</li><li>$\mathbf{x}$ 是输入特征向量。</li><li>$\mathbf{v}_i$ 和 $\mathbf{v}_j$ 是特征$i$和$j$的隐向量，隐向量的维度$k$通常远小于特征数量$n$。</li><li>$\langle \mathbf{v}_i, \mathbf{v}_j \rangle$ 表示特征$i$和$j$的隐向量的点积，用于模拟这两个特征的交互效果。</li></ul><p><em>优势</em>:</p><ol><li><p><strong>高效性</strong>：虽然FM模型包含二阶特征交互项，但是它可以在$O(kn)$的时间复杂度内完成计算，其中$k$是隐向量的维度，$n$是特征的数量。这种高效性主要来源于隐向量的点积运算方式，使得算法适用于大规模的数据集。</p></li><li><p><strong>灵活性</strong>：FM模型不仅适用于任何实数特征，还可以很容易地扩展到高阶交互，如三阶或更高阶的交互。</p></li><li><p><strong>泛化能力</strong>：由于特征间的交互是通过低维隐向量学习的，FM可以很好地泛化到新的、未见过的特征组合，尤其是在数据稀疏的场景下。</p></li></ol><p><em>应用场景</em>：</p><ul><li><strong>推荐系统</strong>：FM广泛用于推荐系统，能有效处理用户和物品的特征交互。</li><li><strong>点击率预测</strong>：在线广告和搜索引擎优化中，FM可以用来预测用户点击广告或搜索结果的概率。</li><li><strong>排名和分类任务</strong>：通过适当的目标函数和优化方法，FM可以适用于各种排名和分类问题。</li></ul><p>因子分解机因其在处理稀疏数据和特征交互中的优势而被广泛研究和应用。</p><h2 id="手写交叉熵损失"><a href="#手写交叉熵损失" class="headerlink" title="手写交叉熵损失"></a>手写交叉熵损失</h2><p>然后让写CrossEntropy，我还是没写出来，然后简单说了下公式的思想，</p><h3 id="chatgpt回答-2"><a href="#chatgpt回答-2" class="headerlink" title="chatgpt回答"></a>chatgpt回答</h3><p>交叉熵损失（Cross-Entropy Loss）是用于衡量模型预测概率分布与真实标签概率分布之间的差异的一个常用损失函数。它在分类任务中特别常见，尤其是在处理二分类或多分类问题时。</p><h4 id="交叉熵损失的公式"><a href="#交叉熵损失的公式" class="headerlink" title="交叉熵损失的公式"></a>交叉熵损失的公式</h4><p>对于二分类问题，交叉熵损失可以表示为：</p><p>[ L &#x3D; -\frac{1}{N} \sum_{i&#x3D;1}^N [y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)] ]</p><p>其中：</p><ul><li>( N ) 是样本的数量。</li><li>( y_i ) 是第 ( i ) 个样本的真实标签，通常为 0 或 1。</li><li>( \hat{y}_i ) 是模型预测第 ( i ) 个样本为正类的概率。</li></ul><p>对于多分类问题，公式扩展为：</p><p>[ L &#x3D; -\frac{1}{N} \sum_{i&#x3D;1}^N \sum_{c&#x3D;1}^C y_{i,c} \log(\hat{y}_{i,c}) ]</p><p>其中：</p><ul><li>( C ) 是类别的总数。</li><li>( y_{i,c} ) 是一个指示变量，如果样本 ( i ) 属于类别 ( c )，则为 1，否则为 0。</li><li>( \hat{y}_{i,c} ) 是模型预测第 ( i ) 个样本属于类别 ( c ) 的概率。</li></ul><h3 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h3><p>如果不使用库函数，可以手动实现交叉熵损失函数。这里我们将以 Python 为例展示如何实现多分类交叉熵损失函数。这将涉及到计算模型输出的对数概率，然后根据真实标签计算损失。</p><h4 id="二分类交叉熵损失"><a href="#二分类交叉熵损失" class="headerlink" title="二分类交叉熵损失"></a>二分类交叉熵损失</h4><p>首先，我们从较简单的二分类交叉熵损失开始：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><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="keyword">def</span> <span class="title function_">binary_cross_entropy_loss</span>(<span class="params">y_true, y_pred</span>):</span><br><span class="line">    <span class="comment"># 防止 log(0) 导致数值问题，所以加上一个非常小的值 epsilon</span></span><br><span class="line">    epsilon = <span class="number">1e-12</span></span><br><span class="line">    y_pred = np.clip(y_pred, epsilon, <span class="number">1</span> - epsilon)</span><br><span class="line">    loss = -np.mean(y_true * np.log(y_pred) + (<span class="number">1</span> - y_true) * np.log(<span class="number">1</span> - y_pred))</span><br><span class="line">    <span class="keyword">return</span> loss</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例</span></span><br><span class="line">y_true = np.array([<span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>])</span><br><span class="line">y_pred = np.array([<span class="number">0.1</span>, <span class="number">0.9</span>, <span class="number">0.8</span>, <span class="number">0.2</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Binary Cross Entropy Loss:&quot;</span>, binary_cross_entropy_loss(y_true, y_pred))</span><br></pre></td></tr></table></figure><h4 id="多分类交叉熵损失"><a href="#多分类交叉熵损失" class="headerlink" title="多分类交叉熵损失"></a>多分类交叉熵损失</h4><p>对于多分类问题，我们通常使用独热编码的方式来表示真实的标签，然后计算每个类别的交叉熵损失：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">categorical_cross_entropy_loss</span>(<span class="params">y_true, y_pred</span>):</span><br><span class="line">    <span class="comment"># 防止 log(0) 导致数值问题，所以加上一个非常小的值 epsilon</span></span><br><span class="line">    epsilon = <span class="number">1e-12</span></span><br><span class="line">    y_pred = np.clip(y_pred, epsilon, <span class="number">1.0</span> - epsilon)</span><br><span class="line">    <span class="comment"># 计算交叉熵</span></span><br><span class="line">    loss = -np.<span class="built_in">sum</span>(y_true * np.log(y_pred)) / y_true.shape[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">return</span> loss</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例</span></span><br><span class="line">y_true = np.array([[<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>], [<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>], [<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>], [<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>]])</span><br><span class="line">y_pred = np.array([</span><br><span class="line">    [<span class="number">0.05</span>, <span class="number">0.9</span>, <span class="number">0.05</span>],</span><br><span class="line">    [<span class="number">0.1</span>, <span class="number">0.1</span>, <span class="number">0.8</span>],</span><br><span class="line">    [<span class="number">0.7</span>, <span class="number">0.2</span>, <span class="number">0.1</span>],</span><br><span class="line">    [<span class="number">0.1</span>, <span class="number">0.8</span>, <span class="number">0.1</span>]</span><br><span class="line">])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Categorical Cross Entropy Loss:&quot;</span>, categorical_cross_entropy_loss(y_true, y_pred))</span><br></pre></td></tr></table></figure><p>在这些示例中，<code>y_pred</code> 中的值必须是有效的概率分布，即所有预测概率的和应为1，且各概率值应在0到1之间。在实际应用中，模型的输出通常通过 softmax 函数来保证这一点。<code>np.clip</code> 用于防止数值问题，如计算对数时输入值为0。这些实现提供了对交叉熵损失计算的直接控制，避免了依赖外部库函数的需求。</p><h2 id="U-Net相关"><a href="#U-Net相关" class="headerlink" title="U-Net相关"></a>U-Net相关</h2><p>我说研究方向主要是医学影像分割分类，然后面试官说前面做的这些和这个也没什么关系，然后问医学影像出了什么成果，然后我也没，然后问U-Net，我说了下，然后问U-Net的原理，我说了下，然后后上下采样是如何做的，残差连接是如何做的，我说是拼接，这块要说原理我也是说的不太清楚，也没系统准备过。</p><p>然后问那个医学影像的竞赛，说用到nnUNet了，然后介绍下，也说不清。</p><p>然后问科研医学影像现在做到什么阶段了，我就说是分割分类任务的大致框架，具体仍然说不系统。</p><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><p>引用链接<a href="https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw">https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw</a></p><ul><li>算法工程师面试常考手撕题<ul><li>注意力（Attention）篇<ul><li>手撕单头注意力机制（ScaledDotProductAttention）函数</li><li>手撕多头注意力（MultiHeadAttention）</li><li>手撕自注意力机制函数（SelfAttention）</li><li>GPT2 解码中的KV Cache</li><li>手撕 MQA 算法</li></ul></li><li>基础机器学习算法篇<ul><li>手撕 numpy写线性回归的随机梯度下降（stochastic gradient descent，SGD）</li><li>手撕 k-means 算法</li></ul></li><li>手撕 Layer Normalization 算法</li><li>手撕 Batch Normalization 算法</li><li>解码算法篇<ul><li>手撕 贪心搜索 （greedy search）</li><li>手撕 集束搜索 beamsearch 算法</li><li>手撕 温度参数采样（Temperature Sampling）算法</li><li>手撕 Top-K Sampling算法</li><li>手撕 Top-P (Nucleus) Sampling 算法</li></ul></li><li>神经网络篇<ul><li>手撕反向传播(backward propagation，BP)法</li><li>手撕 卷积神经网络(CNN)法</li><li>手撕 循环神经网络(RNN)法</li><li>手撕 LSTM法</li><li>手撕 二维卷积 算法</li></ul></li><li>位置编码篇<ul><li>手撕 绝对位置编码 算法</li><li>手撕 可学习位置编码 算法</li><li>手撕 相对位置编码 算法</li><li>手撕 rope 算法</li></ul></li><li>面试题汇总</li><li>致谢</li></ul></li></ul><h2 id="注意力（Attention）篇"><a href="#注意力（Attention）篇" class="headerlink" title="注意力（Attention）篇"></a><strong>注意力（Attention）篇</strong></h2><h3 id="手撕单头注意力机制（ScaledDotProductAttention）函数"><a href="#手撕单头注意力机制（ScaledDotProductAttention）函数" class="headerlink" title="手撕单头注意力机制（ScaledDotProductAttention）函数"></a><strong>手撕单头注意力机制（ScaledDotProductAttention）函数</strong></h3><p>输入是query和 key-value，注意力机制首先计算query与每个key的关联性（compatibility），每个关联性作为每个value的权重（weight），各个权重与value的乘积相加得到输出。<br><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444477-8e426dd3-37ed-473f-ab5f-7d445ed7592b.jpeg#clientId=u8884b877-175c-4&from=paste&id=u147acd2d&originHeight=75&originWidth=390&originalType=url&ratio=1&rotation=0&showTitle=false&size=4542&status=done&style=none&taskId=u5130429b-212c-4651-83d6-557bf8c86d9&title=" alt="image.jpg"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">class ScaledDotProductAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot; Scaled Dot-Product Attention &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self, scale):</span><br><span class="line">        super().__init__()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.scale = scale</span><br><span class="line">        self.softmax = nn.Softmax(dim=2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, q, k, v, mask=None):</span><br><span class="line">        u = torch.bmm(q, k.transpose(1, 2)) # 1.Matmul</span><br><span class="line">        u = u / self.scale # 2.Scale</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if mask is not None:</span><br><span class="line">            u = u.masked_fill(mask, -np.inf) # 3.Mask</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn = self.softmax(u) # 4.Softmax</span><br><span class="line">        output = torch.bmm(attn, v) # 5.Output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return attn, output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    n_q, n_k, n_v = 2, 4, 4</span><br><span class="line">    d_q, d_k, d_v = 128, 128, 64</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    q = torch.randn(batch, n_q, d_q)</span><br><span class="line">    k = torch.randn(batch, n_k, d_k)</span><br><span class="line">    v = torch.randn(batch, n_v, d_v)</span><br><span class="line">    mask = torch.zeros(batch, n_q, n_k).bool()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    attention = ScaledDotProductAttention(scale=np.power(d_k, 0.5))</span><br><span class="line">    attn, output = attention(q, k, v, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    print(attn)</span><br><span class="line">    print(output)</span><br></pre></td></tr></table></figure><h3 id="手撕多头注意力（MultiHeadAttention）"><a href="#手撕多头注意力（MultiHeadAttention）" class="headerlink" title="手撕多头注意力（MultiHeadAttention）"></a><strong>手撕多头注意力（MultiHeadAttention）</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line">class MultiHeadAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot; Multi-Head Attention &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self, n_head, d_k_, d_v_, d_k, d_v, d_o):</span><br><span class="line">        super().__init__()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.n_head = n_head</span><br><span class="line">        self.d_k = d_k</span><br><span class="line">        self.d_v = d_v</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.fc_q = nn.Linear(d_k_, n_head * d_k)</span><br><span class="line">        self.fc_k = nn.Linear(d_k_, n_head * d_k)</span><br><span class="line">        self.fc_v = nn.Linear(d_v_, n_head * d_v)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.attention = ScaledDotProductAttention(scale=np.power(d_k, 0.5))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.fc_o = nn.Linear(n_head * d_v, d_o)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, q, k, v, mask=None):</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        n_head, d_q, d_k, d_v = self.n_head, self.d_k, self.d_k, self.d_v</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        batch, n_q, d_q_ = q.size()</span><br><span class="line">        batch, n_k, d_k_ = k.size()</span><br><span class="line">        batch, n_v, d_v_ = v.size()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        q = self.fc_q(q) # 1.单头变多头</span><br><span class="line">        k = self.fc_k(k)</span><br><span class="line">        v = self.fc_v(v)</span><br><span class="line">        q = q.view(batch, n_q, n_head, d_q).permute(2, 0, 1, 3).contiguous().view(-1, n_q, d_q)</span><br><span class="line">        k = k.view(batch, n_k, n_head, d_k).permute(2, 0, 1, 3).contiguous().view(-1, n_k, d_k)</span><br><span class="line">        v = v.view(batch, n_v, n_head, d_v).permute(2, 0, 1, 3).contiguous().view(-1, n_v, d_v)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if mask is not None:</span><br><span class="line">            mask = mask.repeat(n_head, 1, 1)</span><br><span class="line">        attn, output = self.attention(q, k, v, mask=mask) # 2.当成单头注意力求输出</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        output = output.view(n_head, batch, n_q, d_v).permute(1, 2, 0, 3).contiguous().view(batch, n_q, -1) # 3.Concat</span><br><span class="line">        output = self.fc_o(output) # 4.仿射变换得到最终输出</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return attn, output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    n_q, n_k, n_v = 2, 4, 4</span><br><span class="line">    d_q_, d_k_, d_v_ = 128, 128, 64</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    q = torch.randn(batch, n_q, d_q_)</span><br><span class="line">    k = torch.randn(batch, n_k, d_k_)</span><br><span class="line">    v = torch.randn(batch, n_v, d_v_)    </span><br><span class="line">    mask = torch.zeros(batch, n_q, n_k).bool()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    mha = MultiHeadAttention(n_head=8, d_k_=128, d_v_=64, d_k=256, d_v=128, d_o=128)</span><br><span class="line">    attn, output = mha(q, k, v, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    print(attn.size())</span><br><span class="line">    print(output.size())</span><br></pre></td></tr></table></figure><h3 id="手撕自注意力机制函数（SelfAttention）"><a href="#手撕自注意力机制函数（SelfAttention）" class="headerlink" title="手撕自注意力机制函数（SelfAttention）"></a><strong>手撕自注意力机制函数（SelfAttention）</strong></h3><p>Self-Attention。和Attention类似，他们都是一种注意力机制。不同的是Attention是source对target，输入的source和输出的target内容不同。例如英译中，输入英文，输出中文。而Self-Attention是source对source，是source内部元素之间或者target内部元素之间发生的Attention机制，也可以理解为Target&#x3D;Source这种特殊情况下的注意力机制。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">class SelfAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot; Self-Attention &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self, n_head, d_k, d_v, d_x, d_o):</span><br><span class="line">        self.wq = nn.Parameter(torch.Tensor(d_x, d_k))</span><br><span class="line">        self.wk = nn.Parameter(torch.Tensor(d_x, d_k))</span><br><span class="line">        self.wv = nn.Parameter(torch.Tensor(d_x, d_v))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.mha = MultiHeadAttention(n_head=n_head, d_k_=d_k, d_v_=d_v, d_k=d_k, d_v=d_v, d_o=d_o)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.init_parameters()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def init_parameters(self):</span><br><span class="line">        for param in self.parameters():</span><br><span class="line">            stdv = 1. / np.power(param.size(-1), 0.5)</span><br><span class="line">            param.data.uniform_(-stdv, stdv)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, x, mask=None):</span><br><span class="line">        q = torch.matmul(x, self.wq)   </span><br><span class="line">        k = torch.matmul(x, self.wk)</span><br><span class="line">        v = torch.matmul(x, self.wv)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn, output = self.mha(q, k, v, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return attn, output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    n_x = 4</span><br><span class="line">    d_x = 80</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    x = torch.randn(batch, n_x, d_x)</span><br><span class="line">    mask = torch.zeros(batch, n_x, n_x).bool()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    selfattn = SelfAttention(n_head=8, d_k=128, d_v=64, d_x=80, d_o=80)</span><br><span class="line">    attn, output = selfattn(x, mask=mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    print(attn.size())</span><br><span class="line">    print(output.size())</span><br></pre></td></tr></table></figure><h3 id="GPT2-解码中的KV-Cache"><a href="#GPT2-解码中的KV-Cache" class="headerlink" title="GPT2 解码中的KV Cache"></a><strong>GPT2 解码中的KV Cache</strong></h3><p>无论是encoder-decoder结构，还是现在我们最接近AGI的decoder-only的LLM，解码生成时都是自回归auto-regressive的方式。<br>也就是，解码的时候，先根据当前输入  ，生成下一个  ，然后把新生成的  拼接在  后面，获得新的输入  ，再用  生成  ，依此迭代，直到生成结束。<br>我们可以注意到，下一个step的输入其实包含了上一个step的内容，而且只在最后面多了一点点（一个token）。那么下一个step的计算应该也包含了上一个step的计算。<br>但是模型在推理的时候可不管这些，无论你是不是只要最后一个字的输出，它都把所有输入计算一遍，给出所有输出结果。<br>也就是说中间有很多我们用不到的计算，这样就造成了浪费。<br>而且随着生成的结果越来越多，输入的长度也越来越长，上面这个例子里，输入长度就从step0的10个，每步增长1，直到step5的15个。如果输入的instruction是让模型写作文，那可能就有800个step。这个情况下，step0被算了800次，step1被算了799次…这样浪费的计算资源确实不容忽视。<br>有没有什么办法可以重复利用上一个step里已经计算过的结果，减少浪费呢？<br>答案就是KV Cache，利用一个缓存，把需要重复利用的中间计算结果存下来，减少重复计算。<br>而 k 和 v 就是我要缓存的对象。<br>想象一下，在上面的例子中，假设我们一开始的输入就是3个字，我们第一次预测就是预测第4个字，那么由于一开始没有任何缓存，所有我们每一层还是要老实地计算一遍。然后把 k 、 v 值缓存起来。<br>则有<br><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444470-9305c89b-aa36-4998-b623-9c3b1c0caf41.jpeg#clientId=u8884b877-175c-4&from=paste&id=u1cb9436b&originHeight=196&originWidth=467&originalType=url&ratio=1&rotation=0&showTitle=false&size=6239&status=done&style=none&taskId=ud1d4d6ec-170b-458b-a212-499e5614e66&title=" alt="image.jpg"><br>kv cache的下标l表示模型层数。<br>在进行第二次预测，也就是预测第5个字的时候，在第l层的时候，由于前面我们缓存了每层的ku 值，那本层就只需要算新的 o3，而不用算 o0、o1、o2。<br>因为第l层的 o0、o1、o2本来会经过FNN层之后进到 l十1 层，再经过新的投影变换，成为 l + 1 层的 k、υ 值，但是l十 1 层的 k、υ值我们已经缓存过了!<br>然后我们把本次新增算出来的 k、υ 值也存入缓存。<br><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444594-19c3638e-08f5-4a3c-b392-afed682a17a1.jpeg#clientId=u8884b877-175c-4&from=paste&id=udd8910f7&originHeight=185&originWidth=482&originalType=url&ratio=1&rotation=0&showTitle=false&size=8952&status=done&style=none&taskId=u082ebbba-f43b-4beb-9b72-69ac67eb0ef&title=" alt="image.jpg"><br>这样就节省了attention和FFN的很多重复计算。<br>transformers中，生成的时候传入use_cache&#x3D;True就会开启KV Cache。<br>也可以简单看下GPT2中的实现，中文注释的部分就是使用缓存结果和更新缓存结果</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line">Class GPT2Attention(nn.Module):</span><br><span class="line">    ...</span><br><span class="line">    ...</span><br><span class="line">    def forward(</span><br><span class="line">        self,</span><br><span class="line">        hidden_states: Optional[Tuple[torch.FloatTensor]],</span><br><span class="line">        layer_past: Optional[Tuple[torch.Tensor]] = None,</span><br><span class="line">        attention_mask: Optional[torch.FloatTensor] = None,</span><br><span class="line">        head_mask: Optional[torch.FloatTensor] = None,</span><br><span class="line">        encoder_hidden_states: Optional[torch.Tensor] = None,</span><br><span class="line">        encoder_attention_mask: Optional[torch.FloatTensor] = None,</span><br><span class="line">        use_cache: Optional[bool] = False,</span><br><span class="line">        output_attentions: Optional[bool] = False,</span><br><span class="line">    ) -&gt; Tuple[Union[torch.Tensor, Tuple[torch.Tensor]], ...]:</span><br><span class="line">        if encoder_hidden_states is not None:</span><br><span class="line">            if not hasattr(self, &quot;q_attn&quot;):</span><br><span class="line">                raise ValueError(</span><br><span class="line">                    &quot;If class is used as cross attention, the weights `q_attn` have to be defined. &quot;</span><br><span class="line">                    &quot;Please make sure to instantiate class with `GPT2Attention(..., is_cross_attention=True)`.&quot;</span><br><span class="line">                )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            query = self.q_attn(hidden_states)</span><br><span class="line">            key, value = self.c_attn(encoder_hidden_states).split(self.split_size, dim=2)</span><br><span class="line">            attention_mask = encoder_attention_mask</span><br><span class="line">        else:</span><br><span class="line">            query, key, value = self.c_attn(hidden_states).split(self.split_size, dim=2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        query = self._split_heads(query, self.num_heads, self.head_dim)</span><br><span class="line">        key = self._split_heads(key, self.num_heads, self.head_dim)</span><br><span class="line">        value = self._split_heads(value, self.num_heads, self.head_dim)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        # 过去所存的值</span><br><span class="line">        if layer_past is not None:</span><br><span class="line">            past_key, past_value = layer_past</span><br><span class="line">            key = torch.cat((past_key, key), dim=-2)  # 把当前新的key加入</span><br><span class="line">            value = torch.cat((past_value, value), dim=-2)  # 把当前新的value加入</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if use_cache is True:</span><br><span class="line">            present = (key, value)  # 输出用于保存</span><br><span class="line">        else:</span><br><span class="line">            present = None</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if self.reorder_and_upcast_attn:</span><br><span class="line">            attn_output, attn_weights = self._upcast_and_reordered_attn(query, key, value, attention_mask, head_mask)</span><br><span class="line">        else:</span><br><span class="line">            attn_output, attn_weights = self._attn(query, key, value, attention_mask, head_mask)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn_output = self._merge_heads(attn_output, self.num_heads, self.head_dim)</span><br><span class="line">        attn_output = self.c_proj(attn_output)</span><br><span class="line">        attn_output = self.resid_dropout(attn_output)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        outputs = (attn_output, present)</span><br><span class="line">        if output_attentions:</span><br><span class="line">            outputs += (attn_weights,)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return outputs  # a, present, (attentions)</span><br></pre></td></tr></table></figure><p>总的来说，KV Cache是以空间换时间的做法，通过使用快速的缓存存取，减少了重复计算。（注意，只有decoder结构的模型可用，因为有mask attention的存在，使得前面的token可以不用关注后面的token）</p><h3 id="手撕-MQA-算法"><a href="#手撕-MQA-算法" class="headerlink" title="手撕 MQA 算法"></a><strong>手撕 MQA 算法</strong></h3><p>MQA 让所有的头之间 共享 同一份 Key 和 Value 矩阵，每个头只单独保留了一份 Query 参数，从而大大减少 Key 和 Value 矩阵的参数量。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line">class MultiQueryAttention(nn.Module):</span><br><span class="line">    &quot;&quot;&quot;Multi-Query self attention.</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    Using torch or triton attention implemetation enables user to also use</span><br><span class="line">    additive bias.</span><br><span class="line">    &quot;&quot;&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(</span><br><span class="line">        self,</span><br><span class="line">        d_model: int,</span><br><span class="line">        n_heads: int,</span><br><span class="line">        attn_impl: str = &#x27;triton&#x27;,</span><br><span class="line">        clip_qkv: Optional[float] = None,</span><br><span class="line">        qk_ln: bool = False,</span><br><span class="line">        softmax_scale: Optional[float] = None,</span><br><span class="line">        attn_pdrop: float = 0.0,</span><br><span class="line">        low_precision_layernorm: bool = False,</span><br><span class="line">        verbose: int = 0,</span><br><span class="line">        device: Optional[str] = None,</span><br><span class="line">    ):</span><br><span class="line">        super().__init__()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.attn_impl = attn_impl</span><br><span class="line">        self.clip_qkv = clip_qkv</span><br><span class="line">        self.qk_ln = qk_ln</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.d_model = d_model</span><br><span class="line">        self.n_heads = n_heads</span><br><span class="line">        self.head_dim = d_model // n_heads</span><br><span class="line">        self.softmax_scale = softmax_scale</span><br><span class="line">        if self.softmax_scale is None:</span><br><span class="line">            self.softmax_scale = 1 / math.sqrt(self.head_dim)</span><br><span class="line">        self.attn_dropout_p = attn_pdrop</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.Wqkv = nn.Linear(</span><br><span class="line">            d_model,</span><br><span class="line">            d_model + 2 * self.head_dim,</span><br><span class="line">            device=device,</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        fuse_splits = (d_model, d_model + self.head_dim)</span><br><span class="line">        self.Wqkv._fused = (0, fuse_splits)  # type: ignore</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.attn_fn = scaled_multihead_dot_product_attention</span><br><span class="line">        self.out_proj = nn.Linear(self.d_model, self.d_model, device=device)</span><br><span class="line">        self.out_proj._is_residual = True  # type: ignore</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(</span><br><span class="line">        self,</span><br><span class="line">        x,</span><br><span class="line">        past_key_value=None,</span><br><span class="line">        attn_bias=None,</span><br><span class="line">        attention_mask=None,</span><br><span class="line">        is_causal=True,</span><br><span class="line">        needs_weights=False,</span><br><span class="line">    ):</span><br><span class="line">        qkv = self.Wqkv(x)                                      # (1, 512, 960)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if self.clip_qkv:</span><br><span class="line">            qkv.clamp_(min=-self.clip_qkv, max=self.clip_qkv)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        query, key, value = qkv.split(                                  # query -&gt; (1, 512, 768)</span><br><span class="line">            [self.d_model, self.head_dim, self.head_dim],               # key   -&gt; (1, 512, 96)</span><br><span class="line">            dim=2                                                       # value -&gt; (1, 512, 96)</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        key_padding_mask = attention_mask</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if self.qk_ln:</span><br><span class="line">            # Applying layernorm to qk</span><br><span class="line">            dtype = query.dtype</span><br><span class="line">            query = self.q_ln(query).to(dtype)</span><br><span class="line">            key = self.k_ln(key).to(dtype)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        context, attn_weights, past_key_value = self.attn_fn(</span><br><span class="line">            query,</span><br><span class="line">            key,</span><br><span class="line">            value,</span><br><span class="line">            self.n_heads,</span><br><span class="line">            past_key_value=past_key_value,</span><br><span class="line">            softmax_scale=self.softmax_scale,</span><br><span class="line">            attn_bias=attn_bias,</span><br><span class="line">            key_padding_mask=key_padding_mask,</span><br><span class="line">            is_causal=is_causal,</span><br><span class="line">            dropout_p=self.attn_dropout_p,</span><br><span class="line">            training=self.training,</span><br><span class="line">            needs_weights=needs_weights,</span><br><span class="line">            multiquery=True,</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return self.out_proj(context), attn_weights, past_key_value</span><br></pre></td></tr></table></figure><h2 id="基础机器学习算法篇"><a href="#基础机器学习算法篇" class="headerlink" title="基础机器学习算法篇"></a><strong>基础机器学习算法篇</strong></h2><h3 id="手撕-numpy写线性回归的随机梯度下降（stochastic-gradient-descent，SGD）"><a href="#手撕-numpy写线性回归的随机梯度下降（stochastic-gradient-descent，SGD）" class="headerlink" title="手撕 numpy写线性回归的随机梯度下降（stochastic gradient descent，SGD）"></a><strong>手撕 numpy写线性回归的随机梯度下降（stochastic gradient descent，SGD）</strong></h3><p>在每次更新时用1个样本，可以看到多了随机两个字，随机也就是说我们用样本中的一个例子来近似我所有的样本，来调整θ，因而随机梯度下降是会带来一定的问题，因为计算得到的并不是准确的一个梯度，对于最优化问题，凸问题，虽然不是每次迭代得到的损失函数都向着全局最优方向， 但是大的整体的方向是向全局最优解的，最终的结果往往是在全局最优解附近。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"># 数据加载</span><br><span class="line">from sklearn.datasets import fetch_california_housing</span><br><span class="line">from sklearn.model_selection import train_test_split</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">X, Y = fetch_california_housing(return_X_y=True)</span><br><span class="line">X.shape, Y.shape  # (20640, 8), (20640, )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 数据预处理</span><br><span class="line">ones = np.ones(shape=(X.shape[0], 1))</span><br><span class="line">X = np.hstack([X, ones])</span><br><span class="line">validate_size = 0.2</span><br><span class="line">X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=validate_size, shuffle=True)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># batch 函数</span><br><span class="line">def get_batch(batchsize: int, X: np.ndarray, Y: np.ndarray):</span><br><span class="line">    assert 0 == X.shape[0]%batchsize, f&#x27;&#123;X.shape[0]&#125;%&#123;batchsize&#125; != 0&#x27;</span><br><span class="line">    batchnum = X.shape[0]//batchsize</span><br><span class="line">    X_new = X.reshape((batchnum, batchsize, X.shape[1]))</span><br><span class="line">    Y_new = Y.reshape((batchnum, batchsize, ))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    for i in range(batchnum):</span><br><span class="line">        yield X_new[i, :, :], Y_new[i, :]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 损失函数</span><br><span class="line">def mse(X: np.ndarray, Y: np.ndarray, W: np.ndarray):</span><br><span class="line">    return 0.5 * np.mean(np.square(X@W-Y))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def diff_mse(X: np.ndarray, Y: np.ndarray, W: np.ndarray):</span><br><span class="line">    return X.T@(X@W-Y) / X.shape[0]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 模型训练</span><br><span class="line">lr = 0.001          # 学习率</span><br><span class="line">num_epochs = 1000   # 训练周期</span><br><span class="line">batch_size = 64     # |每个batch包含的样本数</span><br><span class="line">validate_every = 4  # 多少个周期进行一次检验</span><br><span class="line">def train(num_epochs: int, batch_size: int, validate_every: int, W0: np.ndarray, X_train: np.ndarray, Y_train: np.ndarray, X_test: np.ndarray, Y_test: np.ndarray):</span><br><span class="line">    loop = tqdm(range(num_epochs))</span><br><span class="line">    loss_train = []</span><br><span class="line">    loss_validate = []</span><br><span class="line">    W = W0</span><br><span class="line">    # 遍历epoch</span><br><span class="line">    for epoch in loop:</span><br><span class="line">        loss_train_epoch = 0</span><br><span class="line">        # 遍历batch</span><br><span class="line">        for x_batch, y_batch in get_batch(64, X_train, Y_train):</span><br><span class="line">            loss_batch = mse(X=x_batch, Y=y_batch, W=W)</span><br><span class="line">            loss_train_epoch += loss_batch*x_batch.shape[0]/X_train.shape[0]</span><br><span class="line">            grad = diff_mse(X=x_batch, Y=y_batch, W=W)</span><br><span class="line">            W = W - lr*grad</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        loss_train.append(loss_train_epoch)</span><br><span class="line">        loop.set_description(f&#x27;Epoch: &#123;epoch&#125;, loss: &#123;loss_train_epoch&#125;&#x27;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        if 0 == epoch%validate_every:</span><br><span class="line">            loss_validate_epoch = mse(X=X_test, Y=Y_test, W=W)</span><br><span class="line">            loss_validate.append(loss_validate_epoch)</span><br><span class="line">            print(&#x27;============Validate=============&#x27;)</span><br><span class="line">            print(f&#x27;Epoch: &#123;epoch&#125;, train loss: &#123;loss_train_epoch&#125;, val loss: &#123;loss_validate_epoch&#125;&#x27;)</span><br><span class="line">            print(&#x27;================================&#x27;)</span><br><span class="line">    plot_loss(np.array(loss_train), np.array(loss_validate), validate_every)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 程序运行</span><br><span class="line">W0 = np.random.random(size=(X.shape[1], ))  # 初始权重</span><br><span class="line">train(num_epochs=num_epochs, batch_size=batch_size, validate_every=validate_every, W0=W0, X_train=X_train, Y_train=Y_train, X_test=X_test, Y_test=Y_test)</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444701-894badfa-d3a2-4f1b-ac27-42ec384fcb6e.jpeg#clientId=u8884b877-175c-4&from=paste&id=ub19aa012&originHeight=423&originWidth=670&originalType=url&ratio=1&rotation=0&showTitle=false&size=45178&status=done&style=none&taskId=u4c4ffcc6-6980-4366-84f1-ac4104983cd&title=" alt="image.jpg"></p><h3 id="手撕-k-means-算法"><a href="#手撕-k-means-算法" class="headerlink" title="手撕 k-means 算法"></a><strong>手撕 k-means 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">import numpy as np</span><br><span class="line">def kmeans(data, k, thresh=1, max_iterations=100):</span><br><span class="line">  # 随机初始化k个中心点</span><br><span class="line">  centers = data[np.random.choice(data.shape[0], k, replace=False)]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  for _ in range(max_iterations):</span><br><span class="line">    # 计算每个样本到各个中心点的距离</span><br><span class="line">    distances = np.linalg.norm(data[:, None] - centers, axis=2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 根据距离最近的中心点将样本分配到对应的簇</span><br><span class="line">    labels = np.argmin(distances, axis=1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 更新中心点为每个簇的平均值</span><br><span class="line">    new_centers = np.array([data[labels == i].mean(axis=0) for i in range(k)])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 判断中心点是否收敛，多种收敛条件可选</span><br><span class="line">    # 条件1：中心点不再改变</span><br><span class="line">    if np.all(centers == new_centers):</span><br><span class="line">      break</span><br><span class="line">    # 条件2：中心点的阈值小于某个阈值</span><br><span class="line">    # center_change = np.linalg.norm(new_centers - centers)</span><br><span class="line">    # if center_change &lt; thresh:</span><br><span class="line">    #     break</span><br><span class="line">    centers = new_centers</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  return labels, centers</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 生成一些随机数据作为示例输入</span><br><span class="line">data = np.random.rand(100, 2)  # 100个样本，每个样本有两个特征</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 手动实现K均值算法</span><br><span class="line">k = 3  # 聚类数为3</span><br><span class="line">labels, centers = kmeans(data, k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 打印簇标签和聚类中心点</span><br><span class="line">print(&quot;簇标签:&quot;, labels)</span><br><span class="line">print(&quot;聚类中心点:&quot;, centers)</span><br></pre></td></tr></table></figure><h2 id="手撕-Layer-Normalization-算法"><a href="#手撕-Layer-Normalization-算法" class="headerlink" title="手撕 Layer Normalization 算法"></a><strong>手撕 Layer Normalization 算法</strong></h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">from torch import nn</span><br><span class="line"> </span><br><span class="line">class LN(nn.Module):</span><br><span class="line">    # 初始化</span><br><span class="line">    def __init__(self, normalized_shape,  # 在哪个维度上做LN</span><br><span class="line">                 eps:float = 1e-5, # 防止分母为0</span><br><span class="line">                 elementwise_affine:bool = True):  # 是否使用可学习的缩放因子和偏移因子</span><br><span class="line">        super(LN, self).__init__()</span><br><span class="line">        # 需要对哪个维度的特征做LN, torch.size查看维度</span><br><span class="line">        self.normalized_shape = normalized_shape  # [c,w*h]</span><br><span class="line">        self.eps = eps</span><br><span class="line">        self.elementwise_affine = elementwise_affine</span><br><span class="line">        # 构造可训练的缩放因子和偏置</span><br><span class="line">        if self.elementwise_affine:  </span><br><span class="line">            self.gain = nn.Parameter(torch.ones(normalized_shape))  # [c,w*h]</span><br><span class="line">            self.bias = nn.Parameter(torch.zeros(normalized_shape))  # [c,w*h]</span><br><span class="line"> </span><br><span class="line">    # 前向传播</span><br><span class="line">    def forward(self, x: torch.Tensor): # [b,c,w*h]</span><br><span class="line">        # 需要做LN的维度和输入特征图对应维度的shape相同</span><br><span class="line">        assert self.normalized_shape == x.shape[-len(self.normalized_shape):]  # [-2:]</span><br><span class="line">        # 需要做LN的维度索引</span><br><span class="line">        dims = [-(i+1) for i in range(len(self.normalized_shape))]  # [b,c,w*h]维度上取[-1,-2]维度，即[c,w*h]</span><br><span class="line">        # 计算特征图对应维度的均值和方差</span><br><span class="line">        mean = x.mean(dim=dims, keepdims=True)  # [b,1,1]</span><br><span class="line">        mean_x2 = (x**2).mean(dim=dims, keepdims=True)  # [b,1,1]</span><br><span class="line">        var = mean_x2 - mean**2  # [b,c,1,1]</span><br><span class="line">        x_norm = (x-mean) / torch.sqrt(var+self.eps)  # [b,c,w*h]</span><br><span class="line">        # 线性变换</span><br><span class="line">        if self.elementwise_affine:</span><br><span class="line">            x_norm = self.gain * x_norm + self.bias  # [b,c,w*h]</span><br><span class="line">        return x_norm</span><br><span class="line"> </span><br><span class="line"># ------------------------------- #</span><br><span class="line"># 验证</span><br><span class="line"># ------------------------------- #</span><br><span class="line"> </span><br><span class="line">if __name__ == &#x27;__main__&#x27;:</span><br><span class="line"> </span><br><span class="line">    x = torch.linspace(0, 23, 24, dtype=torch.float32)  # 构造输入层</span><br><span class="line">    x = x.reshape([2,3,2*2])  # [b,c,w*h]</span><br><span class="line">    # 实例化</span><br><span class="line">    ln = LN(x.shape[1:])</span><br><span class="line">    # 前向传播</span><br><span class="line">    x = ln(x)</span><br><span class="line">    print(x.shape)</span><br></pre></td></tr></table></figure><h3 id="手撕-Batch-Normalization-算法"><a href="#手撕-Batch-Normalization-算法" class="headerlink" title="手撕 Batch Normalization 算法"></a><strong>手撕 Batch Normalization 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">class MyBN:</span><br><span class="line">    def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        初始化参数值</span><br><span class="line">        :param momentum: 动量，用于计算每个batch均值和方差的滑动均值</span><br><span class="line">        :param eps: 防止分母为0</span><br><span class="line">        :param feat_dim: 特征维度</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        # 均值和方差的滑动均值</span><br><span class="line">        self._running_mean = np.zeros(shape=(feat_dim, ))</span><br><span class="line">        self._running_var = np.ones((shape=(feat_dim, ))</span><br><span class="line">        # 更新self._running_xxx时的动量</span><br><span class="line">        self._momentum = momentum</span><br><span class="line">        # 防止分母计算为0</span><br><span class="line">        self._eps = eps</span><br><span class="line">        # 对应Batch Norm中需要更新的beta和gamma，采用pytorch文档中的初始化值</span><br><span class="line">        self._beta = np.zeros(shape=(feat_dim, ))</span><br><span class="line">        self._gamma = np.ones(shape=(feat_dim, ))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def batch_norm(self, x):</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        BN向传播</span><br><span class="line">        :param x: 数据</span><br><span class="line">        :return: BN输出</span><br><span class="line">        &quot;&quot;&quot;</span><br><span class="line">        if self.training:</span><br><span class="line">            x_mean = x.mean(axis=0)</span><br><span class="line">            x_var = x.var(axis=0)</span><br><span class="line">            # 对应running_mean的更新公式</span><br><span class="line">            self._running_mean = (1-self._momentum)*x_mean + self._momentum*self._running_mean</span><br><span class="line">            self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var</span><br><span class="line">            # 对应论文中计算BN的公式</span><br><span class="line">            x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)</span><br><span class="line">        else:</span><br><span class="line">            x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)</span><br><span class="line">        return self._gamma*x_hat + self._beta</span><br></pre></td></tr></table></figure><h2 id="解码算法篇"><a href="#解码算法篇" class="headerlink" title="解码算法篇"></a><strong>解码算法篇</strong></h2><h3 id="手撕-贪心搜索-（greedy-search）"><a href="#手撕-贪心搜索-（greedy-search）" class="headerlink" title="手撕 贪心搜索 （greedy search）"></a><strong>手撕 贪心搜索 （greedy search）</strong></h3><p>贪心搜索（greedy search）在每个时间步 t 都选取当前概率分布中概率最大的词，即<br><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/1574965/1714410444607-da855304-da72-4fa6-8f9b-fd9cf3440b94.jpeg#clientId=u8884b877-175c-4&from=paste&id=ud89f8e75&originHeight=59&originWidth=273&originalType=url&ratio=1&rotation=0&showTitle=false&size=2641&status=done&style=none&taskId=ub3f3a342-b73d-485d-8964-e14e29f56aa&title=" alt="image.jpg"><br>直到 yt 为或达到预设最大长度时停止生成。<br>贪心搜索本质上是局部最优策略，但并不能保证最终结果一定是全局最优的。由于贪心搜索在解码的任意时刻只保留一条候选序列，所以在搜索效率上，贪心搜索的复杂度显著低于穷举搜索。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">def greedy_decoding(input_ids, max_tokens=300):</span><br><span class="line"> with torch.inference_mode():</span><br><span class="line"> for _ in range(max_tokens):</span><br><span class="line">            outputs = model(input_ids)</span><br><span class="line">            next_token_logits = outputs.logits[:, -1, :]</span><br><span class="line">            next_token = torch.argmax(next_token_logits, dim=-1)</span><br><span class="line"> if next_token == tokenizer.eos_token_id:</span><br><span class="line"> break</span><br><span class="line">            input_ids = torch.cat([input_ids, rearrange(next_token, &#x27;c -&gt; 1 c&#x27;)], dim=-1)</span><br><span class="line">        generated_text = tokenizer.decode(input_ids[0])</span><br><span class="line"> return generated_text</span><br></pre></td></tr></table></figure><h3 id="手撕-集束搜索-beamsearch-算法"><a href="#手撕-集束搜索-beamsearch-算法" class="headerlink" title="手撕 集束搜索 beamsearch 算法"></a><strong>手撕 集束搜索 beamsearch 算法</strong></h3><p>在NLP翻译或对话任务中，在句子解码阶段，经常用到一种搜索算法beam search。这个算法有时候在大厂面试中，甚至可能会被要求手写实现。这里就从beam search的原理出发，最后手写实现一个beam search。</p><ul><li>思路：beam search在贪心搜索上进一步扩大了搜索范围，贪心搜索每下一步只考虑当前最优的top-1结果，beam search考虑最优的top-k个结果。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torch.nn.functional as F</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def beam_search(LM_prob,beam_size = 3):</span><br><span class="line">    batch,seqlen,vocab_size = LM_prob.shape</span><br><span class="line">    #对LM_prob取对数</span><br><span class="line">    log_LM_prob = LM_prob.log()</span><br><span class="line">    #先选择第0个位置的最大beam_size个token，log_emb_prob与indices的shape为(batch,beam)</span><br><span class="line">    log_beam_prob, indices = log_LM_prob[:,0,:].topk(beam_size,sorted = True)</span><br><span class="line">    indices = indices.unsqueeze(-1)</span><br><span class="line">    #对每个长度进行beam search</span><br><span class="line">    for i in range(1,seqlen):</span><br><span class="line">        #log_beam_prob (batch,beam,vocab_size),每个beam的可能产生的概率</span><br><span class="line">        log_beam_prob = log_beam_prob.unsqueeze(-1) + log_LM_prob[:,i,:].unsqueeze(1).repeat(1,beam_size,1)</span><br><span class="line">        #选择当前步概率最高的token</span><br><span class="line">        log_beam_prob, index = log_beam_prob.view(batch,-1).topk(beam_size,sorted = True)</span><br><span class="line">        #下面的计算：beam_id选出新beam来源于之前的哪个beam;index代表真实的token id</span><br><span class="line">        #beam_id,index (batch,beam)</span><br><span class="line">        beam_id = index//vocab_size</span><br><span class="line">        index = index%vocab_size</span><br><span class="line">        mid = torch.Tensor([])</span><br><span class="line">        #对batch内每个样本循环，选出beam的同时拼接上新生成的token id</span><br><span class="line">        for j,bid,idx in zip(range(batch),beam_id,index):</span><br><span class="line">            x = torch.cat([indices[j][bid],idx.unsqueeze(-1)],-1)</span><br><span class="line">            mid = torch.cat([mid,x.unsqueeze(0)],0)</span><br><span class="line">        indices = mid</span><br><span class="line">    return indices,log_beam_prob</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__==&#x27;__main__&#x27;:</span><br><span class="line">    # 建立一个语言模型 LM_prob (batch,seqlen,vocab_size)</span><br><span class="line">    LM_prob = F.softmax(torch.randn([32,20,1000]),dim = -1)</span><br><span class="line">    #最终返回每个候选，以及每个候选的log_prob，shape为(batch,beam_size,seqlen)</span><br><span class="line">    indices,log_prob = beam_search(LM_prob,beam_size = 3)</span><br><span class="line">    print(indices)</span><br></pre></td></tr></table></figure><h3 id="手撕-温度参数采样（Temperature-Sampling）算法"><a href="#手撕-温度参数采样（Temperature-Sampling）算法" class="headerlink" title="手撕 温度参数采样（Temperature Sampling）算法"></a><strong>手撕 温度参数采样（Temperature Sampling）算法</strong></h3><p>温度参数采样（Temperature Sampling）常用于基于概率的生成模型，如语言模型。它通过引入一个称为“温度”（Temperature）的参数来调整模型输出的概率分布，从而控制生成文本的多样性。<br>在温度参数采样中，模型在每个时间步生成词语时，会计算出词语的条件概率分布。然后模型将这个条件概率分布中的每个词语的概率值除以温度参数，对结果进行归一化处理，获得新的归一化概率分布。较高的温度值会使概率分布更平滑，从而增加生成文本的多样性。低概率的词语也有较高的可能性被选择；而较低的温度值则会使概率分布更集中，更倾向于选择高概率的词语，因此生成的文本更加确定性。最后模型根据这个新的归一化概率分布进行随机采样，选择生成的词语。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torch.nn.functional as F</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def temperature_sampling(logits, temperature=1.0):</span><br><span class="line">    logits = logits / temperature</span><br><span class="line">    probabilities = F.softmax(logits, dim=-1)</span><br><span class="line">    sampled_token = torch.multinomial(probabilities, 1)</span><br><span class="line"> return sampled_token.item()</span><br></pre></td></tr></table></figure><h3 id="手撕-Top-K-Sampling算法"><a href="#手撕-Top-K-Sampling算法" class="headerlink" title="手撕 Top-K Sampling算法"></a><strong>手撕 Top-K Sampling算法</strong></h3><p>Top-K 采样（在每个时间步选择条件概率排名前 K 的词语，然后在这 K 个词语中进行随机采样。这种方法既能保持一定的生成质量，又能增加文本的多样性，并且可以通过限制候选词语的数量来控制生成文本的多样性。<br>这个过程使得生成的文本在保持一定的生成质量的同时，也具有一定的多样性，因为在候选词语中仍然存在一定的竞争性。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">def top_k_sampling(input_ids, max_tokens=100, top_k=50, temperature=1.0):</span><br><span class="line"> for _ in range(max_tokens):</span><br><span class="line"> with torch.inference_mode():</span><br><span class="line">            outputs = model(input_ids)</span><br><span class="line">            next_token_logits = outputs.logits[:, -1, :]</span><br><span class="line">            top_k_logits, top_k_indices = torch.topk(next_token_logits, top_k)</span><br><span class="line">            top_k_probs = F.softmax(top_k_logits / temperature, dim=-1)</span><br><span class="line">            next_token_index = torch.multinomial(top_k_probs, num_samples=1)</span><br><span class="line">            next_token = top_k_indices.gather(-1, next_token_index)</span><br><span class="line">            input_ids = torch.cat([input_ids, next_token], dim=-1)</span><br><span class="line">    generated_text = tokenizer.decode(input_ids[0])</span><br><span class="line"> return generated_text</span><br></pre></td></tr></table></figure><h3 id="手撕-Top-P-Nucleus-Sampling-算法"><a href="#手撕-Top-P-Nucleus-Sampling-算法" class="headerlink" title="手撕 Top-P (Nucleus) Sampling 算法"></a><strong>手撕 Top-P (Nucleus) Sampling 算法</strong></h3><p>Nucleus Sampling（核采样），也被称为Top-p Sampling旨在在保持生成文本质量的同时增加多样性。这种方法可以视作是Top-K Sampling的一种变体，它在每个时间步根据模型输出的概率分布选择概率累积超过给定阈值p的词语集合，然后在这个词语集合中进行随机采样。这种方法会动态调整候选词语的数量，以保持一定的文本多样性。<br>在Nucleus Sampling中，模型在每个时间步生成词语时，首先按照概率从高到低对词汇表中的所有词语进行排序，然后模型计算累积概率，并找到累积概率超过给定阈值p的最小词语子集，这个子集就是所谓的“核”（nucleus）。模型在这个核中进行随机采样，根据词语的概率分布来选择最终输出的词语。这样做可以保证所选词语的总概率超过了阈值p，同时也保持了一定的多样性。<br>参数p是Nucleus Sampling中的重要参数，它决定了所选词语的概率总和。p的值会被设置在(0,1]之间，表示词语总概率的一个下界。<br>Nucleus Sampling 能够保持一定的生成质量，因为它在一定程度上考虑了概率分布。通过选择概率总和超过给定阈值p的词语子集进行随机采样，Nucleus Sampling 能够增加生成文本的多样性。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">def top_p_sampling(input_ids, max_tokens=100, top_p=0.95):</span><br><span class="line"> with torch.inference_mode():</span><br><span class="line"> for _ in range(max_tokens):</span><br><span class="line">                outputs = model(input_ids)</span><br><span class="line">                next_token_logits = outputs.logits[:, -1, :]</span><br><span class="line">                sorted_logits, sorted_indices = torch.sort(next_token_logits, descending=True)</span><br><span class="line">                sorted_probabilities = F.softmax(sorted_logits, dim=-1) </span><br><span class="line">                cumulative_probs = torch.cumsum(sorted_probabilities, dim=-1)</span><br><span class="line">                sorted_indices_to_remove = cumulative_probs &gt; top_p</span><br><span class="line">                sorted_indices_to_remove[..., 0] = False </span><br><span class="line">                indices_to_remove = sorted_indices[sorted_indices_to_remove]</span><br><span class="line">                next_token_logits.scatter_(-1, indices_to_remove[None, :], float(&#x27;-inf&#x27;))</span><br><span class="line">                probs = F.softmax(next_token_logits, dim=-1)</span><br><span class="line">                next_token = torch.multinomial(probs, num_samples=1)</span><br><span class="line">                input_ids = torch.cat([input_ids, next_token], dim=-1)</span><br><span class="line">        generated_text = tokenizer.decode(input_ids[0])</span><br><span class="line"> return generated_text</span><br></pre></td></tr></table></figure><h2 id="神经网络篇"><a href="#神经网络篇" class="headerlink" title="神经网络篇"></a><strong>神经网络篇</strong></h2><h3 id="手撕反向传播-backward-propagation，BP-法"><a href="#手撕反向传播-backward-propagation，BP-法" class="headerlink" title="手撕反向传播(backward propagation，BP)法"></a><strong>手撕反向传播(backward propagation，BP)法</strong></h3><p>BP算法就是反向传播，要输入的数据经过一个前向传播会得到一个输出，但是由于权重的原因，所以其输出会和你想要的输出有差距，这个时候就需要进行反向传播，利用梯度下降，对所有的权重进行更新，这样的话在进行前向传播就会发现其输出和你想要的输出越来越接近了。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"># 生成权重以及偏执项layers_dim代表每层的神经元个数，</span><br><span class="line">#比如[2,3,1]代表一个三成的网络，输入为2层，中间为3层输出为1层</span><br><span class="line">def init_parameters(layers_dim):</span><br><span class="line">    </span><br><span class="line">    L = len(layers_dim)</span><br><span class="line">    parameters =&#123;&#125;</span><br><span class="line">    for i in range(1,L):</span><br><span class="line">        parameters[&quot;w&quot;+str(i)] = np.random.random([layers_dim[i],layers_dim[i-1]])</span><br><span class="line">        parameters[&quot;b&quot;+str(i)] = np.zeros((layers_dim[i],1))</span><br><span class="line">    return parameters</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def sigmoid(z):</span><br><span class="line">    return 1.0/(1.0+np.exp(-z))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># sigmoid的导函数</span><br><span class="line">def sigmoid_prime(z):</span><br><span class="line">        return sigmoid(z) * (1-sigmoid(z))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 前向传播，需要用到一个输入x以及所有的权重以及偏执项，都在parameters这个字典里面存储</span><br><span class="line"># 最后返回会返回一个caches里面包含的 是各层的a和z，a[layers]就是最终的输出</span><br><span class="line">def forward(x,parameters):</span><br><span class="line">    a = []</span><br><span class="line">    z = []</span><br><span class="line">    caches = &#123;&#125;</span><br><span class="line">    a.append(x)</span><br><span class="line">    z.append(x)</span><br><span class="line">    layers = len(parameters)//2</span><br><span class="line">    # 前面都要用sigmoid</span><br><span class="line">    for i in range(1,layers):</span><br><span class="line">        z_temp =parameters[&quot;w&quot;+str(i)].dot(x) + parameters[&quot;b&quot;+str(i)]</span><br><span class="line">        z.append(z_temp)</span><br><span class="line">        a.append(sigmoid(z_temp))</span><br><span class="line">    # 最后一层不用sigmoid</span><br><span class="line">    z_temp = parameters[&quot;w&quot;+str(layers)].dot(a[layers-1]) + parameters[&quot;b&quot;+str(layers)]</span><br><span class="line">    z.append(z_temp)</span><br><span class="line">    a.append(z_temp)</span><br><span class="line">    </span><br><span class="line">    caches[&quot;z&quot;] = z</span><br><span class="line">    caches[&quot;a&quot;] = a    </span><br><span class="line">    return  caches,a[layers]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 反向传播，parameters里面存储的是所有的各层的权重以及偏执，caches里面存储各层的a和z</span><br><span class="line"># al是经过反向传播后最后一层的输出，y代表真实值</span><br><span class="line"># 返回的grades代表着误差对所有的w以及b的导数</span><br><span class="line">def backward(parameters,caches,al,y):</span><br><span class="line">    layers = len(parameters)//2</span><br><span class="line">    grades = &#123;&#125;</span><br><span class="line">    m = y.shape[1]</span><br><span class="line">    # 假设最后一层不经历激活函数</span><br><span class="line">    # 就是按照上面的图片中的公式写的</span><br><span class="line">    grades[&quot;dz&quot;+str(layers)] = al - y</span><br><span class="line">    grades[&quot;dw&quot;+str(layers)] = grades[&quot;dz&quot;+str(layers)].dot(caches[&quot;a&quot;][layers-1].T) /m</span><br><span class="line">    grades[&quot;db&quot;+str(layers)] = np.sum(grades[&quot;dz&quot;+str(layers)],axis = 1,keepdims = True) /m</span><br><span class="line">    # 前面全部都是sigmoid激活</span><br><span class="line">    for i in reversed(range(1,layers)):</span><br><span class="line">        grades[&quot;dz&quot;+str(i)] = parameters[&quot;w&quot;+str(i+1)].T.dot(grades[&quot;dz&quot;+str(i+1)]) * sigmoid_prime(caches[&quot;z&quot;][i])</span><br><span class="line">        grades[&quot;dw&quot;+str(i)] = grades[&quot;dz&quot;+str(i)].dot(caches[&quot;a&quot;][i-1].T)/m</span><br><span class="line">        grades[&quot;db&quot;+str(i)] = np.sum(grades[&quot;dz&quot;+str(i)],axis = 1,keepdims = True) /m</span><br><span class="line">    return grades   </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 就是把其所有的权重以及偏执都更新一下</span><br><span class="line">def update_grades(parameters,grades,learning_rate):</span><br><span class="line">    layers = len(parameters)//2</span><br><span class="line">    for i in range(1,layers+1):</span><br><span class="line">        parameters[&quot;w&quot;+str(i)] -= learning_rate * grades[&quot;dw&quot;+str(i)]</span><br><span class="line">        parameters[&quot;b&quot;+str(i)] -= learning_rate * grades[&quot;db&quot;+str(i)]</span><br><span class="line">    return parameters</span><br><span class="line"># 计算误差值</span><br><span class="line">def compute_loss(al,y):</span><br><span class="line">    return np.mean(np.square(al-y))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 加载数据</span><br><span class="line">def load_data():</span><br><span class="line">    &quot;&quot;&quot;</span><br><span class="line">    加载数据集</span><br><span class="line">    &quot;&quot;&quot;</span><br><span class="line">    x = np.arange(0.0,1.0,0.01)</span><br><span class="line">    y =20* np.sin(2*np.pi*x)</span><br><span class="line">    # 数据可视化</span><br><span class="line">    plt.scatter(x,y)</span><br><span class="line">    return x,y</span><br><span class="line">#进行测试</span><br><span class="line">x,y = load_data()</span><br><span class="line">x = x.reshape(1,100)</span><br><span class="line">y = y.reshape(1,100)</span><br><span class="line">plt.scatter(x,y)</span><br><span class="line">parameters = init_parameters([1,25,1])</span><br><span class="line">al = 0</span><br><span class="line">for i in range(4000):</span><br><span class="line">    caches,al = forward(x, parameters)</span><br><span class="line">    grades = backward(parameters, caches, al, y)</span><br><span class="line">    parameters = update_grades(parameters, grades, learning_rate= 0.3)</span><br><span class="line">    if i %100 ==0:</span><br><span class="line">        print(compute_loss(al, y))</span><br><span class="line">plt.scatter(x,al)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><h3 id="手撕-卷积神经网络-CNN-法"><a href="#手撕-卷积神经网络-CNN-法" class="headerlink" title="手撕 卷积神经网络(CNN)法"></a><strong>手撕 卷积神经网络(CNN)法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torch.nn.functional as F #使用functional中的ReLu激活函数</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#CNN模型</span><br><span class="line">class CNNNet(torch.nn.Module):</span><br><span class="line">    def __init__(self):</span><br><span class="line">        super(CNNNet, self).__init__()</span><br><span class="line">        #两个卷积层</span><br><span class="line">        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)  #1为in_channels 10为out_channels</span><br><span class="line">        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)</span><br><span class="line">        #池化层</span><br><span class="line">        self.pooling = torch.nn.MaxPool2d(2)  #2为分组大小2*2</span><br><span class="line">        #全连接层 320 = 20 * 4 * 4</span><br><span class="line">        self.fc = torch.nn.Linear(320, 10)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, x):</span><br><span class="line">        #先从x数据维度中得到batch_size</span><br><span class="line">        batch_size = x.size(0)</span><br><span class="line">        #卷积层-&gt;池化层-&gt;激活函数</span><br><span class="line">        x = F.relu(self.pooling(self.conv1(x)))</span><br><span class="line">        x = F.relu(self.pooling(self.conv2(x)))</span><br><span class="line">        x = x.view(batch_size, -1)  #将数据展开，为输入全连接层做准备</span><br><span class="line">        x = self.fc(x)</span><br><span class="line">        return x</span><br><span class="line">model = CNNNet()</span><br></pre></td></tr></table></figure><h3 id="手撕-循环神经网络-RNN-法"><a href="#手撕-循环神经网络-RNN-法" class="headerlink" title="手撕 循环神经网络(RNN)法"></a><strong>手撕 循环神经网络(RNN)法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"># encoding:utf-8</span><br><span class="line">import torch</span><br><span class="line">import numpy as np</span><br><span class="line">import matplotlib.pyplot as plt</span><br><span class="line">from torch import nn</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># 定义RNN模型(可以类别下方RNN简单测试代码理解)</span><br><span class="line">class Rnn(nn.Module):</span><br><span class="line">    def __init__(self, input_size):</span><br><span class="line">        super(Rnn, self).__init__()</span><br><span class="line">        # 定义RNN网络</span><br><span class="line">        ## hidden_size是自己设置的，貌似取值都是32,64,128这样来取值</span><br><span class="line">        ## num_layers是隐藏层数量，超过2层那就是深度循环神经网络了</span><br><span class="line">        self.rnn = nn.RNN(</span><br><span class="line">                input_size=input_size,</span><br><span class="line">                hidden_size=32,</span><br><span class="line">                num_layers=1,</span><br><span class="line">                batch_first=True  # 输入形状为[批量大小, 数据序列长度, 特征维度]</span><br><span class="line">                )</span><br><span class="line">        # 定义全连接层</span><br><span class="line">        self.out = nn.Linear(32, 1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 定义前向传播函数</span><br><span class="line">    def forward(self, x, h_0):</span><br><span class="line">        r_out, h_n = self.rnn(x, h_0)</span><br><span class="line">        # print(&quot;数据输出结果；隐藏层数据结果&quot;, r_out, h_n)</span><br><span class="line">        # print(&quot;r_out.size()， h_n.size()&quot;, r_out.size(), h_n.size())</span><br><span class="line">        outs = []</span><br><span class="line">        # r_out.size=[1,10,32]即将一个长度为10的序列的每个元素都映射到隐藏层上</span><br><span class="line">        for time in range(r_out.size(1)):  </span><br><span class="line">            # print(&quot;映射&quot;, r_out[:, time, :])</span><br><span class="line">            # 依次抽取序列中每个单词,将之通过全连接层并输出.r_out[:, 0, :].size()=[1,32] -&gt; [1,1]</span><br><span class="line">            outs.append(self.out(r_out[:, time, :])) </span><br><span class="line">            # print(&quot;outs&quot;, outs)</span><br><span class="line">        # stack函数在dim=1上叠加:10*[1,1] -&gt; [1,10,1] 同时h_n已经被更新</span><br><span class="line">        return torch.stack(outs, dim=1), h_n </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">TIME_STEP = 10</span><br><span class="line">INPUT_SIZE = 1</span><br><span class="line">LR = 0.02</span><br><span class="line">model = Rnn(INPUT_SIZE)</span><br><span class="line">print(model)</span><br></pre></td></tr></table></figure><h3 id="手撕-LSTM法"><a href="#手撕-LSTM法" class="headerlink" title="手撕 LSTM法"></a><strong>手撕 LSTM法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"># -*- coding:UTF-8 -*-</span><br><span class="line">import numpy as np</span><br><span class="line">import torch</span><br><span class="line">from torch import nn</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># Define LSTM Neural Networks</span><br><span class="line">class LstmRNN(nn.Module):</span><br><span class="line">    &quot;&quot;&quot;</span><br><span class="line">        Parameters：</span><br><span class="line">        - input_size: feature size</span><br><span class="line">        - hidden_size: number of hidden units</span><br><span class="line">        - output_size: number of output</span><br><span class="line">        - num_layers: layers of LSTM to stack</span><br><span class="line">    &quot;&quot;&quot;</span><br><span class="line">    def __init__(self, input_size, hidden_size=1, output_size=1, num_layers=1):</span><br><span class="line">        super().__init__()</span><br><span class="line"> </span><br><span class="line">        self.lstm = nn.LSTM(input_size, hidden_size, num_layers) # utilize the LSTM model in torch.nn </span><br><span class="line">        self.forwardCalculation = nn.Linear(hidden_size, output_size)</span><br><span class="line"> </span><br><span class="line">    def forward(self, _x):</span><br><span class="line">        x, _ = self.lstm(_x)  # _x is input, size (seq_len, batch, input_size)</span><br><span class="line">        s, b, h = x.shape  # x is output, size (seq_len, batch, hidden_size)</span><br><span class="line">        x = x.view(s*b, h)</span><br><span class="line">        x = self.forwardCalculation(x)</span><br><span class="line">        x = x.view(s, b, -1)</span><br><span class="line">        return x</span><br></pre></td></tr></table></figure><h3 id="手撕-二维卷积-算法"><a href="#手撕-二维卷积-算法" class="headerlink" title="手撕 二维卷积 算法"></a><strong>手撕 二维卷积 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">import numpy as np </span><br><span class="line">def conv2d(img, in_channels, out_channels ,kernels, bias, stride=1, padding=0):</span><br><span class="line">    N, C, H, W = img.shape </span><br><span class="line">    kh, kw = kernels.shape</span><br><span class="line">    p = padding</span><br><span class="line">    assert C == in_channels, &quot;kernels&#x27; input channels do not match with img&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if p:</span><br><span class="line">        img = np.pad(img, ((0,0),(0,0),(p,p),(p,p)), &#x27;constant&#x27;) # padding along with all axis</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    out_h = (H + 2*padding - kh) // stride + 1</span><br><span class="line">    out_w = (W + 2*padding - kw) // stride + 1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    outputs = np.zeros([N, out_channels, out_h, out_w])</span><br><span class="line">    # print(img)</span><br><span class="line">    for n in range(N):</span><br><span class="line">        for out in range(out_channels):</span><br><span class="line">            for i in range(in_channels):</span><br><span class="line">                for h in range(out_h):</span><br><span class="line">                    for w in range(out_w):</span><br><span class="line">                        for x in range(kh):</span><br><span class="line">                            for y in range(kw):</span><br><span class="line">                                outputs[n][out][h][w] += img[n][i][h * stride + x][w * stride + y] * kernels[x][y]</span><br><span class="line">                if i == in_channels - 1:</span><br><span class="line">                    outputs[n][out][:][:] += bias[n][out]</span><br><span class="line">    return outputs</span><br></pre></td></tr></table></figure><h2 id="位置编码篇"><a href="#位置编码篇" class="headerlink" title="位置编码篇"></a><strong>位置编码篇</strong></h2><h3 id="手撕-绝对位置编码-算法"><a href="#手撕-绝对位置编码-算法" class="headerlink" title="手撕 绝对位置编码 算法"></a><strong>手撕 绝对位置编码 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">class SinPositionEncoding(nn.Module):</span><br><span class="line">    def __init__(self, max_sequence_length, d_model, base=10000):</span><br><span class="line">        super().__init__()</span><br><span class="line">        self.max_sequence_length = max_sequence_length</span><br><span class="line">        self.d_model = d_model</span><br><span class="line">        self.base = base</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self):</span><br><span class="line">        pe = torch.zeros(self.max_sequence_length, self.d_model, dtype=torch.float)  # size(max_sequence_length, d_model)</span><br><span class="line">        exp_1 = torch.arange(self.d_model // 2, dtype=torch.float)  # 初始化一半维度，sin位置编码的维度被分为了两部分</span><br><span class="line">        exp_value = exp_1 / (self.d_model / 2)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        alpha = 1 / (self.base ** exp_value)  # size(dmodel/2)</span><br><span class="line">        out = torch.arange(self.max_sequence_length, dtype=torch.float)[:, None] @ alpha[None, :]  # size(max_sequence_length, d_model/2)</span><br><span class="line">        embedding_sin = torch.sin(out)</span><br><span class="line">        embedding_cos = torch.cos(out)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        pe[:, 0::2] = embedding_sin  # 奇数位置设置为sin</span><br><span class="line">        pe[:, 1::2] = embedding_cos  # 偶数位置设置为cos</span><br><span class="line">        return pe</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">SinPositionEncoding(d_model=4, max_sequence_length=10, base=10000).forward()</span><br></pre></td></tr></table></figure><h3 id="手撕-可学习位置编码-算法"><a href="#手撕-可学习位置编码-算法" class="headerlink" title="手撕 可学习位置编码 算法"></a><strong>手撕 可学习位置编码 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">class TrainablePositionEncoding(nn.Module):</span><br><span class="line">    def __init__(self, max_sequence_length, d_model):</span><br><span class="line">        super().__init__()</span><br><span class="line">        self.max_sequence_length = max_sequence_length</span><br><span class="line">        self.d_model = d_model</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self):</span><br><span class="line">        pe = nn.Embedding(self.max_sequence_length, self.d_model)</span><br><span class="line">        nn.init.constant(pe.weight, 0.)</span><br><span class="line">        return pe</span><br></pre></td></tr></table></figure><h3 id="手撕-相对位置编码-算法"><a href="#手撕-相对位置编码-算法" class="headerlink" title="手撕 相对位置编码 算法"></a><strong>手撕 相对位置编码 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line">class RelativePosition(nn.Module):</span><br><span class="line">    def __init__(self, num_units, max_relative_position):</span><br><span class="line">        super().__init__()</span><br><span class="line">        self.num_units = num_units</span><br><span class="line">        self.max_relative_position = max_relative_position</span><br><span class="line">        self.embeddings_table = nn.Parameter(torch.Tensor(max_relative_position * 2 + 1, num_units))</span><br><span class="line">        nn.init.xavier_uniform_(self.embeddings_table)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, length_q, length_k):</span><br><span class="line">        range_vec_q = torch.arange(length_q)</span><br><span class="line">        range_vec_k = torch.arange(length_k)</span><br><span class="line">        distance_mat = range_vec_k[None, :] - range_vec_q[:, None]</span><br><span class="line">        distance_mat_clipped = torch.clamp(distance_mat, -self.max_relative_position, self.max_relative_position)</span><br><span class="line">        final_mat = distance_mat_clipped + self.max_relative_position</span><br><span class="line">        final_mat = torch.LongTensor(final_mat).cuda()</span><br><span class="line">        embeddings = self.embeddings_table[final_mat].cuda()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return embeddings</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class RelativeMultiHeadAttention(nn.Module):</span><br><span class="line">    def __init__(self, d_model, n_heads, dropout=0.1, batch_size=6):</span><br><span class="line">        &quot;Take in model size and number of heads.&quot;</span><br><span class="line">        super(RelativeMultiHeadAttention, self).__init__()</span><br><span class="line">        self.d_model = d_model</span><br><span class="line">        self.n_heads = n_heads</span><br><span class="line">        self.batch_size = batch_size</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        assert d_model % n_heads == 0</span><br><span class="line">        self.head_dim = d_model // n_heads</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.linears = _get_clones(nn.Linear(d_model, d_model), 4)</span><br><span class="line">        self.dropout = nn.Dropout(p=dropout)</span><br><span class="line">        self.relative_position_k = RelativePosition(self.head_dim, max_relative_position=16)</span><br><span class="line">        self.relative_position_v = RelativePosition(self.head_dim, max_relative_position=16)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        self.scale = torch.sqrt(torch.FloatTensor([self.head_dim])).cuda()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def forward(self, query, key, value):</span><br><span class="line">        # embedding</span><br><span class="line">        # query, key, value = [batch_size, len, hid_dim]</span><br><span class="line">        query, key, value = [l(x).view(self.batch_size, -1, self.d_model) for l, x in</span><br><span class="line">                             zip(self.linears, (query, key, value))]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        len_k = query.shape[1]</span><br><span class="line">        len_q = query.shape[1]</span><br><span class="line">        len_v = value.shape[1]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        # Self-Attention</span><br><span class="line">        # r_q1, r_k1 = [batch_size, len, n_heads, head_dim]</span><br><span class="line">        r_q1 = query.view(self.batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)</span><br><span class="line">        r_k1 = key.view(self.batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)</span><br><span class="line">        attn1 = torch.matmul(r_q1, r_k1.permute(0, 1, 3, 2))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        r_q2 = query.permute(1, 0, 2).contiguous().view(len_q, self.batch_size * self.n_heads, self.head_dim)</span><br><span class="line">        r_k2 = self.relative_position_k(len_q, len_k)</span><br><span class="line">        attn2 = torch.matmul(r_q2, r_k2.transpose(1, 2)).transpose(0, 1)</span><br><span class="line">        attn2 = attn2.contiguous().view(self.batch_size, self.n_heads, len_q, len_k)</span><br><span class="line">        attn = (attn1 + attn2) / self.scale</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        attn = self.dropout(torch.softmax(attn, dim=-1))</span><br><span class="line">        # attn = [batch_size, n_heads, len, len]</span><br><span class="line">        r_v1 = value.view(self.batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)</span><br><span class="line">        weight1 = torch.matmul(attn, r_v1)</span><br><span class="line">        r_v2 = self.relative_position_v(len_q, len_v)</span><br><span class="line">        weight2 = attn.permute(2, 0, 1, 3).contiguous().view(len_q, self.batch_size * self.n_heads, len_k)</span><br><span class="line">        weight2 = torch.matmul(weight2, r_v2)</span><br><span class="line">        weight2 = weight2.transpose(0, 1).contiguous().view(self.batch_size, self.n_heads, len_q, self.head_dim)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        x = weight1 + weight2</span><br><span class="line">        # x = [batch size, n heads, query len, head dim]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        x = x.permute(0, 2, 1, 3).contiguous()</span><br><span class="line">        # x = [batch size, query len, n heads, head dim]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        x = x.view(self.batch_size * len_q, self.d_model)</span><br><span class="line">        # x = [batch size * query len, hid dim]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        return self.linears[-1](x)</span><br></pre></td></tr></table></figure><h3 id="手撕-rope-算法"><a href="#手撕-rope-算法" class="headerlink" title="手撕 rope 算法"></a><strong>手撕 rope 算法</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torch.nn as nn</span><br><span class="line">import torch.nn.functional as F</span><br><span class="line">import math</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># %%</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def sinusoidal_position_embedding(batch_size, nums_head, max_len, output_dim, device):</span><br><span class="line">    # (max_len, 1)</span><br><span class="line">    position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(-1)</span><br><span class="line">    # (output_dim//2)</span><br><span class="line">    ids = torch.arange(0, output_dim // 2, dtype=torch.float)  # 即公式里的i, i的范围是 [0,d/2]</span><br><span class="line">    theta = torch.pow(10000, -2 * ids / output_dim)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (max_len, output_dim//2)</span><br><span class="line">    embeddings = position * theta  # 即公式里的：pos / (10000^(2i/d))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (max_len, output_dim//2, 2)</span><br><span class="line">    embeddings = torch.stack([torch.sin(embeddings), torch.cos(embeddings)], dim=-1)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, max_len, output_dim//2, 2)</span><br><span class="line">    embeddings = embeddings.repeat((batch_size, nums_head, *([1] * len(embeddings.shape))))  # 在bs维度重复，其他维度都是1不重复</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, max_len, output_dim)</span><br><span class="line">    # reshape后就是：偶数sin, 奇数cos了</span><br><span class="line">    embeddings = torch.reshape(embeddings, (batch_size, nums_head, max_len, output_dim))</span><br><span class="line">    embeddings = embeddings.to(device)</span><br><span class="line">    return embeddings</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># %%</span><br><span class="line">def RoPE(q, k):</span><br><span class="line">    # q,k: (bs, head, max_len, output_dim)</span><br><span class="line">    batch_size = q.shape[0]</span><br><span class="line">    nums_head = q.shape[1]</span><br><span class="line">    max_len = q.shape[2]</span><br><span class="line">    output_dim = q.shape[-1]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, max_len, output_dim)</span><br><span class="line">    pos_emb = sinusoidal_position_embedding(batch_size, nums_head, max_len, output_dim, q.device)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # cos_pos,sin_pos: (bs, head, max_len, output_dim)</span><br><span class="line">    # 看rope公式可知，相邻cos，sin之间是相同的，所以复制一遍。如(1,2,3)变成(1,1,2,2,3,3)</span><br><span class="line">    cos_pos = pos_emb[...,  1::2].repeat_interleave(2, dim=-1)  # 将奇数列信息抽取出来也就是cos 拿出来并复制</span><br><span class="line">    sin_pos = pos_emb[..., ::2].repeat_interleave(2, dim=-1)  # 将偶数列信息抽取出来也就是sin 拿出来并复制</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # q,k: (bs, head, max_len, output_dim)</span><br><span class="line">    q2 = torch.stack([-q[..., 1::2], q[..., ::2]], dim=-1)</span><br><span class="line">    q2 = q2.reshape(q.shape)  # reshape后就是正负交替了</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # 更新qw, *对应位置相乘</span><br><span class="line">    q = q * cos_pos + q2 * sin_pos</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    k2 = torch.stack([-k[..., 1::2], k[..., ::2]], dim=-1)</span><br><span class="line">    k2 = k2.reshape(k.shape)</span><br><span class="line">    # 更新kw, *对应位置相乘</span><br><span class="line">    k = k * cos_pos + k2 * sin_pos</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    return q, k</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># %%</span><br><span class="line">def attention(q, k, v, mask=None, dropout=None, use_RoPE=True):</span><br><span class="line">    # q.shape: (bs, head, seq_len, dk)</span><br><span class="line">    # k.shape: (bs, head, seq_len, dk)</span><br><span class="line">    # v.shape: (bs, head, seq_len, dk)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if use_RoPE:</span><br><span class="line">        q, k = RoPE(q, k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    d_k = k.size()[-1]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    att_logits = torch.matmul(q, k.transpose(-2, -1))  # (bs, head, seq_len, seq_len)</span><br><span class="line">    att_logits /= math.sqrt(d_k)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if mask is not None:</span><br><span class="line">        att_logits = att_logits.masked_fill(mask == 0, -1e9)  # mask掉为0的部分，设为无穷大</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    att_scores = F.softmax(att_logits, dim=-1)  # (bs, head, seq_len, seq_len)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    if dropout is not None:</span><br><span class="line">        att_scores = dropout(att_scores)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, seq_len, seq_len) * (bs, head, seq_len, dk) = (bs, head, seq_len, dk)</span><br><span class="line">    return torch.matmul(att_scores, v), att_scores</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &#x27;__main__&#x27;:</span><br><span class="line">    # (bs, head, seq_len, dk)</span><br><span class="line">    q = torch.randn((8, 12, 10, 32))</span><br><span class="line">    k = torch.randn((8, 12, 10, 32))</span><br><span class="line">    v = torch.randn((8, 12, 10, 32))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    res, att_scores = attention(q, k, v, mask=None, dropout=None, use_RoPE=True)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    # (bs, head, seq_len, dk),  (bs, head, seq_len, seq_len)</span><br></pre></td></tr></table></figure><pre><code>print(res.shape, att_scores.shape)</code></pre><h2 id="面试题汇总"><a href="#面试题汇总" class="headerlink" title="面试题汇总"></a><strong>面试题汇总</strong></h2><ul><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483957&idx=1&sn=abec4b75b9865b754f8a303c340c13a3&scene=21#wechat_redirect">大模型微调的经验与感想分享</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483942&idx=1&sn=a5ba1da8459df0b76e1ea70bfa4dc068&scene=21#wechat_redirect">百度-NLP算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483919&idx=1&sn=c9a530ecce9e60af4fad4c06062ec9ce&scene=21#wechat_redirect">美团-大模型算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483896&idx=1&sn=6b79f7eb585cc1d91a1f61010941477c&scene=21#wechat_redirect">小米-NLP算法工程师面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483884&idx=1&sn=e1f4d13589606786f2d2467e11b4e2dc&scene=21#wechat_redirect">好未来-NLP算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483862&idx=1&sn=0dc0ee080532d397b2b00bdd20c86260&scene=21#wechat_redirect">百度大模型算法工程师面经</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483853&idx=2&sn=f717767538329ce17325de72aa58ba1b&scene=21#wechat_redirect">昆仑天工大模型算法工程师</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483839&idx=1&sn=b66447f92f4dbfa8be7922f53aa8ba4b&scene=21#wechat_redirect">阿里大模型算法工程师一面</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483790&idx=1&sn=308fb18b66cc66b78f7e15822cdd6eff&scene=21#wechat_redirect">算法工程师面试常考手撕题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483773&idx=1&sn=003c347fc05e1a3fa4328ac09dddb797&scene=21#wechat_redirect">搜狐大模型算法工程师</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483757&idx=1&sn=79394fd14e39948d1fc98aa09e031561&scene=21#wechat_redirect">字节大模型算法实习生</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483750&idx=1&sn=18d9c270e8d58a32dc4792fbc5f8f6e8&scene=21#wechat_redirect">理想汽车大模型算法实习生</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483745&idx=1&sn=ee37c895b25bf2a1f8387edf1d687e30&scene=21#wechat_redirect">百度大模型算法实习生面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483731&idx=1&sn=08cb4b390e80f3ca4a1e0fa2dd5a3020&scene=21#wechat_redirect">腾讯大模型算法实习生面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483723&idx=1&sn=baa9b82a7ac4f12e936ff8b58dcf8977&scene=21#wechat_redirect">阿里大模型算法工程师一面</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483713&idx=1&sn=c90af03630f92999eed214d5dc9f06a3&scene=21#wechat_redirect">某大厂大模型算法工程师面试题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483697&idx=1&sn=82e8cbb46aa2a0a656ae6f76ed225b03&scene=21#wechat_redirect">说说百度大模型算法工程师二面经历</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzkyNTY0Mjg0OQ==&mid=2247483686&idx=1&sn=79b3d0eb8a034cf7fe8746cd5e362899&scene=21#wechat_redirect">阿里大模型算法工程师面试小结</a></li></ul><h2 id="致谢"><a href="#致谢" class="headerlink" title="致谢"></a><strong>致谢</strong></h2><ul><li>LLMs 千面郎君 更新版 <a href="https://mp.weixin.qq.com/s/C6NdO_Ebj3DQx2AVAAgQRQ">https://mp.weixin.qq.com/s/C6NdO_Ebj3DQx2AVAAgQRQ</a></li><li>LLMs九层妖塔 <a href="https://mp.weixin.qq.com/s/Eh0tY1zx2FqXQqIGa2dIBA">https://mp.weixin.qq.com/s/Eh0tY1zx2FqXQqIGa2dIBA</a></li><li>NLP 面无不过 <a href="https://github.com/km1994/NLP-Interview-Notes">https://github.com/km1994/NLP-Interview-Notes</a></li></ul><blockquote><p>来自: <a href="https://mp.weixin.qq.com/s/TAFvUlqdyqP-W6C10F1Hzw">算法工程师面试常考手撕题（更新）</a></p></blockquote>]]>
    </content>
    <id>http://jackzhu.top/2024/04/29/%E6%B7%98%E5%A4%A9%E6%9A%91%E6%9C%9F%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/04/29/%E6%B7%98%E5%A4%A9%E6%9A%91%E6%9C%9F%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-04-29T20:43:37.000Z</published>
    <summary>淘天技术的面试记录分享。</summary>
    <title>淘天暑期实习计算机视觉面试凉经</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="面试记录" scheme="http://jackzhu.top/categories/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <category term="面试记录" scheme="http://jackzhu.top/tags/%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<p>这个稿子是我面试完回忆用语音转文字记录的，然后我稍微整理了一下，然后就是这个样子。</p><h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><p>面试首先面试让我自我介绍然后简单介绍一下学校以及参加的项目。简单介绍完，然后就开始问题项目的情况。我说做的项目有一些。主要是研究方向是医学影像，然后前面做的视觉项目，然后我还有一个论文，然后论文做的啥。然后我说我参加了一些竞赛，本科参加的数学建模和数学竞赛。然后问我在项目中遇到了什么问题，然后怎么解决的然后我举了个在实际项目中解决问题的方法。</p><p>然后我说我个人比较喜欢学习新技术，各方面都了解了一些。其他简单问了一点，具体忘了。然后面试就让写个题，这个题就是类似131切割回文字符串，用最小的切割次数是分割字符串，每一个部分都是回文字符串。然后我煎熬了半天写不出来，只能跟面试官说这是一个动态规划问题。然后问我刷了多少题，我说我最近两个月才开始刷，才是力扣刷了100道。然后我说我数据结构算法是大二学的，现在才刚学，不是特别清楚。</p><p>机器学习相对更清楚一点，然后就开始问我图像处理算法。然后问我的边缘检测用什么算子，然后我忘了是叫什么名字，然后简单画了一个图，但是这个9×9的这个表格中间填啥我给忘了。然后又问我在项目中是用到了机器学习算法吗？然后我说一般图像预处理用机器学习算法，实际做一般用深度学习算法。然后往往都是用一些腐蚀膨胀等这些操作滤波。然后。面试官问，对于边缘检测比较模糊和清晰的分别用什么算子，然后面试官最后说让等待通知下一次面试。</p><h2 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h2><p>然后没几十分钟就到主管面试，主管面试问的非常多而且杂，我简单记一下吧。首先。是让自我介绍一下，介绍完。然后说我知识面相对比较广，面试官问，我就是新知识从哪儿学习，首先关注的公众号，我说就是一些特别前沿的技术的公众号，然后具体问我都是哪些公众号，然后我列举了几个，然后其他还有什么渠道，然后我说在哔哩哔哩上关注了一些博主，然后让我列举，然后我列举了几个，然后问我具体哪个博主印象深刻，然后我举了个哔哩哔哩上的zomi酱，然后说他做ai系统，全栈从底层硬件生态到大语言模型训练以及加速等等有很多很多，感觉学到了很多。然后除此之外还有哪些方面，我说我和朋友一起，他们也比较关注前沿技术，所以平时也会进行一些分享交流。</p><p>然后问我的项目在做的过程中有没有遇到什么问题和困难然后如何解决的，然后我就某一个问题，然后进行了稍微详细的回答，然后我说问题主要体现在技术方面和与人打交道方面，然后我说技术方面如何如何解决的，然后他又问，在与人打交道的过程中如何如何解决的，当和甲方遇到争议的时候，不配合的时候如何如何解决的，这个就是比较套路的准备的问题，然后这块说了一些。</p><p>然后问项目是自己做还是和其他人一起？然后这几个项目都是自己负责的，然后问写作这块的问题，我说只有一个项目，是我带了一个研一的，主要在做，然后问如何分配工作，然后我具体说我是做算法设计，然后给他说具体应该如何如何做，让他自己去查具体的内容，然后他来处理数据。然后问有没有参与别人的项目，说主要这块比较大的项目不是很多。</p><p>然后问在团队协作中有没有什么特别坚持的地方？当与其他人出现分歧的时候，如何解决。</p><p>然后还有后面问到了对华为了解多少，对于公司的狼性文化了解多少，我说。我说年轻就应该奋斗，华为狼性文化是给奋斗者的，其他有些公司向华为学习，但是只只学习内卷，不给钱就不好。然后面试官讲了好多好多关于华为的企业文化的奋斗者什么什么之类的内容，如何为公司创造效益。</p><p>然后问如何看待压力，然后有没有遇到什么有压力的情况，然后我说之前换方向的时候感觉压力很大，然后面试官问面对压力如何解决的，然后我说平时可以锻炼，然后周末找朋友一起玩儿，然后前面遇到压力的困难的时候我也会积极向其他人学习，去找清楚压力的来源，然后解决他。</p><p>然后我有没有参加什么社团，然后我说我本科的时候加入了一个技术社团，然后在这个过程中和很多人进行技术交流学习，然后面试官问，我在这个过程中学到了什么，然后我说一分半是学到了技术，另外一方面是交到了很多好朋友。然后面试官我参加活动的频率，我说我开始闲的时候参加的多，后面我们也举办了一些活动，再后面大三忙起来了就不怎么参加了。</p><p>然后是问我如何看待aigc对人类以及对公司的影响，我说一方面提升生产力，使得低端产业更多的被替代，类似一场工业革命，然后面试官说对公司是什么样的，然后重点说了下对于公司的影响，这块我感觉我说的不太好，这个挺宏观的，对公司的没怎么考虑过。</p><p>最后是反问问我有没有什么问题向他问，我说在学校学的东西，和实际实习过程中，是不是有一些差距？主要体现在什么地方？然后我有什么需要准备的没有？然后他讲了一些讲了不少东西，然后说在学校做的东西很多时候相对偏长远更偏理论一些，但是企业相对来说更重效益，既要看重长远地也要看中短期短期利益，如果一段时间内都没有的话，可能这个项目就要被砍掉。然后我还问了问他们的平时具体业务是做什么，然后他就是说也开发这块，包括很多技术，这些前沿算法，把他们做成软件落地。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总之一面技术面问的时间相对比较短，也不是特别深，然后就半小时，后面半小时我都在写那道算法题，然后也没写出来。然后主管面面的时间还挺长的，然后问了好多。我现在暂时只能记得着这么多，其他应该还有一些问题。面试官说感觉还行，但是我不知道他是不是跟其他人也是这样说的，毕竟华为的池子还是挺大的，好多人都在泡池子。</p><p>6.7日oc</p><p>后续还是得好好刷题，做题这块还是差点意思。</p>]]>
    </content>
    <id>http://jackzhu.top/2024/04/27/%E5%8D%8E%E4%B8%BA%E6%9A%91%E6%9C%9F%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/</id>
    <link href="http://jackzhu.top/2024/04/27/%E5%8D%8E%E4%B8%BA%E6%9A%91%E6%9C%9F%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%88%86%E4%BA%AB/"/>
    <published>2024-04-27T23:28:37.000Z</published>
    <summary>华为一面二面的面试记录分享。</summary>
    <title>华为暑期实习面试分享</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="博客配置" scheme="http://jackzhu.top/categories/%E5%8D%9A%E5%AE%A2%E9%85%8D%E7%BD%AE/"/>
    <category term="博客配置" scheme="http://jackzhu.top/tags/%E5%8D%9A%E5%AE%A2%E9%85%8D%E7%BD%AE/"/>
    <content>
      <![CDATA[<h1 id="Hexo博客配置Github-Actions和仓库分支存储实现自动化编译部署"><a href="#Hexo博客配置Github-Actions和仓库分支存储实现自动化编译部署" class="headerlink" title="Hexo博客配置Github Actions和仓库分支存储实现自动化编译部署"></a>Hexo博客配置Github Actions和仓库分支存储实现自动化编译部署</h1><p>这次的这个自动化其实是解决了一个大的问题，之前不带自动化的处理太麻烦了，而且加上之前也没配置好图床，导致每次写博客不仅要准备很久的材料，还要一张一张的上传照片获取链接，然后源文件建了一个github仓库，然后一个仓库只用作github pages，这样的话，每次写博客都要手动编译，使用<code>hexo g</code>然后<code>hexo d</code>，部署，最后使用另外一个仓库提交更改，然后commit，push到github pages仓库，这样很麻烦，所以我就想到了使用github actions自动部署博客，然后使用仓库分支存储图片等资源。（虽然不怎么写博客）<br>相信以后写文章方便之后一定能经常写（大概）。<br>这块我参考了之前浪潮的一次技术讲座，不过那个稍微麻烦了一些，我这里就简化了一下，做了个升级版。</p><h2 id="1-把源文件和github-pages的文件分开"><a href="#1-把源文件和github-pages的文件分开" class="headerlink" title="1.把源文件和github pages的文件分开"></a>1.把源文件和github pages的文件分开</h2><p>source文件和在github上编译好的github pages还是要区分开，这里选择的还是原来github pages的仓库，clone到本地后</p><p>随后建立静态界面的分支，同一个Bash窗口，键入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> 你的Github用户名.github.io <span class="comment"># 进入博客仓库文件夹</span></span><br><span class="line">git branch html <span class="comment"># 新建静态页面分支，存放生成的博客页面</span></span><br></pre></td></tr></table></figure><h2 id="2-main分支文件修改"><a href="#2-main分支文件修改" class="headerlink" title="2. main分支文件修改"></a>2. main分支文件修改</h2><p>首先将你的仓库文件夹清空。</p><p>注：所有清空操作建议在Git Bash窗口中进行，键入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">rm</span> -f * -r <span class="comment"># 强制递归清空仓库文件夹</span></span><br></pre></td></tr></table></figure><p>这样不会将.git&#x2F;文件夹中的仓库记录(这里此文件夹作为隐藏文件没有显示)删除，否则后续Git无法定位，也就无法继续操作<br>随后将之前博客的文件夹中的所有文件复制到这个仓库文件夹中，注意不要复制.git&#x2F;文件夹，因为这是仓库记录，复制后Git无法定位，也就无法继续操作；另外node_modules&#x2F;文件夹也不需要复制，因为这是node.js的依赖包，不需要上传到仓库中，不然可能会报错。</p><h2 id="3-推送main分支更改"><a href="#3-推送main分支更改" class="headerlink" title="3. 推送main分支更改"></a>3. 推送main分支更改</h2><p>回到仓库文件夹下的Git Bash窗口，输入:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git add . <span class="comment"># 添加所有文件</span></span><br><span class="line">git commit -m <span class="string">&quot;update branch main&quot;</span> <span class="comment"># 提交更改</span></span><br><span class="line">git push <span class="comment"># 将修改推送到远程仓库</span></span><br></pre></td></tr></table></figure><h2 id="4-配置GiHub-Actions工作流文件"><a href="#4-配置GiHub-Actions工作流文件" class="headerlink" title="4. 配置GiHub Actions工作流文件"></a>4. 配置GiHub Actions工作流文件</h2><p>在仓库文件夹.github&#x2F;下新建一个目录workflows&#x2F;(注意有两层目录)，在里面新建一个hexo_build_deploy.yml文件，内容如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">Hexo</span> <span class="string">Build</span> <span class="string">&amp;</span> <span class="string">Deploy</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line"><span class="comment"># 触发事件</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="comment"># 排除分支</span></span><br><span class="line">    <span class="attr">branches-ignore:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;html&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 工作流</span></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">build:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span> <span class="string">branch</span> <span class="string">main</span></span><br><span class="line">          <span class="attr">uses:</span> <span class="string">actions/checkout@v2</span></span><br><span class="line">          <span class="attr">with:</span></span><br><span class="line">            <span class="attr">ref:</span> <span class="string">main</span></span><br><span class="line">            <span class="attr">path:</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 工具安装</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Use</span> <span class="string">Node.js</span></span><br><span class="line">          <span class="attr">uses:</span> <span class="string">actions/setup-node@v3</span></span><br><span class="line">          <span class="attr">with:</span></span><br><span class="line">            <span class="attr">node-version:</span> <span class="string">&#x27;20&#x27;</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">dependencies</span></span><br><span class="line">          <span class="attr">run:</span> <span class="string">npm</span> <span class="string">install</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 构建</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Build</span></span><br><span class="line">          <span class="attr">run:</span> <span class="string">npm</span> <span class="string">run</span> <span class="string">build</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 部署</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Deploy</span></span><br><span class="line">          <span class="attr">uses:</span> <span class="string">JamesIves/github-pages-deploy-action@v4.5.0</span></span><br><span class="line">          <span class="attr">with:</span></span><br><span class="line">            <span class="attr">branch:</span> <span class="string">html</span></span><br><span class="line">            <span class="attr">folder:</span> <span class="string">public</span></span><br></pre></td></tr></table></figure><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240303020755.png" alt="20240303020755"></p><h2 id="5-修改GitHub仓库设置"><a href="#5-修改GitHub仓库设置" class="headerlink" title="5.修改GitHub仓库设置"></a>5.修改GitHub仓库设置</h2><p>先在博客仓库Settings的Pages中将Branch设置为html<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240303020837.png" alt="20240303020837"></p><p>然后将Actions下的General中的Workflow permissons设置为Read and write permissions<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240303020935.png" alt="20240303020935"></p><h2 id="6-推送更改"><a href="#6-推送更改" class="headerlink" title="6. 推送更改"></a>6. 推送更改</h2><p>然后将更改推送到远程仓库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m <span class="string">&quot;update&quot;</span></span><br><span class="line">git push</span><br></pre></td></tr></table></figure><h2 id="7-等待部署完成"><a href="#7-等待部署完成" class="headerlink" title="7. 等待部署完成"></a>7. 等待部署完成</h2><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240303021139.png" alt="20240303021139"><br>没有报错的话就完成了<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240303021234.png" alt="20240303021234"><br>https最好也打开。</p><h2 id="一些其他问题"><a href="#一些其他问题" class="headerlink" title="一些其他问题"></a>一些其他问题</h2><p>我在配置过程中碰到了一些其他问题，腾讯云配置DNS我开始弄的有点问题，就重新配的DNS Pod 。</p><p>除此之外，在配置的过程中node_modules是不需要的，我开始这里加了，后面报错，去掉就好了。</p><p>另外一个问题是我把源文件复制过去之后，最后部署完成之后网页是空白，最后发现是theme文件下的butterfly主题文件夹是空的，我把这个文件夹删了，然后重新clone了一遍butterfly主题，再部署就好了。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="https://www.xdu-inspur.club/blog/site/%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/get_a_blog.html#_7">https://www.xdu-inspur.club/blog/site/%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/get_a_blog.html#_7</a></p>]]>
    </content>
    <id>http://jackzhu.top/2024/03/03/Hexo%E5%8D%9A%E5%AE%A2%E9%85%8D%E7%BD%AEGithub%20Actions%E5%92%8C%E4%BB%93%E5%BA%93%E5%88%86%E6%94%AF%E5%AD%98%E5%82%A8%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E5%8C%96%E7%BC%96%E8%AF%91%E9%83%A8%E7%BD%B2/</id>
    <link href="http://jackzhu.top/2024/03/03/Hexo%E5%8D%9A%E5%AE%A2%E9%85%8D%E7%BD%AEGithub%20Actions%E5%92%8C%E4%BB%93%E5%BA%93%E5%88%86%E6%94%AF%E5%AD%98%E5%82%A8%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E5%8C%96%E7%BC%96%E8%AF%91%E9%83%A8%E7%BD%B2/"/>
    <published>2024-03-03T02:19:37.000Z</published>
    <summary>本文介绍如何使用Github Actions自动部署博客，并且使用仓库分支存储图片等资源。</summary>
    <title>Hexo博客配置Github Actions和仓库分支存储实现自动化编译部署</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="技术问题" scheme="http://jackzhu.top/categories/%E6%8A%80%E6%9C%AF%E9%97%AE%E9%A2%98/"/>
    <category term="技术问题" scheme="http://jackzhu.top/tags/%E6%8A%80%E6%9C%AF%E9%97%AE%E9%A2%98/"/>
    <content>
      <![CDATA[<h1 id="目前碰到的一些技术问题与知识"><a href="#目前碰到的一些技术问题与知识" class="headerlink" title="目前碰到的一些技术问题与知识"></a>目前碰到的一些技术问题与知识</h1><h2 id="ubuntu安装驱动时候出现的问题"><a href="#ubuntu安装驱动时候出现的问题" class="headerlink" title="ubuntu安装驱动时候出现的问题"></a>ubuntu安装驱动时候出现的问题</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">echo -e “blacklist nouveau\noptions nouveau modeset=0” &gt; /etc/modprobe.d/disable-nouveau.conf</span><br><span class="line">update-initramfs -u</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>修改启动参数:在GRUB菜单中选择Ubuntu启动选项，按下”e”键以编辑启动参数。尝试在命令行中添加”nomodeset”参数，然后按下F10键启动。</p><h2 id="ubuntu安装cuda驱动时候出现报错"><a href="#ubuntu安装cuda驱动时候出现报错" class="headerlink" title="ubuntu安装cuda驱动时候出现报错"></a>ubuntu安装cuda驱动时候出现报错</h2><ul><li>安装显卡驱动需要关闭图形界面，在命令行完成安装（提前下好安装包）<br>关闭图形界面</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl isolate multi-user.target</span><br></pre></td></tr></table></figure><p>开机可能出现黑屏，按<code>ctrl + shift + F2</code>即可进入命令行窗口，登录即可，随后安装驱动，安装完成之后，执行下边命令开机默认进入图形用户界面。  </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl isolate graphical.target</span><br></pre></td></tr></table></figure><p><a href="https://blog.csdn.net/xiaoyuxin1/article/details/124526430">给Ubuntu安装驱动（nvidia）保姆级教程（方法一）_X.等雨停的博客-CSDN博客</a></p><h2 id="ubuntu拨号上网以及各种方式上网设置"><a href="#ubuntu拨号上网以及各种方式上网设置" class="headerlink" title="ubuntu拨号上网以及各种方式上网设置"></a>ubuntu拨号上网以及各种方式上网设置</h2><p>命令行输入即可进入图形管理界面</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nm-connection-editor</span><br></pre></td></tr></table></figure><h2 id="ubuntu22-04-向日葵远程无法连接成功"><a href="#ubuntu22-04-向日葵远程无法连接成功" class="headerlink" title="ubuntu22.04 向日葵远程无法连接成功"></a>ubuntu22.04 向日葵远程无法连接成功</h2><p>需要切换桌面模式</p><h2 id="ubuntu机械盘挂载"><a href="#ubuntu机械盘挂载" class="headerlink" title="ubuntu机械盘挂载"></a>ubuntu机械盘挂载</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">创建挂载路径</span></span><br><span class="line">sudo mkdir /data</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">格式化硬盘</span></span><br><span class="line">sudo mkfs -t ext4 /dev/sda</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">挂载硬盘</span></span><br><span class="line">sudo mount -t ext4 /dev/sda /data</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">开机自动挂载</span></span><br><span class="line">sudo vim /etc/fstab</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">在最后一行加入</span></span><br><span class="line">/dev/sda /data ext4  defaults 0 0</span><br></pre></td></tr></table></figure><h2 id="使用yaml创建虚拟环境"><a href="#使用yaml创建虚拟环境" class="headerlink" title="使用yaml创建虚拟环境"></a>使用yaml创建虚拟环境</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda env create -f environment.yml</span><br></pre></td></tr></table></figure><p>例如：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">transformer</span></span><br><span class="line"><span class="attr">channels:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">pytorch</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">conda-forge</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">defaults</span></span><br><span class="line"><span class="attr">dependencies:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">_libgcc_mutex=0.1=main</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">_openmp_mutex=4.5=1_gnu</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">astor=0.8.1=py39h06a4308_0</span></span><br><span class="line">  <span class="bullet">-</span> <span class="string">autograd=1.3=pyhd3eb1b0_1</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">pip:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">argparse==1.4.0</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">einops==0.3.2</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">nystrom-attention==0.0.11</span></span><br><span class="line"><span class="attr">prefix:</span> <span class="string">/home/ps/anaconda3/envs/transformer</span></span><br></pre></td></tr></table></figure><p>最后的<code>prefix</code>指定环境位置</p><h2 id="使用-gitkeep文件保存空文件夹"><a href="#使用-gitkeep文件保存空文件夹" class="headerlink" title="使用.gitkeep文件保存空文件夹"></a>使用<code>.gitkeep</code>文件保存空文件夹</h2><h2 id="创建一个新的普通用户"><a href="#创建一个新的普通用户" class="headerlink" title="创建一个新的普通用户"></a>创建一个新的普通用户</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sudo useradd -m ai -s /bin/bash</span><br><span class="line">sudo passwd ai</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">sudo adduser ai sudo</span></span><br><span class="line">su ai</span><br></pre></td></tr></table></figure><ul><li>创建了可以登录的ai用户并使用&#x2F;bin&#x2F;bash作为shell。</li><li>设置密码。</li><li>为ai用户增加管理员权限。</li><li>切换登录用户为ai。</li></ul><h2 id="为普通用户添加sudo权限"><a href="#为普通用户添加sudo权限" class="headerlink" title="为普通用户添加sudo权限"></a>为普通用户添加sudo权限</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">为用户username添加sudo权限</span></span><br><span class="line">sudo usermod -a -G sudo username</span><br><span class="line"><span class="meta prompt_"> </span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">去除用户username的sudo权限</span></span><br><span class="line">sudo usermod -G usergroup username</span><br></pre></td></tr></table></figure><p>给用户授权</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">groups ai</span><br><span class="line">usermod -aG sudo meow</span><br><span class="line">visudo</span><br></pre></td></tr></table></figure><p>删除用户</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo deluser --remove-home ai</span><br></pre></td></tr></table></figure><p>删除用户目录</p><p>查看所有用户</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep bash /etc/passwd</span><br></pre></td></tr></table></figure><h2 id="linux新建用户也使用原来conda环境"><a href="#linux新建用户也使用原来conda环境" class="headerlink" title="linux新建用户也使用原来conda环境"></a>linux新建用户也使用原来conda环境</h2><p>a用户下安装anaconda，默认地址不变，b用户直接不可用，在b用户登录的终端编辑b用户的<code>.bashrc</code>文件，在文档最后一行加入</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export PATH=$PATH:/home/&lt;Username&gt;/anaconda3/bin</span><br></pre></td></tr></table></figure><p>保存退出，并<code>source .bashrc</code>，然后<code>conda init</code>就可以了</p><p>高级配置参考<a href="https://zhuanlan.zhihu.com/p/570747928?utm_id=0">Anaconda 多用户共享安装（Ubuntu） - 知乎 (zhihu.com)</a></p><h2 id="给用户授予docker使用权限"><a href="#给用户授予docker使用权限" class="headerlink" title="给用户授予docker使用权限"></a>给用户授予docker使用权限</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo gpasswd -a $USER docker </span><br><span class="line">newgrp docker</span><br></pre></td></tr></table></figure><p><code>$USER</code>可以更换为其他，不换就是默认的。第二步有可能需要输密码，然后输入会发现错误，其实是没有设置密码，需要先设置密码，然后这样更新组</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod a+rw /var/run/docker.sock</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo usermod -aG docker $USER</span><br></pre></td></tr></table></figure><h2 id="neofetch"><a href="#neofetch" class="headerlink" title="neofetch"></a>neofetch</h2><p>方便的查看系统的工具</p><h2 id="卸载图形界面"><a href="#卸载图形界面" class="headerlink" title="卸载图形界面"></a>卸载图形界面</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get remove gnome-shell</span><br><span class="line">sudo apt-get remove gnome </span><br><span class="line">sudo apt-get autoremove</span><br><span class="line">sudo apt-get purge gnome</span><br><span class="line">sudo apt-get autoclean</span><br><span class="line">sudo apt-get clean</span><br><span class="line">reboot</span><br></pre></td></tr></table></figure><h2 id="ssh免密登录"><a href="#ssh免密登录" class="headerlink" title="ssh免密登录"></a>ssh免密登录</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -t rsa</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat ~/.ssh/id_rsa.pub &gt;&gt; ~/.ssh/authorized_keys</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-copy-id -i ~/.ssh/id_rsa.pub root@服务器IP</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lscpu</span><br></pre></td></tr></table></figure><h2 id="cudnn-source-problem"><a href="#cudnn-source-problem" class="headerlink" title="cudnn source problem"></a>cudnn source problem</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">W: GPG error: file:/var/cudnn-local-repo-ubuntu2004-8.6.0.163  InRelease: The following signatures couldn&#x27;t be verified because the public key is not available: NO_PUBKEY 323547C4B0FE0A41</span><br><span class="line">E: The repository &#x27;file:/var/cudnn-local-repo-ubuntu2004-8.6.0.163  InRelease&#x27; is not signed.</span><br><span class="line">N: Updating from such a repository can&#x27;t be done securely, and is therefore disabled by default.</span><br><span class="line">N: See apt-secure(8) manpage for repository creation and user configuration details.</span><br></pre></td></tr></table></figure><p>then input <code>sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 323547C4B0FE0A41</code> to solve the problem.</p><p>But it doesn’t work. You can follow the steps below to solve the problem.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo cp /var/cudnn-local-repo-ubuntu2004-8.4.1.50/*.gpg /usr/share/keyrings/</span><br></pre></td></tr></table></figure><p>复制到&#x2F;usr&#x2F;share&#x2F;keyrings即可。</p><h2 id="两个盘软raid命令"><a href="#两个盘软raid命令" class="headerlink" title="两个盘软raid命令"></a>两个盘软raid命令</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install mdadm</span><br><span class="line">#格式化两块硬盘</span><br><span class="line">#sudo mkfs.ext4 -F /dev/sdb</span><br><span class="line">#sudo mkfs.ext4 -F /dev/sdc</span><br><span class="line">#mdadm管理 raid0</span><br><span class="line">sudo mdadm --create --verbose /dev/md0 --level=0 --raid-devices=2 /dev/sd&#123;b,c&#125;</span><br><span class="line">#格式化并挂载</span><br><span class="line">sudo mkfs.ext4 -F /dev/md0 sudo mkdir -p /home/md0 sudo mount /dev/md0 /home/md0</span><br></pre></td></tr></table></figure><h2 id="每隔时间杀程序"><a href="#每隔时间杀程序" class="headerlink" title="每隔时间杀程序"></a>每隔时间杀程序</h2><p>kill_python.py</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">while [ true ];do</span><br><span class="line">sleep 5</span><br><span class="line">ps -ef |grep -w  python |grep -v grep | awk &#x27;&#123;print $2&#125;&#x27; | xargs kill -9</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p>然后再讲其添加到crontab中，执行crontab -e，添加如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*/30 * * * * /usr/bin/sh /aa/bb/cleanFfmpegProcess.sh</span><br></pre></td></tr></table></figure><p>:wq即可。</p><h2 id="关闭服务器自动休眠"><a href="#关闭服务器自动休眠" class="headerlink" title="关闭服务器自动休眠"></a>关闭服务器自动休眠</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target</span><br></pre></td></tr></table></figure><p>查看系统休眠状态</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl status sleep.target</span><br></pre></td></tr></table></figure><h2 id="设置github的ssh连上github"><a href="#设置github的ssh连上github" class="headerlink" title="设置github的ssh连上github"></a>设置github的ssh连上github</h2><p>生成ssh key</p><p>读取公钥</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat ~/.ssh/id_rsa.pub</span><br></pre></td></tr></table></figure><p>复制粘贴到github。github配置ssh key的地方在<br><a href="https://github.com/settings/keys">https://github.com/settings/keys</a></p><p>测试ssh key是否配置成功，在linux开发机上输入<br>$ ssh -T <a href="mailto:&#x67;&#105;&#116;&#64;&#103;&#x69;&#116;&#x68;&#x75;&#98;&#x2e;&#x63;&#111;&#x6d;">git@github.com</a></p><p>如果出现Hi xxx! You’ve successfully authenticated, but GitHub does not provide shell access 。这就表示已成功连上github</p><p>3、配置git的用户名和邮箱</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global user.name &quot;your name&quot;</span><br><span class="line">$ git config --global user.email &quot;your_email@youremail.com&quot;</span><br></pre></td></tr></table></figure><p>以后可以通过git config –global -l来查看全局设置，git config –global -e来编辑</p><h2 id="更新包导致cuda和驱动版本不对应，NVIDIA-SMI-has-failed-because-it-couldn‘t-communicate-with-the-NVIDIA-driver"><a href="#更新包导致cuda和驱动版本不对应，NVIDIA-SMI-has-failed-because-it-couldn‘t-communicate-with-the-NVIDIA-driver" class="headerlink" title="更新包导致cuda和驱动版本不对应，NVIDIA-SMI has failed because it couldn‘t communicate with the NVIDIA driver"></a>更新包导致cuda和驱动版本不对应，NVIDIA-SMI has failed because it couldn‘t communicate with the NVIDIA driver</h2><p>终端nvidia-smi出现这样，<strong>是内核版本更新的问题，导致新版本内核和原来显卡驱动不匹配</strong></p><p>查看已安装内核</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dpkg --get-selections |grep linux-image</span><br></pre></td></tr></table></figure><p>查看正在使用的内核</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uname -a</span><br></pre></td></tr></table></figure><p>利用命令 <code>ll /usr/src/</code> 可查看下面有一个nvidia-470.82.00文件夹，版本号因电脑而异。</p><p>只需执行两条命令就好：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install dkms</span><br><span class="line">sudo dkms install -m nvidia -v 470.82.00</span><br></pre></td></tr></table></figure><p>（470.82.00表示的是驱动版本号）</p><h3 id="禁止内核自动更新"><a href="#禁止内核自动更新" class="headerlink" title="禁止内核自动更新"></a>禁止内核自动更新</h3><p>1）命令行关闭系统自动更新，使用命令打开文件并编辑</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo gedit /etc/apt/apt.conf.d/<span class="number">10</span>periodic</span><br></pre></td></tr></table></figure><p>将双引号中的“1”全部置“0”即可，修改后保存。</p><p>ubuntu默认启动了自动更新内核，为了避免出现重启系统后遇到错误进入不到系统中去，我们可以进一步关闭内核更新，使用当前内核。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ sudo apt-mark hold linux-image-generic linux-headers-generic </span><br><span class="line">linux-image-generic set on hold.</span><br><span class="line">linux-headers-generic set on hold.</span><br></pre></td></tr></table></figure><p>如果要重启启动内核更新：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-mark unhold linux-image-generic linux-headers-generic</span><br></pre></td></tr></table></figure><h2 id="文件格式问题"><a href="#文件格式问题" class="headerlink" title="文件格式问题"></a>文件格式问题</h2><p>qsub:script is written in DOS&#x2F;Windows text format</p><p>dos格式文件传输到unix系统时，会在每行的结尾多一个^M（&#x2F;r），当然也有可能看不到。但是在vim的时候，会在下面显示此文件的格式，比如 “dos.txt” [dos] 120L, 2532C 字样,表示是一个[dos]格式文件，如果是MAC系统的，会显示[MAC]。因为文件格式的原因有时会导致我们的unix程序，或者shell程序出现错误，那么需要把这些dos文件格式转换成unix格式，方法是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">vim dos.txt</span><br><span class="line">:set fileformat=unix</span><br><span class="line">:w</span><br></pre></td></tr></table></figure><h2 id="centos7安装驱动"><a href="#centos7安装驱动" class="headerlink" title="centos7安装驱动"></a>centos7安装驱动</h2><p><a href="https://www.cnblogs.com/2012blog/p/9431432.html">https://www.cnblogs.com/2012blog/p/9431432.html</a></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/</span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge</span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/</span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/</span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/</span><br><span class="line">conda config --set show_channel_urls yes</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="彻底卸载wsl"><a href="#彻底卸载wsl" class="headerlink" title="彻底卸载wsl"></a>彻底卸载wsl</h2><p>在powerShell上<br>先看还有哪些子系统</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wsl --list --all</span><br></pre></td></tr></table></figure><p>注销子系统</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wsl --unregister Ubuntu</span><br></pre></td></tr></table></figure><h2 id="只读文件系统"><a href="#只读文件系统" class="headerlink" title="只读文件系统"></a>只读文件系统</h2><p>在linux硬盘挂载时候，部分情况磁盘变成ro只读而不是rw<br>一般df查看挂载设备</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">umount -i 挂载路径</span><br></pre></td></tr></table></figure><p>rm -rf 目录&#x2F;文件</p><p>在部分情况下，先使用windows系统，后使用linux，可能有系统挂载问题，即使chmod 777 也没用，出现错误：</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/image2.png" alt="image"><br>需要重新挂载</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">umount /dev/sda2</span><br></pre></td></tr></table></figure><p>报错target busy<br>杀死使用该目录的进程：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fuser  -<span class="built_in">mv</span> -k /dev/sda2</span><br></pre></td></tr></table></figure><p> 再次卸载，卸载成功后重新挂载：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mount /dev/sda2 /data</span><br></pre></td></tr></table></figure><p>报错解决：<br>报错1：“The disk contains an unclean file system (0, 0). Metadata kept in Windows cache, refused to mount. Falling back to read-only mount because the NTFS partition is in an unsafe state. Please resume and shutdown Windows fully (no hibernation or fast restarting.)”。说明是NTFS分区格式错误<br>解决方法：ntfsfix修复，需要安装工具：<br>sudo apt-get install ntfs-3g<br>安装完成后进行ntfsfix修复：<br>sudo ntfsfix &#x2F;dev&#x2F;sda2<br>提示修复成功。                        </p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/image3.png" alt="image3"></p><p>报错2：“没有那个文件或目录”。说明&#x2F;data目录不存在，需要创建。<br>解决方法：media根目录下创建新目录：<br>mkdir &#x2F;media&#x2F;jngk&#x2F;data<br>然后重新挂载，即可挂载成功。<br>mount &#x2F;dev&#x2F;sda2 &#x2F;media&#x2F;jngk&#x2F;data<br>现在该目录就不是只读文件系统了，在该目录下右键，新建文件夹选项也不再是灰色不可选状态了。</p><h1 id="无法将“XXX”项识别为-cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写，如果包括路径，请确保路径正确，然后再试一次"><a href="#无法将“XXX”项识别为-cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写，如果包括路径，请确保路径正确，然后再试一次" class="headerlink" title="无法将“XXX”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写，如果包括路径，请确保路径正确，然后再试一次"></a>无法将“XXX”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写，如果包括路径，请确保路径正确，然后再试一次</h1><p>进入PowerShell 模式<br>Get-ExecutionPolicy -List 查看当前所有作用域<br>1</p><p>上图显示就最后一个作用域有权限，其他作用域都没有权限，那么我们就需要去给它设置权限</p><p>设置权限<br>Set-ExecutionPolicy RemoteSigned -Scope &lt; scopeName &gt;,设置当前用户作用域具备权限，具体设置格</p><p>Set-ExecutionPolicy RemoteSigned -Scope CurrentUser<br>1</p><p>按照上面的格式，执行需要加权限的作用域，然后再去尝试之前的方法，发现就不会报错提示了。</p><h1 id="挖矿病毒处理"><a href="#挖矿病毒处理" class="headerlink" title="挖矿病毒处理"></a>挖矿病毒处理</h1><p>找到病毒文件：</p><p>方法一：由于入侵者对程序的名称做了伪装，无法直接通过其进行查找。因此使用PID入手，查找其在&#x2F;proc里的文件，进而发现了关键路径</p><p>将所有病毒文件展示出来，可以发现关键词miner</p><p>对里面的文件作进一步的查看，可以看到ETH（以太坊）、POOL、WALLET等关键词，实锤中了挖矿病毒</p><p>此外，查看run文件，可以发现这个病毒文件确实做了伪装</p><p>方法二：考虑到入侵者可能会用某些方式将病毒程序作一定的隐藏，这里通过查看用户的计划任务来定位病毒文件，因为病毒文件可能被kill掉而入侵者不会每次都自己手动启动的，肯定会设置自动启动。查看计划任务的命令为crontab -l<br>为了查看病毒文件在服务器上是否有多份，可以使用如下命令一次性查看所有用户的计划任务：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> u <span class="keyword">in</span> $(<span class="built_in">cat</span> /etc/passwd | <span class="built_in">cut</span> -d<span class="string">&quot;:&quot;</span> -f1)</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="variable">$u</span>&gt;&gt;temp.txt</span><br><span class="line">    crontab -l -u <span class="variable">$u</span> &gt;&gt; temp.txt</span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">cat</span> temp.txt</span><br><span class="line"><span class="built_in">rm</span> temp.txt</span><br></pre></td></tr></table></figure><p>方法三：尝试定位病毒文件中的特有关键词，比如“miner”<br>updatedb<br>locate miner</p><h1 id="由于locate命令不能查找-dev-shm之类的路径，以防万一可以使用find命令，不过会很慢"><a href="#由于locate命令不能查找-dev-shm之类的路径，以防万一可以使用find命令，不过会很慢" class="headerlink" title="由于locate命令不能查找&#x2F;dev&#x2F;shm之类的路径，以防万一可以使用find命令，不过会很慢"></a>由于locate命令不能查找&#x2F;dev&#x2F;shm之类的路径，以防万一可以使用find命令，不过会很慢</h1><h1 id="find-name-miner"><a href="#find-name-miner" class="headerlink" title="find &#x2F; -name miner"></a>find &#x2F; -name miner</h1><p>应对方法：</p><p>删除整个病毒文件夹（python），kill相关的PID，并删除相关定时任务（使用命令crontab -e，或者如果只有一个定时任务的话可以用命令crontab -r）</p><p>更改所有用户的密码，并设置一定的密码复杂度（可以使用cracklib）</p><p>移除所有除了管理员以外用户的sudo权限</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">admin=<span class="string">&quot;root,sudo,%sudo&quot;</span> <span class="comment"># 填入管理员账号（前三个不能删）</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> $(<span class="built_in">cat</span> /etc/sudoers|grep  <span class="string">&quot;ALL=(ALL:ALL) ALL&quot;</span>|<span class="built_in">cut</span> -f 1|<span class="built_in">cut</span> -f 1 -d <span class="string">&#x27; &#x27;</span>)</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="variable">$i</span></span><br><span class="line">    <span class="keyword">if</span> [ -z <span class="string">&quot;<span class="subst">$(echo $admin|grep $i)</span>&quot;</span> ]</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">&quot;*** deluser <span class="variable">$i</span> sudo&quot;</span></span><br><span class="line">        deluser <span class="variable">$i</span> sudo</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> $(getent group sudo|<span class="built_in">cut</span> -f 4 -d :|<span class="built_in">tr</span> -s <span class="string">&#x27;,&#x27;</span> <span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="variable">$i</span></span><br><span class="line">    <span class="keyword">if</span> [ -z <span class="string">&quot;<span class="subst">$(echo $admin|grep $i)</span>&quot;</span> ]</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">&quot;*** deluser <span class="variable">$i</span> sudo&quot;</span></span><br><span class="line">        deluser <span class="variable">$i</span> sudo</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>删除现有的所有密钥&amp;授权</p><p>updatedb</p><h1 id="删除公钥-密钥"><a href="#删除公钥-密钥" class="headerlink" title="删除公钥+密钥"></a>删除公钥+密钥</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> pub <span class="keyword">in</span> $(locate .pub|grep .pub$)</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    u=$(ll <span class="variable">$pub</span>|awk <span class="string">&#x27;&#123;printf $3&#125;&#x27;</span>)</span><br><span class="line">    <span class="comment"># 根据UID判断所属用户是否为普通用户</span></span><br><span class="line">    <span class="keyword">if</span> [ 999 -lt $(<span class="built_in">id</span> -u <span class="variable">$u</span>) ]</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">            pri=$(<span class="built_in">echo</span> <span class="variable">$&#123;pub%????&#125;</span>)</span><br><span class="line">            <span class="built_in">rm</span> <span class="variable">$pub</span></span><br><span class="line">            <span class="built_in">rm</span> <span class="variable">$pri</span></span><br><span class="line">            <span class="built_in">echo</span> del  <span class="variable">$u</span> <span class="variable">$pub</span> <span class="variable">$pri</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">echo</span> save <span class="variable">$u</span> <span class="variable">$pub</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="comment"># 删除knowN_hosts</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> $(locate known_hosts|grep known_hosts$)</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">rm</span> <span class="variable">$i</span></span><br><span class="line">    <span class="built_in">echo</span> del <span class="variable">$i</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="comment"># 删除authorized_keys</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> $(locate authorized_keys|grep authorized_keys$)</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">rm</span> <span class="variable">$i</span></span><br><span class="line">    <span class="built_in">echo</span> del <span class="variable">$i</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><h1 id="查看剩余"><a href="#查看剩余" class="headerlink" title="查看剩余"></a>查看剩余</h1><p>updatedb<br>locate .pub|grep .pub$<br>locate known_hosts<br>locate authorized_keys</p><p>可能会遇到某个文件用root权限也删除不了</p><p>如果使用命令lsattr发现该文件的隐藏属性中存在除了e以外的属性，则用命令chattr来移除这些属性</p><p>设置远程连接只能使用密钥，不能使用密码</p><p>禁止使用root账号进行远程连接</p><p>使用终端安全杀毒软件、内网安全监控产品、漏洞扫描设备等专业工具</p><h2 id="查杀病毒"><a href="#查杀病毒" class="headerlink" title="查杀病毒"></a>查杀病毒</h2><p>1、crontab -e 发现一条自启动任务，且为“python”程序，但不确定是否为挖矿程序，暂时保留<br>2、根据挖矿程序PID，进入&#x2F;proc&#x2F;PID目录下检查<br>进入&#x2F;tmp&#x2F;…&#x2F;Python目录<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/image4.png" alt="image4"><br>通过sudo vim config.ini检查config.ini文件，发现正是挖矿配置文件，从而自启动任务为挖矿任务<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/image5.png" alt="image5"><br>4、kill挖矿进程，删除挖矿文件及自启动任务，同时关闭挖矿病毒使用的端口<br>5、使用杀毒软件全盘查杀</p><h2 id="在cuda版本过高的情况nvidia官方bug，会出现内存泄露no-such-progress"><a href="#在cuda版本过高的情况nvidia官方bug，会出现内存泄露no-such-progress" class="headerlink" title="在cuda版本过高的情况nvidia官方bug，会出现内存泄露no such progress"></a>在cuda版本过高的情况nvidia官方bug，会出现内存泄露no such progress</h2><p>使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python3 -m pip install --upgrade nvidia-ml-py</span><br></pre></td></tr></table></figure><h2 id="fsl-安装"><a href="#fsl-安装" class="headerlink" title="fsl 安装"></a>fsl 安装</h2><p>使用本地安装包<br>解压缩到要安装的文件夹。推荐和我一样解压缩到&#x2F;usr&#x2F;local目录下<br>有可能会出现权限不足的问题无法解压缩，可以在usr目录中打开终端，输入<br>sudo chmod -R 777 local<br>输入密码后，打开权限<br>配置环境变量.bashrc</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">export FSLDIR=/usr/local/fsl</span><br><span class="line">export PATH=$PATH:$FSLDIR/bin</span><br><span class="line">source $FSLDIR/etc/fslconf/fsl.sh</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="卸载cuda"><a href="#卸载cuda" class="headerlink" title="卸载cuda"></a>卸载cuda</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd /usr/local/cuda-xx.x/bin/</span><br><span class="line">sudo ./cuda-uninstaller</span><br><span class="line">sudo rm -rf /usr/local/cuda-xx.x</span><br></pre></td></tr></table></figure><h1 id="微星tpm"><a href="#微星tpm" class="headerlink" title="微星tpm"></a>微星tpm</h1><p>微星gl63有TPM的，下面教你如何操作：<br>首先进bios<br>然后按右边的CTRL+SHIFT+左边的ALT+F2开启超级模式。<br>之后在高级选项中找到PCH-FW Configuration<br>找到PTT Configuratio<br>把dTPM改为PTT（如果已经是PTT可以跳过此步）。<br>之后在高级选项中找到Trusted Computing<br>找到Security Device Support，将Disable改为Enable<br>按F10保存。</p>]]>
    </content>
    <id>http://jackzhu.top/2023/09/03/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B8%B8%E8%A7%81%E6%8A%80%E6%9C%AF%E9%97%AE%E9%A2%98%E4%B8%8E%E6%8A%80%E5%B7%A7/</id>
    <link href="http://jackzhu.top/2023/09/03/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B8%B8%E8%A7%81%E6%8A%80%E6%9C%AF%E9%97%AE%E9%A2%98%E4%B8%8E%E6%8A%80%E5%B7%A7/"/>
    <published>2023-09-03T12:29:37.000Z</published>
    <summary>本文记录了一些常见的技术问题与技巧</summary>
    <title>服务器常见技术问题与技巧</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="生活" scheme="http://jackzhu.top/categories/%E7%94%9F%E6%B4%BB/"/>
    <category term="随笔" scheme="http://jackzhu.top/tags/%E9%9A%8F%E7%AC%94/"/>
    <content>
      <![CDATA[<h1 id="浅谈《原神》游戏的理解"><a href="#浅谈《原神》游戏的理解" class="headerlink" title="浅谈《原神》游戏的理解"></a>浅谈《原神》游戏的理解</h1><h2 id="关于游戏本身"><a href="#关于游戏本身" class="headerlink" title="关于游戏本身"></a>关于游戏本身</h2><p>游戏本身而言是全平台兼容的角色扮演（RPG）游戏，在更多的时候是划分在手游类别的，在手游中属于天花板的水平，但是相比于很多主机游戏以及部分买断制游戏如埃尔登法环、赛博朋克2077、荒野大镖客等游戏还是有不小的差距。作为一款国产二次元游戏，该游戏做的已经很不错了，自从游戏发布以来，月充值流水屡创新高，小部分的重度氪金用户支撑起了大部分的零氪玩家，不氪金也可以体验到游戏的所有内容和大部分游戏体验，氪金的点主要就在于角色和武器需要抽取，而抽取的成本不低，16元一抽，在非常特殊的情况下甚至需要两千多元才能抽取到一个角色，这也是游戏被诟病的主要一点。</p><h2 id="对于游戏受众"><a href="#对于游戏受众" class="headerlink" title="对于游戏受众"></a>对于游戏受众</h2><p>对于游戏受众而言，可以在游戏中体验到不同的国家，在扮演旅行者的过程中体验到多种多样的内容，很多人是冲着游戏角色来的，也有很多是体验高质量的画风等等。</p><p>游戏好的一点是游戏属于内容制作型，属于PVE即玩家对战环境，这样玩家与玩家之间不存在什么竞争关系，这样使得游戏的风气是非常和谐的，在这么长时间的游戏过程中和很多玩家交流，都是非常热心帮忙的，大佬后期长草经常会帮新玩家探索打boss等等；而很多游戏都是属于PVP，即玩家和玩家对战，这样的竞技性游戏很多时候几乎每次玩都会见到互相骂的情况。相比之下，原神游戏的环境还是好了太多。</p><p>相比于买断制游戏，这种无门槛体验所有内容的方式还是更适应实际的需要，很多人天天呼吁买断制如何好，然而叫好不叫座，动辄数百元的游戏，还没开始玩就要先花几百元，还不知道好不好以及能不能玩得来，成本还是太高了，所谓的外国人更喜欢3A大作也并没有，原神在海外的流水也非常高，也很受国外玩家喜爱。对于很多游戏玩家，不仅有很多买断游戏，也在原神中氪金了很多，很多游戏都玩，也有了不同的游戏体验。</p><p>除此之外，游戏制作中还花重金在音乐和画面上，在游戏中也能体验到各国传统的音乐，相关音乐很多时候都是各国的交响乐团演奏的，制作品质不低，高雅的艺术也变得易于接受和体验，对于玩家而言也是很不错的。游戏的制作过程中也发扬了部分中国传统文化，将很多传统傩舞戏剧等内容加入游戏，将非常晦涩难以接近体验的传统文化变得通俗易接受，也让外国人了解到这些内容，虽然不能完全成为文化输出，但是让外国人了解到这些，甚至进一步加深了解还是很好的，相比之前大价钱在各国建设孔子学院而收效甚微，这种潜移默化的方式还是更容易接受，也赚得了外国人的钱。虽然米哈游作为一个公司也是要盈利，但是在盈利的过程中确实对于中国文化的输出起到了一点的作用，这点也是值得肯定的。</p><h2 id="对于网络喷子"><a href="#对于网络喷子" class="headerlink" title="对于网络喷子"></a>对于网络喷子</h2><p>一部分网络喷子的关注点在于《原神》的全平台兼容性，全平台中很多玩家都是手游玩家，而很多玩家是PC玩家或者主机玩家，在游戏圈中存在着所谓的鄙视链，即主机&gt;PC&gt;手游，这种鄙视究其根本在于经济基础，一般家庭很少会有花几千元买游戏机；个人电脑适用性较广；手游成本最低，可以玩手游的人最为广泛。《原神》更多的时候是作为手游来比较，因此很多时候处于鄙视圈底层。这种鄙视深究可以发现，相比人种歧视，很多时候贫富差距的歧视以及地域歧视都不容忽略。</p><p>另一部分原因是由于游戏做的过于好，游戏质量之高远高于之前的其他很多游戏，游戏火出圈后就存在几种问题：</p><ol><li>很多人接触的游戏都是较少的，只有主流的《王者荣耀》、《和平精英》、《英雄联盟》或者吃鸡、守望、CF、csgo等等，部分游戏质量并不高，但是基于社交的游戏更符合大众推广，哪怕游戏质量一般，但是《原神》在手游上的天花板让很多人第一次接触到很好玩的游戏，日常痴迷甚至到处开喷说不如原神，在和《原神》毫无关系的地方刷游戏的相关内容或者引战。</li><li>游戏玩家数量过多导致的问题就是受众过于广泛，作为一款12+的游戏，游戏中可能有小中学生到高中大学生，再到不同年龄阶段和不同文化水平的人，俗话说林子大了什么鸟都有，存在部分不理智的玩家也很正常，这种就类似于一个地区的人有个别不文明就直接认为整个地区的人都有问题，这样的玩家毕竟是少数。很多游戏如英雄联盟等有时候玩家也会有不理智的行为，也不会有很多人认为这一群体就有问题了，同样也是受众广泛，表现很多时候并不同。</li><li>游戏质量高对于竞争对手的压力，相比之前的很多游戏，这都算是降维打击，演示的视频就是实际实机视频，这种就类似于吃了一碗红烧牛肉面，打开方便面发现包装和实物一样，这种在手游中从未遇到过，导致竞品难以望其项背，相比于投巨资做同等级别的游戏还可能竞争不过，抹黑反串引战已经是成本最低的最优解了，因此从两年半前游戏发布以及游戏刚出之前很多媒体都声称黑暗降临，至今很多都在抹黑并在各种无关的地方刷相关内容，或者取相关的名字换头像，然后发引战言论，这也就是最初所谓op的由来。</li><li>很多玩家的跟风心理，部分玩家也没玩过很多游戏，也没体验过《原神》，但是经常在很多地方刷到相关的反对言论就认为游戏如何不好，低人一等并鄙视玩游戏的人，很多这种人都是心智不成熟，玩游戏还能玩出优越感，也有的这些人后来也开始玩原神，也是出于跟风心理，不过这也正常，毕竟很多人都是这样的。</li><li>部分玩家的崇洋媚外心理，认为外国人做的东西就是好的，中国人做的就是不好的，就要抹黑，然而在国外，现在已经过去了两年半，《原神》游戏的流水还是很高，热度也不断，移动端2022年就超过了190亿，实际而言，身边玩原神的很多，大部分都不是手游玩家，实际流水要高很多，游戏质量得到了肯定。在国外都很受欢迎证明外国人也肯定，那么很多时候在国内黑的更多可能会有点奇怪。</li></ol><h2 id="对玩家潜在的风险"><a href="#对玩家潜在的风险" class="headerlink" title="对玩家潜在的风险"></a>对玩家潜在的风险</h2><p>游戏制作较为精美，而且属于角色扮演类型的游戏可能会导致的问题有以下几点：</p><ol><li>游戏较为精美而容易沉溺于虚拟世界，一玩就会花去很多时间，导致用于平时生活的时间减少，相比于很多游戏而言，《原神》用于每日基本任务等内容的时间约为半小时，加上活动抽时间做，实际而言消耗的时间并不是很严重的问题。</li><li>游戏中角色可以见到多样的世界，扮演能力强大的旅行者，而这种体验是在现实生活中不可能体验到的，出于人类的本能，这种落差是肯定会面对的，很多时候游戏玩家每天会花更多的精力在游戏中，进而影响现实生活。</li><li>游戏虽然可以联机，但是主要玩法和内容都是单机游戏，这种在互联网时代普遍存在的问题更严重了：缺乏和人的交流，更多的和游戏内探索互动，可能导致和人交往的能力下降，性格内向以及部分性格障碍，这种潜在的问题对于部分人而言是有影响的。</li><li>游戏的一大卖点在于人物角色，人物的塑造，声音以及服装的设计是非常优秀的。实事求是的来说，一定程度的软色情，很多时候经常“老婆”的这样叫着，很多时候是出于图一乐的心理，但是有时候也会有不小的问题。游戏中的角色很多时候都有一定的人设和意义，代表着一定的特点，很多时候都是较为完美的，让人非常喜爱，但是过于沉迷于虚拟的人往往会提高人的阈值，这样在现实生活中见到正常人都是不完美的时候产生的巨大落差有可能会使得不能很好的面对现实生活中的人。</li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>写这些更多的是为了树立一个正确的思想，《原神》也不可能非常完美，但是为了黑而黑或者跟风输出可能并没有什么意义，游戏作为日常生活的一个调剂，适度游戏放松也是很好的，过于沉迷游戏而忽略现实生活则会带来问题，游戏只是个工具，想玩什么就玩什么，但是借此非要打个标签就没必要了，愿读者也有正确的价值观。</p>]]>
    </content>
    <id>http://jackzhu.top/2023/02/07/%E6%B5%85%E8%B0%88%E3%80%8A%E5%8E%9F%E7%A5%9E%E3%80%8B%E6%B8%B8%E6%88%8F%E7%9A%84%E7%90%86%E8%A7%A3/</id>
    <link href="http://jackzhu.top/2023/02/07/%E6%B5%85%E8%B0%88%E3%80%8A%E5%8E%9F%E7%A5%9E%E3%80%8B%E6%B8%B8%E6%88%8F%E7%9A%84%E7%90%86%E8%A7%A3/"/>
    <published>2023-02-07T00:00:00.000Z</published>
    <summary>一点点的理解</summary>
    <title>浅谈《原神》游戏的理解</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="时序定位" scheme="http://jackzhu.top/categories/%E6%97%B6%E5%BA%8F%E5%AE%9A%E4%BD%8D/"/>
    <category term="ActionFormer" scheme="http://jackzhu.top/tags/ActionFormer/"/>
    <content>
      <![CDATA[<h1 id="ActionFormer论文分享"><a href="#ActionFormer论文分享" class="headerlink" title="ActionFormer论文分享"></a>ActionFormer论文分享</h1><p>由于水平有限，讲的内容也可能会出现不是很正确的地方，欢迎大家批评指正，沟通交流。今天给大家分享的是之前做过的项目中使用到的一个模型，这个模型在时序定位中取得了非常好的效果，这是Papers with Code上在THUMOS14数据集上的结果，当后面几名还是相差一个点的时候，已经比第二名领先了十多个点，因此我拿来分享一下这个模型，讲一下关于模型的结构以及使用感受。</p><h2 id="ActionFormer在THUMOS‘14效果"><a href="#ActionFormer在THUMOS‘14效果" class="headerlink" title="ActionFormer在THUMOS‘14效果"></a>ActionFormer在THUMOS‘14效果</h2><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/tal_pwc.png" alt="image-20221106153056010"></p><h2 id="视频领域常用的数据集"><a href="#视频领域常用的数据集" class="headerlink" title="视频领域常用的数据集"></a>视频领域常用的数据集</h2><p>**THUMOS14：**数据集包含大量的人类动作在真实环境中开源视频。 动作包括日常生活动作。THUMOS14的主要挑战是动作实例持续时间的巨大变化。具体来说，短动作实例只能持续十分之一秒，而长动作实例可以持续数百秒。</p><p>**ActivityNet ：**是目前视频动作分析方向最大的数据集，包含分类和检测两个任务。目前的1.3版本有200个类别，涵盖了200种不同的日常活动。</p><p>**EPIC Kitchens 100：**记录了多个多角度、无脚本、本地环境中的厨房场景。它们均来自拍摄者真实的日常饮食生活，并且使用了一种新颖的实时音频评论方法来收集注释。</p><h2 id="时序定位任务"><a href="#时序定位任务" class="headerlink" title="时序定位任务"></a>时序定位任务</h2><p>动作识别可以看作是一个纯分类问题，其中要识别的视频基本上已经过剪辑，即每个视频包含一段明确的动作，视频时长较短，且有唯一确定的动作类别。而在时序动作定位领域，视频通常没有被剪辑，视频时长较长，动作通常只发生在视频中的一小段时间内，视频可能包含多个动作，也可能不包含动作，即为背景。找到视频中动作的起始和结束，很多时候还需要找出其中动作属于哪一类。这一任务类似于时间上的目标检测，因此很多目标检测中的方法也常常拿来应用在这一领域，比如Faster-RCNN中两阶段的思想，先找到候选区域，再筛选，回归修正。有基于滑窗的方法，基于候选区域的方法。而本次讲的ActionFormer则是单阶段无锚框的方法，如图中所示，直接通过Transformer模型预测出每一刻的动作类别和他们这一时间点到动作开始和结束的距离。</p><h2 id="ActionFormer模型结构"><a href="#ActionFormer模型结构" class="headerlink" title="ActionFormer模型结构"></a>ActionFormer模型结构</h2><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/base_ActionFormer.png" alt="image-20221106155642020"></p><p>这一模型使用分类分数以及回归分数来计算出动作的情况，分类的分数用于对动作进行分类，回归的分数用于回归出动作的开始和结束的时间点，这个过程就有点像目标检测，事实上，时序定位的很多方法都是从目标检测中借鉴过来的，这里的分类和回归也就像目标检测中找到锚框中目标的类别和对锚框体的回归，不过时序定位的这个任务是在时间上一维的。</p><h3 id="总体结构、输入输出"><a href="#总体结构、输入输出" class="headerlink" title="总体结构、输入输出"></a>总体结构、输入输出</h3><p>模型的输入是首先对视频经过特征提取，根据视频的长度处理成很多个向量，随后把特征向量送入网络，网络的开始是使用卷积进行映射，随后是一个Transformer结构作为编码器，经过这个结构之后，使用了一个轻量级的卷积进行解码，最后使用分类和回归头得到每个时刻的预测类别，开始和结束，最后通过转化变成预测的结果。</p><p>在送入模型训练的时候，只有特征向量是不行的，还是需要一些信息的，比如训练的时候就需要标注信息，片段的起始和末尾，所属的类型，划分为训练还是测试，视频的持续时间和fps帧率信息，在测试的时候不需要标注信息，但是关于视频的帧率和持续时间这些信息还是需要的。</p><p>对于模型的输出，我们需要的是一段时间的开始时刻，结束时刻以及对应的分类，因此问题可以转化为</p><p>对于时间上的每一个时刻，预测出$p(a_t),d^s_t,d^e_t$，其中$p(a_t)$包含C个值，随后使用以下的公式来求出该时刻预测的结果</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/tal_input.png" alt="image-20221106155554231"></p><p>具体的结构可以划分为以下几个部分：</p><ul><li><p><strong>特征提取</strong></p></li><li><p><strong>使用卷积进行映射</strong></p></li><li><p><strong>Transformer编码器</strong></p></li><li><p><strong>卷积网络解码</strong></p></li><li><p><strong>分类和回归头</strong></p></li><li><p><strong>损失计算</strong></p></li></ul><p>接下来我讲详细讲这几个部分。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/action_structure.png" alt="image-20221106160221382"></p><h3 id="特征提取"><a href="#特征提取" class="headerlink" title="特征提取"></a>特征提取</h3><p>特征提取是视频领域常用的操作，因为视频相比图片来说信息量更大，而且视频中存在着非常多的信息冗余，如果直接把视频放入网络，计算量也会很高，因此很多任务会使用特征提取后的特征进行处理。</p><p>使用预训练好的模型进行特征提取，一般常用双流I3D进行特征提取，双流I3D模型是视频领域中经典的模型，一路使用RGB信息建模空间信息，一路采用光流信息建模时间变化信息。预训练一般使用Kinetics-400这样的大型数据集，提取出1024*帧数的矩阵，向量和视频的帧具有时间上的对应关系，不过经过实验，其他模型如R(2+1)D、TSN等模型也可以，效果差不多。这里提取特征的时候，一般是采用一个特征向量对应16帧，然后每次向后移动1帧的这种形式，具体参数根据需要进行更改，这种得到的特征向量个数其实是总帧数-16，不过这点差别是不影响结果的。</p><h3 id="用卷积进行映射"><a href="#用卷积进行映射" class="headerlink" title="用卷积进行映射"></a>用卷积进行映射</h3><p>使用这一操作，论文中说有助于更好地结合时间序列数据的本地上下文，对于这一点，我的理解是卷积操作使得可以更好的捕捉到相邻时间前后的信息。</p><p>另一点是稳定视觉Transformer的训练，这一点怎么体现的具体论文也没说，我也不是很清楚。</p><h3 id="多尺度Transformer进行编码"><a href="#多尺度Transformer进行编码" class="headerlink" title="多尺度Transformer进行编码"></a>多尺度Transformer进行编码</h3><p>把$Z_0$进行特征表示，乘以一个W<br>$$<br>Q&#x3D;Z^0W_Q, K&#x3D;Z^0W_K, V&#x3D;Z^0W_V<br>$$<br>自注意力输出，这里就是一般Transformer的这种方式，计算一个余弦相似度，然后进行缩放，进行softmax操作，最后和Value相乘得到结果。<br>$$<br>S&#x3D;softmax(QK^T&#x2F;\sqrt(D_q))V<br>$$<br>使用Transfomer的时候这里是通过使用可选的下采样构建特征池化金字塔，从而更好的关注到时间上不同距离的影响。</p><p>作者在后续的消融实验中证明了使用Transformer结构是取得好的效果最重要的原因。</p><p>在编码的时候作者也考虑使用位置编码，但是发现加上之后效果会更差，因此默认是没有使用的</p><h3 id="使用卷积网络进行解码"><a href="#使用卷积网络进行解码" class="headerlink" title="使用卷积网络进行解码"></a>使用卷积网络进行解码</h3><p>对于使用卷积网络进行解码这一步骤中，这里使用的是带有分类和回归头的轻量级卷积网络。分类头检查特征金字塔上所有 L 层的每个时刻 t，并预测每个时刻 t 的动作概率 p(at)。分类网络是使用 3 层 1D 卷积实现的。回归头也检查金字塔上所有 L 级的每一时刻 t。不同之处在于，仅当当前时间步 t 位于某个动作中时，回归头才预测到动作开始和偏移的距离。除此之外，在后处理环节还使用了非极大值抑制（nms）操作，把多余的预测消除掉。</p><h3 id="损失计算"><a href="#损失计算" class="headerlink" title="损失计算"></a>损失计算</h3><p>在损失计算这部分，使用了分类损失和回归损失，仅当预测的分类不是背景的情况下计算回归损失，相应的还设置了权重。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/loss_actionformer.png" alt="image-20221106194551616"></p><h2 id="模型缺点与改进方向"><a href="#模型缺点与改进方向" class="headerlink" title="模型缺点与改进方向"></a>模型缺点与改进方向</h2><h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><ul><li><p>最大的问题应该还是在于使用预提取的视频特征，不是端到端的模型，从实际使用来说，特征提取花的时间远大于实际的代码训练与推理，在项目应用中，一个几秒的视频特征提取在1060上需要6秒左右，而模型推理只需要0.02秒，这一问题在应用时感知非常明显。</p></li><li><p>另一问题应该还是使用了大量有标注的信息，而这一信息不易获取而且成本很高。</p></li><li><p>文中还提到了一个问题在于存在预定义动作词汇的约束。</p></li></ul><h3 id="改进方向"><a href="#改进方向" class="headerlink" title="改进方向"></a>改进方向</h3><ul><li><p>我觉得一个问题在于可以通过可学习的前处理操作替代特征提取的操作，特征提取这一步骤使用的预训练好的模型，在使用的时候是不计算梯度，更新参数的。最近我读了一篇视频领域标注的论文SWINBERT，感觉其中的思想可以参考一些，该模型的前面使用了Video Swin Transformer模型，后面使用了稀疏注意力，而且模型对于帧率是自适应的，不需要再指定视频的帧率信息，这一思路或许可以应用在这一领域。</p></li><li><p>另一个问题在于这种方法还是有监督学习，需要使用大量人工标记的视频样本进行学习还有预定义的动作词汇的约束，未来可以从预训练方面还有半监督无监督学习等方向改进，在没有人工标签的情况下从视频和文本语料库中学习。</p></li><li><p>还有一点作者认为目前还缺乏时序动作定位领域的预训练。目前在很多领域都有很大的数据集预训练，随后微调都能取得不错的效果，而在这一领域目前还缺乏。</p></li></ul><h2 id="使用感受"><a href="#使用感受" class="headerlink" title="使用感受"></a>使用感受</h2><ul><li><p>该模型不仅可以预测有开始和结束帧的情况，还可以把开始帧设为0，仅预测结束帧作为关键帧，经过实验发现这样的方法使用起来也没问题，也能取得很好的效果。</p></li><li><p>在项目中，使用该模型效果确实非常好，而且足够轻量级就可以完成一定要求的任务，训练推理都很快。</p></li><li><p>在不调参的情况下，使用其他数据集的参数配置效果就很好。</p></li><li><p>经过实验，在小规模数据集上表现良好。</p></li><li><p>额外增加了特征提取的操作，增加了使用的复杂度，使用起来需要组合，考虑更多的问题。</p></li></ul><p>总而言之，这一模型在时序动作定位领域这一较为小众的方向中取得了不错的成绩，如果是相关方向的值得一看。</p>]]>
    </content>
    <id>http://jackzhu.top/2022/11/09/%E5%AD%A6%E6%9C%AF%E4%BA%A4%E6%B5%81%E5%88%86%E4%BA%ABActionFormer/</id>
    <link href="http://jackzhu.top/2022/11/09/%E5%AD%A6%E6%9C%AF%E4%BA%A4%E6%B5%81%E5%88%86%E4%BA%ABActionFormer/"/>
    <published>2022-11-09T18:01:37.000Z</published>
    <summary>之前读的论文</summary>
    <title>ActionFormer论文分享</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="Ubuntu" scheme="http://jackzhu.top/categories/Ubuntu/"/>
    <category term="Ubuntu" scheme="http://jackzhu.top/tags/Ubuntu/"/>
    <content>
      <![CDATA[<p>实验室的服务器到了，今天给组里的同学们分享了一下Ubuntu的基本使用，匆忙写了一点相关的东西，顺便发上来，虽然也挺基础的，但反正博客也没多少东西，就记录一下好了。</p><h1 id="文件组织结构"><a href="#文件组织结构" class="headerlink" title="文件组织结构"></a>文件组织结构</h1><p><code>/</code>为根目录，为系统最基本的目录</p><p><code>/home</code>下有用户名的文件夹，该文件夹就是<code>~</code>为主目录，为日常使用的目录</p><p>命令在终端中输入，需要注意当前所在的文件夹</p><h1 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h1><p>创建文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir xx</span><br></pre></td></tr></table></figure><p>进入文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd xxx</span><br></pre></td></tr></table></figure><p>可以使用相对路劲和绝对路径</p><p>使用相对目录回到上一级目录</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd ..</span><br></pre></td></tr></table></figure><p>进入根目录</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd /</span><br></pre></td></tr></table></figure><p>根目录下的文件非常重要，不要轻易动。</p><p>显示当前文件夹下有哪些文件和文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls</span><br></pre></td></tr></table></figure><p>后面可以接参数</p><p>如果是接-a则是查看隐藏文件</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls -a</span><br></pre></td></tr></table></figure><p>如果后接-l则是查看详细信息，包括权限</p><p>vim的使用</p><p>vim是一个非常经典的文件编辑工具</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim hello.py</span><br></pre></td></tr></table></figure><p>即可进入编辑</p><p>进入模式之后可以点击键盘的<code>i</code>或者<code>a</code>插入，即可输入，方向键可以控制，详细的命令很多，可以自行查询</p><p>编辑完成之后，需要点击<code>esc</code>退出编辑模式</p><p>随后点击<code>shift + :</code>，就是输入:，然后输入w表示保存，随后输入q表示退出</p><p>即输入<code>:wq</code>完成保存退出，后面有时候需要加上<code>!</code>表示强制</p><h1 id="权限"><a href="#权限" class="headerlink" title="权限"></a>权限</h1><p>root是最高权限，在此状态下不要轻易动一些东西，危险</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo -i</span><br></pre></td></tr></table></figure><p>进入root模式</p><p>或者</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo su</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exit</span><br></pre></td></tr></table></figure><p>退出root模式</p><p>发现文件上有锁或者x说明当前是不能使用的，需要授权</p><p>权限包括三个部分，用户user、组group、其他人other</p><p>权限内容也包括方面，读r、写w、执行x，对应的编码是4、2、1</p><p>如向日葵远程传文件，无法执行，常用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod 777 xxx</span><br></pre></td></tr></table></figure><p>xxx为文件名，包括扩展名</p><p>给文件夹和文件夹下的所有都授权</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod -R 777 xxx</span><br></pre></td></tr></table></figure><p>常用*</p><p>如</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod 777 *</span><br></pre></td></tr></table></figure><p>*一般是指全部，这里就是指当前文件夹下的所有文件（不包含下一级目录）</p><p>很多命令执行没有权限的时候都需要前面加<code>sudo</code></p><p>删除</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rm xx</span><br></pre></td></tr></table></figure><p>有时候后面会跟-rf，表示不询问，把子目录也都删除，<strong>慎用，非常危险</strong></p><h1 id="日常使用文件"><a href="#日常使用文件" class="headerlink" title="日常使用文件"></a>日常使用文件</h1><p>下载的<code>.deb</code>文件可以直接点击安装，或者使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo dpkg -i xxx</span><br></pre></td></tr></table></figure><p>也可以安装</p><p><code>.sh</code>文件可以直接输入<code>./xxx.sh</code>执行，或者<code>sh xxx.sh</code>，没有权限的时候先授权</p><p>常用命令，查看有哪些包可以升级</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br></pre></td></tr></table></figure><p>随后执行</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt upgrade</span><br></pre></td></tr></table></figure><p>将这些包升级，这是经常需要做的</p><p>apt为一种包管理的工具，有很多时候可以直接</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install xx</span><br></pre></td></tr></table></figure><p>直接下载安装</p><p>snap是ubuntu近些年大力推广的一种包管理的工具</p><h1 id="anaconda的基本使用"><a href="#anaconda的基本使用" class="headerlink" title="anaconda的基本使用"></a>anaconda的基本使用</h1><p>创建虚拟环境可以使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda create -n &lt;name&gt; python=3.x</span><br></pre></td></tr></table></figure><p>这种方式，随后可以根据<code>requirements.txt</code>的信息执行</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install -r requirements.txt</span><br></pre></td></tr></table></figure><p>安装所需的包</p><p>也可以通过</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda env create -f environment.yaml</span><br></pre></td></tr></table></figure><p>从yaml文件中创建环境并安装包。</p><p>在linux下使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source activate &lt;name&gt;</span><br></pre></td></tr></table></figure><p>在windows下执行</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda activate &lt;name&gt;</span><br></pre></td></tr></table></figure><p>激活指定的虚拟环境</p><p>使用以下命令可以删除环境</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda remove -n &lt;name&gt; --all</span><br></pre></td></tr></table></figure><h1 id="ssh"><a href="#ssh" class="headerlink" title="ssh"></a>ssh</h1><p>这是一种非常方便的远程控制的方法，广泛使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh ps@10.120.16.12</span><br></pre></td></tr></table></figure><p>输入密码即可远程命令行控制，ps为用户名，后面的为当前局域网下的ip地址，目前在有线校园网的情况下可以直接这样连接</p><p>pycharm（专业版）、vscode等软件都可以直接使用远程ssh的解释器，本地写代码，然后远程直接跑。</p>]]>
    </content>
    <id>http://jackzhu.top/2022/11/01/ubuntu%E7%9A%84%E4%BD%BF%E7%94%A8/</id>
    <link href="http://jackzhu.top/2022/11/01/ubuntu%E7%9A%84%E4%BD%BF%E7%94%A8/"/>
    <published>2022-11-01T23:17:37.000Z</published>
    <summary>一点小记录</summary>
    <title>Ubuntu的基本使用</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="姿态估计" scheme="http://jackzhu.top/categories/%E5%A7%BF%E6%80%81%E4%BC%B0%E8%AE%A1/"/>
    <category term="深度学习" scheme="http://jackzhu.top/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="【论文阅读】-Deep-High-Resolution-Representation-Learning-for-Visual-Recognition"><a href="#【论文阅读】-Deep-High-Resolution-Representation-Learning-for-Visual-Recognition" class="headerlink" title="【论文阅读】 Deep High-Resolution Representation Learning for Visual Recognition"></a>【论文阅读】 Deep High-Resolution Representation Learning for Visual Recognition</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>在计算机视觉中高分辨的表示是非常重要的，HRNet是用于识别的高分辨网络，广泛的用于姿态检测以及语义分割中，也可用于目标检测。</p><h2 id="网络结构"><a href="#网络结构" class="headerlink" title="网络结构"></a>网络结构</h2><p>相比一般的网络，HRNet具有特殊的结构，一般的卷积神经网络往往是随着网络的深入，特征图的分辨率逐渐由高到低，这样的网络结构设计适合一般的视觉问题，视觉空间信息都是冗余的，对信息的精准度要求不高，但是这种结构在面对关键点检测以及语义分割问题的时候就不能很好的完成任务，精准度不够。因此就有了HRNet的结构设计如下图所示：<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/HRnet1.png" alt="HRnet1"><br>HRNet网络在模型的整个过程中都能保持高分辨率，采用并行的网络，不同的流的分辨率不同，在网络的第n个阶段有n流个网络，从前往后每次下采样一个流，同时在阶段的连接出对信息进行交汇，从高分辨率使用卷积到低分辨率，从低分辨率的流上采样到高分辨率的流，最终得到模型。<br>模型的特色有两点：</p><ul><li>使用并行连接从高到低分辨率的卷积流</li><li>跨分辨率反复交换信息</li></ul><p>使用并行连接使得在整个过程中都保持了高分辨率的表示，使用了跨分辨率的反复融合信息使得模型对于位置具有很强的敏感性，可以较好的完成相关的工作。</p><h2 id="模型的变体"><a href="#模型的变体" class="headerlink" title="模型的变体"></a>模型的变体</h2><p>在HRNet模型中共提出了三种模型的结构，HRNetV1 HRNetV2以及HRNetV2p这三种结构<br><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/HRnet2.png" alt="HRnet2"><br>其中V1只使用了融合最后的高分辨率流，这种结构相比V2运算量更小，而在关键点检测任务中和V2版本性能基本没有差别。而V2版本对最后的信息都进行了融合，在语义分割任务中表现较好。V2p则是在V2的基础上形成特征金字塔，更适合目标检测任务。</p><h2 id="消融实验"><a href="#消融实验" class="headerlink" title="消融实验"></a>消融实验</h2><p>消融实验证明分辨率确实会影响关键的检测的质量，这一点与一般的感觉相符。对多分辨率融合的实验也证明了融合会带来好的性能提升。</p><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>对该模型的研究中可以得到一个结论，针对特定的问题来设计网络架构可能是有用的。此外，一个可能的误解在于分辨率越高，HRNet的内存消耗越大，但是实际是在姿态估计、语义分割以及目标检测中，内存成本并未很高。</p>]]>
    </content>
    <id>http://jackzhu.top/2022/09/20/HRNet/</id>
    <link href="http://jackzhu.top/2022/09/20/HRNet/"/>
    <published>2022-09-20T19:57:37.000Z</published>
    <summary>读论文的记录</summary>
    <title>【论文阅读】 Deep High-Resolution Representation Learning for Visual Recognition</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="生活" scheme="http://jackzhu.top/categories/%E7%94%9F%E6%B4%BB/"/>
    <category term="记录生活" scheme="http://jackzhu.top/tags/%E8%AE%B0%E5%BD%95%E7%94%9F%E6%B4%BB/"/>
    <content>
      <![CDATA[<h1 id="我终于把图床修好了"><a href="#我终于把图床修好了" class="headerlink" title="我终于把图床修好了"></a>我终于把图床修好了</h1><p>之前gitee的图床用不了，因此这段时间博客都一片404（其实本来也影响不大，毕竟也没几篇博客），今天总算抽出时间修一下，虽然不是什么特别麻烦的事，就是懒。之前的图还真是手动一个一个传的，效率太低，现在用了typora+picgo+github，总算是能用了，方便了不少，希望以后会多更新下吧。之前说的要把大作业都传上去，也能方便后面的学弟学妹，但是后面要么懒，要么就做毕设，最近毕设做的差不多了，又要开始准备做研究生的项目了（虽然确实不多）。</p><p>不过前段时间做的一个有意义的事是联系了两个小伙伴，把自己的考研经历分享到了<a href="https://github.com/CoderJackZhu/XD-AI-graduate_entrance_exam">GitHub</a>，三人成绩还行（平均380+），也上岸了，下一篇就分享一下经历。</p><p>最近在做毕设论文的修改，同时也学习一下深度学习相关领域的知识，毕竟准研究生了，要学的很多东西可以先准备着了。</p>]]>
    </content>
    <id>http://jackzhu.top/2022/05/10/%E4%BB%8A%E6%97%A5%E9%9A%8F%E7%AC%94/</id>
    <link href="http://jackzhu.top/2022/05/10/%E4%BB%8A%E6%97%A5%E9%9A%8F%E7%AC%94/"/>
    <published>2022-05-10T00:00:00.000Z</published>
    <summary>进步了</summary>
    <title>今日随笔</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="深度学习" scheme="http://jackzhu.top/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="http://jackzhu.top/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="Ubuntu-20-04下Pytorch深度学习环境搭建以及常用工具配置"><a href="#Ubuntu-20-04下Pytorch深度学习环境搭建以及常用工具配置" class="headerlink" title="Ubuntu 20.04下Pytorch深度学习环境搭建以及常用工具配置"></a>Ubuntu 20.04下Pytorch深度学习环境搭建以及常用工具配置</h1><p>作者：CoderJackZhu</p><p>从事计算机相关行业的在今后学习工作过程中总会接触到Linux系统，而且在很多情况下，Windows下可能会出现一些奇奇怪怪的bug，这些问题部分是系统的问题导致的，比如常见的路径中不能带中文。深度学习环境有时候为了更好的管理机器，取得更好的效率也常常采用Linux系统，这里选择Ubuntu是对于个人的萌新而言，应该选择尽量大众些的系统，出问题也容易找到解决办法，比如由于各种误操作，linux系统我至少已经重装过不下二十次了，为了更好的学习相关知识，这样一个系统的搭建也是需要的，这里写出这个博客为了方便使用，也让我之后重装系统的时候不用再找好几个博客了。</p><h2 id="“双系统”中Ubuntu安装"><a href="#“双系统”中Ubuntu安装" class="headerlink" title="“双系统”中Ubuntu安装"></a>“双系统”中Ubuntu安装</h2><p>这里的所说的双系统并不是真正的单个硬盘上多个挂载点的双系统，而是把第二个系统装在移动硬盘里面，这样正常开机默认还是Windows系统，需要选择系统就在进入系统时长按<code>F11</code>（不同品牌电脑不同），选择相应的系统就可以进入了，这样的安装相比一个硬盘上多个挂载点简易不少，配置难度低，而且不易出问题，不然一不小心两个系统都不能用了，这样Linux出了问题只需要直接覆盖重装就可以了，下面是具体步骤：</p><h3 id="准备需要的工具"><a href="#准备需要的工具" class="headerlink" title="准备需要的工具"></a>准备需要的工具</h3><p>这里软件方面需要准备的是，从官方网站上下载Ubuntu的镜像，以及刻录软件。刻录软件使用UltraIso或者balentEtcher都是可以的，balentEtcher相对操作更简易些。</p><p>硬件需要准备的是一个U盘用于制作启动盘，尽量大于等于16G，一个移动硬盘用于安装系统，尽量大于128G，毕竟实际使用过程中数据集也比较大，还是需要给后续留足空间。</p><h3 id="制作启动盘"><a href="#制作启动盘" class="headerlink" title="制作启动盘"></a>制作启动盘</h3><p>安装好Format后选择文件为之前下的系统镜像，选择硬件为U盘，然后点击Flash就可以了，等几分钟安装校验完就可以了。</p><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>然后重启并选择使用刚才的U盘启动，就可以进入安装Ubuntu的界面了，正常使用的话选择中文汉语，正常安装，勾选安装第三方软件。这个时候可以插上移动硬盘了，然后输入自己的用户名密码什么的，之后就进入选择安装位置了，这里点击清理磁盘安装就可以了，不然挂载点就很不太好理解，然后下一步点击你插入的那个硬盘，<strong>这里注意别选错</strong>，根据你的硬盘大小就能判断出了，选错其他盘的话可能你的数据就凉了，然后下一步。然后选择地图上的位置为shanghai就可以了，之后就进入安装了，等一会安装完然后点击重新启动，然后根据提示拔掉U盘，然后开机的时候选择那个硬盘启动，这个时候硬盘的名字就已经是Ubuntu了，然后两次回车就可以进入系统了，到这里，系统的安装就算完成了。</p><h2 id="深度学习Pytorch环境配置"><a href="#深度学习Pytorch环境配置" class="headerlink" title="深度学习Pytorch环境配置"></a>深度学习Pytorch环境配置</h2><p>正常使用深度学习环境跑代码，GPU是必不可少，这里只演示GPU版本的pytorch的安装，所需要的工具为Anaconda、CUDA、cuDNN、Pytorch。Anaconda可以用来管理不同版本的环境，CUDA和cuDNN是使用GPU计算所需要的工具这里需要注意相互之间的匹配关系，首先去<a href="https://pytorch.org/">pytorch官网</a>可以看到</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102136735.png" alt="6"></p><p>因此这里选择CUDA11.3，然后根据CUDA的版本，选择cuDNN的版本，具体在下载cuDNN的时候可以看到。</p><h3 id="安装驱动"><a href="#安装驱动" class="headerlink" title="安装驱动"></a>安装驱动</h3><p>安装NVIDIA驱动有多种方式，比如可以去官网下载最新版，这里介绍最简单的一种，首先打开软件与更新，然后点附加驱动这里，系统默认用的是开源的的驱动，这里选最上面的几个版本高的就可以，这里安装的cuda11.3驱动至少要470以上，然后点击应用更改等一会就可以了。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102136053.png" alt="1"></p><p>安装完成之后在命令行输入<code>nvidia-smi</code>就可以看到下图GPU情况，这就说明驱动基本没问题了。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102136894.png" alt="4"></p><h3 id="下载安装cuda"><a href="#下载安装cuda" class="headerlink" title="下载安装cuda"></a>下载安装cuda</h3><p>这里找<a href="https://developer.nvidia.com/cuda-downloads">官方网站</a></p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102136125.png" alt="2"></p><p>可以看到这里默认是11.6版本的，这里点击下方中的<code>Archive of Previous CUDA Releases</code>并选择对于的11.3版本，都是11.3的情况下选最后一位高的，之后进入以下界面，选择对应版本，然后先后输入下方的两行，第一行输入命令行，就开始下载了，下载完之后在对应的目录打开终端命令行，然后输入第二行，就开始安装了。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102137818.png" alt="3"></p><p>安装过程中看到勾选多个项目的时候，把第一项的X勾选框点下回车取消掉，由于之前已经安装了驱动，所有这里不需要安装里面附带的驱动，然后切换Install并点击回车，等待就可以安装好了。</p><p>然后添加路径，修改<code>.bashrc</code>文件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo gedit ~/.bashrc</span><br></pre></td></tr></table></figure><p>#在末尾添加：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export PATH=/usr/local/cuda/bin$&#123;PATH:+:$&#123;PATH&#125;&#125; </span><br><span class="line">export LD_LIBRARY_PATH=/usr/local/cuda/lib64$&#123;LD_LIBRARY_PATH:+:$&#123;LD_LIBRARY_PATH&#125;&#125; </span><br></pre></td></tr></table></figure><p>更新刚才输入的内容，在命令行输入： <code>source ~/.bashrc</code></p><p>安装成功输入<code>nvcc -V</code></p><h3 id="cuDNN的安装"><a href="#cuDNN的安装" class="headerlink" title="cuDNN的安装"></a>cuDNN的安装</h3><p>到<a href="https://developer.nvidia.com/cudnn">官网</a>下载文件：点击如图<code>Download cuDNN</code>按钮。下载需要NVIDIA的账号，没有的需要先注册一个。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102137133.png" alt="5"></p><p>然后进入<a href="https://developer.nvidia.com/rdp/cudnn-archive#a-collapse742-10">下载界面</a>并选择Previous Archive</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102137217.png" alt="7"></p><p>选择CUDA11.x对于的cuDNNv8.2 选择Runtime Library版的deb文件进行下载：</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102137992.png" alt="8"></p><p>安装cuDNN过程与CUDA类似：<br>在下载的文件的文件夹里面打开终端，执行命令<code>sudo dpkg -i &lt;name&gt;</code>，其中<code>&lt;name&gt;</code>为刚才下载的deb文件名<br>执行命令<code>sudo apt install &lt;name&gt;</code>,其中<code>&lt;name&gt;</code>要和自己下载的cudnn版本匹配，比如这里是libcudnn8</p><h2 id="安装Anaconda"><a href="#安装Anaconda" class="headerlink" title="安装Anaconda"></a>安装Anaconda</h2><p>Anaconda用于控制版本管理，直接在系统的python里装不太方便，库的控制也不那么直观，使用Anaconda之后会方便很多。</p><p>这里直接从<a href="https://www.anaconda.com/products/individual">官网</a>下载就可以，速度也不错，下载完之后在下载的文件夹打开终端，这里重点，命令行不要输入<code>sudo</code>，直接<code>sh &lt;name&gt;</code> <code>name</code>为刚才下的文件名然后可以了，一路回车过完协议书，然后yes同意，然后要么回车要么yes就可以了。千万别在命令行前面加<code>sudo</code>，这样anaconda3的文件夹就安装在<code>root</code>下了，这样感觉有时候不方便；直接<code>sh</code>就可以安装在你的主目录下，装好退出命令行就可以用了。</p><p>安装过程先一路回车，然后按要求都yes就好。</p><p>安装后退出命令行，然后重新进入命令行，然后输入<code>conda</code>，若出现如下则证明安装成功，若出现command not found则重启系统，若还不行则需要添加环境变量。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102137141.png" alt="10"></p><h2 id="添加环境变量"><a href="#添加环境变量" class="headerlink" title="添加环境变量"></a>添加环境变量</h2><p>输入</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><p>再执行conda，若好则安装结束，否则手动添加环境变量</p><p>输入命令</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim ~/.bashrc</span><br></pre></td></tr></table></figure><p>若vim未安装，先安装，可以使用<code>sudo apt install vim</code>安装（或者使用<code>sudo gedit ~/.bashrc</code>也可打开文件），然后执行上述命令，然后在文件的最后添加，这里的内容不要直接复制，根据自己的用户名来定</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export</span><br><span class="line">PATH=/home/&lt;自己的用户名&gt;/anaconda3/bin:$PATH</span><br></pre></td></tr></table></figure><p>输入完成后点击<code>ESC</code>, 然后输入<code>:wq</code>保存退出.</p><p>然后更新环境变量:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><p>输入conda,检查是否配置成功。</p><h2 id="创建环境并安装PyTorch"><a href="#创建环境并安装PyTorch" class="headerlink" title="创建环境并安装PyTorch"></a>创建环境并安装PyTorch</h2><p>安装后一般应用栏里是没有这个软件的，需要在命令行中输入<code>anaconda-navigator</code>等待进入就可以了，然后点左方的environment然后点下方的加号创建环境，想个环境的名字，选择需要的python版本，这里也可以使用命令来创建</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda create -n &lt;环境名&gt; python=3.8</span><br></pre></td></tr></table></figure><p>等待创建完成后在命令行中输入<code>conda info -e</code>即可查看现有哪些环境</p><p>然后进入相应的环境输入下面命令，其中这里使用的环境名为<code>env1</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source activate env1</span><br></pre></td></tr></table></figure><p>即可激活，若为windows下则为<code>conda activate env1</code>。</p><p>这样就进入环境了，随后输入pytorch官网上的命令：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch</span><br></pre></td></tr></table></figure><p>如果速度慢，则需要换源，一般默认使用清华源为以下命令：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/</span><br><span class="line"></span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge</span><br><span class="line"></span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/</span><br><span class="line"></span><br><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/</span><br><span class="line"></span><br><span class="line">conda config --set show_channel_urls yes</span><br></pre></td></tr></table></figure><p><strong>或者</strong>打开主目录下的隐藏文件<code>.condarc</code>，将其内容整体更换为以下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ssl_verify: true</span><br><span class="line">show_channel_urls: true</span><br><span class="line"></span><br><span class="line">channels:</span><br><span class="line">  - http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/win-64/</span><br><span class="line">  - http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/win-64/</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然后运行 <code>conda clean -i</code> 清除索引缓存。</p><p>这时候安装命令就要把最后的<code>-c pytorch</code>去掉，变成</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install pytorch torchvision torchaudio cudatoolkit=11.3</span><br></pre></td></tr></table></figure><p>就可以很快的下载了，也可以使用<a href="https://download.pytorch.org/whl/torch_stable.html">pytorch离线安装下载</a>直接下载whl文件，然后在命令行中进入下载的文件夹，然后输入</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install torch-<span class="number">1.9</span><span class="number">.0</span>+cu111-cp38-cp38-win_amd64.whl</span><br></pre></td></tr></table></figure><p>这样就可以安装了，若为linux则将<code>win_amd64</code>改为<code>linux_x86_64</code>即可。</p><p>若为单次换下载换源则命令为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/  </span><br></pre></td></tr></table></figure><p>在后面加上需要安装的库名字即可。</p><h2 id="验证安装成功"><a href="#验证安装成功" class="headerlink" title="验证安装成功"></a>验证安装成功</h2><p>若全部安装完成，则新建一个python脚本hello.py，内容如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">import torch</span><br><span class="line">import torchvision</span><br><span class="line">print(torch)</span><br><span class="line">print(torchvision)</span><br><span class="line">print(torch.cuda.is_available())</span><br><span class="line">x=torch.randn(5, 3)</span><br><span class="line">print(x)</span><br><span class="line">print(torch.cuda.device)</span><br><span class="line">print(torch.__version__)</span><br><span class="line">print(torchvision.__version__)</span><br><span class="line">print(torch.version)</span><br><span class="line">print(torch.version.cuda) # Corresponding CUDA version</span><br><span class="line">print(torch.backends.cudnn.version()) # Corresponding cuDNN version</span><br><span class="line">print(torch.cuda.get_device_name(0)) # GPU type</span><br></pre></td></tr></table></figure><p>当然你如果想测试一下，也可以跑一下官方示例（非必须）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mkdir ~/CUDA_test</span><br><span class="line">cd ~/CUDA_test</span><br><span class="line">git clone https://github.com/nvidia/cuda-samples</span><br><span class="line">cd ~/CUDA_test/cuda-samples/Samples/1_Utilities/deviceQuery</span><br><span class="line">make</span><br><span class="line">./deviceQuery</span><br></pre></td></tr></table></figure><p>可以得到下面这个结果：</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/img/20240531013518.png" alt="20240531013518"></p><p>检查完用不到也可以把这个samples删了。</p><p>即可查看详细情况，若cuda可用这里显示true就说明安装成功。</p><h2 id="常用软件"><a href="#常用软件" class="headerlink" title="常用软件"></a>常用软件</h2><p>深度学习环境其他非常常用的软件一般还有VScode和Pychram，一般这两个都安装比较好。</p><p>BT下载以及磁力链下载很多时候是需要的，因此需要下载工具</p><p>下载工具可以用Free Download Manger，还是非常好用的，还有qbittorrent和Motrix作为备用下载软件，这两个软件下载后不用安装，需要用的时候打开，也非常不错。</p><p>其他比如截屏剪切板等功能用utools也挺好，不过高级功能后来收费了。</p><h2 id="windows下安装的差异"><a href="#windows下安装的差异" class="headerlink" title="windows下安装的差异"></a>windows下安装的差异</h2><h3 id="更新驱动"><a href="#更新驱动" class="headerlink" title="更新驱动"></a>更新驱动</h3><p>正常使用的话，下载GeForce Experience然后把驱动更新到最新版即可，或者手动下载驱动，没有特殊需要的话默认最新版就好。</p><h3 id="安装cuda和cudnn"><a href="#安装cuda和cudnn" class="headerlink" title="安装cuda和cudnn"></a>安装cuda和cudnn</h3><p>主体部分和linux下大同小异，按要求下载安装对应版本即可，安装cuda后打开命令行输入</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvcc -V</span><br></pre></td></tr></table></figure><p>返回版本号说明安装cuda成功。</p><p>不过cudnn这里下载完后是复制到cuda对应的bin目录里面，一般是C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA。</p><p>安装后有时候不能使用则需要添加环境变量，在系统环境变量里的Path项下添加几个路径</p><p>C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1</p><p> 　　C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\lib\x64</p><p>安装完成后进入路径然后运行测试，成功则为以下界面。</p><p><img src="https://gcore.jsdelivr.net/gh/CoderJackZhu/bloggallery/PicGo/202205102138180.png" alt="11"></p><p>然后运行测试的代码即可。</p>]]>
    </content>
    <id>http://jackzhu.top/2022/04/10/Ubuntu%2020.04%E4%B8%8B%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%BB%A5%E5%8F%8A%E5%B8%B8%E7%94%A8%E5%B7%A5%E5%85%B7%E9%85%8D%E7%BD%AE/</id>
    <link href="http://jackzhu.top/2022/04/10/Ubuntu%2020.04%E4%B8%8B%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%BB%A5%E5%8F%8A%E5%B8%B8%E7%94%A8%E5%B7%A5%E5%85%B7%E9%85%8D%E7%BD%AE/"/>
    <published>2022-04-10T00:08:37.000Z</published>
    <summary>一点小记录</summary>
    <title>Ubuntu 20.04下Pytorch深度学习环境搭建以及常用工具配置</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="生活" scheme="http://jackzhu.top/categories/%E7%94%9F%E6%B4%BB/"/>
    <category term="生活" scheme="http://jackzhu.top/tags/%E7%94%9F%E6%B4%BB/"/>
    <content>
      <![CDATA[<h1 id="考研后的感想"><a href="#考研后的感想" class="headerlink" title="考研后的感想"></a>考研后的感想</h1><h1 id="想法"><a href="#想法" class="headerlink" title="想法"></a>想法</h1><p>今天是考研后的第二天晚上，考研生活算是过去了，然后就是之后的新生活了，对于之后的安排，目前来看，主线是做毕业设计，在期间可以学习一些东西。我计划近期将之前做过的大作业再复习一遍，然后整理出来，发到博客里，这不是什么大的任务，我打算先从这样的小事做起，慢慢学知识。毕竟，在有目标的情况下很多时候还不一定能一直坚持做事，现在时间比较闲了，能做多少事就比较随缘了。</p><p>之后就是毕业设计了，这个毕业设计选择的是视频理解方面的，了解过一些，感觉这块可以好好做，小导师也不错，开始push我准备毕设了，不过这块应该不是特别难，后面做也来得及，先休息下。</p>]]>
    </content>
    <id>http://jackzhu.top/2021/12/29/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/</id>
    <link href="http://jackzhu.top/2021/12/29/%E6%88%91%E7%9A%84%E7%94%9F%E6%B4%BB/"/>
    <published>2021-12-29T00:08:37.000Z</published>
    <summary>反内卷而不躺平</summary>
    <title>考研后的思考</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="机器学习" scheme="http://jackzhu.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="机器学习" scheme="http://jackzhu.top/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="摘-要"><a href="#摘-要" class="headerlink" title="摘 要"></a>摘 要</h2><p>在这次大作业中我完成了波士顿房价预测模型的实现, 波士顿房价预测是一个经典的回归模型。<br>在本次实验中, 首先对数据的分布情况以及特征信息, 相关性信息都进行了查看, 并分别对每个特征的相关性信息进行了分析, 并筛选掉无用的特征, 更好的对结果进行预测。<br>然后根据特征的信息与房价存在线性和非线性相关的关系, 这里依次选择了神经网络预测模型以及线性模型对房价的结果进行了预测, 并检验其效果。这里采用了 sklearn 中的库函数来进行训练集和测试集的划分,将 (30%) 的部分划分为测试集。<br>对于神经网络模型, 采用的是一个三层的全连接网络, 通过均方损失函数和 Adam 优化器对网络的参数进行更新, 最终使得网络可以更好的进行预测。对于线性模型, 采用了 sklearn 中的线性回归函数进行预测。<br>对于两者都进行了与实际值的对比, 并计算方差和相关系数, 从<br>而更好的对比了两者的效果差别。<br>关键词: 特征选择 神经网络 线性模型</p><h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景介绍 1<br>1.1 问题描述 1<br>2 方法 1<br>3 代码实现 1<br>3.1 读取数据及预处理 1<br>3.2 神经网络预测模型实现 3<br>3.3 线性模型 6<br>4 试验结果 6<br>4.1 实验分析 6<br>4.2 对比 13<br>5 结论 16<br>6 总结 16<br>A 程序代码 17</p><h2 id="1-背景介绍"><a href="#1-背景介绍" class="headerlink" title="1 背景介绍"></a>1 背景介绍</h2><h2 id="1-1-问题描述"><a href="#1-1-问题描述" class="headerlink" title="1.1 问题描述"></a>1.1 问题描述</h2><p>在本次的机器学习课程设计中需要选择一个项目, 应用机器学习算法到真实世界的任<br>务中去, 这里我选择了机器学习的经典案例: 波士顿房价预测任务。<br>波士顿房价数据说明: 此数据源于美国某经济学杂志上, 分析研究波士顿房价 (Boston HousePrice) 的数据集。数据集中的每一行数据都是对波士顿周边或城镇房价的情况描述, 下面对数据集变量说明, 数据集包含了 506 组数据, 一共 14 个属性, 为以下内容:<br>CRIM: 城镇人均犯罪率 ZN: 住宅用地所占比例<br>INDUS: 城镇中非住宅用地所占比例 CHAS: 虚拟变量, 用于回归分析 NOX: 环保指数<br>RM: 每栋住宅的房间数<br>AGE: 1940 年以前建成的自住单位的比例 DIS: 距离 5 个波士顿的就业中心的加权距离 RAD: 距离高速公路的便利指数 TAX: 每一万美元的不动产税率 PTRATIO: 城镇中的教师学生比例 B: 城镇中的黑人比例<br>LSTAT: 地区中有多少房东属于低收入人群 MEDV: 自住房屋房价中位数 (也就是均价)</p><h2 id="2-方法"><a href="#2-方法" class="headerlink" title="2 方法"></a>2 方法</h2><p>波士顿房价预测为一个回归模型, 回归模型的研究范围可以包括线性回归, 以及神经网络预测模型等多种, 这里实现了线性回归以及神经网络回归 [1]。</p><h2 id="3-代码实现"><a href="#3-代码实现" class="headerlink" title="3 代码实现"></a>3 代码实现</h2><h2 id="3-1-读取数据及预处理"><a href="#3-1-读取数据及预处理" class="headerlink" title="3.1 读取数据及预处理"></a>3.1 读取数据及预处理</h2><p>首先进行读取数据, 然后对数据预处理, 首先要清楚数据的分布信息, 这里使用了直方图以及箱图来观察, 然后依次查看数据的 13 个特征与房价之间的关系, 便于进行后续处理, 具体代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"></span><br><span class="line"><span class="comment"># Define column names</span></span><br><span class="line">column_names = [<span class="string">&#x27;CRIM&#x27;</span>, <span class="string">&#x27;ZN&#x27;</span>, <span class="string">&#x27;INDUS&#x27;</span>, <span class="string">&#x27;CHAS&#x27;</span>, <span class="string">&#x27;NOX&#x27;</span>, <span class="string">&#x27;RM&#x27;</span>, <span class="string">&#x27;AGE&#x27;</span>, <span class="string">&#x27;DIS&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;RAD&#x27;</span>, <span class="string">&#x27;TAX&#x27;</span>, <span class="string">&#x27;PTRATIO&#x27;</span>, <span class="string">&#x27;B&#x27;</span>, <span class="string">&#x27;LSTAT&#x27;</span>, <span class="string">&#x27;PRICE&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Read data from the CSV file</span></span><br><span class="line">all_data = pd.read_csv(<span class="string">&#x27;./housing.csv&#x27;</span>, header=<span class="literal">None</span>, delimiter=<span class="string">r&quot;\s+&quot;</span>, names=column_names)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot histograms for all columns</span></span><br><span class="line">all_data.hist()</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Show statistical description of the data</span></span><br><span class="line"><span class="built_in">print</span>(all_data.describe())</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot boxplots for the data</span></span><br><span class="line">plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.boxplot(all_data)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Correlation heatmap</span></span><br><span class="line">corr = all_data.corr()</span><br><span class="line">plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">sns.heatmap(corr, annot=<span class="literal">True</span>, cmap=<span class="string">&#x27;twilight_r&#x27;</span>)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Prepare data and labels for scatter plots</span></span><br><span class="line">data = np.array(all_data.iloc[:, :-<span class="number">1</span>], dtype=<span class="built_in">float</span>)</span><br><span class="line">label = np.array(all_data.iloc[:, -<span class="number">1</span>], dtype=<span class="built_in">float</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Scatter plot for each feature</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">13</span>):</span><br><span class="line">    plt.figure(figsize=(<span class="number">10</span>, <span class="number">7</span>))</span><br><span class="line">    plt.grid()</span><br><span class="line">    plt.scatter(data[:, i], label, s=<span class="number">5</span>)  <span class="comment"># X, Y, point size</span></span><br><span class="line">    plt.title(column_names[i])</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Remove less important features (&#x27;CHAS&#x27;)</span></span><br><span class="line">unsF = []  <span class="comment"># List to store indices of less important features</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(data.shape[<span class="number">1</span>]):</span><br><span class="line">    <span class="keyword">if</span> column_names[i] == <span class="string">&#x27;CHAS&#x27;</span>:</span><br><span class="line">        unsF.append(i)</span><br><span class="line"></span><br><span class="line">data = np.delete(data, unsF, axis=<span class="number">1</span>)  <span class="comment"># Remove less important feature</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Remove outliers in the target variable (PRICE &gt; 46)</span></span><br><span class="line">unsT = []  <span class="comment"># List to store indices of outliers</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(label.shape[<span class="number">0</span>]):</span><br><span class="line">    <span class="keyword">if</span> label[i] &gt; <span class="number">46</span>:</span><br><span class="line">        unsT.append(i)</span><br><span class="line"></span><br><span class="line">data = np.delete(data, unsT, axis=<span class="number">0</span>)  <span class="comment"># Remove samples with outlier prices</span></span><br><span class="line">label = np.delete(label, unsT, axis=<span class="number">0</span>)  <span class="comment"># Remove outliers in labels</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在查看完每个特征的信息后, 可以根据实际情况对数据无关部分以及异常的结果进行删改操作, 从而使得结果更加接近实际, 代码实现这里对于和结果相关性不大的特征进行了删除, 也去掉了部分异常高的房价。<br>在查看数据的分布信息后可以看到只有少部分数据的分布和房价是线性相关的, 大部分都不是明显的线性关系, 因此只采用线性模型进行预测可能会结果与实际差别较大, 因此这里对线性模型以及神经网络模型都进行了是实现, 并对比其结果的差别, 从而更好的进行结果分析。</p><h2 id="3-2-神经网络预测模型实现"><a href="#3-2-神经网络预测模型实现" class="headerlink" title="3.2 神经网络预测模型实现"></a>3.2 神经网络预测模型实现</h2><p>对于神经网络的模型, 这里使用的是简单的全连接方式, 用三层的网络来进行预测, 损失函数为均方损失, 优化器为 Adam, 使用 gpu 进行计算, 迭代次数为 1000 次, 训练过程的损失变化也记录下来, 然后将预测的房价与实际的房价进行对比, 即可得出预测的效果, 具体代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><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.optim <span class="keyword">as</span> optim</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader, TensorDataset</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">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> mean_squared_error, r2_score</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert data to tensors</span></span><br><span class="line">data = torch.tensor(data, dtype=torch.<span class="built_in">float</span>)</span><br><span class="line">label = torch.tensor(label, dtype=torch.<span class="built_in">float</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Split data into training and test sets</span></span><br><span class="line">X_train, X_test, y_train, y_test = train_test_split(data, label, test_size=<span class="number">0.3</span>, random_state=<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Training function</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">model, device, train_loader, optimizer, epoch, criterion</span>):</span><br><span class="line">    model.train()</span><br><span class="line">    loss = <span class="number">0.0</span></span><br><span class="line">    <span class="keyword">for</span> i, (data, target) <span class="keyword">in</span> <span class="built_in">enumerate</span>(train_loader):</span><br><span class="line">        data, target = data.to(device), target.to(device)</span><br><span class="line">        optimizer.zero_grad()</span><br><span class="line">        output = model(data)</span><br><span class="line">        loss = criterion(output, target.view_as(output))</span><br><span class="line">        loss.backward()</span><br><span class="line">        optimizer.step()</span><br><span class="line">        <span class="keyword">if</span> i % <span class="number">100</span> == <span class="number">0</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;Train Epoch: &#123;&#125; Loss: &#123;:.6f&#125;&#x27;</span>.<span class="built_in">format</span>(epoch, loss.item() / <span class="built_in">len</span>(train_loader)))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Testing function</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test</span>(<span class="params">model, device, test_loader, criterion</span>):</span><br><span class="line">    model.<span class="built_in">eval</span>()</span><br><span class="line">    test_loss = <span class="number">0</span></span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> data, target <span class="keyword">in</span> test_loader:</span><br><span class="line">            data, target = data.to(device), target.to(device)</span><br><span class="line">            output = model(data)</span><br><span class="line">            test_loss += criterion(output, target.view_as(output)).item()  <span class="comment"># Sum up batch loss</span></span><br><span class="line">    test_loss /= <span class="built_in">len</span>(test_loader.dataset)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Test set: Average loss: &#123;:.4f&#125;\n&#x27;</span>.<span class="built_in">format</span>(test_loss))</span><br><span class="line">    <span class="keyword">return</span> test_loss</span><br><span class="line"></span><br><span class="line"><span class="comment"># Neural network model definition</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Net</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">super</span>(Net, self).__init__()</span><br><span class="line">        self.fc1 = nn.Linear(<span class="number">12</span>, <span class="number">128</span>)</span><br><span class="line">        self.fc2 = nn.Linear(<span class="number">128</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        x = self.fc1(x)</span><br><span class="line">        x = F.relu(x)</span><br><span class="line">        x = self.fc2(x)</span><br><span class="line">        <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set device for training</span></span><br><span class="line">device = torch.device(<span class="string">&quot;cuda&quot;</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">&quot;cpu&quot;</span>)</span><br><span class="line">model = Net().to(device)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Optimizer and loss function</span></span><br><span class="line">optimizer = optim.Adam(model.parameters())</span><br><span class="line">criterion = nn.MSELoss()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Prepare data loaders</span></span><br><span class="line">trainset = TensorDataset(X_train, y_train)</span><br><span class="line">trainloader = DataLoader(trainset, batch_size=<span class="number">64</span>, shuffle=<span class="literal">True</span>, num_workers=<span class="number">0</span>)</span><br><span class="line">testset = TensorDataset(X_test, y_test)</span><br><span class="line">testloader = DataLoader(testset, batch_size=<span class="number">64</span>, shuffle=<span class="literal">False</span>, num_workers=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Train and evaluate the model</span></span><br><span class="line">epoch_list, loss_list = [], []</span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">1000</span>):</span><br><span class="line">    train(model, device, trainloader, optimizer, epoch, criterion)</span><br><span class="line">    test_loss = test(model, device, testloader, criterion)</span><br><span class="line">    epoch_list.append(epoch)</span><br><span class="line">    loss_list.append(test_loss)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the loss over epochs</span></span><br><span class="line">fig = plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.plot(epoch_list, loss_list)</span><br><span class="line">plt.xlabel(<span class="string">&#x27;epoch&#x27;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&#x27;loss&#x27;</span>)</span><br><span class="line">plt.title(<span class="string">&#x27;Error&#x27;</span>)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Function to read and compare predicted and actual results</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read</span>(<span class="params">test_loader</span>):</span><br><span class="line">    model.<span class="built_in">eval</span>()</span><br><span class="line">    output_list, target_list = [], []</span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> data, target <span class="keyword">in</span> test_loader:</span><br><span class="line">            model.to(<span class="string">&#x27;cpu&#x27;</span>)</span><br><span class="line">            output = model(data).detach().cpu().numpy()</span><br><span class="line">            output_list.extend(output)</span><br><span class="line">            target_list.extend(target.cpu().numpy())</span><br><span class="line">    p = pd.DataFrame(output_list, columns=[<span class="string">&#x27;predict&#x27;</span>])</span><br><span class="line">    p[<span class="string">&#x27;real&#x27;</span>] = target_list</span><br><span class="line">    <span class="built_in">print</span>(p.head())</span><br><span class="line">    <span class="keyword">return</span> p</span><br><span class="line"></span><br><span class="line"><span class="comment"># Read predictions and calculate error</span></span><br><span class="line">p = read(testloader)</span><br><span class="line">error1 = mean_squared_error(p[<span class="string">&#x27;real&#x27;</span>], p[<span class="string">&#x27;predict&#x27;</span>]).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># Mean squared error</span></span><br><span class="line">score1 = r2_score(p[<span class="string">&#x27;real&#x27;</span>], p[<span class="string">&#x27;predict&#x27;</span>]).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># R^2 score</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot predictions vs actual values</span></span><br><span class="line">plt.rcParams[<span class="string">&#x27;font.family&#x27;</span>] = <span class="string">&quot;sans-serif&quot;</span></span><br><span class="line">plt.rcParams[<span class="string">&#x27;font.sans-serif&#x27;</span>] = <span class="string">&quot;SimHei&quot;</span></span><br><span class="line">plt.rcParams[<span class="string">&#x27;axes.unicode_minus&#x27;</span>] = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">fig1 = plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.plot(<span class="built_in">range</span>(p.shape[<span class="number">0</span>]), p[<span class="string">&#x27;real&#x27;</span>], color=<span class="string">&#x27;red&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;-&#x27;</span>)</span><br><span class="line">plt.plot(<span class="built_in">range</span>(p.shape[<span class="number">0</span>]), p[<span class="string">&#x27;predict&#x27;</span>], color=<span class="string">&#x27;blue&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;dashdot&#x27;</span>)</span><br><span class="line">plt.legend([<span class="string">&#x27;真实值&#x27;</span>, <span class="string">&#x27;预测值&#x27;</span>])</span><br><span class="line">plt.title(<span class="string">&#x27;神经网络预测值与准确率对比图&#x27;</span>)</span><br><span class="line"></span><br><span class="line">error_text = <span class="string">&quot;标准差d=&quot;</span> + <span class="built_in">str</span>(error1) + <span class="string">&quot;\n&quot;</span> + <span class="string">&quot;相关指数R^2=&quot;</span> + <span class="built_in">str</span>(score1)</span><br><span class="line">plt.xlabel(error_text, size=<span class="number">18</span>, color=<span class="string">&quot;green&quot;</span>)</span><br><span class="line">plt.grid()</span><br><span class="line">plt.show()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-3-线性模型"><a href="#3-3-线性模型" class="headerlink" title="3.3 线性模型"></a>3.3 线性模型</h2><p>这里使用了传统的方法线性模型与神经网络的模型进行对比, 从而反映出效果, 这里的线性模型直接 sklearn 中的库函数来实现, 预测出房价后同样与实际进行对比, 并比较结果, 其代码实现较为简单, 代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LinearRegression</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> mean_squared_error, r2_score</span><br><span class="line"></span><br><span class="line"><span class="comment"># Initialize and train the linear regression model</span></span><br><span class="line">lr = LinearRegression()</span><br><span class="line">lr.fit(X_train, y_train)  <span class="comment"># Train the model with training data</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Predict using the test data</span></span><br><span class="line">y_predict = lr.predict(X_test)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Calculate error metrics</span></span><br><span class="line">error2 = mean_squared_error(y_test.numpy(), y_predict).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># Mean squared error</span></span><br><span class="line">score2 = r2_score(y_test, y_predict).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># R-squared score</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the true vs predicted values</span></span><br><span class="line">fig2 = plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.plot(<span class="built_in">range</span>(y_test.shape[<span class="number">0</span>]), y_test, color=<span class="string">&#x27;red&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;-&#x27;</span>)</span><br><span class="line">plt.plot(<span class="built_in">range</span>(y_test.shape[<span class="number">0</span>]), y_predict, color=<span class="string">&#x27;blue&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;dashdot&#x27;</span>)</span><br><span class="line">plt.legend([<span class="string">&#x27;真实值&#x27;</span>, <span class="string">&#x27;预测值&#x27;</span>])</span><br><span class="line">plt.title(<span class="string">&#x27;线性模型预测值与准确率对比图&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Display error information in the plot</span></span><br><span class="line">error_text = <span class="string">&quot;标准差d=&quot;</span> + <span class="built_in">str</span>(error2) + <span class="string">&quot;\n&quot;</span> + <span class="string">&quot;相关指数R^2=&quot;</span> + <span class="built_in">str</span>(score2)</span><br><span class="line">plt.xlabel(error_text, size=<span class="number">18</span>, color=<span class="string">&quot;green&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Display grid and show plot</span></span><br><span class="line">plt.grid()</span><br><span class="line">plt.show()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="4-试验结果"><a href="#4-试验结果" class="headerlink" title="4 试验结果"></a>4 试验结果</h2><h2 id="4-1-实验分析"><a href="#4-1-实验分析" class="headerlink" title="4.1 实验分析"></a>4.1 实验分析</h2><p>实验过程中首先得到的直方图以及箱图, 以及相关系数矩阵的热力图如下图所示: 由不同特征与房价的直方图可以大致观察到不同特征的基本影响。<br>由盒图可以看到不同特征下, 数据的分布情况基本较好, 几乎没有异常值和离群点的<br>存在。<br>由热力图可以大致看出, 第四列和第四行, 即 CHAS 的颜色较深, 这说明其与其他特征的相关性较低, 这里将此特征作为重点排查对象, 此时并不能完全判断该特征是否合适,<br><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_9.jpg?x=192&y=76&w=457&h=320 "/></p><p>图 1: 特征直方图</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_9.jpg?x=187&y=467&w=466&h=238 "/><p>图 2: 盒图</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_9.jpg?x=188&y=768&w=465&h=253 "/><p>图 3: 特征相关性热力图<br>还有看后续的信息。随后为了准确了解每个特征的信息, 这里分布对每个特征与房价的关系进行了可视化, 结果如下:<br>犯罪率: 高房价的房屋大都集中在低犯罪率地区, 有对预测结果有一定的参考价值</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_10.jpg?x=188&y=142&w=461&h=332 "/><p>图 4: 犯罪率相关图</p><p>住宅用地比例: 与房价无明显的线性关系, 有一定相关性, 可以保留。</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_10.jpg?x=187&y=566&w=462&h=333 "/><p>图 5: 住宅用地比例图</p><p>城镇中非商业用地的所占比例: 与房价无明显的线性关系, 只能说在某一区间内房价<br>呈现一定特征, 保留。<br>是否处于查尔斯河边 (1 表示在河边, 0 表示不在河边): 是否在查尔斯河边影响房价也<br>不明显, 因此考虑把此特征去除, 防止无关特征影响效果。<br>一氧化氮浓度: 一氧化氮浓度与房价的关系呈现极其微弱的线性关系, 一氧化氮低于<br><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_11.jpg?x=186&y=126&w=464&h=333 "/></p><p>图 6: 城镇非商业用地比例图</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_11.jpg?x=186&y=635&w=464&h=334 "/><p>图 7: 是否处于查尔斯河边图<br>0.5 的情况下, 房价绝大部分高于 15 。</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_12.jpg?x=187&y=95&w=462&h=332 "/><p>图 8: 一氧化氮浓度相关图</p><p>每栋住宅的房间数: 与房价之间具有较强的线性关系, 保留。</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_12.jpg?x=186&y=522&w=462&h=331 "/><p>图 9: 每栋住宅房间数图</p><p>1940 年以前建成的业主自住单位的占比: 对房价的影响较小, 但也可保留。<br>距离 5 个波士顿就业中心的平均距离: 平均距离较小的情况下, 房价对应也较低。距离高速公路的便利指数: 房价高于 30 的房产, 近乎都集中在距离高速公路的便利指<br>数低的地区, 有一定的相关性, 可以保留。<br>每一万美元的不动产税率: 与房价的线性相关度较小, 也可保留。城镇中学生教师比例: 对房价的影响较小, 呈微弱的线性关系。<br>黑人比例: 黑人比例对波士顿房价的影响尤其是往后的影响越趋于更小, 对结果预测<br><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_13.jpg?x=186&y=126&w=464&h=334 "/></p><p>图 10: 1940 年前建成业主自住比例图</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_13.jpg?x=186&y=635&w=463&h=333 "/><p>图 11: 距离 5 个波士顿就业中心的距离图<br><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_14.jpg?x=186&y=126&w=463&h=333 "/></p><p>图 12: 距离高速公路的便利指数图</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_14.jpg?x=187&y=635&w=463&h=332 "/><p>图 13: 每一万美元的不动产税率图<br><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_15.jpg?x=185&y=63&w=466&h=333 "/></p><p>图 14: 城镇中学教师比例图</p><p>有一定的影响。</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_15.jpg?x=187&y=493&w=463&h=333 "/><p>图 15: 黑人比例图</p><p>低收入阶层占比: 与房价具有较强的线性关系, 是影响房价的重要因素。<br>从以上对每个特征的分析, 我们可以得出, CHAS 特征与房价的关系非常小, 可以忽<br>略掉, 因此这里的特征仅去掉这一项。</p><h2 id="4-2-对比"><a href="#4-2-对比" class="headerlink" title="4.2 对比"></a>4.2 对比</h2><p>在数据处理完成后, 就是两个模型对于结果的预测。对于神经网络的模型, 其迭代过<br>程的损失变化如下图所示<br><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_16.jpg?x=186&y=126&w=464&h=334 "/></p><p>图 16: 低收入阶层占比图</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_16.jpg?x=189&y=640&w=459&h=324 "/><p>图 17: 迭代过程损失变化图<br>由图中可以看出, 损失在初期迅速下降, 随后缓慢下降, 最后达到了一个很低的水平,<br>说明模型在训练过程中性能越来越好。<br>随后是神经网络模型的预测值与实际值进行的对比图:</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_17.jpg?x=187&y=165&w=469&h=258 "/><p>图 18: 神经网络预测值与准确率对比图</p><p>由图中可以看出, 绝大部分情况预测的准确度还是很高的, 在房价的峰值与谷值这里<br>出现了一定的误差。<br>线性模型的预测值和实际值对比图如下:</p><img src="https://cdn.noedgeai.com/373f4192-7bc0-4d1c-9d14-e9c31dcc7467_17.jpg?x=187&y=591&w=468&h=258 "/><p>图 19: 线性模型预测值与准确率对比图</p><p>由图中可以看出, 预测效果也还不错, 但是预测的误差相对于神经网络还是高一些, 数据也反映出了这一点, 神经网络的标准差是 19.86254, 线性模型的标准差是 28.35625, 这说明了神经网络预测的的误差小,也更加稳定: 神经网络的相关系数 (R^{2}) 为 0.80978,线性模型的相关系数数 (R^{2}) 为 0.72844,神经网络的相关性要比线性模型的更好,与实际更加符合。</p><h2 id="5-结论"><a href="#5-结论" class="headerlink" title="5 结论"></a>5 结论</h2><p>线性模型和神经网络模型都能够较好的对房价进行预测, 神经网络预测的准确性相对<br>较好。</p><h2 id="6-总结"><a href="#6-总结" class="headerlink" title="6 总结"></a>6 总结</h2><p>由于期末时间问题, 课程复习相对较紧张, 而且还有很多事, 机器学习的大作业这里选取的是经典的题目来做, 并未进行创新性质的探索。但是在这次大作业中我也有很多收获, 代码能力得到了提升, 熟练度增加, 也提升了自己的综合能力。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] 欧阳光. 正交回归最小二乘估计 [J]. 湘南学院学报,2021,42(2):1-5. DOI:10.3969&#x2F;j.issn.1672-8173.2021.02.001.</p><h2 id="A-程序代码"><a href="#A-程序代码" class="headerlink" title="A 程序代码"></a>A 程序代码</h2><p>房价预测模型 - price_predict.py </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># coding: utf-8</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sklearn</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> metrics</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><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> 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> TensorDataset</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> seaborn <span class="keyword">as</span> sns</span><br><span class="line"><span class="keyword">from</span> skimage.metrics <span class="keyword">import</span> mean_squared_error</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> r2_score</span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LinearRegression</span><br><span class="line"></span><br><span class="line">column_names = [<span class="string">&#x27;CRIM&#x27;</span>, <span class="string">&#x27;ZN&#x27;</span>, <span class="string">&#x27;INDUS&#x27;</span>, <span class="string">&#x27;CHAS&#x27;</span>, <span class="string">&#x27;NOX&#x27;</span>, <span class="string">&#x27;RM&#x27;</span>, <span class="string">&#x27;AGE&#x27;</span>, <span class="string">&#x27;DIS&#x27;</span>, <span class="string">&#x27;RAD&#x27;</span>, <span class="string">&#x27;TAX&#x27;</span>, <span class="string">&#x27;PTRATIO&#x27;</span>, <span class="string">&#x27;B&#x27;</span>, <span class="string">&#x27;LSTAT&#x27;</span>,</span><br><span class="line">                <span class="string">&#x27;PRICE&#x27;</span>]</span><br><span class="line">all_data = pd.read_csv(<span class="string">&#x27;./housing.csv&#x27;</span>, header=<span class="literal">None</span>, delimiter=<span class="string">r&quot;\s+&quot;</span>, names=column_names)</span><br><span class="line"></span><br><span class="line">all_data.hist()</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line">all_data.describe()</span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.boxplot(all_data)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line">corr = all_data.corr()</span><br><span class="line"></span><br><span class="line">plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">sns.heatmap(corr, annot=<span class="literal">True</span>, cmap=<span class="string">&#x27;twilight_r&#x27;</span>)</span><br><span class="line"></span><br><span class="line">data = all_data.iloc[:, :-<span class="number">1</span>]</span><br><span class="line">label = all_data.iloc[:, -<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line">data = np.array(data, dtype=<span class="built_in">float</span>)</span><br><span class="line">label = np.array(label, dtype=<span class="built_in">float</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">13</span>):</span><br><span class="line">    plt.figure(figsize=(<span class="number">10</span>, <span class="number">7</span>))</span><br><span class="line">    plt.grid()</span><br><span class="line">    plt.scatter(data[:, i], label, s=<span class="number">5</span>)  <span class="comment"># 横纵坐标和点的大小</span></span><br><span class="line">    plt.title(column_names[i])</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line">unsF = []  <span class="comment"># 次要特征下标</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(data.shape[<span class="number">1</span>]):</span><br><span class="line">    <span class="keyword">if</span> column_names[i] == <span class="string">&#x27;CHAS&#x27;</span>:</span><br><span class="line">        unsF.append(i)</span><br><span class="line">data = np.delete(data, unsF, axis=<span class="number">1</span>)  <span class="comment"># 删除次要特征</span></span><br><span class="line"></span><br><span class="line">unsT = []  <span class="comment"># 房价异常值下标</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(data.shape[<span class="number">1</span>]):</span><br><span class="line">    <span class="keyword">if</span> label[i] &gt; <span class="number">46</span>:</span><br><span class="line">        unsT.append(i)</span><br><span class="line">data = np.delete(data, unsT, axis=<span class="number">0</span>)  <span class="comment"># 删除样本异常值数据</span></span><br><span class="line">label = np.delete(label, unsT, axis=<span class="number">0</span>)  <span class="comment"># 删除异常房价</span></span><br><span class="line"></span><br><span class="line">data = torch.tensor(data, dtype=torch.<span class="built_in">float</span>)</span><br><span class="line">label = torch.tensor(label, dtype=torch.<span class="built_in">float</span>)</span><br><span class="line"></span><br><span class="line">X_train, X_test, y_train, y_test = train_test_split(data, label, test_size=<span class="number">0.3</span>, random_state=<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">model, device, train_loader, optimizer, epoch, criterion</span>):</span><br><span class="line">    model.train()</span><br><span class="line">    loss = <span class="number">0.0</span></span><br><span class="line">    <span class="keyword">for</span> i, (data, target) <span class="keyword">in</span> <span class="built_in">enumerate</span>(train_loader):</span><br><span class="line">        data, target = data.to(device), target.to(device)</span><br><span class="line">        optimizer.zero_grad()</span><br><span class="line">        output = model(data)</span><br><span class="line">        loss = criterion(output, target.view_as(output))</span><br><span class="line">        loss.backward()</span><br><span class="line">        optimizer.step()</span><br><span class="line">        <span class="keyword">if</span> i % <span class="number">100</span> == <span class="number">0</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;Train Epoch: &#123;&#125; Loss: &#123;:.6f&#125;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">                epoch, loss.item() / <span class="built_in">len</span>(train_loader)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test</span>(<span class="params">model, device, test_loader, criterion</span>):</span><br><span class="line">    model.<span class="built_in">eval</span>()</span><br><span class="line">    test_loss = <span class="number">0</span></span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> data, target <span class="keyword">in</span> test_loader:</span><br><span class="line">            data, target = data.to(device), target.to(device)</span><br><span class="line">            output = model(data)</span><br><span class="line"></span><br><span class="line">            test_loss += criterion(output, target.view_as(output)).item()  <span class="comment"># sum up batch loss</span></span><br><span class="line"></span><br><span class="line">    test_loss /= <span class="built_in">len</span>(test_loader.dataset)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Test set: Average loss: &#123;:.4f&#125;\n&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">        test_loss))</span><br><span class="line">    <span class="keyword">return</span> test_loss</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Net</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">super</span>(Net, self).__init__()</span><br><span class="line">        self.fc1 = nn.Linear(<span class="number">12</span>, <span class="number">128</span>)</span><br><span class="line">        self.fc2 = nn.Linear(<span class="number">128</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        x = self.fc1(x)</span><br><span class="line">        x = F.relu(x)</span><br><span class="line">        x = self.fc2(x)</span><br><span class="line">        <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">device = torch.device(<span class="string">&quot;cuda&quot;</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">&quot;cpu&quot;</span>)</span><br><span class="line">model = Net().to(device)</span><br><span class="line">optimizer = torch.optim.Adam(params=model.parameters())</span><br><span class="line">criterion = nn.MSELoss()</span><br><span class="line"></span><br><span class="line">trainset = TensorDataset(X_train, y_train)</span><br><span class="line">trainloader = DataLoader(trainset, batch_size=<span class="number">64</span>, shuffle=<span class="literal">True</span>, num_workers=<span class="number">0</span>)</span><br><span class="line">testset = TensorDataset(X_test, y_test)</span><br><span class="line">testloader = DataLoader(testset, batch_size=<span class="number">64</span>, shuffle=<span class="literal">False</span>, num_workers=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">epoch_list, loss_list = [], []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">1000</span>):</span><br><span class="line">    train(model, device, trainloader, optimizer, epoch, criterion)</span><br><span class="line">    test_loss = test(model, device, testloader, criterion)</span><br><span class="line">    epoch_list.append(epoch)</span><br><span class="line">    loss_list.append(test_loss)</span><br><span class="line"></span><br><span class="line">fig = plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.plot(epoch_list, loss_list)</span><br><span class="line">plt.xlabel(<span class="string">&#x27;epoch&#x27;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&#x27;loss&#x27;</span>)</span><br><span class="line">plt.title(<span class="string">&#x27;error&#x27;</span>)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read</span>(<span class="params">test_loader</span>):</span><br><span class="line">    model.<span class="built_in">eval</span>()</span><br><span class="line">    output_list, target_list = [], []</span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> data, target <span class="keyword">in</span> test_loader:</span><br><span class="line">            model.to(<span class="string">&#x27;cpu&#x27;</span>)</span><br><span class="line">            output = model(data).detach().cpu().numpy()</span><br><span class="line">            output_list.extend(output)</span><br><span class="line">            target_list.extend(target.cpu().numpy())</span><br><span class="line">    p = pd.DataFrame(output_list, columns=[<span class="string">&#x27;predict&#x27;</span>])</span><br><span class="line">    p[<span class="string">&#x27;real&#x27;</span>] = target_list</span><br><span class="line">    <span class="built_in">print</span>(p.head())</span><br><span class="line">    <span class="keyword">return</span> p</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">p = read(testloader)</span><br><span class="line"></span><br><span class="line">error1 = mean_squared_error(p.iloc[:, <span class="number">1</span>], p.iloc[:, <span class="number">0</span>]).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># 平方差</span></span><br><span class="line">score1 = r2_score(p.iloc[:, <span class="number">1</span>], p.iloc[:, <span class="number">0</span>]).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># 相关系数</span></span><br><span class="line"></span><br><span class="line">plt.rcParams[<span class="string">&#x27;font.family&#x27;</span>] = <span class="string">&quot;sans-serif&quot;</span></span><br><span class="line">plt.rcParams[<span class="string">&#x27;font.sans-serif&#x27;</span>] = <span class="string">&quot;SimHei&quot;</span></span><br><span class="line">plt.rcParams[<span class="string">&#x27;axes.unicode_minus&#x27;</span>] = <span class="literal">False</span></span><br><span class="line">fig1 = plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.plot(<span class="built_in">range</span>(p.shape[<span class="number">0</span>]), p.iloc[:, <span class="number">1</span>], color=<span class="string">&#x27;red&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;-&#x27;</span>)</span><br><span class="line">plt.plot(<span class="built_in">range</span>(p.shape[<span class="number">0</span>]), p.iloc[:, <span class="number">0</span>], color=<span class="string">&#x27;blue&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;dashdot&#x27;</span>)</span><br><span class="line">plt.legend([<span class="string">&#x27;真实值&#x27;</span>, <span class="string">&#x27;预测值&#x27;</span>])</span><br><span class="line">plt.title(<span class="string">&#x27;神经网络预测值与准确率对比图&#x27;</span>)</span><br><span class="line">error1 = <span class="string">&quot;标准差d=&quot;</span> + <span class="built_in">str</span>(error1) + <span class="string">&quot;\n&quot;</span> + <span class="string">&quot;相关指数R^2=&quot;</span> + <span class="built_in">str</span>(score1)</span><br><span class="line">plt.xlabel(error1, size=<span class="number">18</span>, color=<span class="string">&quot;green&quot;</span>)</span><br><span class="line">plt.grid()</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line">lf = LinearRegression()</span><br><span class="line">lf.fit(X_train, y_train)  <span class="comment"># 训练数据,学习模型参数</span></span><br><span class="line">y_predict = lf.predict(X_test)</span><br><span class="line"></span><br><span class="line">error2 = mean_squared_error(y_test.numpy(), y_predict).<span class="built_in">round</span>(<span class="number">5</span>)  <span class="comment"># 平方差</span></span><br><span class="line">score2 = r2_score(y_test, y_predict).<span class="built_in">round</span>(<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">fig2 = plt.figure(figsize=(<span class="number">20</span>, <span class="number">10</span>))</span><br><span class="line">plt.plot(<span class="built_in">range</span>(y_test.shape[<span class="number">0</span>]), y_test, color=<span class="string">&#x27;red&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;-&#x27;</span>)</span><br><span class="line">plt.plot(<span class="built_in">range</span>(y_test.shape[<span class="number">0</span>]), y_predict, color=<span class="string">&#x27;blue&#x27;</span>, linewidth=<span class="number">1</span>, linestyle=<span class="string">&#x27;dashdot&#x27;</span>)</span><br><span class="line">plt.legend([<span class="string">&#x27;真实值&#x27;</span>, <span class="string">&#x27;预测值&#x27;</span>])</span><br><span class="line">plt.title(<span class="string">&#x27;线性模型预测值与准确率对比图&#x27;</span>)</span><br><span class="line">error2 = <span class="string">&quot;标准差d=&quot;</span> + <span class="built_in">str</span>(error2) + <span class="string">&quot;\n&quot;</span> + <span class="string">&quot;相关指数R^2=&quot;</span> + <span class="built_in">str</span>(score2)</span><br><span class="line">plt.xlabel(error2, size=<span class="number">18</span>, color=<span class="string">&quot;green&quot;</span>)</span><br><span class="line">plt.grid()</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/06/18/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E8%AF%BE%E7%A8%8B%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E9%A2%84%E6%B5%8B/</id>
    <link href="http://jackzhu.top/2021/06/18/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E8%AF%BE%E7%A8%8B%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E6%B3%A2%E5%A3%AB%E9%A1%BF%E6%88%BF%E4%BB%B7%E9%A2%84%E6%B5%8B/"/>
    <published>2021-06-18T12:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘-要"><a href="#摘-要" class="headerlink" title="摘 要"></a>摘 要</h2><p>在这次大作业中我完成了波士顿房价预测模型的实现, 波士顿房价预测是一个经典的回归模型。<br>在本次实验中, 首先对数据的分布情况以]]>
    </summary>
    <title>机器学习课程报告——波士顿房价预测</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="计算智能" scheme="http://jackzhu.top/categories/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD/"/>
    <category term="计算智能" scheme="http://jackzhu.top/tags/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD/"/>
    <content>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景知识 1<br>1.1 基本定义 1<br>1.2 感知器的损失函数 1<br>1.3 感知器的训练 1<br>2 数据集 2<br>3 代码实现 2<br>4 结果展示与分析 4<br>4.1 数据集一 4<br>4.2 数据集二 5<br>4.3 数据集三 5<br>5 探究 6<br>6 总结 7<br>A 程序代码 8</p><h2 id="1-背景知识"><a href="#1-背景知识" class="headerlink" title="1 背景知识"></a>1 背景知识</h2><h2 id="1-1-基本定义"><a href="#1-1-基本定义" class="headerlink" title="1.1 基本定义"></a>1.1 基本定义</h2><p>感知器可以实现简单的布尔运算, 可以拟合任何的线性函数, 任何线性分类或线性问题都可以用感知器来解决, 布尔运算就可以看作是一个二分类问题, 用一条直线将两类分开。感知器无法实现异或运算, 因为异或运算不是线性的, 无法用一条直线将两类分开。<br>与逻辑斯蒂回归从概率的角度判别不同, 感知机可以理解为从几何的角度上做判断, 即<br>求得一个分离超平面, 可以将对应输入空间中的实例划分为正负两类。<br>一个感知器有如下组成部分:</p><ul><li><p>输入权值: 一个感知器可以接收多个输入 $(x_{1},x_{2},\ldots,x_{n} | x_{i} \in \mathbb{R})$, 每个输入上有一个权值 $\omega_{i} \in \mathbb{R}$, 此外还有一个偏置项 $b \in \mathbb{R}$, 就是上图中的 $\omega_{0}$。</p></li><li><p>激活函数: 感知器的激活函数可以有很多选择, 比如我们可以选择下面这个阶跃函数 $f$ 来作为激活函数:<br>$$<br>f(z) &#x3D; \begin{cases}<br>1 &amp; \text{if } z &gt; 0 \<br>0 &amp; \text{otherwise}<br>\end{cases}<br>$$</p></li><li><p>输出: 感知器的输出由下面这个公式来计算<br>$$<br>y &#x3D; f(\mathbf{w} \cdot \mathbf{x} + b)<br>$$</p></li></ul><h2 id="1-2-感知器的损失函数"><a href="#1-2-感知器的损失函数" class="headerlink" title="1.2 感知器的损失函数"></a>1.2 感知器的损失函数</h2><p>为了求得感知器的权重参数, 需要确定一个学习策略, 即定义损失函数并将损失函数<br>极小化。有这样几种选择:</p><ol><li><p>误分类点的总数: 损失函数不是 $w,b$ 的连续可导函数, 不易优化。</p></li><li><p>误分类点到超平面的总距离: 感知器所采用的损失函数。感知器的损失函数是:</p></li></ol>$${-} \frac{1}{{\| w \|}} \sum_{x_{i} \in M} y_{i}(w x_{i} + b)$$<p>感知器学习问题转化为上式损失函数的最优化问题, 最优化的方法是随机梯度下降法。<br>当训练数据集线性可分时, 感知器学习算法原始形式是收玫的。</p><h2 id="1-3-感知器的训练"><a href="#1-3-感知器的训练" class="headerlink" title="1.3 感知器的训练"></a>1.3 感知器的训练</h2><p>感知器的权值和偏置项是利用感知器训练算法得出的, 首先将权重项和偏置项初始化为 0, 然后, 利用下面的感知器规则迭代地修改 $w_{i}$ 和 $b$, 直到训练完成。</p><p>$$<br>w_{i} \leftarrow w_{i} + \Delta w_{i}<br>$$</p><p>$$<br>b \leftarrow b + \Delta b<br>$$</p><p>其中:</p><p>$$<br>\Delta w_{i} &#x3D; \eta(t - y)x_{i}<br>$$</p><p>$$<br>\Delta b &#x3D; \eta(t - y)<br>$$</p><p>$w_{i}$ 是与输入对应的权重项, $b$ 是偏置项。事实上, 可以把 $b$ 看作是值永远为 1 的输入所对应的权重。$t$ 是训练样本的实际值，一般称之为 label。而 $y$ 是感知器的输出值, 它是根据上面的公式计算得出的。$\eta$ 是一个称为学习速率的常数, 其作用是控制每一步调整权的幅度。</p><p>每次从训练数据中取出一个样本的输入向量 $x$, 使用感知器计算其输出 $y$, 再根据上面的规则来调整权重。每处理一个样本就调整一次权重。经过多轮迭代后（即全部的训练数据被反复处理多轮），就可以训练出感知器的权重，使之实现目标函数。</p><h2 id="2-数据集"><a href="#2-数据集" class="headerlink" title="2 数据集"></a>2 数据集</h2><p>在本次实验中, 选择了三组数据, 第一组是自己生成的 200 个二维的数据, 一共两组, 每组 100 个数据,一组是以 (({-}2, {-} 2)) 为均值,标准差为 1.5 的数据,另一组是以 ((2,2)) 为均值, 标准差为 1.5 的数据, 这两类数据中有一点交叉, 使得数据不能完全线性可分。<br>第二组数据是读取的自定义的数据, 一共两类, 这两类数据之间有一定的间隔, 是完全<br>线性可分的。<br>第三组数据是著名的数据集 Sonar, Sonar 数据集是一个声纳信号分类数据集, 声纳信号从一个金属圆柱体上反弹, 或者从一个大致呈圆柱形的岩石上反弹。每个样本是一个 60 维向量。每个数字表示特定频带内的能量, 范围从 0 到 1 。如果是从一块岩石上反弹, 则样本标签为 “R”, 如果是从一个金属圆柱体上反弹则为 “M”。</p><h2 id="3-代码实现"><a href="#3-代码实现" class="headerlink" title="3 代码实现"></a>3 代码实现</h2><p>在代码实现中, 这里首先创建了一个感知器类, 首先根据输入数据的维度初始化权重向量以及偏置的值。然后定义类的内置函数, 包括计算以及权值更新函数, 利用权值以及偏置的更新公式进行更新。随后这里定义了一个作图函数, 将数据展示出, 并画出分界面。随后是分别对三个数据集进行读取, 然后定义激活函数为阶跃函数。最后是主函数, 完成感知器的全过程, 并计算出分类后的准确率。具体代码如下:<br>感知器类的定义函数:</p><hr><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Perceptron</span>():</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">init__</span>(<span class="params">self,input_num,f</span>):</span><br><span class="line">self.input_num=input_num</span><br><span class="line">self.weights=np.ones(input_num)</span><br><span class="line">self.bias=<span class="number">2.0</span></span><br><span class="line">self.activation=f</span><br><span class="line">def_str__(self):</span><br><span class="line"><span class="keyword">return</span> <span class="string">f&#x27;weight=<span class="subst">&#123;self.weights&#125;</span>,bias=<span class="subst">&#123;self.bias&#125;</span>&#x27;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">predict</span>(<span class="params">self,inputs</span>):</span><br><span class="line"><span class="keyword">return</span> self.activation(np.dot(inputs, self.weights)+self.bias)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">self,inputs,labels,rate=<span class="number">0.1</span></span>):</span><br><span class="line"><span class="keyword">for</span> \(j\) <span class="keyword">in</span> <span class="built_in">range</span> (inputs. shape [<span class="number">0</span>]):</span><br><span class="line">output=self.predict(inputs[j])</span><br><span class="line">self.weights=self.weights+rate*(labels[j]-output)*inputs[j]</span><br><span class="line">self.bias=self.bias+rate*(labels[j]-output)</span><br></pre></td></tr></table></figure><p>前两个数据集的作图函数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">plot_result</span>(<span class="params">perceptron, result, data</span>):</span><br><span class="line">    knew = -perceptron.weights[<span class="number">0</span>] / perceptron.weights[<span class="number">1</span>]</span><br><span class="line">    bnew = -perceptron.bias / perceptron.weights[<span class="number">1</span>]</span><br><span class="line">    x = np.linspace(-<span class="number">5</span>, <span class="number">5</span>)</span><br><span class="line">    y = <span class="keyword">lambda</span> x: knew * x + bnew</span><br><span class="line"></span><br><span class="line">    plt.xlim(-<span class="number">8</span>, <span class="number">8</span>)</span><br><span class="line">    plt.ylim(-<span class="number">8</span>, <span class="number">8</span>)</span><br><span class="line">    plt.plot(x, y(x), <span class="string">&#x27;b--&#x27;</span>)</span><br><span class="line">    plt.scatter(data[:, <span class="number">0</span>], data[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;Binary Classification&#x27;</span>)</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>对高维度数据集的作图函数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">imshow</span>(<span class="params">data, result</span>):</span><br><span class="line">    tsne = TSNE(n_components=<span class="number">2</span>, learning_rate=<span class="number">100</span>).fit_transform(data)</span><br><span class="line">    pca = PCA().fit_transform(data)</span><br><span class="line"></span><br><span class="line">    plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># t-SNE plot</span></span><br><span class="line">    plt.subplot(<span class="number">121</span>)</span><br><span class="line">    plt.scatter(tsne[:, <span class="number">0</span>], tsne[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;t-SNE&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># PCA plot</span></span><br><span class="line">    plt.subplot(<span class="number">122</span>)</span><br><span class="line">    plt.scatter(pca[:, <span class="number">0</span>], pca[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;PCA&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    plt.colorbar()</span><br><span class="line">    plt.show()</span><br></pre></td></tr></table></figure><p>对于高维度数据, 主函数中代码适当进行了改动, 只显示了最终的结果图。主函数部分代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    epochs = <span class="number">1000</span></span><br><span class="line">    data, target = get_data()</span><br><span class="line">    </span><br><span class="line">    perceptron = Perceptron(data.shape[<span class="number">1</span>], f)</span><br><span class="line">    <span class="built_in">print</span>(perceptron)</span><br><span class="line">    </span><br><span class="line">    plot_result(perceptron, target, data)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epochs):</span><br><span class="line">        perceptron.train(data, target)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> i % <span class="number">400</span> == <span class="number">0</span>:</span><br><span class="line">            plot_result(perceptron, target, data)</span><br><span class="line">            <span class="built_in">print</span>(perceptron)</span><br><span class="line">    </span><br><span class="line">    acc = <span class="number">0</span></span><br><span class="line">    result = np.zeros_like(target)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(data.shape[<span class="number">0</span>]):</span><br><span class="line">        result[i] = perceptron.predict(data[i])</span><br><span class="line">        acc += (result[i] == target[i])</span><br><span class="line">    </span><br><span class="line">    plot_result(perceptron, target, data)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;acc=&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(acc / <span class="built_in">len</span>(target)))</span><br></pre></td></tr></table></figure><h2 id="4-结果展示与分析"><a href="#4-结果展示与分析" class="headerlink" title="4 结果展示与分析"></a>4 结果展示与分析</h2><h2 id="4-1-数据集一"><a href="#4-1-数据集一" class="headerlink" title="4.1 数据集一"></a>4.1 数据集一</h2><p>首先使用第一个数据集进行展示, 在展示中, 为了更好的体现迭代过程中线性分界面的变化, 这里每隔 400 次展示一次结果, 效果如下图所示, 第一张为初始图, 分解面为初始化的值。</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_5.jpg?x=152&y=415&w=246&h=196 "/><p>图 1: 初始</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_5.jpg?x=437&y=414&w=248&h=196 "/><p>图 2: 迭代 400 次</p><p>可以看到迭代中分界面一直在进行变化来使得全部样本被正确划分, 但本数据集是线<br>性不可分的, 无法找到一条直线可以将所有样本都能正确被划分。</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_5.jpg?x=438&y=744&w=247&h=195 "/><p>图 4: 最终结果</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_5.jpg?x=153&y=744&w=243&h=196 "/><p>图 3: 迭代 800 次</p><p>经过了 1000 次的迭代后, 可以看到分界面几乎可以将绝大部分样本正确划分, 说明感知器模型较好的完成了线性分类的任务, 计算得到分类准确率为 0.965 , 印证了从图像中得到的结果。<br>在迭代的过程中,初始权值为 $[1, 1]$ ,偏置为 2,经过 1000 次迭代后,weight $&#x3D; [-0.10703409, 0.05266879]$, bias $&#x3D; 0.09999999999999931$。</p><h2 id="4-2-数据集二"><a href="#4-2-数据集二" class="headerlink" title="4.2 数据集二"></a>4.2 数据集二</h2><p>与数据集一不同, 该数据集是线性可分的, 其他初始化参数部分基本与数据集一设置<br>相同。</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_6.jpg?x=430&y=227&w=256&h=197 "/><p>图 6: 迭代 400 次</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_6.jpg?x=142&y=228&w=257&h=194 "/><p>图 5: 初始</p><p>可以看到, 经过 400 次迭代后, 数据集已经可以完全正确的划分。</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_6.jpg?x=142&y=531&w=256&h=197 "/><p>图 7: 迭代 800 次</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_6.jpg?x=430&y=531&w=254&h=196 "/><p>图 8: 最终结果</p><p>在迭代 400 次时，就已经实现了正确的分类，因此可以看到后来的迭代中，线性分界面并未发生改变，因为在参数更新中，无错误分类的样本，因此权重并未被更新。<br>最后可以得到分类的准确率为 1.0，效果很好。初始化时，参数为 weight $&#x3D; [1.1]$，bias $&#x3D; 2.0$，经过迭代后，weight $&#x3D; [-0.09547401, 0.96129867]$，bias $&#x3D; -0.500000000000000000$。<br>在这里，我们可以发现一个现象，线性分界面在完全将数据划分开后就不再改变，因此分界面相对来说是有些“偏”的，而不是像支持向量机那样，达到距离最小化的效果。这也反映了感知器的缺陷，只是完成简单的分类，并未考虑到样本整体的情况，这样对于未知样本，效果就会相对差一些。</p><h2 id="4-3-数据集三"><a href="#4-3-数据集三" class="headerlink" title="4.3 数据集三"></a>4.3 数据集三</h2><p>为了更好的展示迭代过程中准确率以及损失的变化, 确定足够迭代次数得到较好的结<br>果, 这里对这两项指标进行了记录, 结果如下图所示:<br><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_7.jpg?x=183&y=79&w=473&h=203 "/></p><p>图 9: 损失准确率结果图</p><p>从上图可以看出, 随着迭代次数的增加, 损失不断减小, 因为是负数, 所以图中是不断<br>上升,一定次数后达到稳定,准确率不断上升,并达到约 (80%) 。<br>与前两个数据集不同, 第三个数据集维度较高, 无法用之前的方法进行可视化, 因此这里使用了两种方法 t-SNE 降维以及 PCA 降维, 从而对结果进行可视化, 结果如下图所示:</p><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_7.jpg?x=178&y=475&w=474&h=254 "/><p>图 10: 降维结果图</p><p>降维效果不理想, 因此下文对其进行了探究。<br>在实验中发现, 由于数据维度过高, 1000 次迭代后, 数据仍未达到稳定的状态, 因此这里设置迭代次数为 50000 次, 由于数据维度过高, 不方便展示, 这里不再进行权重展示, 结果的准确率为 0.7836538461538461 , 也得到了较好的效果。</p><h2 id="5-探究"><a href="#5-探究" class="headerlink" title="5 探究"></a>5 探究</h2><p>降维效果并不理想, 这里存在疑问, 分类准确率较高的情况下, 降维效果不理想, 因此<br>这里又对 Sonar 数据集使用 K-means 聚类后降维展示, 结果如下:<br>从图中可以看出, 降维效果较好, 因此并不是数据集的问题。由于 kmeans 对此高维数据处理后, 降维效果很好, 可以说明并不是因为从维度高降到二维特征减少太多而无法表现良好的效果, 这里推测应该是对于感知器的分类结果并不能很好的降维<br><img src="https://cdn.noedgeai.com/e61111d4-9579-4d1a-b6be-0d3a5e039cb8_8.jpg?x=180&y=83&w=473&h=255 "/></p><p>图 11: Sonar 使用 kmeans 降维结果图</p><h2 id="6-总结"><a href="#6-总结" class="headerlink" title="6 总结"></a>6 总结</h2><p>在本次实验中遇到了一些问题, 在此感谢尚荣华老师以及博士生的指导, 在感知器分<br>类的时候遇到了两个问题。<br>第一,对于数据高维度的 Sonar 数据集,为了可视化效果,这里使用了 (\mathrm{t} {-} \mathrm{SNE}) 以及 PCA 降维可视化, 但分类准确率高的同时, 降维效果并不好, 这里又对 Sonar 数据使用了聚类方法, 随后降维可视化, 其效果较好, 也对问题原因进行了推测。<br>第二, 对于高维数据的损失记录, 开始的记录为损失先迅速降低, 随后慢慢升高, 经过发现后发现记录损失的方式不对, 开始使用了每个样本标签与输出的插值的绝对值作为损失, 并求和, 这样是一中类似于均方误差的计算方式, 在高维超平面移动的过程, 这样的方式计算的并不是实际的损失, 因此查阅资料后使用了正确的损失函数计算, 结果较好。<br>本次实验中对感知器进行了实现, 并利用多种数据集进行展示, 得到了很好的效果, 在这次实验中, 开始在理解题意方面遇到了很多问题, 后来经过多方询问才明白。这次实验中我通过广泛查询资料了解到了相关的知识, 也认真写代码来完成任务, 这份作业的完成确实比较艰巨, 一份顶多份, 但是我还是有很大的收获, 能力也得到了提升。</p><h2 id="A-程序代码"><a href="#A-程序代码" class="headerlink" title="A 程序代码"></a>A 程序代码</h2><p>感知器实现程序 - 感知器.py </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author : JackZhu</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Data : 2021/5/20 18:21</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> scipy.io <span class="keyword">as</span> scio</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Perceptron</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,input_num,f</span>):</span><br><span class="line">        self.input_num=input_num</span><br><span class="line">        self.weights=np.ones(input_num)</span><br><span class="line">        self.bias=<span class="number">2.0</span></span><br><span class="line">        self.activation=f</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&#x27;weight=<span class="subst">&#123;self.weights&#125;</span>,bias=<span class="subst">&#123;self.bias&#125;</span>&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">predict</span>(<span class="params">self,inputs</span>):</span><br><span class="line">        <span class="keyword">return</span> self.activation(np.dot(inputs,self.weights)+self.bias)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">self,inputs,labels,rate=<span class="number">0.1</span></span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(inputs.shape[<span class="number">0</span>]):</span><br><span class="line">            output=self.predict(inputs[j])</span><br><span class="line">            self.weights=self.weights+rate*(labels[j]-output)*inputs[j]</span><br><span class="line">            self.bias=self.bias+rate*(labels[j]-output)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">if</span> x&gt;<span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># def get_data():</span></span><br><span class="line"><span class="comment">#     path = &#x27;./sonar.csv&#x27;</span></span><br><span class="line"><span class="comment">#     file = pd.read_csv(path, header=None)</span></span><br><span class="line"><span class="comment">#     data = file.iloc[:, :-1]</span></span><br><span class="line"><span class="comment">#     target = file.iloc[:, -1]</span></span><br><span class="line"><span class="comment">#     target = pd.get_dummies(target).iloc[:, -1]</span></span><br><span class="line"><span class="comment">#     data = np.array(data)</span></span><br><span class="line"><span class="comment">#     target = np.array(target)</span></span><br><span class="line"><span class="comment">#     return data,target</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_data</span>():</span><br><span class="line">    data=torch.ones(<span class="number">100</span>,<span class="number">2</span>)</span><br><span class="line">    x0=torch.normal(<span class="number">2</span>*data,<span class="number">1.5</span>)</span><br><span class="line">    x1=torch.normal(-<span class="number">2</span>*data,<span class="number">1.5</span>)</span><br><span class="line">    x=torch.cat((x0,x1),<span class="number">0</span>)</span><br><span class="line">    y0=torch.zeros(<span class="number">100</span>)</span><br><span class="line">    y1=torch.ones(<span class="number">100</span>)</span><br><span class="line">    y=torch.cat((y0,y1))</span><br><span class="line">    data=np.array(x)</span><br><span class="line">    target=np.array(y)</span><br><span class="line">    <span class="keyword">return</span> data,target</span><br><span class="line"></span><br><span class="line"><span class="comment"># def get_data():</span></span><br><span class="line"><span class="comment">#     path = &#x27;./long.mat&#x27;</span></span><br><span class="line"><span class="comment">#     file = scio.loadmat(path)[&#x27;long1&#x27;]</span></span><br><span class="line"><span class="comment">#     data = file[:, 0:2]</span></span><br><span class="line"><span class="comment">#     target = file[:, 2]</span></span><br><span class="line"><span class="comment">#     return data,target</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">plot_result</span>(<span class="params">perceptron,result</span>):</span><br><span class="line">    knew = -perceptron.weights[<span class="number">0</span>] / perceptron.weights[<span class="number">1</span>]</span><br><span class="line">    bnew = -perceptron.bias / perceptron.weights[<span class="number">1</span>]</span><br><span class="line">    x = np.linspace(-<span class="number">5</span>, <span class="number">5</span>)</span><br><span class="line">    y = <span class="keyword">lambda</span> x: knew * x + bnew</span><br><span class="line">    plt.xlim(-<span class="number">8</span>,<span class="number">8</span>)</span><br><span class="line">    plt.ylim(-<span class="number">8</span>,<span class="number">8</span>)</span><br><span class="line">    plt.plot(x, y(x), <span class="string">&#x27;b--&#x27;</span>)</span><br><span class="line">    plt.scatter(data[:, <span class="number">0</span>], data[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;Binary Classification&#x27;</span>)</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    epochs = <span class="number">1000</span></span><br><span class="line">    data,target=get_data()</span><br><span class="line">    perceptron=Perceptron(data.shape[<span class="number">1</span>],f)</span><br><span class="line">    <span class="built_in">print</span>(perceptron)</span><br><span class="line">    plot_result(perceptron,target)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epochs):</span><br><span class="line">        perceptron.train(data,target)</span><br><span class="line">        <span class="keyword">if</span> i%<span class="number">400</span>==<span class="number">0</span>:</span><br><span class="line">            plot_result(perceptron,target)</span><br><span class="line">    <span class="built_in">print</span>(perceptron)</span><br><span class="line">    acc=<span class="number">0</span></span><br><span class="line">    result=np.zeros_like(target)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(data.shape[<span class="number">0</span>]):</span><br><span class="line">        result[i]=perceptron.predict(data[i])</span><br><span class="line">        acc+=(result[i]==target[i])</span><br><span class="line">    plot_result(perceptron,target)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;acc=&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(acc / <span class="built_in">len</span>(target)))</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>感知器实现 SONAR. 数据程序 - perceptron2.py </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author : JackZhu</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Data : 2021/5/20 18:21</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> scipy.io <span class="keyword">as</span> scio</span><br><span class="line"><span class="keyword">from</span> sklearn.manifold <span class="keyword">import</span> TSNE</span><br><span class="line"><span class="keyword">from</span> sklearn.decomposition <span class="keyword">import</span> PCA</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Perceptron</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,input_num,f</span>):</span><br><span class="line">        self.input_num=input_num</span><br><span class="line">        self.weights=np.ones(input_num)</span><br><span class="line">        self.bias=<span class="number">2.0</span></span><br><span class="line">        self.activation=f</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&#x27;weight=<span class="subst">&#123;self.weights&#125;</span>,bias=<span class="subst">&#123;self.bias&#125;</span>&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">predict</span>(<span class="params">self,inputs</span>):</span><br><span class="line">        <span class="keyword">return</span> self.activation(np.dot(inputs,self.weights)+self.bias)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">self,inputs,labels,rate=<span class="number">0.1</span></span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(inputs.shape[<span class="number">0</span>]):</span><br><span class="line">            output=self.predict(inputs[j])</span><br><span class="line">            self.weights=self.weights+rate*(labels[j]-output)*inputs[j]</span><br><span class="line">            self.bias=self.bias+rate*(labels[j]-output)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">if</span> x&gt;<span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_data</span>():</span><br><span class="line">    path = <span class="string">&#x27;./sonar.csv&#x27;</span></span><br><span class="line">    file = pd.read_csv(path, header=<span class="literal">None</span>)</span><br><span class="line">    data = file.iloc[:, :-<span class="number">1</span>]</span><br><span class="line">    target = file.iloc[:, -<span class="number">1</span>]</span><br><span class="line">    target = pd.get_dummies(target).iloc[:, -<span class="number">1</span>]</span><br><span class="line">    data = np.array(data)</span><br><span class="line">    target = np.array(target)</span><br><span class="line">    <span class="keyword">return</span> data,target</span><br><span class="line"></span><br><span class="line"><span class="comment"># def get_data():</span></span><br><span class="line"><span class="comment">#     data=torch.ones(100,2)</span></span><br><span class="line"><span class="comment">#     x0=torch.normal(2*data,1.5)</span></span><br><span class="line"><span class="comment">#     x1=torch.normal(-2*data,1.5)</span></span><br><span class="line"><span class="comment">#     x=torch.cat((x0,x1),0)</span></span><br><span class="line"><span class="comment">#     y0=torch.zeros(100)</span></span><br><span class="line"><span class="comment">#     y1=torch.ones(100)</span></span><br><span class="line"><span class="comment">#     y=torch.cat((y0,y1))</span></span><br><span class="line"><span class="comment">#     data=np.array(x)</span></span><br><span class="line"><span class="comment">#     target=np.array(y)</span></span><br><span class="line"><span class="comment">#     return data,target</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># def get_data():</span></span><br><span class="line"><span class="comment">#     path = &#x27;./long.mat&#x27;</span></span><br><span class="line"><span class="comment">#     file = scio.loadmat(path)[&#x27;long1&#x27;]</span></span><br><span class="line"><span class="comment">#     data = file[:, 0:2]</span></span><br><span class="line"><span class="comment">#     target = file[:, 2]</span></span><br><span class="line"><span class="comment">#     return data,target</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">plot_result</span>(<span class="params">perceptron,result</span>):</span><br><span class="line">    knew = -perceptron.weights[<span class="number">0</span>] / perceptron.weights[<span class="number">1</span>]</span><br><span class="line">    bnew = -perceptron.bias / perceptron.weights[<span class="number">1</span>]</span><br><span class="line">    x = np.linspace(-<span class="number">5</span>, <span class="number">5</span>)</span><br><span class="line">    y = <span class="keyword">lambda</span> x: knew * x + bnew</span><br><span class="line">    plt.xlim(-<span class="number">5</span>,<span class="number">5</span>)</span><br><span class="line">    plt.ylim(-<span class="number">1</span>,<span class="number">2</span>)</span><br><span class="line">    plt.plot(x, y(x), <span class="string">&#x27;b--&#x27;</span>)</span><br><span class="line">    plt.scatter(data[:, <span class="number">0</span>], data[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;Binary Classification&#x27;</span>)</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">imshow</span>(<span class="params">data,result</span>):</span><br><span class="line">    tsne = TSNE(n_components=<span class="number">2</span>, learning_rate=<span class="number">100</span>).fit_transform(data)</span><br><span class="line">    pca = PCA().fit_transform(data)</span><br><span class="line">    plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">    plt.subplot(<span class="number">121</span>)</span><br><span class="line">    plt.scatter(tsne[:, <span class="number">0</span>], tsne[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;t-SNE&#x27;</span>)</span><br><span class="line">    plt.subplot(<span class="number">122</span>)</span><br><span class="line">    plt.scatter(pca[:, <span class="number">0</span>], pca[:, <span class="number">1</span>], c=result)</span><br><span class="line">    plt.title(<span class="string">&#x27;PCA&#x27;</span>)</span><br><span class="line">    plt.colorbar()</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    epochs = <span class="number">50000</span></span><br><span class="line">    data,target=get_data()</span><br><span class="line">    perceptron=Perceptron(data.shape[<span class="number">1</span>],f)</span><br><span class="line">    <span class="built_in">print</span>(perceptron)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epochs):</span><br><span class="line">        perceptron.train(data,target)</span><br><span class="line">    <span class="built_in">print</span>(perceptron)</span><br><span class="line">    acc=<span class="number">0</span></span><br><span class="line">    result=np.zeros_like(target)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(data.shape[<span class="number">0</span>]):</span><br><span class="line">        result[i]=perceptron.predict(data[i])</span><br><span class="line">        acc+=(result[i]==target[i])</span><br><span class="line">    imshow(data,target)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;acc=&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(acc / <span class="built_in">len</span>(target)))</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/05/30/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E5%AF%BC%E8%AE%BA%E4%BD%9C%E4%B8%9A%E2%80%94%E2%80%94%E6%84%9F%E7%9F%A5%E5%99%A8%E5%AE%9E%E7%8E%B0%E4%BA%8C%E5%88%86%E7%B1%BB/</id>
    <link href="http://jackzhu.top/2021/05/30/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E5%AF%BC%E8%AE%BA%E4%BD%9C%E4%B8%9A%E2%80%94%E2%80%94%E6%84%9F%E7%9F%A5%E5%99%A8%E5%AE%9E%E7%8E%B0%E4%BA%8C%E5%88%86%E7%B1%BB/"/>
    <published>2021-05-30T11:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景知识 1<br>1.1 基本定义 1<br>1.2 感知器的损失函数 1<br>1.3 感知器的训练 1<br>2 数据集]]>
    </summary>
    <title>计算智能导论作业——感知器实现二分类</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="计算智能" scheme="http://jackzhu.top/categories/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD/"/>
    <category term="计算智能" scheme="http://jackzhu.top/tags/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD/"/>
    <content>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景知识 1<br>1.1 FCM 算法原理 1<br>2 数据集简介 1<br>2.1 Iris 数据集 1<br>3 实验环境 1<br>4 代码实现 2<br>5 结果分析 4<br>6 总结 4<br>A Iris FCM 程序代码 5</p><h2 id="1-背景知识"><a href="#1-背景知识" class="headerlink" title="1 背景知识"></a>1 背景知识</h2><h2 id="1-1-FCM-算法原理"><a href="#1-1-FCM-算法原理" class="headerlink" title="1.1 FCM 算法原理"></a>1.1 FCM 算法原理</h2><p>FCM 算法 (Fuzzy c-Means) 也称为模糊 $C$ 均值算法,是一种基于划分的聚类算法,他的思想就是使得被划分到同一簇的对象之间相似度最大, 而不同簇之间的相似度最小。模糊 $C$ 均值算法是普通 $C$ 均值算法的改进,普通 $C$ 均值算法对于数据的划分是硬性的,而 FCM 则是一种柔性的模糊划分。通过隶属度函数来描述样本属于某个集合的程度, 其自变量范围是所有样本点的所有值,值域范围是 $[0,1]$ ,即 $0 \leq \mu_A(x) \leq 1$。</p><p>有了模糊集合的概念, 一个元素属于某个类就不是硬性的, 而是属于某个聚类的隶属度是区间 $[0,1]$ 之间的值,样本属于所有类的隶属度之和应该等于 1,可以表示为</p><p>$$<br>J(U,z_{1},z_{2},\ldots,z_{c}) &#x3D; \sum_{j &#x3D; 1}^c J_j &#x3D; \sum_{j &#x3D; 1}^c \sum_{i &#x3D; 1}^m u_{ij}^{\alpha} d_{ij}^2<br>$$</p><p>对输入参量进行求导, 使得目标函数达到最小值的条件: 聚类中心:</p><p>$$<br>z_{i} &#x3D; \frac{\sum_{i &#x3D; 1}^m u_{ij}^{\alpha} x_{i}}{\sum_{i &#x3D; 1}^m u_{ij}^{\alpha}}<br>$$</p><p>隶属度矩阵中的值:</p><p>$$<br>u_{ij} &#x3D; \frac{1}{\sum_{k &#x3D; 1}^c \left( \frac{d_{ij}}{d_{ik}} \right)^{\frac{2}{\alpha - 1}}}<br>$$</p><p>该算法的思路即为如下:</p><p>Step1: 初始化隶属度矩阵 </p><p>Step2: 计算聚类中心 </p><p>Step3: 计算代价函数</p><p>Step4: 计算新的隶属度矩阵, 并返回 Step2</p><h2 id="2-数据集简介"><a href="#2-数据集简介" class="headerlink" title="2 数据集简介"></a>2 数据集简介</h2><h2 id="2-1-Iris-数据集"><a href="#2-1-Iris-数据集" class="headerlink" title="2.1 Iris 数据集"></a>2.1 Iris 数据集</h2><p>Iris 数据集是模式识别中最著名的数据集之一。Iris 数据集包含 3 个类, 每个类有 50 个实例, 其中每一类都是指一种鸢尾属植物。有一类是与另外两类是线性可分的, 而另外两类之间是线性不可分的。</p><h2 id="3-实验环境"><a href="#3-实验环境" class="headerlink" title="3 实验环境"></a>3 实验环境</h2><ul><li>系统: Windows 10</li><li>程序运行环境: Python 3.8</li><li>Python 库: numpy、pandas、matplotlib、sklearn、random</li><li>开发工具: Spyder、Pycharm</li></ul><h2 id="4-代码实现"><a href="#4-代码实现" class="headerlink" title="4 代码实现"></a>4 代码实现</h2><p>在代码实现这里, 这里首先创建了一个 FCM 聚类的类, 这个类的初始化部分用于初始化参数, 初始化的参数包括聚类中心与隶属度矩阵, 隶属度矩阵每行之和为 1 , 数据随机设置。随后创建了一个 fit 的函数用于计算聚类中心, 创建了一个 cost 函数计算代价函数, 创建 cal_u 函数更新隶属度矩阵, 随后创建了一个 cal_label 函数计算聚类后的标签, 创建 imshow 函数用于降维可视化, 具体代码如下:</p><hr><p>class FuzzyCMeans():<br>def init__(self, data, c, alpha&#x3D;2):<br>self.alpha ( &#x3D; ) alpha<br>self.data ( &#x3D; ) data<br>self.c ( &#x3D; c)<br>self.row, self.col &#x3D; data.shape<br>self.matrix ( &#x3D; ) np.zeros((self.row,self.c))<br>for i in range(self.row):<br>for (j) in range(self.c-1):<br>if np.sum(self.matrix[i, :]) &lt; 1 :<br>self.matrix[i, j] &#x3D; random.uniform(0, 1 -<br>np.sum(self.matrix[i, :]))<br>self.matrix[i,self.c - 1] &#x3D; 1 - np.sum(self.matrix[i, :])<br>self.centers ( &#x3D; ) np.zeros((self.c,self.col))<br>def fit(self):<br>for (j) in range(self.c):<br>up1 ( &#x3D; 0)<br>down1 ( &#x3D; 0)<br>for i in range(self.row):<br>up1 +&#x3D; (self.matrix[i, j] ** self.alpha) * self.data[i]<br>down1 +&#x3D; self.matrix[i, j] ** self.alpha<br>self.centers[j] &#x3D; up1 &#x2F; down1<br>def cost(self):<br>sum ( &#x3D; 0)<br>for (j) in range(self.c):<br>for i in range(self.row):<br>sum +&#x3D; (self.matrix[i, j] ** self.alpha) *<br>(np.linalg.norm(self.data[i] - self.centers[j]) ** 2)<br>return sum<br>def cal_u(self):<br>for i in range(self.row):<br>for (j) in range(self.c):<br>down2 ( &#x3D; 0)</p><hr><hr><p>for (\mathrm{k}) in range (self.c):<br>down2 +&#x3D; (np.linalg.norm(self.data[i] - self.centers[j])<br>&#x2F; np.linalg.norm(<br>self.data[i] - self.centers[k])) ** (2 &#x2F; (self.alpha<br>( {-} 1)))<br>self.matrix (\lbrack i,j\rbrack &#x3D; 1&#x2F;\left( \operatorname{down}2 \right))<br>def cal_label(self):<br>lab ( &#x3D; ) np.argmax(self.matrix,axis&#x3D;1)<br>return lab<br>def calcute(self, epochs):<br>for epoch in range(epochs):<br>self.fit()<br>result ( &#x3D; \operatorname{self} {\cdot} \operatorname{cost}())<br>print (result)<br>self.cal_u()<br>label &#x3D; self.cal_label()<br>return label<br>def imshow(self, label):<br>tsne ( &#x3D; TSNE(n_) components ( &#x3D; 2) ,<br>learning_rate&#x3D;100).fit_transform(self.data)<br>pca ( &#x3D; ) PCA().fit_transform(self.data)<br>plt.figure(figsize&#x3D;(12, 6))<br>plt.subplot(121)<br>plt.scatter(tsne[:,0],tsne[:,1], c&#x3D;label)<br>plt.title(‘t-SNE’)<br>plt.subplot(122)<br>plt.scatter(pca[:,0],pca[:,1], c&#x3D;label)<br>plt.title(‘PCA’)<br>plt.colorbar()<br>plt.show()</p><hr><p>然后是主函数部分, 此部分是关于创建出的类的使用, 首先读取数据并创建类的对象, 设置迭代次数为 50 , 然后使用 fcm 聚类方法进行计算得到结果, 然后进行可视化并计算聚类的标准轮廓系数, 代码如下:</p><hr><p>if _<em>name</em> &#x3D;&#x3D; ‘<strong>main</strong>‘:<br>(c &#x3D; 3)<br>iris ( &#x3D; ) load_iris()<br>data ( &#x3D; ) iris.data<br># target &#x3D; iris.target<br>(f\mathrm{\ cm} &#x3D; ) FuzzyCMeans(data, (c &#x3D; c) )</p><hr><p>label ( &#x3D; f\mathrm{\ cm}) . calcute (epochs ( &#x3D; 50) ) print(label)<br>fcm.imshow(label)<br>(s &#x3D; ) metrics.silhouette_score(data,label,metric&#x3D;’euclidean’) print(‘轮廓系数为 ({: .4f}) ‘.format(s))</p><h2 id="5-结果分析"><a href="#5-结果分析" class="headerlink" title="5 结果分析"></a>5 结果分析</h2><p>经过实验, 可以得到最终结果如下图所示, 其中代价函数最后为 60.5057, 计算得到的<br>轮廓系数为 0.5495 , 由下图可以看出, 聚类效果很好, 完成了聚类的任务。</p><img src="https://cdn.noedgeai.com/13b8aff7-d660-486b-8d4a-83244a4fc29c_5.jpg?x=181&y=341&w=479&h=254 "/><p>图 1: 结果图</p><h2 id="6-总结"><a href="#6-总结" class="headerlink" title="6 总结"></a>6 总结</h2><p>本次作业算法相当一部分都已经给出, 因此写的过程中问题不大, 在这次实验中遇到的一个问题是在开始初始化隶属度矩阵的时候, 我将矩阵的所有值都统一为聚类数分之一, 这样导致的后果是随着迭代进行, 聚类中心以及隶属度矩阵都没有发生变化, 后来经过仔细检查才发现问题并进行解决。<br>在这次实验中, 开始在理解题意方面遇到了很多问题, 后来经过多方询问才明白。这次实验中我通过广泛查询资料了解到了相关的知识, 也认真写代码来完成任务, 这份作业的完成确实比较艰巨, 一份顶多份, 但是我还是有很大的收获, 能力也得到了提升。</p><h2 id="A-Iris-FCM-程序代码"><a href="#A-Iris-FCM-程序代码" class="headerlink" title="A Iris FCM 程序代码"></a>A Iris FCM 程序代码</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><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 class="keyword">from</span> sklearn <span class="keyword">import</span> metrics</span><br><span class="line"><span class="keyword">from</span> sklearn.datasets <span class="keyword">import</span> load_iris</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> sklearn.manifold <span class="keyword">import</span> TSNE</span><br><span class="line"><span class="keyword">from</span> sklearn.decomposition <span class="keyword">import</span> PCA</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FuzzyCMeans</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, data, c, alpha=<span class="number">2</span></span>):</span><br><span class="line">        self.alpha = alpha</span><br><span class="line">        self.data = data</span><br><span class="line">        self.c = c</span><br><span class="line">        self.row, self.col = data.shape</span><br><span class="line">        self.matrix = np.zeros((self.row, self.c))</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.row):</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.c-<span class="number">1</span>):</span><br><span class="line">                <span class="keyword">if</span> np.<span class="built_in">sum</span>(self.matrix[i, :]) &lt; <span class="number">1</span> :</span><br><span class="line">                    self.matrix[i, j] = random.uniform(<span class="number">0</span>, <span class="number">1</span> - np.<span class="built_in">sum</span>(self.matrix[i, :]))</span><br><span class="line">            self.matrix[i, self.c - <span class="number">1</span>] = <span class="number">1</span> - np.<span class="built_in">sum</span>(self.matrix[i, :])</span><br><span class="line">        self.centers = np.zeros((self.c, self.col))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fit</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.c):</span><br><span class="line">            up1 = <span class="number">0</span></span><br><span class="line">            down1 = <span class="number">0</span></span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.row):</span><br><span class="line">                up1 += (self.matrix[i, j] ** self.alpha) * self.data[i]</span><br><span class="line">                down1 += self.matrix[i, j] ** self.alpha</span><br><span class="line">            self.centers[j] = up1 / down1</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cost</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.c):</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.row):</span><br><span class="line">                <span class="built_in">sum</span> += (self.matrix[i, j] ** self.alpha) * (np.linalg.norm(self.data[i] - self.centers[j]) ** <span class="number">2</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sum</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cal_u</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.row):</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.c):</span><br><span class="line">                down2 = <span class="number">0</span></span><br><span class="line">                <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">range</span>(self.c):</span><br><span class="line">                    down2 += (np.linalg.norm(self.data[i] - self.centers[j]) / np.linalg.norm(</span><br><span class="line">                        self.data[i] - self.centers[k])) ** (<span class="number">2</span> / (self.alpha - <span class="number">1</span>))</span><br><span class="line"></span><br><span class="line">                self.matrix[i, j] = <span class="number">1</span> / (down2)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">cal_label</span>(<span class="params">self</span>):</span><br><span class="line">        lab = np.argmax(self.matrix, axis=<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">return</span> lab</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">calcute</span>(<span class="params">self, epochs</span>):</span><br><span class="line">        <span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(epochs):</span><br><span class="line">            self.fit()</span><br><span class="line">            result = self.cost()</span><br><span class="line">            <span class="built_in">print</span>(result)</span><br><span class="line">            self.cal_u()</span><br><span class="line">        label = self.cal_label()</span><br><span class="line">        <span class="keyword">return</span> label</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">imshow</span>(<span class="params">self, label</span>):</span><br><span class="line">        tsne = TSNE(n_components=<span class="number">2</span>, learning_rate=<span class="number">100</span>).fit_transform(self.data)</span><br><span class="line">        pca = PCA().fit_transform(self.data)</span><br><span class="line">        plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">        plt.subplot(<span class="number">121</span>)</span><br><span class="line">        plt.scatter(tsne[:, <span class="number">0</span>], tsne[:, <span class="number">1</span>], c=label)</span><br><span class="line">        plt.title(<span class="string">&#x27;t-SNE&#x27;</span>)</span><br><span class="line">        plt.subplot(<span class="number">122</span>)</span><br><span class="line">        plt.scatter(pca[:, <span class="number">0</span>], pca[:, <span class="number">1</span>], c=label)</span><br><span class="line">        plt.title(<span class="string">&#x27;PCA&#x27;</span>)</span><br><span class="line">        plt.colorbar()</span><br><span class="line">        plt.show()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    c = <span class="number">3</span></span><br><span class="line">    iris = load_iris()</span><br><span class="line">    data = iris.data</span><br><span class="line">    <span class="comment"># target = iris.target</span></span><br><span class="line">    fcm = FuzzyCMeans(data, c=c)</span><br><span class="line">    label = fcm.calcute(epochs=<span class="number">50</span>)</span><br><span class="line">    <span class="built_in">print</span>(label)</span><br><span class="line">    fcm.imshow(label)</span><br><span class="line">    s = metrics.silhouette_score(data, label, metric=<span class="string">&#x27;euclidean&#x27;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;轮廓系数为&#123;:.4f&#125;&#x27;</span>.<span class="built_in">format</span>(s))</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/05/30/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E5%AF%BC%E8%AE%BA%E4%BD%9C%E4%B8%9A%E2%80%94%E2%80%94FCM%20%E8%81%9A%E7%B1%BB%E7%9A%84%E5%AE%9E%E7%8E%B0/</id>
    <link href="http://jackzhu.top/2021/05/30/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E5%AF%BC%E8%AE%BA%E4%BD%9C%E4%B8%9A%E2%80%94%E2%80%94FCM%20%E8%81%9A%E7%B1%BB%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
    <published>2021-05-30T10:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景知识 1<br>1.1 FCM 算法原理 1<br>2 数据集简介 1<br>2.1 Iris 数据集 1<br>3 实验环]]>
    </summary>
    <title>计算智能导论作业——FCM 聚类的实现</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="机器学习" scheme="http://jackzhu.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="机器学习" scheme="http://jackzhu.top/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="摘-要"><a href="#摘-要" class="headerlink" title="摘 要"></a>摘 要</h1><p>本文使用了K-means和DBSCAN两种聚类方法，较好的完成了聚类任务，并将得到的结果使用了t-SNE和PCA两种方法进行降维可视化，从而更好的得到聚类的效果，并计算了轮廓系数、CH score、DBI这些聚类指标，对于K-means方法还使用了图片进行聚类，使得效果更加直观，实验效果非常好。<br><br>在这次实验中，使用的两种聚类方法，K-means是基于原型的方法，而DBSCAN是基于密度的聚类方法。本文首先介绍了两种聚类方法的背景知识以及相关指标的知识，随后简单介绍了Iris数据集，然后展示出实验环境。<br><br>在代码实现部分，本文分别对两种聚类方法列出了重点的代码，并进行简要的介绍，说明这些部分是如何通过代码实现的。<br><br>在实验结果部分，本文展示了两种聚类方法的结果，对于K-means方法，使用Iris数据集进行了降维可视化，并算出Iris的轮廓系数为0.4976，对图片进行聚类，得到着色后的效果图，并进行降维可视化。对于DBSCAN方法，这里使用了八组数据进行展示，分别对八组数据求出其轮廓系数、CH score、DBI这些聚类指标，并得到了较好的实验效果，数据较多，具体数据以及聚类效果图见正文部分。<br><br>最后对本次实验进行了总结，分析实验得出了收获以及思考。</p><p>关键字: K-means DBSCAN 轮廓系数 PCA t-SNE</p><h1 id="1-背景知识"><a href="#1-背景知识" class="headerlink" title="1 背景知识"></a>1 背景知识</h1><h2 id="1-1-基于原型的方法"><a href="#1-1-基于原型的方法" class="headerlink" title="1.1 基于原型的方法"></a>1.1 基于原型的方法</h2><p>本作业使用的基于原型的方法即 K-means 聚类算法，其原理如下:<br>K-均值 (K-means) 聚类算法是应用最广泛的基于划分的聚类算法之一，适用于处理大样本数据。其基础是最小误差平方和准则，若 $N_{i}$ 是第 $i$ 聚类 $\Gamma$ 中的样本数目，$m_{i}$ 是这些样本的均值，即</p><p>$$<br>m_{i} &#x3D; \frac{1}{N_{i}} \sum_{y \in \Gamma} y<br>$$</p><p>把其中的各样本 $y$ 与均值 $m_{i}$ 间的误差平方和对所有类相加后即得目标函数为:</p><p>$$<br>J_{e} &#x3D; \sum_{i &#x3D; 1}^c \sum_{y \in \Gamma_{i}} \left| y - m_{i} \right|^2<br>$$</p><p>即需进行优化使上式的值取得最小。<br>该算法的的步骤如下:</p><ol><li>Step1: 选取 $K$ 个初始聚类中心。</li><li>Step2: 根据最小距离标准将要分类的模式样本划分到某个簇中心。</li><li>Step3: 计算各个聚类中心的新的向量值及计算各聚类簇中样本数据的均值向量。</li><li>Step4: 若聚类中心与上一次的相同，则返回 Step2，否则计算结束。</li></ol><h2 id="1-2-基于密度的方法论"><a href="#1-2-基于密度的方法论" class="headerlink" title="1.2 基于密度的方法论"></a>1.2 基于密度的方法论</h2><h3 id="1-2-1-基本知识"><a href="#1-2-1-基本知识" class="headerlink" title="1.2.1 基本知识"></a>1.2.1 基本知识</h3><p>基于密度的聚类中著名的是 DBSCAN, DBSCAN (Density-Based Spatial Clustering of Applications with Noise, 具有噪声的基于密度的聚类方法) 是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇, 并在具有噪声的空间数据库中发现任意形状的簇, 它将簇定义为密度相连的点的最大集合。<br>该方法主要有以下特点:</p><ul><li>发现任意类型的聚类</li><li>处理噪音</li><li>一编扫描</li><li>需要密度参数作为终止条件<br>在使用密度聚类算法的时候, 有两个超参数, 领域的最大半径 Eps 和领域中最少的点<br>数 MinPts。</li></ul><h3 id="1-2-2-基本概念"><a href="#1-2-2-基本概念" class="headerlink" title="1.2.2 基本概念"></a>1.2.2 基本概念</h3><p>此处需要定义几个概念:</p><ul><li>核心对象: 一个对象的 $\epsilon$-邻域至少包含最小数目 MinPts 个对象。不是核心点的 Eps 邻域内的对象称为边界点，不属于任何簇的对象为噪声。对于空间中的一个对象，如果它在给定半径 $\epsilon$ 的邻域中的对象个数大于密度阈值 MinPts，则该对象被称为核心对象，否则称为边界对象。</li><li>密度可达: 存在一个从 $p$ 到 $q$ 的 DDR 对象链（如果存在一条链 $&lt; p1, p2, \ldots, pi &gt;$，满足 $p1 &#x3D; p, pi &#x3D; q, p_{i}$ 直接密度可达 $p_{i + 1}$，则称 $p$ 密度可达 $q$）。</li><li>由一个核心对象和其密度可达的所有对象构成一个聚类。</li></ul><h2 id="1-3-轮廓系数"><a href="#1-3-轮廓系数" class="headerlink" title="1.3 轮廓系数"></a>1.3 轮廓系数</h2><p>轮廓系数是聚类好坏的一种评价方式，它结合内聚度和分离度两种因素。可以用来在相同原始数据的基础上用来评价不同算法、或者算法不同运行方式对聚类结果所产生的影响。其计算方法如下:</p><p>对于其中的一个点$i$而言，首先计算$a(i)&#x3D;average(i\text{向量到所有它属于的簇中其它点的距离})$</p><p>然后计算$b(i)&#x3D;min(i\text{向量到与它相邻最近的一簇内的所有点的平均距离})$，那么$i$向量轮廓系数为：</p><p>$$<br>S(i) &#x3D; \frac{b(i) - a(i)}{\max{a(i), b(i)}}<br>$$</p><p>轮廓系数的值是介于 $[-1,1]$，越趋近于 1 则代表内聚度和分离度都相对较优。</p><h1 id="2-Iris-数据集简介"><a href="#2-Iris-数据集简介" class="headerlink" title="2 Iris 数据集简介"></a>2 Iris 数据集简介</h1><p>Iris 数据集是著名的数据集之一。Iris 数据集包含 3 个类, 每个类有 50 个实例, 其中每一类都是指一种鸢尾属植物。有一类是与另外两类是线性可分的, 而另外两类之间是线性不可分的。</p><h2 id="3-实验环境"><a href="#3-实验环境" class="headerlink" title="3 实验环境"></a>3 实验环境</h2><ul><li>系统: Windows 10</li><li>程序运行环境: Python 3.8</li><li>Python 库: numpy、pandas、matplotlib、sklearn、random</li><li>开发工具: Spyder、VSCode</li></ul><h2 id="4-代码实现"><a href="#4-代码实现" class="headerlink" title="4 代码实现"></a>4 代码实现</h2><p>在代码实现中,对于 kmeans 我使用了两种数据集进行实现, 第一种使用了经典的数据集 Iris 鸢尾花, 为了更好的体现聚类的效果, 我使用了一张照片进行聚类, 对不同的聚类使用不同的颜色表示。在评估聚类的效果时,对于 kmeans 聚类, 这里使用了不同聚类的平均轮廓系数来进行分析。对于这两种数据, 都使用了 t-SNE 和 PCA 两种降维方式来展示效果。<br>对于 DBSCAN 聚类, 这里使用了一些聚类的数据并可视化, 可以很好的表达密度聚类<br>的效果。</p><h2 id="4-1-kmeans-代码实现"><a href="#4-1-kmeans-代码实现" class="headerlink" title="4.1 kmeans 代码实现"></a>4.1 kmeans 代码实现</h2><p>对于 kmeans 的 Iris 实现, 这里建立了一个类来进行处理, 在获取到数据后, 首先根据设置的 (\mathrm{k}) 值,选出 (\mathrm{k}) 个初始聚类中心,本代码使用的方法是从数据集中随机抽取 (\mathrm{k}) 个数据来初始化,并将数据的序号记录下来,其 (\mathrm{k}) 个点的四维的数据直接存放到建立好的 (k {\times} 4)<br>的数组中, 随后按照算法一步一步迭代, 直到达到终止条件, 这里终止条件设置的为迭代的次数, 最后可以得到聚类的中心点以及每个数据的标签, 随后运用两种降维方式可视化出来, 然后利用 sklearn 库来计算平均轮廓系数。<br>对于用 kmeans 处理照片, 主函数部分与 Iris 的实现相同, 但是多了一些部分, 首先读取照片, 并将三维转为二维, 从而方便进行处理, 随后进行聚类, 得出结果后, 把数据恢复成三维, 并将图片按照聚类着色为多种颜色, 并利用 t-SNE 和 PCA 进行降维可视化, 得到结果。<br>聚类部分建立的类代码如下: 初始化参数, 函数 fit 进行聚类, imshow 降维展示, plot_img 图片着色。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Kmeans</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,dat,k</span>):</span><br><span class="line">        data=scale(dat)</span><br><span class="line">        self.data=data</span><br><span class="line">        self.row, self.col = data.shape</span><br><span class="line">        self.k=k</span><br><span class="line">        self.centers=np.ndarray((k,self.col))</span><br><span class="line">        choices=random.choices(<span class="built_in">range</span>(self.row),k=k)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(k):</span><br><span class="line">            self.centers[i,:]=self.data[choices[i],:]</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fit</span>(<span class="params">self,counts=<span class="number">15</span></span>):</span><br><span class="line">        count=<span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span>(count&lt;counts):</span><br><span class="line">            self.labels=np.zeros((self.row))</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.data.shape[<span class="number">0</span>]):</span><br><span class="line">                dis=[]</span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.k):</span><br><span class="line">                    dis.append(np.linalg.norm(self.\</span><br><span class="line">                    data[i,:]-self.centers[j,:],axis=<span class="number">0</span>))</span><br><span class="line">                lab=np.argmin(dis,axis=<span class="number">0</span>)</span><br><span class="line">                self.labels[i]=lab</span><br><span class="line">            self.result=&#123;&#125;</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.k):</span><br><span class="line">                <span class="built_in">type</span>=np.where(self.labels==i)[<span class="number">0</span>]</span><br><span class="line">                self.result[i]=<span class="built_in">type</span></span><br><span class="line">                <span class="keyword">if</span> <span class="built_in">len</span>(<span class="built_in">type</span>)==<span class="number">0</span>:</span><br><span class="line">                    self.centers[i, :] =<span class="number">0</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    self.centers[i,:]=np.mean(self.data[<span class="built_in">type</span>,:],axis=<span class="number">0</span>)</span><br><span class="line">            count+=<span class="number">1</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> self.centers, self.result</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">imshow</span>(<span class="params">self</span>):</span><br><span class="line">        tsne = TSNE(n_components=<span class="number">2</span>, learning_rate=<span class="number">100</span>).fit_transform(self.data)</span><br><span class="line">        pca = PCA().fit_transform(self.data)</span><br><span class="line">        plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">        plt.subplot(<span class="number">121</span>)</span><br><span class="line">        plt.scatter(tsne[:, <span class="number">0</span>], tsne[:, <span class="number">1</span>], c=self.labels)</span><br><span class="line">        plt.subplot(<span class="number">122</span>)</span><br><span class="line">        plt.scatter(pca[:, <span class="number">0</span>], pca[:, <span class="number">1</span>], c=self.labels)</span><br><span class="line">        plt.colorbar()</span><br><span class="line">        plt.show()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">plot_img</span>(<span class="params">self,row,col</span>):</span><br><span class="line">        img=self.labels.reshape(row,col)</span><br><span class="line">        im = Image.new(<span class="string">&quot;RGB&quot;</span>, (row, col))  <span class="comment"># 创建图片</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(row):</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(col):</span><br><span class="line">                <span class="keyword">if</span> img[i, j] == <span class="number">0</span>:</span><br><span class="line">                    im.putpixel((i, j), (<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line">                <span class="keyword">if</span> img[i, j] == <span class="number">1</span>:</span><br><span class="line">                    im.putpixel((i, j), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>))</span><br><span class="line">                <span class="keyword">if</span> img[i, j] == <span class="number">2</span>:</span><br><span class="line">                    im.putpixel((i, j), (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>))</span><br><span class="line">        im.show()</span><br><span class="line">        im.save(<span class="string">&#x27;result.jpg&#x27;</span>)</span><br></pre></td></tr></table></figure><p>主函数代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">path=<span class="string">&#x27;./2.bmp&#x27;</span></span><br><span class="line">file=Image.<span class="built_in">open</span>(path,<span class="string">&#x27;r&#x27;</span>)</span><br><span class="line">file=np.array(file)</span><br><span class="line">row,col,_=file.shape</span><br><span class="line">data=file.reshape(-<span class="number">1</span>,<span class="number">3</span>)</span><br><span class="line">kmeans=Kmeans(data,<span class="number">3</span>)</span><br><span class="line">centers,results=kmeans.fit(<span class="number">10</span>)</span><br><span class="line">kmeans.imshow()</span><br><span class="line">kmeans.plot_img(row,col)</span><br><span class="line"><span class="built_in">print</span>(centers)</span><br><span class="line"><span class="built_in">print</span>(results)</span><br></pre></td></tr></table></figure><p>此部分进行图片的读取与降维预处理, 然后进行聚类, 然后可视化。<br>Iris 的代码与之相差不大, 不再单独列出, 见附录。</p><h2 id="4-2-DBSCAN-代码实现"><a href="#4-2-DBSCAN-代码实现" class="headerlink" title="4.2 DBSCAN 代码实现"></a>4.2 DBSCAN 代码实现</h2><p>在代码实现的过程中, 主要有以下步骤: </p><ol><li>读取数据</li><li>构建密度聚类函数</li><li>将聚类后的结果可视化 4. 对聚类效果进行评价<br>读取数据采用了 scipy 中的 scio 来读取.mat 文件, 然后初步处理并传给聚类函数, 然后对结果可视化,最后利用 sklearn 中的库函数 metrics 来计算轮廓系数、 (\mathrm{CH}) score 以及 DBI 这三个指标。<br>首先定义了两个函数用于计算相关信息:</li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">calDist</span>(<span class="params">X1, X2</span>):</span><br><span class="line">    <span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> x1, x2 <span class="keyword">in</span> <span class="built_in">zip</span>(X1, X2):</span><br><span class="line">        <span class="built_in">sum</span> += (x1 - x2) ** <span class="number">2</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sum</span> ** <span class="number">0.5</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getNeibor</span>(<span class="params">data, dataSet, e</span>):</span><br><span class="line">    res = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(dataSet.shape[<span class="number">0</span>]):</span><br><span class="line">        <span class="keyword">if</span> calDist(data, dataSet[i]) &lt; e:</span><br><span class="line">            res.append(i)</span><br><span class="line">    <span class="keyword">return</span> res</span><br></pre></td></tr></table></figure><p>随后定义密度聚类的主函数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">DBSCAN</span>(<span class="params">dataSet, e, minPts</span>):</span><br><span class="line">    coreObjs = &#123;&#125;  <span class="comment"># 初始化核心对象集合</span></span><br><span class="line">    C = &#123;&#125;</span><br><span class="line">    n = dataSet.shape[<span class="number">0</span>]</span><br><span class="line">    <span class="comment"># 找出所有核心对象，key是核心对象的index，value是ε-邻域中对象的index</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line">        neibor = getNeibor(dataSet[i], dataSet, e)</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(neibor) &gt;= minPts:</span><br><span class="line">            coreObjs[i] = neibor</span><br><span class="line">    oldCoreObjs = coreObjs.copy()</span><br><span class="line">    k = <span class="number">0</span>  <span class="comment"># 初始化聚类簇数</span></span><br><span class="line">    notAccess = <span class="built_in">list</span>(<span class="built_in">range</span>(n))  <span class="comment"># 初始化未访问样本集合（索引）</span></span><br><span class="line">    <span class="keyword">while</span> <span class="built_in">len</span>(coreObjs) &gt; <span class="number">0</span>:</span><br><span class="line">        OldNotAccess = []</span><br><span class="line">        OldNotAccess.extend(notAccess)</span><br><span class="line">        cores = coreObjs.keys()</span><br><span class="line">        <span class="comment"># 随机选取一个核心对象</span></span><br><span class="line">        randNum = random.randint(<span class="number">0</span>, <span class="built_in">len</span>(cores) - <span class="number">1</span>)</span><br><span class="line">        cores = <span class="built_in">list</span>(cores)</span><br><span class="line">        core = cores[randNum]</span><br><span class="line">        queue = []</span><br><span class="line">        queue.append(core)</span><br><span class="line">        notAccess.remove(core)</span><br><span class="line">        <span class="keyword">while</span> <span class="built_in">len</span>(queue) &gt; <span class="number">0</span>:</span><br><span class="line">            q = queue[<span class="number">0</span>]</span><br><span class="line">            <span class="keyword">del</span> queue[<span class="number">0</span>]</span><br><span class="line">            <span class="keyword">if</span> q <span class="keyword">in</span> oldCoreObjs.keys():</span><br><span class="line">                delte = [val <span class="keyword">for</span> val <span class="keyword">in</span> oldCoreObjs[q] <span class="keyword">if</span> val <span class="keyword">in</span> notAccess]  <span class="comment"># Δ = N(q)∩Γ</span></span><br><span class="line">                queue.extend(delte)  <span class="comment"># 将Δ中的样本加入队列Q</span></span><br><span class="line">                notAccess = [val <span class="keyword">for</span> val <span class="keyword">in</span> notAccess <span class="keyword">if</span> val <span class="keyword">not</span> <span class="keyword">in</span> delte]  <span class="comment"># Γ = Γ\Δ</span></span><br><span class="line">        k += <span class="number">1</span></span><br><span class="line">        C[k] = [val <span class="keyword">for</span> val <span class="keyword">in</span> OldNotAccess <span class="keyword">if</span> val <span class="keyword">not</span> <span class="keyword">in</span> notAccess]</span><br><span class="line">        <span class="keyword">for</span> x <span class="keyword">in</span> C[k]:</span><br><span class="line">            <span class="keyword">if</span> x <span class="keyword">in</span> coreObjs.keys():</span><br><span class="line">                <span class="keyword">del</span> coreObjs[x]</span><br><span class="line">    <span class="keyword">return</span> C</span><br></pre></td></tr></table></figure><p>随后定义了一个可视化函数, 主函数算出的聚类结果存在字典里, 这个函数将其标签<br>转化为数组形式, 代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">C, D</span>):</span><br><span class="line">    colors = <span class="built_in">list</span>(mcolors.TABLEAU_COLORS.keys())</span><br><span class="line">    predict = np.zeros((D.shape[<span class="number">0</span>], D.shape[<span class="number">1</span>] + <span class="number">1</span>))</span><br><span class="line">    j = <span class="number">0</span></span><br><span class="line">    keys = C.keys()</span><br><span class="line">    <span class="built_in">print</span>(keys)</span><br><span class="line">    <span class="keyword">for</span> k <span class="keyword">in</span> keys:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> C[k]:</span><br><span class="line">            predict[j, <span class="number">0</span>:<span class="number">2</span>] = D[i]</span><br><span class="line">            predict[j, <span class="number">2</span>] = k</span><br><span class="line">            j = j + <span class="number">1</span></span><br><span class="line">            plt.scatter(D[i, <span class="number">0</span>], D[i, <span class="number">1</span>], color=colors[k + <span class="number">1</span>])</span><br><span class="line">    plt.show()</span><br><span class="line">    <span class="keyword">return</span> predict</span><br></pre></td></tr></table></figure><p>随后定义主函数, 读取数据并进行聚类, 随后计算三个指标, 代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    path = <span class="string">&#x27;./data-密度聚类/square1.mat&#x27;</span></span><br><span class="line">    data = scio.loadmat(path)[<span class="string">&#x27;square1&#x27;</span>]</span><br><span class="line">    <span class="comment"># plt.scatter(data[:,0],data[:,1])</span></span><br><span class="line">    <span class="comment"># plt.show()</span></span><br><span class="line">    D = data[:, <span class="number">0</span>:<span class="number">2</span>]</span><br><span class="line">    label = data[<span class="number">2</span>]</span><br><span class="line">    C = DBSCAN(D, <span class="number">0.9</span>, <span class="number">15</span>)</span><br><span class="line">    predict = draw(C, D)</span><br><span class="line">    s1 = metrics.silhouette_score(predict[:, <span class="number">0</span>:<span class="number">2</span>], predict[:, <span class="number">2</span>], metric=<span class="string">&#x27;euclidean&#x27;</span>)</span><br><span class="line">    s2 = calinski_harabasz_score(predict[:, <span class="number">0</span>:<span class="number">2</span>], predict[:, <span class="number">2</span>])  <span class="comment"># 计算CH score</span></span><br><span class="line">    s3 = davies_bouldin_score(predict[:, <span class="number">0</span>:<span class="number">2</span>], predict[:, <span class="number">2</span>])  <span class="comment"># 计算 DBI</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(s1, s2, s3)</span><br></pre></td></tr></table></figure><h1 id="5-结果与分析"><a href="#5-结果与分析" class="headerlink" title="5 结果与分析"></a>5 结果与分析</h1><h2 id="5-1-kmeans-聚类"><a href="#5-1-kmeans-聚类" class="headerlink" title="5.1 kmeans 聚类"></a>5.1 kmeans 聚类</h2><p>利用 K-means 聚类对 Iris 数据的处理结果如图 1 所示,其中 (\mathrm{K}) 设置为 3,迭代次数<br>设置为 15 。</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_9.jpg?x=134&y=473&w=577&h=303 "/><p>图 1: kmeans 对 Iris 的聚类效果</p><p>由图片可以看出, 两种降维方式效果都非常好, 三类数据基本可以较好的分出来, 其中<br>两类较为相似, 这两类与另一类相差较大, 聚类效果较好。<br>此外, 对于 Iris 数据集, 这里还计算了轮廓系数, 在 3 分类迭代次数为 15 的情况下,<br>不同类别的平均轮廓系数为 0.4976 。<br>下面利用 kmeans 聚类对图像进行处理, 图 2 是一张照片:<br>对上面的这张照片聚类处理并着色后,结果如图 3 所示,其中 (\mathrm{K}) 设置为 3,迭代次数<br>设置为 15 。<br>由上图处理后的图像可以看出, 图片的聚类效果较好, 图片的轮廓信息可以较好的保<br>留下来, 聚类任务完成。<br>其中聚类处理后分别使用 t-SNE 和 PCA 降维可视化的效果如图 4 所示。<br><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_10.jpg?x=131&y=96&w=581&h=394 "/></p><p>图 2: 聚类前的原图</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_10.jpg?x=132&y=606&w=580&h=391 "/><p>图 3: kmeans 对图像的聚类效果<br><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_11.jpg?x=131&y=64&w=579&h=300 "/></p><p>图 4: kmeans 对图片的聚类效果降维展示</p><p>由上图可以看出, 两种降维方式中 PCA 的效果更好, 对图片的聚类任务完成的很好,<br>这也印证了图片的着色图效果较好。</p><h2 id="5-2-DBSCAN-聚类"><a href="#5-2-DBSCAN-聚类" class="headerlink" title="5.2 DBSCAN 聚类"></a>5.2 DBSCAN 聚类</h2><p>以下是利用一些数据进行密度聚类的效果图片。</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_11.jpg?x=147&y=584&w=250&h=187 "/><p>图 5: 密度聚类 1</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_11.jpg?x=429&y=584&w=255&h=187 "/><p>图 6: 密度聚类 2</p><p>设置半径 (\mathrm{Eps}) 为 (0.04,\mathrm{Minpts}) 为 5 时,可以将数据三分类,得到图像如图 5 所示,轮<br>廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.356{、}79.043{、}1.265) 。<br>设置半径 Eps 为 0.75, Minpts 为 5 时, 可以将数据四分类, 得到图像如图 6所示, 轮<br>廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.674{、}5185.453{、}0.397) 。<br>设置半径 Eps 为 0.87, Minpts 为 16 时, 可以将数据三分类, 得到图像如图 7 所示,<br>轮廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.572{、}2147.144{、}5.919) 。<br>设置半径 Eps 为 0.2, Minpts 为 5 时, 可以将数据二分类, 得到图像如图 8 所示, 轮<br>廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.321{、}156.074{、}1.017) 。<br>设置半径 (\mathrm{Eps}) 为 (0.2,\mathrm{Minpts}) 为 5 时,可以将数据二分类,得到图像如图 9 所示,轮<br>廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 ( {-} 0.062{、}151.734{、}5.350) 。<br><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_12.jpg?x=153&y=206&w=245&h=187 "/></p><p>图 7: 密度聚类 3</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_12.jpg?x=432&y=205&w=251&h=189 "/><p>图 8: 密度聚类 4</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_12.jpg?x=143&y=714&w=255&h=186 "/><p>图 9: 密度聚类 5</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_12.jpg?x=431&y=715&w=255&h=186 "/><p>图 10: 密度聚类 6<br>设置半径 Eps 为 1.1, Minpts 为 10 时, 可以将数据四分类, 得到图像如图 10 所示,<br>轮廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.664{、}1509.256{、}3.146) 。</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_13.jpg?x=437&y=140&w=246&h=187 "/><p>图 12: 密度聚类 8</p><img src="https://cdn.noedgeai.com/a5fee367-df47-4713-89f7-58942dcd50cc_13.jpg?x=150&y=139&w=248&h=187 "/><p>图 11: 密度聚类 7</p><p>设置半径 (\mathrm{Eps}) 为 0.2,Minpts 为 5 时,可以将数据二分类,得到图像如图 11 所示,轮<br>廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.045{、}13.449{、}5.843) 。<br>设置半径 Eps 为 0.9, Minpts 为 15 时, 可以将数据四分类, 得到图像如图 12 所示,<br>轮廓系数、 (\mathrm{CH}) score、 (\mathrm{DBI}) 分别为 (0.664{、}4527.194{、}1.435) 。<br>从上面对八个数据集进行的密度聚类, 我们可以看出聚类达到了很好的效果, 不同分布结构的数据在设置不同的参数后都可以很好的被分类, 这样相比 kmeans 适用的范围更广, 缺点是算法相对于 kmeans 更复杂, 而且需要调整合适的参数才能得到较好的结果。</p><h2 id="6-总结"><a href="#6-总结" class="headerlink" title="6 总结"></a>6 总结</h2><p>这次的实验任务相对较多, 完成任务花费了不少的时间, 但在这个过程中我也收获了很多东西, 学习到了新的知识, 也加强了自己的代码能力, 在这个过程中也多方请假同学, 并广泛查阅了资料, 能力得到了提升, 对于聚类的了解增强了, 运用能力也得到了强化。</p><h2 id="A-K-means-程序代码"><a href="#A-K-means-程序代码" class="headerlink" title="A K-means 程序代码"></a>A K-means 程序代码</h2><p>KMEANS IRIS 程序 - kmeans.py </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author : JackZhu</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Data : 2021/5/10 23:10</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> sklearn</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> scale</span><br><span class="line"><span class="keyword">from</span> sklearn.manifold <span class="keyword">import</span> TSNE</span><br><span class="line"><span class="keyword">from</span> sklearn.decomposition <span class="keyword">import</span> PCA</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> silhouette_score</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Kmeans</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,dat,k</span>):</span><br><span class="line">        data=scale(dat)</span><br><span class="line">        self.data=data</span><br><span class="line">        self.row, self.col = data.shape</span><br><span class="line">        self.k=k</span><br><span class="line">        self.centers=np.ndarray((k,self.col))</span><br><span class="line">        choices=random.choices(<span class="built_in">range</span>(self.row),k=k)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(k):</span><br><span class="line">            self.centers[i,:]=self.data[choices[i],:]</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fit</span>(<span class="params">self</span>):</span><br><span class="line">        count=<span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span>(count&lt;<span class="number">15</span>):</span><br><span class="line">            self.labels=np.zeros((self.row))</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.data.shape[<span class="number">0</span>]):</span><br><span class="line">                dis=[]</span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.k):</span><br><span class="line">                    dis.append(np.linalg.norm(self.data[i,:]-self.centers[j,:],axis=<span class="number">0</span>))</span><br><span class="line">                lab=np.argmin(dis,axis=<span class="number">0</span>)</span><br><span class="line">                self.labels[i]=lab</span><br><span class="line">            self.result=&#123;&#125;</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.k):</span><br><span class="line">                <span class="built_in">type</span>=np.where(self.labels==i)[<span class="number">0</span>]</span><br><span class="line">                self.result[i]=<span class="built_in">type</span></span><br><span class="line">                <span class="keyword">if</span> <span class="built_in">len</span>(<span class="built_in">type</span>)==<span class="number">0</span>:</span><br><span class="line">                    self.centers[i, :] =<span class="number">0</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    self.centers[i,:]=np.mean(self.data[<span class="built_in">type</span>,:],axis=<span class="number">0</span>)</span><br><span class="line">            count+=<span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> self.centers, self.result,self.labels</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">imshow</span>(<span class="params">self</span>):</span><br><span class="line">        tsne = TSNE(n_components=<span class="number">2</span>, learning_rate=<span class="number">100</span>).fit_transform(self.data)</span><br><span class="line">        pca = PCA().fit_transform(self.data)</span><br><span class="line">        plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">        plt.subplot(<span class="number">121</span>)</span><br><span class="line">        plt.scatter(tsne[:, <span class="number">0</span>], tsne[:, <span class="number">1</span>], c=self.labels)</span><br><span class="line">        plt.title(<span class="string">&#x27;t-SNE&#x27;</span>)</span><br><span class="line">        plt.subplot(<span class="number">122</span>)</span><br><span class="line">        plt.scatter(pca[:, <span class="number">0</span>], pca[:, <span class="number">1</span>], c=self.labels)</span><br><span class="line">        plt.title(<span class="string">&#x27;PCA&#x27;</span>)</span><br><span class="line">        plt.colorbar()</span><br><span class="line">        plt.show()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    iris=datasets.load_iris()</span><br><span class="line">    data=iris.data</span><br><span class="line">    target=iris.target</span><br><span class="line">    kmeans=Kmeans(data,<span class="number">3</span>)</span><br><span class="line">    centers,results,labels=kmeans.fit()</span><br><span class="line">    kmeans.imshow()</span><br><span class="line">    s = silhouette_score(data, labels)</span><br><span class="line">    <span class="built_in">print</span>(centers)</span><br><span class="line">    <span class="built_in">print</span>(results)</span><br><span class="line">    <span class="built_in">print</span>(s)</span><br></pre></td></tr></table></figure><p>KMEANS 处理照片程序 - kmeans_photo.py </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author : JackZhu</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Data : 2021/5/13 12:56</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> sklearn</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> scale</span><br><span class="line"><span class="keyword">from</span> sklearn.manifold <span class="keyword">import</span> TSNE</span><br><span class="line"><span class="keyword">from</span> sklearn.decomposition <span class="keyword">import</span> PCA</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Kmeans</span>():</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,dat,k</span>):</span><br><span class="line">        data=scale(dat)</span><br><span class="line">        self.data=data</span><br><span class="line">        self.row, self.col = data.shape</span><br><span class="line">        self.k=k</span><br><span class="line">        self.centers=np.ndarray((k,self.col))</span><br><span class="line">        choices=random.choices(<span class="built_in">range</span>(self.row),k=k)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(k):</span><br><span class="line">            self.centers[i,:]=self.data[choices[i],:]</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">fit</span>(<span class="params">self,counts=<span class="number">15</span></span>):</span><br><span class="line">        count=<span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span>(count&lt;counts):</span><br><span class="line">            self.labels=np.zeros((self.row))</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.data.shape[<span class="number">0</span>]):</span><br><span class="line">                dis=[]</span><br><span class="line">                <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(self.k):</span><br><span class="line">                    dis.append(np.linalg.norm(self.data[i,:]-self.centers[j,:],axis=<span class="number">0</span>))</span><br><span class="line">                lab=np.argmin(dis,axis=<span class="number">0</span>)</span><br><span class="line">                self.labels[i]=lab</span><br><span class="line">            self.result=&#123;&#125;</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(self.k):</span><br><span class="line">                <span class="built_in">type</span>=np.where(self.labels==i)[<span class="number">0</span>]</span><br><span class="line">                self.result[i]=<span class="built_in">type</span></span><br><span class="line">                <span class="keyword">if</span> <span class="built_in">len</span>(<span class="built_in">type</span>)==<span class="number">0</span>:</span><br><span class="line">                    self.centers[i, :] =<span class="number">0</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    self.centers[i,:]=np.mean(self.data[<span class="built_in">type</span>,:],axis=<span class="number">0</span>)</span><br><span class="line">            count+=<span class="number">1</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> self.centers, self.result</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">imshow</span>(<span class="params">self</span>):</span><br><span class="line">        tsne = TSNE(n_components=<span class="number">2</span>, learning_rate=<span class="number">100</span>).fit_transform(self.data)</span><br><span class="line">        pca = PCA().fit_transform(self.data)</span><br><span class="line">        plt.figure(figsize=(<span class="number">12</span>, <span class="number">6</span>))</span><br><span class="line">        plt.subplot(<span class="number">121</span>)</span><br><span class="line">        plt.scatter(tsne[:, <span class="number">0</span>], tsne[:, <span class="number">1</span>], c=self.labels)</span><br><span class="line">        plt.title(<span class="string">&#x27;t-SNE&#x27;</span>)</span><br><span class="line">        plt.subplot(<span class="number">122</span>)</span><br><span class="line">        plt.scatter(pca[:, <span class="number">0</span>], pca[:, <span class="number">1</span>], c=self.labels)</span><br><span class="line">        plt.title(<span class="string">&#x27;PCA&#x27;</span>)</span><br><span class="line">        plt.colorbar()</span><br><span class="line">        plt.show()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">plot_img</span>(<span class="params">self,row,col</span>):</span><br><span class="line">        img=self.labels.reshape(row,col)</span><br><span class="line">        im = Image.new(<span class="string">&quot;RGB&quot;</span>, (row, col))  <span class="comment"># 创建图片</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(row):</span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(col):</span><br><span class="line">                <span class="keyword">if</span> img[i, j] == <span class="number">0</span>:</span><br><span class="line">                    im.putpixel((i, j), (<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>))</span><br><span class="line">                <span class="keyword">if</span> img[i, j] == <span class="number">1</span>:</span><br><span class="line">                    im.putpixel((i, j), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>))</span><br><span class="line">                <span class="keyword">if</span> img[i, j] == <span class="number">2</span>:</span><br><span class="line">                    im.putpixel((i, j), (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>))</span><br><span class="line">        im.show()</span><br><span class="line">        im.save(<span class="string">&#x27;result.jpg&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">path=<span class="string">&#x27;./2.bmp&#x27;</span></span><br><span class="line"><span class="comment"># path=f&#x27;H:/Python_code/Pattern Recognition/kmeans/kmeans图片/3.bmp&#x27;</span></span><br><span class="line">file=Image.<span class="built_in">open</span>(path,<span class="string">&#x27;r&#x27;</span>)</span><br><span class="line">file=np.array(file)</span><br><span class="line">row,col,_=file.shape</span><br><span class="line">data=file.reshape(-<span class="number">1</span>,<span class="number">3</span>)</span><br><span class="line">kmeans=Kmeans(data,<span class="number">3</span>)</span><br><span class="line">centers,results=kmeans.fit(<span class="number">10</span>)</span><br><span class="line">kmeans.imshow()</span><br><span class="line">kmeans.plot_img(row,col)</span><br><span class="line"><span class="built_in">print</span>(centers)</span><br><span class="line"><span class="built_in">print</span>(results)</span><br></pre></td></tr></table></figure><h2 id="B-DBSCAN-程序代码"><a href="#B-DBSCAN-程序代码" class="headerlink" title="B DBSCAN 程序代码"></a>B DBSCAN 程序代码</h2><p>DBSCAN 程序 - 密度聚类.py</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author : JackZhu</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Data : 2021/5/9 15:16</span></span><br><span class="line"><span class="comment"># 调用科学计算包与绘图包</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> scipy.io <span class="keyword">as</span> scio</span><br><span class="line"><span class="keyword">import</span> matplotlib.colors <span class="keyword">as</span> mcolors</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> metrics</span><br><span class="line"><span class="keyword">import</span> sklearn</span><br><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> StandardScaler</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> silhouette_score, calinski_harabasz_score, davies_bouldin_score</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calDist</span>(<span class="params">X1, X2</span>):</span><br><span class="line">    <span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> x1, x2 <span class="keyword">in</span> <span class="built_in">zip</span>(X1, X2):</span><br><span class="line">        <span class="built_in">sum</span> += (x1 - x2) ** <span class="number">2</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sum</span> ** <span class="number">0.5</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getNeibor</span>(<span class="params">data, dataSet, e</span>):</span><br><span class="line">    res = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(dataSet.shape[<span class="number">0</span>]):</span><br><span class="line">        <span class="keyword">if</span> calDist(data, dataSet[i]) &lt; e:</span><br><span class="line">            res.append(i)</span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">DBSCAN</span>(<span class="params">dataSet, e, minPts</span>):</span><br><span class="line">    coreObjs = &#123;&#125;  <span class="comment"># 初始化核心对象集合</span></span><br><span class="line">    C = &#123;&#125;</span><br><span class="line">    n = dataSet.shape[<span class="number">0</span>]</span><br><span class="line">    <span class="comment"># 找出所有核心对象，key是核心对象的index，value是ε-邻域中对象的index</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line">        neibor = getNeibor(dataSet[i], dataSet, e)</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(neibor) &gt;= minPts:</span><br><span class="line">            coreObjs[i] = neibor</span><br><span class="line">    oldCoreObjs = coreObjs.copy()</span><br><span class="line">    k = <span class="number">0</span>  <span class="comment"># 初始化聚类簇数</span></span><br><span class="line">    notAccess = <span class="built_in">list</span>(<span class="built_in">range</span>(n))  <span class="comment"># 初始化未访问样本集合（索引）</span></span><br><span class="line">    <span class="keyword">while</span> <span class="built_in">len</span>(coreObjs) &gt; <span class="number">0</span>:</span><br><span class="line">        OldNotAccess = []</span><br><span class="line">        OldNotAccess.extend(notAccess)</span><br><span class="line">        cores = coreObjs.keys()</span><br><span class="line">        <span class="comment"># 随机选取一个核心对象</span></span><br><span class="line">        randNum = random.randint(<span class="number">0</span>, <span class="built_in">len</span>(cores) - <span class="number">1</span>)</span><br><span class="line">        cores = <span class="built_in">list</span>(cores)</span><br><span class="line">        core = cores[randNum]</span><br><span class="line">        queue = []</span><br><span class="line">        queue.append(core)</span><br><span class="line">        notAccess.remove(core)</span><br><span class="line">        <span class="keyword">while</span> <span class="built_in">len</span>(queue) &gt; <span class="number">0</span>:</span><br><span class="line">            q = queue[<span class="number">0</span>]</span><br><span class="line">            <span class="keyword">del</span> queue[<span class="number">0</span>]</span><br><span class="line">            <span class="keyword">if</span> q <span class="keyword">in</span> oldCoreObjs.keys():</span><br><span class="line">                delte = [val <span class="keyword">for</span> val <span class="keyword">in</span> oldCoreObjs[q] <span class="keyword">if</span> val <span class="keyword">in</span> notAccess]  <span class="comment"># Δ = N(q)∩Γ</span></span><br><span class="line">                queue.extend(delte)  <span class="comment"># 将Δ中的样本加入队列Q</span></span><br><span class="line">                notAccess = [val <span class="keyword">for</span> val <span class="keyword">in</span> notAccess <span class="keyword">if</span> val <span class="keyword">not</span> <span class="keyword">in</span> delte]  <span class="comment"># Γ = Γ\Δ</span></span><br><span class="line">        k += <span class="number">1</span></span><br><span class="line">        C[k] = [val <span class="keyword">for</span> val <span class="keyword">in</span> OldNotAccess <span class="keyword">if</span> val <span class="keyword">not</span> <span class="keyword">in</span> notAccess]</span><br><span class="line">        <span class="keyword">for</span> x <span class="keyword">in</span> C[k]:</span><br><span class="line">            <span class="keyword">if</span> x <span class="keyword">in</span> coreObjs.keys():</span><br><span class="line">                <span class="keyword">del</span> coreObjs[x]</span><br><span class="line">    <span class="keyword">return</span> C</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">draw</span>(<span class="params">C, D</span>):</span><br><span class="line">    colors = <span class="built_in">list</span>(mcolors.TABLEAU_COLORS.keys())</span><br><span class="line">    predict = np.zeros((D.shape[<span class="number">0</span>], D.shape[<span class="number">1</span>] + <span class="number">1</span>))</span><br><span class="line">    j = <span class="number">0</span></span><br><span class="line">    keys = C.keys()</span><br><span class="line">    <span class="built_in">print</span>(keys)</span><br><span class="line">    <span class="keyword">for</span> k <span class="keyword">in</span> keys:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> C[k]:</span><br><span class="line">            predict[j, <span class="number">0</span>:<span class="number">2</span>] = D[i]</span><br><span class="line">            predict[j, <span class="number">2</span>] = k</span><br><span class="line">            j = j + <span class="number">1</span></span><br><span class="line">            plt.scatter(D[i, <span class="number">0</span>], D[i, <span class="number">1</span>], color=colors[k + <span class="number">1</span>])</span><br><span class="line">    plt.show()</span><br><span class="line">    <span class="keyword">return</span> predict</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    path = <span class="string">&#x27;./data-密度聚类/square1.mat&#x27;</span></span><br><span class="line">    data = scio.loadmat(path)[<span class="string">&#x27;square1&#x27;</span>]</span><br><span class="line">    <span class="comment"># plt.scatter(data[:,0],data[:,1])</span></span><br><span class="line">    <span class="comment"># plt.show()</span></span><br><span class="line">    D = data[:, <span class="number">0</span>:<span class="number">2</span>]</span><br><span class="line">    label = data[<span class="number">2</span>]</span><br><span class="line">    C = DBSCAN(D, <span class="number">0.9</span>, <span class="number">15</span>)</span><br><span class="line">    predict = draw(C, D)</span><br><span class="line">    s1 = metrics.silhouette_score(predict[:, <span class="number">0</span>:<span class="number">2</span>], predict[:, <span class="number">2</span>], metric=<span class="string">&#x27;euclidean&#x27;</span>)</span><br><span class="line">    s2 = calinski_harabasz_score(predict[:, <span class="number">0</span>:<span class="number">2</span>], predict[:, <span class="number">2</span>])  <span class="comment"># 计算CH score</span></span><br><span class="line">    s3 = davies_bouldin_score(predict[:, <span class="number">0</span>:<span class="number">2</span>], predict[:, <span class="number">2</span>])  <span class="comment"># 计算 DBI</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(s1, s2, s3)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/05/23/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B8%8A%E6%9C%BA%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E8%81%9A%E7%B1%BB%E5%88%86%E6%9E%90/</id>
    <link href="http://jackzhu.top/2021/05/23/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B8%8A%E6%9C%BA%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E8%81%9A%E7%B1%BB%E5%88%86%E6%9E%90/"/>
    <published>2021-05-23T12:29:37.000Z</published>
    <summary>本文使用了 K-means 和 DBSCAN 两种聚类方法, 较好的完成了聚类任务, 并将得到的结果使用了 t-SNE 和 PCA 两种方法进行降维可视化,从而更好的得到聚类的效果,并计算了轮廓系数、 \(\mathrm{CH}\) score、 DBI 这些聚类指标, 对于 K-means 方法还使用了图片进行聚类, 使得效果更加直观, 实验效果非常好。</summary>
    <title>机器学习上机报告——聚类分析</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="机器学习" scheme="http://jackzhu.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="机器学习" scheme="http://jackzhu.top/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 实验说明 1<br>2 数据集 1<br>3 特征选择 2<br>3.1 基本方法 2<br>3.2 单变量选择法 2<br>3.3 递归特征消除法 2<br>4 代码实现 2<br>4.1 获取数据 2<br>4.2 数据预处理 3<br>4.3 定义单变量选择函数 3<br>4.4 定义递归特征选择法函数 4<br>4.5 定义获得数据函数 4<br>4.6 定义评定函数 5<br>4.7 主函数 5<br>5 结果分析 6<br>6 优缺点分析 6<br>6.1 Filter 6<br>6.2 Wrapper 7<br>7 总结 7<br>A 作业代码 8</p><h2 id="1-实验说明"><a href="#1-实验说明" class="headerlink" title="1 实验说明"></a>1 实验说明</h2><p>本次实验的目标为利用提供的高光谱遥感数据集进行特征选择, 有以下实验要求:</p><ul><li>利用给定的数据集, 进行数据特征 (波段) 选择。</li><li>具体选择方法和策略不限制。</li><li>实验结果度量标准不少于 4 种, 结合课程学习中的指标。</li><li>建议对比不同类型的选择方法, 给出各种方法的优缺点。<br>数据集给出的数据维数较高, 其中存在了许多冗余的信息以及无关信息, 如果把这些实际的数据直接放到神经网络中则很难得出较好的结果, 而且计算量大大增加, 因此需要进行特征提取, 把影响实验效果的特征清除掉, 用较少的特征对实验结果进行分析。</li></ul><h2 id="2-数据集"><a href="#2-数据集" class="headerlink" title="2 数据集"></a>2 数据集</h2><p>在本次实验中, 提供了多种数据可以选择, 由于目标在于实现过程, 数据集选择无关,<br>此处选择了肯尼迪航天中心数据 KSC。<br>NASA AVIRIS (机载可见光&#x2F;红外成像光谱仪) 仪器于 1996 年 3 月 23 日在佛罗里达州肯尼迪航天中心 (KSC) 上空获取数据。AVIRIS 采集 224 个波段的数据, 这些波段的宽度为 10 纳米, 中心波长为 400-2500 纳米。从大约 20 公里的高度获得的 KSC 数据具有 18 米的空间分辨率。去除吸水率和低信噪比波段后, 用 176 个波段进行分析。训练数据是利用肯尼迪航天中心提供的彩色红外摄影和陆地卫星专题制图仪 (TM) 图像绘制的土地覆盖图选择的。植被分类方案是由 KSC 人员制定的, 目的是确定在陆地卫星和这些 AVIRIS 数据的空间分辨率上可辨别的功能类型。由于某些植被类型的光谱特征具有相似性, 因此很难区分这种环境下的土地覆盖。为便于分类, 为现场定义了 13 个类别, 代表该环境中出现的各种土地覆盖类型。<br>数据集预览如下图所示:</p><img src="https://cdn.noedgeai.com/34077bd4-b209-4949-97d0-44f33cab3459_2.jpg?x=251&y=680&w=342&h=294 "/><p>图 1: KSC 数据预览图</p><p>数据集尺寸为 $512 \times 614 \times 176$, 大小为 $56.8\mathrm{MB}$, 相应的结果尺寸为 $512 \times 614$, 大小为 $3.2\mathrm{KB}$。</p><h2 id="3-特征选择"><a href="#3-特征选择" class="headerlink" title="3 特征选择"></a>3 特征选择</h2><h2 id="3-1-基本方法"><a href="#3-1-基本方法" class="headerlink" title="3.1 基本方法"></a>3.1 基本方法</h2><p>一个典型机器学习问题是通过样本特征预测对应的值, 如果样本特征少, 可以增加特征, 而有时候特征较多, 则需要较少一些特征, 较少过拟合, 提高模型泛化能力, 加快模型训练速度并获得更好的性能,<br>特征选择主要有三种选择方法:</p><ol><li>过滤法 (Filter) : 按照发散性或者相关性对各个特征进行评分, 设定阈值或者待选择阈值的个数, 选择特征。</li><li>包裹法 (Wrapper): 根据目标函数, 每次选择若干特征或者排除若干特征, 直到选择出最佳的子集。</li><li>嵌入法 (Embedding): 先使用某些机器学习的算法和模型进行训练, 得到各个特征的权值系数, 根据系数从大到小选择特征。类似于 Filter 方法, 但是是通过训练来确定特征的优劣。</li></ol><h2 id="3-2-单变量选择法"><a href="#3-2-单变量选择法" class="headerlink" title="3.2 单变量选择法"></a>3.2 单变量选择法</h2><p>单变量特征选择是通过选择那些基于单变量统计检验 (univariate statistical tests) 得出的最优特征来实现的, 这是 Filter 法的一种。它可以看作是估计器的一个预处理步骤。这里使用了 Sklearn 中的 Select KBest 对每个特征进行评分, 并选择出指定数目的特征, 从而达到选择的效果, 这种方法并未考虑到不同特征之间的相互关系, 本题中选择了 50 个特征。</p><h2 id="3-3-递归特征消除法"><a href="#3-3-递归特征消除法" class="headerlink" title="3.3 递归特征消除法"></a>3.3 递归特征消除法</h2><p>递归特征消除 (Recursive feature elimination) 是 Wrapper 法中的一种, 其主要思想是反复构建模型, 然后选出最好的 (或者最差的) 特征 (根据系数来选), 把选出来的特征放到一边, 然后在剩余的特征上重复这个过程, 直到遍历了所有的特征。在这个过程中被消除的次序就是特征的排序, 本题中选择了 30 个特征的组合。<br>RFE 的稳定性很大程度上取决于迭代时, 底层用的哪种模型。比如 RFE 采用的是普通的回归 (LR), 没有经过正则化的回归是不稳定的, 那么 RFE 就是不稳定的。假如采用的是 Lasso&#x2F;Ridge, 正则化的回归是稳定的, 那么 RFE 就是稳定的。</p><h2 id="4-代码实现"><a href="#4-代码实现" class="headerlink" title="4 代码实现"></a>4 代码实现</h2><h2 id="4-1-获取数据"><a href="#4-1-获取数据" class="headerlink" title="4.1 获取数据"></a>4.1 获取数据</h2><p>此处建立函数读取获取数据并将数据从三维降到二维, 将对应的标签从二维降到一维,<br>降维后数据的尺寸为 (314368 {\times} 176) 。<br>其中一个重要的问题是提供的大部分数据是无用的, 即数据对应的标签为 0 , 因此这里<br>仅提取出标签不为零的数据,提取后数据尺寸为 (5211 {\times} 176) ,相关代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><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 class="keyword">from</span> scipy.io <span class="keyword">import</span> loadmat</span><br><span class="line"></span><br><span class="line"><span class="comment"># Function to load and preprocess the data</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_data</span>():</span><br><span class="line">    <span class="comment"># Load the hyperspectral dataset</span></span><br><span class="line">    dat = loadmat(<span class="string">&#x27;./高光谱数据集/KSC.mat&#x27;</span>)[<span class="string">&#x27;KSC&#x27;</span>]</span><br><span class="line">    lab = loadmat(<span class="string">&#x27;./高光谱数据集/KSC_gt.mat&#x27;</span>)[<span class="string">&#x27;KSC_gt&#x27;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Reshape the data and labels</span></span><br><span class="line">    dat = dat.reshape((-<span class="number">1</span>, <span class="number">176</span>))</span><br><span class="line">    lab = lab.reshape((-<span class="number">1</span>))</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(dat.shape)  <span class="comment"># Print the shape of the data</span></span><br><span class="line">    </span><br><span class="line">    data, label = [], []</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Filter out data with non-zero labels</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(dat.shape[<span class="number">0</span>]):</span><br><span class="line">        <span class="keyword">if</span> lab[i] != <span class="number">0</span>:  <span class="comment"># Correct condition: check if the label is not 0</span></span><br><span class="line">            data.append(dat[i, :])</span><br><span class="line">            label.append(lab[i])</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Convert lists to numpy arrays</span></span><br><span class="line">    data = np.array(data)</span><br><span class="line">    label = np.array(label)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> data, label</span><br></pre></td></tr></table></figure><h2 id="4-2-数据预处理"><a href="#4-2-数据预处理" class="headerlink" title="4.2 数据预处理"></a>4.2 数据预处理</h2><p>这里首先利用 sklearn 中的 processing 对数据进行标准化处理, 然后消除方差为 0 的<br>特征, 并利用中位数进行变量的选择。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> StandardScaler</span><br><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> VarianceThreshold</span><br><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"># Function to process the data</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">data</span>):</span><br><span class="line">    <span class="comment"># Standardize the data</span></span><br><span class="line">    data = StandardScaler().fit_transform(data)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;shape=&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(data.shape))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Apply VarianceThreshold (default threshold is 0)</span></span><br><span class="line">    selector = VarianceThreshold()  <span class="comment"># Instantiate, default threshold 0</span></span><br><span class="line">    data = selector.fit_transform(data)</span><br><span class="line">    <span class="built_in">print</span>(data.shape)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Calculate the median variance and apply VarianceThreshold with it</span></span><br><span class="line">    median_num = np.median(np.var(data, axis=<span class="number">0</span>))  <span class="comment"># Calculate the median variance</span></span><br><span class="line">    data = VarianceThreshold(median_num).fit_transform(data)</span><br><span class="line">    <span class="built_in">print</span>(data.shape)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> data</span><br></pre></td></tr></table></figure><h2 id="4-3-定义单变量选择函数"><a href="#4-3-定义单变量选择函数" class="headerlink" title="4.3 定义单变量选择函数"></a>4.3 定义单变量选择函数</h2><p>此处定义了单变量选择函数, 利用 SelectKBest 进行评分, 得到不同特征的得分以及<br>pvalue, 并得出是否选择, 并得到选出的 50 个特征的索引。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> SelectKBest, f_classif</span><br><span class="line"></span><br><span class="line"><span class="comment"># Function to select k best features</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">select_k</span>(<span class="params">data, label, k</span>):</span><br><span class="line">    <span class="comment"># Apply SelectKBest with f_classif scoring</span></span><br><span class="line">    results = SelectKBest(f_classif, k=k).fit(data, label)</span><br><span class="line">    <span class="built_in">print</span>(results)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Create a DataFrame to store feature scores, p-values, and selection status</span></span><br><span class="line">    features = pd.DataFrame(&#123;</span><br><span class="line">        <span class="string">&quot;score&quot;</span>: results.scores_,</span><br><span class="line">        <span class="string">&quot;pvalue&quot;</span>: results.pvalues_,</span><br><span class="line">        <span class="string">&quot;select&quot;</span>: results.get_support()</span><br><span class="line">    &#125;)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Sort the features by score in descending order</span></span><br><span class="line">    features = features.sort_values(<span class="string">&quot;score&quot;</span>, ascending=<span class="literal">False</span>)</span><br><span class="line">    <span class="built_in">print</span>(features)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Get the indices of the selected features</span></span><br><span class="line">    index = results.get_support(indices=<span class="literal">True</span>)</span><br><span class="line">    <span class="built_in">print</span>(index)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> index</span><br></pre></td></tr></table></figure><p>此处简要列出几种特征得分、pvalue 以及选择情况如下图所示。</p><table><thead><th>Index</th><th>score</th><th>pvalue</th><th>select</th></thead><tr><td>128</td><td>2727.83</td><td>0</td><td>True</td></tr><tr><td>129</td><td>2721.86</td><td>0</td><td>True</td></tr><tr><td>130</td><td>2713.15</td><td>0</td><td>True</td></tr><tr><td>131</td><td>3.10662</td><td>0.000214373</td><td>False</td></tr><tr><td>132</td><td>8.8046</td><td>6.66051e-17</td><td>False</td></tr><tr><td>133</td><td>1.56247</td><td>0.0951564</td><td>False</td></tr><tr><td>134</td><td>2085.42</td><td>0</td><td>False</td></tr><tr><td>135</td><td>2101.52</td><td>0</td><td>False</td></tr><tr><td>136</td><td>2107.9</td><td>0</td><td>False</td></tr><tr><td>137</td><td>2116.73</td><td>0</td><td>False</td></tr><tr><td>138</td><td>1.36264</td><td>0.17608</td><td>False</td></tr><tr><td>139</td><td>2165.66</td><td>日</td><td>True</td></tr><tr><td>140</td><td>2219.98</td><td>®</td><td>True</td></tr><tr><td>141</td><td>1.06688</td><td>0.383812</td><td>False</td></tr></table><p>图 2: 特征得分预览图</p><p> 4.4 定义递归特征选择法函数<br>此处同样获取索引, 在本题中, 选择了 30 个特征的组合进行特征选择, 代码如下。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> RFE</span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LogisticRegression</span><br><span class="line"></span><br><span class="line"><span class="comment"># Function to perform Recursive Feature Elimination (RFE)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rfe</span>(<span class="params">data, label, n</span>):</span><br><span class="line">    <span class="comment"># Initialize RFE with LogisticRegression as the estimator and select &#x27;n&#x27; features</span></span><br><span class="line">    results = RFE(estimator=LogisticRegression(), n_features_to_select=n)</span><br><span class="line">    <span class="built_in">print</span>(results)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Fit RFE to the data and labels</span></span><br><span class="line">    results.fit(data, label)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Get the indices of the selected features</span></span><br><span class="line">    index = results.get_support(indices=<span class="literal">True</span>)</span><br><span class="line">    <span class="built_in">print</span>(index)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> index</span><br></pre></td></tr></table></figure><h2 id="4-5-定义获得数据函数"><a href="#4-5-定义获得数据函数" class="headerlink" title="4.5 定义获得数据函数"></a>4.5 定义获得数据函数</h2><p>此函数目的为获取指定索引特征的数据并划分训练测试集, 即将上文中选出的指定特征索引数据选出来。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><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 class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"></span><br><span class="line"><span class="comment"># Function to select specific features based on the provided indices and split the data</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">select_index_data</span>(<span class="params">index, data, label</span>):</span><br><span class="line">    <span class="comment"># Initialize an empty list to store selected features</span></span><br><span class="line">    data_after = []</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Append selected features (columns) from the data based on the indices</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> index:</span><br><span class="line">        data_after.append(data[:, i])</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Convert the list of selected features into a NumPy array and transpose it</span></span><br><span class="line">    data_after = np.array(data_after).transpose()</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(data_after.shape)  <span class="comment"># Print the shape of the selected data</span></span><br><span class="line">    <span class="built_in">print</span>(label.shape)  <span class="comment"># Print the shape of the labels</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Split the selected data and labels into training and test sets</span></span><br><span class="line">    <span class="keyword">return</span> train_test_split(data_after, label, test_size=<span class="number">0.3</span>, random_state=<span class="number">1</span>)</span><br></pre></td></tr></table></figure><h2 id="4-6-定义评定函数"><a href="#4-6-定义评定函数" class="headerlink" title="4.6 定义评定函数"></a>4.6 定义评定函数</h2><p>此处利用 SVM 来测定特征选择的效果, 并利用 sklearn 中的库函数来进行评定, 相关<br>代码如下。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.svm <span class="keyword">import</span> SVC</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> metrics</span><br><span class="line"></span><br><span class="line"><span class="comment"># Function to measure the performance of the SVM classifier</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">measure_feature</span>(<span class="params">train_data, test_data, train_label, test_label, gamma, c</span>):</span><br><span class="line">    <span class="comment"># Initialize the SVM classifier with a polynomial kernel</span></span><br><span class="line">    clf = SVC(kernel=<span class="string">&#x27;poly&#x27;</span>, gamma=gamma, C=c)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Train the classifier</span></span><br><span class="line">    clf.fit(train_data, train_label)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Predict the labels for the test data</span></span><br><span class="line">    predict = clf.predict(test_data)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Get model parameters (if needed)</span></span><br><span class="line">    clf.get_params(deep=<span class="literal">True</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Calculate performance metrics</span></span><br><span class="line">    acc = metrics.accuracy_score(test_label, predict)</span><br><span class="line">    f1 = metrics.f1_score(test_label, predict, average=<span class="string">&#x27;micro&#x27;</span>)</span><br><span class="line">    recall = metrics.recall_score(test_label, predict, average=<span class="string">&#x27;micro&#x27;</span>)</span><br><span class="line">    precision = metrics.precision_score(test_label, predict, average=<span class="string">&#x27;micro&#x27;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> acc, f1, recall, precision</span><br></pre></td></tr></table></figure><h2 id="4-7-主函数"><a href="#4-7-主函数" class="headerlink" title="4.7 主函数"></a>4.7 主函数</h2><p>这部分使用了上文中定义的函数, 首先读取数据, 然后进行数据预处理, 之后分别利用这两种方法, 并提取出特征对应的索引, 之后划分出训练集和测试集来对特征提取的结果进行测试, 最后分别展示出分别用分类准确率, f1 分数, 召回率, 精确度等指标来测试训练集和测试集的效果, 代码如下。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    <span class="comment"># Load and preprocess the data</span></span><br><span class="line">    data, label = get_data()</span><br><span class="line">    data = process(data)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Select features using SelectKBest or RFE</span></span><br><span class="line">    <span class="comment"># index = rfe(data, label, n=30)  # Uncomment if using RFE</span></span><br><span class="line">    index = select_k(data, label, k=<span class="number">50</span>)  <span class="comment"># Using SelectKBest with k=50</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Select train and test data based on selected features</span></span><br><span class="line">    train_data, test_data, train_label, test_label = select_index_data(index, data, label)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Print shapes of train and test sets</span></span><br><span class="line">    <span class="built_in">print</span>(train_data.shape, test_data.shape, train_label.shape, test_label.shape)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Define hyperparameters for the SVM classifier</span></span><br><span class="line">    gamma, c = <span class="number">0.125</span>, <span class="number">60</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Measure performance on the training data</span></span><br><span class="line">    train_acc, train_f1, train_recall, train_precision = measure_feature(</span><br><span class="line">        train_data, train_data, train_label, train_label, gamma, c</span><br><span class="line">    )</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Measure performance on the test data</span></span><br><span class="line">    test_acc, test_f1, test_recall, test_precision = measure_feature(</span><br><span class="line">        train_data, test_data, train_label, test_label, gamma, c</span><br><span class="line">    )</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Print training and test accuracy</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Training Accuracy: <span class="subst">&#123;train_acc&#125;</span>, Test Accuracy: <span class="subst">&#123;test_acc&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Print other performance metrics</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Training F1 Score: <span class="subst">&#123;train_f1&#125;</span>, Test F1 Score: <span class="subst">&#123;test_f1&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Training Recall: <span class="subst">&#123;train_recall&#125;</span>, Test Recall: <span class="subst">&#123;test_recall&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Training Precision: <span class="subst">&#123;train_precision&#125;</span>, Test Precision: <span class="subst">&#123;test_precision&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h2 id="5-结果分析"><a href="#5-结果分析" class="headerlink" title="5 结果分析"></a>5 结果分析</h2><p>表 1: 训练测试结果</p><table><thead><th></th><th>accuracy</th><th>f1 score</th><th>recall</th><th>precision</th></thead><tr><td>UFS 训练集</td><td>0.943</td><td>0.943</td><td>0.943</td><td>0.943</td></tr><tr><td>UFS 测试集</td><td>0.902</td><td>0.902</td><td>0.902</td><td>0.902</td></tr><tr><td>RFE 训练集</td><td>0.994</td><td>0.994</td><td>0.994</td><td>0.994</td></tr><tr><td>RFE 测试集</td><td>0.948</td><td>0.948</td><td>0.948</td><td>0.948</td></tr></table><p>由上表可知, RFE 在选择的特征数少于 UFS 的情况下, 效果仍好于 UFS。在本数据集下, 四种指标结果相同。对于 UFS 法, 优点是直观, 可解释性更好, 但是最优的组合效果并不一定是最好的。对于 RFE 法, 计算量更大, 需要考虑不同组合的效果。递归式特征消除的主要思路是反复建立多种模型, 每一次根据系数的不挑出差的特征, 并去除挑出来的特征, 然后在剩余的特征上重复该过程, 直到遍历了所有的特征。</p><h2 id="6-优缺点分析"><a href="#6-优缺点分析" class="headerlink" title="6 优缺点分析"></a>6 优缺点分析</h2><h2 id="6-1-Filter"><a href="#6-1-Filter" class="headerlink" title="6.1 Filter"></a>6.1 Filter</h2><p>过滤式特征选择的评价标准从数据集本身的内在性质获得, 与特定的学习算法无关, 因此具有具有较好的通用性。通常选择和类别相关度大的特征或者特征子集。过滤式特征选择的研究者认为, 相关度较大的特征或者特征自己会在分类器上获得较高的准确率, dash 和 liu 把过滤式特征选择的评价标准分为四种, 即距离度量, 信息度量, 关联度量以及一致性度量<br>优点: 算法的通用性强, 省去了分类器的训练步骤, 算法复杂性低, 因而适用于大规模<br>数据集, 可以快速去除大量不相关的特征, 作为特征的预筛选器非常合适的<br>缺点: 由于算法的评价标准独立于特定的学习算法, 所选的特征子集在分类准确率方<br>面通常低于 wrapper 方法。</p><h2 id="6-2-Wrapper"><a href="#6-2-Wrapper" class="headerlink" title="6.2 Wrapper"></a>6.2 Wrapper</h2><p>封装式特征选择即 wrapper 方法利用学习算法的性能来评价特征自己的优劣, 因此, 对<br>于一个待评价的特征子集, wrapper 方法需要<br>训练一个分类器, 根据分类器的性能对该特征子集进行评价, wrapper 方法中用以评价特征的学习算法是多种多样的, 例如决策树、神经网路、贝叶斯分类器、近邻法以及支持向量机等等, 本文就使用的支持向量机来进行评价。<br>优点: 相对于 filter 方法, wrapper 方法找到的特征子集分类性能通常更好<br>缺点: wrapper 方法选出的特征通用性不强, 当改变学习算法时, 需要针对该学习算法重新进行特征选择, 由于每次对子集的评价都要进行分类器的训练和测试, 所以算法计算复杂度很高, 尤其对于大规模数据集来说, 算法的执行时间越长。</p><h2 id="7-总结"><a href="#7-总结" class="headerlink" title="7 总结"></a>7 总结</h2><p>在这次实验中, 开始在理解题意方面遇到了很多问题, 后来经过多方询问才明白特征提取的几种方法如何实现。这次实验中我通过广泛查询资料了解到了相关的知识, 也认真写代码来完成任务, 这份作业的完成确实比较艰巨, 一份顶多份, 但是我还是有很大的收获, 能力也得到了提升。</p><h2 id="A-作业代码"><a href="#A-作业代码" class="headerlink" title="A 作业代码"></a>A 作业代码</h2><p>程序 - 高光谱.py</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> scipy.io <span class="keyword">import</span> loadmat</span><br><span class="line"><span class="keyword">import</span> sklearn</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> preprocessing</span><br><span class="line"><span class="keyword">from</span> sklearn.ensemble <span class="keyword">import</span> RandomForestClassifier <span class="keyword">as</span> RFC</span><br><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> SelectFromModel</span><br><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> VarianceThreshold</span><br><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> SelectKBest, f_classif</span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LogisticRegression</span><br><span class="line"><span class="keyword">from</span> sklearn.feature_selection <span class="keyword">import</span> RFE</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> confusion_matrix</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> metrics</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_data</span>():</span><br><span class="line">    dat = loadmat(<span class="string">&#x27;./高光谱数据集/KSC.mat&#x27;</span>)[<span class="string">&#x27;KSC&#x27;</span>]</span><br><span class="line">    lab = loadmat(<span class="string">&#x27;./高光谱数据集/KSC_gt.mat&#x27;</span>)[<span class="string">&#x27;KSC_gt&#x27;</span>]</span><br><span class="line"></span><br><span class="line">    dat = dat.reshape(-<span class="number">1</span>, <span class="number">176</span>)</span><br><span class="line">    lab = lab.reshape(-<span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(dat.shape)</span><br><span class="line"></span><br><span class="line">    data, label = [], []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(dat.shape[<span class="number">0</span>]):</span><br><span class="line">        <span class="keyword">if</span> lab[i].<span class="built_in">all</span>() != <span class="number">0</span>:</span><br><span class="line">            data.append(dat[i, :])</span><br><span class="line">            label.append(lab[i])</span><br><span class="line"></span><br><span class="line">    data = np.array(data)</span><br><span class="line">    label = np.array(label)</span><br><span class="line">    <span class="keyword">return</span> data, label</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process</span>(<span class="params">data</span>):</span><br><span class="line">    data = preprocessing.StandardScaler().fit_transform(data)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;shape=&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(data.shape))</span><br><span class="line">    selector = VarianceThreshold()  <span class="comment"># 实例化，不填参数默认方差为0</span></span><br><span class="line">    data = selector.fit_transform(data)</span><br><span class="line">    <span class="built_in">print</span>(data.shape)</span><br><span class="line">    median_num = np.median(data)</span><br><span class="line">    data = VarianceThreshold(median_num).fit_transform(data)</span><br><span class="line">    <span class="built_in">print</span>(data.shape)</span><br><span class="line">    <span class="keyword">return</span> data</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># acc = cross_val_score(KNN(), data, label, cv=5).mean()</span></span><br><span class="line"><span class="comment"># print(&quot;accuracy:&#123;&#125;,time:&#123;&#125;&quot;.format(acc,time.time()-start))</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">select_k</span>(<span class="params">data, label, k</span>):</span><br><span class="line">    results = SelectKBest(f_classif, k=k).fit(data, label)</span><br><span class="line">    <span class="built_in">print</span>(results)</span><br><span class="line">    features = pd.DataFrame(&#123;</span><br><span class="line">        <span class="string">&quot;score&quot;</span>: results.scores_,</span><br><span class="line">        <span class="string">&quot;pvalue&quot;</span>: results.pvalues_,</span><br><span class="line">        <span class="string">&quot;select&quot;</span>: results.get_support()</span><br><span class="line">    &#125;)</span><br><span class="line">    features.sort_values(<span class="string">&quot;score&quot;</span>, ascending=<span class="literal">False</span>)</span><br><span class="line">    <span class="built_in">print</span>(features)</span><br><span class="line">    index = results.get_support(indices=<span class="literal">True</span>)</span><br><span class="line">    <span class="built_in">print</span>(index)</span><br><span class="line">    <span class="keyword">return</span> index</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rfe</span>(<span class="params">data, label, n</span>):</span><br><span class="line">    results = RFE(estimator=LogisticRegression(), n_features_to_select=n)</span><br><span class="line">    <span class="built_in">print</span>(results)</span><br><span class="line">    results.fit(data, label)</span><br><span class="line">    index = results.get_support(indices=<span class="literal">True</span>)</span><br><span class="line">    <span class="built_in">print</span>(index)</span><br><span class="line">    <span class="keyword">return</span> index</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rfc</span>(<span class="params">data, label</span>):</span><br><span class="line">    RFC_ = RFC(n_estimators=<span class="number">50</span>, random_state=<span class="number">0</span>)</span><br><span class="line">    X_embedded = SelectFromModel(RFC_, threshold=<span class="number">0.005</span>).fit_transform(data, label)</span><br><span class="line">    result = sklearn.model_selection.cross_val_score(RFC_, X_embedded, label, cv=<span class="number">5</span>).mean()</span><br><span class="line">    <span class="built_in">print</span>(result)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">select_index_data</span>(<span class="params">index, data, label</span>):</span><br><span class="line">    data_after = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> index:</span><br><span class="line">        data_after.append(data[:, i])</span><br><span class="line">    data_after = np.array(data_after).transpose()</span><br><span class="line">    <span class="built_in">print</span>(data_after.shape)</span><br><span class="line">    <span class="built_in">print</span>(label.shape)</span><br><span class="line">    <span class="keyword">return</span> train_test_split(data_after, label, test_size=<span class="number">0.3</span>, random_state=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">measure_feature</span>(<span class="params">train_data, test_data, train_label, test_label, gamma, c</span>):</span><br><span class="line">    clf = sklearn.svm.SVC(kernel=<span class="string">&#x27;poly&#x27;</span>, gamma=gamma, C=c)</span><br><span class="line">    clf.fit(train_data, train_label)</span><br><span class="line">    predict = clf.predict(test_data)</span><br><span class="line">    clf.get_params(deep=<span class="literal">True</span>)</span><br><span class="line">    acc = sklearn.metrics.accuracy_score(test_label, predict)</span><br><span class="line">    f1 = sklearn.metrics.f1_score(test_label, predict, average=<span class="string">&#x27;micro&#x27;</span>)</span><br><span class="line">    recall = metrics.recall_score(test_label, predict, average=<span class="string">&#x27;micro&#x27;</span>)</span><br><span class="line">    precision = metrics.precision_score(test_label, predict, average=<span class="string">&#x27;micro&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> acc, f1, recall, precision</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    data, label = get_data()</span><br><span class="line">    data = process(data)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># rfc(data,label)</span></span><br><span class="line">    index = select_k(data, label, k=<span class="number">50</span>)</span><br><span class="line">    <span class="comment"># index = rfe(data, label,n=30)</span></span><br><span class="line">    train_data, test_data, train_label, test_label = select_index_data(index, data, label)</span><br><span class="line">    <span class="built_in">print</span>(train_data.shape, test_data.shape, train_label.shape, test_label.shape)</span><br><span class="line">    gamma, c = <span class="number">0.125</span>, <span class="number">60</span></span><br><span class="line">    train_acc, train_f1, train_recall, train_precision = measure_feature(train_data, train_data, train_label,</span><br><span class="line">                                                                         train_label, gamma, c)</span><br><span class="line">    test_acc, test_f1, test_recall, test_precision = measure_feature(train_data, test_data, train_label, test_label,</span><br><span class="line">                                                                     gamma, c)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(train_acc, test_acc)</span><br><span class="line">    <span class="built_in">print</span>(train_f1, test_f1)</span><br><span class="line">    <span class="built_in">print</span>(train_recall, test_recall)</span><br><span class="line">    <span class="built_in">print</span>(train_precision, test_precision)</span><br><span class="line">    <span class="comment"># print(&#x27;训练集准确率为&#123;:.4f&#125;，测试集准确率为&#123;:.4f&#125;&#x27;.format(train_acc, test_acc))</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/04/28/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E9%AB%98%E5%85%89%E8%B0%B1%E9%81%A5%E6%84%9F%E7%89%B9%E5%BE%81%E9%80%89%E6%8B%A9/</id>
    <link href="http://jackzhu.top/2021/04/28/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E9%AB%98%E5%85%89%E8%B0%B1%E9%81%A5%E6%84%9F%E7%89%B9%E5%BE%81%E9%80%89%E6%8B%A9/"/>
    <published>2021-04-28T12:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 实验说明 1<br>2 数据集 1<br>3 特征选择 2<br>3.1 基本方法 2<br>3.2 单变量选择法 2<br>3]]>
    </summary>
    <title>机器学习作业——高光谱遥感特征选择</title>
    <updated>2026-06-10T08:01:07.568Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="计算智能" scheme="http://jackzhu.top/categories/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD/"/>
    <category term="计算智能" scheme="http://jackzhu.top/tags/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD/"/>
    <content>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景知识 1<br>1.1 最优化问题 1<br>1.2 进化算法 1<br>1.3 遗传算法的基本知识 1<br>1.3.1 生物背景 2<br>1.3.2 基本思想 2<br>1.4 遗传算法的组成部分 2<br>1.4.1 编码机制 3<br>1.4.2 种群初始化 3<br>1.4.3 适应度函数 3<br>1.4.4 遗传算子 3<br>2 算法步骤 4<br>3 实验过程 4<br>3.1 代码实现 4<br>4 结果分析 5<br>5 总结 5<br>A 程序代码 7</p><h2 id="1-背景知识"><a href="#1-背景知识" class="headerlink" title="1 背景知识"></a>1 背景知识</h2><h2 id="1-1-最优化问题"><a href="#1-1-最优化问题" class="headerlink" title="1.1 最优化问题"></a>1.1 最优化问题</h2><p>工程设计中最优化问题 (optimization problem) 的一般提法是要选择一组参数 (变量),<br>在满足一系列有关的限制条件 (约束) 下, 使设计指标 (目标) 达到最优值。<br>最优化问题一般包括两方面问题: 线性问题和非线性问题。一方面是线性问题的求解, 主要在经济活动及工程技术中出现。这类问题一般采用单纯形法来求解。另一方面是非线性问题的求解, 这类问题在工程中经常碰到, 是最为常见的一类问题, 尤其是在物理学和决策中, 许多问题常常可以归结为非线性规划问题。这类问题一般需要先建立一个数学模型, 再进行求解。<br>最优化问题的求解实质就是将物理问题数学化, 把最优化问题的求解转化为目标函数<br>最优解的求解, 利用遗传算法在求解最优解方面的特点, 达到事半功倍的效果。</p><h2 id="1-2-进化算法"><a href="#1-2-进化算法" class="headerlink" title="1.2 进化算法"></a>1.2 进化算法</h2><p>进化算法, 或 “演化算法” (evolutionary algorithms) 是一个 “算法簇”, 是在达尔文 (Darwin) 的进化论和孟德尔 (Mendel) 的遗传变异理论的基础上产生的一种在基因和种群层次上模拟自然界生物进化过程与机制, 进行问题求解的自组织、自适应的随机搜索技术。它以达尔文进化论的 “物竞天择、适者生存” 作为算法的进化规则, 并结合孟德尔的遗传变异理论, 将生物进化过程中的繁殖 (Reproduction), 变异 (Mutation), 竞争 (Competition)、选择 (Selection) 引入到了算法中, 是一种对人类智能的演化模拟, 主要有遗传算法、演化策略、演化规划和遗传规划四大分支。<br>尽管它有很多的变化, 有不同的遗传基因表达方式, 不同的交叉和变异算子, 特殊算子的引用, 以及不同的再生和选择方法, 但它们产生的灵感都来自于大自然的生物进化。<br>与传统的基于微积分的方法和穷举法等优化算法相比, 进化计算是一种成熟的具有高鲁棒性和广泛适用性的全局优化方法, 具有自组织、自适应、自学习的特性, 能够不受问题性质的限制, 有效地处理传统优化算法难以解决的复杂问题。</p><h2 id="1-3-遗传算法的基本知识"><a href="#1-3-遗传算法的基本知识" class="headerlink" title="1.3 遗传算法的基本知识"></a>1.3 遗传算法的基本知识</h2><p>遗传算法 (Genetic Algorithm, GA) 是进化算法的一个分支, 是一种模拟自然界生物进化过程的随机搜索算法。其将 “优胜劣汰, 适者生存” 的生物进化原理引入优化参数形成的编码串联群体中, 按所选择的适应度函数并通过遗传中的复制、交叉及变异对个体进行筛选, 使适应度高的个体被保留下来, 组成新的群体。<br>新的群体既继承了上一代的信息, 又优于上一代, 这样周而复始, 群体中个体适应度不断提高, 直到满足一定的条件。同时, 遗传算法的原理简单, 可并行处理, 并能得到全局最优解。<br>遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型, 是一种通过模拟自然进化过程搜索最优解的方法。该算法通过数学的方式, 利用计算机仿真运算, 将问题的求解过程转换成类似生物进化中的染色体基因的交叉、变异等过程。在求解较为复杂的组合优化问题时, 相对一些常规的优化算法, 通常能够较快地获得较好的优化结果。遗传算法已被人们广泛地应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域。<br>遗传算法直接以目标函数值作为搜索信息。传统的优化算法往往不只需要目标函数值,<br>还需要目标函数的导数等其它信息。这样对许多目标函数无法求导或很难求导的函数, 遗传算法就比较方便。<br>同常规算法相比, 遗传算法有以下特点:<br>遗传算法是对决策变量的编码进行操作, 这样提供的参数信息量大, 优化效果好。遗传算法是从许多点开始并行操作, 因而可以有效地防止搜索过程收玫于局部最优解。遗传算法通过目标函数来计算适配值, 而不需要其他推导和附加信息, 从而对问题的依赖性小。遗传算法的寻优规则是由概率决定的, 而非确定性的。遗传算法在解空间进行高效启发式搜索, 而非盲目地穷举或完全随机搜索。遗传算法对于待寻优的函数基本无限制, 因而应用范围较广。遗传算法具有并行计算的特点, 因而可通过大规模并行计算来提高计算速度。遗传算法更适合大规模复杂问题的优化。遗传算法计算简单, 功能强。</p><h2 id="1-3-1-生物背景"><a href="#1-3-1-生物背景" class="headerlink" title="1.3.1 生物背景"></a>1.3.1 生物背景</h2><ul><li>基因: 一个遗传因子。</li><li>染色体: 包含一组的基因。</li><li>个体: 组成种群的单个生物。</li><li>种群: 生物的进化以群体的形式进行, 这样的一个群体称为种群。</li><li>生存竞争, 适者生存: 对环境适应度高的个体参与繁殖的机会比较多, 后代就会越来越多: 适应度低的个体参与繁殖的机会比较少, 后代就会越来越少。</li></ul><h2 id="1-3-2-基本思想"><a href="#1-3-2-基本思想" class="headerlink" title="1.3.2 基本思想"></a>1.3.2 基本思想</h2><ul><li>遗传算法把问题的解表示成 “染色体”, 在算法中即是以一定方式编码的串。</li><li>在执行遗传算法之前, 给出一群 “染色体”, 也即一组候选解 (种群)。</li><li>把这些假设解置于问题的 “环境” 中, 并按适者生存的原则, 从中选择出较适应环境的 “染色体” 进行复制, 再通过交叉, 变异过程产生更适应环境的新一代 “染色体”群。<br>这样, 一代一代地进化, 最后就会收玫到最适应环境的一个 “染色体” 上, 它就是求解<br>到的最优解。</li></ul><h2 id="1-4-遗传算法的组成部分"><a href="#1-4-遗传算法的组成部分" class="headerlink" title="1.4 遗传算法的组成部分"></a>1.4 遗传算法的组成部分</h2><p>一般的遗传算法由四个部分组成: 1. 编码机制 2. 种群初始化 3. 适应度函数<br>4. 遗传算子 (选择、交叉、变异)</p><h2 id="1-4-1-编码机制"><a href="#1-4-1-编码机制" class="headerlink" title="1.4.1 编码机制"></a>1.4.1 编码机制</h2><p>用遗传算法解决问题时, 首先要对待解决问题的模型结构和参数进行编码, 一般用字符串表示。GA 中的编码方法: 二进制编码、浮点数编码方法、格雷码编码、符号编码等。<br>二进制编码所构成的个体的基因型是一个由 0 或 1 组成的编码串,其符号串的长度 $L$ 与问题所要求的求解精度有关。设某一变量的取值范围是 $\left[ X_{\min}, X_{\max} \right]$, $X_{\min} &lt; X_{\max}$ 则二进制编码的编码精度为:</p><p>$$<br>\delta &#x3D; \frac{X_{\max} - X_{\min}}{2^{L} - 1}<br>$$</p><p>如果某一个体的编码是: $b_{L}b_{L - 1}b_{L - 2} \cdots b_{2}b_{1}$, 则对应的解码公式为:</p><p>$$<br>X &#x3D; X_{\min} + \frac{X_{\max} - X_{\min}}{2^{L} - 1} \left( \sum_{i &#x3D; 1}^L b_{i} 2^{i - 1} \right)<br>$$</p><h2 id="1-4-2-种群初始化"><a href="#1-4-2-种群初始化" class="headerlink" title="1.4.2 种群初始化"></a>1.4.2 种群初始化</h2><p>对种群个体的编码进行随机初始化。首先, 我们需要建立种群个体与遗传算法中有效解的对应概念。其次针对于每个个体,在固定长度的染色体下,对每个基因位进行随机 (0&#x2F;1) 赋值, 也就是对有效解进行初始化的过程。</p><h2 id="1-4-3-适应度函数"><a href="#1-4-3-适应度函数" class="headerlink" title="1.4.3 适应度函数"></a>1.4.3 适应度函数</h2><p>遗传算法以个体适应度的大小来评定各个个体的优劣程度, 从而决定其遗传机会的大小。一般情况下, 可以将目标函数作为适应度函数, 也就是求解问题的优化目标。将种群中的个体的染色体进行解码, 可以得到对应的十进制值, 代入适应度函数即可求得适应值。</p><h2 id="1-4-4-遗传算子"><a href="#1-4-4-遗传算子" class="headerlink" title="1.4.4 遗传算子"></a>1.4.4 遗传算子</h2><p>(1)选择操作<br>选择是对当前种群内的所有个体进行筛选, 流出进入到后续繁殖环节的父代个体的过程, 常采用轮盘赌选择。轮盘赌选择依据个体的适应度值计算每个个体在下一代中出现的概率, 并按照此概率随机选择个体构成子代种群。选择某个体的概率为</p><p>$$<br>p(I_{i}) &#x3D; \frac{f(I_{i})}{\sum_{i &#x3D; 1}^N f(I_{i})}<br>$$</p><p>其中, $f(I_{i})$ 是个体 $I_{i}$ 的适应度值, $N$ 是种群大小。轮盘赌选择的流程如下:</p><ul><li>计算种群中所有个体的适应度值之和, $F &#x3D; \sum_{k &#x3D; 1}^N \text{eval}(v_{k})$ ;</li><li>计算每个个体 $v_{k}$ 的选择概率 $p_{k}$, $p_{k} &#x3D; \frac{\text{eval}(v_{k})}{F}$, $k &#x3D; 1, 2, \ldots, \text{popSize}$ ;</li><li>计算每个个体 $v_{k}$ 的累积概率 $q_{k}$, $q_{k} &#x3D; \sum_{j &#x3D; 1}^k p_{j}$, $k &#x3D; 1, 2, \ldots, \text{popSize}$ ;</li><li>随机产生 $N$ 个 $[0,1]$ 的随机数 $r_{i}$ ;</li><li>对于每一个 $r_{i}$ : 如果 $r_{i} \leq q_{1}$, 选择第一个个体 $v_{1}$; 否则, 如果 $q_{k - 1} &lt; r_{i} &lt; q_{k}$, 选择第 $k$ 个个体 $v_{k}(2 \leq k \leq \text{popSize})$ 。</li></ul><h2 id="2-交叉操作"><a href="#2-交叉操作" class="headerlink" title="(2)交叉操作"></a>(2)交叉操作</h2><p>交叉操作是选中的两个父代个体交换莱些基因位形成子代个体的过程。交叉概率 (P_{c}) 是在种群中个体被选择出进行交叉的概率, 一般的交叉方式有单点交叉、两点交叉、多点交叉、均匀交叉等。其中, 单点交叉是随机产生一个有效的交叉位置, 染色体交换位于该交叉位置后的所有基因。<br>(3)变异操作<br>变异操作是编码按小概率扰动产生的变化,类似于基因的突变。变异概率 (P_{m}) 是控制算法中变异操作的使用频率。常用的变异方式有单点变异、均匀变异、高斯变异等。其中, 单点变异指的是对于某一基因位,产生的随机数小于 (P_{m}) 则改变该基因的取值,否则该基因不发生变异, 保持不变。</p><h2 id="2-算法步骤"><a href="#2-算法步骤" class="headerlink" title="2 算法步骤"></a>2 算法步骤</h2><p>该算法可分为以下 7 步。<br>Step1: 初始化种群;<br>Step2: 计算种群中每个个体的适应度值;<br>Step3: 按由个体适应度值决定的某个规则选择将进入下一代的个体; Step4: 按概率 (P_{c}) 进行交叉操作; Step5: 按概率 (P_{m}) 进行变异操作;<br>Step6: 若没有满足某种终止条件, 则转第 2 步, 否则进入下一步; Step7: 输出种群中适应度值最优的染色体作为问题的满意解或最优解。</p><h2 id="3-实验过程"><a href="#3-实验过程" class="headerlink" title="3 实验过程"></a>3 实验过程</h2><p>本文中采用的函数为 (f(x) &#x3D; {\sum}<em>{i &#x3D; 1}^n\left\lbrack x</em>{i}^{2} {-} 10\cos\left( 2\pi x_{i} + 10 \right) \right\rbrack) ,自变量的定义域为 (\lbrack{-}5.12,5.12\rbrack) , 函数的最小值为 0 。</p><h2 id="3-1-代码实现"><a href="#3-1-代码实现" class="headerlink" title="3.1 代码实现"></a>3.1 代码实现</h2><p>作为遗传算法中较为重要的交叉模块, 我们进行了如下设计。首先, 遍历种群中的每一个个体, 将该个体作为父代, 而子代首先得到父代的全部基因, 父代产生子代时不是必然发生交叉, 而是以一定的概率发生交叉。从种群中选择令一个个体, 将该个体作为另一个附带, 随机产生交叉点后, 子代得到此父代交叉点后的全部基因。同时, 此子代也具有相应的变异概率。<br>这里首先确定了相关参数, 然后定义主要的几个函数, 包括三维作图函数、解码函数、适应度计算函数、交叉变异函数, 然后进行操作整合, 并将函数整合起来, 代码见附录。<br>在本代码中, 不仅对遗传算法中不同个体的适应度情况实时展示, 而且对迭代过程中<br>最小的值进行记录, 画出折线图, 从而使得展示过程更加直观。<br><img src="https://cdn.noedgeai.com/e291bc60-ee22-4de1-aca0-eb54cd5a99af_6.jpg?x=242&y=62&w=409&h=397 "/></p><p>图 1: 函数图</p><h2 id="4-结果分析"><a href="#4-结果分析" class="headerlink" title="4 结果分析"></a>4 结果分析</h2><p>上图为运算过程中的函数以及整个种群的情况:<br>当迭代完成后, 可以看到仅函数中心处有一点, 说明整个种群都收玫到了一处, 如下图<br>所示。<br>下图为迭代过程的最小适应度函数值的变化折线图。<br>由图中可以看出, 所求适应度函数最小的值在不断下降, 最终达到稳定值, 而且最终所<br>处数值接近 5 , 与题中一直条件相吻合, 证明了该遗传算法解决问题的效果较好。<br>最终得到最优的基因型为 [1100000000000000000001100100101111110100010100001], 其中 (x) 和 (y) 分别为 0.006485290913897046,0.003113098330086217,此时的值为: 0.01027 。</p><h2 id="5-总结"><a href="#5-总结" class="headerlink" title="5 总结"></a>5 总结</h2><p>在这次实验中, 开始在理解题意方面遇到了很多问题, 后来经过多方询问才明白。这次实验中我通过广泛查询资料了解到了相关的知识, 也认真写代码来完成任务, 这份作业的完成确实比较艰巨, 一份顶多份, 但是我还是有很大的收获, 能力也得到了提升。<br><img src="https://cdn.noedgeai.com/e291bc60-ee22-4de1-aca0-eb54cd5a99af_7.jpg?x=193&y=172&w=415&h=275 "/></p><p>图 2: 函数图</p><img src="https://cdn.noedgeai.com/e291bc60-ee22-4de1-aca0-eb54cd5a99af_7.jpg?x=189&y=643&w=442&h=348 "/><p>图 3: 函数图</p><h2 id="A-程序代码"><a href="#A-程序代码" class="headerlink" title="A 程序代码"></a>A 程序代码</h2><p>遗传算法程序 - GA.py </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> matplotlib <span class="keyword">import</span> cm</span><br><span class="line"><span class="keyword">from</span> mpl_toolkits.mplot3d <span class="keyword">import</span> Axes3D</span><br><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">DNA_SIZE = <span class="number">24</span></span><br><span class="line">POP_SIZE = <span class="number">200</span></span><br><span class="line">CROSSOVER_RATE = <span class="number">0.8</span></span><br><span class="line">MUTATION_RATE1 = <span class="number">0.03</span></span><br><span class="line">MUTATION_RATE2 = <span class="number">0.001</span></span><br><span class="line">N_GENERATIONS = <span class="number">100</span></span><br><span class="line">arr = []</span><br><span class="line"></span><br><span class="line"><span class="comment"># 取值范围</span></span><br><span class="line">X_BOUND = [-<span class="number">5.12</span>, <span class="number">5.12</span>]</span><br><span class="line">Y_BOUND = [-<span class="number">5.12</span>, <span class="number">5.12</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 函数</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">F</span>(<span class="params">x, y</span>):</span><br><span class="line">    <span class="keyword">return</span> x**<span class="number">2</span> - <span class="number">10</span>* np.cos(<span class="number">2</span>*math.pi*x) + <span class="number">10</span> + y**<span class="number">2</span> - <span class="number">10</span>* np.cos(<span class="number">2</span>*math.pi*y) + <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 画图</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">plot_3D</span>(<span class="params">ax</span>):</span><br><span class="line">    X = np.linspace(*X_BOUND, <span class="number">100</span>)</span><br><span class="line">    Y = np.linspace(*Y_BOUND, <span class="number">100</span>)</span><br><span class="line">    X, Y = np.meshgrid(X, Y)</span><br><span class="line">    Z = F(X, Y)</span><br><span class="line">    ax.plot_surface(X, Y, Z, rstride=<span class="number">1</span>, cstride=<span class="number">1</span>, cmap=cm.coolwarm)</span><br><span class="line">    ax.set_zlim(-<span class="number">10</span>, <span class="number">100</span>)</span><br><span class="line">    ax.set_xlabel(<span class="string">&#x27;X&#x27;</span>)</span><br><span class="line">    ax.set_ylabel(<span class="string">&#x27;Y&#x27;</span>)</span><br><span class="line">    ax.set_zlabel(<span class="string">&#x27;Z&#x27;</span>)</span><br><span class="line">    plt.pause(<span class="number">3</span>)</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解码DNA</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">translateDNA</span>(<span class="params">pop</span>):</span><br><span class="line">    x_pop = pop[:, <span class="number">1</span>::<span class="number">2</span>]</span><br><span class="line">    y_pop = pop[:, ::<span class="number">2</span>]</span><br><span class="line"></span><br><span class="line">    x = x_pop.dot(<span class="number">2</span> ** np.arange(DNA_SIZE)[::-<span class="number">1</span>]) / <span class="built_in">float</span>(<span class="number">2</span> ** DNA_SIZE - <span class="number">1</span>) * (X_BOUND[<span class="number">1</span>] - X_BOUND[<span class="number">0</span>]) + X_BOUND[<span class="number">0</span>]</span><br><span class="line">    y = y_pop.dot(<span class="number">2</span> ** np.arange(DNA_SIZE)[::-<span class="number">1</span>]) / <span class="built_in">float</span>(<span class="number">2</span> ** DNA_SIZE - <span class="number">1</span>) * (Y_BOUND[<span class="number">1</span>] - Y_BOUND[<span class="number">0</span>]) + Y_BOUND[<span class="number">0</span>]</span><br><span class="line"></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"># 适应度</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_fitness</span>(<span class="params">pop</span>):</span><br><span class="line">    x, y = translateDNA(pop)</span><br><span class="line">    pred = F(x, y)</span><br><span class="line">    arr.append(np.<span class="built_in">min</span>(pred))</span><br><span class="line">    <span class="keyword">return</span> - (pred - np.<span class="built_in">max</span>(pred)) + <span class="number">1e-3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#淘汰机制</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">select</span>(<span class="params">pop, fitness</span>):</span><br><span class="line">    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=<span class="literal">True</span>, p=fitness/fitness.<span class="built_in">sum</span>())</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> pop[idx]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 交叉变异</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">crossover_and_mutation</span>(<span class="params">pop, CROSSOVER_RATE = <span class="number">0.8</span>, MUTATIONS_RATE = <span class="number">0.01</span></span>):</span><br><span class="line">    new_pop = []</span><br><span class="line">    <span class="keyword">for</span> father <span class="keyword">in</span> pop:</span><br><span class="line">        child = father</span><br><span class="line">        <span class="keyword">if</span> np.random.rand() &lt; CROSSOVER_RATE:</span><br><span class="line">            mother = pop[np.random.randint(POP_SIZE)]</span><br><span class="line">            cross_points = np.random.randint(low=<span class="number">0</span>, high=DNA_SIZE*<span class="number">2</span>)</span><br><span class="line">            child[cross_points:] = mother[cross_points:]</span><br><span class="line">        mutation(child, MUTATIONS_RATE)</span><br><span class="line">        new_pop.append(child)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> new_pop</span><br><span class="line"></span><br><span class="line"><span class="comment"># 变异</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mutation</span>(<span class="params">child, MUTATION_RATE</span>):</span><br><span class="line">    <span class="keyword">if</span> np.random.rand() &lt; MUTATION_RATE:  <span class="comment"># 以MUTATION_RATE的概率进行变异</span></span><br><span class="line">        mutate_point = np.random.randint(<span class="number">0</span>, DNA_SIZE)  <span class="comment"># 随机产生一个实数，代表要变异基因的位置</span></span><br><span class="line">        child[mutate_point] = child[mutate_point] ^ <span class="number">1</span>  <span class="comment"># 将变异点的二进制为反转</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">print_info</span>(<span class="params">pop</span>):</span><br><span class="line">    fitness = get_fitness(pop)</span><br><span class="line">    max_fitness_index = np.argmax(fitness)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;max_fitness:&quot;</span>, fitness[max_fitness_index])</span><br><span class="line">    x,y = translateDNA(pop)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;最优的基因型:&quot;</span>, pop[max_fitness_index])</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;(x, y ):&#x27;</span>, (x[max_fitness_index], y[max_fitness_index]))</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;此时的值为:&quot;</span>, F(x[max_fitness_index], y[max_fitness_index]))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    fig = plt.figure()</span><br><span class="line">    ax = Axes3D(fig)</span><br><span class="line">    plt.ion()</span><br><span class="line">    plot_3D(ax)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 随机生成种群矩阵，奇数列表示X，偶数列表示Y</span></span><br><span class="line">    pop = np.random.randint(<span class="number">2</span>, size=(POP_SIZE, DNA_SIZE * <span class="number">2</span>))</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(N_GENERATIONS):</span><br><span class="line">        x, y = translateDNA(pop)</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&#x27;sca&#x27;</span> <span class="keyword">in</span> <span class="built_in">locals</span>():</span><br><span class="line">            sca.remove()</span><br><span class="line"></span><br><span class="line">        sca = ax.scatter(x, y, F(x, y), c=<span class="string">&#x27;black&#x27;</span>, marker=<span class="string">&#x27;o&#x27;</span>)</span><br><span class="line">        plt.show()</span><br><span class="line">        plt.pause(<span class="number">0.1</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> i &lt; <span class="number">7</span>*i/<span class="number">10</span>:</span><br><span class="line">            pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE, MUTATIONS_RATE = MUTATION_RATE1 ))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE, MUTATIONS_RATE = MUTATION_RATE2 ))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        fitness = get_fitness(pop)</span><br><span class="line">        pop = select(pop, fitness)</span><br><span class="line"></span><br><span class="line">    print_info(pop)</span><br><span class="line">    plt.ioff()</span><br><span class="line">    plot_3D(ax)</span><br><span class="line"></span><br><span class="line">    x = np.arange(<span class="number">0</span>, <span class="number">101</span>)</span><br><span class="line">    plt.xlabel(<span class="string">&#x27;n&#x27;</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;Minimum&#x27;</span>)</span><br><span class="line">    <span class="comment"># plt.ylim((-0.1, 5))  # y坐标的范围</span></span><br><span class="line">    <span class="comment"># 画图</span></span><br><span class="line">    plt.plot(x, arr, <span class="string">&#x27;b&#x27;</span>, marker=<span class="string">&#x27;o&#x27;</span>, markersize=<span class="number">4</span>)</span><br><span class="line">    plt.savefig(<span class="string">&quot;折线图F5_2.png&quot;</span>, dpi=<span class="number">2000</span>)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/04/16/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E5%AF%BC%E8%AE%BA%E4%BD%9C%E4%B8%9A%E2%80%94%E2%80%94%E9%81%97%E4%BC%A0%E7%AE%97%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0/</id>
    <link href="http://jackzhu.top/2021/04/16/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E5%AF%BC%E8%AE%BA%E4%BD%9C%E4%B8%9A%E2%80%94%E2%80%94%E9%81%97%E4%BC%A0%E7%AE%97%E6%B3%95%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
    <published>2021-04-16T12:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 背景知识 1<br>1.1 最优化问题 1<br>1.2 进化算法 1<br>1.3 遗传算法的基本知识 1<br>1.3.1]]>
    </summary>
    <title>计算智能导论作业——遗传算法的实现</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="机器学习" scheme="http://jackzhu.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <category term="机器学习" scheme="http://jackzhu.top/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 数据集 1<br>2 logistic 回归与神经网络 1<br>2.1 背景知识 1<br>2.1.1 线性及 sigmoid 函数 1<br>2.1.2 计算误差及修正参数 1<br>2.2 代码实现及结果分析 2<br>3 高斯判别分析 3<br>3.1 背景知识 3<br>3.2 代码实现 4<br>4 贝叶斯分类 4<br>4.1 背景知识 4<br>4.2 代码实现 4<br>5 性能分析 4<br>6 时效分析 4<br>7 影响因素分析 5<br>7.1 logistic 分类 5<br>8 总结 5<br>A logistic 分类代码 6<br>B GDA 分类代码 8<br>C 贝叶斯分类代码 11<br>D 贝叶斯库函数调用分类代码 12</p><h2 id="1-数据集"><a href="#1-数据集" class="headerlink" title="1 数据集"></a>1 数据集</h2><p>在本次作业中, 在 UCI 中选取了 Sonar 数据集进行分类, 该数据集具有 208 个样本,<br>一共 60 个维度。<br>任务是训练网络以区分反弹的声纳信号从金属圆柱上弹下来和从大致呈圆柱形的岩石弹起。每个模式是一组 60 个数字, 范围在 0.0 到 1.0 之间。每个数字代表在特定时间段内积分的特定频段内的能量。较高频率的积分孔径在时间上较晚出现, 因为这些频率是在线性调频期间稍后传输的。如果对象是岩石,则与每个记录关联的标签包含字母 “ $\mathrm{R}$ ”,如果是地雷 (金属圆柱体) 则包含字母 “M”。标签中的数字按长宽比的高低顺序排列, 但它们不直接对角度进行编码。</p><h2 id="2-logistic-回归与神经网络"><a href="#2-logistic-回归与神经网络" class="headerlink" title="2 logistic 回归与神经网络"></a>2 logistic 回归与神经网络</h2><p>由于 logistic 分类本质为线性求和以及激活函数的作用, 因此这里使用了神经网络框架来实现 logistic 回归, 即神经网络框架只有一个线性层, 然后使用 sigmoid 激活函数, 在结果的判定中对得到的结果进行分类, 即当结果大于 0.5 的时候为一类, 否则为另一类, 即可得出结果, 因此这两种方法同时实现了。</p><h2 id="2-1-背景知识"><a href="#2-1-背景知识" class="headerlink" title="2.1 背景知识"></a>2.1 背景知识</h2><h2 id="2-1-1-线性及-sigmoid-函数"><a href="#2-1-1-线性及-sigmoid-函数" class="headerlink" title="2.1.1 线性及 sigmoid 函数"></a>2.1.1 线性及 sigmoid 函数</h2><p>logistic 分类为一个线性求和和一个 sigmoid 激活组成，假设有一个 $n$ 维的输入列向量 $x$，也有一个 $n$ 维的参数列向量 $h$，还有一个偏置量 $b$ 那么就可以线性求和得到 $z_{\mathrm{s}}$：</p><p>$$<br>z &#x3D; h^T x + b<br>$$</p><p>这个时候值的范围仍是 $({-}\infty, +\infty)$，无法判断出来分类，这个时候就需要一个激活函数来将值进行划分，这里使用的激活函数是 sigmoid 函数：</p><p>$$<br>\sigma(x) &#x3D; \frac{1}{1 + e^{-x}}<br>$$</p><p>其导数有以下规律：</p><p>$$<br>\sigma’(x) &#x3D; \sigma(x)(1 - \sigma(x))<br>$$</p><p>其图像如下图所示：</p><p>$$<br>a &#x3D; \sigma(z) &#x3D; \sigma\left( h^T x + b \right)<br>$$</p><p>这样进行判别，当 $a$ 大于 0.5 的时候，可以判定 $x$ 属于一类，否则属于另一类，即可进行分类。</p><h2 id="2-1-2-计算误差及修正参数"><a href="#2-1-2-计算误差及修正参数" class="headerlink" title="2.1.2 计算误差及修正参数"></a>2.1.2 计算误差及修正参数</h2><p>在凸优化问题中, 可以通过导数为零进行计算。<br>$$\frac{\partial C}{\partial h} &#x3D; 0, \quad \frac{\partial C}{\partial b} &#x3D; 0$$</p><img src="https://cdn.noedgeai.com/d053b280-ea92-40ab-9212-b8dc526c4b19_3.jpg?x=158&y=61&w=520&h=338 "/><p>图 1: sigmoid 函数图像</p><p>这种直接的计算在小规模情况下可行, 但在大规模数据以及非凸优化的情况下, 采用迭代的方法得到局部最优解的方式更加可行，即如下方法：</p><p>$$<br>h &#x3D; h - \eta \frac{\partial C}{\partial h}<br>$$</p><p>$$<br>b &#x3D; b - \eta \frac{\partial C}{\partial b}<br>$$</p><p>其中 $\eta$ 表示学习率，这里损失函数可以使用平方差损失。即 $C &#x3D; \frac{1}{2}(a - y)$，并进行迭代，即可求出结果。</p><h2 id="2-2-代码实现及结果分析"><a href="#2-2-代码实现及结果分析" class="headerlink" title="2.2 代码实现及结果分析"></a>2.2 代码实现及结果分析</h2><p>这里首先导入数据并将标签进行二值化, 然后利用 sklearn 来将数据进行划分, 得到训练集以及测试集。随后定义网络结构, 即仅有一个线性层并使用 sigmoid 激活函数的神经网络, 并将特征设置为数据的维度, 之后分别定义训练函数以及测试函数。然后将上文划分好的测试集以及训练集利用 TensorDataset 以及 DataLoader 得到可以送入神经网络的迭代器, 定义损失函数使用均方损失, 优化器这里使用了著名的不需要调参数的 Adadelta, 因为之前使用 SGD 的时候结果在参数调整不合适的情况会出现很大问题。最后训练并测试结果, 并将其可视化出来, 得到结果如下图所示:<br>又上图可知, 随着迭代次数的增多, 损失在不断下降, 而训练的精度则为先升后降的趋势,最高可以达到 (83%) 的精度。因为随着迭代次数过多,出现了过拟合的情况,使得模型在训练数据中取得的误差更小, 但是在测试数据中准确率反而不够高, 这也反映了仅使用线性网络可能导致的结果问题, 可以通过调整结果正则化以及控制迭代次数等方法来提高模型的性能。</p><img src="https://cdn.noedgeai.com/d053b280-ea92-40ab-9212-b8dc526c4b19_4.jpg?x=133&y=64&w=574&h=239 "/><p>图 2: logistic 分类结果</p><h2 id="3-高斯判别分析"><a href="#3-高斯判别分析" class="headerlink" title="3 高斯判别分析"></a>3 高斯判别分析</h2><h2 id="3-1-背景知识"><a href="#3-1-背景知识" class="headerlink" title="3.1 背景知识"></a>3.1 背景知识</h2><p>高斯判别分析是一个比较直观的模型, 一个基本的假设就是得到的数据是独立同分布<br>的, 虽然这种假设在实际中很难达到, 但是在有了好的假设后可以得到较好的结果。<br>一维正态分布为:</p><p>$$<br>f(x) &#x3D; \frac{1}{\sqrt{2\pi}\sigma} \exp \left( -\frac{(x - \mu)^2}{2\sigma^2} \right)<br>$$</p><p>其中 $x$ 为样本特征, $\sigma$ 为标准差, $\mu$ 为样本期望值，并将该分布记为 $N(\mu, \sigma^2)$，当 $\mu &#x3D; 0$，$\sigma &#x3D; 1$ 时候的正态分布是标准正态分布。</p><p>$n$ 维正态分布表示为：</p><p>$$<br>p(x; \mu, \Sigma) &#x3D; \frac{1}{(2\pi)^{n&#x2F;2} |\Sigma|^{1&#x2F;2}} \exp\left( -\frac{1}{2} (x - \mu)^T \Sigma^{-1} (x - \mu) \right)<br>$$</p><p>其中 (p(x; \mu, \Sigma)) 中的 (\mu, \Sigma) 分别表示均值向量以及协方差矩阵。</p><p>将 (n) 维高斯分布应用到监督学习中，假设输入数据为 (x)，输出类别为 (y \in {0,1})，则对应分类问题可以描述为：</p><p>$$<br>y \approx \operatorname{Bernoulli}(\phi)<br>$$</p><p>$$<br>x | y &#x3D; 0 \approx \mathcal{N}(\mu_0, \Sigma)<br>$$</p><p>$$<br>x | y &#x3D; 1 \approx \mathcal{N}(\mu_1, \Sigma)<br>$$</p><p>其中 Bernoulli((\phi)) 表示伯努利分布，通过推导可以得出样本分类的依据：</p><p>$$<br>p(y | x) &#x3D; \frac{p(x | y) p(y)}{p(x)}<br>$$</p><p>$$<br>y &#x3D; \underset{y}{\arg\max} , p(y | x)<br>$$</p><p>$$<br>&#x3D; \underset{y}{\arg\max} , p(x | y) p(y)<br>$$</p><h2 id="3-2-代码实现"><a href="#3-2-代码实现" class="headerlink" title="3.2 代码实现"></a>3.2 代码实现</h2><p>首先求出训练数据的均值向量以及协方差矩阵, 然后利用公式分布求出正负样本的概<br>率,最后将测试数据传入并与实际结果对比,得出准确率为 (75.00%) 。</p><h2 id="4-贝叶斯分类"><a href="#4-贝叶斯分类" class="headerlink" title="4 贝叶斯分类"></a>4 贝叶斯分类</h2><h2 id="4-1-背景知识"><a href="#4-1-背景知识" class="headerlink" title="4.1 背景知识"></a>4.1 背景知识</h2><p>贝叶斯分类是一类分类算法的总称, 一类算法以贝叶斯定理为基础, 统称为贝叶斯分类。朴素贝叶斯是贝叶斯分类中最简单常见的一种分类方法。理论上朴素贝叶斯模型比其他分类方法误差率更小, 但是由于朴素贝叶斯模型假设属性之间相互独立, 但是这个假设在实际中往往不成立, 在属性个数多或者属性相关性较大的时候, 分类效果差。朴素贝叶斯逻辑简单, 易于实现, 而且分类过程中开销比较小, 其核心算法是贝叶斯公式:<br>[P(B {\mid} A) &#x3D; \frac{P(A {\mid} B)P(B)}{P(A)}]<br>其中 (A) 为特征, (B) 为类别。</p><h2 id="4-2-代码实现"><a href="#4-2-代码实现" class="headerlink" title="4.2 代码实现"></a>4.2 代码实现</h2><p>这里首先定义一个 gaussion_pdf 函数,这个函数的作用就是利用 (n) 维正态分布的公式,从而求得 (n) 维正态分布的分布情况,从而为预测函数提供概率基础,然后定义一个预测函数 predict,利用 numpy 的 unique 求得分类数,并对每一类分布,求得 (P(y)) 以及 (P(x {\mid} y)) ,最后将测试集传入,并与测试集的标签对比得出结果。<br>经过实践,得到准确率为 (63.46%) 。</p><h2 id="5-性能分析"><a href="#5-性能分析" class="headerlink" title="5 性能分析"></a>5 性能分析</h2><p>从性能上来说, 贝叶斯分类开销比较小, 而 logistic 神经网络法则比较大, 这是因为神经网络使用的空间等相对较大, 而贝叶斯由于采用的仅为样本空间, 因此性能相对较好。<br>从结果来说, (\operatorname{logistic}) 分类的结果在迭代次数合适的时候可以达到 (80%) 以上,高斯判别<br>分析可以达到 (75%) ,贝叶斯分类的准确率为 (63.46%) ,因此 logistic 效果最好。</p><h2 id="6-时效分析"><a href="#6-时效分析" class="headerlink" title="6 时效分析"></a>6 时效分析</h2><p>代码中已经利用了库函数 <code>time</code> 来计算程序运行的时间, 经过测试, logistic 分类经过 100 次迭代使用的时间为 $2.35\mathrm{\ s}$ ,而高斯判别分析用时为 $0.02\mathrm{\ s}$ ,贝叶斯分类用时 $0.01\mathrm{\ s}$ ,调用的贝叶斯分类函数,其用时同样为 $0.01\mathrm{\ s}$ 。<br>从时间上来说, 采用神经网络的 logistic 分类使用的轮次较多, 平均下来, 每训练一轮为 $0.02\mathrm{\ s}$ ,与高斯判别分析时间近似相等,两者都差于贝叶斯分类,自己时间的贝叶斯与库函数实现的贝叶斯时间上相差不大。<br>对于时间, logistic 分类每一轮都遍历一遍样本, 贝叶斯分类只遍历一次样本, 高斯判别分析需要便利每个样本的每个特征。<br>对于空间, logistic 分类只需用一个样本的空间, 贝叶斯分类需要有数据的类别空间, 高斯判别分析需要有正负样本的存储空间。<br>表 1: 时效分析</p><table><thead><tr><th></th><th>logistic 分类</th><th>贝叶斯分类</th><th>GDA 分类</th></tr></thead><tbody><tr><td>时间复杂度</td><td>$\theta(m \cdot k)$</td><td>$\theta(m)$</td><td>$\theta(m \cdot d)$</td></tr><tr><td>空间复杂度</td><td>$\theta(d)$</td><td>$\theta(d \cdot K)$</td><td>$\theta(d^{2})$</td></tr></tbody></table><p>其中 $m$ 为样本数, $d$ 为特征维数, $k$ 为迭代次数。</p><h2 id="7-影响因素分析"><a href="#7-影响因素分析" class="headerlink" title="7 影响因素分析"></a>7 影响因素分析</h2><h2 id="7-1-logistic-分类"><a href="#7-1-logistic-分类" class="headerlink" title="7.1 logistic 分类"></a>7.1 logistic 分类</h2><p>在这个方法中, 我在写的过程中遇到的一个问题就是优化器的选取, 在开始使用 SGD 的时候, 损失在很短的时间就达到很大, 显示出 nan, 经过多次尝试才明白出问题的地方, 修改后, 效果较好。</p><h2 id="8-总结"><a href="#8-总结" class="headerlink" title="8 总结"></a>8 总结</h2><p>在这次的机器学习大作业中我收获很大, 这次的作用并不容易, 不仅仅要完成三个方法的分类任务, 一个重要方面是对方法的分析, 包括性能分析, 时效分析等, 这也是对能力的一次锻炼, 收获很大。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] 详解朴素贝叶斯分类算法 <a href="https://blog.csdn.net/ccblogger/article/details/81712351">https://blog.csdn.net/ccblogger/article/details/81712351</a>? ivk_sa&#x3D;1024320u</p><p>[2] <a href="https://www.cnblogs.com/phoenixzq/p/3539619.html">贝叶斯分类</a> </p><p>[3] <a href="https://zhuanlan.zhihu.com/p/38269530">高斯判别分析</a></p><h2 id="A-logistic-分类代码"><a href="#A-logistic-分类代码" class="headerlink" title="A logistic 分类代码"></a>A logistic 分类代码</h2><ul><li>logistic 分类.py</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Created on Sat Apr 10 16:56:09 2021</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">@author: tremble</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><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.optim <span class="keyword">as</span> optim</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd </span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">start=time.time()</span><br><span class="line">file=pd.read_csv(<span class="string">&#x27;D:/桌面/sonar.csv&#x27;</span>,header=<span class="literal">None</span>)</span><br><span class="line">data=file.iloc[:,:<span class="number">40</span>]</span><br><span class="line">target=file.iloc[:,-<span class="number">1</span>]</span><br><span class="line">data=np.array(data,dtype=<span class="built_in">float</span>)</span><br><span class="line">target=pd.get_dummies(target).iloc[:,<span class="number">0</span>]</span><br><span class="line">data=torch.tensor(data,dtype=torch.<span class="built_in">float</span>)</span><br><span class="line">target=torch.tensor(target,dtype=torch.<span class="built_in">float</span>)</span><br><span class="line"></span><br><span class="line">x_train,x_test,y_train,y_test=train_test_split(data,\</span><br><span class="line">    target,test_size=<span class="number">0.25</span>,random_state=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">logistic_net</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,features</span>):</span><br><span class="line">        <span class="built_in">super</span>(logistic_net,self).__init__()</span><br><span class="line">        self.linear=nn.Linear(features,<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line">        x=self.linear(x)</span><br><span class="line">        x=torch.sigmoid(x)</span><br><span class="line">        x = x.squeeze(-<span class="number">1</span>)    </span><br><span class="line">        <span class="keyword">return</span> x</span><br><span class="line">    </span><br><span class="line">model=logistic_net(<span class="number">40</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">train</span>(<span class="params">model, train_loader, optimizer, epoch, criterion</span>):</span><br><span class="line">    model.train()</span><br><span class="line">    loss = <span class="number">0.0</span></span><br><span class="line">    <span class="keyword">for</span> i, (data, target) <span class="keyword">in</span> <span class="built_in">enumerate</span>(train_loader):</span><br><span class="line">        optimizer.zero_grad()</span><br><span class="line">        output = model(data)</span><br><span class="line">        loss = criterion(output, target)</span><br><span class="line">        loss.backward()</span><br><span class="line">        optimizer.step()</span><br><span class="line">        <span class="keyword">if</span> i % <span class="number">10</span> == <span class="number">0</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;Train Epoch: &#123;&#125; [&#123;&#125;/&#123;&#125; (&#123;:.0f&#125;%)]\tLoss: &#123;:.6f&#125;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">                epoch, i * <span class="built_in">len</span>(data), <span class="built_in">len</span>(train_loader.dataset),</span><br><span class="line">                       <span class="number">100.</span> * i / <span class="built_in">len</span>(train_loader), loss.item()))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test</span>(<span class="params">model,test_loader, criterion</span>):</span><br><span class="line">    model.<span class="built_in">eval</span>()</span><br><span class="line">    test_loss = <span class="number">0</span></span><br><span class="line">    correct = <span class="number">0</span></span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> data, target <span class="keyword">in</span> test_loader:</span><br><span class="line">            pred=torch.Tensor(<span class="built_in">len</span>(target),<span class="number">1</span>)</span><br><span class="line">            output = model(data)</span><br><span class="line">            test_loss += criterion(output, target).item()  <span class="comment"># sum up batch loss</span></span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(target)):</span><br><span class="line">                <span class="keyword">if</span> output[i]&gt;<span class="number">0.5</span>:</span><br><span class="line">                    pred[i]=torch.tensor(<span class="number">1</span>)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    pred[i]=torch.tensor(<span class="number">0</span>)</span><br><span class="line">            <span class="comment">#pred = output.argmax(dim=1, keepdim=True)  # get the index of the max log-probability</span></span><br><span class="line">            correct += pred.eq(target.view_as(pred)).<span class="built_in">sum</span>().item()</span><br><span class="line"></span><br><span class="line">    test_loss /= <span class="built_in">len</span>(test_loader.dataset)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;\nTest set: Average loss: &#123;:.4f&#125;, Accuracy: &#123;&#125;/&#123;&#125; (&#123;:.0f&#125;%)\n&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">        test_loss, correct, <span class="built_in">len</span>(test_loader.dataset),</span><br><span class="line">        <span class="number">100.</span> * correct / <span class="built_in">len</span>(test_loader.dataset)))</span><br><span class="line">    <span class="keyword">return</span> test_loss,<span class="number">100.</span> * correct / <span class="built_in">len</span>(test_loader.dataset)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">trainset=torch.utils.data.TensorDataset(x_train,y_train)</span><br><span class="line">testset=torch.utils.data.TensorDataset(x_test,y_test)</span><br><span class="line">trainloader=torch.utils.data.DataLoader(trainset,batch_size=<span class="number">4</span>,shuffle=<span class="literal">True</span>)</span><br><span class="line">testloader=torch.utils.data.DataLoader(testset,batch_size=<span class="number">4</span>,shuffle=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">criterion = nn.MSELoss()</span><br><span class="line">optimizer = optim.Adadelta(model.parameters(), lr=<span class="number">1.0</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">epoch_list,ls_list,accuracy_list=[],[],[]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">100</span>):</span><br><span class="line">    train(model,trainloader, optimizer, epoch, criterion)</span><br><span class="line">    ls,accuracy=test(model, testloader, criterion)</span><br><span class="line">    epoch_list.append(epoch)</span><br><span class="line">    ls_list.append(ls)</span><br><span class="line">    accuracy_list.append(accuracy)</span><br><span class="line"></span><br><span class="line">fig = plt.figure(figsize=(<span class="number">10</span>,<span class="number">4</span>))</span><br><span class="line">plt.subplot(<span class="number">121</span>)</span><br><span class="line">plt.plot(epoch_list,ls_list,linestyle=<span class="string">&#x27;:&#x27;</span>)</span><br><span class="line">plt.xlabel(<span class="string">&#x27;epoch&#x27;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&#x27;loss&#x27;</span>)</span><br><span class="line">plt.subplot(<span class="number">122</span>)</span><br><span class="line">plt.plot(epoch_list,accuracy_list,linestyle=<span class="string">&#x27;:&#x27;</span>)</span><br><span class="line">plt.xlabel(<span class="string">&#x27;epoch &#x27;</span>)</span><br><span class="line">plt.ylabel(<span class="string">&#x27;accuracy&#x27;</span>)</span><br><span class="line">plt.show()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;用时&#123;:.2f&#125;s&#x27;</span>.<span class="built_in">format</span>(time.time()-start))</span><br></pre></td></tr></table></figure><h2 id="B-GDA-分类代码"><a href="#B-GDA-分类代码" class="headerlink" title="B GDA 分类代码"></a>B GDA 分类代码</h2><ul><li>GDA 分类.py</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Created on Sat Apr 10 18:11:49 2021</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">@author: tremble</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd </span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">import</span> time </span><br><span class="line"></span><br><span class="line">start=time.time()</span><br><span class="line">file=pd.read_csv(<span class="string">&#x27;D:/桌面/sonar.csv&#x27;</span>,header=<span class="literal">None</span>)</span><br><span class="line">data=file.iloc[:,:<span class="number">40</span>]</span><br><span class="line">target=file.iloc[:,-<span class="number">1</span>]</span><br><span class="line">data=np.array(data,dtype=<span class="built_in">float</span>)</span><br><span class="line">target=pd.get_dummies(target).iloc[:,<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">x_train,x_test,y_train,y_test=train_test_split(data,\</span><br><span class="line">                target,test_size=<span class="number">0.25</span>,random_state=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">positive_data=[]</span><br><span class="line">negative_data=[]</span><br><span class="line">positive_num=<span class="number">0</span></span><br><span class="line">negative_num=<span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> (data,label)<span class="keyword">in</span> <span class="built_in">zip</span>(x_train,y_train):</span><br><span class="line">    <span class="keyword">if</span> label ==<span class="number">1</span>:</span><br><span class="line">        positive_data.append(<span class="built_in">list</span>(data))</span><br><span class="line">        positive_num+=<span class="number">1</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        negative_data.append(<span class="built_in">list</span>(data))</span><br><span class="line">        negative_num+=<span class="number">1</span></span><br><span class="line">        </span><br><span class="line">row,col=np.shape(x_train)   </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">positive=positive_num*<span class="number">1.0</span>/row</span><br><span class="line">negative=<span class="number">1</span>-positive</span><br><span class="line">        </span><br><span class="line">positive_data=np.array(positive_data)</span><br><span class="line">negative_data=np.array(negative_data)        </span><br><span class="line">mean_positive=np.mean(positive_data,axis=<span class="number">0</span>)       </span><br><span class="line">mean_negative=np.mean(negative_data,axis=<span class="number">0</span>)        </span><br><span class="line">positive_deta=positive_data-mean_positive</span><br><span class="line">negative_deta=negative_data-mean_negative        </span><br><span class="line">        </span><br><span class="line">sigma=[]</span><br><span class="line"><span class="keyword">for</span> deta <span class="keyword">in</span> positive_deta:</span><br><span class="line">    deta=deta.reshape(<span class="number">1</span>,col)</span><br><span class="line">    ans = deta.T.dot(deta)</span><br><span class="line">    sigma.append(ans)</span><br><span class="line"><span class="keyword">for</span> deta <span class="keyword">in</span> negative_deta:</span><br><span class="line">    deta=deta.reshape(<span class="number">1</span>,col)</span><br><span class="line">    ans = deta.T.dot(deta)</span><br><span class="line">    sigma.append(ans)</span><br><span class="line">sigma=np.array(sigma)</span><br><span class="line">sigma=np.mean(sigma,axis=<span class="number">0</span>)        </span><br><span class="line">        </span><br><span class="line">mean_positive=mean_positive.reshape(<span class="number">1</span>,col)</span><br><span class="line">mean_negative=mean_negative.reshape(<span class="number">1</span>,col)        </span><br><span class="line">        </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">Gaussian</span>(<span class="params">x,mean,cov</span>):</span><br><span class="line">    dim=np.shape(cov)[<span class="number">0</span>]</span><br><span class="line">    covdet = np.linalg.det(cov + np.eye(dim) * <span class="number">0.001</span>)</span><br><span class="line">    covinv = np.linalg.inv(cov + np.eye(dim) * <span class="number">0.001</span>)</span><br><span class="line">    xdiff = (x - mean).reshape((<span class="number">1</span>, dim))</span><br><span class="line">    prob = <span class="number">1.0</span> / (np.power(np.power(<span class="number">2</span> * np.pi, dim) *\</span><br><span class="line">                           np.<span class="built_in">abs</span>(covdet), <span class="number">0.5</span>)) * \</span><br><span class="line">    np.exp(-<span class="number">0.5</span> * xdiff.dot(covinv).dot(xdiff.T))[<span class="number">0</span>][<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">return</span> prob        </span><br><span class="line">        </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">predict</span>(<span class="params">x_test,mean_positive,mean_negetive</span>):</span><br><span class="line">    predict_label=[]</span><br><span class="line">    <span class="keyword">for</span> data <span class="keyword">in</span> x_test:</span><br><span class="line">        positive_pro=Gaussian(data, mean_positive, sigma)</span><br><span class="line">        negative_pro=Gaussian(data, mean_negetive, sigma)</span><br><span class="line">        <span class="keyword">if</span> positive_pro&gt;=negative_pro:</span><br><span class="line">            predict_label.append(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            predict_label.append(<span class="number">0</span>)</span><br><span class="line">    <span class="keyword">return</span> predict_label        </span><br><span class="line">        </span><br><span class="line">test_predict=predict(x_test,mean_positive,mean_negative)        </span><br><span class="line">        </span><br><span class="line">test_predict=np.array(test_predict)</span><br><span class="line">y_test=np.array(y_test)        </span><br><span class="line">        </span><br><span class="line">accuracy=(test_predict==y_test).<span class="built_in">sum</span>().item()/<span class="built_in">len</span>(y_test)        </span><br><span class="line">        </span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;用时&#123;:.2f&#125;s,准确率为&#123;:.2f&#125;%&#x27;</span>.\</span><br><span class="line">      <span class="built_in">format</span>(time.time()-start,accuracy*<span class="number">100.0</span>))        </span><br></pre></td></tr></table></figure><h2 id="C-贝叶斯分类代码"><a href="#C-贝叶斯分类代码" class="headerlink" title="C 贝叶斯分类代码"></a>C 贝叶斯分类代码</h2><ul><li>贝叶斯分类.py</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Created on Sat Apr 10 18:36:49 2021</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">@author: tremble</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd </span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">start=time.time()</span><br><span class="line">file=pd.read_csv(<span class="string">&#x27;D:/桌面/sonar.csv&#x27;</span>,header=<span class="literal">None</span>)</span><br><span class="line">data=file.iloc[:,:<span class="number">40</span>]</span><br><span class="line">target=file.iloc[:,-<span class="number">1</span>]</span><br><span class="line">data=np.array(data,dtype=<span class="built_in">float</span>)</span><br><span class="line">target=pd.get_dummies(target).iloc[:,<span class="number">0</span>]</span><br><span class="line">data=np.array(data,dtype=<span class="built_in">float</span>)</span><br><span class="line">target=np.array(target,dtype=<span class="built_in">float</span>)</span><br><span class="line">x_train,x_test,y_train,y_test=train_test_split(data,\</span><br><span class="line">            target,test_size=<span class="number">0.25</span>,random_state=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">gaussion_pdf</span>(<span class="params">x_test, x</span>):</span><br><span class="line">        temp1 = (x_test - x.mean(<span class="number">0</span>)) * (x_test - x.mean(<span class="number">0</span>))</span><br><span class="line">        temp2 = x.std(<span class="number">0</span>) * x.std(<span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> np.exp(-temp1 / (<span class="number">2</span> * temp2)) / np.sqrt(<span class="number">2</span> * np.pi * temp2)</span><br><span class="line">    </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">predict</span>(<span class="params">x_train,y_train,x_test</span>):</span><br><span class="line">        <span class="keyword">assert</span> <span class="built_in">len</span>(x_test.shape) == <span class="number">2</span></span><br><span class="line">        classes = np.unique(y_train)</span><br><span class="line">        pred_probs = []</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> classes:</span><br><span class="line">            idx_i = y_train == i</span><br><span class="line">            p_y = <span class="built_in">len</span>(idx_i) / <span class="built_in">len</span>(y_train)</span><br><span class="line">            p_x_y = np.prod(gaussion_pdf(x_test,x_train[idx_i]), <span class="number">1</span>)</span><br><span class="line">            prob_i = p_y * p_x_y</span><br><span class="line">            pred_probs.append(prob_i)</span><br><span class="line">        pred_probs = np.vstack(pred_probs)</span><br><span class="line">        label_idx = pred_probs.argmax(<span class="number">0</span>)</span><br><span class="line">        y_pred = classes[label_idx]</span><br><span class="line">        <span class="keyword">return</span> y_pred</span><br><span class="line">    </span><br><span class="line">y_predict=predict(x_train,y_train,x_test)</span><br><span class="line"></span><br><span class="line">accuracy=(y_predict==y_test).<span class="built_in">sum</span>().item()/<span class="built_in">len</span>(y_test)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;准确率为&#123;:.2f&#125;%,用时&#123;:.2f&#125;s&#x27;</span>.<span class="built_in">format</span>(accuracy*<span class="number">100.0</span>,\</span><br><span class="line">                                     time.time()-start))</span><br></pre></td></tr></table></figure><h2 id="D-贝叶斯库函数调用分类代码"><a href="#D-贝叶斯库函数调用分类代码" class="headerlink" title="D 贝叶斯库函数调用分类代码"></a>D 贝叶斯库函数调用分类代码</h2><ul><li>贝叶斯调用实现.py</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Created on Sat Apr 10 19:53:00 2021</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">@author: tremble</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> accuracy_score</span><br><span class="line"><span class="keyword">from</span> sklearn.naive_bayes <span class="keyword">import</span> GaussianNB</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">start=time.time()</span><br><span class="line">file=pd.read_csv(<span class="string">&#x27;D:/桌面/sonar.csv&#x27;</span>,header=<span class="literal">None</span>)</span><br><span class="line">data=file.iloc[:,:<span class="number">40</span>]</span><br><span class="line">target=file.iloc[:,-<span class="number">1</span>]</span><br><span class="line">data=np.array(data,dtype=<span class="built_in">float</span>)</span><br><span class="line">target=pd.get_dummies(target).iloc[:,<span class="number">0</span>]</span><br><span class="line">data=np.array(data,dtype=<span class="built_in">float</span>)</span><br><span class="line">target=np.array(target,dtype=<span class="built_in">float</span>)</span><br><span class="line">x_train,x_test,y_train,y_test=train_test_split(data,\</span><br><span class="line">            target,test_size=<span class="number">0.25</span>,random_state=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">model = GaussianNB()</span><br><span class="line">model.fit(x_train,y_train)</span><br><span class="line">test_predict_model = model.predict(x_test)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;逻辑回归的正确率为：&#123;:.2f&#125;%,用时为&#123;:.2f&#125;s&quot;</span>.\</span><br><span class="line">      <span class="built_in">format</span>(accuracy_score(y_test,\</span><br><span class="line">        test_predict_model)*<span class="number">100.0</span>,time.time() - start))</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://jackzhu.top/2021/04/11/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E6%95%B0%E6%8D%AE%E5%88%86%E7%B1%BB%E7%9A%84%E5%AE%9E%E7%8E%B0/</id>
    <link href="http://jackzhu.top/2021/04/11/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94%E6%95%B0%E6%8D%AE%E5%88%86%E7%B1%BB%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
    <published>2021-04-11T12:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 数据集 1<br>2 logistic 回归与神经网络 1<br>2.1 背景知识 1<br>2.1.1 线性及 sigmoid]]>
    </summary>
    <title>机器学习报告——数据分类的实现</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="数据挖掘" scheme="http://jackzhu.top/categories/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98/"/>
    <category term="数据挖掘" scheme="http://jackzhu.top/tags/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98/"/>
    <content>
      <![CDATA[<h2 id="摘-要"><a href="#摘-要" class="headerlink" title="摘 要"></a>摘 要</h2><p>在本次的大作业中，这里首先利用 MySQL 对数据进行整合，并提取出包含 $CO_2$ 和 $O_2$ 相应指标的数据，然后将提取的数据导出，并导入到 Python 中。然后利用第一组数据为示例分别进行缺失值、离群点处理，并完成去噪以及插值处理。随后选出另两组为示例进行可视化，最后得出实验感想与分析，这里去噪利用了 $3\sigma$ 原则。</p><p>关键词: $\quad$ MySQL Python $\quad 3\sigma$ 原则</p><h2 id="目-录"><a href="#目-录" class="headerlink" title="目 录"></a>目 录</h2><p>1 数据处理 1<br>1.1 数据提取 1<br>1.2 数据具体处理 1<br>1.3 数据合并 1<br>1.4 缺失值处理 2<br>1.5 离群点处理 2<br>1.6 去噪 4<br>1.7 插值 5<br>2 第二组数据 5<br>2.1 离群点处理 5<br>2.2 去噪 6<br>2.3 插值 7<br>3 第三组数据 8<br>3.1 离群点处理 8<br>3.2 去噪 9<br>3.3 插值 9<br>4 总结 10<br>A SQL 程序代码 1 11<br>B SQL 程序代码 2 11<br>(\mathrm{C}) 主程序代码 11<br>D 作业要求 14<br>D. 1 数据集说明 14<br>D. 2 任务说明 14</p><h2 id="1-数据处理"><a href="#1-数据处理" class="headerlink" title="1 数据处理"></a>1 数据处理</h2><h2 id="1-1-数据提取"><a href="#1-1-数据提取" class="headerlink" title="1.1 数据提取"></a>1.1 数据提取</h2><p>这次作业中数据较大, 直接读取很难看出数据具体细节, 直接打开会崩溃, 因此这里开始使用 MySQL, 将提供的三个文件导入进去, 然后进行初步处理, 利用附录 A 和附录 B 中所示代码, 将 PO2 和 PCO2 两个指标提取出来, 得到数据 CO2 如下图所示。</p><table><thead><tr><th>f1</th><th>ROW ID</th><th>SUBJECT ID</th><th>HADM ID</th><th>ITEMID</th><th>CHARTTIMEVALUE</th><th>VALUENUM</th><th>VALUEUOM</th><th>FLAG</th><th>time</th></tr></thead><tbody><tr><td></td><td>974</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2204:31:0025</td><td>25.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>231</td><td>988</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2207:13:0028</td><td>28.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>240</td><td>997</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2210:16:0028</td><td>28.0</td><td>mmHg</td><td>(NuII)</td><td>0</td></tr><tr><td>249</td><td>1006</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2211:21:0027</td><td>27.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>262</td><td>1019</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2213:02:0026</td><td>26.0</td><td>mmHg</td><td>CNUII</td><td>0</td></tr><tr><td>271</td><td>1028</td><td>a</td><td>145B34.0</td><td>50818</td><td>2101-10-2215:59:0024</td><td>24.0</td><td>mmHg</td><td>(NuII)</td><td>0</td></tr><tr><td>285</td><td>1042</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2216:02:0030</td><td>30.0</td><td>mmHg</td><td>(Nuith</td><td>0</td></tr><tr><td>310</td><td>1067</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2221:20:0027</td><td>27.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>325</td><td>1082</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2302:46:0029</td><td>29.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>338</td><td>1095</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2302:49:0033</td><td>33.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>379</td><td>1136</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2310:14:0025</td><td>25.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>392</td><td>1149</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2310:22:0030</td><td>30.0</td><td>mmHg</td><td>CNUI</td><td>0</td></tr><tr><td>406</td><td>1163</td><td>a</td><td>145834.0</td><td>50818</td><td>2101-10-2316:10:0029</td><td>29.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>468</td><td>588</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2020:04:0028</td><td>28.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>486</td><td>606</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2021:51:0033</td><td>33.0</td><td>mmHg</td><td>(Nuil)</td><td>0</td></tr><tr><td>502</td><td>622</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2100:42:0030</td><td>30.0</td><td>mmHg</td><td>(NuII)</td><td>0</td></tr><tr><td>568</td><td>506</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2019:12:0040</td><td>40.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>580</td><td>518</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2019:14:0028</td><td>28.0</td><td>mmHg</td><td>(Num</td><td>0</td></tr><tr><td>783</td><td>189</td><td>3</td><td>(NuII)</td><td>50818</td><td>2101-10-1209:18:00338</td><td>38.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>802</td><td>208</td><td>3</td><td>(NuM)</td><td>50818</td><td>2101-10-1210:51:0039</td><td>39.0</td><td>mmHg</td><td>(Num</td><td>0</td></tr><tr><td>820</td><td>226</td><td>2</td><td>(NuM)</td><td>50818</td><td>2101-10-12 12:05:0044</td><td>44.0</td><td>mmHg</td><td>(NuII)</td><td>0</td></tr><tr><td>836</td><td>242</td><td>3</td><td>(NuII)</td><td>50818</td><td>2101-10-1213:00:0040</td><td>40.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>853</td><td>259</td><td>3</td><td>(Nu 0)</td><td>50818</td><td>2101-10-1214:22:0042</td><td>42.0</td><td>mmHg</td><td>(NuII)</td><td>0</td></tr><tr><td>1089</td><td>2509</td><td>4</td><td>CNUM</td><td>50818</td><td>2191-05-1813:16:0027</td><td>27.0</td><td>mmHg</td><td>abnormal</td><td>0</td></tr><tr><td>1281</td><td>687</td><td>3</td><td>145834.0</td><td>SO818</td><td>2101-10-2103:09:0029</td><td>29.0</td><td>mmHg</td><td>(Null</td><td>0</td></tr><tr><td>1313</td><td>719</td><td>a</td><td>145834.0</td><td>50818</td><td>2101-10-2109:46:0026</td><td>26.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>1322</td><td>728</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2109:51:0033</td><td>33.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>1330</td><td>736</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2110:23:0027</td><td>27.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>1379</td><td>785</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2115:48:0028</td><td>28.0</td><td>mmHg</td><td>(NoII)</td><td>0</td></tr><tr><td>1393</td><td>799</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2115:52:0036</td><td>36.0</td><td>mmHg</td><td>(Null)</td><td>0</td></tr><tr><td>1409</td><td>815</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2117:33:0030</td><td>30.0</td><td>mmHg</td><td>(NuII)</td><td>0</td></tr><tr><td>1424</td><td>830</td><td>3</td><td>145834.0</td><td>50818</td><td>2101-10-2117:37:0037</td><td>37.0</td><td>mmHg</td><td>(Nuti)</td><td>0</td></tr></tbody></table><p>图 1: 初步提取数据图</p><h2 id="1-2-数据具体处理"><a href="#1-2-数据具体处理" class="headerlink" title="1.2 数据具体处理"></a>1.2 数据具体处理</h2><p>由于 MySQL 时间戳利用的 Unix 时间只能表示到 2038 年, 不便于后续处理, 因此这<br>段用 Python 语言进行处理。<br>将上部分的数据导出为 (\mathrm{csv}) 格式,并利用 python 读取。这里首先选择 SUBJECT_ID<br>为 3 的数据作为示例。</p><h2 id="1-3-数据合并"><a href="#1-3-数据合并" class="headerlink" title="1.3 数据合并"></a>1.3 数据合并</h2><p>这里首先将 PO2 和 PCO2 中 SUBJECT_ID 相同的数据合并到一个表格中, 并删除其余无关列, 仅保留下时间 CHARTTIME、住院时期 HADM_ID 以及 O2 和 CO2 的值。这里用的数据 SUBJECT_ID 分别为 3 ,</p><table><thead><tr><th></th><th>HADM_ID_X</th><th>CHARTTIME</th><th>CO2</th><th>02</th></tr></thead><tbody><tr><td>0</td><td>NaN</td><td>2101-10-12 09:18:00</td><td>38</td><td>244</td></tr><tr><td>1</td><td>NaN</td><td>2101-10-12 10:51:00</td><td>39</td><td>159</td></tr><tr><td>2</td><td>NaN</td><td>2101-10-12 12:05:00</td><td>44</td><td>173</td></tr><tr><td>3</td><td>NaN</td><td>2101-10-12 13:00:00</td><td>40</td><td>151</td></tr><tr><td>4</td><td>NaN</td><td>2101-10-12 14:22:00</td><td>42</td><td>138</td></tr><tr><td>5</td><td>NaN</td><td>2101-10-12 18:17:00</td><td>33</td><td>80</td></tr><tr><td>6</td><td>145834.0</td><td>2101-10-20 19:12:00</td><td>40</td><td>20</td></tr><tr><td>7</td><td>145834.0</td><td>2101-10-20 19:14:00</td><td>28</td><td>313</td></tr><tr><td>8</td><td>145834.0</td><td>2101-10-2020:04:00</td><td>28</td><td>329</td></tr><tr><td>9</td><td>145834.0</td><td>2101-10-2021:51:00</td><td>33</td><td>287</td></tr><tr><td>10</td><td>145834.0</td><td>2101-10-2100:42:00</td><td>30</td><td>175</td></tr><tr><td>11</td><td>145834.0</td><td>2101-10-21 03:09:00</td><td>29</td><td>181</td></tr></tbody></table><p>图 2: 初步处理数据图</p><h2 id="1-4-缺失值处理"><a href="#1-4-缺失值处理" class="headerlink" title="1.4 缺失值处理"></a>1.4 缺失值处理</h2><p>可以看到此时的数据中存在一些缺失值, 根据分析, 前部分确实的为一次住院, 最后有<br>一个数据异常, 因此不妨将缺失的值赋为 1 。<br>此时准备对结果进行可视化, 发现氧气和二氧化碳的数据值并非为数型, 而是为字符<br>型, 因此将这两列转为数字型, 此时将数据可视化得到下图:</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_4.jpg?x=131&y=649&w=580&h=232 "/><p>图 3: 离群点处理前</p><h2 id="1-5-离群点处理"><a href="#1-5-离群点处理" class="headerlink" title="1.5 离群点处理"></a>1.5 离群点处理</h2><p>由于离群点仅有一个, 一个病人再住院期间可以进行一次或者多次血气指标收集, 对于一个病人, 在单次住院期间仅收集一次是无意义的, 也无法进行插值, 只有在一个病人单次住院期间收集多次的情况下, 插值才有意义。因此可将该点去除, 去除后数据如下图所<br>示。</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_5.jpg?x=131&y=94&w=580&h=235 "/><p>图 4: 离群点处理后</p><p>由于数据明显可分为两部分, 因此数据应为两次住院期间的数据, 因此将数据分为两<br>部分, 即两次住院的数据。<br>第一部分可视化</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_5.jpg?x=133&y=466&w=577&h=238 "/><p>图 5: 一部分去噪前</p><p>由于该部分数据量较少, 插值准确率低, 因此不采用第一部分数据进行处理。第二部分可视化的结果如下图所示:<br><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_6.jpg?x=130&y=62&w=581&h=238 "/></p><p>图 6: 二部分去噪前</p><h2 id="1-6-去噪"><a href="#1-6-去噪" class="headerlink" title="1.6 去噪"></a>1.6 去噪</h2><p>这里使用了 (3\sigma) 原则进行去噪 (\lbrack 1\rbrack) ,由于样本处于 ((\mu {-} 3\sigma,\mu + 3\sigma)) 的概率为 0.9973 。因<br>此用此原则进行去噪有很大的作用。具体代码如下:</p><hr><p>def drop_noisy(df):<br>(\mathrm{df}) _copy ( &#x3D; \mathrm{df} {\cdot} \operatorname{copy}())<br>df_describe &#x3D; df_copy.describe()<br>for column in [‘CO2’,’O2’]:<br>mean ( &#x3D; ) df_describe.loc [‘mean’,column]<br>std ( &#x3D; ) df_describe.loc [‘std’,column]<br>minvalue ( &#x3D; ) mean ( {-} 3 * ) std<br>maxvalue ( &#x3D; ) mean ( + 3 * ) std<br>df_copy &#x3D; df_copy[df_copy[column] &gt;&#x3D; minvalue]<br>df_copy &#x3D; df_copy[df_copy[column] &lt;&#x3D; maxvalue]<br>return df_copy</p><hr><p>去噪后的结果如下图所示:</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_6.jpg?x=131&y=774&w=579&h=236 "/><p>图 7: 二部分去噪后</p><h2 id="1-7-插值"><a href="#1-7-插值" class="headerlink" title="1.7 插值"></a>1.7 插值</h2><p>在插值地方, 首先将时间一列转为时间戳的形式, 然后分别对每一天的数据进行插值, 这里对于时间使用了线性插值,对 (\mathrm{CO}2) 和 (\mathrm{O}2) 使用了阶梯插值,最后将数据整合到一起, 并将时间戳转化为时间的形式, 从而完成插值。<br>插值前, 结果如下图所示:</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_7.jpg?x=131&y=199&w=579&h=238 "/><p>图 8: 样本三第二部分部分数据插值前</p><p>经过插值后, 结果如下图所示:</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_7.jpg?x=131&y=529&w=579&h=237 "/><p>图 9: 样本三第二部分部分数据插值后</p><h2 id="2-第二组数据"><a href="#2-第二组数据" class="headerlink" title="2 第二组数据"></a>2 第二组数据</h2><p>由上述第一组数据的处理可以得到处理方法, 下述处理同上。</p><h2 id="2-1-离群点处理"><a href="#2-1-离群点处理" class="headerlink" title="2.1 离群点处理"></a>2.1 离群点处理</h2><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_8.jpg?x=133&y=62&w=578&h=235 "/><p>图 10: 第二组数据离群点处理前</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_8.jpg?x=133&y=365&w=577&h=235 "/><p>图 11: 第二组数据离群点处理后</p><p>2.2 去噪<br><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_9.jpg?x=157&y=62&w=524&h=211 "/></p><p>图 12: 第二组数据去噪处理后</p><p>2.3 插值<br>插值前:</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_9.jpg?x=131&y=404&w=580&h=233 "/><p>图 13: 第二组数据插值前<br><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_10.jpg?x=129&y=61&w=583&h=234 "/></p><p>图 14: 第二组数据插值后</p><h2 id="3-第三组数据"><a href="#3-第三组数据" class="headerlink" title="3 第三组数据"></a>3 第三组数据</h2><p>3.1 离群点处理</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_10.jpg?x=128&y=477&w=583&h=240 "/><p>图 15: 第三组数据去离群点前<br><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_11.jpg?x=131&y=62&w=582&h=235 "/></p><p>图 16: 第三组数据去离群点后</p><p>3.2 去噪</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_11.jpg?x=131&y=422&w=583&h=235 "/><p>图 17: 第三组数据去噪后</p><p> 3.3 插值<br><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_12.jpg?x=133&y=62&w=579&h=238 "/></p><p>图 18: 第三组数据插值前</p><img src="https://cdn.noedgeai.com/a33d2563-22a5-40a9-9c0b-dd358f6aff7d_12.jpg?x=132&y=368&w=579&h=239 "/><p>图 19: 第三组数据插值后</p><h2 id="4-总结"><a href="#4-总结" class="headerlink" title="4 总结"></a>4 总结</h2><p>这次作业在很多地方都有较大的坑, 也是很久以来最难的一次大作业, 以至于做了很久才有一点头绪, 对于我而言, 最难的地方在于插值部分, 由于作为横轴的时间必须经过处理后才能进行相关处理, 在这方面进行插值以及可视化都出现了不小的问题, 经过反复思考并请教相关同学才成功解决。<br>通过这次数据挖掘的作业, 我感觉自己收获很大, 也学到了很多知识, 我有了一些新的认识, 数据预处理很重要, 未处理过的数据中往往有很多问题, 如果直接拿去用由于数据中存在的很多有问题的地方会使得结果与预期有很大的差异, 因此做好数据预处理是很有必要的一个步骤。在近期的学习中我学习到了数据库以及数据处理方面的很多技巧, 这对于进一步的数据挖掘应该有着不小的帮助, 也有助于今后在该领域的发展。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] 刘彬, 戴桂平. 基于白化检验和 3 准则的小波阈值去噪算法 [J]. 传感技术学报,2005,18(3):473-476. DOI:10.3969&#x2F;j.issn.1004-1699.2005.03.008.</p><h2 id="A-SQL-程序代码-1"><a href="#A-SQL-程序代码-1" class="headerlink" title="A SQL 程序代码 1"></a>A SQL 程序代码 1</h2><p>提取 PO2 数据</p><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>USE hello_mysql;</td><td>SELECT</td><td></td></tr><tr><td></td><td>*:</td><td></td></tr><tr><td>FROM</td><td></td><td></td></tr><tr><td>labevents mini</td><td></td><td></td></tr><tr><td></td><td>WHERE</td><td></td></tr><tr><td>T</td><td>ITEMID &#x3D; 490</td><td></td></tr><tr><td>M</td><td>OR ITEMID &#x3D; 3785</td><td></td></tr><tr><td>9</td><td>OR ITEMID &#x3D; 3837</td><td></td></tr><tr><td>0</td><td>OR ITEMID &#x3D; 50821</td><td></td></tr></tbody></table><p>B SQL 程序代码 2<br>提取 PCO2 数据</p><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>USE hello_mysql;</td><td>SELECT</td><td></td></tr><tr><td></td><td>*:</td><td></td></tr><tr><td>FROM</td><td></td><td></td></tr><tr><td>labevents_mini</td><td></td><td></td></tr><tr><td></td><td>WHERE</td><td></td></tr><tr><td></td><td>ITEMID &#x3D; 3784</td><td></td></tr><tr><td></td><td>OR ITEMID &#x3D; 3835</td><td></td></tr><tr><td></td><td>OR ITEMID &#x3D; 50818</td><td></td></tr></tbody></table><h2 id="C-主程序代码"><a href="#C-主程序代码" class="headerlink" title="C 主程序代码"></a>C 主程序代码</h2><ul><li>Data enhancement.py</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Created on Thu Apr  8 20:01:07 2021</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">@author: tremble</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd </span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> sklearn</span><br><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> scipy <span class="keyword">import</span> interpolate</span><br><span class="line"><span class="keyword">import</span> time </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">plot_img</span>(<span class="params">df</span>):</span><br><span class="line">    t=df.loc[:,<span class="string">&#x27;CHARTTIME&#x27;</span>]</span><br><span class="line"></span><br><span class="line">    t=[datetime.datetime.strptime(i,<span class="string">&#x27;%Y-%m-%d %H:%M:%S&#x27;</span>) <span class="keyword">for</span> i <span class="keyword">in</span> t]</span><br><span class="line"></span><br><span class="line">    co2 = [<span class="built_in">float</span>(i) <span class="keyword">for</span> i <span class="keyword">in</span> df.loc[:,<span class="string">&#x27;CO2&#x27;</span>]]</span><br><span class="line">    o2 = [<span class="built_in">float</span>(i) <span class="keyword">for</span> i <span class="keyword">in</span> df.loc[:,<span class="string">&#x27;O2&#x27;</span>]]</span><br><span class="line"></span><br><span class="line">    fig = plt.figure(figsize=(<span class="number">20</span>,<span class="number">8</span>))</span><br><span class="line">    plt.subplot(<span class="number">211</span>)</span><br><span class="line">    plt.plot(t,co2,<span class="string">&#x27;o-&#x27;</span>)</span><br><span class="line">    plt.xlabel(<span class="string">&#x27;data&#x27;</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;CO2&#x27;</span>)</span><br><span class="line">    plt.grid(ls=<span class="string">&#x27;--&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    plt.subplot(<span class="number">212</span>)</span><br><span class="line">    plt.plot(t,o2,<span class="string">&#x27;o-&#x27;</span>)</span><br><span class="line">    plt.xlabel(<span class="string">&#x27;data &#x27;</span>)</span><br><span class="line">    plt.ylabel(<span class="string">&#x27;O2&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    plt.grid(ls=<span class="string">&#x27;--&#x27;</span>)</span><br><span class="line">    plt.show()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">drop_noisy</span>(<span class="params">df</span>):</span><br><span class="line">    df_copy = df.copy()</span><br><span class="line">    df_describe = df_copy.describe()</span><br><span class="line">    <span class="keyword">for</span> column <span class="keyword">in</span> [<span class="string">&#x27;CO2&#x27;</span>,<span class="string">&#x27;O2&#x27;</span>]:</span><br><span class="line">        mean = df_describe.loc[<span class="string">&#x27;mean&#x27;</span>,column]</span><br><span class="line">        std = df_describe.loc[<span class="string">&#x27;std&#x27;</span>,column]</span><br><span class="line">        minvalue = mean - <span class="number">3</span>*std</span><br><span class="line">        maxvalue = mean + <span class="number">3</span>*std</span><br><span class="line">        df_copy = df_copy[df_copy[column] &gt;= minvalue]</span><br><span class="line">        df_copy = df_copy[df_copy[column] &lt;= maxvalue]</span><br><span class="line">        <span class="keyword">return</span> df_copy</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">date_to_timestamp</span>(<span class="params">date, format_string=<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span></span>):</span><br><span class="line"> time_array = time.strptime(date, format_string)</span><br><span class="line"> time_stamp = <span class="built_in">int</span>(time.mktime(time_array))</span><br><span class="line"> <span class="keyword">return</span> time_stamp</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timestamp_to_date</span>(<span class="params">time_stamp, format_string=<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span></span>):</span><br><span class="line"> time_array = time.localtime(time_stamp)</span><br><span class="line"> str_date = time.strftime(format_string, time_array)</span><br><span class="line"> <span class="keyword">return</span> str_date</span><br><span class="line"></span><br><span class="line">data_co2=pd.read_csv(<span class="string">r&#x27;D:\桌面\data mining\PCO2.csv&#x27;</span>)</span><br><span class="line">data_o2=pd.read_csv(<span class="string">r&#x27;D:\桌面\data mining\PO2.csv&#x27;</span>) </span><br><span class="line">df1=pd.DataFrame(data_co2)</span><br><span class="line">df2=pd.DataFrame(data_o2)</span><br><span class="line"></span><br><span class="line">df1=df1.loc[df1[<span class="string">&#x27;SUBJECT_ID&#x27;</span>] == <span class="number">3</span>]</span><br><span class="line">df2=df2.loc[df2[<span class="string">&#x27;SUBJECT_ID&#x27;</span>] == <span class="number">3</span>]</span><br><span class="line">df1.rename(columns=&#123;<span class="string">&#x27;VALUE&#x27;</span>:<span class="string">&#x27;CO2&#x27;</span>&#125;,inplace=<span class="literal">True</span>) </span><br><span class="line">df2.rename(columns=&#123;<span class="string">&#x27;VALUE&#x27;</span>:<span class="string">&#x27;O2&#x27;</span>&#125;,inplace=<span class="literal">True</span>) </span><br><span class="line"></span><br><span class="line">df=pd.DataFrame.merge(df1, df2, how=<span class="string">&#x27;inner&#x27;</span>, on=<span class="string">&#x27;CHARTTIME&#x27;</span>, left_on=<span class="literal">None</span>, right_on=<span class="literal">None</span>,   </span><br><span class="line">      left_index=<span class="literal">False</span>, right_index=<span class="literal">False</span>, sort=<span class="literal">True</span>,   </span><br><span class="line">      suffixes=(<span class="string">&#x27;_x&#x27;</span>, <span class="string">&#x27;_y&#x27;</span>), copy=<span class="literal">True</span>, indicator=<span class="literal">False</span>)</span><br><span class="line">df=df[[<span class="string">&#x27;HADM_ID_x&#x27;</span>,<span class="string">&#x27;CHARTTIME&#x27;</span>,<span class="string">&#x27;CO2&#x27;</span>,<span class="string">&#x27;O2&#x27;</span>]]</span><br><span class="line"></span><br><span class="line">df=df.fillna(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">df[[<span class="string">&#x27;CO2&#x27;</span>,<span class="string">&#x27;O2&#x27;</span>]] = df[[<span class="string">&#x27;CO2&#x27;</span>,<span class="string">&#x27;O2&#x27;</span>]].apply(pd.to_numeric)</span><br><span class="line">plot_img(df) </span><br><span class="line">df=df.drop([<span class="number">44</span>],axis=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">df1=df.loc[df[<span class="string">&#x27;HADM_ID_x&#x27;</span>] == <span class="number">1</span>]</span><br><span class="line">df2=df.loc[df[<span class="string">&#x27;HADM_ID_x&#x27;</span>] == <span class="number">145834.0</span>]</span><br><span class="line"></span><br><span class="line">plot_img(df2)</span><br><span class="line"></span><br><span class="line">df2=drop_noisy(df2)</span><br><span class="line"></span><br><span class="line">plot_img(df2)</span><br><span class="line"></span><br><span class="line">df2.interpolate(method=<span class="string">&#x27;time&#x27;</span>,axis=<span class="number">0</span>)</span><br><span class="line">plot_img(df2)</span><br><span class="line"></span><br><span class="line">df2_1=df2[<span class="number">4</span>:<span class="number">17</span>]</span><br><span class="line">plot_img(df2_1)</span><br><span class="line"><span class="built_in">set</span>=df2_1</span><br><span class="line"><span class="built_in">set</span>=np.array(<span class="built_in">set</span>)</span><br><span class="line">x=np.linspace(<span class="number">0</span>,<span class="built_in">len</span>(<span class="built_in">set</span>)-<span class="number">1</span>,<span class="built_in">len</span>(<span class="built_in">set</span>))</span><br><span class="line">value=[]</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(<span class="built_in">set</span>)):</span><br><span class="line">    value.append(date_to_timestamp(<span class="built_in">set</span>[i, <span class="number">1</span>]))</span><br><span class="line">value=np.array(value)</span><br><span class="line">diff=value[-<span class="number">1</span>]-value[<span class="number">0</span>]</span><br><span class="line">location=np.linspace(<span class="number">0</span>,<span class="built_in">len</span>(<span class="built_in">set</span>)-<span class="number">1</span>,diff//<span class="number">3600</span>+<span class="number">1</span>)</span><br><span class="line">f0=interpolate.interp1d(x,value,kind=<span class="string">&#x27;linear&#x27;</span>)</span><br><span class="line">f1=interpolate.interp1d(x,np.array(<span class="built_in">set</span>[:,<span class="number">2</span>]),kind=<span class="string">&#x27;zero&#x27;</span>)</span><br><span class="line">f2=interpolate.interp1d(x,np.array(<span class="built_in">set</span>[:,<span class="number">3</span>]),kind=<span class="string">&#x27;zero&#x27;</span>)</span><br><span class="line">F0=f0(location)</span><br><span class="line">F0_time=[]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(F0)):</span><br><span class="line"> F0_time.append(timestamp_to_date(F0[i]))</span><br><span class="line"> F1 = f1(location)</span><br><span class="line"> F2 = f2(location)</span><br><span class="line">combined_data = np.vstack((np.array(F0_time),F1, F2))</span><br><span class="line">df2_2= np.transpose(combined_data)</span><br><span class="line"></span><br><span class="line">df2_2=pd.DataFrame(columns=[<span class="string">&#x27;CHARTTIME&#x27;</span>,<span class="string">&#x27;CO2&#x27;</span>,<span class="string">&#x27;O2&#x27;</span>,],data=df2_2)</span><br><span class="line">plot_img(df2_2)</span><br></pre></td></tr></table></figure><h2 id="D-作业要求"><a href="#D-作业要求" class="headerlink" title="D 作业要求"></a>D 作业要求</h2><h2 id="D-1-数据集说明"><a href="#D-1-数据集说明" class="headerlink" title="D. 1 数据集说明"></a>D. 1 数据集说明</h2><p>该数据集是一个免费的大型数据库, 包含与 2001 年至 2012 年之某医疗机构重症监护<br>室收治的 40,000 多名患者相关的健康相关数据。该数据集已进行数据脱敏。<br>数据集的说明参阅 <a href="https://mimic.physionet.org/about/mimic/%E3%80%82%E6%AD%A4%E6%AC%A1%E4%BB%BB%E5%8A%A1%E6%B6%89%E5%8F%8A%E5%88%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%AD%E7%9A%84%E4%B8%89%E4%B8%AA%E8%A1%A8%E6%A0%BC">https://mimic.physionet.org/about/mimic/。此次任务涉及到数据集中的三个表格</a>, PATIENTS, CHARTEVENTS 和 LABEVENTS, 表格的说明分别参考<br><a href="https://mimic.physionet.org/mimictables/patients/">https://mimic.physionet.org/mimictables/patients/</a> <a href="https://mimic.physionet.org/mimictables/chartevents/">https://mimic.physionet.org/mimictables/chartevents/</a> <a href="https://mimic.physionet.org/mimictables/labevents/">https://mimic.physionet.org/mimictables/labevents/</a></p><h2 id="D-2-任务说明"><a href="#D-2-任务说明" class="headerlink" title="D. 2 任务说明"></a>D. 2 任务说明</h2><p>本次任务的目的是处理 (\mathrm{pO}2,\mathrm{pCO}2) 两个指标。这两个指标均为病人的血气指标,以一定的时间间隔采集。一个病人一次住院期间可能收集一次或者多次。要求, 按照采集时间的前后顺序,汇总每个病人每次住院期间的所有的 (\mathrm{pO}2,\mathrm{pCO}2) 指标值。涉及到的预处理方法包括插值, 去噪, 缺失值填充, 离群点数据处理, 可视化等。<br>pO2 和 PCO2 数据存储在 CHARTEVENTS 和 LABEVENTS 两个表格中 (不是分别存储, 而是每个表格都包括这两个指标)。两个表格中以 ITEMID 字段进行标注, 其中 PO2 的 ITEMID 为 (\lbrack 490,3785,3837,50821\rbrack) 之一,PCO2 的 ITEMID 为 (\lbrack 3784,3835,50818\rbrack) 之一。SUBJECT_ID 字段指示不同的病人 (如张三和李四的 SUBJECT_ID 分别为 00001<br>和 00002), HADM_ID 指示一次住院时期 (一个病人可能多次入院, 同一 SUBJECT_ID, HADM_ID 不同则认为是同一病人不同的住院经历, 在收集数据时需要区分)。</p>]]>
    </content>
    <id>http://jackzhu.top/2021/04/08/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94MIMC%E6%95%B0%E6%8D%AE%E9%9B%86%E7%9A%84%E9%A2%84%E5%A4%84%E7%90%86/</id>
    <link href="http://jackzhu.top/2021/04/08/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98%E6%8A%A5%E5%91%8A%E2%80%94%E2%80%94MIMC%E6%95%B0%E6%8D%AE%E9%9B%86%E7%9A%84%E9%A2%84%E5%A4%84%E7%90%86/"/>
    <published>2021-04-08T12:29:37.000Z</published>
    <summary>
      <![CDATA[<h2 id="摘-要"><a href="#摘-要" class="headerlink" title="摘 要"></a>摘 要</h2><p>在本次的大作业中，这里首先利用 MySQL 对数据进行整合，并提取出包含 $CO_2$ 和 $O_2$ 相应指标的数据，然后将提取的]]>
    </summary>
    <title>数据挖掘报告——MIMC数据集的预处理</title>
    <updated>2026-06-10T08:01:07.567Z</updated>
  </entry>
  <entry>
    <author>
      <name>Jack Zhu</name>
    </author>
    <category term="生活" scheme="http://jackzhu.top/categories/%E7%94%9F%E6%B4%BB/"/>
    <category term="记录生活" scheme="http://jackzhu.top/tags/%E8%AE%B0%E5%BD%95%E7%94%9F%E6%B4%BB/"/>
    <content>
      <![CDATA[<h1 id="艰难的第一次搭博客"><a href="#艰难的第一次搭博客" class="headerlink" title="艰难的第一次搭博客"></a>艰难的第一次搭博客</h1><p>其实我在很早之前就想搭博客，大概大一开始的时候，就想着做一些有意义，有兴趣的事，结果一直拖到了现在才开始真正意义上的实践，我也是趁着这次对大一的培训搭建博客，顺便才开始做，看上去不是很难的一件事，实际去做，我却遇到了很多困难，就光配置markdown的front-matter的时候，因为不知道里面还有其他东西，就一直报错，而且和其他一起弄的，我还找不到原因，就导致我花了很大的时间，最后遇到的一个问题是图片的在线存储问题，最后用的码云的仓库来放的，最后头像也解决了，我才能说，我基本才算是搭建成了一个基本的框架。</p><p>哎，之前跟现在相比，其实并不忙的，但也是懒，还有各种找的原因，结果到现在，对这样搭博客的基本知识都还都不太了解，有点小愧疚，以后还是得加把劲了。</p><p>现在还有一堆大作业要去写，剩下的时间也不多了，就先这样吧。</p><p>配置起来不是访问有问题需要外网就是图片的问题，真是很难。</p>]]>
    </content>
    <id>http://jackzhu.top/2020/10/31/%E8%AE%B0%E5%BD%95%E7%AC%AC%E4%B8%80%E6%AC%A1%E6%90%AD%E5%8D%9A%E5%AE%A2/</id>
    <link href="http://jackzhu.top/2020/10/31/%E8%AE%B0%E5%BD%95%E7%AC%AC%E4%B8%80%E6%AC%A1%E6%90%AD%E5%8D%9A%E5%AE%A2/"/>
    <published>2020-10-31T00:00:00.000Z</published>
    <summary>我太难了</summary>
    <title>记录我的第一次搭建博客</title>
    <updated>2026-06-10T08:01:07.570Z</updated>
  </entry>
</feed>
