-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.json
More file actions
1 lines (1 loc) · 81.4 KB
/
Copy pathsearch.json
File metadata and controls
1 lines (1 loc) · 81.4 KB
1
[{"title":"build_blog","url":"/2026/02/12/build-blog/","content":"环境准备\n安装Node.js\n安装Git\n\ngit -vnode -vnpm -v\n搭建Hexo\n安装Hexo\n\nnpm install -g hexo-cli\n\n初始化博客\n\n# 创建并进入文件夹mkdir my-blogcd my-blog# 初始化 Hexohexo init# 安装依赖包npm install\n\n本地预览\n\nhexo s\n部署到Github Pages\n创建Github仓库,名字为:你的Github用户名.github.io\n配置Hexo部署信息,打开本地博客根目录下的 _config.yml文件,找到最底部的 deploy字段\n\n# Deploymentdeploy: type: git repo: git@github.com:Seanoll/seanoll.github.io.git # ssh连接比http快,也可用: # repo: https://github.com/你的GitHub用户名/你的GitHub用户名.github.io.git branch: master \n\n安装部署插件\n\nnpm install hexo-deployer-git --savehexo d\n\n回到Github仓库页面,点击Settings -> Pages, 在 Source下拉菜单中,选择 Deploy from a branch,然后选择刚才配置的分支(如 main或 master),点击 Save\n\n内容\n在Hexo官网或者Github上找主题,下载到themes文件夹或者git clone \n在博客根目录下的_config.yml中下拉找到theme改为theme: 什么名字\n在主题的source文件夹里修改配置文件\n使用hexo new 文章名来新建一篇文章,找到并编辑它,写好之后保存\n使用hexo new page 网页名来新建一张页面\n使用hexo g来生成相应的文件,使用hexo d部署更新到网站上\n\n在配置过程中出现版本问题\n在命令行里无法删除一个文件夹Permission Denied可能是因为该文件夹在图形化界面中打开。\nhexo init必须初始化一个空文件夹,如果已经删干净了还是提示有文件,使用ls -a来查看隐藏文件并删除。 \ngit clone或者wget和curl速度太慢时,将网络代理设置成全局模式。\n在 YAML 中,冒号 :后面必须有一个空格。branch:master应该写为 branch: master。非末尾的后大括号后面需要加逗号,。\n安装新版nodejs:\n\n# 1. 安装 nvm(如果还没安装)curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash# 或者wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash# 2. 重新打开终端,然后安装 Node.js 20nvm install 20nvm use 20# 设置为默认版本nvm alias default 20# 3. 验证版本node -v # 应该显示 20.x.x \n","categories":["skill"],"tags":["博客"]},{"title":"Hello World","url":"/1970/01/01/hello-world/","content":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.\nQuick StartCreate a new post$ hexo new "My New Post"\n\nMore info: Writing\nRun server$ hexo server\n\nMore info: Server\nGenerate static files$ hexo generate\n\nMore info: Generating\nDeploy to remote sites$ hexo deploy\n\nMore info: Deployment\n","tags":["博客"]},{"title":"学习,工作,生活与热爱","url":"/2026/02/14/%E5%AD%A6%E4%B9%A0%EF%BC%8C%E5%B7%A5%E4%BD%9C%EF%BC%8C%E7%94%9F%E6%B4%BB%E4%B8%8E%E7%83%AD%E7%88%B1/","content":"一个人的行为应该是由他的价值观所决定的,当我们迷茫的时候,我们应该思考一下自己的价值观了。加缪说:“真正严肃的哲学问题只有一个,那就是自杀。”我们为什么还活着,我们真正看重的东西是什么,这就是我们人生的指南针。在大学以前,我的所有行为都是参考网络和身边观点的局部最优解,但是现在不一样了,现在的观点五花八门,自己没有明确的价值判断标准的时候,就会看哪个都觉得有道理。看完一本书,自认为恍然大悟,后来看到网上的评论说书里的案例都是胡编乱造的,就站到了对立面。因此左右脑互搏,做事情摇摆不定,连自己应该高兴还是伤心都不能确定。人在忙的时候是不会感受到无意义感的,因为这时只会想“忙完这件事,真正的生活就会开始”。然而事实上,真正的生活就是现在,就是忙的时候。\n学习我曾经想,等我有能力了,我要改革中国的教育制度,让中国的学生生活得更轻松幸福。批评高考、大学教育、原生家庭的文章我已经看了不少了,再看都有点厌倦了,指出问题的人很多,但是我不知道怎么做,这就产生了一种深深的无力感。我曾经想,我学习,应该是因为我热爱知识,并且获得新知识的过程也是快乐的。我喜欢创造,我喜欢用现有的工具设计自己的软件,我喜欢设计车灯,设计零食,布置自己的房间,设计最有性价比的购物选择,但是现在它们可以用人工智能来替代。我选择计算机是因为我喜欢它,但是学得深入之后我才发现我喜欢它的浅显知识和成熟应用的部分,我不喜欢深奥的原理,但是除了它我不知道还能选择什么。学习是为了找工作,我很不愿意承认。竞争激烈的行业总会驱使人没有看清方向就赶快前进,看清方向需要时间,大一进实验室的弊端就是不确定自己是否真的对这个研究方向感兴趣。其实看清了方向也不能保证自己选择的方向就是正确的,无论选择哪条路,都会有一部分不满意的地方,都会美化未选择的路。极致的内卷道路已经被探索出来了,但是我没有那么多的能量,亲人的评价和期待很高,但是他们的话权重不高,躺平也算一条出路,但是社会和亲人还是我心里的一道坎。遵从自己的内心,量力而行,才不会后悔。\n工作我曾经想,我一定要找一个work-life-balance的工作,但是无奈计算机行业的人太多,我看到的评价都是说工作时间长,强度大。现在,我只想要一个不会失业,相对轻松的工作了。我曾经想,等我有能力了,我要让中国的工人团结起来,用罢工和革命反抗压迫,普及八小时工作制,缩小贫富差距。建筑工地上搬砖的工人、大街上扫地的清洁工和医生老师应该是一样被尊重的,而不是像现在的社会风气一样看不起赚钱少的,这是我眼里的社会主义。我也看过很多反映社会问题的文章或者视频,比如失业、腐败和法律不公,同样地,我也有深深的无力感。我喜欢软件开发,我看到的评价是竞争激烈,晋升难,易失业。如果我在互联网兴起的时候毕业,或者大家不会因为钱而转行,那将是非常幸运的。现在的风口是人工智能,谁都无法预测等到自己毕业的时候风口是什么,随大流总是安心的。普通家庭出身的孩子,没有看透经济的目光,也没有太多的容错空间,如果选择了不好的专业,可能会导致他的收入很低,如果创业失败了,还有可能背负巨额债务,所以随大流是局部最优解。 \n生活我曾经想,等我有很多钱和很多时间的时候,我要环球旅行,我要看一看世界的多彩,对于高雅的艺术,我可能无法理解,但是我可以用身体感受不同的地方。大概从14岁开始,我不喜欢玩游戏了,游戏的输赢和排名没有什么意义,在固定的规则下完成任务也没有价值。我眼里的游戏已经成为了一种社交的工具,而不是热爱,如果两个人都玩一款游戏,那他们就可以在尴尬的聊天中找到一个难得的共同话题。正如我不能理解过生日的意义。人们非常害怕无聊,所谓的“生活”“休息”和“去玩”只是绞尽脑汁地想出一种逃避无聊和消磨时间的应急措施,或者是遵从本能地寻求单纯的快感,我不认为这是有意义的生活。但是在空闲时间,我也会不知道怎么度过。购物中心已经失去了印象中的吸引力,购买食物和服饰实在是无聊透顶,商场里的娱乐活动也寥寥无几,精神世界匮乏,只有消费主义在弥漫。刷短视频或者打麻将能给予短暂的快感,但是空虚更难忍受。在我眼里,给有需要的人提供实质性的帮助,了解之前从未了解的事物是有价值的生活。所以我愿意做志愿服务,愿意尝试新事物,去博物馆,不过大多是三分钟热度。探索生活的道路永远不会停止,我喜欢的活动也会变,书中给出的观点也各有特色,从事创造性的活动,为理想奉献……\n热爱我曾经想,我会终身奉行“追求源于热爱,极致源于梦想”。但是当热爱变成别人强迫自己完成的工作的时候,它可能就不再吸引人了。我想要的,是自由地探索我热爱的领域,如果发现我不喜欢的地方,我就逃避。很多时候,我分不清到底是因为擅长才热爱还是因为热爱才擅长。在热爱的领域里,自己不如别人优秀,不能成为放弃的理由。我不看重钱,我看重价值,做有价值的事,在什么情况下都能做。身体的伤病可以治疗,心理的、社会关系的痛苦可以调节,只要活着,我就有机会做有价值的事,我的生命就有意义。\n","categories":["thinking"]},{"title":"3D_Gaussian_Splatting笔记","url":"/2026/02/24/3D-Gaussian-Splatting%E7%AC%94%E8%AE%B0/","content":"原理Splatting\n渲染(Rendering)是计算机图形学中的核心环节,指将三维场景(3D Scene)通过计算转化为二维图像(2D Image)的过程。简单来说,就是“把3D模型变成2D画面”。\n在传统渲染中,物体表面由无数个三角形(面)拼接而成。而 Splatting 认为,物体表面也可以由无数个带有大小和颜色的“点”来构成。渲染时,计算机不再计算三角形的边和面,而是直接将这些“点”投影到屏幕上,并填充像素。\n步骤:\n选择雪球\n抛掷雪球:从3D到2D,得到足迹\n加以合成,形成最后的图像\n\n\n选择雪球:\n点是没有体积的,我们需要给点一个核,核可以是高斯/圆/正方体,使得它有体积,能够被投影。\n高斯椭球有很好的数学性质:仿射变换后高斯核仍然闭合,3D降维到2D后依然为高斯椭球。\n三维高斯分布,其概率分布的形状(想一下正态分布的y-x图像)由均值和协方差矩阵决定令为常数, \n经过变形可以得到 \n显然这是一个椭球面的方程,当取不同常数时,椭球面不同,最终形成实心椭球。\n高斯分布的仿射变换:对于三维空间中的做仿射变换,有三维空间中的标准高斯分布为,因为三维高斯分布的协方差矩阵一定是正定对称矩阵,所以其可以被分解为(矩阵的合同变换),所以任意的高斯分布都可以看作是标准高斯分布经过仿射变换得到的。\n对于任意可逆的仿射变换Ax+b,它的线性变换矩阵A都可以被分解为缩放矩阵S和旋转矩阵R的乘积,以下是其推导过程: \n仿射变换是否可逆,完全取决于其线性部分矩阵是否可逆,因为 可逆,故 是对称正定矩阵,可以正交对角化。其中 且 。定义。显然 也是对称正定的,是缩放矩阵。因为可逆,定义,,所以是正交矩阵,所以是旋转矩阵。\n\n\n所以协方差矩阵可以被分解def computeCov3D(scale, mod, rot): # create scaling matrix S = np.array( [[scale[0] * mod, 0, 0], [0, scale[1] * mod, 0], [0, 0, scale[2] * mod]] ) # normalize quaternion to get valid rotation # we use rotation matrix R = rot # compute 3d world covariance matrix Sigma M = np.dot(R, S) cov3D = np.dot(M, M.T) return cov3D\n\n\n\n抛掷观测变换:\n在计算机图形学中,观测变换(Viewing Transformation)包含了视图变换(View Transformation)和投影变换(Projection Transformation)两个阶段。\n模型变换和视图变换:\n在计算机图形学中,模型变换(Model Transformation)是指将三维模型从其局部坐标系(Model Space)变换到世界坐标系(World Space)的过程。视图变换(View Transformation)是指将场景中所有物体的坐标从世界坐标系(World Space)转换到相机坐标系(View Space)的过程。\n模型变换和视图变换都是仿射变换\n世界坐标系:\n高斯核中心:\n高斯核:\n是该高斯分布的协方差矩阵\n\n\n相机坐标系:\n高斯核中心:\n高斯核:\n均值:\n协方差矩阵:\n\n\n\n\n投影变换:\n投影变换通常是非线性变换\n相机坐标系:\n高斯核中心:\n高斯核:\n均值:\n协方差矩阵:\n\n\n投影变换后的坐标系:\n高斯核中心:\n高斯核:\n均值:这里我的理解是:,我们把高斯核中心移动到了中。 \n协方差矩阵:\n\n\n由于这一步是非线性、非仿射的,我们不能用来求出协方差矩阵。但是可以用雅可比矩阵来局部线性近似,从而\n雅可比矩阵的推导:\n\n\n\n视口变换:\n均值控制椭球的位置,协方差矩阵控制椭球的形状,在投影变换中,我们对协方差矩阵只做了从四棱锥到长方体的变换,并没有进行缩放和平移(从长方体到原点为中心的正方体的变换),椭球的形状是正常的,因此我们只需要对均值进行视口变换。 \n像素坐标系:\n均值:\n协方差矩阵:\n\n\n\n着色球谐函数\n球函数(Spherical Function)是定义在单位球面(即半径为1的球面)上的函数。它通常用球坐标表示,自变量是极角(θ)和方位角(φ),记作 。在数学上,球函数通常表示为:\n球函数描述的是球面上的某种属性分布,例如地球表面的温度、重力场,或者计算机图形学中物体表面的光照强度。由于球面是二维曲面,所以球函数只需要两个参数(θ, φ)就能确定。\n球谐函数(Spherical Harmonics)是定义在球面上的特殊函数,它构成了球面上的正交基,类似于傅里叶级数在圆上的作用。简单来说,它是球面上的“正弦波”和“余弦波”。通常表示为:。其中阶数越高,表达能力越强。任何定义在球面上的函数都可以用球谐函数的线性组合来近似表示(类似于傅里叶级数展开)。\n在3D-Gaussian-Splatting中点云颜色(R,G,B)使用球谐函数来表示其中是球谐函数,是系数,也是三维向量(R,G,B),作者采用3阶球谐函数进行拟合,因此我们知道需要个参数。\n我的理解是:我们事先计算好了48个参数,因此现在的颜色只与相机位置和高斯椭球的位置(即均值)有关,这两个变量决定了球谐函数里的和,最终我们实现了对于不同的观测角度都有不同的颜色。\n\n实现训练总流程初始化\n从SfM点云初始化\n\nCOLMAP稀疏点云 ↓┌─────────────────────────────────────────────┐│ 1. 提取3D点位置 ││ μᵢ = 点云中的点位置 │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 2. 初始化颜色 ││ cᵢ = 最近视角的平均颜色 │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 3. 初始化不透明度 ││ αᵢ = 0.1(较小初始值) │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 4. 初始化协方差矩阵 ││ Σᵢ = 基于最近邻距离估计 ││ 或使用固定小值 │└─────────────────────────────────────────────┘使用KNN法找到3近邻,半径是三近邻的距离的平均\n\n从随机位置初始化 \n使用场景:没有SfM点云时\n在场景边界内随机采样位置\n其他参数随机初始化\n\n\n对每一个点,我们有:\n均值(三维向量)\n协方差矩阵(三行三列矩阵)\n颜色(三维向量RGB)\n不透明度(一个数)\n\n\n\n迭代优化\n单次迭代流程\n\n迭代 t:┌─────────────────────────────────────────────┐│ 1. 前向传播 ││ - 可微光栅化 ││ - 生成渲染图像 I_rendered │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 2. 损失计算 ││ L = L1(I_rendered, I_gt) + ││ λ_ssim × L_ssim(I_rendered, I_gt) │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 3. 反向传播 ││ - 计算梯度 ∇L ││ - 更新参数: ││ μ ← μ - lr_μ × ∇L/∇μ ││ c ← c - lr_c × ∇L/∇c ││ α ← α - lr_α × ∇L/∇α ││ Σ ← Σ - lr_Σ × ∇L/∇Σ │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 4. 密度控制(每N次迭代) ││ - 克隆/分割/剪枝 │└─────────────────────────────────────────────┘\n\n训练循环伪代码\n\n# 伪代码示例def train_3dgs(images, cameras, iterations=30000): # 初始化 gaussians = initialize_from_sfm(cameras) for iteration in range(iterations): # 随机选择一个视角 camera_idx = random.randint(0, len(cameras)-1) camera = cameras[camera_idx] image_gt = images[camera_idx] # 前向传播:可微光栅化 image_rendered = rasterize(gaussians, camera) # 损失计算 loss = L1_loss(image_rendered, image_gt) + \\ lambda_ssim * SSIM_loss(image_rendered, image_gt) # 反向传播 loss.backward() # 参数更新 optimizer.step() optimizer.zero_grad() # 密度控制(每100次迭代) if iteration % 100 == 0 and iteration < 15000: densify_and_prune(gaussians) # 定期保存 if iteration % 1000 == 0: save_checkpoint(gaussians, iteration)\n渲染算法迭代流程的第一步:前向传播\n前向渲染流程输入:高斯点云 + 相机参数 ↓┌─────────────────────────────────────────────┐│ 1. 视锥剔除 ││ - 移除视锥外的点 ││ - 减少计算量 │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 2. 投影变换 ││ - 3D高斯 → 2D高斯 ││ - 计算2D协方差矩阵 │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 3. 深度排序 ││ - 按深度值排序 ││ - 准备从前到后渲染 │└─────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────┐│ 4. 光栅化 ││ - 对每个像素: ││ * 找到影响该像素的高斯点 ││ * 计算2D高斯值 ││ * Alpha混合 │└─────────────────────────────────────────────┘ ↓ 输出图像\n视锥剔除\n对于某个相机,把所有的点进行视图变换,判断点是否在视锥里,如果在视锥里,把它放进列表。\n\n投影变换\n使用投影矩阵对视锥之内的点进行变换。计算出2D协方差矩阵。\n\n深度排序\n每个高斯有一个视图空间深度 (View Space Depth),其指在图形学渲染管线中,从相机视角出发,一个3D点到相机平面的垂直距离。将高斯按深度重新排列。\n\n光栅化\n找到影响该像素的高斯点和计算2D高斯值:\n屏幕分块:将整个屏幕或渲染目标划分为固定大小的瓦片(例如 16×16 像素)。\n分配高斯点到瓦片:遍历所有高斯点,通过其2D协方差矩阵计算其影响的大致矩形范围,根据计算出的2D边界框,判断它会影响哪些瓦片,并将其索引添加到这些瓦片的关联列表中。\n这一步适合使用GPU并行计算。\n数据结构:最终得到一个数组或列表 tile_gaussian_lists[tile_id],其中存储了所有可能影响该瓦片的高斯点索引。\n当需要查找影响像素 (x, y)的高斯点时:从 tile_gaussian_lists[tile_id]中取出所有可能影响该瓦片的高斯点索引列表。对于列表中的每个高斯点,进行精确的2D高斯权重计算。\n\n\nAlpha混合:\n根据球谐函数计算出高斯点的颜色\n对光线上的粒子颜色进行求和:\n是光线碰到粒子(光线被粒子阻碍)的概率密度,\\delta$是取样距离\n得到每个像素的颜色\n\n\n\n机器学习损失计算迭代流程第二步\n\nL1 损失(L1 Loss),在机器学习中也称为平均绝对误差,\nD-SSIM Loss(Structural Dissimilarity Loss)是一种基于结构相似性的图像质量评估指标\n损失函数\n\n反向传播迭代优化第三步\n\nStochastic Gradient Descent (SGD),中文常译为随机梯度下降,是机器学习中最基础、最核心的优化算法之一。它的核心思想是利用随机抽取的单个样本(或小批量样本)的梯度来近似整个数据集的梯度,从而进行参数更新。\n\n密度控制\n克隆:\n触发条件:当某个高斯点的梯度(Gradient) 很大,且其位置(Position) 的梯度远大于其形状(Scale) 的梯度时。\n逻辑:梯度大意味着这个点对当前视图的贡献很大,但位置梯度大说明它“站错了地方”。此时,系统会复制一个完全相同的点,并让它们分别向不同的方向微调,从而更精确地覆盖该区域。\n\n\n分裂:\n触发条件:当某个高斯点的形状(Scale) 变得过大时。\n逻辑:一个点变得太大,说明它试图覆盖一个过大的区域,导致细节模糊。此时,系统会将它分裂成两个更小的点,分别继承原点的部分属性,从而提升局部的分辨率。\n\n\n剪枝:\n触发条件:不透明度过低,空间尺度过小 \n逻辑:视觉贡献可以忽略\n\n\n\n操作环境配置\n在AutoDL上租赁一台RTX3090的服务器,选择Miniconda - python 3.10的配置\n在VSCode里安装Remote SSH,使用Ctrl + Shift + P 输入Remote SSH: Add,添加新的SSH主机,输入登录命令\n再Ctrl+Shift+P连接,输入密码\n在终端里配置Git的用户名和密码,生成密钥,添加到Github上\n克隆仓库git clone git@github.com:graphdeco-inria/gaussian-splatting.git --recursive,这一步可能因网络问题失败,可以换国内镜像源\n输入nvcc --version检测已经安装CUDA SDK 11\n输入cd gaussian-splatting然后执行conda env create --file environment.yml\n这一步可能因为网络问题而失败。\n用colmap -h检测有没有COLMAP,如果没有,安装COLMAP,sudo apt update和sudo apt install colmap(这一步可能有版本不兼容的问题,暂时没有想到解决方法)\n首次使用需要conda init bash,再conda activate gaussian_splatting\n\n训练\n用FileZilla连接服务器(sftp)\n在gaussian_splatting下创建存放训练数据的文件夹,在文件夹里创建一个项目的文件夹,然后创建input文件夹\n把拍的照片用FileZilla放到input里\n返回gaussian_splatting,执行python convert.py -s <location>,location是input的父文件夹,这一步完成了COLMAP初始化稀疏点云\n遇到qt.qpa.xcb: could not connect to display问题我也不知道该怎么解决了,于是直接社区镜像\n执行python train.py -s <location>,这一步完成了训练\n在output文件夹下可以找到point_cloud.ply,一个是7000次训练,一个是30000次训练。\n使用supersplat - 导入ply文件查看,如果遇到权限不足的情况,在wsl里执行chmod 777 文件名,拖进去就行\n\n","categories":["learning"],"tags":["科研","3DGS"]},{"title":"大一上学期总结","url":"/2026/02/25/%E5%A4%A7%E4%B8%80%E4%B8%8A%E5%AD%A6%E6%9C%9F%E6%80%BB%E7%BB%93/","content":"高考过去之后,回望高中三年,当时的无比焦虑,现在看来却微不足道。择校,与挑选数码产品是一样的,都是在自己的预算下选择性价比最高的选项。\n查分的时候,不得不承认还是有点失望的,毕竟数学考得不好。纠结了很长时间,最终还是把中山大学排在了北邮的前面,我就这样一分都没有浪费地来到了遥远的广州。\n“试问岭南应不好,却道:此心安处是吾乡。”\n迷茫从小到大,我都被管得很紧,没有自己出过远门。在和父母分别的时候,我忍不住流眼泪了。我不知道自己该如何生活,也担心会把事情搞砸。曾经看过那么多批评原生家庭和断亲的文章,对于一个缺乏安全感又敏感的人来说,也是徒劳的。\n高中的时候,我很少给家里人打电话,在大学里,我也不知道什么时候给家里人打电话,打了电话也不知道该说些什么。亲密关系这一块,我没大有自己的理解。有人能自然地开启话题,招待客人和结交朋友,虽然高中时大家都喜欢我,但大多数都是别人主动认识我。到了大学,生活更加原子化,维持关系也更难,很多人只是打个招呼,便再无话说,我渴望拥有更多朋友,和外向的人一起玩,但是一直找不到方法,不知道聊什么,也没有足够的勇气。\n我也没能理解谈恋爱这件事,虽然我的生物本能对异性产生好感,但是我不知道怎么做。大学之前虽然被别人表白过,但是从未真正进入一段感情。难道谈恋爱就是聊天、看电影、吃饭或者其他无聊的事情吗?我挺认同在网上看到的一句话:即使什么都不做,也想待在一起,这样去见对方的行为才叫约会;在“见面时要做什么”成为问题的那瞬间,其实我们的约会就已经结束了。类似地,我觉得送礼物也是如此,我看到一样东西,自然就会想送给你,不会在乎是否是节日或者纪念日,如果我绞尽脑汁地想给你送什么礼物才能让你开心,那就成为一种刻意的讨好了。不过,如果是你主动提出想要的东西,那也是挺好的。\n我本来以为中山大学在广东省的录取人数多,在广东上中山大学就会很容易,没想到这里大佬云集。有人初中就研究Linux,有人大一就打到ACM金牌,差距这么大我只能无奈地接受。我一直在思考未来的方向,每一个B站课程都浅尝辄止,焦虑研究方向感不感兴趣,焦虑未来的就业情况,但是未来谁也说不清楚。就像这句话:过度考察的人生是没法过的人生。当我们过度关注每一个细节,反而可能失去了生活的乐趣和方向。这种学习方式,还不如深耕一个领域,在分界点的时候再去思考怎么办,总不可能饿死吧。\n途中大一上学期,我渐渐加快写作业速度,几乎每天都去教室里自习。最后绩点4.031,至少是有些成果的。而且认识了一些朋友,虽然不多,但是起码让我感到不那么孤独了。空闲时间自己一个人溜达,我喜欢热闹的氛围却不知道怎么融入其中,表面的繁华最终也是无意义。想去找高中同学却没有人带我入校,社团和团委的成员也没有形成很熟的关系。\n不过把自己照顾得还不错吧,没有吃什么大餐,但是有吃水果,没有激动人心的经历,但是把所有东西都清理得很干净,睡得还行,也没有生什么大病。下学期去实验室实习,空闲时间起码有事做了。\n祝你我前程似锦,未来可期。\n","categories":["thinking"],"tags":["总结"]},{"title":"舍友打呼噜怎么办?","url":"/2026/03/14/%E8%88%8D%E5%8F%8B%E6%89%93%E5%91%BC%E5%99%9C%E6%80%8E%E4%B9%88%E5%8A%9E%EF%BC%9F/","content":"从高中住校开始,我就一直遇到打呼噜的舍友,有可能是因为我睡眠太浅,都对我的影响很大。刚上高中的时候,因为紧张,每天晚上都睡不着。后来干脆走读了,等到分班后换了一个宿舍,舍友还是打呼噜,我买了一个3M耳塞,戴半小时都涨得耳朵疼,而且还没什么用。因为我怕耳塞会拿不出来,还专门买了个带线的。因为走读,只有中午在宿舍睡觉,结果午睡的时候也睡不着,而且常常听到自己的心跳声,后来才知道是因为神经衰弱。到了大学,我还是有打呼噜的舍友,但是睡眠质量明显好多了,因为压力比高中小了很多,但是还是会被呼噜声吵醒,上个学期的时候,我们还会专门下床去叫醒他。这个学期已经懒得叫了,我本来就困,下床再回来给我折腾得都毫无困意了,所以我宁愿不下床。但是每天晚上被吵醒很多次,每次等个二三十分钟才入睡,也不是个办法啊。所以我去网上物色商品,最近买了一个安耳悠的耳塞,确实管用,而且我发现我耳道窄,适合用那个小号的,怪不得高中的时候涨得耳朵疼呢。虽然还是会被吵醒几次,但是睡眠质量有明显的改善,深圳卫健委发的一篇公众号说长期和打呼噜的人一起睡觉还会增加心血管疾病的风险,但是这确实是很多人都面临的一个困境。另一个舍友竟然在被吵醒之后起床学习,每次被吵醒之后看见他在开着灯打字我只能说神了,直接不睡和间歇地睡我还是知道哪个更严重的。不过我有耳塞了真的太开心了,没有给安耳悠打广告的意思,我只是说我很开心嘿嘿。许愿我未来的npy不打呼噜吧。\n","categories":["thinking"]},{"title":"3DGS复现","url":"/2026/04/03/3DGS%E5%A4%8D%E7%8E%B0/","content":"连接云服务器\n在AutoDL里租一台RTX 3090的服务器,选择基础环境:MiniConda Conda3 Python3.10 Ubuntu22.04 CUDA11.8\n在VS Code里按Ctrl + Shift + P,输入Remote-SSH,选择Add…,添加远程主机。\n在AutoDL里复制登录命令和密码,在VS Code里相应地输入\n\n连接SSH\n检查是否安装Git:git --version\n生成密钥:ssh-keygen -t ed25519 -C "email@example.com"\n复制密钥:cat ~/.ssh/id_ed25519.pub\n在Github上的SSH里添加New SSH Key\n\n配置环境\n克隆:git clone git@github.com:graphdeco-inria/gaussian-splatting.git --recursive\n检查是否安装CUDA:nvcc --version\n切换目录:cd gaussian-splatting\n换清华源:\n\n# 1. 添加清华源的 channels (对应 defaults, conda-forge, pytorch)conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/# 2. 设置显示源地址(方便调试)conda config --set show_channel_urls yes# 3. 清理旧缓存(非常重要,否则可能还在用旧的慢速索引)conda clean -i\n\n用mamba:\n\nconda install -n base -c conda-forge mambamamba env create -f environment.yml\n转换\n安装COLMAP:sudo apt update && sudo apt install colmap\n检测COLMAP:colmap -h\n连接FileZilla:协议SFTP,主机即SSH命令,输入对应的密码\n在gaussian_splatting下创建存放训练数据的文件夹,在文件夹里创建一个项目的文件夹,然后创建input文件夹。把拍的照片用FileZilla放到input里。\n虚拟显示:sudo apt update && sudo apt install xvfb -y\n转换:xvfb-run -a python convert.py -s ./data/truck/\n\n训练\n训练:python train.py -s ./data/train\n在output文件夹里得到结果\n\n","categories":["learning"],"tags":["科研","3DGS"]},{"title":"3DGS代码解读","url":"/2026/04/04/3DGS%E4%BB%A3%E7%A0%81%E8%A7%A3%E8%AF%BB/","content":"训练命令行参数:ArgumentParserArgumentParser 是 Python 标准库 argparse 模块中的核心类,用于轻松编写用户友好的命令行接口。它的主要功能是解析命令行参数,使你的 Python 脚本能够接收外部输入,从而实现灵活配置。\n核心工作流程使用 ArgumentParser 通常遵循以下三个步骤:\n\n创建解析器 (Create the parser)首先,你需要创建一个 ArgumentParser 对象。这个对象将保存所有解析命令行参数所需的信息。\nimport argparse# 创建一个解析器实例,并添加描述信息parser = argparse.ArgumentParser(description='这是一个用于处理文件的工具')\n\n添加参数 (Add arguments)接下来,通过调用 add_argument() 方法来告诉解析器,你的程序需要接收哪些命令行参数。你可以定义参数的名称、类型、默认值、帮助信息等。\n\n位置参数 (Positional Arguments): 必须提供的参数,根据它们在命令行中的位置来确定。\n可选参数 (Optional Arguments): 以 - 或 -- 开头的参数,通常有默认值,是可选的。\n\n# 添加一个位置参数 'filename'parser.add_argument('filename', help='要处理的文件名')# 添加一个可选参数 '--verbose',它是一个开关,如果提供则为 Trueparser.add_argument('-v', '--verbose', action='store_true', help='启用详细输出模式')# 添加一个带类型的可选参数 '--count',默认为 1parser.add_argument('--count', type=int, default=1, help='处理次数')\n\ntype指定该参数应该被转换成的数据类型。\n\n默认:字符串 (str)。\n常用:int, float, bool(注意:bool类型通常配合 action 使用,直接设 type=bool 可能会有坑)。\n作用:如果用户输入了错误类型(比如输入了字母但要求是数字),程序会自动报错并提示。\n\ndefault如果命令行中没有提供该参数,变量将取这个值。\n\n场景:比如 --ip 参数,默认是 127.0.0.1,如果你不特意去改它,程序就用本地地址。\n\nhelp一个描述性的字符串,解释这个参数是做什么的。\n\n作用:当用户在命令行输入 python script.py -h 或 --help 时,这段文字会显示出来,帮助用户了解如何使用程序。\n\naction指定当参数在命令行中出现时,应该采取什么动作。最常用的有两个:\n\nstore(默认):保存参数的值。例如 --count 5,就把 5 存起来。\nstore_true / store_false:这是一个开关。\n如果你定义 parser.add_argument('--debug', action='store_true')。\n默认情况下,debug 是 False。\n如果你在命令行里写了 --debug,它的值就变成了 True。这非常适合用来控制是否开启某种模式(如调试模式、静默模式)。\n\nnargs指定应该读取多少个命令行参数。\n\n?:0个或1个。\n*:所有剩余的参数放入一个列表。\n+:至少一个,剩余的放入一个列表。\n数字:例如 2,表示需要紧跟两个数值(常用于坐标 x, y)。\n\n# 示例代码parser.add_argument( \"--detect_anomaly\", # 1. 参数名:在命令行输入 --detect_anomaly action=\"store_true\", # 2. 动作:这是一个开关。写了就是True,不写就是False default=False, # 3. 默认值:默认不开启 help=\"Set debug mode\" # 4. 帮助信息:解释它是干嘛的)\n\n运行效果:\n\n情况 A:你直接运行 python train.py\nargs.detect_anomaly 的值是 False。\n情况 B:你运行 python train.py --detect_anomaly\nargs.detect_anomaly 的值是 True。\n\n这就是 add_argument 的核心逻辑:定义规则,自动解析,赋值给变量。\n\n解析参数 (Parse the arguments)最后,调用 parse_args() 方法。它会读取命令行输入(例如 python script.py myfile.txt -v --count 3),进行解析,并返回一个包含所有参数值的对象。\n# 解析命令行参数args = parser.parse_args()# 通过属性访问解析出的参数print(f\"文件名: {args.filename}\")print(f\"详细模式: {args.verbose}\")print(f\"处理次数: {args.count}\")\n\n核心优势\n自动生成帮助信息: 当你运行脚本并加上 -h 或 --help 参数时,argparse 会自动生成一个格式美观的帮助文档,列出所有可用参数及其说明。\n类型检查与转换: 它可以根据你设置的 type 参数自动将命令行输入的字符串转换为整数、浮点数等,并在类型不匹配时给出清晰的错误提示。\n处理默认值: 为可选参数设置默认值,简化了用户的使用。\n错误处理: 当用户提供了无效的参数或缺少必需的参数时,它会自动退出程序并显示错误信息。\n\n总而言之,ArgumentParser 是构建专业、易用的命令行工具的标准方式,它极大地简化了参数处理逻辑,并提升了用户体验。\nforward预处理:preprocess视锥剔除:in_frustumfloat4 p_hom = transformPoint4x4(p_orig, projmatrix);float p_w = 1.0f / (p_hom.w + 0.0000001f);float3 p_proj = { p_hom.x * p_w, p_hom.y * p_w, p_hom.z * p_w };p_view = transformPoint4x3(p_orig, viewmatrix);\n把点进行视图变换,转换到屏幕空间。\nif (p_view.z <= 0.2f){ if (prefiltered) { printf(\"Point is filtered although prefiltered is set. This shouldn't happen!\"); __trap(); } return false;}return true;\n如果点的深度,说明在相机后面,retrurn false表示不在视锥内。如果 prefiltered 为 true,意味着上层代码已经宣称“这个点是可见的”,现在的 GPU 代码算出来这个点其实是在相机后面的,说明出错了,__trap()强制中断。\nif (!in_frustum(idx, orig_points, viewmatrix, projmatrix, prefiltered, p_view))\treturn;\n如果不在视锥内,则不处理这个点。\n投影float3 p_orig = { orig_points[3 * idx], orig_points[3 * idx + 1], orig_points[3 * idx + 2] };float4 p_hom = transformPoint4x4(p_orig, projmatrix);float p_w = 1.0f / (p_hom.w + 0.0000001f);float3 p_proj = { p_hom.x * p_w, p_hom.y * p_w, p_hom.z * p_w };\n在orig_points里取出这个点,再用投影矩阵进行变换,最后归一化。\n计算3D协方差矩阵:computeCov3Dglm::mat3 S = glm::mat3(1.0f);S[0][0] = mod * scale.x;S[1][1] = mod * scale.y;S[2][2] = mod * scale.z;\n\n创建一个对角矩阵 。其中 是 mod(全局缩放系数), 是各轴向的缩放。\nglm::mat3 R = glm::mat3( 1.f - 2.f * (y * y + z * z), 2.f * (x * y - r * z), 2.f * (x * z + r * y), 2.f * (x * y + r * z), 1.f - 2.f * (x * x + z * z), 2.f * (y * z - r * x), 2.f * (x * z - r * y), 2.f * (y * z + r * x), 1.f - 2.f * (x * x + y * y));\n\n将四元数转换为 3x3 旋转矩阵。(代码中 r 对应 )\nglm::mat3 M = S * R;glm::mat3 Sigma = glm::transpose(M) * M;\n\ncov3D[0] = Sigma[0][0]; // xxcov3D[1] = Sigma[0][1]; // xycov3D[2] = Sigma[0][2]; // xzcov3D[3] = Sigma[1][1]; // yycov3D[4] = Sigma[1][2]; // yzcov3D[5] = Sigma[2][2]; // zz\n协方差矩阵 是一个对称矩阵(即 )。为了节省显存和带宽,代码只存储了矩阵的上三角部分\n计算2D协方差矩阵:computeCov2Dfloat3 t = transformPoint4x3(mean, viewmatrix);const float limx = 1.3f * tan_fovx;const float limy = 1.3f * tan_fovy;const float txtz = t.x / t.z;const float tytz = t.y / t.z;t.x = min(limx, max(-limx, txtz)) * t.z;t.y = min(limy, max(-limy, tytz)) * t.z;``` 将高斯球的中心点 `mean` 从世界坐标系变换到相机坐标系 `t` 。计算视野边界`limx`,`limy`,乘以 `1.3f` 是为了留出额外的缓冲区域,计算点在图像平面上的投影位置`txtz`,`tytz`,确保数值不小于左边界(`-limx`),不大于右边界(`limx`),保证点在视锥体内。```cppglm::mat3 J = glm::mat3( focal_x / t.z, 0.0f, -(focal_x * t.x) / (t.z * t.z), 0.0f, focal_y / t.z, -(focal_y * t.y) / (t.z * t.z), 0, 0, 0);\n\n计算雅可比矩阵,透视除法后2D协方差矩阵不需要深度信息,第三行置0\nglm::mat3 W = glm::mat3( viewmatrix[0], viewmatrix[4], viewmatrix[8], viewmatrix[1], viewmatrix[5], viewmatrix[9], viewmatrix[2], viewmatrix[6], viewmatrix[10]);\n计算视图变换矩阵,这里只提取了视图矩阵的 3x3 旋转部分(去掉了平移,因为协方差只与形状和方向有关,与位置无关)\nglm::mat3 T = W * J;glm::mat3 Vrk = glm::mat3( cov3D[0], cov3D[1], cov3D[2], cov3D[1], cov3D[3], cov3D[4], cov3D[2], cov3D[4], cov3D[5]);glm::mat3 cov = glm::transpose(T) * glm::transpose(Vrk) * T;return { float(cov[0][0]), float(cov[0][1]), float(cov[1][1]) };\n用Vrk重构 3D 协方差矩阵,运用协方差变换公式,最后由于协方差矩阵的对称性,只返回3个值。\nfloat3 cov = computeCov2D(p_orig, focal_x, focal_y, tan_fovx, tan_fovy, cov3D, viewmatrix);\n计算渲染边界float mid = 0.5f * (cov.x + cov.z);float lambda1 = mid + sqrt(max(0.1f, mid * mid - det));float lambda2 = mid - sqrt(max(0.1f, mid * mid - det));\n利用 2x2 矩阵特征值的解析解公式,对于一个 2x2 对称矩阵,其特征值 满足:较大的特征值对应椭圆长轴长度的平方,较小的特征值对应椭圆短轴长度的平方。\nfloat my_radius = ceil(3.f * sqrt(max(lambda1, lambda2)));\nsqrt(lambda1)取出长轴的标准差,根据3-Sigma原则,高斯分布的 99.7% 的能量集中在均值周围 3 倍标准差的范围内。ceil(...)向上取整,确保覆盖所有可能的像素。把高斯椭圆近似看成圆,my_radius 是这个圆屏幕上占据的最大像素半径。\nfloat2 point_image = { ndc2Pix(p_proj.x, W), ndc2Pix(p_proj.y, H) };getRect(point_image, my_radius, rect_min, rect_max, grid);\n其中\nrect_min = { min(grid.x, max((int)0, (int)((p.x - max_radius) / BLOCK_X))), min(grid.y, max((int)0, (int)((p.y - max_radius) / BLOCK_Y)))};rect_max = { min(grid.x, max((int)0, (int)((p.x + max_radius + BLOCK_X - 1) / BLOCK_X))), min(grid.y, max((int)0, (int)((p.y + max_radius + BLOCK_Y - 1) / BLOCK_Y)))};\nndc2Pix将高斯中心点从NDC转换为屏幕像素坐标。根据中心点 point_image 和半径 my_radius,/ BLOCK_X将像素坐标转换为线程块坐标,计算覆盖范围涉及到的Tile,用rect_min和rect_max(左上角和右下角)定义了一个受影响的线程块矩形区域,后续计算只在这些Tile上进行。\nif ((rect_max.x - rect_min.x) * (rect_max.y - rect_min.y) == 0) return;\n如果计算出的矩形面积为 0(例如高斯球太小,或者完全在屏幕外),则直接终止函数,节省 GPU 算力。\n计算颜色:computeColorFromSHglm::vec3 pos = means[idx];glm::vec3 dir = pos - campos;dir = dir / glm::length(dir);\n计算从相机位置 campos 到高斯球中心 pos 的向量,并将其归一化。这代表了当前的观察角度。\nglm::vec3* sh = ((glm::vec3*)shs) + idx * max_coeffs;\n获取系数指针\nresult = SH_C0 * sh[0];result = result - SH_C1 * y * sh[1] + SH_C1 * z * sh[2] - SH_C1 * x * sh[3];...\n\n\nSH_C0, SH_C1, SH_C2 等是球谐函数的常数系数(如)。x,y,xx,xy等是与观察角度有关的函数(如),它们共同构成了基函数,sh存储了三维向量rgb的系数(即)。\n排序生成键值:duplicateWithKeys// 1. 获取当前线程处理的高斯球IDauto idx = cg::this_grid().thread_rank();if (idx >= P) return; // 如果ID超过总数,退出// 2. 可见性剔除:如果半径为0,说明这个球看不见,不参与排序if (radii[idx] > 0){ // 3. 计算写入位置的偏移量 // offsets 数组记录了前面所有高斯球生成了多少个“分身” uint32_t off = (idx == 0) ? 0 : offsets[idx - 1]; // 4. 计算这个高斯球覆盖了屏幕上的哪些 Tiles,存在rect_min和rect_max里 uint2 rect_min, rect_max; getRect(points_xy[idx], radii[idx], rect_min, rect_max, grid); // 5. 遍历这个矩形内的每一个 Tile for (int y = rect_min.y; y < rect_max.y; y++) { for (int x = rect_min.x; x < rect_max.x; x++) { // 计算 Tile ID ,第 y 行第 x 列的 Tile ID = y * 总列数 + x uint64_t key = y * grid.x + x; // 将 Tile ID 左移 32 位,这样 Tile ID 占据 64位整数的高32位 key <<= 32; // 将深度值强制转换为整数,按位或,填入低32位 // 这里直接操作内存,把 float 变成了 uint32 key |= *((uint32_t*)&depths[idx]); // 把拼好的大数字(key)和高斯球ID(value)存起来 gaussian_keys_unsorted[off] = key; gaussian_values_unsorted[off] = idx; off++; } }}\n\n为什么这样排序有效?因为在计算机排序中,数字是按位从高到低比较的:\n\n先比高位:如果两个 Key 的高 32 位(Tile ID)不同,那么数值大的肯定属于不同的 Tile。这保证了同一个 Tile 的数据排在一起。\n再比低位:如果高 32 位相同(同一个 Tile),计算机就会去比低 32 位(Depth)。这保证了深度顺序(从前到后或从后到前)。\n\n排序:SortPairscub::DeviceRadixSort::SortPairs( binningState.list_sorting_space, // 临时工作区(刚才算出来的那块地) binningState.sorting_size, // 工作区大小 binningState.point_list_keys_unsorted, // 输入:乱序的键值 (TileID + Depth) binningState.point_list_keys, // 输出:排好序的键值 binningState.point_list_unsorted, // 输入:乱序的高斯球ID binningState.point_list, // 输出:排好序的高斯球ID num_rendered, // 要排序的元素总数 0, // 排序的起始位(从第0位开始) 32 + bit // 排序的结束位(排多少位));\n这里调用了 NVIDIA CUB 库\n对于 64 位的键值 ,基数排序算法会把它看作二进制串:\n排序过程(从低位到高位):\n\nPass 1:看 ,分两桶(0和1)。\nPass 2:看 ,分两桶(保持上一轮顺序)。\n…\nPass 64:看 (最高位,即 TileID 的最高位)。\n\n0, 32 + bit 含义:处理从第 0 位到第 32+bit 位的所有二进制位\n如果 bit 是 32,那就是处理全部 64 位。这就保证了先比深度(低32位),再比 TileID(高32位),最终 TileID 占主导地位。\n划分边界:identifyTileRanges这段代码是 3D Gaussian Splatting 渲染管线中 Tile-Based Radix Sort 的第三阶段(范围识别)。\n// 1. 获取当前线程处理的位置索引auto idx = cg::this_grid().thread_rank();if (idx >= L) return; // 如果索引超过总数,退出// 2. 读取当前元素的 Key,并提取 Tile IDuint64_t key = point_list_keys[idx];uint32_t currtile = key >> 32; // 取高32位,得到当前属于哪个 Tile// 3. 处理列表开头if (idx == 0) ranges[currtile].x = 0;// 4. 处理中间部分else{ uint32_t prevtile = point_list_keys[idx - 1] >> 32; // 读前一个元素的 Tile ID if (currtile != prevtile) // 如果当前 Tile 和前一个不一样,说明到了“交界处” { ranges[prevtile].y = idx; // 前一个 Tile 在这里结束 ranges[currtile].x = idx; // 当前 Tile 从这里开始 }}// 5. 处理列表结尾if (idx == L - 1) ranges[currtile].y = L;\n\nranges[tile_id].x代表该 Tile 的数据在排序列表中的起始索引。ranges[tile_id].y代表该 Tile 的数据在排序列表中的结束索引。\n如果是列表的第一个元素(idx == 0),那么当前这个 Tile 的起始位置(.x)就是 0。如果不是第一个元素,通过比较当前元素和前一个元素的 TileID,来判断是否到了边界。如果是列表的最后一个元素(idx == L - 1),那么当前这个 Tile 的结束位置(.y)就是列表的总长度 L。\n颜色混合:renderCUDAblock = tile\nthread = pixel\nfloat T = 1.0f;uint32_t contributor = 0;uint32_t last_contributor = 0;float C[CHANNELS] = { 0 };\n\nfloat T = 1.0f;\n\n含义:透射率。表示光线穿过当前已累积的所有高斯球后,还剩下多少能量。\n初始值:(即 100%)。在开始绘制前,假设没有任何物体遮挡,光线完全透过。\n\nuint32_t contributor = 0;\n\n含义:当前贡献者计数器。它记录当前正在处理列表中的第几个高斯球。\n\nuint32_t last_contributor = 0;\n\n含义:最后有效贡献者索引。记录最后一个显著改变了该像素颜色的高斯球在列表中的位置。\n用途:这个变量主要用于反向传播(训练阶段)。在计算梯度时,我们需要知道哪些高斯球参与了该像素的成像。如果 衰减到接近 0,后面的高斯球就不再处理了,last_contributor 就标记了这个截止点。\n\nfloat C[CHANNELS] = { 0 };\n\n含义:累积颜色。\n作用:存储当前像素计算出的最终 RGB 颜色值。\n\nint num_done = __syncthreads_count(done);if (num_done == BLOCK_SIZE) break;\ndone 变量在之前的循环中被置为 true,意味着该像素的透射率 已经趋近于 0,后续的高斯球对它不再有贡献。如果 num_done == BLOCK_SIZE,说明当前线程块内的所有线程(即该 Tile 内的所有像素)都已经完成了渲染(光线被完全遮挡)。\nint progress = i * BLOCK_SIZE + block.thread_rank();\nprogress:计算当前线程负责加载的高斯球在全局列表中的相对进度。\nif (range.x + progress < range.y)\n确保我们要访问的高斯球索引没有超出该线程块分配的总范围(range)。为了防止处理屏幕边缘或列表末尾时的越界访问。\nint coll_id = point_list[range.x + progress];collected_id[block.thread_rank()] = coll_id;collected_xy[block.thread_rank()] = points_xy_image[coll_id];collected_conic_opacity[block.thread_rank()] = conic_opacity[coll_id];\n\n从排序好的 point_list 中取出高斯球的全局 ID (coll_id)。根据 ID,从全局显存中读取该高斯球的属性(xy 坐标、conic_opacity 参数)。\nfloat2 xy = collected_xy[j];float2 d = { xy.x - pixf.x, xy.y - pixf.y };float4 con_o = collected_conic_opacity[j];float power = -0.5f * (con_o.x * d.x * d.x + con_o.z * d.y * d.y) - con_o.y * d.x * d.y;if (power > 0.0f) continue;\n\ncontributor:计数器加 1,记录当前处理到了第几个高斯球。\n数学原理:这是高斯函数的指数部分,是像素点到高斯点的距离 其中 con_o 的 .x, .y, .z 存储了协方差逆矩阵的参数。\n如果 power > 0,说明距离太远或者概率密度极低,该高斯球对此像素没有贡献,直接跳过。\n\nfloat alpha = min(0.99f, con_o.w * exp(power));if (alpha < 1.0f / 255.0f) continue;float test_T = T * (1 - alpha);if (test_T < 0.0001f){ done = true; continue;}\n\n计算当前像素位置的高斯值,并乘以高斯球本身的不透明度(con_o.w)。 \n\nmin(0.99f, ...):强制限制 最大为 0.99。这是为了防止数值溢出或完全遮挡导致的梯度消失问题(如果 ,则 ,后续梯度无法回传)。\n\n如果计算出的 太小(小于 1/255),说明不透明度太低了,肉眼几乎不可见,直接跳过以节省算力。\n\n计算如果加上当前这个高斯球,剩余的光线透射率 会变成多少。 如果 小于 0.0001,说明光线已经被阻挡了 99.99% 以上,后面的高斯球无论是什么颜色都看不见了。done = true:标记该像素渲染完成,后续循环将不再处理该像素。\n\n\nfor (int ch = 0; ch < CHANNELS; ch++) C[ch] += features[collected_id[j] * CHANNELS + ch] * alpha * T;\n\n用Alpha Blending 公式累计颜色。当前高斯球贡献的颜色 = 球的颜色 球的不透明度 之前所有球的透射率。features存储了高斯球的 RGB 颜色值。 \n\nT = test_T;last_contributor = contributor;\n\n更新透射率,供下一个高斯球使用。记录最后一个对该像素有贡献的高斯球索引。\n\nbackward我们有每个像素渲染出的颜色和真实的颜色,根据这些计算出loss,从而计算出\n我们需要更新的是:高斯中心位置,高斯形状,高斯的颜色,高斯的不透明度。因此需要计算出loss对每个参数的梯度\n预处理:preprocessCUDA已知损失函数对2D屏幕空间高斯均值的梯度(代码中 dL_dmean2D),通过链式法则反向传播,计算损失对3D高斯以下参数的梯度:\n\n3D均值(dL_dmeans)\n球谐系数(dL_dsh)\n缩放(dL_dscale)\n旋转(dL_drot)\n\n投影路径的反向传播3D高斯的参数通过多条路径影响最终损失,总梯度是所有路径梯度的累加:$$\\frac{\\partial L}{\\partial \\text{means}} = \\underbrace{\\frac{\\partial L}{\\partial \\text{means}}}{\\text{投影路径}} + \\underbrace{\\frac{\\partial L}{\\partial \\text{means}}}{\\text{颜色路径}} + \\underbrace{\\frac{\\partial L}{\\partial \\text{means}}}_{\\text{形状路径}}$$\nauto idx = cg::this_grid().thread_rank();if (idx >= P || !(radii[idx] > 0)) return;\n为每个3D高斯分配一个线程,仅处理radii[idx] > 0,即屏幕上渲染半径大于0的高斯。不可见高斯对损失无贡献。\n回顾正向投影过程:\n展开为分量形式:再通过透视除法得到2D归一化坐标:\n具体计算过程:\nfloat3 m = means[idx];float4 m_hom = transformPoint4x4(m, proj);float m_w = 1.0f / (m_hom.w + 0.0000001f);glm::vec3 dL_dmean;float mul1 = (proj[0] * m.x + proj[4] * m.y + proj[8] * m.z + proj[12]) * m_w * m_w;float mul2 = (proj[1] * m.x + proj[5] * m.y + proj[9] * m.z + proj[13]) * m_w * m_w;\n\nm = means[idx]:取出当前3D高斯的均值。\nm_hom = transformPoint4x4(m, proj):正向计算齐次裁剪空间坐标(对应上述数学公式)。\nm_w = 1/(m_hom.w + eps):计算透视除法分母的倒数,加小常数 eps 防止除零。\nmul1 和 mul2:预计算和,用于简化后续商的导数计算。\n\ndL_dmean.x = (proj[0] * m_w - proj[3] * mul1) * dL_dmean2D[idx].x + (proj[1] * m_w - proj[3] * mul2) * dL_dmean2D[idx].y;dL_dmean.y = (proj[4] * m_w - proj[7] * mul1) * dL_dmean2D[idx].x + (proj[5] * m_w - proj[7] * mul2) * dL_dmean2D[idx].y;dL_dmean.z = (proj[8] * m_w - proj[11] * mul1) * dL_dmean2D[idx].x + (proj[9] * m_w - proj[11] * mul2) * dL_dmean2D[idx].y;\n\n 已知,需计算(y、z分量同理)。根据链式法则: 再根据商的导数法则(): 代入投影矩阵的偏导数(,),最终得到代码中的表达式。 '_' allowed only in math mode \\frac{\\partial \\text{mean2D}.x}{\\partial m.x} = \\frac{\\text{proj}[0] \\cdot P_{hom}.w - P_{hom}.x \\cdot \\text{proj}[3]}{(P_{hom}.w)^2} = \\frac{\\text{proj}[0]}{P_{hom}.w} - \\frac{\\text{proj}[3] \\cdot P_{hom}.x}{(P_{hom}.w)^2} = \\texttt{proj[0] * m_w - proj[3] * mul1} \ndL_dmeans[idx] += dL_dmean;\n将投影路径的梯度累加到3D均值的总梯度中。3D均值还会通过“颜色路径”和“形状路径”影响损失,总梯度需累加所有路径的贡献。\n颜色路径的反向传播:computeColorFromSH已知损失对RGB颜色的梯度 (代码中 dL_dcolor[idx]),也即,需通过链式法则计算两个目标梯度:\n\n损失对SH系数的梯度 (代码中 dL_dshs),\n损失对3D高斯位置的梯度 (代码中 dL_dmeans)。\n\n计算对SH系数的梯度:\nglm::vec3 pos = means[idx];glm::vec3 dir_orig = pos - campos;glm::vec3 dir = dir_orig / glm::length(dir_orig);\n计算视角方向 (dir_orig),并归一化得到单位方向 (dir)。\n由于RGB是SH系数的线性函数,因此 ( 是第 个SH基函数在 处的值)。根据链式法则:\n代码中按阶数 从0到3依次计算,对应各阶基函数的导数:\n阶数 时实球谐基函数:'_' allowed only in math modeY_0^0 = \\text{SH_C0},其中 '_' allowed only in math mode\\text{SH_C0} = \\frac{1}{\\sqrt{4\\pi}}(归一化常数)。\nfloat dRGBdsh0 = SH_C0;dL_dsh[0] = dRGBdsh0 * dL_dRGB;\n\n阶数 时实球谐基函数:'_' allowed only in math mode \\begin{cases} Y_1^{-1} = -\\text{SH_C1} \\cdot y \\ Y_1^{0} = \\text{SH_C1} \\cdot z \\ Y_1^{1} = -\\text{SH_C1} \\cdot x \\end{cases} 其中 '_' allowed only in math mode\\text{SH_C1} = \\sqrt{\\frac{3}{4\\pi}}(归一化常数), 是单位方向 的分量。\nfloat dRGBdsh1 = -SH_C1 * y;float dRGBdsh2 = SH_C1 * z;float dRGBdsh3 = -SH_C1 * x;dL_dsh[1] = dRGBdsh1 * dL_dRGB;dL_dsh[2] = dRGBdsh2 * dL_dRGB;dL_dsh[3] = dRGBdsh3 * dL_dRGB;\n\n阶数 时部分实球谐基函数'_' allowed only in math mode \\begin{cases} Y_2^{-2} = \\text{SH_C2}[0] \\cdot xy \\ Y_2^{-1} = \\text{SH_C2}[1] \\cdot yz \\ Y_2^{0} = \\text{SH_C2}[2] \\cdot (2z^2 - x^2 - y^2) \\ \\cdots \\end{cases} 代码逻辑与 类似,计算基函数值并乘以梯度 dL_dRGB 得到SH系数梯度。\n时基函数形式更复杂(如 ),代码逻辑同上。\n计算对均值的梯度:其中 是方向向量对位置的雅可比矩阵,代码中通过球谐系数与方向向量的乘积实现。\nglm::vec3 dRGBdx(0, 0, 0); // ∂RGB / ∂dir.xglm::vec3 dRGBdy(0, 0, 0); // ∂RGB / ∂dir.yglm::vec3 dRGBdz(0, 0, 0); // ∂RGB / ∂dir.z\n在 1 阶、2 阶、3 阶 SH 里不断 累加:\n// 1阶 SHdRGBdx = -SH_C1 * sh[3];dRGBdy = -SH_C1 * sh[1];dRGBdz = SH_C1 * sh[2];// 2阶 SHdRGBdx += ...;dRGBdy += ...;dRGBdz += ...;// 3阶 SHdRGBdx += ...;dRGBdy += ...;dRGBdz += ...;\n\n\nd是从相机指向高斯的单位向量:\n对 pos 求导:其中\n代码的巧妙之处在于,它将这两步合并了。\n在第一步中计算的那些包含 x, y, z 的导数表达式(如 -SH_C1 * sh[3]),实际上已经是 和 相乘后的结果。\n例如,对于一阶球谐函数,其基函数与方向分量成正比(如 )。当我们对它求关于位置 的导数时,链式法则告诉我们:\n而 是归一化方向向量的分量 。对它求关于 的导数,就会得到一个包含 的项。代码中那些看似复杂的表达式,正是这些导数经过化简后的形式。\n形状路径的反向传播(缩放+旋转→协方差)if (scales) computeCov3D(idx, scales[idx], scale_modifier, rotations[idx], dL_dcov3D, dL_dscale, dL_drot);\n\n数学背景:3D高斯的协方差矩阵由缩放和旋转合成。正向过程为:\n\n将四元数 rotations 转换为旋转矩阵。\n构建缩放对角矩阵(对角元素为 scales * scale_modifier)。\n计算3D协方差:。\n\n\n反向传播任务:已知(dL_dcov3D),通过链式法则计算:1.(dL_dscale):缩放参数的梯度。2.(dL_drot):旋转四元数的梯度。\n\n\n我们已知损失函数 对协方差矩阵 的梯度 (代码中为 dL_dSigma)。我们需要求 对中间矩阵 的梯度。\n根据矩阵求导法则,若 ,则:\n代码对应:\n// dSigma_dM = 2 * Mglm::mat3 dL_dM = 2.0f * M * dL_dSigma;\n\n反向传播需要用到前向传播时的中间结果,所以代码首先重新计算了旋转矩阵 和缩放矩阵 。\n我们需要求 。因为 ,根据链式法则:这本质上是一个矩阵乘法:。\nglm::mat3 Rt = glm::transpose(R);glm::mat3 dL_dMt = glm::transpose(dL_dM);// 点乘操作实际上是在做矩阵乘法的行提取dL_dscale->x = glm::dot(Rt[0], dL_dMt[0]); // 对应 S_xx 的梯度dL_dscale->y = glm::dot(Rt[1], dL_dMt[1]);dL_dscale->z = glm::dot(Rt[2], dL_dMt[2]);\n\n接下来我们需要求 。路径是:。\n首先,利用 ,我们可以得到 对 的梯度:代码中通过 dL_dMt 乘以 s 来实现这一步(利用了转置性质)。\n接下来是 旋转矩阵对四元数的导数 ()。因为 的每个元素都是 的二次函数(如 等),其导数是线性的。代码中那几行极其复杂的算式(如 2 * z * (dL_dMt[0][1] - dL_dMt[1][0])...)正是 的展开形式。\n最后构建出:\n*dL_drot = float4{ dL_dq.x, dL_dq.y, dL_dq.z, dL_dq.w };","categories":["learning"],"tags":["科研","3DGS"]},{"title":"程序设计II笔记","url":"/2026/04/13/%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1II%E7%AC%94%E8%AE%B0/","content":"现已迁移至程序设计II笔记\n","categories":["learning"],"tags":["C++"]},{"title":"AI浅谈","url":"/2026/04/26/AI%E6%B5%85%E8%B0%88/","content":"从ChatGPT爆火开始,人工智能成为了新的风口,发展速度之快令人咋舌。有人说,生产工具的发展会催生新的就业岗位,但目前来看,人工智能导致了互联网行业更加严重的裁员。在大学里,我听到了很多新的概念,但也是迷迷糊糊一知半解,在OpenClaw爆火的时候,我也懒得折腾。但是当我真的亲眼看到现在Agent的能力时,还是会为之惊叹,对于新技术,在头脑里揣测100次,也不如亲手体验1次,如果懒得自己操作,也一定要亲眼看看它的效果,尤其是在线下。\n现在各行各业都要AI赋能,虽然感觉更多是一群不懂技术的领导拍脑袋的决定,但是不得不说AI确实有这个能力,它确实能提高各个领域各个环节的效率。从K12教育到高等教育都在想方设法地往课堂里结合AI,我为什么选择了人工智能与大数据呢?因为我觉得未来人工智能将会成为通识,正如程序设计这门课一样。掌握了使用更高效的工具的方法,带来的好处不仅在于就业方面,更在于综合素质。\n作为计算机专业的学生,接触到名词很多,所以我想梳理一下AI的基础概念。这里参考了从 LLM 到 Agent Skill,一期视频带你打通底层逻辑!和近年AI应用技术串讲与优质文档分享|Agent、Skill、OpenClaw、Harness……,都是很高质量的视频,推荐。\nLLM:Large Language Model,大语言模型,简称大模型。这是AI浪潮的基础,也是大家最熟悉的一个概念,GPT,Gemini,DeepSeek都是大语言模型,我更愿意把它比作一个函数,它能够接受我们输入的文字,输出回答。它的工作方式是根据你输入的提示词,预测下一个最合理的字词,这句话其实也反映了它是数据驱动的,是经验性的,大语言模型的能力强,是人类在信息时代产生了大量可供学习的语料的结果。\ntoken:词元。现在人们经常说花了多少token,就像说一个货币单位一样,但是实际上它是大模型处理文本的最小单位。大模型实际上是一个数学函数,在用户输入问题后,先把文字切割成一个个token,再把token转换成数字,一句话就这样变成了一个向量,我们经常看到由几层小圆圈相互交织连接成的神经网络示意图,这看起来非常高深,但是实际上这些小圆圈就是线性代数里的矩阵运算,交织在一起的神经网络就是多进行了几层计算。经过计算之后,模型得到了一个数字,这就是一个token,再把这个token转换成文字,回答就完整了一点。\ncontext:上下文。从字面上来看,它指的是我们与模型对话的历史记录,但不够完整,实际上它还包括了模型当前正在输出的回答,工具列表和system prompt。而context window,上下文窗口指的就是模型能够记住的上下文规模。\nprompt:提示词。字面意思,就是你发给大模型的文字,告诉它做什么。上面说的system prompt就是在后台告诉大模型的一些规则,user prompt就是用户输入的提示词,当然这两个词不大常见。曾经很火的Prompt Engineering,提示词工程就是研究怎么写好提示词,让大模型的回答更符合要求。一段好的提示词应该包括角色、任务、背景信息、输出格式等等,比如“你是一位资深律师……”,但是用的比较少。\nFine-tuning:微调。在已有模型的训练基础上,用特定数据再训练,让模型更适合某个具体任务或场景。它修改模型本身的参数,需要花费很多的算力。\nRAG:Retrieval-Augmented Generation,检索增强生成。大模型本身知识有限,当问到“今天广州的天气怎么样”或者“公司今年的年假政策是什么”这样的问题时,它无法从训练过程中学过的知识找到答案,可能会编造答案。RAG就是让大模型在回答问题时,先去资料库里找出与问题相关的片段,把检索到的内容和用户问题一起拼成提示词,基于这些提示词再去生成回答。\ntool:工具。大语言模型明明只能处理文本,为什么还发展出如此强大的能力呢?因为它可以通过语言来调用工具,大模型本身不知道今天的新闻,它可以使用联网搜索tool,大模型不知道本地的文件,它可以调用文件操作tool,调用的时候,需要写一个含有tool名和参数的json文件,外部程序把结果返回给模型。\nMCP:Model Context Protocol,模型上下文协议。MCP出现以前,大模型调用工具方式不一样,不同工具被调用的方式也不一样,使用工具需要写很多套适配代码,MCP就像大模型的USB-C接口,工具按照MCP去制定被调用的方式,大模型按照MCP调用工具,提供工具的那一方就叫MCP Server。\nagent:智能体。普通的LLM无法感知或改变外部世界,只能处理文本,而agent是给LLM增加了感知、规划、记忆和工具调用的能力,让LLM充当大脑,使得AI能够自动化地执行一长串复杂任务。有一个词叫基座模型,也是LLM,虽然只有文本处理功能,但是我们微调之后,通过语言可以实现思考,也可以通过语言调用工具,所以LLM是基础。agent的定义没有那么明确,我有了一个基座模型,我可以微调让它会使用简单的工具,那它也可以叫做一个agent。不过,我们通常认为agent要实现思考 - 行动 - 观察的循环,去模拟人类完成一个任务的模式。\nskill:技能。把一整套agent能力(prompt、多个工具、参考资料、任务流程、规则等)封装为可复用模块,从而实现低门槛分享与复用,一个skill本质上约等于一个子agent。一个skill对应着一个文件夹,包含了所需的资料和工具脚本等。在接收到任务后,agent会把所有skill的名称和描述看一遍,选择适合的skill,再把这些skill文件夹里SKILL.md里的所有内容加载到上下文里,按需使用这个skill的工具或者资产。\n许多老师都说,没有什么知识是你学了就一定能找到工作的。但是对技术的热爱可以支撑我们走下去。\n","categories":["thinking"],"tags":["AI"]},{"title":"W4terCTF2026WP","url":"/2026/04/24/W4terCTF2026WP/","content":"MiscSignIn\n输入ls,发现flag,输入cat flag即可\n\nOSINTfind_me\n在图片上发现了Zeeland 和Uden,查询得知是在荷兰,同时在N277公路上。\n从Zeeland沿着N277公路向南10公里找,由于图片上还有Wi和Wa两个词,找到了一个岔路口,即Middenpeelweg和Noordstraat的交界处\n图片上有谷歌水印,在谷歌地图中找到这个岔路口,然后查看这个地方的街景,在网址里面可以看到它的经纬度(https://www.google.com/maps/@51.6329259,5.7476876,3a,75y,5.25h,95.9t/data=!3m7!1e1!3m5!1sIb87d0Yn2JUiNJ8uCYK1Cg!2e0!6shttps:%2F%2Fstreetviewpixels-pa.googleapis.com%2Fv1%2Fthumbnail%3Fcb_client%3Dmaps_sv.tactile%26w%3D900%26h%3D600%26pitch%3D-5.902840457698332%26panoid%3DIb87d0Yn2JUiNJ8uCYK1Cg%26yaw%3D5.246833661354003!7i16384!8i8192?entry=ttu&g_ep=EgoyMDI2MDQyMS4wIKXMDSoASAFQAw%3D%3D)\n\n","categories":["writeup"],"tags":["CTF"]},{"title":"在hexo博客的子页面部署mkdocs","url":"/2026/05/20/%E5%9C%A8hexo%E5%8D%9A%E5%AE%A2%E7%9A%84%E5%AD%90%E9%A1%B5%E9%9D%A2%E9%83%A8%E7%BD%B2mkdocs/","content":"使用Mkdocs完成内容的配置之后,接下来我们需要把写的文章的这些页面部署到我的博客的子页面上。\n在github上新建一个项目仓库用来存放mkdocs的内容\n在配置文件mkdocs.yml里添加:\nsite_url: https://seanoll.github.io/cpp/ # 换成自己的\n在mkdocs的项目文件夹里初始化git,连接远程仓库,部署\ngit initgit remote add origin git@github.com:Seanoll/cpp.git # 换成自己的# 如果 error: remote origin already exists.# 执行 git remote remove originmkdocs gh-deploy\n\n这个没复现过,只记录一下解决过程\n","categories":["skill"],"tags":["博客"]},{"title":"如何获取信息","url":"/2026/07/19/%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E4%BF%A1%E6%81%AF/","content":"大学阶段,信息是非常宝贵的资源,但是我仍然看到很多人因为信息差而受困。\n在大一刚开学的时候,我视奸看到很多人的微信里都有一个叫做“中山大学计算机学院水群”的聊天,可是我却没有。后来我才知道原来是有的班级的学长团成员告诉了他们班的同学,有些人则是通过高中同学被邀请进这个群聊,可惜我没有在那个班里,又不认识几个人。于是我在“中山大学2025级新生群”里询问谁有计算机学院水群,竟然真的有一位同学加了我好友并拉我进群,真的非常感谢她,加入这个群从而加入了计院资料二群,打破了很多信息差,对我的大学生活有很大的帮助。\n但是,仍然有人不知道这个信息,我的高中同学在高等代数的期中考试里发挥的很不好,和他交流我才知道他不在资料群里,不知道有往年真题,我觉得非常可惜。\n录取结果刚出来,我就从QQ上搜索“中山大学”相关的群聊,然后挑选了两个人多的进群,进群之后在群文件里找到了一些新生指南,在群里也有学长学姐回答我的问题。在开学之后,正如前面所说,从群里还加到了计算机学院水群。\n获取信息需要敏锐的观察,当你加入一个群之后,看它的群公告、群文件,有些时候,从群友的聊天中也能学到很多东西,比如在HPC交流群里,虽然大多数时候我都看不懂大佬们的话,但是偶尔也有对自己有帮助的东西出现,比如API稳定性测评网站和OnlineJudge。当你发现一个网站的时候,探索它的过程也很有趣,比如某个网站的链接是子链接(fanyi.baidu.com,netlabsysu.org/dwu),那你就可以把前缀/后缀去掉,看看它的主网页是什么,再比如有些个人博客的友链,从一个人的博客可以找到非常多人,从每个人的文章或者project里也可以学到一些东西。除此之外,搜索也是一项重要技能,这里的搜索当然不是指百度一下,而是指多种搜索并用,比如当你想获取3DGS相关信息的时候,你可以用百度、必应、谷歌多个搜索引擎,在微信、QQ、小红书、知乎、哔哩哔哩、Github、CSDN、Arxiv等等多个平台上搜索相关信息,同时问多个AI(这里推荐一个浏览器插件SimpleChatHub),你可以加入相关的QQ群,关注相关的社交媒体账号,持续了解相关信息。当然,人脉也是很重要的,如果你认识很多学长学姐,那你就可以直接问他们他们上过的课程评价如何,他们的导师评价如何等等。\n开学之初,有幸加入了金字塔学社,社团里有很多通晓历史和哲学知识的大佬,他们的知识储备和对社会问题的见解让我大受震撼,同时社团的公共资料网盘也极大地帮助了我。有点可惜的是,没有加入AI技术协会,后来我才知道这个社团里也有很多技术大佬,他们的网站和活动也对我很有帮助。后来,在一次保研经验分享会上,我得知了“绿群”,通过这个关键词,我搜到了CS-BAOYAN的仓库,也收获了很多。在超算队招新的时候,我加入了北京大学HPCGame的交流群,也就是后来的HPC交流群。\n写到这里,获取信息的方式差不多就完了,这只是我的个人观点,如果你也有一些获取信息的好方法,也欢迎交流。最后,我整理了一些中山大学计算机学院可能用到的资源,放在了 https://seanoll.github.io/resource 里,注意:计算机类实验室部分里,由于我想整理团队性质的实验室,所以没有放老师个人主页性质的网站;个人博客部分里,烛神的人脉最广,所以我放了他的博客,能在他的友链里找到的人我就不再重复放了;其他部分里,有些很好的网站比如卡码笔记、missing-semester、《谈谈如何让自己看上去、闻起来都像一个CS人》、上海交大生存指南或者说能够在上文所提及的网站中涉及到的资源我就不再放了。\n祝愿你能够打破信息差。\n","categories":["thinking"],"tags":["方法"]},{"title":"大一下学期总结","url":"/2026/08/02/%E5%A4%A7%E4%B8%80%E4%B8%8B%E5%AD%A6%E6%9C%9F%E6%80%BB%E7%BB%93/","content":"由于文笔太烂,我写的文字应该不会像AI生成的……\n转眼间已经成老东西了,我再也不是大学里最年轻的那一批人了。还好,我容易忘记很久以前的事情,这也算是一种活在当下吧。\n学习专业分流之后,我的绩点排名下降了,转专业的同学进来之后,又下降了。本来大一上学期还在前10%的,结果这下子一看跌出10%了。所以这个学期努力学习,晚上有课的次数多了,没有zbh陪我,那就自己去教室学。我也经常看到计算机学院的其他同学比我还要努力,在六教10点关灯之后又去二教学到12点再回宿舍,我很惊讶,也自愧不如。\n大学的绩点有时就是很看运气,我已经努力复习了数学分析,还是有一些错误,我也明白这不是平时不努力的问题,而是临场发挥的问题,我已经尽力了。我看到有些同学说自己的数电平时分不高,还有四史的随机给分,这些其实都是无法改变的,所以只能在自己能力范围内做到最好吧。\n好消息是,最终我以4.1692的绩点和18/349的排名结束了这个学期,马上就能进入前5%了。进入前5%,就达到了外保的门槛(外保真的有那么必要吗?……),接下来就是专心做科研了。\n虽然进入了实验室,但这个学期真没做什么实质性的工作,应该也是因为校区不同。我想,那些大学四年在同一个校区的同学,是不是大一就可以进组做一些实质性的工作了呢?好在暑假期间是真的开始做工作了,我很庆幸老师没有让我打杂(做一些数据标注或者画图之类的工作),虽然现在的方向不算很感兴趣,但我想先熟悉一遍流程,以后再自己选方向吧。(但感觉就算是感兴趣的方向在写论文时也会发现不是想象的那么美好)\n在这里放一个视频: https://www.bilibili.com/video/BV1cWYjzmENy 感觉讲得挺好。\n学习之外这个学期没那么闲了,所以参加志愿活动的次数少了,但是在社团里还能帮着干点活挣点志愿时,不管乐不乐意,反正最后是挺有成就感的。\n周末的时候,我愿意骑车去公园玩,现在回想一下,也去了不少了啊:麓湖公园、东山湖公园、珠江公园、海心沙亚运公园、海珠儿童公园、临江带状公园、水墨园、大夫山森林公园、晓港公园、海印公园。不得不说广州环境真好,闲暇时间有很多好去处,这是我在报志愿时没有想到的意外之喜。不逛公园的时候,我就去二沙岛上骑行,感觉对骑行协会的同学们来说,骑100公里是稀松平常的事,但我才只能骑40公里,速度还很慢。下个学期到东校,可以沿着大学城外环绿道和生物岛那种平坦的路骑,一定很爽。\n新认识了一些同学,班委团委和社团的运作都挺草台班子的,不过我也不追求综测。我不怎么主动说话,但是如果你愿意了解我或者主动询问我,我会非常高兴,就像这blog和我的朋友圈。换了舍友,不用再忍受巨大的呼噜声,我有预感我们可以愉快地相处下去。\n考完期末去了一趟成都,不少高中同学有了新的感情经历,我很羡慕,也很好奇,而且我还有很多需要学呀。很多社交技能是高考完才意识到的,比如通过观察同学和饭店老板的交流学习怎么正确地出去吃饭,通过观察妈妈和出租车司机的交流学习怎么自己坐出租车,感觉自己像个婴儿。但我不知道情侣是怎么表白、怎么约会的,况且有些表达爱的方式是我不能理解和接受的。\n初学游泳的时候,动作僵硬,学了好几天也没学会,而且教练教的和网上不一样,我不相信游泳是这样的,我猜一定有某种神奇的灵感,身体一旦发现了这种感觉,就会像开窍了一样马上学会。\n事实不是这样的,虽然我比其他学员的进度慢了很多,但是我每天都有一点点进步,而且在反复的练习中我逐渐找到了那种感觉,那种身体自然而然就知道怎么游的感觉。这是一次奇妙的经历,或许谈恋爱也是这样吧,并没有什么神奇之处,经历了就知道是怎么回事了。\n还有,旅行的时候,请找一个和你三观相符的人一起,否则就自己。\n","categories":["thinking"],"tags":["总结"]},{"title":"大学英文缩写怎么起","url":"/2026/07/31/%E5%A4%A7%E5%AD%A6%E8%8B%B1%E6%96%87%E7%BC%A9%E5%86%99%E6%80%8E%E4%B9%88%E8%B5%B7/","content":"中国大学数量非常多,英文缩写就容易重复,为了避免重复出现了下面这些有意思的现象。\n湖南工业大学HUT湖北工业大学HBUT河北工业大学HEBUT哈尔滨工业大学HIT哈尔滨理工大学HRBUST(这个多了个science,但是与华科HUST,湖北科技学院HBUST又重名了)\n理论上这五个都能缩写成HUT,因为意思上都是Hxxx University of Technology,哈工大改成了institute研究院,和麻省理工大学MIT有异曲同工之妙,下边的北理工BIT也是这样。\n暨南大学JNU江南大学JNU济南大学UJN \n它们都可以叫JNU,济南大学把Jinan放在后边避免重复,但暨南大学和江南大学的缩写是一模一样的。由于大学的官方网站格式全都是www.xxx.edu.cn,所以只能有一个使用JNU作为网址,通过官网可以发现,江南大学使用了jiangnan而非jnu,类似地,清华大学使用tsinghua而非thu。\n同济大学Tongji天津大学TJU\n同济大学最初由德国人创办,其德语简称为 TCU。后来推行拼音,将“同济”音译为 Tongji。\n北京理工大学BIT北京工业大学BJUT北京科技大学USTB北京化工大学BUCT为了避免重复,可以使用同义词替代(institude,university,academy),顺序调换(univeraity of … / … university),拼音首字母多写(Beijing=B/BJ)等方式,看这些缩写挺有意思。除此之外,还有一些大学明明是同义词却不用一种,让强迫症很难受,比如:中北大学North University of China\n中南大学Central South University\n原因应该是与首都师范大学Capital Normal University首字母相同了。电子科技大学University of Electronic Science and Technology of China\n西安电子科技大学Xidian University\n杭州电子科技大学Hangzhou Dianzi University\n桂林电子科技大学Guilin University Of Electronic Technology\n交通大学校友总会成员为上海交通大学、西安交通大学、西南交通大学、北京交通大学、阳明交通大学,统称“中国交大五校”。\n上海交通大学SJTU西安交通大学XJTU西南交通大学SWJTU北京交通大学BJTU非常的一致(兰州交通大学LZJTU)但是阳明交通大学特立独行,它叫National Yang Ming Chiao Tung University,缩写为NYCU。\n上海戏剧学院Shanghai Theatre Academy\n中央戏剧学院The Central Academy Of Drama\n为什么一个是Theatre,另一个是Drama呢?之前在知乎上搜到过答案,今天看的时候已经失效了。\nhttps://zhuanlan.zhihu.com/p/370166427这篇专栏写得也很好,推荐!\n修改自2023年1月15日发的朋友圈\n","categories":["thinking"]}]