从微积分和线性代数的角度理解最小二乘法
之前尝试实现图形学一些论文的时候用到过最小二乘法,不过之前是从微积分的角度理解的。今年学了线性代数的课,发现这个东西也可以用线性代数的概念解释,并且更加直观(有几何角度的理解),所以准备用这篇文章记录一下最小二乘法的两种理解方式。
开始之前先吐槽一下这个东西的名字 “最小二乘”。就感觉很奇怪,最小化误差的平方为什么叫二乘呢?嗯。。。查了一下发现是从日语翻译过来的,那我只能说这个翻译水平是有点高的。。。虽然我很不想用这个迷惑的名字,但是因为都这么用的,那也没办法了。
问题的定义
学习最小二乘法之前先得理解这个算法在尝试解决什么。最小二乘法最常见的用途是用一个函数拟合数据,进而用这个函数来预测数据的趋势。为了拟合数据我们就需要用数学的方法来定义怎样的拟合是好的,然后我们尽量一个函数更好的拟合数据。
这里我们假设有 mmm 个数据点 (xi,yi) i∈{0,1,⋯ ,m}(x_i, y_i) \ \ i \in \{0, 1, \cdots, m\}(xi,yi) i∈{0,1,⋯,m}。然后有一个函数 f(x)f(x)f(x) 用于拟合这些数据点。我们定义单个点的误差为:
si= ...
GAMES101 学习笔记1
因为学校里的各种事情,以及复习考试,自从上次更新博客已经是四个多月的事情了,距离上次写图形学的博客那就更久了。
最近刚放暑假把之前只看了光线追踪部分的 GAMES101 完整的学习了一遍,还是非常惊喜的:很多之前不太清楚的概念(特别是数学方面的)过了一段时间再看又有了些新的理解。因为课程时间限制的问题,有有部分内容没有比较详细的讲,这里记录一些我自己的理解。
三维旋矩阵
这个东西我已经在之前 RT: The Next Week 的文章中写过了,不过之前的解释比较。。。怪,很啰嗦,并且没有从坐标系变换的角度来解释,这里就重新写下(当然我还没有系统的学过线性代数,所以以下内容可能还是很扯)。
绕三个轴的三维旋转矩阵分别可以写作下列的形式:
Rx(θ)=[1000cosθ−sinθ0sinθcosθ]R_x(\theta) = \begin{bmatrix}
1 & 0 & 0 \\
0 & \cos \theta & -\sin \theta \\
0 & \sin \theta & \cos \theta
\end{bmatrix} ...
USACO23JAN Find and Replace S(洛谷 P9013)题解
题目链接
博客中观看体验更佳
分析
题意非常简洁,即问你通过一系列的字符替换,最少花多少步能把一个 sss 串变成 ttt 串。
拿到题之后,可以先从样例开始分析。
从 BBC→ABC\texttt{BBC} \to \texttt{ABC}BBC→ABC 这个样例可以发现,不可能同时把某个字符替换成两个字符(BB→AB\texttt{BB} \to \texttt{AB}BB→AB),会起冲突。
那直接统计 si≠tis_i \ne t_isi=ti 的个数(给串去重之后,即不存在 s=AA,t=BBs = \texttt{AA}, t = \texttt{BB}s=AA,t=BB 这种)就可以作为答案了吗?可以从最后一个样例发现不是这样的。
环的处理
因为最后一个样例中,CD\texttt{CD}CD 的部分是一样的。我们直接考虑 AB→BA\texttt{AB} \to \texttt{BA}AB→BA 的变换。如果直接执行 A→B\texttt{A} \to \texttt{B}A→B 的操作,会得到一个 BB\texttt{BB}BB 的串。这个时候就有了和前面一样的问 ...
[Stanford CS144] Lab4 实验记录
代码实现 TCP 状态流转图
Lab4 的主要作用是把前面的 receiver 和 sender 结合起来,形成一个完整的 TCP 协议栈。所以熟悉 TCP 的状态流转图就很重要了。
下面是一个 TCP 的状态流转图:
TCP 连接的建立
参考上图,可以看到 TCP 有两种建立连接的方法。第一种是主动连接,即给对方发送一个 SYN 包。第二种是被动连接,即接收到一个 SYN 包后,回复 SYN+ACK 包。
主动连接
对于主动连接,我们需要实现 connect() 函数:
12345void TCPConnection::connect() { _shutted = false; _sender.fill_window(); send_sender_segs();}
这里的 shutted 变量表示连接是否已经关闭,之后在 active() 函数中使用。因为我们之前实现过 TCPSender 的 fill_window() 函数,它会记录连接是否已经建立,如果没有会自动发送 SYN 包。
不过 TCPSender 的 fill_window() ...
[Stanford CS144] Lab0-Lab3 实验记录
注:因为实验指导书和课程文件[1]里都明确的写了不能公开代码,所以博客上的实验记录就主要记录思路以及一些核心代码片段,不会公开完整的仓库。
Lab0 networking warmup
Lab 要求实现一个在内存层面上可靠的字节流(ByteStream),感觉和 unix 中的管道挺像的。其实这样先进先出的结构完全可以直接使用 STL 的 queue<char> 实现,会非常简单。但是考虑到 lab 的要求是一个固定大小(capacity)的字节流,个人认为直接开个数组模拟更合适,速度也应该会更快。
具体来说,就是开一个 string(没有直接使用字符数组是因为实验指导书提到了最好使用现代 C++ 的风格,避免使用 new 来手动分配内存)来储存数据,以及一个头指针和尾指针指向字节流的开始和结尾。这样就实现了一个环形队列,peek_output() 函数的实现大概是下面这样的:
1234567891011string ByteStream::peek_output(const size_t len) const { size_t peek_size = mi ...
CF1774C 题解
吐槽一下,官方题解写的挺难看懂的,看了好久还是挺迷糊的(其实也是我太菜了)。搞懂之后感觉这题挺妙的,来写下题解。
思路
我们首先需要有一个观察,就是对于 sss 串,最后一个连续字串不会增加可能的获胜人数。比如 s=0011s = \texttt{0011}s=0011 时,后面结尾的 11\texttt{11}11 就不会增加可能的获胜人数。
为啥呢,设我们设经过任意次数对战后,玩家可能组合的集合为 ttt 那么对于任意的 x∈tx \in tx∈t,连续在环境 111 中对战任意次数后,最终的赢家一定是 xxx 中温度最高的(因为每个剩下的玩家都会需要连续在环境 111 中对战,唯一能胜出所有对战的玩家一定是最大的)。同理,ttt 中的玩家连续在环境 000 中对战任意次数后,最终的赢家一定是 xxx 中温度最低的。
例如,s=111s = \texttt{111}s=111 时,最后胜出的一定是 444 号玩家。
这样一来,如果结尾段是 111(000 结尾同理,后面为了方便先用 111 的例子了),我们只需要算出前面的部分最多能构造出多少种最大值(玩家温度)不同的玩家组合,就能 ...
反向传播(Backpropagation)算法学习笔记,基于全连接神经网络
upd@2022/11/5:添加了具体实现,修正了推导中的一些符号错误
upd@2024/6/8:修正了推导中的一个下标错误
反向传播算法的主要目的是计算出神经网络中误差对于偏置和权重等参数的偏导数,以此来进行梯度下降。本文的上半部分主要是算法的推导,后半部分使用全连接神经网络和 mnist 数据集实现手写数字识别。
这个算法对我来说还是很难理解的,为了防止自己忘掉,就写了这篇笔记(还有就是神经网络里这些公式的上标下标太多了,如果用真的笔记本写,稍微一不小心就写错了)。如果你对神经网络还没有基本的概念,推荐去看 3b1b 的神经网络系列视频
这里必须说一句 MqCreaple 大佬真的太巨了,看了视频之后直接手推了全部的公式更令人震惊的是居然把我这种人教会了。
公式推导
符号和语言约定
σ(){\sigma()}σ() 表示激活函数
EEE 表示最终误差
err()\operatorname{err}()err() 表示误差函数
y^\hat{y}y^ 表示神经网络给出的预测值,而 yyy 表示实际的答案
lll 表示神经网络的层数,值越小代表离输入层越近
wji[l]w^{[ ...
Ray Tracing : The Next Week 学习笔记(1)
一个多月终于断断续续搞完了第二本书里的内容。和前面两篇文章一样,这篇也会写一些我个人花了较长时间才搞懂的部分,以及一些我在原书基础上加的新功能。
对于原书就有的功能,会直接使用书上的代码,如果是我新加进去的功能,会使用自己的代码。因为我的代码在原书基础上做了较大幅度的变化(即使是原来就有的功能),所以只看一段代码可能不太明白,这里可以参考我的 GitHub 仓库: https://github.com/ttzytt/RTOW
bvh_node
这部分主要是一些小细节我当时没太理解。首先是
1234bvh_node::bvh_node( std::vector<shared_ptr<hittable>>& src_objects, size_t start, size_t end, double time0, double time1)
首先是这个构造函数的范围问题。每颗子树是不负责 end 位置的 hittable 的。也就是这个构造函数负责的是 [start, end) 这样的一个区间。
这也解释了代码中 sort 的用法:
1std::s ...
Ray Tracing in One Weekend 学习笔记(2):相机类的实现
相机类的实现
除了朗伯体,RTOW 中还有个比较有趣的地方就是相机类的实现,特别是背景虚化这部分。
相机的定位
先来看一下相机类里一个相对简单的部分–相机的定位。只要通过三个参数就能确定相机的位置,分别是相机本身的位置(lookfrom),相机正在拍摄的位置(lookat)和表示相机上方位置的向量(vup),书里的图就能很好的解释:
在构造函数中,我们需要把这三个参数转换成表示相机朝向的三个参数,以及做一些对焦距,光圈和 fov 的处理,书中没有在这部分花很多的篇幅,我当时想明白也花了挺久的,下面是我对书中实现的一些思考。
因为我对书中的代码稍作了一些修改(主要是命名?)所以先贴一下代码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#pragma once#include "rtow.h"// 这里的 f8 就是 double (八个字节的 float)class camera { public: camera(v ...
Ray Tracing in One Weekend 学习笔记(1):朗伯体和辐射度量学
最近(距离搞完 RTOW 已经过去一周了,我现在才把这笔记写出来,属实是懒狗)花了一些时间看完了 Ray Tracing in One Weekend (以下简称 RTOW)果然还是我太菜了,这玩意 One Weekend 没搞完,也跟着把代码写出来了。
本书写的非常不错,最后渲染出的效果也是出乎我的意料(封面图)。但是因为我以前对计算机图形学没有任何的认识,很多基本的知识都不了解。
而书上有时会把这些基本知识(或者数学推导和证明)一笔带过,因此准备写个博客把自己的思考过程写一下。
朗伯体材质 (Lambertian) 的实现
在书中,创建一个朗伯体漫反射材质的方法是下面这样:
1234567891011121314151617class lambertian : public material { public: lambertian(const color& alb) : albedo(alb) {} virtual optional<pair<ray, color>> get_ray_ ...
[MIT 6.s081] Xv6 Lab11 Mmap 实验记录
upd@2022/9/14:最近把实验的代码放到 github 上了,如果需要参考可以查看这里:
https://github.com/ttzytt/xv6-riscv
里面不同的分支就是不同的实验。
最后一个 lab 了,终于搞完了!!
Lab11: mmap
描述
实现一个 UNIX 操作系统中常见系统调用 mmap() 和 munmap() 的子集。此系统调用会把文件映射到用户空间的内存,这样用户可以直接通过内存来修改和访问文件,会方便很多。
mmap() 的定义如下:
12void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
意思是映射描述符为 fd 的文件,的前 length 个字节到 addr 开始的位置。并且加上 offset 的偏移量(即不从文件的开头映射)。
如果 addr 参数为 0,系统会自动分配一个空闲的内存区域来映射,并返回这个地址。
在实验中我们只需要支持 addr 和 offset 都为 0 的情况,也就是完全不用考虑用户指定 ...
[MIT 6.s081] Xv6 Lab10 File System 实验记录
upd@2022/9/14:最近把实验的代码放到 github 上了,如果需要参考可以查看这里:
https://github.com/ttzytt/xv6-riscv
里面不同的分支就是不同的实验。
Lab10: file system
Large files
描述
在 xv6 的底层实现中,文件是由 struct dinode 来描述的,如下:
12345678struct dinode { short type; // File type short major; // Major device number (T_DEVICE only) short minor; // Minor device number (T_DEVICE only) short nlink; // Number of links to inode in file system uint size; // Size of file (bytes) uint addrs[NDIRECT + ...