卷积神经网络(PyTorch)

卷积

卷积在pytorch中有两种方式,一种是torch.nn.Conv2d(),一种是torch.nn.functional.conv2d()

这两种形式的卷积对于输入的要求是一样的,首先需要输入一个torch.autograd.Variable()的类型,大小是(batch,channel,H,W),其中batch是输入的一批数据的数目,第二个是通道数,一般彩色图是3,灰度图是1,而卷积网络过程中的通道数比较大,会出现几十到几百的通道数,H和w是输入图片的高度和宽度,比如一个batch是32的图片,每张图片是3通道,高和宽是50和100,那么输入的大小就是(32,3,50,100)。

PyTorch中有函数torch.nn.Conv2d来实现卷积。

torch.nn.Conv2d(
    in_channels,    #输入的通道数
    out_channels,   #输出的通道数
    kernel_size,    #卷积核大小
    stride=1,       #卷积核移动步长
    padding=0,      #补0的多少
    dilation=1,     #kernel间距
    groups=1,       #卷积核个数
    bias=True    
)

例如:

定义一个能够检测边缘的卷积核

#使用nn.Conv2d
conv1 = nn.Conv2d(1,1,3,bias=False) #定义卷积
sobel_kernel = np.array([-1,-1,-1],[-1,8,-1],[-1,-1,-1],dtype='float32') #定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1,1,3,3)) #重新设置成卷积网络需要的格式,输入输出1,长宽3
conv1.weight.data = torch.from_numpy(sobel_kernel)  #给卷积核赋值

#将检测边缘的kernel作用到图片上
edge = conv(Variable(img))  #注意图片转化Variabl

举例两种卷积方式:

import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline
im = Image.open('./cat.png').convert('L') #读入一张灰度图的图片
im = np.array(im,dtype='float32')  #将其转换为一个矩阵
#可视化图片
plt.imshow(im.astype('uint8'),cmap='gray')
#将图片转化为pyorch tensor,并适配卷积输入的要求
im = torch.from_numpy(im.reshape((1,1,im.shape[0],im.shape[1])))

下面定义一个算子对其进行轮廓检测

#使用nn.Conv2d
conv1 = n.Conv2d(1,1,3,bias=False) #定义卷积
sobel_kernel = np.array([-1,-1,-1],[-1,8,-1],[-1,-1,-1],dtype='float32') #定义轮廓检测算子
#定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1,1,3,3)) #重新设置成卷积网络需要的格式,输入输出1,长宽3
conv1.weight.data = torch.from_numpy(sobel_kernel)  #给卷积核赋值

edge1 = conv1(Variable(im))  #作用在图片上
edge1 = edge1.data.squeeze().numpy()  #将输出转换为图片的格式

#可视化
plt.imshow(edge1,cmap='gray')
#使用F.conv2d
sobel_kernel = np.array([-1,-1,-1],[-1,8,-1],[-1,-1,-1],dtype='float32') #定义轮廓检测算子
#定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1,1,3,3)) #重新设置成卷积网络需要的格式,输入输出1,长宽3
weight = Variable(torch.from_numpy(sobel_kernel))  #给卷积核赋值

edge2 = F.conv2d(Variable(im),weight) #作用在图片上
edge2 = edge2.data.squeeze().numpy()  #将输出转化为图片的格式
plt.imshow(edge2,cmap='gray')

可以看出两种形式能达到相同的效果,不同的就是,使用nn.Conv2d()相当于直接定义了一层卷积网络结构,而使用torch.nn.functional.conv2d()相当于定义了一个卷积的操作,所以使用后者需要再额外去定义一个weight,而且这个weight也必须是一个Variable,而使用nn.Conv2d()则会帮我们默认定义一个随机初始化的weight,如果需要修改,那么取出其中的值对其修改,如果不想修改,可以直接使用这个默认初始化的值,非常方便。

池化

PyTorch中有函数torch.nn.MaxPool2d来实现池化。也可以用torch.nn.functional.max_pool2d().

torch.nn.max_pool(
    kernel_size,  #池化核大小
    stride=None,  #步长
    padding=0,    #补0
    dialtion=1,
return_indices=False,
    ceil_model=False 
)

例如:

x相当于下采样。

#使用nn.MaxPool2d
pool = nn.MaxPool2d(2,2)
small_img = pool(Variable(img))

举例:

#使用nn.MaxPool2d
pool1 = nn.MaxPool2d(2,2)
print('before max pool,image shape:{} x {}'.format(im.shape[2],im.shape[3]))
small_im1 = pool1(Variable(im))
small_im1 = small_im1.data.squeeze().numpy()

print('after max pool,image shape:{} x {}'.format(small_im1.shape[0]),small_im1.shape[1]))

图片缩小一半。

plt.imshow(small_im1,cmap='gray')

池化只会减少图片的尺寸,并不影响图片的内容。

#F.max_pool2d
print('before max pool,image shape:{} x {}'.format(im.shape[2],im.shape[3]))
small_im2 = F.max_pool2d(Variable(im),,2,2)
small_im2 = small_im2.data.squeeze().numpy()

print('after max pool,image shape:{} x {}'.format(small_im1.shape[0]),small_im1.shape[1]))
plt.imshow(small_im2,cmap='gray')

标准化

数据处理的常见方法:中心化和标准化。

中心化相当于修正数据的中心位置,实现方法非常简单,就是在每个特征维度上减去对应的均值,最后得到0均值的特征。

标准化就是在数据变成0均值之后,为了使得不同的特征维有相同的规模,可以除以标准差近似为一个标准正态分布,也可以依据最大值和最小值将其转化为-1~1之间。

Batch Normalization

p批标准化,就是对于每一层网络的输出,对其做一个归一化,使其服从标准的正态分布,这样后一层网络的输入也是一个标准的正态分布,所以能够比较好的进行训练,加快收敛速度。

卷积神经网络(PyTorch)_第1张图片

第一行和第二行计算出一个Batch中数据的均值和方差,接着使用第三个公式对Batch中的每个数据点做标准化,参数是为了计算稳定引入的一个小的常数,10-5,最后利用权重修正得到最后的输出结果。

训练技巧

数据增强

常用方法:

  • 对图片进行一定比例的缩放
  • 对图片进行随机位置截取
  • 对图片进行随机的水平和竖直翻转
  • 对图片进行随机角度旋转
  • 对图片进行亮度、对比度和颜色的随机变化
from torchvision import transfors as tfs
new_im = tfs.Resize((100,200))(im)  #图像缩放

random_im1 = tfs.RandomCrop(100)(im)  #随机截取或者CentreCrop中心截取

h_filp = tfs.RandomHorizontalFlip()(im)  #水平翻转,RandomVerticalFlip竖直翻转

rot_im = tfs.RandomRotation(45)(im)  #旋转角度

contrast_im = tfs.ColorJitter(contrast=1)(im) #提高对比度0~2,1表示原图,brightness表示亮度  hue=0.5调整色彩

组合增强:

im_aug = tfs.Compose([
        tfs.Resize(120),
        tfs.RandomHorizontalFlip(),
        tfs.RandomCrop(96),
        tfs.ColorJitter(brightness=0.5,contrast=0.5,hue=0.5)
])

学习率衰减

net = resnet(3,10)
optimizer = torch.optim.SGD(net.parameters(),lr=0.01,weight_decay=1e-4)
optimizer.param_groups[0]['lr'] = 1e-5

Dropout

过拟合。

 

torch.nn.Dropout(p=0.5,inplace=False)

正则化

optimizer = torch.optim.SGD(net.parameters(),lr=0.01,weight_decay=1e-4) #加入正则项weight_decay

数据读取

ImageFolder

torchvision.datasets.ImageFolder()是torchvision中内置的一个模块,专门处理分类问题。

数据集存放格式:

卷积神经网络(PyTorch)_第2张图片

几类就分成几个文件夹,同类别放同一个文件夹。

同时ImageFolder还支持传入数据预处理的方式:

from torchvision.datasets import ImageFolder

#三个文件夹,每个文件夹一共有3张图作为例子
folder_set = ImageFolder('./example_data/image/')

#查看名称和类别下标的对应
folder_set.class_to_idx

#得到所有的图片名字和标签
folder_set.imgs

卷积神经网络(PyTorch)_第3张图片

#取出其中一个数据
im,label = folder_set[0]
im
label
from torchvision import transforms as tfs

#传入数据预处理方式
data_tfs = tfs.ToTensor()
folder_set = ImageFolder('./example_data/image/',transform=data_tfs)
im,label = folder_set[0]
im

卷积神经网络(PyTorch)_第4张图片

Dataset

如果数据是txt不是图片,上面的方法就处理不了了。就需要用到torch.utils.data.Dataset().

其实torchvision.datasets.ImageFolder()torch.utils.data.Dataset()的一个子类。

如果我们希望定义自己的数据读入函数,只需要定义一个子类继承于Dataset,然后重新定义_getitem_()和_len_()这两个函数即可,_getitem_()表示按照下标取出其中一个数据,_len_()表示所有数据的总数。如:

from torch.utils.data import Dataset

#定义一个子类叫custom_dataset,继承Dataset
class custom_dataset(Dataset):
    def _init_(self,txt_path,transform=None):
        self.transform = transform  #传入数据预处理
        with open(txt_path,'r') as f:
            lines = f.readlines()
        self.img_list = [i.split()[0] for i in lines] #得到所有图片名字
        self.label_list = [i.split()[1] for i in lines] #label
    def _getitem_(self,idx): #根据idx取出其中一个
        img = self.img_list[idx]
        label = self.label_list[idx]
        if self.transform is not None:
            img = self.transform(img)
        return img,label
    def _len_(self):
        return len(self.label_list)
tet_dataset = custom_dataset('./example_data/train.txt') #读入txt文件

#取其中一个数据
data,lebel = txt_dataset[0]
print(data)
print(label)

DataLoader

PyTorch提供了一个python的多线程迭代器,能够帮助我们一个batch的读入模型,同时使用多线程速度更快。

DataLoader使用最多的参数:

  • dataset,就是前面定义的数据读入,可以使用ImageFolder,可以使用自己定义的数据读入子类。
  • batch_size,就是一批多少个数据
  • shuffle,是否在每个epoch开始的时候,对数据进行重新打乱
  • sampler,自定义从数据集中取样本的策略,如果指定这个参数,那么shuffle必须为False
  • drop_last,是否扔掉最后无法构成一个批次的数据。
from torch.utils.data import DataLoader
train_data1 = DataLoader(folder_set,batch_size=2,shuffle=True)
for im,lebel in train_data1:#访问迭代器
    print(label)

卷积神经网络(PyTorch)_第5张图片

下面使用自定义的数据读入:

train_data2 = DataLoader(txt_dataset,8,True) #batch size设置为8
im,label = next(iter(train_data2)) #使用这种方式访问迭代器中第一个batch的数据
im

卷积神经网络(PyTorch)_第6张图片

如果希望一个batch输出的label补成相同的长度,短的label用0补充,就需要使用collate_fn来自定义batch的处理方式:

def collate_fn(batch):
    batch.sort(key=lambda x:len(x[1]),reverse=True) #将数据集按照label的长度从大到小排序
    img,label = zip(*batch)  #将数据和label配对取出
    #填充
    pad_label = []
    lens = []
    max_len = len(label[0])
    for i in range(len(label)):
        temp_label = label[i]
        temp_label += '0' * (max_len - len(label[i]))
        pad_label.append(temp_label)
        lens.append(len(label[i]))
    pad_label
    return img,pad_label,lens   #输出label的真实长度

 

你可能感兴趣的:(语言笔记)