pytorch_神经网络构建3
文章目录
- 卷积神经网络
- 实现卷积层,池化层
- 池化层:
- 数据标准化
- AlexNet卷积网络
- 深层网络结构vgg
- googleNet网络结构
- ResNet网络结构
- DensNet网络结构
- 训练卷积神经网络会遇到的一些问题
- 学习率衰减
卷积神经网络
前面讲述了逻辑回归分类,模拟函数回归问题,单层,深层网络,它们以点和向量为代表,接下来讲述如何识别一张图片,
假设一张图片大小为1000x1000,rgb图像,我们应该如何对它识别分类
我们可以像处理手写数字识别一样把图片展开成一行,然后对每个数据参数求权重,然而实际操作确是极其不现实的,也是不合理的
因为仅仅输入就有300万参数,如果我们希望第一层拥有1000个神经元来对他进行输入,那么参数量将会是310^9
如果图像大小为1000010000那么处理一张图片的参数量将会十分巨大
传统意义上处理图片,有缩放,模糊,锐化等操作,可以在一定程度上降低图像质量和大小,但是不影响照片整体的判断
对图片的某个区域进行筛选出特征值,进行缩放是传统处理图像的技巧
受此启发,于是有人提出了滑动窗口的概念,我们不必把图片看做一个整体,而是让神经网络从局部窗口一个个观察它的特征,比如看看猫的耳朵,看看猫的眼睛,最后综合起来,也许不同的窗口学习了图像不同的特征,最后我们用这些特征来评价一张图片,这个滑动窗口,叫做卷积核
它在图片上滑动的过程中,与图片上对应的区域做卷积运算,最后我们训练的参数不再是图片上的每个像素点的权重,而是滑动窗口的权重
卷积核的大小,叫做ksize,在图片上移动的步长叫做stride,如果在图片移动过程中图片大小无法满足卷积核的运算要求,往往还会在原图象上补0以让卷积核进行扫描,这个操作称为padding
经过卷积核ksize和stride扫描后新图像的形状可以由以下公式得出
w为输入图像宽高,卷积核大小为k,填充大小为p,滑动步长为s
每个卷积核就像我们之前进行神经网络训练时的神经元一样,只不过不再是单独的一个w,而是一组w
如下图
输入层–>卷积层–>池化层–>卷积层–>池化层–>全连接层–>全连接层–>全连接层
卷积层很好理解
图像经过每个卷积核后都会生成新的图像,如图32x32x3图像经过5x5x3的卷积运算扫描后得到28x28的一维图像
如果我们有6个卷积核,那么经过第一个卷积层后我们能够得到6个28X28图像,这和以往神经网络对矩阵的运算结果特征十分相似
那么可以说卷积核和他所输出的新图层构成了一个卷积层
当然,卷积层也需要激活函数,对于不必要的特征有时会让relu替我们丢弃
池化层:
作用是对图像大小进行缩放,进行下采样操作,可以迅速降低图像大小而保留图像特征
比如最大池化层max pooling(输出滑动窗口扫描区域的最大值),平均池化层average pooling(输出滑动窗口扫描区域的平均值)
如图,进行快速缩放,最大池化
全连接层:
将图片进行卷积操作后,图像的数据量已经急剧减小,将每张图片摊平成一行,可以为每个筛选下来的图像数据设置权重了
已经可以使用wx进行简单的运算了
至此,卷积神经网络,也被称为cnn,Convolutional Neural Network
实现卷积层,池化层
卷积层:
卷积层重要的是卷积核,需要设置卷积核的大小,扫描步长,以及卷积核的权重
幸运的是pytroch提供了两个函数来实现它,torch.nn.Conv2d(), torch.nn.functional.conv2d()
两者实现的结果是一样的,只是在应用时设置权重的方式不同
torch.nn.Conv2d()不用特意设置权重,设置好卷积核后会自动随机初始化权重,当然也可以指定权重
torch.nn.functional.conv2d()必须为其设定权重才会进行卷积操作
前者用的更多,无论训练和调试都可以胜任
我们用灰度图读取一张图片,查看一下使用conv2d的效果
原始图片
会得到原始图片的宽高,和灰度图
im=Image.open('./cat.png').convert('L')
im=np.array(im,dtype='float32')
plt.imshow(im.astype('uint8'),cmap='gray')
im.shape
接下来随机初始化权重,进行特征提取,我们只需要指定卷积核大小和步长即可
不过,为了进行卷积运算,需要对输入数据进行一次reshape[1,1,224,224]
代表batch size为1,channel为1,height为224,width为224,这是因为pytroch的计算数据格式为NCHW
卷积操作参数为(输入通道数1,输出通道数1,卷积核大小3X3,不设置偏置窗口)
修改卷积核大小可以使用参数(输入通道数1,输出通道数1,kernel_size=(4, 3),不设置偏置窗口)
queeze用来压缩张量,将维度为1的去除,这样就将[1,1,224,224]变为[224,224]的numpy图像矩阵
im=torch.from_numpy(im.reshape((1,1,im.shape[0],im.shape[1])))
conv1=nn.Conv2d(1,1,3,bias=False)
edge1=conv1(Variable(im))
print(conv1.weight.data)
edge1=edge1.data.squeeze().numpy()
plt.imshow(edge1,cmap='gray')
接下来我们为其指定权重,重新查看输出
sobel_kernel=np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]],dtype='float32')
sobel_kernel=sobel_kernel.reshape((1,1,3,3))
weight=torch.from_numpy(sobel_kernel)
conv1.weight.data=weight
edge1=conv1(im)
print(conv1.weight.data)
edge1=edge1.data.squeeze().numpy()
plt.imshow(edge1,cmap='gray')
我们使用第二种方式,指定参数,查看输出
edge2=F.conv2d(im,weight)
edge2=edge2.data.squeeze().numpy()
plt.imshow(edge2,cmap='gray')
结果是一样的,所以torch.nn.functional.conv2d()并不常用
池化层:
受到传统图像处理的启发,我们可以缩小图像而不影响图像的判别
池化层没有参数,对于图像的处理有最大值池化,均值池化
设置滑动窗口的大小为2x2,步长为2,相比于卷积层他没有卷积计算操作
卷积核大小设置和卷积层一致
pool1=nn.MaxPool2d(2,2)
small_im=pool1(im)
print(small_im.shape)
small_im=small_im.data.squeeze().numpy()
plt.imshow(small_im,cmap='gray')
[112,112]
small_im=F.max_pool2d(im,2,2)
small_im=small_im.data.squeeze().numpy()
plt.imshow(small)
同样F.max_pool2d也不常用
到这里常用的卷积层和池化层,以及如何使用卷积和池化层就介绍完了
接下来我们关心的是网络结构的设计问题
但是在此之前,我们需要解决一个迫在眉睫的问题,对于大量数据的输入,我们该如何规范化这些数据,如果没有标准的数据输入,网络结构设计的再好,也很难得到一个好结果
数据标准化
数据预处理:
我们希望所有的数据是规范的,特征分布规模是适当的,
为了某种适当,我们希望数据分布在坐标原点周围来观察规律,可以使用0点中心化方法
每个特征维度都减去均值,最后我们会得到一个0均值分布
进一步,在0均值处理之后,我们可以将其除以标准差(平方差后求根)得到一个近似标准正态分布
更进一步 ,可以根据最大最小值,将其分布控制在(-1,1)区间
然而在实际训练中,尽管第一层输入时特征不相关,近似正态分布输入,但是由于神经网络的非线性特点,最后输出的结果会极大概率偏离N(0,1)正态分布,这让深度网络的构建困难重重
后来有人提出了一个新方法,批标准化来解决这个问题
那就是对每一层网络层输出都进行归一化,使其符合正态分布,
对一个训练批次的数据{x1,x2…}处理如下
第一行和第二行很容易理解,就是求一个batch中的均值和方差,
第三个公式则根据方差对batch中的每个数据做标准化,为了防止底数为0,加了一个e,一般取e=10^-5
最后修正权重得到输出
那么接下来我们构建一个卷积神经网络,比如一个对图像分类的经典网络
AlexNet卷积网络
这个卷积网络当初是在一个图像分类比赛上取得了第一的成绩,如今我们对其进行实践一下
首先我们先下载图像数据集,将False改为True
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
train_set=CIFAR10('./data',train=True,download=False)
test_set=CIFAR10('./data',train=False,download=False)
im,label=test_set[0]
可以通过图像和标签查看其单个数据
cat
那么卷积网络除了创新性的提出了卷积核和池化层外其他训练方式和之前的分类网络并无不同
我们接下来需要设置神经网络结构,loss函数,参数优化器,输入数据标准化
它的网络结构为
卷积层1(3,64,5)(代表输入量3图,输出64图,5x5卷积核),relu激活层,最大池化层(3,2)(3x3池化,步长为2),卷积层2(64,64,5,1),relu激活层,最大池化层(3,2),全连接层(1024,384),全连接层(384,192),全连接层(192,10)
class AlexNet(nn.Module):def __init__(self):super().__init__()self.conv1=nn.Sequential(nn.Conv2d(3,64,5),nn.ReLU(True))self.max_pool1=nn.MaxPool2d(3,2)self.conv2=nn.Sequential(nn.Conv2d(64,64,5,1),nn.ReLU(True))self.max_pool2=nn.MaxPool2d(3,2)self.fc1=nn.Sequential(nn.Linear(1024,384),nn.ReLU(True))self.fc2=nn.Sequential(nn.Linear(384,192),nn.ReLU(True))self.fc3=nn.Linear(192,10)def forward(self,x):x=self.conv1(x)x=self.max_pool1(x)x=self.conv2(x)x=self.max_pool2(x)x=x.view(x.shape[0],-1)x=self.fc1(x)x=self.fc2(x)x=self.fc3(x)return x
alexnet=AlexNet()
定义loss函数,参数优化器,设置网络在GPU上进行训练
net=AlexNet().cuda()
optimizer=torch.optim.SGD(net.parameters(),lr=1e-1)
lossor=nn.CrossEntropyLoss()
为输入的数据进行标准化
x = x.transpose((2, 0, 1))
pytorch使用的数据格式为NCHW,即图片批次数量,通道数,高,宽
输入数据原本格式为HWC,比如1282563,需要交换通道到pytorch格式
from utils import train
def data_tf(x):x=np.array(x,dtype='float32')/255x=(x-0.5)/0.5x=x.transpose((2,0,1))x=torch.from_numpy(x)return x
train_set=CIFAR10('./data',train=True,transform=data_tf)
test_set=CIFAR10('./data',train=False,transform=data_tf)
train_data=torch.utils.data.DataLoader(train_set,batch_size=64,shuffle=True)
test_data=torch.utils.data.DataLoader(test_set,batch_size=128,shuffle=True)
训练同以往一样,从数据迭代器中将数据输入神经网络,得到预测值,根据loss函数算出loss值
参数优化器清空梯度,loss进行反向传播,参数优化器更新参数,继续迭代
进行20次迭代,伪代码为
train(net,train_data,test_data,20,optimizer,lossor)
实际训练得到
20次迭代在测试集上就得到了71%的正确率,已经很不错了
深层网络结构vgg
思路是使用小的33卷积核堆叠和22的池化层,不仅能够减少参数,还能来加深神经网络结构,试图构建真正意义上的深层网络结构
它在2014年夺得了imageNet的第二名,那么第一名是谁呢?比vgg厉害,不过vgg的思路也是第一名的思路,只不过第一名做了比vgg更多的开创性工作
它的网络深度十分复杂,但是构造十分简单,但是为后来更深层次的神经网络提供了很好的借鉴意义
它包含输入层,隐含层,全连接层,输出层,隐含层使用了大量的重复结构块
结构块函数:
设置生成一些重复的网络结构,参数(生成N个卷积层,输入通道数,输出通道数)
因为最后使用了(2,2)池化,最后输出的图像大小为原图的一半
def vgg_block(num_convs, in_channels, out_channels):net = [nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.ReLU(True)] # 定义第一层for i in range(num_convs-1): # 定义后面的很多层net.append(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))net.append(nn.ReLU(True))net.append(nn.MaxPool2d(2, 2)) # 定义池化层return nn.Sequential(*net)
结构块堆叠函数:
用于构建不同的结构块,将它们堆叠起来构成复杂的隐含层
参数(卷积层数列表,通道数目列表)
将根据卷积层数列表,生成对应层数的n个卷积层,其中每个卷积层的输入和输出由对应的通道列表值控制
def vgg_stack(num_convs, channels):net = []for n, c in zip(num_convs, channels):in_c = c[0]out_c = c[1]net.append(vgg_block(n, in_c, out_c))return nn.Sequential(*net)
如图,构建如下隐含层:
vgg_net = vgg_stack((1, 1, 2, 2, 2), ((3, 64), (64, 128), (128, 256), (256, 512), (512, 512)))
print(vgg_net)
将会生成5个网络结构,每个结构对前一个图像减小1/2,其中第一个网络结构为1个3通道输入,64通道输出的卷积层
第二个网络结构为1个64通道输入,128通道输出的卷积层
第三个网络结构为2个128通道输入,256通道输出的卷积层…
举个例子:
testX=torch.zeros(1,3,256,256)
testY=vgg_net(testX)
print(testY.shape)
最后结果为torch.Size([1, 512, 8, 8]),图像大小由256x256降低到8*8,2^5,因为一共有5个2x2池化层对其进行缩小
分别图像降维为128-64-32-16-8
最后,我们为这个深度结构加上全连接层10分类作为输出,vggNet就算构建完成了
class vgg(nn.Module):def __init__(self):super(vgg,self).__init__()self.feature=vgg_netself.fc=nn.Sequential(nn.Linear(512,100),nn.ReLU(True),nn.Linear(100,10))def forward(self,x):x=self.feature(x)x=x.view(x.shape[0],-1)x=self.fc(x)return x
测试一下效果
vggNet=vgg()
optimizer=torch.optim.SGD(vggNet.parameters(),lr=1e-1)
lossor=nn.CrossEntropyLoss()
# print(vggNet)
train(vggNet,train_data,test_data,20,optimizer,lossor)
20次迭代正确率达到了77%,看来更深层次的网络和小的卷积核对神经网络的优化效果十分显著
googleNet网络结构
前面讲述了2014年ImagNet第二名的网络结构,那么来看看第一名的
两者同样意识到了深度网络带来的巨大潜力,只不过第一比第二创新性的提出了一个 inception 模块
这个模块的核心思想是在原图大小不变的情况下,增加图形的通道维度数量
原图经过6个卷积核,1次池化,共四类操作处理后进行拼接进行通道维度扩容
比如39696扩充到2569696,同时在每个卷积层输出的数据后进行数据正态分布标准化,让输出数据分布符合正态分布
单看为每个卷积层所做的工作:
设置输入通道数,输出通道数,卷积核大小,步长,填充
输出结果进行正态分布标准化,又称为批量归一化,eps是为了计算正态分布时防止分母为0
再加一个激活函数,构成了inception模块的卷积块
def conv_relu(in_channel, out_channel, kernel, stride=1, padding=0):layer = nn.Sequential(nn.Conv2d(in_channel, out_channel, kernel, stride, padding),nn.BatchNorm2d(out_channel, eps=1e-3),nn.ReLU(True))return layer
基于此,我们构建如上图的一个inception模块
class inception(nn.Module):def __init__(self, in_channel, out1_1, out2_1, out2_3, out3_1, out3_5, out4_1):super(inception, self).__init__()# 第一条线路self.branch1x1 = conv_relu(in_channel, out1_1, 1)# 第二条线路self.branch3x3 = nn.Sequential( conv_relu(in_channel, out2_1, 1),conv_relu(out2_1, out2_3, 3, padding=1))# 第三条线路self.branch5x5 = nn.Sequential(conv_relu(in_channel, out3_1, 1),conv_relu(out3_1, out3_5, 5, padding=2))# 第四条线路self.branch_pool = nn.Sequential(nn.MaxPool2d(3, stride=1, padding=1),conv_relu(in_channel, out4_1, 1))def forward(self, x):
# print(self.branch_pool)f1 = self.branch1x1(x)f2 = self.branch3x3(x)f3 = self.branch5x5(x)f4 = self.branch_pool(x)print(f1.shape)print(f2.shape)print(f3.shape)print(f4.shape)output = torch.cat((f1, f2, f3, f4), dim=1)return output
我们输入一张测试图,查看是否扩容成功
test_net = inception(3, 64, 48, 64, 64, 96, 32)
test_x = Variable(torch.zeros(1, 3, 96, 96))
print('input shape: {} x {} x {}'.format(test_x.shape[1], test_x.shape[2], test_x.shape[3]))
test_y = test_net(test_x)
print('output shape: {} x {} x {}'.format(test_y.shape[1], test_y.shape[2], test_y.shape[3]))
可以看到第一类操作扩容了64个通道,第二类操作扩容了64个通道,第三类操作扩容96个通道,第四类操作扩容了32个通道,最后将它们拼接在一起成为了一个具备256维度的96*96图像
那么,我们的每个图像都进行如此扩容,然后再放入网络结构中进行训练,就可以设计更加深度的网络
依旧是CIFAR10分类,我们为其设计googlnet网络结构
class googlenet(nn.Module):def __init__(self, in_channel, num_classes, verbose=False):super(googlenet, self).__init__()self.verbose = verboseself.block1 = nn.Sequential(conv_relu(in_channel, out_channel=64, kernel=7, stride=2, padding=3),nn.MaxPool2d(3, 2))self.block2 = nn.Sequential(conv_relu(64, 64, kernel=1),conv_relu(64, 192, kernel=3, padding=1),nn.MaxPool2d(3, 2))self.block3 = nn.Sequential(inception(192, 64, 96, 128, 16, 32, 32),inception(256, 128, 128, 192, 32, 96, 64),nn.MaxPool2d(3, 2))self.block4 = nn.Sequential(inception(480, 192, 96, 208, 16, 48, 64),inception(512, 160, 112, 224, 24, 64, 64),inception(512, 128, 128, 256, 24, 64, 64),inception(512, 112, 144, 288, 32, 64, 64),inception(528, 256, 160, 320, 32, 128, 128),nn.MaxPool2d(3, 2))self.block5 = nn.Sequential(inception(832, 256, 160, 320, 32, 128, 128),inception(832, 384, 182, 384, 48, 128, 128),nn.AvgPool2d(2))self.classifier = nn.Linear(1024, num_classes)def forward(self, x):x = self.block1(x)if self.verbose:print('block 1 output: {}'.format(x.shape))x = self.block2(x)if self.verbose:print('block 2 output: {}'.format(x.shape))x = self.block3(x)if self.verbose:print('block 3 output: {}'.format(x.shape))x = self.block4(x)if self.verbose:print('block 4 output: {}'.format(x.shape))x = self.block5(x)if self.verbose:print('block 5 output: {}'.format(x.shape))x = x.view(x.shape[0], -1)x = self.classifier(x)return x
我们对这个网络的输出进行一次测试,查看其分类结果,这已经是一个具备深度结构的网络了
test_net = googlenet(3, 10,True)
test_x = Variable(torch.zeros(1, 3, 96, 96))
test_y = test_net(test_x)
print('output: {}'.format(test_y.shape))
网络结构已经具备了,接下来为其设置loss函数,参数优化器,以及对输入数据进行标准化,即可展开训练了
然而因为CIFAR原图为28*28,可以适当对其进行放大
def data_tf(x):x = x.resize((96, 96), 2) # 将图片放大到 96 x 96x = np.array(x, dtype='float32') / 255x = (x - 0.5) / 0.5 # 标准化x = x.transpose((2, 0, 1)) x = torch.from_numpy(x)return x
然后创建googlNet对象,查看20次训练效果
googleNet = googlenet(3, 10)
optimizer = torch.optim.SGD(googleNet.parameters(), lr=0.01)
losser = nn.CrossEntropyLoss()
train(googleNet, train_data, test_data, 20, optimizer, losser)
似乎和vggnet差别不大,但是训练多次后其准确率将超过vggnet,深层网络的拟合能力比浅层更强,似乎外国人比我们更早的意识到这一点,当他们愿意花十几亿美金只为了训练一个模型时,我们还在抱着单卡3060ti训练的小模型沾沾自喜,于是chatGPT出来时给大家的震撼程度无异于一颗重磅炸弹
ResNet网络结构
这个结构提出了一个(跨层链接)残差模块用于解决梯度回传消失的问题,类似大脑中的神经元链接不是一层层,而是网状结构,可能输入层直连输出层,输入层也可能直连任何一个中间层,当然这个现在连接还没有那么稠密,resNet仅仅做到了跨层连接,
而真正的特征之间相互链接还需要下面的DenseNet实现
梯度回传消失的问题在深层网络中经常出现
由于反向传播的特性,使用链式法则,随着网络层数的增多,当导数小于1时,越靠近输入层导数越接近为0,导致参数迟迟无法更新
比如sigmoid函数
有时无法确定哪里出了问题,函数迟迟不能收敛,模型无法学习,需要解决梯度过小无法学习的问题
残差模块的思想是将之前的输入越过中间模块,直接传输到目标输出,然后再对中间模块进行更新参数优化,这样不会导致梯度过低无法更新参数的问题
举个最简单的例子是,输入x,目标h(x),正常应该是x-f(x)-h(x),一层层训练,但是我们直接把x传过去,变成x-(x+h(x)),然后不断优化f(x)接近H(x),
f(x)=h(x)-x
接下来先定一层卷积网络作为残差网络的基石,然后我们用True,False参数来控制一个网络块是否是残差模块,设计出一个三维网络
我们可以把之前一层层传递w的网络叫做二维网络,现在有了残差模块让层与层之间网络更短,我们将在神经网络上创建真正意义上第三个维度的参数
这一层主要用来控制卷积核步长,步长将会决定一个输入图是否是原样输出
def conv3x3(in_channel, out_channel, stride=1):return nn.Conv2d(in_channel, out_channel, 3, stride=stride, padding=1, bias=False)
构建残差模块
当输入参数为True时,步长为1,输入将原样输出,为false时步长为2,输出将原图缩小为1/2
class residual_block(nn.Module):def __init__(self, in_channel, out_channel, same_shape=True):super(residual_block, self).__init__()self.same_shape = same_shapestride=1 if self.same_shape else 2self.conv1 = conv3x3(in_channel, out_channel, stride=stride)self.bn1 = nn.BatchNorm2d(out_channel)self.conv2 = conv3x3(out_channel, out_channel)self.bn2 = nn.BatchNorm2d(out_channel)if not self.same_shape:self.conv3 = nn.Conv2d(in_channel, out_channel, 1, stride=stride)def forward(self, x):out = self.conv1(x)out = F.relu(self.bn1(out), True)out = self.conv2(out)out = F.relu(self.bn2(out), True)if not self.same_shape:x = self.conv3(x)return F.relu(x+out, True)
会产生如下效果
设置为残差模块
# 输入输出形状相同
test_net = residual_block(32, 32)
test_x = Variable(torch.zeros(1, 32, 96, 96))
print('input: {}'.format(test_x.shape))
test_y = test_net(test_x)
print('output: {}'.format(test_y.shape))
设置为普通模块
# 输入输出形状不同
test_net = residual_block(3, 32, False)
test_x = Variable(torch.zeros(1, 3, 96, 96))
print('input: {}'.format(test_x.shape))
test_y = test_net(test_x)
print('output: {}'.format(test_y.shape))
接下来我们尝试使用残差模块的堆叠来构建一个网络
class resnet(nn.Module):def __init__(self, in_channel, num_classes, verbose=False):super(resnet, self).__init__()self.verbose = verboseself.block1 = nn.Conv2d(in_channel, 64, 7, 2)self.block2 = nn.Sequential(nn.MaxPool2d(3, 2),residual_block(64, 64),residual_block(64, 64))self.block3 = nn.Sequential(residual_block(64, 128, False),residual_block(128, 128))self.block4 = nn.Sequential(residual_block(128, 256, False),residual_block(256, 256))self.block5 = nn.Sequential(residual_block(256, 512, False),residual_block(512, 512),nn.AvgPool2d(3))self.classifier = nn.Linear(512, num_classes)def forward(self, x):x = self.block1(x)if self.verbose:print('block 1 output: {}'.format(x.shape))x = self.block2(x)if self.verbose:print('block 2 output: {}'.format(x.shape))x = self.block3(x)if self.verbose:print('block 3 output: {}'.format(x.shape))x = self.block4(x)if self.verbose:print('block 4 output: {}'.format(x.shape))x = self.block5(x)if self.verbose:print('block 5 output: {}'.format(x.shape))x = x.view(x.shape[0], -1)x = self.classifier(x)return x
查看网络中每个模块输出之后的图层大小
test_net = resnet(3, 10, True)
test_x = Variable(torch.zeros(1, 3, 96, 96))
test_y = test_net(test_x)
print('output: {}'.format(test_y.shape))
查看训练结果
resNet = resnet(3, 10)
optimizer = torch.optim.SGD(resNet.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()
train(resNet, train_data, test_data, 20, optimizer, criterion)
20次的训练得到了80%正确率,已经领先许多简单的网络了,相比无残差模块的深层网络训练更快,效果也更好
DensNet网络结构
如图所示,拼接在通道维度上进行,底层的输出会传递到后面的所有层,低维的特征和高维的特征将会联合训练
接下来实践这个网络
定义网络块,它将作为denseNet网络的基石
对输入的数据标准化,然后创建一个relu激活函数,和一个卷积核对图像操作
def conv_block(in_channel, out_channel):layer = nn.Sequential(nn.BatchNorm2d(in_channel),nn.ReLU(True),nn.Conv2d(in_channel, out_channel, 3, padding=1, bias=False))return layer
接下来,创建Dense块,它将所有层的数据持续向后传递,
class dense_block(nn.Module):def __init__(self, in_channel, growth_rate, num_layers):super(dense_block, self).__init__()block = []channel = in_channelfor i in range(num_layers):block.append(conv_block(channel, growth_rate))channel += growth_rateself.net = nn.Sequential(*block)def forward(self, x):for layer in self.net:out = layer(x)x = torch.cat((out, x), dim=1)return x
实践是否如此
test_net = dense_block(3, 12, 3)
test_x = Variable(torch.zeros(1, 3, 96, 96))
print('input shape: {} x {} x {}'.format(test_x.shape[1], test_x.shape[2], test_x.shape[3]))
test_y = test_net(test_x)
print('output shape: {} x {} x {}'.format(test_y.shape[1], test_y.shape[2], test_y.shape[3]))
但是存在一个问题,如果网络很深,通道维度持续累加,最后计算量越来越大,这对于构建超深度神经网络是不利的
因此,应该对每一层的输出进行降维,不至于让通道累加太快
denseNet引入了一个叫做过渡层的概念,使用1x1卷积核,但是最后要经过(2,2)池化,这会让图片宽高减半,同时可以更改通道数目
def transition(in_channel, out_channel):trans_layer = nn.Sequential(nn.BatchNorm2d(in_channel),nn.ReLU(True),nn.Conv2d(in_channel, out_channel, 1),nn.AvgPool2d(2, 2))return trans_layer
查看经过过滤后的图像效果
test_net = transition(3, 12)
test_x = Variable(torch.zeros(1, 3, 96, 96))
print('input shape: {} x {} x {}'.format(test_x.shape[1], test_x.shape[2], test_x.shape[3]))
test_y = test_net(test_x)
print('output shape: {} x {} x {}'.format(test_y.shape[1], test_y.shape[2], test_y.shape[3]))
通道数目和图像大小按照我们所要求的进行了变化
根据dense块构建denseNet网络
class densenet(nn.Module):def __init__(self, in_channel, num_classes, growth_rate=32, block_layers=[6, 12, 24, 16]):super(densenet, self).__init__()self.block1 = nn.Sequential(nn.Conv2d(in_channel, 64, 7, 2, 3),nn.BatchNorm2d(64),nn.ReLU(True),nn.MaxPool2d(3, 2, padding=1))channels = 64block = []for i, layers in enumerate(block_layers):block.append(dense_block(channels, growth_rate, layers))channels += layers * growth_rateif i != len(block_layers) - 1:block.append(transition(channels, channels // 2)) # 通过 transition 层将大小减半,通道数减半channels = channels // 2self.block2 = nn.Sequential(*block)self.block2.add_module('bn', nn.BatchNorm2d(channels))self.block2.add_module('relu', nn.ReLU(True))self.block2.add_module('avg_pool', nn.AvgPool2d(3))self.classifier = nn.Linear(channels, num_classes)def forward(self, x):x = self.block1(x)x = self.block2(x)x = x.view(x.shape[0], -1)x = self.classifier(x)return x
查看网络对入输入图像的输出
test_net = densenet(3, 10)
test_x = Variable(torch.zeros(1, 3, 96, 96))
test_y = test_net(test_x)
print('output: {}'.format(test_y.shape))
尝试训练网络
denseNet = densenet(3, 10)
optimizer = torch.optim.SGD(denseNet.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()
train(denseNet, train_data, test_data, 20, optimizer, criterion)
可以看到经过20次训练,正确率还不稳定,但性能已经在测试集上已经有83%
denseNet为神经网络的构建奠定了坚实的基础,为后来的超规模搭建提供了很好的思路
训练卷积神经网络会遇到的一些问题
首先明白几个名词,
欠拟合:模型还没有训练到最优点,增加次数和改变网络结构都可以,表现为训练显示拉胯,测试拉胯
过拟合:模型在训练集上表现良好,在训练集上学习到了特殊的局部特征,表现为训练集表现良好,测试拉胯
改善过拟合现象一般的手法有,数据增强,dropout,正则化
数据增强:
对原始数据进行处理得到新的数据,一定程度上可以避免过拟合问题,这部分原因是数据量和特征不足造成的
1.缩放图片,2对图片随机截取3,对图片随机水平竖直翻转,4,对图片随机角度旋转,5,对图片亮度,对比度,颜色,随机变化
dropout:
受到人类大脑的启发,人脑在发育过程中对于一些不常用的神经元会关闭,建立新的连接
那么我们可以在训练时随机关闭一些神经元,看看不同的结构的输出,提高泛化能力
每个神经元被关闭的概率为p,那么x的期望为px,为了输出相同,往往还要计算1/p来得到x
pytroch中使用nn.Dropout§来表示
dropout一般用于全连接层,但是现在的卷积网络正在逐渐放弃使用全连接层,所以现在很少用dropout了
正则化:
机器学习提出的一种方法,有L1,L2正则化
常见的有L2正则化,它对loss函数进行了改进,加上了参数的二范数
二范数:也被称为L2范数,是向量中各元素的平方和的平方根,
比如
常用它来防止过拟合,实际使用时对λ(权重筛选系数) 进行限制,太大会抑制参数更新,太小会导致贡献不大,常用为0.001
当我们对新的loss函数进行梯度下降,就会有
参数更新时就会有
学习率衰减
学习率控制着梯度更新的步长,我们希望它能让loss下降到最低,当学习率为固定值后可能会在结果那里产生正确率震荡,无法进一步下降
我们希望他能够随着训练的进行不断减小学习率来逼近loss最低点
迁移学习:
将已有的预训练模型放到我们自己的数据集上继续训练,可以根据我们的需要更新最后的全连接层
这对于小批次数据的训练是有利的
举个例子,我们要识别一辆车的图片,但是我们的数据量不足,而ImageNet上有众多的车的图片,我们当然可以使用它的图片来进行训练,但是我们也可以直接拿imageNet已经训练好的模型过来,最后我们更新全连接层,输出对车辆的识别即可
相关文章:

pytorch_神经网络构建3
文章目录 卷积神经网络实现卷积层,池化层池化层:数据标准化AlexNet卷积网络深层网络结构vgggoogleNet网络结构ResNet网络结构DensNet网络结构训练卷积神经网络会遇到的一些问题学习率衰减 卷积神经网络 前面讲述了逻辑回归分类,模拟函数回归问题,单层,深层网络,它们以点和向量…...
遗传算法入门笔记
目录 一、大体实现过程 二、开始我们的进化(具体实现细节) 2.1 先从编码说起 2.1.1 二进制编码法 2.1.2 浮点编码法 2.1.3 符号编码法 2.2 为我们的袋鼠染色体编码 2.3 评价个体的适应度 2.4 射杀一些袋鼠 2.5 遗传--染色体交叉(crossover) 2.6 变异--基…...
【golang】go 返回参数 以及go中 裸返
一、Go 返回参数命名 在Golang中,命名返回参数通常称为命名参数。 Golang允许在函数签名或定义中为函数的返回或结果参数指定名称。或者可以说这是函数定义中返回变量的显式命名。基本上,它解决了在return语句中提及变量名称的要求。 通过使用命名返回参…...

elasticsearch深度分页问题
一、深度分页方式from size es 默认采用的分页方式是 from size 的形式,在深度分页的情况下,这种使用方式效率是非常低的,比如我们执行如下查询 1 GET /student/student/_search 2 { 3 "query":{ 4 "match_all":…...

32、Flink table api和SQL 之用户自定义 Sources Sinks实现及详细示例
Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…...

Java练习题-用冒泡排序法实现数组排序
✅作者简介:CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1🏆 📃个人主页:hacker707的csdn博客 🔥系列专栏:Java练习题 💬个人格言:不断的翻越一座又…...

【SV中的多线程fork...join/join_any/join_none】
SV中fork_join/fork_join_any/fork_join_none 1 一目了然1.1 fork...join1.2 fork...join_any1.3 fork...join_none 2 总结 SV中fork_join和fork_join_any和fork_join_none; Note: fork_join在Verilog中也有,只有其他的两个是SV中独有的; 1 一目了然 1.…...

翻译:网站整站翻译 / 网站国际化 / 极简实现
一、本文目标 以极简单的方法实现整站翻译,轻松实现国际化。 二、js 文件 https://res.zvo.cn/translate/translate.js 三、代码 代码放在浏览器控制台即可实现 var head document.getElementsByTagName(head)[0];var script document.createElement(script);sc…...

深度森林(deep-forest)安装
深度森林(deep-forest)安装 1、打开https://pypi.org/,搜索deep-forest,下载wheel文件 在下载好之后,打开文件下载的位置 首先对下载好的wheel文件进行改名,原名是: deep_forest-0.1.7-cp39-c…...
ping.pe ping 检测IP全球延迟
可以把结果保存为照片 https://ping.pe/全球ping ping ip端口检测 IP:PORT路由追踪 mtr IP 参考 ping.pe...

nodejs 16版本
Index of /download/release/latest-v16.x/...

NSSCTF做题(7)
[第五空间 2021]pklovecloud 反序列化 <?php include flag.php; class pkshow { function echo_name() { return "Pk very safe^.^"; } } class acp { protected $cinder; public $neutron; …...
【GIT版本控制】--高级分支策略
一、分支合并策略 在Git中,高级分支策略是为了有效地管理和整合分支而设计的。其中一个关键方面是分支合并策略,它定义了如何将一个分支的更改合并到另一个分支。以下是几种常见的分支合并策略: 合并提交策略(Merge Commit Stra…...
【Qt控件之QDialog】使用及技巧
简介 QDialog是Qt中的一个类,继承自QWidget类,用于创建对话框窗口,可以显示模态(阻塞当前窗口)或非模态的对话框。对话框可以包含各种控件,如按钮、文本框等,用于与用户进行交互。 主要函数说…...

Transformer预测 | Python实现基于Transformer的股票价格预测(tensorflow)
文章目录 效果一览文章概述程序设计参考资料效果一览 文章概述 Transformer预测 | Python实现基于Transformer的股票价格预测(tensorflow) 程序设计 import numpy as np import matplotlib.pyplot...
spark sql如何行转列
在数据仓库中,行转列通常称为”变形”(Pivoting) 或 “透视”(Pivoting),可使用Spark SQL的pivot语句实现。下面是一个简单的示例: 假设我们有如下表格: -------------------- | name | brand | year | -------------------- |…...

Prompt-Tuning(一)
一、预训练语言模型的发展过程 第一阶段的模型主要是基于自监督学习的训练目标,其中常见的目标包括掩码语言模型(MLM)和下一句预测(NSP)。这些模型采用了Transformer架构,并遵循了Pre-training和Fine-tuni…...

域信息收集
DMZ,是英文“demilitarized zone”的缩写,中文名称为“隔离区”,也称“非军事化区”。它是为了解决安装防火墙后外部网络的访问用户不能访问内部网络服务器的问题,而设立的一个非安全系统与安全系统之间的缓冲区。该缓冲区位于企业…...

MySQ 学习笔记
1.MySQL(老版)基础 开启MySQL服务: net start mysql mysql为安装时的名称 关闭MySQL服务: net stop mysql 注: 需管理员模式下运行Dos命令 . 打开服务窗口命令 services.msc 登录MySQL服务: mysql [-h localhost -P 3306] -u root -p****** Navicat常用快捷键 键动作CTRLG设…...

pdf文档内容提取pdfplumber、PyPDF2
测试pdfplumber识别效果好些;另外pdf这两个如果超过20多页就没法识别了,结果为空 1、pdfplumber 安装:pip install pdfplumber -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com代码: import pdfpl…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...