<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>藤君的小窝</title><description>一个温暖的角落</description><link>https://blog.stivine.fun/</link><language>zh_CN</language><item><title>实践-给 Astro 静态博客加上浏览量和点赞</title><link>https://blog.stivine.fun/posts/practice-add-view-count-and-likes-to-an-astro-static-blog/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/practice-add-view-count-and-likes-to-an-astro-static-blog/</guid><description>根据 AI 推荐，使用 Supabase</description><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;写在前面&lt;/h2&gt;
&lt;p&gt;Astro 静态博客，构建完就是一堆 HTML 文件，没有后端服务器。想要记录浏览量？想要保存点赞？这些都需要数据库啊！&lt;/p&gt;
&lt;p&gt;传统方案要么是自己写个后端 API，要么用 Serverless Functions。但根据我咨询 AI 的结果，我们选择第三方方案：&lt;strong&gt;Supabase&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;supabase/supabase&quot;}&lt;/p&gt;
&lt;h2&gt;技术架构&lt;/h2&gt;
&lt;p&gt;先看看整体思路：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;用户访问页面
    ↓
Svelte 组件加载（客户端）
    ↓
调用 Supabase API
    ↓
PostgreSQL 数据库
    ↓
返回统计数据
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;核心就是：用 Supabase 的数据库存数据，用它的 JavaScript SDK 调接口。页面是静态的，但 JavaScript 可以动态请求数据。&lt;/p&gt;
&lt;h2&gt;数据库设计&lt;/h2&gt;
&lt;p&gt;需要三张表：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;page_stats&lt;/strong&gt; - 统计主表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;page_path: 页面路径&lt;/li&gt;
&lt;li&gt;view_count: 浏览量&lt;/li&gt;
&lt;li&gt;like_count: 点赞数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;page_views&lt;/strong&gt; - 浏览记录（防刷用）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;page_path: 页面路径&lt;/li&gt;
&lt;li&gt;fingerprint: 设备指纹&lt;/li&gt;
&lt;li&gt;viewed_at: 浏览时间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;page_likes&lt;/strong&gt; - 点赞记录（防刷 + 取消点赞）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;page_path: 页面路径&lt;/li&gt;
&lt;li&gt;fingerprint: 设备指纹&lt;/li&gt;
&lt;li&gt;唯一约束：一个设备只能点赞一次&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;核心 SQL&lt;/h3&gt;
&lt;p&gt;创建表的 SQL 不贴完整的了，几个关键点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 统计表，page_path 做唯一索引
CREATE TABLE page_stats (
  page_path VARCHAR(500) UNIQUE NOT NULL,
  view_count INTEGER DEFAULT 0,
  like_count INTEGER DEFAULT 0
);

-- 点赞表，防止重复点赞
CREATE TABLE page_likes (
  page_path VARCHAR(500) NOT NULL,
  fingerprint VARCHAR(100) NOT NULL,
  CONSTRAINT unique_like UNIQUE (page_path, fingerprint)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重点是用数据库函数做原子操作，避免并发问题：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 增加浏览量（1小时内同设备不重复计数）
CREATE FUNCTION increment_view_count(
  p_page_path VARCHAR(500),
  p_fingerprint VARCHAR(100)
) RETURNS BOOLEAN AS $$
BEGIN
  -- 检查最近1小时是否访问过
  IF EXISTS(
    SELECT 1 FROM page_views
    WHERE page_path = p_page_path
      AND fingerprint = p_fingerprint
      AND viewed_at &amp;gt; NOW() - INTERVAL &apos;1 hour&apos;
  ) THEN
    RETURN FALSE;
  END IF;

  -- 插入访问记录
  INSERT INTO page_views (page_path, fingerprint)
  VALUES (p_page_path, p_fingerprint);

  -- 更新统计（没有记录就创建，有就+1）
  INSERT INTO page_stats (page_path, view_count)
  VALUES (p_page_path, 1)
  ON CONFLICT (page_path)
  DO UPDATE SET view_count = page_stats.view_count + 1;

  RETURN TRUE;
END;
$$ LANGUAGE plpgsql;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;点赞的逻辑类似，但要支持取消：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 切换点赞状态
CREATE FUNCTION toggle_like(
  p_page_path VARCHAR(500),
  p_fingerprint VARCHAR(100)
) RETURNS JSON AS $$
DECLARE
  like_exists BOOLEAN;
  new_count INTEGER;
BEGIN
  -- 检查是否已点赞
  SELECT EXISTS(
    SELECT 1 FROM page_likes
    WHERE page_path = p_page_path AND fingerprint = p_fingerprint
  ) INTO like_exists;

  IF like_exists THEN
    -- 取消点赞
    DELETE FROM page_likes
    WHERE page_path = p_page_path AND fingerprint = p_fingerprint;

    UPDATE page_stats
    SET like_count = GREATEST(like_count - 1, 0)
    WHERE page_path = p_page_path
    RETURNING like_count INTO new_count;

    RETURN json_build_object(&apos;liked&apos;, false, &apos;like_count&apos;, COALESCE(new_count, 0));
  ELSE
    -- 添加点赞
    INSERT INTO page_likes (page_path, fingerprint)
    VALUES (p_page_path, p_fingerprint);

    INSERT INTO page_stats (page_path, like_count)
    VALUES (p_page_path, 1)
    ON CONFLICT (page_path)
    DO UPDATE SET like_count = page_stats.like_count + 1
    RETURNING like_count INTO new_count;

    RETURN json_build_object(&apos;liked&apos;, true, &apos;like_count&apos;, new_count);
  END IF;
END;
$$ LANGUAGE plpgsql;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样做的好处是逻辑都在数据库层，前端只管调用，不用担心并发。&lt;/p&gt;
&lt;h2&gt;防刷方案&lt;/h2&gt;
&lt;p&gt;生成设备指纹：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/lib/fingerprint.ts
export async function generateFingerprint(): Promise&amp;lt;string&amp;gt; {
  const components: string[] = [];

  // 收集浏览器特征
  components.push(`${screen.width}x${screen.height}x${screen.colorDepth}`);
  components.push(Intl.DateTimeFormat().resolvedOptions().timeZone);
  components.push(navigator.language);
  components.push(navigator.platform);
  components.push(String(navigator.hardwareConcurrency || 0));

  // Canvas 指纹
  try {
    const canvas = document.createElement(&apos;canvas&apos;);
    const ctx = canvas.getContext(&apos;2d&apos;);
    if (ctx) {
      ctx.textBaseline = &apos;top&apos;;
      ctx.font = &apos;14px Arial&apos;;
      ctx.fillStyle = &apos;#f60&apos;;
      ctx.fillRect(125, 1, 62, 20);
      ctx.fillStyle = &apos;#069&apos;;
      ctx.fillText(&apos;Hello, world!&apos;, 2, 15);
      components.push(canvas.toDataURL().slice(-50));
    }
  } catch (e) {
    components.push(&apos;canvas-error&apos;);
  }

  // 组合并哈希
  const fingerprint = components.join(&apos;|&apos;);
  return await hashString(fingerprint);
}

async function hashString(str: string): Promise&amp;lt;string&amp;gt; {
  const encoder = new TextEncoder();
  const data = encoder.encode(str);
  const hashBuffer = await crypto.subtle.digest(&apos;SHA-256&apos;, data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b =&amp;gt; b.toString(16).padStart(2, &apos;0&apos;)).join(&apos;&apos;);
}

// 缓存到 localStorage，避免重复计算
export async function getFingerprint(): Promise&amp;lt;string&amp;gt; {
  const cached = localStorage.getItem(&apos;device_fingerprint&apos;);
  if (cached) return cached;

  const fingerprint = await generateFingerprint();
  localStorage.setItem(&apos;device_fingerprint&apos;, fingerprint);
  return fingerprint;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;前端实现&lt;/h2&gt;
&lt;h3&gt;API 封装&lt;/h3&gt;
&lt;p&gt;先把 Supabase 的调用封装一下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/lib/supabase.ts
import { createClient } from &apos;@supabase/supabase-js&apos;;
import { supabaseConfig } from &apos;../config&apos;;

export const supabase = createClient(
  supabaseConfig.url,
  supabaseConfig.anonKey
);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// src/lib/stats-api.ts
import { supabase } from &apos;./supabase&apos;;
import { getFingerprint } from &apos;./fingerprint&apos;;

export interface PageStats {
  viewCount: number;
  likeCount: number;
  liked: boolean;
}

export async function getPageStats(pagePath: string): Promise&amp;lt;PageStats | null&amp;gt; {
  try {
    const fingerprint = await getFingerprint();
    const { data, error } = await supabase.rpc(&apos;get_page_stats&apos;, {
      p_page_path: pagePath,
      p_fingerprint: fingerprint,
    });

    if (error) throw error;

    return {
      viewCount: data.view_count || 0,
      likeCount: data.like_count || 0,
      liked: data.liked || false,
    };
  } catch (error) {
    console.error(&apos;Failed to fetch stats:&apos;, error);
    return null;
  }
}

export async function incrementViewCount(pagePath: string): Promise&amp;lt;boolean&amp;gt; {
  try {
    const fingerprint = await getFingerprint();
    const { data, error } = await supabase.rpc(&apos;increment_view_count&apos;, {
      p_page_path: pagePath,
      p_fingerprint: fingerprint,
      p_user_agent: navigator.userAgent,
    });

    if (error) throw error;
    return data === true;
  } catch (error) {
    console.error(&apos;Failed to increment view count:&apos;, error);
    return false;
  }
}

export async function toggleLike(pagePath: string) {
  try {
    const fingerprint = await getFingerprint();
    const { data, error } = await supabase.rpc(&apos;toggle_like&apos;, {
      p_page_path: pagePath,
      p_fingerprint: fingerprint,
    });

    if (error) throw error;
    return {
      liked: data.liked,
      likeCount: data.like_count,
    };
  } catch (error) {
    console.error(&apos;Failed to toggle like:&apos;, error);
    return null;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Svelte 组件&lt;/h3&gt;
&lt;p&gt;这里用 Svelte 写组件（因为项目里有其他 Svelte 组件，保持风格统一）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- src/components/stats/PageStats.svelte --&amp;gt;
&amp;lt;script lang=&quot;ts&quot;&amp;gt;
import { onMount } from &apos;svelte&apos;;
import { getPageStats, incrementViewCount, toggleLike } from &apos;../../lib/stats-api&apos;;

export let pagePath: string;

let viewCount = 0;
let likeCount = 0;
let liked = false;
let loading = true;
let liking = false;

onMount(async () =&amp;gt; {
  // 加载统计信息
  const stats = await getPageStats(pagePath);
  if (stats) {
    viewCount = stats.viewCount;
    likeCount = stats.likeCount;
    liked = stats.liked;
  }
  loading = false;

  // 异步增加浏览量（不阻塞渲染）
  incrementViewCount(pagePath).catch(console.warn);
});

async function handleLike() {
  if (liking) return;

  liking = true;
  const result = await toggleLike(pagePath);

  if (result) {
    liked = result.liked;
    likeCount = result.likeCount;
  }

  liking = false;
}
&amp;lt;/script&amp;gt;

{#if loading}
  &amp;lt;!-- 骨架屏 --&amp;gt;
  &amp;lt;div class=&quot;stats-loading&quot;&amp;gt;
    &amp;lt;div class=&quot;skeleton&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{:else}
  &amp;lt;div class=&quot;page-stats&quot;&amp;gt;
    &amp;lt;!-- 浏览量 --&amp;gt;
    &amp;lt;div class=&quot;stat-item&quot;&amp;gt;
      &amp;lt;div class=&quot;stat-icon&quot;&amp;gt;
        &amp;lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot;&amp;gt;
          &amp;lt;path d=&quot;M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z&quot;/&amp;gt;
          &amp;lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;3&quot;/&amp;gt;
        &amp;lt;/svg&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;span&amp;gt;{viewCount.toLocaleString()}&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- 点赞 --&amp;gt;
    &amp;lt;button
      class=&quot;like-button&quot;
      class:liked
      on:click={handleLike}
      disabled={liking}
    &amp;gt;
      &amp;lt;div class=&quot;stat-icon&quot; class:liked-icon={liked}&amp;gt;
        &amp;lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot;
             fill={liked ? &apos;currentColor&apos; : &apos;none&apos;}
             stroke=&quot;currentColor&quot;&amp;gt;
          &amp;lt;path d=&quot;M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z&quot;/&amp;gt;
        &amp;lt;/svg&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;span&amp;gt;{likeCount.toLocaleString()}&amp;lt;/span&amp;gt;
    &amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}

&amp;lt;style&amp;gt;
  .page-stats {
    display: flex;
    gap: 1.25rem;
    color: rgba(0, 0, 0, 0.3);
  }

  :global(.dark) .page-stats {
    color: rgba(255, 255, 255, 0.3);
  }

  .stat-item, .like-button {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .stat-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 0.375rem;
    background: rgba(0, 0, 0, 0.05);
    transition: all 0.2s;
  }

  :global(.dark) .stat-icon {
    background: rgba(255, 255, 255, 0.1);
  }

  .like-button {
    border: none;
    background: transparent;
    padding: 0;
    cursor: pointer;
    color: inherit;
  }

  .like-button:hover {
    color: rgb(239, 68, 68);
  }

  .like-button:active {
    transform: scale(0.95);
  }

  .liked {
    color: rgb(239, 68, 68);
  }

  .liked-icon {
    background: rgb(239, 68, 68) !important;
    color: white !important;
  }

  .skeleton {
    width: 3rem;
    height: 1rem;
    background: rgba(0, 0, 0, 0.05);
    border-radius: 0.25rem;
    animation: pulse 2s infinite;
  }

  @keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;集成到页面&lt;/h3&gt;
&lt;p&gt;最后在文章页面引入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// src/pages/posts/[...slug].astro
import PageStats from &apos;@components/stats/PageStats.astro&apos;;
// ... 其他 imports
---

&amp;lt;div class=&quot;post-meta&quot;&amp;gt;
  &amp;lt;!-- 原有的字数、阅读时间 --&amp;gt;
  &amp;lt;div&amp;gt;📝 {words} 字&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;⏱️ {minutes} 分钟&amp;lt;/div&amp;gt;

  &amp;lt;!-- 新增：浏览量和点赞 --&amp;gt;
  &amp;lt;PageStats path={`/posts/${entry.slug}`} /&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这里用的是 &lt;code&gt;client:load&lt;/code&gt; 指令（在 Astro 包装组件里设置），让 Svelte 组件在客户端加载。&lt;/p&gt;
&lt;h2&gt;配置管理&lt;/h2&gt;
&lt;p&gt;把配置集中管理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/config.ts
export const supabaseConfig = {
  enable: true,  // 总开关
  url: import.meta.env.PUBLIC_SUPABASE_URL || &apos;&apos;,
  anonKey: import.meta.env.PUBLIC_SUPABASE_ANON_KEY || &apos;&apos;,
  features: {
    viewCount: true,   // 显示浏览量
    likeButton: true,  // 显示点赞
  },
  antiSpam: {
    viewCooldown: 3600,  // 浏览冷却时间（秒）
  },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;环境变量放在 &lt;code&gt;.env&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
PUBLIC_SUPABASE_ANON_KEY=你的密钥
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;部署注意事项&lt;/h2&gt;
&lt;h3&gt;静态站点的特殊性&lt;/h3&gt;
&lt;p&gt;因为 Astro 是在构建时读取环境变量的，所以：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;本地开发&lt;/strong&gt;：创建 &lt;code&gt;.env&lt;/code&gt; 文件&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CI/CD 部署&lt;/strong&gt;：在构建平台配置环境变量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vercel/Netlify：在后台添加环境变量&lt;/li&gt;
&lt;li&gt;GitHub Actions：添加 Secrets，然后在工作流中注入：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;- name: Build
  run: npm run build
  env:
    PUBLIC_SUPABASE_URL: ${{ secrets.PUBLIC_SUPABASE_URL }}
    PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.PUBLIC_SUPABASE_ANON_KEY }}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Supabase 安全配置&lt;/h3&gt;
&lt;p&gt;记得启用 Row Level Security (RLS)：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER TABLE page_stats ENABLE ROW LEVEL SECURITY;

-- 允许所有人读取和写入（通过 anon key）
CREATE POLICY &quot;Public access&quot; ON page_stats FOR ALL USING (true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;anon key 是设计为可以公开的，真正的权限控制靠 RLS 策略。&lt;/p&gt;
&lt;h2&gt;效果展示&lt;/h2&gt;
&lt;p&gt;最终效果就是文章顶部多了两个小图标：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;📝 1234 字    ⏱️ 5 分钟    👁️ 42    ❤️ 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;点击心形图标可以点赞，再点就取消。刷新页面后状态保持。&lt;/p&gt;
&lt;p&gt;浏览量方面，同一设备 1 小时内重复访问不会增加计数，基本能反映真实访问量。&lt;/p&gt;
&lt;h2&gt;后续优化&lt;/h2&gt;
&lt;p&gt;目前的方案已经够用，但还有优化空间：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;更强的防刷&lt;/strong&gt;：可以加入 IP 检测、速率限制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据分析&lt;/strong&gt;：记录访问时间、来源页面等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;热门排行&lt;/strong&gt;：根据浏览量/点赞生成热门文章列表&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;评论联动&lt;/strong&gt;：把评论数也显示出来&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不过暂时没必要搞太复杂~毕竟只是个人博客&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;静态博客想要动态功能，用 Supabase 这类 BaaS 服务是个不错的选择。不用管服务器，不用写后端，专注于前端逻辑就好。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本文部分内容借助AI辅助生成，请注意自主辨别信息准确性。
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>经验-我的日常工具分享（持续更新）</title><link>https://blog.stivine.fun/posts/experience-my-daily-tools/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/experience-my-daily-tools/</guid><description>学习生活中的一些（冷门）app/网站/第三方工具分享</description><pubDate>Fri, 21 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;视频&lt;/h1&gt;
&lt;p&gt;potplayer + 插件，用来看音视频文件、b站在线视频/番剧、各大平台直播（复制链接后直接Ctrl+V即可，也可导入外部播放列表）&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;chen310/BilibiliPotPlayer&quot;}&lt;/p&gt;
&lt;p&gt;在电脑上看电视直播：&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Guovin/iptv-api&quot;}&lt;/p&gt;
&lt;p&gt;获取直播平台直播流：
::github{repo=&quot;wbt5/real-url&quot;}
::github{repo=&quot;streamlink/streamlink&quot;}&lt;/p&gt;
&lt;h1&gt;学习工作&lt;/h1&gt;
&lt;p&gt;typora + obsidian + git插件，用于记录笔记：&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Vinzent03/obsidian-git&quot;}&lt;/p&gt;
&lt;p&gt;当日备忘，当作便签使用：&lt;/p&gt;
&lt;p&gt;:::tip[微软便笺 (Sticky Notes)]
Windows 自带的轻量笔记工具。&lt;br /&gt;
🔗 &lt;a href=&quot;https://apps.microsoft.com/detail/9nblggh4qghw&quot;&gt;Microsoft Store 链接&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;WHPH 用于记录、辅助养成良好习惯：&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;ahmet-cetinkaya/whph&quot;}&lt;/p&gt;
&lt;p&gt;翻译器：
::github{repo=&quot;HIllya51/LunaTranslator&quot;}&lt;/p&gt;
&lt;h1&gt;自媒体&lt;/h1&gt;
&lt;p&gt;:::tip[AI人工智能图片放大]
方便的在线图片超分
🔗 &lt;a href=&quot;https://bigjpg.com/zh&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;:::tip[laplace.live]
内含b站直播相关的大量实用工具
🔗 &lt;a href=&quot;https://laplace.live/&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;h1&gt;二次元&lt;/h1&gt;
&lt;p&gt;:::note[Bangumi 番组计划]
记录看过的动画、书籍、游戏，二次元用户必备。&lt;br /&gt;
📺 &lt;a href=&quot;https://bgm.tv&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;:::note[轻小说机翻机器人]
日本网文自动在线翻译，日轻爱好者必备。&lt;br /&gt;
📺 &lt;a href=&quot;https://n.novelia.cc/&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;:::note[蜜柑计划]
新番RSS订阅服务，配合下文提到的QQ机器人RSS订阅插件使用&lt;br /&gt;
📺 &lt;a href=&quot;https://mikanani.kas.pub/&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;h1&gt;生活实用&lt;/h1&gt;
&lt;p&gt;:::tip[惜食魔法袋]
（可自行搜索小程序/app）
🔗 &lt;a href=&quot;https://magicbag.xishi.life/&quot;&gt;官网&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;:::tip[铁路信息查询]
查车次等详细信息，火车迷最爱&lt;br /&gt;
🔗 &lt;a href=&quot;http://lltskb.com/&quot;&gt;APP主页&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;:::tip[安卓实用工具合集]
我在里面找到了好用的音量增强器~
🔗 &lt;a href=&quot;https://pan.quark.cn/s/d4746bd6dd78#/list/share&quot;&gt;夸克网盘&lt;/a&gt;
:::&lt;/p&gt;
&lt;h1&gt;个人爱好&lt;/h1&gt;
&lt;p&gt;:::tip[观鸟工具]
懂鸟（Aboutbirds）识别全球鸟类 - 鸟类资料查询&lt;br /&gt;
🔗 &lt;a href=&quot;https://bird.art/&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;p&gt;:::tip[中国观鸟记录中心]
中国观鸟记录中心，记录观鸟结果、查看鸟类分布&lt;br /&gt;
🔗 &lt;a href=&quot;http://birdreport.cn/&quot;&gt;访问网站&lt;/a&gt;
:::&lt;/p&gt;
&lt;h1&gt;其他黑科技（？&lt;/h1&gt;
&lt;p&gt;QQ 机器人
::github{repo=&quot;koishijs/koishi&quot;}
::github{repo=&quot;NapNeko/NapCatQQ&quot;}&lt;/p&gt;
&lt;p&gt;AI 写代码用
::github{repo=&quot;ericc-ch/copilot-api&quot;}&lt;/p&gt;
</content:encoded></item><item><title>旅行与发现-国庆假期游记（2025）</title><link>https://blog.stivine.fun/posts/travel-and-discovery-national-day-holiday-travel-diary-2025/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/travel-and-discovery-national-day-holiday-travel-diary-2025/</guid><description>原来我很喜欢海边！</description><pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;舍友 Y 提出舟山是个不错的旅行目的地，而舟山中的嵊泗离原本打算去的上海非常近，因此欣然计划了一次嵊泗三日游。大致思路是2日去上海玩，晚上提前前往滴水湖，3日在滴水湖坐公交直达沈家湾码头，坐船前往泗礁岛，4日去花鸟岛，5日原路返回。&lt;/p&gt;
&lt;p&gt;下面是我的流水账式记录，希望对你有帮助~&lt;/p&gt;
&lt;h1&gt;10.2&lt;/h1&gt;
&lt;p&gt;&amp;lt;!-- 有轨电车2号线 --&amp;gt;
&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚋 苏州高新有轨电车2号线&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;9:13 – 9:47&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;南京大学东 → 苏州新区火车站&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- 公交85路 --&amp;gt;
&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚌 苏州公交 85路&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;9:48 – 10:18&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;沪宁城铁新区站西 → 苏州站南广场&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;这里为什么不坐更稳定且快速的地铁？因为腻了。。。我还是很喜欢85路公交车的。而且坐普速火车的话，到南广场也更近一些，85路是合适的。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- 火车 --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚄 国铁 Z163&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;11:04 – 12:03 &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;苏州站 → 上海站&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;座位&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;无座 05车&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;附注&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;原本是买的Z267，后来觉得太晚了，就改签到了提前的Z163&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚇 上海地铁&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;11:58 – 12:35&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;上海站 → 莲花路&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;在莲花路站附近吃了兰州牛肉面作为午饭。随后步行前往附近的影院看纪录片《窗外是蓝星》，观后感如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;才得知有中国载人航天纪录片在映。原来神舟十三号任务期间，航天员们用电影机录下来大量宝贵的素材，最终形成了《窗外是蓝星》这部纪录片。
一个半小时中，有至少一个小时我觉得自己随时能哭出来。我曾经梦到自己在太空中哭泣，却想象不出眼泪在太空中漂浮是什么样子。我不知道自己现在为什么想哭，是人类的渺小？科技工作者的伟大？我想我只是被简单地震撼了。我回到了小时候。那是现在的我眼中“什么都不知道的我”。但他还能第一次为知识的力量所震撼，为自然科学的奥秘所震撼，为人类文明的概念所震撼。
感受虽如此夸张，但我要强调影片还是有非常明显的缺点的，例如在发射和返回期间的众多工作仅仅是一笔带过。科普内容比较水且缺乏设计。远远比不上同题材纪录片的拍摄水平。但跟随王亚平的第一视角，听着充满感情但又无比克制的叙述，电影院每一个观众都屏气凝神，代入其中。最重要的是，这是【中国】载人航天的故事。有太多我熟悉的人，我熟悉的事出现在片中，和我的记忆深处击掌。
这是真正的种子。对我这样一个不再对知识充满足够敬仰的成年人尚且如此，我暂且羡慕一下我旁边坐的孩子们。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚇 上海地铁&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;15:21-15:56&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;莲花路 → 人民广场&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;因为晚上要参加&lt;strong&gt;摇曳露营的音乐会&lt;/strong&gt;，因此希望找到一些相关的谷子（比较喜欢刚生日不久的芝麻凛），在小红书上发现有人分享找到露营谷子的地点，于是前去寻找。出地铁站的时候走错了出口，费了一番功夫才到达目的地，也就是第一百货的&lt;strong&gt;第一绮丽次元街区&lt;/strong&gt;，经过探索我已认可其浓度——比后面去的静安大悦城强太多。&lt;/p&gt;
&lt;p&gt;比较难受的是从地铁站出去的时候忘记存包，人民广场附近人太多又不方便再返回，只得背着巨大的包在狭窄的谷店中穿来穿去——如果不是这个包我肯定可以多逛一会的。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚇 上海地铁&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;16:55-17:05&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;人民广场 → 曲阜路&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;总算在曲阜路站把包存了下来，&lt;strong&gt;地铁站寄存柜&lt;/strong&gt; 价格非常合理，查看空余寄存柜数量可以下载APP【Metro大都会】，在【服务】中查询。初到上海不方便入住的非常推荐使用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/images/travel-and-discovery-national-day-holiday-travel-diary-2025/IMG_20251002_170452.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;曲阜路站附近的&lt;strong&gt;静安大悦城&lt;/strong&gt;很热闹、很有名，但想坐电梯时等了快十分钟（当然也有一部分原因是我选错了电梯点），谷店同质化严重，体验并不算友好。作为商场建筑倒确实有很多可圈可点之处。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚇 上海地铁&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;18:37-18:45 &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;曲阜路 → 大世界&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;这一站附近是&lt;strong&gt;凯迪拉克上海音乐厅&lt;/strong&gt;，在这里参加了摇曳露营十周年音乐会。キミのね蒙面出场，亚咲花老师非常可爱，唱功也无敌。前期的演奏环节中规中矩，到后面的演唱还是很嗨的。可惜没有提前学一下那几首OP，非常想跟唱www。&lt;/p&gt;
&lt;p&gt;对了，立山秋航老师藏在观众席也很有趣。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/images/travel-and-discovery-national-day-holiday-travel-diary-2025/IMG_20251002_190219.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚇 上海地铁&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;21:32-23:27 &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;大世界 → 临港大道 &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
本次旅行最后一次上海地铁。16号线NB，但是临港真的太远了......
没注意大站快车和直达车的时间，下次去临港注意学习一下，不知道能不能更快。
临港有无人驾驶、氢弹等有趣的交通方式值得体验。下次一定！&lt;/p&gt;
&lt;p&gt;【地铁路上看到窗户外一片漆黑，反射出坐在前面的女生正在用手机app背单词（好像是？）的画面....】&lt;/p&gt;
&lt;p&gt;住宿是舍友在美团上订的民宿，其实就是小区内的一间房子。卫生一般。睡觉时有被蚊子打扰。但家庭布局还是很爽的，适合三人出行，价格也很便宜。&lt;/p&gt;
&lt;h1&gt;10.3&lt;/h1&gt;
&lt;p&gt;民宿到公交站有一段距离，通过打车解决的。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚌 上海公交 洋山专线&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;9:00-10:00&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;票价&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;¥11&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;滴水湖枢纽站 → 沈家湾客运码头&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;附注&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;有直达车安排，坐满即发车。这是一个跨省公交线路。通过东海大桥的好方式。&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;非常喜欢大桥的我可惜没能坐到靠窗的位置。看到了窗外装满集装箱的港口，还是感到非常兴奋。&lt;/p&gt;
&lt;p&gt;至于客运码头，体验和高铁站无异。有的码头要刷身份证，有的则是要用机器上取的船票。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;⛴️ 轮渡 嵊翔15轮（游轮客船） 49491次&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;11:00-12:15&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;票价&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;¥130&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 沈家湾 → 李柱山（泗礁）&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;座次&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 上舱 060&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;附注&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;需要提前6日在官方渠道抢票（微信服务号-嵊泗客运），也可联系民宿老板购票&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;游轮客船比想象中无趣很多。睡觉+发呆中度过。&lt;/p&gt;
&lt;p&gt;到达泗礁李柱山码头后，打车前往基湖沙滩附近的民宿（但一直到最后也没去基湖沙滩玩）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;值得注意的是，李柱山码头有两个出入口，一个要走很远的连廊，另一个则人少、不需要走太远。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;民宿是三床房，非常合适。&lt;/p&gt;
&lt;p&gt;午饭也在民宿附近完成，点了鱼和其他几样菜。味道还是很好的。&lt;/p&gt;
&lt;p&gt;吃完饭回到民宿休息——说是休息，结果打了一个小时的斗地主（哈哈两盘积分我都是倒第一），下午四点才出发。尽管下雨，但还是决定冒雨前进。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-activity-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-activity-header&quot;&amp;gt;🏍 美团共享电单车&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-activity-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;16:00-17:00-18:12 &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-activity-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;民宿 → 六井潭景区 → 东海渔村&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;冒雨骑着电摩，看着海边的景象，经过两个免费海滩、知名的左岸公路，马上感受到海岛的魅力，心情变好。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/images/travel-and-discovery-national-day-holiday-travel-diary-2025/1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在东海渔村旁边的免费海滩玩耍后，打车回到民宿。&lt;/p&gt;
&lt;p&gt;回到民宿后简单休整，仍然是在附近吃晚饭，烧烤贵而难吃，鱼贵而好吃。&lt;/p&gt;
&lt;h1&gt;10.4&lt;/h1&gt;
&lt;p&gt;早早从民宿出发，打车前往&lt;strong&gt;六井潭&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;风景非常好，就是爬山实在给人累到了。&lt;/p&gt;
&lt;p&gt;租用景区小车，从六井潭前往&lt;strong&gt;和尚套&lt;/strong&gt;景区。两个景区都属于&lt;strong&gt;六合朝阳景区&lt;/strong&gt;，票是一起卖的，学生票45r。&lt;/p&gt;
&lt;p&gt;打车从和尚套回到民宿，退房，附近买泡面作为午饭，再打车去往小菜园码头。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;⛴️ 轮渡 嵊翔10轮（常规客船） 11090次&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;12:20-13:50&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;票价&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;¥50&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 小菜园（泗礁） → 花鸟&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;座次&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 中舱 144&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;花鸟岛入岛即收门票，整个岛算作一个景点，我们对此很新奇。&lt;/p&gt;
&lt;p&gt;从花鸟码头步行到民宿，休息后在岛的东部游玩、逛街、买奶茶、吃晚饭。晚饭后同样如此，在花鸟岛随便转了转。这个岛真的非常小，核心道路只有一条码塔路（好像就是码头到灯塔的意思）&lt;/p&gt;
&lt;h1&gt;10.5&lt;/h1&gt;
&lt;p&gt;早起看日出 5:20-6:20
&amp;lt;div class=&quot;fancy-activity-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-activity-header&quot;&amp;gt;🌅 观日出 &amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-activity-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;5:20-6:20&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-activity-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;天气&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;晴，有云&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-activity-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;体验&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;未看到太阳从海平线升起（被云遮住了一会），但还是很壮观&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;步行回民宿简单休息，然后出来吃早饭。花鸟岛的早饭非常贵，有豆腐脑、馄饨啥的，但价格是我们那里的4倍。。。&lt;/p&gt;
&lt;p&gt;景区共享电动车 A站-花鸟灯塔 7:50-8:35&lt;/p&gt;
&lt;p&gt;景区共享电动车 花鸟灯塔-A站 9:13-9:46&lt;/p&gt;
&lt;p&gt;体验非常好。再一次感受到海岛的魅力。&lt;/p&gt;
&lt;p&gt;从A站步行回民宿休息，买了果茶。然后步行去街上吃午饭。一般且贵。回民宿退房，步行到花鸟码头。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;⛴️ 轮渡 嵊翔10轮（常规客船） 11460次&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;14:00-15:30&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;票价&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;¥50&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 花鸟 → 小菜园（泗礁）&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;座次&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 前中舱 060&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;步行到&lt;strong&gt;嘉域生活中心&lt;/strong&gt;，在KFC休息。然后到旁边的&lt;strong&gt;嵊泗县图书馆&lt;/strong&gt;看了会书。又回到KFC买晚餐。随后打车从KFC到李柱山码头。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在图书馆看的是凉宫春日hhh，现在已经能熟悉地使用索书号找感兴趣的书了。顺便浙江省的各地的图书馆外地人办理借书也很方便！江浙沪这一块都做的很不错啊。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;⛴️ 车船联运 舟桥5轮（客滚船） 18201次 + 大巴车&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;18:00-19:20-21:30&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;票价&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;¥100 + ¥34 = ¥134&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 李柱山（泗礁）→ 沈家湾 → 上海南浦&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;座次&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt; 上舱 053&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;客滚船赛高！！！最自由。可以出客舱在栏杆旁边看海。不过旁边坐了很多精力旺盛的小孩子，可爱但是从我并不宽裕的腿前空间跑来跑去还是有点烦人的w&lt;/p&gt;
&lt;p&gt;乘坐联运大巴到达南浦，随后打车到上海站。&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚄 国铁 G7212 CRH2C-????&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;22:23-22:52&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;上海站 → 苏州站&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;座位&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;二等座 · 02车20A号&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚇 苏州地铁&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;22:57-23:17&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;苏州火车站 → 狮子山&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-transit-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-transit-header&quot;&amp;gt;🚋 苏州高新有轨电车1号线&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;时间&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;23:18-0:10&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-transit-row&quot;&amp;gt;
&amp;lt;strong&amp;gt;路线&amp;lt;/strong&amp;gt;
&amp;lt;span&amp;gt;狮子山 → 南京大学南&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;步行回到宿舍，结束！&lt;/p&gt;
&lt;h1&gt;其他经验与总结&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;花鸟岛民宿预订最好直接找到当地老板加微信谈价格。美团上会更贵。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;岛内交通方式为出租（个人建议学会让司机打发票）+共享电单车+景区小车。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;防晒相关、拖鞋、晕船药、止泻药、望远镜...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;div class=&quot;fancy-summary-card&quot;&amp;gt;
&amp;lt;div class=&quot;fancy-summary-header&quot;&amp;gt;拖了一个月的总结&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;fancy-summary-content&quot;&amp;gt;
&amp;lt;p&amp;gt;实际写下来才发现，这好像也算不上一次很有趣的旅行，我们也没探索什么新奇的东西。但有了舍友结伴的高配置，再加上离出发地很近不需要长途跋涉的超绝性价比，还是很宝贵的一次经历呢！也许就像《孤独摇滚》里面大家一起去江之岛一样，是必要的一集。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;多年之后，我一定还有机会来到舟山，探索更多没去过的岛屿，重访这次旅行到过的地点，只是不知道那时候的我会抱着什么样的心情，和着什么样的人一起呢~&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>经验-2025年浙计预推免</title><link>https://blog.stivine.fun/posts/experience-zju-cs-early-test-for-postgraduate-recommendation/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/experience-zju-cs-early-test-for-postgraduate-recommendation/</guid><description>浙寄（√）</description><pubDate>Sun, 21 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本文仅为作者个人日记，不构成任何形式的指南或保证。本文信息可能过时或不适用于你的具体情况。因参考本文产生的任何后果，作者概不负责。
&amp;lt;/div&amp;gt;
时间：2025年9月20日
形式：仅面试（20min）&lt;/p&gt;
&lt;p&gt;5min 自我陈述（含任意一段英文口语）
15min 提问&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;专业课&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;线代：谈谈对特征向量和特征值的理解&lt;/p&gt;
&lt;p&gt;OS：谈谈死锁及几种解决方法【英文提问、英文回答】&lt;/p&gt;
&lt;p&gt;算法：基础排序&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;项目&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lora 的细节&lt;/p&gt;
&lt;p&gt;多模态大模型的基本架构&lt;/p&gt;
&lt;p&gt;大模型生成和理解的区别&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;其他&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;问了问我院和AI院有什么关系&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总体体验&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;比 FDU 压力，但也还好&lt;/p&gt;
&lt;p&gt;我自己回答的依托构式（（（&lt;/p&gt;
</content:encoded></item><item><title>经验-2025年南大智科预推免</title><link>https://blog.stivine.fun/posts/experience-nju-is-early-test-for-postgraduate-recommendation/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/experience-nju-is-early-test-for-postgraduate-recommendation/</guid><description>内容来自和他人交流</description><pubDate>Sat, 13 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本文信息借助不可信回忆+AI生成，不构成任何形式的指南或保证。本文信息可能过时或不适用于你的具体情况。因参考本文产生的任何后果，作者概不负责。
&amp;lt;/div&amp;gt;
时间：2025年9月12日-9月13日&lt;/p&gt;
&lt;p&gt;形式和考察范围等和夏令营基本一致&lt;/p&gt;
&lt;h1&gt;笔试&lt;/h1&gt;
&lt;p&gt;考察了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;命题逻辑、一阶逻辑&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;已知p1, p2, p3 = softmax(z1, z2, z3)，真实标签为y1, y2, y3，求 softmax 关于输入求导，并结合交叉熵损失&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;设：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;模型输出（logits）为：&lt;br /&gt;
$$
\mathbf{z} = [z_1, z_2, z_3]
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;经过 Softmax 后的预测概率为：
$$
p_i = \text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{k=1}^3 e^{z_k}} = \frac{e^{z_i}}{S}, \quad S = \sum_{k=1}^3 e^{z_k}
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;真实标签为 one-hot 编码形式：&lt;br /&gt;
$$
\mathbf{y} = [y_1, y_2, y_3], \quad \text{其中 } y_i \in {0,1}, \sum y_i = 1
$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;交叉熵损失函数为：
$$
L = -\sum_{i=1}^3 y_i \log p_i
$$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们的目标是求：&lt;strong&gt;损失 $ L $ 对 logits $ z_j $ 的导数 $ \frac{\partial L}{\partial z_j} $&lt;/strong&gt;，这在反向传播中非常重要。&lt;/p&gt;
&lt;p&gt;先计算 $ \frac{\partial p_i}{\partial z_j} $。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;情况 1：当 $ i = j $&lt;/strong&gt;
$$
\frac{\partial p_i}{\partial z_i} = \frac{\partial}{\partial z_i} \left( \frac{e^{z_i}}{S} \right)
= \frac{e^{z_i} \cdot S - e^{z_i} \cdot e^{z_i}}{S^2}
= \frac{e^{z_i}}{S} - \frac{e^{2z_i}}{S^2}
= p_i - p_i^2 = p_i(1 - p_i)
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;情况 2：当 $ i \neq j $&lt;/strong&gt;
$$
\frac{\partial p_i}{\partial z_j} = \frac{\partial}{\partial z_j} \left( \frac{e^{z_i}}{S} \right)
= e^{z_i} \cdot \left( -\frac{e^{z_j}}{S^2} \right)
= -\frac{e^{z_i} e^{z_j}}{S^2}
= -p_i p_j
$$&lt;/p&gt;
&lt;p&gt;所以统一写成：&lt;/p&gt;
&lt;p&gt;$$
\frac{\partial p_i}{\partial z_j} =
\begin{cases}
p_i(1 - p_i), &amp;amp; i = j \
-p_i p_j, &amp;amp; i \neq j
\end{cases}
= p_i (\delta_{ij} - p_j)
$$&lt;/p&gt;
&lt;p&gt;其中 $ \delta_{ij} $ 是 Kronecker delta 函数（$ i=j $ 时为 1，否则为 0）。
$$
\frac{\partial L}{\partial z_j} = \frac{\partial}{\partial z_j} \left( -\sum_{i=1}^3 y_i \log p_i \right)
= -\sum_{i=1}^3 y_i \cdot \frac{1}{p_i} \cdot \frac{\partial p_i}{\partial z_j}
$$&lt;/p&gt;
&lt;p&gt;代入上面的导数表达式：&lt;/p&gt;
&lt;p&gt;$$
\frac{\partial L}{\partial z_j} = -\sum_{i=1}^3 y_i \cdot \frac{1}{p_i} \cdot p_i (\delta_{ij} - p_j)
= -\sum_{i=1}^3 y_i (\delta_{ij} - p_j)
$$&lt;/p&gt;
&lt;p&gt;拆开求和：&lt;/p&gt;
&lt;p&gt;$$
= -\left( \sum_{i=1}^3 y_i \delta_{ij} - \sum_{i=1}^3 y_i p_j \right)
= -\left( y_j - p_j \sum_{i=1}^3 y_i \right)
$$&lt;/p&gt;
&lt;p&gt;由于 $ \sum y_i = 1 $（one-hot 标签），所以：&lt;/p&gt;
&lt;p&gt;$$
\frac{\partial L}{\partial z_j} = -(y_j - p_j) = p_j - y_j
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$ \mathbf{p} = \text{softmax}(\mathbf{z}) $&lt;/li&gt;
&lt;li&gt;$ \mathbf{y} $ 为 one-hot 真实标签&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;则：&lt;/p&gt;
&lt;p&gt;$$
\nabla_{\mathbf{z}} L = \mathbf{p} - \mathbf{y}
$$&lt;/p&gt;
&lt;p&gt;这个结果意味着，在实现分类网络时，&lt;strong&gt;Softmax + CrossEntropyLoss 的反向传播梯度就是预测概率与真实标签之差&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;此外，现代框架（如 PyTorch）提供 &lt;code&gt;LogSoftmax&lt;/code&gt; 和 &lt;code&gt;NLLLoss&lt;/code&gt; 或直接 &lt;code&gt;CrossEntropyLoss&lt;/code&gt;，内部已融合优化，避免显式计算 $ \log(p_i) $ 带来的数值问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;动态规划：nxm 矩阵，左下角到右上角，每一步可以→↑↗，求方法总数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判断完全二叉树&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关系分类（关系是不是反射的？）、判断映射（满射？）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;贝叶斯定理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线性回归 wx+b&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CNN 参数量计算&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;机试&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;前两题为强制使用python的算法题，后两题为强制使用C++的算法题。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Q1&lt;/h2&gt;
&lt;p&gt;略，较为简单&lt;/p&gt;
&lt;h2&gt;Q2 最长“山形”子序列&lt;/h2&gt;
&lt;h3&gt;题目描述&lt;/h3&gt;
&lt;p&gt;给定一个长度为 $n$ 的整数数组 $a$，求最长的子序列长度，该子序列满足：&lt;strong&gt;先严格递增，后严格递减&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;特别地，以下情况也被视为合法的“山形”子序列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;仅包含严格递增部分（不下降部分）&lt;/li&gt;
&lt;li&gt;仅包含严格递减部分（不上升部分）&lt;/li&gt;
&lt;li&gt;仅包含一个元素&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;子序列不要求连续。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一行一个正整数 $n$，表示数组长度。&lt;br /&gt;
第二行 $n$ 个整数 $a_1, a_2, \dots, a_n$，表示数组元素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输出格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输出一个整数，表示满足条件的最长子序列的长度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;样例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;样例输入 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;7
1 3 2 5 7 6 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输出 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例解释 1
一个最长的合法子序列为：&lt;code&gt;1 3 5 7 4&lt;/code&gt; 或 &lt;code&gt;1 3 5 6 4&lt;/code&gt;，长度为 5。&lt;/p&gt;
&lt;p&gt;样例输入 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5
5 4 3 2 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输出 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例解释 2
整个序列严格递减，是合法的（只有下降部分）。&lt;/p&gt;
&lt;p&gt;样例输入 3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5
1 2 3 4 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输出 3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例解释 3
整个序列严格递增，是合法的（只有上升部分）。&lt;/p&gt;
&lt;p&gt;数据范围&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于 $30%$ 的数据，$1 \leq n \leq 100$&lt;/li&gt;
&lt;li&gt;对于 $60%$ 的数据，$1 \leq n \leq 1000$&lt;/li&gt;
&lt;li&gt;对于 $100%$ 的数据，$1 \leq n \leq 10^5$，$1 \leq a_i \leq 10^9$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;提示
子序列可以从原序列中不连续地选取元素，但必须保持原有顺序。&lt;/p&gt;
&lt;h3&gt;解题思路&lt;/h3&gt;
&lt;p&gt;我们可以使用动态规划：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;计算以每个位置 &lt;code&gt;i&lt;/code&gt; 结尾的最长严格递增子序列长度 &lt;code&gt;dp_inc[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算以每个位置 &lt;code&gt;i&lt;/code&gt; 开始的最长严格递减子序列长度 &lt;code&gt;dp_dec[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;对于每个位置 &lt;code&gt;i&lt;/code&gt;，它可以作为“山顶”，则通过它的最长山形子序列长度为：&lt;code&gt;dp_inc[i] + dp_dec[i] - 1&lt;/code&gt;（减1是因为山顶被计算了两次）&lt;/li&gt;
&lt;li&gt;答案就是所有位置中的最大值&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;def solve():
    n = int(input())
    if n == 0:
        return 0
    a = list(map(int, input().split()))
    
    # dp_inc[i]: 以位置 i 结尾的最长严格递增子序列长度
    dp_inc = [1] * n
    for i in range(1, n):
        for j in range(i):
            if a[j] &amp;lt; a[i]:
                dp_inc[i] = max(dp_inc[i], dp_inc[j] + 1)
    
    # dp_dec[i]: 从位置 i 开始的最长严格递减子序列长度
    dp_dec = [1] * n
    for i in range(n-2, -1, -1):
        for j in range(i+1, n):
            if a[i] &amp;gt; a[j]:
                dp_dec[i] = max(dp_dec[i], dp_dec[j] + 1)
    
    # 找到最长的山形子序列
    max_length = 0
    for i in range(n):
        length = dp_inc[i] + dp_dec[i] - 1
        max_length = max(max_length, length)
    
    return max_length

# 读取输入并输出结果
print(solve())
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Q3 字符串平移拼接序列的第 K 个字符&lt;/h2&gt;
&lt;h3&gt;题目描述&lt;/h3&gt;
&lt;p&gt;定义一个字符串生成序列 $S_0, S_1, S_2, \dots$，规则如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始值：$S_0 = \texttt{&quot;a&quot;}$&lt;/li&gt;
&lt;li&gt;递推规则：对于 $i \geq 1$，有 $S_i = S_{i-1} + \text{shift}(S_{i-1})$
&lt;ul&gt;
&lt;li&gt;其中 $\text{shift}(T)$ 表示将字符串 $T$ 中每个字符按字母表向后平移一位：
&lt;ul&gt;
&lt;li&gt;$\texttt{&apos;a&apos;} \to \texttt{&apos;b&apos;},\ \texttt{&apos;b&apos;} \to \texttt{&apos;c&apos;},\ \dots,\ \texttt{&apos;y&apos;} \to \texttt{&apos;z&apos;},\ \texttt{&apos;z&apos;} \to \texttt{&apos;a&apos;}$&lt;/li&gt;
&lt;li&gt;即循环右移：字符 $c$ 变为 $(c - \texttt{&apos;a&apos;} + 1) \bmod 26 + \texttt{&apos;a&apos;}$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$S_0 = \texttt{&quot;a&quot;}$&lt;/li&gt;
&lt;li&gt;$S_1 = \texttt{&quot;a&quot; + shift(&quot;a&quot;)} = \texttt{&quot;a&quot; + &quot;b&quot;} = \texttt{&quot;ab&quot;}$&lt;/li&gt;
&lt;li&gt;$S_2 = \texttt{&quot;ab&quot; + shift(&quot;ab&quot;)} = \texttt{&quot;ab&quot; + &quot;bc&quot;} = \texttt{&quot;abbc&quot;}$&lt;/li&gt;
&lt;li&gt;$S_3 = \texttt{&quot;abbc&quot; + shift(&quot;abbc&quot;)} = \texttt{&quot;abbc&quot; + &quot;bccd&quot;} = \texttt{&quot;abbcbccd&quot;}$&lt;/li&gt;
&lt;li&gt;$S_4 = \texttt{&quot;abbcbccd&quot; + shift(&quot;abbcbccd&quot;)} = \texttt{&quot;abbcbccd&quot; + &quot;bccdcdee&quot;} = \texttt{&quot;abbcbccdbccdcdee&quot;}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;给定一个正整数 $k$，请求出在某个足够大的 $n$ 下，$S_n$ 的第 $k$ 个字符（1-indexed）。&lt;/p&gt;
&lt;p&gt;注意：由于 $S_n$ 的长度随 $n$ 指数增长，你不需要构造整个字符串，而应设计高效算法直接定位第 $k$ 个字符。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一行一个正整数 $k$，表示查询位置（从 1 开始计数）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输出格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输出一个字符，表示序列中第 $k$ 个位置上的小写字母。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;样例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;样例输入 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输出 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输入 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输出 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例解释 2
$S_2 = \texttt{&quot;abbc&quot;}$，第 3 个字符是 $\texttt{&apos;b&apos;}$。&lt;/p&gt;
&lt;p&gt;样例输入 3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例输出 3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;样例解释 3
$S_3 = \texttt{&quot;abbcbccd&quot;}$，第 7 个字符是 $\texttt{&apos;c&apos;}$。&lt;/p&gt;
&lt;p&gt;数据范围&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于 $30%$ 的数据，$1 \leq k \leq 10^3$&lt;/li&gt;
&lt;li&gt;对于 $60%$ 的数据，$1 \leq k \leq 10^6$&lt;/li&gt;
&lt;li&gt;对于 $100%$ 的数据，$1 \leq k \leq 10^{18}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;提示&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串长度满足：$\text{len}(S_0) = 1$，$\text{len}(S_i) = 2 \times \text{len}(S_{i-1})$
&lt;ul&gt;
&lt;li&gt;即 $\text{len}(S_i) = 2^i$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;可通过递归方式求解：若 $k \leq 2^{n-1}$，则答案在左半部分；否则在右半部分（需还原平移操作）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解题思路&lt;/h3&gt;
&lt;p&gt;我们观察到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$S_0 = \texttt{&quot;a&quot;}$, 长度为 $2^0 = 1$&lt;/li&gt;
&lt;li&gt;$S_i = S_{i-1} + \text{shift}(S_{i-1})$，长度为 $2^i$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关键性质：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每次操作将字符串长度翻倍&lt;/li&gt;
&lt;li&gt;前半部分就是 $S_{i-1}$&lt;/li&gt;
&lt;li&gt;后半部分是前半部分整体字符向后平移一位（z→a）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此我们可以&lt;strong&gt;递归&lt;/strong&gt;求解第 $k$ 个字符：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找到最小的 $n$ 使得 $2^n \geq k$，即确定 $k$ 所在的字符串层级&lt;/li&gt;
&lt;li&gt;如果 $k == 1$ 且当前视为 $S_0$，返回 &lt;code&gt;&apos;a&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;若 $k \leq 2^{n-1}$，说明在左半部分，递归处理 $S_{n-1}$&lt;/li&gt;
&lt;li&gt;若 $k &amp;gt; 2^{n-1}$，说明在右半部分，对应位置为 $k - 2^{n-1}$，递归得到该位置在左半部分的原始字符，再将其&lt;strong&gt;逆平移&lt;/strong&gt;（向前移一位）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意：右半部分比左半部分每个字符大1，所以我们找到对应位置的字符后要还原。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

char solve(long long k) {
    if (k == 1) {
        return &apos;a&apos;;
    }
    
    // 找到最小的 n 使得 2^n &amp;gt;= k
    long long length = 1;
    while (length &amp;lt; k) {
        length &amp;lt;&amp;lt;= 1; // 相当于 length *= 2
    }
    
    long long half = length &amp;gt;&amp;gt; 1; // 左半部分长度 = length / 2
    
    if (k &amp;lt;= half) {
        // 在左半部分，递归处理
        return solve(k);
    } else {
        // 在右半部分，对应左半部分的位置是 k - half
        char c = solve(k - half);
        // 右半部分字符 = 左半部分字符 + 1 (循环)
        return &apos;a&apos; + (c - &apos;a&apos; + 1) % 26;
    }
}

int main() {
    long long k;
    cin &amp;gt;&amp;gt; k;
    cout &amp;lt;&amp;lt; solve(k) &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;复杂度分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：$O(\log k)$，每次递归将问题规模减半&lt;/li&gt;
&lt;li&gt;空间复杂度：$O(\log k)$，递归栈深度&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;该算法可以高效处理 $k \leq 10^{18}$ 的情况。&lt;/p&gt;
&lt;h2&gt;Q4&lt;/h2&gt;
&lt;p&gt;略，较为简单&lt;/p&gt;
&lt;h1&gt;面试&lt;/h1&gt;
&lt;p&gt;自我介绍
英文：未来研究计划
项目
为何项目和科研兴趣不一致
机器学习 决策树
对三维重建有什么了解
自己的优点和缺点
transformer&lt;/p&gt;
</content:encoded></item><item><title>经验-2025年FDU CS夏令营</title><link>https://blog.stivine.fun/posts/experience-fdu-cs-summer-camp-2025/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/experience-fdu-cs-summer-camp-2025/</guid><description>菜而不练的结果就是变得更菜...</description><pubDate>Thu, 28 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本文仅为作者个人日记，不构成任何形式的指南或保证。本文信息可能过时或不适用于你的具体情况。因参考本文产生的任何后果，作者概不负责。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;时间：2025年8月25日-8月28日&lt;/p&gt;
&lt;h1&gt;基本信息&lt;/h1&gt;
&lt;h1&gt;Day 1: 报到 / 志愿填报&lt;/h1&gt;
&lt;p&gt;TODO&lt;/p&gt;
&lt;h1&gt;Day 2: 机试&lt;/h1&gt;
&lt;p&gt;电脑上有 VS、VSCode（提供 CodeRunner 插件及使用方法）、Dev-cpp 等IDE，类似 OI 赛制，无法得知评测实际得分，支持 C++17&lt;/p&gt;
&lt;h2&gt;Q1 考试分数&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;搞崩我机试的罪魁祸首。。。考场上的我的罪过不是做不出来这题，而是没判断出来这题我的思路不可能做的出来。。。&lt;/p&gt;
&lt;p&gt;考点：&lt;strong&gt;状态压缩DP&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;题面&lt;/h3&gt;
&lt;p&gt;小明参加了多门科目的考试，他只记得各科得分拼接成的字符串 &lt;code&gt;s&lt;/code&gt;，但忘记了具体的分割方式。&lt;/p&gt;
&lt;p&gt;已知：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有 &lt;code&gt;m&lt;/code&gt; 门科目的满分分别是 &lt;code&gt;r[1], r[2], ..., r[m]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;其余科目的满分均为 &lt;code&gt;t&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;请你帮助小明解析字符串 &lt;code&gt;s&lt;/code&gt;，将其分割为若干非负整数（表示每科得分），使得：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个得分不超过其对应科目的满分；&lt;/li&gt;
&lt;li&gt;恰好使用 &lt;code&gt;r&lt;/code&gt; 中的每个满分值一次（即有 &lt;code&gt;m&lt;/code&gt; 个科目使用 &lt;code&gt;r[i]&lt;/code&gt; 作为满分）；&lt;/li&gt;
&lt;li&gt;其余科目使用满分 &lt;code&gt;t&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;所有得分拼接后等于 &lt;code&gt;s&lt;/code&gt;，且每个数无前导零（除非是单独的 &quot;0&quot;）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在所有合法方案中，求最大可能的总分。数据保证存在合法方案。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入格式：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一行三个整数：&lt;code&gt;n&lt;/code&gt;（字符串长度），&lt;code&gt;t&lt;/code&gt;（通用满分），&lt;code&gt;m&lt;/code&gt;（特殊满分科目数）&lt;/p&gt;
&lt;p&gt;第二行：一个长度为 &lt;code&gt;n&lt;/code&gt; 的字符串 &lt;code&gt;s&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;第三行：&lt;code&gt;m&lt;/code&gt; 个整数，表示数组 &lt;code&gt;r&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输出格式：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个整数，表示最大可能总分&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;样例：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;6 100 2
859271
90 80
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;248
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;参考解答&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 答案借助AI生成，仅供参考
// 时间复杂度 O(n² × 2^m × m)
// 空间复杂度 O(n × 2^m)

#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, t, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; t &amp;gt;&amp;gt; m;
    
    string s;
    cin &amp;gt;&amp;gt; s;
    
    vector&amp;lt;int&amp;gt; r(m);
    for (int i = 0; i &amp;lt; m; ++i) {
        cin &amp;gt;&amp;gt; r[i];
    }

    // total_mask: 所有特殊满分科目的状态总数
    int total_mask = 1 &amp;lt;&amp;lt; m;

    // dp[i][mask]: 前 i 个字符，使用了 r 的 mask 集合时的最大总分
    vector&amp;lt;vector&amp;lt;long long&amp;gt;&amp;gt; dp(n + 1, vector&amp;lt;long long&amp;gt;(total_mask, LLONG_MIN));
    dp[0][0] = 0;

    // 预处理：检查从 start 开始长度为 len 的子串是否合法，并获取数值
    auto parseNum = [&amp;amp;](int start, int len) -&amp;gt; long long {
        if (len == 0) return -1;
        if (len &amp;gt; 1 &amp;amp;&amp;amp; s[start] == &apos;0&apos;) return -1; // 前导零非法
        long long num = 0;
        for (int i = start; i &amp;lt; start + len; ++i) {
            if (!isdigit(s[i])) return -1;
            num = num * 10 + (s[i] - &apos;0&apos;);
        }
        return num;
    };

    // 枚举结束位置 i
    for (int i = 1; i &amp;lt;= n; ++i) {
        // 枚举起始位置 j
        for (int j = 0; j &amp;lt; i; ++j) {
            int len = i - j;
            long long num = parseNum(j, len);
            if (num == -1) continue; // 非法数字

            // 枚举之前的状态：dp[j][mask]
            for (int mask = 0; mask &amp;lt; total_mask; ++mask) {
                if (dp[j][mask] == LLONG_MIN) continue;

                // 选项1：使用 t 作为通用满分（剩余科目）
                if (num &amp;lt;= t) {
                    if (dp[i][mask] &amp;lt; dp[j][mask] + num) {
                        dp[i][mask] = dp[j][mask] + num;
                    }
                }

                // 选项2：使用 r 中的某个未使用的特殊满分
                for (int k = 0; k &amp;lt; m; ++k) {
                    if ((mask &amp;amp; (1 &amp;lt;&amp;lt; k)) == 0) { // r[k] 未使用
                        if (num &amp;lt;= r[k]) {
                            int new_mask = mask | (1 &amp;lt;&amp;lt; k);
                            if (dp[i][new_mask] &amp;lt; dp[j][mask] + num) {
                                dp[i][new_mask] = dp[j][mask] + num;
                            }
                        }
                    }
                }
            }
        }
    }

    // 输出结果：处理完所有字符，且所有特殊满分科目都被使用
    cout &amp;lt;&amp;lt; dp[n][total_mask - 1] &amp;lt;&amp;lt; endl;

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意：面试时问我如何进一步优化空间。之前有人说滚动数组，但我目前没想明白在这个题上是怎么弄。&lt;/p&gt;
&lt;h2&gt;Q2 中位数&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;这个还真不难。考场上我基本已经完成了大半......考场上的我怎么会觉得使用set不行呢？？？明明知道set是有序的。。。&lt;/p&gt;
&lt;p&gt;我当时想到流式数据的中位数可以用对顶堆维护，是在力扣hot100学到的。不过删除元素又该怎么办呢？于是考场上的我陷入了深深的迷茫。。。。。。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;题面&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;题目描述：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;给定一个长度为 $ n $ 的整数数组 $ a $，以及一个滑动窗口大小 $ m $。窗口从数组最左端开始，每次向右滑动一个位置，直到覆盖最右端，共产生 $ n - m + 1 $ 个窗口。&lt;/p&gt;
&lt;p&gt;对于每个窗口，定义“&lt;strong&gt;独立数&lt;/strong&gt;”为在该窗口中&lt;strong&gt;恰好出现一次&lt;/strong&gt;的数字。&lt;/p&gt;
&lt;p&gt;将每个窗口的所有独立数按升序排序，取排序后序列的中位数：我们这里取下标为 $ \left\lfloor \frac{k-1}{2} \right\rfloor $ 的元素（$ k $ 为独立数个数）。若 $ k = 0 $，请输出 &quot;No&quot;&lt;/p&gt;
&lt;p&gt;请输出每个窗口的中位数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入格式：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一行两个整数：$ n $（数组长度），$ m $（窗口大小）&lt;/p&gt;
&lt;p&gt;第二行 $ n $ 个整数：表示数组 $ a $&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输出格式：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一行 $ n - m + 1 $ 个整数，表示每个窗口的独立数中位数，用空格隔开。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;数据范围：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$ 1 \leq m \leq n \leq 10^5 $&lt;/li&gt;
&lt;li&gt;$ 1 \leq a[i] \leq 10^9 $&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;样例输入：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;7 3
1 2 2 3 1 4 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;样例输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 3 2 3 4
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;参考解答&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 复杂度分析见结尾

#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

typedef long long ll;

int main() {

    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    vector&amp;lt;int&amp;gt; a(n);
    for (int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; a[i];
    }

    unordered_map&amp;lt;int, int&amp;gt; freq;
    set&amp;lt;int&amp;gt; independent;

    // 初始化第一个窗口
    for (int i = 0; i &amp;lt; m; i++) {
        freq[a[i]]++;
    }
    for (auto&amp;amp; [num, cnt] : freq) {
        if (cnt == 1) {
            independent.insert(num);
        }
    }

    vector&amp;lt;int&amp;gt; ans;

    // 处理每个窗口
    for (int i = 0; i &amp;lt;= n - m; i++) {
        // 计算当前窗口中位数
        if (independent.empty()) {
            ans.push_back(-1);
        } else {
            auto it = independent.begin();
            int k = independent.size();
            int idx = (k - 1) / 2;
            advance(it, idx);
            ans.push_back(*it);
        }

        // 滑动窗口
        if (i == n - m) break;

        int left = a[i];
        int right = a[i + m];

        int old_freq_left = freq[left];
        freq[left]--;
        int new_freq_left = freq[left];

        if (old_freq_left == 1) {
            independent.erase(left);
        } else if (old_freq_left == 2) {
            independent.insert(left);
        }

        if (new_freq_left == 0) {
            freq.erase(left);
        }

        int old_freq_right = freq.count(right) ? freq[right] : 0;
        freq[right]++;
        int new_freq_right = freq[right];

        if (old_freq_right == 0) {
            independent.insert(right);
        } else if (old_freq_right == 1) {
            independent.erase(right);
        }
    }
    
    // 输出结果：-1 转换为 &quot;No&quot;
    for (int i = 0; i &amp;lt; ans.size(); i++) {
        if (i &amp;gt; 0) cout &amp;lt;&amp;lt; &quot; &quot;;
        if (ans[i] == -1) {
            cout &amp;lt;&amp;lt; &quot;No&quot;;
        } else {
            cout &amp;lt;&amp;lt; ans[i];
        }
    }
    cout &amp;lt;&amp;lt; endl;


    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最坏 O(nm)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实际上集合维护是 O(nlogm) 的，但每次找中位数都要 O(k) （k：窗口内独立数个数），最坏可达 O(m)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对顶堆不能删除元素，所以就不能用了么？并非如此，可以用 multiset 模拟堆。还是对顶堆的思想，&lt;strong&gt;维护两个 multiset 即可&lt;/strong&gt;。这样每次插入/删除/查中位数都是 &lt;code&gt;O(log m)&lt;/code&gt;，这样总的时间复杂度也可以保证 O(nlogm)&lt;/p&gt;
&lt;h2&gt;Q3 树的重心&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;OI-wiki 上看到过。没认真看。这下服气了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;题面&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;题目描述&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;给定一棵由 $ n $ 个节点组成的无根树，我们定义一个节点的平衡值为：删除该节点后，所形成的若干连通块（子树）中，节点数的最大值。&lt;/p&gt;
&lt;p&gt;一棵树的&lt;strong&gt;重心&lt;/strong&gt;是指使该平衡值最小的节点。可以证明，一棵树最多有两个重心，且若有两个重心，则它们相邻。&lt;/p&gt;
&lt;p&gt;现在，请你找出这棵树的所有重心。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一行一个正整数 $ n $，表示树的节点数。&lt;/p&gt;
&lt;p&gt;接下来 $ n - 1 $ 行，每行两个正整数 $ u $ 和 $ v $，表示树上存在一条连接节点 $ u $ 和 $ v $ 的边。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输出格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输出共一行，按升序输出所有重心的编号，用空格隔开。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;样例输入&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5
1 2
2 3
3 4
4 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;样例输出&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;参考解答&lt;/h3&gt;
&lt;p&gt;详见 OI-wiki&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 时间复杂度： O(n)
// 空间复杂度： O(n)

const int MAXN = 50005;

int n;
// 这份代码默认节点编号从 1 开始，即 i ∈ [1,n]
int siz[MAXN],  // 这个节点的「大小」（所有子树上节点数 + 该节点）
    weight[MAXN];  // 这个节点的「重量」，即所有子树「大小」的最大值
vector&amp;lt;int&amp;gt; centroids;  // 用于记录树的重心（存的是节点编号）
vector&amp;lt;int&amp;gt; g[MAXN];

void dfs(int cur, int fa) {  // cur 表示当前节点 (current)
  siz[cur] = 1;
  weight[cur] = 0;
  for (int v : g[cur]) {
    if (v != fa) {  // v 表示这条有向边所通向的节点
      dfs(v, cur);
      siz[cur] += siz[v];
      weight[cur] = max(weight[cur], siz[v]);
    }
  }
  weight[cur] = max(weight[cur], n - siz[cur]);
  if (weight[cur] &amp;lt;= n / 2) {  // 依照树的重心的定义统计
    centroids.push_back(cur);
  }
}

void get_centroids() { dfs(1, 0); }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Q4 万年历&lt;/h2&gt;
&lt;h2&gt;Q5  Z-order 曲线 / Morton 序 坐标映射&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;考试的时候看都没看。没想到并不困难。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;题面&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;题目描述&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;给定一个边长为 $2^n$ 的正方形方阵（行和列均从 0 开始编号）。该方阵的构造规则如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将整个 $2^n \times 2^n$ 的方阵划分为四个 $2^{n-1} \times 2^{n-1}$ 的子方阵，分别称为&lt;strong&gt;左上、右上、左下、右下&lt;/strong&gt;子方阵。&lt;/li&gt;
&lt;li&gt;这四个子方阵按照 &lt;strong&gt;左上 → 右上 → 左下 → 右下&lt;/strong&gt; 的顺序，依次填入连续的整数，从 0 开始。&lt;/li&gt;
&lt;li&gt;每个子方阵内部的填充方式递归地遵循相同的规则，直到子方阵的边长为 $2^0 = 1$（即单个格子）为止。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如，当 $n = 2$ 时，方阵如下所示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; 0  1  4  5
 2  3  6  7
 8  9 12 13
10 11 14 15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，给定参数 $n$ 以及坐标 $(x, y)$，请求出该坐标处方阵中的数值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;输入格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输入共两行。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一行包含一个整数 $n$。&lt;/li&gt;
&lt;li&gt;第二行包含两个整数 $x$ 和 $y$，表示查询的坐标。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;输出格式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;输出一个整数，表示坐标 $(x, y)$ 处的数值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;样例输入&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2
3 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;样例输出&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;11
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;参考解答&lt;/h3&gt;
&lt;p&gt;直接构造整个方阵在 $n$ 较大时当然会超出时间和空间限制。&lt;strong&gt;递归即可解决。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 时间 O(n)
// 空间 O(n)

#include &amp;lt;iostream&amp;gt;
using namespace std;

long long solve(int n, long long x, long long y) {
    if (n == 0) {
        return 0;
    }

    long long mid = 1LL &amp;lt;&amp;lt; (n - 1); // 子方阵边长：2^(n-1)
    long long area_size = mid * mid; // 每个子方阵包含的格子数

    bool in_top = (x &amp;lt; mid);
    bool in_left = (y &amp;lt; mid);

    if (in_top &amp;amp;&amp;amp; in_left) {
        // 左上
        return solve(n - 1, x, y);
    } else if (in_top &amp;amp;&amp;amp; !in_left) {
        // 右上
        return area_size + solve(n - 1, x, y - mid);
    } else if (!in_top &amp;amp;&amp;amp; in_left) {
        // 左下
        return 2 * area_size + solve(n - 1, x - mid, y);
    } else {
        // 右下
        return 3 * area_size + solve(n - 1, x - mid, y - mid);
    }
}

int main() {
    int n;
    long long x, y;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; x &amp;gt;&amp;gt; y;

    cout &amp;lt;&amp;lt; solve(n, x, y) &amp;lt;&amp;lt; endl;

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上，Z-order 曲线 / Morton 码可以通过 &lt;strong&gt;位交织（bit interleaving）&lt;/strong&gt; 直接计算：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;long long z_order(int n, long long x, long long y) {
    long long result = 0;
    for (int i = 0; i &amp;lt; n; i++) {
        long long bit_x = (x &amp;gt;&amp;gt; i) &amp;amp; 1;
        long long bit_y = (y &amp;gt;&amp;gt; i) &amp;amp; 1;
        result |= (bit_y &amp;lt;&amp;lt; (2 * i)) | (bit_x &amp;lt;&amp;lt; (2 * i + 1));
    }
    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Day 2: 英语口试&lt;/h1&gt;
&lt;p&gt;共5分钟，简短自我介绍，然后是简单提问，介绍项目等。&lt;/p&gt;
&lt;h1&gt;Day 4: 面试&lt;/h1&gt;
&lt;p&gt;允许带简历。也可以不带。15分钟。&lt;/p&gt;
&lt;p&gt;7个面试官，但只有少数老师在提问。个人感觉负责提问的老师对我非常友善，但我自己非常不争气。。。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;机试
问机试成绩为什么这么差。（我开始解释怎么从第一题在上浪费了大量时间。）于是紧接着问现在有思路了嘛，讲讲第一题思路，如何进一步优化等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数学
一直掷硬币直到连续两次正面，问次数期望。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;项目
问了实现细节、模型效果。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AI
（看我学了深度学习）卷积神经网络的效果为什么好？它的核心特点是什么？
（我的某个项目用到了随机森林）介绍下随机森林算法？知不知道我们学校谁随机森林做的比较好？（看我是南大的想让我回答周老师但我没敢答。。。）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;其他聊天
问如何看待本科生科研和精力分配（看我没什么科研经历以及简历上写了很多乱七八糟的东西hhh）
问为什么报我们复旦？（我提到本校还没有offer）如果本校候补到了，和复旦你选哪个？
问对AIGC了解多少（感觉是老师觉得实在没什么可以问的了，问我啥我啥不会&amp;gt;_&amp;lt;）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;结果&lt;/h1&gt;
&lt;p&gt;铁定寄了&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;没想到成了最终去向w&lt;/p&gt;
</content:encoded></item><item><title>趣识-相似的抛物线与射影几何科普</title><link>https://blog.stivine.fun/posts/fun-knowledge-similar-parabolas/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/fun-knowledge-similar-parabolas/</guid><description>显然但没怎么思考过的性质</description><pubDate>Wed, 27 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;引入&lt;/h1&gt;
&lt;p&gt;刷贴吧的时候，发现数学吧有一个帖子称所有抛物线都是相似的。事实上这通过&lt;strong&gt;解析几何&lt;/strong&gt;的手段非常容易证明，通过同比例伸缩变换（证明略），任何抛物线都可以化为同一条标准抛物线。&lt;/p&gt;
&lt;p&gt;该简单性质的背后有没有什么有趣的东西？&lt;/p&gt;
&lt;p&gt;为什么圆锥曲线可以按离心率划分相似等价类 / 为什么离心率是相似不变量？&lt;/p&gt;
&lt;p&gt;经过简单的互联网搜索，发现很多人讨论过该性质，且在杂志《中小学数学》上有众多讨论该性质的文章，其内容质量令人哭笑不得——请注意，我并不仅仅只是因为其水分而感到可笑，这些文章甚至包含&lt;strong&gt;明显错误的内容和证明。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/images/Fun%20Knowledge-Similar%20Parabolas-1.png&quot; alt=&quot;Fun Knowledge-Similar Parabolas-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;双曲线怎么可能都是相似的？显然，这种文章是完全不靠谱的。&lt;/p&gt;
&lt;p&gt;那么，有没有其他角度看待该性质呢？尝试从射影几何角度分析。&lt;/p&gt;
&lt;h1&gt;射影几何&lt;/h1&gt;
&lt;p&gt;在平面几何里，我们习惯了这样的事实：平面上两条直线可能相交（有交点），也可能平行（没有交点）&lt;/p&gt;
&lt;p&gt;射影几何的关键思想是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;让所有直线都相交，即便是在“无限远”也交。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;无穷远点（points at infinity）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每一组平行线都增加一个共用的无穷远点。
&lt;ul&gt;
&lt;li&gt;例子：所有竖直线的无穷远点在同一个地方、所有水平线的无穷远点在另一个地方。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;无穷远线（line at infinity）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有的无穷远点组成一条直线 → 无穷远线。&lt;/li&gt;
&lt;li&gt;增加了无穷远线的平面称为射影平面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;曲线的无穷远点&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一条曲线在射影平面中也可以“延伸到无限远”，与无穷远线交于某个无穷远点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;渐近线就是曲线在无穷远处的切线。&lt;/strong&gt;（若该切线是无穷远线，通常定义不存在渐近线）&lt;/p&gt;
&lt;p&gt;以标准抛物线为例：$y = x^2$&lt;/p&gt;
&lt;p&gt;在射影平面中，抛物线是一个二次曲线，它有 &lt;strong&gt;唯一的点在无穷远处&lt;/strong&gt;，对应的是它的开口方向&lt;/p&gt;
&lt;p&gt;事实上，抛物线与无穷远线相切（具体见后文），切线显然是 &lt;strong&gt;无穷远线本身&lt;/strong&gt;。故抛物线没有渐近线。&lt;/p&gt;
&lt;p&gt;其他例子：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;直线&lt;/strong&gt;：与无穷远线相交于一个无穷远点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;双曲线&lt;/strong&gt;：与无穷远线相交于两个无穷远点 → 对应两条渐近线。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;椭圆&lt;/strong&gt;：不存在无穷远点&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;在齐次坐标系中理解：
$$
y = kx^2
$$&lt;/p&gt;
&lt;p&gt;在射影平面中，我们通常引入齐次坐标 $[X:Y:Z]$，其中平面点 $(x, y)$ 对应于：&lt;/p&gt;
&lt;p&gt;$$
(x, y) \mapsto [x:y:1]
$$&lt;/p&gt;
&lt;p&gt;将方程 $y = kx^2$ 转换为齐次形式：&lt;/p&gt;
&lt;p&gt;$$
Y = k X^2/Z \quad \Rightarrow \quad YZ = k X^2
$$&lt;/p&gt;
&lt;p&gt;射影平面中的无穷远直线是：&lt;/p&gt;
&lt;p&gt;$$
Z = 0
$$&lt;/p&gt;
&lt;p&gt;所以，如果我们考虑抛物线 $YZ = k X^2$ 在 $Z=0$ 的交点：&lt;/p&gt;
&lt;p&gt;$$
Y \cdot 0 = k X^2 \implies X = 0
$$&lt;/p&gt;
&lt;p&gt;也就是说，这些抛物线都和无穷远直线 $Z=0$ 上交于点 $[0:1:0]$，且交点处是二重根（说明是相切的），此交点正是“y轴方向的无穷远点”。这可以被理解为抛物线的渐近方向。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$YZ = kX^2$ 的示意图如下：
&lt;img src=&quot;../../assets/images/Fun%20Knowledge-Similar%20Parabolas-2.png&quot; alt=&quot;Fun Knowledge-Similar Parabolas-2&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;相比之下，双曲线与无穷远线相交于两个无穷远点，也就对应两条渐近线，渐近线的夹角由方程系数决定，不同渐近线夹角对应的双曲线并不相似。椭圆则不存在无穷远点。&lt;/p&gt;
&lt;p&gt;尽管上面的分析不能直接说明抛物线具有完全相似的性质，但唯有抛物线在无穷远处的结构完全一致（单一切点，且方向固定），加上离心率恒为 1，没有任何额外的形状参数（自由度比双曲线、椭圆少1个），这就为它们在更严格的变换下仍能互相转换提供了可能性。或者说抛物线完全相似符合我们的射影几何直觉。&lt;/p&gt;
&lt;p&gt;我们还可以问：三维中的抛物面 $z = ax^2 + by^2$ 是否也都相似？答案是否定的。$a/b$ 是一个形状比值，在相似变换下不变。例如，$z = x^2 + y^2$ 和 $z = x^2 + 2y^2$ 不相似，因为前者是旋转对称的，后者不是。这再次说明：抛物线的“全能相似性”是一种低维特例，源于其自由度的缺乏。“所有抛物线相似”并不是某种普遍的几何定律，而是二维的偶然结果。&lt;/p&gt;
&lt;h1&gt;总结&lt;/h1&gt;
&lt;p&gt;其实。。。还是挺无聊的就是了。。。&lt;/p&gt;
&lt;p&gt;射影几何的魅力并不在于这么浅显的直观。极点极线、调和点列......背后有大量有趣的性质可以挖掘。&lt;/p&gt;
</content:encoded></item><item><title>经验-2025年南大智科开放日（夏令营）</title><link>https://blog.stivine.fun/posts/experience-nju-is-open-day-2025/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/experience-nju-is-open-day-2025/</guid><description>真的有区分度嘛...</description><pubDate>Sat, 26 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本文仅为作者个人日记，不构成任何形式的指南或保证。本文信息可能过时或不适用于你的具体情况。因参考本文产生的任何后果，作者概不负责。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;时间：2025年7月24日-7月26日&lt;/p&gt;
&lt;h1&gt;基本信息及个人复习经历&lt;/h1&gt;
&lt;p&gt;共300+人入营。&lt;/p&gt;
&lt;p&gt;邮件发布的安排如下（有伏笔）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第一天 面试（双盲&amp;lt;不得提及本人姓名,或以其他形式暗示本人身份信息&amp;gt;，满分150分，及格线90分。申请人无需准备PPT，可携带个人简历&amp;lt;限1页A4纸，不得提及本人姓名&amp;gt;3份；每位申请人陈述时间2-3分钟）&lt;/p&gt;
&lt;p&gt;第二天上午9:30-11:30 机试（闭卷，满分100分。考察C++程序设计、数据结构与算法等知识，涉及 C++, Python等编程语言）&lt;/p&gt;
&lt;p&gt;第二天下午13:00-15:00 笔试（闭卷，满分100分。考察人工智能、机器学习、离散数学、数据结构与算法）&lt;/p&gt;
&lt;p&gt;第三天 自由交流&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;针对这份安排及2024年情况，重点复习了&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;机试：Leetcode Hot100 刷了两遍，做到能看到题面就回忆起基本做法。&lt;/li&gt;
&lt;li&gt;笔试：离散数学【群论、图论、集合论、关系论、数论、组合数学】、数据结构与算法【主要是图部分】、机器学习【西瓜书前10章及配套代码练习】&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;后来发现自己绝大多数“针对性”的复习一点用都没有（×&lt;/p&gt;
&lt;h1&gt;第一天 面试&lt;/h1&gt;
&lt;p&gt;分为11组，每组3个老师负责面试。平均每个人10分钟。&lt;/p&gt;
&lt;p&gt;准备了非常多的专业课......结果一点都没问，连项目细节也是如此，仅仅问了下“具体做了什么贡献”以及确认了“大概是个什么项目”。纯纯的聊天面。&lt;/p&gt;
&lt;p&gt;英语环节还是在的，每个人被要求用英语介绍的东西不一样，不过我个人没认真准备，也完全没意料到老师会问什么问题，说的非常灾难。&lt;/p&gt;
&lt;p&gt;问了有没有报名其他学校。&lt;/p&gt;
&lt;p&gt;因为自己的简历提到了一些爱好，老师针对爱好提了很多问题。。。。&lt;/p&gt;
&lt;p&gt;最后也是让反向提问，只不过缺乏面试经验的我话都说不利索了。。。。&lt;/p&gt;
&lt;h1&gt;第二天 机试&lt;/h1&gt;
&lt;p&gt;电脑上有vscode、Dev-cpp、Clion等IDE，通过小纸条上的链接下载试题，通过NJU-box上传答案（即无OJ，三次提交机会），这些都和去年一样。&lt;/p&gt;
&lt;p&gt;但今年没有了机器学习代码题，&lt;strong&gt;前两题为强制使用python的算法题，后两题为强制使用C++的算法题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;回收伏笔——原来邮件内容说的一个字都没错！把“等”字都删掉，就变成【考察C++程序设计、数据结构与算法，涉及 C++, Python】，没错，C++和Python都要考！说实话当读到邮件这句话的时候是有想过这种可能的，毕竟去年有强制使用C语言的题目。但没想到真的会这么干。&lt;/p&gt;
&lt;p&gt;以下是具体题目（数据范围都很小）&lt;/p&gt;
&lt;p&gt;Q1：&lt;/p&gt;
&lt;p&gt;输入数据行数n，随后是n行数据，每行第一个数字代表微博发送者的ID，第二个数字代表该发送者@到的人数，剩下的若干数字代表该微博@到的人。需要做的是统计输出被提到（次数 or 人数？此处我和部分同学对题意的理解不同，会影响答案，不知道谁是对的）最多的人，并输出所有@到该人的微博发送者ID。&lt;/p&gt;
&lt;p&gt;自己瞎编的样例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入
3
114514 5 1 2 3 2 4
1919 2 1 2
1 2 3 4
2 2 2 5

输出
2
114514 1919 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;纯考python语法来的。&lt;/p&gt;
&lt;p&gt;Q2：&lt;/p&gt;
&lt;p&gt;输入数据行数n，随后是n行数据，每行是两个数字代表一个区间，选取尽可能多的区间使得这些区间互不相交，输出该最大区间数。&lt;/p&gt;
&lt;p&gt;经典贪心。按照右端点升序排序。【我按左端点排序了我在干什么】&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;正确性证明：假设我们有一个最优解 $O$，而我们的贪心解 $G$ 是每次选右端点最早的区间。
我们来证明 $G$ 不会比 $O$ 差。&lt;/p&gt;
&lt;p&gt;步骤1：对比第一个选择&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$G$ 选择的第一个区间是右端点最小的区间，记为 $g_1$。&lt;/li&gt;
&lt;li&gt;$O$ 选择的第一个区间是 $o_1$。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为 $g_1$ 的右端点不大于 $o_1$ 的右端点，
我们可以把 $O$ 的第一个区间替换成 $g_1$，不会影响剩下的区间安排。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这一步的关键是：
$g_1$ 结束时间更早或一样早，所以不会和 $O$ 的后续区间冲突。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;步骤2：递归继续&lt;/p&gt;
&lt;p&gt;替换完第一个区间后，问题变成“从剩下的区间中选择最多的区间”，依然满足相同的性质。
用同样的方法递归下去，最终可以把最优解 $O$ 转换成我们的贪心解 $G$，并且选择的区间数量不减少。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Q3：&lt;/p&gt;
&lt;p&gt;给定字符串，最多连续出现多少次给定子串？&lt;/p&gt;
&lt;p&gt;自己瞎编的样例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入
abcbcbc bc
输出
3

输入
abcbcbc cb
输出
2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;群里一堆讨论 KMP 的给我整不会了。其实直接遍历就能解决。&lt;/p&gt;
&lt;p&gt;我使用的是 DP 的思路。当然这是不必要的。但我写的很顺。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int maxConsecutiveOccurrencesDP(const string&amp;amp; s, const string&amp;amp; t) {
    if (t.empty() || s.empty() || t.size() &amp;gt; s.size()) {
        return 0;
    }

    int n = s.size();
    int len = t.size();
    vector&amp;lt;int&amp;gt; dp(n + 1, 0);  // dp[i]: 以位置 i 结尾的 t 是连续重复的第几次
    int maxCount = 0;

    for (int i = len; i &amp;lt;= n; ++i) {
        int start = i - len;
        if (s.substr(start, len) == t) {
            dp[i] = dp[start] + 1;
            maxCount = max(maxCount, dp[i]);
        }
    }

    return maxCount;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Q4：&lt;/p&gt;
&lt;p&gt;给定树（root），求最深一层所有节点权值之和。&lt;/p&gt;
&lt;p&gt;过于简单了吧。。。。。。。层序遍历。注意边界情况。&lt;/p&gt;
&lt;h1&gt;第二天 笔试&lt;/h1&gt;
&lt;p&gt;和24年一样的8道题，但这里再次回收伏笔，邮件内容和去年的区别在于增加了“人工智能”一词，删去了概率论。&lt;/p&gt;
&lt;p&gt;内容无比简单。感觉毫无区分度（但我还是错了两道呜呜）&lt;/p&gt;
&lt;p&gt;实际考了&lt;/p&gt;
&lt;p&gt;Q1：选择题，要会k进制比大小、8421BCD码、布尔代数（求给定式的最小项个数）&lt;/p&gt;
&lt;p&gt;Q2：一阶命题逻辑转化，找有无矛盾&lt;/p&gt;
&lt;p&gt;Q3：启发式算法【是的，这是我们《人工智能导论》专业课的内容】&lt;/p&gt;
&lt;p&gt;Q4：（一元二次函数）梯度下降手算&lt;/p&gt;
&lt;p&gt;Q5：（二维）线性回归梯度下降手算&lt;/p&gt;
&lt;p&gt;Q6：（三个二维点）PCA手算&lt;/p&gt;
&lt;p&gt;Q7：Kruskal和Prim算法手算&lt;/p&gt;
&lt;p&gt;Q8：斐波那契递归/迭代的时间复杂度分析&lt;/p&gt;
&lt;p&gt;Q2我脑子短路了最后推出个不存在矛盾（这一题暴亏）&lt;/p&gt;
&lt;p&gt;Q3没复习早就忘了。瞎蒙的。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;启发式算法复习&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;g(x)&lt;/code&gt;：从起点到当前节点的实际代价。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;h(x)&lt;/code&gt;：从当前节点到目标节点的估计代价（启发函数）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A* 算法（A-Star）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;加权方式：&lt;code&gt;f(x) = g(x) + h(x)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;说明：这是最经典的启发式搜索算法，&lt;code&gt;h(x)&lt;/code&gt; 必须是&lt;strong&gt;可接受的&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Greedy Best-First Search（贪心最佳优先搜索）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;加权方式：&lt;code&gt;f(x) = h(x)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;说明：只考虑启发函数，完全忽略实际代价。速度快但不保证最优。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要保证启发式搜索算法能找到最优解（即代价最小的路径），必须满足以下两个条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可接受性（Admissibility）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;启发函数 &lt;code&gt;h(x)&lt;/code&gt; 必须&lt;strong&gt;不能高估&lt;/strong&gt;从当前节点到目标的真实代价。&lt;/li&gt;
&lt;li&gt;也就是说：&lt;code&gt;h(x) ≤ h*(x)&lt;/code&gt;，其中 &lt;code&gt;h*(x)&lt;/code&gt; 是从节点 x 到目标的真实最小代价。&lt;/li&gt;
&lt;li&gt;这个条件保证 A* 不会跳过任何可能更优的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;一致性（Consistency，或单调性）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;一致性是一个更强的条件，它要求启发函数满足三角不等式：&lt;pre&gt;&lt;code&gt;h(x) ≤ c(x, y) + h(y)
&lt;/code&gt;&lt;/pre&gt;
其中 &lt;code&gt;c(x, y)&lt;/code&gt; 是从节点 x 到其邻居 y 的代价。&lt;/li&gt;
&lt;li&gt;一致性不仅保证最优性，还保证 A* 的效率（每个节点最多扩展一次）。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;练习（即本次笔试Q3）&lt;/p&gt;
&lt;p&gt;启发式路径算法是一种启发式搜索算法，它的评估函数是 $ f(n) = (2 - w)g(n) + wh(n) $，假设 $ h $ 是可采纳（admissible）的，$ w $ 取什么值能保证算法的最优性？当 $ w = 0 $, $ w = 1 $, $ w = 2 $ 时，分别对应什么搜索算法？&lt;/p&gt;
&lt;p&gt;$$
f(n) = (2 - w)[g(n) + \frac{w}{2 - w}h(n)] \triangleq (2 - w)[g(n) + h&apos;(n)]
$$&lt;/p&gt;
&lt;p&gt;则必须有 $ h&apos;(n) \leq h(n) $ 也即 $ \frac{w}{2 - w} \leq 1 $ 也即 $ w \leq 1 $ 才能保证算法的最优性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$ w = 0 $ 时对应一致代价算法，&lt;/li&gt;
&lt;li&gt;$ w = 1 $ 对应 A* 算法，&lt;/li&gt;
&lt;li&gt;$ w = 2 $ 对应贪心算法。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1&gt;第三天及总结&lt;/h1&gt;
&lt;p&gt;第二天考完试开了一个学院宣讲会（实际上感觉是工程硕博宣讲会hhh）&lt;/p&gt;
&lt;p&gt;然后就没有统一安排了，可以找意向老师交流，不交流的就可以走人啦~&lt;/p&gt;
&lt;p&gt;总之自己发挥的非常一般，之前的复习完全偏离了方向。除了“好热闹呀我们智科院”也没什么更多的感想了。&lt;/p&gt;
&lt;h1&gt;结果&lt;/h1&gt;
&lt;p&gt;TODO&lt;/p&gt;
</content:encoded></item><item><title>迷思-PRCV考后</title><link>https://blog.stivine.fun/posts/reflection-after-prcv-exam/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/reflection-after-prcv-exam/</guid><description>写不完，根本写不完......</description><pubDate>Sat, 21 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;基本情况&lt;/h1&gt;
&lt;p&gt;六百六十六，两个小时100分的试卷让我写9道大题共51个小问的计算/简答，每个小问还好几个问号，写一堆公式就拿一分，有一种游戏里跟着NPC做一大堆支线任务最后发现给的东西一分不值的救赎感ヾ(๑╹ヮ╹๑)ﾉ”&lt;/p&gt;
&lt;p&gt;个人感觉每道题的质量都还是高的，但问题就是没有难度梯度（前易后难？不同题型？都没有！全是简答和计算）+题量太大。&lt;/p&gt;
&lt;p&gt;其实我觉得只要改一个地方就能让这套卷子成为神卷——比如出10道这种大题，考生需要选择其中8道来做什么的。&lt;/p&gt;
&lt;p&gt;可惜改不得。。。&lt;/p&gt;
&lt;p&gt;具体到考点，其实是和老师最后一节复习课说的高度匹配的，说要考的就考了，没说到的还真就没考。不过由于老师没有说这是“划重点”而只是“复习课”，所以他没提到的我也不敢怠慢，复习的时候都记了。&lt;/p&gt;
&lt;p&gt;比如PCA、kNN、HMM三个问题的具体算法流程、非参数估计，都是一点没考。&lt;/p&gt;
&lt;h1&gt;自己生疏的考点&lt;/h1&gt;
&lt;h2&gt;评估&lt;/h2&gt;
&lt;p&gt;PR曲线和ROC曲线没想到考的这么细。。。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;PR曲线和ROC曲线哪个对类别不平衡更敏感？&amp;lt;br&amp;gt;
PR曲线。&amp;lt;br&amp;gt;
PR曲线与ROC曲线的相同点是都采用了TPR (Recall)，都可以用AUC来衡量分类器的效果。不同点是ROC曲线使用了FPR，而PR曲线使用了Precision，因此PR曲线的两个指标都聚焦于正例。类别不平衡问题中由于主要关心正例，所以在此情况下PR曲线被广泛认为优于ROC曲线。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AUC-PR和AUC-ROC的作用分别是？&amp;lt;br&amp;gt;
AUC-ROC：更适合用于评估在类别分布相对均衡的数据集上的模型性能。&amp;lt;br&amp;gt;
AUC-PR：在处理类别极度不平衡的数据集时，更能准确地反映模型的表现，因为它重点关注了正样本的预测质量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于阳性漏检情况，应该更关注PR曲线还是ROC曲线？&amp;lt;br&amp;gt;
即假阴性FN的情况，通常更应该关注PR曲线。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;相机模型&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;张正友相机标定法？&amp;lt;br&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;准备标定板：一个具有已知尺寸的棋盘格图案&lt;/li&gt;
&lt;li&gt;采集图像：从不同角度和位置拍摄多张包含标定板的图像。确保在每张图像中，标定板都是完全可见的，并且覆盖整个视野范围尽可能地展示不同的视角变化。&lt;/li&gt;
&lt;li&gt;检测特征点：对于每一张图像，自动检测出棋盘格上的角点（如Harris）&lt;/li&gt;
&lt;li&gt;计算单应性矩阵（如DLT）&lt;/li&gt;
&lt;li&gt;优化求解相机内外参数。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;度量&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;三角不等式证明？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;度量学习中证明合法度量&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;度量学习中物理意义&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;L1度量相比于L2度量对噪声更鲁棒的原因&amp;lt;br&amp;gt;
L1损失线性增长，L2损失二次增长。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;图像处理&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;时域卷积和频域卷积各自的场景、时间复杂度？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;仿射变换保留的三个几何性质？
共线性：原始图像中的共线点在变换后的图像中依然保持共线
平行性：直线间的平行关系不变
比例分割性质：线段上点的位置比例保持不变。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;单应性变换8个自由度的数学意义？
单应性矩阵有9个元素，但由于齐次坐标&lt;em&gt;的缩放不变性&lt;/em&gt;，这些元素可以按比例缩放，因此实际只有8个自由度。具体求解过程如下：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;$$
\begin{pmatrix}
x&apos;_i \
y&apos;_i \
1
\end{pmatrix}&lt;/h1&gt;
&lt;h1&gt;\begin{bmatrix}
h_{11} &amp;amp; h_{12} &amp;amp; h_{13} \
h_{21} &amp;amp; h_{22} &amp;amp; h_{23} \
h_{31} &amp;amp; h_{32} &amp;amp; h_{33}
\end{bmatrix}
\begin{pmatrix}
x_i \
y_i \
1
\end{pmatrix}&lt;/h1&gt;
&lt;p&gt;\begin{pmatrix}
h_{11} x_i + h_{12} y_i + h_{13} \
h_{21} x_i + h_{22} y_i + h_{23} \
h_{31} x_i + h_{32} y_i + h_{33}
\end{pmatrix}
$$&lt;/p&gt;
&lt;p&gt;$$
x&apos;&lt;em&gt;i = \frac{h&lt;/em&gt;{11} x_i + h_{12} y_i + h_{13}}{h_{31} x_i + h_{32} y_i + h_{33}}
$$&lt;/p&gt;
&lt;p&gt;$$
y&apos;&lt;em&gt;i = \frac{h&lt;/em&gt;{21} x_i + h_{22} y_i + h_{23}}{h_{31} x_i + h_{32} y_i + h_{33}}
$$&lt;/p&gt;
&lt;p&gt;单应性矩阵 $ H $ 与 $ aH $ 其实完全一样（其中 $ a \neq 0 $）即点 $ (x_i, y_i) $ 无论经过 $ H $ 还是 $ aH $ 映射，变化后都是 $ (x&apos;_i, y&apos;_i) $&lt;/p&gt;
&lt;p&gt;如果使 $ a = \frac{1}{h_{33}} $，那么有:&lt;/p&gt;
&lt;p&gt;$$
H&apos; = aH =
\begin{bmatrix}
\frac{h_{11}}{h_{33}} &amp;amp; \frac{h_{12}}{h_{33}} &amp;amp; \frac{h_{13}}{h_{33}} \
\frac{h_{21}}{h_{33}} &amp;amp; \frac{h_{22}}{h_{33}} &amp;amp; \frac{h_{23}}{h_{33}} \
\frac{h_{31}}{h_{33}} &amp;amp; \frac{h_{32}}{h_{33}} &amp;amp; 1
\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;$$
H&apos; =
\begin{bmatrix}
h&apos;&lt;em&gt;{11} &amp;amp; h&apos;&lt;/em&gt;{12} &amp;amp; h&apos;&lt;em&gt;{13} \
h&apos;&lt;/em&gt;{21} &amp;amp; h&apos;&lt;em&gt;{22} &amp;amp; h&apos;&lt;/em&gt;{23} \
h&apos;&lt;em&gt;{31} &amp;amp; h&apos;&lt;/em&gt;{32} &amp;amp; 1
\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;所以单应性矩阵 $ H $ 虽然有9个未知数，但只有8个自由度。&lt;/p&gt;
&lt;h1&gt;自己预判到并回答较为顺利的考点&lt;/h1&gt;
&lt;p&gt;FLD相关过程&lt;/p&gt;
&lt;p&gt;DLT过程&lt;/p&gt;
&lt;p&gt;RANSAC流程&lt;/p&gt;
&lt;p&gt;贝叶斯决策&lt;/p&gt;
&lt;p&gt;Harris&lt;/p&gt;
&lt;p&gt;2D transformations 变换矩阵/自由度&lt;/p&gt;
&lt;p&gt;世界坐标系相机坐标系图像坐标系等相机模型相关计算&lt;/p&gt;
</content:encoded></item><item><title>迷思-NLP考后</title><link>https://blog.stivine.fun/posts/reflection-after-nlp-exam/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/reflection-after-nlp-exam/</guid><description>最羞愧的一次......</description><pubDate>Tue, 17 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;只是一次考试，有点闹麻了，不过这次考得差实在是有些活该。这一学年的学习都没什么劲头，因此这篇迷思主要是对自己最近（真的是最近吗）学习状态的反思吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;阿巴阿巴&lt;/h1&gt;
&lt;p&gt;没有比这更令人难受的考试了————明明只考察了若干小小知识点的记忆，明明有充足的复习时间，明明自己已经看过了那个知识点，但自己没有真正地理解它，导致最后没有写下那个曾经被我走马观花般看过但忘记了的答案。&lt;/p&gt;
&lt;p&gt;考试仅仅进行到一半，我就知道自己严重误判了自己的备考完成度。&lt;/p&gt;
&lt;p&gt;考后大家都在吐槽，说老师考的太偏了。这句话没有错，但却让我有些羞愧。因为这句话太常出现，只是在此之前，它是我拉开和其他人差距的机会，现在却只能成为我自我安慰的理由。&lt;/p&gt;
&lt;p&gt;让我捶胸顿足的三个知识点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BLEU公式&lt;/li&gt;
&lt;li&gt;多项式朴素贝叶斯用于文本分类&lt;/li&gt;
&lt;li&gt;T5模型的预训练任务之prefix&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第一个和第三个的确考的很偏，但我在复习的过程中都注意到过，&lt;strong&gt;甚至在我的复习note中都有&lt;/strong&gt;，但我潜意识中认为它们不可能考到。&lt;/p&gt;
&lt;p&gt;不对，不对，我知道的。根本不是我对考点的预判问题。&lt;/p&gt;
&lt;p&gt;我只是犯了我最瞧不起的错误，所以不敢承认罢了。&lt;/p&gt;
&lt;p&gt;那就是不求甚解。&lt;/p&gt;
&lt;p&gt;明明BLEU的计算是作业的一部分，明明T5模型是Transformer架构的重要应用。&lt;/p&gt;
&lt;p&gt;最可恨的是多项式朴素贝叶斯真的是必考级别的，我却只因为觉得已经会做PPT上的例题，无视了那几个重要的公式推导过程。&lt;/p&gt;
&lt;p&gt;太让人羞愧了。&lt;/p&gt;
&lt;p&gt;课内这点东西都记不住，我如何配得上成为这个领域的研究者呢。&lt;/p&gt;
&lt;p&gt;以下是这几个知识点的真正学习总结。&lt;/p&gt;
&lt;h1&gt;知识总结&lt;/h1&gt;
&lt;h2&gt;BLEU&lt;/h2&gt;
&lt;p&gt;BLEU&lt;/p&gt;
&lt;h2&gt;多项式朴素贝叶斯用于文本分类&lt;/h2&gt;
&lt;p&gt;// TODO&lt;/p&gt;
&lt;h2&gt;T5模型的预训练任务&lt;/h2&gt;
&lt;p&gt;// TODO&lt;/p&gt;
&lt;p&gt;本次考试涉及的其他知识点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CNN用于文本分类&lt;/li&gt;
&lt;li&gt;RNN训练过程&lt;/li&gt;
&lt;li&gt;模型设计：给定上联对出下联&lt;/li&gt;
&lt;li&gt;NLP定义、相对人工语言处理自然语言的难度&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;剩下的想不起来啦。&lt;/p&gt;
</content:encoded></item><item><title>技术与理论-BLEU评估</title><link>https://blog.stivine.fun/posts/tech-and-theory-bleu-evaluation/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/tech-and-theory-bleu-evaluation/</guid><description>初学有诸多疑惑......</description><pubDate>Tue, 17 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;BLEU（Bilingual Evaluation Understudy）&lt;/h2&gt;
&lt;p&gt;初学有诸多疑惑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果是多个参考译文，到底该怎么算？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同一个gram出现多次，怎么统计？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下面的代码到底算了什么？&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from nltk.translate.bleu_score import corpus_bleu

def evaluate_bleu(model, test_loader):
    references = []
    hypotheses = []
    for src, tgt in tqdm(test_loader, desc=&quot;Evaluating BLEU&quot;):
        pred_indices = translate(model, src)
        tgt_indices = tgt[0].tolist()
        tgt_splits = [[t for t in tgt_indices if t not in [PAD_IDX, BOS_IDX, EOS_IDX]]]
        pred_splits = [t for t in pred_indices if t not in [PAD_IDX, BOS_IDX, EOS_IDX]]
        references.append(tgt_splits)
        hypotheses.append(pred_splits)
    score = corpus_bleu(references, hypotheses)
    print(f&quot;BLEU@4 Score: {score * 100:.2f}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本文将进行最准确的BLEU解读！&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;$\text{BLEU} = BP \times \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)$&lt;/p&gt;
&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;N 是最大语法的阶数，实际取4。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$w_n = \frac{1}{N}$，表示每个阶数的权重 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$p_n$ 表示出现在参考译文中的 $n$ 元词语接续组占假设译文中 $n$ 元词语接续组总数的比例。&lt;/p&gt;
&lt;p&gt;（假设译文：即模型翻译结果。参考译文：即答案译文。）&lt;/p&gt;
&lt;p&gt;【关于比例计算这里，一个疑惑在于重复的 $n$ 元词语接续组如何统计，请看后续分析】&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;长度过短句子的惩罚因子 (Brevity Penalty, BP)&lt;/p&gt;
&lt;p&gt;$BP = \begin{cases}
1 &amp;amp; \text{if } c &amp;gt; r \
e^{(1 - r/c)} &amp;amp; \text{if } c \leq r
\end{cases}$&lt;/p&gt;
&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;c 为假设译文中单词的个数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;r 为参考译文单词个数中，与 c 最接近的那个数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一定要注意，多个参考译文，不是取最好的情况，是取最接近的数。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; references = [[&apos;a&apos;] * 13, [&apos;a&apos;] * 2]
&amp;gt;&amp;gt;&amp;gt; hypothesis = [&apos;a&apos;] * 12
&amp;gt;&amp;gt;&amp;gt; hyp_len = len(hypothesis)
&amp;gt;&amp;gt;&amp;gt; closest_ref_len =  closest_ref_length(references, hyp_len)
&amp;gt;&amp;gt;&amp;gt; brevity_penalty(closest_ref_len, hyp_len) 
0.9200...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但如果一样接近，就取较好情况不必惩罚。或者说，取较短的参考译文长度。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; references = [[&apos;a&apos;] * 13, [&apos;a&apos;] * 11]
&amp;gt;&amp;gt;&amp;gt; hypothesis = [&apos;a&apos;] * 12
&amp;gt;&amp;gt;&amp;gt; hyp_len = len(hypothesis)
&amp;gt;&amp;gt;&amp;gt; closest_ref_len =  closest_ref_length(references, hyp_len)
&amp;gt;&amp;gt;&amp;gt; bp1 = brevity_penalty(closest_ref_len, hyp_len)
&amp;gt;&amp;gt;&amp;gt; hyp_len = len(hypothesis)
&amp;gt;&amp;gt;&amp;gt; closest_ref_len =  closest_ref_length(reversed(references), hyp_len)
&amp;gt;&amp;gt;&amp;gt; bp2 = brevity_penalty(closest_ref_len, hyp_len)
&amp;gt;&amp;gt;&amp;gt; bp1 == bp2 == 1
True
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来让我们手算一个句子级别的BLEU（单个源文本）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; hypothesis = [&apos;the&apos;, &apos;cat&apos;, &apos;is&apos;, &apos;on&apos;, &apos;the&apos;, &apos;mat&apos;]

&amp;gt;&amp;gt;&amp;gt; reference1 = [&apos;the&apos;, &apos;cat&apos;, &apos;is&apos;, &apos;on&apos;, &apos;mat&apos;]
&amp;gt;&amp;gt;&amp;gt; reference2 = [&apos;there&apos;, &apos;is&apos;, &apos;a&apos;, &apos;cat&apos;, &apos;on&apos;, &apos;the&apos;, &apos;mat&apos;]
&amp;gt;&amp;gt;&amp;gt; reference3 = [&apos;a&apos;, &apos;cat&apos;, &apos;being&apos;, &apos;on&apos;, &apos;the&apos;, &apos;mat&apos;]
&amp;gt;&amp;gt;&amp;gt; references = [reference1, reference2, reference3]

&amp;gt;&amp;gt;&amp;gt; bleu_score = sentence_bleu(references, hypothesis)
0.6756000774035172
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;p1 = 5/6，p2 = 5/5，p3 = 3/4，p4 = 1/3，BP = 1&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里解决了我们的疑惑。对于unigram，the在hypothesis出现两次，那么分母就要算两次；the在其他reference中最多出现1次，故分子算1次。如果某个reference的the也出现了两次，那么分子也要算两次。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;代入$\text{BLEU} = BP \times \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)$&lt;/p&gt;
&lt;p&gt;即得BLEU = 0.6756000774035172，和程序运行结果一致。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;语料库级别（多个源文本）的bleu计算—源码分析&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;关键词：微平均&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nltk.translate.bleu_score.corpus_bleu(list_of_references, hypotheses, weights=(0.25, 0.25, 0.25, 0.25), smoothing_function=None, auto_reweigh=False)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;list_of_references (list(list(list(str)))) – a corpus of lists of reference sentences&lt;/li&gt;
&lt;li&gt;hypotheses (list(list(str))) – a list of hypothesis sentences&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;list_of_references的第i个元素是第i个源文本的&lt;strong&gt;若干&lt;/strong&gt;参考译文，hypotheses的第i个元素是第i个源文本的假设译文。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;corpus_bleu()&lt;/code&gt;用的是微平均（micro-average）的思想。它与多个 &lt;code&gt;sentence_bleu()&lt;/code&gt; 的平均值是不同的。具体表现在&lt;code&gt;corpus_bleu()&lt;/code&gt;的源码中，$p_i$的计算为&lt;code&gt;p_numerators[i]&lt;/code&gt;和&lt;code&gt;p_denominators[i]&lt;/code&gt;之比。（对于i-gram）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;BP的值也是各个句子级别BP的微平均。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;p_numerators[i]&lt;/code&gt;和&lt;code&gt;p_denominators[i]&lt;/code&gt;都是累加器，对每个源文本的若干参考译文和一个假设译文，通过&lt;code&gt;modified_precision(references, hypothesis, i)&lt;/code&gt;计算一个准确率，它返回的是一个分数对象，包括分子和分母两部分。分子加到&lt;code&gt;p_numerators[i]&lt;/code&gt;上，分母加到&lt;code&gt;p_denominators[i]&lt;/code&gt;上。&lt;/p&gt;
&lt;p&gt;那么&lt;code&gt;modified_precision(references, hypothesis, i)&lt;/code&gt;具体是怎么计算的呢？&lt;/p&gt;
&lt;p&gt;首先&lt;code&gt;counts = Counter(ngrams(hypothesis, i))&lt;/code&gt;统计假设译文中不同i-gram的数量，counts内部计数之和即为返回的分母部分。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 此处释疑：重复的i-gram如何纳入统计？以i=2为例子。

&amp;gt;&amp;gt;&amp;gt; from nltk.util import ngrams
&amp;gt;&amp;gt;&amp;gt; x = ngrams([&apos;I&apos;, &apos;like&apos;, &apos;I&apos;, &apos;like&apos;], 2)
&amp;gt;&amp;gt;&amp;gt; Counter(x)
Counter({(&apos;I&apos;, &apos;like&apos;): 2, (&apos;like&apos;, &apos;I&apos;): 1})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后统计若干参考译文中不同i-gram的数量，得到的Counter逐key取最大值。得到max_counts。&lt;/p&gt;
&lt;p&gt;最后和counts取交集&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;clipped_counts = {
        ngram: min(count, max_counts[ngram]) for ngram, count in counts.items()
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到交集内所有计数之和即返回的分子部分。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Smooth&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其他：&lt;/p&gt;
&lt;p&gt;BLEU 分值范围是 0 ~ 1，分值越高表示译文质量越好。&lt;/p&gt;
&lt;p&gt;BP的范围是 0 ~ 1。&lt;/p&gt;
&lt;p&gt;本文参考自nltk官方文档：&lt;/p&gt;
&lt;p&gt;https://www.nltk.org/api/nltk.translate.bleu_score.html#nltk.translate.bleu_score.corpus_bleu&lt;/p&gt;
</content:encoded></item><item><title>实践-OFA相关研究</title><link>https://blog.stivine.fun/posts/practice-ofa-related-research/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/practice-ofa-related-research/</guid><description>多模态大模型高效微调</description><pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本篇博客是探索过程中的随手记录，包括大量无用信息、AI生成信息。参考价值较低。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h1&gt;OFA&lt;/h1&gt;
&lt;p&gt;OFA: Unifying Architectures, Tasks, and Modalities Through a Simple Sequence-to-Sequence Learning Framework（ICML 2022，阿里巴巴）&lt;/p&gt;
&lt;p&gt;预训练模型。&lt;/p&gt;
&lt;p&gt;仓库链接: &lt;a href=&quot;https://link.zhihu.com/?target=https%3A//github.com/OFA-Sys/OFA&quot;&gt;https://github.com/OFA-Sys/OFA&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;作者提供了很多下游任务的示例&lt;/p&gt;
&lt;p&gt;Image Captioning为例子&lt;/p&gt;
&lt;p&gt;▼ 1. 准备数据集和检查点
下载数据（参见 datasets.md）和模型（参见 checkpoints.md），并将它们放在正确的目录中。数据集压缩文件 caption_data.zip 包含 &lt;code&gt;caption_stage1_train.tsv&lt;/code&gt;、&lt;code&gt;caption_stage2_train.tsv&lt;/code&gt;、&lt;code&gt;caption_val.tsv&lt;/code&gt; 和 &lt;code&gt;caption_test.tsv&lt;/code&gt;。每个图像在 &lt;code&gt;caption_stage1_train.tsv &lt;/code&gt;中只对应一个标题，并在其他 TSV 文件中对应多个标题（每张图像大约有5个标题）。&lt;/p&gt;
&lt;p&gt;数据集的每一行代表一个标题样本，格式如下。唯一标识符 uniq-id、图像标识符 image-id、标题 caption、预测的对象标签（来自 VinVL，未使用）、以及 base64 编码的图像字符串由制表符分隔。&lt;/p&gt;
&lt;p&gt;▼ 2. Finetuning
遵循先前的标准做法，我们将图像标题生成的微调过程分为两个阶段。在第一阶段，我们使用交叉熵损失在4个具有32GB内存的 NVIDIA-V100 GPU 上对 OFA 进行微调（预计在此阶段验证集上的 CIDEr 分数约为139.5）。在第二阶段，我们选择第一阶段的最佳检查点，并在8个 NVIDIA-V100 GPU 上使用 CIDEr 优化进行训练。请注意，CIDEr 优化非常不稳定，需要仔细调整超参数。如果你在第二阶段的微调过程中遇到训练错误，可以增加批量大小或降低学习率。如果这些方法都不奏效，你可以直接设置 --freeze-resnet 来冻结批处理归一化的内部状态。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd run_scripts/caption
nohup sh train_caption_stage1.sh &amp;gt; train_stage1.out &amp;amp; # 第一阶段，使用交叉熵损失进行训练
nohup sh train_caption_stage2.sh &amp;gt; train_stage2.out &amp;amp; # 第二阶段，加载第一阶段的最佳检查点并进行训练
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;▼ 3. 推理
运行以下命令以获取结果并评估你的模型。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd run_scripts/caption ; sh evaluate_caption.sh # 推理与评估
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;OFA的技术细节&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;**I/O统一，**要解决的问题就是图像和文本在transformer模型中如何输入和输出。输入方面，文本的输入则是传统的BPE encoding将文本输入转化为embedding序列，图像的输入则相对复杂。首先图像需要同样转化成embedding序列的形式，方法的核心则参照ViT的分patch实现。参考了CoAtNet和SimVLM的实现，OFA将图像接入ResNet后转化为patch embedding后再与文本embedding进行拼接，而为了实现更好的效果OFA将ResNet部分加入到模型的训练中。但针对图像的输出，图像依然需要离散化的表达，因此OFA的实现和此前DALL-E、Beit等工作一致，将图像使用vector quantization模型转化成code作为模型的target并将code加入到词表中。此外，由于模型的预训练任务包括grounded captioning，visual grouding以及object detection，OFA还需要处理坐标信息的输入和输出。具体而言，参照Pix2Seq的实现，OFA将连续的坐标信息转化成离散化的表示，并将其加入到词典中，从而将文本、图像、坐标都融入到一个统一的词表中。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TODO：ViT、CoAtNet和SimVLM、DALL-E和Beit、Pix2Seq&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;OFA-Chinese&lt;/h1&gt;
&lt;p&gt;在OFA官方项目中，同时实现了fairseq和transformers两套框架的模型结构，并且分别开源了中文和英文的模型权重。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由于笔者对transformers框架更熟悉，所以希望基于transformers框架，使用域内中文数据对OFA模型进行finetune，但OFA的中文预训练权重只有fairseq版本，没有transformers版本。希望将fairseq版本的OFA预训练权重转换为transformers版本，从而便于下游任务进行finetune。&lt;/li&gt;
&lt;li&gt;官方代码库中，由于需要兼容各种实验配置，代码也比较复杂冗余。笔者希望能够将核心逻辑剥离出来，简化使用方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;笔者的主要工作如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阅读分析OFA官方代码库，剥离出核心逻辑，包括训练逻辑、model、tokenizer等，以transformers框架进行下游任务的训练和推理，简化使用方式。&lt;/li&gt;
&lt;li&gt;将官方的fairseq版本的中文预训练权重，转化为transformers版本，用于下游任务进行finetune。&lt;/li&gt;
&lt;li&gt;基于本项目，使用中文多模态MUGE数据集中的Image Caption数据集，以LiT-tuning的方式对模型进行finetune，验证了本项目的有效性。&lt;/li&gt;
&lt;li&gt;开源五个transformers版本的中文OFA模型权重，包括由官方权重转化而来的四个权重，以及笔者使用MUGE数据集finetune得到的权重。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;MixLoRA&lt;/h1&gt;
&lt;p&gt;Multimodal Instruction Tuning with Conditional Mixture of LoRA（ACL 2024）&lt;/p&gt;
&lt;p&gt;中文博客&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/684800191&quot;&gt;（2024，MixLoRA，任务干扰，独立因子选择，条件因子选择）使用 LoRA 的条件混合进行多模态指令调优 - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多模态指令调优通过使用多样化的、基于指令的多模态任务对预训练模型进行微调，展示了其在促进未见过的多模态问题零样本泛化方面的有效性。&lt;/p&gt;
&lt;p&gt;在多模态指令调优中，像LoRA这样的传统PEFT方法的有效性由于依赖于调整有限的共享参数部分以同时适应多样化任务而减弱，导致任务干扰。这是一个在多任务学习中被广泛研究的问题（Yu等人，2020；Liu等人，2021；Navon等人，2022），但在参数高效多模态指令调优的背景下尚未得到充分研究。&lt;/p&gt;
&lt;p&gt;梯度方向的角度是借鉴了（Liu等人，2021）&lt;/p&gt;
&lt;p&gt;与传统LoRA在整个任务和实例中使用共享的低秩适应矩阵A和B不同，MixLoRA通过从两个集合中选择其分解因子，动态地构建针对每个输入实例定制的低秩适应矩阵A和B。MixLoRA引入了一种动态因子选择机制，包括两个独立因子选择（IFS）路由器和一个条件因子选择（CFS）路由器。这两个IFS路由器独立选择适当的因子，以动态构建针对每个输入定制的LoRA A和B矩阵。CFS路由器进一步基于为LoRA A选择的因子来细化LoRA B的选择，确保LoRA A和B的因子选择不仅针对输入进行调整，而且一致对齐。&lt;/p&gt;
&lt;p&gt;为了验证MixLoRA的有效性，在MME（Fu等人，2023）上进行了广泛的实验，这是一个全面的多模态评估基准，并在七个额外的专注于各种能力的多模态评估数据集上进行了实验。&lt;/p&gt;
&lt;p&gt;Vision-Flan（Xu等人，2023a）作为一个全面的人工标注视觉指令调优数据集脱颖而出，涵盖了广泛的187个任务，使其成为我们训练的理想选择。&lt;/p&gt;
&lt;h2&gt;核心技术细节&lt;/h2&gt;
&lt;p&gt;受专家混合（Mixture-of-Experts）概念的启发（Shazeer等人，2016年），我们提出了条件LoRA混合（MixLoRA），它利用低秩分解因子作为动态选择的专家来为特定输入实例构建定制的分解矩阵A和B。MixLoRA促进了针对不同输入实例的动态处理路径，从而增强了处理多样和复杂的多模态指令任务的有效性。&lt;/p&gt;
&lt;p&gt;条件LoRA混合的核心在于通过张量分解表示来自方程1的权重调整矩阵ΔW：&lt;/p&gt;
&lt;p&gt;$ \Delta W = BA = \sum_{i=1}^{r} b_i \otimes a_i, $&lt;/p&gt;
&lt;p&gt;其中$\otimes$表示外积，${a_i, b_i}&lt;em&gt;{i=1}^{r}，a_i \in \mathbb{R}^{d&lt;/em&gt;{in} \times 1}，b_i \in \mathbb{R}^{d_{out} \times 1}$是$\Delta W \in \mathbb{R}^{d_{out} \times d_{in}}$的秩r分解因子。&lt;/p&gt;
&lt;h3&gt;4.1 动态因子选择&lt;/h3&gt;
&lt;p&gt;动态因子选择模块使用两个主要组件来动态构建LoRA矩阵A和B。首先，两个独立因子选择（IFS）路由器（第4.1.1节），独立地选择$r$个相关因子以形成适应矩阵LoRA A和B，确保精确、实例特定的适应。其次，条件因子选择（CFS）路由器（第4.1.2节）通过根据为LoRA A选择的因素进一步细化LoRA B的选择，促进连贯的适应过程。&lt;/p&gt;
&lt;h4&gt;4.1.1 独立因子选择&lt;/h4&gt;
&lt;p&gt;MixLoRA采用两个独立因子选择（IFS）路由器，$R_{\text{IFS}}^{A}(\cdot)和R_{\text{IFS}}^{B}(\cdot)$，分别选择r个相关因子用于LoRA A和B，如图3所示。IFS路由器采用基于实例的路由方法，这种方法在选择r个分解因子时比传统的输入令牌基路由更节省内存。路由策略可以表示为：&lt;/p&gt;
&lt;p&gt;$ R_{\text{IFS}}^{A}(h) = \text{Avg}(h), $&lt;/p&gt;
&lt;p&gt;其中$\text{Avg}(\cdot)$对隐藏状态$h \in \mathbb{R}^{\text{seq} \times d_{\text{in}}}$的序列维度进行平均，结果是$R_{\text{IFS}}^{A}(h) \in \mathbb{R}^{d_{\text{in}}}$。&lt;/p&gt;
&lt;p&gt;因子选择过程涉及计算向量$g_A \in \mathbb{R}^E$和$g_B \in \mathbb{R}^E$，以分别从集合${a_e}&lt;em&gt;{e=1}^{E}$和${b_e}&lt;/em&gt;{e=1}^{E}$中选择特定的分解因子子集。为了计算$g_A$，输入$R_{IFS}^{A}(h) \in \mathbb{R}^{d_{in}}$通过一个权重为$W_A \in \mathbb{R}^{E \times d_{in}}$的全连接层处理，随后进行softmax归一化和top-k选择：&lt;/p&gt;
&lt;p&gt;$ g_A = \text{top}&lt;em&gt;r(\text{softmax}(W_A \cdot R&lt;/em&gt;{IFS}^{A}(h))). $&lt;/p&gt;
&lt;p&gt;这一过程确保了LoRA A的r个因子的选择，其中$g_A[i] = 1$表示因子i被选中。同样的过程应用于确定LoRA B的$g_B$。&lt;/p&gt;
&lt;h4&gt;4.1.2 条件因子选择&lt;/h4&gt;
&lt;p&gt;虽然迄今为止LoRA A和B的因子是独立选择的，我们假设在LoRA A和B的选择之间存在相互依赖关系，这可以被利用来提高模型的整体适应性和性能。为了利用这种关系，我们提出了一种条件因子选择（CFS）策略，在该策略中，LoRA B的投影上升权重的因子选择也受到LoRA A的投影下降权重所选因子的影响。&lt;/p&gt;
&lt;p&gt;与IFS路由器一样，LoRA A由选定的分解因子组装而成，记为$A = [a_1, \cdots, a_r]^T$，其中$A \in \mathbb{R}^{r \times d_{in}}$。接下来，CFS路由器使用一个权重张量$W_{AB} \in \mathbb{R}^{r \times d_{in} \times E}$，将每个因子$A[i] \in \mathbb{R}^{1 \times d_{in}}$映射到专家维度E。对于每个因子$A[i]$的映射过程，通过softmax归一化并跨r个因子聚合，给出如下：&lt;/p&gt;
&lt;p&gt;$ R_{CFS}^{B}(A) = \sum_{i=1}^{r} \text{softmax}(A[i] \cdot W_{AB}[i]), $&lt;/p&gt;
&lt;p&gt;其中$W_{AB}[i] \in \mathbb{R}^{d_{in} \times E}$是与$A[i]$相关的映射矩阵。&lt;/p&gt;
&lt;p&gt;LoRA B的因子选择整合了来自IFS $R_{IFS}^{B}(\cdot)$和CFS $R_{CFS}^{B}(\cdot)$路由器的输出，通过后期融合策略形成选择向量$g_B$，如下所示：&lt;/p&gt;
&lt;p&gt;$ p_{IFS}^{B} = \text{softmax}(W_{IFS}^{B} \cdot R_{IFS}^{B}(h)) $
$ p_{CFS}^{B} = \text{softmax}(R_{CFS}^{B}(A)) $
$ g_B = \text{top}&lt;em&gt;r(p&lt;/em&gt;{IFS}^{B} + p_{CFS}^{B}), $&lt;/p&gt;
&lt;p&gt;其中，对于LoRA B的$R_{IFS}^{B}(h) \in \mathbb{R}^{d_{out}}$计算方式与$R_{IFS}^{A}(h)$相似。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;概率分布&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;概率分布$p_{IFS}^{B} \in \mathbb{R}^E$和$p_{CFS}^{B} \in \mathbb{R}^E$分别反映了独立路由器和条件路由器的选择。最终选择向量$g_B \in \mathbb{R}^E$通过结合这些分布并识别出前r个因子来确定。这种CFS策略使LoRA B的选择能够受到为LoRA A所选因子的影响，促进更连贯的选择过程。&lt;/p&gt;
&lt;h4&gt;4.1.3 动态适应矩阵的重建&lt;/h4&gt;
&lt;p&gt;最后，MixLoRA通过利用因子选择向量$g_A$和$g_B$，收集选定的因子${a_k, b_k}&lt;em&gt;{k \in K}, |K| = r$，来组装LoRA A和B的最终矩阵。因此，在每次前向传递中，权重调整矩阵$\Delta W \in \mathbb{R}^{d&lt;/em&gt;{out} \times d_{in}}$基于这些选定的因子动态计算，公式如下：&lt;/p&gt;
&lt;p&gt;$ \Delta W = BA = [b_1, \cdots, b_r][a_1, \cdots, a_r]^T. $&lt;/p&gt;
&lt;h3&gt;5 实验方法&lt;/h3&gt;
&lt;h4&gt;5.1 数据集&lt;/h4&gt;
&lt;h5&gt;训练数据集&lt;/h5&gt;
&lt;p&gt;为了验证MixLoRA的有效性，我们在Vision-Flan（Xu等人，2023a）上进行指令调优，这是一个包含187种不同任务的人工标注多模态指令调优数据集。其在视觉指令任务中的多样性使其成为研究任务干扰的理想选择。为了最小化计算成本，我们使用了一个每任务最多1,000个实例的缩小版本，总计182,167个实例。&lt;/p&gt;
&lt;h5&gt;评估数据集&lt;/h5&gt;
&lt;p&gt;我们在MME（Fu等人，2023）上评估我们的方法，这是一个全面的多模态评估基准，测量&lt;strong&gt;14&lt;/strong&gt;个子任务上的感知和认知能力，以评估MixLoRA的整体能力。除了MME，我们还使用&lt;strong&gt;7&lt;/strong&gt;个多模态数据集进一步探测模型的各种能力。对于光学字符识别，我们使用TextVQA（Singh等人，2019），而对于推理，我们采用视觉空间推理（VSR）数据集（Liu等人，2022）。感知能力在CIFAR-10/100（Krizhevsky等人，2009）和MNIST（LeCun，1998）上进行测试，遵循Zhai等人（2023）的指导。SNLI-VE数据集（Xie等人，2019）评估视觉蕴含能力，而POPE（Li等人，2023c）则检查对象幻觉的趋势。&lt;/p&gt;
&lt;h4&gt;5.2 评估指标&lt;/h4&gt;
&lt;p&gt;对于 MME 得分，我们使用官方评估工具²，聚合感知和认知指标。对于其他多模态数据集，我们利用 Vicuna 1.5 13B（Chiang 等人，2023），这是一种最先进的开源语言模型（LLM），以评估预测与真实目标输出相比的准确性。更多细节见附录 C。&lt;/p&gt;
&lt;h3&gt;6 结果与讨论&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;与 LoRA 的比较&lt;/strong&gt;&lt;br /&gt;
我们首先详细比较了 MixLoRA 和传统的 LoRA，在 MME 和另外7个多模态任务上的表现，如表1所示。我们观察到，当两种模型在 MME 和附加多模态任务上具有相同的秩时，MixLoRA 一致地超越了 LoRA，并且即使与具有更高秩数的 LoRA 相比，也表现出更优的性能。例如，MixLoRA（秩 r=2 和因子 E=16）在 MME 上比 LoRA（秩 r=32）高出1.7%，在其他多模态评估中平均高出1.6%。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;增加秩数&lt;/strong&gt;&lt;br /&gt;
我们在保持因子数量不变的情况下，研究了增加秩数的影响。如表1所示，当因子数量固定时，随着秩数从2增加到4，MixLoRA 表现出了显著的性能提升。具体来说，将秩 r 从2增加到4，在因子 E=16 时，导致 MME 性能提升了1.8%，MMAvg 提升了3.1%；而在因子 E=32 时，MME 提升了3.5%，MMAvg 提升了0.6%。然而，进一步将秩增加到8显示出性能增益的边际效应递减。我们假设这种下降可能是由于构建适应矩阵的组合池扩展所导致的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;增加因子数量&lt;/strong&gt;&lt;br /&gt;
在秩数保持不变的情况下，我们的发现揭示了 MixLoRA 性能改进的一般趋势，如表1所示。这种改进可归因于模型提供更丰富因子集合的能力，使模型能够针对特定的多模态任务进行定制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;路由策略的效果&lt;/strong&gt;&lt;br /&gt;
在这个实验中，我们考察了不同路由策略对IFS 路由器的影响。特别是，我们实现了任务特定路由范式，该范式利用每个多模态指令任务的定义来指导分解因子的选择（详细信息可以在附录 A 中找到）。表 2 显示，基于实例的路由显著优于任务特定路由，在 MME 得分和附加多模态任务的平均性能上都取得了更高的成绩。基于实例的路由的优越表现可能源于其固有的灵活性。与任务特定路由不同，后者在来自同一任务的不同层输入中选择相同的因子，基于实例的路由根据前一层变化的隐藏状态调整其选择，从而导致更灵活的路由机制。&lt;/p&gt;
&lt;p&gt;此外，我们研究了卓越的表现是否是由于引入了额外的专家参数而不是路由机制。表 2 报告了与随机路由基线的比较，该基线随机选择 r 个因子。我们的观察表明，基于实例的路由和任务特定路由都超过了随机基线，这表明路由机制，而非额外专家参数的加入，才是关键因素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;条件因子选择的影响&lt;/strong&gt;
我们通过消融分析评估了条件因子选择（CFS）的影响，比较了在七个多模态数据集上 MixLoRA 有无 CFS 的平均性能。如图4所示的比较结果表明，在不同的因子和秩设置下，总体上加入 CFS 路由器通常能提高性能。这种增强被认为源于 CFS 在加强 LoRA A 和 B 因子选择之间相互依赖性方面的作用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;未见任务的因子选择模式&lt;/strong&gt;
我们的分析深入探讨了 LoRA A 在未见多模态任务中的因子选择模式。我们从七个未见多模态任务中每个任务随机抽取300个实例，并使用 t-SNE（Van der Maaten 和 Hinton，2008）可视化 MLP 层内的因子选择，如图5所示。我们观察到，来自相同任务的实例倾向于聚类，这表明基于实例的路由策略在跨任务分配不同的因子集方面的有效性。&lt;/p&gt;
&lt;p&gt;此外，我们还可视化了相似的已见和未见任务的因子选择模式。我们将五个不同的未见任务与训练集中五个类似的已见任务配对，每个任务探测不同的能力：SNLI-VE（未见）与 ImageText（已见）用于评估视觉蕴含，TextVQA（未见）与 InfoGraphicVQA（已见）用于OCR能力，VSR（未见）与 GQA（已见）用于推理，Pope（未见）与 VQA-ObjectPresence（已见）用于幻觉检测，以及 CIFAR-10（未见）与 ExDark（已见）用于感知能力。图6中显示的t-SNE可视化描绘了MLP层中的因子选择分布，图例的第一行表示已见任务，第二行表示相应的未见任务。为了清晰起见，每对相似的已见和未见任务使用相同的颜色方案。我们的观察结果表明，MixLoRA有效地激活了类似于那些在类似训练任务中使用的因子。这一发现表明，该模型可以根据其在类似已见任务上的训练，将因子选择策略适应于新的、未见的任务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;任务干扰分析&lt;/strong&gt;&lt;br /&gt;
为了评估 MixLoRA 在缓解任务干扰方面的效果，我们在六个相同的训练任务上测试它：“ScienceQA”、“COCO”、“FairFace”、“iNaturalist”、“ST-VQA”和“PACS”，这些任务在第3.2节中讨论过。对于每个任务，我们随机抽取300个未包含在指令微调阶段的实例进行评估。我们将 MixLoRA 与传统的 LoRA 和任务专用的 LoRA 模型（LoRASpecialist）进行比较，后者针对每个任务进行了特定任务适应参数的微调。表3显示，传统的 LoRA 模型表现出与 LoRA Specialist 相比，在不同任务上表现出不同程度的性能下降。相比之下，MixLoRA 在性能下降方面受到的影响较小，并在不同任务中展现出更一致和稳健的性能，这表明其在减少任务干扰方面的有效性。&lt;/p&gt;
&lt;p&gt;此外，我们使用公式 2 和 3 可视化了任务干扰得分。鉴于 MixLoRA 动态地为不同的实例选择因子子集（从 E 中选出 r 个），我们记录了所有 E 个因子的相关梯度，并比较了标准 LoRA 模型（r=16）和 MixLoRA（E=16，r=4）之间的任务干扰得分。图 7 可视化了 LoRA A 和 LoRA B 跨所有适应层（包括 MLP 和自注意力层）的干扰得分。分析显示，MixLoRA（E=16，r=4）相较于标准 LoRA（r=16）表现出更低的负干扰得分，进一步强调了MixLoRA 在减少任务干扰方面的有效性。&lt;/p&gt;
&lt;h3&gt;7 结论&lt;/h3&gt;
&lt;p&gt;我们引入了条件混合 LoRA，这是一种创新策略，能够针对个体输入动态构建低秩适应矩阵，以缓解参数高效多模态指令微调期间的任务干扰。在各种多模态数据集上的全面实验已经证明了 MixLoRA 的有效性，展示了其在未见多模态任务上相较于传统 LoRA 的增强性能，并证实了其在减轻任务干扰方面的有效性。&lt;/p&gt;
&lt;h3&gt;8 局限性&lt;/h3&gt;
&lt;p&gt;我们的研究专注于参数高效多模态指令微调中的任务干扰，特别是图像和文本模态，将声音和3D点云等其他模态的整合留作未来工作的方向。此外，由于训练大型模型的成本，我们的实验是在 Vision-Flan 的缩减版本上进行的。未来的研究可以从评估 MixLoRA 应用于更广泛的多模态指令微调数据集时的有效性中获益。此外，与相同秩的标准 LoRA 相比，我们的方法引入了额外的训练开销。&lt;/p&gt;
&lt;h1&gt;OFA-Prompt Tuning&lt;/h1&gt;
&lt;p&gt;Prompt Tuning for Generative Multimodal Pretrained Models&lt;/p&gt;
&lt;p&gt;提示调优已成为模型调优的新范式，并在自然语言预训练甚至视觉预训练中展示了成功。在这项工作中，我们探索将提示调优转移到多模态预训练上，重点是生成性多模态预训练模型，而不是对比性的模型。具体来说，我们在统一的序列到序列预训练模型上实现了提示调优，该模型适应于理解和生成任务。实验结果表明，轻量级提示调优可以实现与微调相当的性能，并且超越其他轻量级调优方法。此外，与微调模型相比，提示调优的模型表现出对对抗性攻击的改进鲁棒性。我们进一步发现，包括提示长度、提示深度和重新参数化在内的实验因素对模型性能有重大影响，因此我们经验性地为提示调优的设置提供了建议。尽管观察到了这些优势，我们仍然发现提示调优存在一些局限性，并相应地指出了未来研究的方向。&lt;/p&gt;
&lt;h3&gt;1 Introduction&lt;/h3&gt;
&lt;p&gt;......尽管大规模预训练模型在多个领域取得了巨大成功，但训练这些模型需要大量的计算成本。传统的微调虽然在获得高性能方面有效，但在训练效率上存在不足，特别是在预训练模型规模较大时尤为明显。Brown等人（2020）引入了提示学习的概念，鼓励模型通过手动的任务指令提示或几个任务示例的演示来生成正确的答案，而无需进一步训练调整模型参数。这通常被称为“上下文学习”&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里指的就是Language models are few-shot learners中提到的ICL，是最传统的Prompt tuning&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为模型根据给定的上下文生成响应。它帮助大规模预训练语言模型在少量样本和零样本学习中实现前所未有的性能（Brown等人，2020；Chowdhery等人，2022；Sanh等人，2021；Wei等人，2021）。&lt;/p&gt;
&lt;p&gt;受此启发，研究人员提出了一个新的范式称为提示微调（Li和Liang，2021；Liu等人，2021c；Lester等人，2021；Liu等人，2021a）。与微调相比，提示调优仅通过少量参数（例如，1%）对预训练模型进行调优。提示调优冻结了预训练模型的大多数参数，只调优几个提示嵌入，以及必要时的输出层。最近的进展表明，提示调优可以帮助预训练模型在不同的NLP下游任务中实现与微调相当的性能，包括自然语言理解。&lt;/p&gt;
&lt;p&gt;在自然语言处理以外的领域，最近的研究也展示了提示调优的有效性。Jia等人（2022）证明了视觉提示调优可以在一系列任务中超越微调，并且其在训练效率方面的优势是显著的。在跨模态表示学习中，提示调优的研究主要集中在CLIP类模型上（Radford等人，2021）。CLIP是一种基于对比学习的多模态预训练模型，通过大规模图像-文本对进行预训练。CLIP能够通过将标签转换为带有手动提示模板的文本提示，在零样本图像分类中实现卓越性能。为了提高性能，Radford等人（2021）提出了通过手工制作多个提示模板来实现提示集成。然而，由于创建硬提示繁琐，研究人员转向了CLIP的软提示应用（Rao等人，2021；Zhou等人，2021, 2022）或适配器的整合（Gao等人，2021；Zhang等人，2021）。除了在CLIP类模型上的实现外，另一条路线是将图像提示应用于预训练语言模型以进行多模态表示学习（Yao等人，2021b；Tsimpoukelli等人，2021）。尽管大规模预训练语言模型在下游迁移过程中被冻结，但它可以适应多模态下游任务的少量学习场景。**尽管如此，对于流行的生成性多模态预训练模型，包括BERT类模型和用于跨模态表示学习的编码器-解码器预训练模型，提示调优仍然未被探索。**Yao等人（2022）将调优范式与预训练范式匹配，使用手动提示。然而，轻量级提示调优是否也能对生成性多模态预训练模型有效仍未知。&lt;/p&gt;
&lt;p&gt;这项工作填补了这一空白，并率先探索生成性多模态预训练模型的提示调优。本研究的目标是调查提示调优是否对生成性多模态预训练模型的下游迁移有效，以及它如何相对于传统的微调使大规模预训练模型受益。**具体来说，我们实现了简单但有效的前缀调优，一种最流行的提示调优方法之一，在生成性多模态预训练模型上。前缀调优具有简单性的优势，同时能够在自然语言理解和生成方面实现显著的性能（Li和Liang，2021；Liu等人，2021b）。**与微调相比，提示调优可调参数的数量要小得多（约1%），从而减少了计算成本，例如内存。&lt;/p&gt;
&lt;p&gt;通过广泛的实验，我们观察到轻量级提示调优能够帮助预训练模型在4个多模态下游任务中实现与微调相当的性能，这些任务从理解到生成不等。为了分析微调和提示调优之间的差异，我们遵循假设，即大多数参数在预训练模型中被冻结的提示调优应该诱导模型鲁棒性。我们在对抗性攻击下对调优方法进行实验，并观察与假设一致的现象。更进一步，这项研究深入探讨了实现细节，并调查实验因素如提示长度、提示深度和重新参数化是否会对最终下游性能产生显著影响。我们发现，一般来说，较长的提示长度（超过20个标记）是一个更好的选择，我们的实验表明，在大多数情况下应选择64，因为较长的提示序列不仅会增加计算成本，还会导致性能下降。此外，我们展示了带有额外可训练参数的重新参数化无法在下游性能中引入显著改进。最后，我们反思该方法并说明其计算成本和训练不稳定性的缺陷，并相应地指出了未来工作的方向。&lt;/p&gt;
&lt;p&gt;在接下来的部分中，我们简要回顾相关工作，介绍生成性多模态预训练模型的提示调优，并报告实验结果和分析。最后，我们讨论在这种情景下的提示调优问题，指出未来的工作，并最终总结这项工作。&lt;/p&gt;
&lt;h3&gt;2 相关工作&lt;/h3&gt;
&lt;p&gt;在本节中，我们包括了多模态预训练以及提示调优的综述。我们首先回顾了多模态预训练的两个主要研究线中的研究。即生成性预训练和对比性预训练，并回顾了基于提示的学习在自然语言处理（NLP）和跨模态表示学习中的研究。&lt;/p&gt;
&lt;h4&gt;2.1 多模态预训练&lt;/h4&gt;
&lt;p&gt;视觉与语言预训练的兴起始于BERT（Devlin等人，2019）向跨模态表示学习的转移。一系列的研究（Lu等人，2019；Su等人，2020；Tan和Bansal，2019；Chen等人，2020d；Li等人，2019）将BERT引入多模态预训练。最近，编码器-解码器框架在多模态预训练中引起了关注，因为一些编码器-解码器模型在跨模态理解和生成任务中达到了最先进的性能（Wang等人，2021, 2022；Yu等人，2022）。此外，这种框架允许将任务统一为序列到序列学习格式，从而允许使用手动提示进行多任务预训练（Cho等人，2021；Wang等人，2022）。这促使我们思考提示调优应与最近的统一多模态预训练模型完美结合，并且它可以在计算成本远低于传统微调的情况下释放预训练模型的力量。&lt;/p&gt;
&lt;p&gt;多模态预训练中的另一个趋势是对比学习。最典型的对比预训练模型是CLIP（Radford等人，2021）。它使用Vision Transformer（ViT）（Dosovitskiy等人，2021）或ResNet（He等人，2016；Tan和Le，2019）作为图像编码器，以及Transformer模型作为文本编码器，并通过对比损失联合训练这两个编码器（van den Oord等人，2018）。值得注意的是，该模型是在极其大规模的图像-文本对数据上进行预训练的。继CLIP之后，一系列研究表明了在大规模数据上基于对比学习的预训练路线的成功（Jia等人，2021；Yao等人，2021a）。CLIP在跨模态检索中可以实现显著的性能。真正吸引人的是它在零样本分类中的强大表现，通过提示集成，即以少量手工制作的提示作为输入来集成模型的输出。这开启了多模态预训练中提示的研究。&lt;/p&gt;
&lt;h4&gt;2.2 基于提示的学习&lt;/h4&gt;
&lt;p&gt;Brown等人（2020）表明，大规模预训练模型可以从上下文中学习，并在少量样本和零样本学习中表现出色。Li和Liang（2021）证明**，仅在每一层调整软提示嵌入就足以使预训练模型在自然语言生成中实现具有竞争力的性能**，随后许多研究表明，提示调优对于低资源场景本质上是有效的（Liu等人，2021c；Gu等人，2022；Sun等人，2022b），并且它甚至可以实现与微调相当的性能（Lester等人，2021；Liu等人，2021b）。沿着这一趋势，一系列对提示和适配器的修改（Hu等人，2022；He等人，2021a；Jiang等人，2022；Sun等人，2022a）为了提高性能或训练效率而出现，并使提示调优成为整个NLP社区的热门话题。&lt;/p&gt;
&lt;p&gt;最近针对多模态预训练模型的提示调优方法主要服务于CLIP类模型（Zhou等人，2021, 2022；Rao等人，2021）。同样，研究人员试图将适配器整合到CLIP中，并且也实现了令人满意的性能（Gao等人，2021；Zhang等人，2021）。除了CLIP类模型的提示调优外，另一条研究路线探索了用于冻结语言模型的视觉提示。Tsimpoukelli等人（2021）表明，当存在一个强大的大规模预训练语言模型时，一个视觉编码器对于提示调优就足够进行多模态少量学习。更进一步，Alayrac等人（2022）提出了Flamingo，一个巨大的多模态模型，能够进行上下文学习。它可以在一系列跨模态下游任务中实现最先进的性能，无论是在少量样本还是全样本学习场景中。这种巨大的成功表明了提示调优在多模态预训练中的巨大潜力。在这项工作中，我们专注于一个未被探索的主题，即生成性多模态预训练模型的提示调优。&lt;/p&gt;
&lt;h3&gt;3 方法&lt;/h3&gt;
&lt;p&gt;本节介绍我们提出的方法的详细内容。它提供了在统一的生成性多模态预训练模型上实施提示调优的详细实现。整体框架如图1所示。&lt;/p&gt;
&lt;h4&gt;3.1 基础知识&lt;/h4&gt;
&lt;p&gt;我们选择了统一的序列到序列框架，因为它统一了理解和生成任务，并且我们具体实施了提示调优在最近开源的最先进的模型OFA（Wang等人，2022）上。简而言之，它是基于Transformer框架（Vaswani等人，2017）构建的编码器-解码器结构。&lt;/p&gt;
&lt;p&gt;编码器和解码器都由Transformer层组成。更具体地说，一个编码器层包含一个多头自注意力机制和一个逐点前馈网络（FFN）。为了在编码器和解码器之间建立连接，Transformer解码器层还包含一个交叉注意力模块，这是与编码器层相比额外的部分。交叉注意力本质上是多头注意力，其中键K和值V是编码器输出状态的转换，而不是输入。这种架构可以处理提供序列到序列格式输入的任务。&lt;/p&gt;
&lt;p&gt;在这项工作中，我们专注于提示调优在多模态预训练模型转移中的应用。我们将提示学习在预训练阶段的工作留待未来研究。&lt;/p&gt;
&lt;h4&gt;3.2 生成性多模态预训练模型的提示调优&lt;/h4&gt;
&lt;p&gt;接下来，我们介绍在序列到序列多模态预训练模型上实施提示调优的详细信息。请注意，我们的方法可以扩展到其他生成性多模态预训练模型，例如BERT类模型。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本实现&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们专注于实现【prefix tuning】前缀调优（Li和Liang，2021；Liu等人，2021b），因为它在自然语言理解和生成方面表现出色。与其他提示调优方法相比，例如P-Tuning（Liu等人，2021c）、Prompt Tuning（Lester等人，2021）、PPT（Gu等人，2022），向每一层添加软提示嵌入展示了增强的训练稳定性，即使在相对较小的模型上也能提高下游任务性能。**具体来说，对于编码器和解码器，我们在每一层添加可调提示嵌入。**正式地，我们将预训练模型表示为函数$ \mathcal{M}(\cdot) $，并将提示嵌入的生成函数表示为$ \mathcal{G}(\cdot) $。公式如下所示：&lt;/p&gt;
&lt;p&gt;$
y = \mathcal{M}(\mathcal{G}(L, l), x),
$&lt;/p&gt;
&lt;p&gt;其中$ x $指的是多模态输入，$ L $指的是层数，而$ l $指的是提示长度，应由超参数预先定义。在每一层，我们在输入隐藏状态$ h^{(i)} $前添加软提示嵌入$ p^{(i)} $。注意，我们仅在Transformer层前添加提示嵌入。在最简单的实践中，提示生成器$ \mathcal{G} $是一个稀疏嵌入矩阵$ \mathbb{R}^{L \times l \times h} $，我们选择在第$ i $-th索引和第$ j $-th层对应的嵌入作为提示嵌入。下面我们将提供一些更复杂实现的说明，并在本研究中比较这些方法。&lt;/p&gt;
&lt;p&gt;在下游调优过程中，我们只调整每一层新添加的提示嵌入，并保持大型预训练模型的参数冻结。因此，尽管只有少量参数需要更新，例如1%，计算成本远低于微调。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重新参数化&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;除了在每一层添加稀疏嵌入矩阵这一最简单的实现外，一个更复杂的实现应该是添加一个编码器，例如MLP层，以重新参数化提示嵌入。我们还调查了在此上下文中重新参数化的影响。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;提示长度&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;与先前的研究（Li和Liang，2021；Liu等人，2021b）类似，我们发现提示长度对模型性能有显著影响。提示嵌入长度在不同的下游任务中起着重要作用。在这项研究中，我们调查了这一因素如何在不同的下游任务中影响模型性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;提示深度&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了研究提示嵌入插入位置的影响，我们深入探讨了提示深度的问题。具体来说，我们将问题简化为仅向编码器或解码器添加提示嵌入，以及同时向两个模块添加提示嵌入。&lt;/p&gt;
&lt;h3&gt;4 实验&lt;/h3&gt;
&lt;p&gt;为了验证提示调优对多模态预训练模型的有效性，我们在传统的跨模态任务上进行了实验。具体而言，我们在跨模态理解和生成方面进行实验，包括指代表达理解、视觉蕴含、图像标题生成和视觉问答（VQA）。我们使用最常用的基线大小和大型模型进行实验，其大小分别为约180M和470M。关于实验设置的更多细节，请参见附录A.1。&lt;/p&gt;
&lt;h4&gt;4.1 数据集与指标&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;指代表达理解&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们在指代表达理解的三个子任务上进行实验，即RefCOCO、RefCOCO+和RefCOCOg（Yu等人，2016；Mao等人，2016）。该任务要求模型生成一个正确的边界框，以回答所提供图像上的给定文本查询。我们使用Acc@0.5作为评估指标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;图像标题生成&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们在Microsoft COCO图像标题数据集（Chen等人，2015）上评估我们的方法的图像标题生成能力。在这个任务中，模型应该生成一个与给定图像信息相对应的描述。我们使用BLEU@4（Papineni等人，2002）、METEOR（Lavie和Agarwal，2007）、CIDEr（Vedantam等人，2015）和SPICE（Anderson等人，2016）作为评估指标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;视觉蕴含&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了评估蕴含性能，我们在SNLI-VE（Xie等人，2019）上进行实验。给定一张图像和一段文本，模型应该找出它们之间的关系，即它们是蕴含、矛盾还是中立。我们遵循Wang等人（2022）的设置，并将给定的前提添加到输入中。我们使用准确率作为评估指标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VQA&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们在VQA 2.0（Antol等人，2015；Goyal等人，2017）上进行实验。该任务要求模型根据图像和关于图像上某些信息的问题生成正确的答案。遵循Wang等人（2022），我们使用所有候选评估，这要求模型为3,129个最频繁答案中的每个候选生成一个概率。我们使用准确率作为评估指标。&lt;/p&gt;
&lt;h4&gt;4.2 实验结果&lt;/h4&gt;
&lt;p&gt;下面提供详细的实验结果，包括提示调优与微调的比较，以及提示调优与其他参数高效的调优方法的比较。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;与微调的比较&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们在表1中展示了四个任务的实验结果。总体而言，对于基线大小的模型，提示调优在很大程度上逊色于微调，但对于大型模型，提示调优能够实现相当的性能。具体来说，在指代表达理解的评估中，对于基线大小的模型，提示调优显著落后于微调，平均落后幅度为5.64，跨越RefCOCO、RefCOCO+和RefCOCOg，但对于大型模型，提示调优仅落后于微调一小部分。差距仅为0.59。在视觉蕴含的评估中，算法之间的差距更小，约为0.17。在图像标题生成的CIDEr评分评估中，对于基线大小的模型，提示调优落后微调4.0，但对于大型模型，性能差距仅为0.8。在VQA的评估中，对于基线大小的模型，提示调优和微调之间的性能差距为3.63，而对于大型模型，在测试集上的差距为2.17。与其他任务不同，即使在大型模型的实验中，这一差距仍然显著。我们假设由于提示调优对超参数的敏感性，仍有必要为该任务寻找更好的超参数设置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;与其他参数高效调优方法的比较&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们还添加了一个与其它参数高效调优方法的比较。&lt;/p&gt;
&lt;p&gt;与两种参数高效调优方法进行比较，即Adapter（Houlsby等人，2019）和BitFit（Zaken等人，2022），以测试提示调优是否是轻量级迁移的最佳解决方案。表2显示了在上述数据集上实施的不同轻量级调优方法的结果。在所有下游任务中，提示调优的性能超过了Adapter和BitFit。&lt;/p&gt;
&lt;h1&gt;实操&lt;/h1&gt;
&lt;p&gt;首先希望使用LoRA微调OFA模型或者其他多模态大模型，完成一个下游任务如image captioning&lt;/p&gt;
&lt;p&gt;任务：prefix方法（对应那篇prompt tuning的论文）微调OFA大模型，仓库提供了refcoco的代码，希望能够用prefix微调OFA实现coco。&lt;/p&gt;
&lt;p&gt;分解：&lt;/p&gt;
&lt;p&gt;先看懂那篇prompt tuning的论文，是在搞什么 √&lt;/p&gt;
&lt;p&gt;然后看懂仓库，bash执行了哪些代码，怎么训练&lt;/p&gt;
&lt;p&gt;最后修改bash以完成COCO的任务&lt;/p&gt;
&lt;p&gt;现在实现了的：&lt;/p&gt;
&lt;p&gt;OFA+SFT -&amp;gt; caption 原论文提供&lt;/p&gt;
&lt;p&gt;OFA+LoRA -&amp;gt; caption 基于LoRA原理修改&lt;/p&gt;
&lt;p&gt;OFA+MixLoRA -&amp;gt; caption 基于MixLoRA论文修改&lt;/p&gt;
&lt;p&gt;OFA+Prompt tuning -&amp;gt; refCOCO 论文三提供&lt;/p&gt;
&lt;p&gt;希望实现：&lt;/p&gt;
&lt;p&gt;OFA+Prompt tuning -&amp;gt; caption&lt;/p&gt;
&lt;h2&gt;阅读核心代码&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;run_scripts/refcoco/train_refcoco_prefix.sh&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--encoder-prompt&lt;/code&gt;：是否向编码器插入提示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--decoder-prompt&lt;/code&gt;：是否向解码器插入提示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--encoder-prompt-length&lt;/code&gt;：编码器提示长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--decoder-prompt-length&lt;/code&gt; 解码器提示长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--bitfit&lt;/code&gt;：是否使用 Bitfit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--adapter&lt;/code&gt;：是否使用适配器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--adapter-dim&lt;/code&gt;：适配器投影暗淡&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;export MASTER_PORT=6051
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;指定分布式训练的通信端口（&lt;code&gt;MASTER_PORT=6051&lt;/code&gt;），多任务运行时需用不同端口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;yyc改了&lt;code&gt;train_refcoco_base.sh&lt;/code&gt;的代码，即&lt;/p&gt;
&lt;p&gt;OFA+SFT-&amp;gt;refcoco的&lt;/p&gt;
&lt;p&gt;改成了OFA+LoRA-&amp;gt;refcoco&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CUDA_VISIBLE_DEVICES=0,1,2,3 python3 -m torch.distributed.launch \
--nproc_per_node=4 --master_port=${MASTER_PORT} ../../train.py \
          $data \
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;改成了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CUDA_VISIBLE_DEVICES=1 torchrun \
--nproc_per_node=1 --master_port=${MASTER_PORT} ../../train_lora.py \
          $data \
          --lora \
          --lora-rank=8 \
          --lora-alpha=16 \
          --lora-dropout=0.1 \
          --save-lora-weights-only \
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;希望找到caption到refcoco的变化！&lt;/p&gt;
&lt;p&gt;那么只需要对比&lt;code&gt;train_refcoco.sh&lt;/code&gt;和&lt;code&gt;train_caption.sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但区别来了，&lt;code&gt;train_caption.sh&lt;/code&gt;分为stage1和stage2&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;希望找到prefix加与不加的变化！然后应用到train_caption上面！&lt;/p&gt;
&lt;p&gt;加了prefix后，多了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;prompt_type_method=prefix
encoder_prompt_length=100
decoder_prompt_length=100
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;--encoder-prompt \
          --decoder-prompt \
          --encoder-prompt-type=${prompt_type_method} \
          --decoder-prompt-type=${prompt_type_method} \
          --encoder-prompt-length=${encoder_prompt_length} \
          --decoder-prompt-length=${decoder_prompt_length} \
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改了以下超参数&lt;/p&gt;
&lt;p&gt;batch_size&lt;/p&gt;
&lt;p&gt;max_epoch&lt;/p&gt;
&lt;p&gt;lr&lt;/p&gt;
&lt;p&gt;patch_image_size&lt;/p&gt;
&lt;p&gt;--nproc_per_node&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在这里实现了prefix-tuning：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;models/ofa/unify_transformer.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;以Encoder为例子&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if getattr(args, &quot;encoder_prompt&quot;, False):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是处理参数，如果要启用prefix-tuning则调用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; self.encoder_prompt_encoder = PromptEncoder(
                type=args.encoder_prompt_type,
                length=args.encoder_prompt_length,             projection=args.encoder_prompt_projection,
                embed_dim=args.encoder_embed_dim,
                proj_dim=args.encoder_prompt_dim,
                layers=args.encoder_layers,
                vocab_size=args.vocab_size)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;projection是默认为false的&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PromptEncoder是专属类&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这段代码定义了一个名为 &lt;code&gt;PromptEncoder&lt;/code&gt; 的 PyTorch 模块，它的作用是&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生成用于 Prefix-Tuning 或 Prompt-Tuning 的可学习 prefix token 向量（past_key_values），以插入到 Transformer 编码器或解码器中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;输入：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prefix&lt;/code&gt;: 一个 Tensor，表示 prompt token 的索引（通常是 &lt;code&gt;[batch_size, prompt_length]&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;past_key_values&lt;/code&gt;: 经过编码后的 prefix 向量，形状为 &lt;code&gt;(batch_size, prompt_length, layers * 2 * embed_dim)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;layers&lt;/code&gt;: Transformer 层数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&lt;/code&gt;: 表示每个层有两个向量（key 和 value）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;embed_dim&lt;/code&gt;: 每个 key/value 向量的维度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;提示类型，如 &lt;code&gt;&quot;prefix&quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;prefix 的长度（例如 20）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;projection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;是否使用投影网络（即是否使用 MLP 而不是直接 embedding）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;embed_dim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;嵌入维度（通常等于 encoder/decoder 的 hidden dimension）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proj_dim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;投影层中间维度（如果使用 projection）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;layers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transformer 层数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vocab_size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;词汇表大小&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果不使用 Projection：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;self.embedding = torch.nn.Embedding(prompt_vocab_size, layers * 2 * embed_dim)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;直接从 token 映射到最终的 prefix 向量，没有中间变换。&lt;/li&gt;
&lt;li&gt;更简单但表达能力有限。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;假设你有一个预训练的 Transformer 编码器，现在想用 Prefix-Tuning 微调它而不改变原有参数，你可以：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在输入序列前添加一些 learnable prefix token；&lt;/li&gt;
&lt;li&gt;用这个 &lt;code&gt;PromptEncoder&lt;/code&gt; 生成这些 prefix token 对应的 key-value pairs；&lt;/li&gt;
&lt;li&gt;在 Transformer 的每一层 attention 中将它们作为额外上下文传入；&lt;/li&gt;
&lt;li&gt;只更新 &lt;code&gt;PromptEncoder&lt;/code&gt; 的参数即可完成 fine-tune。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;示例结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;embed_dim = 768&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layers = 6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;每层有 key 和 value → 每个 token 的向量长度是 &lt;code&gt;6 * 2 * 768 = 9216&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以输出 shape 为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(batch_size, prompt_length, 9216)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面的代码来调用此类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def get_encoder_prompt(self, prompt_tokens):
        past_key_values = self.encoder_prompt_encoder(prompt_tokens)
        bsz, seqlen, _ = past_key_values.shape
        past_key_values = past_key_values.view(
            bsz,
            seqlen,
            (self.args.encoder_layers) * 2,
            self.args.encoder_attention_heads,
            self.args.encoder_embed_dim // self.args.encoder_attention_heads,
        )
        past_key_values = self.encoder_dropout(past_key_values)
        past_key_values = past_key_values.permute([2, 0, 3, 1, 4]).split(2)
        return past_key_values
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而这部分代码由下面的代码调用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def forward_scriptable(
        self,
        src_tokens,
        src_lengths,
        patch_images: Optional[torch.Tensor] = None,
        patch_images_2: Optional[torch.Tensor] = None,
        patch_masks: Optional[torch.Tensor] = None,
        return_all_hiddens: bool = False,
        token_embeddings: Optional[torch.Tensor] = None,
        sample_patch_num: Optional[int] = None
    ):
        &quot;&quot;&quot;
        Args:
            src_tokens (LongTensor): tokens in the source language of shape
                `(batch, src_len)`
            src_lengths (torch.LongTensor): lengths of each source sentence of
                shape `(batch)`
            return_all_hiddens (bool, optional): also return all of the
                intermediate hidden states (default: False).
            token_embeddings (torch.Tensor, optional): precomputed embeddings
                default `None` will recompute embeddings

        Returns:
            dict:
                - **encoder_out** (Tensor): the last encoder layer&apos;s output of
                  shape `(src_len, batch, embed_dim)`
                - **encoder_padding_mask** (ByteTensor): the positions of
                  padding elements of shape `(batch, src_len)`
                - **encoder_embedding** (Tensor): the (scaled) embedding lookup
                  of shape `(batch, src_len, embed_dim)`
                - **encoder_states** (List[Tensor]): all intermediate
                  hidden states of shape `(src_len, batch, embed_dim)`.
                  Only populated if *return_all_hiddens* is True.
        &quot;&quot;&quot;
        prompt_tokens = None
        prompt_padding_mask = None
        prompt_kv_list = None
        if self.args.encoder_prompt:
            bsz, seq_len = src_tokens.shape[0], src_tokens.shape[1]
            if self.args.encoder_prompt_type in (&quot;prefix&quot;):
                prompt_tokens = torch.arange(
                    0, self.args.encoder_prompt_length).to(
                    src_tokens.device)
                prompt_tokens = prompt_tokens.unsqueeze(0).expand(bsz, -1)
                prompt_padding_mask = torch.zeros_like(
                    prompt_tokens).to(prompt_tokens.device)
            prompt_kv_list = self.get_encoder_prompt(prompt_tokens)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;可参考的资料&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://modelscope.cn/docs/model-overview/multimodal/OFA-Tutorial/OFA-Tutorial&quot;&gt;OFA_Tutorial · 文档中心&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/448852278&quot;&gt;【Huggingface Transformers】保姆级使用教程—上 - 知乎&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/AlibabaTech1024/article/details/125215198&quot;&gt;ICML 2022｜达摩院多模态模型OFA，实现模态、任务和架构三个统一_ofa模型-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;两个工具包&lt;/h1&gt;
&lt;p&gt;为什么不直接Fairseq，而是要用transformers呢？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fairseq存在比较严重的过度封装问题，接口复杂，omgaconf传参工具不容易迁移，不适合作生产环境部署&lt;/li&gt;
&lt;li&gt;fairseq做评估和ASR应用需要flashlight，由于防火墙的存在，基本上是无法按照官方教程安装的（vcpkg和编译都不容易）&lt;/li&gt;
&lt;li&gt;trms的接口比较直接明确，工具链比较简单&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fairseq&lt;/h2&gt;
&lt;p&gt;Facebook AI Research Sequence-to-Sequence Toolkit （Facebook，2017）&lt;/p&gt;
&lt;p&gt;简单说就是一个seq2seq模型的工具包，方便二次开发。&lt;/p&gt;
&lt;p&gt;Fairseq 专注于序列到序列（Seq2Seq）模型，支持多种任务，兼容 Transformer、LSTM、ConvS2S 等模型架构。集成 BART、RoBERTa、mBART 等模型，支持快速微调。&lt;/p&gt;
&lt;p&gt;Fairseq 的架构围绕以下模块展开：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model：定义模型结构（如 &lt;code&gt;TransformerModel&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;Task：封装数据加载、损失计算（如 &lt;code&gt;TranslationTask&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;Criterion：定义损失函数（如交叉熵损失 &lt;code&gt;CrossEntropyCriterion&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;Optimizer：优化算法（Adam、Adagrad 等）。&lt;/li&gt;
&lt;li&gt;LRScheduler：学习率调度策略。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;动态数据加载（Dynamic Batching）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原理：根据样本长度动态分组，减少 Padding 冗余。&lt;/li&gt;
&lt;li&gt;实现：通过 &lt;code&gt;FairseqDataset&lt;/code&gt; 和 &lt;code&gt;BatchSampler&lt;/code&gt; 动态生成相似长度的样本批次。&lt;/li&gt;
&lt;li&gt;优势：相比静态 Batching，训练速度提升 20-50%。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;分布式训练&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据并行：通过 PyTorch 的 &lt;code&gt;DistributedDataParallel&lt;/code&gt; 实现多卡同步。&lt;/li&gt;
&lt;li&gt;模型并行：支持跨 GPU 分割模型（如 Megatron-LM 式层内并行）。&lt;/li&gt;
&lt;li&gt;混合精度训练：使用 &lt;code&gt;--fp16&lt;/code&gt; 或 &lt;code&gt;--amp&lt;/code&gt; 参数启用，减少显存占用。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li&gt;安装与环境配置&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# 从源码安装（推荐开发模式）
git clone https://github.com/facebookresearch/fairseq
cd fairseq
pip install --editable .
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;--editable 通过将包安装为可编辑的源代码文件夹，我们可以直接编辑源代码，无需重新安装包即可看到更改的效果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;数据预处理&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以机器翻译任务为例（WMT英德翻译）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 数据标准化（分词、BPE编码等）
fairseq-preprocess \
    --source-lang en --target-lang de \
    --trainpref data/train --validpref data/valid --testpref data/test \
    --destdir data-bin/wmt18_en_de \
    --workers 64
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;模型训练&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;fairseq-train data-bin/wmt18_en_de \
    --arch transformer_wmt_en_de \
    --optimizer adam --lr 0.0005 --lr-scheduler inverse_sqrt \
    --max-tokens 4096 --fp16 \
    --save-dir checkpoints/transformer_wmt_en_de
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;模型推理&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;fairseq-interactive data-bin/wmt18_en_de \
    --path checkpoints/transformer_wmt_en_de/checkpoint_best.pt \
    --beam 5 --batch-size 128 --buffer-size 1000
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;高级功能与技巧&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;自定义模型与任务&lt;/p&gt;
&lt;p&gt;扩展模型：继承 &lt;code&gt;FairseqModel&lt;/code&gt; 类，重写 &lt;code&gt;forward()&lt;/code&gt; 方法。&lt;/p&gt;
&lt;p&gt;自定义任务：继承 &lt;code&gt;FairseqTask&lt;/code&gt; 类，实现 &lt;code&gt;load_dataset()&lt;/code&gt; 和 &lt;code&gt;build_model()&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;参数高效微调（PEFT）&lt;/p&gt;
&lt;p&gt;LoRA 支持：可通过修改 &lt;code&gt;TransformerEncoderLayer&lt;/code&gt; 和 &lt;code&gt;TransformerDecoderLayer&lt;/code&gt; 插入低秩适配层。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from fairseq.modules import TransformerEncoderLayer

class LoRATransformerEncoderLayer(TransformerEncoderLayer):
    def __init__(self, args, ...):
        super().__init__(args, ...)
        # 添加 LoRA 参数
        self.lora_A = nn.Linear(d_model, r, bias=False)
        self.lora_B = nn.Linear(r, d_model, bias=False)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;混合专家模型（Mixture of Experts, MoE）&lt;/p&gt;
&lt;p&gt;实现方式：使用 &lt;code&gt;fairseq.modules.MoELayer&lt;/code&gt;，通过 &lt;code&gt;--moe-expert-count&lt;/code&gt; 和 &lt;code&gt;--moe-gating-use-fp32&lt;/code&gt; 参数启用。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;fairseq-train ... --arch transformer_moe_wmt_en_de \
    --moe-expert-count 8 --moe-freq 2
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;Fairseq 生态与扩展&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;预训练模型库&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;BART：序列到序列的预训练模型，支持文本生成和重构任务。&lt;/li&gt;
&lt;li&gt;mBART：多语言机器翻译预训练模型，支持 50+ 语言。&lt;/li&gt;
&lt;li&gt;RoBERTa：优化的 BERT 变体，适用于文本分类和序列标注。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;扩展工具&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Fairseq-Plus：社区增强版，提供更多模型（如 T5、PEGASUS）和训练技巧。&lt;/li&gt;
&lt;li&gt;Hydra 配置系统：支持 YAML 配置文件管理超参数（需 Fairseq &amp;gt;= 0.10.2）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;应用示例&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;机器翻译（Translation）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;数据格式：并行语料（如 WMT）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WMT（Workshop on Machine Translation），机器翻译数据集&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;模型选择：&lt;code&gt;transformer_wmt_en_de&lt;/code&gt;（标准 Transformer）或 &lt;code&gt;transformer_vaswani_wmt_en_de_big&lt;/code&gt;（大模型）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;文本生成（Text Generation）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;任务类型：故事生成、对话生成。&lt;/li&gt;
&lt;li&gt;模型选择：&lt;code&gt;bart.base&lt;/code&gt; 或 &lt;code&gt;transformer_lm&lt;/code&gt;（自回归语言模型）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;显存不足（OOM）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;启用 &lt;code&gt;--fp16&lt;/code&gt; 或 &lt;code&gt;--memory-efficient-fp16&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;降低 &lt;code&gt;--max-tokens&lt;/code&gt; 或 &lt;code&gt;--batch-size&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;使用梯度累积（&lt;code&gt;--update-freq&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;训练速度慢&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;启用动态 Batching（&lt;code&gt;--required-batch-size-multiple&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;增加 &lt;code&gt;--num-workers&lt;/code&gt; 提升数据加载速度。&lt;/li&gt;
&lt;li&gt;使用更高效优化器（如 &lt;code&gt;--optimizer adam --adam-betas &apos;(0.9, 0.98)&apos;&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hugging Face Transformers&lt;/h2&gt;
&lt;p&gt;和Fairseq类似的地位。&lt;/p&gt;
&lt;p&gt;（Hugging Face，2019）&lt;/p&gt;
&lt;p&gt;是一个专注于自然语言处理（NLP）的开源 Python 库，提供了大量基于Transformer 架构的预训练模型（如 BERT、GPT、T5 等）和工具，支持模型的快速加载、微调、推理和部署。其核心目标是简化 Transformer 模型的应用流程，使开发者无需从零实现复杂架构即可完成 NLP 任务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 预训练模型的便捷调用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;模型加载：通过 &lt;code&gt;AutoModel&lt;/code&gt; 和 &lt;code&gt;AutoTokenizer&lt;/code&gt; 类，开发者只需几行代码即可加载预训练模型（如 &lt;code&gt;bert-base-uncased&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from transformers import AutoModel, AutoTokenizer
model = AutoModel.from_pretrained(&quot;bert-base-uncased&quot;)
tokenizer = AutoTokenizer.from_pretrained(&quot;bert-base-uncased&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;任务支持：覆盖文本分类、生成、翻译、问答等任务。例如，使用 &lt;code&gt;pipeline&lt;/code&gt; 快速实现情感分析：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from transformers import pipeline
classifier = pipeline(&quot;sentiment-analysis&quot;)
result = classifier(&quot;I love Hugging Face!&quot;)  # 输出: [{&apos;label&apos;: &apos;POSITIVE&apos;, &apos;score&apos;: 0.9998}]

&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 高效微调与训练&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;迁移学习：通过 &lt;code&gt;Trainer&lt;/code&gt; 类支持在特定任务数据集上微调预训练模型，显著减少训练时间和计算成本。&lt;/li&gt;
&lt;li&gt;分布式训练：支持多 GPU/TPU 并行训练，结合混合精度（FP16）和梯度累积技术提升效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 模型共享与社区协作&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model Hub：开发者可将微调后的模型上传至 Hugging Face Hub，供他人直接调用，形成开源协作的正向循环。&lt;/li&gt;
&lt;li&gt;跨框架兼容：同一模型可导出为 PyTorch 或 TensorFlow 格式，适应不同部署环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Hugging Face 使工业界能快速复现和部署新模型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过社区贡献，Hugging Face 支持了多种小众模型（如 RWKV，结合 Transformer 并行性与 RNN 的长序列处理能力），扩展了 Transformer 生态的边界。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;降低技术门槛 ，非技术人员可通过 GUI 工具（如 Hugging Face Spaces）直接部署模型应用，无需编写代码。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;HFT库的PEFT&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://hugging-face.cn/docs/peft/quicktour&quot;&gt;快速入门PEFT - Hugging Face 机器学习平台&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;支持的 PEFT 类型&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;PROMPT_TUNING&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MULTITASK_PROMPT_TUNING&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;P_TUNING&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PREFIX_TUNING&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LORA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ADALORA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BOFT&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ADAPTION_PROMPT&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IA3&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LOHA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LOKR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OFT&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;XLORA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;POLY&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LN_TUNING&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VERA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FOURIERFT&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HRA&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BONE&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PROMPT_TUNING&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：在输入序列前添加可训练的软提示（soft prompt）向量，通过调整这些提示来引导模型适应下游任务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：仅训练提示参数，冻结预训练模型权重，适用于轻量级微调。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MULTITASK_PROMPT_TUNING&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：扩展 Prompt Tuning，为多个任务设计共享的提示向量，实现多任务联合学习。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：通过共享提示参数提升跨任务泛化能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;P_TUNING&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：引入可学习的连续提示嵌入（continuous prompt embeddings），替代传统离散提示词。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：提示向量插入输入层或中间层，灵活性高于 Prompt Tuning。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PREFIX_TUNING&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：在模型每一层的输入前添加可训练的前缀向量（prefix），通过调节前缀激活模型特定行为。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：参数分布在多个层，更适合复杂任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LORA（Low-Rank Adaptation）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：用低秩矩阵分解模拟权重变化（ΔW = A·B），仅训练低秩矩阵 A 和 B。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：显著减少参数量，适合大模型（如 LLM）高效微调。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ADALORA（Adaptive LoRA）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：动态调整 LoRA 中低秩矩阵的秩（rank），根据重要性分配参数资源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：比标准 LoRA 更高效，参数利用率更高。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;BOFT（Bidirectional Orthogonal Fine-Tuning）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：通过正交变换约束参数更新方向，保持模型稳定性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：适用于防止灾难性遗忘的多任务场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ADAPTION_PROMPT&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：结合 Prompt Tuning 和适配器（Adapter），在特定层插入可训练提示模块。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：增强模型对任务特定模式的捕捉能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IA3（Infused Adapter by Inhibiting and Amplifying Inner Activations）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：在注意力层和前馈层注入可学习的缩放向量，通过抑制或放大激活值调整模型行为。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：参数极少（仅 0.01%），适合超轻量级微调。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LOHA（Low-Rank Hadamard Adaptation）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：利用哈达玛乘积（Hadamard product）优化低秩矩阵，提升参数效率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：类似 LoRA，但数学形式更高效。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LOKR（Low-Rank Kronecker Adaptation）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：使用克罗内克积（Kronecker product）分解权重矩阵，进一步压缩参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：适合极低资源场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OFT（Orthogonal Fine-Tuning）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：约束参数更新在正交空间内，保持模型表征的几何结构。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：提升微调稳定性和泛化性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;XLORA&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：扩展 LoRA，结合专家混合（MoE）机制动态分配低秩矩阵。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：适用于多任务或大规模模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;POLY&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：通过多项式逼近（polynomial approximation）建模参数变化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：数学驱动的高效参数更新。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LN_TUNING（LayerNorm Tuning）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：仅微调 Layer Normalization 层的参数，保留主体权重不变。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：极端轻量化，适用于低资源设备。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VERA（Vector-based Random Matrix Adaptation）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：用随机初始化的低秩向量矩阵调整模型权重，冻结原始参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：类似 LoRA，但更强调随机性的高效利用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FOURIERFT（Fourier Feature Tuning）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：在傅里叶频域中调整模型参数，捕捉高频/低频特征。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：适合处理信号或图像相关任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HRA（Hierarchical Residual Adaptation）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：通过分层残差结构逐步调整模型，从浅层到深层逐步细化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：模仿人类渐进学习过程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;BONE&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：可能指 &lt;strong&gt;Bottleneck Expert Networks&lt;/strong&gt;，通过稀疏激活的专家模块实现高效微调。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：类似 MoE，但更轻量化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;其他多模态预训练模型&lt;/h1&gt;
&lt;h3&gt;UNITER&lt;/h3&gt;
&lt;p&gt;通用图像-文本联合表示学习&lt;/p&gt;
&lt;p&gt;旨在通过大规模预训练学习通用的图像-文本联合表示，支持多种下游视觉-语言（V+L）任务。其关键创新包括 条件掩码机制 和 词-区域对齐优化 。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 模型架构&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单流Transformer结构：将图像区域特征（通过Faster R-CNN提取）与文本词嵌入拼接后输入单一Transformer，实现跨模态交互。&lt;/li&gt;
&lt;li&gt;位置编码：为图像区域和文本词分别编码空间位置和序列位置，增强模态内和跨模态的上下文感知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 预训练任务&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;条件掩码学习：
&lt;ul&gt;
&lt;li&gt;MLM&lt;/li&gt;
&lt;li&gt;MRM（掩码区域建模）：以完整文本为条件，预测被掩码的图像区域特征（含三种变体：MRC、MRFR、MRC-KL）。&lt;/li&gt;
&lt;li&gt;与联合随机掩码相比，条件掩码显著提升了跨模态对齐效果。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;图像-文本匹配（ITM）&lt;/li&gt;
&lt;li&gt;词-区域对齐（WRA）：引入最优传输（Optimal Transport）算法，最小化单词与图像区域的语义分布差异，实现细粒度对齐。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 性能与应用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在6大类V+L任务（如VQA、图像检索、视觉推理等）的9个数据集上达到SOTA，尤其在需要细粒度对齐的任务（如Referring Expression Comprehension）中优势显著。&lt;/li&gt;
&lt;li&gt;通过消融实验验证了条件掩码和WRA任务的有效性，证明二者对模型泛化能力提升贡献最大。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;ViLT&lt;/h3&gt;
&lt;p&gt;Vision-and-Language Transformer&lt;/p&gt;
&lt;p&gt;视觉-语言Transformer的轻量化设计&lt;/p&gt;
&lt;p&gt;首个完全基于Transformer处理多模态输入的模型，摒弃传统目标检测器，直接对图像分块进行特征提取，显著提升了计算效率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 模型架构&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单流Transformer：将图像分块（类似ViT）与文本词嵌入统一输入Transformer，无需预训练目标检测器，减少计算开销。&lt;/li&gt;
&lt;li&gt;轻量化设计：相比UNITER依赖Faster R-CNN，ViLT直接利用线性投影提取图像块特征，参数更少且训练速度更快。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 预训练任务&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基础任务：MLM、ITM，与UNITER类似。&lt;/li&gt;
&lt;li&gt;对比学习增强：引入图像-文本对比损失（类似CLIP），通过负样本对优化跨模态相似性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 优势与局限&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;效率优势：训练速度较UNITER提升3倍以上，适合资源受限场景。&lt;/li&gt;
&lt;li&gt;性能权衡：因缺乏区域级特征，在需要细粒度理解的任务（如视觉推理）上表现略逊于UNITER。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模型&lt;/th&gt;
&lt;th&gt;核心创新&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;th&gt;性能特点&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UNITER&lt;/td&gt;
&lt;td&gt;条件掩码、词-区域对齐&lt;/td&gt;
&lt;td&gt;需细粒度对齐的V+L任务&lt;/td&gt;
&lt;td&gt;SOTA精度，计算成本较高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ViLT&lt;/td&gt;
&lt;td&gt;无检测器的单流Transformer&lt;/td&gt;
&lt;td&gt;资源受限的快速推理场景&lt;/td&gt;
&lt;td&gt;高效，牺牲部分细粒度性能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OFA&lt;/td&gt;
&lt;td&gt;任务无关的统一架构&lt;/td&gt;
&lt;td&gt;多任务联合需求与零样本迁移&lt;/td&gt;
&lt;td&gt;全能型，生成任务表现突出&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;其他可能相关的研究&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/713401876&quot;&gt;MULTIINSTRUCT: Improving Multi-Modal Zero-Shot Learning via Instruction Tuning - 知乎&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;其他同名研究辨析&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yushuiwx/Mixture-of-LoRA-Experts&quot;&gt;yushuiwx/Mixture-of-LoRA-Experts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/TUDB-Labs/MixLoRA&quot;&gt;TUDB-Labs/MixLoRA: State-of-the-art Parameter-Efficient MoE Fine-tuning Method&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EricLBuehler/xlora&quot;&gt;EricLBuehler/xlora: X-LoRA: Mixture of LoRA Experts&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;想法&lt;/h1&gt;
&lt;p&gt;OFA是一个多模态预训练大模型，github提供了SFT的方法。当然也可以到transformers去做各种操作。&lt;/p&gt;
&lt;p&gt;而MixLoRA一种LoRA的改进，是在LLaVA验证的。github仓库提供了微调LLaVA的过程。但不好修改。&lt;/p&gt;
&lt;h1&gt;相关知识&lt;/h1&gt;
&lt;h2&gt;关于MME基准&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;感知类任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;感知能力是MLLM最基本的能力之一，缺乏这种能力很容易导致对象幻觉问题，即MLLM基于自己的想象而非图像的实际内容来回答问题，如图4所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picx.zhimg.com/v2-a8fc55946829cc214fd2f5885a125ca3_1440w.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;图4. 实验中揭示出的常见问题。[Y]/[N]表示真实答案是是/否，[R]是生成的答案。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;粗粒度识别&lt;/strong&gt;：粗粒度识别包括常见对象的存在、数量、颜色和位置。这些图像取自COCO数据集，但指令-答案对均为手工构建，而非直接使用公开的注释。即使MLLM已经见过这些COCO图像，我们手动准备的对并未出现在它们的训练集中。这要求MLLM能够理解指令并推断相应的答案。在存在、数量、颜色和位置的每个感知子任务中，我们准备了30张图像和60个指令-答案对。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;细粒度识别&lt;/strong&gt;：细粒度识别更侧重于测试MLLM的知识资源。子任务包括识别电影海报、名人、场景、地标和艺术品，分别包含147、170、200、200和200张图像。对于名人，我们在图像中为一个面部清晰可见的人物画一个红框，并给出相应的指令：“&lt;em&gt;红框内的演员名叫[名人姓名]吗？请回答是或否。&lt;/em&gt;”与粗粒度识别类似，这些子任务的图像来自公开数据集，所有指令均为手工设计。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OCR&lt;/strong&gt;：光学字符识别（OCR）也是MLLM的基础能力，为后续的文本翻译和理解任务提供支持。这些图像的所有相关指令-答案对均为手工设计。考虑到MLLM仍处于初期阶段，此版本的MME中仅选择了相对简单的样本，图像和指令-答案对的数量分别为20和40。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;认知类任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;团队评估MLLM在感知图像后是否能够进行进一步的逻辑推理，这是MLLM相较于传统方法的一个显著优势。为了推导出正确答案，MLLM需要遵循指令、感知图像内容并调用其储存的知识，这比单纯的感知任务更具挑战性。以下子任务的示例如图1所示。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常识推理&lt;/strong&gt;：与ScienceQA数据集中需要专业知识不同，常识指的是日常生活中的基本知识。例如，给出一张羽绒服的照片，问MLLM在冷天（或热天）穿这件衣服是否合适。这些是人类无需复杂推理即可立即判断的基本知识。因此，团队期望MLLM在零样本情况下能表现良好。这些图像均为手工拍摄或通过扩散模型生成，所有指令-答案对均为手工设计。共有70张图像和140个指令-答案对。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;数值计算&lt;/strong&gt;：这要求MLLM能够读取图像中的算术问题并端到端地输出答案，如在[20]中所示。在此版本中，我们仅考虑相对简单的算术问题，如加法和乘法。共有20张图像和40个指令-答案对，所有图像均为手工拍摄，所有指令-答案对均为手工设计。&lt;/p&gt;
&lt;h2&gt;COCO captions等任务&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;SNLI-VE&lt;/strong&gt;：全称为Stanford Natural Language Inference Visual Entailment，这是一个结合了自然语言推理和视觉信息的任务。它要求模型判断给定的图像是否支持或反驳一个陈述句，从而评估模型理解和整合视觉与文本信息的能力。&lt;/p&gt;
&lt;h3&gt;三个Refcoco&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;标注方式上：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RefCOCOg采用的是非交互式标注法，选定区域请人标注，再请另外一批人根据标注的expression选择对应的region；&lt;/li&gt;
&lt;li&gt;RefCOCO和RefCOCO+采用的是双人游戏 (Refer it game)的方式.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;数据划分方式上：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RefCOCO和RefCOCO+包含train, val, testA, testB。testA的图片包含多个人；testB的图片包含多个除人之外的物体。&lt;strong&gt;同一个图片的object-expression样本对要么全在训练集，要么全在验证\测试集&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;RefCOCOg包含train, val, test。是按照object进行划分的，&lt;strong&gt;同一个图片的object-expression样本对集合可能会在训练集一部分，在验证\测试集另一部分&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;图片选择上：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RefCOCO：图像包含同一类别的多个物体。&lt;/li&gt;
&lt;li&gt;RefCOCO+：图像包含同一类别的多个物体，并且expression不能有绝对位置（e.g., left）的词。&lt;/li&gt;
&lt;li&gt;RefCOCOg：图像包含同一类别的2-4个物体，覆盖面积超过图片面积的5%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;评估指标：&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;Accuracy&lt;/h4&gt;
&lt;p&gt;prediction box和groud-truth box的交并比（intersection over union，IoU）大于0.5记为一次正确定位，以此来计算准确率（Accuracy）。&lt;/p&gt;
&lt;p&gt;最近部分工作还使用Recall@k指标，表示预测概率前k大的prediction box和ground-truth box的IoU大于0.5的定位准确率&lt;/p&gt;
&lt;h4&gt;?&lt;/h4&gt;
&lt;p&gt;Pointing game，选择最终预测的attention mask中权重最大的像素位置，如果该点落在ground-truth区域内，记为一次正确定位。相比Acc指标更加宽松。&lt;/p&gt;
&lt;h3&gt;COCO  Captions&lt;/h3&gt;
&lt;p&gt;BLEU@4（Papineni等人，2002）&lt;/p&gt;
&lt;p&gt;METEOR（Lavie和Agarwal，2007）&lt;/p&gt;
&lt;p&gt;CIDEr（Vedantam等人，2015）&lt;/p&gt;
&lt;p&gt;SPICE&lt;/p&gt;
&lt;h4&gt;BLEU&lt;/h4&gt;
&lt;p&gt;Bilingual Evaluation Understudy，双语评估辅助工具&lt;/p&gt;
&lt;p&gt;核心思想&lt;/p&gt;
&lt;p&gt;比较候选译文和参考译文里的 n-gram 的重合程度，重合程度越高就认为译文质量越高。unigram用于衡量单词翻译的准确性，高阶n-gram用于衡量句子翻译的流畅性。 实践中，通常是取N=1~4，然后对进行加权平均，这种叫做BLEU@4。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;**候选译文：**
the cat sat on the mat

**参考译文1：**
the cat is on the mat

**参考译文2：**
there is a cat sitting on the mat

例子：Unigram 精确度计算

- 候选译文中的unigram集合为：`{the, cat, sat, on, the, mat}`
- 参考译文1中的unigram集合为：`{the, cat, is, on, the, mat}`
- 参考译文2中的unigram集合为：`{there, is, a, cat, sitting, on, the, mat}`

对每个词，计算其在候选译文中出现次数与在两个参考译文中出现的最大次数的最小值。例如，“the”在候选译文中出现了2次，在参考译文1中也出现了2次，在参考译文2中出现了1次。因此，我们取最大匹配数2（来自参考译文1）。

所以，unigram的精确度为：
min(2,2) + min(1,1) + min(1,0) + min(1,1) + min(1,1) / 6 ≈ 0.833
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;细节：引入了短句惩罚&lt;/p&gt;
&lt;h4&gt;ROUGE&lt;/h4&gt;
&lt;p&gt;基于&lt;strong&gt;召回率&lt;/strong&gt;的相似性度量方法，主要考察参考译文的充分性和忠实性，无法评价参考译文的流畅度，它跟BLEU的计算方式几乎一模一样，但是 n-gram 词组是从参考译文中产生的&lt;/p&gt;
&lt;h4&gt;METEOR&lt;/h4&gt;
&lt;p&gt;Metric for Evaluation of Translation with Explicit ORdering&lt;/p&gt;
&lt;p&gt;METEOR 显得更加人性化，它关注到那些翻译准确、但是和候选译文还是对不上的参考译文，比如参考译文用了候选译文的同义词。METEOR 需要 WordNet 扩充同义词集，同时需要考虑单词词性（比如like、likes应该都算对）；在计算方式上它融合了准确率、召回率，利用二者的调和平均值来作为评判标准。&lt;/p&gt;
&lt;p&gt;最终得分计算：METEOR计算了一个加权调和平均数来平衡精确度和召回率，同时应用一个基于chunk的惩罚因子。Chunk指的是在候选译文中连续且正确的词语序列，较长的连续正确序列会导致较低的chunk数量，从而减少惩罚。&lt;/p&gt;
&lt;h4&gt;CIDEr&lt;/h4&gt;
&lt;p&gt;Consensus-based Image Description Evaluation&lt;/p&gt;
&lt;p&gt;CIDEr 首先将 n-grams 在参考译文中出现的频率编码进来，通过TF-IDF 计算每个 n-gram 的权重，将句子用 n-gram 表示成向量的形式，然后计算参考译文和候选译文之间的 TF-IDF 向量余弦距离，以此度量二者的相似性。&lt;/p&gt;
&lt;p&gt;CIDEr旨在通过比较候选字幕和一组参考字幕之间的n-gram重合度来衡量字幕的质量。不同于BLEU直接使用n-gram匹配的方式，CIDEr特别强调了那些在参考集合中出现频率较高的一致性短语。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;TF-IDF加权：首先，对于每个n-gram（通常从unigram到4-gram），计算其在整个数据集中的逆文档频率（IDF）。这一步骤的目的是降低那些在大多数图像描述中都常见的n-gram的重要性，同时提升那些对特定图像更为独特的n-gram的权重。具体来说，一个n-gram的IDF值是通过对所有图像的参考字幕进行统计得出的，计算公式如下：
$ IDF(n\text{-gram}) = \log \left( \frac{N}{df(n\text{-gram})} \right) $
其中，$ N $ 是总的图像数量，$ df(n\text{-gram}) $ 是包含该n-gram的图像数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;向量化表示：接下来，将每个候选字幕和参考字幕转换为基于上述加权n-gram的向量表示。这意味着每个caption都被表示为一个向量，其中每个维度对应一个n-gram，并且值为其TF-IDF加权计数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;余弦相似度：计算候选字幕向量与每个参考字幕向量之间的余弦相似度&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最终得分：最后，候选字幕的CIDEr得分为其与所有参考字幕相似度的平均值。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;TODO：解释不够清楚&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;SPICE&lt;/h4&gt;
&lt;p&gt;Semantic Propositional Image Caption Evaluation&lt;/p&gt;
&lt;p&gt;专注于比较候选字幕和参考字幕之间的场景图（scene graph）表示&lt;/p&gt;
&lt;p&gt;首先，需要将每个候选字幕和参考字幕转换成场景图。这一过程通常借助自然语言处理技术来识别字幕中的对象、属性和关系，并将其映射到一个结构化图中。&lt;/p&gt;
&lt;p&gt;接下来，使用一种称为F-score的方法来衡量候选字幕场景图与一组参考字幕场景图之间的相似性。&lt;/p&gt;
&lt;p&gt;对于每个图像，计算所有参考字幕与候选字幕之间的平均F-score作为该图像的SPICE得分。&lt;/p&gt;
&lt;h3&gt;VQA&lt;/h3&gt;
&lt;p&gt;VQA：Visual Question Answering&lt;/p&gt;
&lt;p&gt;视觉问答任务。在这个任务中，模型需要回答关于图像的问题，这不仅要求模型具备图像理解能力，还需要其能够处理和回答各种类型的自然语言问题。&lt;/p&gt;
&lt;p&gt;数据集被划分为训练集（train）、验证集（val）、开发测试集（test-dev）和标准测试集（test-std）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开发测试集（Test-dev Set）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;主要用于调试和验证实验结果。研究人员可以无限制地向评估服务器提交结果来检查他们的模型在未知数据上的表现。&lt;/li&gt;
&lt;li&gt;Test-dev是公开的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标准测试集（Test-std Set）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;是VQA竞赛中的“默认”测试数据集&lt;/li&gt;
&lt;li&gt;是用于正式评估模型性能的数据集，提供了公平的竞争环境&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>笔记-cs224n</title><link>https://blog.stivine.fun/posts/notes-cs224n/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/notes-cs224n/</guid><description>斯坦福NLP经典课程</description><pubDate>Sun, 20 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;课程基本信息&lt;/h1&gt;
&lt;p&gt;https://web.stanford.edu/class/cs224n/index.html&lt;/p&gt;
&lt;h1&gt;Lecture 1 - Intro &amp;amp; Word Vectors&lt;/h1&gt;
&lt;p&gt;keyword: word2vec algorithm
point: language is a social system, constructed and interpreted by people.&lt;/p&gt;
&lt;p&gt;语言蕴含了太多信息，每个人又会用不同方式解读，我们真正做的事情是，试图更好地猜测语言表述能如何影响其他人。&lt;/p&gt;
&lt;p&gt;类似的观点我还真的想过。。。&lt;/p&gt;
&lt;p&gt;Language是个很新的东西。在地球生命进化的尺度去想。&lt;/p&gt;
&lt;p&gt;AI需要人类的知识来学习，但是这些知识又蕴藏在人类的语言文字记录当中。我们能否在其中建立良性循环？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;机器翻译&lt;/li&gt;
&lt;li&gt;GPT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What is &quot;meaning&quot;? denotational semantics 指称语义&lt;/p&gt;
&lt;p&gt;Is-a关系 WordNet&lt;/p&gt;
&lt;p&gt;a localist representation 对单词one-hot编码&lt;/p&gt;
&lt;p&gt;这些向量是正交的，没有相似的概念，怎么办？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;下面介绍：distributional semantics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;利用context words 非常成功的想法&lt;/p&gt;
&lt;p&gt;我们提到word时要注意我们是把它视作type还是token&lt;/p&gt;
&lt;p&gt;word embeddings&lt;/p&gt;
&lt;p&gt;可视化（二维投影）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;算法：Word2vec (Mikolov, 2013)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;固定尺寸窗口 遍历corpus 语料库&lt;/p&gt;
&lt;p&gt;很好理解的损失函数，负的对数似然&lt;/p&gt;
&lt;p&gt;改进：每个word对应到两个vector，一个是作为中心词的，一个作为上下文词的&lt;/p&gt;
&lt;p&gt;现在的问题：怎么根据单词参数计算相似度的？&lt;/p&gt;
&lt;p&gt;内积，然后SoftMax函数&lt;/p&gt;
&lt;p&gt;下面训练模型 梯度下降最小化损失即可&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NLP库：Gensim&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;Lecture 2 - Neural Classifier&lt;/h1&gt;
&lt;p&gt;上节课对Word2vec进行的是一个大概的描述，注意到他有一个特点是，一个词周围（窗口内）出现特定词的概率是确定的，对每个位置每次都按照相同的概率进行预测。&lt;/p&gt;
&lt;p&gt;SGD的介绍&lt;/p&gt;
&lt;p&gt;注意到，SGD每次处理一个窗口的损失函数，却要对所有参数求梯度，因此求出来的梯度是十分稀疏的。我们可以想到，每次只更新那些窗口内词的参数。Pytorch架中，词向量实际被表示为行向量，这样可以把整个词向量作为连续的内存范围访问，这样才能做到这一点，否则需要为所有词向量维护哈希表。&lt;/p&gt;
&lt;p&gt;Word2vec让每个词对应到两个向量，单纯是为了好计算梯度进而容易优化。对应到一个向量效果可能还会更好。&lt;/p&gt;
&lt;p&gt;实际模型可以分为两种实现：&lt;/p&gt;
&lt;p&gt;Skip-grams（SG）&lt;/p&gt;
&lt;p&gt;Continuous Bag of Words（CBOW）&lt;/p&gt;
&lt;p&gt;计算损失时他们都没有使用SoftMax（计算开销太大了），而是使用Negative sampling&lt;/p&gt;
&lt;p&gt;以SGNS为例&lt;/p&gt;
&lt;p&gt;他们采样的时候，特定单词的概率从它在大语料库中的占比计算得到，特别的是分子上有一个3/4次方，这可以让罕见词概率更大一些。·&lt;/p&gt;
&lt;p&gt;换一个思路：为什么不直接计数？count based方法。&lt;/p&gt;
&lt;p&gt;co-occurrence matrix，十分容易想到的方法&lt;/p&gt;
&lt;p&gt;问题也很容易想到：维数是词汇总数，稀疏。如何降低维数？&lt;/p&gt;
&lt;p&gt;对co-occurrence matrix进行奇异值分解（SVD），但由于数据不服从正态分布，效果不会太好。可以先进行值的缩放。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里符合自己之前学过的知识诶！维数和数据点数一样多，不能用特征值分解。&lt;/p&gt;
&lt;p&gt;吴建鑫《模式识别》的PCA讲解真的好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下面介绍一种融合了多方优点的方法：GloVe&lt;/p&gt;
&lt;p&gt;下面介绍 如何评估word vector&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;word vector analogy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实践中发现的规律： 数据源质量的重要性、维数的选择&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;人类对词相似度的评分，计算相关系数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;词向量对named entity recognition（命名实体识别）的影响&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多义词现象？可以对同一个词进行多种标记，不同意思的用不同向量表示&lt;/p&gt;
&lt;p&gt;但这种词义的分割也是模糊的。实际应用中，还是让一个词用一个向量表示，该向量是多个不同含义的叠加。效果是好的。思考为什么？&lt;/p&gt;
&lt;h1&gt;Lecture 3 - Backprop and Neural Networks&lt;/h1&gt;
&lt;p&gt;从named entity recognition（命名实体识别，NER）引入&lt;/p&gt;
&lt;p&gt;简单的利用神经网络的想法：每个待识别词作为中心词，考虑一个窗口内（比如大小为5）的5个词，用word2vec转换成词向量，连接起来成为长向量，作为分类器的输入。&lt;/p&gt;
&lt;p&gt;然后梯度下降更新网络参数。这里特殊的是，我们还可以同时更新我们的词向量表示。&lt;/p&gt;
&lt;p&gt;实践了下用链式法则求梯度的过程。梯度形状的复杂转换。&lt;/p&gt;
&lt;p&gt;反向传播。&lt;/p&gt;
&lt;p&gt;这里提到General Computation Graph还是很值得学习的，提到拓扑排序，提到正向和反向计算时间复杂度相同。&lt;/p&gt;
&lt;h1&gt;Lecture 4 - Syntactic Structure and Dependency Parsing&lt;/h1&gt;
&lt;p&gt;介绍了&lt;strong&gt;constituency grammars（成分语法）&lt;/strong&gt;，是一种context-free grammar&lt;/p&gt;
&lt;p&gt;又介绍了&lt;strong&gt;dependency grammars / dependency structure（依存语法 / 依存结构）&lt;/strong&gt;，是对句法的分析。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;老师用 look in the large crate in the kitchen by the door 作为例句。箭头从被修饰的词指向修饰词。这是Tesniere的惯例，当然也可以反过来。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;介词短语附着歧义与卡特兰数&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么歧义种数是卡特兰数？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一种二元非对称关系&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;例子：cuddly cat，箭头从cat指向cuddly，cuddly被称为cat的dependent，cat被称为cuddly的head&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;句子中的这些关系最终形成了连通无环有着单一根的有向图—从而形成了依存树&lt;/p&gt;
&lt;p&gt;David Hayes在20世纪60年代发表了（也许是）第一个dependency parser.&lt;/p&gt;
&lt;p&gt;为了方便算法实现，通常让真正的根（中心词）修饰一个假的ROOT。&lt;/p&gt;
&lt;p&gt;人类标注，产生treebanks（树库）&lt;/p&gt;
&lt;p&gt;特征：valency（配价） of the heads&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;个人理解是描述中心词左右通常出现什么词的先验事实？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以专门研究“投射的”解析（projective parse）。即不会存在交叉的解析。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但英语中的介词悬空等现象通常允许非投射的解析&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;如何构建一个parser？&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;DP O(n^3)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Graph algorithms&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Constraint Satisfaction&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Transition-based parsing / deterministic dependency parsing&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们来关注最后一种方法。“类似于shift and reduce parser（移进-归约解析器）”&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Basic transition-based dependency parser

Start: σ = [ROOT], β = w₁, ..., wₙ, A = ∅

1. Shift
   σ, wᵢ|β, A → σ|wᵢ, β, A

2. Left-Arc
   σ|wᵢ|wⱼ, β, A → σ|wⱼ, β, A∪{r(wⱼ,wᵢ)}

3. Right-Arc
   σ|wᵢ|wⱼ, β, A → σ|wᵢ, β, A∪{r(wᵢ,wⱼ)}

Finish: σ = [w], β = ∅

例句：[root] I ate fish. 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我草妙啊。然后训练使得模型能够预测下一个action即可。O(n)时间复杂度。&lt;/p&gt;
&lt;p&gt;如果有20种依存关系，那么action维度就是2x20+1=41种。&lt;/p&gt;
&lt;p&gt;可以贪婪方法，也可以加上beam search等搜索方法。&lt;/p&gt;
&lt;p&gt;那么如何构建特征呢？可能看看栈顶、缓冲区顶的单词之类的。通常这些解析器有million级别的特征。&lt;/p&gt;
&lt;p&gt;如何评估？Acc即可。&lt;/p&gt;
&lt;h1&gt;Lecture 5 - Recurrent Neural networks (RNNs)&lt;/h1&gt;
&lt;p&gt;神经网络的出现到深度学习的繁荣之间有一大段停滞，各种模型细节和训练技巧发挥着重要的作用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;“如今我们不再相信过拟合”，大型网络通过大量参数完美拟合训练集的同时实现泛化性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为了实现泛化性能，正则化是重要的——dropout 两个视角看待，一方面是避免部分特征依赖，增加鲁棒性；另一方面是可以认为dropout是一种集成学习。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;深度学习讨厌for循环，用矩阵向量计算替代。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;简单介绍了优化器问题。记住Adam这个词。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;初始化问题（鞍点？），小范围随机数，后续Layer Norm可以减轻初始化的影响。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Language Model&lt;/p&gt;
&lt;p&gt;n-gram&lt;/p&gt;
&lt;p&gt;平滑方法（加一、回退、插值Kneser-Ney平滑）&lt;/p&gt;
&lt;p&gt;A (fixed-window) Neural Language Model&lt;/p&gt;
&lt;p&gt;RNN的出现使得我们可以处理可变长度的输入&lt;/p&gt;
&lt;p&gt;RNN显著的问题：无法并行、无法解决长距离依赖（梯度消失）&lt;/p&gt;
&lt;p&gt;（梯度爆炸可以用梯度裁剪简单解决）&lt;/p&gt;
&lt;p&gt;训练细节：交叉熵损失（其实就是对数似然，因为真实标签只有一个）、teacher forcing的训练方法（每次用正确答案去预测下一个词）&lt;/p&gt;
&lt;p&gt;$\mathcal{L}&lt;em&gt;t = -\sum&lt;/em&gt;{i=1}^V y_t^{(i)} \log(\hat{y}_t^{(i)})$，其中$y_t$是真实标签的one-hot编码&lt;/p&gt;
&lt;p&gt;RNN的BP：BPTT算法&lt;/p&gt;
&lt;p&gt;RNN通常会限制最大上下文长度，把语料库截断若干片段，用片段训练&lt;/p&gt;
&lt;h1&gt;Lecture 6 - Sequence to Sequence Models&lt;/h1&gt;
&lt;p&gt;如何评估LM&lt;/p&gt;
&lt;p&gt;perplexity 困惑度 预测概率倒数的几何平均值 现在不咋用了&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;困惑度其实就是交叉熵的指数&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RNN的改进——LSTM&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hochreiter, Schmidhuber（1997）最早提出&lt;/p&gt;
&lt;p&gt;但后续的Gers, Schmidhuber（2000）第二篇LSTM的论文也十分重要&lt;/p&gt;
&lt;p&gt;当时没有得到太多重视&lt;/p&gt;
&lt;p&gt;Schmidhuber后来有一个学生Alex Graves用LSTM做了更多工作，还去当了Hinton的博士后，让LSTM越来越火&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;LSTM能一定程度解决梯度消失的核心思想：加性机制，有点像Resnet、Densenet、HighwayNet的思想&lt;/p&gt;
&lt;p&gt;RNN类模型的诸多应用：在各种其他信息的基础上生成文本（conditional language model）&lt;/p&gt;
&lt;p&gt;表示信息可以所有h取均值，也可以直接取最后一个&lt;/p&gt;
&lt;p&gt;BiLSTM 作为表示模型的改进&lt;/p&gt;
&lt;p&gt;多层RNN（2、3层的居多）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;机器翻译&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;统计机器翻译 是一个突破 但效果完全不够好&lt;/p&gt;
&lt;p&gt;神经机器翻译 seq2seq model 伟大的进步&lt;/p&gt;
&lt;p&gt;最简单的例子，用两个LSTM，一个编码一个解码，端到端训练&lt;/p&gt;
&lt;h1&gt;Lecture 7 - Attention, Final Projects and LLM intro&lt;/h1&gt;
&lt;p&gt;BLEU评估&lt;/p&gt;
&lt;p&gt;早期的Attention：编码器每一步的隐藏状态都作为key。对解码器的每一步的隐藏装状态计算注意力得分，根据结果把编码器状态加权，与当前解码器状态连接起来用于输出。&lt;/p&gt;
&lt;p&gt;这在当时大大改进了LSTM机器翻译。&lt;/p&gt;
&lt;p&gt;注意力得分具体是如何计算的？直接点积的改进：bilinear attention（双线性注意力），$a^Tb\to a^TWb$，W可学习。W参数量太多怎么办？让W是两个低秩矩阵的乘积。这其实等价于让两个向量用对应的低秩矩阵投影后再点积。（基本和transformer的做法相同）&lt;/p&gt;
&lt;p&gt;当然也可以用神经网络替代这个注意力得分的计算过程。但实践中还是上面的做法更好。&lt;/p&gt;
&lt;h1&gt;Lecture 8 - Self-Attention and Transformers&lt;/h1&gt;
&lt;p&gt;Self-Attention解决了RNN结构的诸多问题&lt;/p&gt;
&lt;p&gt;位置编码&lt;/p&gt;
&lt;p&gt;Transformer&lt;/p&gt;
&lt;p&gt;这里用矩阵计算的方式讲解真的非常必要！&lt;/p&gt;
&lt;p&gt;LayerNorm这里，下面同学们问的问题真好&lt;/p&gt;
&lt;p&gt;Transformer的缺点 n^2的复杂度&lt;/p&gt;
&lt;h1&gt;TODO: Lecture 9 - Pretraining&lt;/h1&gt;
&lt;h1&gt;Lecture 10 - Post-training&lt;/h1&gt;
&lt;h1&gt;Lecture 11 - Natural Language Generation / Benchmarking&lt;/h1&gt;
&lt;h1&gt;Lecture 12 - Neural Network&lt;/h1&gt;
&lt;h1&gt;Lecture 13 - Interfaces&lt;/h1&gt;
&lt;h1&gt;Lecture 14 - Reasoning and Agents&lt;/h1&gt;
&lt;h1&gt;Lecture 15 - After DPO&lt;/h1&gt;
&lt;h1&gt;Lecture 16 - ConvNets / Multimodal&lt;/h1&gt;
&lt;h1&gt;Lecture 18 NLP, Linguistics, Philosophy&lt;/h1&gt;
&lt;h1&gt;Assignment 1: Exploring Word Vectors&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;用到的库&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Gensim&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gensim提供了Doc2Vec模型（训练一个神经网络，可以将任意长度的文本映射到固定长度的向量上）
Gensim支持Word2Vec和FastText两种词向量模型。Word2Vec通过预测一个词周围的词来学习词向量，而FastText则进一步考虑了词内部的子结构（n-gram），对罕见词甚至未登录词（out-of-vocabulary words）有更好的表现。
Gensim还提供 LDA（此处指潜在迪利克雷分布，和线性判别分析无关）主题建模方法。LDA是一种用来识别大规模文档集中潜在主题的统计方法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NLTK&lt;/p&gt;
&lt;p&gt;常用于语料库下载、分词、词性标注、情感分析等简单任务&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;sklearn&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;主要模块&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;数据预处理&lt;/td&gt;
&lt;td&gt;清洗、标准化、编码类别变量&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sklearn.preprocessing&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型训练与评估&lt;/td&gt;
&lt;td&gt;构建分类/回归模型（决策树、SVM、逻辑回归等），评估性能&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sklearn.model_selection&lt;/code&gt;, &lt;code&gt;sklearn.linear_model&lt;/code&gt;, &lt;code&gt;sklearn.metrics&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;特征工程与降维&lt;/td&gt;
&lt;td&gt;特征选择、降维、构造新特征&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sklearn.decomposition&lt;/code&gt;, &lt;code&gt;sklearn.feature_selection&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;from sklearn.decomposition import TruncatedSVD
from sklearn.decomposition import PCA
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;PCA和SVD的学习推荐学习 lectures &lt;a href=&quot;https://web.stanford.edu/class/cs168/l/l7.pdf&quot;&gt;7&lt;/a&gt;, &lt;a href=&quot;http://theory.stanford.edu/~tim/s15/l/l8.pdf&quot;&gt;8&lt;/a&gt;, and &lt;a href=&quot;https://web.stanford.edu/class/cs168/l/l9.pdf&quot;&gt;9&lt;/a&gt; of CS168.&lt;/p&gt;
&lt;p&gt;CS168：&lt;a href=&quot;https://web.stanford.edu/class/cs168/index.html&quot;&gt;The Modern Algorithmic Toolbox&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;numpy的广播机制建议学习&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jakevdp.github.io/PythonDataScienceHandbook/02.05-computation-on-arrays-broadcasting.html&quot;&gt;Computation on Arrays: Broadcasting | Python Data Science Handbook&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;PART 1&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;nltk.download(&apos;reuters&apos;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;下载一个英文新闻语料库&lt;/p&gt;
&lt;p&gt;&lt;code&gt;from nltk.corpus import reuters&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;reuters.fileids(category)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;令category = crude，得到reuters中关于原油的新闻语料库即若干段新闻句子的list，处理后每个句子都是&amp;lt;START&amp;gt;开始&amp;lt;END&amp;gt;结束的单词（string）列表。&lt;/p&gt;
&lt;p&gt;利用python的set去重，得到完整词汇表。&lt;/p&gt;
&lt;p&gt;通过计数得到共现矩阵：np.zeros初始化M，建立word2Ind字典，遍历每个句子计数。&lt;/p&gt;
&lt;p&gt;SVD分解：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;svd = TruncatedSVD(n_components=k, n_iter=n_iters)
M_reduced=svd.fit_transform(M)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;这里TruncatedSVD使用一种叫做随机化SVD的算法加速计算，n_iters控制迭代次数&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样每个词都被表示为一个二维向量，随便把几个词画在散点图上（&lt;code&gt;plt.scatter&lt;/code&gt;）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;M_lengths = np.linalg.norm(M_reduced_co_occurrence, axis=1)
M_normalized = M_reduced_co_occurrence / M_lengths[:, np.newaxis] 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;M_lengths是(N, )的， M_lengths[:, np.newaxis是(N, 1)的（这里也可以用reshape），M_normalized是N x 2的，因此这里的计算利用了numpy的广播机制。&lt;/p&gt;
&lt;p&gt;注意不能直接除以M_lengths！否则会按行广播！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;PART 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import gensim.downloader as api
wv_from_bin = api.load(&quot;glove-wiki-gigaword-200&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下载并加载了一个训练好了的glove模型&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;wv_from_bin.vocab.keys()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;该用法被gensim 4.0.0弃用了，应该改为&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.index_to_key&lt;/code&gt;、&lt;code&gt;.key_to_index&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;进行和PART1一样的操作，得到散点图&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;wv_from_bin.most_similar(word)&lt;/code&gt;输出和给定word最相似的词，测试该glove模型的特点&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;wv_from_bin.distance(w1,w2)&lt;/code&gt;测试两个词的相似度，yes和correct的相似度大于no和correct的&lt;/p&gt;
&lt;p&gt;&lt;code&gt;most_similar&lt;/code&gt;还有更加实用的用法，例子：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pprint.pprint(wv_from_bin.most_similar(positive=[&apos;woman&apos;, &apos;king&apos;], negative=[&apos;man&apos;]))&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;Assignment 2: word2vec&lt;/h1&gt;
&lt;h2&gt;written&lt;/h2&gt;
&lt;p&gt;理解 the skip-gram word2vec algorithm&lt;/p&gt;
&lt;p&gt;(a) 说明 Naive Softmax loss 实际等价于交叉熵损失&lt;/p&gt;
&lt;p&gt;(b, c) 对特定的中心词和上下文词样本，计算损失函数，计算损失函数关于中心词向量的导数、关于窗口内每个词的上下文词向量导数&lt;/p&gt;
&lt;p&gt;(d) 计算sigmoid函数的导数。&lt;/p&gt;
&lt;p&gt;(e) 改用Negative Sampling loss，计算损失函数关于中心词向量的导数、关于窗口内每个词的上下文词向量导数&lt;/p&gt;
&lt;p&gt;(f) 对给定的中心词，窗口内损失函数求和，计算总损失函数关于窗口内每个词的上下文/中心词向量导数&lt;/p&gt;
&lt;h2&gt;coding&lt;/h2&gt;
&lt;p&gt;(a)&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;word2vec.py&lt;/code&gt;中&lt;/p&gt;
&lt;p&gt;实现&lt;code&gt;sigmoid()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;实现&lt;code&gt;naiveSoftmaxLossAndGradient&lt;/code&gt; ，需要计算loss、梯度&lt;/p&gt;
&lt;p&gt;实现&lt;code&gt;negSamplingLossAndGradient&lt;/code&gt;，需要计算loss、梯度&lt;/p&gt;
&lt;p&gt;实现&lt;code&gt;skipgram&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(b)&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;sgd.py&lt;/code&gt;中&lt;/p&gt;
&lt;p&gt;实现SGD&lt;/p&gt;
&lt;p&gt;(c)&lt;/p&gt;
&lt;p&gt;运行完整代码，在Stanford Sentiment Treebank (SST) dataset训练词向量&lt;/p&gt;
&lt;h1&gt;Assignment 3&lt;/h1&gt;
&lt;h1&gt;Final Project&lt;/h1&gt;
&lt;p&gt;默认项目是实现一个BERT，当然也可以自定义项目&lt;/p&gt;
&lt;p&gt;推荐学习Huggingface的transformers&lt;/p&gt;
</content:encoded></item><item><title>技术与理论-Transformer与大模型</title><link>https://blog.stivine.fun/posts/tech--theory-all-about-transformers/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/tech--theory-all-about-transformers/</guid><description>了不起的技术......</description><pubDate>Thu, 17 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Transformer&lt;/h2&gt;
&lt;p&gt;2017，Google&lt;/p&gt;
&lt;p&gt;Ashish Vaswani，印度裔的&lt;/p&gt;
&lt;p&gt;512维，8个heads，每个64维&lt;/p&gt;
&lt;h3&gt;位置编码相关&lt;/h3&gt;
&lt;p&gt;理解Position Embedding的经典例子：”你爱我“ 和 ”我爱你“&lt;/p&gt;
&lt;p&gt;为序列中的每个位置生成一个独特的向量&lt;/p&gt;
&lt;p&gt;位置编码的设计原则&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;唯一性&lt;/strong&gt;：每个位置应有唯一的编码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相对位置的感知&lt;/strong&gt;：编码应能反映词与词之间的相对距离（如相邻词的编码应相似）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性&lt;/strong&gt;：支持比训练时更长的序列（如Transformer原论文使用固定公式而非可学习参数）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原论文使用&lt;strong&gt;正弦和余弦函数的组合&lt;/strong&gt;生成位置编码：&lt;/p&gt;
&lt;p&gt;$\text{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)$&lt;/p&gt;
&lt;p&gt;$\text{PE}(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)$&lt;/p&gt;
&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pos&lt;/code&gt;：词的位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i&lt;/code&gt;：编码向量的维度索引&lt;/li&gt;
&lt;li&gt;$d_{\text{model}}$：模型的隐藏层维度（与词向量维度相同，=512）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了符合那几个原则，这种编码还有几个妙处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;捕捉了相对位置的线性关系：对任意固定的偏移量 k，存在线性变换矩阵 M，使得：
$\text{PE}(pos + k) = M \cdot \text{PE}(pos)$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;相邻词的距离为定值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多频率编码：不同维度对应不同波长，能捕捉不同粒度的位置信息（高频维度区分近距离，低频维度区分远距离）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;TODO：Transformer-XL、旋转位置编码（RoPE）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;基于transformer的预训练模型可以分为4类&lt;/p&gt;
&lt;p&gt;编码器的&lt;/p&gt;
&lt;p&gt;解码器的&lt;/p&gt;
&lt;p&gt;seq2seq的&lt;/p&gt;
&lt;p&gt;文本-视觉的&lt;/p&gt;
&lt;h3&gt;GPT&lt;/h3&gt;
&lt;p&gt;Generative Pre-trained Transformer（GPT1），2018&lt;/p&gt;
&lt;p&gt;Decoder-Only&lt;/p&gt;
&lt;p&gt;后续发展 2020-GPT3，2022-ChatGPT，2023-GPT4，2024-GPTo1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GPT3 small参数的计算？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;3b1b的视频是这么算的：&lt;/p&gt;
&lt;p&gt;50257 tokens 每个词嵌入12288维 50257x12288&lt;/p&gt;
&lt;p&gt;解嵌入 Matrix  50257x12288&lt;/p&gt;
&lt;p&gt;温度即对softmax的调整&lt;/p&gt;
&lt;p&gt;W_Q W_K 128x12288 = 1572864&lt;/p&gt;
&lt;p&gt;Q K  128xL&lt;/p&gt;
&lt;p&gt;QK^T LxL&lt;/p&gt;
&lt;p&gt;W_V 12288x12288 实际并非如此多参数，是一个12288x128矩阵乘上一个128x12288的矩阵&lt;/p&gt;
&lt;p&gt;V 12288xL&lt;/p&gt;
&lt;p&gt;V·softmax(Q^KT) 12288xL&lt;/p&gt;
&lt;p&gt;GPT3 96头 96层 总参数量12288x128x4x96x96=603,979,776&lt;/p&gt;
&lt;p&gt;此外还要计算MLP的参数量，MLP每次先升维到49152再降回去&lt;/p&gt;
&lt;p&gt;故MLP参数量（不算偏置项、Layer Norm）12288x49152x2x96&lt;/p&gt;
&lt;p&gt;最终合起来是1750亿参数量&lt;/p&gt;
&lt;p&gt;多头自注意力机制那里，&lt;/p&gt;
&lt;p&gt;注意力机制 &lt;strong&gt;输入&lt;/strong&gt;: 向量 $h_i$，形状为 $(n_{ctx}, d_{model})$ &lt;strong&gt;输出&lt;/strong&gt;: 向量 $h_{i+1}$ &lt;strong&gt;QKV 参数&lt;/strong&gt;: 变换为 QKV 的参数矩阵，尺寸都为 $(d_{model}, d_{head})$，含义为（向量维度，注意力头维度） 有 $n_{head}$ 个 $QKV$ 头 转换到 $d_{head}$ 维度后，还有一个矩阵负责转换为 $d_{model}$：这个转换矩阵的维度为 $(n_{head} * d_{head}, d_{model})$ &lt;strong&gt;总参数量&lt;/strong&gt;: 一般而言，$n_{head} * d_{head} = d_{model}$，所以 $4n_{head} d_{head} d_{model} = 4d_{model}^2$&lt;/p&gt;
&lt;p&gt;GPT 的预训练任务：自回归 / 因果语言建模（Auto-regressive / Causal Language Modeling, CLM）&lt;/p&gt;
&lt;p&gt;模型在预训练阶段的目标是： 给定前面的所有词，预测下一个词&lt;/p&gt;
&lt;h3&gt;BERT&lt;/h3&gt;
&lt;p&gt;Bidirectional Encoder Representation from Transformers，2018，Google&lt;/p&gt;
&lt;p&gt;Encoder-Only，有强大的理解能力，但无法自主生成文本。&lt;/p&gt;
&lt;p&gt;作者分别用12层（d=768）和24层（d=1024）Transformer Encoder组装了两套BERT模型，两套模型的参数总数分别为110M和340M。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BERT-Base的参数计算&lt;/strong&gt;（未计算LayerNorm参数）&lt;/p&gt;
&lt;p&gt;总参数量：约110M（1.1 亿）&lt;/p&gt;
&lt;p&gt;1️⃣ Embedding 层（Token + Position + Segment）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Token Embeddings:  30522 x 768&lt;/li&gt;
&lt;li&gt;Position Embeddings: 512 x 768&lt;/li&gt;
&lt;li&gt;Segment Embeddings: 2 x 768（只在做NSP的时候用）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;词表大小（30522）&lt;/li&gt;
&lt;li&gt;隐藏层维度（768）&lt;/li&gt;
&lt;li&gt;最大序列长度（512）&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;总计 23.8M&lt;/p&gt;
&lt;p&gt;2️⃣ Transformer 编码器层（共 12 层）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Query、Key、Value 的权重矩阵：3 x 768 x 768&lt;/li&gt;
&lt;li&gt;输出投影矩阵：768 × 768&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前馈神经网络（Feed-Forward Network, FFN），也是先升维再降维，768x3072x2&lt;/p&gt;
&lt;p&gt;总计 (4x768x768+768x3072x2)x12 = 84.9M&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 BERT 中，多头自注意力机制被用来允许模型在不同的表示子空间中并行地执行注意力操作。采用隐藏层维度 (H) 分割：对于每个注意力头，输入向量首先会被分割成较小的块（即降维），每个头处理输入向量的一个子集。例如，在 BERT-Base 中，隐藏层大小为 768 维，如果有 12 个注意力头，则每个头处理的是 (768 / 12 = 64) 维的子向量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3️⃣ Pooler 层（用于 [CLS] 向量输出）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个全连接层 768 × 768 = 0.59M&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;BERT是用了Transformer的encoder侧的网络，encoder中的Self-attention机制在编码一个token的时候同时利用了其上下文的token，此即为双向的体现，而并非想Bi-LSTM那样把句子倒序输入一遍。在BERT之前是GPT，GPT使用的是Transformer的decoder侧的网络，GPT是一个单向语言模型的预训练过程，更适用于文本生成，通过前文去预测当前的字。（就是那个mask与否的问题）&lt;/p&gt;
&lt;p&gt;Embedding由三种Embedding直接求和而成：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pica.zhimg.com/v2-86cc5ff7f5295dbf9587371034ba7abe_1440w.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a&gt;Token Embeddings&lt;/a&gt;是词向量，第一个单词是CLS标志，其对应的特征用于之后的分类任务&lt;/p&gt;
&lt;p&gt;（GPT uses a sentence separator ([SEP]) and classifier token ([CLS]) which are only introduced at fine-tuning time; BERT learns [SEP], [CLS] and sentence A/B embeddings during pre-training）&lt;/p&gt;
&lt;p&gt;playing 会切割成 play 和 ##ing，这叫做WordPiece tokenization。解决未登录词的常见方法，还使得Bert 在处理英文文本时只需要 30522 个词。&lt;/p&gt;
&lt;p&gt;Token Embeddings 层会将每个词转换成 768 维向量，例子中 11 个Token 会被转换成一个 (11, 768) 的矩阵或 (1, 11, 768) 的张量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a&gt;Segment Embeddings&lt;/a&gt;用来区别两种句子，预训练除了LM，还需要做判断两个句子先后顺序之类的的分类任务。&lt;/p&gt;
&lt;p&gt;前一个句子的每个token都用0表示，后一个句子的每个token都用1表示。如”[CLS] my dog is cute [SEP] he likes play ##ing [SEP]“ 表示成”0 0 0 0 0 0 1 1 1 1 1“。 如果输入仅仅只有一个句子，那么它的segment embedding就是全0。 这也是一个(11, 768)维的张量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a&gt;Position Embeddings&lt;/a&gt;和之前文章中的Transformer不一样，不是三角函数计算的而是学习出来的。&lt;/p&gt;
&lt;p&gt;BERT 中处理的最长序列是 512 个 Token，长度超过 512 会被截取，BERT 在各个位置上学习一个向量来表示序列顺序的信息编码进来，这意味着 Position Embeddings 实际上是一个 (512, 768) 的 lookup 表，表第一行是代表第一个序列的每个位置，第二行代表序列第二个位置。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下为BERT使用的预训练任务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Task 1: Masked Language Model（MLM）&lt;/p&gt;
&lt;p&gt;在将单词序列输入给 BERT 之前，每个序列中有 15％ 的单词被 [MASK] token 替换。训练其对应输出是原单词。&lt;/p&gt;
&lt;p&gt;【后面有人提出SpanBERT，预测一个范围内的被MASK的文本】&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Task 2: Next Sentence Prediction（NSP）&lt;/p&gt;
&lt;p&gt;将两个句子A和B链接起来，预测原始文本中句子B是否排在句子A之后。&lt;/p&gt;
&lt;p&gt;具体训练的时候，50％的输入对在原始文档中是前后关系，另外50％中是从语料库中随机组成的，并且是与第一句断开的。最后用一个简单的分类层将[CLS]标记的输出变换为 2×1 形状的向量。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在训练BERT模型时，Masked LM和 Next Sentence Prediction 是一起训练的，目标就是要最小化两种策略的组合损失函数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这个Task 2后续应用不多&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;BERT的微调（Fine-tunning）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对于不同的下游任务，我们仅需要对BERT不同位置的输出进行处理即可，或者直接将BERT不同位置的输出直接输入到下游模型当中。具体如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于情感分析等单句分类任务，可以直接输入单个句子（不需要[SEP]分隔双句），将[CLS]的输出直接输入到分类器进行分类&lt;/li&gt;
&lt;li&gt;对于句子对任务（句子关系判断任务），需要用[SEP]分隔两个句子输入到模型中，然后同样仅须将[CLS]的输出送到分类器进行分类&lt;/li&gt;
&lt;li&gt;对于问答任务，将问题与答案拼接输入到BERT模型中，然后接两个分类器分别输出答案开始和结束的位置&lt;/li&gt;
&lt;li&gt;对于命名实体识别任务，对每个位置的输出进行分类即可，如果将每个位置的输出作为特征输入到CRF将取得更好的效果。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;TODO: CRF 条件随机场&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;对于常规分类任务，在 Transformer 的输出之上加一个分类层&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;T5&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Transfer Text-to-Text Transformer&lt;/strong&gt; （2019）&lt;/p&gt;
&lt;p&gt;Encoder-Decoder&lt;/p&gt;
&lt;p&gt;把所有任务统一为text to text的任务&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prefix language modeling（前缀语言建模）：输入部分文本，目标是预测剩余的文本。例如，输入“Thank you for inviting”，目标是预测“me to your party last week”。&lt;/li&gt;
&lt;li&gt;BERT-style（BERT风格）：MLM&lt;/li&gt;
&lt;li&gt;Deshuffling（解乱序）：输入被打乱顺序的文本，目标是恢复原始顺序。例如，输入“party me for your to . last fun you inviting week Thank”，目标是恢复为“Thank you for inviting me to your party last week”。&lt;/li&gt;
&lt;li&gt;I.i.d. noise, mask tokens（独立同分布噪声，掩码词）：随机掩码&lt;strong&gt;一些&lt;/strong&gt;词，然后预测这些被掩码的词。例如，输入“Thank you &amp;lt;M&amp;gt; &amp;lt;M&amp;gt; me to your party &amp;lt;M&amp;gt; week .”，目标是预测被掩码的词。&lt;/li&gt;
&lt;li&gt;I.i.d. noise, replace spans（独立同分布噪声，替换片段）：随机替换一些片段，然后预测这些被替换的片段。例如，输入“Thank you &amp;lt;X&amp;gt; me to your party &amp;lt;Y&amp;gt; week .”，目标是预测被替换的片段“&amp;lt;X&amp;gt; for inviting &amp;lt;Y&amp;gt; last &amp;lt;Z&amp;gt;”。&lt;/li&gt;
&lt;li&gt;I.i.d. noise, drop tokens（独立同分布噪声，删除词）：随机删除一些词，然后预测这些被删除的词。例如，输入“Thank you me to your party week .”，目标是预测被删除的词“for inviting last”。&lt;/li&gt;
&lt;li&gt;Random spans（随机片段）：随机选择一些片段进行掩码或替换，然后预测这些被处理的片段。例如，输入“Thank you &amp;lt;X&amp;gt; to &amp;lt;Y&amp;gt; week .”，目标是预测被处理的片段“&amp;lt;X&amp;gt; for inviting me &amp;lt;Y&amp;gt; your party last &amp;lt;Z&amp;gt;”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;【T5在机器翻译效果不好？训练集问题】&lt;/p&gt;
&lt;h3&gt;BART&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Bidirectional and Auto-Regressive Transformers（2019，facebook  AI）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;建立在标准的seq2seq Transformer model的基础之上，旨在解决一系列文本生成任务。&lt;/p&gt;
&lt;p&gt;不同于BERT仅关注理解型任务，BART设计了一个去噪自编码器，该编码器首先对输入文本进行部分破坏（例如删除、替换、重新排序等），然后训练模型尝试恢复原始文本。&lt;/p&gt;
&lt;h3&gt;XLNet&lt;/h3&gt;
&lt;p&gt;Google Brain, CMU（卡内基梅隆大学） 2019&lt;/p&gt;
&lt;p&gt;也是预训练语言模型，旨在结合自回归（AR）和自编码（AE）模型的优势，突破BERT等模型的局限性。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;BERT属于自编码器(AE)语言模型。AE语言模型的优点是它可以在向前和向后两个方向上看到上下文。但是AE语言模型也有其不足之处。它在预训练中使用了&lt;code&gt;[MASK]&lt;/code&gt;，但是这种人为的符号在finetune的时候在实际数据中时没有的，导致了pretraining — finetune的不一致。另一个缺点是它假设预测的(屏蔽的)tokens是相互独立的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其核心创新包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;重排列语言建模（Permutation Language Modeling, PLM）：通过随机排列输入序列的顺序，使模型在自回归框架下捕获双向上下文信息，避免了BERT中因掩码（Mask）导致的预训练与微调不一致问题。&lt;/p&gt;
&lt;p&gt;核心机制：对输入序列的所有可能排列顺序进行采样，模型根据当前排列顺序预测每个位置的词。例如，序列&lt;code&gt;[x1, x2, x3, x4]&lt;/code&gt;可能被排列为&lt;code&gt;[x3, x2, x4, x1]&lt;/code&gt;，预测x2时仅能看到x3，而预测x1时能看到所有其他词。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;双流自注意力机制（Two-Stream Self-Attention）：分离内容流（Content Stream）和查询流（Query Stream），确保模型在预测时既能利用上下文信息，又不泄露当前位置的内容。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;内容流：包含词的内容和位置信息，用于建模上下文，与传统Transformer类似。&lt;/li&gt;
&lt;li&gt;查询流：仅包含位置信息，用于预测当前词时避免信息泄漏。例如，预测x3时，查询流仅利用其位置编码生成注意力权重，确保模型无法直接访问x3的内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Transformer-XL架构：引入长文本建模能力，通过分段循环机制（Segment Recurrence）和相对位置编码（Relative Positional Encoding）处理长序列依赖。&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;分段循环机制：缓存前一段的隐状态，解决长文本依赖的截断问题，使模型能跨段建模（如文档级任务）。&lt;/li&gt;
&lt;li&gt;相对位置编码：用相对距离替代绝对位置编码，增强模型对位置关系的鲁棒性。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;但这个模型后续评价不佳。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;FFN相关&lt;/h3&gt;
&lt;p&gt;原始的Transformer结构中，每一层包含multi-head self-attention block (MHSA) 和一个FFN。FFN的本质就是一个token-wise的升维-过激活-降回原来维度的MLP。其输入是MHSA输出的(n, d)维的序列表示x，其中n为序列中的token数目，d为隐藏层维度，设中间层维度为d&apos;（通常大于d，常见实现中d&apos; = 4d），其权重为升维矩阵W₁ ∈ ℝᵈˣᵈ&apos; 和降维矩阵W₂ ∈ ℝᵈ&apos;ˣᵈ，激活函数为σ。为简便起见忽略bias项。则FFN层的前向过程为：&lt;/p&gt;
&lt;p&gt;$\text{FFN}(x) = \sigma(xW_1)W_2 $&lt;/p&gt;
&lt;p&gt;原始的Transformer和BERT模型中取$σ=GELU$，即$ x \cdot \psi(x)$，其中ψ为标准正态分布的密度&lt;/p&gt;
&lt;h2&gt;Benchmark: GLUE&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;（General Language Understanding Evaluation，2018）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用于评估自然语言处理（NLP）模型通用语言理解能力的BenchMark&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GLUE 子任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(1) Corpus of Linguistic Acceptability (CoLA)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：判断句子是否符合语法（二元分类）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：Matthew’s Correlation Coefficient（尤其适合用来评估不平衡数据集上的二分类器性能）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(2) Stanford Sentiment Treebank (SST-2)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：句子情感分类（正面/负面）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(3) Microsoft Research Paraphrase Corpus (MRPC)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：判断句子对是否语义等价（释义检测）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）和 F1 分数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(4) Quora Question Pairs (QQP)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：判断两个问题是否语义相同（重复问题检测）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）和 F1 分数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(5) Semantic Textual Similarity Benchmark (STS-B)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：评估句子对的语义相似度（1-5 分回归任务）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：Pearson/Spearman 相关系数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(6) Multi-Genre Natural Language Inference (MNLI)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：判断前提（premise）与假设（hypothesis）的关系（蕴含/矛盾/中立）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变体&lt;/strong&gt;：MNLI-m（匹配领域）、MNLI-mm（不匹配领域）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(7) Question-Answering NLI (QNLI)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：判断问题与文本段落是否包含答案（二元分类，改编自 SQuAD）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(8) Recognizing Textual Entailment (RTE)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：二分类版本的文本蕴含任务（判断前提是否蕴含假设）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(9) Winograd NLI (WNLI)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;/strong&gt;：共指消解任务（判断代词指代是否合理）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标&lt;/strong&gt;：准确率（Accuracy）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;大模型中的tokenizer&lt;/h2&gt;
&lt;p&gt;分词器&lt;/p&gt;
&lt;p&gt;按照不同尺度可以分为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;word base&lt;/li&gt;
&lt;li&gt;character base&lt;/li&gt;
&lt;li&gt;subword tokenization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下均为subword tokenization：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;BPE（Byte Pair Encoding）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GPT使用的分词方法 不断合并&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;WordPiece&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;BERT使用的分词方法。和BPE类似也是每次从词表中选出两个子词合并成新的子词，但是是基于概率生成新的subword而不是下一最高频字节对。BPE选择&lt;strong&gt;频数&lt;/strong&gt;最高的相邻子词合并，而WordPiece选择使得&lt;strong&gt;语言模型概率&lt;/strong&gt;最大的相邻子词加入词表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unigram LM&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SentencePiece&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;RLHF&lt;/h2&gt;
&lt;p&gt;基于人类反馈的强化学习（Reinforcement Learning with Human Feedback，简称 RLHF）&lt;/p&gt;
&lt;p&gt;从2016年开始，随着深度学习技术的发展以及大规模数据标注能力的提升，逆强化学习（Inverse Reinforcement Learning, IRL）与深度强化学习开始结合。2017年，DeepMind发表了《Deep Reinforcement Learning from Human Preferences》这篇论文，展示了如何从人类偏好中学习复杂游戏策略，这是RLHF的一个重要里程碑。&lt;/p&gt;
&lt;h3&gt;InstructGPT中的RLHF&lt;/h3&gt;
&lt;p&gt;自2021年起，OpenAI的InstructGPT和ChatGPT等项目将RLHF推向主流，证明了其在语言模型对齐中的有效性。通过这一方法，模型能够更好地理解和回应人类的需求和偏好。&lt;/p&gt;
&lt;p&gt;其训练流程通常包括三个阶段：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;监督微调（Supervised Fine-tuning，SFT）&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;雇佣40名标注人员完成prompt的标注。
此时的SFT模型在遵循指令/对话方面已经优于 GPT-3，但不一定符合人类偏好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;奖励模型训练（Reward Model Training）&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;模型生成出若干结果（可以通过beam search等方法），通过人工为其排序，例如D&amp;gt;C&amp;gt;A=B，可以得到标注的排序pair；基于标注的排序结果，训练一个Reward Model（输出打分）&lt;/p&gt;
&lt;p&gt;具体来说，对多个排序结果，两两组合，形成多个训练数据对。RM模型接受一个输入，给出评价回答质量的分数。这样，对于一对训练数据，调节参数使得高质量回答的打分比低质量的打分要高。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;强化学习微调（PPO）&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用强化学习（如PPO算法）让模型在生成回答时最大化奖励模型的打分，从而使得模型输出更符合人类喜好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;PEFT技术&lt;/h2&gt;
&lt;p&gt;参数高效微调（Parameter-Efficient Fine-tuning, PEFT）&lt;/p&gt;
&lt;p&gt;适用于大语言模型（LLMs）的适配任务。&lt;/p&gt;
&lt;h3&gt;Adapter-Tuning&lt;/h3&gt;
&lt;p&gt;最早是2019年提出&lt;/p&gt;
&lt;p&gt;固定Transformer的全部参数，然后在Transformer的每一个Block里嵌入一些新初始化的Adapter Network。&lt;/p&gt;
&lt;h3&gt;Prefix-Tuning&lt;/h3&gt;
&lt;p&gt;最早2021提出&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Xiang Lisa Li and Percy Liang. 2021. Prefix-tuning:
Optimizing continuous prompts for generation. In
ACL/IJCNLP 2021, pages 4582–4597. Association
for Computational Linguistics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;保持预训练语言模型参数固定（frozen），而只需要在task-specific vector（称为prefix）上进行优化。&lt;/p&gt;
&lt;p&gt;这可以看作一种soft prompt。和固定模板文字的hard prompt相对。&lt;/p&gt;
&lt;p&gt;以下为论文内容&lt;/p&gt;
&lt;p&gt;微调是利用大型预训练语言模型执行下游任务的事实上的方法。然而，它修改了所有语言模型参数，因此需要为每个任务存储一个完整的副本。在本文中，我们提出了前缀微调，这是一种轻量级的替代微调方法，适用于自然语言生成任务，它保持语言模型参数不变，但优化了一个小的连续任务特定向量（称为前缀）。前缀微调从提示中汲取灵感，允许后续的标记关注这个前缀，就像它是“虚拟标记”一样。我们将前缀微调应用于GPT-2进行表格到文本的生成，并应用于BART进行摘要。我们发现，通过&lt;strong&gt;仅学习0.1%的参数&lt;/strong&gt;，前缀微调在完整数据设置下获得可比的性能，在低数据设置下优于微调，并且对训练期间未见过的主题示例具有更好的外推能力。&lt;/p&gt;
&lt;h4&gt;1 引言&lt;/h4&gt;
&lt;p&gt;微调是使用大型预训练语言模型（LMs）执行下游任务（例如，摘要）的主流范式，但它需要更新和存储LM的所有参数。因此，为了构建和部署依赖于大型预训练LM的NLP系统，目前需要为每个任务存储LM参数的一个修改后的副本。鉴于当前LM的巨大规模，这可能非常昂贵；例如，GPT-2有7.74亿个参数，而GPT-3有1750亿个参数。解决这个问题的一种自然方法是轻量级微调，它冻结大部分预训练参数，并用小型可训练模块增强模型。例如，适配器微调（Rebuffi等人，2017；Houlsby等人，2019）在预训练语言模型的层之间插入额外的任务特定层。适配器微调在自然语言理解和生成基准上表现出有希望的性能，在仅添加大约2-4%的任务特定参数的情况下达到与微调相当的性能（Houlsby等人，2019；Lin等人，2020）。&lt;/p&gt;
&lt;p&gt;在极端情况下，GPT-3（Brown等人，2020）可以在没有任何任务特定微调的情况下部署。相反，用户在任务输入前附加一个自然语言任务指令（例如，TL;DR用于摘要）和一些示例；然后从LM生成输出。这种方法被称为上下文学习或提示。&lt;/p&gt;
&lt;p&gt;在本文中，我们提出了前缀微调，这是一种轻量级的替代微调方法，适用于自然语言生成（NLG）任务，灵感来自提示。考虑生成文本描述的任务描述数据表的任务，如图1所示，其中任务输入是一个线性化的表格（例如，“name: Starbucks | type: coffee shop”），输出是一个文本描述（例如，“Starbucks serves coffee.”）。前缀微调在输入前附加一系列连续的任务特定向量，我们称之为前缀，在图1（底部）中用红色块表示。对于后续的标记，Transformer可以像关注一系列“虚拟标记”一样关注前缀，但与提示不同的是，前缀完全由自由参数组成，这些参数不对应于实际的标记。与图1（顶部）中的微调相比，后者更新所有Transformer参数并因此需要为每个任务存储一个调整后的模型副本，前缀微调仅优化前缀。因此，我们只需要存储一个大型Transformer的一个副本和一个学习到的任务特定前缀，从而为每个额外的任务带来非常小的开销（例如，表格到文本任务的250K参数）。&lt;/p&gt;
&lt;p&gt;与微调相比，前缀微调是模块化的：我们训练一个上游前缀来引导下游LM，而下游LM保持不变。因此，单个LM可以同时支持许多任务。在个性化上下文中，任务对应于不同的用户（Shokri和Shmatikov，2015；McMahan等人，2016），我们可以为每个用户训练一个单独的前缀，只基于该用户的数据，从而避免数据交叉污染。此外，基于前缀的架构使我们能够在单个批次中处理来自多个用户/任务的示例，这是其他轻量级微调方法无法实现的。&lt;/p&gt;
&lt;p&gt;我们在表格到文本生成任务上使用GPT-2评估前缀微调，并在摘要任务上使用BART进行抽象总结。在存储方面，前缀微调比微调少存储1000倍的参数。在性能方面，当在完整数据集上训练时，前缀微调和微调在表格到文本任务上表现相当（§6.1），而前缀微调在摘要任务上表现出轻微的性能下降（§6.2）。在低数据设置下，前缀微调在两个任务上平均优于微调（§6.3）。前缀微调还更好地推广到具有未见过主题的表格（用于表格到文本）和文章（用于摘要）（§6.4）。&lt;/p&gt;
&lt;h4&gt;2 相关工作&lt;/h4&gt;
&lt;p&gt;等，2020；Zhu等人，2020；Liu等人，2020）。在本文中，我们专注于使用GPT-2的表格到文本任务和使用BART的摘要任务，但前缀微调可以应用于其他生成任务和预训练模型。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;轻量级微调&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;轻量级微调冻结了大部分预训练参数，并用小型可训练模块修改预训练模型。关键挑战是识别模块的高性能架构和要调整的预训练参数子集。一条研究路线考虑移除参数：通过在模型参数上训练一个二进制掩码来消除一些模型权重（Zhao等人，2020；Radiya-Dixit和Wang，2020）。另一条研究路线考虑插入参数。例如，Zhang等人（2020a）训练了一个“侧”网络，该网络通过求和与预训练模型融合；适配器微调在预训练LM的每一层之间插入任务特定层（适配器）（Houlsby等人，2019；Lin等人，2020；Rebuffi等人，2017；Pfeiffer等人，2020）。与这条工作路线相比，该路线调整了大约3.6%的LM参数，我们的方法在保持相当性能的同时，将任务特定参数进一步减少了30倍，仅调整了0.1%。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;提示意味着在任务输入前附加指令和一些示例，并从LM生成输出。GPT-3（Brown等人，2020）使用手动设计的提示来适应其生成以应对不同任务，这种框架被称为上下文学习。然而，由于Transformer只能基于有限长度的上下文进行条件化（例如，GPT-3的2048个标记），因此上下文学习无法充分利用比上下文窗口更长的训练集。Sun和Lai（2020）还通过关键词提示来控制生成句子的情感或主题。在自然语言理解任务中，提示工程在先前的工作中已被用于像BERT和RoBERTa这样的模型（Liu等人，2019；Jiang等人，2020；Schick和Schütze，2020）。例如，AutoPrompt（Shin等人，2020）搜索一系列离散的触发词，并将其与每个输入连接起来，以从掩码LM中引出情感或事实知识。与AutoPrompt不同，我们的方法优化了连续的前缀，这些前缀更具表现力（§7.2）；此外，我们专注于语言生成任务。&lt;/p&gt;
&lt;p&gt;连续向量已被用于引导语言模型；例如，Subramani等人（2020）表明，预训练的LSTM语言模型可以通过为每个句子优化一个连续向量来重建任意句子，使向量输入特定。相比之下，前缀微调优化了一个适用于该任务所有实例的任务特定前缀。因此，与之前的应用仅限于句子重构的工作不同，前缀微调可以应用于NLG任务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可控生成&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;可控生成旨在引导预训练的语言模型以匹配句子级别的属性（例如，关于体育的积极情感或主题）。这种控制可以在训练时发生：Keskar等人（2019）预训练语言模型（CTRL）以基于元数据如关键词或URL进行条件化。此外，控制可以在解码时通过加权解码（GeDi，Krause等人，2020）或迭代更新过去的激活（PPLM，Dathathri等人，2020）来实现。然而，没有直接的方法将这些可控生成技术应用于生成内容的细粒度控制，这是表格到文本和摘要等任务所要求的。&lt;/p&gt;
&lt;h4&gt;3 问题陈述&lt;/h4&gt;
&lt;p&gt;考虑一个条件生成任务，其中输入是一个上下文x，输出y是一系列标记。我们关注两个任务，如图2（右）所示：在表格到文本任务中，x对应于线性化的数据表，y是文本描述；在摘要任务中，x是一篇文章，y是一个简短的摘要。&lt;/p&gt;
&lt;h5&gt;3.1 自回归LM&lt;/h5&gt;
&lt;p&gt;假设我们有一个基于Transformer架构（Vaswani等人，2017）的自回归语言模型pφ(y | x)（例如，GPT-2；Radford等人2019）并由φ参数化。如图2（顶部）所示，令z = [x; y]为x和y的连接；令X_idx表示对应于x的索引序列，Y_idx表示对应于y的索引序列。&lt;/p&gt;
&lt;p&gt;时间步i的激活是h_i ∈ ℝ^d，其中h_i = [h_i^(1); ... ; h_i^(n)]是此时间步所有激活层的连接，而h_i^(j)是时间步i时第j个Transformer层的激活。1&lt;/p&gt;
&lt;p&gt;自回归Transformer模型将h_i计算为z_i和其左上下文中的过去激活的函数，如下所示：&lt;/p&gt;
&lt;p&gt;$$h_i = LM_φ(z_i, h_&amp;lt;i), (1)$$&lt;/p&gt;
&lt;p&gt;其中最后一层的h_i用于计算下一个标记的分布：$p_φ(z_i+1 | h_≤i) = softmax(W_φ h_i^{n})$，W_φ是一个预训练矩阵，将h_i^(n)映射到词汇表上的对数几率。&lt;/p&gt;
&lt;h5&gt;3.2 编码器-解码器架构&lt;/h5&gt;
&lt;p&gt;我们还可以使用编码器-解码器架构（例如，BART；Lewis等人，2020）来建模p_φ(y | x)，其中x由双向编码器编码，解码器自回归地预测y（基于编码的x及其左上下文）。我们使用相同的索引和激活符号，如图2（底部）所示。对于所有i ∈ X_idx，h_i由双向Transformer编码器计算；对于所有i ∈ Y_idx，h_i由自回归解码器使用相同的方程(1)计算。&lt;/p&gt;
&lt;h5&gt;3.3 方法：微调&lt;/h5&gt;
&lt;p&gt;在微调框架中，我们用预训练参数φ初始化。这里p_φ是一个可训练的语言模型分布，我们对以下对数似然目标进行梯度更新：&lt;/p&gt;
&lt;p&gt;max_φ log p_φ(y | x) = ∑&lt;em&gt;{i∈Y_idx} log p_φ(z_i | h&lt;/em&gt;&amp;lt;i). (2)&lt;/p&gt;
&lt;h4&gt;4 前缀微调&lt;/h4&gt;
&lt;p&gt;我们提出前缀微调作为条件生成任务微调的替代方案。我们在§4.1中首先提供直觉，然后在§4.2中正式定义我们的方法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;h_i^{n}由一个键值对组成。在GPT-2中，每个键和值的维度都是1024。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;4.1 直觉&lt;/h5&gt;
&lt;p&gt;基于提示的直觉，我们认为拥有适当的上下文可以在不改变其参数的情况下引导LM。例如，如果我们希望LM生成一个词（例如，Obama），我们可以在其前附加常见的搭配作为上下文（例如，Barack），LM将为所需的词分配更高的概率。将这种直觉扩展到生成单个词或句子之外，我们希望找到一个能够引导LM解决NLG任务的上下文。直观上，上下文可以通过指导从x中提取什么来影响x的编码；并通过引导下一个标记分布来影响y的生成。然而，是否存在这样的上下文并不明显。自然语言任务指令（例如，“用一句话总结以下表格”）可能会引导专家注释者解决任务，但对于大多数预训练LM来说可能失败。[2] 数据驱动的离散指令优化可能会有所帮助，但离散优化在计算上具有挑战性。&lt;/p&gt;
&lt;p&gt;与其在离散标记上进行优化，我们可以将指令优化为连续的词嵌入，其效果将向上传播到所有Transformer激活层，并向右传播到后续标记。这比需要匹配真实词嵌入的离散提示更具表现力。同时，这不如干预所有激活层（§7.2）那么具有表现力，后者避免了长程依赖并包含更多可调参数。因此，前缀微调优化了前缀的所有层。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[2] 这里指预训练模型可能无法直接理解或有效响应自然语言指令，需要进一步的调整或优化才能达到预期效果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;4.2 方法&lt;/h5&gt;
&lt;p&gt;前缀微调为自回归LM添加一个前缀以获得$z = [PREFIX; x; y]$，或者为编码器和解码器都添加前缀以获得$z = [PREFIX; x; PREFIX&apos;; y]$，如图2所示。这里，$P_{idx}$表示前缀索引序列，我们用$|P_{idx}|$表示前缀的长度。&lt;/p&gt;
&lt;p&gt;我们遵循方程(1)中的递归关系，只是前缀是自由参数。前缀微调初始化一个可训练矩阵$P_θ$（由θ参数化），其维度为$|P_{idx}| × dim(h_i)$，用于存储前缀参数。
$$
h_i =
\begin{cases}
P_θ[i, :], &amp;amp; \text{if } i ∈ P_{idx}, \
LM_φ(z_i, h_{&amp;lt;i}), &amp;amp; \text{otherwise}.
\end{cases}
$$
训练目标与方程(2)相同，但可训练参数集发生变化：语言模型参数φ固定不变，前缀参数θ是唯一的可训练参数。&lt;/p&gt;
&lt;p&gt;在这里，$h_i$（对于所有$i$）是可训练的$P_θ$的函数。当$i ∈ P_idx$时，这是显而易见的，因为$h_i$直接从$P_θ$复制。当$i ∉ P_idx$时，$h_i$仍然依赖于$P_θ$，因为前缀激活始终在左上下文中，并因此会影响其右侧的任何激活。&lt;/p&gt;
&lt;h5&gt;4.3 P_θ的参数化&lt;/h5&gt;
&lt;p&gt;经验上，直接更新$P_θ$参数会导致优化不稳定和性能略有下降。[3] 因此，我们重新参数化矩阵$P_θ[i, :] = MLP_θ(P&apos;_θ[i, :])$，通过较小的矩阵（P&apos;_θ）与大型前馈神经网络（MLP_θ）组合而成。注意P_θ和P&apos;_θ具有相同的行数...&lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;[3] 这里指直接更新前缀参数可能导致训练过程不稳定，影响模型的最终性能，需要采用更稳健的方法来优化这些参数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;BitFit&lt;/h3&gt;
&lt;p&gt;最早2021年提出，ACL 2022&lt;/p&gt;
&lt;p&gt;非常简单，其不需要对预训练模型做任何改动，只需要指定神经网络中的偏向（Bias）为可训练参数即可&lt;/p&gt;
&lt;h3&gt;LoRA&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Low-Rank Adaptation of Large Language Models&lt;/strong&gt;（2021，ICLR）&lt;/p&gt;
&lt;p&gt;传统微调在微调大型预训练模型（如GPT-3、T5）时，通常需要更新所有参数（全参数微调）。&lt;/p&gt;
&lt;p&gt;能否仅更新少量参数即可适配下游任务？&lt;/p&gt;
&lt;p&gt;背景：适配器方法&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心思想&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;低秩假设：模型在适配新任务时，参数变化量（ΔW）具有低秩性质（即ΔW可以被分解为两个小矩阵的乘积）。&lt;/p&gt;
&lt;p&gt;冻结原始参数：保持预训练模型的权重矩阵 &lt;strong&gt;W&lt;/strong&gt; 固定，仅训练一个低秩的适配器（Adapter）来近似ΔW。
$h = W x + \Delta W x = W x + B A x$
其中 &lt;strong&gt;B&lt;/strong&gt; ∈ ℝ^{d×r} 和 &lt;strong&gt;A&lt;/strong&gt; ∈ ℝ^{r×k} 是低秩矩阵（r ≪ min(d,k)），r 是秩（rank）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在Transformer中的应用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;目标层&lt;/strong&gt;：通常应用于Transformer中的 &lt;strong&gt;注意力投影矩阵&lt;/strong&gt;（如Q、K、V的投影矩阵）和 &lt;strong&gt;前馈网络（FFN）的权重矩阵&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;参数注入方式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对原始权重矩阵 &lt;strong&gt;W&lt;/strong&gt;，添加旁路分支 &lt;strong&gt;B A&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;前向传播时，输出为原始权重结果与低秩适配结果的叠加：
$h = W x + \alpha \cdot (B A x)$
其中α是缩放系数（超参数），用于控制适配强度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;训练阶段&lt;/strong&gt;：仅优化低秩矩阵 &lt;strong&gt;B&lt;/strong&gt; 和 &lt;strong&gt;A&lt;/strong&gt;，原始权重 &lt;strong&gt;W&lt;/strong&gt; 保持冻结。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;推理阶段&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可将 &lt;strong&gt;W + B A&lt;/strong&gt; 合并为一个新矩阵，不增加额外计算量。&lt;/li&gt;
&lt;li&gt;支持多任务适配：通过切换不同的 &lt;strong&gt;B A&lt;/strong&gt; 组合适配不同任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;超参数选择&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;秩（r）&lt;/strong&gt;：控制低秩矩阵的维度（通常 r=8 或 r=16 已足够）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适配位置&lt;/strong&gt;：选择哪些层的权重矩阵添加LoRA（例如仅适配注意力层）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缩放系数α&lt;/strong&gt;：平衡原始权重与适配权重的贡献。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;参数效率&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假设对L个权重矩阵应用LoRA，每个矩阵维度为d×k，则总参数量为 &lt;strong&gt;L × r × (d + k)&lt;/strong&gt;。对于GPT-3（175B参数），若对注意力层的96个矩阵应用LoRA（r=8），总训练参数量仅约 &lt;strong&gt;0.1M&lt;/strong&gt;，是全参数量的一千万分之一。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优势&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;内存占用低、部署灵活、性能接近全微调&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进阶技术&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QLoRA&lt;/strong&gt;：结合量化（Quantization）与LoRA，进一步减少显存占用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AdaLoRA&lt;/strong&gt;：动态调整秩（r）和适配位置，提升参数效率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组合其他PEFT方法&lt;/strong&gt;：与Prompt Tuning或Adapter结合，实现多级适配。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;多模态扩展&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;图像-文本模型&lt;/strong&gt;（如CLIP）：可对视觉编码器和文本编码器分别应用LoRA。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多任务学习&lt;/strong&gt;：共享部分LoRA参数，同时适配多个任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Prompt-tuning&lt;/h3&gt;
&lt;p&gt;参考资料：&lt;a href=&quot;https://zhuanlan.zhihu.com/p/618871247#:~:text=%E6%88%AA%E6%AD%A223%E5%B9%B43%E6%9C%88%E5%BA%95%EF%BC%8C%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B%E5%8F%91%E5%B1%95%E8%B5%B0%E8%BF%87%E4%BA%86%E4%B8%89%E4%B8%AA%E9%98%B6%E6%AE%B5%EF%BC%9A&quot;&gt;五万字综述！Prompt-Tuning：深度解读一种新的微调范式 - 知乎&lt;/a&gt;（部分内容并不正确）&lt;/p&gt;
&lt;h4&gt;初步认识&lt;/h4&gt;
&lt;p&gt;特点：不更新预训练模型本身的参数！让语言模型可以在小样本（Few-shot）或零样本（Zero-shot）场景下达到理想的效果。这是怎么做到的？&lt;/p&gt;
&lt;p&gt;我们从Bert的tuning开始！&lt;/p&gt;
&lt;p&gt;假设下游任务是预测该句子是积极的（positive）还是消极的（negative）给定一个句子&lt;code&gt;[CLS] I like the Disney films very much. [SEP]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;传统的Fine-tuning方法是将其通过BERT的Transformer获得 &lt;code&gt;[CLS]&lt;/code&gt;表征之后再喂入新增加的MLP分类器进行二分类，需要一定量的训练数据来训练。&lt;/p&gt;
&lt;p&gt;而Prompt-Tuning则执行如下步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建模板（Template Construction） ：通过人工定义、自动搜索、文本生成等方法，生成与给定句子相关的一个含有&lt;code&gt;[MASK]&lt;/code&gt;标记的模板。例如&lt;code&gt;It was [MASK].&lt;/code&gt;，并拼接到原始的文本中，获得Prompt-Tuning的输入：&lt;code&gt;[CLS] I like the Disney films very much. [SEP] It was [MASK]. [SEP]&lt;/code&gt;。将其喂入BERT模型中，并复用预训练好的MLM分类器（在huggingface中为BertForMaskedLM），即可直接得到&lt;code&gt;[MASK]&lt;/code&gt;预测的各个token的概率分布；&lt;/li&gt;
&lt;li&gt;标签词映射（Label Word Verbalizer）：因为&lt;code&gt;[MASK]&lt;/code&gt;部分我们只对部分词感兴趣，因此需要建立一个映射关系。例如如果&lt;code&gt;[MASK]&lt;/code&gt;预测的词是“great”，则认为是positive类，如果是“terrible”，则认为是negative类。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;可以想到，不同的句子适合的template和label word映射应该都不同，这是Prompt-tuning非常重要的挑战。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;训练 ：根据Verbalizer，则可以获得指定label word的预测概率分布，采用交叉信息熵进行训练。此时只对预训练好的MLM head进行微调。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h4&gt;历程 （ICL &amp;amp; PET）&lt;/h4&gt;
&lt;p&gt;Prompt-Tuning起源于GPT-3的提出&lt;/p&gt;
&lt;p&gt;《Language Models are Few-Shot Learners》（NIPS，2020）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In-context Learning（ICL）&lt;/strong&gt;：直接通过从训练集中挑选一些训练样本作为任务的提示提示（Natural Language Prompt），来实现免参数更新的模型预测。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Translate English to French:
sea → mer
sky → ciel
cheese → fromage
dog →
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;给定一个训练集 和一个测试集 （因为ICT不涉及参数更新，所以一般情况下无需验证集），给定该任务的指令模板 ，给定一个预训练模型。从训练集中采样k个训练样本 （称作 &lt;strong&gt;In-Context Examples&lt;/strong&gt; ），根据指令模板 ，将这k个训练样本进行线性拼接，得到一个上下文模板（ &lt;strong&gt;称作Demonstration&lt;/strong&gt; ）&lt;/p&gt;
&lt;p&gt;给定一个测试样本 ，将其与模板拼接喂入模型中进行预测即可。&lt;/p&gt;
&lt;p&gt;当执行分类时，此时需要对生成的结果进行映射，例如通过Verbalizer的方法，获得Label Word生成的概率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但做这种简单的提示在小模型上效果不会太好。&lt;/p&gt;
&lt;p&gt;因此，大名鼎鼎的PET模型问世——&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PET（Pattern-Exploiting Training）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;《Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference》（EACL，2021）&lt;/p&gt;
&lt;p&gt;借鉴了上面的思想，但在普通的预训练模型上也能用！不一定非要GPT3那种大模型！&lt;/p&gt;
&lt;p&gt;PET详细地设计了Prompt-Tuning的重要组件：Pattern-Verbalizer-Pair（PVP）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pattern ：即上文提到的Template，为额外添加的带有&lt;code&gt;[mask]&lt;/code&gt;标记的短文本&lt;/li&gt;
&lt;li&gt;Verbalizer：Verbalizer的构建需要取决于对应的Pattern
上述两个组件被称为Pattern-Verbalizer-Pair（PVP）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PET还提出了Prompt-Tuning的集成，如同一个句子设计多个不同的pattern，每个Pattern又对应多个label word。&lt;/p&gt;
&lt;p&gt;PET还提供了半监督的学习方法——iterative PET（iPET）&lt;/p&gt;
&lt;h4&gt;更多角度理解&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Prompt本质上是对下游任务的指令，可以作为一种信息增强&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;看似设计指令是一件容易的事情，但是在真实使用过程中，预训练模型很难“理解”这些指令，主要因为预训练模型不够大、模型缺乏指令相关的训练。&lt;/p&gt;
&lt;p&gt;后者似乎可以用上文提到过的head的再训练解决，但我们期望预训练模型还应该在未知的指令上具备一定的泛化性能。&lt;/p&gt;
&lt;p&gt;为了达到这个目的，最常用的方法是 元学习（Meta Learning），也就是Meta Prompt Tuning（MPT），几个代表性的工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《TransPrompt: Towards an Automatic Transferable Prompting Framework for Few-shot Text Classification》：代表方法TransPrompt，利用迁移学习提升预训练语言模型在不同类型任务上的泛化性能；&lt;/li&gt;
&lt;li&gt;《Adapting Language Models for Zero-shot Learning by Meta-tuning on Dataset and Prompt Collections》：代表方法：MPT，统一分类任务范式，并采用元学习进行训练；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Prompt Tuning的本质是复用预训练语言模型在预训练阶段所使用的目标和参数&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;实现基于Prompt的统一范式。&lt;/p&gt;
&lt;p&gt;充分利用这个思想的方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;万物皆可生成 ：将所有任务统一为文本生成，极大化利用单向语言模型目标；&lt;/li&gt;
&lt;li&gt;万物皆可抽取 ：将所有任务统一为抽取式阅读理解，并设计抽取式预训练目标；&lt;/li&gt;
&lt;li&gt;万物皆可推理 ：将所有任务建模为自然语言推断（Natural Language Inference）或相似度匹配任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Prompt Tuning是一种PEFT&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;参数有效性学习过程中，大模型中只需要指定或额外添加少量的可训练参数，而其余的参数全部冻结，这样可以大大提高模型的训练效率的同时，确保指标不会受到太大影响。&lt;/p&gt;
&lt;p&gt;常见经典的参数有效性学习有Adapter-Tuning（2019）、Prefix-Tuning、BitFit。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关于In-Context的更多&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;样本的Input-Output Mapping的正确性是否对ICL有何影响？&lt;/p&gt;
&lt;p&gt;两个来自EMNLP2022针对样本挑选的分析型工作：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;《Rethinking the Role of Demonstrations: What Makes In-Context Learning Work?》（简称 Rethinking）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Ground-Truth Labels Matter: A Deeper Look into Input-Label Demonstrations》（简称 Ground-Truth）&lt;/p&gt;
&lt;p&gt;研究发现使用Demonstration比不使用的效果好，random label对模型性能的破坏并不是很大。宁愿错的map也比没有Demonstration好。换言之。ICL的性能收益主要来自独立规范的输入空间和标签空间，以及正确一致的演示格式。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In-Context Example的选择与顺序对ICL有什么影响？有工作发现，随机采样的方法会面临方差大的风险。&lt;/p&gt;
&lt;p&gt;来自ACL2022的两个经典工作：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;《What Makes Good In-Context Examples for GPT-3?》：代表方法KATE；&lt;/li&gt;
&lt;li&gt;《Fantastically Ordered Prompts and Where to Find Them: Overcoming Few-Shot Prompt Order Sensitivity》：简称Fantastically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一些ICL的改进工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;引入自监督（Self-supervised ICL）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;统一范式+元学习（MetaICL）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对预测进行矫正（Calibrate Before Use）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Instruction-tuning&lt;/h2&gt;
&lt;p&gt;指令学习&lt;/p&gt;
&lt;p&gt;假设是一个Question Generation任务，那么可以为这个任务定义一些指令，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Title：任务的名称；&lt;/li&gt;
&lt;li&gt;Definition：任务的定义，说明这个任务的本质和目的；&lt;/li&gt;
&lt;li&gt;Things to avoid：说明这个任务的注意事项，例如需要避免什么等等；&lt;/li&gt;
&lt;li&gt;Positive / Negative Examples：给出正确和错误的例子，作为提示；&lt;/li&gt;
&lt;li&gt;Prompt：当前任务的提示信息；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当许多任务都按照这种模式定义好模板，让模型在指令化后的数据上进行微调，模型将可以学会如何看到指令做预测。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;感觉思想上和Prompt tuning一样？只不过这个模板更加复杂精确规范了？还有就是它是针对大语言模型的微调，做法上更像是SFT，只不过训练格式统一了instruction的形式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;FLAN&lt;/h3&gt;
&lt;p&gt;《Finetuned Language Models are Zero-shot Learners》&lt;/p&gt;
&lt;p&gt;Finetuned Language Net，Google，2021&lt;/p&gt;
&lt;p&gt;基于Instruction-Tuning&lt;/p&gt;
&lt;p&gt;目标是改善GPT3的zero-shot效果&lt;/p&gt;
&lt;p&gt;62个NLP任务转化为自然语言指令格式，进行多任务训练，每个任务都设计了Instruction，最后得到137B的大模型&lt;/p&gt;
&lt;h2&gt;关于多模态&lt;/h2&gt;
&lt;h3&gt;CLIP&lt;/h3&gt;
&lt;p&gt;Contrastive Language–Image Pretraining（2021，OpenAI）&lt;/p&gt;
&lt;p&gt;预训练模型 多模态&lt;/p&gt;
&lt;p&gt;核心&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;跨模态对齐：将图像和文本映射到共享的语义空间中，使得同一语义的图文对在嵌入空间中距离更近，不相关的图文对距离更远。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;零样本能力：无需针对下游任务进行微调，直接通过自然语言描述完成图像分类、检索等任务。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;CLIP由两部分组成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;图像编码器（Image Encoder）&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;使用CNN（如ResNet）或Vision Transformer（ViT）提取图像特征。&lt;/li&gt;
&lt;li&gt;将输入图像转换为固定维度的特征向量（例如512维）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文本编码器（Text Encoder）&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;使用Transformer架构（类似BERT）处理文本。&lt;/li&gt;
&lt;li&gt;将文本序列转换为固定维度的特征向量（与图像编码器输出维度一致）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CLIP的训练依赖于&lt;strong&gt;对比学习&lt;/strong&gt;（Contrastive Learning），其核心思想是通过最大化正样本对的相似度、最小化负样本对的相似度来优化模型参数。&lt;/p&gt;
&lt;p&gt;使用互联网上的大规模图文对数据（如OpenAI使用的4亿对数据）每个样本对包含一张图像和一段描述该图像的文本。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特征提取&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图像编码器提取图像特征 $ I \in \mathbb{R}^{D} $。&lt;/li&gt;
&lt;li&gt;文本编码器提取文本特征 $ T \in \mathbb{R}^{D} $。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;相似度计算&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;计算图像特征与文本特征的余弦相似度：&lt;br /&gt;
$$
\text{sim}(I, T) = \frac{I \cdot T}{|I| |T|}
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;对比损失函数&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用&lt;strong&gt;InfoNCE损失函数&lt;/strong&gt;（Noise Contrastive Estimation的变体）：&lt;br /&gt;
$$
\mathcal{L} = -\log \frac{\exp(\text{sim}(I, T) / \tau)}{\sum_{i=1}^{N} \exp(\text{sim}(I, T_i) / \tau)}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$ \tau $ 为温度参数（控制分布平滑程度）。&lt;/li&gt;
&lt;li&gt;$ N $ 为负样本数量（通常从其他文本或图像中随机选取）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;双向优化&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同时优化图像到文本和文本到图像的匹配（双向交叉熵损失）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;CLIP最显著的特性是&lt;strong&gt;零样本分类&lt;/strong&gt;（Zero-shot Classification）。其原理如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;传统方法&lt;/strong&gt;：需要针对每个类别进行有监督训练（如ImageNet分类任务需要标注数据）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CLIP的方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;输入一张未见过的图像 $ I $。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;生成多个文本描述 $ T_1, T_2, ..., T_k $（如“dog”、“cat”、“car”）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;计算图像与每个文本描述的相似度，选择相似度最高的文本作为预测类别。&lt;/p&gt;
&lt;p&gt;无需针对特定类别进行训练。可动态扩展类别（只需提供新的文本描述）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;开源项目&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官方实现：https://github.com/openai/CLIP&lt;/li&gt;
&lt;li&gt;社区改进：如&lt;code&gt;open_clip&lt;/code&gt;、&lt;code&gt;clip-interrogator&lt;/code&gt;（图像到文本生成）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;复现CLIP&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用Hugging Face库加载预训练CLIP模型（如&lt;code&gt;clip-vit-base-patch32&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;实现零样本分类任务（如对ImageNet子集进行测试）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;BLIP&lt;/h3&gt;
&lt;p&gt;Bootstrapping Language-Image Pre-training（Saleforce Research，2022）&lt;/p&gt;
&lt;p&gt;核心思想：通过数据增强、&lt;strong&gt;Bootstrapping&lt;/strong&gt;解决噪声数据问题，同时支持理解与生成任务。&lt;/p&gt;
&lt;p&gt;多模态混合编码器-解码器架构：包含图像编码器（ViT）、文本编码器（BERT）和文本解码器（GPT），支持灵活的任务切换（如视觉问答、图像描述生成）。&lt;/p&gt;
&lt;p&gt;Caption Filtering &amp;amp; Rewriting：利用模型自身生成高质量图文对，清洗噪声数据，类似生物学中通过转录组筛选差异表达基因（DEGs）优化关键通路分析。&lt;/p&gt;
&lt;p&gt;集成ITM（Image-Text Matching）、ITC（Image-Text Contrastive Learning）、MLM等任务，提升多任务泛化能力。&lt;/p&gt;
&lt;h3&gt;ALBEF&lt;/h3&gt;
&lt;p&gt;Align Before Fuse（微软，2021）&lt;/p&gt;
&lt;p&gt;强调模态对齐（Image-Text Contrastive Learning）作为多模态融合的前提，通过对比学习拉近图像与文本的嵌入空间距离，再通过跨模态注意力实现特征融合。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;动量蒸馏：利用动量模型生成伪标签，缓解噪声数据（如网络爬取的图文对）对训练的干扰。&lt;/li&gt;
&lt;li&gt;多任务联合训练：结合ITM、ITC、MLM&lt;/li&gt;
&lt;li&gt;轻量级设计：仅需单流架构（ViT+BERT），计算效率高于双流模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Flamingo&lt;/h3&gt;
&lt;p&gt;Flamingo（DeepMind, 2022）&lt;/p&gt;
&lt;p&gt;首个支持少样本学习的多模态模型，通过冻结预训练视觉与语言模块，插入&lt;strong&gt;可训练交叉注意力层实现高效适配&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perceiver Resampler：将视觉特征压缩为固定长度的token序列，解决多图像/视频输入的长序列问题。&lt;/li&gt;
&lt;li&gt;上下文少样本学习：类似生物学中通过环境动态调控藻酸盐结构（如季节变化对M/G比例的影响），模型能根据少量示例动态调整输出策略。&lt;/li&gt;
&lt;li&gt;大规模多模态预训练：融合45M图文对和30M视频文本数据，覆盖开放域任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;多模态大模型的初步认识&lt;/h3&gt;
&lt;p&gt;多模态大模型（Multimodal Large Language Model，MMLLM）&lt;/p&gt;
&lt;h4&gt;基本组成&lt;/h4&gt;
&lt;p&gt;1.1 模态编码器（Modal Encoder）&lt;/p&gt;
&lt;p&gt;如CLIP的ViT（Vision Transformer）&lt;/p&gt;
&lt;p&gt;1.2 模态接口（Modal Interface）&lt;/p&gt;
&lt;p&gt;连接不同模态的编码器与语言模型（LLM），解决模态间的对齐问题。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;跨模态注意力机制：通过Transformer的多头注意力机制，将不同模态的特征进行交互。例如，CLIP中的图文对齐通过交叉注意力实现。&lt;/li&gt;
&lt;li&gt;特征融合策略：如早期融合（在编码阶段融合）、晚期融合（在决策阶段融合）或混合策略。&lt;/li&gt;
&lt;li&gt;模态适配器（Adapter）：在LLM中插入轻量级适配层（如LoRA），使模型能够处理多模态输入。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;1.3 生成器（Generator）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Image Captioning&lt;/li&gt;
&lt;li&gt;Text-to-Image&lt;/li&gt;
&lt;li&gt;跨模态检索，根据文本搜索相关图像，或根据图像搜索相关文本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;核心技术&lt;/h4&gt;
&lt;p&gt;2.1 数据对齐（Data Alignment）&lt;/p&gt;
&lt;p&gt;不同模态的数据在语义空间中存在异质性（如图像的连续空间 vs. 文本的离散空间）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预训练对齐：通过大规模图文对数据（如CLIP使用的4亿对数据）训练模型，使不同模态的嵌入空间对齐。&lt;/li&gt;
&lt;li&gt;动态对齐：在推理时通过注意力机制动态调整模态间的关系（如多模态Transformer）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2.2 数据融合（Data Fusion）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;特征级融合：将不同模态的特征拼接或加权平均。&lt;/li&gt;
&lt;li&gt;决策级融合：在最终决策阶段结合各模态的结果（如投票机制）。&lt;/li&gt;
&lt;li&gt;跨模态交互：通过Transformer的交叉注意力机制实现模态间的深层次交互。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2.3 统一标识空间（Unified Representation Space）&lt;/p&gt;
&lt;p&gt;构建一个共享的语义空间，使得不同模态的数据能够映射到同一空间中进行比较或操作。&lt;/p&gt;
&lt;p&gt;如CLIP的嵌入空间：通过联合训练，使文本和图像的嵌入向量在同一个空间中对齐。&lt;/p&gt;
&lt;h4&gt;下游任务&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;视觉问答（Visual Question Answering，VQA）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;模型根据输入的图像和文本问题生成答案。&lt;/p&gt;
&lt;p&gt;例如，Monkey模型通过反向思维链推理（Backward Chain of Thought）提升复杂问题的解题能力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;图文生成（Image-to-Text / Text-to-Image）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;典型模型：
&lt;ul&gt;
&lt;li&gt;Text-to-Image：Stable Diffusion、DALL-E、Midjourney。&lt;/li&gt;
&lt;li&gt;Image-to-Text：CLIP、BLIP、InstructBLIP。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;技术突破：
&lt;ul&gt;
&lt;li&gt;扩散模型（Diffusion Models）：通过逐步去噪生成高质量图像。&lt;/li&gt;
&lt;li&gt;指令微调（Instruction Tuning）：通过RLHF或LoRA优化模型对复杂指令的理解能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Spatial Visual Reasoning（空间视觉推理）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如，判断一个物体是否在另一个物体的上方&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Visual Commonsense Reasoning（视觉常识推理）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;视觉常识推理要求模型不仅能够识别图像中的物体，还需要理解这些物体之间可能存在的复杂关系，并基于常识进行推理。比如，给出一张图片，模型需要回答“为什么图中的男生正在指着女生”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Referring Expression Comprehension（指代表达理解）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;指代表达理解是指给定一段描述性的文本（如句子），模型需要在一幅或多幅图中找到被这段文本所描述的具体对象。这项任务挑战了模型对自然语言的理解能力以及将语言与视觉信息关联的能力。例如，给定句子“穿红色衣服的女孩旁边有一只黑狗”，模型需要准确地在图片中定位出这个女孩和这只狗。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Image-Text Retrieval（图文检索）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;根据文本查询来检索相关联的图像，或者反过来，根据图像来检索相关的文本描述。&lt;/p&gt;
</content:encoded></item><item><title>实践-bot搭建过程</title><link>https://blog.stivine.fun/posts/practice-bot-building-process/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/practice-bot-building-process/</guid><description>群聊机器人stivine的搭建与二次开发......</description><pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;div style=&quot;
background: #fff3cd;
color: #856404;
border: 2px solid #ffc107;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
font-size: 18px;
font-weight: bold;
&quot;&amp;gt;
⚠️ &amp;lt;strong&amp;gt;注：&amp;lt;/strong&amp;gt;本篇博客是探索过程中的随手记录，包括大量无用信息、AI生成信息。参考价值较低。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h1&gt;本地环境&lt;/h1&gt;
&lt;p&gt;Ubuntu 24.04.1 LTS&lt;/p&gt;
&lt;h1&gt;WSL2 Ubuntu图形化界面安装&lt;/h1&gt;
&lt;p&gt;使用Srdp&lt;/p&gt;
&lt;h1&gt;ECS云服务器&lt;/h1&gt;
&lt;p&gt;ECS：Elastic Compute Service&lt;/p&gt;
&lt;p&gt;公网IP&lt;/p&gt;
&lt;p&gt;密钥对生成&lt;/p&gt;
&lt;p&gt;workbench ssh连接&lt;/p&gt;
&lt;p&gt;wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh&lt;/p&gt;
&lt;p&gt;bash Miniconda3-latest-Linux-x86_64.sh&lt;/p&gt;
&lt;p&gt;安装图形化界面&lt;/p&gt;
&lt;p&gt;以Ubuntu 18操作系统为例介绍安装图形化桌面Ubuntu Desktop的方法，其他发行版的配置可能有所差异，具体情况请参阅相应发行版的官方文档。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;通过VNC远程连接Linux实例。具体操作，请参见&lt;a href=&quot;https://help.aliyun.com/zh/ecs/user-guide/log-on-to-an-instance-by-using-vnc&quot;&gt;使用VNC登录实例&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行以下命令，更新软件源。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行以下命令，安装图形化桌面。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install ubuntu-desktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果安装出现unmet dependencies报错，请参见&lt;a href=&quot;https://help.aliyun.com/zh/ecs/use-cases/installing-a-graphical-desktop-environment-for-a-linux-instance#section-h9d-6ps-66l&quot;&gt;常见问题&lt;/a&gt;中的解决方案解决以后，再启动图形化桌面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行以下命令，设置默认启动为图形化桌面。您可以执行&lt;code&gt;systemctl set-default multi-user.target&lt;/code&gt;，即可取消图形化界面登录。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl set-default graphical.target
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行以下命令，重启ECS实例。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等待系统重启完成，出现图形化界面，确认安装成功。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;说明&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;您无需设置VNC的登录密码，只需要输入实例的用户名和密码即可安全地访问ECS实例。&lt;/p&gt;
&lt;p&gt;如果不小心设置了，无法用root账户登录：&lt;/p&gt;
&lt;p&gt;使用命令vim /etc/pam.d/gdm-password修改gdm-password文件。&lt;/p&gt;
&lt;p&gt;将配置项的user != root删去。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;vscode ssh连接&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;config编写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host ecs-instance-name
    HostName your-ecs-public-ip
    User your-username
    IdentityFile /path/to/your/private-key-file (如果使用密钥认证)
    # Port 22 (如果你更改了默认的SSH端口，则需要指定)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;ssh连接linux服务器中断后，如何让命令继续在服务器运行&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;命令前面加screen&lt;/p&gt;
&lt;p&gt;这个时候如果ssh终端断开了连接。我们只需要再次连接服务器然后输入指令&lt;/p&gt;
&lt;p&gt;screen -ls
这里就会显示ssh断开之前的程序，其实断开后程序依然在后台在运行，只是我们这个时候需要将它放到前台来运行。这个时候我们们已经通过screen -ls查询到了线程号是27267了，所以我们只需要执行下面的指令即可恢复到前台了。&lt;/p&gt;
&lt;p&gt;screen -r 27267
如果想杀掉终端可以执行&lt;/p&gt;
&lt;p&gt;kill 27267&lt;/p&gt;
&lt;p&gt;【注：后来知道了tmux的用法~】&lt;/p&gt;
&lt;h1&gt;一些常用命令&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;jobs -l&lt;/p&gt;
&lt;p&gt;kill -9 &amp;lt;PID&amp;gt; 强行终止进程（树）&lt;/p&gt;
&lt;p&gt;fg &amp;lt;任务号&amp;gt; 恢复&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;弃用&lt;/h1&gt;
&lt;h2&gt;go-cqhttp【弃用】&lt;/h2&gt;
&lt;p&gt;conda create -n qqbot python=3.10 -y
conda activate qqbot&lt;/p&gt;
&lt;p&gt;wget https://github.com/Mrs4s/go-cqhttp/releases/download/v1.0.0-rc3/go-cqhttp_linux_amd64.tar.gz&lt;/p&gt;
&lt;p&gt;https://github.com/Mrs4s/go-cqhttp/releases/download/v1.2.0/go-cqhttp_linux_amd64.tar.gz&lt;/p&gt;
&lt;p&gt;tar -zxvf go-cqhttp_linux_amd64.tar.gz
cd go-cqhttp&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反向 Websocket 通信&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;servers:
  - ws-reverse:
      universal: ws://127.0.0.1:8080/onebot/v11/ws  # nonebot 监听的地址
      reconnect-interval: 5000
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;nonebot【弃用】&lt;/h2&gt;
&lt;p&gt;python -m pip install --user pipx
python -m pipx ensurepath&lt;/p&gt;
&lt;p&gt;pipx install nb-cli&lt;/p&gt;
&lt;p&gt;nb create&lt;/p&gt;
&lt;p&gt;nb run&lt;/p&gt;
&lt;p&gt;在 NapCat 配置添加反向 ws 地址，地址为 &lt;code&gt;ws://127.0.0.1:8080/onebot/v11/ws&lt;/code&gt;, 这里的 &lt;code&gt;8080&lt;/code&gt; 是 NoneBot 输出的端口号，&lt;code&gt;/onebot/v11/ws&lt;/code&gt; 是 NoneBot onebot 适配器默认的路径&lt;/p&gt;
&lt;p&gt;nb adapter install nonebot-adapter-console 测试基础功能&lt;/p&gt;
&lt;p&gt;nb plugin install nonebot-plugin-analysis-bilibili&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://whitechi73.github.io/OpenShamrock/&quot;&gt;OpenShamrock&lt;/a&gt;【弃用】&lt;/h2&gt;
&lt;h1&gt;&lt;a href=&quot;https://github.com/NapNeko/NapCatQQ&quot;&gt;NapCatQQ&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh &amp;amp;&amp;amp; sudo bash napcat.sh&lt;/p&gt;
&lt;p&gt;[2025-02-01 21:54:16]: 输入 xvfb-run -a qq --no-sandbox 命令启动。
[2025-02-01 21:54:16]: 保持后台运行 请输入 screen -dmS napcat bash -c &quot;xvfb-run -a qq --no-sandbox&quot;&lt;br /&gt;
[2025-02-01 21:54:16]: 后台快速登录 请输入 screen -dmS napcat bash -c &quot;xvfb-run -a qq --no-sandbox -q QQ号码&quot;&lt;br /&gt;
[2025-02-01 21:54:16]: Napcat安装位置 /opt/QQ/resources/app/app_launcher/napcat
[2025-02-01 21:54:16]: WEBUI_TOKEN 请自行查看/opt/QQ/resources/app/app_launcher/napcat/config/webui.json文件获取
[2025-02-01 21:54:16]: 注意, 您可以随时使用 screen -r napcat 来进入后台进程并使用 ctrl + a + d 离开(离开不会关闭后台进程)。
[2025-02-01 21:54:16]: 停止后台运行 请输入 screen -S napcat -X quit&lt;/p&gt;
&lt;p&gt;02-01 22:08:02 [info] [NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:6099/webui?token=xxxxxxx&lt;/p&gt;
&lt;p&gt;LIBVA_DRIVER_NAME=nvidia qq&lt;/p&gt;
&lt;h1&gt;koishi&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Koishi.js&lt;/strong&gt;（通称 &lt;strong&gt;Koishi&lt;/strong&gt;）是一个跨平台的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%81%8A%E5%A4%A9%E6%A9%9F%E5%99%A8%E4%BA%BA&quot;&gt;聊天机器人&lt;/a&gt;框架，其使用 &lt;a href=&quot;https://zh.wikipedia.org/wiki/TypeScript&quot;&gt;TypeScript&lt;/a&gt; 开发，可在 &lt;a href=&quot;https://zh.wikipedia.org/wiki/Node.js&quot;&gt;Node.js&lt;/a&gt; 上运行。&lt;/p&gt;
&lt;p&gt;Koishi 标识来源于&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9D%B1%E6%96%B9Project&quot;&gt;东方 Project&lt;/a&gt; 中的角色&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8F%A4%E6%98%8E%E5%9C%B0%E6%88%80&quot;&gt;古明地恋&lt;/a&gt; (Komeiji Koishi)&lt;/p&gt;
&lt;p&gt;AppImage文件的下载&lt;/p&gt;
&lt;p&gt;http://localhost:5140/&lt;/p&gt;
&lt;p&gt;配置koishi上的onebot adapter&lt;/p&gt;
&lt;p&gt;napcat网络配置更改为ws://127.0.0.1:5140/onebot&lt;/p&gt;
&lt;h1&gt;原理学习&lt;/h1&gt;
&lt;p&gt;使用 NoneBot 框架搭建的机器人具有以下几个基本组成部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;NoneBot 机器人框架主体：负责连接各个组成部分，提供基本的机器人功能&lt;/li&gt;
&lt;li&gt;驱动器 &lt;code&gt;Driver&lt;/code&gt;：客户端/服务端的功能实现，负责接收和发送消息（通常为 HTTP 通信）【FAST API】&lt;/li&gt;
&lt;li&gt;适配器 &lt;code&gt;Adapter&lt;/code&gt;：驱动器的上层，负责将&lt;strong&gt;平台消息&lt;/strong&gt;与 NoneBot 事件/操作系统的消息格式相互转换【Onebot】&lt;/li&gt;
&lt;li&gt;插件 &lt;code&gt;Plugin&lt;/code&gt;：机器人的功能实现，通常为负责处理事件并进行一系列的操作&lt;/li&gt;
&lt;/ol&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HTTP服务端&lt;/th&gt;
&lt;th&gt;NapCat作为Http请求接受方 接收对应接口调用并回应 的单工模型&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP客户端&lt;/td&gt;
&lt;td&gt;NapCat作为Http请求发起方 将事件推送至插件/应用框架 的单工模型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket服务端&lt;/td&gt;
&lt;td&gt;通常指正向WS 既能主动推送事件也能接收请求 的双工模型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket客户端&lt;/td&gt;
&lt;td&gt;通常指反向WS 既能主动推送事件也能接收请求 的双工模型&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;为什么其他平台的适配器名字都与平台一致，只有 QQ 对应 OneBot？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是由多方原因共同导致的。&lt;/p&gt;
&lt;p&gt;首先，许多平台都公开了自己的机器人接口，只有腾讯官方对机器人采取封杀的态度。因此只有 QQ 的适配器是基于第三方协议实现的，OneBot 正是这个协议的名字。而第三方协议远远不止一个，所以不应该用 QQ 这个笼统的名称。在未来也可能出现其他面向 QQ 的适配器。&lt;/p&gt;
&lt;p&gt;反过来，OneBot 作为一个协议，未来也可能支持更多的聊天平台。届时只需有 koishi-adapter-onebot，Koishi 也相当于支持了这些平台。一旦出现了这样的情况，用 QQ 作为适配器名反而显得以偏概全了，这也是不妥当的。&lt;/p&gt;
&lt;p&gt;但尽管这么说，从目前来看，当我们在讨论用 Koishi 实现 QQ 机器人时，都默认采用这个协议。&lt;/p&gt;
&lt;h1&gt;Mongo学习&lt;/h1&gt;
&lt;p&gt;是一种Nosql非关系型数据库&lt;/p&gt;
&lt;p&gt;集合（相当于表）、文档（类似json格式的一条记录，bson）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install -y mongodb
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo service mongodb start
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo service mongodb status
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;mongo常用指令&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;连接和切换数据库&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;# 连接到MongoDB服务&lt;/p&gt;
&lt;p&gt;mongo --port 27017&lt;/p&gt;
&lt;p&gt;# 切换到指定数据库，如果不存在则创建&lt;/p&gt;
&lt;p&gt;use mydatabase&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据库操作&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;# 显示所有数据库&lt;/p&gt;
&lt;p&gt;show dbs&lt;/p&gt;
&lt;p&gt;# 删除当前数据库&lt;/p&gt;
&lt;p&gt;db.dropDatabase()&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;集合操作&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;# 创建集合&lt;/p&gt;
&lt;p&gt;db.createCollection(&quot;mycollection&quot;)&lt;/p&gt;
&lt;p&gt;# 显示所有集合&lt;/p&gt;
&lt;p&gt;show collections&lt;/p&gt;
&lt;p&gt;# 删除集合&lt;/p&gt;
&lt;p&gt;db.mycollection.drop()&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;文档操作&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;插入文档&lt;/p&gt;
&lt;p&gt;# 插入单个文档&lt;/p&gt;
&lt;p&gt;db.mycollection.insert({name: &quot;John&quot;, age: 30})&lt;/p&gt;
&lt;p&gt;# 批量插入文档&lt;/p&gt;
&lt;p&gt;db.mycollection.insertMany([{name: &quot;Jane&quot;, age: 25}, {name: &quot;Doe&quot;, age: 22}])&lt;/p&gt;
&lt;p&gt;查询文档&lt;/p&gt;
&lt;p&gt;# 查询所有文档&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;db.&amp;lt;collection_name&amp;gt;.find().pretty()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;# 条件查询文档&lt;/p&gt;
&lt;p&gt;db.mycollection.find({age: {$gt: 25}})&lt;/p&gt;
&lt;p&gt;# 查询单个文档&lt;/p&gt;
&lt;p&gt;db.mycollection.findOne({name: &quot;John&quot;})&lt;/p&gt;
&lt;p&gt;更新文档&lt;/p&gt;
&lt;p&gt;# 覆盖更新文档&lt;/p&gt;
&lt;p&gt;db.mycollection.update({name: &quot;John&quot;}, {name: &quot;John&quot;, age: 31})&lt;/p&gt;
&lt;p&gt;# 局部更新文档&lt;/p&gt;
&lt;p&gt;db.mycollection.update({name: &quot;John&quot;}, {$set: {age: 32}})&lt;/p&gt;
&lt;p&gt;# 批量更新文档&lt;/p&gt;
&lt;p&gt;db.mycollection.update({age: {$lt: 30}}, {$set: {status: &quot;young&quot;}}, {multi: true})&lt;/p&gt;
&lt;p&gt;删除文档&lt;/p&gt;
&lt;p&gt;# 删除单个文档&lt;/p&gt;
&lt;p&gt;db.mycollection.remove({name: &quot;John&quot;})&lt;/p&gt;
&lt;p&gt;# 删除所有文档（慎用）&lt;/p&gt;
&lt;p&gt;db.mycollection.remove({})&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mongodb://localhost:27017/yourDatabaseName
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;p&gt;先安装了mongo 解压后放到/usr/local&lt;/p&gt;
&lt;p&gt;vim /etc/mongodb.conf&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/logs/mongodb.log 
logappend=true
port=27017 
bind_ip=0.0.0.0
fork=true 
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;cd /usr/local/mongodb/bin
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;mongod -f /etc/mongodb.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mongosh安装解压后放到/usr/local&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/usr/local/mongosh/bin/mongosh --host  localhost --port 27017&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;一定要先关闭Mongodb再退出终端！否则重启困难。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mongod --shutdown -f /etc/mongodb.conf&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;mongod --repair -f /etc/mongodb.conf&lt;/p&gt;
&lt;p&gt;注意如果修复不顺利，很有可能是权限问题&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用小技巧&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;bashrc中添加一行&lt;code&gt;alias mongoshell=&apos;/usr/local/mongosh/bin/mongosh --host localhost --port 27017&apos;&lt;/code&gt;，以后即可在终端使用mongoshell启动shell了&lt;/p&gt;
&lt;h2&gt;compass&lt;/h2&gt;
&lt;p&gt;在windows上安装即可&lt;/p&gt;
&lt;p&gt;windows上连接&lt;code&gt;mongodb://localhost:27017/&lt;/code&gt;即可&lt;/p&gt;
&lt;p&gt;如果是云服务器呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;修改 MongoDB 配置以允许远程访问&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;编辑 MongoDB 的配置文件 &lt;code&gt;/etc/mongod.conf&lt;/code&gt;，找到 &lt;code&gt;net&lt;/code&gt; 部分，并确保它如下所示（允许所有 IP 访问，请根据实际情况调整）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net:
  port: 27017
  bindIp: 0.0.0.0  # 允许任何IP访问，或者指定具体的IP地址如 &apos;192.168.1.100&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存更改后，重启 MongoDB 服务使配置生效：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart mongod
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;配置防火墙规则&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;确保服务器防火墙允许外部访问 MongoDB 端口（默认是 27017）。对于 UFW（Ubuntu 的默认防火墙），你可以运行以下命令来开放端口：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow 27017
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;索引&lt;/h2&gt;
&lt;p&gt;在使用 MongoDB 的 &lt;code&gt;find()&lt;/code&gt; 方法进行查询时，MongoDB 不一定会遍历集合中的每一条数据。是否需要遍历所有文档取决于查询条件和索引的使用情况。&lt;/p&gt;
&lt;p&gt;如果你在查询中使用的字段上没有创建索引，那么 MongoDB 将执行全表扫描（Collection Scan），这意味着它会检查集合中的每一个文档来找到匹配的记录。这种情况下，随着集合的增长，查询性能会显著下降。&lt;/p&gt;
&lt;p&gt;但是，如果你在一个或多个字段上创建了索引，MongoDB 可以利用这些索引来快速定位到符合条件的文档，而不需要遍历整个集合。例如，如果你有一个针对 &lt;code&gt;keyxx&lt;/code&gt; 字段的索引，并且执行 &lt;code&gt;find({ keyxx: 1 })&lt;/code&gt; 查询，MongoDB 将使用该索引来直接访问值为 &lt;code&gt;1&lt;/code&gt; 的那些文档，而不是逐个检查每个文档。&lt;/p&gt;
&lt;p&gt;为了验证 MongoDB 是否使用了索引来优化查询，你可以使用 &lt;code&gt;.explain(&quot;executionStats&quot;)&lt;/code&gt; 方法来分析查询计划。这将告诉你MongoDB是如何执行查询的，包括是否使用了索引、扫描了多少文档等信息。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;db.collection.find({ keyxx: 1 }).explain(&quot;executionStats&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这个命令，你可以看到查询的执行细节，包括是否有使用索引（index scan）还是进行了全表扫描（collection scan）。如果查询效率不如预期，考虑创建合适的索引通常是提高查询性能的第一步。不过也要注意，虽然索引可以加快读操作，但它们也会稍微减慢写操作，因为每次写入时都需要更新索引。因此，应该根据具体的使用场景来平衡索引的使用。&lt;/p&gt;
&lt;p&gt;在compass中Indexes选项即可进行相关的操作。&lt;/p&gt;
&lt;h2&gt;迁移&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;mongodump&lt;/code&gt; 和 &lt;code&gt;mongorestore&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;安装见&lt;a href=&quot;https://blog.csdn.net/weixin_44799217/article/details/127940551&quot;&gt;mongodump工具安装及使用详解_mongodump安装-CSDN博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这是最常用的方法之一，特别适合一次性迁移或备份恢复。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;在源服务器上导出数据库&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;mongodump&lt;/code&gt; 命令来导出你要迁移的数据库。假设你的数据库名为 &lt;code&gt;mydatabase&lt;/code&gt;，你可以运行如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mongodump --db mydatabase --out /path/to/backup/
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这将在指定路径下创建一个包含你的数据库的备份文件夹。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;将备份传输到目标服务器&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你可以通过多种方式（如 &lt;code&gt;scp&lt;/code&gt;, FTP, 或者直接使用云存储服务）将备份文件夹传输到目标服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;在目标服务器上导入数据库&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;mongorestore&lt;/code&gt; 来恢复数据库。首先确保你在目标服务器上有MongoDB正在运行，然后执行如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mongorestore --db mydatabase /path/to/backup/mydatabase/
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;脚本开发&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://koishi.chat/zh-CN/manual/starter/boilerplate.html&quot;&gt;创建模板项目 | Koishi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm init koishi@latest&lt;/code&gt;【要代理】&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm run start&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm run setup [name] -- [-c] [-m] [-G] &lt;/code&gt;创建插件工作区，如&lt;code&gt; npm run setup example&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在koishi.yml中修改&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;plugins:
 example:{}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt; 开发模式&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install [...deps] -w koishi-plugin-[name]&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;指令系统&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ctx.command(&apos;echo &amp;lt;message&amp;gt;&apos;)
  .action((_, message) =&amp;gt; message)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定义了echo指令，（必选）&lt;strong&gt;参数&lt;/strong&gt;是&amp;lt;message&amp;gt;，[xxx]用来定义可选参数&lt;/p&gt;
&lt;p&gt;action是&lt;strong&gt;回调函数&lt;/strong&gt;，第一个参数是Argv。其余的自己定义。&lt;/p&gt;
&lt;p&gt;模板：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctx.command(&apos;test &amp;lt;arg1&amp;gt; [arg2] [arg3]&apos;)
  .action((_, arg1, arg2, arg3) =&amp;gt; { /* do something */ })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果想把参数声明为文本（空格的解析），需要改为&lt;code&gt;ctx.command(&apos;echo &amp;lt;message:text&amp;gt;&apos;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;（因此文本参数通常在最后&lt;/p&gt;
&lt;p&gt;指令可以定义&lt;strong&gt;选项&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctx.command(&apos;test&apos;)
  .option(&apos;alpha&apos;, &apos;-a&apos;)          // 定义一个选项
  .option(&apos;beta&apos;, &apos;-b [beta]&apos;)    // 定义一个带参数的可选选项
  .option(&apos;gamma&apos;, &apos;-c &amp;lt;gamma&amp;gt;&apos;)  // 定义一个带参数的必选选项
  .action(({ options }) =&amp;gt; JSON.stringify(options))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;事件系统&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ctx.on(&apos;message&apos;, (session) =&amp;gt; {
    if (session.content === &apos;天王盖地虎&apos;) {
      session.send(&apos;我是二百五&apos;)
    }
  })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;message是事件名称，session是参数，是一个会话对象&lt;/p&gt;
&lt;h2&gt;消息元素&lt;/h2&gt;
&lt;p&gt;阅读rss-owl代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; if (arg.merge===true) {
 message = `&amp;lt;message forward&amp;gt;&amp;lt;author id=&quot;${rssItem.author}&quot;/&amp;gt;${messageList.join(&quot;&quot;)}&amp;lt;/message&amp;gt;`
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即可学会转发消息的发送&lt;/p&gt;
&lt;h2&gt;数据库&lt;/h2&gt;
&lt;h1&gt;聊天记录导出&lt;/h1&gt;
&lt;h2&gt;@seidko/messages&lt;/h2&gt;
&lt;p&gt;实测是好用的，和sqlite一起&lt;/p&gt;
&lt;p&gt;可以保存图片、b站分享等消息&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;但是没有发送者账号记录&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;chathub_message&lt;/h2&gt;
&lt;p&gt;？&lt;/p&gt;
&lt;h2&gt;q78kg/messages-saver-mongodb&lt;/h2&gt;
&lt;p&gt;没问题&lt;/p&gt;
&lt;h2&gt;message-logger&lt;/h2&gt;
&lt;p&gt;？&lt;/p&gt;
&lt;h2&gt;QQ消息管理器中导出消息&lt;/h2&gt;
&lt;p&gt;UTF-8-BOM&lt;/p&gt;
&lt;h1&gt;插件1&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;&apos;node-fetch&apos;的问题&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2025-03-10 18:19:02 [E] loader Error [ERR_REQUIRE_ESM]: require() of ES Module /.../koishi-app/node_modules/node-fetch/src/index.js from /.../koishi-app/external/example/lib/index.js not supported.
                        Instead change the require of /.../koishi-app/node_modules/node-fetch/src/index.js in /.../koishi-app/external/example/lib/index.js to a dynamic import() which is available in all CommonJS modules.
                            at TracingChannel.traceSync (node:diagnostics_channel:315:14)
                            at Object.&amp;lt;anonymous&amp;gt; (/.../koishi-app/external/example/lib/index.js:39:33)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;node-fetch&lt;/code&gt; 从 v3 开始是一个纯 ES 模块（ESM），而你的代码可能是在 CommonJS 环境下运行的（例如使用了 &lt;code&gt;require&lt;/code&gt;）。Node.js 不支持直接通过 &lt;code&gt;require&lt;/code&gt; 导入 ES 模块。&lt;/p&gt;
&lt;p&gt;解决方法 ：降级 &lt;code&gt;node-fetch&lt;/code&gt; 到 v2&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install node-fetch@2 -w @stivine/koishi-plugin-zvv&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;发布插件&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm run pub zvv&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;发布失败是npm的登录问题，tokens权限要为publish&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;更新版本&lt;/p&gt;
&lt;p&gt;npm run bump zvv -3&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;发现报错&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;koishi日志&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SenderError: Error with request send_group_msg, args: {&quot;group_id&quot;:xxxxxx,&quot;message&quot;:&quot;[......]&quot;}, retcode: 1200&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;napcat日志&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[error] stivine | 发生错误 Error: 文件名解析失败&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;换思路&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;getPreviewImageDataUrlsFromText-&amp;gt;getThumbnailDataURL-&amp;gt;extractFrame-&amp;gt;fetchIndex + parseIndex&lt;/p&gt;
&lt;h1&gt;插件2: eew&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;导出模块&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过&lt;code&gt;__export&lt;/code&gt;函数将&lt;code&gt;Config&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, 和 &lt;code&gt;name&lt;/code&gt;这三个标识符导出，使得外部文件可以通过&lt;code&gt;src_exports&lt;/code&gt;对象访问这些资源。&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;__toCommonJS&lt;/code&gt;函数确保模块兼容CommonJS规范。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;配置定义（&lt;code&gt;config.ts&lt;/code&gt;）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;koishi.Schema.intersect&lt;/code&gt;方法创建了一个名为&lt;code&gt;SConfig&lt;/code&gt;的复杂配置模式，该模式由多个部分组成，包括网络设置、基本设置、名单设置以及推送设置等。&lt;/li&gt;
&lt;li&gt;网络设置中包含地震预警WebSocket地址(&lt;code&gt;eewUrl&lt;/code&gt;)和通信超时时间(&lt;code&gt;eewTimeout&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;基本设置中有一个选项决定是否启用&lt;code&gt;EEW&lt;/code&gt;命令(&lt;code&gt;enabledEew&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;名单设置允许用户指定预警信息推送的目标群聊或私聊(&lt;code&gt;eewSendList&lt;/code&gt;)和机器人白名单(&lt;code&gt;eewBotList&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;推送设置让用户选择是否启用来自不同地区或机构（如四川、福建、台湾、日本、中国地震台网等）的地震预警或报告推送。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;工具函数（&lt;code&gt;tools.ts&lt;/code&gt;）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;custLog&lt;/code&gt;：一个自定义的日志记录函数，接受上下文(&lt;code&gt;ctx&lt;/code&gt;)、日志类型(&lt;code&gt;type&lt;/code&gt;)和消息内容(&lt;code&gt;value&lt;/code&gt;)作为参数，并根据类型输出相应级别的日志。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewInConsole&lt;/code&gt;：用于在控制台显示地震预警信息，使用了&lt;code&gt;custLog&lt;/code&gt;函数来实现日志输出。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showTargets&lt;/code&gt;和&lt;code&gt;showPlatforms&lt;/code&gt;：这两个函数分别用来生成关于预警推送目标和平台的信息字符串，支持添加表情符号以增强可读性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getFormatTime&lt;/code&gt;：获取并返回给定时间戳的格式化字符串，默认格式为&quot;yyyy-MM-dd hh:mm:ss&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这段代码定义了一个名为&lt;code&gt;EewAdapter&lt;/code&gt;的类，用于处理地震预警信息的发送和管理。以下是对该类及其方法的详细分析：&lt;/p&gt;
&lt;h3&gt;类 &lt;code&gt;EewAdapter&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;socket&lt;/code&gt;: 预留用于WebSocket连接的属性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sendList&lt;/code&gt;: 推送目标列表，包含需要接收预警信息的目标（如群聊或私聊）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;botsList&lt;/code&gt;: 机器人白名单，指定了哪些机器人可以发送预警信息。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewLogs&lt;/code&gt;: 布尔值，决定是否在终端中显示预警日志。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewEmoji&lt;/code&gt;: 布尔值，决定输出信息中是否使用表情符号增强可读性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctx&lt;/code&gt;: 上下文对象，通常用于提供对Koishi框架功能的访问。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eewAllows&lt;/code&gt;: 对象，定义了不同类型的地震预警或报告是否允许被处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;构造函数&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化时接收上下文(&lt;code&gt;ctx&lt;/code&gt;)、机器人列表(&lt;code&gt;bot_list&lt;/code&gt;)、发送列表(&lt;code&gt;send_list&lt;/code&gt;)、是否显示预警日志(&lt;code&gt;eew_log&lt;/code&gt;)和是否显示表情符号(&lt;code&gt;eew_emoji&lt;/code&gt;)作为参数，并将它们分配给相应的实例属性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sendMessageToFriend(user_id, message)&lt;/code&gt;: 向指定用户ID发送私信。它遍历所有可用的机器人，并通过每个机器人的&lt;code&gt;sendPrivateMessage&lt;/code&gt;方法发送消息。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sendMessageToGroup(guild_id, message)&lt;/code&gt;: 向指定群组ID发送消息。类似于&lt;code&gt;sendMessageToFriend&lt;/code&gt;，但它调用的是机器人的&lt;code&gt;sendMessage&lt;/code&gt;方法。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sendEew(data_object)&lt;/code&gt;: 根据传入的地震数据对象决定如何处理和发送预警信息。首先检查预警类型是否允许被处理，然后根据预警类型实例化对应的预警处理器（例如&lt;code&gt;ScEew&lt;/code&gt;, &lt;code&gt;FjEew&lt;/code&gt;等）。接着调用处理器的&lt;code&gt;eewExecute&lt;/code&gt;方法执行具体的预警逻辑，并通过&lt;code&gt;showEewInfo&lt;/code&gt;方法获取格式化的预警信息。最后，根据发送列表中的目标类型（私聊或群聊），分别调用相应的方法发送预警信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;其他细节&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在&lt;code&gt;sendEew&lt;/code&gt;方法中，对于&lt;code&gt;eqlist&lt;/code&gt;类型的数据，只处理其中的&lt;code&gt;No1&lt;/code&gt;字段，这可能意味着这些数据结构包含了多个条目，但仅对最重要的一个进行处理。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;预警类型与具体处理器之间的映射似乎存在一个小错误：无论是&lt;code&gt;cenc_eqlist&lt;/code&gt;还是&lt;code&gt;jma_eqlist&lt;/code&gt;都使用了&lt;code&gt;ScEew&lt;/code&gt;处理器实例化，而根据预期逻辑，可能应该分别为它们指定不同的处理器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;eew&lt;/code&gt;变量在每次&lt;code&gt;sendEew&lt;/code&gt;方法结束时被设置为&lt;code&gt;void 0&lt;/code&gt;（即&lt;code&gt;undefined&lt;/code&gt;），这可能是为了手动释放资源或重置状态。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;getSenderBotList()&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;该方法用于获取所有可被允许发送消息的机器人列表。它遍历&lt;code&gt;botsList&lt;/code&gt;中的每个ID，并与当前上下文(&lt;code&gt;ctx&lt;/code&gt;)中活跃的机器人列表进行匹配。如果找到匹配项，则将其添加到当前机器人的列表&lt;code&gt;cur_bot_list&lt;/code&gt;中。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;setup(ws_url, time_out)&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;初始化WebSocket连接，使用提供的WebSocket URL (&lt;code&gt;ws_url&lt;/code&gt;)和超时时间(&lt;code&gt;time_out&lt;/code&gt;)。如果当前没有已存在的WebSocket连接，则创建一个新的连接。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;start()&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开始监听WebSocket事件，包括连接打开、接收到消息、连接关闭和发生错误的情况。当接收到不同类型的消息（如地震预警或心跳包）时，会调用相应的处理逻辑。对于地震预警类型的消息，将调用&lt;code&gt;sendEew&lt;/code&gt;方法处理并发送预警信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;stop()&lt;/strong&gt;:
- 关闭WebSocket连接，并将&lt;code&gt;socket&lt;/code&gt;属性设置为&lt;code&gt;undefined&lt;/code&gt;以表示无连接状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pause() 和 resume()&lt;/strong&gt;:
- 分别用于暂停和恢复WebSocket连接的操作。这些方法可能依赖于底层WebSocket实现的支持。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;isEnable()&lt;/strong&gt;:
- 检查是否存在有效的WebSocket连接。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;status(show_emoji = true)&lt;/strong&gt;:
- 返回当前WebSocket连接的状态描述，支持使用表情符号增强可读性。根据WebSocket的&lt;code&gt;readyState&lt;/code&gt;属性值，返回不同的状态描述。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;info(show_emoji = true)&lt;/strong&gt;:
- 提供详细的系统状态信息，包括是否开启预警、通信状态、平台数量和目标数量等。同样支持使用表情符号增强输出。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;setEewSwAllows(...)&lt;/strong&gt;:
- 设置不同类型的地震预警或报告是否允许被处理。这允许动态调整哪些类型的预警信息会被处理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;test2()&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 一个测试方法，模拟接收一条特定格式的地震预警信息(JSON字符串)，并通过`sendEew`方法处理这条消息。这对于调试和验证系统的正确性非常有用。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码定义了两个类：&lt;code&gt;Eew&lt;/code&gt;和继承自&lt;code&gt;Eew&lt;/code&gt;的&lt;code&gt;ScEew&lt;/code&gt;。这些类主要用于处理和展示地震预警信息。以下是详细分析：&lt;/p&gt;
&lt;h3&gt;类 &lt;code&gt;Eew&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;包含了一些基本的地震信息属性，如报告时间(&lt;code&gt;report_time&lt;/code&gt;)、编号(&lt;code&gt;num&lt;/code&gt;)、纬度(&lt;code&gt;latitude&lt;/code&gt;)、经度(&lt;code&gt;longitude&lt;/code&gt;)、地区(&lt;code&gt;region&lt;/code&gt;)、震级(&lt;code&gt;mag&lt;/code&gt;)、发震时间(&lt;code&gt;origin_time&lt;/code&gt;)、深度(&lt;code&gt;depth&lt;/code&gt;)、烈度(&lt;code&gt;intensity&lt;/code&gt;)以及类型(&lt;code&gt;type&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctx&lt;/code&gt;: 上下文对象，用于提供对Koishi框架功能的访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;构造函数&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接收一个上下文对象&lt;code&gt;ctx&lt;/code&gt;作为参数，并将其分配给实例属性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;showEewInfo(show_log, show_emoji)&lt;/code&gt;: 返回一个字符串模板，表示如何展示地震预警信息。具体实现由子类完成。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eewExecute(json_data)&lt;/code&gt;: 根据传入的JSON数据初始化地震信息。它尝试从输入的数据中提取相关字段，并为缺失的值提供默认值或使用辅助函数（如&lt;code&gt;getFormatTime()&lt;/code&gt;）生成当前时间。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static test(ctx, show_emoji = true)&lt;/code&gt;: 提供了一个静态测试方法，用于模拟一条地震预警信息，并通过&lt;code&gt;showEewInConsole&lt;/code&gt;函数输出到控制台。这有助于验证系统在接收特定格式的信息时是否能正确响应。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;类 &lt;code&gt;ScEew&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在父类的基础上增加了一个&lt;code&gt;title&lt;/code&gt;属性，用于标识该地震预警是来自四川地震网。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eewExecute(scEewData)&lt;/code&gt;: 覆写了父类的方法，除了调用父类的&lt;code&gt;eewExecute&lt;/code&gt;方法外，还额外设置了地震烈度(&lt;code&gt;intensity&lt;/code&gt;)和深度(&lt;code&gt;depth&lt;/code&gt;)。如果数据中包含烈度或深度信息，则更新标题为“中国地震台网”。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewInfo(show_log = true, show_emoji = true)&lt;/code&gt;: 实现了具体的地震信息展示逻辑。它构建了一个包含地震详情的字符串，并根据参数决定是否显示在控制台上。此外，它支持通过表情符号增强信息的可读性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这段代码继续扩展了&lt;code&gt;Eew&lt;/code&gt;基类，定义了三个新的子类：&lt;code&gt;FjEew&lt;/code&gt;、&lt;code&gt;CwaEew&lt;/code&gt;和&lt;code&gt;JmaEew&lt;/code&gt;，分别用于处理福建、台湾（湾湾）和日本的地震预警信息。以下是每个子类的具体分析：&lt;/p&gt;
&lt;h4&gt;类 &lt;code&gt;FjEew&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;继承自&lt;code&gt;Eew&lt;/code&gt;的所有属性。&lt;/li&gt;
&lt;li&gt;新增了一个属性&lt;code&gt;type&lt;/code&gt;，用于标识地震预警是否为最终报。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eewExecute(fjEewData)&lt;/code&gt;：重写了父类的方法，除了调用父类的&lt;code&gt;eewExecute&lt;/code&gt;方法外，还根据数据中的&lt;code&gt;isFinal&lt;/code&gt;字段来设置&lt;code&gt;type&lt;/code&gt;属性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewInfo(show_log = true, show_emoji = true)&lt;/code&gt;：实现了具体的展示逻辑，构建了包含地震详情的字符串，并支持通过表情符号增强可读性。如果设置了&lt;code&gt;isFinal&lt;/code&gt;，会在标题后添加“[终]”标识。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;类 &lt;code&gt;CwaEew&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;继承自&lt;code&gt;Eew&lt;/code&gt;的所有属性。&lt;/li&gt;
&lt;li&gt;新增了两个属性：&lt;code&gt;shindo&lt;/code&gt;（最大震度）和&lt;code&gt;title&lt;/code&gt;（标题），用于存储特定于台湾地震预警的信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eewExecute(cwaEewData)&lt;/code&gt;：重写了父类的方法，除了调用父类的&lt;code&gt;eewExecute&lt;/code&gt;方法外，还设置了深度和最大震度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewInfo(show_log = true, show_emoji = true)&lt;/code&gt;：实现了具体的展示逻辑，构建了包含地震详情的字符串，并支持通过表情符号增强可读性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;类 &lt;code&gt;JmaEew&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;继承自&lt;code&gt;Eew&lt;/code&gt;的所有属性。&lt;/li&gt;
&lt;li&gt;新增了几个属性：&lt;code&gt;shindo&lt;/code&gt;（最大震度）、&lt;code&gt;title&lt;/code&gt;（标题）和&lt;code&gt;info&lt;/code&gt;（地震列表信息），用于存储特定于日本地震预警的信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eewExecute(jmaEewData)&lt;/code&gt;：重写了父类的方法，除了调用父类的&lt;code&gt;eewExecute&lt;/code&gt;方法外，还设置了深度、最大震度、最终报标识以及标题和信息内容。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showEewInfo(show_log = true, show_emoji = true)&lt;/code&gt;：实现了具体的展示逻辑，构建了包含地震详情的日语字符串，并支持通过表情符号增强可读性。对于日语标题，使用了特定的格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这段代码定义了一个名为&lt;code&gt;apply&lt;/code&gt;的函数，该函数是Koishi插件的主要入口点，用于处理与地震预警相关的命令和配置。以下是详细的分析：&lt;/p&gt;
&lt;h3&gt;函数 &lt;code&gt;apply&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctx&lt;/code&gt;: Koishi上下文对象，提供访问框架功能的方法。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config&lt;/code&gt;: 插件配置对象，包含所有必要的配置项。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置初始化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从传入的&lt;code&gt;config&lt;/code&gt;中提取各项设置，包括是否启用地震预警(&lt;code&gt;EEW_SW&lt;/code&gt;)、WebSocket地址(&lt;code&gt;EEW_ADDR&lt;/code&gt;)、超时时间(&lt;code&gt;EEW_TIMEOUT&lt;/code&gt;)等，并为每个设置提供了默认值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;事件监听器&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;监听Koishi的生命周期事件，如&lt;code&gt;ready&lt;/code&gt;、&lt;code&gt;exit&lt;/code&gt;和&lt;code&gt;dispose&lt;/code&gt;，分别在插件准备就绪、退出和销毁时执行相应的操作（例如启动或停止WebSocket连接）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;前置检查函数 &lt;code&gt;previousCheck&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在执行某些命令之前进行必要的检查，确保WebSocket地址有效且地震预警指令已启用。如果检查失败，则返回相应的错误信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;命令定义&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;ctx.command&lt;/code&gt;方法定义了一系列命令，使用户能够通过聊天界面与插件交互。这些命令包括：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EEW&lt;/strong&gt;: 地震预警菜单界面。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.开启&lt;/strong&gt;: 开启地震预警服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.关闭&lt;/strong&gt;: 关闭地震预警服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.重置&lt;/strong&gt;: 重置地震预警服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.状态&lt;/strong&gt;: 查看当前地震预警服务的状态信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.测试&lt;/strong&gt;: 发送一个地震预警测试消息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.目标&lt;/strong&gt;: 查看地震预警推送的目标列表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EEW.平台&lt;/strong&gt;: 查看地震预警推送的机器人列表。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;具体实现细节&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于每个命令，都会首先调用&lt;code&gt;previousCheck&lt;/code&gt;进行前置条件检查，以确保可以安全地执行后续操作。&lt;/li&gt;
&lt;li&gt;根据不同的命令，会调用&lt;code&gt;EewAdapter&lt;/code&gt;的不同方法来执行实际的操作，比如启动或停止WebSocket连接，或者显示状态信息等。&lt;/li&gt;
&lt;li&gt;提供了别名(alias)支持，使得用户可以通过多种方式输入相同的命令（例如，&lt;code&gt;EEW.开启&lt;/code&gt;和&lt;code&gt;EEW 开启&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;API相关信息&lt;/h3&gt;
&lt;h4&gt;all_eew&lt;/h4&gt;
&lt;p&gt;接收所有 JSON API 推送:wss://ws-api.wolfx.jp/all_eew&lt;/p&gt;
&lt;p&gt;type字段&quot;sc_eew&quot;&quot;fj_eew&quot;&quot;cwa_eew&quot;&quot;jma_eew&quot;&quot;jma_eqlist&quot;&quot;cenc_eqlist&lt;/p&gt;
&lt;h4&gt;JMA&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;{&quot;Title&quot;:&quot;緊急地震速報（予報）&quot;,&quot;CodeType&quot;:&quot;Ｍ、最大予測震度及び主要動到達予測時刻の緊急地震速報&quot;,&quot;Issue&quot;:{&quot;Source&quot;:&quot;東京&quot;,&quot;Status&quot;:&quot;通常&quot;},&quot;EventID&quot;:&quot;20250401144201&quot;,&quot;Serial&quot;:4,&quot;AnnouncedTime&quot;:&quot;2025/04/01 14:42:50&quot;,&quot;OriginTime&quot;:&quot;2025/04/01 14:41:47&quot;,&quot;Hypocenter&quot;:&quot;根室半島南東沖&quot;,&quot;Latitude&quot;:43.0,&quot;Longitude&quot;:145.9,&quot;Magunitude&quot;:4.2,&quot;Depth&quot;:90,&quot;MaxIntensity&quot;:&quot;2&quot;,&quot;Accuracy&quot;:{&quot;Epicenter&quot;:&quot;IPF 法（5 点以上）&quot;,&quot;Depth&quot;:&quot;IPF 法（5 点以上）&quot;,&quot;Magnitude&quot;:&quot;防災科研システム&quot;},&quot;MaxIntChange&quot;:{&quot;String&quot;:&quot;ほとんど変化なし&quot;,&quot;Reason&quot;:&quot;不明、未設定時、キャンセル時&quot;},&quot;WarnArea&quot;:[],&quot;isSea&quot;:true,&quot;isTraining&quot;:false,&quot;isAssumption&quot;:false,&quot;isWarn&quot;:false,&quot;isFinal&quot;:true,&quot;isCancel&quot;:false,&quot;OriginalText&quot;:&quot;37 03 00 250401144250 C11 250401144147 ND20250401144201 NCN904 JD////////////// JN/// 189 N430 E1459 090 42 02 RK44209 RT10/// RC0//// 9999=&quot;,&quot;Pond&quot;:&quot;68&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意isFinal和Serial两个量，标注是否为最终报、是第几次警报&lt;/p&gt;
&lt;h4&gt;四川&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ID EEW发报ID&lt;/li&gt;
&lt;li&gt;EventID EEW发报事件&lt;/li&gt;
&lt;li&gt;IDReportTimeEEW 发报时间(UTC+8)&lt;/li&gt;
&lt;li&gt;ReportNumEEW发报数&lt;/li&gt;
&lt;li&gt;OriginTime发震时间(UTC+8)&lt;/li&gt;
&lt;li&gt;HypoCenter震源地&lt;/li&gt;
&lt;li&gt;Latitude震源地纬度&lt;/li&gt;
&lt;li&gt;Longitude震源地经度&lt;/li&gt;
&lt;li&gt;Magunitude震级&lt;/li&gt;
&lt;li&gt;Depth震源深度(原报文未提供)&lt;/li&gt;
&lt;li&gt;MaxIntensity&lt;strong&gt;最大烈度&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;希望增加功能：自动重启&lt;/h2&gt;
&lt;p&gt;eew 开启做了什么？检查isEnable，如果False则setup、start&lt;/p&gt;
&lt;p&gt;eew 重置做了什么？stop，new一个eewadapter&lt;/p&gt;
&lt;p&gt;eew 状态做了什么？.info，而info就是检查status，status是检查isEnable和socket.readyState，只要有一个false就是无连接&lt;/p&gt;
&lt;p&gt;isEnable做了什么？return this.socket != void 0;&lt;/p&gt;
&lt;p&gt;socket是怎么来的？setup来的，setup具体内容即&lt;/p&gt;
&lt;p&gt;&lt;code&gt;this.socket = this.socket ?? this.ctx.http.ws(ws_url, { timeout: time_out });&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;解开疑惑！bug状态即isEnable返回true，即socket存在，但status是false，即socket not ready！&lt;/p&gt;
&lt;p&gt;需要插件多加一个功能：定时（如半小时）检查status，如果false即自动进行重置开启之类的工作。&lt;/p&gt;
&lt;h1&gt;简单的学习&lt;/h1&gt;
&lt;h2&gt;Dify学习&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;cd dify
cd docker
cp .env.example .env
docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;http://localhost/install&lt;/p&gt;
&lt;p&gt;后端即服务，提供API接口&lt;/p&gt;
&lt;p&gt;可以实时更新数据库&lt;/p&gt;
&lt;h2&gt;jina学习&lt;/h2&gt;
&lt;p&gt;用来爬取网站内容 / 提供embedding模型&lt;/p&gt;
&lt;h2&gt;ollama学习&lt;/h2&gt;
&lt;p&gt;本地部署embedding&lt;/p&gt;
&lt;p&gt;curl -fsSL https://ollama.com/install.sh | sh&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ollama pull bge-m3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bge-m3作为embedding模型&lt;/p&gt;
&lt;p&gt;http://host.docker.internal:11434&lt;/p&gt;
&lt;p&gt;卡在索引中？&lt;/p&gt;
&lt;h1&gt;Todo&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;直播/视频更新提示是怎么实现的？&lt;/li&gt;
&lt;li&gt;聊天记录导出到数据库？&lt;/li&gt;
&lt;li&gt;接入大模型 聊天功能？&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>笔记-Games101</title><link>https://blog.stivine.fun/posts/notes-games101/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/notes-games101/</guid><description>计算机图形学入门</description><pubDate>Mon, 01 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;01 Overview of Computer Graphics&lt;/h1&gt;
&lt;p&gt;插图风格
电影特效
动画
设计渲染图
可视化
VR
数字绘画
模拟/仿真
GUI图形用户接口
Typography字体&lt;/p&gt;
&lt;p&gt;投影/曲线/表面中的数学
光线和阴影中的物理
3D图形的表示
动画/模拟 球的弹跳&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;光栅化 Rasterization 三维几何体投影到屏幕 实时/离线的概念&lt;/li&gt;
&lt;li&gt;Curves and Meshes&lt;/li&gt;
&lt;li&gt;光追 Ray Tracing&lt;/li&gt;
&lt;li&gt;Animation / Simulation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OpenGL 只是API，其使用不是课程内容
建模、游戏引擎也不是课程内容
CV不是课程内容，其注重理解、推断等&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img2024.cnblogs.com/blog/2997174/202409/2997174-20240924001430133-805385401.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;虎书：Fundamentals of Computer Graphics&lt;/p&gt;
&lt;p&gt;geek一词来自英语方言，意思是“傻瓜”或“怪胎”。含有贬义。另一方面它又有智力超群和努力的含义，通常被用于形容对计算机和网络技术有狂热兴趣并投入大量时间钻研的人。所以俗称发烧友或怪杰。&lt;/p&gt;
&lt;h1&gt;02 Review of Linear Algebra&lt;/h1&gt;
&lt;p&gt;图形学用到的一些基础学科知识：线性代数、微积分、统计、光学、力学、信号处理......&lt;/p&gt;
&lt;p&gt;向量的投影与向量分解的关系&lt;/p&gt;
&lt;p&gt;向量内积可以确定前后方向&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;回顾向量外积 右手定则（有两种）
https://zhuanlan.zhihu.com/p/342679387&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;$\vec x\times\vec y=+\vec z$等价于坐标系是右手系（三个向量指的是坐标轴正方向）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;小知识：unity用的是左手系&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;向量外积可以确定左右/内外
&lt;img src=&quot;https://img2024.cnblogs.com/blog/2997174/202409/2997174-20240926000149139-28711230.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$\vec{p}=(\vec{p}\cdot\vec{u})\vec{u}+(\vec{p}\cdot\vec{v})\vec{v}+(\vec{p}\cdot\vec{w})\vec{w}$，其中u,v,w为右手系的x,y,z轴方向向量&lt;/p&gt;
&lt;p&gt;内积外积都可以写成矩阵运算的形式。
内积的我们很熟悉了。
外积的我还是第一次见。
$\vec a\times\vec b=A^*b=\begin{pmatrix}0&amp;amp;&amp;amp;-z_a&amp;amp;&amp;amp;y_a\z_a&amp;amp;&amp;amp;0&amp;amp;&amp;amp;-x_a\-y_a&amp;amp;&amp;amp;x_a&amp;amp;&amp;amp;0\end{pmatrix}\begin{pmatrix}x_b\y_b\z_b\end{pmatrix}$
又仔细想了想根本不是第一次见。
这不就是行列式算外积的那个式子的变式嘛。&lt;/p&gt;
&lt;h1&gt;03 Transformation&lt;/h1&gt;
&lt;p&gt;分为Modeling和Viewing的Transformation
光栅化成像使用投影transformation&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Scale
$\left[\begin{array}{c}x&apos;\y&apos;\end{array}\right]=\left[\begin{array}{cc}s_x&amp;amp;0\0&amp;amp;s_y\end{array}\right]\left[\begin{array}{c}x\y\end{array}\right]$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reflection
以左右反转为例，上面Scale中$s_x=-1,s_y=1$即可&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shear（切变）
以水平切变为例，纵坐标不变，横坐标平移多少与纵坐标线性相关
$\begin{bmatrix}x&apos;\y&apos;\end{bmatrix}=\begin{bmatrix}1&amp;amp;a\0&amp;amp;1\end{bmatrix}\begin{bmatrix}x\y\end{bmatrix}$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rotate
$\mathbf{R}_\theta=\begin{bmatrix}\cos\theta&amp;amp;-\sin\theta\\sin\theta&amp;amp;\cos\theta\end{bmatrix}$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;以上变换均为线性变换&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;translation（平移）
就是加了个常数坐标向量
不属于线性变换&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了方便讨论此变换，我们引入：&lt;strong&gt;齐次坐标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2D point表示为$(x, y, 1)^T$
2D vector表示为$(x, y, 0)^T$
则线性变换可以用如下方法表示：
$$\begin{pmatrix}x&apos;\y&apos;\w&apos;\end{pmatrix}:=:\begin{pmatrix}1&amp;amp;0&amp;amp;t_x\0&amp;amp;1&amp;amp;t_y\0&amp;amp;0&amp;amp;1\end{pmatrix}\cdot\begin{pmatrix}x\y\1\end{pmatrix}:=:\begin{pmatrix}x+t_x\y+t_y\1\end{pmatrix}$$&lt;/p&gt;
&lt;p&gt;思考，为什么点和向量的表示又不再一样了？希望向量有平移不变性。还可以怎么理解？点和向量好像也可以区别开来。点-点=向量。向量+向量=向量。点+向量=点。这真是完美的表示！&lt;/p&gt;
&lt;p&gt;但还有一个问题！&lt;/p&gt;
&lt;p&gt;点+点，新增的第三维成了2？&lt;/p&gt;
&lt;p&gt;我们规定：&lt;/p&gt;
&lt;p&gt;$\begin{pmatrix}x\y\w\end{pmatrix}$和$\begin{pmatrix}x/w\y/w\1\end{pmatrix}$表示的是同一个点！
这不就体现齐次性了吗！太妙了！！！&lt;/p&gt;
&lt;p&gt;现在把线性变换和平移统一考虑【被称为&lt;strong&gt;仿射变换&lt;/strong&gt;】，在齐次坐标下即为：
$$\begin{pmatrix}x&apos;\y&apos;\1\end{pmatrix} = \begin{pmatrix}a&amp;amp;b&amp;amp;t_x\c&amp;amp;d&amp;amp;t_y\0&amp;amp;0&amp;amp;1\end{pmatrix}\cdot\begin{pmatrix}x\y\1\end{pmatrix}$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逆变换与逆矩阵&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ez&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;复合变换与矩阵乘法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ez
有个好的例子：绕非原点的中心旋转&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;拓展到三维变换&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ez&lt;/p&gt;
&lt;p&gt;旋转变换这里稍微复杂一些，绕某个轴旋转？更一般的旋转？&lt;/p&gt;
&lt;p&gt;$$\mathbf{R}_{xyz}(\alpha,\beta,\gamma):=:\mathbf{R}_x(\alpha):\mathbf{R}_y(\beta):\mathbf{R}_z(\gamma)$$&lt;/p&gt;
&lt;p&gt;&quot;Euler angles&quot;&lt;/p&gt;
&lt;p&gt;$$\mathbf R(\mathbf n,\alpha) = \cos(\alpha) \mathbf I + (1-\cos(\alpha)) \mathbf n\mathbf n^T + \sin(\alpha)\begin{pmatrix}0&amp;amp;&amp;amp;-n_z&amp;amp;&amp;amp;n_y\n_z&amp;amp;&amp;amp;0&amp;amp;&amp;amp;-n_x\-n_y&amp;amp;&amp;amp;n_x&amp;amp;&amp;amp;0\end{pmatrix}$$&lt;/p&gt;
&lt;p&gt;&quot;Rodrigues&apos;s Rotation Formula&quot;
其中$\alpha$是旋转角度，$n$是旋转轴（默认轴过原点）（如果不过原点，还记得怎么做吗？联想二维？）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;感想：自己学过仿射变换，又学过线代，把它们联系起来应该是非常trival的，但自己却没有学习到这一点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;04 Transformation Cont.&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;挖坑。四元数？解决旋转矩阵不方便插值的问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;拍照M(model)V(view)P(projection)三步走：位置？角度？投影！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;view/camera transformation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;需要确定的参数：
position $\vec e$
Look at direction $\hat g$
Up direction $\hat t$&lt;/p&gt;
&lt;p&gt;规定在原点，看-Z方向的物体！Up direction则为+Y轴方向。
所有物体需要跟着相机进行平移与旋转！
旋转矩阵可以先求其逆矩阵（也是转置矩阵，毕竟正交）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;projection transformation&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;orthographic projection 正交投影&lt;/li&gt;
&lt;li&gt;perspective projection 透视投影&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;我靠这个我竟然只看这两个名字就很轻松地理解了！我之前也考虑过这个问题hhh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img2024.cnblogs.com/blog/2997174/202409/2997174-20240928002848128-820849715.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正交投影&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;正交投影似乎十分简单，即丢掉Z轴。注意，还规定缩放到-1到1的矩形中。&lt;/p&gt;
&lt;p&gt;正交投影变换（Orthographic Projection Transformation）定义一个立方体的左右，下上，远近。先平移，再缩放，成一个标准立方体（canonical cube）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;思考：为什么远的一面的z值，大于近的一面的z值？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;透视投影&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;近平面、远平面，但远的平面大一些。四棱锥的尖被削掉了。这个形状叫Frustum（视锥体）&lt;/p&gt;
&lt;p&gt;观察这个东西和长方体的区别，就能理解透视投影和正交投影的区别。&lt;/p&gt;
&lt;p&gt;规定，近平面不会发生任何变化，远平面的z值也就是f也不会变化，中心点不会发生变化。直白说，就是把远平面往中心挤压成近平面大小。&lt;/p&gt;
&lt;p&gt;$y&apos;=\frac nzy\quad x&apos;=\frac nzx$&lt;/p&gt;
&lt;p&gt;考虑齐次坐标下$\begin{pmatrix}x\y\z\1\end{pmatrix}\Rightarrow\begin{pmatrix}nx/z\ny/z\\text{unknown}\1\end{pmatrix}$，再加上z值不变的假设，已经足以确定这个“挤压”变换矩阵了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;哎呀！真的太妙啦！！！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;再进行正交投影，即为透视投影的全过程！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;思考：中间一个点的z值怎么变？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;05 rasterization 1（Triangles）&lt;/h1&gt;
&lt;p&gt;Frustum的宽高比、垂直可视角度&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img2024.cnblogs.com/blog/2997174/202409/2997174-20240929011826507-160600870.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;定义screen，像素的二维数组，一种典型的光栅化成像设备
定义屏幕坐标系&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img2024.cnblogs.com/blog/2997174/202409/2997174-20240929013958075-786869179.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Pixel(X, Y) is centered at(x + 0.5, y + 0.5)&lt;/p&gt;
&lt;p&gt;视口变换&lt;/p&gt;
&lt;p&gt;到底什么是光栅化
Raster Scan 隔行扫描
成像设备&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LCD（Liquid Crystal Display）&lt;/li&gt;
&lt;li&gt;LED（发光二极管）&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先只考虑三角形！&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sampling方法
需要inside函数判断，前面提到过的叉乘法
bounding box对光栅化加速&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bayer pattern&lt;/p&gt;
&lt;h1&gt;06 rasterization 2（Antialiasing and Z-buffing）&lt;/h1&gt;
&lt;p&gt;抗锯齿(aliasing)/反走样&lt;/p&gt;
&lt;p&gt;Artifact 一切看上去不太对的东西&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;锯齿(aliasing)？&lt;/li&gt;
&lt;li&gt;摩尔纹：忽略奇数行奇数列？&lt;/li&gt;
&lt;li&gt;视错觉？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;方案：filter，滤波器进行模糊&lt;/p&gt;
&lt;p&gt;问题的关键：采样频率&lt;/p&gt;
&lt;p&gt;傅里叶展开与傅里叶变换&lt;/p&gt;
&lt;p&gt;采样频率要和函数本身频率关系很大&lt;/p&gt;
&lt;p&gt;滤波就是去掉一些频率的波&lt;/p&gt;
&lt;p&gt;傅里叶变换让我们看到频谱&lt;/p&gt;
&lt;p&gt;经过High-pass filter【高通滤波】，发现剩下的是原图像的边界&lt;/p&gt;
&lt;p&gt;经过Low-pass filter【低通滤波】，发现得到了一张模糊的图&lt;/p&gt;
&lt;p&gt;滤波与卷积与平均之间的关系&lt;/p&gt;
&lt;p&gt;两个信号的卷积，对应到两个信号频域上，是它们的乘积。
（反过来，时域上的乘积也对应频域上的卷积）&lt;/p&gt;
&lt;p&gt;滤波器去做卷积，等价于滤波器和信号经过傅里叶变换后相乘，再逆傅里叶变换。&lt;/p&gt;
&lt;p&gt;box filter就是低通滤波器&lt;/p&gt;
&lt;p&gt;采样就是在重复原始信号的频谱
采样就是时域上原信号与一系列冲激函数的乘积，对应到频域实际上是信号的复制粘贴。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;近似方法MSAA&lt;/strong&gt;
近似了模糊操作&lt;/p&gt;
&lt;p&gt;其他抗锯齿前沿方法
FXAA
TAA（Temporal AA）&lt;/p&gt;
&lt;p&gt;超分辨率与抗锯齿的关系？
DLSS法&lt;/p&gt;
&lt;h1&gt;07 Shading 1 (Illumination, Shading and Graphics Pipeline)&lt;/h1&gt;
&lt;p&gt;Painter&apos;s Algorithm 先画远处物体，再用近处物体覆盖。
但这无法解决覆盖关系成环的情况！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Z-Buffer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;深度缓存！
存储并不断更新深度图 Depth / Z buffer
最后输出 Frame buffer&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;很直白的想法！
此算法对不同物品输入顺序没有要求。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个算法可以和MSAA结合。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shading（着色）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对不同物品应用不同材质的过程。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Blinn-Phong Reflectance Model&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;高光、漫反射、环境光照&lt;/p&gt;
&lt;p&gt;定义 Viewer direction / Surface normal / Light direction / Surface parameters&lt;/p&gt;
&lt;p&gt;Shading is local 如不考虑桌子上其他物体的影子&lt;/p&gt;
&lt;p&gt;漫反射，不同角度，光照亮度不同，余弦定律&lt;/p&gt;
&lt;p&gt;点光源到固定距离的能量 平方反比$L_d=k_d\left(I/r^2\right)\mathrm{max}(0,\mathbf{n}\cdot\mathbf{l})$&lt;/p&gt;
&lt;p&gt;$k_d$表示 diffuse coefficient(color)&lt;/p&gt;
&lt;p&gt;漫反射从哪里看都一样&lt;/p&gt;
&lt;h1&gt;08 Shading 2（Shading, Pipeline and Texture Mapping）&lt;/h1&gt;
&lt;p&gt;考虑高光（镜面反射）情况。
&lt;strong&gt;Blinn-Phong Reflection Model&lt;/strong&gt;
只需考察“半程向量”和平面法线的接近程度：
$\mathbf{h}=\mathrm{bisector}(\mathbf{v},\mathbf{l})=\frac{v+l}{||v+l||}$
$L_{s}=k_{s}:(I/r^{2})\max(0,\cos\alpha)^{p}=k_s:(I/r^2)\max(0,\mathbf{n}\cdot\mathbf{h})^p$
经验性地，p一般比1大（100-200），使得高光更加集中。&lt;/p&gt;
&lt;p&gt;关于&lt;strong&gt;环境光&lt;/strong&gt;：
规定$L_a=k_a:I_a$，是一个常数&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;着色频率 Shading Frequencies&lt;/strong&gt;
flat shading 对每个三角形着色
gouraud shading 对顶点计算着色
Phong shading 对每个像素着色&lt;/p&gt;
&lt;p&gt;怎么求顶点的法线？加权平均法，权是三角形面积。
怎么求点之间位置的法线？重心坐标法&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;图形管线/实时渲染管线 Pipeline&lt;/strong&gt;
shader：控制着色方式 以OpenGL为例
对每个对象执行shader&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vertex shader&lt;/li&gt;
&lt;li&gt;fragment/pixel shader&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;网站：shadertoy&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有很多着色器：geometry shader、compute shader.....&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;纹理映射 Texture Mapping&lt;/strong&gt;
本质是调整物体的不同部位的不同属性，如漫反射系数
怎么形成映射呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想到了GTA罪恶都市的那个skin&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Texture coordinate(u,v)，[0,1]内的正方形
无缝贴图 tiled texture
Wang Tiled&lt;/p&gt;
&lt;h1&gt;09 Shading 3 (Texture Mapping Cont.)&lt;/h1&gt;
&lt;p&gt;关于&lt;strong&gt;重心坐标&lt;/strong&gt;：在三角形内部插值都要用，很多属性都要用&lt;/p&gt;
&lt;p&gt;$(x, y) = \alpha A + \beta B + \gamma C$
$\alpha + \beta + \gamma = 1$（共面条件）
$\alpha=\frac{S_A}{S_A+S_B+S_C}$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我靠，数竞学的平几又用上了！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意一点：不能在投影之后的三角形做插值！重心坐标在这个过程中不是不变量！！！三维空间中的属性，就要在三维空间中做插值。&lt;/p&gt;
&lt;p&gt;进而实现了&lt;strong&gt;纹理的实际操作&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但会有一些问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;纹理的放大
高清图，纹理却像素很低
纹理上的像素称为texel
这样的话，多个pixel都会对应到同一个texel上去，或者说这些pixel在找对应的texel时，得到的是texel序数非整数。效果就很不好。
解决办法：&lt;strong&gt;双线性插值Bilinear&lt;/strong&gt;，进而还有双三次插值Bicubic&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;纹理太大了
摩尔纹、锯齿现象
远处的pixel会覆盖很大一片纹理，再用像素中心采样就会走样
当然可以MSAA，但代价很大
希望把Point Query转化为 Avg. Range Query，这是一个算法问题。希望输入区域得到均值。而不是输入点得到点的值再做操作。
解决办法：&lt;strong&gt;Mipmap&lt;/strong&gt;（fast, approx, square range query）
一张纹理图缩小分辨率生成一系列纹理，仅仅需要多33%的存储
算出对应到纹理的边长，然后小正方形近似
但Mipmap层数是整数，做不到连续变化
没关系！双线性插值后，在层与层之间再次插值。三线性插值！
现在无论什么区域我都能查询了！
但还会出现问题。Overblur问题。
改进：Anisotropic Filtering 各向异性过滤 改变了长宽比的也预计算！喵啊！也就是说，正方形推广到了长方形！但还有问题！斜着的区域不行！ 开销是3倍。
再改进：EWA过滤 拆成多个圆形，多次查询&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Texturing还可以用来做什么？
可以理解成一切用到范围查询的&lt;/p&gt;
&lt;p&gt;环境光照，用Texture实现
假设环境光照来自无穷远处
环境光用球面记录 Spherical Environment Map
扭曲问题？Cube Map！&lt;/p&gt;
&lt;p&gt;凹凸贴图/法线贴图 Texture改变高度 橘子皮例子
具体计算：算梯度，写出切线方向，进而算出法线方向&lt;/p&gt;
&lt;p&gt;位移贴图 顶点的位置真的动了 需要三角形足够大（采样率足够大）但太大也不行，direct x提供了动态的，根据需要决定。
推广到3D.&lt;/p&gt;
&lt;h1&gt;10 Geometry 1 (Introduction)&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;我靠！数学建模？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;分为Implicit Geometry 和 Explicit Geometry
后者判断点在不在内部变难了&lt;/p&gt;
&lt;p&gt;隐式的例子
CSG，Constructive Solid Geometry 利用布尔运算
Distance Fuctions， blend操作，怎么恢复，水平集方法&lt;/p&gt;
&lt;p&gt;Fractals（分型）（Implicit）&lt;/p&gt;
&lt;h1&gt;11 Geometry 2（Curves and Surfaces）&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Point Cloud (Explicit)&lt;/strong&gt;
字面意思，就是一大堆点
点云如何转化为面，是重要的研究&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Polygon Mesh (Explicit)&lt;/strong&gt;
最广泛应用
三角形面&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.obj Format&lt;/strong&gt;
v/vn/vt/f&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;贝塞尔曲线&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;de Casteljau Algorithm&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;妙啊！
怎么代数表示？
Bernstein Polynomials！
妙啊！
贝塞尔曲线的基本性质
关于仿射变换的不变性
凸包性质&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分段贝塞尔曲线&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更好控制
通常使用piecewise cubic Bezier（四个控制点）
关于连续性&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;其他曲线&lt;/strong&gt;
样条曲线spline
B-splines 贝塞尔曲线的扩展 局部性更好 非常复杂
NURBS 更加复杂&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;贝塞尔曲面&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;4×4个控制点&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;欢迎学习胡事民老师的课程&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;12 Geometry 3&lt;/h1&gt;
&lt;p&gt;会逐渐接触到 Ed Catmull 和 Pat Hanrahan 两个图灵奖获得者的工作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mesh Operations&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Mesh subdivision&lt;/strong&gt;
分两步：引入更多三角形、调整它们的位置
以Loop Subdivision为例介绍算法&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;其实还是很像插值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;另一种算法可以不止用在三角形面上： Catmull-Clark Subdivision (General Mesh)
定义 quad face、Non-quad face、Extraordinary vertex(degree != 4)
每次在每个face上取一点，再在每一边上取中点，然后把这些新的点相连
发现face上取的点一定是奇异点，具体度的大小取决于之前face有几条边。
在这之后，奇异点的数目不可能再增加了！因为上一步之后已经没有non-quad face了。
点取完了，再调整位置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Mesh simplification&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;很多时候没必要太细节
一种方法：Edge Collapse 边坍缩
怎么决定坍缩成的点的位置呢？Quadric Error Metrics二次误差度量！优化问题。
实际坍缩的过程。也是根据这个度量选择坍缩哪条边。但是这涉及贪心算法不最优的问题！一个边坍缩会引起其他边变化！
最小堆？确实解决了最小值不断更新的问题，不用每次排序了。但这还是没有解决我说的那个最优性问题啊！一个边坍缩会引起其他边变化啊！不能贪心算法啊！
经过实践，我们决定忽视这个问题。。。。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Mesh regularization&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;略&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关于阴影！&lt;/strong&gt;
着色解决不了这个问题。
提出 Shadow mapping！
核心思想：阴影说明光源看不到这个点。光源和相机同时看到的点则不是阴影点。
【只能处理点光源】【硬阴影】
第一步，从光源看向场景。回忆zbuffer，得到深度图。
第二步，从摄像机出发，看到的点，投影回去，与之前记录的深度比较。
但这个深度比较的过程中会涉及浮点数比较误差的问题。。。。实际操作时会用bias等方式缓解这些问题。但不能真正解决。shadow map的分辨率也是个问题（游戏设置里的 阴影质量）&lt;/p&gt;
&lt;p&gt;然后考察软阴影。本质是因为光源有大小。有的地方能看到部分光源。&lt;/p&gt;
&lt;h1&gt;13 Ray Tracing 1&lt;/h1&gt;
&lt;p&gt;去解决光栅化不能解决的一些 global effects 的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Soft shadows&lt;/li&gt;
&lt;li&gt;Glossy reflection 粗糙面的光泽&lt;/li&gt;
&lt;li&gt;Indirect illumination 间接光照，多次反射&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ray Tracing更加accurate但非常慢，因此更多是非实时场景下应用。&lt;/p&gt;
&lt;p&gt;定义Light Rays：沿直线传播，无碰撞，光路可逆。&lt;/p&gt;
&lt;p&gt;Ray Casting：找到光路
具体有eye ray、shadow ray&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recursive (Whitted-Style) Ray Tracing&lt;/strong&gt;
无数次的反射与折射 着色时都加到像平面对应像素上去
primary ray、secondary ray、shadow ray&lt;/p&gt;
&lt;p&gt;第一步，求光线和物体表面的交点
$$r(t)=o+td,\quad 0\leq t&amp;lt;\infty$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;谁家解析几何
其实就是求解$f(o+td)=0$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;光线和Triangle Mesh的交点又该怎么求？
分为两步，先看光线和三角形所在平面的交点，再判断点是否在三角形内（前面学的用上了）
平面可以由一个点和一根法线确定。进而写出平面方程。
$$(p-p_0)\cdot N = 0$$
和光线方程联立解得
$$t = \frac{(p&apos;-o)\cdot N}{d\cdot N}$$
再判断点是否在三角形内即可&lt;/p&gt;
&lt;p&gt;不满意。希望找到一步到位的算法。
&lt;strong&gt;Moller Trumbore Algorithm&lt;/strong&gt;
找交点其实就是解一个线性方程组
$$\vec{\mathbf{O}}+t\vec{\mathbf{D}}=(1-b_{1}-b_{2})\vec{\mathbf{P}}&lt;em&gt;{0}+b&lt;/em&gt;{1}\vec{\mathbf{P}}&lt;em&gt;{1}+b&lt;/em&gt;{2}\vec{\mathbf{P}}_{2}$$&lt;/p&gt;
&lt;p&gt;还有一个问题，一个模型那么多三角形，要一个个去判断，太慢了，如何加速？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bounding Volumes&lt;/strong&gt;
找一个东西把物品包起来，得到的叫包围盒
如果碰不到包围盒，那么一定也碰不到物品&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有种放缩的感觉&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;取长方体包围盒。长方体本质是三对半平面形成的交集。
而且，这个包围盒还要取轴对齐包围盒（AABB，Axis-Aligned Bounding Box），即与坐标轴平行的。【方便计算】&lt;/p&gt;
&lt;p&gt;思考。光线和长方体各对平面相交的时刻已经能求了。我们只需对这些$t_{min},t_{max}$区间求交集。就能判断什么时候光线进入了盒子。
具体到算法上，$t_enter = max{t_{min}},t_exit = min{t_{max}}$，去看是否有$t_{enter}&amp;lt;t_{exit}$
当然还要记得考虑$t&amp;gt;0$，以及光源在盒子里的特殊情况&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;弹幕有人说算法题，感觉这个问题确实很适合出OJ题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;总之，ray和AABB相交当且仅当
$t_{enter} &amp;lt; t_{exit} AND t_{exit} \geq 0$&lt;/p&gt;
&lt;h1&gt;Lecture 14 Ray Tracing 2&lt;/h1&gt;
&lt;p&gt;具体如何使用AABB加速光线和物品求交点？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Uniform Spatial Partitions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uniform Grids步骤
1.Finding bounding box
2.Create grid
3.Store each object in overlapping cells
4.看光线是否和box相交，也即是否通过被标记的cell，如果是，则去判断一下和物品是否相交&lt;/p&gt;
&lt;p&gt;在这里有个小问题，是光线和cell的相交是怎么判断的。这个和直线的光栅化问题很相似。&lt;/p&gt;
&lt;p&gt;cell划分不能太稀疏也不能太密集
这样的分割有时会出大问题，即不同物品大小不太均匀的时候&lt;/p&gt;
&lt;p&gt;下面才是认真的Uniform Spatial Partitions
Oct-Tree 八叉树
KD-Tree 每次只砍一刀多出来一块空间，横竖交替（二叉树？）
BSP-Tree 可以斜着砍，但不好算&lt;/p&gt;
&lt;p&gt;以KD-Tree为例讲解。很直白。&lt;/p&gt;
&lt;p&gt;还有一个问题，怎么判定box和物品是不是相交呢。暂且不表。&lt;/p&gt;
&lt;p&gt;还有一个劣势，一个物品很容易和很多Box都有交集。这会大大降低效率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Object Partitions &amp;amp; Bounding Volume Hierarchy (BVH)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把物品们分成两堆，分别求box，不断划分下去，也是用KD-Tree的办法，这样一个物品只可能出现在一个box里&lt;/p&gt;
&lt;p&gt;这会引起一个新问题，不同的Box可能相交。但这个问题并不大。&lt;/p&gt;
&lt;p&gt;问题：具体如何划成两堆？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always choose the longest axis in node&lt;/li&gt;
&lt;li&gt;Split node at location of median object 涉及快速选择算法 ~ O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;问题：划分到什么程度？
node包括的elements足够少的时候。&lt;/p&gt;
&lt;p&gt;BVH的遍历。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic radiometry（辐射度量学）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对光和物品的作用有更加精确的描述。&lt;/p&gt;
&lt;p&gt;新名词&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Radiant energy&lt;/strong&gt; 单位：Joule
&lt;strong&gt;Radiant flux / power&lt;/strong&gt; 单位：Watt/lumen
&lt;strong&gt;Radiant Intensity&lt;/strong&gt;
即单位时间单位立体角上的能量
$$I(\omega) = \frac{d\Phi}{d\omega}$$
单位：$\frac{lm}{sr} = cd = candela$
什么是立体角？
$$\Omega = \frac{S}{r^2}$$
球面坐标系
微分立体角
$$dS = r^2sin\theta d\theta d\phi$$&lt;/p&gt;
&lt;h1&gt;Lecture 15 Ray Tracing 3&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Irradiance&lt;/strong&gt;
$E(x) = d\Phi(x)/dA$ 单位：lm/m^2 = lux
引入这个概念后可以解释 Irradiance随距离的平方衰减&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Radiance&lt;/strong&gt;
$$L(p,\omega) = d^2\Phi(p,\omega) / d\omega d Acos\theta$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BRDF（Bidirectional Reflectance Distribution Function）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用于研究不同方向反射光线的分布&lt;/p&gt;
&lt;p&gt;$$f_r(\omega_i\to\omega_r)=\frac{\mathrm{d}L_r(\omega_r)}{\mathrm{d}E_i(\omega_i)}=\frac{\mathrm{d}L_r(\omega_r)}{L_i(\omega_i)\cos\theta_i:\mathrm{d}\omega_i}:\left[\frac{1}{\mathrm{sr}}\right]$$
$$L_r(\mathrm p,\omega_r)=\int_{H^2}f_r(\mathrm p,\omega_i\to\omega_r) L_i(\mathrm p,\omega_i) \cos\theta_i \mathrm d\omega_i$$&lt;/p&gt;
&lt;p&gt;问题：光线反复反射，产生递归&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Rendering Equation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$L_{o}(p,\omega_{o})=L_{e}(p,\omega_{o})+\int_{\Omega^{+}}L_{i}(p,\omega_{i})f_{r}(p,\omega_{i},\omega_{o})(n\cdot\omega_{i}) \mathrm{d}\omega_{i}$$&lt;/p&gt;
&lt;p&gt;补上了“自己产生的光”&lt;/p&gt;
&lt;p&gt;进而，考虑上多光源、面光源&lt;/p&gt;
&lt;p&gt;进而考虑其他物体&lt;/p&gt;
&lt;p&gt;最后转化为解方程问题&lt;/p&gt;
&lt;p&gt;得到全局光照的概念&lt;/p&gt;
&lt;p&gt;$$L = E + KE + K^2E + K^3 + ...$$&lt;/p&gt;
&lt;p&gt;怎么解下节课再说，要用到概率论的方法&lt;/p&gt;
&lt;h1&gt;Lecture 16 Ray Tracing 4&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Monte Carlo Integration&lt;/strong&gt;
$$X_i\sim p(x)$$
$$F_N = \frac{1}{N}\sum_{i=1}^{N}\frac{f(X_i)}{p(X_i)}$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Path Tracing&lt;/strong&gt;
解决Whitted-Style Ray Tracing的一些问题。漫反射不能不考虑。
specular or glossy&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;经典The Utah teapot、The Cornell box模型
color bleeding现象...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;$\omega_i$就是我的随机变量
用蒙特卡洛积分，直接光照很容易就解出来了
稍加修改（考虑其他物品反射来的光线），就成全局光照的算法了&lt;/p&gt;
&lt;p&gt;问题1：光线数量爆炸。可以只打出一条光线！n=1！这就叫路径追踪！
妙啊！把无限种路径的问题，转化成无数次试验但确定路径的问题&lt;/p&gt;
&lt;p&gt;问题2：递归无限深度啊！
Russian Roulette（RR）法！
给定概率p发射光线最后结果再除以p，以1-p的概率不发出光线。期望不变。&lt;/p&gt;
&lt;p&gt;问题3： not really efficient
SPP（samples per pixels）低的时候效果很不好
均匀采样的话，有很多path被浪费了！能不能换一种采样方法？
我只在光源上进行采样！
但这和积分域不一样了啊！
去改渲染方程！$d_\omega\to d_A$
$$d\omega=\frac{dA:\cos\theta^{\prime}}{|x^{\prime}-x|^2}$$
剩下非光源的部分还是用原方法&lt;/p&gt;
&lt;p&gt;问题4：直接光照的时候有无遮挡&lt;/p&gt;
&lt;p&gt;点光源怎么办？太难了。。。在此不表。&lt;/p&gt;
&lt;p&gt;思考：采样这一点是怎么实现的？怎么采样最好？随机数low discrepancy sequences？Mutiple important sampling？pixel reconstruction filter？Radiance怎么转化成color？（Gamma校正，curves, color space）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;FEAR THE SCIENCE&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Lecture 17 Materials and Appearances&lt;/h1&gt;
&lt;p&gt;最强的渲染器也只支持40种材质，靠美工&lt;/p&gt;
&lt;p&gt;图形学中如何定义materials？渲染方程中是如何体现出来的？BRDF！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Material == BRDF&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假设是漫反射，&lt;strong&gt;入射和反射都是均匀的&lt;/strong&gt;
根据能量守恒：
$$\begin{aligned}L_{o}(\omega_{o})&amp;amp;=\int_{H^2}f_rL_i(\omega_i)\cos\theta_i:\mathrm{d}\omega_i\&amp;amp;=f_rL_i\int_{H^2}\cos\theta_i:\mathrm{d}\omega_i\&amp;amp;=\pi f_rL_i\end{aligned}$$
，得到$f_r = \frac{\rho}{\pi}$，其中$\rho$是反射率，也即代表颜色，如果物品是白色的不吸收光，$\rho=1$&lt;/p&gt;
&lt;p&gt;还有其他材质......
&lt;strong&gt;Glossy material BRDF&lt;/strong&gt;
&lt;strong&gt;Perfect Specular Reflection BRDF&lt;/strong&gt;
反射角的计算&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;caustics 如海底的条状高光现象&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;斯涅尔定律
全反射现象&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;snell&apos;s window / circle 现象&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;涉及折射的是BTDF，和BRDF合称BSDF
菲涅尔项：解释多少能量发生了反射，多少发生了折射（垂直的时候折射多，反之反射多），有公式，和两个折射率和夹角有关
算起来很麻烦，所以有一个Schlick&apos;s approximation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microfacet Material&lt;/strong&gt;
离得很远的时候，细节看不到的，假设物品粗糙，但是是平的
但在microsurface层面，是有很多起伏的，但是完全镜面反射的&lt;/p&gt;
&lt;p&gt;全都向上，镜面反射
有些材质法线大都朝上，glossy的
各方向都有，diffuse的&lt;/p&gt;
&lt;p&gt;所以用法线分布来量化材质！
计算BRDF时。用到菲涅尔项。查询多少法线方向和half vecotr方向一致，用到法线分布。还要用到一个shadowing-masking term，考虑microsurface的互相遮挡，自遮挡自投影，在光线和平面夹角很小的时候会出现，这叫grazing angle。
用到这套思想的都可以叫微表面模型。&lt;/p&gt;
&lt;p&gt;isotropic / Anisotropic Materials (BRDFs)
各向同性材质 / 各向异性材质
例子：电梯间的奇怪高光
即微表面法线分布的方向性
也可以从BRDF角度考虑
例子：天鹅绒&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BRDF的性质&lt;/strong&gt;
非负性
线性
可逆性
能量守恒
如果各向同性，四维变三维，而且方位角不用考虑正负&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Measuring BRDF&lt;/strong&gt;
测量算法、加速算法、存储算法、压缩算法...
经典库：MERL，测量了很多材质的BRDF&lt;/p&gt;
&lt;h1&gt;Lecture 18 Advanced Topics in Rendering&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Advanced Light Transport&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Unbiased light transport methods&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bidirectional Path Tracing (BDPT)
半路径的连接 双向路径追踪&lt;/li&gt;
&lt;li&gt;Metropolis Light Transport (MLT)
马尔科夫链（MCMC）生成一系列样本去符合PDF
一个局部的方法，适合做复杂场景
很难估计收敛速度
比较dirty，不能保证不同像素以相同速度收敛&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Biased, but consistent&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Photon Mapping 光子映射
适合caustics场景、SDS（specular-diffuse-specular）
介绍一种实现方法
从光源出发，光子打到difuse的物品时，光子停住，记录好
然后从摄像机开始打subpath，直到diffuse
计算local density estimation
k近邻。k越大越好嘛？会变糊。biased！有偏估计。
光子趋于无限多，才能无偏。&lt;/li&gt;
&lt;li&gt;Vertex Connection and Merging, VCM
把BDPT和Phonton Mapping结合。&lt;/li&gt;
&lt;li&gt;Instant Radiosity, IR
认为subpath停住的地方全都看成新的光源&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Advanced Appearance Modeling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Non-surface models&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Participating Media
例子：云，可能发生emission和absorbtion
怎么表现散射？相位函数！Phase Function&lt;/li&gt;
&lt;li&gt;Hair Appearance
有色高光？
Kajiya-kay Model是一个经典头发的模型
Marschner Model 更加真实
还有多次散射的模拟。。。
动物毛发模型？Hair vs. Fur 髓质的作用
Double Cylinder Model Yan佬的工作&lt;/li&gt;
&lt;li&gt;Granular Material 颗粒材质&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Surface models&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Translucent Mateirial 如玉石、水母
进去之后大量散射，从其他面穿出
这叫次表面反射
BRDF -&amp;gt; BSSRDF 一个延申
渲染方程也要进行更改
近似： Dipole Approximation 比如手指+手电就好像手指里面多了个光源&lt;/li&gt;
&lt;li&gt;Cloth
fibers - ply - yarn
woven or knitted
空间划分？渲染每根纤维？都可以&lt;/li&gt;
&lt;li&gt;真实世界的特殊效果 划痕？ 回忆微表面模型，考虑法线更加复杂的分布&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;太慢了！&lt;/p&gt;
&lt;p&gt;各种p-NDFS&lt;/p&gt;
&lt;p&gt;考虑光的波动性&lt;/p&gt;
&lt;p&gt;Procedural Appearance
定义在空间中 noise function 随用随取不存储而是现算&lt;/p&gt;
&lt;h1&gt;Lecture 19 Cameras, Lenses and Light Fields&lt;/h1&gt;
&lt;p&gt;Imaging = Synthesis + Capture
光线追踪、光栅化都属于Synthesis，拍照则属于Capture&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Camera&lt;/strong&gt;
Shutter 快门 控制光能否进入
Sensor&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么物品直接放在光传感器前上不会成像？相当于记录irradiance。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pinhole Camera 无景深&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Field of View (FOV) 视场&lt;/strong&gt;
$$FOV = 2 arctan(\frac{h}{2f})$$
h是传感器高度，f是焦距（pinhole和sensor之间的距离）
h通常认为是35mm-format film，所以焦距就能决定视场
手机说的是等效焦距&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/tengjun12/p/18468300&quot;&gt;【个人博客】摄影相关&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exposure 曝光&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$H = T \times E$$
$$Exposure = time \times irradiance$$
光圈就是挡光用的，f-stop来调节大小，
ISO gain 感光度 相当于一个后期处理，直接往结果上乘，越大越亮，但是噪声也更大了
F后面的数越小，光圈越大。这个F数就是直径的倒数。
快门速度 运动模糊 某种程度可以看出一种反走样
rolling shutter问题 螺旋桨的扭曲
高速摄影 or 长曝光摄影
大光圈产生景深&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lens&lt;/strong&gt;
研究理想化的薄棱镜
$1/f = 1/z_i + 1/z_o$ 三角形相似，很好证明
&lt;strong&gt;Defocus Blur&lt;/strong&gt;
Computing Circle of Confusion (COC) Size 弥散圆
其大小和透镜大小成正比！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ray Tracing Ideal thin lens&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Depth of Field&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Light Field / Lumigraph&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>迷思-年终总结-2023</title><link>https://blog.stivine.fun/posts/reflection-yearly-summary-2023/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/reflection-yearly-summary-2023/</guid><description>流水账式地想想都发生了什么吧。</description><pubDate>Sun, 31 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2023，先流水账式地想想都发生了什么吧。&lt;/p&gt;
&lt;p&gt;主要分为大一下学期、大学第一个暑假、大二上学期三个阶段。&lt;/p&gt;
&lt;p&gt;大一下学期，居然参加了两次期末啊。22年底疫情导致的大一上学期期末考试延期。这个学期是专注的，纯粹的。晚晚遇到的风波，和***一起长跑，开始喜欢上出门乱逛。喜欢的微积分课，和舍友共同学习生活的日子。晚晚的生日会，我鼓起勇气录制视频。军训，整体很开心，可惜没能陪着**玩的开心。暑假，学车。大二上学期，来到这座城市，爱上这座城市，爱上交通。初步接触科研。动漫社。
但这也是我对我自己学习上最不满意的一个学期吧。拿奖学金，学习上却是没了劲头。&lt;/p&gt;
&lt;p&gt;如今的我，已经能够一定程度上超出学生的普遍局限性，用较高地眼光看这个世界，看自己的周围，认识社会的运作，也能够对未来有一个全新的认知。我有比身边人更强的判断真伪的能力，判断实力高低的能力。这些是经过检验的，源自我广大的知识面。但是，我一直迷茫的事情总归要有一个答案——我到底应该专精于什么方面？我应该如何利用我的优势，去支撑我的前进？另一方面，我能够判断、认识优秀的人，从心底敬佩尊重他们，我应该如何走到他们的身边？&lt;/p&gt;
&lt;p&gt;这是我2023年没能回答的问题，也是我2024年应该解决的问题。&lt;/p&gt;
&lt;p&gt;对自己新学期的几点要求。但一说要求，总有些完不成的意味，但下面几点，都是性价比极高的，即不费多大力气仅凭习惯的惯性就可完成，而且给人的好处多多。&lt;/p&gt;
&lt;p&gt;一、规律的生活习惯&lt;/p&gt;
&lt;p&gt;洗衣服、吃饭、睡觉、喝水，都知道怎么做。再加上每天写博客。&lt;/p&gt;
&lt;p&gt;跑步，这一点我大二上学期简直做的太差了。跑步多好啊，真浪费不了多少时间啊。看看能不能拉别人一起跑。
为了体测成绩可以专门去练练坐位体前屈。&lt;/p&gt;
&lt;p&gt;二、重拾对知识的好奇&lt;/p&gt;
&lt;p&gt;你知道的，我热爱学习。大学这一年半的很多事却是消磨了我太多热情。不怪别人。这只怪我一直没有意识到这件事情。&lt;/p&gt;
&lt;p&gt;三、平衡娱乐方式&lt;/p&gt;
&lt;p&gt;出门探索、资深二次元、创作，足矣。
热爱生活相关的都没问题啦！比如多探索点吃的喝的。
还有了个想法，收集笑话！我小学的时候就喜欢讲笑话！！讲笑话多好啊！！&lt;/p&gt;
&lt;p&gt;我希望我自己能始终热爱着积极向上的那些东西。不是为了别人眼中的成功。仅仅想留住那个不忘初心的少年。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;更具体的想法。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;把自己的一些做视频的想法也好，做软件的想法也好，当成大作业去完成。总之是行动起来。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每天都留出时间给自己学习外语用（英语也好，日语也好）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;去看了眼去年的年终总结，我很喜欢。
有一点让我感到奇怪。去年的年终总结中我感谢别人是很重要的一个部分，怎么到了今年就没想起这个环节呢？
感觉最近的日子里，我的心情不是很舒畅啊。
我会努力调整的。&lt;/p&gt;
</content:encoded></item><item><title>迷思-年终总结-2022</title><link>https://blog.stivine.fun/posts/reflection-yearly-summary-2022/</link><guid isPermaLink="true">https://blog.stivine.fun/posts/reflection-yearly-summary-2022/</guid><description>这一年真的过的太精彩了。</description><pubDate>Sat, 31 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这一年真的过的太精彩了。&lt;/p&gt;
&lt;p&gt;但其实可能不是我那么想要记住的一年，因为这一年承载了我太多的失落，甚至痛苦（亲人的离去）。
上半年备战高考的过程中，我再一次证明了自己不会放弃的决心和追求卓越的劲头，在最后时刻让自己的做题能力回到了顶尖水平，我很高兴自己的会议记录本上记录了很多只言片语，那是我咬牙坚持的投影。那些话是缓解我焦虑的良药。说真的，那段时间我才感受到什么是真正的焦虑，其他同一时间回来的奥赛生都名列前茅了，我还在一次一次地辜负别人的信任，只要我某个过程没完成好，不焦虑是不可能的。但最后我熬过去了。真的熬过去了。&lt;/p&gt;
&lt;p&gt;但高考还是寄了，寄在数学和物理，两个我最推崇的学科。真的很遗憾。真的很遗憾。我其实当时想的很多，我想到直到最后我也没在高中拿过150分，我想到我大概率也不会选择这两个专业，但这真的是我最喜欢的两个学科了。尤其是数学。当时的我下定决心，即使我成功进入了计算机相关的专业，我也要要求自己的数学在顶尖水平。
当然在后半年我完成得并不好。&lt;/p&gt;
&lt;p&gt;然后是报考志愿，能进现在的大学非常非常开心，尽管有很多不确定性，尽管要和预期不同的城市，但我很快接受了这样的现实，满怀信心准备大干一场。事实也确实如此，学校没有让我失望。各种优质的资源等着我。
但我还是让我自己失望了。自诩自学能力佳，但我的大量时间还是挥霍了。没有用来学技能，没有用来卷成绩，没有用来搞创作。怪不了任何人。大学下半年自己很多办事的能力提升了不少，自己的时间学会了自己安排，但这个安排真的很难令人完全满意。&lt;/p&gt;
&lt;p&gt;作为年终总结，似乎上面说的又太丧了些。。。让我们笔锋一转——&lt;/p&gt;
&lt;p&gt;2022认识了太多有趣的人！！！！这个方面我永远都是幸运的啊。我似乎永远都在认识可爱的优秀的人儿。
***的大佬们，****的舍友们，VC群的群友们......爆炸电台真的是太棒啦！ASOUL也在越来越好！呜呜呜在学校里完全没有创作的动力是怎么回事捏，明年得多报名一些活动来激励自己。我可是从小就说过要当大up的人。剪辑还是得接着学啊。
也真的感谢几位老友，就算不在身边，也是让人安心。（此处@***,***,***,***,**,***,***,***......
&amp;lt;!--406--&amp;gt;
&amp;lt;!--0519--&amp;gt;
&amp;lt;!--@ysy,wzy,lwy,myz,xzh,tp,yyt,jmh--&amp;gt;
2023年，希望自己多出去玩，坚持那些自己应该坚持的东西，一定能成为更好的自己！
学习！创作！这两个在2022都没搞好的东西，2023势必拿下！！！&lt;/p&gt;
&lt;p&gt;（知识相关的总结待续）&lt;/p&gt;
&lt;p&gt;课内的东西就不多说了。很高兴自己看了高代，但不知为何放弃了淑芬。。。我果然还是更喜欢实用主义的数学？剪辑水平其实是没什么提升，这个2023要特别注意，不要放弃自媒体创作，昨天为了做游戏还下载了AI（画矢量图那个），需要学的真的还有很多啊。编程水平差太远了。。。。为了备战期末考试，必须要在c语言的算法相关内容花大量时间。一想期末考试就心烦。。。。数学反而是最不需要担心的，物理是真的不愿意翻，英语的话多刷点题（主要还是啃老本）就可以了。如果额外多学习啥的话，主要是编程、微积分、离散还有一些自媒体创作技能。不知道会不会让我去考科目一，不过那玩意应该是挺简单的。其实与其操心学什么，不如骂骂自己，怎么学的东西越来越少了。读书，看番，玩gal，其实都比想象中的少不少呢。。。&lt;/p&gt;
</content:encoded></item></channel></rss>