gyro永不抽风！ - DL
type: channel
link: https://gallery.gyrojeff.top/index.php/tag/DL/
items: PyTorch Data Augmentation 数据增广
items: 读论文——YOLO v1
items: 深度学习分布偏移理论
items: 计算图的简单例子 - 参数复用
items: PyTorch 模块与参数 Cheatsheet
items: 深度学习的正则化方法
items: 从精度问题看 softmax 与 CrossEntropyLoss

PyTorch Data Augmentation 数据增广
type: item
date: 2022-05-02T15:15:26+00:00
description: Data Augmentation 这个词其实很容易误导人，因为 Augmentation 是增广的意思。数据增广，自然而然就会让人理解为：“增加了新的数据”。然而在实现的过程中，不一定是这样。 我们会给定一些随机化的参数，在每个 epoch 内生成随机增广后的图片。 比方说，每个 epoch 都按照一定的 random rule 来 crop 图片、调整色调，便是图像增广。 PyTorch 实现 这里拿 Fashion-MNIST 举例，同样我们会给出例子来证明我们的做法是行之有效的。 首先引入包： %matplotlib inline %config InlineBackend.figure_formats = ['svg'] from matplotlib import pyplot as plt import torch import torchvision from torch.utils import data 加载 FashionMNIST 数据集，并且禁用 shuffle，这样保证我们每个 epoch 图片顺序都是相同的，方便我们验证： def load_data_fashion_mnist(batch_size, dataloader_worker_count, resize=None): trans = [torchvision.transforms.ToTensor()] if resize: trans.insert(0, torchvision.transforms.Resize(resize)) trans.append(torchvision.transforms.RandomCrop((28, 28))) trans = torchvision.transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST( root="../data", train=True, transform=trans, download=True) mnist_test = torchvision.datasets.FashionMNIST( root="../data", train=False, transform=trans, download=True) return (data.DataLoader(mnist_train, batch_size, shuffle=False, num_workers=dataloader_worker_count), data.DataLoader(mnist_test, batch_size, shuffle=False, num_workers=dataloader_worker_count)) 辅助渲染的函数： def get_fashion_mnist_labels(labels): ''' 返回Fashion-MNIST数据集的文本标签 ''' text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot'] return [text_labels[int(i)] for i in labels] def show_images(imgs, num_rows, num_cols, titles=None, scale=1.2): ''' 绘制图像列表 ''' figsize = (num_cols * scale, num_rows * scale) _, axes = plt.subplots(num_rows, num_cols, figsize=figsize) axes = axes.flatten() for i, (ax, img) in enumerate(zip(axes, imgs)): if torch.is_tensor(img): # 图片张量 ax.imshow(img.numpy()) else: # PIL图片 ax.imshow(img) ax.axes.get_xaxis().set_visible(False) ax.axes.get_yaxis().set_visible(False) if titles: ax.set_title(titles[i]) return axes 测试三个 epoch，看每次 epoch 第一个 batch 的数据： train_iter, test_iter = load_data_fashion_mnist(18, 4, resize=40)…

读论文——YOLO v1
type: item
date: 2022-04-30T14:48:20+00:00
description: 论文链接：https://arxiv.org/abs/1506.02640 Abstract 以前都是用 Classifiers 来做 detection 现在：we frame object detection as a regression problem to spatially separated bounding boxes and associated class probabilities end-to-end FAST! Fast YOLO: 155 fps, double mAP of other real-time detectors YOLO: 45 fps Introduction Related works Deformable Parts Models (DPM): sliding window approach where the classifier is run at evenly spaced locations over the entire image R-CNN: first generate potential bounding boxes in an image then run a classifier on these proposed boxes post-processing: refine the bounding boxes, eliminate duplicate detections, and rescore the boxes based on other objects in the scene YOLO Fast, realtime simple architecture see the entire image => less background error learn generalizable representations of objects accuracy 不行, 虽然识别率高，但是定位精准度相对低 Architecture Unified Detection 描述大致的识别思路。 先划分成 $S\times S$ 方格，每个方格需要检测是否有物体的中心在这个方格内，产生 $B$ 个 bounding box 每个 bounding box 会产生五个预测量 $x, y, w, h, \text{confidence}$。注意：$x,y$ 是相对于 cell 的，$w, h$ 是相对于整个图像的。 confidence 描述这个区域有物体的置信度。confidence 的定义： $$ P(\text{Object}) \times \text{IoU}_{\text{pred}}^{\text{truth}} $$ 每个方格产生一个还会对 $C$ 个类别判断的概率，判断这个方格内是否含有类别 $C_i$ $$ P(\text{Class}_i \mid \text{Object}) $$ 测试的时候每个 bounding box 的置信概率就是 $$ P(\text{Class}_i \mid \text{Object}) \times P(\text{Object}) \times \text{IoU}_{\text{pred}}^{\text{truth}} = P(\text{Class}_i) \times \text{IoU}_{\text{pred}}^{\text{truth}} $$ Network Design 我们取 $B=2$，每个区域生成两个 bounding box，区域数量（边） $S=7$，然后分类的类别是 $C=10$。所以最后的输出 $$ S \times S \times (5B + C) = 7 \times 7 \times 30 $$ Training Pretrain ImageNet 数据集 前 20 个卷积层（去掉最后四个卷积层和两个全连接层）再加上一个平均池化层和一个全连接层 Activation 出最后一个层外，使用 Leaky ReLU $$ \phi(x) = \begin{cases} x & x > 0 \\ 0.1x & \text{otherwise} \end{cases} $$ 最后一层文章中说用的是 linear activation function 除此之外，因为最后的结果应该在 $[0,1]$ 范围之内，查到一个 stackoverflow 的问题说最后可能对 output 逐元素做了 sigmoid. https://stackoverflow.com/questions/49707542/yolo-v1-bounding-boxes-during-training-…

深度学习分布偏移理论
type: item
date: 2022-04-02T12:37:00+00:00
description: 本节主要为以下内容的笔记：https://zh-v2.d2l.ai/chapter_multilayer-perceptrons/environment.html 训练到底在做什么？ 训练其实是在最小化通过数据集得到的经验风险： $$ \mathop{\mathrm{minimize}}_f \frac{1}{n} \sum_{i=1}^n l(f(\mathbf{x}_i), y_i), $$ 其中 $l$ 是损失函数，用来度量对于给定的标签 $y_i$，预测值 $f(\mathbf {x}_i)$ 的糟糕程度。 但是我们想要得到的又是什么呢？我们想要的是最小化实际风险，也就是最小化下面这个期望： $$ E_{p(\mathbf{x}, y)} [l(f(\mathbf{x}), y)] = \int\int l(f(\mathbf{x}), y) p(\mathbf{x}, y) \;d\mathbf{x}dy $$ 然而在实践中，我们通常无法获得总体数据。 因此，经验风险最小化， 是一种实用的机器学习策略，希望能近似最小化真实风险。 协变量偏移 在不同分布偏移中，协变量偏移可能是最为广泛研究的。这里我们假设：虽然输入的分布可能随时间而改变，但标签函数（即条件分布 $P(y \mid \mathbf{x})$ ）没有改变。统计学家称之为协变量偏移 （covariate shift），因为这个问题是由于协变量（特征）分布的变化而产生的。虽然有时我们可以在不引用因果关系的情况下对分布偏移进行推断，但在我们认为 $\mathbf{x}$ 导致 $y$ 的情况下，协变量偏移是一种自然假设。 协变量偏移的例子 现在我们使用猫狗照片 $\mathbf x$ 来训练一个猫狗分类器来给出猫狗标签 $y$ 训练数据集是真实的猫狗图片 测试数据集是卡通形象的猫狗图片 虽然猫狗图片的分布 $P(\mathbf{x})$ 被改变了（测试集上图片的分布改变了），但是猫狗图片对于猫狗分类的条件分布 $P(y \mid \mathbf{x})$ 并未改变（确定了照片，分类也就确定了） 这是由于特征分布的变化导致的（协变量的变化，这里的协变量就是图片风格） 在这种情况下，是猫狗图片 $\mathbf x$ 导致猫狗分类 $y$ 的，所以我们一般可以使用协变量偏移假设 协变量偏移的纠正 见：https://zh-v2.d2l.ai/chapter_multilayer-perceptrons/environment.html#subsec-covariate-shift-correction 总结一下： 设目标分布为 $p(x)$，源分布为 $q(x)$，则根据协变量偏移假设 $$ p(y \mid \mathbf x) = q(y \mid \mathbf x) $$ 则 $$ \begin{aligned} &\int\int l(f(\mathbf{x}), y) p(\mathbf{x},y) \;d\mathbf{x}dy \\ =&\int\int l(f(\mathbf{x}), y) p(y \mid \mathbf{x})p(\mathbf{x}) \;d\mathbf{x}dy \\ =&\int\int l(f(\mathbf{x}), y) q(y \mid \mathbf{x})q(\mathbf{x})\frac{p(\mathbf{x})}{q(\mathbf{x})} \;d\mathbf{x}dy. \end{aligned} $$ 所以现在我们需要一个额外的权重 $$ \beta_i \stackrel{\mathrm{def}}{=} \frac{p(\mathbf{x}_i)}{q(\mathbf{x}_i)} $$ 来调整训练方式： $$ \mathop{\mathrm{minimize}}_f \frac{1}{n} \sum_{i=1}^n \beta_i l(f(\mathbf{x}_i), y_i) $$ 为了得到这个权重，我们可以通过训练一个二元分类器，来学习数据到底是来自于训练集还是来自于测试集。如果无法区分这两个分布，则意味着想相关的样本可能来自这两个分布中的任何一个。 另一方面，任何可以很好区分的样本都应该相应地显著增加或减少权重。 接下来是训练的方法：为了简单起见，假设我们分别从 $p(x)$ 和 $q(x)$ 两个分布中抽取相同数量的样本。 现在用标签 $z$ 表示：从 $p$ 抽取的数据为 $1$，从 $q$ 抽取的数据为 $-1$。 根据上面的假设，我们有： $$ P(z=1 \mid \mathbf{x}) = \frac{p(\mathbf{x})}{p(\mathbf{x})+q(\m…

计算图的简单例子 - 参数复用
type: item
date: 2022-03-30T01:49:37+00:00
description: Preface 这块内容比较搞脑子，这里举一个较为简单的参数复用的例子来说明。基础内容请看： https://zh-v2.d2l.ai/chapter_multilayer-perceptrons/backprop.html 回顾偏导数的链式法则 对于 $$ u = g(f_1(x_1, x_2, \cdots, x_n), f_2(x_1, x_2, \cdots, x_n), \cdots, f_m(x_1, x_2, \cdots, x_n)) $$ 而言 $$ \frac{\partial u}{\partial x_j} = \sum_{i = 1} ^ m \frac {\partial g} {\partial f_i} \frac {\partial f_i} {\partial x_j} $$ 我们不妨将链式求导写成雅可比矩阵，更加通俗易懂： $$ \left(\begin{array}{cccc} \frac{\partial u_{1}}{\partial x_{1}} & \frac{\partial u_{1}}{\partial x_{2}} & \cdots & \frac{\partial u_{1}}{\partial x_{n}} \\ \frac{\partial u_{2}}{\partial x_{1}} & \frac{\partial u_{2}}{\partial x_{2}} & \cdots & \frac{\partial u_{2}}{\partial x_{n}} \\ \cdots & \cdots & \cdots & \cdots \\ \frac{\partial u_{p}}{\partial x_{1}} & \frac{\partial u_{p}}{\partial x_{2}} & \cdots & \frac{\partial u_{p}}{\partial x_{n}} \end{array}\right)=\left(\begin{array}{cccc} \frac{\partial g_{1}}{\partial y_{1}} & \frac{\partial g_{1}}{\partial y_{2}} & \cdots & \frac{\partial g_{1}}{\partial y_{m}} \\ \frac{\partial g_{2}}{\partial y_{1}} & \frac{\partial g_{2}}{\partial y_{2}} & \cdots & \frac{\partial g_{2}}{\partial y_{m}} \\ \cdots & \cdots & \cdots & \cdots \\ \frac{\partial g_{p}}{\partial y_{1}} & \frac{\partial g_{p}}{\partial y_{2}} & \cdots & \frac{\partial g_{p}}{\partial y_{m}} \end{array}\right)\left(\begin{array}{cccc} \frac{\partial f_{1}}{\partial x_{1}} & \frac{\partial f_{1}}{\partial x_{2}} & \cdots & \frac{\partial f_{1}}{\partial x_{n}} \\ \frac{\partial f_{2}}{\partial x_{1}} & \frac{\partial f_{2}}{\partial x_{2}} & \cdots & \frac{\partial f_{2}}{\partial x_{n}} \\ \cdots & \cdots & \cdots & \cdots \\ \frac{\partial f_{m}}{\partial x_{1}} & \frac{\partial f_{m}}{\partial x_{2}} & \cdots & \frac{\partial f_{m}}{\partial x_{n}} \end{array}\right) $$ 参数复用的例子 假设我们现在有如下的简单方程 (略去了激活函数来方便验证)： $$ A_1 = W x $$ $$ Z_1 = A_1 + b_1 $$ $$ A_2 = W Z_1 $$ $$ Z_2 = A_2 + b_2 $$ 这个过程中，$W$ 被用到了两次。思考一下在自动求导的过程中如果要求 $\displaystyle \frac{\partial Z_2}{\partial W}$ 会发…

PyTorch 模块与参数 Cheatsheet
type: item
date: 2022-03-30T01:49:10+00:00
description: Preface Define Network Custom Module Custom Module with Params Implement Sequential Custom Forward Function Access Parameter Access All Params Nested Network Params Parameter Initialization Share Parameters Save & Load Network Params GPU Info GPU Device Network on GPU Preface 这块内容繁多冗长，每次查教程也很繁琐，所以就直接整理成 Cheatsheet了。 代码全部出自 d2l chapter 5. Define Network import torch from torch import nn from torch.nn import functional as F net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10)) X = torch.rand(2, 20) net(X) tensor([[-0.1216, 0.0153, 0.0546, -0.0989, -0.0582, 0.1448, -0.3097, -0.0478, -0.1381, 0.0593], [-0.1315, 0.0540, 0.0157, -0.0701, -0.2307, 0.0710, -0.2731, -0.0527, -0.2170, 0.1010]], grad_fn=<AddmmBackward0>) Custom Module class MLP(nn.Module): # 用模型参数声明层。这里，我们声明两个全连接的层 def __init__(self): # 调用MLP的父类Module的构造函数来执行必要的初始化。 # 这样，在类实例化时也可以指定其他函数参数，例如模型参数params（稍后将介绍） super().__init__() self.hidden = nn.Linear(20, 256) # 隐藏层 self.out = nn.Linear(256, 10) # 输出层 # 定义模型的前向传播，即如何根据输入X返回所需的模型输出 def forward(self, X): # 注意，这里我们使用ReLU的函数版本，其在nn.functional模块中定义。 return self.out(F.relu(self.hidden(X))) net = MLP() net(X) tensor([[ 0.0097, 0.0207, 0.1453, -0.0685, 0.0505, 0.2176, 0.0180, -0.2566, 0.1506, -0.0075], [ 0.0425, -0.0261, 0.1969, 0.0842, 0.0037, 0.1542, -0.0176, -0.1798, 0.0179, -0.1200]], grad_fn=<AddmmBackward0>) Custom Module with Params PyTorch 会自动识别参数 (nn.Parameter) class MyLinear(nn.Module): def __init__(self, in_units, units): super().__init__() self.weight = nn.Parameter(torch.randn(in_units, units)) self.bias = nn.Parameter(torch.randn(units,)) def forward(self, X): linear = torch.matmul(X, self.weight.data) + self.bias.data return F.relu(linear) linear = MyLinear(5, 3) linear.weight Parameter containing: tensor([[-2.2981, -1.8825, -0.9347], [ 0.1222, -1.0374, 1.1512], [-0.2859, -0.0680, 0.9072], [ 1.2177, -0.8947, 0.6278], [ 1.4800, 0.5804, -0.9661]], requires_grad=True) Implement Sequential class MySequential(nn.Module): def __init__(self, *a…

深度学习的正则化方法
type: item
date: 2022-03-29T12:23:13+00:00
description: 为什么要正则化？ 在探讨这个问题之前，我们需要先引入欠拟合和过拟合的概念。 左侧：欠拟合，high bias, $error_{train} \gg error_{dev}$ 右侧：过拟合，high variance, $error_{train} \rightarrow 0\%$ 在过拟合的时候我们需要引入正则化，来避免过拟合。 这里介绍两种正则化方式： 权重衰减 (weight decay) Dropout Weight Decay Weight Decay 的主要思想就是加快参数的更新幅度，也就是说增大损失函数，使得梯度下降的步幅变大。但为什么这样可以防止过拟合？根据奥卡姆剃刀法则，我们倾向于认为，参数越小，网络越不复杂，越不容易过拟合，而实践也证明了这一点。 下面的公式节选自我之前的汇报笔记： 逻辑回归: $\min_{w, b}J(w, b), w\in \mathbb R^{n_x}, b\in \mathbb R$ L2 正则化：重定义 $J(w, b)$ $$ J(w, b) = \frac{1}{m}\sum_{i = 1}^{m}\mathcal L(\hat y^{(i)}, y^{(i)}) + \frac{\lambda}{2m}||w||_2^2,~||w||_2^2 = \sum_{j = 1}^{n_x}w_j^2 = w^\mathrm T w $$ L1 正则化：重定义 $J(w, b)$ $$ J(w, b) = \frac{1}{m}\sum_{i = 1}^{m}\mathcal L(\hat y^{(i)}, y^{(i)}) + \frac{\lambda}{2m}||w||_1,~||w||_1 = \sum_{j = 1}^{n_x} |w_j| $$ 神经网络：重定义损失函数： $$ J(W^{[1]}, b^{[1]},\cdots, W^{[l]}, b^{[l]}) = \frac{1}{m}\sum_{i = 1}^m \mathcal L(\hat y^{(i)}, y^{(i)}) + \frac{\lambda}{2m}\sum_{i = 1}^m ||W^{[l]}||_\mathrm F^2 $$ $$ ||W^{[l]}||_\mathrm F^2 = \displaystyle\sum_{i = 1}^{n^{[l]}}\displaystyle\sum_{j=1}^{n^{[l-1]}}(W^{[l]}_{ij})^2 $$ 红色的项标出了正则化后迭代中增加的项： $$ dW^{[l]} = \frac{1}{m} dZ^{[l]} A^{[l-1] T} \color{red}{ + \frac {\lambda}{m}W^{[l]}} $$ $$ W^{[l]} \leftarrow W^{[l]} - \alpha\frac{1}{m} dZ^{[l]} A^{[l-1] T}\color{red}{ - \alpha \frac {\lambda}{m}W^{[l]}} $$ 下面的公式出自 d2l 教科书，能让我们更好的理解超参数 $\lambda$ 的作用： 对于线性回归的损失函数： $$ L(\mathbf{w}, b) = \frac{1}{n}\sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2 $$ 我们将其重定义为： $$ J(\mathbf{w}, b) = L(\mathbf{w}, b) + \frac{\lambda}{2} \|\mathbf{w}\|^2, $$ 我们来看参数的迭代式发生了什么变化。这是原来的迭代式： $$ \begin{aligned} \mathbf{w} &\leftarrow \mathbf{w} - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{\mathbf{w}} l^{(i)}(\mathbf{w}, b) = \mathbf{w} - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \mathbf{x}^{(i)} \left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right),\\ b &\leftarrow b - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_b l^{(i)}(\mathbf{w}, …

从精度问题看 softmax 与 CrossEntropyLoss
type: item
date: 2022-03-28T11:59:42+00:00
description: Softmax 运算 softmax 运算用于将实数序列映射为和为一的概率序列，换句话说，softmax 运算用于将实数值规格化，使其和为一，映射成一个合理的概率分布。 下面是 softmax 的定义： $$ \hat{\mathbf{y}} = \mathrm{softmax}(\mathbf{o})\quad \text{where}\quad \hat{y}_j = \frac{\exp(o_j)}{\sum_k \exp(o_k)} $$ 对数似然与交叉熵损失函数 这里应用 d2l 教科书上的一系列公式与语段： softmax函数给出了一个向量 $\hat{\mathbf{y}}$， 我们可以将其视为“对给定任意输入$\mathbf{x}$的每个类的条件概率”。 例如，$P(y=\text{猫} \mid \mathbf{x})$。 假设整个数据集 $\{\mathbf{X}, \mathbf{Y}\}$ 具有 $n$ 个样本， 其中索引 $i$ 的样本由特征向量 $\mathbf{x}^{(i)}$ 和独热标签向量 $\mathbf{y}^{(i)}$ 组成。 我们可以将估计值与实际值进行比较： $$ P(\mathbf{Y} \mid \mathbf{X}) = \prod_{i=1}^n P(\mathbf{y}^{(i)} \mid \mathbf{x}^{(i)}) $$ 根据最大似然估计，我们最大化 $P(\mathbf{Y} \mid \mathbf{X})$ ，相当于最小化负对数似然： $$ -\log P(\mathbf{Y} \mid \mathbf{X}) = \sum_{i=1}^n -\log P(\mathbf{y}^{(i)} \mid \mathbf{x}^{(i)}) = \sum_{i=1}^n l(\mathbf{y}^{(i)}, \hat{\mathbf{y}}^{(i)}) $$ 其中，对于任何标签 $\mathbf{y}$ 和模型预测 $\hat{\mathbf{y}}$ ，损失函数为： $$ l(\mathbf{y}, \hat{\mathbf{y}}) = - \sum_{j=1}^q y_j \log \hat{y}_j $$ 再论交叉熵损失函数 我们一定要明确一个点：交叉熵损失函数的值是一个数。我们再来看这个公式： $$ l(\mathbf{y}, \hat{\mathbf{y}}) = - \sum_{j=1}^q y_j \log \hat{y}_j $$ $\mathbf{y}$ 是一个独热编码的向量 $\hat{\mathbf{y}}$ 是经过 softmax 计算的各分量和为一的预测向量 对于所有的 $y_j$，只有一个值为 $1$，其余皆为 $0$. 交叉熵损失函数的实现 # y_hat: [vector] -> matrix # y: [scalar, represent index] -> vector # the outer dimension represent sample count def cross_entropy(y_hat, y): # 这里的 range(len(y_hat)) 的意思是对于每一个 sample 都做计算，是一个 index 集合 return - torch.log(y_hat[range(len(y_hat)), y]) y_hat = torch.tensor( [[0.2, 0.7, 0.1], [0.2, 0.7, 0.1], [0.4, 0.6, 0]]) y = [1, 1, 1] cross_entropy(y_hat, y) Out: tensor([0.3567, 0.3567, 0.5108]) 由于是批量计算，所以这里 y_hat 是矩阵，y 是向量，返回值也是一个向量。 Softmax 带来的问题 https://zh-v2.d2l.ai/chapter_linear-networks/softmax-regression-concise.html#subsec-softmax-implementation-revisited 简而言之，就是 softmax 既有可能带来指数所造成的数值过大的问题，也有可能带来除法所造成的精度丧失的问题。 为了解决第一个问题，我们可以尝试通过在计算 softmax 之前从每个参数 $o_k$ 中减去 $\max(o_k)$ : $$ \begin{aligned} \hat y_j & = \frac{\exp(o_j - \max(o_k))\exp(\max(o_k))}{\sum_k \exp(o_k - \max(o_k))\exp(\max(o_k…