【深度学习模型1】慢吞吞地理解CNN……

此处附上两个实践代码。

1. b站刘二大人第10讲。

'''
Convolutional NN 卷积神经网络
1. 在Feature Extraction 特征提取阶段,是直接对图像做卷积运算、转换成向量的;
   然后再用Full Connected NN 全连接网络进行Classification 分类。
'''

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim

# prepare dataset

batch_size=64
transform=transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,),(0.3081,))]) # ToTensor():把灰度范围从0-255变换到0-1之间; transform.Normalize():把0-1变换到(-1,1。

train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
# train=: 如果为True,则从training.pt创建数据集否则来自test.pt。 download: 如果为True,从互联网下载数据集,然后将其放在根目录中;如果数据集已经下载,则不是再次下载。

train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)

# design model using class

class Net(torch.nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        # 两个卷积层,通道1 -> 10 -> 20 ; 每一个卷积核的通道数量 == 输入通道的数量。这种卷积核的总数 == 输出通道的数量。
        self.conv1=torch.nn.Conv2d(1,10,kernel_size=5)  # “1”:输入的Channel,灰色图像是1维的;“10”:输出的Channel,即,第一个卷积层需要10个卷积核;kernel_size=5:卷积核大小是5*5;stride=:进行卷积运算时的步长,默认为1;bias=:卷积运算是否需要偏置bias,默认为False;padding=:卷积操作是否补0。
        self.conv2=torch.nn.Conv2d(10,20,kernel_size=5)
        self.pooling=torch.nn.MaxPool2d(2)  # 池化层。
        self.fc=torch.nn.Linear(320,10)  # 全连接的线性层。 “320”的获取方式:x = x.view(batch_size, -1) print(x.shape) 可得到 (64,320) ;“64”:batch,“320”:进行全连接操作时,输入的特征维度。

    def forward(self,x):
        batch_size=x.size(0)
        x=F.relu(self.pooling(self.conv1(x)))  # 做卷积 -> 池化 -> relu。
        x=F.relu(self.pooling(self.conv2(x)))
        x=x.view(batch_size,-1)  # 先做 view 是为了变成FCNN所需要的输入。此处输出320。
        x=self.fc(x)

        return x

model=Net()

# construct loss and optimizer

criterion=torch.nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameeters(),lr=0.01,momentum=0.5)

# training cycle forward\backward\update

def train(epoch):
    running_loss=0.0
    for batch_idx,data in enumerate(train_loader,0):
        inputs,target=data
        optimizer.zero_grad()

        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/300))
            running_loss = 0.0

def test():
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print('accuracy on test set: %d %% ' % (100*correct/total))
 
 
if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test()

2. b站刘二大人第11讲。

'''
1. 减少代码冗余:构造函数/类;

2. CNN中常见的是卷积层convolution 池化层pooling 激活函数relu 三者的叠加使用;
    *卷积层:在保留数据信息的同时减少空间;重要的两个性质是 “局部连接” 和 “参数共享” 。
        **局部连接:如用一个3*3的窗口对一张9*9的图进行连接操作,窗口之外未连接的部分通过滑动窗口的方法来连接;
        ————这个方法就称作参数(filter)共享:用滑动窗口的方式将 filter 的值共享给原图的每一块区域进行卷积运算。
    *池化层:常见的方法有average pooling、max pooling;
    *激活函数relu:max(0,x)只要>=0的数,<0直接改写成0。

卷积(线性变换) 激活函数(非线性变换) 池化 这个过程若干次后,view打平,进入全连接层
👇

3. 全联接层Fully-Connected:对特征图进行纬度上面的改变;要求 窗口&原图 两者坐标一一对应;
    *softmax函数:分类函数,输出每个类别对应的概率值;如三个类别[0.5,0.4,0.1]那么结果肯定是第一类。

4. 卷积层要求输入输出是四维张量(Batch, Channel, Width, Height),全连接层的输入与输出都是二维张量(Batch, Input_feature)。

5. 卷积(convolution)后,C(Channels)变,W(width)和H(Height)可变可不变,取决于是否padding。
   subsampling(或pooling)后,C不变,W和H变。
'''

import torch
import torch.nn as nn
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim

# prepare dataset
batch_size =64 # 一次训练所选择的样本数。
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) # 归一化,均值和方差
 
train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)

# design model using class
class InceptionA(nn.Module):  # Inception 有4个分支,将其抽象成一个类。
    def __init__(self,in_channels):  # 定义写在 init 里,调用写在 forward 里。
        super(InceptionA, self).__init__()
        self.branch_pool=nn.Conv2d(in_channels,24,kernel_size=1)  # 池化卷积:“24”是 输出的通道数。

        self.branch1x1=nn.Conv2d(in_channels,16,kernel_size=1) # kernel_size: 卷积核大小,此处意味卷积大小为1*1

        self.branch5x5_1=nn.Conv2d(in_channels,16,kernel_size=1)
        self.branch5x5_2=nn.Conv2d(16,24,kernel_size=5,padding=2) # 因为 padding!=0 所以卷积后 w 和 h 都发生了变化。

        self.branch3x3_1=nn.Conv2d(in_channels,16,kernel_size=1)
        self.branch3x3_2=nn.Conv2d(16,24,kernel_size=3,padding=1) # 且上下左右都要填充,如 padding=1 时,32*32 --> 34*34 。
        self.branch3x3_3=nn.Conv2d(24,24,kernel_size=3,padding=1)

    def forward(self,x):
        branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1) # 池化层。
        branch_pool=self.branch_pool(branch_pool)  # 卷积层。

        branch1x1 = self.branch1x1(x)
 
        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)
 
        branch3x3 = self.branch3x3_1(x)
        branch3x3 = self.branch3x3_2(branch3x3)
        branch3x3 = self.branch3x3_3(branch3x3)

        # “concatenate” 拼接运算:先放进一个数组里;再调用 cat 沿着 dim=1 的维度进行concatenate。
        outputs=[branch_pool,branch1x1,branch5x5,branch3x3]
        return torch.cat(outputs,dim=1)

class Net(nn.Module):  # 构造网络
    def __init__(self):
        super(Net,self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(88, 20, kernel_size=5) # “88” 是Inception后的输出通道数; 88 = 24x3 + 16
 
        self.incep1 = InceptionA(in_channels=10) # 初始化,将 input_channels 设为10,与 conv1 中的10对应。
        self.incep2 = InceptionA(in_channels=20) # 初始化,与 conv2 中的20对应。
 
        self.mp = nn.MaxPool2d(2)
        self.fc = nn.Linear(1408, 10)  # inception输出的channel一律为88,且不改变图像的w和h(只有卷积池化会改变)。所以是 88*4*4=1408 。

    def forward(self,x):
        in_size=x.size(0)
        x=F.relu(self.mp(self.conv1(x)))  # 卷积 池化 激活 三件套。这步之后,output_channels=10 。
        x=self.incep1(x)  # 这步之后,output_channels=88 。
        x=F.relu(self.mp(self.conv2(x)))
        x=self.incep2(x)
        x=x.view(in_size,-1)  # view是为了FCNN做准备。
        x=self.fc(x)

        return x

model=Net()

# construct loss and optimizer
criterion=torch.nn.CrossEntropyLoss()
optimizer=optim.SGD(model.parameters(),lr=0.01,momentum=0.5)

#training cycle
def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        optimizer.zero_grad()
 
        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/300))
            running_loss = 0.0
 
 
def test():
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print('accuracy on test set: %d %% ' % (100*correct/total))
 
 
if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值