目录
ResNet 的亮点:
针对第一点,我们知道加深网络层对于提升网络性能至关重要。然而实际情况中,网络层的加深会导致学习无法进行,性能会更差。因为网络的深度会导致梯度消失或者梯度爆炸的问题

因为层数的加深,反向传播的时候,更新权重w的时候,根据链式法则可能要乘上很多项。
例如传过来的是个0.5,那么网络层数深就会导致 0.5 的n次方,从而导致梯度消失,网络无法学习。同理,梯度爆炸是 > 1的概念。
ResNet 提出了一个residual block 残差块的概念,如图。通过右面的一种shortcut 捷径的方式,这样在反向传播的时候,shortcut 就可以将梯度传递过来,从而解决梯度消失或者爆炸的问题。通过这个快捷路径,从而解决了网络加深的问题。

然后,ResNet 使用了 BN (Batch Normalization)代替了Dropout 方法。
根据之前的预处理方式,我们知道,将数据限定到相似的范围内可以帮助网络更好的训练(之前看吴恩达视频的时候,说是特征缩放到类似的区间,这样损失函数就会是一个类似于碗的样子,这样方便梯度下降。否则,可能会是一个碗被挤压的样子,在steep的一边,梯度就会来回横跳)。例如之前的ToTensor,Normalize 等等。
但是之前都是对数据进行特征缩放,然而我们Normalization后的数据经过网络的时候就已经不是之前归一化之类的数据了,所以BN的思想是在每一层后也正规化一下

经过上面的变换,就可以将每一个 batch 的数据变成mean = 0 ,std = 1的分布
这里要注意以下两点:

ResNet 因为加入了shortcut 捷径的方式可以加深更多的网络层。这里常见的resnet有如下的几种,这里只介绍ResNet34

这里50、101、152 每个residual block 第一个都是 1*1 是为了改变特征图的size。观察可以发现,例如conv3_x 、conv4_x、conv5_x 的第一个残差块的第一层右面的shortcut都是虚线
这是因为这里的shortcut需要用1 * 1 的卷积核改变size

并且在conv3_x 、conv4_x、conv5_x 残差块的第一层 3*3 卷积核的stride也发生了变化
因为步长发生了变化,所以这里输出特征图和输入的size以及不是一样了。而右面的shortcut想要和左面的发生叠加,要保证shape一样,所以右面的shortcut上面需要一个1*1 的卷积核来改变size

因为ResNet 网络里面包含很多的residual block
3*3 代表卷积核的size ,64 代表卷积核的个数 = 图像输出的深度


就是网络中上面的结构,所以先将它们封装成一个类

因为residual 结构都是两个3*3 卷积构成的,所以这里先将Conv2d的kernel_size 设置为3
网络结构中没有标stride的都是默认为1,但是这里第一个stride不能直接等于1,因为有的层第一个是2,不是1---------> 所有shortcut是虚线对应的第一个卷积核
然后,经过BN层,ReLU层,残差块的第二个卷积都是3*3 ,且默认stride = 1
这里padding =1 的原因
- 图像输出的size = (in - 3 + 2*1) / 1 + 1 = in 才能保证输出的size是等于输入的
- 如果stride = 2,那么out = (in - 3 + 2*1) / 2 + 1 =in / 2 + 0.5 = in / 2 (向下取整),这样就会将输出的size变成一半,所以conv3_x 、conv4_x、conv5_x 残差块的第一层和上面的size是不一样的。那么右面的捷径就不能由上面直接流过来,因为size不一样没法相加。所以对应的shortcut是虚线,需要特殊的 1*1 卷积核改变size
- 并且,由于BN,所以不需要偏置 b
接下来定义forward:

残差块的传播分为两类,一类是左边主路的传播,这里定义成left。
右边是快捷路径,这里分为两种情况,一种是上游直接传过来,另一种是图像size被改变需要1*1卷积核做运算。如果是第一种,那么为None,residual = x,如果被改变,就为新的shortcut。最后和左边的out相加,经过ReLU输出就可以了
为了方便图像维度的讲解,这里会传入一个batch = 10 的224*224*3 的彩色图像
为了不引起误会,后面描述的依旧是batch而不是10
例如:图像shape:batch*64*56*56 而不是10*64*56*56

首先定义ResNet 前面的传播


因为这里图像的输入是224*224*3 的彩色图像。
首先经过7*7 ,stride = 2 的卷积核,这里padding = 3的原因是为了保证输出size是输入的一半
再经过3*3 最大池化层,就可以将图像的输出维度控制在:batch*64*56*56
这里的stride、padding 都是为了保证图像的size 满足

这里_make_layer 第一个参数为残差块里面卷积核的size,3、4、6、3为每层包含残差块的个数
首先看第一层


如图,第二层(conv3_x)的第一个残差块已经发生了变化,
这里的变化是指:
- stride 由 1变成了2,所以会导致图像的size 变为原来的一半
- 并且图像的深度变成了原来的2倍(因为size减少,少了一部分的特征,所以就拿深度来弥补了)

layer3和layer4都是和layer2一样的
经过layer3的shape是:batch*256*14*14
经过layer4的shape是:batch*512*7*7
全连接层:

forward:

网络的参数个数可由以下得到:
model = ResNet()
print("Total number of paramerters in networks is {} ".format(sum(x.numel() for x in model.parameters())))
ResNet 参数个数为:

训练网络的代码不做讲解,具体的可以看这篇文章:
pytorch 搭建 LeNet 网络对 CIFAR-10 图片分类
https://blog.csdn.net/qq_44886601/article/details/127498256
主要对GPU训练的部分做讲解:
首先判断设备:

然后将网络扔到设备上:

然后训练的时候,输入也需要传到GPU

最后就是测试的时候

这里读取的时候需要注意,因为训练的时候实在GPU上,这里我预测的时候实在cpu上,所以读取网络参数的时候,代码要变成下面这样


预测图像

这里有一个注意点:
因为CIFAR10 图像的size 都是32*32 的,而ResNet网络的输入是224*224。那么预处理时候需要将图像放大,这里就会很模糊。所以在预测的时候,有个smart point就是,将预测的图像也转换成32*32,然后再放大成224*224。这样预测的精度就会上升

处理结果:

这里网上随便找了几张图片,都能预测对且有较好的准确率:

ResNet 网络部分:
import torch.nn as nn
from torch.nn import functional as F
class ResidualBlock(nn.Module): # 搭建 残差结构
def __init__(self, inchannel, outchannel, stride=1, shortcut=None):
super(ResidualBlock, self).__init__()
self.left = nn.Sequential(
# stride 不是1,因为有的残差块第一层 的stride = 2;对应残差块的虚线实线
nn.Conv2d(inchannel,outchannel,kernel_size=3,stride =stride,padding = 1,bias = False),
nn.BatchNorm2d(outchannel),
nn.ReLU(inplace=True),
# 第二个卷积核的stride = 1
nn.Conv2d(outchannel,outchannel,kernel_size=3,stride = 1,padding=1,bias=False), # 输入和输出的特征图size一样
nn.BatchNorm2d(outchannel),
)
self.right = shortcut # 捷径块
def forward(self,x):
out = self.left(x)
# 实线,特征图的输入和输出size一样;虚线,shortcut部分要经过1*1卷积核改变特征图size
residual = x if self.right is None else self.right(x)
out += residual # 加完之后在 ReLU
out = F.relu(out)
return out
class ResNet(nn.Module): # 搭建ResNet 网络
def __init__(self, num_classes=10):
super(ResNet,self).__init__()
self.pre = nn.Sequential(
# 输入:batch * 3 * 224 * 224
# 输出特征图size = (224 - 7 + 2*3) / 2 + 1 = 112 +0.5 = 112 向下取整
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), # 输出 [batch,64,112,112]
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
# 输出特征图size = (112 - 3 + 2*1) / 2 + 1 = 56 +0.5 = 56 向下取整
nn.MaxPool2d(kernel_size=3,stride=2,padding=1) # 输出 [batch,64,56,56]
)
self.layer1 = self._make_layer(64, 3,stride = 1) # conv2_x 有三个残差块
self.layer2 = self._make_layer(128,4,stride = 2) # conv3_x 有四个残差块
self.layer3 = self._make_layer(256,6,stride = 2) # conv4_x 有六个残差块
self.layer4 = self._make_layer(512,3,stride = 2) # conv5_x 有三个残差块
self.fc = nn.Linear(512,num_classes) # 分类层
def _make_layer(self,channel, block_num,stride = 1): # 构建layer,每个层包含3 4 6 3 个残差块block_num
shortcut = None
layers = [] # 网络层
if stride != 1: # conv3/4/5_x 的第一层 stride 都是2,并且shortcut 需要特殊操作
# 定义shortcut 捷径,都是1*1 的kernel ,需要保证和left最后相加的shape一样
shortcut = nn.Sequential(
nn.Conv2d(int(channel / 2),channel,kernel_size=1,stride=stride,bias=False),
nn.BatchNorm2d(channel )
)
layers.append(ResidualBlock(int(channel / 2),channel,stride,shortcut))
else:
layers.append(ResidualBlock(channel, channel, stride, shortcut))
for i in range(1,block_num): # 残差块后面几层的卷积是一样的
layers.append(ResidualBlock(channel,channel))
return nn.Sequential(*layers)
def forward(self,x):
x = self.pre(x) # batch*64*56*56
x = self.layer1(x) # batch*64*56*56
x = self.layer2(x) # batch*128*28*28
x = self.layer3(x) # batch*256*14*14
x = self.layer4(x) # batch*512*7*7
x = F.avg_pool2d(x,7) # batch*512*1*1
x = x.view(x.size(0),-1) # x.size(0)是batch ,保持batch,其余的压成1维
x = self.fc(x) # batch*10(分类的个数)
return x
model = ResNet()
#import torch
#input = torch.randn((10,3,224,224))
#model(input)
# 计算网络参数个数
print("Total number of paramerters in networks is {} ".format(sum(x.numel() for x in model.parameters())))
训练部分:
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms
from model import ResNet
import torchvision
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 判断是cpu还是gpu运行
print("using {} device.".format(device))
data_transform =transforms.Compose([
transforms.Resize((224,224)), # 变换成(224,224)满足ResNet的输入
transforms.ToTensor(), # 变成Tensor,改变通道顺序等等
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 训练集 5W 张图片
trainset = torchvision.datasets.CIFAR10(root = './data',train = True,download= True,transform=data_transform)
trainloader = torch.utils.data.DataLoader(trainset,batch_size = 36,shuffle = True)
# 测试集 1W 张图片
testset = torchvision.datasets.CIFAR10(root = './data',train = False,download= True,transform=data_transform)
testloader = torch.utils.data.DataLoader(testset,batch_size = 36,shuffle = False)
# CIFAR10 十个分类类别的label
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
net = ResNet() # 载入网络
net.to(device)
loss_function = nn.CrossEntropyLoss() # 定义损失函数
optimizer = optim.Adam(net.parameters() , lr=0.0001) # 定义优化器
best_acc = 0.0
save_path = './resNet34.pth' # 网络权重文件保存路径
for epoch in range(5):
# train
net.train()
running_loss = 0.0
for step, data in enumerate(trainloader,start= 0):
images, labels = data
optimizer.zero_grad() # 梯度清零
out = net(images.to(device)) # 前向传播
loss = loss_function(out, labels.to(device)) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 梯度下降
running_loss += loss.item() # 损失值
# test
net.eval()
acc = 0.0
total = 0
with torch.no_grad():
for test_data in testset:
test_images, test_labels = test_data # 取出测试集的image和label
outputs = net(test_images.to(device)) # 前向传播
predict_y = torch.max(outputs, dim=1)[1] # 取出最大的预测值
acc += (predict_y == test_labels.to(device)).sum().item() # 正确 +1
total+= test_labels.size(0)
accurate = acc / total # 计算整个test上面的正确率
print('[epoch %d] train_loss: %.3f test_accuracy: %.3f' %
(epoch + 1, running_loss / step, accurate))
if accurate > best_acc:
best_acc = accurate
torch.save(net.state_dict(), save_path)
print('Finished Training')
预测部分:
import torchvision.transforms as transforms # 预处理
import torch
from PIL import Image
from model import ResNet
data_transform =transforms.Compose([
transforms.Resize((32,32)),
transforms.Resize((224,224)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
classes = ('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
net = ResNet()
net.load_state_dict(torch.load('./resNet34.pth',map_location = 'cpu')) # 加载网络训练的参数
im = Image.open('./2.png')
im = data_transform(im) # 图像维度 (C,H,W)
im = torch.unsqueeze(im,dim = 0) # 增加维度,第0维增加1 ,维度(1,C,H,W)
net.eval() # 打开eval模式
with torch.no_grad():
outputs = net(im) # 预测图像
predict = torch.max(outputs,dim = 1)[1].data.numpy() # 取出最大的结果
pro = torch.softmax(outputs, dim=1)
rate = pro.max().numpy() * 100
print('prediction:%s , accuracy: %.3f %%'%( classes[int(predict)],rate))

迁移学习其实就是将别人学习过的权重拿来用,将它们作为初始值训练的过程
因为最后的分类往往是根据自己的设置,建议将官方的权重copy,然后加一个分类层就可以了
例如:将原网络的输出再加一个全连接层

我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手