[{"content":"VMware虚拟机安装 暂且记录一下安装的东西是啥意思\n基本信息\n全名：这是计算机名字，在命令行终端显示\n用户名：默认用户名，在命令行终端显示\n虚拟机名字和安装位置\n虚拟机名称：指VMware管理虚拟机用的 最大磁盘大小\n最大磁盘大小：虚拟机占用宿主机的最大容量，这个可以调整大一些，因为虚拟机实际占用大小是慢慢增加的，而不是一开始就是指定值大小，并且这个值后期可以修改。 自定义硬件\n不一定给的配置越高虚拟机越流畅，因为一方面会导致宿主机的资源太少，另一方面宿主机调动这么多虚拟机资源也会有负担。这个配置虚拟机创建好之后也可以再更改\n三种网络适配器：\n桥接模式： 虚拟机直接连接到主机的物理网络，就像是网络中的一个独立主机。 虚拟机将会获得与物理网络相同的IP地址（通常通过DHCP分配），因此它可以与网络中的其他物理设备进行直接通信。 适用于需要虚拟机与外部网络设备进行完全交互的场景，例如测试网络服务和配置。 NAT模式： 允许虚拟机通过主机的IP地址访问外部网络。 虚拟机在一个虚拟的内部网络中运行，具有独立的私有IP地址，通过主机的网络连接进行地址转换。 限制了外部网络直接访问虚拟机，但虚拟机可以主动访问外部网络。适合需要访问互联网但不需要被外部设备访问的场景。 仅主机模式： 虚拟机与主机之间建立一个隔离的网络，不与外部网络相连。 虚拟机可以与主机以及同一模式下的其他虚拟机进行通信，但不能直接访问外部网络。 适用于需要隔离测试环境或模拟内部网络的情况，不需要与外部设备通信。 综上，要上网选择 NAT 快照 虚拟机快照用于记录虚拟机在特定时间点的状态，包括虚拟机的内存状态、硬盘数据以及相关的设置。当你创建一个快照时，相当于给当前的虚拟机做了一个“备份”，可以在需要时恢复到此状态。我们在刚创建好虚拟机时创建一个快照方便后续恢复到这个最“纯净”的时候。\n恢复快照可以让虚拟机恢复到拍摄快照对应的时刻。比如可以在进行某个危险操作之前先拍摄快照，如果操作出错那么就可以通过恢复快照回到出错之前的状态，就不再需要重新安装虚拟机了。\nSadServers场景 因为是初学者，可能某些理解不太对。以下仅为个人理解。\n\u0026ldquo;Saint John\u0026rdquo;: what is writing to this log file? Description: A developer created a testing program that is continuously writing to a log file /var/log/bad.log and filling up disk. You can check for example with tail -f /var/log/bad.log. This program is no longer needed. Find it and terminate it. Do not delete the log file.\nSolution：\n找到进程：fuser -v /var/log/bad.log kill 进程号 \u0026ldquo;Saskatoon\u0026rdquo;: counting IPs. Description: There\u0026rsquo;s a web server access log file at /home/admin/access.log. The file consists of one line per HTTP request, with the requester\u0026rsquo;s IP address at the beginning of each line.\nFind what\u0026rsquo;s the IP address that has the most requests in this file (there\u0026rsquo;s no tie; the IP is unique). Write the solution into a file /home/admin/highestip.txt. For example, if your solution is \u0026ldquo;1.2.3.4\u0026rdquo;, you can do echo \u0026quot;1.2.3.4\u0026quot; \u0026gt; /home/admin/highestip.txt\nTest: The SHA1 checksum of the IP address sha1sum /home/admin/highestip.txt is 6ef426c40652babc0d081d438b9f353709008e93 (just a way to verify the solution without giving it away.)\nSolution：\n提取IP文件的地址，统计次数，并排序后打印第一个 awk '{print $1}' /home/admin/access.log | sort | uniq -c | sort -rn | head -1 | awk '{print $2}' {print $1} ：每行的第一个字段（IP地址） sort ：将 IP 地址按字母顺序排序 uniq -c ：统计每个唯一 IP 地址的出现次数，-c ：在每行前面显示该 IP 出现的次数 sort -rn ：按次数降序排序，-r：降序排序，-n：按数值排序 head -1：只取第一行 awk '{print $2}'：从格式 次数 IP地址 中提取 IP 地址（第二列） 用echo写入home/admin/highestip.txt \u0026ldquo;Bucharest\u0026rdquo;: Connecting to Postgres Description: A web application relies on the PostgreSQL 13 database present on this server. However, the connection to the database is not working. Your task is to identify and resolve the issue causing this connection failure. The application connects to a database named app1 with the user app1user and the password app1user.\nCredit PykPyky\nTest: Running PGPASSWORD=app1user psql -h 127.0.0.1 -d app1 -U app1user -c '\\q' succeeds (does not return an error).\nSolution：\n执行命令PGPASSWORD=app1user psql -h 127.0.0.1 -d app1 -U app1user -c '\\q'\n1- psql: error: FATAL: pg_hba.conf rejects connection for host \u0026#34;127.0.0.1\u0026#34;, user \u0026#34;app1user\u0026#34;, database \u0026#34;app1\u0026#34;, SSL on 2- FATAL: pg_hba.conf rejects connection for host \u0026#34;127.0.0.1\u0026#34;, user \u0026#34;app1user\u0026#34;, database \u0026#34;app1\u0026#34;, SSL off sudo -u postgres psql -t -P format=unaligned -c 'SHOW hba_file;'\n找到这个文件的位置，进入目录 查看pg_hba.conf\n发现reject\n最前面两条“全拒绝”规则把任何 TCP/IP 连接都挡掉了，reject 行按顺序匹配，只要客户端走 TCP（含 127.0.0.1），还没轮到后面的 注释两条规则或移到最后\n保存后重载配置\nsudo systemctl reload postgresql Reason：\npg_hba.conf 文件中最前面两条规则使用了 reject 方法，无条件拒绝了所有 TCP/IP 连接（包括 localhost 的 127.0.0.1）\npg_hba.conf 是 PostgreSQL 的主机基认证（Host-Based Authentication）配置文件，它控制谁（user）、从哪里（host）、连接哪个数据库（database）、用什么方式（local 或 host）可以访问，以及采用何种认证方法（md5、trust、reject 等）。 文件中的规则按从上到下的顺序逐一匹配：PostgreSQL 会从第一行开始检查，找到第一条匹配的规则就立即应用它，后面的规则不会再看（官方文档明确强调顺序的重要性）。 通常是人为误配置或安全加固时遗留的问题：\n有人想阻止外部远程连接，把 reject 规则加在前面作为“默认拒绝”，但忘记或没意识到 localhost 的 TCP 连接（127.0.0.1）也会被挡。 正常情况下，localhost TCP 连接应该有单独的允许规则，如 1host all all 127.0.0.1/32 md5 2host all all ::1/128 md5 \u0026ldquo;Alexandria\u0026rdquo;: The Vanishing Backups Description: A critical backup cron job has silently stopped working 3 days ago. The backup script is located at /opt/backup/backup.sh and should create daily backups in /var/backups/daily/, but no new backups have been created recently.\nLooking at the backup directory, you can see old backup files from a few days ago, proving the system used to work. However, there are no error emails, no obvious error logs, and the cron service appears to be running normally.\nFix ALL issues preventing the backups from running, so that backups are created successfully and reliably.\nTest directory: /var/backups/daily/ Backup script: /opt/backup/backup.sh\nTest: The solution will be validated by checking if a backup file has been created in the last 10 minutes.\nThe \u0026ldquo;Check My Solution\u0026rdquo; button runs the script /home/admin/agent/check.sh, which you can see and execute.\nSolution：\n手动运行脚本 sudo /opt/backup/backup.sh 发现锁文件：\n1admin@i-0866e3e49c14825af:~$ sudo /opt/backup/backup.sh 2Error: Backup already running (lock file exists) 查看cron服务状态 systemctl status cron.service：\n1admin@i-0866e3e49c14825af:~$ systemctl status cron.service 2● cron.service - Regular background program processing daemon 3 Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled) 4 Active: active (running) since Sat 2025-12-27 07:29:23 UTC; 1min 25s ago 5 Invocation: b0a20ceeebc04b07ae9aa0519b46a888 6 Docs: man:cron(8) 7 Main PID: 763 (cron) 8 Tasks: 1 (limit: 503) 9 Memory: 496K (peak: 1.8M) 10 CPU: 23ms 11 CGroup: /system.slice/cron.service 12 └─763 /usr/sbin/cron -f 13Dec 27 07:29:23 i-0866e3e49c14825af systemd[1]: Started cron.service - Regular background program pro\u0026gt; 14Dec 27 07:29:23 i-0866e3e49c14825af cron[763]: (CRON) INFO (pidfile fd = 3) 15Dec 27 07:29:23 i-0866e3e49c14825af cron[763]: (CRON) INFO (Running @reboot jobs) 16Dec 27 07:30:01 i-0866e3e49c14825af CRON[1469]: pam_unix(cron:session): session opened for user root(\u0026gt; 17Dec 27 07:30:01 i-0866e3e49c14825af CRON[1471]: (root) CMD (/opt/backup/old_backup.sh \u0026gt; /dev/null 2\u0026gt;\u0026amp;\u0026gt; 18Dec 27 07:30:01 i-0866e3e49c14825af CRON[1469]: pam_unix(cron:session): session closed for user root Dec 27 07:30:01 ... CRON[1471]: (root) CMD (/opt/backup/old_backup.sh \u0026gt; /dev/null 2\u0026gt;\u0026amp;1) 说明crontab里配置的仍然是 old_backup.sh，而不是正确的backup.sh。\n编辑root的**crontab ** sudo crontab -e\nold_backup.sh 改为 backup.sh 查看ls /opt/backup/ 并删除锁文件 sudo rm -f /opt/backup/backup.lock\n重新启动 sudo /opt/backup/backup.sh\nReason：\ncrontab 配置的脚本路径错误 root 的 crontab 里写的仍然是旧脚本：/opt/backup/old_backup.sh 正确的、当前正在使用的备份脚本是 /opt/backup/backup.sh 很可能之前有人把脚本重命名或替换成了新版本，但忘记更新 cron 配置 残留的锁文件导致手动测试也失败 正确的 backup.sh 脚本内部有锁机制：会检查 /opt/backup/backup.lock 是否存在。 如果锁文件存在，就认为“上一次备份还在运行”，直接报错退出： Error: Backup already running (lock file exists) 很可能是几天前备份异常中断（比如机器重启、进程被杀等），导致锁文件没被正常删除，残留了下来。 ","date":"2026-01-28T21:37:24+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/linux-sadsevers-%E8%A7%A3%E9%A2%98%E6%80%9D%E8%B7%AF/","title":"[Linux] SadSevers 解题思路"},{"content":"场景 SSH连接AutoDL报错：\nThe remote host may not meet VS Code Server\u0026rsquo;s prerequisites for glibc and libstdc++ (The remote host does not meet the prerequisites for running VS Code Server)\n原因： 之前是能连接上的，但隔了一段时间很久没用过VScode，以为是remote-SSH的问题，卸载重装也没用。 VS Code 从 1.99 版本（2025年3月） 开始，要求远程服务器的： glibc ≥ 2.28（如 Ubuntu 20.04+、CentOS 8+） libstdc++ 需包含 GLIBCXX_3.4.25 及以上符号版本，若服务器运行老旧系统（如 Ubuntu 18.04、CentOS 7），其默认库版本不满足要求 参考博客，解决Vscode连接服务器报错 ，怀疑是Vscode自动更新的问题，尝试降版本 解决方案 方案1：降级 禁用Vscode自动更新，参考博客 禁用Vscode自动更新 下载安装Vscode1.85版本 注意：\n安装前要退出正在运行的Vscode\n如果出现需要选择安装目录的情况，一定要选择当前VSCode 软件的安装目录。\nVSCode小操作 复制一行 命令太长，想直接复制一行，光标移动到那一行前，直接ctrl+c就复制了 shift+alt+↓自动把这一行内容复制到下一行 ","date":"2025-11-13T16:18:41+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/vscode-vscode%E8%BF%9E%E6%8E%A5%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%98%BE%E7%A4%BAthe-remote-host-may-not-meet-vs-code-servers-prerequisites/","title":"[VSCode] VSCode连接远程服务器显示\"The remote host may not meet VS Code Server‘s prerequisites\""},{"content":"参考 将 Github Pages 个人博客录入搜索引擎（以 Bing 为例） - RainbowC0 - 博客园 即可，但是还是为自己留档稍微写写\n目前百度不再允许导入 gitee.io 和 github.io 等一级域名的网站，理由是这些域名满额了\n添加网站 打开Bing Webmaster Tools，并登录。可以选择三种登录账号：Google、Microsoft、Facebook，这里我用 Microsoft 账号登录。 第一次登录会提升添加网站，如果已经通过GSC，可以用GSC。这里我手动添加，直接复制博客主页的URL粘贴即可 验证网站 有三个验证方式：文件、标签、CNAME ，这里选择文件验证（第一个） 点击 BingSiteAuth.xml 下载验证文件，然后上传到 Github Page 的根目录下，再更新 Github Page 部署，这样个人博客就被更新了，可以访问网站根目录下找到 BingSiteAuth.xml。 **注意：如果是用 Hugo 构建的网站，需要把验证文件放到项目的 static 文件夹下，然后运行 hugo 重新生成网页并上传。**然后点击“验证”即可。 添加网站地图 点击网站地图 用 Hugo 生成的博客网站会自带 sitemap.xml 文件，通常放在网站根目录下，点击“提交网站地图”，输入 https://博客主页URL/sitemap.xml，然后提交。 验证成功 最后可以用搜索引擎尝试搜一下自己的博客网站看看有没有结果，对于其他搜索引擎大体也是如此。\n","date":"2025-10-31T11:04:25+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/site-%E5%B0%86-github-pages-%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E5%BD%95%E5%85%A5%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E4%BB%A5-bing-%E4%B8%BA%E4%BE%8B/","title":"[Site] 将 Github Pages 个人博客录入搜索引擎（以 Bing 为例）"},{"content":"部署到Vercel 因为我的博客是已经部署在 github page 上的，所以已经有了源码仓库和公开仓库。\n一般来说 白嫖 博客在github page上部署结束就可以了，具体可以参考【Hugo】Hugo + Github 免费部署自己的博客。但是考虑到后续的管理，还是打算在Vercel上也部署。\nHugo官方的文档【Host on Vercel】其实已经比较详细了，按照他的步骤来就行，只不过是英文的看起来比较费劲。\n准备 创建并登录 GitHub 账号\n创建仓库并在本地初始化。（因为我已经有了就不细说）\n使用Github授权登录Vercel\n添加文件 在博客主文件夹添加 vercel.json 和 build.sh 文件，具体可参考Hugo官方文档。\n这个 JSON 文件告诉 Vercel 如何构建你的站点（指定构建命令和输出目录）。\n1// vercel.json 2{ 3 \u0026#34;$schema\u0026#34;: \u0026#34;https://openapi.vercel.sh/vercel.json\u0026#34;, 4 \u0026#34;buildCommand\u0026#34;: \u0026#34;chmod a+x build.sh \u0026amp;\u0026amp; ./build.sh\u0026#34;, 5 \u0026#34;outputDirectory\u0026#34;: \u0026#34;public\u0026#34; 6} 这个 Bash 脚本在 Vercel 环境中安装 Hugo、Go、Node.js 和 Dart Sass，然后构建站点。复制整个脚本 。要注意替换成你博客所用的hugo版本，可以在博客主文件夹打开命令行，运行 hugo version 查看 1# build.sh 2#!/usr/bin/env bash 3 4#------------------------------------------------------------------------------ 5# @file 6# Builds a Hugo site hosted on Vercel. 7# 8# The Vercel build image automatically installs Node.js dependencies. 9#------------------------------------------------------------------------------ 10 11main() { 12 13 DART_SASS_VERSION=1.93.2 14 GO_VERSION=1.25.3 15 HUGO_VERSION=0.152.0 # 这里记得替换成自己的版本 16 NODE_VERSION=22.20.0 17 18 export TZ=Europe/Oslo 19 20 # Install Dart Sass 21 echo \u0026#34;Installing Dart Sass ${DART_SASS_VERSION}...\u0026#34; 22 curl -sLJO \u0026#34;https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz\u0026#34; 23 tar -C \u0026#34;${HOME}/.local\u0026#34; -xf \u0026#34;dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz\u0026#34; 24 rm \u0026#34;dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz\u0026#34; 25 export PATH=\u0026#34;${HOME}/.local/dart-sass:${PATH}\u0026#34; 26 27 # Install Go 28 echo \u0026#34;Installing Go ${GO_VERSION}...\u0026#34; 29 curl -sLJO \u0026#34;https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz\u0026#34; 30 tar -C \u0026#34;${HOME}/.local\u0026#34; -xf \u0026#34;go${GO_VERSION}.linux-amd64.tar.gz\u0026#34; 31 rm \u0026#34;go${GO_VERSION}.linux-amd64.tar.gz\u0026#34; 32 export PATH=\u0026#34;${HOME}/.local/go/bin:${PATH}\u0026#34; 33 34 # Install Hugo 35 echo \u0026#34;Installing Hugo ${HUGO_VERSION}...\u0026#34; 36 curl -sLJO \u0026#34;https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; 37 mkdir \u0026#34;${HOME}/.local/hugo\u0026#34; 38 tar -C \u0026#34;${HOME}/.local/hugo\u0026#34; -xf \u0026#34;hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; 39 rm \u0026#34;hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; 40 export PATH=\u0026#34;${HOME}/.local/hugo:${PATH}\u0026#34; 41 42 # Install Node.js 43 echo \u0026#34;Installing Node.js ${NODE_VERSION}...\u0026#34; 44 curl -sLJO \u0026#34;https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz\u0026#34; 45 tar -C \u0026#34;${HOME}/.local\u0026#34; -xf \u0026#34;node-v${NODE_VERSION}-linux-x64.tar.xz\u0026#34; 46 rm \u0026#34;node-v${NODE_VERSION}-linux-x64.tar.xz\u0026#34; 47 export PATH=\u0026#34;${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}\u0026#34; 48 49 # Verify installations 50 echo \u0026#34;Verifying installations...\u0026#34; 51 echo Dart Sass: \u0026#34;$(sass --version)\u0026#34; 52 echo Go: \u0026#34;$(go version)\u0026#34; 53 echo Hugo: \u0026#34;$(hugo version)\u0026#34; 54 echo Node.js: \u0026#34;$(node --version)\u0026#34; 55 56 # Configure Git 57 echo \u0026#34;Configuring Git...\u0026#34; 58 git config core.quotepath false 59 if [ \u0026#34;$(git rev-parse --is-shallow-repository)\u0026#34; = \u0026#34;true\u0026#34; ]; then 60 git fetch --unshallow 61 fi 62 63 # Build the site 64 echo \u0026#34;Building the site\u0026#34; 65 hugo --gc --minify --baseURL \u0026#34;https://${VERCEL_PROJECT_PRODUCTION_URL}\u0026#34; 66 67} 68 69set -euo pipefail 70main \u0026#34;$@\u0026#34; 创建完后还要提交推送到仓库上\n创建项目 打开Vercel，点击 Add New 添加项目 Project 安装Vercel应用，选择博客的主仓库（一般是私密的） 点击导入 Import 修改项目名和框架名后，点击部署 Deploy 等待成功后出现界面： 后续 页面没有文章 文章在发布时设置了 draft = true ，Hugo 默认不构建 draft=true 的文章 → public/ 目录中没有 .html 文件 → 页面为空！\n但在 GitHub Pages 正常显示，但 Vercel 却不显示，这说明不是 draft 的问题。\n查看之前部署在 github page 上的的工作流文件 .github/workflows/hugo_deploy.yaml\n1name: deploy 2 3# 代码提交到main分支时触发github action 4on: 5 push: 6 branches: 7 - main 8 9jobs: 10 deploy: 11 runs-on: ubuntu-latest 12 steps: 13 - name: Checkout 14 uses: actions/checkout@v4 15 with: 16 fetch-depth: 0 17 18 - name: Setup Hugo 19 uses: peaceiris/actions-hugo@v3 20 with: 21 hugo-version: 0.152.0 22 extended: true 23 24 - name: Build Web 25 run: hugo -D 26 27 - name: Deploy Web 28 uses: peaceiris/actions-gh-pages@v4 29 with: 30 PERSONAL_TOKEN: ${{ secrets.TOKEN }} 31 EXTERNAL_REPOSITORY: bobQAQ003/Kuka 32 PUBLISH_BRANCH: main 33 PUBLISH_DIR: ./public 34 commit_message: auto deploy hugo -D 构建时会包含草稿，而部署在 Vercel上的网页生成不包含 draft ，可以修改成：\n1 # Build the site 2 echo \u0026#34;Building the site\u0026#34; 3 hugo --gc --minify --baseURL \u0026#34;https://${VERCEL_PROJECT_PRODUCTION_URL}\u0026#34; 4 hugo --gc --minify --buildDrafts --baseURL \u0026#34;https://${VERCEL_PROJECT_PRODUCTION_URL}\u0026#34; 再推送到github上\n既然是不编译的问题，那么也可以把每篇文章的draft = true 改为 false ，不修改 build.sh， 这样应该也是可以。\n修改域名 点击 Domains ，再点击 Edit\n因为没有申请域名，所以修改的时候不能随便修改，保留.vercel.app 前缀找个合适的保存就行。后续申请了域名再仔细考虑这个，现在进行修改只是为了方便记住。 ","date":"2025-10-30T20:25:25+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/hugo-hugo%E9%83%A8%E7%BD%B2%E5%88%B0vercel/","title":"[Hugo] Hugo部署到Vercel"},{"content":" 论文来自：\nXu H, Xiang L, Huang F, et al. Grace: Graph self-distillation and completion to mitigate degree-related biases[C]//Proceedings of the 29th ACM SIGKDD Conference on Knowledge Discovery and Data Mining. 2023: 2813-2824.\n摘要 真实世界图普遍呈现长尾度分布（long-tail degree distribution），大量节点为低度节点（low-degree nodes）。尽管 GNN 在节点分类任务上表现出色，但其性能严重依赖丰富连接，对低度节点表示不足，导致显著的度相关偏差（degree-related bias）。\n本文提出 Grace，通过以下两大机制缓解该问题：\n自蒸馏（Graph Self-Distillation）：增强低度节点的自表示能力（self-representation） 图补全（Graph Completion）：提升低度节点的邻域同质性比率（Neighborhood Homophily Ratio, NHR） 结合 标签传播（Label Propagation） 防止错误传播。实验表明，Grace 在平衡整体性能与低度节点准确率方面显著优于现有方法。\n问题背景与动机 图1：真实图的度分布与性能偏差 核心挑战 挑战 描述 ① 自表示不足 GNN 过度依赖邻域聚合，低度节点失去邻域后性能崩塌至 MLP 水平 ② 低 NHR 低度节点邻居中同类节点比例极低，违反 GNN 的同质性假设 整体框架 流程 分解 GNN 将 GNN 分成 ST（自变换，MLP） 和 NT（邻域变换） 两部分。 自蒸馏训练 教师模型：完整 GNN（ST + NT） 学生模型：仅 ST（退化为 MLP） 用教师软标签 + 真实标签监督学生，优化 ST 学习“邻域平移”。 图补全（Graph Completion） 对低度节点 $ v $，用自蒸馏输出 $ p(v) $ 预测同类邻居 建模为多标签任务：正样本=当前邻居，负样本=低相似度节点 选前 $ k $ 个高概率同类节点，添加新边，构建新图 $ G\u0026rsquo; $。 训练 Grace-GNN 在补全后的图 $ G\u0026rsquo; $ 上，用增强的 ST + NT 训练最终 GNN。 推理阶段：标签传播 对低度节点，用 $ p(v) $ 选前 $ k $ 个邻居 构建有向边（高置信 → 低度） 单跳传播： $\\hat{p}(v) = (1-\\lambda) p(v) + \\lambda \\sum_{u \\to v} p(u)$ 输出最终预测。 核心特点：自蒸馏增强自表示 + 精准补全提升 NHR + 单跳传播防误传，实现低度节点性能大幅提升，整体无损。\n核心模块 自蒸馏（Graph Self-Distillation） 思路 将 GNN 分解为两部分：\nST（Self-Transformation）：节点自身特征变换（MLP） NT（Neighborhood Transformation）：邻居信息聚合 目标：将图依赖性从 NT 迁移到 ST，使 ST 学习到“邻域平移”\n实现 教师模型：完整 GNN（含 ST + NT） 学生模型：仅 ST 部分（退化为 MLP） 训练流程： 输入节点特征 → 教师模型 → 软标签 $ p_t(v) $ 输入相同特征 → 学生模型 → 输出 $ p_s(v) $ 损失： $\\mathcal{L}_{KD} = \\sum_v \\left[ \\alpha \\cdot KL(p_t(v) | p_s(v)) + (1-\\alpha) \\cdot CE(p_s(v), y_v) \\right]$ 收敛后，ST 隐式编码了邻域信息，增强低度节点自表示\n图补全（Graph Completion） 目标 为低度节点预测同类潜在邻居，提升 NHR\n建模为多标签预测任务 正样本：当前邻居 $ \\mathcal{N}(v) $ 候选负样本：其他节点 预测：使用自蒸馏输出的软标签 $ p(v) $ 选择策略： 取 $ p(v) $ 中前 2 大概率类别 归一化得到权重 负样本：与 $ v $ 余弦相似度 \u0026lt; $ \\eta $ 最终标签： $y_v^{\\text{multi}} = \\text{softmax}\\left( \\sum_{u \\in \\mathcal{N}(v)} p(u) \\right)$ 仅连接最可能同类的节点，避免噪声\n标签传播（Label Propagation） 作用 防止图补全中的误分类传播\n预测阶段流程 根据 $ p(v) $ 选前 $ k $ 个潜在邻居 构建有向边：$ u \\to v $（$ u $ 是高置信邻居） 单跳传播： $\\hat{p}(v) = (1 - \\lambda) p(v) + \\lambda \\sum_{u \\in \\mathcal{N}\u0026rsquo;(v)} p(u)$ 其中 $ \\mathcal{N}\u0026rsquo;(v) $ 为新图中的邻居 信息单向流动：从可靠节点 → 低度节点\n符号表 符号 含义 $ G = (V, E, X, Y) $ 原始图 $ \\deg(v) $ 节点 $ v $ 的度数 $ \\text{NHR}(v) $ 邻域同质性比率 = 同类邻居数 / 总邻居数 $ \\text{ST}(\\cdot), \\text{NT}(\\cdot) $ 自变换、邻域变换 $ p_t(v), p_s(v) $ 教师/学生模型输出概率 $ \\mathcal{L}_{KD} $ 知识蒸馏损失 $ \\eta $ 负样本相似度阈值 $ k $ 预测邻居数量 $ G\u0026rsquo; $ 补全后新图 $ \\mathcal{N}\u0026rsquo;(v) $ 新图中 $ v $ 的邻居集 ","date":"2025-10-29T16:32:18+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/fairness-gracegraph-self-distillation-and-completion-to-mitigate-degree-related-biases/","title":"[Fairness] Grace：Graph self-distillation and completion to mitigate degree-related biases"},{"content":" 引入这个动画是因为加载自定义字体后，因为字体太大导致网站总是先显示默认字体，过一会再显示自定义字体，强迫症犯了。所以希望等加载后再展示网站\n牛顿摆动画 将以下代码复制进layouts/partials/head/custom.html(文件不存在则自行创建) 1\u0026lt;div class=\u0026#34;loading\u0026#34;\u0026gt; 2 \u0026lt;!-- html内容 --\u0026gt; 3 \u0026lt;div class=\u0026#34;newtons-cradle\u0026#34;\u0026gt; 4 \u0026lt;div class=\u0026#34;newtons-cradle__dot\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 5 \u0026lt;div class=\u0026#34;newtons-cradle__dot\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 6 \u0026lt;div class=\u0026#34;newtons-cradle__dot\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 7 \u0026lt;div class=\u0026#34;newtons-cradle__dot\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 8 \u0026lt;/div\u0026gt; 9\u0026lt;/div\u0026gt; 10 11\u0026lt;style\u0026gt; 12 .loading { 13 position: fixed; 14 display: flex; 15 justify-content: center; 16 align-items: center; 17 top: 0; 18 left: 0; 19 width: 100%; 20 height: 100%; 21 z-index: 99; 22 background-color: #f5f5fa; 23 } 24\t25 /* css内容 */ 26 27 /* From Uiverse.io by dovatgabriel */ 28 .newtons-cradle { 29 --uib-size: 50px; 30 --uib-speed: 1.2s; 31 --uib-color: #474554; 32 position: relative; 33 display: flex; 34 align-items: center; 35 justify-content: center; 36 width: var(--uib-size); 37 height: var(--uib-size); 38 } 39 40 .newtons-cradle__dot { 41 position: relative; 42 display: flex; 43 align-items: center; 44 height: 100%; 45 width: 25%; 46 transform-origin: center top; 47 } 48 49 .newtons-cradle__dot::after { 50 content: \u0026#39;\u0026#39;; 51 display: block; 52 width: 100%; 53 height: 25%; 54 border-radius: 50%; 55 background-color: var(--uib-color); 56 } 57 58 .newtons-cradle__dot:first-child { 59 animation: swing var(--uib-speed) linear infinite; 60 } 61 62 .newtons-cradle__dot:last-child { 63 animation: swing2 var(--uib-speed) linear infinite; 64 } 65 66 @keyframes swing { 67 0% { 68 transform: rotate(0deg); 69 animation-timing-function: ease-out; 70 } 71 72 25% { 73 transform: rotate(70deg); 74 animation-timing-function: ease-in; 75 } 76 77 50% { 78 transform: rotate(0deg); 79 animation-timing-function: linear; 80 } 81 } 82 83 @keyframes swing2 { 84 0% { 85 transform: rotate(0deg); 86 animation-timing-function: linear; 87 } 88 89 50% { 90 transform: rotate(0deg); 91 animation-timing-function: ease-out; 92 } 93 94 75% { 95 transform: rotate(-70deg); 96 animation-timing-function: ease-in; 97 } 98 } 99 100 .animated-stop { 101 animation-play-state: paused !important; 102 } 103 104 .scaleAndFadeout { 105 animation: scaleAndFadeOut 1.5s forwards; 106 } 107 108 /** 放大1.5倍，并渐变到透明 */ 109 @keyframes scaleAndFadeOut { 110 0% { 111 transform: scale(1); 112 opacity: 1; 113 } 114 100% { 115 transform: scale(1.5); 116 opacity: 0; 117 } 118 } 119 120\u0026lt;/style\u0026gt; 121 122\u0026lt;script\u0026gt; 123 // 首次加载：等资源完 → 播放动画 → 淡出 124 window.addEventListener(\u0026#39;load\u0026#39;, () =\u0026gt; { 125 const loading = document.querySelector(\u0026#39;.loading\u0026#39;); 126 if (!loading) return; 127 128 setTimeout(() =\u0026gt; loading.classList.add(\u0026#39;scaleAndFadeout\u0026#39;), 600); 129 setTimeout(() =\u0026gt; loading.style.display = \u0026#39;none\u0026#39;, 2000); 130 }); 131 132 // PJAX 切换：直接隐藏 133 document.addEventListener(\u0026#39;pjax:complete\u0026#39;, () =\u0026gt; { 134 const loading = document.querySelector(\u0026#39;.loading\u0026#39;); 135 if (loading) loading.style.display = \u0026#39;none\u0026#39;; 136 }); 137\u0026lt;/script\u0026gt; 这边设置的是第一次进入界面和手动刷新时会加载动画\n自定义动画 前往UIverse，找一个加载动画 将加载动画的html，css分别根据下面的模板填写，然后将内容复制到layouts/partials/head/custom.html 1\u0026lt;div class=\u0026#34;loading\u0026#34;\u0026gt; 2 \u0026lt;!-- html内容 --\u0026gt; 3\u0026lt;/div\u0026gt; 4 5\u0026lt;style\u0026gt; 6 .loading { 7 position: fixed; 8 display: flex; 9 justify-content: center; 10 align-items: center; 11 top: 0; 12 left: 0; 13 width: 100%; 14 height: 100%; 15 z-index: 99; 16 background-color: #f5f5fa; 17 } 18\t19 .animated-stop { 20 animation-play-state: paused !important; 21 } 22 23 .scaleAndFadeout { 24 animation: scaleAndFadeOut 1.5s forwards; 25 } 26 27 /** 放大1.5倍，并渐变到透明 */ 28 @keyframes scaleAndFadeOut { 29 0% { 30 transform: scale(1); 31 opacity: 1; 32 } 33 100% { 34 transform: scale(1.5); 35 opacity: 0; 36 } 37 } 38 39 /* css内容 */ 40\u0026lt;/style\u0026gt; 41 42\u0026lt;script\u0026gt; 43 // 首次加载：等资源完 → 播放动画 → 淡出 44 window.addEventListener(\u0026#39;load\u0026#39;, () =\u0026gt; { 45 const loading = document.querySelector(\u0026#39;.loading\u0026#39;); 46 if (!loading) return; 47 48 setTimeout(() =\u0026gt; loading.classList.add(\u0026#39;scaleAndFadeout\u0026#39;), 600); 49 setTimeout(() =\u0026gt; loading.style.display = \u0026#39;none\u0026#39;, 2000); 50 }); 51 52 // PJAX 切换：直接隐藏 53 document.addEventListener(\u0026#39;pjax:complete\u0026#39;, () =\u0026gt; { 54 const loading = document.querySelector(\u0026#39;.loading\u0026#39;); 55 if (loading) loading.style.display = \u0026#39;none\u0026#39;; 56 }); 57\u0026lt;/script\u0026gt; 具体的细节可以根据需求更改 样式 CSS可能用到样式 1/** 2 * 可能会用到的CSS 3 */ 4{ 5 /* 整体修改元素大小(大于1放大, 小于1缩小) */ 6 zoom: 1.0; 7 8 /* 开始动画播放 */ 9 animation-play-state: running; 10 11 /* 暂停动画播放 */ 12 animation-play-state: paused; 13 14 /* 元素居中(flex布局) */ 15 display: flex; 16 justify-content: center; 17 align-items: center; 18 19 /* 元素隐藏 */ 20 display: none; 21 22 /* 定义动画 */ 23 @keyframes 动画名 { 24 0% {} 25 进度% {} 26 100% {} 27 } 28 29 /* 执行动画 */ 30 animation: 动画名 时间; 31} CSS动画常用参数 1/** 2 * CSS动画常用参数 3 */ 4{ 5 /* 不透明度 */ 6 opacity: 1; 7 8 /* 放大缩小 */ 9 transform: scale(1); 10 11 /* 旋转 */ 12 transform: rotate(360deg); 13 14 /* 平移(水平, 垂直) */ 15 transform: translate(-100%, 0); 16} ","date":"2025-10-28T17:27:50+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/hugo-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A0%E8%BD%BD%E5%8A%A8%E7%94%BB/","title":"[Hugo] 自定义加载动画"},{"content":"引入PJAX 从 MoOx/pjax（不带JQuery）这里引入 我们需要刷新的是左侧边栏 ，右侧边栏 和中间的内容 从源代码可以发现这些元素都被一个\u0026lt;div class=\u0026quot;main-container\u0026quot;\u0026gt;...\u0026lt;/div\u0026gt;包裹着 根据官方文档，在layouts/partials/footer/custom.html加入以下代码来引入PJAX： 1\u0026lt;!-- 【custom.html】 --\u0026gt; 2\u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/npm/pjax/pjax.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 3\u0026lt;script\u0026gt; 4 var pjax = new Pjax({ 5 selectors: [ 6 \u0026#34;.main-container\u0026#34; 7 ] 8 }) 9\u0026lt;/script\u0026gt; 我这里先创建layouts/partials/footer/pjax.html，再在custom.html里引用 1\u0026lt;!-- pjax.html --\u0026gt; 2\u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/npm/pjax/pjax.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 3\u0026lt;script\u0026gt; 4 var pjax = new Pjax({ 5 selectors: [ 6 \u0026#34;.main-container\u0026#34;, 7 ] 8 }) 9\u0026lt;/script\u0026gt; 10 11\u0026lt;!-- custom.html --\u0026gt; 12{{partial \u0026#34;footer/pjax.html\u0026#34;}} 文章样式修复 问题描述： 引入pjax后，重启hugo server -D，发现文章样式丢失，手动点击刷新可以恢复 产生原因： \u0026lt;body\u0026gt;标签中的class名缺失article-page，导致文章样式丢失 解决思路： 通过官方提供了数据预处理方法，来预处理数据，获取到新页面的className，然后我们手动将这className设置到\u0026lt;body\u0026gt;上 具体步骤： 修改layouts/partials/footer/pjax.html，（如果是在custom.html加了pjax就在那里改）引入以下代码： 1\u0026lt;script\u0026gt; 2 pjax._handleResponse = pjax.handleResponse; 3 pjax.handleResponse = function(responseText, request, href, options) { 4 if (request.responseText.match(\u0026#34;\u0026lt;html\u0026#34;)) { 5 if (responseText) { 6 // 将新页面的html字符串解析成DOM对象 7 let newDom = new DOMParser().parseFromString(responseText, \u0026#39;text/html\u0026#39;); 8 // 获取新页面中body的className，并设置回当前页面 9 let bodyClass = newDom.body.className; 10 document.body.setAttribute(\u0026#34;class\u0026#34;, bodyClass) 11 } 12 // 放行，交给pjax自己处理 13 pjax._handleResponse(responseText, request, href, options); 14 } else { 15 // handle non-HTML response here 16 } 17 } 18\u0026lt;/script\u0026gt; 主题切换修复 问题描述： 切换页面后，左下角切换主题颜色按钮不生效 产生原因： 在Stack主题源码assets/ts/colorScheme.ts中，脚本初始化时，会给元素绑定一个点击事件。但因为页面切换了，替换了该元素，但该元素没有重新绑定点击事件，导致点击主题切换失效 解决思路：\n在PJAX切换完页面后，重新执行一遍colorScheme.ts的初始化，使元素重新绑定点击事件 而colorScheme.ts被main.ts引用，在main.ts中执行了初始化，并且main.ts生成了全局变量 Stack 所以在PJAX执行完后，使用全局变量 Stack ，执行里面的初始化方法，重新执行一遍脚本，来绑定点击事件 PJAX官方文档也提供了PJAX执行完后的事件pjax:complete，监听这个事件，Stack 执行初始化就好 具体步骤：\n修改layouts/partials/footer/pjax.html，引入以下代码 1\u0026lt;script\u0026gt; 2 document.addEventListener(\u0026#39;pjax:complete\u0026#39;, () =\u0026gt; { 3 // Stack脚本初始化 4 window.Stack.init(); 5 }) 6\u0026lt;/script\u0026gt; 文章搜索修复 问题描述： 使用文章搜索功能时，输入关键词，无任何搜索记录 产生原因： 查看assets/ts/search.tsx文件，情况和上面的colorScheme.ts类似，存在绑定事件 解决思路： 把 search.tsx 初始化内容封装为一个函数，并把函数 export 出来 由 main.ts 引入这个函数，并放到 Stack.init() 的方法中，利用此方法来重新初始化搜索脚本 具体步骤： 修改assets/ts/search.tsx代码，封装方法并export 1/** 2 * 把window.addEventListener(\u0026#39;load\u0026#39; ...这部分代码注释掉 3 * 初始化工作交给Stack.init()处理了，不需要这个了 4 */ 5... 6function searchInit() { 7 let search = document.querySelector(\u0026#39;.search-result\u0026#39;); 8 if (search) { 9 const searchForm = document.querySelector(\u0026#39;.search-form\u0026#39;) as HTMLFormElement, 10 searchInput = searchForm.querySelector(\u0026#39;input\u0026#39;) as HTMLInputElement, 11 searchResultList = document.querySelector(\u0026#39;.search-result--list\u0026#39;) as HTMLDivElement, 12 searchResultTitle = document.querySelector(\u0026#39;.search-result--title\u0026#39;) as HTMLHeadingElement; 13 14 new Search({ 15 form: searchForm, 16 input: searchInput, 17 list: searchResultList, 18 resultTitle: searchResultTitle, 19 resultTitleTemplate: window.searchResultTitleTemplate 20 }); 21 } 22} 23 24export { 25 searchInit 26} 修改assets/ts/main.ts，引入搜索初始化方法并调用 1... 2import { searchInit } from \u0026#34;ts/search\u0026#34;; 3let Stack = { 4 init: () =\u0026gt; { 5 ... 6 // 调用search脚本初始化方法 7 searchInit(); 8 } 9} 从Stack模板文件复制layouts\\partials\\footer\\components\\script.html，把\u0026quot;JSXFactory\u0026quot; \u0026quot;createElement\u0026quot;补充，改法参考layouts\\page\\search.html， 搜索内容跳转修复 问题描述：\n能进行搜索，但搜索出来的内容并没有被PJAX识别到，导致PJAX没有拦截，进而导致页面刷新 产生原因：\n阅读 search.tsx 源码可知，搜索内容的数据，是通过 React.render() 动态渲染回页面的，这些动态数据没有被PJAX识别到 解决思路： PJAX官方文档提供给了重新解析数据的方法，所以在 React.render() 之后，调用PJAX方法，重新解析页面即可 具体步骤： 修改assets/ts/search.tsx，在动态渲染数据方法末尾让pjax重新解析文档 1private async doSearch(keywords: string[]) { 2 ... 3 /* 4 方法末尾，让pjax重新解析文档数据，识别动态渲染的数据 5 虽然当前文件没有pjax对象，但最后静态页面会生成一个整体的js文件 6 pjax对象那时就能识别到，就可成功调用 7 */ 8 pjax.refresh(document); 9} KaTeX修复 KaTex渲染失效 问题描述： 含有 数学公式(KaTeX) 的文章，里面的数学公式不能正常渲染出来 产生原因： 阅读源码layouts/partials/article/components/math.html，KaTeX渲染公式需要执行renderMathInElement，而PJAX的无刷新技术无法触发DOMContentLoaded的监听 解决思路： 将renderMathInElement封装为函数，交由PJAX加载结束后执行 具体操作： 修改layouts/partials/article/components/math.html，添加一个元素标签，便于判断文档是否使用了KaTeX 1... 2\u0026lt;div class=\u0026#34;math-katex\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; layouts/partials/footer/pjax.html，引入以下代码 1\u0026lt;script\u0026gt; 2 async function renderKaTeX() { 3 // 判断当前页面是否有KateX 4 let katex = document.querySelector(\u0026#34;.math-katex\u0026#34;); 5 if (!katex) { 6 return; 7 } 8 // 等待函数加载成功后，再执行渲染方法 9 while (typeof renderMathInElement !== \u0026#39;function\u0026#39;) { 10 await delay(500); 11 } 12 // KaTeX渲染方法 13 renderMathInElement(document.body, { 14 delimiters: [ 15 { left: \u0026#34;$$\u0026#34;, right: \u0026#34;$$\u0026#34;, display: true }, 16 { left: \u0026#34;$\u0026#34;, right: \u0026#34;$\u0026#34;, display: false }, 17 { left: \u0026#34;\\\\(\u0026#34;, right: \u0026#34;\\\\)\u0026#34;, display: false }, 18 { left: \u0026#34;\\\\[\u0026#34;, right: \u0026#34;\\\\]\u0026#34;, display: true } 19 ], 20 ignoredClasses: [\u0026#34;gist\u0026#34;] 21 }); 22 } 23 24 /** 25 * 同步延迟 26 */ 27 function delay(time) { 28 return new Promise(resolve =\u0026gt; { 29 setTimeout(resolve, time) 30 }) 31 } 32 33 document.addEventListener(\u0026#39;pjax:complete\u0026#39;, () =\u0026gt; { 34 renderKaTeX(); 35 }) 36\u0026lt;/script\u0026gt; 引用块和公式块冲突 问题描述：\n在引用块中又写了公式块，最终渲染出这样： 产生原因：\nTypora 内置的 Markdown 解析器 先合并 blockquote 行、再交给 MathJax，所以Typora显示正常 而 Hugo（Goldmark 解析器）逐行解析，每一行前面都带着 \u0026gt;，于是 Goldmark 把 \u0026gt; 转义成 \u0026gt; 塞进公式节点，KaTeX 收到的是：\u0026gt; \\mu(z_t) = 0.8 \\quad (...) 解决思路：\n目前没有很好的解决方法，只能是避免混用 浏览量统计失效 问题描述： 先前使用的vercount在引入Pjax后失效，必须手动刷新才会生效 产生原因： 原始的Vercount脚本在页面第一次加载时执行，它会在页面中插入浏览量显示的元素。但是当使用Pjax加载x新页面时，脚本不会自动重新执行，因此新页面中的浏览量元素没有被插入。 解决思路： 动态重新加载脚本，移除旧脚本，清除之前的执行状态， 创建新脚本，等待 pjax:complete 完成后再添加到 DOM 中 具体步骤： layouts/partials/footer/pjax.html，引入代码 1\u0026lt;script\u0026gt; 2 // 浏览量统计 3 function initVerCount() { 4 // 移除已存在的 Vercount 脚本 5 const existingScript = document.querySelector(\u0026#39;script[src=\u0026#34;https://cn.vercount.one/js\u0026#34;]\u0026#39;); 6 if (existingScript) { 7 existingScript.remove(); 8 } 9 10 // 创建新的 Vercount 脚本 11 const newScript = document.createElement(\u0026#39;script\u0026#39;); 12 newScript.src = \u0026#39;https://cn.vercount.one/js\u0026#39;; 13 newScript.defer = true; 14 15 // 添加到 head 中 16 document.head.appendChild(newScript); 17 } 18 19 document.addEventListener(\u0026#39;pjax:complete\u0026#39;, () =\u0026gt; { 20 ... 21 initVerCount(); 22 }) 23\u0026lt;/script\u0026gt; 网站标题更改 问题描述： 使用了PJAX后，点击左侧边栏和文章，网站标题都是你在.yaml中设置的languages.zh-cn.title，当你手动刷新才会变成相关页面 产生原因： PJAX 通过 AJAX 加载新页面的内容（这里配置的 selectors: [\u0026quot;.main-container\u0026quot;]，只替换主容器），而不进行完整的页面重载（full page reload）。这意味着title部分没有进行更新。 解决思路： 在 handleResponse 函数中添加逻辑：从新页面的 HTML 响应中提取 \u0026lt;title\u0026gt;标签，设置到document.title 具体步骤： 在\\layouts\\partials\\footer\\pjax.html中修改文章格式的同时，添加title 1\u0026lt;script\u0026gt; 2 // pjax刷新区域 3 var pjax = new Pjax({ 4 selectors: [ 5 \u0026#34;.main-container\u0026#34;, 6 ] 7 }) 8 9 // 修正文章样式丢失 10 pjax._handleResponse = pjax.handleResponse; 11 pjax.handleResponse = function(responseText, request, href, options) { 12 if (request.responseText.match(\u0026#34;\u0026lt;html\u0026#34;)) { 13 // 将新页面的html字符串解析成DOM对象 14 let newDom = new DOMParser().parseFromString(responseText, \u0026#39;text/html\u0026#39;); 15 16 // 1. 更新 body class，获取新页面中body的className，并设置回当前页面 17 let bodyClass = newDom.body.className; 18 document.body.setAttribute(\u0026#34;class\u0026#34;, bodyClass) 19 20 // 2. 更新 \u0026lt;title\u0026gt; 21 let newTitle = newDom.querySelector(\u0026#39;title\u0026#39;); 22 if (newTitle \u0026amp;\u0026amp; newTitle.textContent) { 23 document.title = newTitle.textContent.trim(); 24 } 25 26 pjax._handleResponse(responseText, request, href, options); 27 } else { 28 // handle non-HTML response here 29 } 30 } 31... 32\u0026lt;/script\u0026gt; 进度条加载 问题描述： 使用了PJAX后，原先采用的进度条失效，采用另外的方法 具体步骤： 前往【topbar】下载zip包，将解压后的 topbar.min.js 放到assets\\js\\topbar.min.js 通过监听PJAX两个事件 pjax:send 和 pjax:complete 实现伪进度条 layouts/partials/footer/pjax.html，引入代码 1{{ with resources.Get \u0026#34;js/topbar.min.js\u0026#34; }} 2 \u0026lt;!-- 引入本地JS脚本 --\u0026gt; 3 \u0026lt;script src={{ .Permalink }}\u0026gt;\u0026lt;/script\u0026gt; 4{{ end }} 5\u0026lt;script\u0026gt; 6 // 修改进度条颜色 7 topbar.config({ 8 barColors: { 9 \u0026#39;0\u0026#39;: \u0026#39;rgba(255, 255, 255, 1)\u0026#39;, // 进度0%白色 10 \u0026#39;1.0\u0026#39;: \u0026#39;rgba(0, 149, 234, 1)\u0026#39; // 进度100%蓝色 11 } 12 }) 13\t14 document.addEventListener(\u0026#39;pjax:send\u0026#39;, () =\u0026gt; { 15 // 显示顶部进度条 16 topbar.show(); 17 }) 18\t19 document.addEventListener(\u0026#39;pjax:complete\u0026#39;, () =\u0026gt; { 20 .... 21 // 隐藏顶部进度条 22 topbar.hide(); 23 }) 24\u0026lt;/script\u0026gt; ","date":"2025-10-25T09:40:08+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/hugo-%E7%BE%8E%E5%8C%96%E8%BF%87%E7%A8%8B%E4%B8%AD%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/","title":"[Hugo] 美化过程中遇到的问题"},{"content":" 为避免踩坑，凡是需要更改的文件都从themes/主题复制到主目录下，个性化配置文件建议都在博客主目录下操作，减少更新主题时个性化配置被覆盖的麻烦。\n每次服务启动，会同步更新所有位置配置文件，但主目录配置文件优先级最高\nyaml配置 添加行号后复制代码会带有行号 参考 小白hugo博客装修笔记（2）- B1ain’s Blog\n手动复制有行号 在/assets/scss/custom.scss 文件中添加如下内容，将行号设定为不可选中\n1// 手动复制禁止复制行号 2.highlight .ln { 3 user-select: none; 4} copy按钮复制带行号 修改/themes/hugo-theme-stack/assets/ts/main.ts文件中的复制按钮逻辑。\n但具体情况是，在本地进行调试时，确实手动复制和copy复制都会涉及行号。当部署到github pages打开后，copy不会有影响，反而手动复制可能会复制到行号。\n所以具体情况看个人博客部署情况吧。反正能用，若要修改参考 Hugo-stackの美化\n具体修改assets/ts/main.ts\n1highlights.forEach(highlight =\u0026gt; { 2 const copyButton = document.createElement(\u0026#39;button\u0026#39;); 3 copyButton.innerHTML = copyText; 4 copyButton.classList.add(\u0026#39;copyCodeButton\u0026#39;); 5 highlight.appendChild(copyButton); 6 7 const codeBlock = highlight.querySelector(\u0026#39;code[data-lang]\u0026#39;); 8 if (!codeBlock) return; 9 10 copyButton.addEventListener(\u0026#39;click\u0026#39;, () =\u0026gt; { 11 // 创建一个临时容器来克隆代码块的内容 12 const tempCodeBlock = codeBlock.cloneNode(true) as HTMLElement; 13 14 // 删除行号，行号的元素是 \u0026lt;span class=\u0026#34;ln\u0026#34;\u0026gt; 15 const lineNumbers = tempCodeBlock.querySelectorAll(\u0026#39;.ln\u0026#39;); 16 lineNumbers.forEach(lineNumber =\u0026gt; lineNumber.remove()); 17 18 // 获取没有行号的纯文本内容 19 const codeText = tempCodeBlock.textContent; 20 21 navigator.clipboard.writeText(codeText || \u0026#39;\u0026#39;) 22 // navigator.clipboard.writeText(codeBlock.textContent) 23 .then(() =\u0026gt; { 24 copyButton.textContent = copiedText; 25 26 setTimeout(() =\u0026gt; { 27 copyButton.textContent = copyText; 28 }, 1000); 29 }) 30 .catch(err =\u0026gt; { 31 alert(err) 32 console.log(\u0026#39;Something went wrong\u0026#39;, err); 33 }); 34 }); 35}); 左边栏副标题换行 参考 Hugo-stackの美化 II\n找到 layouts/partials/sidebar/left.html，复制到博客的同名主文件夹下：\n1\u0026lt;div class=\u0026#34;site-meta\u0026#34;\u0026gt; 2 \u0026lt;h1 class=\u0026#34;site-name\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;{{ .Site.BaseURL | relLangURL }}\u0026#34;\u0026gt;{{ .Site.Title }}\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt; 3 \u0026lt;h2 class=\u0026#34;site-description\u0026#34;\u0026gt;{{ .Site.Params.sidebar.subtitle }}\u0026lt;/h2\u0026gt; 4 \u0026lt;h2 class=\u0026#34;site-description\u0026#34;\u0026gt;{{ .Site.Params.sidebar.subtitle | safeHTML }}\u0026lt;/h2\u0026gt; 5\u0026lt;/div\u0026gt; 当然也可以修改stack模板下hugo-theme-stack里的文件，但不建议，毕竟还要通过源码进行学习。\n然后在 hugo.yaml 中在要换行的地方加上 \u0026lt;br\u0026gt;：\n1languages: 2 en: 3 languageName: English 4 title: Example Site 5 weight: 1 6 params: 7 sidebar: 8 subtitle: Example description 9 zh-cn: 10 languageName: 中文 11 title: KukaDam 12 weight: 2 13 params: 14 sidebar: 15 subtitle: \u0026#34;晚来天欲雪\u0026lt;br\u0026gt;能饮一杯无\u0026#34; Markdown样式 键盘样式 参考 Hugo Stack 魔改美化 | Naive Koala\n在 /assets/scss/custom.scss 中加入以下代码：\n1// 键盘样式 2kbd { 3 margin: 0 .1em; 4 padding: .1em .6em; 5 font-size: .8em; 6 color: #242729; 7 background: #fff; 8 border: 1px solid #adb3b9; 9 border-radius: 3px; 10 box-shadow: 0px 1px 0 rgba(12, 13, 14, 0.2), 0 0 0 2px #fff inset; 11 white-space: nowrap; 12 vertical-align: middle; 13 font-family: monospace; 14} 这样就可以呈现如下 Markdown 中的键盘样式： \u0026lt;kbd\u0026gt;CTRL\u0026lt;/kbd\u0026gt; + \u0026lt;kbd\u0026gt;C\u0026lt;/kbd\u0026gt;\nCTRL + C\n图床链接图片居中 目前 Stack 默认只支持本地引用的图片居中，而在使用 url 图片链接时没有居中格式。在 /assets/scss/partials/layout/article.scss Line 256 处（同级任意位置）增加以下代码：\n1// Center image from url source 2p \u0026gt; img { 3 display: block; 4 margin: 0 auto; 5 max-width: 100%; 6 height: auto; 7} 当启用fancybox时，上面的方法对fancybox不起作用，可以再上面代码的下方添加：\n1.post-img-view { 2 text-align: center; 3 4 a { 5 display: inline-block; 6 text-decoration: none; 7 border: none; 8 box-shadow: none; 9 10 img { 11 cursor: zoom-in; // 鼠标显示放大镜图标 12 transition: transform 0.3s ease; // 添加缩放动画效果 13 14 \u0026amp;:hover { 15 transform: scale(1.02); // 悬停时轻微放大 16 } 17 } 18 } 19} 图片放大 修改stack主题的配置文件： hugo.yaml，在params中添加： 1params: 2 fancybox: true 在/layouts/_default/_markup/render-image.html合适位置补充： 1{{if .Page.Site.Params.fancybox }} 2\u0026lt;div class=\u0026#34;post-img-view\u0026#34;\u0026gt; 3\u0026lt;a data-fancybox=\u0026#34;gallery\u0026#34; href=\u0026#34;{{ .Destination | safeURL }}\u0026#34;\u0026gt; 4\u0026lt;img src=\u0026#34;{{ .Destination | safeURL }}\u0026#34; alt=\u0026#34;{{ .Text }}\u0026#34; {{ with .Title}} title=\u0026#34;{{ . }}\u0026#34;{{ end }} /\u0026gt; 5\u0026lt;/a\u0026gt; 6\u0026lt;/div\u0026gt; 7{{ end }} 记得把原来的代码注释，否则会渲染两张图\n1\u0026lt;!-- \u0026lt;img src=\u0026#34;{{ $Permalink }}\u0026#34; 2\t{{ with $Width }}width=\u0026#34;{{ . }}\u0026#34;{{ end }} 3\t{{ with $Height }}height=\u0026#34;{{ . }}\u0026#34;{{ end }} 4\t{{ with $Srcset }}srcset=\u0026#34;{{ . }}\u0026#34;{{ end }} 5\tloading=\u0026#34;lazy\u0026#34; 6\t{{ with $alt }} 7\talt=\u0026#34;{{ . }}\u0026#34; 8\t{{ end }} 9\t{{ if $galleryImage }} 10\tclass=\u0026#34;gallery-image\u0026#34; 11\tdata-flex-grow=\u0026#34;{{ div (mul $image.Width 100) $image.Height }}\u0026#34; 12\tdata-flex-basis=\u0026#34;{{ div (mul $image.Width 240) $image.Height }}px\u0026#34; 13\t{{ end }} 14\u0026gt; --\u0026gt; 同理，在 layouts\\partials\\article\\components\\footer.html 中添加： 1{{if .Page.Site.Params.fancybox }} 2\u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 3\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css\u0026#34; /\u0026gt; 4\u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 5{{ end }} 为了看到效果，可以在\\assets\\scss\\partials\\layout\\article.scss中修改图片放大时的动画：\n1.article-content { 2 ... 3 // 为 Fancybox 图片添加样式 4 .post-img-view { 5 text-align: center; 6 7 a { 8 display: inline-block; 9 text-decoration: none; 10 border: none; 11 box-shadow: none; 12 13 img { 14 cursor: zoom-in; // 鼠标显示放大镜图标 15 transition: transform 0.3s ease; // 添加缩放动画效果 16 17 \u0026amp;:hover { 18 transform: scale(1.02); // 悬停时轻微放大 19 } 20 } 21 } 22} 字体修改 去字体网站，比如【100font】，下载自己想要的字体文件 .ttf\n把字体文件放到放入assets/font下（没有自己建）\n将以下代码修改并复制到layouts/partials/footer/custom.html文件中\n字体名：给字体命名一个别名，随便填写就好，保持统一就行 字体文件名：字体文件的全名，带后缀名的，也就是 xxx.ttf 1\u0026lt;style\u0026gt; 2 @font-face { 3 /* 提高自定义字体加载速度 */ 4 unicode-range: U+4E00-9FFF,U+0025-00F0,U+3040-30FF,U+1100-11FF,U+3130-318F,U+AC00-D7FF; 5 font-family: \u0026#39;字体名\u0026#39;; 6 src: url({{ (resources.Get \u0026#34;font/字体文件名\u0026#34;).Permalink }}) format(\u0026#39;truetype\u0026#39;); 7 } 8 9 :root { 10 --base-font-family: \u0026#39;字体名\u0026#39;; 11 --code-font-family: \u0026#39;字体名\u0026#39;; 12 } 13\u0026lt;/style\u0026gt; 测试：l，L，i，I （这几个字母字体用不好分不清）\n固定代码块高度 把以下内容添加到 assets/scss/partials/article.scss ：\n1.article-content { // 大概 line 205 2 .highlight { // line 331 3 background-color: var(--pre-background-color); 4 padding: var(--card-padding); 5 position: relative; 6 ... 7 // 修改 8 pre { // 可以注释之前的 9 margin: initial; 10 padding: 0; 11 margin: 0; 12 width: auto; 13 max-height: 30em; 14 scrollbar-width: none; /* Firefox */ 15 \u0026amp;::-webkit-scrollbar { 16 display: auto; 17 /* Chrome Safari */ 18 } 19 } 20 } 21} 固定高度后，还要去.yaml文件中修改参数，否则行号和代码块的滚动不一致\n1markup: 2 highlight: 3 lineNos: true 4 lineNumbersInTable: true 5 # lineNumbersInTable：使用表来格式化行号和代码, 而不是标签。这个属性一般设置为 true. 6 # lineNos：是否使用行号 设置lineNumbersInTable: false后，一般手动复制代码块和copy键复制都会把行号复制上，具体的修改看第一节\n组件 顶部返回按钮 参考Stack 主题的自定义 | L1nSn0w’s Log\n个人不是很喜欢返回键把左边菜单隐藏，给自己留个档。\n加载进度条 在layouts/partials/footer/custom.html加入代码：\n1\u0026lt;!-- 加载进度条 --\u0026gt; 2\u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/gh/zhixuan2333/gh-blog@v0.1.0/js/nprogress.min.js\u0026#34; integrity=\u0026#34;sha384-bHDlAEUFxsRI7JfULv3DTpL2IXbbgn4JHQJibgo5iiXSK6Iu8muwqHANhun74Cqg\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 3\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdn.jsdelivr.net/gh/zhixuan2333/gh-blog@v0.1.0/css/nprogress.css\u0026#34; integrity=\u0026#34;sha384-KJyhr2syt5+4M9Pz5dipCvTrtvOmLk/olWVdfhAp858UCa64Ia5GFpTN7+G4BWpE\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34; /\u0026gt; 4\u0026lt;script\u0026gt; 5 NProgress.start(); 6 document.addEventListener(\u0026#34;readystatechange\u0026#34;, () =\u0026gt; { 7 if (document.readyState === \u0026#34;interactive\u0026#34;) NProgress.inc(0.8); 8 if (document.readyState === \u0026#34;complete\u0026#34;) NProgress.done(); 9 }); 10\u0026lt;/script\u0026gt; 但这个方法可能在引入pjax后会失效，需要查看pjax文档进行监听\n樱花背景 下载地址【sakura.js】(CTRL + C 保存)，并放到assets/background文件夹下\n在layouts/partials/footer/custom.html加入代码：\n1\u0026lt;!-- 樱花背景 --\u0026gt; 2\u0026lt;script src={{ (resources.Get \u0026#34;background/sakura.js\u0026#34;).Permalink }}\u0026gt;\u0026lt;/script\u0026gt; 文章最后修改时间 在hugo.yaml加入代码：\n1frontmatter: 2 # 按优先级排序： 3 # :git - 文件提交修改时间 4 # lastmod - 文章里 lastmod 字段 5 # :fileModTime - 文件修改时间 6 # :default - 默认时间 7 lastmod: [\u0026#34;:git\u0026#34;, \u0026#34;lastmod\u0026#34;, \u0026#34;:fileModTime\u0026#34;, \u0026#34;:default\u0026#34;] 8 9enableGitInfo: true 10gitRepo: \u0026#34;https://github.com/your_username/your_username.github.io\u0026#34; 这个方法比较简单，但是时间会出现在文章末尾：\n更新时间的格式去 hugo.yaml 中的 params.dateFormat.lastUpdated 进行修改：\n1dateFormat: 2 published: 2006-01-02 3 lastUpdated: 2006-01-02 留档：文章开头显示更新时间，参考【Hugo】Stack主题自定义修改\n页面浏览量 这里用的是vercount\n修改layouts/partials/footer/custom.html，引入脚本：\n1\u0026lt;script defer src=\u0026#34;https://cn.vercount.one/js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 修改layouts/partials/article/components/footer.html，（没有文件夹可以从模板中复制）在合适的位置下加入代码：\n1\u0026lt;footer class=\u0026#34;article-footer\u0026#34;\u0026gt; 2 {{ partial \u0026#34;article/components/tags\u0026#34; . }} 3 4 {{ if and (.Site.Params.article.license.enabled) (not (eq .Params.license false)) }} 5 \u0026lt;section class=\u0026#34;article-copyright\u0026#34;\u0026gt; 6\t... 7 \u0026lt;/section\u0026gt; 8 {{ end }} 9 10 {{- if ne .Lastmod .Date -}} 11 \u0026lt;section class=\u0026#34;article-lastmod\u0026#34;\u0026gt; 12\t... 13 \u0026lt;/section\u0026gt; 14 {{- end -}} 15 \u0026lt;section\u0026gt; 16 页面浏览量\u0026lt;span id=\u0026#34;vercount_value_page_pv\u0026#34;\u0026gt;loading... \u0026lt;/span\u0026gt; 17 \u0026lt;/section\u0026gt; 18\u0026lt;/footer\u0026gt; 若想浏览次数出现在文章开头，参考【Hugo】Stack主题自定义修改，但是会有bug，咱小白先弄简单的。\n页面样式 首页文章标题悬停动画 F12 打开开发者模式，找到标题元素的位置，是在.article-list下的article/h2元素的a标签\n在assets/scss/custom.scss文件中添加代码\n1.article-list article h2 a { 2 display: inline-block; 3 transition: transform 0.25s ease; 4} 5 6.article-list article h2 a:hover{ 7 transform: scale(1.05); 8} 首页文章分类悬停动画 同理找到该标签的位置，在article-category 的 a 标签中\n添加assets/scss/custom.scss文件中添加代码:\n1.article-category a { 2 display: inline-block; 3 transition: transform 0.25s ease; 4} 5 6.article-category a:hover { 7 transform: scale(1.05); 8} 右侧边栏动画 原理和上面首页的动画方式类似，找到相应位置，在assets/scss/custom.scss中添加代码就行\n1//右侧标签悬停动画 2.tagCloud .tagCloud-tags a { 3 display: inline-block; 4 transition: transform 0.25s ease; 5} 6 7.tagCloud .tagCloud-tags a:hover { 8 transform: scale(1.05); 9} 10 11//归档小图标放大动画 12.archives .widget-archive--list { 13 display: inline-block; 14 transition: transform 0.25s ease; 15} 16 17.archives .widget-archive--list:hover { 18 transform: scale(1.05); 19} 参考文献 小白hugo博客装修笔记（2）- B1ain’s Blog\nHugo-stackの美化 II\nHugo Stack 魔改美化 | Naive Koala\nStack 主题的自定义 | L1nSn0w’s Log\n【Hugo】Stack主题自定义修改\n凡凡小站\n","date":"2025-10-24T19:19:24+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/hugo-hugo%E7%BE%8E%E5%8C%96/","title":"[Hugo] Hugo美化"},{"content":" 论文来自于：\nSajadmanesh S, Gatica-Perez D. Locally private graph neural networks[C]//Proceedings of the 2021 ACM SIGSAC conference on computer and communications security. 2021: 2130-2145.\n摘要 图神经网络（GNN）在学习节点表示以完成多种图推理任务时表现出色。然而，当节点代表个人或涉及敏感信息的人类相关变量时，基于图数据的学习可能引发隐私问题。尽管已有大量针对非关系数据的隐私保护深度学习技术，但针对图上深度学习算法的隐私问题研究较少。\n本文研究节点数据隐私问题：图节点拥有潜在敏感数据（特征向量与标签）保持私有，但可被中心服务器用于训练GNN。提出一种基于局部差分隐私（LDP）的隐私保护GNN训练算法，具备形式化的隐私保证。\n核心贡献 多比特机制（Multi-bit Mechanism）：LDP编码器 + 无偏整流器，实现高效通信下特征扰动与收集。 KProp层：基于多跳聚合的简单图卷积层，增强去噪能力，提升首层卷积估计精度。 Drop鲁棒训练框架：利用KProp对噪声标签进行去噪，提升泛化性能。 理论分析：隐私保证、误差界。 实验验证：在真实数据集上实现良好的准确率-隐私权衡。 问题定义与背景 图结构 图 $ G = (V, E, X, Y) $ $ V = V_L \\cup V_U $：有标签节点 + 无标签节点 $ X \\in \\mathbb{R}^{|V| \\times d} $：特征矩阵（私有） $ Y \\in {0,1}^{|V| \\times c} $：标签矩阵（私有） 服务器拥有：$ V, E $ 节点私有：$ X, Y $ 目标 在不泄露原始 $ X, Y $ 的前提下，训练GNN。\n图神经网络（GNN）基础 每层嵌入更新：\n$$ h^l_{N(v)} = \\text{Aggregate}^l \\left( \\{ h^{l-1}_u \\mid \\forall u \\in \\mathcal{N}(v) \\} \\right) $$$$ h^l_v = \\text{Update}^l \\left( h^l_{N(v)} \\right) $$ 初始：$ h^0_v = x_v $ 最后一层输出 $ c $ 维向量 + softmax 预测标签 局部差分隐私（LDP） 定义 2.1：机制 $ M $ 满足 $ \\epsilon $-LDP，若对任意输入 $ x, x\u0026rsquo; $ 和输出 $ y $：\n$$ \\Pr[M(x)=y] \\leq e^\\epsilon \\Pr[M(x')=y] $$ $ \\epsilon $：隐私预算（越小越强） 实现方式：本地扰动 → 聚合去噪 整体框架 流程 节点本地扰动 每个节点用 Multi-bit Encoder 对特征进行局部扰动，生成编码向量；有标签节点用 广义随机响应 扰动标签。一次性上传至服务器。 服务器去偏估计 服务器用 Multi-bit Rectifier 对所有扰动向量进行无偏校正，估计近似首层卷积输入。 插入 KProp 去噪 在 GNN 前插入 KProp 多跳聚合层，通过 K 步线性传播平均化噪声，增强低度节点表示。 GNN 训练（Drop 鲁棒框架） 前向传播得到预测概率 用 KProp 聚合预测概率，生成“净化标签” 结合扰动标签 + 净化标签计算损失 反向传播更新模型 迭代至收敛 每轮重复扰动→上传→去偏→训练，直至模型收敛。 推理 训练好的 GNN 直接用于新节点分类（无需再扰动）。 核心特点：一次通信 + 聚合去噪 + 标签净化，实现 隐私保护下的高精度 GNN 训练。\n核心组件 Multi-bit Encoder（节点端） 输入：节点 $ v $ 的原始特征 $ x_v \\in \\mathbb{R}^d $ 输出：扰动编码向量 目标：高维特征下高效通信 + LDP保证 方法：扩展1-bit机制至多维，逐维度采样并翻转 每个节点仅需一次通信发送扰动特征\nMulti-bit Rectifier（服务器端） 输入：所有节点的扰动编码 输出：无偏估计的聚合特征 作用：校正统计偏差，近似首层图卷积 关键：利用线性聚合作为天然去噪机制 聚合平均化注入的差分隐私噪声\nKProp（多跳聚合层） $$ h_v^{(k)} = (1 - \\alpha) h_v^{(k-1)} + \\alpha \\cdot \\text{Aggregate}(h_u^{(k-1)}, u \\in \\mathcal{N}(v)) $$ 输入：扰动后特征 操作：迭代K次线性聚合，扩展有效邻域至K跳 作用： 增强去噪（噪声 ~ 1/√聚合规模） 提升低度节点估计精度 可插入任意GNN前作为预处理层 特别适用于幂律分布图（多数节点度数低）\n标签扰动（Randomized Response） 正确标签保留概率：$ p = \\frac{e^\\epsilon}{e^\\epsilon + c - 1} $\n其他类别随机翻转为：$ q = \\frac{1}{e^\\epsilon + c - 1} $\nDrop：鲁棒训练框架 挑战 噪声标签 → 过拟合 → 泛化差 无干净验证集 → 难以调参 解决方案 利用KProp对标签预测概率进行多跳聚合 估计每个节点的邻域标签频率 选择频率最高者作为“净化标签” 用于损失计算或早停判断 不依赖任何干净数据（特征/标签）\n符号表（文中主要符号） 符号 含义 $ G = (V, E, X, Y) $ 图结构 $ V_L, V_U $ 有/无标签节点集 $ x_v \\in \\mathbb{R}^d $ 节点 $ v $ 的特征向量 $ y_v \\in {0,1}^c $ 节点 $ v $ 的one-hot标签 $ \\mathcal{N}(v) $ 节点 $ v $ 的邻居集（可含自身） $ h^l_v $ 第 $ l $ 层节点 $ v $ 的嵌入 $ \\epsilon $ 隐私预算 $ K $ KProp 步数 $ M(\\cdot) $ LDP扰动机制 $ \\text{MB-Encoder} $ 多比特编码器 $ \\text{MB-Rectifier} $ 多比特整流器 ","date":"2025-10-24T15:58:38+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/privacy-lpgnnlocally-private-graph-neural-networks/","title":"[Privacy] LPGNN：Locally private graph neural networks"},{"content":"按度数分布划分区间，总隐私预算以区间 Yuan Y, Lei D, Fan Q, et al. Achieving Adaptive Privacy-Preserving Graph Neural Networks Training in Cloud Environment[C]//2024 IEEE 12th International Conference on Information, Communication and Networks (ICICN). IEEE, 2024: 181-186.\nYuan Y, Lei D, Zhang C, et al. Personalized differential privacy graph neural network[J]. IEEE/CAA Journal of Automatica Sinica, 2025.\n给出一个总体区间 $[ε_1, ε_2]$，再按节点度数把用户分桶，并把 $[ε_1, ε_2]$切成多个子区间；每个用户的“初始”隐私预算 $ε_s$ 会从对应子区间里随机采样（按指数分布采样），因此不同用户拿到的起始预算彼此不同\n根据度数的大小，我们可以将这些节点划分成几个区间。例如：\n区间1：度数小于等于 10 区间2：度数大于 10 且小于等于 50 区间3：度数大于 50 根据区间的划分，隐私预算会在区间 $[ε₁, ε₂]$ 内进行分配。假设$ε₁ = 0.1$，$ε₂ = 1$，并且使用指数分布来决定隐私预算的具体值：\n对于区间1，为其分配较高的隐私预算（如接近$ε₂ = 1$）。 对于区间，隐私预算会适中。 对于区间3，隐私预算会较低（接近$ε₁ = 0.1$）。 隐私预算的分配在度数区间的划分上使用了指数分布。其公式为： $$ \\text{Sample from exponential distribution } f(y, \\lambda) = \\lambda e^{-\\lambda y}, \\, (y \\geq 0) $$ 之后用户用各自的 $ε_s$ 加噪：$\\hat X_s=f(X_s)+\\mathrm{Lap}(\\Delta f/ε_s)$，\nAPPGNN按照节点度数划分为若干个区间，并将 隐私预算区间 对应划分，根据节点度数的 比例分布，将其 映射到指数分布的分位数区间，从中 采样出个性化的隐私预算。\n拓扑重要性个性化，总隐私预算以区间 Lei D, Song Z, Yuan Y, et al. Achieving Personalized Privacy-Preserving Graph Neural Network via Topology Awareness[C]//Proceedings of the ACM on Web Conference 2025. 2025: 3552-3560.\n提出“邻接信息熵”（Adjacency Information Entropy, AIE）来衡量节点拓扑重要性，既考虑直连邻居也考虑间接关系：先算邻接度 $AD_u=\\sum_{v\\in \\mathcal N_u} D_v$，再定义概率 $p_u=D_u/AD_v$，最后得信息熵式的 $AIE_u=-\\sum_{v\\in \\mathcal N_u}(p_u\\log_2 p_u),p_v$（式(4)–(6)）。重要性越高→隐私敏感度越高。\n将总预算区间 $[,\\epsilon_b,\\epsilon_e,]$ 按节点隐私敏感度分成 $M$ 个等级与对应子区间，并假设预算在该区间内服从指数分布（真实网络度分布常呈幂律，少数节点很重要）。通过指数分布分位点把 $[,\\epsilon_b,\\epsilon_e,]$ 切成 $(\\epsilon_b,\\epsilon_1],(\\epsilon_1,\\epsilon_2],\\dots,(\\epsilon_{M-2},\\epsilon_e]$，切分边界用式(7)(8)的分位数关系确定；重要节点→分到更小的 $\\epsilon$（更强保护/更大噪声），不太重要的节点→更大的 $\\epsilon$（更少噪声）。每个节点最终从其子区间随机采样得到个性化预算 $\\epsilon_i$。\n各节点用自己的 $\\epsilon_i$ 做拉普拉斯机制：$\\hat X_i=f(X_i)+\\mathrm{Lap}(\\Delta f/\\epsilon_i)$（式(3)、(9)），并用随机响应扰动标签（式(12)），实现特征+标签双重保护。\n因不同邻居被加的噪声强度不同，直接平均会放大高噪声邻居的负面影响。论文据“越重要→预算越小→噪声越大”的链条，给重要邻居更小权重： $$ W_{u,v}=1+\\frac{1}{D_u}-\\frac{AIE_v}{\\sum_{i\\in Ner_u}AIE_{u,i}+AIE_u}, $$ 再做加权聚合（式(10)(11)）。这样能抑制差异化DP噪声对表示学习的影响。\nTDP-GNN 通过拓扑结构识别节点重要性，划分为多个隐私敏感度等级，映射到指数分布的预算区间并采样个性化预算，再结合加权聚合抑制噪声。\n上下文多臂赌博机（CMAB）算法分配（区间） Zhang X, Zhou Y, Hu M, et al. BGTplanner: Maximizing Training Accuracy for Differentially Private Federated Recommenders via Strategic Privacy Budget Allocation[J]. IEEE Transactions on Services Computing, 2025.\n步骤 1：生成奖励的预测\n对于每个动作 $a_t$ 和上下文 $X_t$，BGTplanner使用高斯过程回归模型预测奖励 $r_t$： $$ \\mu(z_t) = \\text{GPR}(a_t, X_t) $$ 步骤 2：计算动作的评分\n根据预测的奖励 $\\mu(z_t)$，为每个可能的动作（隐私预算分配方案）计算一个评分 $\\beta_t(a)$，公式如下： $$ \\beta_t(a) = \\mu(z_t) - \\langle \\epsilon_{\\text{total}} - \\epsilon_t, \\lambda_t \\rangle $$​\t其中，$\\lambda_t$ 是用于长期隐私预算约束的拉格朗日乘子，确保在整个训练过程中不会超出总预算。\n步骤 3：选择最优的隐私预算分配方案 $\\gamma$ 是探索和利用的权衡参数，控制着系统是否偏向于选择当前最优的动作（利用）或探索其他可能的动作。 $A$ 是动作空间的大小，表示可能的隐私预算分配方案的数量。 步骤 4：更新隐私预算消耗 在每轮训练之后，BGTplanner通过隐私预算消耗函数来计算实际消耗的隐私预算 $\\epsilon_t$，并更新剩余预算。 假设我们有如下参数：\n总隐私预算 $\\epsilon_{\\text{total}} = 10$ 隐私预算区间：$\\epsilon_{\\text{min}} = 1, \\epsilon_{\\text{max}} = 5$ 每轮的隐私预算分配动作是从区间 $[1, 5]$ 中选择的。 在某个训练回合中，BGTplanner预测奖励为：\n$$ \\mu(z_t) = 0.8 \\quad (\\text{基于历史信息和上下文的奖励预测}) $$ 接着，BGTplanner计算所有可能动作的评分，并选择评分最高的动作。例如，假设动作 $a_1$ 得分为 0.9，动作 $a_2$ 得分为 0.7，最终选择 $a_1$ 作为隐私预算分配方案。\nBGTplanner 每轮都用 CMAB 从一组预算选项中，智能选一个最合适的隐私预算来用\n探讨隐私预算的推荐值 Du W, Ma X, Dong W, et al. Calibrating privacy budgets for locally private graph neural networks[C]//2021 International Conference on Networking and Network Applications (NaNA). IEEE, 2021: 23-29.\n采用了 Multi-bit LDP Mechanism (LPGNN的方法)对用户特征进行扰动:\n输入：\n用户特征向量$ x∈[α,β]^d$ 隐私预算$\\epsilon$ 控制参数 m（每次扰动的特征维度数） 输出：\n扰动后的特征向量 $ x∈{-1,0,1}^d$ 通过链路预测准确率和加入属性推断攻击后的F1-score值推荐隐私预算值\n项目 内容 目标 在 LDP 保护的 GNN 中合理选择隐私预算 ε 方法 利用属性推断攻击效果作为隐私度量，结合链路预测准确率评估效用 隐私机制 Multi-bit LDP 机制，用户本地扰动特征，服务器无偏重构 推荐 ε 值 0.5 ~ 1（视具体业务对隐私和效用的需求） 基于遗传算法（GA）的隐私预算分配 Li Y, Song X, Tu Y, et al. GAPBAS: Genetic algorithm-based privacy budget allocation strategy in differential privacy K-means clustering algorithm[J]. Computers \u0026amp; Security, 2024, 139: 103697.\n通过分析噪声对质心的影响，推导出 最小隐私预算$ε_m$： $$ ε_m=(\\frac{200k^3d+(1+d)^2}{N^2}(1+ρ^2))^{1/2} $$ 其中：\nk：聚类数；d：数据维度；N：样本数量；ρ：噪声相关系数（通常取 0.225）； 每轮预算必须满足：$ε_t$≥$ε_m$，否则噪声过大导致质心不收敛。\n每个个体是一个长度为 $T$ 的浮点数组：${ε_1,ε_2,\u0026hellip;,ε_T}$ GAPBAS 将每轮隐私预算组合成序列，作为遗传算法的个体，在满足总预算和最小预算约束下，优化出使聚类效果（NICV）最优的预算分配策略。\n基于隐私安全等级（Privacy Security Level, PSL） Shen Z, He S, Wang H, et al. A differential privacy budget allocation method combining privacy security level[J]. Journal of Communications and Information Networks, 2023, 8(1): 90-98.\n提出 PSL 方法：为每个位置分配一个“隐私安全等级”，并据此动态分配隐私预算 $ε$，实现 个性化、拓扑感知的隐私保护。\n步骤：\n使用 P-series（p-级数） 为初始敏感位置分配隐私预算\n使用 P-series 为初始敏感位置分配预算： $$ \\varepsilon_{m} = \\frac{\\varepsilon}{\\zeta(p)} \\times \\frac{1}{m^{p}}, \\quad m \\in \\mathbb{N}^{+}, \\quad p \u003e 1 $$ $\\varepsilon$：总隐私预算； $\\zeta(p)$：P级数收敛值（如 p=2 时，$\\zeta(2)= π²/6$； m：敏感位置编号（按重要性排序）； 结果：重要位置（m 小）获得更大预算（更小噪声） 根据 距离与节点度 为敏感点的邻居分配预算，并支持 动态时间调整\nPSL定义： $$ \\mathrm{PSL}(k_{m}) = \\lambda \\times \\varepsilon_{m} = \\lambda \\times \\frac{\\varepsilon}{\\zeta(p)} \\times m^{p} $$ PSL 与预算$\\varepsilon$ 成反比； $\\lambda$ 为调节参数; 用于衡量位置的“隐私敏感度”。 将 PSL 映射为隐私预算$ε$，满足 $\\epsilon × PSL = \\gamma$（$\\gamma$ 为常数）\n假设：\n敏感节点 $k_1$ 的 PSL = 0.6079； 邻居 A 距离为 1，邻居 B 距离为 2； NPS = {A, B}； 则： $$ \\mathrm{PSL}_{A} = \\frac{1/1}{(1/1 + 1/2)} \\times 0.6079 = 0.4053, \\\\ \\mathrm{PSL}_{B} = \\frac{1/2}{(1/1 + 1/2)} \\times 0.6079 = 0.2026 $$ 再映射回预算： $$ \\epsilon_{A} = \\frac{\\gamma}{\\mathrm{PSL}_{A}} = \\frac{0.5}{0.4053} \\approx 1.23 , \\\\ \\epsilon_{B} = \\frac{\\gamma}{\\mathrm{PSL}_{B}} = \\frac{0.5}{0.2026} \\approx 2.47 $$ PSL 方法通过 P-series 为敏感位置分配递减预算，再结合距离与节点度为邻居分配个性化预算，并支持时间动态调整，实现“重要位置多留数据，敏感位置多加噪声”的精细化隐私保护。\n","date":"2025-10-23T19:00:59+08:00","permalink":"https://bobqaq003.github.io/Kuka-hugo/p/privacy-%E8%87%AA%E9%80%82%E5%BA%94%E9%9A%90%E7%A7%81%E9%A2%84%E7%AE%97%E5%88%86%E9%85%8D/","title":"[Privacy] 自适应隐私预算分配"}]