Pytorch:十、卷积神经网络(基础)


  • 全连接网络:网络中所使用的全都是线性层,并且串行器来,就跟上一章的那个模型一样的样子:

线性层中输入\出层的任意两个节点之间都有权重,即所有输入节点都要参与到下一层输出的计算中去。我们就把这样的线性层叫做全连接层full connected(FC)

但是做成全连接层也有一些不足:

以上图为例,将一个数字转换为1维的长串时,可能会丢失一些空间信息,即在长串中隔得很远的两个点可能在原来的数字图像中是相邻的。这说明:做成全连接之后会丧失一些原有的空间信息;

相应的,CNN则是把图像按照原始空间结构进行保存,从而保留住了许多空间信息。

卷积核进行的卷积计算

卷积计算:就是像上面这样,通过卷积核来对原来的图像进行压缩;

CNN流程图

不难看出,在进行下采样时,通道数不变,但是宽度和高度则是按照采样规格进行缩减;
下采样:为了减少数据量,从而降低运算需求;
最终将n1的输出映射到10维的n2去,这样就可以接上 交叉熵损失 从而计算其分布,这样就可以解决分类问题了;

通过上面的图,可以得出一套工作流程:
通过这些层,来进行维度/尺寸上的大小变化,最终将它映射到想要的输出空间中去;

特征提取器:也就是卷积,下采样等,表示通过卷积运算来找到某种特征(此阶段直接对图像进行卷积运算从而转为向量);
分类器,就是把经过特征提取后的向量放到全连接层中去做分类;

可以通过扩展来提升像素值:

将原来2* 2的图像扩展为一个3* 3的,空出来的部分用平均值来进行填充即可

注意,经过卷积操作后的通道数量也是可能发生变化的,因为原来有三个通道,现在卷积核有5个通道,这五个通道把你原来三个通道都遍历一遍(多少个卷积核就会得到不同的特征图),卷积核个数是下一层通道数

卷积的图示

将上面的过程整合一下就是:

不难看出,无论中间那个卷积核是多少层的,最后输出的结果一定是一个单通道的结果,如果想要一个多通道的结果就需要很多个卷积层,如下图所示;

这里的cat是指拼接的意思;

从这张图我们不难发现:

  1. 每个卷积核的通道数量要求和输入通道一样;
  2. 最终输出的通道数就是卷积核的数量;

如此一来就可以推出以下结论:

看看代码的计算过程:

import torch
#分别是n和m
in_channels, out_channels = 5,10
#这里的宽和高是图像的大小
w,h = 100, 100
#kernel_size表示卷积核的数量,输入一个3就是3*3,也可以直接给个数组
kernel_size = 3
#这里是指图片的数量
batch_size = 1

#生成一个输入图像
input = torch.rand(batch_size
                 ,in_channels #这个最需要关注,一定要和卷积的in一样
                 ,w
                 ,h
                 )

#Conv2d:对由多个输入平面组成的输入信号进行二维卷积
conv_layer = torch.nn.Conv2d(in_channels
                            ,out_channels
                            ,kernel_size = kernel_size
                            )

#用上面的那个卷积层来对input进行卷积并生成output
output = conv_layer(input)

print(input.shape)
#torch.Size([1, 5, 100, 100])
print(output.shape)
#torch.Size([1, 10, 98, 98])
print(conv_layer.weight.shape) #卷积层权重的形状
#torch.Size([10, 5, 3, 3]) 

卷积层中其他常见的参数:

  • padding:通过这个参数来改变输出矩阵的规格;

在外围加了一圈格子,从而使其在经过卷积核后的结果为5* 5 (7-3+1=5)的,一般都是按实际需求选择是否填充。填充的圈数就是卷积核的大小/2后取整的值

最常见的padding就是填充0,如下图所示:

而这一步也可以通过代码实现

import torch

#原来的矩阵
input = [3,4,5,6,7
        ,2,4,6,8,2
        ,1,6,7,8,4
        ,9,7,6,4,2
        ,3,7,5,4,1]
#按要求进行转换,view的四个数字分别是:batch, channel, w, h
input = torch.Tensor(input).view(1, 1, 5, 5)

#bias是加偏执量的参数,这里不需要加
conv_layer = torch.nn.Conv2d(1, 1, kernel_size = 3, padding=1, bias=False)

#构造卷积核
kernel = torch.Tensor([1,2,3,4,5,6,7,8,9]).view(1, 1, 3, 3)
#通过赋值来进行卷积层权重的初始化
conv_layer.weight.data = kernel.data

output = conv_layer(input)
print(output)
  • stride:步长,表示每次卷积之后框移动的距离;
import torch

input = [3,4,5,6,7
        ,2,4,6,8,2
        ,1,6,7,8,4
        ,9,7,6,4,2
        ,3,7,5,4,1]
input = torch.Tensor(input).view(1, 1, 5, 5)

conv_layer = torch.nn.Conv2d(1, 1, kernel_size = 3, stride=2, bias=False)

kernel = torch.Tensor([1,2,3,4,5,6,7,8,9]).view(1, 1, 3, 3)
conv_layer.weight.data = kernel.data

output = conv_layer(input)
print(output)
#tensor([[[[208., 263.],
#          [263., 167.]]]], grad_fn=<ConvolutionBackward0>)

下采样方法介绍:

  1. MaxPooling:按宽和高进行分组,然后在每组内找到最大值来作为那一组的代表;


默认stride=2。这里只能在同一个通道内进行MaxPooling,不能在不同通道之间进行;

import torch

input = [3,4,5,6
        ,2,4,6,8
        ,1,6,7,8
        ,9,7,6,4]
input = torch.Tensor(input).view(1,1,4,4)

#当kernel_size设为2时,默认步长也会变成2
maxpooling_layer = torch.nn.MaxPool2d(kernel_size=2)

output = maxpooling_layer(input)
print(output)

接下来做一个简单的CNN:

第一个卷积层:5* 5的卷积核,输入通道为1,输出通道为5;
第一个池化层:batch和10都不变,宽和高的24都减小一半;
以此类推。。。
全连接层:把结果映射成10个输出向量来作为预测值;

最后的那个分类器最在乎每个样本的元素个数,因此要看看前面的输出有多少个。但是在实操时可以先不写最后这个分类器--,定义到最后一个batch就停,然后看看它的输出是咋样的,然后根据输出来编写全连接层即可

CNN的网络架构图

在放进FC层之前要进行一次view,也就是把2044变成320再丢给FC;

import torch.nn.functional as F

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(10,20,kernel_size=5)
        self.pooling = torch.nn.MaxPool2d(2)
        self.fc = torch.nn.Linear(320,10)
        
    def forward(self, x):
        batch_size = x.size(0)
        x = self.pooling(F.relu(self.conv1(x)))
        x = self.pooling(F.relu(self.conv2(x)))
        x = x.view(batch_size, -1)
        x = self.fc(x)
        return x
    
model = Net()
#用显卡来算,就是把模型迁移到GPU上去
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#通过这句话改跑模型的设备
model.to(device)

def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        #把他俩都送去device上,一定要放到同一块显卡上
        inputs, target = inputs.to(device), target.to(device)
        optimizer.zero_grad()
        # forward + backward + update
        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if batch_idx % 300 == 299:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 2000))
            running_loss = 0.0
            
def test():
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            inputs, target = data
            #测试这里也要放到显卡上
            inputs, target = inputs.to(device), target.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, dim=1)
            total += target.size(0)
            correct += (predicted == target).sum().item()
        print('Accuracy on test set: %d %% [%d/%d]' % (100 * correct / total, correct, total))
用了GPU之后的结果,没给数据我们自己不好跑
  • 作业:
要求在右边,具体参数自己设置就行
class Net1(torch.nn.Module):
    def __init__(self):
        super(Net1, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=4)#10,13,13
        self.conv2 = torch.nn.Conv2d(10,20,kernel_size=4) #20,6,6
        self.conv3 = torch.nn.Conv2d(20,40,kernel_size=4) #40,2,2
        self.pooling = torch.nn.MaxPool2d(2)
        self.fc1 = torch.nn.Linear(160,80)
        self.fc2 = torch.nn.Linear(80,40)
        self.fc3 = torch.nn.Linear(40,10)
        
    def forward(self, x):
        batch_size = x.size(0)
        x = self.pooling(F.relu(self.conv1(x)))
        x = self.pooling(F.relu(self.conv2(x)))
        x = self.pooling(F.relu(self.conv3(x)))
        x = x.view(batch_size, -1)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x

不知道对不对,应该是这样?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容