分类 PyTorch 下的文章

信息量、信息熵、交叉熵是非常重要的数学概念。它们非常重要,相关书籍和资料也很多,不过都不够友好——世上的事情总是如此,你尚不理解的,对于你而言太难;而你已然理解的,对你而言又太过简单。

所以很难有适合所有人的学习资料。这是我以程序员的视角,向自己介绍这几个相关概念。

这篇笔记题目起得太大,不是面向程序员的直观解释,而是面向我这个程序员的直观解释。

信息量

考虑一个抛硬币的游戏:抛出一个硬币,问表示这个事件发生的结果,最多需要几个比特?显然,一个比特位就够了(\(2^1=2\)),比如规定:

  • 1: 代表正面
  • 0: 代表反面

让我们整理一下这里的术语:

  • 随机事件:表示一次抛硬币的事件,要么正面朝上,要么反面朝上,只能是其中之一。
  • 随机变量:表示一个变量,其值是各个随机事件。对于抛硬币来说,可能是正面朝上,也可能是反面朝上。
  • 编码:用数字来对随机事件进行唯一编号。
  • 比特:计算机术语,一个存储位,可以表示两种情况。可以用灯来比喻。

我们重新描述一下上面的问题:

用随机变量\(X\)表示一次抛硬币的结果。我们可以用1来编码正面朝上这个随机事件,用0来编码反面朝上这个事件。需要分配1个比特位(一盏灯)就足够了。
如果硬币被人做了手脚,必然正面朝上,那么对于这种必然事件,我们连一个比特位都不需要分配,即需要0个比特。
同理,如果硬币被人做了手脚,必然反面朝上,我们也不需要分配任何比特位,即需要0个比特。。

如果你的数学直觉足够好,你可能会意识到,要编码上面丢硬币的结果,需要的比特数和随机事件发生的概率有关:

  • 当硬币是正面朝上和反面朝上的概率均是50%时,我们需要1个比特;
  • 而当正面朝上是100%的概率时,我们需要0个比特;
  • 而当反面朝上是100%的概率时,我们也需要0个比特;
  • 如果我们把比特从整数扩展到实数,当正面朝上和反面朝上的概率取其它值时,需要几个比特来编码结果?从直觉上,我们可以猜测,需要的比特数应该介于\((0,1)\)之间。

甚至,我们可以猜测:

  • 当概率构成是(0.5, 0.5)时,我们所需要的1个比特有一半分给了编码正面朝上、另一半分给了反面朝上。
  • 而当概率构成是(1.0, 0.0)时,正面朝上是必然事件,无需比特进行编码;反面朝下也是必然事件,也无需比特进行编码。
  • 而当概率构成是(0.0, 1.0)时,正面朝下是必然事件,无需比特进行编码;反面朝上也是必然事件,也无需比特进行编码。
  • 当概率构成是(p, 1-p)时,这个需要的比特量里有一部分被分给了对正面朝上编码,另一部分属于反面朝上进行编码。至于这个构成是多少,我们留待下面进行更多的探究。

再考虑需要的比特量稍大一点的情况。已知有一个随机整数,取值范围是\([1,16]\)。那么表示这个数到底是多少,需要几个比特?显然,\(2^4=16\),也就是需要4个比特。这个问题也可以换个角度观察:由于有4个比特位,如果逐一确定这里的四个比特位分别是多少,我们共需要测试四次。

阅读剩余部分

如何绘制PR曲线?

基本思想

二元预测函数的输出是一个得分。从预测得分到判定是否属于某类,还需要结合阈值来完成。比如大于某个阈值,就认为是某个类。调节阈值,会影响预测的结果类别,最终会影响精准率和召回率。在直觉上,精准率和召回率在一定程度上会呈现负相关关系——漏杀低了,容易过杀;过杀低了,又容易漏杀。我们想把这个关系量化表示,一个简单办法就是绘制P-R曲线。

示例

假设我们有一个二元分类问题,我们对每一行样本都进行了预测,并给出了预测得分:

序号 真实值 预测分数
1    0    0.1
2    1    0.4
3    1    0.35
4    0    0.8
5    1    0.9
6    0    0.2
7    1    0.5
8    0    0.3
9    0    0.6
10    1    0.85

既然要调节阈值来观测输出,不妨把上面各行先按预测得分来排列:

阅读剩余部分

OpenCV图像读取后,默认是预处理为HWC形状,而在PyTorch中,希望的是CHW形状; 另一方面,在通道顺序上,OpenCV默认是BGR的顺序,而在PyTorch的张量处理环节,我们一般希望的是RGB顺序。这里就涉及到了数据处理和转换。

OpenCV图像到PyTorch张量的转换

我们先模拟一个高度为2,宽度为3的彩色图像:

from torch import tensor

a = torch.randint(low=0, high=255, size=(2, 3, 3))

这里的a似于:

tensor([[[ 58, 223,  24], [ 35, 116, 249], [ 81, 220, 168]],
        [[242, 169, 149], [149,  87, 167], [165,  66,  88]]])

如果这个数据是从OpenCV读取过来,那么最内层通道顺序是BGR

为了得到RGB,我们可以将最内侧倒排一下:

b = a[:, :, [2,1,0]]

结果类似于:

tensor([[[ 24, 223,  58], [249, 116,  35], [168, 220,  81]],
        [[149, 169, 242], [167,  87, 149], [ 88,  66, 165]]])

但问题是,上面的张量形状是 (H,W,C),而在PyTorch中,我们期望的是(C,H,W)。现在我们将它拆分重排成RGB三个通道,其中每个通道都是一个2*3灰度图:

c = b.permute(2,0,1 )
print(c)

结果类似于:

tensor([
        # R通道
        [[ 24, 249, 168],
         [149, 167,  88]],         
        # G通道
        [[223, 116, 220],
         [169,  87,  66]],
        # B通道
        [[ 58,  35,  81],
         [242, 149, 165]]])

一个数据集加载示例

在《Modern Computer Vision》中,讲解了使用VGG16进行迁移学习,然后用于猫狗分类的示例。一方面VGG16预训练模型是在ImageNet数据集上训练得来的,其中所有图像都被缩放为 224 × 224 作为输入。为了充分利用预训练的权重,我们需要把训练的输入图像调整为相同大小。

阅读剩余部分