文章目录

  • 写在前面
  • nn.Module
  • nn.Conv2d 卷积层
  • nn.MaxPool2d 池化层
  • Non-linear Activations 非线性激活
    • ReLU
    • Sigmoid
  • nn.Linear 线性层
  • 其他层
  • 小小实战

写在前面

本博客参考b站up主“我是土堆”的视频:点击跳转
本系列笔记为pytorch入门学习,所以主要学习使用pytorch框架进行神经网络的实现过程,对于神经网络的基本原理可能不会做过多解释,主要着重于用法。

传送门:
PyTorch入门(一)数据集的一些基础操作

这一篇在上一篇的知识基础上,从零开始搭建一个神经网络
我们打开pytorch官网。在torch.nn里面我们可以看到
这些是我们搭建神经网络所需要用到的一些东西。

下面我们会按顺序讲解这些如何使用,如何从零开始搭建一个神经网络

nn.Module

首先说第一个Containers。这里面比较常用的就是Module:所有神经网络模块的基类。

下面讲解一下用法。
(在官网我们可以点进去查看这个函数的用法以及作用,如果有不明白的可以随时查看。)

创建一个神经网络的第一步,我们先要有一个“骨架”
刚才说了,Module是所有神经网络模块的基类。
所以我们只需要自己定义一个类,继承Module,即可完成骨架的搭建。(这么说可能不太严谨,但是理解意思即可)
下面我们来写一个简单的神经网络

from torch import nnclass network(nn.Module):def __init__(self):super().__init__()def forward(self,input):output = input +1 #把输入加1 返回return output

可以看到,非常简单我们就完成了一个神经网络的骨架,或者说模板。这个简单的神经网络叫network,他的功能就是给他一个输入,他把输入+1然后输出。
我们测试一下这个简单的神经网络

import torch
from torch import nnclass network(nn.Module):def __init__(self):super().__init__()def forward(self,input):output = input +1 #把输入加1 返回return outputtest = network()  # 实例化一个神经网络
x = torch.tensor(1.0) # 把1.0转化为tensor类型
output = test(x) # 输入到神经网络中去  输出给output
print(output) # 打印一下

输出:tensor(2.) 证明成功了,那么一个简单的神经网络我们就搭建出来了。
补充两个点:
1、注意,x = torch.tensor(1.0)这里的t是小写的,如果是大写torch.Tensor()则是一个类,具体可以看这俩的区别:https://blog.csdn.net/qq_36930266/article/details/104602792
2、顺便这里解释一下,这里我们直接test(x),并没有写调用forward(),但是却执行了forword函数,这是因为这里调用了__call__
我们可以去看下module的源码,可以看到__call__里面写了_call_impl

再去看_call_impl里面写了一句话,就是在这里调用的forword的

nn.Conv2d 卷积层

上面我们搭建的那个神经网络实在是太过于简单。我们经常说卷积神经网络,那么自然要有卷积了
关于卷积操作,简单一句话就是:卷积核与图像对应位置相乘,然后再相加作为输出。滑动卷积核,重复操作。

关于卷积的操作可以看我写的这篇博文:https://zpf1900.blog.csdn.net/article/details/122377935。
放一个链接:https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md
这里面用动图生动形象的展示了卷积操作

这几个函数1d就是一维的,2d就是二维的,点进去都有函数用法的介绍。

我们主要介绍一下nn.Conv2d的用法

Class: torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode=‘zeros’, device=None, dtype=None)

说一下这几个参数。

  • in_channels:输入的通道数
  • out_channels:输出的通道数
  • kernel_size:卷积核的大小,类型为int 或者元组,当卷积是方形的时候,只需要一个整数边长即可,卷积不是方形,要输入一个元组表示 高和宽。(卷积核不需要你设置,只需要给定大小,里面的值是随机生成的)
  • stride:步长(就是每次挪动几个像素,默认是1)
  • padding:填充几圈,默认是0,不填充(填充的值为0)
  • dilation:控制卷积核之间的间距(设置这个可以做空洞卷积)
  • groups:控制输入和输出之间的连接
  • bias:偏置,是否将一个 学习到的 bias 增加输出中,默认是True
  • padding_mode:设置填充的模式

ps:参数 kernel_size,stride,padding,dilation 都可以是一个整数或者是一个元组,一个值的情况将会同时作用于高和宽 两个维度,两个值的元组情况代表分别作用于高和宽维度。
ps:再说一下通道数的问题,当我们只有一个通道的时候,就拿一个卷积核,对他卷积,最后输出也是一个通道。当我们是两个通道的时候,我们就拿两个卷积核(可以是不一样的),分别对两个通道进行卷积,最后得到的结果自然也是两个通道的,三个通道类似。
那么我们写代码试一试:

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
# 准备数据集 转化为tensor类型
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader 一次输入64个图片
dataloader = DataLoader(dataset,batch_size=64)
# 创建神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()# 定义一个卷积操作 输入通道3 输出通道6 卷积核3x3 不填充self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)def forward(self,x):x = self.conv1(x) # x输入进来 先进行一个卷积操作 结果仍然赋值给xreturn x # 输出test = network()
print(test) # 看一下这个神经网络的结构

输出结果:可以看到这个网络中有一个卷积层,卷积层的设置也列出来了。

我们用我们这个神经网络处理一个数据集试试:

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
# 准备数据集
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader
dataloader = DataLoader(dataset,batch_size=64)
# 创建神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()# 定义一个卷积操作 输入通道3 输出通道6 卷积核3x3 不填充self.conv = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)def forward(self,x):x = self.conv(x) # x输入进来 先进行一个卷积操作 结果仍然赋值给xreturn x # 输出test = network() # 实例化一个神经网络for data in dataloader: # 遍历dataloader 每次64个图像imgs,targets = dataprint(imgs.shape) # 看一下原始数据的大小output = test(imgs) # 把数据送入神经网络 处理后给outputprint(output.shape)# 看一下处理后的大小

输出结果:

可以看到,本来是64张图片,3通道,32x32大小。处理完之后变成了64张图片,6通道,30x30大小
(ps:关于通道数变化的计算,可以看我写的这篇博文https://zpf1900.blog.csdn.net/article/details/122377935)
这是因为我们设置的输出通道数为6,且没有设置填充,所以变成了30x30
我们可以使用之前学的tensorboard来展示一下处理后的图片,这样更加直观的看到卷积后的图像:

import torch
import torchvision
from tensorboardX import SummaryWriter
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
# 准备数据集
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader
dataloader = DataLoader(dataset,batch_size=64)
# 创建神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()# 定义一个卷积操作 输入通道3 输出通道6 卷积核3x3 不填充self.conv = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)def forward(self,x):x = self.conv(x) # x输入进来 先进行一个卷积操作 结果仍然赋值给xreturn x # 输出test = network() # 实例化一个神经网络
step = 0
writer = SummaryWriter("../logs")
for data in dataloader: # 遍历dataloader 每次64个图像imgs,targets = dataoutput = test(imgs) # 把数据送入神经网络 处理后给outputwriter.add_images("input",imgs,global_step=step) # 展示一下原始数据# 需要注意 我们设置的输出通道数是6 彩色图像通道数是3  这里直接显示会报错 他无法显示6个通道# 这里解释一下 本来是64张图片 6个通道 我们给他resize为3通道了 那么他是怎么处理的呢# 其实是把6个通道拆开 拆成两个3通道的 可以理解为一个6层的魔方 切开 变成两个三层的魔方(不严谨 理解意思即可)# 那么拆开 必然导致图片数量就多了 就不是64个了 这里写-1 代表自动计算 他会自动计算图片个数output = torch.reshape(output,(-1,3,30,30))writer.add_images("output", output, global_step=step)  # 展示一下输出数据step = step +1

运行代码,然后在终端执行tensorboard --logdir=logs
然后打开浏览器,结果如下:
input

output

看结果我们可以看到,本来是64张图片,处理完显示的时候显示了128张,这是通道数的原因(代码里有注释)
这就是对图像卷积操作后的样子。

nn.MaxPool2d 池化层


我们着重看一下nn.MaxPool2d这个函数

CLASS torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

这个很简单,参数主要需要一个kernel_size,类似前面说的卷积核,只不过这里不是卷积操作,而是拿这个3x3的窗口,去对应图像中对应的像素,然后取最大的即可。所以叫最大池化。
其他的参数,步长,填充什么的就不多说了。注意一下池化的时候步长stride默认会等于池化核的大小。stride = kernel_size
最后有个参数ceil_mode可以看一下,如果是True就用ceil模式,如果是false就用floor模式。所谓的floor就是向下取整,ceil就是向上取整。比如,下图这个池化操作,从左开始池化,然后到最右边的时候只剩下3x2了,并且没有填充。那么右边这里还要不要去池化呢?这就是看ceil还是floor了,ceil就要,floor就不要。

那么我们写代码试一试:
还有个要注意的:MaxPool2d要求输入的格式是这个样子的,所以输入的时候要注意一下

代码:

import torch
from torch import nn
from torch.nn import MaxPool2dinput = torch.tensor([[1,2,0,3,1],[0,1,2,3,1],[1,2,1,0,0],[5,2,3,1,1],[2,1,0,1,1]],dtype=torch.float) #池化一般是浮点数 所以这里转换一下 input = torch.reshape(input,(1,5,5)) # 转化成输入要求的格式
# 定义神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()self.maxpool = MaxPool2d(kernel_size=3,ceil_mode=True) # 池化操作def forward(self,x):x = self.maxpool(x) # 执行池化操作return xtest_network = network() # 实例化
output = test_network(input) # 把input输入到网络中去
print(output) # 打印结果

结果:


验证了我们刚才的结论:这里我们代码里设置了ceil_mode=True 所以是四个

我们也可以不设置ceil_mode,默认是false,结果就是一个2。可以试试
那么,通过代码我们可以看出来,最大池化的作用就是降低了数据量。本来5x5的,池化后变成2x2的,甚至成1x1的。数据量减少了,就可以加快我们的训练速度。同时还保留了原来的一些特征,只不过是用最大的那个代替了原来的3x3格。你也可以用别的方式,比如平均池化。
我们来用真实的数据集试一下池化操作,这样看的更直观

import torch
import torchvision
from tensorboardX import SummaryWriter
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
# 准备数据集
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader
dataloader = DataLoader(dataset,batch_size=64)# 定义神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()self.maxpool = MaxPool2d(kernel_size=3) # 池化操作def forward(self,x):x = self.maxpool(x) # 执行池化操作return xtest_network = network() # 实例化
writer = SummaryWriter("../logs")
step = 0
for data in dataloader:imgs,targets = datawriter.add_images("input_pool",imgs,global_step=step) # 显示下输入数据output = test_network(imgs) # 把图片输入进神经网络writer.add_images("output_pool", output, global_step=step) # 显示处理后的数据step =step + 1

显示效果:


有种马赛克的感觉,因为用局部代替整体了嘛。好了这就是池化。

Non-linear Activations 非线性激活

至于什么是激活函数?有什么作用?这些这里先不讲,回头用空了再说。可以百度或者看看书

这里我们介绍两个常用的激活函数

ReLU


看图,可以简单的看出来,他对我们的输入input做了一个截断,大于0的输出他本身,小于0的都输出0。(横坐标是input纵坐标是output)
这个ReLU只用给一个参数:inplace 意思是“是否原位操作”。举个例子,这个参数如果是True,那么输入input=-1 则处理完了之后input=0,但是如果这个参数是False,那就是不在原位进行操作,输入input=-1,处理问了之后,返回一个output=0,其input仍然为-1。这个参数默认就是False,一般我们也选择false。
我们来写代码试一试:

import torch
from torch import nn
from torch.nn import ReLU
# 定义输入数据
input = torch.tensor([[1,-0.5],[-1,3]])# 创建神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()self.relu = ReLU()def forward(self,input):output = self.relu(input) # ReLU 我们inplace默认是false 所以我们要返回output 保留inputreturn output # 输出test_network = network()
output = test_network(input)
print(output)

输出结果: 可以看到,小于0的做了一个截断

Sigmoid



我们用sigmoid处理一下图片看下效果:

import torch
import torchvision
from tensorboardX import SummaryWriter
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader# 准备数据集
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader
dataloader = DataLoader(dataset,batch_size=64)# tensorboard 可视化
writer = SummaryWriter("../logs")
step = 0
# 创建神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()self.relu = ReLU() # ReLUself.sigmoid = Sigmoid() # Sigmoiddef forward(self,input):output = self.sigmoid(input) # 让输入数据经过sigmoid处理return output # 输出test_network = network()
for data in dataloader:imgs,targets = datawriter.add_images("input_sigmoid", imgs, global_step=step)  # 显示输入数据output = test_network(imgs) # 用网络处理imgswriter.add_images("output_sigmoid", output, global_step=step)  # 显示输出数据step = step + 1

处理效果

nn.Linear 线性层

这个nn.linear需要设置几个参数

这都是什么意思呢

我们可以看下面这张图,其实in_features就是x1x_1x1​、x2x_2x2​、xdx_dxd​这些输入的数量,比如这个就是d
out_features其实就是g1g_1g1​、g2g_2g2​、gLg_LgL​这个输出的个数,比如这里就是L个
这计算方式是这样的,x1x_1x1​会乘上一个权重k,再加上一个偏置b,得到一个结果,x2x_2x2​同理,直到xdx_dxd​,得到d个结果,相加,就是g1g_1g1​,那么g2g_2g2​也是这种方法算出来的。

这里的权重k和偏置b不需要我们设置,他的初始化方式如下图。我们只需要设置一下bais是不是True,是True的话就是他会在网络训练中学习到b,并且加入。如果设置为false,那就不加b。默认是True,一般我们只需要给出in_features和out_features

然后我们来写代码试试:
写代码我们用这个例子,如图这个网络的最后,有一步处理
把一个1x1x4096 处理完之后是1x1x1000 其实就是把in_features设置为4096 out_features设置为1000

写代码之前我们先介绍一个操作:torch.flatten
他的作用很简单,就是把矩阵摊平。看例子,很容易看懂。

我们用之前的数据集试试

import torch
from torch.nn import Linear
from torch.utils.data import DataLoader
import torchvision
from torch import nn
# 准备数据集
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader
dataloader = DataLoader(dataset,batch_size=64)
for data in dataloader: # 遍历dataloaderimgs,targets = dataprint(imgs.shape) # 输出图像大小output = torch.flatten(imgs) # flatten操作print(output.shape) # 输出操作后的大小

运行结果:

我们可以看到,本来我们64张图片,3通道,32x32大小,摊平后,就成了196608(其实就是把像素排成一列,64x3x32x32=196608)
接下来我们写一个神经网络
把196608个输入,经过线性层处理为10个输出

import torch
from torch.nn import Linear
from torch.utils.data import DataLoader
import torchvision
from torch import nn# 准备数据集
dataset = torchvision.datasets.CIFAR10("../dataset/cifar",train=False,transform=torchvision.transforms.ToTensor(),download=False)
# 设置dataloader
dataloader = DataLoader(dataset,batch_size=64)# 创建神经网络
class network(nn.Module):def __init__(self):super(network, self).__init__()self.linear = Linear(196608,10) # 指定in_features和out_featuresdef forward(self,input):output = self.linear(input)return output # 输出test_network = network()for data in dataloader: # 遍历dataloaderimgs,targets = dataprint(imgs.shape) # 原始图像的shapeoutput = torch.flatten(imgs)# flatten处理print(output.shape)  # 输出处理后的大小output = test_network(output) # 送入网络处理print(output.shape) #经过网络后的大小

运行结果:

其他层

如图,蓝色框是本文已经说了的网络层(其中padding layer层没说,他就跟那个卷积层设置padding是差不多的)
剩下的没说的,用到可以再看官方文档,其实官网文档写的比较清楚,用法示例什么的都有

小小实战

根据前面学习的,我们来搭建一个神经网络试试

我们以CIFAR10的网络模型为例子,尝试写一个简单的神经网络试试
网络架构就按照下图来写:

写之前先观察这个图,我们可以看到,输入数据input是3通道,32x32的图像(也就是cifar10的数据集),然后呢经过一个卷积层,卷积核是5x5的,得到了32通道,32x32大小的数据。然后经过一个池化层,池化核是2x2的,得到了32通道,16x16的数据,然后再进行卷积,卷积核是5x5,卷完仍然是32通道,16x16的图像,然后经过一个池化层,依然是2x2池化核,得到了32通道,8x8大小的,然后再卷积,卷积得到64通道,8x8的数据,然后继续池化,变成64通道,4x4的数据,然后展平操作Flatten,得到一列长度为64x4x4。然后拿这一列经过一个线性层(全连接层),处理为64长度,然后再经过一个线性层,处理为10。输出,结束!

这里有个问题需要解决,我们输入3通道,32x32的图像,经过第一次卷积,得到了32通道,32x32大小的图像。他告诉你了卷积核大小是5x5,但是步长和padding是多少呢,这需要我们自己算一下。有一个公式,在pytorch官网可以看到:
那我们呢可以算一下,Hout=32,Hin=32,padding不知道,dilation也不知道,但是我们按照默认的1来设置,核大小是5,那么这个式子就是32=[(32+2padding - 1(5-1)-1)/stride]+1 。化简一下可以得到,31=(27+2*padding)/stride。那么这个式子,我们假设步长stride=1,即可得到padding=2,假设步长=2呢,就得到padding=48.5,显然这个答案不现实,所以他的stride是1,padding是2。
后面的层也同理,可以计算出他的步长和padding

下面就开始写这个网络
按照我们刚才分析的挨个层来写

from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linearclass Network(nn.Module):def __init__(self):super(Network, self).__init__()self.conv1 = Conv2d(3,32,5,padding=2) # 卷积层self.maxpool1 = MaxPool2d(2) # 池化层self.conv2 = Conv2d(32,32,5,padding=2) # 卷积层self.maxpool2 = MaxPool2d(2) # 池化层self.conv3 = Conv2d(32, 64, 5, padding=2) # 卷积层self.maxpool3 = MaxPool2d(2) # 池化层self.flatten = Flatten() # 展平self.linear1 = Linear(1024,64) # 线性层self.linear2 = Linear(64, 10) # 线性层def forward(self,x):x = self.conv1(x)x = self.maxpool1(x)x = self.conv2(x)x = self.maxpool2(x)x = self.conv3(x)x = self.maxpool3(x)x = self.flatten(x)x = self.linear1(x)x = self.linear2(x)return x
# 实例化这个网络
test = Network()
print(test)

输出结果:

Network(
(conv1): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(maxpool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(maxpool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv3): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(maxpool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear1): Linear(in_features=1024, out_features=64, bias=True)
(linear2): Linear(in_features=64, out_features=10, bias=True)
)

可以看到我们这个网络就写好了,每一层都是按照我们刚才分析的要求来写的
那么我们该如何验证我们的网络正确不正确呢,我们可以设置一个输入数据试一下
torch.ones()可以帮我们创建全是1的数据,并且可以指定他的大小
比如torch.ones((64,3,32,32))就可以创建出我们刚才那个网络所需要的输入类型,里面填充的全是1
使用这个数据就可以测试一下刚才我们写好的网络:

test = Network()
# print(test)
input = torch.ones((64,3,32,32))
output = test(input)
print(output.shape)

输出结果:torch.Size([64, 10])
64代表64个图片,长度为1,这就是我们刚才那个网络最终的输出形状,所以是对的
假如我们刚才前面那些参数有计算错误的,导致我们写的网络有问题,那么就会报错。

然后呢,这样写有些麻烦,所以下面我们介绍一个东西叫Sequential
用法很简单,看代码就懂了,这样我们的forward就可以很简洁的写了,他会依次经过Sequential里面写的层

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequentialclass Network(nn.Module):def __init__(self):super(Network, self).__init__()self.model1 = Sequential(Conv2d(3, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 32, 5, padding=2),MaxPool2d(2),Conv2d(32, 64, 5, padding=2),MaxPool2d(2),Flatten(),Linear(1024, 64),Linear(64, 10),)def forward(self,x):x = self.model1(x)return x
# 实例化这个网络
test = Network()
# print(test)
input = torch.ones((64,3,32,32))
output = test(input)
print(output.shape)

PyTorch入门(二)从零开始搭建一个神经网络相关推荐

  1. spring boot:从零开始搭建一个项目 - day 7 springboot devtools热加载+MybatisPlus配置+kisso从入门到放弃

    spring boot:从零开始搭建一个项目 - day 7 springboot devtools热加载+MybatisPlus配置+kisso从入门到放弃 一.springboot devtool ...

  2. 从零学习pytorch 第1课 搭建一个超简单的网络

    课程目录(在更新,喜欢加个关注点个赞呗): 从零学习pytorch 第1课 搭建一个超简单的网络 从零学习pytorch 第1.5课 训练集.验证集和测试集的作用 从零学习pytorch 第2课 Da ...

  3. 从零开始搭建一个语音对话机器人

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] ♪ 点击上方绿标 收听从零开始搭建一个语音对话机器人 从零开始搭建一个语音对话机器人 目录 01-初心缘由 01-准备工作 02-语音机器 ...

  4. espeak 中文语音包_从零开始搭建一个语音对话机器人

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] ♪ 点击上方绿标 收听从零开始搭建一个语音对话机器人 从零开始搭建一个语音对话机器人 目录 01-初心缘由 01-准备工作 02-语音机器 ...

  5. 深度学习笔记:利用numpy从零搭建一个神经网络

    很多人说深度学习就是个黑箱子,把图像预处理之后丢进 tensorflow 就能出来预测结果,简单有效又省时省力.但正如我在上一篇推送中所说,如果你已是一名功力纯厚的深度学习工程师,这么做当然没问题.但 ...

  6. 【Vue 快速入门】从零开始搭建 VUE + Element UI后台管理系统框架

    [Vue 快速入门]从零开始搭建 VUE + Element UI后台管理系统框架 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条 ...

  7. 以太坊开发入门,如何搭建一个区块链DApp投票系统

    点击关注异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 第一节 概述 对于初学者,需要了解以太坊开发相关的基本概念,另外就是如何构建一个基于以太坊的完整去中心化应用例如一个区块链投票 ...

  8. 从零开始搭建一个GIS开发小框架(一)——基本框架

    目录 1.概述 1.1 项目背景 1.2 传送门 2.技术选型:GMap.NET 3.底图功能实现 2.1 方式一:在线地图(以高德为例) 2.2 greatmaps生成底图(瓦片地图) 3.搭建主程 ...

  9. spring boot:从零开始搭建一个项目 - day 4 控制台输出日志美化 + swagger2

    spring boot:从零开始搭建一个项目 - day 4 控制台输出日志美化 + swagger2 一.logback.xml配置日志美化 二.集成swagger2 1.引入依赖 2.编写配置文件 ...

  10. spring boot:从零开始搭建一个项目 - day8 实现jwtToken验证

    锻炼不就是为了吃更多好吃的吗 --刚去完健身房然后开了包薯片=-= spring boot:从零开始搭建一个项目 - day8 实现jwtToken验证 一.生成token 二.重写拦截器 咳,书接上 ...

最新文章

  1. 什么时候应该避免写代码注释?
  2. RabbitMQ 关键词解释
  3. 使用ANNdotNET GUI工具创建CIFAR-10深度学习模型
  4. Delphi XE组件开发技术
  5. Map集合遍历的四种方式
  6. SS端口已被占用 1080
  7. 佛系 vue -01
  8. u盘无法读出显示计算机被锁定,U盘无法打开,提示被写保护了怎么办?
  9. 选择华为账号第三方登录,让你无法拒绝的3个理由
  10. 以喷管雷诺数,当地大气压及射流有效温度计算所需质量流量,静温
  11. markdown 目录一键生成和转为 word 格式
  12. 连接远程桌面提示“无法验证此远程计算机的身份”的解决办法
  13. 六级(2020/12-2) Text2
  14. 都2022年了,还在争论编程语言?
  15. 如何理解「外汇储备导致人民币货币增加」?
  16. awk ERES 基础表达式符号介绍
  17. 妈蛋,终于在TQ2440上点亮一个led了。。。
  18. 确定账号登录和密码确认
  19. python爬虫爬取b站_python爬虫11 | 这次,将带你使用python爬取b站上的NBA形象大使蔡徐坤和他的球友们-Go语言中文社区...
  20. 修改maven配置文件settings.xml(阿里云镜像,下载速度快)

热门文章

  1. UITableView介绍 之 复杂cell的高度计算
  2. cactiez的monitor主机名乱码
  3. 批量删除多台linux服务器文件
  4. [CLR团队公告]CLR基础研究团队纲领
  5. redis通过lua脚本实现分布式锁
  6. C++ 派生类和virtual
  7. img的属性alt 与 title的区别
  8. python ConfigParser模块 配置文件解析
  9. 数据挖掘 自习笔记 第三章 定性归纳实践(下)
  10. JSTL(c标签)与Struts2(s标签)标签的常用功能对比