世界除了线性,还有非线性。
图片来源:zcool.com.cn
何谓神经网络
简而言之一句话:模拟人脑学习非线性关系的计算模型。学习非线性关系意味着可以学习的东西更多,更复杂,更深入。它的构成可以简化为:输入->处理->输出。这样的三步骤,其中的处理步骤就是可以大书特书的部分,由最原始的感知机,到卷积神经网络,循环神经网络等等。这不是本文最主要的内容,所以简要一提。
pytorch--搭建神经网络的利器
pytorch最大的好处就是灵活,你可以任意去构建属于自己的神经网络,但灵活就意味着很多内容需要你自己去编写,但是无所谓,它已经足够好了。
下载pytorch
不知道你的电脑有没有GPU,没有的话你直接使用这句话去在自己的anaconda环境里去下载吧。
pip3 install torch torchvision torchaudio
输入在这个地方,按回车键就行有GPU的大哥们,就去官网上看一看,下载对应的版本。
找到自己对应的cuda版本,复制run this command里的话,放到上图的anaconda环境里下载就行了。
pytorch的基础用法
1.张量操作
pytorch的基础用法和numpy特别像,熟悉numpy也可以在pytorch里照猫画虎,只不过人家用的东西叫张量,何谓张量,在pytorch中用tensor表示。一维张量,叫向量,二维张量叫矩阵,更高维的,不知道,没明确的定义。黑白的图片通常二维张量即可表示,但是彩色图片,多了颜色这个维度,那么就成了三维张量。哦对了,0维的叫标量。
张量的数据类型和numpy.array基本一一对应,但是不支持str类型。 包括:
torch.float64(torch.double), torch.float32(torch.float), torch.float16, torch.int64(torch.long), torch.int32(torch.int), torch.int16, torch.int8, torch.uint8, torch.bool
1.1 构建张量
#向量示例
vector = torch.tensor([1.0,2.0,3.0,4.0]) #向量,1维张量
print(vector)
print(vector.dim())#使用dim查看维度
#输出
tensor([1., 2., 3., 4.])
1
#矩阵示例
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
print(matrix)
print(matrix.dim())
#输出
tensor([[1., 2.],
[3., 4.]])
2
#三维张量示例
tensor3 = torch.tensor([[[1.0,2.0],[3.0,4.0]],[[5.0,6.0],[7.0,8.0]]]) # 3维张量
print(tensor3)
print(tensor3.dim())
#输出
tensor([[[1., 2.],
[3., 4.]],
[[5., 6.],
[7., 8.]]])
3
规律这不就是来了,有几个括号就是几维张量(数一边就行了啊)
1.2 张量尺寸
查看张量在每个维度上的长度,有两种方法size()和shape(注意括号的有无)
vector=torch.tensor([1.0,2.0,3.0])#一维张量
vector.shape,vector.size()
#输出
(torch.Size([3]), torch.Size([3]))
#可以看到是长度为3的一维张量
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
matrix.shape,matrix.size()
#输出
(torch.Size([2, 2]), torch.Size([2, 2]))
#可以看到是2行,2列,元素个数为2*2的二维张量
改变张量形状的方法有两个,一个是view(行数,列数),另一个是reshape(行数,列数)。如果存在转置操作,会使得数据结构扭曲,使用view会报错,这时候可以使用reshape强制转化
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
matrix.view(4,1),matrix.reshape(4,1)
#输出
(tensor([[1.],
[2.],
[3.],
[4.]]),
tensor([[1.],
[2.],
[3.],
[4.]]))
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
matrix.view(4,-1)#使用-1表示该位置的长度由程序自己判断,你可别俩都输入-1哈
#输出
tensor([[1.],
[2.],
[3.],
[4.]])
1.3 张量和numpy
可以用numpy方法从Tensor得到numpy数组,也可以用torch.from_numpy从numpy数组得到Tensor。这两种方法关联的Tensor和numpy数组是共享数据内存的。如果改变其中一个,另外一个的值也会发生改变。
#torch.from_numpy函数从numpy数组得到Tensor
array= np.ones(3)
tensor = torch.from_numpy(array)
#输出
tensor([1., 1., 1.], dtype=torch.float64)
#由tensor转换为numpy
tensor = torch.ones(3)
array = tensor.numpy()
print(type(array))
#输出
<class 'numpy.ndarray'>
# item方法和tolist方法可以将张量转换成Python数值和数值列表
number = torch.tensor(1.0)
number = number.item()
print(type(number))
#输出
<class 'float'>
num_list = torch.rand(2,2)
num_list = tensor.tolist()
print(type(num_list))
#输出
<class 'list'>
1.4 张量的结构操作
张量的操作在很多地方和numpy非常的像
以代码为例
torch.arange(1,10,step = 2)#创建以1开始,9为结束,步长为2的一维张量
#输出
tensor([1, 3, 5, 7, 9])
#构建1到10,个数为5的等差数列
torch.linspace(1,10,steps=5)
#输出
tensor([ 1.0000, 3.2500, 5.5000, 7.7500, 10.0000])
#构建全为0的3行3列的二维张量
torch.zeros((3,3))
#输出
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
#构建全为1的3行3列的二维张量
torch.ones((3,3))
#输出
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
#构建一个和a形状一样的全0二维张量
a=torch.ones((3,3))
b=torch.zeros_like(a)#如果要全为1的话使用ones_like
print(b)
#输出
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
#对每个元素都添加相同的数
#a是一个全为1的二维张量
a=torch.ones((3,3))
b=torch.add(a,1)#每个元素都添加1
print(b)
#输出
tensor([[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]])
#替换全部元素为某一个数,使用fill_
a=torch.ones((3,3))
b=torch.fill_(a,5)#将1全部替换为5
print(b)
#输出
tensor([[5., 5., 5.],
[5., 5., 5.],
[5., 5., 5.]])
#生成随机数种子,方便下次结果复现。
#如果不设置随机数种子,你每次随机取值都会变化
#设置之后,随机取值结果不再改变
torch.manual_seed(0)#设置随机数种子
#[0, 1)的均匀分布的随机数
torch.rand(5)) # 返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数,5代表返回张量的长度。
#输出
tensor([0.4963, 0.7682, 0.0885, 0.1320, 0.3074])
#正太分布随机数
#注意,下面这个是创建了一个均值0,标准差为1的0维张量
d=torch.normal(mean=torch.tensor(0.0),std=torch.tensor(1.0))
print(d)
d.size()
#输出
tensor(0.2490)
torch.Size([])
#创建服从正态分布的随机一维张量,长度为3
b = torch.normal(mean = torch.zeros(3), std = torch.ones(3))
print(b)
#输出
tensor([-0.3354, 0.4564, -0.6255])
#创建三维服从正态分布的随机张量
b = torch.normal(mean = torch.zeros(3,3), std = torch.ones(3,3))
print(b)
#输出
tensor([[ 0.4539, -1.3740, 2.8474],
[-0.7322, 0.3833, 1.1000],
[-0.0877, 0.4154, 0.6426]])
#创建一个0到9的整数随机排列一维张量
d = torch.randperm(10)
print(d)
#输出
tensor([9, 0, 2, 7, 3, 6, 4, 8, 1, 5])
#创建1个单位二维张量
m=torch.eye(2,2)
print(m)
#输出
tensor([[1., 0.],
[0., 1.]])
#创建1个对角二维张量
m=torch.diag(torch.tensor([5,6,8]))#输入必须是tensor
print(m)
#输出
tensor([[5, 0, 0],
[0, 6, 0],
[0, 0, 8]])
对于张量的切片索引这一部分主要展示不规则索引,因为其他的索引方式几乎和numpy一模一样。这里借鉴算法美食屋的20天吃掉一个pytorch中的例子来做展示。对于不规则的切片提取,可以使用torch.index_select, torch.take, torch.gather, torch.masked_select.
生成一个类似于4个班,每班5人,每人7门课的一个三维张量。
minval=0
maxval=100
scores = torch.floor(minval + (maxval-minval)*torch.rand([4,5,7])).int()
print(scores)
#输出
tensor([[[79, 56, 73, 25, 8, 7, 99],
[81, 15, 69, 87, 99, 93, 88],
[38, 32, 91, 78, 19, 94, 74],
[77, 18, 64, 32, 89, 41, 69],
[58, 71, 33, 74, 15, 61, 16]],
[[ 0, 9, 89, 77, 96, 90, 5],
[15, 41, 17, 84, 12, 25, 1],
[21, 91, 90, 85, 88, 94, 37],
[72, 94, 66, 99, 75, 81, 32],
[73, 55, 38, 21, 21, 11, 83]],
[[85, 44, 21, 88, 81, 53, 26],
[95, 70, 12, 97, 87, 31, 78],
[21, 42, 92, 52, 14, 33, 36],
[40, 54, 96, 52, 19, 52, 73],
[74, 4, 41, 12, 28, 68, 14]],
[[68, 92, 53, 16, 32, 60, 11],
[74, 4, 1, 1, 39, 83, 2],
[91, 29, 64, 52, 4, 91, 76],
[99, 75, 16, 91, 52, 73, 9],
[35, 0, 30, 60, 10, 65, 76]]], dtype=torch.int32)
抽取每个班级第0个学生,第2个学生,第4个学生的全部成绩
torch.index_select(scores,dim = 1,index = torch.tensor([0,2,4]))
#输出
tensor([[[79, 56, 73, 25, 8, 7, 99],
[38, 32, 91, 78, 19, 94, 74],
[58, 71, 33, 74, 15, 61, 16]],
[[ 0, 9, 89, 77, 96, 90, 5],
[21, 91, 90, 85, 88, 94, 37],
[73, 55, 38, 21, 21, 11, 83]],
[[85, 44, 21, 88, 81, 53, 26],
[21, 42, 92, 52, 14, 33, 36],
[74, 4, 41, 12, 28, 68, 14]],
[[68, 92, 53, 16, 32, 60, 11],
[91, 29, 64, 52, 4, 91, 76],
[35, 0, 30, 60, 10, 65, 76]]], dtype=torch.int32)
如果dim设置为2的话,意味着抽取,每个班,每个学生,第0门,第2和第4门课的成绩
tensor([[[79, 73, 8],
[81, 69, 99],
[38, 91, 19],
[77, 64, 89],
[58, 33, 15]],
[[ 0, 89, 96],
[15, 17, 12],
[21, 90, 88],
[72, 66, 75],
[73, 38, 21]],
[[85, 21, 81],
[95, 12, 87],
[21, 92, 14],
[40, 96, 19],
[74, 41, 28]],
[[68, 53, 32],
[74, 1, 39],
[91, 64, 4],
[99, 16, 52],
[35, 30, 10]]], dtype=torch.int32)
如果dim设置为0,tensor设置为[0,2],则表示取出第0个班和第二个班的所有人的所有成绩
torch.index_select(scores,dim =0,index = torch.tensor([0,2]))
#输出
tensor([[[79, 56, 73, 25, 8, 7, 99],
[81, 15, 69, 87, 99, 93, 88],
[38, 32, 91, 78, 19, 94, 74],
[77, 18, 64, 32, 89, 41, 69],
[58, 71, 33, 74, 15, 61, 16]],
[[85, 44, 21, 88, 81, 53, 26],
[95, 70, 12, 97, 87, 31, 78],
[21, 42, 92, 52, 14, 33, 36],
[40, 54, 96, 52, 19, 52, 73],
[74, 4, 41, 12, 28, 68, 14]]], dtype=torch.int32)
抽取第0个班级第0个学生的第0门课程,第2个班级的第3个学生的第1门课程,第3个班级的第4个学生第6门课程成绩
#take将输入看成一维数组,输出和index同形状
s = torch.take(scores,torch.tensor([0*5*7+0,2*5*7+3*7+1,3*5*7+4*7+6]))
print(s)
#输出
tensor([79, 54, 76], dtype=torch.int32)
抽取分数大于等于80分的分数(布尔索引),结果是1维张量
g = torch.masked_select(scores,scores>=80)
print(g)
#输出
tensor([99, 81, 87, 99, 93, 88, 91, 94, 89, 89, 96, 90, 84, 91, 90, 85, 88, 94,
94, 99, 81, 83, 85, 88, 81, 95, 97, 87, 92, 96, 92, 83, 91, 91, 99, 91],
dtype=torch.int32)
赋值操作
#如果分数大于60分,赋值成1,否则赋值成0
ifpass = torch.where(scores>60,torch.tensor(1),torch.tensor(0))
print(ifpass)
#输出
tensor([[[1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 0, 1],
[0, 1, 0, 1, 0, 1, 0]],
[[0, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 0, 1]],
[[1, 0, 0, 1, 1, 0, 0],
[1, 1, 0, 1, 1, 0, 1],
[0, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 1, 0]],
[[1, 1, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 0, 1, 0, 0, 1, 1],
[1, 1, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 1, 1]]])
#将每个班级第0个学生,第2个学生,第4个学生的全部成绩赋值成满分
torch.index_fill(scores,dim = 1,index = torch.tensor([0,2,4]),value = 100)
#等价于 scores.index_fill(dim = 1,index = torch.tensor([0,2,4]),value = 100)
#输出
tensor([[[100, 100, 100, 100, 100, 100, 100],
[ 81, 15, 69, 87, 99, 93, 88],
[100, 100, 100, 100, 100, 100, 100],
[ 77, 18, 64, 32, 89, 41, 69],
[100, 100, 100, 100, 100, 100, 100]],
[[100, 100, 100, 100, 100, 100, 100],
[ 15, 41, 17, 84, 12, 25, 1],
[100, 100, 100, 100, 100, 100, 100],
[ 72, 94, 66, 99, 75, 81, 32],
[100, 100, 100, 100, 100, 100, 100]],
[[100, 100, 100, 100, 100, 100, 100],
[ 95, 70, 12, 97, 87, 31, 78],
[100, 100, 100, 100, 100, 100, 100],
[ 40, 54, 96, 52, 19, 52, 73],
[100, 100, 100, 100, 100, 100, 100]],
[[100, 100, 100, 100, 100, 100, 100],
[ 74, 4, 1, 1, 39, 83, 2],
[100, 100, 100, 100, 100, 100, 100],
[ 99, 75, 16, 91, 52, 73, 9],
[100, 100, 100, 100, 100, 100, 100]]], dtype=torch.int32)
#将分数小于60分的分数赋值成60分
b = torch.masked_fill(scores,scores<60,60)
#等价于b = scores.masked_fill(scores<60,60)
#输出
tensor([[[79, 60, 73, 60, 60, 60, 99],
[81, 60, 69, 87, 99, 93, 88],
[60, 60, 91, 78, 60, 94, 74],
[77, 60, 64, 60, 89, 60, 69],
[60, 71, 60, 74, 60, 61, 60]],
[[60, 60, 89, 77, 96, 90, 60],
[60, 60, 60, 84, 60, 60, 60],
[60, 91, 90, 85, 88, 94, 60],
[72, 94, 66, 99, 75, 81, 60],
[73, 60, 60, 60, 60, 60, 83]],
[[85, 60, 60, 88, 81, 60, 60],
[95, 70, 60, 97, 87, 60, 78],
[60, 60, 92, 60, 60, 60, 60],
[60, 60, 96, 60, 60, 60, 73],
[74, 60, 60, 60, 60, 68, 60]],
[[68, 92, 60, 60, 60, 60, 60],
[74, 60, 60, 60, 60, 83, 60],
[91, 60, 64, 60, 60, 91, 76],
[99, 75, 60, 91, 60, 73, 60],
[60, 60, 60, 60, 60, 65, 76]]], dtype=torch.int32)
增维和降维
torch.squeeze 可以减少维度。
torch.unsqueeze 可以增加维度。
#由二维降维至一维
a = torch.tensor([[1.0,2.0]])
s = torch.squeeze(a)
print(a)
print(s)
#输出
tensor([[1., 2.]])
tensor([1., 2.])
#增维
#二维升至三维
a = torch.tensor([[1.0,2.0]])
s = torch.unsqueeze(a,dim=0)#dim赋值0和1效果相同
print(a)
print(s)
#输出
tensor([[1., 2.]])
tensor([[[1., 2.]]])
合并分割
合并方法:
cat:不会增加维度,只起到连接作用
stack:堆叠,会增加维度
分割方法:
split:cat的逆运算,可以指定每份分割的数量
tensor1 = torch.tensor([[1.0,2.0],[3.0,4.0]])
tensor2 = torch.tensor([[5.0,6.0],[7.0,8.0]])
tensor3 = torch.tensor([[9.0,10.0],[11.0,12.0]])
cat = torch.cat([tensor1,tensor2,tensor3],dim = 0)
#输出
tensor([[ 1., 2.],
[ 3., 4.],
[ 5., 6.],
[ 7., 8.],
[ 9., 10.],
[11., 12.]])
tensor1 = torch.tensor([[1.0,2.0],[3.0,4.0]])
tensor2 = torch.tensor([[5.0,6.0],[7.0,8.0]])
tensor3 = torch.tensor([[9.0,10.0],[11.0,12.0]])
stack= torch.stack([tensor1,tensor2,tensor3],dim = 0)
#输出
tensor([[[ 1., 2.],
[ 3., 4.]],
[[ 5., 6.],
[ 7., 8.]],
[[ 9., 10.],
[11., 12.]]])
tensor1 = torch.tensor([[1.0,2.0],[3.0,4.0]])
tensor2 = torch.tensor([[5.0,6.0],[7.0,8.0]])
tensor3 = torch.tensor([[9.0,10.0],[11.0,12.0]])
cat= torch.cat([tensor1,tensor2,tensor3],dim = 0)
cat_split=torch.split(cat,split_size_or_sections =[3,2,1],dim = 0)#cat的逆运算,第一份3个,第二份2个,第三份1个
#输出
(tensor([[1., 2.],
[3., 4.],
[5., 6.]]),
tensor([[ 7., 8.],
[ 9., 10.]]),
tensor([[11., 12.]]))
1.5 张量的数学运算
这一部分通过代码来展示
#基本操作
a = torch.tensor([[1.0,2],[-3,4.0]])
b = torch.tensor([[5.0,6],[7.0,8.0]])
a+b
a-b
a*b
a**2 #a的2次方
a/b
a%3 #求模
#输出
tensor([[ 6., 8.],
[ 4., 12.]])
tensor([[ -4., -4.],
[-10., -4.]])
tensor([[ 5., 12.],
[-21., 32.]])
tensor([[ 1., 4.],
[ 9., 16.]])
tensor([[ 0.2000, 0.3333],
[-0.4286, 0.5000]])
tensor([[1., 2.],
[-0., 1.]])
#
print(torch.ge(a,2))#判断是不是大于等于2
print(torch.eq(a,2))#判断是不是等于2
print(torch.sqrt(a))#对a开平方
print(torch.min(a,b))#取出a,b在同维度上的最小值
print(torch.max(a,b))#取出a,b在同维度上的最大值
print(torch.div(a,b))#做除法
#
x = torch.tensor([2.6,-2.7])
print(torch.round(x)) #保留整数部分,四舍五入
print(torch.floor(x)) #保留整数部分,向下归整
print(torch.ceil(x)) #保留整数部分,向上归整
print(torch.trunc(x)) #保留整数部分,向0归整
print(torch.fmod(x,2)) #作除法取余数
print(torch.remainder(x,2)) #作除法取剩余的部分,结果恒正
#输出
tensor([ 3., -3.])
tensor([ 2., -3.])
tensor([ 3., -2.])
tensor([ 2., -2.])
tensor([ 0.6000, -0.7000])
tensor([0.6000, 1.3000])
# 幅值裁剪
x = torch.tensor([0.9,-0.8,100.0,-20.0,0.7])
y = torch.clamp(x,min=-1,max = 1)
z = torch.clamp(x,max = 1)
print(y)
print(z)
#输出
tensor([ 0.9000, -0.8000, 1.0000, -1.0000, 0.7000])
tensor([ 0.9000, -0.8000, 1.0000, -20.0000, 0.7000])
矩阵运算
!!!操作的张量至少是二维张量
tensor1 = torch.tensor([[1.0,2],[-3,4.0]])
tensor2 = torch.tensor([[5.0,6],[7.0,8.0]])
print(tensor1@tensor2)#矩阵乘法
#输出
tensor([[19., 22.],
[43., 50.]])
#矩阵转置
print(tensor1.t())
#输出
tensor([[ 1., -3.],
[ 2., 4.]])
#求逆矩阵
print(torch.inverse(tensor1))
#输出
tensor([[ 0.4000, -0.2000],
[ 0.3000, 0.1000]])
#求矩阵trace
print(torch.trace(tensor1))
#输出
tensor(5.)
#求矩阵的范数
print(torch.norm(tensor1))
#输出
tensor(5.4772)
#求矩阵的行列式
print(torch.det(tensor2))
#输出
tensor(-2.0000)
#求矩阵的特征值和特征向量
print(torch.linalg.eig(tensor2))
#输出
torch.return_types.linalg_eig(
eigenvalues=tensor([-0.1521+0.j, 13.1521+0.j]),
eigenvectors=tensor([[-0.7587+0.j, -0.5928+0.j],
[ 0.6515+0.j, -0.8054+0.j]]))
2.nn.Module介绍
Pytorch和神经网络相关的功能组件大多都封装在 torch.nn模块下。 这个模块下面有搭建神经网络,需要的很多东西。如:
1.模型层
基础层
nn.Linear:全连接层。参数个数 = 输入层特征数× 输出层特征数(weight)+ 输出层特征数(bias) nn.Flatten:压平层,用于将多维张量样本压成一维张量样本。 nn.BatchNorm1d:一维批标准化层。通过线性变换将输入批次缩放平移到稳定的均值和标准差。可以增强模型对输入不同分布的适应性,加快模型训练速度,有轻微正则化效果。一般在激活函数之前使用。可以用afine参数设置该层是否含有可以训练的参数。 nn.BatchNorm2d:二维批标准化层。 nn.BatchNorm3d:三维批标准化层。 nn.Dropout:一维随机丢弃层。一种正则化手段。 nn.Dropout2d:二维随机丢弃层。 nn.Dropout3d:三维随机丢弃层。
卷积网络相关层
nn.Conv1d:普通一维卷积,常用于文本。参数个数 = 输入通道数×卷积核尺寸(如3)×卷积核个数 + 卷积核尺寸(如3)
nn.Conv2d:普通二维卷积,常用于图像。参数个数 = 输入通道数×卷积核尺寸(如3乘3)×卷积核个数 + 卷积核尺寸(如3乘3) 通过调整dilation参数大于1,可以变成空洞卷积,增大卷积核感受野。 通过调整groups参数不为1,可以变成分组卷积。分组卷积中不同分组使用相同的卷积核,显著减少参数数量。 当groups参数等于通道数时,相当于tensorflow中的二维深度卷积层tf.keras.layers.DepthwiseConv2D。 利用分组卷积和1乘1卷积的组合操作,可以构造相当于Keras中的二维深度可分离卷积层tf.keras.layers.SeparableConv2D。
nn.Conv3d:普通三维卷积,常用于视频。参数个数 = 输入通道数×卷积核尺寸(如3乘3乘3)×卷积核个数 + 卷积核尺寸(如3乘3乘3) 。
nn.MaxPool1d: 一维最大池化。
nn.MaxPool2d:二维最大池化。一种下采样方式。没有需要训练的参数。
nn.MaxPool3d:三维最大池化
循环网络相关层
nn.Embedding:嵌入层。一种比Onehot更加有效的对离散特征进行编码的方法。一般用于将输入中的单词映射为稠密向量。嵌入层的参数需要学习。
nn.LSTM:长短记忆循环网络层【支持多层】。最普遍使用的循环网络层。具有携带轨道,遗忘门,更新门,输出门。可以较为有效地缓解梯度消失问题,从而能够适用长期依赖问题。设置bidirectional = True时可以得到双向LSTM。需要注意的时,默认的输入和输出形状是(seq,batch,feature), 如果需要将batch维度放在第0维,则要设置batch_first参数设置为True。
nn.GRU:门控循环网络层【支持多层】。LSTM的低配版,不具有携带轨道,参数数量少于LSTM,训练速度更快。
Transformer相关层
nn.Transformer:Transformer网络结构。Transformer网络结构是替代循环网络的一种结构,解决了循环网络难以并行,难以捕捉长期依赖的缺陷。它是目前NLP任务的主流模型的主要构成部分。Transformer网络结构由TransformerEncoder编码器和TransformerDecoder解码器组成。编码器和解码器的核心是MultiheadAttention多头注意力层。 nn.TransformerEncoder:Transformer编码器结构。由多个 nn.TransformerEncoderLayer编码器层组成。 nn.TransformerDecoder:Transformer解码器结构。由多个 nn.TransformerDecoderLayer解码器层组成。 nn.TransformerEncoderLayer:Transformer的编码器层。 nn.TransformerDecoderLayer:Transformer的解码器层。 nn.MultiheadAttention:多头注意力层。
2.激活函数
Sigmoid又叫作 Logistic 激活函数,它将实数值压缩进 0 到 1 的区间内,还可以在预测概率的输出层中使用。
Tanh 激活函数又叫作双曲正切激活函数。与 Sigmoid 函数类似,Tanh 函数也使用真值,但 Tanh 函数将其压缩至-1 到 1 的区间内。与 Sigmoid 不同,Tanh 函数的输出以零为中心,因为区间在-1 到 1 之间。你可以将 Tanh 函数想象成两个 Sigmoid 函数放在一起。在实践中,Tanh 函数的使用优先性高于 Sigmoid 函数。
ReLU激活函数使网络更快速地收敛。它不会饱和,即它可以对抗梯度消失问题,至少在正区域(x> 0 时)可以这样,因此神经元至少在一半区域中不会把所有零进行反向传播。由于使用了简单的阈值化(thresholding),ReLU 计算效率很高。
3.损失函数
均方误差损失 MSELoss。计算 output 和 target 之差的均方差。
torch.nn.MSELoss(reduction='mean')
#参数
reduction-三个值,none: 不使用约简;mean:返回loss和的平均值; sum:返回loss的和。默认:mean。
交叉熵损失 CrossEntropyLoss.当训练有 C 个类别的分类问题时很有效. 可选参数 weight 必须是一个1维 Tensor, 权重将被分配给各个类别. 对于不平衡的训练集非常有效。在多分类任务中,经常采用 softmax 激活函数+交叉熵损失函数,因为交叉熵描述了两个概率分布的差异,然而神经网络输出的是向量,并不是概率分布的形式。所以需要 softmax激活函数将一个向量进行“归一化”成概率分布的形式,再采用交叉熵损失函数计算 loss。
torch.nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean')
#参数
weight (Tensor, optional) – 自定义的每个类别的权重. 必须是一个长度为 C 的 Tensor
ignore_index (int, optional) – 设置一个目标值, 该目标值会被忽略, 从而不会影响到 输入的梯度。
reduction-三个值,none: 不使用约简;mean:返回loss和的平均值; sum:返回loss的和。默认:mean。
二进制交叉熵损失 BCELoss二分类任务时的交叉熵计算函数。用于测量重构的误差, 例如自动编码机. 注意目标的值 t[i] 的范围为0到1之间.
torch.nn.BCELoss(weight=None, reduction='mean')
#参数
weight (Tensor, optional) – 自定义的每个 batch 元素的 loss 的权重. 必须是一个长度为 “nbatch” 的 的 Tensor
pos_weight(Tensor, optional) – 自定义的每个正样本的 loss 的权重. 必须是一个长度 为 “classes” 的 Tensor
BCEWithLogitsLoss。BCEWithLogitsLoss损失函数把 Sigmoid 层集成到了 BCELoss 类中. 该版比用一个简单的 Sigmoid 层和 BCELoss 在数值上更稳定, 因为把这两个操作合并为一个层之后, 可以利用 log-sum-exp 的 技巧来实现数值稳定.
torch.nn.BCEWithLogitsLoss(weight=None, reduction='mean', pos_weight=None)
#参数
weight (Tensor, optional) – 自定义的每个 batch 元素的 loss 的权重. 必须是一个长度 为 “nbatch” 的 Tensor
pos_weight(Tensor, optional) – 自定义的每个正样本的 loss 的权重. 必须是一个长度 为 “classes” 的 Tensor
搭建属于自己的神经网络
首先,对于神经网络做一个剖析。神经网络的构造如下:
输入层 隐藏层(可多可少) 输出层
就好比,一个人问你世界最高的山峰是哪个(输入层),你的大脑经过思考之后(隐藏层),告诉那个人是珠穆朗玛峰(输出层)。
激活函数的意义,就像神经元受到刺激,刺激达到一定程度之后,对刺激做出反应,而激活函数必须是非线性的。
损失函数就更好说,我们要衡量真实情况和预测情况之间的差距,差距越小则代表学习的越好。
到这一步,还不算真正意义上的神经网络,真正的神经网络必须自己去主动纠正偏差以达到最真实的情况。优化器横空出世,最流行的方法便是随机梯度下降。(曲面上方向导数的最大值的方向就代表了梯度的方向,因此我们在做梯度下降的时候,应该是沿着梯度的反方向进行权重的更新,可以大致找到全局的最优解)。这就让神经网络自己学会怎么改变权重和偏置的值。
改变的权重和偏置的值该怎么回到最初的位置,再来一次这样的计算呢,这就需要反向传播了。
这样便可以设置训练次数,来循环这个计算过程,来训练模型。
知道了神经网络是什么之后,那我便来搭建自己的神经网络吧。
1.导包
import torch
from torch import nn
import torch.nn.functional as F
2.搭建神经网络的方式有几种,这里我们采用class类的方式来搭建。(都是标准化的,跟搭积木一样)。全连接层是比较贵的,针对不同的任务可以使用不同的层,这在上文中都有提及。
class Net(nn.Module):
def __init__(self):
super().__init__()#继承nn.Module的功能
#使用三个全连接层(最简单的模式)
self.layer1 = nn.Linear(2,4)
self.layer2 = nn.Linear(4,8)
self.layer3 = nn.Linear(8,1)
def forward(self,x):
x = F.relu(self.layer1(x))#对于第一层的内容进行激活操作
x = F.relu(self.layer2(x))#对于第一层激活到第二层的内容再激活
y = self.layer3(x)#输出不需要激活!!!
return y
这就结束了,一个简单的神经网络,就搭建好了,等等损失函数,优化器,反向传播呢??先别着急嘛。我们先打印看看,自己的神经网络是什么样子。
net=Net()#对于类进行实例化操作,必要步骤!
print(net)
#输出
Net(
(layer1): Linear(in_features=2, out_features=4, bias=True)
(layer2): Linear(in_features=4, out_features=8, bias=True)
(layer3): Linear(in_features=8, out_features=1, bias=True)
)
由于pytorch并没有集成训练函数,不能像keras一样使用fit进行训练,但好在有一位大佬,写了一个python库--torchkeras,使我们可以像keras一样,优雅的使用fit去训练模型。介绍如下:
from torchkeras import KerasModel
from torchkeras.metrics import Accuracy#验证精度,集成的方法比较多
#损失函数,不同的问题选用不同的损失函数
loss_fn = nn.BCEWithLogitsLoss()#损失函数
#优化器
#一般有两种优化器,Adam和SGD
#lr:学习率
#net.parameters():对网络的参数进行优化
optimizer= torch.optim.Adam(net.parameters(),lr = 0.01)
#验证精度(字典形式)
metrics_dict = {"acc":Accuracy()}
接下来,将模型net和损失函数这些放进模型里,然后启用model.fit进行训练。
model = KerasModel(net,
loss_fn=loss_fn,
metrics_dict = metrics_dict,
optimizer = optimizer)
model.fit(
train_data = train_data,#训练数据集
val_data= val_data,#验证数据集
epochs=10,#训练批次数
monitor="val_acc", #可选val_loss和val_acc
mode="max",#如果是val_acc,则选择max,如果是val_loss,则选择min.
plot=True,
cpu=True#可以不设置,如果有gpu,会默认使用gpu。
)
这里以一个很经典的例子,给大家,展示从数据处理,到模型搭建,再到训练,验证的全流程。(其实最麻烦的就是数据处理)
判断泰坦尼克号上的人的生存与否。
https://tianchi.aliyun.com/dataset/111507 这是数据集的网址链接。
OK,那开始吧。代码如下:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from torch import nn
import torch
import torchkeras
from torchkeras.metrics import Accuracy
from torch.utils.data import Dataset,DataLoader,TensorDataset
#读取数据
train = pd.read_csv("train.csv")
test= pd.read_csv("test.csv")
我们可以看到,test数据里居然没有生存情况,也就是survived情况。 无奈,只有把训练集划分出一部分做验证集
#从sklearn里导出划分用的工具
from sklearn.model_selection import train_test_split
#训练集大小设置为70%,也可以为80%
df_train,df_test=train_test_split(train,train_size=0.7)
survived就只有0和1,Pclass和Embarked有三种情况
这不好,我们只希望用0和1表示,这就需要用到独热编码了。效果如下,False就是0,True就是1。对于Pclass,Embarked和sex都进行这样的处理。对于其余存在null的,进行如下处理
def pre_data(data):
"""
定义一个处理数据的函数
"""
#建立一个空dataframe
df=pd.DataFrame()
#对sex,Embarked和pclass进行onehot处理
pclass=pd.get_dummies(data['Pclass'])
pclass.columns = ['Pclass_' +str(x) for x in pclass.columns ]#更换列名
sex = pd.get_dummies(data['Sex'])
Embarked = pd.get_dummies(data['Embarked'],dummy_na=True)#对于null也进行onehot处理
Embarked.columns = ['Embarked_' + str(x) for x in Embarked.columns]
df=pd.concat([pclass,sex,Embarked],axis=1)
df['Age'] = data['Age'].fillna(0)
df['Age_null'] = pd.isna(data['Age']).astype('int32')
df['SibSp'] = data['SibSp']
df['Parch'] = data['Parch']
df['Fare'] = data['Fare']
df['Cabin_null'] = pd.isna(data['Cabin']).astype('int32')
return df
x_train = pre_data(df_train).values
y_train=df_train[['Survived']].values
x_test = pre_data(df_test).values
y_test = df_test[['Survived']].values
ok,来看看数据的形状吧
数据处理还没完哦,这并不是pytorch能接受的形式,我们需要经过以下处理。
x_train = x_train.astype(float)
y_train=y_train.astype(float)
x_test = x_test.astype(float)
y_test=y_test.astype(float)
#dataloader,数据迭代器一样
train_data = DataLoader(TensorDataset(torch.tensor(x_train).float(),torch.tensor(y_train).float()),
shuffle = True, batch_size = 8)
#shuffle:随机取,batch_size:一次取多少
val_data = DataLoader(TensorDataset(torch.tensor(x_test).float(),torch.tensor(y_test).float()),
shuffle = False, batch_size = 8)
看看最终处理好的数据吧
for features,labels in train_data:
print(features,labels)
break
接下来,来训练模型吧,这里要做一点点改动,数据有15列,所以将模型改成如下,一般都是先升再降15->20->15->1.
class Net(nn.Module):
def __init__(self):
super().__init__()#继承nn.Module的功能
#使用三个全连接层(最简单的模式)
self.layer1 = nn.Linear(15,20)
self.layer2 = nn.Linear(20,15)
self.layer3 = nn.Linear(15,1)
def forward(self,x):
x = F.relu(self.layer1(x))#对于第一层的内容进行激活操作
x = F.relu(self.layer2(x))#对于第一层激活到第二层的内容再激活
y = self.layer3(x)#输出不需要激活!!!
return y
net=Net()
loss_fn = nn.BCEWithLogitsLoss()#损失函数
optimizer= torch.optim.Adam(net.parameters(),lr = 0.01)
metrics_dict = {"acc":Accuracy()}
model = KerasModel(net,
loss_fn=loss_fn,
metrics_dict = metrics_dict,
optimizer = optimizer)
model.fit(
train_data = train_data,#训练数据集
val_data= val_data,#验证数据集
epochs=20,#训练批次数
monitor="val_acc", #可选val_loss和val_acc
mode="max",#如果是val_acc,则选择max,如果是val_loss,则选择min.
plot=True,
cpu=True#可以不设置,如果有gpu,会默认使用gpu。
)
训练结果如下:训练的过程是动态可视化的,效果一般般,但并不妨碍这是一个全流程的框架。
好啦,以上就是今天的全部内容,写了一天了,可累死我了
参考文章及代码:
https://pytorch.org/ https://gitee.com/iiiiiisu/eat_pytorch_in_20_days https://blog.csdn.net/shanglianlm/article/details/85019768 https://www.jiqizhixin.com/articles/2017-11-02-26 https://blog.csdn.net/q923714892/article/details/118411839