Featured image of post 特征工程学习笔记

特征工程学习笔记

数据挖掘中回归分析的学习笔记

# 卷积神经网络(CNN)

# CNN的基本架构

一个典型的卷积网络是由卷积层、 汇聚层、 全连接层交叉堆叠而成。

常用的卷积网络整体结构

一个卷积块为连续 𝑀 个卷积层和 𝑏 个汇聚层( 𝑀 通常设置为2 ∼ 5,𝑏为0或1)。 一个卷积网络中可以堆叠𝑁 个连续的卷积块, 然后在后面接着 𝐾 个全连接层( 𝑁 的取值区间比较大, 比如 1 ∼ 100 或者更大;𝐾 一般为0 ∼ 2)。

上面这张图里的汇聚层也就是下面图中的池化层

图形识别的CNN模型

  • 输入层。在上图中就是最左边的船的图像,计算机理解为输入若干个矩阵。
  • 卷积层(Convolution Layer)。卷积层的激活函数使用的是ReLU,$ReLU(x) = max(0,x)$。
  • 池化层(Pooling layer)。池化层没有激活函数。

卷积层+池化层的组合可以在隐藏层出现很多次,上图中出现两次。这里卷积层+池化层可随意组合,如卷积层+卷积层,或者卷积层+卷积层+池化层。不过我们一般都是若干卷积层+池化层的组合。

  • 全连接层(Fully Connected Layer, 简称FC),输出层使用了Softmax激活函数来做图像识别的分

为了统一起见,我们下面对于Pooling layer的中文翻译都为汇聚层

# 卷积

对于一维卷积,我们假设滤波器长度为$K$, 它和一个信号序列$x_1,x_2,…$的卷积为:

$$ y_t=\sum_{k=1}^{K}w_kx_{t-k+1} $$

为了简单起见, 这里假设卷积的输出$y_t$ 的下标$t$从$k$开始

信号序列$x$和滤波器$w$(也叫卷积核)的卷积定义为:

$$ y=w*x $$

其中$*$表示卷积运算。

对于二维矩阵,给定图像$X(M\times N)$,滤波器$W(U \times V)$我们定义:

$$ y_{ij}=\sum_{u=1}^{U}\sum_{v=1}^{V} w_{uv}x_{i-u+1,j-v+1} $$

光看公式还是比较抽象的,还是要举个例子:

图中的输入是一个二维的3x4的矩阵,而卷积核是一个2x2的矩阵。这里我们假设卷积是一次移动一个像素来卷积的,那么首先我们对输入的左上角2x2局部和卷积核卷积,即各个位置的元素相乘再相加,得到的输出矩阵S的$S_{00}$的元素,值为$aw+bx+ey+fz$。接着我们将输入的局部向右平移一个像素,现在是(b,c,f,g)四个元素构成的矩阵和卷积核来卷积,这样我们得到了输出矩阵S的$S_{01}$的元素,同样的方法,我们可以得到输出矩阵S的$S_{02},S_{10},S_{11}, S_{12}$的元素。

二维卷积的运算

最终我们得到卷积输出的矩阵为一个2x3的矩阵S。

如果这个例子还是不直观,可以看下面这个动图:

输入 滤波器 计算过程(点开才能动)

可是,考试时可不仅仅会考二维的矩阵,还会考三维矩阵。

在介绍三维矩阵之前,我们先引入两个概念:

  • 步长(Stride):指卷积核在滑动时的时间间隔。说人话就是矩阵每次右移的长度,比如下面要说的例子中步长为2就右移2个像素。
  • 零填充(Zero Padding):是在输入向量两端进行补零。比如下面要说的例子pad=1,就在矩阵周围填一圈0

在斯坦福大学的cs231n的课程上,有一个动态的例子,我们就看这个例子。

Convolution demo

可以看出来,对于三维的矩阵,我们是将其拆成3个二维的矩阵,然后对应的将输入$X$和卷积核$W$进行卷积运算。

# 汇聚层

其作用是进行特征选择,降低特征数量, 从而减少参数数量。说人话就是输入张量的各个子矩阵进行压缩。假如是2x2的汇聚,那么就将子矩阵的每2x2个元素变成一个元素,如果是3x3的汇聚,那么就将子矩阵的每3x3个元素变成一个元素,这样输入矩阵的维度就变小了。

要想将输入子矩阵的每nxn个元素变成一个元素,那么需要一个汇聚标准。常见的汇聚标准有2个,MAX或者是MEAN。即取对应区域的最大值或者平均值作为汇聚后的元素值。

下面这个例子采用取最大值的汇聚方法。同时采用的是2x2的汇聚。步幅为2。

最大化汇聚实例

首先对红色2x2区域进行汇聚,由于此2x2区域的最大值为6。那么对应的汇聚输出位置的值为6,由于步幅为2,此时移动到绿色的位置去进行汇聚,输出的最大值为8。同样的方法,可以得到黄色区域和蓝色区域的输出值。最终,我们的输入4x4的矩阵在汇聚后变成了2x2的矩阵,进行了压缩。

# 例题

有了上面这些内容的铺垫,我们就会惊喜的发现,下面这道题可以轻松的完成了

去年的例题

# 循环神经网络(RNN)

在前面讲到的DNN和CNN中,训练样本的输入和输出是比较的确定的。但是有一类问题DNN和CNN不好解决,就是训练样本输入是连续的序列,且序列的长短不一,比如基于时间的序列:一段段连续的语音,一段段连续的手写文字。这些序列比较长,且长度不一,比较难直接的拆分成一个个独立的样本来通过DNN/CNN进行训练。

这时就需要RNN了。RNN是一类具有短期记忆能力的神经网络,RNN的模型如下:

循环神经网络

# 单一神经元的RNN

结构图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class SingleLayerRNN(nn.Module):
    def __init__(self, inputSize, hiddenSize):
        super(SingleLayerRNN, self).__init__()
        # inputSize x hiddenSize : 4 x 1
        self.Wx = torch.randn(inputSize, hiddenSize)
        # hiddenSize x hiddenSize : 1 x 1
        self.Wy = torch.randn(hiddenSize, hiddenSize)
        self.b = torch.zeros(1, hiddenSize)  # 1 xhiddenSize : 1 x 4

    def forward(self, X0, X1):
        # batchSize x hiddenSize : 1 x 4
        self.Y0 = torch.tanh(torch.mm(X0, self.Wx) + self.b)
        # batchSize x hiddenSize : 1 X 4
        self.Y1 = torch.tanh(torch.mm(X1, self.Wx) +
                             self.b + torch.mm(self.Y0, self.Wy))
        return self.Y0, self.Y1

INPUT_SIZE = 4
HIDDEN_SIZE = 1
# t=0 => batchSize x inputSize : 4 x 4
X0_batch = torch.tensor([[0, 1, 2, 0], [3, 4, 5, 0], [6, 7, 8, 0], 
                        [9, 0, 1, 0]], dtype=torch.float)  
# t=1 => batchSize x inputSize : 4 x 4
X1_batch = torch.tensor([[9, 8, 7, 0], [0, 0, 0, 0], [6, 5, 4, 0], 
                        [3, 2, 1, 0]], dtype=torch.float)  
model = SingleLayerRNN(INPUT_SIZE, HIDDEN_SIZE)
Y0_batch, Y1_batch = model(X0_batch, X1_batch)

print(Y0_batch)
print(Y1_batch)

# 使用RNN进行MNIST训练

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim
import os

os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
BATCH_SIZE = 64
DOWNLOAD_DATASET = False

# list all transformations
transform = transforms.Compose([
    transforms.Resize((28, 28)),
    transforms.ToTensor(),
])

# download and load training dataset
trainDataset = torchvision.datasets.MNIST(root='./MNIST/', train=True, download=DOWNLOAD_DATASET, transform=transform)
trainLoader = torch.utils.data.DataLoader(trainDataset, batch_size=BATCH_SIZE, shuffle=True)

# download and load testing dataset
testDataset = torchvision.datasets.MNIST(root='./MNIST/', train=False, download=DOWNLOAD_DATASET, transform=transform)
testLoader = torch.utils.data.DataLoader(testDataset, batch_size=BATCH_SIZE, shuffle=False)

# functions to show an image
def imshow(img):
    # img = img / 2 + 0.5 # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))

# get some random training images
dataIter = iter(trainLoader)
images, labels = dataIter.next()

# show images
imshow(torchvision.utils.make_grid(images))

# parameters
INPUT_SIZE = 28
HIDDEN_SIZE = 150
OUTPUT_SIZE = 10
TIME_STEP = 28
MAX_EPOCH = 10

class ImageRNN(nn.Module):
    def __init__(self, batchSize, timeStep, inputSize, hiddenSize, outputSize):
        super(ImageRNN, self).__init__()

        self.hiddenSize = hiddenSize
        self.batchSize = batchSize
        self.timeStep = timeStep
        self.inputSize = inputSize
        self.outputSize = outputSize

        self.RNN = nn.RNN(self.inputSize, self.hiddenSize)

        self.fc = nn.Linear(self.hiddenSize, self.outputSize)

    def initializeHidden(self, ):
        # (num_layers, batch_size, hidden_size)
        return torch.zeros(1, self.batchSize, self.hiddenSize)

    def forward(self, X):
        # transforms X to dimensions: timeStep x batchSize x inputSize
        X = X.permute(1, 0, 2)

        self.batchSize = X.size(1)
        self.hidden = self.initializeHidden()

        # rnnOutput => timeStep, batchSize, hiddenSize (hidden states for each time step)
        # self.hidden => 1, batchSize, hiddenSize (final state from each rnnOutput)
        rnnOutput, self.hidden = self.RNN(X, self.hidden)
        out = self.fc(self.hidden)

        return out.view(-1, self.outputSize)  # batchSize x outputSize

dataIter = iter(trainLoader)
images, labels = dataIter.next()
model = ImageRNN(BATCH_SIZE, TIME_STEP, INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE)
ypred = model(images.view(-1, 28, 28))
print(ypred[0:10])

# Device
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# Model instance
model = ImageRNN(BATCH_SIZE, TIME_STEP, INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
def computeAccuracy(output, target):
    ''' Obtain accuracy for training round '''
    corrects = (torch.max(output, 1)[1].view(target.size()).data == target.data).sum()
    accuracy = 100.0 * corrects / torch.numel(target)
    return accuracy.item()
for epoch in range(MAX_EPOCH):  # loop over the dataset multiple times
    trainLoss = 0.0
    trainAccuracy = 0.0
    model.train()
    # TRAINING ROUND
    for i, data in enumerate(trainLoader):
        # reset hidden states
        model.hidden = model.initializeHidden()
        # get the inputs
        inputs, labels = data
        inputs = inputs.view(-1, 28, 28)
        # forward
        ypred = model(inputs)
        loss = criterion(ypred, labels)
        # backward + optimize
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        trainLoss += loss.detach().item()
        trainAccuracy += computeAccuracy(ypred, labels)
    model.eval()
    print('Epoch: {:2d} | Loss: {:8.4f} | Train Accuracy: {:5.2f}'.format(epoch, trainLoss / i, trainAccuracy / i))
model.eval()
testAccuracy = 0.0
for i, (images, labels) in enumerate(testLoader, 0):
    images = images.to(device)
    labels = labels.to(device)
    outputs = model(images.view(-1, 28, 28))
    testAccuracy += computeAccuracy(outputs, labels)
print('Test Accuracy: {:6.2f}'.format(testAccuracy / len(testLoader)))

模型构建

测试代码

# 参考资料