《Pytorch深度学习和图神经网络(卷 1)》学习笔记——第七章

这一章内容有点丰富,多用了一些时间,实例就有四五个。
这章内容是真多啊!(学完之后又回到开头感叹)

大脑分级处理机制:

将图像从基础像素到局部信息再到整体信息
即将图片由低级特征到高级特征进行逐级计算,逐级累计。

视觉神经网络中的离散积分

计算机中对图片的处理可以理解为离散微积分的过程。
利用卷积操作对图片的局部信息处理,生成低级特征
对低级特征进行多次卷积操作,生成中级特征、高级特征
将多个局部信息的高级特征组合到一起,生成最终的解释结果
这就是卷积神经网络

比全连接权重更少,对数据进行基于区域小规模运算,改善了难收敛,提高了泛化能力。

可以用全连接网络为参照,卷积神经网络更像是多个全连接片段的组合。

卷积网络输出的每个节点,都是原数据中局部区域节点经过神经元计算后得到的结果。
全连接网络输出的每个节点,都是原数据中全部节点经过神经元计算后得到的结果。
因此卷积神经网络包含的局部信息更为明显,在计算机视觉领域被广泛应用。

1D卷积常用来处理文本或特征数值类数据
2D卷积常用来处理平面图片类数据
3D卷积常用来处理立体图像或视频类数据

实例分析:Sobel算子的原理
Sobel算子其实是卷积操作中的一个典型例子,手动配置好权重的卷积核,实现图片的边缘检测。
a为水平 b为垂直方向

a=[[-1,0,1],
	[-2,0,2],
	[-1,0,1]]
b=[[1,2,1],
	[0,0,0],
	[-1,-2,1]]

新生成的像素值不一定在0到256之间,要进行归一化然后乘256。

sobel算子原理

第一行(-1,0,-1)的卷积核进行1D卷积时,本质上是计算相隔像素之间的距离。
1256788卷积后44221,少了2位因为核的缘故。
如果将像素差值数据用图片的方式显示出来,就是轮廓图片。
第二行(-2,0,2)的同上,只不过将距离放大2倍,起到增强效果。
思想是,对卷积核3行像素差值再做加权处理,以第二行像素差值为中心,距离中心点近影响越大的原理,对第二行加强,在结果中产生主要影响。
第二行与第一行相同也可以产生轮廓,OpenCV中有scharr函数…实sobel变了权重。
垂直其实就是水平算子的转置。

深层神经网络中的卷积核

这时的卷积核是经过大量样本训练之后计算出来的,若生成了若干个有特定功能的卷积核,有的计算像素差值,提取轮廓特征;有的计算平均值,提取纹理特征。

卷积分

y = 3 x + 2. y = 3x+2. y=3x+2.
y = 2 x 2 + 3 x − 1. y = 2x^{2}+3x-1. y=2x2+3x1.
代数的角度理解是相乘
y = 6 x 3 + 13 x 2 + 3 x − 2. y = 6x^{3}+13x^{2}+3x-2. y=6x3+13x2+3x2.

卷积神经网络的实现

卷积的操作类型:
窄卷积、同卷积、全卷积。
计算规则:
H o u t = H i n + 2 × p a d d i n g [ 0 ] − d i l a t i o n [ 0 ] × ( k e r n e l s i z e [ 0 ] − 1 ) − 1 s t r i d e [ 0 ] + 1. H_{out} = \frac{H_{in}+2\times padding[0]-dilation[0]\times (kernel_size[0]-1)-1}{stride[0]}+1. Hout=stride[0]Hin+2×padding[0]dilation[0]×(kernelsize[0]1)1+1.
W o u t = W i n + 2 × p a d d i n g [ 1 ] − d i l a t i o n [ 1 ] × ( k e r n e l s i z e [ 1 ] − 1 ) − 1 s t r i d e [ 1 ] + 1. W_{out} = \frac{W_{in}+2\times padding[1]-dilation[1]\times (kernel_size[1]-1)-1}{stride[1]}+1. Wout=stride[1]Win+2×padding[1]dilation[1]×(kernelsize[1]1)1+1.

实例6:卷积函数应用

观察卷积核个数和图像通道数对卷积核维度的影响。

import torch
# [batch, in_channels, in_height, in_width] [训练时一个batch的图片数量, 图像通道数, 图片高度, 图片宽度]
input1 = torch.ones([1, 1, 5, 5])
input2 = torch.ones([1, 2, 5, 5])
input3 = torch.ones([1, 1, 4, 4])
# [ out_channels, in_channels,filter_height, filter_width] [卷积核个数,图像通道数,卷积核的高度,卷积核的宽度]
filter1 =  torch.tensor([-1.0,0,0,-1]).reshape([1, 1, 2, 2])
filter2 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1]).reshape([2,1,2, 2])
filter3 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1,-1.0,0,0,-1]).reshape([3,1,2, 2])
filter4 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1,
                                   -1.0,0,0,-1,
                                   -1.0,0,0,-1]).reshape([2, 2, 2, 2])
filter5 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1]).reshape([1,2, 2, 2])

print(filter1)
print(filter2)
print(filter3)
print(filter4)
print(filter5)
#tensor([[[[-1.,  0.],
#          [ 0., -1.]]]])
卷积核个数X图像通道数=1,共一个卷积核单通道
#tensor([[[[-1.,  0.],
#          [ 0., -1.]]],
#
#        [[[-1.,  0.],
#          [ 0., -1.]]]])
2X1=2,两个卷积核单通道
#tensor([[[[-1.,  0.],
#          [ 0., -1.]]],
#
#        [[[-1.,  0.],
#          [ 0., -1.]]],
#
#        [[[-1.,  0.],
#          [ 0., -1.]]]])
3X1=3,三个卷积核,单通道
#tensor([[[[-1.,  0.],
#          [ 0., -1.]],
#         [[-1.,  0.],
#          [ 0., -1.]]],
#
#        [[[-1.,  0.],
#          [ 0., -1.]],
#         [[-1.,  0.],
#          [ 0., -1.]]]])
2X2=4,两个卷积核且两个通道
#tensor([[[[-1.,  0.],
#          [ 0., -1.]],
#         [[-1.,  0.],
#          [ 0., -1.]]]])
1X2=2,一个卷积核且两个通道
[ ]里为一个通道,[[]]里为一个卷积核

观察padding填充,padding=(1,2)是(上,下)填充,padding=1,为对周围填充一圈。

#验证padding补0的规则 ——上下左右都补0
padding1 = torch.nn.functional.conv2d(input1, torch.ones([1,1,1,1]), stride=1, padding=1)
print(padding1)
#tensor([[[[0., 0., 0., 0., 0., 0., 0.],   
#          [0., 1., 1., 1., 1., 1., 0.],   
#          [0., 1., 1., 1., 1., 1., 0.],   
#          [0., 1., 1., 1., 1., 1., 0.],   
#          [0., 1., 1., 1., 1., 1., 0.],   
#          [0., 1., 1., 1., 1., 1., 0.],   
#          [0., 0., 0., 0., 0., 0., 0.]]]])
padding2 = torch.nn.functional.conv2d(input1, torch.ones([1,1,1,1]), stride=1, padding=(1,2))
print(padding2)
#tensor([[[[0., 0., 0., 0., 0., 0., 0., 0., 0.],
#          [0., 0., 1., 1., 1., 1., 1., 0., 0.],
#          [0., 0., 1., 1., 1., 1., 1., 0., 0.],
#          [0., 0., 1., 1., 1., 1., 1., 0., 0.],
#          [0., 0., 1., 1., 1., 1., 1., 0., 0.],
#          [0., 0., 1., 1., 1., 1., 1., 0., 0.],
#          [0., 0., 0., 0., 0., 0., 0., 0., 0.]]]])

几个卷积核生成几个特征图,几个通道数相加为一个通道图

op1:单卷积核单通道,生成1个feature map
 tensor([[[[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]]]])
op2:双卷积核单通道,生成2个feature map
 tensor([[[[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]],

         [[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]]]])
op3:三卷积核单通道,生成3个feature map
 tensor([[[[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]],

         [[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]],

         [[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]]]])
op4:双卷积核双通道,生成2个feature map
 tensor([[[[-2., -2., -2.],
          [-2., -4., -4.],
          [-2., -4., -4.]],

         [[-2., -2., -2.],
          [-2., -4., -4.],
          [-2., -4., -4.]]]]) 
op5:单卷积核双通道,生成1个feature map
 tensor([[[[-2., -2., -2.],
          [-2., -4., -4.],
          [-2., -4., -4.]]]]) 
---------------------------------------
op1:
 tensor([[[[-1., -1., -1.],
          [-1., -2., -2.],
          [-1., -2., -2.]]]]) 
op6:不加padding
 tensor([[[[-2., -2.],
          [-2., -2.]]]]) 

自行设计了单卷积核三通道,证明上述分析。

# [batch, in_channels, in_height, in_width] [训练时一个batch的图片数量, 图像通道数, 图片高度, 图片宽度]5])
input8 = torch.ones([1, 3, 5, 5])
op8 = torch.nn.functional.conv2d(input8, filter8, stride=2, padding=1) 
# [ out_channels, in_channels,filter_height, filter_width] [卷积核个数,图像通道数,卷积核的高度,卷积核的宽度]
filter8= torch.tensor([
                        [[[-1.,  0.],
                          [ 0., -1.]],
                         [[-1.,  0.],
                          [ 0., -1.]],
                         [[-1.,  0.],
                          [ 0., -1.]]]
                      ]).reshape([1,3, 2, 2])
print("op8:\n",op8,filter8)
tensor([[[[-3., -3., -3.],
          [-3., -6., -6.],
          [-3., -6., -6.]]]]) 

完整代码:

import torch
# [batch, in_channels, in_height, in_width] [训练时一个batch的图片数量, 图像通道数, 图片高度, 图片宽度]
input1 = torch.ones([1, 1, 5, 5])
input2 = torch.ones([1, 2, 5, 5])
input3 = torch.ones([1, 1, 4, 4])
# [ out_channels, in_channels,filter_height, filter_width] [卷积核个数,图像通道数,卷积核的高度,卷积核的宽度]
filter1 =  torch.tensor([-1.0,0,0,-1]).reshape([2, 2, 1, 1])
filter2 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1]).reshape([2,1,2, 2])
filter3 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1,-1.0,0,0,-1]).reshape([3,1,2, 2])
filter4 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1,
                                   -1.0,0,0,-1,
                                   -1.0,0,0,-1]).reshape([2, 2, 2, 2])
filter5 =  torch.tensor([-1.0,0,0,-1,-1.0,0,0,-1]).reshape([1,2, 2, 2])

#class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
#condv = torch.nn.Conv2d(1,1,kernel_size=1,padding=1, bias=False)
#condv.weight = torch.nn.Parameter(torch.ones([1,1,1,1]))
#padding1 = condv(input1)
#print(padding1)

#验证padding补0的规则 ——上下左右都补0
padding1 = torch.nn.functional.conv2d(input1, torch.ones([1,1,1,1]), stride=1, padding=1)
print(padding1)


padding2 = torch.nn.functional.conv2d(input1, torch.ones([1,1,1,1]), stride=1, padding=(1,2))
print(padding2)

##1个通道输入,生成1个feature map
#filter1 =  torch.tensor([-1.0,0,0,-1]).reshape([1, 1, 2, 2])
#op1 = torch.nn.functional.conv2d(input1, filter1, stride=2, padding=1)
#print('\n')
#print(padding1)
#print(filter1)
#print(op1)

#torch.nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
#torch.nn.functional.conv1d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)
#torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)





op1 = torch.nn.functional.conv2d(input1, filter1, stride=2, padding=1) #1个通道输入,生成1个feature map
op2 = torch.nn.functional.conv2d(input1, filter2, stride=2, padding=1) #1个通道输入,生成2个feature map
op3 = torch.nn.functional.conv2d(input1, filter3, stride=2, padding=1) #1个通道输入,生成3个feature map

op4 = torch.nn.functional.conv2d(input2, filter4, stride=2, padding=1) # 2个通道输入,生成2个feature
op5 = torch.nn.functional.conv2d(input2, filter5, stride=2, padding=1) # 2个通道输入,生成一个feature map

op6 = torch.nn.functional.conv2d(input1, filter1, stride=2, padding=0) # 5*5 对于pading不同而不同


print("op1:\n",op1,filter1)#1-1  后面补0
print("------------------")

print("op2:\n",op2,filter2) #1-2多卷积核 按列取
print("op3:\n",op3,filter3) #1-3
print("------------------")

print("op4:\n",op4,filter4)#2-2    通道叠加
print("op5:\n",op5,filter5)#2-1
print("------------------")

print("op1:\n",op1,filter1)#1-1
print("op6:\n",op6,filter1)

实例7:使用卷积提取图片的轮廓

shape为(3264,2448,3)
transforms.ToTensor类能将图片转化为Pytorch所支持的形状(【通道数,高,宽】),同时将图片数值归一化为0到1的小数
sobelfilter = torch.tensor([[-1.0,0,1], [-2,0,2], [-1.0,0,1.0]]*3).reshape([1,3,3, 3])
三通道就乘三

import matplotlib.pyplot as plt # plt 用于显示图片
import matplotlib.image as mpimg # mpimg 用于读取图片
import torch
import torchvision.transforms as transforms

myimg = mpimg.imread('pytorch\chapter7\img.jpg') # 读取和代码处于同一目录下的图片
plt.imshow(myimg) # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()
print(myimg.shape)
(3264, 2448, 3)

pil2tensor = transforms.ToTensor()
rgb_image = pil2tensor(myimg)
print(rgb_image[0][0])
tensor([0.8471, 0.8471, 0.8471,  ..., 0.6824, 0.6824, 0.6824])
print(rgb_image.shape)
torch.Size([3, 3264, 2448])

sobelfilter =  torch.tensor([[-1.0,0,1],  [-2,0,2],  [-1.0,0,1.0]]*3).reshape([1,3,3, 3])
print(sobelfilter)
tensor([[[[-1.,  0.,  1.],
          [-2.,  0.,  2.],
          [-1.,  0.,  1.]],

         [[-1.,  0.,  1.],
          [-2.,  0.,  2.],
          [-1.,  0.,  1.]],

         [[-1.,  0.,  1.],
          [-2.,  0.,  2.],
          [-1.,  0.,  1.]]]])

op =torch.nn.functional.conv2d(rgb_image.unsqueeze(0), sobelfilter, stride=3,padding = 1) #3个通道输入,生成1个feature map

#对卷积结果进行处理,数据不能保证在0到255内,必须归一化再乘255
ret = (op - op.min()).div(op.max() - op.min())
ret =ret.clamp(0., 1.).mul(255).int()
print(ret)
tensor([[[[193,  99,  99,  ...,  99,  99,  99],
          [225,  99,  99,  ...,  99, 100,  99],
          [224,  99,  99,  ...,  99, 100,  99],
          ...,
          [177,  97, 100,  ...,  95, 100, 100],
          [178, 100, 100,  ..., 100,  98,  97],
          [177,  99,  98,  ...,  96, 100,  98]]]], dtype=torch.int32)


plt.imshow(ret.squeeze(),cmap='Greys_r') # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()

op=torch.nn.functional.max_pool2d(op,kernel_size =5, stride=5)
op = op.transpose(1,3).transpose(1,2)
print(op.shape)
torch.Size([1, 217, 163, 1])

在这里插入图片描述
在这里插入图片描述

关于灰度图,对3个通道的图片取平均值,或计算图片0维上的平均值。

r_image=rgb_image[0]
g_image=rgb_image[0]
b_image=rgb_image[0]
grayscale_image=(r_image=rgb_image[0]+g_image=rgb_image[0]+b_image=rgb_image[0]).div(3.0)
plt.imshow(grayscale_image,cmap='Greys_r') # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()
或者
plt.imshow(rgb_image.mean(),cmap='Greys_r') # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()

深层卷积神经网络

是多个卷积层和若干其他的神经网络叠加在一起的,原始的主要是输入、卷积、池化、全连接(或全局平均池化层)等部分组成。
输入层:将每个像素作为一个特征节点输入网络
卷积层:多个滤波器组合而成
池化层:将卷积结果降维
全局平均池化层:对生成的特征图取全局平均值,该层也可以用全连接网络代替
输出层:网络需要将数据分成几类,就输出几个节点。

卷积神经网络的反向传播

卷积操作反向求导时,要将特征图做一次padding再与转置后的卷积核做一次卷积操作,即可得到输入端的误差,实现误差的反向传播。

池化操作

主要目的是降维,在保持原有特征的基础上最大限度的将数组的维度变小。
池化只关心滤波器的尺寸,不考虑内部的值,算法是将滤波器映射区域内的像素点取平均值或最大值。
有均值池化(对背景信息更敏感)和最大池化(对纹理特征信息更敏感)。
也有两种实现方式,函数调用和类的方式

实例8:池化函数的应用

手动生成一个4x4的矩阵来模拟图片,两个通道,定义一个2x2的滤波器
pooling3是常用的操作手法,也称全局池化法,与输入数据经两次平均值计算结果数值一致,只有形状不同。

import torch

img=torch.tensor([ [ [0.,0.,0.,0.],[1.,1.,1.,1.],[2.,2.,2.,2.],[3.,3.,3.,3.] ],
                   [ [4.,4.,4.,4.],[5.,5.,5.,5.],[6.,6.,6.,6.],[7.,7.,7.,7.] ]
                 ]).reshape([1,2,4,4])
print(img)
#两个通道
tensor([[[[0., 0., 0., 0.],
          [1., 1., 1., 1.],
          [2., 2., 2., 2.],
          [3., 3., 3., 3.]],

         [[4., 4., 4., 4.],
          [5., 5., 5., 5.],
          [6., 6., 6., 6.],
          [7., 7., 7., 7.]]]])
print(img[0][0])
tensor([[0., 0., 0., 0.],
        [1., 1., 1., 1.],
        [2., 2., 2., 2.],
        [3., 3., 3., 3.]])
print(img[0][1])
tensor([[4., 4., 4., 4.],
        [5., 5., 5., 5.],
        [6., 6., 6., 6.],
        [7., 7., 7., 7.]])
#torch.nn.functional.avg_pool2d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True)
pooling=torch.nn.functional.max_pool2d(img,kernel_size =2)
print("pooling:\n",pooling)
pooling:
 tensor([[[[1., 1.],
          [3., 3.]],

         [[5., 5.],
          [7., 7.]]]])
pooling1=torch.nn.functional.max_pool2d(img,kernel_size =2,stride=1)
print("pooling1:\n",pooling1)
pooling1:
 tensor([[[[1., 1., 1.],
          [2., 2., 2.],
          [3., 3., 3.]],

         [[5., 5., 5.],
          [6., 6., 6.],
          [7., 7., 7.]]]])

pooling2=torch.nn.functional.avg_pool2d(img,kernel_size =4,stride=1,padding=1)
print("pooling2:\n",pooling2)
pooling2:
 tensor([[[[0.5625, 0.7500, 0.5625],
          [1.1250, 1.5000, 1.1250],
          [1.1250, 1.5000, 1.1250]],

         [[2.8125, 3.7500, 2.8125],
          [4.1250, 5.5000, 4.1250],
          [3.3750, 4.5000, 3.3750]]]])
pooling3=torch.nn.functional.avg_pool2d(img,kernel_size =4)
print("pooling3:\n",pooling3)
pooling3:
 tensor([[[[1.5000]],

         [[5.5000]]]])
m1 = img.mean(3)
print("第1次平均值结果:\n",m1)1次平均值结果:
 tensor([[[0., 1., 2., 3.],
         [4., 5., 6., 7.]]])
print("第2次平均值结果:\n",m1.mean(2))2次平均值结果:
 tensor([[1.5000, 5.5000]])

上述可以修改,结果等价

img=torch.tensor( [ [0.,0.,0.,0.],[1.,1.,1.,1.],[2.,2.,2.,2.],[3.,3.,3.,3.] ,
                    [4.,4.,4.,4.],[5.,5.,5.,5.],[6.,6.,6.,6.],[7.,7.,7.,7.] ]
                 ).reshape([2,4,4])
m1 = img.mean(2)
print("第2次平均值结果:\n",m1.mean(1))

实例9:搭建卷积神经网络

对第六章实例5进行修改,将2个全连接变为全局平均池化层。
将最后3个全连接层,改为1个卷积层和1个全局平均池化层,卷积核由5改为3
替换一下网络类定义就行了

class myConNet(torch.nn.Module):
    def __init__(self):
        super(myConNet, self).__init__()
        #定义卷积层
        self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=6, kernel_size=3)
        self.conv2 = torch.nn.Conv2d(in_channels=6, out_channels=12, kernel_size=3)
        self.conv3 = torch.nn.Conv2d(in_channels=12, out_channels=10, kernel_size=3)

    def forward(self, t):#搭建正向结构
        #第一层卷积和池化处理
        t = self.conv1(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size=2, stride=2)
        #第二层卷积和池化处理
        t = self.conv2(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size=2, stride=2)

        #第三层卷积和池化处理
        t = self.conv3(t)
        t = F.avg_pool2d(t, kernel_size=t.shape[-2:], stride=t.shape[-2:])

        return t.reshape(t.shape[:2])
    #训练完记得
    torch.save(network.state_dict(),'./CNNFashionMNIST2.pth')#保存模型
    network.load_state_dict(torch.load( './CNNFashionMNIST2.pth'))#加载模型

测试结果:
Accuracy of T-shirt : 66 %
Accuracy of Trouser : 92 %
Accuracy of Pullover : 72 %
Accuracy of Dress : 80 %
Accuracy of Coat : 68 %
Accuracy of Sandal : 94 %
Accuracy of Shirt : 58 %
Accuracy of Sneaker : 91 %
Accuracy of Bag : 95 %
Accuracy of Ankle_Boot : 96 %
Accuracy of all : 81 %
对比之前的模型在某些类别上提升很明显:
Accuracy of Trouser : 90 %
Accuracy of Pullover : 49 %
Accuracy of Dress : 85 %
Accuracy of Coat : 81 %
Accuracy of Sandal : 92 %
Accuracy of Shirt : 42 %
Accuracy of Sneaker : 91 %
Accuracy of Bag : 94 %
Accuracy of Ankle_Boot : 94 %
Accuracy of all : 80 %

循环神经网络(Recurrent Neural Network ,RNN)

是一个具有记忆功能的网络,它可以发现样本彼此间的相互关系,它多用于处理带有序列特征的样本数据。
人的记忆原理。
婴儿虽然说话能表达意思,但有时会很奇怪,要零食的时候说把“我要”说成“要我”,大脑对这两个字是有先后顺序的。
当获得“我来找你玩游”的时候,大脑语言模型会自动匹配“戏”,而不是游泳、游乐。
用下列伪代码表示逻辑:
(input我+empty-input)→output我
(input来+output我)→output来
(input找+output来)→output找
(input你+output找)→output你
如让小孩背三字经,名俱扬下一句很容易说,问上一句是什么,小孩从头背了一遍。
对于序列化的特征任务,如情感分析、关键字提取、语音识别、机器翻译、股票分析等等适合循环神经网络来解决。

基本结构是,将全连接网络的输出节点复制一份,传回到输入节点,与输入数据一起进行下一次计算。

实例10: 简单循环神经网络实现——设计一个退位减法器

定义基本函数,手写sigmoid及其导数(用于反向传播)

import copy, numpy as np
np.random.seed(0) #随机数生成器的种子,可以每次得到一样的值
# compute sigmoid nonlinearity
def sigmoid(x): #激活函数
    output = 1/(1+np.exp(-x))
    return output
# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative(output):#激活函数的导数
    return output*(1-output)

建立二进制映射,将减法允许最大值设置为255,即8位二级制,定义int与二进制之间的映射组int2binary。

int2binary = {} #整数到其二进制表示的映射
binary_dim = 8 #暂时制作256以内的减法
## 计算0-256的二进制表示
largest_number = pow(2,binary_dim)
binary = np.unpackbits(
    np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
for i in range(largest_number):
    int2binary[i] = binary[i]
print(int2binary)
{0: array([0, 0, 0, 0, 0, 0, 0, 0], dtype=uint8), 1: array([0, 0, 0, 0, 0, 0, 0, 1], dtype=uint8), 2: array([0, 0, 0, 0, 0, 0, 
1, 0], dtype=uint8), 3: array([0, 0, 0, 0, 0, 0, 1, 1], dtype=uint8), 4: array([0, 0, 0, 0, 0, 1, 0, 0], dtype=uint8), 5: array([0, 0, 0, 0, 0, 1, 0, 1], dtype=uint8)..........}

定义参数
隐藏层的权重为synapse_0,循环节点的权重为synapse_h(输入16节点、输出16节点),输出层的权重为synapse_1(输入16节点输出1节点)。
synapse_0_update在前面很少见,是因为它被隐藏在优化器里了,这里是自动手写,需要定义一组变量来存放反向优化参数时需要调整的值。对于前面3个权重synapse_0到3。

# input variables
alpha = 0.9 #学习速率
input_dim = 2 #输入的维度是2
hidden_dim = 16 
output_dim = 1 #输出维度为1

# initialize neural network weights
synapse_0 = (2*np.random.random((input_dim,hidden_dim)) - 1)*0.05 #维度为2*16, 2是输入维度,16是隐藏层维度
synapse_1 = (2*np.random.random((hidden_dim,output_dim)) - 1)*0.05
synapse_h = (2*np.random.random((hidden_dim,hidden_dim)) - 1)*0.05
# => [-0.05, 0.05),

# 用于存放反向传播的权重更新值
synapse_0_update = np.zeros_like(synapse_0)
synapse_1_update = np.zeros_like(synapse_1)
synapse_h_update = np.zeros_like(synapse_h)

准备样本数据
建立循环生成样本数据,先生成两个数a和b。如果a小于b,就交换位置,保证被减数大。计算出相减结果c,将3个数转化为二进制,为模型计算做准备。
模型初始化
初始化输出值为0,初始化总误差为0,定义layer_2_deltas为存储反向传播过程中的循环层的误差,layer_1_values为隐藏层的输出值。由于第一个数据输入时,没有前面隐藏层输出值来作为本次的输入,因此需要定义一个初始值,这里初始化为0.1。
正向传播
future_layer_1_delta = np.zeros(hidden_dim)是为了反向传播准备的初始化,反向传播是从正向传播的最后一次计算开始反向计算误差,它没有后一次的输出,因此要初始化一个值作为其后一次的输入,这里初始化为0。
反向传播
开始从高位往回遍历,一次对每一位的所有层计算误差,并对权重求偏导,得到调整值,最后将每一位算出的各层权重的调整值加在一起乘以学习率来更新各层参数。每次更新完后中间变量会清零。
输出结果
每运行800次输出一次结果。

# training 
for j in range(10000):
    
    #生成一个数字a
    a_int = np.random.randint(largest_number) 
    #生成一个数字b,b的最大值取的是largest_number/2,作为被减数,让它小一点。
    b_int = np.random.randint(largest_number/2) 
    #如果生成的b大了,那么交换一下
    if a_int<b_int:
        tt = a_int
        b_int = a_int
        a_int=tt
    
    a = int2binary[a_int] # binary encoding
    b = int2binary[b_int] # binary encoding    
    # true answer
    c_int = a_int - b_int
    c = int2binary[c_int]
    
    # 存储神经网络的预测值
    d = np.zeros_like(c)
    overallError = 0 #每次把总误差清零
    
    layer_2_deltas = list() #存储每个时间点输出层的误差
    layer_1_values = list() #存储每个时间点隐藏层的值
    
    layer_1_values.append(np.ones(hidden_dim)*0.1) # 一开始没有隐藏层,所以初始化一下原始值为0.1
    
    # moving along the positions in the binary encoding
    for position in range(binary_dim):#循环遍历每一个二进制位
        
        # generate input and output
        X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])#从右到左,每次去两个输入数字的一个bit位
        y = np.array([[c[binary_dim - position - 1]]]).T#正确答案
        # hidden layer (input ~+ prev_hidden)
        layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))#(输入层 + 之前的隐藏层) -> 新的隐藏层,这是体现循环神经网络的最核心的地方!!!
        # output layer (new binary representation)
        layer_2 = sigmoid(np.dot(layer_1,synapse_1)) #隐藏层 * 隐藏层到输出层的转化矩阵synapse_1 -> 输出层
        
        layer_2_error = y - layer_2 #预测误差
        layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2)) #把每一个时间点的误差导数都记录下来
        overallError += np.abs(layer_2_error[0])#总误差
    
        d[binary_dim - position - 1] = np.round(layer_2[0][0]) #记录下每一个预测bit位
        
        # store hidden layer so we can use it in the next timestep
        layer_1_values.append(copy.deepcopy(layer_1))#记录下隐藏层的值,在下一个时间点用
    
    future_layer_1_delta = np.zeros(hidden_dim)
    
    #反向传播,从最后一个时间点到第一个时间点
    for position in range(binary_dim):
        
        X = np.array([[a[position],b[position]]]) #最后一次的两个输入
        layer_1 = layer_1_values[-position-1] #当前时间点的隐藏层
        prev_layer_1 = layer_1_values[-position-2] #前一个时间点的隐藏层
        
        # error at output layer
        layer_2_delta = layer_2_deltas[-position-1] #当前时间点输出层导数
        # error at hidden layer
        # 通过后一个时间点(因为是反向传播)的隐藏层误差和当前时间点的输出层误差,计算当前时间点的隐藏层误差
        layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)
        
        
       # 等到完成了所有反向传播误差计算, 才会更新权重矩阵,先暂时把更新矩阵存起来。
        synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
        synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
        synapse_0_update += X.T.dot(layer_1_delta)
        
        future_layer_1_delta = layer_1_delta
    
    # 完成所有反向传播之后,更新权重矩阵。并把矩阵变量清零
    synapse_0 += synapse_0_update * alpha
    synapse_1 += synapse_1_update * alpha
    synapse_h += synapse_h_update * alpha
    synapse_0_update *= 0
    synapse_1_update *= 0
    synapse_h_update *= 0
   
    # print out progress
    if(j % 800 == 0):
        #print(synapse_0,synapse_h,synapse_1)
        print("总误差:" + str(overallError))
        print("Pred:" + str(d))
        print("True:" + str(c))
        out = 0
        for index,x in enumerate(reversed(d)):
            out += x*pow(2,index)
        print(str(a_int) + " - " + str(b_int) + " = " + str(out))
        print("------------")
总误差:[3.97242498]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 0 0 0 0 0]
9 - 9 = 0
------------
总误差:[2.1721182]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 1 0 0 0 1]
17 - 0 = 0
------------
总误差:[1.1082385]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 0 0 0 0 0]
59 - 59 = 0
------------
总误差:[0.18727913]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 0 0 0 0 0]
19 - 19 = 0
------------
总误差:[0.21914293]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 0 0 0 0 0]
71 - 71 = 0
------------
总误差:[0.26861004]
Pred:[0 0 1 1 1 1 0 0]
True:[0 0 1 1 1 1 0 0]
71 - 11 = 60
------------
总误差:[0.11815367]
Pred:[1 0 0 0 0 0 0 0]
True:[1 0 0 0 0 0 0 0]
230 - 102 = 128
------------
总误差:[0.2927243]
Pred:[0 1 1 1 0 0 0 1]
True:[0 1 1 1 0 0 0 1]
160 - 47 = 113
------------
总误差:[0.04298749]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 0 0 0 0 0]
3 - 3 = 0
------------
总误差:[0.04243453]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 0 0 0 0 0]
17 - 17 = 0
------------
总误差:[0.04588656]
Pred:[1 0 0 1 0 1 1 0]
True:[1 0 0 1 0 1 1 0]
167 - 17 = 150
------------
总误差:[0.08098026]
Pred:[1 0 0 1 1 0 0 0]
True:[1 0 0 1 1 0 0 0]
204 - 52 = 152
------------
总误差:[0.03262333]
Pred:[1 1 0 0 0 0 0 0]
True:[1 1 0 0 0 0 0 0]
209 - 17 = 192
------------

从训练结果可以看出,一开始不准确,多次迭代后就精准了。

完整代码:

import copy, numpy as np
np.random.seed(0) #随机数生成器的种子,可以每次得到一样的值
# compute sigmoid nonlinearity
def sigmoid(x): #激活函数
    output = 1/(1+np.exp(-x))
    return output
# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative(output):#激活函数的导数
    return output*(1-output)


int2binary = {} #整数到其二进制表示的映射
binary_dim = 8 #暂时制作256以内的减法
## 计算0-256的二进制表示
largest_number = pow(2,binary_dim)
binary = np.unpackbits(
    np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
for i in range(largest_number):
    int2binary[i] = binary[i]

# input variables
alpha = 0.9 #学习速率
input_dim = 2 #输入的维度是2
hidden_dim = 16 
output_dim = 1 #输出维度为1

# initialize neural network weights
synapse_0 = (2*np.random.random((input_dim,hidden_dim)) - 1)*0.05 #维度为2*16, 2是输入维度,16是隐藏层维度
synapse_1 = (2*np.random.random((hidden_dim,output_dim)) - 1)*0.05
synapse_h = (2*np.random.random((hidden_dim,hidden_dim)) - 1)*0.05
# => [-0.05, 0.05),

# 用于存放反向传播的权重更新值
synapse_0_update = np.zeros_like(synapse_0)
synapse_1_update = np.zeros_like(synapse_1)
synapse_h_update = np.zeros_like(synapse_h)

# training 
for j in range(10000):
    
    #生成一个数字a
    a_int = np.random.randint(largest_number) 
    #生成一个数字b,b的最大值取的是largest_number/2,作为被减数,让它小一点。
    b_int = np.random.randint(largest_number/2) 
    #如果生成的b大了,那么交换一下
    if a_int<b_int:
        tt = a_int
        b_int = a_int
        a_int=tt
    
    a = int2binary[a_int] # binary encoding
    b = int2binary[b_int] # binary encoding    
    # true answer
    c_int = a_int - b_int
    c = int2binary[c_int]
    
    # 存储神经网络的预测值
    d = np.zeros_like(c)
    overallError = 0 #每次把总误差清零
    
    layer_2_deltas = list() #存储每个时间点输出层的误差
    layer_1_values = list() #存储每个时间点隐藏层的值
    
    layer_1_values.append(np.ones(hidden_dim)*0.1) # 一开始没有隐藏层,所以初始化一下原始值为0.1
    
    # moving along the positions in the binary encoding
    for position in range(binary_dim):#循环遍历每一个二进制位
        
        # generate input and output
        X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])#从右到左,每次去两个输入数字的一个bit位
        y = np.array([[c[binary_dim - position - 1]]]).T#正确答案
        # hidden layer (input ~+ prev_hidden)
        layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))#(输入层 + 之前的隐藏层) -> 新的隐藏层,这是体现循环神经网络的最核心的地方!!!
        # output layer (new binary representation)
        layer_2 = sigmoid(np.dot(layer_1,synapse_1)) #隐藏层 * 隐藏层到输出层的转化矩阵synapse_1 -> 输出层
        
        layer_2_error = y - layer_2 #预测误差
        layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2)) #把每一个时间点的误差导数都记录下来
        overallError += np.abs(layer_2_error[0])#总误差
    
        d[binary_dim - position - 1] = np.round(layer_2[0][0]) #记录下每一个预测bit位
        
        # store hidden layer so we can use it in the next timestep
        layer_1_values.append(copy.deepcopy(layer_1))#记录下隐藏层的值,在下一个时间点用
    
    future_layer_1_delta = np.zeros(hidden_dim)
    
    #反向传播,从最后一个时间点到第一个时间点
    for position in range(binary_dim):
        
        X = np.array([[a[position],b[position]]]) #最后一次的两个输入
        layer_1 = layer_1_values[-position-1] #当前时间点的隐藏层
        prev_layer_1 = layer_1_values[-position-2] #前一个时间点的隐藏层
        
        # error at output layer
        layer_2_delta = layer_2_deltas[-position-1] #当前时间点输出层导数
        # error at hidden layer
        # 通过后一个时间点(因为是反向传播)的隐藏层误差和当前时间点的输出层误差,计算当前时间点的隐藏层误差
        layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)
        
        
       # 等到完成了所有反向传播误差计算, 才会更新权重矩阵,先暂时把更新矩阵存起来。
        synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
        synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
        synapse_0_update += X.T.dot(layer_1_delta)
        
        future_layer_1_delta = layer_1_delta
    
    # 完成所有反向传播之后,更新权重矩阵。并把矩阵变量清零
    synapse_0 += synapse_0_update * alpha
    synapse_1 += synapse_1_update * alpha
    synapse_h += synapse_h_update * alpha
    synapse_0_update *= 0
    synapse_1_update *= 0
    synapse_h_update *= 0
   
    # print out progress
    if(j % 800 == 0):
        #print(synapse_0,synapse_h,synapse_1)
        print("总误差:" + str(overallError))
        print("Pred:" + str(d))
        print("True:" + str(c))
        out = 0
        for index,x in enumerate(reversed(d)):
            out += x*pow(2,index)
        print(str(a_int) + " - " + str(b_int) + " = " + str(out))
        print("------------")

常见的循环神经网络单元及结构

上述实例仅限于简单的逻辑和样本,对于相对复杂的问题有缺陷,原因在激活函数上。
通常像Sigmoid、tanh这类激活函数在神经网络里最多只能有6层左右,因为反向误差的传播会导致随着层数增加传递的误差值越小。RNN中,误差传递不光在层之间,还在每层的样本序列之间,因此其无法学习太长的序列特征。
在深层网络结构中,会将简单的RNN模型从两个角度进行改造:
1.使用结构更复杂的RNN模型的基本单元,使其在单层网络上提取更好的记忆特征。
2.将多个基本单元结合起来,组成不同的结构(多层RNN,双向RNN等),有时还会配合全连接网络、卷积网络等多种模型结构,一起组成拟合能力更强的网络模型。

长短记忆(LSTM)单元

一种使用了类似搭桥术结构的RNN单元,可以学习长期序列信息,是RNN网络中最常用的Cell之一。
看起来比较复杂其实是一个带有tanh激活函数的简单RNN,原理是引入一个成为细胞状态的连接,用来存放想要记忆的东西,(对应于简单RNN中的h,只不过不再只是保存上一次的状态了,而是通过网络学习存放那些有用的状态),同时在里面加入3个门,忘记门、输入门、输出门。

门控制循环单元(GRU)

几乎是与LSTM功能一样的常用网络结构,它将忘记门和输入门合成了一个单一的更新门,同时又将细胞状态和隐藏状态进行混合,以及一些其他的改动,最终的模型比LSTM模型要简单,少一个状态输出,但效果几乎一样,可以让代码更简单。

只有忘记门的LSTM(JANET)单元

只有忘记门时,性能居然优于标志LSTM,该优化方式也可以用在GRU。

独立循环单元(IndRNN)单元

效果和速度都优于LSTM单元,不但能解决传统RNN模型存在的梯度消失和梯度爆炸问题,而且可以更好学习样本中长期依赖的关系。
在搭建模型时:
可以用堆叠、残差、全连接的方式使用IndRNN单元,搭建更深的网络结构;
将IndRNN单元配合ReLU等非饱和激活函数一起使用,会使模型表现出很好的鲁棒性。

IndRNN与LSTM单元相比,使用了更简单的结构,比其快10倍,更像一个原始的RNN模型结构(只将神经元的输出复制到节点之中),其在循环层部分做了特殊处理。
通过公式来详细介绍…

双向RNN结构

又称Bi-RNN,采用了两个方向的RNN模型。正反结合比单向的循环网络有更高的拟合度。例如预测一个语句中缺失的词语,需要根据上下文来预测。
略…

实例11:用循环神经网络训练语言模型

还涉及自然语言处理(NLP)领域的相关知识,语言模型包括文法语言模型和统计语言模型,一般指统计语言模型。略…只做了解。
PyTorch中,有两个封装好的RNN类,LSTM和GRU。
解码要改成utf-8
labels =labels+label.decode(‘utf-8’)

import torch
import torch.nn.functional as F
import time
import random
import numpy as np
from collections import Counter

import sys

# print(sys.getdefaultencoding()) 

RANDOM_SEED = 123
torch.manual_seed(RANDOM_SEED)

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')



def elapsed(sec):
    if sec<60:
        return str(sec) + " sec"
    elif sec<(60*60):
        return str(sec/60) + " min"
    else:
        return str(sec/(60*60)) + " hr"


training_file = 'wordstest.txt'


#中文多文件
def readalltxt(txt_files):
    labels = []
    for txt_file in txt_files:
        
        target = get_ch_lable(txt_file)
        labels.append(target)  
    return labels
    
#中文字
def get_ch_lable(txt_file):  
    labels= ""
    with open(txt_file, 'rb') as f:
        for label in f: 
            #labels =label.decode('utf-8')
            labels =labels+label.decode('utf-8')
           
    return  labels
    


#优先转文件里的字符到向量
def get_ch_lable_v(txt_file,word_num_map,txt_label=None):
      
    words_size = len(word_num_map)   
    to_num = lambda word: word_num_map.get(word, words_size) 
    if txt_file!= None:
        txt_label = get_ch_lable(txt_file)

    labels_vector = list(map(to_num, txt_label)) 
    return labels_vector  
    
training_data =get_ch_lable(training_file)

print("Loaded training data...")

print('样本长度:',len(training_data))
counter = Counter(training_data)  
words = sorted(counter)
words_size= len(words)
word_num_map = dict(zip(words, range(words_size))) 

print('字表大小:', words_size)     
wordlabel = get_ch_lable_v(training_file,word_num_map)

class GRURNN(torch.nn.Module):
    def __init__(self, word_size, embed_dim,
                 hidden_dim, output_size, num_layers):
        super(GRURNN, self).__init__()

        self.num_layers = num_layers
        self.hidden_dim = hidden_dim
        
        self.embed = torch.nn.Embedding(word_size, embed_dim)
        self.gru = torch.nn.GRU(input_size=embed_dim,
                                hidden_size=hidden_dim,
                                num_layers=num_layers,bidirectional=True)
        self.fc = torch.nn.Linear(hidden_dim*2, output_size)

    
    def forward(self, features, hidden):
        embedded = self.embed(features.view(1, -1))
        output, hidden = self.gru(embedded.view(1, 1, -1), hidden)
        output = self.fc(output.view(1, -1))
        return output, hidden
      
    def init_zero_state(self):
        init_hidden = torch.zeros(self.num_layers*2, 1, self.hidden_dim).to(DEVICE)
        return init_hidden



EMBEDDING_DIM = 10
HIDDEN_DIM = 20
NUM_LAYERS = 1

model = GRURNN(words_size, EMBEDDING_DIM, HIDDEN_DIM, words_size, NUM_LAYERS)
model = model.to(DEVICE)
optimizer = torch.optim.Adam(model.parameters(), lr=0.005)


def evaluate(model, prime_str, predict_len, temperature=0.8):

    hidden = model.init_zero_state().to(DEVICE)
    predicted = ''

    #处理输入语义
    for p in range(len(prime_str) - 1):
        _, hidden = model(prime_str[p], hidden)
        predicted +=words[prime_str[p]]
    inp = prime_str[-1]
    predicted +=words[inp]
    
    for p in range(predict_len):
        output, hidden = model(inp, hidden)
        
        #从多项式分布中采样
        output_dist = output.data.view(-1).div(temperature).exp()
        inp = torch.multinomial(output_dist, 1)[0]
        
        predicted += words[inp]

    return predicted


#定义参数训练模型
training_iters = 5000
display_step = 1000
n_input = 4
step = 0
offset = random.randint(0,n_input+1)
end_offset = n_input + 1

while step < training_iters:
    start_time = time.time()

    # 随机取一个位置偏移
    if offset > (len(training_data)-end_offset):
        offset = random.randint(0, n_input+1)
   

    inwords =wordlabel[offset:offset+n_input]
    inwords = np.reshape(np.array(inwords), [n_input, -1,  1])

    out_onehot = wordlabel[offset+1:offset+n_input+1]


    hidden = model.init_zero_state()
    optimizer.zero_grad()
    
    loss = 0.
    inputs, targets = torch.LongTensor(inwords).to(DEVICE), torch.LongTensor(out_onehot).to(DEVICE)
    for c in range(n_input):

        outputs, hidden = model(inputs[c], hidden)
        loss += F.cross_entropy(outputs, targets[c].view(1))

    loss /= n_input
    loss.backward()

    optimizer.step()

    #输出日志
    with torch.set_grad_enabled(False):
        if (step+1) % display_step == 0:
            print(f'Time elapsed: {(time.time() - start_time)/60:.4f} min')
            print(f'step {step+1} | Loss {loss.item():.2f}\n\n')
            with torch.no_grad():
                print(evaluate(model, inputs, 32), '\n')
            print(50*'=')

    step += 1
    offset += (n_input+1)#中间隔了一个,作为预测

print("Finished!")

while True:
    prompt = "请输入几个字,最好是%s个: " % n_input
    sentence = input(prompt)
    inputword = sentence.strip()
    
    try:
        inputword = get_ch_lable_v(None,word_num_map,inputword)
        keys = np.reshape(np.array(inputword), [ len(inputword),-1, 1])
        model.eval()
        with torch.no_grad():
            sentence =evaluate(model, torch.LongTensor(keys).to(DEVICE), 32)

        print(sentence)
    except:
        print("该字我还没学会")

过拟合问题及优化技巧

介绍一下神经网络在训练过程中的一些常用技巧。

实例12:训练具有过拟合问题的模型

可以用下面引用其他写的example1.py,不知道为什么会运行一遍原来文件的函数。
可以看图看出过拟合。

import sys
sys.path.append('pytorch\chapter3')
from example1 import LogicNet,moving_average,predict,plot_decision_boundary
import sklearn.datasets     #引入数据集
import torch
import numpy as np
import matplotlib.pyplot as plt
import sys
sys.path.append('pytorch\chapter3')
from example1 import LogicNet,moving_average,predict,plot_decision_boundary

np.random.seed(0)           #设置随机数种子
X, Y = sklearn.datasets.make_moons(40,noise=0.2) #生成2组半圆形数据

arg = np.squeeze(np.argwhere(Y==0),axis = 1)     #获取第1组数据索引
arg2 = np.squeeze(np.argwhere(Y==1),axis = 1)#获取第2组数据索引

plt.title("train moons data")
plt.scatter(X[arg,0], X[arg,1], s=100,c='b',marker='+',label='data1')
plt.scatter(X[arg2,0], X[arg2,1],s=40, c='r',marker='o',label='data2')
plt.legend()
plt.show()


model = LogicNet(inputdim=2,hiddendim=500,outputdim=2)#初始化模型
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)#定义优化器


xt = torch.from_numpy(X).type(torch.FloatTensor)#将Numpy数据转化为张量
yt = torch.from_numpy(Y).type(torch.LongTensor)
epochs = 1000#定义迭代次数
losses = []#定义列表,用于接收每一步的损失值
for i in range(epochs):
    loss = model.getloss(xt,yt)
    losses.append(loss.item())
    optimizer.zero_grad()#清空之前的梯度
    loss.backward()#反向传播损失值
    optimizer.step()#更新参数


avgloss= moving_average(losses) #获得损失值的移动平均值
plt.figure(1)
plt.subplot(211)
plt.plot(range(len(avgloss)), avgloss, 'b--')
plt.xlabel('step number')
plt.ylabel('Training loss')
plt.title('step number vs. Training loss')
plt.show()


plot_decision_boundary(lambda x : predict(model,x) ,X, Y)
from sklearn.metrics import accuracy_score
print("训练时的准确率:",accuracy_score(model.predict(xt),yt))

Xtest, Ytest = sklearn.datasets.make_moons(80,noise=0.2) #生成2组半圆形数据
plot_decision_boundary(lambda x : predict(model,x) ,Xtest, Ytest)
Xtest_t = torch.from_numpy(Xtest).type(torch.FloatTensor)#将Numpy数据转化为张量
Ytest_t = torch.from_numpy(Ytest).type(torch.LongTensor)
print("测试时的准确率:",accuracy_score(model.predict(Xtest_t),Ytest_t))

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
训练时的准确率: 1.0
在这里插入图片描述
测试时的准确率: 0.9375

改善模型过拟合的方法

如early stopping、数据集扩增、正则化、Dropout。
early stopping:在数据过拟合之前结束,不好把控。
数据集扩增:让模型看到更多的情况,最大化满足全样本,但在实际中为未来事件预测力不从心。
正则化:通过引入范数概念,增强泛化能力,有L1正则化、L2正则化。
Dropout:每次训练舍去一些节点来增强泛化能力

了解正则化

所谓正则化,就是在神经网络计算损失过程中,在损失后面加一项来干扰,实现模型无法和与样本完全拟合,从而抑制过拟合。

正则化的分类和公式

干扰项一定要有以下特征
当欠拟合时,希望它对模型误差影响尽量小,让模型快速拟合实际。
过拟合时,希望影响大,让模型不要产生过拟合的情况。
于是引入了两个范数——L1、L2
L1:所有学习参数的w的绝对值的和
L2:所有学习参数的w的平方和,然后求平方根
实际应用中L2最常用

L2正则化的实现

直接的方式是用优化器自带的weight_decay参数指定权重值衰减率,相当于L2正则化中的λ。默认对w和b都处理,实际上只需要对w,如果对b可能会欠拟合。

实例13:用L2正则改善模型的过拟合情况

在实例12上添加正则化处理,重新进行训练。

#添加正则化处理
weight_p, bias_p = [],[]
for name, p in model.named_parameters():
    if 'bias' in name:
        bias_p += [p]
    else:
        weight_p += [p]
optimizer = torch.optim.Adam([{'params': weight_p, 'weight_decay':0.001},
                      {'params': bias_p, 'weight_decay':0}],
                      lr=0.01)

在这里插入图片描述

训练时的准确率由1到0.975,是由于L2正则化干扰项
测试时的准确率由0.9375到0.9875,表明L2改善了过拟合
在这里插入图片描述
在这里插入图片描述

观察训练和测试图片没有了闭合区间,更接近原始的数据分布。

实例14:通过增大数据集改善模型的过拟合状况

不再生成一次,而是循环生成40次,修改每次训练都加入新的数据集。
在迭代中加入

    X, Y = sklearn.datasets.make_moons(40,noise=0.2) #生成2组半圆形数据
    xt = torch.from_numpy(X).type(torch.FloatTensor)#将Numpy数据转化为张量
    yt = torch.from_numpy(Y).type(torch.LongTensor)

可以看出loss曲线有明显的抖动,是由于新数据对上一次模型的拟合能力冲突较大,经过多次迭代就可以不断修正错误,达到合理的拟合能力。
在这里插入图片描述
与之前对比
训练时的准确率由1到0.95,是由于训练了新的数据
测试时的准确率由0.9375到0.975,表明增大数据集的方法改进了过拟合情况。
在这里插入图片描述
在这里插入图片描述
观察图片没有了闭合区间,更接近原始的数据分布。

Dropout方法

原理:每次随机选择一部分节点不去学习,是因为过拟合是把一些异常数据当成规律来学习,但其量非常少,利用上述特性,每次训练忽略一些节点,将小概率的异常数据获得学习的机会变得更低。但不是丢弃越多越好,会降低拟合速度。它改变了网络结构,只能训练的时候用,测试时候要改成False。使用类的方式时候,没有training参数,因为它会根据调用方式自己调节。

实例15:通过Dropout方法改善模型的过拟合状况

为了简化代码,之间继承模型类,然后重写前向结构。

#继承LogicNet类,构建网络模型
class Logic_Dropout_Net(LogicNet):
    def __init__(self,inputdim,hiddendim,outputdim):#初始化网络结构
        super(Logic_Dropout_Net,self).__init__(inputdim,hiddendim,outputdim)

    def forward(self,x): #搭建用两层全连接组成的网络模型
        x = self.Linear1(x)#将输入数据传入第1层
        x = torch.tanh(x)#对第一层的结果进行非线性变换
        x = nn.functional.dropout(x, p=0.07, training=self.training)
        x = self.Linear2(x)#再将数据传入第2层
        return x

在这里插入图片描述

与之前对比
训练时的准确率由1到0.925
测试时的准确率由0.9375到0.95
测试准确率同样没有低于训练准确率,说明Dropout方式有效改善过拟合
在这里插入图片描述
在这里插入图片描述

观察图片没有了闭合区间,更接近原始的数据分布。

全连接网络的深浅与泛化能力的联系

浅层网络有更好的拟合能力,但泛化能力弱,深层反之。
实际过程要考虑二者平衡,如wide_deep模型,就是单层线性模型(浅层全连接网络模型)和深度的全连接模型(深层全连接网络模型)。

了解批量归一化(BN)算法

一般用在全连接神经网络和卷积神经网络中,它的问世让整个神经网络的识别准确率上升了一个台阶。
权重值差太大,会让网络无法计算产生梯度爆炸,原因是网络内部协变量的转移,即正向传播时的不同层的参数会将反向训练计算时所参照的数据样本分布改变。
引入批量归一化的作用:最大限度的保证每次正向传播输出在同一分布上,这样反向计算时参照的数据样本分布就会与正向一样了。保证了分布统一,对权重的调整才会更有意义。
算法实现就是将每一层运算出来的数据归一化成均值为0、方差为1的标准高斯分布,这样就会在保留样本的分布特征的同时,又消除了层与层之间的分布差异。实际上加两个参数通过训练获得。

实例16:手动实现批量归一化的计算方法

data为2个样本,2个通道高宽为2和1。
BatchNorm2d接口为数据的每个通道创建一套自适应参数,实际计算中根据每个通道的数据进行批量归一化计算的。
经过批量归一化后,只改变了值没有改变形状。
最后手动计算第1通道中第一个数据的BN,结果与接口一致。

import torch
import torch.nn as nn
data=torch.randn(2,2,2,1)
print(data)
tensor([[[[-0.3322],  
          [ 0.2331]], 

         [[ 0.0162],  
          [ 1.0788]]],


        [[[ 0.6592],
          [ 1.3542]],

         [[-0.0912],
          [ 0.9763]]]])
obn=nn.BatchNorm2d(2,affine=True) #实例化自适应BN对象
output=obn(data)
print(obn.weight)
print(obn.bias)
print(obn.eps)
print(output,output.size())
#自适应参数
Parameter containing:
tensor([1., 1.], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)
1e-05
tensor([[[[-1.3166],
          [-0.3986]],

         [[-0.8948],
          [ 1.0910]]],


        [[[ 0.2933],
          [ 1.4218]],

         [[-1.0955],
          [ 0.8994]]]], grad_fn=<NativeBatchNormBackward0>) torch.Size([2, 2, 2, 1])

print("第1通道的数据:",data[:,0])
 
#计算第1通道数据的均值和方差
Mean=torch.Tensor.mean(data[:,0])
Var=torch.Tensor.var(data[:,0],False)   #false表示贝塞尔校正不会被使用
print(Mean)
print(Var)

#计算第1通道中第一个数据的BN
batchnorm=((data[0][0][0][0]-Mean)/(torch.pow(Var,0.5)+obn.eps))\
    *obn.weight[0]+obn.bias[0]
print(batchnorm)1通道的数据: tensor([[[-0.3322],
         [ 0.2331]],

        [[ 0.6592],
         [ 1.3542]]])
tensor(0.4786)
tensor(0.3793)
tensor(-1.3166, grad_fn=<AddBackward0>)

实例17:通过批量归一化方法改善模型形状

继承模型后进行BN处理

#继承LogicNet类,构建网络模型
class Logic_BN_Net(LogicNet):
    def __init__(self,inputdim,hiddendim,outputdim):#初始化网络结构
        super(Logic_BN_Net,self).__init__(inputdim,hiddendim,outputdim)
        self.BN = nn.BatchNorm1d(hiddendim) #定义BN层
    def forward(self,x): #搭建用两层全连接组成的网络模型
        x = self.Linear1(x)#将输入数据传入第1层
        x = torch.tanh(x)#对第一层的结果进行非线性变换
        x = self.BN(x)#将第一层的数据做BN处理
        x = self.Linear2(x)#再将数据传入第2层
        return x

在这里插入图片描述
与之前对比
训练时的准确率由1到0.975
测试时的准确率由0.9375到0.925
说明BN有效改善过拟合
在这里插入图片描述
在这里插入图片描述

注意力机制

让神经网络忽略不重要的特征向量,重点计算有用的,抛弃无用特征对拟合效果的干扰同时能提高运算速度。
通过注意力分数来实现,是一个0到1的值,可以用在任何网络中。
可以作用在RNN模型中的每个序列上,在模型输出的特征向量上。
有两种模式,软模式(所有数据都注意)、硬模式(会舍弃一部分不符合条件的注意力权值为0)。
多头注意力机制…自注意力机制…

实例18:利用注意力循环神经网络对图片分类

import torchvision
import torchvision.transforms as tranforms
data_dir = './fashion_mnist/'
tranform = tranforms.Compose([tranforms.ToTensor()])
train_dataset = torchvision.datasets.FashionMNIST(data_dir, train=True, transform=tranform,download=True)

print("训练数据集条数",len(train_dataset))
val_dataset  = torchvision.datasets.FashionMNIST(root=data_dir, train=False, transform=tranform)
print("测试数据集条数",len(val_dataset))
import pylab
im = train_dataset[0][0]
im = im.reshape(-1,28)
pylab.imshow(im)
pylab.show()
print("该图片的标签为:",train_dataset[0][1])

############数据集的制作
import torch
batch_size = 10
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False)


from matplotlib import pyplot as plt
import numpy as np
def imshow(img):
    print("图片形状:",np.shape(img))
    npimg = img.numpy()
    plt.axis('off')
    plt.imshow(np.transpose(npimg, (1, 2, 0)))

classes = ('T-shirt', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle_Boot')
sample = iter(train_loader)
images, labels = sample.next()
print('样本形状:',np.shape(images))
print('样本标签:',labels)
imshow(torchvision.utils.make_grid(images,nrow=batch_size))
print(','.join('%5s' % classes[labels[j]] for j in range(len(images))))

############


#########################################################################################################################



#定义myLSTMNet模型类,该模型包括 2个RNN层和1个全连接层
class myLSTMNet(torch.nn.Module):
    def __init__(self,in_dim, hidden_dim, n_layer, n_class):
        super(myLSTMNet, self).__init__()
        #定义循环神经网络层
        self.lstm = torch.nn.LSTM(in_dim, hidden_dim, n_layer,batch_first=True)
        self.Linear = torch.nn.Linear(hidden_dim*28, n_class)#定义全连接层
        self.attention = AttentionSeq(hidden_dim,hard=0.03)

    def forward(self, t):    #搭建正向结构
        t, _ = self.lstm(t)  #进行RNN处理
        t = self.attention(t)
        t=t.reshape(t.shape[0],-1)
#        t = t[:, -1, :]      #获取RNN网络的最后一个序列数据
        out = self.Linear(t) #进行全连接处理
        return out
class AttentionSeq(torch.nn.Module):

    def __init__(self, hidden_dim,hard= 0):
        super(AttentionSeq, self).__init__()
        self.hidden_dim = hidden_dim
        self.dense = torch.nn.Linear(hidden_dim, hidden_dim)
        self.hard = hard

    def forward(self, features, mean=False):
        #[batch,seq,dim]
        batch_size, time_step, hidden_dim = features.size()
        weight = torch.nn.Tanh()(self.dense(features))

        # mask给负无穷使得权重为0
        mask_idx = torch.sign(torch.abs(features).sum(dim=-1))
#        mask_idx = mask_idx.unsqueeze(-1).expand(batch_size, time_step, hidden_dim)
        mask_idx = mask_idx.unsqueeze(-1).repeat(1, 1, hidden_dim)


        weight = torch.where(mask_idx== 1, weight,
                             torch.full_like(mask_idx,(-2 ** 32 + 1)))
        weight = weight.transpose(2, 1)
        weight = torch.nn.Softmax(dim=2)(weight)
        if self.hard!=0: #hard mode
            weight = torch.where(weight>self.hard, weight, torch.full_like(weight,0))
        
        if mean:
            weight = weight.mean(dim=1)
            weight = weight.unsqueeze(1)
            weight = weight.repeat(1, hidden_dim, 1)
        weight = weight.transpose(2, 1)
        features_attention = weight * features

        return features_attention
#实例化模型对象
network = myLSTMNet(28, 128, 2, 10)  # 图片大小是28x28
#指定设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
network.to(device)
print(network)#打印网络

criterion = torch.nn.CrossEntropyLoss()  #实例化损失函数类
optimizer = torch.optim.Adam(network.parameters(), lr=.01)

for epoch in range(2): #数据集迭代2次
    running_loss = 0.0
    for i, data in enumerate(train_loader, 0): #循环取出批次数据
        inputs, labels = data
        inputs = inputs.squeeze(1)
        inputs, labels = inputs.to(device), labels.to(device) #
        optimizer.zero_grad()#清空之前的梯度
        outputs = network(inputs)
        loss = criterion(outputs, labels)#计算损失
        loss.backward()  #反向传播
        optimizer.step() #更新参数

        running_loss += loss.item()
        if i % 1000 == 999:
            print('[%d, %5d] loss: %.3f' %
                (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0




print('Finished Training')


#使用模型
dataiter = iter(test_loader)
images, labels = dataiter.next()

inputs, labels = images.to(device), labels.to(device)


imshow(torchvision.utils.make_grid(images,nrow=batch_size))
print('真实标签: ', ' '.join('%5s' % classes[labels[j]] for j in range(len(images))))
inputs = inputs.squeeze(1)
outputs = network(inputs)
_, predicted = torch.max(outputs, 1)


print('预测结果: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(len(images))))


#测试模型
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in test_loader:
        images, labels = data
        images = images.squeeze(1)
        inputs, labels = images.to(device), labels.to(device)
        outputs = network(inputs)
        _, predicted = torch.max(outputs, 1)
        predicted = predicted.to(device)
        c = (predicted == labels).squeeze()
        for i in range(10):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


sumacc = 0
for i in range(10):
    Accuracy = 100 * class_correct[i] / class_total[i]
    print('Accuracy of %5s : %2d %%' % (classes[i], Accuracy ))
    sumacc =sumacc+Accuracy
print('Accuracy of all : %2d %%' % ( sumacc/10. ))

输出结果如下:
训练数据集条数 60000
测试数据集条数 10000
libpng warning: iCCP: cHRM chunk does not match sRGB
该图片的标签为: 9
样本形状: torch.Size([10, 1, 28, 28])
样本标签: tensor([2, 2, 5, 3, 7, 1, 7, 9, 2, 9])
图片形状: torch.Size([3, 32, 302])
Pullover,Pullover,Sandal,Dress,Sneaker,Trouser,Sneaker,Ankle_Boot,Pullover,Ankle_Boot
cpu
myLSTMNet(
(lstm): LSTM(28, 128, num_layers=2, batch_first=True)
(Linear): Linear(in_features=3584, out_features=10, bias=True)
(attention): AttentionSeq(
(dense): Linear(in_features=128, out_features=128, bias=True)
)
)
[1, 1000] loss: 0.381
[1, 2000] loss: 0.261
[1, 3000] loss: 0.240
[1, 4000] loss: 0.216
[1, 5000] loss: 0.205
[1, 6000] loss: 0.207
[2, 1000] loss: 0.231
[2, 2000] loss: 0.281
[2, 3000] loss: 0.282
[2, 4000] loss: 0.265
[2, 5000] loss: 0.238
[2, 6000] loss: 0.230
Finished Training
图片形状: torch.Size([3, 32, 302])
真实标签: Ankle_Boot Pullover Trouser Trouser Shirt Trouser Coat Shirt Sandal Sneaker
预测结果: Ankle_Boot Pullover Trouser Trouser Shirt Trouser Pullover Shirt Sandal Sneaker
Accuracy of T-shirt : 78 %
Accuracy of Trouser : 95 %
Accuracy of Pullover : 81 %
Accuracy of Dress : 75 %
Accuracy of Coat : 65 %
Accuracy of Sandal : 89 %
Accuracy of Shirt : 54 %
Accuracy of Sneaker : 91 %
Accuracy of Bag : 95 %
Accuracy of Ankle_Boot : 95 %
Accuracy of all : 82 %

这一章内容也太多了,都有点烦了。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/34280.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于群组实现从 Azure AD 到极狐GitLab 的单点登录

目录 配置单点登录 在 Azure AD 中创建企业应用 SAML 基础配置 配置 Azure “Attributes & Claims” 配置用户同步 在极狐GitLab 创建 SCIM Token 配置 Azure Provisioning Azure 手动用户预配 测试单点登录 Azure 自动用户同步 配置群组同步 配置 SAML 群组链…

打jar包

pom文件指定打包的类型是jar&#xff0c;并指定启动主类 <packaging>jar</packaging><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>…

什么条件下会出现死锁,如何避免?

文章目录 一、什么是死锁二、产生死锁的原因&#xff1a;三、如何避免死锁&#xff1a; 一、什么是死锁 死锁&#xff0c;简单来说就是两个或者两个以上的线程在执行过程中&#xff0c;去争夺同一个共享资源导致相互等待的现象。如果没有外部干预&#xff0c;线程会一直处于阻塞…

uniapp封装门票等票务样式

先看效果图 ticketpage组件引用后&#xff0c;根据父级背景颜色改变镂空的颜色,空组件只有中间镂空的样式&#xff0c;上面是插槽heaer,下面内容是插槽content&#xff0c;可以自定义自己的内容和样式。我实现的最终效果是用的uview组件&#xff0c;如果复现需要项目引入。可…

Nginx负载均衡与动静分离

一、Nginx负载均衡&#xff1a; 1.概述&#xff1a; Nginx是一款http服务器软件&#xff0c;支持高达50000个并发连接数的响应。 &#xff08;1&#xff09;拥有强大的处理静态资源的能力。 &#xff08;2&#xff09;运行稳定。 &#xff08;3&#xff09;CPU&#xff0c…

【Unity3D编辑器开发】Unity3D中初次尝试使用PropertyDrawer属性

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 前段时间一直比较忙&#xff0c;没有时间更新博客&#xff0c;…

2011年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&…

tomcat接入skywalking

tomcat接入skywalking 一、说明二、步骤2.1 准备java-agent包2.2 tomcat部署2.2.1 下载2.2.2 tomcat修改catalina.sh文件2.2.3 tomcat修改启动端口2.2.4 启动tomcat 三、验证四、问题排查4.1 tomcat的启动日志 一、说明 服务器中已经运行着skywalking&#xff0c;准备在同一台…

7-WebApis-5

Web APIs - 5 目标&#xff1a; 能够利用JS操作浏览器,具备利用本地存储实现学生就业表的能力 BOM操作综合案例 js组成 JavaScript的组成 ECMAScript: 规定了js基础语法核心知识。比如&#xff1a;变量、分支语句、循环语句、对象等等 Web APIs : DOM 文档对象模型&#xff…

2023全云在线联合微软AIGC专场沙龙:人工智能与企业创新,促进创造力的数字化转型

6月29日&#xff0c;由全云在线平台和微软联合主办的人工智能与企业创新&#xff1a;促进创造力的数字化转型——2023AIGC微软专场沙龙在广州天河区正佳万豪酒店举行。 关于2023AIGC微软专场沙龙 GPT翻开了AGI新的一页&#xff0c;也翻开了各行各业的新篇章。 2022年11月30日…

使用Jmeter读取和使用Redis数据

目录 前言 缓存 Redis服务和客户端安装 Jmeter使用Redis 前言 在使用 JMeter 进行性能测试时&#xff0c;有时需要读取和使用 Redis 数据。Redis 是一个开源的内存数据存储系统&#xff0c;常用于缓存、消息队列和数据存储等场景。 缓存 Web系统通常使用数据库来存储数据…

性能测试讲解超详细Jmeter

目录 什么是性能 性能测试的目的 功能测试和性能测试 基准测试 负载测试 稳定性测试 压力测试 并发测试 总结 性能测试指标 响应时间 并发数 吞吐量 点击数 错误率 资源使用率 总结 性能测试流程 性能测试需求分析 性能测试计划和方案 ​编辑性能测试用例​编辑 性…

【MySQL】MySQL数据库,RDBMS 术语,使用说明和报错解决的详细讲解

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

CommonJS 和 ES6 module

本文主要自己觉得要记录的点记录下来,不耽误大家时间&#xff0c;会持续更新。 Module对象 Module {id: xxx/demo/1.js, //加载文件的绝对路径path: xxx/demo,// 加载文件所在目录的绝对路径exports: [Function (anonymous)],filename: xxx/demo/1.js,加载文件的绝对路径load…

蓝桥杯专题-试题版-【圆的面积】【字符串对比】【字母图形】【核桃的数量】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

Lesson1-1:OpenCV简介

图像处理 学习目标 了解图像的起源知道数字图像的表示 1 图像的起源 1.1 图像是什么 图像是人类视觉的基础&#xff0c;是自然景物的客观反映&#xff0c;是人类认识世界和人类本身的重要源泉。“图”是物体反射或透射光的分布&#xff0c;“像“是人的视觉系统所接受的图在…

【数据库】MySQL 高级(进阶) SQL 语句

文章目录 前提条件一、常用查询1. SELECT&#xff08;显示查询&#xff09;2. DISTINCT&#xff08;不重复查询&#xff09;3. WHERE&#xff08;有条件查询&#xff09;4. AND/OR&#xff08;且/或&#xff09;5. IN &#xff08;显示已知值的字段&#xff09;6. BETWEEN&…

【探索 Kubernetes|作业管理篇 系列 14】StatefulSet 存储状态

前言 大家好&#xff0c;我是秋意零。 在上一篇中&#xff0c;我们讲解了 StatefulSet 的拓扑状态&#xff1b;我们发现&#xff0c;它的拓扑状态&#xff0c;就是顺序启动/删除、Pod 名称编号命名、将 Pod 名称设为 Hostname 名称、通过 Service 无头服务的 DNS 记录访问。 …

4通道AD采集子卡模块有哪些推荐?

FMC134是一款4通道3.2GSPS&#xff08;2通道6.4GSPS&#xff09;采样率12位AD采集FMC子卡模块&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.4规范&#xff0c;可以作为一个理想的IO模块耦合至FPGA前端&#xff0c;16通道的JESD204B接口通过FMC连接器连接至FPGA的高速串行…

Excel的一些操作:移动列,添加索引

移动列 在 Excel 中移动列的 5 种方法——分步教程 选中某一列&#xff0c;然后鼠标放在边缘&#xff0c;移动到你想移动到的列 添加索引 例如&#xff0c;我想添加的索引列名为“index”&#xff0c;然后选中想要添加序列的行&#xff0c;点击“填充-->录入123序列”